Thursday, December 31, 2009

Indexed drawing

I was getting annoyed with having to cast Math.PI which is double to float32 every time. F#, like C# 3.0 allows you to extend existing types by adding your own functionality. You don't have access to any of the internals, but you can add methods and properties that access the public interface. Here is my extension to System.Math:

module Flow.TypeExtensions
open System

type System.Math with
static member Pi = float32(Math.PI)
static member PiOver2 = Math.Pi/2.0f
static member TwoPi = Math.Pi*2.0f


In this tutorial we are drawing a cube. Since a cube requires more vertices than a single triangle and many of them will be the same, we are going to use an index buffer to minimize the amount of vertices. We still need 4 vertices per face since the normals are unique per face. Still, 4 is better than 6. We create a new class type called Shape which will hold our vertices and indices. The helper function Shape.Box will create our cube by contructing a single face and rotating it to form each of the sides.

type Shape = {
Vertices : array< Vertex >
Indices : array< int32 >
}
with
//-------------------------------------------------------------------------
static member Box =
let vertices =
let normal = Vector3( 0.0f, 0.0f,-1.0f)
let face = [|
Vector3(-1.0f, 1.0f,-1.0f);
Vector3( 1.0f,-1.0f,-1.0f);
Vector3(-1.0f,-1.0f,-1.0f);
Vector3( 1.0f, 1.0f,-1.0f)
|]

let rotatedFace rotation =
let vertices = Vector3.TransformCoordinate(face, ref rotation)
let normal = Vector3.TransformCoordinate(normal, rotation)
Array.map (fun vec -> Vertex(vec, normal)) vertices

let aboutY angle = Matrix.RotationY(angle)
let aboutX angle = Matrix.RotationX(angle)

// Create the sides by rotating the front face 4 times about the Y axis
let rec sides(angle, acc) =
if (angle < Math.TwoPi) then
sides(angle+Math.PiOver2, Array.append acc (rotatedFace(aboutY angle)))
else
acc
// Create the top and bottom by rotating the front face 90 and
// -90 degrees about the X axis
let top, bottom = rotatedFace(aboutX Math.PiOver2), rotatedFace(aboutX -Math.PiOver2)
Array.concat ( seq { yield sides(0.0f, [||])
yield top
yield bottom } )

let indices =
// Create indices by incrementing each element of the first face by
// the base offset of the face. This will ensure the order is consistent
// so all the triangles are front facing.
let nextIndices indices increment =
Array.map (fun x -> x+increment) indices

let frontIndices = [|0;1;2;0;3;1|]
let increments = [|for i in [0..5] do yield i*4|]

Array.collect (nextIndices frontIndices) increments
{ Vertices = vertices; Indices = indices }

Our Geometry object will now have an index buffer.

type Geometry(device, shape) =
...
let indexBufferDesc = new BufferDescription(
BindFlags = BindFlags.IndexBuffer,
SizeInBytes = shape.Indices.Length*sizeof< uint32 >,
Usage = ResourceUsage.Immutable)
let indexBuffer = new SlimDX.Direct3D11.Buffer(
device,
new DataStream(shape.Indices, true, false),
indexBufferDesc)

When preparing this geometry we set the index buffer. When drawing we now have to use DrawIndexed instead of the normal Draw call.

member this.Prepare(transforms) =
...
do deviceContext.InputAssembler.SetIndexBuffer(indexBuffer, Format.R32_UInt, 0)

member this.Draw() =
deviceContext.DrawIndexed(shape.Indices.Length, 0, 0)


That pretty much it for this tutorial. As usual the full source code can be found on Google Code

Let there be light

Our previous example was pretty boring since we always used the same color. To spice up the realism we need to add some color and light. To allow different colors we need to add constants that the pixel shader can use. We also need to add normals to our vertices and a light direction vector so we can shade our triangle based on how much it is oriented towards the light. We will also rotate the triangle at a constant velocity so we can see the effect of the light.

Lets look at the changes in the shaders first. We added a constant buffer storing a diffuse color. This will be used by our pixel shader.

cbuffer psMain
{
float4 DiffuseColor;
};

In addition to the WorldViewProjection matrix we now also need the World matrix. We use this to transform the normal to world space in order to calculate the angle between the light direction and the normal.

cbuffer vsMain
{
row_major matrix World;
row_major matrix WorldViewProjection;
};

