Shaders with HLSL #01 : Shaders with HLSL for WPF Developers

Complete Tutorial Series

In this first part of our tutorial, we will briefly enlighten ourselves on key concepts involving shaders and why it is required. We will also write our first shader application, introducing ourself to the HLSL semantics.

What is a Shader ?

Shaders are small programs written to run on the GPU (Graphical Processing Unit) and describes how to render each pixel. Since running parallely on the GPU hardware, they typically produces faster output,compared to having it run on the CPU.
There are mainly two types of Shaders

  • Vertex Shader – Vertex Shader gets the first chance to work on Model, prior to even Rasterizer and is responsible for transforming vertex.
  • Pixel Shader – Pixel Shader comes into pipeline a little later, after Rasterizer and is responsible for determining the final color of a pixel

WPF and Shaders

The huge computing power of modern GPUs ensures complex and graphically rich applications have little impact on performance. Begining with .Net 3.5 SP1, Microsoft introduced hardware accelerated affects for WPF (Effects in WPF, for example Blur Effect and DropShadowEffect) utilizing the power of shaders. We could write our own custom shaders and use them in our WPF applications.

Evolution of HLSL/C

One of the earliest approaches for writing Shaders used assembly language, but soon it was evident that the inherient complexity of Assembly Language was not making it any easier. This lead to development of Cg (C for Graphics) by NVida and HLSL (High Level Shader Language) by Microsoft. Both languages follows a similiar syntax and is closely associated to C Language in syntax and compile shaders for DirectX.

Getting Started with HLSL

Let’s write some code first and then we will go in detail examining the code as well as how to use it in your WPF Application.

sampler2D input: register(s0);

float4 main(float2 uv:TEXCOORD) : COLOR
{
return float4(1.0f, 0.0f, 0.0f, 1.0f);
}

The first line of code represent our input parameters, stored in GPU registers.

sampler2D input: register(s0);

Pixel Shaders rely on GPU registers input, which are high speed storage areas. There are different types of registers, but most of our applications written in C# depends on S and C register1 types. The S registers, also known as Sampler Registers are mainly used for storing input images data2. If we need to pass another Image Source, we could pass it via the register s1. We could pass other arguements to our shaders using the C registers (floating point registers), but for the sake of our first example, we will keep it simple with just a single input parameter, which is our Image data.

The first line of code specifies an input variable input and the register to use for the purpose. It also specifies the input type, which in this case is sampler2D and refers to the image it is being applied to.

The main as one can easily guess, represent the entry point of the shader code. The incoming parameter uv represents the incoming texture cordinates, while the return type float4 represents an RGBA color.

float4 main(float2 uv:TEXCOORD) : COLOR

The body of our first Shader application is pretty simple, it returns a color representing Red.

return float4(1.0f, 0.0f, 0.0f, 1.0f);

Let’s save the above code with extension “hlsl”. In the next part of tutorial we will examine how to compile our HLSL code to ps format, which would be later consumed by our WPF application.


  1. More on Registers : Source : Microsoft 
  2. Image, in this context refers to visual representation of the target control. 
Advertisements

Early Exceptions for Iterator and async Methods

One of the first things you would need to be vary of while working with Iterator methods and async methods is exception handling. Both Iterator methods and async methods delay the execution of your code, right until you need the result, but it also results in delayed exceptions.

Late Exception With Iterator Methods

Let’s consider a typical example. The following method generates positive integers from 1 to ‘n’. Of course, you could do this with Enumerable.Range but, that’s not the whole point.

void Main()
{
var maxValue = 0;
var generatedCollection = GenerateSequence(maxValue);
Console.WriteLine("No Exception raised yet");
foreach(var item in generatedCollection)
Console.WriteLine(item);
}

IEnumerable<int> GenerateSequence(int maxValue)
{
if(lastNumber <= 0)
throw new Exception($"Parameter {nameof(maxValue)} should be positive, non-zero");

for(int i=0;i<maxValue;i++)
yield return i;
}

The above method has a precondition which validates if the maxValue parameter is a positive, non-zero number. However if one was to execute the code, one can notice that exception isn’t raised until the result is consumed. This is expected. But, there could be scenarios where one might require you to have methods raise early exceptions.

Early Exception With Iterator Methods

Let’s rewrite the method with a little bit of tweak1.

void Main()
{
var maxValue = 0;
var generatedCollection = GenerateSequence(maxValue);
Console.WriteLine("No Exception raised yet");
foreach(var item in generatedCollection)
Console.WriteLine(item);
}

IEnumerable<int> GenerateSequence(int lastNumber)
{
if(lastNumber <= 0)
throw new Exception($"Parameter {nameof(lastNumber)} should be positive, non-zero");

return Generate();
IEnumerable<int> Generate()
{
for(int i=0;i<lastNumber;i++)
yield return i;
}
}

Try executing the code, and you can notice that now it supports early exception. This very approach could be used for async method. Let’s do the problematic code first.

Late Exception with async Methods

async Task<int> Increment(int numberToPrint)
{
if(numberToPrint <= 0)
throw new Exception($"Parameter {nameof(numberToPrint)} should be positive, non-zero");

return await Task.FromResult(++numberToPrint);
}

