Add rest of dx12tutorial

This commit is contained in:
Eero Holmala 2023-04-26 15:20:23 +03:00
parent f19e439410
commit 6765e41bdb
26 changed files with 3049 additions and 0 deletions

View File

@ -0,0 +1,9 @@
# CMakeList.txt : Top-level CMake project file, do global configuration
# and include sub-projects here.
#
cmake_minimum_required (VERSION 3.8)
project ("dx12tutorial")
# Include sub-projects.
add_subdirectory ("dx12tutorial")

View File

@ -0,0 +1,61 @@
{
"version": 3,
"configurePresets": [
{
"name": "windows-base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"installDir": "${sourceDir}/out/install/${presetName}",
"cacheVariables": {
"CMAKE_C_COMPILER": "cl.exe",
"CMAKE_CXX_COMPILER": "cl.exe"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "x64-debug",
"displayName": "x64 Debug",
"inherits": "windows-base",
"architecture": {
"value": "x64",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "x64-release",
"displayName": "x64 Release",
"inherits": "x64-debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "x86-debug",
"displayName": "x86 Debug",
"inherits": "windows-base",
"architecture": {
"value": "x86",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "x86-release",
"displayName": "x86 Release",
"inherits": "x86-debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
}
]
}

View File

@ -0,0 +1,69 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
APP_ICON ICON "Resources\\Icon\\app_icon.ico"
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@ -0,0 +1,598 @@
#include "DX12LibPCH.h"
#include "Application.h"
//#include "..\resource.h"
#include "Game.h"
#include "CommandQueue.h"
#include "Window.h"
constexpr wchar_t WINDOW_CLASS_NAME[] = L"DX12RenderWindowClass";
using WindowPtr = std::shared_ptr<Window>;
using WindowMap = std::map< HWND, WindowPtr >;
using WindowNameMap = std::map< std::wstring, WindowPtr >;
static Application* gs_pSingelton = nullptr;
static WindowMap gs_Windows;
static WindowNameMap gs_WindowByName;
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
// A wrapper struct to allow shared pointers for the window class.
struct MakeWindow : public Window
{
MakeWindow(HWND hWnd, const std::wstring& windowName, int clientWidth, int clientHeight, bool vSync)
: Window(hWnd, windowName, clientWidth, clientHeight, vSync)
{}
};
Application::Application(HINSTANCE hInst)
: m_hInstance(hInst)
, m_TearingSupported(false)
{
// Windows 10 Creators update adds Per Monitor V2 DPI awareness context.
// Using this awareness context allows the client area of the window
// to achieve 100% scaling while still allowing non-client window content to
// be rendered in a DPI sensitive fashion.
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
#if defined(_DEBUG)
// Always enable the debug layer before doing anything DX12 related
// so all possible errors generated while creating DX12 objects
// are caught by the debug layer.
ComPtr<ID3D12Debug> debugInterface;
ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(&debugInterface)));
debugInterface->EnableDebugLayer();
#endif
WNDCLASSEXW wndClass = { 0 };
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = &WndProc;
wndClass.hInstance = m_hInstance;
wndClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
wndClass.hIcon = nullptr;//LoadIcon(m_hInstance, MAKEINTRESOURCE(APP_ICON));
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndClass.lpszMenuName = nullptr;
wndClass.lpszClassName = WINDOW_CLASS_NAME;
wndClass.hIconSm = nullptr;//LoadIcon(m_hInstance, MAKEINTRESOURCE(APP_ICON));
if (!RegisterClassExW(&wndClass))
{
MessageBoxA(NULL, "Unable to register the window class.", "Error", MB_OK | MB_ICONERROR);
}
m_dxgiAdapter = GetAdapter(false);
if (m_dxgiAdapter)
{
m_d3d12Device = CreateDevice(m_dxgiAdapter);
}
if (m_d3d12Device)
{
m_DirectCommandQueue = std::make_shared<CommandQueue>(m_d3d12Device, D3D12_COMMAND_LIST_TYPE_DIRECT);
m_ComputeCommandQueue = std::make_shared<CommandQueue>(m_d3d12Device, D3D12_COMMAND_LIST_TYPE_COMPUTE);
m_CopyCommandQueue = std::make_shared<CommandQueue>(m_d3d12Device, D3D12_COMMAND_LIST_TYPE_COPY);
m_TearingSupported = CheckTearingSupport();
}
}
void Application::Create(HINSTANCE hInst)
{
if (!gs_pSingelton)
{
gs_pSingelton = new Application(hInst);
}
}
Application& Application::Get()
{
assert(gs_pSingelton);
return *gs_pSingelton;
}
void Application::Destroy()
{
if (gs_pSingelton)
{
assert(gs_Windows.empty() && gs_WindowByName.empty() &&
"All windows should be destroyed before destroying the application instance.");
delete gs_pSingelton;
gs_pSingelton = nullptr;
}
}
Application::~Application()
{
Flush();
}
Microsoft::WRL::ComPtr<IDXGIAdapter4> Application::GetAdapter(bool bUseWarp)
{
ComPtr<IDXGIFactory4> dxgiFactory;
UINT createFactoryFlags = 0;
#if defined(_DEBUG)
createFactoryFlags = DXGI_CREATE_FACTORY_DEBUG;
#endif
ThrowIfFailed(CreateDXGIFactory2(createFactoryFlags, IID_PPV_ARGS(&dxgiFactory)));
ComPtr<IDXGIAdapter1> dxgiAdapter1;
ComPtr<IDXGIAdapter4> dxgiAdapter4;
if (bUseWarp)
{
ThrowIfFailed(dxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&dxgiAdapter1)));
ThrowIfFailed(dxgiAdapter1.As(&dxgiAdapter4));
}
else
{
SIZE_T maxDedicatedVideoMemory = 0;
for (UINT i = 0; dxgiFactory->EnumAdapters1(i, &dxgiAdapter1) != DXGI_ERROR_NOT_FOUND; ++i)
{
DXGI_ADAPTER_DESC1 dxgiAdapterDesc1;
dxgiAdapter1->GetDesc1(&dxgiAdapterDesc1);
// Check to see if the adapter can create a D3D12 device without actually
// creating it. The adapter with the largest dedicated video memory
// is favored.
if ((dxgiAdapterDesc1.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) == 0 &&
SUCCEEDED(D3D12CreateDevice(dxgiAdapter1.Get(),
D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), nullptr)) &&
dxgiAdapterDesc1.DedicatedVideoMemory > maxDedicatedVideoMemory)
{
maxDedicatedVideoMemory = dxgiAdapterDesc1.DedicatedVideoMemory;
ThrowIfFailed(dxgiAdapter1.As(&dxgiAdapter4));
}
}
}
return dxgiAdapter4;
}
Microsoft::WRL::ComPtr<ID3D12Device2> Application::CreateDevice(Microsoft::WRL::ComPtr<IDXGIAdapter4> adapter)
{
ComPtr<ID3D12Device2> d3d12Device2;
ThrowIfFailed(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&d3d12Device2)));
// NAME_D3D12_OBJECT(d3d12Device2);
// Enable debug messages in debug mode.
#if defined(_DEBUG)
ComPtr<ID3D12InfoQueue> pInfoQueue;
if (SUCCEEDED(d3d12Device2.As(&pInfoQueue)))
{
pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE);
pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE);
pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE);
// Suppress whole categories of messages
//D3D12_MESSAGE_CATEGORY Categories[] = {};
// Suppress messages based on their severity level
D3D12_MESSAGE_SEVERITY Severities[] =
{
D3D12_MESSAGE_SEVERITY_INFO
};
// Suppress individual messages by their ID
D3D12_MESSAGE_ID DenyIds[] = {
D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE, // I'm really not sure how to avoid this message.
D3D12_MESSAGE_ID_MAP_INVALID_NULLRANGE, // This warning occurs when using capture frame while graphics debugging.
D3D12_MESSAGE_ID_UNMAP_INVALID_NULLRANGE, // This warning occurs when using capture frame while graphics debugging.
};
D3D12_INFO_QUEUE_FILTER NewFilter = {};
//NewFilter.DenyList.NumCategories = _countof(Categories);
//NewFilter.DenyList.pCategoryList = Categories;
NewFilter.DenyList.NumSeverities = _countof(Severities);
NewFilter.DenyList.pSeverityList = Severities;
NewFilter.DenyList.NumIDs = _countof(DenyIds);
NewFilter.DenyList.pIDList = DenyIds;
ThrowIfFailed(pInfoQueue->PushStorageFilter(&NewFilter));
}
#endif
return d3d12Device2;
}
bool Application::CheckTearingSupport()
{
BOOL allowTearing = FALSE;
// Rather than create the DXGI 1.5 factory interface directly, we create the
// DXGI 1.4 interface and query for the 1.5 interface. This is to enable the
// graphics debugging tools which will not support the 1.5 factory interface
// until a future update.
ComPtr<IDXGIFactory4> factory4;
if (SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&factory4))))
{
ComPtr<IDXGIFactory5> factory5;
if (SUCCEEDED(factory4.As(&factory5)))
{
factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING,
&allowTearing, sizeof(allowTearing));
}
}
return allowTearing == TRUE;
}
bool Application::IsTearingSupported() const
{
return m_TearingSupported;
}
std::shared_ptr<Window> Application::CreateRenderWindow(const std::wstring& windowName, int clientWidth, int clientHeight, bool vSync)
{
// First check if a window with the given name already exists.
WindowNameMap::iterator windowIter = gs_WindowByName.find(windowName);
if (windowIter != gs_WindowByName.end())
{
return windowIter->second;
}
RECT windowRect = { 0, 0, clientWidth, clientHeight };
AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);
HWND hWnd = CreateWindowW(WINDOW_CLASS_NAME, windowName.c_str(),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
windowRect.right - windowRect.left,
windowRect.bottom - windowRect.top,
nullptr, nullptr, m_hInstance, nullptr);
if (!hWnd)
{
MessageBoxA(NULL, "Could not create the render window.", "Error", MB_OK | MB_ICONERROR);
return nullptr;
}
WindowPtr pWindow = std::make_shared<MakeWindow>(hWnd, windowName, clientWidth, clientHeight, vSync);
gs_Windows.insert(WindowMap::value_type(hWnd, pWindow));
gs_WindowByName.insert(WindowNameMap::value_type(windowName, pWindow));
return pWindow;
}
void Application::DestroyWindow(std::shared_ptr<Window> window)
{
if (window) window->Destroy();
}
void Application::DestroyWindow(const std::wstring& windowName)
{
WindowPtr pWindow = GetWindowByName(windowName);
if (pWindow)
{
DestroyWindow(pWindow);
}
}
std::shared_ptr<Window> Application::GetWindowByName(const std::wstring& windowName)
{
std::shared_ptr<Window> window;
WindowNameMap::iterator iter = gs_WindowByName.find(windowName);
if (iter != gs_WindowByName.end())
{
window = iter->second;
}
return window;
}
int Application::Run(std::shared_ptr<Game> pGame)
{
if (!pGame->Initialize()) return 1;
if (!pGame->LoadContent()) return 2;
MSG msg = { 0 };
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Flush any commands in the commands queues before quiting.
Flush();
pGame->UnloadContent();
pGame->Destroy();
return static_cast<int>(msg.wParam);
}
void Application::Quit(int exitCode)
{
PostQuitMessage(exitCode);
}
Microsoft::WRL::ComPtr<ID3D12Device2> Application::GetDevice() const
{
return m_d3d12Device;
}
std::shared_ptr<CommandQueue> Application::GetCommandQueue(D3D12_COMMAND_LIST_TYPE type) const
{
std::shared_ptr<CommandQueue> commandQueue;
switch (type)
{
case D3D12_COMMAND_LIST_TYPE_DIRECT:
commandQueue = m_DirectCommandQueue;
break;
case D3D12_COMMAND_LIST_TYPE_COMPUTE:
commandQueue = m_ComputeCommandQueue;
break;
case D3D12_COMMAND_LIST_TYPE_COPY:
commandQueue = m_CopyCommandQueue;
break;
default:
assert(false && "Invalid command queue type.");
}
return commandQueue;
}
void Application::Flush()
{
m_DirectCommandQueue->Flush();
m_ComputeCommandQueue->Flush();
m_CopyCommandQueue->Flush();
}
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> Application::CreateDescriptorHeap(UINT numDescriptors, D3D12_DESCRIPTOR_HEAP_TYPE type)
{
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
desc.Type = type;
desc.NumDescriptors = numDescriptors;
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
desc.NodeMask = 0;
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> descriptorHeap;
ThrowIfFailed(m_d3d12Device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&descriptorHeap)));
return descriptorHeap;
}
UINT Application::GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE type) const
{
return m_d3d12Device->GetDescriptorHandleIncrementSize(type);
}
// Remove a window from our window lists.
static void RemoveWindow(HWND hWnd)
{
WindowMap::iterator windowIter = gs_Windows.find(hWnd);
if (windowIter != gs_Windows.end())
{
WindowPtr pWindow = windowIter->second;
gs_WindowByName.erase(pWindow->GetWindowName());
gs_Windows.erase(windowIter);
}
}
// Convert the message ID into a MouseButton ID
MouseButtonEventArgs::MouseButton DecodeMouseButton(UINT messageID)
{
MouseButtonEventArgs::MouseButton mouseButton = MouseButtonEventArgs::None;
switch (messageID)
{
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
{
mouseButton = MouseButtonEventArgs::Left;
}
break;
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDBLCLK:
{
mouseButton = MouseButtonEventArgs::Right;
}
break;
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDBLCLK:
{
mouseButton = MouseButtonEventArgs::Middel;
}
break;
}
return mouseButton;
}
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
WindowPtr pWindow;
{
WindowMap::iterator iter = gs_Windows.find(hwnd);
if (iter != gs_Windows.end())
{
pWindow = iter->second;
}
}
if (pWindow)
{
switch (message)
{
case WM_PAINT:
{
// Delta time will be filled in by the Window.
UpdateEventArgs updateEventArgs(0.0f, 0.0f);
pWindow->OnUpdate(updateEventArgs);
RenderEventArgs renderEventArgs(0.0f, 0.0f);
// Delta time will be filled in by the Window.
pWindow->OnRender(renderEventArgs);
}
break;
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
{
MSG charMsg;
// Get the Unicode character (UTF-16)
unsigned int c = 0;
// For printable characters, the next message will be WM_CHAR.
// This message contains the character code we need to send the KeyPressed event.
// Inspired by the SDL 1.2 implementation.
if (PeekMessage(&charMsg, hwnd, 0, 0, PM_NOREMOVE) && charMsg.message == WM_CHAR)
{
GetMessage(&charMsg, hwnd, 0, 0);
c = static_cast<unsigned int>(charMsg.wParam);
}
bool shift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
bool control = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
bool alt = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0;
KeyCode::Key key = (KeyCode::Key)wParam;
unsigned int scanCode = (lParam & 0x00FF0000) >> 16;
KeyEventArgs keyEventArgs(key, c, KeyEventArgs::Pressed, shift, control, alt);
pWindow->OnKeyPressed(keyEventArgs);
}
break;
case WM_SYSKEYUP:
case WM_KEYUP:
{
bool shift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
bool control = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
bool alt = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0;
KeyCode::Key key = (KeyCode::Key)wParam;
unsigned int c = 0;
unsigned int scanCode = (lParam & 0x00FF0000) >> 16;
// Determine which key was released by converting the key code and the scan code
// to a printable character (if possible).
// Inspired by the SDL 1.2 implementation.
unsigned char keyboardState[256];
GetKeyboardState(keyboardState);
wchar_t translatedCharacters[4];
if (int result = ToUnicodeEx(static_cast<UINT>(wParam), scanCode, keyboardState, translatedCharacters, 4, 0, NULL) > 0)
{
c = translatedCharacters[0];
}
KeyEventArgs keyEventArgs(key, c, KeyEventArgs::Released, shift, control, alt);
pWindow->OnKeyReleased(keyEventArgs);
}
break;
// The default window procedure will play a system notification sound
// when pressing the Alt+Enter keyboard combination if this message is
// not handled.
case WM_SYSCHAR:
break;
case WM_MOUSEMOVE:
{
bool lButton = (wParam & MK_LBUTTON) != 0;
bool rButton = (wParam & MK_RBUTTON) != 0;
bool mButton = (wParam & MK_MBUTTON) != 0;
bool shift = (wParam & MK_SHIFT) != 0;
bool control = (wParam & MK_CONTROL) != 0;
int x = ((int)(short)LOWORD(lParam));
int y = ((int)(short)HIWORD(lParam));
MouseMotionEventArgs mouseMotionEventArgs(lButton, mButton, rButton, control, shift, x, y);
pWindow->OnMouseMoved(mouseMotionEventArgs);
}
break;
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
{
bool lButton = (wParam & MK_LBUTTON) != 0;
bool rButton = (wParam & MK_RBUTTON) != 0;
bool mButton = (wParam & MK_MBUTTON) != 0;
bool shift = (wParam & MK_SHIFT) != 0;
bool control = (wParam & MK_CONTROL) != 0;
int x = ((int)(short)LOWORD(lParam));
int y = ((int)(short)HIWORD(lParam));
MouseButtonEventArgs mouseButtonEventArgs(DecodeMouseButton(message), MouseButtonEventArgs::Pressed, lButton, mButton, rButton, control, shift, x, y);
pWindow->OnMouseButtonPressed(mouseButtonEventArgs);
}
break;
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
{
bool lButton = (wParam & MK_LBUTTON) != 0;
bool rButton = (wParam & MK_RBUTTON) != 0;
bool mButton = (wParam & MK_MBUTTON) != 0;
bool shift = (wParam & MK_SHIFT) != 0;
bool control = (wParam & MK_CONTROL) != 0;
int x = ((int)(short)LOWORD(lParam));
int y = ((int)(short)HIWORD(lParam));
MouseButtonEventArgs mouseButtonEventArgs(DecodeMouseButton(message), MouseButtonEventArgs::Released, lButton, mButton, rButton, control, shift, x, y);
pWindow->OnMouseButtonReleased(mouseButtonEventArgs);
}
break;
case WM_MOUSEWHEEL:
{
// The distance the mouse wheel is rotated.
// A positive value indicates the wheel was rotated to the right.
// A negative value indicates the wheel was rotated to the left.
float zDelta = ((int)(short)HIWORD(wParam)) / (float)WHEEL_DELTA;
short keyStates = (short)LOWORD(wParam);
bool lButton = (keyStates & MK_LBUTTON) != 0;
bool rButton = (keyStates & MK_RBUTTON) != 0;
bool mButton = (keyStates & MK_MBUTTON) != 0;
bool shift = (keyStates & MK_SHIFT) != 0;
bool control = (keyStates & MK_CONTROL) != 0;
int x = ((int)(short)LOWORD(lParam));
int y = ((int)(short)HIWORD(lParam));
// Convert the screen coordinates to client coordinates.
POINT clientToScreenPoint;
clientToScreenPoint.x = x;
clientToScreenPoint.y = y;
ScreenToClient(hwnd, &clientToScreenPoint);
MouseWheelEventArgs mouseWheelEventArgs(zDelta, lButton, mButton, rButton, control, shift, (int)clientToScreenPoint.x, (int)clientToScreenPoint.y);
pWindow->OnMouseWheel(mouseWheelEventArgs);
}
break;
case WM_SIZE:
{
int width = ((int)(short)LOWORD(lParam));
int height = ((int)(short)HIWORD(lParam));
ResizeEventArgs resizeEventArgs(width, height);
pWindow->OnResize(resizeEventArgs);
}
break;
case WM_DESTROY:
{
// If a window is being destroyed, remove it from the
// window maps.
RemoveWindow(hwnd);
if (gs_Windows.empty())
{
// If there are no more windows, quit the application.
PostQuitMessage(0);
}
}
break;
default:
return DefWindowProcW(hwnd, message, wParam, lParam);
}
}
else
{
return DefWindowProcW(hwnd, message, wParam, lParam);
}
return 0;
}

