Skip to content

Commit e6d5b0e

Browse files
committed
Update scripts and loader template to allow object handles in structs.
Also document limitations of this implementation in CONTRIB.rst
1 parent 5d5c810 commit e6d5b0e

File tree

3 files changed

+219
-3
lines changed

3 files changed

+219
-3
lines changed

scripts/core/CONTRIB.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,26 @@ arguments:
205205
* Pointer to handle arguments, such as out parameters, are prefixed with
206206
``ph`` i.e. ``phQueue``.
207207

208+
Limitations
209+
-----------
210+
211+
There are some limitations on the patterns our spec generator can handle. These
212+
limitations are due to convenience of implementation rather than design: if
213+
they are preventing you from implementing a feature please open an issue and we
214+
will be happy to try and accommodate your use case. Otherwise beware of the
215+
following:
216+
217+
* A function parameter which is a struct type that has any of the following
218+
members in its type definition must not have the ``[range]`` tag:
219+
220+
* An object handle with the ``[range]`` tag
221+
222+
* A struct type with the ``[range]`` tag that has an object handle member
223+
224+
* A struct member which is a pointer to a struct type must not have the
225+
``[optional]`` tag if that struct (or any of its members, recursively) has
226+
an object handle member in its definition.
227+
208228
Forks and Pull Requests
209229
=======================
210230

scripts/templates/helper.py

Lines changed: 119 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,15 +231,15 @@ def is_array(cls, name):
231231
return True if re.match(cls.RE_ARRAY, name) else False
232232
except:
233233
return False
234-
234+
235235
@classmethod
236236
def get_array_length(cls, name):
237237
if not cls.is_array(name):
238238
raise Exception("Cannot find array length of non-array type.")
239239

240240
match = re.match(cls.RE_ARRAY, name)
241241
return match.groups()[1]
242-
242+
243243
@classmethod
244244
def get_array_element_type(cls, name):
245245
if not cls.is_array(name):
@@ -427,7 +427,7 @@ def is_bounds(cls, item):
427427
return True if re.match(cls.RE_BOUNDS, item['desc']) else False
428428
except:
429429
return False
430-
430+
431431
@classmethod
432432
def tagged_member(cls, item):
433433
try:
@@ -1266,6 +1266,122 @@ def get_loader_prologue(namespace, tags, obj, meta):
12661266

12671267
return prologue
12681268

