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