View File

@ -0,0 +1,124 @@
/**
* The application class is used to create windows for our application.
*/
#pragma once
#include <d3d12.h>
#include <dxgi1_6.h>
#include <wrl.h>
#include <memory>
#include <string>
class Window;
class Game;
class CommandQueue;
class Application
{
public:
/**
* Create the application singleton with the application instance handle.
*/
static void Create(HINSTANCE hInst);
/**
* Destroy the application instance and all windows created by this application instance.
*/
static void Destroy();
/**
* Get the application singleton.
*/
static Application& Get();
/**
* Check to see if VSync-off is supported.
*/
bool IsTearingSupported() const;
/**
* Create a new DirectX11 render window instance.
* @param windowName The name of the window. This name will appear in the title bar of the window. This name should be unique.
* @param clientWidth The width (in pixels) of the window's client area.
* @param clientHeight The height (in pixels) of the window's client area.
* @param vSync Should the rendering be synchronized with the vertical refresh rate of the screen.
* @param windowed If true, the window will be created in windowed mode. If false, the window will be created full-screen.
* @returns The created window instance. If an error occurred while creating the window an invalid
* window instance is returned. If a window with the given name already exists, that window will be
* returned.
*/
std::shared_ptr<Window> CreateRenderWindow(const std::wstring& windowName, int clientWidth, int clientHeight, bool vSync = true);
/**
* Destroy a window given the window name.
*/
void DestroyWindow(const std::wstring& windowName);
/**
* Destroy a window given the window reference.
*/
void DestroyWindow(std::shared_ptr<Window> window);
/**
* Find a window by the window name.
*/
std::shared_ptr<Window> GetWindowByName(const std::wstring& windowName);
/**
* Run the application loop and message pump.
* @return The error code if an error occurred.
*/
int Run(std::shared_ptr<Game> pGame);
/**
* Request to quit the application and close all windows.
* @param exitCode The error code to return to the invoking process.
*/
void Quit(int exitCode = 0);
/**
* Get the Direct3D 12 device
*/
Microsoft::WRL::ComPtr<ID3D12Device2> GetDevice() const;
/**
* Get a command queue. Valid types are:
* - D3D12_COMMAND_LIST_TYPE_DIRECT : Can be used for draw, dispatch, or copy commands.
* - D3D12_COMMAND_LIST_TYPE_COMPUTE: Can be used for dispatch or copy commands.
* - D3D12_COMMAND_LIST_TYPE_COPY : Can be used for copy commands.
*/
std::shared_ptr<CommandQueue> GetCommandQueue(D3D12_COMMAND_LIST_TYPE type = D3D12_COMMAND_LIST_TYPE_DIRECT) const;
// Flush all command queues.
void Flush();
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> CreateDescriptorHeap(UINT numDescriptors, D3D12_DESCRIPTOR_HEAP_TYPE type);
UINT GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE type) const;
protected:
// Create an application instance.
Application(HINSTANCE hInst);
// Destroy the application instance and all windows associated with this application.
virtual ~Application();
Microsoft::WRL::ComPtr<IDXGIAdapter4> GetAdapter(bool bUseWarp);
Microsoft::WRL::ComPtr<ID3D12Device2> CreateDevice(Microsoft::WRL::ComPtr<IDXGIAdapter4> adapter);
bool CheckTearingSupport();
private:
Application(const Application& copy) = delete;
Application& operator=(const Application& other) = delete;
// The application instance handle that this application was created with.
HINSTANCE m_hInstance;
Microsoft::WRL::ComPtr<IDXGIAdapter4> m_dxgiAdapter;
Microsoft::WRL::ComPtr<ID3D12Device2> m_d3d12Device;
std::shared_ptr<CommandQueue> m_DirectCommandQueue;
std::shared_ptr<CommandQueue> m_ComputeCommandQueue;
std::shared_ptr<CommandQueue> m_CopyCommandQueue;
bool m_TearingSupported;
};

