Home | History | Annotate | Download | only in skc
      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
    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
    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
    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 *
    202 skc_grid_get_data(skc_grid_t const grid)
    203 {
    204   return grid->data;
    205 }
    206 
    207 void
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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