diff options
author | WlodekM <[email protected]> | 2024-06-16 10:35:45 +0300 |
---|---|---|
committer | WlodekM <[email protected]> | 2024-06-16 10:35:45 +0300 |
commit | abef6da56913f1c55528103e60a50451a39628b1 (patch) | |
tree | b3c8092471ecbb73e568cd0d336efa0e7871ee8d /src/Graphics_D3D11.c |
initial commit
Diffstat (limited to 'src/Graphics_D3D11.c')
-rw-r--r-- | src/Graphics_D3D11.c | 1209 |
1 files changed, 1209 insertions, 0 deletions
diff --git a/src/Graphics_D3D11.c b/src/Graphics_D3D11.c new file mode 100644 index 0000000..1b06591 --- /dev/null +++ b/src/Graphics_D3D11.c @@ -0,0 +1,1209 @@ +#include "Core.h" +#if CC_GFX_BACKEND == CC_GFX_BACKEND_D3D11 +#include "_GraphicsBase.h" +#include "Errors.h" +#include "Window.h" +#include "../misc/windows/D3D11Shaders.h" + +/* Avoid pointless includes */ +#define WIN32_LEAN_AND_MEAN +#define NOSERVICE +#define NOMCX +#define NOIME +#define COBJMACROS +#include <d3d11.h> +static const GUID guid_ID3D11Texture2D = { 0x6f15aaf2, 0xd208, 0x4e89, { 0x9a, 0xb4, 0x48, 0x95, 0x35, 0xd3, 0x4f, 0x9c } }; +static const GUID guid_IXDGIDevice = { 0x54ec77fa, 0x1377, 0x44e6, { 0x8c, 0x32, 0x88, 0xfd, 0x5f, 0x44, 0xc8, 0x4c } }; + +// some generally useful debugging links +// https://docs.microsoft.com/en-us/visualstudio/debugger/graphics/visual-studio-graphics-diagnostics +// https://stackoverflow.com/questions/50591937/how-do-i-launch-the-hlsl-debugger-in-visual-studio-2017 +// https://docs.microsoft.com/en-us/visualstudio/debugger/graphics/graphics-object-table?view=vs-2019 +// https://github.com/gfx-rs/wgpu/issues/1171 +// Some generally useful background links +// https://gist.github.com/d7samurai/261c69490cce0620d0bfc93003cd1052 + +static int depthBits; // TODO implement depthBits?? for ZNear calc +static GfxResourceID white_square; + +#ifdef _MSC_VER +#define CC_ALIGNED(x) __declspec(align(x)) +#else +#define CC_ALIGNED(x) __attribute__((aligned(x))) +#endif + +static ID3D11Device* device; +static ID3D11DeviceContext* context; +static IDXGIDevice1* dxgi_device; +static IDXGIAdapter* dxgi_adapter; +static IDXGIFactory1* dxgi_factory; +static IDXGISwapChain* swapchain; +struct ShaderDesc { const void* data; int len; }; + +static void IA_UpdateLayout(void); +static void VS_UpdateShader(void); +static void PS_UpdateShader(void); +static void InitPipeline(void); +static void FreePipeline(void); + +static PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN _D3D11CreateDeviceAndSwapChain; + +static void LoadD3D11Library(void) { + static const struct DynamicLibSym funcs[] = { + DynamicLib_Sym(D3D11CreateDeviceAndSwapChain) + }; + static const cc_string path = String_FromConst("d3d11.dll"); + void* lib; + DynamicLib_LoadAll(&path, funcs, Array_Elems(funcs), &lib); + + if (lib) return; + Logger_FailToStart("Failed to load d3d11.dll. You may need to install Direct3D11.\n\nNOTE: Direct3D11 requires Windows 7 or later\nYou may need to use the Direct3D9 version instead.\n"); +} + +static void CreateDeviceAndSwapChain(void) { + // https://docs.microsoft.com/en-us/windows/uwp/gaming/simple-port-from-direct3d-9-to-11-1-part-1--initializing-direct3d + DWORD createFlags = 0; + D3D_FEATURE_LEVEL fl; + HRESULT hr; +#ifdef _DEBUG + createFlags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + + DXGI_SWAP_CHAIN_DESC desc = { 0 }; + desc.BufferCount = 1; + desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + // RefreshRate intentionally left at 0 so display's refresh rate is used + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.OutputWindow = Window_Main.Handle; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Windowed = TRUE; + + hr = _D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, + createFlags, NULL, 0, D3D11_SDK_VERSION, + &desc, &swapchain, &device, &fl, &context); + if (hr) Logger_Abort2(hr, "Failed to create D3D11 device"); + + // The fog calculation requires reading Z/W of fragment position (SV_POSITION) in pixel shader, + // unfortunately this is unsupported in Direct3d9 (https://docs.microsoft.com/en-us/windows/uwp/gaming/glsl-to-hlsl-reference) + // So for the sake of simplicity and since only a few old GPUs don't support feature level 10 anyways + // https://walbourn.github.io/direct3d-feature-levels/ + // https://github.com/MonoGame/MonoGame/issues/5789 + // I decided to just not support GPUs that do not support at least feature level 10 + if (fl < D3D_FEATURE_LEVEL_10_0) + Logger_FailToStart("Your GPU is too old to support the Direct3D11 version.\nTry using the Direct3D9 version instead.\n"); + + // https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_texture2d_desc + // https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits + // see "Max Texture Dimension" row in Feature Support table + Gfx.MaxTexWidth = fl < D3D_FEATURE_LEVEL_11_0 ? 8192 : 16384; + Gfx.MaxTexHeight = fl < D3D_FEATURE_LEVEL_11_0 ? 8192 : 16384; +} + +void Gfx_Create(void) { + LoadD3D11Library(); + CreateDeviceAndSwapChain(); + Gfx.Created = true; + customMipmapsLevels = true; + Gfx_RestoreState(); +} + +void Gfx_Free(void) { + Gfx_FreeState(); + IDXGISwapChain_Release(swapchain); + ID3D11DeviceContext_Release(context); + +#ifndef _DEBUG + ID3D11Device_Release(device); +#else + ULONG refCount = ID3D11Device_Release(device); + if (refCount == 0) return; // device destroyed with no issues + + ID3D11Debug *d3dDebug; + static const GUID guid_d3dDebug = { 0x79cf2233, 0x7536, 0x4948,{ 0x9d, 0x36, 0x1e, 0x46, 0x92, 0xdc, 0x57, 0x60 } }; + HRESULT hr = ID3D11Device_QueryInterface(device, &guid_d3dDebug, &d3dDebug); + if (SUCCEEDED(hr)) + { + hr = ID3D11Debug_ReportLiveDeviceObjects(d3dDebug, D3D11_RLDO_DETAIL); + } +#endif +} + +static cc_bool inited; +cc_bool Gfx_TryRestoreContext(void) { + // https://docs.microsoft.com/en-us/windows/uwp/gaming/handling-device-lost-scenarios + Gfx_Free(); + Gfx_Create(); + return true; +} + +static void Gfx_FreeState(void) { + if (!inited) return; + inited = false; + + FreeDefaultResources(); + FreePipeline(); + Gfx_DeleteTexture(&white_square); +} + +static void Gfx_RestoreState(void) { + if (inited) return; + inited = true; + + InitDefaultResources(); + gfx_format = -1; + InitPipeline(); + + /* 1x1 dummy white texture */ + struct Bitmap bmp; + BitmapCol pixels[1] = { BITMAPCOLOR_WHITE }; + Bitmap_Init(bmp, 1, 1, pixels); + Gfx_RecreateTexture(&white_square, &bmp, 0, false); +} + + +/*########################################################################################################################* +*---------------------------------------------------------Textures--------------------------------------------------------* +*#########################################################################################################################*/ +static void D3D11_DoMipmaps(ID3D11Resource* texture, int x, int y, struct Bitmap* bmp, int rowWidth) { + BitmapCol* prev = bmp->scan0; + BitmapCol* cur; + + int lvls = CalcMipmapsLevels(bmp->width, bmp->height); + int lvl, width = bmp->width, height = bmp->height; + + for (lvl = 1; lvl <= lvls; lvl++) + { + x /= 2; y /= 2; + if (width > 1) width /= 2; + if (height > 1) height /= 2; + + cur = (BitmapCol*)Mem_Alloc(width * height, 4, "mipmaps"); + GenMipmaps(width, height, cur, prev, rowWidth); + + D3D11_BOX box; + box.front = 0; + box.back = 1; + box.left = x; + box.right = x + width; + box.top = y; + box.bottom = y + height; + + // https://eatplayhate.me/2013/09/29/d3d11-texture-update-costs/ + // Might not be ideal, but seems to work well enough + int stride = width * 4; + ID3D11DeviceContext_UpdateSubresource(context, texture, lvl, &box, cur, stride, stride * height); + + if (prev != bmp->scan0) Mem_Free(prev); + prev = cur; + rowWidth = width; + } + if (prev != bmp->scan0) Mem_Free(prev); +} + +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { + ID3D11Texture2D* tex = NULL; + ID3D11ShaderResourceView* view = NULL; + HRESULT hr; + + D3D11_TEXTURE2D_DESC desc = { 0 }; + desc.Width = bmp->width; + desc.Height = bmp->height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + desc.Usage = D3D11_USAGE_DEFAULT; // TODO D3D11_USAGE_IMMUTABLE when not dynamic + desc.SampleDesc.Count = 1; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + + D3D11_SUBRESOURCE_DATA data; + data.pSysMem = bmp->scan0; + data.SysMemPitch = rowWidth * 4; + data.SysMemSlicePitch = 0; + D3D11_SUBRESOURCE_DATA* src = &data; + + // Direct3D11 specifies pInitialData as an array of D3D11_SUBRESOURCE_DATA of length desc->MipsLevels + // Rather than writing such code just to support the mipmaps case, I went with the simpler approach of + // leaving pInitialData as NULL and specfiying the texture data later using Gfx_UpdateTexturePart + if (mipmaps) { + desc.MipLevels += CalcMipmapsLevels(bmp->width, bmp->height); + src = NULL; + } + + while ((hr = ID3D11Device_CreateTexture2D(device, &desc, src, &tex))) + { + if (hr == E_OUTOFMEMORY) { + // insufficient VRAM or RAM left to allocate texture, try to reduce the memory in use first + // if can't reduce, return 'empty' texture so that at least the game will continue running + if (!Game_ReduceVRAM()) return 0; + } else { + // unknown issue, so don't even try to handle the error + Logger_Abort2(hr, "Failed to create texture"); + } + } + + hr = ID3D11Device_CreateShaderResourceView(device, tex, NULL, &view); + if (hr) Logger_Abort2(hr, "Failed to create view"); + + if (mipmaps) Gfx_UpdateTexture(view, 0, 0, bmp, rowWidth, mipmaps); + return view; +} + +void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, int rowWidth, cc_bool mipmaps) { + ID3D11ShaderResourceView* view = (ID3D11ShaderResourceView*)texId; + ID3D11Resource* res = NULL; + D3D11_BOX box; + box.front = 0; + box.back = 1; + box.left = x; + box.right = x + part->width; + box.top = y; + box.bottom = y + part->height; + + // https://eatplayhate.me/2013/09/29/d3d11-texture-update-costs/ + // Might not be ideal, but seems to work well enough + int stride = rowWidth * 4; + ID3D11ShaderResourceView_GetResource(view, &res); + ID3D11DeviceContext_UpdateSubresource(context, res, 0, &box, part->scan0, stride, stride * part->height); + + if (mipmaps) D3D11_DoMipmaps(res, x, y, part, rowWidth); + ID3D11Resource_Release(res); +} + +void Gfx_DeleteTexture(GfxResourceID* texId) { + ID3D11ShaderResourceView* view = (ID3D11ShaderResourceView*)(*texId); + ID3D11Resource* res = NULL; + + if (view) { + ID3D11ShaderResourceView_GetResource(view, &res); + ID3D11Resource_Release(res); + ID3D11ShaderResourceView_Release(view); + + // note that ID3D11ShaderResourceView_GetResource increments refcount, so need to Release twice + // https://docs.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11view-getresource + ID3D11Resource_Release(res); + } + *texId = NULL; +} + + +/*########################################################################################################################* +*-------------------------------------------------------Index buffers-----------------------------------------------------* +*#########################################################################################################################*/ +GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) { + ID3D11Buffer* buffer = NULL; + cc_uint16 indices[GFX_MAX_INDICES]; + fillFunc(indices, count, obj); + + D3D11_BUFFER_DESC desc = { 0 }; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.ByteWidth = count * sizeof(cc_uint16); + desc.BindFlags = D3D11_BIND_INDEX_BUFFER; + + D3D11_SUBRESOURCE_DATA data; + data.pSysMem = indices; + data.SysMemPitch = 0; + data.SysMemSlicePitch = 0; + + HRESULT hr = ID3D11Device_CreateBuffer(device, &desc, &data, &buffer); + if (hr) Logger_Abort2(hr, "Failed to create index buffer"); + return buffer; +} + +void Gfx_DeleteIb(GfxResourceID* ib) { + ID3D11Buffer* buffer = (ID3D11Buffer*)(*ib); + if (buffer) ID3D11Buffer_Release(buffer); + *ib = NULL; +} + + +/*########################################################################################################################* +*------------------------------------------------------Vertex buffers-----------------------------------------------------* +*#########################################################################################################################*/ +static ID3D11Buffer* CreateVertexBuffer(VertexFormat fmt, int count, cc_bool dynamic) { + ID3D11Buffer* buffer = NULL; + + D3D11_BUFFER_DESC desc = { 0 }; + desc.Usage = dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = dynamic ? D3D11_CPU_ACCESS_WRITE : 0; + desc.ByteWidth = count * strideSizes[fmt]; + desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + /* TODO set data initially */ + + HRESULT hr = ID3D11Device_CreateBuffer(device, &desc, NULL, &buffer); + if (hr) Logger_Abort2(hr, "Failed to create vertex buffer"); + return buffer; +} + +static GfxResourceID Gfx_AllocStaticVb(VertexFormat fmt, int count) { + /* TODO immutable? */ + return CreateVertexBuffer(fmt, count, false); +} + +void Gfx_DeleteVb(GfxResourceID* vb) { + ID3D11Buffer* buffer = (ID3D11Buffer*)(*vb); + if (buffer) ID3D11Buffer_Release(buffer); + *vb = NULL; +} + +static void* tmp; +void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) { + tmp = Mem_TryAlloc(count, strideSizes[fmt]); + return tmp; +} + +void Gfx_UnlockVb(GfxResourceID vb) { + ID3D11Buffer* buffer = (ID3D11Buffer*)vb; + ID3D11DeviceContext_UpdateSubresource(context, buffer, 0, NULL, tmp, 0, 0); + Mem_Free(tmp); + tmp = NULL; +} + + +/*########################################################################################################################* +*--------------------------------------------------Dynamic vertex buffers-------------------------------------------------* +*#########################################################################################################################*/ +static GfxResourceID Gfx_AllocDynamicVb(VertexFormat fmt, int maxVertices) { + return CreateVertexBuffer(fmt, maxVertices, true); +} + +void Gfx_DeleteDynamicVb(GfxResourceID* vb) { + ID3D11Buffer* buffer = (ID3D11Buffer*)(*vb); + if (buffer) ID3D11Buffer_Release(buffer); + *vb = NULL; +} + +static D3D11_MAPPED_SUBRESOURCE mapDesc; +void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) { + ID3D11Buffer* buffer = (ID3D11Buffer*)vb; + mapDesc.pData = NULL; + + HRESULT hr = ID3D11DeviceContext_Map(context, buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapDesc); + if (hr) Logger_Abort2(hr, "Failed to lock dynamic VB"); + return mapDesc.pData; +} + +void Gfx_UnlockDynamicVb(GfxResourceID vb) { + ID3D11Buffer* buffer = (ID3D11Buffer*)vb; + ID3D11DeviceContext_Unmap(context, buffer, 0); + Gfx_BindDynamicVb(vb); +} + + +/*########################################################################################################################* +*-----------------------------------------------------Vertex rendering----------------------------------------------------* +*#########################################################################################################################*/ +void Gfx_SetVertexFormat(VertexFormat fmt) { + if (fmt == gfx_format) return; + gfx_format = fmt; + gfx_stride = strideSizes[fmt]; + + IA_UpdateLayout(); + VS_UpdateShader(); + PS_UpdateShader(); +} + +void Gfx_DrawVb_Lines(int verticesCount) { + ID3D11DeviceContext_IASetPrimitiveTopology(context, D3D11_PRIMITIVE_TOPOLOGY_LINELIST); + ID3D11DeviceContext_Draw(context, verticesCount, 0); + ID3D11DeviceContext_IASetPrimitiveTopology(context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); +} + +void Gfx_DrawVb_IndexedTris(int verticesCount) { + ID3D11DeviceContext_DrawIndexed(context, ICOUNT(verticesCount), 0, 0); +} + +void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) { + ID3D11DeviceContext_DrawIndexed(context, ICOUNT(verticesCount), 0, startVertex); +} + +void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { + ID3D11DeviceContext_DrawIndexed(context, ICOUNT(verticesCount), 0, startVertex); +} + + +/*########################################################################################################################* +*---------------------------------------------------------Matrices--------------------------------------------------------* +*#########################################################################################################################*/ +void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) { + // Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixorthooffcenterrh + // The simplified calculation below uses: L = 0, R = width, T = 0, B = height + // NOTE: This calculation is shared with Direct3D 9 backend + *matrix = Matrix_Identity; + + matrix->row1.x = 2.0f / width; + matrix->row2.y = -2.0f / height; + matrix->row3.z = 1.0f / (zNear - zFar); + + matrix->row4.x = -1.0f; + matrix->row4.y = 1.0f; + matrix->row4.z = zNear / (zNear - zFar); +} + +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } +void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { + // Deliberately swap zNear/zFar in projection matrix calculation to produce + // a projection matrix that results in a reversed depth buffer + // https://developer.nvidia.com/content/depth-precision-visualized + float zNear_ = zFar; + float zFar_ = Reversed_CalcZNear(fov, 24); // TODO don't always hardcode to 24 bits + + // Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixperspectivefovrh + // NOTE: This calculation is shared with Direct3D 9 backend + float c = Cotangent(0.5f * fov); + *matrix = Matrix_Identity; + + matrix->row1.x = c / aspect; + matrix->row2.y = c; + matrix->row3.z = zFar_ / (zNear_ - zFar_); + matrix->row3.w = -1.0f; + matrix->row4.z = (zNear_ * zFar_) / (zNear_ - zFar_); + matrix->row4.w = 0.0f; +} + +//#####################z################################################################################################### +//-------------------------------------------------------Input Assembler-------------------------------------------------- +//######################################################################################################################## +// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-input-assembler-stage +static ID3D11InputLayout* input_textured; + +static void IA_CreateLayouts(void) { + ID3D11InputLayout* input = NULL; + // https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-input-assembler-stage-getting-started + // https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-legacy-formats + // https://stackoverflow.com/questions/23398711/d3d11-input-element-desc-element-types-ordering-packing + // D3D11_APPEND_ALIGNED_ELEMENT + static D3D11_INPUT_ELEMENT_DESC T_layout[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR" , 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + HRESULT hr = ID3D11Device_CreateInputLayout(device, T_layout, Array_Elems(T_layout), + vs_textured, sizeof(vs_textured), &input); + input_textured = input; +} + +static void IA_UpdateLayout(void) { + ID3D11DeviceContext_IASetInputLayout(context, input_textured); +} + +static void IA_Init(void) { + IA_CreateLayouts(); + ID3D11DeviceContext_IASetPrimitiveTopology(context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); +} + +static void IA_Free(void) { + ID3D11InputLayout_Release(input_textured); +} + +void Gfx_BindIb(GfxResourceID ib) { + ID3D11Buffer* buffer = (ID3D11Buffer*)ib; + ID3D11DeviceContext_IASetIndexBuffer(context, buffer, DXGI_FORMAT_R16_UINT, 0); +} + +void Gfx_BindVb(GfxResourceID vb) { + ID3D11Buffer* buffer = (ID3D11Buffer*)vb; + static UINT32 offset[] = { 0 }; + ID3D11DeviceContext_IASetVertexBuffers(context, 0, 1, &buffer, &gfx_stride, offset); +} + +void Gfx_BindDynamicVb(GfxResourceID vb) { + ID3D11Buffer* buffer = (ID3D11Buffer*)vb; + static UINT32 offset[] = { 0 }; + ID3D11DeviceContext_IASetVertexBuffers(context, 0, 1, &buffer, &gfx_stride, offset); +} + + +//######################################################################################################################## +//--------------------------------------------------------Vertex shader--------------------------------------------------- +//######################################################################################################################## +// https://docs.microsoft.com/en-us/windows/win32/direct3d11/vertex-shader-stage +static ID3D11VertexShader* vs_shaders[3]; +static ID3D11Buffer* vs_cBuffer; + +static struct CC_ALIGNED(64) VSConstants { + struct Matrix mvp; + float texX, texY; +} vs_constants; +static const struct ShaderDesc vs_descs[] = { + { vs_colored, sizeof(vs_colored) }, + { vs_textured, sizeof(vs_textured) }, + { vs_textured_offset, sizeof(vs_textured_offset) }, +}; + +static void VS_CreateShaders(void) { + for (int i = 0; i < Array_Elems(vs_shaders); i++) + { + HRESULT hr = ID3D11Device_CreateVertexShader(device, vs_descs[i].data, vs_descs[i].len, NULL, &vs_shaders[i]); + if (hr) Logger_Abort2(hr, "Failed to compile vertex shader"); + } +} + +static void VS_CreateConstants(void) { + // https://developer.nvidia.com/content/constant-buffers-without-constant-pain-0 + // https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-buffers-constant-how-to + // https://gamedev.stackexchange.com/questions/18026/directx11-how-do-i-manage-and-update-multiple-shader-constant-buffers + D3D11_BUFFER_DESC desc = { 0 }; + desc.ByteWidth = sizeof(vs_constants); + //desc.Usage = D3D11_USAGE_DYNAMIC; + //desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + //desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + + D3D11_SUBRESOURCE_DATA data; + data.pSysMem = &vs_constants; + data.SysMemPitch = 0; + data.SysMemSlicePitch = 0; + + HRESULT hr = ID3D11Device_CreateBuffer(device, &desc, &data, &vs_cBuffer); + ID3D11DeviceContext_VSSetConstantBuffers(context, 0, 1, &vs_cBuffer); +} + +static int VS_CalcShaderIndex(void) { + if (gfx_format == VERTEX_FORMAT_COLOURED) return 0; + + cc_bool has_offset = vs_constants.texX != 0 || vs_constants.texY != 0; + return has_offset ? 2 : 1; +} + +static void VS_UpdateShader(void) { + int idx = VS_CalcShaderIndex(); + ID3D11DeviceContext_VSSetShader(context, vs_shaders[idx], NULL, 0); +} + +static void VS_FreeShaders(void) { + for (int i = 0; i < Array_Elems(vs_shaders); i++) + { + ID3D11VertexShader_Release(vs_shaders[i]); + } +} + +static void VS_UpdateConstants(void) { + ID3D11DeviceContext_UpdateSubresource(context, vs_cBuffer, 0, NULL, &vs_constants, 0, 0); +} + +static void VS_FreeConstants(void) { + ID3D11Buffer_Release(vs_cBuffer); +} + +static void VS_Init(void) { + VS_CreateShaders(); + VS_CreateConstants(); + VS_UpdateShader(); +} + +static void VS_Free(void) { + VS_FreeShaders(); + VS_FreeConstants(); +} + +static struct Matrix _view, _proj; +void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) { + if (type == MATRIX_VIEW) _view = *matrix; + if (type == MATRIX_PROJECTION) _proj = *matrix; + + Matrix_Mul(&vs_constants.mvp, &_view, &_proj); + VS_UpdateConstants(); +} + +void Gfx_LoadIdentityMatrix(MatrixType type) { + Gfx_LoadMatrix(type, &Matrix_Identity); +} + +void Gfx_EnableTextureOffset(float x, float y) { + vs_constants.texX = x; + vs_constants.texY = y; + VS_UpdateShader(); + VS_UpdateConstants(); +} + +void Gfx_DisableTextureOffset(void) { + vs_constants.texX = 0; + vs_constants.texY = 0; + VS_UpdateShader(); +} + + +//######################################################################################################################## +//---------------------------------------------------------Rasteriser----------------------------------------------------- +//######################################################################################################################## +// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-rasterizer-stage +static ID3D11RasterizerState* rs_states[2]; +static cc_bool rs_culling; + +static void RS_CreateRasterState(void) { + // https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_rasterizer_desc + D3D11_RASTERIZER_DESC desc = { 0 }; + desc.CullMode = D3D11_CULL_NONE; + desc.FillMode = D3D11_FILL_SOLID; + desc.FrontCounterClockwise = true; + desc.DepthClipEnable = true; // otherwise vertices/pixels beyond far plane are still wrongly rendered + ID3D11Device_CreateRasterizerState(device, &desc, &rs_states[0]); + + desc.CullMode = D3D11_CULL_BACK; + ID3D11Device_CreateRasterizerState(device, &desc, &rs_states[1]); +} + +void Gfx_SetViewport(int x, int y, int w, int h) { + D3D11_VIEWPORT viewport; + viewport.TopLeftX = x; + viewport.TopLeftY = y; + viewport.Width = w; + viewport.Height = h; + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + ID3D11DeviceContext_RSSetViewports(context, 1, &viewport); +} + +static void RS_UpdateRasterState(void) { + ID3D11DeviceContext_RSSetState(context, rs_states[rs_culling]); +} + +static void RS_FreeRasterStates(void) { + for (int i = 0; i < Array_Elems(rs_states); i++) + { + ID3D11RasterizerState_Release(rs_states[i]); + } +} + +static void RS_Init(void) { + RS_CreateRasterState(); + Gfx_SetViewport(0, 0, Game.Width, Game.Height); + RS_UpdateRasterState(); +} + +static void RS_Free(void) { + RS_FreeRasterStates(); +} + +void Gfx_SetFaceCulling(cc_bool enabled) { + rs_culling = enabled; + RS_UpdateRasterState(); +} + + +//######################################################################################################################## +//--------------------------------------------------------Pixel shader---------------------------------------------------- +//######################################################################################################################## +// https://docs.microsoft.com/en-us/windows/win32/direct3d11/pixel-shader-stage +static ID3D11SamplerState* ps_samplers[2]; +static ID3D11PixelShader* ps_shaders[12]; +static ID3D11Buffer* ps_cBuffer; +static cc_bool ps_mipmaps; +static float ps_fogEnd, ps_fogDensity; +static PackedCol ps_fogColor; +static int ps_fogMode; + +static struct CC_ALIGNED(64) PSConstants { + float fogValue; + float fogR, fogG, fogB; +} ps_constants; +static const struct ShaderDesc ps_descs[] = { + { ps_colored, sizeof(ps_colored) }, + { ps_textured, sizeof(ps_textured) }, + { ps_colored_test, sizeof(ps_colored_test) }, + { ps_textured_test, sizeof(ps_textured_test) }, + { ps_colored_linear, sizeof(ps_colored_linear) }, + { ps_textured_linear, sizeof(ps_textured_linear) }, + { ps_colored_test_linear, sizeof(ps_colored_test_linear) }, + { ps_textured_test_linear, sizeof(ps_textured_test_linear) }, + { ps_colored_density, sizeof(ps_colored_density) }, + { ps_textured_density, sizeof(ps_textured_density) }, + { ps_colored_test_density, sizeof(ps_colored_test_density) }, + { ps_textured_test_density, sizeof(ps_textured_test_density) }, +}; + +static void PS_CreateShaders(void) { + for (int i = 0; i < Array_Elems(ps_shaders); i++) + { + HRESULT hr = ID3D11Device_CreatePixelShader(device, ps_descs[i].data, ps_descs[i].len, NULL, &ps_shaders[i]); + if (hr) Logger_Abort2(hr, "Failed to compile pixel shader"); + } +} + +static int PS_CalcShaderIndex(void) { + int idx = gfx_format == VERTEX_FORMAT_COLOURED ? 0 : 1; + if (gfx_alphaTest) idx += 2; + + if (gfx_fogEnabled) { + // uncomment when it works + if (ps_fogMode == FOG_LINEAR) idx += 4; + if (ps_fogMode == FOG_EXP) idx += 8; + } + return idx; +} + +static void PS_UpdateShader(void) { + int idx = PS_CalcShaderIndex(); + ID3D11DeviceContext_PSSetShader(context, ps_shaders[idx], NULL, 0); +} + +static void PS_FreeShaders(void) { + for (int i = 0; i < Array_Elems(ps_shaders); i++) + { + ID3D11PixelShader_Release(ps_shaders[i]); + } +} + +static void PS_CreateSamplers(void) { + // https://docs.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11device-createsamplerstate + // https://gamedev.stackexchange.com/questions/18026/directx11-how-do-i-manage-and-update-multiple-shader-constant-buffers + // https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-buffers-constant-how-to + D3D11_SAMPLER_DESC desc = { 0 }; + + desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + desc.MaxAnisotropy = 1; + desc.MaxLOD = D3D11_FLOAT32_MAX; + desc.ComparisonFunc = D3D11_COMPARISON_NEVER; + + desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + HRESULT hr1 = ID3D11Device_CreateSamplerState(device, &desc, &ps_samplers[0]); + + desc.Filter = D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; + HRESULT hr2 = ID3D11Device_CreateSamplerState(device, &desc, &ps_samplers[1]); +} + +static void PS_UpdateSampler(void) { + ID3D11DeviceContext_PSSetSamplers(context, 0, 1, &ps_samplers[ps_mipmaps]); +} + +static void PS_FreeSamplers(void) { + for (int i = 0; i < Array_Elems(ps_samplers); i++) + { + ID3D11SamplerState_Release(ps_samplers[i]); + } +} + +static void PS_CreateConstants(void) { + D3D11_BUFFER_DESC desc = { 0 }; // TODO see notes in VS_CreateConstants + desc.ByteWidth = sizeof(ps_constants); + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + + D3D11_SUBRESOURCE_DATA data; + data.pSysMem = &ps_constants; + data.SysMemPitch = 0; + data.SysMemSlicePitch = 0; + + HRESULT hr = ID3D11Device_CreateBuffer(device, &desc, &data, &ps_cBuffer); + ID3D11DeviceContext_PSSetConstantBuffers(context, 0, 1, &ps_cBuffer); +} + +static void PS_UpdateConstants(void) { + ps_constants.fogR = PackedCol_R(ps_fogColor) / 255.0f; + ps_constants.fogG = PackedCol_G(ps_fogColor) / 255.0f; + ps_constants.fogB = PackedCol_B(ps_fogColor) / 255.0f; + + // avoid doing - in pixel shader for density fog + ps_constants.fogValue = ps_fogMode == FOG_LINEAR ? ps_fogEnd : -ps_fogDensity; + ID3D11DeviceContext_UpdateSubresource(context, ps_cBuffer, 0, NULL, &ps_constants, 0, 0); +} + +static void PS_FreeConstants(void) { + ID3D11Buffer_Release(ps_cBuffer); +} + +static void PS_Init(void) { + PS_CreateShaders(); + PS_CreateSamplers(); + PS_CreateConstants(); + PS_UpdateSampler(); + PS_UpdateShader(); +} + +static void PS_Free(void) { + PS_FreeShaders(); + PS_FreeSamplers(); + PS_FreeConstants(); +} + +static void SetAlphaTest(cc_bool enabled) { + PS_UpdateShader(); +} +// unnecessary? check if any performance is gained, probably irrelevant +void Gfx_SetAlphaArgBlend(cc_bool enabled) { } + +void Gfx_BindTexture(GfxResourceID texId) { + /* defasult texture is otherwise transparent black */ + if (!texId) texId = white_square; + + ID3D11ShaderResourceView* view = (ID3D11ShaderResourceView*)texId; + ID3D11DeviceContext_PSSetShaderResources(context, 0, 1, &view); +} + +void Gfx_SetFog(cc_bool enabled) { + if (gfx_fogEnabled == enabled) return; + gfx_fogEnabled = enabled; + PS_UpdateShader(); +} + +void Gfx_SetFogCol(PackedCol color) { + if (color == ps_fogColor) return; + ps_fogColor = color; + PS_UpdateConstants(); +} + +void Gfx_SetFogDensity(float value) { + if (value == ps_fogDensity) return; + ps_fogDensity = value; + PS_UpdateConstants(); +} + +void Gfx_SetFogEnd(float value) { + if (value == ps_fogEnd) return; + ps_fogEnd = value; + PS_UpdateConstants(); +} + +void Gfx_SetFogMode(FogFunc func) { + if (ps_fogMode == func) return; + ps_fogMode = func; + PS_UpdateShader(); +} + +void Gfx_EnableMipmaps(void) { + if (!Gfx.Mipmaps) return; + ps_mipmaps = true; + PS_UpdateSampler(); +} + +void Gfx_DisableMipmaps(void) { + if (!Gfx.Mipmaps) return; + ps_mipmaps = false; + PS_UpdateSampler(); +} + + +//######################################################################################################################## +//-------------------------------------------------------Output merger---------------------------------------------------- +//######################################################################################################################## +// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage +static ID3D11RenderTargetView* backbuffer; +static ID3D11Texture2D* depthbuffer; +static ID3D11DepthStencilView* depthbufferView; +static ID3D11BlendState* om_blendStates[16 * 2]; +static ID3D11DepthStencilState* om_depthStates[4]; +static float gfx_clearColor[4]; +static cc_bool gfx_channels[4] = { true, true, true, true }; +static cc_bool gfx_depthTest, gfx_depthWrite; + +static void OM_Clear(GfxBuffers buffers) { + if (buffers & GFX_BUFFER_COLOR) { + ID3D11DeviceContext_ClearRenderTargetView(context, backbuffer, gfx_clearColor); + } + if (buffers & GFX_BUFFER_DEPTH) { + ID3D11DeviceContext_ClearDepthStencilView(context, depthbufferView, D3D11_CLEAR_DEPTH, 0.0f, 0); + } +} + +static void OM_UpdateTarget(void) { + ID3D11DeviceContext_OMSetRenderTargets(context, 1, &backbuffer, depthbufferView); +} + +static void OM_InitTargets(void) { + // https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-depth-stencil + D3D11_TEXTURE2D_DESC desc; + ID3D11Texture2D* pBackBuffer; + HRESULT hr; + + hr = IDXGISwapChain_GetBuffer(swapchain, 0, &guid_ID3D11Texture2D, (void**)&pBackBuffer); + if (hr) Logger_Abort2(hr, "Failed to get swapchain backbuffer"); + + hr = ID3D11Device_CreateRenderTargetView(device, pBackBuffer, NULL, &backbuffer); + if (hr) Logger_Abort2(hr, "Failed to create render target"); + + ID3D11Texture2D_GetDesc(pBackBuffer, &desc); + desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; + desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + + hr = ID3D11Device_CreateTexture2D(device, &desc, NULL, &depthbuffer); + if (hr) Logger_Abort2(hr, "Failed to create depthbuffer texture"); + + hr = ID3D11Device_CreateDepthStencilView(device, depthbuffer, NULL, &depthbufferView); + if (hr) Logger_Abort2(hr, "Failed to create depthbuffer view"); + + ID3D11Texture2D_Release(pBackBuffer); + OM_UpdateTarget(); +} + +static void OM_CreateDepthStates(void) { + D3D11_DEPTH_STENCIL_DESC desc = { 0 }; + HRESULT hr; + desc.DepthFunc = D3D11_COMPARISON_GREATER_EQUAL; + + for (int i = 0; i < Array_Elems(om_depthStates); i++) + { + desc.DepthEnable = (i & 1) != 0; + desc.DepthWriteMask = (i & 2) != 0; + + hr = ID3D11Device_CreateDepthStencilState(device, &desc, &om_depthStates[i]); + if (hr) Logger_Abort2(hr, "Failed to create depth state"); + } +} + +static void OM_UpdateDepthState(void) { + ID3D11DepthStencilState* depthState = om_depthStates[gfx_depthTest | (gfx_depthWrite << 1)]; + ID3D11DeviceContext_OMSetDepthStencilState(context, depthState, 0); +} + +static void OM_FreeDepthStates(void) { + for (int i = 0; i < Array_Elems(om_depthStates); i++) + { + ID3D11DepthStencilState_Release(om_depthStates[i]); + } +} + +static void OM_CreateBlendStates(void) { + // https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-blend-state + D3D11_BLEND_DESC desc = { 0 }; + HRESULT hr; + desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; + desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA; + desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + + for (int i = 0; i < Array_Elems(om_blendStates); i++) + { + int mask = 0; + if (i & 0x01) mask |= D3D11_COLOR_WRITE_ENABLE_RED; + if (i & 0x02) mask |= D3D11_COLOR_WRITE_ENABLE_GREEN; + if (i & 0x04) mask |= D3D11_COLOR_WRITE_ENABLE_BLUE; + if (i & 0x08) mask |= D3D11_COLOR_WRITE_ENABLE_ALPHA; + + desc.RenderTarget[0].RenderTargetWriteMask = mask; + desc.RenderTarget[0].BlendEnable = (i & 0x10) != 0; + + hr = ID3D11Device_CreateBlendState(device, &desc, &om_blendStates[i]); + if (hr) Logger_Abort2(hr, "Failed to create blend state"); + } +} + +static void OM_UpdateBlendState(void) { + int idx = (gfx_channels[0]) | (gfx_channels[1] << 1) | (gfx_channels[2] << 2) | (gfx_channels[3] << 3) | (gfx_alphaBlend << 4); + ID3D11BlendState* blendState = om_blendStates[idx]; + ID3D11DeviceContext_OMSetBlendState(context, blendState, NULL, 0xffffffff); +} + +static void OM_FreeBlendStates(void) { + for (int i = 0; i < Array_Elems(om_blendStates); i++) + { + ID3D11BlendState_Release(om_blendStates[i]); + } +} + +static void OM_Init(void) { + OM_InitTargets(); + OM_CreateDepthStates(); + OM_UpdateDepthState(); + OM_CreateBlendStates(); + OM_UpdateBlendState(); +} + +static void OM_FreeTargets(void) { + ID3D11DeviceContext_OMSetRenderTargets(context, 0, NULL, NULL); + ID3D11RenderTargetView_Release(backbuffer); + ID3D11DepthStencilView_Release(depthbufferView); + ID3D11Texture2D_Release(depthbuffer); +} + +static void OM_Free(void) { + OM_FreeTargets(); + OM_FreeDepthStates(); + OM_FreeBlendStates(); +} + +void Gfx_ClearColor(PackedCol color) { + gfx_clearColor[0] = PackedCol_R(color) / 255.0f; + gfx_clearColor[1] = PackedCol_G(color) / 255.0f; + gfx_clearColor[2] = PackedCol_B(color) / 255.0f; + gfx_clearColor[3] = PackedCol_A(color) / 255.0f; +} + +void Gfx_SetDepthTest(cc_bool enabled) { + gfx_depthTest = enabled; + OM_UpdateDepthState(); +} + +void Gfx_SetDepthWrite(cc_bool enabled) { + gfx_depthWrite = enabled; + OM_UpdateDepthState(); +} + +static void SetAlphaBlend(cc_bool enabled) { + OM_UpdateBlendState(); +} + +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { + gfx_channels[0] = r; + gfx_channels[1] = g; + gfx_channels[2] = b; + gfx_channels[3] = a; + OM_UpdateBlendState(); +} + +void Gfx_DepthOnlyRendering(cc_bool depthOnly) { + cc_bool enabled = !depthOnly; + SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], + enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); +} + + +/*########################################################################################################################* +*-----------------------------------------------------------Misc----------------------------------------------------------* +*#########################################################################################################################*/ +static BitmapCol* D3D11_GetRow(struct Bitmap* bmp, int y, void* ctx) { + D3D11_MAPPED_SUBRESOURCE* buffer = (D3D11_MAPPED_SUBRESOURCE*)ctx; + char* row = (char*)buffer->pData + y * buffer->RowPitch; + return (BitmapCol*)row; +} + +cc_result Gfx_TakeScreenshot(struct Stream* output) { + ID3D11Texture2D* tmp = NULL; + struct Bitmap bmp; + HRESULT hr; + + ID3D11Resource* backbuffer_res; + D3D11_RENDER_TARGET_VIEW_DESC backbuffer_desc; + D3D11_MAPPED_SUBRESOURCE buffer; + ID3D11RenderTargetView_GetResource(backbuffer, &backbuffer_res); + ID3D11RenderTargetView_GetDesc(backbuffer, &backbuffer_desc); + + D3D11_TEXTURE2D_DESC desc = { 0 }; + desc.Width = Window_Main.Width; + desc.Height = Window_Main.Height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_STAGING; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + + hr = ID3D11Device_CreateTexture2D(device, &desc, NULL, &tmp); + if (hr) goto finished; + ID3D11DeviceContext_CopyResource(context, tmp, backbuffer_res); + + hr = ID3D11DeviceContext_Map(context, tmp, 0, D3D11_MAP_READ, 0, &buffer); + if (hr) goto finished; + { + Bitmap_Init(bmp, desc.Width, desc.Height, NULL); + hr = Png_Encode(&bmp, output, D3D11_GetRow, false, &buffer); + } + ID3D11DeviceContext_Unmap(context, tmp, 0); + +finished: + if (tmp) { ID3D11Texture2D_Release(tmp); } + ID3D11Resource_Release(backbuffer_res); + return hr; +} + +void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) { + gfx_minFrameMs = minFrameMs; + gfx_vsync = vsync; +} +void Gfx_BeginFrame(void) { OM_UpdateTarget(); } + +void Gfx_ClearBuffers(GfxBuffers buffers) { + OM_Clear(buffers); +} + +void Gfx_EndFrame(void) { + // https://docs.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgiswapchain-present + // gfx_vsync happens to match SyncInterval parameter + HRESULT hr = IDXGISwapChain_Present(swapchain, gfx_vsync, 0); + + // run at reduced FPS when minimised + if (hr == DXGI_STATUS_OCCLUDED) { + TickReducedPerformance(); return; + } + EndReducedPerformance(); + + if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { + Gfx_LoseContext(" (Direct3D11 device lost)"); + } else if (hr) { + Logger_Abort2(hr, "Failed to swap buffers"); + } + if (gfx_minFrameMs) LimitFPS(); +} + +cc_bool Gfx_WarnIfNecessary(void) { return false; } + +void Gfx_GetApiInfo(cc_string* info) { + int pointerSize = sizeof(void*) * 8; + HRESULT hr; + String_Format1(info, "-- Using Direct3D11 (%i bit) --\n", &pointerSize); + + // TODO this overlaps with global declarations, switch to them at some point.. ? + // apparently using D3D11CreateDeviceAndSwapChain is bad, need to investigate + IDXGIDevice* dxgi_device = NULL; + hr = ID3D11Device_QueryInterface(device, &guid_IXDGIDevice, &dxgi_device); + if (hr || !dxgi_device) return; + + IDXGIAdapter* dxgi_adapter; + hr = IDXGIDevice_GetAdapter(dxgi_device, &dxgi_adapter); + if (hr || !dxgi_adapter) goto release_device; + + DXGI_ADAPTER_DESC desc = { 0 }; + hr = IDXGIAdapter_GetDesc(dxgi_adapter, &desc); + if (hr) goto release_adapter; + + // desc.Description is a WCHAR, convert to char + char adapter[128] = { 0 }; + for (int i = 0; i < 128; i++) { adapter[i] = desc.Description[i]; } + + SIZE_T vram = desc.DedicatedVideoMemory; + SIZE_T dram = desc.DedicatedSystemMemory; + SIZE_T sram = desc.SharedSystemMemory; + float tram_ = (vram + dram + sram) / (1024.0 * 1024.0); + float vram_ = vram / (1024.0 * 1024.0); + + String_Format1(info, "Adapter: %c\n", adapter); + String_Format2(info, "Graphics memory: %f2 MB total (%f2 MB VRAM)\n", &tram_, &vram_); + PrintMaxTextureInfo(info); + +release_adapter: + IDXGIAdapter_Release(dxgi_adapter); +release_device: + IDXGIDevice_Release(dxgi_device); +} + +void Gfx_OnWindowResize(void) { + // https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/d3d10-graphics-programming-guide-dxgi#handling-window-resizing + OM_FreeTargets(); + HRESULT hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0); + if (hr) Logger_Abort2(hr, "Failed to resize swapchain"); + + OM_InitTargets(); + Gfx_SetViewport(0, 0, Game.Width, Game.Height); +} + +static void InitPipeline(void) { + // https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-graphics-pipeline + IA_Init(); + VS_Init(); + RS_Init(); + PS_Init(); + OM_Init(); +} + +static void FreePipeline(void) { + IA_Free(); + VS_Free(); + RS_Free(); + PS_Free(); + OM_Free(); + + ID3D11DeviceContext_ClearState(context); + // Direct3D11 uses deferred resource destruction, so Flush to force destruction + // https://stackoverflow.com/questions/44155133/directx11-com-object-with-0-references-not-released + // https://stackoverflow.com/questions/20032816/can-someone-explain-why-i-still-have-live-objects-after-releasing-the-pointers-t + // https://www.gamedev.net/forums/topic/659651-dxgi-leak-warnings/5172345/ + ID3D11DeviceContext_Flush(context); +} +#endif |