Esempio n. 1
0
        public override void Run()
        {
            #region Create renderers

            // Note: the renderers take care of creating their own
            // device resources and listen for DeviceManager.OnInitialize

            List <string[]> ipShaders = new List <string[]>();
            ipShaders.Add(new[] { "DesaturateCS" });
            ipShaders.Add(new[] { "SaturateCS" });
            ipShaders.Add(new[] { "NegativeCS" });
            ipShaders.Add(new[] { "ContrastCS" });
            ipShaders.Add(new[] { "BrightnessCS" });
            ipShaders.Add(new[] { "SepiaCS" });
            ipShaders.Add(new[] { "BoxFilter3TapHorizontalCS", "BoxFilter3TapVerticalCS" });
            ipShaders.Add(new[] { "BoxFilter5TapHorizontalCS", "BoxFilter5TapVerticalCS" });
            ipShaders.Add(new[] { "BlurFilterHorizontalCS", "BlurFilterVerticalCS" });
            ipShaders.Add(new[] { "SobelEdgeCS" });
            ipShaders.Add(new[] { "ApproxMedianHorizontalCS", "ApproxMedianVerticalCS" });
            ipShaders.Add(new[] { "Median3x3TapSinglePassCS" });
            ipShaders.Add(new[] { "SobelEdgeOverlayCS" });
            int shaderIndex = 0;

            string[] images = new[] {
                "rendered scene",
                "Village.png",
                "Grass.jpg",
                "Sun.jpg",
                "Sand1.jpg",
                "Sand2.jpg",
                "QT1.jpg",
                "QT2.jpg",
                "QT3.jpg"
            };
            var imageIndex = 1;

            var ip32x32 = ToDispose(new ImageProcessingCS());
            ip32x32.ThreadsX = 32;
            ip32x32.ThreadsY = 32;
            ip32x32.LoadSourceImage(images[imageIndex]);
            ip32x32.Initialize(this);

            var ip16x4 = ToDispose(new ImageProcessingCS());
            ip16x4.ThreadsX = 16;
            ip16x4.ThreadsY = 4;
            ip16x4.LoadSourceImage(images[imageIndex]);
            ip16x4.Initialize(this);

            var ip32x4 = ToDispose(new ImageProcessingCS());
            ip32x4.ThreadsX = 32;
            ip32x4.ThreadsY = 4;
            ip32x4.LoadSourceImage(images[imageIndex]);
            ip32x4.Initialize(this);

            var ip128x4 = ToDispose(new ImageProcessingCS());
            ip128x4.ThreadsX = 128;
            ip128x4.ThreadsY = 4;
            ip128x4.LoadSourceImage(images[imageIndex]);
            ip128x4.Initialize(this);

            var screenAlignedQuad = ToDispose(new ScreenAlignedQuadRenderer());
            screenAlignedQuad.Initialize(this);

            // Create a axis-grid renderer
            var axisGrid = ToDispose(new AxisGridRenderer());
            axisGrid.Initialize(this);

            // Create and initialize the mesh renderer
            var loadedMesh             = Common.Mesh.LoadFromFile("cartoon_village.cmo");
            List <MeshRenderer> meshes = new List <MeshRenderer>();
            meshes.AddRange((from mesh in loadedMesh
                             select ToDispose(new MeshRenderer(mesh))));
            foreach (var m in meshes)
            {
                m.Initialize(this);
                m.World = Matrix.Identity;
            }

            // Set the first animation as the current animation and start clock
            foreach (var m in meshes)
            {
                if (m.Mesh.Animations != null && m.Mesh.Animations.Any())
                {
                    m.CurrentAnimation = m.Mesh.Animations.First().Value;
                }
                m.Clock.Start();
            }

            // Create and initialize a Direct2D FPS text renderer
            var fps = ToDispose(new Common.FpsRenderer("Calibri", Color.CornflowerBlue, new Point(8, 8), 16));
            fps.Initialize(this);

            // Create and initialize a general purpose Direct2D text renderer
            // This will display some instructions and the current view and rotation offsets
            var textRenderer = ToDispose(new Common.TextRenderer("Calibri", Color.CornflowerBlue, new Point(8, 40), 12));
            textRenderer.Initialize(this);

            #endregion

            // Initialize the world matrix
            var worldMatrix = Matrix.Identity;

            // Set the camera position
            var cameraPosition = new Vector3(0, 0, 2);
            var cameraTarget   = Vector3.Zero;  // Looking at the origin 0,0,0
            var cameraUp       = Vector3.UnitY; // Y+ is Up

            // Prepare matrices
            // Create the view matrix from our camera position, look target and up direction
            var viewMatrix = Matrix.LookAtRH(cameraPosition, cameraTarget, cameraUp);
            viewMatrix.TranslationVector += new Vector3(0, -0.98f, 0);

            // Create the projection matrix
            /* FoV 60degrees = Pi/3 radians */
            // Aspect ratio (based on window size), Near clip, Far clip
            var projectionMatrix = Matrix.PerspectiveFovRH((float)Math.PI / 3f, Width / (float)Height, 0.5f, 100f);

            // Maintain the correct aspect ratio on resize
            Window.Resize += (s, e) =>
            {
                projectionMatrix = Matrix.PerspectiveFovRH((float)Math.PI / 3f, Width / (float)Height, 0.5f, 100f);
            };

            #region Rotation and window event handlers

            // Create a rotation vector to keep track of the rotation
            // around each of the axes
            var rotation = new Vector3(0.0f, 0.0f, 0.0f);
            var lerpT    = 1.0f;
            // We will call this action to update text
            // for the text renderer
            Action updateText = () =>
            {
                string shaders = String.Empty;
                foreach (var s in ipShaders[shaderIndex])
                {
                    shaders += s + " ";
                }
                textRenderer.Text =
                    String.Format("Rotation ({0}) (Up/Down Left/Right Wheel+-)\nView ({1}) (A/D, W/S, Shift+Wheel+-)"
                                  + "\nShader {4}(PgUp/PgDn to change)"
                                  + "\nImage: {3} (Ctrl +/- to change)"
                                  + "\nLerp (t) = {2:#0.00} (+/- to change)"
                                  //+ "\nPress Z to show/hide depth buffer - Press F to toggle wireframe"
                                  //+ "\nPress 1-8 to switch shaders"
                                  , rotation,
                                  viewMatrix.TranslationVector,
                                  lerpT,
                                  images[imageIndex],
                                  shaders
                                  );
            };

            Dictionary <Keys, bool> keyToggles = new Dictionary <Keys, bool>();
            keyToggles[Keys.Z] = false;
            keyToggles[Keys.F] = false;

            // Support keyboard/mouse input to rotate or move camera view
            var moveFactor = 0.02f; // how much to change on each keypress
            var shiftKey   = false;
            var ctrlKey    = false;
            var background = Color.White;
            Window.KeyDown += (s, e) =>
            {
                var context = DeviceManager.Direct3DContext;

                shiftKey = e.Shift;
                ctrlKey  = e.Control;

                switch (e.KeyCode)
                {
                // WASD -> pans view
                case Keys.A:
                    viewMatrix.TranslationVector += new Vector3(moveFactor * 2, 0f, 0f);
                    break;

                case Keys.D:
                    viewMatrix.TranslationVector -= new Vector3(moveFactor * 2, 0f, 0f);
                    break;

                case Keys.S:
                    if (shiftKey)
                    {
                        viewMatrix.TranslationVector += new Vector3(0f, moveFactor * 2, 0f);
                    }
                    else
                    {
                        viewMatrix.TranslationVector -= new Vector3(0f, 0f, 1) * moveFactor * 2;
                    }
                    break;

                case Keys.W:
                    if (shiftKey)
                    {
                        viewMatrix.TranslationVector -= new Vector3(0f, moveFactor * 2, 0f);
                    }
                    else
                    {
                        viewMatrix.TranslationVector += new Vector3(0f, 0f, 1) * moveFactor * 2;
                    }
                    break;

                // Up/Down and Left/Right - rotates around X / Y respectively
                // (Mouse wheel rotates around Z)
                case Keys.Down:
                    worldMatrix *= Matrix.RotationX(moveFactor);
                    rotation    += new Vector3(moveFactor, 0f, 0f);
                    break;

                case Keys.Up:
                    worldMatrix *= Matrix.RotationX(-moveFactor);
                    rotation    -= new Vector3(moveFactor, 0f, 0f);
                    break;

                case Keys.Left:
                    worldMatrix *= Matrix.RotationY(moveFactor);
                    rotation    += new Vector3(0f, moveFactor, 0f);
                    break;

                case Keys.Right:
                    worldMatrix *= Matrix.RotationY(-moveFactor);
                    rotation    -= new Vector3(0f, moveFactor, 0f);
                    break;

                case Keys.T:
                    fps.Show          = !fps.Show;
                    textRenderer.Show = !textRenderer.Show;
                    break;

                case Keys.B:
                    if (background == Color.White)
                    {
                        background = new Color(30, 30, 34);
                    }
                    else
                    {
                        background = Color.White;
                    }
                    break;

                case Keys.G:
                    axisGrid.Show = !axisGrid.Show;
                    break;

                case Keys.P:
                    // Pause or resume mesh animation
                    meshes.ForEach(m => {
                        if (m.Clock.IsRunning)
                        {
                            m.Clock.Stop();
                        }
                        else
                        {
                            m.Clock.Start();
                        }
                    });
                    break;

                case Keys.X:
                    // To test for correct resource recreation
                    // Simulate device reset or lost.
                    System.Diagnostics.Debug.WriteLine(SharpDX.Diagnostics.ObjectTracker.ReportActiveObjects());
                    DeviceManager.Initialize(DeviceManager.Dpi);
                    System.Diagnostics.Debug.WriteLine(SharpDX.Diagnostics.ObjectTracker.ReportActiveObjects());
                    break;

                case Keys.Z:
                    keyToggles[Keys.Z] = !keyToggles[Keys.Z];
                    if (keyToggles[Keys.Z])
                    {
                        context.PixelShader.Set(depthPixelShader);
                    }
                    else
                    {
                        context.PixelShader.Set(pixelShader);
                    }
                    break;

                case Keys.F:
                    keyToggles[Keys.F] = !keyToggles[Keys.F];
                    RasterizerStateDescription rasterDesc;
                    if (context.Rasterizer.State != null)
                    {
                        rasterDesc = context.Rasterizer.State.Description;
                    }
                    else
                    {
                        rasterDesc = new RasterizerStateDescription()
                        {
                            CullMode = CullMode.Back,
                            FillMode = FillMode.Solid
                        }
                    };
                    if (keyToggles[Keys.F])
                    {
                        rasterDesc.FillMode      = FillMode.Wireframe;
                        context.Rasterizer.State = ToDispose(new RasterizerState(context.Device, rasterDesc));
                    }
                    else
                    {
                        rasterDesc.FillMode      = FillMode.Solid;
                        context.Rasterizer.State = ToDispose(new RasterizerState(context.Device, rasterDesc));
                    }
                    break;

                case Keys.D1:
                    context.PixelShader.Set(pixelShader);
                    break;

                case Keys.D2:
                    context.PixelShader.Set(lambertShader);
                    break;

                case Keys.D3:
                    context.PixelShader.Set(phongShader);
                    break;

                case Keys.D4:
                    context.PixelShader.Set(blinnPhongShader);
                    break;

                case Keys.PageUp:
                    shaderIndex++;
                    if (shaderIndex > ipShaders.Count - 1)
                    {
                        shaderIndex = 0;
                    }
                    updateText();
                    break;

                case Keys.PageDown:
                    shaderIndex--;
                    if (shaderIndex < 0)
                    {
                        shaderIndex = ipShaders.Count - 1;
                    }
                    updateText();
                    break;

                case Keys.Add:
                    var lerpAdd = 0.01f;
                    if (ctrlKey)
                    {
                        imageIndex++;
                        if (imageIndex > images.Length - 1)
                        {
                            imageIndex = 0;
                        }
                        else if (imageIndex != 0)
                        {
                            ip128x4.LoadSourceImage(images[imageIndex]);
                            ip32x32.LoadSourceImage(images[imageIndex]);
                            ip16x4.LoadSourceImage(images[imageIndex]);
                            ip32x4.LoadSourceImage(images[imageIndex]);
                        }
                        break;
                    }
                    if (shiftKey)
                    {
                        lerpAdd *= 10;
                    }
                    lerpT += lerpAdd;


                    updateText();
                    break;

                case Keys.Subtract:
                    var lerpSub = 0.01f;

                    if (ctrlKey)
                    {
                        imageIndex--;
                        if (imageIndex < 0)
                        {
                            imageIndex = images.Length - 1;
                        }
                        if (imageIndex > 0)
                        {
                            ip128x4.LoadSourceImage(images[imageIndex]);
                            ip32x32.LoadSourceImage(images[imageIndex]);
                            ip16x4.LoadSourceImage(images[imageIndex]);
                            ip32x4.LoadSourceImage(images[imageIndex]);
                        }
                        break;
                    }

                    if (shiftKey)
                    {
                        lerpSub *= 10;
                    }
                    lerpT -= lerpSub;
                    updateText();
                    break;
                }

                updateText();
            };
            Window.KeyUp += (s, e) =>
            {
                // Clear the shift/ctrl keys so they aren't sticky
                if (e.KeyCode == Keys.ShiftKey)
                {
                    shiftKey = false;
                }
                if (e.KeyCode == Keys.ControlKey)
                {
                    ctrlKey = false;
                }
            };
            Window.MouseWheel += (s, e) =>
            {
                if (shiftKey)
                {
                    // Zoom in/out
                    viewMatrix.TranslationVector += new Vector3(0f, 0f, (e.Delta / 120f) * moveFactor * 2);
                }
                else
                {
                    // rotate around Z-axis
                    viewMatrix *= Matrix.RotationZ((e.Delta / 120f) * moveFactor);
                    rotation   += new Vector3(0f, 0f, (e.Delta / 120f) * moveFactor);
                }
                updateText();
            };

            var lastX = 0;
            var lastY = 0;

            Window.MouseDown += (s, e) =>
            {
                if (e.Button == MouseButtons.Left)
                {
                    lastX = e.X;
                    lastY = e.Y;
                }
            };

            Window.MouseMove += (s, e) =>
            {
                if (e.Button == MouseButtons.Left)
                {
                    var yRotate = lastX - e.X;
                    var xRotate = lastY - e.Y;
                    lastY = e.Y;
                    lastX = e.X;

                    // Mouse move changes
                    viewMatrix *= Matrix.RotationX(-xRotate * moveFactor);
                    viewMatrix *= Matrix.RotationY(-yRotate * moveFactor);

                    updateText();
                }
            };

            // Display instructions with initial values
            updateText();

            #endregion

            var clock = new System.Diagnostics.Stopwatch();
            clock.Start();

            StringBuilder stats   = new StringBuilder();
            long[]        elapsed = new long[5];
            //long elapsed = 0;
            long frames = 0;

            long elapsedPS = 0;

            #region Render loop
            // Create and run the render loop
            RenderLoop.Run(Window, () =>
            {
                // Start of frame:

                // Retrieve immediate context
                var context = DeviceManager.Direct3DContext;

                // Clear depth stencil view
                context.ClearDepthStencilView(DepthStencilView, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1.0f, 0);
                // Clear render target view
                context.ClearRenderTargetView(RenderTargetView, background);

                // Create viewProjection matrix
                var viewProjection = Matrix.Multiply(viewMatrix, projectionMatrix);

                // Extract camera position from view
                var camPosition = Matrix.Transpose(Matrix.Invert(viewMatrix)).Column4;
                cameraPosition  = new Vector3(camPosition.X, camPosition.Y, camPosition.Z);

                var perFrame             = new ConstantBuffers.PerFrame();
                perFrame.Light.Color     = new Color(1f, 1f, 1f, 1.0f);
                var lightDir             = Vector3.Transform(new Vector3(1f, -1f, -1f), worldMatrix);
                perFrame.Light.Direction = new Vector3(lightDir.X, lightDir.Y, lightDir.Z);// new Vector3(Vector3.Transform(new Vector3(1f, -1f, 1f), worldMatrix * Matrix.RotationAxis(Vector3.UnitY, time)).ToArray().Take(3).ToArray());

                perFrame.CameraPosition = cameraPosition;
                context.UpdateSubresource(ref perFrame, perFrameBuffer);

                // Render each object

                var perMaterial           = new ConstantBuffers.PerMaterial();
                perMaterial.Ambient       = new Color4(0.2f);
                perMaterial.Diffuse       = Color.White;
                perMaterial.Emissive      = new Color4(0);
                perMaterial.Specular      = Color.White;
                perMaterial.SpecularPower = 20f;
                perMaterial.HasTexture    = 0;
                perMaterial.UVTransform   = Matrix.Identity;
                context.UpdateSubresource(ref perMaterial, perMaterialBuffer);

                var perObject = new ConstantBuffers.PerObject();

                foreach (var m in meshes)
                {
                    // MESH
                    perObject.World = m.World * worldMatrix;
                    perObject.WorldInverseTranspose = Matrix.Transpose(Matrix.Invert(perObject.World));
                    perObject.WorldViewProjection   = perObject.World * viewProjection;
                    perObject.Transpose();
                    context.UpdateSubresource(ref perObject, perObjectBuffer);

                    // Provide the material constant buffer to the mesh renderer
                    m.PerMaterialBuffer = perMaterialBuffer;
                    m.PerArmatureBuffer = perArmatureBuffer;
                    m.Render();
                }

                // AXIS GRID
                using (var prevPixelShader = context.PixelShader.Get())
                {
                    perMaterial.HasTexture  = 0;
                    perMaterial.UVTransform = Matrix.Identity;
                    context.UpdateSubresource(ref perMaterial, perMaterialBuffer);
                    context.PixelShader.Set(pixelShader);
                    perObject.World = worldMatrix;
                    perObject.WorldInverseTranspose = Matrix.Transpose(Matrix.Invert(perObject.World));
                    perObject.WorldViewProjection   = perObject.World * viewProjection;
                    perObject.Transpose();
                    context.UpdateSubresource(ref perObject, perObjectBuffer);
                    axisGrid.Render();
                    context.PixelShader.Set(prevPixelShader);
                }


                #region Image Processing

                // This step ensures that we have an image that is not
                // multisampled as the CS cannot use multisampled textures
                // Note: resolvedTarget uses the same format as renderTarget
                if (renderTarget.Description.SampleDescription.Count > 1 || renderTarget.Description.SampleDescription.Quality > 0)
                {
                    context.ResolveSubresource(renderTarget, 0, resolvedTarget, 0, resolvedTarget.Description.Format);
                }
                else
                {
                    // Not multisampled, so just copy to the resolvedTarget
                    context.CopyResource(renderTarget, resolvedTarget);
                }


                // TODO: This should be moved outside of the render loop
                TexturePingPong pp = new TexturePingPong();
                pp.SetSRVs(altRenderTargetSRV, alt2RenderTargetSRV);
                pp.SetUAVs(altRenderTargetUAV, alt2RenderTargetUAV);
                pp.SetUIntUAVs(altRenderTargetUIntUAV, alt2RenderTargetUIntUAV);

                ip128x4.Constants.LerpT = lerpT;
                ip32x32.Constants.LerpT = lerpT;
                ip16x4.Constants.LerpT  = lerpT;
                ip32x4.Constants.LerpT  = lerpT;

                // By default shaders are located in ImageProcessingCS.hlsl
                // other source files can be used by using the imageProcessing.CompileComputeShader function
                // Be sure to set the threadX/Y counts first

                // An array of shaders to apply
                string[] shaders = ipShaders[shaderIndex];// { "BlurFilterHorizontalCS", "BlurFilterVerticalCS" }; //"DesaturateCS" };//

                // Median3x3TapSinglePassCS
                // SobelEdgeOverlayCS
                // SobelEdgeCS
                // SepiaCS
                // BrightnessCS
                // ContrastCS
                // NegativeCS
                // SaturateCS
                // DesaturateCS

                // Possible to control the thread count for each shader if necessary
                ImageProcessingCS.ComputeConfig[] shaderConfig = null;
                //new[] { // Horizontal filter
                //    new ImageProcessingCS.ComputeConfig {
                //        Constants = imageProcessing.Constants,
                //        ThreadsX = 1024,
                //        ThreadsY = 1
                //    },  // Vertical filter
                //    new ImageProcessingCS.ComputeConfig {
                //        Constants = imageProcessing.Constants,
                //        ThreadsX = 1,
                //        ThreadsY = 1024
                //    },
                //}

                // Ctrl +/- changes the imageIndex
                // Page Up/Down changes the shader code
                if (imageIndex > 0)
                {
                    // Process the loaded static image
                    //ip128x4.RunChainedCS(pp, shaders, shaderConfig);
                    //ip32x32.RunChainedCS(pp, shaders, shaderConfig);
                    ip16x4.RunChainedCS(pp, shaders, shaderConfig);
                    //ip32x4.RunChainedCS(pp, shaders, shaderConfig);
                }
                else
                {
                    // Process the currently rendered scene
                    //ip128x4.RunChainedCS(resolvedTargetSRV, pp, shaders, shaderConfig);
                    //ip32x32.RunChainedCS(resolvedTargetSRV, pp, shaders, shaderConfig);
                    ip16x4.RunChainedCS(resolvedTargetSRV, pp, shaders, shaderConfig);
                    //ip32x4.RunChainedCS(pp, shaders, shaderConfig);
                }

                // Run histogram shader and retrieve result
                //var histo = imageProcessing.Histogram();

                // Keep track of how long the dispatch calls are taking
                //elapsed[0] += ip128x4.LastDispatchTicks;
                //elapsed[1] += ip32x32.LastDispatchTicks;
                elapsed[2] += ip16x4.LastDispatchTicks;
                //elapsed[3] += ip32x4.LastDispatchTicks;

                clock.Restart();
                // Render the result to the render target with our screen-aligned quad
                context.PixelShader.SetShaderResource(0, pp.GetCurrentAsSRV());
                screenAlignedQuad.Render();
                elapsedPS += clock.ElapsedTicks;

                frames++;


                // Output core statistics
                // Warning!!! the timings for the CS are the accumulated Dispatch calls only, not setting / clearing resources etc..
                // A comparison with the screen-aligned pixel shader is provided - this way an operation can easily be tested for performance in both
                if (frames % 2500 == 0)
                {
                    string shader = String.Empty;
                    foreach (var s in ipShaders[shaderIndex])
                    {
                        shader += s + " ";
                    }
                    textRenderer.Text = string.Format("CS: ({0:F6} ms) - {2}\nPS: ({1:F6} ms)",
                                                      (double)elapsed[2] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                                                      (double)elapsedPS / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                                                      shader);

                    //textRenderer.Text = string.Format("CS:\n1024x1:{0:F6} ms)\n32x32:{1:F6} ms\n16x4:{2:F6} ms\n256x4:{3:F6} ms)\nPS: ({4:F6} ms)",
                    //    (double)elapsed[0] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[1] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[2] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[3] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsedPS / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0);

                    //stats.AppendLine(string.Format("{0:F9},{1:F9},{2:F9},{3:F9}",
                    //    (double)elapsed[0] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[1] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[2] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[3] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0
                    //    ));

                    //System.IO.File.AppendAllText("stats.csv", string.Format("{0:F9},{1:F9},{2:F9},{3:F9}\r\n",
                    //    (double)elapsed[0] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[1] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[2] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[3] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0
                    //    ));
                }
                else if (frames > 2500)
                {
                    frames     = 0;
                    elapsed[0] = 0;
                    elapsed[1] = 0;
                    elapsed[2] = 0;
                    elapsed[3] = 0;
                    elapsedPS  = 0;
                }

                #endregion

                // Render FPS
                fps.Render();

                // Render instructions + position changes
                textRenderer.Render();

                // Present the frame
                Present();
            });
            #endregion
        }
        protected override void DoRender()
        {
            // Calculate elapsed seconds
            var time = clock.ElapsedMilliseconds / 1000.0f;

            // Retrieve device context
            var context = this.DeviceManager.Direct3DContext;

            // Calculate skin matrices for each bone
            ConstantBuffers.PerArmature skinMatrices = new ConstantBuffers.PerArmature();
            if (mesh.Bones != null)
            {
                // Retrieve each bone's local transform
                for (var i = 0; i < mesh.Bones.Count; i++)
                {
                    skinMatrices.Bones[i] = mesh.Bones[i].BoneLocalTransform;
                }

                // Load bone transforms from animation frames
                if (CurrentAnimation.HasValue)
                {
                    // Keep track of the last key-frame used for each bone
                    Mesh.Keyframe?[] lastKeyForBones = new Mesh.Keyframe?[mesh.Bones.Count];
                    // Keep track of whether a bone has been interpolated
                    bool[] lerpedBones = new bool[mesh.Bones.Count];
                    for (var i = 0; i < CurrentAnimation.Value.Keyframes.Count; i++)
                    {
                        // Retrieve current key-frame
                        var frame = CurrentAnimation.Value.Keyframes[i];

                        // If the current frame is not in the future
                        if (frame.Time <= time)
                        {
                            // Keep track of last key-frame for bone
                            lastKeyForBones[frame.BoneIndex] = frame;
                            // Retrieve transform from current key-frame
                            skinMatrices.Bones[frame.BoneIndex] = frame.Transform;
                        }
                        // Frame is in the future, check if we should interpolate
                        else
                        {
                            // Only interpolate a bone's key-frames ONCE
                            if (!lerpedBones[frame.BoneIndex])
                            {
                                // Retrieve the previous key-frame if exists
                                Mesh.Keyframe prevFrame;
                                if (lastKeyForBones[frame.BoneIndex] != null)
                                {
                                    prevFrame = lastKeyForBones[frame.BoneIndex].Value;
                                }
                                else
                                {
                                    continue; // nothing to interpolate
                                }
                                // Make sure we only interpolate with
                                // one future frame for this bone
                                lerpedBones[frame.BoneIndex] = true;

                                // Calculate time difference between frames
                                var frameLength = frame.Time - prevFrame.Time;
                                var timeDiff    = time - prevFrame.Time;
                                var amount      = timeDiff / frameLength;

                                // Interpolation using Lerp on scale and translation, and Slerp on Rotation (Quaternion)
                                Vector3    t1, t2; // Translation
                                Quaternion q1, q2; // Rotation
                                float      s1, s2; // Scale
                                // Decompose the previous key-frame's transform
                                prevFrame.Transform.DecomposeUniformScale(out s1, out q1, out t1);
                                // Decompose the current key-frame's transform
                                frame.Transform.DecomposeUniformScale(out s2, out q2, out t2);

                                // Perform interpolation and reconstitute matrix
                                skinMatrices.Bones[frame.BoneIndex] =
                                    Matrix.Scaling(MathUtil.Lerp(s1, s2, amount)) *
                                    Matrix.RotationQuaternion(Quaternion.Slerp(q1, q2, amount)) *
                                    Matrix.Translation(Vector3.Lerp(t1, t2, amount));
                            }
                        }
                    }
                }

                // Apply parent bone transforms
                // We assume here that the first bone has no parent
                // and that each parent bone appears before children
                for (var i = 1; i < mesh.Bones.Count; i++)
                {
                    var bone = mesh.Bones[i];
                    if (bone.ParentIndex > -1)
                    {
                        var parentTransform = skinMatrices.Bones[bone.ParentIndex];
                        skinMatrices.Bones[i] = (skinMatrices.Bones[i] * parentTransform);
                    }
                }

                // Change the bone transform from rest pose space into bone space (using the inverse of the bind/rest pose)
                for (var i = 0; i < mesh.Bones.Count; i++)
                {
                    skinMatrices.Bones[i] = Matrix.Transpose(mesh.Bones[i].InvBindPose * skinMatrices.Bones[i]);
                }

                // Check need to loop animation
                if (!PlayOnce && CurrentAnimation.HasValue && CurrentAnimation.Value.EndTime <= time)
                {
                    this.Clock.Restart();
                }
            }

            // Update the constant buffer with the skin matrices for each bone
            context.UpdateSubresource(skinMatrices.Bones, PerArmatureBuffer);

            // Draw sub-meshes grouped by material
            for (var mIndx = 0; mIndx < mesh.Materials.Count; mIndx++)
            {
                // Retrieve sub meshes for this material
                var subMeshesForMaterial =
                    (from sm in mesh.SubMeshes
                     where sm.MaterialIndex == mIndx
                     select sm).ToArray();

                // If the material buffer is available and there are submeshes
                // using the material update the PerMaterialBuffer
                if (PerMaterialBuffer != null && subMeshesForMaterial.Length > 0)
                {
                    // update the PerMaterialBuffer constant buffer
                    var material = new ConstantBuffers.PerMaterial()
                    {
                        Ambient       = new Color4(mesh.Materials[mIndx].Ambient),
                        Diffuse       = new Color4(mesh.Materials[mIndx].Diffuse),
                        Emissive      = new Color4(mesh.Materials[mIndx].Emissive),
                        Specular      = new Color4(mesh.Materials[mIndx].Specular),
                        SpecularPower = mesh.Materials[mIndx].SpecularPower,
                        UVTransform   = mesh.Materials[mIndx].UVTransform,
                    };

                    // Bind textures to the pixel shader
                    int texIndxOffset = mIndx * Common.Mesh.MaxTextures;
                    material.HasTexture = (uint)(textureViews[texIndxOffset] != null ? 1 : 0); // 0=false
                    context.PixelShader.SetShaderResources(0, textureViews.GetRange(texIndxOffset, Common.Mesh.MaxTextures).ToArray());

                    // Set texture sampler state
                    context.PixelShader.SetSampler(0, samplerState);

                    // Update material buffer
                    context.UpdateSubresource(ref material, PerMaterialBuffer);
                }

                // For each sub-mesh
                foreach (var subMesh in subMeshesForMaterial)
                {
                    // Ensure the vertex buffer and index buffers are in range
                    if (subMesh.VertexBufferIndex < vertexBuffers.Count && subMesh.IndexBufferIndex < indexBuffers.Count)
                    {
                        // Retrieve and set the vertex and index buffers
                        var vertexBuffer = vertexBuffers[(int)subMesh.VertexBufferIndex];
                        context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, Utilities.SizeOf <Vertex>(), 0));
                        context.InputAssembler.SetIndexBuffer(indexBuffers[(int)subMesh.IndexBufferIndex], Format.R16_UInt, 0);
                        // Set topology
                        context.InputAssembler.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleList;
                    }

                    // Draw the sub-mesh (includes Primitive count which we multiply by 3)
                    // The submesh also includes a start index into the vertex buffer
                    context.DrawIndexed((int)subMesh.PrimCount * 3, (int)subMesh.StartIndex, 0);
                }
            }
        }
        protected override void DoRender()
        {
            // Calculate elapsed seconds
            var time = clock.ElapsedMilliseconds / 1000.0f;

            // Retrieve device context
            var context = this.DeviceManager.Direct3DContext;

            // Calculate skin matrices for each bone
            ConstantBuffers.PerArmature skinMatrices = new ConstantBuffers.PerArmature();
            if (mesh.Bones != null)
            {
                // Retrieve each bone's local transform
                for (var i = 0; i < mesh.Bones.Count; i++)
                {
                    skinMatrices.Bones[i] = mesh.Bones[i].BoneLocalTransform;
                }

                // Load bone transforms from animation frames
                if (CurrentAnimation.HasValue)
                {
                    // Keep track of the last key-frame used for each bone
                    Mesh.Keyframe?[] lastKeyForBones = new Mesh.Keyframe?[mesh.Bones.Count];
                    // Keep track of whether a bone has been interpolated
                    bool[] lerpedBones = new bool[mesh.Bones.Count];
                    for (var i = 0; i < CurrentAnimation.Value.Keyframes.Count; i++)
                    {
                        // Retrieve current key-frame
                        var frame = CurrentAnimation.Value.Keyframes[i];

                        // If the current frame is not in the future
                        if (frame.Time <= time)
                        {
                            // Keep track of last key-frame for bone
                            lastKeyForBones[frame.BoneIndex] = frame;
                            // Retrieve transform from current key-frame
                            skinMatrices.Bones[frame.BoneIndex] = frame.Transform;
                        }
                        // Frame is in the future, check if we should interpolate
                        else
                        {
                            // Only interpolate a bone's key-frames ONCE
                            if (!lerpedBones[frame.BoneIndex])
                            {
                                // Retrieve the previous key-frame if exists
                                Mesh.Keyframe prevFrame;
                                if (lastKeyForBones[frame.BoneIndex] != null)
                                    prevFrame = lastKeyForBones[frame.BoneIndex].Value;
                                else
                                    continue; // nothing to interpolate
                                // Make sure we only interpolate with
                                // one future frame for this bone
                                lerpedBones[frame.BoneIndex] = true;

                                // Calculate time difference between frames
                                var frameLength = frame.Time - prevFrame.Time;
                                var timeDiff = time - prevFrame.Time;
                                var amount = timeDiff / frameLength;

                                // Interpolation using Lerp on scale and translation, and Slerp on Rotation (Quaternion)
                                Vector3 t1, t2;   // Translation
                                Quaternion q1, q2;// Rotation
                                float s1, s2;     // Scale
                                // Decompose the previous key-frame's transform
                                prevFrame.Transform.DecomposeUniformScale(out s1, out q1, out t1);
                                // Decompose the current key-frame's transform
                                frame.Transform.DecomposeUniformScale(out s2, out q2, out t2);

                                // Perform interpolation and reconstitute matrix
                                skinMatrices.Bones[frame.BoneIndex] =
                                    Matrix.Scaling(MathUtil.Lerp(s1, s2, amount)) *
                                    Matrix.RotationQuaternion(Quaternion.Slerp(q1, q2, amount)) *
                                    Matrix.Translation(Vector3.Lerp(t1, t2, amount));
                            }
                        }

                    }
                }

                // Apply parent bone transforms
                // We assume here that the first bone has no parent
                // and that each parent bone appears before children
                for (var i = 1; i < mesh.Bones.Count; i++)
                {
                    var bone = mesh.Bones[i];
                    if (bone.ParentIndex > -1)
                    {
                        var parentTransform = skinMatrices.Bones[bone.ParentIndex];
                        skinMatrices.Bones[i] = (skinMatrices.Bones[i] * parentTransform);
                    }
                }

                // Change the bone transform from rest pose space into bone space (using the inverse of the bind/rest pose)
                for (var i = 0; i < mesh.Bones.Count; i++)
                {
                   skinMatrices.Bones[i] = Matrix.Transpose(mesh.Bones[i].InvBindPose * skinMatrices.Bones[i]);
                }

                // Check need to loop animation
                if (!PlayOnce && CurrentAnimation.HasValue && CurrentAnimation.Value.EndTime <= time)
                {
                    this.Clock.Restart();
                }
            }

            // Update the constant buffer with the skin matrices for each bone
            context.UpdateSubresource(skinMatrices.Bones, PerArmatureBuffer);

            // Draw sub-meshes grouped by material
            for (var mIndx = 0; mIndx < mesh.Materials.Count; mIndx++)
            {
                // Retrieve sub meshes for this material
                var subMeshesForMaterial =
                    (from sm in mesh.SubMeshes
                        where sm.MaterialIndex == mIndx
                        select sm).ToArray();

                // If the material buffer is available and there are submeshes
                // using the material update the PerMaterialBuffer
                if (PerMaterialBuffer != null && subMeshesForMaterial.Length > 0)
                {
                    // update the PerMaterialBuffer constant buffer
                    var material = new ConstantBuffers.PerMaterial()
                    {
                        Ambient = new Color4(mesh.Materials[mIndx].Ambient),
                        Diffuse = new Color4(mesh.Materials[mIndx].Diffuse),
                        Emissive = new Color4(mesh.Materials[mIndx].Emissive),
                        Specular = new Color4(mesh.Materials[mIndx].Specular),
                        SpecularPower = mesh.Materials[mIndx].SpecularPower,
                        UVTransform = mesh.Materials[mIndx].UVTransform,
                    };

                    // Bind textures to the pixel shader
                    int texIndxOffset = mIndx * Common.Mesh.MaxTextures;
                    material.HasTexture = (uint)(textureViews[texIndxOffset] != null ? 1 : 0); // 0=false
                    context.PixelShader.SetShaderResources(0, textureViews.GetRange(texIndxOffset, Common.Mesh.MaxTextures).ToArray());

                    // Set texture sampler state
                    context.PixelShader.SetSampler(0, samplerState);

                    // Update material buffer
                    context.UpdateSubresource(ref material, PerMaterialBuffer);
                }

                // For each sub-mesh
                foreach (var subMesh in subMeshesForMaterial)
                {
                    // Ensure the vertex buffer and index buffers are in range
                    if (subMesh.VertexBufferIndex < vertexBuffers.Count && subMesh.IndexBufferIndex < indexBuffers.Count)
                    {
                        // Retrieve and set the vertex and index buffers
                        var vertexBuffer = vertexBuffers[(int)subMesh.VertexBufferIndex];
                        context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, Utilities.SizeOf<Vertex>(), 0));
                        context.InputAssembler.SetIndexBuffer(indexBuffers[(int)subMesh.IndexBufferIndex], Format.R16_UInt, 0);
                        // Set topology
                        context.InputAssembler.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleList;
                    }

                    // Draw the sub-mesh (includes Primitive count which we multiply by 3)
                    // The submesh also includes a start index into the vertex buffer
                    context.DrawIndexed((int)subMesh.PrimCount * 3, (int)subMesh.StartIndex, 0);
                }
            }
        }
        public override void Run()
        {
            #region Create renderers

            // Note: the renderers take care of creating their own
            // device resources and listen for DeviceManager.OnInitialize

            List<string[]> ipShaders = new List<string[]>();
            ipShaders.Add(new[] { "DesaturateCS" });
            ipShaders.Add(new[] { "SaturateCS" });
            ipShaders.Add(new[] { "NegativeCS" });
            ipShaders.Add(new[] { "ContrastCS" });
            ipShaders.Add(new[] { "BrightnessCS" });
            ipShaders.Add(new[] { "SepiaCS" });
            ipShaders.Add(new[] { "BoxFilter3TapHorizontalCS", "BoxFilter3TapVerticalCS" });
            ipShaders.Add(new[] { "BoxFilter5TapHorizontalCS", "BoxFilter5TapVerticalCS" });
            ipShaders.Add(new[] { "BlurFilterHorizontalCS", "BlurFilterVerticalCS" });
            ipShaders.Add(new[] { "SobelEdgeCS" });
            ipShaders.Add(new[] { "ApproxMedianHorizontalCS", "ApproxMedianVerticalCS" });
            ipShaders.Add(new[] { "Median3x3TapSinglePassCS" });
            ipShaders.Add(new[] { "SobelEdgeOverlayCS" });
            int shaderIndex = 0;

            string[] images = new[]{
                "rendered scene",
                "Village.png",
                "Grass.jpg",
                "Sun.jpg",
                "Sand1.jpg",
                "Sand2.jpg",
                "QT1.jpg",
                "QT2.jpg",
                "QT3.jpg"
            };
            var imageIndex = 1;

            var ip32x32 = ToDispose(new ImageProcessingCS());
            ip32x32.ThreadsX = 32;
            ip32x32.ThreadsY = 32;
            ip32x32.LoadSourceImage(images[imageIndex]);
            ip32x32.Initialize(this);

            var ip16x4 = ToDispose(new ImageProcessingCS());
            ip16x4.ThreadsX = 16;
            ip16x4.ThreadsY = 4;
            ip16x4.LoadSourceImage(images[imageIndex]);
            ip16x4.Initialize(this);

            var ip32x4 = ToDispose(new ImageProcessingCS());
            ip32x4.ThreadsX = 32;
            ip32x4.ThreadsY = 4;
            ip32x4.LoadSourceImage(images[imageIndex]);
            ip32x4.Initialize(this);

            var ip128x4 = ToDispose(new ImageProcessingCS());
            ip128x4.ThreadsX = 128;
            ip128x4.ThreadsY = 4;
            ip128x4.LoadSourceImage(images[imageIndex]);
            ip128x4.Initialize(this);

            var screenAlignedQuad = ToDispose(new ScreenAlignedQuadRenderer());
            screenAlignedQuad.Initialize(this);

            // Create a axis-grid renderer
            var axisGrid = ToDispose(new AxisGridRenderer());
            axisGrid.Initialize(this);

            // Create and initialize the mesh renderer
            var loadedMesh = Common.Mesh.LoadFromFile("cartoon_village.cmo");
            List<MeshRenderer> meshes = new List<MeshRenderer>();
            meshes.AddRange((from mesh in loadedMesh
                             select ToDispose(new MeshRenderer(mesh))));
            foreach (var m in meshes) {
                m.Initialize(this);
                m.World = Matrix.Identity;
            }

            // Set the first animation as the current animation and start clock
            foreach (var m in meshes)
            {
                if (m.Mesh.Animations != null && m.Mesh.Animations.Any())
                    m.CurrentAnimation = m.Mesh.Animations.First().Value;
                m.Clock.Start();
            }

            // Create and initialize a Direct2D FPS text renderer
            var fps = ToDispose(new Common.FpsRenderer("Calibri", Color.CornflowerBlue, new Point(8, 8), 16));
            fps.Initialize(this);

            // Create and initialize a general purpose Direct2D text renderer
            // This will display some instructions and the current view and rotation offsets
            var textRenderer = ToDispose(new Common.TextRenderer("Calibri", Color.CornflowerBlue, new Point(8, 40), 12));
            textRenderer.Initialize(this);

            #endregion

            // Initialize the world matrix
            var worldMatrix = Matrix.Identity;

            // Set the camera position
            var cameraPosition = new Vector3(0, 0, 2);
            var cameraTarget = Vector3.Zero; // Looking at the origin 0,0,0
            var cameraUp = Vector3.UnitY; // Y+ is Up

            // Prepare matrices
            // Create the view matrix from our camera position, look target and up direction
            var viewMatrix = Matrix.LookAtRH(cameraPosition, cameraTarget, cameraUp);
            viewMatrix.TranslationVector += new Vector3(0, -0.98f, 0);

            // Create the projection matrix
            /* FoV 60degrees = Pi/3 radians */
            // Aspect ratio (based on window size), Near clip, Far clip
            var projectionMatrix = Matrix.PerspectiveFovRH((float)Math.PI / 3f, Width / (float)Height, 0.5f, 100f);

            // Maintain the correct aspect ratio on resize
            Window.Resize += (s, e) =>
            {
                projectionMatrix = Matrix.PerspectiveFovRH((float)Math.PI / 3f, Width / (float)Height, 0.5f, 100f);
            };

            #region Rotation and window event handlers

            // Create a rotation vector to keep track of the rotation
            // around each of the axes
            var rotation = new Vector3(0.0f, 0.0f, 0.0f);
            var lerpT = 1.0f;
            // We will call this action to update text
            // for the text renderer
            Action updateText = () =>
            {
                string shaders = String.Empty;
                foreach (var s in ipShaders[shaderIndex])
                    shaders += s + " ";
                textRenderer.Text =
                    String.Format("Rotation ({0}) (Up/Down Left/Right Wheel+-)\nView ({1}) (A/D, W/S, Shift+Wheel+-)"
                    + "\nShader {4}(PgUp/PgDn to change)"
                    + "\nImage: {3} (Ctrl +/- to change)"
                    + "\nLerp (t) = {2:#0.00} (+/- to change)"
                    //+ "\nPress Z to show/hide depth buffer - Press F to toggle wireframe"
                    //+ "\nPress 1-8 to switch shaders"
                        , rotation,
                        viewMatrix.TranslationVector,
                        lerpT,
                        images[imageIndex],
                        shaders
                        );
            };

            Dictionary<Keys, bool> keyToggles = new Dictionary<Keys, bool>();
            keyToggles[Keys.Z] = false;
            keyToggles[Keys.F] = false;

            // Support keyboard/mouse input to rotate or move camera view
            var moveFactor = 0.02f; // how much to change on each keypress
            var shiftKey = false;
            var ctrlKey = false;
            var background = Color.White;
            Window.KeyDown += (s, e) =>
            {
                var context = DeviceManager.Direct3DContext;

                shiftKey = e.Shift;
                ctrlKey = e.Control;

                switch (e.KeyCode)
                {
                    // WASD -> pans view
                    case Keys.A:
                        viewMatrix.TranslationVector += new Vector3(moveFactor * 2, 0f, 0f);
                        break;
                    case Keys.D:
                        viewMatrix.TranslationVector -= new Vector3(moveFactor * 2, 0f, 0f);
                        break;
                    case Keys.S:
                        if (shiftKey)
                            viewMatrix.TranslationVector += new Vector3(0f, moveFactor * 2, 0f);
                        else
                            viewMatrix.TranslationVector -= new Vector3(0f, 0f, 1) * moveFactor * 2;
                        break;
                    case Keys.W:
                        if (shiftKey)
                            viewMatrix.TranslationVector -= new Vector3(0f, moveFactor * 2, 0f);
                        else
                            viewMatrix.TranslationVector += new Vector3(0f, 0f, 1) * moveFactor * 2;
                        break;
                    // Up/Down and Left/Right - rotates around X / Y respectively
                    // (Mouse wheel rotates around Z)
                    case Keys.Down:
                        worldMatrix *= Matrix.RotationX(moveFactor);
                        rotation += new Vector3(moveFactor, 0f, 0f);
                        break;
                    case Keys.Up:
                        worldMatrix *= Matrix.RotationX(-moveFactor);
                        rotation -= new Vector3(moveFactor, 0f, 0f);
                        break;
                    case Keys.Left:
                        worldMatrix *= Matrix.RotationY(moveFactor);
                        rotation += new Vector3(0f, moveFactor, 0f);
                        break;
                    case Keys.Right:
                        worldMatrix *= Matrix.RotationY(-moveFactor);
                        rotation -= new Vector3(0f, moveFactor, 0f);
                        break;
                    case Keys.T:
                        fps.Show = !fps.Show;
                        textRenderer.Show = !textRenderer.Show;
                        break;
                    case Keys.B:
                        if (background == Color.White)
                        {
                            background = new Color(30, 30, 34);
                        }
                        else
                        {
                            background = Color.White;
                        }
                        break;
                    case Keys.G:
                        axisGrid.Show = !axisGrid.Show;
                        break;
                    case Keys.P:
                        // Pause or resume mesh animation
                        meshes.ForEach(m => {
                            if (m.Clock.IsRunning)
                                m.Clock.Stop();
                            else
                                m.Clock.Start();
                        });
                        break;
                    case Keys.X:
                        // To test for correct resource recreation
                        // Simulate device reset or lost.
                        System.Diagnostics.Debug.WriteLine(SharpDX.Diagnostics.ObjectTracker.ReportActiveObjects());
                        DeviceManager.Initialize(DeviceManager.Dpi);
                        System.Diagnostics.Debug.WriteLine(SharpDX.Diagnostics.ObjectTracker.ReportActiveObjects());
                        break;
                    case Keys.Z:
                        keyToggles[Keys.Z] = !keyToggles[Keys.Z];
                        if (keyToggles[Keys.Z])
                        {
                            context.PixelShader.Set(depthPixelShader);
                        }
                        else
                        {
                            context.PixelShader.Set(pixelShader);
                        }
                        break;
                    case Keys.F:
                        keyToggles[Keys.F] = !keyToggles[Keys.F];
                        RasterizerStateDescription rasterDesc;
                        if (context.Rasterizer.State != null)
                            rasterDesc = context.Rasterizer.State.Description;
                        else
                            rasterDesc = new RasterizerStateDescription()
                            {
                                CullMode = CullMode.Back,
                                FillMode = FillMode.Solid
                            };
                        if (keyToggles[Keys.F])
                        {
                            rasterDesc.FillMode = FillMode.Wireframe;
                            context.Rasterizer.State = ToDispose(new RasterizerState(context.Device, rasterDesc));
                        }
                        else
                        {
                            rasterDesc.FillMode = FillMode.Solid;
                            context.Rasterizer.State = ToDispose(new RasterizerState(context.Device, rasterDesc));
                        }
                        break;
                    case Keys.D1:
                        context.PixelShader.Set(pixelShader);
                        break;
                    case Keys.D2:
                        context.PixelShader.Set(lambertShader);
                        break;
                    case Keys.D3:
                        context.PixelShader.Set(phongShader);
                        break;
                    case Keys.D4:
                        context.PixelShader.Set(blinnPhongShader);
                        break;
                    case Keys.PageUp:
                        shaderIndex++;
                        if (shaderIndex > ipShaders.Count - 1)
                            shaderIndex = 0;
                        updateText();
                        break;
                    case Keys.PageDown:
                        shaderIndex--;
                        if (shaderIndex < 0)
                            shaderIndex = ipShaders.Count - 1;
                        updateText();
                        break;
                    case Keys.Add:
                        var lerpAdd = 0.01f;
                        if (ctrlKey)
                        {
                            imageIndex++;
                            if (imageIndex > images.Length - 1)
                            {
                                imageIndex = 0;
                            }
                            else if (imageIndex != 0)
                            {
                                ip128x4.LoadSourceImage(images[imageIndex]);
                                ip32x32.LoadSourceImage(images[imageIndex]);
                                ip16x4.LoadSourceImage(images[imageIndex]);
                                ip32x4.LoadSourceImage(images[imageIndex]);
                            }
                            break;
                        }
                        if (shiftKey)
                            lerpAdd *= 10;
                        lerpT += lerpAdd;

                        updateText();
                        break;
                    case Keys.Subtract:
                        var lerpSub = 0.01f;

                        if (ctrlKey)
                        {
                            imageIndex--;
                            if (imageIndex < 0)
                            {
                                imageIndex = images.Length - 1;
                            }
                            if (imageIndex > 0)
                            {
                                ip128x4.LoadSourceImage(images[imageIndex]);
                                ip32x32.LoadSourceImage(images[imageIndex]);
                                ip16x4.LoadSourceImage(images[imageIndex]);
                                ip32x4.LoadSourceImage(images[imageIndex]);
                            }
                            break;
                        }

                        if (shiftKey)
                            lerpSub *= 10;
                        lerpT -= lerpSub;
                        updateText();
                        break;
                }

                updateText();
            };
            Window.KeyUp += (s, e) =>
            {
                // Clear the shift/ctrl keys so they aren't sticky
                if (e.KeyCode == Keys.ShiftKey)
                    shiftKey = false;
                if (e.KeyCode == Keys.ControlKey)
                    ctrlKey = false;
            };
            Window.MouseWheel += (s, e) =>
            {
                if (shiftKey)
                {
                    // Zoom in/out
                    viewMatrix.TranslationVector += new Vector3(0f, 0f, (e.Delta / 120f) * moveFactor * 2);
                }
                else
                {
                    // rotate around Z-axis
                    viewMatrix *= Matrix.RotationZ((e.Delta / 120f) * moveFactor);
                    rotation += new Vector3(0f, 0f, (e.Delta / 120f) * moveFactor);
                }
                updateText();
            };

            var lastX = 0;
            var lastY = 0;

            Window.MouseDown += (s, e) =>
            {
                if (e.Button == MouseButtons.Left)
                {
                    lastX = e.X;
                    lastY = e.Y;
                }
            };

            Window.MouseMove += (s, e) =>
            {
                if (e.Button == MouseButtons.Left)
                {
                    var yRotate = lastX - e.X;
                    var xRotate = lastY - e.Y;
                    lastY = e.Y;
                    lastX = e.X;

                    // Mouse move changes
                    viewMatrix *= Matrix.RotationX(-xRotate * moveFactor);
                    viewMatrix *= Matrix.RotationY(-yRotate * moveFactor);

                    updateText();
                }
            };

            // Display instructions with initial values
            updateText();

            #endregion

            var clock = new System.Diagnostics.Stopwatch();
            clock.Start();

            StringBuilder stats = new StringBuilder();
            long[] elapsed = new long[5];
            //long elapsed = 0;
            long frames = 0;

            long elapsedPS = 0;

            #region Render loop
            // Create and run the render loop
            RenderLoop.Run(Window, () =>
            {
                // Start of frame:

                // Retrieve immediate context
                var context = DeviceManager.Direct3DContext;

                // Clear depth stencil view
                context.ClearDepthStencilView(DepthStencilView, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1.0f, 0);
                // Clear render target view
                context.ClearRenderTargetView(RenderTargetView, background);

                // Create viewProjection matrix
                var viewProjection = Matrix.Multiply(viewMatrix, projectionMatrix);

                // Extract camera position from view
                var camPosition = Matrix.Transpose(Matrix.Invert(viewMatrix)).Column4;
                cameraPosition = new Vector3(camPosition.X, camPosition.Y, camPosition.Z);

                var perFrame = new ConstantBuffers.PerFrame();
                perFrame.Light.Color = new Color(1f, 1f, 1f, 1.0f);
                var lightDir = Vector3.Transform(new Vector3(1f, -1f, -1f), worldMatrix);
                perFrame.Light.Direction = new Vector3(lightDir.X, lightDir.Y, lightDir.Z);// new Vector3(Vector3.Transform(new Vector3(1f, -1f, 1f), worldMatrix * Matrix.RotationAxis(Vector3.UnitY, time)).ToArray().Take(3).ToArray());

                perFrame.CameraPosition = cameraPosition;
                context.UpdateSubresource(ref perFrame, perFrameBuffer);

                // Render each object

                var perMaterial = new ConstantBuffers.PerMaterial();
                perMaterial.Ambient = new Color4(0.2f);
                perMaterial.Diffuse = Color.White;
                perMaterial.Emissive = new Color4(0);
                perMaterial.Specular = Color.White;
                perMaterial.SpecularPower = 20f;
                perMaterial.HasTexture = 0;
                perMaterial.UVTransform = Matrix.Identity;
                context.UpdateSubresource(ref perMaterial, perMaterialBuffer);

                var perObject = new ConstantBuffers.PerObject();

                foreach (var m in meshes)
                {
                    // MESH
                    perObject.World = m.World * worldMatrix;
                    perObject.WorldInverseTranspose = Matrix.Transpose(Matrix.Invert(perObject.World));
                    perObject.WorldViewProjection = perObject.World * viewProjection;
                    perObject.Transpose();
                    context.UpdateSubresource(ref perObject, perObjectBuffer);

                    // Provide the material constant buffer to the mesh renderer
                    m.PerMaterialBuffer = perMaterialBuffer;
                    m.PerArmatureBuffer = perArmatureBuffer;
                    m.Render();
                }

                // AXIS GRID
                using (var prevPixelShader = context.PixelShader.Get())
                {
                    perMaterial.HasTexture = 0;
                    perMaterial.UVTransform = Matrix.Identity;
                    context.UpdateSubresource(ref perMaterial, perMaterialBuffer);
                    context.PixelShader.Set(pixelShader);
                    perObject.World = worldMatrix;
                    perObject.WorldInverseTranspose = Matrix.Transpose(Matrix.Invert(perObject.World));
                    perObject.WorldViewProjection = perObject.World * viewProjection;
                    perObject.Transpose();
                    context.UpdateSubresource(ref perObject, perObjectBuffer);
                    axisGrid.Render();
                    context.PixelShader.Set(prevPixelShader);
                }

                #region Image Processing

                // This step ensures that we have an image that is not
                // multisampled as the CS cannot use multisampled textures
                // Note: resolvedTarget uses the same format as renderTarget
                if (renderTarget.Description.SampleDescription.Count > 1 || renderTarget.Description.SampleDescription.Quality > 0)
                {
                    context.ResolveSubresource(renderTarget, 0, resolvedTarget, 0, resolvedTarget.Description.Format);
                }
                else
                {
                    // Not multisampled, so just copy to the resolvedTarget
                    context.CopyResource(renderTarget, resolvedTarget);
                }

                // TODO: This should be moved outside of the render loop
                TexturePingPong pp = new TexturePingPong();
                pp.SetSRVs(altRenderTargetSRV, alt2RenderTargetSRV);
                pp.SetUAVs(altRenderTargetUAV, alt2RenderTargetUAV);
                pp.SetUIntUAVs(altRenderTargetUIntUAV, alt2RenderTargetUIntUAV);

                ip128x4.Constants.LerpT = lerpT;
                ip32x32.Constants.LerpT = lerpT;
                ip16x4.Constants.LerpT = lerpT;
                ip32x4.Constants.LerpT = lerpT;

                // By default shaders are located in ImageProcessingCS.hlsl
                // other source files can be used by using the imageProcessing.CompileComputeShader function
                // Be sure to set the threadX/Y counts first

                // An array of shaders to apply
                string[] shaders = ipShaders[shaderIndex];// { "BlurFilterHorizontalCS", "BlurFilterVerticalCS" }; //"DesaturateCS" };//

                // Median3x3TapSinglePassCS
                // SobelEdgeOverlayCS
                // SobelEdgeCS
                // SepiaCS
                // BrightnessCS
                // ContrastCS
                // NegativeCS
                // SaturateCS
                // DesaturateCS

                // Possible to control the thread count for each shader if necessary
                ImageProcessingCS.ComputeConfig[] shaderConfig = null;
                //new[] { // Horizontal filter
                //    new ImageProcessingCS.ComputeConfig {
                //        Constants = imageProcessing.Constants,
                //        ThreadsX = 1024,
                //        ThreadsY = 1
                //    },  // Vertical filter
                //    new ImageProcessingCS.ComputeConfig {
                //        Constants = imageProcessing.Constants,
                //        ThreadsX = 1,
                //        ThreadsY = 1024
                //    },
                //}

                // Ctrl +/- changes the imageIndex
                // Page Up/Down changes the shader code
                if (imageIndex > 0)
                {
                    // Process the loaded static image
                    //ip128x4.RunChainedCS(pp, shaders, shaderConfig);
                    //ip32x32.RunChainedCS(pp, shaders, shaderConfig);
                    ip16x4.RunChainedCS(pp, shaders, shaderConfig);
                    //ip32x4.RunChainedCS(pp, shaders, shaderConfig);
                }
                else
                {
                    // Process the currently rendered scene
                    //ip128x4.RunChainedCS(resolvedTargetSRV, pp, shaders, shaderConfig);
                    //ip32x32.RunChainedCS(resolvedTargetSRV, pp, shaders, shaderConfig);
                    ip16x4.RunChainedCS(resolvedTargetSRV, pp, shaders, shaderConfig);
                    //ip32x4.RunChainedCS(pp, shaders, shaderConfig);
                }

                // Run histogram shader and retrieve result
                //var histo = imageProcessing.Histogram();

                // Keep track of how long the dispatch calls are taking
                //elapsed[0] += ip128x4.LastDispatchTicks;
                //elapsed[1] += ip32x32.LastDispatchTicks;
                elapsed[2] += ip16x4.LastDispatchTicks;
                //elapsed[3] += ip32x4.LastDispatchTicks;

                clock.Restart();
                // Render the result to the render target with our screen-aligned quad
                context.PixelShader.SetShaderResource(0, pp.GetCurrentAsSRV());
                screenAlignedQuad.Render();
                elapsedPS += clock.ElapsedTicks;

                frames++;

                // Output core statistics
                // Warning!!! the timings for the CS are the accumulated Dispatch calls only, not setting / clearing resources etc..
                // A comparison with the screen-aligned pixel shader is provided - this way an operation can easily be tested for performance in both
                if (frames % 2500 == 0)
                {
                    string shader = String.Empty;
                    foreach (var s in ipShaders[shaderIndex])
                        shader += s + " ";
                    textRenderer.Text = string.Format("CS: ({0:F6} ms) - {2}\nPS: ({1:F6} ms)",
                        (double)elapsed[2] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                        (double)elapsedPS / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                        shader);

                    //textRenderer.Text = string.Format("CS:\n1024x1:{0:F6} ms)\n32x32:{1:F6} ms\n16x4:{2:F6} ms\n256x4:{3:F6} ms)\nPS: ({4:F6} ms)",
                    //    (double)elapsed[0] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[1] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[2] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[3] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsedPS / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0);

                    //stats.AppendLine(string.Format("{0:F9},{1:F9},{2:F9},{3:F9}",
                    //    (double)elapsed[0] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[1] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[2] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[3] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0
                    //    ));

                    //System.IO.File.AppendAllText("stats.csv", string.Format("{0:F9},{1:F9},{2:F9},{3:F9}\r\n",
                    //    (double)elapsed[0] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[1] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[2] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0,
                    //    (double)elapsed[3] / (double)frames / System.Diagnostics.Stopwatch.Frequency * 1000.0
                    //    ));
                }
                else if (frames > 2500)
                {
                    frames = 0;
                    elapsed[0] = 0;
                    elapsed[1] = 0;
                    elapsed[2] = 0;
                    elapsed[3] = 0;
                    elapsedPS = 0;
                }

                #endregion

                // Render FPS
                fps.Render();

                // Render instructions + position changes
                textRenderer.Render();

                // Present the frame
                Present();
            });
            #endregion
        }