SOL9 Sample: Direct3D11On12TextLayout

SOL9 2.0 Samples

1 Screenshot


2 Source code

/*
 * Direct3D11On12TextLayout.cpp 
 * Copyright (c) 2015 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED. 
 */


// 2016/11/29
// This is based on the sample program in
// https://github.com/Microsoft/DirectX-Graphics-Samples
// See: DirectX-Graphics-Samples-master\Samples\Desktop\D3D1211On12

// You can rotate a torus by the left and right keys, without changing a text string.
  
// 2017/01/28 Updated to use ModuleFileName and caught macro.

#define COMMONCONTROLS_V6
#define WIN10

#include <sol/direct3d12/DirectX3D12MainView.h>
#include <sol/direct3d12/DirectXMatrix.h>
#include <sol/direct3d12/Direct3D11On12Device.h>
#include <sol/direct3d12/Direct3D11On12WrappedResource.h>
#include <sol/direct3d12/Direct3D12CommandAllocator.h>
#include <sol/direct3d12/Direct3D12RenderTargetView.h>
#include <sol/direct3d12/Direct3D12DepthStencilView.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/D3D12Transform.h>
#include <sol/direct3d12/D3D12GraphicsPipelineStateDesc.h>
#include <sol/direct3d12/Direct3D12Synchronizer.h>

#include <sol/direct3d12/Direct3DX12Torus.h>
#include <sol/direct3d12/DirectXTransform.h>
#include <sol/direct3d12/Direct3D12CommonDescriptorHeap.h>

#include <sol/directx/Direct2D1Factory3.h>
#include <sol/directx/Direct2D1Device2.h>
#include <sol/directx/Direct2D1DeviceContext2.h>
#include <sol/directx/Direct2D1Bitmap1.h>
#include <sol/directx/Direct2D1RenderTarget.h>
#include <sol/directx/Direct2D1SolidColorBrush.h>
#include <sol/directx/DirectWriteFactory.h> 
#include <sol/directx/DirectWriteTextFormat.h>
#include <sol/directx/DirectWriteTextLayout.h>
#include <sol/dxgi/DirectXGIDevice1.h>

#include "resource.h"

namespace SOL {
  
class MainView :public DirectX3D12MainView {
private:
  static const int FRAME_COUNT = 3;
  
private:
  //Inner class starts.
  //-----------------------------------------------------------------------------------------
  class DirectXTextRenderer : public  DirectXGIDevice1 {
    
  private:    
    static const int               LAYOUT_WIDTH  = 280;
    static const int               LAYOUT_HEIGHT = 280;
      SmartPtr<Direct2D1Device2>              d2d1Device2;
    SmartPtr<Direct2D1DeviceContext2>       d2d1DeviceContext2;
    
    SmartPtr<Direct3D11On12WrappedResource> wrappedBackBuffers[FRAME_COUNT];
    SmartPtr<Direct2D1Bitmap1>              d2d1RenderTargets1[FRAME_COUNT];
    SmartPtr<Direct2D1Factory3>             d2d1Factory3;
    SmartPtr<DirectWriteFactory>            writeFactory;
    SmartPtr<DirectWriteTextFormat>         textFormat;
    SmartPtr<DirectWriteTextLayout>         textLayout;
    SmartPtr<Direct2D1SolidColorBrush>      colorBrush;

    StringT<wchar_t>               text;

    void createWrappedBackBuffers(ID3D11On12Device* d3d11On12Device,
                              Direct3D12RenderTargetView* renderTargetView)
    {
    try {
      float dpiX;
      float dpiY;
      d2d1Factory3->getDesktopDpi(&dpiX, &dpiY);

      for (UINT n = 0; n < FRAME_COUNT; n++) {
        wrappedBackBuffers[n] = new Direct3D11On12WrappedResource(d3d11On12Device, 
                renderTargetView->getResource(n));
        IDXGISurface* surface = nullptr;
        wrappedBackBuffers[n]->queryInterface(IID_PPV_ARGS(&surface));
        
        d2d1RenderTargets1[n] = new Direct2D1Bitmap1(*d2d1DeviceContext2, surface,
              dpiX,
              dpiY);
        surface->Release();
      }
    }
    catch (Exception& ex) {
      caught(ex);
    }
  }
    
