仍然闪烁
This commit is contained in:
+224
-40
@@ -11,7 +11,9 @@
|
||||
#include <system_error>
|
||||
#include <variant>
|
||||
#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 {
|
||||
@@ -137,16 +139,46 @@ struct Curve_Geometry_Request {
|
||||
std::shared_ptr<const Curve_Source> source;
|
||||
std::pmr::vector<std::shared_ptr<Update_State>> 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<Curve_Geometry_Result::Segment> 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<const Curve_Geometry_Request> 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<const Curve_Geometry_Request> request;
|
||||
Curve_Geometry_Key key;
|
||||
std::vector<Curve_Geometry_Partition> partitions;
|
||||
std::vector<Curve_Geometry_Partial_Result> results;
|
||||
std::pmr::vector<std::shared_ptr<Update_State>> 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_update_states(Curve_Geometry_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_curve_result_states(Curve_Geometry_Result& result, std::error_code error, Update_Outcome outcome) {
|
||||
for (const auto& state : result.update_states) {
|
||||
if (state)
|
||||
@@ -213,37 +245,166 @@ static void append_curve_sample_run(Curve_Geometry_Result& result, const Curve_G
|
||||
result.segments.back().points = std::move(points);
|
||||
}
|
||||
|
||||
static void build_curve_geometry(Curve_Geometry_Result& result, const Curve_Geometry_Request& request) {
|
||||
result.segments.clear();
|
||||
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<std::size_t>(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;
|
||||
return 0;
|
||||
if (const auto* data_points = std::get_if<Curve_Point_Source>(request.source.get()))
|
||||
return data_points->values.size();
|
||||
if (const auto* samples = std::get_if<Curve_Sample_Source>(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<Curve_Point_Source>(request.source.get())) {
|
||||
result.segments.reserve(data_points->values.size());
|
||||
Curve_Geometry_Result::Segment* segment = nullptr;
|
||||
for (const PointF& point : data_points->values)
|
||||
append_curve_point_segment(result, segment, request.mapping, point);
|
||||
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<Curve_Sample_Source>(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<int>(samples->values.size()), static_cast<double>(index));
|
||||
return finite_point(request.mapping.map(coord, value));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::shared_ptr<Curve_Geometry_Job> create_curve_geometry_job(std::shared_ptr<Curve_Geometry_Request> 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<std::shared_ptr<Update_State>> update_states = std::move(request->update_states);
|
||||
auto job = std::make_shared<Curve_Geometry_Job>(
|
||||
std::shared_ptr<const Curve_Geometry_Request>(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;
|
||||
}
|
||||
const auto* samples = std::get_if<Curve_Sample_Source>(request.source.get());
|
||||
if (!samples || samples->values.size() < 2 || request.data_range.length() == 0.0)
|
||||
if (const auto* data_points = std::get_if<Curve_Point_Source>(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<Curve_Sample_Source>(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<int>(i);
|
||||
continue;
|
||||
}
|
||||
if (run_start >= 0)
|
||||
append_curve_sample_run(result, request, *samples, run_start, static_cast<int>(i) - 1);
|
||||
run_start = -1;
|
||||
}
|
||||
if (run_start >= 0)
|
||||
append_curve_sample_run(result, request, *samples, run_start, static_cast<int>(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;
|
||||
int run_start = -1;
|
||||
int sample_count = static_cast<int>(samples->values.size());
|
||||
for (int i = 0; i < sample_count; ++i) {
|
||||
double value = samples->values[static_cast<std::size_t>(i)];
|
||||
if (std::isfinite(value)) {
|
||||
if (run_start < 0)
|
||||
run_start = i;
|
||||
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;
|
||||
}
|
||||
if (run_start >= 0)
|
||||
append_curve_sample_run(result, request, *samples, run_start, i - 1);
|
||||
run_start = -1;
|
||||
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;
|
||||
}
|
||||
if (run_start >= 0)
|
||||
append_curve_sample_run(result, request, *samples, run_start, sample_count - 1);
|
||||
result.update_states = std::move(job.update_states);
|
||||
return result;
|
||||
}
|
||||
struct Curve_Private : Typed_Render_Data<Curve, Curve_Render_State, Curve_Input_Data> {
|
||||
struct Curve_Private : Typed_Render_Data<Curve, Curve_Render_State, Curve_Input_Data>, Frame_Prepare_Subtask_Source {
|
||||
static constexpr std::size_t Input_Queue_Capacity = 8;
|
||||
std::shared_ptr<const Curve_Source> source = std::make_shared<Curve_Source>();
|
||||
rigtorp::MPMCQueue<std::shared_ptr<Curve_Input_Block>> input_queue;
|
||||
@@ -303,13 +464,39 @@ struct Curve_Private : Typed_Render_Data<Curve, Curve_Render_State, Curve_Input_
|
||||
}
|
||||
void prepare_data(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override {
|
||||
(void)snapshot;
|
||||
consume_input_queue();
|
||||
submit_geometry_if_needed(snapshot, frame_view);
|
||||
(void)frame_view;
|
||||
}
|
||||
void submit_geometry_if_needed(const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& 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<Render_Task_Graph_Node> 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<Curve_Geometry_Job> 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;
|
||||
return {};
|
||||
auto domain_axis = s->domain_axis.lock();
|
||||
auto value_axis = s->value_axis.lock();
|
||||
if (!domain_axis || !value_axis) {
|
||||
@@ -318,7 +505,7 @@ struct Curve_Private : Typed_Render_Data<Curve, Curve_Render_State, Curve_Input_
|
||||
state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
|
||||
}
|
||||
pending_geometry_update_states.clear();
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
Axis_Mapping_2D mapping = Axis_Render_Access::mapping(domain_axis.get(), value_axis.get(), snapshot);
|
||||
Curve_Geometry_Key key;
|
||||
@@ -334,8 +521,7 @@ struct Curve_Private : Typed_Render_Data<Curve, Curve_Render_State, Curve_Input_
|
||||
key.domain = curve_axis_key(mapping.domain);
|
||||
key.value = curve_axis_key(mapping.value);
|
||||
if (submitted_geometry_key && key == *submitted_geometry_key)
|
||||
return;
|
||||
submitted_geometry_key = key;
|
||||
return {};
|
||||
auto request = std::make_shared<Curve_Geometry_Request>();
|
||||
request->mode = key.mode;
|
||||
request->mapping = mapping;
|
||||
@@ -346,10 +532,8 @@ struct Curve_Private : Typed_Render_Data<Curve, Curve_Render_State, Curve_Input_
|
||||
request->source = source;
|
||||
request->update_states = std::move(pending_geometry_update_states);
|
||||
pending_geometry_update_states = std::pmr::vector<std::shared_ptr<Update_State>>(memory_resource(Memory_Domain::Update_Completion));
|
||||
geometry_result.emplace();
|
||||
geometry_result->mapping = request->mapping;
|
||||
build_curve_geometry(*geometry_result, *request);
|
||||
geometry_result->update_states = std::move(request->update_states);
|
||||
std::size_t worker_count = std::max<std::size_t>(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);
|
||||
|
||||
Reference in New Issue
Block a user