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 "macros.h"
16 #include "handle.h"
17 #include "extent_cl_12.h"
18 #include "device_cl_12.h"
19 
20 //
21 // FIXME -- THIS DOCUMENTATION IS STALE NOW THAT A REFERENCE COUNT REP
22 // IS A {HOST:DEVICE} PAIR.
23 //
24 // Host-side handle pool
25 //
26 // The bulk size of the three extents is currently 6 bytes of overhead
27 // per number of host handles.  The number of host handles is usually
28 // less than the number of blocks in the pool.  Note that the maximum
29 // number of blocks is 2^27.
30 //
31 // A practical instantiation might provide a combined 2^20 path and
32 // raster host handles. This would occupy 6 MB of host RAM for the
33 // 32-bit handle, 8-bit reference count and 8-bit handle-to-grid map.
34 //
35 // Also note that we could use isolated/separate path and raster block
36 // pools. Worst case, this would double the memory footprint of SKC.
37 //
38 // Host-side handle reference count
39 //
40 //   [0      ] : release
41 //   [1..UMAX] : retain
42 //
43 // In a garbage-collected environment we might want to rely on an
44 // existing mechanism for determing whether a handle is live.
45 //
46 // Otherwise, we probably want to have a 16 or 32-bit ref count.
47 //
48 // The handle reference count is defensive and will not allow the host
49 // to underflow a handle that's still retained by the pipeline.
50 //
51 // The single reference counter is split into host and device counts.
52 //
53 
54 union skc_handle_refcnt
55 {
56   skc_ushort  hd; // host and device
57 
58   struct {
59     skc_uchar h;  // host
60     skc_uchar d;  // device
61   };
62 };
63 
64 SKC_STATIC_ASSERT(SKC_MEMBER_SIZE(union skc_handle_refcnt,hd) ==
65                   SKC_MEMBER_SIZE(union skc_handle_refcnt,h) +
66                   SKC_MEMBER_SIZE(union skc_handle_refcnt,d));
67 
68 //
69 //
70 //
71 
72 struct skc_handle_bih
73 {
74   skc_uint       block;
75   skc_uint       rem;
76   skc_handle_t * handles;
77 };
78 
79 struct skc_handle_reclaim
80 {
81   struct skc_handle_bih bih;
82 
83   cl_kernel             kernel;
84   skc_device_kernel_id  kernel_id;
85 };
86 
87 union skc_handle_reclaim_rec
88 {
89   // ELEMENT  0
90   struct skc_runtime * runtime;
91 
92   // ELEMENT  1
93   struct {
94     skc_uint           rem;   // # of available records
95     skc_uint           head;  // index of first record
96   };
97 
98   // ELEMENTS 2+
99   struct {
100     skc_uint           index; // index of this record -- never modified
101     union {
102       skc_uint         next;  // index of next record
103       skc_uint         block; // block index of reclaimed handles
104     };
105   };
106 };
107 
108 SKC_STATIC_ASSERT(sizeof(union skc_handle_reclaim_rec) == sizeof(skc_uint2));
109 
110 //
111 //
112 //
113 
114 typedef enum skc_handle_reclaim_type_e {
115 
116   SKC_HANDLE_RECLAIM_TYPE_PATH,
117   SKC_HANDLE_RECLAIM_TYPE_RASTER,
118 
119   SKC_HANDLE_RECLAIM_TYPE_COUNT
120 
121 } skc_handle_reclaim_type_e;
122 
123 struct skc_handle_pool
124 {
125   //
126   // FIXME -- should we be pedantic and make these always-host-side
127   // allocations "extents" as well?  I think it's OK not being an
128   // extent structure for now and is mostly consistent with the rest
129   // of the code.
130   //
131   // FIXME -- the cbs[] array is a little idiosyncratic but the intent
132   // is to avoid storing the 64-bit backpointer inside of every single
133   // record.  This can be harmonized later.  Note that only a few
134   // hundred outstanding callbacks would represent many many subgroups
135   // of work and would fully occupy the GPU (if we allow it).
136   //
137   //
138   struct skc_extent_pdrw         map;     // device-managed extent mapping a host handle to device block id
139 
140   struct {
141     skc_handle_t               * indices; // array of individual host handles -- fragmented into blocks
142     union skc_handle_refcnt    * refcnts; // array of reference counts indexed by an individual handle
143     skc_uint                     count;
144   } handle;
145 
146   struct {
147     skc_uint                   * indices; // stack of indices to fixed-size blocks of host handles
148     skc_uint                     count;   // number of handles -- valid from [0,size)
149     skc_uint                     width;   // width of a fixed-size block of handles
150     skc_uint                     tos;     // grows upward   / push++ / --pop / # fixed-size blocks for reading
151     skc_uint                     bos;     // grows downward / --push / pop++ / # fixed-size blocks for writing
152   } block;
153 
154   union skc_handle_reclaim_rec * recs;    // array of reclaim records
155 
156   struct skc_handle_bih          acquire;
157   struct skc_handle_reclaim      reclaim[SKC_HANDLE_RECLAIM_TYPE_COUNT];
158 };
159 
160 //
161 //
162 //
163 
164 void
165 skc_handle_pool_create(struct skc_runtime     * const runtime,
166                        struct skc_handle_pool * const handle_pool,
167                        skc_uint                 const size,
168                        skc_uint                 const width,
169                        skc_uint                 const recs);
170 
171 void
172 skc_handle_pool_dispose(struct skc_runtime     * const runtime,
173                         struct skc_handle_pool * const handle_pool);
174 
175 //
176 //
177 //
178