Advertisement

Issue with personal DirectX Framework

Started by July 01, 2015 09:06 PM
0 comments, last by Gaius Baltar 9 years, 6 months ago
First off I'd like to point out that I'm an absolute beginner regarding 3D graphics programming and DirectX in general. So if you have any code optimization tips for me I'll accept them with open arms.

I've been learning my way about the DirectX API using SlimDX and the help of some useful resources like Frank D. Luna's book on programming with DirectX 11 and this site . (The book and site go very well together, especially for someone learning to use DirectX with C#).

Anyhow, I've recently finished chapter 6 of Luna's book and decided to try and make a personal DirectX framework of sorts for learning purposes, but now that I've got a sort of skeleton of the thing going, it throws weird errors at me occasionally or it just doesn't do what I want it to (display a cube in this case).

So, here it is:

Window.cs
[spoiler]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SlimDX;
using SlimDX.Windows;
using System.Drawing;
using System.Windows.Forms;
using SlimDX.Direct3D11;
using Device = SlimDX.Direct3D11.Device;
using Debug = System.Diagnostics.Debug;
using SlimDX.DXGI;
 
namespace MyFramework
{
    public delegate void Renderer();
    public delegate void Updater(float dt);
    public delegate void Disposer();
    public class Window : RenderForm
    {
        public Event windowsEvent;
 
        public Renderer OnRender;
        public Updater OnUpdate;
        public Disposer OnDispose;
        public Timer timer;
        private Device device;
        private SwapChain swapChain;
        private DeviceContext immediateContext;
        private RenderTargetView renderTargetView;
        private DepthStencilView depthStencilView;
        private Texture2D depthStencilBuffer;
        private Viewport viewport;
        private Rectangle scissorRect;
        private Shader shader;
        private EffectMatrixVariable fxWVP;
        private Matrix proj;
 
        private RasterizerStateDescription rsd;
        private RasterizerState rs;
 
        private int msaa4XQuality;
        private bool enableMsaa4X;
        private float viewportX;
        private float viewportY;
        private float viewportWidth;
        private float viewportHeight;
 
        public bool ToggleMsaa4XEnabled
        {
            get { return enableMsaa4X; }
            set { enableMsaa4X = value; }
        }
 
        public bool ToggleWireframe
        {
            get
            {
                if (rsd.FillMode == FillMode.Wireframe)
                    return true;
                else
                    return false;
            }
            set
            {
                if (value)
                    rsd.FillMode = FillMode.Wireframe;
                else
                    rsd.FillMode = FillMode.Solid;
            }
        }
 
        public bool ToggleScissorMode
        {
            get { return rsd.IsScissorEnabled; }
            set { rsd.IsScissorEnabled = value; }
        }
 
        public Device Device
        {
            get { return device; }
        }
 
        public void SetScissorRect(int x, int y, int width, int height)
        {
            scissorRect = new Rectangle(x, y, width, height);
        }
        public Window(string title, Size size, float vX = 0, float vY = 0, float vWidth = -1, float vHeight = -1)
            : base(title)
        {
            this.Size = size;
            this.Icon = null;
            viewportX = vX;
            viewportY = vY;
            viewportWidth = vWidth >= 0 ? vWidth : size.Width;
            viewportHeight = vHeight >= 0 ? vHeight : size.Height;
 
            Init();
        }
 
        private void Init()
        {
            device = null;
            swapChain = null;
            immediateContext = null;
            renderTargetView = null;
            depthStencilView = null;
            depthStencilBuffer = null;
            rs = null;
            shader = null;
            fxWVP = null;
 
            msaa4XQuality = 0;
            enableMsaa4X = true;
 
            scissorRect = new Rectangle();
            windowsEvent = new Event();
            timer = new Timer();
            rsd = new RasterizerStateDescription();
            viewport = new Viewport();
 
            OnRender += () => { };
            OnUpdate += (dt) => { };
            OnDispose += () => { };
 
            this.ResizeBegin += (sender, args) =>
            {
                timer.Stop();
            };
            this.ResizeEnd += (sender, args) =>
            {
                timer.Start();
                OnResize();
            };
            this.FormClosing += (sender, args) =>
            {
                DisposeWindow();
                OnDispose();
                Environment.Exit(0);
 
            };
            this.Show();
            this.Update();
 
            InitD3D();
        }
 
        private void InitD3D()
        {
            try
            {
                device = new Device(DriverType.Hardware, DeviceCreationFlags.None);
            }
            catch (Exception ex)
            {
                MessageBox.Show("D3D11Device creation failed\n" + ex.Message + "\n" + ex.StackTrace, "Error");
            }
 
            Debug.Assert((msaa4XQuality = device.CheckMultisampleQualityLevels(Format.R8G8B8A8_UNorm, 4)) > 0);
            try
            {
                var description = new SwapChainDescription()
                {
                    BufferCount = 1,
                    Usage = Usage.RenderTargetOutput,
                    OutputHandle = this.Handle,
                    IsWindowed = true,
                    ModeDescription = new ModeDescription(0, 0, new Rational(60, 1), Format.R8G8B8A8_UNorm),
                    SampleDescription = enableMsaa4X ? new SampleDescription(4, msaa4XQuality - 1) : new SampleDescription(1, 0),
                    Flags = SwapChainFlags.AllowModeSwitch,
                    SwapEffect = SwapEffect.Discard
                };
                Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.None, description, out device, out swapChain);
                immediateContext = device.ImmediateContext;
 
                rsd = new RasterizerStateDescription()
                {
                    FillMode = FillMode.Solid,
                    CullMode = CullMode.Back,
                    IsAntialiasedLineEnabled = true,
                    IsFrontCounterclockwise = false,
                    IsDepthClipEnabled = true,
                    IsScissorEnabled = false
                };
                rs = RasterizerState.FromDescription(device, rsd);
 
                using (var factory = swapChain.GetParent<SlimDX.DXGI.Factory>())
                    factory.SetWindowAssociation(this.Handle, WindowAssociationFlags.IgnoreAltEnter);
 
                this.KeyDown += (o, e) =>
                {
                    if (e.Alt && e.KeyCode == Keys.Enter)
                    {
                        swapChain.IsFullScreen = !swapChain.IsFullScreen;
                    }
                };
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + "\n" + ex.StackTrace, "Error");
            }
            OnResize();
        }
 
        public void InitShader(string filePath)
        {
            shader = new Shader(ref device, filePath);
            fxWVP = shader.fx.GetVariableByName("worldViewProj").AsMatrix();
        }
        public void InitCustomShader(string filePath, string[] inputElements, Format[] iEFormats, int[] iEStrides)
        {
            shader = new Shader(ref device, filePath, inputElements, iEFormats, iEStrides);
        }
 
        private void DisposeWindow()
        {
            Util.ReleaseCom(ref renderTargetView);
            Util.ReleaseCom(ref depthStencilView);
            Util.ReleaseCom(ref depthStencilBuffer);
            if (shader != null) shader.Dispose();
            if (immediateContext != null) immediateContext.ClearState();
            if (swapChain.IsFullScreen) swapChain.SetFullScreenState(false, null);
            Util.ReleaseCom(ref swapChain);
            Util.ReleaseCom(ref immediateContext);
            Util.ReleaseCom(ref device);
        }
 
        private void OnResize()
        {
            Util.ReleaseCom(ref renderTargetView);
            Util.ReleaseCom(ref depthStencilView);
            Util.ReleaseCom(ref depthStencilBuffer);
            swapChain.ResizeBuffers(1, this.Width, this.Height, Format.R8G8B8A8_UNorm, SwapChainFlags.None);
            using (var resource = SlimDX.Direct3D11.Resource.FromSwapChain<Texture2D>(swapChain, 0))
            {
                renderTargetView = new RenderTargetView(device, resource);
            }
            var depthStencilDesc = new Texture2DDescription()
            {
                Width = this.Width,
                Height = this.Height,
                MipLevels = 1,
                ArraySize = 1,
                Format = Format.D24_UNorm_S8_UInt,
                SampleDescription = enableMsaa4X ? new SampleDescription(4, msaa4XQuality - 1) : new SampleDescription(1, 0),
                Usage = ResourceUsage.Default,
                BindFlags = BindFlags.DepthStencil,
                CpuAccessFlags = CpuAccessFlags.None,
                OptionFlags = ResourceOptionFlags.None
            };
            depthStencilBuffer = new Texture2D(device, depthStencilDesc);
            depthStencilView = new DepthStencilView(device, depthStencilBuffer);
            immediateContext.OutputMerger.SetTargets(depthStencilView, renderTargetView);
            viewport = new Viewport(viewportX, viewportY, viewportWidth, viewportHeight, 0.0f, 1.0f);
            immediateContext.Rasterizer.SetViewports(viewport);
 
            proj = Matrix.PerspectiveFovLH(0.25f * (float)Math.PI, this.Width / this.Height, 0.01f, 500.0f);
        }
 
        public Viewport Viewport
        {
            get { return viewport; }
            set
            {
                viewportX = value.X;
                viewportY = value.Y;
                viewportWidth = value.Width;
                viewportHeight = value.Height;
                OnResize();
            }
        }
 
        public void Run()
        {
            MessagePump.Run(() =>
            {
                timer.Tick();
                OnRender();
                OnUpdate(timer.DeltaTime);
            });
        }
 
        public void ClearScreen(Color4 clearColor)
        {
            immediateContext.ClearRenderTargetView(renderTargetView, clearColor);
            immediateContext.ClearDepthStencilView(depthStencilView, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1.0f, 0);
            immediateContext.Rasterizer.State = rs;
        }
 
        public void SetPrimitiveTobology(PrimitiveTopology topology)
        {
            immediateContext.InputAssembler.PrimitiveTopology = topology;
        }
 
        public void SwapBuffers()
        {
            swapChain.Present(0, PresentFlags.None);
        }
 
        public void Draw<T>(ref T entity) where T : Entity3D
        {
            immediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(entity.vb, Vertex.stride, 0));
            immediateContext.InputAssembler.SetIndexBuffer(entity.ib, Format.R32_UInt, 0);
 
            if (shader != null)
            {
                Vector3 pos = new Vector3(3, 3, 3);
                Vector3 target = new Vector3(0);
                Vector3 up = new Vector3(0, 1, 0);
                Matrix view = Matrix.LookAtLH(pos, target, up);
                fxWVP.SetMatrix(entity.shapeWorld * view * proj);
                for (int p = 0; p < shader.tech.Description.PassCount; p++)
                {
                    shader.tech.GetPassByIndex(p).Apply(immediateContext);
                    immediateContext.DrawIndexed(entity.indexCount, 0, 0);
                }
            }
            else
            {
                immediateContext.DrawIndexed(entity.indexCount, 0, 0);
            }
        }
 
        protected override void WndProc(ref Message m)
        {
            windowsEvent.SetType(m);
            base.WndProc(ref m);
        }
    }
}
 
