introduction to directx 11
DESCRIPTION
A short introduction to WinAPI and DirectX 11 that I created for Game Designers' Students Club on Poznań University of Technology. It isn't finished, but still I hope I can help someone with it :) //edit: I've just found out that some text in the source code is cropped, here you can find the online version of the presentation (in which you can scroll the code): http://jotunheim.kmarciniak.com/prezentacje/directx/online/#/TRANSCRIPT
INTRODUCTION TO
Note: this was supposed to be in Polish, but Quicksanddoesn't have latin-ext subset. Sorry!
ABOUT ME on Twitter, on the Web
3rd year student of Computer Science at the PoznańUniversity of TechnologyC++ user for... quite a lot of time (since before high school)my bachelor's thesis will be about implementing realtimeglobal illumination algorithms, the example will be a gameon DirectX 11 in C++
@hun7err kmarciniak.com
WHAT IS DIRECTX?multimedia application API
2D/3D graphics (DirectDraw, Direct2D/Direct3D [D3D])sound/music (DirectSound, DirectMusic)handling input (DirectInput)
OpenGL alternative (or the other way around)
HISTORY
Source: http://www.technama.com/wp-content/uploads/2010/11/Directx-Timeline.jpg
DirectX 11.1 was released on 1st August 2012, DirectX 12 isstill to be released.
WHY DIRECTX?
GAMES USING DIRECTXBattlefield 4Call of Duty: GhostsCrysis 3The Witcher (1-3)Metro 2033...
Generally speaking AAA games on CryEngine, FrostbiteEngine, REDengine, ... (but not only AAA, small indie projects
as well)
HARDWARE VENDORSUPPORT
EXPERIENCE
SUPPORTED LANGUAGESC#C++Visual Basic (wait, what?)
DIRECTX VERSUS OPENGLRenderTargetViews and Framebuffers (no default rendertarget in DX)swapChain in DXshaders: HLSL and GLSL
INTRODUCED IN DIRECTX 11hardware tessellation supportnew RenderTarget formats like R11B11G10 (see PhilipHammer's talk on rendering in 'Lords of the Fallen' fromDigitalDragons 2014)... (to-do)
DRAWBACKSDirectX is not cross-platformDirectX API changes constantly (trust me, I've experiencedthat)WinAPI is pure evil
LET'S GO!
WINMAINheader: Windows.h
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
hInstance - handle to current instancehPrevInstance - handle to previous instancelpCmdLine - cmd line argumentsnCmdShow - how should the window appear whencreated
WNDCLASSEX - struct that holds window class information
WINDOW CREATION (1/2)
WNDCLASSEX wc;ZeroMemory(&wc, sizeof(WNDCLASSEX)); // eq. of memset(&wc, 0, sizeof(WNDCLASSEX))
wc.cbSize = sizeof(WNDCLASSEX); // size (no ****, Sherlock)wc.style = CS_HREDRAW | CS_VREDRAW; // window style: redraw after horizontal and vertical movementwc.lpfnWndProc = WindowProc; // what function shall we use to handle messages from Windows?wc.hInstance = hInstance; // application instancewc.hCursor = LoadCursor(NULL, IDC_ARROW); // guess ;-)wc.hbrBackground = (HBRUSH)COLOR_WINDOW; // ̂--wc.lpszClassName = L"MyWindowClass"; // name of the window class
RegisterClassEx(&wc); // register the window class
HWND - i ow andle
WINDOW CREATION (2/2)Wnd h
HWND CreateWindowEx(DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance,// handle to application instance (again?) // spoiler: it's not the last time (noooooo-) LPVOID lpParam); // usually NULL
BOOL ShowWindow(HWND hWnd, int nCmdShow);
MESSAGE PROCESSING (1/4)
LRESULT CALLBACK WindowProc(HWND hWnd, // remember? UINT message, // message identifier WPARAM wParam, // more message information LPARAM lParam // more information about more message information);
What happens when an event occurs? Well, *we* have to:
MESSAGE PROCESSING (2/4)
use an appropriate function that gets message from thequeuecall TranslateMessage() that is used to... well, it doessomethingcall DispatchMessage() to execute the appropriateWindowProc function
Blocking calls? ain't nobody got time for that!
Passing (0,0) in (wMsgFilterMin, wMsgFilterMax) means wedon't care what we get
MESSAGE PROCESSING (3/4)
BOOL GetMessage(LPMSG lpMsg, // pointer to message struct HWND hWnd, UINT wMsgFilterMin, // lowest msg id value UINT wMsgFilterMax // highest msg id value);// ̂-- blocking, evilBOOL PeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg); // PM_REMOVE/PM_NOREMOVE
Complete main loop:
MESSAGE PROCESSING(4/4)
MSG msg = {0};while(TRUE){ if(PeekMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg);
DispatchMessage(&msg);
if(msg.message == WM_QUIT) break; } else { // rendering and other stuff (physics, AI etc.) }}
THAT'S ALL, FOLKS!
Just kidding. You thought that was all, didn't you?WindowProc function implementation:
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lparam){ switch(message) { case WM_DESTROY: { PostQuitMessage( 0); return 0; } break; } return DefWindowProc(hWnd, message, wParam, lParam);}
THE FUN PART: DIRECTX
DIRECTX(INFRA)STRUCTURE
Source: http://msdn.microsoft.com/en-us/library/windows/desktop/bb205075(v=vs.85).aspx
DXGI = Microsoft DirectX Graphics Infrastructure
returns true if HRESULT represents a failed status
DEVICE, DEVICE CONTEXTdevice - "represents the video adapter"device context - manages the gpu (important!)
BOOL FAILED( HRESULT hr);
COM = Component Object Model
call Release() on every COM object and see the world aroundyou becoming a better place.
COM OBJECTS ANDCLEANING
SwapChain is a thing that is used for setting up bufferswapping.
IDXGISwapChain interface is used for that, descriptionstored in DXGI_SWAP_CHAIN_DESC
SWAPCHAIN (1/3)
SWAPCHAIN (2/3)
typedef struct DXGI_SWAP_CHAIN_DESC { DXGI_MODE_DESC BufferDesc; DXGI_SAMPLE_DESC SampleDesc; DXGI_USAGE BufferUsage; UINT BufferCount; HWND OutputWindow; BOOL Windowed; DXGI_SWAP_EFFECT SwapEffect; UINT Flags;} DXGI_SWAP_CHAIN_DESC;
SWAPCHAIN (3/3)
typedef struct DXGI_MODE_DESC { UINT Width; UINT Height; DXGI_RATIONAL RefreshRate; DXGI_FORMAT Format; DXGI_MODE_SCANLINE_ORDER ScanlineOrdering; DXGI_MODE_SCALING Scaling;} DXGI_MODE_DESC;
typedef enum DXGI_FORMAT { DXGI_FORMAT_UNKNOWN = 0, ..., DXGI_FORMAT_R8G8B8A8_UNORM = 28, ..., DXGI_FORMAT_B4G4R4A4_UNORM = 115, DXGI_FORMAT_FORCE_UINT = 0xffffffffUL} DXGI_FORMAT;
HRESULT D3D11CreateDeviceAndSwapChain( _In_ IDXGIAdapter *pAdapter, // if NULL, uses the default adapter _In_ D3D_DRIVER_TYPE DriverType, // D3D_DRIVER_TYPE_HARDWARE/_SOFTWARE/_REFERENCE/_UNKNOWN _In_ HMODULE Software, // a handle to a DLL that implements software rasterizer if DriverType == SOFTWARE _In_ UINT Flags, _In_ const D3D_FEATURE_LEVEL *pFeatureLevels, // array of features _In_ UINT FeatureLevels, // size of pFeatureLevels _In_ UINT SDKVersion, // D3D11_SDK_VERSION _In_ const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, _Out_ IDXGISwapChain **ppSwapChain, _Out_ ID3D11Device **ppDevice, _Out_ D3D_FEATURE_LEVEL *pFeatureLevel, _Out_ ID3D11DeviceContext **ppImmediateContext);
INITIALIZATION
IDXGISwapChain *swapchain;ID3D11Device *dev;ID3D11DeviceContext *devcon;DXGI_SWAP_CHAIN_DESC scd;
ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));
scd.BufferCount = 1;scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;scd.OutputWindow = hWnd;scd.SampleDesc.Count = 4;scd.Windowed = TRUE;
D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL, D3D11_SDK_VERSION, &scd, &swapchain, &dev, NULL, &devcon);
RENDER TARGETS
__uuidof operator returns GUID
SETTING RENDER TARGET
ID3D11RenderTargetView *backbuffer;
ID3D11Texture2D *pBackBuffer;swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);// ̂-- __uuidof -> here returns const IID, LPVOID = void* (we all love void*)
dev->CreateRenderTargetView(pBackBuffer, NULL, &backbuffer);pBackBuffer->Release();
devcon->OMSetRenderTargets(1, &backbuffer, NULL);// The "OM" prefix in this method's name comes from the new, simplified graphics pipeline model, where subsequent stages are called: Input Assembler (IA), Vertex Shader (VS), Hull Shader (HS), Tesselator Stage, Domain Shader (DS), Geometry Shader (GS), Stream Output (SO), Rasterizer Stage (RS), Pixel Shader (PS), Output Merger (OM) and separate Compute Shader (CS)
MSDN HAS SPOKENA GUID is a 128-bit value consisting of onegroup of 8 hexadecimal digits, followed bythree groups of 4 hexadecimal digits each,followed by one group of 12 hexadecimal
digits. The following example GUID shows thegroupings of hexadecimal digits in a GUID:
6B29FC40-CA47-1067-B31D-00DD010662DA
used to translate window coordinates to normalized <-1,1>
VIEWPORTS
D3D11_VIEWPORT viewport;ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));
viewport.TopLeftX = 0;viewport.TopLeftY = 0;viewport.Width = 800;viewport.Height = 600;
devcon->RSSetviewports(1, &viewport);
fullscreen: scd.Flags =DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH,
swapchain->SetFullscreenState()
PRESENTING RESULTS
HRESULT Present( [in] UINT SyncInterval, // 0 -> no synchronisation [in] UINT Flags);
HRESULT SetFullscreenState( [in] BOOL Fullscreen, [in] IDXGIOutput *pTarget);
Shaders and effects (.fx)
SHADERS, SHADERS
HRESULT WINAPI D3DCompileFromFile( in LPCWSTR pFileName, // shader file name in_opt const D3D_SHADER_MACRO *pDefines,// an array of D3D_SHADER_MACRO structures describing shader macros (I don't know what it is either) in_opt ID3DInclude *pInclude, // pointer to an ID3DInclude interface [...] // ̂-- D3D_COMPILE_STANDARD_FILE_INCLUDE is recommended instead of NULL in LPCSTR pEntrypoint, // entry point in LPCSTR pTarget, // a string specifying set of shader features (like shader model) // ̂-- vs_4_0, ps_4_0, gs_4_0, ... in UINT Flags1, // shader compile options for HLSL in UINT Flags2, // like above, but for effects out ID3DBlob **ppCode, // pointer to a blob object for compiled shader code out_opt ID3DBlob **ppErrorMsgs // );
SHADER USAGE
ID3D10Blob *pVertexShaderBlob, *pPixelShaderBlob; // that's not a mistakeID3D11VertexShader *pVertexShader, *pPixelShader; // remember to use Release(), save pandas!
D3DCompileFromFile((LPCWSTR)m_VertexShaderName.c_str(), 0, 0, m_VertexShaderEntryPointName, D3DCompileFromFile((LPCWSTR)m_PixelShaderName.c_str(), 0, 0, m_PixelShaderEntryPointName,
dev->CreateVertexShader(pVertexShaderBlob->GetBufferPointer(), pVertexShaderBlob->GetBufferSize(), NULL, &pVertexShader);dev->CreatePixelShader(...);
devcon->VSSetShader(pVertexShader);devcon->PSSetShader(pPixelShader);
HLSL CODE EXAMPLE
struct VOut{ float4 position : SV_POSITION; float4 color : COLOR;};
VOut VShader(float4 position : POSITION, float4 color : COLOR){ VOut output;
output.position = position; output.color = color;
return output;}
float4 PShader(float4 position : SV_POSITION, float4 color : COLOR) : SV_TARGET{ return color;}
UNDER CONSTRUCTION :(
QUESTIONS?
THANK YOU FOR YOUR ATTENTION
Sources:
Microsoft Developer Network
directxtutorial.com
Raster Tek