forked from MoonsideGames/Refresh
2235 lines
60 KiB
C
2235 lines
60 KiB
C
/* 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 <evan@moonside.games>
|
|
*
|
|
*/
|
|
|
|
#if REFRESH_DRIVER_D3D11
|
|
|
|
#define D3D11_NO_HELPERS
|
|
#define CINTERFACE
|
|
#define COBJMACROS
|
|
#include <d3d11.h>
|
|
#include <dxgi.h>
|
|
#include <d3dcompiler.h>
|
|
|
|
#include "Refresh_Driver.h"
|
|
#include "Refresh_Driver_D3D11_cdefines.h"
|
|
|
|
#include <SDL.h>
|
|
#include <SDL_syswm.h>
|
|
|
|
#include <spirv_cross_c.h>
|
|
|
|
/* Defines */
|
|
|
|
#define D3D11_DLL "d3d11.dll"
|
|
#define DXGI_DLL "dxgi.dll"
|
|
#define WINDOW_SWAPCHAIN_DATA "Refresh_D3D11Swapchain"
|
|
|
|
#define NOT_IMPLEMENTED SDL_assert(0 && "Not implemented!");
|
|
|
|
/* Macros */
|
|
|
|
#define ERROR_CHECK(msg) \
|
|
if (FAILED(res)) \
|
|
{ \
|
|
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_FLOAT, /* SINGLE */
|
|
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
|
|
{
|
|
spvc_context context;
|
|
size_t numEntryPoints;
|
|
const spvc_entry_point *entryPoints;
|
|
ID3D11DeviceChild **shaders; /* ID3D11VertexShader, ID3D11PixelShader, ID3D11ComputeShader */
|
|
ID3D10Blob **blobs;
|
|
} 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,
|
|
ID3D10Blob *shaderBlob
|
|
) {
|
|
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,
|
|
(void*) ID3D10Blob_GetBufferPointer(shaderBlob),
|
|
ID3D10Blob_GetBufferSize(shaderBlob),
|
|
&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;
|
|
int32_t vertexShaderIndex, fragmentShaderIndex;
|
|
int32_t i;
|
|
|
|
/* 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 + Input Layout */
|
|
vertexShaderIndex = -1;
|
|
for (i = 0; i < vertShaderModule->numEntryPoints; i += 1)
|
|
{
|
|
if ( vertShaderModule->entryPoints[i].execution_model == SpvExecutionModelVertex &&
|
|
SDL_strcmp(vertShaderModule->entryPoints[i].name, pipelineCreateInfo->vertexShaderInfo.entryPointName) == 0 )
|
|
{
|
|
vertexShaderIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
if (vertexShaderIndex == -1)
|
|
{
|
|
Refresh_LogError(
|
|
"Graphics pipeline creation failed! No entry point '%s' exists for the vertex shader!",
|
|
pipelineCreateInfo->vertexShaderInfo.entryPointName
|
|
);
|
|
return NULL;
|
|
}
|
|
|
|
pipeline->inputLayout = D3D11_INTERNAL_FetchInputLayout(
|
|
renderer,
|
|
pipelineCreateInfo->vertexInputState,
|
|
vertShaderModule->blobs[vertexShaderIndex]
|
|
);
|
|
pipeline->vertexShader = (ID3D11VertexShader*) vertShaderModule->shaders[vertexShaderIndex];
|
|
|
|
/* Fragment Shader */
|
|
fragmentShaderIndex = -1;
|
|
for (i = 0; i < fragShaderModule->numEntryPoints; i += 1)
|
|
{
|
|
if ( fragShaderModule->entryPoints[i].execution_model == SpvExecutionModelFragment &&
|
|
SDL_strcmp(fragShaderModule->entryPoints[i].name, pipelineCreateInfo->fragmentShaderInfo.entryPointName) == 0 )
|
|
{
|
|
fragmentShaderIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
if (fragmentShaderIndex == -1)
|
|
{
|
|
Refresh_LogError(
|
|
"Graphics pipeline creation failed! No entry point '%s' exists for the fragment shader!",
|
|
pipelineCreateInfo->vertexShaderInfo.entryPointName
|
|
);
|
|
return NULL;
|
|
}
|
|
|
|
pipeline->fragmentShader = (ID3D11PixelShader*) fragShaderModule->shaders[fragmentShaderIndex];
|
|
|
|
/* 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;
|
|
spvc_context context;
|
|
spvc_result result;
|
|
spvc_parsed_ir parsedIR;
|
|
spvc_compiler compiler;
|
|
const spvc_entry_point *entryPoints;
|
|
size_t numEntryPoints;
|
|
spvc_compiler_options compilerOptions;
|
|
char *hlslSource;
|
|
const char *shaderModel;
|
|
ID3DBlob **blobs;
|
|
ID3DBlob *errorBlob = NULL;
|
|
ID3D11DeviceChild **shaders;
|
|
D3D11ShaderModule *shaderModule;
|
|
uint32_t i;
|
|
HRESULT res;
|
|
|
|
/* Create the context */
|
|
result = spvc_context_create(&context);
|
|
if (result != SPVC_SUCCESS)
|
|
{
|
|
Refresh_LogError("Could not create SPIRV Cross context! Error: %X", result);
|
|
return NULL;
|
|
}
|
|
|
|
/* Parse the SPIRV input */
|
|
result = spvc_context_parse_spirv(
|
|
context,
|
|
shaderModuleCreateInfo->byteCode,
|
|
shaderModuleCreateInfo->codeSize / sizeof(uint32_t), /* word count, not byte length */
|
|
&parsedIR
|
|
);
|
|
if (result != SPVC_SUCCESS)
|
|
{
|
|
Refresh_LogError("Could not parse SPIRV! Error: %X", result);
|
|
spvc_context_destroy(context); /* free all context-related memory */
|
|
return NULL;
|
|
}
|
|
|
|
/* Create the cross compiler */
|
|
result = spvc_context_create_compiler(
|
|
context,
|
|
SPVC_BACKEND_HLSL,
|
|
parsedIR,
|
|
SPVC_CAPTURE_MODE_TAKE_OWNERSHIP,
|
|
&compiler
|
|
);
|
|
if (result != SPVC_SUCCESS)
|
|
{
|
|
Refresh_LogError("Could not create SPIRV to HLSL cross compiler! Error: %X", result);
|
|
spvc_context_destroy(context); /* free all context-related memory */
|
|
return NULL;
|
|
}
|
|
|
|
/* Get entry points from the source bytecode */
|
|
result = spvc_compiler_get_entry_points(compiler, &entryPoints, &numEntryPoints);
|
|
if (result != SPVC_SUCCESS)
|
|
{
|
|
Refresh_LogError("Could not get SPIRV entry points! Error: %X", result);
|
|
spvc_context_destroy(context); /* free all context-related memory */
|
|
return NULL;
|
|
}
|
|
|
|
/* Set HLSL cross-compile options (target SM5.0) */
|
|
spvc_compiler_create_compiler_options(compiler, &compilerOptions);
|
|
spvc_compiler_options_set_uint(
|
|
compilerOptions,
|
|
SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL,
|
|
50 /* shader model 5.0 */
|
|
);
|
|
spvc_compiler_options_set_bool(
|
|
compilerOptions,
|
|
SPVC_COMPILER_OPTION_FLIP_VERTEX_Y,
|
|
SPVC_TRUE
|
|
);
|
|
spvc_compiler_install_compiler_options(compiler, compilerOptions);
|
|
|
|
/* Cross compile to HLSL */
|
|
result = spvc_compiler_compile(compiler, &hlslSource);
|
|
if (result != SPVC_SUCCESS)
|
|
{
|
|
Refresh_LogError("Could not cross-compile SPIRV to HLSL! Error: %X", result);
|
|
spvc_context_destroy(context); /* free all context-related memory */
|
|
return NULL;
|
|
}
|
|
|
|
/* Allocate memory for the D3D11 shader list */
|
|
shaders = (ID3D11DeviceChild**) SDL_malloc(numEntryPoints * sizeof(ID3D11DeviceChild*));
|
|
blobs = (ID3D10Blob**) SDL_malloc(numEntryPoints * sizeof(ID3D10Blob*));
|
|
|
|
/* Compile each HLSL entry point into a D3D shader */
|
|
for (i = 0; i < numEntryPoints; i += 1)
|
|
{
|
|
/* Determine the exact shader model to use */
|
|
switch (entryPoints[i].execution_model)
|
|
{
|
|
case SpvExecutionModelVertex:
|
|
shaderModel = "vs_5_0";
|
|
break;
|
|
|
|
case SpvExecutionModelFragment:
|
|
shaderModel = "ps_5_0";
|
|
break;
|
|
|
|
case SpvExecutionModelGLCompute:
|
|
shaderModel = "cs_5_0";
|
|
break;
|
|
|
|
default:
|
|
Refresh_LogError(
|
|
"Attempting to compile a shader with an unknown execution model: %X",
|
|
entryPoints[i].execution_model
|
|
);
|
|
spvc_context_destroy(context); /* free all context-related memory */
|
|
SDL_free(shaders);
|
|
return NULL;
|
|
}
|
|
|
|
/* Compile the shader blob */
|
|
res = renderer->D3DCompileFunc(
|
|
hlslSource,
|
|
SDL_strlen(hlslSource),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
entryPoints[i].name,
|
|
shaderModel,
|
|
0,
|
|
0,
|
|
&blobs[i],
|
|
&errorBlob
|
|
);
|
|
if (FAILED(res))
|
|
{
|
|
Refresh_LogError(
|
|
"D3DCompile failed on HLSL shader with entry point '%s'! Error: %X\nCompiler error message: %s",
|
|
entryPoints[i].name,
|
|
res,
|
|
errorBlob ? (const char*) ID3D10Blob_GetBufferPointer(errorBlob) : "<none>"
|
|
);
|
|
spvc_context_destroy(context); /* free all context-related memory */
|
|
SDL_free(shaders);
|
|
return NULL;
|
|
}
|
|
|
|
/* Create the shader from the blob */
|
|
switch (entryPoints[i].execution_model)
|
|
{
|
|
case SpvExecutionModelVertex:
|
|
res = ID3D11Device_CreateVertexShader(
|
|
renderer->device,
|
|
ID3D10Blob_GetBufferPointer(blobs[i]),
|
|
ID3D10Blob_GetBufferSize(blobs[i]),
|
|
NULL,
|
|
(ID3D11VertexShader**) &shaders[i]
|
|
);
|
|
break;
|
|
|
|
case SpvExecutionModelFragment:
|
|
res = ID3D11Device_CreatePixelShader(
|
|
renderer->device,
|
|
ID3D10Blob_GetBufferPointer(blobs[i]),
|
|
ID3D10Blob_GetBufferSize(blobs[i]),
|
|
NULL,
|
|
(ID3D11PixelShader**) &shaders[i]
|
|
);
|
|
break;
|
|
|
|
case SpvExecutionModelGLCompute:
|
|
res = ID3D11Device_CreateComputeShader(
|
|
renderer->device,
|
|
ID3D10Blob_GetBufferPointer(blobs[i]),
|
|
ID3D10Blob_GetBufferSize(blobs[i]),
|
|
NULL,
|
|
(ID3D11ComputeShader**) &shaders[i]
|
|
);
|
|
break;
|
|
}
|
|
if (FAILED(res))
|
|
{
|
|
Refresh_LogError("D3D11 shader creation failed! Error code: %X", res);
|
|
spvc_context_destroy(context); /* free all context-related memory */
|
|
SDL_free(shaders);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Create the final shader module object to return */
|
|
shaderModule = (D3D11ShaderModule*) SDL_malloc(sizeof(D3D11ShaderModule));
|
|
shaderModule->context = context;
|
|
shaderModule->entryPoints = entryPoints;
|
|
shaderModule->numEntryPoints = numEntryPoints;
|
|
shaderModule->shaders = shaders;
|
|
shaderModule->blobs = blobs;
|
|
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;
|
|
uint32_t i;
|
|
|
|
/* Release the D3D11 shader objects and free the arrays */
|
|
for (i = 0; i < d3dShaderModule->numEntryPoints; i += 1)
|
|
{
|
|
ID3D11DeviceChild_Release(d3dShaderModule->shaders[i]);
|
|
ID3D10Blob_Release(d3dShaderModule->blobs[i]);
|
|
}
|
|
SDL_free(d3dShaderModule->shaders);
|
|
SDL_free(d3dShaderModule->blobs);
|
|
|
|
/* Destroy the SPIRV-Cross context.
|
|
* This should destroy the entryPoints list as well.
|
|
*/
|
|
spvc_context_destroy(d3dShaderModule->context);
|
|
}
|
|
|
|
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
|
|
};
|
|
|
|
#endif //REFRESH_DRIVER_D3D11
|