Swapchain creation, resizing, and destruction

Caleb Cornett 2022-03-07 21:26:08 -05:00
parent cd8f48c444
commit a93d49a05e
2 changed files with 305 additions and 14 deletions

View File

@ -36,9 +36,17 @@
#include "Refresh_Driver_D3D11_cdefines.h"
#include <SDL.h>
#include <SDL_syswm.h>
/* Defines */
#define D3D11_DLL "d3d11.dll"
#define DXGI_DLL "dxgi.dll"
#define WINDOW_SWAPCHAIN_DATA "Refresh_D3D11Swapchain"
#define NOT_IMPLEMENTED SDL_assert(0 && "Not implemented!");
/* Macros */
#define ERROR_CHECK(msg) \
if (FAILED(res)) \
@ -78,8 +86,6 @@
); \
}
#define NOT_IMPLEMENTED SDL_assert(0 && "Not implemented!");
/* Conversions */
static DXGI_FORMAT RefreshToD3D11_SurfaceFormat[] =
@ -235,13 +241,26 @@ static D3D11_TEXTURE_ADDRESS_MODE RefreshToD3D11_SamplerAddressMode[] =
/* Structs */
typedef struct D3D11Texture
{
ID3D11RenderTargetView *rtv;
} D3D11Texture;
typedef struct D3D11SwapchainData
{
IDXGISwapChain* swapchain;
D3D11Texture refreshTexture;
void* windowHandle;
} D3D11SwapchainData;
typedef struct D3D11CommandBuffer
{
ID3D11DeviceContext *context;
ID3D11CommandList *commandList;
D3D11SwapchainData *swapchainData;
SDL_threadID threadID;
uint8_t recording;
uint8_t fixed;
ID3D11CommandList *commandList;
} D3D11CommandBuffer;
typedef struct D3D11CommandBufferPool
@ -253,7 +272,6 @@ typedef struct D3D11CommandBufferPool
typedef struct D3D11Renderer
{
/* Persistent D3D11 Objects*/
ID3D11Device *device;
ID3D11DeviceContext *immediateContext;
IDXGIFactory1 *factory;
@ -261,18 +279,22 @@ typedef struct D3D11Renderer
void *d3d11_dll;
void *dxgi_dll;
/* Deferred Contexts */
D3D11CommandBufferPool *commandBufferPool;
/* Blend State */
D3D11SwapchainData** swapchainDatas;
uint32_t swapchainDataCount;
uint32_t swapchainDataCapacity;
Refresh_Vec4 blendFactor;
/* Capabilities */
uint8_t debugMode;
D3D_FEATURE_LEVEL featureLevel;
} D3D11Renderer;
/* Predeclarations */
static void D3D11_Wait(Refresh_Renderer* driverData);
/* Logging */
static void D3D11_INTERNAL_LogError(
@ -331,6 +353,187 @@ static void D3D11_INTERNAL_LogError(
Refresh_LogError("%s! Error Code: %s (0x%08X)", msg, wszMsgBuff, res);
}
/* Swapchain Management */
static uint8_t D3D11_INTERNAL_CreateSwapchain(
D3D11Renderer *renderer,
void *windowHandle
) {
IDXGIFactory1 *pParent;
DXGI_MODE_DESC swapchainBufferDesc;
DXGI_SWAP_CHAIN_DESC swapchainDesc;
IDXGISwapChain *swapchain;
D3D11SwapchainData *swapchainData;
SDL_SysWMinfo info;
HWND dxgiHandle;
ID3D11Texture2D *swapchainTexture;
D3D11_RENDER_TARGET_VIEW_DESC swapchainViewDesc;
HRESULT res;
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo((SDL_Window*) windowHandle, &info);
dxgiHandle = info.info.win.window;
/* Initialize swapchain buffer descriptor */
swapchainBufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapchainBufferDesc.Width = 0;
swapchainBufferDesc.Height = 0;
swapchainBufferDesc.RefreshRate.Numerator = 0;
swapchainBufferDesc.RefreshRate.Denominator = 0;
swapchainBufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
swapchainBufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
/* Initialize the swapchain descriptor */
swapchainDesc.BufferDesc = swapchainBufferDesc;
swapchainDesc.BufferCount = 3;
swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapchainDesc.OutputWindow = dxgiHandle;
swapchainDesc.Flags = 0;
swapchainDesc.SampleDesc.Count = 1;
swapchainDesc.SampleDesc.Quality = 0;
swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
swapchainDesc.Windowed = 1;
/* Create the swapchain! */
res = IDXGIFactory1_CreateSwapChain(
renderer->factory,
(IUnknown*) renderer->device,
&swapchainDesc,
&swapchain
);
ERROR_CHECK_RETURN("Could not create swap chain", 0);
/*
* The swapchain's parent is a separate factory from the factory that
* we used to create the swapchain, and only that parent can be used to
* set the window association. Trying to set an association on our factory
* will silently fail and doesn't even verify arguments or return errors.
* See https://gamedev.net/forums/topic/634235-dxgidisabling-altenter/4999955/
*/
res = IDXGISwapChain_GetParent(
swapchain,
&D3D_IID_IDXGIFactory1,
(void**) &pParent
);
if (FAILED(res))
{
Refresh_LogWarn(
"Could not get swapchain parent! Error Code: %08X",
res
);
}
else
{
/* Disable DXGI window crap */
res = IDXGIFactory1_MakeWindowAssociation(
pParent,
dxgiHandle,
DXGI_MWA_NO_WINDOW_CHANGES
);
if (FAILED(res))
{
Refresh_LogWarn(
"MakeWindowAssociation failed! Error Code: %08X",
res
);
}
}
/* Set up the swapchain data */
swapchainData = (D3D11SwapchainData*) SDL_malloc(sizeof(D3D11SwapchainData));
swapchainData->swapchain = swapchain;
swapchainData->windowHandle = windowHandle;
swapchainData->refreshTexture.rtv = NULL;
SDL_SetWindowData((SDL_Window*) windowHandle, WINDOW_SWAPCHAIN_DATA, swapchainData);
if (renderer->swapchainDataCount >= renderer->swapchainDataCapacity)
{
renderer->swapchainDataCapacity *= 2;
renderer->swapchainDatas = SDL_realloc(
renderer->swapchainDatas,
renderer->swapchainDataCapacity * sizeof(D3D11SwapchainData*)
);
}
renderer->swapchainDatas[renderer->swapchainDataCount] = swapchainData;
renderer->swapchainDataCount += 1;
/* Create the RTV for the swapchain */
swapchainViewDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapchainViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
swapchainViewDesc.Texture2D.MipSlice = 0;
res = IDXGISwapChain_GetBuffer(
swapchainData->swapchain,
0,
&D3D_IID_ID3D11Texture2D,
(void**) &swapchainTexture
);
ERROR_CHECK_RETURN("Could not get buffer from swapchain", 0);
res = ID3D11Device_CreateRenderTargetView(
renderer->device,
(ID3D11Resource*) swapchainTexture,
&swapchainViewDesc,
&swapchainData->refreshTexture.rtv
);
ERROR_CHECK_RETURN("Swapchain RT view creation failed", 0);
/* Cleanup */
ID3D11Texture2D_Release(swapchainTexture);
return 1;
}
static uint8_t D3D11_INTERNAL_ResizeSwapchain(
D3D11Renderer *renderer,
D3D11SwapchainData *swapchainData
) {
ID3D11Texture2D *swapchainTexture;
D3D11_RENDER_TARGET_VIEW_DESC swapchainViewDesc;
int w, h;
HRESULT res;
/* Release the old RTV */
ID3D11RenderTargetView_Release(swapchainData->refreshTexture.rtv);
/* Resize the swapchain */
SDL_GetWindowSize((SDL_Window*) swapchainData->windowHandle, &w, &h);
res = IDXGISwapChain_ResizeBuffers(
swapchainData->swapchain,
0, /* Keep buffer count the same */
w,
h,
DXGI_FORMAT_UNKNOWN, /* Keep the old format */
0
);
ERROR_CHECK_RETURN("Could not resize swapchain buffers", 0);
/* Recreate the RTV using the new swapchain buffer */
swapchainViewDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapchainViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
swapchainViewDesc.Texture2D.MipSlice = 0;
res = IDXGISwapChain_GetBuffer(
swapchainData->swapchain,
0,
&D3D_IID_ID3D11Texture2D,
&swapchainTexture
);
ERROR_CHECK_RETURN("Could not get buffer from swapchain", 0);
res = ID3D11Device_CreateRenderTargetView(
renderer->device,
(ID3D11Resource*) swapchainTexture,
&swapchainViewDesc,
&swapchainData->refreshTexture.rtv
);
ERROR_CHECK_RETURN("Could not create render target view for swapchain", 0);
/* Cleanup */
ID3D11Texture2D_Release(swapchainTexture);
return 1;
}
/* Quit */
static void D3D11_DestroyDevice(
@ -354,6 +557,15 @@ static void D3D11_DestroyDevice(
SDL_free(renderer->commandBufferPool->elements);
SDL_free(renderer->commandBufferPool);
/* Release swapchain */
for (i = 0; i < renderer->swapchainDataCount; i += 1)
{
ID3D11RenderTargetView_Release(renderer->swapchainDatas[i]->refreshTexture.rtv);
IDXGISwapChain_Release(renderer->swapchainDatas[i]->swapchain);
SDL_free(renderer->swapchainDatas[i]);
}
SDL_free(renderer->swapchainDatas);
/* Release persistent D3D11 objects */
ID3D11DeviceContext_Release(renderer->immediateContext);
ID3D11Device_Release(renderer->device);
@ -776,24 +988,79 @@ static Refresh_CommandBuffer* D3D11_AcquireCommandBuffer(
commandBuffer->threadID = SDL_ThreadID();
commandBuffer->recording = 1;
commandBuffer->fixed = fixed;
commandBuffer->swapchainData = NULL;
commandBuffer->commandList = NULL;
return (Refresh_CommandBuffer*) commandBuffer;
}
static D3D11SwapchainData* D3D11_INTERNAL_FetchSwapchainData(
D3D11Renderer* renderer,
void* windowHandle
) {
D3D11SwapchainData* swapchainData = NULL;
swapchainData = (D3D11SwapchainData*) SDL_GetWindowData(windowHandle, WINDOW_SWAPCHAIN_DATA);
if (swapchainData == NULL)
{
if (D3D11_INTERNAL_CreateSwapchain(renderer, windowHandle))
{
swapchainData = (D3D11SwapchainData*) SDL_GetWindowData(windowHandle, WINDOW_SWAPCHAIN_DATA);
}
}
return swapchainData;
}
Refresh_Texture* D3D11_AcquireSwapchainTexture(
Refresh_Renderer *driverData,
Refresh_CommandBuffer *commandBuffer,
void *windowHandle
) {
NOT_IMPLEMENTED
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11CommandBuffer *cmdbuf = (D3D11CommandBuffer*) commandBuffer;
D3D11SwapchainData *swapchainData;
DXGI_SWAP_CHAIN_DESC swapchainDesc;
int w, h;
HRESULT res;
/* Fetch the swapchain data, creating a new swapchain if needed. */
swapchainData = D3D11_INTERNAL_FetchSwapchainData(renderer, windowHandle);
if (swapchainData == NULL)
{
return NULL;
}
/* Check for window size changes and resize the swapchain if needed. */
IDXGISwapChain_GetDesc(swapchainData->swapchain, &swapchainDesc);
SDL_GetWindowSize((SDL_Window*) windowHandle, &w, &h);
if (w != swapchainDesc.BufferDesc.Width || h != swapchainDesc.BufferDesc.Height)
{
res = D3D11_INTERNAL_ResizeSwapchain(renderer, swapchainData);
ERROR_CHECK_RETURN("Could not resize swapchain", NULL);
}
/* Let's try this again... */
swapchainData = D3D11_INTERNAL_FetchSwapchainData(renderer, windowHandle);
if (swapchainData == NULL)
{
return NULL;
}
/* Let the command buffer know it's associated with this swapchain. */
cmdbuf->swapchainData = swapchainData;
/* Return the swapchain texture */
return (Refresh_Texture*) &swapchainData->refreshTexture;
}
Refresh_TextureFormat D3D11_GetSwapchainFormat(
Refresh_Renderer* driverData,
void* windowHandle
) {
NOT_IMPLEMENTED
return DXGI_FORMAT_R8G8B8A8_UNORM;
}
static void D3D11_Submit(
@ -814,6 +1081,7 @@ static void D3D11_Submit(
if (commandBuffer->fixed && !commandBuffer->recording)
{
/* Grab the prerecorded command list. */
commandList = commandBuffer->commandList;
}
else
@ -847,6 +1115,16 @@ static void D3D11_Submit(
/* Mark the command buffer as not-recording so that it can be used to record again. */
commandBuffer->recording = 0;
/* Present, if applicable */
if (commandBuffer->swapchainData)
{
IDXGISwapChain_Present(
commandBuffer->swapchainData->swapchain,
1, /* FIXME: Assumes vsync! */
0
);
}
}
}
@ -1002,6 +1280,18 @@ tryCreateDevice:
result->driverData = (Refresh_Renderer*) renderer;
/* Create the initial swapchain */
renderer->swapchainDataCapacity = 1;
renderer->swapchainDataCount = 0;
renderer->swapchainDatas = SDL_malloc(
renderer->swapchainDataCapacity * sizeof(D3D11SwapchainData*)
);
if (!D3D11_INTERNAL_CreateSwapchain(renderer, presentationParameters->deviceWindowHandle))
{
return NULL;
}
return result;
}

View File

@ -32,6 +32,7 @@ typedef HRESULT(WINAPI* PFN_CREATE_DXGI_FACTORY)(const GUID* riid, void** ppFact
static const IID D3D_IID_IDXGIFactory1 = { 0x770aae78,0xf26f,0x4dba,{0xa8,0x29,0x25,0x3c,0x83,0xd1,0xb3,0x87} };
static const IID D3D_IID_IDXGIFactory6 = { 0xc1b6694f,0xff09,0x44a9,{0xb0,0x3c,0x77,0x90,0x0a,0x0a,0x1d,0x17} };
static const IID D3D_IID_IDXGIAdapter1 = { 0x29038f61,0x3839,0x4626,{0x91,0xfd,0x08,0x68,0x79,0x01,0x1a,0x05} };
static const IID D3D_IID_ID3D11Texture2D = { 0x6f15aaf2,0xd208,0x4e89,{0x9a,0xb4,0x48,0x95,0x35,0xd3,0x4f,0x9c} };
/* IDXGIFactory6 (taken from dxgi1_6.h, cleaned up a bit) */
typedef enum