[/spoiler]

Sahder.cs
[spoiler]

using SlimDX.D3DCompiler;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Device = SlimDX.Direct3D11.Device;
 
namespace MyFramework
{
    class Shader
    {
        public InputLayout inputLayout;
        public Effect fx;
        public EffectTechnique tech;
 
        public Shader(ref Device device, string filePath, string[] inputElements, Format[] iEformats, int[] iEStrides)
        {
            CompileShaders(ref device, filePath);
            CreateInputLayout(ref device, inputElements, iEformats, iEStrides);
        }
 
        public Shader(ref Device device, string filePath)
        {
            CompileShaders(ref device, filePath);
            string[] inputElements = new string[] { "POSITION", "COLOR" };
            Format[] iEformats = new Format[] { Format.R32G32B32_Float, Format.R32G32B32A32_Float };
            int[] iEStrides = new int[] { 12 };
 
            CreateInputLayout(ref device, inputElements, iEformats, iEStrides);
        }
 
        private void CompileShaders(ref Device device, string filePath)
        {
            string errors = null;
            ShaderBytecode compiledShader = null;
            try
            {
                compiledShader = ShaderBytecode.CompileFromFile(
                filePath,
                null,
                "fx_5_0",
                ShaderFlags.None,
                EffectFlags.None,
                null,
                null,
                out errors);
                fx = new Effect(device, compiledShader);
            }
            catch (Exception ex)
            {
                if (!string.IsNullOrEmpty(errors))
                {
                    MessageBox.Show(errors);
                }
                MessageBox.Show(ex.Message);
                return;
            }
            finally
            {
                Util.ReleaseCom(ref compiledShader);
            }
            tech = fx.GetTechniqueByName("Tech");
        }
 