  public:
    //
    //Inner class constructor
    DirectXTextRenderer(ID3D11On12Device* d3d11On12Device,
                  Direct3D12RenderTargetView* renderTargetView,
                  const wchar_t* string)
    :DirectXGIDevice1(d3d11On12Device),
    text(string)
    {
      d2d1Factory3       = new Direct2D1Factory3();
             
      d2d1Device2        = new Direct2D1Device2(*d2d1Factory3, *this);
  
      d2d1DeviceContext2 = new Direct2D1DeviceContext2(*d2d1Device2);

      createWrappedBackBuffers(d3d11On12Device, renderTargetView);

      writeFactory      = new DirectWriteFactory();
      textFormat           = new DirectWriteTextFormat(*writeFactory,
                             L"Gabriola",
                             NULL,
                             DWRITE_FONT_WEIGHT_NORMAL,
                             DWRITE_FONT_STYLE_NORMAL,
                             DWRITE_FONT_STRETCH_NORMAL,
                             40.0f,  
                             L"");      

      textFormat -> setTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
      textFormat -> setParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
        
      textLayout = new DirectWriteTextLayout(*writeFactory,
                             (const wchar_t*)text,
                              text.getLength(),
                              *textFormat,
                              (float)LAYOUT_WIDTH,
                              (float)LAYOUT_HEIGHT);
    
      const wchar_t* dragon = L"dragon";
      int pos = text.findPosition(dragon);

      DWRITE_TEXT_RANGE textRange = {pos, wcslen(dragon) }; //"dragon" range in text
      textLayout -> setFontSize(80.0f, textRange);
      textLayout -> setUnderline(TRUE, textRange);
      
      colorBrush = new Direct2D1SolidColorBrush(*d2d1DeviceContext2,
                    D2D1::ColorF(D2D1::ColorF::Red));
    }

    ~DirectXTextRenderer()
    {
    }

    void drawLayout(int frameIndex)
    {
      try {
        D2D1_SIZE_F rtSize   = d2d1RenderTargets1[frameIndex]->getSize();
        D2D1_RECT_F textRect = D2D1::RectF(0, 0, rtSize.width, rtSize.height);
     
        wrappedBackBuffers[frameIndex]->acquire();
        d2d1DeviceContext2->setTarget(*d2d1RenderTargets1[frameIndex]);
      
        d2d1DeviceContext2->beginDraw();
        d2d1DeviceContext2->setTransform(D2D1::Matrix3x2F::Identity());

        D2D1_POINT_2F origin = D2D1::Point2F(0, 0);
        d2d1DeviceContext2->drawTextLayout(
            origin,
            *textLayout,
            *colorBrush
          );
        
        d2d1DeviceContext2->endDraw();
        wrappedBackBuffers[frameIndex]->release();
      }
      catch (Exception& ex) {
        caught(ex);
      }
    }    
  };
  //Inner class ends.
  //-----------------------------------------------------------------------------------------
    
private:

  SmartPtr<Direct3D12CommandQueue>         commandQueue;
  SmartPtr<Direct3D12CommandAllocator>     commandAllocators[FRAME_COUNT]; 
  SmartPtr<DirectXGISwapChain3>            swapChain;
  SmartPtr<Direct3D12RenderTargetView>     renderTargetView;
  SmartPtr<Direct3D12DepthStencilView>     depthStencilView;
  SmartPtr<Direct3D12RootSignature>        rootSignature;
  SmartPtr<Direct3D12CommonDescriptorHeap>  commonDescriptorHeap;
  SmartPtr<Direct3D12TransformConstantBufferView>   constantBufferView;
  SmartPtr<Direct3D12GraphicsCommandList> graphicsCommandList;
  SmartPtr<Direct3D12PipelineState>       pipelineState;
  SmartPtr<Direct3D12Synchronizer>        synchronizer;
  SmartPtr<Direct3DX12Torus>              sphere;
  UINT                                    frameIndex;
  DirectXTransform                        worldViewProjection;
  float                                   angle;
  StringT<TCHAR>                          directory;
  
