Refresh/src/d3d11/Refresh_d3d11.c

7050 lines
205 KiB
C

/* Refresh - a cross-platform hardware-accelerated graphics library with modern capabilities
*
* Copyright (c) 2020-2024 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_D3D11
#define D3D11_NO_HELPERS
#define CINTERFACE
#define COBJMACROS
#include <d3d11.h>
#include <d3d11_1.h>
#include <dxgi.h>
#include <dxgi1_6.h>
#include <dxgidebug.h>
#include "SDL_gpu_driver.h"
#include <SDL_syswm.h>
/* MinGW doesn't implement this yet */
#ifdef _WIN32
#define HAVE_IDXGIINFOQUEUE
#endif
/* Function Pointer Signatures */
typedef HRESULT(WINAPI* PFN_CREATE_DXGI_FACTORY1)(const GUID* riid, void** ppFactory);
typedef HRESULT(WINAPI* PFN_DXGI_GET_DEBUG_INTERFACE)(const GUID* riid, void** ppDebug);
/* IIDs (from https://magnumdb.com) */
static const IID D3D_IID_IDXGIFactory1 = { 0x770aae78,0xf26f,0x4dba,{0xa8,0x29,0x25,0x3c,0x83,0xd1,0xb3,0x87} };
static const IID D3D_IID_IDXGIFactory4 = { 0x1bc6ea02,0xef36,0x464f,{0xbf,0x0c,0x21,0xca,0x39,0xe5,0x16,0x8a} };
static const IID D3D_IID_IDXGIFactory5 = { 0x7632e1f5,0xee65,0x4dca,{0x87,0xfd,0x84,0xcd,0x75,0xf8,0x83,0x8d} };
static const IID D3D_IID_IDXGIFactory6 = { 0xc1b6694f,0xff09,0x44a9,{0xb0,0x3c,0x77,0x90,0x0a,0x0a,0x1d,0x17} };
static const IID D3D_IID_IDXGIAdapter1 = { 0x29038f61,0x3839,0x4626,{0x91,0xfd,0x08,0x68,0x79,0x01,0x1a,0x05} };
static const IID D3D_IID_IDXGISwapChain3 = { 0x94d99bdb,0xf1f8,0x4ab0,{0xb2,0x36,0x7d,0xa0,0x17,0x0e,0xda,0xb1} };
static const IID D3D_IID_ID3D11Texture2D = { 0x6f15aaf2,0xd208,0x4e89,{0x9a,0xb4,0x48,0x95,0x35,0xd3,0x4f,0x9c} };
static const IID D3D_IID_ID3DUserDefinedAnnotation = { 0xb2daad8b,0x03d4,0x4dbf,{0x95,0xeb,0x32,0xab,0x4b,0x63,0xd0,0xab} };
static const IID D3D_IID_ID3D11Device1 = { 0xa04bfb29,0x08ef,0x43d6,{0xa4,0x9c,0xa9,0xbd,0xbd,0xcb,0xe6,0x86} };
static const IID D3D_IID_IDXGIDebug = { 0x119e7452,0xde9e,0x40fe,{0x88,0x06,0x88,0xf9,0x0c,0x12,0xb4,0x41} };
#ifdef HAVE_IDXGIINFOQUEUE
static const IID D3D_IID_IDXGIInfoQueue = { 0xd67441c7,0x672a,0x476f,{0x9e,0x82,0xcd,0x55,0xb4,0x49,0x49,0xce} };
#endif
static const GUID D3D_IID_D3DDebugObjectName = { 0x429b8c22, 0x9188, 0x4b0c, { 0x87, 0x42, 0xac, 0xb0, 0xbf, 0x85, 0xc2, 0x00 } };
static const GUID D3D_IID_DXGI_DEBUG_ALL = { 0xe48ae283,0xda80,0x490b,{0x87,0xe6,0x43,0xe9,0xa9,0xcf,0xda,0x08} };
/* Defines */
#if defined(_WIN32)
#define D3D11_DLL "d3d11.dll"
#define DXGI_DLL "dxgi.dll"
#define DXGIDEBUG_DLL "dxgidebug.dll"
#elif defined(__APPLE__)
#define D3D11_DLL "libdxvk_d3d11.dylib"
#define DXGI_DLL "libdxvk_dxgi.dylib"
#define DXGIDEBUG_DLL "libdxvk_dxgidebug.dylib"
#else
#define D3D11_DLL "libdxvk_d3d11.so"
#define DXGI_DLL "libdxvk_dxgi.so"
#define DXGIDEBUG_DLL "libdxvk_dxgidebug.so"
#endif
#define D3D11_CREATE_DEVICE_FUNC "D3D11CreateDevice"
#define CREATE_DXGI_FACTORY1_FUNC "CreateDXGIFactory1"
#define DXGI_GET_DEBUG_INTERFACE_FUNC "DXGIGetDebugInterface"
#define WINDOW_PROPERTY_DATA "Refresh_D3D11WindowPropertyData"
#define UNIFORM_BUFFER_SIZE 1048576 /* 1 MiB */
#ifdef _WIN32
#define HRESULT_FMT "(0x%08lX)"
#else
#define HRESULT_FMT "(0x%08X)"
#endif
/* Built-in shaders, compiled with compile_shaders.bat */
#define g_main D3D11_BlitFrom2D
#include "D3D11_BlitFrom2D.h"
#undef g_main
#define g_main D3D11_BlitFrom2DArray
#include "D3D11_BlitFrom2DArray.h"
#undef g_main
#define g_main D3D11_FullscreenVert
#include "D3D11_FullscreenVert.h"
#undef g_main
/* 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_ARRAY_IF_NEEDED(arr, elementType, newCount, capacity, newCapacity) \
if (newCount >= capacity) \
{ \
capacity = newCapacity; \
arr = (elementType*) SDL_realloc( \
arr, \
sizeof(elementType) * capacity \
); \
}
#define TRACK_RESOURCE(resource, type, array, count, capacity) \
Uint32 i; \
\
for (i = 0; i < commandBuffer->count; i += 1) \
{ \
if (commandBuffer->array[i] == resource) \
{ \
return; \
} \
} \
\
if (commandBuffer->count == commandBuffer->capacity) \
{ \
commandBuffer->capacity += 1; \
commandBuffer->array = SDL_realloc( \
commandBuffer->array, \
commandBuffer->capacity * sizeof(type) \
); \
} \
commandBuffer->array[commandBuffer->count] = resource; \
commandBuffer->count += 1; \
SDL_AtomicIncRef(&resource->referenceCount);
/* Forward Declarations */
static void D3D11_Wait(Refresh_Renderer *driverData);
static void D3D11_UnclaimWindow(
Refresh_Renderer * driverData,
SDL_Window *window
);
static void D3D11_INTERNAL_DestroyBlitPipelines(Refresh_Renderer *driverData);
/* Conversions */
static DXGI_FORMAT SwapchainCompositionToTextureFormat[] =
{
DXGI_FORMAT_B8G8R8A8_UNORM, /* SDR */
DXGI_FORMAT_B8G8R8A8_UNORM, /* SDR_SRGB */ /* NOTE: The RTV uses the sRGB format */
DXGI_FORMAT_R16G16B16A16_FLOAT, /* HDR */
DXGI_FORMAT_R10G10B10A2_UNORM, /* HDR_ADVANCED*/
};
static DXGI_COLOR_SPACE_TYPE SwapchainCompositionToColorSpace[] =
{
DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, /* SDR */
DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, /* SDR_SRGB */
DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709, /* HDR */
DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 /* HDR_ADVANCED */
};
static DXGI_FORMAT RefreshToD3D11_TextureFormat[] =
{
DXGI_FORMAT_R8G8B8A8_UNORM, /* R8G8B8A8 */
DXGI_FORMAT_B8G8R8A8_UNORM, /* B8G8R8A8 */
DXGI_FORMAT_B5G6R5_UNORM, /* R5G6B5 */ /* FIXME: Swizzle? */
DXGI_FORMAT_B5G5R5A1_UNORM, /* A1R5G5B5 */ /* FIXME: Swizzle? */
DXGI_FORMAT_B4G4R4A4_UNORM, /* B4G4R4A4 */
DXGI_FORMAT_R10G10B10A2_UNORM, /* A2R10G10B10 */
DXGI_FORMAT_UNKNOWN, /* A2B10G10R10 */ /* UNSUPPORTED BY D3D11 */
DXGI_FORMAT_R16G16_UNORM, /* R16G16 */
DXGI_FORMAT_R16G16B16A16_UNORM, /* R16G16B16A16 */
DXGI_FORMAT_R8_UNORM, /* R8 */
DXGI_FORMAT_A8_UNORM, /* A8 */
DXGI_FORMAT_BC1_UNORM, /* BC1 */
DXGI_FORMAT_BC2_UNORM, /* BC2 */
DXGI_FORMAT_BC3_UNORM, /* BC3 */
DXGI_FORMAT_BC7_UNORM, /* BC7 */
DXGI_FORMAT_R8G8_SNORM, /* R8G8_SNORM */
DXGI_FORMAT_R8G8B8A8_SNORM, /* R8G8B8A8_SNORM */
DXGI_FORMAT_R16_FLOAT, /* R16_SFLOAT */
DXGI_FORMAT_R16G16_FLOAT, /* R16G16_SFLOAT */
DXGI_FORMAT_R16G16B16A16_FLOAT, /* R16G16B16A16_SFLOAT */
DXGI_FORMAT_R32_FLOAT, /* R32_SFLOAT */
DXGI_FORMAT_R32G32_FLOAT, /* R32G32_SFLOAT */
DXGI_FORMAT_R32G32B32A32_FLOAT, /* R32G32B32A32_SFLOAT */
DXGI_FORMAT_R8_UINT, /* R8_UINT */
DXGI_FORMAT_R8G8_UINT, /* R8G8_UINT */
DXGI_FORMAT_R8G8B8A8_UINT, /* R8G8B8A8_UINT */
DXGI_FORMAT_R16_UINT, /* R16_UINT */
DXGI_FORMAT_R16G16_UINT, /* R16G16_UINT */
DXGI_FORMAT_R16G16B16A16_UINT, /* R16G16B16A16_UINT */
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, /* R8G8B8A8_SRGB */
DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, /* B8G8R8A8_SRGB */
DXGI_FORMAT_BC3_UNORM_SRGB, /* BC3_SRGB */
DXGI_FORMAT_BC7_UNORM_SRGB, /* BC7_SRGB */
DXGI_FORMAT_D16_UNORM, /* D16_UNORM */
DXGI_FORMAT_D24_UNORM_S8_UINT, /* D24_UNORM */
DXGI_FORMAT_D32_FLOAT, /* D32_SFLOAT */
DXGI_FORMAT_D24_UNORM_S8_UINT, /* D24_UNORM_S8_UINT */
DXGI_FORMAT_D32_FLOAT_S8X24_UINT, /* D32_SFLOAT_S8_UINT */
};
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 Uint32 RefreshToD3D11_SampleCount[] =
{
1, /* REFRESH_SAMPLECOUNT_1 */
2, /* REFRESH_SAMPLECOUNT_2 */
4, /* REFRESH_SAMPLECOUNT_4 */
8 /* REFRESH_SAMPLECOUNT_8 */
};
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_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 */
};
static D3D11_BLEND RefreshToD3D11_BlendFactorAlpha[] =
{
D3D11_BLEND_ZERO, /* ZERO */
D3D11_BLEND_ONE, /* ONE */
D3D11_BLEND_SRC_ALPHA, /* SRC_COLOR */
D3D11_BLEND_INV_SRC_ALPHA, /* ONE_MINUS_SRC_COLOR */
D3D11_BLEND_DEST_ALPHA, /* DST_COLOR */
D3D11_BLEND_INV_DEST_ALPHA, /* 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 */
};
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 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 */
};
static void RefreshToD3D11_BorderColor(
Refresh_SamplerCreateInfo *createInfo,
D3D11_SAMPLER_DESC *desc
) {
switch (createInfo->borderColor)
{
case REFRESH_BORDERCOLOR_FLOAT_OPAQUE_BLACK:
case REFRESH_BORDERCOLOR_INT_OPAQUE_BLACK:
desc->BorderColor[0] = 0.0f;
desc->BorderColor[1] = 0.0f;
desc->BorderColor[2] = 0.0f;
desc->BorderColor[3] = 1.0f;
break;
case REFRESH_BORDERCOLOR_FLOAT_OPAQUE_WHITE:
case REFRESH_BORDERCOLOR_INT_OPAQUE_WHITE:
desc->BorderColor[0] = 1.0f;
desc->BorderColor[1] = 1.0f;
desc->BorderColor[2] = 1.0f;
desc->BorderColor[3] = 1.0f;
break;
case REFRESH_BORDERCOLOR_FLOAT_TRANSPARENT_BLACK:
case REFRESH_BORDERCOLOR_INT_TRANSPARENT_BLACK:
desc->BorderColor[0] = 0.0f;
desc->BorderColor[1] = 0.0f;
desc->BorderColor[2] = 0.0f;
desc->BorderColor[3] = 0.0f;
break;
}
}
static D3D11_FILTER RefreshToD3D11_Filter(Refresh_SamplerCreateInfo *createInfo)
{
if (createInfo->minFilter == REFRESH_FILTER_LINEAR)
{
if (createInfo->magFilter == REFRESH_FILTER_LINEAR)
{
if (createInfo->mipmapMode == REFRESH_SAMPLERMIPMAPMODE_LINEAR)
{
return D3D11_FILTER_MIN_MAG_MIP_LINEAR;
}
else
{
return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
}
}
else
{
if (createInfo->mipmapMode == REFRESH_SAMPLERMIPMAPMODE_LINEAR)
{
return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
}
else
{
return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT;
}
}
}
else
{
if (createInfo->magFilter == REFRESH_FILTER_LINEAR)
{
if (createInfo->mipmapMode == REFRESH_SAMPLERMIPMAPMODE_LINEAR)
{
return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR;
}
else
{
return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
}
}
else
{
if (createInfo->mipmapMode == REFRESH_SAMPLERMIPMAPMODE_LINEAR)
{
return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR;
}
else
{
return D3D11_FILTER_MIN_MAG_MIP_POINT;
}
}
}
}
/* Structs */
typedef struct D3D11Texture D3D11Texture;
typedef struct D3D11TextureSubresource
{
D3D11Texture *parent;
Uint32 layer;
Uint32 level;
Uint32 index;
ID3D11RenderTargetView *colorTargetView; /* NULL if not a color target */
ID3D11DepthStencilView *depthStencilTargetView; /* NULL if not a depth stencil target */
ID3D11ShaderResourceView *srv; /* NULL if not a storage texture */
ID3D11UnorderedAccessView *uav; /* NULL if not a storage texture */
ID3D11Resource *msaaHandle; /* NULL if not using MSAA */
ID3D11RenderTargetView *msaaTargetView; /* NULL if not an MSAA color target */
SDL_atomic_t referenceCount;
} D3D11TextureSubresource;
struct D3D11Texture
{
/* D3D Handles */
ID3D11Resource *handle; /* ID3D11Texture2D* or ID3D11Texture3D* */
ID3D11ShaderResourceView *shaderView;
D3D11TextureSubresource *subresources;
Uint32 subresourceCount; /* layerCount * levelCount */
/* Basic Info */
Refresh_TextureFormat format;
Uint32 width;
Uint32 height;
Uint32 depth;
Uint32 levelCount;
Uint32 layerCount;
Uint8 isCube;
Uint8 isRenderTarget;
};
typedef struct D3D11TextureContainer
{
Refresh_TextureCreateInfo createInfo;
D3D11Texture *activeTexture;
Uint8 canBeCycled;
Uint32 textureCapacity;
Uint32 textureCount;
D3D11Texture **textures;
char *debugName;
} D3D11TextureContainer;
typedef struct D3D11Fence
{
ID3D11Query *handle;
SDL_atomic_t referenceCount;
} D3D11Fence;
typedef struct D3D11WindowData
{
SDL_Window *window;
IDXGISwapChain *swapchain;
D3D11Texture texture;
D3D11TextureContainer textureContainer;
Refresh_PresentMode presentMode;
Refresh_SwapchainComposition swapchainComposition;
DXGI_FORMAT swapchainFormat;
DXGI_COLOR_SPACE_TYPE swapchainColorSpace;
D3D11Fence *inFlightFences[MAX_FRAMES_IN_FLIGHT];
Uint32 frameCounter;
} D3D11WindowData;
typedef struct D3D11Shader
{
ID3D11DeviceChild *shader; /* ID3D11VertexShader, ID3D11PixelShader, ID3D11ComputeShader */
void* bytecode;
size_t bytecodeLength;
} D3D11Shader;
typedef struct D3D11GraphicsPipeline
{
float blendConstants[4];
Sint32 numColorAttachments;
DXGI_FORMAT colorAttachmentFormats[MAX_COLOR_TARGET_BINDINGS];
ID3D11BlendState *colorAttachmentBlendState;
Refresh_MultisampleState multisampleState;
Uint8 hasDepthStencilAttachment;
DXGI_FORMAT depthStencilAttachmentFormat;
ID3D11DepthStencilState *depthStencilState;
Uint32 stencilRef;
Refresh_PrimitiveType primitiveType;
ID3D11RasterizerState *rasterizerState;
ID3D11VertexShader *vertexShader;
ID3D11PixelShader *fragmentShader;
ID3D11InputLayout *inputLayout;
Uint32 *vertexStrides;
Uint32 vertexSamplerCount;
Uint32 vertexStorageTextureCount;
Uint32 vertexStorageBufferCount;
Uint32 vertexUniformBufferCount;
Uint32 fragmentSamplerCount;
Uint32 fragmentStorageTextureCount;
Uint32 fragmentStorageBufferCount;
Uint32 fragmentUniformBufferCount;
} D3D11GraphicsPipeline;
typedef struct D3D11ComputePipeline
{
ID3D11ComputeShader *computeShader;
Uint32 readOnlyStorageTextureCount;
Uint32 readWriteStorageTextureCount;
Uint32 readOnlyStorageBufferCount;
Uint32 readWriteStorageBufferCount;
Uint32 uniformBufferCount;
} D3D11ComputePipeline;
typedef struct D3D11Buffer
{
ID3D11Buffer *handle;
ID3D11UnorderedAccessView *uav;
ID3D11ShaderResourceView *srv;
Uint32 size;
SDL_atomic_t referenceCount;
} D3D11Buffer;
typedef struct D3D11BufferContainer
{
D3D11Buffer *activeBuffer;
Uint32 bufferCapacity;
Uint32 bufferCount;
D3D11Buffer **buffers;
D3D11_BUFFER_DESC bufferDesc;
char *debugName;
} D3D11BufferContainer;
typedef struct D3D11BufferTransfer
{
ID3D11Buffer *stagingBuffer;
} D3D11BufferTransfer;
typedef struct D3D11TextureTransfer
{
Uint8 *data;
/* TODO: can get rid of all of this by using a compute shader for texture-to-buffer copy */
ID3D11Resource *downloadTexture;
Uint32 downloadWidth;
Uint32 downloadHeight;
Uint32 downloadDepth;
Uint32 downloadBytesPerRow;
Uint32 downloadBytesPerDepthSlice;
SDL_bool downloadTightlyPacked;
} D3D11TextureTransfer;
typedef struct D3D11TransferBuffer
{
Uint32 size;
SDL_atomic_t referenceCount;
union
{
D3D11BufferTransfer bufferTransfer;
D3D11TextureTransfer textureTransfer;
};
} D3D11TransferBuffer;
typedef struct D3D11TransferBufferContainer
{
Refresh_TransferUsage usage;
Refresh_TransferBufferMapFlags mapFlags;
D3D11TransferBuffer *activeBuffer;
/* These are all the buffers that have been used by this container.
* If the resource is bound and then updated with DISCARD, a new resource
* will be added to this list.
* These can be reused after they are submitted and command processing is complete.
*/
Uint32 bufferCapacity;
Uint32 bufferCount;
D3D11TransferBuffer **buffers;
} D3D11TransferBufferContainer;
typedef struct D3D11UniformBuffer
{
D3D11BufferContainer *bufferContainer;
Uint32 drawOffset;
Uint32 offset;
Uint32 currentBlockSize;
} D3D11UniformBuffer;
typedef struct D3D11Renderer D3D11Renderer;
typedef struct D3D11CommandBuffer
{
CommandBufferCommonHeader common;
D3D11Renderer *renderer;
/* Deferred Context */
ID3D11DeviceContext1 *context;
/* Presentation */
D3D11WindowData **windowDatas;
Uint32 windowDataCount;
Uint32 windowDataCapacity;
/* Render Pass */
D3D11GraphicsPipeline *graphicsPipeline;
/* Render Pass MSAA resolve */
D3D11Texture *colorTargetResolveTexture[MAX_COLOR_TARGET_BINDINGS];
Uint32 colorTargetResolveSubresourceIndex[MAX_COLOR_TARGET_BINDINGS];
ID3D11Resource *colorTargetMsaaHandle[MAX_COLOR_TARGET_BINDINGS];
/* Compute Pass */
D3D11ComputePipeline *computePipeline;
/* Resource slot state */
SDL_bool needVertexSamplerBind;
SDL_bool needVertexResourceBind;
SDL_bool needFragmentSamplerBind;
SDL_bool needFragmentResourceBind;
SDL_bool needComputeUAVBind;
SDL_bool needComputeSRVBind;
ID3D11SamplerState *vertexSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
ID3D11ShaderResourceView *vertexShaderResourceViews[
MAX_TEXTURE_SAMPLERS_PER_STAGE +
MAX_STORAGE_BUFFERS_PER_STAGE +
MAX_STORAGE_TEXTURES_PER_STAGE
];
ID3D11SamplerState *fragmentSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
ID3D11ShaderResourceView *fragmentShaderResourceViews[
MAX_TEXTURE_SAMPLERS_PER_STAGE +
MAX_STORAGE_BUFFERS_PER_STAGE +
MAX_STORAGE_TEXTURES_PER_STAGE
];
ID3D11ShaderResourceView *computeShaderResourceViews[
MAX_STORAGE_TEXTURES_PER_STAGE +
MAX_STORAGE_BUFFERS_PER_STAGE
];
ID3D11UnorderedAccessView *computeUnorderedAccessViews[
MAX_STORAGE_TEXTURES_PER_STAGE +
MAX_STORAGE_BUFFERS_PER_STAGE
];
/* Uniform buffers */
D3D11UniformBuffer *vertexUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
D3D11UniformBuffer *fragmentUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
D3D11UniformBuffer *computeUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
SDL_bool vertexUniformBufferNeedsReset[MAX_UNIFORM_BUFFERS_PER_STAGE];
SDL_bool fragmentUniformBufferNeedsReset[MAX_UNIFORM_BUFFERS_PER_STAGE];
SDL_bool computeUniformBufferNeedsReset[MAX_UNIFORM_BUFFERS_PER_STAGE];
Uint32 initializedVertexUniformBufferCount;
Uint32 initializedFragmentUniformBufferCount;
Uint32 initializedComputeUniformBufferCount;
/* Fences */
D3D11Fence *fence;
Uint8 autoReleaseFence;
/* Reference Counting */
D3D11Buffer **usedBuffers;
Uint32 usedBufferCount;
Uint32 usedBufferCapacity;
D3D11TransferBuffer **usedTransferBuffers;
Uint32 usedTransferBufferCount;
Uint32 usedTransferBufferCapacity;
D3D11TextureSubresource **usedTextureSubresources;
Uint32 usedTextureSubresourceCount;
Uint32 usedTextureSubresourceCapacity;
} D3D11CommandBuffer;
typedef struct D3D11Sampler
{
ID3D11SamplerState *handle;
} D3D11Sampler;
typedef struct D3D11OcclusionQuery
{
ID3D11Query *handle;
} D3D11OcclusionQuery;
struct D3D11Renderer
{
ID3D11Device1 *device;
ID3D11DeviceContext *immediateContext;
IDXGIFactory1 *factory;
IDXGIAdapter1 *adapter;
IDXGIDebug *dxgiDebug;
#ifdef HAVE_IDXGIINFOQUEUE
IDXGIInfoQueue *dxgiInfoQueue;
#endif
void *d3d11_dll;
void *dxgi_dll;
void *dxgidebug_dll;
Uint8 debugMode;
BOOL supportsTearing;
Uint8 supportsFlipDiscard;
ID3DUserDefinedAnnotation *annotation;
SDL_iconv_t iconv;
/* Blit */
Refresh_Shader *fullscreenVertexShader;
Refresh_Shader *blitFrom2DPixelShader;
Refresh_Shader *blitFrom2DArrayPixelShader;
Refresh_GraphicsPipeline *blitFrom2DPipeline;
Refresh_GraphicsPipeline *blitFrom2DArrayPipeline; /* also cube */
Refresh_Sampler *blitNearestSampler;
Refresh_Sampler *blitLinearSampler;
/* Resource Tracking */
D3D11WindowData **claimedWindows;
Uint32 claimedWindowCount;
Uint32 claimedWindowCapacity;
D3D11CommandBuffer **availableCommandBuffers;
Uint32 availableCommandBufferCount;
Uint32 availableCommandBufferCapacity;
D3D11CommandBuffer **submittedCommandBuffers;
Uint32 submittedCommandBufferCount;
Uint32 submittedCommandBufferCapacity;
D3D11Fence **availableFences;
Uint32 availableFenceCount;
Uint32 availableFenceCapacity;
D3D11TransferBufferContainer **transferBufferContainersToDestroy;
Uint32 transferBufferContainersToDestroyCount;
Uint32 transferBufferContainersToDestroyCapacity;
D3D11BufferContainer **bufferContainersToDestroy;
Uint32 bufferContainersToDestroyCount;
Uint32 bufferContainersToDestroyCapacity;
D3D11TextureContainer **textureContainersToDestroy;
Uint32 textureContainersToDestroyCount;
Uint32 textureContainersToDestroyCapacity;
SDL_mutex *contextLock;
SDL_mutex *acquireCommandBufferLock;
SDL_mutex *fenceLock;
SDL_mutex *windowLock;
};
/* Null arrays for resetting shader resource slots */
ID3D11RenderTargetView *nullRTVs[
MAX_COLOR_TARGET_BINDINGS
];
ID3D11ShaderResourceView *nullSRVs[
MAX_TEXTURE_SAMPLERS_PER_STAGE +
MAX_STORAGE_TEXTURES_PER_STAGE +
MAX_STORAGE_BUFFERS_PER_STAGE
];
ID3D11SamplerState *nullSamplers[
MAX_TEXTURE_SAMPLERS_PER_STAGE
];
ID3D11UnorderedAccessView *nullUAVs[
MAX_STORAGE_TEXTURES_PER_STAGE +
MAX_STORAGE_BUFFERS_PER_STAGE
];
/* Logging */
static void D3D11_INTERNAL_LogError(
ID3D11Device1 *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. */
#ifdef _WIN32
dwChars = FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
res,
0,
wszMsgBuff,
MAX_ERROR_LEN,
NULL
);
#else
/* FIXME: Do we have error strings in dxvk-native? -flibit */
dwChars = 0;
#endif
/* No message? Screw it, just post the code. */
if (dwChars == 0)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s! Error Code: " HRESULT_FMT, 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';
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s! Error Code: %s " HRESULT_FMT, msg, wszMsgBuff, res);
}
/* Helper Functions */
static inline Uint32 D3D11_INTERNAL_CalcSubresource(
Uint32 mipLevel,
Uint32 arraySlice,
Uint32 numLevels
) {
return mipLevel + (arraySlice * numLevels);
}
static inline Uint32 D3D11_INTERNAL_NextHighestAlignment(
Uint32 n,
Uint32 align
) {
return align * ((n + align - 1) / align);
}
static DXGI_FORMAT D3D11_INTERNAL_GetTypelessFormat(
DXGI_FORMAT typedFormat
) {
switch (typedFormat)
{
case DXGI_FORMAT_D16_UNORM:
return DXGI_FORMAT_R16_TYPELESS;
case DXGI_FORMAT_D32_FLOAT:
return DXGI_FORMAT_R32_TYPELESS;
case DXGI_FORMAT_D24_UNORM_S8_UINT:
return DXGI_FORMAT_R24G8_TYPELESS;
case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
return DXGI_FORMAT_R32G8X24_TYPELESS;
default:
return DXGI_FORMAT_UNKNOWN;
}
}
static DXGI_FORMAT D3D11_INTERNAL_GetSampleableFormat(
DXGI_FORMAT format
) {
switch (format)
{
case DXGI_FORMAT_R16_TYPELESS:
return DXGI_FORMAT_R16_UNORM;
case DXGI_FORMAT_R32_TYPELESS:
return DXGI_FORMAT_R32_FLOAT;
case DXGI_FORMAT_R24G8_TYPELESS:
return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
case DXGI_FORMAT_R32G8X24_TYPELESS:
return DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
default:
return format;
}
}
/* Quit */
static void D3D11_INTERNAL_DestroyBufferContainer(
D3D11BufferContainer *container
) {
for (Uint32 i = 0; i < container->bufferCount; i += 1)
{
D3D11Buffer *d3d11Buffer = container->buffers[i];
if (d3d11Buffer->uav != NULL)
{
ID3D11UnorderedAccessView_Release(d3d11Buffer->uav);
}
if (d3d11Buffer->srv != NULL)
{
ID3D11ShaderResourceView_Release(d3d11Buffer->srv);
}
ID3D11Buffer_Release(d3d11Buffer->handle);
SDL_free(d3d11Buffer);
}
SDL_free(container->buffers);
SDL_free(container);
}
static void D3D11_DestroyDevice(
Refresh_Device *device
) {
D3D11Renderer *renderer = (D3D11Renderer*) device->driverData;
/* Flush any remaining GPU work... */
D3D11_Wait(device->driverData);
/* Release the window data */
for (Sint32 i = renderer->claimedWindowCount - 1; i >= 0; i -= 1)
{
D3D11_UnclaimWindow(device->driverData, renderer->claimedWindows[i]->window);
}
SDL_free(renderer->claimedWindows);
/* Release the blit resources */
D3D11_INTERNAL_DestroyBlitPipelines(device->driverData);
/* Release command buffer infrastructure */
for (Uint32 i = 0; i < renderer->availableCommandBufferCount; i += 1)
{
D3D11CommandBuffer *commandBuffer = renderer->availableCommandBuffers[i];
ID3D11DeviceContext_Release(commandBuffer->context);
SDL_free(commandBuffer->usedBuffers);
SDL_free(commandBuffer->usedTransferBuffers);
for (Uint32 j = 0; j < commandBuffer->initializedVertexUniformBufferCount; j += 1)
{
D3D11_INTERNAL_DestroyBufferContainer(
commandBuffer->vertexUniformBuffers[j]->bufferContainer
);
SDL_free(commandBuffer->vertexUniformBuffers[j]);
}
for (Uint32 j = 0; j < commandBuffer->initializedFragmentUniformBufferCount; j += 1)
{
D3D11_INTERNAL_DestroyBufferContainer(
commandBuffer->fragmentUniformBuffers[j]->bufferContainer
);
SDL_free(commandBuffer->fragmentUniformBuffers[j]);
}
for (Uint32 j = 0; j < commandBuffer->initializedComputeUniformBufferCount; j += 1)
{
D3D11_INTERNAL_DestroyBufferContainer(
commandBuffer->computeUniformBuffers[j]->bufferContainer
);
SDL_free(commandBuffer->computeUniformBuffers[j]);
}
SDL_free(commandBuffer);
}
SDL_free(renderer->availableCommandBuffers);
SDL_free(renderer->submittedCommandBuffers);
/* Release fence infrastructure */
for (Uint32 i = 0; i < renderer->availableFenceCount; i += 1)
{
D3D11Fence *fence = renderer->availableFences[i];
ID3D11Query_Release(fence->handle);
SDL_free(fence);
}
SDL_free(renderer->availableFences);
/* Release the annotation/iconv, if applicable */
if (renderer->annotation != NULL)
{
ID3DUserDefinedAnnotation_Release(renderer->annotation);
}
if (renderer->iconv != NULL)
{
SDL_iconv_close(renderer->iconv);
}
/* Release the mutexes */
SDL_DestroyMutex(renderer->acquireCommandBufferLock);
SDL_DestroyMutex(renderer->contextLock);
SDL_DestroyMutex(renderer->fenceLock);
SDL_DestroyMutex(renderer->windowLock);
/* Release the device and associated objects */
ID3D11DeviceContext_Release(renderer->immediateContext);
ID3D11Device_Release(renderer->device);
IDXGIAdapter_Release(renderer->adapter);
IDXGIFactory_Release(renderer->factory);
/* Report leaks and clean up debug objects */
if (renderer->dxgiDebug)
{
IDXGIDebug_ReportLiveObjects(
renderer->dxgiDebug,
D3D_IID_DXGI_DEBUG_ALL,
DXGI_DEBUG_RLO_SUMMARY | DXGI_DEBUG_RLO_DETAIL
);
IDXGIDebug_Release(renderer->dxgiDebug);
}
#ifdef HAVE_IDXGIINFOQUEUE
if (renderer->dxgiInfoQueue)
{
IDXGIInfoQueue_Release(renderer->dxgiInfoQueue);
}
#endif
/* Release the DLLs */
SDL_UnloadObject(renderer->d3d11_dll);
SDL_UnloadObject(renderer->dxgi_dll);
if (renderer->dxgidebug_dll)
{
SDL_UnloadObject(renderer->dxgidebug_dll);
}
/* Free the primary structures */
SDL_free(renderer);
SDL_free(device);
}
/* Resource tracking */
static void D3D11_INTERNAL_TrackBuffer(
D3D11CommandBuffer *commandBuffer,
D3D11Buffer *buffer
) {
TRACK_RESOURCE(
buffer,
D3D11Buffer*,
usedBuffers,
usedBufferCount,
usedBufferCapacity
);
}
static void D3D11_INTERNAL_TrackTransferBuffer(
D3D11CommandBuffer *commandBuffer,
D3D11TransferBuffer *buffer
) {
TRACK_RESOURCE(
buffer,
D3D11TransferBuffer*,
usedTransferBuffers,
usedTransferBufferCount,
usedTransferBufferCapacity
);
}
static void D3D11_INTERNAL_TrackTextureSubresource(
D3D11CommandBuffer *commandBuffer,
D3D11TextureSubresource *textureSubresource
) {
TRACK_RESOURCE(
textureSubresource,
D3D11TextureSubresource*,
usedTextureSubresources,
usedTextureSubresourceCount,
usedTextureSubresourceCapacity
);
}
/* Disposal */
static void D3D11_INTERNAL_DestroyTextureContainer(
D3D11TextureContainer *container
) {
for (Uint32 i = 0; i < container->textureCount; i += 1)
{
D3D11Texture *d3d11Texture = container->textures[i];
if (d3d11Texture->shaderView)
{
ID3D11ShaderResourceView_Release(d3d11Texture->shaderView);
}
for (Uint32 subresourceIndex = 0; subresourceIndex < d3d11Texture->subresourceCount; subresourceIndex += 1)
{
if (d3d11Texture->subresources[subresourceIndex].msaaHandle != NULL)
{
ID3D11Resource_Release(d3d11Texture->subresources[subresourceIndex].msaaHandle);
}
if (d3d11Texture->subresources[subresourceIndex].msaaTargetView != NULL)
{
ID3D11RenderTargetView_Release(d3d11Texture->subresources[subresourceIndex].msaaTargetView);
}
if (d3d11Texture->subresources[subresourceIndex].colorTargetView != NULL)
{
ID3D11RenderTargetView_Release(d3d11Texture->subresources[subresourceIndex].colorTargetView);
}
if (d3d11Texture->subresources[subresourceIndex].depthStencilTargetView != NULL)
{
ID3D11DepthStencilView_Release(d3d11Texture->subresources[subresourceIndex].depthStencilTargetView);
}
if (d3d11Texture->subresources[subresourceIndex].srv != NULL)
{
ID3D11ShaderResourceView_Release(d3d11Texture->subresources[subresourceIndex].srv);
}
if (d3d11Texture->subresources[subresourceIndex].uav != NULL)
{
ID3D11UnorderedAccessView_Release(d3d11Texture->subresources[subresourceIndex].uav);
}
}
SDL_free(d3d11Texture->subresources);
ID3D11Resource_Release(d3d11Texture->handle);
}
SDL_free(container->textures);
SDL_free(container);
}
static void D3D11_ReleaseTexture(
Refresh_Renderer *driverData,
Refresh_Texture *texture
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11TextureContainer *container = (D3D11TextureContainer*) texture;
SDL_LockMutex(renderer->contextLock);
EXPAND_ARRAY_IF_NEEDED(
renderer->textureContainersToDestroy,
D3D11TextureContainer*,
renderer->textureContainersToDestroyCount + 1,
renderer->textureContainersToDestroyCapacity,
renderer->textureContainersToDestroyCapacity + 1
);
renderer->textureContainersToDestroy[
renderer->textureContainersToDestroyCount
] = container;
renderer->textureContainersToDestroyCount += 1;
SDL_UnlockMutex(renderer->contextLock);
}
static void D3D11_ReleaseSampler(
Refresh_Renderer *driverData,
Refresh_Sampler *sampler
) {
(void) driverData; /* used by other backends */
D3D11Sampler *d3d11Sampler = (D3D11Sampler*) sampler;
ID3D11SamplerState_Release(d3d11Sampler->handle);
SDL_free(d3d11Sampler);
}
static void D3D11_ReleaseBuffer(
Refresh_Renderer *driverData,
Refresh_Buffer *buffer
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11BufferContainer *container = (D3D11BufferContainer*) buffer;
SDL_LockMutex(renderer->contextLock);
EXPAND_ARRAY_IF_NEEDED(
renderer->bufferContainersToDestroy,
D3D11BufferContainer*,
renderer->bufferContainersToDestroyCount + 1,
renderer->bufferContainersToDestroyCapacity,
renderer->bufferContainersToDestroyCapacity + 1
);
renderer->bufferContainersToDestroy[
renderer->bufferContainersToDestroyCount
] = container;
renderer->bufferContainersToDestroyCount += 1;
SDL_UnlockMutex(renderer->contextLock);
}
static void D3D11_ReleaseTransferBuffer(
Refresh_Renderer *driverData,
Refresh_TransferBuffer *transferBuffer
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
SDL_LockMutex(renderer->contextLock);
EXPAND_ARRAY_IF_NEEDED(
renderer->transferBufferContainersToDestroy,
D3D11TransferBufferContainer*,
renderer->transferBufferContainersToDestroyCount + 1,
renderer->transferBufferContainersToDestroyCapacity,
renderer->transferBufferContainersToDestroyCapacity + 1
);
renderer->transferBufferContainersToDestroy[
renderer->transferBufferContainersToDestroyCount
] = (D3D11TransferBufferContainer*) transferBuffer;
renderer->transferBufferContainersToDestroyCount += 1;
SDL_UnlockMutex(renderer->contextLock);
}
static void D3D11_INTERNAL_DestroyTransferBufferContainer(
D3D11TransferBufferContainer *transferBufferContainer
) {
for (Uint32 i = 0; i < transferBufferContainer->bufferCount; i += 1)
{
if (transferBufferContainer->usage == REFRESH_TRANSFERUSAGE_BUFFER)
{
ID3D11Buffer_Release(transferBufferContainer->buffers[i]->bufferTransfer.stagingBuffer);
}
else /* TEXTURE */
{
SDL_free(transferBufferContainer->buffers[i]->textureTransfer.data);
if (transferBufferContainer->buffers[i]->textureTransfer.downloadTexture != NULL)
{
ID3D11Resource_Release(transferBufferContainer->buffers[i]->textureTransfer.downloadTexture);
}
}
SDL_free(transferBufferContainer->buffers[i]);
}
SDL_free(transferBufferContainer->buffers);
}
static void D3D11_ReleaseShader(
Refresh_Renderer *driverData,
Refresh_Shader *shader
) {
(void) driverData; /* used by other backends */
D3D11Shader *d3dShader = (D3D11Shader*) shader;
if (d3dShader->shader)
{
ID3D11DeviceChild_Release(d3dShader->shader);
}
if (d3dShader->bytecode)
{
SDL_free(d3dShader->bytecode);
}
SDL_free(d3dShader);
}
static void D3D11_ReleaseComputePipeline(
Refresh_Renderer *driverData,
Refresh_ComputePipeline *computePipeline
) {
D3D11ComputePipeline *d3d11ComputePipeline = (D3D11ComputePipeline*) computePipeline;
ID3D11ComputeShader_Release(d3d11ComputePipeline->computeShader);
/* TODO: teardown resource layout structure */
SDL_free(d3d11ComputePipeline);
}
static void D3D11_ReleaseGraphicsPipeline(
Refresh_Renderer *driverData,
Refresh_GraphicsPipeline *graphicsPipeline
) {
(void) driverData; /* used by other backends */
D3D11GraphicsPipeline *d3d11GraphicsPipeline = (D3D11GraphicsPipeline*) graphicsPipeline;
ID3D11BlendState_Release(d3d11GraphicsPipeline->colorAttachmentBlendState);
ID3D11DepthStencilState_Release(d3d11GraphicsPipeline->depthStencilState);
ID3D11RasterizerState_Release(d3d11GraphicsPipeline->rasterizerState);
if (d3d11GraphicsPipeline->inputLayout)
{
ID3D11InputLayout_Release(d3d11GraphicsPipeline->inputLayout);
}
if (d3d11GraphicsPipeline->vertexStrides)
{
SDL_free(d3d11GraphicsPipeline->vertexStrides);
}
ID3D11VertexShader_Release(d3d11GraphicsPipeline->vertexShader);
ID3D11PixelShader_Release(d3d11GraphicsPipeline->fragmentShader);
/* TODO: teardown resource layout structure */
SDL_free(d3d11GraphicsPipeline);
}
static void D3D11_ReleaseOcclusionQuery(
Refresh_Renderer *renderer,
Refresh_OcclusionQuery *query
) {
D3D11OcclusionQuery *d3dQuery = (D3D11OcclusionQuery*) query;
ID3D11Query_Release(d3dQuery->handle);
SDL_free(query);
}
/* State Creation */
static ID3D11BlendState* D3D11_INTERNAL_FetchBlendState(
D3D11Renderer *renderer,
Uint32 numColorAttachments,
Refresh_ColorAttachmentDescription *colorAttachments
) {
ID3D11BlendState *result;
D3D11_BLEND_DESC blendDesc;
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 (Uint32 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_BlendFactorAlpha[
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_BlendFactorAlpha[
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 = RefreshToD3D11_CompareOp[depthStencilState.compareOp];
dsDesc.DepthWriteMask = (
depthStencilState.depthWriteEnable ?
D3D11_DEPTH_WRITE_MASK_ALL :
D3D11_DEPTH_WRITE_MASK_ZERO
);
dsDesc.BackFace.StencilFunc = RefreshToD3D11_CompareOp[depthStencilState.backStencilState.compareOp];
dsDesc.BackFace.StencilDepthFailOp = RefreshToD3D11_StencilOp[depthStencilState.backStencilState.depthFailOp];
dsDesc.BackFace.StencilFailOp = RefreshToD3D11_StencilOp[depthStencilState.backStencilState.failOp];
dsDesc.BackFace.StencilPassOp = RefreshToD3D11_StencilOp[depthStencilState.backStencilState.passOp];
dsDesc.FrontFace.StencilFunc = RefreshToD3D11_CompareOp[depthStencilState.frontStencilState.compareOp];
dsDesc.FrontFace.StencilDepthFailOp = RefreshToD3D11_StencilOp[depthStencilState.frontStencilState.depthFailOp];
dsDesc.FrontFace.StencilFailOp = RefreshToD3D11_StencilOp[depthStencilState.frontStencilState.failOp];
dsDesc.FrontFace.StencilPassOp = RefreshToD3D11_StencilOp[depthStencilState.frontStencilState.passOp];
dsDesc.StencilReadMask = depthStencilState.compareMask;
dsDesc.StencilWriteMask = depthStencilState.writeMask;
if (depthStencilState.depthBoundsTestEnable)
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D11 does not support Depth Bounds tests!");
}
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 = SDL_lroundf(rasterizerState.depthBiasConstantFactor);
rasterizerDesc.DepthBiasClamp = rasterizerState.depthBiasClamp;
rasterizerDesc.DepthClipEnable = TRUE;
rasterizerDesc.FillMode = (rasterizerState.fillMode == REFRESH_FILLMODE_FILL) ? D3D11_FILL_SOLID : D3D11_FILL_WIREFRAME;
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 D3D11_INTERNAL_FindIndexOfVertexBinding(
Uint32 targetBinding,
const Refresh_VertexBinding *bindings,
Uint32 numBindings
) {
for (Uint32 i = 0; i < numBindings; i += 1)
{
if (bindings[i].binding == targetBinding)
{
return i;
}
}
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not find vertex binding %u!", 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 bindingIndex;
HRESULT res;
/* Don't bother creating/fetching an input layout if there are no attributes. */
if (inputState.vertexAttributeCount == 0)
{
return NULL;
}
/* Allocate an array of vertex elements */
elementDescs = SDL_stack_alloc(
D3D11_INPUT_ELEMENT_DESC,
inputState.vertexAttributeCount
);
/* Create the array of input elements */
for (Uint32 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 = (
inputState.vertexBindings[bindingIndex].stepRate > 0 ?
inputState.vertexBindings[bindingIndex].stepRate :
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))
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not create input layout! Error: " HRESULT_FMT, 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;
}
/* Pipeline Creation */
static Refresh_ComputePipeline* D3D11_CreateComputePipeline(
Refresh_Renderer *driverData,
Refresh_ComputePipelineCreateInfo *pipelineCreateInfo
) {
(void) driverData; /* used by other backends */
D3D11Shader *shader = (D3D11Shader*) pipelineCreateInfo->computeShader;
D3D11ComputePipeline *pipeline = SDL_malloc(sizeof(D3D11ComputePipeline));
pipeline->computeShader = (ID3D11ComputeShader*) shader->shader;
ID3D11ComputeShader_AddRef(pipeline->computeShader);
pipeline->readOnlyStorageTextureCount = pipelineCreateInfo->pipelineResourceInfo.readOnlyStorageTextureCount;
pipeline->readWriteStorageTextureCount = pipelineCreateInfo->pipelineResourceInfo.readWriteStorageTextureCount;
pipeline->readOnlyStorageBufferCount = pipelineCreateInfo->pipelineResourceInfo.readOnlyStorageBufferCount;
pipeline->readWriteStorageBufferCount = pipelineCreateInfo->pipelineResourceInfo.readWriteStorageBufferCount;
pipeline->uniformBufferCount = pipelineCreateInfo->pipelineResourceInfo.uniformBufferCount;
return (Refresh_ComputePipeline*) pipeline;
}
static Refresh_GraphicsPipeline* D3D11_CreateGraphicsPipeline(
Refresh_Renderer *driverData,
Refresh_GraphicsPipelineCreateInfo *pipelineCreateInfo
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11Shader *vertShader = (D3D11Shader*) pipelineCreateInfo->vertexShader;
D3D11Shader *fragShader = (D3D11Shader*) pipelineCreateInfo->fragmentShader;
D3D11GraphicsPipeline *pipeline = SDL_malloc(sizeof(D3D11GraphicsPipeline));
Uint32 i;
/* Blend */
pipeline->colorAttachmentBlendState = D3D11_INTERNAL_FetchBlendState(
renderer,
pipelineCreateInfo->attachmentInfo.colorAttachmentCount,
pipelineCreateInfo->attachmentInfo.colorAttachmentDescriptions
);
pipeline->numColorAttachments = pipelineCreateInfo->attachmentInfo.colorAttachmentCount;
for (i = 0; i < pipeline->numColorAttachments; i += 1)
{
pipeline->colorAttachmentFormats[i] = RefreshToD3D11_TextureFormat[
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];
/* Multisample */
pipeline->multisampleState = pipelineCreateInfo->multisampleState;
/* Depth-Stencil */
pipeline->depthStencilState = D3D11_INTERNAL_FetchDepthStencilState(
renderer,
pipelineCreateInfo->depthStencilState
);
pipeline->hasDepthStencilAttachment = pipelineCreateInfo->attachmentInfo.hasDepthStencilAttachment;
pipeline->depthStencilAttachmentFormat = RefreshToD3D11_TextureFormat[
pipelineCreateInfo->attachmentInfo.depthStencilFormat
];
pipeline->stencilRef = pipelineCreateInfo->depthStencilState.reference;
/* Rasterizer */
pipeline->primitiveType = pipelineCreateInfo->primitiveType;
pipeline->rasterizerState = D3D11_INTERNAL_FetchRasterizerState(
renderer,
pipelineCreateInfo->rasterizerState
);
/* Shaders */
pipeline->vertexShader = (ID3D11VertexShader*) vertShader->shader;
ID3D11VertexShader_AddRef(pipeline->vertexShader);
pipeline->fragmentShader = (ID3D11PixelShader*) fragShader->shader;
ID3D11PixelShader_AddRef(pipeline->fragmentShader);
/* Input Layout */
pipeline->inputLayout = D3D11_INTERNAL_FetchInputLayout(
renderer,
pipelineCreateInfo->vertexInputState,
vertShader->bytecode,
vertShader->bytecodeLength
);
if (pipelineCreateInfo->vertexInputState.vertexBindingCount > 0)
{
pipeline->vertexStrides = SDL_malloc(
sizeof(Uint32) *
pipelineCreateInfo->vertexInputState.vertexBindingCount
);
for (i = 0; i < pipelineCreateInfo->vertexInputState.vertexBindingCount; i += 1)
{
pipeline->vertexStrides[i] = pipelineCreateInfo->vertexInputState.vertexBindings[i].stride;
}
}
else
{
pipeline->vertexStrides = NULL;
}
/* Resource layout */
pipeline->vertexSamplerCount = pipelineCreateInfo->vertexResourceInfo.samplerCount;
pipeline->vertexStorageTextureCount = pipelineCreateInfo->vertexResourceInfo.storageTextureCount;
pipeline->vertexStorageBufferCount = pipelineCreateInfo->vertexResourceInfo.storageBufferCount;
pipeline->vertexUniformBufferCount = pipelineCreateInfo->vertexResourceInfo.uniformBufferCount;
pipeline->fragmentSamplerCount = pipelineCreateInfo->fragmentResourceInfo.samplerCount;
pipeline->fragmentStorageTextureCount = pipelineCreateInfo->fragmentResourceInfo.storageTextureCount;
pipeline->fragmentStorageBufferCount = pipelineCreateInfo->fragmentResourceInfo.storageBufferCount;
pipeline->fragmentUniformBufferCount = pipelineCreateInfo->fragmentResourceInfo.uniformBufferCount;
return (Refresh_GraphicsPipeline*) pipeline;
}
/* Debug Naming */
static void D3D11_INTERNAL_SetBufferName(
D3D11Renderer *renderer,
D3D11Buffer *buffer,
const char *text
) {
if (renderer->debugMode)
{
ID3D11DeviceChild_SetPrivateData(
buffer->handle,
&D3D_IID_D3DDebugObjectName,
(UINT) SDL_strlen(text),
text
);
}
}
static void D3D11_SetBufferName(
Refresh_Renderer *driverData,
Refresh_Buffer *buffer,
const char *text
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11BufferContainer *container = (D3D11BufferContainer*) buffer;
if (renderer->debugMode)
{
container->debugName = SDL_realloc(
container->debugName,
SDL_strlen(text) + 1
);
SDL_utf8strlcpy(
container->debugName,
text,
SDL_strlen(text) + 1
);
for (Uint32 i = 0; i < container->bufferCount; i += 1)
{
D3D11_INTERNAL_SetBufferName(
renderer,
container->buffers[i],
text
);
}
}
}
static void D3D11_INTERNAL_SetTextureName(
D3D11Renderer *renderer,
D3D11Texture *texture,
const char *text
) {
if (renderer->debugMode)
{
ID3D11DeviceChild_SetPrivateData(
texture->handle,
&D3D_IID_D3DDebugObjectName,
(UINT) SDL_strlen(text),
text
);
}
}
static void D3D11_SetTextureName(
Refresh_Renderer *driverData,
Refresh_Texture *texture,
const char *text
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11TextureContainer *container = (D3D11TextureContainer*) texture;
if (renderer->debugMode)
{
container->debugName = SDL_realloc(
container->debugName,
SDL_strlen(text) + 1
);
SDL_utf8strlcpy(
container->debugName,
text,
SDL_strlen(text) + 1
);
for (Uint32 i = 0; i < container->textureCount; i += 1)
{
D3D11_INTERNAL_SetTextureName(
renderer,
container->textures[i],
text
);
}
}
}
static void D3D11_SetStringMarker(
Refresh_CommandBuffer *commandBuffer,
const char *text
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer;
if (renderer->annotation == NULL)
{
return;
}
wchar_t wstr[256];
wchar_t *out = wstr;
size_t inlen, outlen, result;
if (renderer->iconv == NULL)
{
renderer->iconv = SDL_iconv_open("WCHAR_T", "UTF-8");
SDL_assert(renderer->iconv);
}
/* Convert... */
inlen = SDL_strlen(text) + 1;
outlen = sizeof(wstr);
result = SDL_iconv(
renderer->iconv,
&text,
&inlen,
(char**) &out,
&outlen
);
/* Check... */
switch (result)
{
case SDL_ICONV_ERROR:
case SDL_ICONV_E2BIG:
case SDL_ICONV_EILSEQ:
case SDL_ICONV_EINVAL:
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to convert string marker to wchar_t!");
return;
default:
break;
}
/* Mark, finally. */
ID3DUserDefinedAnnotation_SetMarker(renderer->annotation, wstr);
}
/* Resource Creation */
static Refresh_Sampler* D3D11_CreateSampler(
Refresh_Renderer *driverData,
Refresh_SamplerCreateInfo *samplerCreateInfo
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11_SAMPLER_DESC samplerDesc;
ID3D11SamplerState *samplerStateHandle;
D3D11Sampler *d3d11Sampler;
HRESULT res;
samplerDesc.AddressU = RefreshToD3D11_SamplerAddressMode[samplerCreateInfo->addressModeU];
samplerDesc.AddressV = RefreshToD3D11_SamplerAddressMode[samplerCreateInfo->addressModeV];
samplerDesc.AddressW = RefreshToD3D11_SamplerAddressMode[samplerCreateInfo->addressModeW];
RefreshToD3D11_BorderColor(
samplerCreateInfo,
&samplerDesc
);
samplerDesc.ComparisonFunc = (
samplerCreateInfo->compareEnable ?
RefreshToD3D11_CompareOp[samplerCreateInfo->compareOp] :
RefreshToD3D11_CompareOp[REFRESH_COMPAREOP_ALWAYS]
);
samplerDesc.MaxAnisotropy = (
samplerCreateInfo->anisotropyEnable ?
(UINT) samplerCreateInfo->maxAnisotropy :
0
);
samplerDesc.Filter = RefreshToD3D11_Filter(samplerCreateInfo);
samplerDesc.MaxLOD = samplerCreateInfo->maxLod;
samplerDesc.MinLOD = samplerCreateInfo->minLod;
samplerDesc.MipLODBias = samplerCreateInfo->mipLodBias;
res = ID3D11Device_CreateSamplerState(
renderer->device,
&samplerDesc,
&samplerStateHandle
);
ERROR_CHECK_RETURN("Could not create sampler state", NULL);
d3d11Sampler = (D3D11Sampler*) SDL_malloc(sizeof(D3D11Sampler));
d3d11Sampler->handle = samplerStateHandle;
return (Refresh_Sampler*) d3d11Sampler;
}
Refresh_Shader* D3D11_CreateShader(
Refresh_Renderer *driverData,
Refresh_ShaderCreateInfo *shaderCreateInfo
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11Shader* shaderModule;
Refresh_ShaderStage shaderStage = shaderCreateInfo->stage;
ID3D11DeviceChild *shader = NULL;
HRESULT res;
if (shaderCreateInfo->format != REFRESH_SHADERFORMAT_DXBC)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Incompatible shader format for D3D11");
return NULL;
}
/* Create the shader from the byte blob */
if (shaderStage == REFRESH_SHADERSTAGE_VERTEX)
{
res = ID3D11Device_CreateVertexShader(
renderer->device,
shaderCreateInfo->code,
shaderCreateInfo->codeSize,
NULL,
(ID3D11VertexShader**) &shader
);
if (FAILED(res))
{
D3D11_INTERNAL_LogError(renderer->device, "Could not create vertex shader", res);
return NULL;
}
}
else if (shaderStage == REFRESH_SHADERSTAGE_FRAGMENT)
{
res = ID3D11Device_CreatePixelShader(
renderer->device,
shaderCreateInfo->code,
shaderCreateInfo->codeSize,
NULL,
(ID3D11PixelShader**) &shader
);
if (FAILED(res))
{
D3D11_INTERNAL_LogError(renderer->device, "Could not create pixel shader", res);
return NULL;
}
}
else if (shaderStage == REFRESH_SHADERSTAGE_COMPUTE)
{
res = ID3D11Device_CreateComputeShader(
renderer->device,
shaderCreateInfo->code,
shaderCreateInfo->codeSize,
NULL,
(ID3D11ComputeShader**) &shader
);
if (FAILED(res))
{
D3D11_INTERNAL_LogError(renderer->device, "Could not create compute shader", res);
return NULL;
}
}
/* Allocate and set up the shader module */
shaderModule = (D3D11Shader*) SDL_calloc(1, sizeof(D3D11Shader));
shaderModule->shader = shader;
if (shaderStage == REFRESH_SHADERSTAGE_VERTEX)
{
/* Store the raw bytecode and its length for creating InputLayouts */
shaderModule->bytecode = SDL_malloc(shaderCreateInfo->codeSize);
SDL_memcpy(shaderModule->bytecode, shaderCreateInfo->code, shaderCreateInfo->codeSize);
shaderModule->bytecodeLength = shaderCreateInfo->codeSize;
}
return (Refresh_Shader*) shaderModule;
}
static D3D11Texture* D3D11_INTERNAL_CreateTexture(
D3D11Renderer *renderer,
Refresh_TextureCreateInfo *textureCreateInfo
) {
Uint8 isSampler, isColorTarget, isDepthStencil, isMultisample, needSubresourceSRV, needSubresourceUAV;
DXGI_FORMAT format;
ID3D11Resource *textureHandle;
ID3D11ShaderResourceView *srv = NULL;
D3D11Texture *d3d11Texture;
HRESULT res;
isColorTarget = textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_COLOR_TARGET_BIT;
isDepthStencil = textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT;
isSampler = textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_SAMPLER_BIT;
needSubresourceSRV =
(textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_GRAPHICS_STORAGE_READ_BIT) ||
(textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_READ_BIT);
needSubresourceUAV =
(textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT);
isMultisample = textureCreateInfo->sampleCount > 1;
format = RefreshToD3D11_TextureFormat[textureCreateInfo->format];
if (isDepthStencil)
{
format = D3D11_INTERNAL_GetTypelessFormat(format);
}
if (textureCreateInfo->depth <= 1)
{
D3D11_TEXTURE2D_DESC desc2D;
desc2D.BindFlags = 0;
if (isSampler || needSubresourceSRV)
{
desc2D.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
}
if (needSubresourceUAV)
{
desc2D.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
}
if (isColorTarget)
{
desc2D.BindFlags |= D3D11_BIND_RENDER_TARGET;
}
if (isDepthStencil)
{
desc2D.BindFlags |= D3D11_BIND_DEPTH_STENCIL;
}
desc2D.Width = textureCreateInfo->width;
desc2D.Height = textureCreateInfo->height;
desc2D.ArraySize = textureCreateInfo->isCube ? 6 : textureCreateInfo->layerCount;
desc2D.CPUAccessFlags = 0;
desc2D.Format = format;
desc2D.MipLevels = textureCreateInfo->levelCount;
desc2D.MiscFlags = textureCreateInfo->isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
desc2D.SampleDesc.Count = 1;
desc2D.SampleDesc.Quality = 0;
desc2D.Usage = D3D11_USAGE_DEFAULT;
res = ID3D11Device_CreateTexture2D(
renderer->device,
&desc2D,
NULL,
(ID3D11Texture2D**) &textureHandle
);
ERROR_CHECK_RETURN("Could not create Texture2D", NULL);
/* Create the SRV, if applicable */
if (isSampler)
{
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
srvDesc.Format = D3D11_INTERNAL_GetSampleableFormat(format);
if (textureCreateInfo->isCube)
{
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
srvDesc.TextureCube.MipLevels = desc2D.MipLevels;
srvDesc.TextureCube.MostDetailedMip = 0;
}
else if (textureCreateInfo->layerCount > 1)
{
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
srvDesc.Texture2DArray.MipLevels = desc2D.MipLevels;
srvDesc.Texture2DArray.MostDetailedMip = 0;
srvDesc.Texture2DArray.FirstArraySlice = 0;
srvDesc.Texture2DArray.ArraySize = textureCreateInfo->layerCount;
}
else
{
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = desc2D.MipLevels;
srvDesc.Texture2D.MostDetailedMip = 0;
}
res = ID3D11Device_CreateShaderResourceView(
renderer->device,
textureHandle,
&srvDesc,
&srv
);
if (FAILED(res))
{
ID3D11Resource_Release(textureHandle);
D3D11_INTERNAL_LogError(renderer->device, "Could not create SRV for 2D texture", res);
return NULL;
}
}
}
else
{
D3D11_TEXTURE3D_DESC desc3D;
desc3D.BindFlags = 0;
if (isSampler || needSubresourceSRV)
{
desc3D.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
}
if (needSubresourceUAV)
{
desc3D.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
}
if (isColorTarget)
{
desc3D.BindFlags |= D3D11_BIND_RENDER_TARGET;
}
desc3D.Width = textureCreateInfo->width;
desc3D.Height = textureCreateInfo->height;
desc3D.Depth = textureCreateInfo->depth;
desc3D.CPUAccessFlags = 0;
desc3D.Format = format;
desc3D.MipLevels = textureCreateInfo->levelCount;
desc3D.MiscFlags = 0;
desc3D.Usage = D3D11_USAGE_DEFAULT;
res = ID3D11Device_CreateTexture3D(
renderer->device,
&desc3D,
NULL,
(ID3D11Texture3D**) &textureHandle
);
ERROR_CHECK_RETURN("Could not create Texture3D", NULL);
/* Create the SRV, if applicable */
if (isSampler)
{
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
srvDesc.Format = format;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
srvDesc.Texture3D.MipLevels = desc3D.MipLevels;
srvDesc.Texture3D.MostDetailedMip = 0;
res = ID3D11Device_CreateShaderResourceView(
renderer->device,
textureHandle,
&srvDesc,
&srv
);
if (FAILED(res))
{
ID3D11Resource_Release(textureHandle);
D3D11_INTERNAL_LogError(renderer->device, "Could not create SRV for 3D texture", res);
return NULL;
}
}
}
d3d11Texture = (D3D11Texture*) SDL_malloc(sizeof(D3D11Texture));
d3d11Texture->handle = textureHandle;
d3d11Texture->format = textureCreateInfo->format;
d3d11Texture->width = textureCreateInfo->width;
d3d11Texture->height = textureCreateInfo->height;
d3d11Texture->depth = textureCreateInfo->depth;
d3d11Texture->levelCount = textureCreateInfo->levelCount;
d3d11Texture->layerCount = textureCreateInfo->layerCount;
d3d11Texture->isCube = textureCreateInfo->isCube;
d3d11Texture->isRenderTarget = isColorTarget || isDepthStencil;
d3d11Texture->shaderView = srv;
d3d11Texture->subresourceCount = d3d11Texture->levelCount * d3d11Texture->layerCount;
d3d11Texture->subresources = SDL_malloc(
d3d11Texture->subresourceCount * sizeof(D3D11TextureSubresource)
);
for (Uint32 layerIndex = 0; layerIndex < d3d11Texture->layerCount; layerIndex += 1)
{
for (Uint32 levelIndex = 0; levelIndex < d3d11Texture->levelCount; levelIndex += 1)
{
Uint32 subresourceIndex = D3D11_INTERNAL_CalcSubresource(
levelIndex,
layerIndex,
d3d11Texture->levelCount
);
d3d11Texture->subresources[subresourceIndex].parent = d3d11Texture;
d3d11Texture->subresources[subresourceIndex].layer = layerIndex;
d3d11Texture->subresources[subresourceIndex].level = levelIndex;
d3d11Texture->subresources[subresourceIndex].index = subresourceIndex;
d3d11Texture->subresources[subresourceIndex].colorTargetView = NULL;
d3d11Texture->subresources[subresourceIndex].depthStencilTargetView = NULL;
d3d11Texture->subresources[subresourceIndex].srv = NULL;
d3d11Texture->subresources[subresourceIndex].uav = NULL;
d3d11Texture->subresources[subresourceIndex].msaaHandle = NULL;
d3d11Texture->subresources[subresourceIndex].msaaTargetView = NULL;
SDL_AtomicSet(&d3d11Texture->subresources[subresourceIndex].referenceCount, 0);
if (isMultisample)
{
D3D11_TEXTURE2D_DESC desc2D;
if (isColorTarget)
{
desc2D.BindFlags = D3D11_BIND_RENDER_TARGET;
}
else if (isDepthStencil)
{
desc2D.BindFlags = D3D11_BIND_DEPTH_STENCIL;
}
desc2D.Width = textureCreateInfo->width;
desc2D.Height = textureCreateInfo->height;
desc2D.ArraySize = 1;
desc2D.CPUAccessFlags = 0;
desc2D.Format = format;
desc2D.MipLevels = 1;
desc2D.MiscFlags = 0;
desc2D.SampleDesc.Count = RefreshToD3D11_SampleCount[textureCreateInfo->sampleCount];
desc2D.SampleDesc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN;
desc2D.Usage = D3D11_USAGE_DEFAULT;
res = ID3D11Device_CreateTexture2D(
renderer->device,
&desc2D,
NULL,
(ID3D11Texture2D**) &d3d11Texture->subresources[subresourceIndex].msaaHandle
);
ERROR_CHECK_RETURN("Could not create MSAA texture!", NULL);
if (!isDepthStencil)
{
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
rtvDesc.Format = format;
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
res = ID3D11Device_CreateRenderTargetView(
renderer->device,
d3d11Texture->subresources[subresourceIndex].msaaHandle,
&rtvDesc,
&d3d11Texture->subresources[subresourceIndex].msaaTargetView
);
ERROR_CHECK_RETURN("Could not create MSAA RTV!", NULL);
}
}
if (d3d11Texture->isRenderTarget)
{
if (isDepthStencil)
{
D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
dsvDesc.Format = RefreshToD3D11_TextureFormat[d3d11Texture->format];
dsvDesc.Flags = 0;
if (isMultisample)
{
dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
}
else
{
dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
dsvDesc.Texture2D.MipSlice = levelIndex;
}
res = ID3D11Device_CreateDepthStencilView(
renderer->device,
isMultisample ? d3d11Texture->subresources[subresourceIndex].msaaHandle : d3d11Texture->handle,
&dsvDesc,
&d3d11Texture->subresources[subresourceIndex].depthStencilTargetView
);
ERROR_CHECK_RETURN("Could not create DSV!", NULL);
}
else
{
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
rtvDesc.Format = RefreshToD3D11_TextureFormat[d3d11Texture->format];
if (d3d11Texture->layerCount > 1)
{
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
rtvDesc.Texture2DArray.MipSlice = levelIndex;
rtvDesc.Texture2DArray.FirstArraySlice = layerIndex;
rtvDesc.Texture2DArray.ArraySize = 1;
}
else if (d3d11Texture->depth > 1)
{
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
rtvDesc.Texture3D.MipSlice = levelIndex;
rtvDesc.Texture3D.FirstWSlice = 0;
rtvDesc.Texture3D.WSize = d3d11Texture->depth;
}
else
{
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtvDesc.Texture2D.MipSlice = levelIndex;
}
res = ID3D11Device_CreateRenderTargetView(
renderer->device,
d3d11Texture->handle,
&rtvDesc,
&d3d11Texture->subresources[subresourceIndex].colorTargetView
);
ERROR_CHECK_RETURN("Could not create RTV!", NULL);
}
}
if (needSubresourceSRV)
{
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
srvDesc.Format = format;
if (d3d11Texture->layerCount > 1)
{
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
srvDesc.Texture2DArray.ArraySize = 1;
srvDesc.Texture2DArray.FirstArraySlice = layerIndex;
srvDesc.Texture2DArray.MipLevels = 1;
srvDesc.Texture2DArray.MostDetailedMip = levelIndex;
}
else if (d3d11Texture->depth > 1)
{
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
srvDesc.Texture3D.MipLevels = 1;
srvDesc.Texture3D.MostDetailedMip = levelIndex;
}
else
{
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = 1;
srvDesc.Texture2D.MostDetailedMip = levelIndex;
}
res = ID3D11Device_CreateShaderResourceView(
renderer->device,
d3d11Texture->handle,
&srvDesc,
&d3d11Texture->subresources[subresourceIndex].srv
);
ERROR_CHECK_RETURN("Could not create SRV!", NULL);
}
if (needSubresourceUAV)
{
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
uavDesc.Format = format;
if (d3d11Texture->layerCount > 1)
{
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
uavDesc.Texture2DArray.MipSlice = levelIndex;
uavDesc.Texture2DArray.FirstArraySlice = layerIndex;
uavDesc.Texture2DArray.ArraySize = 1;
}
else if (d3d11Texture->depth > 1)
{
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;
uavDesc.Texture3D.MipSlice = levelIndex;
uavDesc.Texture3D.FirstWSlice = 0;
uavDesc.Texture3D.WSize = d3d11Texture->layerCount;
}
else
{
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
uavDesc.Texture2D.MipSlice = levelIndex;
}
res = ID3D11Device_CreateUnorderedAccessView(
renderer->device,
d3d11Texture->handle,
&uavDesc,
&d3d11Texture->subresources[subresourceIndex].uav
);
ERROR_CHECK_RETURN("Could not create UAV!", NULL);
}
}
}
return d3d11Texture;
}
static Refresh_Texture* D3D11_CreateTexture(
Refresh_Renderer *driverData,
Refresh_TextureCreateInfo *textureCreateInfo
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11TextureContainer *container;
D3D11Texture *texture;
texture = D3D11_INTERNAL_CreateTexture(
renderer,
textureCreateInfo
);
if (texture == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create texture!");
return NULL;
}
container = SDL_malloc(sizeof(D3D11TextureContainer));
container->canBeCycled = 1;
container->createInfo = *textureCreateInfo;
container->activeTexture = texture;
container->textureCapacity = 1;
container->textureCount = 1;
container->textures = SDL_malloc(
container->textureCapacity * sizeof(D3D11Texture*)
);
container->textures[0] = texture;
container->debugName = NULL;
return (Refresh_Texture*) container;
}
static void D3D11_INTERNAL_CycleActiveTexture(
D3D11Renderer *renderer,
D3D11TextureContainer *container
) {
for (Uint32 i = 0; i < container->textureCount; i += 1)
{
Uint32 refCountTotal = 0;
for (Uint32 j = 0; j < container->textures[i]->subresourceCount; j += 1)
{
refCountTotal += SDL_AtomicGet(&container->textures[i]->subresources[j].referenceCount);
}
if (refCountTotal == 0)
{
container->activeTexture = container->textures[i];
return;
}
}
EXPAND_ARRAY_IF_NEEDED(
container->textures,
D3D11Texture*,
container->textureCount + 1,
container->textureCapacity,
container->textureCapacity + 1
);
container->textures[container->textureCount] = D3D11_INTERNAL_CreateTexture(
renderer,
&container->createInfo
);
container->textureCount += 1;
container->activeTexture = container->textures[container->textureCount - 1];
if (renderer->debugMode && container->debugName != NULL)
{
D3D11_INTERNAL_SetTextureName(
renderer,
container->activeTexture,
container->debugName
);
}
}
static D3D11TextureSubresource* D3D11_INTERNAL_FetchTextureSubresource(
D3D11Texture *texture,
Uint32 layer,
Uint32 level
) {
Uint32 index = D3D11_INTERNAL_CalcSubresource(level, layer, texture->levelCount);
return &texture->subresources[index];
}
static D3D11TextureSubresource* D3D11_INTERNAL_PrepareTextureSubresourceForWrite(
D3D11Renderer *renderer,
D3D11TextureContainer *container,
Uint32 layer,
Uint32 level,
SDL_bool cycle
) {
D3D11TextureSubresource *subresource = D3D11_INTERNAL_FetchTextureSubresource(
container->activeTexture,
layer,
level
);
if (
container->canBeCycled &&
cycle &&
SDL_AtomicGet(&subresource->referenceCount) > 0
) {
D3D11_INTERNAL_CycleActiveTexture(
renderer,
container
);
subresource = D3D11_INTERNAL_FetchTextureSubresource(
container->activeTexture,
layer,
level
);
}
return subresource;
}
static D3D11Buffer* D3D11_INTERNAL_CreateBuffer(
D3D11Renderer *renderer,
D3D11_BUFFER_DESC *bufferDesc,
Uint32 sizeInBytes
) {
ID3D11Buffer *bufferHandle;
ID3D11UnorderedAccessView *uav = NULL;
ID3D11ShaderResourceView *srv = NULL;
D3D11Buffer *d3d11Buffer;
HRESULT res;
/* Storage buffers have to be 4-aligned, so might as well align them all */
sizeInBytes = D3D11_INTERNAL_NextHighestAlignment(sizeInBytes, 4);
res = ID3D11Device_CreateBuffer(
renderer->device,
bufferDesc,
NULL,
&bufferHandle
);
ERROR_CHECK_RETURN("Could not create buffer", NULL);
/* Storage buffer */
if (bufferDesc->MiscFlags & D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS)
{
/* Create a UAV for the buffer */
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
uavDesc.Format = DXGI_FORMAT_R32_TYPELESS;
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
uavDesc.Buffer.FirstElement = 0;
uavDesc.Buffer.NumElements = sizeInBytes / sizeof(Uint32);
uavDesc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;
res = ID3D11Device_CreateUnorderedAccessView(
renderer->device,
(ID3D11Resource*) bufferHandle,
&uavDesc,
&uav
);
if (FAILED(res))
{
ID3D11Buffer_Release(bufferHandle);
ERROR_CHECK_RETURN("Could not create UAV for buffer!", NULL);
}
/* Create a SRV for the buffer */
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
srvDesc.Format = DXGI_FORMAT_R32_TYPELESS;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX;
srvDesc.BufferEx.FirstElement = 0;
srvDesc.BufferEx.NumElements = sizeInBytes / sizeof(Uint32);
srvDesc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW;
res = ID3D11Device_CreateShaderResourceView(
renderer->device,
(ID3D11Resource*) bufferHandle,
&srvDesc,
&srv
);
if (FAILED(res))
{
ID3D11Buffer_Release(bufferHandle);
ERROR_CHECK_RETURN("Could not create SRV for buffer!", NULL);
}
}
d3d11Buffer = SDL_malloc(sizeof(D3D11Buffer));
d3d11Buffer->handle = bufferHandle;
d3d11Buffer->size = sizeInBytes;
d3d11Buffer->uav = uav;
d3d11Buffer->srv = srv;
SDL_AtomicSet(&d3d11Buffer->referenceCount, 0);
return d3d11Buffer;
}
static Refresh_Buffer* D3D11_CreateBuffer(
Refresh_Renderer *driverData,
Refresh_BufferUsageFlags usageFlags,
Uint32 sizeInBytes
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11BufferContainer *container;
D3D11Buffer *buffer;
D3D11_BUFFER_DESC bufferDesc;
bufferDesc.BindFlags = 0;
if (usageFlags & REFRESH_BUFFERUSAGE_VERTEX_BIT)
{
bufferDesc.BindFlags |= D3D11_BIND_VERTEX_BUFFER;
}
if (usageFlags & REFRESH_BUFFERUSAGE_INDEX_BIT)
{
bufferDesc.BindFlags |= D3D11_BIND_INDEX_BUFFER;
}
if (usageFlags & REFRESH_BUFFERUSAGE_INDIRECT_BIT)
{
bufferDesc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
}
if (usageFlags & (
REFRESH_BUFFERUSAGE_GRAPHICS_STORAGE_READ_BIT |
REFRESH_BUFFERUSAGE_COMPUTE_STORAGE_READ_BIT |
REFRESH_BUFFERUSAGE_COMPUTE_STORAGE_WRITE_BIT
)) {
bufferDesc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
}
bufferDesc.ByteWidth = sizeInBytes;
bufferDesc.Usage = D3D11_USAGE_DEFAULT;
bufferDesc.CPUAccessFlags = 0;
bufferDesc.StructureByteStride = 0;
bufferDesc.MiscFlags = 0;
if (usageFlags & REFRESH_BUFFERUSAGE_INDIRECT_BIT)
{
bufferDesc.MiscFlags |= D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS;
}
if (usageFlags & (
REFRESH_BUFFERUSAGE_GRAPHICS_STORAGE_READ_BIT |
REFRESH_BUFFERUSAGE_COMPUTE_STORAGE_READ_BIT |
REFRESH_BUFFERUSAGE_COMPUTE_STORAGE_WRITE_BIT
))
{
bufferDesc.MiscFlags |= D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
}
buffer = D3D11_INTERNAL_CreateBuffer(
renderer,
&bufferDesc,
sizeInBytes
);
if (buffer == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create buffer!");
return NULL;
}
container = SDL_malloc(sizeof(D3D11BufferContainer));
container->activeBuffer = buffer;
container->bufferCapacity = 1;
container->bufferCount = 1;
container->buffers = SDL_malloc(
container->bufferCapacity * sizeof(D3D11Buffer*)
);
container->buffers[0] = container->activeBuffer;
container->bufferDesc = bufferDesc;
container->debugName = NULL;
return (Refresh_Buffer*) container;
}
static D3D11UniformBuffer *D3D11_INTERNAL_CreateUniformBuffer(
D3D11Renderer *renderer,
Uint32 sizeInBytes
) {
D3D11UniformBuffer *uniformBuffer;
D3D11BufferContainer *container;
D3D11Buffer *buffer;
D3D11_BUFFER_DESC bufferDesc;
bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bufferDesc.ByteWidth = sizeInBytes;
bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
bufferDesc.MiscFlags = 0;
bufferDesc.StructureByteStride = 0;
bufferDesc.Usage = D3D11_USAGE_DYNAMIC;
buffer = D3D11_INTERNAL_CreateBuffer(
renderer,
&bufferDesc,
sizeInBytes
);
if (buffer == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create UniformBuffer!");
return NULL;
}
container = SDL_malloc(sizeof(D3D11BufferContainer));
container->activeBuffer = buffer;
container->bufferCapacity = 1;
container->bufferCount = 1;
container->buffers = SDL_malloc(
container->bufferCapacity * sizeof(D3D11Buffer*)
);
container->buffers[0] = container->activeBuffer;
container->bufferDesc = bufferDesc;
container->debugName = NULL;
uniformBuffer = SDL_malloc(sizeof(D3D11UniformBuffer));
uniformBuffer->bufferContainer = container;
uniformBuffer->offset = 0;
uniformBuffer->currentBlockSize = 0;
return uniformBuffer;
}
static void D3D11_INTERNAL_CycleActiveBuffer(
D3D11Renderer *renderer,
D3D11BufferContainer *container
) {
Uint32 size = container->activeBuffer->size;
for (Uint32 i = 0; i < container->bufferCount; i += 1)
{
if (SDL_AtomicGet(&container->buffers[i]->referenceCount) == 0)
{
container->activeBuffer = container->buffers[i];
return;
}
}
EXPAND_ARRAY_IF_NEEDED(
container->buffers,
D3D11Buffer*,
container->bufferCount + 1,
container->bufferCapacity,
container->bufferCapacity + 1
);
container->buffers[container->bufferCount] = D3D11_INTERNAL_CreateBuffer(
renderer,
&container->bufferDesc,
size
);
container->bufferCount += 1;
container->activeBuffer = container->buffers[container->bufferCount - 1];
if (renderer->debugMode && container->debugName != NULL)
{
D3D11_INTERNAL_SetBufferName(
renderer,
container->activeBuffer,
container->debugName
);
}
}
static D3D11Buffer* D3D11_INTERNAL_PrepareBufferForWrite(
D3D11Renderer *renderer,
D3D11BufferContainer *container,
SDL_bool cycle
) {
if (
cycle &&
SDL_AtomicGet(&container->activeBuffer->referenceCount) > 0
) {
D3D11_INTERNAL_CycleActiveBuffer(
renderer,
container
);
}
return container->activeBuffer;
}
static D3D11TransferBuffer* D3D11_INTERNAL_CreateTransferBuffer(
D3D11Renderer *renderer,
Refresh_TransferUsage usage,
Refresh_TransferBufferMapFlags mapFlags,
Uint32 sizeInBytes
) {
D3D11TransferBuffer *transferBuffer = SDL_malloc(sizeof(D3D11TransferBuffer));
UINT cpuAccessFlags = 0;
transferBuffer->size = sizeInBytes;
SDL_AtomicSet(&transferBuffer->referenceCount, 0);
if (mapFlags & REFRESH_TRANSFER_MAP_READ)
{
cpuAccessFlags |= D3D11_CPU_ACCESS_READ;
}
if (mapFlags & REFRESH_TRANSFER_MAP_WRITE)
{
cpuAccessFlags |= D3D11_CPU_ACCESS_WRITE;
}
if (usage == REFRESH_TRANSFERUSAGE_BUFFER)
{
D3D11_BUFFER_DESC stagingBufferDesc;
HRESULT res;
stagingBufferDesc.ByteWidth = sizeInBytes;
stagingBufferDesc.Usage = D3D11_USAGE_STAGING;
stagingBufferDesc.BindFlags = 0;
stagingBufferDesc.CPUAccessFlags = cpuAccessFlags;
stagingBufferDesc.MiscFlags = 0;
stagingBufferDesc.StructureByteStride = 0;
res = ID3D11Device_CreateBuffer(
renderer->device,
&stagingBufferDesc,
NULL,
&transferBuffer->bufferTransfer.stagingBuffer
);
ERROR_CHECK_RETURN("Could not create staging buffer", NULL);
}
else /* TRANSFERUSAGE_TEXTURE */
{
transferBuffer->textureTransfer.data = (Uint8*) SDL_malloc(sizeInBytes);
transferBuffer->textureTransfer.downloadTexture = NULL;
}
return transferBuffer;
}
/* This actually returns a container handle so we can rotate buffers on Cycle. */
static Refresh_TransferBuffer* D3D11_CreateTransferBuffer(
Refresh_Renderer *driverData,
Refresh_TransferUsage usage,
Refresh_TransferBufferMapFlags mapFlags,
Uint32 sizeInBytes
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) SDL_malloc(sizeof(D3D11TransferBufferContainer));
container->usage = usage;
container->mapFlags = mapFlags;
container->bufferCapacity = 1;
container->bufferCount = 1;
container->buffers = SDL_malloc(
container->bufferCapacity * sizeof(D3D11TransferBuffer*)
);
container->buffers[0] = D3D11_INTERNAL_CreateTransferBuffer(
renderer,
usage,
mapFlags,
sizeInBytes
);
container->activeBuffer = container->buffers[0];
return (Refresh_TransferBuffer*) container;
}
/* TransferBuffer Data */
static void D3D11_INTERNAL_CycleActiveTransferBuffer(
D3D11Renderer *renderer,
D3D11TransferBufferContainer *container
) {
Uint32 size = container->activeBuffer->size;
for (Uint32 i = 0; i < container->bufferCount; i += 1)
{
if (SDL_AtomicGet(&container->buffers[i]->referenceCount) == 0)
{
container->activeBuffer = container->buffers[i];
return;
}
}
EXPAND_ARRAY_IF_NEEDED(
container->buffers,
D3D11TransferBuffer*,
container->bufferCount + 1,
container->bufferCapacity,
container->bufferCapacity + 1
);
container->buffers[container->bufferCount] = D3D11_INTERNAL_CreateTransferBuffer(
renderer,
container->usage,
container->mapFlags,
size
);
container->bufferCount += 1;
container->activeBuffer = container->buffers[container->bufferCount - 1];
}
static void D3D11_MapTransferBuffer(
Refresh_Renderer *driverData,
Refresh_TransferBuffer *transferBuffer,
SDL_bool cycle,
void **ppData
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer;
D3D11TransferBuffer *buffer = container->activeBuffer;
D3D11_MAPPED_SUBRESOURCE mappedSubresource;
HRESULT res;
/* Rotate the transfer buffer if necessary */
if (
cycle &&
SDL_AtomicGet(&container->activeBuffer->referenceCount) > 0
) {
D3D11_INTERNAL_CycleActiveTransferBuffer(
renderer,
container
);
buffer = container->activeBuffer;
}
if (container->usage == REFRESH_TRANSFERUSAGE_BUFFER)
{
SDL_LockMutex(renderer->contextLock);
res = ID3D11DeviceContext_Map(
renderer->immediateContext,
(ID3D11Resource*) buffer->bufferTransfer.stagingBuffer,
0,
D3D11_MAP_WRITE,
0,
&mappedSubresource
);
SDL_UnlockMutex(renderer->contextLock);
ERROR_CHECK_RETURN("Failed to map staging buffer", );
*ppData = (Uint8*) mappedSubresource.pData;
}
else /* TEXTURE */
{
*ppData = buffer->textureTransfer.data;
}
}
static void D3D11_UnmapTransferBuffer(
Refresh_Renderer *driverData,
Refresh_TransferBuffer *transferBuffer
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer;
D3D11TransferBuffer *buffer = container->activeBuffer;
if (container->usage == REFRESH_TRANSFERUSAGE_BUFFER)
{
SDL_LockMutex(renderer->contextLock);
ID3D11DeviceContext_Unmap(
renderer->immediateContext,
(ID3D11Resource*) buffer->bufferTransfer.stagingBuffer,
0
);
SDL_UnlockMutex(renderer->contextLock);
}
/* TEXTURE unmap is a no-op */
}
static void D3D11_SetTransferData(
Refresh_Renderer *driverData,
void* data,
Refresh_TransferBuffer *transferBuffer,
Refresh_BufferCopy *copyParams,
SDL_bool cycle
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer;
D3D11TransferBuffer *buffer = container->activeBuffer;
HRESULT res;
/* Rotate the transfer buffer if necessary */
if (
cycle &&
SDL_AtomicGet(&container->activeBuffer->referenceCount) > 0
) {
D3D11_INTERNAL_CycleActiveTransferBuffer(
renderer,
container
);
buffer = container->activeBuffer;
}
if (container->usage == REFRESH_TRANSFERUSAGE_BUFFER)
{
D3D11_MAPPED_SUBRESOURCE mappedSubresource;
SDL_LockMutex(renderer->contextLock);
res = ID3D11DeviceContext_Map(
renderer->immediateContext,
(ID3D11Resource*) buffer->bufferTransfer.stagingBuffer,
0,
D3D11_MAP_WRITE,
0,
&mappedSubresource
);
ERROR_CHECK_RETURN("Failed to map staging buffer", );
SDL_memcpy(
((Uint8*) mappedSubresource.pData) + copyParams->dstOffset,
((Uint8*) data) + copyParams->srcOffset,
copyParams->size
);
ID3D11DeviceContext_Unmap(
renderer->immediateContext,
(ID3D11Resource*) buffer->bufferTransfer.stagingBuffer,
0
);
SDL_UnlockMutex(renderer->contextLock);
}
else /* TEXTURE */
{
SDL_memcpy(
buffer->textureTransfer.data + copyParams->dstOffset,
((Uint8*) data) + copyParams->srcOffset,
copyParams->size
);
}
}
static void D3D11_GetTransferData(
Refresh_Renderer *driverData,
Refresh_TransferBuffer *transferBuffer,
void* data,
Refresh_BufferCopy *copyParams
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer;
D3D11TransferBuffer *buffer = container->activeBuffer;
D3D11_MAPPED_SUBRESOURCE subresource;
Uint8* dataPtr;
Uint32 dataPtrOffset;
Uint32 depth, row, copySize;
HRESULT res;
if (container->usage == REFRESH_TRANSFERUSAGE_BUFFER)
{
SDL_LockMutex(renderer->contextLock);
res = ID3D11DeviceContext_Map(
renderer->immediateContext,
(ID3D11Resource*) buffer->bufferTransfer.stagingBuffer,
0,
D3D11_MAP_READ,
0,
&subresource
);
ERROR_CHECK_RETURN("Failed to map staging buffer", );
SDL_memcpy(
((Uint8*) data) + copyParams->dstOffset,
((Uint8*) subresource.pData) + copyParams->srcOffset,
copyParams->size
);
ID3D11DeviceContext_Unmap(
renderer->immediateContext,
(ID3D11Resource*) buffer->bufferTransfer.stagingBuffer,
0
);
SDL_UnlockMutex(renderer->contextLock);
}
else /* TEXTURE */
{
SDL_LockMutex(renderer->contextLock);
/* Read from the staging texture */
res = ID3D11DeviceContext_Map(
renderer->immediateContext,
buffer->textureTransfer.downloadTexture,
0,
D3D11_MAP_READ,
0,
&subresource
);
ERROR_CHECK_RETURN("Could not map texture for reading",)
dataPtr = (Uint8*) data;
dataPtrOffset = copyParams->dstOffset;
if (buffer->textureTransfer.downloadTightlyPacked)
{
SDL_memcpy(
dataPtr + dataPtrOffset,
(Uint8*) subresource.pData + copyParams->srcOffset,
SDL_min(copyParams->size, buffer->textureTransfer.downloadDepth * buffer->textureTransfer.downloadHeight * buffer->textureTransfer.downloadBytesPerRow)
);
}
else
{
for (depth = 0; depth < buffer->textureTransfer.downloadDepth; depth += 1)
{
for (row = 0; row < buffer->textureTransfer.downloadHeight; row += 1)
{
copySize = SDL_min(copyParams->size - dataPtrOffset, buffer->textureTransfer.downloadBytesPerRow);
if (copySize == 0)
{
goto unmap;
}
SDL_memcpy(
dataPtr + dataPtrOffset,
(Uint8*) subresource.pData + copyParams->srcOffset + (depth * buffer->textureTransfer.downloadBytesPerDepthSlice) + (row * buffer->textureTransfer.downloadBytesPerRow),
copySize
);
dataPtrOffset += copySize;
}
}
}
unmap:
ID3D11DeviceContext1_Unmap(
renderer->immediateContext,
buffer->textureTransfer.downloadTexture,
0
);
SDL_UnlockMutex(renderer->contextLock);
}
}
/* Copy Pass */
static void D3D11_BeginCopyPass(
Refresh_CommandBuffer *commandBuffer
) {
/* no-op */
}
static void D3D11_UploadToTexture(
Refresh_CommandBuffer *commandBuffer,
Refresh_TransferBuffer *transferBuffer,
Refresh_TextureRegion *textureRegion,
Refresh_BufferImageCopy *copyParams,
SDL_bool cycle
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer;
D3D11TransferBufferContainer *transferContainer = (D3D11TransferBufferContainer*) transferBuffer;
D3D11TransferBuffer *d3d11TransferBuffer = transferContainer->activeBuffer;
D3D11TextureContainer *d3d11TextureContainer = (D3D11TextureContainer*) textureRegion->textureSlice.texture;
Uint32 bufferStride = copyParams->bufferStride;
Uint32 bufferImageHeight = copyParams->bufferImageHeight;
Sint32 w = textureRegion->w;
Sint32 h = textureRegion->h;
D3D11TextureContainer *stagingTexture;
Refresh_TextureCreateInfo stagingTextureCreateInfo;
D3D11TextureSubresource *textureSubresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite(
renderer,
d3d11TextureContainer,
textureRegion->textureSlice.layer,
textureRegion->textureSlice.mipLevel,
cycle);
Sint32 blockSize = Texture_GetBlockSize(textureSubresource->parent->format);
if (blockSize > 1) {
w = (w + blockSize - 1) & ~(blockSize - 1);
h = (h + blockSize - 1) & ~(blockSize - 1);
}
if (bufferStride == 0 || bufferImageHeight == 0)
{
bufferStride = BytesPerRow(w, textureSubresource->parent->format);
bufferImageHeight = h * Refresh_TextureFormatTexelBlockSize(textureSubresource->parent->format);
}
D3D11_BOX stagingTextureBox;
stagingTextureBox.left = 0;
stagingTextureBox.top = 0;
stagingTextureBox.front = 0;
stagingTextureBox.right = w;
stagingTextureBox.bottom = h;
stagingTextureBox.back = textureRegion->d;
/* UpdateSubresource1 is completely busted on AMD, it truncates after X bytes.
* So we get to do this Fun (Tm) workaround where we create a staging texture
* and upload to it in the immediate context before using a copy command.
*/
stagingTextureCreateInfo.width = w;
stagingTextureCreateInfo.height = h;
stagingTextureCreateInfo.depth = textureRegion->d;
stagingTextureCreateInfo.layerCount = 1;
stagingTextureCreateInfo.levelCount = 1;
stagingTextureCreateInfo.isCube = 0;
stagingTextureCreateInfo.usageFlags = 0;
stagingTextureCreateInfo.sampleCount = REFRESH_SAMPLECOUNT_1;
stagingTextureCreateInfo.format = ((D3D11TextureContainer *)textureRegion->textureSlice.texture)->createInfo.format;
stagingTexture = (D3D11TextureContainer*) D3D11_CreateTexture(
(Refresh_Renderer*) d3d11CommandBuffer->renderer,
&stagingTextureCreateInfo
);
if (stagingTexture == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Staging texture creation failed");
}
SDL_LockMutex(renderer->contextLock);
ID3D11DeviceContext_UpdateSubresource(
renderer->immediateContext,
stagingTexture->activeTexture->handle,
0,
&stagingTextureBox,
(Uint8*) d3d11TransferBuffer->textureTransfer.data + copyParams->bufferOffset,
bufferStride,
bufferStride * bufferImageHeight
);
SDL_UnlockMutex(renderer->contextLock);
ID3D11DeviceContext1_CopySubresourceRegion1(
d3d11CommandBuffer->context,
textureSubresource->parent->handle,
textureSubresource->index,
textureRegion->x,
textureRegion->y,
textureRegion->z,
stagingTexture->activeTexture->handle,
0,
&stagingTextureBox,
D3D11_COPY_NO_OVERWRITE
);
/* Clean up the staging texture */
D3D11_ReleaseTexture(
(Refresh_Renderer*) d3d11CommandBuffer->renderer,
(Refresh_Texture *)stagingTexture
);
D3D11_INTERNAL_TrackTextureSubresource(d3d11CommandBuffer, &stagingTexture->activeTexture->subresources[0]);
D3D11_INTERNAL_TrackTextureSubresource(d3d11CommandBuffer, textureSubresource);
D3D11_INTERNAL_TrackTransferBuffer(d3d11CommandBuffer, d3d11TransferBuffer);
}
static void D3D11_UploadToBuffer(
Refresh_CommandBuffer *commandBuffer,
Refresh_TransferBuffer *transferBuffer,
Refresh_Buffer *buffer,
Refresh_BufferCopy *copyParams,
SDL_bool cycle
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer;
D3D11TransferBufferContainer *transferContainer = (D3D11TransferBufferContainer*) transferBuffer;
D3D11TransferBuffer *d3d11TransferBuffer = transferContainer->activeBuffer;
D3D11BufferContainer *bufferContainer = (D3D11BufferContainer*) buffer;
D3D11_BOX srcBox = { copyParams->srcOffset, 0, 0, copyParams->srcOffset + copyParams->size, 1, 1 };
D3D11Buffer *d3d11Buffer = D3D11_INTERNAL_PrepareBufferForWrite(
renderer,
bufferContainer,
cycle
);
ID3D11DeviceContext1_CopySubresourceRegion1(
d3d11CommandBuffer->context,
(ID3D11Resource*) d3d11Buffer->handle,
0,
copyParams->dstOffset,
0,
0,
(ID3D11Resource*) d3d11TransferBuffer->bufferTransfer.stagingBuffer,
0,
&srcBox,
D3D11_COPY_NO_OVERWRITE /* always no overwrite because we manually discard */
);
D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, d3d11Buffer);
D3D11_INTERNAL_TrackTransferBuffer(d3d11CommandBuffer, d3d11TransferBuffer);
}
static void D3D11_DownloadFromTexture(
Refresh_CommandBuffer *commandBuffer,
Refresh_TextureRegion *textureRegion,
Refresh_TransferBuffer *transferBuffer,
Refresh_BufferImageCopy *copyParams
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11Renderer *renderer = d3d11CommandBuffer->renderer;
D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer;
D3D11TransferBuffer *d3d11TransferBuffer = container->activeBuffer;
D3D11TextureContainer *d3d11TextureContainer = (D3D11TextureContainer*) textureRegion->textureSlice.texture;
D3D11_TEXTURE2D_DESC stagingDesc2D;
D3D11_TEXTURE3D_DESC stagingDesc3D;
D3D11TextureSubresource *textureSubresource = D3D11_INTERNAL_FetchTextureSubresource(
d3d11TextureContainer->activeTexture,
textureRegion->textureSlice.layer,
textureRegion->textureSlice.mipLevel
);
Uint32 bufferStride = copyParams->bufferStride;
Uint32 bufferImageHeight = copyParams->bufferImageHeight;
Uint32 bytesPerRow, bytesPerDepthSlice;
D3D11_BOX srcBox = {textureRegion->x, textureRegion->y, textureRegion->z, textureRegion->x + textureRegion->w, textureRegion->y + textureRegion->h, 1};
HRESULT res;
if (bufferStride == 0 || bufferImageHeight == 0)
{
bufferStride = textureRegion->w;
bufferImageHeight = textureRegion->h;
}
bytesPerRow = BytesPerRow(bufferStride, textureSubresource->parent->format);
bytesPerDepthSlice = bytesPerRow * bufferImageHeight;
if (d3d11TransferBuffer->textureTransfer.downloadTexture != NULL)
{
ID3D11Resource_Release(d3d11TransferBuffer->textureTransfer.downloadTexture);
}
if (textureRegion->d == 1)
{
stagingDesc2D.Width = textureRegion->w;
stagingDesc2D.Height = textureRegion->h;
stagingDesc2D.MipLevels = 1;
stagingDesc2D.ArraySize = 1;
stagingDesc2D.Format = RefreshToD3D11_TextureFormat[textureSubresource->parent->format];
stagingDesc2D.SampleDesc.Count = 1;
stagingDesc2D.SampleDesc.Quality = 0;
stagingDesc2D.Usage = D3D11_USAGE_STAGING;
stagingDesc2D.BindFlags = 0;
stagingDesc2D.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
stagingDesc2D.MiscFlags = 0;
res = ID3D11Device_CreateTexture2D(
renderer->device,
&stagingDesc2D,
NULL,
(ID3D11Texture2D**) &d3d11TransferBuffer->textureTransfer.downloadTexture
);
ERROR_CHECK_RETURN("Staging texture creation failed",)
}
else
{
stagingDesc3D.Width = textureRegion->w;
stagingDesc3D.Height = textureRegion->h;
stagingDesc3D.Depth = textureRegion->d;
stagingDesc3D.MipLevels = 1;
stagingDesc3D.Format = RefreshToD3D11_TextureFormat[textureSubresource->parent->format];
stagingDesc3D.Usage = D3D11_USAGE_STAGING;
stagingDesc3D.BindFlags = 0;
stagingDesc3D.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
stagingDesc3D.MiscFlags = 0;
res = ID3D11Device_CreateTexture3D(
renderer->device,
&stagingDesc3D,
NULL,
(ID3D11Texture3D**) &d3d11TransferBuffer->textureTransfer.downloadTexture
);
}
d3d11TransferBuffer->textureTransfer.downloadWidth = textureRegion->w;
d3d11TransferBuffer->textureTransfer.downloadHeight = textureRegion->h;
d3d11TransferBuffer->textureTransfer.downloadDepth = textureRegion->d;
d3d11TransferBuffer->textureTransfer.downloadBytesPerRow = bytesPerRow;
d3d11TransferBuffer->textureTransfer.downloadBytesPerDepthSlice = bytesPerDepthSlice;
d3d11TransferBuffer->textureTransfer.downloadTightlyPacked = textureRegion->w == bufferStride && textureRegion->h == bufferImageHeight;
ID3D11DeviceContext1_CopySubresourceRegion1(
d3d11CommandBuffer->context,
d3d11TransferBuffer->textureTransfer.downloadTexture,
0,
0,
0,
0,
textureSubresource->parent->handle,
textureSubresource->index,
&srcBox,
D3D11_COPY_NO_OVERWRITE
);
}
static void D3D11_DownloadFromBuffer(
Refresh_CommandBuffer *commandBuffer,
Refresh_Buffer *buffer,
Refresh_TransferBuffer *transferBuffer,
Refresh_BufferCopy *copyParams
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer;
D3D11TransferBuffer *d3d11TransferBuffer = container->activeBuffer;
D3D11BufferContainer *d3d11BufferContainer = (D3D11BufferContainer*) buffer;
D3D11_BOX srcBox = { copyParams->srcOffset, 0, 0, copyParams->size, 1, 1 };
ID3D11DeviceContext1_CopySubresourceRegion1(
d3d11CommandBuffer->context,
(ID3D11Resource*) d3d11TransferBuffer->bufferTransfer.stagingBuffer,
0,
copyParams->dstOffset,
0,
0,
(ID3D11Resource*) d3d11BufferContainer->activeBuffer->handle,
0,
&srcBox,
D3D11_COPY_NO_OVERWRITE
);
}
static void D3D11_CopyTextureToTexture(
Refresh_CommandBuffer *commandBuffer,
Refresh_TextureRegion *source,
Refresh_TextureRegion *destination,
SDL_bool cycle
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer;
D3D11TextureContainer *srcContainer = (D3D11TextureContainer*) source->textureSlice.texture;
D3D11TextureContainer *dstContainer = (D3D11TextureContainer*) destination->textureSlice.texture;
D3D11_BOX srcBox = { source->x, source->y, source->z, source->x + source->w, source->y + source->h, 1 };
D3D11TextureSubresource *srcSubresource = D3D11_INTERNAL_FetchTextureSubresource(
srcContainer->activeTexture,
source->textureSlice.layer,
source->textureSlice.mipLevel
);
D3D11TextureSubresource *dstSubresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite(
renderer,
dstContainer,
destination->textureSlice.layer,
destination->textureSlice.mipLevel,
cycle
);
ID3D11DeviceContext1_CopySubresourceRegion1(
d3d11CommandBuffer->context,
dstSubresource->parent->handle,
dstSubresource->index,
destination->x,
destination->y,
destination->z,
srcSubresource->parent->handle,
srcSubresource->index,
&srcBox,
D3D11_COPY_NO_OVERWRITE
);
D3D11_INTERNAL_TrackTextureSubresource(d3d11CommandBuffer, srcSubresource);
D3D11_INTERNAL_TrackTextureSubresource(d3d11CommandBuffer, dstSubresource);
}
static void D3D11_CopyBufferToBuffer(
Refresh_CommandBuffer *commandBuffer,
Refresh_Buffer *source,
Refresh_Buffer *destination,
Refresh_BufferCopy *copyParams,
SDL_bool cycle
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer;
D3D11BufferContainer *srcBufferContainer = (D3D11BufferContainer*) source;
D3D11BufferContainer *dstBufferContainer = (D3D11BufferContainer*) destination;
D3D11_BOX srcBox = { copyParams->srcOffset, 0, 0, copyParams->srcOffset + copyParams->size, 1, 1 };
D3D11Buffer *srcBuffer = srcBufferContainer->activeBuffer;
D3D11Buffer *dstBuffer = D3D11_INTERNAL_PrepareBufferForWrite(
renderer,
dstBufferContainer,
cycle
);
ID3D11DeviceContext1_CopySubresourceRegion1(
d3d11CommandBuffer->context,
(ID3D11Resource*) dstBuffer->handle,
0,
copyParams->dstOffset,
0,
0,
(ID3D11Resource*) srcBuffer->handle,
0,
&srcBox,
D3D11_COPY_NO_OVERWRITE /* always no overwrite because we either manually discard or the write is unsafe */
);
D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, srcBuffer);
D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, dstBuffer);
}
static void D3D11_GenerateMipmaps(
Refresh_CommandBuffer *commandBuffer,
Refresh_Texture *texture
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11TextureContainer *d3d11TextureContainer = (D3D11TextureContainer*) texture;
ID3D11DeviceContext1_GenerateMips(
d3d11CommandBuffer->context,
d3d11TextureContainer->activeTexture->shaderView
);
for (Uint32 i = 0; i < d3d11TextureContainer->activeTexture->subresourceCount; i += 1)
{
D3D11_INTERNAL_TrackTextureSubresource(
d3d11CommandBuffer,
&d3d11TextureContainer->activeTexture->subresources[i]
);
}
}
static void D3D11_EndCopyPass(
Refresh_CommandBuffer *commandBuffer
) {
/* no-op */
}
/* Uniforms */
/* TODO: we could get a big performance boost by storing data and mapping right before submitting commands */
static void D3D11_INTERNAL_SetUniformBufferData(
D3D11Renderer *renderer,
D3D11CommandBuffer *commandBuffer,
D3D11Buffer *uniformBuffer,
Uint32 offset,
void* data,
Uint32 dataLength
) {
D3D11_MAPPED_SUBRESOURCE subres;
HRESULT res = ID3D11DeviceContext_Map(
commandBuffer->context,
(ID3D11Resource*) uniformBuffer->handle,
0,
offset == 0 ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE,
0,
&subres
);
ERROR_CHECK_RETURN("Could not map buffer for writing!", );
SDL_memcpy(
(Uint8*) subres.pData + offset,
data,
dataLength
);
ID3D11DeviceContext_Unmap(
commandBuffer->context,
(ID3D11Resource*) uniformBuffer->handle,
0
);
}
/* Graphics State */
static void D3D11_INTERNAL_AllocateCommandBuffers(
D3D11Renderer *renderer,
Uint32 allocateCount
) {
D3D11CommandBuffer *commandBuffer;
HRESULT res;
renderer->availableCommandBufferCapacity += allocateCount;
renderer->availableCommandBuffers = SDL_realloc(
renderer->availableCommandBuffers,
sizeof(D3D11CommandBuffer*) * renderer->availableCommandBufferCapacity
);
for (Uint32 i = 0; i < allocateCount; i += 1)
{
commandBuffer = SDL_malloc(sizeof(D3D11CommandBuffer));
commandBuffer->renderer = renderer;
/* Deferred Device Context */
res = ID3D11Device1_CreateDeferredContext1(
renderer->device,
0,
&commandBuffer->context
);
ERROR_CHECK("Could not create deferred context");
commandBuffer->initializedVertexUniformBufferCount = 0;
commandBuffer->initializedFragmentUniformBufferCount = 0;
commandBuffer->initializedComputeUniformBufferCount = 0;
commandBuffer->windowDataCapacity = 1;
commandBuffer->windowDataCount = 0;
commandBuffer->windowDatas = SDL_malloc(
commandBuffer->windowDataCapacity * sizeof(D3D11WindowData*)
);
/* Reference Counting */
commandBuffer->usedBufferCapacity = 4;
commandBuffer->usedBufferCount = 0;
commandBuffer->usedBuffers = SDL_malloc(
commandBuffer->usedBufferCapacity * sizeof(D3D11Buffer*)
);
commandBuffer->usedTransferBufferCapacity = 4;
commandBuffer->usedTransferBufferCount = 0;
commandBuffer->usedTransferBuffers = SDL_malloc(
commandBuffer->usedTransferBufferCapacity * sizeof(D3D11TransferBuffer*)
);
commandBuffer->usedTextureSubresourceCapacity = 4;
commandBuffer->usedTextureSubresourceCount = 0;
commandBuffer->usedTextureSubresources = SDL_malloc(
commandBuffer->usedTextureSubresourceCapacity * sizeof(D3D11TextureSubresource*)
);
renderer->availableCommandBuffers[renderer->availableCommandBufferCount] = commandBuffer;
renderer->availableCommandBufferCount += 1;
}
}
static D3D11CommandBuffer* D3D11_INTERNAL_GetInactiveCommandBufferFromPool(
D3D11Renderer *renderer
) {
D3D11CommandBuffer *commandBuffer;
if (renderer->availableCommandBufferCount == 0)
{
D3D11_INTERNAL_AllocateCommandBuffers(
renderer,
renderer->availableCommandBufferCapacity
);
}
commandBuffer = renderer->availableCommandBuffers[renderer->availableCommandBufferCount - 1];
renderer->availableCommandBufferCount -= 1;
return commandBuffer;
}
static Uint8 D3D11_INTERNAL_CreateFence(
D3D11Renderer *renderer
) {
D3D11_QUERY_DESC queryDesc;
ID3D11Query *queryHandle;
D3D11Fence* fence;
HRESULT res;
queryDesc.Query = D3D11_QUERY_EVENT;
queryDesc.MiscFlags = 0;
res = ID3D11Device_CreateQuery(
renderer->device,
&queryDesc,
&queryHandle
);
ERROR_CHECK_RETURN("Could not create query", 0);
fence = SDL_malloc(sizeof(D3D11Fence));
fence->handle = queryHandle;
SDL_AtomicSet(&fence->referenceCount, 0);
/* Add it to the available pool */
if (renderer->availableFenceCount >= renderer->availableFenceCapacity)
{
renderer->availableFenceCapacity *= 2;
renderer->availableFences = SDL_realloc(
renderer->availableFences,
sizeof(D3D11Fence*) * renderer->availableFenceCapacity
);
}
renderer->availableFences[renderer->availableFenceCount] = fence;
renderer->availableFenceCount += 1;
return 1;
}
static Uint8 D3D11_INTERNAL_AcquireFence(
D3D11CommandBuffer *commandBuffer
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer;
D3D11Fence *fence;
/* Acquire a fence from the pool */
SDL_LockMutex(renderer->fenceLock);
if (renderer->availableFenceCount == 0)
{
if (!D3D11_INTERNAL_CreateFence(renderer))
{
SDL_UnlockMutex(renderer->fenceLock);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create fence!");
return 0;
}
}
fence = renderer->availableFences[renderer->availableFenceCount - 1];
renderer->availableFenceCount -= 1;
SDL_UnlockMutex(renderer->fenceLock);
/* Associate the fence with the command buffer */
commandBuffer->fence = fence;
(void)SDL_AtomicIncRef(&commandBuffer->fence->referenceCount);
return 1;
}
static Refresh_CommandBuffer* D3D11_AcquireCommandBuffer(
Refresh_Renderer *driverData
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11CommandBuffer *commandBuffer;
Uint32 i;
SDL_LockMutex(renderer->acquireCommandBufferLock);
commandBuffer = D3D11_INTERNAL_GetInactiveCommandBufferFromPool(renderer);
commandBuffer->graphicsPipeline = NULL;
commandBuffer->computePipeline = NULL;
for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1)
{
commandBuffer->colorTargetResolveTexture[i] = NULL;
commandBuffer->colorTargetResolveSubresourceIndex[i] = 0;
commandBuffer->colorTargetMsaaHandle[i] = NULL;
}
for (i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1)
{
commandBuffer->vertexUniformBufferNeedsReset[i] = SDL_TRUE;
commandBuffer->fragmentUniformBufferNeedsReset[i] = SDL_TRUE;
commandBuffer->computeUniformBufferNeedsReset[i] = SDL_TRUE;
}
commandBuffer->needVertexSamplerBind = SDL_TRUE;
commandBuffer->needVertexResourceBind = SDL_TRUE;
commandBuffer->needFragmentSamplerBind = SDL_TRUE;
commandBuffer->needFragmentResourceBind = SDL_TRUE;
commandBuffer->needComputeUAVBind = SDL_TRUE;
commandBuffer->needComputeSRVBind = SDL_TRUE;
SDL_zeroa(commandBuffer->vertexSamplers);
SDL_zeroa(commandBuffer->vertexShaderResourceViews);
SDL_zeroa(commandBuffer->fragmentSamplers);
SDL_zeroa(commandBuffer->fragmentShaderResourceViews);
SDL_zeroa(commandBuffer->computeShaderResourceViews);
SDL_zeroa(commandBuffer->computeUnorderedAccessViews);
D3D11_INTERNAL_AcquireFence(commandBuffer);
commandBuffer->autoReleaseFence = 1;
SDL_UnlockMutex(renderer->acquireCommandBufferLock);
return (Refresh_CommandBuffer*) commandBuffer;
}
static void D3D11_INTERNAL_PushUniformData(
D3D11CommandBuffer *d3d11CommandBuffer,
Refresh_ShaderStage shaderStage,
Uint32 slotIndex,
void *data,
Uint32 dataLengthInBytes
) {
D3D11Renderer *renderer = d3d11CommandBuffer->renderer;
D3D11UniformBuffer *d3d11UniformBuffer;
ID3D11Buffer *nullBuf = NULL;
Uint32 offsetInConstants, blockSizeInConstants;
Uint32 drawOffset;
if (shaderStage == REFRESH_SHADERSTAGE_VERTEX)
{
d3d11UniformBuffer = d3d11CommandBuffer->vertexUniformBuffers[slotIndex];
}
else if (shaderStage == REFRESH_SHADERSTAGE_FRAGMENT)
{
d3d11UniformBuffer = d3d11CommandBuffer->fragmentUniformBuffers[slotIndex];
}
else if (shaderStage == REFRESH_SHADERSTAGE_COMPUTE)
{
d3d11UniformBuffer = d3d11CommandBuffer->computeUniformBuffers[slotIndex];
}
else
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized shader stage!");
return;
}
d3d11UniformBuffer->currentBlockSize =
D3D11_INTERNAL_NextHighestAlignment(
dataLengthInBytes,
256
);
if (d3d11UniformBuffer->offset + d3d11UniformBuffer->currentBlockSize >= d3d11UniformBuffer->bufferContainer->activeBuffer->size)
{
D3D11_INTERNAL_CycleActiveBuffer(
renderer,
d3d11UniformBuffer->bufferContainer
);
d3d11UniformBuffer->offset = 0;
D3D11_INTERNAL_TrackBuffer(
d3d11CommandBuffer,
d3d11UniformBuffer->bufferContainer->activeBuffer
);
}
drawOffset = d3d11UniformBuffer->offset;
D3D11_INTERNAL_SetUniformBufferData(
renderer,
d3d11CommandBuffer,
d3d11UniformBuffer->bufferContainer->activeBuffer,
d3d11UniformBuffer->offset,
data,
dataLengthInBytes
);
d3d11UniformBuffer->offset += d3d11UniformBuffer->currentBlockSize;
offsetInConstants = drawOffset / 16;
blockSizeInConstants = d3d11UniformBuffer->currentBlockSize / 16;
if (shaderStage == REFRESH_SHADERSTAGE_VERTEX)
{
/* stupid workaround for god awful D3D11 drivers
* see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-vssetconstantbuffers1#calling-vssetconstantbuffers1-with-command-list-emulation
*/
ID3D11DeviceContext1_VSSetConstantBuffers(
d3d11CommandBuffer->context,
slotIndex,
1,
&nullBuf
);
ID3D11DeviceContext1_VSSetConstantBuffers1(
d3d11CommandBuffer->context,
slotIndex,
1,
&d3d11UniformBuffer->bufferContainer->activeBuffer->handle,
&offsetInConstants,
&blockSizeInConstants
);
}
else if (shaderStage == REFRESH_SHADERSTAGE_FRAGMENT)
{
/* stupid workaround for god awful D3D11 drivers
* see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-vssetconstantbuffers1#calling-vssetconstantbuffers1-with-command-list-emulation
*/
ID3D11DeviceContext1_PSSetConstantBuffers(
d3d11CommandBuffer->context,
slotIndex,
1,
&nullBuf
);
ID3D11DeviceContext1_PSSetConstantBuffers1(
d3d11CommandBuffer->context,
slotIndex,
1,
&d3d11UniformBuffer->bufferContainer->activeBuffer->handle,
&offsetInConstants,
&blockSizeInConstants
);
}
else if (shaderStage == REFRESH_SHADERSTAGE_COMPUTE)
{
/* stupid workaround for god awful D3D11 drivers
* see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-vssetconstantbuffers1#calling-vssetconstantbuffers1-with-command-list-emulation
*/
ID3D11DeviceContext1_CSSetConstantBuffers(
d3d11CommandBuffer->context,
slotIndex,
1,
&nullBuf
);
ID3D11DeviceContext1_CSSetConstantBuffers1(
d3d11CommandBuffer->context,
slotIndex,
1,
&d3d11UniformBuffer->bufferContainer->activeBuffer->handle,
&offsetInConstants,
&blockSizeInConstants
);
}
else
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized shader stage!");
}
}
static void D3D11_BeginRenderPass(
Refresh_CommandBuffer *commandBuffer,
Refresh_ColorAttachmentInfo *colorAttachmentInfos,
Uint32 colorAttachmentCount,
Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer;
ID3D11RenderTargetView* rtvs[MAX_COLOR_TARGET_BINDINGS];
ID3D11DepthStencilView *dsv = NULL;
Uint32 vpWidth = SDL_MAX_UINT32;
Uint32 vpHeight = SDL_MAX_UINT32;
D3D11_VIEWPORT viewport;
D3D11_RECT scissorRect;
d3d11CommandBuffer->needVertexSamplerBind = SDL_TRUE;
d3d11CommandBuffer->needVertexResourceBind = SDL_TRUE;
d3d11CommandBuffer->needFragmentSamplerBind = SDL_TRUE;
d3d11CommandBuffer->needFragmentResourceBind = SDL_TRUE;
/* Clear the bound targets for the current command buffer */
for (Uint32 i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1)
{
d3d11CommandBuffer->colorTargetResolveTexture[i] = NULL;
d3d11CommandBuffer->colorTargetResolveSubresourceIndex[i] = 0;
d3d11CommandBuffer->colorTargetMsaaHandle[i] = NULL;
}
/* Set up the new color target bindings */
for (Uint32 i = 0; i < colorAttachmentCount; i += 1)
{
D3D11TextureContainer *container = (D3D11TextureContainer*) colorAttachmentInfos[i].textureSlice.texture;
D3D11TextureSubresource *subresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite(
renderer,
container,
colorAttachmentInfos[i].textureSlice.layer,
colorAttachmentInfos[i].textureSlice.mipLevel,
colorAttachmentInfos[i].cycle
);
if (subresource->msaaHandle != NULL)
{
d3d11CommandBuffer->colorTargetResolveTexture[i] = subresource->parent;
d3d11CommandBuffer->colorTargetResolveSubresourceIndex[i] = subresource->index;
d3d11CommandBuffer->colorTargetMsaaHandle[i] = subresource->msaaHandle;
rtvs[i] = subresource->msaaTargetView;
}
else
{
rtvs[i] = subresource->colorTargetView;
}
D3D11_INTERNAL_TrackTextureSubresource(
d3d11CommandBuffer,
subresource
);
}
/* Get the DSV for the depth stencil attachment, if applicable */
if (depthStencilAttachmentInfo != NULL)
{
D3D11TextureContainer *container = (D3D11TextureContainer*) depthStencilAttachmentInfo->textureSlice.texture;
D3D11TextureSubresource *subresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite(
renderer,
container,
depthStencilAttachmentInfo->textureSlice.layer,
depthStencilAttachmentInfo->textureSlice.mipLevel,
depthStencilAttachmentInfo->cycle
);
dsv = subresource->depthStencilTargetView;
D3D11_INTERNAL_TrackTextureSubresource(
d3d11CommandBuffer,
subresource
);
}
/* Actually set the RTs */
ID3D11DeviceContext_OMSetRenderTargets(
d3d11CommandBuffer->context,
colorAttachmentCount,
colorAttachmentCount > 0 ? rtvs : NULL,
dsv
);
/* Perform load ops on the RTs */
for (Uint32 i = 0; i < colorAttachmentCount; i += 1)
{
if (colorAttachmentInfos[i].loadOp == REFRESH_LOADOP_CLEAR)
{
float clearColors[] =
{
colorAttachmentInfos[i].clearColor.r,
colorAttachmentInfos[i].clearColor.g,
colorAttachmentInfos[i].clearColor.b,
colorAttachmentInfos[i].clearColor.a
};
ID3D11DeviceContext_ClearRenderTargetView(
d3d11CommandBuffer->context,
rtvs[i],
clearColors
);
}
}
if (depthStencilAttachmentInfo != NULL)
{
D3D11_CLEAR_FLAG 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(
d3d11CommandBuffer->context,
dsv,
dsClearFlags,
depthStencilAttachmentInfo->depthStencilClearValue.depth,
(Uint8) depthStencilAttachmentInfo->depthStencilClearValue.stencil
);
}
}
/* The viewport cannot be larger than the smallest attachment. */
for (Uint32 i = 0; i < colorAttachmentCount; i += 1)
{
D3D11Texture *texture = ((D3D11TextureContainer*) colorAttachmentInfos[i].textureSlice.texture)->activeTexture;
Uint32 w = texture->width >> colorAttachmentInfos[i].textureSlice.mipLevel;
Uint32 h = texture->height >> colorAttachmentInfos[i].textureSlice.mipLevel;
if (w < vpWidth)
{
vpWidth = w;
}
if (h < vpHeight)
{
vpHeight = h;
}
}
if (depthStencilAttachmentInfo != NULL)
{
D3D11Texture *texture = ((D3D11TextureContainer*) depthStencilAttachmentInfo->textureSlice.texture)->activeTexture;
Uint32 w = texture->width >> depthStencilAttachmentInfo->textureSlice.mipLevel;
Uint32 h = texture->height >> depthStencilAttachmentInfo->textureSlice.mipLevel;
if (w < vpWidth)
{
vpWidth = w;
}
if (h < vpHeight)
{
vpHeight = h;
}
}
/* Set default viewport and scissor state */
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = (FLOAT) vpWidth;
viewport.Height = (FLOAT) vpHeight;
viewport.MinDepth = 0;
viewport.MaxDepth = 1;
ID3D11DeviceContext_RSSetViewports(
d3d11CommandBuffer->context,
1,
&viewport
);
scissorRect.left = 0;
scissorRect.right = (LONG) viewport.Width;
scissorRect.top = 0;
scissorRect.bottom = (LONG) viewport.Height;
ID3D11DeviceContext_RSSetScissorRects(
d3d11CommandBuffer->context,
1,
&scissorRect
);
}
static void D3D11_BindGraphicsPipeline(
Refresh_CommandBuffer *commandBuffer,
Refresh_GraphicsPipeline *graphicsPipeline
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11GraphicsPipeline *pipeline = (D3D11GraphicsPipeline*) graphicsPipeline;
Uint32 offsetInConstants, blockSizeInConstants;
Uint32 i;
d3d11CommandBuffer->graphicsPipeline = pipeline;
ID3D11DeviceContext_OMSetBlendState(
d3d11CommandBuffer->context,
pipeline->colorAttachmentBlendState,
pipeline->blendConstants,
pipeline->multisampleState.sampleMask
);
ID3D11DeviceContext_OMSetDepthStencilState(
d3d11CommandBuffer->context,
pipeline->depthStencilState,
pipeline->stencilRef
);
ID3D11DeviceContext_IASetPrimitiveTopology(
d3d11CommandBuffer->context,
RefreshToD3D11_PrimitiveType[pipeline->primitiveType]
);
ID3D11DeviceContext_IASetInputLayout(
d3d11CommandBuffer->context,
pipeline->inputLayout
);
ID3D11DeviceContext_RSSetState(
d3d11CommandBuffer->context,
pipeline->rasterizerState
);
ID3D11DeviceContext_VSSetShader(
d3d11CommandBuffer->context,
pipeline->vertexShader,
NULL,
0
);
ID3D11DeviceContext_PSSetShader(
d3d11CommandBuffer->context,
pipeline->fragmentShader,
NULL,
0
);
for (i = d3d11CommandBuffer->initializedVertexUniformBufferCount; i < pipeline->vertexUniformBufferCount; i += 1)
{
d3d11CommandBuffer->vertexUniformBuffers[i] = D3D11_INTERNAL_CreateUniformBuffer(d3d11CommandBuffer->renderer, UNIFORM_BUFFER_SIZE);
d3d11CommandBuffer->initializedVertexUniformBufferCount += 1;
}
for (i = d3d11CommandBuffer->initializedFragmentUniformBufferCount; i < pipeline->fragmentUniformBufferCount; i += 1)
{
d3d11CommandBuffer->fragmentUniformBuffers[i] = D3D11_INTERNAL_CreateUniformBuffer(d3d11CommandBuffer->renderer, UNIFORM_BUFFER_SIZE);
d3d11CommandBuffer->initializedFragmentUniformBufferCount += 1;
}
for (i = 0; i < pipeline->vertexUniformBufferCount; i += 1)
{
/* The first map call on a deferred context must be DISCARD so we reset here */
if (d3d11CommandBuffer->vertexUniformBufferNeedsReset[i])
{
D3D11_INTERNAL_CycleActiveBuffer(
d3d11CommandBuffer->renderer,
d3d11CommandBuffer->vertexUniformBuffers[i]->bufferContainer
);
d3d11CommandBuffer->vertexUniformBuffers[i]->offset = 0;
d3d11CommandBuffer->vertexUniformBuffers[i]->drawOffset = 0;
d3d11CommandBuffer->vertexUniformBufferNeedsReset[i] = SDL_FALSE;
}
/* stupid workaround for god awful D3D11 drivers
* see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-vssetconstantbuffers1#calling-vssetconstantbuffers1-with-command-list-emulation
*/
ID3D11DeviceContext1_VSSetConstantBuffers(
d3d11CommandBuffer->context,
i,
1,
&d3d11CommandBuffer->vertexUniformBuffers[i]->bufferContainer->activeBuffer->handle
);
offsetInConstants = d3d11CommandBuffer->vertexUniformBuffers[i]->drawOffset / 16;
blockSizeInConstants = d3d11CommandBuffer->vertexUniformBuffers[i]->currentBlockSize / 16;
ID3D11DeviceContext1_VSSetConstantBuffers1(
d3d11CommandBuffer->context,
i,
1,
&d3d11CommandBuffer->vertexUniformBuffers[i]->bufferContainer->activeBuffer->handle,
&offsetInConstants,
&blockSizeInConstants
);
D3D11_INTERNAL_TrackBuffer(
d3d11CommandBuffer,
d3d11CommandBuffer->vertexUniformBuffers[i]->bufferContainer->activeBuffer
);
}
for (i = 0; i < pipeline->fragmentUniformBufferCount; i += 1)
{
/* The first map call on a deferred context must be DISCARD so we reset here */
if (d3d11CommandBuffer->fragmentUniformBufferNeedsReset[i])
{
D3D11_INTERNAL_CycleActiveBuffer(
d3d11CommandBuffer->renderer,
d3d11CommandBuffer->fragmentUniformBuffers[i]->bufferContainer
);
d3d11CommandBuffer->fragmentUniformBuffers[i]->offset = 0;
d3d11CommandBuffer->fragmentUniformBuffers[i]->drawOffset = 0;
d3d11CommandBuffer->fragmentUniformBufferNeedsReset[i] = SDL_FALSE;
}
/* stupid workaround for god awful D3D11 drivers
* see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-pssetconstantbuffers1#calling-pssetconstantbuffers1-with-command-list-emulation
*/
ID3D11DeviceContext1_PSSetConstantBuffers(
d3d11CommandBuffer->context,
i,
1,
&d3d11CommandBuffer->fragmentUniformBuffers[i]->bufferContainer->activeBuffer->handle
);
offsetInConstants = d3d11CommandBuffer->fragmentUniformBuffers[i]->drawOffset / 16;
blockSizeInConstants = d3d11CommandBuffer->fragmentUniformBuffers[i]->currentBlockSize / 16;
ID3D11DeviceContext1_PSSetConstantBuffers1(
d3d11CommandBuffer->context,
i,
1,
&d3d11CommandBuffer->fragmentUniformBuffers[i]->bufferContainer->activeBuffer->handle,
&offsetInConstants,
&blockSizeInConstants
);
D3D11_INTERNAL_TrackBuffer(
d3d11CommandBuffer,
d3d11CommandBuffer->fragmentUniformBuffers[i]->bufferContainer->activeBuffer
);
}
}
static void D3D11_SetViewport(
Refresh_CommandBuffer *commandBuffer,
Refresh_Viewport *viewport
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11_VIEWPORT vp =
{
viewport->x,
viewport->y,
viewport->w,
viewport->h,
viewport->minDepth,
viewport->maxDepth
};
ID3D11DeviceContext_RSSetViewports(
d3d11CommandBuffer->context,
1,
&vp
);
}
static void D3D11_SetScissor(
Refresh_CommandBuffer *commandBuffer,
Refresh_Rect *scissor
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11_RECT rect =
{
scissor->x,
scissor->y,
scissor->x + scissor->w,
scissor->y + scissor->h
};
ID3D11DeviceContext_RSSetScissorRects(
d3d11CommandBuffer->context,
1,
&rect
);
}
static void D3D11_BindVertexBuffers(
Refresh_CommandBuffer *commandBuffer,
Uint32 firstBinding,
Refresh_BufferBinding *pBindings,
Uint32 bindingCount
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
ID3D11Buffer *bufferHandles[MAX_BUFFER_BINDINGS];
UINT bufferOffsets[MAX_BUFFER_BINDINGS];
for (Uint32 i = 0; i < bindingCount; i += 1)
{
D3D11Buffer *currentBuffer = ((D3D11BufferContainer*) pBindings[i].buffer)->activeBuffer;
bufferHandles[i] = currentBuffer->handle;
bufferOffsets[i] = pBindings[i].offset;
D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, currentBuffer);
}
ID3D11DeviceContext_IASetVertexBuffers(
d3d11CommandBuffer->context,
firstBinding,
bindingCount,
bufferHandles,
&d3d11CommandBuffer->graphicsPipeline->vertexStrides[firstBinding],
bufferOffsets
);
}
static void D3D11_BindIndexBuffer(
Refresh_CommandBuffer *commandBuffer,
Refresh_BufferBinding *pBinding,
Refresh_IndexElementSize indexElementSize
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11Buffer *d3d11Buffer = ((D3D11BufferContainer*) pBinding->buffer)->activeBuffer;
D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, d3d11Buffer);
ID3D11DeviceContext_IASetIndexBuffer(
d3d11CommandBuffer->context,
d3d11Buffer->handle,
RefreshToD3D11_IndexType[indexElementSize],
(UINT) pBinding->offset
);
}
static void D3D11_BindVertexSamplers(
Refresh_CommandBuffer *commandBuffer,
Uint32 firstSlot,
Refresh_TextureSamplerBinding *textureSamplerBindings,
Uint32 bindingCount
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11TextureContainer *textureContainer;
Uint32 i, j;
for (i = 0; i < bindingCount; i += 1)
{
textureContainer = (D3D11TextureContainer*) textureSamplerBindings[i].texture;
for (j = 0; j < textureContainer->activeTexture->subresourceCount; j += 1)
{
D3D11_INTERNAL_TrackTextureSubresource(
d3d11CommandBuffer,
&textureContainer->activeTexture->subresources[j]
);
}
d3d11CommandBuffer->vertexSamplers[firstSlot + i] =
((D3D11Sampler*) textureSamplerBindings[i].sampler)->handle;
d3d11CommandBuffer->vertexShaderResourceViews[firstSlot + i] =
textureContainer->activeTexture->shaderView;
}
d3d11CommandBuffer->needVertexSamplerBind = SDL_TRUE;
d3d11CommandBuffer->needVertexResourceBind = SDL_TRUE;
}
static void D3D11_BindVertexStorageTextures(
Refresh_CommandBuffer *commandBuffer,
Uint32 firstSlot,
Refresh_TextureSlice *storageTextureSlices,
Uint32 bindingCount
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11TextureContainer *textureContainer;
D3D11TextureSubresource *textureSubresource;
Uint32 i;
for (i = 0; i < bindingCount; i += 1)
{
textureContainer = (D3D11TextureContainer*) storageTextureSlices[i].texture;
textureSubresource = D3D11_INTERNAL_FetchTextureSubresource(
textureContainer->activeTexture,
storageTextureSlices[i].layer,
storageTextureSlices[i].mipLevel
);
D3D11_INTERNAL_TrackTextureSubresource(
d3d11CommandBuffer,
textureSubresource
);
d3d11CommandBuffer->vertexShaderResourceViews[
firstSlot + i +
d3d11CommandBuffer->graphicsPipeline->vertexSamplerCount
] = textureSubresource->srv;
}
d3d11CommandBuffer->needVertexResourceBind = SDL_TRUE;
}
static void D3D11_BindVertexStorageBuffers(
Refresh_CommandBuffer *commandBuffer,
Uint32 firstSlot,
Refresh_Buffer **storageBuffers,
Uint32 bindingCount
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11BufferContainer *bufferContainer;
Uint32 i;
for (i = 0; i < bindingCount; i += 1)
{
bufferContainer = (D3D11BufferContainer*) storageBuffers[i];
D3D11_INTERNAL_TrackBuffer(
d3d11CommandBuffer,
bufferContainer->activeBuffer
);
d3d11CommandBuffer->vertexShaderResourceViews[
firstSlot + i +
d3d11CommandBuffer->graphicsPipeline->vertexSamplerCount +
d3d11CommandBuffer->graphicsPipeline->vertexStorageTextureCount
] = bufferContainer->activeBuffer->srv;
}
d3d11CommandBuffer->needVertexResourceBind = SDL_TRUE;
}
static void D3D11_BindFragmentSamplers(
Refresh_CommandBuffer *commandBuffer,
Uint32 firstSlot,
Refresh_TextureSamplerBinding *textureSamplerBindings,
Uint32 bindingCount
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11TextureContainer *textureContainer;
Uint32 i, j;
for (i = 0; i < bindingCount; i += 1)
{
textureContainer = (D3D11TextureContainer*) textureSamplerBindings[i].texture;
for (j = 0; j < textureContainer->activeTexture->subresourceCount; j += 1)
{
D3D11_INTERNAL_TrackTextureSubresource(
d3d11CommandBuffer,
&textureContainer->activeTexture->subresources[j]
);
}
d3d11CommandBuffer->fragmentSamplers[firstSlot + i] =
((D3D11Sampler*) textureSamplerBindings[i].sampler)->handle;
d3d11CommandBuffer->fragmentShaderResourceViews[firstSlot + i] =
textureContainer->activeTexture->shaderView;
}
d3d11CommandBuffer->needFragmentSamplerBind = SDL_TRUE;
d3d11CommandBuffer->needFragmentResourceBind = SDL_TRUE;
}
static void D3D11_BindFragmentStorageTextures(
Refresh_CommandBuffer *commandBuffer,
Uint32 firstSlot,
Refresh_TextureSlice *storageTextureSlices,
Uint32 bindingCount
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11TextureContainer *textureContainer;
D3D11TextureSubresource *textureSubresource;
Uint32 i;
for (i = 0; i < bindingCount; i += 1)
{
textureContainer = (D3D11TextureContainer*) storageTextureSlices[i].texture;
textureSubresource = D3D11_INTERNAL_FetchTextureSubresource(
textureContainer->activeTexture,
storageTextureSlices[i].layer,
storageTextureSlices[i].mipLevel
);
D3D11_INTERNAL_TrackTextureSubresource(
d3d11CommandBuffer,
textureSubresource
);
d3d11CommandBuffer->fragmentShaderResourceViews[
firstSlot + i +
d3d11CommandBuffer->graphicsPipeline->fragmentSamplerCount
] = textureSubresource->srv;
}
d3d11CommandBuffer->needFragmentResourceBind = SDL_TRUE;
}
static void D3D11_BindFragmentStorageBuffers(
Refresh_CommandBuffer *commandBuffer,
Uint32 firstSlot,
Refresh_Buffer **storageBuffers,
Uint32 bindingCount
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11BufferContainer *bufferContainer;
Uint32 i;
for (i = 0; i < bindingCount; i += 1)
{
bufferContainer = (D3D11BufferContainer*) storageBuffers[i];
D3D11_INTERNAL_TrackBuffer(
d3d11CommandBuffer,
bufferContainer->activeBuffer
);
d3d11CommandBuffer->fragmentShaderResourceViews[
firstSlot + i +
d3d11CommandBuffer->graphicsPipeline->fragmentSamplerCount +
d3d11CommandBuffer->graphicsPipeline->fragmentStorageTextureCount
] = bufferContainer->activeBuffer->srv;
}
d3d11CommandBuffer->needFragmentResourceBind = SDL_TRUE;
}
static void D3D11_INTERNAL_BindGraphicsResources(
D3D11CommandBuffer *commandBuffer
) {
D3D11GraphicsPipeline *graphicsPipeline = commandBuffer->graphicsPipeline;
Uint32 vertexResourceCount =
graphicsPipeline->vertexSamplerCount +
graphicsPipeline->vertexStorageTextureCount +
graphicsPipeline->vertexStorageBufferCount;
Uint32 fragmentResourceCount =
graphicsPipeline->fragmentSamplerCount +
graphicsPipeline->fragmentStorageTextureCount +
graphicsPipeline->fragmentStorageBufferCount;
if (commandBuffer->needVertexSamplerBind)
{
if (graphicsPipeline->vertexSamplerCount > 0)
{
ID3D11DeviceContext_VSSetSamplers(
commandBuffer->context,
0,
graphicsPipeline->vertexSamplerCount,
commandBuffer->vertexSamplers
);
}
commandBuffer->needVertexSamplerBind = SDL_FALSE;
}
if (commandBuffer->needVertexResourceBind)
{
if (vertexResourceCount > 0)
{
ID3D11DeviceContext_VSSetShaderResources(
commandBuffer->context,
0,
vertexResourceCount,
commandBuffer->vertexShaderResourceViews
);
}
commandBuffer->needVertexResourceBind = SDL_FALSE;
}
if (commandBuffer->needFragmentSamplerBind)
{
if (graphicsPipeline->fragmentSamplerCount > 0)
{
ID3D11DeviceContext_PSSetSamplers(
commandBuffer->context,
0,
graphicsPipeline->fragmentSamplerCount,
commandBuffer->fragmentSamplers
);
}
commandBuffer->needFragmentSamplerBind = SDL_FALSE;
}
if (commandBuffer->needFragmentResourceBind)
{
if (fragmentResourceCount > 0)
{
ID3D11DeviceContext_PSSetShaderResources(
commandBuffer->context,
0,
fragmentResourceCount,
commandBuffer->fragmentShaderResourceViews
);
}
commandBuffer->needFragmentResourceBind = SDL_FALSE;
}
}
static void D3D11_DrawIndexedPrimitives(
Refresh_CommandBuffer *commandBuffer,
Uint32 baseVertex,
Uint32 startIndex,
Uint32 primitiveCount,
Uint32 instanceCount
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11_INTERNAL_BindGraphicsResources(d3d11CommandBuffer);
ID3D11DeviceContext_DrawIndexedInstanced(
d3d11CommandBuffer->context,
PrimitiveVerts(d3d11CommandBuffer->graphicsPipeline->primitiveType, primitiveCount),
instanceCount,
startIndex,
baseVertex,
0
);
}
static void D3D11_DrawPrimitives(
Refresh_CommandBuffer *commandBuffer,
Uint32 vertexStart,
Uint32 primitiveCount
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11_INTERNAL_BindGraphicsResources(d3d11CommandBuffer);
ID3D11DeviceContext_Draw(
d3d11CommandBuffer->context,
PrimitiveVerts(d3d11CommandBuffer->graphicsPipeline->primitiveType, primitiveCount),
vertexStart
);
}
static void D3D11_DrawPrimitivesIndirect(
Refresh_CommandBuffer *commandBuffer,
Refresh_Buffer *buffer,
Uint32 offsetInBytes,
Uint32 drawCount,
Uint32 stride
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11_INTERNAL_BindGraphicsResources(d3d11CommandBuffer);
D3D11Buffer *d3d11Buffer = ((D3D11BufferContainer*) buffer)->activeBuffer;
/* D3D11: "We have multi-draw at home!"
* Multi-draw at home:
*/
for (Uint32 i = 0; i < drawCount; i += 1)
{
ID3D11DeviceContext_DrawInstancedIndirect(
d3d11CommandBuffer->context,
d3d11Buffer->handle,
offsetInBytes + (stride * i)
);
}
D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, d3d11Buffer);
}
static void D3D11_DrawIndexedPrimitivesIndirect(
Refresh_CommandBuffer *commandBuffer,
Refresh_Buffer *buffer,
Uint32 offsetInBytes,
Uint32 drawCount,
Uint32 stride
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11_INTERNAL_BindGraphicsResources(d3d11CommandBuffer);
D3D11Buffer *d3d11Buffer = ((D3D11BufferContainer*) buffer)->activeBuffer;
/* D3D11: "We have multi-draw at home!"
* Multi-draw at home:
*/
for (Uint32 i = 0; i < drawCount; i += 1)
{
ID3D11DeviceContext_DrawIndexedInstancedIndirect(
d3d11CommandBuffer->context,
d3d11Buffer->handle,
offsetInBytes + (stride * i)
);
}
D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, d3d11Buffer);
}
static void D3D11_EndRenderPass(
Refresh_CommandBuffer *commandBuffer
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
Uint32 i;
/* Set render target slots to NULL to avoid NULL set behavior */
/* https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-pssetshaderresources */
ID3D11DeviceContext_OMSetRenderTargets(
d3d11CommandBuffer->context,
MAX_COLOR_TARGET_BINDINGS,
nullRTVs,
NULL
);
/* Resolve MSAA color render targets */
for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1)
{
if (d3d11CommandBuffer->colorTargetMsaaHandle[i] != NULL)
{
ID3D11DeviceContext_ResolveSubresource(
d3d11CommandBuffer->context,
d3d11CommandBuffer->colorTargetResolveTexture[i]->handle,
d3d11CommandBuffer->colorTargetResolveSubresourceIndex[i],
d3d11CommandBuffer->colorTargetMsaaHandle[i],
0,
RefreshToD3D11_TextureFormat[d3d11CommandBuffer->colorTargetResolveTexture[i]->format]
);
}
}
}
static void D3D11_PushVertexUniformData(
Refresh_CommandBuffer *commandBuffer,
Uint32 slotIndex,
void *data,
Uint32 dataLengthInBytes
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
if (slotIndex >= d3d11CommandBuffer->graphicsPipeline->vertexUniformBufferCount)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No vertex uniforms exist on slot %i for this pipeline", slotIndex);
return;
}
D3D11_INTERNAL_PushUniformData(
d3d11CommandBuffer,
REFRESH_SHADERSTAGE_VERTEX,
slotIndex,
data,
dataLengthInBytes
);
}
static void D3D11_PushFragmentUniformData(
Refresh_CommandBuffer *commandBuffer,
Uint32 slotIndex,
void *data,
Uint32 dataLengthInBytes
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
if (slotIndex >= d3d11CommandBuffer->graphicsPipeline->fragmentUniformBufferCount)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No fragment uniforms exist on slot %i for this pipeline", slotIndex);
return;
}
D3D11_INTERNAL_PushUniformData(
d3d11CommandBuffer,
REFRESH_SHADERSTAGE_FRAGMENT,
slotIndex,
data,
dataLengthInBytes
);
}
/* Blit */
static void D3D11_Blit(
Refresh_CommandBuffer *commandBuffer,
Refresh_TextureRegion *source,
Refresh_TextureRegion *destination,
Refresh_Filter filterMode,
SDL_bool cycle
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer;
D3D11TextureContainer *sourceTextureContainer = (D3D11TextureContainer*) source->textureSlice.texture;
D3D11TextureContainer *destinationTextureContainer = (D3D11TextureContainer*) destination->textureSlice.texture;
Refresh_ColorAttachmentInfo colorAttachmentInfo;
Refresh_Viewport viewport;
Refresh_TextureSamplerBinding textureSamplerBinding;
if (destinationTextureContainer->activeTexture->depth > 1)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "3D blit destination not implemented!");
return;
}
/* Unused */
colorAttachmentInfo.clearColor.r = 0;
colorAttachmentInfo.clearColor.g = 0;
colorAttachmentInfo.clearColor.b = 0;
colorAttachmentInfo.clearColor.a = 0;
/* If the entire destination is blitted, we don't have to load */
if (
destinationTextureContainer->activeTexture->layerCount == 1 &&
destinationTextureContainer->activeTexture->levelCount == 1 &&
destination->w == destinationTextureContainer->activeTexture->width &&
destination->h == destinationTextureContainer->activeTexture->height &&
destination->d == destinationTextureContainer->activeTexture->depth
) {
colorAttachmentInfo.loadOp = REFRESH_LOADOP_DONT_CARE;
}
else
{
colorAttachmentInfo.loadOp = REFRESH_LOADOP_LOAD;
}
colorAttachmentInfo.storeOp = REFRESH_STOREOP_STORE;
colorAttachmentInfo.textureSlice = destination->textureSlice;
colorAttachmentInfo.cycle = cycle;
D3D11_BeginRenderPass(
commandBuffer,
&colorAttachmentInfo,
1,
NULL
);
viewport.x = (float) destination->x;
viewport.y = (float) destination->y;
viewport.w = (float) destination->w;
viewport.h = (float) destination->h;
viewport.minDepth = 0;
viewport.maxDepth = 1;
D3D11_SetViewport(
commandBuffer,
&viewport
);
if (
sourceTextureContainer->activeTexture->layerCount == 1 &&
sourceTextureContainer->activeTexture->depth == 1
) {
/* 2D source */
D3D11_BindGraphicsPipeline(
commandBuffer,
renderer->blitFrom2DPipeline
);
}
else if (
sourceTextureContainer->activeTexture->layerCount > 1
) {
/* 2D array source */
D3D11_BindGraphicsPipeline(
commandBuffer,
renderer->blitFrom2DArrayPipeline
);
D3D11_PushFragmentUniformData(
commandBuffer,
0,
&source->textureSlice.layer,
sizeof(Uint32)
);
}
else
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "3D blit source not implemented!");
return;
}
textureSamplerBinding.texture = source->textureSlice.texture;
textureSamplerBinding.sampler =
filterMode == REFRESH_FILTER_NEAREST ?
renderer->blitNearestSampler :
renderer->blitLinearSampler;
if (((D3D11TextureContainer*) textureSamplerBinding.texture)->activeTexture->shaderView == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Blit source texture must be created with SAMPLER bit!");
}
D3D11_BindFragmentSamplers(
commandBuffer,
0,
&textureSamplerBinding,
1
);
D3D11_DrawPrimitives(
commandBuffer,
0,
1
);
D3D11_EndRenderPass(commandBuffer);
}
/* Compute State */
static void D3D11_BeginComputePass(
Refresh_CommandBuffer *commandBuffer,
Refresh_StorageTextureReadWriteBinding *storageTextureBindings,
Uint32 storageTextureBindingCount,
Refresh_StorageBufferReadWriteBinding *storageBufferBindings,
Uint32 storageBufferBindingCount
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11TextureContainer *textureContainer;
D3D11TextureSubresource *textureSubresource;
D3D11BufferContainer *bufferContainer;
D3D11Buffer *buffer;
Uint32 i;
for (i = 0; i < storageTextureBindingCount; i += 1)
{
textureContainer = (D3D11TextureContainer*) storageTextureBindings[i].textureSlice.texture;
textureSubresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite(
d3d11CommandBuffer->renderer,
textureContainer,
storageTextureBindings[i].textureSlice.layer,
storageTextureBindings[i].textureSlice.mipLevel,
storageTextureBindings[i].cycle
);
D3D11_INTERNAL_TrackTextureSubresource(
d3d11CommandBuffer,
textureSubresource
);
d3d11CommandBuffer->computeUnorderedAccessViews[i] =
textureSubresource->uav;
}
for (i = 0; i < storageBufferBindingCount; i += 1)
{
bufferContainer = (D3D11BufferContainer*) storageBufferBindings[i].buffer;
buffer = D3D11_INTERNAL_PrepareBufferForWrite(
d3d11CommandBuffer->renderer,
bufferContainer,
storageBufferBindings[i].cycle
);
D3D11_INTERNAL_TrackBuffer(
d3d11CommandBuffer,
buffer
);
d3d11CommandBuffer->computeUnorderedAccessViews[
i + storageTextureBindingCount
] = buffer->uav;
}
d3d11CommandBuffer->needComputeUAVBind = SDL_TRUE;
}
static void D3D11_BindComputePipeline(
Refresh_CommandBuffer *commandBuffer,
Refresh_ComputePipeline *computePipeline
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11ComputePipeline *pipeline = (D3D11ComputePipeline*) computePipeline;
Uint32 offsetInConstants, blockSizeInConstants;
Uint32 i;
d3d11CommandBuffer->computePipeline = pipeline;
ID3D11DeviceContext_CSSetShader(
d3d11CommandBuffer->context,
pipeline->computeShader,
NULL,
0
);
for (i = d3d11CommandBuffer->initializedComputeUniformBufferCount; i < pipeline->uniformBufferCount; i += 1)
{
d3d11CommandBuffer->computeUniformBuffers[i] = D3D11_INTERNAL_CreateUniformBuffer(d3d11CommandBuffer->renderer, UNIFORM_BUFFER_SIZE);
d3d11CommandBuffer->initializedComputeUniformBufferCount += 1;
}
for (i = 0; i < pipeline->uniformBufferCount; i += 1)
{
/* The first map call on a deferred context must be DISCARD so we reset here */
if (d3d11CommandBuffer->computeUniformBufferNeedsReset[i])
{
D3D11_INTERNAL_CycleActiveBuffer(
d3d11CommandBuffer->renderer,
d3d11CommandBuffer->computeUniformBuffers[i]->bufferContainer
);
d3d11CommandBuffer->computeUniformBuffers[i]->offset = 0;
d3d11CommandBuffer->computeUniformBuffers[i]->drawOffset = 0;
d3d11CommandBuffer->computeUniformBufferNeedsReset[i] = SDL_FALSE;
}
/* stupid workaround for god awful D3D11 drivers
* see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-vssetconstantbuffers1#calling-vssetconstantbuffers1-with-command-list-emulation
*/
ID3D11DeviceContext1_CSSetConstantBuffers(
d3d11CommandBuffer->context,
i,
1,
&d3d11CommandBuffer->computeUniformBuffers[i]->bufferContainer->activeBuffer->handle
);
offsetInConstants = d3d11CommandBuffer->computeUniformBuffers[i]->drawOffset / 16;
blockSizeInConstants = d3d11CommandBuffer->computeUniformBuffers[i]->currentBlockSize / 16;
ID3D11DeviceContext1_CSSetConstantBuffers1(
d3d11CommandBuffer->context,
i,
1,
&d3d11CommandBuffer->computeUniformBuffers[i]->bufferContainer->activeBuffer->handle,
&offsetInConstants,
&blockSizeInConstants
);
D3D11_INTERNAL_TrackBuffer(
d3d11CommandBuffer,
d3d11CommandBuffer->computeUniformBuffers[i]->bufferContainer->activeBuffer
);
}
}
static void D3D11_BindComputeStorageTextures(
Refresh_CommandBuffer *commandBuffer,
Uint32 firstSlot,
Refresh_TextureSlice *storageTextureSlices,
Uint32 bindingCount
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11TextureContainer *textureContainer;
D3D11TextureSubresource *textureSubresource;
Uint32 i;
for (i = 0; i < bindingCount; i += 1)
{
textureContainer = (D3D11TextureContainer*) storageTextureSlices[i].texture;
textureSubresource = D3D11_INTERNAL_FetchTextureSubresource(
textureContainer->activeTexture,
storageTextureSlices[i].layer,
storageTextureSlices[i].mipLevel
);
D3D11_INTERNAL_TrackTextureSubresource(
d3d11CommandBuffer,
textureSubresource
);
d3d11CommandBuffer->computeShaderResourceViews[firstSlot + i] =
textureSubresource->srv;
}
d3d11CommandBuffer->needComputeSRVBind = SDL_TRUE;
}
static void D3D11_BindComputeStorageBuffers(
Refresh_CommandBuffer *commandBuffer,
Uint32 firstSlot,
Refresh_Buffer **storageBuffers,
Uint32 bindingCount
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11BufferContainer *bufferContainer;
Uint32 i;
for (i = 0; i < bindingCount; i += 1)
{
bufferContainer = (D3D11BufferContainer*) storageBuffers[i];
D3D11_INTERNAL_TrackBuffer(
d3d11CommandBuffer,
bufferContainer->activeBuffer
);
d3d11CommandBuffer->computeShaderResourceViews[
firstSlot + i +
d3d11CommandBuffer->computePipeline->readOnlyStorageTextureCount
] = bufferContainer->activeBuffer->srv;
}
d3d11CommandBuffer->needComputeSRVBind = SDL_TRUE;
}
static void D3D11_PushComputeUniformData(
Refresh_CommandBuffer *commandBuffer,
Uint32 slotIndex,
void *data,
Uint32 dataLengthInBytes
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
if (slotIndex >= d3d11CommandBuffer->computePipeline->uniformBufferCount)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No compute uniforms exist on slot %i for this pipeline", slotIndex);
return;
}
D3D11_INTERNAL_PushUniformData(
d3d11CommandBuffer,
REFRESH_SHADERSTAGE_COMPUTE,
slotIndex,
data,
dataLengthInBytes
);
}
static void D3D11_INTERNAL_BindComputeResources(
D3D11CommandBuffer *commandBuffer
) {
D3D11ComputePipeline *computePipeline = commandBuffer->computePipeline;
Uint32 readOnlyResourceCount =
computePipeline->readOnlyStorageTextureCount +
computePipeline->readOnlyStorageBufferCount;
Uint32 readWriteResourceCount =
computePipeline->readWriteStorageTextureCount +
computePipeline->readWriteStorageBufferCount;
if (commandBuffer->needComputeUAVBind)
{
ID3D11DeviceContext_CSSetUnorderedAccessViews(
commandBuffer->context,
0,
readWriteResourceCount,
commandBuffer->computeUnorderedAccessViews,
NULL
);
commandBuffer->needComputeUAVBind = SDL_FALSE;
}
if (commandBuffer->needComputeSRVBind)
{
ID3D11DeviceContext_CSSetShaderResources(
commandBuffer->context,
0,
readOnlyResourceCount,
commandBuffer->computeShaderResourceViews
);
commandBuffer->needComputeSRVBind = SDL_FALSE;
}
}
static void D3D11_DispatchCompute(
Refresh_CommandBuffer *commandBuffer,
Uint32 groupCountX,
Uint32 groupCountY,
Uint32 groupCountZ
) {
D3D11CommandBuffer* d3d11CommandBuffer = (D3D11CommandBuffer*)commandBuffer;
D3D11_INTERNAL_BindComputeResources(d3d11CommandBuffer);
ID3D11DeviceContext_Dispatch(
d3d11CommandBuffer->context,
groupCountX,
groupCountY,
groupCountZ
);
}
static void D3D11_EndComputePass(
Refresh_CommandBuffer *commandBuffer
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
/* reset UAV slots to avoid NULL set behavior */
/* https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-cssetshaderresources */
ID3D11DeviceContext_CSSetUnorderedAccessViews(
d3d11CommandBuffer->context,
0,
MAX_STORAGE_TEXTURES_PER_STAGE + MAX_STORAGE_BUFFERS_PER_STAGE,
nullUAVs,
NULL
);
d3d11CommandBuffer->computePipeline = NULL;
}
/* Fence Cleanup */
static void D3D11_INTERNAL_ReleaseFenceToPool(
D3D11Renderer *renderer,
D3D11Fence *fence
) {
SDL_LockMutex(renderer->fenceLock);
if (renderer->availableFenceCount == renderer->availableFenceCapacity)
{
renderer->availableFenceCapacity *= 2;
renderer->availableFences = SDL_realloc(
renderer->availableFences,
renderer->availableFenceCapacity * sizeof(D3D11Fence*)
);
}
renderer->availableFences[renderer->availableFenceCount] = fence;
renderer->availableFenceCount += 1;
SDL_UnlockMutex(renderer->fenceLock);
}
static void D3D11_ReleaseFence(
Refresh_Renderer *driverData,
Refresh_Fence *fence
) {
D3D11Fence *d3d11Fence = (D3D11Fence*) fence;
if (SDL_AtomicDecRef(&d3d11Fence->referenceCount))
{
D3D11_INTERNAL_ReleaseFenceToPool(
(D3D11Renderer*) driverData,
d3d11Fence
);
}
}
/* Cleanup */
static void D3D11_INTERNAL_CleanCommandBuffer(
D3D11Renderer *renderer,
D3D11CommandBuffer *commandBuffer
) {
/* Reference Counting */
for (Uint32 i = 0; i < commandBuffer->usedBufferCount; i += 1)
{
(void)SDL_AtomicDecRef(&commandBuffer->usedBuffers[i]->referenceCount);
}
commandBuffer->usedBufferCount = 0;
for (Uint32 i = 0; i < commandBuffer->usedTransferBufferCount; i += 1)
{
(void)SDL_AtomicDecRef(&commandBuffer->usedTransferBuffers[i]->referenceCount);
}
commandBuffer->usedTransferBufferCount = 0;
for (Uint32 i = 0; i < commandBuffer->usedTextureSubresourceCount; i += 1)
{
(void)SDL_AtomicDecRef(&commandBuffer->usedTextureSubresources[i]->referenceCount);
}
commandBuffer->usedTextureSubresourceCount = 0;
/* Reset presentation */
commandBuffer->windowDataCount = 0;
/* The fence is now available (unless SubmitAndAcquireFence was called) */
if (commandBuffer->autoReleaseFence)
{
D3D11_ReleaseFence(
(Refresh_Renderer*) renderer,
(Refresh_Fence*) commandBuffer->fence
);
}
/* Return command buffer to pool */
SDL_LockMutex(renderer->acquireCommandBufferLock);
if (renderer->availableCommandBufferCount == renderer->availableCommandBufferCapacity)
{
renderer->availableCommandBufferCapacity += 1;
renderer->availableCommandBuffers = SDL_realloc(
renderer->availableCommandBuffers,
renderer->availableCommandBufferCapacity * sizeof(D3D11CommandBuffer*)
);
}
renderer->availableCommandBuffers[renderer->availableCommandBufferCount] = commandBuffer;
renderer->availableCommandBufferCount += 1;
SDL_UnlockMutex(renderer->acquireCommandBufferLock);
/* Remove this command buffer from the submitted list */
for (Uint32 i = 0; i < renderer->submittedCommandBufferCount; i += 1)
{
if (renderer->submittedCommandBuffers[i] == commandBuffer)
{
renderer->submittedCommandBuffers[i] = renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount - 1];
renderer->submittedCommandBufferCount -= 1;
}
}
}
static void D3D11_INTERNAL_PerformPendingDestroys(
D3D11Renderer *renderer
) {
Sint32 referenceCount = 0;
Sint32 i;
Uint32 j, k;
for (i = renderer->transferBufferContainersToDestroyCount - 1; i >= 0; i -= 1)
{
referenceCount = 0;
for (j = 0; j < renderer->transferBufferContainersToDestroy[i]->bufferCount; j += 1)
{
referenceCount += SDL_AtomicGet(&renderer->transferBufferContainersToDestroy[i]->buffers[j]->referenceCount);
}
if (referenceCount == 0)
{
D3D11_INTERNAL_DestroyTransferBufferContainer(
renderer->transferBufferContainersToDestroy[i]
);
renderer->transferBufferContainersToDestroy[i] = renderer->transferBufferContainersToDestroy[renderer->transferBufferContainersToDestroyCount - 1];
renderer->transferBufferContainersToDestroyCount -= 1;
}
}
for (i = renderer->bufferContainersToDestroyCount - 1; i >= 0; i -= 1)
{
referenceCount = 0;
for (j = 0; j < renderer->bufferContainersToDestroy[i]->bufferCount; j += 1)
{
referenceCount += SDL_AtomicGet(&renderer->bufferContainersToDestroy[i]->buffers[j]->referenceCount);
}
if (referenceCount == 0)
{
D3D11_INTERNAL_DestroyBufferContainer(
renderer->bufferContainersToDestroy[i]
);
renderer->bufferContainersToDestroy[i] = renderer->bufferContainersToDestroy[renderer->bufferContainersToDestroyCount - 1];
renderer->bufferContainersToDestroyCount -= 1;
}
}
for (i = renderer->textureContainersToDestroyCount - 1; i >= 0; i -= 1)
{
referenceCount = 0;
for (j = 0; j < renderer->textureContainersToDestroy[i]->textureCount; j += 1)
{
for (k = 0; k < renderer->textureContainersToDestroy[i]->textures[j]->subresourceCount; k += 1)
{
referenceCount += SDL_AtomicGet(&renderer->textureContainersToDestroy[i]->textures[j]->subresources[k].referenceCount);
}
}
if (referenceCount == 0)
{
D3D11_INTERNAL_DestroyTextureContainer(
renderer->textureContainersToDestroy[i]
);
renderer->textureContainersToDestroy[i] = renderer->textureContainersToDestroy[renderer->textureContainersToDestroyCount - 1];
renderer->textureContainersToDestroyCount -= 1;
}
}
}
/* Fences */
static void D3D11_INTERNAL_WaitForFence(
D3D11Renderer *renderer,
D3D11Fence *fence
) {
BOOL queryData;
HRESULT res;
SDL_LockMutex(renderer->contextLock);
do
{
res = ID3D11DeviceContext_GetData(
renderer->immediateContext,
(ID3D11Asynchronous*)fence->handle,
&queryData,
sizeof(queryData),
0
);
}
while (res != S_OK); /* Spin until we get a result back... */
SDL_UnlockMutex(renderer->contextLock);
}
static void D3D11_WaitForFences(
Refresh_Renderer *driverData,
SDL_bool waitAll,
Uint32 fenceCount,
Refresh_Fence **pFences
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11Fence *fence;
BOOL queryData;
HRESULT res = S_FALSE;
if (waitAll)
{
for (Uint32 i = 0; i < fenceCount; i += 1)
{
fence = (D3D11Fence*) pFences[i];
D3D11_INTERNAL_WaitForFence(renderer, fence);
}
}
else
{
SDL_LockMutex(renderer->contextLock);
while (res != S_OK)
{
for (Uint32 i = 0; i < fenceCount; i += 1)
{
fence = (D3D11Fence*) pFences[i];
res = ID3D11DeviceContext_GetData(
renderer->immediateContext,
(ID3D11Asynchronous*) fence->handle,
&queryData,
sizeof(queryData),
0
);
if (res == S_OK)
{
break;
}
}
}
SDL_UnlockMutex(renderer->contextLock);
}
SDL_LockMutex(renderer->contextLock);
/* Check if we can perform any cleanups */
for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1)
{
res = ID3D11DeviceContext_GetData(
renderer->immediateContext,
(ID3D11Asynchronous*) renderer->submittedCommandBuffers[i]->fence->handle,
&queryData,
sizeof(queryData),
0
);
if (res == S_OK)
{
D3D11_INTERNAL_CleanCommandBuffer(
renderer,
renderer->submittedCommandBuffers[i]
);
}
}
D3D11_INTERNAL_PerformPendingDestroys(renderer);
SDL_UnlockMutex(renderer->contextLock);
}
static SDL_bool D3D11_QueryFence(
Refresh_Renderer *driverData,
Refresh_Fence *fence
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11Fence *d3d11Fence = (D3D11Fence*) fence;
BOOL queryData;
HRESULT res;
SDL_LockMutex(renderer->contextLock);
res = ID3D11DeviceContext_GetData(
renderer->immediateContext,
(ID3D11Asynchronous*) d3d11Fence->handle,
&queryData,
sizeof(queryData),
0
);
SDL_UnlockMutex(renderer->contextLock);
return res == S_OK;
}
/* Window and Swapchain Management */
static D3D11WindowData* D3D11_INTERNAL_FetchWindowData(
SDL_Window *window
) {
return (D3D11WindowData*) SDL_GetWindowData(window, WINDOW_PROPERTY_DATA);
}
static Uint8 D3D11_INTERNAL_InitializeSwapchainTexture(
D3D11Renderer *renderer,
IDXGISwapChain *swapchain,
DXGI_FORMAT swapchainFormat,
DXGI_FORMAT rtvFormat,
D3D11Texture *pTexture
) {
ID3D11Texture2D *swapchainTexture;
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
D3D11_TEXTURE2D_DESC textureDesc;
ID3D11ShaderResourceView *srv;
ID3D11RenderTargetView *rtv;
ID3D11UnorderedAccessView *uav;
HRESULT res;
/* Clear all the texture data */
SDL_zerop(pTexture);
/* 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 SRV for the swapchain */
srvDesc.Format = swapchainFormat;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = 1;
srvDesc.Texture2D.MostDetailedMip = 0;
res = ID3D11Device_CreateShaderResourceView(
renderer->device,
(ID3D11Resource *)swapchainTexture,
&srvDesc,
&srv
);
if (FAILED(res))
{
ID3D11Texture2D_Release(swapchainTexture);
D3D11_INTERNAL_LogError(renderer->device, "Swapchain SRV creation failed", res);
return 0;
}
/* Create the RTV for the swapchain */
rtvDesc.Format = rtvFormat;
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtvDesc.Texture2D.MipSlice = 0;
res = ID3D11Device_CreateRenderTargetView(
renderer->device,
(ID3D11Resource*) swapchainTexture,
&rtvDesc,
&rtv
);
if (FAILED(res))
{
ID3D11ShaderResourceView_Release(srv);
ID3D11Texture2D_Release(swapchainTexture);
D3D11_INTERNAL_LogError(renderer->device, "Swapchain RTV creation failed", res);
return 0;
}
uavDesc.Format = swapchainFormat;
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
uavDesc.Texture2D.MipSlice = 0;
res = ID3D11Device_CreateUnorderedAccessView(
renderer->device,
(ID3D11Resource *)swapchainTexture,
&uavDesc,
&uav
);
if (FAILED(res))
{
ID3D11ShaderResourceView_Release(srv);
ID3D11RenderTargetView_Release(rtv);
ID3D11Texture2D_Release(swapchainTexture);
D3D11_INTERNAL_LogError(renderer->device, "Swapchain UAV creation failed", res);
return 0;
}
/* Fill out the texture struct */
pTexture->handle = NULL; /* This will be set in AcquireSwapchainTexture. */
pTexture->shaderView = srv;
pTexture->subresourceCount = 1;
pTexture->subresources = SDL_malloc(sizeof(D3D11TextureSubresource));
pTexture->subresources[0].colorTargetView = rtv;
pTexture->subresources[0].srv = srv;
pTexture->subresources[0].uav = uav;
pTexture->subresources[0].depthStencilTargetView = NULL;
pTexture->subresources[0].msaaHandle = NULL;
pTexture->subresources[0].msaaTargetView = NULL;
pTexture->subresources[0].layer = 0;
pTexture->subresources[0].level = 0;
pTexture->subresources[0].index = 0;
pTexture->subresources[0].parent = pTexture;
SDL_AtomicSet(&pTexture->subresources[0].referenceCount, 0);
ID3D11Texture2D_GetDesc(swapchainTexture, &textureDesc);
pTexture->levelCount = textureDesc.MipLevels;
pTexture->width = textureDesc.Width;
pTexture->height = textureDesc.Height;
pTexture->depth = 1;
pTexture->isCube = 0;
pTexture->isRenderTarget = 1;
/* Cleanup */
ID3D11Texture2D_Release(swapchainTexture);
return 1;
}
static Uint8 D3D11_INTERNAL_CreateSwapchain(
D3D11Renderer *renderer,
D3D11WindowData *windowData,
Refresh_SwapchainComposition swapchainComposition,
Refresh_PresentMode presentMode
) {
SDL_SysWMinfo info;
HWND dxgiHandle;
int width, height;
Uint32 i;
DXGI_SWAP_CHAIN_DESC swapchainDesc;
DXGI_FORMAT swapchainFormat;
IDXGIFactory1 *pParent;
IDXGISwapChain *swapchain;
IDXGISwapChain3 *swapchain3;
Uint32 colorSpaceSupport;
HRESULT res;
/* Get the DXGI handle */
#ifdef _WIN32
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo((SDL_Window*) windowData->window, &info);
dxgiHandle = info.info.win.window;
#else
dxgiHandle = (HWND) windowData->window;
#endif
/* Get the window size */
SDL_GetWindowSize(windowData->window, &width, &height);
swapchainFormat = SwapchainCompositionToTextureFormat[swapchainComposition];
/* Initialize the swapchain buffer descriptor */
swapchainDesc.BufferDesc.Width = 0;
swapchainDesc.BufferDesc.Height = 0;
swapchainDesc.BufferDesc.RefreshRate.Numerator = 0;
swapchainDesc.BufferDesc.RefreshRate.Denominator = 0;
swapchainDesc.BufferDesc.Format = swapchainFormat;
swapchainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapchainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
/* Initialize the rest of the swapchain descriptor */
swapchainDesc.SampleDesc.Count = 1;
swapchainDesc.SampleDesc.Quality = 0;
swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_UNORDERED_ACCESS | DXGI_USAGE_SHADER_INPUT;
swapchainDesc.BufferCount = 2;
swapchainDesc.OutputWindow = dxgiHandle;
swapchainDesc.Windowed = 1;
if (renderer->supportsTearing)
{
swapchainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
/* We know this is supported because tearing support implies DXGI 1.5+ */
swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
}
else
{
swapchainDesc.Flags = 0;
swapchainDesc.SwapEffect = (
renderer->supportsFlipDiscard ?
DXGI_SWAP_EFFECT_FLIP_DISCARD :
DXGI_SWAP_EFFECT_DISCARD
);
}
/* Create the swapchain! */
res = IDXGIFactory1_CreateSwapChain(
(IDXGIFactory1*) renderer->factory,
(IUnknown*) renderer->device,
&swapchainDesc,
&swapchain
);
ERROR_CHECK_RETURN("Could not create swapchain", 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))
{
SDL_LogWarn(
SDL_LOG_CATEGORY_APPLICATION,
"Could not get swapchain parent! Error Code: " HRESULT_FMT,
res
);
}
else
{
/* Disable DXGI window crap */
res = IDXGIFactory1_MakeWindowAssociation(
pParent,
dxgiHandle,
DXGI_MWA_NO_WINDOW_CHANGES
);
if (FAILED(res))
{
SDL_LogWarn(
SDL_LOG_CATEGORY_APPLICATION,
"MakeWindowAssociation failed! Error Code: " HRESULT_FMT,
res
);
}
/* We're done with the parent now */
IDXGIFactory1_Release(pParent);
}
/* Initialize the swapchain data */
windowData->swapchain = swapchain;
windowData->presentMode = presentMode;
windowData->swapchainComposition = swapchainComposition;
windowData->swapchainFormat = swapchainFormat;
windowData->swapchainColorSpace = SwapchainCompositionToColorSpace[swapchainComposition];
windowData->frameCounter = 0;
for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1)
{
windowData->inFlightFences[i] = NULL;
}
if (SUCCEEDED(IDXGISwapChain3_QueryInterface(
swapchain,
&D3D_IID_IDXGISwapChain3,
(void**) &swapchain3
))) {
IDXGISwapChain3_CheckColorSpaceSupport(
swapchain3,
windowData->swapchainColorSpace,
&colorSpaceSupport
);
if (!(colorSpaceSupport & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT))
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Requested colorspace is unsupported!");
return 0;
}
IDXGISwapChain3_SetColorSpace1(
swapchain3,
windowData->swapchainColorSpace
);
IDXGISwapChain3_Release(swapchain3);
}
else
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "DXGI 1.4 not supported, cannot use colorspace other than REFRESH_COLORSPACE_NONLINEAR_SRGB!");
return 0;
}
/* If a you are using a FLIP model format you can't create the swapchain as DXGI_FORMAT_B8G8R8A8_UNORM_SRGB.
* You have to create the swapchain as DXGI_FORMAT_B8G8R8A8_UNORM and then set the render target view's format to DXGI_FORMAT_B8G8R8A8_UNORM_SRGB
*/
if (!D3D11_INTERNAL_InitializeSwapchainTexture(
renderer,
swapchain,
swapchainFormat,
(swapchainComposition == REFRESH_SWAPCHAINCOMPOSITION_SDR_LINEAR) ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : windowData->swapchainFormat,
&windowData->texture
)) {
IDXGISwapChain_Release(swapchain);
return 0;
}
/* Initialize dummy container */
SDL_zerop(&windowData->textureContainer);
windowData->textureContainer.textures = SDL_calloc(1, sizeof(D3D11Texture*));
return 1;
}
static Uint8 D3D11_INTERNAL_ResizeSwapchain(
D3D11Renderer *renderer,
D3D11WindowData *windowData,
Sint32 width,
Sint32 height
) {
/* Release the old views */
ID3D11ShaderResourceView_Release(windowData->texture.shaderView);
ID3D11RenderTargetView_Release(windowData->texture.subresources[0].colorTargetView);
/* NOTE: subresource srv is same as shaderView, not released */
ID3D11UnorderedAccessView_Release(windowData->texture.subresources[0].uav);
SDL_free(windowData->texture.subresources);
/* Resize the swapchain */
HRESULT res = IDXGISwapChain_ResizeBuffers(
windowData->swapchain,
0, /* Keep buffer count the same */
width,
height,
DXGI_FORMAT_UNKNOWN, /* Keep the old format */
renderer->supportsTearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0
);
ERROR_CHECK_RETURN("Could not resize swapchain buffers", 0);
/* Create the texture object for the swapchain */
return D3D11_INTERNAL_InitializeSwapchainTexture(
renderer,
windowData->swapchain,
windowData->swapchainFormat,
(windowData->swapchainComposition == REFRESH_SWAPCHAINCOMPOSITION_SDR_LINEAR) ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : windowData->swapchainFormat,
&windowData->texture
);
}
static SDL_bool D3D11_SupportsSwapchainComposition(
Refresh_Renderer *driverData,
SDL_Window *window,
Refresh_SwapchainComposition swapchainComposition
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
DXGI_FORMAT format;
Uint32 formatSupport = 0;
HRESULT res;
format = SwapchainCompositionToTextureFormat[swapchainComposition];
res = ID3D11Device_CheckFormatSupport(
renderer->device,
format,
&formatSupport
);
if (FAILED(res))
{
/* Format is apparently unknown */
return SDL_FALSE;
}
return (formatSupport & D3D11_FORMAT_SUPPORT_DISPLAY);
}
static SDL_bool D3D11_SupportsPresentMode(
Refresh_Renderer *driverData,
SDL_Window *window,
Refresh_PresentMode presentMode
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
(void)window; /* used by other backends */
switch (presentMode)
{
case REFRESH_PRESENTMODE_IMMEDIATE:
case REFRESH_PRESENTMODE_VSYNC:
return SDL_TRUE;
case REFRESH_PRESENTMODE_MAILBOX:
return renderer->supportsFlipDiscard;
}
SDL_assert(!"Unrecognized present mode");
return SDL_FALSE;
}
static SDL_bool D3D11_ClaimWindow(
Refresh_Renderer *driverData,
SDL_Window *window,
Refresh_SwapchainComposition swapchainComposition,
Refresh_PresentMode presentMode
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(window);
if (windowData == NULL)
{
windowData = (D3D11WindowData*) SDL_malloc(sizeof(D3D11WindowData));
windowData->window = window;
if (D3D11_INTERNAL_CreateSwapchain(renderer, windowData, swapchainComposition, presentMode))
{
SDL_SetWindowData(window, WINDOW_PROPERTY_DATA, windowData);
SDL_LockMutex(renderer->windowLock);
if (renderer->claimedWindowCount >= renderer->claimedWindowCapacity)
{
renderer->claimedWindowCapacity *= 2;
renderer->claimedWindows = SDL_realloc(
renderer->claimedWindows,
renderer->claimedWindowCapacity * sizeof(D3D11WindowData*)
);
}
renderer->claimedWindows[renderer->claimedWindowCount] = windowData;
renderer->claimedWindowCount += 1;
SDL_UnlockMutex(renderer->windowLock);
return 1;
}
else
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not create swapchain, failed to claim window!");
SDL_free(windowData);
return 0;
}
}
else
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Window already claimed!");
return 0;
}
}
static void D3D11_INTERNAL_DestroySwapchain(
D3D11Renderer *renderer,
D3D11WindowData *windowData
) {
Uint32 i;
D3D11_Wait((Refresh_Renderer*) renderer);
ID3D11ShaderResourceView_Release(windowData->texture.shaderView);
ID3D11RenderTargetView_Release(windowData->texture.subresources[0].colorTargetView);
/* NOTE: subresource srv is same as shaderView, not released */
ID3D11UnorderedAccessView_Release(windowData->texture.subresources[0].uav);
SDL_free(windowData->texture.subresources);
SDL_free(windowData->textureContainer.textures);
IDXGISwapChain_Release(windowData->swapchain);
/* DXGI will crash if we don't flush deferred swapchain destruction */
SDL_LockMutex(renderer->contextLock);
ID3D11DeviceContext_ClearState(renderer->immediateContext);
ID3D11DeviceContext_Flush(renderer->immediateContext);
SDL_UnlockMutex(renderer->contextLock);
windowData->swapchain = NULL;
for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1)
{
if (windowData->inFlightFences[i] != NULL)
{
D3D11_ReleaseFence(
(Refresh_Renderer*) renderer,
(Refresh_Fence*) windowData->inFlightFences[i]
);
}
}
}
static void D3D11_UnclaimWindow(
Refresh_Renderer *driverData,
SDL_Window *window
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(window);
if (windowData == NULL)
{
return;
}
D3D11_INTERNAL_DestroySwapchain(
renderer,
windowData
);
SDL_LockMutex(renderer->windowLock);
for (Uint32 i = 0; i < renderer->claimedWindowCount; i += 1)
{
if (renderer->claimedWindows[i]->window == window)
{
renderer->claimedWindows[i] = renderer->claimedWindows[renderer->claimedWindowCount - 1];
renderer->claimedWindowCount -= 1;
break;
}
}
SDL_UnlockMutex(renderer->windowLock);
SDL_free(windowData);
SDL_SetWindowData(window, WINDOW_PROPERTY_DATA, NULL);
}
static Refresh_Texture* D3D11_AcquireSwapchainTexture(
Refresh_CommandBuffer *commandBuffer,
SDL_Window *window,
Uint32 *pWidth,
Uint32 *pHeight
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer;
D3D11WindowData *windowData;
DXGI_SWAP_CHAIN_DESC swapchainDesc;
int w, h;
HRESULT res;
windowData = D3D11_INTERNAL_FetchWindowData(window);
if (windowData == NULL)
{
return NULL;
}
/* Check for window size changes and resize the swapchain if needed. */
IDXGISwapChain_GetDesc(windowData->swapchain, &swapchainDesc);
SDL_GetWindowSize(window, &w, &h);
if (w != swapchainDesc.BufferDesc.Width || h != swapchainDesc.BufferDesc.Height)
{
res = D3D11_INTERNAL_ResizeSwapchain(
renderer,
windowData,
w,
h
);
ERROR_CHECK_RETURN("Could not resize swapchain", NULL);
}
if (windowData->inFlightFences[windowData->frameCounter] != NULL)
{
if (windowData->presentMode == REFRESH_PRESENTMODE_VSYNC)
{
/* In VSYNC mode, block until the least recent presented frame is done */
D3D11_WaitForFences(
(Refresh_Renderer*) renderer,
SDL_TRUE,
1,
(Refresh_Fence**) &windowData->inFlightFences[windowData->frameCounter]
);
}
else
{
if (!D3D11_QueryFence(
(Refresh_Renderer*) d3d11CommandBuffer->renderer,
(Refresh_Fence*) windowData->inFlightFences[windowData->frameCounter]
)) {
/*
* In MAILBOX or IMMEDIATE mode, if the least recent fence is not signaled,
* return NULL to indicate that rendering should be skipped
*/
return NULL;
}
}
D3D11_ReleaseFence(
(Refresh_Renderer*) d3d11CommandBuffer->renderer,
(Refresh_Fence*) windowData->inFlightFences[windowData->frameCounter]
);
windowData->inFlightFences[windowData->frameCounter] = NULL;
}
/* Set the handle on the windowData texture data. */
res = IDXGISwapChain_GetBuffer(
windowData->swapchain,
0,
&D3D_IID_ID3D11Texture2D,
(void**) &windowData->texture.handle
);
ERROR_CHECK_RETURN("Could not acquire swapchain!", NULL);
/* Send the dimensions to the out parameters. */
*pWidth = windowData->texture.width;
*pHeight = windowData->texture.height;
/* Set up the texture container */
windowData->textureContainer.canBeCycled = 0;
windowData->textureContainer.activeTexture = &windowData->texture;
windowData->textureContainer.textures[0] = &windowData->texture;
windowData->textureContainer.textureCapacity = 1;
windowData->textureContainer.textureCount = 1;
/* Set up presentation */
if (d3d11CommandBuffer->windowDataCount == d3d11CommandBuffer->windowDataCapacity)
{
d3d11CommandBuffer->windowDataCapacity += 1;
d3d11CommandBuffer->windowDatas = SDL_realloc(
d3d11CommandBuffer->windowDatas,
d3d11CommandBuffer->windowDataCapacity * sizeof(D3D11WindowData*)
);
}
d3d11CommandBuffer->windowDatas[d3d11CommandBuffer->windowDataCount] = windowData;
d3d11CommandBuffer->windowDataCount += 1;
/* Return the swapchain texture */
return (Refresh_Texture*) &windowData->textureContainer;
}
static Refresh_TextureFormat D3D11_GetSwapchainTextureFormat(
Refresh_Renderer *driverData,
SDL_Window *window
) {
D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(window);
if (windowData == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Cannot get swapchain format, window has not been claimed!");
return 0;
}
switch (windowData->swapchainFormat)
{
case DXGI_FORMAT_B8G8R8A8_UNORM:
return REFRESH_TEXTUREFORMAT_B8G8R8A8;
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
return REFRESH_TEXTUREFORMAT_B8G8R8A8_SRGB;
case DXGI_FORMAT_R16G16B16A16_FLOAT:
return REFRESH_TEXTUREFORMAT_R16G16B16A16_SFLOAT;
case DXGI_FORMAT_R10G10B10A2_UNORM:
return REFRESH_TEXTUREFORMAT_A2R10G10B10;
default:
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized swapchain format!");
return 0;
}
}
static void D3D11_SetSwapchainParameters(
Refresh_Renderer *driverData,
SDL_Window *window,
Refresh_SwapchainComposition swapchainComposition,
Refresh_PresentMode presentMode
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(window);
if (
swapchainComposition != windowData->swapchainComposition ||
presentMode != windowData->presentMode
) {
D3D11_Wait(driverData);
/* Recreate the swapchain */
D3D11_INTERNAL_DestroySwapchain(
renderer,
windowData
);
D3D11_INTERNAL_CreateSwapchain(
renderer,
windowData,
swapchainComposition,
presentMode
);
}
}
/* Submission */
static void D3D11_Submit(
Refresh_CommandBuffer *commandBuffer
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer;
ID3D11CommandList *commandList;
HRESULT res;
SDL_LockMutex(renderer->contextLock);
/* Notify the command buffer completion query that we have completed recording */
ID3D11DeviceContext_End(
renderer->immediateContext,
(ID3D11Asynchronous*) d3d11CommandBuffer->fence->handle
);
/* Serialize the commands into the command list */
res = ID3D11DeviceContext_FinishCommandList(
d3d11CommandBuffer->context,
0,
&commandList
);
ERROR_CHECK("Could not finish command list recording!");
/* Submit the command list to the immediate context */
ID3D11DeviceContext_ExecuteCommandList(
renderer->immediateContext,
commandList,
0
);
ID3D11CommandList_Release(commandList);
/* Mark the command buffer as submitted */
if (renderer->submittedCommandBufferCount >= renderer->submittedCommandBufferCapacity)
{
renderer->submittedCommandBufferCapacity = renderer->submittedCommandBufferCount + 1;
renderer->submittedCommandBuffers = SDL_realloc(
renderer->submittedCommandBuffers,
sizeof(D3D11CommandBuffer*) * renderer->submittedCommandBufferCapacity
);
}
renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount] = d3d11CommandBuffer;
renderer->submittedCommandBufferCount += 1;
/* Present, if applicable */
for (Uint32 i = 0; i < d3d11CommandBuffer->windowDataCount; i += 1)
{
D3D11WindowData *windowData = d3d11CommandBuffer->windowDatas[i];
Uint32 syncInterval = 1;
if (windowData->presentMode == REFRESH_PRESENTMODE_IMMEDIATE ||
(renderer->supportsFlipDiscard && windowData->presentMode == REFRESH_PRESENTMODE_MAILBOX)
) {
syncInterval = 0;
}
Uint32 presentFlags = 0;
if ( renderer->supportsTearing &&
windowData->presentMode == REFRESH_PRESENTMODE_IMMEDIATE )
{
presentFlags = DXGI_PRESENT_ALLOW_TEARING;
}
IDXGISwapChain_Present(
windowData->swapchain,
syncInterval,
presentFlags
);
ID3D11Texture2D_Release(windowData->texture.handle);
windowData->inFlightFences[windowData->frameCounter] = d3d11CommandBuffer->fence;
(void)SDL_AtomicIncRef(&d3d11CommandBuffer->fence->referenceCount);
windowData->frameCounter = (windowData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT;
}
/* Check if we can perform any cleanups */
for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1)
{
BOOL queryData;
res = ID3D11DeviceContext_GetData(
renderer->immediateContext,
(ID3D11Asynchronous*) renderer->submittedCommandBuffers[i]->fence->handle,
&queryData,
sizeof(queryData),
0
);
if (res == S_OK)
{
D3D11_INTERNAL_CleanCommandBuffer(
renderer,
renderer->submittedCommandBuffers[i]
);
}
}
D3D11_INTERNAL_PerformPendingDestroys(renderer);
SDL_UnlockMutex(renderer->contextLock);
}
static Refresh_Fence* D3D11_SubmitAndAcquireFence(
Refresh_CommandBuffer *commandBuffer
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11Fence *fence = d3d11CommandBuffer->fence;
d3d11CommandBuffer->autoReleaseFence = 0;
D3D11_Submit(commandBuffer);
return (Refresh_Fence*) fence;
}
static void D3D11_Wait(
Refresh_Renderer *driverData
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11CommandBuffer *commandBuffer;
/*
* Wait for all submitted command buffers to complete.
* Sort of equivalent to vkDeviceWaitIdle.
*/
for (Uint32 i = 0; i < renderer->submittedCommandBufferCount; i += 1)
{
D3D11_INTERNAL_WaitForFence(
renderer,
renderer->submittedCommandBuffers[i]->fence
);
}
SDL_LockMutex(renderer->contextLock); /* This effectively acts as a lock around submittedCommandBuffers */
for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1)
{
commandBuffer = renderer->submittedCommandBuffers[i];
D3D11_INTERNAL_CleanCommandBuffer(renderer, commandBuffer);
}
D3D11_INTERNAL_PerformPendingDestroys(renderer);
SDL_UnlockMutex(renderer->contextLock);
}
/* Queries */
static Refresh_OcclusionQuery* D3D11_CreateOcclusionQuery(
Refresh_Renderer *driverData
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11OcclusionQuery *query = (D3D11OcclusionQuery*) SDL_malloc(sizeof(D3D11OcclusionQuery));
D3D11_QUERY_DESC desc;
HRESULT res;
desc.Query = D3D11_QUERY_OCCLUSION;
desc.MiscFlags = 0;
res = ID3D11Device_CreateQuery(
renderer->device,
&desc,
&query->handle
);
ERROR_CHECK_RETURN("Query creation failed", NULL)
return (Refresh_OcclusionQuery*) query;
}
static void D3D11_OcclusionQueryBegin(
Refresh_CommandBuffer *commandBuffer,
Refresh_OcclusionQuery *query
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11OcclusionQuery *d3dQuery = (D3D11OcclusionQuery*) query;
ID3D11DeviceContext1_Begin(
d3d11CommandBuffer->context,
(ID3D11Asynchronous*) d3dQuery->handle
);
}
static void D3D11_OcclusionQueryEnd(
Refresh_CommandBuffer *commandBuffer,
Refresh_OcclusionQuery *query
) {
D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer;
D3D11OcclusionQuery *d3dQuery = (D3D11OcclusionQuery*) query;
ID3D11DeviceContext1_End(
d3d11CommandBuffer->context,
(ID3D11Asynchronous*) d3dQuery->handle
);
}
static SDL_bool D3D11_OcclusionQueryPixelCount(
Refresh_Renderer *driverData,
Refresh_OcclusionQuery *query,
Uint32 *pixelCount
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
D3D11OcclusionQuery *d3dQuery = (D3D11OcclusionQuery*) query;
uint64_t result;
HRESULT res;
SDL_LockMutex(renderer->contextLock);
res = ID3D11DeviceContext_GetData(
renderer->immediateContext,
(ID3D11Asynchronous*) d3dQuery->handle,
&result,
sizeof(result),
0
);
SDL_UnlockMutex(renderer->contextLock);
*pixelCount = (Uint32) result;
return res == S_OK;
}
/* Format Info */
static SDL_bool D3D11_IsTextureFormatSupported(
Refresh_Renderer *driverData,
Refresh_TextureFormat format,
Refresh_TextureType type,
Refresh_TextureUsageFlags usage
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
DXGI_FORMAT dxgiFormat = RefreshToD3D11_TextureFormat[format];
DXGI_FORMAT typelessFormat = D3D11_INTERNAL_GetTypelessFormat(dxgiFormat);
UINT formatSupport, sampleableFormatSupport;
HRESULT res;
res = ID3D11Device_CheckFormatSupport(
renderer->device,
dxgiFormat,
&formatSupport
);
if (FAILED(res))
{
/* Format is apparently unknown */
return SDL_FALSE;
}
/* Depth textures are stored as typeless textures, but interpreted as color textures for sampling.
* In order to get supported usages for both interpretations, we have to do this.
*/
if (typelessFormat != DXGI_FORMAT_UNKNOWN)
{
res = ID3D11Device_CheckFormatSupport(
renderer->device,
D3D11_INTERNAL_GetSampleableFormat(typelessFormat),
&sampleableFormatSupport
);
if (SUCCEEDED(res))
{
formatSupport |= sampleableFormatSupport;
}
}
/* Is the texture type supported? */
if (type == REFRESH_TEXTURETYPE_2D && !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D))
{
return SDL_FALSE;
}
if (type == REFRESH_TEXTURETYPE_3D && !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE3D))
{
return SDL_FALSE;
}
if (type == REFRESH_TEXTURETYPE_CUBE && !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURECUBE))
{
return SDL_FALSE;
}
/* Are the usage flags supported? */
if ((usage & REFRESH_TEXTUREUSAGE_SAMPLER_BIT) && !(formatSupport & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE))
{
return SDL_FALSE;
}
if ((usage & (REFRESH_TEXTUREUSAGE_GRAPHICS_STORAGE_READ_BIT | REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_READ_BIT | REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT)) && !(formatSupport & D3D11_FORMAT_SUPPORT_SHADER_LOAD))
{
return SDL_FALSE;
}
if ((usage & REFRESH_TEXTUREUSAGE_COLOR_TARGET_BIT) && !(formatSupport & D3D11_FORMAT_SUPPORT_RENDER_TARGET))
{
return SDL_FALSE;
}
if ((usage & REFRESH_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT) && !(formatSupport & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL))
{
return SDL_FALSE;
}
return SDL_TRUE;
}
static Refresh_SampleCount D3D11_GetBestSampleCount(
Refresh_Renderer *driverData,
Refresh_TextureFormat format,
Refresh_SampleCount desiredSampleCount
) {
D3D11Renderer *renderer = (D3D11Renderer*) driverData;
Refresh_SampleCount maxSupported = REFRESH_SAMPLECOUNT_8;
Uint32 levels;
HRESULT res;
while (maxSupported > REFRESH_SAMPLECOUNT_1)
{
res = ID3D11Device_CheckMultisampleQualityLevels(
renderer->device,
RefreshToD3D11_TextureFormat[format],
RefreshToD3D11_SampleCount[desiredSampleCount],
&levels
);
if (SUCCEEDED(res) && levels > 0)
{
break;
}
maxSupported -= 1;
}
return (Refresh_SampleCount) SDL_min(maxSupported, desiredSampleCount);
}
/* SPIR-V Cross Interop */
extern Refresh_Shader* D3D11_CompileFromSPIRVCross(
Refresh_Renderer *driverData,
Refresh_ShaderStage shader_stage,
const char *entryPointName,
const char *source
);
/* Device Creation */
static SDL_bool D3D11_PrepareDriver()
{
void *d3d11_dll, *dxgi_dll;
PFN_D3D11_CREATE_DEVICE D3D11CreateDeviceFunc;
D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_1 };
PFN_CREATE_DXGI_FACTORY1 CreateDXGIFactoryFunc;
HRESULT res;
/* Can we load D3D11? */
d3d11_dll = SDL_LoadObject(D3D11_DLL);
if (d3d11_dll == NULL)
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D11: Could not find " D3D11_DLL);
return 0;
}
D3D11CreateDeviceFunc = (PFN_D3D11_CREATE_DEVICE) SDL_LoadFunction(
d3d11_dll,
D3D11_CREATE_DEVICE_FUNC
);
if (D3D11CreateDeviceFunc == NULL)
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D11: Could not find function " D3D11_CREATE_DEVICE_FUNC " in " D3D11_DLL);
SDL_UnloadObject(d3d11_dll);
return 0;
}
/* Can we create a device? */
res = D3D11CreateDeviceFunc(
NULL,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
levels,
SDL_arraysize(levels),
D3D11_SDK_VERSION,
NULL,
NULL,
NULL
);
SDL_UnloadObject(d3d11_dll);
if (FAILED(res))
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D11: Could not create D3D11Device with feature level 11_0");
return 0;
}
/* Can we load DXGI? */
dxgi_dll = SDL_LoadObject(DXGI_DLL);
if (dxgi_dll == NULL)
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D11: Could not find " DXGI_DLL);
return 0;
}
CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY1) SDL_LoadFunction(
dxgi_dll,
CREATE_DXGI_FACTORY1_FUNC
);
SDL_UnloadObject(dxgi_dll); /* We're not going to call this function, so we can just unload now. */
if (CreateDXGIFactoryFunc == NULL)
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D11: Could not find function " CREATE_DXGI_FACTORY1_FUNC " in " DXGI_DLL);
return 0;
}
return 1;
}
static void D3D11_INTERNAL_TryInitializeDXGIDebug(D3D11Renderer *renderer)
{
PFN_DXGI_GET_DEBUG_INTERFACE DXGIGetDebugInterfaceFunc;
HRESULT res;
renderer->dxgidebug_dll = SDL_LoadObject(DXGIDEBUG_DLL);
if (renderer->dxgidebug_dll == NULL)
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not find " DXGIDEBUG_DLL);
return;
}
DXGIGetDebugInterfaceFunc = (PFN_DXGI_GET_DEBUG_INTERFACE) SDL_LoadFunction(
renderer->dxgidebug_dll,
DXGI_GET_DEBUG_INTERFACE_FUNC
);
if (DXGIGetDebugInterfaceFunc == NULL)
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: " DXGI_GET_DEBUG_INTERFACE_FUNC);
return;
}
res = DXGIGetDebugInterfaceFunc(&D3D_IID_IDXGIDebug, (void**) &renderer->dxgiDebug);
if (FAILED(res))
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not get IDXGIDebug interface");
}
#ifdef HAVE_IDXGIINFOQUEUE
res = DXGIGetDebugInterfaceFunc(&D3D_IID_IDXGIInfoQueue, (void**) &renderer->dxgiInfoQueue);
if (FAILED(res))
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not get IDXGIInfoQueue interface");
}
#endif
}
static void D3D11_INTERNAL_InitBlitPipelines(
D3D11Renderer *renderer
) {
Refresh_ShaderCreateInfo shaderModuleCreateInfo;
Refresh_GraphicsPipelineCreateInfo blitPipelineCreateInfo;
Refresh_SamplerCreateInfo samplerCreateInfo;
Refresh_VertexBinding binding;
Refresh_VertexAttribute attribute;
Refresh_ColorAttachmentDescription colorAttachmentDesc;
/* Fullscreen vertex shader */
shaderModuleCreateInfo.code = (Uint8*) D3D11_FullscreenVert;
shaderModuleCreateInfo.codeSize = sizeof(D3D11_FullscreenVert);
shaderModuleCreateInfo.stage = REFRESH_SHADERSTAGE_VERTEX;
shaderModuleCreateInfo.format = REFRESH_SHADERFORMAT_DXBC;
shaderModuleCreateInfo.entryPointName = "main";
renderer->fullscreenVertexShader = D3D11_CreateShader(
(Refresh_Renderer*) renderer,
&shaderModuleCreateInfo
);
if (renderer->fullscreenVertexShader == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile fullscreen vertex shader!");
}
/* Blit from 2D pixel shader */
shaderModuleCreateInfo.code = (Uint8*) D3D11_BlitFrom2D;
shaderModuleCreateInfo.codeSize = sizeof(D3D11_BlitFrom2D);
shaderModuleCreateInfo.stage = REFRESH_SHADERSTAGE_FRAGMENT;
renderer->blitFrom2DPixelShader = D3D11_CreateShader(
(Refresh_Renderer*) renderer,
&shaderModuleCreateInfo
);
if (renderer->blitFrom2DPixelShader == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile blit from 2D pixel shader!");
}
/* Blit from 2D array pixel shader */
shaderModuleCreateInfo.code = (Uint8 *) D3D11_BlitFrom2DArray;
shaderModuleCreateInfo.codeSize = sizeof(D3D11_BlitFrom2DArray);
shaderModuleCreateInfo.stage = REFRESH_SHADERSTAGE_FRAGMENT;
renderer->blitFrom2DArrayPixelShader = D3D11_CreateShader(
(Refresh_Renderer*) renderer,
&shaderModuleCreateInfo
);
if (renderer->blitFrom2DArrayPixelShader == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile blit from 2D array pixel shader!");
}
/* Blit from 2D pipeline */
SDL_memset(&colorAttachmentDesc, '\0', sizeof(Refresh_ColorAttachmentDescription));
colorAttachmentDesc.blendState.blendEnable = 0;
colorAttachmentDesc.blendState.colorWriteMask =
REFRESH_COLORCOMPONENT_R_BIT |
REFRESH_COLORCOMPONENT_G_BIT |
REFRESH_COLORCOMPONENT_B_BIT |
REFRESH_COLORCOMPONENT_A_BIT;
colorAttachmentDesc.format = REFRESH_TEXTUREFORMAT_R8G8B8A8; /* format doesn't matter in d3d11 */
blitPipelineCreateInfo.attachmentInfo.colorAttachmentDescriptions = &colorAttachmentDesc;
blitPipelineCreateInfo.attachmentInfo.colorAttachmentCount = 1;
blitPipelineCreateInfo.attachmentInfo.depthStencilFormat = REFRESH_TEXTUREFORMAT_D16_UNORM; /* arbitrary */
blitPipelineCreateInfo.attachmentInfo.hasDepthStencilAttachment = 0;
SDL_memset(&blitPipelineCreateInfo.depthStencilState, '\0', sizeof(Refresh_DepthStencilState));
blitPipelineCreateInfo.depthStencilState.depthTestEnable = 0;
blitPipelineCreateInfo.depthStencilState.depthWriteEnable = 0;
blitPipelineCreateInfo.depthStencilState.stencilTestEnable = 0;
binding.binding = 0;
binding.inputRate = REFRESH_VERTEXINPUTRATE_VERTEX;
binding.stepRate = 0;
binding.stride = 64;
attribute.binding = 0;
attribute.format = REFRESH_VERTEXELEMENTFORMAT_VECTOR2;
attribute.location = 0;
attribute.offset = 0;
blitPipelineCreateInfo.vertexInputState.vertexAttributeCount = 1;
blitPipelineCreateInfo.vertexInputState.vertexAttributes = &attribute;
blitPipelineCreateInfo.vertexInputState.vertexBindingCount = 1;
blitPipelineCreateInfo.vertexInputState.vertexBindings = &binding;
blitPipelineCreateInfo.vertexShader = renderer->fullscreenVertexShader;
blitPipelineCreateInfo.fragmentShader = renderer->blitFrom2DPixelShader;
blitPipelineCreateInfo.multisampleState.multisampleCount = REFRESH_SAMPLECOUNT_1;
blitPipelineCreateInfo.multisampleState.sampleMask = 0xFFFFFFFF;
blitPipelineCreateInfo.rasterizerState.cullMode = REFRESH_CULLMODE_NONE;
blitPipelineCreateInfo.rasterizerState.fillMode = REFRESH_FILLMODE_FILL;
blitPipelineCreateInfo.rasterizerState.frontFace = REFRESH_FRONTFACE_CLOCKWISE;
blitPipelineCreateInfo.rasterizerState.depthBiasEnable = 0;
blitPipelineCreateInfo.rasterizerState.depthBiasClamp = 0.0f;
blitPipelineCreateInfo.rasterizerState.depthBiasConstantFactor = 0.0f;
blitPipelineCreateInfo.rasterizerState.depthBiasSlopeFactor = 0.0f;
blitPipelineCreateInfo.primitiveType = REFRESH_PRIMITIVETYPE_TRIANGLELIST;
blitPipelineCreateInfo.blendConstants[0] = 1.0f;
blitPipelineCreateInfo.blendConstants[1] = 1.0f;
blitPipelineCreateInfo.blendConstants[2] = 1.0f;
blitPipelineCreateInfo.blendConstants[3] = 1.0f;
blitPipelineCreateInfo.vertexResourceInfo.samplerCount = 0;
blitPipelineCreateInfo.vertexResourceInfo.storageTextureCount = 0;
blitPipelineCreateInfo.vertexResourceInfo.storageBufferCount = 0;
blitPipelineCreateInfo.vertexResourceInfo.uniformBufferCount = 0;
blitPipelineCreateInfo.fragmentResourceInfo.samplerCount = 1;
blitPipelineCreateInfo.fragmentResourceInfo.storageTextureCount = 0;
blitPipelineCreateInfo.fragmentResourceInfo.storageBufferCount = 0;
blitPipelineCreateInfo.fragmentResourceInfo.uniformBufferCount = 0;
renderer->blitFrom2DPipeline = D3D11_CreateGraphicsPipeline(
(Refresh_Renderer*) renderer,
&blitPipelineCreateInfo
);
if (renderer->blitFrom2DPipeline == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create blit pipeline!");
}
/* Blit from 2D array pipeline */
blitPipelineCreateInfo.fragmentShader = renderer->blitFrom2DArrayPixelShader;
blitPipelineCreateInfo.fragmentResourceInfo.uniformBufferCount = 1;
renderer->blitFrom2DArrayPipeline = D3D11_CreateGraphicsPipeline(
(Refresh_Renderer*) renderer,
&blitPipelineCreateInfo
);
/* Create samplers */
samplerCreateInfo.addressModeU = REFRESH_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
samplerCreateInfo.addressModeV = REFRESH_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
samplerCreateInfo.addressModeW = REFRESH_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
samplerCreateInfo.anisotropyEnable = 0;
samplerCreateInfo.compareEnable = 0;
samplerCreateInfo.magFilter = REFRESH_FILTER_NEAREST;
samplerCreateInfo.minFilter = REFRESH_FILTER_NEAREST;
samplerCreateInfo.mipmapMode = REFRESH_SAMPLERMIPMAPMODE_NEAREST;
samplerCreateInfo.mipLodBias = 0.0f;
samplerCreateInfo.minLod = 0;
samplerCreateInfo.maxLod = 1000;
samplerCreateInfo.borderColor = REFRESH_BORDERCOLOR_FLOAT_TRANSPARENT_BLACK;
renderer->blitNearestSampler = D3D11_CreateSampler(
(Refresh_Renderer*) renderer,
&samplerCreateInfo
);
if (renderer->blitNearestSampler == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create blit nearest sampler!");
}
samplerCreateInfo.magFilter = REFRESH_FILTER_LINEAR;
samplerCreateInfo.minFilter = REFRESH_FILTER_LINEAR;
samplerCreateInfo.mipmapMode = REFRESH_SAMPLERMIPMAPMODE_LINEAR;
renderer->blitLinearSampler = D3D11_CreateSampler(
(Refresh_Renderer*) renderer,
&samplerCreateInfo
);
if (renderer->blitLinearSampler == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create blit linear sampler!");
}
}
static void D3D11_INTERNAL_DestroyBlitPipelines(
Refresh_Renderer *driverData
) {
D3D11Renderer *renderer = (D3D11Renderer *)driverData;
D3D11_ReleaseSampler(driverData, renderer->blitLinearSampler);
D3D11_ReleaseSampler(driverData, renderer->blitNearestSampler);
D3D11_ReleaseGraphicsPipeline(driverData, renderer->blitFrom2DPipeline);
D3D11_ReleaseGraphicsPipeline(driverData, renderer->blitFrom2DArrayPipeline);
D3D11_ReleaseShader(driverData, renderer->fullscreenVertexShader);
D3D11_ReleaseShader(driverData, renderer->blitFrom2DPixelShader);
D3D11_ReleaseShader(driverData, renderer->blitFrom2DArrayPixelShader);
}
static Refresh_Device* D3D11_CreateDevice(SDL_bool debugMode)
{
D3D11Renderer *renderer;
PFN_CREATE_DXGI_FACTORY1 CreateDXGIFactoryFunc;
PFN_D3D11_CREATE_DEVICE D3D11CreateDeviceFunc;
D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_1 };
IDXGIFactory4 *factory4;
IDXGIFactory5 *factory5;
IDXGIFactory6 *factory6;
Uint32 flags;
DXGI_ADAPTER_DESC1 adapterDesc;
HRESULT res;
Refresh_Device* result;
/* Allocate and zero out the renderer */
renderer = (D3D11Renderer*) SDL_calloc(1, sizeof(D3D11Renderer));
/* Load the DXGI library */
renderer->dxgi_dll = SDL_LoadObject(DXGI_DLL);
if (renderer->dxgi_dll == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not find " DXGI_DLL);
return NULL;
}
/* Load the CreateDXGIFactory1 function */
CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY1) SDL_LoadFunction(
renderer->dxgi_dll,
CREATE_DXGI_FACTORY1_FUNC
);
if (CreateDXGIFactoryFunc == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: " CREATE_DXGI_FACTORY1_FUNC);
return NULL;
}
/* Create the DXGI factory */
res = CreateDXGIFactoryFunc(
&D3D_IID_IDXGIFactory1,
(void**) &renderer->factory
);
ERROR_CHECK_RETURN("Could not create DXGIFactory", NULL);
/* Check for flip-model discard support (supported on Windows 10+) */
res = IDXGIFactory1_QueryInterface(
renderer->factory,
&D3D_IID_IDXGIFactory4,
(void**) &factory4
);
if (SUCCEEDED(res))
{
renderer->supportsFlipDiscard = 1;
IDXGIFactory4_Release(factory4);
}
/* Check for explicit tearing support */
res = IDXGIFactory1_QueryInterface(
renderer->factory,
&D3D_IID_IDXGIFactory5,
(void**) &factory5
);
if (SUCCEEDED(res))
{
res = IDXGIFactory5_CheckFeatureSupport(
factory5,
DXGI_FEATURE_PRESENT_ALLOW_TEARING,
&renderer->supportsTearing,
sizeof(renderer->supportsTearing)
);
if (FAILED(res))
{
renderer->supportsTearing = FALSE;
}
IDXGIFactory5_Release(factory5);
}
/* Select the appropriate device for rendering */
res = IDXGIAdapter1_QueryInterface(
renderer->factory,
&D3D_IID_IDXGIFactory6,
(void**) &factory6
);
if (SUCCEEDED(res))
{
IDXGIFactory6_EnumAdapterByGpuPreference(
factory6,
0,
DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE,
&D3D_IID_IDXGIAdapter1,
(void**) &renderer->adapter
);
IDXGIFactory6_Release(factory6);
}
else
{
IDXGIFactory1_EnumAdapters1(
renderer->factory,
0,
&renderer->adapter
);
}
/* Get information about the selected adapter. Used for logging info. */
IDXGIAdapter1_GetDesc1(renderer->adapter, &adapterDesc);
/* Initialize the DXGI debug layer, if applicable */
if (debugMode)
{
D3D11_INTERNAL_TryInitializeDXGIDebug(renderer);
}
/* Load the D3D library */
renderer->d3d11_dll = SDL_LoadObject(D3D11_DLL);
if (renderer->d3d11_dll == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not find " D3D11_DLL);
return NULL;
}
/* Load the CreateDevice function */
D3D11CreateDeviceFunc = (PFN_D3D11_CREATE_DEVICE) SDL_LoadFunction(
renderer->d3d11_dll,
D3D11_CREATE_DEVICE_FUNC
);
if (D3D11CreateDeviceFunc == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: " D3D11_CREATE_DEVICE_FUNC);
return NULL;
}
/* Set up device flags */
flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
if (debugMode)
{
flags |= D3D11_CREATE_DEVICE_DEBUG;
}
/* Create the device */
ID3D11Device *d3d11Device;
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,
&d3d11Device,
NULL,
&renderer->immediateContext
);
if (FAILED(res) && debugMode)
{
/* If device creation failed, and we're in debug mode, remove the debug flag and try again. */
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Creating device in debug mode failed with error " HRESULT_FMT ". Trying non-debug.", res);
flags &= ~D3D11_CREATE_DEVICE_DEBUG;
debugMode = 0;
goto tryCreateDevice;
}
ERROR_CHECK_RETURN("Could not create D3D11 device", NULL);
/* The actual device we want is the ID3D11Device1 interface... */
res = ID3D11Device_QueryInterface(
d3d11Device,
&D3D_IID_ID3D11Device1,
(void**) &renderer->device
);
ERROR_CHECK_RETURN("Could not get ID3D11Device1 interface", NULL);
/* Release the old device interface, we don't need it anymore */
ID3D11Device_Release(d3d11Device);
#ifdef HAVE_IDXGIINFOQUEUE
/* Set up the info queue */
if (renderer->dxgiInfoQueue)
{
DXGI_INFO_QUEUE_MESSAGE_SEVERITY sevList[] =
{
DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION,
DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR,
DXGI_INFO_QUEUE_MESSAGE_SEVERITY_WARNING,
// DXGI_INFO_QUEUE_MESSAGE_SEVERITY_INFO, /* This can be a bit much, so toggle as needed for debugging. */
DXGI_INFO_QUEUE_MESSAGE_SEVERITY_MESSAGE
};
DXGI_INFO_QUEUE_FILTER filter = { 0 };
filter.AllowList.NumSeverities = SDL_arraysize(sevList);
filter.AllowList.pSeverityList = sevList;
IDXGIInfoQueue_PushStorageFilter(
renderer->dxgiInfoQueue,
D3D_IID_DXGI_DEBUG_ALL,
&filter
);
}
#endif
/* Print driver info */
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SDL GPU Driver: D3D11");
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "D3D11 Adapter: %S", adapterDesc.Description);
/* Create mutexes */
renderer->contextLock = SDL_CreateMutex();
renderer->acquireCommandBufferLock = SDL_CreateMutex();
renderer->fenceLock = SDL_CreateMutex();
renderer->windowLock = SDL_CreateMutex();
/* Initialize miscellaneous renderer members */
renderer->debugMode = (flags & D3D11_CREATE_DEVICE_DEBUG);
/* Initialize SetStringMarker support, if available */
res = ID3D11DeviceContext_QueryInterface(
renderer->immediateContext,
&D3D_IID_ID3DUserDefinedAnnotation,
(void**) &renderer->annotation
);
if (res < 0)
{
D3D11_INTERNAL_LogError(renderer->device, "Could not get UserDefinedAnnotation", res);
renderer->annotation = NULL;
}
/* Create command buffer pool */
D3D11_INTERNAL_AllocateCommandBuffers(renderer, 2);
/* Create fence pool */
renderer->availableFenceCapacity = 2;
renderer->availableFences = SDL_malloc(
sizeof(D3D11Fence*) * renderer->availableFenceCapacity
);
/* Create deferred destroy arrays */
renderer->transferBufferContainersToDestroyCapacity = 2;
renderer->transferBufferContainersToDestroyCount = 0;
renderer->transferBufferContainersToDestroy = SDL_malloc(
renderer->transferBufferContainersToDestroyCapacity * sizeof(D3D11TransferBufferContainer*)
);
renderer->bufferContainersToDestroyCapacity = 2;
renderer->bufferContainersToDestroyCount = 0;
renderer->bufferContainersToDestroy = SDL_malloc(
renderer->bufferContainersToDestroyCapacity * sizeof(D3D11BufferContainer*)
);
renderer->textureContainersToDestroyCapacity = 2;
renderer->textureContainersToDestroyCount = 0;
renderer->textureContainersToDestroy = SDL_malloc(
renderer->textureContainersToDestroyCapacity * sizeof(D3D11TextureContainer*)
);
/* Create claimed window list */
renderer->claimedWindowCapacity = 1;
renderer->claimedWindows = SDL_malloc(
sizeof(D3D11WindowData*) * renderer->claimedWindowCapacity
);
/* Initialize null states */
SDL_zeroa(nullRTVs);
SDL_zeroa(nullSRVs);
SDL_zeroa(nullSamplers);
SDL_zeroa(nullUAVs);
/* Initialize built-in pipelines */
D3D11_INTERNAL_InitBlitPipelines(renderer);
/* Create the Refresh_ Device */
result = (Refresh_Device*) SDL_malloc(sizeof(Refresh_Device));
ASSIGN_DRIVER(D3D11)
result->driverData = (Refresh_Renderer*) renderer;
return result;
}
Refresh_Driver D3D11Driver = {
"D3D11",
REFRESH_BACKEND_D3D11,
D3D11_PrepareDriver,
D3D11_CreateDevice
};
#endif /* REFRESH_D3D11 */