        private void CreateInputLayout(ref Device device, string[] inputElements, Format[] iEformats, int[] iEStrides)
        {
            InputElement[] vertexDesc = new InputElement[inputElements.Length];
            for (int i = 0; i < vertexDesc.Length; i++)
            {
                vertexDesc[i] = new InputElement(inputElements[i], 0, iEformats[i], i > 0 ? iEStrides[i - 1] : 0, 0, InputClassification.PerVertexData, 0);
            }
 
            var passDesc = tech.GetPassByIndex(0).Description;
            inputLayout = new InputLayout(device, passDesc.Signature, vertexDesc);
        }
 
        public void Dispose()
        {
            Util.ReleaseCom(ref inputLayout);
            Util.ReleaseCom(ref fx);
        }
    }
}
 
[/spoiler]

Entity3D.cs
[spoiler]

using SlimDX;
using SlimDX.Direct3D11;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Buffer = SlimDX.Direct3D11.Buffer;
 
namespace MyFramework
{
    public class Entity3D
    {
        public int vertexCount;
        public int indexCount;
        public Vertex[] vertices;
        public uint[] indices;
        public Matrix shapeWorld;
        public Buffer vb;
        public Buffer ib;
        
 
        public Entity3D()
        {
            vertexCount = 0;
            indexCount = 0;
            vertices = null;
            indices = null;
            vb = null;
            ib = null;
            shapeWorld = Matrix.Identity;
        }
 
