Skip to content

Commit 51641a0

Browse files
committed
- improve handling of recursive / nested struct includes, adds ability to use structured push_constants and still calculate the correct number of 32bit consts, fix an issue where resource: was not correctly stripping the colon from the name, and therfore not including the resource in the generated source
1 parent 32bee31 commit 51641a0

File tree

4 files changed

+96
-49
lines changed

4 files changed

+96
-49
lines changed

cgu.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,11 @@ def type_name(type_declaration):
161161
# skip past templates (allow multiple template parameters)
162162
if type_declaration.find("<") != -1:
163163
template_end = enclose("<", ">", type_declaration, 0)
164-
return type_declaration[template_end:].strip().split()[0]
164+
return type_declaration[template_end:].strip().split()[0].strip(":")
165165
else:
166166
# assume the type name is the second token
167167
pos = type_declaration.find("{")
168-
name = type_declaration[:pos].strip().split()[1]
168+
name = type_declaration[:pos].strip().split()[1].strip(":")
169169
return name
170170

171171

docs/v2.pmfx_doc

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,17 @@ pipeline: {
7373
}
7474
}
7575
/// auto-generated (based on shader usage and register bindings)
76-
desciptor_layout: {
77-
// ..
76+
pipeline_layout: {
77+
bindings: {
78+
// .. autogenerated
79+
}
80+
// add push constants in the pipeline to make buffer usages be visible as push constants instead of descriptors
81+
push_constants: {
82+
// .. autogenerated
83+
}
84+
static_samplers: {
85+
// .. autogenerated
86+
}
7887
}
7988
/// specify the name of a shader entry point inside .hlsl files to use as a vertex shader
8089
/// only makes sense on it's own in combination with ps. cs must be null

pmfx_pipeline.py

Lines changed: 79 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def get_type_size_info(type):
166166
return lookup[type]
167167

168168

169-
# returnnumber of 32 bit values for a member of a push constants cbuffer
169+
# return number of 32 bit values for a member of a push constants cbuffer
170170
def get_num_32bit_values(type):
171171
lookup = {
172172
"float": 1,
@@ -186,7 +186,9 @@ def get_num_32bit_values(type):
186186
"uint3": 3,
187187
"uint4": 4,
188188
}
189-
return lookup[type]
189+
if type in lookup:
190+
return lookup[type]
191+
return 0
190192

191193

192194
# returns a vertex format type from the data type
@@ -522,10 +524,27 @@ def get_vertex_elements(pmfx, entry_point):
522524
return elements
523525

524526

527+
# recurses through nested structure to reach and sum the number of 32bit values from primitive types
528+
def structure_get_num_32_bit_values(typename, structs):
529+
if typename in structs:
530+
total = 0
531+
for member in structs[typename]["members"]:
532+
533+
total += structure_get_num_32_bit_values(member["data_type"], structs)
534+
return total
535+
else:
536+
value = get_num_32bit_values(typename)
537+
if value == 0:
538+
print(json.dumps(structs, indent=4))
539+
print("mssing member {}".format(typename))
540+
assert(0)
541+
return value
542+
543+
525544
# builds a descriptor set from resources used in the pipeline
526-
def generate_descriptor_layout(pmfx, pmfx_pipeline, resources):
545+
def generate_pipeline_layout(pmfx, pmfx_pipeline, resources):
527546
bindable_resources = get_bindable_resource_keys()
528-
descriptor_layout = {
547+
pipeline_layout = {
529548
"bindings": [],
530549
"push_constants": [],
531550
"static_samplers": []
@@ -544,10 +563,7 @@ def generate_descriptor_layout(pmfx, pmfx_pipeline, resources):
544563
if num_values == 0:
545564
if "template_type" in resource:
546565
t = resource["template_type"]
547-
if t in resources:
548-
tres = resources[t]
549-
for member in tres["members"]:
550-
num_values += get_num_32bit_values(member["data_type"])
566+
num_values = structure_get_num_32_bit_values(t, resources)
551567
push_constants = {
552568
"shader_register": resource["shader_register"],
553569
"register_space": resource["register_space"],
@@ -556,7 +572,7 @@ def generate_descriptor_layout(pmfx, pmfx_pipeline, resources):
556572
"num_values": num_values,
557573
"name": resource["name"]
558574
}
559-
descriptor_layout["push_constants"].append(push_constants)
575+
pipeline_layout["push_constants"].append(push_constants)
560576
continue
561577
if "static_samplers" in pmfx_pipeline:
562578
if r in pmfx_pipeline["static_samplers"]:
@@ -568,7 +584,7 @@ def generate_descriptor_layout(pmfx, pmfx_pipeline, resources):
568584
"sampler_info": pmfx["sampler_states"][lookup],
569585
"name": resource["name"]
570586
}
571-
descriptor_layout["static_samplers"].append(static_sampler)
587+
pipeline_layout["static_samplers"].append(static_sampler)
572588
continue
573589
# fall trhough and add as a bindable resource
574590
if resource_type in bindable_resources:
@@ -580,15 +596,14 @@ def generate_descriptor_layout(pmfx, pmfx_pipeline, resources):
580596
"num_descriptors": get_descriptor_array_size(resource),
581597
"name": resource["name"]
582598
}
583-
descriptor_layout["bindings"].append(binding)
599+
pipeline_layout["bindings"].append(binding)
584600

585601
# combine bindings on the same slot (allow resource type aliasing)
586602
combined_bindings = dict()
587-
for binding in descriptor_layout["bindings"]:
603+
for binding in pipeline_layout["bindings"]:
588604
bh = pmfx_hash({
589605
"shader_register": binding["shader_register"],
590606
"register_space": binding["register_space"],
591-
"register_space": binding["register_space"],
592607
"binding_type": binding["binding_type"]
593608
})
594609
if bh not in combined_bindings:
@@ -616,9 +631,9 @@ def generate_descriptor_layout(pmfx, pmfx_pipeline, resources):
616631
sorted_bindings[i] = dict(temp)
617632
sorted = False
618633

619-
descriptor_layout["bindings"] = list(sorted_bindings)
634+
pipeline_layout["bindings"] = list(sorted_bindings)
620635

621-
return descriptor_layout
636+
return pipeline_layout
622637

623638

624639
# wrtie out c++ header from the json info
@@ -726,6 +741,15 @@ def add_built_in_defines(src):
726741
return src
727742

728743

744+
# adds structs to respurces recursively
745+
def add_struct_members_recursive(typename, stage, structs, resources, depth):
746+
if typename in structs and typename not in resources:
747+
resources[typename] = add_used_shader_resource(structs[typename], stage)
748+
resources[typename]["depth"] = depth
749+
for member in structs[typename]["members"]:
750+
add_struct_members_recursive(member["data_type"], stage, structs, resources, depth + 1)
751+
752+
729753
# given an entry point generate src code and resource meta data for the shader
730754
def generate_shader_info(pmfx, entry_point, stage, permute=None):
731755
# resource categories
@@ -737,7 +761,12 @@ def generate_shader_info(pmfx, entry_point, stage, permute=None):
737761

738762
# start globals then with entry point src code
739763
src = pmfx["functions"][entry_point]["source"]
764+
765+
# grab any attributes
766+
attribs = pmfx["functions"][entry_point]["attributes"]
767+
740768
resources = dict()
769+
recursive_resources = dict()
741770
vertex_elements = None
742771
# recursively insert used functions
743772
complete = False
@@ -775,24 +804,25 @@ def generate_shader_info(pmfx, entry_point, stage, permute=None):
775804
if template_typeame in pmfx["resources"]["structs"]:
776805
struct_resource = pmfx["resources"]["structs"][template_typeame]
777806
resources[template_typeame] = add_used_shader_resource(struct_resource, stage)
807+
add_struct_members_recursive(template_typeame, stage, pmfx["resources"]["structs"], recursive_resources, 0)
808+
778809
# add resource and append resource src code
779810
for token in tokens:
780811
p = cgu.find_token(token, src)
781812
if p != -1:
813+
# ignore struct member refernces with names which collide with cbuffer members
782814
if resource["type"] == "cbuffer":
783-
if cgu.find_prev_non_whitespace(src, p) != ".":
784-
resources[r] = add_used_shader_resource(resource, stage)
785-
break
786-
else:
787-
# add nested members
788-
if resource["type"] == "cbuffer" or resource["type"] == "struct":
789-
for member in resource["members"]:
790-
ty = member["data_type"]
791-
if ty in pmfx["resources"]["structs"] and ty not in resources:
792-
resources[ty] = add_used_shader_resource(pmfx["resources"]["structs"][ty], stage)
793-
# add the resource itself
794-
resources[r] = add_used_shader_resource(resource, stage)
795-
break
815+
if cgu.find_prev_non_whitespace(src, p) == ".":
816+
continue
817+
818+
# add nested members
819+
if resource["type"] == "cbuffer" or resource["type"] == "struct":
820+
for member in resource["members"]:
821+
add_struct_members_recursive(member["data_type"], stage, pmfx["resources"]["structs"], recursive_resources, 1)
822+
823+
# add the resource itself
824+
resources[r] = add_used_shader_resource(resource, stage)
825+
break
796826

797827
# add any globals..
798828
globals = ""
@@ -813,15 +843,11 @@ def generate_shader_info(pmfx, entry_point, stage, permute=None):
813843

814844
# resources input structs, textures, buffers etc
815845
if len(resources) > 0:
816-
for resource in resources:
817-
if resources[resource]["type"] == "struct":
818-
res += "// forward resource declarations\n"
819-
break
820-
for resource in resources:
821-
if resources[resource]["type"] == "struct":
822-
res += "{} {};\n".format(resources[resource]["type"], resources[resource]["name"])
823-
824846
res += "// resource declarations\n"
847+
for resource in recursive_resources:
848+
if recursive_resources[resource]["depth"] > 0:
849+
res += recursive_resources[resource]["declaration"] + ";\n"
850+
825851
for resource in resources:
826852
res += resources[resource]["declaration"] + ";\n"
827853

@@ -846,8 +872,9 @@ def generate_shader_info(pmfx, entry_point, stage, permute=None):
846872
return {
847873
"src": src,
848874
"src_hash": hashlib.md5(src.encode("utf-8")).hexdigest(),
849-
"resources": dict(resources),
850-
"vertex_elements": vertex_elements
875+
"resources": merge_dicts(dict(resources), dict(recursive_resources)),
876+
"vertex_elements": vertex_elements,
877+
"attributes": attribs
851878
}
852879

853880

@@ -926,17 +953,29 @@ def generate_pipeline_permutation(pipeline_name, pipeline, output_pmfx, shaders,
926953
output_pipeline["{}_hash:".format(stage)] = pmfx_hash(shader_info["src_hash"])
927954
shader = shader_info
928955
resources = merge_dicts(resources, dict(shader["resources"]), ["visibility"])
956+
# generate vertex layout
929957
if stage == "vs":
930958
pmfx_vertex_layout = dict()
931959
if "vertex_layout" in pipeline:
932960
pmfx_vertex_layout = pipeline["vertex_layout"]
933961
output_pipeline["vertex_layout"] = generate_vertex_layout(shader["vertex_elements"], pmfx_vertex_layout)
962+
# extract numthreads
963+
if stage == "cs":
964+
for attrib in shader["attributes"]:
965+
if attrib.find("numthreads") != -1:
966+
start, end = cgu.enclose_start_end("(", ")", attrib, 0)
967+
xyz = attrib[start:end].split(",")
968+
numthreads = []
969+
for a in xyz:
970+
numthreads.append(int(a.strip()))
971+
output_pipeline["numthreads"] = numthreads
972+
934973
# set non zero error codes to track failures
935974
if shader_info["error_code"] != 0:
936975
output_pipeline["error_code"] = shader_info["error_code"]
937976

938-
# build descriptor set
939-
output_pipeline["descriptor_layout"] = generate_descriptor_layout(output_pmfx, pipeline, resources)
977+
# build pipeline layout
978+
output_pipeline["pipeline_layout"] = generate_pipeline_layout(output_pmfx, pipeline, resources)
940979

941980
# fill in any useful defaults
942981
output_pipeline = get_pipeline_with_defaults(output_pipeline, pipeline)
@@ -995,7 +1034,6 @@ def generate_pipeline_permutation(pipeline_name, pipeline, output_pmfx, shaders,
9951034
output_pipeline["error_code"] = 219
9961035

9971036
output_pipeline["hash"] = pmfx_hash(expanded)
998-
9991037
# output_pipeline["resources"] = resources
10001038

10011039
# need to has the state objects

readme.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ commandline arguments:
8282
There are 2 code paths supported by pmfx, this is in an effort to keep up-to-date with modern graphics API's but also offer backward comptibility support to older graphics API's, mobile and web platforms.
8383

8484
- [Version 1](https://github.com/polymonster/pmfx-shader#version-1) - (bindful render model, techniques, macro based cross platform shaders)
85-
- [Version 2](https://github.com/polymonster/pmfx-shader#version-2) - (bindless render model, descriptor sets, pipelines, SPIR-V based cross-compilation)
85+
- [Version 2](https://github.com/polymonster/pmfx-shader#version-2) - (bindless render model, pipelines, descriptors, SPIR-V based cross-compilation)
8686

8787
## Version 1
8888

@@ -620,7 +620,7 @@ Take a look at the example [code](https://github.com/polymonster/pmfx-shader/tre
620620

621621
Compiled shaders and reflection information will be emitted to your chosen `-o` outout directory, Each `.pmfx` file will create a directory which it will compile shader binaries into. Shader compilation is minimised and reduced within single `.pmfx` files by sharing and re-using binaries which are identical across different shader permitations or stages.
622622

623-
Descriptor layout and Vertex layout can be automatically generated based on resource usage inside shaders, the whole pipeline is exported as `.json` along with the built shaders. Hashes for the various pieces of the render pipline states are stored so you can quickly check for pipelines that may need rebuilding as part of a hot reloading process.
623+
Pipeline layout, descriptor bindings and vertex layout can be automatically generated based on resource usage inside shaders, the whole pipeline is exported as `.json` along with the built shaders. Hashes for the various pieces of the render pipline states are stored so you can quickly check for pipelines that may need rebuilding as part of a hot reloading process.
624624

625625
```json
626626
"imdraw_2d": {
@@ -656,7 +656,7 @@ Descriptor layout and Vertex layout can be automatically generated based on reso
656656
],
657657
"error_code": 0,
658658
"ps_hash:": 2326464525,
659-
"descriptor_layout": {
659+
"pipeline_layout": {
660660
"bindings": [],
661661
"push_constants": [
662662
{
@@ -678,7 +678,7 @@ You can take a look an example output `json` reflection file included in this re
678678

679679
### Touch
680680

681-
When building descriptor or vertex layouts, `pmfx` will detect which resources you are using, when debugging you may comment out or remove references to used resources and this may cause knock on issues with the associated slots and layouts you expect in your graphics engine. You can use the `pmfx_touch` macro to ensure that a reosurce type is included in a descriptor or vertex layout to avoid this issue without throwing a warning.
681+
When building pipeline, descriptor or vertex layouts, `pmfx` will detect which resources you are using, when debugging you may comment out or remove references to used resources and this may cause knock on issues with the associated slots and layouts you expect in your graphics engine. You can use the `pmfx_touch` macro to ensure that a reosurce type is included in a particular layout to avoid this issue without throwing a warning.
682682

683683
```hlsl
684684
struct cbuffer_struct {

0 commit comments

Comments
 (0)