// Client code
var value = 0;
var task = Increment(value);
Console.WriteLine("No Exception raised yet");
await task;

The problem with late exceptions is visible in the above code as well. The exception is not raised where the method is called (in code), but rather it is raised when the Task is awaited.

Early Exception with async Methods

Task<int> Increment(int numberToPrint)
{
if(numberToPrint <= 0)
throw new Exception($"Parameter {nameof(numberToPrint)} should be positive, non-zero");

return Increment();
async Task<int> Increment()
{
return await Task.FromResult(++numberToPrint);
}
}

// Client Code
var value = 0;
var task = Increment(value);
Console.WriteLine("No Exception raised yet");
await task;

Reference


  1. Reference : More Effective C# : version 7.0 

Allocation free ‘async’ Methods

Task Asynchronous Programming (TAP) model will go down as one of the landmark of C# language revolution. The typical method signature with return type Task/Task<T> has since then made significant appearances in our programming life.

But despite all its glorious functionalities, it needs to be noted that it comes at a certain cost – performance bottleneck, especially when you are working in tight loops in a memory constrained environment. Memory allocations of Task/Task<T> (note both are references types) impacts the performances adversely in critical paths. What developers longed for in such scenarios is a comparatively light-weight value type, which could be returned instead of the references types in the async methods. This was a limitation till C# 7.x happened.

Starting from C# 7, an async method could return any type that has an accessible ‘GetAwaiter’ method, which returns an object that implements INotifyCompletion and ICriticalNotifyCompletion Interfaces. The ValueTask<T> is a "goto" under such circumstances. Being a value type, it doesn’t require additional memory allocations. This is extremely useful when you already have the ‘cached result’ and the operation can be completed synchronously. But like Task, it comes at a cost, which we will discuss later in the article.

To begin with, let’s write some sample code to demonstrate the need of ValueTask<T> or rather, need for replacing Task<T>. Consider the following code.

public interface IWeatherService
{
  Task<double> GetWeather();
}

public class MockWeatherService : IWeatherService 
{
  public async Task<double> GetWeather() => await Task.FromResult(30);
}

The above code demonstrate a Mock Weather Service. In real world scenario, this might be accessing a Web API to fetch current weather details. For the sake of example, only the temperature is fetched, which is hard coded. Consider GetWeather to be invoked quite frequently (say in a loop) by the Client. There is a chance this might turn disastrous if this was executed in a memory constraint environment.

Ideally, you wouldn’t want to fetch from the Web API always. You would prefer to cache the result, and only fetch from the Web API only after specific interval (from the time the last value was cached). Despite doing the operation (fetch from cache) synchronously, you are allocating memory for the reference type Task<T>. One would have wished to avoid this scenario and yet use the async syntax.

This is possible by making use of the ValueTask<T> DataType. Let’s change our syntax a bit.

public interface ICachedWeatherService
{
  ValueTask<double> GetWeather();
}
public class MockCachedWeatherService : ICachedWeatherService
{
  private DateTime _lastAccessedTime = DateTime.MinValue;
  private double _lastAccessedValue = 30;
  public async ValueTask<double> GetWeather()
  {
    if(DateTime.Now - _lastAccessedTime < TimeSpan.FromSeconds(10))
    {
      return _lastAccessedValue;
    }
    _lastAccessedTime = DateTime.Now;
    return await Task.FromResult(30);
   }
}

In the above code, we have changed the signature of the method (return type) to ValueTask<T>. Since it implements all the necessary requirements that is needed for async method (as discussed earlier), we could still use the same signature in client to access the method, just like in Task based syntax.

Using Task Return Type

var _weatherService = new MockWeatherService();
           
for(int i = 0; i < 10; i++)
{
  await Task.Delay(5000);
  var currentWeather = await _weatherService.GetWeather();
  Console.WriteLine($"Weather @ {DateTime.Now.ToString("hh:mm:ss")}={currentWeather}");
}

Using ValueTask Return Type

var _cachedWeatherService = new MockCachedWeatherService();
for (int i = 0; i < 10; i++)
{
  await Task.Delay(5000);
  var currentWeather = await _cachedWeatherService.GetWeather();
  Console.WriteLine($"Weather @ {DateTime.Now.ToString("hh:mm:ss")}={currentWeather}");
}

Check Complete Code Sample

Difference is, when the method needs to return from the cache, it returns ValueTask avoiding the pain of creating the Task Reference Type. However, when it has to go through the Web API, it can still return Task<T>.

Even though ValueTask seem highly useful, you need to maintain caution and should rather use it only if there is considerable performance issues that were measured. The reason for this is that, despite the ValueTask helps you avoid unnecessary allocation, it comes with its own cost. The ValueTask is a two field structure compared to Task<T>, which despite being reference type has single field. More fields would result in more data to copy. It fact Microsoft warns us in the API documentation itself.

Quoting from Documentation

