4.10 How to draw shapes by using multiple PipelineStates in Direct3D12?

  In the lastest SOL9.2.0 library, we have added new classes to support Direct3D12 on Windows 10 to 'sol/direct3d12' folder. We have implemented the Direct3D12 in a similar way of our Direct3D11 classes to keep compatibilies between them.
For example, we have defined ConstantBufferView, DepthStencilView and RenderTargetView in Direct3D12 as in the case of Direct3D11.

Direct3D12Object
Direct3D12DeviceChild
Direct3D12Pageable
Direct3D12DescriptorHeap
Direct3D12View
Direct3D12ConstantBufferView
Direct3D12DepthStencilView
Direct3D12RenderTargetView
We have also implemented Direct3D12BarrieredGraphicsCommandList class derived from Direct3D12GraphicsCommandList. It contains CommandAllocator and ResourceBarrier objects to simplify the processing of startRendering and endRendering.

Direct3D12Object
Direct3D12DeviceChild
Direct3D12CommandList
Direct3D12GraphicsCommandList
Direct3D12BarrieredGraphicsCommandList

  The following Direct3D12OrbitersWithMultiplePipelineStates is a very simple sample program to draw two orbiters, a wireframed sphere and a solid torus, moving on a circular orbit by using two PipelineState objects, and TimerThread.



/*
 * Direct3D12OrbitersWithMultiplePipelineStates.cpp 
 * Copyright (c) 2016 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED. 
 */


// 2016/10/30

#define COMMONCONTROLS_V6
#define WIN10
#include <sol/CriticalSection.h>

#include <sol/direct3d12/DirectX3D12MainView.h>
#include <sol/direct3d12/DirectX3D12View.h>
#include <sol/direct3d12/DirectXMatrix.h>
#include <sol/direct3d12/Direct3D12Debug.h>
#include <sol/direct3d12/Direct3D12CommandAllocator.h>
#include <sol/direct3d12/Direct3D12RenderTargetView.h>
#include <sol/direct3d12/Direct3D12DepthStencilView.h>
#include <sol/direct3d12/Direct3D12CommonDescriptorHeap.h>

#include <sol/direct3d12/Direct3D12TransformConstantBufferView.h>

#include <sol/direct3d12/Direct3D12RootSignature.h>
#include <sol/direct3d12/Direct3D12BarrieredGraphicsCommandList.h>

#include <sol/direct3d12/Direct3D12PipelineState.h>

#include <sol/direct3d12/D3D12RasterizerDesc.h>
#include <sol/direct3d12/D3D12BlendDesc.h>
#include <sol/direct3d12/D3D12GraphicsPipelineStateDesc.h>
#include <sol/direct3d12/D3D12ResourceBarrier.h>
#include <sol/direct3d12/D3D12Transform.h>

#include <sol/direct3d12/D3D12GraphicsPipelineStateDesc.h>
#include <sol/direct3d12/Direct3D12Synchronizer.h>
#include <sol/direct3d12/Direct3DX12Sphere.h>
#include <sol/direct3d12/Direct3DX12Torus.h>
#include <sol/direct3d12/DirectXTransform.h>
#include <sol/direct3d12/DirectX3D12TimerThread.h>

#include "resource.h"

namespace SOL {
  
class MainView :public DirectX3D12MainView {
  
  //Inner class 
  class SimpleView :public DirectX3D12View {
  private:
    static const int MAX_PIPELINE_STATE = 2;
    
    enum FILLMODE {
      FILL_MODE_WIREFRAME = 0,
      FILL_MODE_SOLID     = 1,  
    };
    
    SmartPtr<Direct3D12CommandAllocator>      commandAllocator;
    SmartPtr<Direct3D12RenderTargetView>      renderTargetView;
    SmartPtr<Direct3D12DepthStencilView>      depthStencilView;
    SmartPtr<Direct3D12RootSignature>         rootSignature;
    SmartPtr<Direct3D12CommonDescriptorHeap>  commonDescriptorHeap;
    SmartPtr<Direct3D12TransformConstantBufferView>   constantBufferView;
    SmartPtr<Direct3D12BarrieredGraphicsCommandList>  graphicsCommandList;

    SmartPtr<Direct3D12PipelineState>        pipelineState[MAX_PIPELINE_STATE];
    SmartPtr<Direct3D12Synchronizer>         synchronizer;
    SmartPtr<Direct3DX12Sphere>              sphere;
    SmartPtr<Direct3DX12Torus>               torus;
    SmartPtr<DirectX3D12TimerThread>         timerThread;
    
    int                             pipelineCount;
    UINT                            frameIndex;
        
    int                             renderingInterval;
    DirectXTransform                worldViewProjection;
    int                             angle;
    StringT<TCHAR>                  directory;

