Direct2D ships with a library of effects that perform a variety of common image operations.
See the built-in effects topic for the complete list of effects. For functionality that cannot be
achieved with the built-in effects, Direct2D allows you to write your own custom effects
using standard HLSL. You can use these custom effects alongside the built-in effects
that ship with Direct2D.

In this topic, we show you the steps and concepts you need to design and create a fully-featured custom effect.

Introduction: What is inside an effect?

Conceptually, a Direct2D effect performs an imaging task, like changing brightness, de-saturating an
image, or as shown above, creating a drop shadow. To the app, they are simple. They can accept zero or more input images, expose multiple properties that control their operation, and generate a single output image.

There are four different parts of a custom effect that an effect author is responsible for:

Effect Interface: The effect interface conceptually defines how an app interacts with a custom effect (like how many inputs the effect accepts and what properties are available).
The effect interface manages a transform graph, which contains the actual imaging operations.

Transform graph: Each effect creates an internal transform graph made up of individual transforms. Each transform represents a single image operation. The effect is responsible
for linking these transforms together into a graph to perform the intended imaging effect. An effect can add, remove, modify, and reorder transforms in response to changes to the effect's external properties.

Transform: A transform represents a single image operation. Its main purpose is to house the shaders that are executed for each output pixel. To that end, it is responsible for
calculating the new size of its output image based on logic in its shaders. It also must calculate which area of its input image the shaders need to read from to render the requested output region.

Shader: A shader is executed against the transform's input on the GPU (or CPU if software rendering is specified when the app creates the Direct3D device).
Effect shaders are written in High Level Shading Language (HLSL) and are compiled into byte code during
the effect's compilation, which is then loaded by the effect during run-time. This reference document describes how to write Direct2D-compliant
HLSL. The Direct3D documentation contains a basic HLSL overview.

Creating an effect interface

The effect interface defines how an app interacts with the custom effect. To create an effect interface, a class must implement ID2D1EffectImpl, define metadata that describes the effect
(such as its name, input count and properties), and create methods that register the custom effect for use with Direct2D.

Once all of the components for an effect interface have been implemented, the class' header will appear like this:

Implement ID2D1EffectImpl

Direct2D calls the Initialize
method after the ID2D1DeviceContext::CreateEffect method has been called by the app. You can use this
method to perform internal initialization or any other operations needed for the effect. Additionally, you can use it to create the effect's initial transform graph.

SetGraph(ID2D1TransformGraph *pTransformGraph)

Direct2D calls the SetGraph method when the
number of inputs to the effect is changed. While most effects have a constant number of inputs, others like the Composite effect support
a variable number of inputs. This method allows these effects to update their transform graph in response to a changing input count. If an effect does not support a variable input count,
this method can simply return E_NOTIMPL.

PrepareForRender (D2D1_CHANGE_TYPE changeType)

The PrepareForRender method provides an opportunity for effects to perform any operations in response to
external changes. Direct2D calls this method just before it renders an effect if at least one of these is true:

The effect has been previously initialized but not yet drawn.

An effect property has changed since the last draw call.

The state of the calling Direct2D context (like DPI) has changed since the last draw call.

Implement the effect registration and callback methods

Apps must register effects with Direct2D before instantiating them. This registration is scoped to an instance
of a Direct2D factory, and must be repeated each time the app is run. To enable this registration,
a custom effect defines a unique GUID, a public method that registers the effect, and a private callback method that returns an instance of the effect.

// Example GUID used to uniquely identify the effect. It is passed to Direct2D during// effect registration, and used by the developer to identify the effect for any// ID2D1DeviceContext::CreateEffect calls in the app. The app should create// a unique name for the effect, as well as a unique GUID using a generation tool.
DEFINE_GUID(CLSID_SampleEffect, 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);

Define a public registration method