Our Vertex shader now expects a normal in addition to the position for each vertex. It will transform the normal and pass it along to the pixel shader for the lighting calculation.

struct VSInput
{
float4 Position : POSITION;
float3 Normal : NORMAL;
};

struct VSOutput
{
float4 Position : SV_POSITION;
float3 Normal : NORMAL;
};
When calculating the Normal in world space we are not interested in its translation. Our light is a directional light and just described by a direction vector without a position. We therefor have to transform our normal only by the rotation in order for both vectors to be at the origin. Fortunately, that is exactly what happens when you multiply a vector3 with a matrix4x4. The last component of the vector is implicitly zero which effectively cancels any translation component the matrix may have had.

VSOutput VertShader(VSInput input )
{
VSOutput Out;
Out.Position = mul( input.Position, WorldViewProjection );
Out.Normal = mul( input.Normal, World );
return Out;
}

In the pixel shader we hard code the light direction for now. This should really be a pixel shader constant in order to change the light dynamically. The amount of light for the pixel is dot product between the normal and the light direction. We multiply that value by our diffuse color constant to get our final color. Later we will add some more properties like specular and ambient color.

float4 PixShader( VSOutput In ) : SV_TARGET
{
float3 LightDirection = float3(0.0f, 0.0f, 1.0f);
return float4(DiffuseColor.rgb*(dot(In.Normal, -LightDirection)), DiffuseColor.a);
}

The Geometry, Material and ShaderConstants types now reside in their own files.
For Geometry we now need to specify that our vertices consist of a position and a normal. A vertex is now defined as:

#nowarn "9"
[<Struct; StructLayout(LayoutKind.Sequential)>]
type Vertex(position : Vector3, normal : Vector3) =
member this.Position = position
member this.Normal = normal

Note that when describing our layout, we have to be explicit about the offset of the normal in relation to the position. Since our structure layout is defined as sequential the normal will start immediately after the position.

let inputLayout =
let position = InputElement(
SemanticName = "POSITION",
Format = Format.R32G32B32_Float,
Classification = InputClassification.PerVertexData)
let normal = InputElement(
SemanticName = "NORMAL",
Format = Format.R32G32B32_Float,
AlignedByteOffset = sizeof,
Classification = InputClassification.PerVertexData)
new InputLayout(
device,
ShaderSignature.GetInputSignature(vsByteCode),
[|position; normal|])

Our previous example only had a single set of vertex shader constants that represented only the world view projection transformation in a single matrix. We now also have a second set of constants associated with the pixel shader. I have moved the VS constants to be updated together with the geometry while the PS constants are updated together with the material.
Since we now have more than one VS constant we need to specify the structure.

#nowarn "9"
[<Struct; StructLayout(LayoutKind.Sequential)>]
type Transforms(world : Matrix,
worldViewProjection : Matrix) =
member this.World = world
member this.WorldViewProjection = worldViewProjection

Our PS constants only contains the diffuse color for now.

#nowarn "9"
[<Struct; StructLayout(LayoutKind.Sequential)>]
type MaterialProperties(diffuseColor : Color4) =
member this.DiffuseColor = diffuseColor

In our main application we now have to specify the normals for each of the vertices in our triangle. We also give the material color when asking the renderer to create a drawable object.

let triangleVertices = [|
Vertex(Vector3(-1.0f,0.0f,0.0f), Vector3(0.0f,0.0f,-1.0f));
Vertex(Vector3(0.0f,1.0f,0.0f), Vector3(0.0f,0.0f,-1.0f));
Vertex(Vector3(1.0f, 0.0f, 0.0f), Vector3(0.0f,0.0f,-1.0f))|]

let green = new Color4(Red = 0.0f, Green = 1.0f, Blue = 0.0f, Alpha = 1.0f)
let triangle = renderer.CreateDrawable(triangleVertices, MaterialProperties(green))

The other interesting change in this iteration is our main loop. In the previous examples we relied on the Paint event to signal when we should draw. It turns out this event is only sent when the form thinks it should be updated. Usually only the first time the window is shown or if it is minimized and then maximized. To make our triangle move, we need to draw several times per second. Our main loop now looks like this:

let OnApplicationIdle _ =
while (Windows.AppStillIdle()) do
...
// as before
...
let rotationSpeed = float32(Math.PI/2.0) // per second
let elapsedSeconds = float32(timer.ElapsedMilliseconds)/1000.0f;
let world = Matrix.RotationY(rotationSpeed*elapsedSeconds)
let transforms = Transforms(world, world*view*projection)
renderer.Draw(triangle, transforms)