        public virtual void Dispose()
        {
            Util.ReleaseCom(ref vb);
            Util.ReleaseCom(ref ib);
        }
    }
}
[/spoiler]

Cube.cs
[spoiler]

using SlimDX;
using SlimDX.Direct3D11;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Buffer = SlimDX.Direct3D11.Buffer;
 
namespace MyFramework
{
    public class Cube : Entity3D
    {
        public Cube(Device device, float width, float height, float depth, Color4 color)
            : base()
        {
            float x = 0.5f * width;
            float y = 0.5f * height;
            float z = 0.5f * depth;
 
            vertexCount = 8;
            indexCount = 36;
            vertices = new Vertex[] { 
                new Vertex(-x,-y,-z, color), // 0
                new Vertex( x,-y,-z, color), // 1
                new Vertex( x, y,-z, color), // 2
                new Vertex(-x, y,-z, color), // 3
                new Vertex(-x,-y, z, color), // 4
                new Vertex( x,-y, z, color), // 5
                new Vertex( x, y, z, color), // 6
                new Vertex(-x, y, z, color), // 7
            };
            indices = new uint[] { 
                0,3,2,
                0,2,1,
                2,6,1,
                6,5,1,
                6,7,5,
                7,4,5,
                7,3,4,
                3,0,4,
                7,6,3,
                3,6,2,
                0,1,4,
                1,5,4
            };
 
            BufferDescription vbd = new BufferDescription(Vertex.stride * vertexCount, ResourceUsage.Immutable, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
            vb = new Buffer(device, new DataStream(vertices, false, false), vbd);
            BufferDescription ibd = new BufferDescription(sizeof(uint) * indexCount, ResourceUsage.Immutable, BindFlags.IndexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
            ib = new Buffer(device, new DataStream(indices, false, false), ibd);
        }
    }
}
 
[/spoiler]

Vertex.cs
[spoiler]

using SlimDX;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace MyFramework
{
    public struct Vertex
    {
        public Vector3 position;
        public Color4 color;
        public const int stride = 28;
 
        public Vertex(Vector3 pos, Color4 col)
        {
            position = pos;
            color = col;
        }
        public Vertex(float x, float y, float z, Color4 col)
        {
            position = new Vector3(x, y, z);
            color = col;
        }
    }
}
 
[/spoiler]

Program.cs //Main entry point

[spoiler]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MyFramework;
using System.Drawing;
using SlimDX;
 
namespace FrameworkTester
{
    class Program
    {
        static void Main(string[] args)
        {
            SlimDX.Configuration.EnableObjectTracking = true;
            Window window = new Window("Test", new Size(1280, 720));
 
            window.InitShader("FX/shader.fx");
 
            window.OnUpdate += (dt) => { 
                
            };
 
            Cube cube = new Cube(window.Device, 0.5f, 0.5f, 0.5f, Color.White);
 
            window.OnRender += () => {
                window.ClearScreen(Color.CornflowerBlue);
                window.SetPrimitiveTobology(SlimDX.Direct3D11.PrimitiveTopology.TriangleList);
                window.Draw(ref cube);
                window.SwapBuffers();
            };
 
            window.OnDispose += () => {
                cube.Dispose();
            };
 
            window.Run();
        }
    }
}
 
[/spoiler]

And lastly, my shader, "shader.fx"
[spoiler]

cbuffer wvpm{
float4x4 worldViewProj;
};
 
struct VertexIn {
float3 PosL : POSITION;
float4 Color : COLOR;
};
struct VertexOut {
float4 PosH : SV_POSITION;
float4 Color: COLOR;
};
RasterizerState rsWireframe{ 
FillMode = Wireframe; 
CullMode = Back; 
FrontCounterClockwise = false;
};
VertexOut VS(VertexIn vin){
VertexOut vout;
vout.PosH = mul(float4(vin.PosL, 1.0f), worldViewProj);
vout.Color = vin.Color;
return vout;
}
float4 PS(VertexOut pin) :SV_Target{
return pin.Color;
}
technique11 Tech {
pass P0{
SetVertexShader(CompileShader(vs_4_0, VS()));
SetGeometryShader(NULL);
SetPixelShader(CompileShader(ps_4_0, PS()));
}
}
[/spoiler]

I'd like to leave you with a screenshot of what I get when I run Program.cs:
xLWUvXf.png

Also, please don't bash the hard-coded stuff too much, everything so far is mostly just placeholder code for me to get my bearings.

Okay, I just realized that I forgot to add the InputLayout to the immediateContext. That basically solved the issue.

After some edits to the code I finally get the output I was hoping for:

Q6nBcYF.png

This topic is closed to new replies.

Advertisement