blob: 934fa9f8bfd094ec476e36790ab47e812fe9b0e9 [file] [log] [blame]
Allan MacKinnon4359d522018-06-19 13:57:04 -07001/*
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
66typedef skc_uchar skc_grid_id_t; // 256 values
67#define SKC_GRID_ID_INVALID SKC_UCHAR_MAX // 255
68
69#define SKC_GRID_SIZE_WORDS 8 // 256 bits
70#define SKC_GRID_SIZE_IDS ((32 * SKC_GRID_SIZE_WORDS) - 1) // 255 ids
71
72//
73//
74//
75
76typedef 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
93struct skc_grid_pfn_name
94{
95 skc_grid_pfn pfn;
96 char const * name;
97};
98
99//
100//
101//
102
103struct 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
132struct 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
149static
150void
151skc_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
158static
159void
160skc_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
171static
172void
173skc_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
185skc_grid_t
186skc_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
201void *
202skc_grid_get_data(skc_grid_t const grid)
203{
204 return grid->data;
205}
206
207void
208skc_grid_set_data(skc_grid_t const grid, void * const data)
209{
210 grid->data = data;
211}
212
213//
214//
215//
216
217skc_grid_deps_t
218skc_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
258void
259skc_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
274void
275skc_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
307skc_grid_t
308skc_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 // FIXME -- no more ids -- either fatal or flush & wait for grids to be released
319 assert(deps->count < SKC_GRID_SIZE_IDS);
320
321 // otherwise, an id exists so decrement count
322 deps->count += 1;
323
324 // find first set bit (1:inactive)
325 skc_uint * active = deps->active;
326 skc_uint first = 0;
327
328 while (1)
329 {
330 skc_uint const idx = SKC_LZCNT_32(*active);
331
332 first += idx;
333
334 if (idx < 32)
335 {
336 // make inactive bit active: 1 -> 0
337 *active &= ~(0x80000000 >> idx); // 0:active
338 break;
339 }
340
341 // otherwise, inspect next word for inactive bit
342 active += 1;
343 }
344
345 struct skc_grid * const grid = deps->grids + first;
346
347 // save grid pointer
348 if (addr != NULL)
349 *addr = grid;
350
351 // initialize elem
352 *grid = (struct skc_grid){
353 .state = SKC_GRID_STATE_READY,
354 .id = first,
355 .deps = deps,
356 .addr = addr,
357 .data = data,
358 .waiting = { .pfn = waiting_pfn, .name = waiting_name },
359 .execute = { .pfn = execute_pfn, .name = execute_name },
360 .dispose = { .pfn = dispose_pfn, .name = dispose_name },
361 .before = { { 0 }, 0 },
362 .after = { { 0 }, 0 }
363 };
364
365 return grid;
366}
367
368//
369//
370//
371
372static
373skc_bool
374skc_grid_words_set(skc_uint ids[SKC_GRID_SIZE_WORDS], skc_uint const id)
375{
376 skc_uint * const ptr = ids + (id/32);
377 skc_uint const pre = *ptr;
378 skc_uint const post = pre | (0x80000000 >> (id & 0x1F)); // set
379
380 *ptr = post;
381
382 return pre != post;
383}
384
385static
386skc_bool
387skc_grid_words_clear(skc_uint ids[SKC_GRID_SIZE_WORDS], skc_uint const id)
388{
389 skc_uint * const ptr = ids + (id/32);
390 skc_uint const pre = *ptr;
391 skc_uint const post = pre & ~(0x80000000 >> (id & 0x1F)); // clear
392
393 *ptr = post;
394
395 return pre != post;
396}
397
398//
399// we may want to allow the host to detach a grid
400//
401
402static
403void
404skc_grid_detach(skc_grid_t const grid)
405{
406 // for now make sure grid is complete
407 // assert(*grid->state >= SKC_GRID_STATE_COMPLETE);
408
409 // transition state
410 grid->state = SKC_GRID_STATE_DETACHED;
411
412 //
413 // FIXME -- save profiling info
414 //
415
416 if (skc_grid_words_set(grid->deps->active,grid->id)) // 1:inactive
417 grid->deps->count -= 1;
418}
419
420//
421//
422//
423
424void
425skc_grid_map(skc_grid_t const grid, skc_handle_t const handle)
426{
427 grid->deps->handle_map[handle] = grid->id;
428}
429
430//
431//
432//
433
434void
435skc_grid_deps_force(skc_grid_deps_t const deps,
436 skc_handle_t const * const handles,
437 skc_uint const count)
438{
439 //
440 // FIXME -- test to make sure handles aren't completely out of range integers
441 //
442 skc_grid_id_t * const handle_map = deps->handle_map;
443
444 for (skc_uint ii=0; ii<count; ii++)
445 {
446 skc_grid_id_t grid_id = handle_map[SKC_TYPED_HANDLE_TO_HANDLE(handles[ii])];
447
448 if (grid_id != SKC_GRID_ID_INVALID)
449 {
450 skc_grid_t const grid = deps->grids + grid_id;
451
452 skc_grid_force(grid);
453
454 while (grid->state >= SKC_GRID_STATE_COMPLETE)
455 skc_scheduler_wait_one(deps->scheduler);
456 }
457 }
458}
459
460void
461skc_grid_deps_unmap(skc_grid_deps_t const deps,
462 skc_handle_t const * const handles,
463 skc_uint const count)
464{
465 skc_grid_id_t * const handle_map = deps->handle_map;
466
467 for (skc_uint ii=0; ii<count; ii++)
468 handle_map[handles[ii]] = SKC_GRID_ID_INVALID;
469}
470
471//
472// NOTE: We want this routine to be very very fast. The array of bit
473// flags is probably as fast as we can go for a modest number of
474// grids.
475//
476// NOTE: The before grid should never be NULL. This means the grid's
477// lifecycle should match the lifetime of the object it represents.
478// This also means the grid "invalidation upon start" feature should
479// be well understood before using it to clear the skc_grid_t.
480//
481
482void
483skc_grid_happens_after_grid(skc_grid_t const after,
484 skc_grid_t const before)
485{
486 assert(after->state == SKC_GRID_STATE_READY);
487
488 if (before->state >= SKC_GRID_STATE_COMPLETE)
489 return;
490
491 if (skc_grid_words_set(after->before.words,before->id))
492 after->before.count += 1;
493
494 if (skc_grid_words_set(before->after.words,after->id))
495 before->after.count += 1;
496}
497
498void
499skc_grid_happens_after_handle(skc_grid_t const after, skc_handle_t const before)
500{
501 assert(after->state == SKC_GRID_STATE_READY);
502
503 skc_uint const id_before = after->deps->handle_map[before];
504
505 if (id_before == SKC_GRID_ID_INVALID)
506 return;
507
508 if (skc_grid_words_set(after->before.words,id_before))
509 after->before.count += 1;
510
511 skc_grid_t const grid_before = after->deps->grids + id_before;
512
513 if (skc_grid_words_set(grid_before->after.words,after->id))
514 grid_before->after.count += 1;
515}
516
517//
518// Remove dependency from grid
519//
520
521static
522void
523skc_grid_clear_dependency(skc_grid_t const after, skc_uint const before)
524{
525 skc_bool const is_change = skc_grid_words_clear(after->before.words,before);
526
527 assert(is_change); // for now let's make sure this is a rising edge
528
529 after->before.count -= 1;
530
531 if ((after->before.count == 0) && ((after->state == SKC_GRID_STATE_WAITING) ||
532 (after->state == SKC_GRID_STATE_FORCED)))
533 {
534 // schedule grid for execution
535 after->state = SKC_GRID_STATE_EXECUTING;
536 skc_grid_schedule(after,&after->execute);
537 }
538}
539
540//
541// Start the ready grid and wait for dependencies to complete
542//
543
544void
545skc_grid_start(skc_grid_t const grid)
546{
547 // nothing to do if this grid isn't in a ready state
548 if (grid->state != SKC_GRID_STATE_READY)
549 return;
550
551 // record transition through waiting state
552 grid->state = SKC_GRID_STATE_WAITING;
553
554 // the waiting pfn may be null -- e.g. the path builder
555 // skc_grid_schedule(grid,&grid->waiting);
556 skc_grid_call(grid,&grid->waiting);
557
558 // clear the reference
559 skc_grid_invalidate(grid);
560
561 // execute if there are no dependencies
562 if (grid->before.count == 0)
563 {
564 // tell grid it can execute
565 grid->state = SKC_GRID_STATE_EXECUTING;
566 skc_grid_schedule(grid,&grid->execute);
567 }
568}
569
570//
571// Start this grid and all its ready dependencies
572//
573
574void
575skc_grid_force(skc_grid_t const grid)
576{
577 // return if this grid was forced, executing or complete
578 if (grid->state >= SKC_GRID_STATE_FORCED)
579 return;
580
581 // if ready then move to waiting state
582 if (grid->state == SKC_GRID_STATE_READY)
583 {
584 // tell grid to wait for execution
585 grid->state = SKC_GRID_STATE_WAITING;
586
587 // the waiting pfn may be null -- e.g. the path builder
588 // skc_grid_schedule(grid,&grid->waiting);
589 skc_grid_call(grid,&grid->waiting);
590
591 // clear the reference
592 skc_grid_invalidate(grid);
593 }
594
595 skc_uint before_count = grid->before.count;
596
597 // if there are no grid dependencies then execute
598 if (before_count == 0)
599 {
600 // tell grid it can execute
601 grid->state = SKC_GRID_STATE_EXECUTING;
602 skc_grid_schedule(grid,&grid->execute);
603 }
604 else // otherwise, start or make waiting all dependencies
605 {
606 grid->state = SKC_GRID_STATE_FORCED;
607
608 struct skc_grid * before = grid->deps->grids;
609 skc_uint * before_words = grid->before.words;
610 skc_uint active = *before_words++;
611
612 while (true)
613 {
614 // find first active
615 skc_uint const idx = SKC_LZCNT_32(active);
616
617 // no bits set so inspect next word
618 if (idx == 32)
619 {
620 active = *before_words++;
621 before += 1;
622 continue;
623 }
624 else // clear active
625 {
626 active &= ~(0x80000000 >> idx);
627 before_count -= 1;
628 }
629
630 // otherwise, force this elem with dependent
631 skc_grid_force(before+idx);
632
633 // no more bits?
634 if (before_count == 0)
635 break;
636 }
637 }
638}
639
640//
641// Notify grids dependent on this grid that this grid is complete
642//
643
644void
645skc_grid_complete(skc_grid_t const grid)
646{
647 // debug: grid was executing
648 assert(grid->state == SKC_GRID_STATE_EXECUTING);
649
650 // move grid to completion and dispose after notifying dependents
651 grid->state = SKC_GRID_STATE_COMPLETE;
652
653 skc_uint after_count = grid->after.count;
654
655 if (after_count > 0)
656 {
657 // find set bits
658 struct skc_grid * after = grid->deps->grids;
659 skc_uint * after_words = grid->after.words;
660 skc_uint active = *after_words++;
661
662 while (true)
663 {
664 // find first active
665 skc_uint const idx = SKC_LZCNT_32(active);
666
667 // no bits set so inspect next word
668 if (idx == 32)
669 {
670 active = *after_words++;
671 after += 1;
672 continue;
673 }
674 else // clear active
675 {
676 active &= ~(0x80000000 >> idx);
677 after_count -= 1;
678 }
679
680 // otherwise, clear this dependency
681 skc_grid_clear_dependency(after+idx,grid->id);
682
683 // no more bits?
684 if (after_count == 0)
685 break;
686 }
687 }
688
689 // dispose of resources
690 skc_grid_call(grid,&grid->dispose);
691
692 // we don't need to hang on to this grid id any longer
693 skc_grid_detach(grid);
694}
695
696///////////////////////////////////////////////////////////////////////////
697//
698// ALTERNATIVE IMPLEMENTATION WOULD SUPPORT A VARIABLE NUMBER OF
699// ACTIVE GRIDS PER STAGE PRESUMABLY RESULTING IN SLIGHTLY LESS MEMORY
700// USAGE.
701//
702// THE #1 OBJECTIVE OF THE GRID IMPLEMENTATION IS TO ENSURE THAT THE
703// "HAPPENS-BEFORE" DECLARATION REMAINS ***FAST*** SINCE IT WILL BE
704// CALLED FREQUENTLY. THUS THE |GRIDS|^2 BIT ARRAY...
705//
706// WE DON'T NEED THIS RIGHT NOW...
707//
708
709#if 0
710
711//
712// For now, make them all the same size
713//
714
715#define SKC_GRID_STAGE_WORDS_PATH_BUILDER SKC_GRID_MASK_WORDS
716#define SKC_GRID_STAGE_WORDS_RASTER_BUILDER SKC_GRID_MASK_WORDS
717#define SKC_GRID_STAGE_WORDS_COMPOSITION SKC_GRID_MASK_WORDS
718#define SKC_GRID_STAGE_WORDS_STYLING SKC_GRID_MASK_WORDS
719#define SKC_GRID_STAGE_WORDS_SURFACE_COMPOSITION SKC_GRID_MASK_WORDS
720#define SKC_GRID_STAGE_WORDS_SURFACE_STYLING SKC_GRID_MASK_WORDS
721
722//
723//
724//
725
726typedef enum skc_grid_stage_type {
727
728 SKC_GRID_STAGE_TYPE_PATH_BUILDER,
729 SKC_GRID_STAGE_TYPE_RASTER_BUILDER,
730 SKC_GRID_STAGE_TYPE_COMPOSITION,
731 SKC_GRID_STAGE_TYPE_STYLING,
732 SKC_GRID_STAGE_TYPE_SURFACE_COMPOSITION,
733 SKC_GRID_STAGE_TYPE_SURFACE_STYLING,
734
735 SKC_GRID_STAGE_TYPE_COUNT
736
737} skc_grid_stage_type;
738
739//
740//
741//
742
743union skc_grid_id
744{
745 skc_grid_id_t u32;
746
747 struct {
748 skc_ushort index;
749 skc_ushort stage;
750 };
751}
752
753SKC_STATIC_ASSERT(sizeof(union skc_grid_id) == sizeof(skc_uint));
754
755//
756//
757//
758
759#endif
760
761//
762//
763//