Skip to content

Latest commit

 

History

History
809 lines (621 loc) · 27.7 KB

SHADERS.md

File metadata and controls

809 lines (621 loc) · 27.7 KB

OpenGL GLSL IBMulator shader system

The OpenGL GLSL IBMulator shader system implements the new (draft) libretro's Vulkan GLSL RetroArch shader system specification, with only minor differences and some extension.

IBMulator's shader system aims to be fully compatible with the Vulkan GLSL RetroArch shader system but keep in mind that:

  1. the libretro's specification is still a draft and evolving, so more recent versions of its shaders might fail to load in IBMulator.
  2. IBMulator is based on the public specification, so shaders that rely on specific RetroArch behaviours (and bugs) might not work as expected.

A shader is defined by a preset (.slangp) and at least one source file (.slang).
To load a shader in IBMulator you need to specify the path of its .slangp preset file in ibmulator.ini. You cannot load .slang source files directly.

A Vulkan GLSL RetroArch shader source is translated to OpenGL GLSL by IBMulator and can be used unmodified, assuming your video card supports the OpenGL version required by the shader. Most RetroArch shaders are created for GLSL version 4.50, so your drivers should support at least OpenGL 4.5 in case you want to use any of those. IBMulator's own shaders are for GLSL version 3.30 and the minimum required OpenGL version is 3.3.

High level Overview

The IBMulator shader format outlines a filter chain/graph, a series of shader passes which operate on previously generated data to produce a final result. Every individual pass can access information from all previous shader passes, even across frames.

  • The filter chain specifies a number of shader passes to be executed one after the other.
  • Each pass renders a full-screen quad to a texture of a certain resolution and format.
  • The resolution can be dependent on external information.
  • All filter chains begin at an input texture, which is generated by the emulated video card.
  • All filter chains terminate by rendering to the "backbuffer".

The backbuffer is somewhat special since the resolution of it cannot be controlled by the shader. It can also not be fed back into the filter chain later because the frontend will render UI elements on top of the final pass output.

Single pass

