home *** CD-ROM | disk | FTP | other *** search
/ Introduction to 3D Game …ogramming with DirectX 12 / Introduction-to-3D-Game-Programming-with-DirectX-12.ISO / Code.Textures / Common / d3dApp.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2016-03-02  |  19.0 KB  |  669 lines

  1. //***************************************************************************************
  2. // d3dApp.cpp by Frank Luna (C) 2015 All Rights Reserved.
  3. //***************************************************************************************
  4.  
  5. #include "d3dApp.h"
  6. #include <WindowsX.h>
  7.  
  8. using Microsoft::WRL::ComPtr;
  9. using namespace std;
  10. using namespace DirectX;
  11.  
  12. LRESULT CALLBACK
  13. MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  14. {
  15.     // Forward hwnd on because we can get messages (e.g., WM_CREATE)
  16.     // before CreateWindow returns, and thus before mhMainWnd is valid.
  17.     return D3DApp::GetApp()->MsgProc(hwnd, msg, wParam, lParam);
  18. }
  19.  
  20. D3DApp* D3DApp::mApp = nullptr;
  21. D3DApp* D3DApp::GetApp()
  22. {
  23.     return mApp;
  24. }
  25.  
  26. D3DApp::D3DApp(HINSTANCE hInstance)
  27. :    mhAppInst(hInstance)
  28. {
  29.     // Only one D3DApp can be constructed.
  30.     assert(mApp == nullptr);
  31.     mApp = this;
  32. }
  33.  
  34. D3DApp::~D3DApp()
  35. {
  36.     if(md3dDevice != nullptr)
  37.         FlushCommandQueue();
  38. }
  39.  
  40. HINSTANCE D3DApp::AppInst()const
  41. {
  42.     return mhAppInst;
  43. }
  44.  
  45. HWND D3DApp::MainWnd()const
  46. {
  47.     return mhMainWnd;
  48. }
  49.  
  50. float D3DApp::AspectRatio()const
  51. {
  52.     return static_cast<float>(mClientWidth) / mClientHeight;
  53. }
  54.  
  55. bool D3DApp::Get4xMsaaState()const
  56. {
  57.     return m4xMsaaState;
  58. }
  59.  
  60. void D3DApp::Set4xMsaaState(bool value)
  61. {
  62.     if(m4xMsaaState != value)
  63.     {
  64.         m4xMsaaState = value;
  65.  
  66.         // Recreate the swapchain and buffers with new multisample settings.
  67.         CreateSwapChain();
  68.         OnResize();
  69.     }
  70. }
  71.  
  72. int D3DApp::Run()
  73. {
  74.     MSG msg = {0};
  75.  
  76.     mTimer.Reset();
  77.  
  78.     while(msg.message != WM_QUIT)
  79.     {
  80.         // If there are Window messages then process them.
  81.         if(PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
  82.         {
  83.             TranslateMessage( &msg );
  84.             DispatchMessage( &msg );
  85.         }
  86.         // Otherwise, do animation/game stuff.
  87.         else
  88.         {    
  89.             mTimer.Tick();
  90.  
  91.             if( !mAppPaused )
  92.             {
  93.                 CalculateFrameStats();
  94.                 Update(mTimer);    
  95.                 Draw(mTimer);
  96.             }
  97.             else
  98.             {
  99.                 Sleep(100);
  100.             }
  101.         }
  102.     }
  103.  
  104.     return (int)msg.wParam;
  105. }
  106.  
  107. bool D3DApp::Initialize()
  108. {
  109.     if(!InitMainWindow())
  110.         return false;
  111.  
  112.     if(!InitDirect3D())
  113.         return false;
  114.  
  115.     // Do the initial resize code.
  116.     OnResize();
  117.  
  118.     return true;
  119. }
  120.  
  121. void D3DApp::CreateRtvAndDsvDescriptorHeaps()
  122. {
  123.     D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc;
  124.     rtvHeapDesc.NumDescriptors = SwapChainBufferCount;
  125.     rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
  126.     rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
  127.     rtvHeapDesc.NodeMask = 0;
  128.     ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
  129.         &rtvHeapDesc, IID_PPV_ARGS(mRtvHeap.GetAddressOf())));
  130.  
  131.  
  132.     D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc;
  133.     dsvHeapDesc.NumDescriptors = 1;
  134.     dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
  135.     dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
  136.     dsvHeapDesc.NodeMask = 0;
  137.     ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
  138.         &dsvHeapDesc, IID_PPV_ARGS(mDsvHeap.GetAddressOf())));
  139. }
  140.  
  141. void D3DApp::OnResize()
  142. {
  143.     assert(md3dDevice);
  144.     assert(mSwapChain);
  145.     assert(mDirectCmdListAlloc);
  146.  
  147.     // Flush before changing any resources.
  148.     FlushCommandQueue();
  149.  
  150.     ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));
  151.  
  152.     // Release the previous resources we will be recreating.
  153.     for (int i = 0; i < SwapChainBufferCount; ++i)
  154.         mSwapChainBuffer[i].Reset();
  155.     mDepthStencilBuffer.Reset();
  156.     
  157.     // Resize the swap chain.
  158.     ThrowIfFailed(mSwapChain->ResizeBuffers(
  159.         SwapChainBufferCount, 
  160.         mClientWidth, mClientHeight, 
  161.         mBackBufferFormat, 
  162.         DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH));
  163.  
  164.     mCurrBackBuffer = 0;
  165.  
  166.     CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHeapHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart());
  167.     for (UINT i = 0; i < SwapChainBufferCount; i++)
  168.     {
  169.         ThrowIfFailed(mSwapChain->GetBuffer(i, IID_PPV_ARGS(&mSwapChainBuffer[i])));
  170.         md3dDevice->CreateRenderTargetView(mSwapChainBuffer[i].Get(), nullptr, rtvHeapHandle);
  171.         rtvHeapHandle.Offset(1, mRtvDescriptorSize);
  172.     }
  173.  
  174.     // Create the depth/stencil buffer and view.
  175.     D3D12_RESOURCE_DESC depthStencilDesc;
  176.     depthStencilDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
  177.     depthStencilDesc.Alignment = 0;
  178.     depthStencilDesc.Width = mClientWidth;
  179.     depthStencilDesc.Height = mClientHeight;
  180.     depthStencilDesc.DepthOrArraySize = 1;
  181.     depthStencilDesc.MipLevels = 1;
  182.     depthStencilDesc.Format = mDepthStencilFormat;
  183.     depthStencilDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
  184.     depthStencilDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
  185.     depthStencilDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
  186.     depthStencilDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
  187.  
  188.     D3D12_CLEAR_VALUE optClear;
  189.     optClear.Format = mDepthStencilFormat;
  190.     optClear.DepthStencil.Depth = 1.0f;
  191.     optClear.DepthStencil.Stencil = 0;
  192.     ThrowIfFailed(md3dDevice->CreateCommittedResource(
  193.         &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
  194.         D3D12_HEAP_FLAG_NONE,
  195.         &depthStencilDesc,
  196.         D3D12_RESOURCE_STATE_COMMON,
  197.         &optClear,
  198.         IID_PPV_ARGS(mDepthStencilBuffer.GetAddressOf())));
  199.  
  200.     // Create descriptor to mip level 0 of entire resource using the format of the resource.
  201.     md3dDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), nullptr, DepthStencilView());
  202.  
  203.     // Transition the resource from its initial state to be used as a depth buffer.
  204.     mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mDepthStencilBuffer.Get(),
  205.         D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_DEPTH_WRITE));
  206.     
  207.     // Execute the resize commands.
  208.     ThrowIfFailed(mCommandList->Close());
  209.     ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
  210.     mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);
  211.  
  212.     // Wait until resize is complete.
  213.     FlushCommandQueue();
  214.  
  215.     // Update the viewport transform to cover the client area.
  216.     mScreenViewport.TopLeftX = 0;
  217.     mScreenViewport.TopLeftY = 0;
  218.     mScreenViewport.Width    = static_cast<float>(mClientWidth);
  219.     mScreenViewport.Height   = static_cast<float>(mClientHeight);
  220.     mScreenViewport.MinDepth = 0.0f;
  221.     mScreenViewport.MaxDepth = 1.0f;
  222.  
  223.     mScissorRect = { 0, 0, mClientWidth, mClientHeight };
  224. }
  225.  
  226. LRESULT D3DApp::MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  227. {
  228.     switch( msg )
  229.     {
  230.     // WM_ACTIVATE is sent when the window is activated or deactivated.  
  231.     // We pause the game when the window is deactivated and unpause it 
  232.     // when it becomes active.  
  233.     case WM_ACTIVATE:
  234.         if( LOWORD(wParam) == WA_INACTIVE )
  235.         {
  236.             mAppPaused = true;
  237.             mTimer.Stop();
  238.         }
  239.         else
  240.         {
  241.             mAppPaused = false;
  242.             mTimer.Start();
  243.         }
  244.         return 0;
  245.  
  246.     // WM_SIZE is sent when the user resizes the window.  
  247.     case WM_SIZE:
  248.         // Save the new client area dimensions.
  249.         mClientWidth  = LOWORD(lParam);
  250.         mClientHeight = HIWORD(lParam);
  251.         if( md3dDevice )
  252.         {
  253.             if( wParam == SIZE_MINIMIZED )
  254.             {
  255.                 mAppPaused = true;
  256.                 mMinimized = true;
  257.                 mMaximized = false;
  258.             }
  259.             else if( wParam == SIZE_MAXIMIZED )
  260.             {
  261.                 mAppPaused = false;
  262.                 mMinimized = false;
  263.                 mMaximized = true;
  264.                 OnResize();
  265.             }
  266.             else if( wParam == SIZE_RESTORED )
  267.             {
  268.                 
  269.                 // Restoring from minimized state?
  270.                 if( mMinimized )
  271.                 {
  272.                     mAppPaused = false;
  273.                     mMinimized = false;
  274.                     OnResize();
  275.                 }
  276.  
  277.                 // Restoring from maximized state?
  278.                 else if( mMaximized )
  279.                 {
  280.                     mAppPaused = false;
  281.                     mMaximized = false;
  282.                     OnResize();
  283.                 }
  284.                 else if( mResizing )
  285.                 {
  286.                     // If user is dragging the resize bars, we do not resize 
  287.                     // the buffers here because as the user continuously 
  288.                     // drags the resize bars, a stream of WM_SIZE messages are
  289.                     // sent to the window, and it would be pointless (and slow)
  290.                     // to resize for each WM_SIZE message received from dragging
  291.                     // the resize bars.  So instead, we reset after the user is 
  292.                     // done resizing the window and releases the resize bars, which 
  293.                     // sends a WM_EXITSIZEMOVE message.
  294.                 }
  295.                 else // API call such as SetWindowPos or mSwapChain->SetFullscreenState.
  296.                 {
  297.                     OnResize();
  298.                 }
  299.             }
  300.         }
  301.         return 0;
  302.  
  303.     // WM_EXITSIZEMOVE is sent when the user grabs the resize bars.
  304.     case WM_ENTERSIZEMOVE:
  305.         mAppPaused = true;
  306.         mResizing  = true;
  307.         mTimer.Stop();
  308.         return 0;
  309.  
  310.     // WM_EXITSIZEMOVE is sent when the user releases the resize bars.
  311.     // Here we reset everything based on the new window dimensions.
  312.     case WM_EXITSIZEMOVE:
  313.         mAppPaused = false;
  314.         mResizing  = false;
  315.         mTimer.Start();
  316.         OnResize();
  317.         return 0;
  318.  
  319.     // WM_DESTROY is sent when the window is being destroyed.
  320.     case WM_DESTROY:
  321.         PostQuitMessage(0);
  322.         return 0;
  323.  
  324.     // The WM_MENUCHAR message is sent when a menu is active and the user presses 
  325.     // a key that does not correspond to any mnemonic or accelerator key. 
  326.     case WM_MENUCHAR:
  327.         // Don't beep when we alt-enter.
  328.         return MAKELRESULT(0, MNC_CLOSE);
  329.  
  330.     // Catch this message so to prevent the window from becoming too small.
  331.     case WM_GETMINMAXINFO:
  332.         ((MINMAXINFO*)lParam)->ptMinTrackSize.x = 200;
  333.         ((MINMAXINFO*)lParam)->ptMinTrackSize.y = 200; 
  334.         return 0;
  335.  
  336.     case WM_LBUTTONDOWN:
  337.     case WM_MBUTTONDOWN:
  338.     case WM_RBUTTONDOWN:
  339.         OnMouseDown(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  340.         return 0;
  341.     case WM_LBUTTONUP:
  342.     case WM_MBUTTONUP:
  343.     case WM_RBUTTONUP:
  344.         OnMouseUp(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  345.         return 0;
  346.     case WM_MOUSEMOVE:
  347.         OnMouseMove(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  348.         return 0;
  349.     case WM_KEYUP:
  350.         if(wParam == VK_ESCAPE)
  351.         {
  352.             PostQuitMessage(0);
  353.         }
  354.         else if((int)wParam == VK_F2)
  355.             Set4xMsaaState(!m4xMsaaState);
  356.  
  357.         return 0;
  358.     }
  359.  
  360.     return DefWindowProc(hwnd, msg, wParam, lParam);
  361. }
  362.  
  363. bool D3DApp::InitMainWindow()
  364. {
  365.     WNDCLASS wc;
  366.     wc.style         = CS_HREDRAW | CS_VREDRAW;
  367.     wc.lpfnWndProc   = MainWndProc; 
  368.     wc.cbClsExtra    = 0;
  369.     wc.cbWndExtra    = 0;
  370.     wc.hInstance     = mhAppInst;
  371.     wc.hIcon         = LoadIcon(0, IDI_APPLICATION);
  372.     wc.hCursor       = LoadCursor(0, IDC_ARROW);
  373.     wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
  374.     wc.lpszMenuName  = 0;
  375.     wc.lpszClassName = L"MainWnd";
  376.  
  377.     if( !RegisterClass(&wc) )
  378.     {
  379.         MessageBox(0, L"RegisterClass Failed.", 0, 0);
  380.         return false;
  381.     }
  382.  
  383.     // Compute window rectangle dimensions based on requested client area dimensions.
  384.     RECT R = { 0, 0, mClientWidth, mClientHeight };
  385.     AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false);
  386.     int width  = R.right - R.left;
  387.     int height = R.bottom - R.top;
  388.  
  389.     mhMainWnd = CreateWindow(L"MainWnd", mMainWndCaption.c_str(), 
  390.         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, 0, 0, mhAppInst, 0); 
  391.     if( !mhMainWnd )
  392.     {
  393.         MessageBox(0, L"CreateWindow Failed.", 0, 0);
  394.         return false;
  395.     }
  396.  
  397.     ShowWindow(mhMainWnd, SW_SHOW);
  398.     UpdateWindow(mhMainWnd);
  399.  
  400.     return true;
  401. }
  402.  
  403. bool D3DApp::InitDirect3D()
  404. {
  405. #if defined(DEBUG) || defined(_DEBUG) 
  406.     // Enable the D3D12 debug layer.
  407. {
  408.     ComPtr<ID3D12Debug> debugController;
  409.     ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)));
  410.     debugController->EnableDebugLayer();
  411. }
  412. #endif
  413.  
  414.     ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&mdxgiFactory)));
  415.  
  416.     // Try to create hardware device.
  417.     HRESULT hardwareResult = D3D12CreateDevice(
  418.         nullptr,             // default adapter
  419.         D3D_FEATURE_LEVEL_11_0,
  420.         IID_PPV_ARGS(&md3dDevice));
  421.  
  422.     // Fallback to WARP device.
  423.     if(FAILED(hardwareResult))
  424.     {
  425.         ComPtr<IDXGIAdapter> pWarpAdapter;
  426.         ThrowIfFailed(mdxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&pWarpAdapter)));
  427.  
  428.         ThrowIfFailed(D3D12CreateDevice(
  429.             pWarpAdapter.Get(),
  430.             D3D_FEATURE_LEVEL_11_0,
  431.             IID_PPV_ARGS(&md3dDevice)));
  432.     }
  433.  
  434.     ThrowIfFailed(md3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE,
  435.         IID_PPV_ARGS(&mFence)));
  436.  
  437.     mRtvDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
  438.     mDsvDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
  439.     mCbvSrvUavDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
  440.  
  441.     // Check 4X MSAA quality support for our back buffer format.
  442.     // All Direct3D 11 capable devices support 4X MSAA for all render 
  443.     // target formats, so we only need to check quality support.
  444.  
  445.     D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msQualityLevels;
  446.     msQualityLevels.Format = mBackBufferFormat;
  447.     msQualityLevels.SampleCount = 4;
  448.     msQualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
  449.     msQualityLevels.NumQualityLevels = 0;
  450.     ThrowIfFailed(md3dDevice->CheckFeatureSupport(
  451.         D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
  452.         &msQualityLevels,
  453.         sizeof(msQualityLevels)));
  454.  
  455.     m4xMsaaQuality = msQualityLevels.NumQualityLevels;
  456.     assert(m4xMsaaQuality > 0 && "Unexpected MSAA quality level.");
  457.     
  458. #ifdef _DEBUG
  459.     LogAdapters();
  460. #endif
  461.  
  462.     CreateCommandObjects();
  463.     CreateSwapChain();
  464.     CreateRtvAndDsvDescriptorHeaps();
  465.  
  466.     return true;
  467. }
  468.  
  469. void D3DApp::CreateCommandObjects()
  470. {
  471.     D3D12_COMMAND_QUEUE_DESC queueDesc = {};
  472.     queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
  473.     queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
  474.     ThrowIfFailed(md3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)));
  475.  
  476.     ThrowIfFailed(md3dDevice->CreateCommandAllocator(
  477.         D3D12_COMMAND_LIST_TYPE_DIRECT,
  478.         IID_PPV_ARGS(mDirectCmdListAlloc.GetAddressOf())));
  479.  
  480.     ThrowIfFailed(md3dDevice->CreateCommandList(
  481.         0,
  482.         D3D12_COMMAND_LIST_TYPE_DIRECT,
  483.         mDirectCmdListAlloc.Get(), // Associated command allocator
  484.         nullptr,                   // Initial PipelineStateObject
  485.         IID_PPV_ARGS(mCommandList.GetAddressOf())));
  486.  
  487.     // Start off in a closed state.  This is because the first time we refer 
  488.     // to the command list we will Reset it, and it needs to be closed before
  489.     // calling Reset.
  490.     mCommandList->Close();
  491. }
  492.  
  493. void D3DApp::CreateSwapChain()
  494. {
  495.     // Release the previous swapchain we will be recreating.
  496.     mSwapChain.Reset();
  497.  
  498.     DXGI_SWAP_CHAIN_DESC sd;
  499.     sd.BufferDesc.Width = mClientWidth;
  500.     sd.BufferDesc.Height = mClientHeight;
  501.     sd.BufferDesc.RefreshRate.Numerator = 60;
  502.     sd.BufferDesc.RefreshRate.Denominator = 1;
  503.     sd.BufferDesc.Format = mBackBufferFormat;
  504.     sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
  505.     sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
  506.     sd.SampleDesc.Count = m4xMsaaState ? 4 : 1;
  507.     sd.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
  508.     sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
  509.     sd.BufferCount = SwapChainBufferCount;
  510.     sd.OutputWindow = mhMainWnd;
  511.     sd.Windowed = true;
  512.     sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
  513.     sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
  514.  
  515.     // Note: Swap chain uses queue to perform flush.
  516.     ThrowIfFailed(mdxgiFactory->CreateSwapChain(
  517.         mCommandQueue.Get(),
  518.         &sd, 
  519.         mSwapChain.GetAddressOf()));
  520. }
  521.  
  522. void D3DApp::FlushCommandQueue()
  523. {
  524.     // Advance the fence value to mark commands up to this fence point.
  525.     mCurrentFence++;
  526.  
  527.     // Add an instruction to the command queue to set a new fence point.  Because we 
  528.     // are on the GPU timeline, the new fence point won't be set until the GPU finishes
  529.     // processing all the commands prior to this Signal().
  530.     ThrowIfFailed(mCommandQueue->Signal(mFence.Get(), mCurrentFence));
  531.  
  532.     // Wait until the GPU has completed commands up to this fence point.
  533.     if(mFence->GetCompletedValue() < mCurrentFence)
  534.     {
  535.         HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
  536.  
  537.         // Fire event when GPU hits current fence.  
  538.         ThrowIfFailed(mFence->SetEventOnCompletion(mCurrentFence, eventHandle));
  539.  
  540.         // Wait until the GPU hits current fence event is fired.
  541.         WaitForSingleObject(eventHandle, INFINITE);
  542.         CloseHandle(eventHandle);
  543.     }
  544. }
  545.  
  546. ID3D12Resource* D3DApp::CurrentBackBuffer()const
  547. {
  548.     return mSwapChainBuffer[mCurrBackBuffer].Get();
  549. }
  550.  
  551. D3D12_CPU_DESCRIPTOR_HANDLE D3DApp::CurrentBackBufferView()const
  552. {
  553.     return CD3DX12_CPU_DESCRIPTOR_HANDLE(
  554.         mRtvHeap->GetCPUDescriptorHandleForHeapStart(),
  555.         mCurrBackBuffer,
  556.         mRtvDescriptorSize);
  557. }
  558.  
  559. D3D12_CPU_DESCRIPTOR_HANDLE D3DApp::DepthStencilView()const
  560. {
  561.     return mDsvHeap->GetCPUDescriptorHandleForHeapStart();
  562. }
  563.  
  564. void D3DApp::CalculateFrameStats()
  565. {
  566.     // Code computes the average frames per second, and also the 
  567.     // average time it takes to render one frame.  These stats 
  568.     // are appended to the window caption bar.
  569.     
  570.     static int frameCnt = 0;
  571.     static float timeElapsed = 0.0f;
  572.  
  573.     frameCnt++;
  574.  
  575.     // Compute averages over one second period.
  576.     if( (mTimer.TotalTime() - timeElapsed) >= 1.0f )
  577.     {
  578.         float fps = (float)frameCnt; // fps = frameCnt / 1
  579.         float mspf = 1000.0f / fps;
  580.  
  581.         wstring fpsStr = to_wstring(fps);
  582.         wstring mspfStr = to_wstring(mspf);
  583.  
  584.         wstring windowText = mMainWndCaption +
  585.             L"    fps: " + fpsStr +
  586.             L"   mspf: " + mspfStr;
  587.  
  588.         SetWindowText(mhMainWnd, windowText.c_str());
  589.         
  590.         // Reset for next average.
  591.         frameCnt = 0;
  592.         timeElapsed += 1.0f;
  593.     }
  594. }
  595.  
  596. void D3DApp::LogAdapters()
  597. {
  598.     UINT i = 0;
  599.     IDXGIAdapter* adapter = nullptr;
  600.     std::vector<IDXGIAdapter*> adapterList;
  601.     while(mdxgiFactory->EnumAdapters(i, &adapter) != DXGI_ERROR_NOT_FOUND)
  602.     {
  603.         DXGI_ADAPTER_DESC desc;
  604.         adapter->GetDesc(&desc);
  605.  
  606.         std::wstring text = L"***Adapter: ";
  607.         text += desc.Description;
  608.         text += L"\n";
  609.  
  610.         OutputDebugString(text.c_str());
  611.  
  612.         adapterList.push_back(adapter);
  613.         
  614.         ++i;
  615.     }
  616.  
  617.     for(size_t i = 0; i < adapterList.size(); ++i)
  618.     {
  619.         LogAdapterOutputs(adapterList[i]);
  620.         ReleaseCom(adapterList[i]);
  621.     }
  622. }
  623.  
  624. void D3DApp::LogAdapterOutputs(IDXGIAdapter* adapter)
  625. {
  626.     UINT i = 0;
  627.     IDXGIOutput* output = nullptr;
  628.     while(adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND)
  629.     {
  630.         DXGI_OUTPUT_DESC desc;
  631.         output->GetDesc(&desc);
  632.         
  633.         std::wstring text = L"***Output: ";
  634.         text += desc.DeviceName;
  635.         text += L"\n";
  636.         OutputDebugString(text.c_str());
  637.  
  638.         LogOutputDisplayModes(output, mBackBufferFormat);
  639.  
  640.         ReleaseCom(output);
  641.  
  642.         ++i;
  643.     }
  644. }
  645.  
  646. void D3DApp::LogOutputDisplayModes(IDXGIOutput* output, DXGI_FORMAT format)
  647. {
  648.     UINT count = 0;
  649.     UINT flags = 0;
  650.  
  651.     // Call with nullptr to get list count.
  652.     output->GetDisplayModeList(format, flags, &count, nullptr);
  653.  
  654.     std::vector<DXGI_MODE_DESC> modeList(count);
  655.     output->GetDisplayModeList(format, flags, &count, &modeList[0]);
  656.  
  657.     for(auto& x : modeList)
  658.     {
  659.         UINT n = x.RefreshRate.Numerator;
  660.         UINT d = x.RefreshRate.Denominator;
  661.         std::wstring text =
  662.             L"Width = " + std::to_wstring(x.Width) + L" " +
  663.             L"Height = " + std::to_wstring(x.Height) + L" " +
  664.             L"Refresh = " + std::to_wstring(n) + L"/" + std::to_wstring(d) +
  665.             L"\n";
  666.  
  667.         ::OutputDebugString(text.c_str());
  668.     }
  669. }