    static const int    CIRCLE_ANGLE = 360;
    
    CriticalSection                 criticalSection;

  private:

    virtual void createWireFramePipelineState(ID3D12Device* device, Direct3DX12Shape* shape)
    {
      //Create a graphicPipelineStateDes.
      D3D12GraphicsPipelineStateDesc graphicsPipelineStateDesc(*rootSignature);

      UINT count = 0;
      const D3D12_INPUT_ELEMENT_DESC* inputElements = sphere->getInputElementDesc(count);
      
      D3D12RasterizerDesc  rasterDesc(D3D12_FILL_MODE_WIREFRAME,  D3D12_CULL_MODE_NONE,  true);
      
      D3D12BlendDesc       blendDesc;
            
      graphicsPipelineStateDesc.setInputLayput(inputElements, count);
      
      graphicsPipelineStateDesc.setRasterizerState(rasterDesc);
      graphicsPipelineStateDesc.setBlendState(blendDesc);
      
      StringT<TCHAR> vsshaderFile = getShaderFilePath(directory, _T("VertexShader.cso"));
      StringT<TCHAR> psshaderFile = getShaderFilePath(directory, _T("PixelShader.cso"));

      graphicsPipelineStateDesc.setVertexShader(vsshaderFile);
      graphicsPipelineStateDesc.setPixelShader(psshaderFile);

      pipelineState[pipelineCount++] = new Direct3D12PipelineState(device, graphicsPipelineStateDesc);
    }
    
    virtual void createSolidPipelineState(ID3D12Device* device, Direct3DX12Shape* shape)
    {
      //Create a graphicPipelineStateDes.
      D3D12GraphicsPipelineStateDesc graphicsPipelineStateDesc(*rootSignature);

      
      UINT count = 0;
      const D3D12_INPUT_ELEMENT_DESC* inputElements = sphere->getInputElementDesc(count);
      
      D3D12RasterizerDesc  rasterDesc(D3D12_FILL_MODE_SOLID, D3D12_CULL_MODE_NONE,  true);
      
      D3D12BlendDesc       blendDesc;
            
      graphicsPipelineStateDesc.setInputLayput(inputElements, count);
      
      graphicsPipelineStateDesc.setRasterizerState(rasterDesc);
      graphicsPipelineStateDesc.setBlendState(blendDesc);
      
      StringT<TCHAR> vsshaderFile = getShaderFilePath(directory, _T("VertexShader.cso"));
      StringT<TCHAR> psshaderFile = getShaderFilePath(directory, _T("PixelShader.cso"));

      graphicsPipelineStateDesc.setVertexShader(vsshaderFile);
      graphicsPipelineStateDesc.setPixelShader(psshaderFile);

      pipelineState[pipelineCount] = new Direct3D12PipelineState(device, graphicsPipelineStateDesc);
    }

    float SIN(int degree)
    {
      float radian = (float)degree * MATH_PI / 180.0f;
      return (float)sin(radian);
    }
  
    float COS(int degree)
    { 
      float radian = (float)degree * MATH_PI / 180.0f;
      return (float)cos(radian);
    }
    
    
    XMFLOAT3 getOrbitPosition(int pos)
    {
      static XMFLOAT3  vertices[CIRCLE_ANGLE] = {};
      UINT count = _countof(vertices);
      
      static bool initialized = false;
      if (!initialized) { 
        float px     = 0.0f;
        float py     = 0.0f;
        float radius = 2.0f;

        for (UINT i = 0; i<count - 1; i += 2) {
          vertices[i    ] = XMFLOAT3(px + radius * COS(i    ), py + radius * SIN(i    ), 0.5f);
          vertices[i + 1] = XMFLOAT3(px + radius * COS(i + 1), py + radius * SIN(i + 1), 0.5f);
        }
        initialized = true;
      }
      
      if (pos > CIRCLE_ANGLE) {
        pos = 0;
      }
      return vertices[pos];          
    }

        
    void setDirectXTransform()
    {
      int width  = 0;
      int height = 0;
      getClientSize(width, height);

      try {        
        XMVECTOR eye = XMVectorSet(  2.0f,  3.0f, -6.0f,  0.0f );
        XMVECTOR at  = XMVectorSet(  0.0f,  0.0f,  0.0f,  0.0f );
        XMVECTOR up  = XMVectorSet(  0.0f,  1.0f,  0.0f,  0.0f );

        worldViewProjection.world      = DirectXMatrix::rotationY( (float)angle );
        worldViewProjection.view       = DirectXMatrix::lookAtLH(eye, at, up);
        worldViewProjection.projection = DirectXMatrix::perspectiveFovLH( XM_PIDIV2*0.3f, 
          width / (FLOAT)height, 0.01f, 100.0f );
       
        if (constantBufferView != nullptr) {
          constantBufferView->update(worldViewProjection);
        }
      } catch (Exception& ex) {
        caught(ex);      
      }
    }
    
