Advertisement

DirectX12 - SharpDX - how to texture a quad?

Started by September 12, 2019 10:47 AM
4 comments, last by Jozin 5 years, 4 months ago

Hi,

I am coding a DirectX12 engine using SharpDX in C#. I have it all working, but once I add a texture to the quad, I get a error message. I can't see any mistakes in the code.

Here is the relevant code. If I comment out the texture heap part it runs fine, a colored quad, but if I uncomment it, then it crashes at CommandQueue.close(), saying there is an incorrect parameter.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GraphicsPlatform;
using SharpDX;
using SharpDX.Direct3D12;
using SharpDX.DXGI;
using DX12Plat.DataTypes;
using System.Runtime.InteropServices;

namespace DX12Plat
{
    public class DX12Platform : GraphicsPlatform.GraphicsPlatform
    {


        public D3DApp D3D = null;
        private int _wW, _wH;
        public static DX12Platform ThisPlatform = null;

        public override void InitWindow(string title, int width, int height, bool fullscreen)
        {

            ThisPlatform = this;

            _wW = width;
            _wH = height;

            D3D = new D3DApp();

            D3D.SetMetrics(width, height, title, false);

            D3D.Initialize();

            D3D.CommandList.Reset(D3D.DirectCmdListAlloc, null);

            BuildRectHeap();
            BuildRectConstantBuffers();
            BuildRectRootSignature();
            BuildRectShadersAndInputLayout();
         //   BuildRectGeo(20, 20, 200, 200);
            RectBuildPSO();

            D3D.CommandList.Close();

            D3D.CommandQueue.ExecuteCommandList(D3D.CommandList);

            D3D.FlushCommandQueue();



            base.InitWindow(title, width, height, fullscreen);



        }


        // - 2D drawing props.

        private RootSignature _RectrootSignature;
        private DescriptorHeap _cbvHeap;
        private DescriptorHeap[] _descriptorHeaps;

        private UploadBuffer<DataTypes.ObjectConstant2D> _RectobjectCB;

      

        private ShaderBytecode _rectVSByteCode;
        private ShaderBytecode _mrectPSByteCode;

        private InputLayoutDescription _inputLayout;

        private PipelineState _RectPSO;

        private Matrix _proj = Matrix.Identity;
        private Matrix _view = Matrix.Identity;

        private float _theta = 1.5f * MathUtil.Pi;
        private float _phi = MathUtil.PiOverFour;
        private float _radius = 5.0f;

        private Point _lastMousePos;

        private DescriptorHeap _tvHeap;

        public void BuildRectHeap()
        {

            var cbvHeapDesc = new DescriptorHeapDescription
            {
                DescriptorCount = 1,
                Type = DescriptorHeapType.ConstantBufferViewShaderResourceViewUnorderedAccessView,
                Flags = DescriptorHeapFlags.ShaderVisible,
                NodeMask = 0
            };
            var svHeapDesc = new DescriptorHeapDescription
            {
                DescriptorCount = 1,
                Type = DescriptorHeapType.ConstantBufferViewShaderResourceViewUnorderedAccessView,
                Flags = DescriptorHeapFlags.ShaderVisible,
                NodeMask = 0
            };
            
            
            _cbvHeap = D3D.Device.CreateDescriptorHeap(cbvHeapDesc);
            _tvHeap = D3D.Device.CreateDescriptorHeap(svHeapDesc);
         

        }

        private void BuildRectConstantBuffers()
        {
            int sizeInBytes = D3DUtil.CalcConstantBufferByteSize<DataTypes.ObjectConstant2D>();

            _RectobjectCB = new UploadBuffer<DataTypes.ObjectConstant2D>(D3D.Device, 1, true);

            var cbvDesc = new ConstantBufferViewDescription
            {
                BufferLocation = _RectobjectCB.Resource.GPUVirtualAddress,
                SizeInBytes = sizeInBytes
            };
            CpuDescriptorHandle cbvHeapHandle = _cbvHeap.CPUDescriptorHandleForHeapStart;
            D3D.Device.CreateConstantBufferView(cbvDesc, cbvHeapHandle);

        }

