diff --git a/CMakeLists.txt b/CMakeLists.txt index 2beff84..a22ec80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,11 +36,23 @@ elseif(WIN32) set(CMAKE_SHARED_LIBRARY_PREFIX "") endif() +set(BUILD_D3D11 OFF) + +if (WIN32) + set(BUILD_D3D11 ON) +endif() + # Defines add_definitions( -DREFRESH_DRIVER_VULKAN ) +if (BUILD_D3D11) + add_definitions( + -DREFRESH_DRIVER_D3D11 + ) +endif() + # Source lists add_library(Refresh # Public Headers @@ -49,8 +61,10 @@ add_library(Refresh # Internal Headers src/Refresh_Driver.h src/Refresh_Driver_Vulkan_vkfuncs.h + src/Refresh_Driver_D3D11_cdefines.h # Source Files src/Refresh.c + src/Refresh_Driver_D3D11.c src/Refresh_Driver_Vulkan.c src/Refresh_Image.c ) diff --git a/src/Refresh.c b/src/Refresh.c index e12c387..2ce2a96 100644 --- a/src/Refresh.c +++ b/src/Refresh.c @@ -34,7 +34,12 @@ /* Drivers */ static const Refresh_Driver *drivers[] = { +#if REFRESH_DRIVER_VULKAN &VulkanDriver, +#endif +#if REFRESH_DRIVER_D3D11 + &D3D11Driver, +#endif NULL }; @@ -124,21 +129,42 @@ uint32_t Refresh_LinkedVersion(void) /* Driver Functions */ -static int32_t selectedDriver = 0; +static int32_t selectedDriver = -1; Refresh_Device* Refresh_CreateDevice( Refresh_PresentationParameters *presentationParameters, uint8_t debugMode ) { - if (selectedDriver < 0) + uint32_t result = 0; + uint32_t i; + const char *hint = SDL_GetHint("REFRESH_FORCE_DRIVER"); + for (i = 0; drivers[i] != NULL; i += 1) { - return NULL; + if (hint != NULL) + { + if (SDL_strcmp(hint, drivers[i]->Name) != 0) + { + continue; + } + } + + /* FIXME: add fallback driver handling */ + break; } - return drivers[selectedDriver]->CreateDevice( - presentationParameters, - debugMode - ); + if (drivers[i] == NULL) + { + Refresh_LogError("No supported Refresh driver found!"); + return NULL; + } + else + { + selectedDriver = i; + return drivers[selectedDriver]->CreateDevice( + presentationParameters, + debugMode + ); + } } void Refresh_DestroyDevice(Refresh_Device *device) diff --git a/src/Refresh_Driver.h b/src/Refresh_Driver.h index a46270d..c99241e 100644 --- a/src/Refresh_Driver.h +++ b/src/Refresh_Driver.h @@ -522,6 +522,7 @@ typedef struct Refresh_Driver } Refresh_Driver; extern Refresh_Driver VulkanDriver; +extern Refresh_Driver D3D11Driver; #endif /* REFRESH_DRIVER_H */ diff --git a/src/Refresh_Driver_D3D11.c b/src/Refresh_Driver_D3D11.c new file mode 100644 index 0000000..c3c8ec8 --- /dev/null +++ b/src/Refresh_Driver_D3D11.c @@ -0,0 +1,2067 @@ +/* Refresh - XNA-inspired 3D Graphics Library with modern capabilities + * + * Copyright (c) 2020 Evan Hemsley + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + * Evan "cosmonaut" Hemsley + * + */ + +#if REFRESH_DRIVER_D3D11 + +#define D3D11_NO_HELPERS +#define CINTERFACE +#define COBJMACROS +#include +#include +#include + +#include "Refresh_Driver.h" +#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)) \ + { \ + D3D11_INTERNAL_LogError(renderer->device, msg, res); \ + } + +#define ERROR_CHECK_RETURN(msg, ret) \ + if (FAILED(res)) \ + { \ + D3D11_INTERNAL_LogError(renderer->device, msg, res); \ + return ret; \ + } + +#define EXPAND_ELEMENTS_IF_NEEDED(arr, initialValue, type) \ + if (arr->count == arr->capacity) \ + { \ + if (arr->capacity == 0) \ + { \ + arr->capacity = initialValue; \ + } \ + else \ + { \ + arr->capacity *= 2; \ + } \ + arr->elements = (type*) SDL_realloc( \ + arr->elements, \ + arr->capacity * sizeof(type) \ + ); \ + } + +/* D3DCompile signature */ + +typedef HRESULT(WINAPI *PFN_D3DCOMPILE)( + LPCVOID pSrcData, + SIZE_T SrcDataSize, + LPCSTR pSourceName, + const D3D_SHADER_MACRO* pDefines, + ID3DInclude* pInclude, + LPCSTR pEntrypoint, + LPCSTR pTarget, + UINT Flags1, + UINT Flags2, + ID3DBlob **ppCode, + ID3DBlob **ppErrorMsgs +); + +/* Conversions */ + +static DXGI_FORMAT RefreshToD3D11_SurfaceFormat[] = +{ + DXGI_FORMAT_R8G8B8A8_UNORM, /* R8G8B8A8 */ + DXGI_FORMAT_B8G8R8A8_UNORM, /* B8G8R8A8 */ + DXGI_FORMAT_B5G6R5_UNORM, /* R5G6B5 */ + DXGI_FORMAT_B5G5R5A1_UNORM, /* A1R5G5B5 */ + DXGI_FORMAT_B4G4R4A4_UNORM, /* B4G4R4A4 */ + DXGI_FORMAT_BC1_UNORM, /* BC1 */ + DXGI_FORMAT_BC3_UNORM, /* BC3 */ + DXGI_FORMAT_BC5_UNORM, /* BC5 */ + DXGI_FORMAT_R8G8_SNORM, /* R8G8_SNORM */ + DXGI_FORMAT_R8G8B8A8_SNORM, /* R8G8B8A8_SNORM */ + DXGI_FORMAT_R10G10B10A2_UNORM, /* A2R10G10B10 */ + DXGI_FORMAT_R16G16_UNORM, /* R16G16 */ + DXGI_FORMAT_R16G16B16A16_UNORM, /* R16G16B16A16 */ + DXGI_FORMAT_R8_UNORM, /* R8 */ + DXGI_FORMAT_R32_FLOAT, /* R32_SFLOAT */ + DXGI_FORMAT_R32G32_FLOAT, /* R32G32_SFLOAT */ + DXGI_FORMAT_R32G32B32A32_FLOAT, /* R32G32B32A32_SFLOAT */ + DXGI_FORMAT_R16_FLOAT, /* R16_SFLOAT */ + DXGI_FORMAT_R16G16_FLOAT, /* R16G16_SFLOAT */ + DXGI_FORMAT_R16G16B16A16_FLOAT, /* R16G16B16A16_SFLOAT */ + DXGI_FORMAT_D16_UNORM, /* D16 */ + DXGI_FORMAT_D32_FLOAT, /* D32 */ + DXGI_FORMAT_D24_UNORM_S8_UINT, /* D16S8 */ + DXGI_FORMAT_D32_FLOAT_S8X24_UINT /* D32S8 */ +}; + +static DXGI_FORMAT RefreshToD3D11_VertexFormat[] = +{ + DXGI_FORMAT_R32_UINT, /* UINT */ + DXGI_FORMAT_R32_FLOAT, /* FLOAT */ + DXGI_FORMAT_R32G32_FLOAT, /* VECTOR2 */ + DXGI_FORMAT_R32G32B32_FLOAT, /* VECTOR3 */ + DXGI_FORMAT_R32G32B32A32_FLOAT, /* VECTOR4 */ + DXGI_FORMAT_R8G8B8A8_UNORM, /* COLOR */ + DXGI_FORMAT_R8G8B8A8_UINT, /* BYTE4 */ + DXGI_FORMAT_R16G16_SINT, /* SHORT2 */ + DXGI_FORMAT_R16G16B16A16_SINT, /* SHORT4 */ + DXGI_FORMAT_R16G16_SNORM, /* NORMALIZEDSHORT2 */ + DXGI_FORMAT_R16G16B16A16_SNORM, /* NORMALIZEDSHORT4 */ + DXGI_FORMAT_R16G16_FLOAT, /* HALFVECTOR2 */ + DXGI_FORMAT_R16G16B16A16_FLOAT /* HALFVECTOR4 */ +}; + +static DXGI_FORMAT RefreshToD3D11_IndexType[] = +{ + DXGI_FORMAT_R16_UINT, /* 16BIT */ + DXGI_FORMAT_R32_UINT /* 32BIT */ +}; + +static D3D11_PRIMITIVE_TOPOLOGY RefreshToD3D11_PrimitiveType[] = +{ + D3D_PRIMITIVE_TOPOLOGY_POINTLIST, /* POINTLIST */ + D3D_PRIMITIVE_TOPOLOGY_LINELIST, /* LINELIST */ + D3D_PRIMITIVE_TOPOLOGY_LINESTRIP, /* LINESTRIP */ + D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, /* TRIANGLELIST */ + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP /* TRIANGLESTRIP */ +}; + +static D3D11_FILL_MODE RefreshToD3D11_FillMode[] = +{ + D3D11_FILL_SOLID, /* FILL */ + D3D11_FILL_WIREFRAME, /* LINE */ +}; + +static D3D11_CULL_MODE RefreshToD3D11_CullMode[] = +{ + D3D11_CULL_NONE, /* NONE */ + D3D11_CULL_FRONT, /* FRONT */ + D3D11_CULL_BACK /* BACK */ +}; + +static D3D11_BLEND RefreshToD3D11_BlendFactor[] = +{ + D3D11_BLEND_ZERO, /* ZERO */ + D3D11_BLEND_ONE, /* ONE */ + D3D11_BLEND_SRC_COLOR, /* SRC_COLOR */ + D3D11_BLEND_INV_SRC_COLOR, /* ONE_MINUS_SRC_COLOR */ + D3D11_BLEND_DEST_COLOR, /* DST_COLOR */ + D3D11_BLEND_INV_DEST_COLOR, /* ONE_MINUS_DST_COLOR */ + D3D11_BLEND_SRC_ALPHA, /* SRC_ALPHA */ + D3D11_BLEND_INV_SRC_ALPHA, /* ONE_MINUS_SRC_ALPHA */ + D3D11_BLEND_DEST_ALPHA, /* DST_ALPHA */ + D3D11_BLEND_INV_DEST_ALPHA, /* ONE_MINUS_DST_ALPHA */ + D3D11_BLEND_BLEND_FACTOR, /* CONSTANT_COLOR */ + D3D11_BLEND_INV_BLEND_FACTOR, /* ONE_MINUS_CONSTANT_COLOR */ + D3D11_BLEND_SRC_ALPHA_SAT, /* SRC_ALPHA_SATURATE */ + D3D11_BLEND_SRC1_COLOR, /* SRC1_COLOR */ + D3D11_BLEND_INV_SRC1_COLOR, /* ONE_MINUS_SRC1_COLOR */ + D3D11_BLEND_SRC1_ALPHA, /* SRC1_ALPHA */ + D3D11_BLEND_INV_SRC1_ALPHA /* ONE_MINUS_SRC1_ALPHA */ +}; + +static D3D11_BLEND_OP RefreshToD3D11_BlendOp[] = +{ + D3D11_BLEND_OP_ADD, /* ADD */ + D3D11_BLEND_OP_SUBTRACT, /* SUBTRACT */ + D3D11_BLEND_OP_REV_SUBTRACT, /* REVERSE_SUBTRACT */ + D3D11_BLEND_OP_MIN, /* MIN */ + D3D11_BLEND_OP_MAX /* MAX */ +}; + +static D3D11_COMPARISON_FUNC RefreshToD3D11_CompareOp[] = +{ + D3D11_COMPARISON_NEVER, /* NEVER */ + D3D11_COMPARISON_LESS, /* LESS */ + D3D11_COMPARISON_EQUAL, /* EQUAL */ + D3D11_COMPARISON_LESS_EQUAL, /* LESS_OR_EQUAL */ + D3D11_COMPARISON_GREATER, /* GREATER */ + D3D11_COMPARISON_NOT_EQUAL, /* NOT_EQUAL */ + D3D11_COMPARISON_GREATER_EQUAL, /* GREATER_OR_EQUAL */ + D3D11_COMPARISON_ALWAYS /* ALWAYS */ +}; + +static D3D11_STENCIL_OP RefreshToD3D11_StencilOp[] = +{ + D3D11_STENCIL_OP_KEEP, /* KEEP */ + D3D11_STENCIL_OP_ZERO, /* ZERO */ + D3D11_STENCIL_OP_REPLACE, /* REPLACE */ + D3D11_STENCIL_OP_INCR_SAT, /* INCREMENT_AND_CLAMP */ + D3D11_STENCIL_OP_DECR_SAT, /* DECREMENT_AND_CLAMP */ + D3D11_STENCIL_OP_INVERT, /* INVERT */ + D3D11_STENCIL_OP_INCR, /* INCREMENT_AND_WRAP */ + D3D11_STENCIL_OP_DECR /* DECREMENT_AND_WRAP */ +}; + +static int32_t RefreshToD3D11_SampleCount[] = +{ + 1, /* 1 */ + 2, /* 2 */ + 4, /* 4 */ + 8, /* 8 */ + 16, /* 16 */ + 32, /* 32 */ + 64 /* 64 */ +}; + +static D3D11_INPUT_CLASSIFICATION RefreshToD3D11_VertexInputRate[] = +{ + D3D11_INPUT_PER_VERTEX_DATA, /* VERTEX */ + D3D11_INPUT_PER_INSTANCE_DATA /* INSTANCE */ +}; + +static D3D11_TEXTURE_ADDRESS_MODE RefreshToD3D11_SamplerAddressMode[] = +{ + D3D11_TEXTURE_ADDRESS_WRAP, /* REPEAT */ + D3D11_TEXTURE_ADDRESS_MIRROR, /* MIRRORED_REPEAT */ + D3D11_TEXTURE_ADDRESS_CLAMP, /* CLAMP_TO_EDGE */ + D3D11_TEXTURE_ADDRESS_BORDER /* CLAMP_TO_BORDER */ +}; + +/* Structs */ + +typedef struct D3D11Texture +{ + /* D3D Handles */ + ID3D11Resource *handle; /* ID3D11Texture2D* or ID3D11Texture3D* */ + ID3D11ShaderResourceView *shaderView; + + /* Basic Info */ + int32_t levelCount; + uint8_t isRenderTarget; + + /* Dimensions */ + #define REFRESH_D3D11_RENDERTARGET_2D 0 + #define REFRESH_D3D11_RENDERTARGET_3D 1 + #define REFRESH_D3D11_RENDERTARGET_CUBE 2 + uint8_t rtType; + REFRESHNAMELESS union + { + struct + { + int32_t width; + int32_t height; + ID3D11View *targetView; /* ID3D11RenderTargetView* or ID3D11DepthStencilView* */ + } twod; + struct + { + int32_t width; + int32_t height; + int32_t depth; + } threed; + struct + { + int32_t size; + ID3D11RenderTargetView **rtViews; + } cube; + }; +} D3D11Texture; + +typedef struct D3D11Buffer +{ + ID3D11Buffer *handle; +} D3D11Buffer; + +typedef struct D3D11SwapchainData +{ + IDXGISwapChain* swapchain; + D3D11Texture refreshTexture; + void* windowHandle; +} D3D11SwapchainData; + +typedef struct D3D11CommandBuffer +{ + /* D3D11 Object References */ + ID3D11DeviceContext *context; + ID3D11CommandList *commandList; + D3D11SwapchainData *swapchainData; + + /* Render Pass */ + uint8_t numBoundColorAttachments; + ID3D11RenderTargetView *rtViews[MAX_COLOR_TARGET_BINDINGS]; + ID3D11DepthStencilView* dsView; + + /* State */ + SDL_threadID threadID; + uint8_t recording; + uint8_t fixed; +} D3D11CommandBuffer; + +typedef struct D3D11CommandBufferPool +{ + D3D11CommandBuffer **elements; + uint32_t count; + uint32_t capacity; +} D3D11CommandBufferPool; + +typedef struct D3D11ShaderModule +{ + ID3D11DeviceChild *shader; /* ID3D11VertexShader, ID3D11PixelShader, ID3D11ComputeShader */ + ID3D10Blob *blob; + char *shaderSource; + size_t shaderSourceLength; +} D3D11ShaderModule; + +typedef struct D3D11GraphicsPipeline +{ + float blendConstants[4]; + + int32_t numColorAttachments; + int32_t colorAttachmentSampleCounts[MAX_COLOR_TARGET_BINDINGS]; + DXGI_FORMAT colorAttachmentFormats[MAX_COLOR_TARGET_BINDINGS]; + ID3D11BlendState *colorAttachmentBlendState; + + uint8_t hasDepthStencilAttachment; + DXGI_FORMAT depthStencilAttachmentFormat; + + D3D11_PRIMITIVE_TOPOLOGY primitiveTopology; + uint32_t stencilRef; + ID3D11DepthStencilState *depthStencilState; + ID3D11RasterizerState *rasterizerState; + ID3D11InputLayout *inputLayout; + + Refresh_MultisampleState multisampleState; + ID3D11VertexShader *vertexShader; + ID3D11PixelShader *fragmentShader; +} D3D11GraphicsPipeline; + +typedef struct D3D11Renderer +{ + ID3D11Device *device; + ID3D11DeviceContext *immediateContext; + IDXGIFactory1 *factory; + IDXGIAdapter1* adapter; + void *d3d11_dll; + void *dxgi_dll; + void *d3dcompiler_dll; + SDL_mutex *contextLock; + + D3D11CommandBufferPool *commandBufferPool; + SDL_mutex *commandBufferAcquisitionMutex; + + D3D11SwapchainData** swapchainDatas; + uint32_t swapchainDataCount; + uint32_t swapchainDataCapacity; + + Refresh_Vec4 blendFactor; + + uint8_t debugMode; + D3D_FEATURE_LEVEL featureLevel; + PFN_D3DCOMPILE D3DCompileFunc; +} D3D11Renderer; + +/* Predeclarations */ + +static void D3D11_Wait(Refresh_Renderer* driverData); + +/* Logging */ + +static void D3D11_INTERNAL_LogError( + ID3D11Device *device, + const char *msg, + HRESULT res +) { + #define MAX_ERROR_LEN 1024 /* FIXME: Arbitrary! */ + + /* Buffer for text, ensure space for \0 terminator after buffer */ + char wszMsgBuff[MAX_ERROR_LEN + 1]; + DWORD dwChars; /* Number of chars returned. */ + + if (res == DXGI_ERROR_DEVICE_REMOVED) + { + res = ID3D11Device_GetDeviceRemovedReason(device); + } + + /* Try to get the message from the system errors. */ + dwChars = FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + res, + 0, + wszMsgBuff, + MAX_ERROR_LEN, + NULL + ); + + /* No message? Screw it, just post the code. */ + if (dwChars == 0) + { + Refresh_LogError("%s! Error Code: 0x%08X", msg, res); + return; + } + + /* Ensure valid range */ + dwChars = SDL_min(dwChars, MAX_ERROR_LEN); + + /* Trim whitespace from tail of message */ + while (dwChars > 0) + { + if (wszMsgBuff[dwChars - 1] <= ' ') + { + dwChars--; + } + else + { + break; + } + } + + /* Ensure null-terminated string */ + wszMsgBuff[dwChars] = '\0'; + + Refresh_LogError("%s! Error Code: %s (0x%08X)", msg, wszMsgBuff, res); +} + +/* Swapchain Management */ + +static uint8_t D3D11_INTERNAL_InitializeSwapchainTexture( + D3D11Renderer *renderer, + D3D11Texture *resultTexture, + IDXGISwapChain *swapchain +) { + ID3D11Texture2D *swapchainTexture; + D3D11_RENDER_TARGET_VIEW_DESC swapchainViewDesc; + D3D11_TEXTURE2D_DESC textureDesc; + HRESULT res; + + /* Clear all the texture data. */ + SDL_memset(resultTexture, 0, sizeof(D3D11Texture)); + + /* Grab the buffer from the swapchain */ + res = IDXGISwapChain_GetBuffer( + swapchain, + 0, + &D3D_IID_ID3D11Texture2D, + (void**) &swapchainTexture + ); + ERROR_CHECK_RETURN("Could not get buffer from swapchain", 0); + + /* Create the RTV for the swapchain */ + swapchainViewDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapchainViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + swapchainViewDesc.Texture2D.MipSlice = 0; + + res = ID3D11Device_CreateRenderTargetView( + renderer->device, + (ID3D11Resource*) swapchainTexture, + &swapchainViewDesc, + (ID3D11RenderTargetView**) &resultTexture->twod.targetView + ); + ERROR_CHECK_RETURN("Swapchain RT view creation failed", 0); + + /* Fill out the rest of the texture struct */ + resultTexture->handle = NULL; /* FIXME: Is drawing the backbuffer to an offscreen RT allowed? If so we'll need to fill in this and shaderView. */ + resultTexture->shaderView = NULL; + resultTexture->isRenderTarget = 1; + + ID3D11Texture2D_GetDesc(swapchainTexture, &textureDesc); + resultTexture->levelCount = textureDesc.MipLevels; + resultTexture->twod.width = textureDesc.Width; + resultTexture->twod.height = textureDesc.Height; + + /* Cleanup */ + ID3D11Texture2D_Release(swapchainTexture); + + return 1; +} + +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; + 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; + + /* Add the swapchain data to the window data */ + 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 Refresh-side texture for the swapchain */ + return D3D11_INTERNAL_InitializeSwapchainTexture( + renderer, + &swapchainData->refreshTexture, + swapchainData->swapchain + ); +} + +static uint8_t D3D11_INTERNAL_ResizeSwapchain( + D3D11Renderer *renderer, + D3D11SwapchainData *swapchainData +) { + int w, h; + HRESULT res; + + /* Release the old RTV */ + ID3D11RenderTargetView_Release(swapchainData->refreshTexture.twod.targetView); + + /* 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); + + /* Create the Refresh-side texture for the swapchain */ + return D3D11_INTERNAL_InitializeSwapchainTexture( + renderer, + &swapchainData->refreshTexture, + swapchainData->swapchain + ); +} + +/* Quit */ + +static void D3D11_DestroyDevice( + Refresh_Device* device +) { + D3D11Renderer *renderer = (D3D11Renderer*) device->driverData; + D3D11CommandBuffer *commandBuffer; + uint32_t i; + + /* Free the command buffer pool */ + for (i = 0; i < renderer->commandBufferPool->count; i += 1) + { + commandBuffer = renderer->commandBufferPool->elements[i]; + if (commandBuffer->commandList != NULL) + { + ID3D11CommandList_Release(commandBuffer->commandList); + } + ID3D11DeviceContext_Release(commandBuffer->context); + SDL_free(commandBuffer); + } + 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.twod.targetView); + 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); + + /* Release DXGI objects */ + IDXGIAdapter1_Release(renderer->adapter); + IDXGIFactory1_Release(renderer->factory); + + /* Unload the DLLs */ + SDL_UnloadObject(renderer->d3d11_dll); + SDL_UnloadObject(renderer->dxgi_dll); + SDL_UnloadObject(renderer->d3dcompiler_dll); + + /* Free the renderer and Refresh Device */ + SDL_free(renderer); + SDL_free(device); +} + +/* Drawing */ + +static void D3D11_DrawInstancedPrimitives( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + uint32_t baseVertex, + uint32_t startIndex, + uint32_t primitiveCount, + uint32_t instanceCount, + uint32_t vertexParamOffset, + uint32_t fragmentParamOffset +) { + NOT_IMPLEMENTED +} + +static void D3D11_DrawIndexedPrimitives( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + uint32_t baseVertex, + uint32_t startIndex, + uint32_t primitiveCount, + uint32_t vertexParamOffset, + uint32_t fragmentParamOffset +) { + NOT_IMPLEMENTED +} + +static void D3D11_DrawPrimitives( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + uint32_t vertexStart, + uint32_t primitiveCount, + uint32_t vertexParamOffset, + uint32_t fragmentParamOffset +) { + D3D11CommandBuffer *cmdbuf = (D3D11CommandBuffer*) commandBuffer; + ID3D11DeviceContext_Draw( + cmdbuf->context, + primitiveCount * 3, /* FIXME: Needs to use a primitive lookup table! */ + vertexStart + ); + + /* FIXME: vertex/fragment param offsets */ +} + +static void D3D11_DispatchCompute( + Refresh_Renderer* device, + Refresh_CommandBuffer* commandBuffer, + uint32_t groupCountX, + uint32_t groupCountY, + uint32_t groupCountZ, + uint32_t computeParamOffset +) { + NOT_IMPLEMENTED +} + +/* State Creation */ + +static ID3D11BlendState* D3D11_INTERNAL_FetchBlendState( + D3D11Renderer *renderer, + uint32_t numColorAttachments, + Refresh_ColorAttachmentDescription *colorAttachments +) { + ID3D11BlendState *result; + D3D11_BLEND_DESC blendDesc; + uint32_t i; + HRESULT res; + + /* Create a new blend state. + * The spec says the driver will not create duplicate states, so there's no need to cache. + */ + SDL_zero(blendDesc); /* needed for any unused RT entries */ + + blendDesc.AlphaToCoverageEnable = FALSE; + blendDesc.IndependentBlendEnable = TRUE; + + for (i = 0; i < numColorAttachments; i += 1) + { + blendDesc.RenderTarget[i].BlendEnable = colorAttachments[i].blendState.blendEnable; + blendDesc.RenderTarget[i].BlendOp = RefreshToD3D11_BlendOp[ + colorAttachments[i].blendState.colorBlendOp + ]; + blendDesc.RenderTarget[i].BlendOpAlpha = RefreshToD3D11_BlendOp[ + colorAttachments[i].blendState.alphaBlendOp + ]; + blendDesc.RenderTarget[i].DestBlend = RefreshToD3D11_BlendFactor[ + colorAttachments[i].blendState.dstColorBlendFactor + ]; + blendDesc.RenderTarget[i].DestBlendAlpha = RefreshToD3D11_BlendFactor[ + colorAttachments[i].blendState.dstAlphaBlendFactor + ]; + blendDesc.RenderTarget[i].RenderTargetWriteMask = colorAttachments[i].blendState.colorWriteMask; + blendDesc.RenderTarget[i].SrcBlend = RefreshToD3D11_BlendFactor[ + colorAttachments[i].blendState.srcColorBlendFactor + ]; + blendDesc.RenderTarget[i].SrcBlendAlpha = RefreshToD3D11_BlendFactor[ + colorAttachments[i].blendState.srcAlphaBlendFactor + ]; + } + + res = ID3D11Device_CreateBlendState( + renderer->device, + &blendDesc, + &result + ); + ERROR_CHECK_RETURN("Could not create blend state", NULL); + + return result; +} + +static ID3D11DepthStencilState* D3D11_INTERNAL_FetchDepthStencilState( + D3D11Renderer *renderer, + Refresh_DepthStencilState depthStencilState +) { + ID3D11DepthStencilState *result; + D3D11_DEPTH_STENCIL_DESC dsDesc; + HRESULT res; + + /* Create a new depth-stencil state. + * The spec says the driver will not create duplicate states, so there's no need to cache. + */ + dsDesc.DepthEnable = depthStencilState.depthTestEnable; + dsDesc.StencilEnable = depthStencilState.stencilTestEnable; + dsDesc.DepthFunc = depthStencilState.compareOp; + dsDesc.DepthWriteMask = ( + depthStencilState.depthWriteEnable ? + D3D11_DEPTH_WRITE_MASK_ALL : + D3D11_DEPTH_WRITE_MASK_ZERO + ); + + dsDesc.BackFace.StencilFunc = depthStencilState.backStencilState.compareOp; + dsDesc.BackFace.StencilDepthFailOp = depthStencilState.backStencilState.depthFailOp; + dsDesc.BackFace.StencilFailOp = depthStencilState.backStencilState.failOp; + dsDesc.BackFace.StencilPassOp = depthStencilState.backStencilState.passOp; + + dsDesc.FrontFace.StencilFunc = depthStencilState.frontStencilState.compareOp; + dsDesc.FrontFace.StencilDepthFailOp = depthStencilState.frontStencilState.depthFailOp; + dsDesc.FrontFace.StencilFailOp = depthStencilState.frontStencilState.failOp; + dsDesc.FrontFace.StencilPassOp = depthStencilState.frontStencilState.passOp; + + /* FIXME: D3D11 doesn't have separate read/write masks for each stencil side. What should we do? */ + dsDesc.StencilReadMask = depthStencilState.backStencilState.compareMask; + dsDesc.StencilWriteMask = depthStencilState.backStencilState.writeMask; + + /* FIXME: What do we do with these? + * depthStencilState.depthBoundsTestEnable + * depthStencilState.maxDepthBounds + * depthStencilState.minDepthBounds + */ + + res = ID3D11Device_CreateDepthStencilState( + renderer->device, + &dsDesc, + &result + ); + ERROR_CHECK_RETURN("Could not create depth-stencil state", NULL); + + return result; +} + +static ID3D11RasterizerState* D3D11_INTERNAL_FetchRasterizerState( + D3D11Renderer *renderer, + Refresh_RasterizerState rasterizerState +) { + ID3D11RasterizerState *result; + D3D11_RASTERIZER_DESC rasterizerDesc; + HRESULT res; + + /* Create a new rasterizer state. + * The spec says the driver will not create duplicate states, so there's no need to cache. + */ + rasterizerDesc.AntialiasedLineEnable = FALSE; + rasterizerDesc.CullMode = RefreshToD3D11_CullMode[rasterizerState.cullMode]; + rasterizerDesc.DepthBias = (INT) rasterizerState.depthBiasConstantFactor; /* FIXME: Is this cast correct? */ + rasterizerDesc.DepthBiasClamp = rasterizerState.depthBiasClamp; + rasterizerDesc.DepthClipEnable = TRUE; /* FIXME: Do we want this...? */ + rasterizerDesc.FillMode = RefreshToD3D11_FillMode[rasterizerState.fillMode]; + rasterizerDesc.FrontCounterClockwise = (rasterizerState.frontFace == REFRESH_FRONTFACE_COUNTER_CLOCKWISE); + rasterizerDesc.MultisampleEnable = TRUE; /* only applies to MSAA render targets */ + rasterizerDesc.ScissorEnable = TRUE; + rasterizerDesc.SlopeScaledDepthBias = rasterizerState.depthBiasSlopeFactor; + + res = ID3D11Device_CreateRasterizerState( + renderer->device, + &rasterizerDesc, + &result + ); + ERROR_CHECK_RETURN("Could not create rasterizer state", NULL); + + return result; +} + +static uint32_t D3D11_INTERNAL_FindIndexOfVertexBinding( + uint32_t targetBinding, + const Refresh_VertexBinding *bindings, + uint32_t numBindings +) { + uint32_t i; + for (i = 0; i < numBindings; i += 1) + { + if (bindings[i].binding == targetBinding) + { + return i; + } + } + + Refresh_LogError("Could not find vertex binding %d!", targetBinding); + return 0; +} + +static ID3D11InputLayout* D3D11_INTERNAL_FetchInputLayout( + D3D11Renderer *renderer, + Refresh_VertexInputState inputState, + void *shaderBytes, + size_t shaderByteLength +) { + ID3D11InputLayout *result = NULL; + D3D11_INPUT_ELEMENT_DESC *elementDescs; + uint32_t i, bindingIndex; + HRESULT res; + + /* Allocate an array of vertex elements */ + elementDescs = SDL_stack_alloc( + D3D11_INPUT_ELEMENT_DESC, + inputState.vertexAttributeCount + ); + + /* Create the array of input elements */ + for (i = 0; i < inputState.vertexAttributeCount; i += 1) + { + elementDescs[i].AlignedByteOffset = inputState.vertexAttributes[i].offset; + elementDescs[i].Format = RefreshToD3D11_VertexFormat[ + inputState.vertexAttributes[i].format + ]; + elementDescs[i].InputSlot = inputState.vertexAttributes[i].binding; + + bindingIndex = D3D11_INTERNAL_FindIndexOfVertexBinding( + elementDescs[i].InputSlot, + inputState.vertexBindings, + inputState.vertexBindingCount + ); + elementDescs[i].InputSlotClass = RefreshToD3D11_VertexInputRate[ + inputState.vertexBindings[bindingIndex].inputRate + ]; + /* The spec requires this to be 0 for per-vertex data */ + elementDescs[i].InstanceDataStepRate = ( + elementDescs[i].InputSlotClass == D3D11_INPUT_PER_INSTANCE_DATA ? 1 : 0 + ); + + elementDescs[i].SemanticIndex = inputState.vertexAttributes[i].location; + elementDescs[i].SemanticName = "TEXCOORD"; + } + + res = ID3D11Device_CreateInputLayout( + renderer->device, + elementDescs, + inputState.vertexAttributeCount, + shaderBytes, + shaderByteLength, + &result + ); + if (FAILED(res)) + { + Refresh_LogError("Could not create input layout! Error: %X", res); + SDL_stack_free(elementDescs); + return NULL; + } + + /* FIXME: + * These are not cached by the driver! Should we cache them, or allow duplicates? + * If we have one input layout per graphics pipeline maybe that wouldn't be so bad...? + */ + + SDL_stack_free(elementDescs); + return result; +} + +static Refresh_ComputePipeline* D3D11_CreateComputePipeline( + Refresh_Renderer* driverData, + Refresh_ComputeShaderInfo* computeShaderInfo +) { + NOT_IMPLEMENTED +} + +static Refresh_GraphicsPipeline* D3D11_CreateGraphicsPipeline( + Refresh_Renderer* driverData, + Refresh_GraphicsPipelineCreateInfo* pipelineCreateInfo +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11GraphicsPipeline *pipeline = (D3D11GraphicsPipeline*) SDL_malloc(sizeof(D3D11GraphicsPipeline)); + D3D11ShaderModule *vertShaderModule = (D3D11ShaderModule*) pipelineCreateInfo->vertexShaderInfo.shaderModule; + D3D11ShaderModule *fragShaderModule = (D3D11ShaderModule*) pipelineCreateInfo->fragmentShaderInfo.shaderModule; + ID3D10Blob *errorBlob; + int32_t i; + HRESULT res; + + /* Color attachments */ + pipeline->numColorAttachments = pipelineCreateInfo->attachmentInfo.colorAttachmentCount; + for (i = 0; i < pipeline->numColorAttachments; i += 1) + { + pipeline->colorAttachmentSampleCounts[i] = RefreshToD3D11_SampleCount[ + pipelineCreateInfo->attachmentInfo.colorAttachmentDescriptions[i].sampleCount + ]; + + pipeline->colorAttachmentFormats[i] = RefreshToD3D11_SurfaceFormat[ + pipelineCreateInfo->attachmentInfo.colorAttachmentDescriptions[i].format + ]; + } + + pipeline->blendConstants[0] = pipelineCreateInfo->blendConstants[0]; + pipeline->blendConstants[1] = pipelineCreateInfo->blendConstants[1]; + pipeline->blendConstants[2] = pipelineCreateInfo->blendConstants[2]; + pipeline->blendConstants[3] = pipelineCreateInfo->blendConstants[3]; + pipeline->multisampleState = pipelineCreateInfo->multisampleState; + + pipeline->colorAttachmentBlendState = D3D11_INTERNAL_FetchBlendState( + renderer, + pipelineCreateInfo->attachmentInfo.colorAttachmentCount, + pipelineCreateInfo->attachmentInfo.colorAttachmentDescriptions + ); + + /* Depth stencil */ + pipeline->hasDepthStencilAttachment = pipelineCreateInfo->attachmentInfo.hasDepthStencilAttachment; + pipeline->depthStencilAttachmentFormat = RefreshToD3D11_SurfaceFormat[ + pipelineCreateInfo->attachmentInfo.depthStencilFormat + ]; + pipeline->stencilRef = pipelineCreateInfo->depthStencilState.backStencilState.reference; /* FIXME: Should we use front or back? */ + + pipeline->depthStencilState = D3D11_INTERNAL_FetchDepthStencilState( + renderer, + pipelineCreateInfo->depthStencilState + ); + + /* Rasterizer state */ + pipeline->primitiveTopology = RefreshToD3D11_PrimitiveType[pipelineCreateInfo->primitiveType]; + pipeline->rasterizerState = D3D11_INTERNAL_FetchRasterizerState( + renderer, + pipelineCreateInfo->rasterizerState + ); + + /* Vertex Shader */ + if (vertShaderModule->shader == NULL) + { + res = renderer->D3DCompileFunc( + vertShaderModule->shaderSource, + vertShaderModule->shaderSourceLength, + NULL, + NULL, + NULL, + "main", + "vs_5_0", + 0, + 0, + &vertShaderModule->blob, + &errorBlob + ); + if (FAILED(res)) + { + Refresh_LogError("Vertex Shader Compile Error: %s", ID3D10Blob_GetBufferPointer(errorBlob)); + return NULL; + } + + res = ID3D11Device_CreateVertexShader( + renderer->device, + ID3D10Blob_GetBufferPointer(vertShaderModule->blob), + ID3D10Blob_GetBufferSize(vertShaderModule->blob), + NULL, + (ID3D11VertexShader**) &vertShaderModule->shader + ); + ERROR_CHECK_RETURN("Could not create vertex shader", NULL); + } + pipeline->vertexShader = (ID3D11VertexShader*)vertShaderModule->shader; + + /* Input Layout */ + pipeline->inputLayout = D3D11_INTERNAL_FetchInputLayout( + renderer, + pipelineCreateInfo->vertexInputState, + ID3D10Blob_GetBufferPointer(vertShaderModule->blob), + ID3D10Blob_GetBufferSize(vertShaderModule->blob) + ); + + /* Fragment Shader */ + if (fragShaderModule->shader == NULL) + { + res = renderer->D3DCompileFunc( + fragShaderModule->shaderSource, + fragShaderModule->shaderSourceLength, + NULL, + NULL, + NULL, + "main", + "ps_5_0", + 0, + 0, + &fragShaderModule->blob, + &errorBlob + ); + if (FAILED(res)) + { + Refresh_LogError("Fragment Shader Compile Error: %s", ID3D10Blob_GetBufferPointer(errorBlob)); + return NULL; + } + + res = ID3D11Device_CreatePixelShader( + renderer->device, + ID3D10Blob_GetBufferPointer(fragShaderModule->blob), + ID3D10Blob_GetBufferSize(fragShaderModule->blob), + NULL, + (ID3D11PixelShader**) &fragShaderModule->shader + ); + ERROR_CHECK_RETURN("Could not create pixel shader", NULL); + } + pipeline->fragmentShader = (ID3D11PixelShader*) fragShaderModule->shader; + + /* FIXME: Need to create uniform buffers for the shaders */ + + return (Refresh_GraphicsPipeline*) pipeline; +} + +static Refresh_Sampler* D3D11_CreateSampler( + Refresh_Renderer* driverData, + Refresh_SamplerStateCreateInfo* samplerStateCreateInfo +) { + NOT_IMPLEMENTED +} + +static Refresh_ShaderModule* D3D11_CreateShaderModule( + Refresh_Renderer *driverData, + Refresh_ShaderModuleCreateInfo *shaderModuleCreateInfo +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + + D3D11ShaderModule *shaderModule = (D3D11ShaderModule*) SDL_malloc(sizeof(D3D11ShaderModule)); + shaderModule->shader = NULL; /* created when binding to a pipeline */ + shaderModule->blob = NULL; /* created when binding to a pipeline */ + shaderModule->shaderSourceLength = shaderModuleCreateInfo->codeSize; + shaderModule->shaderSource = (char*) SDL_malloc(shaderModule->shaderSourceLength); + SDL_memcpy(shaderModule->shaderSource, shaderModuleCreateInfo->byteCode, shaderModuleCreateInfo->codeSize); + + return (Refresh_ShaderModule*) shaderModule; +} + +static Refresh_Texture* D3D11_CreateTexture( + Refresh_Renderer* driverData, + Refresh_TextureCreateInfo* textureCreateInfo +) { + NOT_IMPLEMENTED +} + +static Refresh_Buffer* D3D11_CreateBuffer( + Refresh_Renderer* driverData, + Refresh_BufferUsageFlags usageFlags, + uint32_t sizeInBytes +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + ID3D11Buffer *bufferHandle; + D3D11_BUFFER_DESC bufferDesc; + uint32_t bindFlags = 0; + D3D11Buffer *result; + HRESULT res; + + if (usageFlags & REFRESH_BUFFERUSAGE_INDEX_BIT) + { + bindFlags |= D3D11_BIND_INDEX_BUFFER; + } + if (usageFlags & REFRESH_BUFFERUSAGE_VERTEX_BIT) + { + bindFlags |= D3D11_BIND_VERTEX_BUFFER; + } + if (usageFlags & REFRESH_BUFFERUSAGE_COMPUTE_BIT) + { + bindFlags |= D3D11_BIND_UNORDERED_ACCESS; + bindFlags |= D3D11_BIND_SHADER_RESOURCE; + } + + bufferDesc.BindFlags = bindFlags; + bufferDesc.ByteWidth = sizeInBytes; + bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + bufferDesc.MiscFlags = 0; + bufferDesc.StructureByteStride = 0; /* FIXME: Is this right...? */ + bufferDesc.Usage = D3D11_USAGE_DYNAMIC; + + res = ID3D11Device_CreateBuffer( + renderer->device, + &bufferDesc, + NULL, + &bufferHandle + ); + ERROR_CHECK_RETURN("Could not create buffer", NULL); + + result = (D3D11Buffer*) SDL_malloc(sizeof(D3D11Buffer)); + result->handle = bufferHandle; + return (Refresh_Buffer*) result; +} + +/* Setters */ + +static void D3D11_SetTextureData( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + Refresh_TextureSlice* textureSlice, + void* data, + uint32_t dataLengthInBytes +) { + NOT_IMPLEMENTED +} + +static void D3D11_SetTextureDataYUV( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + Refresh_Texture* y, + Refresh_Texture* u, + Refresh_Texture* v, + uint32_t yWidth, + uint32_t yHeight, + uint32_t uvWidth, + uint32_t uvHeight, + void* data, + uint32_t dataLength +) { + NOT_IMPLEMENTED +} + +static void D3D11_CopyTextureToTexture( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + Refresh_TextureSlice* sourceTextureSlice, + Refresh_TextureSlice* destinationTextureSlice, + Refresh_Filter filter +) { + NOT_IMPLEMENTED +} + +static void D3D11_CopyTextureToBuffer( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + Refresh_TextureSlice* textureSlice, + Refresh_Buffer* buffer +) { + NOT_IMPLEMENTED +} + +static void D3D11_SetBufferData( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_Buffer *buffer, + uint32_t offsetInBytes, + void *data, + uint32_t dataLength +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *cmdbuf = (D3D11CommandBuffer*) commandBuffer; + D3D11Buffer *buf = (D3D11Buffer*) buffer; + D3D11_MAPPED_SUBRESOURCE mappedSubresource; + HRESULT res; + + /* FIXME: How should we handle partial updates? */ + + /* Map the buffer */ + res = ID3D11DeviceContext_Map( + cmdbuf->context, + (ID3D11Resource*) buf->handle, + 0, + D3D11_MAP_WRITE_DISCARD, + 0, + &mappedSubresource + ); + ERROR_CHECK_RETURN("Could not map buffer",); + + /* Copy in the data */ + SDL_memcpy( + (uint8_t*) mappedSubresource.pData + offsetInBytes, + data, + dataLength + ); + + /* Unmap the buffer */ + ID3D11DeviceContext_Unmap( + cmdbuf->context, + (ID3D11Resource*) buf->handle, + 0 + ); +} + +static uint32_t D3D11_PushVertexShaderUniforms( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + void* data, + uint32_t dataLengthInBytes +) { + NOT_IMPLEMENTED +} + +static uint32_t D3D11_PushFragmentShaderUniforms( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + void* data, + uint32_t dataLengthInBytes +) { + NOT_IMPLEMENTED +} + +static uint32_t D3D11_PushComputeShaderUniforms( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + void* data, + uint32_t dataLengthInBytes +) { + NOT_IMPLEMENTED +} + +static void D3D11_BindVertexSamplers( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + Refresh_Texture** pTextures, + Refresh_Sampler** pSamplers +) { + NOT_IMPLEMENTED +} + +static void D3D11_BindFragmentSamplers( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + Refresh_Texture** pTextures, + Refresh_Sampler** pSamplers +) { + NOT_IMPLEMENTED +} + +/* Getters */ + +static void D3D11_GetBufferData( + Refresh_Renderer* driverData, + Refresh_Buffer* buffer, + void* data, + uint32_t dataLengthInBytes +) { + NOT_IMPLEMENTED +} + +/* Disposal */ + +static void D3D11_QueueDestroyTexture( + Refresh_Renderer* driverData, + Refresh_Texture* texture +) { + NOT_IMPLEMENTED +} + +static void D3D11_QueueDestroySampler( + Refresh_Renderer* driverData, + Refresh_Sampler* sampler +) { + NOT_IMPLEMENTED +} + +static void D3D11_QueueDestroyBuffer( + Refresh_Renderer* driverData, + Refresh_Buffer* buffer +) { + D3D11Buffer *d3dBuffer = (D3D11Buffer*) buffer; + ID3D11Buffer_Release(d3dBuffer->handle); + SDL_free(d3dBuffer); +} + +static void D3D11_QueueDestroyShaderModule( + Refresh_Renderer* driverData, + Refresh_ShaderModule* shaderModule +) { + D3D11ShaderModule *d3dShaderModule = (D3D11ShaderModule*) shaderModule; + ID3D11DeviceChild_Release(d3dShaderModule->shader); + ID3D10Blob_Release(d3dShaderModule->blob); + SDL_free(d3dShaderModule->shaderSource); + SDL_free(d3dShaderModule); +} + +static void D3D11_QueueDestroyComputePipeline( + Refresh_Renderer* driverData, + Refresh_ComputePipeline* computePipeline +) { + NOT_IMPLEMENTED +} + +static void D3D11_QueueDestroyGraphicsPipeline( + Refresh_Renderer* driverData, + Refresh_GraphicsPipeline* graphicsPipeline +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11GraphicsPipeline *d3dGraphicsPipeline = (D3D11GraphicsPipeline*) graphicsPipeline; + + ID3D11BlendState_Release(d3dGraphicsPipeline->colorAttachmentBlendState); + ID3D11DepthStencilState_Release(d3dGraphicsPipeline->depthStencilState); + ID3D11RasterizerState_Release(d3dGraphicsPipeline->rasterizerState); + ID3D11InputLayout_Release(d3dGraphicsPipeline->inputLayout); + + /* FIXME: Release uniform buffers, once that's written in */ + + SDL_free(d3dGraphicsPipeline); +} + +/* Graphics State */ + +static void D3D11_BeginRenderPass( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_Rect *renderArea, + Refresh_ColorAttachmentInfo *colorAttachmentInfos, + uint32_t colorAttachmentCount, + Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *cmdbuf = (D3D11CommandBuffer*) commandBuffer; + D3D11Texture *texture; + float clearColors[4]; + D3D11_CLEAR_FLAG dsClearFlags; + D3D11_VIEWPORT viewports[1]; + D3D11_RECT scissorRects[1]; + uint8_t i; + + /* Clear the list of attachments for the command buffer */ + for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) + { + cmdbuf->rtViews[i] = NULL; + } + cmdbuf->dsView = NULL; + + /* Get the RTVs for each color attachment. */ + cmdbuf->numBoundColorAttachments = colorAttachmentCount; + for (i = 0; i < colorAttachmentCount; i += 1) + { + cmdbuf->rtViews[i] = (ID3D11RenderTargetView*) ((D3D11Texture*) colorAttachmentInfos[i].texture)->twod.targetView; + } + + /* Get the DSV for the depth stencil attachment, if one exists */ + if (depthStencilAttachmentInfo != NULL) + { + cmdbuf->dsView = (ID3D11DepthStencilView*) ((D3D11Texture*) depthStencilAttachmentInfo->texture)->twod.targetView; + } + + /* Set the render targets. */ + ID3D11DeviceContext_OMSetRenderTargets( + cmdbuf->context, + colorAttachmentCount, + cmdbuf->rtViews, + cmdbuf->dsView + ); + + /* Perform load ops on those render targets. */ + for (i = 0; i < colorAttachmentCount; i += 1) + { + texture = (D3D11Texture*) colorAttachmentInfos[i].texture; + + if (colorAttachmentInfos[i].loadOp == REFRESH_LOADOP_CLEAR) + { + clearColors[0] = colorAttachmentInfos[i].clearColor.x; + clearColors[1] = colorAttachmentInfos[i].clearColor.y; + clearColors[2] = colorAttachmentInfos[i].clearColor.z; + clearColors[3] = colorAttachmentInfos[i].clearColor.w; + + ID3D11DeviceContext_ClearRenderTargetView( + cmdbuf->context, + (ID3D11RenderTargetView*) texture->twod.targetView, + clearColors + ); + } + } + + if (cmdbuf->dsView != NULL) + { + dsClearFlags = 0; + if (depthStencilAttachmentInfo->loadOp == REFRESH_LOADOP_CLEAR) + { + dsClearFlags |= D3D11_CLEAR_DEPTH; + } + if (depthStencilAttachmentInfo->stencilLoadOp == REFRESH_LOADOP_CLEAR) + { + dsClearFlags |= D3D11_CLEAR_STENCIL; + } + + if (dsClearFlags != 0) + { + ID3D11DeviceContext_ClearDepthStencilView( + cmdbuf->context, + (ID3D11DepthStencilView*) ((D3D11Texture*) depthStencilAttachmentInfo->texture)->twod.targetView, + dsClearFlags, + depthStencilAttachmentInfo->depthStencilClearValue.depth, + (uint8_t) depthStencilAttachmentInfo->depthStencilClearValue.stencil + ); + } + } + + /* Set default viewport and scissor state */ + /* FIXME: Check how Vulkan sets these defaults */ + viewports[0].TopLeftX = 0; + viewports[0].TopLeftY = 0; + viewports[0].Width = (float) ((D3D11Texture*) colorAttachmentInfos[0].texture)->twod.width; + viewports[0].Height = (float) ((D3D11Texture*) colorAttachmentInfos[0].texture)->twod.height; + viewports[0].MinDepth = 0; /* FIXME: Check what Vulkan does for these. */ + viewports[0].MaxDepth = 1; + + ID3D11DeviceContext_RSSetViewports( + cmdbuf->context, + 1, + viewports + ); + + scissorRects[0].left = 0; + scissorRects[0].right = (LONG) viewports[0].Width; + scissorRects[0].top = 0; + scissorRects[0].bottom = (LONG) viewports[0].Height; + + ID3D11DeviceContext_RSSetScissorRects( + cmdbuf->context, + 1, + scissorRects + ); + + /* FIXME: What should we do with render area? */ +} + +static void D3D11_EndRenderPass( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer +) { + /* FIXME: What should we do here? */ +} + +static void D3D11_BindGraphicsPipeline( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + Refresh_GraphicsPipeline* graphicsPipeline +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *cmdbuf = (D3D11CommandBuffer*) commandBuffer; + D3D11GraphicsPipeline *pipeline = (D3D11GraphicsPipeline*) graphicsPipeline; + + ID3D11DeviceContext_OMSetBlendState( + cmdbuf->context, + pipeline->colorAttachmentBlendState, + pipeline->blendConstants, + 0xffffffff + ); + + ID3D11DeviceContext_OMSetDepthStencilState( + cmdbuf->context, + pipeline->depthStencilState, + pipeline->stencilRef + ); + + ID3D11DeviceContext_IASetPrimitiveTopology( + cmdbuf->context, + pipeline->primitiveTopology + ); + + ID3D11DeviceContext_IASetInputLayout( + cmdbuf->context, + pipeline->inputLayout + ); + + ID3D11DeviceContext_RSSetState( + cmdbuf->context, + pipeline->rasterizerState + ); + + ID3D11DeviceContext_VSSetShader( + cmdbuf->context, + pipeline->vertexShader, + NULL, + 0 + ); + + ID3D11DeviceContext_PSSetShader( + cmdbuf->context, + pipeline->fragmentShader, + NULL, + 0 + ); +} + +static void D3D11_SetViewport( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + Refresh_Viewport* viewport +) { + NOT_IMPLEMENTED +} + +static void D3D11_SetScissor( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + Refresh_Rect* scissor +) { + NOT_IMPLEMENTED +} + +static void D3D11_BindVertexBuffers( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + uint32_t firstBinding, + uint32_t bindingCount, + Refresh_Buffer** pBuffers, + uint64_t* pOffsets +) { + D3D11CommandBuffer *cmdbuf = (D3D11CommandBuffer*) commandBuffer; + ID3D11Buffer **buffers; + uint32_t *strides; + uint32_t i; + + buffers = SDL_stack_alloc(ID3D11Buffer*, bindingCount); + strides = SDL_stack_alloc(uint32_t, bindingCount); + for (i = 0; i < bindingCount; i += 1) + { + buffers[i] = ((D3D11Buffer*) pBuffers[i])->handle; + strides[i] = 20; + /* FIXME: Strides! Will probably need to get this from InputLayout somehow. */ + } + + /* FIXME: State shadowing? */ + ID3D11DeviceContext_IASetVertexBuffers( + cmdbuf->context, + firstBinding, + bindingCount, + buffers, + strides, + (const UINT*) pOffsets + ); + + SDL_stack_free(buffers); + SDL_stack_free(strides); +} + +static void D3D11_BindIndexBuffer( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + Refresh_Buffer* buffer, + uint64_t offset, + Refresh_IndexElementSize indexElementSize +) { + NOT_IMPLEMENTED +} + +static void D3D11_BindComputePipeline( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + Refresh_ComputePipeline* computePipeline +) { + NOT_IMPLEMENTED +} + +static void D3D11_BindComputeBuffers( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + Refresh_Buffer** pBuffers +) { + NOT_IMPLEMENTED +} + +static void D3D11_BindComputeTextures( + Refresh_Renderer* driverData, + Refresh_CommandBuffer* commandBuffer, + Refresh_Texture** pTextures +) { + NOT_IMPLEMENTED +} + +static Refresh_CommandBuffer* D3D11_AcquireCommandBuffer( + Refresh_Renderer* driverData, + uint8_t fixed +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *commandBuffer = NULL; + uint32_t i; + HRESULT res; + + /* Make sure multiple threads can't acquire the same command buffer. */ + SDL_LockMutex(renderer->commandBufferAcquisitionMutex); + + /* Try to use an existing command buffer, if one is available. */ + for (i = 0; i < renderer->commandBufferPool->count; i += 1) + { + /* Search for a command buffer in the pool that is not fixed, and is not recording. */ + if (!renderer->commandBufferPool->elements[i]->fixed && !renderer->commandBufferPool->elements[i]->recording) + { + commandBuffer = renderer->commandBufferPool->elements[i]; + break; + } + } + + /* If there are no free command buffers, make a new one. */ + if (commandBuffer == NULL) + { + /* Expand the capacity as needed. */ + EXPAND_ELEMENTS_IF_NEEDED( + renderer->commandBufferPool, + 2, + D3D11CommandBuffer* + ); + + /* Create a new command buffer */ + renderer->commandBufferPool->elements[i] = (D3D11CommandBuffer*) SDL_malloc( + sizeof(D3D11CommandBuffer) + ); + + /* Assign it a new deferred context */ + res = ID3D11Device_CreateDeferredContext( + renderer->device, + 0, + &renderer->commandBufferPool->elements[i]->context + ); + if (FAILED(res)) + { + SDL_UnlockMutex(renderer->commandBufferAcquisitionMutex); + ERROR_CHECK_RETURN("Could not create deferred context for command buffer", NULL); + } + + /* Now we have a new command buffer we can use! */ + commandBuffer = renderer->commandBufferPool->elements[i]; + renderer->commandBufferPool->count += 1; + } + + /* Set up the command buffer */ + commandBuffer->threadID = SDL_ThreadID(); + commandBuffer->recording = 1; + commandBuffer->fixed = fixed; + commandBuffer->swapchainData = NULL; + commandBuffer->commandList = NULL; + commandBuffer->dsView = NULL; + commandBuffer->numBoundColorAttachments = 0; + for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) + { + commandBuffer->rtViews[i] = NULL; + } + + SDL_UnlockMutex(renderer->commandBufferAcquisitionMutex); + + 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; +} + +static Refresh_Texture* D3D11_AcquireSwapchainTexture( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + void *windowHandle, + uint32_t *pWidth, + uint32_t *pHeight +) { + 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; + + /* Send the dimensions to the out parameters. */ + *pWidth = swapchainData->refreshTexture.twod.width; + *pHeight = swapchainData->refreshTexture.twod.height; + + /* Return the swapchain texture */ + return (Refresh_Texture*) &swapchainData->refreshTexture; +} + +Refresh_TextureFormat D3D11_GetSwapchainFormat( + Refresh_Renderer* driverData, + void* windowHandle +) { + return DXGI_FORMAT_R8G8B8A8_UNORM; +} + +static void D3D11_Submit( + Refresh_Renderer* driverData, + uint32_t commandBufferCount, + Refresh_CommandBuffer** pCommandBuffers +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + ID3D11CommandList *commandList; + uint32_t i; + HRESULT res; + + for (i = 0; i < commandBufferCount; i += 1) + { + D3D11CommandBuffer *commandBuffer = (D3D11CommandBuffer*) pCommandBuffers[i]; + + /* FIXME: Should add sanity check that current thread ID matches the command buffer's threadID. */ + + if (commandBuffer->fixed && !commandBuffer->recording) + { + /* Grab the prerecorded command list. */ + commandList = commandBuffer->commandList; + } + else + { + /* Serialize the commands into a command list */ + res = ID3D11DeviceContext_FinishCommandList( + commandBuffer->context, + 0, + &commandList + ); + ERROR_CHECK("Could not finish command list recording"); + } + + /* Submit the command list to the immediate context */ + SDL_LockMutex(renderer->contextLock); + ID3D11DeviceContext_ExecuteCommandList( + renderer->immediateContext, + commandList, + 0 + ); + SDL_UnlockMutex(renderer->contextLock); + + /* Now that we're done, either save the command list or release it. */ + if (commandBuffer->fixed) + { + commandBuffer->commandList = commandList; + } + else + { + ID3D11CommandList_Release(commandList); + } + + /* 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) + { + SDL_LockMutex(renderer->contextLock); + IDXGISwapChain_Present( + commandBuffer->swapchainData->swapchain, + 1, /* FIXME: Assumes vsync! */ + 0 + ); + SDL_UnlockMutex(renderer->contextLock); + } + } +} + +static void D3D11_Wait( + Refresh_Renderer* driverData +) { + NOT_IMPLEMENTED +} + +static Refresh_Device* D3D11_CreateDevice( + Refresh_PresentationParameters* presentationParameters, + uint8_t debugMode +) { + D3D11Renderer *renderer; + PFN_CREATE_DXGI_FACTORY CreateDXGIFactoryFunc; + PFN_D3D11_CREATE_DEVICE D3D11CreateDeviceFunc; + D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_0 }; + void* factory6; + uint32_t flags; + DXGI_ADAPTER_DESC1 adapterDesc; + HRESULT res; + Refresh_Device* result; + + /* Allocate and zero out the renderer */ + renderer = (D3D11Renderer*) SDL_malloc(sizeof(D3D11Renderer)); + SDL_memset(renderer, 0, sizeof(D3D11Renderer)); + + /* Load the D3DCompiler library */ + renderer->d3dcompiler_dll = SDL_LoadObject(D3DCOMPILER_DLL); + if (renderer->d3dcompiler_dll == NULL) + { + Refresh_LogError("Could not find " D3DCOMPILER_DLL); + return NULL; + } + + /* Load the D3DCompile function pointer */ + renderer->D3DCompileFunc = (PFN_D3DCOMPILE) SDL_LoadFunction( + renderer->d3dcompiler_dll, + "D3DCompile" + ); + if (renderer->D3DCompileFunc == NULL) + { + Refresh_LogError("Could not load D3DCompile function!"); + return NULL; + } + + /* Load the DXGI library */ + renderer->dxgi_dll = SDL_LoadObject(DXGI_DLL); + if (renderer->dxgi_dll == NULL) + { + Refresh_LogError("Could not find " DXGI_DLL); + return NULL; + } + + /* Load the CreateDXGIFactory function */ + CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY) SDL_LoadFunction( + renderer->dxgi_dll, + "CreateDXGIFactory1" + ); + if (CreateDXGIFactoryFunc == NULL) + { + Refresh_LogError("Could not load CreateDXGIFactory1 function!"); + return NULL; + } + + /* Create the DXGI factory */ + res = CreateDXGIFactoryFunc( + &D3D_IID_IDXGIFactory1, + &renderer->factory + ); + ERROR_CHECK_RETURN("Could not create DXGIFactory", NULL); + + /* Get the default adapter */ + res = IDXGIAdapter1_QueryInterface( + renderer->factory, + &D3D_IID_IDXGIFactory6, + (void**) &factory6 + ); + if (SUCCEEDED(res)) + { + IDXGIFactory6_EnumAdapterByGpuPreference( + (IDXGIFactory6*) factory6, + 0, + DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, + &D3D_IID_IDXGIAdapter1, + &renderer->adapter + ); + } + else + { + IDXGIFactory1_EnumAdapters1( + renderer->factory, + 0, + &renderer->adapter + ); + } + + /* Get information about the selected adapter. Used for logging info. */ + IDXGIAdapter1_GetDesc1(renderer->adapter, &adapterDesc); + + /* Load the D3D library */ + renderer->d3d11_dll = SDL_LoadObject(D3D11_DLL); + if (renderer->d3d11_dll == NULL) + { + Refresh_LogError("Could not find " D3D11_DLL); + return NULL; + } + + /* Load the CreateDevice function */ + D3D11CreateDeviceFunc = (PFN_D3D11_CREATE_DEVICE) SDL_LoadFunction( + renderer->d3d11_dll, + "D3D11CreateDevice" + ); + if (D3D11CreateDeviceFunc == NULL) + { + Refresh_LogError("Could not load D3D11CreateDevice function!"); + return NULL; + } + + /* Set up device flags */ + flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + if (debugMode) + { + flags |= D3D11_CREATE_DEVICE_DEBUG; + } + + /* Create the device */ +tryCreateDevice: + res = D3D11CreateDeviceFunc( + (IDXGIAdapter*) renderer->adapter, + D3D_DRIVER_TYPE_UNKNOWN, /* Must be UNKNOWN if adapter is non-null according to spec */ + NULL, + flags, + levels, + SDL_arraysize(levels), + D3D11_SDK_VERSION, + &renderer->device, + &renderer->featureLevel, + &renderer->immediateContext + ); + if (FAILED(res) && debugMode) + { + /* If device creation failed, and we're in debug mode, remove the debug flag and try again. */ + Refresh_LogWarn("Creating device in debug mode failed with error %08X. Trying non-debug.", res); + flags &= ~D3D11_CREATE_DEVICE_DEBUG; + debugMode = 0; + goto tryCreateDevice; + } + + ERROR_CHECK_RETURN("Could not create D3D11 device", NULL); + + /* Print driver info */ + Refresh_LogInfo("Refresh Driver: D3D11"); + Refresh_LogInfo("D3D11 Adapter: %S", adapterDesc.Description); + + /* Create the command buffer pool */ + renderer->commandBufferPool = (D3D11CommandBufferPool*) SDL_malloc( + sizeof(D3D11CommandBufferPool) + ); + SDL_memset(renderer->commandBufferPool, 0, sizeof(D3D11CommandBufferPool)); + + /* Create mutexes */ + renderer->contextLock = SDL_CreateMutex(); + renderer->commandBufferAcquisitionMutex = SDL_CreateMutex(); + + /* Initialize miscellaneous renderer members */ + renderer->debugMode = (flags & D3D11_CREATE_DEVICE_DEBUG) != 0; + renderer->blendFactor.x = 1.0f; + renderer->blendFactor.y = 1.0f; + renderer->blendFactor.z = 1.0f; + renderer->blendFactor.w = 1.0f; + + /* Create the Refresh Device */ + result = (Refresh_Device*) SDL_malloc(sizeof(Refresh_Device)); + ASSIGN_DRIVER(D3D11) + + 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; +} + +Refresh_Driver D3D11Driver = { + "D3D11", + D3D11_CreateDevice +}; + +#else + +extern int this_tu_is_empty; + +#endif //REFRESH_DRIVER_D3D11 diff --git a/src/Refresh_Driver_D3D11_cdefines.h b/src/Refresh_Driver_D3D11_cdefines.h new file mode 100644 index 0000000..e269212 --- /dev/null +++ b/src/Refresh_Driver_D3D11_cdefines.h @@ -0,0 +1,215 @@ +/* Refresh - XNA-inspired 3D Graphics Library with modern capabilities + * + * Copyright (c) 2020 Evan Hemsley + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + * Evan "cosmonaut" Hemsley + * + */ + +/* Function Pointer Signatures */ +typedef HRESULT(WINAPI* PFN_CREATE_DXGI_FACTORY)(const GUID* riid, void** ppFactory); + + /* IIDs (from https://magnumdb.com) */ + +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 +{ + DXGI_FEATURE_PRESENT_ALLOW_TEARING = 0 +} DXGI_FEATURE; + +typedef enum +{ + DXGI_GPU_PREFERENCE_UNSPECIFIED = 0, + DXGI_GPU_PREFERENCE_MINIMUM_POWER = (DXGI_GPU_PREFERENCE_UNSPECIFIED + 1), + DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE = (DXGI_GPU_PREFERENCE_MINIMUM_POWER + 1) +} DXGI_GPU_PREFERENCE; + +typedef struct IDXGIFactory6 IDXGIFactory6; +typedef struct IDXGIFactory6Vtbl +{ + HRESULT(STDMETHODCALLTYPE* QueryInterface)( + IDXGIFactory6* This, + REFIID riid, + void** ppvObject); + + ULONG(STDMETHODCALLTYPE* AddRef)( + IDXGIFactory6* This); + + ULONG(STDMETHODCALLTYPE* Release)( + IDXGIFactory6* This); + + HRESULT(STDMETHODCALLTYPE* SetPrivateData)( + IDXGIFactory6* This, + REFGUID Name, + UINT DataSize, + const void* pData); + + HRESULT(STDMETHODCALLTYPE* SetPrivateDataInterface)( + IDXGIFactory6* This, + REFGUID Name, + const IUnknown* pUnknown); + + HRESULT(STDMETHODCALLTYPE* GetPrivateData)( + IDXGIFactory6* This, + REFGUID Name, + UINT* pDataSize, + void* pData); + + HRESULT(STDMETHODCALLTYPE* GetParent)( + IDXGIFactory6* This, + REFIID riid, + void** ppParent); + + HRESULT(STDMETHODCALLTYPE* EnumAdapters)( + IDXGIFactory6* This, + UINT Adapter, + IDXGIAdapter** ppAdapter); + + HRESULT(STDMETHODCALLTYPE* MakeWindowAssociation)( + IDXGIFactory6* This, + HWND WindowHandle, + UINT Flags); + + HRESULT(STDMETHODCALLTYPE* GetWindowAssociation)( + IDXGIFactory6* This, + HWND* pWindowHandle); + + HRESULT(STDMETHODCALLTYPE* CreateSwapChain)( + IDXGIFactory6* This, + IUnknown* pDevice, + DXGI_SWAP_CHAIN_DESC* pDesc, + IDXGISwapChain** ppSwapChain); + + HRESULT(STDMETHODCALLTYPE* CreateSoftwareAdapter)( + IDXGIFactory6* This, + HMODULE Module, + IDXGIAdapter** ppAdapter); + + HRESULT(STDMETHODCALLTYPE* EnumAdapters1)( + IDXGIFactory6* This, + UINT Adapter, + IDXGIAdapter1** ppAdapter); + + BOOL(STDMETHODCALLTYPE* IsCurrent)( + IDXGIFactory6* This); + + BOOL(STDMETHODCALLTYPE* IsWindowedStereoEnabled)( + IDXGIFactory6* This); + + HRESULT(STDMETHODCALLTYPE* CreateSwapChainForHwnd)( + IDXGIFactory6* This, + IUnknown* pDevice, + HWND hWnd, + void* pDesc, + void* pFullscreenDesc, + void* pRestrictToOutput, + void** ppSwapChain); + + HRESULT(STDMETHODCALLTYPE* CreateSwapChainForCoreWindow)( + IDXGIFactory6* This, + IUnknown* pDevice, + IUnknown* pWindow, + void* pDesc, + void* pRestrictToOutput, + void** ppSwapChain); + + HRESULT(STDMETHODCALLTYPE* GetSharedResourceAdapterLuid)( + IDXGIFactory6* This, + HANDLE hResource, + LUID* pLuid); + + HRESULT(STDMETHODCALLTYPE* RegisterStereoStatusWindow)( + IDXGIFactory6* This, + HWND WindowHandle, + UINT wMsg, + DWORD* pdwCookie); + + HRESULT(STDMETHODCALLTYPE* RegisterStereoStatusEvent)( + IDXGIFactory6* This, + HANDLE hEvent, + DWORD* pdwCookie); + + void (STDMETHODCALLTYPE* UnregisterStereoStatus)( + IDXGIFactory6* This, + DWORD dwCookie); + + HRESULT(STDMETHODCALLTYPE* RegisterOcclusionStatusWindow)( + IDXGIFactory6* This, + HWND WindowHandle, + UINT wMsg, + DWORD* pdwCookie); + + HRESULT(STDMETHODCALLTYPE* RegisterOcclusionStatusEvent)( + IDXGIFactory6* This, + HANDLE hEvent, + DWORD* pdwCookie); + + void (STDMETHODCALLTYPE* UnregisterOcclusionStatus)( + IDXGIFactory6* This, + DWORD dwCookie); + + HRESULT(STDMETHODCALLTYPE* CreateSwapChainForComposition)( + IDXGIFactory6* This, + IUnknown* pDevice, + void* pDesc, + void* pRestrictToOutput, + void** ppSwapChain); + + UINT(STDMETHODCALLTYPE* GetCreationFlags)( + IDXGIFactory6* This); + + HRESULT(STDMETHODCALLTYPE* EnumAdapterByLuid)( + IDXGIFactory6* This, + LUID AdapterLuid, + REFIID riid, + void** ppvAdapter); + + HRESULT(STDMETHODCALLTYPE* EnumWarpAdapter)( + IDXGIFactory6* This, + REFIID riid, + void** ppvAdapter); + + HRESULT(STDMETHODCALLTYPE* CheckFeatureSupport)( + IDXGIFactory6* This, + DXGI_FEATURE Feature, + void* pFeatureSupportData, + UINT FeatureSupportDataSize); + + HRESULT(STDMETHODCALLTYPE* EnumAdapterByGpuPreference)( + IDXGIFactory6* This, + UINT Adapter, + DXGI_GPU_PREFERENCE GpuPreference, + REFIID riid, + void** ppvAdapter); +} IDXGIFactory6Vtbl; + +struct IDXGIFactory6 +{ + struct IDXGIFactory6Vtbl* lpVtbl; +}; + +#define IDXGIFactory6_EnumAdapterByGpuPreference(This,Adapter,GpuPreference,riid,ppvAdapter) \ + ( (This)->lpVtbl -> EnumAdapterByGpuPreference(This,Adapter,GpuPreference,riid,ppvAdapter) ) diff --git a/src/Refresh_Driver_Vulkan.c b/src/Refresh_Driver_Vulkan.c index 72c2a5c..b5fb204 100644 --- a/src/Refresh_Driver_Vulkan.c +++ b/src/Refresh_Driver_Vulkan.c @@ -10642,4 +10642,8 @@ Refresh_Driver VulkanDriver = { VULKAN_CreateDevice }; +#else + +extern int this_tu_is_empty; + #endif //REFRESH_DRIVER_VULKAN diff --git a/visualc/Refresh.sln b/visualc/Refresh.sln index e69634a..0cec952 100644 --- a/visualc/Refresh.sln +++ b/visualc/Refresh.sln @@ -8,19 +8,29 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 MinSizeRel|x64 = MinSizeRel|x64 + MinSizeRel|x86 = MinSizeRel|x86 Release|x64 = Release|x64 + Release|x86 = Release|x86 RelWithDebInfo|x64 = RelWithDebInfo|x64 + RelWithDebInfo|x86 = RelWithDebInfo|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {6DB15344-E000-45CB-A48A-1D72F7D6E945}.Debug|x64.ActiveCfg = Debug|x64 {6DB15344-E000-45CB-A48A-1D72F7D6E945}.Debug|x64.Build.0 = Debug|x64 + {6DB15344-E000-45CB-A48A-1D72F7D6E945}.Debug|x86.ActiveCfg = Debug|Win32 + {6DB15344-E000-45CB-A48A-1D72F7D6E945}.Debug|x86.Build.0 = Debug|Win32 {6DB15344-E000-45CB-A48A-1D72F7D6E945}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64 {6DB15344-E000-45CB-A48A-1D72F7D6E945}.MinSizeRel|x64.Build.0 = MinSizeRel|x64 + {6DB15344-E000-45CB-A48A-1D72F7D6E945}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64 {6DB15344-E000-45CB-A48A-1D72F7D6E945}.Release|x64.ActiveCfg = Release|x64 {6DB15344-E000-45CB-A48A-1D72F7D6E945}.Release|x64.Build.0 = Release|x64 + {6DB15344-E000-45CB-A48A-1D72F7D6E945}.Release|x86.ActiveCfg = Release|Win32 + {6DB15344-E000-45CB-A48A-1D72F7D6E945}.Release|x86.Build.0 = Release|Win32 {6DB15344-E000-45CB-A48A-1D72F7D6E945}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64 {6DB15344-E000-45CB-A48A-1D72F7D6E945}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64 + {6DB15344-E000-45CB-A48A-1D72F7D6E945}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/visualc/Refresh.vcxproj b/visualc/Refresh.vcxproj index d73b03c..6a37b21 100644 --- a/visualc/Refresh.vcxproj +++ b/visualc/Refresh.vcxproj @@ -61,7 +61,9 @@ Level3 Disabled - REFRESH_DRIVER_VULKAN;%(PreprocessorDefinitions) + REFRESH_DRIVER_D3D11;REFRESH_DRIVER_VULKAN;%(PreprocessorDefinitions) + + DebugFull @@ -72,9 +74,11 @@ Level3 MaxSpeed - REFRESH_DRIVER_VULKAN;%(PreprocessorDefinitions) + REFRESH_DRIVER_D3D11;REFRESH_DRIVER_VULKAN;%(PreprocessorDefinitions) true true + + true @@ -84,6 +88,7 @@ + @@ -91,6 +96,7 @@ + diff --git a/visualc/Refresh.vcxproj.filters b/visualc/Refresh.vcxproj.filters index 67135bd..769e63a 100644 --- a/visualc/Refresh.vcxproj.filters +++ b/visualc/Refresh.vcxproj.filters @@ -10,6 +10,9 @@ Source Files + + Source Files + @@ -24,6 +27,9 @@ Header Files + + Header Files +