  public:
    //Create a renderTargetView and a depthStencilView.
    void createViews(ID3D12Device* device, IDXGISwapChain3* swapChain,
                  int width, int height)
    {
      renderTargetView = new Direct3D12RenderTargetView(device, swapChain);
      depthStencilView = new Direct3D12DepthStencilView(device,  width, height);
    }
    
    //Delete a renderTargetView and a depthStencilView.
    void deleteViews()
    {
      graphicsCommandList->setOMRenderTargets(0, nullptr, FALSE, nullptr);
      renderTargetView = NULL;
      depthStencilView = NULL;
    }
    
    virtual void initialize()
    {
      int width  = 0;
      int height = 0;
      validateClientSize(width, height);
      Direct3D12Device*    device    = getD3D12Device();
      DirectXGISwapChain3* swapChain = getSwapChain3();
      Direct3D12CommandQueue* commandQueue = getCommandQueue();
      
      try {

        commandAllocator         = new Direct3D12CommandAllocator(*device);

        renderTargetView         = new Direct3D12RenderTargetView(*device, *swapChain);

        depthStencilView         = new Direct3D12DepthStencilView(*device,  width, height);
          
        graphicsCommandList      = new Direct3D12BarrieredGraphicsCommandList(*device, *commandAllocator);
        
        rootSignature            = new Direct3D12RootSignature(*device);
        
        commonDescriptorHeap     = new Direct3D12CommonDescriptorHeap(*device, 1);

        constantBufferView       = new Direct3D12TransformConstantBufferView(*device,
                      commonDescriptorHeap->getCPUHandle(CBV_HANDLE) );

        synchronizer            = new Direct3D12Synchronizer(*device, *commandQueue);

        //1 Create a renderTargetView and depthStencilView
        createViews(*device, *swapChain, width, height);
        
        //2 Define your own specific initialization procedures.
        // Create your own shape
        sphere                     = new Direct3DX12Sphere(*device, 0.5f, 20, 20);
        
        torus = new Direct3DX12Torus(*device, 0.2f, 1.0f, 30, 30);
        
        //3 Create two pipelineState(wireframe and solid) from the sphere. 
        createWireFramePipelineState(*device, sphere);
        
        createSolidPipelineState(*device, sphere);

        //4 Set world, view and projection to the constantBuffer.
        setDirectXTransform();

        //5 Create a rendering TimerThread.
        timerThread = new DirectX3D12TimerThread(this, renderingInterval);
        timerThread -> start();

      } catch (Exception& ex) {
        caught(ex);
      }
    }

    bool ready()
    {
      Direct3D12Device*    device          = getD3D12Device();
      DirectXGISwapChain3* swapChain       = getSwapChain3();
      Direct3D12CommandQueue* commandQueue = getCommandQueue();
      
      if (
        device                    == nullptr ||
        commandQueue              == nullptr ||
        swapChain                 == nullptr ||
        
        commandAllocator          == nullptr ||
        renderTargetView          == nullptr ||
        depthStencilView          == nullptr ||

        rootSignature             == nullptr ||
        commonDescriptorHeap      == nullptr ||
        constantBufferView        == nullptr ||
        sphere                    == nullptr ||
        torus                     == nullptr ||

        graphicsCommandList       == nullptr ||
        pipelineState             == nullptr ||
        timerThread               == nullptr ||
        synchronizer              == nullptr ) {

        return false;
      }
      return true;
    }
    