View File

@ -0,0 +1,36 @@
# CMakeList.txt : CMake project for dx12tutorial, include source and define
# project specific logic here.
#
cmake_minimum_required (VERSION 3.8)
set( SHADER_FILES
Shaders/VertexShader.hlsl
Shaders/PixelShader.hlsl
)
source_group( "Resources\\Shaders" FILES ${SHADER_FILES} )
set_source_files_properties( Shaders/VertexShader.hlsl PROPERTIES
VS_SHADER_TYPE Vertex
VS_SHADER_MODEL 5.1
)
set_source_files_properties( Shaders/PixelShader.hlsl PROPERTIES
VS_SHADER_TYPE Pixel
VS_SHADER_MODEL 5.1
)
add_subdirectory("DirectX-Headers")
# Add source to this project's executable.
add_executable (dx12tutorial WIN32 "main.cpp" "Helpers.h" "CommandQueue.h" "CommandQueue.cpp" "Game.h" "Game.cpp" "Tutorial2.h" "Tutorial2.cpp" "Application.h" "Application.cpp" "Window.h" "Window.cpp" "Events.h" "Keycodes.h" "DX12LibPCH.h" "HighResolutionClock.h" "HighResolutionClock.cpp" ${SHADER_FILES})
target_include_directories(dx12tutorial PRIVATE "./DirectX-Headers/include/directx")
target_link_libraries(dx12tutorial PRIVATE Microsoft::DirectX-Guids Microsoft::DirectX-Headers)
target_link_libraries(dx12tutorial PRIVATE d3d12.lib dxgi.lib d3dcompiler.lib dxguid.lib Pathcch.lib Shlwapi.lib)
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET dx12tutorial PROPERTY CXX_STANDARD 20)
endif()
# TODO: Add tests and install targets if needed.

View File

@ -0,0 +1,136 @@
#include "DX12LibPCH.h"
#include "CommandQueue.h"
CommandQueue::CommandQueue(Microsoft::WRL::ComPtr<ID3D12Device2> device, D3D12_COMMAND_LIST_TYPE type)
: m_FenceValue(0)
, m_CommandListType(type)
, m_d3d12Device(device)
{
D3D12_COMMAND_QUEUE_DESC desc = {};
desc.Type = type;
desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
desc.NodeMask = 0;
ThrowIfFailed(m_d3d12Device->CreateCommandQueue(&desc, IID_PPV_ARGS(&m_d3d12CommandQueue)));
ThrowIfFailed(m_d3d12Device->CreateFence(m_FenceValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_d3d12Fence)));
m_FenceEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
assert(m_FenceEvent && "Failed to create fence event handle.");
}
CommandQueue::~CommandQueue()
{
}
uint64_t CommandQueue::Signal()
{
uint64_t fenceValue = ++m_FenceValue;
m_d3d12CommandQueue->Signal(m_d3d12Fence.Get(), fenceValue);
return fenceValue;
}
bool CommandQueue::IsFenceComplete(uint64_t fenceValue)
{
return m_d3d12Fence->GetCompletedValue() >= fenceValue;
}
void CommandQueue::WaitForFenceValue(uint64_t fenceValue)
{
if (!IsFenceComplete(fenceValue))
{
m_d3d12Fence->SetEventOnCompletion(fenceValue, m_FenceEvent);
::WaitForSingleObject(m_FenceEvent, DWORD_MAX);
}
}
void CommandQueue::Flush()
{
WaitForFenceValue(Signal());
}
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> CommandQueue::CreateCommandAllocator()
{
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> commandAllocator;
ThrowIfFailed(m_d3d12Device->CreateCommandAllocator(m_CommandListType, IID_PPV_ARGS(&commandAllocator)));
return commandAllocator;
}
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> CommandQueue::CreateCommandList(Microsoft::WRL::ComPtr<ID3D12CommandAllocator> allocator)
{
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList;
ThrowIfFailed(m_d3d12Device->CreateCommandList(0, m_CommandListType, allocator.Get(), nullptr, IID_PPV_ARGS(&commandList)));
return commandList;
}
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> CommandQueue::GetCommandList()
{
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> commandAllocator;
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList;
if (!m_CommandAllocatorQueue.empty() && IsFenceComplete(m_CommandAllocatorQueue.front().fenceValue))
{
commandAllocator = m_CommandAllocatorQueue.front().commandAllocator;
m_CommandAllocatorQueue.pop();
ThrowIfFailed(commandAllocator->Reset());
}
else
{
commandAllocator = CreateCommandAllocator();
}
if (!m_CommandListQueue.empty())
{
commandList = m_CommandListQueue.front();
m_CommandListQueue.pop();
ThrowIfFailed(commandList->Reset(commandAllocator.Get(), nullptr));
}
else
{
commandList = CreateCommandList(commandAllocator);
}
// Associate the command allocator with the command list so that it can be
// retrieved when the command list is executed.
ThrowIfFailed(commandList->SetPrivateDataInterface(__uuidof(ID3D12CommandAllocator), commandAllocator.Get()));
return commandList;
}
// Execute a command list.
// Returns the fence value to wait for for this command list.
uint64_t CommandQueue::ExecuteCommandList(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList)
{
commandList->Close();
ID3D12CommandAllocator* commandAllocator;
UINT dataSize = sizeof(commandAllocator);
ThrowIfFailed(commandList->GetPrivateData(__uuidof(ID3D12CommandAllocator), &dataSize, &commandAllocator));
ID3D12CommandList* const ppCommandLists[] = {
commandList.Get()
};
m_d3d12CommandQueue->ExecuteCommandLists(1, ppCommandLists);
uint64_t fenceValue = Signal();
m_CommandAllocatorQueue.emplace(CommandAllocatorEntry{ fenceValue, commandAllocator });
m_CommandListQueue.push(commandList);
// The ownership of the command allocator has been transferred to the ComPtr
// in the command allocator queue. It is safe to release the reference
// in this temporary COM pointer here.
commandAllocator->Release();
return fenceValue;
}
Microsoft::WRL::ComPtr<ID3D12CommandQueue> CommandQueue::GetD3D12CommandQueue() const
{
return m_d3d12CommandQueue;
}

View File

@ -0,0 +1,56 @@
/**
* Wrapper class for a ID3D12CommandQueue.
*/
#pragma once
#include <d3d12.h> // For ID3D12CommandQueue, ID3D12Device2, and ID3D12Fence
#include <wrl.h> // For Microsoft::WRL::ComPtr
#include <cstdint> // For uint64_t
#include <queue> // For std::queue
class CommandQueue
{
public:
CommandQueue(Microsoft::WRL::ComPtr<ID3D12Device2> device, D3D12_COMMAND_LIST_TYPE type);
virtual ~CommandQueue();
// Get an available command list from the command queue.
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> GetCommandList();
// Execute a command list.
// Returns the fence value to wait for for this command list.
uint64_t ExecuteCommandList(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList);
uint64_t Signal();
bool IsFenceComplete(uint64_t fenceValue);
void WaitForFenceValue(uint64_t fenceValue);
void Flush();
Microsoft::WRL::ComPtr<ID3D12CommandQueue> GetD3D12CommandQueue() const;
protected:
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> CreateCommandAllocator();
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> CreateCommandList(Microsoft::WRL::ComPtr<ID3D12CommandAllocator> allocator);
private:
// Keep track of command allocators that are "in-flight"
struct CommandAllocatorEntry
{
uint64_t fenceValue;
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> commandAllocator;
};
using CommandAllocatorQueue = std::queue<CommandAllocatorEntry>;
using CommandListQueue = std::queue< Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> >;
D3D12_COMMAND_LIST_TYPE m_CommandListType;
Microsoft::WRL::ComPtr<ID3D12Device2> m_d3d12Device;
Microsoft::WRL::ComPtr<ID3D12CommandQueue> m_d3d12CommandQueue;
Microsoft::WRL::ComPtr<ID3D12Fence> m_d3d12Fence;
HANDLE m_FenceEvent;
uint64_t m_FenceValue;
CommandAllocatorQueue m_CommandAllocatorQueue;
CommandListQueue m_CommandListQueue;
};

View File

@ -0,0 +1,44 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <shellapi.h> // For CommandLineToArgvW
// The min/max macros conflict with like-named member functions.
// Only use std::min and std::max defined in <algorithm>.
#if defined(min)
#undef min
#endif
#if defined(max)
#undef max
#endif
// In order to define a function called CreateWindow, the Windows macro needs to
// be undefined.
#if defined(CreateWindow)
#undef CreateWindow
#endif
// Windows Runtime Library. Needed for Microsoft::WRL::ComPtr<> template class.
#include <wrl.h>
using namespace Microsoft::WRL;
// DirectX 12 specific headers.
#include <d3d12.h>
#include <dxgi1_6.h>
#include <d3dcompiler.h>
#include <DirectXMath.h>
// D3D12 extension library.
#include <d3dx12.h>
// STL Headers
#include <algorithm>
#include <cassert>
#include <chrono>
#include <map>
#include <memory>
// Helper functions
#include "Helpers.h"

@ -0,0 +1 @@
Subproject commit 2b8cd08a930a39ce27245d072ab4baf54a696d12

View File

