Add rest of dx12tutorial
This commit is contained in:
parent
f19e439410
commit
6765e41bdb
9
dx12tutorial/CMakeLists.txt
Normal file
9
dx12tutorial/CMakeLists.txt
Normal 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")
|
||||
61
dx12tutorial/CMakePresets.json
Normal file
61
dx12tutorial/CMakePresets.json
Normal 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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
69
dx12tutorial/dx12tutorial.rc
Normal file
69
dx12tutorial/dx12tutorial.rc
Normal 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
|
||||
598
dx12tutorial/dx12tutorial/Application.cpp
Normal file
598
dx12tutorial/dx12tutorial/Application.cpp
Normal 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;
|
||||
}
|
||||
124
dx12tutorial/dx12tutorial/Application.h
Normal file
124
dx12tutorial/dx12tutorial/Application.h
Normal 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;
|
||||
|
||||
};
|
||||
36
dx12tutorial/dx12tutorial/CMakeLists.txt
Normal file
36
dx12tutorial/dx12tutorial/CMakeLists.txt
Normal 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.
|
||||
136
dx12tutorial/dx12tutorial/CommandQueue.cpp
Normal file
136
dx12tutorial/dx12tutorial/CommandQueue.cpp
Normal 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;
|
||||
}
|
||||
56
dx12tutorial/dx12tutorial/CommandQueue.h
Normal file
56
dx12tutorial/dx12tutorial/CommandQueue.h
Normal 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;
|
||||
};
|
||||
44
dx12tutorial/dx12tutorial/DX12LibPCH.h
Normal file
44
dx12tutorial/dx12tutorial/DX12LibPCH.h
Normal 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"
|
||||
1
dx12tutorial/dx12tutorial/DirectX-Headers
Submodule
1
dx12tutorial/dx12tutorial/DirectX-Headers
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 2b8cd08a930a39ce27245d072ab4baf54a696d12
|
||||
190
dx12tutorial/dx12tutorial/Events.h
Normal file
190
dx12tutorial/dx12tutorial/Events.h
Normal 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;
|
||||
};
|
||||
94
dx12tutorial/dx12tutorial/Game.cpp
Normal file
94
dx12tutorial/dx12tutorial/Game.cpp
Normal 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();
|
||||
}
|
||||
114
dx12tutorial/dx12tutorial/Game.h
Normal file
114
dx12tutorial/dx12tutorial/Game.h
Normal 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;
|
||||
|
||||
};
|
||||
14
dx12tutorial/dx12tutorial/Helpers.h
Normal file
14
dx12tutorial/dx12tutorial/Helpers.h
Normal 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();
|
||||
}
|
||||
}
|
||||
63
dx12tutorial/dx12tutorial/HighResolutionClock.cpp
Normal file
63
dx12tutorial/dx12tutorial/HighResolutionClock.cpp
Normal 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;
|
||||
}
|
||||
39
dx12tutorial/dx12tutorial/HighResolutionClock.h
Normal file
39
dx12tutorial/dx12tutorial/HighResolutionClock.h
Normal 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;
|
||||
};
|
||||
214
dx12tutorial/dx12tutorial/Keycodes.h
Normal file
214
dx12tutorial/dx12tutorial/Keycodes.h
Normal 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
|
||||
};
|
||||
}
|
||||
9
dx12tutorial/dx12tutorial/Shaders/PixelShader.hlsl
Normal file
9
dx12tutorial/dx12tutorial/Shaders/PixelShader.hlsl
Normal file
@ -0,0 +1,9 @@
|
||||
struct PixelShaderInput
|
||||
{
|
||||
float4 Color : COLOR;
|
||||
};
|
||||
|
||||
float4 main(PixelShaderInput IN) : SV_Target
|
||||
{
|
||||
return IN.Color;
|
||||
}
|
||||
28
dx12tutorial/dx12tutorial/Shaders/VertexShader.hlsl
Normal file
28
dx12tutorial/dx12tutorial/Shaders/VertexShader.hlsl
Normal 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;
|
||||
}
|
||||
456
dx12tutorial/dx12tutorial/Tutorial2.cpp
Normal file
456
dx12tutorial/dx12tutorial/Tutorial2.cpp
Normal 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);
|
||||
}
|
||||
99
dx12tutorial/dx12tutorial/Tutorial2.h
Normal file
99
dx12tutorial/dx12tutorial/Tutorial2.h
Normal 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;
|
||||
};
|
||||
366
dx12tutorial/dx12tutorial/Window.cpp
Normal file
366
dx12tutorial/dx12tutorial/Window.cpp
Normal 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;
|
||||
}
|
||||
163
dx12tutorial/dx12tutorial/Window.h
Normal file
163
dx12tutorial/dx12tutorial/Window.h
Normal 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;
|
||||
|
||||
};
|
||||
8
dx12tutorial/dx12tutorial/dx12tutorial.h
Normal file
8
dx12tutorial/dx12tutorial/dx12tutorial.h
Normal 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.
|
||||
42
dx12tutorial/dx12tutorial/main.cpp
Normal file
42
dx12tutorial/dx12tutorial/main.cpp
Normal 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
16
dx12tutorial/resource.h
Normal 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
|
||||
Loading…
x
Reference in New Issue
Block a user