Next, define a public method for the app to call to register the effect with Direct2D.
Because effect registration is specific to an instance of a Direct2D factory, the method accepts
an ID2D1Factory1 interface as a parameter. To register the effect, the method then calls
the ID2D1Factory1::RegisterEffectFromString API on the ID2D1Factory1
parameter.

This API accepts an XML string that describes the metadata, inputs, and properties of the effect. The metadata for an effect is for informational purposes only, and can be queried by
the app through the ID2D1Properties interface. The input and property data, on the other hand, is
used by Direct2D and represents the effect's functionality.

An XML string for a minimal sample effect is shown here. Adding custom properties to the XML is covered in the Adding custom properties to an effect section.

Implement the IUnknown interface

Finally, the effect must implement the IUnknown interface for compatibility with COM.

Creating the effect's transform graph

An effect can use several different transforms (individual image operations) to create its desired imaging effect. To control the order in which these transforms are applied to the input image,
the effect arranges them into a transform graph. A transform graph can make use of the effects and transforms included in Direct2D
as well as custom transforms created by the effect author.

Creating a single-node transform graph

Once you create a transform, the effect's input needs to be connected to the transform's input, and the transform's output needs to be connected to the effect's output.
When an effect only contains a single transform, you can use the ID2D1TransformGraph::SetSingleTransformNode
method to easily accomplish this.

You can create or modify a transform in the effect's Initialize
or SetGraph methods using the provided ID2D1TransformGraph parameter.
If an effect needs to make changes to the transform graph in another method where this parameter is not available, the effect can save
the ID2D1TransformGraph parameter as a member variable of the class and access it elsewhere,
such as PrepareForRender or a custom property callback method.

A sample Initialize method is shown here. This method creates a single-node transform graph that
offsets the image by one hundred pixels in each axis.

Creating a multi-node transform graph

Adding multiple transforms to an effect's transform graph allows effects to internally perform multiple image operations that are presented to an app as a single unified effect.

As noted above, the effect's transform graph may be edited in any effect method using the ID2D1TransformGraph parameter
received in the effect's Initialize method. The following APIs on that interface can be used to create or modify an effect's transform graph:

AddNode(ID2D1TransformNode *pNode)

The AddNode method, in effect, 'registers' the transform with the effect, and must be called before the transform
can be used with any of the other transform graph methods.

Adding custom properties to an effect

Effects can define custom properties that allow an app to change the effect's behavior during runtime. There are three steps to define a property for a custom effect:

Add the property metadata to the effect's registration data

Add property to registration XML

You must define a custom effect's properties during the effect's initial registration with Direct2D.
First, you must update the effect's registration XML in its public registration method with the new property:

When you define an effect property in XML, it needs a name, a type, and a display name. A property's display name, as well as the overall effect's category, author,
and description values can and should be localized.

For each property, an effect can optionally specify default, min, and max values. These values are for informational use only.
They are not enforced by Direct2D. It is up to you to implement any specified default/min/max logic in the effect class yourself.

The type value listed in the XML for the property must match the corresponding data type used by the property's getter and setter methods.
Corresponding XML values for each data type are shown in this table:

const D2D1_PROPERTY_BINDING bindings[] =
{
D2D1_VALUE_TYPE_BINDING(
L"Offset", // The name of property. Must match name attribute in XML.
&SetOffset, // The setter method that is called on "SetValue".
&GetOffset // The getter method that is called on "GetValue".
)
};

The D2D1_VALUE_TYPE_BINDING macro requires the effect class to inherit from ID2D1EffectImpl before any other interface.

Custom properties for an effect are indexed in the order they are declared in the XML, and once created can be accessed by the
app using the ID2D1Properties::SetValue
and ID2D1Properties::GetValue methods.
For convenience, you can create a public enumeration that lists each property in the effect's header file:

Create the getter and setter methods for the property

The next step is to create the getter and setter methods for the new property. The names of the methods must match the ones specified in
the D2D1_PROPERTY_BINDING array. In addition, the property type specified
in the effect's XML must match the type of the setter method's parameter and the getter method's return value.