        private void BuildRectRootSignature()
        {
            // Shader programs typically require resources as input (constant buffers,
            // textures, samplers). The root signature defines the resources the shader
            // programs expect. If we think of the shader programs as a function, and
            // the input resources as function parameters, then the root signature can be
            // thought of as defining the function signature.

            // Root parameter can be a table, root descriptor or root constants.

            // Create a single descriptor table of CBVs.
            var cbvTable = new DescriptorRange(DescriptorRangeType.ConstantBufferView, 1, 0);
            var svTable = new DescriptorRange
            {
                RangeType = DescriptorRangeType.ShaderResourceView,
                DescriptorCount = 1,
               // OffsetInDescriptorsFromTableStart = int.MinValue,
                BaseShaderRegister = 0
            };

            // A root signature is an array of root parameters.
            var rootSigDesc = new RootSignatureDescription(RootSignatureFlags.AllowInputAssemblerInputLayout, new[]
            {
                new RootParameter(ShaderVisibility.Vertex,cbvTable),
                new RootParameter(ShaderVisibility.Pixel,svTable)

            },
            new[]
            {
                new StaticSamplerDescription(ShaderVisibility.Pixel,0,0)
                {
                    Filter = Filter.MinimumMinMagMipPoint,
                    AddressUVW = TextureAddressMode.Border
                   
                }
            }
            ) ;

            _RectrootSignature = D3D.Device.CreateRootSignature(rootSigDesc.Serialize());
        }

        private void BuildRectShadersAndInputLayout()
        {
            _rectVSByteCode = D3DUtil.CompileShader("data/platform/dx12/shader/rect2d.hlsl", "VS", "vs_5_0");
            _mrectPSByteCode = D3DUtil.CompileShader("data/platform/dx12/shader/rect2d.hlsl", "PS", "ps_5_0");

            _inputLayout = new InputLayoutDescription(new[] // TODO: API suggestion: Add params overload
            {
                new InputElement("POSITION", 0, Format.R32G32B32_Float, 0, 0),
                new InputElement("COLOR", 0, Format.R32G32B32A32_Float, 12, 0),
                new InputElement("TEXCOORD",0,Format.R32G32B32A32_Float,28,0)
            });



        }

        private MeshGeometry _RectGeo;

        private long GetRequiredIntermediateSize(SharpDX.Direct3D12.Resource destinationResource, int firstSubresource, int NumSubresources)
        {
            var desc = destinationResource.Description;
            long requiredSize;
            D3D.Device.GetCopyableFootprints(ref desc, firstSubresource, NumSubresources, 0, null, null, null, out requiredSize);
            return requiredSize;

        }

        public override Texture2D LoadTexture2D(string path)
        {

            System.Drawing.Bitmap bits = new System.Drawing.Bitmap(path);

            int w = bits.Width;
            int h = bits.Height;

            byte[] pix = new byte[w * h * 4];

            int loc = 0;

            for(int y = 0; y < h; y++)
            {
                for(int x = 0; x < w; x++)
                {
                    var col = bits.GetPixel(x, y);
                    pix[loc++] = col.R;
                    pix[loc++] = col.G;
                    pix[loc++] = col.B;
                    pix[loc++] = col.A;
                }
            }

            return CreateTexture2D(w, h, pix);

        }

