244 lines
8.6 KiB
C++
244 lines
8.6 KiB
C++
#include "Explicit_Renderable_Runtime.h"
|
|
#include "../../architecture/Renderable.h"
|
|
#include "../../base/Memory.h"
|
|
#include <mutex>
|
|
#include <system_error>
|
|
#include <utility>
|
|
|
|
namespace renderive {
|
|
|
|
namespace {
|
|
|
|
struct Explicit_Input_Slot {
|
|
Input_Data* data{};
|
|
std::uint64_t version = 0;
|
|
bool pending = false;
|
|
std::shared_ptr<Update_State> 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<std::shared_ptr<Update_State>> committed_update_states{memory_resource(Memory_Domain::Update_Completion)};
|
|
|
|
void initialize(Renderable& renderable) {
|
|
clear();
|
|
state_owner = renderable.create_render_state();
|
|
state = state_owner.get<Render_State>();
|
|
if (state)
|
|
state->version = 1;
|
|
input_owner = renderable.create_input_data();
|
|
input.data = input_owner.get<Input_Data>();
|
|
input.version = 0;
|
|
input.pending = false;
|
|
input.completion_state.reset();
|
|
}
|
|
|
|
void cancel_pending_completions() {
|
|
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> lock)
|
|
: lock(std::move(lock)) {}
|
|
std::unique_lock<std::mutex> lock;
|
|
};
|
|
|
|
struct Explicit_Input_Frame_Handle final : Render_Input_Frame_Handle {
|
|
Explicit_Input_Frame_Handle(Explicit_Renderable_Runtime_Data* runtime, std::unique_lock<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> lock;
|
|
};
|
|
|
|
struct Explicit_Input_Write_Handle final : Render_Input_Write_Handle {
|
|
Explicit_Input_Write_Handle(Explicit_Renderable_Runtime_Data* runtime, std::unique_lock<std::mutex> 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<Update_State> 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<std::mutex> lock;
|
|
bool notify_render{};
|
|
};
|
|
|
|
} // namespace
|
|
|
|
Explicit_Renderable_Runtime::Explicit_Renderable_Runtime(Renderable& owner)
|
|
: d(std::make_unique<Explicit_Renderable_Runtime_Data>()) {
|
|
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<std::shared_ptr<Update_State>>& 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<std::mutex> lock(d->state_mutex);
|
|
if (!d->state)
|
|
return {};
|
|
return Render_State_Read_Guard(d->state, std::make_unique<Explicit_State_Read_Handle>(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<std::mutex> lock(d->state_mutex);
|
|
if (!d->state)
|
|
return {};
|
|
return Render_State_Write_Guard(d->state, std::make_unique<Explicit_State_Write_Handle>(d.get(), std::move(lock)));
|
|
}
|
|
|
|
Render_Input_Write_Guard Explicit_Renderable_Runtime::acquire_edit_input(bool notify_render) {
|
|
std::unique_lock<std::mutex> lock(d->input_mutex);
|
|
if (!d->input.data)
|
|
return {};
|
|
return Render_Input_Write_Guard(d->input.data, std::make_unique<Explicit_Input_Write_Handle>(d.get(), std::move(lock), notify_render));
|
|
}
|
|
|
|
Render_Input_Frame_Guard Explicit_Renderable_Runtime::acquire_input_read(Renderable_Input_Role) {
|
|
std::unique_lock<std::mutex> 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<Explicit_Input_Frame_Handle>(d.get(), std::move(lock)));
|
|
}
|
|
|
|
Renderable_Frame_View Explicit_Renderable_Runtime::capture_frame_view() {
|
|
std::unique_lock<std::mutex> state_lock(d->state_mutex);
|
|
if (!d->state)
|
|
return {};
|
|
auto state_guard = Render_State_Read_Guard(
|
|
d->state,
|
|
std::make_unique<Explicit_State_Read_Handle>(std::move(state_lock)));
|
|
|
|
Render_Input_Frame_Guard input_guard;
|
|
std::unique_lock<std::mutex> input_lock(d->input_mutex);
|
|
if (d->input.data) {
|
|
input_guard = Render_Input_Frame_Guard(
|
|
d->input.data,
|
|
std::make_unique<Explicit_Input_Frame_Handle>(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<std::mutex> 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<Renderable_Runtime> make_explicit_renderable_runtime(Renderable& owner) {
|
|
return std::make_unique<Explicit_Renderable_Runtime>(owner);
|
|
}
|
|
|
|
} // namespace renderive
|