276 lines
10 KiB
C++
276 lines
10 KiB
C++
#include "Explicit_Frame_Flow.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<Render_Output> output) : output(std::move(output)) {}
|
|
std::shared_ptr<Render_Output> output;
|
|
};
|
|
|
|
struct Explicit_Present_Surface_Handle final {
|
|
explicit Explicit_Present_Surface_Handle(std::shared_ptr<Render_Output> output) : output_value(std::move(output)) {}
|
|
|
|
[[nodiscard]] Render_Output* output() const {
|
|
return output_value.get();
|
|
}
|
|
|
|
std::shared_ptr<Render_Output> output_value;
|
|
};
|
|
|
|
Explicit_Render_Surface_Handle* explicit_handle(Render_Frame_Surface& surface) {
|
|
return static_cast<Explicit_Render_Surface_Handle*>(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<std::mutex> 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;
|
|
}
|
|
|
|
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),
|
|
present_active.load(std::memory_order_acquire),
|
|
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<std::mutex> 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<Render_Output> output;
|
|
{
|
|
std::lock_guard<std::mutex> lock(surface_mutex);
|
|
output = std::move(present_surface);
|
|
}
|
|
if (output)
|
|
cancel_update_states(output->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<Render_Output>();
|
|
auto handle = std::make_unique<Explicit_Render_Surface_Handle>(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->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<Render_Output> output = std::move(handle->output);
|
|
Size size = output ? output->image.size() : Size{};
|
|
{
|
|
std::lock_guard<std::mutex> 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<Render_Output> output;
|
|
{
|
|
std::lock_guard<std::mutex> lock(surface_mutex);
|
|
output = present_surface;
|
|
}
|
|
if (!output)
|
|
return result;
|
|
if (output->metadata) {
|
|
output->metadata->update_request_id = ++next_present_request_id;
|
|
output->metadata->update_request_time_ns = now_ns;
|
|
result.update_request_id = 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<Frame_Lifecycle_Record>& returned_frame_record) {
|
|
returned_frame_record.reset();
|
|
std::shared_ptr<Render_Output> output;
|
|
{
|
|
std::lock_guard<std::mutex> lock(surface_mutex);
|
|
output = present_surface;
|
|
}
|
|
if (!output || present_active.exchange(true, std::memory_order_acq_rel))
|
|
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);
|
|
if (is_new && output->metadata) {
|
|
output->metadata->transition_23_ns = now_ns;
|
|
output->metadata->present_queue_wait_ns = now_ns > output->metadata->transition_12_ns ? now_ns - output->metadata->transition_12_ns : 0;
|
|
}
|
|
return Present_Surface_Lease_Access::from_handle<Explicit_Present_Surface_Handle>(
|
|
is_new,
|
|
pending,
|
|
std::move(output));
|
|
}
|
|
|
|
std::shared_ptr<Frame_Lifecycle_Record> Explicit_Frame_Flow::end_present(Present_Surface_Lease&& surface, std::uint64_t paint_begin_ns, std::uint64_t paint_end_ns) {
|
|
std::shared_ptr<Frame_Lifecycle_Record> 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);
|
|
present_active.store(false, std::memory_order_release);
|
|
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(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<std::shared_ptr<Update_State>>& output) {
|
|
std::lock_guard<std::mutex> 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<std::mutex> lock(request_mutex);
|
|
return Explicit_Diagnostics{
|
|
view_active_state.load(std::memory_order_acquire),
|
|
static_cast<std::uint64_t>(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);
|
|
present_active.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
|