Update effect's transforms in response to property change

To actually update an effect's image output in response to a property change, the effect needs to change its underlying transforms.
This is typically done in the effect's PrepareForRender method
which Direct2D automatically calls when one of an effect's properties has been changed.
However, transforms can be updated in any of the effect's methods: such as Initialize or the effect's property setter methods.

For example, if an effect contained an ID2D1OffsetTransform and wanted to modify its offset value in
response to the effect's Offset property being changed, it would add the following code in PrepareForRender:

Creating a custom transform

To implement image operations beyond what is provided in Direct2D, you must implement custom transforms.
Custom transforms can arbitrarily change an input image through the use of custom HLSL shaders.

Transforms implement one of two different interfaces depending on the types of shaders they use. Transforms using pixel and/or vertex shaders must
implement ID2D1DrawTransform, while transforms using compute shaders
must implement ID2D1ComputeTransform. These interfaces both inherit
from ID2D1Transform. This section focuses on implementing the functionality that is common to both.

GetInputCount

MapInputRectsToOutputRect

Direct2D calls the MapInputRectsToOutputRect
method each time the transform is rendered. Direct2D passes a rectangle representing the bounds of each of the inputs to the transform.
The transform is then responsible for calculating the bounds of the output image. The size of the rectangles for all the methods on this interface (ID2D1Transform)
are defined in pixels, not DIPs.

This method is also responsible for calculating the region of the output that is opaque based on the logic of its shader and the opaque regions of each input. An opaque region of an image is
defined as that where the alpha channel is '1' for the entirety of the rectangle. If it is unclear whether a transform's output is opaque, the output opaque rectangle should be set to (0, 0, 0, 0) as a safe value.
Direct2D uses this info to perform rendering optimizations with 'guaranteed opaque' content. If this value is inaccurate,
it can result in incorrect rendering.

The you can modify the transform's rendering behavior (as defined in sections 6 through 8) during this method. However, the you can't modify other transforms in the transform graph, or the graph layout itself here.

For a more complex example, consider how a simple blur operation would be represented:

If a blur operation uses a 5 pixel radius, the size of the output rectangle must expand by 5 pixels, as shown below. When modifying rectangle coordinates,
a transform must ensure that its logic does not cause any over/underflows in the rectangle coordinates.

Because the image is blurred, a region of the image which was opaque may now be partially transparent. This is because the area outside the image defaults to
transparent black and this transparency will be blended into the image around the edges. The transform must reflect this in its output opaque rectangle calculations:

Likewise, if a transform shrinks or expands an image (like the blur example here), pixels often use surrounding pixels to calculate their value. With a blur, a pixel is averaged with its surrounding pixels,
even if they are outside of the bounds of the input image. This behavior is reflected in the calculation. As before, the transform checks for overflows when expanding a rectangle's coordinates.

This figure visualizes the calculation. Direct2D automatically samples transparent black pixels where the input image doesn't exist,
allowing the blur to be blended gradually with the existing content on the screen.

If the mapping is non-trivial, then this method should set the input rectangle to the maximum area to guarantee correct results.
To do this, set the left and top edges to INT_MIN, and the right and bottom edges to INT_MAX.

MapInvalidRect

Direct2D also calls the MapInvalidRect method.
However, unlike the MapInputRectsToOutputRect
and MapOutputRectToInputRects methods Direct2D
is not guaranteed to call it at any particular time. This method conceptually decides what part of a transform's output needs to be re-rendered in response to part or all of its input changing.
There are three different scenarios for which to calculate a transform's invalid rect.

Transforms with one-to-one pixel mapping

For transforms that map pixels 1-1, simply pass the invalid input rectangle through to the invalid output rectangle:

Transforms with many-to-many pixel mapping

When a transform's output pixels are dependent on their surrounding area, the invalid input rectangle must correspondingly be expanded. This is to reflect that pixels surrounding
the invalid input rectangle will also be affected and become invalid. For example, a five pixel blur uses the following calculation:

Transforms with complex pixel mapping

For transforms where input and output pixels do not have a simple mapping, the entire output can be marked as invalid. For example, if a transform simply outputs the average color of the input,
the entire output of the transform changes if even a small part of the input is changed. In this case, the invalid output rectangle should be set to a logically infinite rectangle (shown below).
Direct2D automatically clamps this to the bounds of the output.

// If any change in the input image affects the entire output, the// transform should set pInvalidOutputRect to a logically infinite rect.
*pInvalidOutputRect = D2D1::RectL(LONG_MIN, LONG_MIN, LONG_MAX, LONG_MAX);

Adding a pixel shader to a custom transform

Once a transform has been created, it needs to provide a shader that will manipulate the image pixels. This section covers the steps to using a pixel shader with a custom transform.

Implementing ID2D1DrawTransform

To use a pixel shader, the transform must implement the ID2D1DrawTransform interface, which inherits from the ID2D1Transform interface described in section 5. This interface contains one new method to implement:

SetDrawInfo(ID2D1DrawInfo *pDrawInfo)

Direct2D calls the SetDrawInfo method when the transform is first added to an effect's transform graph. This method provides an ID2D1DrawInfo parameter that controls how the transform is rendered. See the ID2D1DrawInfo topic for the methods available here.

Creating a GUID for the pixel shader

Next, the transform must define a unique GUID for the pixel shader itself. This is used when Direct2D loads the shader into memory, as well as when the transform chooses which pixel shader to use for execution. Tools such as guidgen.exe, which is included with Visual Studio, can be used to generate a random GUID.

// Example GUID used to uniquely identify HLSL shader. Passed to Direct2D during// shader load, and used by the transform to identify the shader for the// ID2D1DrawInfo::SetPixelShader method. The effect author should create a// unique name for the shader as well as a unique GUID using// a GUID generation tool.
DEFINE_GUID(GUID_SamplePixelShader, 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);

Loading the pixel shader with Direct2D

A pixel shader must be loaded into memory before it can be used by the transform.

To load the pixel shader into memory, the transform should read the compiled shader byte code from the .CSO file generated by Visual Studio (see Direct3D documentation for details) into a byte array.
This technique is demonstrated in detail in the D2DCustomEffects SDK sample.

Once the shader data has been loaded into a byte array, call the LoadPixelShader method on the effect's ID2D1EffectContext object. Direct2D ignores calls to LoadPixelShader when a shader with the same GUID has already been loaded.

After a pixel shader has been loaded into memory, the transform needs to select it for execution by passing its GUID to the SetPixelShader method on the ID2D1DrawInfo parameter provided during the SetDrawInfo method. The pixel shader must be already loaded into memory before being selected for execution.

Changing shader operation with constant buffers

To change how a shader executes, a transform may pass a constant buffer to the pixel shader. To do so, a transform defines a struct that contains the desired variables in the class header:

Once the buffer has been defined, the values contained within can be read from anywhere within the pixel shader.

Writing a pixel shader for Direct2D

Direct2D transforms use shaders authored using standard HLSL. However, there are a few key concepts to writing a pixel shader that executes from the context of a transform. For a completed example of a fully functionally pixel shader,
see the D2DCustomEffects SDK sample.

Direct2D automatically maps a transform's inputs to Texture2D and SamplerState objects in the HLSL. The first Texture2D is located at register t0, and the first SamplerState is located at register s0. Each additional input is located at the next corresponding registers (t1 and s1 for example). Pixel data for a particular input can be sampled by calling Sample on the Texture2D object and passing in the corresponding SamplerState object and the texel coordinates.

A custom pixel shader is run once for each pixel that is rendered. Each time the shader is run, Direct2D automatically provides three parameters that identify its current execution position:

Scene-space output: This parameter represents the current execution position in terms of the overall target surface. It is defined in pixels and its min/max values correspond to the bounds of the rectangle returned by MapInputRectsToOutputRect.