@ -0,0 +1,190 @@
#pragma once
#include "KeyCodes.h"
// Base class for all event args
class EventArgs
{
public:
EventArgs()
{}
};
class KeyEventArgs : public EventArgs
{
public:
enum KeyState
{
Released = 0,
Pressed = 1
};
typedef EventArgs base;
KeyEventArgs(KeyCode::Key key, unsigned int c, KeyState state, bool control, bool shift, bool alt)
: Key(key)
, Char(c)
, State(state)
, Control(control)
, Shift(shift)
, Alt(alt)
{}
KeyCode::Key Key; // The Key Code that was pressed or released.
unsigned int Char; // The 32-bit character code that was pressed. This value will be 0 if it is a non-printable character.
KeyState State; // Was the key pressed or released?
bool Control;// Is the Control modifier pressed
bool Shift; // Is the Shift modifier pressed
bool Alt; // Is the Alt modifier pressed
};
class MouseMotionEventArgs : public EventArgs
{
public:
typedef EventArgs base;
MouseMotionEventArgs(bool leftButton, bool middleButton, bool rightButton, bool control, bool shift, int x, int y)
: LeftButton(leftButton)
, MiddleButton(middleButton)
, RightButton(rightButton)
, Control(control)
, Shift(shift)
, X(x)
, Y(y)
{}
bool LeftButton; // Is the left mouse button down?
bool MiddleButton; // Is the middle mouse button down?
bool RightButton; // Is the right mouse button down?
bool Control; // Is the CTRL key down?
bool Shift; // Is the Shift key down?
int X; // The X-position of the cursor relative to the upper-left corner of the client area.
int Y; // The Y-position of the cursor relative to the upper-left corner of the client area.
int RelX; // How far the mouse moved since the last event.
int RelY; // How far the mouse moved since the last event.
};
class MouseButtonEventArgs : public EventArgs
{
public:
enum MouseButton
{
None = 0,
Left = 1,
Right = 2,
Middel = 3
};
enum ButtonState
{
Released = 0,
Pressed = 1
};
typedef EventArgs base;
MouseButtonEventArgs(MouseButton buttonID, ButtonState state, bool leftButton, bool middleButton, bool rightButton, bool control, bool shift, int x, int y)
: Button(buttonID)
, State(state)
, LeftButton(leftButton)
, MiddleButton(middleButton)
, RightButton(rightButton)
, Control(control)
, Shift(shift)
, X(x)
, Y(y)
{}
MouseButton Button; // The mouse button that was pressed or released.
ButtonState State; // Was the button pressed or released?
bool LeftButton; // Is the left mouse button down?
bool MiddleButton; // Is the middle mouse button down?
bool RightButton; // Is the right mouse button down?
bool Control; // Is the CTRL key down?
bool Shift; // Is the Shift key down?
int X; // The X-position of the cursor relative to the upper-left corner of the client area.
int Y; // The Y-position of the cursor relative to the upper-left corner of the client area.
};
class MouseWheelEventArgs : public EventArgs
{
public:
typedef EventArgs base;
MouseWheelEventArgs(float wheelDelta, bool leftButton, bool middleButton, bool rightButton, bool control, bool shift, int x, int y)
: WheelDelta(wheelDelta)
, LeftButton(leftButton)
, MiddleButton(middleButton)
, RightButton(rightButton)
, Control(control)
, Shift(shift)
, X(x)
, Y(y)
{}
float WheelDelta; // How much the mouse wheel has moved. A positive value indicates that the wheel was moved to the right. A negative value indicates the wheel was moved to the left.
bool LeftButton; // Is the left mouse button down?
bool MiddleButton; // Is the middle mouse button down?
bool RightButton; // Is the right mouse button down?
bool Control; // Is the CTRL key down?
bool Shift; // Is the Shift key down?
int X; // The X-position of the cursor relative to the upper-left corner of the client area.
int Y; // The Y-position of the cursor relative to the upper-left corner of the client area.
};
class ResizeEventArgs : public EventArgs
{
public:
typedef EventArgs base;
ResizeEventArgs(int width, int height)
: Width(width)
, Height(height)
{}
// The new width of the window
int Width;
// The new height of the window.
int Height;
};
class UpdateEventArgs : public EventArgs
{
public:
typedef EventArgs base;
UpdateEventArgs(double fDeltaTime, double fTotalTime)
: ElapsedTime(fDeltaTime)
, TotalTime(fTotalTime)
{}
double ElapsedTime;
double TotalTime;
};
class RenderEventArgs : public EventArgs
{
public:
typedef EventArgs base;
RenderEventArgs(double fDeltaTime, double fTotalTime)
: ElapsedTime(fDeltaTime)
, TotalTime(fTotalTime)
{}
double ElapsedTime;
double TotalTime;
};
class UserEventArgs : public EventArgs
{
public:
typedef EventArgs base;
UserEventArgs(int code, void* data1, void* data2)
: Code(code)
, Data1(data1)
, Data2(data2)
{}
int Code;
void* Data1;
void* Data2;
};

View File

@ -0,0 +1,94 @@
#include "DX12LibPCH.h"
#include "Application.h"
#include "Game.h"
#include "Window.h"
Game::Game(const std::wstring& name, int width, int height, bool vSync)
: m_Name(name)
, m_Width(width)
, m_Height(height)
, m_vSync(vSync)
{
}
Game::~Game()
{
assert(!m_pWindow && "Use Game::Destroy() before destruction.");
}
bool Game::Initialize()
{
// Check for DirectX Math library support.
if (!DirectX::XMVerifyCPUSupport())
{
MessageBoxA(NULL, "Failed to verify DirectX Math library support.", "Error", MB_OK | MB_ICONERROR);
return false;
}
m_pWindow = Application::Get().CreateRenderWindow(m_Name, m_Width, m_Height, m_vSync);
m_pWindow->RegisterCallbacks(shared_from_this());
m_pWindow->Show();
return true;
}
void Game::Destroy()
{
Application::Get().DestroyWindow(m_pWindow);
m_pWindow.reset();
}
void Game::OnUpdate(UpdateEventArgs& e)
{
}
void Game::OnRender(RenderEventArgs& e)
{
}
void Game::OnKeyPressed(KeyEventArgs& e)
{
// By default, do nothing.
}
void Game::OnKeyReleased(KeyEventArgs& e)
{
// By default, do nothing.
}
void Game::OnMouseMoved(class MouseMotionEventArgs& e)
{
// By default, do nothing.
}
void Game::OnMouseButtonPressed(MouseButtonEventArgs& e)
{
// By default, do nothing.
}
void Game::OnMouseButtonReleased(MouseButtonEventArgs& e)
{
// By default, do nothing.
}
void Game::OnMouseWheel(MouseWheelEventArgs& e)
{
// By default, do nothing.
}
void Game::OnResize(ResizeEventArgs& e)
{
m_Width = e.Width;
m_Height = e.Height;
}
void Game::OnWindowDestroy()
{
// If the Window which we are registered to is
// destroyed, then any resources which are associated
// to the window must be released.
UnloadContent();
}

View File

@ -0,0 +1,114 @@
/**
* @brief The Game class is the abstract base class for DirecX 12 demos.
*/
#pragma once
#include "Events.h"
#include <memory>
#include <string>
class Window;
class Game : public std::enable_shared_from_this<Game>
{
public:
/**
* Create the DirectX demo using the specified window dimensions.
*/
Game(const std::wstring& name, int width, int height, bool vSync);
virtual ~Game();
int GetClientWidth() const
{
return m_Width;
}
int GetClientHeight() const
{
return m_Height;
}
/**
* Initialize the DirectX Runtime.
*/
virtual bool Initialize();
/**
* Load content required for the demo.
*/
virtual bool LoadContent() = 0;
/**
* Unload demo specific content that was loaded in LoadContent.
*/
virtual void UnloadContent() = 0;
/**
* Destroy any resource that are used by the game.
*/
virtual void Destroy();
protected:
friend class Window;
/**
* Update the game logic.
*/
virtual void OnUpdate(UpdateEventArgs& e);
/**
* Render stuff.
*/
virtual void OnRender(RenderEventArgs& e);
/**
* Invoked by the registered window when a key is pressed
* while the window has focus.
*/
virtual void OnKeyPressed(KeyEventArgs& e);
/**
* Invoked when a key on the keyboard is released.
*/
virtual void OnKeyReleased(KeyEventArgs& e);
/**
* Invoked when the mouse is moved over the registered window.
*/
virtual void OnMouseMoved(MouseMotionEventArgs& e);
/**
* Invoked when a mouse button is pressed over the registered window.
*/
virtual void OnMouseButtonPressed(MouseButtonEventArgs& e);
/**
* Invoked when a mouse button is released over the registered window.
*/
virtual void OnMouseButtonReleased(MouseButtonEventArgs& e);
/**
* Invoked when the mouse wheel is scrolled while the registered window has focus.
*/
virtual void OnMouseWheel(MouseWheelEventArgs& e);
/**
* Invoked when the attached window is resized.
*/
virtual void OnResize(ResizeEventArgs& e);
/**
* Invoked when the registered window instance is destroyed.
*/
virtual void OnWindowDestroy();
std::shared_ptr<Window> m_pWindow;
private:
std::wstring m_Name;
int m_Width;
int m_Height;
bool m_vSync;
};

View File

@ -0,0 +1,14 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <Windows.h> // For HRESULT
// From DXSampleHelper.h
// Source: https://github.com/Microsoft/DirectX-Graphics-Samples
inline void ThrowIfFailed(HRESULT hr)
{
if (FAILED(hr))
{
throw std::exception();
}
}

View File

@ -0,0 +1,63 @@
#include "DX12LibPCH.h"
#include "HighResolutionClock.h"
HighResolutionClock::HighResolutionClock()
: m_DeltaTime(0)
, m_TotalTime(0)
{
m_T0 = std::chrono::high_resolution_clock::now();
}
void HighResolutionClock::Tick()
{
auto t1 = std::chrono::high_resolution_clock::now();
m_DeltaTime = t1 - m_T0;
m_TotalTime += m_DeltaTime;
m_T0 = t1;
}
void HighResolutionClock::Reset()
{
m_T0 = std::chrono::high_resolution_clock::now();
m_DeltaTime = std::chrono::high_resolution_clock::duration();
m_TotalTime = std::chrono::high_resolution_clock::duration();
}
double HighResolutionClock::GetDeltaNanoseconds() const
{
return m_DeltaTime.count() * 1.0;
}
double HighResolutionClock::GetDeltaMicroseconds() const
{
return m_DeltaTime.count() * 1e-3;
}
double HighResolutionClock::GetDeltaMilliseconds() const
{
return m_DeltaTime.count() * 1e-6;
}
double HighResolutionClock::GetDeltaSeconds() const
{
return m_DeltaTime.count() * 1e-9;
}
double HighResolutionClock::GetTotalNanoseconds() const
{
return m_TotalTime.count() * 1.0;
}
double HighResolutionClock::GetTotalMicroseconds() const
{
return m_TotalTime.count() * 1e-3;
}
double HighResolutionClock::GetTotalMilliSeconds() const
{
return m_TotalTime.count() * 1e-6;
}
double HighResolutionClock::GetTotalSeconds() const
{
return m_TotalTime.count() * 1e-9;
}

View File

@ -0,0 +1,39 @@
/**
* High resolution clock used for performing timings.
*/
#pragma once
#include <chrono>
class HighResolutionClock
{
public:
HighResolutionClock();
// Tick the high resolution clock.
// Tick the clock before reading the delta time for the first time.
// Only tick the clock once per frame.
// Use the Get* functions to return the elapsed time between ticks.
void Tick();
// Reset the clock.
void Reset();
double GetDeltaNanoseconds() const;
double GetDeltaMicroseconds() const;
double GetDeltaMilliseconds() const;
double GetDeltaSeconds() const;
double GetTotalNanoseconds() const;
double GetTotalMicroseconds() const;
double GetTotalMilliSeconds() const;
double GetTotalSeconds() const;
private:
// Initial time point.
std::chrono::high_resolution_clock::time_point m_T0;
// Time since last tick.
std::chrono::high_resolution_clock::duration m_DeltaTime;
std::chrono::high_resolution_clock::duration m_TotalTime;
};

View File