    virtual void display()
    {
      int width  = 0;
      int height = 0;
      validateClientSize(width, height);
      if ( !ready() ) {
        return;
      }
      criticalSection.enter();
      
      try {
        DirectXGISwapChain3*    swapChain = getSwapChain3();
        Direct3D12CommandQueue* commandQueue = getCommandQueue();
        static const int INCREMENT = 2;
        if (angle <(CIRCLE_ANGLE - INCREMENT) ) {
          angle += INCREMENT;
        }
        else {
          angle = INCREMENT;
        }
        XMFLOAT3 position = getOrbitPosition(angle);
        
        DirectXMatrix world = DirectXMatrix::scaling( 1.0f, 1.0f, 1.0f);
        DirectXMatrix m = DirectXMatrix::translation(position.x, position.y, position.z);
        world += m;
 
        worldViewProjection.world = world;
    
        constantBufferView->update(worldViewProjection);
        
        frameIndex = swapChain -> getCurrentBackBufferIndex();
        
        graphicsCommandList->startRendering(renderTargetView->getResource(frameIndex));

        graphicsCommandList->setDescriptorHeap(*commonDescriptorHeap);

        graphicsCommandList->setGraphicsRootSignature(*rootSignature);

        graphicsCommandList->setPipelineState(*pipelineState[FILL_MODE_WIREFRAME]);
        
        graphicsCommandList->setGraphicsRootDescriptorTable(CBV_HANDLE, 
                commonDescriptorHeap->getGPUHandle(CBV_HANDLE));

        graphicsCommandList-> setRSViewport(0, 0, width, height);

        graphicsCommandList-> setRSScissorRect(0, 0, width, height);

        D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = renderTargetView->getHandle(frameIndex);
                 
        graphicsCommandList->clearRenderTargetView(rtvHandle, XMColor(0.2f, 0.2f, 0.6f, 1.0f));

        D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = *depthStencilView;
        
        graphicsCommandList->clearDepthStencilView(dsvHandle, D3D12_CLEAR_FLAG_DEPTH);
        
        graphicsCommandList->setOMRenderTargets(1, &rtvHandle, FALSE, &dsvHandle);
        
        //Draw the sphere on the worldViewProjection by wireFrame mode.
        sphere -> drawIndexedInstanced(graphicsCommandList);
       
        //Draw the torus on the worldViewProjection by solid mode.
        graphicsCommandList->setPipelineState(*pipelineState[FILL_MODE_SOLID]);
 
        torus->drawIndexedInstanced(graphicsCommandList);

        graphicsCommandList->endRendering();
          
        commandQueue->executeCommandList(*graphicsCommandList); //*graphicsCommandList);

        swapChain->present(1, 0);
        
        synchronizer->waitForCompletion();
       
      } catch (Exception& ex) {
        caught(ex);
      }
      criticalSection.leave();
    }
    
    
    virtual void resize(int width, int height)
    {
      Direct3D12Device*   device = getD3D12Device();
      DirectXGISwapChain3* swapChain = getSwapChain3();
      if (device           == NULL || 
         swapChain        == NULL) { 
        return ;
      }
      
      try {
        deleteViews();
        
        swapChain->resizeBuffers(width, height);

        createViews(*device, *swapChain, width, height);
        
      } catch (Exception& ex) {
        caught(ex);
      }
    }
    
  public:
    //Constructor
    SimpleView(DirectX3D12MainView* parent, const TCHAR* name, Args& args)
    :DirectX3D12View(parent, name, args),
    angle (30),
    frameIndex(0),
    directory(_T("")),
    pipelineCount(0),
    renderingInterval(100)
    {
      directory = (const TCHAR*)args.get(XmNapplicationDirectory);
      renderingInterval = (int)args.get(XmNrenderingInterval);
      postResizeRequest();
    }
    
    ~SimpleView()
    {
    }
  };
  // Inner class ends.
  
private:
  SmartPtr<SimpleView> view;

public:
  /**
   * Constructor
   */
  MainView(Application& applet, const TCHAR* name, Args& args)
  :DirectX3D12MainView(applet, name,
                 args.set(XmNstyle, (ulong)WS_CLIPCHILDREN|WS_CLIPSIBLINGS) ),
  view(NULL)
  {
    const TCHAR* directory = (const TCHAR*)args.get(XmNapplicationDirectory);
    int renderingInterval  = (int) args.get(XmNrenderingInterval);

    // 1 Restore the replacement of MainView 
    restorePlacement();

    // 2 Create a view of SimpleView.
    Args ar;
    int width  = 0;
    int height = 0;
    getClientSize(width, height);

    ar.set(XmNwidth, width);
    ar.set(XmNheight,height);
    ar.set(XmNapplicationDirectory, (const TCHAR*)directory);
    ar.set(XmNrenderingInterval, renderingInterval); 

    ar.set(XmNstyle, WS_BORDER|WS_CHILD|WS_VISIBLE);
    view = new SimpleView(this, _T(""), ar);

    // 3 Post a resize request to this MainView.
    postResizeRequest();
  }

public:
  ~MainView()
  {
  }

  void resize(int width, int height)
  {
    if (view != nullptr) {
      view -> reshape(0, 0, width, height);      
    }
  }  
};
}


//////////////////////////////////////////////
//
void  Main(int argc, TCHAR** argv)
{
  TCHAR directory[MAX_PATH];
  appDirectory(argv[0], directory, CountOf(directory));

  const TCHAR* appClass = appName(argv[0]); 
  try {    
    Application applet(appClass, argc, argv);

    Args args;
    args.set(XmNwidth,  480);
    args.set(XmNheight, 480);
    args.set(XmNapplicationDirectory, directory);
    args.set(XmNrenderingInterval, 100); //100ms

    MainView mainView(applet, appClass, args);
    mainView.realize();

    applet.run();
  } catch (Exception& ex) {
    caught(ex);
  }
}

Last modified: 30 Oct 2016

Copyright (c) 2016 Antillia.com ALL RIGHTS RESERVED.