        public Texture2D CreateTexture2D(int w, int h,byte[] data)
        {

            D3D.DirectCmdListAlloc.Reset();

            D3D.CommandList.Reset(D3D.DirectCmdListAlloc,null);

            var tex = new Texture2DDX12();

            var textureDesc = ResourceDescription.Texture2D(Format.R8G8B8A8_UNorm, w, h);

            SharpDX.Direct3D12.Resource texture = D3D.Device.CreateCommittedResource(new HeapProperties(HeapType.Default), HeapFlags.None, textureDesc, ResourceStates.CopyDestination);

            tex.texture = texture;
            tex.textureDesc = textureDesc;
            tex.Width = w;
            tex.Height = h;

            long uploadSize = GetRequiredIntermediateSize(texture, 0, 1);

            var textureUploadHeap = D3D.Device.CreateCommittedResource(new HeapProperties(CpuPageProperty.WriteBack, MemoryPool.L0), HeapFlags.None, ResourceDescription.Texture2D(Format.R8G8B8A8_UNorm,w,h), ResourceStates.GenericRead);


            var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
            var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(data, 0);
            textureUploadHeap.WriteToSubresource(0, null, ptr, 4 * w, data.Length);
            handle.Free();

            D3D.CommandList.CopyTextureRegion(new TextureCopyLocation(texture, 0), 0, 0, 0, new TextureCopyLocation(textureUploadHeap, 0), null);

            D3D.CommandList.ResourceBarrierTransition(texture, ResourceStates.CopyDestination, ResourceStates.PixelShaderResource);

            var srvDesc = new ShaderResourceViewDescription
            {
                Shader4ComponentMapping = D3DXUtilities.DefaultComponentMapping(),
                Format = textureDesc.Format,
                Dimension = ShaderResourceViewDimension.Texture2D,
                
              
            };
            srvDesc.Texture2D.MipLevels = 1;


        

            D3D.Device.CreateShaderResourceView(texture, srvDesc, _tvHeap.CPUDescriptorHandleForHeapStart);

            D3D.CommandList.Close();
            D3D.CommandQueue.ExecuteCommandList(D3D.CommandList);

            D3D.FlushCommandQueue();





            return tex;

        }

        private void RectBuildPSO()
        {
            var psoDesc = new GraphicsPipelineStateDescription
            {
                InputLayout = _inputLayout,
                RootSignature = _RectrootSignature,
                VertexShader = _rectVSByteCode,
                PixelShader = _mrectPSByteCode,
                RasterizerState = RasterizerStateDescription.Default(),
                BlendState = BlendStateDescription.Default(),
                DepthStencilState = DepthStencilStateDescription.Default(),
                SampleMask = int.MaxValue,
                PrimitiveTopologyType = PrimitiveTopologyType.Triangle,
                RenderTargetCount = 1,
                SampleDescription = new SampleDescription(D3D.MsaaCount, D3D.MsaaQuality),
                DepthStencilFormat = D3D.DepthStencilFormat,
                StreamOutput = new StreamOutputDescription(),
                Flags = PipelineStateFlags.None
            };
            psoDesc.RenderTargetFormats[0] = D3D.BackBufferFormat;
           

            _RectPSO = D3D.Device.CreateGraphicsPipelineState(psoDesc);
        }

        public override void BeginRun()
        {

            D3D.BeginRun();

        }

        public override void UpdateRun()
        {
            D3D.UpdateRun();



        }

        public override void BeginFrame()
        {

            //D3D.BeginFrame();


        }

        public override void Present()
        {

            // Indicate a state transition on the resource usage.
            D3D.CommandList.ResourceBarrierTransition(D3D.CurrentBackBuffer, ResourceStates.RenderTarget, ResourceStates.Present);

            // Done recording commands.
            D3D.CommandList.Close();

            // Add the command list to the queue for execution.
            D3D.CommandQueue.ExecuteCommandList(D3D.CommandList);

            // Present the buffer to the screen. Presenting will automatically swap the back and front buffers.
            D3D.SwapChain.Present(0, PresentFlags.None);


            // Wait until frame commands are complete. This waiting is inefficient and is
            // done for simplicity. Later we will show how to organize our rendering code
            // so we do not have to wait per frame.
            D3D.FlushCommandQueue();


        }

        public List<DXRect> Rects = new List<DXRect>();
        private float drawZ = 0.01f;
        public override void BeginDrawing()
        {

            Rects.Clear();
            drawZ = 0.99f;
        }