Clip-space output: This parameter is used by Direct3D, and is must not be used in a transform's pixel shader.

Texel-space input: This parameter represents the current execution position in a particular input texture. A shader should not take any dependencies on how this value is calculated. It should only use it to sample the pixel shader's input, as shown in the code below:

Adding a vertex shader to a custom transform

You can use vertex shaders to accomplish different imaging scenarios than pixel shaders. In particular, vertex shaders can perform geometry-based image effects by transforming vertices that comprise an image. Vertex shaders can be used independently of or in conjunction with transform-specified pixel shaders. If a vertex shader is not specified, Direct2D substitutes in a default vertex shader for use with the custom pixel shader.

The process for adding a vertex shader to a custom transform is similar to that of a pixel shader – the transform implements the ID2D1DrawTransform interface, creates a GUID, and (optionally) passes constant buffers to the shader. However, there are a few key additional steps that are unique to vertex shaders:

Creating a vertex buffer

A vertex shader by definition executes on vertices passed to it, not individual pixels. To specify the vertices for the shader to execute on, a transform creates a vertex buffer to pass to the shader. The layout of vertex buffers is beyond the scope of this document. Please see the Direct3D reference for details, or the D2DCustomEffects SDK sample for a sample implementation.

If no vertex buffer is specified by the transform, Direct2D passes a default vertex buffer representing the rectangular image location.

Changing SetDrawInfo to utilize a vertex shader

Like with pixel shaders, the transform must load and select a vertex shader for execution. To load the vertex shader, it calls the LoadVertexShader method on the ID2D1EffectContext method received in the effect's Initialize method. To select the vertex shader for execution, it calls SetVertexProcessing on the ID2D1DrawInfo parameter received in the transform's SetDrawInfo method. This method accepts a GUID for a previously-loaded vertex shader as well as (optionally) a previously-created vertex buffer for the shader to execute on.

Implementing a Direct2D vertex shader

A draw transform can contain both a pixel shader and a vertex shader. If a transform defines both a pixel shader and a vertex shader, then the output from the vertex shader is given directly to the pixel shader: the app can customize the return signature of the vertex shader / the parameters of the pixel shader as long as they are consistent.

On the other hand, if a transform only contains a vertex shader, and relies on Direct2D's default pass-through pixel shader, it must return the following default output:

A vertex shader stores the result of its vertex transformations in the shader's Scene-space output variable. To compute the Clip-space output and the Texel-space input variables, Direct2D automatically provides conversion matrices in a constant buffer:

// Constant buffer b0 is used to store the transformation matrices from scene space// to clip space. Depending on the number of inputs to the vertex shader, there// may be more or fewer "sceneToInput" matrices.
cbuffer Direct2DTransforms : register(b0)
{
float2x1 sceneToOutputX;
float2x1 sceneToOutputY;
float2x1 sceneToInput0X;
float2x1 sceneToInput0Y;
};

Sample vertex shader code can be found below that uses the conversion matrices to calculate the correct clip and texel spaces expected by Direct2D:

The above code can be used as a starting point for a vertex shader. It merely passes through the input image without performing any transforms. Again,
see the D2DCustomEffects SDK sample for a fully-implemented vertex shader-based transform.

If no vertex buffer is specified by the transform, Direct2D substitutes in a default vertex buffer representing the rectangular image location. The parameters to the vertex shader are changed to those of the default shader output:

The vertex shader may not modify its sceneSpaceOutput and clipSpaceOutput parameters. It must return them unchanged. It may however modify the texelSpaceInput parameter(s) for each input image.
If the transform also contains a custom pixel shader, the vertex shader is still able to pass additional custom parameters directly to the pixel shader. Additionally, the sceneSpace conversion matrices custom buffer (b0) is no longer provided.

Adding a compute shader to a custom transform

