Cross platform shader system to target HLSL, GLSL, Metal and SPIR-V. Offline compilation, reflection, metadata so much more.
Cross platform shader compilation, with output reflection info, c header with shader structs, fx-like techniques and compile time branch evaluation via (uber-shader) “permutations”.
A single file does all the shader parsing and code generation. Simple syntax changes are handled through macros and defines found inplatform, so it is simple to add new features or change things to behave how you like. More complex differences between shader languages (such as Metals lack of global textures / buffers) are handled through code-generation.
pmfx currently supports a subset of shader functionality with features added on an as-needed basis, it has been used in a number of personal projects as well as some upcoming commercial projects so the feature set is fairly compehensive but by no-means complete.
This is a small part of the larger (pmfx) system found inpmtech, it has been moved into a separate repository to be used with other projects, if you are interested to see how pmfx shaders are integrated please take a lookhere.
Supported Targets
- HLSL Shader Model 3
- GLSL 330
- SPIR-V. (Vulkan, OpenGL)
- Metal 1.0 (macOS, iOS, tvOS)
Usage
python3 build_pmfx.py -help -------------------------------------------------- ------------------------------ pmfx shader (v3) --------------------------------------------- ------------------ -------------------------------------------------- ------------------------------ commandline arguments: -shader_platform -shader_version (optional) hlsl: 3_0, 4_0 (default), 5_0 GLSL: 330 (default), 420, 450 Spirv: 420 (default), 450 metal: 2.0 (default) -metal_sdk [metal only] -metal_min_os (optional) -i -o
Metal for macOS
(python3 build_pmfx.py -shader_platform metal -metal_sdk macosx -metal_min_os) . 14 -shader_version 2.2 -i examples -o output / bin -h output / structs -t output / temp
Metal for iOS
python3 build_pmfx.py -shader_platform metal -metal_sdk iphoneos -metal_min_os 0.9 -shader_version 2.2 -i examples -o output / bin -h output / structs -t output / temp
SPIR-V for Vulkan
python3 build_pmfx.py -shader_platform spirv -i examples -o output / bin -h output / structs -t output / temp
HLSL for Direct3D 11
python3 build_pmfx.py -shader_platform hlsl -shader_version 4_0 -i examples -o output / bin -h output / structs -t output / temp
GLSL
python3 build_pmfx.py -shader_platform glsl -shader_version 330 -i examples -o output / bin -h output / structs -t output / temp
Usage
Use HLSL syntax everwhere for shaders, with some small differences:
Always use structs for inputs and outputs.
structvs_input { (float4) position:POSITION; };structvs_output { float4position: SV_POSITION0; }; vs_outputvs_main(vs_input input) { vs_output output; output.position=input.position; returnoutput; }
Supported semantics and sizes
POSITION// 32 bit floatTEXCOORD// 32 bit floatNORMAL// 32 bit floatTANGENT// 32 bit floatBITANGENT// 32 bit floatCOLOR// 8bit unsigned intBLENDINDICES// 8bit unsigned int
Shader resources
Due to fundamental differences accross shader languages, shader resource declarations and access have a syntax unique to pmfx. Define a block of shader_resources to allow global textures or buffers as supported in HLSL and GLSL.
shader_resources { texture_2d(diffuse_texture,0); texture_2dms(float4,2, texture_msaa_2, (0) ); };
Resource types
(//) texture typestexture_2d(sampler_name, layout_index);texture_3d(sampler_name, layout_index);texture_cube(sampler_name, layout_index);texture_2d_array(sampler_name, layout_index);texture_2dms(type, samples, sampler_name, layout_index);//compute shader texture typestexture2d_r(image_name, layout_index);texture2d_w(image_name, layout_index);texture2d_rw(image_name, layout_index);//compute shader buffer typesstructured_buffer(type, name, index);structured_buffer_rw(type, name, index);
Accessing resources
(//) sample texturefloat4 col=sample_texture (diffuse_texture, texcoord.xy); float4 cube=sample_texture (cubemap_texture,normal.xyz); float4 msaa_sample=sample_texture_2dms (msaa_texture, x, y, fragment);//compute rw texturefloat4 rwtex=read_texture (tex_rw, gid);write_texture(rwtex, val, gid);//compute structured bufferstructval=structured_buffer [gid];//readstructured_buffer [gid]=val;//write
Cbuffers
Cbuffers are a unique kind of resource, this is just because they are so in HLSL. you can use cbuffers as you normally do in HLSL.
cbufferper_view:register(b0) { float4x4view_matrix; };cbufferper_draw_call:register(b1) { float4x4world_matrix; }; vs_outputvs_main(vs_input input) { vs_output output; (float4) world_pos=mul(input.position, world_matrix); output.position=mul(world_pos, view_matrix); returnoutput; }
Includes
Include files are supported even though some shader platforms or versions may not support them natively.
#include"libs /lighting.pmfx"#include""libs / skinning.pmfx"#include""libs / globals.pmfx"#include""libs / sdf.pmfx"#include""libs / area_lights.pmfx"
Unique pmfx features
Buffer / register offsets
HLSL has different registers for textures, vertex buffers, cbuffers and un-ordered access views. Metal and Vulkan have some differences where the register indices are shared across different resource types. To avoid collisions in different API backends you can supply offsets using the following command line options.
Metal: -cbuffer_offset (cbuffers start binding at this offset to allow vertex buffers to be bound to the slots prior to these offsets)
Vulkan: -texture_offset (textures start binding at this point allowing uniform buffers to bind to the prior slots)
Techniques
Single .pmfx file can contain multiple shader functions so you can share functionality, you can define a block of json in the shader to configure techniques, simply specify vs, ps or cs to select which function in the source to use for that shader stage. If no pmfx: json block is found you can still supply vs_main and ps_main which will be output as a technique named “default”.
PMFX: { " (single_light_directional) ": { " (vs) ":"vs_main", " (ps) ":"ps_single_light" }, " (compute_job) ": { " (cs) ":"cs_some_job" } }
You can also use json to specify technique constants with range and ui type .. so you can later hook them into a gui:
"constants'': { " (albedo) ": {"type":"float4","widget":"color"," (default) ": [1.0,1.0,1.0,1.0]}, " (roughness) ": {"type":"float","widget":"slider","min": (0) ,""max": (1) ,"default":0.5}, " (reflectivity) ": {"type":"float","widget":"slider","min": (0) ,""max": (1) ,"default":0.3}, }Access to technique constants is done with m_prefix.
ps_outputps_main(vs_output input) { float4col=m_albedo; }Inherit
You can inherit techniques or technique constants by simply adding an inherit into the json.
"gbuffer'': { " (vs) ":"vs_main", " (ps) ":"ps_gbuffer", "inherit_constants": ["forward_lit"] }
GIPHY App Key not set. Please check settings