do Application.Idle.Add(OnApplicationIdle)
do Application.Run(form)

We only update while our application is 'idle' As soon as any windows event happens we yield control to our window. The Windows.AppStillIdle() is a native method that does a PeekMessage() to see if there is anything on the event queue. When the app is done handling the event we will receive another idle event and we continue spinning our green triangle.

Tuesday, December 29, 2009

Drawing a triangle

For my next trick, I am going to show you how to draw a triangle. It turns out there is quite a lot of water that needs to flow underneath the bridge before we can achieve this seemingly simple feat. To start off, I moved everything related to DirectX and graphics into a separate file called Renderer under a new namespace called Flow.Graphics. This file contains several new types/classes that will help us draw things to our render window. Our new main program is now much shorter. I'll start with the new main method and then delve deeper into each of the new classes in Renderer.


open System
open System.Windows.Forms

open SlimDX
open Flow.Graphics

//---------------------------------------------------------------------------------------
[<STAThread>]
[<EntryPoint>]
let main(args) =
let width, height = 400, 300
let form = new Form(Text = "Blue skies", Width = width, Height = height)
let renderer = new Renderer(form.Handle, width, height)
let triangleVertices = [|
Vector3(-1.0f,0.0f,0.0f);
Vector3(0.0f,1.0f,0.0f);
Vector3(1.0f, 0.0f, 0.0f)|]
let triangle = renderer.CreateDrawable(triangleVertices)

let paint _ =
let eye, lookAt, up =
Vector3(0.0f,0.0f,-5.0f),
Vector3.Zero,
Vector3(0.0f,1.0f,0.0f)
// Look at the origin
let view = Matrix.LookAtLH(eye, lookAt, up)
// Place our triangle at the origin
let world = Matrix.Identity
let fovy, aspect, near, far =
float32(Math.PI)/4.0f,
float32(width)/float32(height),
1.0f,
20.0f
let projection = Matrix.PerspectiveFovLH(fovy, aspect, near, far)

renderer.Draw(triangle, world*view*projection)

do form.Paint.Add(paint)
do Application.Run(form)
for item in ObjectTable.Objects do
item.Dispose()
0


Most of the action happens in Renderer. We ask it to create a drawable object with the supplied vertices. This list represents the three corners of our triangle. During the 'paint' callback we calculate a world, view and projection matrix in order to transform our triangle from world space to screen space. We then ask the renderer to draw our triangle with the given transformation.


type Renderer(windowHandle, width, height) =
...
// Create device,swap chain and rendertarget view as before
...
//Create a viewport with the same size as the window
let viewport =
new Viewport(
Width = float32(width),
Height = float32(height),
MinZ = 0.0f,
MaxZ = 1.0f,
X = 0.0f,
Y = 0.0f)
let deviceContext = device.ImmediateContext

do deviceContext.OutputMerger.SetTargets([|renderTargetView|] )
do deviceContext.Rasterizer.SetViewports([|viewport|])

member this.CreateDrawable(triangle) = new Drawable(device, triangle)
member this.Draw(drawable : Drawable, transform) =
do device.ImmediateContext.ClearRenderTargetView(
renderTargetView,
new Color4(Alpha = 1.0f, Red = 0.0f, Green = 0.0f, Blue = 1.0f))
do drawable.Draw(transform)
do swapChain.Present(0, PresentFlags.None) |> ignore


Now that we are going to draw something in our window we need to tell create a viewport to describe which part of the window to use. We will use the entire window. The draw method looks similiar to our previous 'paint' callback, except that we now actually draw something between clearing the back buffer and switching to the front. This is our triangle object that was created with CreateDrawable(...) Let's look at this class.

type Drawable(device, triangle) =
let geometry = new Geometry(device, triangle)
let material = new Material(device)
let constants = new ShaderConstants(device)

member this.Draw(transform) =
constants.Prepare(transform)
material.Prepare()
geometry.Prepare()
geometry.Draw()

The Drawable class serves as a container for all the components that is needed to draw something to the screen. The Geometry describes what needs to be drawn. All the vertex data resides here. A Material describes what an object's surface should look like. The constants are additional data needed to draw an object. In our example, we need a transformation matrix in order to know where to draw the geometry. This typically changes every frame so we pass in this information in our draw call. Before drawing the geometry, the device context needs to be prepared by telling it which vertex and pixels shaders to use and to copy the constants associated with this object to the GPU. This is done in the prepare methods of our constants, material and geometry objects.