@ -0,0 +1,214 @@
#pragma once
// Key code values for Windows
namespace KeyCode
{
enum Key
{
None = 0x00, // No key was pressed
LButton = 0x01, // Left mouse button
RButton = 0x02, // Right mouse button
Cancel = 0x03, // Cancel key
MButton = 0x04, // Middle mouse button
XButton1 = 0x05, // X1 mouse button
XButton2 = 0x06, // X2 mouse button
// 0x07 is undefined
Back = 0x08,
Tab = 0x09,
// 0x0A-0B are reserved
Clear = 0x0c, // The CLEAR key
Enter = 0x0d, // The Enter key
// 0x0E-0F are undefined
ShiftKey = 0x10, // The Shift key
ControlKey = 0x11, // The Ctrl key
AltKey = 0x12, // The Alt key
Pause = 0x13, // The Pause key
Capital = 0x14, // The Caps Lock key
CapsLock = 0x14, // The Caps Lock key
KanaMode = 0x15, // IMI Kana mode
HanguelMode = 0x15, // IMI Hanguel mode (Use HangulMode)
HangulMode = 0x15, // IMI Hangul mode
// 0x16 is undefined
JunjaMode = 0x17, // IMI Janja mode
FinalMode = 0x18, // IMI Final mode
HanjaMode = 0x19, // IMI Hanja mode
KanjiMode = 0x19, // IMI Kanji mode
// 0x1A is undefined
Escape = 0x1b, // The ESC key
IMEConvert = 0x1c, // IMI convert key
IMINoConvert = 0x1d, // IMI noconvert key
IMEAccept = 0x1e, // IMI accept key
IMIModeChange = 0x1f, // IMI mode change key
Space = 0x20, // The Space key
Prior = 0x21, // The Page Up key
PageUp = 0x21, // The Page Up key
Next = 0x22, // The Page Down key
PageDown = 0x22, // The Page Down key
End = 0x23, // The End key
Home = 0x24, // The Home key
Left = 0x25, // The Left arrow key
Up = 0x26, // The Up arrow key
Right = 0x27, // The Right arrow key
Down = 0x28, // The Down arrow key
Select = 0x29, // The Select key
Print = 0x2a, // The Print key
Execute = 0x2b, // The Execute key
PrintScreen = 0x2c, // The Print Screen key
Snapshot = 0x2c, // The Print Screen key
Insert = 0x2d, // The Insert key
Delete = 0x2e, // The Delete key
Help = 0x2f, // The Help key
D0 = 0x30, // 0
D1 = 0x31, // 1
D2 = 0x32, // 2
D3 = 0x33, // 3
D4 = 0x34, // 4
D5 = 0x35, // 5
D6 = 0x36, // 6
D7 = 0x37, // 7
D8 = 0x38, // 8
D9 = 0x39, // 9
// 0x3A - 40 are undefined
A = 0x41, // A
B = 0x42, // B
C = 0x43, // C
D = 0x44, // D
E = 0x45, // E
F = 0x46, // F
G = 0x47, // G
H = 0x48, // H
I = 0x49, // I
J = 0x4a, // J
K = 0x4b, // K
L = 0x4c, // L
M = 0x4d, // M
N = 0x4e, // N
O = 0x4f, // O
P = 0x50, // P
Q = 0x51, // Q
R = 0x52, // R
S = 0x53, // S
T = 0x54, // T
U = 0x55, // U
V = 0x56, // V
W = 0x57, // W
X = 0x58, // X
Y = 0x59, // Y
Z = 0x5a, // Z
LWin = 0x5b, // Left Windows key
RWin = 0x5c, // Right Windows key
Apps = 0x5d, // Apps key
// 0x5E is reserved
Sleep = 0x5f, // The Sleep key
NumPad0 = 0x60, // The Numeric keypad 0 key
NumPad1 = 0x61, // The Numeric keypad 1 key
NumPad2 = 0x62, // The Numeric keypad 2 key
NumPad3 = 0x63, // The Numeric keypad 3 key
NumPad4 = 0x64, // The Numeric keypad 4 key
NumPad5 = 0x65, // The Numeric keypad 5 key
NumPad6 = 0x66, // The Numeric keypad 6 key
NumPad7 = 0x67, // The Numeric keypad 7 key
NumPad8 = 0x68, // The Numeric keypad 8 key
NumPad9 = 0x69, // The Numeric keypad 9 key
Multiply = 0x6a, // The Multiply key
Add = 0x6b, // The Add key
Separator = 0x6c, // The Separator key
Subtract = 0x6d, // The Subtract key
Decimal = 0x6e, // The Decimal key
Divide = 0x6f, // The Divide key
F1 = 0x70, // The F1 key
F2 = 0x71, // The F2 key
F3 = 0x72, // The F3 key
F4 = 0x73, // The F4 key
F5 = 0x74, // The F5 key
F6 = 0x75, // The F6 key
F7 = 0x76, // The F7 key
F8 = 0x77, // The F8 key
F9 = 0x78, // The F9 key
F10 = 0x79, // The F10 key
F11 = 0x7a, // The F11 key
F12 = 0x7b, // The F12 key
F13 = 0x7c, // The F13 key
F14 = 0x7d, // The F14 key
F15 = 0x7e, // The F15 key
F16 = 0x7f, // The F16 key
F17 = 0x80, // The F17 key
F18 = 0x81, // The F18 key
F19 = 0x82, // The F19 key
F20 = 0x83, // The F20 key
F21 = 0x84, // The F21 key
F22 = 0x85, // The F22 key
F23 = 0x86, // The F23 key
F24 = 0x87, // The F24 key
// 0x88 - 0x8f are unassigned
NumLock = 0x90, // The Num Lock key
Scroll = 0x91, // The Scroll Lock key
// 0x92 - 96 are OEM specific
// 0x97 - 9f are unassigned
LShiftKey = 0xa0, // The Left Shift key
RShiftKey = 0xa1, // The Right Shift key
LControlKey = 0xa2, // The Left Control key
RControlKey = 0xa3, // The Right Control key
LMenu = 0xa4, // The Left Alt key
RMenu = 0xa5, // The Right Alt key
BrowserBack = 0xa6, // The Browser Back key
BrowserForward = 0xa7, // The Browser Forward key
BrowserRefresh = 0xa8, // The Browser Refresh key
BrowserStop = 0xa9, // The Browser Stop key
BrowserSearch = 0xaa, // The Browser Search key
BrowserFavorites = 0xab, // The Browser Favorites key
BrowserHome = 0xac, // The Browser Home key
VolumeMute = 0xad, // The Volume Mute key
VolumeDown = 0xae, // The Volume Down key
VolumeUp = 0xaf, // The Volume Up key
MediaNextTrack = 0xb0, // The Next Track key
MediaPreviousTrack = 0xb1, // The Previous Track key
MediaStop = 0xb2, // The Stop Media key
MediaPlayPause = 0xb3, // The Play/Pause Media key
LaunchMail = 0xb4, // The Start Mail key
SelectMedia = 0xb5, // The Select Media key
LaunchApplication1 = 0xb6, // The Launch Application 1 key.
LaunchApplication2 = 0xb7, // The Launch Application 2 key.
// 0xB8 - B9 are reserved
OemSemicolon = 0xba, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ';:' key
Oem1 = 0xba, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ';:' key
OemPlus = 0xbb, // For any country/region, the '+' key
OemComma = 0xbc, // For any country/region, the ',' key
OemMinus = 0xbd, // For any country/region, the '-' key
OemPeriod = 0xbe, // For any country/region, the '.' key
OemQuestion = 0xbf, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '/?' key
Oem2 = 0xbf, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '/?' key
OemTilde = 0xc0, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '`~' key
Oem3 = 0xc0, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '`~' key
// 0xC1 - D7 are reserved
// 0xD8 - DA are unassigned
OemOpenBrackets = 0xdb, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '[{' key
Oem4 = 0xdb, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '[{' key
OemPipe = 0xdc, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '\|' key
Oem5 = 0xdc, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '\|' key
OemCloseBrackets = 0xdd, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ']}' key
Oem6 = 0xdd, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ']}' key
OemQuotes = 0xde, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key
Oem7 = 0xde, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key
Oem8 = 0xdf, // Used for miscellaneous characters; it can vary by keyboard.
// 0xE0 is reserved
// 0xE1 is OEM specific
OemBackslash = 0xe2, // Either the angle bracket key or the backslash key on the RT 102-key keyboard
Oem102 = 0xe2, // Either the angle bracket key or the backslash key on the RT 102-key keyboard
// 0xE3 - E4 OEM specific
ProcessKey = 0xe5, // IME Process key
// 0xE6 is OEM specific
Packet = 0xe7, // Used to pass Unicode characters as if they were keystrokes. The Packet key value is the low word of a 32-bit virtual-key value used for non-keyboard input methods.
// 0xE8 is unassigned
// 0xE9 - F5 OEM specific
Attn = 0xf6, // The Attn key
CrSel = 0xf7, // The CrSel key
ExSel = 0xf8, // The ExSel key
EraseEof = 0xf9, // The Erase EOF key
Play = 0xfa, // The Play key
Zoom = 0xfb, // The Zoom key
NoName = 0xfc, // Reserved
Pa1 = 0xfd, // The PA1 key
OemClear = 0xfe, // The Clear key
};
}

View File

@ -0,0 +1,9 @@
struct PixelShaderInput
{
float4 Color : COLOR;
};
float4 main(PixelShaderInput IN) : SV_Target
{
return IN.Color;
}

View File

@ -0,0 +1,28 @@
struct VertexPosColor
{
float3 Position : POSITION;
float3 Color : COLOR;
};
struct ModelViewProjection
{
matrix MVP;
};
ConstantBuffer<ModelViewProjection> ModelViewProjectionCB : register(b0);
struct VertexShaderOutput
{
float4 Color : COLOR;
float4 Position : SV_Position;
};
VertexShaderOutput main(VertexPosColor IN)
{
VertexShaderOutput OUT;
OUT.Position = mul(ModelViewProjectionCB.MVP, float4(IN.Position, 1.0f));
OUT.Color = float4(IN.Color, 1.0f);
return OUT;
}

View File

