-
-
Save mmozeiko/3c9ba3a0ec0c54aff8b2dec7ac724208 to your computer and use it in GitHub Desktop.
instancing example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define COBJMACROS | |
#define WIN32_LEAN_AND_MEAN | |
#include <initguid.h> | |
#include <windows.h> | |
#include <d3dcompiler.h> | |
#include <dxgidebug.h> | |
#include <dxgi1_3.h> | |
#include <d3d11.h> | |
#include "c2d.h" | |
#define _USE_MATH_DEFINES | |
#include <math.h> | |
#include <string.h> | |
#include <stddef.h> | |
#include <intrin.h> | |
#define Assert(cond) do { if (!(cond)) __debugbreak(); } while (0) | |
#define AssertHR(hr) Assert(SUCCEEDED(hr)) | |
#pragma comment (lib, "gdi32") | |
#pragma comment (lib, "user32") | |
#pragma comment (lib, "dxguid") | |
#pragma comment (lib, "dxgi") | |
#pragma comment (lib, "d3d11") | |
#pragma comment (lib, "d3dcompiler") | |
static LRESULT CALLBACK WindowProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) | |
{ | |
switch (msg) | |
{ | |
case WM_DESTROY: | |
PostQuitMessage(0); | |
return 0; | |
} | |
return DefWindowProcW(wnd, msg, wparam, lparam); | |
} | |
int main() | |
{ | |
HINSTANCE instance = 0; | |
WNDCLASSEXW wc = | |
{ | |
.cbSize = sizeof(wc), | |
.lpfnWndProc = WindowProc, | |
.hInstance = instance, | |
.hIcon = LoadIcon(NULL, IDI_APPLICATION), | |
.hCursor = LoadCursor(NULL, IDC_ARROW), | |
.lpszClassName = L"d3d11_window_class", | |
}; | |
ATOM atom = RegisterClassExW(&wc); | |
Assert(atom && "Failed to register window class"); | |
HWND window = CreateWindowExW( | |
WS_EX_APPWINDOW, wc.lpszClassName, L"D3D11 Window", WS_OVERLAPPEDWINDOW, | |
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, | |
NULL, NULL, wc.hInstance, NULL); | |
Assert(window && "Failed to create window"); | |
HRESULT hr; | |
ID3D11Device* device; | |
ID3D11DeviceContext* context; | |
// create D3D11 device & context | |
{ | |
UINT flags = D3D11_CREATE_DEVICE_DEBUG | D3D11_CREATE_DEVICE_BGRA_SUPPORT; | |
D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_0 }; | |
hr = D3D11CreateDevice( | |
NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, levels, ARRAYSIZE(levels), | |
D3D11_SDK_VERSION, &device, NULL, &context); | |
AssertHR(hr); | |
} | |
{ | |
ID3D11InfoQueue* info; | |
ID3D11Device_QueryInterface(device, &IID_ID3D11InfoQueue, (void**)&info); | |
ID3D11InfoQueue_SetBreakOnSeverity(info, D3D11_MESSAGE_SEVERITY_CORRUPTION, TRUE); | |
ID3D11InfoQueue_SetBreakOnSeverity(info, D3D11_MESSAGE_SEVERITY_ERROR, TRUE); | |
ID3D11InfoQueue_Release(info); | |
IDXGIInfoQueue* dxgiInfo; | |
hr = DXGIGetDebugInterface1(0, &IID_IDXGIInfoQueue, (void**)&dxgiInfo); | |
AssertHR(hr); | |
IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfo, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, TRUE); | |
IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfo, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, TRUE); | |
IDXGIInfoQueue_Release(dxgiInfo); | |
} | |
// create DXGI swap chain | |
IDXGISwapChain1* swapChain; | |
{ | |
// get DXGI device from D3D11 device | |
IDXGIDevice* dxgiDevice; | |
IDXGIAdapter* dxgiAdapter; | |
IDXGIFactory2* factory; | |
AssertHR(ID3D11Device_QueryInterface(device, &IID_IDXGIDevice, (void**)&dxgiDevice)); | |
AssertHR(IDXGIDevice_GetAdapter(dxgiDevice, &dxgiAdapter)); | |
AssertHR(IDXGIAdapter_GetParent(dxgiAdapter, &IID_IDXGIFactory2, (void**)&factory)); | |
DXGI_SWAP_CHAIN_DESC1 desc = | |
{ | |
.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, | |
.SampleDesc = { 1, 0 }, | |
.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT, | |
.BufferCount = 2, | |
.Scaling = DXGI_SCALING_STRETCH, | |
.SwapEffect = DXGI_SWAP_EFFECT_DISCARD, | |
}; | |
AssertHR(IDXGIFactory2_CreateSwapChainForHwnd(factory, (IUnknown*)device, window, &desc, NULL, NULL, &swapChain)); | |
// disable silly Alt+Enter changing monitor resolution to match window size | |
IDXGIFactory_MakeWindowAssociation(factory, window, DXGI_MWA_NO_ALT_ENTER); | |
IDXGIFactory2_Release(factory); | |
IDXGIAdapter_Release(dxgiAdapter); | |
IDXGIDevice_Release(dxgiDevice); | |
} | |
struct Vertex | |
{ | |
float position[2]; | |
}; | |
struct VertexInstance | |
{ | |
float position[2]; | |
float color[4]; | |
}; | |
ID3D11Buffer* vbuffer; | |
{ | |
D3D11_BUFFER_DESC desc = | |
{ | |
.ByteWidth = 2*3 * sizeof(struct Vertex) + 3 * sizeof(struct VertexInstance), | |
.Usage = D3D11_USAGE_DYNAMIC, | |
.BindFlags = D3D11_BIND_VERTEX_BUFFER, | |
.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE, | |
}; | |
ID3D11Device_CreateBuffer(device,&desc, NULL, &vbuffer); | |
} | |
// vertex & pixel shaders for drawing triangle, plus input layout for vertex input | |
ID3D11InputLayout* layout; | |
ID3D11VertexShader* vshader; | |
ID3D11PixelShader* pshader; | |
{ | |
// these must match vertex shader input layout (VS_INPUT in vertex shader source below) | |
D3D11_INPUT_ELEMENT_DESC desc[] = | |
{ | |
{ "V_POS", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(struct Vertex, position), D3D11_INPUT_PER_VERTEX_DATA, 0 }, | |
{ "I_POS", 0, DXGI_FORMAT_R32G32_FLOAT, 1, offsetof(struct VertexInstance, position), D3D11_INPUT_PER_INSTANCE_DATA, 1 }, | |
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, offsetof(struct VertexInstance, color), D3D11_INPUT_PER_INSTANCE_DATA, 1 }, | |
}; | |
const char hlsl[] = | |
" struct VS_INPUT \n" | |
" { \n" | |
" float2 vpos : V_POS; \n" | |
" float2 ipos : I_POS; \n" | |
" float4 color : COLOR; \n" | |
" }; \n" | |
" \n" | |
" struct PS_INPUT \n" | |
" { \n" | |
" float4 pos : SV_POSITION; \n" | |
" float4 color : COLOR; \n" | |
" }; \n" | |
" \n" | |
" PS_INPUT vs(VS_INPUT input) \n" | |
" { \n" | |
" PS_INPUT output; \n" | |
" float2 pos = input.vpos + input.ipos; \n" | |
" output.pos = float4(pos, 0, 1); \n" | |
" output.color = input.color; \n" | |
" return output; \n" | |
" } \n" | |
" \n" | |
" float4 ps(PS_INPUT input) : SV_TARGET \n" | |
" { \n" | |
" return input.color; \n" | |
" } \n" | |
; | |
UINT flags = D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS; | |
#ifndef NDEBUG | |
flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; | |
#else | |
flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3; | |
#endif | |
ID3DBlob* error; | |
ID3DBlob* vblob; | |
hr = D3DCompile(hlsl, sizeof(hlsl), NULL, NULL, NULL, "vs", "vs_5_0", flags, 0, &vblob, &error); | |
if (FAILED(hr)) | |
{ | |
const char* message = ID3D10Blob_GetBufferPointer(error); | |
OutputDebugStringA(message); | |
Assert(!"Failed to compile vertex shader!"); | |
} | |
ID3DBlob* pblob; | |
hr = D3DCompile(hlsl, sizeof(hlsl), NULL, NULL, NULL, "ps", "ps_5_0", flags, 0, &pblob, &error); | |
if (FAILED(hr)) | |
{ | |
const char* message = ID3D10Blob_GetBufferPointer(error); | |
OutputDebugStringA(message); | |
Assert(!"Failed to compile pixel shader!"); | |
} | |
ID3D11Device_CreateVertexShader(device, ID3D10Blob_GetBufferPointer(vblob), ID3D10Blob_GetBufferSize(vblob), NULL, &vshader); | |
ID3D11Device_CreatePixelShader(device, ID3D10Blob_GetBufferPointer(pblob), ID3D10Blob_GetBufferSize(pblob), NULL, &pshader); | |
ID3D11Device_CreateInputLayout(device, desc, ARRAYSIZE(desc), ID3D10Blob_GetBufferPointer(vblob), ID3D10Blob_GetBufferSize(vblob), &layout); | |
ID3D10Blob_Release(pblob); | |
ID3D10Blob_Release(vblob); | |
} | |
ID3D11RenderTargetView* rtView = NULL; | |
// show the window | |
ShowWindow(window, SW_SHOWDEFAULT); | |
IDWriteFactory* dwFactory; | |
IDWriteTextFormat* dwTextFormat; | |
ID2D1Factory* d2Factory; | |
ID2D1RenderTarget* d2RenderTarget = NULL; | |
ID2D1SolidColorBrush* d2Brush = NULL; | |
const wchar_t wszText[] = | |
L"Hello World using DirectWrite!\n" | |
// single emoji | |
L"\U0001F603\n" | |
// combined emoji, should render just one glyph | |
L"\U0001F469\U0001F3FD\U0000200D\U0001F468\U0001F3FD\U0000200D\U0001F466\U0001F3FD"; | |
UINT32 cTextLength = sizeof(wszText)/2 - 1; | |
D2D1_FACTORY_OPTIONS options = { D2D1_DEBUG_LEVEL_NONE }; | |
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &IID_ID2D1Factory, &options, (void**)&d2Factory); | |
AssertHR(hr); | |
hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, &IID_IDWriteFactory, (void**)&dwFactory); | |
AssertHR(hr); | |
hr = IDWriteFactory_CreateTextFormat(dwFactory, L"Gabriola", NULL, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 72.0f, L"en-us", &dwTextFormat); | |
AssertHR(hr); | |
hr = IDWriteTextFormat_SetTextAlignment(dwTextFormat, DWRITE_TEXT_ALIGNMENT_CENTER); | |
AssertHR(hr); | |
hr = IDWriteTextFormat_SetParagraphAlignment(dwTextFormat, DWRITE_PARAGRAPH_ALIGNMENT_CENTER); | |
AssertHR(hr); | |
hr = IDWriteTextFormat_SetWordWrapping(dwTextFormat, DWRITE_WORD_WRAPPING_NO_WRAP); | |
D2D1_STROKE_STYLE_PROPERTIES sProps = | |
{ | |
.startCap = D2D1_CAP_STYLE_FLAT, | |
.endCap = D2D1_CAP_STYLE_FLAT, | |
.dashCap = D2D1_CAP_STYLE_FLAT, | |
.lineJoin = D2D1_LINE_JOIN_MITER, | |
.miterLimit = 1.f, | |
.dashStyle = D2D1_DASH_STYLE_SOLID, | |
.dashOffset = 0.f, | |
}; | |
ID2D1StrokeStyle* strokeStyle; | |
hr = ID2D1Factory_CreateStrokeStyle(d2Factory, &sProps, NULL, 0, &strokeStyle); | |
AssertHR(hr); | |
int currentWidth = 0; | |
int currentHeight = 0; | |
for (;;) | |
{ | |
MSG msg; | |
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) | |
{ | |
if (msg.message == WM_QUIT) | |
{ | |
ExitProcess(0); | |
} | |
TranslateMessage(&msg); | |
DispatchMessageW(&msg); | |
} | |
// get current size for window client area | |
RECT rect; | |
GetClientRect(window, &rect); | |
int width = rect.right - rect.left; | |
int height = rect.bottom - rect.top; | |
// resize swap chain if needed | |
if (rtView == NULL || width != currentWidth || height != currentHeight) | |
{ | |
if (rtView) | |
{ | |
// release old swap chain buffers | |
ID3D11DeviceContext_ClearState(context); | |
ID3D11RenderTargetView_Release(rtView); | |
rtView = NULL; | |
} | |
if (d2Brush) ID2D1SolidColorBrush_Release(d2Brush); | |
if (d2RenderTarget) ID2D1RenderTarget_Release(d2RenderTarget); | |
d2Brush = NULL; | |
d2RenderTarget = NULL; | |
// resize to new size for non-zero size | |
if (width != 0 && height != 0) | |
{ | |
IDXGISwapChain1_ResizeBuffers(swapChain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0); | |
// create RenderTarget view for new backbuffer texture | |
ID3D11Texture2D* backbuffer; | |
IDXGISwapChain1_GetBuffer(swapChain, 0, &IID_ID3D11Texture2D, (void**)&backbuffer); | |
ID3D11Device_CreateRenderTargetView(device, (ID3D11Resource*)backbuffer, NULL, &rtView); | |
IDXGISurface* surface; | |
ID3D11Texture2D_QueryInterface(backbuffer, &IID_IDXGISurface, (void**)&surface); | |
D2D1_RENDER_TARGET_PROPERTIES rtProps = | |
{ | |
.type = D2D1_RENDER_TARGET_TYPE_DEFAULT, | |
.pixelFormat = | |
{ | |
.format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, | |
.alphaMode = D2D1_ALPHA_MODE_IGNORE, | |
}, | |
.dpiX = 0, | |
.dpiY = 0, | |
.usage = D2D1_RENDER_TARGET_USAGE_NONE, | |
.minLevel = D2D1_FEATURE_LEVEL_DEFAULT, | |
}; | |
AssertHR(ID2D1Factory_CreateDxgiSurfaceRenderTarget(d2Factory, surface, &rtProps, &d2RenderTarget)); | |
D2D1_COLOR_F yellow = { 1, 1, 0, 1 }; | |
AssertHR(ID2D1RenderTarget_CreateSolidColorBrush(d2RenderTarget, &yellow, NULL, &d2Brush)); | |
IDXGISurface_Release(surface); | |
ID3D11Texture2D_Release(backbuffer); | |
} | |
currentWidth = width; | |
currentHeight = height; | |
} | |
if (rtView) | |
{ | |
D3D11_MAPPED_SUBRESOURCE mapped; | |
ID3D11DeviceContext_Map(context, (ID3D11Resource*)vbuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); | |
struct Vertex* vtx = mapped.pData; | |
float w = (float)width; | |
float h = (float)height; | |
const float qsize = 400; | |
const float pos[3][2] = { { 100, 300 }, { 300, 500 }, { 600, 300 } }; | |
const float col[3][4] = { {1,0,0,1}, {0,1,0,1}, {0,0,1,1} }; | |
const float corner[4][2] = { { 0, 0 }, { 0, 1 }, { 1, 1 }, { 1, 0 } }; | |
{ | |
float ax = (qsize * corner[0][0]) / w * 2 - 1; | |
float ay = (qsize * corner[0][1]) / h * 2 - 1; | |
float bx = (qsize * corner[1][0]) / w * 2 - 1; | |
float by = (qsize * corner[1][1]) / h * 2 - 1; | |
float cx = (qsize * corner[2][0]) / w * 2 - 1; | |
float cy = (qsize * corner[2][1]) / h * 2 - 1; | |
float dx = (qsize * corner[3][0]) / w * 2 - 1; | |
float dy = (qsize * corner[3][1]) / h * 2 - 1; | |
*vtx++ = (struct Vertex) { { ax, -ay } }; | |
*vtx++ = (struct Vertex) { { cx, -cy } }; | |
*vtx++ = (struct Vertex) { { bx, -by } }; | |
*vtx++ = (struct Vertex) { { cx, -cy } }; | |
*vtx++ = (struct Vertex) { { ax, -ay } }; | |
*vtx++ = (struct Vertex) { { dx, -dy } }; | |
} | |
struct VertexInstance* ivtx = (void*)vtx; | |
for (int q=0; q<3; q++) | |
{ | |
const float* c = col[q]; | |
const float* p = pos[q]; | |
float x = p[0] / w * 2; | |
float y = p[1] / h * 2; | |
*ivtx++ = (struct VertexInstance) { { x, -y }, { c[0], c[1], c[2], c[3] } }; | |
} | |
ID3D11DeviceContext_Unmap(context, (ID3D11Resource*)vbuffer, 0); | |
D3D11_VIEWPORT viewport = | |
{ | |
.TopLeftX = 0, | |
.TopLeftY = 0, | |
.Width = w, | |
.Height = h, | |
.MinDepth = 0, | |
.MaxDepth = 1, | |
}; | |
// clear screen | |
FLOAT color[] = { 0.127f, 0.306f, 0.850f, 1.f }; | |
ID3D11DeviceContext_ClearRenderTargetView(context, rtView, color); | |
// Input Assembler | |
ID3D11DeviceContext_IASetInputLayout(context, layout); | |
ID3D11DeviceContext_IASetPrimitiveTopology(context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); | |
UINT strides[2] = { sizeof(struct Vertex), sizeof(struct VertexInstance) }; | |
UINT offsets[2] = { 0, 2*3 * sizeof(struct Vertex) }; | |
ID3D11Buffer* vbuffers[2] = { vbuffer, vbuffer }; | |
ID3D11DeviceContext_IASetVertexBuffers(context, 0, 2, vbuffers, strides, offsets); | |
ID3D11DeviceContext_VSSetShader(context, vshader, NULL, 0); | |
ID3D11DeviceContext_PSSetShader(context, pshader, NULL, 0); | |
ID3D11DeviceContext_RSSetViewports(context, 1, &viewport); | |
ID3D11DeviceContext_OMSetRenderTargets(context, 1, &rtView, NULL); | |
ID3D11DeviceContext_DrawInstanced(context, 3*2, 3, 0, 0); | |
ID2D1RenderTarget_BeginDraw(d2RenderTarget); | |
D2D1_RECT_F layout = { 200, 300, 200 + 600, 300 + 300 }; | |
ID2D1RenderTarget_DrawText(d2RenderTarget, wszText, cTextLength, dwTextFormat, &layout, d2Brush, D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT, DWRITE_MEASURING_MODE_NATURAL); | |
D2D1_ROUNDED_RECT rrect = { | |
.rect = { 200, 700, 200 + 600, 700 + 50 }, | |
.radiusX = 25.f, | |
.radiusY = 25.f, | |
}; | |
ID2D1RenderTarget_DrawRoundedRectangle(d2RenderTarget, &rrect, d2Brush, 5.f, strokeStyle); | |
hr = ID2D1RenderTarget_EndDraw(d2RenderTarget, NULL, NULL); | |
if (hr == D2DERR_RECREATE_TARGET) { } // error | |
AssertHR(hr); | |
} | |
hr = IDXGISwapChain1_Present(swapChain, 1, 0); | |
if (hr == DXGI_STATUS_OCCLUDED) | |
{ | |
Sleep(10); | |
} | |
else | |
{ | |
AssertHR(hr); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment