diff --git a/.gitignore b/.gitignore index ca24c54..af92971 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /.idea /cmake-build* +/render_2D/old/ +/render_3D/old/ diff --git a/CMakeLists.txt b/CMakeLists.txt index ea6ea3b..6e7441c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,4 +5,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) include("${CMAKE_CURRENT_LIST_DIR}/third_party/build_infra/start.cmake") include("${CMAKE_CURRENT_LIST_DIR}/kernel/main.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/render_2D/main.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/render_3D/main.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/web_server/main.cmake") include("${CMAKE_CURRENT_LIST_DIR}/third_party/build_infra/end.cmake") \ No newline at end of file diff --git a/render_2D/CMakeLists.txt b/render_2D/main.cmake similarity index 100% rename from render_2D/CMakeLists.txt rename to render_2D/main.cmake diff --git a/render_2D/render_2D/main.cpp b/render_2D/render_2D/main.cpp new file mode 100644 index 0000000..33c14ce --- /dev/null +++ b/render_2D/render_2D/main.cpp @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/render_3D/CMakeLists.txt b/render_3D/main.cmake similarity index 91% rename from render_3D/CMakeLists.txt rename to render_3D/main.cmake index 4b90397..f685057 100644 --- a/render_3D/CMakeLists.txt +++ b/render_3D/main.cmake @@ -66,22 +66,6 @@ if (WIN32) set_property(GLOBAL PROPERTY RENDERIVE_RENDER_3D_RUNTIME_FILES "${Aethera_render_3D_shaderc_runtime}") endif () -function(renderive_stage_render_3D_runtime target) - if (NOT TARGET "${target}") - message(FATAL_ERROR "Cannot stage render_3D runtime for missing target: ${target}") - endif () - renderive_stage_kernel_runtime("${target}") - if (WIN32) - get_property(Aethera_render_3D_runtime_files GLOBAL PROPERTY - RENDERIVE_RENDER_3D_RUNTIME_FILES) - add_custom_command(TARGET "${target}" POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy_if_different - ${Aethera_render_3D_runtime_files} - "$" - COMMENT "Staging Datoviz runtime dependencies for ${target}" - VERBATIM) - endif () -endfunction() if (RENDERIVE_BUILD_TESTS) set(Aethera_render_3D_test_targets) find_package(GTest CONFIG REQUIRED) diff --git a/render_3D/render_3D/Datoviz_Visuals.cpp b/render_3D/render_3D/Datoviz_Visuals.cpp deleted file mode 100644 index 55ea6ec..0000000 --- a/render_3D/render_3D/Datoviz_Visuals.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "Datoviz_Visuals.h" -// The visual family is template-based intentionally: every concrete type retains its own -// State/Impl/Observer chain while sharing the storage and validation mechanism. diff --git a/render_3D/render_3D/Datoviz_Visuals.h b/render_3D/render_3D/Datoviz_Visuals.h deleted file mode 100644 index aa383d6..0000000 --- a/render_3D/render_3D/Datoviz_Visuals.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "Glyph_Visual.h" -#include "Image_Visual.h" -#include "Labels_Visual.h" -#include "Marker_Visual.h" -#include "Mesh_Visual.h" -#include "Path_Visual.h" -#include "Pixel_Visual.h" -#include "Primitive_Visual.h" -#include "Segment_Visual.h" -#include "Sphere_Visual.h" -#include "Splat_Visual.h" -#include "Text_Visual.h" -#include "Vector_Visual.h" -#include "Volume_Visual.h" diff --git a/render_3D/render_3D/Glyph_Visual.h b/render_3D/render_3D/Glyph_Visual.h deleted file mode 100644 index 152f23e..0000000 --- a/render_3D/render_3D/Glyph_Visual.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "visuals/Basic_Visual.h" -namespace renderive::render_3d { -struct Glyph { - Vec3 position; - Vec4 bounds; - Vec4 texture_coordinates; - Rgba8 color; - float angle{}; - bool operator==(const Glyph&) const = default; -}; -namespace detail { -struct Glyph_Spec { - using Item = Glyph; - using Settings = Texture_Field_Settings; - static constexpr const char* name = "Glyph"; - static bool valid(const Item& item) noexcept { - return finite(item.position) && finite(item.bounds) && - finite(item.texture_coordinates) && finite(item.angle); - } -}; -} -using Glyph_Visual = detail::Attached_Visual; -using Glyph_State = Glyph_Visual::Properties; -} diff --git a/render_3D/render_3D/Image_Visual.h b/render_3D/render_3D/Image_Visual.h deleted file mode 100644 index 580365a..0000000 --- a/render_3D/render_3D/Image_Visual.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "visuals/Basic_Visual.h" -namespace renderive::render_3d { -struct Image { - Vec3 position; - Vec2 extent{1.0F, 1.0F}; - Vec4 texture_rectangle{0.0F, 0.0F, 1.0F, 1.0F}; - Rgba8 tint; - bool operator==(const Image&) const = default; -}; -namespace detail { -struct Image_Spec { - using Item = Image; - using Settings = Texture_Field_Settings; - static constexpr const char* name = "Image"; - static bool valid(const Item& item) noexcept { - return finite(item.position) && finite(item.extent) && item.extent.x > 0 && - item.extent.y > 0 && finite(item.texture_rectangle); - } -}; -} -using Image_Visual = detail::Attached_Visual; -using Image_State = Image_Visual::Properties; -} diff --git a/render_3D/render_3D/Labels_Visual.h b/render_3D/render_3D/Labels_Visual.h deleted file mode 100644 index bc690e8..0000000 --- a/render_3D/render_3D/Labels_Visual.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "visuals/Basic_Visual.h" -namespace renderive::render_3d { -struct Label { - Vec3 position; - std::uint32_t image_index{}; - Vec2 extent{32.0F, 16.0F}; - Rgba8 tint; - bool operator==(const Label&) const = default; -}; -namespace detail { -struct Labels_Spec { - using Item = Label; - using Settings = Texture_Field_Settings; - static constexpr const char* name = "Labels"; - static bool valid(const Item& item) noexcept { - return finite(item.position) && finite(item.extent) && item.extent.x > 0 && - item.extent.y > 0; - } -}; -} -using Labels_Visual = detail::Attached_Visual; -using Labels_State = Labels_Visual::Properties; -} diff --git a/render_3D/render_3D/Marker_Visual.h b/render_3D/render_3D/Marker_Visual.h deleted file mode 100644 index f0ac48b..0000000 --- a/render_3D/render_3D/Marker_Visual.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include "visuals/Basic_Visual.h" -namespace renderive::render_3d { -enum struct Marker_Shape : std::uint8_t { Disc, Square, Triangle, Diamond, Cross }; -struct Marker { - Vec3 position; - Rgba8 color; - float diameter_px{12.0F}; - float angle{}; - Marker_Shape shape{Marker_Shape::Disc}; - bool operator==(const Marker&) const = default; -}; -namespace detail { -struct Marker_Spec { - using Item = Marker; - using Settings = Visual_Settings; - static constexpr const char* name = "Marker"; - static bool valid(const Item& item) noexcept { - return finite(item.position) && finite(item.diameter_px) && - item.diameter_px > 0 && finite(item.angle); - } -}; -} -using Marker_Visual = detail::Attached_Visual; -using Marker_State = Marker_Visual::Properties; -} diff --git a/render_3D/render_3D/Mesh_Visual.h b/render_3D/render_3D/Mesh_Visual.h deleted file mode 100644 index 4cd8433..0000000 --- a/render_3D/render_3D/Mesh_Visual.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "visuals/Basic_Visual.h" -namespace renderive::render_3d { -struct Mesh_Vertex { - Vec3 position; - Rgba8 color; - Vec3 normal; - Vec2 texture_coordinate; - bool operator==(const Mesh_Vertex&) const = default; -}; -namespace detail { -struct Mesh_Spec { - using Item = Mesh_Vertex; - using Settings = Visual_Settings; - static constexpr const char* name = "Mesh"; - static bool valid(const Item& item) noexcept { - return finite(item.position) && finite(item.normal) && - finite(item.texture_coordinate); - } -}; -} -using Mesh_Visual = detail::Attached_Visual; -using Mesh_State = Mesh_Visual::Properties; -} diff --git a/render_3D/render_3D/Path_Visual.h b/render_3D/render_3D/Path_Visual.h deleted file mode 100644 index 1896b46..0000000 --- a/render_3D/render_3D/Path_Visual.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "visuals/Basic_Visual.h" -namespace renderive::render_3d { -struct Path_Vertex { - Vec3 position; - Rgba8 color; - float width_px{1.0F}; - bool operator==(const Path_Vertex&) const = default; -}; -namespace detail { -struct Path_Spec { - using Item = Path_Vertex; - using Settings = Visual_Settings; - static constexpr const char* name = "Path"; - static bool valid(const Item& item) noexcept { - return finite(item.position) && finite(item.width_px) && item.width_px > 0; - } -}; -} -using Path_Visual = detail::Attached_Visual; -using Path_State = Path_Visual::Properties; -} diff --git a/render_3D/render_3D/Pixel_Visual.h b/render_3D/render_3D/Pixel_Visual.h deleted file mode 100644 index ad6aaeb..0000000 --- a/render_3D/render_3D/Pixel_Visual.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "visuals/Basic_Visual.h" -namespace renderive::render_3d { -struct Pixel { - Vec3 position; - Rgba8 color; - float size_px{1.0F}; - bool operator==(const Pixel&) const = default; -}; -namespace detail { -struct Pixel_Spec { - using Item = Pixel; - using Settings = Visual_Settings; - static constexpr const char* name = "Pixel"; - static bool valid(const Item& item) noexcept { - return finite(item.position) && finite(item.size_px) && item.size_px > 0; - } -}; -} -using Pixel_Visual = detail::Attached_Visual; -using Pixel_State = Pixel_Visual::Properties; -} diff --git a/render_3D/render_3D/Point_Visual.cpp b/render_3D/render_3D/Point_Visual.cpp deleted file mode 100644 index bfb5917..0000000 --- a/render_3D/render_3D/Point_Visual.cpp +++ /dev/null @@ -1,202 +0,0 @@ -#include -#include "Point_Visual.h" -#include "detail/Point_Core.h" -#include "renderable/Renderable_p.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace renderive::render_3d::detail { -namespace { -bool valid_points(const std::vector& points) noexcept { - for (const auto& point : points) { - if (!std::isfinite(point.position.x) || - !std::isfinite(point.position.y) || - !std::isfinite(point.position.z) || - !std::isfinite(point.diameter_px) || point.diameter_px <= 0.0F) - return false; - } - return true; -} -struct Point_Payload_Tag {}; -using Point_Payload = std::shared_ptr>; -using Point_Buffer_Layout = ::Double_Buffer_Layout< - ::Buffered_Data>; -using Point_Buffer_Strategy = -::Multi_Double_Buffer_Strategy< - Point_Buffer_Layout, std::mutex, - Observer_State<::Double_Buffer_Frame_Observer>>; -} // namespace -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) - ::renderive::error::unexpected( - "point stroke width must be finite and nonnegative"); - for (float value : state.transform.values) { - if (!std::isfinite(value)) - ::renderive::error::unexpected( - "point transform must contain finite values"); - } -} -struct Point_Visual::Impl : Base::next_Impl, Point_Buffer_Strategy { - using Observer = Point_Visual::Observer; - static_assert(::renderive::inheritance::Inherits_Chain_Parent< - Impl, Base::next_Impl>); - explicit Impl(std::vector points) { - if (update_points(std::move(points)) != Visual_Data_Error::none) ::renderive::error::unexpected("point payload contains invalid coordinates or diameter"); - } - std::shared_ptr real_time_data_binding; - [[nodiscard]] Visual_Data_Error update_points(std::vector points) { - if (!valid_points(points)) return Visual_Data_Error::invalid_data; - write( - std::make_shared>(std::move(points))); - return Visual_Data_Error::none; - } - [[nodiscard]] Visual_Data_Error edit_points(const std::function&)>& edit) { - if (!edit) return Visual_Data_Error::empty_edit; - const auto cached = latest_snapshot(); - auto next = cached ? *cached : std::vector{}; - edit(next); - return update_points(std::move(next)); - } - [[nodiscard]] const Point_Payload& points() const noexcept { - return render_buffer_value(); - } - [[nodiscard]] std::size_t point_count() const { - const auto cached = latest_snapshot(); - return cached ? cached->size() : 0U; - } - [[nodiscard]] std::uint64_t dependent_state_revision() const noexcept { - return revision(); - } - void capture_frame_data(Frame_Render_Snapshot& snapshot) const override { - const auto& control = static_cast(owner()); - auto input = std::make_shared(); - input->state = control.render_state(); - input->points = points(); - input->state_revision = control.state_revision(); - input->data_revision = buffer_revision(); - snapshot.capture(renderable_id, std::move(input)); - } - void prepare(const Prepare_Render_Context& context) override { - const auto& scene = context.frame.scene_state(); - const auto input = context.captured(); - if (!input) ::renderive::error::unexpected("Point_Visual frame input was not captured"); - auto output = std::make_shared(); - output->state = input->state; - output->family = scene.visual_family; - output->state_revision = input->state_revision; - output->data_revision = input->data_revision; - const auto& source = input->points; - const std::size_t count = source ? source->size() : 0U; - if (count > std::numeric_limits::max()) ::renderive::error::unexpected("Datoviz point payload is too large"); - output->positions.reserve(count); - output->colors.reserve(count); - output->sizes.reserve(count); - if (source) { - for (const auto& point : *source) { - output->positions.push_back( - {point.position.x, point.position.y, point.position.z}); - output->colors.push_back( - { - point.color.red, point.color.green, point.color.blue, - point.color.alpha - }); - output->sizes.push_back(point.diameter_px); - } - } - switch (output->family) { - case Visual_Family::Splat: output->sigma.resize(count); - output->angles.assign(count, 0.0F); - for (std::size_t index = 0; index < count; ++index) - output->sigma[index] = { - output->sizes[index] * 0.35F, - output->sizes[index] * 0.18F - }; - break; - case Visual_Family::Marker: output->angles.assign(count, 0.0F); - output->shapes.assign(count, DVZ_MARKER_SHAPE_DIAMOND); - break; - case Visual_Family::Sphere: for (float& size : output->sizes) size *= 0.0025F; - break; - case Visual_Family::Segment: output->secondary_positions = output->positions; - for (auto& end : output->secondary_positions) end[1] += 0.28F; - output->sizes.assign(count, 4.0F); - break; - case Visual_Family::Vector: output->secondary_positions.assign( - count, {0.18F, 0.28F, 0.0F}); - output->sizes.assign(count, 3.0F); - break; - case Visual_Family::Primitive: - case Visual_Family::Mesh: if (count >= 3) { - output->positions[0] = {-0.62F, -0.42F, 0.0F}; - output->positions[1] = {0.62F, -0.42F, 0.0F}; - output->positions[2] = {0.0F, 0.58F, 0.0F}; - } - output->normals.assign(count, {0.0F, 0.0F, 1.0F}); - break; - case Visual_Family::Path: output->sizes.assign(count, 4.0F); - break; - case Visual_Family::Image: output->extents.assign(count, {0.38F, 0.38F}); - break; - case Visual_Family::Labels: output->positions.assign(1, {0.0F, 0.0F, 0.0F}); - output->extents.assign(1, {1.44F, 1.44F}); - break; - case Visual_Family::Point: - case Visual_Family::Pixel: - case Visual_Family::Glyph: - case Visual_Family::Text: - case Visual_Family::Volume: break; - } - if (context.metrics) { - context.metrics->set(Node_Metric_Kind::input_count, count); - context.metrics->set(Node_Metric_Kind::primitive_count, - output->positions.size()); - } - context.publish(std::move(output)); - } - void handle_observation( - const Renderable_Event_View& observation) noexcept; -}; -void Point_Visual::Impl::handle_observation( - const Renderable_Event_View& observation) noexcept { - if (observation.event == Renderable_Observer_Event::Published) Point_Buffer_Strategy::publish(); -} -Point_Visual::Point_Visual(const State& state, std::vector points) : Renderable(With_Attached_Impl{}, state, std::move(points)) { - State_Validator{}(state); - d_func().real_time_data_binding = - d_func().Point_Buffer_Strategy::bind(*this); -} -Point_Visual::~Point_Visual() = default; -Visual_Data_Error Point_Visual::update_points(std::vector points) { - const auto error = d_func().update_points(std::move(points)); - if (error == Visual_Data_Error::none) d_func().changed(); - return error; -} -Visual_Data_Error Point_Visual::edit_points( - const std::function&)>& edit) { - const auto error = d_func().edit_points(edit); - if (error == Visual_Data_Error::none) d_func().changed(); - return error; -} -std::size_t Point_Visual::point_count() const { - return d_func().point_count(); -} -std::uint64_t Point_Visual::data_revision() const { - return d_func().cache_revision(); -} -Point_Visual::Observer Point_Visual::capture_observation() const { - const auto value = Renderable::render_state(); - Observer result; - result.state = value; - result.point_count = point_count(); - result.data_revision = data_revision(); - return result; -} -} // namespace renderive::render_3d::detail diff --git a/render_3D/render_3D/Point_Visual.h b/render_3D/render_3D/Point_Visual.h deleted file mode 100644 index 8d4d14d..0000000 --- a/render_3D/render_3D/Point_Visual.h +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once -#include "renderable/Inheritance.h" -#include -#include -#include -#include -#include -#include -#include -namespace renderive::render_3d { -struct Vec3 { - float x{}; - float y{}; - float z{}; - bool operator==(const Vec3&) const = default; -}; -struct Rgba8 { - std::uint8_t red{255}; - std::uint8_t green{255}; - std::uint8_t blue{255}; - std::uint8_t alpha{255}; - bool operator==(const Rgba8&) const = default; -}; -struct Matrix4 { - std::array values{ - 1.0F, 0.0F, 0.0F, 0.0F, - 0.0F, 1.0F, 0.0F, 0.0F, - 0.0F, 0.0F, 1.0F, 0.0F, - 0.0F, 0.0F, 0.0F, 1.0F - }; - bool operator==(const Matrix4&) const = default; -}; -struct Point { - Vec3 position; - Rgba8 color; - float diameter_px{8.0F}; - bool operator==(const Point&) const = default; -}; -enum class Visual_Data_Error : std::uint8_t { - none, - invalid_data, - empty_edit -}; -enum class Point_Aspect : std::uint8_t { - Filled, - Stroke, - Outline, -}; -struct Point_Style { - Rgba8 edge_color{0, 0, 0, 255}; - float stroke_width_px{}; - Point_Aspect aspect{Point_Aspect::Filled}; - bool operator==(const Point_Style&) const = default; -}; -namespace detail { -struct Point_Visual : Renderable { - using Base = Renderable; -protected: - struct Builder : Base::template next_Builder {}; -public: - struct State : Base::template next_State { - Point_Style style; - Matrix4 transform; - bool visible{true}; - bool depth_test{true}; - bool operator==(const State& other) const { - return style == other.style && transform == other.transform && - visible == other.visible && depth_test == other.depth_test; - } - }; - struct State_Validator { - void operator()(const State& state) const; - }; - template - using Business_Builder = - ::renderive::render_3d::Renderable_Builder< - Product, Properties, State_Validator>; -private: - friend struct ::renderive::renderable::attach; - explicit Point_Visual(const State& state, - std::vector points = {}); -public: - ~Point_Visual() override; - [[nodiscard]] Visual_Data_Error update_points(std::vector points); - [[nodiscard]] Visual_Data_Error edit_points(const std::function&)>& edit); - [[nodiscard]] std::size_t point_count() const; - [[nodiscard]] std::uint64_t data_revision() const; -protected: - template Value> - void set_state(Value&& value) { - auto next = Renderable::template state(); - next.*Member = std::forward(value); - State_Validator{}(next); - Renderable::template update_state( - [next = std::move(next)](State_Type& target) mutable { - target = std::move(next); - }); - } -private: - struct Impl; -public: - struct Observer : Base::template next_Observer { - State state; - std::size_t point_count{}; - std::uint64_t data_revision{}; - }; -private: - [[nodiscard]] Observer capture_observation() const; -}; -} // namespace detail -using Point_Visual = ::renderive::renderable::attach; -using Point_State = Point_Visual::Properties; -} // namespace renderive::render_3d diff --git a/render_3D/render_3D/Primitive_Visual.h b/render_3D/render_3D/Primitive_Visual.h deleted file mode 100644 index 5d89bc0..0000000 --- a/render_3D/render_3D/Primitive_Visual.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "visuals/Basic_Visual.h" -namespace renderive::render_3d { -struct Primitive_Vertex { - Vec3 position; - Rgba8 color; - Vec3 normal; - bool operator==(const Primitive_Vertex&) const = default; -}; -namespace detail { -struct Primitive_Spec { - using Item = Primitive_Vertex; - using Settings = Primitive_Settings; - static constexpr const char* name = "Primitive"; - static bool valid(const Item& item) noexcept { - return finite(item.position) && finite(item.normal); - } -}; -} -using Primitive_Visual = detail::Attached_Visual; -using Primitive_State = Primitive_Visual::Properties; -} diff --git a/render_3D/render_3D/Render_Scene_3D.cpp b/render_3D/render_3D/Render_Scene_3D.cpp deleted file mode 100644 index ee0cf1c..0000000 --- a/render_3D/render_3D/Render_Scene_3D.cpp +++ /dev/null @@ -1,530 +0,0 @@ -#include -#include "Render_Scene_3D.h" -#include -#include "detail/Datoviz_Visual_Backend.h" -#include "detail/Gpu_Completion_Service.h" -#include "detail/Point_Core.h" -#include "detail/Render_Domain.h" -#include "frame_control/Point_Frame_Control.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace renderive::render_3d { -namespace { -bool valid(Extent extent) noexcept { - return !extent.empty(); -} -bool valid(Clear_Color color) noexcept { - const auto component = [](float value) { - return std::isfinite(value) && value >= 0.0F && value <= 1.0F; - }; - return component(color.red) && component(color.green) && - component(color.blue) && component(color.alpha); -} -float datoviz_wheel_step(float pixel_delta, float angle_delta) noexcept { - return angle_delta != 0.0F - ? angle_delta / 120.0F - : pixel_delta / 100.0F; -} -void publish_trace(Node_Execution_Metrics* metrics, - Node_Diagnostic_Sink diagnostics, - detail::Datoviz_Frame_Trace trace) { - if (metrics) { - metrics->set(Node_Metric_Kind::queue_wait_ns, - trace.render_domain_queue_wait_ns); - metrics->set(Node_Metric_Kind::apply_duration_ns, trace.apply_ns); - metrics->set(Node_Metric_Kind::plan_emit_duration_ns, trace.emit_ns); - metrics->set(Node_Metric_Kind::backend_execute_duration_ns, - trace.execute_ns); - metrics->set(Node_Metric_Kind::submit_duration_ns, trace.submit_ns); - metrics->set(Node_Metric_Kind::gpu_fence_wait_ns, - trace.gpu_fence_wait_ns); - metrics->set(Node_Metric_Kind::readback_duration_ns, - trace.readback_ns); - if (trace.gpu) { - metrics->set(Node_Metric_Kind::gpu_render_duration_ns, - trace.gpu->render_ns); - metrics->set(Node_Metric_Kind::gpu_transition_duration_ns, - trace.gpu->transition_ns); - metrics->set(Node_Metric_Kind::gpu_copy_duration_ns, - trace.gpu->copy_ns); - metrics->set(Node_Metric_Kind::gpu_total_duration_ns, - trace.gpu->total_ns); - } - metrics->set(Node_Metric_Kind::backend_resource_version, - trace.artifact_resource_version); - metrics->set(Node_Metric_Kind::backend_frame_index, - trace.artifact_frame_index); - metrics->set(Node_Metric_Kind::backend_artifact_status, - trace.artifact_status); - metrics->set(Node_Metric_Kind::backend_validation_code, - trace.validation_code); - metrics->set(Node_Metric_Kind::backend_validation_command, - trace.validation_command_index); - metrics->set(Node_Metric_Kind::backend_artifact_json_bytes, - trace.artifact_json.size()); - } - if (!trace.artifact_json.empty()) - diagnostics.attach("datoviz.drp2.artifact.json", - std::move(trace.artifact_json)); -} -struct Point_Backend_State final { - explicit Point_Backend_State(std::uint32_t gpu_index) : render_domain(detail::Render_Domain::acquire(gpu_index)) {} - ~Point_Backend_State() { - if (backend) - static_cast( - render_domain->invoke([this] { - backend.reset(); - })); - } - detail::Datoviz_Visual_Backend& require_backend() { - if (!backend) - ::renderive::error::unexpected( - "Datoviz backend invariant is unavailable"); - return *backend; - } - [[nodiscard]] bool backend_available() const noexcept { - return !quarantined.load(std::memory_order_acquire); - } - void quarantine_backend() { - quarantined.store(true, std::memory_order_release); - static_cast(backend.release()); - std::lock_guard lock(frame_mutex); - latest.reset(); - } - std::shared_ptr render_domain; - std::unique_ptr backend; - std::atomic_bool quarantined{}; - mutable std::mutex frame_mutex; - std::shared_ptr latest; -}; -} // namespace -void detail::Render_Scene_3D::State_Validator::operator()( - const State& state) const { - if (!valid(state.viewport)) - ::renderive::error::unexpected( - "Point_Scene viewport must be nonempty"); - if (!valid(state.clear_color)) - ::renderive::error::unexpected( - "Point_Scene clear color must be in [0, 1]"); -} -struct detail::Render_Scene_3D::Impl -: detail::Render_Scene_3D::Base::next_Impl, - Scene_Base::Impl::Frame_Control_Capability, - Scene_Base::Impl::Scene_State_Capability, - Scene_Base::Impl::Renderer { - using State_Storage = Published_State_Storage< - State, Atomic_Spin_Mutex, Observer_State<>>; - using Observer = detail::Render_Scene_3D::Observer; - static_assert(::renderive::inheritance::Inherits_Chain_Parent< - Impl, detail::Render_Scene_3D::Base::next_Impl>); - Impl(std::pmr::memory_resource& resource, const State& state, - std::uint32_t gpu_index) : Base::next_Impl(resource), scene_state(state), - backend_state( - std::make_shared(gpu_index)) {} - Scene_Control_Error dispatch_pointer( - ::renderive::Event_Type type, float x, - float y, ::renderive::Mouse_Button button, - ::renderive::Keyboard_Modifier modifiers) { - const Extent viewport = - scene_state.template get<&State::viewport>(); - const auto backend = backend_state; - if (!backend->backend_available()) return Scene_Control_Error::backend_unavailable; - const auto invocation = backend->render_domain->invoke( - [backend, type, x, y, button, modifiers, viewport] { - if (!backend->backend_available()) return Scene_Control_Error::backend_unavailable; - backend->require_backend().dispatch_pointer( - type, x, y, button, modifiers, viewport); - return Scene_Control_Error::none; - }); - if (!invocation) return Scene_Control_Error::backend_unavailable; - return *invocation.value; - } - Scene_Control_Error dispatch_wheel( - float x, float y, float delta_x, - float delta_y, ::renderive::Keyboard_Modifier modifiers) { - const Extent viewport = - scene_state.template get<&State::viewport>(); - const auto backend = backend_state; - if (!backend->backend_available()) return Scene_Control_Error::backend_unavailable; - const auto invocation = backend->render_domain->invoke( - [backend, x, y, delta_x, delta_y, modifiers, viewport] { - if (!backend->backend_available()) return Scene_Control_Error::backend_unavailable; - backend->require_backend().dispatch_wheel( - x, y, delta_x, delta_y, modifiers, viewport); - return Scene_Control_Error::none; - }); - if (!invocation) return Scene_Control_Error::backend_unavailable; - return *invocation.value; - } - Scene_Control_Error dispatch_key(const ::renderive::Key_Event& event) { - const auto backend = backend_state; - if (!backend->backend_available()) return Scene_Control_Error::backend_unavailable; - const auto invocation = backend->render_domain->invoke( - [backend, event] { - if (!backend->backend_available()) return Scene_Control_Error::backend_unavailable; - backend->require_backend().dispatch_key(event); - return Scene_Control_Error::none; - }); - if (!invocation) return Scene_Control_Error::backend_unavailable; - return *invocation.value; - } - [[nodiscard]] std::shared_ptr latest_frame() const { - const auto backend = backend_state; - std::lock_guard lock(backend->frame_mutex); - return backend->latest; - } - Node_Execution_Result render_scene( - const Scene_Render_Context& context) { - if (!backend_state->backend_available()) - return Node_Execution_Result::failed( - External_Operation_Result::external_failure); - const auto prepared = - context.prepared(point_id); - if (!prepared) - ::renderive::error::unexpected( - "Point_Visual did not publish prepared data"); - const State scene_state_value = context.frame.scene_state(); - const std::uint64_t scene_revision = - context.frame.scene_state_revision(); - const std::uint64_t frame_sequence = - context.frame.render_sequence; - Node_Execution_Metrics* const metrics = context.metrics; - const Node_Diagnostic_Sink diagnostics = context.diagnostics; - const bool observe = metrics != nullptr; - const auto backend = backend_state; - auto source = std::make_shared(); - const auto operation = source->operation(); - using Pending_Frame = - detail::Datoviz_Visual_Backend::Pending_Frame; - static_assert(std::is_nothrow_move_constructible_v); - using Completion_Result = detail::Gpu_Completion_Service::Result; - static_assert( - std::is_nothrow_move_assignable_v); - struct Async_Frame_State { - std::optional pending; - Completion_Result completion; - }; - auto async_frame = std::make_shared(); - auto prepared_completion = backend->render_domain->prepare( - [backend, async_frame, metrics, diagnostics, source]() mutable { - if (!async_frame->pending) { - static_cast(source->fail( - ::renderive::error::capture( - "GPU completion has no pending frame"))); - return; - } - auto pending = std::move(*async_frame->pending); - async_frame->pending.reset(); - pending.trace.gpu_fence_wait_ns = - async_frame->completion.wait_duration_ns; - if (async_frame->completion.error != - detail::Gpu_Completion_Service::Completion_Error::none) { - try { - backend->quarantine_backend(); - static_cast(source->fail( - External_Operation_Result::external_failure)); - } - catch (...) { - static_cast(source->fail( - ::renderive::error::capture( - "quarantining Point_Scene backend", - std::current_exception()))); - } - return; - } - try { - auto completed = backend->require_backend().collect( - std::move(pending)); - auto frame = std::move(completed.frame); - auto trace = - std::make_shared( - std::move(completed.trace)); - static_cast(source->complete( - [backend, metrics, diagnostics, - frame = std::move(frame), - trace = std::move(trace)]() mutable { - if (metrics && frame) { - metrics->set( - Node_Metric_Kind::pixel_count, - static_cast( - frame->extent.width) * - frame->extent.height); - } - publish_trace(metrics, diagnostics, - std::move(*trace)); - std::lock_guard lock(backend->frame_mutex); - backend->latest = std::move(frame); - })); - } - catch (...) { - static_cast(source->fail( - ::renderive::error::capture( - "collecting Point_Scene frame", - std::current_exception()))); - } - }, - [source](std::exception_ptr exception) { - static_cast(source->fail(std::move(exception))); - }); - if (!prepared_completion) { - static_cast(source->cancel()); - return Node_Execution_Result::external(operation); - } - auto render_completion = - std::make_shared( - std::move(prepared_completion.task)); - auto prepared_gpu_completion = - detail::Gpu_Completion_Service::instance().prepare( - [backend, async_frame, render_completion, source]( - Completion_Result result) { - async_frame->completion = std::move(result); - try { - if (backend->render_domain->post( - std::move(*render_completion)) != - detail::Render_Domain::Admission_Result::none) - static_cast(source->cancel()); - } - catch (...) { - static_cast(source->fail( - ::renderive::error::capture( - "posting Point_Scene completion", - std::current_exception()))); - } - }, - [source](std::exception_ptr exception) { - static_cast(source->fail(std::move(exception))); - }, - observe); - if (!prepared_gpu_completion) { - static_cast(source->cancel()); - return Node_Execution_Result::external(operation); - } - auto completion = - std::make_shared( - std::move(prepared_gpu_completion.reservation)); - const auto queued_at = observe - ? std::chrono::steady_clock::now() - : std::chrono::steady_clock::time_point{}; - try { - const auto post_error = backend->render_domain->post( - [backend, scene_state_value, scene_revision, - frame_sequence, prepared, source, async_frame, - completion, observe, queued_at] { - try { - if (source->operation().status() == - External_Operation_Status::cancelled) - return; - if (!backend->backend_available()) { - static_cast(source->fail( - External_Operation_Result::external_failure)); - return; - } - std::uint64_t queue_wait_ns{}; - if (observe) { - const auto queue_wait = - std::chrono::duration_cast< - std::chrono::nanoseconds>( - std::chrono::steady_clock::now() - - queued_at) - .count(); - queue_wait_ns = queue_wait > 0 - ? static_cast(queue_wait) - : 0; - } - auto pending = backend->require_backend().submit( - scene_state_value, scene_revision, *prepared, - frame_sequence, observe); - if (!pending) { - static_cast(source->complete([backend] { - std::lock_guard lock( - backend->frame_mutex); - backend->latest.reset(); - })); - return; - } - pending->trace.render_domain_queue_wait_ns = - queue_wait_ns; - auto& owned = async_frame->pending.emplace( - std::move(*pending)); - completion->watch(owned.device, owned.fence); - } - catch (...) { - static_cast(source->fail( - ::renderive::error::capture( - "submitting Point_Scene frame", - std::current_exception()))); - } - }, - [source](std::exception_ptr exception) { - static_cast(source->fail(std::move(exception))); - }); - if (post_error != - detail::Render_Domain::Admission_Result::none) - static_cast(source->cancel()); - } - catch (...) { - static_cast(source->fail( - ::renderive::error::capture( - "posting Point_Scene render task", - std::current_exception()))); - } - return Node_Execution_Result::external(operation); - } - void before_scene_state_swap() override { - const auto pending = scene_state.read( - [](const State& value) { - return value; - }); - const auto published = scene_state.published_state(); - if (pending.viewport != published.viewport) invalidate_renderables(); - } - void swap_scene_state() override { - scene_state.publish(); - } - std::shared_ptr frame_control_strategy() override { - return frame_control->runtime(); - } - std::shared_ptr frame_control_strategy() const override { - return frame_control->runtime(); - } - void replace_frame_control_strategy( - std::shared_ptr strategy) override { - frame_control->replace(std::move(strategy)); - } - std::uint64_t acquire_scene_state() override { - return scene_state.state_revision(); - } - void capture_scene_state( - Frame_Render_Snapshot& snapshot) const override { - snapshot.set_scene_state(scene_state.published_state()); - } - State_Storage scene_state; - std::unique_ptr frame_control; - std::shared_ptr visual; - Renderable_Id point_id{}; - std::shared_ptr backend_state; -}; -detail::Render_Scene_3D::Render_Scene_3D( - const State& state, std::shared_ptr visual, - std::shared_ptr frame_strategy, - std::uint32_t gpu_index, bool validation_enabled) : detail::Scene( - *std::pmr::get_default_resource(), - std::make_unique(*std::pmr::get_default_resource(), - state, gpu_index)) { - if (!visual) - ::renderive::error::unexpected( - "Point_Scene requires a Point_Visual"); - auto& impl = d_func(); - impl.frame_control = std::make_unique( - std::move(frame_strategy)); - impl.visual = std::move(visual); - impl.point_id = impl.visual->renderable_id(); - const State initial = impl.scene_state.read( - [](const State& value) { - return value; - }); - const auto backend = impl.backend_state; - const auto invocation = backend->render_domain->invoke( - [backend, gpu_index, validation_enabled, initial] { - backend->backend = - std::make_unique( - gpu_index, validation_enabled, initial); - }); - if (!invocation) - ::renderive::error::unexpected( - "render domain stopped during Point_Scene initialization"); -} -detail::Render_Scene_3D::~Render_Scene_3D() { - d_func().shutdown(); -} -detail::Render_Scene_3D::Observer detail::Render_Scene_3D::capture_observation() const { - const auto& impl = d_func(); - const auto runtime = impl.frame_control->runtime(); - Observer result; - result.state = impl.scene_state.published_state(); - result.renderable_count = Scene_Base::renderable_count(); - result.point_count = impl.visual->point_count(); - result.frequency_hz = runtime->frequency_hz(); - result.next_refresh_interval_ns = runtime->next_refresh_interval_ns(); - result.backend_available = impl.backend_state->backend_available(); - return result; -} -Scene_Control_Error detail::Render_Scene_3D::dispatch( - const ::renderive::Event& event) { - if (const auto* wheel = - dynamic_cast(&event)) { - const auto* pointer = - dynamic_cast(&event); - if (!pointer) - ::renderive::error::unexpected( - "wheel event has no pointer capability"); - const float x = static_cast(pointer->position_x()); - const float y = static_cast(pointer->position_y()); - const float pixel_delta_x = - static_cast(wheel->pixel_delta_x_value()); - const float pixel_delta_y = - static_cast(wheel->pixel_delta_y_value()); - const float angle_delta_x = - static_cast(wheel->angle_delta_x_value()); - const float angle_delta_y = - static_cast(wheel->angle_delta_y_value()); - if (!std::isfinite(x) || !std::isfinite(y) || - !std::isfinite(pixel_delta_x) || - !std::isfinite(pixel_delta_y) || - !std::isfinite(angle_delta_x) || - !std::isfinite(angle_delta_y)) - return Scene_Control_Error::invalid_event; - return d_func().dispatch_wheel( - x, y, - datoviz_wheel_step(pixel_delta_x, angle_delta_x), - datoviz_wheel_step(pixel_delta_y, angle_delta_y), - pointer->keyboard_modifiers()); - } - if (const auto* pointer = - dynamic_cast(&event)) { - const float x = static_cast(pointer->position_x()); - const float y = static_cast(pointer->position_y()); - if (!std::isfinite(x) || !std::isfinite(y)) return Scene_Control_Error::invalid_event; - if (event.type != ::renderive::Event_Type::Pointer_Move && - event.type != ::renderive::Event_Type::Pointer_Press && - event.type != ::renderive::Event_Type::Pointer_Release) - return Scene_Control_Error::invalid_event; - return d_func().dispatch_pointer( - event.type, x, y, pointer->pointer_button(), - pointer->keyboard_modifiers()); - } - if (const auto* key = dynamic_cast(&event)) return d_func().dispatch_key(*key); - return Scene_Control_Error::none; -} -Frame_Request_Result detail::Render_Scene_3D::request_frame() { - const auto requested = request_frame({}); - if (requested != Frame_Request_Result::none) return requested; - switch (wait_for_render()) { - case Scene_Render_Result::none: return Frame_Request_Result::none; - case Scene_Render_Result::cancelled: return Frame_Request_Result::cancelled; - case Scene_Render_Result::deadline_exceeded: - return Frame_Request_Result::deadline_exceeded; - case Scene_Render_Result::external_failure: - return Frame_Request_Result::external_failure; - case Scene_Render_Result::shutting_down: - return Frame_Request_Result::scene_shutting_down; - } - return Frame_Request_Result::external_failure; -} -Frame_Request_Result detail::Render_Scene_3D::request_frame(Scene_Render_Completion completion) { - return d_func().frame_control->request(*this, std::move(completion)); -} -std::shared_ptr detail::Render_Scene_3D::latest_frame() const { - return d_func().latest_frame(); -} -} // namespace renderive::render_3d diff --git a/render_3D/render_3D/Render_Scene_3D.h b/render_3D/render_3D/Render_Scene_3D.h deleted file mode 100644 index e543aa5..0000000 --- a/render_3D/render_3D/Render_Scene_3D.h +++ /dev/null @@ -1,175 +0,0 @@ -#pragma once -#include "Point_Visual.h" -#include -#include -#include -#include -#include -#include -#include -#include -namespace renderive::render_3d { -struct Scene3D_Frame_Data : Abstract_Frame {}; -struct Extent { - std::uint32_t width{}; - std::uint32_t height{}; - [[nodiscard]] constexpr bool empty() const noexcept { - return width == 0 || height == 0; - } - bool operator==(const Extent&) const = default; -}; -struct Clear_Color { - float red{0.035F}; - float green{0.045F}; - float blue{0.07F}; - float alpha{1.0F}; - bool operator==(const Clear_Color&) const = default; -}; -enum class Scene_Control_Error : std::uint8_t { - none, - invalid_event, - backend_unavailable -}; -enum class Frame_Request_Result : std::uint8_t { - none, - painter_unavailable, - no_pending_frame, - renderer_unavailable, - cancelled, - deadline_exceeded, - external_failure, - scene_shutting_down -}; -enum struct Visual_Family : std::uint8_t { - Point, - Splat, - Pixel, - Marker, - Sphere, - Segment, - Vector, - Primitive, - Mesh, - Path, - Image, - Labels, - Glyph, - Text, - Volume, -}; -struct Pixel_Frame { - Extent extent; - std::uint64_t sequence{}; - std::vector rgba8; -}; -namespace detail { -template -struct Scene : ::renderive::scene::scene_base { - using Base = ::renderive::scene::scene_base; - template - using Business_Builder = - ::renderive::scene::Scene_Builder; -protected: - struct Builder : Base::template next_Builder {}; - struct State : Base::template next_State {}; - struct Impl : Base::template next_Impl { - using Base_Impl = typename Base::template next_Impl; - using Base_Impl::Base_Impl; - }; - struct Observer : Base::template next_Observer {}; - class Renderable_Editor : public Base::template next_Editor< - Renderable_Editor> { - using Editor_Base = typename Base::template next_Editor< - Renderable_Editor>; - using Editor_Base::Editor_Base; - }; - template - using next_Builder = ::renderive::scene_inheritance::Builder_Node< - Next, Builder>; - template - using next_State = ::renderive::scene_inheritance::State_Node< - Next, State>; - template - using next_Impl = ::renderive::scene_inheritance::Impl_Node< - Next, Impl>; - template - using next_Observer = ::renderive::scene_inheritance::Observer_Node< - Next, Observer>; - template - using next_Editor = ::renderive::scene_inheritance::Editor_Node< - Next, Renderable_Editor>; - static_assert(::renderive::inheritance::Defines_Chain_Layer, next_Builder<::renderive::inheritance::Chain_Probe>, ::renderive::scene_inheritance::Builder_Node<::renderive::inheritance::Chain_Probe, Builder>>); - static_assert(::renderive::inheritance::Defines_Chain_Layer, next_State<::renderive::inheritance::Chain_Probe>, ::renderive::scene_inheritance::State_Node<::renderive::inheritance::Chain_Probe, State>>); - static_assert(::renderive::inheritance::Defines_Chain_Layer, next_Impl<::renderive::inheritance::Chain_Probe>, ::renderive::scene_inheritance::Impl_Node<::renderive::inheritance::Chain_Probe, Impl>>); - static_assert(::renderive::inheritance::Defines_Chain_Layer, next_Observer<::renderive::inheritance::Chain_Probe>, ::renderive::scene_inheritance::Observer_Node<::renderive::inheritance::Chain_Probe, Observer>>); - static_assert(::renderive::inheritance::Defines_Chain_Layer, next_Editor<::renderive::inheritance::Chain_Probe>, ::renderive::scene_inheritance::Editor_Node<::renderive::inheritance::Chain_Probe, Renderable_Editor>>); - using Base::Base; -}; -// Owns frame scheduling and the single Datoviz mutation/render domain. -class Render_Scene_3D : public Scene { - using Base = Scene; -public: - using Frame = Scene3D_Frame_Data; -protected: - struct Builder : Base::template next_Builder {}; - class Renderable_Editor : public Base::template next_Editor< - Renderable_Editor> { - using Editor_Base = typename Base::template next_Editor< - Renderable_Editor>; - using Editor_Base::Editor_Base; - }; -public: - struct State_Fields { - Extent viewport{560, 320}; - Clear_Color clear_color; - Visual_Family visual_family{Visual_Family::Point}; - }; - struct State : Base::template next_State, State_Fields { - [[nodiscard]] bool operator==(const State& other) const { - return viewport == other.viewport && - clear_color == other.clear_color && - visual_family == other.visual_family; - } - }; - struct State_Validator { - void operator()(const State& state) const; - }; - template - using Business_Builder = ::renderive::scene::Scene_Builder< - Product, Properties, State_Validator>; - struct Observer_Fields { - State state; - std::size_t renderable_count{}; - std::size_t point_count{}; - double frequency_hz{}; - std::uint64_t next_refresh_interval_ns{}; - bool backend_available{}; - }; - struct Observer : Base::template next_Observer, Observer_Fields {}; - [[nodiscard]] Scene_Control_Error dispatch( - const ::renderive::Event& event); - [[nodiscard]] Frame_Request_Result request_frame(); - [[nodiscard]] Frame_Request_Result request_frame(Scene_Render_Completion completion); - [[nodiscard]] std::shared_ptr latest_frame() const; - ~Render_Scene_3D() override; -private: - [[nodiscard]] Observer capture_observation() const; - Render_Scene_3D( - const State& state, std::shared_ptr visual, - std::shared_ptr frame_strategy, - std::uint32_t gpu_index = 0, bool validation_enabled = false); - struct Impl; - friend struct ::renderive::scene::attach; - friend struct ::renderive::scene::scene_base; -}; -} // namespace detail -using Render_Scene_3D = -::renderive::scene::attach; -using Manual_Render_Scene_3D_Strategy = -Manual_Refresh_Strategy; -using Low_Latency_Render_Scene_3D_Strategy = -Low_Latency_Strategy; -using Playback_Render_Scene_3D_Strategy = -Flow_Refresh_Strategy; -} // namespace renderive::render_3d diff --git a/render_3D/render_3D/Segment_Visual.h b/render_3D/render_3D/Segment_Visual.h deleted file mode 100644 index 339fc7b..0000000 --- a/render_3D/render_3D/Segment_Visual.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "visuals/Basic_Visual.h" -namespace renderive::render_3d { -struct Segment { - Vec3 start; - Vec3 end; - Rgba8 color; - float width_px{1.0F}; - bool operator==(const Segment&) const = default; -}; -namespace detail { -struct Segment_Spec { - using Item = Segment; - using Settings = Visual_Settings; - static constexpr const char* name = "Segment"; - static bool valid(const Item& item) noexcept { - return finite(item.start) && finite(item.end) && finite(item.width_px) && - item.width_px > 0; - } -}; -} -using Segment_Visual = detail::Attached_Visual; -using Segment_State = Segment_Visual::Properties; -} diff --git a/render_3D/render_3D/Sphere_Visual.h b/render_3D/render_3D/Sphere_Visual.h deleted file mode 100644 index 5a93d85..0000000 --- a/render_3D/render_3D/Sphere_Visual.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "visuals/Basic_Visual.h" -namespace renderive::render_3d { -struct Sphere { - Vec3 center; - Rgba8 color; - float radius{0.1F}; - bool operator==(const Sphere&) const = default; -}; -namespace detail { -struct Sphere_Spec { - using Item = Sphere; - using Settings = Visual_Settings; - static constexpr const char* name = "Sphere"; - static bool valid(const Item& item) noexcept { - return finite(item.center) && finite(item.radius) && item.radius > 0; - } -}; -} -using Sphere_Visual = detail::Attached_Visual; -using Sphere_State = Sphere_Visual::Properties; -} diff --git a/render_3D/render_3D/Splat_Visual.h b/render_3D/render_3D/Splat_Visual.h deleted file mode 100644 index 40a9474..0000000 --- a/render_3D/render_3D/Splat_Visual.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "visuals/Basic_Visual.h" -namespace renderive::render_3d { -struct Splat { - Vec3 position; - Rgba8 color; - Vec2 sigma{0.05F, 0.05F}; - float angle{}; - bool operator==(const Splat&) const = default; -}; -namespace detail { -struct Splat_Spec { - using Item = Splat; - using Settings = Visual_Settings; - static constexpr const char* name = "Splat"; - static bool valid(const Item& item) noexcept { - return finite(item.position) && finite(item.sigma) && item.sigma.x > 0 && - item.sigma.y > 0 && finite(item.angle); - } -}; -} -using Splat_Visual = detail::Attached_Visual; -using Splat_State = Splat_Visual::Properties; -} diff --git a/render_3D/render_3D/Text_Visual.h b/render_3D/render_3D/Text_Visual.h deleted file mode 100644 index ae73444..0000000 --- a/render_3D/render_3D/Text_Visual.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "visuals/Basic_Visual.h" -#include -namespace renderive::render_3d { -struct Text_Label { - Vec3 position; - std::string text; - Rgba8 color; - float size_px{18.0F}; - bool operator==(const Text_Label&) const = default; -}; -namespace detail { -struct Text_Spec { - using Item = Text_Label; - using Settings = Visual_Settings; - static constexpr const char* name = "Text"; - static bool valid(const Item& item) noexcept { - return finite(item.position) && !item.text.empty() && finite(item.size_px) && - item.size_px > 0; - } -}; -} -using Text_Visual = detail::Attached_Visual; -using Text_State = Text_Visual::Properties; -} diff --git a/render_3D/render_3D/Vector_Visual.h b/render_3D/render_3D/Vector_Visual.h deleted file mode 100644 index 2293693..0000000 --- a/render_3D/render_3D/Vector_Visual.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "visuals/Basic_Visual.h" -namespace renderive::render_3d { -struct Vector_Glyph { - Vec3 origin; - Vec3 direction{1.0F, 0.0F, 0.0F}; - Rgba8 color; - float width_px{1.0F}; - bool operator==(const Vector_Glyph&) const = default; -}; -namespace detail { -struct Vector_Spec { - using Item = Vector_Glyph; - using Settings = Visual_Settings; - static constexpr const char* name = "Vector"; - static bool valid(const Item& item) noexcept { - return finite(item.origin) && finite(item.direction) && - finite(item.width_px) && item.width_px > 0; - } -}; -} -using Vector_Visual = detail::Attached_Visual; -using Vector_State = Vector_Visual::Properties; -} diff --git a/render_3D/render_3D/Volume_Visual.h b/render_3D/render_3D/Volume_Visual.h deleted file mode 100644 index 17a3f44..0000000 --- a/render_3D/render_3D/Volume_Visual.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "visuals/Basic_Visual.h" -namespace renderive::render_3d { -struct Voxel { - float value{}; - bool operator==(const Voxel&) const = default; -}; -namespace detail { -struct Volume_Spec { - using Item = Voxel; - using Settings = Volume_Field_Settings; - static constexpr const char* name = "Volume"; - static bool valid(const Item& item) noexcept { - return finite(item.value); - } -}; -} -using Volume_Visual = detail::Attached_Visual; -using Volume_State = Volume_Visual::Properties; -} diff --git a/render_3D/render_3D/detail/Datoviz_Frame_Trace.h b/render_3D/render_3D/detail/Datoviz_Frame_Trace.h deleted file mode 100644 index f6f5506..0000000 --- a/render_3D/render_3D/detail/Datoviz_Frame_Trace.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include -#include -#include -namespace renderive::render_3d::detail { -struct Datoviz_Gpu_Timing { - std::uint64_t render_ns{}; - std::uint64_t transition_ns{}; - std::uint64_t copy_ns{}; - std::uint64_t total_ns{}; -}; -struct Datoviz_Frame_Trace { - std::uint64_t render_sequence{}; - bool observed{}; - std::uint64_t render_domain_queue_wait_ns{}; - std::uint64_t apply_ns{}; - std::uint64_t emit_ns{}; - std::uint64_t execute_ns{}; - std::uint64_t submit_ns{}; - std::uint64_t gpu_fence_wait_ns{}; - std::uint64_t readback_ns{}; - std::optional gpu; - std::uint64_t artifact_resource_version{}; - std::uint64_t artifact_frame_index{}; - std::uint32_t artifact_status{}; - std::uint32_t validation_code{}; - std::uint64_t validation_command_index{}; - bool validation_ok{}; - std::string artifact_json; -}; -} // namespace renderive::render_3d::detail diff --git a/render_3D/render_3D/detail/Datoviz_Visual_Backend.cpp b/render_3D/render_3D/detail/Datoviz_Visual_Backend.cpp deleted file mode 100644 index 71b1997..0000000 --- a/render_3D/render_3D/detail/Datoviz_Visual_Backend.cpp +++ /dev/null @@ -1,1096 +0,0 @@ -#include -#include "Datoviz_Visual_Backend.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace renderive::render_3d::detail { -namespace { -constexpr std::uint64_t color_target_id = 0x5256504f494e54ULL; -std::uint64_t trace_now_ns() noexcept { - return static_cast( - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()).count()); -} -template -Resource* allocate_wrapper(Allocate allocate, const char* message) { - Resource* resource = allocate(); - if (resource == nullptr) ::renderive::error::unexpected(message); - return resource; -} -int modifiers(::renderive::Keyboard_Modifier value) { - const auto bits = static_cast(value); - int result = DVZ_KEY_MODIFIER_NONE; - if ((bits & static_cast(::renderive::Keyboard_Modifier::Shift)) != 0) result |= DVZ_KEY_MODIFIER_SHIFT; - if ((bits & static_cast(::renderive::Keyboard_Modifier::Ctrl)) != 0) result |= DVZ_KEY_MODIFIER_CONTROL; - if ((bits & static_cast(::renderive::Keyboard_Modifier::Alt)) != 0) result |= DVZ_KEY_MODIFIER_ALT; - if ((bits & static_cast(::renderive::Keyboard_Modifier::Meta)) != 0) result |= DVZ_KEY_MODIFIER_SUPER; - return result; -} -DvzPointerButton button(::renderive::Mouse_Button value) { - switch (value) { - case ::renderive::Mouse_Button::Left: return DVZ_POINTER_BUTTON_LEFT; - case ::renderive::Mouse_Button::Middle: return DVZ_POINTER_BUTTON_MIDDLE; - case ::renderive::Mouse_Button::Right: return DVZ_POINTER_BUTTON_RIGHT; - case ::renderive::Mouse_Button::None: return DVZ_POINTER_BUTTON_NONE; - } - return DVZ_POINTER_BUTTON_NONE; -} -DvzKeyCode key_code(::renderive::Key key, std::uint32_t native_key) { - switch (key) { - case ::renderive::Key::Escape: return DVZ_KEY_ESCAPE; - case ::renderive::Key::Enter: return DVZ_KEY_ENTER; - case ::renderive::Key::Space: return DVZ_KEY_SPACE; - case ::renderive::Key::Delete: return DVZ_KEY_DELETE; - case ::renderive::Key::Backspace: return DVZ_KEY_BACKSPACE; - case ::renderive::Key::Left: return DVZ_KEY_LEFT; - case ::renderive::Key::Right: return DVZ_KEY_RIGHT; - case ::renderive::Key::Up: return DVZ_KEY_UP; - case ::renderive::Key::Down: return DVZ_KEY_DOWN; - case ::renderive::Key::Unknown: break; - } - return native_key <= static_cast(DVZ_KEY_LAST) - ? static_cast(native_key) - : DVZ_KEY_UNKNOWN; -} -DvzShapeAspect aspect(Point_Aspect value) { - switch (value) { - case Point_Aspect::Filled: return DVZ_SHAPE_ASPECT_FILLED; - case Point_Aspect::Stroke: return DVZ_SHAPE_ASPECT_STROKE; - case Point_Aspect::Outline: return DVZ_SHAPE_ASPECT_OUTLINE; - } - return DVZ_SHAPE_ASPECT_FILLED; -} -void configure_glyph_text(DvzScene* scene, DvzVisual* visual, const char* text) { - auto font_descriptor = dvz_font_desc(); - font_descriptor.family = "Roboto"; - font_descriptor.style = "Regular"; - auto* font = dvz_font(scene, &font_descriptor); - auto atlas_specification = - dvz_text_atlas_spec(DVZ_TEXT_RENDERER_MSDF_ATLAS, 64.0F); - if (font == nullptr || - !dvz_font_atlas_ensure_string(font, &atlas_specification, text)) - ::renderive::error::unexpected("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) ::renderive::error::unexpected("failed to bind Datoviz text atlas"); - float line_width = 0.0F; - for (const auto* character = text; *character != '\0'; ++character) { - const auto* glyph = - dvz_text_atlas_glyph(atlas, static_cast(*character)); - if (glyph != nullptr) line_width += glyph->advance; - } - std::vector> positions; - std::vector> bounds; - std::vector> texture_coordinates; - std::vector> colors; - std::vector angles; - const auto atlas_info = dvz_text_atlas_info(atlas); - float cursor_x = 0.0F; - std::size_t glyph_index = 0; - for (const auto* character = text; *character != '\0'; ++character) { - const auto* glyph = - dvz_text_atlas_glyph(atlas, static_cast(*character)); - if (glyph == nullptr) continue; - const float x0 = cursor_x + glyph->xoff - 0.5F * line_width; - const float y0 = 0.5F * atlas_info.ascent + glyph->yoff; - const std::array glyph_bounds{ - x0, y0, x0 + glyph->width, y0 + glyph->height - }; - const std::array glyph_texture{ - glyph->uv[0], glyph->uv[1], glyph->uv[2], glyph->uv[3] - }; - const std::array glyph_color = - glyph_index % 2 == 0 - ? std::array{40, 235, 205, 255} - : std::array{255, 190, 80, 255}; - for (std::uint32_t vertex = 0; vertex < 6; ++vertex) { - positions.push_back({0.0F, 0.0F, 0.0F}); - bounds.push_back(glyph_bounds); - texture_coordinates.push_back(glyph_texture); - colors.push_back(glyph_color); - angles.push_back(0.0F); - } - cursor_x += glyph->advance; - ++glyph_index; - } - if (positions.empty()) ::renderive::error::unexpected("Datoviz text atlas contains no visible glyphs"); - const auto count = static_cast(positions.size()); - const std::array updates{ - { - {"position", positions.data(), count}, {"bounds", bounds.data(), count}, - {"texcoords", texture_coordinates.data(), count}, - {"color", colors.data(), count}, {"angle", angles.data(), count} - } - }; - if (dvz_visual_set_data_many(visual, updates.data(), - static_cast(updates.size())) != DVZ_OK || - dvz_visual_set_depth_test(visual, false) != DVZ_OK) - ::renderive::error::unexpected("failed to upload Datoviz text geometry"); -} -} // namespace -class Datoviz_Visual_Backend::Frame_Target final { -public: - struct Collection { - std::vector pixels; - std::optional gpu_timing; - }; - 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()) ::renderive::error::unexpected("invalid Datoviz point frame target"); - const std::uint64_t byte_size = static_cast(extent.width) * - extent.height * 4ULL; - if (byte_size > std::numeric_limits::max()) ::renderive::error::unexpected("Datoviz point frame target is too large"); - byte_size_ = static_cast(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) ::renderive::error::unexpected("Datoviz GPU context is incomplete"); - try { - image_ = allocate_wrapper(dvz_images_create_wrapper, - "failed to allocate Datoviz image"); - dvz_images(device, allocator, VK_IMAGE_TYPE_2D, 1, image_); - dvz_images_format(image_, VK_FORMAT_R8G8B8A8_UNORM); - dvz_images_size(image_, extent.width, extent.height, 1); - dvz_images_tiling(image_, VK_IMAGE_TILING_OPTIMAL); - dvz_images_usage(image_, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_TRANSFER_SRC_BIT); - dvz_images_alloc_flags(image_, DVZ_ALLOC_FLAGS_NONE); - if (dvz_images_create(image_) != 0) ::renderive::error::unexpected("failed to create Datoviz point image"); - view_ = allocate_wrapper(dvz_image_views_create_wrapper, - "failed to allocate Datoviz image view"); - dvz_image_views(image_, view_); - dvz_image_views_type(view_, VK_IMAGE_VIEW_TYPE_2D); - dvz_image_views_aspect(view_, VK_IMAGE_ASPECT_COLOR_BIT); - dvz_image_views_mip(view_, 0, 1); - dvz_image_views_layers(view_, 0, 1); - if (dvz_image_views_create(view_) != 0) ::renderive::error::unexpected("failed to create Datoviz point image view"); - commands_ = allocate_wrapper(dvz_commands_create_wrapper, - "failed to allocate Datoviz commands"); - dvz_commands(device, queue, 1, commands_); - if (dvz_commands_handle(commands_) == VK_NULL_HANDLE) ::renderive::error::unexpected("failed to create Datoviz command buffer"); - fence_ = allocate_wrapper(dvz_fence_create_wrapper, - "failed to allocate Datoviz fence"); - dvz_fence(device, true, fence_); - if (dvz_fence_handle(fence_) == VK_NULL_HANDLE) ::renderive::error::unexpected("failed to create Datoviz fence"); - submit_ = allocate_wrapper(dvz_submit_create_wrapper, - "failed to allocate Datoviz submit"); - readback_ = allocate_wrapper(dvz_buffer_create_wrapper, - "failed to allocate Datoviz readback"); - dvz_buffer(device, allocator, readback_); - dvz_buffer_size(readback_, byte_size_); - 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) ::renderive::error::unexpected("failed to create Datoviz readback buffer"); - } - catch (...) { - destroy(); - ::renderive::error::unexpected( - "creating Datoviz frame target", std::current_exception()); - } - } - ~Frame_Target() noexcept(false) { - destroy(); - } - void begin(bool observe) { - if (in_flight_) ::renderive::error::unexpected("Datoviz frame target is still in flight"); - observing_ = observe; - if (observing_ && !timestamps_initialized_) { - initialize_timestamps( - dvz_gpu_ctx_device(gpu_context_), - dvz_gpu_ctx_queue(gpu_context_, DVZ_QUEUE_MAIN)); - } - dvz_cmd_reset(commands_); - if (dvz_cmd_begin_result(commands_) != 0) ::renderive::error::unexpected("failed to begin Datoviz command buffer"); - DvzBarriers barriers{}; - dvz_barriers(&barriers); - auto* image_barrier = dvz_barriers_image(&barriers, dvz_image_handle(image_, 0)); - if (completed_layout_ == VK_IMAGE_LAYOUT_UNDEFINED) { - dvz_barrier_image_stage(image_barrier, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT); - dvz_barrier_image_access( - image_barrier, 0, - VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT); - } - else { - dvz_barrier_image_stage(image_barrier, VK_PIPELINE_STAGE_2_TRANSFER_BIT, - VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT); - dvz_barrier_image_access( - image_barrier, VK_ACCESS_2_TRANSFER_READ_BIT, - VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT); - } - dvz_barrier_image_layout(image_barrier, completed_layout_, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - dvz_barrier_image_aspect(image_barrier, VK_IMAGE_ASPECT_COLOR_BIT); - dvz_barrier_image_mip(image_barrier, 0, 1); - dvz_barrier_image_layers(image_barrier, 0, 1); - dvz_cmd_barriers(commands_, &barriers); - if (observing_ && timestamps_supported_) { - const VkCommandBuffer command_buffer = - dvz_commands_handle(commands_); - vkCmdResetQueryPool(command_buffer, query_pool_, 0, 4); - vkCmdWriteTimestamp( - command_buffer, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - query_pool_, 0); - } - recording_ = true; - } - [[nodiscard]] DvzStreamFrame stream_frame() const { - DvzStreamFrame frame{}; - frame.image = dvz_image_handle(image_, 0); - frame.command_buffer = dvz_commands_handle(commands_); - frame.image_view = dvz_image_views_handle(view_, 0); - frame.extent = {extent_.width, extent_.height}; - frame.color_format = VK_FORMAT_R8G8B8A8_UNORM; - frame.image_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - frame.usage = DVZ_STREAM_FRAME_USAGE_RENDER_TARGET | DVZ_STREAM_FRAME_USAGE_COPY_SRC; - frame.command_buffer_recording = recording_; - frame.image_borrowed = true; - frame.image_view_borrowed = true; - frame.command_buffer_borrowed = true; - frame.handles_dirty = true; - frame.resource_generation = generation_; - frame.image_valid = true; - frame.memory_fd = -1; - frame.wait_semaphore_fd = -1; - return frame; - } - void submit() { - if (!recording_) ::renderive::error::unexpected("Datoviz frame target is not recording"); - const VkCommandBuffer command_buffer = dvz_commands_handle(commands_); - if (observing_ && timestamps_supported_) { - vkCmdWriteTimestamp( - command_buffer, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - query_pool_, 1); - } - DvzBarriers image_barriers{}; - dvz_barriers(&image_barriers); - auto* image_barrier = - dvz_barriers_image(&image_barriers, dvz_image_handle(image_, 0)); - dvz_barrier_image_stage(image_barrier, - VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_2_TRANSFER_BIT); - dvz_barrier_image_access(image_barrier, - VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_2_TRANSFER_READ_BIT); - dvz_barrier_image_layout(image_barrier, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - dvz_barrier_image_aspect(image_barrier, VK_IMAGE_ASPECT_COLOR_BIT); - dvz_barrier_image_mip(image_barrier, 0, 1); - dvz_barrier_image_layers(image_barrier, 0, 1); - dvz_cmd_barriers(commands_, &image_barriers); - if (observing_ && timestamps_supported_) { - vkCmdWriteTimestamp(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, - query_pool_, 2); - } - DvzImageRegion region{}; - dvz_image_region(®ion); - dvz_image_region_extent(®ion, extent_.width, extent_.height, 1); - dvz_cmd_copy_image_to_buffer( - commands_, dvz_image_handle(image_, 0), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, ®ion, - dvz_buffer_handle(readback_), 0); - DvzBarriers buffer_barriers{}; - dvz_barriers(&buffer_barriers); - auto* buffer_barrier = dvz_barriers_buffer( - &buffer_barriers, dvz_buffer_handle(readback_), 0, byte_size_); - dvz_barrier_buffer_stage(buffer_barrier, VK_PIPELINE_STAGE_2_TRANSFER_BIT, - VK_PIPELINE_STAGE_2_HOST_BIT); - dvz_barrier_buffer_access(buffer_barrier, VK_ACCESS_2_TRANSFER_WRITE_BIT, - VK_ACCESS_2_HOST_READ_BIT); - dvz_cmd_barriers(commands_, &buffer_barriers); - if (observing_ && timestamps_supported_) { - vkCmdWriteTimestamp(command_buffer, - VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - query_pool_, 3); - } - if (dvz_cmd_end_result(commands_) != 0) ::renderive::error::unexpected("failed to end Datoviz command buffer"); - recording_ = false; - dvz_fence_reset(fence_); - dvz_submit(submit_); - dvz_submit_command(submit_, dvz_commands_handle(commands_)); - 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) - ::renderive::error::unexpected("failed to submit Datoviz point frame"); - in_flight_ = true; - completed_layout_ = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - } - [[nodiscard]] Collection collect() { - if (!in_flight_) ::renderive::error::unexpected("Datoviz frame target has no pending frame"); - Collection result; - try { - result.pixels.resize(static_cast(byte_size_)); - dvz_buffer_download(readback_, 0, byte_size_, result.pixels.data()); - result.gpu_timing = collect_gpu_timing(); - } - catch (...) { - in_flight_ = false; - observing_ = false; - ::renderive::error::unexpected( - "submitting Datoviz frame target", std::current_exception()); - } - in_flight_ = false; - observing_ = false; - return result; - } - void discard_after_completion() { - if (!in_flight_) ::renderive::error::unexpected("Datoviz frame target has no pending frame"); - in_flight_ = false; - observing_ = false; - } - [[nodiscard]] VkDevice device() const { - return dvz_device_handle(dvz_gpu_ctx_device(gpu_context_)); - } - [[nodiscard]] VkFence fence() const { - return dvz_fence_handle(fence_); - } - [[nodiscard]] std::uint64_t generation() const noexcept { - return generation_; - } - void abort() noexcept { - if (recording_ && commands_ != nullptr) dvz_cmd_reset(commands_); - recording_ = false; - observing_ = false; - } -private: - void initialize_timestamps(DvzDevice* device, DvzQueue* queue) noexcept { - timestamps_initialized_ = true; - if (device == nullptr || queue == nullptr || - vkGetPhysicalDeviceQueueFamilyProperties == nullptr || - vkGetPhysicalDeviceProperties == nullptr || - vkCreateQueryPool == nullptr || - vkCmdResetQueryPool == nullptr || - vkCmdWriteTimestamp == nullptr || - vkGetQueryPoolResults == nullptr) - return; - const VkPhysicalDevice physical = - dvz_device_physical_device(device); - const VkDevice logical = dvz_device_handle(device); - if (physical == VK_NULL_HANDLE || logical == VK_NULL_HANDLE) return; - std::uint32_t family_count{}; - vkGetPhysicalDeviceQueueFamilyProperties( - physical, &family_count, nullptr); - if (family_count == 0) return; - std::vector families(family_count); - vkGetPhysicalDeviceQueueFamilyProperties( - physical, &family_count, families.data()); - const std::uint32_t family = dvz_queue_family(queue); - if (family >= family_count || - families[family].timestampValidBits == 0) - return; - VkPhysicalDeviceProperties properties{}; - vkGetPhysicalDeviceProperties(physical, &properties); - VkQueryPoolCreateInfo configuration{ - VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO - }; - configuration.queryType = VK_QUERY_TYPE_TIMESTAMP; - configuration.queryCount = 4; - if (vkCreateQueryPool(logical, &configuration, nullptr, - &query_pool_) != VK_SUCCESS) { - query_pool_ = VK_NULL_HANDLE; - return; - } - timestamp_period_ns_ = properties.limits.timestampPeriod; - timestamp_valid_bits_ = families[family].timestampValidBits; - timestamps_supported_ = timestamp_period_ns_ > 0.0F; - } - [[nodiscard]] std::optional collect_gpu_timing() const noexcept { - if (!observing_ || !timestamps_supported_ || - query_pool_ == VK_NULL_HANDLE) - return std::nullopt; - const VkDevice device = - dvz_device_handle(dvz_gpu_ctx_device(gpu_context_)); - std::array timestamps{}; - if (vkGetQueryPoolResults( - device, query_pool_, 0, - static_cast(timestamps.size()), - sizeof(timestamps), timestamps.data(), sizeof(std::uint64_t), - VK_QUERY_RESULT_64_BIT) != VK_SUCCESS) - return std::nullopt; - const auto elapsed = [this](std::uint64_t begin, - std::uint64_t end) noexcept { - std::uint64_t ticks = end - begin; - if (timestamp_valid_bits_ < 64) { - const std::uint64_t mask = - (std::uint64_t{1} << timestamp_valid_bits_) - 1; - ticks &= mask; - } - const long double nanoseconds = - static_cast(ticks) * timestamp_period_ns_; - return nanoseconds >= - static_cast( - std::numeric_limits::max()) - ? std::numeric_limits::max() - : static_cast(nanoseconds); - }; - return Datoviz_Gpu_Timing{ - elapsed(timestamps[0], timestamps[1]), - elapsed(timestamps[1], timestamps[2]), - elapsed(timestamps[2], timestamps[3]), - elapsed(timestamps[0], timestamps[3]) - }; - } - void destroy() { - if (in_flight_) - ::renderive::error::unexpected( - "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_)), - query_pool_, nullptr); - query_pool_ = VK_NULL_HANDLE; - } - if (readback_ != nullptr) { - dvz_buffer_destroy(readback_); - dvz_buffer_free(readback_); - readback_ = nullptr; - } - if (fence_ != nullptr) { - dvz_fence_destroy(fence_); - dvz_fence_free(fence_); - fence_ = nullptr; - } - if (submit_ != nullptr) { - dvz_submit_free(submit_); - submit_ = nullptr; - } - if (commands_ != nullptr) { - dvz_commands_destroy(commands_); - dvz_commands_free(commands_); - commands_ = nullptr; - } - if (view_ != nullptr) { - dvz_image_views_destroy(view_); - dvz_image_views_free(view_); - view_ = nullptr; - } - if (image_ != nullptr) { - dvz_images_destroy(image_); - dvz_images_free(image_); - image_ = nullptr; - } - } - DvzGpuCtx* gpu_context_{}; - Extent extent_{}; - std::uint64_t generation_{}; - DvzSize byte_size_{}; - DvzImages* image_{}; - DvzImageViews* view_{}; - DvzCommands* commands_{}; - DvzFence* fence_{}; - DvzSubmit* submit_{}; - DvzBuffer* readback_{}; - VkQueryPool query_pool_{VK_NULL_HANDLE}; - VkImageLayout completed_layout_{VK_IMAGE_LAYOUT_UNDEFINED}; - float timestamp_period_ns_{}; - std::uint32_t timestamp_valid_bits_{}; - bool recording_{}; - bool in_flight_{}; - bool observing_{}; - bool timestamps_initialized_{}; - bool timestamps_supported_{}; -}; -Datoviz_Visual_Backend::Datoviz_Visual_Backend( - std::uint32_t gpu_index, bool validation_enabled, - const Scene_State& initial_scene) : domain_thread_(std::this_thread::get_id()) { - try { - DvzGpuCtxConfig configuration = dvz_gpu_ctx_config(); - dvz_gpu_ctx_config_validation(&configuration, validation_enabled); - dvz_gpu_ctx_config_gpu(&configuration, gpu_index); - dvz_gpu_ctx_config_enable_canvas_extensions(&configuration, false); - gpu_context_ = dvz_gpu_ctx(&configuration); - if (gpu_context_ == nullptr) ::renderive::error::unexpected("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) ::renderive::error::unexpected("failed to create Datoviz DRP2 runtime"); - create_scene(initial_scene); - } - catch (...) { - destroy(); - ::renderive::error::unexpected( - "creating Datoviz backend", std::current_exception()); - } -} -Datoviz_Visual_Backend::~Datoviz_Visual_Backend() noexcept(false) { - destroy(); -} -void Datoviz_Visual_Backend::require_domain() const { - if (std::this_thread::get_id() != domain_thread_) ::renderive::error::unexpected("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) ::renderive::error::unexpected("failed to create Datoviz scene"); - const auto capabilities = dvz_capability_snapshot(); - if (dvz_scene_set_capabilities(scene_, &capabilities) != DVZ_OK) ::renderive::error::unexpected("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; - if (panel_ != nullptr) { - switch (family) { - case Visual_Family::Point: visual_ = dvz_point(scene_, 0); - break; - case Visual_Family::Splat: visual_ = dvz_splat(scene_, 0); - break; - case Visual_Family::Pixel: visual_ = dvz_pixel(scene_, 0); - break; - case Visual_Family::Marker: visual_ = dvz_marker(scene_, 0); - break; - case Visual_Family::Sphere: visual_ = dvz_sphere(scene_, 0); - break; - case Visual_Family::Segment: visual_ = dvz_segment(scene_, 0); - break; - case Visual_Family::Vector: visual_ = dvz_vector(scene_, 0); - break; - case Visual_Family::Primitive: visual_ = dvz_primitive( - scene_, DVZ_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0); - break; - case Visual_Family::Mesh: visual_ = dvz_mesh(scene_, 0); - break; - case Visual_Family::Path: visual_ = dvz_path(scene_, 0); - break; - case Visual_Family::Image: visual_ = dvz_image(scene_, 0); - break; - case Visual_Family::Labels: visual_ = dvz_labels(scene_, 0); - break; - case Visual_Family::Glyph: - case Visual_Family::Text: visual_ = dvz_glyph(scene_, 0); - break; - case Visual_Family::Volume: visual_ = dvz_volume(scene_, 0); - break; - } - } - if (visual_ != nullptr && - dvz_visual_set_alpha_mode(visual_, DVZ_ALPHA_OPAQUE) != DVZ_OK) - ::renderive::error::unexpected("failed to configure Datoviz visual alpha mode"); - if (visual_ != nullptr && family == Visual_Family::Glyph) configure_glyph_text(scene_, visual_, "GLYPH"); - if (visual_ != nullptr && family == Visual_Family::Text) configure_glyph_text(scene_, visual_, "TEXT"); - if (visual_ != nullptr && - (family == Visual_Family::Image || family == Visual_Family::Glyph || - family == Visual_Family::Text)) { - constexpr std::uint32_t width = 32; - constexpr std::uint32_t height = 32; - std::vector> pixels(width * height); - for (std::uint32_t y = 0; y < height; ++y) { - for (std::uint32_t x = 0; x < width; ++x) { - const bool stroke = family == Visual_Family::Text - ? (y < 6 || (x >= 13 && x <= 18)) - : ((x / 8 + y / 8) % 2 == 0); - pixels[y * width + x] = stroke - ? std::array{40, 235, 205, 255} - : std::array{18, 42, 72, 255}; - } - } - auto descriptor = dvz_sampled_field_desc(); - descriptor.dim = DVZ_FIELD_DIM_2D; - descriptor.format = DVZ_FIELD_FORMAT_RGBA8_UNORM; - descriptor.semantic = DVZ_FIELD_SEMANTIC_COLOR; - descriptor.color_role = DVZ_COLOR_ROLE_SRGB_COLOR; - descriptor.width = width; - descriptor.height = height; - descriptor.depth = 1; - auto* field = dvz_sampled_field(scene_, &descriptor); - auto view = dvz_field_data_view(); - view.data = pixels.data(); - view.bytes_per_row = width * sizeof(pixels.front()); - 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) - ::renderive::error::unexpected("failed to create Datoviz 2D sampled field"); - } - if (visual_ != nullptr && family == Visual_Family::Labels) { - constexpr std::uint32_t width = 8; - constexpr std::uint32_t height = 8; - std::array labels{}; - for (std::uint32_t y = 0; y < height; ++y) - for (std::uint32_t x = 0; x < width; ++x) labels[y * width + x] = static_cast((x / 4) + 2 * (y / 4)); - auto descriptor = dvz_sampled_field_desc(); - descriptor.dim = DVZ_FIELD_DIM_2D; - descriptor.format = DVZ_FIELD_FORMAT_R32_SINT; - descriptor.semantic = DVZ_FIELD_SEMANTIC_LABEL; - descriptor.color_role = DVZ_COLOR_ROLE_DATA; - descriptor.width = width; - descriptor.height = height; - descriptor.depth = 1; - auto* field = dvz_sampled_field(scene_, &descriptor); - auto view = dvz_field_data_view(); - view.data = labels.data(); - view.bytes_per_row = width * sizeof(labels.front()); - view.rows_per_image = height; - auto scale_descriptor = dvz_scale_desc(); - scale_descriptor.kind = DVZ_SCALE_CATEGORICAL; - auto* scale = dvz_scale(scene_, &scale_descriptor); - const std::array categories{ - { - {.category_id = 0, .order = 0, .label = "north west", .color = {235, 70, 70, 255}}, - {.category_id = 1, .order = 1, .label = "north east", .color = {70, 220, 100, 255}}, - {.category_id = 2, .order = 2, .label = "south west", .color = {70, 120, 245, 255}}, - {.category_id = 3, .order = 3, .label = "south east", .color = {245, 210, 55, 255}}, - } - }; - if (field == nullptr || scale == nullptr || - dvz_sampled_field_set_data(field, &view) != DVZ_OK || - dvz_visual_set_field(visual_, "field", field) != DVZ_OK || - dvz_scale_set_categories(scale, categories.data(), - static_cast(categories.size())) != DVZ_OK || - dvz_visual_set_scale(visual_, "labels", scale) != DVZ_OK) - ::renderive::error::unexpected("failed to create Datoviz label field"); - } - if (visual_ != nullptr && family == Visual_Family::Volume) { - constexpr std::uint32_t side = 16; - std::vector voxels(side * side * side); - for (std::uint32_t z = 0; z < side; ++z) { - for (std::uint32_t y = 0; y < side; ++y) { - for (std::uint32_t x = 0; x < side; ++x) { - const float dx = static_cast(x) - 7.5F; - const float dy = static_cast(y) - 7.5F; - const float dz = static_cast(z) - 7.5F; - const float distance = std::sqrt(dx * dx + dy * dy + dz * dz); - voxels[(z * side + y) * side + x] = - distance < 6.5F ? static_cast(255 - distance * 22) : 0; - } - } - } - auto descriptor = dvz_sampled_field_desc(); - descriptor.dim = DVZ_FIELD_DIM_3D; - descriptor.format = DVZ_FIELD_FORMAT_R8_UNORM; - descriptor.semantic = DVZ_FIELD_SEMANTIC_SCALAR; - descriptor.color_role = DVZ_COLOR_ROLE_DATA; - descriptor.width = side; - descriptor.height = side; - descriptor.depth = side; - auto* field = dvz_sampled_field(scene_, &descriptor); - auto view = dvz_field_data_view(); - view.data = voxels.data(); - view.bytes_per_row = side; - view.rows_per_image = side; - if (field == nullptr || dvz_sampled_field_set_data(field, &view) != DVZ_OK || - 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) - ::renderive::error::unexpected("failed to create Datoviz volume field"); - } - if (figure_ == nullptr || panel_ == nullptr || visual_ == nullptr || - dvz_panel_add_visual(panel_, visual_, nullptr) != DVZ_OK) - ::renderive::error::unexpected("failed to create Datoviz visual family"); - DvzCameraDesc camera = dvz_camera_desc(); - camera.view.eye[0] = 0.0F; - camera.view.eye[1] = 0.0F; - camera.view.eye[2] = 4.0F; - camera.view.target[0] = 0.0F; - camera.view.target[1] = 0.0F; - camera.view.target[2] = 0.0F; - camera.projection.near_clip = 0.01F; - camera.projection.far_clip = 100.0F; - if (dvz_panel_set_camera_desc(panel_, &camera) != DVZ_OK) ::renderive::error::unexpected("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) - ::renderive::error::unexpected("failed to bind Datoviz arcball controller"); - input_router_ = dvz_input_router(); - gesture_handler_ = input_router_ != nullptr - ? dvz_pointer_gesture_handler(input_router_) - : nullptr; - if (input_router_ == nullptr || gesture_handler_ == nullptr || - dvz_panel_connect_input(panel_, input_router_) != DVZ_OK) - ::renderive::error::unexpected("failed to connect Datoviz point input"); - DvzInputResizeEvent resize{ - initial_scene.viewport.width, initial_scene.viewport.height, - initial_scene.viewport.width, initial_scene.viewport.height, 1.0F, 1.0F - }; - dvz_input_emit_resize(input_router_, &resize); -} -void Datoviz_Visual_Backend::apply( - const Scene_State& scene, std::uint64_t scene_revision, - const Prepared_Point& point) { - require_domain(); - if (scene_revision != applied_scene_revision_) { - if (dvz_figure_resize(figure_, scene.viewport.width, - scene.viewport.height) != DVZ_OK) - ::renderive::error::unexpected("failed to resize Datoviz point figure"); - DvzInputResizeEvent resize{ - scene.viewport.width, scene.viewport.height, - scene.viewport.width, scene.viewport.height, 1.0F, 1.0F - }; - dvz_input_emit_resize(input_router_, &resize); - applied_scene_revision_ = scene_revision; - } - if (point.state_revision != applied_state_revision_) { - const auto& state = point.state; - mat4 transform{}; - for (std::size_t row = 0; row < 4; ++row) - for (std::size_t column = 0; column < 4; ++column) transform[row][column] = state.transform.values[row * 4 + column]; - if (point.family == Visual_Family::Point) { - DvzPointStyleDesc style = dvz_point_style_desc(); - style.edge_color.r = state.style.edge_color.red; - style.edge_color.g = state.style.edge_color.green; - style.edge_color.b = state.style.edge_color.blue; - style.edge_color.a = state.style.edge_color.alpha; - style.stroke_width_px = state.style.stroke_width_px; - style.aspect = aspect(state.style.aspect); - if (dvz_point_set_style(visual_, &style) != DVZ_OK) ::renderive::error::unexpected("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) - ::renderive::error::unexpected("failed to apply Datoviz visual state"); - applied_state_revision_ = point.state_revision; - } - if (point.data_revision == applied_data_revision_) return; - if (point.positions.empty()) { - if (dvz_visual_set_visible(visual_, false) != DVZ_OK) ::renderive::error::unexpected("failed to hide empty Datoviz point visual"); - applied_data_revision_ = point.data_revision; - return; - } - const auto count = static_cast(point.positions.size()); - DvzResult result = DVZ_OK; - switch (point.family) { - case Visual_Family::Point: { - const std::array updates{ - { - {"position", point.positions.data(), count}, - {"color", point.colors.data(), count}, - {"diameter_px", point.sizes.data(), count} - } - }; - result = dvz_visual_set_data_many(visual_, updates.data(), 3); - break; - } - case Visual_Family::Splat: { - const std::array updates{ - { - {"position", point.positions.data(), count}, - {"color", point.colors.data(), count}, - {"sigma", point.sigma.data(), count}, - {"angle", point.angles.data(), count} - } - }; - result = dvz_visual_set_data_many(visual_, updates.data(), 4); - break; - } - case Visual_Family::Pixel: { - const std::array updates{ - { - {"position", point.positions.data(), count}, - {"color", point.colors.data(), count}, - {"pixel_size_px", point.sizes.data(), count} - } - }; - result = dvz_visual_set_data_many(visual_, updates.data(), 3); - break; - } - case Visual_Family::Marker: { - const std::array updates{ - { - {"position", point.positions.data(), count}, - {"color", point.colors.data(), count}, - {"diameter_px", point.sizes.data(), count}, - {"angle", point.angles.data(), count}, - {"shape", point.shapes.data(), count} - } - }; - result = dvz_visual_set_data_many(visual_, updates.data(), 5); - break; - } - case Visual_Family::Sphere: { - const std::array updates{ - { - {"position", point.positions.data(), count}, - {"color", point.colors.data(), count}, - {"radius", point.sizes.data(), count} - } - }; - result = dvz_visual_set_data_many(visual_, updates.data(), 3); - break; - } - case Visual_Family::Segment: { - const std::array updates{ - { - {"position_start", point.positions.data(), count}, - {"position_end", point.secondary_positions.data(), count}, - {"color", point.colors.data(), count}, - {"stroke_width_px", point.sizes.data(), count} - } - }; - result = dvz_visual_set_data_many(visual_, updates.data(), 4); - break; - } - case Visual_Family::Vector: { - const std::array updates{ - { - {"position", point.positions.data(), count}, - {"vector", point.secondary_positions.data(), count}, - {"color", point.colors.data(), count}, - {"stroke_width_px", point.sizes.data(), count} - } - }; - result = dvz_visual_set_data_many(visual_, updates.data(), 4); - break; - } - case Visual_Family::Primitive: - case Visual_Family::Mesh: { - const std::array updates{ - { - {"position", point.positions.data(), count}, - {"color", point.colors.data(), count}, - {"normal", point.normals.data(), count} - } - }; - result = dvz_visual_set_data_many(visual_, updates.data(), 3); - break; - } - case Visual_Family::Path: { - const std::array updates{ - { - {"position", point.positions.data(), count}, - {"color", point.colors.data(), count}, - {"stroke_width_px", point.sizes.data(), count} - } - }; - result = dvz_visual_set_data_many(visual_, updates.data(), 3); - break; - } - case Visual_Family::Image: - case Visual_Family::Labels: { - const std::array updates{ - { - {"position", point.positions.data(), count}, - {"extent", point.extents.data(), count} - } - }; - result = dvz_visual_set_data_many(visual_, updates.data(), 2); - break; - } - case Visual_Family::Glyph: - case Visual_Family::Text: - case Visual_Family::Volume: break; - } - if (result != DVZ_OK || - dvz_visual_set_visible(visual_, point.state.visible) != DVZ_OK) - ::renderive::error::unexpected("failed to upload Datoviz visual payload"); - applied_data_revision_ = point.data_revision; -} -void Datoviz_Visual_Backend::dispatch_pointer( - ::renderive::Event_Type event, float x, float y, - ::renderive::Mouse_Button mouse_button, - ::renderive::Keyboard_Modifier keyboard_modifiers, Extent viewport) { - require_domain(); - const float width = static_cast(viewport.width); - const float height = static_cast(viewport.height); - const DvzPointerEventType type = - event == ::renderive::Event_Type::Pointer_Move - ? DVZ_POINTER_EVENT_MOVE - : event == ::renderive::Event_Type::Pointer_Press - ? DVZ_POINTER_EVENT_PRESS - : DVZ_POINTER_EVENT_RELEASE; - dvz_pointer_emit_position(input_router_, type, x, y, width, height, - button(mouse_button), - modifiers(keyboard_modifiers), 1.0F, - dvz_input_timestamp_ns(), nullptr); -} -void Datoviz_Visual_Backend::dispatch_wheel( - float x, float y, float delta_x, float delta_y, - ::renderive::Keyboard_Modifier keyboard_modifiers, Extent viewport) { - require_domain(); - dvz_pointer_emit_wheel( - input_router_, x, y, static_cast(viewport.width), - static_cast(viewport.height), delta_x, delta_y, - modifiers(keyboard_modifiers), 1.0F, dvz_input_timestamp_ns(), nullptr); -} -void Datoviz_Visual_Backend::dispatch_key( - const ::renderive::Key_Event& event) { - require_domain(); - const DvzKeyboardEventType type = - event.type == ::renderive::Event_Type::Key_Release - ? DVZ_KEYBOARD_EVENT_RELEASE - : event.auto_repeat - ? DVZ_KEYBOARD_EVENT_REPEAT - : DVZ_KEYBOARD_EVENT_PRESS; - dvz_keyboard_emit(input_router_, type, - key_code(event.key, event.native_key), - modifiers(event.modifiers), nullptr); -} -DvzSceneFrameArtifact* Datoviz_Visual_Backend::emit( - const Scene_State& scene) { - DvzFramePlanEmitConfig configuration = dvz_frame_plan_emit_config(); - configuration.shader_format = DVZ_SCENE_SHADER_FORMAT_GLSL; - configuration.external_color_target = true; - configuration.color_target_id = color_target_id; - configuration.color_target_format = DVZ_FORMAT_R8G8B8A8_UNORM; - configuration.target_width = scene.viewport.width; - configuration.target_height = scene.viewport.height; - configuration.clear_color[0] = scene.clear_color.red; - configuration.clear_color[1] = scene.clear_color.green; - configuration.clear_color[2] = scene.clear_color.blue; - configuration.clear_color[3] = scene.clear_color.alpha; - const auto capabilities = dvz_capability_snapshot(); - DvzDiagnosticReport report{}; - dvz_diagnostic_report_init(&report); - auto* artifact = - dvz_figure_emit_frame(figure_, &capabilities, &report, &configuration); - if (artifact == nullptr) { - std::string message = "failed to emit Datoviz visual frame"; - const auto count = dvz_diagnostic_report_count(&report); - if (count != 0) { - if (const char* diagnostic = dvz_diagnostic_report_get(&report, 0)) message += ": " + std::string(diagnostic); - } - ::renderive::error::unexpected(message); - } - return artifact; -} -std::optional Datoviz_Visual_Backend::submit( - const Scene_State& scene, std::uint64_t scene_revision, - const Prepared_Point& point, std::uint64_t frame_sequence, - bool observe) { - require_domain(); - if (scene.viewport.empty()) return std::nullopt; - Datoviz_Frame_Trace trace; - trace.render_sequence = frame_sequence; - trace.observed = observe; - std::uint64_t phase_started = observe ? trace_now_ns() : 0; - apply(scene, scene_revision, point); - if (observe) trace.apply_ns = trace_now_ns() - phase_started; - if (target_ == nullptr || target_extent_ != scene.viewport) { - target_.reset(); - target_extent_ = scene.viewport; - target_ = std::make_unique(gpu_context_, target_extent_, - ++target_generation_); - } - target_->begin(observe); - DvzSceneFrameArtifact* artifact{}; - try { - if (observe) phase_started = trace_now_ns(); - artifact = emit(scene); - if (observe) trace.emit_ns = trace_now_ns() - phase_started; - } - catch (...) { - target_->abort(); - ::renderive::error::unexpected( - "preparing Datoviz frame", std::current_exception()); - } - if (observe) { - trace.artifact_status = static_cast( - dvz_scene_frame_artifact_status(artifact)); - trace.artifact_resource_version = - dvz_scene_frame_artifact_resource_version(artifact); - trace.artifact_frame_index = - dvz_scene_frame_artifact_frame_index(artifact); - } - const DvzDrp2CommandStream* stream = dvz_scene_frame_artifact_stream(artifact); - const DvzStreamFrame target_frame = target_->stream_frame(); - if (observe) phase_started = trace_now_ns(); - const bool attached = stream != nullptr && - dvz_drp2_runtime_attach_frame_target( - runtime_, color_target_id, &target_frame); - const DvzDrp2ValidationResult result = - attached - ? dvz_drp2_runtime_execute(runtime_, stream) - : DvzDrp2ValidationResult{}; - if (observe) trace.execute_ns = trace_now_ns() - phase_started; - trace.validation_ok = attached && result.ok; - trace.validation_code = static_cast(result.code); - trace.validation_command_index = result.command_index; - if (observe || !trace.validation_ok) { - if (char* json = dvz_scene_frame_artifact_json( - artifact, "renderive_frame")) { - trace.artifact_json = json; - dvz_drp2_stream_json_destroy(json); - } - } - dvz_scene_frame_artifact_destroy(artifact); - if (!attached) { - target_->abort(); - ::renderive::error::unexpected("failed to attach the Datoviz point frame target"); - } - if (!result.ok) { - target_->abort(); - std::string message = - "failed to execute Datoviz point frame: validation code " + - std::to_string(static_cast(result.code)) + - ", command " + std::to_string(result.command_index); - if (!trace.artifact_json.empty()) { - message += ", artifact " + - trace.artifact_json.substr( - 0, std::min(trace.artifact_json.size(), 2048)); - } - ::renderive::error::unexpected(std::move(message)); - } - if (observe) phase_started = trace_now_ns(); - target_->submit(); - if (observe) trace.submit_ns = trace_now_ns() - phase_started; - return Pending_Frame{ - target_->device(), target_->fence(), scene.viewport, - frame_sequence, target_->generation(), - std::move(trace) - }; -} -Datoviz_Visual_Backend::Completed_Frame Datoviz_Visual_Backend::collect( - Pending_Frame pending) { - require_domain(); - if (target_ == nullptr || - target_->generation() != pending.target_generation) - ::renderive::error::unexpected("Datoviz pending frame target no longer exists"); - const std::uint64_t readback_started = pending.trace.observed - ? trace_now_ns() - : 0; - auto collection = target_->collect(); - if (pending.trace.observed) pending.trace.readback_ns = trace_now_ns() - readback_started; - pending.trace.gpu = std::move(collection.gpu_timing); - auto output = std::make_shared(); - output->extent = pending.extent; - output->sequence = pending.sequence; - output->rgba8 = std::move(collection.pixels); - return {std::move(output), std::move(pending.trace)}; -} -void Datoviz_Visual_Backend::discard(Pending_Frame pending) { - require_domain(); - if (target_ == nullptr || - target_->generation() != pending.target_generation) - ::renderive::error::unexpected("Datoviz pending frame target no longer exists"); - target_->discard_after_completion(); -} -void Datoviz_Visual_Backend::destroy() { - if (std::this_thread::get_id() != domain_thread_) - ::renderive::error::unexpected( - "Datoviz backend may only be destroyed on the render domain"); - if (runtime_ != nullptr) { - dvz_drp2_runtime_destroy(runtime_); - runtime_ = nullptr; - } - target_.reset(); - if (panel_ != nullptr && input_router_ != nullptr) (void)dvz_panel_connect_input(panel_, nullptr); - if (gesture_handler_ != nullptr) { - dvz_pointer_gesture_handler_destroy(gesture_handler_); - gesture_handler_ = nullptr; - } - if (input_router_ != nullptr) { - dvz_input_router_destroy(input_router_); - input_router_ = nullptr; - } - visual_ = nullptr; - panel_ = nullptr; - figure_ = nullptr; - if (scene_ != nullptr) { - dvz_scene_destroy(scene_); - scene_ = nullptr; - } - if (gpu_context_ != nullptr) { - dvz_gpu_ctx_destroy(gpu_context_); - gpu_context_ = nullptr; - } -} -} // namespace renderive::render_3d::detail diff --git a/render_3D/render_3D/detail/Datoviz_Visual_Backend.h b/render_3D/render_3D/detail/Datoviz_Visual_Backend.h deleted file mode 100644 index d7d71e7..0000000 --- a/render_3D/render_3D/detail/Datoviz_Visual_Backend.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once -#include "Datoviz_Frame_Trace.h" -#include "Point_Core.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace renderive::render_3d::detail { -class Datoviz_Visual_Backend final { -public: - struct Pending_Frame { - VkDevice device{VK_NULL_HANDLE}; - VkFence fence{VK_NULL_HANDLE}; - Extent extent{}; - std::uint64_t sequence{}; - std::uint64_t target_generation{}; - Datoviz_Frame_Trace trace; - }; - struct Completed_Frame { - std::shared_ptr frame; - Datoviz_Frame_Trace trace; - }; - Datoviz_Visual_Backend(std::uint32_t gpu_index, bool validation_enabled, - const Scene_State& initial_scene); - ~Datoviz_Visual_Backend() noexcept(false); - Datoviz_Visual_Backend(const Datoviz_Visual_Backend&) = delete; - Datoviz_Visual_Backend& operator=(const Datoviz_Visual_Backend&) = delete; - [[nodiscard]] std::optional submit( - const Scene_State& scene, std::uint64_t scene_revision, - const Prepared_Point& point, std::uint64_t frame_sequence, - bool observe); - [[nodiscard]] Completed_Frame collect(Pending_Frame pending); - void discard(Pending_Frame pending); - void dispatch_pointer(::renderive::Event_Type type, float x, float y, - ::renderive::Mouse_Button button, - ::renderive::Keyboard_Modifier modifiers, - Extent viewport); - void dispatch_wheel(float x, float y, float delta_x, float delta_y, - ::renderive::Keyboard_Modifier modifiers, - Extent viewport); - void dispatch_key(const ::renderive::Key_Event& event); -private: - class Frame_Target; - void require_domain() const; - void create_scene(const Scene_State& initial_scene); - void apply(const Scene_State& scene, std::uint64_t scene_revision, - const Prepared_Point& point); - [[nodiscard]] DvzSceneFrameArtifact* emit(const Scene_State& scene); - void destroy(); - std::thread::id domain_thread_; - DvzGpuCtx* gpu_context_{}; - DvzDrp2Runtime* runtime_{}; - DvzScene* scene_{}; - DvzFigure* figure_{}; - DvzPanel* panel_{}; - DvzVisual* visual_{}; - DvzInputRouter* input_router_{}; - DvzPointerGestureHandler* gesture_handler_{}; - std::unique_ptr target_; - Extent target_extent_{}; - std::uint64_t target_generation_{}; - std::uint64_t applied_scene_revision_{}; - std::uint64_t applied_state_revision_{}; - std::uint64_t applied_data_revision_{}; -}; -} // namespace renderive::render_3d::detail diff --git a/render_3D/render_3D/detail/Gpu_Completion_Service.cpp b/render_3D/render_3D/detail/Gpu_Completion_Service.cpp deleted file mode 100644 index 9154545..0000000 --- a/render_3D/render_3D/detail/Gpu_Completion_Service.cpp +++ /dev/null @@ -1,309 +0,0 @@ -#include -#include "Gpu_Completion_Service.h" -#include -#include -#include -#include -namespace renderive::render_3d::detail { -Gpu_Completion_Service& Gpu_Completion_Service::instance() { - static Gpu_Completion_Service service; - return service; -} -Gpu_Completion_Service::Gpu_Completion_Service() { - pending_.set_capacity(default_capacity); - thread_ = std::thread([this] { - run(); - }); -} -Gpu_Completion_Service::~Gpu_Completion_Service() { - stopping_.store(true, std::memory_order_release); - wake(); - if (thread_.joinable()) thread_.join(); -} -Gpu_Completion_Service::Reservation::Reservation( - std::shared_ptr pending) noexcept : pending_(std::move(pending)) {} -Gpu_Completion_Service::Reservation::~Reservation() noexcept(false) { - cancel(); -} -Gpu_Completion_Service::Reservation::Reservation(Reservation&& other) noexcept : pending_(std::exchange(other.pending_, {})) {} -void Gpu_Completion_Service::Reservation::watch(VkDevice device, - VkFence fence) { - if (!pending_ || device == VK_NULL_HANDLE || fence == VK_NULL_HANDLE) ::renderive::error::unexpected("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) ::renderive::error::unexpected("GPU completion reservation is not reserved"); - pending->device = device; - pending->fence = fence; - // This timestamp is part of correctness, not only observability: it - // bounds the lifetime of a submission whose fence never signals. - pending->watched_at = std::chrono::steady_clock::now(); - const std::size_t watched = - service->watched_.fetch_add(1, std::memory_order_relaxed) + 1; - update_peak(service->peak_watched_, watched); - pending->status = Pending_Fence::Status::watched; - } - service->wake(); -} -void Gpu_Completion_Service::Reservation::cancel() { - if (!pending_) return; - auto pending = std::exchange(pending_, {}); - cancel_reserved(pending); - pending->service->wake(); -} -void Gpu_Completion_Service::update_peak(std::atomic_size_t& peak, - std::size_t value) noexcept { - std::size_t current = peak.load(std::memory_order_relaxed); - while (current < value && - !peak.compare_exchange_weak(current, value, - std::memory_order_relaxed)) {} -} -void Gpu_Completion_Service::cancel_reserved( - const std::shared_ptr& pending) { - if (!pending) return; - std::lock_guard lock(pending->mutex); - if (pending->status == Pending_Fence::Status::reserved) pending->status = Pending_Fence::Status::canceled; -} -void Gpu_Completion_Service::acquire_slot() { - const auto started = std::chrono::steady_clock::now(); - if (!slots_.try_acquire()) { - backpressure_count_.fetch_add(1, std::memory_order_relaxed); - slots_.acquire(); - const auto waited = std::chrono::duration_cast( - std::chrono::steady_clock::now() - started) - .count(); - if (waited > 0) - backpressure_wait_ns_.fetch_add( - static_cast(waited), - std::memory_order_relaxed); - } - const std::size_t in_flight = - in_flight_.fetch_add(1, std::memory_order_relaxed) + 1; - update_peak(peak_in_flight_, in_flight); -} -void Gpu_Completion_Service::release_slot() noexcept { - in_flight_.fetch_sub(1, std::memory_order_relaxed); - slots_.release(); -} -Gpu_Completion_Service::Prepare_Result Gpu_Completion_Service::prepare( - Completion completion, Exception_Handler on_exception, bool observe) { - if (!completion) ::renderive::error::unexpected("GPU completion callback is empty"); - if (!on_exception) ::renderive::error::unexpected("GPU completion exception handler is empty"); - if (stopping_.load(std::memory_order_acquire)) return {{}, Admission_Result::stopping}; - auto pending = std::make_shared(); - pending->completion = std::move(completion); - pending->on_exception = std::move(on_exception); - pending->observe = observe; - pending->service = this; - acquire_slot(); - if (stopping_.load(std::memory_order_acquire)) { - release_slot(); - return {{}, Admission_Result::stopping}; - } - if (!pending_.try_push(pending)) { - release_slot(); - ::renderive::error::unexpected("GPU completion admission invariant violated"); - } - wake(); - return {Reservation(std::move(pending)), Admission_Result::none}; -} -Gpu_Completion_Service::Statistics Gpu_Completion_Service::statistics() const noexcept { - return { - static_cast(default_capacity), - in_flight_.load(std::memory_order_relaxed), - peak_in_flight_.load(std::memory_order_relaxed), - watched_.load(std::memory_order_relaxed), - peak_watched_.load(std::memory_order_relaxed), - backpressure_count_.load(std::memory_order_relaxed), - backpressure_wait_ns_.load(std::memory_order_relaxed), - fault_count_.load(std::memory_order_relaxed), - abandoned_count_.load(std::memory_order_relaxed) - }; -} -void Gpu_Completion_Service::wake() noexcept { - wake_generation_.fetch_add(1, std::memory_order_release); - wake_condition_.notify_one(); -} -void Gpu_Completion_Service::run() { - struct Device_Fences { - VkDevice device{VK_NULL_HANDLE}; - std::vector fences; - }; - std::vector> active; - active.reserve(static_cast(default_capacity)); - std::size_t wait_group_index{}; - const auto wait_age_ns = [](std::chrono::steady_clock::time_point started) { - const auto elapsed = std::chrono::duration_cast( - std::chrono::steady_clock::now() - started) - .count(); - return elapsed > 0 ? static_cast(elapsed) : 0ULL; - }; - const auto finish = [this, &wait_age_ns]( - const std::shared_ptr& pending, - VkResult result, Completion_Error error) { - Completion completion; - Exception_Handler on_exception; - std::chrono::steady_clock::time_point watched_at{}; - bool observe{}; - { - std::lock_guard lock(pending->mutex); - completion = std::move(pending->completion); - on_exception = std::move(pending->on_exception); - watched_at = pending->watched_at; - observe = pending->observe; - pending->status = Pending_Fence::Status::canceled; - } - watched_.fetch_sub(1, std::memory_order_relaxed); - Result completion_result; - completion_result.error = error; - completion_result.vulkan_result = result; - if (observe) completion_result.wait_duration_ns = wait_age_ns(watched_at); - if (error != Completion_Error::none) { - fault_count_.fetch_add(1, std::memory_order_relaxed); - abandoned_count_.fetch_add(1, std::memory_order_relaxed); - } - try { - completion(std::move(completion_result)); - } - catch (...) { - on_exception(::renderive::error::capture( - "delivering GPU completion", std::current_exception())); - } - release_slot(); - }; - for (;;) { - const std::uint64_t wake_generation = - wake_generation_.load(std::memory_order_acquire); - std::shared_ptr incoming; - while (pending_.try_pop(incoming)) active.push_back(std::move(incoming)); - std::vector groups; - for (auto iterator = active.begin(); iterator != active.end();) { - Pending_Fence::Status status; - VkDevice device{VK_NULL_HANDLE}; - VkFence fence{VK_NULL_HANDLE}; - { - std::lock_guard lock((*iterator)->mutex); - status = (*iterator)->status; - device = (*iterator)->device; - fence = (*iterator)->fence; - } - if (status == Pending_Fence::Status::canceled || - (status == Pending_Fence::Status::reserved && - stopping_.load(std::memory_order_acquire))) { - cancel_reserved(*iterator); - iterator = active.erase(iterator); - release_slot(); - continue; - } - if (status == Pending_Fence::Status::watched) { - auto group = std::find_if( - groups.begin(), groups.end(), - [device](const Device_Fences& item) { - return item.device == device; - }); - if (group == groups.end()) { - groups.push_back(Device_Fences{device, {}}); - group = groups.end() - 1; - } - group->fences.push_back(fence); - } - ++iterator; - } - if (stopping_.load(std::memory_order_acquire) && active.empty() && - pending_.empty()) - return; - // Probe every watched fence first. A permanently unsignaled fence is - // converted into a logical failure after a bounded interval. The - // submission is explicitly marked abandoned so its owner can - // quarantine, rather than recycle, the referenced GPU resources. - bool completed_any = false; - for (auto iterator = active.begin(); iterator != active.end();) { - VkDevice device{VK_NULL_HANDLE}; - VkFence fence{VK_NULL_HANDLE}; - Pending_Fence::Status status; - std::chrono::steady_clock::time_point watched_at{}; - { - std::lock_guard lock((*iterator)->mutex); - status = (*iterator)->status; - device = (*iterator)->device; - fence = (*iterator)->fence; - watched_at = (*iterator)->watched_at; - } - if (status != Pending_Fence::Status::watched) { - ++iterator; - continue; - } - if (stopping_.load(std::memory_order_acquire)) { - auto pending = *iterator; - iterator = active.erase(iterator); - finish(pending, VK_TIMEOUT, Completion_Error::fence_abandoned); - completed_any = true; - continue; - } - if (wait_age_ns(watched_at) >= maximum_fence_age_ns) { - auto pending = *iterator; - iterator = active.erase(iterator); - finish(pending, VK_TIMEOUT, Completion_Error::fence_abandoned); - completed_any = true; - continue; - } - const VkResult result = vkGetFenceStatus(device, fence); - if (result == VK_NOT_READY) { - ++iterator; - continue; - } - auto pending = *iterator; - iterator = active.erase(iterator); - finish(pending, result, result == VK_SUCCESS - ? Completion_Error::none - : Completion_Error::vulkan_failure); - completed_any = true; - } - if (completed_any) continue; - if (groups.empty()) { - std::unique_lock lock(wait_mutex_); - if (wake_generation_.load(std::memory_order_acquire) == - wake_generation) { - wake_condition_.wait(lock, [this, wake_generation] { - return wake_generation_.load(std::memory_order_acquire) != - wake_generation; - }); - } - continue; - } - wait_group_index %= groups.size(); - const Device_Fences& group = groups[wait_group_index++]; - const VkResult wait_result = vkWaitForFences( - group.device, static_cast(group.fences.size()), - group.fences.data(), VK_FALSE, fence_wait_timeout_ns); - for (auto iterator = active.begin(); iterator != active.end();) { - VkDevice device{VK_NULL_HANDLE}; - VkFence fence{VK_NULL_HANDLE}; - Pending_Fence::Status status; - { - std::lock_guard lock((*iterator)->mutex); - status = (*iterator)->status; - device = (*iterator)->device; - fence = (*iterator)->fence; - } - if (status != Pending_Fence::Status::watched || - device != group.device) { - ++iterator; - continue; - } - VkResult result = wait_result; - if (wait_result == VK_SUCCESS || wait_result == VK_TIMEOUT) result = vkGetFenceStatus(device, fence); - if (result == VK_NOT_READY) { - ++iterator; - continue; - } - auto pending = *iterator; - iterator = active.erase(iterator); - finish(pending, result, result == VK_SUCCESS - ? Completion_Error::none - : Completion_Error::vulkan_failure); - } - } -} -} diff --git a/render_3D/render_3D/detail/Gpu_Completion_Service.h b/render_3D/render_3D/detail/Gpu_Completion_Service.h deleted file mode 100644 index f55332f..0000000 --- a/render_3D/render_3D/detail/Gpu_Completion_Service.h +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace renderive::render_3d::detail { -class Gpu_Completion_Service final { - struct Pending_Fence; -public: - enum class Admission_Result : std::uint8_t { - none, - stopping - }; - enum class Completion_Error : std::uint8_t { - none, - fence_abandoned, - vulkan_failure - }; - struct Result { - Completion_Error error{}; - VkResult vulkan_result{VK_SUCCESS}; - std::uint64_t wait_duration_ns{}; - }; - struct Statistics { - std::size_t capacity{}; - std::size_t in_flight{}; - std::size_t peak_in_flight{}; - std::size_t watched{}; - std::size_t peak_watched{}; - std::uint64_t backpressure_count{}; - std::uint64_t backpressure_wait_ns{}; - std::uint64_t fault_count{}; - std::uint64_t abandoned_count{}; - }; - using Completion = std::function; - using Exception_Handler = std::function; - class Reservation final { - public: - Reservation() = default; - ~Reservation() noexcept(false); - Reservation(const Reservation&) = delete; - Reservation& operator=(const Reservation&) = delete; - Reservation(Reservation&& other) noexcept; - Reservation& operator=(Reservation&&) = delete; - void watch(VkDevice device, VkFence fence); - private: - explicit Reservation(std::shared_ptr pending) noexcept; - void cancel(); - std::shared_ptr pending_; - friend class Gpu_Completion_Service; - }; - struct Prepare_Result { - Reservation reservation; - Admission_Result result{}; - [[nodiscard]] explicit operator bool() const noexcept { - return result == Admission_Result::none; - } - }; - static Gpu_Completion_Service& instance(); - Gpu_Completion_Service(const Gpu_Completion_Service&) = delete; - Gpu_Completion_Service& operator=(const Gpu_Completion_Service&) = delete; - [[nodiscard]] Prepare_Result prepare(Completion completion, - Exception_Handler on_exception, - bool observe); - [[nodiscard]] Statistics statistics() const noexcept; -private: - struct Pending_Fence { - enum class Status { - reserved, - watched, - canceled - }; - std::mutex mutex; - VkDevice device{VK_NULL_HANDLE}; - VkFence fence{VK_NULL_HANDLE}; - Completion completion; - Exception_Handler on_exception; - std::chrono::steady_clock::time_point watched_at{}; - Gpu_Completion_Service* service{}; - Status status{Status::reserved}; - bool observe{}; - }; - Gpu_Completion_Service(); - ~Gpu_Completion_Service(); - static void update_peak(std::atomic_size_t& peak, std::size_t value) noexcept; - static void cancel_reserved(const std::shared_ptr& pending); - void acquire_slot(); - void release_slot() noexcept; - void wake() noexcept; - void run(); - static constexpr std::ptrdiff_t default_capacity = 1024; - static constexpr std::uint64_t fence_wait_timeout_ns = 1'000'000; - static constexpr std::uint64_t maximum_fence_age_ns = 30'000'000'000ULL; - std::counting_semaphore slots_{default_capacity}; - oneapi::tbb::concurrent_bounded_queue> pending_; - std::mutex wait_mutex_; - std::condition_variable wake_condition_; - std::atomic_uint64_t wake_generation_{}; - std::atomic_size_t in_flight_{}; - std::atomic_size_t peak_in_flight_{}; - std::atomic_size_t watched_{}; - std::atomic_size_t peak_watched_{}; - std::atomic_uint64_t backpressure_count_{}; - std::atomic_uint64_t backpressure_wait_ns_{}; - std::atomic_uint64_t fault_count_{}; - std::atomic_uint64_t abandoned_count_{}; - std::atomic_bool stopping_{}; - std::thread thread_; -}; -} diff --git a/render_3D/render_3D/detail/Point_Core.h b/render_3D/render_3D/detail/Point_Core.h deleted file mode 100644 index 0c66367..0000000 --- a/render_3D/render_3D/detail/Point_Core.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include "render_3D/Render_Scene_3D.h" -#include -#include -#include -#include -namespace renderive::render_3d::detail { -using Scene_State = Render_Scene_3D::State; -// Immutable input captured at the frame publication boundary. Playback frames -// retain this shared payload instead of consulting a later buffer role. -struct Captured_Point { - Point_State state; - std::shared_ptr> points; - std::uint64_t state_revision{}; - std::uint64_t data_revision{}; -}; -// Immutable, frame-local CPU output. oneTBB workers create this data; only -// the Datoviz render-domain task consumes it. -struct Prepared_Point { - Point_State state; - Visual_Family family{Visual_Family::Point}; - std::uint64_t state_revision{}; - std::uint64_t data_revision{}; - std::vector> positions; - std::vector> colors; - std::vector sizes; - std::vector> sigma; - std::vector angles; - std::vector shapes; - std::vector> secondary_positions; - std::vector> normals; - std::vector> extents; -}; -} // namespace renderive::render_3d::detail diff --git a/render_3D/render_3D/detail/Render_Domain.cpp b/render_3D/render_3D/detail/Render_Domain.cpp deleted file mode 100644 index 6ff11c8..0000000 --- a/render_3D/render_3D/detail/Render_Domain.cpp +++ /dev/null @@ -1,168 +0,0 @@ -#include -#include "Render_Domain.h" -#include -#include -#include -namespace renderive::render_3d::detail { -namespace { -struct Render_Domain_Registry { - std::mutex mutex; - std::unordered_map> domains; -}; -Render_Domain_Registry& registry() { - static Render_Domain_Registry value; - return value; -} -} -Render_Domain::Prepared_Task::~Prepared_Task() { - release(); -} -Render_Domain::Prepared_Task::Prepared_Task(Prepared_Task&& other) noexcept : domain_(std::move(other.domain_)), task_(std::move(other.task_)) {} -Render_Domain::Prepared_Task& Render_Domain::Prepared_Task::operator=(Prepared_Task&& other) noexcept { - if (this == &other) return *this; - release(); - domain_ = std::move(other.domain_); - task_ = std::move(other.task_); - return *this; -} -void Render_Domain::Prepared_Task::release() noexcept { - if (!domain_) return; - task_.reset(); - domain_->release_admission(); - domain_.reset(); -} -std::shared_ptr Render_Domain::acquire(std::uint32_t gpu_index) { - auto& storage = registry(); - std::lock_guard lock(storage.mutex); - auto& entry = storage.domains[gpu_index]; - if (auto domain = entry.lock()) return domain; - auto domain = std::shared_ptr(new Render_Domain(), - &Render_Domain::destroy); - entry = domain; - return domain; -} -Render_Domain::Render_Domain() { - tasks_.set_capacity(default_capacity); - thread_ = std::thread([this] { - run(); - }); -} -Render_Domain::~Render_Domain() { - request_stop(); - if (thread_.joinable()) thread_.join(); -} -void Render_Domain::destroy(Render_Domain* domain) noexcept { - if (!domain) return; - if (current_domain_ != domain) { - delete domain; - return; - } - domain->stopping_.store(true, std::memory_order_release); - domain->destroy_on_exit_.store(true, std::memory_order_release); -} -void Render_Domain::request_stop() noexcept { - bool expected = false; - if (!stopping_.compare_exchange_strong( - expected, true, std::memory_order_acq_rel)) - return; - slots_.acquire(); - tasks_.push(std::unique_ptr{}); -} -void Render_Domain::update_peak(std::atomic_size_t& peak, std::size_t value) noexcept { - std::size_t current = peak.load(std::memory_order_relaxed); - while (current < value && !peak.compare_exchange_weak(current, value, std::memory_order_relaxed)) {} -} -void Render_Domain::acquire_admission() { - const auto started = std::chrono::steady_clock::now(); - if (!slots_.try_acquire()) { - backpressure_count_.fetch_add(1, std::memory_order_relaxed); - slots_.acquire(); - const auto waited = std::chrono::duration_cast( - std::chrono::steady_clock::now() - started).count(); - if (waited > 0) backpressure_wait_ns_.fetch_add(static_cast(waited), std::memory_order_relaxed); - } - const std::size_t admitted = admitted_.fetch_add(1, std::memory_order_relaxed) + 1; - update_peak(peak_admitted_, admitted); -} -void Render_Domain::release_admission() noexcept { - admitted_.fetch_sub(1, std::memory_order_relaxed); - slots_.release(); -} -Render_Domain::Prepare_Result Render_Domain::prepare( - std::function function, Exception_Handler on_exception) { - if (!function) ::renderive::error::unexpected("render domain task is empty"); - if (!on_exception) ::renderive::error::unexpected("render domain exception handler is empty"); - if (stopping_.load(std::memory_order_acquire)) return {{}, Admission_Result::stopping}; - acquire_admission(); - if (stopping_.load(std::memory_order_acquire)) { - release_admission(); - return {{}, Admission_Result::stopping}; - } - try { - return { - Prepared_Task(shared_from_this(), std::make_unique( - std::move(function), std::move(on_exception))), - Admission_Result::none - }; - } - catch (...) { - release_admission(); - ::renderive::error::unexpected( - "preparing render domain task", std::current_exception()); - } -} -Render_Domain::Admission_Result Render_Domain::post( - std::function function, Exception_Handler on_exception) { - auto prepared = prepare(std::move(function), std::move(on_exception)); - if (!prepared) return prepared.result; - return post(std::move(prepared.task)); -} -Render_Domain::Admission_Result Render_Domain::post(Prepared_Task task) { - if (!task.task_ || task.domain_.get() != this) ::renderive::error::unexpected("render domain prepared task is invalid"); - if (stopping_.load(std::memory_order_acquire)) return Admission_Result::stopping; - if (!tasks_.try_push(std::move(task.task_))) ::renderive::error::unexpected("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(); - return Admission_Result::none; -} -Render_Domain::Statistics Render_Domain::statistics() const noexcept { - return { - static_cast(default_capacity), - admitted_.load(std::memory_order_relaxed), - peak_admitted_.load(std::memory_order_relaxed), - queued_.load(std::memory_order_relaxed), - peak_queued_.load(std::memory_order_relaxed), - backpressure_count_.load(std::memory_order_relaxed), - backpressure_wait_ns_.load(std::memory_order_relaxed) - }; -} -void Render_Domain::run() { - current_domain_ = this; - for (;;) { - std::unique_ptr task; - tasks_.pop(task); - if (!task) { - slots_.release(); - current_domain_ = nullptr; - return; - } - queued_.fetch_sub(1, std::memory_order_relaxed); - release_admission(); - try { - task->function(); - } - catch (...) { - task->on_exception(::renderive::error::capture( - "executing render domain task", std::current_exception())); - } - task.reset(); - if (destroy_on_exit_.load(std::memory_order_acquire)) { - current_domain_ = nullptr; - if (thread_.joinable()) thread_.detach(); - delete this; - return; - } - } -} -} diff --git a/render_3D/render_3D/detail/Render_Domain.h b/render_3D/render_3D/detail/Render_Domain.h deleted file mode 100644 index a40d195..0000000 --- a/render_3D/render_3D/detail/Render_Domain.h +++ /dev/null @@ -1,148 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace renderive::render_3d::detail { -class Render_Domain final : public std::enable_shared_from_this { - struct Task { - Task(std::function value, - std::function exception_handler) : function(std::move(value)), - on_exception(std::move(exception_handler)) {} - std::function function; - std::function on_exception; - }; -public: - enum class Admission_Result : std::uint8_t { - none, - stopping - }; - struct Statistics { - std::size_t capacity{}; - std::size_t admitted{}; - std::size_t peak_admitted{}; - std::size_t queued{}; - std::size_t peak_queued{}; - std::uint64_t backpressure_count{}; - std::uint64_t backpressure_wait_ns{}; - }; - class Prepared_Task final { - public: - Prepared_Task() = default; - ~Prepared_Task(); - Prepared_Task(const Prepared_Task&) = delete; - Prepared_Task& operator=(const Prepared_Task&) = delete; - Prepared_Task(Prepared_Task&& other) noexcept; - Prepared_Task& operator=(Prepared_Task&& other) noexcept; - private: - Prepared_Task(std::shared_ptr domain, std::unique_ptr task) noexcept : domain_(std::move(domain)), task_(std::move(task)) {} - void release() noexcept; - std::shared_ptr domain_; - std::unique_ptr task_; - friend class Render_Domain; - }; - struct Prepare_Result { - Prepared_Task task; - Admission_Result result{}; - [[nodiscard]] explicit operator bool() const noexcept { - return result == Admission_Result::none; - } - }; - static std::shared_ptr acquire(std::uint32_t gpu_index); - ~Render_Domain(); - Render_Domain(const Render_Domain&) = delete; - Render_Domain& operator=(const Render_Domain&) = delete; - using Exception_Handler = std::function; - [[nodiscard]] Prepare_Result prepare(std::function function, - Exception_Handler on_exception); - [[nodiscard]] Admission_Result post(std::function function, - Exception_Handler on_exception); - [[nodiscard]] Admission_Result post(Prepared_Task task); - template - struct Invoke_Result { - using Value = std::conditional_t, std::monostate, Result>; - std::optional value; - Admission_Result result{}; - [[nodiscard]] explicit operator bool() const noexcept { - return result == Admission_Result::none; - } - }; - template - [[nodiscard]] auto invoke(Function&& function) -> Invoke_Result> { - using Result = std::invoke_result_t; - using Value = typename Invoke_Result::Value; - Invoke_Result output; - if (current_domain_ == this) { - try { - if constexpr (std::is_void_v) { - std::invoke(std::forward(function)); - output.value.emplace(); - } - else { - output.value.emplace(std::invoke(std::forward(function))); - } - } - catch (...) { - ::renderive::error::unexpected( - "executing inline render domain invocation", - std::current_exception()); - } - return output; - } - auto promise = std::make_shared>(); - auto future = promise->get_future(); - auto callable = std::make_shared>( - std::forward(function)); - output.result = post( - [promise, callable] { - if constexpr (std::is_void_v) { - std::invoke(*callable); - promise->set_value(std::monostate{}); - } - else { - promise->set_value(std::invoke(*callable)); - } - }, - [promise](std::exception_ptr exception) { - promise->set_exception(std::move(exception)); - }); - if (output.result != Admission_Result::none) return output; - output.value.emplace(future.get()); - return output; - } - [[nodiscard]] Statistics statistics() const noexcept; -private: - Render_Domain(); - static void destroy(Render_Domain* domain) noexcept; - void request_stop() noexcept; - static void update_peak(std::atomic_size_t& peak, std::size_t value) noexcept; - void acquire_admission(); - void release_admission() noexcept; - void run(); - static constexpr std::ptrdiff_t default_capacity = 64; - inline static thread_local Render_Domain* current_domain_{}; - std::counting_semaphore slots_{default_capacity}; - oneapi::tbb::concurrent_bounded_queue> tasks_; - std::atomic_size_t admitted_{}; - std::atomic_size_t peak_admitted_{}; - std::atomic_size_t queued_{}; - std::atomic_size_t peak_queued_{}; - std::atomic_uint64_t backpressure_count_{}; - std::atomic_uint64_t backpressure_wait_ns_{}; - std::atomic_bool stopping_{}; - std::atomic_bool destroy_on_exit_{}; - std::thread thread_; -}; -} diff --git a/render_3D/render_3D/frame_control/Point_Frame_Control.cpp b/render_3D/render_3D/frame_control/Point_Frame_Control.cpp deleted file mode 100644 index 8a10127..0000000 --- a/render_3D/render_3D/frame_control/Point_Frame_Control.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "Point_Frame_Control.h" -#include -#include -#include -#include -#include -namespace renderive::render_3d::detail { -namespace { -using Manual_Strategy = Manual_Refresh_Strategy; -using Low_Latency_Strategy_Type = Low_Latency_Strategy; -using Playback_Strategy = Flow_Refresh_Strategy; -template -decltype(auto) visit(Frame_Control_Strategy_Base& strategy, - Function&& function) { - if (auto* value = dynamic_cast(&strategy)) return std::forward(function)(*value); - if (auto* value = dynamic_cast(&strategy)) return std::forward(function)(*value); - if (auto* value = dynamic_cast(&strategy)) return std::forward(function)(*value); - ::renderive::error::unexpected( - "unknown Point_Scene frame strategy"); -} -template -decltype(auto) visit(const Frame_Control_Strategy_Base& strategy, - Function&& function) { - if (const auto* value = dynamic_cast(&strategy)) return std::forward(function)(*value); - if (const auto* value = - dynamic_cast(&strategy)) - return std::forward(function)(*value); - if (const auto* value = dynamic_cast(&strategy)) return std::forward(function)(*value); - ::renderive::error::unexpected( - "unknown Point_Scene frame strategy"); -} -} // namespace -struct Point_Frame_Control::Impl { - std::shared_ptr strategy; -}; -Point_Frame_Control::Point_Frame_Control( - std::shared_ptr strategy) : impl_(std::make_unique()) { - replace(std::move(strategy)); -} -Point_Frame_Control::~Point_Frame_Control() = default; -std::shared_ptr Point_Frame_Control::runtime() { - return impl_->strategy; -} -std::shared_ptr Point_Frame_Control::runtime() const { - return impl_->strategy; -} -Frame_Request_Result Point_Frame_Control::request(Render_Scene_3D& scene) { - return request(scene, {}); -} -Frame_Request_Result Point_Frame_Control::request(Render_Scene_3D& scene, Scene_Render_Completion completion) { - const auto prepared = visit(*impl_->strategy, [&scene](auto& strategy) { - auto frame = strategy.acquire_painter(); - if (!frame) return false; - scene.publish_frame_state(*frame); - return true; - }); - if (!prepared) return Frame_Request_Result::painter_unavailable; - if (auto* strategy = dynamic_cast(impl_->strategy.get()); strategy && strategy->refresh() != Manual_Refresh_Result::none) return Frame_Request_Result::no_pending_frame; - return visit(*impl_->strategy, [&scene, completion = std::move(completion)](auto& strategy) mutable { - auto frame = strategy.acquire_renderer(); - if (!frame) return Frame_Request_Result::renderer_unavailable; - const auto result = scene.Scene_Base::render(*frame, std::move(completion)); - const auto converted = - adapter::enum_cast(result); - if (!converted) - ::renderive::error::unexpected( - "unknown scene render result"); - return *converted; - }); -} -void Point_Frame_Control::replace( - std::shared_ptr strategy) { - if (!strategy) - ::renderive::error::unexpected( - "Point_Scene requires a frame strategy"); - visit(*strategy, [](const auto&) {}); - impl_->strategy = std::move(strategy); -} -} // namespace renderive::render_3d::detail diff --git a/render_3D/render_3D/frame_control/Point_Frame_Control.h b/render_3D/render_3D/frame_control/Point_Frame_Control.h deleted file mode 100644 index 2086d7d..0000000 --- a/render_3D/render_3D/frame_control/Point_Frame_Control.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "../Render_Scene_3D.h" -#include -namespace renderive::render_3d::detail { -class Point_Frame_Control final { -public: - explicit Point_Frame_Control( - std::shared_ptr strategy); - ~Point_Frame_Control(); - Point_Frame_Control(const Point_Frame_Control&) = delete; - Point_Frame_Control& operator=(const Point_Frame_Control&) = delete; - Point_Frame_Control(Point_Frame_Control&&) = delete; - Point_Frame_Control& operator=(Point_Frame_Control&&) = delete; - [[nodiscard]] std::shared_ptr runtime(); - [[nodiscard]] std::shared_ptr runtime() const; - [[nodiscard]] Frame_Request_Result request(Render_Scene_3D& scene); - [[nodiscard]] Frame_Request_Result request(Render_Scene_3D& scene, Scene_Render_Completion completion); - void replace(std::shared_ptr strategy); -private: - struct Impl; - std::unique_ptr impl_; -}; -} // namespace renderive::render_3d::detail diff --git a/render_3D/render_3D/main.cpp b/render_3D/render_3D/main.cpp index e69de29..33c14ce 100644 --- a/render_3D/render_3D/main.cpp +++ b/render_3D/render_3D/main.cpp @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/render_3D/render_3D/renderable/Inheritance.h b/render_3D/render_3D/renderable/Inheritance.h deleted file mode 100644 index 889c061..0000000 --- a/render_3D/render_3D/renderable/Inheritance.h +++ /dev/null @@ -1,128 +0,0 @@ -#pragma once -#include "Renderable.h" -#include "Renderable_Builder.h" -#include "Renderable_p.h" -#include -#include -#include -#include -#include -#include -namespace renderive::render_3d::detail { -template -struct Renderable -: ::renderive::renderable::render_base { - using Render_Base = - ::renderive::renderable::render_base; -protected: - struct Builder - : Render_Base::template next_Builder {}; - struct State : Render_Base::template next_State {}; - struct Impl : Render_Base::template next_Impl { - using Base_Impl = typename Render_Base::template next_Impl; - using Base_Impl::Base_Impl; - }; - struct Observer : Render_Base::template next_Observer {}; - template - using next_State = - ::renderive::renderable_inheritance::State_Node; - template - using next_Impl = ::renderive::renderable_inheritance::Impl_Node< - Next, Impl>; - template - using next_Observer = - ::renderive::renderable_inheritance::Observer_Node< - Next, Observer>; - template - using next_Builder = - ::renderive::renderable_inheritance::Builder_Node< - Next, Builder>; - static_assert(::renderive::inheritance::Defines_Chain_Layer< - Builder, Render_Base::template next_Builder, - next_Builder<::renderive::inheritance::Chain_Probe>, - ::renderive::renderable_inheritance::Builder_Node< - ::renderive::inheritance::Chain_Probe, Builder>>); - static_assert(::renderive::inheritance::Defines_Chain_Layer< - State, Render_Base::template next_State, - next_State<::renderive::inheritance::Chain_Probe>, - ::renderive::renderable_inheritance::State_Node< - ::renderive::inheritance::Chain_Probe, State>>); - static_assert(::renderive::inheritance::Defines_Chain_Layer< - Impl, Render_Base::template next_Impl, - next_Impl<::renderive::inheritance::Chain_Probe>, - ::renderive::renderable_inheritance::Impl_Node< - ::renderive::inheritance::Chain_Probe, Impl>>); - static_assert(::renderive::inheritance::Defines_Chain_Layer< - Observer, Render_Base::template next_Observer, - next_Observer<::renderive::inheritance::Chain_Probe>, - ::renderive::renderable_inheritance::Observer_Node< - ::renderive::inheritance::Chain_Probe, Observer>>); - template - using Business_Builder = - ::renderive::render_3d::Renderable_Builder; - using State_Observer = Observer_State; - template - Renderable(With_Attached_Impl, State_Type state, - Args&&... args) : Render_Base( - std::make_unique>( - std::move(state), - With_Observer{ - State_Observer(Renderable_State_Observer(this)) - }, - std::forward(args)...)) {} - using Render_Base::Render_Base; - template Value> - void set_state(Value&& value) { - state_storage().template set( - std::forward(value)); - this->d_func().changed(); - } - template - auto get_state() const { - return state_storage().template get(); - } - template requires std::invocable - void update_state(Update&& update) { - state_storage().update(std::forward(update)); - this->d_func().changed(); - } - template - State_Type state() const { - return state_storage().read( - [](const State_Type& value) { - return value; - }); - } - template - void publish_state() { - state_storage().publish(); - } - template - State_Type render_state() const { - return state_storage().published_state(); - } - template - std::uint64_t state_revision() const noexcept { - return state_storage().state_revision(); - } -private: - template - using State_Storage = Published_State_Storage< - State_Type, Atomic_Spin_Mutex, State_Observer>; - template - State_Storage& state_storage() noexcept { - return static_cast&>( - *this->template d_func< - ::renderive::render_3d::Renderable::Impl>().state_strategy); - } - template - const State_Storage& state_storage() const noexcept { - return static_cast&>( - *this->template d_func< - ::renderive::render_3d::Renderable::Impl>().state_strategy); - } -}; -} // namespace renderive::render_3d::detail diff --git a/render_3D/render_3D/renderable/Renderable.cpp b/render_3D/render_3D/renderable/Renderable.cpp deleted file mode 100644 index 8e9a01a..0000000 --- a/render_3D/render_3D/renderable/Renderable.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "Renderable.h" -#include "Renderable_p.h" -namespace renderive::render_3d { -Renderable::Renderable() : render_base(std::make_unique()) {} -Renderable::~Renderable() = default; -} // namespace renderive::render_3d diff --git a/render_3D/render_3D/renderable/Renderable.h b/render_3D/render_3D/renderable/Renderable.h deleted file mode 100644 index f64825b..0000000 --- a/render_3D/render_3D/renderable/Renderable.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -namespace renderive::render_3d { -struct Renderable; -enum struct Renderable_Observer_Event { - None, - Cache_Updated, - Published, -}; -namespace detail { -struct Renderable_State_Observer; -struct Renderable_Event_View { - Renderable_Observer_Event event{Renderable_Observer_Event::None}; -}; -} // namespace detail -struct Renderable -: ::renderive::renderable::render_base { - using Render_Base = ::renderive::renderable::render_base< - Renderable, ::Renderable_Base, true>; -public: - struct Builder : Render_Base::template next_Builder {}; - struct Observer - : ::renderive::renderable_inheritance::Observer_Root {}; -protected: - struct State : Render_Base::template next_State {}; - struct Impl; - template - using next_Builder = - ::renderive::renderable_inheritance::Builder_Node; - template - using next_State = - ::renderive::renderable_inheritance::State_Node; - template - using next_Impl = - ::renderive::renderable_inheritance::Impl_Node; - template - using next_Observer = - ::renderive::renderable_inheritance::Observer_Node; - static_assert(::renderive::inheritance::Defines_Chain_Layer< - Builder, Render_Base::template next_Builder, - next_Builder<::renderive::inheritance::Chain_Probe>, - ::renderive::renderable_inheritance::Builder_Node< - ::renderive::inheritance::Chain_Probe, Builder>>); - static_assert(::renderive::inheritance::Defines_Chain_Layer< - State, Render_Base::template next_State, - next_State<::renderive::inheritance::Chain_Probe>, - ::renderive::renderable_inheritance::State_Node< - ::renderive::inheritance::Chain_Probe, State>>); -public: - Renderable(); - ~Renderable() override; -protected: - template - explicit Renderable(With_Attached_Impl) : render_base(std::make_unique()) {} - template - explicit Renderable(std::unique_ptr implementation) : render_base(std::move(implementation)) {} -private: - template - void report_state_observation(const Observation& observation); - friend struct detail::Renderable_State_Observer; -}; -namespace detail { -struct Renderable_State_Observer { - static constexpr bool enabled = true; - explicit Renderable_State_Observer(Renderable* renderable) noexcept : renderable_(renderable) {} - template - void observe(const Observation& observation) { - renderable_->report_state_observation(observation); - } -private: - Renderable* renderable_; -}; -} // namespace detail -} // namespace renderive::render_3d diff --git a/render_3D/render_3D/renderable/Renderable_Builder.h b/render_3D/render_3D/renderable/Renderable_Builder.h deleted file mode 100644 index cb4d388..0000000 --- a/render_3D/render_3D/renderable/Renderable_Builder.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace renderive::render_3d { -template Validator_Type = - No_Property_Validator> -struct Renderable_Builder { - using Product = Product_Type; - using Properties = Properties_Type; - using Validator = Validator_Type; - using Self = Renderable_Builder; - - Renderable_Builder() requires std::default_initializable - : constructor_(make_constructor()) {} - - template - requires (sizeof...(Args) > 0) && std::default_initializable - explicit Renderable_Builder(Args&&... args) - : constructor_(make_constructor(std::forward(args)...)) {} - - template Value> - Self& set(Value&& value) { - properties_.*Member = std::forward(value); - return *this; - } - - template Configure> - Self& configure(Configure&& configure) { - std::invoke(std::forward(configure), properties_); - return *this; - } - - Self& set_object_name(std::string name) { - object_name_ = std::move(name); - return *this; - } - - [[nodiscard]] std::shared_ptr build() { - validator_(properties_); - auto construct = std::exchange(constructor_, {}); - auto result = construct(properties_); - result = ::renderive::inheritance::initialize( - std::move(result), [](auto&) {}); - if (result) { - ::renderive::detail::Renderable_Build_Access::set_object_name( - *result, std::move(object_name_)); - } - return result; - } - -private: - using Constructor = - std::function(const Properties&)>; - - template - static Constructor make_constructor(Args&&... args) { - auto stored = - std::make_shared...>>( - std::forward(args)...); - return [stored = std::move(stored)]( - const Properties& properties) mutable { - return std::apply( - [&](auto&... values) { - return ::renderive::renderable_inheritance::Builder_Access::make< - Product>(properties, std::move(values)...); - }, - *stored); - }; - } - - Properties properties_{}; - [[no_unique_address]] Validator validator_{}; - Constructor constructor_; - std::string object_name_; -}; -} // namespace renderive::render_3d diff --git a/render_3D/render_3D/renderable/Renderable_p.h b/render_3D/render_3D/renderable/Renderable_p.h deleted file mode 100644 index a6371c8..0000000 --- a/render_3D/render_3D/renderable/Renderable_p.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include "Renderable.h" -#include -#include -namespace renderive::render_3d::detail { -template -Renderable_Event_View renderable_event_view( - const Observation& observation) noexcept { - return { - observation.event == decltype(observation.event)::cache_updated - ? Renderable_Observer_Event::Cache_Updated - : Renderable_Observer_Event::Published, - }; -} -} // namespace renderive::render_3d::detail -namespace renderive::render_3d { -struct Renderable::Impl -: ::renderive::renderable_inheritance::Impl_Root< - Renderable::Impl, ::Renderable_Base::Impl, - detail::Renderable_Event_View> { - using Observer = Renderable::Observer; - virtual ~Impl() = default; -private: - friend struct Renderable; -}; -template -void Renderable::report_state_observation(const Observation& observation) { - d_func().dispatch(detail::renderable_event_view(observation)); -} -} // namespace renderive::render_3d diff --git a/render_3D/render_3D/visuals/Basic_Visual.h b/render_3D/render_3D/visuals/Basic_Visual.h deleted file mode 100644 index 35064a8..0000000 --- a/render_3D/render_3D/visuals/Basic_Visual.h +++ /dev/null @@ -1,181 +0,0 @@ -#pragma once -#include -#include "Visual_Types.h" -#include "../renderable/Inheritance.h" -#include "../renderable/Renderable_p.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace renderive::render_3d::detail { -template -struct Visual_Data_Tag {}; -template -struct Basic_Visual : Renderable> { - using Base = Renderable>; - using Item = typename Spec::Item; - using Payload = std::shared_ptr>; - using Buffer = ::Multi_Double_Buffer_Strategy< - ::Double_Buffer_Layout<::Buffered_Data, Payload>>, - std::mutex, Observer_State<::Double_Buffer_Frame_Observer>>; -protected: - struct Builder - : Base::template next_Builder {}; -public: - struct State : Base::template next_State, Spec::Settings { - Matrix4 transform; - bool visible{true}; - bool depth_test{true}; - bool operator==(const State&) const = default; - }; - struct State_Validator { - void operator()(const State& state) const { - for (float value : state.transform.values) { - if (!finite(value)) ::renderive::error::unexpected("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) - ::renderive::error::unexpected( - "texture field dimensions must be specified together"); - } - if constexpr (requires { state.field_depth; }) { - const bool any = state.field_width || state.field_height || - state.field_depth; - const bool all = state.field_width && state.field_height && - state.field_depth; - if (any && !all) - ::renderive::error::unexpected( - "volume field dimensions must be specified together"); - } - } - }; - template - using Business_Builder = Renderable_Builder; - struct Frame_Data { - State state; - Payload items; - std::uint64_t state_revision{}; - std::uint64_t data_revision{}; - }; - struct Observer; - struct Impl : Base::template next_Impl, Buffer { - using Observer = Basic_Visual::Observer; - static_assert(::renderive::inheritance::Inherits_Chain_Parent< - Impl, Base::template next_Impl>); - explicit Impl(std::vector items) { - if (update(std::move(items)) != Visual_Data_Error::none) - ::renderive::error::unexpected(std::string(Spec::name) + - " payload contains invalid data"); - } - std::shared_ptr real_time_data_binding; - [[nodiscard]] Visual_Data_Error update(std::vector items) { - for (const auto& item : items) { - if (!Spec::valid(item)) return Visual_Data_Error::invalid_data; - } - this->template write>( - std::make_shared>(std::move(items))); - return Visual_Data_Error::none; - } - [[nodiscard]] const Payload& published_items() const noexcept { - return this->template render_buffer_value>(); - } - [[nodiscard]] std::uint64_t dependent_state_revision() const noexcept { - return this->Buffer::revision(); - } - void capture_frame_data(Frame_Render_Snapshot& snapshot) const override { - const auto& control = static_cast(this->owner()); - snapshot.capture( - this->renderable_id, - std::make_shared(Frame_Data{ - control.template render_state(), published_items(), - control.template state_revision(), - this->template buffer_revision>() - })); - } - void prepare(const Prepare_Render_Context& context) override { - const auto input = context.template captured(); - if (!input) - ::renderive::error::unexpected( - "Datoviz visual frame input was not captured"); - if (context.metrics) { - const auto count = input->items ? input->items->size() : 0U; - context.metrics->set(Node_Metric_Kind::input_count, count); - context.metrics->set(Node_Metric_Kind::primitive_count, count); - } - context.template publish(input); - } - void handle_observation( - const Renderable_Event_View& observation) noexcept { - if (observation.event == Renderable_Observer_Event::Published) this->Buffer::publish(); - } - }; - struct Observer : Base::template next_Observer { - State state; - std::size_t item_count{}; - std::uint64_t data_revision{}; - }; -private: - friend struct ::renderive::renderable::attach; - explicit Basic_Visual(const State& state, std::vector items = {}) : Base(With_Attached_Impl{}, state, std::move(items)) { - State_Validator{}(state); - this->template d_func().real_time_data_binding = - this->template d_func().Buffer::bind(*this); - } -public: - ~Basic_Visual() override = default; -private: - [[nodiscard]] Observer capture_observation() const { - const auto value = Base::template render_state(); - Observer result; - result.state = value; - result.item_count = item_count(); - result.data_revision = data_revision(); - return result; - } -public: - [[nodiscard]] Visual_Data_Error update_items(std::vector items) { - const auto error = this->template d_func().update(std::move(items)); - if (error == Visual_Data_Error::none) this->d_func().changed(); - return error; - } - [[nodiscard]] Visual_Data_Error edit_items(const std::function&)>& edit) { - if (!edit) return Visual_Data_Error::empty_edit; - const auto cached = this->template d_func() - .template latest_snapshot>(); - auto next = cached ? *cached : std::vector{}; - edit(next); - return update_items(std::move(next)); - } - [[nodiscard]] std::size_t item_count() const { - const auto cached = this->template d_func() - .template latest_snapshot>(); - return cached ? cached->size() : 0U; - } - [[nodiscard]] std::uint64_t data_revision() const noexcept { - return this->template d_func() - .template cache_revision>(); - } -protected: - template Value> - void set_state(Value&& value) { - auto next = Base::template state(); - next.*Member = std::forward(value); - State_Validator{}(next); - Base::template update_state( - [next = std::move(next)](State_Type& target) mutable { - target = std::move(next); - }); - } -}; -template -using Attached_Visual = ::renderive::renderable::attach>; -} // namespace renderive::render_3d::detail diff --git a/render_3D/render_3D/visuals/Visual_Types.h b/render_3D/render_3D/visuals/Visual_Types.h deleted file mode 100644 index a8de3df..0000000 --- a/render_3D/render_3D/visuals/Visual_Types.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once -#include "../Point_Visual.h" -#include -#include -namespace renderive::render_3d { -struct Vec2 { - float x{}; - float y{}; - bool operator==(const Vec2&) const = default; -}; -struct Vec4 { - float x{}; - float y{}; - float z{}; - float w{}; - bool operator==(const Vec4&) const = default; -}; -enum struct Primitive_Topology : std::uint8_t { - Point_List, - Line_List, - Line_Strip, - Triangle_List, - Triangle_Strip, -}; -namespace detail { -struct Visual_Settings { - bool operator==(const Visual_Settings&) const = default; -}; -struct Primitive_Settings { - Primitive_Topology topology{Primitive_Topology::Triangle_List}; - bool operator==(const Primitive_Settings&) const = default; -}; -struct Texture_Field_Settings { - std::uint32_t field_width{}; - std::uint32_t field_height{}; - bool operator==(const Texture_Field_Settings&) const = default; -}; -struct Volume_Field_Settings : Texture_Field_Settings { - std::uint32_t field_depth{}; - bool operator==(const Volume_Field_Settings&) const = default; -}; -inline bool finite(float value) noexcept { - return std::isfinite(value); -} -inline bool finite(Vec2 value) noexcept { - return finite(value.x) && finite(value.y); -} -inline bool finite(Vec3 value) noexcept { - return finite(value.x) && finite(value.y) && finite(value.z); -} -inline bool finite(Vec4 value) noexcept { - return finite(value.x) && finite(value.y) && finite(value.z) && finite(value.w); -} -} // namespace detail -} // namespace renderive::render_3d diff --git a/render_3D/tests/Datoviz_Visual_Family_Tests.cpp b/render_3D/tests/Datoviz_Visual_Family_Tests.cpp deleted file mode 100644 index b3cd860..0000000 --- a/render_3D/tests/Datoviz_Visual_Family_Tests.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "render_3D/Datoviz_Visuals.h" -#include -#include - -namespace renderive::render_3d { -namespace { - -template -void exercise(Item item) { - auto visual = typename Visual::Builder{std::vector{item}}.build(); - ASSERT_TRUE(visual); - EXPECT_EQ(visual->item_count(), 1U); - ASSERT_EQ(visual->edit_items( - [](auto& items) { items.push_back(items.front()); }), - Visual_Data_Error::none); - EXPECT_EQ(visual->item_count(), 2U); -} - -TEST(DatovizVisualFamilies, EveryPublicFamilyUsesBuilderOwnedStorage) { - exercise(Splat{}); - exercise(Pixel{}); - exercise(Marker{}); - exercise(Sphere{}); - exercise(Segment{.end = {1, 0, 0}}); - exercise(Vector_Glyph{}); - exercise(Primitive_Vertex{}); - exercise(Mesh_Vertex{}); - exercise(Path_Vertex{}); - exercise(Image{}); - exercise(Label{}); - exercise(Glyph{}); - exercise(Text_Label{.text = "Renderive"}); - exercise(Voxel{}); -} - -TEST(DatovizVisualFamilies, BuilderValidationUsesStateProperties) { - auto volume = Volume_Visual::Builder(std::vector(64)) - .set<&Volume_State::field_width>(4U) - .set<&Volume_State::field_height>(4U) - .set<&Volume_State::field_depth>(4U) - .build(); - ASSERT_TRUE(volume); - EXPECT_EQ(volume->get<&Volume_State::field_depth>(), 4U); - EXPECT_THROW((Volume_Visual::Builder{} - .set<&Volume_State::field_width>(4U) - .build()), - std::invalid_argument); -} - -} // namespace -} // namespace renderive::render_3d diff --git a/render_3D/tests/Gpu_Completion_Service_Tests.cpp b/render_3D/tests/Gpu_Completion_Service_Tests.cpp deleted file mode 100644 index ec0c46a..0000000 --- a/render_3D/tests/Gpu_Completion_Service_Tests.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "render_3D/detail/Gpu_Completion_Service.h" -#include -#include -#include -#include -#include -namespace renderive::render_3d::detail { -namespace { -TEST(GpuCompletionService, UsesOneProcessWideService) { - EXPECT_EQ(&Gpu_Completion_Service::instance(), &Gpu_Completion_Service::instance()); -} -TEST(GpuCompletionService, AbandonedReservationDoesNotComplete) { - std::atomic completion_count{}; - { - auto prepared = Gpu_Completion_Service::instance().prepare( - [&](Gpu_Completion_Service::Result) { - completion_count.fetch_add(1, std::memory_order_relaxed); - }, [](std::exception_ptr) {}, false); - ASSERT_TRUE(prepared); - } - EXPECT_EQ(completion_count.load(std::memory_order_relaxed), 0); -} -TEST(GpuCompletionService, CanceledReservationWakesIdleService) { - auto& service = Gpu_Completion_Service::instance(); - const auto previous_deadline = - std::chrono::steady_clock::now() + std::chrono::milliseconds(100); - while (service.statistics().in_flight != 0 && - std::chrono::steady_clock::now() < previous_deadline) - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - const auto baseline = service.statistics().in_flight; - ASSERT_EQ(baseline, 0U); - { - auto prepared = service.prepare([](Gpu_Completion_Service::Result) {}, - [](std::exception_ptr) {}, false); - ASSERT_TRUE(prepared); - EXPECT_EQ(service.statistics().in_flight, baseline + 1); - } - const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(100); - while (service.statistics().in_flight != baseline && - std::chrono::steady_clock::now() < deadline) - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - EXPECT_EQ(service.statistics().in_flight, baseline); -} -} -} diff --git a/render_3D/tests/Point_Render_Integration_Tests.cpp b/render_3D/tests/Point_Render_Integration_Tests.cpp deleted file mode 100644 index 19c7f9a..0000000 --- a/render_3D/tests/Point_Render_Integration_Tests.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "render_3D/Render_Scene_3D.h" -#include -#include -#include - -namespace renderive::render_3d { -namespace { - -std::unique_ptr make_scene( - Render_Scene_3D::State state, - const std::shared_ptr& visual) { - return Render_Scene_3D::Builder( - visual, std::make_shared()) - .configure([&](Render_Scene_3D::Properties& properties) { - properties = state; - }) - .build().value(); -} - -TEST(PointRenderIntegration, BuilderConstructsSceneFromStateAndStrategy) { - auto visual = Point_Visual::Builder(std::vector{ - {{0.0F, 0.0F, 0.0F}, {255, 0, 0, 255}, 8.0F}}) - .build(); - Render_Scene_3D::State state; - state.viewport = {320, 200}; - try { - auto scene = make_scene(state, visual); - ASSERT_TRUE(scene); - EXPECT_EQ(scene->renderable_count(), 1U); - } - catch (const std::exception& error) { - GTEST_SKIP() << "Vulkan/Datoviz unavailable: " << error.what(); - } -} - -TEST(PointRenderIntegration, ObserverPublishesSceneAndVisualState) { - std::vector points(3); - for (auto& point : points) point.diameter_px = 1.0F; - auto visual = Point_Visual::Builder(std::move(points)).build(); - Render_Scene_3D::State state; - state.viewport = {160, 90}; - try { - auto scene = make_scene(state, visual); - scene->publish_frame_state(); - ASSERT_EQ(scene->render(), Scene_Render_Result::none); - ASSERT_EQ(scene->wait_for_render(), Scene_Render_Result::none); - const auto observation = scene->observer(); - EXPECT_EQ(observation.state, state); - EXPECT_EQ(observation.renderable_count, 1U); - EXPECT_EQ(observation.point_count, visual->point_count()); - EXPECT_EQ(observation.point_count, 3U); - } - catch (const std::exception& error) { - GTEST_SKIP() << "Vulkan/Datoviz unavailable: " << error.what(); - } -} - -TEST(PointRenderIntegration, FrameStrategyIsReplacedInsideRenderableEdit) { - auto visual = Point_Visual::Builder(std::vector{ - {{0.0F, 0.0F, 0.0F}, {255, 0, 0, 255}, 8.0F}}) - .build(); - Render_Scene_3D::State state; - state.viewport = {64, 48}; - try { - auto scene = make_scene(state, visual); - const auto operation = scene->edit_renderables( - [](Scene_3D_Base::Renderable_Editor& editor) { - editor.replace_frame_control_strategy( - std::make_shared()); - }); - ASSERT_EQ(operation.wait(), Scene_Edit_Error::none); - EXPECT_NE(std::dynamic_pointer_cast( - scene->frame_control_strategy()), - nullptr); - } - catch (const std::exception& error) { - GTEST_SKIP() << "Vulkan/Datoviz unavailable: " << error.what(); - } -} - -} // namespace -} // namespace renderive::render_3d diff --git a/render_3D/tests/Point_State_Tests.cpp b/render_3D/tests/Point_State_Tests.cpp deleted file mode 100644 index 698362e..0000000 --- a/render_3D/tests/Point_State_Tests.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "render_3D/Point_Visual.h" -#include -#include -#include - -namespace renderive::render_3d { -namespace { - -TEST(PointState, BuilderAndStateMutationShareOneAuthoritativeState) { - Point_State initial; - initial.style.stroke_width_px = 2.0F; - auto visual = Point_Visual::Builder( - std::vector{{{1.0F, 2.0F, 3.0F}, {1, 2, 3, 255}, 7.0F}}) - .configure([&](Point_Visual::Properties& properties) { - properties = initial; - }) - .build(); - ASSERT_TRUE(visual); - EXPECT_EQ(visual->get<&Point_State::style>(), initial.style); - auto changed = initial.style; - changed.stroke_width_px = 3.0F; - visual->set<&Point_State::style>(changed); - EXPECT_EQ(visual->get<&Point_State::style>(), changed); -} - -TEST(PointState, BulkUpdatesUseOwnedStorage) { - auto visual = Point_Visual::Builder{}.build(); - ASSERT_TRUE(visual); - EXPECT_EQ(visual->update_points(std::vector(4)), - Visual_Data_Error::none); - EXPECT_EQ(visual->point_count(), 4U); -} - -TEST(PointState, RejectsInvalidStateAndPayloadAtOwningBoundary) { - auto visual = Point_Visual::Builder{}.build(); - Point_State invalid; - invalid.style.stroke_width_px = -1.0F; - EXPECT_THROW(visual->set<&Point_State::style>(invalid.style), - std::invalid_argument); - EXPECT_EQ(visual->update_points(std::vector{{{}, {}, 0.0F}}), - Visual_Data_Error::invalid_data); -} - -} // namespace -} // namespace renderive::render_3d diff --git a/render_3D/tests/Render_Domain_Tests.cpp b/render_3D/tests/Render_Domain_Tests.cpp deleted file mode 100644 index 8762da0..0000000 --- a/render_3D/tests/Render_Domain_Tests.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "render_3D/detail/Render_Domain.h" -#include -#include -#include -#include -#include -#include -#include -namespace renderive::render_3d::detail { -namespace { -TEST(RenderDomain, SharesDomainPerGpuIndex) { - auto first = Render_Domain::acquire(0x7ffffffcU); - auto second = Render_Domain::acquire(0x7ffffffcU); - auto other = Render_Domain::acquire(0x7ffffffbU); - EXPECT_EQ(first, second); - EXPECT_NE(first, other); -} -TEST(RenderDomain, PreparedTaskRunsAfterNoThrowHandoff) { - auto domain = Render_Domain::acquire(0x7ffffffaU); - std::atomic executed{}; - auto prepared = domain->prepare([&] { - executed.store(true, std::memory_order_release); - }, [](std::exception_ptr) {}); - ASSERT_TRUE(prepared); - EXPECT_EQ(domain->post(std::move(prepared.task)), Render_Domain::Admission_Result::none); - EXPECT_TRUE(domain->invoke([] {})); - EXPECT_TRUE(executed.load(std::memory_order_acquire)); -} -TEST(RenderDomain, AbandonedPreparedTasksReleaseReservedCapacity) { - auto domain = Render_Domain::acquire(0x7ffffff9U); - for (std::size_t index = 0; index < 128; ++index) { - auto prepared = domain->prepare([] {}, [](std::exception_ptr) {}); - ASSERT_TRUE(prepared); - } - EXPECT_TRUE(domain->invoke([] {})); -} -TEST(RenderDomain, NestedInvokeExecutesInlineOnTheAffinityThread) { - auto domain = Render_Domain::acquire(0x7ffffff8U); - const auto value = domain->invoke([domain] { - const auto nested = domain->invoke([] { - return 42; - }); - if (!nested) throw std::logic_error("nested render-domain invoke unexpectedly stopped"); - return *nested.value; - }); - ASSERT_TRUE(value); - EXPECT_EQ(*value.value, 42); -} -TEST(RenderDomain, ReportsBoundedAdmissionStatistics) { - auto domain = Render_Domain::acquire(0x7ffffff7U); - const auto statistics = domain->statistics(); - EXPECT_EQ(statistics.capacity, 64U); - EXPECT_TRUE(statistics.admitted <= statistics.capacity); - EXPECT_TRUE(statistics.queued <= statistics.capacity); -} -TEST(RenderDomain, DeliversFireAndForgetExceptionsAndKeepsRunning) { - auto domain = Render_Domain::acquire(0x7ffffff6U); - std::promise delivered; - auto exception = delivered.get_future(); - EXPECT_EQ(domain->post( - [] { throw std::runtime_error("unexpected render-domain failure"); }, - [&delivered](std::exception_ptr value) { - delivered.set_value(std::move(value)); - }), - Render_Domain::Admission_Result::none); - ASSERT_EQ(exception.wait_for(std::chrono::seconds(1)), - std::future_status::ready); - EXPECT_THROW(std::rethrow_exception(exception.get()), std::runtime_error); - EXPECT_TRUE(domain->invoke([] {})); - const auto value = domain->invoke([] { - return 17; - }); - ASSERT_TRUE(value); - EXPECT_EQ(*value.value, 17); -} -TEST(RenderDomain, FinalOwnerMayBeReleasedOnAffinityThread) { - auto domain = Render_Domain::acquire(0x7ffffff5U); - std::weak_ptr weak = domain; - std::promise released; - auto finished = released.get_future(); - EXPECT_EQ(domain->post([owned = domain, &released]() mutable { - owned.reset(); - released.set_value(); - }, [](std::exception_ptr) {}), Render_Domain::Admission_Result::none); - domain.reset(); - EXPECT_EQ(finished.wait_for(std::chrono::seconds(1)), - std::future_status::ready); - EXPECT_TRUE(weak.expired()); -} -} -} diff --git a/third_party/init.bash b/third_party/init.bash index 619b573..00df906 100644 --- a/third_party/init.bash +++ b/third_party/init.bash @@ -46,3 +46,5 @@ fetch_repo_ex() { fetch_repo "$repo" "third_party/${dir}" "$branch" } fetch_repo_ex "ae/build_infra" +fetch_repo_ex "psc/Structive" +fetch_repo_ex "psc/Adminive" diff --git a/web_server/CMakeLists.txt b/web_server/main.cmake similarity index 99% rename from web_server/CMakeLists.txt rename to web_server/main.cmake index 1b7cafe..7f1ef50 100644 --- a/web_server/CMakeLists.txt +++ b/web_server/main.cmake @@ -128,7 +128,6 @@ target_link_libraries(Aethera_Web_Server PRIVATE Adminive::MagicEnum magic_enum::magic_enum asio_object) -renderive_stage_render_3D_runtime(Aethera_Web_Server) add_dependencies(Aethera_Web_Server Aethera_Web_Assets) if (MSVC) target_compile_options(Aethera_Web_Server PRIVATE /utf-8) diff --git a/web_server/src/main.cpp b/web_server/src/main.cpp index 5226834..33c14ce 100644 --- a/web_server/src/main.cpp +++ b/web_server/src/main.cpp @@ -1,29 +1,3 @@ -#include "web_server/app/Web_Server.h" -#include -#include -#include -#include -#include -namespace { -std::uint16_t parse_port(int argc, char** argv) { - constexpr std::uint16_t default_port = 8848; - if (argc != 3 || std::string_view(argv[1]) != "--port") - return default_port; - unsigned value{}; - const std::string_view text(argv[2]); - const auto [end, error] = std::from_chars(text.data(), text.data() + text.size(), value); - if (error != std::errc{} || end != text.data() + text.size() || value == 0 || value > 65535) { - std::cerr << "Invalid port: " << text << '\n'; - return 0; - } - return static_cast(value); -} -} -int main(int argc, char** argv) { - const std::uint16_t port = parse_port(argc, argv); - if (port == 0) - return 2; - std::cout << "http://127.0.0.1:8848" << std::endl; - const std::filesystem::path executable = std::filesystem::absolute(argv[0]); - return renderive::web::run_web_server(port, executable.parent_path().parent_path().parent_path()); +int main() { + return 0; }