The Material in this example is extremely simple. It creates a pixel shader found in the Simple.hlsl file. When the time comes to prepare the renderer with this material, it sets the pixel shader for the device context to this shader.

type Material(device) =
let pixelShader =
let psByteCode = ShaderBytecode.CompileFromFile(
"Simple.hlsl",
"PixShader",
"ps_4_0",
ShaderFlags.None,
EffectFlags.None)
new PixelShader(device, psByteCode)
member this.Prepare() =
do device.ImmediateContext.PixelShader.Set(pixelShader)


The pixel shader will always draw the object in red. We will create some more interesting materials in the near future...

float4 PixShader(float4 position : SV_POSITION ) : SV_TARGET
{
return float4(1,0,0,1); //RED
}


-To setup our geometry we compile the vertex shader found in the same file as our pixel shader.
-We have to specify an input layout so the GPU knows how to interpret the vertex data. For now we are just using a Vector3 to represent a vertex position. In the future we will expand on this by adding normal and texture coordinates to our vertices.
- Next we create a vertex buffer which will copy our vertices to the GPU. Note that this buffer is immutable. We will not be able to change these vertices again.
- When preparing to draw this geometry, we need to specify the input layout, the primitive topology(triangle list in this case), our vertex buffer and our vertex shader. This could change between different objects so we have to update these states before drawing.
- When drawing we need to tell the renderer how many vertices to draw.

type Geometry(device, vertices:array) =
let vsByteCode = ShaderBytecode.CompileFromFile(
"Simple.hlsl",
"VertShader",
"vs_4_0",
ShaderFlags.None,
EffectFlags.None)

let vertexShader = new VertexShader(device, vsByteCode)
let inputLayout =
let position = new InputElement(
SemanticName = "POSITION",
Format = Format.R32G32B32_Float,
Classification = InputClassification.PerVertexData)
new InputLayout(
device,
ShaderSignature.GetInputSignature(vsByteCode),
[|position|])
let vertexSize = sizeof
let vertexBuffer =
let vertexBufferDesc = new BufferDescription(
BindFlags = BindFlags.VertexBuffer,
SizeInBytes = vertexSize*vertices.Length,
Usage = ResourceUsage.Immutable)
new SlimDX.Direct3D11.Buffer(device,
new DataStream(vertices, true, false),
vertexBufferDesc)

let deviceContext = device.ImmediateContext
member this.Prepare() =
do deviceContext.InputAssembler.PrimitiveTopology <-
PrimitiveTopology.TriangleList
do deviceContext.InputAssembler.InputLayout <- inputLayout
do deviceContext.VertexShader.Set(vertexShader)
do deviceContext.InputAssembler.SetVertexBuffers(
0, new VertexBufferBinding(vertexBuffer, vertexSize, 0))

member this.Draw() =
deviceContext.Draw(vertices.Length, 0)

Our vertex shader is also very simple. It simply transforms a vertex from world space to screen space by multiplying with the given transformation matrix.

float4 VertShader(float4 position : POSITION ) : SV_POSITION
{
return mul( position, WorldViewProjection );
}


Finally we have our shader constants. This will be used to update our transformation matrix each frame.

type ShaderConstants(device) =
let vsConstBuffer =
let vsConstSize = sizeof
let vsConstBufferDesc = new BufferDescription(
BindFlags = BindFlags.ConstantBuffer,
SizeInBytes = vsConstSize,
CpuAccessFlags = CpuAccessFlags.Write,
Usage = ResourceUsage.Dynamic)
new SlimDX.Direct3D11.Buffer( device, vsConstBufferDesc )

let deviceContext = device.ImmediateContext
let updateShaderConstants constBuffer sizeInBytes data =
let constData = deviceContext.MapSubresource(
constBuffer,
0,
sizeInBytes,
MapMode.WriteDiscard,
SlimDX.Direct3D11.MapFlags.None)
Marshal.StructureToPtr( data, constData.Data.DataPointer, false )
deviceContext.UnmapSubresource( constBuffer, 0 )

member this.Prepare( worldViewProjection ) =
updateShaderConstants vsConstBuffer sizeof worldViewProjection
do deviceContext.VertexShader.SetConstantBuffers([|vsConstBuffer|], 0, 1)

