774 lines
25 KiB
C
774 lines
25 KiB
C
|
// 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 <windows.h>
|
||
|
#include <d3d11_1.h>
|
||
|
#include <dxgi1_4.h>
|
||
|
#include <d3dcompiler.h>
|
||
|
#include <stddef.h>
|
||
|
|
||
|
#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;
|
||
|
}
|