diff --git a/src/Refresh_Driver_D3D11.c b/src/Refresh_Driver_D3D11.c index 2723bf7..1d895ae 100644 --- a/src/Refresh_Driver_D3D11.c +++ b/src/Refresh_Driver_D3D11.c @@ -36,9 +36,17 @@ #include "Refresh_Driver_D3D11_cdefines.h" #include +#include + +/* 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,9 +86,7 @@ ); \ } -#define NOT_IMPLEMENTED SDL_assert(0 && "Not implemented!"); - - /* Conversions */ +/* 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; } -Refresh_Texture* D3D11_AcquireSwapchainTexture( - Refresh_Renderer* driverData, - Refresh_CommandBuffer* commandBuffer, +static D3D11SwapchainData* D3D11_INTERNAL_FetchSwapchainData( + D3D11Renderer* renderer, void* windowHandle ) { - NOT_IMPLEMENTED + 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 +) { + 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; } diff --git a/src/Refresh_Driver_D3D11_cdefines.h b/src/Refresh_Driver_D3D11_cdefines.h index 16f8b5d..e269212 100644 --- a/src/Refresh_Driver_D3D11_cdefines.h +++ b/src/Refresh_Driver_D3D11_cdefines.h @@ -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