The buffer is created similar to the VertexBuffer. In this case though we specified that it is dynamic since we want to update this buffer later. When we prepare the shader constants, we copy the new transformation matrix directly to the mapped region reserved for our constant data. The layout needs to exactly map to the layout described in HLSL below. In this case though we only have one element so it is trivial.

cbuffer vsMain
{
row_major matrix WorldViewProjection;
};

Wow, what a lot of work to draw a triangle!
The full sourcecode can be found on GoogleCode. I use Mercurial for version control. Why? Mostly because I have always wanted to experiment with distributed version control and at the moment I have the time to do it. If you are used to SVN and Tortoise, you will find that TortoiseHg is very similiar. So far I am enjoying Mercurial. The main benefit for me at the moment is that I can make local commits. I like to commit often to give myself a safe place to revert to. The code may not be in a state that I want to commit it to the server yet, so I just commit it locally. Even when my internet is down, I can still commit. Later when everything works and I want others to benefit from from my changes, I push all my commits to the server. It is pretty painless.

Monday, December 28, 2009

Would you look at that sky, talk about blue...

I have been interested in functional programming lately and decided to start a blog in an attempt to document my experience in learning an exciting new language called F#. Actually it is not all that new. It has been in development for several years now but will only officially ship with Visual Studio 2010 as a first class language for the .NET CLR.

The other new technology that I am very interested in is DirectX 11. Since both the hardware and the software are very new, there are precious few tutorials or books out there on how to get started. While I am certainly no authority on either F# or DirectX, I hope my learning experience will help give others a head start.

While XNA already provides an excellent way for .NET programmers to play with game programming and computer graphics, it only supports DirectX 9 and Shader Model 3. Compute Shaders in DirectX 11 allows us to write more general purpose code on the GPU. Things like game physics and AI can now be calculated on the GPU without having to jump through all kinds of hoops.

In the absence of XNA there are two options to create DirectX applications for .NET.
- Windows API Code Pack
- SlimDX
I have chosen the latter since it provides access to several other APIs like DirectInput in addition to the graphics wrappers.

I hope to create a series of tutorials over the next couple of weeks. The first will focus on how to get up and running with DirectX 11 and highlight a couple of cool F# features along the way. Today we will create a basic render window that renders a blue screen. In order for this example to work, you will need a DirectX 11 graphics card. At the moment the ATI Radeon 5800 series are the only cards that support this.

Setting up your project
-Create a new F# Application. In Visual Studio 2010 Beta 2 this will be a console application by default. Change the Ouput type to 'Windows Application' under Properties->Application tab.
- Download SlimDX and reference the assembly in your F# project.
- Add references to System.Windows.Forms and System.Drawing
- Set the target framework to .NET 4.0 (Not the client framework)

The code
To prevent having to write the fully qualified class names we will use a couple of import declarations. In F# we use the 'open' keyword.

open System
open System.Windows.Forms
open System.Drawing

open SlimDX
open SlimDX.Direct3D11
open SlimDX.DXGI

The first thing we will need is a device to access the graphics hardware. We will also need a swap chain which will allow us to present the front buffer after drawing a frame.

let deviceAndSwapChain windowHandle width height =
let modeDescription = new ModeDescription(
Width = width,
Height = height,
Format = Format.R8G8B8A8_UNorm,
RefreshRate = new Rational(60,1))

let swapChainDescription = new SwapChainDescription(
BufferCount = 1,
ModeDescription = modeDescription,
Usage = Usage.RenderTargetOutput,
OutputHandle = windowHandle,
SampleDescription = new SampleDescription(Count = 1),
IsWindowed = true,
SwapEffect = SwapEffect.Discard)

let _, device, swapChain = Device.CreateWithSwapChain(
null,
DriverType.Hardware,
DeviceCreationFlags.None,
swapChainDescription )
device, swapChain

There are a couple of interesting things about this first function.
- We defined a function that takes three parameters: a window handle, the screen width and the screen height. Yet we did not specify any types. One of F#'s strengths is that even though it is a statically typed language, you rarely need to specify any types. Most of the time they can be inferred by the compiler. This removes a lot of the clutter that you are forced to write in other languages like C# or C++ without sacrificing type safety.
- The arguments to a function can be separated by spaces. This is by personal choice. It could have been written as:

