#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