#include "Plot_Frame_Pipeline.h" #include "../architecture/Update_Completion.h" #include namespace renderive { Plot_Frame::Plot_Frame() : presented_update_states(memory_resource(Memory_Domain::Update_Completion)) {} Plot_Frame_Lease::Plot_Frame_Lease(Plot_Frame* buffer, Triple_Role_Buffer_Control* control, Triple_Buffer_Lease lease) : frame(buffer), control(control), lease(lease) {} Plot_Frame_Lease::Plot_Frame_Lease(Plot_Frame_Lease&& other) noexcept : frame(std::exchange(other.frame, nullptr)), control(std::exchange(other.control, nullptr)), lease(std::exchange(other.lease, Triple_Buffer_Lease{})) {} Plot_Frame_Lease& Plot_Frame_Lease::operator=(Plot_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; } Plot_Frame_Lease::~Plot_Frame_Lease() { reset(); } void Plot_Frame_Lease::reset() { if (!control) return; control->unmark_use(lease); frame = nullptr; control = nullptr; lease = {}; } Plot_Frame_Pipeline::Plot_Frame_Pipeline() { frame_control.reset(); } Plot_Frame_Pipeline::~Plot_Frame_Pipeline() { cancel_all_update_states(); } void Plot_Frame_Pipeline::mark_edit_state() { edit_state_version.fetch_add(1, std::memory_order_release); } bool Plot_Frame_Pipeline::has_new_state() const { return buffer_version_newer(edit_state_version.load(std::memory_order_acquire), render_state_version); } Plot_Frame* Plot_Frame_Pipeline::frame_by_index(std::uint8_t index) { return &frames[index]; } const Plot_Frame* Plot_Frame_Pipeline::frame_by_index(std::uint8_t index) const { return &frames[index]; } Plot_Frame* Plot_Frame_Pipeline::frame_by_role(std::uint8_t role) { return frame_by_index(frame_control.read_view()[role]); } const Plot_Frame* Plot_Frame_Pipeline::frame_by_role(std::uint8_t role) const { return frame_by_index(frame_control.read_view()[role]); } Plot_Frame_Lease Plot_Frame_Pipeline::try_begin_render() { auto record = frame_control.try_mark_use_role(Frame_Rendering); if (record.result != Triple_Buffer_Result::Success) { ++attempts.render_role_busy_count; return {}; } return {frame_by_index(record.lease.index), &frame_control, record.lease}; } Plot_Frame_Pipeline::Frame_Publish_Result Plot_Frame_Pipeline::try_publish_rendered(std::uint64_t now_ns) { Frame_Publish_Result result; Plot_Frame* rendered = frame_by_role(Frame_Rendering); Plot_Frame* old_middle = frame_by_role(Frame_Middle); const Plot_Frame* painting = frame_by_role(Frame_Painting); bool supersede_middle = buffer_version_newer(old_middle->version.load(std::memory_order_acquire), painting->version.load(std::memory_order_acquire)); auto swap = frame_control.try_swap_role(Frame_Rendering, Frame_Middle, [this](const Triple_Buffer_View& view) { const Plot_Frame* rendering = frame_by_index(view[Frame_Rendering]); const Plot_Frame* middle = frame_by_index(view[Frame_Middle]); return buffer_version_newer(rendering->version.load(std::memory_order_acquire), middle->version.load(std::memory_order_acquire)); }); if (swap.result != Triple_Buffer_Result::Success) { rendered->metadata.outcome = Frame_Outcome::Publish_Dropped; result.dropped_frame = rendered; ++attempts.publish_dropped_count; return result; } rendered->metadata.transition_12_ns = now_ns; rendered->metadata.wait_12_ns = now_ns > rendered->metadata.render_end_ns ? now_ns - rendered->metadata.render_end_ns : 0; frame_by_role(Frame_Rendering)->metadata.render_role_enter_ns = now_ns; if (supersede_middle && old_middle->metadata.frame_id) { old_middle->metadata.superseded_time_ns = now_ns; old_middle->metadata.outcome = Frame_Outcome::Superseded; result.superseded_middle = old_middle; } Frame_Publish_Result update_result = request_paint_update(now_ns); update_result.superseded_middle = result.superseded_middle; update_result.dropped_frame = result.dropped_frame; return update_result; } Plot_Frame_Pipeline::Frame_Publish_Result Plot_Frame_Pipeline::request_paint_update(std::uint64_t now_ns) { Frame_Publish_Result result; if (!middle_has_new_frame()) return result; Plot_Frame* middle = frame_by_role(Frame_Middle); if (paint_request_pending.load(std::memory_order_acquire)) { middle->metadata.update_request_id = pending_update_request_id.load(std::memory_order_acquire); middle->metadata.update_request_time_ns = pending_update_request_time.load(std::memory_order_acquire); return result; } result.update_request_id = ++next_update_request_id; result.update_request_time = now_ns; middle->metadata.update_request_id = result.update_request_id; middle->metadata.update_request_time_ns = result.update_request_time; pending_update_request_id.store(result.update_request_id, std::memory_order_release); pending_update_request_time.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; return result; } Plot_Frame_Pipeline::Frame_Consume_Result Plot_Frame_Pipeline::try_begin_paint(std::uint64_t now_ns) { Frame_Consume_Result result; result.update_was_pending = paint_request_pending.exchange(false, std::memory_order_acq_rel); if (middle_has_new_frame()) { Plot_Frame* old_painting = frame_by_role(Frame_Painting); Plot_Frame* new_painting = frame_by_role(Frame_Middle); auto swap = frame_control.try_swap_role(Frame_Middle, Frame_Painting, [this](const Triple_Buffer_View& view) { const Plot_Frame* middle = frame_by_index(view[Frame_Middle]); const Plot_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)); }); if (swap.result == Triple_Buffer_Result::Success) { result.new_frame = true; if (old_painting->metadata.frame_id && old_painting->metadata.paint_end_ns) { old_painting->metadata.transition_32_ns = now_ns; old_painting->metadata.wait_32_ns = now_ns > old_painting->metadata.paint_end_ns ? now_ns - old_painting->metadata.paint_end_ns : 0; result.returned_frame = old_painting->metadata; result.has_returned_frame = true; } new_painting->metadata.transition_23_ns = now_ns; new_painting->metadata.wait_23_ns = now_ns > new_painting->metadata.transition_12_ns ? now_ns - new_painting->metadata.transition_12_ns : 0; } else { result.paint_acquire_dropped = true; ++attempts.paint_role_busy_count; } } auto record = frame_control.try_mark_use_role(Frame_Painting); if (record.result == Triple_Buffer_Result::Success) result.lease = {frame_by_index(record.lease.index), &frame_control, record.lease}; else { result.paint_acquire_dropped = true; ++attempts.paint_role_busy_count; } return result; } bool Plot_Frame_Pipeline::middle_has_new_frame() const { const Plot_Frame* middle = frame_by_role(Frame_Middle); const Plot_Frame* painting = frame_by_role(Frame_Painting); return buffer_version_newer(middle->version.load(std::memory_order_acquire), painting->version.load(std::memory_order_acquire)); } bool Plot_Frame_Pipeline::paint_active() const { return frame_control.role_busy(Frame_Painting); } void Plot_Frame_Pipeline::cancel_all_update_states() { for (Plot_Frame& frame : frames) cancel_update_states(frame.presented_update_states); } } // namespace renderive