There are tradeoffs to using a ValueTask<TResult> instead of a Task<TResult>. For example, while a ValueTask<TResult> can help avoid an allocation in the case where the successful result is available synchronously, it also contains two fields whereas a Task<TResult> as a reference type is a single field. This means that a method call ends up returning two fields worth of data instead of one, which is more data to copy. It also means that if a method that returns one of these is awaited within an async method, the state machine for that async method will be larger due to needing to store the struct that’s two fields instead of a single reference.

Hence, stick onto Task/Task<T> for almost all your use, until performance analysis pushes you to use ValueTask<T>. Complete code samples described in the example is available in my Github

Updated Span API : NonPortableCast and AsReadOnlySpan to MemoryMarshal.Cast and AsSpan

One of the interesting changes (not sure why Microsoft changed this) was moving/changing the Span<T> API’s a bit. For example, the Span<T>.NonPortableCast Method was moved as an extension method under MemoryMarshal.

Previous API

Span<byte> byteSpan = BitConverter.GetBytes(originalPrimitiveType);
Span<int> intSpan = byteSpan.NonPortableCast<byte, int>();

Current API

Span<byte> byteSpan = BitConverter.GetBytes(originalPrimitiveType);
Span<int> intSpan = MemoryMarshal.Cast<byte, int>(byteSpan);

Similarly, the string.AsReadonlySpan method has been renamed. The new API is as follows.

Previous API

ReadOnlySpan<char> keyString = "SomeText".AsReadOnlySpan();

Current API

ReadOnlySpan<char> keyString = "SomeText".AsSpan();

The MemoryMarshal extensions can be found under the System.Runtime.InteropServices namespace. Code discussed in my earlier post about Benchmarking Span has been updated in my Github.

The new String Interpolation and Ternary Operator

A quick tip for the new C# string interpolation. The new string interpolation has made things more simpler and readable. One might be tempted to include ternary operators within string interpolation.

However if you attempt something like following, the compiler would throw an error

Console.WriteLine($"{1==1?"Msg1":"Msg2"}");

The fix is as simple as it can get. Just include a brace within your ternary statement.

Console.WriteLine($"{(1==1?"Msg1":"Msg2")}");

That’s it. Brace up for a week with lot of fun and coding.

Deconstruct and Extension Methods

In an earlier post, we explored the Deconstruct feature for Non-Tuples. It was a pretty useful feature that came along in the 7.x series . If one does have access to source, adding deconstruct is quite easy. But what about classes we do not have (source code) access to ? For example, the .Net framework classes ? Can we add a deconstruct to it ? It turns out, it is quite simple as well. Extension Methods allows us to add deconstruct to existing classes without breaking the Open Closed Principle.

For demonstration purpose, let us write a deconstruct for Point Class under the System.Drawing namespace.

public static class ExtensionMethods
{
    public static void Deconstruct(this Point source,out double x,out double y)
    {
        x = source.X;
        y = source.Y;
    }
}

void Main()
{
       Point point = new Point(100,500);
       var (x,y) = point;
       Console.WriteLine($"Point.X={x},Point.Y={y}");
}

As observed, it is quite easy to add deconstruct to your existing classes using the Extension Methods.

Caliburn.Micro #007 : Bootstrapper with Unity

Unity is one of the most commonly used IOC containers used by developers, and it would be unfair if I don’t include it as a part of the Caliburn.Micro WPF Tutorials (Creating Bootstrappers).  This particular post is dedicated to building bootstrapper for Caliburn.Micro application using Unity as the IoC Container.


The key for creating Bootstrappers for Caliburn.Micro application lies in overriding 4 methods from the BootstrapperBase class. The 4 methods are
  1. Configure
  2. BuildUp
  3. GetInstance
  4. GetAllInstance
The implementation using SimpleContainer and MEF is described in the earlier parts of the Caliburn.Micro Tutorials. Let’s go ahead and implement the Configure method using Unity.
private IUnityContainer _unityContainer;
protected override void Configure()
{
    _unityContainer = new UnityContainer();
    _unityContainer.RegisterInstance<IWindowManager>(new WindowManager());
    _unityContainer.RegisterInstance<IEventAggregator>(new EventAggregator(), new ContainerControlledLifetimeManager());
    //  View Models
    _unityContainer.RegisterInstance<IShellViewModel>(new ShellViewModel());
}
Container using Unity is initialized using the UnityContainer class. RegisterInstance methods are used to register your Classes. For cases, where you need Singletons, you can make use of the ContainerControlledLifeTimeManager as seen in the above example. This place will also hold your ViewModel registrations as well.


Rest of the 3 methods are pretty simple and straightforward to understand.
protected override void BuildUp(object instance)
{
  _unityContainer.BuildUp(instance);
  base.BuildUp(instance);
}
protected override object GetInstance(Type service, string key)
{
  return string.IsNullOrEmpty(key) ? _unityContainer.Resolve(service, key) : _unityContainer.Resolve(service);
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
  return _unityContainer.ResolveAll(service);
}
Finally, similar to earlier Bootstrappers, we use the OnStartup Method to invoke the ShellViewModel.

protected override void OnStartup(object sender, StartupEventArgs e)
{
   DisplayRootViewFor<IShellViewModel>();
}

The complete code sample can be accessed at my Github. The bootstrapper template is also included as a part of Caliburn.Micro Template Pack for WPF.