// example how to set up D3D11 rendering // set to 0 to create resizable window #define WINDOW_WIDTH 1280 #define WINDOW_HEIGHT 720 // do you need depth buffer? #define WINDOW_DEPTH 1 // do you need stencil buffer? #define WINDOW_STENCIL 0 // use sRGB or MSAA for color buffer // set 0 to disable, or 1 to enable sRGB // typical values for MSAA are 2, 4, 8, 16, ... // when enabled, D3D11 cannot use more modern lower-latency flip swap effect on Windows 8.1/10 // instead you can use sRGB/MSAA render target and copy it to non-sRGB window #define WINDOW_SRGB 0 #define WINDOW_MSAA 0 // do you need vsync? #define WINDOW_VSYNC 1 // keep this enabled when debugging #define USE_DEBUG_MODE 1 #define COBJMACROS #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #pragma comment (lib, "user32.lib") #pragma comment (lib, "dxgi.lib") #pragma comment (lib, "d3d11.lib") #pragma comment (lib, "dxguid.lib") #define SAFE_RELEASE(release, obj) if (obj) release##_Release(obj) #define LOG_AND_RETURN_ERROR(hr, msg) do \ { \ if (FAILED(hr)) \ { \ LogWin32Error(hr, msg); \ return hr; \ } \ } while (0) struct Vertex { float x, y; float r, g, b; }; static const struct Vertex vertices[] = { { 0.0f, 0.5f, 1.f, 0.f, 0.f }, { 0.5f, -0.5f, 0.f, 1.f, 0.f }, { -0.5f, -0.5f, 0.f, 0.f, 1.f }, }; // You can compile shader to bytecode at build time, this will increase startup // performance and also will avoid dependency on d3dcompiler dll file. // To do so, put the shader source in shader.hlsl file and run these two commands: // fxc.exe /nologo /T vs_4_0_level_9_0 /E vs /O3 /WX /Zpc /Ges /Fh d3d11_vshader.h /Vn d3d11_vshader /Qstrip_reflect /Qstrip_debug /Qstrip_priv shader.hlsl // fxc.exe /nologo /T ps_4_0_level_9_0 /E ps /O3 /WX /Zpc /Ges /Fh d3d11_pshader.h /Vn d3d11_pshader /Qstrip_reflect /Qstrip_debug /Qstrip_priv shader.hlsl // then set next setting to 1 #define USE_PRECOMPILED_SHADERS 0 #if USE_PRECOMPILED_SHADERS #include "d3d11_vshader.h" #include "d3d11_pshader.h" #else #pragma comment (lib, "d3dcompiler.lib") static const char d3d11_shader[] = "struct VS_INPUT \n" "{ \n" " float2 pos : POSITION; \n" " float3 col : COLOR0; \n" "}; \n" " \n" "struct PS_INPUT \n" "{ \n" " float4 pos : SV_POSITION; \n" " float3 col : COLOR0; \n" "}; \n" " \n" "PS_INPUT vs(VS_INPUT input) \n" "{ \n" " PS_INPUT output; \n" " output.pos = float4(input.pos.xy, 0.f, 1.f); \n" " output.col = input.col; \n" " return output; \n" "} \n" " \n" "float4 ps(PS_INPUT input) : SV_Target \n" "{ \n" " return float4(input.col, 1.f); \n" "} \n"; #endif // is window visible? // if this is 0 you can skip rendering part in your code to save time static int render_occluded; static IDXGISwapChain* render_swapchain; static ID3D11Device* render_device; static ID3D11DeviceContext* render_context; static ID3D11DeviceContext1* render_context1; static ID3D11RenderTargetView* render_window_rtview; #if WINDOW_DEPTH || WINDOW_STENCIL static ID3D11DepthStencilView* render_window_dpview; #endif static HANDLE render_frame_latency_wait; static ID3D11RasterizerState* render_raster_state; static ID3D11DepthStencilState* render_depthstencil_state; static ID3D11BlendState* render_blend_state; static ID3D11PixelShader* render_pixel_shader; static ID3D11VertexShader* render_vertex_shader; static ID3D11InputLayout* render_input_layout; static ID3D11Buffer* render_vertex_buffer; static void LogWin32Error(DWORD err, const char* msg) { OutputDebugStringA(msg); OutputDebugStringA("!\n"); LPWSTR str; if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPWSTR)&str, 0, NULL)) { OutputDebugStringW(str); LocalFree(str); } } static void LogWin32LastError(const char* msg) { LogWin32Error(GetLastError(), msg); } static HRESULT FatalDeviceLostError() { MessageBoxW(NULL, L"Cannot recreate D3D11 device, it is reset or removed!", L"Error", MB_ICONEXCLAMATION); return E_FAIL; } // called when device & all d3d resources needs to be released // can happen multiple times (e.g. after device is removed/reset) static void RenderDestroy() { if (render_context) { ID3D11DeviceContext_ClearState(render_context); } SAFE_RELEASE(ID3D11Buffer, render_vertex_buffer); SAFE_RELEASE(ID3D11InputLayout, render_input_layout); SAFE_RELEASE(ID3D11VertexShader, render_vertex_shader); SAFE_RELEASE(ID3D11PixelShader, render_pixel_shader); SAFE_RELEASE(ID3D11RasterizerState, render_raster_state); SAFE_RELEASE(ID3D11DepthStencilState, render_depthstencil_state); SAFE_RELEASE(ID3D11BlendState, render_blend_state); #if WINDOW_DEPTH || WINDOW_STENCIL SAFE_RELEASE(ID3D11DepthStencilView, render_window_dpview); #endif SAFE_RELEASE(ID3D11RenderTargetView, render_window_rtview); SAFE_RELEASE(ID3D11DeviceContext, render_context); SAFE_RELEASE(ID3D11Device, render_device); SAFE_RELEASE(IDXGISwapChain, render_swapchain); render_context1 = NULL; render_frame_latency_wait = NULL; } // called any time device needs to be created // can happen multiple times (e.g. after device is removed/reset) static HRESULT RenderCreate(HWND wnd) { HRESULT hr; D3D_FEATURE_LEVEL level; // device, context { UINT flags = D3D11_CREATE_DEVICE_SINGLETHREADED; #if USE_DEBUG_MODE flags |= D3D11_CREATE_DEVICE_DEBUG; #endif if (FAILED(hr = D3D11CreateDevice( NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, NULL, 0, D3D11_SDK_VERSION, &render_device, &level, &render_context))) { if (FAILED(hr = D3D11CreateDevice( NULL, D3D_DRIVER_TYPE_WARP, NULL, flags, NULL, 0, D3D11_SDK_VERSION, &render_device, &level, &render_context))) { LOG_AND_RETURN_ERROR(hr, "D3D11CreateDevice failed"); } } hr = ID3D11DeviceContext_QueryInterface(render_context, &IID_ID3D11DeviceContext1, &render_context1); if (SUCCEEDED(hr)) { // using ID3D11DeviceContext1 to discard render target ID3D11DeviceContext_Release(render_context); render_context = (ID3D11DeviceContext*)render_context1; } } // swap chain { IDXGIFactory* factory; if (FAILED(hr = CreateDXGIFactory(&IID_IDXGIFactory, &factory))) { LOG_AND_RETURN_ERROR(hr, "CreateDXGIFactory failed"); } DXGI_SWAP_CHAIN_DESC desc = { .BufferDesc = { #if WINDOW_SRGB .Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, #else .Format = DXGI_FORMAT_R8G8B8A8_UNORM, #endif .RefreshRate = { .Numerator = 60, .Denominator = 1, }, }, .SampleDesc = { #if WINDOW_MSAA .Count = WINDOW_MSAA, #else .Count = 1, #endif .Quality = 0, }, .BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT, .OutputWindow = wnd, .Windowed = TRUE, }; #if !WINDOW_SRGB && !WINDOW_MSAA // Windows 10 and up desc.BufferCount = 2; desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; desc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; if (FAILED(IDXGIFactory_CreateSwapChain(factory, (IUnknown*)render_device, &desc, &render_swapchain))) { // Windows 8.1 desc.BufferCount = 2; desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; desc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown*)render_device, &desc, &render_swapchain); } #else hr = E_FAIL; #endif if (FAILED(hr)) { // older Windows desc.BufferCount = 1; desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; desc.Flags = 0; hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown*)render_device, &desc, &render_swapchain); LOG_AND_RETURN_ERROR(hr, "IDXGIFactory::CreateSwapChain failed"); } if (desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT) { IDXGISwapChain2* swapchain2; if (SUCCEEDED(hr = IDXGISwapChain_QueryInterface(render_swapchain, &IID_IDXGISwapChain2, &swapchain2))) { // using IDXGISwapChain2 for frame latency control render_frame_latency_wait = IDXGISwapChain2_GetFrameLatencyWaitableObject(swapchain2); IDXGISwapChain2_Release(swapchain2); } } hr = IDXGIFactory_MakeWindowAssociation(factory, wnd, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER); LOG_AND_RETURN_ERROR(hr, "IDXGIFactory::MakeWindowAssociation failed"); IDXGIFactory_Release(factory); } // rasterizer state { D3D11_RASTERIZER_DESC desc = { .FillMode = D3D11_FILL_SOLID, .CullMode = D3D11_CULL_BACK, .FrontCounterClockwise = FALSE, .DepthBias = 0, .DepthBiasClamp = 0, .SlopeScaledDepthBias = 0.f, .DepthClipEnable = TRUE, .ScissorEnable = FALSE, .MultisampleEnable = WINDOW_MSAA > 0, .AntialiasedLineEnable = FALSE, }; hr = ID3D11Device_CreateRasterizerState(render_device, &desc, &render_raster_state); LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateRasterizerState failed"); } #if WINDOW_DEPTH || WINDOW_STENCIL // depth & stencil state { D3D11_DEPTH_STENCIL_DESC desc = { .DepthEnable = WINDOW_DEPTH ? TRUE : FALSE, .DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL, .DepthFunc = D3D11_COMPARISON_LESS, .StencilEnable = FALSE, // if you need stencil, set up it here .StencilReadMask = 0, .StencilWriteMask = 0, }; hr = ID3D11Device_CreateDepthStencilState(render_device, &desc, &render_depthstencil_state); LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateDepthStencilState failed"); } #endif // blend state { D3D11_BLEND_DESC desc = { .AlphaToCoverageEnable = FALSE, .IndependentBlendEnable = FALSE, .RenderTarget = { { .BlendEnable = FALSE, .SrcBlend = D3D11_BLEND_SRC_ALPHA, .DestBlend = D3D11_BLEND_INV_SRC_ALPHA, .BlendOp = D3D11_BLEND_OP_ADD, .SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA, .DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA, .BlendOpAlpha = D3D11_BLEND_OP_ADD, .RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL, }, }, }; hr = ID3D11Device_CreateBlendState(render_device, &desc, &render_blend_state); LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateBlendState failed"); } #if !USE_PRECOMPILED_SHADERS UINT shader_flags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS #if USE_DEBUG_MODE | D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG; #else | D3DCOMPILE_OPTIMIZATION_LEVEL3; #endif #endif // vertex shader & input layout { D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(struct Vertex, x), D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(struct Vertex, r), D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; ID3DBlob* code = NULL; const void* vshader; size_t vshader_size; #if USE_PRECOMPILED_SHADERS vshader = d3d11_vshader; vshader_size = sizeof(d3d11_vshader); #else ID3DBlob* error; hr = D3DCompile(d3d11_shader, sizeof(d3d11_shader)-1, NULL, NULL, NULL, "vs", "vs_4_0_level_9_0", shader_flags, 0, &code, &error); if (FAILED(hr)) { const void* data = ID3D10Blob_GetBufferPointer(error); size_t size = ID3D10Blob_GetBufferSize(error); char msg[1024]; lstrcpynA(msg, data, (int)size); msg[size] = 0; OutputDebugStringA(msg); ID3D10Blob_Release(error); LOG_AND_RETURN_ERROR(hr, "D3DCompile vs failed"); } vshader = ID3D10Blob_GetBufferPointer(code); vshader_size = ID3D10Blob_GetBufferSize(code); #endif hr = ID3D11Device_CreateVertexShader(render_device, vshader, vshader_size, NULL, &render_vertex_shader); if (FAILED(hr)) { SAFE_RELEASE(ID3D10Blob, code); LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateVertexShader failed"); } hr = ID3D11Device_CreateInputLayout(render_device, layout, _countof(layout), vshader, vshader_size, &render_input_layout); SAFE_RELEASE(ID3D10Blob, code); LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateInputLayout failed"); } // pixel shader { ID3DBlob* code = NULL; const void* pshader; size_t pshader_size; #if USE_PRECOMPILED_SHADERS pshader = d3d11_pshader; pshader_size = sizeof(d3d11_pshader); #else ID3DBlob* error; hr = D3DCompile(d3d11_shader, sizeof(d3d11_shader)-1, NULL, NULL, NULL, "ps", "ps_4_0_level_9_0", shader_flags, 0, &code, &error); if (FAILED(hr)) { const void* data = ID3D10Blob_GetBufferPointer(error); size_t size = ID3D10Blob_GetBufferSize(error); char msg[1024]; lstrcpynA(msg, data, (int)size); msg[size] = 0; OutputDebugStringA(msg); ID3D10Blob_Release(error); LOG_AND_RETURN_ERROR(hr, "D3DCompile ps failed"); } pshader = ID3D10Blob_GetBufferPointer(code); pshader_size = ID3D10Blob_GetBufferSize(code); #endif hr = ID3D11Device_CreatePixelShader(render_device, pshader, pshader_size, NULL, &render_pixel_shader); SAFE_RELEASE(ID3D10Blob, code); LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreatePixelShader failed"); } // vertex buffer { D3D11_BUFFER_DESC desc = { .ByteWidth = sizeof(vertices), .Usage = D3D11_USAGE_IMMUTABLE, .BindFlags = D3D11_BIND_VERTEX_BUFFER, }; D3D11_SUBRESOURCE_DATA data = { .pSysMem = vertices, }; hr = ID3D11Device_CreateBuffer(render_device, &desc, &data, &render_vertex_buffer); LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateBuffer failed"); } return S_OK; } // called when device is reset or removed, recreate it static HRESULT RecreateDevice(HWND wnd) { RenderDestroy(); HRESULT hr = RenderCreate(wnd); if (FAILED(hr)) { RenderDestroy(); } return hr; } // called when window is resized static HRESULT RenderResize(HWND wnd, int width, int height) { if (width == 0 || height == 0) { return S_OK; } if (render_window_rtview) { ID3D11DeviceContext_OMSetRenderTargets(render_context, 0, NULL, NULL); ID3D11RenderTargetView_Release(render_window_rtview); render_window_rtview = NULL; } #if WINDOW_DEPTH || WINDOW_STENCIL if (render_window_dpview) { ID3D11DepthStencilView_Release(render_window_dpview); render_window_dpview = NULL; } #endif UINT flags = render_frame_latency_wait ? DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT : 0; HRESULT hr = IDXGISwapChain_ResizeBuffers(render_swapchain, 0, width, height, DXGI_FORMAT_UNKNOWN, flags); if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET || hr == DXGI_ERROR_DRIVER_INTERNAL_ERROR) { if (FAILED(RecreateDevice(wnd))) { return FatalDeviceLostError(); } } else { LOG_AND_RETURN_ERROR(hr, "IDXGISwapChain::ResizeBuffers failed"); } ID3D11Texture2D* window_buffer; hr = IDXGISwapChain_GetBuffer(render_swapchain, 0, &IID_ID3D11Texture2D, &window_buffer); LOG_AND_RETURN_ERROR(hr, "IDXGISwapChain::GetBuffer failed"); hr = ID3D11Device_CreateRenderTargetView(render_device, (ID3D11Resource*)window_buffer, NULL, &render_window_rtview); ID3D11Texture2D_Release(window_buffer); LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateRenderTargetView failed"); #if WINDOW_DEPTH || WINDOW_STENCIL { D3D11_TEXTURE2D_DESC desc = { .Width = width, .Height = height, .MipLevels = 1, .ArraySize = 1, .Format = (WINDOW_STENCIL || ID3D11Device_GetFeatureLevel(render_device) < D3D_FEATURE_LEVEL_10_0) ? DXGI_FORMAT_D24_UNORM_S8_UINT : DXGI_FORMAT_D32_FLOAT, .SampleDesc = { #if WINDOW_MSAA .Count = WINDOW_MSAA, #else .Count = 1, #endif .Quality = 0, }, .Usage = D3D11_USAGE_DEFAULT, .BindFlags = D3D11_BIND_DEPTH_STENCIL, }; ID3D11Texture2D* depth_stencil; hr = ID3D11Device_CreateTexture2D(render_device, &desc, NULL, &depth_stencil); LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateTexture2D failed"); hr = ID3D11Device_CreateDepthStencilView(render_device, (ID3D11Resource*)depth_stencil, NULL, &render_window_dpview); ID3D11Texture2D_Release(depth_stencil); LOG_AND_RETURN_ERROR(hr, "ID3D11Device::CreateDepthStencilView failed"); } #endif D3D11_VIEWPORT viewport = { .TopLeftX = 0.f, .TopLeftY = 0.f, .Width = (float)width, .Height = (float)height, .MinDepth = 0.f, .MaxDepth = 1.f, }; ID3D11DeviceContext_RSSetViewports(render_context, 1, &viewport); return S_OK; } // called at end of frame static HRESULT RenderPresent(HWND wnd) { HRESULT hr = S_OK; if (render_occluded) { hr = IDXGISwapChain_Present(render_swapchain, 0, DXGI_PRESENT_TEST); if (SUCCEEDED(hr) && hr != DXGI_STATUS_OCCLUDED) { // DXGI window is back to normal, resuming rendering render_occluded = 0; } } if (!render_occluded) { hr = IDXGISwapChain_Present(render_swapchain, WINDOW_VSYNC, 0); } if (hr == DXGI_ERROR_DEVICE_RESET || hr == DXGI_ERROR_DEVICE_REMOVED) { if (FAILED(RecreateDevice(wnd))) { return FatalDeviceLostError(); } RECT rect; if (!GetClientRect(wnd, &rect)) { LogWin32LastError("GetClientRect failed"); } else { RenderResize(wnd, rect.right - rect.left, rect.bottom - rect.top); } } else if (hr == DXGI_STATUS_OCCLUDED) { // DXGI window is occluded, skipping rendering render_occluded = 1; } else { LOG_AND_RETURN_ERROR(hr, "IDXGISwapChain::Present failed"); } if (render_occluded) { Sleep(10); } else { if (render_context1) { ID3D11DeviceContext1_DiscardView(render_context1, (ID3D11View*)render_window_rtview); } } return S_OK; } // this is where rendering happens static void RenderFrame() { if (!render_occluded) { if (render_frame_latency_wait) { WaitForSingleObjectEx(render_frame_latency_wait, INFINITE, TRUE); } #if WINDOW_DEPTH || WINDOW_STENCIL ID3D11DeviceContext_OMSetRenderTargets(render_context, 1, &render_window_rtview, render_window_dpview); ID3D11DeviceContext_OMSetDepthStencilState(render_context, render_depthstencil_state, 0); ID3D11DeviceContext_ClearDepthStencilView(render_context, render_window_dpview, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.f, 0); #else ID3D11DeviceContext_OMSetRenderTargets(render_context, 1, &render_window_rtview, NULL); #endif // clear background FLOAT clear_color[] = { 100.f/255.f, 149.f/255.f, 237.f/255.f, 1.f }; ID3D11DeviceContext_ClearRenderTargetView(render_context, render_window_rtview, clear_color); // draw a triangle const UINT stride = sizeof(struct Vertex); const UINT offset = 0; ID3D11DeviceContext_IASetInputLayout(render_context, render_input_layout); ID3D11DeviceContext_IASetVertexBuffers(render_context, 0, 1, &render_vertex_buffer, &stride, &offset); ID3D11DeviceContext_IASetPrimitiveTopology(render_context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); ID3D11DeviceContext_VSSetShader(render_context, render_vertex_shader, NULL, 0); ID3D11DeviceContext_PSSetShader(render_context, render_pixel_shader, NULL, 0); ID3D11DeviceContext_RSSetState(render_context, render_raster_state); ID3D11DeviceContext_OMSetBlendState(render_context, render_blend_state, NULL, ~0U); ID3D11DeviceContext_Draw(render_context, _countof(vertices), 0); } } static LRESULT CALLBACK WindowProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_CREATE: if (FAILED(RenderCreate(wnd))) { return -1; } return 0; case WM_DESTROY: RenderDestroy(wnd); PostQuitMessage(0); return 0; case WM_SIZE: if (FAILED(RenderResize(wnd, LOWORD(lparam), HIWORD(lparam)))) { DestroyWindow(wnd); } return 0; } return DefWindowProcW(wnd, msg, wparam, lparam); } int WINAPI WinMain(HINSTANCE instance, HINSTANCE prev_instance, LPSTR cmd_line, int cmd_show) { WNDCLASSEXW wc = { .cbSize = sizeof(wc), .lpfnWndProc = WindowProc, .hInstance = instance, .hIcon = LoadIconA(NULL, IDI_APPLICATION), .hCursor = LoadCursorA(NULL, IDC_ARROW), .lpszClassName = L"d3d11_window_class", }; if (!RegisterClassExW(&wc)) { LogWin32LastError("RegisterClassEx failed"); } else { int width = CW_USEDEFAULT; int height = CW_USEDEFAULT; DWORD exstyle = WS_EX_APPWINDOW; DWORD style = WS_OVERLAPPEDWINDOW; if (WINDOW_WIDTH && WINDOW_HEIGHT) { style &= ~WS_THICKFRAME & ~WS_MAXIMIZEBOX; RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT }; if (!AdjustWindowRectEx(&rect, style, FALSE, exstyle)) { LogWin32LastError("AdjustWindowRectEx failed"); style = WS_OVERLAPPEDWINDOW; } else { width = rect.right - rect.left; height = rect.bottom - rect.top; } } HWND wnd = CreateWindowExW(exstyle, wc.lpszClassName, L"D3D11 Window", style | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, wc.hInstance, NULL); if (!wnd) { LogWin32LastError("CreateWindow failed"); } else { for (;;) { MSG msg; if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { break; } TranslateMessage(&msg); DispatchMessageW(&msg); continue; } RenderFrame(); if (FAILED(RenderPresent(wnd))) { break; } } } UnregisterClassW(wc.lpszClassName, wc.hInstance); } return 0; }