1 /*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can
5 * be found in the LICENSE file.
6 *
7 */
8
9 #pragma once
10
11 //
12 //
13 //
14
15 #include <string.h>
16 #include <assert.h>
17 #include <stdbool.h>
18
19 #include "grid.h"
20 #include "macros.h"
21 #include "runtime_cl_12.h"
22
23 //
24 // SKC grid dependencies can be represented with a DAG.
25 //
26 // This dependency graph may be modified to include some sort of block
27 // pool barrier to make block recovery explicit (and guaranteed safe).
28 //
29 //
30 // PATH BUILDER
31 // |
32 // |
33 // |
34 // v
35 // RASTER BUILDER
36 // |
37 // +----+ | +----+
38 // Set Ops | | | | | Set Ops
39 // | v v v |
40 // +--COMPOSITION STYLING--+
41 // | |
42 // | +--------+
43 // | |
44 // v v
45 // SURFACE
46 //
47 //
48 // STAGE DEPENDENCIES
49 // ============== ============================
50 // PATH BUILDER -
51 // RASTER BUILDER PATH BUILDER
52 // COMPOSITION RASTER BUILDER, *COMPOSITION
53 // STYLING -, *STYLING
54 // SURFACE COMPOSITION, STYLING
55 //
56
57 //
58 // How many active grids can/should we have?
59 //
60 // FIXME -- we'll need to provide a small level of indirection if we
61 // want to support a much larger number of work-in-progress grids
62 //
63 // For now and for simplicity, unify all grid ids in one set.
64 //
65
66 typedef skc_uchar skc_grid_id_t; // 256 values
67 #define SKC_GRID_ID_INVALID SKC_UCHAR_MAX // 255
68
69 #define SKC_GRID_SIZE_IDS (SKC_GRID_ID_INVALID-1)
70 #define SKC_GRID_SIZE_WORDS ((SKC_GRID_SIZE_IDS+31)/32)
71
72 //
73 //
74 //
75
76 typedef enum skc_grid_state_e {
77
78 SKC_GRID_STATE_READY,
79 SKC_GRID_STATE_WAITING,
80 SKC_GRID_STATE_FORCED,
81 SKC_GRID_STATE_EXECUTING,
82 SKC_GRID_STATE_COMPLETE,
83 SKC_GRID_STATE_DETACHED,
84
85 SKC_GRID_STATE_COUNT
86
87 } skc_grid_state_e;
88
89 //
90 //
91 //
92
93 struct skc_grid_pfn_name
94 {
95 skc_grid_pfn pfn;
96 char const * name;
97 };
98
99 //
100 //
101 //
102
103 struct skc_grid
104 {
105 skc_grid_state_e state;
106 skc_uint id;
107
108 struct skc_grid_deps * deps; // backpointer to deps
109 void * * addr; // pointer to invalidate
110
111 void * data;
112
113 struct skc_grid_pfn_name waiting; // optional - if defined, typically used to yank the grid away from host
114 struct skc_grid_pfn_name execute; // optional - starts execution of waiting grid
115 struct skc_grid_pfn_name dispose; // optional - invoked when grid is complete
116
117 struct {
118 skc_uint words[SKC_GRID_SIZE_WORDS]; // 0:inactive, 1:active
119 skc_uint count;
120 } before;
121
122 struct {
123 skc_uint words[SKC_GRID_SIZE_WORDS]; // 0:inactive, 1:active
124 skc_uint count;
125 } after;
126 };
127
128 //
129 //
130 //
131
132 struct skc_grid_deps
133 {
134 struct skc_runtime * runtime;
135 struct skc_scheduler * scheduler;
136
137 skc_grid_id_t * handle_map;
138
139 struct skc_grid grids [SKC_GRID_SIZE_IDS]; // deps + pfns + data
140 skc_uint active[SKC_GRID_SIZE_WORDS]; // 1:inactive, 0:active
141
142 skc_uint count; // number of active ids
143 };
144
145 //
146 //
147 //
148
149 static
150 void
skc_grid_call(skc_grid_t const grid,struct skc_grid_pfn_name const * const pn)151 skc_grid_call(skc_grid_t const grid, struct skc_grid_pfn_name const * const pn)
152 {
153 if (pn->pfn != NULL) {
154 pn->pfn(grid);
155 }
156 }
157
158 static
159 void
skc_grid_schedule(skc_grid_t const grid,struct skc_grid_pfn_name const * const pn)160 skc_grid_schedule(skc_grid_t const grid, struct skc_grid_pfn_name const * const pn)
161 {
162 if (pn->pfn != NULL) {
163 skc_scheduler_schedule(grid->deps->scheduler,pn->pfn,grid,pn->name);
164 }
165 }
166
167 //
168 //
169 //
170
171 static
172 void
skc_grid_invalidate(skc_grid_t const grid)173 skc_grid_invalidate(skc_grid_t const grid)
174 {
175 if (grid->addr != NULL) {
176 *grid->addr = NULL;
177 }
178 }
179
180 //
181 //
182 //
183
184 #if 0
185 skc_grid_t
186 skc_grid_move(skc_grid_t const grid,
187 skc_grid_state_e * const state,
188 skc_grid_t * const addr,
189 void * const data)
190 {
191 skc_grid_invalidate(grid);
192
193 grid->state = state;
194 grid->addr = addr;
195 grid->data = data;
196
197 return grid;
198 }
199 #endif
200
201 void *
skc_grid_get_data(skc_grid_t const grid)202 skc_grid_get_data(skc_grid_t const grid)
203 {
204 return grid->data;
205 }
206
207 void
skc_grid_set_data(skc_grid_t const grid,void * const data)208 skc_grid_set_data(skc_grid_t const grid, void * const data)
209 {
210 grid->data = data;
211 }
212
213 //
214 //
215 //
216
217 skc_grid_deps_t
skc_grid_deps_create(struct skc_runtime * const runtime,struct skc_scheduler * const scheduler,skc_uint const handle_pool_size)218 skc_grid_deps_create(struct skc_runtime * const runtime,
219 struct skc_scheduler * const scheduler,
220 skc_uint const handle_pool_size)
221 {
222 struct skc_grid_deps * const deps = skc_runtime_host_perm_alloc(runtime,SKC_MEM_FLAGS_READ_WRITE,sizeof(*deps));
223
224 // save runtime
225 deps->runtime = runtime;
226 deps->scheduler = scheduler;
227
228 size_t const handle_map_size = sizeof(*deps->handle_map) * handle_pool_size;
229
230 // allocate handle map
231 deps->handle_map = skc_runtime_host_perm_alloc(runtime,SKC_MEM_FLAGS_READ_WRITE,handle_map_size);
232
233 // initialize handle map
234 memset(deps->handle_map,0xFF,handle_map_size);
235
236 // grids
237 struct skc_grid * const grids = deps->grids;
238
239 #if 0 // DELETE ME LATER
240 // initalize ids once -- could always infer id using offsetof()
241 for (skc_uint id=0; id < SKC_GRID_SIZE_IDS; id++)
242 grids[id].id = id;
243 #endif
244
245 // mark all grids inactive except for last bit -- 1:inactive / 0:active
246 for (skc_uint ii=0; ii < SKC_GRID_SIZE_WORDS-1; ii++)
247 deps->active[ii] = 0xFFFFFFFF;
248
249 // last bit is marked active so that it is never allocated
250 deps->active[SKC_GRID_SIZE_WORDS-1] = 0x7FFFFFFF;
251
252 // nothing active
253 deps->count = 1;
254
255 return deps;
256 }
257
258 void
skc_grid_deps_dispose(skc_grid_deps_t const deps)259 skc_grid_deps_dispose(skc_grid_deps_t const deps)
260 {
261 //
262 // FIXME -- debug checks for active grids
263 //
264 skc_runtime_host_perm_free(deps->runtime,deps->handle_map);
265 skc_runtime_host_perm_free(deps->runtime,deps);
266 }
267
268 //
269 //
270 //
271
272 #ifndef NDEBUG
273
274 void
skc_grid_deps_debug(struct skc_grid_deps const * const deps)275 skc_grid_deps_debug(struct skc_grid_deps const * const deps)
276 {
277 fprintf(stderr,
278 "00000000000000001111111111111111\n"
279 "0123456789ABCDEF0123456789ABCDEF\n"
280 "--------------------------------\n");
281
282 for (skc_uint ii=0; ii<SKC_GRID_SIZE_WORDS; ii++)
283 {
284 skc_uint const a = deps->active[ii];
285 fprintf(stderr,
286 "%1u%1u%1u%1u%1u%1u%1u%1u%1u%1u%1u%1u%1u%1u%1u%1u"
287 "%1u%1u%1u%1u%1u%1u%1u%1u%1u%1u%1u%1u%1u%1u%1u%1u\n",
288 (a>>0x00)&1,(a>>0x01)&1,(a>>0x02)&1,(a>>0x03)&1,
289 (a>>0x04)&1,(a>>0x05)&1,(a>>0x06)&1,(a>>0x07)&1,
290 (a>>0x08)&1,(a>>0x09)&1,(a>>0x0A)&1,(a>>0x0B)&1,
291 (a>>0x0C)&1,(a>>0x0D)&1,(a>>0x0E)&1,(a>>0x0F)&1,
292 (a>>0x10)&1,(a>>0x11)&1,(a>>0x12)&1,(a>>0x13)&1,
293 (a>>0x14)&1,(a>>0x15)&1,(a>>0x16)&1,(a>>0x17)&1,
294 (a>>0x18)&1,(a>>0x19)&1,(a>>0x1A)&1,(a>>0x1B)&1,
295 (a>>0x1C)&1,(a>>0x1D)&1,(a>>0x1E)&1,(a>>0x1F)&1);
296 }
297
298 fprintf(stderr,"\n");
299 }
300
301 #endif
302
303 //
304 //
305 //
306
307 skc_grid_t
skc_grid_deps_attach(skc_grid_deps_t const deps,skc_grid_t * const addr,void * const data,skc_grid_pfn waiting_pfn,skc_grid_pfn execute_pfn,skc_grid_pfn dispose_pfn,char const * const waiting_name,char const * const execute_name,char const * const dispose_name)308 skc_grid_deps_attach(skc_grid_deps_t const deps,
309 skc_grid_t * const addr,
310 void * const data,
311 skc_grid_pfn waiting_pfn, // upon READY > WAITING
312 skc_grid_pfn execute_pfn, // upon READY/WAITING > EXECUTING
313 skc_grid_pfn dispose_pfn, // upon EXECUTING > COMPLETE
314 char const * const waiting_name,
315 char const * const execute_name,
316 char const * const dispose_name)
317 {
318 //
319 // FIXME -- no more ids -- either fatal or flush & wait for grids to be released
320 //
321 // assert(deps->count < SKC_GRID_SIZE_IDS);
322 //
323 while (deps->count == SKC_GRID_SIZE_IDS)
324 skc_scheduler_wait_one(deps->scheduler);
325
326 // otherwise, an id exists so decrement count
327 deps->count += 1;
328
329 // find first set bit (1:inactive)
330 skc_uint * active = deps->active;
331 skc_uint first = 0;
332
333 while (1)
334 {
335 skc_uint const idx = SKC_LZCNT_32(*active);
336
337 first += idx;
338
339 if (idx < 32)
340 {
341 // make inactive bit active: 1 -> 0
342 *active &= ~(0x80000000 >> idx); // 0:active
343 break;
344 }
345
346 // otherwise, inspect next word for inactive bit
347 active += 1;
348 }
349
350 struct skc_grid * const grid = deps->grids + first;
351
352 // save grid pointer
353 if (addr != NULL)
354 *addr = grid;
355
356 // initialize elem
357 *grid = (struct skc_grid){
358 .state = SKC_GRID_STATE_READY,
359 .id = first,
360 .deps = deps,
361 .addr = addr,
362 .data = data,
363 .waiting = { .pfn = waiting_pfn, .name = waiting_name },
364 .execute = { .pfn = execute_pfn, .name = execute_name },
365 .dispose = { .pfn = dispose_pfn, .name = dispose_name },
366 .before = { { 0 }, 0 },
367 .after = { { 0 }, 0 }
368 };
369
370 return grid;
371 }
372
373 //
374 //
375 //
376
377 static
378 skc_bool
skc_grid_words_set(skc_uint ids[SKC_GRID_SIZE_WORDS],skc_uint const id)379 skc_grid_words_set(skc_uint ids[SKC_GRID_SIZE_WORDS], skc_uint const id)
380 {
381 skc_uint * const ptr = ids + (id/32);
382 skc_uint const pre = *ptr;
383 skc_uint const post = pre | (0x80000000 >> (id & 0x1F)); // set
384
385 *ptr = post;
386
387 return pre != post;
388 }
389
390 static
391 skc_bool
skc_grid_words_clear(skc_uint ids[SKC_GRID_SIZE_WORDS],skc_uint const id)392 skc_grid_words_clear(skc_uint ids[SKC_GRID_SIZE_WORDS], skc_uint const id)
393 {
394 skc_uint * const ptr = ids + (id/32);
395 skc_uint const pre = *ptr;
396 skc_uint const post = pre & ~(0x80000000 >> (id & 0x1F)); // clear
397
398 *ptr = post;
399
400 return pre != post;
401 }
402
403 //
404 // we may want to allow the host to detach a grid
405 //
406
407 static
408 void
skc_grid_detach(skc_grid_t const grid)409 skc_grid_detach(skc_grid_t const grid)
410 {
411 // for now make sure grid is complete
412 // assert(grid->state == SKC_GRID_STATE_COMPLETE);
413
414 // transition state
415 grid->state = SKC_GRID_STATE_DETACHED;
416
417 //
418 // FIXME -- save profiling info
419 //
420
421 // cleanup
422 if (skc_grid_words_set(grid->deps->active,grid->id)) // 1:inactive
423 grid->deps->count -= 1;
424 }
425
426 //
427 //
428 //
429
430 void
skc_grid_map(skc_grid_t const grid,skc_handle_t const handle)431 skc_grid_map(skc_grid_t const grid, skc_handle_t const handle)
432 {
433 grid->deps->handle_map[handle] = grid->id;
434 }
435
436 //
437 //
438 //
439
440 void
skc_grid_deps_force(skc_grid_deps_t const deps,skc_handle_t const * const handles,skc_uint const count)441 skc_grid_deps_force(skc_grid_deps_t const deps,
442 skc_handle_t const * const handles,
443 skc_uint const count)
444 {
445 //
446 // FIXME -- test to make sure handles aren't completely out of range integers
447 //
448 skc_grid_id_t * const handle_map = deps->handle_map;
449
450 for (skc_uint ii=0; ii<count; ii++)
451 {
452 skc_grid_id_t grid_id = handle_map[SKC_TYPED_HANDLE_TO_HANDLE(handles[ii])];
453
454 if (grid_id < SKC_GRID_ID_INVALID)
455 {
456 skc_grid_t const grid = deps->grids + grid_id;
457
458 skc_grid_force(grid);
459
460 while (grid->state >= SKC_GRID_STATE_COMPLETE)
461 skc_scheduler_wait_one(deps->scheduler);
462 }
463 }
464 }
465
466 void
skc_grid_deps_unmap(skc_grid_deps_t const deps,skc_handle_t const * const handles,skc_uint const count)467 skc_grid_deps_unmap(skc_grid_deps_t const deps,
468 skc_handle_t const * const handles,
469 skc_uint const count)
470 {
471 skc_grid_id_t * const handle_map = deps->handle_map;
472
473 for (skc_uint ii=0; ii<count; ii++)
474 handle_map[handles[ii]] = SKC_GRID_ID_INVALID;
475 }
476
477 //
478 // NOTE: We want this routine to be very very fast. The array of bit
479 // flags is probably as fast as we can go for a modest number of
480 // grids.
481 //
482 // NOTE: The before grid should never be NULL. This means the grid's
483 // lifecycle should match the lifetime of the object it represents.
484 // This also means the grid "invalidation upon start" feature should
485 // be well understood before using it to clear the skc_grid_t.
486 //
487
488 void
skc_grid_happens_after_grid(skc_grid_t const after,skc_grid_t const before)489 skc_grid_happens_after_grid(skc_grid_t const after,
490 skc_grid_t const before)
491 {
492 // declarations can't be made on non-ready grids
493 assert(after->state == SKC_GRID_STATE_READY);
494
495 if (before->state >= SKC_GRID_STATE_COMPLETE)
496 return;
497
498 if (skc_grid_words_set(after->before.words,before->id))
499 after->before.count += 1;
500
501 if (skc_grid_words_set(before->after.words,after->id))
502 before->after.count += 1;
503 }
504
505 void
skc_grid_happens_after_handle(skc_grid_t const after,skc_handle_t const before)506 skc_grid_happens_after_handle(skc_grid_t const after, skc_handle_t const before)
507 {
508 assert(after->state == SKC_GRID_STATE_READY);
509
510 skc_uint const id_before = after->deps->handle_map[before];
511
512 if (id_before >= SKC_GRID_ID_INVALID)
513 return;
514
515 if (skc_grid_words_set(after->before.words,id_before))
516 after->before.count += 1;
517
518 skc_grid_t const grid_before = after->deps->grids + id_before;
519
520 if (skc_grid_words_set(grid_before->after.words,after->id))
521 grid_before->after.count += 1;
522 }
523
524 //
525 // Remove dependency from grid
526 //
527
528 static
529 void
skc_grid_clear_dependency(skc_grid_t const after,skc_uint const before)530 skc_grid_clear_dependency(skc_grid_t const after, skc_uint const before)
531 {
532 skc_bool const is_change = skc_grid_words_clear(after->before.words,before);
533
534 assert(is_change); // for now let's make sure this is a rising edge
535
536 after->before.count -= 1;
537
538 if ((after->before.count == 0) && ((after->state == SKC_GRID_STATE_WAITING) ||
539 (after->state == SKC_GRID_STATE_FORCED)))
540 {
541 // schedule grid for execution
542 after->state = SKC_GRID_STATE_EXECUTING;
543 skc_grid_schedule(after,&after->execute);
544 }
545 }
546
547 //
548 // Start the ready grid and wait for dependencies to complete
549 //
550
551 void
skc_grid_start(skc_grid_t const grid)552 skc_grid_start(skc_grid_t const grid)
553 {
554 // nothing to do if this grid isn't in a ready state
555 if (grid->state != SKC_GRID_STATE_READY)
556 return;
557
558 // record transition through waiting state
559 grid->state = SKC_GRID_STATE_WAITING;
560
561 // the waiting pfn may be null -- e.g. the path builder
562 // skc_grid_schedule(grid,&grid->waiting);
563 skc_grid_call(grid,&grid->waiting);
564
565 // clear the reference
566 skc_grid_invalidate(grid);
567
568 // execute if there are no dependencies
569 if (grid->before.count == 0)
570 {
571 // tell grid it can execute
572 grid->state = SKC_GRID_STATE_EXECUTING;
573 skc_grid_schedule(grid,&grid->execute);
574 }
575 }
576
577 //
578 // Start this grid and all its ready dependencies
579 //
580
581 void
skc_grid_force(skc_grid_t const grid)582 skc_grid_force(skc_grid_t const grid)
583 {
584 // return if this grid was forced, executing or complete
585 if (grid->state >= SKC_GRID_STATE_FORCED)
586 return;
587
588 // if ready then move to waiting state
589 if (grid->state == SKC_GRID_STATE_READY)
590 {
591 // tell grid to wait for execution
592 grid->state = SKC_GRID_STATE_WAITING;
593
594 // the waiting pfn may be null -- e.g. the path builder
595 // skc_grid_schedule(grid,&grid->waiting);
596 skc_grid_call(grid,&grid->waiting);
597
598 // clear the reference
599 skc_grid_invalidate(grid);
600 }
601
602 skc_uint before_count = grid->before.count;
603
604 // if there are no grid dependencies then execute
605 if (before_count == 0)
606 {
607 // tell grid it can execute
608 grid->state = SKC_GRID_STATE_EXECUTING;
609 skc_grid_schedule(grid,&grid->execute);
610 }
611 else // otherwise, start or make waiting all dependencies
612 {
613 grid->state = SKC_GRID_STATE_FORCED;
614
615 struct skc_grid * before = grid->deps->grids;
616 skc_uint * before_words = grid->before.words;
617 skc_uint active = *before_words++;
618
619 while (true)
620 {
621 // find first active
622 skc_uint const idx = SKC_LZCNT_32(active);
623
624 // no bits set so inspect next word
625 if (idx == 32)
626 {
627 active = *before_words++;
628 before += 1;
629 continue;
630 }
631 else // clear active
632 {
633 active &= ~(0x80000000 >> idx);
634 before_count -= 1;
635 }
636
637 // otherwise, force this elem with dependent
638 skc_grid_force(before+idx);
639
640 // no more bits?
641 if (before_count == 0)
642 break;
643 }
644 }
645 }
646
647 //
648 // Notify grids dependent on this grid that this grid is complete
649 //
650
651 void
skc_grid_complete(skc_grid_t const grid)652 skc_grid_complete(skc_grid_t const grid)
653 {
654 // debug: grid was executing
655 assert(grid->state == SKC_GRID_STATE_EXECUTING);
656
657 // move grid to completion and dispose after notifying dependents
658 grid->state = SKC_GRID_STATE_COMPLETE;
659
660 skc_uint after_count = grid->after.count;
661
662 if (after_count > 0)
663 {
664 // find set bits
665 struct skc_grid * after = grid->deps->grids;
666 skc_uint * after_words = grid->after.words;
667 skc_uint active = *after_words++;
668
669 while (true)
670 {
671 // find first active
672 skc_uint const idx = SKC_LZCNT_32(active);
673
674 // no bits set so inspect next word
675 if (idx == 32)
676 {
677 active = *after_words++;
678 after += 32;
679 continue;
680 }
681 else // clear active
682 {
683 active &= ~(0x80000000 >> idx);
684 after_count -= 1;
685 }
686
687 // otherwise, clear this dependency
688 skc_grid_clear_dependency(after+idx,grid->id);
689
690 // no more bits?
691 if (after_count == 0)
692 break;
693 }
694 }
695
696 // dispose of resources
697 skc_grid_call(grid,&grid->dispose);
698
699 // we don't need to hang on to this grid id any longer
700 skc_grid_detach(grid);
701 }
702
703 ///////////////////////////////////////////////////////////////////////////
704 //
705 // ALTERNATIVE IMPLEMENTATION WOULD SUPPORT A VARIABLE NUMBER OF
706 // ACTIVE GRIDS PER STAGE PRESUMABLY RESULTING IN SLIGHTLY LESS MEMORY
707 // USAGE.
708 //
709 // THE #1 OBJECTIVE OF THE GRID IMPLEMENTATION IS TO ENSURE THAT THE
710 // "HAPPENS-BEFORE" DECLARATION REMAINS ***FAST*** SINCE IT WILL BE
711 // CALLED FREQUENTLY. THUS THE |GRIDS|^2 BIT ARRAY...
712 //
713 // WE DON'T NEED THIS RIGHT NOW...
714 //
715
716 #if 0
717
718 //
719 // For now, make them all the same size
720 //
721
722 #define SKC_GRID_STAGE_WORDS_PATH_BUILDER SKC_GRID_MASK_WORDS
723 #define SKC_GRID_STAGE_WORDS_RASTER_BUILDER SKC_GRID_MASK_WORDS
724 #define SKC_GRID_STAGE_WORDS_COMPOSITION SKC_GRID_MASK_WORDS
725 #define SKC_GRID_STAGE_WORDS_STYLING SKC_GRID_MASK_WORDS
726 #define SKC_GRID_STAGE_WORDS_SURFACE_COMPOSITION SKC_GRID_MASK_WORDS
727 #define SKC_GRID_STAGE_WORDS_SURFACE_STYLING SKC_GRID_MASK_WORDS
728
729 //
730 //
731 //
732
733 typedef enum skc_grid_stage_type {
734
735 SKC_GRID_STAGE_TYPE_PATH_BUILDER,
736 SKC_GRID_STAGE_TYPE_RASTER_BUILDER,
737 SKC_GRID_STAGE_TYPE_COMPOSITION,
738 SKC_GRID_STAGE_TYPE_STYLING,
739 SKC_GRID_STAGE_TYPE_SURFACE_COMPOSITION,
740 SKC_GRID_STAGE_TYPE_SURFACE_STYLING,
741
742 SKC_GRID_STAGE_TYPE_COUNT
743
744 } skc_grid_stage_type;
745
746 //
747 //
748 //
749
750 union skc_grid_id
751 {
752 skc_grid_id_t u32;
753
754 struct {
755 skc_ushort index;
756 skc_ushort stage;
757 };
758 }
759
760 SKC_STATIC_ASSERT(sizeof(union skc_grid_id) == sizeof(skc_uint));
761
762 //
763 //
764 //
765
766 #endif
767
768 //
769 //
770 //
771