@ -0,0 +1,456 @@
#include "Tutorial2.h"
#include "Helpers.h"
#include "Application.h"
#include "CommandQueue.h"
#include "Helpers.h"
#include "Window.h"
#include "Game.h"
#include <shlwapi.h>
#include <wrl.h>
using namespace Microsoft::WRL;
#include <d3dx12.h>
#include <d3dcompiler.h>
#include <algorithm> // For std::min and std::max.
#if defined(min)
#undef min
#endif
#if defined(max)
#undef max
#endif
using namespace DirectX;
// Clamp a value between a min and max range.
template<typename T>
constexpr const T& clamp(const T& val, const T& min, const T& max)
{
return val < min ? min : val > max ? max : val;
}
// Vertex data for a colored cube.
struct VertexPosColor
{
XMFLOAT3 Position;
XMFLOAT3 Color;
};
static VertexPosColor g_Vertices[8] = {
{ XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT3(0.0f, 0.0f, 0.0f) }, // 0
{ XMFLOAT3(-1.0f, 1.0f, -1.0f), XMFLOAT3(0.0f, 1.0f, 0.0f) }, // 1
{ XMFLOAT3(1.0f, 1.0f, -1.0f), XMFLOAT3(1.0f, 1.0f, 0.0f) }, // 2
{ XMFLOAT3(1.0f, -1.0f, -1.0f), XMFLOAT3(1.0f, 0.0f, 0.0f) }, // 3
{ XMFLOAT3(-1.0f, -1.0f, 1.0f), XMFLOAT3(0.0f, 0.0f, 1.0f) }, // 4
{ XMFLOAT3(-1.0f, 1.0f, 1.0f), XMFLOAT3(0.0f, 1.0f, 1.0f) }, // 5
{ XMFLOAT3(1.0f, 1.0f, 1.0f), XMFLOAT3(1.0f, 1.0f, 1.0f) }, // 6
{ XMFLOAT3(1.0f, -1.0f, 1.0f), XMFLOAT3(1.0f, 0.0f, 1.0f) } // 7
};
static WORD g_Indicies[36] =
{
0, 1, 2, 0, 2, 3,
4, 6, 5, 4, 7, 6,
4, 5, 1, 4, 1, 0,
3, 2, 6, 3, 6, 7,
1, 5, 6, 1, 6, 2,
4, 0, 3, 4, 3, 7
};
Tutorial2::Tutorial2(const std::wstring& name, int width, int height, bool vSync)
: super(name, width, height, vSync)
, m_ScissorRect(CD3DX12_RECT(0, 0, LONG_MAX, LONG_MAX))
, m_Viewport(CD3DX12_VIEWPORT(0.0f, 0.0f, static_cast<float>(width), static_cast<float>(height)))
, m_FoV(45.0)
, m_ContentLoaded(false)
{
}
void Tutorial2::UpdateBufferResource(
ComPtr<ID3D12GraphicsCommandList2> commandList,
ID3D12Resource** pDestinationResource,
ID3D12Resource** pIntermediateResource,
size_t numElements, size_t elementSize, const void* bufferData,
D3D12_RESOURCE_FLAGS flags)
{
auto device = Application::Get().GetDevice();
size_t bufferSize = numElements * elementSize;
auto constantHeapBufferPropertiesTypeDefault = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
auto constantResourceDescriptorBufferWithFlags = CD3DX12_RESOURCE_DESC::Buffer(bufferSize, flags);
// Create a committed resource for the GPU resource in a default heap.
ThrowIfFailed(device->CreateCommittedResource(
&constantHeapBufferPropertiesTypeDefault,
D3D12_HEAP_FLAG_NONE,
&constantResourceDescriptorBufferWithFlags,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(pDestinationResource)));
// Create an committed resource for the upload.
if (bufferData)
{
auto constantHeapBufferPropertiesTypeUpload = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
auto constantResourceDescriptorBuffer = CD3DX12_RESOURCE_DESC::Buffer(bufferSize);
ThrowIfFailed(device->CreateCommittedResource(
&constantHeapBufferPropertiesTypeUpload,
D3D12_HEAP_FLAG_NONE,
&constantResourceDescriptorBuffer,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(pIntermediateResource)));
D3D12_SUBRESOURCE_DATA subresourceData = {};
subresourceData.pData = bufferData;
subresourceData.RowPitch = bufferSize;
subresourceData.SlicePitch = subresourceData.RowPitch;
UpdateSubresources(commandList.Get(),
*pDestinationResource, *pIntermediateResource,
0, 0, 1, &subresourceData);
}
}
bool Tutorial2::LoadContent()
{
auto device = Application::Get().GetDevice();
auto commandQueue = Application::Get().GetCommandQueue(D3D12_COMMAND_LIST_TYPE_COPY);
auto commandList = commandQueue->GetCommandList();
// Upload vertex buffer data.
ComPtr<ID3D12Resource> intermediateVertexBuffer;
UpdateBufferResource(commandList,
&m_VertexBuffer, &intermediateVertexBuffer,
_countof(g_Vertices), sizeof(VertexPosColor), g_Vertices);
// Create the vertex buffer view.
m_VertexBufferView.BufferLocation = m_VertexBuffer->GetGPUVirtualAddress();
m_VertexBufferView.SizeInBytes = sizeof(g_Vertices);
m_VertexBufferView.StrideInBytes = sizeof(VertexPosColor);
// Upload index buffer data.
ComPtr<ID3D12Resource> intermediateIndexBuffer;
UpdateBufferResource(commandList,
&m_IndexBuffer, &intermediateIndexBuffer,
_countof(g_Indicies), sizeof(WORD), g_Indicies);
// Create index buffer view.
m_IndexBufferView.BufferLocation = m_IndexBuffer->GetGPUVirtualAddress();
m_IndexBufferView.Format = DXGI_FORMAT_R16_UINT;
m_IndexBufferView.SizeInBytes = sizeof(g_Indicies);
// Create the descriptor heap for the depth-stencil view.
D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc = {};
dsvHeapDesc.NumDescriptors = 1;
dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
ThrowIfFailed(device->CreateDescriptorHeap(&dsvHeapDesc, IID_PPV_ARGS(&m_DSVHeap)));
//HRESULT D3DCompileFromFile(
// [in] LPCWSTR pFileName,
// [in, optional] const D3D_SHADER_MACRO * pDefines,
// [in, optional] ID3DInclude * pInclude,
// [in] LPCSTR pEntrypoint,
// [in] LPCSTR pTarget,
// [in] UINT Flags1,
// [in] UINT Flags2,
// [out] ID3DBlob * *ppCode,
// [out, optional] ID3DBlob * *ppErrorMsgs
//);
#if defined(_DEBUG)
// Enable better shader debugging with the graphics debugging tools.
UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
UINT compileFlags = 0;
#endif
// Load the vertex shader.
ComPtr<ID3DBlob> vertexShaderBlob;
ComPtr<ID3DBlob> vertexShaderBlobErrors;
ThrowIfFailed(D3DCompileFromFile(L"Shaders\\VertexShader.hlsl", NULL, D3D_COMPILE_STANDARD_FILE_INCLUDE, "main", "vs_5_1", compileFlags, 0, &vertexShaderBlob, &vertexShaderBlobErrors));
// Load the pixel shader.
ComPtr<ID3DBlob> pixelShaderBlob;
ComPtr<ID3DBlob> pixelShaderBlobErrors;
ThrowIfFailed(D3DCompileFromFile(L"Shaders\\PixelShader.hlsl", NULL, D3D_COMPILE_STANDARD_FILE_INCLUDE, "main", "ps_5_1", compileFlags, 0, &pixelShaderBlob, &pixelShaderBlobErrors));
// Create the vertex input layout
D3D12_INPUT_ELEMENT_DESC inputLayout[] = {
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
// Create a root signature.
D3D12_FEATURE_DATA_ROOT_SIGNATURE featureData = {};
featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_1;
if (FAILED(device->CheckFeatureSupport(D3D12_FEATURE_ROOT_SIGNATURE, &featureData, sizeof(featureData))))
{
featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_0;
}
// Allow input layout and deny unnecessary access to certain pipeline stages.
D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags =
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS;
// A single 32-bit constant root parameter that is used by the vertex shader.
CD3DX12_ROOT_PARAMETER1 rootParameters[1];
rootParameters[0].InitAsConstants(sizeof(XMMATRIX) / 4, 0, 0, D3D12_SHADER_VISIBILITY_VERTEX);
CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDescription;
rootSignatureDescription.Init_1_1(_countof(rootParameters), rootParameters, 0, nullptr, rootSignatureFlags);
// Serialize the root signature.
ComPtr<ID3DBlob> rootSignatureBlob;
ComPtr<ID3DBlob> errorBlob;
ThrowIfFailed(D3DX12SerializeVersionedRootSignature(&rootSignatureDescription,
featureData.HighestVersion, &rootSignatureBlob, &errorBlob));
// Create the root signature.
ThrowIfFailed(device->CreateRootSignature(0, rootSignatureBlob->GetBufferPointer(),
rootSignatureBlob->GetBufferSize(), IID_PPV_ARGS(&m_RootSignature)));
struct PipelineStateStream
{
CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature;
CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT InputLayout;
CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY PrimitiveTopologyType;
CD3DX12_PIPELINE_STATE_STREAM_VS VS;
CD3DX12_PIPELINE_STATE_STREAM_PS PS;
CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat;
CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats;
} pipelineStateStream;
D3D12_RT_FORMAT_ARRAY rtvFormats = {};
rtvFormats.NumRenderTargets = 1;
rtvFormats.RTFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
pipelineStateStream.pRootSignature = m_RootSignature.Get();
pipelineStateStream.InputLayout = { inputLayout, _countof(inputLayout) };
pipelineStateStream.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
pipelineStateStream.VS = CD3DX12_SHADER_BYTECODE(vertexShaderBlob.Get());
pipelineStateStream.PS = CD3DX12_SHADER_BYTECODE(pixelShaderBlob.Get());
pipelineStateStream.DSVFormat = DXGI_FORMAT_D32_FLOAT;
pipelineStateStream.RTVFormats = rtvFormats;
D3D12_PIPELINE_STATE_STREAM_DESC pipelineStateStreamDesc = {
sizeof(PipelineStateStream), &pipelineStateStream
};
ThrowIfFailed(device->CreatePipelineState(&pipelineStateStreamDesc, IID_PPV_ARGS(&m_PipelineState)));
auto fenceValue = commandQueue->ExecuteCommandList(commandList);
commandQueue->WaitForFenceValue(fenceValue);
m_ContentLoaded = true;
// Resize/Create the depth buffer.
ResizeDepthBuffer(GetClientWidth(), GetClientHeight());
return true;
}
void Tutorial2::UnloadContent()
{
m_ContentLoaded = false;
}
void Tutorial2::ResizeDepthBuffer(int width, int height)
{
if (m_ContentLoaded)
{
// Flush any GPU commands that might be referencing the depth buffer.
Application::Get().Flush();
width = std::max(1, width);
height = std::max(1, height);
auto device = Application::Get().GetDevice();
// Resize screen dependent resources.
// Create a depth buffer.
D3D12_CLEAR_VALUE optimizedClearValue = {};
optimizedClearValue.Format = DXGI_FORMAT_D32_FLOAT;
optimizedClearValue.DepthStencil = { 1.0f, 0 };
auto heapPropertiesTypeDefault = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
auto resourceDescriptor = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_D32_FLOAT, width, height,
1, 0, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);
ThrowIfFailed(device->CreateCommittedResource(
&heapPropertiesTypeDefault,
D3D12_HEAP_FLAG_NONE,
&resourceDescriptor,
D3D12_RESOURCE_STATE_DEPTH_WRITE,
&optimizedClearValue,
IID_PPV_ARGS(&m_DepthBuffer)
));
// Update the depth-stencil view.
D3D12_DEPTH_STENCIL_VIEW_DESC dsv = {};
dsv.Format = DXGI_FORMAT_D32_FLOAT;
dsv.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
dsv.Texture2D.MipSlice = 0;
dsv.Flags = D3D12_DSV_FLAG_NONE;
device->CreateDepthStencilView(m_DepthBuffer.Get(), &dsv,
m_DSVHeap->GetCPUDescriptorHandleForHeapStart());
}
}
void Tutorial2::OnMouseWheel(MouseWheelEventArgs& e)
{
m_FoV -= e.WheelDelta;
m_FoV = clamp(m_FoV, 12.0f, 90.0f);
char buffer[256];
sprintf_s(buffer, "FoV: %f\n", m_FoV);
OutputDebugStringA(buffer);
}
void Tutorial2::OnResize(ResizeEventArgs& e)
{
if (e.Width != GetClientWidth() || e.Height != GetClientHeight())
{
super::OnResize(e);
m_Viewport = CD3DX12_VIEWPORT(0.0f, 0.0f,
static_cast<float>(e.Width), static_cast<float>(e.Height));
ResizeDepthBuffer(e.Width, e.Height);
}
}
void Tutorial2::OnUpdate(UpdateEventArgs& e)
{
static uint64_t frameCount = 0;
static double totalTime = 0.0;
super::OnUpdate(e);
totalTime += e.ElapsedTime;
frameCount++;
if (totalTime > 1.0)
{
double fps = frameCount / totalTime;
char buffer[512];
sprintf_s(buffer, "FPS: %f\n", fps);
OutputDebugStringA(buffer);
frameCount = 0;
totalTime = 0.0;
}
// Update the model matrix.
float angle = static_cast<float>(e.TotalTime * 90.0);
const XMVECTOR rotationAxis = XMVectorSet(0, 1, 1, 0);
m_ModelMatrix = XMMatrixRotationAxis(rotationAxis, XMConvertToRadians(angle));
// Update the view matrix.
const XMVECTOR eyePosition = XMVectorSet(0, 0, -10, 1);
const XMVECTOR focusPoint = XMVectorSet(0, 0, 0, 1);
const XMVECTOR upDirection = XMVectorSet(0, 1, 0, 0);
m_ViewMatrix = XMMatrixLookAtLH(eyePosition, focusPoint, upDirection);
// Update the projection matrix.
float aspectRatio = GetClientWidth() / static_cast<float>(GetClientHeight());
m_ProjectionMatrix = XMMatrixPerspectiveFovLH(XMConvertToRadians(m_FoV), aspectRatio, 0.1f, 100.0f);
}
// Transition a resource
void Tutorial2::TransitionResource(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList,
Microsoft::WRL::ComPtr<ID3D12Resource> resource,
D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState)
{
CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(
resource.Get(),
beforeState, afterState);
commandList->ResourceBarrier(1, &barrier);
}
// Clear a render target.
void Tutorial2::ClearRTV(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList,
D3D12_CPU_DESCRIPTOR_HANDLE rtv, FLOAT* clearColor)
{
commandList->ClearRenderTargetView(rtv, clearColor, 0, nullptr);
}
void Tutorial2::OnRender(RenderEventArgs& e)
{
super::OnRender(e);
auto commandQueue = Application::Get().GetCommandQueue(D3D12_COMMAND_LIST_TYPE_DIRECT);
auto commandList = commandQueue->GetCommandList();
UINT currentBackBufferIndex = m_pWindow->GetCurrentBackBufferIndex();
auto backBuffer = m_pWindow->GetCurrentBackBuffer();
auto rtv = m_pWindow->GetCurrentRenderTargetView();
auto dsv = m_DSVHeap->GetCPUDescriptorHandleForHeapStart();
// Clear the render targets.
{
TransitionResource(commandList, backBuffer,
D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
FLOAT clearColor[] = { 0.4f, 0.6f, 0.9f, 1.0f };
ClearRTV(commandList, rtv, clearColor);
ClearDepth(commandList, dsv);
}
commandList->SetPipelineState(m_PipelineState.Get());
commandList->SetGraphicsRootSignature(m_RootSignature.Get());
commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
commandList->IASetVertexBuffers(0, 1, &m_VertexBufferView);
commandList->IASetIndexBuffer(&m_IndexBufferView);
commandList->RSSetViewports(1, &m_Viewport);
commandList->RSSetScissorRects(1, &m_ScissorRect);
commandList->OMSetRenderTargets(1, &rtv, FALSE, &dsv);
// Update the MVP matrix
XMMATRIX mvpMatrix = XMMatrixMultiply(m_ModelMatrix, m_ViewMatrix);
mvpMatrix = XMMatrixMultiply(mvpMatrix, m_ProjectionMatrix);
commandList->SetGraphicsRoot32BitConstants(0, sizeof(XMMATRIX) / 4, &mvpMatrix, 0);
commandList->DrawIndexedInstanced(_countof(g_Indicies), 1, 0, 0, 0);
// Present
{
TransitionResource(commandList, backBuffer,
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
m_FenceValues[currentBackBufferIndex] = commandQueue->ExecuteCommandList(commandList);
currentBackBufferIndex = m_pWindow->Present();
commandQueue->WaitForFenceValue(m_FenceValues[currentBackBufferIndex]);
}
}
void Tutorial2::OnKeyPressed(KeyEventArgs& e)
{
super::OnKeyPressed(e);
switch (e.Key)
{
case KeyCode::Escape:
Application::Get().Quit(0);
break;
case KeyCode::Enter:
if (e.Alt)
{
case KeyCode::F11:
m_pWindow->ToggleFullscreen();
break;
}
case KeyCode::V:
m_pWindow->ToggleVSync();
break;
}
}
void Tutorial2::ClearDepth(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList,
D3D12_CPU_DESCRIPTOR_HANDLE dsv, FLOAT depth)
{
commandList->ClearDepthStencilView(dsv, D3D12_CLEAR_FLAG_DEPTH, depth, 0, 0, nullptr);
}

View File

@ -0,0 +1,99 @@
#pragma once
#include "Game.h"
#include "Window.h"
#include <DirectXMath.h>
class Tutorial2 : public Game
{
public:
using super = Game;
Tutorial2(const std::wstring& name, int width, int height, bool vSync = false);
/**
* Load content required for the demo.
*/
virtual bool LoadContent() override;
/**
* Unload demo specific content that was loaded in LoadContent.
*/
virtual void UnloadContent() override;
protected:
/**
* Update the game logic.
*/
virtual void OnUpdate(UpdateEventArgs& e) override;
/**
* Render stuff.
*/
virtual void OnRender(RenderEventArgs& e) override;
/**
* Invoked by the registered window when a key is pressed
* while the window has focus.
*/
virtual void OnKeyPressed(KeyEventArgs& e) override;
/**
* Invoked when the mouse wheel is scrolled while the registered window has focus.
*/
virtual void OnMouseWheel(MouseWheelEventArgs& e) override;
virtual void OnResize(ResizeEventArgs& e) override;
private:
// Helper functions
// Transition a resource
void TransitionResource(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList,
Microsoft::WRL::ComPtr<ID3D12Resource> resource,
D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState);
// Clear a render target view.
void ClearRTV(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList,
D3D12_CPU_DESCRIPTOR_HANDLE rtv, FLOAT* clearColor);
// Clear the depth of a depth-stencil view.
void ClearDepth(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList,
D3D12_CPU_DESCRIPTOR_HANDLE dsv, FLOAT depth = 1.0f);
// Create a GPU buffer.
void UpdateBufferResource(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList,
ID3D12Resource** pDestinationResource, ID3D12Resource** pIntermediateResource,
size_t numElements, size_t elementSize, const void* bufferData,
D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE);
// Resize the depth buffer to match the size of the client area.
void ResizeDepthBuffer(int width, int height);
uint64_t m_FenceValues[Window::BufferCount] = {};
// Vertex buffer for the cube.
Microsoft::WRL::ComPtr<ID3D12Resource> m_VertexBuffer;
D3D12_VERTEX_BUFFER_VIEW m_VertexBufferView;
// Index buffer for the cube.
Microsoft::WRL::ComPtr<ID3D12Resource> m_IndexBuffer;
D3D12_INDEX_BUFFER_VIEW m_IndexBufferView;
// Depth buffer.
Microsoft::WRL::ComPtr<ID3D12Resource> m_DepthBuffer;
// Descriptor heap for depth buffer.
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> m_DSVHeap;
// Root signature
Microsoft::WRL::ComPtr<ID3D12RootSignature> m_RootSignature;
// Pipeline state object.
Microsoft::WRL::ComPtr<ID3D12PipelineState> m_PipelineState;
D3D12_VIEWPORT m_Viewport;
D3D12_RECT m_ScissorRect;
float m_FoV;
DirectX::XMMATRIX m_ModelMatrix;
DirectX::XMMATRIX m_ViewMatrix;
DirectX::XMMATRIX m_ProjectionMatrix;
bool m_ContentLoaded;
};

View File

@ -0,0 +1,366 @@
#include "DX12LibPCH.h"
#include "Application.h"
#include "CommandQueue.h"
#include "Window.h"
#include "Game.h"
Window::Window(HWND hWnd, const std::wstring& windowName, int clientWidth, int clientHeight, bool vSync)
: m_hWnd(hWnd)
, m_WindowName(windowName)
, m_ClientWidth(clientWidth)
, m_ClientHeight(clientHeight)
, m_VSync(vSync)
, m_Fullscreen(false)
, m_FrameCounter(0)
{
Application& app = Application::Get();
m_IsTearingSupported = app.IsTearingSupported();
m_dxgiSwapChain = CreateSwapChain();
m_d3d12RTVDescriptorHeap = app.CreateDescriptorHeap(BufferCount, D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
m_RTVDescriptorSize = app.GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
UpdateRenderTargetViews();
}
Window::~Window()
{
// Window should be destroyed with Application::DestroyWindow before
// the window goes out of scope.
assert(!m_hWnd && "Use Application::DestroyWindow before destruction.");
}
HWND Window::GetWindowHandle() const
{
return m_hWnd;
}
const std::wstring& Window::GetWindowName() const
{
return m_WindowName;
}
void Window::Show()
{
::ShowWindow(m_hWnd, SW_SHOW);
}
/**
* Hide the window.
*/
void Window::Hide()
{
::ShowWindow(m_hWnd, SW_HIDE);
}
void Window::Destroy()
{
if (auto pGame = m_pGame.lock())
{
// Notify the registered game that the window is being destroyed.
pGame->OnWindowDestroy();
}
if (m_hWnd)
{
DestroyWindow(m_hWnd);
m_hWnd = nullptr;
}
}
int Window::GetClientWidth() const
{
return m_ClientWidth;
}
int Window::GetClientHeight() const
{
return m_ClientHeight;
}
bool Window::IsVSync() const
{
return m_VSync;
}
void Window::SetVSync(bool vSync)
{
m_VSync = vSync;
}
void Window::ToggleVSync()
{
SetVSync(!m_VSync);
}
bool Window::IsFullScreen() const
{
return m_Fullscreen;
}
// Set the fullscreen state of the window.
void Window::SetFullscreen(bool fullscreen)
{
if (m_Fullscreen != fullscreen)
{
m_Fullscreen = fullscreen;
if (m_Fullscreen) // Switching to fullscreen.
{
// Store the current window dimensions so they can be restored
// when switching out of fullscreen state.
::GetWindowRect(m_hWnd, &m_WindowRect);
// Set the window style to a borderless window so the client area fills
// the entire screen.
UINT windowStyle = WS_OVERLAPPEDWINDOW & ~(WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
::SetWindowLongW(m_hWnd, GWL_STYLE, windowStyle);
// Query the name of the nearest display device for the window.
// This is required to set the fullscreen dimensions of the window
// when using a multi-monitor setup.
HMONITOR hMonitor = ::MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTONEAREST);
MONITORINFOEX monitorInfo = {};
monitorInfo.cbSize = sizeof(MONITORINFOEX);
::GetMonitorInfo(hMonitor, &monitorInfo);
::SetWindowPos(m_hWnd, HWND_TOPMOST,
monitorInfo.rcMonitor.left,
monitorInfo.rcMonitor.top,
monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left,
monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top,
SWP_FRAMECHANGED | SWP_NOACTIVATE);
::ShowWindow(m_hWnd, SW_MAXIMIZE);
}
else
{
// Restore all the window decorators.
::SetWindowLong(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
::SetWindowPos(m_hWnd, HWND_NOTOPMOST,
m_WindowRect.left,
m_WindowRect.top,
m_WindowRect.right - m_WindowRect.left,
m_WindowRect.bottom - m_WindowRect.top,
SWP_FRAMECHANGED | SWP_NOACTIVATE);
::ShowWindow(m_hWnd, SW_NORMAL);
}
}
}
void Window::ToggleFullscreen()
{
SetFullscreen(!m_Fullscreen);
}
void Window::RegisterCallbacks(std::shared_ptr<Game> pGame)
{
m_pGame = pGame;
return;
}
void Window::OnUpdate(UpdateEventArgs&)
{
m_UpdateClock.Tick();
if (auto pGame = m_pGame.lock())
{
m_FrameCounter++;
UpdateEventArgs updateEventArgs(m_UpdateClock.GetDeltaSeconds(), m_UpdateClock.GetTotalSeconds());
pGame->OnUpdate(updateEventArgs);
}
}
void Window::OnRender(RenderEventArgs&)
{
m_RenderClock.Tick();
if (auto pGame = m_pGame.lock())
{
RenderEventArgs renderEventArgs(m_RenderClock.GetDeltaSeconds(), m_RenderClock.GetTotalSeconds());
pGame->OnRender(renderEventArgs);
}
}
void Window::OnKeyPressed(KeyEventArgs& e)
{
if (auto pGame = m_pGame.lock())
{
pGame->OnKeyPressed(e);
}
}
void Window::OnKeyReleased(KeyEventArgs& e)
{
if (auto pGame = m_pGame.lock())
{
pGame->OnKeyReleased(e);
}
}
// The mouse was moved
void Window::OnMouseMoved(MouseMotionEventArgs& e)
{
if (auto pGame = m_pGame.lock())
{
pGame->OnMouseMoved(e);
}
}
// A button on the mouse was pressed
void Window::OnMouseButtonPressed(MouseButtonEventArgs& e)
{
if (auto pGame = m_pGame.lock())
{
pGame->OnMouseButtonPressed(e);
}
}
// A button on the mouse was released
void Window::OnMouseButtonReleased(MouseButtonEventArgs& e)
{
if (auto pGame = m_pGame.lock())
{
pGame->OnMouseButtonReleased(e);
}
}
// The mouse wheel was moved.
void Window::OnMouseWheel(MouseWheelEventArgs& e)
{
if (auto pGame = m_pGame.lock())
{
pGame->OnMouseWheel(e);
}
}
void Window::OnResize(ResizeEventArgs& e)
{
// Update the client size.
if (m_ClientWidth != e.Width || m_ClientHeight != e.Height)
{
m_ClientWidth = std::max(1, e.Width);
m_ClientHeight = std::max(1, e.Height);
Application::Get().Flush();
for (int i = 0; i < BufferCount; ++i)
{
m_d3d12BackBuffers[i].Reset();
}
DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
ThrowIfFailed(m_dxgiSwapChain->GetDesc(&swapChainDesc));
ThrowIfFailed(m_dxgiSwapChain->ResizeBuffers(BufferCount, m_ClientWidth,
m_ClientHeight, swapChainDesc.BufferDesc.Format, swapChainDesc.Flags));
m_CurrentBackBufferIndex = m_dxgiSwapChain->GetCurrentBackBufferIndex();
UpdateRenderTargetViews();
}
if (auto pGame = m_pGame.lock())
{
pGame->OnResize(e);
}
}
Microsoft::WRL::ComPtr<IDXGISwapChain4> Window::CreateSwapChain()
{
Application& app = Application::Get();
ComPtr<IDXGISwapChain4> dxgiSwapChain4;
ComPtr<IDXGIFactory4> dxgiFactory4;
UINT createFactoryFlags = 0;
#if defined(_DEBUG)
createFactoryFlags = DXGI_CREATE_FACTORY_DEBUG;
#endif
ThrowIfFailed(CreateDXGIFactory2(createFactoryFlags, IID_PPV_ARGS(&dxgiFactory4)));
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
swapChainDesc.Width = m_ClientWidth;
swapChainDesc.Height = m_ClientHeight;
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.Stereo = FALSE;
swapChainDesc.SampleDesc = { 1, 0 };
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = BufferCount;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
// It is recommended to always allow tearing if tearing support is available.
swapChainDesc.Flags = m_IsTearingSupported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
ID3D12CommandQueue* pCommandQueue = app.GetCommandQueue()->GetD3D12CommandQueue().Get();
ComPtr<IDXGISwapChain1> swapChain1;
ThrowIfFailed(dxgiFactory4->CreateSwapChainForHwnd(
pCommandQueue,
m_hWnd,
&swapChainDesc,
nullptr,
nullptr,
&swapChain1));
// Disable the Alt+Enter fullscreen toggle feature. Switching to fullscreen
// will be handled manually.
ThrowIfFailed(dxgiFactory4->MakeWindowAssociation(m_hWnd, DXGI_MWA_NO_ALT_ENTER));
ThrowIfFailed(swapChain1.As(&dxgiSwapChain4));
m_CurrentBackBufferIndex = dxgiSwapChain4->GetCurrentBackBufferIndex();
return dxgiSwapChain4;
}
// Update the render target views for the swapchain back buffers.
void Window::UpdateRenderTargetViews()
{
auto device = Application::Get().GetDevice();
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_d3d12RTVDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
for (int i = 0; i < BufferCount; ++i)
{
ComPtr<ID3D12Resource> backBuffer;
ThrowIfFailed(m_dxgiSwapChain->GetBuffer(i, IID_PPV_ARGS(&backBuffer)));
device->CreateRenderTargetView(backBuffer.Get(), nullptr, rtvHandle);
m_d3d12BackBuffers[i] = backBuffer;
rtvHandle.Offset(m_RTVDescriptorSize);
}
}
D3D12_CPU_DESCRIPTOR_HANDLE Window::GetCurrentRenderTargetView() const
{
return CD3DX12_CPU_DESCRIPTOR_HANDLE(m_d3d12RTVDescriptorHeap->GetCPUDescriptorHandleForHeapStart(),
m_CurrentBackBufferIndex, m_RTVDescriptorSize);
}
Microsoft::WRL::ComPtr<ID3D12Resource> Window::GetCurrentBackBuffer() const
{
return m_d3d12BackBuffers[m_CurrentBackBufferIndex];
}
UINT Window::GetCurrentBackBufferIndex() const
{
return m_CurrentBackBufferIndex;
}
UINT Window::Present()
{
UINT syncInterval = m_VSync ? 1 : 0;
UINT presentFlags = m_IsTearingSupported && !m_VSync ? DXGI_PRESENT_ALLOW_TEARING : 0;
ThrowIfFailed(m_dxgiSwapChain->Present(syncInterval, presentFlags));
m_CurrentBackBufferIndex = m_dxgiSwapChain->GetCurrentBackBufferIndex();
return m_CurrentBackBufferIndex;
}

View File

@ -0,0 +1,163 @@
/**
* @brief A window for our application.
*/
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <wrl.h>
#include <d3d12.h>
#include <dxgi1_5.h>
#include "Events.h"
#include "HighResolutionClock.h"
// Forward-declare the DirectXTemplate class.
class Game;
class Window
{
public:
// Number of swapchain back buffers.
static const UINT BufferCount = 3;
/**
* Get a handle to this window's instance.
* @returns The handle to the window instance or nullptr if this is not a valid window.
*/
HWND GetWindowHandle() const;
/**
* Destroy this window.
*/
void Destroy();
const std::wstring& GetWindowName() const;
int GetClientWidth() const;
int GetClientHeight() const;
/**
* Should this window be rendered with vertical refresh synchronization.
*/
bool IsVSync() const;
void SetVSync(bool vSync);
void ToggleVSync();
/**
* Is this a windowed window or full-screen?
*/
bool IsFullScreen() const;
// Set the fullscreen state of the window.
void SetFullscreen(bool fullscreen);
void ToggleFullscreen();
/**
* Show this window.
*/
void Show();
/**
* Hide the window.
*/
void Hide();
/**
* Return the current back buffer index.
*/
UINT GetCurrentBackBufferIndex() const;
/**
* Present the swapchain's back buffer to the screen.
* Returns the current back buffer index after the present.
*/
UINT Present();
/**
* Get the render target view for the current back buffer.
*/
D3D12_CPU_DESCRIPTOR_HANDLE GetCurrentRenderTargetView() const;
/**
* Get the back buffer resource for the current back buffer.
*/
Microsoft::WRL::ComPtr<ID3D12Resource> GetCurrentBackBuffer() const;
protected:
// The Window procedure needs to call protected methods of this class.
friend LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
// Only the application can create a window.
friend class Application;
// The DirectXTemplate class needs to register itself with a window.
friend class Game;
Window() = delete;
Window(HWND hWnd, const std::wstring& windowName, int clientWidth, int clientHeight, bool vSync);
virtual ~Window();
// Register a Game with this window. This allows
// the window to callback functions in the Game class.
void RegisterCallbacks(std::shared_ptr<Game> pGame);
// Update and Draw can only be called by the application.
virtual void OnUpdate(UpdateEventArgs& e);
virtual void OnRender(RenderEventArgs& e);
// A keyboard key was pressed
virtual void OnKeyPressed(KeyEventArgs& e);
// A keyboard key was released
virtual void OnKeyReleased(KeyEventArgs& e);
// The mouse was moved
virtual void OnMouseMoved(MouseMotionEventArgs& e);
// A button on the mouse was pressed
virtual void OnMouseButtonPressed(MouseButtonEventArgs& e);
// A button on the mouse was released
virtual void OnMouseButtonReleased(MouseButtonEventArgs& e);
// The mouse wheel was moved.
virtual void OnMouseWheel(MouseWheelEventArgs& e);
// The window was resized.
virtual void OnResize(ResizeEventArgs& e);
// Create the swapchian.
Microsoft::WRL::ComPtr<IDXGISwapChain4> CreateSwapChain();
// Update the render target views for the swapchain back buffers.
void UpdateRenderTargetViews();
private:
// Windows should not be copied.
Window(const Window& copy) = delete;
Window& operator=(const Window& other) = delete;
HWND m_hWnd;
std::wstring m_WindowName;
int m_ClientWidth;
int m_ClientHeight;
bool m_VSync;
bool m_Fullscreen;
HighResolutionClock m_UpdateClock;
HighResolutionClock m_RenderClock;
uint64_t m_FrameCounter;
std::weak_ptr<Game> m_pGame;
Microsoft::WRL::ComPtr<IDXGISwapChain4> m_dxgiSwapChain;
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> m_d3d12RTVDescriptorHeap;
Microsoft::WRL::ComPtr<ID3D12Resource> m_d3d12BackBuffers[BufferCount];
UINT m_RTVDescriptorSize;
UINT m_CurrentBackBufferIndex;
RECT m_WindowRect;
bool m_IsTearingSupported;
};

View File

@ -0,0 +1,8 @@
// dx12tutorial.h : Include file for standard system include files,
// or project specific include files.
#pragma once
#include <iostream>
// TODO: Reference additional headers your program requires here.

View File

@ -0,0 +1,42 @@
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <pathcch.h>
#include "Application.h"
#include "Tutorial2.h"
#include <dxgidebug.h>
void ReportLiveObjects()
{
IDXGIDebug1* dxgiDebug;
DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgiDebug));
dxgiDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_IGNORE_INTERNAL);
dxgiDebug->Release();
}
int CALLBACK wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR lpCmdLine, int nCmdShow)
{
int retCode = 0;
// Set the working directory to the path of the executable.
WCHAR path[MAX_PATH];
HMODULE hModule = GetModuleHandleW(NULL);
if (GetModuleFileNameW(hModule, path, MAX_PATH) > 0)
{
PathCchRemoveFileSpec(path, MAX_PATH);
SetCurrentDirectoryW(path);
}
Application::Create(hInstance);
{
std::shared_ptr<Tutorial2> demo = std::make_shared<Tutorial2>(L"Learning DirectX 12 - Lesson 2", 1280, 720);
retCode = Application::Get().Run(demo);
}
Application::Destroy();
atexit(&ReportLiveObjects);
return retCode;
}

16
dx12tutorial/resource.h Normal file
View File

@ -0,0 +1,16 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by DX12Lib.rc
//
#define APP_ICON 5
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif