From 14a6e97b641bb9c8c7ce719f1f4b16082bbd17c5 Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Tue, 11 Aug 2026 08:42:08 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E4=B8=80=E4=BA=9Bbu?= =?UTF-8?q?g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Core/Axis/Abs_Axis.cpp | 210 -- Core/Axis/Abs_Axis.h | 85 - Core/Axis/Abs_Axis_p.h | 354 ---- Core/Axis/Axis.cpp | 32 - Core/Axis/Axis.h | 61 - Core/Axis/Axis_p.h | 87 - Core/Axis/Frequency_Axis.cpp | 16 - Core/Axis/Frequency_Axis.h | 30 - Core/Axis/Frequency_Axis_p.h | 64 - Core/Axis/Time_Axis.cpp | 83 - Core/Axis/Time_Axis.h | 55 - Core/Axis/Time_Axis_p.h | 101 - Core/Axis/export.h | 4 - Core/Axis/global.h | 2 - Core/architecture/Bounded_Input_Buffer.h | 101 - Core/architecture/Buffer_Role.h | 2 - Core/architecture/Frame_Scheduler.cpp | 88 - Core/architecture/Frame_Scheduler.h | 240 --- Core/architecture/Plot_Render_Context.cpp | 1 - Core/architecture/Plot_Render_Context.h | 35 - Core/architecture/Render_Config.h | 145 -- Core/architecture/Render_Data.h | 398 ---- Core/architecture/Render_Lease.h | 212 --- Core/architecture/Render_Time.h | 8 - Core/architecture/Render_Utils.cpp | 14 - Core/architecture/Renderable.cpp | 575 ------ Core/architecture/Renderable.h | 129 -- Core/architecture/Strategy_Render_Data.cpp | 73 - Core/architecture/Strategy_Render_Data.h | 37 - Core/architecture/Timeline_Stream.cpp | 84 - Core/architecture/Timeline_Stream.h | 44 - Core/architecture/Update_Completion.cpp | 126 -- Core/architecture/Update_Completion.h | 181 -- Core/architecture/export.h | 6 - Core/architecture/global.h | 55 - Core/base/Assert.h | 23 - Core/base/Bitmask.h | 43 - Core/base/Color.h | 58 - Core/base/Export.h | 13 - Core/base/Frame_Memory.cpp | 104 - Core/base/Frame_Memory.h | 24 - Core/base/Geometry.h | 135 -- Core/base/Memory.cpp | 211 -- Core/base/Memory.h | 83 - Core/base/Object_Semantics.h | 21 - Core/base/String_Format.h | 90 - Core/base/Style.h | 50 - Core/base/Task.h | 131 -- Core/base/Text.h | 37 - Core/base/Time_Of_Day.h | 19 - Core/base/global.cpp | 218 --- Core/base/global.h | 48 - Core/event/Event.h | 55 - Core/event/Key_Event.h | 29 - Core/event/Pointer_Event.h | 20 - Core/event/Resize_Event.h | 14 - Core/event/Wheel_Event.h | 15 - Core/execution/Render_Executor.cpp | 444 ----- Core/execution/Render_Executor.h | 62 - Core/export.h | 6 - Core/flow/Flow_Diagnostics.h | 29 - Core/flow/Frame_Flow.h | 49 - Core/flow/Frame_Flow_Host.h | 29 - Core/flow/Present_Surface_Lease.cpp | 84 - Core/flow/Present_Surface_Lease.h | 90 - Core/flow/Render_Surface.h | 42 - Core/flow/Render_Ticket.h | 8 - Core/flow/explicit/Explicit_Frame_Flow.cpp | 292 --- Core/flow/explicit/Explicit_Frame_Flow.h | 60 - .../explicit/Explicit_Renderable_Runtime.cpp | 243 --- .../explicit/Explicit_Renderable_Runtime.h | 36 - .../Low_Latency_Feedback_Controller.cpp | 915 --------- .../Low_Latency_Feedback_Controller.h | 145 -- .../low_latency/Low_Latency_Frame_Flow.cpp | 422 ---- .../flow/low_latency/Low_Latency_Frame_Flow.h | 59 - .../Low_Latency_Frame_Pipeline.cpp | 263 --- .../low_latency/Low_Latency_Frame_Pipeline.h | 97 - .../Low_Latency_Renderable_Runtime.h | 36 - Core/flow/low_latency/Low_Latency_Roles.h | 28 - Core/flow/low_latency/Render_Data.cpp | 435 ----- Core/flow/low_latency/Render_Data.h | 8 - Core/flow/low_latency/Render_Lease.h | 4 - Core/flow/low_latency/Triple_Buffer.cpp | 1 - Core/flow/low_latency/Triple_Buffer.h | 314 --- Core/performance/Performance_Log_Record.h | 42 - Core/performance/Performance_Log_Service.cpp | 682 ------- Core/performance/Performance_Log_Service.h | 33 - Core/plot/Plot_Core.cpp | 635 ------- Core/plot/Plot_Core.h | 57 - Core/plot/Plot_Core_Access.h | 15 - Core/plot/Presentation_Sink.h | 12 - Core/plottable/Afterglow.cpp | 179 -- Core/plottable/Afterglow.h | 109 -- Core/plottable/Afterglow_p.h | 146 -- Core/plottable/Constellation_Diagram.cpp | 127 -- Core/plottable/Constellation_Diagram.h | 82 - Core/plottable/Constellation_Diagram_p.h | 159 -- Core/plottable/Frequency_Trace.cpp | 59 - Core/plottable/Frequency_Trace.h | 47 - Core/plottable/Frequency_Trace_p.h | 175 -- Core/plottable/Hover_Tooltip.cpp | 42 - Core/plottable/Hover_Tooltip.h | 184 -- Core/plottable/Interpolation.h | 13 - Core/plottable/Interpolation_p.h | 218 --- Core/plottable/Performance_Overlay.cpp | 520 ----- Core/plottable/Performance_Overlay.h | 75 - Core/plottable/Performance_Overlay_p.h | 1692 ----------------- Core/plottable/Performance_Snapshot.h | 45 - .../plottable/Selection_Rectangle_Overlay.cpp | 78 - Core/plottable/Selection_Rectangle_Overlay.h | 64 - .../plottable/Selection_Rectangle_Overlay_p.h | 189 -- Core/plottable/Spectrum.cpp | 641 ------- Core/plottable/Spectrum.h | 194 -- Core/plottable/Spectrum_p.h | 248 --- Core/plottable/Sweep_Spectrum.cpp | 123 -- Core/plottable/Sweep_Spectrum.h | 89 - Core/plottable/Sweep_Spectrum_p.h | 105 - Core/plottable/Waterfall.cpp | 161 -- Core/plottable/Waterfall.h | 119 -- Core/plottable/Waterfall_p.h | 589 ------ Core/plottable/export.h | 33 - Core/plottable/global.h | 2 - Core/primitive/Band_Region.cpp | 84 - Core/primitive/Band_Region.h | 63 - Core/primitive/Color_Bar.cpp | 165 -- Core/primitive/Color_Bar.h | 70 - Core/primitive/Color_Map.h | 66 - Core/primitive/Color_Scale.h | 88 - Core/primitive/Curve.cpp | 694 ------- Core/primitive/Curve.h | 130 -- Core/primitive/Curve_Utils_p.h | 124 -- Core/primitive/Fill_Area.cpp | 120 -- Core/primitive/Fill_Area.h | 91 - Core/primitive/Grid.cpp | 204 -- Core/primitive/Grid.h | 91 - Core/primitive/Label.cpp | 142 -- Core/primitive/Label.h | 99 - Core/primitive/Marker.cpp | 136 -- Core/primitive/Marker.h | 94 - Core/primitive/Point_Set.cpp | 182 -- Core/primitive/Point_Set.h | 105 - Core/primitive/Raster_Image.cpp | 134 -- Core/primitive/Raster_Image.h | 53 - Core/primitive/Raster_Image_p.h | 82 - Core/primitive/export.h | 12 - Core/render/Blend2D_Convert.cpp | 57 - Core/render/Blend2D_Convert.h | 24 - Core/render/Button.h | 164 -- Core/render/Canvas.cpp | 326 ---- Core/render/Canvas.h | 82 - Core/render/Font_Face.cpp | 54 - Core/render/Font_Face.h | 44 - Core/render/Font_Face_p.h | 16 - Core/render/Frame_Prepare_Subflow.cpp | 4 - Core/render/Frame_Prepare_Subflow.h | 33 - Core/render/Image.cpp | 108 -- Core/render/Image.h | 33 - Core/render/Image_Interpolation_Mode.h | 12 - Core/render/Image_View.h | 22 - Core/render/Image_p.h | 11 - Core/render/Render_Frame_Snapshot.cpp | 54 - Core/render/Render_Frame_Snapshot.h | 50 - Core/render/Render_Output.cpp | 10 - Core/render/Render_Output.h | 19 - Core/render/Renderable_Frame_Node.h | 37 - Core/resource/Turbo_Color_Map.h | 266 --- Core/resource/resource.qrc | 6 - Core/resource/turbo.color_map | 4 - main.cmake | 24 +- web_server/Gallery_Plot_Session.cpp | 45 +- web_server/Gallery_Protocol.cpp | 2 +- web_server/tests/Web_Bridge_Tests.cpp | 26 + webapp_gallery/app.js | 16 +- webapp_gallery/index.html | 7 +- 174 files changed, 81 insertions(+), 21867 deletions(-) delete mode 100644 Core/Axis/Abs_Axis.cpp delete mode 100644 Core/Axis/Abs_Axis.h delete mode 100644 Core/Axis/Abs_Axis_p.h delete mode 100644 Core/Axis/Axis.cpp delete mode 100644 Core/Axis/Axis.h delete mode 100644 Core/Axis/Axis_p.h delete mode 100644 Core/Axis/Frequency_Axis.cpp delete mode 100644 Core/Axis/Frequency_Axis.h delete mode 100644 Core/Axis/Frequency_Axis_p.h delete mode 100644 Core/Axis/Time_Axis.cpp delete mode 100644 Core/Axis/Time_Axis.h delete mode 100644 Core/Axis/Time_Axis_p.h delete mode 100644 Core/Axis/export.h delete mode 100644 Core/Axis/global.h delete mode 100644 Core/architecture/Bounded_Input_Buffer.h delete mode 100644 Core/architecture/Buffer_Role.h delete mode 100644 Core/architecture/Frame_Scheduler.cpp delete mode 100644 Core/architecture/Frame_Scheduler.h delete mode 100644 Core/architecture/Plot_Render_Context.cpp delete mode 100644 Core/architecture/Plot_Render_Context.h delete mode 100644 Core/architecture/Render_Config.h delete mode 100644 Core/architecture/Render_Data.h delete mode 100644 Core/architecture/Render_Lease.h delete mode 100644 Core/architecture/Render_Time.h delete mode 100644 Core/architecture/Render_Utils.cpp delete mode 100644 Core/architecture/Renderable.cpp delete mode 100644 Core/architecture/Renderable.h delete mode 100644 Core/architecture/Strategy_Render_Data.cpp delete mode 100644 Core/architecture/Strategy_Render_Data.h delete mode 100644 Core/architecture/Timeline_Stream.cpp delete mode 100644 Core/architecture/Timeline_Stream.h delete mode 100644 Core/architecture/Update_Completion.cpp delete mode 100644 Core/architecture/Update_Completion.h delete mode 100644 Core/architecture/export.h delete mode 100644 Core/architecture/global.h delete mode 100644 Core/base/Assert.h delete mode 100644 Core/base/Bitmask.h delete mode 100644 Core/base/Color.h delete mode 100644 Core/base/Export.h delete mode 100644 Core/base/Frame_Memory.cpp delete mode 100644 Core/base/Frame_Memory.h delete mode 100644 Core/base/Geometry.h delete mode 100644 Core/base/Memory.cpp delete mode 100644 Core/base/Memory.h delete mode 100644 Core/base/Object_Semantics.h delete mode 100644 Core/base/String_Format.h delete mode 100644 Core/base/Style.h delete mode 100644 Core/base/Task.h delete mode 100644 Core/base/Text.h delete mode 100644 Core/base/Time_Of_Day.h delete mode 100644 Core/base/global.cpp delete mode 100644 Core/base/global.h delete mode 100644 Core/event/Event.h delete mode 100644 Core/event/Key_Event.h delete mode 100644 Core/event/Pointer_Event.h delete mode 100644 Core/event/Resize_Event.h delete mode 100644 Core/event/Wheel_Event.h delete mode 100644 Core/execution/Render_Executor.cpp delete mode 100644 Core/execution/Render_Executor.h delete mode 100644 Core/export.h delete mode 100644 Core/flow/Flow_Diagnostics.h delete mode 100644 Core/flow/Frame_Flow.h delete mode 100644 Core/flow/Frame_Flow_Host.h delete mode 100644 Core/flow/Present_Surface_Lease.cpp delete mode 100644 Core/flow/Present_Surface_Lease.h delete mode 100644 Core/flow/Render_Surface.h delete mode 100644 Core/flow/Render_Ticket.h delete mode 100644 Core/flow/explicit/Explicit_Frame_Flow.cpp delete mode 100644 Core/flow/explicit/Explicit_Frame_Flow.h delete mode 100644 Core/flow/explicit/Explicit_Renderable_Runtime.cpp delete mode 100644 Core/flow/explicit/Explicit_Renderable_Runtime.h delete mode 100644 Core/flow/low_latency/Low_Latency_Feedback_Controller.cpp delete mode 100644 Core/flow/low_latency/Low_Latency_Feedback_Controller.h delete mode 100644 Core/flow/low_latency/Low_Latency_Frame_Flow.cpp delete mode 100644 Core/flow/low_latency/Low_Latency_Frame_Flow.h delete mode 100644 Core/flow/low_latency/Low_Latency_Frame_Pipeline.cpp delete mode 100644 Core/flow/low_latency/Low_Latency_Frame_Pipeline.h delete mode 100644 Core/flow/low_latency/Low_Latency_Renderable_Runtime.h delete mode 100644 Core/flow/low_latency/Low_Latency_Roles.h delete mode 100644 Core/flow/low_latency/Render_Data.cpp delete mode 100644 Core/flow/low_latency/Render_Data.h delete mode 100644 Core/flow/low_latency/Render_Lease.h delete mode 100644 Core/flow/low_latency/Triple_Buffer.cpp delete mode 100644 Core/flow/low_latency/Triple_Buffer.h delete mode 100644 Core/performance/Performance_Log_Record.h delete mode 100644 Core/performance/Performance_Log_Service.cpp delete mode 100644 Core/performance/Performance_Log_Service.h delete mode 100644 Core/plot/Plot_Core.cpp delete mode 100644 Core/plot/Plot_Core.h delete mode 100644 Core/plot/Plot_Core_Access.h delete mode 100644 Core/plot/Presentation_Sink.h delete mode 100644 Core/plottable/Afterglow.cpp delete mode 100644 Core/plottable/Afterglow.h delete mode 100644 Core/plottable/Afterglow_p.h delete mode 100644 Core/plottable/Constellation_Diagram.cpp delete mode 100644 Core/plottable/Constellation_Diagram.h delete mode 100644 Core/plottable/Constellation_Diagram_p.h delete mode 100644 Core/plottable/Frequency_Trace.cpp delete mode 100644 Core/plottable/Frequency_Trace.h delete mode 100644 Core/plottable/Frequency_Trace_p.h delete mode 100644 Core/plottable/Hover_Tooltip.cpp delete mode 100644 Core/plottable/Hover_Tooltip.h delete mode 100644 Core/plottable/Interpolation.h delete mode 100644 Core/plottable/Interpolation_p.h delete mode 100644 Core/plottable/Performance_Overlay.cpp delete mode 100644 Core/plottable/Performance_Overlay.h delete mode 100644 Core/plottable/Performance_Overlay_p.h delete mode 100644 Core/plottable/Performance_Snapshot.h delete mode 100644 Core/plottable/Selection_Rectangle_Overlay.cpp delete mode 100644 Core/plottable/Selection_Rectangle_Overlay.h delete mode 100644 Core/plottable/Selection_Rectangle_Overlay_p.h delete mode 100644 Core/plottable/Spectrum.cpp delete mode 100644 Core/plottable/Spectrum.h delete mode 100644 Core/plottable/Spectrum_p.h delete mode 100644 Core/plottable/Sweep_Spectrum.cpp delete mode 100644 Core/plottable/Sweep_Spectrum.h delete mode 100644 Core/plottable/Sweep_Spectrum_p.h delete mode 100644 Core/plottable/Waterfall.cpp delete mode 100644 Core/plottable/Waterfall.h delete mode 100644 Core/plottable/Waterfall_p.h delete mode 100644 Core/plottable/export.h delete mode 100644 Core/plottable/global.h delete mode 100644 Core/primitive/Band_Region.cpp delete mode 100644 Core/primitive/Band_Region.h delete mode 100644 Core/primitive/Color_Bar.cpp delete mode 100644 Core/primitive/Color_Bar.h delete mode 100644 Core/primitive/Color_Map.h delete mode 100644 Core/primitive/Color_Scale.h delete mode 100644 Core/primitive/Curve.cpp delete mode 100644 Core/primitive/Curve.h delete mode 100644 Core/primitive/Curve_Utils_p.h delete mode 100644 Core/primitive/Fill_Area.cpp delete mode 100644 Core/primitive/Fill_Area.h delete mode 100644 Core/primitive/Grid.cpp delete mode 100644 Core/primitive/Grid.h delete mode 100644 Core/primitive/Label.cpp delete mode 100644 Core/primitive/Label.h delete mode 100644 Core/primitive/Marker.cpp delete mode 100644 Core/primitive/Marker.h delete mode 100644 Core/primitive/Point_Set.cpp delete mode 100644 Core/primitive/Point_Set.h delete mode 100644 Core/primitive/Raster_Image.cpp delete mode 100644 Core/primitive/Raster_Image.h delete mode 100644 Core/primitive/Raster_Image_p.h delete mode 100644 Core/primitive/export.h delete mode 100644 Core/render/Blend2D_Convert.cpp delete mode 100644 Core/render/Blend2D_Convert.h delete mode 100644 Core/render/Button.h delete mode 100644 Core/render/Canvas.cpp delete mode 100644 Core/render/Canvas.h delete mode 100644 Core/render/Font_Face.cpp delete mode 100644 Core/render/Font_Face.h delete mode 100644 Core/render/Font_Face_p.h delete mode 100644 Core/render/Frame_Prepare_Subflow.cpp delete mode 100644 Core/render/Frame_Prepare_Subflow.h delete mode 100644 Core/render/Image.cpp delete mode 100644 Core/render/Image.h delete mode 100644 Core/render/Image_Interpolation_Mode.h delete mode 100644 Core/render/Image_View.h delete mode 100644 Core/render/Image_p.h delete mode 100644 Core/render/Render_Frame_Snapshot.cpp delete mode 100644 Core/render/Render_Frame_Snapshot.h delete mode 100644 Core/render/Render_Output.cpp delete mode 100644 Core/render/Render_Output.h delete mode 100644 Core/render/Renderable_Frame_Node.h delete mode 100644 Core/resource/Turbo_Color_Map.h delete mode 100644 Core/resource/resource.qrc delete mode 100644 Core/resource/turbo.color_map diff --git a/Core/Axis/Abs_Axis.cpp b/Core/Axis/Abs_Axis.cpp deleted file mode 100644 index c32feba..0000000 --- a/Core/Axis/Abs_Axis.cpp +++ /dev/null @@ -1,210 +0,0 @@ -#include -#include -#include "Abs_Axis_p.h" -namespace renderive { -RENDERIVE_DEFINE_RENDERABLE_BINDING(Abs_Axis, Abs_Axis_Private, Abs_Axis_Render_State, Abs_Axis_Input_Data, "AbsAxis") -RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, int, x) -RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, int, y) -RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, Orientation, orientation) -size_t Abs_Axis::pixel_length() { - return d()->edit_state_value(&Abs_Axis_Render_State::pixel_length); -} -void Abs_Axis::set_pixel_length(size_t value) { - d()->set_state_value(&Abs_Axis_Render_State::pixel_length, std::max(1, value)); -} -RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, int, tick_length) -RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, int, sub_tick_length) -RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, Color, color) -RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, Number_Locale, locale) -RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, std::string, unit_text) -RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, Font, unit_text_font) -RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, Pen, unit_text_pen) -RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, Brush, unit_text_background_brush) -RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, int, label_rotation_degrees) -double Abs_Axis::pixel_to_coord(double pixel) { - return d()->edit_pixel_to_coord(pixel); -} -double Abs_Axis::coord_to_pixel(double coord) { - return d()->edit_coord_to_pixel(coord); -} -std::vector Abs_Axis_Private::create_tick_vector(double tick_step, const Range& range) { - double tick_origin = range.origin; - std::vector result; - auto first_step = static_cast(std::floor(std::abs((range.origin - tick_origin) / tick_step))); - auto last_step = static_cast(std::ceil(std::abs(range.target - tick_origin) / tick_step)); - int tick_count = static_cast(std::abs(last_step - first_step) + 1); - if (tick_count < 0) - tick_count = 0; - if (range.origin > range.target) - tick_step = -tick_step; - result.resize(tick_count); - for (int i = 0; i < tick_count; ++i) { - result[i] = tick_origin + (double)(first_step + i) * tick_step; - } - return result; -} -std::vector Abs_Axis_Private::create_label_vector(const std::vector& ticks, Abs_Axis_Render_State* state) { - std::vector result; - result.reserve(ticks.size()); - for (double tick_coord : ticks) - result.push_back(get_tick_label(tick_coord, state)); - return result; -} -std::vector Abs_Axis_Private::create_sub_tick_vector(int sub_tick_count, const std::vector& ticks, const Range& range) { - std::vector result; - if (sub_tick_count <= 0 || ticks.size() < 2) - return result; - result.reserve((ticks.size() - 1) * sub_tick_count); - for (int i = 1; i < ticks.size(); ++i) { - double sub_tick_step = (ticks.at(i) - ticks.at(i - 1)) / double(sub_tick_count + 1); - for (int k = 1; k <= sub_tick_count; ++k) - result.push_back(ticks.at(i - 1) + k * sub_tick_step); - } - while (!result.empty() && !range.contains(result.back())) { - result.pop_back(); - } - return result; -} -int Abs_Axis_Private::get_sub_tick_count(double tick_step) { - int result = 1; - double epsilon = 0.01; - double int_partf; - int int_part; - double frac_part = modf(get_mantissa(tick_step), &int_partf); - int_part = int(int_partf); - if (frac_part < epsilon || 1.0 - frac_part < epsilon) { - if (1.0 - frac_part < epsilon) - ++int_part; - switch (int_part) { - case 1: - result = 4; - break; - case 2: - result = 3; - break; - case 3: - result = 2; - break; - case 4: - result = 3; - break; - case 5: - result = 4; - break; - case 6: - result = 2; - break; - case 7: - result = 6; - break; - case 8: - result = 3; - break; - case 9: - result = 2; - break; - } - } - else { - if (std::abs(frac_part - 0.5) < epsilon) { - switch (int_part) { - case 1: - result = 2; - break; - case 2: - result = 4; - break; - case 3: - result = 4; - break; - case 4: - result = 2; - break; - case 5: - result = 4; - break; - case 6: - result = 4; - break; - case 7: - result = 2; - break; - case 8: - result = 4; - break; - case 9: - result = 4; - break; - } - } - } - return result; -} -double Abs_Axis_Private::get_mantissa(double input, double* magnitude) { - const double mag = std::pow(10.0, std::floor(std::log10(input))); - if (magnitude) - *magnitude = mag; - return input / mag; -} -double Abs_Axis_Private::clean_mantissa(double input) { - double magnitude; - const double mantissa = get_mantissa(input, &magnitude); - return pick_closest(mantissa, {1.0, 2.0, 2.5, 5.0, 10.0}) * magnitude; -} -double Abs_Axis_Private::pick_closest(double target, const std::vector& candidates) { - if (candidates.empty()) - return target; - if (candidates.size() == 1) - return candidates.front(); - std::vector::const_iterator it = std::lower_bound(candidates.cbegin(), candidates.cend(), target); - if (it == candidates.cend()) - return *(it - 1); - else if (it == candidates.cbegin()) - return *it; - else - return target - *(it - 1) < *it - target ? *(it - 1) : *it; -} -std::string Abs_Axis_Private::get_tick_label(double tick, Abs_Axis_Render_State* state) { - return format_number(tick, Number_Format{.precision = state->label_precision}); -} -double Abs_Axis::start_coord() { - return d()->edit_axis_state()->coord_start; -} -double Abs_Axis::end_coord() { - auto render_state = d()->edit_axis_state(); - return render_state->coord_start + render_state->coord_length; -} -Range Abs_Axis::coord_range() { - auto render_state = d()->edit_axis_state(); - return {render_state->coord_start, render_state->coord_start + render_state->coord_length}; -} -int Abs_Axis::pixel_sample_count(Range range) { - auto render_state = d()->edit_axis_state(); - double length = coord_range().length(); - if (length == 0.0) - return 0; - return static_cast(std::abs(range.length() / length) * render_state->pixel_length); -} -int Abs_Axis::pixel_sample_count() { - auto render_state = d()->edit_axis_state(); - return static_cast(render_state->pixel_length); -} -double Abs_Axis::tick_step(const Range& range) { - return d()->get_tick_step(range); -} -int Abs_Axis::sub_tick_count(double tick_step) { - return d()->get_sub_tick_count(tick_step); -} -std::string Abs_Axis::tick_label(double tick) { - return d()->edit_tick_label(tick); -} -std::vector Abs_Axis::create_label_vector(const std::vector& ticks) { - return d()->edit_label_vector(ticks); -} -std::vector Abs_Axis::create_tick_vector(double tick_step, const Range& range) { - return d()->create_tick_vector(tick_step, range); -} -std::vector Abs_Axis::create_sub_tick_vector(int sub_tick_count, const std::vector& ticks, const Range& range) { - return d()->create_sub_tick_vector(sub_tick_count, ticks, range); -} -} // namespace renderive diff --git a/Core/Axis/Abs_Axis.h b/Core/Axis/Abs_Axis.h deleted file mode 100644 index ad7f52a..0000000 --- a/Core/Axis/Abs_Axis.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once -#include "global.h" -#include "../base/Color.h" -#include "../base/Geometry.h" -#include "../base/String_Format.h" -#include "../base/Style.h" -#include "../base/Text.h" -#include -namespace renderive { -struct Axis_Render_Access; -struct Abs_Axis_Prop { - int x{}; - int y{}; - Orientation orientation = Orientation::Horizontal; - int tick_length = 10; - int sub_tick_length = 5; - double coord_start = 0; - double coord_length = 20; - Color color = Color::white(); - Number_Locale locale; - std::string unit_text = ""; - Font unit_text_font; - Pen unit_text_pen = Pen{.color = Color::white()}; - Brush unit_text_background_brush = Brush{.color = Color::black()}; - int label_rotation_degrees = 0; -}; -struct Abs_Axis_Private; -class LIB_DECL Abs_Axis : public Renderable { -public: - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; - Abs_Axis(); - PROP(int, x) - PROP(int, y) - PROP(Orientation, orientation) - PROP(size_t, pixel_length) - PROP(int, tick_length) - PROP(int, sub_tick_length) - PROP(Color, color) - PROP(Number_Locale, locale) - PROP(std::string, unit_text) - PROP(Font, unit_text_font) - PROP(Pen, unit_text_pen) - PROP(Brush, unit_text_background_brush) - PROP(int, label_rotation_degrees) - [[nodiscard]] virtual double pixel_to_coord(double pixel); - [[nodiscard]] virtual double coord_to_pixel(double coord); - [[nodiscard]] virtual double tick_step(const Range& range); - [[nodiscard]] virtual int sub_tick_count(double tick_step); - virtual std::string tick_label(double tick); - virtual std::vector create_tick_vector(double tick_step, const Range& range); - virtual std::vector create_sub_tick_vector(int sub_tick_count, const std::vector& ticks, const Range& range); - virtual std::vector create_label_vector(const std::vector& ticks); - double start_coord(); - double end_coord(); - Range coord_range(); - int pixel_sample_count(Range range); - int pixel_sample_count(); - template - struct Builder_T; -private: - friend struct Axis_Render_Access; - Abs_Axis_Private* d(); -}; -template -struct Abs_Axis::Builder_T : protected Abs_Axis_Prop { - PROP_BT(int, x) - PROP_BT(int, y) - PROP_BT(Orientation, orientation) - PROP_BT(size_t, pixel_length) - PROP_BT(int, tick_length) - PROP_BT(int, sub_tick_length) - PROP_BT(Color, color) - PROP_BT(Number_Locale, locale) - PROP_BT(std::string, unit_text) - PROP_BT(Font, unit_text_font) - PROP_BT(Pen, unit_text_pen) - PROP_BT(Brush, unit_text_background_brush) - PROP_BT(int, label_rotation_degrees) - std::shared_ptr parent{}; - size_t pixel_size{}; - void set(Abs_Axis* ret); -}; -} // namespace renderive diff --git a/Core/Axis/Abs_Axis_p.h b/Core/Axis/Abs_Axis_p.h deleted file mode 100644 index 4f9b699..0000000 --- a/Core/Axis/Abs_Axis_p.h +++ /dev/null @@ -1,354 +0,0 @@ -#pragma once -#include -#include -#include -#include "../architecture/Render_Lease.h" -#include "../render/Canvas.h" -#include "../render/Render_Frame_Snapshot.h" -#include "Abs_Axis.h" -#include "blend2d/blend2d.h" -namespace renderive { -struct Abs_Axis_Render_State : Render_State, Abs_Axis_Prop { - size_t pixel_length{}; - int label_precision = 2; - void scale_range(double factor, double center) { - coord_start = (coord_start - center) * factor + center; - coord_length *= factor; - } -}; -struct Abs_Axis_Input_Data : Input_Data {}; -struct Axis_Frame_Snapshot { - Orientation orientation = Orientation::Horizontal; - Range coord_range{0.0, 1.0}; - Range pixel_range{0.0, 1.0}; - [[nodiscard]] double coord_to_pixel(double coord) const { - if (coord_range.length() == 0.0) - return pixel_range.origin; - return pixel_range.origin + pixel_range.length() * (coord - coord_range.origin) / coord_range.length(); - } - [[nodiscard]] double pixel_to_coord(double pixel) const { - if (pixel_range.length() == 0.0) - return coord_range.origin; - return coord_range.origin + coord_range.length() * (pixel - pixel_range.origin) / pixel_range.length(); - } - [[nodiscard]] int pixel_size() const { - return static_cast(std::max(1.0, pixel_range.size())); - } -}; -struct Abs_Axis_Private : Typed_Render_Data { - int tick_count = 2; - [[nodiscard]] Abs_Axis_Render_State* edit_axis_state() { - return edit_state(); - } - [[nodiscard]] Abs_Axis_Render_State* render_axis_state() { - return static_cast(state(Renderable_State_Role::Render)); - } - [[nodiscard]] Abs_Axis_Render_State* render_axis_state(const Renderable_Frame_View& frame_view) const { - return render_state(frame_view); - } - static int pixel_start_of(const Abs_Axis_Render_State* s) { - return s->orientation == Orientation::Horizontal ? s->x : s->y; - } - static Axis_Frame_Snapshot axis_frame_snapshot(const Abs_Axis_Render_State* s) { - if (!s) - return {}; - double pixel_start = static_cast(pixel_start_of(s)); - return Axis_Frame_Snapshot{ - s->orientation, - Range{s->coord_start, s->coord_start + s->coord_length}, - Range{pixel_start, pixel_start + static_cast(s->pixel_length)} - }; - } - virtual Axis_Frame_Snapshot frame_snapshot(const Render_Frame_Snapshot&, const Renderable_Frame_View& frame_view) { - return axis_frame_snapshot(render_axis_state(frame_view)); - } - double get_mantissa(double input, double* magnitude = nullptr); - [[nodiscard]] double clean_mantissa(double input); - [[nodiscard]] double pick_closest(double target, const std::vector& candidates); - void prepare_data(const Render_Frame_Snapshot&, const Renderable_Frame_View&) override {} - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Abs_Axis_Render_State* s = render_axis_state(frame_view); - if (!s) - return; - Axis_Frame_Snapshot axis_snapshot = frame_snapshot(snapshot, frame_view); - const int pixel_start = pixel_start_of(s); - Range range = axis_snapshot.coord_range; - double tick_step = get_tick_step(range); - std::vector ticks = create_frame_tick_vector(tick_step, range, s, snapshot, frame_view); - while (!ticks.empty() && !range.contains(ticks.back())) { - ticks.pop_back(); - } - int sub_tick_count = get_sub_tick_count(tick_step); - std::vector sub_ticks = create_frame_sub_tick_vector(sub_tick_count, ticks, range, s, snapshot, frame_view); - while (!sub_ticks.empty() && !range.contains(sub_ticks.back())) { - sub_ticks.pop_back(); - } - std::vector labels = create_frame_label_vector(ticks, s, snapshot, frame_view); - Text_Metrics tm = canvas.measure_text(!s->unit_text_font.valid() ? "0" : " "); - double h = tm.line_height(); - canvas.set_pen(Pen{.color = s->color}); - if (s->orientation == Orientation::Horizontal) { - canvas.draw_line({(double)s->x, (double)s->y}, {(double)(s->x + s->pixel_length), (double)s->y}); - int n = (int)ticks.size(); - for (int i = 0; i < n; ++i) { - double tick = ticks[i]; - double x = axis_snapshot.coord_to_pixel(tick); - canvas.draw_line({x, (double)s->y}, {x, (double)(s->y + s->tick_length)}); - const std::string& label = labels[i]; - PointF start{x, (double)(s->y + s->tick_length)}; - canvas.save(); - canvas.translate(start.x, start.y); - canvas.rotate((double)s->label_rotation_degrees * 3.14159265358979 / 180.0); - canvas.draw_text({0.0, 0.0}, label); - canvas.restore(); - } - for (double tick : sub_ticks) { - double x = axis_snapshot.coord_to_pixel(tick); - canvas.draw_line({x, (double)s->y}, {x, (double)(s->y + s->sub_tick_length)}); - } - } - else if (s->orientation == Orientation::Vertical) { - canvas.draw_line({(double)s->x, (double)s->y}, {(double)s->x, (double)(s->y + s->pixel_length)}); - int n = (int)ticks.size(); - for (int i = 0; i < n; ++i) { - double tick = ticks[i]; - double y = axis_snapshot.coord_to_pixel(tick); - const std::string& label = labels[i]; - canvas.draw_line({(double)s->x, y}, {(double)(s->x + s->tick_length), y}); - PointF start{(double)(s->x + s->tick_length * 2), y + h / 2.0}; - canvas.save(); - canvas.translate(start.x, start.y); - canvas.rotate((double)s->label_rotation_degrees * 3.14159265358979 / 180.0); - canvas.draw_text({0.0, 0.0}, label); - canvas.restore(); - } - for (double tick : sub_ticks) { - double y = axis_snapshot.coord_to_pixel(tick); - canvas.draw_line({(double)s->x, y}, {(double)(s->x + s->sub_tick_length), y}); - } - } - if (s->unit_text.empty()) - return; - Text_Metrics um = canvas.measure_text(s->unit_text_font, s->unit_text); - double w = um.width; - if (s->orientation == Orientation::Horizontal) { - double x = pixel_start + (double)s->pixel_length - w; - double y = (double)s->y - h; - RectF area(x, y, w, h); - canvas.set_brush(s->unit_text_background_brush); - canvas.fill_rect(area); - canvas.set_pen(s->unit_text_pen); - canvas.set_font(s->unit_text_font); - canvas.draw_text(area, Text_Align::Center, s->unit_text); - } - else if (s->orientation == Orientation::Vertical) { - double x = (double)s->x; - double y = (double)pixel_start; - RectF area(x, y, w, h); - canvas.set_brush(s->unit_text_background_brush); - canvas.fill_rect(area); - canvas.set_pen(s->unit_text_pen); - canvas.set_font(s->unit_text_font); - canvas.draw_text(area, Text_Align::Center, s->unit_text); - } - } - [[nodiscard]] virtual double pixel_to_coord(double pixel, Abs_Axis_Render_State* s) { - return s->coord_start + s->coord_length * (pixel - (double)pixel_start_of(s)) / (double)s->pixel_length; - } - [[nodiscard]] virtual double coord_to_pixel(double coord, Abs_Axis_Render_State* s) { - return pixel_start_of(s) + (double)s->pixel_length * (coord - s->coord_start) / s->coord_length; - } - virtual double get_tick_step(const Range& range) { - double exact_step = range.size() / double(tick_count + 1e-10); - return clean_mantissa(exact_step); - } - virtual int get_sub_tick_count(double tick_step); - virtual std::vector create_tick_vector(double tick_step, const Range& range); - virtual std::vector create_sub_tick_vector(int sub_tick_count, const std::vector& ticks, const Range& range); - virtual std::string get_tick_label(double tick, Abs_Axis_Render_State* state); - virtual std::vector create_label_vector(const std::vector& ticks, Abs_Axis_Render_State* state); - virtual std::vector create_frame_tick_vector(double tick_step, const Range& range, Abs_Axis_Render_State*, const Render_Frame_Snapshot&, const Renderable_Frame_View&) { - return create_tick_vector(tick_step, range); - } - virtual std::vector create_frame_sub_tick_vector(int sub_tick_count, const std::vector& ticks, const Range& range, Abs_Axis_Render_State*, const Render_Frame_Snapshot&, const Renderable_Frame_View&) { - return create_sub_tick_vector(sub_tick_count, ticks, range); - } - virtual std::vector create_frame_label_vector(const std::vector& ticks, Abs_Axis_Render_State* state, const Render_Frame_Snapshot&, const Renderable_Frame_View&) { - return create_label_vector(ticks, state); - } - virtual std::string frame_tick_label(double tick, const Render_Frame_Snapshot&, const Renderable_Frame_View& frame_view) { - return get_tick_label(tick, render_axis_state(frame_view)); - } - [[nodiscard]] double edit_pixel_to_coord(double pixel) { - return pixel_to_coord(pixel, edit_axis_state()); - } - [[nodiscard]] double render_pixel_to_coord(double pixel) { - return pixel_to_coord(pixel, render_axis_state()); - } - [[nodiscard]] double edit_coord_to_pixel(double coord) { - return coord_to_pixel(coord, edit_axis_state()); - } - [[nodiscard]] double render_coord_to_pixel(double coord) { - return coord_to_pixel(coord, render_axis_state()); - } - [[nodiscard]] Range edit_coord_range() { - const auto* s = edit_axis_state(); - return {s->coord_start, s->coord_start + s->coord_length}; - } - [[nodiscard]] Range render_coord_range() { - const auto* s = render_axis_state(); - return {s->coord_start, s->coord_start + s->coord_length}; - } - [[nodiscard]] int render_pixel_size() { - return std::abs(static_cast(render_axis_state()->pixel_length)); - } - [[nodiscard]] std::string edit_tick_label(double tick) { - return get_tick_label(tick, edit_axis_state()); - } - [[nodiscard]] std::string render_tick_label(double tick) { - return get_tick_label(tick, render_axis_state()); - } - [[nodiscard]] std::vector edit_label_vector(const std::vector& ticks) { - return create_label_vector(ticks, edit_axis_state()); - } - [[nodiscard]] std::vector render_label_vector(const std::vector& ticks) { - return create_label_vector(ticks, render_axis_state()); - } -}; -struct Axis_Mapping_2D { - Axis_Frame_Snapshot domain; - Axis_Frame_Snapshot value; - [[nodiscard]] PointF map(double domain_coord, double value_coord) const { - PointF ret{}; - double domain_pixel = domain.coord_to_pixel(domain_coord); - double value_pixel = value.coord_to_pixel(value_coord); - if (domain.orientation == Orientation::Horizontal) { - ret.x = domain_pixel; - ret.y = value_pixel; - } - else { - ret.x = value_pixel; - ret.y = domain_pixel; - } - return ret; - } - [[nodiscard]] PointF map(PointF data_point) const { - return map(data_point.x, data_point.y); - } - [[nodiscard]] double domain_pixel(PointF screen_point) const { - return domain.orientation == Orientation::Horizontal ? screen_point.x : screen_point.y; - } - [[nodiscard]] double value_pixel(PointF screen_point) const { - return value.orientation == Orientation::Horizontal ? screen_point.x : screen_point.y; - } -}; -struct Axis_Basis_2D { - PointF origin{}; - PointF domain_vector{}; - PointF value_vector{}; - [[nodiscard]] static Axis_Basis_2D from_ranges(const Axis_Mapping_2D& mapping, const Range& domain_range, const Range& value_range) { - PointF origin = mapping.map(domain_range.origin, value_range.origin); - PointF domain_target = mapping.map(domain_range.target, value_range.origin); - PointF value_target = mapping.map(domain_range.origin, value_range.target); - return Axis_Basis_2D{ - origin, - PointF{domain_target.x - origin.x, domain_target.y - origin.y}, - PointF{value_target.x - origin.x, value_target.y - origin.y} - }; - } -}; -struct Axis_Render_Access final { - static Axis_Frame_Snapshot snapshot_from_state(const Abs_Axis_Render_State* s) { - return Abs_Axis_Private::axis_frame_snapshot(s); - } - static Axis_Frame_Snapshot snapshot(Abs_Axis* axis) { - const auto* s = axis->d()->render_axis_state(); - return snapshot_from_state(s); - } - static const Abs_Axis_Render_State* frame_state(Abs_Axis* axis, const Render_Frame_Snapshot& frame_snapshot) { - const auto* node = frame_snapshot.frame_node_for(axis); - if (!node) - return nullptr; - return dynamic_cast(node->frame_view.state); - } - static Axis_Frame_Snapshot snapshot(Abs_Axis* axis, const Render_Frame_Snapshot& frame_snapshot) { - if (const auto* node = frame_snapshot.frame_node_for(axis)) - return axis->d()->frame_snapshot(frame_snapshot, node->frame_view); - return snapshot(axis); - } - static Axis_Mapping_2D mapping(Abs_Axis* domain_axis, Abs_Axis* value_axis) { - return Axis_Mapping_2D{snapshot(domain_axis), snapshot(value_axis)}; - } - static Axis_Mapping_2D mapping(Abs_Axis* domain_axis, Abs_Axis* value_axis, const Render_Frame_Snapshot& frame_snapshot) { - return Axis_Mapping_2D{snapshot(domain_axis, frame_snapshot), snapshot(value_axis, frame_snapshot)}; - } - static bool orientations_orthogonal(Abs_Axis* domain_axis, Abs_Axis* value_axis) { - return domain_axis && value_axis && orientation(domain_axis) != orientation(value_axis); - } - static double coord_to_pixel(Abs_Axis* axis, double coord) { - return axis->d()->render_coord_to_pixel(coord); - } - static double pixel_to_coord(Abs_Axis* axis, double pixel) { - return axis->d()->render_pixel_to_coord(pixel); - } - static Range coord_range(Abs_Axis* axis) { - return axis->d()->render_coord_range(); - } - static Range coord_range(Abs_Axis* axis, const Render_Frame_Snapshot& frame_snapshot) { - Axis_Frame_Snapshot s = snapshot(axis, frame_snapshot); - return s.coord_range; - } - static int pixel_size(Abs_Axis* axis) { - return axis->d()->render_pixel_size(); - } - static int pixel_size(Abs_Axis* axis, const Render_Frame_Snapshot& frame_snapshot) { - return snapshot(axis, frame_snapshot).pixel_size(); - } - static Orientation orientation(Abs_Axis* axis) { - return axis->d()->render_axis_state()->orientation; - } - static Orientation orientation(Abs_Axis* axis, const Render_Frame_Snapshot& frame_snapshot) { - return snapshot(axis, frame_snapshot).orientation; - } - static std::string tick_label(Abs_Axis* axis, double tick) { - return axis->d()->render_tick_label(tick); - } - static std::string tick_label(Abs_Axis* axis, double tick, const Render_Frame_Snapshot& frame_snapshot) { - if (axis) { - if (const auto* node = frame_snapshot.frame_node_for(axis)) - return axis->d()->frame_tick_label(tick, frame_snapshot, node->frame_view); - } - return tick_label(axis, tick); - } - static std::string unit_text(Abs_Axis* axis, const Render_Frame_Snapshot& frame_snapshot) { - if (const auto* s = frame_state(axis, frame_snapshot)) - return s->unit_text; - return axis ? axis->unit_text() : std::string{}; - } - static Font unit_text_font(Abs_Axis* axis, const Render_Frame_Snapshot& frame_snapshot) { - if (const auto* s = frame_state(axis, frame_snapshot)) - return s->unit_text_font; - return axis ? axis->unit_text_font() : Font{}; - } -}; -template -void Abs_Axis::Builder_T::set(Abs_Axis* ret) { - ret->init(parent); - Abs_Axis_Private* pd = ret->d(); - Render_Edit_Lease edit(pd); - Abs_Axis_Render_State* sc = edit.operator->(); - sc->x = this->x; - sc->y = this->y; - sc->orientation = this->orientation; - sc->pixel_length = std::max(1, this->pixel_size); - sc->tick_length = this->tick_length; - sc->sub_tick_length = this->sub_tick_length; - sc->color = this->color; - sc->locale = this->locale; - sc->unit_text = this->unit_text; - sc->unit_text_font = this->unit_text_font; - sc->unit_text_pen = this->unit_text_pen; - sc->unit_text_background_brush = this->unit_text_background_brush; - sc->label_rotation_degrees = this->label_rotation_degrees; -} -} // namespace renderive diff --git a/Core/Axis/Axis.cpp b/Core/Axis/Axis.cpp deleted file mode 100644 index 2a9155a..0000000 --- a/Core/Axis/Axis.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "Axis_p.h" -#include "../base/Memory.h" -namespace renderive { -RENDERIVE_DEFINE_RENDERABLE_BINDING(Axis, Axis_Private, Axis_Render_State, Axis_Input_Data, "Axis") -RENDERIVE_DEFINE_STATE_PROP(Axis, Axis_Render_State, int, label_precision) -RENDERIVE_DEFINE_STATE_PROP(Axis, Axis_Render_State, double, coord_start) -double Axis::coord_length() { - return d()->edit_state_value(&Axis_Render_State::coord_length); -} -void Axis::set_coord_length(double value) { - if (value == 0.0) - return; - d()->set_state_value(&Axis_Render_State::coord_length, value); -} -void Axis::set_coord_range(Range range) { - if (range.length() == 0.0) - return; - Render_Edit_Lease edit(d()); - edit->coord_start = range.origin; - edit->coord_length = range.target - range.origin; -} -Axis::Builder::Builder(std::shared_ptr parent, Orientation orientation) { - ASSERT(parent && parent->attached_to_plot(), "Axis build error! parent invalid"); - this->parent = parent; - this->orientation = orientation; -} -std::shared_ptr Axis::Builder::build() { - auto ret = renderive::make_shared(); - Axis::Builder_T::set(ret.get()); - return ret; -} -} // namespace renderive diff --git a/Core/Axis/Axis.h b/Core/Axis/Axis.h deleted file mode 100644 index 911c84a..0000000 --- a/Core/Axis/Axis.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once -#include "Abs_Axis.h" -#include -namespace renderive { -struct Axis_Render_State; -struct Axis_Input_Data; -struct Axis_Private; -/// 普通数值轴。 -class LIB_DECL Axis : public Abs_Axis { -public: - /// 返回私有渲染数据。 - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; - Axis(); - PROP(int, label_precision) - PROP(double, coord_start) - PROP(double, coord_length) - /// 设置轴坐标范围。 - void set_coord_range(Range range); - template - struct Builder_T; - struct Builder; -private: - Axis_Private* d(); -}; -template -struct Axis::Builder_T : Abs_Axis::Builder_T { - PROP_BT(int, label_precision) - // PROP_BT(double, coord_start) - // PROP_BT(double, coord_length) - That& set_coord_range(Range r) { - That& t = *static_cast(this); - if (r.length() == 0.0) - return t; - t.coord_start = std::move(r.origin); - t.coord_length = std::move(r.length()); - return t; - } - That& set_use_wheel(bool use_wheel) { - That& t = *static_cast(this); - this->use_wheel = use_wheel; - return t; - } - That& set_use_drag(bool use_drag) { - That& t = *static_cast(this); - this->use_drag = use_drag; - return t; - } - void set(Axis* ret); - int label_precision = 2; - bool use_wheel = false; - bool use_drag = false; -}; -struct LIB_DECL Axis::Builder : Builder_T { - /// 创建数值轴构造器。 - Builder(std::shared_ptr parent, Orientation orientation); - /// 构建并返回数值轴对象。 - std::shared_ptr build(); -}; -} // namespace renderive diff --git a/Core/Axis/Axis_p.h b/Core/Axis/Axis_p.h deleted file mode 100644 index 5917b12..0000000 --- a/Core/Axis/Axis_p.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once -#include -#include "../event/Pointer_Event.h" -#include "../event/Wheel_Event.h" -#include "Abs_Axis_p.h" -#include "Axis.h" -namespace renderive { -struct Axis_Render_State : Abs_Axis_Render_State {}; -struct Axis_Input_Data : Abs_Axis_Input_Data {}; -struct Axis_Private : Typed_Render_Data_Base, Hit_Testable, Pointer_Interactive, Wheel_Interactive { - bool use_wheel = false; - bool use_drag = false; - bool select_test(const PointF& pos) override { - return true; - } - void wheel_event(const Wheel_Event& event) override { - if (use_wheel) { - Render_Edit_Lease edit(this); - const double delta = event.angle_delta_y; - double wheel_steps = delta / 120.0; - double factor = std::pow(0.8, wheel_steps); - double cursor_pixel = edit->orientation == Orientation::Horizontal ? event.position.x : event.position.y; - double cursor_coord = edit_pixel_to_coord(cursor_pixel); - double new_coord_length = edit->coord_length * factor; - double new_coord_start = cursor_coord - new_coord_length * (cursor_coord - edit->coord_start) / edit->coord_length; - edit->coord_start = new_coord_start; - edit->coord_length = new_coord_length; - event.accept(); - } - } - bool dragging = false; - PointF start_global_pos; - double start_coord_value; - double pixel_pre_coord; - void mouse_press_event(const Pointer_Event& event) override { - if (!use_drag) - return; - if (event.button != Mouse_Button::Left) - return; - if (dragging) - return; - dragging = true; - start_global_pos = event.global_position; - Render_State_Lease state(this); - start_coord_value = state->coord_start; - pixel_pre_coord = state->coord_length / static_cast(state->pixel_length); - event.accept(); - } - void mouse_move_event(const Pointer_Event& event) override { - if (!use_drag) - return; - if (!(event.buttons & static_cast(Mouse_Button::Left))) - return; - if (!dragging) - return; - Render_Edit_Lease edit(this); - PointF delta{start_global_pos.x - event.global_position.x, start_global_pos.y - event.global_position.y}; - auto coord_delta = pixel_pre_coord * (edit->orientation == Orientation::Horizontal ? delta.x : delta.y); - edit->coord_start = start_coord_value + coord_delta; - event.accept(); - } - void mouse_release_event(const Pointer_Event& event) override { - if (!use_drag) - return; - if (event.button != Mouse_Button::Left) - return; - if (!dragging) - return; - dragging = false; - event.accept(); - } -}; -template -void Axis::Builder_T::set(Axis* ret) { - Abs_Axis::Builder_T::set(ret); - Axis_Private* pd = ret->d(); - Render_Edit_Lease edit(pd); - Axis_Render_State* sc = edit.operator->(); - PROP_RT(int, label_precision) - PROP_RT(double, coord_start) - PROP_RT(double, coord_length) - if (sc->coord_length == 0.0) - sc->coord_length = 1.0; - pd->use_wheel = use_wheel; - pd->use_drag = use_drag; -} -} // namespace renderive diff --git a/Core/Axis/Frequency_Axis.cpp b/Core/Axis/Frequency_Axis.cpp deleted file mode 100644 index 7388412..0000000 --- a/Core/Axis/Frequency_Axis.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "Axis_p.h" -#include "Frequency_Axis_p.h" -#include "../base/Memory.h" -namespace renderive { -RENDERIVE_DEFINE_RENDERABLE_BINDING(Frequency_Axis, Frequency_Axis_Private, Frequency_Axis_Render_State, Frequency_Axis_Input_Data, "Frequency_Axis") -Frequency_Axis::Builder::Builder(std::shared_ptr parent, Orientation orientation) { - ASSERT(parent && parent->attached_to_plot(), "Frequency_Axis build error! parent invalid"); - this->parent = parent; - this->orientation = orientation; -} -std::shared_ptr Frequency_Axis::Builder::build() { - auto ret = renderive::make_shared(); - set(ret.get()); - return ret; -} -} // namespace renderive diff --git a/Core/Axis/Frequency_Axis.h b/Core/Axis/Frequency_Axis.h deleted file mode 100644 index 70041c3..0000000 --- a/Core/Axis/Frequency_Axis.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include "Axis.h" -#include -namespace renderive { -struct Frequency_Axis_Private; -/// 频率轴,负责频率单位显示。 -class LIB_DECL Frequency_Axis : public Axis { -public: - /// 返回私有渲染数据。 - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; - Frequency_Axis(); - struct Builder; - -private: - Frequency_Axis_Private* d(); -}; -struct LIB_DECL Frequency_Axis::Builder : Builder_T{ - /// 创建频率轴构造器。 - Builder(std::shared_ptr parent, Orientation orientation); - /// 构建并返回频率轴对象。 - std::shared_ptr build(); - - - - - -}; -} // namespace renderive diff --git a/Core/Axis/Frequency_Axis_p.h b/Core/Axis/Frequency_Axis_p.h deleted file mode 100644 index 2012e53..0000000 --- a/Core/Axis/Frequency_Axis_p.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once -#include -#include -#include -#include "Abs_Axis_p.h" -#include "Axis_p.h" -#include "Frequency_Axis.h" -namespace renderive { -struct Frequency_Axis_Input_Data : Axis_Input_Data {}; -struct Frequency_Axis_Render_State : Axis_Render_State {}; -struct Frequency_Axis_Private : Typed_Render_Data_Base { - std::vector create_label_vector(const std::vector& ticks, Abs_Axis_Render_State* state) override { - const int exponent = unit_exponent(ticks); - if (!ticks.empty()) - state->unit_text = "Frequency/" + unit_text(exponent); - std::vector result; - result.reserve(ticks.size()); - for (const double& tick_coord : ticks) - result.push_back(get_true_value(get_tick_label(tick_coord, state), exponent, state)); - return result; - } - static int unit_exponent(const std::vector& ticks) { - if (ticks.empty()) - return 0; - const double range = std::abs(ticks.back() - ticks.front()); - if (range >= 1e9) - return 9; - if (range >= 1e6) - return 6; - if (range >= 1e3) - return 3; - return 0; - } - static std::string unit_text(int exponent) { - switch (exponent) { - case 0: - return "Hz"; - case 3: - return "kHz"; - case 6: - return "MHz"; - case 9: - return "GHz"; - } - return "Hz"; - } - std::string get_true_value(std::string value, int exponent, Abs_Axis_Render_State* state) { - auto s = static_cast(state); - int precision = s->label_precision; - auto dot_pos = value.find('.'); - if (dot_pos == std::string::npos) - dot_pos = value.size(); - std::string number = value.substr(0, dot_pos) + value.substr(dot_pos + 1); - int after_dot_index = static_cast(dot_pos) - exponent; - if (after_dot_index < 1) { - for (int i = 0; i < 1 - after_dot_index; ++i) - number.insert(0, 1, '0'); - after_dot_index = 1; - } - number.insert(after_dot_index, "."); - return format_number(std::stod(number), {.precision = precision}); - } -}; -} // namespace renderive diff --git a/Core/Axis/Time_Axis.cpp b/Core/Axis/Time_Axis.cpp deleted file mode 100644 index 65d1950..0000000 --- a/Core/Axis/Time_Axis.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "Time_Axis_p.h" -#include "../base/Memory.h" -namespace renderive { -RENDERIVE_DEFINE_RENDERABLE_BINDING(Time_Axis, Time_Axis_Private, Time_Axis_Render_State, Time_Axis_Input_Data, "time_axis") -int Time_Axis::visible_time_point_count() { - return d()->edit_state_value(&Time_Axis_Render_State::visible_time_point_count); -} -void Time_Axis::set_visible_time_point_count(int value) { - value = std::max(1, value); - d()->set_state_value(&Time_Axis_Render_State::visible_time_point_count, value); -} -int Time_Axis::tick_label_spacing_px() { - return d()->edit_state_value(&Time_Axis_Render_State::tick_label_spacing_px); -} -void Time_Axis::set_tick_label_spacing_px(int value) { - d()->set_state_value(&Time_Axis_Render_State::tick_label_spacing_px, std::max(1, value)); -} -RENDERIVE_DEFINE_STATE_PROP(Time_Axis, Time_Axis_Render_State, std::string, time_format) -RENDERIVE_DEFINE_STATE_PROP(Time_Axis, Time_Axis_Render_State, Font, font) -std::shared_ptr Time_Axis::timeline_stream() { - return d()->edit_state_value(&Time_Axis_Render_State::timeline_stream); -} -void Time_Axis::set_timeline_stream(std::shared_ptr value) { - d()->set_state_value(&Time_Axis_Render_State::timeline_stream, std::move(value)); -} -bool Time_Axis::newest_at_axis_start() { - return d()->edit_state_value(&Time_Axis_Render_State::newest_at_axis_start); -} -void Time_Axis::set_newest_at_axis_start(bool value) { - d()->set_state_value(&Time_Axis_Render_State::newest_at_axis_start, value); -} -int Time_Axis::append_time(Time_Of_Day time) { - if (!ok()) - return -1; - auto stream = timeline_stream(); - if (!stream) - return -1; - int tick = stream->append_time(time); - mark_render_dirty(); - return tick; -} -Time_Of_Day Time_Axis::tick_to_time(int tick) { - std::shared_ptr stream; - int point_count = 1; - int interval_hint = 1; - bool newest_at_start = false; - { - Render_State_Lease state(d()); - if (state) { - stream = state->timeline_stream; - point_count = state->visible_time_point_count; - interval_hint = state->label_tick_interval_hint(); - newest_at_start = state->newest_at_axis_start; - } - } - if (stream) - return stream->capture(point_count, interval_hint, newest_at_start).time_by_tick(tick); - return {}; -} -Time_Axis::Builder::Builder(std::shared_ptr parent, Orientation orientation) { - ASSERT(parent && parent->attached_to_plot(), "Time_Axis build error! parent invalid"); - this->parent = parent; - this->orientation = orientation; -} -std::shared_ptr Time_Axis::Builder::build() { - auto ret = renderive::make_shared(); - set(ret.get()); - Time_Axis_Private* pd = ret->d(); - Render_Edit_Lease edit(pd); - Time_Axis_Render_State* sc = edit.operator->(); - PROP_R(int, visible_time_point_count) - PROP_R(int, tick_label_spacing_px) - PROP_R(std::string, time_format) - PROP_R(Font, font) - PROP_R(std::shared_ptr, timeline_stream) - PROP_R(bool, newest_at_axis_start) - sc->visible_time_point_count = std::max(1, sc->visible_time_point_count); - sc->tick_label_spacing_px = std::max(1, sc->tick_label_spacing_px); - if (!sc->timeline_stream) - sc->timeline_stream = renderive::make_shared(); - return ret; -} -} // namespace renderive diff --git a/Core/Axis/Time_Axis.h b/Core/Axis/Time_Axis.h deleted file mode 100644 index 325fafc..0000000 --- a/Core/Axis/Time_Axis.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once -#include "Abs_Axis.h" -#include "../base/Time_Of_Day.h" -#include -#include -namespace renderive { -struct Time_Axis_Private; -struct Time_Axis_Render_Access; -class Timeline_Stream; -/// 时间轴,输入 tick 后按格式显示时间标签。 -class LIB_DECL Time_Axis : public Abs_Axis { -public: - /// 返回私有渲染数据。 - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; - Time_Axis(); - PROP(int, visible_time_point_count) - PROP(int, tick_label_spacing_px) - PROP(std::string, time_format) - PROP(Font, font) - PROP(std::shared_ptr, timeline_stream) - PROP(bool, newest_at_axis_start) - /// 推入一个新的时间点并返回对应 tick。 - int append_time(Time_Of_Day time); - /// 将 tick 转换成时间。 - Time_Of_Day tick_to_time(int tick); - struct Builder; -private: - friend struct Time_Axis_Render_Access; - Time_Axis_Private* d(); -}; -struct Time_Axis_Prop { - int visible_time_point_count = 100; - int tick_label_spacing_px = 8; - std::string time_format = "mm:ss.zzz"; - Font font; - std::shared_ptr timeline_stream; - bool newest_at_axis_start = false; -}; -struct LIB_DECL Time_Axis::Builder : Abs_Axis::Builder_T, - public - Time_Axis_Prop { - PROP_B(int, visible_time_point_count) - PROP_B(int, tick_label_spacing_px) - PROP_B(std::string, time_format) - PROP_B(Font, font) - PROP_B(std::shared_ptr, timeline_stream) - PROP_B(bool, newest_at_axis_start) - /// 创建时间轴构造器。 - Builder(std::shared_ptr parent, Orientation orientation); - /// 构建并返回时间轴对象。 - std::shared_ptr build(); -}; -} // namespace renderive diff --git a/Core/Axis/Time_Axis_p.h b/Core/Axis/Time_Axis_p.h deleted file mode 100644 index 7e14b88..0000000 --- a/Core/Axis/Time_Axis_p.h +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include "../architecture/Timeline_Stream.h" -#include "../base/Memory.h" -#include "../base/String_Format.h" -#include "../base/Time_Of_Day.h" -#include "../render/Canvas.h" -#include "../render/Render_Frame_Snapshot.h" -#include "Abs_Axis_p.h" -#include "Time_Axis.h" -namespace renderive { -struct Time_Axis_Input_Data : Abs_Axis_Input_Data {}; -struct Time_Axis_Render_State : Abs_Axis_Render_State, Time_Axis_Prop { - int label_tick_interval_hint() const { - double rate = (double)visible_time_point_count / (double)pixel_length; - double ret = rate * (tick_label_spacing_px + (double)(orientation == Orientation::Horizontal ? 80 : 20)); - return std::max(1, (int)ret); - } -}; -struct Time_Axis_Private : Typed_Render_Data_Base { - std::shared_ptr capture_timeline(const Render_Frame_Snapshot& snapshot, const Time_Axis_Render_State* state) const { - if (!state || !state->timeline_stream) - return {}; - return snapshot.timeline_snapshot(state->timeline_stream, state->visible_time_point_count, state->label_tick_interval_hint(), state->newest_at_axis_start); - } - std::shared_ptr capture_timeline(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) { - return capture_timeline(snapshot, render_state(frame_view)); - } - void prepare_data(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - (void)capture_timeline(snapshot, frame_view); - } - Axis_Frame_Snapshot frame_snapshot(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - auto s = render_state(frame_view); - Axis_Frame_Snapshot result = axis_frame_snapshot(s); - auto captured = capture_timeline(snapshot, s); - if (captured) - result.coord_range = Range{static_cast(captured->coordinate_begin), static_cast(captured->coordinate_begin + captured->visible_time_point_count)}; - return result; - } - std::vector create_label_vector(const std::vector& ticks, Abs_Axis_Render_State* state) override { - auto s = static_cast(state); - std::vector result; - result.reserve(ticks.size()); - for (double tick : ticks) - result.push_back(format_time(Time_Of_Day{}, s->time_format)); - return result; - } - std::vector create_tick_vector(double tick_step, const Range& range) override { - return Abs_Axis_Private::create_tick_vector(tick_step, range); - } - std::vector create_sub_tick_vector(int sub_tick_count, const std::vector& ticks, const Range& range) override { - return {}; - } - std::vector create_frame_tick_vector(double, const Range&, Abs_Axis_Render_State* state, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View&) override { - std::vector result; - auto captured = capture_timeline(snapshot, static_cast(state)); - if (!captured) - return result; - result.reserve(captured->ticks.size()); - for (const Timeline_Label_Tick& tick : captured->ticks) - result.push_back(tick.coordinate); - return result; - } - std::vector create_frame_sub_tick_vector(int, const std::vector&, const Range&, Abs_Axis_Render_State*, const Render_Frame_Snapshot&, const Renderable_Frame_View&) override { - return {}; - } - std::vector create_frame_label_vector(const std::vector& ticks, Abs_Axis_Render_State* state, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View&) override { - auto s = static_cast(state); - auto captured = capture_timeline(snapshot, s); - std::vector result; - result.reserve(ticks.size()); - for (double tick : ticks) { - Time_Of_Day time = captured ? captured->time_by_tick(static_cast(tick)) : Time_Of_Day{}; - result.push_back(format_time(time, s->time_format)); - } - return result; - } - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Abs_Axis_Private::draw(canvas, snapshot, frame_view); - } - std::string get_tick_label(double tick, Abs_Axis_Render_State* state) override { - auto render_state = static_cast(state); - return format_time(Time_Of_Day{}, render_state->time_format); - } - std::string frame_tick_label(double tick, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - auto s = render_state(frame_view); - auto captured = capture_timeline(snapshot, s); - Time_Of_Day time = captured ? captured->time_by_tick(static_cast(tick)) : Time_Of_Day{}; - return format_time(time, s ? s->time_format : std::string{}); - } -}; -struct Time_Axis_Render_Access final { - static std::shared_ptr capture_timeline(Time_Axis* axis, const Render_Frame_Snapshot& snapshot) { - const auto* node = axis ? snapshot.frame_node_for(axis) : nullptr; - return axis && node ? axis->d()->capture_timeline(snapshot, node->frame_view) : std::shared_ptr{}; - } -}; -} diff --git a/Core/Axis/export.h b/Core/Axis/export.h deleted file mode 100644 index eec460a..0000000 --- a/Core/Axis/export.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "Frequency_Axis.h" -#include "../architecture/Timeline_Stream.h" -#include "Time_Axis.h" diff --git a/Core/Axis/global.h b/Core/Axis/global.h deleted file mode 100644 index 1965dd9..0000000 --- a/Core/Axis/global.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include "../architecture/Renderable.h" diff --git a/Core/architecture/Bounded_Input_Buffer.h b/Core/architecture/Bounded_Input_Buffer.h deleted file mode 100644 index cf84015..0000000 --- a/Core/architecture/Bounded_Input_Buffer.h +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../base/Memory.h" -#include "Psc_Cpp_Core/Base/RingBuffer.hpp" -namespace renderive { -class Bounded_Input_Buffer { -public: - void push(const void* data, std::size_t size, std::size_t max_packet_count) { - configure(size, max_packet_count); - while (!buffer.write(data, size)) { - if (!buffer.skip_one()) - return; - } - } - template - void read_all(Handler handler) { - std::size_t packet_size{}; - while (buffer.peek_len(packet_size)) { - read_buffer.resize(packet_size); - std::size_t read_size = packet_size; - if (!buffer.read(read_buffer.data(), read_size)) - return; - handler(read_buffer.data(), read_size); - } - } - void clear() { - while (buffer.skip_one()) {} - } -private: - void configure(std::size_t max_packet_size, std::size_t max_packet_count) { - max_packet_count = std::max(1, max_packet_count); - max_packet_size = std::max(1, max_packet_size); - if (Initialized && max_packet_size <= this->max_packet_size && max_packet_count == this->max_packet_count) - return; - this->max_packet_size = max_packet_size; - this->max_packet_count = max_packet_count; - buffer.init((Psc::RingBuffer_ST::HEADER_SIZE + max_packet_size) * max_packet_count); - Initialized = true; - } - bool Initialized = false; - std::size_t max_packet_size{}; - std::size_t max_packet_count{}; - Psc::RingBuffer_ST buffer{memory_resource(Memory_Domain::Input_Buffer)}; - std::pmr::vector read_buffer{memory_resource(Memory_Domain::Input_Buffer)}; -}; -class Bounded_Vector_Double_Input_Buffer { -public: - void push(std::span data, std::size_t max_packet_count) { - prepare_push(max_packet_count); - std::pmr::vector& row = row_at(row_count); - row.assign(data.begin(), data.end()); - ++row_count; - } - void push(std::pmr::vector&& data, std::size_t max_packet_count) { - prepare_push(max_packet_count); - rows[row_count].emplace(std::move(data)); - ++row_count; - } - template - void read_all(Handler handler) { - for (std::size_t i = 0; i < row_count; ++i) - handler(*rows[i]); - row_count = 0; - } - void clear() { - row_count = 0; - } -private: - static constexpr std::size_t Storage_Count = 64; - std::pmr::vector& row_at(std::size_t index) { - if (!rows[index]) - rows[index].emplace(memory_resource(Memory_Domain::Input_Buffer)); - return *rows[index]; - } - void prepare_push(std::size_t max_packet_count) { - max_packet_count = std::clamp(max_packet_count, 1, Storage_Count); - while (row_count >= max_packet_count) - drop_front(); - } - void drop_front() { - if (!row_count) - return; - rows[0].reset(); - for (std::size_t i = 1; i < row_count; ++i) { - rows[i - 1].emplace(std::move(*rows[i])); - rows[i].reset(); - } - --row_count; - } - std::array>, Storage_Count> rows; - std::size_t row_count{}; -}; -} diff --git a/Core/architecture/Buffer_Role.h b/Core/architecture/Buffer_Role.h deleted file mode 100644 index 8b14fbe..0000000 --- a/Core/architecture/Buffer_Role.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include diff --git a/Core/architecture/Frame_Scheduler.cpp b/Core/architecture/Frame_Scheduler.cpp deleted file mode 100644 index 32fb050..0000000 --- a/Core/architecture/Frame_Scheduler.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "Frame_Scheduler.h" -#include -#include - -namespace renderive { -void Frame_Lifecycle_Record::record_renderable_render(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth, Renderable_Cache_Mode cache_mode, std::uint64_t render_time_ns) { - Renderable_Frame_Stat& stat = renderable_stat(node_id, parent_node_id, object_name, parent_object_name, tree_depth); - stat.cache_enabled = cache_mode == Renderable_Cache_Mode::Local_Pixel; - ++stat.render_count; - stat.total_render_time_ns += render_time_ns; - stat.last_total_render_time_ns = render_time_ns; -} - -void Frame_Lifecycle_Record::record_renderable_self_draw(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth, Renderable_Cache_Mode cache_mode, std::uint64_t draw_time_ns) { - Renderable_Frame_Stat& stat = renderable_stat(node_id, parent_node_id, object_name, parent_object_name, tree_depth); - stat.cache_enabled = cache_mode == Renderable_Cache_Mode::Local_Pixel; - ++stat.self_draw_count; - stat.self_draw_time_ns += draw_time_ns; - stat.last_self_draw_time_ns = draw_time_ns; -} - -void Frame_Lifecycle_Record::record_renderable_cache_hit(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth) { - ++renderable_cache_hit_count; - ++cache_stat(node_id, parent_node_id, object_name, parent_object_name, tree_depth).hit_count; -} - -void Frame_Lifecycle_Record::record_renderable_cache_miss(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth) { - ++renderable_cache_miss_count; - ++cache_stat(node_id, parent_node_id, object_name, parent_object_name, tree_depth).miss_count; -} - -void Frame_Lifecycle_Record::record_renderable_cache_rebuild(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth, std::uint64_t rebuild_time_ns, std::uint64_t image_bytes, std::uint64_t image_area, std::uint64_t edit_version, std::uint64_t ready_version) { - ++renderable_cache_rebuild_count; - Renderable_Cache_Frame_Stats& stat = cache_stat(node_id, parent_node_id, object_name, parent_object_name, tree_depth); - renderable_cache_rebuild_time_ns += rebuild_time_ns; - renderable_cache_image_bytes += image_bytes; - renderable_cache_image_area += image_area; - ++stat.rebuild_count; - stat.rebuild_time_ns += rebuild_time_ns; - stat.last_rebuild_time_ns = rebuild_time_ns; - stat.last_image_bytes = image_bytes; - stat.last_image_area = image_area; - stat.last_edit_version = edit_version; - stat.last_ready_version = ready_version; -} - -void Frame_Lifecycle_Record::record_renderable_cache_compose(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth, std::uint64_t compose_time_ns, std::uint64_t image_bytes, std::uint64_t image_area, std::uint64_t edit_version, std::uint64_t ready_version) { - renderable_cache_compose_time_ns += compose_time_ns; - Renderable_Cache_Frame_Stats& stat = cache_stat(node_id, parent_node_id, object_name, parent_object_name, tree_depth); - stat.compose_time_ns += compose_time_ns; - stat.last_compose_time_ns = compose_time_ns; - stat.last_image_bytes = image_bytes; - stat.last_image_area = image_area; - stat.last_edit_version = edit_version; - stat.last_ready_version = ready_version; -} - -Renderable_Frame_Stat& Frame_Lifecycle_Record::renderable_stat(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth) { - for (auto& stat : renderable_stats) { - if (stat.node_id == node_id && stat.parent_node_id == parent_node_id && stat.tree_depth == tree_depth) - return stat; - } - Renderable_Frame_Stat stat; - stat.node_id = node_id; - stat.parent_node_id = parent_node_id; - stat.object_name = std::string(object_name); - stat.parent_object_name = std::string(parent_object_name); - stat.tree_depth = tree_depth; - renderable_stats.push_back(std::move(stat)); - return renderable_stats.back(); -} - -Renderable_Cache_Frame_Stats& Frame_Lifecycle_Record::cache_stat(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth) { - for (auto& stat : renderable_cache_stats) { - if (stat.node_id == node_id && stat.parent_node_id == parent_node_id && stat.tree_depth == tree_depth) - return stat; - } - Renderable_Cache_Frame_Stats stat; - stat.node_id = node_id; - stat.parent_node_id = parent_node_id; - stat.object_name = std::string(object_name); - stat.parent_object_name = std::string(parent_object_name); - stat.tree_depth = tree_depth; - renderable_cache_stats.push_back(std::move(stat)); - return renderable_cache_stats.back(); -} - -} // namespace renderive diff --git a/Core/architecture/Frame_Scheduler.h b/Core/architecture/Frame_Scheduler.h deleted file mode 100644 index 7118713..0000000 --- a/Core/architecture/Frame_Scheduler.h +++ /dev/null @@ -1,240 +0,0 @@ -#pragma once -#include "Render_Config.h" -#include -#include -#include -#include -#include -namespace renderive { -struct Frame_Lifecycle_Record; -struct Frame_Feedback_Event_Record; - -struct Frame_Lifecycle_Observer { - virtual ~Frame_Lifecycle_Observer() = default; - virtual bool enabled() const = 0; - virtual bool capture_enabled() const { - return enabled(); - } - virtual void collect_frame(std::shared_ptr frame) = 0; - virtual void collect_feedback_event(std::shared_ptr) {} -}; - -struct Refresh_Control_Snapshot { - Refresh_Limit_Source source = Refresh_Limit_Source::Unknown; - Refresh_Limit_Source candidate_source = Refresh_Limit_Source::Unknown; - Low_Latency_Bottleneck_State bottleneck = Low_Latency_Bottleneck_State::Unknown; - Low_Latency_Bottleneck_State stable_bottleneck = Low_Latency_Bottleneck_State::Unknown; - Low_Latency_Bottleneck_State candidate_bottleneck = Low_Latency_Bottleneck_State::Unknown; - Low_Latency_Production_Mode production_mode = Low_Latency_Production_Mode::Latency_Eager; - Low_Latency_Limit_Reason limit_reason = Low_Latency_Limit_Reason::User_Fps; - Consumer_Confidence consumer_confidence = Consumer_Confidence::Low; - Consumer_Sample_Result last_consumer_sample_result = Consumer_Sample_Result::Rejected_No_New_Frame; - Low_Latency_Feedback_Decision last_decision = Low_Latency_Feedback_Decision::Hold_User_Period; - std::string decision_reason; - std::uint64_t decision_timestamp_ns{}; - std::uint64_t candidate_age_ns{}; - std::uint64_t candidate_samples{}; - std::uint64_t producer_interval_ns{}; - std::uint64_t producer_interval_current_ns{}; - std::uint64_t producer_interval_srtt_ns{}; - std::uint64_t producer_interval_jitter_ns{}; - std::uint64_t input_period_ns{}; - std::uint64_t input_interval_current_ns{}; - std::uint64_t input_interval_srtt_ns{}; - std::uint64_t input_jitter_ns{}; - std::uint64_t render_period_ns{}; - std::uint64_t render_service_current_ns{}; - std::uint64_t render_service_srtt_ns{}; - std::uint64_t render_jitter_ns{}; - std::uint64_t paint_period_ns{}; - std::uint64_t paint_jitter_ns{}; - std::uint64_t ready_queue_srtt_ns{}; - std::uint64_t ready_queue_jitter_ns{}; - std::uint64_t gui_srtt_ns{}; - std::uint64_t gui_jitter_ns{}; - std::uint64_t consumption_srtt_ns{}; - std::uint64_t consumption_jitter_ns{}; - std::uint64_t request_to_update_post_current_ns{}; - std::uint64_t request_to_update_post_srtt_ns{}; - std::uint64_t request_to_update_post_jitter_ns{}; - std::uint64_t update_to_paint_begin_ns{}; - std::uint64_t update_post_to_paint_current_ns{}; - std::uint64_t update_post_to_paint_srtt_ns{}; - std::uint64_t update_to_paint_jitter_ns{}; - std::uint64_t paint_service_ns{}; - std::uint64_t paint_service_current_ns{}; - std::uint64_t paint_service_srtt_ns{}; - std::uint64_t paint_service_jitter_ns{}; - std::uint64_t display_frame_interval_ns{}; - std::uint64_t display_frame_interval_jitter_ns{}; - std::uint64_t raw_display_interval_current_ns{}; - std::uint64_t raw_display_interval_srtt_ns{}; - std::uint64_t raw_display_interval_jitter_ns{}; - std::uint64_t accepted_consumer_interval_current_ns{}; - std::uint64_t accepted_consumer_interval_srtt_ns{}; - std::uint64_t accepted_consumer_interval_jitter_ns{}; - std::uint64_t frame_age_srtt_ns{}; - std::uint64_t frame_age_jitter_ns{}; - std::uint64_t user_period_ns{}; - std::uint64_t target_period_ns{}; - std::uint64_t effective_min_render_interval_ns{}; - std::uint64_t next_render_time_ns{}; - std::uint64_t admission_wait_elapsed_ns{}; - std::uint64_t admission_wait_remaining_ns{}; - std::uint64_t executor_queue_wait_ns{}; - std::uint64_t present_wait_ns{}; - std::uint64_t consumer_sample_accepted_count{}; - std::uint64_t consumer_sample_rejected_no_backlog_count{}; - std::uint64_t consumer_sample_rejected_starved_count{}; - std::uint64_t consumer_sample_rejected_no_new_frame_count{}; - std::uint64_t consumer_sample_rejected_invalid_timestamp_count{}; - std::uint64_t consumer_sample_total_count{}; - double consumer_sample_accepted_ratio{}; - std::uint64_t consumer_window_size{}; - std::uint64_t consumer_window_accepted{}; - std::uint64_t consumer_window_rejected_no_backlog{}; - std::uint64_t consumer_window_rejected_starved{}; - std::uint64_t consumer_window_rejected_no_new_frame{}; - std::uint64_t consumer_window_rejected_invalid{}; - double consumer_window_accepted_ratio{}; - std::uint64_t producer_starvation_count{}; - std::uint64_t producer_starvation_epoch_id{}; - bool producer_starvation_active{}; - std::uint64_t mode_enter_count{}; - std::uint64_t mode_exit_count{}; - std::uint64_t update_posted_count{}; - std::uint64_t duplicate_present_request_suppressed_count{}; - std::uint64_t update_ack_count{}; - std::uint64_t update_repost_count{}; - std::uint64_t paint_without_posted_update_count{}; - std::uint64_t middle_published_count{}; - std::uint64_t middle_superseded_count{}; - std::uint64_t painting_advanced_count{}; - std::uint64_t presented_frame_count{}; - std::uint64_t superseded_frame_count{}; - std::uint64_t repeated_frame_count{}; - std::uint64_t underrun_count{}; - double display_efficiency = 1.0; -}; - -inline std::uint32_t dirty_reason_bit(Dirty_Reason reason) { - return 1u << static_cast(reason); -} - -struct Renderable_Cache_Frame_Stats { - std::uint64_t node_id{}; - std::uint64_t parent_node_id{}; - std::string object_name; - std::string parent_object_name; - int tree_depth{}; - std::uint64_t hit_count{}; - std::uint64_t miss_count{}; - std::uint64_t rebuild_count{}; - std::uint64_t rebuild_time_ns{}; - std::uint64_t compose_time_ns{}; - std::uint64_t last_rebuild_time_ns{}; - std::uint64_t last_compose_time_ns{}; - std::uint64_t last_image_bytes{}; - std::uint64_t last_image_area{}; - std::uint64_t last_edit_version{}; - std::uint64_t last_ready_version{}; -}; -struct Renderable_Frame_Stat { - std::uint64_t node_id{}; - std::uint64_t parent_node_id{}; - std::string object_name; - std::string parent_object_name; - int tree_depth{}; - bool cache_enabled{}; - std::uint64_t render_count{}; - std::uint64_t self_draw_count{}; - std::uint64_t total_render_time_ns{}; - std::uint64_t self_draw_time_ns{}; - std::uint64_t last_total_render_time_ns{}; - std::uint64_t last_self_draw_time_ns{}; -}; -struct Frame_Lifecycle_Record { - std::uint64_t frame_id{}; - std::uint64_t input_version{}; - std::uint64_t core_data_time_ns{}; - std::uint64_t render_request_ns{}; - std::uint64_t render_role_enter_ns{}; - std::uint64_t render_begin_ns{}; - std::uint64_t prepare_begin_ns{}; - std::uint64_t prepare_end_ns{}; - std::uint64_t draw_begin_ns{}; - std::uint64_t draw_end_ns{}; - std::uint64_t render_end_ns{}; - std::uint64_t transition_12_ns{}; - std::uint64_t update_request_id{}; - std::uint64_t update_request_time_ns{}; - std::uint64_t update_posted_ns{}; - std::uint64_t transition_23_ns{}; - std::uint64_t paint_begin_ns{}; - std::uint64_t paint_end_ns{}; - std::uint64_t presented_frame_id{}; - std::uint64_t transition_32_ns{}; - std::uint64_t render_slot_idle_wait_ns{}; - std::uint64_t executor_queue_wait_ns{}; - std::uint64_t ready_queue_wait_ns{}; - std::uint64_t present_queue_wait_ns{}; - std::uint64_t paint_return_wait_ns{}; - std::uint64_t superseded_time_ns{}; - Frame_Outcome outcome = Frame_Outcome::Render_Acquire_Dropped; - Refresh_Control_Snapshot refresh; - Frame_Worker_Stats worker; - std::uint64_t raw_input_count{}; - std::uint64_t prepared_output_count{}; - std::uint64_t renderable_cache_hit_count{}; - std::uint64_t renderable_cache_miss_count{}; - std::uint64_t renderable_cache_rebuild_count{}; - std::uint64_t renderable_cache_rebuild_time_ns{}; - std::uint64_t renderable_cache_compose_time_ns{}; - std::uint64_t renderable_cache_image_bytes{}; - std::uint64_t renderable_cache_image_area{}; - std::uint64_t pixel_width{}; - std::uint64_t pixel_height{}; - std::uint64_t viewport_version{}; - std::uint32_t dirty_reason_mask{}; - std::vector renderable_stats; - std::vector renderable_cache_stats; - void record_renderable_render(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth, Renderable_Cache_Mode cache_mode, std::uint64_t render_time_ns); - void record_renderable_self_draw(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth, Renderable_Cache_Mode cache_mode, std::uint64_t draw_time_ns); - void record_renderable_cache_hit(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth); - void record_renderable_cache_miss(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth); - void record_renderable_cache_rebuild(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth, std::uint64_t rebuild_time_ns, std::uint64_t image_bytes, std::uint64_t image_area, std::uint64_t edit_version, std::uint64_t ready_version); - void record_renderable_cache_compose(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth, std::uint64_t compose_time_ns, std::uint64_t image_bytes, std::uint64_t image_area, std::uint64_t edit_version, std::uint64_t ready_version); -private: - Renderable_Frame_Stat& renderable_stat(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth); - Renderable_Cache_Frame_Stats& cache_stat(std::uint64_t node_id, std::uint64_t parent_node_id, std::string_view object_name, std::string_view parent_object_name, int tree_depth); -}; -struct Frame_Feedback_Event_Record { - std::string event; - Frame_Lifecycle_Record frame; - std::uint64_t timestamp_ns{}; - Low_Latency_Production_Mode mode_before = Low_Latency_Production_Mode::Latency_Eager; - Low_Latency_Production_Mode mode_after = Low_Latency_Production_Mode::Latency_Eager; - std::uint64_t effective_interval_before_ns{}; - std::uint64_t effective_interval_after_ns{}; - bool dirty{}; - bool render_active{}; - bool middle_has_new_frame{}; - bool paint_active{}; - bool paint_update_posted{}; - bool backlog_epoch_active{}; - bool continuously_backlogged{}; - bool producer_starved{}; - std::uint64_t backlog_epoch_id{}; - std::uint64_t backlog_rendered_frames{}; - std::uint64_t backlog_superseded_frames{}; - bool flush{}; -}; -inline std::uint64_t renderable_cache_subtree_compose_time(const std::vector& stats, const Renderable_Cache_Frame_Stats& node) { - std::uint64_t compose_time_ns = node.compose_time_ns; - for (const auto& stat : stats) { - if (stat.parent_node_id == node.node_id && stat.tree_depth == node.tree_depth + 1) - compose_time_ns += renderable_cache_subtree_compose_time(stats, stat); - } - return compose_time_ns; -} -} // namespace renderive diff --git a/Core/architecture/Plot_Render_Context.cpp b/Core/architecture/Plot_Render_Context.cpp deleted file mode 100644 index 3cdb60e..0000000 --- a/Core/architecture/Plot_Render_Context.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "Plot_Render_Context.h" diff --git a/Core/architecture/Plot_Render_Context.h b/Core/architecture/Plot_Render_Context.h deleted file mode 100644 index 2c64174..0000000 --- a/Core/architecture/Plot_Render_Context.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once -#include "Frame_Scheduler.h" -#include -#include -#include -#include -#include -#include -#include - -namespace renderive { - -class Plot_Core; -class Timeline_Stream; - -struct Plot_Render_Context { - std::atomic_bool destroying{false}; - std::atomic_bool first_resize{true}; - std::atomic_bool first_prepare_data{true}; - std::atomic_int render_width{0}; - std::atomic_int render_height{0}; - std::atomic viewport_version{0}; - std::atomic background_rgba{0xff000000u}; - std::shared_ptr frame_lifecycle_observer; - std::mutex performance_update_callback_mutex; - std::function performance_update_callback; - std::mutex performance_clipboard_callback_mutex; - std::function performance_clipboard_callback; - std::atomic performance_plot_id{0}; - mutable std::mutex performance_plot_name_mutex; - std::string performance_plot_name; - std::atomic owner{}; -}; - -} // namespace renderive diff --git a/Core/architecture/Render_Config.h b/Core/architecture/Render_Config.h deleted file mode 100644 index 23ac7ea..0000000 --- a/Core/architecture/Render_Config.h +++ /dev/null @@ -1,145 +0,0 @@ -#pragma once -#include -#include -namespace renderive { -using Plot_Execution_Id = std::uint64_t; -enum class Dirty_Reason : std::uint8_t { - Core_Data, - Config, - Viewport -}; -enum class Render_Task_Kind : std::uint8_t { - Frame, - Curve, - Waterfall, - Primitive, - Compose, - Performance -}; -struct Render_Runtime_Config { - std::size_t worker_count{}; -}; -struct Render_Executor_Snapshot { - std::size_t worker_count{}; - std::size_t active_workers{}; - std::size_t idle_workers{}; - std::size_t active_frame_jobs{}; - std::size_t queued_frame_jobs{}; - std::uint64_t submitted_topologies{}; - std::uint64_t started_topologies{}; - std::uint64_t completed_topologies{}; - std::uint64_t started_task_nodes{}; - std::uint64_t completed_task_nodes{}; - std::uint64_t rejected_frame_jobs{}; - std::uint64_t dropped_frame_attempts{}; - std::uint64_t peak_concurrency{}; - std::uint64_t average_topology_queue_wait_ns{}; - std::uint64_t max_topology_queue_wait_ns{}; - std::uint64_t average_task_node_duration_ns{}; - std::uint64_t max_task_node_duration_ns{}; - std::uint64_t curve_tasks{}; - std::uint64_t waterfall_tasks{}; - std::uint64_t primitive_tasks{}; - std::uint64_t compose_tasks{}; - std::uint64_t performance_tasks{}; -}; -struct Frame_Worker_Stats { - Render_Executor_Snapshot executor_at_submit; - Render_Executor_Snapshot executor_at_finish; - std::uint32_t business_task_count{}; - std::uint32_t business_task_peak_parallelism{}; - std::uint64_t topology_queue_wait_total_ns{}; - std::uint64_t topology_queue_wait_max_ns{}; - std::uint64_t business_task_run_total_ns{}; - std::uint64_t topology_run_total_ns{}; - std::uint64_t rejected_topology_count{}; -}; -enum class Refresh_Limit_Source : std::uint8_t { - Unknown, - Input, - Render, - Paint, - User -}; -enum class Low_Latency_Limit_Reason : std::uint8_t { - User_Fps, - Consumer_Capacity, - Render_Capacity, - No_Work, - Shutdown -}; -enum class Low_Latency_Bottleneck_State : std::uint8_t { - Unknown, - Input_Limited, - Producer_Limited, - Consumer_Limited, - User_Limited -}; -enum class Low_Latency_Production_Mode : std::uint8_t { - Latency_Eager, - Consumer_Paced -}; -enum class Consumer_Confidence : std::uint8_t { - Low, - Medium, - High -}; -enum class Consumer_Sample_Result : std::uint8_t { - Accepted, - Rejected_No_Backlog, - Rejected_Producer_Starved, - Rejected_No_New_Frame, - Rejected_Invalid_Timestamp -}; -enum class Low_Latency_Feedback_Decision : std::uint8_t { - Hold_User_Period, - Enter_Consumer_Paced, - Exit_Consumer_Paced, - Reject_Consumer_Sample, - Producer_Starved -}; -struct Consumer_Backlog_Epoch { - std::uint64_t begin_ns{}; - std::uint64_t end_ns{}; - std::uint32_t rendered_frames{}; - std::uint32_t superseded_frames{}; - bool continuously_backlogged{}; - bool producer_starved{}; -}; -struct Low_Latency_Performance_Diagnostics { - std::uint64_t update_posted_count{}; - std::uint64_t duplicate_present_request_suppressed_count{}; - std::uint64_t update_ack_count{}; - std::uint64_t update_repost_count{}; - std::uint64_t consumer_sample_accepted_count{}; - std::uint64_t consumer_sample_rejected_no_backlog_count{}; - std::uint64_t consumer_sample_rejected_starved_count{}; - std::uint64_t consumer_sample_rejected_no_new_frame_count{}; - std::uint64_t consumer_sample_rejected_invalid_timestamp_count{}; - std::uint64_t producer_starvation_count{}; - std::uint64_t mode_enter_count{}; - std::uint64_t mode_exit_count{}; - std::uint64_t middle_published_count{}; - std::uint64_t middle_superseded_count{}; - std::uint64_t painting_advanced_count{}; - std::uint64_t painting_repeated_count{}; - std::uint64_t underrun_count{}; - std::uint64_t paint_without_posted_update_count{}; -}; -enum class Frame_Outcome : std::uint8_t { - Presented, - Superseded, - Render_Acquire_Dropped, - Publish_Dropped, - Paint_Acquire_Dropped, - Worker_Budget_Dropped, - Cancelled -}; -/// @brief Rendering cache strategy for a renderable subtree. -enum class Renderable_Cache_Mode : std::uint8_t { - /// @brief Draw directly into the target painter each frame. - Direct, - /// @brief Rebuild a local image cache when state changes, then compose it. - Local_Pixel -}; -} // namespace renderive diff --git a/Core/architecture/Render_Data.h b/Core/architecture/Render_Data.h deleted file mode 100644 index 930d9b6..0000000 --- a/Core/architecture/Render_Data.h +++ /dev/null @@ -1,398 +0,0 @@ -#pragma once -#include "../base/Geometry.h" -#include "../base/Memory.h" -#include "../base/Object_Semantics.h" -#include "../event/Event.h" -#include "../event/Key_Event.h" -#include "../event/Pointer_Event.h" -#include "../event/Resize_Event.h" -#include "../event/Wheel_Event.h" -#include "Render_Config.h" -#include "Update_Completion.h" -#include "global.h" -#include -#include -#include -#include -#include -#include -#include - -namespace renderive { - -class Canvas; -struct Render_Frame_Snapshot; -class Renderable; -class Renderable_Runtime; - -struct Input_Data { - virtual ~Input_Data() = default; - virtual void clear() {} - virtual void consume() { - clear(); - } -}; - -struct Latest_Input_Data : Input_Data { - bool pending = false; - - void mark_latest_pushed() { - pending = true; - } - - void clear_latest() { - pending = false; - } -}; - -struct Render_State { - virtual ~Render_State() = default; - std::uint64_t version = 0; -}; - -struct Hit_Testable { - virtual ~Hit_Testable() = default; - virtual bool select_test(const PointF& pos) = 0; -}; - -struct Pointer_Interactive { - virtual ~Pointer_Interactive() = default; - virtual void mouse_press_event(const Pointer_Event& event) = 0; - virtual void mouse_move_event(const Pointer_Event& event) = 0; - virtual void mouse_release_event(const Pointer_Event& event) = 0; -}; - -struct Wheel_Interactive { - virtual ~Wheel_Interactive() = default; - virtual void wheel_event(const Wheel_Event& event) = 0; -}; - -struct Key_Press_Interactive { - virtual ~Key_Press_Interactive() = default; - virtual void key_press_event(const Key_Event& event) = 0; -}; - -struct Key_Release_Interactive { - virtual ~Key_Release_Interactive() = default; - virtual void key_release_event(const Key_Event& event) = 0; -}; - -struct Hover_Interactive { - virtual ~Hover_Interactive() = default; - virtual void set_hover_state(Point pos, bool active) = 0; -}; - -struct Render_Object_Owner : Move_Only { - Render_Object_Owner() noexcept = default; - - Render_Object_Owner(void* pointer, std::pmr::memory_resource* resource, void (*destroy)(void*, std::pmr::memory_resource*)) noexcept - : pointer(pointer), resource(resource), destroy(destroy) {} - - Render_Object_Owner(Render_Object_Owner&& other) noexcept - : pointer(std::exchange(other.pointer, nullptr)), - resource(std::exchange(other.resource, nullptr)), - destroy(std::exchange(other.destroy, nullptr)) {} - - Render_Object_Owner& operator=(Render_Object_Owner&& other) noexcept { - if (this == &other) - return *this; - reset(); - pointer = std::exchange(other.pointer, nullptr); - resource = std::exchange(other.resource, nullptr); - destroy = std::exchange(other.destroy, nullptr); - return *this; - } - - ~Render_Object_Owner() { - reset(); - } - - template - T* get() const { - return static_cast(pointer); - } - - void reset() noexcept { - if (!pointer) - return; - destroy(pointer, resource); - pointer = nullptr; - resource = nullptr; - destroy = nullptr; - } - - explicit operator bool() const noexcept { - return pointer != nullptr; - } - - void* pointer{}; - std::pmr::memory_resource* resource{}; - void (*destroy)(void*, std::pmr::memory_resource*){}; -}; - -template -Render_Object_Owner make_render_object(Args&&... args) { - std::pmr::memory_resource* resource = memory_resource(Memory_Domain::Renderable); - std::pmr::polymorphic_allocator allocator(resource); - T* pointer = allocator.allocate(1); - try { - std::construct_at(pointer, std::forward(args)...); - } - catch (...) { - allocator.deallocate(pointer, 1); - throw; - } - return Render_Object_Owner(pointer, resource, [](void* object, std::pmr::memory_resource* resource) { - std::pmr::polymorphic_allocator allocator(resource); - std::destroy_at(static_cast(object)); - allocator.deallocate(static_cast(object), 1); - }); -} - -struct Render_State_Read_Handle { - virtual ~Render_State_Read_Handle() = default; -}; - -struct Render_State_Write_Handle { - virtual ~Render_State_Write_Handle() = default; -}; - -struct Render_Input_Write_Handle { - virtual ~Render_Input_Write_Handle() = default; - virtual void set_completion(std::shared_ptr state) { - (void)state; - } -}; - -struct Render_Input_Frame_Handle { - virtual ~Render_Input_Frame_Handle() = default; - virtual void consume_input(Input_Data* input) { - if (input) - input->consume(); - } -}; - -struct Render_State_Read_Guard : Move_Only { - Render_State_Read_Guard() = default; - Render_State_Read_Guard(Render_State* state, std::unique_ptr handle) - : state(state), handle(std::move(handle)) {} - Render_State_Read_Guard(Render_State_Read_Guard&&) noexcept = default; - Render_State_Read_Guard& operator=(Render_State_Read_Guard&&) noexcept = default; - ~Render_State_Read_Guard() = default; - - explicit operator bool() const noexcept { - return state && handle; - } - - Render_State* state{}; - std::unique_ptr handle; -}; - -struct Render_State_Write_Guard : Move_Only { - Render_State_Write_Guard() = default; - Render_State_Write_Guard(Render_State* state, std::unique_ptr handle) - : state(state), handle(std::move(handle)) {} - Render_State_Write_Guard(Render_State_Write_Guard&&) noexcept = default; - Render_State_Write_Guard& operator=(Render_State_Write_Guard&&) noexcept = default; - ~Render_State_Write_Guard() = default; - - explicit operator bool() const noexcept { - return state && handle; - } - - Render_State* state{}; - std::unique_ptr handle; -}; - -struct Render_Input_Write_Guard : Move_Only { - Render_Input_Write_Guard() = default; - Render_Input_Write_Guard(Input_Data* input, std::unique_ptr handle) - : input(input), handle(std::move(handle)) {} - Render_Input_Write_Guard(Render_Input_Write_Guard&&) noexcept = default; - Render_Input_Write_Guard& operator=(Render_Input_Write_Guard&&) noexcept = default; - ~Render_Input_Write_Guard() = default; - - explicit operator bool() const noexcept { - return input && handle; - } - - Input_Data* input{}; - std::unique_ptr handle; - - void set_completion(std::shared_ptr state) { - if (handle) - handle->set_completion(std::move(state)); - } -}; - -struct Render_Input_Frame_Guard : Move_Only { - Render_Input_Frame_Guard() = default; - Render_Input_Frame_Guard(Input_Data* input, std::unique_ptr handle) - : input(input), handle(std::move(handle)) {} - Render_Input_Frame_Guard(Render_Input_Frame_Guard&&) noexcept = default; - Render_Input_Frame_Guard& operator=(Render_Input_Frame_Guard&&) noexcept = default; - ~Render_Input_Frame_Guard() = default; - - explicit operator bool() const noexcept { - return input && handle; - } - - Input_Data* input{}; - std::unique_ptr handle; - - void consume_input() const { - if (handle) - handle->consume_input(input); - } -}; - -enum class Renderable_State_Role : std::uint8_t { - Edit, - Ready, - Render -}; - -enum class Renderable_Input_Role : std::uint8_t { - Edit, - Ready, - Render -}; - -struct Renderable_Frame_View : Move_Only { - Renderable_Frame_View() = default; - Renderable_Frame_View(Render_State_Read_Guard state_guard, Render_Input_Frame_Guard input_guard) - : state(state_guard.state), - input(input_guard.input), - state_guard(std::move(state_guard)), - input_guard(std::move(input_guard)) {} - Renderable_Frame_View(Renderable_Frame_View&&) noexcept = default; - Renderable_Frame_View& operator=(Renderable_Frame_View&&) noexcept = default; - ~Renderable_Frame_View() = default; - - explicit operator bool() const noexcept { - return state && state_guard; - } - - Render_State* state{}; - Input_Data* input{}; - Render_State_Read_Guard state_guard; - Render_Input_Frame_Guard input_guard; - - void consume_input() const { - input_guard.consume_input(); - } -}; - -class Renderable_Runtime { -public: - using Copy_Render_State_Function = std::function; - - virtual ~Renderable_Runtime() = default; - virtual void set_render_state_copier(Copy_Render_State_Function copier) = 0; - virtual void cancel_pending_completions() = 0; - virtual void drain_committed_update_states(std::pmr::vector>& output) = 0; - virtual Render_State_Read_Guard acquire_state_read(Renderable_State_Role role) = 0; - virtual Render_State_Read_Guard acquire_render_state_read() = 0; - virtual Render_State_Read_Guard acquire_edit_state_read() = 0; - virtual Render_State_Write_Guard acquire_edit_state() = 0; - virtual Render_Input_Write_Guard acquire_edit_input(bool notify_render) = 0; - virtual Render_Input_Frame_Guard acquire_input_read(Renderable_Input_Role role) = 0; - virtual Renderable_Frame_View capture_frame_view() = 0; - virtual Render_State* state(Renderable_State_Role role) = 0; - virtual Input_Data* input_data(Renderable_Input_Role role) = 0; - virtual void clear_render_input() = 0; -}; - -struct Render_Data { - Renderable* q_ptr{}; - - virtual void initialize_runtime(std::unique_ptr runtime) = 0; - virtual void cancel_pending_completions() = 0; - virtual void drain_committed_update_states(std::pmr::vector>& output) = 0; - virtual Render_State_Read_Guard acquire_state_read(Renderable_State_Role role) = 0; - virtual Render_State_Read_Guard acquire_render_state_read() = 0; - virtual Render_State_Read_Guard acquire_edit_state_read() = 0; - virtual Render_State_Write_Guard acquire_edit_state() = 0; - virtual Render_Input_Write_Guard acquire_edit_input(bool notify_render) = 0; - virtual Render_Input_Frame_Guard acquire_input_read(Renderable_Input_Role role) = 0; - virtual Renderable_Frame_View capture_frame_view() = 0; - virtual Render_State* state(Renderable_State_Role role) = 0; - virtual Render_State* render_state() = 0; - virtual Render_State* ready_state() = 0; - virtual Render_State* edit_state() = 0; - virtual Input_Data* input_data(Renderable_Input_Role role) = 0; - virtual Input_Data* render_input_data() = 0; - virtual Input_Data* edit_input_data() = 0; - virtual ~Render_Data() = default; - virtual void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) {} - virtual void prepare_data(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) {} - virtual Rect pixel_bounds(const Size& plot_size) { - return {0, 0, plot_size.width, plot_size.height}; - } - [[nodiscard]] virtual bool requires_frame_view() const { - return false; - } -protected: - virtual void copy_render_state_object(Render_State& target, const Render_State& source) { - target.version = source.version; - } -}; - -struct Base_Render_Data : Render_Data { - void initialize_runtime(std::unique_ptr) override {} - void cancel_pending_completions() override {} - void drain_committed_update_states(std::pmr::vector>&) override {} - Render_State_Read_Guard acquire_state_read(Renderable_State_Role) override { - return {}; - } - Render_State_Read_Guard acquire_render_state_read() override { - return {}; - } - Render_State_Read_Guard acquire_edit_state_read() override { - return {}; - } - Render_State_Write_Guard acquire_edit_state() override { - return {}; - } - Render_Input_Write_Guard acquire_edit_input(bool) override { - return {}; - } - Render_Input_Frame_Guard acquire_input_read(Renderable_Input_Role) override { - return {}; - } - Render_State* state(Renderable_State_Role) override { - return nullptr; - } - Render_State* render_state() override { - return nullptr; - } - Render_State* ready_state() override { - return nullptr; - } - Render_State* edit_state() override { - return nullptr; - } - Input_Data* render_input_data() override { - return nullptr; - } - Input_Data* edit_input_data() override { - return nullptr; - } - Input_Data* input_data(Renderable_Input_Role) override { - return nullptr; - } - Renderable_Frame_View capture_frame_view() override { - return {}; - } -}; - -template -concept Render_State_Type = std::derived_from && std::is_copy_assignable_v; - -template -concept Input_Data_Type = std::derived_from; - -template -concept Render_Data_Type = std::derived_from; - -} // namespace renderive diff --git a/Core/architecture/Render_Lease.h b/Core/architecture/Render_Lease.h deleted file mode 100644 index a3a4b3d..0000000 --- a/Core/architecture/Render_Lease.h +++ /dev/null @@ -1,212 +0,0 @@ -#pragma once -#include "Renderable.h" -#include "Strategy_Render_Data.h" -#include - -namespace renderive { - -template - requires std::derived_from -struct Typed_Render_Data_Base : Base { - Owner* q() { - return reinterpret_cast(this->q_ptr); - } - State* render_state() override { - return reinterpret_cast(this->state(Renderable_State_Role::Render)); - } - State* ready_state() override { - return reinterpret_cast(this->state(Renderable_State_Role::Ready)); - } - State* edit_state() override { - return reinterpret_cast(this->state(Renderable_State_Role::Edit)); - } - Input* render_input_data() override { - return reinterpret_cast(this->input_data(Renderable_Input_Role::Render)); - } - Input* edit_input_data() override { - return reinterpret_cast(this->input_data(Renderable_Input_Role::Edit)); - } - State* render_state(const Renderable_Frame_View& frame_view) const { - return reinterpret_cast(frame_view.state); - } - Input* render_input_data(const Renderable_Frame_View& frame_view) const { - return reinterpret_cast(frame_view.input); - } - State* state_data(Renderable_State_Role role) { - return reinterpret_cast(this->state(role)); - } - template - requires std::derived_from - Field state_value(Field Source_State::* field, Renderable_State_Role role) { - auto guard = this->acquire_state_read(role); - if (!guard) - return {}; - auto state = reinterpret_cast(guard.state); - return state->*field; - } - template - requires std::derived_from - Field edit_state_value(Field Source_State::* field) { - return state_value(field, Renderable_State_Role::Edit); - } - template - requires std::derived_from - Field ready_state_value(Field Source_State::* field) { - return state_value(field, Renderable_State_Role::Ready); - } - template - requires std::derived_from - Field render_state_value(Field Source_State::* field) { - return state_value(field, Renderable_State_Role::Render); - } - template - requires std::derived_from - void set_state_value(Field Source_State::* field, Field value) { - auto guard = this->acquire_edit_state(); - if (!guard) - return; - auto state = reinterpret_cast(guard.state); - state->*field = std::move(value); - } -protected: - void copy_render_state_object(Render_State& target, const Render_State& source) override { - static_cast(target) = static_cast(source); - } -}; - -template -using Typed_Render_Data = Typed_Render_Data_Base; - -template -struct Renderable_Binding { - static void init(Owner* owner, const char* name) { - owner->d_owner = make_render_object(); - owner->d_ptr = static_cast(owner->d_owner.template get()); - owner->set_object_name(name); - } - static Data* d(Owner* owner) { - return reinterpret_cast(owner->d_ptr); - } - static Render_Object_Owner create_render_data() { - return make_render_object(); - } - static Render_Object_Owner create_render_state() { - return make_render_object(); - } - static Render_Object_Owner create_input_data() { - if constexpr (std::is_same_v) - return {}; - return make_render_object(); - } -}; - -#define RENDERIVE_DEFINE_RENDERABLE_BINDING(Owner, Data, State, Input, Name) \ - using Owner##_Binding = ::renderive::Renderable_Binding; \ - Owner::Owner() { \ - Owner##_Binding::init(this, Name); \ - } \ - Data* Owner::d() { \ - return Owner##_Binding::d(this); \ - } \ - ::renderive::Render_Object_Owner Owner::create_render_data() { \ - return Owner##_Binding::create_render_data(); \ - } \ - ::renderive::Render_Object_Owner Owner::create_render_state() { \ - return Owner##_Binding::create_render_state(); \ - } \ - ::renderive::Render_Object_Owner Owner::create_input_data() { \ - return Owner##_Binding::create_input_data(); \ - } - -template - requires std::derived_from -struct Render_State_Lease : Immovable { - explicit Render_State_Lease(Data* data) : data(data) { - guard = data->acquire_edit_state_read(); - state = reinterpret_cast(guard.state); - } - ~Render_State_Lease() = default; - State* operator->() { - return state; - } - State& operator*() { - return *state; - } - explicit operator bool() const { - return state && guard; - } - Data* data{}; - Render_State_Read_Guard guard; - State* state{}; -}; - -template - requires std::derived_from -struct Render_Edit_Lease : Immovable { - explicit Render_Edit_Lease(Data* data) : data(data) { - guard = data->acquire_edit_state(); - state = reinterpret_cast(guard.state); - } - ~Render_Edit_Lease() = default; - State* operator->() { - return state; - } - State& operator*() { - return *state; - } - explicit operator bool() const { - return state && guard; - } - Data* data{}; - Render_State_Write_Guard guard; - State* state{}; -}; - -template - requires std::derived_from -struct Render_Input_Lease : Immovable { - explicit Render_Input_Lease(Data* data, bool notify_render = true) : data(data), notify_render(notify_render) { - guard = data->acquire_edit_input(notify_render); - input = reinterpret_cast(guard.input); - } - ~Render_Input_Lease() = default; - Input* operator->() { - return input; - } - Input& operator*() { - return *input; - } - explicit operator bool() const { - return input && guard; - } - void set_completion(std::shared_ptr state) { - guard.set_completion(std::move(state)); - } - Data* data{}; - bool notify_render{}; - Render_Input_Write_Guard guard; - Input* input{}; -}; - -template - requires std::derived_from -struct Render_Input_Read_Lease : Immovable { - explicit Render_Input_Read_Lease(Data* data, Renderable_Input_Role role = Renderable_Input_Role::Render) { - guard = data->acquire_input_read(role); - input = reinterpret_cast(guard.input); - } - ~Render_Input_Read_Lease() = default; - const Input* operator->() const { - return input; - } - const Input& operator*() const { - return *input; - } - explicit operator bool() const { - return input && guard; - } - Render_Input_Frame_Guard guard; - Input* input{}; -}; - -} // namespace renderive diff --git a/Core/architecture/Render_Time.h b/Core/architecture/Render_Time.h deleted file mode 100644 index fb5bafe..0000000 --- a/Core/architecture/Render_Time.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include -#include -namespace renderive { -inline std::uint64_t steady_now_ns() { - return static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count()); -} -} // namespace renderive diff --git a/Core/architecture/Render_Utils.cpp b/Core/architecture/Render_Utils.cpp deleted file mode 100644 index 7d7f3bb..0000000 --- a/Core/architecture/Render_Utils.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "Renderable.h" -#include "../resource/Turbo_Color_Map.h" - -namespace renderive { - -std::vector get_turbo_color_gradient() { - std::vector colors; - colors.reserve(Turbo_Color_Map.size()); - for (const auto& c : Turbo_Color_Map) - colors.push_back(pack_rgba(c.r, c.g, c.b, c.a)); - return colors; -} - -} diff --git a/Core/architecture/Renderable.cpp b/Core/architecture/Renderable.cpp deleted file mode 100644 index 301c0a5..0000000 --- a/Core/architecture/Renderable.cpp +++ /dev/null @@ -1,575 +0,0 @@ -#include -#include -#include "../base/Memory.h" -#include "../render/Frame_Prepare_Subflow.h" -#include "Plot_Render_Context.h" -#include "../plot/Plot_Core.h" -#include "../plot/Plot_Core_Access.h" -#include "../render/Render_Frame_Snapshot.h" -#include "Renderable.h" -#include "Render_Time.h" -namespace renderive { -namespace { -std::uint64_t next_renderable_node_id() { - static std::atomic next_id{1}; - return next_id.fetch_add(1, std::memory_order_acq_rel); -} -} // namespace - -Renderable_Cache_Mode Renderable::get_cache_mode() const { - return cache_mode.load(std::memory_order_acquire); -} -void Renderable::set_cache_mode(Renderable_Cache_Mode mode) { - cache_mode.store(mode, std::memory_order_release); - mark_render_dirty(Dirty_Reason::Config); -} -bool Renderable::attached_to_plot() const { - return plot != nullptr; -} -bool Renderable::shares_plot_with(const Renderable& other) const { - return plot != nullptr && plot == other.plot; -} -const std::string& Renderable::object_name() const { - return object_name_; -} -std::uint64_t Renderable::node_id() const { - return node_id_; -} -void Renderable::set_object_name(std::string name) { - if (initialized.load(std::memory_order_acquire) && object_name_ != name) { - ASSERT(false, "Renderable object_name must be fixed before attach"); - return; - } - object_name_ = std::move(name); -} -bool Renderable::is_visible() const { - return visible_.load(std::memory_order_acquire); -} -void Renderable::set_visible(bool value) { - bool old = visible_.exchange(value, std::memory_order_acq_rel); - if (old == value) - return; - mark_render_dirty(Dirty_Reason::Config); -} -bool Renderable::should_prepare() const { - return !get_retiring() && visible_.load(std::memory_order_acquire) && d_ptr; -} -void Renderable::detach_from_parent() { - if (auto parent = parent_renderable.lock()) - parent->remove_child(shared_from_this()); -} -std::string Renderable::cache_parent_object_name() const { - std::shared_ptr parent = parent_renderable.lock(); - while (parent) { - if (parent->get_cache_mode() == Renderable_Cache_Mode::Local_Pixel) - return parent->object_name_; - parent = parent->parent_renderable.lock(); - } - return {}; -} -int Renderable::cache_tree_depth() const { - int depth = 0; - std::shared_ptr parent = parent_renderable.lock(); - while (parent) { - if (parent->get_cache_mode() == Renderable_Cache_Mode::Local_Pixel) - ++depth; - parent = parent->parent_renderable.lock(); - } - return depth; -} -std::string Renderable::render_parent_object_name() const { - if (auto parent = parent_renderable.lock()) - return parent->object_name_; - return {}; -} -int Renderable::render_tree_depth() const { - int depth = 0; - std::shared_ptr parent = parent_renderable.lock(); - while (parent) { - ++depth; - parent = parent->parent_renderable.lock(); - } - return depth; -} -void Renderable::init_root(Plot_Core* plot) { - if (!plot) { - ASSERT(plot, "Renderable root init error! plot == nullptr"); - return; - } - bool initialized = this->initialized.exchange(true, std::memory_order_acq_rel); - if (initialized) { - ASSERT(!initialized, "Renderable root init error! Renderable already initialized"); - return; - } - init_common(plot); -} -void Renderable::init(const std::shared_ptr& parent) { - if (!parent || !parent->plot) { - ASSERT(parent && parent->plot, "Renderable init error! parent invalid"); - return; - } - bool initialized = this->initialized.exchange(true, std::memory_order_acq_rel); - if (initialized) { - ASSERT(!initialized, "Renderable init error! Renderable already initialized"); - return; - } - init_common(parent->plot); - parent->add_child(shared_from_this()); -} -void Renderable::init_common(Plot_Core* plot) { - this->plot = plot; - if (!node_id_) - node_id_ = next_renderable_node_id(); - plot_context = Plot_Core_Context_Access::render_context(*plot); - retiring.store(false, std::memory_order_release); - if (!d_ptr) { - d_owner = create_render_data(); - d_ptr = d_owner.get(); - } - d_ptr->q_ptr = this; - d_ptr->initialize_runtime(plot->create_renderable_runtime(*this)); - plot->notify_model_dirty(Dirty_Reason::Config); -} -void Renderable::add_child(const std::shared_ptr& child) { - if (!child || child.get() == this || child->plot != plot || !child->parent_renderable.expired() || get_retiring() || child->get_retiring()) { - ASSERT(child && child.get() != this && child->plot == plot && child->parent_renderable.expired() && !get_retiring() && !child->get_retiring(), "Renderable add_child error! invalid child"); - return; - } - { - std::lock_guard lock(children_mutex); - if (get_retiring()) - return; - if (std::find(children.begin(), children.end(), child) != children.end()) - return; - child->parent_renderable = shared_from_this(); - children.push_back(child); - } - // Marking dirty may render synchronously, so the child list lock must be released first. - mark_render_dirty(Dirty_Reason::Config); -} -void Renderable::remove_child(const std::shared_ptr& child) { - if (!child || child->parent_renderable.lock().get() != this) - return; - std::shared_ptr removed; - { - std::lock_guard lock(children_mutex); - auto it = std::find(children.begin(), children.end(), child); - if (it == children.end()) - return; - removed = *it; - removed->parent_renderable.reset(); - children.erase(it); - } - removed->detach_from_plot_tree(); - mark_render_dirty(Dirty_Reason::Config); -} -std::vector> Renderable::children_snapshot() const { - std::lock_guard lock(children_mutex); - return children; -} -void Renderable::append_tree_snapshot( - Renderable_Frame_Tree& snapshot, - std::uint64_t parent_node_id, - const std::string& parent_object_name, - int render_tree_depth, - std::uint64_t cache_parent_node_id, - const std::string& cache_parent_object_name, - int cache_tree_depth) { - std::size_t index = snapshot.size(); - Renderable_Cache_Mode mode = get_cache_mode(); - Renderable_Frame_Node node; - node.renderable = shared_from_this(); - node.subtree_end = index + 1; - node.node_id = node_id_; - node.parent_node_id = parent_node_id; - node.parent_object_name = parent_object_name; - node.render_tree_depth = render_tree_depth; - node.object_name = object_name_; - node.cache_parent_node_id = cache_parent_node_id; - node.cache_parent_object_name = cache_parent_object_name; - node.cache_tree_depth = cache_tree_depth; - bool should_capture_frame_view = d_ptr && should_prepare(); - node.frame_view_required = should_capture_frame_view && d_ptr->requires_frame_view(); - if (should_capture_frame_view) - node.frame_view = d_ptr->capture_frame_view(); - snapshot.push_back(std::move(node)); - std::string child_cache_parent = cache_parent_object_name; - std::uint64_t child_cache_parent_id = cache_parent_node_id; - int child_cache_depth = cache_tree_depth; - if (mode == Renderable_Cache_Mode::Local_Pixel) { - child_cache_parent = object_name_; - child_cache_parent_id = node_id_; - ++child_cache_depth; - } - std::vector> children = children_snapshot(); - for (const auto& child : children) { - if (child) - child->append_tree_snapshot(snapshot, node_id_, object_name_, render_tree_depth + 1, child_cache_parent_id, child_cache_parent, child_cache_depth); - } - snapshot[index].subtree_end = snapshot.size(); -} -std::vector> Renderable::hit_renderables(const PointF& pos) { - std::vector> hits; - append_hit_renderables(hits, pos); - return hits; -} -void Renderable::append_hit_renderables(std::vector>& hits, const PointF& pos) { - if (get_retiring() || !visible_.load(std::memory_order_acquire)) - return; - std::vector> children = children_snapshot(); - for (int i = static_cast(children.size()) - 1; i >= 0; --i) { - const auto& child = children.at(i); - if (child) - child->append_hit_renderables(hits, pos); - } - if (select_test(pos)) - hits.push_back(shared_from_this()); -} -void Renderable::render(Canvas& canvas, const Render_Frame_Snapshot& snapshot) { - if (get_retiring() || !visible_.load(std::memory_order_acquire)) - return; - Renderable_Cache_Mode mode = get_cache_mode(); - std::uint64_t begin_time = steady_now_ns(); - if (mode == Renderable_Cache_Mode::Local_Pixel) - render_cached(canvas, snapshot); - else - render_direct(canvas, snapshot); - if (snapshot.frame_metadata) - snapshot.frame_metadata->record_renderable_render(node_id_, 0, object_name_, render_parent_object_name(), render_tree_depth(), mode, steady_now_ns() - begin_time); -} -void Renderable::render_tree_snapshot(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_Tree& tree, std::size_t index) { - render_tree_node(canvas, snapshot, tree, index); -} -void Renderable::render_direct(Canvas& canvas, const Render_Frame_Snapshot& snapshot) { - render_self(canvas, snapshot); - render_children(canvas, snapshot); -} -void Renderable::render_children(Canvas& canvas, const Render_Frame_Snapshot& snapshot) { - std::vector> children = children_snapshot(); - for (const auto& child : children) { - if (child) - child->render(canvas, snapshot); - } -} -void Renderable::render_cached(Canvas& canvas, const Render_Frame_Snapshot& snapshot) { - Size size = snapshot.size; - Rect bounds = d_ptr ? d_ptr->pixel_bounds(size).intersected(Rect{0, 0, size.width, size.height}) : Rect{0, 0, size.width, size.height}; - if (bounds.empty()) - return; - std::uint64_t edit_version = cache_edit_version.load(std::memory_order_acquire); - std::string parent_object_name = cache_parent_object_name(); - int tree_depth = cache_tree_depth(); - bool ready = cache_ready_version.load(std::memory_order_acquire) == edit_version && cache_image.size() == Size{bounds.width, bounds.height} && cache_bounds.x == bounds.x && cache_bounds.y == bounds.y && cache_bounds.width == bounds.width && cache_bounds.height == bounds.height; - if (ready) { - if (snapshot.frame_metadata) - snapshot.frame_metadata->record_renderable_cache_hit(node_id_, 0, object_name_, parent_object_name, tree_depth); - std::uint64_t begin_time = steady_now_ns(); - canvas.draw_image(RectF{static_cast(bounds.x), static_cast(bounds.y), static_cast(cache_image.width()), static_cast(cache_image.height())}, cache_image); - if (snapshot.frame_metadata) - snapshot.frame_metadata->record_renderable_cache_compose(node_id_, 0, object_name_, parent_object_name, tree_depth, steady_now_ns() - begin_time, static_cast(cache_image.byte_size()), static_cast(bounds.width) * static_cast(bounds.height), edit_version, cache_ready_version.load(std::memory_order_acquire)); - return; - } - if (snapshot.frame_metadata) - snapshot.frame_metadata->record_renderable_cache_miss(node_id_, 0, object_name_, parent_object_name, tree_depth); - std::uint64_t rebuild_begin_time = steady_now_ns(); - render_cache_image(bounds, snapshot, edit_version); - if (snapshot.frame_metadata) - snapshot.frame_metadata->record_renderable_cache_rebuild(node_id_, 0, object_name_, parent_object_name, tree_depth, steady_now_ns() - rebuild_begin_time, cache_image.empty() ? 0 : static_cast(cache_image.byte_size()), static_cast(bounds.width) * static_cast(bounds.height), edit_version, cache_ready_version.load(std::memory_order_acquire)); - if (!cache_image.empty()) { - std::uint64_t begin_time = steady_now_ns(); - canvas.draw_image(RectF{static_cast(bounds.x), static_cast(bounds.y), static_cast(cache_image.width()), static_cast(cache_image.height())}, cache_image); - if (snapshot.frame_metadata) - snapshot.frame_metadata->record_renderable_cache_compose(node_id_, 0, object_name_, parent_object_name, tree_depth, steady_now_ns() - begin_time, static_cast(cache_image.byte_size()), static_cast(bounds.width) * static_cast(bounds.height), edit_version, cache_ready_version.load(std::memory_order_acquire)); - } -} -std::size_t Renderable::render_tree_node(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_Tree& tree, std::size_t index) { - if (index >= tree.size()) - return tree.size(); - const Renderable_Frame_Node& node = tree[index]; - Renderable* current = node.renderable.get(); - if (!current) - return std::max(index + 1, node.subtree_end); - if (current != this) - return current->render_tree_node(canvas, snapshot, tree, index); - if (get_retiring() || !visible_.load(std::memory_order_acquire)) - return std::max(index + 1, node.subtree_end); - Renderable_Cache_Mode mode = get_cache_mode(); - std::uint64_t begin_time = steady_now_ns(); - if (mode == Renderable_Cache_Mode::Local_Pixel) - render_cached_tree_node(canvas, snapshot, tree, index); - else - render_direct_tree_node(canvas, snapshot, tree, index); - if (snapshot.frame_metadata) - snapshot.frame_metadata->record_renderable_render(node.node_id, node.parent_node_id, node.object_name, node.parent_object_name, node.render_tree_depth, mode, steady_now_ns() - begin_time); - return std::max(index + 1, node.subtree_end); -} -void Renderable::render_direct_tree_node(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_Tree& tree, std::size_t index) { - render_self(canvas, snapshot, &tree[index]); - std::size_t child_index = index + 1; - while (child_index < tree[index].subtree_end && child_index < tree.size()) { - auto child = tree[child_index].renderable; - if (!child) { - ++child_index; - continue; - } - child_index = child->render_tree_node(canvas, snapshot, tree, child_index); - } -} -void Renderable::render_cached_tree_node(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_Tree& tree, std::size_t index) { - Size size = snapshot.size; - Rect bounds = d_ptr ? d_ptr->pixel_bounds(size).intersected(Rect{0, 0, size.width, size.height}) : Rect{0, 0, size.width, size.height}; - if (bounds.empty()) - return; - const Renderable_Frame_Node& node = tree[index]; - std::uint64_t edit_version = cache_edit_version.load(std::memory_order_acquire); - bool ready = cache_ready_version.load(std::memory_order_acquire) == edit_version && cache_image.size() == Size{bounds.width, bounds.height} && cache_bounds.x == bounds.x && cache_bounds.y == bounds.y && cache_bounds.width == bounds.width && cache_bounds.height == bounds.height; - if (ready) { - if (snapshot.frame_metadata) - snapshot.frame_metadata->record_renderable_cache_hit(node.node_id, node.cache_parent_node_id, node.object_name, node.cache_parent_object_name, node.cache_tree_depth); - std::uint64_t begin_time = steady_now_ns(); - canvas.draw_image(RectF{static_cast(bounds.x), static_cast(bounds.y), static_cast(cache_image.width()), static_cast(cache_image.height())}, cache_image); - if (snapshot.frame_metadata) - snapshot.frame_metadata->record_renderable_cache_compose(node.node_id, node.cache_parent_node_id, node.object_name, node.cache_parent_object_name, node.cache_tree_depth, steady_now_ns() - begin_time, static_cast(cache_image.byte_size()), static_cast(bounds.width) * static_cast(bounds.height), edit_version, cache_ready_version.load(std::memory_order_acquire)); - return; - } - if (snapshot.frame_metadata) - snapshot.frame_metadata->record_renderable_cache_miss(node.node_id, node.cache_parent_node_id, node.object_name, node.cache_parent_object_name, node.cache_tree_depth); - std::uint64_t rebuild_begin_time = steady_now_ns(); - render_cache_image_tree_node(bounds, snapshot, tree, index, edit_version); - if (snapshot.frame_metadata) - snapshot.frame_metadata->record_renderable_cache_rebuild(node.node_id, node.cache_parent_node_id, node.object_name, node.cache_parent_object_name, node.cache_tree_depth, steady_now_ns() - rebuild_begin_time, cache_image.empty() ? 0 : static_cast(cache_image.byte_size()), static_cast(bounds.width) * static_cast(bounds.height), edit_version, cache_ready_version.load(std::memory_order_acquire)); - if (!cache_image.empty()) { - std::uint64_t begin_time = steady_now_ns(); - canvas.draw_image(RectF{static_cast(bounds.x), static_cast(bounds.y), static_cast(cache_image.width()), static_cast(cache_image.height())}, cache_image); - if (snapshot.frame_metadata) - snapshot.frame_metadata->record_renderable_cache_compose(node.node_id, node.cache_parent_node_id, node.object_name, node.cache_parent_object_name, node.cache_tree_depth, steady_now_ns() - begin_time, static_cast(cache_image.byte_size()), static_cast(bounds.width) * static_cast(bounds.height), edit_version, cache_ready_version.load(std::memory_order_acquire)); - } -} -void Renderable::render_cache_image_tree_node(const Rect& bounds, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_Tree& tree, std::size_t index, std::uint64_t version) { - if (get_retiring()) - return; - Image temp_image; - temp_image.resize(bounds.width, bounds.height); - temp_image.fill(Color::transparent()); - { - Canvas cache_canvas(temp_image); - cache_canvas.translate(static_cast(-bounds.x), static_cast(-bounds.y)); - render_direct_tree_node(cache_canvas, snapshot, tree, index); - } - cache_bounds = bounds; - this->cache_image = std::move(temp_image); - cache_ready_version.store(version, std::memory_order_release); -} -void Renderable::render_cache_image(const Rect& bounds, const Render_Frame_Snapshot& snapshot, std::uint64_t version) { - if (get_retiring()) - return; - Image temp_image; - temp_image.resize(bounds.width, bounds.height); - temp_image.fill(Color::transparent()); - { - Canvas cache_canvas(temp_image); - cache_canvas.translate(static_cast(-bounds.x), static_cast(-bounds.y)); - render_direct(cache_canvas, snapshot); - } - cache_bounds = bounds; - this->cache_image = std::move(temp_image); - cache_ready_version.store(version, std::memory_order_release); -} -void Renderable::render_self(Canvas& canvas, const Render_Frame_Snapshot& snapshot) { - render_self(canvas, snapshot, nullptr); -} -void Renderable::render_self(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_Node* node) { - if (!d_ptr) - return; - Renderable_Frame_View local_frame_view; - const Renderable_Frame_View* frame_view = node ? &node->frame_view : nullptr; - if (!frame_view) { - local_frame_view = d_ptr->capture_frame_view(); - frame_view = &local_frame_view; - } - if (!*frame_view) - return; - Renderable_Cache_Mode mode = get_cache_mode(); - std::uint64_t begin_time = steady_now_ns(); - draw(canvas, snapshot, *frame_view); - std::uint64_t draw_time = steady_now_ns() - begin_time; - if (snapshot.frame_metadata) { - const std::string& parent_name = node ? node->parent_object_name : render_parent_object_name(); - int depth = node ? node->render_tree_depth : render_tree_depth(); - snapshot.frame_metadata->record_renderable_self_draw(node ? node->node_id : node_id_, node ? node->parent_node_id : 0, node ? node->object_name : object_name_, parent_name, depth, mode, draw_time); - } -} -void Renderable::draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) { - if (get_retiring()) - return; - d_ptr->draw(canvas, snapshot, frame_view); -} -void Renderable::prepare_data(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) { - if (get_retiring()) - return; - if (!frame_view) - return; - d_ptr->prepare_data(snapshot, frame_view); -} - -void build_renderable_prepare( - Renderable& renderable, - Render_Task_Subflow& subflow, - const Render_Frame_Snapshot& snapshot, - const Renderable_Frame_View& frame_view) { - if (renderable.get_retiring() || !frame_view || !renderable.d_ptr) - return; - if (auto* source = dynamic_cast(renderable.d_ptr)) { - if (source->build_prepare_subtasks(subflow, snapshot, frame_view)) - return; - } - renderable.prepare_data(snapshot, frame_view); -} - -void drain_renderable_prepare_updates(Renderable& renderable, std::pmr::vector>& output) { - if (renderable.get_retiring() || !renderable.d_ptr) - return; - renderable.d_ptr->drain_committed_update_states(output); -} - -bool Renderable::select_test(const PointF& pos) { - if (get_retiring()) - return false; - auto* hit_testable = dynamic_cast(d_ptr); - return hit_testable && hit_testable->select_test(pos); -} -void Renderable::plot_event(const Event& event) { - if (get_retiring() || event.is_accepted()) - return; - switch (event.type) { - case Event_Type::Resize: - resize_event(static_cast(event)); - break; - case Event_Type::Pointer_Move: - mouse_move_event(static_cast(event)); - break; - case Event_Type::Pointer_Press: - mouse_press_event(static_cast(event)); - break; - case Event_Type::Pointer_Release: - mouse_release_event(static_cast(event)); - break; - case Event_Type::Wheel: - wheel_event(static_cast(event)); - break; - case Event_Type::Key_Press: - key_press_event(static_cast(event)); - break; - case Event_Type::Key_Release: - key_release_event(static_cast(event)); - break; - default: - break; - } -} -void Renderable::mouse_press_event(const Pointer_Event& event) { - if (get_retiring() || event.is_accepted()) - return; - auto* interactive = dynamic_cast(d_ptr); - if (interactive) - interactive->mouse_press_event(event); -} -void Renderable::mouse_move_event(const Pointer_Event& event) { - if (get_retiring() || event.is_accepted()) - return; - auto* interactive = dynamic_cast(d_ptr); - if (interactive) - interactive->mouse_move_event(event); -} -void Renderable::mouse_release_event(const Pointer_Event& event) { - if (get_retiring() || event.is_accepted()) - return; - auto* interactive = dynamic_cast(d_ptr); - if (interactive) - interactive->mouse_release_event(event); -} -void Renderable::set_hover_state(Point pos, bool active) { - if (get_retiring()) - return; - auto* interactive = dynamic_cast(d_ptr); - if (interactive) - interactive->set_hover_state(pos, active); -} -void Renderable::key_press_event(const Key_Event& event) { - if (get_retiring() || event.is_accepted()) - return; - auto* interactive = dynamic_cast(d_ptr); - if (interactive) - interactive->key_press_event(event); -} -void Renderable::key_release_event(const Key_Event& event) { - if (get_retiring() || event.is_accepted()) - return; - auto* interactive = dynamic_cast(d_ptr); - if (interactive) - interactive->key_release_event(event); -} -void Renderable::resize_event(const Resize_Event& event) { - if (get_retiring()) - return; - (void)event; -} -void Renderable::wheel_event(const Wheel_Event& event) { - if (get_retiring() || event.is_accepted()) - return; - auto* interactive = dynamic_cast(d_ptr); - if (interactive) - interactive->wheel_event(event); -} -void Renderable::mark_render_dirty(Dirty_Reason reason) { - if (get_retiring()) - return; - auto context = plot_context.lock(); - if (!context || context->destroying.load(std::memory_order_acquire)) - return; - cache_edit_version.fetch_add(1, std::memory_order_acq_rel); - if (auto parent = parent_renderable.lock()) { - parent->mark_render_dirty(reason); - return; - } - Plot_Core* target_plot = context->owner.load(std::memory_order_acquire); - if (!context->destroying.load(std::memory_order_acquire) && target_plot) - target_plot->notify_model_dirty(reason); -} -bool Renderable::ok() const { - auto context = plot_context.lock(); - return d_ptr && initialized.load(std::memory_order_acquire) && !get_retiring() && context && !context->destroying.load(std::memory_order_acquire); -} -bool Renderable::get_retiring() const { - return retiring.load(std::memory_order_acquire); -} -void Renderable::detach_from_plot_tree() { - bool was_retiring = retiring.exchange(true, std::memory_order_acq_rel); - if (was_retiring && plot == nullptr) - return; - if (d_ptr) - d_ptr->cancel_pending_completions(); - std::vector> detached_children; - { - std::lock_guard lock(children_mutex); - detached_children = std::move(children); - for (const auto& child : detached_children) { - if (child) - child->parent_renderable.reset(); - } - children.clear(); - } - for (const auto& child : detached_children) { - if (child) - child->detach_from_plot_tree(); - } - plot = nullptr; - cache_image.clear(); - cache_bounds = {}; -} -Renderable::~Renderable() { - detach_from_plot_tree(); - d_owner.reset(); - d_ptr = nullptr; -} -} // namespace renderive diff --git a/Core/architecture/Renderable.h b/Core/architecture/Renderable.h deleted file mode 100644 index 1a0b75b..0000000 --- a/Core/architecture/Renderable.h +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once -#include "../base/Geometry.h" -#include "../base/Object_Semantics.h" -#include "../base/Text.h" -#include "../event/Event.h" -#include "../event/Key_Event.h" -#include "../event/Pointer_Event.h" -#include "../event/Resize_Event.h" -#include "../event/Wheel_Event.h" -#include "../render/Canvas.h" -#include "../render/Image.h" -#include "../render/Renderable_Frame_Node.h" -#include "Render_Data.h" -#include "Render_Config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace renderive { -class Plot_Core; -class Renderable; -class Render_Task_Subflow; -struct Plot_Render_Context; -template -struct Renderable_Binding; -template -struct Hover_Tooltip_Mixin; -class Renderable : public std::enable_shared_from_this { -public: - virtual Render_Object_Owner create_render_data() { - return make_render_object(); - } - virtual Render_Object_Owner create_render_state() { - return make_render_object(); - } - virtual Render_Object_Owner create_input_data() { - return {}; - } - virtual void init(const std::shared_ptr& parent); - virtual void init_root(Plot_Core* plot); - void add_child(const std::shared_ptr& child); - void remove_child(const std::shared_ptr& child); - std::vector> children_snapshot() const; - std::vector> hit_renderables(const PointF& pos); - void append_tree_snapshot( - Renderable_Frame_Tree& snapshot, - std::uint64_t parent_node_id = 0, - const std::string& parent_object_name = {}, - int render_tree_depth = 0, - std::uint64_t cache_parent_node_id = 0, - const std::string& cache_parent_object_name = {}, - int cache_tree_depth = 0); - void render(Canvas& canvas, const Render_Frame_Snapshot& snapshot); - void render_tree_snapshot(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_Tree& tree, std::size_t index); - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view); - void prepare_data(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view); - bool select_test(const PointF& pos); - void plot_event(const Event& event); - void mouse_press_event(const Pointer_Event& event); - void mouse_move_event(const Pointer_Event& event); - void mouse_release_event(const Pointer_Event& event); - void set_hover_state(Point pos, bool active); - void key_press_event(const Key_Event& event); - void key_release_event(const Key_Event& event); - void resize_event(const Resize_Event& event); - void wheel_event(const Wheel_Event& event); - Renderable_Cache_Mode get_cache_mode() const; - void set_cache_mode(Renderable_Cache_Mode mode); - [[nodiscard]] bool attached_to_plot() const; - [[nodiscard]] bool shares_plot_with(const Renderable& other) const; - [[nodiscard]] const std::string& object_name() const; - [[nodiscard]] std::uint64_t node_id() const; - void set_object_name(std::string name); - [[nodiscard]] bool is_visible() const; - void set_visible(bool value); - [[nodiscard]] bool should_prepare() const; - void detach_from_parent(); - std::string cache_parent_object_name() const; - int cache_tree_depth() const; - std::string render_parent_object_name() const; - int render_tree_depth() const; - void mark_render_dirty(Dirty_Reason reason = Dirty_Reason::Core_Data); - [[nodiscard]] bool ok() const; - [[nodiscard]] bool get_retiring() const; - virtual ~Renderable(); -private: - friend void build_renderable_prepare(Renderable& renderable, Render_Task_Subflow& subflow, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view); - friend void drain_renderable_prepare_updates(Renderable& renderable, std::pmr::vector>& output); - template - friend struct Renderable_Binding; - template - friend struct Hover_Tooltip_Mixin; - Render_Object_Owner d_owner; - Render_Data* d_ptr{}; - Plot_Core* plot{}; - std::uint64_t node_id_{}; - std::string object_name_; - std::weak_ptr parent_renderable; - std::atomic_bool visible_{true}; - std::vector> children; - mutable std::mutex children_mutex; - std::weak_ptr plot_context; - std::atomic cache_mode{Renderable_Cache_Mode::Direct}; - std::atomic cache_edit_version{1}; - std::atomic cache_ready_version{0}; - Image cache_image; - Rect cache_bounds; - std::atomic_bool retiring{true}; - std::atomic_bool initialized{false}; - void init_common(Plot_Core* plot); - void render_direct(Canvas& canvas, const Render_Frame_Snapshot& snapshot); - void render_cached(Canvas& canvas, const Render_Frame_Snapshot& snapshot); - void render_self(Canvas& canvas, const Render_Frame_Snapshot& snapshot); - void render_children(Canvas& canvas, const Render_Frame_Snapshot& snapshot); - void render_cache_image(const Rect& bounds, const Render_Frame_Snapshot& snapshot, std::uint64_t version); - std::size_t render_tree_node(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_Tree& tree, std::size_t index); - void render_direct_tree_node(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_Tree& tree, std::size_t index); - void render_cached_tree_node(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_Tree& tree, std::size_t index); - void render_cache_image_tree_node(const Rect& bounds, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_Tree& tree, std::size_t index, std::uint64_t version); - void render_self(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_Node* node); - void append_hit_renderables(std::vector>& hits, const PointF& pos); - void detach_from_plot_tree(); -}; -} // namespace renderive diff --git a/Core/architecture/Strategy_Render_Data.cpp b/Core/architecture/Strategy_Render_Data.cpp deleted file mode 100644 index 08c6034..0000000 --- a/Core/architecture/Strategy_Render_Data.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "Strategy_Render_Data.h" -#include - -namespace renderive { - -Strategy_Render_Data::Strategy_Render_Data() = default; -Strategy_Render_Data::~Strategy_Render_Data() = default; - -void Strategy_Render_Data::initialize_runtime(std::unique_ptr value) { - runtime = std::move(value); - if (!runtime) - return; - runtime->set_render_state_copier([this](Render_State& target, const Render_State& source) { - copy_render_state_object(target, source); - }); -} - -void Strategy_Render_Data::cancel_pending_completions() { - if (runtime) - runtime->cancel_pending_completions(); -} - -void Strategy_Render_Data::drain_committed_update_states(std::pmr::vector>& output) { - if (runtime) - runtime->drain_committed_update_states(output); -} - -Render_State_Read_Guard Strategy_Render_Data::acquire_state_read(Renderable_State_Role role) { - return runtime ? runtime->acquire_state_read(role) : Render_State_Read_Guard{}; -} - -Render_State_Read_Guard Strategy_Render_Data::acquire_render_state_read() { - return runtime ? runtime->acquire_render_state_read() : Render_State_Read_Guard{}; -} - -Render_State_Read_Guard Strategy_Render_Data::acquire_edit_state_read() { - return runtime ? runtime->acquire_edit_state_read() : Render_State_Read_Guard{}; -} - -Render_State_Write_Guard Strategy_Render_Data::acquire_edit_state() { - return runtime ? runtime->acquire_edit_state() : Render_State_Write_Guard{}; -} - -Render_Input_Write_Guard Strategy_Render_Data::acquire_edit_input(bool notify_render) { - return runtime ? runtime->acquire_edit_input(notify_render) : Render_Input_Write_Guard{}; -} - -Render_Input_Frame_Guard Strategy_Render_Data::acquire_input_read(Renderable_Input_Role role) { - return runtime ? runtime->acquire_input_read(role) : Render_Input_Frame_Guard{}; -} - -Renderable_Frame_View Strategy_Render_Data::capture_frame_view() { - return runtime ? runtime->capture_frame_view() : Renderable_Frame_View{}; -} - -Render_State* Strategy_Render_Data::state(Renderable_State_Role role) { - return runtime ? runtime->state(role) : nullptr; -} - -Input_Data* Strategy_Render_Data::input_data(Renderable_Input_Role role) { - return runtime ? runtime->input_data(role) : nullptr; -} - -void Strategy_Render_Data::clear_render_input() { - if (runtime) - runtime->clear_render_input(); -} - -bool Strategy_Render_Data::requires_frame_view() const { - return static_cast(runtime); -} - -} // namespace renderive diff --git a/Core/architecture/Strategy_Render_Data.h b/Core/architecture/Strategy_Render_Data.h deleted file mode 100644 index 31ddb9d..0000000 --- a/Core/architecture/Strategy_Render_Data.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once -#include "Render_Data.h" -#include "global.h" -#include -#include -#include -#include -#include - -namespace renderive { - -struct LIB_DECL Strategy_Render_Data : Render_Data { - Strategy_Render_Data(); - Strategy_Render_Data(const Strategy_Render_Data&) = delete; - Strategy_Render_Data& operator=(const Strategy_Render_Data&) = delete; - ~Strategy_Render_Data() override; - - void initialize_runtime(std::unique_ptr runtime) override; - void cancel_pending_completions() override; - void drain_committed_update_states(std::pmr::vector>& output) override; - Render_State_Read_Guard acquire_state_read(Renderable_State_Role role) override; - Render_State_Read_Guard acquire_render_state_read() override; - Render_State_Read_Guard acquire_edit_state_read() override; - Render_State_Write_Guard acquire_edit_state() override; - Render_Input_Write_Guard acquire_edit_input(bool notify_render) override; - Render_Input_Frame_Guard acquire_input_read(Renderable_Input_Role role) override; - Renderable_Frame_View capture_frame_view() override; - Render_State* state(Renderable_State_Role role) override; - Input_Data* input_data(Renderable_Input_Role role) override; - void clear_render_input(); - [[nodiscard]] bool requires_frame_view() const override; - -private: - std::unique_ptr runtime; -}; - -} // namespace renderive diff --git a/Core/architecture/Timeline_Stream.cpp b/Core/architecture/Timeline_Stream.cpp deleted file mode 100644 index d41ef09..0000000 --- a/Core/architecture/Timeline_Stream.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "Timeline_Stream.h" -#include -#include -#include -namespace renderive { -int Timeline_Stream_Snapshot::coordinate_end() const { - return coordinate_begin + visible_time_point_count - 1; -} -bool Timeline_Stream_Snapshot::coord_by_stream_tick(int stream_tick, int& coord) const { - if (times.empty()) - return false; - if (stream_tick < first_stream_tick || stream_tick > last_stream_tick) - return false; - int count = static_cast(times.size()); - if (newest_at_axis_start) - coord = coordinate_begin + (last_stream_tick - stream_tick); - else - coord = coordinate_begin + (visible_time_point_count - count) + (stream_tick - first_stream_tick); - return coord >= coordinate_begin && coord <= coordinate_end(); -} -Time_Of_Day Timeline_Stream_Snapshot::time_by_tick(int tick) const { - int offset = tick - coordinate_begin; - int count = static_cast(times.size()); - int index = newest_at_axis_start ? count - 1 - offset : offset - (visible_time_point_count - count); - if (index < 0 || index >= count) - return {}; - return times[static_cast(index)]; -} -Timeline_Stream::Timeline_Stream() = default; -int Timeline_Stream::append_time(Time_Of_Day time) { - int tick = next_tick_value.fetch_add(1, std::memory_order_acq_rel); - if (tick == std::numeric_limits::max()) { - int expected = std::numeric_limits::min(); - next_tick_value.compare_exchange_strong(expected, 1, std::memory_order_acq_rel, std::memory_order_acquire); - tick = 0; - } - { - std::lock_guard lock(samples_mutex); - if (samples.size() >= Max_Buffered_Time_Count) - samples.pop_front(); - samples.push_back(Sample{tick, time}); - version_value.fetch_add(1, std::memory_order_release); - } - return tick; -} -Timeline_Stream_Snapshot Timeline_Stream::capture(int time_point_size, int label_tick_interval, bool start_coord_to_end_coord) const { - time_point_size = std::max(1, time_point_size); - label_tick_interval = std::max(1, label_tick_interval); - Timeline_Stream_Snapshot snapshot; - std::pmr::vector captured; - { - std::lock_guard lock(samples_mutex); - std::uint64_t version = version_value.load(std::memory_order_acquire); - snapshot.version = version + 1; - std::size_t count = std::min(static_cast(time_point_size), samples.size()); - captured.reserve(count); - auto first = samples.end() - static_cast(count); - for (auto it = first; it != samples.end(); ++it) - captured.push_back(*it); - } - snapshot.newest_at_axis_start = start_coord_to_end_coord; - snapshot.visible_time_point_count = time_point_size; - if (captured.empty()) { - snapshot.coordinate_begin = 0; - return snapshot; - } - snapshot.first_stream_tick = captured.front().stream_tick; - snapshot.last_stream_tick = captured.back().stream_tick; - snapshot.coordinate_begin = snapshot.last_stream_tick - time_point_size + 1; - snapshot.times.reserve(captured.size()); - for (const auto& sample : captured) - snapshot.times.push_back(sample.time); - snapshot.ticks.reserve(captured.size() / static_cast(label_tick_interval) + 1); - for (const auto& sample : captured) { - int coord{}; - if ((label_tick_interval == 1 || sample.stream_tick % label_tick_interval == 0) && snapshot.coord_by_stream_tick(sample.stream_tick, coord)) - snapshot.ticks.push_back({sample.time, coord}); - } - std::sort(snapshot.ticks.begin(), snapshot.ticks.end(), [](const Timeline_Label_Tick& left, const Timeline_Label_Tick& right) { - return left.coordinate < right.coordinate; - }); - return snapshot; -} -} diff --git a/Core/architecture/Timeline_Stream.h b/Core/architecture/Timeline_Stream.h deleted file mode 100644 index fddbb63..0000000 --- a/Core/architecture/Timeline_Stream.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include "../base/Time_Of_Day.h" -#include "global.h" -namespace renderive { -struct Timeline_Label_Tick { - Time_Of_Day time; - int coordinate{}; -}; -struct LIB_DECL Timeline_Stream_Snapshot { - std::uint64_t version{}; - bool newest_at_axis_start = false; // 当前 true 时 代表最新时间映射到起始坐标 - int coordinate_begin{}; - int visible_time_point_count = 1; - int first_stream_tick{}; - int last_stream_tick = -1; - std::vector times; - std::vector ticks; - [[nodiscard]] int coordinate_end() const; - [[nodiscard]] bool coord_by_stream_tick(int stream_tick, int& coord) const; - [[nodiscard]] Time_Of_Day time_by_tick(int tick) const; -}; -class LIB_DECL Timeline_Stream { -public: - Timeline_Stream(); - int append_time(Time_Of_Day time); - [[nodiscard]] Timeline_Stream_Snapshot capture(int time_point_size, int label_tick_interval, bool start_coord_to_end_coord) const; -private: - struct Sample { - int stream_tick{}; - Time_Of_Day time; - }; - static constexpr std::size_t Max_Buffered_Time_Count = 65536; - mutable std::mutex samples_mutex; - std::deque samples; - std::atomic version_value{0}; - std::atomic next_tick_value{0}; -}; -} diff --git a/Core/architecture/Update_Completion.cpp b/Core/architecture/Update_Completion.cpp deleted file mode 100644 index 78bff80..0000000 --- a/Core/architecture/Update_Completion.cpp +++ /dev/null @@ -1,126 +0,0 @@ -#include "Update_Completion.h" -#include -namespace renderive { -namespace { -std::atomic next_update_id{1}; -int stage_index(Update_Stage stage) { - switch (stage) { - case Update_Stage::Input_Released: - return 0; - case Update_Stage::Committed: - return 1; - case Update_Stage::Rendered: - return 2; - case Update_Stage::Presented: - return 3; - } - ASSERT(false, "Invalid Update_Stage"); - return 3; -} -} -Update_State::Stage_Record::Stage_Record(std::pmr::memory_resource* resource) : waiters(resource) {} -Update_State::Update_State(Update_Id id) : update_id(id), input_released(memory_resource(Memory_Domain::Update_Completion)), committed(memory_resource(Memory_Domain::Update_Completion)), rendered(memory_resource(Memory_Domain::Update_Completion)), presented(memory_resource(Memory_Domain::Update_Completion)) {} -Update_Id Update_State::id() const noexcept { - return update_id; -} -bool Update_State::is_complete() const noexcept { - return terminal.load(std::memory_order_acquire); -} -bool Update_State::stage_complete(Update_Stage stage) const noexcept { - std::lock_guard lock(mutex); - return slot(stage).completed; -} -Update_State::Stage_Record& Update_State::slot(Update_Stage stage) { - switch (stage) { - case Update_Stage::Input_Released: - return input_released; - case Update_Stage::Committed: - return committed; - case Update_Stage::Rendered: - return rendered; - case Update_Stage::Presented: - return presented; - } - ASSERT(false, "Invalid Update_Stage"); - return presented; -} -const Update_State::Stage_Record& Update_State::slot(Update_Stage stage) const { - return const_cast(this)->slot(stage); -} -void Update_State::register_waiter(Update_Stage stage, Update_Waiter waiter) { - std::error_code error; - Update_Outcome outcome = Update_Outcome::Cancelled; - bool ready = false; - { - std::lock_guard lock(mutex); - Stage_Record& record = slot(stage); - if (record.completed) { - error = record.error; - outcome = record.outcome; - ready = true; - } - else { - record.waiters.push_back(std::move(waiter)); - } - } - if (ready) - waiter.complete(error, outcome); -} -void Update_State::drain_completed_waiters(Stage_Record& record, std::pmr::vector& waiters, std::error_code error, Update_Outcome outcome) { - if (record.completed) - return; - record.completed = true; - record.error = error; - record.outcome = outcome; - for (auto& waiter : record.waiters) - waiters.push_back(std::move(waiter)); - record.waiters.clear(); -} -void Update_State::complete_until(Update_Stage stage, std::error_code error, Update_Outcome outcome) { - std::pmr::vector waiters(memory_resource(Memory_Domain::Update_Completion)); - { - std::lock_guard lock(mutex); - int last = std::min(stage_index(stage), stage_index(Update_Stage::Presented)); - if (last >= stage_index(Update_Stage::Input_Released)) - drain_completed_waiters(input_released, waiters, error, outcome); - if (last >= stage_index(Update_Stage::Committed)) - drain_completed_waiters(committed, waiters, error, outcome); - if (last >= stage_index(Update_Stage::Rendered)) - drain_completed_waiters(rendered, waiters, error, outcome); - if (last >= stage_index(Update_Stage::Presented)) { - drain_completed_waiters(presented, waiters, error, outcome); - terminal.store(true, std::memory_order_release); - } - } - for (auto& waiter : waiters) - waiter.complete(error, outcome); -} -void Update_State::complete_all(std::error_code error, Update_Outcome outcome) { - complete_until(Update_Stage::Presented, error, outcome); -} -std::shared_ptr make_update_state() { - std::pmr::polymorphic_allocator allocator(memory_resource(Memory_Domain::Update_Completion)); - return std::allocate_shared(allocator, next_update_id.fetch_add(1, std::memory_order_relaxed)); -} -void complete_update_states(std::pmr::vector>& states, Update_Stage stage, std::error_code error, Update_Outcome outcome) { - for (auto& state : states) { - if (state) - state->complete_until(stage, error, outcome); - } - states.clear(); -} -void cancel_update_states(std::pmr::vector>& states) { - for (auto& state : states) { - if (state) - state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - } - states.clear(); -} -void supersede_update_states(std::pmr::vector>& states) { - for (auto& state : states) { - if (state) - state->complete_all({}, Update_Outcome::Superseded); - } - states.clear(); -} -} diff --git a/Core/architecture/Update_Completion.h b/Core/architecture/Update_Completion.h deleted file mode 100644 index 5bf7907..0000000 --- a/Core/architecture/Update_Completion.h +++ /dev/null @@ -1,181 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../base/Memory.h" -#include "../base/Object_Semantics.h" -#include "../base/Task.h" -#include "asio.hpp" -namespace renderive { -/// @brief Lifecycle stage that an asynchronous update ticket can wait for. -enum class Update_Stage : std::uint8_t { - /// @brief Input storage no longer references caller-owned data. - Input_Released, - /// @brief The update has been committed to a version visible to the render pipeline. - Committed, - /// @brief The update has participated in a completed CPU render pass. - Rendered, - /// @brief A frame containing the update has been presented by the frontend. - Presented -}; -/// @brief Final result reported when an update wait operation completes. -enum class Update_Outcome : std::uint8_t { - /// @brief The requested lifecycle stage completed normally. - Completed, - /// @brief The update was replaced by newer data before completing all stages. - Superseded, - /// @brief The update was stopped by cancellation, destruction, or shutdown. - Cancelled -}; -using Update_Id = std::uint64_t; -class Update_Waiter : Move_Only { -public: - Update_Waiter() noexcept = default; - template - explicit Update_Waiter(Handler&& handler) { - using Model = Waiter_Model>; - resource = memory_resource(Memory_Domain::Update_Completion); - std::pmr::polymorphic_allocator allocator(resource); - object = allocator.allocate(1); - try { - std::construct_at(static_cast(object), std::forward(handler)); - } - catch (...) { - allocator.deallocate(static_cast(object), 1); - object = nullptr; - resource = nullptr; - throw; - } - complete_function = &complete_model; - destroy_function = &destroy_model; - } - Update_Waiter(Update_Waiter&& other) noexcept { - move_from(std::move(other)); - } - Update_Waiter& operator=(Update_Waiter&& other) noexcept { - if (this == &other) - return *this; - reset(); - move_from(std::move(other)); - return *this; - } - ~Update_Waiter() { - reset(); - } - void complete(std::error_code error, Update_Outcome outcome) { - if (complete_function) - complete_function(object, error, outcome); - } - explicit operator bool() const noexcept { - return object != nullptr; - } -private: - template - struct Waiter_Model { - using Executor = asio::associated_executor_t; - template - explicit Waiter_Model(Source&& value) : handler(std::forward(value)), executor(asio::get_associated_executor(handler, asio::system_executor())) {} - Handler handler; - Executor executor; - }; - template - static void complete_model(void* object, std::error_code error, Update_Outcome outcome) { - auto& model = *static_cast(object); - auto handler = std::move(model.handler); - auto executor = model.executor; - asio::post(executor, [handler = std::move(handler), error, outcome]() mutable { - handler(error, outcome); - }); - } - template - static void destroy_model(void* object, std::pmr::memory_resource* resource) { - std::pmr::polymorphic_allocator allocator(resource); - std::destroy_at(static_cast(object)); - allocator.deallocate(static_cast(object), 1); - } - void move_from(Update_Waiter&& other) noexcept { - object = std::exchange(other.object, nullptr); - resource = std::exchange(other.resource, nullptr); - complete_function = std::exchange(other.complete_function, nullptr); - destroy_function = std::exchange(other.destroy_function, nullptr); - } - void reset() noexcept { - if (!object) - return; - destroy_function(object, resource); - object = nullptr; - resource = nullptr; - complete_function = nullptr; - destroy_function = nullptr; - } - void* object{}; - std::pmr::memory_resource* resource{}; - void (*complete_function)(void*, std::error_code, Update_Outcome){}; - void (*destroy_function)(void*, std::pmr::memory_resource*){}; -}; -struct LIB_DECL Update_State { - explicit Update_State(Update_Id id); - Update_Id id() const noexcept; - bool is_complete() const noexcept; - bool stage_complete(Update_Stage stage) const noexcept; - void register_waiter(Update_Stage stage, Update_Waiter waiter); - void complete_until(Update_Stage stage, std::error_code error = {}, Update_Outcome outcome = Update_Outcome::Completed); - void complete_all(std::error_code error = {}, Update_Outcome outcome = Update_Outcome::Completed); -private: - struct Stage_Record { - explicit Stage_Record(std::pmr::memory_resource* resource); - bool completed = false; - std::error_code error; - Update_Outcome outcome = Update_Outcome::Cancelled; - std::pmr::vector waiters; - }; - Stage_Record& slot(Update_Stage stage); - const Stage_Record& slot(Update_Stage stage) const; - void drain_completed_waiters(Stage_Record& record, std::pmr::vector& waiters, std::error_code error, Update_Outcome outcome); - Update_Id update_id{}; - mutable std::mutex mutex; - Stage_Record input_released; - Stage_Record committed; - Stage_Record rendered; - Stage_Record presented; - std::atomic_bool terminal{}; -}; -class Update_Ticket { -public: - Update_Ticket() noexcept = default; - explicit Update_Ticket(std::shared_ptr state) : state_(std::move(state)) {} - Update_Id id() const noexcept { - return state_ ? state_->id() : 0; - } - explicit operator bool() const noexcept { - return static_cast(state_); - } - bool is_complete() const noexcept { - return state_ && state_->is_complete(); - } - template - auto async_wait(Update_Stage stage, CompletionToken&& token) { - return asio::async_initiate( - [state = state_, stage](auto handler) mutable { - if (!state) { - Update_Waiter waiter(std::move(handler)); - waiter.complete(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - return; - } - state->register_waiter(stage, Update_Waiter(std::move(handler))); - }, token); - } -private: - std::shared_ptr state_; -}; -LIB_DECL std::shared_ptr make_update_state(); -LIB_DECL void complete_update_states(std::pmr::vector>& states, Update_Stage stage, std::error_code error = {}, Update_Outcome outcome = Update_Outcome::Completed); -LIB_DECL void cancel_update_states(std::pmr::vector>& states); -LIB_DECL void supersede_update_states(std::pmr::vector>& states); -} diff --git a/Core/architecture/export.h b/Core/architecture/export.h deleted file mode 100644 index b580486..0000000 --- a/Core/architecture/export.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include "Render_Config.h" -#include "Renderable.h" -#include "Timeline_Stream.h" -#include "Update_Completion.h" -#include "global.h" diff --git a/Core/architecture/global.h b/Core/architecture/global.h deleted file mode 100644 index 0e0c8cd..0000000 --- a/Core/architecture/global.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include "../base/Assert.h" -#include "../base/Color.h" -#include "../base/Export.h" -#include "../base/Geometry.h" -namespace renderive { -std::vector get_turbo_color_gradient(); -} -#define LIB_DECL RENDERIVE_API -#define PROP(Type, Name) \ - Type Name(); \ - void set_##Name(Type); -#define RENDERIVE_DEFINE_STATE_PROP(Owner, State, Type, Field) \ - Type Owner::Field() { \ - return d()->edit_state_value(&State::Field); \ - } \ - void Owner::set_##Field(Type value) { \ - d()->set_state_value(&State::Field, std::move(value)); \ - } -#define RENDERIVE_DEFINE_WEAK_STATE_PROP(Owner, State, Pointee, Field) \ - std::shared_ptr Owner::Field() { \ - return d()->edit_state_value(&State::Field).lock(); \ - } \ - void Owner::set_##Field(std::shared_ptr value) { \ - d()->set_state_value(&State::Field, std::weak_ptr(value)); \ - } -#define PROP_B(TYPE, FIELD) \ -public: \ - Builder& set_##FIELD(TYPE value) { \ - this->FIELD = std::move(value); \ - return *this; \ - } -#define PROP_BT(TYPE, FIELD) \ -public: \ - That& set_##FIELD(TYPE value) { \ - static_cast(this)->FIELD = std::move(value); \ - return *static_cast(this); \ - } -#define PROP_R(TYPE, FIELD) sc->FIELD = this->FIELD; -#define PROP_RT(TYPE, FIELD) sc->FIELD = static_cast(this)->FIELD; -#define SETTER(TYPE, FIELD, DEFAULT) \ - \ - TYPE FIELD = DEFAULT; \ -public: \ - Builder& set_##FIELD(TYPE value) { \ - this->FIELD = std::move(value); \ - return *this; \ - } -#undef min -#undef max diff --git a/Core/base/Assert.h b/Core/base/Assert.h deleted file mode 100644 index b7a991f..0000000 --- a/Core/base/Assert.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include -#include - -#ifdef NDEBUG -# define RENDERIVE_ASSERT(condition, message) ((void)0) -#else -# define RENDERIVE_ASSERT(condition, message) \ - do { \ - if (!(condition)) { \ - std::cerr << "Assertion failed: " << message \ - << "\nFile: " << __FILE__ \ - << "\nLine: " << __LINE__ \ - << std::endl; \ - throw std::runtime_error(message); \ - } \ - } \ - while (0) -#endif - -#ifndef ASSERT -# define ASSERT(condition, message) RENDERIVE_ASSERT(condition, message) -#endif diff --git a/Core/base/Bitmask.h b/Core/base/Bitmask.h deleted file mode 100644 index 252cc35..0000000 --- a/Core/base/Bitmask.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -#include - -template -struct enable_bitmask : std::false_type {}; - -#define RENDERIVE_ENABLE_BITMASK(E) \ - template <> \ - struct enable_bitmask : std::true_type {}; - -template -constexpr std::enable_if_t::value, E> operator|(E lhs, E rhs) { - using U = std::underlying_type_t; - return static_cast(static_cast(lhs) | static_cast(rhs)); -} - -template -constexpr std::enable_if_t::value, E> operator&(E lhs, E rhs) { - using U = std::underlying_type_t; - return static_cast(static_cast(lhs) & static_cast(rhs)); -} - -template -constexpr std::enable_if_t::value, E> operator^(E lhs, E rhs) { - using U = std::underlying_type_t; - return static_cast(static_cast(lhs) ^ static_cast(rhs)); -} - -template -constexpr std::enable_if_t::value, E> operator~(E e) { - using U = std::underlying_type_t; - return static_cast(~static_cast(e)); -} - -template -constexpr std::enable_if_t::value, E&> operator|=(E& lhs, E rhs) { - return lhs = lhs | rhs; -} - -template -constexpr std::enable_if_t::value, E&> operator&=(E& lhs, E rhs) { - return lhs = lhs & rhs; -} diff --git a/Core/base/Color.h b/Core/base/Color.h deleted file mode 100644 index b840d53..0000000 --- a/Core/base/Color.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once -#include -namespace renderive { -struct Color { - std::uint8_t r{}; - std::uint8_t g{}; - std::uint8_t b{}; - std::uint8_t a{255}; - static constexpr Color transparent() { - return {0, 0, 0, 0}; - } - static constexpr Color black() { - return {0, 0, 0, 255}; - } - static constexpr Color white() { - return {255, 255, 255, 255}; - } - static constexpr Color red() { - return {255, 0, 0, 255}; - } - static constexpr Color green() { - return {0, 255, 0, 255}; - } - static constexpr Color yellow() { - return {255, 255, 0, 255}; - } - bool operator==(const Color& other) const { - return r == other.r && g == other.g && b == other.b && a == other.a; - } - bool operator!=(const Color& other) const { - return !(*this == other); - } -}; -using Pixel = std::uint32_t; -constexpr Pixel premultiply(Color color) { - auto r = static_cast((color.r * color.a + 127) / 255); - auto g = static_cast((color.g * color.a + 127) / 255); - auto b = static_cast((color.b * color.a + 127) / 255); - return (static_cast(color.a) << 24) | (r << 16) | (g << 8) | b; -} -constexpr Pixel pack_rgba(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a = 255) { - return (static_cast(a) << 24) | (static_cast(r) << 16) | (static_cast(g) << 8) | static_cast(b); -} -constexpr Color premultiplied_to_color(Pixel pixel) { - auto a = static_cast(pixel >> 24); - if (a == 0) - return {0, 0, 0, 0}; - auto r = static_cast((pixel >> 16) & 0xFF); - auto g = static_cast((pixel >> 8) & 0xFF); - auto b = static_cast(pixel & 0xFF); - return { - static_cast((r * 255 + a / 2) / a), - static_cast((g * 255 + a / 2) / a), - static_cast((b * 255 + a / 2) / a), - a - }; -} -} // namespace renderive diff --git a/Core/base/Export.h b/Core/base/Export.h deleted file mode 100644 index 2b9f0bb..0000000 --- a/Core/base/Export.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#if defined(_WIN32) -# if defined(RENDERIVE_BUILD_SHARED) -# define RENDERIVE_API __declspec(dllexport) -# elif defined(RENDERIVE_USE_SHARED) -# define RENDERIVE_API __declspec(dllimport) -# else -# define RENDERIVE_API -# endif -#else -# define RENDERIVE_API __attribute__((visibility("default"))) -#endif diff --git a/Core/base/Frame_Memory.cpp b/Core/base/Frame_Memory.cpp deleted file mode 100644 index 08d0ca5..0000000 --- a/Core/base/Frame_Memory.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "Frame_Memory.h" -#include "Memory.h" -#include -#include -#include -namespace renderive { -namespace { -constexpr std::size_t Frame_Arena_Min_Size = 64 * 1024; -constexpr std::size_t Frame_Arena_Max_Retained_Size = 64 * 1024 * 1024; -} -struct Thread_Frame_Scratch; -class Frame_Overflow_Resource final : public std::pmr::memory_resource { -public: - explicit Frame_Overflow_Resource(Thread_Frame_Scratch* scratch) : scratch(scratch) {} -private: - void* do_allocate(std::size_t size, std::size_t alignment) override; - void do_deallocate(void* address, std::size_t size, std::size_t alignment) override; - bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { - return this == &other; - } - Thread_Frame_Scratch* scratch{}; -}; -struct Thread_Frame_Scratch { - Thread_Frame_Scratch() { - storage.resize(Frame_Arena_Min_Size); - desired_size = Frame_Arena_Min_Size; - } - std::pmr::memory_resource* overflow_upstream() { - if (!overflow_pool) - overflow_pool = std::make_unique(memory_resource(Memory_Domain::Frame_Arena)); - return overflow_pool.get(); - } - void begin_frame(std::size_t initial_buffer_size) { - frame_overflow_count = 0; - frame_overflow_bytes = 0; - std::size_t target_size = std::max(initial_buffer_size, desired_size); - target_size = std::max(target_size, Frame_Arena_Min_Size); - target_size = std::min(target_size, Frame_Arena_Max_Retained_Size); - if (storage.size() < target_size) { - storage.resize(target_size); - } - } - void finish_frame() { - std::uint64_t frame_bytes = static_cast(storage.size() + frame_overflow_bytes); -#if RENDERIVE_FRAME_ARENA_GROW - if (frame_overflow_bytes) { - std::size_t next_size = static_cast(frame_bytes + frame_bytes / 8 + 4096); - desired_size = std::clamp(next_size, Frame_Arena_Min_Size, Frame_Arena_Max_Retained_Size); - } -#endif - frame_overflow_count = 0; - frame_overflow_bytes = 0; - } - std::vector storage; - std::unique_ptr overflow_pool; - Frame_Overflow_Resource overflow_resource{this}; - std::size_t desired_size{}; - std::size_t frame_overflow_count{}; - std::size_t frame_overflow_bytes{}; - int depth{}; -}; -void* Frame_Overflow_Resource::do_allocate(std::size_t size, std::size_t alignment) { - scratch->frame_overflow_count += 1; - scratch->frame_overflow_bytes += size; - return scratch->overflow_upstream()->allocate(size, alignment); -} -void Frame_Overflow_Resource::do_deallocate(void* address, std::size_t size, std::size_t alignment) { - scratch->overflow_upstream()->deallocate(address, size, alignment); -} -namespace { -thread_local std::pmr::memory_resource* current_frame_resource{}; -Thread_Frame_Scratch& thread_frame_scratch() { - thread_local Thread_Frame_Scratch value; - return value; -} -} -Frame_Arena::Frame_Arena(std::size_t initial_buffer_size) { - scratch = &thread_frame_scratch(); - previous = current_frame_resource; - if (scratch->depth == 0) { - scratch->begin_frame(initial_buffer_size); - arena.emplace(scratch->storage.data(), scratch->storage.size(), &scratch->overflow_resource); - } - else { - arena.emplace(&scratch->overflow_resource); - } - ++scratch->depth; - current_frame_resource = resource(); -} -Frame_Arena::~Frame_Arena() { - current_frame_resource = previous; - bool outer = scratch && scratch->depth == 1; - arena.reset(); - if (outer) - scratch->finish_frame(); - --scratch->depth; -} -std::pmr::memory_resource* Frame_Arena::resource() noexcept { - return arena ? &*arena : memory_resource(); -} -std::pmr::memory_resource* frame_memory_resource() { - return current_frame_resource ? current_frame_resource : memory_resource(); -} -} diff --git a/Core/base/Frame_Memory.h b/Core/base/Frame_Memory.h deleted file mode 100644 index 2e5180a..0000000 --- a/Core/base/Frame_Memory.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include "../architecture/global.h" -#include "Object_Semantics.h" -#ifndef RENDERIVE_FRAME_ARENA_GROW -#define RENDERIVE_FRAME_ARENA_GROW 1 -#endif -namespace renderive { -struct Thread_Frame_Scratch; -class LIB_DECL Frame_Arena : Immovable { -public: - explicit Frame_Arena(std::size_t initial_buffer_size = 64 * 1024); - ~Frame_Arena(); - std::pmr::memory_resource* resource() noexcept; -private: - Thread_Frame_Scratch* scratch{}; - std::pmr::memory_resource* previous{}; - std::optional arena; -}; -LIB_DECL std::pmr::memory_resource* frame_memory_resource(); -} diff --git a/Core/base/Geometry.h b/Core/base/Geometry.h deleted file mode 100644 index 18d4b5f..0000000 --- a/Core/base/Geometry.h +++ /dev/null @@ -1,135 +0,0 @@ -#pragma once -#include -#include -#include -namespace renderive { -struct Point { - int x{}; - int y{}; -}; -struct PointF { - double x{}; - double y{}; -}; -struct Size { - int width{}; - int height{}; - [[nodiscard]] bool empty() const { - return width <= 0 || height <= 0; - } - bool operator==(const Size& other) const { - return width == other.width && height == other.height; - } - bool operator!=(const Size& other) const { - return !(*this == other); - } -}; -struct Rect { - int x{}; - int y{}; - int width{}; - int height{}; - [[nodiscard]] bool empty() const { - return width <= 0 || height <= 0; - } - [[nodiscard]] int right() const { - return x + width; - } - [[nodiscard]] int bottom() const { - return y + height; - } - [[nodiscard]] Point top_left() const { - return {x, y}; - } - [[nodiscard]] bool contains(Point point) const { - return point.x >= x && point.x <= right() && point.y >= y && point.y <= bottom(); - } - [[nodiscard]] Rect intersected(const Rect& other) const { - int r = std::min(right(), other.right()); - int b = std::min(bottom(), other.bottom()); - int nx = std::max(x, other.x); - int ny = std::max(y, other.y); - int w = r - nx; - int h = b - ny; - if (w <= 0 || h <= 0) - return {}; - return {nx, ny, w, h}; - } -}; -struct RectF { - double x{}; - double y{}; - double width{}; - double height{}; - [[nodiscard]] bool empty() const { - return width <= 0.0 || height <= 0.0; - } - [[nodiscard]] double right() const { - return x + width; - } - [[nodiscard]] double bottom() const { - return y + height; - } - [[nodiscard]] PointF top_left() const { - return {x, y}; - } - [[nodiscard]] bool contains(PointF point) const { - return point.x >= x && point.x <= right() && point.y >= y && point.y <= bottom(); - } - [[nodiscard]] RectF normalized() const { - double nx = x; - double ny = y; - double nw = width; - double nh = height; - if (nw < 0.0) { - nx += nw; - nw = -nw; - } - if (nh < 0.0) { - ny += nh; - nh = -nh; - } - return {nx, ny, nw, nh}; - } - [[nodiscard]] RectF intersected(const RectF& other) const { - double r = std::min(right(), other.right()); - double b = std::min(bottom(), other.bottom()); - double nx = std::max(x, other.x); - double ny = std::max(y, other.y); - double w = r - nx; - double h = b - ny; - if (w <= 0.0 || h <= 0.0) - return {}; - return {nx, ny, w, h}; - } -}; -enum class Orientation : std::uint8_t { - Horizontal, - Vertical -}; -struct Range { - double origin; - double target; - [[nodiscard]] double size() const { - return target > origin ? target - origin : origin - target; - } - [[nodiscard]] double length() const { - return target - origin; - } - [[nodiscard]] double center() const { - return origin + (target - origin) / 2.0; - } - bool operator==(const Range& other) const { - return origin == other.origin && target == other.target; - } - bool operator!=(const Range& other) const { - return !(*this == other); - } - [[nodiscard]] bool contains(double value) const { - if (origin < target) - return value > origin - 0.0001 && value < target + 0.0001; - return value > target - 0.0001 && value < origin + 0.0001; - } -}; -std::ostream& operator<<(std::ostream& stream, const Range& range); -} // namespace renderive diff --git a/Core/base/Memory.cpp b/Core/base/Memory.cpp deleted file mode 100644 index 64a6119..0000000 --- a/Core/base/Memory.cpp +++ /dev/null @@ -1,211 +0,0 @@ -#include "Memory.h" -#include -#include -#include -#include -#include -#include -namespace renderive { -namespace { -constexpr std::size_t memory_domain_count = static_cast(Memory_Domain::Other) + 1; - -std::size_t memory_domain_index(Memory_Domain domain) noexcept { - std::size_t index = static_cast(domain); - return index < memory_domain_count ? index : static_cast(Memory_Domain::Other); -} - -void* default_allocate(void*, std::size_t size, std::size_t alignment) { - return std::pmr::new_delete_resource()->allocate(size, alignment); -} -void default_deallocate(void*, void* address, std::size_t size, std::size_t alignment) { - std::pmr::new_delete_resource()->deallocate(address, size, alignment); -} -class Callback_Memory_Resource final : public std::pmr::memory_resource { -public: - Callback_Memory_Resource() : callbacks{nullptr, default_allocate, default_deallocate} {} - void set_callbacks(const Memory_Callbacks& value) { - callbacks = value.allocate ? value : Memory_Callbacks{nullptr, default_allocate, default_deallocate}; - } - Memory_Stats stats() const noexcept { - Memory_Stats result; - result.upstream_allocation_count = allocation_count.load(std::memory_order_relaxed); - result.upstream_deallocation_count = deallocation_count.load(std::memory_order_relaxed); - result.upstream_current_bytes = current_bytes.load(std::memory_order_relaxed); - result.upstream_peak_bytes = peak_bytes.load(std::memory_order_relaxed); - return result; - } -private: - void* do_allocate(std::size_t size, std::size_t alignment) override { - void* result = callbacks.allocate(callbacks.context, size, alignment); - if (!result) - throw std::bad_alloc(); - allocation_count.fetch_add(1, std::memory_order_relaxed); - std::uint64_t current = current_bytes.fetch_add(size, std::memory_order_relaxed) + size; - std::uint64_t peak = peak_bytes.load(std::memory_order_relaxed); - while (peak < current && !peak_bytes.compare_exchange_weak(peak, current, std::memory_order_relaxed)) {} - return result; - } - void do_deallocate(void* address, std::size_t size, std::size_t alignment) override { - callbacks.deallocate(callbacks.context, address, size, alignment); - deallocation_count.fetch_add(1, std::memory_order_relaxed); - current_bytes.fetch_sub(size, std::memory_order_relaxed); - } - bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { - return this == &other; - } - Memory_Callbacks callbacks; - std::atomic allocation_count{}; - std::atomic deallocation_count{}; - std::atomic current_bytes{}; - std::atomic peak_bytes{}; -}; -class alignas(64) Domain_Memory_Resource final : public std::pmr::memory_resource { -public: - void set_domain(Memory_Domain value) noexcept { - domain = value; - } - Memory_Domain_Stats stats() const noexcept { - Memory_Domain_Stats result; -#if RENDERIVE_MEMORY_DOMAIN_STATS - result.allocation_count = allocation_count.load(std::memory_order_relaxed); - result.deallocation_count = deallocation_count.load(std::memory_order_relaxed); - result.current_bytes = current_bytes.load(std::memory_order_relaxed); - result.peak_bytes = peak_bytes.load(std::memory_order_relaxed); -#endif - return result; - } -private: - void* do_allocate(std::size_t size, std::size_t alignment) override; - void do_deallocate(void* address, std::size_t size, std::size_t alignment) override; - bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { - return this == &other; - } - Memory_Domain domain = Memory_Domain::Other; -#if RENDERIVE_MEMORY_DOMAIN_STATS - std::atomic allocation_count{}; - std::atomic deallocation_count{}; - std::atomic current_bytes{}; - std::atomic peak_bytes{}; -#endif -}; -struct Memory_System { - Memory_System() { - for (std::size_t i = 0; i < domains.size(); ++i) - domains[i].set_domain(static_cast(i)); - } - std::mutex mutex; - Callback_Memory_Resource upstream; - std::unique_ptr small_pool; - std::atomic small_resource{}; - std::atomic_bool initialized{}; - bool configured{}; - std::array domains; -}; -Memory_System& memory_system() { - static Memory_System value; - return value; -} -bool domain_uses_small_pool(Memory_Domain domain) { - switch (domain) { - case Memory_Domain::Renderable: - case Memory_Domain::Scheduler_Task: - case Memory_Domain::Update_Completion: - case Memory_Domain::Other: - return true; - default: - return false; - } -} -std::pmr::memory_resource* upstream_resource(Memory_System& system) { - system.initialized.store(true, std::memory_order_release); - return &system.upstream; -} -std::pmr::memory_resource* small_object_resource(Memory_System& system) { - system.initialized.store(true, std::memory_order_release); -#if RENDERIVE_MEMORY_GLOBAL_POOL - std::pmr::memory_resource* resource = system.small_resource.load(std::memory_order_acquire); - if (resource) - return resource; - std::lock_guard lock(system.mutex); - resource = system.small_resource.load(std::memory_order_relaxed); - if (resource) - return resource; - std::pmr::pool_options options; - options.max_blocks_per_chunk = 64; - options.largest_required_pool_block = 4 * 1024; - system.small_pool = std::make_unique(options, &system.upstream); - resource = system.small_pool.get(); - system.small_resource.store(resource, std::memory_order_release); - return resource; -#else - return &system.upstream; -#endif -} -std::pmr::memory_resource* domain_resource(Memory_Domain domain) { - Memory_System& system = memory_system(); - if (domain_uses_small_pool(domain)) - return small_object_resource(system); - return upstream_resource(system); -} -void* Domain_Memory_Resource::do_allocate(std::size_t size, std::size_t alignment) { - void* result = domain_resource(domain)->allocate(size, alignment); -#if RENDERIVE_MEMORY_DOMAIN_STATS - allocation_count.fetch_add(1, std::memory_order_relaxed); - std::uint64_t current = current_bytes.fetch_add(size, std::memory_order_relaxed) + size; - std::uint64_t peak = peak_bytes.load(std::memory_order_relaxed); - while (peak < current && !peak_bytes.compare_exchange_weak(peak, current, std::memory_order_relaxed)) {} -#endif - return result; -} -void Domain_Memory_Resource::do_deallocate(void* address, std::size_t size, std::size_t alignment) { - domain_resource(domain)->deallocate(address, size, alignment); -#if RENDERIVE_MEMORY_DOMAIN_STATS - deallocation_count.fetch_add(1, std::memory_order_relaxed); - current_bytes.fetch_sub(size, std::memory_order_relaxed); -#endif -} -} -void set_memory_callbacks(const Memory_Callbacks& callbacks) { - if (!!callbacks.allocate != !!callbacks.deallocate) - throw std::invalid_argument("allocate and deallocate must be configured together"); - Memory_System& system = memory_system(); - std::lock_guard lock(system.mutex); - if (system.configured || system.initialized.load(std::memory_order_acquire)) - throw std::logic_error("Core memory resource is already configured"); - system.upstream.set_callbacks(callbacks); - system.configured = true; -} -Memory_Stats memory_stats() noexcept { - return memory_system().upstream.stats(); -} -Memory_Domain_Stats memory_domain_stats(Memory_Domain domain) noexcept { - return memory_system().domains[memory_domain_index(domain)].stats(); -} -std::pmr::memory_resource* memory_resource() { - return small_object_resource(memory_system()); -} -std::pmr::memory_resource* memory_resource(Memory_Domain domain) { -#if RENDERIVE_MEMORY_DOMAIN_STATS - Memory_System& system = memory_system(); - system.initialized.store(true, std::memory_order_release); - return &system.domains[memory_domain_index(domain)]; -#else - return domain_resource(domain); -#endif -} -void release_unused_memory() { - Memory_System& system = memory_system(); - std::lock_guard lock(system.mutex); - if (system.small_pool) - system.small_pool->release(); -} -void shutdown_memory() { - Memory_System& system = memory_system(); - std::lock_guard lock(system.mutex); - system.small_resource.store(nullptr, std::memory_order_release); - system.small_pool.reset(); - system.initialized.store(false, std::memory_order_release); - system.configured = false; - system.upstream.set_callbacks(Memory_Callbacks{}); -} -} diff --git a/Core/base/Memory.h b/Core/base/Memory.h deleted file mode 100644 index d353f79..0000000 --- a/Core/base/Memory.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include "../architecture/global.h" -#ifndef RENDERIVE_MEMORY_DOMAIN_STATS -#define RENDERIVE_MEMORY_DOMAIN_STATS 1 -#endif -#ifndef RENDERIVE_MEMORY_GLOBAL_POOL -#define RENDERIVE_MEMORY_GLOBAL_POOL 1 -#endif -namespace renderive { -struct Memory_Callbacks { - void* context{}; - void* (*allocate)(void* context, std::size_t size, std::size_t alignment){}; - void (*deallocate)(void* context, void* address, std::size_t size, std::size_t alignment){}; -}; -struct Memory_Stats { - std::uint64_t upstream_allocation_count{}; - std::uint64_t upstream_deallocation_count{}; - std::uint64_t upstream_current_bytes{}; - std::uint64_t upstream_peak_bytes{}; -}; -/// @brief Allocation statistics tag used by Core memory resources. -enum class Memory_Domain : std::uint8_t { - /// @brief Renderable facade and render-data objects. - Renderable, - /// @brief Pending input packet buffers. - Input_Buffer, - /// @brief Waterfall data and image working storage. - Waterfall, - /// @brief Spectrum data and rendering storage. - Spectrum, - /// @brief Per-frame temporary arena overflow storage. - Frame_Arena, - /// @brief Generic ring-buffer storage. - Ring_Buffer, - /// @brief Image pixel storage. - Image, - /// @brief Audio-frequency data and rendering storage. - Audio, - /// @brief Afterglow data and rendering storage. - Afterglow, - /// @brief Planisphere data and rendering storage. - Constellation_Diagram, - /// @brief Curve data and rendering storage. - Curve, - /// @brief Point-set data and rendering storage. - Point_Set, - /// @brief Axis data and rendering storage. - Axis, - /// @brief Scheduler task objects. - Scheduler_Task, - /// @brief Renderable local cache images. - Renderable_Cache_Image, - /// @brief Plot frame images and metadata. - Plot_Frame, - /// @brief Update completion state and waiters. - Update_Completion, - /// @brief Fallback tag for allocations without a narrower category. - Other -}; -struct Memory_Domain_Stats { - std::uint64_t allocation_count{}; - std::uint64_t deallocation_count{}; - std::uint64_t current_bytes{}; - std::uint64_t peak_bytes{}; -}; -LIB_DECL void set_memory_callbacks(const Memory_Callbacks& callbacks); -LIB_DECL Memory_Stats memory_stats() noexcept; -LIB_DECL Memory_Domain_Stats memory_domain_stats(Memory_Domain domain) noexcept; -LIB_DECL std::pmr::memory_resource* memory_resource(); -LIB_DECL std::pmr::memory_resource* memory_resource(Memory_Domain domain); -LIB_DECL void release_unused_memory(); -LIB_DECL void shutdown_memory(); -template -std::shared_ptr make_shared(Args&&... args) { - std::pmr::polymorphic_allocator allocator(memory_resource()); - return std::allocate_shared(allocator, std::forward(args)...); -} -} diff --git a/Core/base/Object_Semantics.h b/Core/base/Object_Semantics.h deleted file mode 100644 index 7ae82ea..0000000 --- a/Core/base/Object_Semantics.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -namespace renderive { -class Move_Only { -protected: - Move_Only() = default; - Move_Only(const Move_Only&) = delete; - Move_Only& operator=(const Move_Only&) = delete; - Move_Only(Move_Only&&) noexcept = default; - Move_Only& operator=(Move_Only&&) noexcept = default; - ~Move_Only() = default; -}; -class Immovable { -protected: - Immovable() = default; - Immovable(const Immovable&) = delete; - Immovable& operator=(const Immovable&) = delete; - Immovable(Immovable&&) = delete; - Immovable& operator=(Immovable&&) = delete; - ~Immovable() = default; -}; -} diff --git a/Core/base/String_Format.h b/Core/base/String_Format.h deleted file mode 100644 index e07f705..0000000 --- a/Core/base/String_Format.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once -#include -#include -#include -#include "Time_Of_Day.h" - -namespace renderive { - -enum class Number_Notation : std::uint8_t { - Fixed, - Scientific, - General -}; - -struct Number_Format { - Number_Notation notation = Number_Notation::Fixed; - int precision = 2; -}; - -struct Number_Locale { - char decimal_separator = '.'; - char group_separator = ','; - bool use_group_separator = false; -}; - -inline std::string format_number(double value, const Number_Format& format) { - char buf[64]; - const char* fmt = "f"; - if (format.notation == Number_Notation::Scientific) - fmt = "e"; - else if (format.notation == Number_Notation::General) - fmt = "g"; - - int prec = format.precision; - if (prec < 0) - prec = 0; - if (prec > 20) - prec = 20; - - auto result = std::to_chars(buf, buf + sizeof(buf) - 1, value, - fmt == "f" ? std::chars_format::fixed - : fmt == "e" ? std::chars_format::scientific - : std::chars_format::general, prec); - if (result.ec == std::errc{}) - return std::string(buf, result.ptr); - return {}; -} - -inline std::string format_time(Time_Of_Day time, std::string_view pattern) { - if (!time.valid()) - return {}; - - auto ms = static_cast(time.milliseconds); - auto hours = static_cast((ms / (1000 * 60 * 60)) % 24); - auto minutes = static_cast((ms / (1000 * 60)) % 60); - auto seconds = static_cast((ms / 1000) % 60); - auto msecs = static_cast(ms % 1000); - - std::string result; - result.reserve(pattern.size() + 4); - for (std::size_t i = 0; i < pattern.size(); ++i) { - char c = pattern[i]; - if (c == 'h' && i + 1 < pattern.size() && pattern[i + 1] == 'h') { - char buf[4]; - snprintf(buf, sizeof(buf), "%02d", hours); - result += buf; - ++i; - } else if (c == 'm' && i + 1 < pattern.size() && pattern[i + 1] == 'm') { - char buf[4]; - snprintf(buf, sizeof(buf), "%02d", minutes); - result += buf; - ++i; - } else if (c == 's' && i + 1 < pattern.size() && pattern[i + 1] == 's') { - char buf[4]; - snprintf(buf, sizeof(buf), "%02d", seconds); - result += buf; - ++i; - } else if (c == 'z' && i + 2 < pattern.size() && pattern[i + 1] == 'z' && pattern[i + 2] == 'z') { - char buf[6]; - snprintf(buf, sizeof(buf), "%03d", msecs); - result += buf; - i += 2; - } else { - result += c; - } - } - return result; -} - -} // namespace renderive diff --git a/Core/base/Style.h b/Core/base/Style.h deleted file mode 100644 index ba92d22..0000000 --- a/Core/base/Style.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once -#include "Color.h" -#include - -namespace renderive { - -enum class Line_Style : std::uint8_t { - None, - Solid, - Dash, - Dot -}; - -enum class Line_Cap : std::uint8_t { - Butt, - Square, - Round -}; - -enum class Line_Join : std::uint8_t { - Miter, - Bevel, - Round -}; - -struct Pen { - Color color = Color::white(); - double width = 1.0; - Line_Style style = Line_Style::Solid; - Line_Cap cap = Line_Cap::Butt; - Line_Join join = Line_Join::Miter; - [[nodiscard]] bool enabled() const { - return style != Line_Style::None && width > 0.0 && color.a != 0; - } -}; - -enum class Brush_Style : std::uint8_t { - None, - Solid -}; - -struct Brush { - Color color = Color::transparent(); - Brush_Style style = Brush_Style::None; - [[nodiscard]] bool enabled() const { - return style != Brush_Style::None && color.a != 0; - } -}; - -} // namespace renderive diff --git a/Core/base/Task.h b/Core/base/Task.h deleted file mode 100644 index e0ffb6d..0000000 --- a/Core/base/Task.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include "Memory.h" -#include "Object_Semantics.h" -namespace renderive { -class Task : Move_Only { -public: - static constexpr std::size_t Inline_Size = 64; - static constexpr std::size_t Inline_Align = alignof(std::max_align_t); - Task() noexcept = default; - Task(std::nullptr_t) noexcept {} - template - requires (!std::is_same_v, Task>) - Task(Callable&& callable) { - using Model = Task_Model>; - if constexpr (sizeof(Model) <= Inline_Size && alignof(Model) <= Inline_Align && std::is_nothrow_move_constructible_v) { - object = inline_storage(); - std::construct_at(static_cast(object), std::forward(callable)); - inline_object = true; - } - else { - resource = memory_resource(Memory_Domain::Scheduler_Task); - std::pmr::polymorphic_allocator allocator(resource); - object = allocator.allocate(1); - try { - std::construct_at(static_cast(object), std::forward(callable)); - } - catch (...) { - allocator.deallocate(static_cast(object), 1); - object = nullptr; - resource = nullptr; - throw; - } - } - invoke_function = &invoke; - destroy_function = &destroy; - move_function = &move; - } - Task(Task&& other) noexcept { - move_from(std::move(other)); - } - Task& operator=(Task&& other) noexcept { - if (this == &other) - return *this; - reset(); - move_from(std::move(other)); - return *this; - } - ~Task() { - reset(); - } - void operator()() { - if (invoke_function) - invoke_function(object); - } - explicit operator bool() const noexcept { - return object != nullptr; - } -private: - template - struct Task_Model { - template - explicit Task_Model(Source&& callable) : callable(std::forward(callable)) {} - Callable callable; - }; - template - static void invoke(void* object) { - static_cast(object)->callable(); - } - template - static void destroy(void* object, std::pmr::memory_resource* resource) { - std::destroy_at(static_cast(object)); - if (resource) { - std::pmr::polymorphic_allocator allocator(resource); - allocator.deallocate(static_cast(object), 1); - } - } - template - static void move(void* destination, void* source) { - std::construct_at(static_cast(destination), std::move(*static_cast(source))); - std::destroy_at(static_cast(source)); - } - void* inline_storage() noexcept { - return static_cast(storage); - } - void move_from(Task&& other) noexcept { - invoke_function = std::exchange(other.invoke_function, nullptr); - destroy_function = std::exchange(other.destroy_function, nullptr); - move_function = std::exchange(other.move_function, nullptr); - inline_object = std::exchange(other.inline_object, false); - if (!other.object) { - object = nullptr; - resource = nullptr; - return; - } - if (inline_object) { - object = inline_storage(); - resource = nullptr; - move_function(object, other.object); - other.object = nullptr; - return; - } - object = std::exchange(other.object, nullptr); - resource = std::exchange(other.resource, nullptr); - } - void reset() noexcept { - if (!object) - return; - destroy_function(object, resource); - object = nullptr; - resource = nullptr; - invoke_function = nullptr; - destroy_function = nullptr; - move_function = nullptr; - inline_object = false; - } - alignas(std::max_align_t) std::byte storage[Inline_Size]{}; - void* object{}; - std::pmr::memory_resource* resource{}; - void (*invoke_function)(void*){}; - void (*destroy_function)(void*, std::pmr::memory_resource*){}; - void (*move_function)(void*, void*){}; - bool inline_object{}; -}; -} diff --git a/Core/base/Text.h b/Core/base/Text.h deleted file mode 100644 index 84d99ad..0000000 --- a/Core/base/Text.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once -#include -#include -#include "Bitmask.h" -namespace renderive { -enum class Text_Align : std::uint16_t { - None = 0, - Left = 1 << 0, - Right = 1 << 1, - Horizontal_Center = 1 << 2, - Top = 1 << 3, - Bottom = 1 << 4, - Vertical_Center = 1 << 5, - Center = Horizontal_Center | Vertical_Center -}; -struct Font_Face; -struct Font { - std::shared_ptr face; - double size = 12.0; - int weight = 400; - bool italic = false; - [[nodiscard]] bool valid() const { - return face != nullptr && size > 0.0; - } -}; -struct Text_Metrics { - double width{}; - double height{}; - double ascent{}; - double descent{}; - double line_gap{}; - [[nodiscard]] double line_height() const { - return ascent + descent + line_gap; - } -}; -} // namespace renderive -RENDERIVE_ENABLE_BITMASK(renderive::Text_Align) diff --git a/Core/base/Time_Of_Day.h b/Core/base/Time_Of_Day.h deleted file mode 100644 index 77995c1..0000000 --- a/Core/base/Time_Of_Day.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include - -namespace renderive { - -struct Time_Of_Day { - std::int64_t milliseconds = -1; - [[nodiscard]] bool valid() const { - return milliseconds >= 0 && milliseconds < 24LL * 60LL * 60LL * 1000LL; - } - bool operator==(const Time_Of_Day& other) const { - return milliseconds == other.milliseconds; - } - bool operator!=(const Time_Of_Day& other) const { - return !(*this == other); - } -}; - -} // namespace renderive diff --git a/Core/base/global.cpp b/Core/base/global.cpp deleted file mode 100644 index a29c9c7..0000000 --- a/Core/base/global.cpp +++ /dev/null @@ -1,218 +0,0 @@ -#include "global.h" -#include -#include "Assert.h" -#include "Memory.h" -#include "../plot/Plot_Core.h" -#include "../render/Canvas.h" -#include "asio.hpp" -#include - -namespace renderive { - -struct Render_Scheduler_Private { - asio::io_context io_context; - std::unique_ptr> work_guard; - std::unique_ptr render_executor; - std::thread scheduler_thread; - std::thread::id scheduler_thread_id; - std::vector plots; - std::atomic_bool running{false}; - std::size_t configured_worker_count{}; -}; - -Render_Scheduler::Render_Scheduler() : d(std::make_unique()) {} -Global::Global() {} - -namespace { -Global*& global_instance_storage() { - static Global* value = nullptr; - return value; -} -} // namespace - -Global* Global::instance() { - Global*& value = global_instance_storage(); - if (!value) - value = new Global(); - return value; -} - -void Global::destroy() { - Global*& value = global_instance_storage(); - delete value; - value = nullptr; -} - -Render_Scheduler::~Render_Scheduler() { - shutdown(); -} - -void Render_Scheduler::start(Render_Runtime_Config config) { - memory_resource(); - Canvas::diagnose_default_font_once(); - std::size_t resolved_worker_count = resolve_render_worker_count(config.worker_count); - if (d->running.exchange(true, std::memory_order_acq_rel)) { - ASSERT(d->configured_worker_count == resolved_worker_count, "Render scheduler already started with a different Taskflow worker count"); - return; - } - d->io_context.restart(); - d->work_guard = std::make_unique>(d->io_context.get_executor()); - d->configured_worker_count = resolved_worker_count; - d->render_executor = std::make_unique(Render_Runtime_Config{resolved_worker_count}); - d->scheduler_thread = std::thread([this]() { - d->scheduler_thread_id = std::this_thread::get_id(); - for (Plot_Core* plot : d->plots) - initialize_plot_on_scheduler(plot); - d->io_context.run(); - d->running.store(false, std::memory_order_release); - }); -} - -void Render_Scheduler::shutdown() { - if (!d->running.load(std::memory_order_acquire)) { - if (d->scheduler_thread.joinable() && d->scheduler_thread.get_id() != std::this_thread::get_id()) - d->scheduler_thread.join(); - if (d->render_executor) - d->render_executor->shutdown(); - d->render_executor.reset(); - d->work_guard.reset(); - return; - } - if (is_scheduler_thread()) { - shutdown_on_scheduler(); - d->work_guard.reset(); - if (d->render_executor) - d->render_executor->shutdown(); - return; - } - post_and_wait([this]() { - shutdown_on_scheduler(); - }); - post([this]() { - d->work_guard.reset(); - }); - if (d->scheduler_thread.joinable()) - d->scheduler_thread.join(); - if (d->render_executor) - d->render_executor->shutdown(); - d->render_executor.reset(); -} - -void Render_Scheduler::post(Task task) { - asio::post(d->io_context, std::move(task)); -} - -void Render_Scheduler::post_at(std::uint64_t steady_time_ns, Task task) { - if (!task) - return; - auto timer = std::make_shared(d->io_context); - timer->expires_at(std::chrono::steady_clock::time_point(std::chrono::nanoseconds(steady_time_ns))); - timer->async_wait([timer, task = std::move(task)](const asio::error_code& error) mutable { - if (!error) - task(); - }); -} - -bool Render_Scheduler::try_submit_render_task(Render_Executor_Task task) { - if (!d->render_executor) - return false; - return d->render_executor->try_submit(std::move(task)); -} - -Render_Executor_Snapshot Render_Scheduler::render_executor_snapshot() const { - if (!d->render_executor) - return {}; - return d->render_executor->snapshot(); -} - -std::size_t Render_Scheduler::render_worker_count() const { - return d->configured_worker_count; -} - -void Render_Scheduler::register_plot(Plot_Core* plot) { - if (d->running.load(std::memory_order_acquire) && !is_scheduler_thread()) { - post_and_wait([this, plot]() { - register_plot_on_scheduler(plot); - }); - return; - } - register_plot_on_scheduler(plot); -} - -void Render_Scheduler::remove_plot(Plot_Core* plot) { - if (d->running.load(std::memory_order_acquire) && !is_scheduler_thread()) { - post_and_wait([this, plot]() { - remove_plot_on_scheduler(plot); - }); - return; - } - remove_plot_on_scheduler(plot); -} - -bool Render_Scheduler::is_scheduler_thread() const { - return std::this_thread::get_id() == d->scheduler_thread_id; -} - -bool Render_Scheduler::is_running() const { - return d->running.load(std::memory_order_acquire); -} - -asio::io_context& Render_Scheduler::io_context() { - return d->io_context; -} - -void Render_Scheduler::post_and_wait(Task task) { - if (is_scheduler_thread() || !d->running.load(std::memory_order_acquire)) { - task(); - return; - } - std::mutex mutex; - std::condition_variable cv; - bool done = false; - post([task = std::move(task), &mutex, &cv, &done]() mutable { - task(); - { - std::lock_guard lock(mutex); - done = true; - } - cv.notify_one(); - }); - std::unique_lock lock(mutex); - cv.wait(lock, [&done]() { - return done; - }); -} - -void Render_Scheduler::register_plot_on_scheduler(Plot_Core* plot) { - if (std::find(d->plots.begin(), d->plots.end(), plot) == d->plots.end()) - d->plots.push_back(plot); - if (d->running.load(std::memory_order_acquire)) - initialize_plot_on_scheduler(plot); -} - -void Render_Scheduler::remove_plot_on_scheduler(Plot_Core* plot) { - d->plots.erase(std::remove(d->plots.begin(), d->plots.end(), plot), d->plots.end()); - plot->deactivate_view(); -} - -void Render_Scheduler::shutdown_on_scheduler() { - for (Plot_Core* plot : d->plots) - plot->deactivate_view(); - d->plots.clear(); -} - -void Render_Scheduler::initialize_plot_on_scheduler(Plot_Core* plot) { - plot->set_viewport_size(plot->viewport_size()); - if (plot->view_active()) - plot->activate_view(); -} - -Render_Scheduler& Global::render_scheduler() { - return render_scheduler_value; -} - -void Global::start_render_scheduler(Render_Runtime_Config config) { - render_scheduler_value.start(config); -} - -} // namespace renderive diff --git a/Core/base/global.h b/Core/base/global.h deleted file mode 100644 index e6440f0..0000000 --- a/Core/base/global.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once -#include "../architecture/global.h" -#include "../execution/Render_Executor.h" -#include "Task.h" -#include -namespace asio { -class io_context; -} -namespace renderive { -class Plot_Core; -struct Render_Scheduler_Private; -class LIB_DECL Render_Scheduler { -public: - Render_Scheduler(); - ~Render_Scheduler(); - void start(Render_Runtime_Config config = {}); - void shutdown(); - void post(Task task); - void post_at(std::uint64_t steady_time_ns, Task task); - bool try_submit_render_task(Render_Executor_Task task); - [[nodiscard]] Render_Executor_Snapshot render_executor_snapshot() const; - [[nodiscard]] std::size_t render_worker_count() const; - void register_plot(Plot_Core* plot); - void remove_plot(Plot_Core* plot); - [[nodiscard]] bool is_scheduler_thread() const; - [[nodiscard]] bool is_running() const; - asio::io_context& io_context(); -private: - void post_and_wait(Task task); - void register_plot_on_scheduler(Plot_Core* plot); - void remove_plot_on_scheduler(Plot_Core* plot); - void shutdown_on_scheduler(); - void initialize_plot_on_scheduler(Plot_Core* plot); - std::unique_ptr d; -}; -class LIB_DECL Global { -public: - static Global* instance(); - static void destroy(); - Global(); - Global(const Global&) = delete; - Global& operator=(const Global&) = delete; - Render_Scheduler& render_scheduler(); - void start_render_scheduler(Render_Runtime_Config config = {}); -private: - Render_Scheduler render_scheduler_value; -}; -} // namespace renderive diff --git a/Core/event/Event.h b/Core/event/Event.h deleted file mode 100644 index f5f1e8b..0000000 --- a/Core/event/Event.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once -#include "../base/Bitmask.h" -#include - -namespace renderive { - -enum class Event_Type : std::uint8_t { - Resize, - Show, - Hide, - Leave, - Pointer_Move, - Pointer_Press, - Pointer_Release, - Wheel, - Key_Press, - Key_Release -}; - -struct Event { - explicit Event(Event_Type t) : type(t) {} - virtual ~Event() = default; - void accept() const { - accepted = true; - } - void ignore() const { - accepted = false; - } - [[nodiscard]] bool is_accepted() const { - return accepted; - } - Event_Type type; -private: - mutable bool accepted = false; -}; - -enum class Mouse_Button : std::uint8_t { - None, - Left, - Right, - Middle -}; - -enum class Keyboard_Modifier : std::uint8_t { - Ctrl = 1 << 0, - Shift = 1 << 1, - Alt = 1 << 2, - Meta = 1 << 3 -}; - -using Keyboard_Modifier_Mask = Keyboard_Modifier; - -} // namespace renderive - -RENDERIVE_ENABLE_BITMASK(renderive::Keyboard_Modifier) diff --git a/Core/event/Key_Event.h b/Core/event/Key_Event.h deleted file mode 100644 index 915dc39..0000000 --- a/Core/event/Key_Event.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include "Event.h" -#include - -namespace renderive { - -enum class Key : std::uint16_t { - Unknown, - Escape, - Enter, - Space, - Delete, - Backspace, - Left, - Right, - Up, - Down -}; - -struct Key_Event : Event { - Key key = Key::Unknown; - std::uint32_t native_key{}; - Keyboard_Modifier modifiers{}; - bool auto_repeat{}; - - Key_Event(Event_Type t) : Event(t) {} -}; - -} // namespace renderive diff --git a/Core/event/Pointer_Event.h b/Core/event/Pointer_Event.h deleted file mode 100644 index c7fa886..0000000 --- a/Core/event/Pointer_Event.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "../base/Geometry.h" -#include "Event.h" - -namespace renderive { - -using Mouse_Button_Mask = std::uint8_t; - -struct Pointer_Event : Event { - PointF position; - PointF global_position; - Mouse_Button button = Mouse_Button::None; - Mouse_Button_Mask buttons{}; - Keyboard_Modifier modifiers{}; - - Pointer_Event() : Event(Event_Type::Pointer_Move) {} - explicit Pointer_Event(Event_Type t) : Event(t) {} -}; - -} // namespace renderive diff --git a/Core/event/Resize_Event.h b/Core/event/Resize_Event.h deleted file mode 100644 index 3c98e4b..0000000 --- a/Core/event/Resize_Event.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include "../base/Geometry.h" -#include "Event.h" - -namespace renderive { - -struct Resize_Event : Event { - Size old_size; - Size new_size; - - Resize_Event() : Event(Event_Type::Resize) {} -}; - -} // namespace renderive diff --git a/Core/event/Wheel_Event.h b/Core/event/Wheel_Event.h deleted file mode 100644 index 3a60dfe..0000000 --- a/Core/event/Wheel_Event.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "Pointer_Event.h" - -namespace renderive { - -struct Wheel_Event : Pointer_Event { - double angle_delta_x{}; - double angle_delta_y{}; - double pixel_delta_x{}; - double pixel_delta_y{}; - - Wheel_Event() : Pointer_Event(Event_Type::Wheel) {} -}; - -} // namespace renderive diff --git a/Core/execution/Render_Executor.cpp b/Core/execution/Render_Executor.cpp deleted file mode 100644 index adfeae5..0000000 --- a/Core/execution/Render_Executor.cpp +++ /dev/null @@ -1,444 +0,0 @@ -#include "Render_Executor.h" -#include "../architecture/Render_Time.h" -#include -#include -#include -#include -#include -#include -#include - -namespace renderive { -struct Render_Executor_Metrics; - -struct Frame_Business_Task_Counters { - std::atomic_uint32_t business_task_count{0}; - std::atomic_uint32_t active_business_task_count{0}; - std::atomic_uint32_t business_task_peak_parallelism{0}; - std::atomic_uint64_t business_task_duration_total_ns{0}; -}; - -struct Taskflow_Graph_Impl { - Taskflow_Graph_Impl( - tf::Taskflow& taskflow, - std::shared_ptr metrics, - std::shared_ptr frame_counters) - : taskflow(taskflow), - metrics(std::move(metrics)), - frame_counters(std::move(frame_counters)) {} - tf::Taskflow& taskflow; - std::shared_ptr metrics; - std::shared_ptr frame_counters; - std::vector tasks; -}; - -struct Taskflow_Subflow_Impl { - Taskflow_Subflow_Impl( - tf::Subflow& subflow, - std::shared_ptr metrics, - std::shared_ptr frame_counters) - : subflow(subflow), - metrics(std::move(metrics)), - frame_counters(std::move(frame_counters)) {} - tf::Subflow& subflow; - std::shared_ptr metrics; - std::shared_ptr frame_counters; - std::vector tasks; -}; - -struct Render_Executor_Metrics { - std::size_t worker_count{}; - std::atomic active_workers{0}; - std::atomic active_frame_jobs{0}; - std::atomic queued_frame_jobs{0}; - std::atomic submitted_topologies{0}; - std::atomic started_topologies{0}; - std::atomic completed_topologies{0}; - std::atomic started_task_nodes{0}; - std::atomic completed_task_nodes{0}; - std::atomic rejected_frame_jobs{0}; - std::atomic dropped_frame_attempts{0}; - std::atomic peak_concurrency{0}; - std::atomic topology_queue_wait_total_ns{0}; - std::atomic topology_queue_wait_max_ns{0}; - std::atomic task_node_duration_total_ns{0}; - std::atomic task_node_duration_max_ns{0}; - std::atomic curve_tasks{0}; - std::atomic waterfall_tasks{0}; - std::atomic primitive_tasks{0}; - std::atomic compose_tasks{0}; - std::atomic performance_tasks{0}; -}; - -void update_peak(std::atomic& target, std::uint64_t value) { - std::uint64_t current = target.load(std::memory_order_acquire); - while (current < value && !target.compare_exchange_weak(current, value, std::memory_order_acq_rel, std::memory_order_acquire)) {} -} - -void add_kind_count(Render_Executor_Metrics& metrics, Render_Task_Kind kind) { - switch (kind) { - case Render_Task_Kind::Frame: - case Render_Task_Kind::Primitive: - metrics.primitive_tasks.fetch_add(1, std::memory_order_relaxed); - break; - case Render_Task_Kind::Curve: - metrics.curve_tasks.fetch_add(1, std::memory_order_relaxed); - break; - case Render_Task_Kind::Waterfall: - metrics.waterfall_tasks.fetch_add(1, std::memory_order_relaxed); - break; - case Render_Task_Kind::Compose: - metrics.compose_tasks.fetch_add(1, std::memory_order_relaxed); - break; - case Render_Task_Kind::Performance: - metrics.performance_tasks.fetch_add(1, std::memory_order_relaxed); - break; - } -} - -void begin_business_task(Render_Executor_Metrics* metrics, Frame_Business_Task_Counters* counters, Render_Task_Kind kind) { - if (metrics) - add_kind_count(*metrics, kind); - if (!counters) - return; - counters->business_task_count.fetch_add(1, std::memory_order_relaxed); - std::uint32_t active = counters->active_business_task_count.fetch_add(1, std::memory_order_acq_rel) + 1; - std::uint32_t peak = counters->business_task_peak_parallelism.load(std::memory_order_acquire); - while (peak < active && !counters->business_task_peak_parallelism.compare_exchange_weak(peak, active, std::memory_order_acq_rel, std::memory_order_acquire)) {} -} - -void end_business_task(Frame_Business_Task_Counters* counters, std::uint64_t begin_ns) { - if (!counters) - return; - std::uint64_t end_ns = steady_now_ns(); - counters->business_task_duration_total_ns.fetch_add(end_ns > begin_ns ? end_ns - begin_ns : 0, std::memory_order_relaxed); - counters->active_business_task_count.fetch_sub(1, std::memory_order_acq_rel); -} - -Task wrap_business_task( - Render_Task_Kind kind, - Task work, - std::shared_ptr metrics, - std::shared_ptr counters) { - return Task([kind, work = std::move(work), metrics = std::move(metrics), counters = std::move(counters)]() mutable { - std::uint64_t begin_ns = steady_now_ns(); - begin_business_task(metrics.get(), counters.get(), kind); - if (work) - work(); - end_business_task(counters.get(), begin_ns); - }); -} - -Render_Task_Subflow::Render_Task_Subflow(void* impl) noexcept : impl(impl) {} - -Render_Task_Graph_Node Render_Task_Subflow::emplace(Render_Task_Kind kind, Task work) { - auto* graph = static_cast(impl); - if (!graph || !work) - return {}; - auto wrapped = std::make_shared(wrap_business_task(kind, std::move(work), graph->metrics, graph->frame_counters)); - auto task = graph->subflow.emplace([wrapped]() mutable { - if (wrapped && *wrapped) - (*wrapped)(); - }); - graph->tasks.push_back(task); - return {static_cast(graph->tasks.size() - 1)}; -} - -void Render_Task_Subflow::precede(Render_Task_Graph_Node before, Render_Task_Graph_Node after) { - auto* graph = static_cast(impl); - if (!graph || !before || !after) - return; - if (before.index >= graph->tasks.size() || after.index >= graph->tasks.size()) - return; - graph->tasks[before.index].precede(graph->tasks[after.index]); -} - -void Render_Task_Subflow::join() { - if (joined) - return; - auto* graph = static_cast(impl); - if (!graph) - return; - graph->subflow.join(); - joined = true; -} - -Render_Task_Graph::Render_Task_Graph(void* impl) noexcept : impl(impl) {} - -Render_Task_Graph_Node Render_Task_Graph::emplace(Render_Task_Kind kind, Task work) { - auto* graph = static_cast(impl); - if (!graph || !work) - return {}; - auto wrapped = std::make_shared(wrap_business_task(kind, std::move(work), graph->metrics, graph->frame_counters)); - auto task = graph->taskflow.emplace([wrapped]() mutable { - if (wrapped && *wrapped) - (*wrapped)(); - }); - graph->tasks.push_back(task); - return {static_cast(graph->tasks.size() - 1)}; -} - -Render_Task_Graph_Node Render_Task_Graph::emplace_subflow(Render_Task_Kind kind, Subflow_Builder builder) { - auto* graph = static_cast(impl); - if (!graph || !builder) - return {}; - auto task = graph->taskflow.emplace([kind, builder = std::move(builder), metrics = graph->metrics, counters = graph->frame_counters](tf::Subflow& raw_subflow) mutable { - std::uint64_t begin_ns = steady_now_ns(); - begin_business_task(metrics.get(), counters.get(), kind); - Taskflow_Subflow_Impl subflow_impl(raw_subflow, metrics, counters); - Render_Task_Subflow subflow(&subflow_impl); - builder(subflow); - subflow.join(); - end_business_task(counters.get(), begin_ns); - }); - graph->tasks.push_back(task); - return {static_cast(graph->tasks.size() - 1)}; -} - -void Render_Task_Graph::precede(Render_Task_Graph_Node before, Render_Task_Graph_Node after) { - auto* graph = static_cast(impl); - if (!graph || !before || !after) - return; - if (before.index >= graph->tasks.size() || after.index >= graph->tasks.size()) - return; - graph->tasks[before.index].precede(graph->tasks[after.index]); -} - -class Render_Executor_Observer final : public tf::ObserverInterface { -public: - explicit Render_Executor_Observer(std::shared_ptr metrics) - : metrics(std::move(metrics)) {} - - void set_up(std::size_t worker_count) override { - if (metrics) - metrics->worker_count = worker_count; - worker_entry_ns.clear(); - worker_entry_ns.resize(worker_count); - } - - void on_entry(tf::WorkerView worker, tf::TaskView) override { - if (!metrics) - return; - std::size_t worker_id = worker.id(); - if (worker_id < worker_entry_ns.size()) - worker_entry_ns[worker_id] = steady_now_ns(); - std::size_t active = metrics->active_workers.fetch_add(1, std::memory_order_acq_rel) + 1; - metrics->started_task_nodes.fetch_add(1, std::memory_order_relaxed); - update_peak(metrics->peak_concurrency, static_cast(active)); - } - - void on_exit(tf::WorkerView worker, tf::TaskView) override { - if (!metrics) - return; - std::size_t worker_id = worker.id(); - if (worker_id < worker_entry_ns.size()) { - std::uint64_t begin_ns = worker_entry_ns[worker_id]; - std::uint64_t end_ns = steady_now_ns(); - std::uint64_t duration_ns = end_ns > begin_ns ? end_ns - begin_ns : 0; - metrics->task_node_duration_total_ns.fetch_add(duration_ns, std::memory_order_relaxed); - update_peak(metrics->task_node_duration_max_ns, duration_ns); - } - metrics->completed_task_nodes.fetch_add(1, std::memory_order_relaxed); - metrics->active_workers.fetch_sub(1, std::memory_order_acq_rel); - } - -private: - std::shared_ptr metrics; - std::vector worker_entry_ns; -}; - -class Render_Executor_Private { - struct Topology_Run_Record { - std::uint64_t begin_ns{}; - std::uint64_t queue_wait_ns{}; - }; - -public: - explicit Render_Executor_Private(std::size_t worker_count) - : metrics(std::make_shared()), - executor(worker_count) { - metrics->worker_count = worker_count; - observer = executor.make_observer(metrics); - } - - ~Render_Executor_Private() { - shutdown(); - } - - bool try_submit(Render_Executor_Task task) { - if (shutting_down.load(std::memory_order_acquire) || (!task.work && !task.build_graph)) { - reject(task); - return false; - } - - std::uint64_t enqueue_ns = steady_now_ns(); - record_submission(task); - submit_to_executor(std::move(task), enqueue_ns); - return true; - } - - void shutdown() { - if (shutting_down.exchange(true, std::memory_order_acq_rel)) - return; - executor.wait_for_all(); - } - - Render_Executor_Snapshot snapshot() const { - std::uint64_t submitted_topologies = metrics->submitted_topologies.load(std::memory_order_acquire); - std::uint64_t started_topologies = metrics->started_topologies.load(std::memory_order_acquire); - std::uint64_t completed_topologies = metrics->completed_topologies.load(std::memory_order_acquire); - std::uint64_t completed_task_nodes = metrics->completed_task_nodes.load(std::memory_order_acquire); - std::uint64_t started_task_nodes = metrics->started_task_nodes.load(std::memory_order_acquire); - std::uint64_t queue_total = metrics->topology_queue_wait_total_ns.load(std::memory_order_acquire); - std::uint64_t duration_total = metrics->task_node_duration_total_ns.load(std::memory_order_acquire); - std::size_t active = metrics->active_workers.load(std::memory_order_acquire); - std::size_t workers = metrics->worker_count; - return { - workers, - active, - workers > active ? workers - active : 0, - metrics->active_frame_jobs.load(std::memory_order_acquire), - metrics->queued_frame_jobs.load(std::memory_order_acquire), - submitted_topologies, - started_topologies, - completed_topologies, - started_task_nodes, - completed_task_nodes, - metrics->rejected_frame_jobs.load(std::memory_order_acquire), - metrics->dropped_frame_attempts.load(std::memory_order_acquire), - metrics->peak_concurrency.load(std::memory_order_acquire), - started_topologies ? queue_total / started_topologies : 0, - metrics->topology_queue_wait_max_ns.load(std::memory_order_acquire), - completed_task_nodes ? duration_total / completed_task_nodes : 0, - metrics->task_node_duration_max_ns.load(std::memory_order_acquire), - metrics->curve_tasks.load(std::memory_order_acquire), - metrics->waterfall_tasks.load(std::memory_order_acquire), - metrics->primitive_tasks.load(std::memory_order_acquire), - metrics->compose_tasks.load(std::memory_order_acquire), - metrics->performance_tasks.load(std::memory_order_acquire) - }; - } - - std::size_t worker_count() const { - return metrics->worker_count; - } - -private: - void record_submission(Render_Executor_Task& task) { - metrics->submitted_topologies.fetch_add(1, std::memory_order_relaxed); - if (task.frame_job) - metrics->queued_frame_jobs.fetch_add(1, std::memory_order_relaxed); - if (task.frame_stat) - task.frame_stat->executor_at_submit = snapshot(); - } - - void submit_to_executor(Render_Executor_Task task, std::uint64_t enqueue_ns) { - auto task_ptr = std::make_shared(std::move(task)); - auto completion = std::make_shared(std::move(task_ptr->completion)); - auto run_record = std::make_shared(); - auto frame_counters = std::make_shared(); - auto topology = std::make_shared(); - auto entry = topology->emplace([this, enqueue_ns, task_ptr, run_record]() mutable { - Render_Executor_Task& task = *task_ptr; - std::uint64_t begin_ns = steady_now_ns(); - run_record->begin_ns = begin_ns; - std::uint64_t queue_wait_ns = begin_ns > enqueue_ns ? begin_ns - enqueue_ns : 0; - run_record->queue_wait_ns = queue_wait_ns; - metrics->started_topologies.fetch_add(1, std::memory_order_relaxed); - metrics->topology_queue_wait_total_ns.fetch_add(queue_wait_ns, std::memory_order_relaxed); - update_peak(metrics->topology_queue_wait_max_ns, queue_wait_ns); - if (task.frame_job) { - metrics->queued_frame_jobs.fetch_sub(1, std::memory_order_relaxed); - metrics->active_frame_jobs.fetch_add(1, std::memory_order_relaxed); - } - }); - auto exit = topology->emplace([this, task_ptr, run_record, frame_counters]() mutable { - Render_Executor_Task& task = *task_ptr; - std::uint64_t end_ns = steady_now_ns(); - std::uint64_t begin_ns = run_record->begin_ns; - std::uint64_t queue_wait_ns = run_record->queue_wait_ns; - std::uint64_t run_ns = end_ns > begin_ns ? end_ns - begin_ns : 0; - if (task.frame_stat) { - Frame_Worker_Stats& stat = *task.frame_stat; - std::uint32_t business_task_count = frame_counters ? frame_counters->business_task_count.load(std::memory_order_acquire) : 0; - std::uint32_t business_task_peak_parallelism = frame_counters ? frame_counters->business_task_peak_parallelism.load(std::memory_order_acquire) : 0; - std::uint64_t business_task_duration_total_ns = frame_counters ? frame_counters->business_task_duration_total_ns.load(std::memory_order_acquire) : 0; - stat.business_task_count += business_task_count; - stat.business_task_peak_parallelism = std::max(stat.business_task_peak_parallelism, business_task_peak_parallelism); - stat.topology_queue_wait_total_ns += queue_wait_ns; - stat.topology_queue_wait_max_ns = std::max(stat.topology_queue_wait_max_ns, queue_wait_ns); - stat.business_task_run_total_ns += business_task_duration_total_ns; - stat.topology_run_total_ns += run_ns; - } - if (task.frame_job) - metrics->active_frame_jobs.fetch_sub(1, std::memory_order_relaxed); - metrics->completed_topologies.fetch_add(1, std::memory_order_relaxed); - }); - if (task_ptr->build_graph) { - Taskflow_Graph_Impl graph_impl(*topology, metrics, frame_counters); - graph_impl.tasks.push_back(entry); - graph_impl.tasks.push_back(exit); - Render_Task_Graph graph(&graph_impl); - task_ptr->build_graph(graph, {0}, {1}); - } - else { - auto wrapped = std::make_shared(wrap_business_task(task_ptr->kind, std::move(task_ptr->work), metrics, frame_counters)); - auto work = topology->emplace([wrapped]() mutable { - if (wrapped && *wrapped) - (*wrapped)(); - }); - entry.precede(work); - work.precede(exit); - } - executor.run(*topology, [this, topology, completion, task_ptr]() mutable { - if (task_ptr->frame_stat) - task_ptr->frame_stat->executor_at_finish = snapshot(); - if (completion && *completion) - (*completion)(); - }); - } - - void reject(const Render_Executor_Task& task) { - if (task.frame_job) - metrics->rejected_frame_jobs.fetch_add(1, std::memory_order_relaxed); - else - metrics->dropped_frame_attempts.fetch_add(1, std::memory_order_relaxed); - if (task.frame_stat) - task.frame_stat->rejected_topology_count++; - } - - std::shared_ptr metrics; - tf::Executor executor; - std::shared_ptr observer; - std::atomic_bool shutting_down{false}; -}; - -std::size_t resolve_render_worker_count(std::size_t configured_count) { - if (configured_count) - return configured_count; - auto cpu_count = std::thread::hardware_concurrency(); - return cpu_count > 1 ? cpu_count - 1 : 1; -} - -Render_Executor::Render_Executor(Render_Runtime_Config config) - : d(std::make_unique(resolve_render_worker_count(config.worker_count))) {} - -Render_Executor::~Render_Executor() = default; - -bool Render_Executor::try_submit(Render_Executor_Task task) { - return d->try_submit(std::move(task)); -} - -void Render_Executor::shutdown() { - d->shutdown(); -} - -Render_Executor_Snapshot Render_Executor::snapshot() const { - return d->snapshot(); -} - -std::size_t Render_Executor::worker_count() const { - return d->worker_count(); -} - -} // namespace renderive diff --git a/Core/execution/Render_Executor.h b/Core/execution/Render_Executor.h deleted file mode 100644 index edaf77a..0000000 --- a/Core/execution/Render_Executor.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once -#include "../architecture/Render_Config.h" -#include "../architecture/global.h" -#include "../base/Task.h" -#include -#include -#include -namespace renderive { -struct Render_Task_Graph_Node { - std::uint32_t index = UINT32_MAX; - explicit operator bool() const noexcept { - return index != UINT32_MAX; - } -}; -class LIB_DECL Render_Task_Subflow { -public: - explicit Render_Task_Subflow(void* impl) noexcept; - [[nodiscard]] Render_Task_Graph_Node emplace(Render_Task_Kind kind, Task work); - void precede(Render_Task_Graph_Node before, Render_Task_Graph_Node after); - void join(); - -private: - void* impl{}; - bool joined{}; -}; -class LIB_DECL Render_Task_Graph { -public: - using Subflow_Builder = std::function; - explicit Render_Task_Graph(void* impl) noexcept; - [[nodiscard]] Render_Task_Graph_Node emplace(Render_Task_Kind kind, Task work); - [[nodiscard]] Render_Task_Graph_Node emplace_subflow(Render_Task_Kind kind, Subflow_Builder builder); - void precede(Render_Task_Graph_Node before, Render_Task_Graph_Node after); -private: - void* impl{}; -}; -struct Render_Executor_Task { - using Graph_Builder = std::function; - Plot_Execution_Id plot_id{}; - std::uint64_t frame_id{}; - Render_Task_Kind kind = Render_Task_Kind::Primitive; - Task work; - Graph_Builder build_graph; - Task completion; - std::shared_ptr frame_stat; - bool frame_job{}; -}; -class Render_Executor_Private; -class LIB_DECL Render_Executor { -public: - explicit Render_Executor(Render_Runtime_Config config = {}); - Render_Executor(const Render_Executor&) = delete; - Render_Executor& operator=(const Render_Executor&) = delete; - ~Render_Executor(); - bool try_submit(Render_Executor_Task task); - void shutdown(); - [[nodiscard]] Render_Executor_Snapshot snapshot() const; - [[nodiscard]] std::size_t worker_count() const; -private: - std::unique_ptr d; -}; -std::size_t resolve_render_worker_count(std::size_t configured_count); -} // namespace renderive diff --git a/Core/export.h b/Core/export.h deleted file mode 100644 index a257f3d..0000000 --- a/Core/export.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include "architecture/Renderable.h" -#include "Axis/export.h" -#include "base/Memory.h" -#include "plottable/export.h" -#include "primitive/export.h" diff --git a/Core/flow/Flow_Diagnostics.h b/Core/flow/Flow_Diagnostics.h deleted file mode 100644 index 8c8ad60..0000000 --- a/Core/flow/Flow_Diagnostics.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include "../architecture/Frame_Scheduler.h" -#include -#include - -namespace renderive { - -struct Common_Flow_Diagnostics { - bool active{}; -}; - -struct Low_Latency_Diagnostics { - bool active{}; - double max_render_fps{}; - Refresh_Control_Snapshot refresh; - Low_Latency_Performance_Diagnostics performance; -}; - -struct Explicit_Diagnostics { - bool active{}; - std::uint64_t pending_request_count{}; -}; - -struct Flow_Diagnostics { - Common_Flow_Diagnostics common; - std::variant strategy; -}; - -} // namespace renderive diff --git a/Core/flow/Frame_Flow.h b/Core/flow/Frame_Flow.h deleted file mode 100644 index a0f3d06..0000000 --- a/Core/flow/Frame_Flow.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once -#include "../architecture/Frame_Scheduler.h" -#include "../architecture/Update_Completion.h" -#include "../base/Geometry.h" -#include "Flow_Diagnostics.h" -#include "Frame_Flow_Host.h" -#include "Present_Surface_Lease.h" -#include "Render_Surface.h" -#include -#include -#include -#include - -namespace renderive { - -class Renderable_Runtime; -class Renderable; - -class Frame_Flow { -public: - virtual ~Frame_Flow() = default; - - virtual void attach(Frame_Flow_Host& host) = 0; - [[nodiscard]] virtual std::unique_ptr create_renderable_runtime(Renderable& owner) const = 0; - [[nodiscard]] virtual Frame_Flow_Runtime_Snapshot runtime_snapshot(std::uint64_t now_ns, bool destroying) const = 0; - virtual void advance_edit_version() = 0; - virtual void cancel_all_update_states() = 0; - [[nodiscard]] virtual Render_Frame_Surface begin_render_frame() = 0; - virtual void cancel_render_frame(Render_Frame_Surface&& surface) = 0; - virtual void discard_render_frame(Render_Frame_Surface&& surface) = 0; - [[nodiscard]] virtual Render_Surface_Publish_Result publish_rendered_frame(Render_Frame_Surface&& surface, std::uint64_t now_ns) = 0; - [[nodiscard]] virtual Render_Surface_Publish_Result request_present(std::uint64_t now_ns) = 0; - [[nodiscard]] virtual Present_Surface_Lease begin_present(std::uint64_t now_ns, std::shared_ptr& returned_frame_record) = 0; - [[nodiscard]] virtual std::shared_ptr end_present(Present_Surface_Lease&& surface, std::uint64_t paint_begin_ns, std::uint64_t paint_end_ns) = 0; - virtual void activate_view(std::uint64_t now_ns) = 0; - virtual void deactivate_view() = 0; - [[nodiscard]] virtual bool view_active() const = 0; - virtual void notify_model_dirty(Dirty_Reason reason, std::uint64_t now_ns) = 0; - virtual void notify_viewport_changed(Size viewport, std::uint64_t now_ns) = 0; - virtual void notify_render_finished(Frame_Lifecycle_Record& frame, std::uint64_t now_ns) = 0; - virtual void notify_paint_finished(std::uint64_t now_ns) = 0; - virtual void notify_frame_returned(Frame_Lifecycle_Record& frame, std::uint64_t now_ns) = 0; - virtual void notify_timer_fired(std::uint64_t now_ns) = 0; - virtual void drain_frame_update_states(std::pmr::vector>& output) = 0; - [[nodiscard]] virtual Flow_Diagnostics diagnostics() const = 0; - virtual void shutdown() = 0; -}; - -} // namespace renderive diff --git a/Core/flow/Frame_Flow_Host.h b/Core/flow/Frame_Flow_Host.h deleted file mode 100644 index 9f0ebff..0000000 --- a/Core/flow/Frame_Flow_Host.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include "../architecture/Frame_Scheduler.h" -#include -#include - -namespace renderive { - -struct Frame_Flow_Runtime_Snapshot { - bool destroying{}; - bool dirty{}; - bool render_job_active{}; - bool middle_has_new_frame{}; - bool paint_active{}; - std::uint64_t now_ns{}; -}; - -class Frame_Flow_Host { -public: - virtual ~Frame_Flow_Host() = default; - - [[nodiscard]] virtual Frame_Flow_Runtime_Snapshot runtime_snapshot(std::uint64_t now_ns) const = 0; - [[nodiscard]] virtual bool try_render_once() = 0; - virtual void request_paint_from_middle() = 0; - virtual void schedule_render_deadline(std::uint64_t deadline_ns) = 0; - virtual void cancel_render_deadline() = 0; - virtual void record_feedback_event(std::shared_ptr) {} -}; - -} // namespace renderive diff --git a/Core/flow/Present_Surface_Lease.cpp b/Core/flow/Present_Surface_Lease.cpp deleted file mode 100644 index 2c80726..0000000 --- a/Core/flow/Present_Surface_Lease.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "Present_Surface_Lease.h" - -namespace renderive { - -Present_Surface_Lease::Present_Surface_Lease() = default; - -Present_Surface_Lease::Present_Surface_Lease(Present_Surface_Lease&& other) noexcept { - move_from(std::move(other)); -} - -Present_Surface_Lease& Present_Surface_Lease::operator=(Present_Surface_Lease&& other) noexcept { - if (this == &other) - return *this; - reset(); - move_from(std::move(other)); - return *this; -} - -Present_Surface_Lease::~Present_Surface_Lease() { - reset(); -} - -Present_Surface_Lease::operator bool() const { - return output() != nullptr; -} - -bool Present_Surface_Lease::new_frame() const { - return is_new_frame; -} - -bool Present_Surface_Lease::request_was_pending() const { - return update_was_pending; -} - -Image_View Present_Surface_Lease::image() const { - Render_Output* current = output(); - if (!current) - return {}; - return current->image.view(); -} - -Present_Surface_Lease Present_Surface_Lease::empty(bool is_new_frame, bool update_was_pending) { - Present_Surface_Lease lease; - lease.is_new_frame = is_new_frame; - lease.update_was_pending = update_was_pending; - return lease; -} - -Render_Output* Present_Surface_Lease::output() const { - return output_ ? output_(&storage_) : nullptr; -} - -void Present_Surface_Lease::reset() { - if (destroy_) - destroy_(&storage_); - output_ = nullptr; - destroy_ = nullptr; - move_ = nullptr; - is_new_frame = false; - update_was_pending = false; -} - -void Present_Surface_Lease::move_from(Present_Surface_Lease&& other) noexcept { - if (!other.move_) { - is_new_frame = other.is_new_frame; - update_was_pending = other.update_was_pending; - other.is_new_frame = false; - other.update_was_pending = false; - return; - } - other.move_(&storage_, &other.storage_); - output_ = other.output_; - destroy_ = other.destroy_; - move_ = other.move_; - is_new_frame = other.is_new_frame; - update_was_pending = other.update_was_pending; - other.output_ = nullptr; - other.destroy_ = nullptr; - other.move_ = nullptr; - other.is_new_frame = false; - other.update_was_pending = false; -} - -} // namespace renderive diff --git a/Core/flow/Present_Surface_Lease.h b/Core/flow/Present_Surface_Lease.h deleted file mode 100644 index 67abdbc..0000000 --- a/Core/flow/Present_Surface_Lease.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once -#include "../render/Image_View.h" -#include "../render/Render_Output.h" -#include -#include -#include -#include -#include -#include - -namespace renderive { - -struct Present_Surface_Lease_Access; - -class Present_Surface_Lease { -public: - Present_Surface_Lease(); - Present_Surface_Lease(Present_Surface_Lease&& other) noexcept; - Present_Surface_Lease& operator=(Present_Surface_Lease&& other) noexcept; - ~Present_Surface_Lease(); - - [[nodiscard]] explicit operator bool() const; - [[nodiscard]] bool new_frame() const; - [[nodiscard]] bool request_was_pending() const; - [[nodiscard]] Image_View image() const; - -private: - friend struct Present_Surface_Lease_Access; - - static constexpr std::size_t Storage_Size = 96; - static constexpr std::size_t Storage_Align = alignof(std::max_align_t); - using Storage = std::aligned_storage_t; - using Output_Fn = Render_Output* (*)(const void*); - using Destroy_Fn = void (*)(void*); - using Move_Fn = void (*)(void*, void*); - - template - static Present_Surface_Lease from_handle(bool is_new_frame, bool update_was_pending, Args&&... args) { - static_assert(sizeof(Handle) <= Storage_Size, "Present surface handle exceeds inline lease storage"); - static_assert(alignof(Handle) <= Storage_Align, "Present surface handle exceeds inline lease alignment"); - Present_Surface_Lease lease; - std::construct_at(reinterpret_cast(&lease.storage_), std::forward(args)...); - lease.output_ = [](const void* storage) -> Render_Output* { - return reinterpret_cast(storage)->output(); - }; - lease.destroy_ = [](void* storage) { - std::destroy_at(reinterpret_cast(storage)); - }; - lease.move_ = [](void* destination, void* source) { - std::construct_at(reinterpret_cast(destination), std::move(*reinterpret_cast(source))); - std::destroy_at(reinterpret_cast(source)); - }; - lease.is_new_frame = is_new_frame; - lease.update_was_pending = update_was_pending; - return lease; - } - - static Present_Surface_Lease empty(bool is_new_frame, bool update_was_pending); - [[nodiscard]] Render_Output* output() const; - void reset(); - void move_from(Present_Surface_Lease&& other) noexcept; - - Storage storage_{}; - Output_Fn output_{}; - Destroy_Fn destroy_{}; - Move_Fn move_{}; - bool is_new_frame{}; - bool update_was_pending{}; -}; - -struct Present_Surface_Lease_Access { - template - static Present_Surface_Lease from_handle(bool is_new_frame, bool update_was_pending, Args&&... args) { - return Present_Surface_Lease::from_handle(is_new_frame, update_was_pending, std::forward(args)...); - } - - static Present_Surface_Lease empty(bool is_new_frame, bool update_was_pending) { - return Present_Surface_Lease::empty(is_new_frame, update_was_pending); - } - - static Render_Output* output(const Present_Surface_Lease& lease) { - return lease.output(); - } - - static void reset(Present_Surface_Lease& lease) { - lease.reset(); - } -}; - -} // namespace renderive diff --git a/Core/flow/Render_Surface.h b/Core/flow/Render_Surface.h deleted file mode 100644 index 14c353c..0000000 --- a/Core/flow/Render_Surface.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include "../base/Geometry.h" -#include "../base/Object_Semantics.h" -#include "../architecture/Frame_Scheduler.h" -#include "../render/Render_Output.h" -#include -#include -#include - -namespace renderive { - -struct Render_Frame_Surface_Handle { - virtual ~Render_Frame_Surface_Handle() = default; -}; - -struct Render_Frame_Surface : Move_Only { - Render_Frame_Surface() = default; - Render_Frame_Surface(Render_Output* output, std::uint64_t input_version, std::unique_ptr handle) - : output(output), input_version(input_version), handle(std::move(handle)) {} - Render_Frame_Surface(Render_Frame_Surface&&) noexcept = default; - Render_Frame_Surface& operator=(Render_Frame_Surface&&) noexcept = default; - ~Render_Frame_Surface() = default; - - Render_Output* output{}; - std::uint64_t input_version{}; - std::unique_ptr handle; - - [[nodiscard]] explicit operator bool() const { - return output && handle; - } -}; - -struct Render_Surface_Publish_Result { - Rect dirty_rect; - std::shared_ptr superseded_frame_record; - std::shared_ptr dropped_frame_record; - std::uint64_t update_request_id{}; - std::uint64_t update_request_time{}; - bool request_present{}; -}; - -} // namespace renderive diff --git a/Core/flow/Render_Ticket.h b/Core/flow/Render_Ticket.h deleted file mode 100644 index 4023b24..0000000 --- a/Core/flow/Render_Ticket.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "../architecture/Update_Completion.h" - -namespace renderive { - -using Render_Ticket = Update_Ticket; - -} // namespace renderive diff --git a/Core/flow/explicit/Explicit_Frame_Flow.cpp b/Core/flow/explicit/Explicit_Frame_Flow.cpp deleted file mode 100644 index 2c877bc..0000000 --- a/Core/flow/explicit/Explicit_Frame_Flow.cpp +++ /dev/null @@ -1,292 +0,0 @@ -#include "Explicit_Frame_Flow.h" -#include "Explicit_Renderable_Runtime.h" -#include "../../architecture/Render_Time.h" -#include "../../architecture/Update_Completion.h" - -namespace renderive { - -namespace { -struct Explicit_Render_Surface_Handle final : Render_Frame_Surface_Handle { - explicit Explicit_Render_Surface_Handle(std::shared_ptr output) : output(std::move(output)) {} - std::shared_ptr output; -}; - -struct Explicit_Present_Surface_Handle final { - explicit Explicit_Present_Surface_Handle(std::shared_ptr output) : output_value(std::move(output)) {} - - [[nodiscard]] Render_Output* output() const { - return const_cast(output_value.get()); - } - - std::shared_ptr output_value; -}; - -Explicit_Render_Surface_Handle* explicit_handle(Render_Frame_Surface& surface) { - return static_cast(surface.handle.get()); -} -} // namespace - -Explicit_Frame_Flow::Explicit_Frame_Flow() - : pending_requests(memory_resource(Memory_Domain::Update_Completion)) {} - -Render_Ticket Explicit_Frame_Flow::request_render() { - auto state = make_update_state(); - { - std::lock_guard lock(request_mutex); - pending_requests.push_back(state); - } - manual_request_pending.store(true, std::memory_order_release); - return Render_Ticket(std::move(state)); -} - -void Explicit_Frame_Flow::attach(Frame_Flow_Host& host_value) { - host = &host_value; -} - -std::unique_ptr Explicit_Frame_Flow::create_renderable_runtime(Renderable& owner) const { - return make_explicit_renderable_runtime(owner); -} - -Frame_Flow_Runtime_Snapshot Explicit_Frame_Flow::runtime_snapshot(std::uint64_t now_ns, bool destroying) const { - return { - destroying, - manual_request_pending.load(std::memory_order_acquire), - render_job_active.load(std::memory_order_acquire), - frame_ready.load(std::memory_order_acquire), - false, - now_ns - }; -} - -void Explicit_Frame_Flow::advance_edit_version() { - edit_version.fetch_add(1, std::memory_order_acq_rel); -} - -void Explicit_Frame_Flow::cancel_all_update_states() { - { - std::lock_guard lock(request_mutex); - for (auto& state : pending_requests) { - if (state) - state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - } - pending_requests.clear(); - } - std::shared_ptr output; - { - std::lock_guard lock(surface_mutex); - output = present_surface; - } - if (output) - cancel_update_states(const_cast(output.get())->presented_update_states); -} - -Render_Frame_Surface Explicit_Frame_Flow::begin_render_frame() { - if (render_job_active.exchange(true, std::memory_order_acq_rel)) - return {}; - auto output = std::make_shared(); - output->render_complete.store(false, std::memory_order_release); - auto handle = std::make_unique(output); - auto* output_pointer = output.get(); - std::uint64_t input_version = edit_version.load(std::memory_order_acquire); - return Render_Frame_Surface(output_pointer, input_version, std::move(handle)); -} - -void Explicit_Frame_Flow::cancel_render_frame(Render_Frame_Surface&& surface) { - (void)surface; - render_job_active.store(false, std::memory_order_release); -} - -void Explicit_Frame_Flow::discard_render_frame(Render_Frame_Surface&& surface) { - (void)surface; - render_job_active.store(false, std::memory_order_release); -} - -Render_Surface_Publish_Result Explicit_Frame_Flow::publish_rendered_frame(Render_Frame_Surface&& surface, std::uint64_t now_ns) { - Render_Surface_Publish_Result result; - auto* handle = explicit_handle(surface); - if (!handle || !handle->output) { - render_job_active.store(false, std::memory_order_release); - return result; - } - if (!handle->output->render_complete.load(std::memory_order_acquire) - || !handle->output->metadata - || !handle->output->metadata->render_end_ns - || handle->output->image.empty()) { - if (handle->output->metadata) - handle->output->metadata->outcome = Frame_Outcome::Publish_Dropped; - result.dropped_frame_record = handle->output->metadata; - supersede_update_states(handle->output->presented_update_states); - render_job_active.store(false, std::memory_order_release); - return result; - } - if (handle->output->metadata) { - handle->output->metadata->transition_12_ns = now_ns; - handle->output->metadata->ready_queue_wait_ns = now_ns > handle->output->metadata->render_end_ns ? now_ns - handle->output->metadata->render_end_ns : 0; - handle->output->metadata->update_request_id = ++next_present_request_id; - handle->output->metadata->update_request_time_ns = now_ns; - result.update_request_id = handle->output->metadata->update_request_id; - result.update_request_time = now_ns; - } - std::shared_ptr output = std::move(handle->output); - Size size = output ? output->image.size() : Size{}; - { - std::lock_guard lock(surface_mutex); - present_surface = output; - } - frame_ready.store(true, std::memory_order_release); - paint_request_pending.store(true, std::memory_order_release); - render_job_active.store(false, std::memory_order_release); - result.dirty_rect = Rect{0, 0, size.width, size.height}; - result.request_present = true; - return result; -} - -Render_Surface_Publish_Result Explicit_Frame_Flow::request_present(std::uint64_t now_ns) { - Render_Surface_Publish_Result result; - std::shared_ptr output; - { - std::lock_guard lock(surface_mutex); - output = present_surface; - } - if (!output) - return result; - Render_Output* mutable_output = const_cast(output.get()); - if (mutable_output->metadata) { - mutable_output->metadata->update_request_id = ++next_present_request_id; - mutable_output->metadata->update_request_time_ns = now_ns; - result.update_request_id = mutable_output->metadata->update_request_id; - result.update_request_time = now_ns; - } - paint_request_pending.store(true, std::memory_order_release); - Size size = output->image.size(); - result.dirty_rect = Rect{0, 0, size.width, size.height}; - result.request_present = true; - return result; -} - -Present_Surface_Lease Explicit_Frame_Flow::begin_present(std::uint64_t now_ns, std::shared_ptr& returned_frame_record) { - returned_frame_record.reset(); - std::shared_ptr output; - { - std::lock_guard lock(surface_mutex); - output = present_surface; - } - if (!output) - return {}; - bool is_new = frame_ready.exchange(false, std::memory_order_acq_rel); - bool pending = paint_request_pending.exchange(false, std::memory_order_acq_rel); - Render_Output* mutable_output = const_cast(output.get()); - if (is_new && mutable_output->metadata) { - mutable_output->metadata->transition_23_ns = now_ns; - mutable_output->metadata->present_queue_wait_ns = now_ns > mutable_output->metadata->transition_12_ns ? now_ns - mutable_output->metadata->transition_12_ns : 0; - } - return Present_Surface_Lease_Access::from_handle( - is_new, - pending, - std::move(output)); -} - -std::shared_ptr Explicit_Frame_Flow::end_present(Present_Surface_Lease&& surface, std::uint64_t paint_begin_ns, std::uint64_t paint_end_ns) { - std::shared_ptr record; - Render_Output* output = Present_Surface_Lease_Access::output(surface); - if (output && surface.new_frame()) { - record = output->metadata; - if (record) { - record->paint_begin_ns = paint_begin_ns; - record->paint_end_ns = paint_end_ns; - record->outcome = Frame_Outcome::Presented; - } - complete_update_states(output->presented_update_states, Update_Stage::Presented); - } - Present_Surface_Lease_Access::reset(surface); - return record; -} - -void Explicit_Frame_Flow::activate_view(std::uint64_t now_ns) { - view_active_state.store(true, std::memory_order_release); - evaluate(now_ns); -} - -void Explicit_Frame_Flow::deactivate_view() { - view_active_state.store(false, std::memory_order_release); -} - -bool Explicit_Frame_Flow::view_active() const { - return view_active_state.load(std::memory_order_acquire); -} - -void Explicit_Frame_Flow::notify_model_dirty(Dirty_Reason, std::uint64_t now_ns) { - evaluate(now_ns); -} - -void Explicit_Frame_Flow::notify_viewport_changed(Size, std::uint64_t now_ns) { - evaluate(now_ns); -} - -void Explicit_Frame_Flow::notify_render_finished(Frame_Lifecycle_Record&, std::uint64_t now_ns) { - evaluate(now_ns); -} - -void Explicit_Frame_Flow::notify_paint_finished(std::uint64_t now_ns) { - evaluate(now_ns); -} - -void Explicit_Frame_Flow::notify_frame_returned(Frame_Lifecycle_Record&, std::uint64_t now_ns) { - evaluate(now_ns); -} - -void Explicit_Frame_Flow::notify_timer_fired(std::uint64_t now_ns) { - evaluate(now_ns); -} - -void Explicit_Frame_Flow::drain_frame_update_states(std::pmr::vector>& output) { - std::lock_guard lock(request_mutex); - for (auto& state : pending_requests) - output.push_back(std::move(state)); - pending_requests.clear(); - manual_request_pending.store(false, std::memory_order_release); -} - -Explicit_Diagnostics Explicit_Frame_Flow::explicit_diagnostics() const { - std::lock_guard lock(request_mutex); - return Explicit_Diagnostics{ - view_active_state.load(std::memory_order_acquire), - static_cast(pending_requests.size()) - }; -} - -Flow_Diagnostics Explicit_Frame_Flow::diagnostics() const { - Explicit_Diagnostics typed = explicit_diagnostics(); - return Flow_Diagnostics{ - Common_Flow_Diagnostics{typed.active}, - typed - }; -} - -void Explicit_Frame_Flow::shutdown() { - host = nullptr; - cancel_all_update_states(); - view_active_state.store(false, std::memory_order_release); - manual_request_pending.store(false, std::memory_order_release); - render_job_active.store(false, std::memory_order_release); - frame_ready.store(false, std::memory_order_release); - paint_request_pending.store(false, std::memory_order_release); -} - -void Explicit_Frame_Flow::evaluate(std::uint64_t now_ns) { - if (!host) - return; - Frame_Flow_Runtime_Snapshot runtime = host->runtime_snapshot(now_ns); - if (runtime.destroying) - return; - if (runtime.middle_has_new_frame && !runtime.paint_active) - host->request_paint_from_middle(); - if (!manual_request_pending.load(std::memory_order_acquire)) - return; - if (runtime.render_job_active) - return; - if (host->try_render_once()) - manual_request_pending.store(false, std::memory_order_release); -} - -} // namespace renderive diff --git a/Core/flow/explicit/Explicit_Frame_Flow.h b/Core/flow/explicit/Explicit_Frame_Flow.h deleted file mode 100644 index 9055cf3..0000000 --- a/Core/flow/explicit/Explicit_Frame_Flow.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -#include "../Frame_Flow.h" -#include "../Render_Ticket.h" -#include -#include -#include - -namespace renderive { - -class Explicit_Frame_Flow final : public Frame_Flow { -public: - Explicit_Frame_Flow(); - [[nodiscard]] Render_Ticket request_render(); - - void attach(Frame_Flow_Host& host) override; - [[nodiscard]] std::unique_ptr create_renderable_runtime(Renderable& owner) const override; - [[nodiscard]] Frame_Flow_Runtime_Snapshot runtime_snapshot(std::uint64_t now_ns, bool destroying) const override; - void advance_edit_version() override; - void cancel_all_update_states() override; - [[nodiscard]] Render_Frame_Surface begin_render_frame() override; - void cancel_render_frame(Render_Frame_Surface&& surface) override; - void discard_render_frame(Render_Frame_Surface&& surface) override; - [[nodiscard]] Render_Surface_Publish_Result publish_rendered_frame(Render_Frame_Surface&& surface, std::uint64_t now_ns) override; - [[nodiscard]] Render_Surface_Publish_Result request_present(std::uint64_t now_ns) override; - [[nodiscard]] Present_Surface_Lease begin_present(std::uint64_t now_ns, std::shared_ptr& returned_frame_record) override; - [[nodiscard]] std::shared_ptr end_present(Present_Surface_Lease&& surface, std::uint64_t paint_begin_ns, std::uint64_t paint_end_ns) override; - void activate_view(std::uint64_t now_ns) override; - void deactivate_view() override; - [[nodiscard]] bool view_active() const override; - void notify_model_dirty(std::uint64_t now_ns) { - notify_model_dirty(Dirty_Reason::Core_Data, now_ns); - } - void notify_model_dirty(Dirty_Reason reason, std::uint64_t now_ns) override; - void notify_viewport_changed(Size viewport, std::uint64_t now_ns) override; - void notify_render_finished(Frame_Lifecycle_Record& frame, std::uint64_t now_ns) override; - void notify_paint_finished(std::uint64_t now_ns) override; - void notify_frame_returned(Frame_Lifecycle_Record& frame, std::uint64_t now_ns) override; - void notify_timer_fired(std::uint64_t now_ns) override; - void drain_frame_update_states(std::pmr::vector>& output) override; - [[nodiscard]] Explicit_Diagnostics explicit_diagnostics() const; - [[nodiscard]] Flow_Diagnostics diagnostics() const override; - void shutdown() override; - -private: - void evaluate(std::uint64_t now_ns); - Frame_Flow_Host* host{}; - std::atomic_bool view_active_state{false}; - std::atomic_bool manual_request_pending{false}; - std::atomic_bool render_job_active{false}; - std::atomic_bool frame_ready{false}; - std::atomic_bool paint_request_pending{false}; - mutable std::mutex request_mutex; - mutable std::mutex surface_mutex; - std::pmr::vector> pending_requests; - std::shared_ptr present_surface; - std::atomic edit_version{1}; - std::uint64_t next_present_request_id{}; -}; - -} // namespace renderive diff --git a/Core/flow/explicit/Explicit_Renderable_Runtime.cpp b/Core/flow/explicit/Explicit_Renderable_Runtime.cpp deleted file mode 100644 index 124330d..0000000 --- a/Core/flow/explicit/Explicit_Renderable_Runtime.cpp +++ /dev/null @@ -1,243 +0,0 @@ -#include "Explicit_Renderable_Runtime.h" -#include "../../architecture/Renderable.h" -#include "../../base/Memory.h" -#include -#include -#include - -namespace renderive { - -namespace { - -struct Explicit_Input_Slot { - Input_Data* data{}; - std::uint64_t version = 0; - bool pending = false; - std::shared_ptr completion_state; -}; - -} // namespace - -struct Explicit_Renderable_Runtime_Data { - Renderable* owner{}; - mutable std::mutex state_mutex; - mutable std::mutex input_mutex; - Render_Object_Owner state_owner; - Render_State* state{}; - Render_Object_Owner input_owner; - Explicit_Input_Slot input; - std::uint64_t input_version = 0; - std::pmr::vector> committed_update_states{memory_resource(Memory_Domain::Update_Completion)}; - - void initialize(Renderable& renderable) { - clear(); - state_owner = renderable.create_render_state(); - state = state_owner.get(); - if (state) - state->version = 1; - input_owner = renderable.create_input_data(); - input.data = input_owner.get(); - input.version = 0; - input.pending = false; - input.completion_state.reset(); - } - - void cancel_pending_completions() { - std::lock_guard lock(input_mutex); - if (input.completion_state) { - input.completion_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - input.completion_state.reset(); - } - cancel_update_states(committed_update_states); - } - - void clear() { - { - std::lock_guard input_lock(input_mutex); - if (input.completion_state) { - input.completion_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - input.completion_state.reset(); - } - input.data = nullptr; - input.version = 0; - input.pending = false; - input_owner.reset(); - input_version = 0; - } - { - std::lock_guard state_lock(state_mutex); - state = nullptr; - state_owner.reset(); - } - } -}; - -namespace { - -struct Explicit_State_Read_Handle final : Render_State_Read_Handle { - explicit Explicit_State_Read_Handle(std::unique_lock lock) - : lock(std::move(lock)) {} - std::unique_lock lock; -}; - -struct Explicit_Input_Frame_Handle final : Render_Input_Frame_Handle { - Explicit_Input_Frame_Handle(Explicit_Renderable_Runtime_Data* runtime, std::unique_lock lock) - : runtime(runtime), lock(std::move(lock)) {} - void consume_input(Input_Data* input) override { - if (consumed || !runtime) - return; - consumed = true; - if (runtime->input.completion_state) { - runtime->input.completion_state->complete_until(Update_Stage::Committed); - runtime->committed_update_states.push_back(std::move(runtime->input.completion_state)); - } - if (input) - input->consume(); - runtime->input.pending = false; - } - Explicit_Renderable_Runtime_Data* runtime{}; - std::unique_lock lock; - bool consumed = false; -}; - -struct Explicit_State_Write_Handle final : Render_State_Write_Handle { - Explicit_State_Write_Handle(Explicit_Renderable_Runtime_Data* runtime, std::unique_lock lock) - : runtime(runtime), lock(std::move(lock)) {} - ~Explicit_State_Write_Handle() override { - if (!runtime || !runtime->state) - return; - ++runtime->state->version; - lock.unlock(); - if (runtime->owner) - runtime->owner->mark_render_dirty(Dirty_Reason::Config); - } - Explicit_Renderable_Runtime_Data* runtime{}; - std::unique_lock lock; -}; - -struct Explicit_Input_Write_Handle final : Render_Input_Write_Handle { - Explicit_Input_Write_Handle(Explicit_Renderable_Runtime_Data* runtime, std::unique_lock lock, bool notify_render) - : runtime(runtime), lock(std::move(lock)), notify_render(notify_render) {} - ~Explicit_Input_Write_Handle() override { - if (!runtime) - return; - runtime->input.version = ++runtime->input_version; - runtime->input.pending = true; - lock.unlock(); - if (notify_render && runtime->owner) - runtime->owner->mark_render_dirty(); - } - void set_completion(std::shared_ptr state) override { - if (!runtime) - return; - if (runtime->input.completion_state && runtime->input.completion_state != state) - runtime->input.completion_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - runtime->input.completion_state = std::move(state); - } - Explicit_Renderable_Runtime_Data* runtime{}; - std::unique_lock lock; - bool notify_render{}; -}; - -} // namespace - -Explicit_Renderable_Runtime::Explicit_Renderable_Runtime(Renderable& owner) - : d(std::make_unique()) { - d->owner = &owner; - d->initialize(owner); -} - -Explicit_Renderable_Runtime::~Explicit_Renderable_Runtime() { - d->clear(); -} - -void Explicit_Renderable_Runtime::set_render_state_copier(Copy_Render_State_Function) {} - -void Explicit_Renderable_Runtime::cancel_pending_completions() { - d->cancel_pending_completions(); -} - -void Explicit_Renderable_Runtime::drain_committed_update_states(std::pmr::vector>& output) { - for (auto& state : d->committed_update_states) - output.push_back(std::move(state)); - d->committed_update_states.clear(); -} - -Render_State_Read_Guard Explicit_Renderable_Runtime::acquire_state_read(Renderable_State_Role) { - return acquire_render_state_read(); -} - -Render_State_Read_Guard Explicit_Renderable_Runtime::acquire_render_state_read() { - std::unique_lock lock(d->state_mutex); - if (!d->state) - return {}; - return Render_State_Read_Guard(d->state, std::make_unique(std::move(lock))); -} - -Render_State_Read_Guard Explicit_Renderable_Runtime::acquire_edit_state_read() { - return acquire_render_state_read(); -} - -Render_State_Write_Guard Explicit_Renderable_Runtime::acquire_edit_state() { - std::unique_lock lock(d->state_mutex); - if (!d->state) - return {}; - return Render_State_Write_Guard(d->state, std::make_unique(d.get(), std::move(lock))); -} - -Render_Input_Write_Guard Explicit_Renderable_Runtime::acquire_edit_input(bool notify_render) { - std::unique_lock lock(d->input_mutex); - if (!d->input.data) - return {}; - return Render_Input_Write_Guard(d->input.data, std::make_unique(d.get(), std::move(lock), notify_render)); -} - -Render_Input_Frame_Guard Explicit_Renderable_Runtime::acquire_input_read(Renderable_Input_Role) { - std::unique_lock lock(d->input_mutex, std::try_to_lock); - if (!lock || !d->input.data) - return {}; - return Render_Input_Frame_Guard(d->input.data, std::make_unique(d.get(), std::move(lock))); -} - -Renderable_Frame_View Explicit_Renderable_Runtime::capture_frame_view() { - std::unique_lock state_lock(d->state_mutex); - if (!d->state) - return {}; - auto state_guard = Render_State_Read_Guard( - d->state, - std::make_unique(std::move(state_lock))); - - Render_Input_Frame_Guard input_guard; - std::unique_lock input_lock(d->input_mutex); - if (d->input.data) { - input_guard = Render_Input_Frame_Guard( - d->input.data, - std::make_unique(d.get(), std::move(input_lock))); - } - return Renderable_Frame_View(std::move(state_guard), std::move(input_guard)); -} - -Render_State* Explicit_Renderable_Runtime::state(Renderable_State_Role) { - return d->state; -} - -Input_Data* Explicit_Renderable_Runtime::input_data(Renderable_Input_Role) { - return d->input.data; -} - -void Explicit_Renderable_Runtime::clear_render_input() { - std::lock_guard lock(d->input_mutex); - if (d->input.completion_state) { - d->input.completion_state->complete_until(Update_Stage::Committed); - d->committed_update_states.push_back(std::move(d->input.completion_state)); - } - if (d->input.data) - d->input.data->clear(); - d->input.pending = false; -} - -std::unique_ptr make_explicit_renderable_runtime(Renderable& owner) { - return std::make_unique(owner); -} - -} // namespace renderive diff --git a/Core/flow/explicit/Explicit_Renderable_Runtime.h b/Core/flow/explicit/Explicit_Renderable_Runtime.h deleted file mode 100644 index e87c6e2..0000000 --- a/Core/flow/explicit/Explicit_Renderable_Runtime.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#include "../../architecture/Render_Data.h" -#include - -namespace renderive { - -struct Explicit_Renderable_Runtime_Data; - -class Explicit_Renderable_Runtime final : public Renderable_Runtime { -public: - explicit Explicit_Renderable_Runtime(Renderable& owner); - Explicit_Renderable_Runtime(const Explicit_Renderable_Runtime&) = delete; - Explicit_Renderable_Runtime& operator=(const Explicit_Renderable_Runtime&) = delete; - ~Explicit_Renderable_Runtime() override; - - void set_render_state_copier(Copy_Render_State_Function copier) override; - void cancel_pending_completions() override; - void drain_committed_update_states(std::pmr::vector>& output) override; - Render_State_Read_Guard acquire_state_read(Renderable_State_Role role) override; - Render_State_Read_Guard acquire_render_state_read() override; - Render_State_Read_Guard acquire_edit_state_read() override; - Render_State_Write_Guard acquire_edit_state() override; - Render_Input_Write_Guard acquire_edit_input(bool notify_render) override; - Render_Input_Frame_Guard acquire_input_read(Renderable_Input_Role role) override; - Renderable_Frame_View capture_frame_view() override; - Render_State* state(Renderable_State_Role role) override; - Input_Data* input_data(Renderable_Input_Role role) override; - void clear_render_input() override; - -private: - std::unique_ptr d; -}; - -std::unique_ptr make_explicit_renderable_runtime(Renderable& owner); - -} // namespace renderive diff --git a/Core/flow/low_latency/Low_Latency_Feedback_Controller.cpp b/Core/flow/low_latency/Low_Latency_Feedback_Controller.cpp deleted file mode 100644 index 0e765fb..0000000 --- a/Core/flow/low_latency/Low_Latency_Feedback_Controller.cpp +++ /dev/null @@ -1,915 +0,0 @@ -#include "Low_Latency_Feedback_Controller.h" -#include -#include - -namespace renderive { -namespace { -constexpr double Enter_Ratio_Floor = 0.15; -constexpr std::uint64_t Enter_Window_Ns = 250000000; -constexpr std::uint64_t Exit_Consumer_Window_Ns = 500000000; -constexpr std::uint64_t Switch_Samples = 16; -constexpr std::uint64_t Required_Consumer_Samples = 8; -constexpr std::uint64_t Exit_Recovery_Samples = 16; -constexpr double Accepted_Ratio_Enter = 0.75; -constexpr double Accepted_Ratio_Exit = 0.50; -constexpr double Consumer_Display_User_Factor = 1.35; -constexpr double Render_Service_Display_Factor = 0.80; -constexpr double Effective_Rise_Factor = 1.10; -constexpr double Effective_Fall_Factor = 0.80; -constexpr std::uint64_t Consumer_Interval_Max_Multiplier = 4; -constexpr double Consumer_Display_Exit_Factor = 1.15; -constexpr double Producer_Starvation_Factor = 1.5; - -std::uint64_t max_period(std::uint64_t a, std::uint64_t b, std::uint64_t c, std::uint64_t d) { - return std::max(std::max(a, b), std::max(c, d)); -} - -std::uint64_t consumer_sample_total(const Refresh_Control_Snapshot& state) { - return state.consumer_sample_accepted_count - + state.consumer_sample_rejected_no_backlog_count - + state.consumer_sample_rejected_starved_count - + state.consumer_sample_rejected_no_new_frame_count - + state.consumer_sample_rejected_invalid_timestamp_count; -} - -double accepted_ratio(std::uint64_t accepted, std::uint64_t rejected_no_backlog, std::uint64_t rejected_starved, std::uint64_t rejected_no_new_frame, std::uint64_t rejected_invalid_timestamp) { - std::uint64_t total = accepted + rejected_no_backlog + rejected_starved + rejected_no_new_frame + rejected_invalid_timestamp; - return total ? static_cast(accepted) / static_cast(total) : 0.0; -} - -void decrement_window_counter(Consumer_Sample_Result result, std::uint64_t& accepted, std::uint64_t& rejected_no_backlog, std::uint64_t& rejected_starved, std::uint64_t& rejected_no_new_frame, std::uint64_t& rejected_invalid) { - switch (result) { - case Consumer_Sample_Result::Accepted: - if (accepted) - --accepted; - break; - case Consumer_Sample_Result::Rejected_No_Backlog: - if (rejected_no_backlog) - --rejected_no_backlog; - break; - case Consumer_Sample_Result::Rejected_Producer_Starved: - if (rejected_starved) - --rejected_starved; - break; - case Consumer_Sample_Result::Rejected_No_New_Frame: - if (rejected_no_new_frame) - --rejected_no_new_frame; - break; - case Consumer_Sample_Result::Rejected_Invalid_Timestamp: - if (rejected_invalid) - --rejected_invalid; - break; - } -} - -void increment_window_counter(Consumer_Sample_Result result, std::uint64_t& accepted, std::uint64_t& rejected_no_backlog, std::uint64_t& rejected_starved, std::uint64_t& rejected_no_new_frame, std::uint64_t& rejected_invalid) { - switch (result) { - case Consumer_Sample_Result::Accepted: - ++accepted; - break; - case Consumer_Sample_Result::Rejected_No_Backlog: - ++rejected_no_backlog; - break; - case Consumer_Sample_Result::Rejected_Producer_Starved: - ++rejected_starved; - break; - case Consumer_Sample_Result::Rejected_No_New_Frame: - ++rejected_no_new_frame; - break; - case Consumer_Sample_Result::Rejected_Invalid_Timestamp: - ++rejected_invalid; - break; - } -} -} // namespace - -void Low_Latency_Smoothed_Interval::update(double sample_ms) { - if (sample_ms <= 0.0 || !std::isfinite(sample_ms)) - return; - current_ms = sample_ms; - if (!sample_count) { - srtt_ms = sample_ms; - jitter_ms = sample_ms * 0.5; - sample_count = 1; - return; - } - double error = std::abs(sample_ms - srtt_ms); - jitter_ms += 0.25 * (error - jitter_ms); - srtt_ms += 0.125 * (sample_ms - srtt_ms); - ++sample_count; -} - -void Low_Latency_Smoothed_Interval::update_wait(double sample_ms) { - if (sample_ms < 0.0 || !std::isfinite(sample_ms)) - return; - current_ms = sample_ms; - if (!sample_count) { - srtt_ms = sample_ms; - jitter_ms = sample_ms * 0.5; - sample_count = 1; - return; - } - double error = std::abs(sample_ms - srtt_ms); - jitter_ms += 0.25 * (error - jitter_ms); - srtt_ms += 0.125 * (sample_ms - srtt_ms); - ++sample_count; -} - -double Low_Latency_Smoothed_Interval::safe_ms(double jitter_factor) const { - return sample_count ? srtt_ms + jitter_factor * jitter_ms : 0.0; -} - -std::uint64_t Low_Latency_Smoothed_Interval::srtt_ns() const { - return sample_count ? static_cast(srtt_ms * 1000000.0) : 0; -} - -std::uint64_t Low_Latency_Smoothed_Interval::jitter_ns() const { - return sample_count ? static_cast(jitter_ms * 1000000.0) : 0; -} - -std::uint64_t Low_Latency_Smoothed_Interval::current_ns() const { - return sample_count ? static_cast(current_ms * 1000000.0) : 0; -} - -bool Low_Latency_Smoothed_Interval::valid() const { - return sample_count > 0; -} - -void Low_Latency_Feedback_Controller::reset() { - std::uint64_t user_period = state_value.user_period_ns; - *this = Low_Latency_Feedback_Controller(); - state_value.user_period_ns = user_period; - refresh_control_snapshot(); -} - -void Low_Latency_Feedback_Controller::set_max_render_fps(double fps) { - state_value.user_period_ns = fps > 0.0 ? ms_to_ns(1000.0 / fps) : 0; - refresh_control_snapshot(); -} - -void Low_Latency_Feedback_Controller::on_input_changed(std::uint64_t now_ns, std::uint64_t input_version, Dirty_Reason reason) { - if (reason == Dirty_Reason::Core_Data && last_input_change_ns && now_ns > last_input_change_ns) - input_period.update(static_cast(now_ns - last_input_change_ns) / 1000000.0); - if (reason == Dirty_Reason::Core_Data) - last_input_change_ns = now_ns; - if (input_version) - latest_input_version = input_version; - else - ++latest_input_version; - refresh_control_snapshot(now_ns); -} - -void Low_Latency_Feedback_Controller::on_render_started(Frame_Lifecycle_Record& frame, std::uint64_t now_ns) { - exit_producer_starvation(now_ns, "render_started"); - if (last_render_start_ns && now_ns > last_render_start_ns) - producer_interval.update(static_cast(now_ns - last_render_start_ns) / 1000000.0); - last_render_start_ns = now_ns; - last_render_begin_ns = now_ns; - if (!frame.render_begin_ns) - frame.render_begin_ns = now_ns; - refresh_control_snapshot(now_ns); - write_state(frame); -} - -void Low_Latency_Feedback_Controller::on_render_completed(Frame_Lifecycle_Record& frame) { - if (frame.render_begin_ns && frame.render_begin_ns != last_render_start_ns) - on_render_started(frame, frame.render_begin_ns); - exit_producer_starvation(frame.render_end_ns, "render_completed"); - std::uint64_t render_duration_ns = frame.render_end_ns > frame.render_begin_ns ? frame.render_end_ns - frame.render_begin_ns : 0; - last_render_begin_ns = frame.render_begin_ns; - render_service_period.update(static_cast(render_duration_ns) / 1000000.0); - executor_queue_period.update_wait(static_cast(frame.executor_queue_wait_ns) / 1000000.0); - if (consumer_epoch_active) - ++consumer_epoch.rendered_frames; - refresh_control_snapshot(frame.render_end_ns); - write_state(frame); -} - -void Low_Latency_Feedback_Controller::on_frame_presented(Frame_Lifecycle_Record& frame) { - std::uint64_t paint_duration_ns = frame.paint_end_ns > frame.paint_begin_ns ? frame.paint_end_ns - frame.paint_begin_ns : 0; - paint_service_period.update(static_cast(paint_duration_ns) / 1000000.0); - if (frame.update_request_time_ns && frame.update_posted_ns >= frame.update_request_time_ns) - request_to_update_post_period.update_wait(static_cast(frame.update_posted_ns - frame.update_request_time_ns) / 1000000.0); - if (frame.update_posted_ns && frame.paint_begin_ns >= frame.update_posted_ns) - update_to_paint_period.update_wait(static_cast(frame.paint_begin_ns - frame.update_posted_ns) / 1000000.0); - - Consumer_Sample_Result sample_result = Consumer_Sample_Result::Rejected_No_New_Frame; - std::uint64_t display_sample_ns = 0; - if (!frame.frame_id || !frame.paint_begin_ns || !frame.paint_end_ns || frame.paint_end_ns < frame.paint_begin_ns) { - sample_result = Consumer_Sample_Result::Rejected_Invalid_Timestamp; - } - else if (frame.frame_id == last_consumed_frame_id) { - sample_result = Consumer_Sample_Result::Rejected_No_New_Frame; - } - else if (!last_consume_sample_ns) { - sample_result = Consumer_Sample_Result::Rejected_No_New_Frame; - } - else if (frame.paint_begin_ns <= last_consume_sample_ns) { - sample_result = Consumer_Sample_Result::Rejected_Invalid_Timestamp; - } - else { - display_sample_ns = frame.paint_begin_ns - last_consume_sample_ns; - if (!consumer_epoch_active || !consumer_epoch.continuously_backlogged) - sample_result = Consumer_Sample_Result::Rejected_No_Backlog; - else if (consumer_epoch.producer_starved) - sample_result = Consumer_Sample_Result::Rejected_Producer_Starved; - else if (consumer_epoch.rendered_frames == 0 && consumer_epoch.superseded_frames == 0) - sample_result = Consumer_Sample_Result::Rejected_No_New_Frame; - else - sample_result = Consumer_Sample_Result::Accepted; - } - - if (frame.frame_id && frame.frame_id != last_consumed_frame_id && frame.paint_begin_ns) { - if (display_sample_ns) - raw_display_interval.update(static_cast(display_sample_ns) / 1000000.0); - last_consume_sample_ns = frame.paint_begin_ns; - last_consumed_frame_id = frame.frame_id; - last_display_sample_ns = display_sample_ns; - } - apply_consumer_sample(sample_result, display_sample_ns, frame.paint_begin_ns ? frame.paint_begin_ns : frame.paint_end_ns); - - if (frame.core_data_time_ns && frame.paint_end_ns > frame.core_data_time_ns) - frame_age_period.update(static_cast(frame.paint_end_ns - frame.core_data_time_ns) / 1000000.0); - ++presented_frame_count; - frame.outcome = Frame_Outcome::Presented; - frame.presented_frame_id = frame.frame_id; - consumer_epoch = {}; - consumer_epoch_active = false; - refresh_control_snapshot(frame.paint_end_ns); - write_state(frame); -} - -void Low_Latency_Feedback_Controller::on_frame_superseded(Frame_Lifecycle_Record& frame) { - ++superseded_frame_count; - if (consumer_epoch_active) - ++consumer_epoch.superseded_frames; - frame.outcome = Frame_Outcome::Superseded; - refresh_control_snapshot(frame.superseded_time_ns); - write_state(frame); -} - -void Low_Latency_Feedback_Controller::on_frame_repeated() { - ++repeated_frame_count; - ++performance_counters.painting_repeated_count; - refresh_control_snapshot(); -} - -void Low_Latency_Feedback_Controller::on_frame_underrun() { - ++underrun_count; - ++performance_counters.underrun_count; - refresh_control_snapshot(); -} - -void Low_Latency_Feedback_Controller::on_frame_dropped(Frame_Lifecycle_Record& frame, Frame_Outcome outcome) { - if (outcome == Frame_Outcome::Paint_Acquire_Dropped || outcome == Frame_Outcome::Render_Acquire_Dropped) { - ++underrun_count; - ++performance_counters.underrun_count; - } - frame.outcome = outcome; - refresh_control_snapshot(frame.superseded_time_ns ? frame.superseded_time_ns : frame.render_end_ns); - write_state(frame); -} - -void Low_Latency_Feedback_Controller::observe_runtime(std::uint64_t now_ns, bool backlog_pressure, bool dirty, bool render_active) { - if (render_active) - exit_producer_starvation(now_ns, "render_active"); - if (backlog_pressure) { - if (!consumer_epoch_active) { - consumer_epoch = {}; - consumer_epoch.begin_ns = now_ns; - consumer_epoch.continuously_backlogged = true; - consumer_epoch_active = true; - } - refresh_control_snapshot(now_ns); - return; - } - if (consumer_epoch_active) { - consumer_epoch.end_ns = now_ns; - consumer_epoch.continuously_backlogged = false; - bool late = next_render_time && now_ns > next_render_time; - std::uint64_t base_period = std::max(state_value.user_period_ns, render_service_period.srtt_ns()); - if (!base_period) - base_period = 16666667; - std::uint64_t threshold = static_cast(static_cast(base_period) * Producer_Starvation_Factor); - bool starvation_candidate = dirty && !render_active && late; - if (starvation_candidate) { - if (!producer_starvation_candidate_since_ns) - producer_starvation_candidate_since_ns = now_ns; - if (now_ns >= producer_starvation_candidate_since_ns - && now_ns - producer_starvation_candidate_since_ns >= threshold) { - consumer_epoch.producer_starved = true; - enter_producer_starvation(now_ns, "dirty_without_render_after_deadline"); - } - } - else { - producer_starvation_candidate_since_ns = 0; - } - } - refresh_control_snapshot(now_ns); -} - -void Low_Latency_Feedback_Controller::note_admission_wait(std::uint64_t remaining_ns, std::uint64_t now_ns) { - if (!admission_wait_begin_ns) - admission_wait_begin_ns = now_ns; - admission_wait_elapsed_ns = now_ns > admission_wait_begin_ns ? now_ns - admission_wait_begin_ns : 0; - admission_wait_remaining_ns = remaining_ns; - last_decision = Low_Latency_Feedback_Decision::Hold_User_Period; - decision_reason = "admission_wait"; - decision_timestamp_ns = now_ns; - refresh_control_snapshot(now_ns); -} - -void Low_Latency_Feedback_Controller::note_render_admitted(std::uint64_t now_ns) { - if (!admission_wait_begin_ns && !admission_wait_elapsed_ns && !admission_wait_remaining_ns) - return; - admission_wait_elapsed_ns = admission_wait_begin_ns && now_ns > admission_wait_begin_ns ? now_ns - admission_wait_begin_ns : 0; - admission_wait_remaining_ns = 0; - admission_wait_begin_ns = 0; - refresh_control_snapshot(now_ns); -} - -void Low_Latency_Feedback_Controller::cancel_admission_wait(std::uint64_t now_ns) { - if (!admission_wait_begin_ns && !admission_wait_elapsed_ns && !admission_wait_remaining_ns) - return; - admission_wait_begin_ns = 0; - admission_wait_elapsed_ns = 0; - admission_wait_remaining_ns = 0; - refresh_control_snapshot(now_ns); -} - -void Low_Latency_Feedback_Controller::note_update_posted(std::uint64_t now_ns) { - ++performance_counters.update_posted_count; - refresh_control_snapshot(now_ns); -} - -void Low_Latency_Feedback_Controller::note_duplicate_present_request_suppressed(std::uint64_t now_ns) { - ++performance_counters.duplicate_present_request_suppressed_count; - refresh_control_snapshot(now_ns); -} - -void Low_Latency_Feedback_Controller::note_update_ack(std::uint64_t now_ns) { - ++performance_counters.update_ack_count; - refresh_control_snapshot(now_ns); -} - -void Low_Latency_Feedback_Controller::note_update_repost(std::uint64_t now_ns) { - ++performance_counters.update_repost_count; - refresh_control_snapshot(now_ns); -} - -void Low_Latency_Feedback_Controller::note_paint_without_posted_update(std::uint64_t now_ns) { - ++performance_counters.paint_without_posted_update_count; - refresh_control_snapshot(now_ns); -} - -void Low_Latency_Feedback_Controller::note_middle_published(std::uint64_t now_ns) { - exit_producer_starvation(now_ns, "middle_published"); - ++performance_counters.middle_published_count; - if (!consumer_epoch_active) { - consumer_epoch = {}; - consumer_epoch.begin_ns = now_ns; - consumer_epoch.continuously_backlogged = true; - consumer_epoch_active = true; - } - refresh_control_snapshot(now_ns); -} - -void Low_Latency_Feedback_Controller::note_middle_superseded(std::uint64_t now_ns) { - ++performance_counters.middle_superseded_count; - refresh_control_snapshot(now_ns); -} - -void Low_Latency_Feedback_Controller::note_painting_advanced(std::uint64_t now_ns) { - ++performance_counters.painting_advanced_count; - refresh_control_snapshot(now_ns); -} - -Low_Latency_Performance_Diagnostics Low_Latency_Feedback_Controller::performance_diagnostics() const { - Low_Latency_Performance_Diagnostics result = performance_counters; - result.consumer_sample_accepted_count = consumer_sample_accepted_count; - result.consumer_sample_rejected_no_backlog_count = consumer_sample_rejected_no_backlog_count; - result.consumer_sample_rejected_starved_count = consumer_sample_rejected_starved_count; - result.consumer_sample_rejected_no_new_frame_count = consumer_sample_rejected_no_new_frame_count; - result.consumer_sample_rejected_invalid_timestamp_count = consumer_sample_rejected_invalid_timestamp_count; - result.producer_starvation_count = producer_starvation_count; - result.mode_enter_count = mode_enter_count; - result.mode_exit_count = mode_exit_count; - result.painting_repeated_count = repeated_frame_count; - result.underrun_count = underrun_count; - return result; -} - -bool Low_Latency_Feedback_Controller::rate_limit_ready(std::uint64_t now_ns) const { - return next_render_time == 0 || now_ns >= next_render_time; -} - -std::uint64_t Low_Latency_Feedback_Controller::next_render_time_ns() const { - return next_render_time; -} - -const Refresh_Control_Snapshot& Low_Latency_Feedback_Controller::state() const { - return state_value; -} - -void Low_Latency_Feedback_Controller::write_state(Frame_Lifecycle_Record& frame) const { - frame.refresh = state_value; -} - -void Low_Latency_Feedback_Controller::apply_consumer_sample(Consumer_Sample_Result result, std::uint64_t sample_ns, std::uint64_t now_ns) { - last_consumer_sample_result = result; - record_consumer_window_sample(result); - switch (result) { - case Consumer_Sample_Result::Accepted: - if (sample_ns) - accepted_consumer_interval.update(static_cast(sample_ns) / 1000000.0); - ++consumer_sample_accepted_count; - break; - case Consumer_Sample_Result::Rejected_No_Backlog: - ++consumer_sample_rejected_no_backlog_count; - last_decision = Low_Latency_Feedback_Decision::Reject_Consumer_Sample; - decision_reason = "no_continuous_backlog"; - decision_timestamp_ns = now_ns; - break; - case Consumer_Sample_Result::Rejected_Producer_Starved: - ++consumer_sample_rejected_starved_count; - last_decision = Low_Latency_Feedback_Decision::Producer_Starved; - decision_reason = "producer_starvation"; - decision_timestamp_ns = now_ns; - break; - case Consumer_Sample_Result::Rejected_No_New_Frame: - ++consumer_sample_rejected_no_new_frame_count; - last_decision = Low_Latency_Feedback_Decision::Reject_Consumer_Sample; - decision_reason = "no_new_frame"; - decision_timestamp_ns = now_ns; - break; - case Consumer_Sample_Result::Rejected_Invalid_Timestamp: - ++consumer_sample_rejected_invalid_timestamp_count; - last_decision = Low_Latency_Feedback_Decision::Reject_Consumer_Sample; - decision_reason = "invalid_timestamp"; - decision_timestamp_ns = now_ns; - break; - } - evaluate_consumer_mode(now_ns); -} - -void Low_Latency_Feedback_Controller::record_consumer_window_sample(Consumer_Sample_Result result) { - if (consumer_window_count_value < consumer_window.size()) { - std::size_t index = (consumer_window_begin + consumer_window_count_value) % consumer_window.size(); - consumer_window[index] = result; - ++consumer_window_count_value; - } - else { - Consumer_Sample_Result old = consumer_window[consumer_window_begin]; - decrement_window_counter(old, - consumer_window_accepted, - consumer_window_rejected_no_backlog, - consumer_window_rejected_starved, - consumer_window_rejected_no_new_frame, - consumer_window_rejected_invalid); - consumer_window[consumer_window_begin] = result; - consumer_window_begin = (consumer_window_begin + 1) % consumer_window.size(); - } - increment_window_counter(result, - consumer_window_accepted, - consumer_window_rejected_no_backlog, - consumer_window_rejected_starved, - consumer_window_rejected_no_new_frame, - consumer_window_rejected_invalid); -} - -std::uint64_t Low_Latency_Feedback_Controller::consumer_window_count(Consumer_Sample_Result result) const { - switch (result) { - case Consumer_Sample_Result::Accepted: - return consumer_window_accepted; - case Consumer_Sample_Result::Rejected_No_Backlog: - return consumer_window_rejected_no_backlog; - case Consumer_Sample_Result::Rejected_Producer_Starved: - return consumer_window_rejected_starved; - case Consumer_Sample_Result::Rejected_No_New_Frame: - return consumer_window_rejected_no_new_frame; - case Consumer_Sample_Result::Rejected_Invalid_Timestamp: - return consumer_window_rejected_invalid; - } - return 0; -} - -std::uint64_t Low_Latency_Feedback_Controller::consumer_window_total() const { - return static_cast(consumer_window_count_value); -} - -double Low_Latency_Feedback_Controller::consumer_window_ratio() const { - std::uint64_t total = consumer_window_total(); - return total ? static_cast(consumer_window_accepted) / static_cast(total) : 0.0; -} - -double Low_Latency_Feedback_Controller::recent_consumer_acceptance_ratio(std::size_t limit) const { - if (!consumer_window_count_value || !limit) - return 0.0; - std::size_t count = std::min(limit, consumer_window_count_value); - std::uint64_t accepted = 0; - for (std::size_t i = 0; i < count; ++i) { - std::size_t offset = consumer_window_count_value - 1 - i; - std::size_t index = (consumer_window_begin + offset) % consumer_window.size(); - if (consumer_window[index] == Consumer_Sample_Result::Accepted) - ++accepted; - } - return static_cast(accepted) / static_cast(count); -} - -Consumer_Confidence Low_Latency_Feedback_Controller::current_consumer_confidence() const { - std::uint64_t total = consumer_window_total(); - double ratio = consumer_window_ratio(); - if (total >= 16 && consumer_window_accepted >= 8 && ratio >= 0.75) - return Consumer_Confidence::High; - if (total >= 8 && consumer_window_accepted >= 4 && ratio >= 0.50) - return Consumer_Confidence::Medium; - return Consumer_Confidence::Low; -} - -void Low_Latency_Feedback_Controller::evaluate_consumer_mode(std::uint64_t now_ns) { - std::uint64_t total = consumer_window_total(); - double ratio = consumer_window_ratio(); - std::uint64_t user_period_ns = state_value.user_period_ns; - std::uint64_t display_ns = accepted_consumer_interval.srtt_ns(); - std::uint64_t render_ns = render_service_period.srtt_ns(); - bool high_confidence = current_consumer_confidence() == Consumer_Confidence::High; - bool consumer_limited = high_confidence - && user_period_ns - && display_ns > static_cast(static_cast(user_period_ns) * Consumer_Display_User_Factor) - && (!render_ns || render_ns < static_cast(static_cast(display_ns) * Render_Service_Display_Factor)); - - if (production_mode == Low_Latency_Production_Mode::Latency_Eager) { - consumer_recovery_sample_count = 0; - if (consumer_limited) - ++consumer_limited_evaluation_count; - else - consumer_limited_evaluation_count = 0; - if (consumer_limited_evaluation_count >= 2) { - production_mode = Low_Latency_Production_Mode::Consumer_Paced; - stable_bottleneck = Low_Latency_Bottleneck_State::Consumer_Limited; - candidate_bottleneck = Low_Latency_Bottleneck_State::Consumer_Limited; - candidate_bottleneck_since_ns = now_ns; - candidate_bottleneck_samples = 0; - ++mode_enter_count; - ++performance_counters.mode_enter_count; - last_decision = Low_Latency_Feedback_Decision::Enter_Consumer_Paced; - decision_reason = "high_window_confidence"; - decision_timestamp_ns = now_ns; - } - else if (total) { - last_decision = Low_Latency_Feedback_Decision::Hold_User_Period; - decision_reason = high_confidence ? "consumer_capacity_not_limited" : "low_consumer_confidence"; - decision_timestamp_ns = now_ns; - } - return; - } - - bool recent_low_ratio = recent_consumer_acceptance_ratio(16) < Accepted_Ratio_Exit; - bool accepted_interval_recovered = user_period_ns && display_ns <= static_cast(static_cast(user_period_ns) * Consumer_Display_Exit_Factor); - bool should_exit = recent_low_ratio || accepted_interval_recovered || producer_starvation_active; - if (should_exit) - ++consumer_recovery_sample_count; - else - consumer_recovery_sample_count = 0; - - if (consumer_recovery_sample_count >= Exit_Recovery_Samples) { - production_mode = Low_Latency_Production_Mode::Latency_Eager; - stable_bottleneck = Low_Latency_Bottleneck_State::User_Limited; - candidate_bottleneck = Low_Latency_Bottleneck_State::User_Limited; - candidate_bottleneck_since_ns = now_ns; - candidate_bottleneck_samples = 0; - consumer_recovery_sample_count = 0; - ++mode_exit_count; - ++performance_counters.mode_exit_count; - last_decision = Low_Latency_Feedback_Decision::Exit_Consumer_Paced; - decision_reason = ratio < Accepted_Ratio_Exit ? "sample_ratio_recovered" : "display_interval_recovered"; - decision_timestamp_ns = now_ns; - } -} - -void Low_Latency_Feedback_Controller::enter_producer_starvation(std::uint64_t now_ns, const char* reason) { - if (producer_starvation_active) - return; - producer_starvation_active = true; - producer_starvation_begin_ns = now_ns; - ++producer_starvation_epoch_id; - ++producer_starvation_count; - ++performance_counters.producer_starvation_count; - last_decision = Low_Latency_Feedback_Decision::Producer_Starved; - decision_reason = reason ? reason : "producer_starvation"; - decision_timestamp_ns = now_ns; -} - -void Low_Latency_Feedback_Controller::exit_producer_starvation(std::uint64_t now_ns, const char* reason) { - if (!producer_starvation_active) - return; - producer_starvation_active = false; - producer_starvation_candidate_since_ns = 0; - producer_starvation_begin_ns = 0; - last_decision = Low_Latency_Feedback_Decision::Hold_User_Period; - decision_reason = reason ? reason : "producer_recovered"; - decision_timestamp_ns = now_ns; -} - -void Low_Latency_Feedback_Controller::refresh_control_snapshot(std::uint64_t now_ns) { - if (now_ns) - latest_event_ns = now_ns; - else - now_ns = latest_event_ns; - - std::uint64_t producer_interval_ns = producer_interval.valid() ? ms_to_ns(producer_interval.safe_ms(0.5)) : 0; - std::uint64_t render_service_ns = render_service_period.valid() ? ms_to_ns(render_service_period.safe_ms(0.5)) : 0; - std::uint64_t paint_service_ns = paint_service_period.valid() ? ms_to_ns(paint_service_period.safe_ms(0.5)) : 0; - std::uint64_t input_period_ns = input_period.valid() ? ms_to_ns(input_period.safe_ms(0.5)) : 0; - std::uint64_t update_to_paint_ns = update_to_paint_period.valid() ? ms_to_ns(update_to_paint_period.safe_ms(0.5)) : 0; - std::uint64_t raw_display_interval_ns = raw_display_interval.valid() ? ms_to_ns(raw_display_interval.safe_ms(0.5)) : 0; - std::uint64_t accepted_consumer_interval_ns = accepted_consumer_interval.valid() ? ms_to_ns(accepted_consumer_interval.safe_ms(0.5)) : 0; - std::uint64_t user_period_ns = state_value.user_period_ns; - std::uint64_t consumer_period_ns = accepted_consumer_interval_ns; - - Low_Latency_Bottleneck_State candidate = classify_bottleneck(input_period_ns, render_service_ns, consumer_period_ns, user_period_ns); - std::uint64_t candidate_period = period_for_bottleneck(candidate, input_period_ns, render_service_ns, consumer_period_ns, user_period_ns); - update_stable_bottleneck(candidate, candidate_period, now_ns, input_period_ns, render_service_ns, consumer_period_ns, user_period_ns); - if (production_mode == Low_Latency_Production_Mode::Consumer_Paced) - stable_bottleneck = Low_Latency_Bottleneck_State::Consumer_Limited; - - std::uint64_t target_period = max_period(input_period_ns, render_service_ns, consumer_period_ns, user_period_ns); - std::uint64_t effective_interval = user_period_ns; - std::uint64_t accepted_total = consumer_sample_accepted_count; - std::uint64_t rejected_total = consumer_sample_rejected_no_backlog_count - + consumer_sample_rejected_starved_count - + consumer_sample_rejected_no_new_frame_count - + consumer_sample_rejected_invalid_timestamp_count; - std::uint64_t sample_total = accepted_total + rejected_total; - double ratio = sample_total ? static_cast(accepted_total) / static_cast(sample_total) : 0.0; - Consumer_Confidence confidence = current_consumer_confidence(); - - if (production_mode == Low_Latency_Production_Mode::Consumer_Paced && confidence == Consumer_Confidence::High && user_period_ns) { - std::uint64_t consumer_capacity = accepted_consumer_interval.srtt_ns(); - std::uint64_t bounded_target = clamp_interval(consumer_capacity, user_period_ns, user_period_ns * Consumer_Interval_Max_Multiplier); - if (!effective_interval_current_ns) - effective_interval_current_ns = user_period_ns; - if (bounded_target > effective_interval_current_ns) { - std::uint64_t rise_limit = std::max(effective_interval_current_ns + 1, static_cast(static_cast(effective_interval_current_ns) * Effective_Rise_Factor)); - effective_interval_current_ns = std::min(bounded_target, rise_limit); - } - else { - std::uint64_t fall_limit = static_cast(static_cast(effective_interval_current_ns) * Effective_Fall_Factor); - effective_interval_current_ns = std::max(bounded_target, fall_limit); - } - effective_interval = effective_interval_current_ns; - } - else { - effective_interval_current_ns = user_period_ns; - effective_interval = user_period_ns; - } - std::uint64_t resolved_next_render_time = next_render_time; - if (last_render_begin_ns && effective_interval) { - std::uint64_t deadline = last_render_begin_ns + effective_interval; - resolved_next_render_time = resolved_next_render_time ? std::max(resolved_next_render_time, deadline) : deadline; - } - else if (!effective_interval) { - resolved_next_render_time = 0; - } - - Refresh_Limit_Source source = source_for_bottleneck(stable_bottleneck); - Low_Latency_Limit_Reason limit_reason = Low_Latency_Limit_Reason::User_Fps; - if (production_mode == Low_Latency_Production_Mode::Consumer_Paced) - limit_reason = Low_Latency_Limit_Reason::Consumer_Capacity; - else if (stable_bottleneck == Low_Latency_Bottleneck_State::Producer_Limited) - limit_reason = Low_Latency_Limit_Reason::Render_Capacity; - - state_value.producer_interval_ns = producer_interval_ns; - state_value.producer_interval_current_ns = producer_interval.current_ns(); - state_value.producer_interval_srtt_ns = producer_interval.srtt_ns(); - state_value.producer_interval_jitter_ns = producer_interval.jitter_ns(); - state_value.input_period_ns = input_period_ns; - state_value.input_interval_current_ns = input_period.current_ns(); - state_value.input_interval_srtt_ns = input_period.srtt_ns(); - state_value.input_jitter_ns = input_period.jitter_ns(); - state_value.render_period_ns = render_service_ns; - state_value.render_service_current_ns = render_service_period.current_ns(); - state_value.render_service_srtt_ns = render_service_period.srtt_ns(); - state_value.render_jitter_ns = render_service_period.jitter_ns(); - state_value.paint_period_ns = paint_service_ns; - state_value.paint_jitter_ns = paint_service_period.jitter_ns(); - state_value.ready_queue_srtt_ns = executor_queue_period.srtt_ns(); - state_value.ready_queue_jitter_ns = executor_queue_period.jitter_ns(); - state_value.gui_srtt_ns = update_to_paint_period.srtt_ns(); - state_value.gui_jitter_ns = update_to_paint_period.jitter_ns(); - state_value.consumption_srtt_ns = accepted_consumer_interval.srtt_ns(); - state_value.consumption_jitter_ns = accepted_consumer_interval.jitter_ns(); - state_value.request_to_update_post_current_ns = request_to_update_post_period.current_ns(); - state_value.request_to_update_post_srtt_ns = request_to_update_post_period.srtt_ns(); - state_value.request_to_update_post_jitter_ns = request_to_update_post_period.jitter_ns(); - state_value.update_to_paint_begin_ns = update_to_paint_ns; - state_value.update_post_to_paint_current_ns = update_to_paint_period.current_ns(); - state_value.update_post_to_paint_srtt_ns = update_to_paint_period.srtt_ns(); - state_value.update_to_paint_jitter_ns = update_to_paint_period.jitter_ns(); - state_value.paint_service_ns = paint_service_ns; - state_value.paint_service_current_ns = paint_service_period.current_ns(); - state_value.paint_service_srtt_ns = paint_service_period.srtt_ns(); - state_value.paint_service_jitter_ns = paint_service_period.jitter_ns(); - state_value.display_frame_interval_ns = raw_display_interval_ns; - state_value.display_frame_interval_jitter_ns = raw_display_interval.jitter_ns(); - state_value.raw_display_interval_current_ns = raw_display_interval.current_ns(); - state_value.raw_display_interval_srtt_ns = raw_display_interval.srtt_ns(); - state_value.raw_display_interval_jitter_ns = raw_display_interval.jitter_ns(); - state_value.accepted_consumer_interval_current_ns = accepted_consumer_interval.current_ns(); - state_value.accepted_consumer_interval_srtt_ns = accepted_consumer_interval.srtt_ns(); - state_value.accepted_consumer_interval_jitter_ns = accepted_consumer_interval.jitter_ns(); - state_value.frame_age_srtt_ns = frame_age_period.srtt_ns(); - state_value.frame_age_jitter_ns = frame_age_period.jitter_ns(); - state_value.source = source; - state_value.candidate_source = source_for_bottleneck(candidate_bottleneck); - state_value.bottleneck = stable_bottleneck; - state_value.stable_bottleneck = stable_bottleneck; - state_value.candidate_bottleneck = candidate_bottleneck; - state_value.candidate_samples = candidate_bottleneck_samples; - state_value.candidate_age_ns = candidate_bottleneck_since_ns && now_ns > candidate_bottleneck_since_ns ? now_ns - candidate_bottleneck_since_ns : 0; - state_value.production_mode = production_mode; - state_value.limit_reason = limit_reason; - state_value.consumer_confidence = confidence; - state_value.last_consumer_sample_result = last_consumer_sample_result; - state_value.last_decision = last_decision; - state_value.decision_reason = decision_reason; - state_value.decision_timestamp_ns = decision_timestamp_ns; - state_value.target_period_ns = target_period; - state_value.effective_min_render_interval_ns = effective_interval; - state_value.next_render_time_ns = resolved_next_render_time; - state_value.admission_wait_elapsed_ns = admission_wait_elapsed_ns; - state_value.admission_wait_remaining_ns = admission_wait_remaining_ns; - state_value.executor_queue_wait_ns = executor_queue_period.srtt_ns(); - state_value.present_wait_ns = raw_display_interval.srtt_ns(); - state_value.consumer_sample_accepted_count = consumer_sample_accepted_count; - state_value.consumer_sample_rejected_no_backlog_count = consumer_sample_rejected_no_backlog_count; - state_value.consumer_sample_rejected_starved_count = consumer_sample_rejected_starved_count; - state_value.consumer_sample_rejected_no_new_frame_count = consumer_sample_rejected_no_new_frame_count; - state_value.consumer_sample_rejected_invalid_timestamp_count = consumer_sample_rejected_invalid_timestamp_count; - state_value.consumer_sample_total_count = consumer_sample_total(state_value); - state_value.consumer_sample_accepted_ratio = ratio; - state_value.consumer_window_size = consumer_window_total(); - state_value.consumer_window_accepted = consumer_window_accepted; - state_value.consumer_window_rejected_no_backlog = consumer_window_rejected_no_backlog; - state_value.consumer_window_rejected_starved = consumer_window_rejected_starved; - state_value.consumer_window_rejected_no_new_frame = consumer_window_rejected_no_new_frame; - state_value.consumer_window_rejected_invalid = consumer_window_rejected_invalid; - state_value.consumer_window_accepted_ratio = consumer_window_ratio(); - state_value.producer_starvation_count = producer_starvation_count; - state_value.producer_starvation_epoch_id = producer_starvation_epoch_id; - state_value.producer_starvation_active = producer_starvation_active; - state_value.mode_enter_count = mode_enter_count; - state_value.mode_exit_count = mode_exit_count; - state_value.update_posted_count = performance_counters.update_posted_count; - state_value.duplicate_present_request_suppressed_count = performance_counters.duplicate_present_request_suppressed_count; - state_value.update_ack_count = performance_counters.update_ack_count; - state_value.update_repost_count = performance_counters.update_repost_count; - state_value.paint_without_posted_update_count = performance_counters.paint_without_posted_update_count; - state_value.middle_published_count = performance_counters.middle_published_count; - state_value.middle_superseded_count = performance_counters.middle_superseded_count; - state_value.painting_advanced_count = performance_counters.painting_advanced_count; - state_value.presented_frame_count = presented_frame_count; - state_value.superseded_frame_count = superseded_frame_count; - state_value.repeated_frame_count = repeated_frame_count; - state_value.underrun_count = underrun_count; - std::uint64_t total_display_attempts = presented_frame_count + superseded_frame_count + repeated_frame_count + underrun_count; - state_value.display_efficiency = total_display_attempts ? static_cast(presented_frame_count) / static_cast(total_display_attempts) : 1.0; - - next_render_time = resolved_next_render_time; -} - -Low_Latency_Bottleneck_State Low_Latency_Feedback_Controller::classify_bottleneck(std::uint64_t input_period_ns, std::uint64_t render_period_ns, std::uint64_t consumer_period_ns, std::uint64_t user_period_ns) const { - std::uint64_t best_period = 0; - Low_Latency_Bottleneck_State best = Low_Latency_Bottleneck_State::Unknown; - auto consider = [&best, &best_period](Low_Latency_Bottleneck_State candidate, std::uint64_t period_ns) { - if (period_ns && period_ns > best_period) { - best_period = period_ns; - best = candidate; - } - }; - consider(Low_Latency_Bottleneck_State::Input_Limited, input_period_ns); - consider(Low_Latency_Bottleneck_State::Producer_Limited, render_period_ns); - if (has_consumer_backpressure()) - consider(Low_Latency_Bottleneck_State::Consumer_Limited, consumer_period_ns); - consider(Low_Latency_Bottleneck_State::User_Limited, user_period_ns); - return best; -} - -std::uint64_t Low_Latency_Feedback_Controller::period_for_bottleneck(Low_Latency_Bottleneck_State bottleneck, std::uint64_t input_period_ns, std::uint64_t render_period_ns, std::uint64_t consumer_period_ns, std::uint64_t user_period_ns) const { - switch (bottleneck) { - case Low_Latency_Bottleneck_State::Input_Limited: - return input_period_ns; - case Low_Latency_Bottleneck_State::Producer_Limited: - return render_period_ns; - case Low_Latency_Bottleneck_State::Consumer_Limited: - return consumer_period_ns; - case Low_Latency_Bottleneck_State::User_Limited: - return user_period_ns; - case Low_Latency_Bottleneck_State::Unknown: - break; - } - return 0; -} - -std::uint64_t Low_Latency_Feedback_Controller::jitter_for_bottleneck(Low_Latency_Bottleneck_State bottleneck) const { - switch (bottleneck) { - case Low_Latency_Bottleneck_State::Input_Limited: - return input_period.jitter_ns(); - case Low_Latency_Bottleneck_State::Producer_Limited: - return render_service_period.jitter_ns(); - case Low_Latency_Bottleneck_State::Consumer_Limited: - return accepted_consumer_interval.jitter_ns(); - case Low_Latency_Bottleneck_State::User_Limited: - case Low_Latency_Bottleneck_State::Unknown: - break; - } - return 0; -} - -bool Low_Latency_Feedback_Controller::has_consumer_backpressure() const { - std::uint64_t accepted = consumer_window_accepted; - double ratio = consumer_window_ratio(); - std::uint64_t user_period_ns = state_value.user_period_ns; - std::uint64_t display_ns = accepted_consumer_interval.srtt_ns(); - std::uint64_t render_ns = render_service_period.srtt_ns(); - return current_consumer_confidence() == Consumer_Confidence::High - && accepted >= Required_Consumer_Samples - && ratio >= Accepted_Ratio_Enter - && user_period_ns - && display_ns > static_cast(static_cast(user_period_ns) * Consumer_Display_User_Factor) - && (!render_ns || render_ns < static_cast(static_cast(display_ns) * Render_Service_Display_Factor)); -} - -void Low_Latency_Feedback_Controller::update_stable_bottleneck(Low_Latency_Bottleneck_State candidate, std::uint64_t candidate_period_ns, std::uint64_t now_ns, std::uint64_t input_period_ns, std::uint64_t render_period_ns, std::uint64_t consumer_period_ns, std::uint64_t user_period_ns) { - if (candidate == Low_Latency_Bottleneck_State::Unknown) - return; - if (stable_bottleneck == Low_Latency_Bottleneck_State::Unknown && candidate != Low_Latency_Bottleneck_State::Consumer_Limited) { - stable_bottleneck = candidate; - candidate_bottleneck = candidate; - candidate_bottleneck_since_ns = now_ns; - candidate_bottleneck_samples = 0; - return; - } - if (candidate == stable_bottleneck) { - candidate_bottleneck = candidate; - candidate_bottleneck_since_ns = now_ns; - candidate_bottleneck_samples = 0; - return; - } - if (candidate != candidate_bottleneck) { - candidate_bottleneck = candidate; - candidate_bottleneck_since_ns = now_ns; - candidate_bottleneck_samples = 1; - return; - } - ++candidate_bottleneck_samples; - std::uint64_t age_ns = candidate_bottleneck_since_ns && now_ns > candidate_bottleneck_since_ns ? now_ns - candidate_bottleneck_since_ns : 0; - std::uint64_t required_age = stable_bottleneck == Low_Latency_Bottleneck_State::Consumer_Limited ? Exit_Consumer_Window_Ns : Enter_Window_Ns; - if (candidate_bottleneck_samples < Switch_Samples || age_ns < required_age) - return; - - std::uint64_t current_period = period_for_bottleneck(stable_bottleneck, input_period_ns, render_period_ns, consumer_period_ns, user_period_ns); - std::uint64_t current_jitter = jitter_for_bottleneck(stable_bottleneck); - double jitter_ratio = current_period ? static_cast(current_jitter) / static_cast(current_period) : 0.0; - double required_ratio = 1.0 + std::max(Enter_Ratio_Floor, jitter_ratio * 2.0); - bool period_can_enter = stable_bottleneck == Low_Latency_Bottleneck_State::Unknown - || current_period == 0 - || static_cast(candidate_period_ns) > static_cast(current_period) * required_ratio; - bool leaving_consumer = stable_bottleneck == Low_Latency_Bottleneck_State::Consumer_Limited && candidate != Low_Latency_Bottleneck_State::Consumer_Limited; - if (!period_can_enter && !leaving_consumer) - return; - - stable_bottleneck = candidate; - candidate_bottleneck_samples = 0; - candidate_bottleneck_since_ns = now_ns; -} - -Refresh_Limit_Source Low_Latency_Feedback_Controller::source_for_bottleneck(Low_Latency_Bottleneck_State bottleneck) { - switch (bottleneck) { - case Low_Latency_Bottleneck_State::Input_Limited: - return Refresh_Limit_Source::Input; - case Low_Latency_Bottleneck_State::Producer_Limited: - return Refresh_Limit_Source::Render; - case Low_Latency_Bottleneck_State::Consumer_Limited: - return Refresh_Limit_Source::Paint; - case Low_Latency_Bottleneck_State::User_Limited: - return Refresh_Limit_Source::User; - case Low_Latency_Bottleneck_State::Unknown: - break; - } - return Refresh_Limit_Source::Unknown; -} - -std::uint64_t Low_Latency_Feedback_Controller::ms_to_ns(double ms) { - if (ms <= 0.0 || !std::isfinite(ms)) - return 0; - return static_cast(ms * 1000000.0); -} - -std::uint64_t Low_Latency_Feedback_Controller::clamp_interval(std::uint64_t value, std::uint64_t low, std::uint64_t high) { - if (!value) - return low; - return std::min(std::max(value, low), high); -} - -} // namespace renderive diff --git a/Core/flow/low_latency/Low_Latency_Feedback_Controller.h b/Core/flow/low_latency/Low_Latency_Feedback_Controller.h deleted file mode 100644 index 517e818..0000000 --- a/Core/flow/low_latency/Low_Latency_Feedback_Controller.h +++ /dev/null @@ -1,145 +0,0 @@ -#pragma once -#include "../../architecture/Frame_Scheduler.h" -#include -#include -#include - -namespace renderive { - -struct Low_Latency_Smoothed_Interval { - double current_ms{}; - double srtt_ms{}; - double jitter_ms{}; - std::uint64_t sample_count{}; - void update(double sample_ms); - void update_wait(double sample_ms); - [[nodiscard]] double safe_ms(double jitter_factor = 2.0) const; - [[nodiscard]] std::uint64_t srtt_ns() const; - [[nodiscard]] std::uint64_t jitter_ns() const; - [[nodiscard]] std::uint64_t current_ns() const; - [[nodiscard]] bool valid() const; -}; - -struct Low_Latency_Frame_Attempt_Accumulator { - std::uint64_t render_role_busy_count{}; - std::uint64_t publish_dropped_count{}; - std::uint64_t paint_role_busy_count{}; -}; - -class Low_Latency_Feedback_Controller { -public: - void reset(); - void set_max_render_fps(double fps); - void on_input_changed(std::uint64_t now_ns, std::uint64_t input_version = 0, Dirty_Reason reason = Dirty_Reason::Core_Data); - void on_render_started(Frame_Lifecycle_Record& frame, std::uint64_t now_ns); - void on_render_completed(Frame_Lifecycle_Record& frame); - void on_frame_presented(Frame_Lifecycle_Record& frame); - void on_frame_superseded(Frame_Lifecycle_Record& frame); - void on_frame_repeated(); - void on_frame_underrun(); - void on_frame_dropped(Frame_Lifecycle_Record& frame, Frame_Outcome outcome); - void observe_runtime(std::uint64_t now_ns, bool backlog_pressure, bool dirty, bool render_active); - void note_admission_wait(std::uint64_t remaining_ns, std::uint64_t now_ns); - void note_render_admitted(std::uint64_t now_ns); - void cancel_admission_wait(std::uint64_t now_ns); - void note_update_posted(std::uint64_t now_ns); - void note_duplicate_present_request_suppressed(std::uint64_t now_ns); - void note_update_ack(std::uint64_t now_ns); - void note_update_repost(std::uint64_t now_ns); - void note_paint_without_posted_update(std::uint64_t now_ns); - void note_middle_published(std::uint64_t now_ns); - void note_middle_superseded(std::uint64_t now_ns); - void note_painting_advanced(std::uint64_t now_ns); - [[nodiscard]] Low_Latency_Performance_Diagnostics performance_diagnostics() const; - [[nodiscard]] bool rate_limit_ready(std::uint64_t now_ns) const; - [[nodiscard]] std::uint64_t next_render_time_ns() const; - [[nodiscard]] const Refresh_Control_Snapshot& state() const; - void write_state(Frame_Lifecycle_Record& frame) const; - -private: - void refresh_control_snapshot(std::uint64_t now_ns = 0); - void apply_consumer_sample(Consumer_Sample_Result result, std::uint64_t sample_ns, std::uint64_t now_ns); - void record_consumer_window_sample(Consumer_Sample_Result result); - [[nodiscard]] std::uint64_t consumer_window_count(Consumer_Sample_Result result) const; - [[nodiscard]] std::uint64_t consumer_window_total() const; - [[nodiscard]] double consumer_window_ratio() const; - [[nodiscard]] double recent_consumer_acceptance_ratio(std::size_t limit) const; - [[nodiscard]] Consumer_Confidence current_consumer_confidence() const; - void evaluate_consumer_mode(std::uint64_t now_ns); - void enter_producer_starvation(std::uint64_t now_ns, const char* reason); - void exit_producer_starvation(std::uint64_t now_ns, const char* reason); - [[nodiscard]] Low_Latency_Bottleneck_State classify_bottleneck(std::uint64_t input_period_ns, std::uint64_t render_period_ns, std::uint64_t consumer_period_ns, std::uint64_t user_period_ns) const; - [[nodiscard]] std::uint64_t period_for_bottleneck(Low_Latency_Bottleneck_State bottleneck, std::uint64_t input_period_ns, std::uint64_t render_period_ns, std::uint64_t consumer_period_ns, std::uint64_t user_period_ns) const; - [[nodiscard]] std::uint64_t jitter_for_bottleneck(Low_Latency_Bottleneck_State bottleneck) const; - [[nodiscard]] bool has_consumer_backpressure() const; - void update_stable_bottleneck(Low_Latency_Bottleneck_State candidate, std::uint64_t candidate_period_ns, std::uint64_t now_ns, std::uint64_t input_period_ns, std::uint64_t render_period_ns, std::uint64_t consumer_period_ns, std::uint64_t user_period_ns); - static Refresh_Limit_Source source_for_bottleneck(Low_Latency_Bottleneck_State bottleneck); - static std::uint64_t ms_to_ns(double ms); - static std::uint64_t clamp_interval(std::uint64_t value, std::uint64_t low, std::uint64_t high); - - Refresh_Control_Snapshot state_value; - Low_Latency_Smoothed_Interval producer_interval; - Low_Latency_Smoothed_Interval render_service_period; - Low_Latency_Smoothed_Interval paint_service_period; - Low_Latency_Smoothed_Interval input_period; - Low_Latency_Smoothed_Interval executor_queue_period; - Low_Latency_Smoothed_Interval request_to_update_post_period; - Low_Latency_Smoothed_Interval update_to_paint_period; - Low_Latency_Smoothed_Interval raw_display_interval; - Low_Latency_Smoothed_Interval accepted_consumer_interval; - Low_Latency_Smoothed_Interval frame_age_period; - Low_Latency_Bottleneck_State stable_bottleneck = Low_Latency_Bottleneck_State::Unknown; - Low_Latency_Bottleneck_State candidate_bottleneck = Low_Latency_Bottleneck_State::Unknown; - std::uint64_t candidate_bottleneck_since_ns{}; - std::uint64_t candidate_bottleneck_samples{}; - std::uint64_t latest_input_version{}; - std::uint64_t last_input_change_ns{}; - std::uint64_t last_render_begin_ns{}; - std::uint64_t last_render_start_ns{}; - std::uint64_t last_consume_sample_ns{}; - std::uint64_t last_consumed_frame_id{}; - std::uint64_t latest_event_ns{}; - std::uint64_t next_render_time{}; - Consumer_Backlog_Epoch consumer_epoch; - bool consumer_epoch_active{}; - std::uint64_t last_display_sample_ns{}; - static constexpr std::size_t Consumer_Window_Size = 64; - std::array consumer_window{}; - std::size_t consumer_window_begin{}; - std::size_t consumer_window_count_value{}; - std::uint64_t consumer_window_accepted{}; - std::uint64_t consumer_window_rejected_no_backlog{}; - std::uint64_t consumer_window_rejected_starved{}; - std::uint64_t consumer_window_rejected_no_new_frame{}; - std::uint64_t consumer_window_rejected_invalid{}; - std::uint64_t consumer_sample_accepted_count{}; - std::uint64_t consumer_sample_rejected_no_backlog_count{}; - std::uint64_t consumer_sample_rejected_starved_count{}; - std::uint64_t consumer_sample_rejected_no_new_frame_count{}; - std::uint64_t consumer_sample_rejected_invalid_timestamp_count{}; - std::uint64_t producer_starvation_count{}; - bool producer_starvation_active{}; - std::uint64_t producer_starvation_epoch_id{}; - std::uint64_t producer_starvation_candidate_since_ns{}; - std::uint64_t producer_starvation_begin_ns{}; - std::uint64_t mode_enter_count{}; - std::uint64_t mode_exit_count{}; - std::uint64_t consumer_recovery_sample_count{}; - std::uint64_t consumer_limited_evaluation_count{}; - Low_Latency_Production_Mode production_mode = Low_Latency_Production_Mode::Latency_Eager; - Consumer_Sample_Result last_consumer_sample_result = Consumer_Sample_Result::Rejected_No_New_Frame; - Low_Latency_Feedback_Decision last_decision = Low_Latency_Feedback_Decision::Hold_User_Period; - std::string decision_reason = "initial"; - std::uint64_t decision_timestamp_ns{}; - std::uint64_t effective_interval_current_ns{}; - std::uint64_t admission_wait_begin_ns{}; - std::uint64_t admission_wait_elapsed_ns{}; - std::uint64_t admission_wait_remaining_ns{}; - Low_Latency_Performance_Diagnostics performance_counters; - std::uint64_t presented_frame_count{}; - std::uint64_t superseded_frame_count{}; - std::uint64_t repeated_frame_count{}; - std::uint64_t underrun_count{}; -}; - -} // namespace renderive diff --git a/Core/flow/low_latency/Low_Latency_Frame_Flow.cpp b/Core/flow/low_latency/Low_Latency_Frame_Flow.cpp deleted file mode 100644 index 27723dd..0000000 --- a/Core/flow/low_latency/Low_Latency_Frame_Flow.cpp +++ /dev/null @@ -1,422 +0,0 @@ -#include "Low_Latency_Frame_Flow.h" -#include "Low_Latency_Feedback_Controller.h" -#include "Low_Latency_Frame_Pipeline.h" -#include "Low_Latency_Renderable_Runtime.h" -#include "../../architecture/Render_Time.h" -#include "../../architecture/Update_Completion.h" - -namespace renderive { - -namespace { -struct Low_Latency_Render_Surface_Handle final : Render_Frame_Surface_Handle { - Low_Latency_Frame_Lease lease; - std::uint64_t previous_rendered_edit_version{}; -}; - -struct Low_Latency_Present_Surface_Handle final { - explicit Low_Latency_Present_Surface_Handle(Low_Latency_Frame_Lease lease) : lease(std::move(lease)) {} - - [[nodiscard]] Render_Output* output() const { - return lease.frame; - } - - Low_Latency_Frame_Lease lease; -}; - -Low_Latency_Render_Surface_Handle* low_latency_handle(Render_Frame_Surface& surface) { - return static_cast(surface.handle.get()); -} - -Render_Surface_Publish_Result to_surface_result(Low_Latency_Frame_Pipeline::Frame_Publish_Result result) { - return Render_Surface_Publish_Result{ - result.dirty_rect, - std::move(result.superseded_frame_record), - std::move(result.dropped_frame_record), - result.update_request_id, - result.update_request_time, - result.request_present - }; -} - -void record_pipeline_publish_result(Low_Latency_Feedback_Controller& feedback, const Low_Latency_Frame_Pipeline::Frame_Publish_Result& result, std::uint64_t now_ns) { - if (result.middle_published) - feedback.note_middle_published(now_ns); - if (result.superseded_frame_record) - feedback.note_middle_superseded(now_ns); - if (result.update_posted) - feedback.note_update_posted(now_ns); - if (result.duplicate_present_request_suppressed) - feedback.note_duplicate_present_request_suppressed(now_ns); -} -} // namespace - -struct Low_Latency_Frame_Flow::Private { - std::atomic max_render_fps{60.0}; - std::atomic_bool active{false}; - Frame_Flow_Host* host{}; - Low_Latency_Feedback_Controller feedback; - Low_Latency_Frame_Pipeline pipeline; - - void record_feedback_event(std::string event, std::uint64_t now_ns, const Frame_Lifecycle_Record* source_frame = nullptr, const Frame_Flow_Runtime_Snapshot* source_runtime = nullptr, const Refresh_Control_Snapshot* before = nullptr, bool flush = false) { - if (!host || event.empty()) - return; - Frame_Flow_Runtime_Snapshot runtime = source_runtime ? *source_runtime : host->runtime_snapshot(now_ns); - Refresh_Control_Snapshot after = feedback.state(); - auto record = std::make_shared(); - record->event = std::move(event); - if (source_frame) - record->frame = *source_frame; - record->frame.refresh = after; - record->timestamp_ns = now_ns; - record->mode_before = before ? before->production_mode : after.production_mode; - record->mode_after = after.production_mode; - record->effective_interval_before_ns = before ? before->effective_min_render_interval_ns : after.effective_min_render_interval_ns; - record->effective_interval_after_ns = after.effective_min_render_interval_ns; - record->dirty = runtime.dirty; - record->render_active = runtime.render_job_active; - record->middle_has_new_frame = runtime.middle_has_new_frame; - record->paint_active = runtime.paint_active; - record->paint_update_posted = pipeline.paint_update_posted(); - record->backlog_epoch_active = runtime.middle_has_new_frame || record->paint_update_posted || after.consumer_window_size > 0; - record->continuously_backlogged = runtime.middle_has_new_frame || record->paint_update_posted; - record->producer_starved = after.producer_starvation_active; - record->backlog_epoch_id = after.producer_starvation_epoch_id; - record->flush = flush; - host->record_feedback_event(std::move(record)); - } - - void record_state_transition_events(const Refresh_Control_Snapshot& before, std::uint64_t now_ns, const Frame_Lifecycle_Record* frame = nullptr, const Frame_Flow_Runtime_Snapshot* runtime = nullptr) { - const Refresh_Control_Snapshot& after = feedback.state(); - if (!before.producer_starvation_active && after.producer_starvation_active) - record_feedback_event("ProducerStarvationEnter", now_ns, frame, runtime, &before, true); - if (before.producer_starvation_active && !after.producer_starvation_active) - record_feedback_event("ProducerStarvationExit", now_ns, frame, runtime, &before, true); - if (before.production_mode != after.production_mode) { - record_feedback_event(after.production_mode == Low_Latency_Production_Mode::Consumer_Paced ? "EnterConsumerPaced" : "ExitConsumerPaced", - now_ns, - frame, - runtime, - &before, - true); - } - if (before.effective_min_render_interval_ns - && after.effective_min_render_interval_ns - && before.effective_min_render_interval_ns != after.effective_min_render_interval_ns) { - record_feedback_event("EffectiveIntervalChanged", now_ns, frame, runtime, &before, false); - } - } - - void record_publish_events(const Low_Latency_Frame_Pipeline::Frame_Publish_Result& result, std::uint64_t now_ns) { - if (result.middle_published) - record_feedback_event("MiddlePublished", now_ns); - if (result.superseded_frame_record) - record_feedback_event("MiddleSuperseded", now_ns, result.superseded_frame_record.get(), nullptr, nullptr, true); - if (result.update_posted) - record_feedback_event("UpdatePosted", now_ns); - if (result.duplicate_present_request_suppressed) - record_feedback_event("DuplicatePresentRequestSuppressed", now_ns); - } - - void evaluate(std::uint64_t now_ns) { - if (!host) - return; - feedback.set_max_render_fps(max_render_fps.load(std::memory_order_acquire)); - Frame_Flow_Runtime_Snapshot runtime = host->runtime_snapshot(now_ns); - if (!active.load(std::memory_order_acquire) || runtime.destroying) { - feedback.cancel_admission_wait(runtime.now_ns); - host->cancel_render_deadline(); - return; - } - bool update_posted = pipeline.paint_update_posted(); - Refresh_Control_Snapshot before_observe = feedback.state(); - feedback.observe_runtime(runtime.now_ns, runtime.middle_has_new_frame || update_posted || runtime.paint_active, runtime.dirty, runtime.render_job_active); - record_state_transition_events(before_observe, runtime.now_ns, nullptr, &runtime); - if (runtime.middle_has_new_frame && !runtime.paint_active && !update_posted) { - Refresh_Control_Snapshot before_repost = feedback.state(); - feedback.note_update_repost(runtime.now_ns); - record_feedback_event("UpdateRepost", runtime.now_ns, nullptr, &runtime, &before_repost); - record_state_transition_events(before_repost, runtime.now_ns, nullptr, &runtime); - host->request_paint_from_middle(); - } - if (!runtime.dirty || runtime.render_job_active) { - if (!runtime.dirty) { - feedback.cancel_admission_wait(runtime.now_ns); - host->cancel_render_deadline(); - } - return; - } - std::uint64_t next_ns = feedback.next_render_time_ns(); - if (next_ns && !feedback.rate_limit_ready(runtime.now_ns)) { - Refresh_Control_Snapshot before_wait = feedback.state(); - feedback.note_admission_wait(next_ns > runtime.now_ns ? next_ns - runtime.now_ns : 0, runtime.now_ns); - record_feedback_event("AdmissionDeadlineWait", runtime.now_ns, nullptr, &runtime, &before_wait); - record_state_transition_events(before_wait, runtime.now_ns, nullptr, &runtime); - host->schedule_render_deadline(next_ns); - return; - } - Refresh_Control_Snapshot before_admit = feedback.state(); - if (!host->try_render_once()) { - feedback.cancel_admission_wait(runtime.now_ns); - return; - } - feedback.note_render_admitted(runtime.now_ns); - record_feedback_event("RenderAdmitted", runtime.now_ns, nullptr, &runtime, &before_admit); - record_state_transition_events(before_admit, runtime.now_ns, nullptr, &runtime); - } -}; - -static constexpr double k_default_max_fps = 60.0; - -Low_Latency_Frame_Flow::Low_Latency_Frame_Flow() - : d(std::make_unique()) {} - -Low_Latency_Frame_Flow::~Low_Latency_Frame_Flow() = default; - -double Low_Latency_Frame_Flow::max_render_fps() const { - return d->max_render_fps.load(std::memory_order_acquire); -} - -void Low_Latency_Frame_Flow::set_max_render_fps(double fps) { - if (fps <= 0.0) - fps = k_default_max_fps; - d->max_render_fps.store(fps, std::memory_order_release); -} - -Refresh_Control_Snapshot Low_Latency_Frame_Flow::feedback_state() const { - return d->feedback.state(); -} - -Low_Latency_Diagnostics Low_Latency_Frame_Flow::low_latency_diagnostics() const { - return Low_Latency_Diagnostics{ - d->active.load(std::memory_order_acquire), - max_render_fps(), - d->feedback.state(), - d->feedback.performance_diagnostics() - }; -} - -void Low_Latency_Frame_Flow::attach(Frame_Flow_Host& host) { - d->host = &host; -} - -std::unique_ptr Low_Latency_Frame_Flow::create_renderable_runtime(Renderable& owner) const { - return make_low_latency_renderable_runtime(owner); -} - -Frame_Flow_Runtime_Snapshot Low_Latency_Frame_Flow::runtime_snapshot(std::uint64_t now_ns, bool destroying) const { - return { - destroying, - d->pipeline.has_unrendered_edit(), - d->pipeline.render_job_state != Frame_Job_State::Idle, - d->pipeline.middle_frame_ready(), - d->pipeline.painting_frame_in_use(), - now_ns - }; -} - -void Low_Latency_Frame_Flow::advance_edit_version() { - d->pipeline.advance_edit_version(); -} - -void Low_Latency_Frame_Flow::cancel_all_update_states() { - d->pipeline.cancel_all_update_states(); -} - -Render_Frame_Surface Low_Latency_Frame_Flow::begin_render_frame() { - auto frame_lease = d->pipeline.try_acquire_render_frame(); - if (!frame_lease) - return {}; - auto handle = std::make_unique(); - handle->previous_rendered_edit_version = d->pipeline.rendered_edit_version; - handle->lease = std::move(frame_lease); - d->pipeline.render_job_state = Frame_Job_State::Running; - d->pipeline.rendered_edit_version = d->pipeline.edit_version.load(std::memory_order_acquire); - auto* output = handle->lease.frame; - output->render_complete.store(false, std::memory_order_release); - output->image.clear(); - std::uint64_t input_version = d->pipeline.rendered_edit_version; - return Render_Frame_Surface(output, input_version, std::move(handle)); -} - -void Low_Latency_Frame_Flow::cancel_render_frame(Render_Frame_Surface&& surface) { - if (auto* handle = low_latency_handle(surface)) - handle->lease.reset(); - d->pipeline.render_job_state = Frame_Job_State::Idle; -} - -void Low_Latency_Frame_Flow::discard_render_frame(Render_Frame_Surface&& surface) { - if (auto* handle = low_latency_handle(surface)) { - handle->lease.reset(); - d->pipeline.rendered_edit_version = handle->previous_rendered_edit_version; - } - d->pipeline.render_job_state = Frame_Job_State::Idle; -} - -Render_Surface_Publish_Result Low_Latency_Frame_Flow::publish_rendered_frame(Render_Frame_Surface&& surface, std::uint64_t now_ns) { - Render_Surface_Publish_Result result; - if (auto* handle = low_latency_handle(surface)) { - if (handle->lease.frame && handle->lease.frame->metadata) - handle->lease.frame->version.store(handle->lease.frame->metadata->frame_id, std::memory_order_release); - Refresh_Control_Snapshot before_publish = d->feedback.state(); - auto pipeline_result = d->pipeline.try_publish_rendered_frame(std::move(handle->lease), now_ns); - record_pipeline_publish_result(d->feedback, pipeline_result, now_ns); - d->record_publish_events(pipeline_result, now_ns); - d->record_state_transition_events(before_publish, now_ns); - result = to_surface_result(std::move(pipeline_result)); - if (result.dropped_frame_record) { - Refresh_Control_Snapshot before_drop = d->feedback.state(); - d->feedback.on_frame_dropped(*result.dropped_frame_record, result.dropped_frame_record->outcome); - d->record_feedback_event("PublishDropped", now_ns, result.dropped_frame_record.get(), nullptr, &before_drop, true); - d->record_state_transition_events(before_drop, now_ns, result.dropped_frame_record.get()); - } - } - d->pipeline.render_job_state = Frame_Job_State::Idle; - return result; -} - -Render_Surface_Publish_Result Low_Latency_Frame_Flow::request_present(std::uint64_t now_ns) { - auto pipeline_result = d->pipeline.request_present(now_ns); - record_pipeline_publish_result(d->feedback, pipeline_result, now_ns); - d->record_publish_events(pipeline_result, now_ns); - return to_surface_result(std::move(pipeline_result)); -} - -Present_Surface_Lease Low_Latency_Frame_Flow::begin_present(std::uint64_t now_ns, std::shared_ptr& returned_frame_record) { - auto consumed = d->pipeline.acquire_paint_frame(now_ns); - if (consumed.present_request_was_pending) { - d->feedback.note_update_ack(now_ns); - d->record_feedback_event("UpdateAck", now_ns); - } - else if (consumed.paint_without_posted_update) { - d->feedback.note_paint_without_posted_update(now_ns); - d->record_feedback_event("PaintWithoutCorePost", now_ns); - } - d->record_feedback_event("PaintBegin", now_ns); - if (consumed.has_new_frame) { - d->feedback.note_painting_advanced(now_ns); - d->record_feedback_event("PaintingAdvanced", now_ns); - } - returned_frame_record = std::move(consumed.returned_frame_record); - if (consumed.underrun) { - d->feedback.on_frame_underrun(); - d->record_feedback_event("Underrun", now_ns); - } - else if (consumed.lease && !consumed.has_new_frame) { - d->feedback.on_frame_repeated(); - d->record_feedback_event("PaintingRepeated", now_ns); - } - if (!consumed.lease) - return Present_Surface_Lease_Access::empty(consumed.has_new_frame, consumed.present_request_was_pending); - return Present_Surface_Lease_Access::from_handle( - consumed.has_new_frame, - consumed.present_request_was_pending, - std::move(consumed.lease)); -} - -std::shared_ptr Low_Latency_Frame_Flow::end_present(Present_Surface_Lease&& surface, std::uint64_t paint_begin_ns, std::uint64_t paint_end_ns) { - Render_Output* output = Present_Surface_Lease_Access::output(surface); - if (output) { - if (output->metadata) { - output->metadata->paint_begin_ns = paint_begin_ns; - output->metadata->paint_end_ns = paint_end_ns; - output->metadata->presented_frame_id = output->metadata->frame_id; - output->metadata->present_queue_wait_ns = paint_begin_ns > output->metadata->transition_12_ns ? paint_begin_ns - output->metadata->transition_12_ns : 0; - } - complete_update_states(output->presented_update_states, Update_Stage::Presented); - } - Present_Surface_Lease_Access::reset(surface); - return {}; -} - -void Low_Latency_Frame_Flow::activate_view(std::uint64_t now_ns) { - bool was_active = d->active.exchange(true, std::memory_order_acq_rel); - if (!was_active) - d->feedback.reset(); - d->evaluate(now_ns); -} - -void Low_Latency_Frame_Flow::deactivate_view() { - d->active.store(false, std::memory_order_release); - if (d->host) - d->host->cancel_render_deadline(); -} - -bool Low_Latency_Frame_Flow::view_active() const { - return d->active.load(std::memory_order_acquire); -} - -void Low_Latency_Frame_Flow::notify_model_dirty(Dirty_Reason reason, std::uint64_t now_ns) { - Refresh_Control_Snapshot before = d->feedback.state(); - d->feedback.on_input_changed(now_ns, 0, reason); - d->record_feedback_event("InputChanged", now_ns, nullptr, nullptr, &before); - d->record_state_transition_events(before, now_ns); - d->evaluate(now_ns); -} - -void Low_Latency_Frame_Flow::notify_viewport_changed(Size, std::uint64_t now_ns) { - d->evaluate(now_ns); -} - -void Low_Latency_Frame_Flow::notify_render_finished(Frame_Lifecycle_Record& frame, std::uint64_t now_ns) { - if (frame.render_begin_ns) { - Refresh_Control_Snapshot before_start = d->feedback.state(); - d->feedback.on_render_started(frame, frame.render_begin_ns); - d->record_feedback_event("RenderStarted", frame.render_begin_ns, &frame, nullptr, &before_start); - d->record_state_transition_events(before_start, frame.render_begin_ns, &frame); - } - Refresh_Control_Snapshot before_complete = d->feedback.state(); - d->feedback.on_render_completed(frame); - d->record_feedback_event("RenderCompleted", now_ns, &frame, nullptr, &before_complete); - d->record_state_transition_events(before_complete, now_ns, &frame); - d->evaluate(now_ns); -} - -void Low_Latency_Frame_Flow::notify_paint_finished(std::uint64_t now_ns) { - d->evaluate(now_ns); -} - -void Low_Latency_Frame_Flow::notify_frame_returned(Frame_Lifecycle_Record& frame, std::uint64_t now_ns) { - Refresh_Control_Snapshot before = d->feedback.state(); - if (frame.outcome == Frame_Outcome::Superseded) - d->feedback.on_frame_superseded(frame); - else - d->feedback.on_frame_presented(frame); - const Refresh_Control_Snapshot& after = d->feedback.state(); - if (frame.outcome == Frame_Outcome::Superseded) - d->record_feedback_event("MiddleSuperseded", now_ns, &frame, nullptr, &before, true); - else if (after.last_consumer_sample_result == Consumer_Sample_Result::Accepted) - d->record_feedback_event("ConsumerSampleAccepted", now_ns, &frame, nullptr, &before); - else - d->record_feedback_event("ConsumerSampleRejected", now_ns, &frame, nullptr, &before); - d->record_state_transition_events(before, now_ns, &frame); - d->evaluate(now_ns); -} - -void Low_Latency_Frame_Flow::notify_timer_fired(std::uint64_t now_ns) { - d->evaluate(now_ns); -} - -void Low_Latency_Frame_Flow::drain_frame_update_states(std::pmr::vector>&) {} - -Flow_Diagnostics Low_Latency_Frame_Flow::diagnostics() const { - Low_Latency_Diagnostics typed = low_latency_diagnostics(); - return Flow_Diagnostics{ - Common_Flow_Diagnostics{typed.active}, - typed - }; -} - -void Low_Latency_Frame_Flow::shutdown() { - deactivate_view(); - d->host = nullptr; -} - -Low_Latency_Frame_Pipeline& Low_Latency_Frame_Flow::pipeline_for_test() { - return d->pipeline; -} - -const Low_Latency_Frame_Pipeline& Low_Latency_Frame_Flow::pipeline_for_test() const { - return d->pipeline; -} - -} // namespace renderive diff --git a/Core/flow/low_latency/Low_Latency_Frame_Flow.h b/Core/flow/low_latency/Low_Latency_Frame_Flow.h deleted file mode 100644 index 384d142..0000000 --- a/Core/flow/low_latency/Low_Latency_Frame_Flow.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -#include "../Frame_Flow.h" -#include -#include -#include - -namespace renderive { - -class Low_Latency_Frame_Pipeline; -struct Low_Latency_Frame_Flow_Test_Probe; - -class Low_Latency_Frame_Flow final : public Frame_Flow { -public: - Low_Latency_Frame_Flow(); - ~Low_Latency_Frame_Flow() override; - - [[nodiscard]] double max_render_fps() const; - void set_max_render_fps(double fps); - [[nodiscard]] Refresh_Control_Snapshot feedback_state() const; - [[nodiscard]] Low_Latency_Diagnostics low_latency_diagnostics() const; - - void attach(Frame_Flow_Host& host) override; - [[nodiscard]] std::unique_ptr create_renderable_runtime(Renderable& owner) const override; - [[nodiscard]] Frame_Flow_Runtime_Snapshot runtime_snapshot(std::uint64_t now_ns, bool destroying) const override; - void advance_edit_version() override; - void cancel_all_update_states() override; - [[nodiscard]] Render_Frame_Surface begin_render_frame() override; - void cancel_render_frame(Render_Frame_Surface&& surface) override; - void discard_render_frame(Render_Frame_Surface&& surface) override; - [[nodiscard]] Render_Surface_Publish_Result publish_rendered_frame(Render_Frame_Surface&& surface, std::uint64_t now_ns) override; - [[nodiscard]] Render_Surface_Publish_Result request_present(std::uint64_t now_ns) override; - [[nodiscard]] Present_Surface_Lease begin_present(std::uint64_t now_ns, std::shared_ptr& returned_frame_record) override; - [[nodiscard]] std::shared_ptr end_present(Present_Surface_Lease&& surface, std::uint64_t paint_begin_ns, std::uint64_t paint_end_ns) override; - void activate_view(std::uint64_t now_ns) override; - void deactivate_view() override; - [[nodiscard]] bool view_active() const override; - void notify_model_dirty(std::uint64_t now_ns) { - notify_model_dirty(Dirty_Reason::Core_Data, now_ns); - } - void notify_model_dirty(Dirty_Reason reason, std::uint64_t now_ns) override; - void notify_viewport_changed(Size viewport, std::uint64_t now_ns) override; - void notify_render_finished(Frame_Lifecycle_Record& frame, std::uint64_t now_ns) override; - void notify_paint_finished(std::uint64_t now_ns) override; - void notify_frame_returned(Frame_Lifecycle_Record& frame, std::uint64_t now_ns) override; - void notify_timer_fired(std::uint64_t now_ns) override; - void drain_frame_update_states(std::pmr::vector>& output) override; - [[nodiscard]] Flow_Diagnostics diagnostics() const override; - void shutdown() override; - -private: - friend struct Low_Latency_Frame_Flow_Test_Probe; - [[nodiscard]] Low_Latency_Frame_Pipeline& pipeline_for_test(); - [[nodiscard]] const Low_Latency_Frame_Pipeline& pipeline_for_test() const; - - struct Private; - std::unique_ptr d; -}; - -} // namespace renderive diff --git a/Core/flow/low_latency/Low_Latency_Frame_Pipeline.cpp b/Core/flow/low_latency/Low_Latency_Frame_Pipeline.cpp deleted file mode 100644 index e7aa439..0000000 --- a/Core/flow/low_latency/Low_Latency_Frame_Pipeline.cpp +++ /dev/null @@ -1,263 +0,0 @@ -#include "Low_Latency_Frame_Pipeline.h" -#include "../../architecture/Update_Completion.h" -#include "../../base/Assert.h" -#include -#include - -namespace renderive { -namespace { - -bool has_complete_frame(const Low_Latency_Frame* frame) { - return frame && frame->version.load(std::memory_order_acquire) != 0 - && frame->render_complete.load(std::memory_order_acquire) - && frame->metadata && frame->metadata->render_end_ns - && !frame->image.empty(); -} - -} // namespace - -Low_Latency_Frame::Low_Latency_Frame() - = default; - -Low_Latency_Frame_Lease::Low_Latency_Frame_Lease(Low_Latency_Frame* buffer, Triple_Role_Buffer_Control* control, Triple_Buffer_Lease lease) - : frame(buffer), control(control), lease(lease) {} - -Low_Latency_Frame_Lease::Low_Latency_Frame_Lease(Low_Latency_Frame_Lease&& other) noexcept - : frame(std::exchange(other.frame, nullptr)), - control(std::exchange(other.control, nullptr)), - lease(std::exchange(other.lease, Triple_Buffer_Lease{})) {} - -Low_Latency_Frame_Lease& Low_Latency_Frame_Lease::operator=(Low_Latency_Frame_Lease&& other) noexcept { - if (this == &other) - return *this; - reset(); - frame = std::exchange(other.frame, nullptr); - control = std::exchange(other.control, nullptr); - lease = std::exchange(other.lease, Triple_Buffer_Lease{}); - return *this; -} - -Low_Latency_Frame_Lease::~Low_Latency_Frame_Lease() { - reset(); -} - -void Low_Latency_Frame_Lease::reset() { - if (!control) - return; - control->unmark_use(lease); - frame = nullptr; - control = nullptr; - lease = {}; -} - -Low_Latency_Frame_Pipeline::Low_Latency_Frame_Pipeline() { - frame_control.reset(); -} - -Low_Latency_Frame_Pipeline::~Low_Latency_Frame_Pipeline() { - cancel_all_update_states(); -} - -void Low_Latency_Frame_Pipeline::advance_edit_version() { - edit_version.fetch_add(1, std::memory_order_release); -} - -bool Low_Latency_Frame_Pipeline::has_unrendered_edit() const { - return buffer_version_newer(edit_version.load(std::memory_order_acquire), rendered_edit_version); -} - -Low_Latency_Frame* Low_Latency_Frame_Pipeline::frame_by_index(std::uint8_t index) { - return &frames[index]; -} - -const Low_Latency_Frame* Low_Latency_Frame_Pipeline::frame_by_index(std::uint8_t index) const { - return &frames[index]; -} - -Low_Latency_Frame* Low_Latency_Frame_Pipeline::frame_by_role(std::uint8_t role) { - return frame_by_index(frame_control.read_view()[role]); -} - -const Low_Latency_Frame* Low_Latency_Frame_Pipeline::frame_by_role(std::uint8_t role) const { - return frame_by_index(frame_control.read_view()[role]); -} - -Low_Latency_Frame_Lease Low_Latency_Frame_Pipeline::try_acquire_render_frame() { - auto record = frame_control.try_acquire_role(Frame_Rendering); - if (record.result != Triple_Buffer_Result::Success) { - ++attempt_counters.render_role_busy_count; - return {}; - } - return {frame_by_index(record.lease.index), &frame_control, record.lease}; -} - -Low_Latency_Frame_Pipeline::Frame_Publish_Result Low_Latency_Frame_Pipeline::try_publish_rendered_frame(Low_Latency_Frame_Lease rendering_lease, std::uint64_t now_ns) { - Frame_Publish_Result result; - if (!rendering_lease) - return result; - Low_Latency_Frame* rendered = rendering_lease.frame; - if (!has_complete_frame(rendered)) { - if (rendered->metadata) - rendered->metadata->outcome = Frame_Outcome::Publish_Dropped; - result.dropped_frame_record = rendered->metadata; - supersede_update_states(rendered->presented_update_states); - ++attempt_counters.publish_dropped_count; - return result; - } - - auto swap = frame_control.try_swap_role_with_left_lease(Frame_Rendering, Frame_Middle, rendering_lease.lease, [this](const Triple_Buffer_View& view) { - const Low_Latency_Frame* rendering = frame_by_index(view[Frame_Rendering]); - const Low_Latency_Frame* middle = frame_by_index(view[Frame_Middle]); - return has_complete_frame(rendering) - && buffer_version_newer(rendering->version.load(std::memory_order_acquire), middle->version.load(std::memory_order_acquire)); - }, [this, &result, now_ns](const Triple_Buffer_View& old_view, const Triple_Buffer_View& new_view) { - Low_Latency_Frame* rendered = frame_by_index(old_view[Frame_Rendering]); - Low_Latency_Frame* old_middle = frame_by_index(old_view[Frame_Middle]); - const Low_Latency_Frame* painting = frame_by_index(old_view[Frame_Painting]); - bool supersede_middle = buffer_version_newer(old_middle->version.load(std::memory_order_acquire), painting->version.load(std::memory_order_acquire)); - - rendered->metadata->transition_12_ns = now_ns; - rendered->metadata->ready_queue_wait_ns = now_ns > rendered->metadata->render_end_ns ? now_ns - rendered->metadata->render_end_ns : 0; - - if (supersede_middle && old_middle->metadata && old_middle->metadata->frame_id) { - old_middle->metadata->superseded_time_ns = now_ns; - old_middle->metadata->outcome = Frame_Outcome::Superseded; - result.superseded_frame_record = old_middle->metadata; - supersede_update_states(old_middle->presented_update_states); - } - - Low_Latency_Frame* next_rendering = frame_by_index(new_view[Frame_Rendering]); - next_rendering->metadata = std::make_shared(); - next_rendering->metadata->render_role_enter_ns = now_ns; - }); - if (swap.result != Triple_Buffer_Result::Success) { - rendered->metadata->outcome = Frame_Outcome::Publish_Dropped; - result.dropped_frame_record = rendered->metadata; - supersede_update_states(rendered->presented_update_states); - ++attempt_counters.publish_dropped_count; - return result; - } - - rendering_lease.reset(); - has_published_frame.store(true, std::memory_order_release); - result.middle_published = true; - Frame_Publish_Result update_result = request_present(now_ns); - update_result.superseded_frame_record = result.superseded_frame_record; - update_result.dropped_frame_record = result.dropped_frame_record; - update_result.middle_published = true; - return update_result; -} - -Low_Latency_Frame_Pipeline::Frame_Publish_Result Low_Latency_Frame_Pipeline::request_present(std::uint64_t now_ns) { - Frame_Publish_Result result; - Triple_Buffer_View view = frame_control.read_view(); - const Low_Latency_Frame* middle = frame_by_index(view[Frame_Middle]); - const Low_Latency_Frame* painting = frame_by_index(view[Frame_Painting]); - if (!has_complete_frame(middle) - || !buffer_version_newer(middle->version.load(std::memory_order_acquire), painting->version.load(std::memory_order_acquire))) - return result; - - if (paint_request_pending.load(std::memory_order_acquire)) { - result.duplicate_present_request_suppressed = true; - return result; - } - result.update_request_id = ++next_present_request_id; - result.update_request_time = now_ns; - pending_present_request_id.store(result.update_request_id, std::memory_order_release); - pending_present_request_time_ns.store(result.update_request_time, std::memory_order_release); - paint_request_pending.store(true, std::memory_order_release); - Size size = middle->image.size(); - result.dirty_rect = Rect{0, 0, size.width, size.height}; - result.request_present = true; - result.update_posted = true; - return result; -} - -Low_Latency_Frame_Pipeline::Frame_Consume_Result Low_Latency_Frame_Pipeline::acquire_paint_frame(std::uint64_t now_ns) { - Frame_Consume_Result result; - result.present_request_was_pending = paint_request_pending.exchange(false, std::memory_order_acq_rel); - result.paint_without_posted_update = !result.present_request_was_pending; - - for (;;) { - bool attempted_middle_swap = false; - Triple_Buffer_Result middle_swap_result = Triple_Buffer_Result::Condition_Failed; - if (middle_frame_ready()) { - attempted_middle_swap = true; - auto swap = frame_control.try_swap_role(Frame_Middle, Frame_Painting, [this](const Triple_Buffer_View& view) { - const Low_Latency_Frame* middle = frame_by_index(view[Frame_Middle]); - const Low_Latency_Frame* painting = frame_by_index(view[Frame_Painting]); - return buffer_version_newer(middle->version.load(std::memory_order_acquire), painting->version.load(std::memory_order_acquire)); - }, [this, &result, now_ns](const Triple_Buffer_View& old_view, const Triple_Buffer_View&) { - Low_Latency_Frame* old_painting = frame_by_index(old_view[Frame_Painting]); - Low_Latency_Frame* new_painting = frame_by_index(old_view[Frame_Middle]); - result.has_new_frame = true; - if (old_painting->metadata && old_painting->metadata->frame_id && old_painting->metadata->paint_end_ns) { - old_painting->metadata->transition_32_ns = now_ns; - old_painting->metadata->paint_return_wait_ns = now_ns > old_painting->metadata->paint_end_ns ? now_ns - old_painting->metadata->paint_end_ns : 0; - result.returned_frame_record = old_painting->metadata; - } - new_painting->metadata->transition_23_ns = now_ns; - new_painting->metadata->present_queue_wait_ns = now_ns > new_painting->metadata->transition_12_ns ? now_ns - new_painting->metadata->transition_12_ns : 0; - std::uint64_t request_id = pending_present_request_id.load(std::memory_order_acquire); - if (request_id) { - new_painting->metadata->update_request_id = request_id; - new_painting->metadata->update_request_time_ns = pending_present_request_time_ns.load(std::memory_order_acquire); - new_painting->metadata->update_posted_ns = new_painting->metadata->update_request_time_ns; - } - }); - middle_swap_result = swap.result; - } - - auto record = frame_control.acquire_role(Frame_Painting); - if (record.result == Triple_Buffer_Result::Success) { - result.lease = {frame_by_index(record.lease.index), &frame_control, record.lease}; - result.has_valid_frame = has_complete_frame(result.lease.frame); - if (result.has_valid_frame) - { - return result; - } - result.lease.reset(); - if (!has_published_frame.load(std::memory_order_acquire)) { - result.underrun = true; - return result; - } - if (attempted_middle_swap && middle_swap_result != Triple_Buffer_Result::Success) { - std::this_thread::yield(); - continue; - } - ASSERT(false, "Low Latency invariant violated: published frame exists but Painting is empty"); - result.underrun = true; - return result; - } - - result.paint_frame_unavailable = true; - result.underrun = true; - if (record.result == Triple_Buffer_Result::Busy) - ++attempt_counters.paint_role_busy_count; - if (has_published_frame.load(std::memory_order_acquire)) - ASSERT(false, "Low Latency invariant violated: published frame exists but Painting lease is unavailable"); - return result; - } -} - -bool Low_Latency_Frame_Pipeline::middle_frame_ready() const { - const Low_Latency_Frame* middle = frame_by_role(Frame_Middle); - const Low_Latency_Frame* painting = frame_by_role(Frame_Painting); - return has_complete_frame(middle) - && buffer_version_newer(middle->version.load(std::memory_order_acquire), painting->version.load(std::memory_order_acquire)); -} - -bool Low_Latency_Frame_Pipeline::painting_frame_in_use() const { - return frame_control.role_busy(Frame_Painting); -} - -bool Low_Latency_Frame_Pipeline::paint_update_posted() const { - return paint_request_pending.load(std::memory_order_acquire); -} - -void Low_Latency_Frame_Pipeline::cancel_all_update_states() { - for (Low_Latency_Frame& frame : frames) - cancel_update_states(frame.presented_update_states); -} - -} // namespace renderive diff --git a/Core/flow/low_latency/Low_Latency_Frame_Pipeline.h b/Core/flow/low_latency/Low_Latency_Frame_Pipeline.h deleted file mode 100644 index dce77ca..0000000 --- a/Core/flow/low_latency/Low_Latency_Frame_Pipeline.h +++ /dev/null @@ -1,97 +0,0 @@ -#pragma once -#include "../../architecture/Frame_Scheduler.h" -#include "../../architecture/Render_Config.h" -#include "../../base/Geometry.h" -#include "../../base/Object_Semantics.h" -#include "../../render/Render_Output.h" -#include "Low_Latency_Feedback_Controller.h" -#include "Low_Latency_Roles.h" -#include "Triple_Buffer.h" -#include -#include -#include -#include -#include -namespace renderive { -struct Update_State; -class Low_Latency_Frame_Flow; -struct Low_Latency_Frame_Pipeline_Test_Probe; -enum class Frame_Job_State { - Idle, - Running -}; -struct Low_Latency_Frame : Render_Output { - Low_Latency_Frame(); - std::atomic version{0}; -}; -struct Low_Latency_Frame_Lease : Move_Only { - Low_Latency_Frame_Lease() = default; - Low_Latency_Frame_Lease(Low_Latency_Frame* buffer, Triple_Role_Buffer_Control* control, Triple_Buffer_Lease lease); - Low_Latency_Frame_Lease(Low_Latency_Frame_Lease&& other) noexcept; - Low_Latency_Frame_Lease& operator=(Low_Latency_Frame_Lease&& other) noexcept; - ~Low_Latency_Frame_Lease(); - void reset(); - Low_Latency_Frame* frame{}; - Triple_Role_Buffer_Control* control{}; - Triple_Buffer_Lease lease{}; - explicit operator bool() const { - return frame && lease; - } -}; -class Low_Latency_Frame_Pipeline { -public: - struct Frame_Publish_Result { - Rect dirty_rect; - std::shared_ptr superseded_frame_record; - std::shared_ptr dropped_frame_record; - std::uint64_t update_request_id{}; - std::uint64_t update_request_time{}; - bool request_present{}; - bool middle_published{}; - bool update_posted{}; - bool duplicate_present_request_suppressed{}; - }; - struct Frame_Consume_Result { - Low_Latency_Frame_Lease lease; - std::shared_ptr returned_frame_record; - bool has_new_frame{}; - bool present_request_was_pending{}; - bool paint_frame_unavailable{}; - bool has_valid_frame{}; - bool underrun{}; - bool paint_without_posted_update{}; - }; - Low_Latency_Frame_Pipeline(); - ~Low_Latency_Frame_Pipeline(); - void advance_edit_version(); - [[nodiscard]] bool has_unrendered_edit() const; - [[nodiscard]] Low_Latency_Frame* frame_by_index(std::uint8_t index); - [[nodiscard]] const Low_Latency_Frame* frame_by_index(std::uint8_t index) const; - [[nodiscard]] Low_Latency_Frame* frame_by_role(std::uint8_t role); - [[nodiscard]] const Low_Latency_Frame* frame_by_role(std::uint8_t role) const; - [[nodiscard]] Low_Latency_Frame_Lease try_acquire_render_frame(); - [[nodiscard]] Frame_Publish_Result try_publish_rendered_frame(Low_Latency_Frame_Lease rendering_lease, std::uint64_t now_ns); - [[nodiscard]] Frame_Publish_Result request_present(std::uint64_t now_ns); - [[nodiscard]] Frame_Consume_Result acquire_paint_frame(std::uint64_t now_ns); - [[nodiscard]] bool middle_frame_ready() const; - [[nodiscard]] bool painting_frame_in_use() const; - [[nodiscard]] bool paint_update_posted() const; - void cancel_all_update_states(); - -private: - friend class Low_Latency_Frame_Flow; - friend struct Low_Latency_Frame_Pipeline_Test_Probe; - - Triple_Role_Buffer_Control frame_control; - std::array frames; - std::atomic edit_version{1}; - std::uint64_t rendered_edit_version = 0; - Frame_Job_State render_job_state = Frame_Job_State::Idle; - Low_Latency_Frame_Attempt_Accumulator attempt_counters; - std::atomic_bool paint_request_pending{false}; - std::atomic_bool has_published_frame{false}; - std::atomic pending_present_request_id{0}; - std::atomic pending_present_request_time_ns{0}; - std::uint64_t next_present_request_id{}; -}; -} // namespace renderive diff --git a/Core/flow/low_latency/Low_Latency_Renderable_Runtime.h b/Core/flow/low_latency/Low_Latency_Renderable_Runtime.h deleted file mode 100644 index c029187..0000000 --- a/Core/flow/low_latency/Low_Latency_Renderable_Runtime.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#include "../../architecture/Render_Data.h" -#include - -namespace renderive { - -struct Low_Latency_Renderable_Runtime_Data; - -class Low_Latency_Renderable_Runtime final : public Renderable_Runtime { -public: - explicit Low_Latency_Renderable_Runtime(Renderable& owner); - Low_Latency_Renderable_Runtime(const Low_Latency_Renderable_Runtime&) = delete; - Low_Latency_Renderable_Runtime& operator=(const Low_Latency_Renderable_Runtime&) = delete; - ~Low_Latency_Renderable_Runtime() override; - - void set_render_state_copier(Copy_Render_State_Function copier) override; - void cancel_pending_completions() override; - void drain_committed_update_states(std::pmr::vector>& output) override; - Render_State_Read_Guard acquire_state_read(Renderable_State_Role role) override; - Render_State_Read_Guard acquire_render_state_read() override; - Render_State_Read_Guard acquire_edit_state_read() override; - Render_State_Write_Guard acquire_edit_state() override; - Render_Input_Write_Guard acquire_edit_input(bool notify_render) override; - Render_Input_Frame_Guard acquire_input_read(Renderable_Input_Role role) override; - Renderable_Frame_View capture_frame_view() override; - Render_State* state(Renderable_State_Role role) override; - Input_Data* input_data(Renderable_Input_Role role) override; - void clear_render_input() override; - -private: - std::unique_ptr d; -}; - -std::unique_ptr make_low_latency_renderable_runtime(Renderable& owner); - -} // namespace renderive diff --git a/Core/flow/low_latency/Low_Latency_Roles.h b/Core/flow/low_latency/Low_Latency_Roles.h deleted file mode 100644 index 028fbd7..0000000 --- a/Core/flow/low_latency/Low_Latency_Roles.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include - -namespace renderive { - -enum Frame_Role : std::uint8_t { - Frame_Rendering, - Frame_Middle, - Frame_Painting -}; - -enum State_Role : std::uint8_t { - State_Edit, - State_Ready, - State_Render -}; - -enum Input_Role : std::uint8_t { - Input_Edit, - Input_Ready, - Input_Render -}; - -inline bool buffer_version_newer(std::uint64_t a, std::uint64_t b) { - return static_cast(a - b) > 0; -} - -} // namespace renderive diff --git a/Core/flow/low_latency/Render_Data.cpp b/Core/flow/low_latency/Render_Data.cpp deleted file mode 100644 index 8210320..0000000 --- a/Core/flow/low_latency/Render_Data.cpp +++ /dev/null @@ -1,435 +0,0 @@ -#include "Low_Latency_Renderable_Runtime.h" -#include "../../architecture/Renderable.h" -#include "../../base/Memory.h" -#include "Low_Latency_Roles.h" -#include "Triple_Buffer.h" -#include -#include -#include -#include -#include - -namespace renderive { - -std::ostream& operator<<(std::ostream& stream, const Range& range) { - stream << "Range(" << range.origin << ", " << range.target << ")"; - return stream; -} - -namespace { - -struct Input_Buffer_Slot { - Input_Data* data{}; - std::uint64_t version = 0; - bool pending = false; - std::shared_ptr completion_state; -}; - -std::uint8_t state_role_index(Renderable_State_Role role) { - switch (role) { - case Renderable_State_Role::Edit: - return State_Edit; - case Renderable_State_Role::Ready: - return State_Ready; - case Renderable_State_Role::Render: - return State_Render; - } - return State_Render; -} - -std::uint8_t input_role_index(Renderable_Input_Role role) { - switch (role) { - case Renderable_Input_Role::Edit: - return Input_Edit; - case Renderable_Input_Role::Ready: - return Input_Ready; - case Renderable_Input_Role::Render: - return Input_Render; - } - return Input_Render; -} - -} // namespace - -struct Low_Latency_Renderable_Runtime_Data { - Renderable* owner{}; - Renderable_Runtime::Copy_Render_State_Function copy_render_state; - Triple_Role_Buffer_Control state_control; - std::array state_buffers{}; - std::array state_buffer_owners{}; - Triple_Role_Buffer_Control input_control; - std::array input_buffers{}; - std::array input_buffer_owners{}; - std::pmr::vector> committed_update_states{memory_resource(Memory_Domain::Update_Completion)}; - std::uint64_t input_version = 0; - - Render_State* state_by_index(std::uint8_t index) const { - return index < state_buffers.size() ? state_buffers[index] : nullptr; - } - - Render_State* state(std::uint8_t role) const { - return state_by_index(state_control.read_view()[role]); - } - - Input_Data* input_data_by_index(std::uint8_t index) const { - return index < input_buffers.size() ? input_buffers[index].data : nullptr; - } - - Input_Buffer_Slot& input_slot_by_index(std::uint8_t index) { - return input_buffers[index]; - } - - const Input_Buffer_Slot& input_slot_by_index(std::uint8_t index) const { - return input_buffers[index]; - } - - Input_Buffer_Slot& input_slot(std::uint8_t role) { - return input_slot_by_index(input_control.read_view()[role]); - } - - const Input_Buffer_Slot& input_slot(std::uint8_t role) const { - return input_slot_by_index(input_control.read_view()[role]); - } - - Input_Data* input_data(std::uint8_t role) const { - return input_slot(role).data; - } - - Triple_Buffer_Lease try_mark_state_role(std::uint8_t role) { - auto record = state_control.try_acquire_role(role); - return record.result == Triple_Buffer_Result::Success ? record.lease : Triple_Buffer_Lease{}; - } - - Triple_Buffer_Lease mark_state_role(std::uint8_t role) { - auto record = state_control.acquire_role(role); - return record.result == Triple_Buffer_Result::Success ? record.lease : Triple_Buffer_Lease{}; - } - - Triple_Buffer_Lease try_mark_input_role(std::uint8_t role) { - auto record = input_control.try_acquire_role(role); - return record.result == Triple_Buffer_Result::Success ? record.lease : Triple_Buffer_Lease{}; - } - - void mark_input_dirty(Triple_Buffer_Lease lease) { - Input_Buffer_Slot& slot = input_slot_by_index(lease.index); - slot.version = ++input_version; - slot.pending = true; - } - - void set_input_completion(Triple_Buffer_Lease lease, std::shared_ptr state) { - Input_Buffer_Slot& slot = input_slot_by_index(lease.index); - if (slot.completion_state && slot.completion_state != state) - slot.completion_state->complete_all({}, Update_Outcome::Superseded); - slot.completion_state = std::move(state); - } - - void cancel_pending_completions() { - for (Input_Buffer_Slot& slot : input_buffers) { - if (!slot.completion_state) - continue; - slot.completion_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - slot.completion_state.reset(); - } - cancel_update_states(committed_update_states); - } - - void clear_state_buffers() { - state_control.close_and_wait(); - for (std::size_t i = 0; i < state_buffers.size(); ++i) { - state_buffer_owners[i].reset(); - state_buffers[i] = nullptr; - } - state_control.reset(); - } - - void clear_input_buffers() { - input_control.close_and_wait(); - cancel_pending_completions(); - for (std::size_t i = 0; i < input_buffers.size(); ++i) { - input_buffer_owners[i].reset(); - input_buffers[i].data = nullptr; - input_buffers[i].version = 0; - input_buffers[i].pending = false; - input_buffers[i].completion_state.reset(); - } - input_version = 0; - input_control.reset(); - } - - void initialize_buffers(Renderable& renderable) { - clear_state_buffers(); - clear_input_buffers(); - state_buffer_owners[State_Edit] = renderable.create_render_state(); - state_buffer_owners[State_Ready] = renderable.create_render_state(); - state_buffer_owners[State_Render] = renderable.create_render_state(); - state_buffers[State_Edit] = state_buffer_owners[State_Edit].get(); - state_buffers[State_Ready] = state_buffer_owners[State_Ready].get(); - state_buffers[State_Render] = state_buffer_owners[State_Render].get(); - input_buffer_owners[Input_Edit] = renderable.create_input_data(); - input_buffer_owners[Input_Ready] = renderable.create_input_data(); - input_buffer_owners[Input_Render] = renderable.create_input_data(); - if (input_buffer_owners[Input_Edit]) - input_buffers[Input_Edit].data = input_buffer_owners[Input_Edit].get(); - if (input_buffer_owners[Input_Ready]) - input_buffers[Input_Ready].data = input_buffer_owners[Input_Ready].get(); - if (input_buffer_owners[Input_Render]) - input_buffers[Input_Render].data = input_buffer_owners[Input_Render].get(); - if (state_buffers[State_Edit]) - state_buffers[State_Edit]->version = 1; - } - - void sync_state_pipeline() { - auto edit_record = state_control.try_acquire_role(State_Edit); - if (edit_record.result != Triple_Buffer_Result::Success) - return; - auto ready_record = state_control.try_acquire_role(State_Ready); - if (ready_record.result != Triple_Buffer_Result::Success) { - state_control.unmark_use(edit_record.lease); - return; - } - Render_State* ready = state_by_index(ready_record.lease.index); - Render_State* edit = state_by_index(edit_record.lease.index); - if (ready && edit && buffer_version_newer(edit->version, ready->version)) { - if (copy_render_state) - copy_render_state(*ready, *edit); - else - ready->version = edit->version; - } - state_control.unmark_use(ready_record.lease); - state_control.unmark_use(edit_record.lease); - state_control.try_swap_role(State_Ready, State_Render, [this](const Triple_Buffer_View& view) { - auto* ready = state_by_index(view[State_Ready]); - auto* render = state_by_index(view[State_Render]); - return ready && render && buffer_version_newer(ready->version, render->version); - }); - input_control.try_swap_role(Input_Edit, Input_Ready, [this](const Triple_Buffer_View& view) { - const Input_Buffer_Slot& edit = input_slot_by_index(view[Input_Edit]); - const Input_Buffer_Slot& ready = input_slot_by_index(view[Input_Ready]); - return edit.pending && buffer_version_newer(edit.version, ready.version); - }); - input_control.try_swap_role(Input_Ready, Input_Render, [this](const Triple_Buffer_View& view) { - const Input_Buffer_Slot& ready = input_slot_by_index(view[Input_Ready]); - const Input_Buffer_Slot& render = input_slot_by_index(view[Input_Render]); - return ready.pending && buffer_version_newer(ready.version, render.version); - }); - } - - void clear_input_role(Renderable_Input_Role role) { - Input_Buffer_Slot& slot = input_slot(input_role_index(role)); - if (slot.completion_state) { - if (role == Renderable_Input_Role::Render) { - slot.completion_state->complete_until(Update_Stage::Committed); - committed_update_states.push_back(std::move(slot.completion_state)); - } - else { - slot.completion_state->complete_all({}, Update_Outcome::Superseded); - slot.completion_state.reset(); - } - } - if (slot.data) - slot.data->clear(); - slot.pending = false; - } - - void consume_input_lease(Triple_Buffer_Lease lease) { - if (!lease) - return; - Input_Buffer_Slot& slot = input_slot_by_index(lease.index); - if (slot.completion_state) { - slot.completion_state->complete_until(Update_Stage::Committed); - committed_update_states.push_back(std::move(slot.completion_state)); - } - if (slot.data) - slot.data->consume(); - slot.pending = false; - } -}; - -namespace { - -struct Low_Latency_State_Read_Handle final : Render_State_Read_Handle { - Low_Latency_State_Read_Handle(Low_Latency_Renderable_Runtime_Data* runtime, Triple_Buffer_Lease lease) - : runtime(runtime), lease(lease) {} - ~Low_Latency_State_Read_Handle() override { - if (runtime && lease) - runtime->state_control.unmark_use(lease); - } - Low_Latency_Renderable_Runtime_Data* runtime{}; - Triple_Buffer_Lease lease{}; -}; - -struct Low_Latency_Input_Frame_Handle final : Render_Input_Frame_Handle { - Low_Latency_Input_Frame_Handle(Low_Latency_Renderable_Runtime_Data* runtime, Triple_Buffer_Lease lease) - : runtime(runtime), lease(lease) {} - ~Low_Latency_Input_Frame_Handle() override { - if (runtime && lease) - runtime->input_control.unmark_use(lease); - } - void consume_input(Input_Data*) override { - if (consumed || !runtime || !lease) - return; - consumed = true; - runtime->consume_input_lease(lease); - } - Low_Latency_Renderable_Runtime_Data* runtime{}; - Triple_Buffer_Lease lease{}; - bool consumed = false; -}; - -struct Low_Latency_State_Write_Handle final : Render_State_Write_Handle { - Low_Latency_State_Write_Handle(Low_Latency_Renderable_Runtime_Data* runtime, Triple_Buffer_Lease lease) - : runtime(runtime), lease(lease) {} - ~Low_Latency_State_Write_Handle() override { - if (!runtime || !lease) - return; - if (auto* state = runtime->state_by_index(lease.index)) - ++state->version; - runtime->state_control.unmark_use(lease); - if (runtime->owner) - runtime->owner->mark_render_dirty(Dirty_Reason::Config); - } - Low_Latency_Renderable_Runtime_Data* runtime{}; - Triple_Buffer_Lease lease{}; -}; - -struct Low_Latency_Input_Write_Handle final : Render_Input_Write_Handle { - Low_Latency_Input_Write_Handle(Low_Latency_Renderable_Runtime_Data* runtime, Triple_Buffer_Lease lease, bool notify_render) - : runtime(runtime), lease(lease), notify_render(notify_render) {} - ~Low_Latency_Input_Write_Handle() override { - if (!runtime || !lease) - return; - runtime->mark_input_dirty(lease); - runtime->input_control.unmark_use(lease); - if (notify_render && runtime->owner) - runtime->owner->mark_render_dirty(); - } - void set_completion(std::shared_ptr state) override { - if (runtime && lease) - runtime->set_input_completion(lease, std::move(state)); - } - Low_Latency_Renderable_Runtime_Data* runtime{}; - Triple_Buffer_Lease lease{}; - bool notify_render{}; -}; - -} // namespace - -Low_Latency_Renderable_Runtime::Low_Latency_Renderable_Runtime(Renderable& owner) - : d(std::make_unique()) { - d->owner = &owner; - d->initialize_buffers(owner); -} - -Low_Latency_Renderable_Runtime::~Low_Latency_Renderable_Runtime() { - d->clear_state_buffers(); - d->clear_input_buffers(); -} - -void Low_Latency_Renderable_Runtime::set_render_state_copier(Copy_Render_State_Function copier) { - d->copy_render_state = std::move(copier); -} - -void Low_Latency_Renderable_Runtime::cancel_pending_completions() { - d->cancel_pending_completions(); -} - -void Low_Latency_Renderable_Runtime::drain_committed_update_states(std::pmr::vector>& output) { - for (auto& state : d->committed_update_states) - output.push_back(std::move(state)); - d->committed_update_states.clear(); -} - -Render_State_Read_Guard Low_Latency_Renderable_Runtime::acquire_state_read(Renderable_State_Role role) { - Triple_Buffer_Lease lease = d->mark_state_role(state_role_index(role)); - if (!lease) - return {}; - return Render_State_Read_Guard(d->state_by_index(lease.index), std::make_unique(d.get(), lease)); -} - -Render_State_Read_Guard Low_Latency_Renderable_Runtime::acquire_render_state_read() { - Triple_Buffer_Lease lease = d->try_mark_state_role(State_Render); - if (!lease) - return {}; - return Render_State_Read_Guard(d->state_by_index(lease.index), std::make_unique(d.get(), lease)); -} - -Render_State_Read_Guard Low_Latency_Renderable_Runtime::acquire_edit_state_read() { - Triple_Buffer_Lease lease = d->mark_state_role(State_Edit); - if (!lease) - return {}; - return Render_State_Read_Guard(d->state_by_index(lease.index), std::make_unique(d.get(), lease)); -} - -Render_State_Write_Guard Low_Latency_Renderable_Runtime::acquire_edit_state() { - Triple_Buffer_Lease lease = d->mark_state_role(State_Edit); - if (!lease) - return {}; - return Render_State_Write_Guard(d->state_by_index(lease.index), std::make_unique(d.get(), lease)); -} - -Render_Input_Write_Guard Low_Latency_Renderable_Runtime::acquire_edit_input(bool notify_render) { - auto record = d->input_control.acquire_role(Input_Edit); - if (record.result != Triple_Buffer_Result::Success) - return {}; - Input_Data* input = d->input_data_by_index(record.lease.index); - if (!input) { - d->input_control.unmark_use(record.lease); - return {}; - } - return Render_Input_Write_Guard(input, std::make_unique(d.get(), record.lease, notify_render)); -} - -Render_Input_Frame_Guard Low_Latency_Renderable_Runtime::acquire_input_read(Renderable_Input_Role role) { - Triple_Buffer_Lease lease = d->try_mark_input_role(input_role_index(role)); - if (!lease) - return {}; - Input_Data* input = d->input_data_by_index(lease.index); - if (!input) { - d->input_control.unmark_use(lease); - return {}; - } - return Render_Input_Frame_Guard(input, std::make_unique(d.get(), lease)); -} - -Renderable_Frame_View Low_Latency_Renderable_Runtime::capture_frame_view() { - d->sync_state_pipeline(); - Triple_Buffer_Lease state_lease = d->try_mark_state_role(State_Render); - if (!state_lease) - return {}; - auto state_guard = Render_State_Read_Guard( - d->state_by_index(state_lease.index), - std::make_unique(d.get(), state_lease)); - - Render_Input_Frame_Guard input_guard; - Triple_Buffer_Lease input_lease = d->try_mark_input_role(Input_Render); - if (input_lease) { - Input_Data* input = d->input_data_by_index(input_lease.index); - if (input) { - input_guard = Render_Input_Frame_Guard( - input, - std::make_unique(d.get(), input_lease)); - } - else { - d->input_control.unmark_use(input_lease); - } - } - return Renderable_Frame_View(std::move(state_guard), std::move(input_guard)); -} - -Render_State* Low_Latency_Renderable_Runtime::state(Renderable_State_Role role) { - return d->state(state_role_index(role)); -} - -Input_Data* Low_Latency_Renderable_Runtime::input_data(Renderable_Input_Role role) { - return d->input_data(input_role_index(role)); -} - -void Low_Latency_Renderable_Runtime::clear_render_input() { - d->clear_input_role(Renderable_Input_Role::Render); -} - -std::unique_ptr make_low_latency_renderable_runtime(Renderable& owner) { - return std::make_unique(owner); -} - -} // namespace renderive diff --git a/Core/flow/low_latency/Render_Data.h b/Core/flow/low_latency/Render_Data.h deleted file mode 100644 index c3d3e43..0000000 --- a/Core/flow/low_latency/Render_Data.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "../../architecture/Strategy_Render_Data.h" - -namespace renderive { - -using Low_Latency_Render_Data = Strategy_Render_Data; - -} // namespace renderive diff --git a/Core/flow/low_latency/Render_Lease.h b/Core/flow/low_latency/Render_Lease.h deleted file mode 100644 index 3845f28..0000000 --- a/Core/flow/low_latency/Render_Lease.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "../../architecture/Render_Lease.h" -#include "Low_Latency_Roles.h" - diff --git a/Core/flow/low_latency/Triple_Buffer.cpp b/Core/flow/low_latency/Triple_Buffer.cpp deleted file mode 100644 index 2f0c242..0000000 --- a/Core/flow/low_latency/Triple_Buffer.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "Triple_Buffer.h" diff --git a/Core/flow/low_latency/Triple_Buffer.h b/Core/flow/low_latency/Triple_Buffer.h deleted file mode 100644 index 818d785..0000000 --- a/Core/flow/low_latency/Triple_Buffer.h +++ /dev/null @@ -1,314 +0,0 @@ -#pragma once -#include -#include -#include - -namespace renderive { - -static constexpr std::uint8_t triple_buffer_invalid_index = 255; - -enum class Triple_Buffer_Result : std::uint8_t { - Success, - Busy, - State_Changed, - Condition_Failed, - Cancelled -}; - -struct Triple_Buffer_View { - std::array index{}; - std::uint8_t operator[](std::uint8_t role) const { - return index[role]; - } -}; - -struct Triple_Buffer_Lease { - std::uint8_t index = triple_buffer_invalid_index; - explicit operator bool() const { - return index != triple_buffer_invalid_index; - } -}; - -struct Triple_Buffer_Mark_Record { - Triple_Buffer_Result result = Triple_Buffer_Result::Condition_Failed; - Triple_Buffer_Lease lease{}; - std::uint8_t busy_index = triple_buffer_invalid_index; -}; - -struct Triple_Buffer_Swap_Record { - Triple_Buffer_Result result = Triple_Buffer_Result::Condition_Failed; - Triple_Buffer_View old_view{}; - std::uint8_t busy_index = triple_buffer_invalid_index; -}; - -struct alignas(64) Triple_Buffer_Busy { - std::atomic_bool busy{false}; -}; - -class Triple_Role_Buffer_Control { -public: - void reset() { - closed.store(false, std::memory_order_release); - slot_state.store(0, std::memory_order_release); - for (auto& item : buffer_busy) { - item.busy.store(false, std::memory_order_release); - item.busy.notify_all(); - } - wake_generation.fetch_add(1, std::memory_order_acq_rel); - wake_generation.notify_all(); - slot_state.notify_all(); - } - - void close() { - closed.store(true, std::memory_order_release); - wake_generation.fetch_add(1, std::memory_order_acq_rel); - wake_generation.notify_all(); - slot_state.notify_all(); - } - - void wait_until_idle() { - for (;;) { - bool any_busy = false; - for (auto& item : buffer_busy) { - if (!item.busy.load(std::memory_order_acquire)) - continue; - any_busy = true; - item.busy.wait(true, std::memory_order_acquire); - break; - } - if (!any_busy) - return; - } - } - - void close_and_wait() { - close(); - wait_until_idle(); - } - - Triple_Buffer_View read_view() const { - return decode_order(slot_state.load(std::memory_order_acquire)); - } - - Triple_Buffer_Mark_Record try_acquire_role(std::uint8_t role) { - if (closed.load(std::memory_order_acquire)) - return {Triple_Buffer_Result::Cancelled, {}, triple_buffer_invalid_index}; - auto order = slot_state.load(std::memory_order_acquire); - auto view = decode_order(order); - auto index = view[role]; - if (!try_mark_index(index)) - return {Triple_Buffer_Result::Busy, {}, index}; - auto cur_order = slot_state.load(std::memory_order_acquire); - if (cur_order != order) { - unmark_index(index); - return {Triple_Buffer_Result::State_Changed, {}, triple_buffer_invalid_index}; - } - return {Triple_Buffer_Result::Success, {index}, triple_buffer_invalid_index}; - } - - Triple_Buffer_Mark_Record acquire_role(std::uint8_t role) { - while (!closed.load(std::memory_order_acquire)) { - auto order = slot_state.load(std::memory_order_acquire); - auto view = decode_order(order); - auto index = view[role]; - if (!try_mark_index(index)) { - auto wake = wake_generation.load(std::memory_order_acquire); - if (buffer_busy[index].busy.load(std::memory_order_acquire)) - wake_generation.wait(wake, std::memory_order_acquire); - continue; - } - auto cur_order = slot_state.load(std::memory_order_acquire); - if (cur_order != order) { - unmark_index(index); - continue; - } - if (closed.load(std::memory_order_acquire)) { - unmark_index(index); - break; - } - return {Triple_Buffer_Result::Success, {index}, triple_buffer_invalid_index}; - } - return {Triple_Buffer_Result::Cancelled, {}, triple_buffer_invalid_index}; - } - - void unmark_use(Triple_Buffer_Lease lease) { - if (lease) - unmark_index(lease.index); - } - - template - Triple_Buffer_Swap_Record try_swap_role(std::uint8_t left_role, std::uint8_t right_role, Can_Swap can_swap) { - std::uint8_t busy_index = triple_buffer_invalid_index; - return try_swap_role_once(left_role, right_role, can_swap, [](const Triple_Buffer_View&, const Triple_Buffer_View&) {}, busy_index); - } - - template - Triple_Buffer_Swap_Record try_swap_role(std::uint8_t left_role, std::uint8_t right_role, Can_Swap can_swap, On_Success on_success) { - std::uint8_t busy_index = triple_buffer_invalid_index; - return try_swap_role_once(left_role, right_role, can_swap, on_success, busy_index); - } - - template - Triple_Buffer_Swap_Record try_swap_role_with_left_lease(std::uint8_t left_role, std::uint8_t right_role, Triple_Buffer_Lease left_lease, Can_Swap can_swap, On_Success on_success) { - return try_swap_role_with_left_lease_once(left_role, right_role, left_lease, can_swap, on_success); - } - - bool role_busy(std::uint8_t role) const { - auto view = read_view(); - return buffer_busy[view[role]].busy.load(std::memory_order_acquire); - } - -private: - std::atomic slot_state{0}; - std::atomic_bool closed{false}; - std::atomic wake_generation{0}; - std::array buffer_busy{}; - - static Triple_Buffer_View decode_order(std::uint8_t order) { - switch (order) { - case 0: - return {{0, 1, 2}}; - case 1: - return {{0, 2, 1}}; - case 2: - return {{1, 0, 2}}; - case 3: - return {{1, 2, 0}}; - case 4: - return {{2, 0, 1}}; - case 5: - return {{2, 1, 0}}; - } - return {{0, 1, 2}}; - } - - static std::uint8_t encode_order(const Triple_Buffer_View& view) { - if (view.index[0] == 0) - return view.index[1] == 1 ? 0 : 1; - if (view.index[0] == 1) - return view.index[1] == 0 ? 2 : 3; - return view.index[1] == 0 ? 4 : 5; - } - - static std::uint8_t swap_order(std::uint8_t order, std::uint8_t left_role, std::uint8_t right_role) { - auto view = decode_order(order); - auto temp = view.index[left_role]; - view.index[left_role] = view.index[right_role]; - view.index[right_role] = temp; - return encode_order(view); - } - - bool try_mark_index(std::uint8_t index) { - bool expected = false; - return buffer_busy[index].busy.compare_exchange_strong(expected, true, std::memory_order_acq_rel, std::memory_order_acquire); - } - - void unmark_index(std::uint8_t index) { - buffer_busy[index].busy.store(false, std::memory_order_release); - buffer_busy[index].busy.notify_all(); - wake_generation.fetch_add(1, std::memory_order_acq_rel); - wake_generation.notify_all(); - } - - bool try_mark_two_indices(std::uint8_t left_index, std::uint8_t right_index, std::uint8_t& busy_index) { - if (left_index == right_index) { - if (!try_mark_index(left_index)) { - busy_index = left_index; - return false; - } - return true; - } - auto first = left_index < right_index ? left_index : right_index; - auto second = left_index < right_index ? right_index : left_index; - if (!try_mark_index(first)) { - busy_index = first; - return false; - } - if (!try_mark_index(second)) { - unmark_index(first); - busy_index = second; - return false; - } - return true; - } - - void unmark_two_indices(std::uint8_t left_index, std::uint8_t right_index) { - if (left_index == right_index) { - unmark_index(left_index); - return; - } - unmark_index(left_index); - unmark_index(right_index); - } - - template - Triple_Buffer_Swap_Record try_swap_role_once(std::uint8_t left_role, std::uint8_t right_role, Can_Swap can_swap, On_Success on_success, std::uint8_t& busy_index) { - if (closed.load(std::memory_order_acquire)) - return {Triple_Buffer_Result::Cancelled, {}, triple_buffer_invalid_index}; - auto old_order = slot_state.load(std::memory_order_acquire); - auto old_view = decode_order(old_order); - auto left_index = old_view[left_role]; - auto right_index = old_view[right_role]; - if (!try_mark_two_indices(left_index, right_index, busy_index)) - return {Triple_Buffer_Result::Busy, old_view, busy_index}; - auto cur_order = slot_state.load(std::memory_order_acquire); - if (cur_order != old_order) { - unmark_two_indices(left_index, right_index); - return {Triple_Buffer_Result::State_Changed, old_view, triple_buffer_invalid_index}; - } - if (!can_swap(old_view)) { - unmark_two_indices(left_index, right_index); - return {Triple_Buffer_Result::Condition_Failed, old_view, triple_buffer_invalid_index}; - } - auto new_order = swap_order(old_order, left_role, right_role); - auto expected = old_order; - if (!slot_state.compare_exchange_strong(expected, new_order, std::memory_order_acq_rel, std::memory_order_acquire)) { - unmark_two_indices(left_index, right_index); - return {Triple_Buffer_Result::State_Changed, old_view, triple_buffer_invalid_index}; - } - on_success(old_view, decode_order(new_order)); - unmark_two_indices(left_index, right_index); - slot_state.notify_all(); - wake_generation.fetch_add(1, std::memory_order_acq_rel); - wake_generation.notify_all(); - return {Triple_Buffer_Result::Success, old_view, triple_buffer_invalid_index}; - } - - template - Triple_Buffer_Swap_Record try_swap_role_with_left_lease_once(std::uint8_t left_role, std::uint8_t right_role, Triple_Buffer_Lease left_lease, Can_Swap can_swap, On_Success on_success) { - if (!left_lease) - return {Triple_Buffer_Result::Condition_Failed, {}, triple_buffer_invalid_index}; - if (closed.load(std::memory_order_acquire)) - return {Triple_Buffer_Result::Cancelled, {}, triple_buffer_invalid_index}; - auto old_order = slot_state.load(std::memory_order_acquire); - auto old_view = decode_order(old_order); - if (old_view[left_role] != left_lease.index) - return {Triple_Buffer_Result::State_Changed, old_view, triple_buffer_invalid_index}; - auto right_index = old_view[right_role]; - if (!try_mark_index(right_index)) - return {Triple_Buffer_Result::Busy, old_view, right_index}; - auto cur_order = slot_state.load(std::memory_order_acquire); - if (cur_order != old_order) { - unmark_index(right_index); - return {Triple_Buffer_Result::State_Changed, old_view, triple_buffer_invalid_index}; - } - if (!can_swap(old_view)) { - unmark_index(right_index); - return {Triple_Buffer_Result::Condition_Failed, old_view, triple_buffer_invalid_index}; - } - auto new_order = swap_order(old_order, left_role, right_role); - auto expected = old_order; - if (!slot_state.compare_exchange_strong(expected, new_order, std::memory_order_acq_rel, std::memory_order_acquire)) { - unmark_index(right_index); - return {Triple_Buffer_Result::State_Changed, old_view, triple_buffer_invalid_index}; - } - on_success(old_view, decode_order(new_order)); - unmark_index(right_index); - slot_state.notify_all(); - wake_generation.fetch_add(1, std::memory_order_acq_rel); - wake_generation.notify_all(); - return {Triple_Buffer_Result::Success, old_view, triple_buffer_invalid_index}; - } -}; - -} // namespace renderive diff --git a/Core/performance/Performance_Log_Record.h b/Core/performance/Performance_Log_Record.h deleted file mode 100644 index 5a6eb06..0000000 --- a/Core/performance/Performance_Log_Record.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include -#include -#include - -namespace renderive { - -struct Performance_Log_Options { - bool enabled{}; - std::string directory; - std::string session_name = "renderive"; - std::size_t max_file_size = 64 * 1024 * 1024; - std::size_t max_files = 8; - std::size_t queue_capacity = 8192; - std::uint32_t flush_interval_ms = 1000; -}; - -struct Performance_Log_Plot_Identity { - std::uint64_t plot_id{}; - std::string plot_name; -}; - -enum class Performance_Log_Record_Kind : std::uint8_t { - Frame, - Feedback -}; - -struct Performance_Log_Record { - Performance_Log_Record_Kind kind = Performance_Log_Record_Kind::Frame; - std::string line; - bool flush{}; -}; - -struct Performance_Log_Session_Info { - std::string session_id; - std::string frame_log_path; - std::string feedback_log_path; - std::string meta_path; - std::uint64_t dropped_record_count{}; -}; - -} // namespace renderive diff --git a/Core/performance/Performance_Log_Service.cpp b/Core/performance/Performance_Log_Service.cpp deleted file mode 100644 index 8f1ede7..0000000 --- a/Core/performance/Performance_Log_Service.cpp +++ /dev/null @@ -1,682 +0,0 @@ -#include "Performance_Log_Service.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef _WIN32 -#include -#else -#include -#endif - -namespace renderive { -namespace { -constexpr int Log_Schema_Version = 3; - -std::uint64_t process_id() { -#ifdef _WIN32 - return static_cast(GetCurrentProcessId()); -#else - return static_cast(getpid()); -#endif -} - -std::uint64_t thread_id_hash() { - return static_cast(std::hash{}(std::this_thread::get_id())); -} - -std::uint64_t clock_ns() { - return static_cast( - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()).count()); -} - -std::string session_id_for(const Performance_Log_Options& options) { - std::ostringstream out; - out << (options.session_name.empty() ? "renderive" : options.session_name) - << '_' << process_id() - << '_' << clock_ns(); - return out.str(); -} - -std::string log_bool(bool value) { - return value ? "1" : "0"; -} - -std::uint64_t duration_ns(std::uint64_t begin_ns, std::uint64_t end_ns) { - return end_ns > begin_ns ? end_ns - begin_ns : 0; -} - -std::uint64_t timestamp_for(const Frame_Lifecycle_Record& frame) { - if (frame.paint_end_ns) - return frame.paint_end_ns; - if (frame.superseded_time_ns) - return frame.superseded_time_ns; - if (frame.render_end_ns) - return frame.render_end_ns; - if (frame.render_begin_ns) - return frame.render_begin_ns; - return clock_ns(); -} - -std::string source_text(Refresh_Limit_Source source) { - switch (source) { - case Refresh_Limit_Source::Unknown: return "unknown"; - case Refresh_Limit_Source::Input: return "input"; - case Refresh_Limit_Source::Render: return "render"; - case Refresh_Limit_Source::Paint: return "paint"; - case Refresh_Limit_Source::User: return "user"; - } - return "unknown"; -} - -std::string outcome_text(Frame_Outcome outcome) { - switch (outcome) { - case Frame_Outcome::Presented: return "presented"; - case Frame_Outcome::Superseded: return "superseded"; - case Frame_Outcome::Render_Acquire_Dropped: return "render_acquire_dropped"; - case Frame_Outcome::Publish_Dropped: return "publish_dropped"; - case Frame_Outcome::Paint_Acquire_Dropped: return "paint_acquire_dropped"; - case Frame_Outcome::Worker_Budget_Dropped: return "worker_budget_dropped"; - case Frame_Outcome::Cancelled: return "cancelled"; - } - return "unknown"; -} - -std::string bottleneck_text(Low_Latency_Bottleneck_State state) { - switch (state) { - case Low_Latency_Bottleneck_State::Unknown: return "unknown"; - case Low_Latency_Bottleneck_State::Input_Limited: return "input"; - case Low_Latency_Bottleneck_State::Producer_Limited: return "producer"; - case Low_Latency_Bottleneck_State::Consumer_Limited: return "consumer"; - case Low_Latency_Bottleneck_State::User_Limited: return "user"; - } - return "unknown"; -} - -std::string production_mode_text(Low_Latency_Production_Mode mode) { - switch (mode) { - case Low_Latency_Production_Mode::Latency_Eager: return "latency_eager"; - case Low_Latency_Production_Mode::Consumer_Paced: return "consumer_paced"; - } - return "latency_eager"; -} - -std::string limit_reason_text(Low_Latency_Limit_Reason reason) { - switch (reason) { - case Low_Latency_Limit_Reason::User_Fps: return "user_fps"; - case Low_Latency_Limit_Reason::Consumer_Capacity: return "consumer_capacity"; - case Low_Latency_Limit_Reason::Render_Capacity: return "render_capacity"; - case Low_Latency_Limit_Reason::No_Work: return "no_work"; - case Low_Latency_Limit_Reason::Shutdown: return "shutdown"; - } - return "user_fps"; -} - -std::string consumer_confidence_text(Consumer_Confidence confidence) { - switch (confidence) { - case Consumer_Confidence::Low: return "low"; - case Consumer_Confidence::Medium: return "medium"; - case Consumer_Confidence::High: return "high"; - } - return "low"; -} - -std::string consumer_sample_result_text(Consumer_Sample_Result result) { - switch (result) { - case Consumer_Sample_Result::Accepted: return "accepted"; - case Consumer_Sample_Result::Rejected_No_Backlog: return "rejected_no_backlog"; - case Consumer_Sample_Result::Rejected_Producer_Starved: return "rejected_producer_starved"; - case Consumer_Sample_Result::Rejected_No_New_Frame: return "rejected_no_new_frame"; - case Consumer_Sample_Result::Rejected_Invalid_Timestamp: return "rejected_invalid_timestamp"; - } - return "rejected_invalid_timestamp"; -} - -std::string feedback_decision_text(Low_Latency_Feedback_Decision decision) { - switch (decision) { - case Low_Latency_Feedback_Decision::Hold_User_Period: return "hold_user_period"; - case Low_Latency_Feedback_Decision::Enter_Consumer_Paced: return "enter_consumer_paced"; - case Low_Latency_Feedback_Decision::Exit_Consumer_Paced: return "exit_consumer_paced"; - case Low_Latency_Feedback_Decision::Reject_Consumer_Sample: return "reject_consumer_sample"; - case Low_Latency_Feedback_Decision::Producer_Starved: return "producer_starved"; - } - return "hold_user_period"; -} - -std::string dirty_reason_text(std::uint32_t mask) { - std::string result; - auto append = [&result](const char* text) { - if (!result.empty()) - result += '|'; - result += text; - }; - if (mask & dirty_reason_bit(Dirty_Reason::Core_Data)) - append("core"); - if (mask & dirty_reason_bit(Dirty_Reason::Config)) - append("config"); - if (mask & dirty_reason_bit(Dirty_Reason::Viewport)) - append("viewport"); - return result.empty() ? "none" : result; -} - -std::string log_escape(const std::string& value) { - std::string escaped; - escaped.reserve(value.size()); - for (char ch : value) { - switch (ch) { - case '\\': escaped += "\\\\"; break; - case '\r': escaped += "\\r"; break; - case '\n': escaped += "\\n"; break; - case '\t': escaped += "\\t"; break; - default: escaped.push_back(ch); break; - } - } - return escaped; -} - -std::string json_escape(const std::string& value) { - std::string escaped; - escaped.reserve(value.size() + 2); - escaped.push_back('"'); - for (char ch : value) { - switch (ch) { - case '\\': escaped += "\\\\"; break; - case '"': escaped += "\\\""; break; - case '\r': escaped += "\\r"; break; - case '\n': escaped += "\\n"; break; - case '\t': escaped += "\\t"; break; - default: escaped.push_back(ch); break; - } - } - escaped.push_back('"'); - return escaped; -} - -class Log_Record_Builder { -public: - explicit Log_Record_Builder(std::string kind) - : kind_(std::move(kind)) { - out_ << '[' << kind_ << ']'; - } - - template - void add(const std::string& key, const T& value) { - out_ << '\n' << key << '=' << value; - } - - std::string finish() { - out_ << "\n[/" << kind_ << ']'; - return out_.str(); - } - -private: - std::string kind_; - std::ostringstream out_; -}; - -void append_renderable_stats(Log_Record_Builder& out, const Frame_Lifecycle_Record& frame) { - out.add("renderable_stats.count", frame.renderable_stats.size()); - for (std::size_t i = 0; i < frame.renderable_stats.size(); ++i) { - const auto& stat = frame.renderable_stats[i]; - const std::string prefix = "renderable_stats." + std::to_string(i) + '.'; - out.add(prefix + "object_name", log_escape(stat.object_name)); - out.add(prefix + "parent_object_name", log_escape(stat.parent_object_name)); - out.add(prefix + "tree_depth", stat.tree_depth); - out.add(prefix + "cache_enabled", log_bool(stat.cache_enabled)); - out.add(prefix + "render_count", stat.render_count); - out.add(prefix + "self_draw_count", stat.self_draw_count); - out.add(prefix + "total_render_time_ns", stat.total_render_time_ns); - out.add(prefix + "self_draw_time_ns", stat.self_draw_time_ns); - out.add(prefix + "last_total_render_time_ns", stat.last_total_render_time_ns); - out.add(prefix + "last_self_draw_time_ns", stat.last_self_draw_time_ns); - } -} - -void append_renderable_cache_stats(Log_Record_Builder& out, const Frame_Lifecycle_Record& frame) { - out.add("renderable_cache_stats.count", frame.renderable_cache_stats.size()); - for (std::size_t i = 0; i < frame.renderable_cache_stats.size(); ++i) { - const auto& stat = frame.renderable_cache_stats[i]; - const std::string prefix = "renderable_cache_stats." + std::to_string(i) + '.'; - out.add(prefix + "object_name", log_escape(stat.object_name)); - out.add(prefix + "parent_object_name", log_escape(stat.parent_object_name)); - out.add(prefix + "tree_depth", stat.tree_depth); - out.add(prefix + "hit_count", stat.hit_count); - out.add(prefix + "miss_count", stat.miss_count); - out.add(prefix + "rebuild_count", stat.rebuild_count); - out.add(prefix + "rebuild_time_ns", stat.rebuild_time_ns); - out.add(prefix + "compose_time_ns", stat.compose_time_ns); - out.add(prefix + "subtree_compose_time_ns", renderable_cache_subtree_compose_time(frame.renderable_cache_stats, stat)); - out.add(prefix + "last_rebuild_time_ns", stat.last_rebuild_time_ns); - out.add(prefix + "last_compose_time_ns", stat.last_compose_time_ns); - out.add(prefix + "last_image_bytes", stat.last_image_bytes); - out.add(prefix + "last_image_area", stat.last_image_area); - out.add(prefix + "last_edit_version", stat.last_edit_version); - out.add(prefix + "last_ready_version", stat.last_ready_version); - } -} - -std::string logger_name(const std::string& session_id, const char* suffix) { - return session_id + "_" + suffix; -} - -std::shared_ptr make_log_logger(const std::filesystem::path& path, std::size_t max_file_size, std::size_t max_files, const std::string& name) { - std::ofstream(path, std::ios::trunc).close(); - auto sink = std::make_shared( - path.string(), - std::max(1024, max_file_size), - std::max(1, max_files), - false); - auto logger = std::make_shared(name, sink); - logger->set_pattern("%v"); - logger->set_level(spdlog::level::info); - return logger; -} - -bool same_options(const Performance_Log_Options& a, const Performance_Log_Options& b) { - return a.enabled == b.enabled - && a.directory == b.directory - && a.session_name == b.session_name - && a.max_file_size == b.max_file_size - && a.max_files == b.max_files - && a.queue_capacity == b.queue_capacity - && a.flush_interval_ms == b.flush_interval_ms; -} - -void write_meta_json(const std::filesystem::path& path, const Performance_Log_Options& options, const std::string& session_id) { - std::ofstream out(path, std::ios::trunc); - out << "{\n" - << " \"schema_version\": " << Log_Schema_Version << ",\n" - << " \"session_id\": " << json_escape(session_id) << ",\n" - << " \"session_name\": " << json_escape(options.session_name) << ",\n" - << " \"start_time_ns\": " << clock_ns() << ",\n" - << " \"process_id\": " << process_id() << ",\n" -#ifdef NDEBUG - << " \"build_type\": \"release\",\n" -#else - << " \"build_type\": \"debug\",\n" -#endif -#ifdef _MSC_VER - << " \"compiler\": \"msvc-" << _MSC_VER << "\",\n" -#else - << " \"compiler\": \"unknown\",\n" -#endif - << " \"configured_user_fps\": 0,\n" - << " \"log_schema_version\": " << Log_Schema_Version << "\n" - << "}\n"; -} -} // namespace - -struct Performance_Log_Service::Private { - mutable std::mutex mutex; - std::condition_variable cv; - Performance_Log_Options options; - Performance_Log_Session_Info session; - std::unique_ptr> queue; - std::thread worker; - std::shared_ptr frame_logger; - std::shared_ptr feedback_logger; - std::atomic_bool running{false}; - std::atomic dropped_records{0}; - - void stop() { - std::thread worker_to_join; - { - std::lock_guard lock(mutex); - running.store(false, std::memory_order_release); - cv.notify_all(); - if (worker.joinable()) - worker_to_join = std::move(worker); - } - if (worker_to_join.joinable()) - worker_to_join.join(); - { - std::lock_guard lock(mutex); - if (frame_logger) - frame_logger->flush(); - if (feedback_logger) - feedback_logger->flush(); - frame_logger.reset(); - feedback_logger.reset(); - queue.reset(); - } - } - - void write_record(const Performance_Log_Record& record) { - auto& logger = record.kind == Performance_Log_Record_Kind::Frame ? frame_logger : feedback_logger; - if (!logger) - return; - logger->info("{}", record.line); - if (record.flush) - logger->flush(); - } - - void run() { - auto next_flush = std::chrono::steady_clock::now() + std::chrono::milliseconds(std::max(1, options.flush_interval_ms)); - while (running.load(std::memory_order_acquire)) { - bool did_work = false; - Performance_Log_Record record; - while (queue && queue->try_pop(record)) { - write_record(record); - did_work = true; - } - auto now = std::chrono::steady_clock::now(); - if (now >= next_flush) { - if (frame_logger) - frame_logger->flush(); - if (feedback_logger) - feedback_logger->flush(); - next_flush = now + std::chrono::milliseconds(std::max(1, options.flush_interval_ms)); - } - if (did_work) - continue; - std::unique_lock lock(mutex); - cv.wait_until(lock, next_flush, [this]() { - return !running.load(std::memory_order_acquire); - }); - } - Performance_Log_Record record; - while (queue && queue->try_pop(record)) - write_record(record); - if (frame_logger) - frame_logger->flush(); - if (feedback_logger) - feedback_logger->flush(); - } -}; - -Performance_Log_Service::Performance_Log_Service() - : d(std::make_unique()) {} - -Performance_Log_Service::~Performance_Log_Service() { - shutdown(); -} - -void Performance_Log_Service::configure(Performance_Log_Options options) { - if (options.enabled) { - if (options.directory.empty()) - options.directory = "performance"; - if (options.session_name.empty()) - options.session_name = "renderive"; - options.queue_capacity = std::max(1, options.queue_capacity); - } - { - std::lock_guard lock(d->mutex); - if (d->running.load(std::memory_order_acquire) && same_options(d->options, options)) - return; - } - d->stop(); - std::lock_guard lock(d->mutex); - d->options = std::move(options); - d->session = {}; - if (!d->options.enabled) - return; - std::filesystem::create_directories(d->options.directory); - d->session.session_id = session_id_for(d->options); - std::filesystem::path directory = d->options.directory; - std::filesystem::path frame_path = directory / (d->options.session_name + "_frames.log"); - std::filesystem::path feedback_path = directory / (d->options.session_name + "_feedback.log"); - std::filesystem::path meta_path = directory / (d->options.session_name + "_meta.json"); - d->session.frame_log_path = frame_path.string(); - d->session.feedback_log_path = feedback_path.string(); - d->session.meta_path = meta_path.string(); - d->frame_logger = make_log_logger(frame_path, d->options.max_file_size, d->options.max_files, logger_name(d->session.session_id, "frames")); - d->feedback_logger = make_log_logger(feedback_path, d->options.max_file_size, d->options.max_files, logger_name(d->session.session_id, "feedback")); - write_meta_json(meta_path, d->options, d->session.session_id); - d->queue = std::make_unique>(d->options.queue_capacity); - d->running.store(true, std::memory_order_release); - d->worker = std::thread([this]() { d->run(); }); -} - -Performance_Log_Options Performance_Log_Service::options() const { - std::lock_guard lock(d->mutex); - return d->options; -} - -bool Performance_Log_Service::enabled() const { - return d->running.load(std::memory_order_acquire); -} - -Performance_Log_Session_Info Performance_Log_Service::session_info() const { - std::lock_guard lock(d->mutex); - Performance_Log_Session_Info info = d->session; - info.dropped_record_count = dropped_record_count(); - return info; -} - -std::uint64_t Performance_Log_Service::dropped_record_count() const { - return d->dropped_records.load(std::memory_order_acquire); -} - -bool Performance_Log_Service::enqueue(Performance_Log_Record record) { - if (!enabled()) - return false; - bool pushed = false; - { - std::lock_guard lock(d->mutex); - pushed = d->queue && d->queue->try_push(std::move(record)); - } - if (!pushed) { - d->dropped_records.fetch_add(1, std::memory_order_acq_rel); - return false; - } - d->cv.notify_one(); - return true; -} - -bool Performance_Log_Service::enqueue_frame(Performance_Log_Plot_Identity identity, std::shared_ptr frame) { - if (!frame) - return false; - Performance_Log_Record record; - record.kind = Performance_Log_Record_Kind::Frame; - const Refresh_Control_Snapshot& r = frame->refresh; - const auto& e = frame->worker.executor_at_finish; - Log_Record_Builder out("frame"); - out.add("schema_version", Log_Schema_Version); - out.add("session_id", log_escape(d->session.session_id)); - out.add("plot_id", identity.plot_id); - out.add("plot_name", log_escape(identity.plot_name)); - out.add("process_id", process_id()); - out.add("thread_id", thread_id_hash()); - out.add("timestamp_ns", timestamp_for(*frame)); - out.add("frame_id", frame->frame_id); - out.add("input_version", frame->input_version); - out.add("outcome", outcome_text(frame->outcome)); - out.add("dirty_reason", dirty_reason_text(frame->dirty_reason_mask)); - out.add("production_mode", production_mode_text(r.production_mode)); - out.add("limit_reason", limit_reason_text(r.limit_reason)); - out.add("stable_bottleneck", bottleneck_text(r.stable_bottleneck)); - out.add("candidate_bottleneck", bottleneck_text(r.candidate_bottleneck)); - out.add("consumer_confidence", consumer_confidence_text(r.consumer_confidence)); - out.add("last_consumer_sample_result", consumer_sample_result_text(r.last_consumer_sample_result)); - out.add("last_decision", feedback_decision_text(r.last_decision)); - out.add("decision_reason", log_escape(r.decision_reason)); - out.add("decision_timestamp_ns", r.decision_timestamp_ns); - out.add("input_interval_current_ns", r.input_interval_current_ns); - out.add("input_interval_srtt_ns", r.input_interval_srtt_ns); - out.add("input_interval_jitter_ns", r.input_jitter_ns); - out.add("producer_interval_current_ns", r.producer_interval_current_ns); - out.add("producer_interval_srtt_ns", r.producer_interval_srtt_ns); - out.add("producer_interval_jitter_ns", r.producer_interval_jitter_ns); - out.add("render_service_current_ns", r.render_service_current_ns); - out.add("render_service_srtt_ns", r.render_service_srtt_ns); - out.add("render_service_jitter_ns", r.render_jitter_ns); - out.add("request_to_update_post_current_ns", r.request_to_update_post_current_ns); - out.add("request_to_update_post_srtt_ns", r.request_to_update_post_srtt_ns); - out.add("request_to_update_post_jitter_ns", r.request_to_update_post_jitter_ns); - out.add("update_post_to_paint_current_ns", r.update_post_to_paint_current_ns); - out.add("update_post_to_paint_srtt_ns", r.update_post_to_paint_srtt_ns); - out.add("update_post_to_paint_jitter_ns", r.update_to_paint_jitter_ns); - out.add("paint_service_current_ns", r.paint_service_current_ns); - out.add("paint_service_srtt_ns", r.paint_service_srtt_ns); - out.add("paint_service_jitter_ns", r.paint_service_jitter_ns); - out.add("raw_display_interval_current_ns", r.raw_display_interval_current_ns); - out.add("raw_display_interval_srtt_ns", r.raw_display_interval_srtt_ns); - out.add("raw_display_interval_jitter_ns", r.raw_display_interval_jitter_ns); - out.add("accepted_consumer_interval_current_ns", r.accepted_consumer_interval_current_ns); - out.add("accepted_consumer_interval_srtt_ns", r.accepted_consumer_interval_srtt_ns); - out.add("accepted_consumer_interval_jitter_ns", r.accepted_consumer_interval_jitter_ns); - out.add("user_period_ns", r.user_period_ns); - out.add("target_period_ns", r.target_period_ns); - out.add("effective_interval_ns", r.effective_min_render_interval_ns); - out.add("next_render_time_ns", r.next_render_time_ns); - out.add("admission_wait_elapsed_ns", r.admission_wait_elapsed_ns); - out.add("admission_wait_remaining_ns", r.admission_wait_remaining_ns); - out.add("render_slot_idle_wait_ns", frame->render_slot_idle_wait_ns); - out.add("executor_queue_wait_current_ns", frame->executor_queue_wait_ns); - out.add("executor_queue_wait_srtt_ns", r.executor_queue_wait_ns); - out.add("present_queue_wait_ns", r.present_wait_ns); - out.add("paint_return_wait_ns", frame->paint_return_wait_ns); - out.add("consumer_window_size", r.consumer_window_size); - out.add("consumer_window_accepted", r.consumer_window_accepted); - out.add("consumer_window_rejected_no_backlog", r.consumer_window_rejected_no_backlog); - out.add("consumer_window_rejected_starved", r.consumer_window_rejected_starved); - out.add("consumer_window_rejected_no_new_frame", r.consumer_window_rejected_no_new_frame); - out.add("consumer_window_rejected_invalid", r.consumer_window_rejected_invalid); - out.add("consumer_window_accepted_ratio", r.consumer_window_accepted_ratio); - out.add("consumer_accepted_total", r.consumer_sample_accepted_count); - out.add("consumer_rejected_no_backlog_total", r.consumer_sample_rejected_no_backlog_count); - out.add("consumer_rejected_starved_total", r.consumer_sample_rejected_starved_count); - out.add("consumer_rejected_no_new_frame_total", r.consumer_sample_rejected_no_new_frame_count); - out.add("consumer_rejected_invalid_total", r.consumer_sample_rejected_invalid_timestamp_count); - out.add("producer_starvation_total", r.producer_starvation_count); - out.add("producer_starvation_epoch_id", r.producer_starvation_epoch_id); - out.add("producer_starvation_active", log_bool(r.producer_starvation_active)); - out.add("mode_enter_total", r.mode_enter_count); - out.add("mode_exit_total", r.mode_exit_count); - out.add("update_posted", r.update_posted_count); - out.add("duplicate_present_request_suppressed", r.duplicate_present_request_suppressed_count); - out.add("update_ack", r.update_ack_count); - out.add("update_repost", r.update_repost_count); - out.add("paint_without_core_post", r.paint_without_posted_update_count); - out.add("rendered", log_bool(frame->render_end_ns != 0)); - out.add("middle_published", r.middle_published_count); - out.add("middle_superseded", r.middle_superseded_count); - out.add("painting_advanced", r.painting_advanced_count); - out.add("painting_repeated", r.repeated_frame_count); - out.add("underrun", r.underrun_count); - out.add("display_efficiency", r.display_efficiency); - out.add("logger_dropped", dropped_record_count()); - out.add("executor_workers", e.worker_count); - out.add("executor_active_workers", e.active_workers); - out.add("executor_queued_frame_jobs", e.queued_frame_jobs); - out.add("executor_active_frame_jobs", e.active_frame_jobs); - out.add("executor_submitted_topologies", e.submitted_topologies); - out.add("executor_started_topologies", e.started_topologies); - out.add("executor_completed_topologies", e.completed_topologies); - out.add("executor_started_task_nodes", e.started_task_nodes); - out.add("executor_completed_task_nodes", e.completed_task_nodes); - out.add("executor_rejected_frame_jobs", e.rejected_frame_jobs); - out.add("frame_business_task_count", frame->worker.business_task_count); - out.add("frame_business_task_peak_parallelism", frame->worker.business_task_peak_parallelism); - out.add("frame_topology_queue_wait_ns", frame->worker.topology_queue_wait_max_ns); - out.add("frame_business_task_run_total_ns", frame->worker.business_task_run_total_ns); - out.add("render_request_to_start_ns", duration_ns(frame->render_request_ns, frame->render_begin_ns)); - out.add("prepare_ns", duration_ns(frame->prepare_begin_ns, frame->prepare_end_ns)); - out.add("draw_ns", duration_ns(frame->draw_begin_ns, frame->draw_end_ns)); - out.add("render_total_ns", duration_ns(frame->render_begin_ns, frame->render_end_ns)); - out.add("ready_to_paint_ns", duration_ns(frame->transition_12_ns, frame->paint_begin_ns ? frame->paint_begin_ns : frame->superseded_time_ns)); - out.add("paint_ns", duration_ns(frame->paint_begin_ns, frame->paint_end_ns)); - out.add("input_to_display_age_ns", duration_ns(frame->core_data_time_ns, frame->paint_end_ns)); - out.add("e2e_total_ns", duration_ns(frame->render_request_ns, frame->transition_32_ns ? frame->transition_32_ns : frame->superseded_time_ns)); - append_renderable_stats(out, *frame); - append_renderable_cache_stats(out, *frame); - record.line = out.finish(); - record.flush = frame->outcome != Frame_Outcome::Presented || r.last_decision == Low_Latency_Feedback_Decision::Enter_Consumer_Paced || r.last_decision == Low_Latency_Feedback_Decision::Exit_Consumer_Paced; - return enqueue(std::move(record)); -} - -bool Performance_Log_Service::enqueue_feedback_event(Performance_Log_Plot_Identity identity, const Frame_Feedback_Event_Record& event_record) { - const Frame_Lifecycle_Record& frame = event_record.frame; - const Refresh_Control_Snapshot& r = frame.refresh; - Performance_Log_Record record; - record.kind = Performance_Log_Record_Kind::Feedback; - record.flush = event_record.flush; - std::uint64_t window_rejected = r.consumer_window_rejected_no_backlog - + r.consumer_window_rejected_starved - + r.consumer_window_rejected_no_new_frame - + r.consumer_window_rejected_invalid; - Log_Record_Builder out("feedback"); - out.add("schema_version", Log_Schema_Version); - out.add("session_id", log_escape(d->session.session_id)); - out.add("plot_id", identity.plot_id); - out.add("plot_name", log_escape(identity.plot_name)); - out.add("process_id", process_id()); - out.add("thread_id", thread_id_hash()); - out.add("timestamp_ns", event_record.timestamp_ns ? event_record.timestamp_ns : timestamp_for(frame)); - out.add("event", log_escape(event_record.event)); - out.add("frame_id", frame.frame_id); - out.add("mode_before", production_mode_text(event_record.mode_before)); - out.add("mode_after", production_mode_text(event_record.mode_after)); - out.add("decision", feedback_decision_text(r.last_decision)); - out.add("reason", log_escape(r.decision_reason)); - out.add("dirty", log_bool(event_record.dirty)); - out.add("render_active", log_bool(event_record.render_active)); - out.add("middle_has_new_frame", log_bool(event_record.middle_has_new_frame)); - out.add("paint_active", log_bool(event_record.paint_active)); - out.add("paint_update_posted", log_bool(event_record.paint_update_posted)); - out.add("backlog_epoch_active", log_bool(event_record.backlog_epoch_active)); - out.add("continuously_backlogged", log_bool(event_record.continuously_backlogged)); - out.add("producer_starved", log_bool(event_record.producer_starved)); - out.add("backlog_epoch_id", event_record.backlog_epoch_id); - out.add("backlog_rendered_frames", event_record.backlog_rendered_frames); - out.add("backlog_superseded_frames", event_record.backlog_superseded_frames); - out.add("consumer_sample_result", consumer_sample_result_text(r.last_consumer_sample_result)); - out.add("raw_display_sample_ns", r.raw_display_interval_current_ns); - out.add("accepted_consumer_sample_ns", r.accepted_consumer_interval_current_ns); - out.add("window_accepted", r.consumer_window_accepted); - out.add("window_rejected", window_rejected); - out.add("window_ratio", r.consumer_window_accepted_ratio); - out.add("user_period_ns", r.user_period_ns); - out.add("consumer_capacity_ns", r.accepted_consumer_interval_srtt_ns); - out.add("target_period_ns", r.target_period_ns); - out.add("effective_interval_before_ns", event_record.effective_interval_before_ns); - out.add("effective_interval_after_ns", event_record.effective_interval_after_ns); - out.add("next_render_time_ns", r.next_render_time_ns); - record.line = out.finish(); - return enqueue(std::move(record)); -} - -bool Performance_Log_Service::enqueue_feedback_event(Performance_Log_Plot_Identity identity, const Frame_Lifecycle_Record& frame, std::string event, bool flush) { - Frame_Feedback_Event_Record event_record; - event_record.event = std::move(event); - event_record.frame = frame; - event_record.timestamp_ns = timestamp_for(frame); - event_record.dirty = frame.dirty_reason_mask != 0; - event_record.middle_has_new_frame = frame.refresh.middle_published_count > 0; - event_record.paint_active = frame.paint_begin_ns && !frame.paint_end_ns; - event_record.paint_update_posted = frame.update_posted_ns != 0; - event_record.backlog_epoch_active = frame.refresh.consumer_window_size > 0; - event_record.continuously_backlogged = frame.refresh.last_consumer_sample_result == Consumer_Sample_Result::Accepted; - event_record.producer_starved = frame.refresh.producer_starvation_active; - event_record.backlog_epoch_id = frame.refresh.producer_starvation_epoch_id; - event_record.mode_before = frame.refresh.production_mode; - event_record.mode_after = frame.refresh.production_mode; - event_record.effective_interval_before_ns = frame.refresh.effective_min_render_interval_ns; - event_record.effective_interval_after_ns = frame.refresh.effective_min_render_interval_ns; - event_record.flush = flush; - return enqueue_feedback_event(std::move(identity), event_record); -} - -void Performance_Log_Service::shutdown() { - d->stop(); -} - -Performance_Log_Service& performance_log_service() { - static Performance_Log_Service service; - return service; -} - -} // namespace renderive diff --git a/Core/performance/Performance_Log_Service.h b/Core/performance/Performance_Log_Service.h deleted file mode 100644 index dfa2ba5..0000000 --- a/Core/performance/Performance_Log_Service.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#include "Performance_Log_Record.h" -#include "../architecture/Frame_Scheduler.h" -#include -#include - -namespace renderive { - -class Performance_Log_Service { -public: - Performance_Log_Service(); - ~Performance_Log_Service(); - - void configure(Performance_Log_Options options); - [[nodiscard]] Performance_Log_Options options() const; - [[nodiscard]] bool enabled() const; - [[nodiscard]] Performance_Log_Session_Info session_info() const; - [[nodiscard]] std::uint64_t dropped_record_count() const; - - bool enqueue(Performance_Log_Record record); - bool enqueue_frame(Performance_Log_Plot_Identity identity, std::shared_ptr frame); - bool enqueue_feedback_event(Performance_Log_Plot_Identity identity, const Frame_Feedback_Event_Record& event); - bool enqueue_feedback_event(Performance_Log_Plot_Identity identity, const Frame_Lifecycle_Record& frame, std::string event, bool flush = false); - void shutdown(); - -private: - struct Private; - std::unique_ptr d; -}; - -Performance_Log_Service& performance_log_service(); - -} // namespace renderive diff --git a/Core/plot/Plot_Core.cpp b/Core/plot/Plot_Core.cpp deleted file mode 100644 index adae023..0000000 --- a/Core/plot/Plot_Core.cpp +++ /dev/null @@ -1,635 +0,0 @@ -#include "Plot_Core.h" -#include "Plot_Core_Access.h" -#include "../architecture/Renderable.h" -#include "../architecture/Plot_Render_Context.h" -#include "../architecture/Render_Time.h" -#include "../architecture/Update_Completion.h" -#include "../base/global.h" -#include "../render/Canvas.h" -#include "../render/Frame_Prepare_Subflow.h" -#include "../render/Render_Frame_Snapshot.h" -#include -#include -#include -namespace renderive { -class Plot_Core_Private; -struct Frame_Render_Job { - std::shared_ptr owner; - std::uint64_t generation{}; - Render_Frame_Surface frame_surface; - Render_Output* buffer{}; - std::shared_ptr lifecycle; - Render_Frame_Snapshot render_snapshot; - Renderable_Frame_Tree renderables; - std::shared_ptr worker_stat; - std::atomic_uint64_t prepared_output_count{0}; -}; -class Plot_Core_Private final : public std::enable_shared_from_this, public Frame_Flow_Host { -public: - explicit Plot_Core_Private(std::unique_ptr selected_flow) - : flow(std::move(selected_flow)) {} - ~Plot_Core_Private() = default; - bool destroying() const { - return !context || context->destroying.load(std::memory_order_acquire); - } - Frame_Flow_Runtime_Snapshot runtime_snapshot(std::uint64_t now_ns) const override { - return flow ? flow->runtime_snapshot(now_ns, destroying()) : Frame_Flow_Runtime_Snapshot{destroying(), false, false, false, false, now_ns}; - } - bool submit_render_once() { - if (destroying()) - return false; - if (!root) - return false; - Size size{ - context->render_width.load(std::memory_order_acquire), - context->render_height.load(std::memory_order_acquire) - }; - if (size.empty()) - return false; - auto frame_surface = flow ? flow->begin_render_frame() : Render_Frame_Surface{}; - if (!frame_surface) - return false; - Render_Output* buffer = frame_surface.output; - buffer->render_complete.store(false, std::memory_order_release); - cancel_update_states(buffer->presented_update_states); - if (flow) - flow->drain_frame_update_states(buffer->presented_update_states); - std::uint64_t render_role_enter_ns = buffer->metadata ? buffer->metadata->render_role_enter_ns : 0; - buffer->metadata = std::make_shared(); - auto frame_record = buffer->metadata; - Frame_Lifecycle_Record& frame = *frame_record; - std::uint64_t submit_time = steady_now_ns(); - frame.frame_id = ++next_frame_id; - frame.input_version = frame_surface.input_version; - frame.render_request_ns = submit_time; - frame.render_role_enter_ns = render_role_enter_ns ? render_role_enter_ns : submit_time; - frame.core_data_time_ns = latest_input_time_ns.load(std::memory_order_acquire); - frame.raw_input_count = pending_input_count.exchange(0, std::memory_order_acq_rel); - frame.pixel_width = static_cast(size.width); - frame.pixel_height = static_cast(size.height); - frame.viewport_version = context->viewport_version.load(std::memory_order_acquire); - frame.dirty_reason_mask = pending_dirty_reason_mask.exchange(0, std::memory_order_acq_rel); - frame.render_slot_idle_wait_ns = submit_time > frame.render_role_enter_ns ? submit_time - frame.render_role_enter_ns : 0; - auto job = std::make_shared(); - job->owner = shared_from_this(); - job->generation = generation.load(std::memory_order_acquire); - job->frame_surface = std::move(frame_surface); - job->buffer = buffer; - job->lifecycle = frame_record; - job->worker_stat = std::make_shared(); - job->render_snapshot.destroying_token = destroying_token; - job->render_snapshot.size = size; - job->render_snapshot.background_color = background_color(); - job->render_snapshot.previous_frame_time_ns = previous_frame_time_ns; - job->render_snapshot.frame_time_ns = submit_time; - job->render_snapshot.frame_metadata = frame_record.get(); - job->render_snapshot.frame_update_states = &buffer->presented_update_states; - previous_frame_time_ns = submit_time; - job->renderables = renderable_tree_snapshot(); - job->render_snapshot.frame_tree = &job->renderables; - if (!frame_tree_views_complete(job->renderables)) { - frame.outcome = Frame_Outcome::Cancelled; - if (flow) - flow->cancel_render_frame(std::move(job->frame_surface)); - supersede_update_states(buffer->presented_update_states); - release_frame_views(job); - collect_frame(frame_record); - notify_render_finished(frame); - return false; - } - auto& scheduler = Global::instance()->render_scheduler(); - bool submitted = scheduler.try_submit_render_task(Render_Executor_Task{ - plot_execution_id, - frame.frame_id, - Render_Task_Kind::Frame, - Task{}, - [job](Render_Task_Graph& graph, Render_Task_Graph_Node entry, Render_Task_Graph_Node exit) { - auto prepare_begin = graph.emplace(Render_Task_Kind::Frame, Task([job]() { - job->owner->begin_prepare_frame_job(job); - })); - auto prepare_dispatch = graph.emplace_subflow(Render_Task_Kind::Frame, [job](Render_Task_Subflow& subflow) { - job->owner->dispatch_prepare_subflow(job, subflow); - }); - auto prepare_end = graph.emplace(Render_Task_Kind::Frame, Task([job]() { - job->owner->end_prepare_frame_job(job); - })); - auto draw = graph.emplace(Render_Task_Kind::Primitive, Task([job]() { - job->owner->draw_frame_job(job); - })); - auto finalize = graph.emplace(Render_Task_Kind::Frame, Task([job]() { - job->owner->finalize_frame_job(job); - })); - graph.precede(entry, prepare_begin); - graph.precede(prepare_begin, prepare_dispatch); - graph.precede(prepare_dispatch, prepare_end); - graph.precede(prepare_end, draw); - graph.precede(draw, finalize); - graph.precede(finalize, exit); - }, - Task([job]() { - Global::instance()->render_scheduler().post([job]() { - job->owner->complete_frame_job(job); - }); - }), - job->worker_stat, - true - }); - if (submitted) - return true; - frame.outcome = Frame_Outcome::Worker_Budget_Dropped; - frame.worker.rejected_topology_count++; - if (flow) - flow->discard_render_frame(std::move(job->frame_surface)); - release_frame_views(job); - collect_frame(frame_record); - notify_render_finished(frame); - return false; - } - static bool frame_tree_views_complete(const Renderable_Frame_Tree& tree) { - return std::all_of(tree.begin(), tree.end(), [](const Renderable_Frame_Node& node) { - return !node.frame_view_required || static_cast(node.frame_view); - }); - } - static void release_frame_views(const std::shared_ptr& job) { - if (!job) - return; - job->render_snapshot.frame_tree = nullptr; - job->renderables.clear(); - } - static bool frame_job_valid(const std::shared_ptr& job) { - return job && job->buffer && job->lifecycle && !job->render_snapshot.destroying(); - } - void begin_prepare_frame_job(const std::shared_ptr& job) { - if (!frame_job_valid(job)) - return; - Frame_Lifecycle_Record& frame = *job->lifecycle; - std::uint64_t begin_ns = steady_now_ns(); - frame.render_begin_ns = begin_ns; - frame.prepare_begin_ns = begin_ns; - job->prepared_output_count.store(0, std::memory_order_release); - } - void dispatch_prepare_subflow(const std::shared_ptr& job, Render_Task_Subflow& subflow) { - if (!frame_job_valid(job) || !job->lifecycle->prepare_begin_ns) - return; - for (const auto& node : job->renderables) { - const auto& renderable = node.renderable; - if (!renderable || !renderable->should_prepare()) - continue; - build_renderable_prepare(*renderable, subflow, job->render_snapshot, node.frame_view); - job->prepared_output_count.fetch_add(1, std::memory_order_relaxed); - } - } - void end_prepare_frame_job(const std::shared_ptr& job) { - if (!frame_job_valid(job) || !job->lifecycle->prepare_begin_ns) - return; - Frame_Lifecycle_Record& frame = *job->lifecycle; - if (job->render_snapshot.frame_update_states) { - for (const auto& node : job->renderables) { - if (node.renderable) - drain_renderable_prepare_updates(*node.renderable, *job->render_snapshot.frame_update_states); - } - } - frame.prepared_output_count = job->prepared_output_count.load(std::memory_order_acquire); - frame.prepare_end_ns = steady_now_ns(); - if (job->owner && job->owner->context) - job->owner->context->first_prepare_data.store(false, std::memory_order_release); - } - void draw_frame_job(const std::shared_ptr& job) { - if (!frame_job_valid(job) || !job->lifecycle->prepare_end_ns) - return; - Frame_Lifecycle_Record& frame = *job->lifecycle; - job->buffer->render_complete.store(false, std::memory_order_release); - job->buffer->image.resize(job->render_snapshot.size.width, job->render_snapshot.size.height); - job->buffer->image.fill(job->render_snapshot.background_color); - Canvas canvas(job->buffer->image); - frame.draw_begin_ns = steady_now_ns(); - if (!job->renderables.empty() && job->renderables.front().renderable) - job->renderables.front().renderable->render_tree_snapshot(canvas, job->render_snapshot, job->renderables, 0); - frame.draw_end_ns = steady_now_ns(); - frame.render_end_ns = steady_now_ns(); - } - void finalize_frame_job(const std::shared_ptr& job) { - if (!frame_job_valid(job) || !job->lifecycle->render_end_ns) - return; - Frame_Lifecycle_Record& frame = *job->lifecycle; - for (const auto& update_state : job->buffer->presented_update_states) { - if (update_state) - update_state->complete_until(Update_Stage::Rendered); - } - job->buffer->render_complete.store(true, std::memory_order_release); - } - void execute_frame_job(const std::shared_ptr& job) { - if (!job || !job->buffer || !job->lifecycle || job->render_snapshot.destroying()) - return; - begin_prepare_frame_job(job); - Render_Task_Subflow no_subflow(nullptr); - dispatch_prepare_subflow(job, no_subflow); - end_prepare_frame_job(job); - draw_frame_job(job); - finalize_frame_job(job); - } - void complete_frame_job(const std::shared_ptr& job) { - if (!job || !job->buffer || !job->lifecycle) - return; - Frame_Lifecycle_Record& frame = *job->lifecycle; - if (job->worker_stat) { - frame.worker = *job->worker_stat; - frame.executor_queue_wait_ns = frame.worker.topology_queue_wait_total_ns; - } - bool valid_generation = job->generation == generation.load(std::memory_order_acquire); - bool valid_context = !destroying(); - bool render_complete = job->buffer->render_complete.load(std::memory_order_acquire); - if (!valid_generation || !valid_context || !frame.render_end_ns || !render_complete) { - if (flow) - flow->cancel_render_frame(std::move(job->frame_surface)); - frame.outcome = Frame_Outcome::Cancelled; - supersede_update_states(job->buffer->presented_update_states); - release_frame_views(job); - notify_render_finished(frame); - return; - } - auto publish_result = flow ? flow->publish_rendered_frame(std::move(job->frame_surface), steady_now_ns()) : Render_Surface_Publish_Result{}; - release_frame_views(job); - if (publish_result.superseded_frame_record) { - notify_frame_returned(publish_result.superseded_frame_record); - } - if (publish_result.dropped_frame_record) { - collect_frame(publish_result.dropped_frame_record); - } - if (publish_result.request_present) - request_present(publish_result.dirty_rect); - notify_render_finished(frame); - } - void post_scheduler_action(Task task) { - if (!task) - return; - auto guard = context; - auto action = [guard, task = std::move(task)]() mutable { - if (!guard || guard->destroying.load(std::memory_order_acquire)) - return; - task(); - }; - auto& scheduler = Global::instance()->render_scheduler(); - if (scheduler.is_scheduler_thread()) { - action(); - return; - } - scheduler.post(std::move(action)); - } - bool try_render_once() override { - cancel_render_deadline(); - return submit_render_once(); - } - void request_paint_from_middle() override { - auto update_result = flow ? flow->request_present(steady_now_ns()) : Render_Surface_Publish_Result{}; - if (update_result.request_present) - request_present(update_result.dirty_rect); - } - void notify_model_dirty(Dirty_Reason reason, std::uint64_t now_ns) { - if (flow) - flow->notify_model_dirty(reason, now_ns); - } - void notify_viewport_changed(Size viewport, std::uint64_t now_ns) { - if (flow) - flow->notify_viewport_changed(viewport, now_ns); - } - void notify_render_finished(Frame_Lifecycle_Record& frame) { - if (flow) - flow->notify_render_finished(frame, steady_now_ns()); - } - void notify_started() { - if (flow) - flow->activate_view(steady_now_ns()); - } - void notify_paused() { - if (flow) - flow->deactivate_view(); - } - void notify_frame_returned(std::shared_ptr frame) { - if (!frame) - return; - if (flow) - flow->notify_frame_returned(*frame, steady_now_ns()); - collect_frame(frame); - } - void notify_paint_finished() { - if (flow) - flow->notify_paint_finished(steady_now_ns()); - } - void schedule_render_deadline(std::uint64_t deadline_ns) override { - if (deadline_ns <= steady_now_ns()) { - cancel_render_deadline(); - if (flow) - flow->notify_timer_fired(steady_now_ns()); - return; - } - if (scheduled_flow_deadline_ns && scheduled_flow_deadline_ns <= deadline_ns) - return; - scheduled_flow_deadline_ns = deadline_ns; - std::uint64_t generation = ++flow_deadline_generation; - auto guard = context; - auto self = shared_from_this(); - Global::instance()->render_scheduler().post_at(deadline_ns, [guard, self, generation, deadline_ns]() { - if (!guard || guard->destroying.load(std::memory_order_acquire)) - return; - if (self->flow_deadline_generation != generation || self->scheduled_flow_deadline_ns != deadline_ns) - return; - self->scheduled_flow_deadline_ns = 0; - if (self->flow) - self->flow->notify_timer_fired(steady_now_ns()); - }); - } - void cancel_render_deadline() override { - scheduled_flow_deadline_ns = 0; - ++flow_deadline_generation; - } - void dispatch_event(const Event& event) { - if (!root) - return; - switch (event.type) { - case Event_Type::Pointer_Move: { - const auto& pointer_event = static_cast(event); - update_hover_targets(pointer_event); - dispatch_to_renderables(root->hit_renderables(pointer_event.position), event); - break; - } - case Event_Type::Pointer_Press: - case Event_Type::Pointer_Release: { - const auto& pointer_event = static_cast(event); - dispatch_to_renderables(root->hit_renderables(pointer_event.position), event); - break; - } - case Event_Type::Wheel: { - const auto& wheel_event = static_cast(event); - dispatch_to_renderables(root->hit_renderables(wheel_event.position), event); - break; - } - case Event_Type::Key_Press: - case Event_Type::Key_Release: - dispatch_to_renderables(reverse_renderable_snapshot(), event); - break; - case Event_Type::Leave: - clear_hover_targets(); - dispatch_to_renderables(renderable_snapshot(), event); - break; - default: - dispatch_to_renderables(renderable_snapshot(), event); - break; - } - } - std::vector> renderable_snapshot() const { - std::vector> result; - append_renderable_snapshot(result, root); - return result; - } - Renderable_Frame_Tree renderable_tree_snapshot() const { - Renderable_Frame_Tree result; - if (root) - root->append_tree_snapshot(result); - return result; - } - std::vector> reverse_renderable_snapshot() const { - std::vector> result = renderable_snapshot(); - std::reverse(result.begin(), result.end()); - return result; - } - void append_renderable_snapshot(std::vector>& result, const std::shared_ptr& renderable) const { - if (!renderable) - return; - result.push_back(renderable); - for (const auto& child : renderable->children_snapshot()) - append_renderable_snapshot(result, child); - } - void dispatch_to_renderables(const std::vector>& renderables, const Event& event) { - for (const auto& renderable : renderables) { - if (!renderable || event.is_accepted()) - break; - renderable->plot_event(event); - } - } - void update_hover_targets(const Pointer_Event& event) { - Point pos{static_cast(event.position.x), static_cast(event.position.y)}; - std::vector> hits = root->hit_renderables(event.position); - for (const auto& old_target : hover_targets) { - auto target = old_target.lock(); - if (!target) - continue; - if (std::find(hits.begin(), hits.end(), target) == hits.end()) - target->set_hover_state(pos, false); - } - hover_targets.clear(); - hover_targets.reserve(hits.size()); - for (const auto& target : hits) { - if (!target) - continue; - target->set_hover_state(pos, true); - hover_targets.push_back(target); - } - } - void clear_hover_targets() { - Point pos{}; - for (const auto& old_target : hover_targets) { - if (auto target = old_target.lock()) - target->set_hover_state(pos, false); - } - hover_targets.clear(); - } - void request_present(Rect dirty_rect) { - if (auto target = sink.lock()) - target->request_present(dirty_rect); - } - void collect_frame(std::shared_ptr frame) { - if (!frame) - return; - auto observer = std::atomic_load_explicit(&context->frame_lifecycle_observer, std::memory_order_acquire); - if (observer && observer->capture_enabled()) - observer->collect_frame(frame); - } - void record_feedback_event(std::shared_ptr event) override { - if (!event) - return; - auto observer = std::atomic_load_explicit(&context->frame_lifecycle_observer, std::memory_order_acquire); - if (observer && observer->capture_enabled()) - observer->collect_feedback_event(event); - } - Color background_color() const { - Pixel p = context->background_rgba.load(std::memory_order_acquire); - return premultiplied_to_color(p); - } - std::unique_ptr flow; - std::shared_ptr context = std::make_shared(); - std::shared_ptr destroying_token = std::make_shared(false); - std::shared_ptr root; - std::vector> hover_targets; - std::weak_ptr sink; - std::atomic latest_input_time_ns{0}; - std::atomic pending_input_count{0}; - std::atomic pending_dirty_reason_mask{0}; - std::uint64_t flow_deadline_generation{}; - std::uint64_t scheduled_flow_deadline_ns{}; - std::uint64_t next_frame_id{}; - std::uint64_t previous_frame_time_ns{}; - Plot_Execution_Id plot_execution_id{}; - std::atomic generation{1}; -}; -namespace { -Plot_Execution_Id next_plot_execution_id() { - static std::atomic next_id{1}; - return next_id.fetch_add(1, std::memory_order_acq_rel); -} -} // namespace -Plot_Core::Plot_Core(std::unique_ptr flow) - : d(std::make_shared(std::move(flow))) { - d->plot_execution_id = next_plot_execution_id(); - d->context->performance_plot_id.store(d->plot_execution_id, std::memory_order_release); - d->context->owner.store(this, std::memory_order_release); - if (d->flow) - d->flow->attach(*d); - Global::instance()->render_scheduler().register_plot(this); -} -Plot_Core::~Plot_Core() { - d->context->destroying.store(true, std::memory_order_release); - d->destroying_token->store(true, std::memory_order_release); - d->generation.fetch_add(1, std::memory_order_acq_rel); - if (d->flow) - d->flow->shutdown(); - Global::instance()->render_scheduler().remove_plot(this); - if (d->flow) - d->flow->cancel_all_update_states(); - d->context->owner.store(nullptr, std::memory_order_release); -} -void Plot_Core::init() { - if (!d->root) { - d->root = std::make_shared(); - d->root->set_object_name("Root_Renderable"); - d->root->init_root(this); - } - d->context->first_resize.store(true, std::memory_order_release); -} -std::shared_ptr Plot_Core::root_renderable() const { - return d->root; -} -std::shared_ptr Plot_Core::create_renderable_node( - const std::shared_ptr& parent, std::string object_name) { - if (!parent) - return {}; - auto node = std::make_shared(); - node->set_object_name(std::move(object_name)); - node->init(parent); - return node; -} -void Plot_Core::remove_renderable(const std::shared_ptr& renderable) { - if (!renderable) - return; - if (renderable == d->root) - d->root.reset(); - else - renderable->detach_from_parent(); - notify_model_dirty(); -} -void Plot_Core::set_background_color(Color color) { - d->context->background_rgba.store(premultiply(color), std::memory_order_release); - notify_model_dirty(Dirty_Reason::Config); -} -Color Plot_Core::background_color() const { - return d->background_color(); -} -void Plot_Core::set_viewport_size(Size size) { - Size previous{ - d->context->render_width.load(std::memory_order_acquire), - d->context->render_height.load(std::memory_order_acquire) - }; - d->context->render_width.store(size.width, std::memory_order_release); - d->context->render_height.store(size.height, std::memory_order_release); - if (previous != size) - d->context->viewport_version.fetch_add(1, std::memory_order_acq_rel); - if (!size.empty()) - d->context->first_resize.store(false, std::memory_order_release); - if (previous != size) { - d->pending_dirty_reason_mask.fetch_or(dirty_reason_bit(Dirty_Reason::Viewport), std::memory_order_acq_rel); - d->post_scheduler_action([data = d, size]() { - data->notify_viewport_changed(size, steady_now_ns()); - }); - } -} -Size Plot_Core::viewport_size() const { - return { - d->context->render_width.load(std::memory_order_acquire), - d->context->render_height.load(std::memory_order_acquire) - }; -} -void Plot_Core::dispatch_event(const Event& event) { - d->dispatch_event(event); -} -void Plot_Core::notify_model_dirty(Dirty_Reason reason) { - if (d->destroying()) - return; - std::uint64_t now_ns = steady_now_ns(); - if (d->flow) - d->flow->advance_edit_version(); - d->pending_dirty_reason_mask.fetch_or(dirty_reason_bit(reason), std::memory_order_acq_rel); - d->latest_input_time_ns.store(now_ns, std::memory_order_release); - d->pending_input_count.fetch_add(1, std::memory_order_relaxed); - d->post_scheduler_action([data = d, reason, now_ns]() { - data->notify_model_dirty(reason, now_ns); - }); -} -void Plot_Core::request_explicit_render() { - if (d->destroying()) - return; - d->post_scheduler_action([data = d]() { - data->notify_model_dirty(Dirty_Reason::Core_Data, steady_now_ns()); - }); -} -void Plot_Core::activate_view() { - if (d->destroying()) - return; - d->post_scheduler_action([data = d]() { - data->notify_started(); - }); -} -void Plot_Core::deactivate_view() { - d->post_scheduler_action([data = d]() { - data->notify_paused(); - }); -} -bool Plot_Core::view_active() const { - return d->flow && d->flow->view_active(); -} -void Plot_Core::set_presentation_sink(std::weak_ptr sink) { - d->sink = std::move(sink); -} -std::unique_ptr Plot_Core::create_renderable_runtime(Renderable& owner) const { - return d->flow ? d->flow->create_renderable_runtime(owner) : nullptr; -} -Present_Surface_Lease Plot_Core::begin_present() { - Present_Surface_Lease result; - if (!d->root || !d->flow) - return result; - std::shared_ptr returned_frame; - result = d->flow->begin_present(steady_now_ns(), returned_frame); - if (returned_frame) { - d->post_scheduler_action([data = d, frame = std::move(returned_frame)]() mutable { - data->notify_frame_returned(frame); - }); - } - return result; -} -void Plot_Core::end_present(Present_Surface_Lease&& frame, std::uint64_t paint_begin_ns, std::uint64_t paint_end_ns) { - if (d->flow) { - if (auto returned = d->flow->end_present(std::move(frame), paint_begin_ns, paint_end_ns)) - d->collect_frame(returned); - } - d->post_scheduler_action([data = d]() mutable { - data->notify_paint_finished(); - }); -} - -std::shared_ptr Plot_Core_Context_Access::render_context(Plot_Core& plot) { - return plot.d ? plot.d->context : std::shared_ptr{}; -} - -std::shared_ptr Plot_Core_Context_Access::render_context(const Plot_Core& plot) { - return plot.d ? plot.d->context : std::shared_ptr{}; -} -} // namespace renderive diff --git a/Core/plot/Plot_Core.h b/Core/plot/Plot_Core.h deleted file mode 100644 index d509223..0000000 --- a/Core/plot/Plot_Core.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once -#include "../architecture/Render_Config.h" -#include "../base/Color.h" -#include "../base/Geometry.h" -#include "../event/Event.h" -#include "../flow/Frame_Flow.h" -#include "../flow/Present_Surface_Lease.h" -#include "Presentation_Sink.h" -#include -#include - -namespace renderive { - -class Plot_Core_Private; -struct Plot_Core_Context_Access; -class Renderable; -class Renderable_Runtime; - -class Plot_Core { -public: - explicit Plot_Core(std::unique_ptr flow); - ~Plot_Core(); - - void init(); - - [[nodiscard]] std::shared_ptr root_renderable() const; - [[nodiscard]] std::shared_ptr create_renderable_node( - const std::shared_ptr& parent, std::string object_name = {}); - - void remove_renderable(const std::shared_ptr& renderable); - - void set_background_color(Color color); - [[nodiscard]] Color background_color() const; - - void set_viewport_size(Size size); - [[nodiscard]] Size viewport_size() const; - - void dispatch_event(const Event& event); - void notify_model_dirty(Dirty_Reason reason = Dirty_Reason::Core_Data); - void request_explicit_render(); - - void activate_view(); - void deactivate_view(); - [[nodiscard]] bool view_active() const; - - void set_presentation_sink(std::weak_ptr sink); - [[nodiscard]] std::unique_ptr create_renderable_runtime(Renderable& owner) const; - - Present_Surface_Lease begin_present(); - void end_present(Present_Surface_Lease&& frame, std::uint64_t paint_begin_ns, std::uint64_t paint_end_ns); - -private: - friend struct Plot_Core_Context_Access; - std::shared_ptr d; -}; - -} // namespace renderive diff --git a/Core/plot/Plot_Core_Access.h b/Core/plot/Plot_Core_Access.h deleted file mode 100644 index 7a7cdd9..0000000 --- a/Core/plot/Plot_Core_Access.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "../architecture/global.h" -#include - -namespace renderive { - -class Plot_Core; -struct Plot_Render_Context; - -struct LIB_DECL Plot_Core_Context_Access { - [[nodiscard]] static std::shared_ptr render_context(Plot_Core& plot); - [[nodiscard]] static std::shared_ptr render_context(const Plot_Core& plot); -}; - -} // namespace renderive diff --git a/Core/plot/Presentation_Sink.h b/Core/plot/Presentation_Sink.h deleted file mode 100644 index 51d76f0..0000000 --- a/Core/plot/Presentation_Sink.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../base/Geometry.h" - -namespace renderive { - -class Presentation_Sink { -public: - virtual ~Presentation_Sink() = default; - virtual void request_present(Rect dirty_rect) = 0; -}; - -} // namespace renderive diff --git a/Core/plottable/Afterglow.cpp b/Core/plottable/Afterglow.cpp deleted file mode 100644 index 8d0fd2c..0000000 --- a/Core/plottable/Afterglow.cpp +++ /dev/null @@ -1,179 +0,0 @@ -#include -#include "Afterglow_p.h" -#include "../base/Frame_Memory.h" -#include "../base/Memory.h" -namespace renderive { -RENDERIVE_DEFINE_RENDERABLE_BINDING(Afterglow, Afterglow_Private, Afterglow_Render_State, Afterglow_Input_Data, "Afterglow") -std::shared_ptr Afterglow::frequency_axis() { - return d()->edit_state_value(&Afterglow_Render_State::frequency_axis).lock(); -} -void Afterglow::set_frequency_axis(std::shared_ptr value) { - d()->set_state_value(&Afterglow_Render_State::frequency_axis, std::weak_ptr(value)); -} -std::shared_ptr Afterglow::power_axis() { - return d()->edit_state_value(&Afterglow_Render_State::power_axis).lock(); -} -void Afterglow::set_power_axis(std::shared_ptr value) { - d()->set_state_value(&Afterglow_Render_State::power_axis, std::weak_ptr(value)); -} -Range Afterglow::frequency_range() { - return d()->edit_state_value(&Afterglow_Render_State::frequency_range); -} -void Afterglow::set_frequency_range(Range value) { - if (value.length() == 0.0) - return; - d()->set_state_value(&Afterglow_Render_State::frequency_range, std::move(value)); -} -Range Afterglow::power_range() { - return d()->edit_state_value(&Afterglow_Render_State::color_scale).range(); -} -void Afterglow::set_power_range(Range value) { - if (value.size() == 0.0) - return; - Render_Edit_Lease edit(d()); - edit->color_scale.set_range(value); -} -int Afterglow::frequency_point_size() { - return d()->edit_state_value(&Afterglow_Render_State::frequency_bin_count); -} -int Afterglow::power_point_size() { - return d()->edit_state_value(&Afterglow_Render_State::power_bin_count); -} -bool Afterglow::interpolate() { - return d()->edit_state_value(&Afterglow_Render_State::interpolate_power_bins); -} -void Afterglow::set_interpolate(bool value) { - d()->set_state_value(&Afterglow_Render_State::interpolate_power_bins, value); -} -double Afterglow::attenuation_rate() { - return d()->edit_state_value(&Afterglow_Render_State::decay_rate); -} -void Afterglow::set_attenuation_rate(double value) { - d()->set_state_value(&Afterglow_Render_State::decay_rate, std::clamp(value, 0.0, 1.0)); -} -int Afterglow::init_strong_value() { - return d()->edit_state_value(&Afterglow_Render_State::initial_intensity_threshold); -} -void Afterglow::set_init_strong_value(int value) { - d()->set_state_value(&Afterglow_Render_State::initial_intensity_threshold, std::max(1, value)); -} -Color_Map Afterglow::color_map() { - return d()->edit_state_value(&Afterglow_Render_State::color_scale).color_map(); -} -void Afterglow::set_color_map(Color_Map value) { - Render_Edit_Lease edit(d()); - edit->color_scale.set_color_map(std::move(value)); -} -std::shared_ptr Afterglow::color_bar() const { - return const_cast(this)->d()->color_bar.lock(); -} -void Afterglow::set_frequency_point_size(int t) { - Afterglow_Private* pd = d(); - Render_Edit_Lease edit(pd); - edit->frequency_bin_count = std::max(1, t); - edit->power_bin_count = std::max(1, edit->power_bin_count); -} -void Afterglow::set_power_point_size(int t) { - Afterglow_Private* pd = d(); - Render_Edit_Lease edit(pd); - edit->frequency_bin_count = std::max(1, edit->frequency_bin_count); - edit->power_bin_count = std::max(1, t); -} -Afterglow::Builder::Builder(std::shared_ptr parent, std::shared_ptr frequency_axis, std::shared_ptr power_axis) { - init(parent, frequency_axis, power_axis); -} -std::shared_ptr Afterglow::Builder::build() { - auto ret = renderive::make_shared(); - set(ret.get()); - return ret; -} -void Afterglow::append_spectrum(std::span power_range_data) { - if (!ok()) - return; - Afterglow_Private* pd = d(); - int frequency_point_size{}; - { - Render_State_Lease state(pd); - frequency_point_size = state->frequency_bin_count; - } - if (power_range_data.size() != frequency_point_size) - return; - Render_Input_Lease input(pd); - input->pending_spectra.push(power_range_data, Afterglow_Input_Data::Max_Pending_Frame_Count); -} -void Afterglow::append_spectrum(std::pmr::vector&& power_range_data) { - if (!ok()) - return; - Afterglow_Private* pd = d(); - int frequency_point_size{}; - { - Render_State_Lease state(pd); - frequency_point_size = state->frequency_bin_count; - } - if (power_range_data.size() != frequency_point_size) - return; - Render_Input_Lease input(pd); - input->pending_spectra.push(std::move(power_range_data), Afterglow_Input_Data::Max_Pending_Frame_Count); -} -void Afterglow_Private::push_data(std::span power_range_data, const Afterglow_Render_State* s, const Color_Scale_Snapshot& color_scale) { - if (power_range_data.size() != s->frequency_bin_count || - s->initial_intensity_threshold <= 0 || - color_scale.range.size() == 0.0 || - s->frequency_bin_count <= 0 || - s->power_bin_count <= 0) - return; - std::pmr::vector index_list(s->frequency_bin_count, frame_memory_resource()); - double rate = (double)s->power_bin_count / color_scale.range.size(); - for (int i = 0; i < s->frequency_bin_count; ++i) { - int line_index = (int)((power_range_data[i] - color_scale.range.origin) * rate); - if (line_index < 0) - line_index = 0; - if (line_index > s->power_bin_count - 1) - line_index = s->power_bin_count - 1; - index_list[i] = line_index; - } - if (!s->interpolate_power_bins) { - for (int i = 0; i < s->frequency_bin_count; ++i) { - int index = index_list[i] * s->frequency_bin_count + i; - current_intensity_grid[index]++; - } - } - else { - int last_lower = -1, last_upper = -1; - for (int i = 0; i < s->frequency_bin_count; ++i) { - int y = index_list[i]; - current_intensity_grid[y * s->frequency_bin_count + i]++; - if (y - last_upper >= 2) { - for (int cur_y = last_upper + 1; cur_y < y; ++cur_y) { - current_intensity_grid[cur_y * s->frequency_bin_count + i]++; - } - last_upper = y; - last_lower = last_lower + 1; - continue; - } - if (last_lower - y >= 2) { - for (int cur_y = y + 1; cur_y < last_lower; ++cur_y) { - current_intensity_grid[cur_y * s->frequency_bin_count + i]++; - } - last_upper = last_lower - 1; - last_lower = y; - continue; - } - last_upper = y; - last_lower = y; - } - } - current_history_index++; - if (current_history_index < s->initial_intensity_threshold) - return; - current_history_index = 0; - double old_remain = s->decay_rate == 1.0 ? 1.0 : 1.0 - s->decay_rate; - double cache_remain = s->decay_rate / (double)s->initial_intensity_threshold; - for (int i = 0; i < cached_cell_count; ++i) { - merged_intensity_grid[i] = old_remain * previous_intensity_grid[i] + cache_remain * current_intensity_grid[i]; - current_intensity_grid[i] = 0; - } - previous_intensity_grid = merged_intensity_grid; - image_dirty = true; -} -} // namespace renderive diff --git a/Core/plottable/Afterglow.h b/Core/plottable/Afterglow.h deleted file mode 100644 index 132db3e..0000000 --- a/Core/plottable/Afterglow.h +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include "../base/Memory.h" -#include "global.h" -#include "../primitive/Color_Bar.h" -#include "../primitive/Color_Scale.h" -namespace renderive { -class Frequency_Axis; -class Afterglow_Plot; -struct Afterglow_Private; -/// @brief 残影频谱图元,按强度衰减显示历史能量。 -class LIB_DECL Afterglow : public Renderable { -public: - /// @brief 创建残影频谱图元。 - Afterglow(); - /// @brief 绑定频率轴。 - PROP(std::shared_ptr, frequency_axis) - /// @brief 绑定功率轴。 - PROP(std::shared_ptr, power_axis) - /// @brief 频率数据范围。 - PROP(Range, frequency_range) - /// @brief 功率数据范围。 - PROP(Range, power_range) - /// @brief 每帧频率采样点数。 - PROP(int, frequency_point_size) - /// @brief 功率方向采样点数。 - PROP(int, power_point_size) - /// @brief 残影累计缓冲大小。 - /// @brief 是否在功率方向做线性补点。 - PROP(bool, interpolate) - /// @brief 历史残影衰减率。 - PROP(double, attenuation_rate) - /// @brief 初始强度累计阈值。 - PROP(int, init_strong_value) - /// @brief 颜色映射表。 - PROP(Color_Map, color_map) - /// @brief 返回构造时启用的颜色条。 - std::shared_ptr color_bar() const; - /// @brief 写入一帧功率数据。 - void append_spectrum(std::span power_range_data); - /// @brief 移动写入一帧功率数据。 - void append_spectrum(std::pmr::vector&& power_range_data); - /// @brief 用 writer 填充并写入一帧功率数据。 - template - void write_data(std::size_t count, Writer&& writer) { - std::pmr::vector values(memory_resource(Memory_Domain::Afterglow)); - values.resize(count); - std::forward(writer)(std::span(values.data(), values.size())); - append_spectrum(std::move(values)); - } - template - struct Builder_T; - struct Builder; -private: - Afterglow_Private* d(); - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; -}; -struct Afterglow_Prop { - std::weak_ptr frequency_axis{}; - std::weak_ptr power_axis{}; - Range frequency_range{0, 10}; - int frequency_bin_count{}; - int power_bin_count{}; - bool interpolate_power_bins = true; - double decay_rate = 0.2; - int initial_intensity_threshold = 100; - Color_Scale color_scale{Color_Map{}, Range{0, 10}}; - bool color_bar_enabled = false; -}; -template -struct Afterglow::Builder_T { - PROP_BT(std::shared_ptr, frequency_axis) - PROP_BT(std::shared_ptr, power_axis) - PROP_BT(Range, frequency_range) - That& set_power_range(Range value) { - static_cast(this)->color_scale.set_range(value); - return *static_cast(this); - } - PROP_BT(int, frequency_bin_count) - PROP_BT(int, power_bin_count) - PROP_BT(bool, interpolate_power_bins) - PROP_BT(double, decay_rate) - PROP_BT(int, initial_intensity_threshold) - That& set_color_map(Color_Map value) { - static_cast(this)->color_scale.set_color_map(std::move(value)); - return *static_cast(this); - } - PROP_BT(Color_Scale, color_scale) - PROP_BT(bool, color_bar_enabled) - /// 初始化残影构造器。 - void init(std::shared_ptr parent, std::shared_ptr frequency_axis, std::shared_ptr power_axis); - /// 把构造参数写入目标对象。 - void set(Afterglow* t); - std::shared_ptr parent{}; -}; -/// @brief 残影频谱构造器。 -struct LIB_DECL Afterglow::Builder : Afterglow::Builder_T, Afterglow_Prop { - /// @brief 构建并返回残影对象。 - std::shared_ptr build(); - /// @brief 创建残影构造器。 - Builder(std::shared_ptr parent, std::shared_ptr frequency_axis, std::shared_ptr power_axis); -}; -} // namespace renderive diff --git a/Core/plottable/Afterglow_p.h b/Core/plottable/Afterglow_p.h deleted file mode 100644 index 6db765d..0000000 --- a/Core/plottable/Afterglow_p.h +++ /dev/null @@ -1,146 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include "../Axis/Frequency_Axis.h" -#include "../Axis/Frequency_Axis_p.h" -#include "../architecture/Bounded_Input_Buffer.h" -#include "../base/Memory.h" -#include "../base/global.h" -#include "../primitive/Raster_Image_p.h" -#include "Afterglow.h" -namespace renderive { -struct Afterglow_Render_State : Render_State, Afterglow_Prop {}; -struct Afterglow_Input_Data : Input_Data { - static constexpr std::size_t Max_Pending_Frame_Count = 4; - Bounded_Vector_Double_Input_Buffer pending_spectra; - void clear() override { - pending_spectra.clear(); - } -}; -struct Afterglow_Private : Typed_Render_Data { - Raster_Image_Buffer image; - std::weak_ptr color_bar; - bool image_dirty = false; - int current_history_index = 0; - int cached_frequency_bin_count{}; - int cached_power_bin_count{}; - int cached_cell_count{}; - std::uint64_t cache_color_map_hash{}; - std::pmr::vector current_intensity_grid{memory_resource(Memory_Domain::Afterglow)}; - std::pmr::vector previous_intensity_grid{memory_resource(Memory_Domain::Afterglow)}; - std::pmr::vector merged_intensity_grid{memory_resource(Memory_Domain::Afterglow)}; - void update_cache_size(int frequency_point_size, int power_point_size) { - cached_frequency_bin_count = frequency_point_size; - cached_power_bin_count = power_point_size; - cached_cell_count = frequency_point_size * power_point_size; - current_intensity_grid.resize(cached_cell_count); - std::fill(current_intensity_grid.begin(), current_intensity_grid.end(), 0.0); - previous_intensity_grid.resize(cached_cell_count); - std::fill(previous_intensity_grid.begin(), previous_intensity_grid.end(), 0.0); - merged_intensity_grid.resize(cached_cell_count); - std::fill(merged_intensity_grid.begin(), merged_intensity_grid.end(), 0.0); - current_history_index = 0; - } - void push_data(std::span power_range_data, const Afterglow_Render_State* s, const Color_Scale_Snapshot& color_scale); - void draw_image(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Afterglow_Render_State* s, const Color_Scale_Snapshot& color_scale) { - auto frequency_axis = s->frequency_axis.lock(); - auto power_axis = s->power_axis.lock(); - if (!frequency_axis || !power_axis || image.is_null()) - return; - if (s->frequency_range.length() == 0.0 || color_scale.range.length() == 0.0) - return; - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(frequency_axis.get(), power_axis.get(), snapshot); - Axis_Basis_2D basis = Axis_Basis_2D::from_ranges(mapping, s->frequency_range, color_scale.range); - canvas.save(); - canvas.transform( - basis.domain_vector.x / static_cast(image.width()), - basis.domain_vector.y / static_cast(image.width()), - basis.value_vector.x / static_cast(image.height()), - basis.value_vector.y / static_cast(image.height()), - basis.origin.x, - basis.origin.y); - image.draw(canvas, RectF{0.0, 0.0, static_cast(image.width()), static_cast(image.height())}); - canvas.restore(); - } - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Afterglow_Render_State* s = render_state(frame_view); - if (!s) - return; - auto color_scale = s->color_scale.snapshot(); - draw_image(canvas, snapshot, s, *color_scale); - } - void prepare_data(const Render_Frame_Snapshot&, const Renderable_Frame_View& frame_view) override { - Afterglow_Render_State* s = render_state(frame_view); - Afterglow_Input_Data* input = render_input_data(frame_view); - if (!s || !input) - return; - if (s->frequency_bin_count <= 0 || s->power_bin_count <= 0) - return; - auto color_scale = s->color_scale.snapshot(); - if (cached_frequency_bin_count != s->frequency_bin_count || cached_power_bin_count != s->power_bin_count) { - update_cache_size(s->frequency_bin_count, s->power_bin_count); - } - bool image_size_changed = image.width() != s->frequency_bin_count || image.height() != s->power_bin_count; - std::uint64_t color_map_hash = color_scale->color_map.hash(); - if (cache_color_map_hash != color_map_hash) { - cache_color_map_hash = color_map_hash; - image_dirty = true; - } - if (image_size_changed) { - image.resize(s->frequency_bin_count, s->power_bin_count); - image.fill(Color::transparent()); - image_dirty = true; - } - input->pending_spectra.read_all([this, s, color_scale](const auto& data) { - if (data.size() != s->frequency_bin_count) { - return; - } - push_data(data, s, *color_scale); - }); - if (image_dirty) { - image.write_normalized_grid(previous_intensity_grid.data(), s->frequency_bin_count, s->power_bin_count, color_scale->color_map); - image_dirty = false; - } - frame_view.consume_input(); - } -}; -template -void Afterglow::Builder_T::init(std::shared_ptr parent, std::shared_ptr frequency_axis, std::shared_ptr power_axis) { - ASSERT(parent && parent->attached_to_plot(), "Afterglow build error! parent invalid"); - ASSERT(frequency_axis && power_axis, "Afterglow build error! axis == nullptr"); - ASSERT(frequency_axis->attached_to_plot(), "Afterglow build error! frequency_axis->plot == nullptr"); - ASSERT(frequency_axis->shares_plot_with(*power_axis), "Afterglow build error! frequency_axis->plot != power_axis->plot"); - ASSERT(parent->shares_plot_with(*frequency_axis), "Afterglow build error! parent->plot != frequency_axis->plot"); - ASSERT(frequency_axis->orientation() != power_axis->orientation(), "Afterglow build error! axes must be orthogonal"); - this->parent = parent; - static_cast(this)->frequency_axis = frequency_axis; - static_cast(this)->power_axis = power_axis; -} -template -void Afterglow::Builder_T::set(Afterglow* ret) { - ret->init(parent); - std::shared_ptr owner = ret->shared_from_this(); - Afterglow_Private* pd = ret->d(); - Render_Edit_Lease edit(pd); - Afterglow_Render_State* sc = edit.operator->(); - PROP_RT(std::weak_ptr, frequency_axis) - PROP_RT(std::weak_ptr, power_axis) - PROP_RT(Range, frequency_range) - PROP_RT(int, frequency_bin_count) - PROP_RT(int, power_bin_count) - PROP_RT(bool, interpolate_power_bins) - PROP_RT(double, decay_rate) - PROP_RT(int, initial_intensity_threshold) - sc->color_scale = static_cast(this)->color_scale.clone(); - sc->frequency_bin_count = std::max(1, sc->frequency_bin_count); - sc->power_bin_count = std::max(1, sc->power_bin_count); - sc->decay_rate = std::clamp(sc->decay_rate, 0.0, 1.0); - sc->initial_intensity_threshold = std::max(1, sc->initial_intensity_threshold); - if (static_cast(this)->color_bar_enabled) { - auto bar = Color_Bar::Builder(owner).set_color_scale(sc->color_scale).set_name("Afterglow").build(); - pd->color_bar = bar; - } -} -} // namespace renderive diff --git a/Core/plottable/Constellation_Diagram.cpp b/Core/plottable/Constellation_Diagram.cpp deleted file mode 100644 index e6d4261..0000000 --- a/Core/plottable/Constellation_Diagram.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include -#include -#include "../Axis/Abs_Axis_p.h" -#include "../base/Memory.h" -#include "Constellation_Diagram_p.h" -namespace renderive { -RENDERIVE_DEFINE_RENDERABLE_BINDING(Constellation_Diagram, Constellation_Diagram_Private, Constellation_Diagram_State, Constellation_Diagram_Input_Data, "Constellation_Diagram") -std::shared_ptr Constellation_Diagram::i_axis() { - return d()->edit_state_value(&Constellation_Diagram_State::i_axis).lock(); -} -void Constellation_Diagram::set_i_axis(std::shared_ptr value) { - d()->set_state_value(&Constellation_Diagram_State::i_axis, std::weak_ptr(value)); -} -std::shared_ptr Constellation_Diagram::q_axis() { - return d()->edit_state_value(&Constellation_Diagram_State::q_axis).lock(); -} -void Constellation_Diagram::set_q_axis(std::shared_ptr value) { - d()->set_state_value(&Constellation_Diagram_State::q_axis, std::weak_ptr(value)); -} -Range Constellation_Diagram::i_range() { - return d()->edit_state_value(&Constellation_Diagram_State::i_range); -} -void Constellation_Diagram::set_i_range(Range value) { - if (value.length() == 0.0) - return; - d()->set_state_value(&Constellation_Diagram_State::i_range, std::move(value)); -} -Range Constellation_Diagram::q_range() { - return d()->edit_state_value(&Constellation_Diagram_State::q_range); -} -void Constellation_Diagram::set_q_range(Range value) { - if (value.length() == 0.0) - return; - d()->set_state_value(&Constellation_Diagram_State::q_range, std::move(value)); -} -Color Constellation_Diagram::point_color() { - return d()->edit_state_value(&Constellation_Diagram_State::point_color); -} -void Constellation_Diagram::set_point_color(Color value) { - d()->set_state_value(&Constellation_Diagram_State::point_color, std::move(value)); -} -Color Constellation_Diagram::anchor_color() { - return d()->edit_state_value(&Constellation_Diagram_State::anchor_color); -} -void Constellation_Diagram::set_anchor_color(Color value) { - d()->set_state_value(&Constellation_Diagram_State::anchor_color, std::move(value)); -} -int Constellation_Diagram::point_lifetime_ms() { - return d()->edit_state_value(&Constellation_Diagram_State::point_lifetime_ms); -} -void Constellation_Diagram::set_point_lifetime_ms(int value) { - d()->set_state_value(&Constellation_Diagram_State::point_lifetime_ms, std::max(1, value)); -} -Constellation_Diagram_Type Constellation_Diagram::type() { - return d()->edit_state_value(&Constellation_Diagram_State::type); -} -void Constellation_Diagram::set_type(Constellation_Diagram_Type value) { - d()->set_state_value(&Constellation_Diagram_State::type, value); -} -double Constellation_Diagram::phase_offset_radians() { - return d()->edit_state_value(&Constellation_Diagram_State::phase_offset_radians); -} -void Constellation_Diagram::set_phase_offset_radians(double value) { - d()->set_state_value(&Constellation_Diagram_State::phase_offset_radians, value); -} -void Constellation_Diagram::append_point(PointF pos) { - if (!ok()) - return; - Render_Input_Lease input(d()); - input->push(pos); -} -Constellation_Diagram::Builder::Builder(std::shared_ptr parent, std::shared_ptr i_axis, std::shared_ptr q_axis) { - ASSERT(parent, "Planisphere build error! parent == nullptr"); - ASSERT(i_axis && q_axis, "Planisphere build error! axis == nullptr"); - ASSERT(i_axis->attached_to_plot(), "Planisphere build error! i_axis->plot == nullptr"); - ASSERT(i_axis->shares_plot_with(*q_axis), "Planisphere build error! i_axis->plot != q_axis->plot"); - ASSERT(parent->shares_plot_with(*i_axis), "Planisphere build error! parent->plot != axis->plot"); - ASSERT(i_axis->orientation() != q_axis->orientation(), "Planisphere build error! axes must be orthogonal"); - this->parent = parent; - this->i_axis = i_axis; - this->q_axis = q_axis; -} -std::shared_ptr Constellation_Diagram::Builder::build() { - auto ret = renderive::make_shared(); - ret->init(parent); - Constellation_Diagram_Private* pd = ret->d(); - Render_Edit_Lease edit(pd); - Constellation_Diagram_State* sc = edit.operator->(); - PROP_R(std::weak_ptr, i_axis) - PROP_R(std::weak_ptr, q_axis) - PROP_R(Range, i_range) - PROP_R(Range, q_range) - PROP_R(Color, point_color) - PROP_R(Color, anchor_color) - PROP_R(int, point_lifetime_ms) - PROP_R(Constellation_Diagram_Type, type) - PROP_R(double, phase_offset_radians) - if (sc->i_range.length() == 0.0) - sc->i_range = {0.0, 1.0}; - if (sc->q_range.length() == 0.0) - sc->q_range = {0.0, 1.0}; - sc->point_lifetime_ms = std::max(1, sc->point_lifetime_ms); - return ret; -} -void Constellation_Diagram::fit_square_to_axes() { - std::shared_ptr i_axis; - std::shared_ptr q_axis; - { - Render_State_Lease state(d()); - if (state) { - i_axis = state->i_axis.lock(); - q_axis = state->q_axis.lock(); - } - } - if (!i_axis || !q_axis) - return; - double w = i_axis->pixel_length(), h = q_axis->pixel_length(); - if (w <= 0.0 || h <= 0.0) - return; - double l = w < h ? w : h; - double i_l = i_axis->coord_range().size() / w * l / 2; - double q_l = q_axis->coord_range().size() / h * l / 2; - double im = i_axis->coord_range().center(), qm = q_axis->coord_range().center(); - set_i_range({im - i_l, im + i_l}); - set_q_range({qm - q_l, qm + q_l}); -} -} // namespace renderive diff --git a/Core/plottable/Constellation_Diagram.h b/Core/plottable/Constellation_Diagram.h deleted file mode 100644 index 7297def..0000000 --- a/Core/plottable/Constellation_Diagram.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once -#include "../base/Color.h" -#include "../base/Geometry.h" -#include "global.h" -namespace renderive { -/// @brief 星座图调制类型。 -/// @brief PSK constellation phase-state count. -enum class Constellation_Diagram_Type : std::uint8_t { - /// @brief 8PSK 星座图。 - /// @brief 4PSK constellation, commonly also called QPSK. - Psk4 = 4, - /// @brief 8PSK constellation. - Psk8 = 8, - /// @brief 16PSK constellation. - Psk16 = 16 -}; -struct Constellation_Diagram_Private; -class Grid; -/// @brief I/Q 星座图元。 -class LIB_DECL Constellation_Diagram : public Renderable { -public: - /// @brief 创建 I/Q 星座图元。 - Constellation_Diagram(); - /// @brief 写入一个 I/Q 点。 - void append_point(PointF pos); - /// @brief 绑定 I 轴。 - PROP(std::shared_ptr, i_axis) - /// @brief 绑定 Q 轴。 - PROP(std::shared_ptr, q_axis) - /// @brief I 轴星座范围。 - PROP(Range, i_range) - /// @brief Q 轴星座范围。 - PROP(Range, q_range) - /// @brief 输入点颜色。 - PROP(Color, point_color) - /// @brief 锚点颜色。 - PROP(Color, anchor_color) - /// @brief 输入点保留时间。 - PROP(int, point_lifetime_ms) - /// @brief 星座调制类型。 - PROP(Constellation_Diagram_Type, type) - /// @brief PSK constellation phase offset in radians. - PROP(double, phase_offset_radians) - /// @brief 设置坐标轴到星座图中心范围。 - void fit_square_to_axes(); - struct Builder; -private: - Constellation_Diagram_Private* d(); - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; -}; -struct Constellation_Diagram_Prop { - std::weak_ptr i_axis{}; - std::weak_ptr q_axis{}; - Range i_range{0, 100}; - Range q_range{0, 100}; - Color point_color = Color::red(); - Color anchor_color = Color::yellow(); - int point_lifetime_ms = 1000; - Constellation_Diagram_Type type = Constellation_Diagram_Type::Psk8; - double phase_offset_radians = 0.0; -}; -/// @brief 星座图构造器。 -struct LIB_DECL Constellation_Diagram::Builder : Constellation_Diagram_Prop { - PROP_B(std::shared_ptr, parent) - PROP_B(std::shared_ptr, i_axis) - PROP_B(std::shared_ptr, q_axis) - PROP_B(Range, i_range) - PROP_B(Range, q_range) - PROP_B(Color, point_color) - PROP_B(Color, anchor_color) - PROP_B(int, point_lifetime_ms) - PROP_B(Constellation_Diagram_Type, type) - PROP_B(double, phase_offset_radians) - /// 创建星座图构造器。 - Builder(std::shared_ptr parent, std::shared_ptr i_axis, std::shared_ptr q_axis); - /// 构建并返回星座图对象。 - std::shared_ptr build(); - std::shared_ptr parent{}; -}; -} // namespace renderive diff --git a/Core/plottable/Constellation_Diagram_p.h b/Core/plottable/Constellation_Diagram_p.h deleted file mode 100644 index 51a6d7b..0000000 --- a/Core/plottable/Constellation_Diagram_p.h +++ /dev/null @@ -1,159 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include "../architecture/Render_Lease.h" -#include "../Axis/Axis_p.h" -#include "../architecture/Bounded_Input_Buffer.h" -#include "../base/Frame_Memory.h" -#include "../base/Memory.h" -#include "../base/Color.h" -#include "../base/Geometry.h" -#include "../base/Style.h" -#include "../render/Canvas.h" -#include "../render/Render_Frame_Snapshot.h" -#include "Constellation_Diagram.h" -namespace renderive { -struct Constellation_Diagram_Data { - PointF pos; - std::uint64_t birth_time_ns{}; - std::uint64_t expire_time_ns{}; -}; -struct Constellation_Diagram_Input_Data : Input_Data { - static constexpr std::size_t Max_Pending_Point_Count = 128; - struct Packet { - double x{}; - double y{}; - }; - Bounded_Input_Buffer data_list; - void push(PointF pos) { - Packet packet{pos.x, pos.y}; - data_list.push(&packet, sizeof(packet), Max_Pending_Point_Count); - } - template - void read_all(Handler handler) { - data_list.read_all([&handler](const void* data, std::size_t size) { - (void)size; - Packet packet; - std::memcpy(&packet, data, sizeof(packet)); - handler(PointF{packet.x, packet.y}); - }); - } - void clear() override { - data_list.clear(); - } -}; -struct Constellation_Diagram_State : Render_State, Constellation_Diagram_Prop {}; -struct Constellation_Diagram_Private : Typed_Render_Data { - std::pmr::list data_list{memory_resource(Memory_Domain::Constellation_Diagram)}; - std::pmr::vector prepared_points{memory_resource(Memory_Domain::Constellation_Diagram)}; - std::pmr::vector prepared_colors{memory_resource(Memory_Domain::Constellation_Diagram)}; - std::pmr::vector prepared_anchor_points{memory_resource(Memory_Domain::Constellation_Diagram)}; - std::pmr::vector anchor_points(const Constellation_Diagram_State* s) const { - std::pmr::vector points(frame_memory_resource()); - const unsigned order = static_cast(s->type); - if (order == 0) - return points; - constexpr double pi = 3.14159265358979323846; - const double center_x = s->i_range.center(); - const double center_y = s->q_range.center(); - const double radius = std::min(s->i_range.size(), s->q_range.size()) / 3.0; - points.reserve(order); - for (unsigned i = 0; i < order; ++i) { - const double angle = s->phase_offset_radians + 2.0 * pi * static_cast(i) / static_cast(order); - points.push_back({center_x + radius * std::cos(angle), center_y + radius * std::sin(angle)}); - } - return points; - } - void refresh(std::uint64_t now_ns) { - for (auto it = data_list.begin(); it != data_list.end();) { - if (it->expire_time_ns <= now_ns) - data_list.erase(it++); - else - ++it; - } - } - void prepare_data(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Constellation_Diagram_Input_Data* d = render_input_data(frame_view); - Constellation_Diagram_State* s = render_state(frame_view); - if (!d || !s) - return; - std::uint64_t now_ns = snapshot.frame_time_ns; - const std::uint64_t lifetime_ns = static_cast(std::max(1, s->point_lifetime_ms)) * 1000000ull; - d->read_all([this, now_ns, lifetime_ns](const PointF& pos) { - Constellation_Diagram_Data data; - data.pos = pos; - data.birth_time_ns = now_ns; - data.expire_time_ns = now_ns + lifetime_ns; - data_list.push_back(data); - }); - frame_view.consume_input(); - refresh(now_ns); - prepared_points.clear(); - prepared_colors.clear(); - prepared_points.reserve(static_cast(data_list.size())); - prepared_colors.reserve(static_cast(data_list.size())); - for (const Constellation_Diagram_Data& data : data_list) { - Color color = s->point_color; - const std::uint64_t total_ns = data.expire_time_ns > data.birth_time_ns ? data.expire_time_ns - data.birth_time_ns : 1; - const std::uint64_t remain_ns = data.expire_time_ns > now_ns ? data.expire_time_ns - now_ns : 0; - color.a = static_cast(static_cast(color.a) * remain_ns / total_ns); - prepared_points.push_back(data.pos); - prepared_colors.push_back(color); - } - prepared_anchor_points = anchor_points(s); - } - PointF map_point(const Axis_Mapping_2D& mapping, const PointF& point) const { - return mapping.map(point); - } - void draw_grid(Canvas& canvas, const Axis_Mapping_2D& mapping, const Constellation_Diagram_State* s) const { - canvas.set_pen(Pen{Color::white(), 1.0, Line_Style::Dot}); - constexpr int divisions = 4; - for (int i = 0; i <= divisions; ++i) { - const double t = static_cast(i) / static_cast(divisions); - const double i_coord = s->i_range.origin + s->i_range.length() * t; - const double q_coord = s->q_range.origin + s->q_range.length() * t; - canvas.draw_line(mapping.map(i_coord, s->q_range.origin), mapping.map(i_coord, s->q_range.target)); - canvas.draw_line(mapping.map(s->i_range.origin, q_coord), mapping.map(s->i_range.target, q_coord)); - } - } - void draw_points(Canvas& canvas, const Axis_Mapping_2D& mapping) const { - canvas.set_antialias(true); - canvas.set_pen(Pen{.style = Line_Style::None}); - for (std::size_t i = 0; i < prepared_points.size(); ++i) { - const Color color = i < prepared_colors.size() ? prepared_colors[i] : Color::red(); - canvas.set_brush(Brush{color, Brush_Style::Solid}); - canvas.draw_ellipse(map_point(mapping, prepared_points[i]), 2.0, 2.0); - } - } - void draw_anchors(Canvas& canvas, const Axis_Mapping_2D& mapping, Color color) const { - canvas.set_antialias(true); - canvas.set_pen(Pen{color, 2.0}); - canvas.set_brush(Brush{}); - constexpr double half = 6.0; - for (const PointF& point : prepared_anchor_points) { - PointF pixel = map_point(mapping, point); - canvas.draw_line(PointF{pixel.x - half, pixel.y}, PointF{pixel.x + half, pixel.y}); - canvas.draw_line(PointF{pixel.x, pixel.y - half}, PointF{pixel.x, pixel.y + half}); - } - } - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Constellation_Diagram_State* s = render_state(frame_view); - if (!s) - return; - auto i_axis = s->i_axis.lock(); - auto q_axis = s->q_axis.lock(); - if (!i_axis || !q_axis) - return; - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(i_axis.get(), q_axis.get(), snapshot); - canvas.save(); - draw_grid(canvas, mapping, s); - draw_points(canvas, mapping); - draw_anchors(canvas, mapping, s->anchor_color); - canvas.restore(); - } -}; -} // namespace renderive diff --git a/Core/plottable/Frequency_Trace.cpp b/Core/plottable/Frequency_Trace.cpp deleted file mode 100644 index ed0c586..0000000 --- a/Core/plottable/Frequency_Trace.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "../Axis/Time_Axis.h" -#include "Frequency_Trace_p.h" -#include "../base/Memory.h" -namespace renderive { -RENDERIVE_DEFINE_RENDERABLE_BINDING(Frequency_Trace, Frequency_Trace_Private, Frequency_Trace_Render_State, Frequency_Trace_Input_Data, "Frequency_Trace") -std::shared_ptr Frequency_Trace::time_axis() { - return d()->edit_state_value(&Frequency_Trace_Render_State::time_axis).lock(); -} -void Frequency_Trace::set_time_axis(std::shared_ptr value) { - d()->set_state_value(&Frequency_Trace_Render_State::time_axis, std::weak_ptr(value)); -} -std::shared_ptr Frequency_Trace::value_axis() { - return d()->edit_state_value(&Frequency_Trace_Render_State::value_axis).lock(); -} -void Frequency_Trace::set_value_axis(std::shared_ptr value) { - d()->set_state_value(&Frequency_Trace_Render_State::value_axis, std::weak_ptr(value)); -} -Frequency_Trace::Builder::Builder(std::shared_ptr parent, std::shared_ptr time_axis, std::shared_ptr value_axis) { - ASSERT(parent && parent->attached_to_plot(), "Audio_Frequency build error! parent invalid"); - ASSERT(time_axis && value_axis, "Audio_Frequency build error! axis == nullptr"); - ASSERT(time_axis->attached_to_plot(), "Audio_Frequency build error! time_axis->plot == nullptr"); - ASSERT(time_axis->shares_plot_with(*value_axis), "Audio_Frequency build error! time_axis->plot != value_axis->plot"); - ASSERT(parent->shares_plot_with(*time_axis), "Audio_Frequency build error! parent->plot != time_axis->plot"); - ASSERT(time_axis->orientation() != value_axis->orientation(), "Audio_Frequency build error! axes must be orthogonal"); - this->parent = parent; - this->time_axis = time_axis; - this->value_axis = value_axis; -} -std::shared_ptr Frequency_Trace::Builder::build() { - auto ret = renderive::make_shared(); - ret->init(parent); - Frequency_Trace_Private* pd = ret->d(); - Render_Edit_Lease edit(pd); - Frequency_Trace_Render_State* sc = edit.operator->(); - PROP_R(std::weak_ptr, time_axis) - PROP_R(std::weak_ptr, value_axis) - sc->pen = Pen{.color = color, .width = 1.0}; - return ret; -} -void Frequency_Trace::append_sample(int tick, double data) { - if (!ok()) - return; - Render_Input_Lease input(d()); - input->push(tick, data); -} -void Frequency_Trace::append_sample(Time_Of_Day time, double data) { - if (!ok()) - return; - auto axis = time_axis(); - if (!axis) - return; - auto stream = axis->timeline_stream(); - if (!stream) - return; - int tick = stream->append_time(time); - Render_Input_Lease input(d()); - input->push(tick, data); -} -} // namespace renderive diff --git a/Core/plottable/Frequency_Trace.h b/Core/plottable/Frequency_Trace.h deleted file mode 100644 index 202fdc9..0000000 --- a/Core/plottable/Frequency_Trace.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -#include "../Axis/Axis.h" -#include "../base/Color.h" -#include "../base/Style.h" -#include "../base/Time_Of_Day.h" -#include "global.h" -namespace renderive { -class Time_Axis; -struct Frequency_Trace_Private; -/// @brief 音频频率随时间变化的折线图元。 -class LIB_DECL Frequency_Trace : public Renderable { -public: - /// @brief 创建音频频率图元。 - Frequency_Trace(); - /// @brief 写入指定 tick 对应的音频频率值。 - void append_sample(int tick, double data); - /// @brief 写入指定时间对应的音频频率值。 - void append_sample(Time_Of_Day time, double data); - /// @brief 绑定时间轴。 - PROP(std::shared_ptr, time_axis) - /// @brief 绑定数值轴。 - PROP(std::shared_ptr, value_axis) - /// @brief 曲线颜色。 - struct Builder; -private: - Frequency_Trace_Private* d(); - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; -}; -struct Frequency_Trace_Prop { - std::weak_ptr time_axis; - std::weak_ptr value_axis{}; - Pen pen{.color = Color::yellow(), .width = 1.0}; -}; -struct LIB_DECL Frequency_Trace::Builder : Frequency_Trace_Prop { - PROP_B(std::shared_ptr, time_axis) - PROP_B(std::shared_ptr, value_axis) - PROP_B(Color, color) - /// 创建音频频率构造器。 - Builder(std::shared_ptr parent, std::shared_ptr time_axis, std::shared_ptr value_axis); - /// 构建并返回音频频率对象。 - std::shared_ptr build(); - std::shared_ptr parent{}; - Color color = Color::yellow(); -}; -} // namespace renderive diff --git a/Core/plottable/Frequency_Trace_p.h b/Core/plottable/Frequency_Trace_p.h deleted file mode 100644 index f0df955..0000000 --- a/Core/plottable/Frequency_Trace_p.h +++ /dev/null @@ -1,175 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include "../Axis/Axis_p.h" -#include "../Axis/Time_Axis_p.h" -#include "../architecture/Bounded_Input_Buffer.h" -#include "../architecture/Renderable.h" -#include "../base/Memory.h" -#include "../primitive/Curve_Utils_p.h" -#include "../render/Render_Frame_Snapshot.h" -#include "Psc_Cpp_Core/Base/RingBuffer.hpp" -#include "Frequency_Trace.h" -namespace renderive { -struct Frequency_Sample { - int tick{}; - double power{}; -}; -struct Frequency_Trace_Input_Data : Input_Data { - static constexpr std::size_t Max_Pending_Value_Count = 256; - Bounded_Input_Buffer data_list; - void push(int tick, double power) { - Frequency_Sample data{tick, power}; - data_list.push(&data, sizeof(data), Max_Pending_Value_Count); - } - template - void read_all(Handler handler) { - data_list.read_all([&handler](const void* data, std::size_t size) { - (void)size; - Frequency_Sample value; - std::memcpy(&value, data, sizeof(value)); - handler(value); - }); - } - void clear() override { - data_list.clear(); - } -}; -struct Frequency_Trace_Render_State : Render_State, Frequency_Trace_Prop {}; -struct Frequency_Trace_Private : Typed_Render_Data { - Psc::StreamRingBuffer_ST buffer{memory_resource(Memory_Domain::Audio)}; - std::pmr::vector data_snapshot{memory_resource(Memory_Domain::Audio)}; - std::pmr::vector prepared_points{memory_resource(Memory_Domain::Audio)}; - std::pmr::vector segment_offsets{memory_resource(Memory_Domain::Audio)}; - int data_count{}; - int point_count{}; - int tick_gap_count{}; - int non_monotonic_tick_count{}; - int segment_count{}; - static bool trace_debug_enabled() { - static const bool enabled = []() { - const char* value = std::getenv("RENDERIVE_FREQUENCY_TRACE_DEBUG"); - return value && std::atoi(value) != 0; - }(); - return enabled; - } - void resize_power_buffer(int count) { - point_count = count; - data_count = 0; - data_snapshot.resize(count); - buffer.init(sizeof(Frequency_Sample) * count); - } - void push_power(const Frequency_Sample& data) { - if (data_count == point_count) { - Frequency_Sample ignored; - std::size_t len = sizeof(Frequency_Sample); - buffer.read(&ignored, len); - --data_count; - } - buffer.write(&data, sizeof(data)); - ++data_count; - } - void snapshot_power_buffer() { - const std::size_t copied = buffer.peek_best_effort(data_snapshot.data(), sizeof(Frequency_Sample) * data_count); - data_count = static_cast(copied / sizeof(Frequency_Sample)); - } - void rebuild_prepared_points(const Timeline_Stream_Snapshot& timeline) { - prepared_points.clear(); - segment_offsets.clear(); - tick_gap_count = 0; - non_monotonic_tick_count = 0; - segment_count = 0; - prepared_points.reserve(static_cast(data_count)); - segment_offsets.reserve(static_cast(data_count)); - - bool in_segment = false; - bool has_previous_tick = false; - int previous_tick = 0; - auto start_segment = [this, &in_segment]() { - if (in_segment) - return; - segment_offsets.push_back(prepared_points.size()); - in_segment = true; - }; - for (int i = 0; i < data_count; ++i) { - const Frequency_Sample& item = data_snapshot[static_cast(i)]; - int coord{}; - if (!timeline.coord_by_stream_tick(item.tick, coord)) { - in_segment = false; - has_previous_tick = false; - continue; - } - if (has_previous_tick) { - if (item.tick <= previous_tick) { - ++non_monotonic_tick_count; - in_segment = false; - } - else if (item.tick != previous_tick + 1) { - ++tick_gap_count; - in_segment = false; - } - } - start_segment(); - prepared_points.emplace_back(static_cast(coord), item.power); - previous_tick = item.tick; - has_previous_tick = true; - } - segment_count = static_cast(segment_offsets.size()); - if (trace_debug_enabled()) { - std::fprintf(stderr, - "frequency_tick_gap_count=%d frequency_non_monotonic_count=%d frequency_segment_count=%d\n", - tick_gap_count, - non_monotonic_tick_count, - segment_count); - } - } - void prepare_data(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Frequency_Trace_Render_State* s = render_state(frame_view); - Frequency_Trace_Input_Data* d = render_input_data(frame_view); - if (!s || !d) - return; - auto time_axis = s->time_axis.lock(); - auto value_axis = s->value_axis.lock(); - if (!time_axis || !value_axis) - return; - auto timeline = Time_Axis_Render_Access::capture_timeline(time_axis.get(), snapshot); - if (!timeline) - return; - if (point_count != timeline->visible_time_point_count) - resize_power_buffer(timeline->visible_time_point_count); - d->read_all([this](const Frequency_Sample& data) { - push_power(data); - }); - snapshot_power_buffer(); - rebuild_prepared_points(*timeline); - frame_view.consume_input(); - } - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Frequency_Trace_Render_State* s = render_state(frame_view); - if (!s) - return; - auto time_axis = s->time_axis.lock(); - auto value_axis = s->value_axis.lock(); - if (!time_axis || !value_axis || prepared_points.empty()) - return; - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(time_axis.get(), value_axis.get(), snapshot); - canvas.save(); - std::pmr::vector pixels(frame_memory_resource()); - for (std::size_t i = 0; i < segment_offsets.size(); ++i) { - const std::size_t begin = segment_offsets[i]; - const std::size_t end = i + 1 < segment_offsets.size() ? segment_offsets[i + 1] : prepared_points.size(); - if (end <= begin + 1) - continue; - pixels.clear(); - pixels.reserve(end - begin); - for (std::size_t point_index = begin; point_index < end; ++point_index) - pixels.push_back(mapping.map(prepared_points[point_index])); - Curve_Utils::draw_polyline(&canvas, pixels, s->pen); - } - canvas.restore(); - } -}; -} diff --git a/Core/plottable/Hover_Tooltip.cpp b/Core/plottable/Hover_Tooltip.cpp deleted file mode 100644 index bbcc8af..0000000 --- a/Core/plottable/Hover_Tooltip.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "Hover_Tooltip.h" -#include "../Axis/Abs_Axis_p.h" -namespace renderive { -void Hover_Tooltip_State::draw_hover_tooltip(Canvas* canvas, Abs_Axis* domain_axis, Abs_Axis* value_axis, Hover_Tooltip_Provider* renderable, const Render_Frame_Snapshot* snapshot) { - Point pos = hover_position; - std::vector lines = renderable->build_hover_lines(domain_axis, value_axis, pos, snapshot); - double max_width = 0; - double line_height = 0; - for (const std::string& s : lines) { - Text_Metrics metrics = canvas->measure_text(hover_info_font, s); - max_width = std::max(max_width, metrics.width); - line_height = std::max(line_height, metrics.line_height()); - } - Rect rect(pos.x + hover_info_left, pos.y + hover_info_top, - static_cast(max_width) + hover_info_right + hover_info_left, - static_cast(line_height * lines.size()) + hover_info_bottom); - canvas->set_brush(tooltip_background_brush); - canvas->fill_rect(RectF{(double)rect.x, (double)rect.y, (double)rect.width, (double)rect.height}); - canvas->set_pen(tooltip_text_pen); - canvas->set_font(hover_info_font); - int lh = static_cast(line_height); - for (int i = 0; i < (int)lines.size(); ++i) { - RectF line_rect((double)rect.x, (double)(rect.y + i * lh), (double)rect.width, (double)lh); - canvas->draw_text(line_rect, Text_Align::Center, lines[i]); - } -} -bool Hover_Tooltip_State::can_show_hover(Renderable* able) const { - auto tmp = dynamic_cast(able); - return use_hover_tooltip && hover_info_active && tmp->hit_test_hover(hover_position); -} -std::vector Hover_Tooltip_Provider::build_hover_lines(Abs_Axis* h, Abs_Axis* v, Point pos, const Render_Frame_Snapshot* snapshot) { - Axis_Mapping_2D mapping = snapshot ? Axis_Render_Access::mapping(h, v, *snapshot) : Axis_Render_Access::mapping(h, v); - PointF screen_point{static_cast(pos.x), static_cast(pos.y)}; - double tick1 = mapping.domain.pixel_to_coord(mapping.domain_pixel(screen_point)); - double tick2 = mapping.value.pixel_to_coord(mapping.value_pixel(screen_point)); - std::string line1 = (snapshot ? Axis_Render_Access::tick_label(h, tick1, *snapshot) : Axis_Render_Access::tick_label(h, tick1)) - + (snapshot ? Axis_Render_Access::unit_text(h, *snapshot) : h->unit_text()); - std::string line2 = (snapshot ? Axis_Render_Access::tick_label(v, tick2, *snapshot) : Axis_Render_Access::tick_label(v, tick2)) - + (snapshot ? Axis_Render_Access::unit_text(v, *snapshot) : v->unit_text()); - return {line1, line2}; -} -} // namespace renderive diff --git a/Core/plottable/Hover_Tooltip.h b/Core/plottable/Hover_Tooltip.h deleted file mode 100644 index 1ddc907..0000000 --- a/Core/plottable/Hover_Tooltip.h +++ /dev/null @@ -1,184 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include "../Axis/export.h" -#include "../architecture/Render_Data.h" -#include "../base/Color.h" -#include "../base/Geometry.h" -#include "../base/Style.h" -#include "../base/Text.h" -#include "../render/Canvas.h" -namespace renderive { -struct Render_Frame_Snapshot; -struct Hover_Tooltip_State; -struct Hover_Tooltip_Provider; -struct LIB_DECL Hover_Tooltip_State { - virtual ~Hover_Tooltip_State() = default; - Hover_Tooltip_State() { - tooltip_background_brush = Brush{Color::white(), Brush_Style::Solid}; - tooltip_text_pen = Pen{Color::black()}; - } - int hover_info_left = 4; - int hover_info_top = 4; - int hover_info_right = 4; - int hover_info_bottom = 4; - Brush tooltip_background_brush; - Font hover_info_font; - Pen tooltip_text_pen; - bool use_hover_tooltip = true; - bool hover_info_active = false; - Point hover_position; - void draw_hover_tooltip(Canvas* canvas, Abs_Axis* domain_axis, Abs_Axis* value_axis, Hover_Tooltip_Provider* renderable, const Render_Frame_Snapshot* snapshot = nullptr); - bool can_show_hover(Renderable* able) const; -}; -struct LIB_DECL Hover_Tooltip_Provider { - struct Hover_Read_Guard { - Hover_Read_Guard() = default; - Hover_Read_Guard(Hover_Tooltip_State* state, Render_State_Read_Guard guard) - : state(state), guard(std::move(guard)) {} - Hover_Read_Guard(const Hover_Read_Guard&) = delete; - Hover_Read_Guard& operator=(const Hover_Read_Guard&) = delete; - Hover_Read_Guard(Hover_Read_Guard&&) noexcept = default; - Hover_Read_Guard& operator=(Hover_Read_Guard&&) noexcept = default; - ~Hover_Read_Guard() = default; - Hover_Tooltip_State* operator->() const { - return state; - } - explicit operator bool() const { - return state && guard; - } - - Hover_Tooltip_State* state{}; - Render_State_Read_Guard guard; - }; - - struct Hover_Edit_Guard { - Hover_Edit_Guard() = default; - Hover_Edit_Guard(Hover_Tooltip_State* state, Render_State_Write_Guard guard) - : state(state), guard(std::move(guard)) {} - Hover_Edit_Guard(const Hover_Edit_Guard&) = delete; - Hover_Edit_Guard& operator=(const Hover_Edit_Guard&) = delete; - Hover_Edit_Guard(Hover_Edit_Guard&&) noexcept = default; - Hover_Edit_Guard& operator=(Hover_Edit_Guard&&) noexcept = default; - ~Hover_Edit_Guard() = default; - Hover_Tooltip_State* operator->() const { - return state; - } - explicit operator bool() const { - return state && guard; - } - - Hover_Tooltip_State* state{}; - Render_State_Write_Guard guard; - }; - virtual std::vector build_hover_lines(Abs_Axis* h, Abs_Axis* v, Point pos, const Render_Frame_Snapshot* snapshot = nullptr); - virtual ~Hover_Tooltip_Provider() = default; - virtual void draw_hover_tooltip(Canvas* canvas, Abs_Axis* h, Abs_Axis* v) { - auto state = begin_hover_read(); - if (state) - state->draw_hover_tooltip(canvas, h, v, this); - } - virtual bool hover_ok(Renderable* able) { - auto state = begin_hover_read(); - return state && state->can_show_hover(able); - } - virtual bool hit_test_hover(Point pos) { - (void)pos; - return true; - } - bool hover_tooltip_enabled() { - auto state = begin_hover_read(); - return state && state->use_hover_tooltip; - } - void set_use_hover_info(bool use) { - auto edit = begin_hover_edit(); - if (!edit) - return; - edit->use_hover_tooltip = use; - } - void set_hover_tooltip_font(const Font& font) { - auto edit = begin_hover_edit(); - if (!edit) - return; - edit->hover_info_font = font; - } - Font hover_tooltip_font() { - auto state = begin_hover_read(); - return state ? state->hover_info_font : Font{}; - } - Brush hover_info_background_brush() { - auto state = begin_hover_read(); - return state ? state->tooltip_background_brush : Brush{}; - } - void set_hover_tooltip_background_brush(const Brush& brush) { - auto edit = begin_hover_edit(); - if (!edit) - return; - edit->tooltip_background_brush = brush.color.a != 0 ? brush : Brush{}; - } - Pen tooltip_text_pen() { - auto state = begin_hover_read(); - return state ? state->tooltip_text_pen : Pen{.style = Line_Style::None}; - } - void set_tooltip_text_pen(const Pen& pen) { - auto edit = begin_hover_edit(); - if (!edit) - return; - edit->tooltip_text_pen = pen.color.a != 0 ? pen : Pen{.style = Line_Style::None}; - } - void get_hover_tooltip_contents_margins(int& left, int& top, int& right, int& bottom) { - auto s = begin_hover_read(); - if (!s) { - left = top = right = bottom = 0; - return; - } - left = s->hover_info_left; - top = s->hover_info_top; - right = s->hover_info_right; - bottom = s->hover_info_bottom; - } - void set_hover_tooltip_contents_margins(int left, int top, int right, int bottom) { - auto edit = begin_hover_edit(); - if (!edit) - return; - edit->hover_info_left = left; - edit->hover_info_top = top; - edit->hover_info_right = right; - edit->hover_info_bottom = bottom; - } - Hover_Edit_Guard begin_hover_edit() { - return begin_hover_edit_impl(); - } - Hover_Read_Guard begin_hover_read() { - return begin_hover_read_impl(); - } -private: - virtual Hover_Read_Guard begin_hover_read_impl() = 0; - virtual Hover_Edit_Guard begin_hover_edit_impl() = 0; -}; -template -struct LIB_DECL Hover_Tooltip_Mixin : Hover_Tooltip_Provider { -private: - That* that() { - return static_cast(this); - } - Hover_Read_Guard begin_hover_read_impl() override { - auto t = that(); - auto guard = t->d_ptr->acquire_state_read(Renderable_State_Role::Edit); - auto* state = dynamic_cast(guard.state); - if (!state) - return {}; - return Hover_Read_Guard(state, std::move(guard)); - } - Hover_Edit_Guard begin_hover_edit_impl() override { - auto t = that(); - auto guard = t->d_ptr->acquire_edit_state(); - auto* state = dynamic_cast(guard.state); - if (!state) - return {}; - return Hover_Edit_Guard(state, std::move(guard)); - } -}; -} // namespace renderive diff --git a/Core/plottable/Interpolation.h b/Core/plottable/Interpolation.h deleted file mode 100644 index e5cf682..0000000 --- a/Core/plottable/Interpolation.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../render/Image_Interpolation_Mode.h" -#include -namespace renderive { -enum class Line_Interpolation_Mode : std::uint8_t { - Nearest_Sample, - Linear_Value, - Linear_Power_Domain, - Step_Left, - Step_Right, - Cubic_Value -}; -} diff --git a/Core/plottable/Interpolation_p.h b/Core/plottable/Interpolation_p.h deleted file mode 100644 index 9a54e0d..0000000 --- a/Core/plottable/Interpolation_p.h +++ /dev/null @@ -1,218 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include "../Axis/Abs_Axis_p.h" -#include "../base/Frame_Memory.h" -#include "../base/Color.h" -#include "../base/Geometry.h" -#include "../render/Image.h" -#include "Interpolation.h" -namespace renderive { -struct Visible_Sample_Window { - double first_index{}; - double last_index{}; - int sample_count{}; - explicit operator bool() const { - return sample_count > 0; - } -}; -inline bool intersect_range(const Range& data_range, const Range& clip_range, Range& result) { - double data_min = std::min(data_range.origin, data_range.target); - double data_max = std::max(data_range.origin, data_range.target); - double clip_min = std::min(clip_range.origin, clip_range.target); - double clip_max = std::max(clip_range.origin, clip_range.target); - double lower = std::max(data_min, clip_min); - double upper = std::min(data_max, clip_max); - if (lower > upper) - return false; - result = data_range.origin <= data_range.target ? Range{lower, upper} : Range{upper, lower}; - return true; -} -inline double source_index_to_coordinate(const Range& data_range, int total_count, double index) { - return data_range.origin + data_range.length() * index / static_cast(total_count - 1); -} -inline double coordinate_to_source_index(const Range& data_range, int total_count, double coord) { - return static_cast(total_count - 1) * (coord - data_range.origin) / data_range.length(); -} -inline Visible_Sample_Window compute_visible_sample_window(const Axis_Frame_Snapshot& axis, const Range& data_range, int total_count, int available_count, bool visible_range_only) { - if (total_count < 2 || available_count < 1 || data_range.length() == 0.0) - return {}; - Range available_range{data_range.origin, source_index_to_coordinate(data_range, total_count, available_count - 1)}; - Range sample_range = available_range; - if (visible_range_only && !intersect_range(available_range, axis.coord_range, sample_range)) - return {}; - double first_index = coordinate_to_source_index(data_range, total_count, sample_range.origin); - double last_index = coordinate_to_source_index(data_range, total_count, sample_range.target); - double first_pixel = axis.coord_to_pixel(sample_range.origin); - double last_pixel = axis.coord_to_pixel(sample_range.target); - int source_samples = static_cast(std::ceil(std::abs(last_index - first_index))) + 1; - int pixel_samples = static_cast(std::ceil(std::abs(last_pixel - first_pixel))) + 1; - pixel_samples = std::min(pixel_samples, axis.pixel_size() + 1); - return {first_index, last_index, std::max(source_samples, pixel_samples)}; -} -inline Visible_Sample_Window compute_visible_sample_window(Abs_Axis* axis, const Range& data_range, int total_count, int available_count, bool visible_range_only) { - return compute_visible_sample_window(Axis_Render_Access::snapshot(axis), data_range, total_count, available_count, visible_range_only); -} -template -double interpolate_sample_value(double index, int count, Line_Interpolation_Mode mode, Value_At value_at) { - if (count <= 0) - return std::numeric_limits::quiet_NaN(); - index = std::clamp(index, 0.0, static_cast(count - 1)); - int left = static_cast(std::floor(index)); - int right = std::min(left + 1, count - 1); - double t = index - left; - double left_value = value_at(left); - double right_value = value_at(right); - switch (mode) { - case Line_Interpolation_Mode::Nearest_Sample: { - double value = value_at(static_cast(std::round(index))); - return std::isfinite(value) ? value : std::numeric_limits::quiet_NaN(); - } - case Line_Interpolation_Mode::Linear_Value: - if (!std::isfinite(left_value) || !std::isfinite(right_value)) - return std::numeric_limits::quiet_NaN(); - return left_value + (right_value - left_value) * t; - case Line_Interpolation_Mode::Linear_Power_Domain: { - if (!std::isfinite(left_value) || !std::isfinite(right_value)) - return std::numeric_limits::quiet_NaN(); - constexpr double min_db = -3000.0; - constexpr double max_db = 3000.0; - left_value = std::clamp(left_value, min_db, max_db); - right_value = std::clamp(right_value, min_db, max_db); - double left_power = std::pow(10.0, left_value / 10.0); - double right_power = std::pow(10.0, right_value / 10.0); - double mixed_power = left_power + (right_power - left_power) * t; - if (!std::isfinite(mixed_power) || mixed_power <= 0.0) - return min_db; - return std::clamp(10.0 * std::log10(mixed_power), min_db, max_db); - } - case Line_Interpolation_Mode::Step_Left: - return std::isfinite(left_value) ? left_value : std::numeric_limits::quiet_NaN(); - case Line_Interpolation_Mode::Step_Right: - if (t <= 1e-12) - return std::isfinite(left_value) ? left_value : std::numeric_limits::quiet_NaN(); - return std::isfinite(right_value) ? right_value : std::numeric_limits::quiet_NaN(); - case Line_Interpolation_Mode::Cubic_Value: { - double p0 = value_at(std::max(left - 1, 0)); - double p1 = left_value; - double p2 = right_value; - double p3 = value_at(std::min(right + 1, count - 1)); - if (!std::isfinite(p0) || !std::isfinite(p1) || !std::isfinite(p2) || !std::isfinite(p3)) - return std::numeric_limits::quiet_NaN(); - double a = -0.5 * p0 + 1.5 * p1 - 1.5 * p2 + 0.5 * p3; - double b = p0 - 2.5 * p1 + 2.0 * p2 - 0.5 * p3; - double c = -0.5 * p0 + 0.5 * p2; - return ((a * t + b) * t + c) * t + p1; - } - } - return left_value; -} -template -std::pmr::vector build_sample_line_points(const Axis_Mapping_2D& mapping, const Range& data_range, int total_count, int available_count, bool visible_range_only, Line_Interpolation_Mode mode, Value_At value_at, std::pmr::memory_resource* resource = frame_memory_resource()) { - Visible_Sample_Window window = compute_visible_sample_window(mapping.domain, data_range, total_count, available_count, visible_range_only); - if (!window) - return {}; - double first_index = std::clamp(std::min(window.first_index, window.last_index), 0.0, static_cast(available_count - 1)); - double last_index = std::clamp(std::max(window.first_index, window.last_index), first_index, static_cast(available_count - 1)); - int first_source = std::clamp(static_cast(std::ceil(first_index)), 0, available_count - 1); - int last_source = std::clamp(static_cast(std::floor(last_index)), first_source, available_count - 1); - std::pmr::vector points(resource); - points.reserve(static_cast(std::max(0, last_source - first_source + 3))); - double last_appended = -std::numeric_limits::infinity(); - double last_coord = 0.0; - double last_value = 0.0; - bool has_last_point = false; - auto append_index = [&](double index) { - if (std::abs(index - last_appended) <= 1e-9) - return; - double value = interpolate_sample_value(index, available_count, mode, value_at); - if (!std::isfinite(value)) { - has_last_point = false; - return; - } - double coord = source_index_to_coordinate(data_range, total_count, index); - PointF point = mapping.map(coord, value); - if (has_last_point && (mode == Line_Interpolation_Mode::Step_Left || mode == Line_Interpolation_Mode::Step_Right)) { - PointF elbow = mode == Line_Interpolation_Mode::Step_Left ? mapping.map(coord, last_value) : mapping.map(last_coord, value); - if (points.empty() || points.back().x != elbow.x || points.back().y != elbow.y) - points.push_back(elbow); - } - if (points.empty() || points.back().x != point.x || points.back().y != point.y) - points.push_back(point); - last_appended = index; - last_coord = coord; - last_value = value; - has_last_point = true; - }; - append_index(first_index); - for (int i = first_source; i <= last_source; ++i) - append_index(static_cast(i)); - append_index(last_index); - return points; -} -template -std::pmr::vector build_interpolated_line_points(const Axis_Mapping_2D& mapping, const Range& data_range, int total_count, int available_count, bool visible_range_only, Line_Interpolation_Mode mode, Value_At value_at, std::pmr::memory_resource* resource = frame_memory_resource()) { - Visible_Sample_Window window = compute_visible_sample_window(mapping.domain, data_range, total_count, available_count, visible_range_only); - if (!window) - return {}; - std::pmr::vector points(window.sample_count, resource); - double denominator = static_cast(std::max(window.sample_count - 1, 1)); - for (int i = 0; i < window.sample_count; ++i) { - double index = window.first_index + (window.last_index - window.first_index) * static_cast(i) / denominator; - double coord = source_index_to_coordinate(data_range, total_count, index); - double value = interpolate_sample_value(index, available_count, mode, value_at); - points[i] = mapping.map(coord, value); - } - return points; -} -template -std::pmr::vector build_interpolated_line_points(Abs_Axis* domain_axis, Abs_Axis* value_axis, const Range& data_range, int total_count, int available_count, bool visible_range_only, Line_Interpolation_Mode mode, Value_At value_at) { - return build_interpolated_line_points(Axis_Render_Access::mapping(domain_axis, value_axis), data_range, total_count, available_count, visible_range_only, mode, value_at, frame_memory_resource()); -} -inline double cubic_channel(double p0, double p1, double p2, double p3, double t) { - double a = -0.5 * p0 + 1.5 * p1 - 1.5 * p2 + 0.5 * p3; - double b = p0 - 2.5 * p1 + 2.0 * p2 - 0.5 * p3; - double c = -0.5 * p0 + 0.5 * p2; - return ((a * t + b) * t + c) * t + p1; -} -inline Pixel bicubic_pixel(const Image& source, double x, double y) { - int x1 = static_cast(std::floor(x)); - int y1 = static_cast(std::floor(y)); - double tx = x - x1; - double ty = y - y1; - double rows[4][4]{}; - for (int row = -1; row <= 2; ++row) { - int source_y = std::clamp(y1 + row, 0, source.height() - 1); - const Pixel* source_line = source.row(source_y); - Pixel pixels[4]{}; - for (int col = -1; col <= 2; ++col) { - int source_x = std::clamp(x1 + col, 0, source.width() - 1); - pixels[col + 1] = source_line[source_x]; - } - rows[0][row + 1] = cubic_channel((pixels[0] >> 24) & 0xFF, (pixels[1] >> 24) & 0xFF, (pixels[2] >> 24) & 0xFF, (pixels[3] >> 24) & 0xFF, tx); - rows[1][row + 1] = cubic_channel((pixels[0] >> 16) & 0xFF, (pixels[1] >> 16) & 0xFF, (pixels[2] >> 16) & 0xFF, (pixels[3] >> 16) & 0xFF, tx); - rows[2][row + 1] = cubic_channel((pixels[0] >> 8) & 0xFF, (pixels[1] >> 8) & 0xFF, (pixels[2] >> 8) & 0xFF, (pixels[3] >> 8) & 0xFF, tx); - rows[3][row + 1] = cubic_channel(pixels[0] & 0xFF, pixels[1] & 0xFF, pixels[2] & 0xFF, pixels[3] & 0xFF, tx); - } - double channels[4]{}; - for (int channel = 0; channel < 4; ++channel) { - channels[channel] = std::clamp(cubic_channel(rows[channel][0], rows[channel][1], rows[channel][2], rows[channel][3], ty), 0.0, 255.0); - } - return pack_rgba(static_cast(channels[1]), static_cast(channels[2]), static_cast(channels[3]), static_cast(channels[0])); -} -inline Image bicubic_scale(const Image& source, const RectF& source_rect, const Size& target_size) { - Image target(target_size.width, target_size.height); - for (int y = 0; y < target.height(); ++y) { - Pixel* line = target.row(y); - double source_y = source_rect.y + (static_cast(y) + 0.5) * source_rect.height / target.height() - 0.5; - for (int x = 0; x < target.width(); ++x) { - double source_x = source_rect.x + (static_cast(x) + 0.5) * source_rect.width / target.width() - 0.5; - line[x] = bicubic_pixel(source, source_x, source_y); - } - } - return target; -} -} // namespace renderive diff --git a/Core/plottable/Performance_Overlay.cpp b/Core/plottable/Performance_Overlay.cpp deleted file mode 100644 index f647c2d..0000000 --- a/Core/plottable/Performance_Overlay.cpp +++ /dev/null @@ -1,520 +0,0 @@ -#include "Performance_Overlay_p.h" -#include "../architecture/Plot_Render_Context.h" -#include "../performance/Performance_Log_Service.h" -#include "../plot/Plot_Core_Access.h" -#include -#include "../base/Task.h" -#include "../plot/Plot_Core.h" -namespace renderive { -namespace { -class Performance_Overlay_Service { -public: - ~Performance_Overlay_Service() { - shutdown(); - } - bool try_submit(Task task) { - if (!task || shutting_down.load(std::memory_order_acquire)) - return false; - auto topology = std::make_shared(); - auto task_ptr = std::make_shared(std::move(task)); - topology->emplace([task_ptr]() { - (*task_ptr)(); - }); - executor.run(*topology, [topology]() {}); - return true; - } - void shutdown() { - if (shutting_down.exchange(true, std::memory_order_acq_rel)) - return; - executor.wait_for_all(); - } -private: - tf::Executor executor{1}; - std::atomic_bool shutting_down{false}; -}; -Performance_Overlay_Service& performance_background_service() { - static Performance_Overlay_Service service; - return service; -} -double duration_ms(std::uint64_t begin_ns, std::uint64_t end_ns) { - return end_ns > begin_ns ? static_cast(end_ns - begin_ns) / 1000000.0 : 0.0; -} -double cache_ratio(std::uint64_t hit, std::uint64_t miss) { - std::uint64_t total = hit + miss; - return total ? static_cast(hit) / static_cast(total) : 0.0; -} -std::string source_text(Refresh_Limit_Source source) { - switch (source) { - case Refresh_Limit_Source::Unknown: - return "unknown"; - case Refresh_Limit_Source::Input: - return "input"; - case Refresh_Limit_Source::Render: - return "render"; - case Refresh_Limit_Source::Paint: - return "paint"; - case Refresh_Limit_Source::User: - return "user"; - } - return "unknown"; -} -std::string outcome_text(Frame_Outcome outcome) { - switch (outcome) { - case Frame_Outcome::Presented: - return "presented"; - case Frame_Outcome::Superseded: - return "superseded"; - case Frame_Outcome::Render_Acquire_Dropped: - return "render_acquire_dropped"; - case Frame_Outcome::Publish_Dropped: - return "publish_dropped"; - case Frame_Outcome::Paint_Acquire_Dropped: - return "paint_acquire_dropped"; - case Frame_Outcome::Worker_Budget_Dropped: - return "worker_budget_dropped"; - case Frame_Outcome::Cancelled: - return "cancelled"; - } - return "unknown"; -} -std::string bottleneck_text(Low_Latency_Bottleneck_State state) { - switch (state) { - case Low_Latency_Bottleneck_State::Unknown: - return "unknown"; - case Low_Latency_Bottleneck_State::Input_Limited: - return "input"; - case Low_Latency_Bottleneck_State::Producer_Limited: - return "producer"; - case Low_Latency_Bottleneck_State::Consumer_Limited: - return "consumer"; - case Low_Latency_Bottleneck_State::User_Limited: - return "user"; - } - return "unknown"; -} -std::string production_mode_text(Low_Latency_Production_Mode mode) { - switch (mode) { - case Low_Latency_Production_Mode::Latency_Eager: - return "latency_eager"; - case Low_Latency_Production_Mode::Consumer_Paced: - return "consumer_paced"; - } - return "latency_eager"; -} -std::string limit_reason_text(Low_Latency_Limit_Reason reason) { - switch (reason) { - case Low_Latency_Limit_Reason::User_Fps: - return "user_fps"; - case Low_Latency_Limit_Reason::Consumer_Capacity: - return "consumer_capacity"; - case Low_Latency_Limit_Reason::Render_Capacity: - return "render_capacity"; - case Low_Latency_Limit_Reason::No_Work: - return "no_work"; - case Low_Latency_Limit_Reason::Shutdown: - return "shutdown"; - } - return "user_fps"; -} -std::string consumer_confidence_text(Consumer_Confidence confidence) { - switch (confidence) { - case Consumer_Confidence::Low: - return "low"; - case Consumer_Confidence::Medium: - return "medium"; - case Consumer_Confidence::High: - return "high"; - } - return "low"; -} -static std::shared_ptr performance_overlay_owner(const std::shared_ptr& ctx) { - if (!ctx) - return {}; - auto observer = std::atomic_load_explicit(&ctx->frame_lifecycle_observer, std::memory_order_acquire); - return std::dynamic_pointer_cast(observer); -} -static Performance_Log_Plot_Identity performance_plot_identity(const std::shared_ptr& ctx) { - Performance_Log_Plot_Identity identity; - if (!ctx) - return identity; - identity.plot_id = ctx->performance_plot_id.load(std::memory_order_acquire); - { - std::lock_guard lock(ctx->performance_plot_name_mutex); - identity.plot_name = ctx->performance_plot_name; - } - if (identity.plot_name.empty()) - identity.plot_name = "plot_" + std::to_string(identity.plot_id); - return identity; -} -static std::function performance_update_callback(const std::shared_ptr& ctx) { - if (!ctx) - return {}; - std::lock_guard lock(ctx->performance_update_callback_mutex); - return ctx->performance_update_callback; -} -static std::function performance_clipboard_callback(const std::shared_ptr& ctx) { - if (!ctx) - return {}; - std::lock_guard lock(ctx->performance_clipboard_callback_mutex); - return ctx->performance_clipboard_callback; -} -static std::string performance_snapshot_text(const Performance_Display_Snapshot& snapshot) { - std::size_t size = 0; - for (const auto& line : snapshot.lines) - size += line.size() + 1; - std::string text; - text.reserve(size); - for (std::size_t i = 0; i < snapshot.lines.size(); ++i) { - if (i) - text.push_back('\n'); - text += snapshot.lines[i]; - } - return text; -} -static void request_performance_update(const std::shared_ptr& worker) { - if (!worker) - return; - std::function callback; - { - std::lock_guard lock(worker->update_callback_mutex); - callback = worker->update_callback; - } - if (callback) - callback(); -} -} -void schedule_performance_metrics_worker(const std::shared_ptr& state, bool request_repaint) { - if (!state || state->cancelled.load(std::memory_order_acquire)) - return; - if (request_repaint) - state->repaint_requested.store(true, std::memory_order_release); - state->work_requested.store(true, std::memory_order_release); - if (state->worker_active.exchange(true, std::memory_order_acq_rel)) - return; - if (!performance_background_service().try_submit(Task([state]() { - Performance_Overlay_Private::run_metrics_worker(state); - }))) { - state->worker_active.store(false, std::memory_order_release); - } -} -Performance_Overlay::Performance_Overlay() - : private_data(std::make_unique()) {} -Performance_Overlay::~Performance_Overlay() = default; -Performance_Overlay_Private* Performance_Overlay::d() { - return private_data.get(); -} -static std::shared_ptr attach_performance_overlay_impl(Plot_Core& plot, const Performance_Overlay_Options* options) { - auto ctx = Plot_Core_Context_Access::render_context(plot); - if (!ctx) - return {}; - auto shower = performance_overlay_owner(ctx); - if (shower) { - shower->set_log_identity(performance_plot_identity(ctx)); - if (options) - shower->set_options(*options); - return shower; - } - auto created_shower = renderive::make_shared(); - created_shower->set_log_identity(performance_plot_identity(ctx)); - if (options) - created_shower->set_options(*options); - created_shower->set_update_callback(performance_update_callback(ctx)); - created_shower->set_clipboard_callback(performance_clipboard_callback(ctx)); - std::atomic_store_explicit(&ctx->frame_lifecycle_observer, std::static_pointer_cast(created_shower), std::memory_order_release); - return created_shower; -} -std::shared_ptr attach_performance_overlay(Plot_Core& plot) { - return attach_performance_overlay_impl(plot, nullptr); -} -std::shared_ptr attach_performance_overlay(Plot_Core& plot, const Performance_Overlay_Options& options) { - return attach_performance_overlay_impl(plot, &options); -} -std::shared_ptr performance_overlay(Plot_Core& plot) { - return performance_overlay_owner(Plot_Core_Context_Access::render_context(plot)); -} -void set_performance_plot_name(Plot_Core& plot, std::string name) { - auto ctx = Plot_Core_Context_Access::render_context(plot); - if (!ctx) - return; - { - std::lock_guard lock(ctx->performance_plot_name_mutex); - ctx->performance_plot_name = std::move(name); - } - auto shower = performance_overlay_owner(ctx); - if (shower) - shower->set_log_identity(performance_plot_identity(ctx)); -} -void detach_performance_overlay(Plot_Core& plot) { - auto ctx = Plot_Core_Context_Access::render_context(plot); - if (!ctx) - return; - auto shower = performance_overlay_owner(ctx); - std::atomic_store_explicit(&ctx->frame_lifecycle_observer, std::shared_ptr{}, std::memory_order_release); - if (shower) - shower->set_enabled(false); -} -bool performance_overlay_enabled(const Plot_Core& plot) { - auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot)); - return shower && shower->enabled(); -} -void set_performance_overlay_enabled(Plot_Core& plot, bool enabled) { - if (!enabled) { - auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot)); - if (shower) - shower->set_enabled(false); - return; - } - auto shower = attach_performance_overlay(plot); - if (shower) { - shower->set_enabled(true); - auto ctx = Plot_Core_Context_Access::render_context(plot); - shower->set_viewport(plot.viewport_size(), ctx ? ctx->viewport_version.load(std::memory_order_acquire) : 0); - } -} -std::shared_ptr performance_overlay_snapshot(const Plot_Core& plot) { - auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot)); - if (!shower || !shower->enabled()) - return {}; - return shower->display_snapshot(); -} -void set_performance_overlay_update_callback(Plot_Core& plot, std::function callback) { - auto ctx = Plot_Core_Context_Access::render_context(plot); - if (!ctx) - return; - { - std::lock_guard lock(ctx->performance_update_callback_mutex); - ctx->performance_update_callback = std::move(callback); - } - auto shower = performance_overlay_owner(ctx); - if (shower) - shower->set_update_callback(performance_update_callback(ctx)); -} -void set_performance_overlay_clipboard_callback(Plot_Core& plot, std::function callback) { - auto ctx = Plot_Core_Context_Access::render_context(plot); - if (!ctx) - return; - { - std::lock_guard lock(ctx->performance_clipboard_callback_mutex); - ctx->performance_clipboard_callback = std::move(callback); - } - auto shower = performance_overlay_owner(ctx); - if (shower) - shower->set_clipboard_callback(performance_clipboard_callback(ctx)); -} -void notify_performance_overlay_viewport_changed(Plot_Core& plot) { - auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot)); - if (!shower || !shower->enabled()) - return; - auto ctx = Plot_Core_Context_Access::render_context(plot); - shower->set_viewport(plot.viewport_size(), ctx ? ctx->viewport_version.load(std::memory_order_acquire) : 0); -} -bool performance_overlay_handle_wheel(Plot_Core& plot, PointF position, double angle_delta_y, double pixel_delta_y) { - auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot)); - return shower && shower->enabled() && shower->handle_wheel(position, angle_delta_y, pixel_delta_y); -} -bool performance_overlay_handle_pointer_press(Plot_Core& plot, PointF position) { - auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot)); - return shower && shower->enabled() && shower->handle_pointer_press(position); -} -bool performance_overlay_handle_pointer_move(Plot_Core& plot, PointF position) { - auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot)); - return shower && shower->enabled() && shower->handle_pointer_move(position); -} -bool performance_overlay_handle_pointer_release(Plot_Core& plot, PointF position) { - auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot)); - return shower && shower->enabled() && shower->handle_pointer_release(position); -} -bool performance_overlay_handle_pointer_leave(Plot_Core& plot) { - auto shower = performance_overlay_owner(Plot_Core_Context_Access::render_context(plot)); - return shower && shower->enabled() && shower->handle_pointer_leave(); -} -void Performance_Overlay::set_enabled(bool enabled) { - auto worker = d()->metrics_worker; - d()->reset_button_states(); - d()->enabled_value.store(enabled, std::memory_order_release); - worker->enabled.store(enabled, std::memory_order_release); - if (!enabled) { - worker->repaint_requested.store(false, std::memory_order_release); - worker->published_snapshot.store({}, std::memory_order_release); - request_performance_update(worker); - return; - } - worker->reset_requested.store(true, std::memory_order_release); - worker->snapshot_rebuild_requested.store(true, std::memory_order_release); - schedule_performance_metrics_worker(worker, true); -} -bool Performance_Overlay::enabled() const { - return const_cast(this)->d()->enabled_value.load(std::memory_order_acquire); -} -std::shared_ptr Performance_Overlay::display_snapshot() const { - return const_cast(this)->d()->metrics_worker->published_snapshot.load(std::memory_order_acquire); -} -void Performance_Overlay::set_options(Performance_Overlay_Options options) { - auto worker = d()->metrics_worker; - if (options.log.enabled) - performance_log_service().configure(options.log); - { - std::lock_guard lock(worker->options_mutex); - worker->options = std::move(options); - } - worker->snapshot_rebuild_requested.store(true, std::memory_order_release); - if (enabled()) - schedule_performance_metrics_worker(worker, true); -} -Performance_Overlay_Options Performance_Overlay::options() const { - auto worker = const_cast(this)->d()->metrics_worker; - std::lock_guard lock(worker->options_mutex); - return worker->options; -} -void Performance_Overlay::set_log_options(Performance_Log_Options options) { - Performance_Overlay_Options value = this->options(); - value.log = std::move(options); - set_options(std::move(value)); -} -Performance_Log_Options Performance_Overlay::log_options() const { - return options().log; -} -bool Performance_Overlay::logging_enabled() const { - return log_options().enabled; -} -bool Performance_Overlay::capture_enabled() const { - return enabled() || logging_enabled(); -} -void Performance_Overlay::set_log_identity(Performance_Log_Plot_Identity identity) { - d()->set_identity(std::move(identity)); -} -void Performance_Overlay::set_font(Font font) { - Performance_Overlay_Options value = options(); - value.font = std::move(font); - set_options(std::move(value)); -} -void Performance_Overlay::set_foreground(Color color) { - Performance_Overlay_Options value = options(); - value.foreground = color; - value.section_color = color; - value.label_color = color; - value.value_color = color; - value.unit_color = color; - set_options(std::move(value)); -} -void Performance_Overlay::set_background(Color color) { - Performance_Overlay_Options value = options(); - value.background = color; - set_options(std::move(value)); -} -void Performance_Overlay::collect_frame(std::shared_ptr frame) { - if (!frame) - return; - if (enabled()) - d()->enqueue_frame_metric(frame); - if (logging_enabled()) - d()->enqueue_frame_log(frame); -} -void Performance_Overlay::collect_feedback_event(std::shared_ptr event) { - if (!event || !logging_enabled()) - return; - d()->enqueue_feedback_log(event); -} -void Performance_Overlay::set_viewport(Size viewport, std::uint64_t version) { - d()->metrics_worker->viewport_width.store(viewport.width, std::memory_order_release); - d()->metrics_worker->viewport_height.store(viewport.height, std::memory_order_release); - d()->metrics_worker->viewport_version.store(version, std::memory_order_release); - d()->metrics_worker->snapshot_rebuild_requested.store(true, std::memory_order_release); - schedule_performance_metrics_worker(d()->metrics_worker, true); -} -bool Performance_Overlay::handle_wheel(PointF position, double angle_delta_y, double pixel_delta_y) { - auto snapshot = display_snapshot(); - if (!snapshot || !snapshot->destination_rect.contains(position)) - return false; - double line_height = snapshot->line_height > 0.0 ? snapshot->line_height : d()->metrics_worker->last_line_height; - double delta = 0.0; - if (pixel_delta_y != 0.0) - delta = -pixel_delta_y; - else if (angle_delta_y != 0.0) - delta = -(angle_delta_y / 120.0) * 3.0 * std::max(1.0, line_height); - if (delta != 0.0) { - performance_atomic_add(d()->metrics_worker->pending_wheel_delta_px, delta); - schedule_performance_metrics_worker(d()->metrics_worker, true); - } - return true; -} -bool Performance_Overlay::handle_pointer_press(PointF position) { - auto snapshot = display_snapshot(); - if (!snapshot) - return false; - Button_Event_Result copy_result = d()->copy_button_interaction.pointer_press(position, snapshot->copy_button_rect); - Button_Event_Result close_result = d()->close_button_interaction.pointer_press(position, snapshot->close_button_rect); - d()->publish_button_states(copy_result.changed || close_result.changed); - if (copy_result.consumed || close_result.consumed) - return true; - if (snapshot->max_scroll_offset <= 0.0) - return false; - if (snapshot->scroll_thumb_rect.contains(position)) { - d()->metrics_worker->drag_anchor_y.store(position.y - snapshot->scroll_thumb_rect.y, std::memory_order_release); - d()->metrics_worker->drag_position_y.store(position.y, std::memory_order_release); - d()->metrics_worker->drag_end_requested.store(false, std::memory_order_release); - d()->metrics_worker->drag_active.store(true, std::memory_order_release); - schedule_performance_metrics_worker(d()->metrics_worker, true); - return true; - } - if (snapshot->scroll_track_rect.contains(position)) { - int page = position.y < snapshot->scroll_thumb_rect.y ? -1 : 1; - d()->metrics_worker->pending_page_delta.fetch_add(page, std::memory_order_acq_rel); - schedule_performance_metrics_worker(d()->metrics_worker, true); - return true; - } - return false; -} -bool Performance_Overlay::handle_pointer_move(PointF position) { - if (d()->metrics_worker->drag_active.load(std::memory_order_acquire)) { - d()->metrics_worker->drag_position_y.store(position.y, std::memory_order_release); - schedule_performance_metrics_worker(d()->metrics_worker, true); - return true; - } - auto snapshot = display_snapshot(); - if (!snapshot) - return false; - Button_Event_Result copy_result = d()->copy_button_interaction.pointer_move(position, snapshot->copy_button_rect); - Button_Event_Result close_result = d()->close_button_interaction.pointer_move(position, snapshot->close_button_rect); - d()->publish_button_states(copy_result.changed || close_result.changed); - return copy_result.consumed || close_result.consumed; -} -bool Performance_Overlay::handle_pointer_release(PointF position) { - auto snapshot = display_snapshot(); - if (snapshot) { - Button_Event_Result copy_result = d()->copy_button_interaction.pointer_release(position, snapshot->copy_button_rect); - Button_Event_Result close_result = d()->close_button_interaction.pointer_release(position, snapshot->close_button_rect); - d()->publish_button_states(copy_result.changed || close_result.changed); - if (copy_result.clicked) - d()->copy_to_clipboard(performance_snapshot_text(*snapshot)); - if (close_result.clicked) - set_enabled(false); - if (copy_result.consumed || close_result.consumed) - return true; - } - if (!d()->metrics_worker->drag_active.load(std::memory_order_acquire)) - return false; - d()->metrics_worker->drag_position_y.store(position.y, std::memory_order_release); - d()->metrics_worker->drag_end_requested.store(true, std::memory_order_release); - schedule_performance_metrics_worker(d()->metrics_worker, true); - return true; -} -bool Performance_Overlay::handle_pointer_leave() { - Button_Event_Result copy_result = d()->copy_button_interaction.pointer_leave(); - Button_Event_Result close_result = d()->close_button_interaction.pointer_leave(); - d()->publish_button_states(copy_result.changed || close_result.changed); - return copy_result.consumed || close_result.consumed; -} -void Performance_Overlay::set_update_callback(std::function callback) { - std::lock_guard lock(d()->metrics_worker->update_callback_mutex); - d()->metrics_worker->update_callback = std::move(callback); -} -void Performance_Overlay::set_clipboard_callback(std::function callback) { - d()->set_clipboard_callback(std::move(callback)); -} -void write_frame_performance_log(std::shared_ptr frame) { - if (!frame) - return; - performance_log_service().enqueue_frame({}, std::move(frame)); -} -} // namespace renderive diff --git a/Core/plottable/Performance_Overlay.h b/Core/plottable/Performance_Overlay.h deleted file mode 100644 index af35c04..0000000 --- a/Core/plottable/Performance_Overlay.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include "../architecture/Frame_Scheduler.h" -#include "../base/Color.h" -#include "../base/Geometry.h" -#include "../base/Text.h" -#include "../performance/Performance_Log_Record.h" -#include "global.h" -#include "Performance_Snapshot.h" - -namespace renderive { - -struct Performance_Overlay_Private; - -struct Performance_Overlay_Options { - Font font; - int top_margin = 4; - int left_margin = 6; - int right_margin = 12; - int bottom_margin = 6; - int scroll_bar_width = 8; - int scroll_bar_margin = 2; - Color background = Color::white(); - Color foreground = Color::black(); - Color section_color = Color{64, 142, 255}; - Color label_color = Color{190, 190, 190}; - Color value_color = Color{235, 218, 130}; - Color unit_color = Color{150, 150, 150}; - Color warning_color = Color{255, 96, 96}; - Color muted_color = Color{95, 95, 95}; - Color scroll_track = Color{220, 220, 220}; - Color scroll_thumb = Color{80, 80, 80}; - Performance_Log_Options log; -}; - -class LIB_DECL Performance_Overlay : public Frame_Lifecycle_Observer { -public: - Performance_Overlay(); - ~Performance_Overlay(); - - void set_enabled(bool enabled); - bool enabled() const override; - void collect_frame(std::shared_ptr frame) override; - void collect_feedback_event(std::shared_ptr event) override; - [[nodiscard]] std::shared_ptr display_snapshot() const; - - void set_options(Performance_Overlay_Options options); - [[nodiscard]] Performance_Overlay_Options options() const; - void set_log_options(Performance_Log_Options options); - [[nodiscard]] Performance_Log_Options log_options() const; - [[nodiscard]] bool logging_enabled() const; - [[nodiscard]] bool capture_enabled() const override; - void set_log_identity(Performance_Log_Plot_Identity identity); - void set_font(Font font); - void set_foreground(Color color); - void set_background(Color color); - - void set_viewport(Size viewport, std::uint64_t version); - bool handle_wheel(PointF position, double angle_delta_y, double pixel_delta_y); - bool handle_pointer_press(PointF position); - bool handle_pointer_move(PointF position); - bool handle_pointer_release(PointF position); - bool handle_pointer_leave(); - void set_update_callback(std::function callback); - void set_clipboard_callback(std::function callback); - -private: - Performance_Overlay_Private* d(); - std::unique_ptr private_data; -}; - -} // namespace renderive diff --git a/Core/plottable/Performance_Overlay_p.h b/Core/plottable/Performance_Overlay_p.h deleted file mode 100644 index 50e97ec..0000000 --- a/Core/plottable/Performance_Overlay_p.h +++ /dev/null @@ -1,1692 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../architecture/Frame_Scheduler.h" -#include "../architecture/Render_Data.h" -#include "../performance/Performance_Log_Service.h" -#include "../base/Color.h" -#include "../base/Geometry.h" -#include "../base/Memory.h" -#include "../base/Style.h" -#include "../base/String_Format.h" -#include "../base/Text.h" -#include "../render/Button.h" -#include "../render/Canvas.h" -#include "Performance_Overlay.h" -#include "Psc_Cpp_Core/Statistics/Statistics.h" -namespace renderive { -void write_frame_performance_log(std::shared_ptr frame); -struct Performance_Overlay_Render_State : Render_State, Performance_Overlay_Options {}; -struct Performance_Overlay_Input_Data : Input_Data {}; -struct Renderable_Cache_Aggregate_Stat { - std::uint64_t node_id{}; - std::uint64_t parent_node_id{}; - std::string object_name; - std::string parent_object_name; - int tree_depth{}; - std::uint64_t hit_count{}; - std::uint64_t miss_count{}; - std::uint64_t rebuild_count{}; - std::uint64_t rebuild_time_ns{}; - std::uint64_t compose_time_ns{}; - std::uint64_t subtree_compose_time_ns{}; - std::uint64_t last_rebuild_time_ns{}; - std::uint64_t last_compose_time_ns{}; - std::uint64_t last_image_bytes{}; - std::uint64_t last_image_area{}; - std::uint64_t last_edit_version{}; - std::uint64_t last_ready_version{}; - Psc::Value_Statistics rebuild_ms; - Psc::Value_Statistics compose_ms; - Psc::Value_Statistics subtree_compose_ms; -}; -struct Renderable_Aggregate_Stat { - std::uint64_t node_id{}; - std::uint64_t parent_node_id{}; - std::string object_name; - std::string parent_object_name; - int tree_depth{}; - bool cache_enabled{}; - std::uint64_t render_count{}; - std::uint64_t self_draw_count{}; - std::uint64_t total_render_time_ns{}; - std::uint64_t self_draw_time_ns{}; - std::uint64_t last_total_render_time_ns{}; - std::uint64_t last_self_draw_time_ns{}; - Psc::Value_Statistics total_render_ms; - Psc::Value_Statistics self_draw_ms; - Psc::Value_Statistics total_render_ratio; - Psc::Value_Statistics self_draw_ratio; -}; -struct Performance_Aggregate { - Psc::Value_Statistics request_wait_ms; - Psc::Value_Statistics producer_interval_ms; - Psc::Value_Statistics admission_wait_ms; - Psc::Value_Statistics render_slot_idle_wait_ms; - Psc::Value_Statistics executor_queue_wait_ms; - Psc::Value_Statistics prepare_ms; - Psc::Value_Statistics draw_ms; - Psc::Value_Statistics render_ms; - Psc::Value_Statistics ready_wait_ms; - Psc::Value_Statistics update_wait_ms; - Psc::Value_Statistics paint_ms; - Psc::Value_Statistics display_interval_ms; - Psc::Value_Statistics effective_interval_ms; - Psc::Value_Statistics frame_age_ms; - Psc::Value_Statistics frame_total_ms; - Psc::Value_Statistics cache_rebuild_ms; - Psc::Value_Statistics cache_compose_ms; - Psc::Value_Statistics cache_rebuild_ratio; - Psc::Value_Statistics cache_compose_ratio; - Frame_Lifecycle_Record latest; - std::uint64_t observed_frame_count{}; - std::uint64_t render_time_ns{}; - std::uint64_t consumed_outcome_count{}; - std::uint64_t superseded_outcome_count{}; - std::uint64_t cancelled_outcome_count{}; - std::uint64_t cache_hit_count{}; - std::uint64_t cache_miss_count{}; - std::uint64_t cache_rebuild_count{}; - std::uint64_t cache_rebuild_time_ns{}; - std::uint64_t cache_compose_time_ns{}; - std::uint64_t input_capacity_dropped{}; - bool has_frame{}; - std::vector renderable_stats; - std::vector renderable_cache_stats; - void clear() { - *this = {}; - } - static Frame_Lifecycle_Record summarize_frame(const Frame_Lifecycle_Record& frame) { - Frame_Lifecycle_Record summary; - summary.frame_id = frame.frame_id; - summary.input_version = frame.input_version; - summary.core_data_time_ns = frame.core_data_time_ns; - summary.render_request_ns = frame.render_request_ns; - summary.render_role_enter_ns = frame.render_role_enter_ns; - summary.render_begin_ns = frame.render_begin_ns; - summary.prepare_begin_ns = frame.prepare_begin_ns; - summary.prepare_end_ns = frame.prepare_end_ns; - summary.draw_begin_ns = frame.draw_begin_ns; - summary.draw_end_ns = frame.draw_end_ns; - summary.render_end_ns = frame.render_end_ns; - summary.transition_12_ns = frame.transition_12_ns; - summary.update_request_id = frame.update_request_id; - summary.update_request_time_ns = frame.update_request_time_ns; - summary.update_posted_ns = frame.update_posted_ns; - summary.transition_23_ns = frame.transition_23_ns; - summary.paint_begin_ns = frame.paint_begin_ns; - summary.paint_end_ns = frame.paint_end_ns; - summary.presented_frame_id = frame.presented_frame_id; - summary.transition_32_ns = frame.transition_32_ns; - summary.render_slot_idle_wait_ns = frame.render_slot_idle_wait_ns; - summary.executor_queue_wait_ns = frame.executor_queue_wait_ns; - summary.ready_queue_wait_ns = frame.ready_queue_wait_ns; - summary.present_queue_wait_ns = frame.present_queue_wait_ns; - summary.paint_return_wait_ns = frame.paint_return_wait_ns; - summary.superseded_time_ns = frame.superseded_time_ns; - summary.outcome = frame.outcome; - summary.refresh = frame.refresh; - summary.worker = frame.worker; - summary.raw_input_count = frame.raw_input_count; - summary.prepared_output_count = frame.prepared_output_count; - summary.renderable_cache_hit_count = frame.renderable_cache_hit_count; - summary.renderable_cache_miss_count = frame.renderable_cache_miss_count; - summary.renderable_cache_rebuild_count = frame.renderable_cache_rebuild_count; - summary.renderable_cache_rebuild_time_ns = frame.renderable_cache_rebuild_time_ns; - summary.renderable_cache_compose_time_ns = frame.renderable_cache_compose_time_ns; - summary.renderable_cache_image_bytes = frame.renderable_cache_image_bytes; - summary.renderable_cache_image_area = frame.renderable_cache_image_area; - summary.pixel_width = frame.pixel_width; - summary.pixel_height = frame.pixel_height; - summary.viewport_version = frame.viewport_version; - return summary; - } - void update(const Frame_Lifecycle_Record& frame) { - latest = summarize_frame(frame); - has_frame = true; - ++observed_frame_count; - std::uint64_t frame_render_time_ns = duration_ns(frame.render_begin_ns, frame.render_end_ns); - render_time_ns += frame_render_time_ns; - update_duration(request_wait_ms, frame.render_request_ns, frame.render_begin_ns); - if (frame.refresh.producer_interval_ns) - producer_interval_ms.update(ns_to_ms(frame.refresh.producer_interval_ns)); - admission_wait_ms.update(ns_to_ms(frame.refresh.admission_wait_elapsed_ns)); - render_slot_idle_wait_ms.update(ns_to_ms(frame.render_slot_idle_wait_ns)); - executor_queue_wait_ms.update(ns_to_ms(frame.executor_queue_wait_ns)); - update_duration(prepare_ms, frame.prepare_begin_ns, frame.prepare_end_ns); - update_duration(draw_ms, frame.draw_begin_ns, frame.draw_end_ns); - update_duration(render_ms, frame.render_begin_ns, frame.render_end_ns); - update_duration(ready_wait_ms, frame.transition_12_ns, frame.paint_begin_ns ? frame.paint_begin_ns : frame.superseded_time_ns); - update_duration(update_wait_ms, frame.update_request_time_ns, frame.paint_begin_ns); - update_duration(paint_ms, frame.paint_begin_ns, frame.paint_end_ns); - if (frame.refresh.display_frame_interval_ns) - display_interval_ms.update(ns_to_ms(frame.refresh.display_frame_interval_ns)); - if (frame.refresh.effective_min_render_interval_ns) - effective_interval_ms.update(ns_to_ms(frame.refresh.effective_min_render_interval_ns)); - update_duration(frame_age_ms, frame.core_data_time_ns, frame.paint_end_ns); - update_duration(frame_total_ms, frame.render_request_ns, frame.transition_32_ns ? frame.transition_32_ns : frame.superseded_time_ns); - cache_hit_count += frame.renderable_cache_hit_count; - cache_miss_count += frame.renderable_cache_miss_count; - cache_rebuild_count += frame.renderable_cache_rebuild_count; - cache_rebuild_time_ns += frame.renderable_cache_rebuild_time_ns; - cache_compose_time_ns += frame.renderable_cache_compose_time_ns; - if (frame.renderable_cache_rebuild_time_ns) - cache_rebuild_ms.update(ns_to_ms(frame.renderable_cache_rebuild_time_ns)); - if (frame.renderable_cache_compose_time_ns) - cache_compose_ms.update(ns_to_ms(frame.renderable_cache_compose_time_ns)); - if (frame_render_time_ns) { - cache_rebuild_ratio.update(static_cast(frame.renderable_cache_rebuild_time_ns) * 100.0 / static_cast(frame_render_time_ns)); - cache_compose_ratio.update(static_cast(frame.renderable_cache_compose_time_ns) * 100.0 / static_cast(frame_render_time_ns)); - } - for (const auto& stat : frame.renderable_stats) { - Renderable_Aggregate_Stat& aggregate_stat = renderable_stat(stat.node_id, stat.parent_node_id, stat.object_name, stat.parent_object_name, stat.tree_depth); - aggregate_stat.cache_enabled = stat.cache_enabled; - aggregate_stat.render_count += stat.render_count; - aggregate_stat.self_draw_count += stat.self_draw_count; - aggregate_stat.total_render_time_ns += stat.total_render_time_ns; - aggregate_stat.self_draw_time_ns += stat.self_draw_time_ns; - aggregate_stat.last_total_render_time_ns = stat.last_total_render_time_ns ? stat.last_total_render_time_ns : aggregate_stat.last_total_render_time_ns; - aggregate_stat.last_self_draw_time_ns = stat.last_self_draw_time_ns ? stat.last_self_draw_time_ns : aggregate_stat.last_self_draw_time_ns; - if (stat.total_render_time_ns) - aggregate_stat.total_render_ms.update(ns_to_ms(stat.total_render_time_ns)); - if (stat.self_draw_time_ns) - aggregate_stat.self_draw_ms.update(ns_to_ms(stat.self_draw_time_ns)); - if (frame_render_time_ns) { - aggregate_stat.total_render_ratio.update(static_cast(stat.total_render_time_ns) * 100.0 / static_cast(frame_render_time_ns)); - aggregate_stat.self_draw_ratio.update(static_cast(stat.self_draw_time_ns) * 100.0 / static_cast(frame_render_time_ns)); - } - } - for (const auto& stat : frame.renderable_cache_stats) { - Renderable_Cache_Aggregate_Stat& aggregate_stat = cache_stat(stat.node_id, stat.parent_node_id, stat.object_name, stat.parent_object_name, stat.tree_depth); - aggregate_stat.hit_count += stat.hit_count; - aggregate_stat.miss_count += stat.miss_count; - aggregate_stat.rebuild_count += stat.rebuild_count; - aggregate_stat.rebuild_time_ns += stat.rebuild_time_ns; - aggregate_stat.compose_time_ns += stat.compose_time_ns; - aggregate_stat.last_rebuild_time_ns = stat.last_rebuild_time_ns ? stat.last_rebuild_time_ns : aggregate_stat.last_rebuild_time_ns; - aggregate_stat.last_compose_time_ns = stat.last_compose_time_ns ? stat.last_compose_time_ns : aggregate_stat.last_compose_time_ns; - aggregate_stat.last_image_bytes = stat.last_image_bytes ? stat.last_image_bytes : aggregate_stat.last_image_bytes; - aggregate_stat.last_image_area = stat.last_image_area ? stat.last_image_area : aggregate_stat.last_image_area; - aggregate_stat.last_edit_version = stat.last_edit_version ? stat.last_edit_version : aggregate_stat.last_edit_version; - aggregate_stat.last_ready_version = stat.last_ready_version ? stat.last_ready_version : aggregate_stat.last_ready_version; - if (stat.rebuild_time_ns) - aggregate_stat.rebuild_ms.update(ns_to_ms(stat.rebuild_time_ns)); - if (stat.compose_time_ns) - aggregate_stat.compose_ms.update(ns_to_ms(stat.compose_time_ns)); - } - update_cache_subtree_compose(frame.renderable_cache_stats); - switch (frame.outcome) { - case Frame_Outcome::Presented: - ++consumed_outcome_count; - break; - case Frame_Outcome::Superseded: - ++superseded_outcome_count; - break; - case Frame_Outcome::Render_Acquire_Dropped: - case Frame_Outcome::Publish_Dropped: - case Frame_Outcome::Paint_Acquire_Dropped: - case Frame_Outcome::Worker_Budget_Dropped: - case Frame_Outcome::Cancelled: - ++cancelled_outcome_count; - break; - } - } -private: - Renderable_Aggregate_Stat& renderable_stat(std::uint64_t node_id, std::uint64_t parent_node_id, const std::string& object_name, const std::string& parent_object_name, int tree_depth) { - for (auto& stat : renderable_stats) { - if (stat.node_id == node_id && stat.parent_node_id == parent_node_id && stat.tree_depth == tree_depth) - return stat; - } - Renderable_Aggregate_Stat stat; - stat.node_id = node_id; - stat.parent_node_id = parent_node_id; - stat.object_name = object_name; - stat.parent_object_name = parent_object_name; - stat.tree_depth = tree_depth; - renderable_stats.push_back(std::move(stat)); - return renderable_stats.back(); - } - void update_cache_subtree_compose(const std::vector& frame_stats) { - for (const auto& stat : frame_stats) { - std::uint64_t subtree_compose_time_ns = renderable_cache_subtree_compose_time(frame_stats, stat); - if (!subtree_compose_time_ns) - continue; - Renderable_Cache_Aggregate_Stat& aggregate_stat = cache_stat(stat.node_id, stat.parent_node_id, stat.object_name, stat.parent_object_name, stat.tree_depth); - aggregate_stat.subtree_compose_time_ns += subtree_compose_time_ns; - aggregate_stat.subtree_compose_ms.update(ns_to_ms(subtree_compose_time_ns)); - } - } - Renderable_Cache_Aggregate_Stat& cache_stat(std::uint64_t node_id, std::uint64_t parent_node_id, const std::string& object_name, const std::string& parent_object_name, int tree_depth) { - for (auto& stat : renderable_cache_stats) { - if (stat.node_id == node_id && stat.parent_node_id == parent_node_id) - return stat; - } - Renderable_Cache_Aggregate_Stat stat; - stat.node_id = node_id; - stat.parent_node_id = parent_node_id; - stat.object_name = object_name; - stat.parent_object_name = parent_object_name; - stat.tree_depth = tree_depth; - renderable_cache_stats.push_back(std::move(stat)); - return renderable_cache_stats.back(); - } - static double ns_to_ms(std::uint64_t ns) { - return static_cast(ns) / 1000000.0; - } - static std::uint64_t duration_ns(std::uint64_t begin_ns, std::uint64_t end_ns) { - return end_ns > begin_ns ? end_ns - begin_ns : 0; - } - static void update_duration(Psc::Value_Statistics& statistics, std::uint64_t begin_ns, std::uint64_t end_ns) { - if (end_ns > begin_ns) - statistics.update(static_cast(end_ns - begin_ns) / 1000000.0); - } -}; -enum class Performance_Metric_Id : std::uint16_t { - Frame_Id, - Input_Version, - Render_Period, - Paint_Period, - User_Period, - Target_Period, - Render_Slot_Idle_Wait, - Executor_Queue_Wait, - Ready_Queue_Wait, - Present_Queue_Wait, - Paint_Return_Wait, - Worker_Count, - Active_Workers, - Queued_Frame_Jobs, - Active_Frame_Jobs, - Submitted_Topologies, - Started_Topologies, - Completed_Topologies, - Started_Task_Nodes, - Completed_Task_Nodes, - Rejected_Frame_Jobs, - Topology_Queue_Wait_Avg, - Topology_Queue_Wait_Max, - Task_Node_Run_Avg, - Task_Node_Run_Max, - Frame_Business_Task_Count, - Frame_Business_Task_Peak_Parallelism, - Frame_Topology_Queue_Wait, - Frame_Business_Task_Run_Total, - Outcome, - Cache_Hit, - Cache_Miss, - Cache_Ratio, - Cache_Rebuild_Count, - Cache_Rebuild_Time, - Cache_Compose_Time, - Cache_Image_Area, - Cache_Image_Bytes, - Cache_Edit_Version, - Cache_Ready_Version, - Current_Ms, - Smooth_Ms, - Variation_Ms, - P50_Ms, - P95_Ms, - P99_Ms, - Current_Percent, - Smooth_Percent, - Variation_Percent, - P50_Percent, - P95_Percent, - P99_Percent, - Raw_Input_Count, - Prepared_Output_Count, - Observed_Frame_Count, - Consumed_Frame_Count, - Superseded_Frame_Count, - Cancelled_Frame_Count, - Dropped_Input_Count, - Generic_Count, - Generic_Number, - Count -}; -inline constexpr std::size_t performance_field_count = static_cast(Performance_Metric_Id::Count); -struct Performance_Field_Layout_State { - double max_value_width{}; -}; -struct Performance_Text_Span { - bool value{}; - Performance_Metric_Id field_id = Performance_Metric_Id::Generic_Number; - Performance_Text_Role role = Performance_Text_Role::Value; - std::string text; - std::string field_key; - double measured_width{}; - double reserved_width{}; - double x_offset{}; - int column_index{}; -}; -struct Performance_Line_Layout { - std::vector runs; - double width{}; -}; -using Performance_Frame_Record = std::shared_ptr; -struct Performance_Worker_State { - static constexpr std::size_t Capacity = 256; - Performance_Worker_State() - : metric_queue(Capacity) {} - rigtorp::MPMCQueue metric_queue; - std::atomic dropped_frame_record_count{0}; - std::atomic_bool enabled{false}; - std::atomic_bool worker_active{false}; - std::atomic_bool work_requested{false}; - std::atomic_bool cancelled{false}; - std::atomic_bool reset_requested{false}; - std::atomic_bool snapshot_rebuild_requested{false}; - std::atomic_bool repaint_requested{false}; - std::atomic copy_button_state{Button_State::Normal}; - std::atomic close_button_state{Button_State::Normal}; - std::atomic scroll_offset{0.0}; - std::atomic pending_wheel_delta_px{0.0}; - std::atomic pending_page_delta{0}; - std::atomic_bool drag_active{false}; - std::atomic_bool drag_end_requested{false}; - std::atomic drag_position_y{0.0}; - std::atomic drag_anchor_y{0.0}; - std::atomic viewport_width{0}; - std::atomic viewport_height{0}; - std::atomic viewport_version{0}; - std::atomic next_content_version{1}; - Performance_Aggregate aggregate; - std::array field_layout_states{}; - std::unordered_map field_max_value_widths; - RectF last_scroll_track_rect; - RectF last_scroll_thumb_rect; - double last_content_viewport_height{}; - double last_line_height{1.0}; - double maximum_scroll_offset{}; - double maximum_content_width{}; - mutable std::mutex options_mutex; - Performance_Overlay_Options options; - std::mutex update_callback_mutex; - std::function update_callback; - std::atomic> published_snapshot; -}; -inline double performance_clamp_scroll_offset(double offset, double max_offset) { - if (!std::isfinite(offset)) - return 0.0; - if (max_offset <= 0.0) - return 0.0; - return std::clamp(offset, 0.0, max_offset); -} -inline double performance_atomic_exchange(std::atomic& value, double replacement = 0.0) { - double current = value.load(std::memory_order_acquire); - while (!value.compare_exchange_weak(current, replacement, std::memory_order_acq_rel, std::memory_order_acquire)) {} - return current; -} -inline void performance_atomic_add(std::atomic& value, double delta) { - double current = value.load(std::memory_order_acquire); - while (!value.compare_exchange_weak(current, current + delta, std::memory_order_acq_rel, std::memory_order_acquire)) {} -} -void schedule_performance_metrics_worker(const std::shared_ptr& state, bool request_repaint = false); - -struct Performance_Metric_Cell { - std::string label; - std::string value; - std::string unit; - Performance_Metric_Id metric_id = Performance_Metric_Id::Generic_Number; - Performance_Text_Role value_role = Performance_Text_Role::Value; -}; - -struct Performance_Metric_Row { - std::vector cells; -}; - -struct Performance_Metric_Section { - std::string title; - std::vector rows; -}; - -struct Performance_Layout_Builder { - Performance_Aggregate aggregate; - std::vector sections; - std::vector lines; - std::vector render_lines; - std::vector text_runs; - std::array field_layout_states{}; - std::unordered_map field_max_value_widths; - RectF panel_rect; - RectF scroll_bar_rect; - RectF scroll_thumb_rect; - Button copy_button{"Copy"}; - Button close_button{"Close"}; - double content_width{}; - double toolbar_height{}; - double button_gap{4.0}; - static std::string value(double number, int precision = 2) { - return format_number(number, {.precision = precision}); - } - double panel_height(double line_height, Performance_Overlay_Render_State* state, int plot_height) const { - return std::min(content_height(line_height, state), static_cast(std::max(0, plot_height))); - } - double text_top(Performance_Overlay_Render_State* state) const { - return static_cast(state->top_margin) + toolbar_height + 4.0; - } - double content_height(double line_height, Performance_Overlay_Render_State* state) const { - return line_height * static_cast(render_lines.size()) + text_top(state) + state->bottom_margin; - } - double content_viewport_height(Performance_Overlay_Render_State* state, double height) const { - return std::max(0.0, height - text_top(state) - state->bottom_margin); - } - void update_toolbar_button_rects(Performance_Overlay_Render_State* state) { - copy_button.set_rect({}); - close_button.set_rect({}); - if (panel_rect.empty() || toolbar_height <= 0.0) - return; - double right = panel_rect.width - static_cast(state->right_margin + state->scroll_bar_width + state->scroll_bar_margin * 2); - double left = static_cast(state->left_margin); - double available_width = std::max(0.0, right - left); - double close_width = std::min(close_button.preferred_width(), available_width); - if (close_width <= 0.0) - return; - close_button.set_rect(RectF{right - close_width, static_cast(state->top_margin), close_width, toolbar_height}); - available_width = std::max(0.0, close_button.rect().x - button_gap - left); - double copy_width = std::min(copy_button.preferred_width(), available_width); - if (copy_width <= 0.0) - return; - copy_button.set_rect(RectF{close_button.rect().x - button_gap - copy_width, static_cast(state->top_margin), copy_width, toolbar_height}); - } - void update_scroll_bar_rects(Performance_Overlay_Render_State* state, double max_scroll, double scroll, double visible_height, double total_height) { - if (max_scroll <= 0.0 || panel_rect.width <= 0.0 || panel_rect.height <= 0.0 || total_height <= 0.0) { - scroll_bar_rect = {}; - scroll_thumb_rect = {}; - return; - } - double track_height = std::max(0.0, panel_rect.height - static_cast(state->scroll_bar_margin * 2)); - scroll_bar_rect = RectF{ - panel_rect.x + panel_rect.width - static_cast(state->scroll_bar_width + state->scroll_bar_margin), - static_cast(state->scroll_bar_margin), - static_cast(state->scroll_bar_width), - track_height - }; - double thumb_height = std::max(16.0, scroll_bar_rect.height * visible_height / std::max(visible_height, total_height)); - thumb_height = std::min(thumb_height, scroll_bar_rect.height); - double travel = std::max(0.0, scroll_bar_rect.height - thumb_height); - double thumb_y = scroll_bar_rect.y + (max_scroll > 0.0 ? travel * scroll / max_scroll : 0.0); - scroll_thumb_rect = RectF{scroll_bar_rect.x, thumb_y, scroll_bar_rect.width, thumb_height}; - } - static bool ends_with(std::string_view text, std::string_view suffix) { - return text.size() >= suffix.size() && text.substr(text.size() - suffix.size()) == suffix; - } - static bool contains(std::string_view text, std::string_view needle) { - return text.find(needle) != std::string_view::npos; - } - static Performance_Metric_Id field_id_from_key(std::string_view key) { - if (key == "frame_id") - return Performance_Metric_Id::Frame_Id; - if (key == "input_version") - return Performance_Metric_Id::Input_Version; - if (key == "render_period") - return Performance_Metric_Id::Render_Period; - if (key == "paint_period") - return Performance_Metric_Id::Paint_Period; - if (key == "user_period") - return Performance_Metric_Id::User_Period; - if (key == "target_period") - return Performance_Metric_Id::Target_Period; - if (key == "render_slot_idle_wait") - return Performance_Metric_Id::Render_Slot_Idle_Wait; - if (key == "executor_queue_wait") - return Performance_Metric_Id::Executor_Queue_Wait; - if (key == "ready_queue_wait") - return Performance_Metric_Id::Ready_Queue_Wait; - if (key == "present_queue_wait") - return Performance_Metric_Id::Present_Queue_Wait; - if (key == "paint_return_wait") - return Performance_Metric_Id::Paint_Return_Wait; - if (key == "taskflow_workers") - return Performance_Metric_Id::Worker_Count; - if (key == "taskflow_active") - return Performance_Metric_Id::Active_Workers; - if (key == "taskflow_queued_frames") - return Performance_Metric_Id::Queued_Frame_Jobs; - if (key == "taskflow_active_frames") - return Performance_Metric_Id::Active_Frame_Jobs; - if (key == "taskflow_submitted") - return Performance_Metric_Id::Submitted_Topologies; - if (key == "taskflow_topologies_started") - return Performance_Metric_Id::Started_Topologies; - if (key == "taskflow_topologies_completed") - return Performance_Metric_Id::Completed_Topologies; - if (key == "taskflow_started") - return Performance_Metric_Id::Started_Task_Nodes; - if (key == "taskflow_completed") - return Performance_Metric_Id::Completed_Task_Nodes; - if (key == "taskflow_rejected") - return Performance_Metric_Id::Rejected_Frame_Jobs; - if (key == "taskflow_queue_wait_avg") - return Performance_Metric_Id::Topology_Queue_Wait_Avg; - if (key == "taskflow_queue_wait_max") - return Performance_Metric_Id::Topology_Queue_Wait_Max; - if (key == "taskflow_run_avg") - return Performance_Metric_Id::Task_Node_Run_Avg; - if (key == "taskflow_run_max") - return Performance_Metric_Id::Task_Node_Run_Max; - if (key == "frame_worker_tasks") - return Performance_Metric_Id::Frame_Business_Task_Count; - if (key == "frame_worker_peak") - return Performance_Metric_Id::Frame_Business_Task_Peak_Parallelism; - if (key == "frame_worker_wait_max") - return Performance_Metric_Id::Frame_Topology_Queue_Wait; - if (key == "frame_worker_run_total") - return Performance_Metric_Id::Frame_Business_Task_Run_Total; - if (key == "frame_outcome") - return Performance_Metric_Id::Outcome; - if (key == "cache_hit_count") - return Performance_Metric_Id::Cache_Hit; - if (key == "cache_miss_count") - return Performance_Metric_Id::Cache_Miss; - if (key == "cache_hit_ratio") - return Performance_Metric_Id::Cache_Ratio; - if (key == "cache_rebuild_count") - return Performance_Metric_Id::Cache_Rebuild_Count; - if (key == "cache_rebuild_time") - return Performance_Metric_Id::Cache_Rebuild_Time; - if (key == "cache_compose_time") - return Performance_Metric_Id::Cache_Compose_Time; - if (key == "raw_input_count") - return Performance_Metric_Id::Raw_Input_Count; - if (key == "prepared_output_count") - return Performance_Metric_Id::Prepared_Output_Count; - if (key == "observed_frame_count") - return Performance_Metric_Id::Observed_Frame_Count; - if (key == "consumed_outcome_count") - return Performance_Metric_Id::Consumed_Frame_Count; - if (key == "superseded_outcome_count") - return Performance_Metric_Id::Superseded_Frame_Count; - if (key == "cancelled_outcome_count") - return Performance_Metric_Id::Cancelled_Frame_Count; - if (key == "input_capacity_dropped") - return Performance_Metric_Id::Dropped_Input_Count; - if (ends_with(key, "_cache_hit")) - return Performance_Metric_Id::Cache_Hit; - if (ends_with(key, "_cache_miss")) - return Performance_Metric_Id::Cache_Miss; - if (ends_with(key, "_cache_rebuild")) - return Performance_Metric_Id::Cache_Rebuild_Count; - if (ends_with(key, "_cache_ratio")) - return Performance_Metric_Id::Cache_Ratio; - if (ends_with(key, "_cache_image_area")) - return Performance_Metric_Id::Cache_Image_Area; - if (ends_with(key, "_cache_image_bytes")) - return Performance_Metric_Id::Cache_Image_Bytes; - if (ends_with(key, "_cache_edit_version")) - return Performance_Metric_Id::Cache_Edit_Version; - if (ends_with(key, "_cache_ready_version")) - return Performance_Metric_Id::Cache_Ready_Version; - if (contains(key, "ratio")) { - if (ends_with(key, "_instant")) - return Performance_Metric_Id::Current_Percent; - if (ends_with(key, "_smooth")) - return Performance_Metric_Id::Smooth_Percent; - if (ends_with(key, "_variation")) - return Performance_Metric_Id::Variation_Percent; - if (ends_with(key, "_p50")) - return Performance_Metric_Id::P50_Percent; - if (ends_with(key, "_p95")) - return Performance_Metric_Id::P95_Percent; - if (ends_with(key, "_p99")) - return Performance_Metric_Id::P99_Percent; - return Performance_Metric_Id::Current_Percent; - } - if (ends_with(key, "_instant")) - return Performance_Metric_Id::Current_Ms; - if (ends_with(key, "_smooth") || ends_with(key, "_srtt")) - return Performance_Metric_Id::Smooth_Ms; - if (ends_with(key, "_variation") || ends_with(key, "_jitter")) - return Performance_Metric_Id::Variation_Ms; - if (ends_with(key, "_p50")) - return Performance_Metric_Id::P50_Ms; - if (ends_with(key, "_p95")) - return Performance_Metric_Id::P95_Ms; - if (ends_with(key, "_p99")) - return Performance_Metric_Id::P99_Ms; - if (ends_with(key, "_fps") || ends_with(key, "_count")) - return Performance_Metric_Id::Generic_Count; - return Performance_Metric_Id::Generic_Number; - } - static constexpr std::array outcome_values() { - return { - Frame_Outcome::Presented, - Frame_Outcome::Superseded, - Frame_Outcome::Render_Acquire_Dropped, - Frame_Outcome::Publish_Dropped, - Frame_Outcome::Paint_Acquire_Dropped, - Frame_Outcome::Worker_Budget_Dropped, - Frame_Outcome::Cancelled - }; - } - void seed_static_field_widths(Canvas& canvas) { - for (Frame_Outcome outcome : outcome_values()) { - Performance_Text_Span run; - run.value = true; - run.field_id = Performance_Metric_Id::Outcome; - run.text = outcome_text(outcome); - measure_run(canvas, run); - } - } - std::string number_text(const std::string& key, double number, int precision = 2) { - return value(number, precision); - } - std::string count_text(const std::string& key, std::uint64_t count) { - return std::to_string(count); - } - std::string duration_line(const std::string& key, const std::string& name, const Psc::Value_Statistics& statistics) { - return name + " current:" + number_text(key + "_instant", statistics.instant) + "ms smooth:" + number_text(key + "_smooth", statistics.smooth) + "ms P50:" + number_text(key + "_p50", statistics.p50) + "ms P95:" + number_text(key + "_p95", statistics.p95) + "ms P99:" + number_text(key + "_p99", statistics.p99) + "ms"; - } - std::string percent_stat_line(const std::string& key, const std::string& name, const Psc::Value_Statistics& statistics) { - return name + " current:" + number_text(key + "_instant", statistics.instant) + "% smooth:" + number_text(key + "_smooth", statistics.smooth) + "% P50:" + number_text(key + "_p50", statistics.p50) + "% P95:" + number_text(key + "_p95", statistics.p95) + "% P99:" + number_text(key + "_p99", statistics.p99) + "%"; - } - std::string interval_line(const std::string& key, const std::string& name, double srtt, double jitter, double fps) { - return name + " interval_smooth:" + number_text(key + "_srtt", srtt) + "ms var:" + number_text(key + "_jitter", jitter) + "ms actual:" + number_text(key + "_fps", fps) + "fps"; - } - static double percent(double value) { - return value * 100.0; - } - std::string percent_text(const std::string& key, double ratio) { - return number_text(key, percent(ratio)) + "%"; - } - static std::string limit_source_text(Refresh_Limit_Source source) { - switch (source) { - case Refresh_Limit_Source::Unknown: - return "unknown"; - case Refresh_Limit_Source::Input: - return "input"; - case Refresh_Limit_Source::Render: - return "render"; - case Refresh_Limit_Source::Paint: - return "paint"; - case Refresh_Limit_Source::User: - return "user"; - } - return "unknown"; - } - static std::string bottleneck_text(Low_Latency_Bottleneck_State state) { - switch (state) { - case Low_Latency_Bottleneck_State::Unknown: - return "unknown"; - case Low_Latency_Bottleneck_State::Input_Limited: - return "input"; - case Low_Latency_Bottleneck_State::Producer_Limited: - return "producer"; - case Low_Latency_Bottleneck_State::Consumer_Limited: - return "consumer"; - case Low_Latency_Bottleneck_State::User_Limited: - return "user"; - } - return "unknown"; - } - static std::string production_mode_text(Low_Latency_Production_Mode mode) { - switch (mode) { - case Low_Latency_Production_Mode::Latency_Eager: - return "latency_eager"; - case Low_Latency_Production_Mode::Consumer_Paced: - return "consumer_paced"; - } - return "latency_eager"; - } - static std::string limit_reason_text(Low_Latency_Limit_Reason reason) { - switch (reason) { - case Low_Latency_Limit_Reason::User_Fps: - return "user_fps"; - case Low_Latency_Limit_Reason::Consumer_Capacity: - return "consumer_capacity"; - case Low_Latency_Limit_Reason::Render_Capacity: - return "render_capacity"; - case Low_Latency_Limit_Reason::No_Work: - return "no_work"; - case Low_Latency_Limit_Reason::Shutdown: - return "shutdown"; - } - return "user_fps"; - } - static std::string consumer_confidence_text(Consumer_Confidence confidence) { - switch (confidence) { - case Consumer_Confidence::Low: - return "low"; - case Consumer_Confidence::Medium: - return "medium"; - case Consumer_Confidence::High: - return "high"; - } - return "low"; - } - static std::string consumer_sample_result_text(Consumer_Sample_Result result) { - switch (result) { - case Consumer_Sample_Result::Accepted: - return "accepted"; - case Consumer_Sample_Result::Rejected_No_Backlog: - return "rejected_no_backlog"; - case Consumer_Sample_Result::Rejected_Producer_Starved: - return "rejected_producer_starved"; - case Consumer_Sample_Result::Rejected_No_New_Frame: - return "rejected_no_new_frame"; - case Consumer_Sample_Result::Rejected_Invalid_Timestamp: - return "rejected_invalid_timestamp"; - } - return "rejected_invalid_timestamp"; - } - static std::string feedback_decision_text(Low_Latency_Feedback_Decision decision) { - switch (decision) { - case Low_Latency_Feedback_Decision::Hold_User_Period: - return "hold_user_period"; - case Low_Latency_Feedback_Decision::Enter_Consumer_Paced: - return "enter_consumer_paced"; - case Low_Latency_Feedback_Decision::Exit_Consumer_Paced: - return "exit_consumer_paced"; - case Low_Latency_Feedback_Decision::Reject_Consumer_Sample: - return "reject_consumer_sample"; - case Low_Latency_Feedback_Decision::Producer_Starved: - return "producer_starved"; - } - return "hold_user_period"; - } - static std::string dirty_reason_text(std::uint32_t mask) { - std::string result; - auto append = [&result](const char* text) { - if (!result.empty()) - result += "|"; - result += text; - }; - if (mask & dirty_reason_bit(Dirty_Reason::Core_Data)) - append("core"); - if (mask & dirty_reason_bit(Dirty_Reason::Config)) - append("config"); - if (mask & dirty_reason_bit(Dirty_Reason::Viewport)) - append("viewport"); - return result.empty() ? "none" : result; - } - static std::string outcome_text(Frame_Outcome outcome) { - switch (outcome) { - case Frame_Outcome::Presented: - return "presented"; - case Frame_Outcome::Superseded: - return "superseded"; - case Frame_Outcome::Render_Acquire_Dropped: - return "render acquire dropped"; - case Frame_Outcome::Publish_Dropped: - return "publish dropped"; - case Frame_Outcome::Paint_Acquire_Dropped: - return "paint acquire dropped"; - case Frame_Outcome::Worker_Budget_Dropped: - return "worker budget dropped"; - case Frame_Outcome::Cancelled: - return "cancelled"; - } - return "unknown"; - } - std::string period_text(const std::string& key, std::uint64_t ns) { - return number_text(key, static_cast(ns) / 1000000.0) + "ms"; - } - static double cache_total(const Renderable_Cache_Aggregate_Stat& stat) { - return static_cast(stat.hit_count + stat.miss_count); - } - std::string cache_ratio_text(const std::string& key, std::uint64_t hit, std::uint64_t miss) { - std::uint64_t total = hit + miss; - if (!total) - return number_text(key, 0.0) + "%"; - return number_text(key, static_cast(hit) * 100.0 / static_cast(total)) + "%"; - } - std::string ns_text(const std::string& key, std::uint64_t ns) { - return number_text(key, static_cast(ns) / 1000000.0); - } - std::string cache_key(const Renderable_Cache_Aggregate_Stat& stat) const { - return stat.parent_object_name + "/" + stat.object_name; - } - std::string renderable_key(const Renderable_Aggregate_Stat& stat) const { - return stat.parent_object_name + "/" + stat.object_name + "/" + std::to_string(stat.tree_depth); - } - static std::string indent_str(int depth) { - return std::string(static_cast(std::max(0, depth)) * 2, ' '); - } - static std::string cache_mode_text(bool cache_enabled) { - return cache_enabled ? "cached" : "direct"; - } - static std::uint64_t frame_render_time_ns(const Frame_Lifecycle_Record& frame) { - return frame.render_end_ns > frame.render_begin_ns ? frame.render_end_ns - frame.render_begin_ns : 0; - } - std::string ns_ratio_text(const std::string& key, std::uint64_t numerator, std::uint64_t denominator) { - if (!denominator) - return number_text(key, 0.0) + "%"; - return number_text(key, static_cast(numerator) * 100.0 / static_cast(denominator)) + "%"; - } - bool should_show_cache(const Renderable_Cache_Aggregate_Stat& stat) const { - return cache_total(stat) > 0.0 || stat.rebuild_count || stat.rebuild_ms.times || stat.compose_ms.times || stat.subtree_compose_ms.times; - } - bool should_show_renderable(const Renderable_Aggregate_Stat& stat) const { - return stat.render_count || stat.self_draw_count || stat.total_render_ms.times || stat.self_draw_ms.times; - } - std::string cache_duration_text(const std::string& key, const std::string& name, const Psc::Value_Statistics& statistics) { - return name + " current:" + number_text(key + "_instant", statistics.instant) + "ms smooth:" + number_text(key + "_smooth", statistics.smooth) + "ms P50:" + number_text(key + "_p50", statistics.p50) + "ms P95:" + number_text(key + "_p95", statistics.p95) + "ms P99:" + number_text(key + "_p99", statistics.p99) + "ms"; - } - void append_cache_node_lines(const Renderable_Cache_Aggregate_Stat& stat) { - std::string key = cache_key(stat); - std::string indent = indent_str(stat.tree_depth); - std::string name = stat.object_name.empty() ? "unnamed" : stat.object_name; - lines.push_back("cache tree " + indent + name + " hit:" + count_text(key + "_cache_hit", stat.hit_count) + " stale:" + count_text(key + "_cache_miss", stat.miss_count) + " miss:" + count_text(key + "_cache_rebuild", stat.rebuild_count) + " ratio:" + cache_ratio_text(key + "_cache_ratio", stat.hit_count, stat.miss_count)); - lines.push_back("cache image " + indent + name + " area:" + count_text(key + "_cache_image_area", stat.last_image_area) + " bytes:" + count_text(key + "_cache_image_bytes", stat.last_image_bytes) + " version:" + count_text(key + "_cache_edit_version", stat.last_edit_version) + "/" + count_text(key + "_cache_ready_version", stat.last_ready_version)); - lines.push_back("cache rebuild " + indent + name + " " + cache_duration_text(key + "_rebuild", "rebuild", stat.rebuild_ms)); - lines.push_back("cache compose " + indent + name + " " + cache_duration_text(key + "_compose", "self compose", stat.compose_ms)); - lines.push_back("cache subtree " + indent + name + " " + cache_duration_text(key + "_subtree_compose", "subtree compose", stat.subtree_compose_ms)); - } - void append_renderable_node_lines(const Renderable_Aggregate_Stat& stat) { - std::string key = renderable_key(stat); - std::string indent = indent_str(stat.tree_depth); - std::string name = (stat.object_name.empty() ? "unnamed" : stat.object_name); - std::uint64_t latest_render_time = frame_render_time_ns(aggregate.latest); - std::string name_indent = indent + std::string(name.size(), ' '); - lines.push_back(indent + " " + name + " cache:" + cache_mode_text(stat.cache_enabled) + " render:" + count_text(key + "_render_count", stat.render_count) + " draws:" + count_text(key + "_self_draw_count", stat.self_draw_count) + " cur%:" + ns_ratio_text(key + "_current_total_ratio", stat.last_total_render_time_ns, latest_render_time) + " total%:" + ns_ratio_text(key + "_aggregate_total_ratio", stat.total_render_time_ns, aggregate.render_time_ns)); - lines.push_back(name_indent + " total " + duration_line(key + "_total_render", "time", stat.total_render_ms)); - lines.push_back(name_indent + " total% " + percent_stat_line(key + "_total_ratio", "ratio", stat.total_render_ratio)); - lines.push_back(name_indent + " self " + duration_line(key + "_self_draw", "time", stat.self_draw_ms)); - lines.push_back(name_indent + " self% " + percent_stat_line(key + "_self_ratio", "ratio", stat.self_draw_ratio)); - } - void append_renderable_tree_lines(std::uint64_t parent_node_id, int tree_depth, std::vector& emitted) { - std::vector children; - for (const auto& stat : aggregate.renderable_stats) { - if (stat.parent_node_id == parent_node_id && stat.tree_depth == tree_depth && should_show_renderable(stat)) - children.push_back(&stat); - } - std::stable_sort(children.begin(), children.end(), [](const auto* a, const auto* b) { - return a->object_name < b->object_name; - }); - for (const auto* stat : children) { - if (std::find(emitted.begin(), emitted.end(), stat) != emitted.end()) - continue; - emitted.push_back(stat); - append_renderable_node_lines(*stat); - append_renderable_tree_lines(stat->node_id, stat->tree_depth + 1, emitted); - } - } - void append_renderable_tree_lines() { - std::vector emitted; - append_renderable_tree_lines(0, 0, emitted); - for (const auto& stat : aggregate.renderable_stats) { - if (!should_show_renderable(stat) || std::find(emitted.begin(), emitted.end(), &stat) != emitted.end()) - continue; - emitted.push_back(&stat); - append_renderable_node_lines(stat); - } - } - void append_cache_tree_lines(std::uint64_t parent_node_id, int tree_depth, std::vector& emitted) { - std::vector children; - for (const auto& stat : aggregate.renderable_cache_stats) { - if (stat.parent_node_id == parent_node_id && stat.tree_depth == tree_depth && should_show_cache(stat)) - children.push_back(&stat); - } - std::stable_sort(children.begin(), children.end(), [](const auto* a, const auto* b) { - return a->object_name < b->object_name; - }); - for (const auto* stat : children) { - if (std::find(emitted.begin(), emitted.end(), stat) != emitted.end()) - continue; - emitted.push_back(stat); - append_cache_node_lines(*stat); - append_cache_tree_lines(stat->node_id, stat->tree_depth + 1, emitted); - } - } - void append_cache_tree_lines() { - std::vector emitted; - append_cache_tree_lines(0, 0, emitted); - for (const auto& stat : aggregate.renderable_cache_stats) { - if (!should_show_cache(stat) || std::find(emitted.begin(), emitted.end(), &stat) != emitted.end()) - continue; - emitted.push_back(&stat); - append_cache_node_lines(stat); - } - } - void rebuild_lines() { - if (!aggregate.has_frame) { - lines = {"waiting for frame stats"}; - return; - } - const Frame_Lifecycle_Record& frame = aggregate.latest; - const Refresh_Control_Snapshot& refresh = frame.refresh; - const Frame_Worker_Stats& worker = frame.worker; - const Render_Executor_Snapshot& executor = worker.executor_at_finish; - std::uint64_t aggregate_cache_total = aggregate.cache_hit_count + aggregate.cache_miss_count; - std::uint64_t latest_render_time = frame_render_time_ns(frame); - double cache_hit_ratio = aggregate_cache_total ? static_cast(aggregate.cache_hit_count) * 100.0 / static_cast(aggregate_cache_total) : 0.0; - lines.clear(); - lines.push_back("[common] frame:" + count_text("frame_id", frame.frame_id) + " input_ver:" + count_text("input_version", frame.input_version) + " outcome:" + outcome_text(frame.outcome) + " dirty:" + dirty_reason_text(frame.dirty_reason_mask)); - lines.push_back("[low_latency] mode:" + production_mode_text(refresh.production_mode) + " limit_reason:" + limit_reason_text(refresh.limit_reason) + " consumer_confidence:" + consumer_confidence_text(refresh.consumer_confidence) + " effective:" + period_text("effective_interval", refresh.effective_min_render_interval_ns) + " user:" + period_text("user_period", refresh.user_period_ns)); - lines.push_back("[low_latency] consumer_samples accepted:" + count_text("consumer_accepted", refresh.consumer_sample_accepted_count) + " rejected:" + count_text("consumer_rejected", refresh.consumer_sample_total_count - refresh.consumer_sample_accepted_count) + " ratio:" + number_text("consumer_ratio", refresh.consumer_sample_accepted_ratio * 100.0) + "% last:" + consumer_sample_result_text(refresh.last_consumer_sample_result)); - lines.push_back("[low_latency] timing producer_interval:" + period_text("producer_interval", refresh.producer_interval_ns) + " render_service:" + period_text("render_period", refresh.render_period_ns) + " request_to_post:" + period_text("request_to_update_post", refresh.request_to_update_post_srtt_ns) + " post_to_paint:" + period_text("update_to_paint", refresh.update_post_to_paint_srtt_ns) + " paint_service:" + period_text("paint_period", refresh.paint_service_ns) + " raw_display:" + period_text("raw_display", refresh.raw_display_interval_srtt_ns) + " accepted_consumer:" + period_text("accepted_consumer", refresh.accepted_consumer_interval_srtt_ns)); - lines.push_back("[low_latency] consumer_window size:" + count_text("consumer_window_size", refresh.consumer_window_size) + " accepted:" + count_text("consumer_window_accepted", refresh.consumer_window_accepted) + " rejected:" + count_text("consumer_window_rejected", refresh.consumer_window_size - refresh.consumer_window_accepted) + " ratio:" + number_text("consumer_window_ratio", refresh.consumer_window_accepted_ratio * 100.0) + "%"); - lines.push_back("[low_latency] pacing admission_wait:" + ns_text("admission_wait", refresh.admission_wait_elapsed_ns) + "ms render_slot_idle:" + ns_text("render_slot_idle_wait", frame.render_slot_idle_wait_ns) + "ms"); - lines.push_back("[pipeline] queue executor_wait:" + ns_text("executor_queue_wait", frame.executor_queue_wait_ns) + "ms present_wait:" + ns_text("present_wait", frame.present_queue_wait_ns) + "ms paint_return:" + ns_text("paint_return_wait", frame.paint_return_wait_ns) + "ms"); - lines.push_back("[low_latency] update posted:" + count_text("update_posted_count", refresh.update_posted_count) + " duplicate_suppressed:" + count_text("duplicate_present_request_suppressed_count", refresh.duplicate_present_request_suppressed_count) + " ack:" + count_text("update_ack_count", refresh.update_ack_count) + " repost:" + count_text("update_repost_count", refresh.update_repost_count) + " paint_without_posted:" + count_text("paint_without_posted", refresh.paint_without_posted_update_count)); - lines.push_back("[logger] dropped:" + count_text("logger_dropped", performance_log_service().dropped_record_count())); - lines.push_back("[low_latency] buffers rendered:" + count_text("presented_feedback", refresh.presented_frame_count) + " middle_published:" + count_text("middle_published", refresh.middle_published_count) + " middle_superseded:" + count_text("middle_superseded", refresh.middle_superseded_count) + " painting_advanced:" + count_text("painting_advanced", refresh.painting_advanced_count) + " painting_repeated:" + count_text("repeated_feedback", refresh.repeated_frame_count) + " underrun:" + count_text("underrun_feedback", refresh.underrun_count)); - lines.push_back("[low_latency] decision:" + feedback_decision_text(refresh.last_decision) + " reason:" + refresh.decision_reason + " at:" + count_text("decision_time", refresh.decision_timestamp_ns) + " producer_starvation:" + count_text("producer_starvation", refresh.producer_starvation_count) + " starvation_active:" + (refresh.producer_starvation_active ? std::string("1") : std::string("0")) + " enter:" + count_text("mode_enter", refresh.mode_enter_count) + " exit:" + count_text("mode_exit", refresh.mode_exit_count) + " efficiency:" + number_text("display_efficiency", refresh.display_efficiency * 100.0) + "%"); - lines.push_back("[executor/global] workers:" + count_text("taskflow_workers", static_cast(executor.worker_count)) + " active_workers:" + count_text("taskflow_active", static_cast(executor.active_workers)) + " queued_frame_jobs:" + count_text("taskflow_queued_frames", static_cast(executor.queued_frame_jobs)) + " active_frame_jobs:" + count_text("taskflow_active_frames", static_cast(executor.active_frame_jobs))); - lines.push_back("[executor/global] topologies submitted:" + count_text("taskflow_submitted", executor.submitted_topologies) + " started:" + count_text("taskflow_topologies_started", executor.started_topologies) + " completed:" + count_text("taskflow_topologies_completed", executor.completed_topologies) + " rejected_frame_jobs:" + count_text("taskflow_rejected", executor.rejected_frame_jobs)); - lines.push_back("[executor/global] task_nodes started:" + count_text("taskflow_started", executor.started_task_nodes) + " completed:" + count_text("taskflow_completed", executor.completed_task_nodes)); - lines.push_back("[executor/global] topology_queue_wait avg:" + ns_text("taskflow_queue_wait_avg", executor.average_topology_queue_wait_ns) + "ms max:" + ns_text("taskflow_queue_wait_max", executor.max_topology_queue_wait_ns) + "ms task_node_run avg:" + ns_text("taskflow_run_avg", executor.average_task_node_duration_ns) + "ms max:" + ns_text("taskflow_run_max", executor.max_task_node_duration_ns) + "ms"); - lines.push_back("[executor/frame] business_tasks:" + count_text("frame_worker_tasks", worker.business_task_count) + " peak_parallel:" + count_text("frame_worker_peak", worker.business_task_peak_parallelism) + " topology_queue_wait:" + ns_text("frame_worker_wait_max", worker.topology_queue_wait_max_ns) + "ms business_run_total:" + ns_text("frame_worker_run_total", worker.business_task_run_total_ns) + "ms"); - lines.push_back(duration_line("request_wait", "lifecycle render_request_to_start", aggregate.request_wait_ms)); - lines.push_back(duration_line("producer_interval", "timing producer_interval", aggregate.producer_interval_ms)); - lines.push_back(duration_line("admission_wait", "timing admission_wait", aggregate.admission_wait_ms)); - lines.push_back(duration_line("render_slot_idle_wait", "timing render_slot_idle_wait", aggregate.render_slot_idle_wait_ms)); - lines.push_back(duration_line("executor_queue_wait", "timing executor_queue_wait", aggregate.executor_queue_wait_ms)); - lines.push_back(duration_line("prepare", "lifecycle prepare", aggregate.prepare_ms)); - lines.push_back(duration_line("draw", "lifecycle draw", aggregate.draw_ms)); - lines.push_back(duration_line("render", "lifecycle render_total", aggregate.render_ms)); - lines.push_back(duration_line("ready_wait", "lifecycle ready_to_paint", aggregate.ready_wait_ms)); - lines.push_back(duration_line("update_wait", "timing update_to_paint", aggregate.update_wait_ms)); - lines.push_back(duration_line("paint", "timing paint_service", aggregate.paint_ms)); - lines.push_back(duration_line("display_interval", "timing display_interval", aggregate.display_interval_ms)); - lines.push_back(duration_line("effective_interval", "timing effective_interval", aggregate.effective_interval_ms)); - lines.push_back(duration_line("frame_age", "lifecycle input_to_display_age", aggregate.frame_age_ms)); - lines.push_back(duration_line("frame_total", "lifecycle e2e_total", aggregate.frame_total_ms)); - lines.push_back("cache hit:" + count_text("cache_hit_count", aggregate.cache_hit_count) + " miss:" + count_text("cache_miss_count", aggregate.cache_miss_count) + " ratio:" + number_text("cache_hit_ratio", cache_hit_ratio)); - lines.push_back("cache rebuild count:" + count_text("cache_rebuild_count", aggregate.cache_rebuild_count) + " time:" + ns_text("cache_rebuild_time", aggregate.cache_rebuild_time_ns) + "ms compose:" + ns_text("cache_compose_time", aggregate.cache_compose_time_ns) + "ms"); - lines.push_back(duration_line("cache_rebuild_slide", "cache total rebuild", aggregate.cache_rebuild_ms)); - lines.push_back(duration_line("cache_compose_slide", "cache total compose", aggregate.cache_compose_ms)); - lines.push_back(percent_stat_line("cache_rebuild_ratio_slide", "cache rebuild ratio", aggregate.cache_rebuild_ratio)); - lines.push_back(percent_stat_line("cache_compose_ratio_slide", "cache compose ratio", aggregate.cache_compose_ratio)); - lines.push_back("cache cur rebuild:" + ns_ratio_text("cache_rebuild_current_ratio", frame.renderable_cache_rebuild_time_ns, latest_render_time) + " compose:" + ns_ratio_text("cache_compose_current_ratio", frame.renderable_cache_compose_time_ns, latest_render_time) + " total rebuild:" + ns_ratio_text("cache_rebuild_aggregate_ratio", aggregate.cache_rebuild_time_ns, aggregate.render_time_ns) + " compose:" + ns_ratio_text("cache_compose_aggregate_ratio", aggregate.cache_compose_time_ns, aggregate.render_time_ns)); - append_renderable_tree_lines(); - append_cache_tree_lines(); - lines.push_back("work raw:" + count_text("raw_input_count", frame.raw_input_count) + " prepared:" + count_text("prepared_output_count", frame.prepared_output_count)); - lines.push_back("observed:" + count_text("observed_frame_count", aggregate.observed_frame_count) + " consumed:" + count_text("consumed_outcome_count", aggregate.consumed_outcome_count) + " superseded:" + count_text("superseded_outcome_count", aggregate.superseded_outcome_count) + " cancelled:" + count_text("cancelled_outcome_count", aggregate.cancelled_outcome_count) + " dropped:" + count_text("input_capacity_dropped", aggregate.input_capacity_dropped)); - } - std::vector stripped_lines() const { - return lines; - } - std::vector display_lines() const { - std::vector result; - result.reserve(render_lines.size()); - for (const auto& line : render_lines) - result.push_back(render_line_text(line)); - return result; - } - static bool warning_line(std::string_view line) { - return (contains(line, "underrun:") && !contains(line, "underrun:0")) - || (contains(line, "dropped:") && !contains(line, "dropped:0")) - || (contains(line, "starvation_active:1")) - || (contains(line, "confidence:low") && contains(line, "consumer_paced")); - } - static bool whitespace(char value) { - return std::isspace(static_cast(value)) != 0; - } - static std::size_t metric_unit_offset(std::string_view value) { - std::size_t index = 0; - if (index < value.size() && (value[index] == '+' || value[index] == '-')) - ++index; - bool has_digit = false; - while (index < value.size()) { - char current = value[index]; - if (std::isdigit(static_cast(current))) { - has_digit = true; - ++index; - continue; - } - if (current == '.' || current == ',') { - ++index; - continue; - } - break; - } - if (!has_digit || index == value.size()) - return std::string_view::npos; - char unit = value[index]; - if (unit == '%' || std::isalpha(static_cast(unit))) - return index; - return std::string_view::npos; - } - static std::size_t first_metric_item(std::string_view text, std::size_t begin) { - std::size_t cursor = begin; - while (cursor < text.size()) { - while (cursor < text.size() && whitespace(text[cursor])) - ++cursor; - std::size_t token_end = cursor; - while (token_end < text.size() && !whitespace(text[token_end])) - ++token_end; - if (text.substr(cursor, token_end - cursor).find(':') != std::string_view::npos) - return cursor; - cursor = token_end; - } - return std::string_view::npos; - } - std::vector plain_line_runs(const std::string& text, Canvas& canvas) { - std::vector runs; - std::unordered_map field_occurrences; - double offset = 0.0; - bool warning = warning_line(text); - double space_width = canvas.measure_text("0 0").width - canvas.measure_text("00").width; - if (space_width <= 0.0) - space_width = std::max(1.0, canvas.measure_text("0").width * 0.5); - auto trim = [](std::string_view value) { - while (!value.empty() && whitespace(value.front())) - value.remove_prefix(1); - while (!value.empty() && whitespace(value.back())) - value.remove_suffix(1); - return std::string(value); - }; - auto append = [&](std::string value, Performance_Text_Role role, bool is_value = false, std::string field_key = {}) { - if (value.empty()) - return; - Performance_Text_Span run; - run.text = std::move(value); - run.field_key = std::move(field_key); - run.role = role; - run.value = is_value; - run.column_index = 0; - run.x_offset = offset; - measure_run(canvas, run); - if (std::all_of(run.text.begin(), run.text.end(), whitespace)) { - run.measured_width = space_width * static_cast(run.text.size()); - run.reserved_width = run.measured_width; - } - offset += run_width(run); - runs.push_back(std::move(run)); - }; - auto make_field_key = [&](std::string_view section, std::string_view context, std::string_view label) { - std::string key; - key.reserve(section.size() + context.size() + label.size() + 3); - key.append(section); - key.push_back('|'); - key.append(context); - key.push_back('|'); - key.append(label); - std::size_t occurrence = field_occurrences[key]++; - key.push_back('|'); - key += std::to_string(occurrence); - return key; - }; - auto append_value = [&](std::string_view value, const std::string& field_key) { - Performance_Text_Role value_role = warning ? Performance_Text_Role::Warning : Performance_Text_Role::Value; - std::size_t unit_offset = metric_unit_offset(value); - if (unit_offset == std::string_view::npos) { - append(std::string(value), value_role, true, field_key); - return; - } - append(std::string(value.substr(0, unit_offset)), value_role, true, field_key); - append(std::string(value.substr(unit_offset)), warning ? Performance_Text_Role::Warning : Performance_Text_Role::Unit); - }; - std::size_t cursor = 0; - std::string section; - std::size_t section_end = text.find(']'); - if (!text.empty() && text.front() == '[' && section_end != std::string::npos) { - section = text.substr(0, section_end + 1); - append(section, Performance_Text_Role::Section); - cursor = section_end + 1; - } - std::size_t item_begin = first_metric_item(text, cursor); - if (item_begin == std::string_view::npos) { - append(text.substr(cursor), warning ? Performance_Text_Role::Warning : Performance_Text_Role::Value); - return runs; - } - std::string context = trim(std::string_view(text).substr(cursor, item_begin - cursor)); - if (item_begin > cursor) - append(text.substr(cursor, item_begin - cursor), Performance_Text_Role::Label); - cursor = item_begin; - while (cursor < text.size()) { - std::size_t whitespace_end = cursor; - while (whitespace_end < text.size() && whitespace(text[whitespace_end])) - ++whitespace_end; - if (whitespace_end > cursor) - append(text.substr(cursor, whitespace_end - cursor), Performance_Text_Role::Muted); - cursor = whitespace_end; - if (cursor >= text.size()) - break; - std::size_t token_end = cursor; - while (token_end < text.size() && !whitespace(text[token_end])) - ++token_end; - std::size_t colon = text.find(':', cursor); - if (colon == std::string::npos || colon >= token_end) { - std::string token = text.substr(cursor, token_end - cursor); - append(token, warning ? Performance_Text_Role::Warning : Performance_Text_Role::Label); - std::size_t next = token_end; - while (next < text.size() && whitespace(text[next])) - ++next; - std::size_t next_end = next; - while (next_end < text.size() && !whitespace(text[next_end])) - ++next_end; - std::size_t next_colon = text.find(':', next); - if (next < text.size() && next_colon != std::string::npos && next_colon < next_end) - context = token; - cursor = token_end; - continue; - } - std::string_view label = std::string_view(text).substr(cursor, colon - cursor); - std::string field_key = make_field_key(section, context, label); - append(text.substr(cursor, colon - cursor + 1), Performance_Text_Role::Label); - append_value(std::string_view(text).substr(colon + 1, token_end - colon - 1), field_key); - cursor = token_end; - } - return runs; - } - void build_render_lines(Canvas& canvas) { - render_lines.clear(); - text_runs.clear(); - for (const auto& text : lines) { - Performance_Line_Layout line; - std::vector runs = plain_line_runs(text, canvas); - for (auto& run : runs) { - line.width = std::max(line.width, run.x_offset + run_width(run)); - line.runs.push_back(std::move(run)); - } - if (!line.runs.empty()) - render_lines.push_back(std::move(line)); - } - if (render_lines.empty()) { - Performance_Line_Layout line; - Performance_Text_Span run; - run.text = "waiting for frame stats"; - measure_run(canvas, run); - append_run(line, run); - render_lines.push_back(std::move(line)); - } - } - void update_content_width(Performance_Overlay_Render_State* state, double& held_width, int viewport_width) { - double max_width = 0.0; - for (const auto& line : render_lines) - max_width = std::max(max_width, line.width); - max_width = std::max(max_width, copy_button.preferred_width() + button_gap + close_button.preferred_width()); - double measured_width = max_width - + static_cast(state->left_margin + state->right_margin + state->scroll_bar_width + state->scroll_bar_margin * 2); - held_width = std::max(held_width, measured_width); - content_width = std::min(held_width, static_cast(std::max(0, viewport_width))); - if (content_width <= 0.0) - content_width = std::min(measured_width, static_cast(std::max(0, viewport_width))); - } - static Color role_color(const Performance_Overlay_Render_State& state, Performance_Text_Role role) { - switch (role) { - case Performance_Text_Role::Section: - return state.section_color; - case Performance_Text_Role::Label: - return state.label_color; - case Performance_Text_Role::Value: - return state.value_color; - case Performance_Text_Role::Unit: - return state.unit_color; - case Performance_Text_Role::Warning: - return state.warning_color; - case Performance_Text_Role::Muted: - return state.muted_color; - } - return state.value_color; - } - void draw_render_line(Canvas& canvas, const Performance_Line_Layout& line, double x, double y, double line_height, const Performance_Overlay_Render_State& state) { - for (const auto& run : line.runs) { - if (run.text.empty()) - continue; - canvas.set_pen(Pen{role_color(state, run.role)}); - canvas.draw_text(PointF{x + run.x_offset, y}, run.text); - text_runs.push_back(Performance_Display_Text_Run{ - run.text, - run.role, - RectF{x + run.x_offset, y, run.measured_width, line_height}, - run.column_index - }); - } - } - static double run_width(const Performance_Text_Span& run) { - return run.value ? run.reserved_width : run.measured_width; - } - static void append_run(Performance_Line_Layout& line, const Performance_Text_Span& run) { - line.runs.push_back(run); - line.width += run_width(run); - } - void append_wrapped_runs(std::vector runs, Canvas& canvas, double available_width) { - Performance_Line_Layout current; - for (auto& original_run : runs) { - Performance_Text_Span run = std::move(original_run); - while (!run.text.empty()) { - double remaining = available_width - current.width; - double width = run_width(run); - if (current.runs.empty() || width <= remaining) { - if (run.value || width <= available_width) { - append_run(current, run); - break; - } - Performance_Text_Span fragment = split_text_run(canvas, run, available_width); - if (fragment.text.empty()) { - append_run(current, run); - break; - } - append_run(current, fragment); - push_non_empty_line(current); - continue; - } - push_non_empty_line(current); - } - } - push_non_empty_line(current); - } - void push_non_empty_line(Performance_Line_Layout& line) { - if (line.runs.empty()) - return; - render_lines.push_back(std::move(line)); - line = {}; - } - Performance_Text_Span split_text_run(Canvas& canvas, Performance_Text_Span& run, double available_width) { - Performance_Text_Span fragment; - if (run.value || run.text.empty()) - return fragment; - std::size_t split = split_utf8_text_to_width(canvas, run.text, available_width); - if (!split || split >= run.text.size()) - return fragment; - fragment.text = run.text.substr(0, split); - measure_run(canvas, fragment); - std::size_t rest = split; - while (rest < run.text.size() && run.text[rest] == ' ') - ++rest; - run.text = run.text.substr(rest); - measure_run(canvas, run); - return fragment; - } - static std::size_t next_utf8_boundary(std::string_view text, std::size_t pos) { - if (pos >= text.size()) - return text.size(); - unsigned char c = static_cast(text[pos]); - std::size_t advance = 1; - if ((c & 0x80u) == 0) - advance = 1; - else if ((c & 0xE0u) == 0xC0u) - advance = 2; - else if ((c & 0xF0u) == 0xE0u) - advance = 3; - else if ((c & 0xF8u) == 0xF0u) - advance = 4; - return std::min(text.size(), pos + advance); - } - static std::size_t split_utf8_text_to_width(Canvas& canvas, const std::string& text, double max_width) { - std::size_t best = 0; - std::size_t best_space = 0; - for (std::size_t pos = 0; pos < text.size();) { - std::size_t next = next_utf8_boundary(text, pos); - std::string candidate = text.substr(0, next); - if (canvas.measure_text(candidate).width > max_width) - break; - best = next; - if (text[pos] == ' ') - best_space = next; - pos = next; - } - if (best_space > 0) - return best_space; - if (best > 0) - return best; - return next_utf8_boundary(text, 0); - } - std::vector legacy_plain_runs(const std::string& line, Canvas& canvas) { - std::vector runs; - Performance_Text_Span run; - run.text = line; - measure_run(canvas, run); - runs.push_back(std::move(run)); - return runs; - } - void measure_run(Canvas& canvas, Performance_Text_Span& run) { - run.measured_width = run.text.empty() ? 0.0 : canvas.measure_text(run.text).width; - if (!run.value) { - run.reserved_width = run.measured_width; - return; - } - if (!run.field_key.empty()) { - double& max_width = field_max_value_widths[run.field_key]; - max_width = std::max(max_width, run.measured_width); - run.reserved_width = max_width; - return; - } - auto index = static_cast(run.field_id); - if (index >= field_layout_states.size()) - index = static_cast(Performance_Metric_Id::Generic_Number); - field_layout_states[index].max_value_width = std::max(field_layout_states[index].max_value_width, run.measured_width); - run.reserved_width = field_layout_states[index].max_value_width; - } - static std::string render_line_text(const Performance_Line_Layout& line) { - std::string text; - for (const auto& run : line.runs) - text += run.text; - return text; - } - static std::string plain_text(const std::string& line) { - return line; - } -}; - -struct Performance_Overlay_Private { - std::atomic_bool enabled_value{false}; - std::shared_ptr metrics_worker; - mutable std::mutex identity_mutex; - Performance_Log_Plot_Identity log_identity; - mutable std::mutex clipboard_callback_mutex; - std::function clipboard_callback; - Button_Interaction copy_button_interaction; - Button_Interaction close_button_interaction; - - Performance_Overlay_Private() - : metrics_worker(std::make_shared()) {} - - ~Performance_Overlay_Private() { - if (!metrics_worker) - return; - metrics_worker->cancelled.store(true, std::memory_order_release); - Performance_Frame_Record frame; - while (metrics_worker->metric_queue.try_pop(frame)) {} - } - - void enqueue_frame_metric(std::shared_ptr frame) { - if (!frame) - return; - if (!metrics_worker->metric_queue.try_push(std::move(frame))) { - metrics_worker->dropped_frame_record_count.fetch_add(1, std::memory_order_relaxed); - return; - } - schedule_performance_metrics_worker(metrics_worker); - } - - void set_identity(Performance_Log_Plot_Identity identity) { - std::lock_guard lock(identity_mutex); - log_identity = std::move(identity); - } - - Performance_Log_Plot_Identity identity() const { - std::lock_guard lock(identity_mutex); - return log_identity; - } - - void set_clipboard_callback(std::function callback) { - std::lock_guard lock(clipboard_callback_mutex); - clipboard_callback = std::move(callback); - } - - void copy_to_clipboard(std::string text) const { - std::function callback; - { - std::lock_guard lock(clipboard_callback_mutex); - callback = clipboard_callback; - } - if (callback) - callback(std::move(text)); - } - - void publish_button_states(bool changed) { - if (!changed) - return; - metrics_worker->copy_button_state.store(copy_button_interaction.state(), std::memory_order_release); - metrics_worker->close_button_state.store(close_button_interaction.state(), std::memory_order_release); - metrics_worker->snapshot_rebuild_requested.store(true, std::memory_order_release); - if (enabled_value.load(std::memory_order_acquire)) - schedule_performance_metrics_worker(metrics_worker, true); - } - - void reset_button_states() { - copy_button_interaction.reset(); - close_button_interaction.reset(); - metrics_worker->copy_button_state.store(Button_State::Normal, std::memory_order_release); - metrics_worker->close_button_state.store(Button_State::Normal, std::memory_order_release); - } - - static const char* feedback_outcome_event(Frame_Outcome outcome) { - switch (outcome) { - case Frame_Outcome::Presented: - return "PaintingAdvanced"; - case Frame_Outcome::Superseded: - return "MiddleSuperseded"; - case Frame_Outcome::Render_Acquire_Dropped: - return "RenderAcquireDropped"; - case Frame_Outcome::Publish_Dropped: - return "PublishDropped"; - case Frame_Outcome::Paint_Acquire_Dropped: - return "PaintAcquireDropped"; - case Frame_Outcome::Worker_Budget_Dropped: - return "WorkerBudgetDropped"; - case Frame_Outcome::Cancelled: - return "Cancelled"; - } - return "UnknownOutcome"; - } - - void enqueue_frame_log(std::shared_ptr frame) { - if (!frame) - return; - Performance_Log_Plot_Identity plot = identity(); - performance_log_service().enqueue_frame(plot, frame); - } - - void enqueue_feedback_log(std::shared_ptr event) { - if (!event) - return; - performance_log_service().enqueue_feedback_event(identity(), *event); - } - - static void run_metrics_worker(const std::shared_ptr& state) { - if (!state) - return; - while (!state->cancelled.load(std::memory_order_acquire)) { - state->work_requested.store(false, std::memory_order_release); - bool changed = false; - if (state->reset_requested.exchange(false, std::memory_order_acq_rel)) { - state->aggregate.clear(); - state->scroll_offset.store(0.0, std::memory_order_release); - changed = true; - } - if (state->snapshot_rebuild_requested.exchange(false, std::memory_order_acq_rel)) - changed = true; - if (consume_scroll_commands(*state)) - changed = true; - std::uint64_t dropped = state->dropped_frame_record_count.exchange(0, std::memory_order_acq_rel); - if (dropped) { - state->aggregate.input_capacity_dropped += dropped; - changed = true; - } - Performance_Frame_Record frame; - while (state->metric_queue.try_pop(frame)) { - if (frame) - state->aggregate.update(*frame); - changed = true; - } - if (changed && state->enabled.load(std::memory_order_acquire)) { - state->published_snapshot.store(build_display_snapshot(*state), std::memory_order_release); - if (state->repaint_requested.exchange(false, std::memory_order_acq_rel)) - invoke_update_callback(*state); - } - if (state->work_requested.load(std::memory_order_acquire)) - continue; - state->worker_active.store(false, std::memory_order_release); - if (!state->work_requested.load(std::memory_order_acquire) || state->worker_active.exchange(true, std::memory_order_acq_rel)) - return; - } - state->worker_active.store(false, std::memory_order_release); - } - - static std::shared_ptr build_display_snapshot(Performance_Worker_State& worker_state) { - Performance_Layout_Builder layout; - layout.aggregate = worker_state.aggregate; - layout.field_layout_states = worker_state.field_layout_states; - layout.field_max_value_widths = worker_state.field_max_value_widths; - layout.rebuild_lines(); - auto snapshot = std::make_shared(); - snapshot->content_version = worker_state.next_content_version.fetch_add(1, std::memory_order_acq_rel); - Size viewport = current_viewport(worker_state); - snapshot->viewport_size = viewport; - snapshot->viewport_version = current_viewport_version(worker_state); - snapshot->viewport_rect = RectF{0.0, 0.0, static_cast(viewport.width), static_cast(viewport.height)}; - if (viewport.empty() || layout.lines.empty()) { - snapshot->lines = layout.stripped_lines(); - return snapshot; - } - Performance_Overlay_Render_State state; - { - std::lock_guard lock(worker_state.options_mutex); - static_cast(state) = worker_state.options; - } - Image measure_image(1, 1); - measure_image.fill(Color::transparent()); - Text_Metrics metrics; - { - Canvas measure_canvas(measure_image); - measure_canvas.set_font(state.font); - metrics = measure_canvas.measure_text("0123456789 ABC \xE4\xB8\xAD\xE6\x96\x87"); - if (metrics.line_height() <= 0.0) - metrics = measure_canvas.measure_text("M"); - Button_Style close_style = layout.close_button.style(); - close_style.text = state.warning_color; - close_style.hover_text = state.warning_color; - close_style.pressed_text = state.warning_color; - layout.close_button.set_style(std::move(close_style)); - layout.copy_button.set_state(worker_state.copy_button_state.load(std::memory_order_acquire)); - layout.close_button.set_state(worker_state.close_button_state.load(std::memory_order_acquire)); - layout.copy_button.measure(measure_canvas); - layout.close_button.measure(measure_canvas); - layout.toolbar_height = std::max(layout.copy_button.preferred_height(), layout.close_button.preferred_height()); - layout.seed_static_field_widths(measure_canvas); - layout.build_render_lines(measure_canvas); - } - worker_state.field_layout_states = layout.field_layout_states; - worker_state.field_max_value_widths = layout.field_max_value_widths; - snapshot->lines = layout.display_lines(); - layout.update_content_width(&state, worker_state.maximum_content_width, viewport.width); - double line_height = std::max(1.0, metrics.line_height()); - double panel_width = std::min(layout.content_width, static_cast(std::max(0, viewport.width))); - double height = layout.panel_height(line_height, &state, viewport.height); - if (panel_width <= 0.0 || height <= 0.0) - return snapshot; - double content_height = layout.content_height(line_height, &state); - double text_content_height = line_height * static_cast(layout.render_lines.size()); - double content_viewport_height = layout.content_viewport_height(&state, height); - double max_scroll = std::max(0.0, text_content_height - content_viewport_height); - double scroll = performance_clamp_scroll_offset(worker_state.scroll_offset.load(std::memory_order_acquire), max_scroll); - worker_state.scroll_offset.store(scroll, std::memory_order_release); - layout.panel_rect = RectF{0.0, 0.0, panel_width, height}; - layout.update_toolbar_button_rects(&state); - Image image(static_cast(std::ceil(panel_width)), static_cast(std::ceil(height))); - image.fill(Color::transparent()); - Canvas canvas(image); - canvas.save(); - canvas.set_font(state.font); - canvas.set_brush(Brush{state.background, Brush_Style::Solid}); - canvas.fill_rect(RectF{0.0, 0.0, panel_width, height}); - canvas.set_pen(Pen{state.foreground}); - RectF text_rect{ - static_cast(state.left_margin), - layout.text_top(&state), - std::max(0.0, panel_width - state.left_margin - state.right_margin - state.scroll_bar_width - state.scroll_bar_margin * 2), - std::max(0.0, height - layout.text_top(&state) - state.bottom_margin) - }; - canvas.set_clip_rect(text_rect); - int first_line = std::max(0, static_cast(std::floor(scroll / line_height))); - double y = layout.text_top(&state) - (scroll - static_cast(first_line) * line_height); - layout.text_runs.clear(); - for (int i = first_line; i < static_cast(layout.render_lines.size()) && y < text_rect.bottom(); ++i, y += line_height) { - if (y + line_height < text_rect.y) - continue; - layout.draw_render_line(canvas, layout.render_lines[static_cast(i)], text_rect.x, y, line_height, state); - } - canvas.reset_clip(); - layout.copy_button.draw(canvas); - layout.close_button.draw(canvas); - layout.update_scroll_bar_rects(&state, max_scroll, scroll, content_viewport_height, text_content_height); - if (!layout.scroll_bar_rect.empty()) { - canvas.set_brush(Brush{state.scroll_track, Brush_Style::Solid}); - canvas.fill_rect(layout.scroll_bar_rect); - canvas.set_brush(Brush{state.scroll_thumb, Brush_Style::Solid}); - canvas.fill_rect(layout.scroll_thumb_rect); - } - canvas.restore(); - snapshot->destination_rect = layout.panel_rect; - snapshot->scroll_track_rect = layout.scroll_bar_rect; - snapshot->scroll_thumb_rect = layout.scroll_thumb_rect; - snapshot->copy_button_rect = layout.copy_button.rect(); - snapshot->close_button_rect = layout.close_button.rect(); - snapshot->content_height = content_height; - snapshot->scroll_offset = scroll; - snapshot->max_scroll_offset = max_scroll; - snapshot->line_height = line_height; - snapshot->image = std::move(image); - snapshot->text_runs = layout.text_runs; - worker_state.last_scroll_track_rect = layout.scroll_bar_rect; - worker_state.last_scroll_thumb_rect = layout.scroll_thumb_rect; - worker_state.last_content_viewport_height = content_viewport_height; - worker_state.last_line_height = line_height; - worker_state.maximum_scroll_offset = max_scroll; - return snapshot; - } - -private: - static Size current_viewport(const Performance_Worker_State& worker_state) { - int width = worker_state.viewport_width.load(std::memory_order_acquire); - int height = worker_state.viewport_height.load(std::memory_order_acquire); - if (width > 0 && height > 0) - return {width, height}; - if (!worker_state.aggregate.has_frame) - return {}; - const Frame_Lifecycle_Record& frame = worker_state.aggregate.latest; - return { - static_cast(std::min(frame.pixel_width, static_cast(std::numeric_limits::max()))), - static_cast(std::min(frame.pixel_height, static_cast(std::numeric_limits::max()))) - }; - } - - static std::uint64_t current_viewport_version(const Performance_Worker_State& worker_state) { - std::uint64_t version = worker_state.viewport_version.load(std::memory_order_acquire); - if (version || !worker_state.aggregate.has_frame) - return version; - return worker_state.aggregate.latest.viewport_version; - } - - static void invoke_update_callback(Performance_Worker_State& worker_state) { - std::function callback; - { - std::lock_guard lock(worker_state.update_callback_mutex); - callback = worker_state.update_callback; - } - if (callback) - callback(); - } - - static bool consume_scroll_commands(Performance_Worker_State& worker_state) { - double offset = worker_state.scroll_offset.load(std::memory_order_acquire); - double previous = offset; - double line_height = std::max(1.0, worker_state.last_line_height); - double wheel_delta = performance_atomic_exchange(worker_state.pending_wheel_delta_px); - if (wheel_delta != 0.0) - offset += wheel_delta; - int page_delta = worker_state.pending_page_delta.exchange(0, std::memory_order_acq_rel); - if (page_delta) - offset += static_cast(page_delta) * std::max(line_height, worker_state.last_content_viewport_height * 0.8); - bool dragging = worker_state.drag_active.load(std::memory_order_acquire); - bool drag_end = worker_state.drag_end_requested.exchange(false, std::memory_order_acq_rel); - if (dragging && worker_state.maximum_scroll_offset > 0.0) { - const RectF& track = worker_state.last_scroll_track_rect; - const RectF& thumb = worker_state.last_scroll_thumb_rect; - double travel = track.height - thumb.height; - if (track.height > 0.0 && thumb.height > 0.0 && travel > 0.0) { - double thumb_top = worker_state.drag_position_y.load(std::memory_order_acquire) - - worker_state.drag_anchor_y.load(std::memory_order_acquire); - double ratio = (thumb_top - track.y) / travel; - offset = std::clamp(ratio, 0.0, 1.0) * worker_state.maximum_scroll_offset; - } - } - if (drag_end) - worker_state.drag_active.store(false, std::memory_order_release); - offset = performance_clamp_scroll_offset(offset, worker_state.maximum_scroll_offset); - worker_state.scroll_offset.store(offset, std::memory_order_release); - return wheel_delta != 0.0 || page_delta != 0 || drag_end || std::abs(offset - previous) > 0.01; - } -}; -} // namespace renderive diff --git a/Core/plottable/Performance_Snapshot.h b/Core/plottable/Performance_Snapshot.h deleted file mode 100644 index f1ad6eb..0000000 --- a/Core/plottable/Performance_Snapshot.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -#include "../base/Geometry.h" -#include "../render/Image.h" -#include -#include -#include - -namespace renderive { - -enum class Performance_Text_Role : std::uint8_t { - Section, - Label, - Value, - Unit, - Warning, - Muted -}; - -struct Performance_Display_Text_Run { - std::string text; - Performance_Text_Role role = Performance_Text_Role::Value; - RectF rect; - int column_index{}; -}; - -struct Performance_Display_Snapshot { - Image image; - RectF destination_rect; - RectF viewport_rect; - RectF scroll_track_rect; - RectF scroll_thumb_rect; - RectF copy_button_rect; - RectF close_button_rect; - Size viewport_size; - double content_height{}; - double scroll_offset{}; - double max_scroll_offset{}; - double line_height{}; - std::uint64_t viewport_version{}; - std::uint64_t content_version{}; - std::vector lines; - std::vector text_runs; -}; - -} // namespace renderive diff --git a/Core/plottable/Selection_Rectangle_Overlay.cpp b/Core/plottable/Selection_Rectangle_Overlay.cpp deleted file mode 100644 index c32cf1b..0000000 --- a/Core/plottable/Selection_Rectangle_Overlay.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "Selection_Rectangle_Overlay_p.h" -#include "../base/Memory.h" -namespace renderive { -RENDERIVE_DEFINE_RENDERABLE_BINDING(Selection_Rectangle_Overlay, Selection_Rectangle_Overlay_Private, Selection_Rectangle_Overlay_Render_State, Selection_Rectangle_Overlay_Input_Data, "Selection_Rectangle_Overlay") -Selection_Rectangle_Overlay::Builder::Builder(std::shared_ptr parent, std::shared_ptr key_axis, std::shared_ptr value_axis) { - ASSERT(parent && parent->attached_to_plot(), "Selection_Rectangle_Overlay build error! parent invalid"); - ASSERT(key_axis && value_axis, "Selection_Rectangle_Overlay build error! axis == nullptr"); - ASSERT(key_axis->attached_to_plot(), "Selection_Rectangle_Overlay build error! key_axis->plot == nullptr"); - ASSERT(key_axis->shares_plot_with(*value_axis), "Selection_Rectangle_Overlay build error! key_axis->plot != value_axis->plot"); - ASSERT(parent->shares_plot_with(*key_axis), "Selection_Rectangle_Overlay build error! parent->plot != key_axis->plot"); - ASSERT(key_axis->orientation() != value_axis->orientation(), "Selection_Rectangle_Overlay build error! axes must be orthogonal"); - this->parent = parent; - this->domain_axis = key_axis; - this->value_axis = value_axis; -} -std::shared_ptr Selection_Rectangle_Overlay::Builder::build() { - auto ret = renderive::make_shared(); - ret->init(parent); - Selection_Rectangle_Overlay_Private* pd = ret->d(); - Render_Edit_Lease edit(pd); - Selection_Rectangle_Overlay_Render_State* sc = edit.operator->(); - sc->horizontal_axis = domain_axis; - sc->vertical_axis = value_axis; - sc->label_font = font; - sc->label_pen = font_pen; - sc->selection_brush = rect_brush; - sc->selection_border_pen = border_pen; - return ret; -} -void Selection_Rectangle_Overlay::set_horizontal_axis(std::shared_ptr axis) { - Selection_Rectangle_Overlay_Private* pd = d(); - Render_Edit_Lease edit(pd); - edit->horizontal_axis = std::weak_ptr(axis); - pd->invalidate_interaction_mapping(); -} -void Selection_Rectangle_Overlay::set_vertical_axis(std::shared_ptr axis) { - Selection_Rectangle_Overlay_Private* pd = d(); - Render_Edit_Lease edit(pd); - edit->vertical_axis = std::weak_ptr(axis); - pd->invalidate_interaction_mapping(); -} -Font Selection_Rectangle_Overlay::label_font() { - return d()->edit_state_value(&Selection_Rectangle_Overlay_Render_State::label_font); -} -Pen Selection_Rectangle_Overlay::label_pen() { - return d()->edit_state_value(&Selection_Rectangle_Overlay_Render_State::label_pen); -} -Brush Selection_Rectangle_Overlay::selection_brush() { - return d()->edit_state_value(&Selection_Rectangle_Overlay_Render_State::selection_brush); -} -Pen Selection_Rectangle_Overlay::selection_border_pen() { - return d()->edit_state_value(&Selection_Rectangle_Overlay_Render_State::selection_border_pen); -} -void Selection_Rectangle_Overlay::set_label_font(const Font& font) { - Render_Edit_Lease edit(d()); - edit->label_font = font; -} -void Selection_Rectangle_Overlay::set_label_pen(const Pen& pen) { - Render_Edit_Lease edit(d()); - edit->label_pen = pen.color.a != 0 ? pen : Pen{.style = Line_Style::None}; -} -void Selection_Rectangle_Overlay::set_selection_brush(const Brush& brush) { - Render_Edit_Lease edit(d()); - edit->selection_brush = brush.color.a != 0 ? brush : Brush{}; -} -void Selection_Rectangle_Overlay::set_selection_border_pen(const Pen& pen) { - Render_Edit_Lease edit(d()); - edit->selection_border_pen = pen.color.a != 0 ? pen : Pen{.style = Line_Style::None}; -} -std::vector Selection_Rectangle_Overlay::selected_regions() { - return d()->edit_state_value(&Selection_Rectangle_Overlay_Render_State::rects); -} -void Selection_Rectangle_Overlay::clear_selected_regions() { - Render_Edit_Lease edit(d()); - edit->rects.clear(); - d()->active.store(false, std::memory_order_release); -} -} // namespace renderive diff --git a/Core/plottable/Selection_Rectangle_Overlay.h b/Core/plottable/Selection_Rectangle_Overlay.h deleted file mode 100644 index 9d8f06e..0000000 --- a/Core/plottable/Selection_Rectangle_Overlay.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once -#include -#include "../base/Color.h" -#include "../base/Geometry.h" -#include "../base/Style.h" -#include "../base/Text.h" -#include "global.h" -namespace renderive { -struct Selection_Rectangle_Overlay_Private; -inline Brush default_selection_rectangle_overlay_brush() { - return Brush{Color{0, 0, 255, 50}, Brush_Style::Solid}; -} -inline Pen default_selection_rectangle_overlay_pen() { - return Pen{Color::white(), 1.0, Line_Style::Dash}; -} -/// @brief 多区域框选图元。 -class LIB_DECL Selection_Rectangle_Overlay : public Renderable { -public: - /// @brief 创建多区域框选图元。 - Selection_Rectangle_Overlay(); - /// @brief 设置水平坐标轴。 - void set_horizontal_axis(std::shared_ptr axis); - /// @brief 设置垂直坐标轴。 - void set_vertical_axis(std::shared_ptr axis); - /// @brief 返回标注字体。 - Font label_font(); - /// @brief 返回标注画笔。 - Pen label_pen(); - /// @brief 返回矩形填充刷。 - Brush selection_brush(); - /// @brief 返回矩形边框画笔。 - Pen selection_border_pen(); - /// @brief 设置标注字体。 - void set_label_font(const Font& font); - /// @brief 设置标注画笔。 - void set_label_pen(const Pen& pen); - /// @brief 设置矩形填充刷。 - void set_selection_brush(const Brush& brush); - /// @brief 设置矩形边框画笔。 - void set_selection_border_pen(const Pen& pen); - std::vector selected_regions(); - void clear_selected_regions(); - struct Builder; -private: - Selection_Rectangle_Overlay_Private* d(); - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; -}; -/// @brief 多区域框选构造器。 -struct LIB_DECL Selection_Rectangle_Overlay::Builder { - SETTER(Font, font, Font()) - SETTER(Pen, font_pen, Pen{.color = Color::white()}) - SETTER(Brush, rect_brush, default_selection_rectangle_overlay_brush()) - SETTER(Pen, border_pen, default_selection_rectangle_overlay_pen()) - /// @brief 创建多区域框选构造器。 - Builder(std::shared_ptr parent, std::shared_ptr key_axis, std::shared_ptr value_axis); - /// @brief 构建并返回多区域框选对象。 - std::shared_ptr build(); - std::weak_ptr domain_axis; - std::weak_ptr value_axis{}; - std::shared_ptr parent{}; -}; -} // namespace renderive diff --git a/Core/plottable/Selection_Rectangle_Overlay_p.h b/Core/plottable/Selection_Rectangle_Overlay_p.h deleted file mode 100644 index 8385f05..0000000 --- a/Core/plottable/Selection_Rectangle_Overlay_p.h +++ /dev/null @@ -1,189 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include "../Axis/Abs_Axis_p.h" -#include "../event/Event.h" -#include "../event/Key_Event.h" -#include "../event/Pointer_Event.h" -#include "../base/String_Format.h" -#include "../render/Canvas.h" -#include "Selection_Rectangle_Overlay.h" -namespace renderive { -struct Selection_Rectangle_Overlay_Render_State : Render_State { - std::vector rects; - Font label_font; - Pen selection_border_pen, label_pen = Pen{Color::white()}; - Brush selection_brush = Brush{Color{0, 0, 255, 50}, Brush_Style::Solid}; - std::weak_ptr horizontal_axis{}, vertical_axis{}; - Selection_Rectangle_Overlay_Render_State() { - selection_border_pen.style = Line_Style::Dash; - selection_border_pen.color = Color::white(); - } -}; -struct Selection_Rectangle_Overlay_Input_Data : Input_Data {}; -struct Selection_Rectangle_Overlay_Private : Typed_Render_Data, Hit_Testable, Pointer_Interactive, Key_Press_Interactive { - std::atomic_bool active{false}; - std::mutex interaction_mapping_mutex; - Axis_Mapping_2D interaction_mapping_value{}; - bool interaction_mapping_valid{}; - bool interaction_mapping(Axis_Mapping_2D& mapping) { - std::lock_guard lock(interaction_mapping_mutex); - if (!interaction_mapping_valid) - return false; - mapping = interaction_mapping_value; - return true; - } - void publish_interaction_mapping(const Axis_Mapping_2D& mapping) { - std::lock_guard lock(interaction_mapping_mutex); - interaction_mapping_value = mapping; - interaction_mapping_valid = true; - } - void invalidate_interaction_mapping() { - std::lock_guard lock(interaction_mapping_mutex); - interaction_mapping_valid = false; - } - bool select_test(const PointF& pos) override { - Axis_Mapping_2D mapping; - if (!interaction_mapping(mapping)) - return false; - PointF first = mapping.map(mapping.domain.coord_range.origin, mapping.value.coord_range.origin); - PointF second = mapping.map(mapping.domain.coord_range.target, mapping.value.coord_range.target); - double rx = std::min(first.x, second.x); - double ry = std::min(first.y, second.y); - double rw = std::max(first.x, second.x) - rx; - double rh = std::max(first.y, second.y) - ry; - return RectF{rx, ry, rw, rh}.contains(pos); - } - void mouse_press_event(const Pointer_Event& event) override { - if (event.button != Mouse_Button::Left) - return; - Render_Edit_Lease edit(this); - auto horizontal_axis = edit->horizontal_axis.lock(); - auto vertical_axis = edit->vertical_axis.lock(); - if (!horizontal_axis || !vertical_axis) - return; - Axis_Mapping_2D mapping; - if (!interaction_mapping(mapping)) - return; - std::vector& rect_list = edit->rects; - if (!static_cast(event.modifiers & Keyboard_Modifier::Ctrl)) { - rect_list.clear(); - } - else { - int n = static_cast(rect_list.size()); - for (int i = n - 1; i >= 0; i--) { - auto& rect = rect_list[i]; - if (std::abs(rect.width) < 1e-12 || std::abs(rect.height) < 1e-12) - rect_list.erase(rect_list.begin() + i); - } - } - PointF screen_point{static_cast(event.position.x), static_cast(event.position.y)}; - double x = mapping.domain.pixel_to_coord(mapping.domain_pixel(screen_point)); - double y = mapping.value.pixel_to_coord(mapping.value_pixel(screen_point)); - rect_list.push_back(RectF{x, y, 0.0, 0.0}); - active.store(true, std::memory_order_release); - event.accept(); - } - void mouse_move_event(const Pointer_Event& event) override { - if (!active.load(std::memory_order_acquire)) - return; - if (!(event.buttons & 1)) - return; - Render_Edit_Lease edit(this); - auto horizontal_axis = edit->horizontal_axis.lock(); - auto vertical_axis = edit->vertical_axis.lock(); - if (!horizontal_axis || !vertical_axis) - return; - Axis_Mapping_2D mapping; - if (!interaction_mapping(mapping)) - return; - std::vector& rect_list = edit->rects; - if (rect_list.empty()) - return; - PointF screen_point{static_cast(event.position.x), static_cast(event.position.y)}; - double x = mapping.domain.pixel_to_coord(mapping.domain_pixel(screen_point)); - double y = mapping.value.pixel_to_coord(mapping.value_pixel(screen_point)); - rect_list.back().width = x - rect_list.back().x; - rect_list.back().height = y - rect_list.back().y; - event.accept(); - } - void mouse_release_event(const Pointer_Event& event) override { - if (event.button != Mouse_Button::Left || !active.load(std::memory_order_acquire)) - return; - active.store(false, std::memory_order_release); - Render_Edit_Lease edit(this); - if (!edit->rects.empty() && - (std::abs(edit->rects.back().width) < 1e-12 || std::abs(edit->rects.back().height) < 1e-12)) - edit->rects.pop_back(); - event.accept(); - } - void key_press_event(const Key_Event& event) override { - if (event.key != Key::Escape || !active.load(std::memory_order_acquire)) - return; - Render_Edit_Lease edit(this); - active.store(false, std::memory_order_release); - std::vector& rect_list = edit->rects; - if (rect_list.empty()) - return; - rect_list.pop_back(); - event.accept(); - } - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Selection_Rectangle_Overlay_Render_State* s = render_state(frame_view); - if (!s) - return; - auto horizontal_axis = s->horizontal_axis.lock(); - auto vertical_axis = s->vertical_axis.lock(); - if (!horizontal_axis || !vertical_axis) - return; - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(horizontal_axis.get(), vertical_axis.get(), snapshot); - publish_interaction_mapping(mapping); - std::vector draw_rect_list; - std::vector coord_rect_list; - for (RectF r : s->rects) { - if (std::abs(r.width) < 1e-12 || std::abs(r.height) < 1e-12) - continue; - coord_rect_list.push_back(r.normalized()); - PointF first = mapping.map(r.x, r.y); - PointF second = mapping.map(r.right(), r.bottom()); - RectF dr{first.x, first.y, second.x - first.x, second.y - first.y}; - draw_rect_list.push_back(dr.normalized()); - } - canvas.save(); - canvas.set_font(Axis_Render_Access::unit_text_font(horizontal_axis.get(), snapshot)); - Text_Metrics xfm = canvas.measure_text("M"); - canvas.set_font(Axis_Render_Access::unit_text_font(vertical_axis.get(), snapshot)); - Text_Metrics yfm = canvas.measure_text("M"); - canvas.set_pen(s->selection_border_pen); - canvas.set_brush(s->selection_brush); - for (auto& rect : draw_rect_list) - canvas.draw_rect(rect); - canvas.set_pen(s->label_pen); - canvas.set_font(s->label_font); - int n = static_cast(draw_rect_list.size()); - for (int i = 0; i < n; ++i) { - RectF& rect = draw_rect_list[i]; - RectF& coord_rect = coord_rect_list[i]; - double offset_x = coord_rect.right() - coord_rect.x; - double offset_y = coord_rect.bottom() - coord_rect.y; - std::string text1 = "offset_x == " + format_number(offset_x, Number_Format{Number_Notation::General, 6}) - + Axis_Render_Access::unit_text(horizontal_axis.get(), snapshot); - std::string text2 = "offset_y == " + format_number(offset_y, Number_Format{Number_Notation::General, 6}) - + Axis_Render_Access::unit_text(vertical_axis.get(), snapshot); - double width1 = canvas.measure_text(text1).width + 32.0; - double width2 = canvas.measure_text(text2).width + 32.0; - double rect1_x = rect.x + rect.width / 2.0 - width1 / 2.0; - double rect2_x = rect.x + rect.width / 2.0 - width2 / 2.0; - RectF rect1{rect1_x, rect.y, width1, xfm.line_height()}; - RectF rect2{rect2_x, rect1.bottom(), width2, yfm.line_height()}; - canvas.draw_text(rect1, Text_Align::Center, text1); - canvas.draw_text(rect2, Text_Align::Center, text2); - } - canvas.restore(); - } -}; -} // namespace renderive diff --git a/Core/plottable/Spectrum.cpp b/Core/plottable/Spectrum.cpp deleted file mode 100644 index 0879bd7..0000000 --- a/Core/plottable/Spectrum.cpp +++ /dev/null @@ -1,641 +0,0 @@ -#include -#include -#include "../base/Frame_Memory.h" -#include "../base/Memory.h" -#include "../primitive/Curve.h" -#include "../primitive/Curve_Utils_p.h" -#include "../primitive/Label.h" -#include "../primitive/Marker.h" -#include "Spectrum_p.h" -namespace renderive { -RENDERIVE_DEFINE_RENDERABLE_BINDING(Spectrum, Spectrum_Private, Spectrum_Render_State, Spectrum_Input_Data, "Spectrum") -std::shared_ptr Spectrum::frequency_axis() { - return d()->edit_state_value(&Spectrum_Render_State::frequency_axis).lock(); -} -void Spectrum::set_frequency_axis(std::shared_ptr value) { - d()->set_state_value(&Spectrum_Render_State::frequency_axis, std::weak_ptr(value)); -} -std::shared_ptr Spectrum::power_axis() { - return d()->edit_state_value(&Spectrum_Render_State::power_axis).lock(); -} -void Spectrum::set_power_axis(std::shared_ptr value) { - d()->set_state_value(&Spectrum_Render_State::power_axis, std::weak_ptr(value)); -} -int Spectrum::frequency_point_size() { - return d()->edit_state_value(&Spectrum_Render_State::frequency_point_size); -} -Range Spectrum::frequency_range() { - return d()->edit_state_value(&Spectrum_Render_State::frequency_range); -} -double Spectrum::center_frequency() { - return d()->edit_state_value(&Spectrum_Render_State::center_frequency); -} -void Spectrum::set_center_frequency(double value) { - d()->set_state_value(&Spectrum_Render_State::center_frequency, value); -} -Range Spectrum::sweep_frequency_range() { - return d()->edit_state_value(&Spectrum_Render_State::sweep_frequency_range); -} -void Spectrum::set_sweep_frequency_range(Range value) { - if (value.length() == 0.0) - return; - d()->set_state_value(&Spectrum_Render_State::sweep_frequency_range, std::move(value)); -} -bool Spectrum::max_hold_visible() { - return d()->edit_state_value(&Spectrum_Render_State::max_hold_visible); -} -void Spectrum::set_max_hold_visible(bool value) { - d()->set_state_value(&Spectrum_Render_State::max_hold_visible, value); -} -bool Spectrum::min_hold_visible() { - return d()->edit_state_value(&Spectrum_Render_State::min_hold_visible); -} -void Spectrum::set_min_hold_visible(bool value) { - d()->set_state_value(&Spectrum_Render_State::min_hold_visible, value); -} -bool Spectrum::max_marker_visible() { - return d()->edit_state_value(&Spectrum_Render_State::max_marker_visible); -} -void Spectrum::set_max_marker_visible(bool value) { - d()->set_state_value(&Spectrum_Render_State::max_marker_visible, value); -} -bool Spectrum::use_min_marker() { - return d()->edit_state_value(&Spectrum_Render_State::use_min_marker); -} -void Spectrum::set_use_min_marker(bool value) { - d()->set_state_value(&Spectrum_Render_State::use_min_marker, value); -} -bool Spectrum::sweep_region_visible() { - return d()->edit_state_value(&Spectrum_Render_State::sweep_region_visible); -} -void Spectrum::set_sweep_region_visible(bool value) { - d()->set_state_value(&Spectrum_Render_State::sweep_region_visible, value); -} -bool Spectrum::visible_range_only() { - return d()->edit_state_value(&Spectrum_Render_State::visible_range_only); -} -void Spectrum::set_visible_range_only(bool value) { - d()->set_state_value(&Spectrum_Render_State::visible_range_only, value); -} -Line_Interpolation_Mode Spectrum::interpolation_mode() { - return d()->edit_state_value(&Spectrum_Render_State::interpolation_mode); -} -void Spectrum::set_interpolation_mode(Line_Interpolation_Mode value) { - d()->set_state_value(&Spectrum_Render_State::interpolation_mode, value); -} -Line_Sampling_Strategy Spectrum::sample_mode() { - return d()->edit_state_value(&Spectrum_Render_State::sampling_strategy); -} -void Spectrum::set_sample_mode(Line_Sampling_Strategy value) { - d()->set_state_value(&Spectrum_Render_State::sampling_strategy, value); -} -Brush Spectrum::max_brush() { - return d()->edit_state_value(&Spectrum_Render_State::max_brush); -} -void Spectrum::set_max_brush(Brush value) { - d()->set_state_value(&Spectrum_Render_State::max_brush, std::move(value)); -} -Brush Spectrum::current_brush() { - return d()->edit_state_value(&Spectrum_Render_State::current_brush); -} -void Spectrum::set_current_brush(Brush value) { - d()->set_state_value(&Spectrum_Render_State::current_brush, std::move(value)); -} -Brush Spectrum::min_brush() { - return d()->edit_state_value(&Spectrum_Render_State::min_brush); -} -void Spectrum::set_min_brush(Brush value) { - d()->set_state_value(&Spectrum_Render_State::min_brush, std::move(value)); -} -Pen Spectrum::max_pen() { - return d()->edit_state_value(&Spectrum_Render_State::max_pen); -} -void Spectrum::set_max_pen(Pen value) { - d()->set_state_value(&Spectrum_Render_State::max_pen, std::move(value)); -} -Pen Spectrum::current_pen() { - return d()->edit_state_value(&Spectrum_Render_State::cur_pen); -} -void Spectrum::set_current_pen(Pen value) { - d()->set_state_value(&Spectrum_Render_State::cur_pen, std::move(value)); -} -Pen Spectrum::min_pen() { - return d()->edit_state_value(&Spectrum_Render_State::min_pen); -} -void Spectrum::set_min_pen(Pen value) { - d()->set_state_value(&Spectrum_Render_State::min_pen, std::move(value)); -} -Pen Spectrum::selected_marker_pen() { - return d()->edit_state_value(&Spectrum_Render_State::selected_marker_pen); -} -void Spectrum::set_selected_marker_pen(Pen value) { - d()->set_state_value(&Spectrum_Render_State::selected_marker_pen, std::move(value)); -} -Pen Spectrum::marker_pen() { - return d()->edit_state_value(&Spectrum_Render_State::marker_pen); -} -void Spectrum::set_marker_pen(Pen value) { - d()->set_state_value(&Spectrum_Render_State::marker_pen, std::move(value)); -} -Pen Spectrum::middle_frequency_pen() { - return d()->edit_state_value(&Spectrum_Render_State::middle_frequency_pen); -} -void Spectrum::set_middle_frequency_pen(Pen value) { - d()->set_state_value(&Spectrum_Render_State::middle_frequency_pen, std::move(value)); -} -Brush Spectrum::sweep_region_brush() { - return d()->edit_state_value(&Spectrum_Render_State::sweep_region_brush); -} -void Spectrum::set_sweep_region_brush(Brush value) { - d()->set_state_value(&Spectrum_Render_State::sweep_region_brush, std::move(value)); -} -void Spectrum::set_frequency_point_size(int frequency_point_size) { - Render_Edit_Lease edit(d()); - edit->frequency_point_size = std::max(1, frequency_point_size); -} -void Spectrum::set_frequency_range(Range frequency_range) { - if (frequency_range.length() == 0.0) - return; - Render_Edit_Lease edit(d()); - edit->frequency_range = frequency_range; -} -Spectrum::Builder::Builder(std::shared_ptr parent, std::shared_ptr frequency_axis, std::shared_ptr power_axis) { - ASSERT(parent, "Spectrum build error! parent == nullptr"); - ASSERT(frequency_axis && power_axis, "Spectrum build error! axis == nullptr"); - ASSERT(frequency_axis->attached_to_plot(), "Spectrum build error! frequency_axis->plot == nullptr"); - ASSERT(frequency_axis->shares_plot_with(*power_axis), "Spectrum build error! frequency_axis->plot != power_axis->plot"); - ASSERT(parent->shares_plot_with(*frequency_axis), "Spectrum build error! parent->plot != axis->plot"); - ASSERT(frequency_axis->orientation() != power_axis->orientation(), "Spectrum build error! axes must be orthogonal"); - this->parent = parent; - this->frequency_axis = frequency_axis; - this->power_axis = power_axis; -} -std::shared_ptr Spectrum::Builder::build() { - auto ret = renderive::make_shared(); - ret->init(parent); - Spectrum_Private* pd = ret->d(); - Render_Edit_Lease edit(pd); - Spectrum_Render_State* sc = edit.operator->(); - PROP_R(std::weak_ptr, frequency_axis) - PROP_R(std::weak_ptr, power_axis); - PROP_R(int, frequency_point_size); - PROP_R(Range, frequency_range); - PROP_R(double, center_frequency); - PROP_R(Range, sweep_frequency_range); - PROP_R(bool, max_hold_visible); - PROP_R(bool, min_hold_visible); - PROP_R(bool, max_marker_visible); - PROP_R(bool, use_min_marker); - PROP_R(bool, sweep_region_visible); - PROP_R(bool, visible_range_only); - PROP_R(Line_Interpolation_Mode, interpolation_mode); - PROP_R(Line_Sampling_Strategy, sampling_strategy); - PROP_R(Brush, max_brush); - PROP_R(Brush, current_brush); - PROP_R(Brush, min_brush); - PROP_R(Pen, max_pen); - PROP_R(Pen, cur_pen); - PROP_R(Pen, min_pen); - PROP_R(Pen, selected_marker_pen); - PROP_R(Pen, marker_pen); - PROP_R(Pen, middle_frequency_pen); - PROP_R(Brush, sweep_region_brush); - sc->frequency_point_size = std::max(1, sc->frequency_point_size); - if (sc->frequency_range.length() == 0.0) - sc->frequency_range = {0.0, 1.0}; - if (sc->sweep_frequency_range.length() == 0.0) - sc->sweep_frequency_range = {0.0, 1.0}; - return ret; -} -void Spectrum_Private::update_frequency_point_size(int frequency_point_size, Range frequency_range) { - if (frequency_point_size <= 0) { - cache_frequency_point_size = frequency_point_size; - cache_frequency_range = frequency_range; - has_power_data = false; - max_powers.clear(); - cur_powers.clear(); - min_powers.clear(); - frequency.clear(); - return; - } - cache_frequency_point_size = frequency_point_size; - cache_frequency_range = frequency_range; - has_power_data = false; - max_powers.resize(frequency_point_size); - cur_powers.resize(frequency_point_size); - min_powers.resize(frequency_point_size); - frequency.resize(frequency_point_size); - if (frequency_point_size == 1) { - frequency[0] = frequency_range.origin; - return; - } - double cur = frequency_range.origin; - double step = frequency_range.length() / (frequency_point_size - 1); - frequency[0] = frequency_range.origin; - for (int i = 1; i < frequency_point_size; ++i) { - cur += step; - frequency[i] = cur; - } -} -void Spectrum_Private::set_frequency_data(const std::pmr::vector& line_data, const std::pmr::vector* max_data, const std::pmr::vector* min_data, const Spectrum_Render_State* s) { - if (s->frequency_point_size <= 0) - return; - if (line_data.size() != s->frequency_point_size) - return; - if ((max_data && max_data->size() != s->frequency_point_size) || (min_data && min_data->size() != s->frequency_point_size)) - return; - cur_powers.assign(line_data.begin(), line_data.end()); - if (!has_power_data) { - max_powers.assign(cur_powers.begin(), cur_powers.end()); - min_powers.assign(cur_powers.begin(), cur_powers.end()); - has_power_data = true; - } - double min = std::numeric_limits::max(); - double max = std::numeric_limits::lowest(); - int min_index = 0, max_index = 0; - for (int i = 0; i < s->frequency_point_size; ++i) { - double& cur_data = cur_powers[i]; - if (s->max_hold_visible) { - double hold_max = max_data ? max_data->at(i) : cur_data; - if (hold_max > max_powers.at(i)) - max_powers[i] = hold_max; - } - if (s->min_hold_visible) { - double hold_min = min_data ? min_data->at(i) : cur_data; - if (hold_min < min_powers.at(i)) - min_powers[i] = hold_min; - } - if (s->max_marker_visible && cur_data > max) { - max = cur_data; - max_index = i; - } - if (s->use_min_marker && cur_data < min) { - min = cur_data; - min_index = i; - } - } - max_marker_key = frequency[max_index]; - max_marker_value = cur_powers[max_index]; - min_marker_key = frequency[min_index]; - min_marker_value = cur_powers[min_index]; -} -double Spectrum_Private::get_y(double x, bool& ok, Line_Interpolation_Mode mode) { - if (cur_powers.size() < 2 || cache_frequency_range.length() == 0.0 || !cache_frequency_range.contains(x)) { - ok = false; - return -1; - } - double index = coordinate_to_source_index(cache_frequency_range, cur_powers.size(), x); - ok = true; - return interpolate_sample_value(index, cur_powers.size(), mode, [this](int i) { - return cur_powers.at(i); - }); -} -void Spectrum_Private::draw_power_curve(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Spectrum_Render_State* s, const std::pmr::vector& powers, const Pen& pen, const Brush& brush) { - auto frequency_axis = s->frequency_axis.lock(); - auto power_axis = s->power_axis.lock(); - if (!frequency_axis || !power_axis) - return; - if (powers.size() < 2 || s->frequency_range.length() == 0.0) - return; - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(frequency_axis.get(), power_axis.get(), snapshot); - std::pmr::vector points(frame_memory_resource()); - int point_count = static_cast(powers.size()); - if (s->sampling_strategy == Line_Sampling_Strategy::Pixel_Min_Max_Envelope) { - points = Curve_Utils::build_pixel_envelope_points(mapping, s->frequency_range, point_count, point_count, s->visible_range_only, [&powers](int i) { - return powers.at(i); - }); - } - else { - points = Curve_Utils::build_resampled_points(mapping, s->frequency_range, point_count, point_count, s->visible_range_only, s->interpolation_mode, [&powers](int i) { - return powers.at(i); - }); - } - canvas.save(); - Curve_Utils::draw_polyline(&canvas, points, pen); - Curve_Utils::draw_fill_to_value(&canvas, points, mapping, mapping.value.coord_range.target, brush); - canvas.restore(); -} -void Spectrum_Private::update_marker_items(const Spectrum_Render_State* s, const Render_Frame_Snapshot& snapshot) { - auto frequency_axis = s->frequency_axis.lock(); - auto power_axis = s->power_axis.lock(); - prepared_marker_items.clear(); - prepared_label_items.clear(); - if (!frequency_axis || !power_axis) - return; - auto append_marker = [&](double frequency, double power, bool draw_line, bool selected) { - Pen pen = selected ? s->selected_marker_pen : s->marker_pen; - Marker_Item marker; - marker.position = PointF(frequency, power); - marker.radius = 2.0; - marker.pen = pen; - marker.mode = draw_line ? Marker_Mode::Domain_Line : Marker_Mode::Point; - prepared_marker_items.push_back(marker); - std::string frequency_text = std::to_string(frequency) + Axis_Render_Access::unit_text(frequency_axis.get(), snapshot); - std::string power_text = std::to_string(power) + Axis_Render_Access::unit_text(power_axis.get(), snapshot); - Label_Item frequency_label; - frequency_label.position = marker.position; - frequency_label.offset = PointF(0.0, -28.0); - frequency_label.text = frequency_text; - frequency_label.alignment = Text_Align::Horizontal_Center | Text_Align::Bottom; - frequency_label.text_pen = pen; - prepared_label_items.push_back(frequency_label); - Label_Item power_label = frequency_label; - power_label.offset = PointF(0.0, -14.0); - power_label.text = power_text; - prepared_label_items.push_back(power_label); - }; - if (s->max_marker_visible) - append_marker(max_marker_key, max_marker_value, false, false); - if (s->use_min_marker) - append_marker(min_marker_key, min_marker_value, false, false); - for (int i = 0; i < (int)s->marker_list.size(); ++i) { - const Spectrum_Marker& custom_marker = s->marker_list.at(i); - bool ok; - double value = get_y(custom_marker.frequency, ok, s->interpolation_mode); - if (ok) - append_marker(custom_marker.frequency, value, custom_marker.is_line_marker, i == s->cur_select_index); - } -} -void Spectrum_Private::draw_spectrum(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Spectrum_Render_State* s) { - if (s->sweep_region_visible) { - s->draw_axis_rect(canvas, snapshot, s->sweep_frequency_range, s->center_frequency); - } -} -void Spectrum_Private::draw_marker_items(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Spectrum_Render_State* s) { - auto frequency_axis = s->frequency_axis.lock(); - auto power_axis = s->power_axis.lock(); - if (!frequency_axis || !power_axis) - return; - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(frequency_axis.get(), power_axis.get(), snapshot); - canvas.save(); - for (const Marker_Item& item : prepared_marker_items) { - PointF point = mapping.map(item.position); - PointF domain_start = mapping.map(item.position.x, mapping.value.coord_range.origin); - PointF domain_end = mapping.map(item.position.x, mapping.value.coord_range.target); - PointF value_start = mapping.map(mapping.domain.coord_range.origin, item.position.y); - PointF value_end = mapping.map(mapping.domain.coord_range.target, item.position.y); - canvas.set_pen(item.pen); - canvas.set_brush(item.brush); - if (item.mode == Marker_Mode::Point) { - canvas.draw_ellipse(point, item.radius, item.radius); - } - else if (item.mode == Marker_Mode::Domain_Line) { - canvas.draw_line(domain_start, domain_end); - } - else if (item.mode == Marker_Mode::Value_Line) { - canvas.draw_line(value_start, value_end); - } - else { - canvas.draw_line(domain_start, domain_end); - canvas.draw_line(value_start, value_end); - } - } - canvas.restore(); -} -void Spectrum_Private::draw_label_items(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Spectrum_Render_State* s) { - auto frequency_axis = s->frequency_axis.lock(); - auto power_axis = s->power_axis.lock(); - if (!frequency_axis || !power_axis) - return; - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(frequency_axis.get(), power_axis.get(), snapshot); - for (const Label_Item& item : prepared_label_items) { - if (item.text.empty()) - continue; - canvas.save(); - canvas.set_font(item.font); - Text_Metrics tm = canvas.measure_text(item.text); - PointF anchor = mapping.map(item.position); - PointF top_left{anchor.x + item.offset.x, anchor.y + item.offset.y}; - if ((item.alignment & Text_Align::Horizontal_Center) == Text_Align::Horizontal_Center) - top_left.x -= tm.width / 2.0; - else if ((item.alignment & Text_Align::Right) == Text_Align::Right) - top_left.x -= tm.width; - if ((item.alignment & Text_Align::Vertical_Center) == Text_Align::Vertical_Center) - top_left.y -= tm.height / 2.0; - else if ((item.alignment & Text_Align::Bottom) == Text_Align::Bottom) - top_left.y -= tm.height; - RectF rect{top_left.x, top_left.y, tm.width, tm.height}; - if (item.background_brush.style != Brush_Style::None || item.border_pen.style != Line_Style::None) { - canvas.set_pen(item.border_pen); - canvas.set_brush(item.background_brush); - canvas.draw_rect(rect); - } - canvas.set_pen(item.text_pen); - canvas.draw_text(rect, Text_Align::Left | Text_Align::Vertical_Center, item.text); - canvas.restore(); - } -} -void Spectrum_Private::draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) { - auto s = render_state(frame_view); - if (!s) - return; - draw_power_curve(canvas, snapshot, s, cur_powers, s->cur_pen, s->current_brush); - draw_spectrum(canvas, snapshot, s); - if (s->max_hold_visible) - draw_power_curve(canvas, snapshot, s, max_powers, s->max_pen, s->max_brush); - if (s->min_hold_visible) - draw_power_curve(canvas, snapshot, s, min_powers, s->min_pen, s->min_brush); - draw_marker_items(canvas, snapshot, s); - draw_label_items(canvas, snapshot, s); - if (s->can_show_hover(q())) { - auto frequency_axis = s->frequency_axis.lock(); - auto power_axis = s->power_axis.lock(); - if (frequency_axis && power_axis) - s->draw_hover_tooltip(&canvas, frequency_axis.get(), power_axis.get(), q(), &snapshot); - } -} -void Spectrum::update_samples(std::span line_data) { - if (!ok()) - return; - Spectrum_Private* pd = d(); - int frequency_point_size{}; - Range frequency_range{}; - Line_Interpolation_Mode interpolation_mode{}; - bool use_max_line{}; - bool use_min_line{}; - { - Render_State_Lease state(pd); - frequency_point_size = state->frequency_point_size; - frequency_range = state->frequency_range; - interpolation_mode = state->interpolation_mode; - use_max_line = state->max_hold_visible; - use_min_line = state->min_hold_visible; - } - if (line_data.size() != frequency_point_size) - return; - Render_Input_Lease input(pd); - input->push(line_data, frequency_range, interpolation_mode, use_max_line, use_min_line); -} -void Spectrum::update_samples(std::pmr::vector&& line_data) { - if (!ok()) - return; - Spectrum_Private* pd = d(); - int frequency_point_size{}; - Range frequency_range{}; - Line_Interpolation_Mode interpolation_mode{}; - bool use_max_line{}; - bool use_min_line{}; - { - Render_State_Lease state(pd); - frequency_point_size = state->frequency_point_size; - frequency_range = state->frequency_range; - interpolation_mode = state->interpolation_mode; - use_max_line = state->max_hold_visible; - use_min_line = state->min_hold_visible; - } - if (line_data.size() != frequency_point_size) - return; - Render_Input_Lease input(pd); - input->push(std::move(line_data), frequency_range, interpolation_mode, use_max_line, use_min_line); -} -Update_Ticket Spectrum::submit_data(std::span line_data) { - auto update_state = make_update_state(); - if (!ok()) { - update_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - return Update_Ticket(update_state); - } - Spectrum_Private* pd = d(); - int frequency_point_size{}; - Range frequency_range{}; - Line_Interpolation_Mode interpolation_mode{}; - bool use_max_line{}; - bool use_min_line{}; - { - Render_State_Lease state(pd); - frequency_point_size = state->frequency_point_size; - frequency_range = state->frequency_range; - interpolation_mode = state->interpolation_mode; - use_max_line = state->max_hold_visible; - use_min_line = state->min_hold_visible; - } - if (line_data.size() != frequency_point_size) { - update_state->complete_all(std::make_error_code(std::errc::invalid_argument), Update_Outcome::Cancelled); - return Update_Ticket(update_state); - } - { - Render_Input_Lease input(pd); - input->push(line_data, frequency_range, interpolation_mode, use_max_line, use_min_line); - input.set_completion(update_state); - } - update_state->complete_until(Update_Stage::Input_Released); - return Update_Ticket(update_state); -} -Update_Ticket Spectrum::submit_data(std::pmr::vector&& line_data) { - auto update_state = make_update_state(); - if (!ok()) { - update_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - return Update_Ticket(update_state); - } - Spectrum_Private* pd = d(); - int frequency_point_size{}; - Range frequency_range{}; - Line_Interpolation_Mode interpolation_mode{}; - bool use_max_line{}; - bool use_min_line{}; - { - Render_State_Lease state(pd); - frequency_point_size = state->frequency_point_size; - frequency_range = state->frequency_range; - interpolation_mode = state->interpolation_mode; - use_max_line = state->max_hold_visible; - use_min_line = state->min_hold_visible; - } - if (line_data.size() != frequency_point_size) { - update_state->complete_all(std::make_error_code(std::errc::invalid_argument), Update_Outcome::Cancelled); - return Update_Ticket(update_state); - } - { - Render_Input_Lease input(pd); - input->push(std::move(line_data), frequency_range, interpolation_mode, use_max_line, use_min_line); - input.set_completion(update_state); - } - update_state->complete_until(Update_Stage::Input_Released); - return Update_Ticket(update_state); -} -void Spectrum::add_custom_marker(double frequency) { - Render_Edit_Lease edit(d()); - edit->marker_list.push_back(Spectrum_Marker{frequency, false}); -} -void Spectrum::add_custom_line_marker(double frequency) { - Render_Edit_Lease edit(d()); - edit->marker_list.push_back(Spectrum_Marker{frequency, true}); -} -void Spectrum::remove_custom_marker(double frequency) { - Render_Edit_Lease edit(d()); - for (auto marker = edit->marker_list.begin(); marker != edit->marker_list.end(); ++marker) { - if (std::abs(marker->frequency - frequency) < 1e-12) { - edit->marker_list.erase(marker); - break; - } - } -} -void Spectrum::remove_selected_marker() { - Render_Edit_Lease edit(d()); - if (edit->cur_select_index < 0 || edit->cur_select_index >= (int)edit->marker_list.size()) - return; - edit->marker_list.erase(edit->marker_list.begin() + edit->cur_select_index); - edit->cur_select_index = -1; -} -void Spectrum::clear_custom_markers() { - Render_Edit_Lease edit(d()); - edit->marker_list.clear(); -} -double Spectrum::marker_frequency(int mark_index) { - Render_State_Lease state(d()); - if (!state || mark_index < 0 || mark_index >= static_cast(state->marker_list.size())) - return -1; - return state->marker_list[static_cast(mark_index)].frequency; -} -void Spectrum::set_marker_frequency(int marker_index, double frequency) { - Render_Edit_Lease edit(d()); - auto& markers = edit->marker_list; - if (marker_index < 0 || marker_index >= (int)markers.size()) - return; - markers[marker_index].frequency = frequency; -} -void Spectrum::set_current_marker_frequency(double frequency) { - Render_Edit_Lease edit(d()); - edit->set_cur_frequency(frequency); -} -int Spectrum::selectable_line_marker_count() { - Render_State_Lease state(d()); - return state ? static_cast(state->marker_list.size()) : 0; -} -int Spectrum::selected_marker_index() { - return d()->edit_state_value(&Spectrum_Render_State::cur_select_index); -} -void Spectrum::set_selected_marker_index(int marker_index) { - Render_Edit_Lease edit(d()); - edit->set_select_index(marker_index); -} -void Spectrum::select_next_marker() { - Render_Edit_Lease edit(d()); - edit->select_next_marker(); -} -void Spectrum::select_previous_marker() { - Render_Edit_Lease edit(d()); - edit->select_prev_marker(); -} -void Spectrum::clear_marker_selection() { - Render_Edit_Lease edit(d()); - edit->un_select(); -} -double Spectrum::power_at(double frequency, bool& ok) { - Spectrum_Private* pd = d(); - Render_Input_Read_Lease input(pd); - if (!input || !input->has_data) { - ok = false; - return -1.0; - } - const auto& powers = input->cur_data; - if (powers.size() < 2 || input->frequency_range.length() == 0.0 || !input->frequency_range.contains(frequency)) { - ok = false; - return -1.0; - } - double index = coordinate_to_source_index(input->frequency_range, powers.size(), frequency); - ok = true; - return interpolate_sample_value(index, powers.size(), input->interpolation_mode, [&powers](int i) { - return powers.at(i); - }); -} -} // namespace renderive diff --git a/Core/plottable/Spectrum.h b/Core/plottable/Spectrum.h deleted file mode 100644 index e17232f..0000000 --- a/Core/plottable/Spectrum.h +++ /dev/null @@ -1,194 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include "../architecture/Update_Completion.h" -#include "../base/Color.h" -#include "../base/Memory.h" -#include "../base/Style.h" -#include "../primitive/Curve.h" -#include "Hover_Tooltip.h" -#include "Interpolation.h" -namespace renderive { -struct Spectrum_Private; -class Label; -class Marker; -/// @brief 单条频谱线图元,支持可见范围裁剪和多种线插值策略。 -class LIB_DECL Spectrum : public Renderable, public Hover_Tooltip_Mixin { -public: - /// @brief 创建频谱线图元。 - Spectrum(); - /// @brief 绑定频率轴。 - PROP(std::shared_ptr, frequency_axis) - /// @brief 绑定功率轴。 - PROP(std::shared_ptr, power_axis); - /// @brief 频率采样点数。 - PROP(int, frequency_point_size); - /// @brief 频率数据范围。 - PROP(Range, frequency_range); - /// @brief 中心扫频频率。 - PROP(double, center_frequency); - /// @brief 扫频高亮频率范围。 - PROP(Range, sweep_frequency_range); - /// @brief 是否绘制最大保持线。 - PROP(bool, max_hold_visible); - /// @brief 是否绘制最小保持线。 - PROP(bool, min_hold_visible); - /// @brief 是否显示最大值标记。 - bool max_marker_visible(); - void set_max_marker_visible(bool); - /// @brief 是否显示最小值标记。 - PROP(bool, use_min_marker); - /// @brief 是否显示扫频高亮区域。 - bool sweep_region_visible(); - void set_sweep_region_visible(bool); - /// @brief 是否只生成当前可见频率范围。 - PROP(bool, visible_range_only); - /// @brief 折线插值模式。 - PROP(Line_Interpolation_Mode, interpolation_mode); - /// @brief 采样降采样模式。 - PROP(Line_Sampling_Strategy, sample_mode); - /// @brief 最大保持线填充刷。 - PROP(Brush, max_brush); - /// @brief 当前频谱线填充刷。 - Brush current_brush(); - void set_current_brush(Brush); - /// @brief 最小保持线填充刷。 - PROP(Brush, min_brush); - /// @brief 最大保持线画笔。 - PROP(Pen, max_pen); - /// @brief 当前频谱线画笔。 - Pen current_pen(); - void set_current_pen(Pen); - /// @brief 最小保持线画笔。 - PROP(Pen, min_pen); - /// @brief 选中标记画笔。 - Pen selected_marker_pen(); - void set_selected_marker_pen(Pen); - /// @brief 普通标记画笔。 - PROP(Pen, marker_pen); - /// @brief 中心频率线画笔。 - PROP(Pen, middle_frequency_pen); - /// @brief 扫频区域填充刷。 - Brush sweep_region_brush(); - void set_sweep_region_brush(Brush); - /// @brief 写入一帧频谱线数据。 - void update_samples(std::span line_data); - /// @brief 移动写入一帧频谱线数据。 - void update_samples(std::pmr::vector&& line_data); - /// @brief 提交一帧频谱线数据并返回完成票据。 - Update_Ticket submit_data(std::span line_data); - /// @brief 移动提交一帧频谱线数据并返回完成票据。 - Update_Ticket submit_data(std::pmr::vector&& line_data); - /// @brief 用 writer 填充并写入一帧频谱线数据。 - template - void write_data(std::size_t count, Writer&& writer) { - std::pmr::vector values(memory_resource(Memory_Domain::Spectrum)); - values.resize(count); - std::forward(writer)(std::span(values.data(), values.size())); - update_samples(std::move(values)); - } - /// 查询指定频率位置的功率值。 - double power_at(double frequency, bool& ok); - /// 添加自定义频率标记。 - void add_custom_marker(double frequency); - /// 添加自定义线标记。 - void add_custom_line_marker(double frequency); - /// 删除指定频率附近的自定义标记。 - void remove_custom_marker(double frequency); - /// 删除当前选中的标记。 - void remove_selected_marker(); - /// 清空所有自定义标记。 - void clear_custom_markers(); - /// 返回可选择线标记数量。 - int selectable_line_marker_count(); - /// 返回当前选中线标记索引。 - int selected_marker_index(); - /// 设置当前选中线标记索引。 - void set_selected_marker_index(int marker_index); - /// 选择下一个线标记。 - void select_next_marker(); - /// 取消当前线标记选择。 - void clear_marker_selection(); - /// 选择上一个线标记。 - void select_previous_marker(); - /// 返回指定标记索引对应频率。 - double marker_frequency(int mark_index); - /// 设置指定标记频率。 - void set_marker_frequency(int marker_index, double frequency); - /// 设置当前选中标记频率。 - void set_current_marker_frequency(double frequency); - struct Builder; -private: - Spectrum_Private* d(); - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; -}; -struct Spectrum_Prop { - std::weak_ptr frequency_axis{}; - std::weak_ptr power_axis{}; - int frequency_point_size{}; - Range frequency_range{}; - double center_frequency = 50; - Range sweep_frequency_range{40, 60}; - bool max_hold_visible = false; - bool min_hold_visible = false; - bool max_marker_visible = false; - bool use_min_marker = false; - bool sweep_region_visible = false; - bool visible_range_only = true; - Line_Interpolation_Mode interpolation_mode = Line_Interpolation_Mode::Linear_Value; - Line_Sampling_Strategy sampling_strategy = Line_Sampling_Strategy::All_Samples; - Brush max_brush; - Brush current_brush; - Brush min_brush; - Pen max_pen = Pen{Color::red()}; - Pen cur_pen = Pen{Color::green()}; - Pen min_pen = Pen{Color::white()}; - Pen selected_marker_pen = Pen{Color{0, 0, 139, 255}, 2.0}; - Pen marker_pen = Pen{Color::red()}; - Pen middle_frequency_pen = Pen{Color::red()}; - Brush sweep_region_brush = Brush{Color{255, 255, 0, 100}, Brush_Style::Solid}; - Spectrum_Prop() { - max_brush = Brush{}; - current_brush = Brush{}; - min_brush = Brush{}; - } -}; -/// @brief 频谱线构造器。 -struct LIB_DECL Spectrum::Builder : Spectrum_Prop { - PROP_B(std::shared_ptr, parent) - PROP_B(std::shared_ptr, frequency_axis) - PROP_B(std::shared_ptr, power_axis); - PROP_B(int, frequency_point_size); - PROP_B(Range, frequency_range); - PROP_B(double, center_frequency); - PROP_B(Range, sweep_frequency_range); - PROP_B(bool, max_hold_visible); - PROP_B(bool, min_hold_visible); - PROP_B(bool, max_marker_visible); - PROP_B(bool, use_min_marker); - PROP_B(bool, sweep_region_visible); - PROP_B(bool, visible_range_only); - PROP_B(Line_Interpolation_Mode, interpolation_mode); - PROP_B(Line_Sampling_Strategy, sampling_strategy); - PROP_B(Brush, max_brush); - PROP_B(Brush, current_brush); - PROP_B(Brush, min_brush); - PROP_B(Pen, max_pen); - PROP_B(Pen, cur_pen); - PROP_B(Pen, min_pen); - PROP_B(Pen, selected_marker_pen); - PROP_B(Pen, marker_pen); - PROP_B(Pen, middle_frequency_pen); - PROP_B(Brush, sweep_region_brush); - /// 创建频谱线构造器。 - Builder(std::shared_ptr parent, std::shared_ptr frequency_axis, std::shared_ptr axis); - /// 构建并返回频谱线对象。 - std::shared_ptr build(); - std::shared_ptr parent{}; -}; -} // namespace renderive diff --git a/Core/plottable/Spectrum_p.h b/Core/plottable/Spectrum_p.h deleted file mode 100644 index a7848ff..0000000 --- a/Core/plottable/Spectrum_p.h +++ /dev/null @@ -1,248 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include "../Axis/Frequency_Axis_p.h" -#include "../Axis/Axis_p.h" -#include "../architecture/Renderable.h" -#include "../base/Frame_Memory.h" -#include "../base/Memory.h" -#include "../base/Color.h" -#include "../base/Geometry.h" -#include "../base/Style.h" -#include "../render/Canvas.h" -#include "Hover_Tooltip.h" -#include "Interpolation_p.h" -#include "../primitive/Label.h" -#include "../primitive/Marker.h" -#include "Spectrum.h" -namespace renderive { -struct Spectrum_Marker { - Spectrum_Marker() = default; - Spectrum_Marker(double frequency, bool is_line_marker) : frequency(frequency), is_line_marker(is_line_marker) {} - double frequency{}; - bool is_line_marker = true; - bool operator==(const Spectrum_Marker& other) const { - return std::abs(frequency - other.frequency) < 1e-12 && is_line_marker == other.is_line_marker; - } -}; -struct Spectrum_Input_Data : Latest_Input_Data { - std::pmr::vector cur_data{memory_resource(Memory_Domain::Spectrum)}; - std::pmr::vector max_data{memory_resource(Memory_Domain::Spectrum)}; - std::pmr::vector min_data{memory_resource(Memory_Domain::Spectrum)}; - Range frequency_range{}; - Line_Interpolation_Mode interpolation_mode = Line_Interpolation_Mode::Nearest_Sample; - bool has_data = false; - bool has_max_data = false; - bool has_min_data = false; - void push(std::span data, Range range, Line_Interpolation_Mode mode, bool use_max, bool use_min) { - if (has_data && frequency_range != range) { - max_data.clear(); - min_data.clear(); - has_max_data = false; - has_min_data = false; - } - frequency_range = range; - interpolation_mode = mode; - cur_data.assign(data.begin(), data.end()); - has_data = true; - if (use_max) { - if (!has_max_data || max_data.size() != data.size()) { - max_data.assign(data.begin(), data.end()); - has_max_data = true; - } - else { - for (size_t i = 0; i < data.size(); ++i) { - if (data[i] > max_data[i]) - max_data[i] = data[i]; - } - } - } - if (use_min) { - if (!has_min_data || min_data.size() != data.size()) { - min_data.assign(data.begin(), data.end()); - has_min_data = true; - } - else { - for (size_t i = 0; i < data.size(); ++i) { - if (data[i] < min_data[i]) - min_data[i] = data[i]; - } - } - } - mark_latest_pushed(); - } - void push(std::pmr::vector&& data, Range range, Line_Interpolation_Mode mode, bool use_max, bool use_min) { - if (has_data && frequency_range != range) { - max_data.clear(); - min_data.clear(); - has_max_data = false; - has_min_data = false; - } - frequency_range = range; - interpolation_mode = mode; - cur_data = std::move(data); - has_data = true; - if (use_max) { - if (!has_max_data || max_data.size() != cur_data.size()) { - max_data.assign(cur_data.begin(), cur_data.end()); - has_max_data = true; - } - else { - for (size_t i = 0; i < cur_data.size(); ++i) { - if (cur_data[i] > max_data[i]) - max_data[i] = cur_data[i]; - } - } - } - if (use_min) { - if (!has_min_data || min_data.size() != cur_data.size()) { - min_data.assign(cur_data.begin(), cur_data.end()); - has_min_data = true; - } - else { - for (size_t i = 0; i < cur_data.size(); ++i) { - if (cur_data[i] < min_data[i]) - min_data[i] = cur_data[i]; - } - } - } - mark_latest_pushed(); - } - template - void read_all(Handler handler) const { - if (!pending || !has_data) - return; - handler(cur_data, has_max_data ? &max_data : nullptr, has_min_data ? &min_data : nullptr); - } - void consume() override { - max_data.clear(); - min_data.clear(); - has_max_data = false; - has_min_data = false; - clear_latest(); - } - void clear() override { - cur_data.clear(); - max_data.clear(); - min_data.clear(); - has_data = false; - has_max_data = false; - has_min_data = false; - clear_latest(); - } -}; -struct Spectrum_Render_State : Render_State, Hover_Tooltip_State, Spectrum_Prop { - std::pmr::vector marker_list{memory_resource(Memory_Domain::Spectrum)}; - int cur_select_index = -1; - void un_select() { - cur_select_index = -1; - } - void select_next_marker() { - if (marker_list.empty()) - return; - cur_select_index = (cur_select_index + 1) % static_cast(marker_list.size()); - } - void select_prev_marker() { - if (marker_list.empty()) - return; - cur_select_index = (cur_select_index - 1 + static_cast(marker_list.size())) % static_cast(marker_list.size()); - } - void set_select_index(int index) { - if (index < 0 || index >= static_cast(marker_list.size())) - return; - cur_select_index = index; - } - void set_cur_frequency(double frequency) { - set_frequency(cur_select_index, frequency); - } - void set_frequency(int index, double frequency) { - if (index < 0 || index >= static_cast(marker_list.size())) - return; - marker_list[index].frequency = frequency; - } - void draw_axis_rect(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Range range, double middle_x) const { - auto frequency_axis = this->frequency_axis.lock(); - auto power_axis = this->power_axis.lock(); - if (!frequency_axis || !power_axis) - return; - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(frequency_axis.get(), power_axis.get(), snapshot); - canvas.save(); - canvas.set_pen(Pen{.style = Line_Style::None}); - canvas.set_brush(sweep_region_brush); - std::array rect_points{ - mapping.map(range.origin, mapping.value.coord_range.origin), - mapping.map(range.target, mapping.value.coord_range.origin), - mapping.map(range.target, mapping.value.coord_range.target), - mapping.map(range.origin, mapping.value.coord_range.target) - }; - canvas.draw_polygon(std::span(rect_points.data(), rect_points.size()), true); - canvas.set_pen(middle_frequency_pen); - canvas.draw_line( - mapping.map(middle_x, mapping.value.coord_range.origin), - mapping.map(middle_x, mapping.value.coord_range.target)); - canvas.restore(); - } -}; -struct Spectrum_Private : Typed_Render_Data, Hit_Testable, Hover_Interactive { - double min_marker_key{}; - double min_marker_value{}; - double max_marker_key{}; - double max_marker_value{}; - int cache_frequency_point_size{}; - Range cache_frequency_range{}; - bool has_power_data = false; - std::pmr::vector frequency{memory_resource(Memory_Domain::Spectrum)}; - std::pmr::vector max_powers{memory_resource(Memory_Domain::Spectrum)}; - std::pmr::vector cur_powers{memory_resource(Memory_Domain::Spectrum)}; - std::pmr::vector min_powers{memory_resource(Memory_Domain::Spectrum)}; - std::pmr::vector prepared_marker_items{memory_resource(Memory_Domain::Other)}; - std::pmr::vector prepared_label_items{memory_resource(Memory_Domain::Other)}; - void update_frequency_point_size(int frequency_point_size, Range frequency_range); - void set_frequency_data(const std::pmr::vector& line_data, const std::pmr::vector* max_data, const std::pmr::vector* min_data, const Spectrum_Render_State* s); - void update_marker_items(const Spectrum_Render_State* s, const Render_Frame_Snapshot& snapshot); - [[nodiscard]] double get_y(double x, bool& ok, Line_Interpolation_Mode mode); - void draw_power_curve(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Spectrum_Render_State* s, const std::pmr::vector& powers, const Pen& pen, const Brush& brush); - void draw_spectrum(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Spectrum_Render_State* s); - void draw_marker_items(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Spectrum_Render_State* s); - void draw_label_items(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Spectrum_Render_State* s); - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override; - void prepare_data(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - auto s = render_state(frame_view); - auto input = render_input_data(frame_view); - if (!s || !input) - return; - if (cache_frequency_point_size != s->frequency_point_size || cache_frequency_range != s->frequency_range) { - update_frequency_point_size(s->frequency_point_size, s->frequency_range); - } - input->read_all([this, s](const std::pmr::vector& data, const std::pmr::vector* max_data, const std::pmr::vector* min_data) { - set_frequency_data(data, max_data, min_data, s); - }); - if (has_power_data) { - update_marker_items(s, snapshot); - } - frame_view.consume_input(); - } - bool select_test(const PointF& pos) override { - const auto sc = edit_state(); - const auto frequency_axis = sc->frequency_axis.lock(); - const auto power_axis = sc->power_axis.lock(); - if (!frequency_axis || !power_axis) - return false; - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(frequency_axis.get(), power_axis.get()); - const double frequency_val = mapping.domain.pixel_to_coord(mapping.domain_pixel(pos)); - bool ok; - const double power = q()->power_at(frequency_val, ok); - if (!ok) - return false; - return std::abs(mapping.value.coord_to_pixel(power) - mapping.value_pixel(pos)) < 4.0; - } - void set_hover_state(Point pos, bool active) override { - Render_Edit_Lease edit(this); - edit->hover_position = pos; - edit->hover_info_active = active; - } -}; -} // namespace renderive diff --git a/Core/plottable/Sweep_Spectrum.cpp b/Core/plottable/Sweep_Spectrum.cpp deleted file mode 100644 index 94b649c..0000000 --- a/Core/plottable/Sweep_Spectrum.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include "../Axis/Abs_Axis.h" -#include "../base/Memory.h" -#include "Sweep_Spectrum_p.h" -namespace renderive { -RENDERIVE_DEFINE_RENDERABLE_BINDING(Sweep_Spectrum, Sweep_Spectrum_Private, Sweep_Spectrum_Render_State, Sweep_Spectrum_Input_Data, "Sweep_Frequency") -std::shared_ptr Sweep_Spectrum::frequency_axis() { - return d()->edit_state_value(&Sweep_Spectrum_Render_State::frequency_axis).lock(); -} -void Sweep_Spectrum::set_frequency_axis(std::shared_ptr value) { - d()->set_state_value(&Sweep_Spectrum_Render_State::frequency_axis, std::weak_ptr(value)); -} -std::shared_ptr Sweep_Spectrum::power_axis() { - return d()->edit_state_value(&Sweep_Spectrum_Render_State::power_axis).lock(); -} -void Sweep_Spectrum::set_power_axis(std::shared_ptr value) { - d()->set_state_value(&Sweep_Spectrum_Render_State::power_axis, std::weak_ptr(value)); -} -Range Sweep_Spectrum::frequency_range() { - return d()->edit_state_value(&Sweep_Spectrum_Render_State::frequency_range); -} -void Sweep_Spectrum::set_frequency_range(Range value) { - if (value.length() == 0.0) - return; - d()->set_state_value(&Sweep_Spectrum_Render_State::frequency_range, std::move(value)); -} -int Sweep_Spectrum::bins_per_block() { - return d()->edit_state_value(&Sweep_Spectrum_Render_State::bins_per_block); -} -void Sweep_Spectrum::set_bins_per_block(int value) { - d()->set_state_value(&Sweep_Spectrum_Render_State::bins_per_block, std::max(1, value)); -} -int Sweep_Spectrum::block_count() { - return d()->edit_state_value(&Sweep_Spectrum_Render_State::block_num); -} -void Sweep_Spectrum::set_block_count(int value) { - d()->set_state_value(&Sweep_Spectrum_Render_State::block_num, std::max(1, value)); -} -Pen Sweep_Spectrum::pen() { - return d()->edit_state_value(&Sweep_Spectrum_Render_State::pen); -} -void Sweep_Spectrum::set_pen(Pen value) { - d()->set_state_value(&Sweep_Spectrum_Render_State::pen, std::move(value)); -} -Pen Sweep_Spectrum::cur_frequency_pen() { - return d()->edit_state_value(&Sweep_Spectrum_Render_State::current_frequency_pen); -} -void Sweep_Spectrum::set_cur_frequency_pen(Pen value) { - d()->set_state_value(&Sweep_Spectrum_Render_State::current_frequency_pen, std::move(value)); -} -bool Sweep_Spectrum::visible_range_only() { - return d()->edit_state_value(&Sweep_Spectrum_Render_State::visible_range_only); -} -void Sweep_Spectrum::set_visible_range_only(bool value) { - d()->set_state_value(&Sweep_Spectrum_Render_State::visible_range_only, value); -} -Line_Interpolation_Mode Sweep_Spectrum::interpolation_mode() { - return d()->edit_state_value(&Sweep_Spectrum_Render_State::interpolation_mode); -} -void Sweep_Spectrum::set_interpolation_mode(Line_Interpolation_Mode value) { - d()->set_state_value(&Sweep_Spectrum_Render_State::interpolation_mode, value); -} -void Sweep_Spectrum::append_block(std::span data) { - if (!ok()) - return; - Sweep_Spectrum_Private* pd = d(); - int block_frequency_point_size{}; - { - Render_State_Lease state(pd); - block_frequency_point_size = state->bins_per_block; - } - if (data.size() != block_frequency_point_size) - return; - Render_Input_Lease input(pd); - input->data.push(data, Sweep_Spectrum_Input_Data::Latest_Only_Count); -} -void Sweep_Spectrum::append_block(std::pmr::vector&& data) { - if (!ok()) - return; - Sweep_Spectrum_Private* pd = d(); - int block_frequency_point_size{}; - { - Render_State_Lease state(pd); - block_frequency_point_size = state->bins_per_block; - } - if (data.size() != block_frequency_point_size) - return; - Render_Input_Lease input(pd); - input->data.push(std::move(data), Sweep_Spectrum_Input_Data::Latest_Only_Count); -} -Sweep_Spectrum::Builder::Builder(std::shared_ptr parent, std::shared_ptr frequency_axis, std::shared_ptr power_axis) { - ASSERT(parent, "Sweep_Frequency build error! parent == nullptr"); - ASSERT(frequency_axis && power_axis, "Sweep_Frequency build error! axis == nullptr"); - ASSERT(frequency_axis->attached_to_plot(), "Sweep_Frequency build error! frequency_axis->plot == nullptr"); - ASSERT(frequency_axis->shares_plot_with(*power_axis), "Sweep_Frequency build error! frequency_axis->plot != power_axis->plot"); - ASSERT(parent->shares_plot_with(*frequency_axis), "Sweep_Frequency build error! parent->plot != axis->plot"); - ASSERT(frequency_axis->orientation() != power_axis->orientation(), "Sweep_Frequency build error! axes must be orthogonal"); - this->parent = parent; - this->frequency_axis = frequency_axis; - this->power_axis = power_axis; -} -std::shared_ptr Sweep_Spectrum::Builder::build() { - auto ret = renderive::make_shared(); - ret->init(parent); - Sweep_Spectrum_Private* pd = ret->d(); - Render_Edit_Lease edit(pd); - Sweep_Spectrum_Render_State* sc = edit.operator->(); - PROP_R(std::weak_ptr, frequency_axis) - PROP_R(std::weak_ptr, power_axis) - PROP_R(Range, frequency_range) - PROP_R(int, bins_per_block) - PROP_R(int, block_num) - PROP_R(Pen, pen) - PROP_R(Pen, current_frequency_pen) - PROP_R(bool, visible_range_only) - PROP_R(Line_Interpolation_Mode, interpolation_mode) - sc->bins_per_block = std::max(1, sc->bins_per_block); - sc->block_num = std::max(1, sc->block_num); - if (sc->frequency_range.length() == 0.0) - sc->frequency_range = {0.0, 1.0}; - return ret; -} -} // namespace renderive diff --git a/Core/plottable/Sweep_Spectrum.h b/Core/plottable/Sweep_Spectrum.h deleted file mode 100644 index 03d8099..0000000 --- a/Core/plottable/Sweep_Spectrum.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include "../base/Color.h" -#include "../base/Memory.h" -#include "../base/Style.h" -#include "Interpolation.h" -#include "global.h" -namespace renderive { -struct Sweep_Spectrum_Private; -/// @brief 扫频频谱图元,按块组织输入频率数据。 -class LIB_DECL Sweep_Spectrum : public Renderable { -public: - /// @brief 创建扫频频谱图元。 - Sweep_Spectrum(); - /// @brief 写入一块扫频数据。 - void append_block(std::span data); - /// @brief 移动写入一块扫频数据。 - void append_block(std::pmr::vector&& data); - /// @brief 用 writer 填充并写入一块扫频数据。 - template - void write_block(std::size_t count, Writer&& writer) { - std::pmr::vector values(memory_resource(Memory_Domain::Other)); - values.resize(count); - std::forward(writer)(std::span(values.data(), values.size())); - append_block(std::move(values)); - } - /// @brief 绑定频率轴。 - PROP(std::shared_ptr, frequency_axis) - /// @brief 绑定功率轴。 - PROP(std::shared_ptr, power_axis) - /// @brief 扫频频率范围。 - PROP(Range, frequency_range) - /// @brief 每块频率采样点数。 - PROP(int, bins_per_block) - /// @brief 扫频块数量。 - PROP(int, block_count) - /// @brief 频谱线画笔。 - PROP(Pen, pen) - /// @brief 当前扫频位置画笔。 - PROP(Pen, cur_frequency_pen) - /// @brief 是否只生成当前可见频率范围。 - PROP(bool, visible_range_only) - /// @brief 折线插值模式。 - PROP(Line_Interpolation_Mode, interpolation_mode) - struct Builder; -private: - Sweep_Spectrum_Private* d(); - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; -}; -struct Sweep_Spectrum_Prop { - std::weak_ptr frequency_axis{}; - std::weak_ptr power_axis{}; - Range frequency_range{}; - int bins_per_block{}; - int block_num{}; - Pen pen; - Pen current_frequency_pen; - bool visible_range_only = true; - Line_Interpolation_Mode interpolation_mode = Line_Interpolation_Mode::Linear_Value; - Sweep_Spectrum_Prop() { - pen = Pen{Color::yellow()}; - current_frequency_pen = Pen{Color::red(), 2.0}; - } -}; -/// @brief 扫频频谱构造器。 -struct LIB_DECL Sweep_Spectrum::Builder : Sweep_Spectrum_Prop { - PROP_B(std::shared_ptr, parent) - PROP_B(std::shared_ptr, frequency_axis) - PROP_B(std::shared_ptr, power_axis) - PROP_B(Range, frequency_range) - PROP_B(int, bins_per_block) - PROP_B(int, block_num) - PROP_B(Pen, pen) - PROP_B(Pen, current_frequency_pen) - PROP_B(bool, visible_range_only) - PROP_B(Line_Interpolation_Mode, interpolation_mode) - /// 创建扫频构造器。 - Builder(std::shared_ptr parent, std::shared_ptr frequency_axis, std::shared_ptr power_axis); - /// 构建并返回扫频对象。 - std::shared_ptr build(); - std::shared_ptr parent{}; -}; -} // namespace renderive diff --git a/Core/plottable/Sweep_Spectrum_p.h b/Core/plottable/Sweep_Spectrum_p.h deleted file mode 100644 index 2bb6bf5..0000000 --- a/Core/plottable/Sweep_Spectrum_p.h +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once -#include "../Axis/Axis_p.h" -#include "../architecture/Bounded_Input_Buffer.h" -#include "../base/Frame_Memory.h" -#include "../base/Geometry.h" -#include "../base/Style.h" -#include "../event/Pointer_Event.h" -#include "../render/Canvas.h" -#include "../primitive/Curve.h" -#include "../primitive/Curve_Utils_p.h" -#include "Sweep_Spectrum.h" -namespace renderive { -struct Sweep_Spectrum_Render_State : renderive::Render_State, Sweep_Spectrum_Prop {}; -struct Sweep_Spectrum_Input_Data : renderive::Input_Data { - static constexpr std::size_t Latest_Only_Count = 1; - Bounded_Vector_Double_Input_Buffer data; - void clear() override { - data.clear(); - } -}; -struct Sweep_Spectrum_Private : renderive::Typed_Render_Data { - std::vector sweep_points; - int next_block_index{}; - int bins_per_block{}; - int block_count{}; - Range frequency_range; - bool first_sweep_incomplete = true; - void reset_sweep_grid(int block_frequency_point_size, int block_num, Range frequency_range) { - this->bins_per_block = block_frequency_point_size; - this->block_count = block_num; - this->frequency_range = frequency_range; - int total_point_count = block_num * block_frequency_point_size; - if (total_point_count <= 0) { - sweep_points.clear(); - first_sweep_incomplete = true; - return; - } - sweep_points.resize(total_point_count); - int interval_count = total_point_count - 1; - if (interval_count == 0) { - sweep_points[0].x = frequency_range.origin; - first_sweep_incomplete = true; - return; - } - double step = frequency_range.length() / interval_count; - double frequency = frequency_range.origin; - for (int i = 0; i < interval_count; ++i) { - sweep_points[i].x = frequency; - frequency += step; - } - sweep_points.back().x = frequency_range.target; - first_sweep_incomplete = true; - } - void prepare_data(const Render_Frame_Snapshot&, const Renderable_Frame_View& frame_view) override { - Sweep_Spectrum_Render_State* s = render_state(frame_view); - Sweep_Spectrum_Input_Data* d = render_input_data(frame_view); - if (!s || !d) - return; - if ( - bins_per_block != s->bins_per_block || - block_count != s->block_num || - frequency_range != s->frequency_range) { - reset_sweep_grid(s->bins_per_block, s->block_num, s->frequency_range); - next_block_index = 0; - } - d->data.read_all([this, d](const auto& data) { - if ((int)data.size() != bins_per_block || sweep_points.empty()) { - return; - } - PointF* start = &sweep_points[next_block_index * bins_per_block]; - for (int i = 0; i < bins_per_block; ++i) { - start[i].y = data[i]; - } - next_block_index++; - if (next_block_index == block_count) { - next_block_index = 0; - first_sweep_incomplete = false; - } - }); - frame_view.consume_input(); - } - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Sweep_Spectrum_Render_State* s = render_state(frame_view); - if (!s) - return; - auto frequency_axis = s->frequency_axis.lock(); - auto power_axis = s->power_axis.lock(); - if (!frequency_axis || !power_axis) - return; - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(frequency_axis.get(), power_axis.get(), snapshot); - int total_point_count = (int)sweep_points.size(); - int available_count = !first_sweep_incomplete ? total_point_count : next_block_index * bins_per_block; - std::pmr::vector data(frame_memory_resource()); - data = Curve_Utils::build_resampled_points(mapping, s->frequency_range, total_point_count, available_count, s->visible_range_only, s->interpolation_mode, [this](int i) { - return sweep_points.at(i).y; - }); - Curve_Utils::draw_polyline(&canvas, data, s->pen); - canvas.set_pen(s->current_frequency_pen); - double current_frequency = block_count > 1 ? frequency_range.origin + next_block_index * frequency_range.length() / (block_count - 1.0) : frequency_range.origin; - canvas.draw_line( - mapping.map(current_frequency, mapping.value.coord_range.origin), - mapping.map(current_frequency, mapping.value.coord_range.target)); - } -}; -} // namespace renderive diff --git a/Core/plottable/Waterfall.cpp b/Core/plottable/Waterfall.cpp deleted file mode 100644 index 8b40d0f..0000000 --- a/Core/plottable/Waterfall.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include -#include "../base/Memory.h" -#include "Waterfall_p.h" -namespace renderive { -RENDERIVE_DEFINE_RENDERABLE_BINDING(Waterfall, Waterfall_Private, Waterfall_Render_State, Waterfall_Input_Data, "Waterfall") -std::shared_ptr Waterfall::frequency_axis() { - return d()->edit_state_value(&Waterfall_Render_State::frequency_axis).lock(); -} -void Waterfall::set_frequency_axis(std::shared_ptr value) { - d()->set_state_value(&Waterfall_Render_State::frequency_axis, std::weak_ptr(value)); -} -std::shared_ptr Waterfall::time_axis() { - return d()->edit_state_value(&Waterfall_Render_State::time_axis).lock(); -} -void Waterfall::set_time_axis(std::shared_ptr value) { - d()->set_state_value(&Waterfall_Render_State::time_axis, std::weak_ptr(value)); -} -Range Waterfall::frequency_range() { - return d()->edit_state_value(&Waterfall_Render_State::frequency_range); -} -void Waterfall::set_frequency_range(Range value) { - if (value.length() == 0.0) - return; - d()->set_state_value(&Waterfall_Render_State::frequency_range, std::move(value)); -} -Range Waterfall::power_range() { - return d()->edit_state_value(&Waterfall_Render_State::color_scale).range(); -} -void Waterfall::set_power_range(Range value) { - if (value.size() == 0.0) - return; - Render_Edit_Lease edit(d()); - edit->color_scale.set_range(value); -} -int Waterfall::frequency_bin_count() { - return d()->edit_state_value(&Waterfall_Render_State::frequency_bin_count); -} -void Waterfall::set_frequency_bin_count(int value) { - d()->set_state_value(&Waterfall_Render_State::frequency_bin_count, std::max(1, value)); -} -bool Waterfall::visible_range_only() { - return d()->edit_state_value(&Waterfall_Render_State::visible_range_only); -} -void Waterfall::set_visible_range_only(bool value) { - d()->set_state_value(&Waterfall_Render_State::visible_range_only, value); -} -Image_Interpolation_Mode Waterfall::interpolation_mode() { - return d()->edit_state_value(&Waterfall_Render_State::interpolation_mode); -} -void Waterfall::set_interpolation_mode(Image_Interpolation_Mode value) { - d()->set_state_value(&Waterfall_Render_State::interpolation_mode, value); -} -Waterfall_Row_Update_Policy Waterfall::row_update_policy() { - return d()->edit_state_value(&Waterfall_Render_State::row_update_policy); -} -void Waterfall::set_row_update_policy(Waterfall_Row_Update_Policy value) { - d()->set_state_value(&Waterfall_Render_State::row_update_policy, value); -} -int Waterfall::latest_row_min_interval_ms() { - return d()->edit_state_value(&Waterfall_Render_State::latest_row_min_interval_ms); -} -void Waterfall::set_latest_row_min_interval_ms(int value) { - d()->set_state_value(&Waterfall_Render_State::latest_row_min_interval_ms, std::max(1, value)); -} -Color_Map Waterfall::color_map() { - return d()->edit_state_value(&Waterfall_Render_State::color_scale).color_map(); -} -void Waterfall::set_color_map(Color_Map value) { - Render_Edit_Lease edit(d()); - edit->color_scale.set_color_map(std::move(value)); -} -std::shared_ptr Waterfall::get_color_bar() const { - return const_cast(this)->d()->color_bar.lock(); -} -void Waterfall::append_row(int tick, std::span data) { - if (!ok()) - return; - d()->enqueue_row(tick, data); -} -void Waterfall::append_row(int tick, std::pmr::vector&& data) { - if (!ok()) - return; - d()->enqueue_row(tick, std::move(data)); -} -void Waterfall::append_row(Time_Of_Day time, std::span data) { - if (!ok()) - return; - auto axis = time_axis(); - if (!axis) - return; - auto stream = axis->timeline_stream(); - if (!stream) - return; - int tick = stream->append_time(time); - d()->enqueue_row(tick, data); -} -void Waterfall::append_row(Time_Of_Day time, std::pmr::vector&& data) { - if (!ok()) - return; - auto axis = time_axis(); - if (!axis) - return; - auto stream = axis->timeline_stream(); - if (!stream) - return; - int tick = stream->append_time(time); - d()->enqueue_row(tick, std::move(data)); -} -Update_Ticket Waterfall::submit_data(int tick, std::span data) { - auto state = make_update_state(); - if (!ok()) { - state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - return Update_Ticket(state); - } - d()->enqueue_row(tick, data, state); - return Update_Ticket(state); -} -Update_Ticket Waterfall::submit_data(int tick, std::pmr::vector&& data) { - auto state = make_update_state(); - if (!ok()) { - state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - return Update_Ticket(state); - } - d()->enqueue_row(tick, std::move(data), state); - return Update_Ticket(state); -} -Waterfall::Builder::Builder(std::shared_ptr parent, std::shared_ptr frequency_axis, std::shared_ptr time_axis) { - ASSERT(parent && parent->attached_to_plot(), "Waterfall build error! parent invalid"); - ASSERT(frequency_axis && time_axis, "Waterfall build error! axis == nullptr"); - ASSERT(frequency_axis->attached_to_plot(), "Waterfall build error! frequency_axis->plot == nullptr"); - ASSERT(frequency_axis->shares_plot_with(*time_axis), "Waterfall build error! frequency_axis->plot != time_axis->plot"); - ASSERT(parent->shares_plot_with(*frequency_axis), "Waterfall build error! parent->plot != frequency_axis->plot"); - ASSERT(frequency_axis->orientation() != time_axis->orientation(), "Waterfall build error! axes must be orthogonal"); - this->parent = parent; - this->frequency_axis = frequency_axis; - this->time_axis = time_axis; -} -std::shared_ptr Waterfall::Builder::build() { - auto ret = renderive::make_shared(); - ret->init(parent); - Waterfall_Private* pd = ret->d(); - Render_Edit_Lease edit(pd); - Waterfall_Render_State* sc = edit.operator->(); - PROP_R(std::weak_ptr, frequency_axis) - PROP_R(std::weak_ptr, time_axis) - PROP_R(Range, frequency_range) - PROP_R(int, frequency_bin_count) - PROP_R(bool, visible_range_only) - PROP_R(Image_Interpolation_Mode, interpolation_mode) - PROP_R(Waterfall_Row_Update_Policy, row_update_policy) - PROP_R(int, latest_row_min_interval_ms) - sc->color_scale = color_scale.clone(); - sc->frequency_bin_count = std::max(1, sc->frequency_bin_count); - sc->latest_row_min_interval_ms = std::max(1, sc->latest_row_min_interval_ms); - if (color_bar_enabled) { - auto bar = Color_Bar::Builder(ret).set_color_scale(sc->color_scale).set_name("Waterfall").build(); - pd->color_bar = bar; - } - return ret; -} -} // namespace renderive diff --git a/Core/plottable/Waterfall.h b/Core/plottable/Waterfall.h deleted file mode 100644 index 8277107..0000000 --- a/Core/plottable/Waterfall.h +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include "../architecture/Update_Completion.h" -#include "../base/Memory.h" -#include "../base/Time_Of_Day.h" -#include "../primitive/Color_Bar.h" -#include "../primitive/Color_Scale.h" -#include "Hover_Tooltip.h" -#include "Interpolation.h" -namespace renderive { -struct Waterfall_Private; -class Frequency_Axis; -class Time_Axis; -/// @brief Policy for consuming pending waterfall rows. -enum class Waterfall_Row_Update_Policy : std::uint8_t { - /// @brief Consume every currently pending row in submit order. - All_Pending, - /// @brief Consume only the latest pending row and drop older pending rows. - Latest_Only, - /// @brief Consume only the latest row while enforcing a minimum interval between commits. - Rate_Limited_Latest -}; -/// @brief 瀑布图图元,按时间行保存二维频谱强度。 -class LIB_DECL Waterfall : public Renderable, public Hover_Tooltip_Mixin { -public: - /// @brief 创建瀑布图图元。 - Waterfall(); - /// @brief 写入指定 tick 对应的一行频谱数据。 - void append_row(int tick, std::span data); - /// @brief 移动写入指定 tick 对应的一行频谱数据。 - void append_row(int tick, std::pmr::vector&& data); - /// @brief 写入指定时间对应的一行频谱数据。 - void append_row(Time_Of_Day time, std::span data); - /// @brief 移动写入指定时间对应的一行频谱数据。 - void append_row(Time_Of_Day time, std::pmr::vector&& data); - /// @brief 提交指定 tick 对应的一行频谱数据并返回完成票据。 - Update_Ticket submit_data(int tick, std::span data); - /// @brief 移动提交指定 tick 对应的一行频谱数据并返回完成票据。 - Update_Ticket submit_data(int tick, std::pmr::vector&& data); - /// @brief 用 writer 填充并写入指定 tick 对应的一行频谱数据。 - template - void write_data(int tick, std::size_t count, Writer&& writer) { - std::pmr::vector values(memory_resource(Memory_Domain::Waterfall)); - values.resize(count); - std::forward(writer)(std::span(values.data(), values.size())); - append_row(tick, std::move(values)); - } - /// @brief 绑定频率轴。 - PROP(std::shared_ptr, frequency_axis) - /// @brief 绑定时间轴。 - PROP(std::shared_ptr, time_axis) - /// @brief 频率数据范围。 - PROP(Range, frequency_range) - /// @brief 功率数据范围。 - PROP(Range, power_range) - /// @brief 每行频率采样点数。 - PROP(int, frequency_bin_count) - /// @brief 是否只生成当前可见频率范围。 - PROP(bool, visible_range_only) - /// @brief 图像缩放插值模式。 - PROP(Image_Interpolation_Mode, interpolation_mode) - /// @brief 瀑布图增量更新模式。 - PROP(Waterfall_Row_Update_Policy, row_update_policy) - /// @brief Timed_Latest 模式下的最小更新间隔。 - PROP(int, latest_row_min_interval_ms) - /// @brief 颜色映射表。 - PROP(Color_Map, color_map) - /// @brief 返回构造时启用的颜色条。 - std::shared_ptr get_color_bar() const; - struct Builder; -private: - Waterfall_Private* d(); - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; -}; -struct Waterfall_Prop { - std::weak_ptr frequency_axis{}; - std::weak_ptr time_axis{}; - Range frequency_range{0, 10}; - int frequency_bin_count{}; - bool visible_range_only = true; - Image_Interpolation_Mode interpolation_mode = Image_Interpolation_Mode::Nearest; - Waterfall_Row_Update_Policy row_update_policy = Waterfall_Row_Update_Policy::All_Pending; - int latest_row_min_interval_ms = 10; - Color_Scale color_scale{Color_Map{}, Range{0, 10}}; - bool color_bar_enabled = false; -}; -/// @brief 瀑布图构造器。 -struct LIB_DECL Waterfall::Builder : Waterfall_Prop { - PROP_B(std::shared_ptr, frequency_axis) - PROP_B(std::shared_ptr, time_axis) - PROP_B(Range, frequency_range) - Builder& set_power_range(Range value) { - color_scale.set_range(value); - return *this; - } - PROP_B(int, frequency_bin_count) - PROP_B(bool, visible_range_only) - PROP_B(Image_Interpolation_Mode, interpolation_mode) - PROP_B(Waterfall_Row_Update_Policy, row_update_policy) - PROP_B(int, latest_row_min_interval_ms) - Builder& set_color_map(Color_Map value) { - color_scale.set_color_map(std::move(value)); - return *this; - } - PROP_B(Color_Scale, color_scale) - PROP_B(bool, color_bar_enabled) - /// 创建瀑布图构造器。 - Builder(std::shared_ptr parent, std::shared_ptr frequency_axis, std::shared_ptr time_axis); - /// 构建并返回瀑布图对象。 - std::shared_ptr build(); - std::shared_ptr parent{}; -}; -} // namespace renderive diff --git a/Core/plottable/Waterfall_p.h b/Core/plottable/Waterfall_p.h deleted file mode 100644 index 4358de9..0000000 --- a/Core/plottable/Waterfall_p.h +++ /dev/null @@ -1,589 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../Axis/Frequency_Axis_p.h" -#include "../Axis/Time_Axis_p.h" -#include "../architecture/Render_Time.h" -#include "../architecture/Renderable.h" -#include "../base/Memory.h" -#include "../base/global.h" -#include "../render/Canvas.h" -#include "../render/Frame_Prepare_Subflow.h" -#include "../render/Image.h" -#include "../render/Render_Frame_Snapshot.h" -#include "Psc_Cpp_Core/Base/RingBuffer.hpp" -#include "Interpolation_p.h" -#include "Waterfall.h" -namespace renderive { -struct Waterfall_Data { - int tick{}; - std::pmr::vector frequency{memory_resource(Memory_Domain::Waterfall)}; - std::shared_ptr completion_state; -}; -struct Waterfall_Input_Data : Input_Data {}; -struct Waterfall_Render_State : Render_State, Hover_Tooltip_State, Waterfall_Prop {}; -struct Waterfall_Ring_Buffer { - Psc::StreamRingBuffer_ST buffer{memory_resource(Memory_Domain::Waterfall)}; - std::pmr::vector record_buffer{memory_resource(Memory_Domain::Waterfall)}; - std::pmr::vector snapshot_buffer{memory_resource(Memory_Domain::Waterfall)}; - std::pmr::vector row_buffer{memory_resource(Memory_Domain::Waterfall)}; - std::size_t record_byte_size{}; - std::size_t row_byte_size{}; - int row_count{}; - int col_count{}; - int data_count{}; - bool match(int col, int row) const { - return col_count == col && row_count == row && record_byte_size; - } - void resize(int col, int row) { - col_count = col; - row_count = row; - data_count = 0; - row_byte_size = sizeof(double) * col_count; - record_byte_size = row_byte_size + sizeof(int); - record_buffer.resize(record_byte_size); - snapshot_buffer.resize(record_byte_size * row_count); - row_buffer.resize(col_count); - buffer.init(record_byte_size * row_count); - } - void push_data(const double* data, int tick) { - if (data_count == row_count) { - buffer.skip(record_byte_size); - --data_count; - } - std::memcpy(record_buffer.data(), data, row_byte_size); - std::memcpy(record_buffer.data() + row_byte_size, &tick, sizeof(tick)); - buffer.write(record_buffer.data(), record_byte_size); - ++data_count; - } - int snapshot() { - buffer.peek_best_effort(snapshot_buffer.data(), record_byte_size * data_count); - return data_count; - } - const double* frequency_data(int row, int first_col, int last_col) { - std::size_t offset = static_cast(first_col) * sizeof(double); - std::size_t size = static_cast(last_col - first_col + 1) * sizeof(double); - std::memcpy(reinterpret_cast(row_buffer.data()) + offset, snapshot_buffer.data() + row * record_byte_size + offset, size); - return row_buffer.data(); - } - const double* frequency_data(int row) { - std::memcpy(row_buffer.data(), snapshot_buffer.data() + row * record_byte_size, row_byte_size); - return row_buffer.data(); - } - int tick(int row) const { - int value; - std::memcpy(&value, snapshot_buffer.data() + row * record_byte_size + row_byte_size, sizeof(value)); - return value; - } -}; -struct Waterfall_Image_Cache_Key { - int start_tick{}; - int first_col{}; - int last_col{}; - Range frequency_range{}; - Range power_range{}; - Range visible_range{}; - Color background; - std::uint64_t color_map_hash{}; - int width{}; - int height{}; - bool operator==(const Waterfall_Image_Cache_Key& other) const { - return start_tick == other.start_tick && - first_col == other.first_col && - last_col == other.last_col && - frequency_range == other.frequency_range && - power_range == other.power_range && - visible_range == other.visible_range && - background == other.background && - color_map_hash == other.color_map_hash && - width == other.width && - height == other.height; - } -}; -struct Waterfall_Image_Snapshot { - Waterfall_Image_Snapshot() : update_states(memory_resource(Memory_Domain::Update_Completion)) {} - Waterfall_Image_Cache_Key key; - Image image; - Range frequency_range{}; - Range time_range{}; - RectF source_rect{}; - std::pmr::vector> update_states; -}; -struct Waterfall_Image_Request { - Waterfall_Image_Request() - : rows(memory_resource(Memory_Domain::Waterfall)), - update_states(memory_resource(Memory_Domain::Update_Completion)) {} - Waterfall_Image_Cache_Key key; - Color_Map color_map; - double value_start_coord{}; - double rate{}; - int lower_stream_tick{}; - int upper_stream_tick{}; - int visible_time_count{}; - bool newest_at_axis_start{}; - Range draw_frequency_range{}; - Range draw_time_range{}; - RectF draw_source_rect{}; - std::pmr::vector rows; - std::pmr::vector> update_states; -}; -struct Waterfall_Image_Tile { - int x{}; - int y{}; - Image image; -}; -struct Waterfall_Image_Job { - explicit Waterfall_Image_Job(std::shared_ptr request, int columns, int rows) - : request(std::move(request)), - update_states(memory_resource(Memory_Domain::Update_Completion)), - tile_columns(columns), - tile_rows(rows) { - tiles.resize(static_cast(columns * rows)); - } - ~Waterfall_Image_Job() { - for (auto& state : update_states) { - if (state) - state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - } - update_states.clear(); - } - std::shared_ptr request; - std::pmr::vector> update_states; - int tile_columns{}; - int tile_rows{}; - std::vector tiles; -}; -static void complete_waterfall_request_states(Waterfall_Image_Request& request, std::error_code error, Update_Outcome outcome) { - for (auto& state : request.update_states) { - if (state) - state->complete_all(error, outcome); - } - request.update_states.clear(); -} -static void complete_waterfall_result_states(Waterfall_Image_Snapshot& snapshot, std::error_code error, Update_Outcome outcome) { - for (const auto& state : snapshot.update_states) { - if (state) - state->complete_all(error, outcome); - } - snapshot.update_states.clear(); -} -static bool waterfall_request_coord_by_stream_tick(const Waterfall_Image_Request& request, int stream_tick, int& coord) { - if (request.visible_time_count <= 0) - return false; - if (stream_tick < request.lower_stream_tick || stream_tick > request.upper_stream_tick) - return false; - if (request.newest_at_axis_start) - coord = request.key.start_tick + (request.upper_stream_tick - stream_tick); - else - coord = request.key.start_tick + (request.key.height - request.visible_time_count) + (stream_tick - request.lower_stream_tick); - return coord >= request.key.start_tick && coord < request.key.start_tick + request.key.height; -} -static int waterfall_tile_column_count(int width) { - static constexpr int Tile_Width = 128; - return std::max(1, (width + Tile_Width - 1) / Tile_Width); -} -static int waterfall_tile_row_count(int height) { - static constexpr int Tile_Height = 64; - return std::max(1, (height + Tile_Height - 1) / Tile_Height); -} -static std::shared_ptr create_waterfall_image_job(std::shared_ptr request) { - if (!request || request->key.width <= 0 || request->key.height <= 0) - return {}; - int columns = waterfall_tile_column_count(request->key.width); - int rows = waterfall_tile_row_count(request->key.height); - std::pmr::vector> update_states = std::move(request->update_states); - auto job = std::make_shared( - std::shared_ptr(std::move(request)), - columns, - rows); - job->update_states = std::move(update_states); - return job; -} -static void write_waterfall_tile(Waterfall_Image_Job& job, int tile_index) { - const Waterfall_Image_Request& request = *job.request; - int tile_x = tile_index % job.tile_columns; - int tile_y = tile_index / job.tile_columns; - int x_begin = request.key.width * tile_x / job.tile_columns; - int x_end = request.key.width * (tile_x + 1) / job.tile_columns; - int y_begin = request.key.height * tile_y / job.tile_rows; - int y_end = request.key.height * (tile_y + 1) / job.tile_rows; - Waterfall_Image_Tile& tile = job.tiles[static_cast(tile_index)]; - tile.x = x_begin; - tile.y = y_begin; - int tile_width = std::max(0, x_end - x_begin); - int tile_height = std::max(0, y_end - y_begin); - tile.image.resize(tile_width, tile_height); - tile.image.fill(request.key.background); - for (const Waterfall_Data& row : request.rows) { - int coord{}; - if (!waterfall_request_coord_by_stream_tick(request, row.tick, coord)) - continue; - int image_y = coord - request.key.start_tick; - if (image_y < y_begin || image_y >= y_end) - continue; - Pixel* line = tile.image.row(image_y - y_begin); - if (!line) - continue; - int first_col = std::max(request.key.first_col, x_begin); - int last_col = std::min(request.key.last_col, x_end - 1); - for (int col = first_col; col <= last_col; ++col) { - std::size_t source_col = static_cast(col - request.key.first_col); - if (source_col >= row.frequency.size()) - continue; - int color_offset = static_cast((row.frequency[source_col] - request.value_start_coord) * request.rate); - line[col - x_begin] = request.color_map.at_offset(color_offset); - } - } -} -static Waterfall_Image_Snapshot compose_waterfall_image(Waterfall_Image_Job& job) { - const Waterfall_Image_Request& request = *job.request; - Waterfall_Image_Snapshot snapshot; - snapshot.key = request.key; - snapshot.frequency_range = request.draw_frequency_range; - snapshot.time_range = request.draw_time_range; - snapshot.source_rect = request.draw_source_rect; - snapshot.image.resize(request.key.width, request.key.height); - for (const Waterfall_Image_Tile& tile : job.tiles) { - for (int y = 0; y < tile.image.height(); ++y) { - Pixel* target = snapshot.image.row(tile.y + y); - const Pixel* source = tile.image.row(y); - if (target && source) - std::memcpy(target + tile.x, source, static_cast(tile.image.width()) * sizeof(Pixel)); - } - } - snapshot.update_states = std::move(job.update_states); - return snapshot; -} -struct Waterfall_Private : Typed_Render_Data, Hit_Testable, Hover_Interactive, Frame_Prepare_Subtask_Source { - static constexpr std::size_t Row_Queue_Capacity = 256; - Waterfall_Ring_Buffer ring_buffer; - std::weak_ptr color_bar; - std::optional pending_latest_row; - std::pmr::vector> pending_image_update_states{memory_resource(Memory_Domain::Update_Completion)}; - rigtorp::MPMCQueue> row_queue; - std::atomic_uint64_t dropped_row_count{0}; - std::uint64_t last_line_commit_ns{}; - std::optional image_snapshot; - std::optional submitted_image_key; - std::uint64_t pending_latest_deadline_ns{}; - Waterfall_Private() - : row_queue(Row_Queue_Capacity) {} - bool select_test(const PointF& pos) override { - return true; - } - void set_hover_state(Point pos, bool active) override { - Render_Edit_Lease edit(this); - edit->hover_position = pos; - edit->hover_info_active = active; - } - ~Waterfall_Private() override { - if (image_snapshot) - complete_waterfall_result_states(*image_snapshot, std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - std::shared_ptr row; - while (row_queue.try_pop(row)) { - if (row && row->completion_state) - row->completion_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - } - if (pending_latest_row && pending_latest_row->completion_state) - pending_latest_row->completion_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - for (auto& state : pending_image_update_states) { - if (state) - state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - } - pending_image_update_states.clear(); - } - void enqueue_row(int tick, std::span data, std::shared_ptr completion_state = {}) { - auto row = std::make_shared(); - row->tick = tick; - row->frequency.assign(data.begin(), data.end()); - row->completion_state = std::move(completion_state); - enqueue_row(std::move(row)); - } - void enqueue_row(int tick, std::pmr::vector&& data, std::shared_ptr completion_state = {}) { - auto row = std::make_shared(); - row->tick = tick; - row->frequency = std::move(data); - row->completion_state = std::move(completion_state); - enqueue_row(std::move(row)); - } - void enqueue_row(std::shared_ptr row) { - if (!row) - return; - if (!row_queue.try_push(row)) { - if (row->completion_state) - row->completion_state->complete_all({}, Update_Outcome::Superseded); - dropped_row_count.fetch_add(1, std::memory_order_relaxed); - return; - } - if (row->completion_state) - row->completion_state->complete_until(Update_Stage::Input_Released); - if (q()) - q()->mark_render_dirty(); - } - void consume_row_queue(std::pmr::vector& output) { - std::shared_ptr row; - while (row_queue.try_pop(row)) { - if (!row) - continue; - output.push_back(std::move(*row)); - } - } - void commit_row_ticket(Waterfall_Data& row) { - if (!row.completion_state) - return; - row.completion_state->complete_until(Update_Stage::Committed); - pending_image_update_states.push_back(std::move(row.completion_state)); - } - void schedule_pending_latest_deadline(std::uint64_t deadline_ns) { - if (!deadline_ns || pending_latest_deadline_ns == deadline_ns) - return; - pending_latest_deadline_ns = deadline_ns; - std::weak_ptr owner = q() ? std::weak_ptr(q()->shared_from_this()) : std::weak_ptr(); - Global::instance()->render_scheduler().post_at(deadline_ns, [owner]() { - if (auto renderable = owner.lock()) - renderable->mark_render_dirty(); - }); - } - bool push_row(std::span frequency_data, int tick, int col_count) { - if (frequency_data.size() != col_count) { - return false; - } - ring_buffer.push_data(frequency_data.data(), tick); - return true; - } - bool push_owned_row(std::pmr::vector& frequency_data, int tick, int col_count, Waterfall_Row_Update_Policy update_policy) { - if (frequency_data.size() != col_count) { - return false; - } - ring_buffer.push_data(frequency_data.data(), tick); - (void)update_policy; - return true; - } - void prepare_data(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - prepare_waterfall(snapshot, frame_view, nullptr); - } - bool build_prepare_subtasks(Render_Task_Subflow& subflow, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - prepare_waterfall(snapshot, frame_view, &subflow); - return true; - } - void prepare_waterfall(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view, Render_Task_Subflow* subflow) { - Waterfall_Render_State* s = render_state(frame_view); - if (!s) - return; - auto time_axis = s->time_axis.lock(); - if (!time_axis) - return; - auto timeline = Time_Axis_Render_Access::capture_timeline(time_axis.get(), snapshot); - if (!timeline) - return; - int col_count = s->frequency_bin_count, row_count = timeline->visible_time_point_count; - if (col_count <= 0 || row_count <= 0) - return; - if (!ring_buffer.match(col_count, row_count)) { - ring_buffer.resize(col_count, row_count); - } - std::pmr::vector queued_rows(memory_resource(Memory_Domain::Waterfall)); - consume_row_queue(queued_rows); - switch (s->row_update_policy) { - case Waterfall_Row_Update_Policy::All_Pending: - for (auto& row : queued_rows) { - if (push_row(row.frequency, row.tick, col_count)) - commit_row_ticket(row); - else if (row.completion_state) - row.completion_state->complete_all(std::make_error_code(std::errc::invalid_argument), Update_Outcome::Cancelled); - } - break; - case Waterfall_Row_Update_Policy::Latest_Only: - case Waterfall_Row_Update_Policy::Rate_Limited_Latest: { - for (auto& row : queued_rows) { - if (row.frequency.size() != col_count) { - if (row.completion_state) - row.completion_state->complete_all(std::make_error_code(std::errc::invalid_argument), Update_Outcome::Cancelled); - continue; - } - if (pending_latest_row && pending_latest_row->completion_state) - pending_latest_row->completion_state->complete_all({}, Update_Outcome::Superseded); - pending_latest_row = std::move(row); - } - if (pending_latest_row && pending_latest_row->frequency.size() != col_count) { - if (pending_latest_row->completion_state) - pending_latest_row->completion_state->complete_all(std::make_error_code(std::errc::invalid_argument), Update_Outcome::Cancelled); - pending_latest_row.reset(); - } - if (pending_latest_row) { - bool allow_commit = true; - std::uint64_t now_ns = steady_now_ns(); - std::uint64_t deadline_ns = 0; - if (s->row_update_policy == Waterfall_Row_Update_Policy::Rate_Limited_Latest && last_line_commit_ns != 0) { - std::uint64_t interval_ns = static_cast(std::max(1, s->latest_row_min_interval_ms)) * 1000000ull; - deadline_ns = last_line_commit_ns + interval_ns; - allow_commit = now_ns >= deadline_ns; - } - if (allow_commit) { - if (push_owned_row(pending_latest_row->frequency, pending_latest_row->tick, col_count, s->row_update_policy)) { - commit_row_ticket(*pending_latest_row); - last_line_commit_ns = now_ns; - pending_latest_deadline_ns = 0; - pending_latest_row.reset(); - } - } - else { - schedule_pending_latest_deadline(deadline_ns); - } - } - break; - } - } - update_image_item(snapshot, *timeline, s, subflow); - } - void update_image_item(const Render_Frame_Snapshot& snapshot, const Timeline_Stream_Snapshot& timeline, Waterfall_Render_State* s, Render_Task_Subflow* subflow) { - if (!s) - return; - auto frequency_axis = s->frequency_axis.lock(); - auto time_axis = s->time_axis.lock(); - if (!frequency_axis || !time_axis) - return; - auto color_scale = s->color_scale.snapshot(); - Abs_Axis* h_axis = frequency_axis.get(); - Range h_range = s->frequency_range; - Range v_range{static_cast(timeline.coordinate_begin), static_cast(timeline.coordinate_begin + timeline.visible_time_point_count)}; - if (s->visible_range_only && !intersect_range(s->frequency_range, Axis_Render_Access::coord_range(h_axis, snapshot), h_range)) { - image_snapshot.reset(); - return; - } - int col_count = s->frequency_bin_count, row_count = timeline.visible_time_point_count; - if (col_count <= 0 || row_count <= 0 || s->frequency_range.length() == 0.0 || color_scale->range.size() == 0.0) - return; - double source_start = static_cast(col_count) * (h_range.origin - s->frequency_range.origin) / s->frequency_range.length(); - double source_end = static_cast(col_count) * (h_range.target - s->frequency_range.origin) / s->frequency_range.length(); - double source_min = std::min(source_start, source_end); - double source_max = std::max(source_start, source_end); - int first_col = std::clamp(static_cast(std::floor(source_min)), 0, col_count - 1); - int last_col = std::clamp(static_cast(std::ceil(source_max)) - 1, first_col, col_count - 1); - double value_start_coord = color_scale->range.origin; - double rate = static_cast(color_scale->color_map.size()) / color_scale->range.size(); - int buffer_data_count = ring_buffer.snapshot(); - int start_tick = timeline.coordinate_begin; - std::uint64_t color_map_hash = color_scale->color_map.hash(); - Waterfall_Image_Cache_Key key{ - start_tick, - first_col, - last_col, - s->frequency_range, - color_scale->range, - h_range, - snapshot.background_color, - color_map_hash, - col_count, - row_count - }; - bool should_submit = !submitted_image_key || !(*submitted_image_key == key) || !pending_image_update_states.empty(); - if (should_submit) { - if (!subflow) - return; - auto request = std::make_shared(); - request->key = key; - request->color_map = color_scale->color_map; - request->value_start_coord = value_start_coord; - request->rate = rate; - request->lower_stream_tick = timeline.first_stream_tick; - request->upper_stream_tick = timeline.last_stream_tick; - request->visible_time_count = static_cast(timeline.times.size()); - request->newest_at_axis_start = timeline.newest_at_axis_start; - request->draw_frequency_range = h_range; - request->draw_time_range = v_range; - request->draw_source_rect = RectF(source_start, 0.0, source_end - source_start, static_cast(row_count)); - request->rows.reserve(static_cast(buffer_data_count)); - for (int row = 0; row < buffer_data_count; ++row) { - int buffer_row = buffer_data_count - row - 1; - int stream_tick = ring_buffer.tick(buffer_row); - int image_coord{}; - if (!waterfall_request_coord_by_stream_tick(*request, stream_tick, image_coord)) - continue; - const double* list = ring_buffer.frequency_data(buffer_row, first_col, last_col); - Waterfall_Data image_row; - image_row.tick = stream_tick; - image_row.frequency.assign(list + first_col, list + last_col + 1); - request->rows.push_back(std::move(image_row)); - } - request->update_states = std::move(pending_image_update_states); - pending_image_update_states = std::pmr::vector>(memory_resource(Memory_Domain::Update_Completion)); - auto job = create_waterfall_image_job(std::move(request)); - if (!job || job->tiles.empty()) - return; - std::vector tile_tasks; - tile_tasks.reserve(job->tiles.size()); - for (std::size_t i = 0; i < job->tiles.size(); ++i) { - tile_tasks.push_back(subflow->emplace(Render_Task_Kind::Waterfall, Task([job, i]() { - write_waterfall_tile(*job, static_cast(i)); - }))); - } - auto compose = subflow->emplace(Render_Task_Kind::Compose, Task([this, job]() { - if (q() && q()->get_retiring()) - return; - Waterfall_Image_Snapshot result = compose_waterfall_image(*job); - if (q() && q()->get_retiring()) { - complete_waterfall_result_states(result, std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - return; - } - image_snapshot = std::move(result); - submitted_image_key = image_snapshot->key; - })); - for (auto tile : tile_tasks) - subflow->precede(tile, compose); - } - } - void draw_image(Canvas& canvas, const Render_Frame_Snapshot& snapshot, Waterfall_Render_State* s, Abs_Axis* h_axis, Time_Axis* v_axis) { - if (!image_snapshot || image_snapshot->image.empty()) - return; - if (image_snapshot->frequency_range.length() == 0.0 || image_snapshot->time_range.length() == 0.0) - return; - if (image_snapshot->source_rect.width == 0.0 || image_snapshot->source_rect.height == 0.0) - return; - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(h_axis, v_axis, snapshot); - Axis_Basis_2D basis = Axis_Basis_2D::from_ranges(mapping, image_snapshot->frequency_range, image_snapshot->time_range); - canvas.save(); - canvas.set_image_interpolation(s->interpolation_mode); - canvas.transform( - basis.domain_vector.x / image_snapshot->source_rect.width, - basis.domain_vector.y / image_snapshot->source_rect.width, - basis.value_vector.x / image_snapshot->source_rect.height, - basis.value_vector.y / image_snapshot->source_rect.height, - basis.origin.x, - basis.origin.y); - canvas.draw_image(RectF{0.0, 0.0, image_snapshot->source_rect.width, image_snapshot->source_rect.height}, image_snapshot->image, image_snapshot->source_rect); - canvas.restore(); - } - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Waterfall_Render_State* s = render_state(frame_view); - if (!s) - return; - auto frequency_axis = s->frequency_axis.lock(); - auto time_axis = s->time_axis.lock(); - if (!frequency_axis || !time_axis) - return; - if (snapshot.frame_update_states && image_snapshot) { - for (const auto& state : image_snapshot->update_states) - snapshot.frame_update_states->push_back(state); - image_snapshot->update_states.clear(); - } - Abs_Axis* h_axis = frequency_axis.get(); - Time_Axis* v_axis = time_axis.get(); - draw_image(canvas, snapshot, s, h_axis, v_axis); - if (s->can_show_hover(q())) { - s->draw_hover_tooltip(&canvas, h_axis, v_axis, q(), &snapshot); - } - } -}; -} // namespace renderive diff --git a/Core/plottable/export.h b/Core/plottable/export.h deleted file mode 100644 index e5cb14f..0000000 --- a/Core/plottable/export.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#include -#include -#include -#include "Afterglow.h" -#include "Frequency_Trace.h" -#include "Hover_Tooltip.h" -#include "Interpolation.h" -#include "Selection_Rectangle_Overlay.h" -#include "Constellation_Diagram.h" -#include "Performance_Overlay.h" -#include "Spectrum.h" -#include "Sweep_Spectrum.h" -#include "Waterfall.h" -namespace renderive { -class Plot_Core; -LIB_DECL std::shared_ptr attach_performance_overlay(Plot_Core& plot); -LIB_DECL std::shared_ptr attach_performance_overlay(Plot_Core& plot, const Performance_Overlay_Options& options); -LIB_DECL std::shared_ptr performance_overlay(Plot_Core& plot); -LIB_DECL void set_performance_plot_name(Plot_Core& plot, std::string name); -LIB_DECL void detach_performance_overlay(Plot_Core& plot); -LIB_DECL bool performance_overlay_enabled(const Plot_Core& plot); -LIB_DECL void set_performance_overlay_enabled(Plot_Core& plot, bool enabled); -LIB_DECL std::shared_ptr performance_overlay_snapshot(const Plot_Core& plot); -LIB_DECL void set_performance_overlay_update_callback(Plot_Core& plot, std::function callback); -LIB_DECL void set_performance_overlay_clipboard_callback(Plot_Core& plot, std::function callback); -LIB_DECL void notify_performance_overlay_viewport_changed(Plot_Core& plot); -LIB_DECL bool performance_overlay_handle_wheel(Plot_Core& plot, PointF position, double angle_delta_y, double pixel_delta_y); -LIB_DECL bool performance_overlay_handle_pointer_press(Plot_Core& plot, PointF position); -LIB_DECL bool performance_overlay_handle_pointer_move(Plot_Core& plot, PointF position); -LIB_DECL bool performance_overlay_handle_pointer_release(Plot_Core& plot, PointF position); -LIB_DECL bool performance_overlay_handle_pointer_leave(Plot_Core& plot); -} // namespace renderive diff --git a/Core/plottable/global.h b/Core/plottable/global.h deleted file mode 100644 index 293d499..0000000 --- a/Core/plottable/global.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include "../Axis/export.h" diff --git a/Core/primitive/Band_Region.cpp b/Core/primitive/Band_Region.cpp deleted file mode 100644 index 966e210..0000000 --- a/Core/primitive/Band_Region.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "Band_Region.h" -#include -#include -#include -#include -#include "../Axis/Abs_Axis_p.h" -#include "../base/Memory.h" -#include "../render/Canvas.h" -namespace renderive { -struct Band_Region_Render_State : Render_State, Band_Region_Prop {}; -struct Band_Region_Private : Typed_Render_Data { - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Band_Region_Render_State* s = render_state(frame_view); - if (!s) - return; - auto domain_axis = s->domain_axis.lock(); - auto value_axis = s->value_axis.lock(); - if (!domain_axis || !value_axis || s->range.length() == 0.0) - return; - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(domain_axis.get(), value_axis.get(), snapshot); - std::array points{}; - if (s->orientation == Band_Axis::Domain) { - points = { - mapping.map(s->range.origin, mapping.value.coord_range.origin), - mapping.map(s->range.target, mapping.value.coord_range.origin), - mapping.map(s->range.target, mapping.value.coord_range.target), - mapping.map(s->range.origin, mapping.value.coord_range.target)}; - } - else { - points = { - mapping.map(mapping.domain.coord_range.origin, s->range.origin), - mapping.map(mapping.domain.coord_range.target, s->range.origin), - mapping.map(mapping.domain.coord_range.target, s->range.target), - mapping.map(mapping.domain.coord_range.origin, s->range.target)}; - } - canvas.save(); - canvas.set_pen(s->pen); - canvas.set_brush(s->brush); - canvas.draw_polygon(std::span(points.data(), points.size()), true); - canvas.restore(); - } -}; -RENDERIVE_DEFINE_RENDERABLE_BINDING(Band_Region, Band_Region_Private, Band_Region_Render_State, Input_Data, "BandRegion") -RENDERIVE_DEFINE_WEAK_STATE_PROP(Band_Region, Band_Region_Render_State, Abs_Axis, domain_axis) -RENDERIVE_DEFINE_WEAK_STATE_PROP(Band_Region, Band_Region_Render_State, Abs_Axis, value_axis) -Range Band_Region::range() { - return d()->edit_state_value(&Band_Region_Render_State::range); -} -void Band_Region::set_range(Range value) { - if (value.length() == 0.0) - return; - d()->set_state_value(&Band_Region_Render_State::range, value); -} -RENDERIVE_DEFINE_STATE_PROP(Band_Region, Band_Region_Render_State, Band_Axis, orientation) -RENDERIVE_DEFINE_STATE_PROP(Band_Region, Band_Region_Render_State, Brush, brush) -RENDERIVE_DEFINE_STATE_PROP(Band_Region, Band_Region_Render_State, Pen, pen) -Band_Region::Builder::Builder(std::shared_ptr parent, std::shared_ptr domain_axis, std::shared_ptr value_axis) { - ASSERT(parent, "Band_Region build error! parent == nullptr"); - ASSERT(domain_axis && value_axis, "Band_Region build error! axis == nullptr"); - ASSERT(domain_axis->attached_to_plot(), "Band_Region build error! domain_axis->plot == nullptr"); - ASSERT(domain_axis->shares_plot_with(*value_axis), "Band_Region build error! domain_axis->plot != value_axis->plot"); - ASSERT(parent->shares_plot_with(*domain_axis), "Band_Region build error! parent->plot != axis->plot"); - ASSERT(domain_axis->orientation() != value_axis->orientation(), "Band_Region build error! axes must be orthogonal"); - this->parent = parent; - this->domain_axis = domain_axis; - this->value_axis = value_axis; -} -std::shared_ptr Band_Region::Builder::build() { - auto ret = renderive::make_shared(); - ret->init(parent); - Band_Region_Private* pd = ret->d(); - Render_Edit_Lease edit(pd); - Band_Region_Render_State* sc = edit.operator->(); - PROP_R(std::weak_ptr, domain_axis) - PROP_R(std::weak_ptr, value_axis) - PROP_R(Range, range) - PROP_R(Band_Axis, orientation) - PROP_R(Brush, brush) - PROP_R(Pen, pen) - if (sc->range.length() == 0.0) - sc->range = {0.0, 1.0}; - return ret; -} -} // namespace renderive diff --git a/Core/primitive/Band_Region.h b/Core/primitive/Band_Region.h deleted file mode 100644 index fc42475..0000000 --- a/Core/primitive/Band_Region.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once -#include -#include "../architecture/Renderable.h" -#include "../Axis/Abs_Axis.h" -#include "../base/Style.h" -namespace renderive { -struct Band_Region_Private; -/// @brief 带状区域方向。 -enum class Band_Axis : std::uint8_t { - /// @brief 沿 X 轴范围绘制竖向区域。 - Domain, - /// @brief 沿 Y 轴范围绘制横向区域。 - Value -}; -/// @brief 带状区域构造属性。 -struct Band_Region_Prop { - std::weak_ptr domain_axis{}; - std::weak_ptr value_axis{}; - Range range{0.0, 1.0}; - Band_Axis orientation = Band_Axis::Domain; - Brush brush = Brush{Color{255, 255, 255, 48}, Brush_Style::Solid}; - Pen pen = Pen{.style = Line_Style::None}; -}; -/// @brief 在两个坐标轴之间绘制指定范围的带状区域。 -class LIB_DECL Band_Region : public Renderable { -public: - /// @brief 绑定 X 轴。 - PROP(std::shared_ptr, domain_axis) - /// @brief 绑定 Y 轴。 - PROP(std::shared_ptr, value_axis) - /// @brief 数据区域范围。 - PROP(Range, range) - /// @brief 带状区域方向。 - PROP(Band_Axis, orientation) - /// @brief 区域填充刷。 - PROP(Brush, brush) - /// @brief 区域边框画笔。 - PROP(Pen, pen) - struct Builder; - /// @brief 创建带状区域图元。 - Band_Region(); -private: - Band_Region_Private* d(); - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; -}; -/// @brief 带状区域构造器。 -struct LIB_DECL Band_Region::Builder : Band_Region_Prop { - PROP_B(std::shared_ptr, parent) - PROP_B(std::shared_ptr, domain_axis) - PROP_B(std::shared_ptr, value_axis) - PROP_B(Range, range) - PROP_B(Band_Axis, orientation) - PROP_B(Brush, brush) - PROP_B(Pen, pen) - /// @brief 创建带状区域构造器。 - Builder(std::shared_ptr parent, std::shared_ptr domain_axis, std::shared_ptr value_axis); - /// @brief 构建并返回带状区域对象。 - std::shared_ptr build(); - std::shared_ptr parent{}; -}; -} // namespace renderive diff --git a/Core/primitive/Color_Bar.cpp b/Core/primitive/Color_Bar.cpp deleted file mode 100644 index 967586c..0000000 --- a/Core/primitive/Color_Bar.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include "Color_Bar.h" -#include -#include -#include -#include -#include "../base/Memory.h" -#include "../base/String_Format.h" -#include "../architecture/Render_Lease.h" -#include "../render/Canvas.h" -#include "../render/Image.h" -#include "../render/Render_Frame_Snapshot.h" - -namespace renderive { - -struct Color_Bar_Render_State : Render_State, Color_Bar_Prop {}; - -struct Color_Bar_Private : Typed_Render_Data { - RectF default_rect(const Size& size, Orientation orientation) const { - if (orientation == Orientation::Horizontal) - return RectF{20.0, static_cast(std::max(0, size.height - 40)), static_cast(std::max(1, size.width - 40)), 16.0}; - return RectF{static_cast(std::max(0, size.width - 48)), 20.0, 16.0, static_cast(std::max(1, size.height - 40))}; - } - - void fill_gradient(Image& image, const Color_Map& color_map, Orientation orientation) const { - int w = image.width(); - int h = image.height(); - if (orientation == Orientation::Horizontal) { - int denom = std::max(1, w - 1); - for (int x = 0; x < w; ++x) { - Pixel color = color_map.at_normalized(static_cast(x) / static_cast(denom)); - for (int y = 0; y < h; ++y) - image.row(y)[x] = color; - } - return; - } - int denom = std::max(1, h - 1); - for (int y = 0; y < h; ++y) { - Pixel color = color_map.at_normalized(1.0 - static_cast(y) / static_cast(denom)); - for (int x = 0; x < w; ++x) - image.row(y)[x] = color; - } - } - - std::string value_text(double value, const std::string& unit) const { - std::string ret = format_number(value, Number_Format{Number_Notation::General, 6}); - if (!unit.empty()) - ret += " " + unit; - return ret; - } - - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Color_Bar_Render_State* s = render_state(frame_view); - if (!s) - return; - auto scale = s->color_scale.snapshot(); - if (scale->range.length() == 0.0) - return; - RectF bar_rect = s->pixel_rect.empty() ? default_rect(snapshot.size, s->orientation) : s->pixel_rect; - if (bar_rect.width <= 0.0 || bar_rect.height <= 0.0) - return; - int iw = std::max(1, static_cast(bar_rect.width)); - int ih = std::max(1, static_cast(bar_rect.height)); - Image image(iw, ih); - fill_gradient(image, scale->color_map, s->orientation); - - canvas.save(); - canvas.set_font(s->font); - canvas.draw_image(bar_rect, image); - - canvas.set_pen(s->border_pen); - canvas.set_brush(Brush{}); - canvas.draw_rect(bar_rect); - - canvas.set_pen(s->text_pen); - Text_Metrics metrics = canvas.measure_text("M"); - std::string origin_text = value_text(scale->range.origin, s->unit); - std::string target_text = value_text(scale->range.target, s->unit); - Text_Metrics tm_target = canvas.measure_text(target_text); - - if (s->orientation == Orientation::Horizontal) { - canvas.draw_text(PointF{bar_rect.x, bar_rect.bottom() + metrics.height}, origin_text); - canvas.draw_text(PointF{bar_rect.right() - tm_target.width, bar_rect.bottom() + metrics.height}, target_text); - if (!s->name.empty()) - canvas.draw_text(PointF{bar_rect.x, bar_rect.y - 4.0}, s->name); - } else { - canvas.draw_text(PointF{bar_rect.right() + 4.0, bar_rect.y + metrics.ascent}, target_text); - canvas.draw_text(PointF{bar_rect.right() + 4.0, bar_rect.bottom()}, origin_text); - if (!s->name.empty()) - canvas.draw_text(PointF{bar_rect.x, bar_rect.y - 4.0}, s->name); - } - canvas.restore(); - } -}; - -RENDERIVE_DEFINE_RENDERABLE_BINDING(Color_Bar, Color_Bar_Private, Color_Bar_Render_State, Input_Data, "ColorBar") - -Color_Scale Color_Bar::color_scale() { - return d()->edit_state_value(&Color_Bar_Render_State::color_scale); -} - -void Color_Bar::set_color_scale(Color_Scale value) { - d()->set_state_value(&Color_Bar_Render_State::color_scale, std::move(value)); -} - -Color_Map Color_Bar::color_map() { - return d()->edit_state_value(&Color_Bar_Render_State::color_scale).color_map(); -} - -void Color_Bar::set_color_map(Color_Map value) { - Render_Edit_Lease edit(d()); - edit->color_scale.set_color_map(std::move(value)); -} - -Range Color_Bar::range() { - return d()->edit_state_value(&Color_Bar_Render_State::color_scale).range(); -} - -void Color_Bar::set_range(Range value) { - if (value.size() == 0.0) - return; - Render_Edit_Lease edit(d()); - edit->color_scale.set_range(value); -} - -RENDERIVE_DEFINE_STATE_PROP(Color_Bar, Color_Bar_Render_State, std::string, name) -RENDERIVE_DEFINE_STATE_PROP(Color_Bar, Color_Bar_Render_State, std::string, unit) -RENDERIVE_DEFINE_STATE_PROP(Color_Bar, Color_Bar_Render_State, RectF, pixel_rect) -RENDERIVE_DEFINE_STATE_PROP(Color_Bar, Color_Bar_Render_State, Orientation, orientation) -RENDERIVE_DEFINE_STATE_PROP(Color_Bar, Color_Bar_Render_State, Font, font) -RENDERIVE_DEFINE_STATE_PROP(Color_Bar, Color_Bar_Render_State, Pen, text_pen) -RENDERIVE_DEFINE_STATE_PROP(Color_Bar, Color_Bar_Render_State, Pen, border_pen) - -Color_Bar::Builder::Builder(std::shared_ptr parent) { - ASSERT(parent && parent->attached_to_plot(), "Color_Bar build error! parent invalid"); - this->parent = parent; -} - -Color_Bar::Builder& Color_Bar::Builder::set_color_map(Color_Map value) { - color_scale.set_color_map(std::move(value)); - return *this; -} - -Color_Bar::Builder& Color_Bar::Builder::set_range(Range value) { - color_scale.set_range(value); - return *this; -} - -std::shared_ptr Color_Bar::Builder::build() { - auto ret = renderive::make_shared(); - ret->init(parent); - Color_Bar_Private* pd = ret->d(); - Render_Edit_Lease edit(pd); - Color_Bar_Render_State* sc = edit.operator->(); - sc->color_scale = color_scale.clone(); - PROP_R(std::string, name) - PROP_R(std::string, unit) - PROP_R(RectF, pixel_rect) - PROP_R(Orientation, orientation) - PROP_R(Font, font) - PROP_R(Pen, text_pen) - PROP_R(Pen, border_pen) - return ret; -} - -} // namespace renderive diff --git a/Core/primitive/Color_Bar.h b/Core/primitive/Color_Bar.h deleted file mode 100644 index e1dfdf6..0000000 --- a/Core/primitive/Color_Bar.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once -#include -#include -#include "../architecture/Renderable.h" -#include "../base/Geometry.h" -#include "../base/Style.h" -#include "../base/Text.h" -#include "Color_Scale.h" - -namespace renderive { - -struct Color_Bar_Private; - -struct Color_Bar_Prop { - Color_Scale color_scale{}; - std::string name{}; - std::string unit{}; - RectF pixel_rect{}; - Orientation orientation = Orientation::Vertical; - Font font{}; - Pen text_pen{}; - Pen border_pen{.color = Color{255, 255, 255, 120}}; -}; - -class LIB_DECL Color_Bar : public Renderable { -public: - Color_Scale color_scale(); - void set_color_scale(Color_Scale value); - Color_Map color_map(); - void set_color_map(Color_Map value); - Range range(); - void set_range(Range value); - PROP(std::string, name) - PROP(std::string, unit) - PROP(RectF, pixel_rect) - PROP(Orientation, orientation) - PROP(Font, font) - PROP(Pen, text_pen) - PROP(Pen, border_pen) - - struct Builder; - Color_Bar(); - -private: - Color_Bar_Private* d(); - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; -}; - -struct LIB_DECL Color_Bar::Builder : Color_Bar_Prop { - PROP_B(std::shared_ptr, parent) - PROP_B(Color_Scale, color_scale) - PROP_B(std::string, name) - PROP_B(std::string, unit) - PROP_B(RectF, pixel_rect) - PROP_B(Orientation, orientation) - PROP_B(Font, font) - PROP_B(Pen, text_pen) - PROP_B(Pen, border_pen) - - explicit Builder(std::shared_ptr parent); - Builder& set_color_map(Color_Map value); - Builder& set_range(Range value); - std::shared_ptr build(); - - std::shared_ptr parent{}; -}; - -} // namespace renderive diff --git a/Core/primitive/Color_Map.h b/Core/primitive/Color_Map.h deleted file mode 100644 index 35b261c..0000000 --- a/Core/primitive/Color_Map.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include "../architecture/global.h" -#include "../base/Color.h" -namespace renderive { -/// @brief 将归一化数值映射到颜色的查找表。 -class LIB_DECL Color_Map { -public: - /// @brief 使用默认 Turbo 色表创建颜色映射。 - Color_Map() : color_values(get_turbo_color_gradient()) { - ensure_not_empty(); - rebuild_hash(); - } - /// @brief 使用指定颜色序列创建颜色映射。 - explicit Color_Map(std::vector colors) : color_values(std::move(colors)) { - ensure_not_empty(); - rebuild_hash(); - } - /// @brief 返回当前颜色序列。 - [[nodiscard]] const std::vector& colors() const { - return color_values; - } - /// @brief 替换当前颜色序列。 - void set_colors(std::vector colors) { - color_values = std::move(colors); - ensure_not_empty(); - rebuild_hash(); - } - /// @brief 返回颜色数量。 - [[nodiscard]] int size() const { - return static_cast(color_values.size()); - } - /// @brief 按离散偏移取颜色,偏移会裁剪到有效范围。 - [[nodiscard]] Pixel at_offset(int offset) const { - return color_values.at(static_cast(std::clamp(offset, 0, size() - 1))); - } - /// @brief 按 0..1 归一化数值取颜色。 - [[nodiscard]] Pixel at_normalized(double value) const { - return at_offset(static_cast(value * static_cast(color_values.size() - 1))); - } - /// @brief 返回当前色表内容哈希。 - [[nodiscard]] std::uint64_t hash() const { - return color_hash; - } -private: - void ensure_not_empty() { - if (color_values.empty()) - color_values = get_turbo_color_gradient(); - if (color_values.empty()) - color_values.push_back(pack_rgba(0, 0, 0, 255)); - } - void rebuild_hash() { - std::uint64_t ret = 1469598103934665603ull; - for (Pixel value : color_values) { - ret ^= static_cast(value); - ret *= 1099511628211ull; - } - color_hash = ret; - } - std::vector color_values; - std::uint64_t color_hash{}; -}; -} // namespace renderive diff --git a/Core/primitive/Color_Scale.h b/Core/primitive/Color_Scale.h deleted file mode 100644 index c6cefa5..0000000 --- a/Core/primitive/Color_Scale.h +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "../base/Geometry.h" -#include "Color_Map.h" - -namespace renderive { - -struct Color_Scale_Snapshot { - Color_Map color_map{}; - Range range{0.0, 1.0}; -}; - -class LIB_DECL Color_Scale { -private: - struct Model; - -public: - Color_Scale() : model(std::make_shared(Color_Scale_Snapshot{})) {} - - Color_Scale(Color_Map color_map, Range range) - : model(std::make_shared(normalized(Color_Scale_Snapshot{std::move(color_map), range}))) {} - - [[nodiscard]] std::shared_ptr snapshot() const { - if (!model) { - static const auto fallback = std::make_shared(); - return fallback; - } - return model->current.load(std::memory_order_acquire); - } - - [[nodiscard]] Color_Map color_map() const { - return snapshot()->color_map; - } - - [[nodiscard]] Range range() const { - return snapshot()->range; - } - - void set_color_map(Color_Map value) { - ensure_model(); - auto current = snapshot(); - Color_Scale_Snapshot next{std::move(value), current->range}; - model->current.store(std::make_shared(normalized(std::move(next))), std::memory_order_release); - } - - void set_range(Range value) { - if (value.size() == 0.0) - return; - ensure_model(); - auto current = snapshot(); - Color_Scale_Snapshot next{current->color_map, value}; - model->current.store(std::make_shared(normalized(std::move(next))), std::memory_order_release); - } - - [[nodiscard]] Color_Scale clone() const { - return Color_Scale(std::make_shared(*snapshot())); - } - -private: - struct Model { - std::atomic> current; - - explicit Model(Color_Scale_Snapshot snapshot) { - current.store(std::make_shared(normalized(std::move(snapshot))), std::memory_order_release); - } - }; - - explicit Color_Scale(std::shared_ptr model) : model(std::move(model)) {} - - static Color_Scale_Snapshot normalized(Color_Scale_Snapshot value) { - if (value.range.size() == 0.0) - value.range = Range{0.0, 1.0}; - return value; - } - - void ensure_model() { - if (!model) - model = std::make_shared(Color_Scale_Snapshot{}); - } - - std::shared_ptr model; -}; - -} // namespace renderive diff --git a/Core/primitive/Curve.cpp b/Core/primitive/Curve.cpp deleted file mode 100644 index 6555f54..0000000 --- a/Core/primitive/Curve.cpp +++ /dev/null @@ -1,694 +0,0 @@ -#include "Curve.h" -#include "Curve_Utils_p.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../base/Memory.h" -#include "../base/global.h" -#include "../render/Canvas.h" -#include "../render/Frame_Prepare_Subflow.h" -#include "../render/Render_Frame_Snapshot.h" -namespace renderive { -enum class Curve_Input_Mode : std::uint8_t { - None, - Samples, - Points -}; -struct Curve_Input_Data : Input_Data {}; -struct Curve_Render_State : Render_State, Curve_Prop {}; -struct Curve_Sample_Source { - Curve_Sample_Source() : values(memory_resource(Memory_Domain::Curve)) {} - explicit Curve_Sample_Source(std::span input) : values(memory_resource(Memory_Domain::Curve)) { - values.assign(input.begin(), input.end()); - } - explicit Curve_Sample_Source(std::pmr::vector&& input) : values(std::move(input)) {} - std::pmr::vector values; -}; -struct Curve_Point_Source { - Curve_Point_Source() : values(memory_resource(Memory_Domain::Curve)) {} - explicit Curve_Point_Source(std::span input) : values(memory_resource(Memory_Domain::Curve)) { - values.assign(input.begin(), input.end()); - } - explicit Curve_Point_Source(std::pmr::vector&& input) : values(std::move(input)) {} - std::pmr::vector values; -}; -using Curve_Source = std::variant; -static Curve_Input_Mode curve_source_mode(const Curve_Source& source) { - if (std::holds_alternative(source)) - return Curve_Input_Mode::Samples; - if (std::holds_alternative(source)) - return Curve_Input_Mode::Points; - return Curve_Input_Mode::None; -} -static std::size_t curve_sample_count(const Curve_Source& source) { - const auto* samples = std::get_if(&source); - return samples ? samples->values.size() : 0u; -} -static std::size_t curve_point_count(const Curve_Source& source) { - const auto* points = std::get_if(&source); - return points ? points->values.size() : 0u; -} -struct Curve_Input_Block { - std::shared_ptr source = std::make_shared(); - std::shared_ptr completion_state; - static std::shared_ptr samples_block(std::span values, std::shared_ptr state = {}) { - auto block = std::make_shared(); - block->source = std::make_shared(Curve_Sample_Source(values)); - block->completion_state = std::move(state); - return block; - } - static std::shared_ptr samples_block(std::pmr::vector&& values, std::shared_ptr state = {}) { - auto block = std::make_shared(); - block->source = std::make_shared(Curve_Sample_Source(std::move(values))); - block->completion_state = std::move(state); - return block; - } - static std::shared_ptr points_block(std::span values, std::shared_ptr state = {}) { - auto block = std::make_shared(); - block->source = std::make_shared(Curve_Point_Source(values)); - block->completion_state = std::move(state); - return block; - } - static std::shared_ptr points_block(std::pmr::vector&& values, std::shared_ptr state = {}) { - auto block = std::make_shared(); - block->source = std::make_shared(Curve_Point_Source(std::move(values))); - block->completion_state = std::move(state); - return block; - } -}; -struct Curve_Axis_Geometry_Key { - Orientation orientation = Orientation::Horizontal; - Range coord_range{0.0, 1.0}; - Range pixel_range{0.0, 1.0}; - bool operator==(const Curve_Axis_Geometry_Key& other) const { - return orientation == other.orientation && coord_range == other.coord_range && pixel_range == other.pixel_range; - } -}; -struct Curve_Geometry_Key { - std::uint64_t input_generation{}; - std::uint64_t state_version{}; - Curve_Input_Mode mode = Curve_Input_Mode::None; - Line_Sampling_Strategy sample_mode = Line_Sampling_Strategy::Pixel_Min_Max_Envelope; - Line_Interpolation_Mode interpolation_mode = Line_Interpolation_Mode::Linear_Value; - bool visible_range_only{}; - Range data_range{0.0, 1.0}; - std::size_t sample_count{}; - std::size_t point_count{}; - Curve_Axis_Geometry_Key domain; - Curve_Axis_Geometry_Key value; - bool operator==(const Curve_Geometry_Key& other) const { - return input_generation == other.input_generation && - state_version == other.state_version && - mode == other.mode && - sample_mode == other.sample_mode && - interpolation_mode == other.interpolation_mode && - visible_range_only == other.visible_range_only && - data_range == other.data_range && - sample_count == other.sample_count && - point_count == other.point_count && - domain == other.domain && - value == other.value; - } -}; -struct Curve_Geometry_Result { - struct Segment { - explicit Segment(std::pmr::memory_resource* resource = memory_resource(Memory_Domain::Curve)) : points(resource) {} - std::pmr::vector points; - }; - - Curve_Geometry_Result() : segments(memory_resource(Memory_Domain::Curve)), update_states(memory_resource(Memory_Domain::Update_Completion)) {} - Axis_Mapping_2D mapping; - std::pmr::vector segments; - std::pmr::vector> update_states; -}; -struct Curve_Geometry_Request { - Curve_Geometry_Request() : update_states(memory_resource(Memory_Domain::Update_Completion)) {} - Curve_Input_Mode mode = Curve_Input_Mode::None; - Axis_Mapping_2D mapping; - Range data_range{0.0, 1.0}; - bool visible_range_only{}; - Line_Interpolation_Mode interpolation_mode = Line_Interpolation_Mode::Linear_Value; - Line_Sampling_Strategy sample_mode = Line_Sampling_Strategy::Pixel_Min_Max_Envelope; - std::shared_ptr source; - std::pmr::vector> update_states; -}; -struct Curve_Geometry_Partition { - std::size_t source_begin{}; - std::size_t source_end{}; - int pixel_begin{}; - int pixel_end{}; - bool joins_previous{}; -}; -struct Curve_Geometry_Partial_Result { - Curve_Geometry_Partial_Result() - : segments(memory_resource(Memory_Domain::Curve)) {} - std::pmr::vector segments; - std::size_t source_begin{}; - std::size_t source_end{}; - bool begins_with_continuous_point{}; - bool ends_with_continuous_point{}; - bool joins_previous{}; -}; -struct Curve_Geometry_Job { - explicit Curve_Geometry_Job(std::shared_ptr request, Curve_Geometry_Key key) - : request(std::move(request)), - key(std::move(key)), - update_states(memory_resource(Memory_Domain::Update_Completion)) {} - ~Curve_Geometry_Job() { - for (auto& state : update_states) { - if (state) - state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - } - update_states.clear(); - } - std::shared_ptr request; - Curve_Geometry_Key key; - std::vector partitions; - std::vector results; - std::pmr::vector> update_states; -}; -static constexpr std::size_t Min_Curve_Items_Per_Task = 4096; -static constexpr std::size_t Max_Curve_Partition_Count = 8; -static Curve_Axis_Geometry_Key curve_axis_key(const Axis_Frame_Snapshot& axis) { - return {axis.orientation, axis.coord_range, axis.pixel_range}; -} -static void complete_curve_result_states(Curve_Geometry_Result& result, std::error_code error, Update_Outcome outcome) { - for (const auto& state : result.update_states) { - if (state) - state->complete_all(error, outcome); - } - result.update_states.clear(); -} - -static bool finite_point(const PointF& point) { - return std::isfinite(point.x) && std::isfinite(point.y); -} - -static void append_curve_point_segment(Curve_Geometry_Result& result, Curve_Geometry_Result::Segment*& segment, const Axis_Mapping_2D& mapping, const PointF& point) { - if (!finite_point(point)) { - segment = nullptr; - return; - } - PointF mapped = mapping.map(point); - if (!finite_point(mapped)) { - segment = nullptr; - return; - } - if (!segment) { - result.segments.emplace_back(memory_resource(Memory_Domain::Curve)); - segment = &result.segments.back(); - } - if (!segment->points.empty() && segment->points.back().x == mapped.x && segment->points.back().y == mapped.y) - return; - segment->points.push_back(mapped); -} - -static void append_curve_sample_run(Curve_Geometry_Result& result, const Curve_Geometry_Request& request, const Curve_Sample_Source& samples, int run_start, int run_end) { - if (run_start > run_end) - return; - auto coord_at = [&](int index) { - return source_index_to_coordinate(request.data_range, static_cast(samples.values.size()), index); - }; - if (run_start == run_end) { - double coord = coord_at(run_start); - if (request.visible_range_only && !request.mapping.domain.coord_range.contains(coord)) - return; - PointF point = request.mapping.map(coord, samples.values[static_cast(run_start)]); - if (!finite_point(point)) - return; - result.segments.emplace_back(memory_resource(Memory_Domain::Curve)); - result.segments.back().points.push_back(point); - return; - } - int run_count = run_end - run_start + 1; - Range run_range{coord_at(run_start), coord_at(run_end)}; - auto value_at = [&samples, run_start](int i) { - return samples.values[static_cast(run_start + i)]; - }; - std::pmr::vector points(memory_resource(Memory_Domain::Curve)); - if (request.sample_mode == Line_Sampling_Strategy::Pixel_Min_Max_Envelope) { - points = Curve_Utils::build_pixel_envelope_points(request.mapping, run_range, run_count, run_count, request.visible_range_only, value_at, memory_resource(Memory_Domain::Curve)); - } - else { - points = Curve_Utils::build_resampled_points(request.mapping, run_range, run_count, run_count, request.visible_range_only, request.interpolation_mode, value_at, memory_resource(Memory_Domain::Curve)); - } - if (points.empty()) - return; - result.segments.emplace_back(memory_resource(Memory_Domain::Curve)); - result.segments.back().points = std::move(points); -} - -std::size_t resolve_curve_partition_count(std::size_t source_count, std::size_t worker_count) { - if (source_count == 0) - return 0; - if (worker_count <= 1 || source_count < Min_Curve_Items_Per_Task) - return 1; - std::size_t by_size = (source_count + Min_Curve_Items_Per_Task - 1) / Min_Curve_Items_Per_Task; - return std::max(1, std::min({worker_count, by_size, Max_Curve_Partition_Count})); -} - -static std::size_t curve_request_source_count(const Curve_Geometry_Request& request) { - if (!request.source) - return 0; - if (const auto* data_points = std::get_if(request.source.get())) - return data_points->values.size(); - if (const auto* samples = std::get_if(request.source.get())) - return samples->values.size(); - return 0; -} - -static bool curve_sample_valid(const Curve_Geometry_Request& request, std::size_t index) { - if (!request.source) - return false; - if (const auto* data_points = std::get_if(request.source.get())) { - if (index >= data_points->values.size()) - return false; - PointF point = data_points->values[index]; - return finite_point(point) && finite_point(request.mapping.map(point)); - } - if (const auto* samples = std::get_if(request.source.get())) { - if (index >= samples->values.size()) - return false; - double value = samples->values[index]; - if (!std::isfinite(value)) - return false; - double coord = source_index_to_coordinate(request.data_range, static_cast(samples->values.size()), static_cast(index)); - return finite_point(request.mapping.map(coord, value)); - } - return false; -} - -static std::shared_ptr create_curve_geometry_job(std::shared_ptr request, Curve_Geometry_Key key, std::size_t worker_count) { - if (!request) - return {}; - std::size_t source_count = curve_request_source_count(*request); - std::size_t partition_count = resolve_curve_partition_count(source_count, worker_count); - std::pmr::vector> update_states = std::move(request->update_states); - auto job = std::make_shared( - std::shared_ptr(std::move(request)), - std::move(key)); - job->update_states = std::move(update_states); - job->partitions.reserve(partition_count); - job->results.resize(partition_count); - for (std::size_t i = 0; i < partition_count; ++i) { - std::size_t begin = source_count * i / partition_count; - std::size_t end = source_count * (i + 1) / partition_count; - Curve_Geometry_Partition partition; - partition.source_begin = begin; - partition.source_end = end; - partition.joins_previous = begin > 0 && curve_sample_valid(*job->request, begin - 1) && curve_sample_valid(*job->request, begin); - job->partitions.push_back(partition); - } - return job; -} - -static void build_curve_partition(Curve_Geometry_Job& job, std::size_t partition_index) { - if (!job.request || partition_index >= job.partitions.size() || partition_index >= job.results.size()) - return; - const Curve_Geometry_Request& request = *job.request; - const Curve_Geometry_Partition& partition = job.partitions[partition_index]; - Curve_Geometry_Partial_Result partial; - partial.source_begin = partition.source_begin; - partial.source_end = partition.source_end; - partial.joins_previous = partition.joins_previous; - Curve_Geometry_Result result; - result.mapping = request.mapping; - if (!request.source || partition.source_begin >= partition.source_end) { - job.results[partition_index] = std::move(partial); - return; - } - if (const auto* data_points = std::get_if(request.source.get())) { - result.segments.reserve(partition.source_end - partition.source_begin); - Curve_Geometry_Result::Segment* segment = nullptr; - for (std::size_t i = partition.source_begin; i < partition.source_end && i < data_points->values.size(); ++i) - append_curve_point_segment(result, segment, request.mapping, data_points->values[i]); - } - else if (const auto* samples = std::get_if(request.source.get())) { - if (samples->values.size() >= 2 && request.data_range.length() != 0.0) { - std::size_t compute_begin = partition.source_begin; - if (partition.joins_previous && compute_begin > 0) - --compute_begin; - int run_start = -1; - std::size_t end = std::min(partition.source_end, samples->values.size()); - for (std::size_t i = compute_begin; i < end; ++i) { - double value = samples->values[i]; - if (std::isfinite(value)) { - if (run_start < 0) - run_start = static_cast(i); - continue; - } - if (run_start >= 0) - append_curve_sample_run(result, request, *samples, run_start, static_cast(i) - 1); - run_start = -1; - } - if (run_start >= 0) - append_curve_sample_run(result, request, *samples, run_start, static_cast(end) - 1); - } - } - partial.begins_with_continuous_point = partition.source_begin < partition.source_end && curve_sample_valid(request, partition.source_begin); - partial.ends_with_continuous_point = partition.source_end > partition.source_begin && curve_sample_valid(request, partition.source_end - 1); - partial.segments = std::move(result.segments); - job.results[partition_index] = std::move(partial); -} - -static void append_curve_segment(Curve_Geometry_Result& result, Curve_Geometry_Result::Segment&& source_segment) { - if (!source_segment.points.empty()) - result.segments.push_back(std::move(source_segment)); -} - -static void merge_curve_first_segment(Curve_Geometry_Result& result, Curve_Geometry_Result::Segment& source_segment) { - if (source_segment.points.empty()) - return; - if (result.segments.empty()) { - result.segments.push_back(std::move(source_segment)); - return; - } - auto& target_points = result.segments.back().points; - for (const PointF& point : source_segment.points) { - if (!target_points.empty() && target_points.back().x == point.x && target_points.back().y == point.y) - continue; - target_points.push_back(point); - } -} - -static Curve_Geometry_Result merge_curve_geometry(Curve_Geometry_Job& job) { - Curve_Geometry_Result result; - if (job.request) - result.mapping = job.request->mapping; - bool previous_can_join = false; - for (std::size_t i = 0; i < job.results.size(); ++i) { - Curve_Geometry_Partial_Result& partial = job.results[i]; - if (partial.segments.empty()) { - previous_can_join = false; - continue; - } - bool join = previous_can_join && partial.joins_previous; - if (join) { - merge_curve_first_segment(result, partial.segments.front()); - for (std::size_t s = 1; s < partial.segments.size(); ++s) - append_curve_segment(result, std::move(partial.segments[s])); - } - else { - for (auto& segment : partial.segments) - append_curve_segment(result, std::move(segment)); - } - previous_can_join = partial.ends_with_continuous_point; - } - result.update_states = std::move(job.update_states); - return result; -} -struct Curve_Private : Typed_Render_Data, Frame_Prepare_Subtask_Source { - static constexpr std::size_t Input_Queue_Capacity = 8; - std::shared_ptr source = std::make_shared(); - rigtorp::MPMCQueue> input_queue; - std::atomic_uint64_t dropped_input_count{0}; - std::pmr::vector> pending_geometry_update_states{memory_resource(Memory_Domain::Update_Completion)}; - std::optional geometry_result; - std::optional submitted_geometry_key; - std::uint64_t input_generation{}; - Curve_Private() - : input_queue(Input_Queue_Capacity) {} - ~Curve_Private() override { - if (geometry_result) - complete_curve_result_states(*geometry_result, std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - std::shared_ptr block; - while (input_queue.try_pop(block)) { - if (block && block->completion_state) - block->completion_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - } - for (auto& state : pending_geometry_update_states) { - if (state) - state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - } - pending_geometry_update_states.clear(); - } - void enqueue_input(std::shared_ptr block) { - if (!block) - return; - if (!input_queue.try_push(block)) { - if (block->completion_state) - block->completion_state->complete_all({}, Update_Outcome::Superseded); - dropped_input_count.fetch_add(1, std::memory_order_relaxed); - return; - } - if (block->completion_state) - block->completion_state->complete_until(Update_Stage::Input_Released); - if (q()) - q()->mark_render_dirty(); - } - void consume_input_queue() { - std::shared_ptr selected; - std::shared_ptr block; - while (input_queue.try_pop(block)) { - if (!block) - continue; - if (selected && selected->completion_state) - selected->completion_state->complete_all({}, Update_Outcome::Superseded); - selected = std::move(block); - } - if (!selected) - return; - source = std::move(selected->source); - ++input_generation; - if (selected->completion_state) { - selected->completion_state->complete_until(Update_Stage::Committed); - pending_geometry_update_states.push_back(std::move(selected->completion_state)); - } - } - void prepare_data(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - (void)snapshot; - (void)frame_view; - } - bool build_prepare_subtasks(Render_Task_Subflow& subflow, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - consume_input_queue(); - auto job = create_geometry_job_if_needed(snapshot, frame_view); - if (!job) - return true; - std::vector partition_tasks; - partition_tasks.reserve(job->partitions.size()); - for (std::size_t i = 0; i < job->partitions.size(); ++i) { - partition_tasks.push_back(subflow.emplace(Render_Task_Kind::Curve, Task([job, i]() { - build_curve_partition(*job, i); - }))); - } - auto merge = subflow.emplace(Render_Task_Kind::Compose, Task([this, job]() { - if (q() && q()->get_retiring()) - return; - Curve_Geometry_Result result = merge_curve_geometry(*job); - if (q() && q()->get_retiring()) { - complete_curve_result_states(result, std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - return; - } - geometry_result = std::move(result); - submitted_geometry_key = job->key; - })); - for (auto task : partition_tasks) - subflow.precede(task, merge); - return true; - } - std::shared_ptr create_geometry_job_if_needed(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) { - Curve_Render_State* s = render_state(frame_view); - if (!s) - return {}; - auto domain_axis = s->domain_axis.lock(); - auto value_axis = s->value_axis.lock(); - if (!domain_axis || !value_axis) { - for (auto& state : pending_geometry_update_states) { - if (state) - state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - } - pending_geometry_update_states.clear(); - return {}; - } - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(domain_axis.get(), value_axis.get(), snapshot); - Curve_Geometry_Key key; - key.input_generation = input_generation; - key.state_version = s->version; - key.mode = source ? curve_source_mode(*source) : Curve_Input_Mode::None; - key.sample_mode = s->sample_mode; - key.interpolation_mode = s->interpolation_mode; - key.visible_range_only = s->visible_range_only; - key.data_range = s->data_range; - key.sample_count = source ? curve_sample_count(*source) : 0u; - key.point_count = source ? curve_point_count(*source) : 0u; - key.domain = curve_axis_key(mapping.domain); - key.value = curve_axis_key(mapping.value); - if (submitted_geometry_key && key == *submitted_geometry_key) - return {}; - auto request = std::make_shared(); - request->mode = key.mode; - request->mapping = mapping; - request->data_range = s->data_range; - request->visible_range_only = s->visible_range_only; - request->interpolation_mode = s->interpolation_mode; - request->sample_mode = s->sample_mode; - request->source = source; - request->update_states = std::move(pending_geometry_update_states); - pending_geometry_update_states = std::pmr::vector>(memory_resource(Memory_Domain::Update_Completion)); - std::size_t worker_count = std::max(1, Global::instance()->render_scheduler().render_worker_count()); - return create_curve_geometry_job(std::move(request), std::move(key), worker_count); - } - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Curve_Render_State* s = render_state(frame_view); - if (!s) - return; - if (!geometry_result) - return; - if (snapshot.frame_update_states) { - for (const auto& state : geometry_result->update_states) - snapshot.frame_update_states->push_back(state); - geometry_result->update_states.clear(); - } - if (geometry_result->segments.empty()) - return; - canvas.save(); - canvas.set_antialias(s->antialias); - for (const auto& segment : geometry_result->segments) { - if (segment.points.empty()) - continue; - Curve_Utils::draw_polyline(&canvas, segment.points, s->pen); - Curve_Utils::draw_fill_to_value(&canvas, segment.points, geometry_result->mapping, geometry_result->mapping.value.coord_range.target, s->fill_brush); - } - canvas.restore(); - } -}; -RENDERIVE_DEFINE_RENDERABLE_BINDING(Curve, Curve_Private, Curve_Render_State, Curve_Input_Data, "Curve") -void Curve::update_samples(std::span values) { - if (!ok()) - return; - d()->enqueue_input(Curve_Input_Block::samples_block(values)); -} -void Curve::update_samples(std::pmr::vector&& values) { - if (!ok()) - return; - d()->enqueue_input(Curve_Input_Block::samples_block(std::move(values))); -} -Update_Ticket Curve::submit_samples(std::span values) { - auto state = make_update_state(); - if (!ok()) { - state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - return Update_Ticket(state); - } - d()->enqueue_input(Curve_Input_Block::samples_block(values, state)); - return Update_Ticket(state); -} -Update_Ticket Curve::submit_samples(std::pmr::vector&& values) { - auto state = make_update_state(); - if (!ok()) { - state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - return Update_Ticket(state); - } - d()->enqueue_input(Curve_Input_Block::samples_block(std::move(values), state)); - return Update_Ticket(state); -} -void Curve::update_points(std::span points) { - if (!ok()) - return; - d()->enqueue_input(Curve_Input_Block::points_block(points)); -} -void Curve::update_points(std::pmr::vector&& points) { - if (!ok()) - return; - d()->enqueue_input(Curve_Input_Block::points_block(std::move(points))); -} -Update_Ticket Curve::submit_points(std::span points) { - auto state = make_update_state(); - if (!ok()) { - state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - return Update_Ticket(state); - } - d()->enqueue_input(Curve_Input_Block::points_block(points, state)); - return Update_Ticket(state); -} -Update_Ticket Curve::submit_points(std::pmr::vector&& points) { - auto state = make_update_state(); - if (!ok()) { - state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); - return Update_Ticket(state); - } - d()->enqueue_input(Curve_Input_Block::points_block(std::move(points), state)); - return Update_Ticket(state); -} -RENDERIVE_DEFINE_WEAK_STATE_PROP(Curve, Curve_Render_State, Abs_Axis, domain_axis) -RENDERIVE_DEFINE_WEAK_STATE_PROP(Curve, Curve_Render_State, Abs_Axis, value_axis) -Range Curve::data_range() { - return d()->edit_state_value(&Curve_Render_State::data_range); -} -void Curve::set_data_range(Range value) { - if (value.length() == 0.0) - return; - d()->set_state_value(&Curve_Render_State::data_range, value); -} -RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, Pen, pen) -RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, Brush, fill_brush) -RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, bool, visible_range_only) -RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, bool, antialias) -RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, Line_Interpolation_Mode, interpolation_mode) -RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, Line_Sampling_Strategy, sample_mode) -Curve::Builder::Builder(std::shared_ptr parent, std::shared_ptr domain_axis, std::shared_ptr value_axis) { - ASSERT(parent, "Curve build error! parent == nullptr"); - ASSERT(domain_axis && value_axis, "Curve build error! axis == nullptr"); - ASSERT(domain_axis->attached_to_plot(), "Curve build error! domain_axis->plot == nullptr"); - ASSERT(domain_axis->shares_plot_with(*value_axis), "Curve build error! domain_axis->plot != value_axis->plot"); - ASSERT(parent->shares_plot_with(*domain_axis), "Curve build error! parent->plot != axis->plot"); - ASSERT(domain_axis->orientation() != value_axis->orientation(), "Curve build error! axes must be orthogonal"); - this->parent = parent; - this->domain_axis = domain_axis; - this->value_axis = value_axis; -} -std::shared_ptr Curve::Builder::build() { - auto ret = renderive::make_shared(); - ret->init(parent); - Curve_Private* pd = ret->d(); - Render_Edit_Lease edit(pd); - Curve_Render_State* sc = edit.operator->(); - PROP_R(std::weak_ptr, domain_axis) - PROP_R(std::weak_ptr, value_axis) - PROP_R(Range, data_range) - PROP_R(Pen, pen) - PROP_R(Brush, fill_brush) - PROP_R(bool, visible_range_only) - PROP_R(bool, antialias) - PROP_R(Line_Interpolation_Mode, interpolation_mode) - PROP_R(Line_Sampling_Strategy, sample_mode) - if (sc->data_range.length() == 0.0) - sc->data_range = {0.0, 1.0}; - return ret; -} -void Curve_Utils::draw_polyline(Canvas* canvas, std::span points, const Pen& pen) { - if (points.empty() || pen.style == Line_Style::None) - return; - canvas->set_pen(pen); - canvas->draw_polyline(points); -} -void Curve_Utils::draw_fill_to_value(Canvas* canvas, std::span points, const Axis_Mapping_2D& mapping, double baseline_value, const Brush& brush) { - if (points.empty() || brush.style == Brush_Style::None) - return; - double first_domain = mapping.domain.pixel_to_coord(mapping.domain_pixel(points.front())); - double last_domain = mapping.domain.pixel_to_coord(mapping.domain_pixel(points.back())); - std::pmr::vector fill_points(points.size() + 2, frame_memory_resource()); - fill_points.front() = mapping.map(first_domain, baseline_value); - fill_points.back() = mapping.map(last_domain, baseline_value); - for (int i = 0; i < points.size(); ++i) - fill_points[i + 1] = points[i]; - canvas->set_brush(brush); - canvas->fill_polygon(std::span(fill_points.data(), fill_points.size())); -} -void Curve_Utils::append_envelope_point(std::pmr::vector& points, const Axis_Mapping_2D& mapping, const Range& data_range, int total_count, int index, double value) { - if (index < 0) - return; - if (!std::isfinite(value)) - return; - PointF point = mapping.map(source_index_to_coordinate(data_range, total_count, index), value); - if (!points.empty() && points.back().x == point.x && points.back().y == point.y) - return; - points.push_back(point); -} -} // namespace renderive diff --git a/Core/primitive/Curve.h b/Core/primitive/Curve.h deleted file mode 100644 index 5b6c82a..0000000 --- a/Core/primitive/Curve.h +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include "../architecture/Renderable.h" -#include "../Axis/Abs_Axis.h" -#include "../base/Color.h" -#include "../base/Geometry.h" -#include "../base/Memory.h" -#include "../base/Style.h" -#include "../architecture/Update_Completion.h" -#include "../plottable/Interpolation.h" -#include "../render/Canvas.h" -namespace renderive { -struct Curve_Private; -/// @brief 曲线采样模式。 -/// @brief Sampling strategy used when converting a continuous sample sequence to screen polyline points. -enum class Line_Sampling_Strategy : std::uint8_t { - /// @brief 直接按输入数据采样。 - /// @brief Use every input sample. - All_Samples, - /// @brief 按像素峰值桶降采样。 - /// @brief Keep a local peak envelope per screen-pixel bucket to limit the number of drawn points. - Pixel_Min_Max_Envelope -}; -/// @brief 曲线构造属性。 -struct Curve_Prop { - std::weak_ptr domain_axis{}; - std::weak_ptr value_axis{}; - Range data_range{0.0, 1.0}; - Pen pen = Pen{Color::white()}; - Brush fill_brush = Brush{}; - bool visible_range_only = true; - bool antialias = false; - Line_Interpolation_Mode interpolation_mode = Line_Interpolation_Mode::Linear_Value; - Line_Sampling_Strategy sample_mode = Line_Sampling_Strategy::All_Samples; -}; -/// @brief 按坐标轴绘制折线或填充曲线的图元。 -class LIB_DECL Curve : public Renderable { -public: - /// @brief 写入按 data_range 均匀分布的采样值。 - void update_samples(std::span values); - /// @brief 移动写入按 data_range 均匀分布的采样值。 - void update_samples(std::pmr::vector&& values); - /// @brief 提交采样值并返回完成票据。 - Update_Ticket submit_samples(std::span values); - /// @brief 移动提交采样值并返回完成票据。 - Update_Ticket submit_samples(std::pmr::vector&& values); - /// @brief 写入容器中的采样值。 - template - void update_samples(const Values& values) { - update_samples(std::span(values.data(), values.size())); - } - /// @brief 用 writer 填充并写入采样值。 - template - void write_samples(std::size_t count, Writer&& writer) { - std::pmr::vector values(memory_resource(Memory_Domain::Curve)); - values.resize(count); - std::forward(writer)(std::span(values.data(), values.size())); - update_samples(std::move(values)); - } - /// @brief 写入显式坐标点。 - void update_points(std::span points); - /// @brief 移动写入显式坐标点。 - void update_points(std::pmr::vector&& points); - /// @brief 提交显式坐标点并返回完成票据。 - Update_Ticket submit_points(std::span points); - /// @brief 移动提交显式坐标点并返回完成票据。 - Update_Ticket submit_points(std::pmr::vector&& points); - /// @brief 写入容器中的显式坐标点。 - template - void update_points(const Points& points) { - update_points(std::span(points.data(), points.size())); - } - /// @brief 用 writer 填充并写入显式坐标点。 - template - void write_points(std::size_t count, Writer&& writer) { - std::pmr::vector points(memory_resource(Memory_Domain::Curve)); - points.resize(count); - std::forward(writer)(std::span(points.data(), points.size())); - update_points(std::move(points)); - } - /// @brief 绑定 X 轴。 - PROP(std::shared_ptr, domain_axis) - /// @brief 绑定 Y 轴。 - PROP(std::shared_ptr, value_axis) - /// @brief 采样值映射到 X 轴的数据范围。 - PROP(Range, data_range) - /// @brief 折线画笔。 - PROP(Pen, pen) - /// @brief 填充到 Y 轴目标边界的画刷。 - PROP(Brush, fill_brush) - /// @brief 是否只生成当前可见范围。 - PROP(bool, visible_range_only) - /// @brief 是否启用抗锯齿。 - PROP(bool, antialias) - /// @brief 折线插值模式。 - PROP(Line_Interpolation_Mode, interpolation_mode) - /// @brief 曲线采样模式。 - PROP(Line_Sampling_Strategy, sample_mode) - struct Builder; - /// @brief 创建曲线图元。 - Curve(); -private: - Curve_Private* d(); - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; -}; -/// @brief 曲线构造器。 -struct LIB_DECL Curve::Builder : Curve_Prop { - PROP_B(std::shared_ptr, parent) - PROP_B(std::shared_ptr, domain_axis) - PROP_B(std::shared_ptr, value_axis) - PROP_B(Range, data_range) - PROP_B(Pen, pen) - PROP_B(Brush, fill_brush) - PROP_B(bool, visible_range_only) - PROP_B(bool, antialias) - PROP_B(Line_Interpolation_Mode, interpolation_mode) - PROP_B(Line_Sampling_Strategy, sample_mode) - /// @brief 创建曲线构造器。 - Builder(std::shared_ptr parent, std::shared_ptr domain_axis, std::shared_ptr value_axis); - /// @brief 构建并返回曲线对象。 - std::shared_ptr build(); - std::shared_ptr parent{}; -}; -} // namespace renderive diff --git a/Core/primitive/Curve_Utils_p.h b/Core/primitive/Curve_Utils_p.h deleted file mode 100644 index 6517dea..0000000 --- a/Core/primitive/Curve_Utils_p.h +++ /dev/null @@ -1,124 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include "../Axis/Abs_Axis_p.h" -#include "../base/Frame_Memory.h" -#include "../plottable/Interpolation_p.h" -#include "Curve.h" -namespace renderive { -struct Pixel_Envelope_Bucket { - bool has_samples = false; - int first_index = -1; - int last_index = -1; - int minimum_index = -1; - int maximum_index = -1; - double minimum_value = std::numeric_limits::max(); - double maximum_value = std::numeric_limits::lowest(); -}; -class Curve_Utils { -public: - template - static std::pmr::vector create_points(int count, Point_At point_at, std::pmr::memory_resource* resource = frame_memory_resource()) { - std::pmr::vector points(resource); - points.reserve(count); - for (int i = 0; i < count; ++i) - points.push_back(point_at(i)); - return points; - } - template - static std::pmr::vector build_resampled_points(Abs_Axis* domain_axis, Abs_Axis* value_axis, const Range& sample_domain_range, int source_sample_count, int available_sample_count, bool visible_range_only, Line_Interpolation_Mode mode, Value_At value_at) { - return build_sample_line_points(Axis_Render_Access::mapping(domain_axis, value_axis), sample_domain_range, source_sample_count, available_sample_count, visible_range_only, mode, value_at, frame_memory_resource()); - } - template - static std::pmr::vector build_resampled_points(const Axis_Mapping_2D& mapping, const Range& sample_domain_range, int source_sample_count, int available_sample_count, bool visible_range_only, Line_Interpolation_Mode mode, Value_At value_at, std::pmr::memory_resource* resource = frame_memory_resource()) { - return build_sample_line_points(mapping, sample_domain_range, source_sample_count, available_sample_count, visible_range_only, mode, value_at, resource); - } - template - static std::pmr::vector build_pixel_envelope_points(const Axis_Mapping_2D& mapping, const Range& sample_domain_range, int source_sample_count, int available_sample_count, bool visible_range_only, Value_At value_at, std::pmr::memory_resource* resource = frame_memory_resource()) { - Visible_Sample_Window window = compute_visible_sample_window(mapping.domain, sample_domain_range, source_sample_count, available_sample_count, visible_range_only); - if (!window) - return {}; - double first_index = std::clamp(std::min(window.first_index, window.last_index), 0.0, static_cast(available_sample_count - 1)); - double last_index = std::clamp(std::max(window.first_index, window.last_index), first_index, static_cast(available_sample_count - 1)); - int first_source = std::clamp(static_cast(std::ceil(first_index)), 0, available_sample_count - 1); - int last_source = std::clamp(static_cast(std::floor(last_index)), first_source, available_sample_count - 1); - double first_pixel = mapping.domain.coord_to_pixel(source_index_to_coordinate(sample_domain_range, source_sample_count, first_index)); - double last_pixel = mapping.domain.coord_to_pixel(source_index_to_coordinate(sample_domain_range, source_sample_count, last_index)); - int pixel_floor_min = static_cast(std::floor(std::min(first_pixel, last_pixel))); - int pixel_floor_max = static_cast(std::floor(std::max(first_pixel, last_pixel))); - int bucket_count = std::max(1, pixel_floor_max - pixel_floor_min + 1); - bucket_count = std::min(bucket_count, mapping.domain.pixel_size() + 1); - std::pmr::vector buckets(bucket_count, resource); - for (int i = first_source; i <= last_source; ++i) { - double value = value_at(i); - if (!std::isfinite(value)) - continue; - double coord = source_index_to_coordinate(sample_domain_range, source_sample_count, i); - double pixel = mapping.domain.coord_to_pixel(coord); - int bucket_index = std::clamp(static_cast(std::floor(pixel)) - pixel_floor_min, 0, bucket_count - 1); - Pixel_Envelope_Bucket& bucket = buckets[bucket_index]; - if (!bucket.has_samples) { - bucket.has_samples = true; - bucket.first_index = i; - bucket.minimum_index = i; - bucket.maximum_index = i; - bucket.minimum_value = value; - bucket.maximum_value = value; - } - bucket.last_index = i; - if (value < bucket.minimum_value) { - bucket.minimum_value = value; - bucket.minimum_index = i; - } - if (value > bucket.maximum_value) { - bucket.maximum_value = value; - bucket.maximum_index = i; - } - } - std::pmr::vector points(resource); - points.reserve(bucket_count * 4 + 2); - double last_appended_index = -std::numeric_limits::infinity(); - auto append_index = [&](double index) { - if (std::abs(index - last_appended_index) <= 1e-9) - return; - double value = interpolate_sample_value(index, available_sample_count, Line_Interpolation_Mode::Linear_Value, value_at); - if (!std::isfinite(value)) - return; - PointF point = mapping.map(source_index_to_coordinate(sample_domain_range, source_sample_count, index), value); - if (!points.empty() && points.back().x == point.x && points.back().y == point.y) - return; - points.push_back(point); - last_appended_index = index; - }; - append_index(first_index); - int first_bucket = std::clamp(static_cast(std::floor(first_pixel)) - pixel_floor_min, 0, bucket_count - 1); - int last_bucket = std::clamp(static_cast(std::floor(last_pixel)) - pixel_floor_min, 0, bucket_count - 1); - bool bucket_forward = first_bucket <= last_bucket; - for (int b = bucket_forward ? 0 : bucket_count - 1; bucket_forward ? b < bucket_count : b >= 0; bucket_forward ? ++b : --b) { - const Pixel_Envelope_Bucket& bucket = buckets[b]; - if (!bucket.has_samples) - continue; - std::array indexes{bucket.first_index, bucket.minimum_index, bucket.maximum_index, bucket.last_index}; - std::sort(indexes.begin(), indexes.end()); - auto end = std::unique(indexes.begin(), indexes.end()); - for (auto it = indexes.begin(); it != end; ++it) - append_index(static_cast(*it)); - } - append_index(last_index); - return points; - } - template - static std::pmr::vector build_pixel_envelope_points(Abs_Axis* domain_axis, Abs_Axis* value_axis, const Range& sample_domain_range, int source_sample_count, int available_sample_count, bool visible_range_only, Value_At value_at) { - return build_pixel_envelope_points(Axis_Render_Access::mapping(domain_axis, value_axis), sample_domain_range, source_sample_count, available_sample_count, visible_range_only, value_at, frame_memory_resource()); - } - static void draw_polyline(Canvas* painter, std::span points, const Pen& pen); - static void draw_fill_to_value(Canvas* painter, std::span points, const Axis_Mapping_2D& mapping, double baseline_value, const Brush& brush); -private: - static void append_envelope_point(std::pmr::vector& points, const Axis_Mapping_2D& mapping, const Range& sample_domain_range, int source_sample_count, int index, double value); -}; -} // namespace renderive diff --git a/Core/primitive/Fill_Area.cpp b/Core/primitive/Fill_Area.cpp deleted file mode 100644 index 6bf86c8..0000000 --- a/Core/primitive/Fill_Area.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "Fill_Area.h" -#include -#include "../Axis/Abs_Axis_p.h" -#include "../base/Frame_Memory.h" -#include "../base/Memory.h" -#include "../render/Canvas.h" -namespace renderive { -struct Fill_Area_Input_Data : Latest_Input_Data { - std::pmr::vector points{memory_resource(Memory_Domain::Curve)}; - void push(std::span values) { - points.assign(values.begin(), values.end()); - mark_latest_pushed(); - } - void push(std::pmr::vector&& values) { - points = std::move(values); - mark_latest_pushed(); - } - template - void read_all(Handler handler) { - if (pending) - handler(points); - } - void clear() override { - points.clear(); - clear_latest(); - } -}; -struct Fill_Area_Render_State : Render_State, Fill_Area_Prop {}; -struct Fill_Area_Private : Typed_Render_Data { - std::pmr::vector data_points{memory_resource(Memory_Domain::Curve)}; - void prepare_data(const Render_Frame_Snapshot&, const Renderable_Frame_View& frame_view) override { - Fill_Area_Input_Data* input = render_input_data(frame_view); - if (!input) - return; - input->read_all([this](std::pmr::vector& points) { - data_points.swap(points); - }); - frame_view.consume_input(); - } - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Fill_Area_Render_State* s = render_state(frame_view); - if (!s) - return; - auto domain_axis = s->domain_axis.lock(); - auto value_axis = s->value_axis.lock(); - if (!domain_axis || !value_axis || data_points.empty()) - return; - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(domain_axis.get(), value_axis.get(), snapshot); - std::pmr::vector polygon(frame_memory_resource()); - if (s->mode == Fill_Area_Mode::To_Baseline) { - polygon.resize(data_points.size() + 2); - polygon.front() = mapping.map(data_points.front().x, s->baseline); - polygon.back() = mapping.map(data_points.back().x, s->baseline); - for (int i = 0; i < data_points.size(); ++i) { - const PointF& point = data_points.at(i); - polygon[i + 1] = mapping.map(point); - } - } - else { - polygon.resize(data_points.size()); - for (int i = 0; i < data_points.size(); ++i) { - const PointF& point = data_points.at(i); - polygon[i] = mapping.map(point); - } - } - canvas.save(); - canvas.set_antialias(s->antialias); - canvas.set_pen(s->pen); - canvas.set_brush(s->brush); - canvas.draw_polygon(std::span(polygon.data(), polygon.size()), false); - canvas.restore(); - } -}; -RENDERIVE_DEFINE_RENDERABLE_BINDING(Fill_Area, Fill_Area_Private, Fill_Area_Render_State, Fill_Area_Input_Data, "FillArea") -void Fill_Area::update_points(std::span points) { - if (!ok()) - return; - Render_Input_Lease input(d()); - input->push(points); -} -void Fill_Area::update_points(std::pmr::vector&& points) { - if (!ok()) - return; - Render_Input_Lease input(d()); - input->push(std::move(points)); -} -RENDERIVE_DEFINE_WEAK_STATE_PROP(Fill_Area, Fill_Area_Render_State, Abs_Axis, domain_axis) -RENDERIVE_DEFINE_WEAK_STATE_PROP(Fill_Area, Fill_Area_Render_State, Abs_Axis, value_axis) -RENDERIVE_DEFINE_STATE_PROP(Fill_Area, Fill_Area_Render_State, double, baseline) -RENDERIVE_DEFINE_STATE_PROP(Fill_Area, Fill_Area_Render_State, Brush, brush) -RENDERIVE_DEFINE_STATE_PROP(Fill_Area, Fill_Area_Render_State, Pen, pen) -RENDERIVE_DEFINE_STATE_PROP(Fill_Area, Fill_Area_Render_State, bool, antialias) -RENDERIVE_DEFINE_STATE_PROP(Fill_Area, Fill_Area_Render_State, Fill_Area_Mode, mode) -Fill_Area::Builder::Builder(std::shared_ptr parent, std::shared_ptr domain_axis, std::shared_ptr value_axis) { - ASSERT(parent, "Fill_Area build error! parent == nullptr"); - ASSERT(domain_axis && value_axis, "Fill_Area build error! axis == nullptr"); - ASSERT(domain_axis->attached_to_plot(), "Fill_Area build error! domain_axis->plot == nullptr"); - ASSERT(domain_axis->shares_plot_with(*value_axis), "Fill_Area build error! domain_axis->plot != value_axis->plot"); - ASSERT(parent->shares_plot_with(*domain_axis), "Fill_Area build error! parent->plot != axis->plot"); - ASSERT(domain_axis->orientation() != value_axis->orientation(), "Fill_Area build error! axes must be orthogonal"); - this->parent = parent; - this->domain_axis = domain_axis; - this->value_axis = value_axis; -} -std::shared_ptr Fill_Area::Builder::build() { - auto ret = renderive::make_shared(); - ret->init(parent); - Fill_Area_Private* pd = ret->d(); - Render_Edit_Lease edit(pd); - Fill_Area_Render_State* sc = edit.operator->(); - PROP_R(std::weak_ptr, domain_axis) - PROP_R(std::weak_ptr, value_axis) - PROP_R(double, baseline) - PROP_R(Brush, brush) - PROP_R(Pen, pen) - PROP_R(bool, antialias) - PROP_R(Fill_Area_Mode, mode) - return ret; -} -} // namespace renderive diff --git a/Core/primitive/Fill_Area.h b/Core/primitive/Fill_Area.h deleted file mode 100644 index abd6a4e..0000000 --- a/Core/primitive/Fill_Area.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include "../architecture/Renderable.h" -#include "../Axis/Abs_Axis.h" -#include "../base/Geometry.h" -#include "../base/Memory.h" -#include "../base/Style.h" -namespace renderive { -struct Fill_Area_Private; -/// @brief 填充区域生成方式。 -enum class Fill_Area_Mode : std::uint8_t { - /// @brief 按输入折线填充到基线。 - To_Baseline, - /// @brief 按输入点直接组成多边形。 - Polygon -}; -/// @brief 填充区域构造属性。 -struct Fill_Area_Prop { - std::weak_ptr domain_axis{}; - std::weak_ptr value_axis{}; - double baseline = 0.0; - Brush brush = Brush{}; - Pen pen = Pen{.style = Line_Style::None}; - bool antialias = false; - Fill_Area_Mode mode = Fill_Area_Mode::To_Baseline; -}; -/// @brief 按坐标点绘制填充区域的图元。 -class LIB_DECL Fill_Area : public Renderable { -public: - /// @brief 写入填充区域坐标点。 - void update_points(std::span points); - /// @brief 移动写入填充区域坐标点。 - void update_points(std::pmr::vector&& points); - /// @brief 写入容器中的填充区域坐标点。 - template - void update_points(const Points& points) { - update_points(std::span(points.data(), points.size())); - } - /// @brief 用 writer 填充并写入坐标点。 - template - void write_points(std::size_t count, Writer&& writer) { - std::pmr::vector points(memory_resource(Memory_Domain::Curve)); - points.resize(count); - std::forward(writer)(std::span(points.data(), points.size())); - update_points(std::move(points)); - } - /// @brief 绑定 X 轴。 - PROP(std::shared_ptr, domain_axis) - /// @brief 绑定 Y 轴。 - PROP(std::shared_ptr, value_axis) - /// @brief To_Baseline 模式下的基线坐标。 - PROP(double, baseline) - /// @brief 填充刷。 - PROP(Brush, brush) - /// @brief 边界画笔。 - PROP(Pen, pen) - /// @brief 是否启用抗锯齿。 - PROP(bool, antialias) - /// @brief 填充生成方式。 - PROP(Fill_Area_Mode, mode) - struct Builder; - /// @brief 创建填充区域图元。 - Fill_Area(); -private: - Fill_Area_Private* d(); - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; -}; -/// @brief 填充区域构造器。 -struct LIB_DECL Fill_Area::Builder : Fill_Area_Prop { - PROP_B(std::shared_ptr, parent) - PROP_B(std::shared_ptr, domain_axis) - PROP_B(std::shared_ptr, value_axis) - PROP_B(double, baseline) - PROP_B(Brush, brush) - PROP_B(Pen, pen) - PROP_B(bool, antialias) - PROP_B(Fill_Area_Mode, mode) - /// @brief 创建填充区域构造器。 - Builder(std::shared_ptr parent, std::shared_ptr domain_axis, std::shared_ptr value_axis); - /// @brief 构建并返回填充区域对象。 - std::shared_ptr build(); - std::shared_ptr parent{}; -}; -} // namespace renderive diff --git a/Core/primitive/Grid.cpp b/Core/primitive/Grid.cpp deleted file mode 100644 index 182d4df..0000000 --- a/Core/primitive/Grid.cpp +++ /dev/null @@ -1,204 +0,0 @@ -#include "Grid.h" -#include -#include "../Axis/Abs_Axis_p.h" -#include "../base/Memory.h" -#include "../render/Canvas.h" -namespace renderive { -struct Grid_Render_State : Render_State, Grid_Prop {}; -struct Grid_Private : Typed_Render_Data { - std::vector create_division_ticks(Range range, int divisions) { - std::vector ticks; - if (range.length() == 0.0) - return ticks; - divisions = std::max(1, divisions); - ticks.reserve(divisions + 1); - double step = range.length() / static_cast(divisions); - for (int i = 0; i <= divisions; ++i) - ticks.push_back(range.origin + step * i); - return ticks; - } - void draw_axis_grid(Canvas& canvas, Abs_Axis* axis, const Axis_Frame_Snapshot& axis_frame, const Axis_Frame_Snapshot& cross_frame, const Range& axis_range, const Range& cross_range, const Pen& pen, const Pen& sub_pen, bool sub_grid) { - if (pen.style == Line_Style::None && (!sub_grid || sub_pen.style == Line_Style::None)) - return; - double cross0 = cross_frame.coord_to_pixel(cross_range.origin); - double cross1 = cross_frame.coord_to_pixel(cross_range.target); - double step = axis->tick_step(axis_range); - std::vector ticks = axis->create_tick_vector(step, axis_range); - if (sub_grid && sub_pen.style != Line_Style::None) { - canvas.set_pen(sub_pen); - std::vector sub_ticks = axis->create_sub_tick_vector(axis->sub_tick_count(step), ticks, axis_range); - draw_ticks(canvas, axis_frame, cross0, cross1, sub_ticks); - } - if (pen.style != Line_Style::None) { - canvas.set_pen(pen); - draw_ticks(canvas, axis_frame, cross0, cross1, ticks); - } - } - void draw_division_grid(Canvas& canvas, const Axis_Frame_Snapshot& axis_frame, const Axis_Frame_Snapshot& cross_frame, const Range& axis_range, const Range& cross_range, const Pen& pen, int divisions) { - if (pen.style == Line_Style::None) - return; - double cross0 = cross_frame.coord_to_pixel(cross_range.origin); - double cross1 = cross_frame.coord_to_pixel(cross_range.target); - std::vector ticks = create_division_ticks(axis_range, divisions); - canvas.set_pen(pen); - draw_ticks(canvas, axis_frame, cross0, cross1, ticks); - } - void draw_ticks(Canvas& canvas, const Axis_Frame_Snapshot& axis, double cross0, double cross1, const std::vector& ticks) { - bool vertical = axis.orientation == Orientation::Horizontal; - for (double tick : ticks) { - double pixel = axis.coord_to_pixel(tick); - if (vertical) - canvas.draw_line(PointF{pixel, cross0}, PointF{pixel, cross1}); - else - canvas.draw_line(PointF{cross0, pixel}, PointF{cross1, pixel}); - } - } - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Grid_Render_State* s = render_state(frame_view); - if (!s) - return; - auto domain_axis = s->domain_axis.lock(); - auto value_axis = s->value_axis.lock(); - if (!domain_axis || !value_axis) - return; - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(domain_axis.get(), value_axis.get(), snapshot); - Range domain_range = mapping.domain.coord_range; - Range value_range = mapping.value.coord_range; - if (s->domain_range.length() != 0.0) - domain_range = s->domain_range; - if (s->value_range.length() != 0.0) - value_range = s->value_range; - canvas.save(); - if (s->line_mode == Grid_Line_Mode::Range_Division) { - if (s->domain_grid) - draw_division_grid(canvas, mapping.domain, mapping.value, domain_range, value_range, s->domain_pen, s->domain_divisions); - if (s->value_grid) - draw_division_grid(canvas, mapping.value, mapping.domain, value_range, domain_range, s->value_pen, s->value_divisions); - } - else { - if (s->domain_grid) - draw_axis_grid(canvas, domain_axis.get(), mapping.domain, mapping.value, domain_range, value_range, s->domain_pen, s->sub_pen, s->sub_grid); - if (s->value_grid) - draw_axis_grid(canvas, value_axis.get(), mapping.value, mapping.domain, value_range, domain_range, s->value_pen, s->sub_pen, s->sub_grid); - } - canvas.restore(); - } -}; -RENDERIVE_DEFINE_RENDERABLE_BINDING(Grid, Grid_Private, Grid_Render_State, Input_Data, "Grid") -std::shared_ptr Grid::domain_axis() { - return d()->edit_state_value(&Grid_Render_State::domain_axis).lock(); -} -void Grid::set_domain_axis(std::shared_ptr value) { - d()->set_state_value(&Grid_Render_State::domain_axis, std::weak_ptr(value)); -} -std::shared_ptr Grid::value_axis() { - return d()->edit_state_value(&Grid_Render_State::value_axis).lock(); -} -void Grid::set_value_axis(std::shared_ptr value) { - d()->set_state_value(&Grid_Render_State::value_axis, std::weak_ptr(value)); -} -bool Grid::domain_grid() { - return d()->edit_state_value(&Grid_Render_State::domain_grid); -} -void Grid::set_domain_grid(bool value) { - d()->set_state_value(&Grid_Render_State::domain_grid, value); -} -bool Grid::value_grid() { - return d()->edit_state_value(&Grid_Render_State::value_grid); -} -void Grid::set_value_grid(bool value) { - d()->set_state_value(&Grid_Render_State::value_grid, value); -} -bool Grid::sub_grid() { - return d()->edit_state_value(&Grid_Render_State::sub_grid); -} -void Grid::set_sub_grid(bool value) { - d()->set_state_value(&Grid_Render_State::sub_grid, value); -} -Grid_Line_Mode Grid::line_mode() { - return d()->edit_state_value(&Grid_Render_State::line_mode); -} -void Grid::set_line_mode(Grid_Line_Mode value) { - d()->set_state_value(&Grid_Render_State::line_mode, value); -} -Range Grid::domain_range() { - return d()->edit_state_value(&Grid_Render_State::domain_range); -} -void Grid::set_domain_range(Range value) { - if (value.length() == 0.0) - return; - d()->set_state_value(&Grid_Render_State::domain_range, value); -} -Range Grid::value_range() { - return d()->edit_state_value(&Grid_Render_State::value_range); -} -void Grid::set_value_range(Range value) { - if (value.length() == 0.0) - return; - d()->set_state_value(&Grid_Render_State::value_range, value); -} -int Grid::domain_divisions() { - return d()->edit_state_value(&Grid_Render_State::domain_divisions); -} -void Grid::set_domain_divisions(int value) { - d()->set_state_value(&Grid_Render_State::domain_divisions, std::max(1, value)); -} -int Grid::value_divisions() { - return d()->edit_state_value(&Grid_Render_State::value_divisions); -} -void Grid::set_value_divisions(int value) { - d()->set_state_value(&Grid_Render_State::value_divisions, std::max(1, value)); -} -Pen Grid::domain_pen() { - return d()->edit_state_value(&Grid_Render_State::domain_pen); -} -void Grid::set_domain_pen(Pen value) { - d()->set_state_value(&Grid_Render_State::domain_pen, std::move(value)); -} -Pen Grid::value_pen() { - return d()->edit_state_value(&Grid_Render_State::value_pen); -} -void Grid::set_value_pen(Pen value) { - d()->set_state_value(&Grid_Render_State::value_pen, std::move(value)); -} -Pen Grid::sub_pen() { - return d()->edit_state_value(&Grid_Render_State::sub_pen); -} -void Grid::set_sub_pen(Pen value) { - d()->set_state_value(&Grid_Render_State::sub_pen, std::move(value)); -} -Grid::Builder::Builder(std::shared_ptr parent, std::shared_ptr domain_axis, std::shared_ptr value_axis) { - ASSERT(parent, "Grid build error! parent == nullptr"); - ASSERT(domain_axis && value_axis, "Grid build error! axis == nullptr"); - ASSERT(domain_axis->attached_to_plot(), "Grid build error! domain_axis->plot == nullptr"); - ASSERT(domain_axis->shares_plot_with(*value_axis), "Grid build error! domain_axis->plot != value_axis->plot"); - ASSERT(parent->shares_plot_with(*domain_axis), "Grid build error! parent->plot != axis->plot"); - ASSERT(domain_axis->orientation() != value_axis->orientation(), "Grid build error! axes must be orthogonal"); - this->parent = parent; - this->domain_axis = domain_axis; - this->value_axis = value_axis; -} -std::shared_ptr Grid::Builder::build() { - auto ret = renderive::make_shared(); - ret->init(parent); - Grid_Private* pd = ret->d(); - Render_Edit_Lease edit(pd); - Grid_Render_State* sc = edit.operator->(); - PROP_R(std::weak_ptr, domain_axis) - PROP_R(std::weak_ptr, value_axis) - PROP_R(bool, domain_grid) - PROP_R(bool, value_grid) - PROP_R(bool, sub_grid) - PROP_R(Grid_Line_Mode, line_mode) - PROP_R(Range, domain_range) - PROP_R(Range, value_range) - PROP_R(int, domain_divisions) - PROP_R(int, value_divisions) - PROP_R(Pen, domain_pen) - PROP_R(Pen, value_pen) - PROP_R(Pen, sub_pen) - sc->domain_divisions = std::max(1, sc->domain_divisions); - sc->value_divisions = std::max(1, sc->value_divisions); - return ret; -} -} // namespace renderive diff --git a/Core/primitive/Grid.h b/Core/primitive/Grid.h deleted file mode 100644 index 3887854..0000000 --- a/Core/primitive/Grid.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once -#include "../architecture/Renderable.h" -#include "../Axis/Abs_Axis.h" -#include "../base/Style.h" -#include -namespace renderive { -struct Grid_Private; -/// @brief 网格线生成方式。 -enum class Grid_Line_Mode : std::uint8_t { - /// @brief 使用坐标轴刻度生成网格。 - Axis_Tick, - /// @brief 使用固定分段数生成网格。 - Range_Division -}; -/// @brief 网格构造属性。 -struct Grid_Prop { - std::weak_ptr domain_axis{}; - std::weak_ptr value_axis{}; - bool domain_grid = true; - bool value_grid = true; - bool sub_grid = false; - Grid_Line_Mode line_mode = Grid_Line_Mode::Axis_Tick; - Range domain_range{}; - Range value_range{}; - int domain_divisions = 4; - int value_divisions = 4; - Pen domain_pen = Pen{Color{255, 255, 255, 40}}; - Pen value_pen = Pen{Color{255, 255, 255, 40}}; - Pen sub_pen = Pen{Color{255, 255, 255, 20}}; -}; -/// @brief 在两个坐标轴之间绘制网格线的图元。 -class LIB_DECL Grid : public Renderable { -public: - /// @brief 绑定 X 轴。 - PROP(std::shared_ptr, domain_axis) - /// @brief 绑定 Y 轴。 - PROP(std::shared_ptr, value_axis) - /// @brief 是否绘制 X 轴方向网格。 - PROP(bool, domain_grid) - /// @brief 是否绘制 Y 轴方向网格。 - PROP(bool, value_grid) - /// @brief 是否绘制子网格。 - PROP(bool, sub_grid) - /// @brief 网格线生成方式。 - PROP(Grid_Line_Mode, line_mode) - /// @brief X 轴网格范围;为空时使用轴当前范围。 - PROP(Range, domain_range) - /// @brief Y 轴网格范围;为空时使用轴当前范围。 - PROP(Range, value_range) - /// @brief Range_Division 模式下 X 方向分段数。 - PROP(int, domain_divisions) - /// @brief Range_Division 模式下 Y 方向分段数。 - PROP(int, value_divisions) - /// @brief X 方向主网格画笔。 - PROP(Pen, domain_pen) - /// @brief Y 方向主网格画笔。 - PROP(Pen, value_pen) - /// @brief 子网格画笔。 - PROP(Pen, sub_pen) - struct Builder; - /// @brief 创建网格图元。 - Grid(); -private: - Grid_Private* d(); - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; -}; -/// @brief 网格构造器。 -struct LIB_DECL Grid::Builder : Grid_Prop { - PROP_B(std::shared_ptr, parent) - PROP_B(std::shared_ptr, domain_axis) - PROP_B(std::shared_ptr, value_axis) - PROP_B(bool, domain_grid) - PROP_B(bool, value_grid) - PROP_B(bool, sub_grid) - PROP_B(Grid_Line_Mode, line_mode) - PROP_B(Range, domain_range) - PROP_B(Range, value_range) - PROP_B(int, domain_divisions) - PROP_B(int, value_divisions) - PROP_B(Pen, domain_pen) - PROP_B(Pen, value_pen) - PROP_B(Pen, sub_pen) - /// @brief 创建网格构造器。 - Builder(std::shared_ptr parent, std::shared_ptr domain_axis, std::shared_ptr value_axis); - /// @brief 构建并返回网格对象。 - std::shared_ptr build(); - std::shared_ptr parent{}; -}; -} // namespace renderive diff --git a/Core/primitive/Label.cpp b/Core/primitive/Label.cpp deleted file mode 100644 index 617bdf0..0000000 --- a/Core/primitive/Label.cpp +++ /dev/null @@ -1,142 +0,0 @@ -#include "Label.h" -#include "../Axis/Abs_Axis_p.h" -#include "../base/Memory.h" -#include "../render/Canvas.h" -namespace renderive { -struct Label_Render_State : Render_State, Label_Prop {}; -struct Label_Input_Data : Latest_Input_Data { - std::pmr::vector items{memory_resource(Memory_Domain::Other)}; - void push(std::span value) { - items.assign(value.begin(), value.end()); - mark_latest_pushed(); - } - void push(std::pmr::vector&& value) { - items = std::move(value); - mark_latest_pushed(); - } - void clear() override { - items.clear(); - clear_latest(); - } -}; -struct Label_Private : Typed_Render_Data { - std::pmr::vector prepared_items{memory_resource(Memory_Domain::Other)}; - bool use_prepared_items = false; - void prepare_data(const Render_Frame_Snapshot&, const Renderable_Frame_View& frame_view) override { - Label_Input_Data* input = render_input_data(frame_view); - if (!input) - return; - if (!input->pending) - return; - prepared_items.swap(input->items); - use_prepared_items = true; - frame_view.consume_input(); - } - void draw_item(Canvas& canvas, const Label_Item& item, const Axis_Mapping_2D& mapping) { - if (item.text.empty()) - return; - canvas.save(); - canvas.set_font(item.font); - Text_Metrics tm = canvas.measure_text(item.text); - double text_w = tm.width; - double text_h = tm.height; - PointF anchor = mapping.map(item.position); - PointF top_left{anchor.x + item.offset.x, anchor.y + item.offset.y}; - if ((item.alignment & Text_Align::Horizontal_Center) == Text_Align::Horizontal_Center) - top_left.x -= text_w / 2.0; - else if ((item.alignment & Text_Align::Right) == Text_Align::Right) - top_left.x -= text_w; - if ((item.alignment & Text_Align::Vertical_Center) == Text_Align::Vertical_Center) - top_left.y -= text_h / 2.0; - else if ((item.alignment & Text_Align::Bottom) == Text_Align::Bottom) - top_left.y -= text_h; - RectF rect{top_left.x, top_left.y, text_w, text_h}; - if (item.background_brush.style != Brush_Style::None || item.border_pen.style != Line_Style::None) { - canvas.set_pen(item.border_pen); - canvas.set_brush(item.background_brush); - canvas.draw_rect(rect); - } - canvas.set_pen(item.text_pen); - canvas.draw_text(rect, Text_Align::Left | Text_Align::Vertical_Center, item.text); - canvas.restore(); - } - void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override { - Label_Render_State* s = render_state(frame_view); - if (!s) - return; - auto domain_axis = s->domain_axis.lock(); - auto value_axis = s->value_axis.lock(); - if (!domain_axis || !value_axis) - return; - Axis_Mapping_2D mapping = Axis_Render_Access::mapping(domain_axis.get(), value_axis.get(), snapshot); - if (use_prepared_items) { - for (const Label_Item& item : prepared_items) - draw_item(canvas, item, mapping); - } - else { - Label_Item item; - item.position = s->position; - item.offset = s->offset; - item.text = s->text; - item.alignment = s->alignment; - item.font = s->font; - item.text_pen = s->text_pen; - item.background_brush = s->background_brush; - item.border_pen = s->border_pen; - draw_item(canvas, item, mapping); - } - } -}; -RENDERIVE_DEFINE_RENDERABLE_BINDING(Label, Label_Private, Label_Render_State, Label_Input_Data, "Label") -void Label::update_items(std::span items) { - if (!ok()) - return; - Render_Input_Lease input(d()); - input->push(items); -} -void Label::update_items(std::pmr::vector&& items) { - if (!ok()) - return; - Render_Input_Lease input(d()); - input->push(std::move(items)); -} -RENDERIVE_DEFINE_WEAK_STATE_PROP(Label, Label_Render_State, Abs_Axis, domain_axis) -RENDERIVE_DEFINE_WEAK_STATE_PROP(Label, Label_Render_State, Abs_Axis, value_axis) -RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, PointF, position) -RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, PointF, offset) -RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, std::string, text) -RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, Text_Align, alignment) -RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, Font, font) -RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, Pen, text_pen) -RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, Brush, background_brush) -RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, Pen, border_pen) -Label::Builder::Builder(std::shared_ptr parent, std::shared_ptr domain_axis, std::shared_ptr value_axis) { - ASSERT(parent, "Label build error! parent == nullptr"); - ASSERT(domain_axis && value_axis, "Label build error! axis == nullptr"); - ASSERT(domain_axis->attached_to_plot(), "Label build error! domain_axis->plot == nullptr"); - ASSERT(domain_axis->shares_plot_with(*value_axis), "Label build error! domain_axis->plot != value_axis->plot"); - ASSERT(parent->shares_plot_with(*domain_axis), "Label build error! parent->plot != axis->plot"); - ASSERT(domain_axis->orientation() != value_axis->orientation(), "Label build error! axes must be orthogonal"); - this->parent = parent; - this->domain_axis = domain_axis; - this->value_axis = value_axis; -} -std::shared_ptr