(Input) -> [ Shader Pass #0 ] -> (Backbuffer)

In this case there are no offscreen render targets necessary since the input is rendered directly to screen (backbuffer).

Multiple passes

(Input) -> [ Shader Pass #0 ] -> (Framebuffer) -> [ Shader Pass #1 ] -> (Backbuffer)

Framebuffer here might have a different resolution than both Input and Backbuffer.

Multiple passes and multiple inputs

     /------------------------------------------------\
    /                                                  v
(Input) -> [ Shader Pass #0 ] -> (Framebuffer #0) -> [ Shader Pass #1 ] -> (Backbuffer)

In this case, Shader pass #1 has two inputs, both the original input as well as the result of a pass in-between. All the inputs to a pass can have different resolutions.

Multiple passes, multiple inputs, with history

Frame N:     (Input     N, Input N - 1, Input N - 2) -> [ Shader Pass #0 ] -> (Framebuffer     N, Framebuffer N - 1, Input N - 3) -> [ Shader Pass #1 ] -> (Backbuffer)
Frame N - 1: (Input N - 1, Input N - 2, Input N - 3) -> [ Shader Pass #0 ] -> (Framebuffer N - 1, Framebuffer N - 2, Input N - 4) -> [ Shader Pass #1 ] -> (Backbuffer)
Frame N - 2: (Input N - 2, Input N - 3, Input N - 4) -> [ Shader Pass #0 ] -> (Framebuffer N - 2, Framebuffer N - 3, Input N - 5) -> [ Shader Pass #1 ] -> (Backbuffer)

For framebuffers the previous frame's framebuffer can be read. Just like IIR filters, the "response" of such a feedback in the filter graph gives essentially "infinite" history back in time.

Input textures can have arbitrary number of textures as history (just limited by memory). They cannot feedback since the filter chain cannot render into it, so it effectively is finite response (FIR).

For the very first frames, the content of Input frames with frame N < 0 is the same as the content of frame 0.

No POT padding

No texture in the filter chain is padded at any time. It is possible for resolutions in the filter chain to vary over time. In this scenarios, the textures and framebuffers are simply resized appropriately. Older frames still keep their old resolution in the brief moment that the resolution is changing.

Deduce shader inputs by reflection

There are three main types of inputs in this shader system.

  • Texture samplers (sampler2D).
  • Look-up textures for static input data.
  • Uniform data describing dimensions of textures.
  • Uniform ancillary data for render target dimensions, backbuffer target dimensions, frame count, etc.
  • Uniform user-defined parameters.
  • Uniform matrices for the vertex shader.

Deduction for what a sampler2D uniform wants to sample from is based on built-in identifiers which correspond to certain textures, eg.:

// Source here being defined as the texture from previous framebuffer pass or the input texture if this is the first pass in the chain.
uniform sampler2D Source;

OpenGL GLSL specification

Reflection

The frontend will use reflection in order to deduce what each uniform or texture means. The main types of data passed to shaders are read-only and can be classified as:

  • uniform sampler2D: This is used for input textures, framebuffer results and lookup-textures.
  • uniform Block { };: Uniform blocks can be used for builtins and user parameters that need to be passed to the shader.
  • uniform (int|uint|float|vec4|mat4) NAME;: Builtins and parameters can also be specified as uniforms at global scope.

Resource usage rules

Certain rules must be adhered to in order to make it easier for the frontend to dynamically set up bindings to resources.

  • If version 4.20+ is used, layout(binding = #N) must be declared for all uniform blocks and sampler2Ds.
  • If version 4.20+ is used, all resources must use different bindings.
  • If a block is used in both vertex and fragment stages, their binding number must match.
  • If a block is used in both vertex and fragment stages, members with the same name must have the same offset/binary interface.
  • sampler2D cannot be used in vertex stage, although the size parameters of samplers can be used in vertex stage.
  • Other resource types such as SSBOs, images, atomic counters, etc, etc, are not allowed.
  • Every uniform, block member, and sampler2D must be meaningful to the frontend in some way.

Differences with the Vulkan GLSL RetroArch shader system:

  • layout(push_constant) is ignored.
  • There can be any number of uniform blocks and their names can be anything valid for GLSL.

Preprocess of slang files

#version

The very first line of a .slang file must contain a #version statement.

#include and #pragma include

#include and #pragma include are functionally the same. #include statements are translated to pragmas by IBMulator's preprocessor.

#include FILEPATH statements are replaced by the text contained in FILEPATH.

The include process does not consider any preprocessor #defines or conditional expressions.

The include path must always be relative, and it will be relative to the file path of the current file. Nested includes are allowed, but includes in a cycle are undefined as preprocessor guards are not considered.

E.g.:

#include "../common.inc"

#pragma stage

This pragma controls which part of a .slang file are visible to certain shader stages. Two variants of this pragma are supported:

  • #pragma stage vertex
  • #pragma stage fragment

If no #pragma stage has been encountered yet, lines of code in a shader belong to all shader stages. If a #pragma stage statement has been encountered, that stage is considered active, and the following lines of shader code will only be used when building source for that particular shader stage. A new #pragma stage can override which stage is active.

#pragma name

This pragma lets a shader set its identifier. This identifier can be used to create aliases for other passes. This pragma has precedence over aliasN defined in the preset file.

E.g.:

#pragma name HorizontalPass

Then to reference the framebuffer output of the current pass from a subsequent pass:

uniform sampler2D HorizontalPass;

#pragma format

This pragma controls the format of the framebuffer which this shader will render to. The default render target format is R8G8B8A8_UNORM. This pragma has precedence over float_framebufferN and srgb_framebufferN in the preset file.

Available render target formats are listed below. Actual support depends on the OpenGL driver.

Rendering to uint/int formats requires an UINT/SINT fragment shader output target.

8-bit

  • R8_UNORM
  • R8_UINT
  • R8_SINT
  • R8G8_UNORM
  • R8G8_UINT
  • R8G8_SINT
  • R8G8B8A8_UNORM
  • R8G8B8A8_UINT
  • R8G8B8A8_SINT
  • R8G8B8A8_SRGB

10-bit

  • A2B10G10R10_UNORM_PACK32
  • A2B10G10R10_UINT_PACK32

16-bit

  • R16_UINT
  • R16_SINT
  • R16_SFLOAT
  • R16G16_UINT
  • R16G16_SINT
  • R16G16_SFLOAT
  • R16G16B16A16_UINT
  • R16G16B16A16_SINT
  • R16G16B16A16_SFLOAT

32-bit

  • R32_UINT
  • R32_SINT
  • R32_SFLOAT
  • R32G32_UINT
  • R32G32_SINT
  • R32G32_SFLOAT
  • R32G32B32A32_UINT
  • R32G32B32A32_SINT
  • R32G32B32A32_SFLOAT

E.g.:

#pragma format R16_SFLOAT

#pragma parameter

Shader parameters allow shaders to take user-defined inputs as uniform values. Parameters can only be linked to uniforms of type float.

The format is:

#pragma parameter IDENTIFIER "DESCRIPTION" INITIAL MINIMUM MAXIMUM [STEP]
  • IDENTIFIER is the meaningful string which is also the name of the uniform.
  • DESCRIPTION is a string which is human readable representation of IDENTIFIER, used by the configuration tool in the GUI.
  • INITIAL, MINIMUM and MAXIMUM are floating point values for the uniform.
  • STEP (optional) represent the quantity by which the value is incremented or decremented using the configuration tool in the GUI.

E.g:

#pragma parameter DummyVariable "This is a dummy variable" 1.0 0.2 2.0 0.1

uniform Parameters {
   float DummyVariable;
} params;

Parameters are defined in shader source files, but their IDENTIFIER must is unique to the shader chain. The definition of a parameter is set by the first shader that declares IDENTIFIER.

The value of a parameter is determined in this order:

  1. the INITIAL value.
  2. the value set in the preset.

I/O interface variables

The shader spec specifies two vertex inputs and one fragment output. Varyings between vertex and fragment shaders are user-defined.

Vertex inputs

Two attributes are provided and must be present in a shader. It is only the layout(location = #N) which is actually significant. The particular names of input and output variables are ignored, but should be consistent for readability.

layout(location = 0) in vec4 Position

This attribute is a 2D position in the form vec4(x, y, 0.0, 1.0). Shaders should not try to extract meaning from the x, y. gl_Position must be assigned, eg.:

gl_Position = MVP * Position;
layout(location = 1) in vec2 TexCoord

The texture coordinate is semantically such that (0.0, 0.0) is top-left and (1.0, 1.0) is bottom right. If TexCoord is passed to a varying unmodified, the interpolated varying will be uv = 0.5 / OutputSize when rendering the upper left pixel as expected and uv = 1.0 - 0.5 / OutputSize when rendering the bottom-right pixel.

Vertex/Fragment interface

When using GLSL version 4.20 or later, vertex outputs and fragment inputs can link by location, otherwise they link by name.

E.g.:

#version 450
...
// Vertex
layout(location = 0) out vec4 varying;
// Fragment
layout(location = 0) in vec4 some_other_name;

Fragment outputs

layout(location = 0) out vec4 FragColor

Fragment shaders must have a single output to location = 0. If no location is specified it is assumed to be 0. Multiple render targets are not allowed. The type of the output depends on the render target format. int/uint type must be used if UINT/SINT render target formats are used, otherwise float type.

Builtin variables

Builtin sampler2D

The input of textures get their meaning from their name.

  • Original: This accesses the input of the filter chain (the video card's output), accessible from any pass.
  • Source: This accesses the output from the previous shader pass, or Original if accessed in the first pass of the filter chain.
  • OriginalHistory#: This accesses Original # frames back in time. There is no limit on #, except larger numbers will consume more VRAM. OriginalHistory0 is an alias for Original, OriginalHistory1 is the previous frame and so on.
  • PassOutput#: This accesses the output from pass # in this frame. PassOutput# must be causal, as it is an error to access PassOutputN in pass M if N >= M. PassOutput# will typically be aliased to a more readable value.
  • PassFeedback#: This accesses PassOutput# from the previous frame. Any pass can read the feedback of any feedback, since it is causal. PassFeedback# will typically be aliased to a more readable value.

Differences with the Vulkan GLSL RetroArch shader system:

  • User#: look-up textures access by number # is not supported.

Builtin texture size uniform variables

If a uniform is called ???Size# where ???# is the name of a texture variable, that member must be a vec4, which will receive these values:

  • x: Horizontal size of texture
  • y: Vertical size of texture
  • z: 1.0 / (Horizontal size of texture)
  • w: 1.0 / (Vertical size of texture)

It is valid to use a size variable without declaring the texture itself. It is valid for a variable to be present in multiple uniform blocks at the same time.

E.g.:

uniform Block {
   vec4 OriginalSize;
   vec4 OriginalHistorySize1;
   vec4 MyTextureSize;
} globals;

uniform vec4 SourceSize;

Builtin uniform variables

Other than sampler related uniforms, there are other special uniforms available. These builtin variables may be declared as global uniforms or part of a uniform block.

  • MVP: mat4 Model View Projection matrix.
  • OutputSize: a vec4(x, y, 1.0 / x, 1.0 / y) variable describing the render target size (x, y) for this pass.
  • FinalViewportSize: a vec4(x, y, 1.0 / x, 1.0 / y) variable describing the render target size for the final pass. Accessible from any pass.
  • FrameCount: a uint variable taking a value which increases by one every frame. This value could be pre-wrapped by modulo if specified in the preset.

IBMulator's extensions to the Vulkan GLSL RetroArch shader system:

  • ibmu_Projection: mat4 Projection matrix.
  • ibmu_ModelView: mat4 Model View matrix.
  • ibmu_Brightness, ibmu_Contrast, ibmu_Saturation: float values for the brightness, contrast, and saturation levels as set by the user via the CRT controls.
  • ibmu_Ambient: float, the ambient light intensity.
  • ibmu_Monochrome: int with boolean values, 1 if the installed system monitor is monochromatic, 0 if color.
  • ibmu_PowerOn: int with boolean values, 1 when the machine is on, 0 when off.
  • ibmu_PassNumber: uint taking the pass number of the shader.

Aliases

Aliases can give meaning to arbitrary names in a shader file.

If a shader pass has a #pragma name NAME or aliasN=NAME associated with it, meaning is given to the shader:

  • NAME, is a sampler2D.
  • NAMESize is a vec4 size uniform associated with NAME.
  • NAMEFeedback is a sampler2D for the previous frame.
  • NAMEFeedbackSize is a vec4 size uniform associated with NAMEFeedback.

User textures must be accessed by their alias specified in the preset. If a texture has an alias MYTEXTURE then:

  • MYTEXTURE is a sampler2D.
  • MYTEXTURESize is a vec4 size uniform associated with MYTEXTURE.

Example shader code

#version 450

layout(binding = 0, std140) uniform UBO
{
   mat4 MVP;
   vec4 SourceSize; // Not used here (ignored)
   float ColorMod;
} globals;

#pragma name StockShader
#pragma format R8G8B8A8_UNORM
#pragma parameter ColorMod "Color intensity" 1.0 0.1 2.0 0.1

#pragma stage vertex
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord;
void main()
{
   gl_Position = globals.MVP * Position;
   vTexCoord = TexCoord;
}

#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 FragColor;
layout(binding = 1) uniform sampler2D Source;
void main()
{
   FragColor = texture(Source, vTexCoord) * globals.ColorMod;
}

Sampler objects

The configuration of the OpenGL's sampler object associated with a sampler2D uniform is specified by the preset format.
The value of the ibmu_samplers_mode setting determines the way samplers for builtin textures are created. User textures have their own samplers.

Either:

  1. every builtin texture has its own sampler configuration that is used by all the passes that use that texture.
  2. or every pass has a general input sampler that will be used for sampling every builtin texture (Original, Source, ...) it uses.

The RetroArch shader system adheres to the first interpretation (apparently).

Sampler objects remain constant throughout the frame, there is no way to select samplers on a frame-by-frame basis.

sRGB

The input to the filter chain (the Original texture) will not be of an sRGB format. If needed, it's possible to have a first pass which linearizes the input to a proper sRGB render target. In this way, custom gammas can be used as well. Similarly, the final pass will not be an sRGB backbuffer.

Pre-defined preprocessor substitutions

Every shader will have the following #define statements added right after the #version statement.

  • IBMU_PASS_NUMBER: receives the number of the current pass.

Custom additional substitutions can be specified in the preset file.

Preset format (.slangp)

Shader chains are defined in text files having the .slangp extension.

The purpose of preset files is to combine several shader programs into a multi-pass shader, defining all the necessary configuration parameters like scaling, filtering, etc, as well as user textures.

Preset files use a syntax similar to .ini files:

  • values are set with key = value pairs.
  • a global unnamed block is used so [section] blocks are ignored.
  • single line comments starts with ; or # or /. If a comment is at the end of a key-value pair, the comment character must be separated from the value by at least a blank space.
  • comment blocks are /* ... */.

Values can be surrounded by double quotation marks ("). In this case the " characters are ignored.

Parameters

  • shaders (int): defines the number of passes, ie. how many slang shaders will be loaded. This value must be at least one.
    Required

  • ibmu_samplers_mode (string): sets the way sampler objects for builtin textures are created (user textures have their own samplers).

    • "texture": sampler objects are created per texture, ie. textures are sampled using the sampler object stored in the textures.
    • "pass": sampler objects are created per pass, ie. every pass uses its own sampler object for every builtin texture it uses.

    Default: "texture"

    Sampler objects are configured with filter_linearN, wrap_modeN, and mipmap_inputN.

  • ibmu_input_size (string): sets the size of the input texture Original. Can be set to one of these values:

    • "crtc": the size is equivalent to the resolution generated by the video card's CRTC clocking.
    • "video_mode": the size is set by the current video mode, and is determined by the CRTC resolution, the Clocking Mode Register, and the Maximum Scan Line Register.

    Default: "video_mode"

    E.g.: the famous Mode 13h video mode is 320x200 pixels. It is generated using a screen resolution of 640x400 pixels. Using "crtc" the input texture will have a size of 640x400, whereas with "video_mode" it will have a size of 320x200.

  • ibmu_output_size (string): forces the output to a particular resolution. This acts as an override to shader_output_size in ibmulator.ini. Can be set to one of these values:

    • "native": use the native resolution of the monitor.
    • "WxH": a specific size in pixels, maintaining the viewport's aspect ratio.
    • "max_WxH": a maximum size in pixels, maintaining the viewport's aspect ratio.

    Default: not set

  • shaderN (string): defines the path of the shader for pass N, where N goes from 0 to shaders - 1. The path is relative to the directory the preset was loaded from.
    Required

  • aliasN (string): defines a name alias for pass N. Aliases are used to give meaningful names to samplers of pass outputs.
    Default: ""

  • filter_linearN (boolean): enables linear filtering for the output texture of pass N-1 or input sampler object for pass N (depending on the configuration).
    Default: true

  • mipmap_inputN (boolean): enables mip-mapping for output texture of pass N-1 or the inputs referenced by pass N (depending on the configuration).
    Default: false

  • wrap_modeN (string): defines the repeating mode of the output texture of pass N-1 or the input sampler object of pass N (depending on the configuration). Can be set to one of these values:

    • "repeat": equivalent to GL_REPEAT.
    • "clamp_to_border": equivalent to GL_CLAMP_TO_BORDER.
    • "clamp_to_edge": equivalent to GL_CLAMP_TO_EDGE.
    • "mirrored_repeat": equivalent to GL_MIRRORED_REPEAT.
    • "mirror_clamp_to_edge": equivalent to GL_MIRROR_CLAMP_TO_EDGE.

    Default: "clamp_to_border", with border color (0,0,0,0).

  • float_framebufferN (boolean): defines if shader N should render to a 32-bit floating point buffer. This only takes effect if shader N is actually rendered to an FBO. If the shader file has a valid #pragma format directive, this value is ignored.
    Default: false

  • srgb_framebufferN (boolean): defines if shader N should render to an sRGB buffer. This only takes effect if shader N is actually rendered to an FBO. If the shader file has a valid #pragma format directive, this value is ignored. float_framebufferN takes precedence.
    Default: false

  • frame_count_modN (uint): defines which modulo to apply to FrameCount for pass N. FrameCount will take the value FrameCount % frame_count_modN.
    Default: 0 (disabled)

  • scale_typeN, scale_type_xN, scale_type_yN (string): can be set to one of these values:

    • "original": output size of shader N is relative to the size of the Original texture.
    • "source": output size of shader N is relative to the size of the Source texture.
    • "viewport": output size of shader N is relative to the value of FinalViewportSize. This value will change when the user resizes their window.
    • "absolute": output size is statically defined to a certain size.

    Default: "viewport" or "source"

    If no scale_type is set for the very last shader, the default is "viewport", otherwise "source". If there is only one shader, it is considered to be the very last shader. If the last shader has "viewport" with both scale values set to 1.0 and no feedback is needed then rendering is direct to backbuffer. If any other scaling combination is defined, it has to go through a FBO, and subsequently rendered to screen. The filtering option used when stretching to the backbuffer is set in ibmulator.ini.

  • scaleN, scale_xN, scale_yN (float/int): these values control the scaling params from scale_typeN. The values may be either floating or integer depending on the type. scaleN controls both scaling type in horizontal and vertical directions.
    Default: 1.0

    If scaleN is defined, scale_xN and scale_yN have no effect. scale_xN and scale_yN control scaling properties for the directions separately.

    Should scale_type_xN and scale_type_yN be set to different values, the use of scaleN is undefined (i.e. if X-type is "absolute" (takes int), and Y-type is "source" (takes float).)

  • ibmu_blending_outputN (boolean): enables blending during rendering of pass N.
    Default: false

  • textures (multiple strings): defines one or more user textures names. Several names are delimited with ; like "TEXNAME1;TEXNAME2;..."

    E.g.:

    textures = "foo;bar"
    

    These texture names serves as the names for GLSL sampler uniforms.

    E.g.:

    uniform sampler2D foo;
    uniform sampler2D bar;
    
  • TEXNAME (string): sets the path of the texture TEXNAME defined in textures.

    E.g.:

    foo = image0.png
    bar = ../resources/image1.png
    

    Paths are relative to the directory the preset was loaded from.

    The only supported file format is PNG.

  • TEXNAME_linear orTEXNAME_filter_linear (boolean): enables linear filtering for texture named TEXNAME.
    Default: true

  • TEXNAME_wrap_mode or TEXNAME_repeat_mode (string): defines the repeating mode for texture TEXNAME. Can be set to one of these values:

    • "repeat": equivalent to GL_REPEAT.
    • "clamp_to_border": equivalent to GL_CLAMP_TO_BORDER.
    • "clamp_to_edge": equivalent to GL_CLAMP_TO_EDGE.
    • "mirrored_repeat": equivalent to GL_MIRRORED_REPEAT.
    • "mirror_clamp_to_edge": equivalent to GL_MIRROR_CLAMP_TO_EDGE.

    Default: "clamp_to_border", with border color (0,0,0,0).

  • TEXNAME_mipmap (boolean): enables mip-mapping for texture TEXNAME.
    Default: false

  • ibmu_defines (multiple strings): adds one or more #define preprocessor statements to all the shaders in the chain immediatly after the #version statement. Several values are delimited with ; like "DEFINE1;DEFINE2;..."

    E.g.:

    ibmu_defines = "MYVALUE;MYCOLOR"
    
  • DEFINE (string): sets the value for DEFINE declared in ibmu_defines.

    E.g.:

    MYVALUE = "0.5"
    MYCOLOR = "vec3(1.0,MYVALUE,0.0)"
    

    Values are used as they are, without any interpretation. Thus the #defines for the previous examples are:

    #define MYVALUE 0.5
    #define MYCOLOR vec3(1.0,MYVALUE,0.0)
    
Realistic GUI mode integration

A shader can be integrated in the IBMulator's realistic GUI mode using the following preset settings:

  • ibmu_rendering_size (string): tells the renderer what part of the system the shader is supposed to render. Can be one of:

    • "vga": only the graphics card's output
    • "crt": the VGA output and the CRT glass
    • "monitor": the whole monitor, including the frame/bezels, the CRT glass, and the VGA.

    Default: "vga"

  • ibmu_monitor_width (int): total monitor width in pixels.

  • ibmu_monitor_height (int): total monitor height in pixels.

  • ibmu_crt_width (int): the CRT width in pixels.

  • ibmu_crt_height (int): the CRT height in pixels.

  • ibmu_monitor_bezelw (int): the left/right bezel width in pixels.

  • ibmu_monitor_bezelh (int): the top bezel height in pixels.

  • ibmu_vga_scale (float): scale factor of the VGA relative to the CRT glass, from 0.0 to 1.0.

Pixel values generally refer to a texture.

Inclusions with #reference PRESET

Preset files can be referenced by other preset files via the #reference statement.

The referenced preset will be used as a base and the values of its keys will be overridden by the same keys in the current preset.

PRESET path must always be relative, and it will be relative to the path of the current file. Nested references are allowed, but references in a cycle will result in an error.

E.g.:

#reference 'base_preset.slangp'
MyValue = 2.0

where base_preset.slangp contains:

shaders = 1
shader0 = myshader.slang
MyValue = 1.0

will result in:

shaders = 1
shader0 = myshader.slang
MyValue = 2.0