/* 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