Skip to content
Alex Dixon edited this page Jul 9, 2021 · 19 revisions

Pmfx is a high level shader system and data driven renderer. It uses HLSL as a basis for the shader language and extends HLSL to add techniques, permutations and shader parameters which can be configured using json. Pmfx can be user to render ecs entities through scene view renderers and the rendering pipeline can also be configured using json config files. Pmfx is used to drive the lower level pen::renderer and can be used in conjunction with the entity component system (ecs) to provide a simple and rapid high-level interface coupled with excellent low-level performance.

For full api reference take a look at the source:

And some examples of shaders (.pmfx) and render configs (.jsn):

For more reference take a look at the examples, many of them use pmfx shaders and jsn configs.

Shaders

Documentation is now in the pmfx-shader repository.

Renderer

pmfx renderer can be configured using jsn config files to define render pipeline state and make draw calls through a mechanism called a scene view. A scene view specifies a render function to be dispatched into a render target with a specified pipeline state.

Render States

Render states can be created and re-used by name:

blend_states:
{
    additive:
    {
	blend_enable: true,
	src_blend   : one,
	dest_blend  : one
    },
},

sampler_states:
{
    wrap_linear:
    {
	filter : linear,
	address: wrap
    },
},

depth_stencil_states:
{
    default:
    {
	depth_enable: true,
	depth_write : true,
	depth_func  : "less"
    },
},

raster_states:
{
    front_face_cull:
    {
	cull_mode: front,
	fill_mode: solid
    },
},

render_targets:
{                
    shadow_map:
    {
	size  : [2048, 2048],
	format: d24s8
    }
}

Render states can also be obtained and set in code like the following:

u32 sampler_state = pmfx::get_render_state(PEN_HASH("wrap_linear"), pmfx::RS_SAMPLER);

Views

Render states are collected into scene views, scene views can inherit one another by using inherit: view_name

main_view:
{
    target: [main_colour, main_depth],
    clear_colour : [0.0, 0.0, 0.0, 1.0],
    clear_depth : 1.0,
    colour_write_mask : 0xf,
    blend_state : disabled,
    viewport : [0.0, 0.0, 1.0, 1.0],
    raster_state : default,
    depth_stencil_state : default,
    scene : main_scene,
    camera : model_viewer_camera,
    scene_views : ["ces_render_scene"],
    render_flags : ["forward_lit"]

    sampler_bindings :
    [
        { texture: shadow_map, unit : 15, state : wrap_linear, shader : ps },
    ],
},

At startup scene view functions and cameras need to be registered with pmfx, so they can be accessed via the config files, ecs scenes have a render function which draw any geometry entites and also to draw light volumes for deferred rendering. Scene views will setup render state for each pass, but render functions are free to modify render state themselves.

Views are collected together into view sets and a single view set is enabled at one time. A view set for a game or program may look like this:

  • render shadow maps
  • render main scene into gbuffer
  • render light volumes and apply shadow maps
  • render debug lines

You can setup multiple view sets and switch between them in real time, it may be useful for example to switch between a game mode and edit mode, or potentially a game might want to switch between forward and deferred rendering depending on the environment.

Post process sets and chains

Post processes are treated as views and can be chained together inside a post process set to create complex post processing effects. Post process sets consist of a chain of post processes and a set of parameters.

  • A chain is a set of post processes.
  • A post process is a set of views.
  • Post process parameters are .pmfx shader constants.

For example bloom would consist of:

  • High pass filter
  • Downsample
  • Blur Horizontal
  • Blur Vertical
  • Repeat for wider blooms

Users do not need to worry about "ping-ponging" to different read and write buffers. Simply reading and writing to a render target in a config file is enough, the pmfx system will work out render target dependencies and allocate the minimum number of auxiliary targets for the effects required.

A UI is automatically created to make creating and editing post processes simple and easy, it can save out the resulting .jsn config file with post process sets, chains and parameters all setup ready to be loaded.

pmfx pp

Clone this wiki locally