(SDK root)\Samples\Managed\Direct3D\FragmentLinker

This sample shows how to use the FragmentLinker class. Shader source code can be split into a series of shader fragments which are compiled separately and linked together to form a complete shader. This linking stage is very efficient, making it suitable for run-time use. In this way, a Microsoft Direct3D application can custom-build an appropriate shader for the current graphics card.

FragmentLinker sample

Supported Languages

  • C#

Path

Source: (SDK root)\Samples\Managed\Direct3D\FragmentLinker
Executable: (SDK root)\Samples\Managed\Direct3D\Bin\x86\csFragmentLinker.exe

Sample Overview

Large-scale Direct3D applications commonly employ a large set of redundant shaders covering every supported fallback case for a given technique (often generated by uniform parameters) from which the appropriate shader for the current graphics hardware is selected at run time. This approach can result in a large amount of compiled shaders being included with the application, only a small fraction of which are ever used on a given machine. Using shader fragments, the desired shader for the current graphics card can be built at run time.

How the Sample Works

This sample links together one of two vertex shader fragments that handles projection (vertex animated or static) and one of two vertex shader fragments that handles vertex lighting (diffuse enabled or ambient only). These fragments are compiled once during initialization and are then linked together at run time.

Only a few steps need to be followed when using shader fragments in your application:

  1. Create the FragmentLinker object with the FragmentLinker Constructor.
  2. Load and compile the shader fragments by calling one of the following FragmentLinker methods: GatherFragmentsFromFile, GatherFragmentsFromStream, or GatherFragmentsFromString.
  3. Add the compiled fragments to the linker's internal list with a call to FragmentLinker.AddFragments.
  4. Retrieve the handles of the fragments you want linked through calls to FragmentLinker.GetFragmentHandle.
  5. Link together a subset of the added fragments by passing fragment handles to one of the following FragmentLinker methods: LinkVertexShader, LinkPixelShader, or LinkShader. These methods return an object that contains a compiled shader that can be used for rendering.

The following sample application performs these steps upon device creation within the sample's OnCreateDevice function:

          [C#]
          
private void OnCreateDevice( object sender, DeviceEventArgs e )
.
.
.
// Create the fragment linker interface
fragmentLinker = new FragmentLinker( e.Device, 0 );

// Compile the fragments to a buffer. The fragments must be linked 
// together to form a shader before they can be used for rendering.
path = Utility.FindMediaFile( "FragmentLinker.fx" );
compiledFragments = 
    Microsoft.DirectX.Direct3D.FragmentLinker.GatherFragmentsFromFile( 
        path, null, null, ShaderFlags.None );

// Build the list of compiled fragments
fragmentLinker.AddFragments( compiledFragments );

// Store the fragment handles
ComboBox cb1 = sampleUI.GetComboBox( Lighting );
cb1.Clear();
cb1.AddItem( "Ambient", 
    fragmentLinker.GetFragmentHandle( "AmbientFragment" ) );
cb1.AddItem( "Ambient & Diffuse", 
    fragmentLinker.GetFragmentHandle( "AmbientDiffuseFragment" ) );

ComboBox cb2 = sampleUI.GetComboBox( Animation );
cb2.Clear();
cb2.AddItem( "On" , 
    fragmentLinker.GetFragmentHandle( "ProjectionFragment_Animated" ) );
cb2.AddItem( "Off", 
    fragmentLinker.GetFragmentHandle( "ProjectionFragment_Static" ) );

// Link the desired fragments to create the vertex shader
LinkVertexShader();

The final linking step is performed within the FragmentLinker.LinkVertexShader method. In addition to being called upon device creation, this method is also called whenever the user changes a fragment combobox selection. The currently selected fragments are retrieved from the user interface controls and the shader data is updated.

          [C#]
          
private void LinkVertexShader()
{
    const int NumberFragments = 2;
    Device device = sampleFramework.Device;
    EffectHandle[] aHandles = new EffectHandle[NumberFragments];

    aHandles[0] = 
        sampleUI.GetComboBox( Animation ).GetSelectedData() as EffectHandle;
    aHandles[1] = 
        sampleUI.GetComboBox( Lighting ).GetSelectedData() as EffectHandle;

    if ( vertexShader != null )
    {
        vertexShader.Dispose();
    }

    string errors;
    using (GraphicsStream code = fragmentLinker.LinkShader( 
        "vs_1_1", ShaderFlags.None, aHandles, out errors ) )
    {
        vertexShader = new VertexShader( device, code );
        constTable = ConstantTable.FromShader( code );
    }
    // Set the global variables
    device.VertexShader = vertexShader;
    if ( constTable != null )
    {
        constTable.SetValue( device, "g_vMaterialAmbient", MaterialAmbient );
        constTable.SetValue( device, "g_vMaterialDiffuse", MaterialDiffuse );
        constTable.SetValue( device, "g_vLightColor", LightColor );
        constTable.SetValue( device, "g_vLightPosition",LightPosition );
    }
        }