2
votes

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?)

enter image description hereenter image description hereenter image description hereenter image description here

1
It will be easier to help you if you show us the render results you get, to visualize the problem. For now, my only thought about improving the quality of your texture qould be changing the Filter from MinMagMipLinear to Anisotropic.Elvithari
Ok, look at my edits with the images.KairisCharm
Ok, test how these textures behave when you change the Filter in the SamplerDescription. It can change how the texture appear greatly.Elvithari

1 Answers

1
votes

About multisampling, generally you can keep quality to 0, quality setting generally are different "subpixels" (aka : samples) patterns. 0 generally does fine.

In case you render to texture with multisampling, you also need to resolve your resource, multi sampled textures are bound as Texture2DMS (instead of Texture2D) in shaders.

To do so, you need to create a second texture (with same format/size), but with only one sample.

Then once you're done rendering your multisampled texture, you need to do the following call:

deviceContext.ResolveSubresource(multisampledtexture, 0, nonmultisampledtexture,
                0, format);

You can then use the ShaderView of the non multisampled texture in subsequent passes.

From what I see you should not need to use a depth stencil, just make sure you draw your elements in the correct order.

About formats, this is normal since depth is a bit "special", you need to pass different formats for resource/views. If you want to use D24_UNorm_S8_UInt (most common format i'd say), you need to setup the following:

  • In the texture description, format needs to be Format.R24_UNorm_X8_Typeless
  • In the Depth Stencil view description, Format.D24_UNorm_S8_UInt
  • In the shader view description, Format.R24_UNorm_X8_Typeless

That will allow you to build a depth stencil that you can read (if you don't need to read your depth buffer, just ignore shader view and use depth format directly).

Also you can increase quality by using mipmaps (which would help a lot, specially when zooming out).

To do so, in your texture description, set the following options (make sure that this texture is not multisampled)

texBufferDesc.OptionFlags |= ResourceOptionFlags.GenerateMipMaps;
texBufferDesc.MipLevels = 0; //0 means "all"

once you're done with your rendering, call:

context.GenerateMips

using the shader resource view of the texture that just got rendered.

About drawing the lines directly behind that's definitely possible, and for certain will give you the best quality.

Not sure how many lines you render, but it doesn't look like something a reasonably modern card would struggle with. And a bit of culling can easily help discard lines that are out of the screen so they don't get drawn.

You could also do some "hybrid" (use texture when zoomed out, render a subset of the lines when zoomed in), that's not too hard to setup either.