#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include <d3d11_1.h>
#include <d3dcompiler.h>
#include <assert.h>
#include <cmath>
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dcompiler.lib")
#define M_PI 3.14159265358979323846
struct float2 {
float x, y;
};
struct float4 {
float x, y, z, w;
};
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
bool DoesFileExist(LPWSTR lpszFilename)
{
return GetFileAttributes(lpszFilename) != INVALID_FILE_ATTRIBUTES;
}
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// Create a window
HWND hwnd;
{
WNDCLASSEXW winClass = {};
winClass.cbSize = sizeof(WNDCLASSEXW);
winClass.style = CS_HREDRAW | CS_VREDRAW;
winClass.lpfnWndProc = &WndProc;
winClass.hInstance = hInstance;
winClass.hCursor = LoadCursorW(0, IDC_ARROW);
winClass.lpszClassName = L"WindowClassName";
if (!RegisterClassEx(&winClass)) {
MessageBoxA(0, "RegisterClassEx failed", "Fatal Error", MB_OK);
return GetLastError();
}
RECT rect = { 0, 0, 1024, 768 };
// Calculates the required size of the window rectangle
AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, FALSE, WS_EX_OVERLAPPEDWINDOW);
LONG width = rect.right - rect.left;
LONG height = rect.bottom - rect.top;
hwnd = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW,
winClass.lpszClassName,
L"Direct3D 11 - Move a square up and down - change color while moving",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
width,
height,
0, 0, hInstance, 0);
if (!hwnd) {
MessageBoxA(0, "CreateWindowEx failed", "Fatal Error", MB_OK);
return GetLastError();
}
}
// Create Direct3D 11 Device and Context
ID3D11Device1* d3d11Device;
ID3D11DeviceContext1* d3d11DeviceContext;
{
ID3D11Device* device;
ID3D11DeviceContext* deviceContext;
D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 };
UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; // Creates a device that supports BGRA Color formats
// Returns the address of a pointer to an ID3D11Device
HRESULT hResult = D3D11CreateDevice(
0, D3D_DRIVER_TYPE_HARDWARE,
0, creationFlags,
featureLevels, ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION, &device,
0, &deviceContext);
if (FAILED(hResult)) {
MessageBoxA(0, "D3D11CreateDevice() failed", "Fatal Error", MB_OK);
return GetLastError();
}
// Get interface of D3D11 Device and Context
hResult = device->QueryInterface(__uuidof(ID3D11Device1), (void**)&d3d11Device);
assert(SUCCEEDED(hResult));
device->Release();
// A query interface queries information from the GPU
hResult = deviceContext->QueryInterface(__uuidof(ID3D11DeviceContext1), (void**)&d3d11DeviceContext);
assert(SUCCEEDED(hResult));
deviceContext->Release();
}
// Create Swap Chain
IDXGISwapChain1* d3d11SwapChain;
{
IDXGIFactory2* dxgiFactory; // Microsoft DirectX Graphics Infrastructure (DXGI)
{
IDXGIDevice1* dxgiDevice;
HRESULT hResult = d3d11Device->QueryInterface(__uuidof(IDXGIDevice1), (void**)&dxgiDevice);
assert(SUCCEEDED(hResult));
IDXGIAdapter* dxgiAdapter;
// Get the adapter for the specified device
hResult = dxgiDevice->GetAdapter(&dxgiAdapter);
assert(SUCCEEDED(hResult));
dxgiDevice->Release();
DXGI_ADAPTER_DESC adapterDesc;
// Describes an adapter (or video card) by using DXGI 1.0
dxgiAdapter->GetDesc(&adapterDesc);
OutputDebugStringA("(adapterDesc.Description) Graphics Device: ");
OutputDebugStringW(adapterDesc.Description);
hResult = dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), (void**)&dxgiFactory);
assert(SUCCEEDED(hResult));
dxgiAdapter->Release();
}
DXGI_SWAP_CHAIN_DESC1 d3d11SwapChainDesc = {};
d3d11SwapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
d3d11SwapChainDesc.SampleDesc.Count = 1;
d3d11SwapChainDesc.SampleDesc.Quality = 0;
d3d11SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
d3d11SwapChainDesc.BufferCount = 2;
d3d11SwapChainDesc.Scaling = DXGI_SCALING_STRETCH;
d3d11SwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
d3d11SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
d3d11SwapChainDesc.Flags = 0;
HRESULT hResult = dxgiFactory->CreateSwapChainForHwnd(d3d11Device, hwnd, &d3d11SwapChainDesc, 0, 0, &d3d11SwapChain);
assert(SUCCEEDED(hResult));
dxgiFactory->Release();
}
// Create Render Target View
ID3D11RenderTargetView* d3d11FrameBufferView;
{
ID3D11Texture2D* d3d11FrameBuffer;
HRESULT hResult = d3d11SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&d3d11FrameBuffer);
assert(SUCCEEDED(hResult));
// Creates a render-target view for accessing resource data
hResult = d3d11Device->CreateRenderTargetView(d3d11FrameBuffer, 0, &d3d11FrameBufferView);
assert(SUCCEEDED(hResult));
d3d11FrameBuffer->Release();
}
// Create Vertex Shader
ID3DBlob* vsBlob;
ID3D11VertexShader* vertexShader;
{
ID3DBlob* shaderCompileErrorsBlob;
HRESULT hResult = D3DCompileFromFile(L"shader.hlsl", nullptr, nullptr, "vs_main", "vs_5_0", 0, 0, &vsBlob, &shaderCompileErrorsBlob);
if (FAILED(hResult))
{
const char* errorString = NULL;
if (hResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
errorString = "Could not compile shader; file not found";
else if (shaderCompileErrorsBlob) {
errorString = (const char*)shaderCompileErrorsBlob->GetBufferPointer();
shaderCompileErrorsBlob->Release();
}
MessageBoxA(0, errorString, "Shader Compiler Error", MB_ICONERROR | MB_OK);
return 1;
}
hResult = d3d11Device->CreateVertexShader(vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), nullptr, &vertexShader);
assert(SUCCEEDED(hResult));
}
// Create Pixel Shader
ID3D11PixelShader* pixelShader;
{
ID3DBlob* psBlob;
ID3DBlob* shaderCompileErrorsBlob;
HRESULT hResult = D3DCompileFromFile(L"shader.hlsl", nullptr, nullptr, "ps_main", "ps_5_0", 0, 0, &psBlob, &shaderCompileErrorsBlob);
if (FAILED(hResult))
{
const char* errorString = NULL;
if (hResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) {
errorString = "Could not compile shader; file not found";
}
else if (shaderCompileErrorsBlob) {
errorString = (const char*)shaderCompileErrorsBlob->GetBufferPointer();
shaderCompileErrorsBlob->Release();
}
MessageBoxA(0, errorString, "Shader Compiler Error", MB_ICONERROR | MB_OK);
return 1;
}
hResult = d3d11Device->CreatePixelShader(psBlob->GetBufferPointer(), psBlob->GetBufferSize(), nullptr, &pixelShader);
assert(SUCCEEDED(hResult));
psBlob->Release();
}
// Create Input Layout
ID3D11InputLayout* inputLayout;
{
D3D11_INPUT_ELEMENT_DESC inputElementDesc[] =
{
{ "POS", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
HRESULT hResult = d3d11Device->CreateInputLayout(inputElementDesc, ARRAYSIZE(inputElementDesc), vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), &inputLayout);
assert(SUCCEEDED(hResult));
vsBlob->Release();
}
// Create Vertex Buffer
ID3D11Buffer* vertexBuffer;
UINT totalVertices;
UINT stride;
UINT offset;
{
float vertexData[] = {
// draw 2 triangle to create a square
// x, y
-0.5f, 0.5f,
0.5f, -0.5f,
-0.5f, -0.5f,
-0.5f, 0.5f,
0.5f, 0.5f,
0.5f, -0.5f
};
stride = 2 * sizeof(float);
totalVertices = sizeof(vertexData) / stride; // 6
offset = 0;
D3D11_BUFFER_DESC vertexBufferDesc = {};
vertexBufferDesc.ByteWidth = sizeof(vertexData);
vertexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
D3D11_SUBRESOURCE_DATA vertexSubresourceData = { vertexData };
HRESULT hResult = d3d11Device->CreateBuffer(&vertexBufferDesc, &vertexSubresourceData, &vertexBuffer);
assert(SUCCEEDED(hResult));
}
// Create Constant Buffer
struct Constants
{
float2 pos;
float2 paddingUnused; // float2 + float2 = 16 byte
float4 color; // color needs to be 16 byte
};
ID3D11Buffer* constantBuffer;
{
D3D11_BUFFER_DESC constantBufferDesc = {};
// ByteWidth must be a multiple of 16
constantBufferDesc.ByteWidth = sizeof(Constants) + 0xf & 0xfffffff0;
constantBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
constantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
constantBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
HRESULT hResult = d3d11Device->CreateBuffer(&constantBufferDesc, nullptr, &constantBuffer);
assert(SUCCEEDED(hResult));
}
// Timing - performance and frequency
LONGLONG startPerfCount = 0;
LONGLONG perfCounterFrequency = 0;
{
LARGE_INTEGER perfCount;
// Retrieves the current value of the performance counter
QueryPerformanceCounter(&perfCount);
startPerfCount = perfCount.QuadPart;
LARGE_INTEGER perfFreq;
// Retrieves the frequency of the performance counter
// The frequency of the performance counter is fixed at system boot and is consistent across all processors
QueryPerformanceFrequency(&perfFreq);
perfCounterFrequency = perfFreq.QuadPart;
}
double currentTimeInSeconds = 0.0;
bool running = true;
while (running)
{
float elapsed_time;
{
double previousTimeInSeconds = currentTimeInSeconds;
LARGE_INTEGER perfCount;
QueryPerformanceCounter(&perfCount);
currentTimeInSeconds = (double)(perfCount.QuadPart - startPerfCount) / (double)perfCounterFrequency;
elapsed_time = (float)(currentTimeInSeconds - previousTimeInSeconds);
if (elapsed_time > (1.f / 60.f)) {
elapsed_time = (1.f / 60.f);
}
}
MSG msg = {};
while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT) {
running = false;
}
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
// Modulate square y-position // move up and down
float2 squarePos = {};
const float posCycleAmplitude = 0.5f;
const float posCyclePeriod = 3.f; // in seconds
const float posCycleFreq = 2 * (float)(M_PI / posCyclePeriod);
squarePos.y = posCycleAmplitude * sinf(posCycleFreq * (float)currentTimeInSeconds);
// Cycle square color // change color while moving
float4 squareColor;
const float colorCyclePeriod = 5.f; // in seconds
const float colorCycleFreq = 2 * (float)(M_PI / colorCyclePeriod);
squareColor.x = 0.5f * (sinf(colorCycleFreq * (float)currentTimeInSeconds) + 1);
squareColor.y = 1 - squareColor.x;
squareColor.z = 0.f;
squareColor.w = 1.f;
// Update constant buffer
D3D11_MAPPED_SUBRESOURCE mappedSubresource;
d3d11DeviceContext->Map(constantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedSubresource);
Constants* constants = (Constants*)(mappedSubresource.pData);
constants->pos = squarePos;
constants->color = squareColor;
d3d11DeviceContext->Unmap(constantBuffer, 0);
FLOAT backgroundColor[4] = { 0.3f, 0.1f, 0.7f, 1.0f };
d3d11DeviceContext->ClearRenderTargetView(d3d11FrameBufferView, backgroundColor);
RECT winClientRect;
GetClientRect(hwnd, &winClientRect);
D3D11_VIEWPORT viewport = { 0.0f, 0.0f, (FLOAT)(winClientRect.right - winClientRect.left), (FLOAT)(winClientRect.bottom - winClientRect.top), 0.0f, 1.0f };
// An array of D3D11_VIEWPORT structures to bind to the device
d3d11DeviceContext->RSSetViewports(1, &viewport);
// Bind one or more render targets atomically and the depth-stencil buffer to the output-merger stage
d3d11DeviceContext->OMSetRenderTargets(1, &d3d11FrameBufferView, nullptr);
// Bind information about the primitive type
d3d11DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
d3d11DeviceContext->IASetInputLayout(inputLayout);
d3d11DeviceContext->VSSetShader(vertexShader, nullptr, 0);
d3d11DeviceContext->PSSetShader(pixelShader, nullptr, 0);
d3d11DeviceContext->VSSetConstantBuffers(0, 1, &constantBuffer);
d3d11DeviceContext->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);
d3d11DeviceContext->Draw(totalVertices, 0);
d3d11SwapChain->Present(1, 0);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
LRESULT result = 0;
switch (msg)
{
case WM_KEYDOWN:
{
if (wparam == VK_ESCAPE) {
DestroyWindow(hwnd);
}
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
default:
result = DefWindowProcW(hwnd, msg, wparam, lparam);
}
return result;
}
/*
run:
*/
// shader.hlsl
cbuffer constants : register(b0)
{
float2 offset;
float4 uniformColor;
};
struct VS_Output {
float4 pos : SV_POSITION;
float4 color : COLOR;
};
VS_Output vs_main(float2 pos : POS)
{
VS_Output output;
output.pos = float4(pos + offset, 0.0f, 1.0f);
output.color = uniformColor;
return output;
}
float4 ps_main(VS_Output input) : SV_Target
{
return input.color;
}