Here's my requirement: Using DirectX11 (via SlimDX) I have to download a series of verteces and use them to create a Texture2D of a map of county borders. Then I need to do the same thing with state borders, and draw them over the county borders. Then, I need to take that texture and create 2 different textures from it, each containing unique radar data. Then I want to take those textures and display them so that the user can look at, for example, base reflectivity and base velocity side by side. The user should be able to zoom in and out of particular areas of the map.
Here's what I've got working: I'm creating my Texture2D without multisampling or depth on a billboard which is displaying in 2 separate views. But it looks blocky, and if you zoom too far out, some of the borders start to disappear.
Here are my issues:
1) I can't for the life of me get any multisampling quality. I'm using an ATI Radeon HD 5750, so I know it must be able to do it, but no formats I've tried support a quality greater than 0.
2) I'm uncertain whether I need to use a depth stencil since I'm drawing all these textures on top of each other. I hope not because when I try, the ShaderResourceView says, "Puny Human! You cannot use a depth stencil format in a ShaderResourceView! Bwa ha ha!" (I'm embellishing)
I'm willing to bet that a lot of these issues would be solved if I just drew the primitives directly into the world space, but when I do that rendering takes way too long because there are so many lines to render. Is there perhaps a way I can cut down on the time it takes?
And here's the code of my last working version:
using SlimDX;
using SlimDX.D3DCompiler;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using SlimDX.Windows;
using System;
using System.Windows.Forms;
using System.Collections.Generic;
using Device = SlimDX.Direct3D11.Device;
using Buffer = SlimDX.Direct3D11.Buffer;
using Resource = SlimDX.Direct3D11.Resource;
using Format = SlimDX.DXGI.Format;
using MapFlags = SlimDX.Direct3D11.MapFlags;
namespace Radar
{
abstract public class Renderer
{
protected static Device mDevice = null;
protected SwapChain mSwapChain = null;
protected RenderTargetView RenderTarget { get; set; }
public static Device Device { get { return mDevice; } protected set { mDevice = value; } }
public static DeviceContext Context { get { return Device.ImmediateContext; } }
protected SwapChain SwapChain { get { return mSwapChain; } set { mSwapChain = value; } }
public Texture2D Texture { get; protected set; }
protected int RenderTargetIndex { get; set; }
protected VertexShader VertexShader { get; set; }
protected PixelShader PixelShader { get; set; }
protected Buffer VertexBuffer { get; set; }
protected Buffer MatrixBuffer { get; set; }
protected InputLayout Layout { get; set; }
protected ShaderSignature InputSignature { get; set; }
protected SamplerState SamplerState { get; set; }
protected Color4 mClearColor = new Color4(0.117f, 0.117f, 0.117f);
protected Color4 ClearColor { get { return mClearColor; } }
protected void CreateDevice(IntPtr inHandle)
{
if (Device == null)
Device = new Device(DriverType.Hardware, DeviceCreationFlags.Debug);
SwapChainDescription chainDescription = new SwapChainDescription()
{
BufferCount = 2,
Usage = Usage.RenderTargetOutput,
OutputHandle = inHandle,
IsWindowed = true,
ModeDescription = new ModeDescription(0, 0, new Rational(60, 1), Format.R8G8B8A8_UNorm),
SampleDescription = new SampleDescription(8, 0),
Flags = SwapChainFlags.AllowModeSwitch,
SwapEffect = SwapEffect.Discard
};
SwapChain = new SwapChain(Device.Factory, Device, chainDescription);
}
protected void SetupViewport(int inWidth, int inHeight)
{
Viewport viewport = new Viewport(0.0f, 0.0f, inWidth, inHeight);
Context.OutputMerger.SetTargets(RenderTarget);
Context.Rasterizer.SetViewports(viewport);
}
public void Clear()
{
Context.ClearRenderTargetView(RenderTarget, ClearColor);
}
public void Present()
{
SwapChain.Present(0, PresentFlags.None);
}
// I do this to ensure the texture is correct
public void Save()
{
Texture2D.ToFile(Context, Texture, ImageFileFormat.Png, "test.png");
}
public virtual void Dispose()
{
Texture.Dispose();
SamplerState.Dispose();
VertexBuffer.Dispose();
Layout.Dispose();
InputSignature.Dispose();
VertexShader.Dispose();
PixelShader.Dispose();
RenderTarget.Dispose();
SwapChain.Dispose();
Device.Dispose();
}
public class RenderTargetParameters
{
public int Width { get; set; }
public int Height { get; set; }
public IntPtr Handle { get; set; }
public RenderTargetParameters()
{
Width = 0;
Height = 0;
Handle = new IntPtr(0);
}
}
public abstract void Render(int inWidth, int inHeight, int inCount = -1);
public abstract void Prepare(string inShaderName = null);
}
public class TextureRenderer : Renderer
{
public TextureRenderer(RenderTargetParameters inParms)
{
CreateDevice(inParms.Handle);
Texture2DDescription description = new Texture2DDescription()
{
Width = inParms.Width,
Height = inParms.Height,
MipLevels = 1,
ArraySize = 1,
Format = Format.R8G8B8A8_UNorm,
SampleDescription = new SampleDescription(8, 0),
Usage = ResourceUsage.Default,
BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None
};
Texture = new Texture2D(Device, description);
RenderTarget = new RenderTargetView(Device, Texture);
SetupViewport(inParms.Width, inParms.Height);
using (ShaderBytecode bytecode = ShaderBytecode.CompileFromFile("ShaderFX.fx", "VShader", "vs_5_0", ShaderFlags.Debug, EffectFlags.None))
{
InputSignature = ShaderSignature.GetInputSignature(bytecode);
VertexShader = new VertexShader(Device, bytecode);
}
// load and compile the pixel shader
InputElement[] elements = new[] { new InputElement("POSITION", 0, Format.R32G32B32_Float, 0) };
Layout = new InputLayout(Device, InputSignature, elements);
Context.InputAssembler.InputLayout = Layout;
Context.InputAssembler.PrimitiveTopology = PrimitiveTopology.LineStrip;
Context.VertexShader.Set(VertexShader);
}
public override void Prepare(string inShaderName)
{
using (ShaderBytecode bytecode = ShaderBytecode.CompileFromFile("ShaderFX.fx", inShaderName, "ps_4_0", ShaderFlags.Debug, EffectFlags.None))
PixelShader = new PixelShader(Device, bytecode);
Context.PixelShader.Set(PixelShader);
}
public void SetVertices(DataStream inShape)
{
VertexBuffer = new Buffer(Device, inShape, (int)inShape.Length, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
Context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(VertexBuffer, 12, 0));
}
public override void Render(int inWidth, int inHeight, int inCount = -1)
{
Context.Draw(inCount, 0);
}
}
public class RuntimeRenderer : Renderer
{
private ShaderResourceView ResourceView { get; set; }
public RuntimeRenderer(RenderTargetParameters inParms, ref TextureRenderer inTextureRenderer)
{
CreateDevice(inParms.Handle);
Texture = inTextureRenderer.Texture;
using (Resource resource = Resource.FromSwapChain<Texture2D>(SwapChain, 0))
RenderTarget = new RenderTargetView(Device, resource);
//using (var factory = SwapChain.GetParent<Factory>())
//factory.SetWindowAssociation(inParms.Handle, WindowAssociationFlags.IgnoreAltEnter);
}
public void Resize()
{
RenderTarget.Dispose();
SwapChain.ResizeBuffers(2, 0, 0, Format.R8G8B8A8_UNorm, SwapChainFlags.AllowModeSwitch);
using (SlimDX.Direct3D11.Resource resource = Resource.FromSwapChain<Texture2D>(SwapChain, 0))
RenderTarget = new RenderTargetView(Device, resource);
}
public override void Prepare(string inShaderName)
{
using (ShaderBytecode bytecode = ShaderBytecode.CompileFromFile("ShaderFX.fx", "TextureVertexShader", "vs_4_0", ShaderFlags.EnableStrictness, EffectFlags.None))
{
InputSignature = ShaderSignature.GetInputSignature(bytecode);
VertexShader = new VertexShader(Device, bytecode);
}
using (ShaderBytecode bytecode = ShaderBytecode.CompileFromFile("ShaderFX.fx", "TexturePixelShader", "ps_4_0", ShaderFlags.EnableStrictness, EffectFlags.None))
PixelShader = new PixelShader(Device, bytecode);
InputElement[] elements = new InputElement[2];
elements[0].SemanticName = "POSITION";
elements[0].SemanticIndex = 0;
elements[0].Format = Format.R32G32B32_Float;
elements[0].Slot = 0;
elements[0].AlignedByteOffset = 0;
elements[0].Classification = InputClassification.PerVertexData;
elements[0].InstanceDataStepRate = 0;
elements[1].SemanticName = "TEXCOORD";
elements[1].SemanticIndex = 0;
elements[1].Format = Format.R32G32_Float;
elements[1].Slot = 0;
elements[1].AlignedByteOffset = InputElement.AppendAligned;
elements[1].Classification = InputClassification.PerVertexData;
elements[1].InstanceDataStepRate = 0;
Layout = new InputLayout(Device, InputSignature, elements);
BufferDescription matrixDescription = new BufferDescription()
{
Usage = ResourceUsage.Dynamic,
SizeInBytes = sizeof(float) * 16 * 4,
BindFlags = BindFlags.ConstantBuffer,
CpuAccessFlags = CpuAccessFlags.Write,
OptionFlags = ResourceOptionFlags.None,
StructureByteStride = 0
};
MatrixBuffer = new Buffer(Device, matrixDescription);
ShaderResourceViewDescription resourceViewDescription = new ShaderResourceViewDescription()
{
Format = Texture.Description.Format,
Dimension = ShaderResourceViewDimension.Texture2DMultisampled,
MipLevels = Texture.Description.MipLevels,
MostDetailedMip = 0,
};
//Texture2D.ToFile(Context, Texture, ImageFileFormat.Png, "test.png");
ResourceView = new ShaderResourceView(Device, Texture, resourceViewDescription);
SamplerDescription samplerDescription = new SamplerDescription()
{
Filter = Filter.MinMagMipLinear,
AddressU = TextureAddressMode.Wrap,
AddressV = TextureAddressMode.Wrap,
AddressW = TextureAddressMode.Wrap,
MipLodBias = 0.0f,
MaximumAnisotropy = 1,
ComparisonFunction = Comparison.Always,
BorderColor = ClearColor,
MinimumLod = 0,
MaximumLod = 99999
};
SamplerState = SamplerState.FromDescription(Device, samplerDescription);
}
public override void Render(int inWidth, int inHeight, int inCount = -1)
{
Clear();
Billboard.SetVerteces(Device, Texture.Description.Width, Texture.Description.Height, inWidth, inHeight);
SetupViewport(inWidth, inHeight);
Context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(Billboard.Verteces, 20, 0));
Context.InputAssembler.SetIndexBuffer(Billboard.Indeces, Format.R32_UInt, 0);
Context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleStrip;
Context.InputAssembler.InputLayout = Layout;
Context.VertexShader.Set(VertexShader);
Context.PixelShader.Set(PixelShader);
Context.PixelShader.SetSampler(SamplerState, 0);
Context.VertexShader.SetConstantBuffer(MatrixBuffer, 0);
Context.PixelShader.SetConstantBuffer(MatrixBuffer, 0);
Context.PixelShader.SetShaderResource(ResourceView, 0);
Context.DrawIndexed(4, 0, 0);
Present();
}
}
}
Image 1 is what it looks like if I save the texture to a file (I scaled this down a LOT so it would fit in my post).
Image 2 is what it looks like in runtime when viewed at about a medium distance (not ideal, but not so bad)
Image 3 is what it looks like zoomed in to a county (Eww! Blocky and fuzzy!)
Image 4 is what it looks like zoomed out (where did all the borders go?)