1269+
1270+
"""
1271+
Private:
1272+
Takes a list of struct members and recursively searches for class handles.
1273+
Returns a list of class handles with access chains to reach them (e.g.
1274+
"struct_a->struct_b.handle"). Also handles ranges of class handles and
1275+
ranges of structs with class handle members, although the latter only works
1276+
to one level of recursion i.e. a range of structs with a range of structs
1277+
with a handle member will not work.
1278+
"""
1279+
def get_struct_handle_members(namespace,
1280+
tags,
1281+
meta,
1282+
members,
1283+
parent='',
1284+
is_struct_range=False):
1285+
handle_members = []
1286+
for m in members:
1287+
if type_traits.is_class_handle(m['type'], meta):
1288+
m_tname = _remove_const_ptr(subt(namespace, tags, m['type']))
1289+
m_objname = re.sub(r"(\w+)_handle_t", r"\1_object_t", m_tname)
1290+
# We can deal with a range of handles, but not if it's in a range of structs
1291+
if param_traits.is_range(m) and not is_struct_range:
1292+
handle_members.append({
1293+
'parent': parent,
1294+
'name': m['name'],
1295+
'obj_name': m_objname,
1296+
'type': m_tname,
1297+
'range_start': param_traits.range_start(m),
1298+
'range_end': param_traits.range_end(m)
1299+
})
1300+
else:
1301+
handle_members.append({
1302+
'parent': parent,
1303+
'name': m['name'],
1304+
'obj_name': m_objname,
1305+
'optional': param_traits.is_optional(m)
1306+
})
1307+
elif type_traits.is_struct(m['type'], meta):
1308+
member_struct_type = _remove_const_ptr(m['type'])
1309+
member_struct_members = meta['struct'][member_struct_type][
1310+
'members']
1311+
if param_traits.is_range(m):
1312+
# If we've hit a range of structs we need to start a new recursion looking
1313+
# for handle members. We do not support range within range, so skip that
1314+
if is_struct_range:
1315+
continue
1316+
range_handle_members = get_struct_handle_members(
1317+
namespace, tags, meta, member_struct_members, '', True)
1318+
if range_handle_members:
1319+
handle_members.append({
1320+
'parent': parent,
1321+
'name': m['name'],
1322+
'type': subt(namespace, tags, member_struct_type),
1323+
'range_start': param_traits.range_start(m),
1324+
'range_end': param_traits.range_end(m),
1325+
'handle_members': range_handle_members
1326+
})
1327+
else:
1328+
# If it's just a struct we can keep recursing in search of handles
1329+
m_is_pointer = type_traits.is_pointer(m['type'])
1330+
new_parent_deref = '->' if m_is_pointer else '.'
1331+
new_parent = m['name'] + new_parent_deref
1332+
handle_members += get_struct_handle_members(
1333+
namespace, tags, meta, member_struct_members, new_parent,
1334+
is_struct_range)
1335+
1336+
return handle_members
1337+
1338+
1339+
"""
1340+
Public:
1341+
Strips a string of all dereferences.
1342+
1343+
This is useful in layer templates for creating unique variable names. For
1344+
instance if we need to copy pMyStruct->member.hObject out of a function
1345+
parameter into a local variable we use this to get the unique (or at least
1346+
distinct from any other parameter we might copy) variable name
1347+
pMyStructmemberhObject.
1348+
"""
1349+
def strip_deref(string_to_strip):
1350+
string_to_strip = string_to_strip.replace('.', '')
1351+
return string_to_strip.replace('->', '')
1352+
1353+
1354+
"""
1355+
Public:
1356+
Takes a function object and recurses through its struct parameters to return
1357+
a list of structs that have handle object members the loader will need to
1358+
convert.
1359+
"""
1360+
def get_object_handle_structs_to_convert(namespace, tags, obj, meta):
1361+
structs = []
1362+
params = _filter_param_list(obj['params'], ["[in]"])
1363+
1364+
for item in params:
1365+
if type_traits.is_struct(item['type'], meta):
1366+
members = meta['struct'][_remove_const_ptr(
1367+
item['type'])]['members']
1368+
handle_members = get_struct_handle_members(namespace, tags, meta,
1369+
members)
1370+
if handle_members:
1371+
name = subt(namespace, tags, item['name'])
1372+
tname = _remove_const_ptr(subt(namespace, tags, item['type']))
1373+
struct = {
1374+
'name': name,
1375+
'type': tname,
1376+
'optional': param_traits.is_optional(item),
1377+
'members': handle_members
1378+
}
1379+
1380+
structs.append(struct)
1381+
1382+
return structs
1383+
1384+
12691385
"""
12701386
Public:
12711387
returns an enum object with the given name

scripts/templates/ldrddi.cpp.mako

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,86 @@ namespace ur_loader
175175
size_t sizeret = 0;
176176
if (pPropSizeRet == NULL)
177177
pPropSizeRet = &sizeret;
178+
179+
## Here we deal with handles buried inside struct type parameters. First
180+
## we create a local copy of the struct, then we convert all the handles
181+
## in that local copy and set the parameter to point to it before forwarding
182+
## it to the final API call.
183+
<% handle_structs = th.get_object_handle_structs_to_convert(n, tags, obj, meta) %>
184+
%if handle_structs:
185+
// Deal with any struct parameters that have handle members we need to convert.
186+
%for struct in handle_structs:
187+
%if struct['optional']:
188+
${struct['type']} ${struct['name']}Local = {};
189+
if(${struct['name']})
190+
${struct['name']}Local = *${struct['name']};
191+
%else:
192+
auto ${struct['name']}Local = *${struct['name']};
193+
%endif
194+
%endfor
195+
196+
%for struct in handle_structs:
197+
%for member in struct['members']:
198+
## If this member has a handle_members field that means it's a range of
199+
## structs which each contain a handle to convert.
200+
%if 'handle_members' in member:
201+
## we use the parent info stripped of derefs for a unique variable name
202+
<%
203+
parent_no_deref = th.strip_deref(member['parent'])
204+
range_vector_name = struct['name'] + parent_no_deref + member['name']
205+
## we need to check if range bounds are literals or variables: variables
206+
## need the full reference chain prepended to them
207+
range_start = member['range_start']
208+
if not re.match(r"[0-9]+$", range_start):
209+
range_start = struct['name'] + "->" + member['parent'] + range_start
210+
range_end = member['range_end']
211+
if not re.match(r"[0-9]+$", range_end):
212+
range_end = struct['name'] + "->" + member['parent'] + range_end %>
213+
std::vector<${member['type']}> ${range_vector_name};
214+
for(uint32_t i = ${range_start}; i < ${range_end}; i++) {
215+
${member['type']} NewRangeStruct = ${struct['name']}Local.${member['parent']}${member['name']}[i];
216+
%for handle_member in member['handle_members']:
217+
%if handle_member['optional']:
218+
if(NewRangeStruct.${handle_member['parent']}${handle_member['name']})
219+
%endif
220+
NewRangeStruct.${handle_member['parent']}${handle_member['name']} =
221+
reinterpret_cast<${handle_member['obj_name']}*>(
222+
NewRangeStruct.${handle_member['parent']}${handle_member['name']})
223+
->handle;
224+
%endfor
225+
226+
${range_vector_name}.push_back(NewRangeStruct);
227+
}
228+
${struct['name']}Local.${member['parent']}${member['name']} = ${range_vector_name}.data();
229+
## If the member has range_start then its a range of handles
230+
%elif 'range_start' in member:
231+
## we use the parent info stripped of derefs for a unique variable name
232+
<%
233+
parent_no_deref = th.strip_deref(member['parent'])
234+
range_vector_name = struct['name'] + parent_no_deref + member['name'] %>
235+
std::vector<${member['type']}> ${range_vector_name};
236+
for(uint32_t i = 0;i < ${struct['name']}->${member['parent']}${member['range_end']};i++) {
237+
${range_vector_name}.push_back(reinterpret_cast<${member['obj_name']}*>(${struct['name']}->${member['parent']}${member['name']}[i])->handle);
238+
}
239+
${struct['name']}Local.${member['parent']}${member['name']} = ${range_vector_name}.data();
240+
%else:
241+
%if member['optional']:
242+
if(${struct['name']}Local.${member['parent']}${member['name']})
243+
%endif
244+
${struct['name']}Local.${member['parent']}${member['name']} =
245+
reinterpret_cast<${member['obj_name']}*>(
246+
${struct['name']}Local.${member['parent']}${member['name']})->handle;
247+
%endif
248+
%endfor
249+
%endfor
250+
251+
// Now that we've converted all the members update the param pointers
252+
%for struct in handle_structs:
253+
%if struct['optional']:
254+
if(${struct['name']})
255+
%endif
256+
${struct['name']} = &${struct['name']}Local;
257+
%endfor
178258
%endif
179259
180260
// forward to device-platform

0 commit comments

Comments
 (0)