let deviceAndSwapChain(windowHandle, width, height)

The first form also allows you to do a funky thing called currying, which I will not get into now.
- In F# there are no need for curly braces. Statement blocks are implied by the tabbing.
- Note how we can initialize the ModeDescription and SwapChainDescription structures by specifying the values in what looks like the constructor, even though there is no such constructor. This is some F# syntactical sugar to avoid the much clumsier classical way of initializing a struct:

let deviceAndSwapChain windowHandle width height =
let modeDescription = new ModeDescription()
modeDescription.Width <- width
modeDescription.Height <- height
modeDescription.Format <- Format.R8G8B8A8_UNorm
modeDescription.RefreshRate <- new Rational(60,1)

- There is no 'return' statement at the end of the function definition. It is implicit.
- We can return multiple values. These are wrapped as a tuple.

Next up is our main function. We have to apply the EntryPoint attribute to indicate that this is where the application should start. We first create our form in order to get a valid window handle. We then use this handle with the screen dimensions to create our device and swap chain.

[<STAThread>]
[<EntryPoint>]
let main(args) =
let width, height = 800, 600
let form = new Form(Text = "Flow Edit", Width = width, Height = height)
let device, swapChain = deviceAndSwapChain form.Handle width height

We need a RenderTargetView in order to gain access the back buffer. This will be cleared at the start of each frame during the the callback to 'paint'. We register to the Paint event by adding our own handler that clears the back buffer and flips it using the swap chain. Note that since we do not use the PaintEventArgs in the callback, we simply name the argument with an underscore, which happens to be a valid identifier in F#. The struct initialization syntax again comes in handy. This time there is actually a constructor available that takes 4 float arguments. When inspecting the code however, new Color4(1.0f,0.0f,0.0f,1.0f) would be a bit ambiguous. Is this ARGB (blue) or RGBA (red)? By providing named parameters we avoid any confusion.

let renderTargetView =
use renderTarget = Resource.FromSwapChain(swapChain, 0)
new RenderTargetView(device, renderTarget)

let paint _ =
do device.ImmediateContext.ClearRenderTargetView(
renderTargetView,
new Color4(Alpha = 1.0f, Red = 0.0f, Green = 0.0f, Blue = 1.0f))
do swapChain.Present(0, PresentFlags.None) |> ignore

form.Paint.Add(paint)

When we run our form, lo and behold, we have a window rendering a glorious blue sky. ;-)

do Application.Run(form)


When the form is closed, we need to dispose of any SlimDX objects. For this simple example, I just rely on the ObjectTable to make sure that all objects are disposed.

for item in ObjectTable.Objects do
item.Dispose()
0

Below is the full source code listing. You can find the Visual Studio 2010 solution at Google Code

open System
open System.Windows.Forms
open System.Drawing

open SlimDX
open SlimDX.Direct3D11
open SlimDX.DXGI

let deviceAndSwapChain windowHandle width height =
let modeDescription = new ModeDescription(
Width = width,
Height = height,
Format = Format.R8G8B8A8_UNorm,
RefreshRate = new Rational(60,1))

let swapChainDescription = new SwapChainDescription(
BufferCount = 1,
ModeDescription = modeDescription,
Usage = Usage.RenderTargetOutput,
OutputHandle = windowHandle,
SampleDescription = new SampleDescription(Count = 1),
IsWindowed = true,
SwapEffect = SwapEffect.Discard)

let _, device, swapChain = Device.CreateWithSwapChain(
null,
DriverType.Hardware,
DeviceCreationFlags.None,
swapChainDescription )
device, swapChain

//-----------------------------------------------------------------------------------
[<STAThread>]
[<EntryPoint>]
let main(args) =
let width, height = 400, 300
let form = new Form(Text = "Blue skies", Width = width, Height = height)
let device, swapChain = deviceAndSwapChain form.Handle width height
let renderTargetView =
use renderTarget = Resource.FromSwapChain(swapChain, 0)
new RenderTargetView(device, renderTarget)

let paint _ =
do device.ImmediateContext.ClearRenderTargetView(
renderTargetView,
new Color4(Alpha = 1.0f, Red = 0.0f, Green = 0.0f, Blue = 1.0f))
do swapChain.Present(0, PresentFlags.None) |> ignore

form.Paint.Add(paint)

do Application.Run(form)

for item in ObjectTable.Objects do
item.Dispose()
0