  SmartPtr<Direct3D11On12Device>          d3d11On12Device;
  SmartPtr<DirectXTextRenderer>           textRenderer;
    
public:
  
  virtual void createPipelineState(ID3D12Device* device)
  {
    //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 = new Direct3D12PipelineState(device, graphicsPipelineStateDesc);
  }

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

    try {
      XMVECTOR eye = XMVectorSet(0.0f, 2.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(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:
  void deleteViews()
  {
    if (graphicsCommandList) {
      graphicsCommandList->setOMRenderTargets(0, nullptr, FALSE, nullptr);
    }
      renderTargetView = nullptr;
      depthStencilView = nullptr;
  }

  virtual void createViews()
  {
    int width = 0;
    int height = 0;
    validateClientSize(width, height);

    try {      
      Direct3D12Device*   device  = getD3D12Device();      
      DirectXGISwapChain3* swapChain = getSwapChain3();

      renderTargetView = new Direct3D12RenderTargetView(*device, *swapChain, FRAME_COUNT);
      depthStencilView = new Direct3D12DepthStencilView(*device, width, height);

    } catch (Exception& ex) {
      caught(ex);
    }
  }
  
  DirectXGISwapChain3* getSwapChain3()
  {
    return swapChain;
  }
  
  Direct3D12CommandQueue* getCommandQueue()
  {
    return commandQueue;
  }
  
  virtual void initialize()
  {
    int width  = 0;
    int height = 0;
    validateClientSize(width, height);
    
    DirectXGIFactory4* factory = getDXGIFactory4(); //From DirectX3D12MainView

    Direct3D12Device*  device  = getD3D12Device();  //From DirectX3D12MainView

    try {
      commandQueue             = new Direct3D12CommandQueue(*device);
  
      for (int i = 0; i < FRAME_COUNT; i++) {
        commandAllocators[i] = new Direct3D12CommandAllocator(*device);
      }

      swapChain                = new DirectXGISwapChain3(*factory, *commandQueue, FRAME_COUNT,
                                      getWindow(), width, height, false);
       
      d3d11On12Device          =  new Direct3D11On12Device(*device, *commandQueue);

      createViews();
      
      textRenderer             = new DirectXTextRenderer(*d3d11On12Device, renderTargetView,
                                   L"The dragon wing of night o'erspreads the earth.");
        
      graphicsCommandList      = new Direct3D12GraphicsCommandList(*device, *commandAllocators[0]);
        
      rootSignature            = new Direct3D12RootSignature(*device);

      commonDescriptorHeap     = new Direct3D12CommonDescriptorHeap(*device, 1);

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

      synchronizer             = new Direct3D12Synchronizer(*device, *commandQueue);
   
      sphere                   = new Direct3DX12Torus(*device, 0.2f, 0.5f, 20, 20);
                
      createPipelineState(*device);

      setDirectXTransform();

    } catch (Exception& ex) {
      caught(ex);
    }
  }
  
  bool ready()
  {
    Direct3D12Device*    device          = getD3D12Device();
    DirectXGISwapChain3* swapChain       = getSwapChain3();
    Direct3D12CommandQueue* commandQueue = getCommandQueue();
      
    if (
        commandQueue          == nullptr ||
        swapChain             == nullptr ||
        renderTargetView      == nullptr ||
        depthStencilView      == nullptr ||
        rootSignature         == nullptr ||
        commonDescriptorHeap  == nullptr ||
        constantBufferView    == nullptr ||
        graphicsCommandList   == nullptr ||
        pipelineState         == nullptr ||
        synchronizer          == nullptr ||
        sphere                == nullptr ||
        textRenderer          == nullptr) {

      return false;
    }
    return true;
  }
  
  virtual void display()
  {
    int width  = 0;
    int height = 0;
    validateClientSize(width, height);
    if ( !ready() ) {
      return;
    }
    
    try {
        DirectXGISwapChain3*    swapChain = getSwapChain3();
        Direct3D12CommandQueue* commandQueue = getCommandQueue();
        
        setDirectXTransform();

        frameIndex = swapChain -> getCurrentBackBufferIndex();

        commandAllocators[frameIndex]->reset();

        graphicsCommandList->reset(*commandAllocators[frameIndex], nullptr);

        D3D12ResourceBarrier barrier(renderTargetView->getResource(frameIndex));

        barrier.startRendering();

        graphicsCommandList->resourceBarrier(1, barrier);

        graphicsCommandList->setDescriptorHeap(*commonDescriptorHeap);

        graphicsCommandList->setGraphicsRootSignature(*rootSignature);

        graphicsCommandList->setPipelineState(*pipelineState);
        
        graphicsCommandList->setGraphicsRootDescriptorTable(0, 
                commonDescriptorHeap->getGPUHandle(0));

        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.0f, 0.0f, 0.0f, 1.0f));

        D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = *depthStencilView;
        
        graphicsCommandList->clearDepthStencilView(dsvHandle, D3D12_CLEAR_FLAG_DEPTH);
        
        graphicsCommandList->setOMRenderTargets(1, &rtvHandle, FALSE, &dsvHandle);
       
        sphere->drawIndexedInstanced(graphicsCommandList);

        barrier.endRendering();

        graphicsCommandList->resourceBarrier(1, barrier);

        graphicsCommandList->close();

        commandQueue->executeCommandList(*graphicsCommandList);

        textRenderer -> drawLayout(frameIndex);

        d3d11On12Device->flush();
      
        swapChain->present(1, 0);
        
        synchronizer->waitForCompletion();
       
    } catch (Exception& ex) {
        caught(ex);
    }
  }
    
public:
  /**
   * Constructor
   */
  MainView(Application& applet, const TCHAR* name, Args& args)
  :DirectX3D12MainView(applet, name,
                 args.set(XmNstyle, (ulong)WS_CLIPCHILDREN) )
  {
    directory = (const TCHAR*)args.get(XmNapplicationDirectory);
    angle = 1.0f;
    restorePlacement();

    postResizeRequest(); 
  }

public:
  ~MainView()
  {
  }

private:

  virtual void resize(int width, int height)
  {
    Direct3D12Device*   device = getD3D12Device();
    DirectXGISwapChain3* swapChain = getSwapChain3();
    if (device           == nullptr || 
        swapChain        == nullptr) { 
      return ;
    }
      
    try {
      deleteViews();
      
      swapChain->resizeBuffers(width, height);
      
      createViews();
    } catch (Exception& ex) {
      caught(ex);
    }
  }
  
  virtual void keyDown(Event& event)
  {
    WPARAM wparam = event.getWParam();
    switch(wparam) {
    case VK_LEFT:
        angle += 0.1f;
        display();
        break;
        
    case VK_RIGHT:
        angle -= 0.1f;
        display();
        break;
    }  
  }
};

}


//////////////////////////////////////////////
//
void  Main(int argc, TCHAR** argv)
{
  ModuleFileName module(argv[0]);
  const TCHAR* directory = module.getDirectory();
  const TCHAR* appClass =  module.getAppName(); 

  try {    
    Application applet(appClass, argc, argv);

    Args args;
    args.set(XmNwidth,  700);
    args.set(XmNheight, 480);
    args.set(XmNapplicationDirectory, directory);

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

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


Last modified: 1 Feb 2017

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