        public override void EndDrawing()
        {

            _proj = Matrix.OrthoLH(_wW, -_wH, 0, 1);
            //_proj = Matrix.Identity;


            var cb = new DataTypes.ObjectConstant2D
            {
                Proj = _proj
            };

            _RectobjectCB.CopyData(0, ref cb);

            D3D.BeginRender(_RectPSO);

            _descriptorHeaps = new DescriptorHeap[2];

            _descriptorHeaps[0] = _cbvHeap;
            _descriptorHeaps[1] = _tvHeap;




            // TODO: Make requiring explicit length optional.
            D3D.CommandList.SetDescriptorHeaps(_descriptorHeaps.Length, _descriptorHeaps);

            D3D.CommandList.SetGraphicsRootSignature(_RectrootSignature);


            D3D.CommandList.SetGraphicsRootDescriptorTable(1, _cbvHeap.GPUDescriptorHandleForHeapStart);
            D3D.CommandList.SetGraphicsRootDescriptorTable(0, _tvHeap.GPUDescriptorHandleForHeapStart);

            D3D.CommandList.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleList;

            foreach (var rect in Rects)
            {

                D3D.CommandList.SetVertexBuffer(0, rect._Geo.VertexBufferView);
                D3D.CommandList.SetIndexBuffer(rect._Geo.IndexBufferView);
        
                D3D.CommandList.DrawIndexedInstanced(rect._Geo.IndexCount, 1, 0, 0, 0);
            }

        }

        float xi = -0.5f;
        bool first = true;
        public override void DrawRect(int x, int y, int w, int h, float r, float g, float b, float a,Texture2D tex)
        {
            //   x = x - _wW / 2;
            x = x - _wW / 2;
            y = y - _wH / 2;

            xi = xi + 0.001f;


            DXRect rect = new DXRect(D3D, x, y, w, h, r, g, b, a, drawZ, tex as Texture2DDX12);
            //rect.DrawZ = drawZ;
            drawZ -= 0.001f;


            Rects.Add(rect);
        //    _proj = Matrix.OrthoOffCenterLH(_wW,0, _wH,0, 0, 1);


         


        }


    }

    public class D3DXUtilities
    {

        public const int ComponentMappingMask = 0x7;

        public const int ComponentMappingShift = 3;

        public const int ComponentMappingAlwaysSetBitAvoidingZeromemMistakes = (1 << (ComponentMappingShift * 4));

        public static int ComponentMapping(int src0, int src1, int src2, int src3)
        {

            return ((((src0) & ComponentMappingMask) |
            (((src1) & ComponentMappingMask) << ComponentMappingShift) |
                                                                (((src2) & ComponentMappingMask) << (ComponentMappingShift * 2)) |
                                                                (((src3) & ComponentMappingMask) << (ComponentMappingShift * 3)) |
                                                                ComponentMappingAlwaysSetBitAvoidingZeromemMistakes));
        }

        public static int DefaultComponentMapping()
        {
            return ComponentMapping(0, 1, 2, 3);
        }

        public static int ComponentMapping(int ComponentToExtract, int Mapping)
        {
            return ((Mapping >> (ComponentMappingShift * ComponentToExtract) & ComponentMappingMask));
        }
    }
}

 

Thanks, Antony.

Or if no one knows, can you point me to any DX12 tutorials you know? Preferably c# but any other language is ok.

 

Specifically, ones that cover both constant buffers and textures at the same time?

Thanks, Antony.

Advertisement

Hi, custom SharpDX engine too but DX11 only I'm afraid.

Sorry I'm probably pointing the obvious but did you try to run the samples on GitHub?
https://github.com/sharpdx/SharpDX-Samples/tree/master/Desktop/Direct3D12/HelloTexture

Good luck!

yes I can get that to work, or I can get a demo to work with constant buffers, but when i combine them is the problem, as no tutorial i've found does so, and my attempts failed.

 

thanks for the link though.

Thanks, Antony.

dude its very good new for us all, sharpdx has not actually dead yet, such great news, Im always wondering how we being a game programmers just using another engines to  translate our work into this world instead of just write some game usign plain C add some monsters and just take over the world. But I know now that its not the old good times when you was able to just take some idea and implement it, because now its all about how much time it will take from start to finish

Jeronimo!

This topic is closed to new replies.

Advertisement