Finally, custom transforms may utilize compute shaders for certain targeted scenarios. Compute shaders can be used to implement complex image effects that require arbitrary access to input and output image buffers. For example, a basic histogram algorithm cannot be implemented with a pixel shader due to limitations on memory access.

Because compute shaders have higher hardware feature level requirements than pixel shaders, pixel shaders should be used when possible to implement a given effect. Specifically, compute shaders only run on most DirectX 10 level cards and higher. If a transform chooses to use a compute shader, it must check for the appropriate hardware support during instantiation in addition to implementing the ID2D1ComputeTransform interface.

There are two different types of compute shaders that a transform can use: Shader Model 4 (DirectX 10) and Shader Model 5 (DirectX 11). There are certain limitations to Shader Model 4 shaders. See the Direct3D documentation for details. Transforms can contain both types of shaders, and can fall back to Shader Model 4 when required:
see the D2DCustomEffects SDK sample for an implementation of this.

Implement ID2D1ComputeTransform

This interface contains two new methods to implement in addition to the ones in ID2D1Transform:

SetComputeInfo(ID2D1ComputeInfo *pComputeInfo)

Like with pixel and vertex shaders, Direct2D calls the SetComputeInfo method when the transform is first added to an effect's transform graph. This method provides an ID2D1ComputeInfo parameter that controls how the transform is rendered. This includes choosing the compute shader to execute through the ID2D1ComputeInfo::SetComputeShader method. If the transform chooses to store this parameter as a class member variable, it can be accessed and changed from any transform or effect method with the exception of the MapOutputRectToInputRects and MapInvalidRect methods. See the ID2D1ComputeInfo topic for other methods available here.

Whereas pixel shaders are executed on a per-pixel basis and vertex shaders are executed on a per-vertex basis, compute shaders are executed on a per-'threadgroup' basis. A threadgroup represents a number of threads that execute concurrently on the GPU. The compute shader HLSL code decides how many threads should be executed per threadgroup. The effect scales the number of threadgroups so that the shader executes the desired number of times, depending on the shader's logic.

The CalculateThreadgroups method allows the transform to inform Direct2D how many thread groups are required, based on the size of the image and the transform's own knowledge of the shader.

The number of times the compute shader is executed is a product of the threadgroup counts specified here and the 'numthreads' annotation in the compute shader HLSL. For example, if the transform sets the threadgroup dimensions to be (2,2,1) the shader specifies (3,3,1) threads per threadgroup, then 4 threadgroups will be executed, each with 9 threads in them, for a total of 36 thread instances.

A common scenario is to process one output pixel for each instance of the compute shader. To calculate the number of thread groups for this scenario, the transform divides the width and height of the image by the respective x and y dimensions of the 'numthreads' annotation in the compute shader HLSL.

Importantly, if this division is performed, then the number of thread groups requested must always be rounded up to the nearest integer, otherwise the 'remainder' pixels will not be executed upon. If a shader (for example) computes a single pixel with each thread, the method's code would appear as follows.

IFACEMETHODIMP SampleTransform::CalculateThreadgroups(
_In_ const D2D1_RECT_L* pOutputRect,
_Out_ UINT32* pDimensionX,
_Out_ UINT32* pDimensionY,
_Out_ UINT32* pDimensionZ
)
{
// The input image's dimensions are divided by the corresponding number of threads in each// threadgroup. This is specified in the HLSL, and in this example is 24 for both the x and y// dimensions. Dividing the image dimensions by these values calculates the number of// thread groups that need to be executed.
*pDimensionX = static_cast<UINT32>(
ceil((m_inputRect.right - m_inputRect.left) / 24.0f);
*pDimensionY = static_cast<UINT32>(
ceil((m_inputRect.bottom - m_inputRect.top) / 24.0f);
// The z dimension is set to '1' in this example because the shader will// only be executed once for each pixel in the two-dimensional input image.// This value can be increased to perform additional executions for a given// input position.
*pDimensionZ = 1;
return S_OK;
}

The HLSL uses the following code to specify the number of threads in each thread group: