22 using sptr = std::shared_ptr<component_renderer_t>;
25 return std::make_shared<component_renderer_t>();
28 fabric::wiring::output_signal<component_renderer_t, compositing::compositing_node_t*>
39 return is_compositing_.test();
43 return compositing_mtx;
52 ZoneScopedN(
"Render Flow");
53 if (is_compositing_.test_and_set()) {
54 composite_is_outdated.test_and_set();
57 update_fragments(component);
59 ZoneScopedN(
"Start Render");
67 ZoneScopedN(
"Start layout render");
68 self->start_render(component_, w);
75 bool needs_compositing =
false;
77 ZoneScopedN(
"Render");
79 needs_compositing = repaint(component.get(), &window);
81 if (needs_compositing) {
82 ZoneScopedN(
"Queuing Composition");
88 std::atomic_flag* completion_flag,
89 std::atomic_flag* is_outdated,
93 ZoneScopedN(
"Compositing Layout");
94 std::scoped_lock lock(*mtx);
95 auto&& [root_node, must_recompose] = self->compose(root_ptr.get(), w);
98 ZoneScopedN(
"Compositing Frame");
102 completion_flag->clear();
103 if (is_outdated->test()) {
108 std::atomic_flag& flag = *is_outdated;
110 w->
bus->get_executor()->schedule(
113 std::atomic_flag* is_outdated,
116 ) -> fabric::task<> {
117 selff->
render(*w_, root_ptr_);
118 is_outdated->clear();
132 &composite_is_outdated,
140 ZoneScopedN(
"Compose All");
141 if (is_compositing_.test_and_set()) {
142 composite_is_outdated.test_and_set();
150 std::atomic_flag* completion_flag,
151 std::atomic_flag* is_outdated,
154 ) -> fabric::task<> {
155 ZoneScopedN(
"Compositing Layout");
156 std::scoped_lock lock(*mtx);
157 auto&& [root_node, must_recompose] = self->compose(root_ptr.get(), w);
159 if (must_recompose) {
160 ZoneScopedN(
"Compositing Frame");
164 completion_flag->clear();
165 if (is_outdated->test()) {
170 std::atomic_flag& flag = *is_outdated;
172 w->
bus->get_executor()->schedule(
175 std::atomic_flag* is_outdated,
178 ) -> fabric::task<> {
179 selff->
render(*w_, root_ptr_);
180 is_outdated->clear();
194 &composite_is_outdated,
201 ZoneScopedN(
"Repaint Component");
202 auto* parent = component->parent.has_value()
203 ? &(component->parent.value()->get_data<render_data_t>().compositing_node_)
206 update_fragment(component.get(), parent);
210 ZoneScopedN(
"Render All");
211 if (is_compositing_.test()) {
216 ZoneScopedN(
"Start Render");
224 ZoneScopedN(
"Start render");
225 self->start_render(component_, w);
233 bool needs_compositing =
false;
235 ZoneScopedN(
"Render");
237 needs_compositing = repaint(root.get(), &win);
240 return needs_compositing;
245 ZoneScopedN(
"Fragments");
246 auto* parent = component->parent.has_value()
247 ? &(component->parent.value()->get_data<render_data_t>().compositing_node_)
249 update_fragment(component.get(), parent);
251 for (
auto& child: component->children) {
252 update_fragments(child);
256 bool update_compositing_operation(
257 component_base_t* component, compositing::compositing_node_t* parent_node
259 ZoneScopedN(
"Update Compose Op");
260 static auto get_num_value = [](
const auto& it) ->
auto {
267 bool old_is_flattened = data.compositing_node_.is_flattened_node();
269 auto& at = component->get_style();
270 data.compositing_node_.id = (
unsigned long)(component->state().get());
271 data.compositing_node_.
op = {
272 .x =
static_cast<int>(get_num_value(at.x) + get_num_value(at.margin.left)),
273 .y =
static_cast<int>(get_num_value(at.y) + get_num_value(at.margin.top)),
274 .orig_x =
static_cast<int>(get_num_value(at.padding.left)),
275 .orig_y =
static_cast<int>(get_num_value(at.padding.top)),
276 .w =
static_cast<int>(get_num_value(at.width)),
277 .h =
static_cast<int>(get_num_value(at.height)),
278 .rot = at.rotation.value_as_base_unit(),
281 .animated = component->state()->is_animated(),
284 data.compositing_node_.set_parent(parent_node);
286 return old_op != data.compositing_node_.
op
287 or old_is_flattened != data.compositing_node_.is_flattened_node();
291 update_fragment(component_base_t* component, compositing::compositing_node_t* parent_node) {
292 ZoneScopedN(
"Update Fragment");
295 if (update_compositing_operation(component, parent_node)) {
296 data.graphics_dirty_ =
true;
299 if (data.graphics_dirty_) {
300 data.compositing_node_.mark_flattening_target_dirty();
301 paint_fragment(component, data.compositing_node_);
306 paint_fragment(component_base_t* component, compositing::compositing_node_t& compositing_node) {
307 auto get_num_value = [](
const auto& it) ->
auto {
311 auto& fragment = compositing_node.graphics;
314 auto& at = component->get_style();
315 int half_top_border = at.border_width.top >> 1;
316 int half_bottom_border = (at.border_width.bottom >> 1) + (at.border_width.bottom & 1);
317 int half_left_border = at.border_width.left >> 1;
318 int half_right_border = (at.border_width.right >> 1) + (at.border_width.right & 1);
321 int x1 = (half_left_border)-get_num_value(at.padding.left);
322 int y1 = (half_top_border)-get_num_value(at.padding.top);
324 int x2 = x1 + get_num_value(at.width) - (half_left_border) - (half_right_border);
328 int y3 = y1 + get_num_value(at.height) - (half_top_border) - (half_bottom_border);
333 fragment.draw<vg::rectangle>()
338 .fill(component->get_style().background);
339 fragment.draw<vg::line>()
340 .x1(x1 - half_left_border)
342 .x2(x2 + half_right_border)
344 .stroke(at.border.top)
345 .stroke_width(at.border_width.top)
346 .stroke_dasharray(at.border_dasharray.top);
347 fragment.draw<vg::line>()
348 .x1(x4 + half_right_border)
350 .x2(x3 - half_left_border)
352 .stroke(at.border.bottom)
353 .stroke_width(at.border_width.bottom)
354 .stroke_dasharray(at.border_dasharray.bottom);
355 fragment.draw<vg::line>()
357 .y1(y3 + half_bottom_border)
359 .y2(y1 - half_top_border)
360 .stroke(at.border.left)
361 .stroke_width(at.border_width.left)
362 .stroke_dasharray(at.border_dasharray.left);
363 fragment.draw<vg::line>()
365 .y1(y2 - half_top_border)
367 .y2(y4 + half_bottom_border)
368 .stroke(at.border.right)
369 .stroke_width(at.border_width.right)
370 .stroke_dasharray(at.border_dasharray.right);
373 component->get_event_dispatcher()->paint_fragment(fragment);
389 void start_render(component_base_t* component, graphics::window_t* render_target) {
391 if (data.graphics_dirty_ or data.compositing_node_.is_flattened_node()) {
392 data.compositing_node_.start_render(render_target);
395 for (
auto& child: component->children) {
396 start_render(child.get(), render_target);
400 bool repaint(component_base_t* component, graphics::window_t* render_target) {
402 if (data.graphics_dirty_ or data.compositing_node_.is_dirty_from_flattening()) {
403 data.graphics_dirty_ =
false;
404 data.compositing_dirty_ =
true;
405 data.compositing_node_.render(render_target);
408 for (
auto& child: component->children) {
409 if (repaint(child.get(), render_target)) {
410 data.compositing_dirty_ =
true;
416 std::pair<compositing::compositing_node_t*, bool>
417 compose(component_base_t* component, graphics::window_t* render_target) {
420 bool did_compositing =
false;
421 if (data.compositing_dirty_) {
422 data.compositing_dirty_ =
false;
424 data.compositing_node_.compose_own(render_target);
426 for (
auto& child: component->children) {
427 auto&& [node, _] = compose(child.get(), render_target);
428 if (
nullptr != node) {
429 data.compositing_node_.compose(render_target, node);
433 did_compositing =
true;
436 if (data.compositing_node_.is_flattened_node()) {
437 return {
nullptr, did_compositing};
439 return {&data.compositing_node_, did_compositing};
444 std::atomic_flag is_compositing_{
false};
445 std::atomic_flag composite_is_outdated{
false};
447 std::mutex compositing_mtx{};