diff --git a/CMakeLists.txt b/CMakeLists.txt index 7095da0..c79d471 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,12 @@ add_definitions( -DREFRESH_DRIVER_VULKAN ) +if (WIN32) + add_definitions( + -DREFRESH_DRIVER_D3D11 + ) +endif() + # Source lists add_library(Refresh # Public Headers @@ -49,8 +55,10 @@ add_library(Refresh # Internal Headers src/Refresh_Driver.h src/Refresh_Driver_Vulkan_vkfuncs.h + src/Refresh_Driver_D3D11_cdefines.h # Source Files src/Refresh.c + src/Refresh_Driver_D3D11.c src/Refresh_Driver_Vulkan.c src/Refresh_Image.c ) diff --git a/include/Refresh.h b/include/Refresh.h index 3e1a490..c4be92d 100644 --- a/include/Refresh.h +++ b/include/Refresh.h @@ -343,6 +343,7 @@ typedef enum Refresh_Backend { REFRESH_BACKEND_DONTCARE, REFRESH_BACKEND_VULKAN, + REFRESH_BACKEND_D3D11, REFRESH_BACKEND_PS5, REFRESH_BACKEND_INVALID } Refresh_Backend; @@ -1192,46 +1193,6 @@ REFRESHAPI void Refresh_UploadToBuffer( Refresh_WriteOptions writeOption ); -/* GPU-to-CPU copies occur on the GPU timeline. - * - * You may NOT assume that the data in the TransferBuffer is fully copied - * until the command buffer has finished execution. - */ - -/* - * transferOption: - * SAFEDISCARD: - * If this TransferBuffer has been used in commands that have not completed, - * the issued commands will still be valid at the cost of increased memory usage. - * You may NOT assume that any of the previous data is retained. - * If the TransferBuffer was not in use, this option is equivalent to OVERWRITE. - * It is not recommended to use this option with large TransferBuffers. - * - * OVERWRITE: - * Overwrites the data regardless of whether a command has been issued. - * Use this option with great care, as it can cause data races to occur! - */ - -/* Downloads data from a texture to a TransferBuffer. */ -REFRESHAPI void Refresh_DownloadFromTexture( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureRegion *textureRegion, - Refresh_TransferBuffer *transferBuffer, - Refresh_BufferImageCopy *copyParams, - Refresh_TransferOptions transferOption -); - -/* Downloads data from a GpuBuffer object. */ -REFRESHAPI void Refresh_DownloadFromBuffer( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_GpuBuffer *gpuBuffer, - Refresh_TransferBuffer *transferBuffer, - Refresh_BufferCopy *copyParams, - Refresh_TransferOptions transferOption -); - /* GPU-to-GPU copies occur on the GPU timeline, * and you may assume the copy has finished in subsequent commands. */ @@ -1259,26 +1220,6 @@ REFRESHAPI void Refresh_CopyTextureToTexture( Refresh_WriteOptions writeOption ); -/* Copies image data from a texture slice into a buffer. */ -REFRESHAPI void Refresh_CopyTextureToBuffer( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureRegion *textureRegion, - Refresh_GpuBuffer *gpuBuffer, - Refresh_BufferImageCopy *copyParams, - Refresh_WriteOptions writeOption -); - -/* Copies data from a buffer to a texture slice. */ -REFRESHAPI void Refresh_CopyBufferToTexture( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_GpuBuffer *gpuBuffer, - Refresh_TextureRegion *textureRegion, - Refresh_BufferImageCopy *copyParams, - Refresh_WriteOptions writeOption -); - /* Copies data from a buffer to a buffer. */ REFRESHAPI void Refresh_CopyBufferToBuffer( Refresh_Device *device, @@ -1420,6 +1361,49 @@ REFRESHAPI void Refresh_ReleaseFence( Refresh_Fence *fence ); +/* Readback */ + +/* GPU-to-CPU copies occur immediately on the CPU timeline. + * + * If you modify data on the GPU and then call these functions without calling Wait or WaitForFences first, + * the data will be undefined! + * + * Readback forces a sync point and is generally a bad thing to do. + * Only use these functions if you have exhausted all other options. + */ + +/* + * transferOption: + * SAFEDISCARD: + * If this TransferBuffer has been used in commands that have not completed, + * the issued commands will still be valid at the cost of increased memory usage. + * You may NOT assume that any of the previous data is retained. + * If the TransferBuffer was not in use, this option is equivalent to OVERWRITE. + * It is not recommended to use this option with large TransferBuffers. + * + * OVERWRITE: + * Overwrites the data regardless of whether a command has been issued. + * Use this option with great care, as it can cause data races to occur! + */ + +/* Downloads data from a texture to a TransferBuffer. */ +REFRESHAPI void Refresh_DownloadFromTexture( + Refresh_Device *device, + Refresh_TextureRegion *textureRegion, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferImageCopy *copyParams, + Refresh_TransferOptions transferOption +); + +/* Downloads data from a GpuBuffer object. */ +REFRESHAPI void Refresh_DownloadFromBuffer( + Refresh_Device *device, + Refresh_GpuBuffer *gpuBuffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams, + Refresh_TransferOptions transferOption +); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/shadercompiler/Program.cs b/shadercompiler/Program.cs index 1dba27e..7e3f10d 100644 --- a/shadercompiler/Program.cs +++ b/shadercompiler/Program.cs @@ -194,26 +194,37 @@ partial class Program // Magic writer.Write(new char[] { 'R', 'F', 'S', 'H'}); + // Type + uint shaderTypeIndex; + switch (shaderType) + { + default: + case ".vert": shaderTypeIndex = 0; break; + case ".frag": shaderTypeIndex = 1; break; + case ".comp": shaderTypeIndex = 2; break; + } + writer.Write(shaderTypeIndex); + if (data.vulkan) { string inputPath = Path.Combine(tempDir, $"{shaderName}.spv"); WriteShaderBlob(writer, inputPath, 1); } + if (data.d3d11) + { + string inputPath = Path.Combine(tempDir, $"{shaderName}.hlsl"); + WriteShaderBlob(writer, inputPath, 2); + } + #if PS5 if (data.ps5) { string ext = GetPS5ShaderFileExtension(); string inputPath = Path.Combine(tempDir, $"{shaderName}{ext}"); - WriteShaderBlob(writer, inputPath, 2); - } -#endif - - if (data.d3d11) - { - string inputPath = Path.Combine(tempDir, $"{shaderName}.hlsl"); WriteShaderBlob(writer, inputPath, 3); } +#endif } } @@ -254,7 +265,7 @@ partial class Program { Process spirvcross = Process.Start( "spirv-cross", - $"\"{spirvPath}\" --hlsl --shader-model 50 --output \"{outputPath}\"" + $"\"{spirvPath}\" --hlsl --flip-vert-y --shader-model 50 --output \"{outputPath}\"" ); spirvcross.WaitForExit(); if (spirvcross.ExitCode != 0) diff --git a/src/Refresh.c b/src/Refresh.c index 08cdfb4..e6ac313 100644 --- a/src/Refresh.c +++ b/src/Refresh.c @@ -39,6 +39,12 @@ #define VULKAN_DRIVER NULL #endif +#ifdef REFRESH_DRIVER_D3D11 + #define D3D11_DRIVER &D3D11Driver +#else + #define D3D11_DRIVER NULL +#endif + #ifdef REFRESH_DRIVER_PS5 #define PS5_DRIVER &PS5Driver #else @@ -48,6 +54,7 @@ static const Refresh_Driver *backends[] = { NULL, VULKAN_DRIVER, + D3D11_DRIVER, PS5_DRIVER }; @@ -235,7 +242,7 @@ Refresh_ShaderModule* Refresh_CreateShaderModule( Refresh_Device *device, Refresh_ShaderModuleCreateInfo *shaderModuleCreateInfo ) { - Refresh_ShaderModuleCreateInfo driverSpecificCreateInfo = { 0, NULL }; + Refresh_Driver_ShaderModuleCreateInfo driverSpecificCreateInfo = { 0, NULL, 0 }; uint8_t *bytes; uint32_t i, size; @@ -245,12 +252,24 @@ Refresh_ShaderModule* Refresh_CreateShaderModule( bytes = (uint8_t*) shaderModuleCreateInfo->byteCode; if (bytes[0] != 'R' || bytes[1] != 'F' || bytes[2] != 'S' || bytes[3] != 'H') { - Refresh_LogError("Cannot parse malformed Refresh shader blob!"); + Refresh_LogError("Cannot parse malformed Refresh shader blob: Incorrect magic number"); + return NULL; + } + + /* get the type of shader */ + driverSpecificCreateInfo.type = (Refresh_Driver_ShaderType) *((uint32_t*) &bytes[4]); + if ( driverSpecificCreateInfo.type < 0 || + driverSpecificCreateInfo.type > REFRESH_DRIVER_SHADERTYPE_COMPUTE ) + { + Refresh_LogError( + "Cannot parse malformed Refresh shader blob: Unknown shader type (%d)", + driverSpecificCreateInfo.type + ); return NULL; } /* find the code for the selected backend */ - i = 4; + i = 8; while (i < shaderModuleCreateInfo->codeSize) { size = *((uint32_t*) &bytes[i + 1]); @@ -805,44 +824,6 @@ void Refresh_UploadToBuffer( ); } -void Refresh_DownloadFromTexture( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureRegion *textureRegion, - Refresh_TransferBuffer *transferBuffer, - Refresh_BufferImageCopy *copyParams, - Refresh_TransferOptions transferOption -) { - NULL_RETURN(device); - device->DownloadFromTexture( - device->driverData, - commandBuffer, - textureRegion, - transferBuffer, - copyParams, - transferOption - ); -} - -void Refresh_DownloadFromBuffer( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_GpuBuffer *gpuBuffer, - Refresh_TransferBuffer *transferBuffer, - Refresh_BufferCopy *copyParams, - Refresh_TransferOptions transferOption -) { - NULL_RETURN(device); - device->DownloadFromBuffer( - device->driverData, - commandBuffer, - gpuBuffer, - transferBuffer, - copyParams, - transferOption - ); -} - void Refresh_CopyTextureToTexture( Refresh_Device *device, Refresh_CommandBuffer *commandBuffer, @@ -860,44 +841,6 @@ void Refresh_CopyTextureToTexture( ); } -void Refresh_CopyTextureToBuffer( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureRegion *textureRegion, - Refresh_GpuBuffer *gpuBuffer, - Refresh_BufferImageCopy *copyParameters, - Refresh_WriteOptions writeOption -) { - NULL_RETURN(device); - device->CopyTextureToBuffer( - device->driverData, - commandBuffer, - textureRegion, - gpuBuffer, - copyParameters, - writeOption - ); -} - -void Refresh_CopyBufferToTexture( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_GpuBuffer *gpuBuffer, - Refresh_TextureRegion *textureRegion, - Refresh_BufferImageCopy *copyParams, - Refresh_WriteOptions writeOption -) { - NULL_RETURN(device); - device->CopyBufferToTexture( - device->driverData, - commandBuffer, - gpuBuffer, - textureRegion, - copyParams, - writeOption - ); -} - void Refresh_CopyBufferToBuffer( Refresh_Device *device, Refresh_CommandBuffer *commandBuffer, @@ -1067,9 +1010,7 @@ int Refresh_QueryFence( Refresh_Device *device, Refresh_Fence *fence ) { - if (device == NULL) { - return 0; - } + if (device == NULL) { return 0; } return device->QueryFence( device->driverData, @@ -1088,4 +1029,38 @@ void Refresh_ReleaseFence( ); } +void Refresh_DownloadFromTexture( + Refresh_Device *device, + Refresh_TextureRegion *textureRegion, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferImageCopy *copyParams, + Refresh_TransferOptions transferOption +) { + NULL_RETURN(device); + device->DownloadFromTexture( + device->driverData, + textureRegion, + transferBuffer, + copyParams, + transferOption + ); +} + +void Refresh_DownloadFromBuffer( + Refresh_Device *device, + Refresh_GpuBuffer *gpuBuffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams, + Refresh_TransferOptions transferOption +) { + NULL_RETURN(device); + device->DownloadFromBuffer( + device->driverData, + gpuBuffer, + transferBuffer, + copyParams, + transferOption + ); +} + /* vim: set noexpandtab shiftwidth=8 tabstop=8: */ diff --git a/src/Refresh_Driver.h b/src/Refresh_Driver.h index a7d58c7..bb6d60f 100644 --- a/src/Refresh_Driver.h +++ b/src/Refresh_Driver.h @@ -48,6 +48,46 @@ void Refresh_LogError(const char *fmt, ...); /* Internal Helper Utilities */ +static inline int32_t Texture_GetBlockSize( + Refresh_TextureFormat format +) { + switch (format) + { + case REFRESH_TEXTUREFORMAT_BC1: + case REFRESH_TEXTUREFORMAT_BC2: + case REFRESH_TEXTUREFORMAT_BC3: + case REFRESH_TEXTUREFORMAT_BC7: + return 4; + case REFRESH_TEXTUREFORMAT_R8: + case REFRESH_TEXTUREFORMAT_R8_UINT: + case REFRESH_TEXTUREFORMAT_R5G6B5: + case REFRESH_TEXTUREFORMAT_B4G4R4A4: + case REFRESH_TEXTUREFORMAT_A1R5G5B5: + case REFRESH_TEXTUREFORMAT_R16_SFLOAT: + case REFRESH_TEXTUREFORMAT_R8G8_SNORM: + case REFRESH_TEXTUREFORMAT_R8G8_UINT: + case REFRESH_TEXTUREFORMAT_R16_UINT: + case REFRESH_TEXTUREFORMAT_R8G8B8A8: + case REFRESH_TEXTUREFORMAT_R32_SFLOAT: + case REFRESH_TEXTUREFORMAT_R16G16_SFLOAT: + case REFRESH_TEXTUREFORMAT_R8G8B8A8_SNORM: + case REFRESH_TEXTUREFORMAT_A2R10G10B10: + case REFRESH_TEXTUREFORMAT_R8G8B8A8_UINT: + case REFRESH_TEXTUREFORMAT_R16G16_UINT: + case REFRESH_TEXTUREFORMAT_R16G16B16A16_SFLOAT: + case REFRESH_TEXTUREFORMAT_R16G16B16A16: + case REFRESH_TEXTUREFORMAT_R32G32_SFLOAT: + case REFRESH_TEXTUREFORMAT_R16G16B16A16_UINT: + case REFRESH_TEXTUREFORMAT_R32G32B32A32_SFLOAT: + return 1; + default: + Refresh_LogError( + "Unrecognized TextureFormat!" + ); + return 0; + } +} + static inline uint32_t Texture_GetFormatSize( Refresh_TextureFormat format ) { @@ -87,7 +127,7 @@ static inline uint32_t Texture_GetFormatSize( return 16; default: Refresh_LogError( - "Unrecognized SurfaceFormat!" + "Unrecognized TextureFormat!" ); return 0; } @@ -171,6 +211,22 @@ static inline int32_t BytesPerImage( #define MAX_COLOR_TARGET_BINDINGS 4 #define MAX_PRESENT_COUNT 16 +/* Internal Shader Module Create Info */ + +typedef enum Refresh_Driver_ShaderType +{ + REFRESH_DRIVER_SHADERTYPE_VERTEX, + REFRESH_DRIVER_SHADERTYPE_FRAGMENT, + REFRESH_DRIVER_SHADERTYPE_COMPUTE +} Refresh_Driver_ShaderType; + +typedef struct Refresh_Driver_ShaderModuleCreateInfo +{ + size_t codeSize; + const uint32_t* byteCode; + Refresh_Driver_ShaderType type; +} Refresh_Driver_ShaderModuleCreateInfo; + /* Refresh_Device Definition */ typedef struct Refresh_Renderer Refresh_Renderer; @@ -200,7 +256,7 @@ struct Refresh_Device Refresh_ShaderModule* (*CreateShaderModule)( Refresh_Renderer *driverData, - Refresh_ShaderModuleCreateInfo *shaderModuleCreateInfo + Refresh_Driver_ShaderModuleCreateInfo *shaderModuleCreateInfo ); Refresh_Texture* (*CreateTexture)( @@ -450,24 +506,6 @@ struct Refresh_Device Refresh_WriteOptions writeOption ); - void (*DownloadFromTexture)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureRegion *textureSlice, - Refresh_TransferBuffer *transferBuffer, - Refresh_BufferImageCopy *copyParams, - Refresh_TransferOptions transferOption - ); - - void (*DownloadFromBuffer)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_GpuBuffer *gpuBuffer, - Refresh_TransferBuffer *transferBuffer, - Refresh_BufferCopy *copyParams, - Refresh_TransferOptions transferOption - ); - void (*CopyTextureToTexture)( Refresh_Renderer *driverData, Refresh_CommandBuffer *commandBuffer, @@ -476,24 +514,6 @@ struct Refresh_Device Refresh_WriteOptions writeOption ); - void (*CopyTextureToBuffer)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureRegion *textureSlice, - Refresh_GpuBuffer *gpuBuffer, - Refresh_BufferImageCopy *copyParams, - Refresh_WriteOptions writeOption - ); - - void (*CopyBufferToTexture)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_GpuBuffer *gpuBuffer, - Refresh_TextureRegion *textureSlice, - Refresh_BufferImageCopy *copyParams, - Refresh_WriteOptions writeOption - ); - void (*CopyBufferToBuffer)( Refresh_Renderer *driverData, Refresh_CommandBuffer *commandBuffer, @@ -581,6 +601,22 @@ struct Refresh_Device Refresh_Fence *fence ); + void (*DownloadFromTexture)( + Refresh_Renderer *driverData, + Refresh_TextureRegion *textureSlice, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferImageCopy *copyParams, + Refresh_TransferOptions transferOption + ); + + void (*DownloadFromBuffer)( + Refresh_Renderer *driverData, + Refresh_GpuBuffer *gpuBuffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams, + Refresh_TransferOptions transferOption + ); + /* Opaque pointer for the Driver */ Refresh_Renderer *driverData; }; @@ -633,8 +669,6 @@ struct Refresh_Device ASSIGN_DRIVER_FUNC(DownloadFromTexture, name) \ ASSIGN_DRIVER_FUNC(DownloadFromBuffer, name) \ ASSIGN_DRIVER_FUNC(CopyTextureToTexture, name) \ - ASSIGN_DRIVER_FUNC(CopyTextureToBuffer, name) \ - ASSIGN_DRIVER_FUNC(CopyBufferToTexture, name) \ ASSIGN_DRIVER_FUNC(CopyBufferToBuffer, name) \ ASSIGN_DRIVER_FUNC(GenerateMipmaps, name) \ ASSIGN_DRIVER_FUNC(EndCopyPass, name) \ @@ -661,6 +695,7 @@ typedef struct Refresh_Driver } Refresh_Driver; extern Refresh_Driver VulkanDriver; +extern Refresh_Driver D3D11Driver; extern Refresh_Driver PS5Driver; #endif /* REFRESH_DRIVER_H */ diff --git a/src/Refresh_Driver_D3D11.c b/src/Refresh_Driver_D3D11.c new file mode 100644 index 0000000..a11621b --- /dev/null +++ b/src/Refresh_Driver_D3D11.c @@ -0,0 +1,4678 @@ +/* Refresh - XNA-inspired 3D Graphics Library with modern capabilities + * + * Copyright (c) 2020 Evan Hemsley + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + * Evan "cosmonaut" Hemsley + * + */ + +#if REFRESH_DRIVER_D3D11 + +#define D3D11_NO_HELPERS +#define CINTERFACE +#define COBJMACROS + +#include +#include +#include +#include +#include +#include + +#include "Refresh_Driver.h" +#include "Refresh_Driver_D3D11_cdefines.h" + +#include +#include + + /* Defines */ + +#define D3D11_DLL "d3d11.dll" +#define DXGI_DLL "dxgi.dll" +#define DXGIDEBUG_DLL "dxgidebug.dll" +#define D3D11_CREATE_DEVICE_FUNC "D3D11CreateDevice" +#define D3DCOMPILE_FUNC "D3DCompile" +#define CREATE_DXGI_FACTORY1_FUNC "CreateDXGIFactory1" +#define DXGI_GET_DEBUG_INTERFACE_FUNC "DXGIGetDebugInterface" +#define WINDOW_DATA "Refresh_D3D11WindowData" +#define UBO_BUFFER_SIZE 1048576 /* 1 MiB */ + +#define NOT_IMPLEMENTED SDL_assert(0 && "Not implemented!"); + +/* Macros */ + +#define ERROR_CHECK(msg) \ + if (FAILED(res)) \ + { \ + D3D11_INTERNAL_LogError(renderer->device, msg, res); \ + } + +#define ERROR_CHECK_RETURN(msg, ret) \ + if (FAILED(res)) \ + { \ + D3D11_INTERNAL_LogError(renderer->device, msg, res); \ + return ret; \ + } + +#define EXPAND_ARRAY_IF_NEEDED(arr, elementType, newCount, capacity, newCapacity) \ + if (newCount >= capacity) \ + { \ + capacity = newCapacity; \ + arr = (elementType*) SDL_realloc( \ + arr, \ + sizeof(elementType) * capacity \ + ); \ + } + +/* D3DCompile signature */ + +typedef HRESULT(WINAPI *PFN_D3DCOMPILE)( + LPCVOID pSrcData, + SIZE_T SrcDataSize, + LPCSTR pSourceName, + const D3D_SHADER_MACRO* pDefines, + ID3DInclude* pInclude, + LPCSTR pEntrypoint, + LPCSTR pTarget, + UINT Flags1, + UINT Flags2, + ID3DBlob **ppCode, + ID3DBlob **ppErrorMsgs +); + +/* Forward Declarations */ + +static void D3D11_Wait(Refresh_Renderer *driverData); +static void D3D11_UnclaimWindow( + Refresh_Renderer * driverData, + void *windowHandle +); + + /* Conversions */ + +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_R16G16_UNORM, /* R16G16 */ + DXGI_FORMAT_R16G16B16A16_UNORM, /* R16G16B16A16 */ + DXGI_FORMAT_R8_UNORM, /* R8 */ + 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_D16_UNORM, /* D16_UNORM */ + DXGI_FORMAT_D32_FLOAT, /* D32_SFLOAT */ + DXGI_FORMAT_D24_UNORM_S8_UINT, /* D16_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_t 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_FILL_MODE RefreshToD3D11_PolygonMode[] = +{ + D3D11_FILL_SOLID, /* FILL */ + D3D11_FILL_WIREFRAME, /* LINE */ +}; + +static D3D11_CULL_MODE RefreshToD3D11_CullMode[] = +{ + D3D11_CULL_NONE, /* NONE */ + D3D11_CULL_FRONT, /* FRONT */ + D3D11_CULL_BACK /* BACK */ +}; + +static D3D11_BLEND RefreshToD3D11_BlendFactor[] = +{ + D3D11_BLEND_ZERO, /* ZERO */ + D3D11_BLEND_ONE, /* ONE */ + D3D11_BLEND_SRC_COLOR, /* SRC_COLOR */ + D3D11_BLEND_INV_SRC_COLOR, /* ONE_MINUS_SRC_COLOR */ + D3D11_BLEND_DEST_COLOR, /* DST_COLOR */ + D3D11_BLEND_INV_DEST_COLOR, /* ONE_MINUS_DST_COLOR */ + D3D11_BLEND_SRC_ALPHA, /* SRC_ALPHA */ + D3D11_BLEND_INV_SRC_ALPHA, /* ONE_MINUS_SRC_ALPHA */ + D3D11_BLEND_DEST_ALPHA, /* DST_ALPHA */ + D3D11_BLEND_INV_DEST_ALPHA, /* ONE_MINUS_DST_ALPHA */ + D3D11_BLEND_BLEND_FACTOR, /* CONSTANT_COLOR */ + D3D11_BLEND_INV_BLEND_FACTOR, /* ONE_MINUS_CONSTANT_COLOR */ + D3D11_BLEND_SRC_ALPHA_SAT, /* SRC_ALPHA_SATURATE */ +}; + +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_SamplerStateCreateInfo *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_SamplerStateCreateInfo *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 D3D11TextureSubresource +{ + ID3D11RenderTargetView *colorTargetView; /* NULL if not a color target */ + ID3D11DepthStencilView *depthStencilTargetView; /* NULL if not a depth stencil target */ + ID3D11UnorderedAccessView *uav; /* NULL if not used in compute */ + ID3D11Resource *msaaHandle; /* NULL if not using MSAA */ + ID3D11RenderTargetView *msaaTargetView; /* NULL if not an MSAA color target */ + uint32_t level; + uint32_t layer; +} D3D11TextureSubresource; + +typedef struct D3D11Texture +{ + /* D3D Handles */ + ID3D11Resource *handle; /* ID3D11Texture2D* or ID3D11Texture3D* */ + ID3D11ShaderResourceView *shaderView; + + D3D11TextureSubresource *subresources; /* count is layerCount * levelCount */ + + /* Basic Info */ + Refresh_TextureFormat format; + uint32_t width; + uint32_t height; + uint32_t depth; + uint32_t levelCount; + uint32_t layerCount; + uint8_t isCube; + uint8_t isRenderTarget; +} D3D11Texture; + +typedef struct D3D11WindowData +{ + void* windowHandle; + IDXGISwapChain *swapchain; + D3D11Texture texture; + Refresh_PresentMode presentMode; +} D3D11WindowData; + +typedef struct D3D11ShaderModule +{ + ID3D11DeviceChild *shader; /* ID3D11VertexShader, ID3D11PixelShader, ID3D11ComputeShader */ + ID3D10Blob *blob; +} D3D11ShaderModule; + +typedef struct D3D11GraphicsPipeline +{ + float blendConstants[4]; + int32_t numColorAttachments; + DXGI_FORMAT colorAttachmentFormats[MAX_COLOR_TARGET_BINDINGS]; + ID3D11BlendState *colorAttachmentBlendState; + + Refresh_MultisampleState multisampleState; + + uint8_t hasDepthStencilAttachment; + DXGI_FORMAT depthStencilAttachmentFormat; + ID3D11DepthStencilState *depthStencilState; + uint32_t stencilRef; + + Refresh_PrimitiveType primitiveType; + ID3D11RasterizerState *rasterizerState; + + ID3D11VertexShader *vertexShader; + ID3D11InputLayout *inputLayout; + uint32_t *vertexStrides; + uint32_t numVertexSamplers; + uint32_t vertexUniformBlockSize; + + ID3D11PixelShader *fragmentShader; + uint32_t numFragmentSamplers; + uint32_t fragmentUniformBlockSize; +} D3D11GraphicsPipeline; + +typedef struct D3D11ComputePipeline +{ + ID3D11ComputeShader *computeShader; + uint32_t computeUniformBlockSize; + uint32_t numTextures; + uint32_t numBuffers; +} D3D11ComputePipeline; + +typedef struct D3D11Buffer +{ + ID3D11Buffer *handle; + ID3D11UnorderedAccessView* uav; + uint32_t size; +} D3D11Buffer; + +typedef struct D3D11TransferBuffer +{ + uint8_t *data; + uint32_t size; + SDL_atomic_t referenceCount; +} D3D11TransferBuffer; + +typedef struct D3D11TransferBufferContainer +{ + D3D11TransferBuffer *activeBuffer; + + /* These are all the buffers that have been used by this container. + * If the resource is bound and then updated with SafeDiscard, a new resource + * will be added to this list. + * These can be reused after they are submitted and command processing is complete. + */ + uint32_t bufferCapacity; + uint32_t bufferCount; + D3D11TransferBuffer *buffers; +} D3D11TransferBufferContainer; + +typedef struct D3D11UniformBuffer +{ + D3D11Buffer d3d11Buffer; + uint32_t offset; /* number of bytes written */ + uint32_t drawOffset; /* parameter for SetConstantBuffers */ +} D3D11UniformBuffer; + +typedef struct D3D11Fence +{ + ID3D11Query *handle; +} D3D11Fence; + +typedef struct D3D11CommandBuffer +{ + /* Deferred Context */ + ID3D11DeviceContext1 *context; + + /* Window */ + D3D11WindowData *windowData; + + /* Render Pass */ + D3D11GraphicsPipeline *graphicsPipeline; + + /* Render Pass MSAA resolve */ + D3D11Texture *colorTargetResolveTexture[MAX_COLOR_TARGET_BINDINGS]; + uint32_t colorTargetResolveSubresourceIndex[MAX_COLOR_TARGET_BINDINGS]; + ID3D11Resource *colorTargetMsaaHandle[MAX_COLOR_TARGET_BINDINGS]; + + /* Compute Pass */ + D3D11ComputePipeline *computePipeline; + + /* Fences */ + D3D11Fence *fence; + uint8_t autoReleaseFence; + + /* Uniforms */ + D3D11UniformBuffer *vertexUniformBuffer; + D3D11UniformBuffer *fragmentUniformBuffer; + D3D11UniformBuffer *computeUniformBuffer; + + D3D11UniformBuffer **boundUniformBuffers; + uint32_t boundUniformBufferCount; + uint32_t boundUniformBufferCapacity; + + /* Transfer Reference Counting */ + D3D11TransferBuffer **usedTransferBuffers; + uint32_t usedTransferBufferCount; + uint32_t usedTransferBufferCapacity; +} D3D11CommandBuffer; + +typedef struct D3D11Sampler +{ + ID3D11SamplerState *handle; +} D3D11Sampler; + +typedef struct D3D11Renderer +{ + ID3D11Device1 *device; + ID3D11DeviceContext *immediateContext; + IDXGIFactory1 *factory; + IDXGIAdapter1 *adapter; + IDXGIDebug *dxgiDebug; + IDXGIInfoQueue *dxgiInfoQueue; + void *d3d11_dll; + void *dxgi_dll; + void *dxgidebug_dll; + void *d3dcompiler_dll; + + uint8_t debugMode; + BOOL supportsTearing; + uint8_t supportsFlipDiscard; + + PFN_D3DCOMPILE D3DCompileFunc; + + D3D11WindowData **claimedWindows; + uint32_t claimedWindowCount; + uint32_t claimedWindowCapacity; + + D3D11CommandBuffer **availableCommandBuffers; + uint32_t availableCommandBufferCount; + uint32_t availableCommandBufferCapacity; + + D3D11CommandBuffer **submittedCommandBuffers; + uint32_t submittedCommandBufferCount; + uint32_t submittedCommandBufferCapacity; + + D3D11UniformBuffer **availableUniformBuffers; + uint32_t availableUniformBufferCount; + uint32_t availableUniformBufferCapacity; + + D3D11Fence **availableFences; + uint32_t availableFenceCount; + uint32_t availableFenceCapacity; + + D3D11TransferBufferContainer **transferBufferContainersToDestroy; + uint32_t transferBufferContainersToDestroyCount; + uint32_t transferBufferContainersToDestroyCapacity; + + SDL_mutex *contextLock; + SDL_mutex *acquireCommandBufferLock; + SDL_mutex *uniformBufferLock; + SDL_mutex *fenceLock; + SDL_mutex *windowLock; +} D3D11Renderer; + +/* 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. */ + dwChars = FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + res, + 0, + wszMsgBuff, + MAX_ERROR_LEN, + NULL + ); + + /* No message? Screw it, just post the code. */ + if (dwChars == 0) + { + Refresh_LogError("%s! Error Code: 0x%08X", msg, res); + return; + } + + /* Ensure valid range */ + dwChars = SDL_min(dwChars, MAX_ERROR_LEN); + + /* Trim whitespace from tail of message */ + while (dwChars > 0) + { + if (wszMsgBuff[dwChars - 1] <= ' ') + { + dwChars--; + } + else + { + break; + } + } + + /* Ensure null-terminated string */ + wszMsgBuff[dwChars] = '\0'; + + Refresh_LogError("%s! Error Code: %s (0x%08X)", msg, wszMsgBuff, res); +} + +/* Helper Functions */ + +static inline uint32_t D3D11_INTERNAL_CalcSubresource( + uint32_t mipLevel, + uint32_t arraySlice, + uint32_t numLevels +) { + return mipLevel + (arraySlice * numLevels); +} + +static inline uint32_t D3D11_INTERNAL_NextHighestAlignment( + uint32_t n, + uint32_t 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: + Refresh_LogError("Cannot get typeless DXGI format of format %d", typedFormat); + return 0; + } +} + +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_DestroyDevice( + Refresh_Device *device +) { + D3D11Renderer *renderer = (D3D11Renderer*) device->driverData; + + /* Flush any remaining GPU work... */ + D3D11_Wait(device->driverData); + + /* Release the window data */ + for (int32_t i = renderer->claimedWindowCount - 1; i >= 0; i -= 1) + { + D3D11_UnclaimWindow(device->driverData, renderer->claimedWindows[i]->windowHandle); + } + SDL_free(renderer->claimedWindows); + + /* Release command buffer infrastructure */ + for (uint32_t i = 0; i < renderer->availableCommandBufferCount; i += 1) + { + D3D11CommandBuffer *commandBuffer = renderer->availableCommandBuffers[i]; + ID3D11DeviceContext_Release(commandBuffer->context); + SDL_free(commandBuffer->boundUniformBuffers); + SDL_free(commandBuffer->usedTransferBuffers); + SDL_free(commandBuffer); + } + SDL_free(renderer->availableCommandBuffers); + SDL_free(renderer->submittedCommandBuffers); + + /* Release uniform buffer infrastructure */ + for (uint32_t i = 0; i < renderer->availableUniformBufferCount; i += 1) + { + D3D11UniformBuffer *uniformBuffer = renderer->availableUniformBuffers[i]; + ID3D11Buffer_Release(uniformBuffer->d3d11Buffer.handle); + SDL_free(uniformBuffer); + } + SDL_free(renderer->availableUniformBuffers); + + /* Release fence infrastructure */ + for (uint32_t 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 mutexes */ + SDL_DestroyMutex(renderer->acquireCommandBufferLock); + SDL_DestroyMutex(renderer->contextLock); + SDL_DestroyMutex(renderer->uniformBufferLock); + 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); + } + + if (renderer->dxgiInfoQueue) + { + IDXGIInfoQueue_Release(renderer->dxgiInfoQueue); + } + + /* Release the DLLs */ + SDL_UnloadObject(renderer->d3d11_dll); + SDL_UnloadObject(renderer->dxgi_dll); + if (renderer->dxgidebug_dll) + { + SDL_UnloadObject(renderer->dxgidebug_dll); + } + SDL_UnloadObject(renderer->d3dcompiler_dll); + + /* Free the primary Refresh structures */ + SDL_free(renderer); + SDL_free(device); +} + +/* Drawing */ + +static void D3D11_SetGraphicsConstantBuffers( + D3D11CommandBuffer *commandBuffer +) { + uint32_t vertexOffsetInConstants = commandBuffer->vertexUniformBuffer != NULL ? commandBuffer->vertexUniformBuffer->drawOffset / 16 : 0; + uint32_t fragmentOffsetInConstants = commandBuffer->fragmentUniformBuffer != NULL ? commandBuffer->fragmentUniformBuffer->drawOffset / 16 : 0; + uint32_t vertexBlockSizeInConstants = commandBuffer->graphicsPipeline->vertexUniformBlockSize / 16; + uint32_t fragmentBlockSizeInConstants = commandBuffer->graphicsPipeline->fragmentUniformBlockSize / 16; + ID3D11Buffer *nullBuf = NULL; + + if (commandBuffer->vertexUniformBuffer != NULL) + { + /* 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( + commandBuffer->context, + 0, + 1, + &nullBuf + ); + + ID3D11DeviceContext1_VSSetConstantBuffers1( + commandBuffer->context, + 0, + 1, + &commandBuffer->vertexUniformBuffer->d3d11Buffer.handle, + &vertexOffsetInConstants, + &vertexBlockSizeInConstants + ); + } + + if (commandBuffer->fragmentUniformBuffer != NULL) + { + /* another stupid workaround for god awful D3D11 drivers */ + ID3D11DeviceContext1_PSSetConstantBuffers( + commandBuffer->context, + 0, + 1, + &nullBuf + ); + + ID3D11DeviceContext1_PSSetConstantBuffers1( + commandBuffer->context, + 0, + 1, + &commandBuffer->fragmentUniformBuffer->d3d11Buffer.handle, + &fragmentOffsetInConstants, + &fragmentBlockSizeInConstants + ); + } +} + +static void D3D11_DrawInstancedPrimitives( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + uint32_t baseVertex, + uint32_t startIndex, + uint32_t primitiveCount, + uint32_t instanceCount +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + + D3D11_SetGraphicsConstantBuffers(d3d11CommandBuffer); + + ID3D11DeviceContext_DrawIndexedInstanced( + d3d11CommandBuffer->context, + PrimitiveVerts(d3d11CommandBuffer->graphicsPipeline->primitiveType, primitiveCount), + instanceCount, + startIndex, + baseVertex, + 0 + ); +} + +static void D3D11_DrawIndexedPrimitives( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + uint32_t baseVertex, + uint32_t startIndex, + uint32_t primitiveCount +) { + D3D11_DrawInstancedPrimitives( + driverData, + commandBuffer, + baseVertex, + startIndex, + primitiveCount, + 1 + ); +} + +static void D3D11_DrawPrimitives( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + uint32_t vertexStart, + uint32_t primitiveCount +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + + D3D11_SetGraphicsConstantBuffers(d3d11CommandBuffer); + + ID3D11DeviceContext_Draw( + d3d11CommandBuffer->context, + PrimitiveVerts(d3d11CommandBuffer->graphicsPipeline->primitiveType, primitiveCount), + vertexStart + ); +} + +static void D3D11_DrawPrimitivesIndirect( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_GpuBuffer *gpuBuffer, + uint32_t offsetInBytes, + uint32_t drawCount, + uint32_t stride +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Buffer *d3d11Buffer = (D3D11Buffer*) gpuBuffer; + + D3D11_SetGraphicsConstantBuffers(d3d11CommandBuffer); + + /* D3D11: "We have multi-draw at home!" + * Multi-draw at home: + */ + for (uint32_t i = 0; i < drawCount; i += 1) + { + ID3D11DeviceContext_DrawInstancedIndirect( + d3d11CommandBuffer->context, + d3d11Buffer->handle, + offsetInBytes + (stride * i) + ); + } +} + +static void D3D11_DispatchCompute( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + uint32_t groupCountX, + uint32_t groupCountY, + uint32_t groupCountZ +) { + D3D11CommandBuffer* d3d11CommandBuffer = (D3D11CommandBuffer*)commandBuffer; + uint32_t computeOffsetInConstants = d3d11CommandBuffer->computeUniformBuffer != NULL ? d3d11CommandBuffer->computeUniformBuffer->drawOffset / 16 : 0; + uint32_t computeBlockSizeInConstants = (uint32_t) (d3d11CommandBuffer->computePipeline->computeUniformBlockSize / 16); + + ID3D11Buffer *nullBuf = NULL; + + if (d3d11CommandBuffer->computeUniformBuffer != NULL) + { + /* another stupid workaround for god awful D3D11 drivers */ + ID3D11DeviceContext1_CSSetConstantBuffers( + d3d11CommandBuffer->context, + 0, + 1, + &nullBuf + ); + + ID3D11DeviceContext1_CSSetConstantBuffers1( + d3d11CommandBuffer->context, + 0, + 1, + &d3d11CommandBuffer->computeUniformBuffer->d3d11Buffer.handle, + &computeOffsetInConstants, + &computeBlockSizeInConstants + ); + } + + ID3D11DeviceContext_Dispatch( + d3d11CommandBuffer->context, + groupCountX, + groupCountY, + groupCountZ + ); +} + +/* State Creation */ + +static ID3D11BlendState* D3D11_INTERNAL_FetchBlendState( + D3D11Renderer *renderer, + uint32_t 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_t i = 0; i < numColorAttachments; i += 1) + { + blendDesc.RenderTarget[i].BlendEnable = colorAttachments[i].blendState.blendEnable; + blendDesc.RenderTarget[i].BlendOp = RefreshToD3D11_BlendOp[ + colorAttachments[i].blendState.colorBlendOp + ]; + blendDesc.RenderTarget[i].BlendOpAlpha = RefreshToD3D11_BlendOp[ + colorAttachments[i].blendState.alphaBlendOp + ]; + blendDesc.RenderTarget[i].DestBlend = RefreshToD3D11_BlendFactor[ + colorAttachments[i].blendState.dstColorBlendFactor + ]; + blendDesc.RenderTarget[i].DestBlendAlpha = RefreshToD3D11_BlendFactor[ + colorAttachments[i].blendState.dstAlphaBlendFactor + ]; + blendDesc.RenderTarget[i].RenderTargetWriteMask = colorAttachments[i].blendState.colorWriteMask; + blendDesc.RenderTarget[i].SrcBlend = RefreshToD3D11_BlendFactor[ + colorAttachments[i].blendState.srcColorBlendFactor + ]; + blendDesc.RenderTarget[i].SrcBlendAlpha = RefreshToD3D11_BlendFactor[ + colorAttachments[i].blendState.srcAlphaBlendFactor + ]; + } + + res = ID3D11Device_CreateBlendState( + renderer->device, + &blendDesc, + &result + ); + ERROR_CHECK_RETURN("Could not create blend state", NULL); + + return result; +} + +static ID3D11DepthStencilState* D3D11_INTERNAL_FetchDepthStencilState( + D3D11Renderer *renderer, + Refresh_DepthStencilState depthStencilState +) { + ID3D11DepthStencilState *result; + D3D11_DEPTH_STENCIL_DESC dsDesc; + HRESULT res; + + /* Create a new depth-stencil state. + * The spec says the driver will not create duplicate states, so there's no need to cache. + */ + dsDesc.DepthEnable = depthStencilState.depthTestEnable; + dsDesc.StencilEnable = depthStencilState.stencilTestEnable; + dsDesc.DepthFunc = 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_CompareOp[depthStencilState.backStencilState.depthFailOp]; + dsDesc.BackFace.StencilFailOp = RefreshToD3D11_CompareOp[depthStencilState.backStencilState.failOp]; + dsDesc.BackFace.StencilPassOp = RefreshToD3D11_CompareOp[depthStencilState.backStencilState.passOp]; + + dsDesc.FrontFace.StencilFunc = RefreshToD3D11_CompareOp[depthStencilState.frontStencilState.compareOp]; + dsDesc.FrontFace.StencilDepthFailOp = RefreshToD3D11_CompareOp[depthStencilState.frontStencilState.depthFailOp]; + dsDesc.FrontFace.StencilFailOp = RefreshToD3D11_CompareOp[depthStencilState.frontStencilState.failOp]; + dsDesc.FrontFace.StencilPassOp = RefreshToD3D11_CompareOp[depthStencilState.frontStencilState.passOp]; + + dsDesc.StencilReadMask = depthStencilState.compareMask; + dsDesc.StencilWriteMask = depthStencilState.writeMask; + + if (depthStencilState.depthBoundsTestEnable) + { + Refresh_LogWarn("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 = (INT) 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_t D3D11_INTERNAL_FindIndexOfVertexBinding( + uint32_t targetBinding, + const Refresh_VertexBinding *bindings, + uint32_t numBindings +) { + for (uint32_t i = 0; i < numBindings; i += 1) + { + if (bindings[i].binding == targetBinding) + { + return i; + } + } + + Refresh_LogError("Could not find vertex binding %d!", targetBinding); + return 0; +} + +static ID3D11InputLayout* D3D11_INTERNAL_FetchInputLayout( + D3D11Renderer *renderer, + Refresh_VertexInputState inputState, + void *shaderBytes, + size_t shaderByteLength +) { + ID3D11InputLayout *result = NULL; + D3D11_INPUT_ELEMENT_DESC *elementDescs; + uint32_t 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_t i = 0; i < inputState.vertexAttributeCount; i += 1) + { + elementDescs[i].AlignedByteOffset = inputState.vertexAttributes[i].offset; + elementDescs[i].Format = RefreshToD3D11_VertexFormat[ + inputState.vertexAttributes[i].format + ]; + elementDescs[i].InputSlot = inputState.vertexAttributes[i].binding; + + bindingIndex = D3D11_INTERNAL_FindIndexOfVertexBinding( + elementDescs[i].InputSlot, + inputState.vertexBindings, + inputState.vertexBindingCount + ); + elementDescs[i].InputSlotClass = RefreshToD3D11_VertexInputRate[ + inputState.vertexBindings[bindingIndex].inputRate + ]; + /* The spec requires this to be 0 for per-vertex data */ + elementDescs[i].InstanceDataStepRate = ( + elementDescs[i].InputSlotClass == D3D11_INPUT_PER_INSTANCE_DATA ? 1 : 0 + ); + + elementDescs[i].SemanticIndex = inputState.vertexAttributes[i].location; + elementDescs[i].SemanticName = "TEXCOORD"; + } + + res = ID3D11Device_CreateInputLayout( + renderer->device, + elementDescs, + inputState.vertexAttributeCount, + shaderBytes, + shaderByteLength, + &result + ); + if (FAILED(res)) + { + Refresh_LogError("Could not create input layout! Error: %X", res); + SDL_stack_free(elementDescs); + return NULL; + } + + /* FIXME: + * These are not cached by the driver! Should we cache them, or allow duplicates? + * If we have one input layout per graphics pipeline maybe that wouldn't be so bad...? + */ + + SDL_stack_free(elementDescs); + return result; +} + +/* Pipeline Creation */ + +static Refresh_ComputePipeline* D3D11_CreateComputePipeline( + Refresh_Renderer *driverData, + Refresh_ComputeShaderInfo *computeShaderInfo +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11ShaderModule *shaderModule = (D3D11ShaderModule*) computeShaderInfo->shaderModule; + + D3D11ComputePipeline *pipeline = SDL_malloc(sizeof(D3D11ComputePipeline)); + pipeline->numTextures = computeShaderInfo->imageBindingCount; + pipeline->numBuffers = computeShaderInfo->bufferBindingCount; + pipeline->computeShader = (ID3D11ComputeShader*) shaderModule->shader; + pipeline->computeUniformBlockSize = D3D11_INTERNAL_NextHighestAlignment( + computeShaderInfo->uniformBufferSize, + 256 + ); + + return (Refresh_ComputePipeline*) pipeline; +} + +static Refresh_GraphicsPipeline* D3D11_CreateGraphicsPipeline( + Refresh_Renderer *driverData, + Refresh_GraphicsPipelineCreateInfo *pipelineCreateInfo +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11ShaderModule *vertShaderModule = (D3D11ShaderModule*) pipelineCreateInfo->vertexShaderInfo.shaderModule; + D3D11ShaderModule *fragShaderModule = (D3D11ShaderModule*) pipelineCreateInfo->fragmentShaderInfo.shaderModule; + + D3D11GraphicsPipeline *pipeline = SDL_malloc(sizeof(D3D11GraphicsPipeline)); + + /* Blend */ + + pipeline->colorAttachmentBlendState = D3D11_INTERNAL_FetchBlendState( + renderer, + pipelineCreateInfo->attachmentInfo.colorAttachmentCount, + pipelineCreateInfo->attachmentInfo.colorAttachmentDescriptions + ); + + pipeline->numColorAttachments = pipelineCreateInfo->attachmentInfo.colorAttachmentCount; + for (int32_t 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 + ); + + /* Vertex Shader */ + + pipeline->vertexShader = (ID3D11VertexShader*) vertShaderModule->shader; + pipeline->numVertexSamplers = pipelineCreateInfo->vertexShaderInfo.samplerBindingCount; + pipeline->vertexUniformBlockSize = D3D11_INTERNAL_NextHighestAlignment( + (uint32_t) pipelineCreateInfo->vertexShaderInfo.uniformBufferSize, + 256 + ); + + /* Input Layout */ + + pipeline->inputLayout = D3D11_INTERNAL_FetchInputLayout( + renderer, + pipelineCreateInfo->vertexInputState, + ID3D10Blob_GetBufferPointer(vertShaderModule->blob), + ID3D10Blob_GetBufferSize(vertShaderModule->blob) + ); + + if (pipelineCreateInfo->vertexInputState.vertexBindingCount > 0) + { + pipeline->vertexStrides = SDL_malloc( + sizeof(uint32_t) * + pipelineCreateInfo->vertexInputState.vertexBindingCount + ); + + for (uint32_t i = 0; i < pipelineCreateInfo->vertexInputState.vertexBindingCount; i += 1) + { + pipeline->vertexStrides[i] = pipelineCreateInfo->vertexInputState.vertexBindings[i].stride; + } + } + else + { + pipeline->vertexStrides = NULL; + } + + /* Fragment Shader */ + + pipeline->fragmentShader = (ID3D11PixelShader*) fragShaderModule->shader; + pipeline->numFragmentSamplers = pipelineCreateInfo->fragmentShaderInfo.samplerBindingCount; + pipeline->fragmentUniformBlockSize = D3D11_INTERNAL_NextHighestAlignment( + (uint32_t) pipelineCreateInfo->fragmentShaderInfo.uniformBufferSize, + 256 + ); + + return (Refresh_GraphicsPipeline*) pipeline; +} + +/* Resource Creation */ + +static Refresh_Sampler* D3D11_CreateSampler( + Refresh_Renderer *driverData, + Refresh_SamplerStateCreateInfo *samplerStateCreateInfo +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11_SAMPLER_DESC samplerDesc; + ID3D11SamplerState *samplerStateHandle; + D3D11Sampler *d3d11Sampler; + HRESULT res; + + samplerDesc.AddressU = RefreshToD3D11_SamplerAddressMode[samplerStateCreateInfo->addressModeU]; + samplerDesc.AddressV = RefreshToD3D11_SamplerAddressMode[samplerStateCreateInfo->addressModeV]; + samplerDesc.AddressW = RefreshToD3D11_SamplerAddressMode[samplerStateCreateInfo->addressModeW]; + + RefreshToD3D11_BorderColor( + samplerStateCreateInfo, + &samplerDesc + ); + + samplerDesc.ComparisonFunc = ( + samplerStateCreateInfo->compareEnable ? + RefreshToD3D11_CompareOp[samplerStateCreateInfo->compareOp] : + RefreshToD3D11_CompareOp[REFRESH_COMPAREOP_ALWAYS] + ); + samplerDesc.MaxAnisotropy = ( + samplerStateCreateInfo->anisotropyEnable ? + (UINT) samplerStateCreateInfo->maxAnisotropy : + 0 + ); + samplerDesc.Filter = RefreshToD3D11_Filter(samplerStateCreateInfo); + samplerDesc.MaxLOD = samplerStateCreateInfo->maxLod; + samplerDesc.MinLOD = samplerStateCreateInfo->minLod; + samplerDesc.MipLODBias = samplerStateCreateInfo->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; +} + +static Refresh_ShaderModule* D3D11_CreateShaderModule( + Refresh_Renderer *driverData, + Refresh_Driver_ShaderModuleCreateInfo *shaderModuleCreateInfo +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11ShaderModule* shaderModule; + Refresh_Driver_ShaderType shaderType = shaderModuleCreateInfo->type; + const char *profileNames[] = { "vs_5_0", "ps_5_0", "cs_5_0" }; + ID3D10Blob *blob, *errorBlob; + ID3D11DeviceChild *shader = NULL; + HRESULT res; + + /* Compile HLSL to DXBC */ + res = renderer->D3DCompileFunc( + shaderModuleCreateInfo->byteCode, + shaderModuleCreateInfo->codeSize, + NULL, + NULL, + NULL, + "main", /* API FIXME: Intentionally ignoring entryPointName because it MUST be "main" anyway */ + profileNames[shaderType], + 0, + 0, + &blob, + &errorBlob + ); + if (FAILED(res)) + { + Refresh_LogError( + "D3DCompile Error (%s): %s", + profileNames[shaderType], + ID3D10Blob_GetBufferPointer(errorBlob) + ); + ID3D10Blob_Release(errorBlob); + return NULL; + } + + /* Actually create the shader */ + if (shaderType == REFRESH_DRIVER_SHADERTYPE_VERTEX) + { + res = ID3D11Device_CreateVertexShader( + renderer->device, + ID3D10Blob_GetBufferPointer(blob), + ID3D10Blob_GetBufferSize(blob), + NULL, + (ID3D11VertexShader**) &shader + ); + if (FAILED(res)) + { + D3D11_INTERNAL_LogError(renderer->device, "Could not compile vertex shader", res); + ID3D10Blob_Release(blob); + return NULL; + } + } + else if (shaderType == REFRESH_DRIVER_SHADERTYPE_FRAGMENT) + { + res = ID3D11Device_CreatePixelShader( + renderer->device, + ID3D10Blob_GetBufferPointer(blob), + ID3D10Blob_GetBufferSize(blob), + NULL, + (ID3D11PixelShader**) &shader + ); + if (FAILED(res)) + { + D3D11_INTERNAL_LogError(renderer->device, "Could not compile pixel shader", res); + ID3D10Blob_Release(blob); + return NULL; + } + } + else if (shaderType == REFRESH_DRIVER_SHADERTYPE_COMPUTE) + { + res = ID3D11Device_CreateComputeShader( + renderer->device, + ID3D10Blob_GetBufferPointer(blob), + ID3D10Blob_GetBufferSize(blob), + NULL, + (ID3D11ComputeShader**) &shader + ); + if (FAILED(res)) + { + D3D11_INTERNAL_LogError(renderer->device, "Could not compile compute shader", res); + ID3D10Blob_Release(blob); + return NULL; + } + } + + /* Allocate and set up the shader module */ + shaderModule = (D3D11ShaderModule*) SDL_malloc(sizeof(D3D11ShaderModule)); + shaderModule->shader = shader; + shaderModule->blob = blob; + + return (Refresh_ShaderModule*) shaderModule; +} + +static Refresh_Texture* D3D11_CreateTexture( + Refresh_Renderer *driverData, + Refresh_TextureCreateInfo *textureCreateInfo +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + uint8_t isColorTarget, isDepthStencil, isSampler, isCompute, isMultisample; + 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; + isCompute = textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_COMPUTE_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) + { + desc2D.BindFlags |= D3D11_BIND_SHADER_RESOURCE; + } + if (isCompute) + { + 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) + { + desc3D.BindFlags |= D3D11_BIND_SHADER_RESOURCE; + } + if (isCompute) + { + 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->subresources = SDL_malloc( + d3d11Texture->levelCount * d3d11Texture->layerCount * sizeof(D3D11TextureSubresource) + ); + + for (uint32_t layerIndex = 0; layerIndex < d3d11Texture->layerCount; layerIndex += 1) + { + for (uint32_t levelIndex = 0; levelIndex < d3d11Texture->levelCount; levelIndex += 1) + { + uint32_t subresourceIndex = D3D11_INTERNAL_CalcSubresource( + levelIndex, + layerIndex, + d3d11Texture->levelCount + ); + + d3d11Texture->subresources[subresourceIndex].colorTargetView = NULL; + d3d11Texture->subresources[subresourceIndex].depthStencilTargetView = NULL; + d3d11Texture->subresources[subresourceIndex].uav = NULL; + d3d11Texture->subresources[subresourceIndex].msaaHandle = NULL; + d3d11Texture->subresources[subresourceIndex].msaaTargetView = NULL; + d3d11Texture->subresources[subresourceIndex].layer = layerIndex; + d3d11Texture->subresources[subresourceIndex].level = levelIndex; + + 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 (isCompute) + { + 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 (Refresh_Texture*) d3d11Texture; +} + +static Refresh_GpuBuffer* D3D11_CreateGpuBuffer( + Refresh_Renderer *driverData, + Refresh_BufferUsageFlags usageFlags, + uint32_t sizeInBytes +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11_BUFFER_DESC bufferDesc; + ID3D11Buffer *bufferHandle; + ID3D11UnorderedAccessView *uav = NULL; + D3D11Buffer *d3d11Buffer; + HRESULT res; + + 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_COMPUTE_BIT) || (usageFlags & REFRESH_BUFFERUSAGE_INDIRECT_BIT)) + { + bufferDesc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; + } + + 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_COMPUTE_BIT) + { + bufferDesc.MiscFlags |= D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; + } + + res = ID3D11Device_CreateBuffer( + renderer->device, + &bufferDesc, + NULL, + &bufferHandle + ); + ERROR_CHECK_RETURN("Could not create buffer", NULL); + + /* Create a UAV for the buffer */ + if (usageFlags & REFRESH_BUFFERUSAGE_COMPUTE_BIT) + { + D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc; + uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; + uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; + uavDesc.Buffer.FirstElement = 0; + uavDesc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW; + uavDesc.Buffer.NumElements = sizeInBytes / sizeof(uint32_t); + + 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); + } + } + + d3d11Buffer = SDL_malloc(sizeof(D3D11Buffer)); + d3d11Buffer->handle = bufferHandle; + d3d11Buffer->size = sizeInBytes; + d3d11Buffer->uav = uav; + + return (Refresh_GpuBuffer*) d3d11Buffer; +} + +static void D3D11_INTERNAL_TrackTransferBuffer( + D3D11CommandBuffer *commandBuffer, + D3D11TransferBuffer *buffer +) { + for (uint32_t i = 0; i < commandBuffer->usedTransferBufferCount; i += 1) + { + if (commandBuffer->usedTransferBuffers[i] == buffer) + { + return; + } + } + + EXPAND_ARRAY_IF_NEEDED( + commandBuffer->usedTransferBuffers, + D3D11TransferBuffer*, + commandBuffer->usedTransferBufferCount + 1, + commandBuffer->usedTransferBufferCapacity, + commandBuffer->boundUniformBufferCapacity * 2 + ); + + SDL_AtomicIncRef(&buffer->referenceCount); + + commandBuffer->usedTransferBuffers[ + commandBuffer->usedTransferBufferCount + ] = buffer; + commandBuffer->usedTransferBufferCount += 1; +} + +static void D3D11_INTERNAL_InitTransferBuffer( + D3D11Renderer *renderer, + uint32_t sizeInBytes, + D3D11TransferBuffer *pTransferBuffer +) { + pTransferBuffer->data = (uint8_t*) SDL_malloc(sizeInBytes); + pTransferBuffer->size = sizeInBytes; + SDL_AtomicSet(&pTransferBuffer->referenceCount, 0); +} + +/* This actually returns a container handle so we can rotate buffers on Discard. */ +static Refresh_TransferBuffer* D3D11_CreateTransferBuffer( + Refresh_Renderer *driverData, + uint32_t sizeInBytes +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) SDL_malloc(sizeof(D3D11TransferBufferContainer)); + + container->bufferCapacity = 1; + container->bufferCount = 1; + container->buffers = SDL_malloc( + container->bufferCapacity * sizeof(D3D11TransferBuffer) + ); + D3D11_INTERNAL_InitTransferBuffer( + renderer, + sizeInBytes, + &container->buffers[0] + ); + + container->activeBuffer = &container->buffers[0]; + + return (Refresh_TransferBuffer*) container; +} + +/* TransferBuffer Data */ + +static void D3D11_INTERNAL_DiscardActiveTransferBuffer( + D3D11Renderer *renderer, + D3D11TransferBufferContainer *container +) { + for (uint32_t 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 + ); + + D3D11_INTERNAL_InitTransferBuffer( + renderer, + container->activeBuffer->size, + &container->buffers[container->bufferCount] + ); + container->bufferCount += 1; + + container->activeBuffer = &container->buffers[container->bufferCount - 1]; +} + +static void D3D11_SetTransferData( + Refresh_Renderer *driverData, + void* data, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams, + Refresh_TransferOptions transferOption +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer; + D3D11TransferBuffer *buffer = container->activeBuffer; + + /* Rotate the transfer buffer if necessary */ + if ( + transferOption == REFRESH_TRANSFEROPTIONS_SAFEDISCARD && + SDL_AtomicGet(&container->activeBuffer->referenceCount) > 0 + ) { + D3D11_INTERNAL_DiscardActiveTransferBuffer( + renderer, + container + ); + buffer = container->activeBuffer; + } + + SDL_memcpy( + (uint8_t*) buffer->data + copyParams->dstOffset, + ((uint8_t*) 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; + + SDL_memcpy( + ((uint8_t*) data) + copyParams->dstOffset, + (uint8_t*) buffer->data + copyParams->srcOffset, + copyParams->size + ); +} + +/* Copy Pass */ + +static void D3D11_BeginCopyPass( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer +) { + /* no-op */ +} + +static void D3D11_UploadToTexture( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_TextureRegion *textureRegion, + Refresh_BufferImageCopy *copyParams, + Refresh_WriteOptions writeOption +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer; + D3D11TransferBuffer *d3d11TransferBuffer = container->activeBuffer; + D3D11Texture *d3d11Texture = (D3D11Texture*) textureRegion->textureSlice.texture; + uint32_t bufferStride = copyParams->bufferStride; + uint32_t bufferImageHeight = copyParams->bufferImageHeight; + int32_t w = textureRegion->w; + int32_t h = textureRegion->h; + + int32_t blockSize = Texture_GetBlockSize(d3d11Texture->format); + if (blockSize > 1) + { + w = (w + blockSize - 1) & ~(blockSize - 1); + h = (h + blockSize - 1) & ~(blockSize - 1); + } + + if (bufferStride == 0) + { + bufferStride = BytesPerRow(w, d3d11Texture->format); + } + if (bufferImageHeight == 0) + { + bufferImageHeight = h; + } + + D3D11_BOX dstBox; + dstBox.left = textureRegion->x; + dstBox.top = textureRegion->y; + dstBox.front = textureRegion->z; + dstBox.right = textureRegion->x + w; + dstBox.bottom = textureRegion->y + h; + dstBox.back = textureRegion->z + 1; + + ID3D11DeviceContext1_UpdateSubresource1( + d3d11CommandBuffer->context, + d3d11Texture->handle, + D3D11_INTERNAL_CalcSubresource( + textureRegion->textureSlice.mipLevel, + textureRegion->textureSlice.layer, + 1 + ), + &dstBox, + (uint8_t*) d3d11TransferBuffer->data + copyParams->bufferOffset, + bufferStride, + bufferStride * bufferImageHeight, + writeOption == REFRESH_WRITEOPTIONS_SAFEDISCARD ? D3D11_COPY_DISCARD : 0 + ); + + D3D11_INTERNAL_TrackTransferBuffer(d3d11CommandBuffer, d3d11TransferBuffer); +} + +static void D3D11_UploadToBuffer( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_GpuBuffer *gpuBuffer, + Refresh_BufferCopy *copyParams, + Refresh_WriteOptions writeOption +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer; + D3D11TransferBuffer *d3d11TransferBuffer = container->activeBuffer; + D3D11Buffer *d3d11Buffer = (D3D11Buffer*) gpuBuffer; + D3D11_BOX dstBox = { copyParams->dstOffset, 0, 0, copyParams->dstOffset + copyParams->size, 1, 1 }; + + ID3D11DeviceContext1_UpdateSubresource1( + d3d11CommandBuffer->context, + (ID3D11Resource*) d3d11Buffer->handle, + 0, + &dstBox, + d3d11TransferBuffer->data + copyParams->srcOffset, + 0, + 0, + writeOption == REFRESH_WRITEOPTIONS_SAFEDISCARD ? D3D11_COPY_DISCARD : 0 + ); + + D3D11_INTERNAL_TrackTransferBuffer(d3d11CommandBuffer, d3d11TransferBuffer); +} + +static void D3D11_DownloadFromTexture( + Refresh_Renderer *driverData, + Refresh_TextureRegion *textureRegion, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferImageCopy *copyParams, + Refresh_TransferOptions transferOption +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer; + D3D11TransferBuffer *d3d11TransferBuffer = container->activeBuffer; + D3D11Texture *d3d11Texture = (D3D11Texture*) textureRegion->textureSlice.texture; + D3D11_TEXTURE2D_DESC stagingDesc; + ID3D11Resource *stagingTexture; + uint32_t subresourceIndex = D3D11_INTERNAL_CalcSubresource( + textureRegion->textureSlice.mipLevel, + textureRegion->textureSlice.layer, + d3d11Texture->levelCount + ); + int32_t formatSize = Texture_GetFormatSize(d3d11Texture->format); + uint32_t bufferStride = copyParams->bufferStride == 0 ? (textureRegion->w * formatSize) : copyParams->bufferStride; + uint32_t bufferImageHeight = copyParams->bufferImageHeight == 0 ? (textureRegion->h * formatSize) : copyParams->bufferImageHeight; + D3D11_BOX srcBox = {textureRegion->x, textureRegion->y, textureRegion->z, textureRegion->x + textureRegion->w, textureRegion->y + textureRegion->h, 1}; + D3D11_MAPPED_SUBRESOURCE subresource; + HRESULT res; + + /* Rotate the transfer buffer if necessary */ + if ( + transferOption == REFRESH_TRANSFEROPTIONS_SAFEDISCARD && + SDL_AtomicGet(&container->activeBuffer->referenceCount) > 0 + ) { + D3D11_INTERNAL_DiscardActiveTransferBuffer( + renderer, + container + ); + d3d11TransferBuffer = container->activeBuffer; + } + + stagingDesc.Width = textureRegion->w; + stagingDesc.Height = textureRegion->h; + stagingDesc.MipLevels = 1; + stagingDesc.ArraySize = 1; + stagingDesc.Format = RefreshToD3D11_TextureFormat[d3d11Texture->format]; + stagingDesc.SampleDesc.Count = 1; + stagingDesc.SampleDesc.Quality = 0; + stagingDesc.Usage = D3D11_USAGE_STAGING; + stagingDesc.BindFlags = 0; + stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + stagingDesc.MiscFlags = 0; + + res = ID3D11Device_CreateTexture2D( + renderer->device, + &stagingDesc, + NULL, + (ID3D11Texture2D**) &stagingTexture + ); + ERROR_CHECK_RETURN("Staging texture creation failed",) + + /* Readback is only possible on CPU timeline in D3D11 */ + SDL_LockMutex(renderer->contextLock); + ID3D11DeviceContext_CopySubresourceRegion( + renderer->immediateContext, + stagingTexture, + 0, + 0, + 0, + 0, + d3d11Texture->handle, + subresourceIndex, + &srcBox + ); + + /* Read from the staging texture */ + res = ID3D11DeviceContext_Map( + renderer->immediateContext, + stagingTexture, + 0, + D3D11_MAP_READ, + 0, + &subresource + ); + ERROR_CHECK_RETURN("Could not map texture for reading",) + + uint8_t* dataPtr = (uint8_t*) d3d11TransferBuffer->data + copyParams->bufferOffset; + + /* TODO: figure out 3D copy */ + for (uint32_t row = textureRegion->y; row < textureRegion->h; row += 1) + { + SDL_memcpy( + dataPtr, + (uint8_t*) subresource.pData + (row * subresource.RowPitch) + (textureRegion->x * formatSize), + bufferStride + ); + dataPtr += bufferStride; + } + + ID3D11DeviceContext1_Unmap( + renderer->immediateContext, + stagingTexture, + 0 + ); + SDL_UnlockMutex(renderer->contextLock); + + /* Clean up the staging texture */ + ID3D11Texture2D_Release(stagingTexture); +} + +static void D3D11_DownloadFromBuffer( + Refresh_Renderer *driverData, + Refresh_GpuBuffer *gpuBuffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams, + Refresh_TransferOptions transferOption +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer; + D3D11TransferBuffer *d3d11TransferBuffer = container->activeBuffer; + D3D11Buffer *d3d11Buffer = (D3D11Buffer*) gpuBuffer; + D3D11_BOX srcBox = { copyParams->srcOffset, 0, 0, copyParams->size, 1, 1 }; + ID3D11Resource *stagingBuffer; + D3D11_MAPPED_SUBRESOURCE mappedSubresource; + D3D11_BUFFER_DESC stagingBufferDesc; + HRESULT res; + + /* Rotate the transfer buffer if necessary */ + if ( + transferOption == REFRESH_TRANSFEROPTIONS_SAFEDISCARD && + SDL_AtomicGet(&container->activeBuffer->referenceCount) > 0 + ) { + D3D11_INTERNAL_DiscardActiveTransferBuffer( + renderer, + container + ); + d3d11TransferBuffer = container->activeBuffer; + } + + /* Create staging buffer */ + stagingBufferDesc.ByteWidth = copyParams->size; + stagingBufferDesc.Usage = D3D11_USAGE_STAGING; + stagingBufferDesc.BindFlags = 0; + stagingBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + stagingBufferDesc.MiscFlags = 0; + stagingBufferDesc.StructureByteStride = 0; + + res = ID3D11Device_CreateBuffer( + renderer->device, + &stagingBufferDesc, + NULL, + (ID3D11Buffer**) &stagingBuffer + ); + ERROR_CHECK_RETURN("Could not create staging buffer for readback", ); + + /* Readback is only possible on CPU timeline in D3D11 */ + SDL_LockMutex(renderer->contextLock); + ID3D11DeviceContext_CopySubresourceRegion( + renderer->immediateContext, + (ID3D11Resource*) stagingBuffer, + 0, + 0, + 0, + 0, + (ID3D11Resource*) d3d11Buffer->handle, + 0, + &srcBox + ); + + /* Read from the staging buffer */ + res = ID3D11DeviceContext_Map( + renderer->immediateContext, + stagingBuffer, + 0, + D3D11_MAP_READ, + 0, + &mappedSubresource + ); + if (FAILED(res)) + { + D3D11_INTERNAL_LogError( + renderer->device, + "Failed to map staging buffer for read!", + res + ); + ID3D11Buffer_Release(stagingBuffer); + return; + } + + SDL_memcpy( + d3d11TransferBuffer->data + copyParams->dstOffset, + mappedSubresource.pData, + copyParams->size + ); + + ID3D11DeviceContext1_Unmap( + renderer->immediateContext, + stagingBuffer, + 0 + ); + SDL_UnlockMutex(renderer->contextLock); + + /* Clean up the staging buffer */ + ID3D11Buffer_Release(stagingBuffer); +} + +static void D3D11_CopyTextureToTexture( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_TextureRegion *source, + Refresh_TextureRegion *destination, + Refresh_WriteOptions writeOption +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Texture *srcTexture = (D3D11Texture*) source->textureSlice.texture; + uint32_t srcSubresourceIndex = D3D11_INTERNAL_CalcSubresource( + source->textureSlice.mipLevel, + source->textureSlice.layer, + srcTexture->levelCount + ); + D3D11Texture *dstTexture = (D3D11Texture*) destination->textureSlice.texture; + uint32_t dstSubresourceIndex = D3D11_INTERNAL_CalcSubresource( + destination->textureSlice.mipLevel, + destination->textureSlice.layer, + dstTexture->levelCount + ); + D3D11_BOX srcBox = { source->x, source->y, source->z, source->x + source->w, source->y + source->h, 1 }; + + ID3D11DeviceContext1_CopySubresourceRegion1( + d3d11CommandBuffer->context, + dstTexture->handle, + dstSubresourceIndex, + destination->x, + destination->y, + destination->z, + srcTexture->handle, + srcSubresourceIndex, + &srcBox, + writeOption == REFRESH_WRITEOPTIONS_SAFEDISCARD ? D3D11_COPY_DISCARD : 0 + ); +} + +static void D3D11_CopyBufferToBuffer( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_GpuBuffer *source, + Refresh_GpuBuffer *destination, + Refresh_BufferCopy *copyParams, + Refresh_WriteOptions writeOption +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Buffer *srcBuffer = (D3D11Buffer*) source; + D3D11Buffer *dstBuffer = (D3D11Buffer*) destination; + D3D11_BOX srcBox = { copyParams->srcOffset, 0, 0, copyParams->srcOffset + copyParams->size, 1, 1 }; + + ID3D11DeviceContext1_CopySubresourceRegion1( + d3d11CommandBuffer->context, + (ID3D11Resource*) dstBuffer->handle, + 0, + copyParams->dstOffset, + 0, + 0, + (ID3D11Resource*) srcBuffer->handle, + 0, + &srcBox, + writeOption == REFRESH_WRITEOPTIONS_SAFEDISCARD ? D3D11_COPY_DISCARD : 0 + ); +} + +static void D3D11_GenerateMipmaps( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_Texture *texture +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Texture *d3d11Texture = (D3D11Texture*) texture; + + ID3D11DeviceContext1_GenerateMips( + d3d11CommandBuffer->context, + d3d11Texture->shaderView + ); +} + +static void D3D11_EndCopyPass( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer +) { + /* no-op */ +} + +/* Uniforms */ + +static uint8_t D3D11_INTERNAL_CreateUniformBuffer( + D3D11Renderer *renderer +) { + D3D11_BUFFER_DESC bufferDesc; + ID3D11Buffer *bufferHandle; + D3D11UniformBuffer *uniformBuffer; + HRESULT res; + + bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + bufferDesc.ByteWidth = UBO_BUFFER_SIZE; + bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + bufferDesc.MiscFlags = 0; + bufferDesc.StructureByteStride = 0; + bufferDesc.Usage = D3D11_USAGE_DYNAMIC; + + res = ID3D11Device_CreateBuffer( + renderer->device, + &bufferDesc, + NULL, + &bufferHandle + ); + ERROR_CHECK_RETURN("Failed to create uniform buffer", 0); + + uniformBuffer = SDL_malloc(sizeof(D3D11UniformBuffer)); + uniformBuffer->offset = 0; + uniformBuffer->drawOffset = 0; + uniformBuffer->d3d11Buffer.handle = bufferHandle; + uniformBuffer->d3d11Buffer.size = UBO_BUFFER_SIZE; + uniformBuffer->d3d11Buffer.uav = NULL; + + /* Add it to the available pool */ + if (renderer->availableUniformBufferCount >= renderer->availableUniformBufferCapacity) + { + renderer->availableUniformBufferCapacity *= 2; + + renderer->availableUniformBuffers = SDL_realloc( + renderer->availableUniformBuffers, + sizeof(D3D11UniformBuffer*) * renderer->availableUniformBufferCapacity + ); + } + + renderer->availableUniformBuffers[renderer->availableUniformBufferCount] = uniformBuffer; + renderer->availableUniformBufferCount += 1; + + return 1; +} + +static uint8_t D3D11_INTERNAL_AcquireUniformBuffer( + D3D11Renderer *renderer, + D3D11CommandBuffer *commandBuffer, + D3D11UniformBuffer **uniformBufferToBind +) { + D3D11UniformBuffer *uniformBuffer; + + /* Acquire a uniform buffer from the pool */ + SDL_LockMutex(renderer->uniformBufferLock); + + if (renderer->availableUniformBufferCount == 0) + { + if (!D3D11_INTERNAL_CreateUniformBuffer(renderer)) + { + SDL_UnlockMutex(renderer->uniformBufferLock); + Refresh_LogError("Failed to create uniform buffer!"); + return 0; + } + } + + uniformBuffer = renderer->availableUniformBuffers[renderer->availableUniformBufferCount - 1]; + renderer->availableUniformBufferCount -= 1; + + SDL_UnlockMutex(renderer->uniformBufferLock); + + /* Reset the uniform buffer */ + uniformBuffer->offset = 0; + uniformBuffer->drawOffset = 0; + + /* Bind the uniform buffer to the command buffer */ + if (commandBuffer->boundUniformBufferCount >= commandBuffer->boundUniformBufferCapacity) + { + commandBuffer->boundUniformBufferCapacity *= 2; + commandBuffer->boundUniformBuffers = SDL_realloc( + commandBuffer->boundUniformBuffers, + sizeof(D3D11UniformBuffer*) * commandBuffer->boundUniformBufferCapacity + ); + } + commandBuffer->boundUniformBuffers[commandBuffer->boundUniformBufferCount] = uniformBuffer; + commandBuffer->boundUniformBufferCount += 1; + + *uniformBufferToBind = uniformBuffer; + + return 1; +} + +static void D3D11_INTERNAL_SetUniformBufferData( + D3D11Renderer *renderer, + D3D11CommandBuffer *commandBuffer, + D3D11UniformBuffer *uniformBuffer, + void* data, + uint32_t dataLength +) { + D3D11_MAPPED_SUBRESOURCE subres; + + HRESULT res = ID3D11DeviceContext_Map( + commandBuffer->context, + (ID3D11Resource*) uniformBuffer->d3d11Buffer.handle, + 0, + uniformBuffer->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_t*) subres.pData + uniformBuffer->offset, + data, + dataLength + ); + + ID3D11DeviceContext_Unmap( + commandBuffer->context, + (ID3D11Resource*) uniformBuffer->d3d11Buffer.handle, + 0 + ); +} + +static void D3D11_PushVertexShaderUniforms( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + void *data, + uint32_t dataLengthInBytes +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11GraphicsPipeline *graphicsPipeline = d3d11CommandBuffer->graphicsPipeline; + + if (d3d11CommandBuffer->vertexUniformBuffer->offset + graphicsPipeline->vertexUniformBlockSize >= UBO_BUFFER_SIZE) + { + /* Out of space! Get a new uniform buffer. */ + D3D11_INTERNAL_AcquireUniformBuffer( + renderer, + d3d11CommandBuffer, + &d3d11CommandBuffer->vertexUniformBuffer + ); + } + + d3d11CommandBuffer->vertexUniformBuffer->drawOffset = d3d11CommandBuffer->vertexUniformBuffer->offset; + + D3D11_INTERNAL_SetUniformBufferData( + renderer, + d3d11CommandBuffer, + d3d11CommandBuffer->vertexUniformBuffer, + data, + dataLengthInBytes + ); + + d3d11CommandBuffer->vertexUniformBuffer->offset += graphicsPipeline->vertexUniformBlockSize; +} + +static void D3D11_PushFragmentShaderUniforms( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + void *data, + uint32_t dataLengthInBytes +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11GraphicsPipeline *graphicsPipeline = d3d11CommandBuffer->graphicsPipeline; + + if (d3d11CommandBuffer->fragmentUniformBuffer->offset + graphicsPipeline->fragmentUniformBlockSize >= UBO_BUFFER_SIZE) + { + /* Out of space! Get a new uniform buffer. */ + D3D11_INTERNAL_AcquireUniformBuffer( + renderer, + d3d11CommandBuffer, + &d3d11CommandBuffer->fragmentUniformBuffer + ); + } + + d3d11CommandBuffer->fragmentUniformBuffer->drawOffset = d3d11CommandBuffer->fragmentUniformBuffer->offset; + + D3D11_INTERNAL_SetUniformBufferData( + renderer, + d3d11CommandBuffer, + d3d11CommandBuffer->fragmentUniformBuffer, + data, + dataLengthInBytes + ); + + d3d11CommandBuffer->fragmentUniformBuffer->offset += graphicsPipeline->fragmentUniformBlockSize; +} + +static void D3D11_PushComputeShaderUniforms( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + void *data, + uint32_t dataLengthInBytes +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11ComputePipeline *computePipeline = d3d11CommandBuffer->computePipeline; + + if (d3d11CommandBuffer->computeUniformBuffer->offset + computePipeline->computeUniformBlockSize >= UBO_BUFFER_SIZE) + { + /* Out of space! Get a new uniform buffer. */ + D3D11_INTERNAL_AcquireUniformBuffer( + renderer, + d3d11CommandBuffer, + &d3d11CommandBuffer->computeUniformBuffer + ); + } + + d3d11CommandBuffer->computeUniformBuffer->drawOffset = d3d11CommandBuffer->computeUniformBuffer->offset; + + D3D11_INTERNAL_SetUniformBufferData( + renderer, + d3d11CommandBuffer, + d3d11CommandBuffer->computeUniformBuffer, + data, + dataLengthInBytes + ); + + d3d11CommandBuffer->computeUniformBuffer->offset += + (uint32_t) computePipeline->computeUniformBlockSize; +} + +/* Samplers */ + +static void D3D11_BindVertexSamplers( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_TextureSamplerBinding *pBindings +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + ID3D11ShaderResourceView* srvs[MAX_VERTEXTEXTURE_SAMPLERS]; + ID3D11SamplerState* d3d11Samplers[MAX_VERTEXTEXTURE_SAMPLERS]; + + int32_t numVertexSamplers = d3d11CommandBuffer->graphicsPipeline->numVertexSamplers; + + for (int32_t i = 0; i < numVertexSamplers; i += 1) + { + srvs[i] = ((D3D11Texture*) pBindings[i].texture)->shaderView; + d3d11Samplers[i] = ((D3D11Sampler*) pBindings[i].sampler)->handle; + } + + ID3D11DeviceContext_VSSetShaderResources( + d3d11CommandBuffer->context, + 0, + numVertexSamplers, + srvs + ); + + ID3D11DeviceContext_VSSetSamplers( + d3d11CommandBuffer->context, + 0, + numVertexSamplers, + d3d11Samplers + ); +} + +static void D3D11_BindFragmentSamplers( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_TextureSamplerBinding *pBindings +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + ID3D11ShaderResourceView* srvs[MAX_TEXTURE_SAMPLERS]; + ID3D11SamplerState* d3d11Samplers[MAX_TEXTURE_SAMPLERS]; + + int32_t numFragmentSamplers = d3d11CommandBuffer->graphicsPipeline->numFragmentSamplers; + + for (int32_t i = 0; i < numFragmentSamplers; i += 1) + { + srvs[i] = ((D3D11Texture*) pBindings[i].texture)->shaderView; + d3d11Samplers[i] = ((D3D11Sampler*) pBindings[i].sampler)->handle; + } + + ID3D11DeviceContext_PSSetShaderResources( + d3d11CommandBuffer->context, + 0, + numFragmentSamplers, + srvs + ); + + ID3D11DeviceContext_PSSetSamplers( + d3d11CommandBuffer->context, + 0, + numFragmentSamplers, + d3d11Samplers + ); +} + +/* Disposal */ + +static void D3D11_QueueDestroyTexture( + Refresh_Renderer *driverData, + Refresh_Texture *texture +) { + D3D11Texture *d3d11Texture = (D3D11Texture*) texture; + + if (d3d11Texture->shaderView) + { + ID3D11ShaderResourceView_Release(d3d11Texture->shaderView); + } + + for (uint32_t layerIndex = 0; layerIndex < d3d11Texture->layerCount; layerIndex += 1) + { + for (uint32_t levelIndex = 0; levelIndex < d3d11Texture->levelCount; levelIndex += 1) + { + uint32_t subresourceIndex = D3D11_INTERNAL_CalcSubresource( + levelIndex, + layerIndex, + d3d11Texture->levelCount + ); + + 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].uav != NULL) + { + ID3D11UnorderedAccessView_Release(d3d11Texture->subresources[subresourceIndex].uav); + } + } + } + SDL_free(d3d11Texture->subresources); + + ID3D11Resource_Release(d3d11Texture->handle); + + SDL_free(d3d11Texture); +} + +static void D3D11_QueueDestroySampler( + Refresh_Renderer *driverData, + Refresh_Sampler *sampler +) { + D3D11Sampler *d3d11Sampler = (D3D11Sampler*) sampler; + ID3D11SamplerState_Release(d3d11Sampler->handle); + SDL_free(d3d11Sampler); +} + +static void D3D11_QueueDestroyGpuBuffer( + Refresh_Renderer *driverData, + Refresh_GpuBuffer *gpuBuffer +) { + D3D11Buffer *d3d11Buffer = (D3D11Buffer*) gpuBuffer; + + if (d3d11Buffer->uav) + { + ID3D11UnorderedAccessView_Release(d3d11Buffer->uav); + } + + ID3D11Buffer_Release(d3d11Buffer->handle); + + SDL_free(d3d11Buffer); +} + +static void D3D11_QueueDestroyTransferBuffer( + 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_t i = 0; i < transferBufferContainer->bufferCount; i += 1) + { + SDL_free(transferBufferContainer->buffers[i].data); + } + SDL_free(transferBufferContainer->buffers); +} + +static void D3D11_QueueDestroyShaderModule( + Refresh_Renderer *driverData, + Refresh_ShaderModule *shaderModule +) { + D3D11ShaderModule *d3dShaderModule = (D3D11ShaderModule*) shaderModule; + + if (d3dShaderModule->shader) + { + ID3D11DeviceChild_Release(d3dShaderModule->shader); + } + if (d3dShaderModule->blob) + { + ID3D10Blob_Release(d3dShaderModule->blob); + } + + SDL_free(d3dShaderModule); +} + +static void D3D11_QueueDestroyComputePipeline( + Refresh_Renderer *driverData, + Refresh_ComputePipeline *computePipeline +) { + D3D11ComputePipeline *d3d11ComputePipeline = (D3D11ComputePipeline*) computePipeline; + SDL_free(d3d11ComputePipeline); +} + +static void D3D11_QueueDestroyGraphicsPipeline( + Refresh_Renderer *driverData, + Refresh_GraphicsPipeline *graphicsPipeline +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + 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); + } + + SDL_free(d3d11GraphicsPipeline); +} + +/* Graphics State */ + +static void D3D11_INTERNAL_AllocateCommandBuffers( + D3D11Renderer *renderer, + uint32_t allocateCount +) { + D3D11CommandBuffer *commandBuffer; + HRESULT res; + + renderer->availableCommandBufferCapacity += allocateCount; + + renderer->availableCommandBuffers = SDL_realloc( + renderer->availableCommandBuffers, + sizeof(D3D11CommandBuffer*) * renderer->availableCommandBufferCapacity + ); + + for (uint32_t i = 0; i < allocateCount; i += 1) + { + commandBuffer = SDL_malloc(sizeof(D3D11CommandBuffer)); + + /* Deferred Device Context */ + res = ID3D11Device1_CreateDeferredContext1( + renderer->device, + 0, + &commandBuffer->context + ); + ERROR_CHECK("Could not create deferred context"); + + /* Bound Uniform Buffers */ + commandBuffer->boundUniformBufferCapacity = 16; + commandBuffer->boundUniformBufferCount = 0; + commandBuffer->boundUniformBuffers = SDL_malloc( + commandBuffer->boundUniformBufferCapacity * sizeof(D3D11UniformBuffer*) + ); + + /* Reference Counting */ + commandBuffer->usedTransferBufferCapacity = 4; + commandBuffer->usedTransferBufferCount = 0; + commandBuffer->usedTransferBuffers = SDL_malloc( + commandBuffer->usedTransferBufferCapacity * sizeof(D3D11TransferBuffer*) + ); + + 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_t 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; + + /* 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_t D3D11_INTERNAL_AcquireFence( + D3D11Renderer *renderer, + D3D11CommandBuffer *commandBuffer +) { + 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); + Refresh_LogError("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; + + return 1; +} + +static Refresh_CommandBuffer* D3D11_AcquireCommandBuffer( + Refresh_Renderer *driverData +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *commandBuffer; + + SDL_LockMutex(renderer->acquireCommandBufferLock); + + commandBuffer = D3D11_INTERNAL_GetInactiveCommandBufferFromPool(renderer); + commandBuffer->windowData = NULL; + commandBuffer->graphicsPipeline = NULL; + commandBuffer->computePipeline = NULL; + commandBuffer->vertexUniformBuffer = NULL; + commandBuffer->fragmentUniformBuffer = NULL; + commandBuffer->computeUniformBuffer = NULL; + for (uint32_t i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) + { + commandBuffer->colorTargetResolveTexture[i] = NULL; + commandBuffer->colorTargetResolveSubresourceIndex[i] = 0; + commandBuffer->colorTargetMsaaHandle[i] = NULL; + } + + D3D11_INTERNAL_AcquireFence(renderer, commandBuffer); + commandBuffer->autoReleaseFence = 1; + + SDL_UnlockMutex(renderer->acquireCommandBufferLock); + + return (Refresh_CommandBuffer*) commandBuffer; +} + +static void D3D11_BeginRenderPass( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_ColorAttachmentInfo *colorAttachmentInfos, + uint32_t colorAttachmentCount, + Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + ID3D11RenderTargetView* rtvs[MAX_COLOR_TARGET_BINDINGS]; + ID3D11DepthStencilView *dsv = NULL; + uint32_t vpWidth = UINT_MAX; + uint32_t vpHeight = UINT_MAX; + D3D11_VIEWPORT viewport; + D3D11_RECT scissorRect; + + /* Clear the bound targets for the current command buffer */ + for (uint32_t 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_t i = 0; i < colorAttachmentCount; i += 1) + { + D3D11Texture *texture = (D3D11Texture*) colorAttachmentInfos[i].textureSlice.texture; + + uint32_t subresourceIndex = D3D11_INTERNAL_CalcSubresource( + colorAttachmentInfos[i].textureSlice.mipLevel, + colorAttachmentInfos[i].textureSlice.layer, + texture->levelCount + ); + + if (texture->subresources[subresourceIndex].msaaHandle != NULL) + { + d3d11CommandBuffer->colorTargetResolveTexture[i] = texture; + d3d11CommandBuffer->colorTargetResolveSubresourceIndex[i] = subresourceIndex; + d3d11CommandBuffer->colorTargetMsaaHandle[i] = texture->subresources[subresourceIndex].msaaHandle; + + rtvs[i] = texture->subresources[subresourceIndex].msaaTargetView; + } + else + { + rtvs[i] = texture->subresources[subresourceIndex].colorTargetView; + } + } + + /* Get the DSV for the depth stencil attachment, if applicable */ + if (depthStencilAttachmentInfo != NULL) + { + D3D11Texture *texture = (D3D11Texture*) depthStencilAttachmentInfo->textureSlice.texture; + + uint32_t subresourceIndex = D3D11_INTERNAL_CalcSubresource( + depthStencilAttachmentInfo->textureSlice.mipLevel, + depthStencilAttachmentInfo->textureSlice.layer, + texture->levelCount + ); + + if (depthStencilAttachmentInfo->writeOption == REFRESH_WRITEOPTIONS_SAFEDISCARD) + { + ID3D11DeviceContext1_DiscardView( + d3d11CommandBuffer->context, + (ID3D11View*) texture->subresources[subresourceIndex].depthStencilTargetView + ); + } + + dsv = texture->subresources[subresourceIndex].depthStencilTargetView; + } + + /* Actually set the RTs */ + ID3D11DeviceContext_OMSetRenderTargets( + d3d11CommandBuffer->context, + colorAttachmentCount, + rtvs, + dsv + ); + + /* Perform load ops on the RTs */ + for (uint32_t i = 0; i < colorAttachmentCount; i += 1) + { + if (colorAttachmentInfos[i].loadOp == REFRESH_LOADOP_CLEAR) + { + float clearColors[] = + { + colorAttachmentInfos[i].clearColor.x, + colorAttachmentInfos[i].clearColor.y, + colorAttachmentInfos[i].clearColor.z, + colorAttachmentInfos[i].clearColor.w + }; + 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_t) depthStencilAttachmentInfo->depthStencilClearValue.stencil + ); + } + } + + /* The viewport cannot be larger than the smallest attachment. */ + for (uint32_t i = 0; i < colorAttachmentCount; i += 1) + { + D3D11Texture *texture = (D3D11Texture*) colorAttachmentInfos[i].textureSlice.texture; + uint32_t w = texture->width >> colorAttachmentInfos[i].textureSlice.mipLevel; + uint32_t h = texture->height >> colorAttachmentInfos[i].textureSlice.mipLevel; + + if (w < vpWidth) + { + vpWidth = w; + } + + if (h < vpHeight) + { + vpHeight = h; + } + } + + if (depthStencilAttachmentInfo != NULL) + { + D3D11Texture *texture = (D3D11Texture*) depthStencilAttachmentInfo->textureSlice.texture; + uint32_t w = texture->width >> depthStencilAttachmentInfo->textureSlice.mipLevel; + uint32_t 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_EndRenderPass( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + + d3d11CommandBuffer->vertexUniformBuffer = NULL; + d3d11CommandBuffer->fragmentUniformBuffer = NULL; + d3d11CommandBuffer->computeUniformBuffer = NULL; + + /* Resolve MSAA color render targets */ + for (uint32_t 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_BindGraphicsPipeline( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_GraphicsPipeline *graphicsPipeline +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11GraphicsPipeline *pipeline = (D3D11GraphicsPipeline*) graphicsPipeline; + + d3d11CommandBuffer->graphicsPipeline = pipeline; + + /* Get a vertex uniform buffer if we need one */ + if (d3d11CommandBuffer->vertexUniformBuffer == NULL && pipeline->vertexUniformBlockSize > 0) + { + D3D11_INTERNAL_AcquireUniformBuffer( + renderer, + d3d11CommandBuffer, + &d3d11CommandBuffer->vertexUniformBuffer + ); + } + + /* Get a fragment uniform buffer if we need one */ + if (d3d11CommandBuffer->fragmentUniformBuffer == NULL && pipeline->fragmentUniformBlockSize > 0) + { + D3D11_INTERNAL_AcquireUniformBuffer( + renderer, + d3d11CommandBuffer, + &d3d11CommandBuffer->fragmentUniformBuffer + ); + } + + 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 + ); +} + +static void D3D11_SetViewport( + Refresh_Renderer *driverData, + 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_Renderer *driverData, + 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_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + uint32_t firstBinding, + uint32_t bindingCount, + Refresh_BufferBinding *pBindings +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + ID3D11Buffer *bufferHandles[MAX_BUFFER_BINDINGS]; + UINT bufferOffsets[MAX_BUFFER_BINDINGS]; + + for (uint32_t i = 0; i < bindingCount; i += 1) + { + bufferHandles[i] = ((D3D11Buffer*) pBindings[i].gpuBuffer)->handle; + bufferOffsets[i] = pBindings[i].offset; + } + + ID3D11DeviceContext_IASetVertexBuffers( + d3d11CommandBuffer->context, + firstBinding, + bindingCount, + bufferHandles, + &d3d11CommandBuffer->graphicsPipeline->vertexStrides[firstBinding], + bufferOffsets + ); +} + +static void D3D11_BindIndexBuffer( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_BufferBinding *pBinding, + Refresh_IndexElementSize indexElementSize +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Buffer *d3d11Buffer = (D3D11Buffer*) pBinding->gpuBuffer; + + ID3D11DeviceContext_IASetIndexBuffer( + d3d11CommandBuffer->context, + d3d11Buffer->handle, + RefreshToD3D11_IndexType[indexElementSize], + (UINT) pBinding->offset + ); +} + +/* Compute State */ + +static void D3D11_BeginComputePass( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer +) { + /* no-op */ +} + +static void D3D11_BindComputePipeline( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_ComputePipeline *computePipeline +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11ComputePipeline *pipeline = (D3D11ComputePipeline*) computePipeline; + + d3d11CommandBuffer->computePipeline = pipeline; + + /* Get a compute uniform buffer if we need one */ + if (d3d11CommandBuffer->computeUniformBuffer == NULL && pipeline->computeUniformBlockSize > 0) + { + D3D11_INTERNAL_AcquireUniformBuffer( + renderer, + d3d11CommandBuffer, + &d3d11CommandBuffer->computeUniformBuffer + ); + } + + ID3D11DeviceContext_CSSetShader( + d3d11CommandBuffer->context, + pipeline->computeShader, + NULL, + 0 + ); +} + +/* D3D11 can't discard when setting a UAV, so just ignore writeOption */ +static void D3D11_BindComputeBuffers( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_ComputeBufferBinding *pBindings +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + ID3D11UnorderedAccessView* uavs[MAX_BUFFER_BINDINGS]; + + int32_t numBuffers = d3d11CommandBuffer->computePipeline->numBuffers; + + for (int32_t i = 0; i < numBuffers; i += 1) + { + uavs[i] = ((D3D11Buffer*) pBindings[i].gpuBuffer)->uav; + } + + ID3D11DeviceContext_CSSetUnorderedAccessViews( + d3d11CommandBuffer->context, + 0, + numBuffers, + uavs, + NULL + ); +} + +/* D3D11 can't discard when setting a UAV, so just ignore writeOption */ +static void D3D11_BindComputeTextures( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + Refresh_ComputeTextureBinding *pBindings +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + ID3D11UnorderedAccessView *uavs[MAX_TEXTURE_SAMPLERS]; + + int32_t numTextures = d3d11CommandBuffer->computePipeline->numTextures; + + for (int32_t i = 0; i < numTextures; i += 1) + { + D3D11Texture *texture = ((D3D11Texture*) pBindings[i].textureSlice.texture); + uint32_t subresourceIndex = D3D11_INTERNAL_CalcSubresource( + pBindings[i].textureSlice.mipLevel, + pBindings[i].textureSlice.layer, + texture->levelCount + ); + + uavs[i] = texture->subresources[subresourceIndex].uav; + } + + ID3D11DeviceContext_CSSetUnorderedAccessViews( + d3d11CommandBuffer->context, + 0, + numTextures, + uavs, + NULL + ); +} + +static void D3D11_EndComputePass( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer +) { + /* no-op */ +} + +/* Window and Swapchain Management */ + +static D3D11WindowData* D3D11_INTERNAL_FetchWindowData( + void *windowHandle +) { + return (D3D11WindowData*) SDL_GetWindowData(windowHandle, WINDOW_DATA); +} + +static uint8_t D3D11_INTERNAL_InitializeSwapchainTexture( + D3D11Renderer *renderer, + IDXGISwapChain *swapchain, + 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 = DXGI_FORMAT_R8G8B8A8_UNORM; + 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 = DXGI_FORMAT_R8G8B8A8_UNORM; + 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 = DXGI_FORMAT_R8G8B8A8_UNORM; + 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->subresources = SDL_malloc(sizeof(D3D11TextureSubresource)); + pTexture->subresources[0].colorTargetView = rtv; + 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; + + 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_t D3D11_INTERNAL_CreateSwapchain( + D3D11Renderer *renderer, + D3D11WindowData *windowData, + Refresh_PresentMode presentMode +) { + SDL_SysWMinfo info; + HWND dxgiHandle; + int width, height; + DXGI_SWAP_CHAIN_DESC swapchainDesc; + IDXGIFactory1 *pParent; + IDXGISwapChain *swapchain; + HRESULT res; + + /* Get the DXGI handle */ + SDL_VERSION(&info.version); + SDL_GetWindowWMInfo((SDL_Window*) windowData->windowHandle, &info); + dxgiHandle = info.info.win.window; + + /* Get the window size */ + SDL_GetWindowSize((SDL_Window*) windowData->windowHandle, &width, &height); + + /* Initialize the swapchain buffer descriptor */ + swapchainDesc.BufferDesc.Width = 0; + swapchainDesc.BufferDesc.Height = 0; + swapchainDesc.BufferDesc.RefreshRate.Numerator = 0; + swapchainDesc.BufferDesc.RefreshRate.Denominator = 0; + /* TODO: support different swapchain formats? */ + swapchainDesc.BufferDesc.Format = RefreshToD3D11_TextureFormat[REFRESH_TEXTUREFORMAT_R8G8B8A8]; + 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)) + { + Refresh_LogWarn( + "Could not get swapchain parent! Error Code: %08X", + res + ); + } + else + { + /* Disable DXGI window crap */ + res = IDXGIFactory1_MakeWindowAssociation( + pParent, + dxgiHandle, + DXGI_MWA_NO_WINDOW_CHANGES + ); + if (FAILED(res)) + { + Refresh_LogWarn( + "MakeWindowAssociation failed! Error Code: %08X", + res + ); + } + + /* We're done with the parent now */ + IDXGIFactory1_Release(pParent); + } + + /* Initialize the swapchain data */ + windowData->swapchain = swapchain; + windowData->presentMode = presentMode; + + if (!D3D11_INTERNAL_InitializeSwapchainTexture( + renderer, + swapchain, + &windowData->texture + )) { + IDXGISwapChain_Release(swapchain); + return 0; + } + + return 1; +} + +static uint8_t D3D11_INTERNAL_ResizeSwapchain( + D3D11Renderer *renderer, + D3D11WindowData *windowData, + int32_t width, + int32_t height +) { + /* Release the old views */ + ID3D11ShaderResourceView_Release(windowData->texture.shaderView); + ID3D11RenderTargetView_Release(windowData->texture.subresources[0].colorTargetView); + 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 Refresh-side texture for the swapchain */ + return D3D11_INTERNAL_InitializeSwapchainTexture( + renderer, + windowData->swapchain, + &windowData->texture + ); +} + +static uint8_t D3D11_ClaimWindow( + Refresh_Renderer *driverData, + void *windowHandle, + Refresh_PresentMode presentMode +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(windowHandle); + + if (windowData == NULL) + { + windowData = (D3D11WindowData*) SDL_malloc(sizeof(D3D11WindowData)); + windowData->windowHandle = windowHandle; + + if (D3D11_INTERNAL_CreateSwapchain(renderer, windowData, presentMode)) + { + SDL_SetWindowData((SDL_Window*) windowHandle, WINDOW_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 + { + Refresh_LogError("Could not create swapchain, failed to claim window!"); + SDL_free(windowData); + return 0; + } + } + else + { + Refresh_LogWarn("Window already claimed!"); + return 0; + } +} + +static void D3D11_UnclaimWindow( + Refresh_Renderer *driverData, + void *windowHandle +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(windowHandle); + + if (windowData == NULL) + { + return; + } + + D3D11_Wait(driverData); + + ID3D11ShaderResourceView_Release(windowData->texture.shaderView); + ID3D11RenderTargetView_Release(windowData->texture.subresources[0].colorTargetView); + ID3D11UnorderedAccessView_Release(windowData->texture.subresources[0].uav); + SDL_free(windowData->texture.subresources); + IDXGISwapChain_Release(windowData->swapchain); + + SDL_LockMutex(renderer->windowLock); + for (uint32_t i = 0; i < renderer->claimedWindowCount; i += 1) + { + if (renderer->claimedWindows[i]->windowHandle == windowHandle) + { + renderer->claimedWindows[i] = renderer->claimedWindows[renderer->claimedWindowCount - 1]; + renderer->claimedWindowCount -= 1; + break; + } + } + SDL_UnlockMutex(renderer->windowLock); + + SDL_free(windowData); + SDL_SetWindowData((SDL_Window*) windowHandle, WINDOW_DATA, NULL); +} + +static Refresh_Texture* D3D11_AcquireSwapchainTexture( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer, + void *windowHandle, + uint32_t *pWidth, + uint32_t *pHeight +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11WindowData *windowData; + DXGI_SWAP_CHAIN_DESC swapchainDesc; + int w, h; + HRESULT res; + + windowData = D3D11_INTERNAL_FetchWindowData(windowHandle); + if (windowData == NULL) + { + return NULL; + } + + /* Check for window size changes and resize the swapchain if needed. */ + IDXGISwapChain_GetDesc(windowData->swapchain, &swapchainDesc); + SDL_GetWindowSize((SDL_Window*) windowHandle, &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); + } + + /* 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); + + /* Let the command buffer know it's associated with this swapchain. */ + d3d11CommandBuffer->windowData = windowData; + + /* Send the dimensions to the out parameters. */ + *pWidth = windowData->texture.width; + *pHeight = windowData->texture.height; + + /* Return the swapchain texture */ + return (Refresh_Texture*) &windowData->texture; +} + +static Refresh_TextureFormat D3D11_GetSwapchainFormat( + Refresh_Renderer *driverData, + void *windowHandle +) { + return REFRESH_TEXTUREFORMAT_R8G8B8A8; +} + +static void D3D11_SetSwapchainPresentMode( + Refresh_Renderer *driverData, + void *windowHandle, + Refresh_PresentMode presentMode +) { + D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(windowHandle); + windowData->presentMode = presentMode; +} + +/* Submission and Fences */ + +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_INTERNAL_CleanCommandBuffer( + D3D11Renderer *renderer, + D3D11CommandBuffer *commandBuffer +) { + /* Bound uniform buffers are now available */ + SDL_LockMutex(renderer->uniformBufferLock); + for (uint32_t i = 0; i < commandBuffer->boundUniformBufferCount; i += 1) + { + if (renderer->availableUniformBufferCount == renderer->availableUniformBufferCapacity) + { + renderer->availableUniformBufferCapacity *= 2; + renderer->availableUniformBuffers = SDL_realloc( + renderer->availableUniformBuffers, + renderer->availableUniformBufferCapacity * sizeof(D3D11UniformBuffer*) + ); + } + + renderer->availableUniformBuffers[renderer->availableUniformBufferCount] = commandBuffer->boundUniformBuffers[i]; + renderer->availableUniformBufferCount += 1; + } + SDL_UnlockMutex(renderer->uniformBufferLock); + + commandBuffer->boundUniformBufferCount = 0; + + /* Reference Counting */ + + for (uint32_t i = 0; i < commandBuffer->usedTransferBufferCount; i += 1) + { + SDL_AtomicDecRef(&commandBuffer->usedTransferBuffers[i]->referenceCount); + } + commandBuffer->usedTransferBufferCount = 0; + + /* The fence is now available (unless SubmitAndAcquireFence was called) */ + if (commandBuffer->autoReleaseFence) + { + D3D11_INTERNAL_ReleaseFenceToPool(renderer, 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_t 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_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_INTERNAL_PerformPendingDestroys( + D3D11Renderer *renderer +) { + for (int32_t i = renderer->transferBufferContainersToDestroyCount - 1; i >= 0; i -= 1) + { + int32_t referenceCount = 0; + for (uint32_t 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; + } + } +} + +static void D3D11_Submit( + Refresh_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + 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 */ + if (d3d11CommandBuffer->windowData) + { + /* FIXME: Is there some way to emulate FIFO_RELAXED? */ + + uint32_t syncInterval = 1; + if ( d3d11CommandBuffer->windowData->presentMode == REFRESH_PRESENTMODE_IMMEDIATE || + (renderer->supportsFlipDiscard && d3d11CommandBuffer->windowData->presentMode == REFRESH_PRESENTMODE_MAILBOX) + ) { + syncInterval = 0; + } + + uint32_t presentFlags = 0; + if ( renderer->supportsTearing && + d3d11CommandBuffer->windowData->presentMode == REFRESH_PRESENTMODE_IMMEDIATE ) + { + presentFlags = DXGI_PRESENT_ALLOW_TEARING; + } + + IDXGISwapChain_Present( + d3d11CommandBuffer->windowData->swapchain, + syncInterval, + presentFlags + ); + + ID3D11Texture2D_Release(d3d11CommandBuffer->windowData->texture.handle); + } + + /* Check if we can perform any cleanups */ + for (int32_t 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_Renderer *driverData, + Refresh_CommandBuffer *commandBuffer +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Fence *fence = d3d11CommandBuffer->fence; + + d3d11CommandBuffer->autoReleaseFence = 0; + D3D11_Submit(driverData, 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_t 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 (int32_t 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); +} + +static void D3D11_WaitForFences( + Refresh_Renderer *driverData, + uint8_t waitAll, + uint32_t fenceCount, + Refresh_Fence **pFences +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11Fence *fence; + BOOL queryData; + HRESULT res = S_FALSE; + + if (waitAll) + { + for (uint32_t 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_t 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); + } +} + +static int 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; +} + +static void D3D11_ReleaseFence( + Refresh_Renderer *driverData, + Refresh_Fence *fence +) { + D3D11_INTERNAL_ReleaseFenceToPool( + (D3D11Renderer*) driverData, + (D3D11Fence*) fence + ); +} + +/* Device Creation */ + +static uint8_t D3D11_PrepareDriver( + uint32_t *flags +) { + void *d3d11_dll, *d3dcompiler_dll, *dxgi_dll; + PFN_D3D11_CREATE_DEVICE D3D11CreateDeviceFunc; + D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_1 }; + PFN_D3DCOMPILE D3DCompileFunc; + PFN_CREATE_DXGI_FACTORY1 CreateDXGIFactoryFunc; + HRESULT res; + + /* Can we load D3D11? */ + + d3d11_dll = SDL_LoadObject(D3D11_DLL); + if (d3d11_dll == NULL) + { + Refresh_LogWarn("D3D11: Could not find " D3D11_DLL); + return 0; + } + + D3D11CreateDeviceFunc = (PFN_D3D11_CREATE_DEVICE) SDL_LoadFunction( + d3d11_dll, + D3D11_CREATE_DEVICE_FUNC + ); + if (D3D11CreateDeviceFunc == NULL) + { + Refresh_LogWarn("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)) + { + Refresh_LogWarn("D3D11: Could not create D3D11Device with feature level 11_0"); + return 0; + } + + /* Can we load D3DCompiler? */ + + d3dcompiler_dll = SDL_LoadObject(D3DCOMPILER_DLL); + if (d3dcompiler_dll == NULL) + { + Refresh_LogWarn("D3D11: Could not find " D3DCOMPILER_DLL); + return 0; + } + + D3DCompileFunc = (PFN_D3DCOMPILE) SDL_LoadFunction( + d3dcompiler_dll, + D3DCOMPILE_FUNC + ); + SDL_UnloadObject(d3dcompiler_dll); /* We're not going to call this function, so we can just unload now. */ + if (D3DCompileFunc == NULL) + { + Refresh_LogWarn("D3D11: Could not find function " D3DCOMPILE_FUNC " in " D3DCOMPILER_DLL); + return 0; + } + + /* Can we load DXGI? */ + + dxgi_dll = SDL_LoadObject(DXGI_DLL); + if (dxgi_dll == NULL) + { + Refresh_LogWarn("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) + { + Refresh_LogWarn("D3D11: Could not find function " CREATE_DXGI_FACTORY1_FUNC " in " DXGI_DLL); + return 0; + } + + /* No window flags required */ + SDL_SetHint(SDL_HINT_VIDEO_EXTERNAL_CONTEXT, "1"); + + 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) + { + Refresh_LogWarn("Could not find " DXGIDEBUG_DLL); + return; + } + + DXGIGetDebugInterfaceFunc = SDL_LoadFunction( + renderer->dxgidebug_dll, + DXGI_GET_DEBUG_INTERFACE_FUNC + ); + if (DXGIGetDebugInterfaceFunc == NULL) + { + Refresh_LogWarn("Could not load function: " DXGI_GET_DEBUG_INTERFACE_FUNC); + return; + } + + res = DXGIGetDebugInterfaceFunc(&D3D_IID_IDXGIDebug, &renderer->dxgiDebug); + if (FAILED(res)) + { + Refresh_LogWarn("Could not get IDXGIDebug interface"); + } + + res = DXGIGetDebugInterfaceFunc(&D3D_IID_IDXGIInfoQueue, &renderer->dxgiInfoQueue); + if (FAILED(res)) + { + Refresh_LogWarn("Could not get IDXGIInfoQueue interface"); + } +} + +static Refresh_Device* D3D11_CreateDevice( + uint8_t 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_t 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 D3DCompiler library */ + renderer->d3dcompiler_dll = SDL_LoadObject(D3DCOMPILER_DLL); + if (renderer->d3dcompiler_dll == NULL) + { + Refresh_LogError("Could not find " D3DCOMPILER_DLL); + return NULL; + } + + /* Load the D3DCompile function pointer */ + renderer->D3DCompileFunc = (PFN_D3DCOMPILE) SDL_LoadFunction( + renderer->d3dcompiler_dll, + D3DCOMPILE_FUNC + ); + if (renderer->D3DCompileFunc == NULL) + { + Refresh_LogError("Could not load function: " D3DCOMPILE_FUNC); + return NULL; + } + + /* Load the DXGI library */ + renderer->dxgi_dll = SDL_LoadObject(DXGI_DLL); + if (renderer->dxgi_dll == NULL) + { + Refresh_LogError("Could not find " DXGI_DLL); + return NULL; + } + + /* Load the CreateDXGIFactory1 function */ + CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY1) SDL_LoadFunction( + renderer->dxgi_dll, + CREATE_DXGI_FACTORY1_FUNC + ); + if (CreateDXGIFactoryFunc == NULL) + { + Refresh_LogError("Could not load function: " CREATE_DXGI_FACTORY1_FUNC); + return NULL; + } + + /* Create the DXGI factory */ + res = CreateDXGIFactoryFunc( + &D3D_IID_IDXGIFactory1, + &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, + &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, + &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) + { + Refresh_LogError("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) + { + Refresh_LogError("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. */ + Refresh_LogWarn("Creating device in debug mode failed with error %08X. Trying non-debug.", res); + flags &= ~D3D11_CREATE_DEVICE_DEBUG; + debugMode = 0; + goto tryCreateDevice; + } + + ERROR_CHECK_RETURN("Could not create D3D11 device", NULL); + + /* The actual device we want is the ID3D11Device1 interface... */ + res = ID3D11Device_QueryInterface( + d3d11Device, + &D3D_IID_ID3D11Device1, + &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); + + /* 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 + ); + } + + /* Print driver info */ + Refresh_LogInfo("Refresh Driver: D3D11"); + Refresh_LogInfo("D3D11 Adapter: %S", adapterDesc.Description); + + /* Create mutexes */ + renderer->contextLock = SDL_CreateMutex(); + renderer->acquireCommandBufferLock = SDL_CreateMutex(); + renderer->uniformBufferLock = SDL_CreateMutex(); + renderer->fenceLock = SDL_CreateMutex(); + renderer->windowLock = SDL_CreateMutex(); + + /* Initialize miscellaneous renderer members */ + renderer->debugMode = (flags & D3D11_CREATE_DEVICE_DEBUG); + + /* Create command buffer pool */ + D3D11_INTERNAL_AllocateCommandBuffers(renderer, 2); + + /* Create uniform buffer pool */ + renderer->availableUniformBufferCapacity = 16; + renderer->availableUniformBuffers = SDL_malloc( + sizeof(D3D11UniformBuffer*) * renderer->availableUniformBufferCapacity + ); + + /* Create fence pool */ + renderer->availableFenceCapacity = 2; + renderer->availableFences = SDL_malloc( + sizeof(D3D11Fence*) * renderer->availableFenceCapacity + ); + + /* Create deferred transfer array */ + renderer->transferBufferContainersToDestroyCapacity = 2; + renderer->transferBufferContainersToDestroyCount = 0; + renderer->transferBufferContainersToDestroy = SDL_malloc( + renderer->transferBufferContainersToDestroyCapacity * sizeof(D3D11TransferBufferContainer*) + ); + + /* Create claimed window list */ + renderer->claimedWindowCapacity = 1; + renderer->claimedWindows = SDL_malloc( + sizeof(D3D11WindowData*) * renderer->claimedWindowCapacity + ); + + /* 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", + D3D11_PrepareDriver, + D3D11_CreateDevice +}; + +#endif //REFRESH_DRIVER_D3D11 diff --git a/src/Refresh_Driver_D3D11_cdefines.h b/src/Refresh_Driver_D3D11_cdefines.h new file mode 100644 index 0000000..3caa2e6 --- /dev/null +++ b/src/Refresh_Driver_D3D11_cdefines.h @@ -0,0 +1,42 @@ +/* Refresh - XNA-inspired 3D Graphics Library with modern capabilities + * + * Copyright (c) 2020 Evan Hemsley + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + * Evan "cosmonaut" Hemsley + * + */ + +/* Function Pointer Signatures */ +typedef HRESULT(WINAPI* PFN_CREATE_DXGI_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_ID3D11Texture2D = { 0x6f15aaf2,0xd208,0x4e89,{0x9a,0xb4,0x48,0x95,0x35,0xd3,0x4f,0x9c} }; +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} }; +static const IID D3D_IID_IDXGIInfoQueue = { 0xd67441c7,0x672a,0x476f,{0x9e,0x82,0xcd,0x55,0xb4,0x49,0x49,0xce} }; + +static const GUID D3D_IID_DXGI_DEBUG_ALL = { 0xe48ae283,0xda80,0x490b,{0x87,0xe6,0x43,0xe9,0xa9,0xcf,0xda,0x08} }; diff --git a/src/Refresh_Driver_Template.txt b/src/Refresh_Driver_Template.txt index a56ffb1..8052dfb 100644 --- a/src/Refresh_Driver_Template.txt +++ b/src/Refresh_Driver_Template.txt @@ -318,7 +318,7 @@ static Refresh_Sampler* TEMPLATE_CreateSampler( static Refresh_ShaderModule* TEMPLATE_CreateShaderModule( Refresh_Renderer *driverData, - Refresh_ShaderModuleCreateInfo *shaderModuleCreateInfo + Refresh_Driver_ShaderModuleCreateInfo *shaderModuleCreateInfo ) { NOT_IMPLEMENTED } diff --git a/src/Refresh_Driver_Vulkan.c b/src/Refresh_Driver_Vulkan.c index c4a34e5..743a4ee 100644 --- a/src/Refresh_Driver_Vulkan.c +++ b/src/Refresh_Driver_Vulkan.c @@ -6670,7 +6670,7 @@ static Refresh_Sampler* VULKAN_CreateSampler( static Refresh_ShaderModule* VULKAN_CreateShaderModule( Refresh_Renderer *driverData, - Refresh_ShaderModuleCreateInfo *shaderModuleCreateInfo + Refresh_Driver_ShaderModuleCreateInfo *shaderModuleCreateInfo ) { VulkanShaderModule *vulkanShaderModule = SDL_malloc(sizeof(VulkanShaderModule)); VkResult vulkanResult; @@ -8550,130 +8550,6 @@ static void VULKAN_UploadToBuffer( VULKAN_INTERNAL_TrackCopiedBuffer(renderer, vulkanCommandBuffer, gpuBufferContainer->activeBufferHandle->vulkanBuffer); } -static void VULKAN_DownloadFromTexture( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureRegion *textureRegion, - Refresh_TransferBuffer *transferBuffer, - Refresh_BufferImageCopy *copyParams, - Refresh_TransferOptions transferOption -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanTextureSlice *vulkanTextureSlice; - VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer*) transferBuffer; - VkBufferImageCopy imageCopy; - - vulkanTextureSlice = VULKAN_INTERNAL_RefreshToVulkanTextureSlice(&textureRegion->textureSlice); - - if ( - transferOption == REFRESH_TRANSFEROPTIONS_SAFEDISCARD && - SDL_AtomicGet(&transferBufferContainer->activeBufferHandle->vulkanBuffer->referenceCount) > 0 - ) { - VULKAN_INTERNAL_DiscardActiveBuffer( - renderer, - transferBufferContainer - ); - vulkanTextureSlice = VULKAN_INTERNAL_RefreshToVulkanTextureSlice(&textureRegion->textureSlice); - } - - VULKAN_INTERNAL_BufferMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_WRITE, - transferBufferContainer->activeBufferHandle->vulkanBuffer - ); - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_READ, - vulkanTextureSlice - ); - - imageCopy.imageExtent.width = textureRegion->w; - imageCopy.imageExtent.height = textureRegion->h; - imageCopy.imageExtent.depth = textureRegion->d; - imageCopy.imageOffset.x = textureRegion->x; - imageCopy.imageOffset.y = textureRegion->y; - imageCopy.imageOffset.z = textureRegion->z; - imageCopy.imageSubresource.aspectMask = vulkanTextureSlice->parent->aspectFlags; - imageCopy.imageSubresource.baseArrayLayer = textureRegion->textureSlice.layer; - imageCopy.imageSubresource.layerCount = 1; - imageCopy.imageSubresource.mipLevel = textureRegion->textureSlice.mipLevel; - imageCopy.bufferOffset = copyParams->bufferOffset; - imageCopy.bufferRowLength = copyParams->bufferStride; - imageCopy.bufferImageHeight = copyParams->bufferImageHeight; - - renderer->vkCmdCopyImageToBuffer( - vulkanCommandBuffer->commandBuffer, - vulkanTextureSlice->parent->image, - AccessMap[vulkanTextureSlice->resourceAccessType].imageLayout, - transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer, - 1, - &imageCopy - ); - - VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer); - VULKAN_INTERNAL_TrackTextureSlice(renderer, vulkanCommandBuffer, vulkanTextureSlice); - VULKAN_INTERNAL_TrackCopiedTextureSlice(renderer, vulkanCommandBuffer, vulkanTextureSlice); -} - -static void VULKAN_DownloadFromBuffer( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_GpuBuffer *gpuBuffer, - Refresh_TransferBuffer *transferBuffer, - Refresh_BufferCopy *copyParams, - Refresh_TransferOptions transferOption -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanBufferContainer *gpuBufferContainer = (VulkanBufferContainer*) gpuBuffer; - VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer*) transferBuffer; - VkBufferCopy bufferCopy; - - if ( - transferOption == REFRESH_TRANSFEROPTIONS_SAFEDISCARD && - SDL_AtomicGet(&transferBufferContainer->activeBufferHandle->vulkanBuffer->referenceCount) > 0 - ) { - VULKAN_INTERNAL_DiscardActiveBuffer( - renderer, - transferBufferContainer - ); - } - - VULKAN_INTERNAL_BufferMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_WRITE, - transferBufferContainer->activeBufferHandle->vulkanBuffer - ); - - VULKAN_INTERNAL_BufferMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_READ, - gpuBufferContainer->activeBufferHandle->vulkanBuffer - ); - - bufferCopy.srcOffset = copyParams->srcOffset; - bufferCopy.dstOffset = copyParams->dstOffset; - bufferCopy.size = copyParams->size; - - renderer->vkCmdCopyBuffer( - vulkanCommandBuffer->commandBuffer, - gpuBufferContainer->activeBufferHandle->vulkanBuffer->buffer, - transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer, - 1, - &bufferCopy - ); - - VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer); - VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, gpuBufferContainer->activeBufferHandle->vulkanBuffer); - VULKAN_INTERNAL_TrackCopiedBuffer(renderer, vulkanCommandBuffer, gpuBufferContainer->activeBufferHandle->vulkanBuffer); -} - static void VULKAN_CopyTextureToTexture( Refresh_Renderer *driverData, Refresh_CommandBuffer *commandBuffer, @@ -8751,147 +8627,6 @@ static void VULKAN_CopyTextureToTexture( VULKAN_INTERNAL_TrackCopiedTextureSlice(renderer, vulkanCommandBuffer, dstSlice); } -static void VULKAN_CopyTextureToBuffer( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureRegion *textureRegion, - Refresh_GpuBuffer *gpuBuffer, - Refresh_BufferImageCopy *copyParams, - Refresh_WriteOptions writeOption -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanTextureSlice *vulkanTextureSlice; - VulkanBufferContainer *bufferContainer = (VulkanBufferContainer*) gpuBuffer; - VkBufferImageCopy imageCopy; - - vulkanTextureSlice = VULKAN_INTERNAL_RefreshToVulkanTextureSlice(&textureRegion->textureSlice); - - if ( - writeOption == REFRESH_WRITEOPTIONS_SAFEDISCARD && - SDL_AtomicGet(&bufferContainer->activeBufferHandle->vulkanBuffer->referenceCount) > 0 - ) { - VULKAN_INTERNAL_DiscardActiveBuffer( - renderer, - bufferContainer - ); - } - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_READ, - vulkanTextureSlice - ); - - VULKAN_INTERNAL_BufferMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_WRITE, - bufferContainer->activeBufferHandle->vulkanBuffer - ); - - imageCopy.imageExtent.width = textureRegion->w; - imageCopy.imageExtent.height = textureRegion->h; - imageCopy.imageExtent.depth = textureRegion->d; - imageCopy.imageOffset.x = textureRegion->x; - imageCopy.imageOffset.y = textureRegion->y; - imageCopy.imageOffset.z = textureRegion->z; - imageCopy.imageSubresource.aspectMask = vulkanTextureSlice->parent->aspectFlags; - imageCopy.imageSubresource.baseArrayLayer = textureRegion->textureSlice.layer; - imageCopy.imageSubresource.layerCount = 1; - imageCopy.imageSubresource.mipLevel = textureRegion->textureSlice.mipLevel; - imageCopy.bufferOffset = copyParams->bufferOffset; - imageCopy.bufferRowLength = copyParams->bufferStride; - imageCopy.bufferImageHeight = copyParams->bufferImageHeight; - - renderer->vkCmdCopyImageToBuffer( - vulkanCommandBuffer->commandBuffer, - vulkanTextureSlice->parent->image, - AccessMap[vulkanTextureSlice->resourceAccessType].imageLayout, - bufferContainer->activeBufferHandle->vulkanBuffer->buffer, - 1, - &imageCopy - ); - - VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, bufferContainer->activeBufferHandle->vulkanBuffer); - VULKAN_INTERNAL_TrackTextureSlice(renderer, vulkanCommandBuffer, vulkanTextureSlice); - VULKAN_INTERNAL_TrackCopiedBuffer(renderer, vulkanCommandBuffer, bufferContainer->activeBufferHandle->vulkanBuffer); - VULKAN_INTERNAL_TrackCopiedTextureSlice(renderer, vulkanCommandBuffer, vulkanTextureSlice); -} - -static void VULKAN_CopyBufferToTexture( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_GpuBuffer *gpuBuffer, - Refresh_TextureRegion *textureRegion, - Refresh_BufferImageCopy *copyParams, - Refresh_WriteOptions writeOption -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanBufferContainer *bufferContainer = (VulkanBufferContainer*) gpuBuffer; - VulkanTextureContainer *textureContainer = (VulkanTextureContainer*) textureRegion->textureSlice.texture; - VulkanTextureSlice *vulkanTextureSlice; - VkBufferImageCopy imageCopy; - - vulkanTextureSlice = VULKAN_INTERNAL_RefreshToVulkanTextureSlice(&textureRegion->textureSlice); - - if ( - writeOption == REFRESH_WRITEOPTIONS_SAFEDISCARD && - textureContainer->canBeDiscarded && - SDL_AtomicGet(&vulkanTextureSlice->referenceCount) > 0 - ) { - VULKAN_INTERNAL_DiscardActiveTexture( - renderer, - textureContainer - ); - vulkanTextureSlice = VULKAN_INTERNAL_RefreshToVulkanTextureSlice(&textureRegion->textureSlice); - } - - VULKAN_INTERNAL_BufferMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_READ, - bufferContainer->activeBufferHandle->vulkanBuffer - ); - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_WRITE, - vulkanTextureSlice - ); - - imageCopy.imageExtent.width = textureRegion->w; - imageCopy.imageExtent.height = textureRegion->h; - imageCopy.imageExtent.depth = textureRegion->d; - imageCopy.imageOffset.x = textureRegion->x; - imageCopy.imageOffset.y = textureRegion->y; - imageCopy.imageOffset.z = textureRegion->z; - imageCopy.imageSubresource.aspectMask = vulkanTextureSlice->parent->aspectFlags; - imageCopy.imageSubresource.baseArrayLayer = textureRegion->textureSlice.layer; - imageCopy.imageSubresource.layerCount = 1; - imageCopy.imageSubresource.mipLevel = textureRegion->textureSlice.mipLevel; - imageCopy.bufferOffset = copyParams->bufferOffset; - imageCopy.bufferRowLength = copyParams->bufferStride; - imageCopy.bufferImageHeight = copyParams->bufferImageHeight; - - renderer->vkCmdCopyBufferToImage( - vulkanCommandBuffer->commandBuffer, - bufferContainer->activeBufferHandle->vulkanBuffer->buffer, - vulkanTextureSlice->parent->image, - AccessMap[vulkanTextureSlice->resourceAccessType].imageLayout, - 1, - &imageCopy - ); - - VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, bufferContainer->activeBufferHandle->vulkanBuffer); - VULKAN_INTERNAL_TrackTextureSlice(renderer, vulkanCommandBuffer, vulkanTextureSlice); - VULKAN_INTERNAL_TrackCopiedBuffer(renderer, vulkanCommandBuffer, bufferContainer->activeBufferHandle->vulkanBuffer); - VULKAN_INTERNAL_TrackCopiedTextureSlice(renderer, vulkanCommandBuffer, vulkanTextureSlice); -} - static void VULKAN_CopyBufferToBuffer( Refresh_Renderer *driverData, Refresh_CommandBuffer *commandBuffer, @@ -10461,6 +10196,175 @@ static void VULKAN_ReleaseFence( VULKAN_INTERNAL_ReturnFenceToPool((VulkanRenderer*) driverData, (VkFence) fence); } +/* Readback */ + +static void VULKAN_DownloadFromTexture( + Refresh_Renderer *driverData, + Refresh_TextureRegion *textureRegion, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferImageCopy *copyParams, + Refresh_TransferOptions transferOption +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanTextureSlice *vulkanTextureSlice; + VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer*) transferBuffer; + VkBufferImageCopy imageCopy; + vulkanTextureSlice = VULKAN_INTERNAL_RefreshToVulkanTextureSlice(&textureRegion->textureSlice); + Refresh_Fence *fence; + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) VULKAN_AcquireCommandBuffer(driverData); + VulkanResourceAccessType originalTextureSliceAccessType; + VulkanResourceAccessType originalBufferAccessType; + + if ( + transferOption == REFRESH_TRANSFEROPTIONS_SAFEDISCARD && + SDL_AtomicGet(&transferBufferContainer->activeBufferHandle->vulkanBuffer->referenceCount) > 0 + ) { + VULKAN_INTERNAL_DiscardActiveBuffer( + renderer, + transferBufferContainer + ); + vulkanTextureSlice = VULKAN_INTERNAL_RefreshToVulkanTextureSlice(&textureRegion->textureSlice); + } + + originalTextureSliceAccessType = vulkanTextureSlice->resourceAccessType; + originalBufferAccessType = transferBufferContainer->activeBufferHandle->vulkanBuffer->resourceAccessType; + + VULKAN_INTERNAL_BufferMemoryBarrier( + renderer, + vulkanCommandBuffer->commandBuffer, + RESOURCE_ACCESS_TRANSFER_WRITE, + transferBufferContainer->activeBufferHandle->vulkanBuffer + ); + + VULKAN_INTERNAL_ImageMemoryBarrier( + renderer, + vulkanCommandBuffer->commandBuffer, + RESOURCE_ACCESS_TRANSFER_READ, + vulkanTextureSlice + ); + + imageCopy.imageExtent.width = textureRegion->w; + imageCopy.imageExtent.height = textureRegion->h; + imageCopy.imageExtent.depth = textureRegion->d; + imageCopy.imageOffset.x = textureRegion->x; + imageCopy.imageOffset.y = textureRegion->y; + imageCopy.imageOffset.z = textureRegion->z; + imageCopy.imageSubresource.aspectMask = vulkanTextureSlice->parent->aspectFlags; + imageCopy.imageSubresource.baseArrayLayer = textureRegion->textureSlice.layer; + imageCopy.imageSubresource.layerCount = 1; + imageCopy.imageSubresource.mipLevel = textureRegion->textureSlice.mipLevel; + imageCopy.bufferOffset = copyParams->bufferOffset; + imageCopy.bufferRowLength = copyParams->bufferStride; + imageCopy.bufferImageHeight = copyParams->bufferImageHeight; + + renderer->vkCmdCopyImageToBuffer( + vulkanCommandBuffer->commandBuffer, + vulkanTextureSlice->parent->image, + AccessMap[vulkanTextureSlice->resourceAccessType].imageLayout, + transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer, + 1, + &imageCopy + ); + + VULKAN_INTERNAL_ImageMemoryBarrier( + renderer, + vulkanCommandBuffer->commandBuffer, + originalTextureSliceAccessType, + vulkanTextureSlice + ); + + VULKAN_INTERNAL_BufferMemoryBarrier( + renderer, + vulkanCommandBuffer->commandBuffer, + originalBufferAccessType, + transferBufferContainer->activeBufferHandle->vulkanBuffer); + + VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer); + VULKAN_INTERNAL_TrackTextureSlice(renderer, vulkanCommandBuffer, vulkanTextureSlice); + VULKAN_INTERNAL_TrackCopiedTextureSlice(renderer, vulkanCommandBuffer, vulkanTextureSlice); + + fence = VULKAN_SubmitAndAcquireFence(driverData, (Refresh_CommandBuffer*) vulkanCommandBuffer); + VULKAN_WaitForFences(driverData, 1, 1, &fence); + VULKAN_ReleaseFence(driverData, fence); +} + +static void VULKAN_DownloadFromBuffer( + Refresh_Renderer *driverData, + Refresh_GpuBuffer *gpuBuffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams, + Refresh_TransferOptions transferOption +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanBufferContainer *gpuBufferContainer = (VulkanBufferContainer*) gpuBuffer; + VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer*) transferBuffer; + VkBufferCopy bufferCopy; + Refresh_Fence *fence; + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) VULKAN_AcquireCommandBuffer(driverData); + VulkanResourceAccessType originalTransferBufferAccessType; + VulkanResourceAccessType originalGpuBufferAccessType; + + if ( + transferOption == REFRESH_TRANSFEROPTIONS_SAFEDISCARD && + SDL_AtomicGet(&transferBufferContainer->activeBufferHandle->vulkanBuffer->referenceCount) > 0 + ) { + VULKAN_INTERNAL_DiscardActiveBuffer( + renderer, + transferBufferContainer + ); + } + + originalTransferBufferAccessType = transferBufferContainer->activeBufferHandle->vulkanBuffer->resourceAccessType; + originalGpuBufferAccessType = gpuBufferContainer->activeBufferHandle->vulkanBuffer->resourceAccessType; + + VULKAN_INTERNAL_BufferMemoryBarrier( + renderer, + vulkanCommandBuffer->commandBuffer, + RESOURCE_ACCESS_TRANSFER_WRITE, + transferBufferContainer->activeBufferHandle->vulkanBuffer + ); + + VULKAN_INTERNAL_BufferMemoryBarrier( + renderer, + vulkanCommandBuffer->commandBuffer, + RESOURCE_ACCESS_TRANSFER_READ, + gpuBufferContainer->activeBufferHandle->vulkanBuffer + ); + + bufferCopy.srcOffset = copyParams->srcOffset; + bufferCopy.dstOffset = copyParams->dstOffset; + bufferCopy.size = copyParams->size; + + renderer->vkCmdCopyBuffer( + vulkanCommandBuffer->commandBuffer, + gpuBufferContainer->activeBufferHandle->vulkanBuffer->buffer, + transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer, + 1, + &bufferCopy + ); + + VULKAN_INTERNAL_BufferMemoryBarrier( + renderer, + vulkanCommandBuffer->commandBuffer, + originalTransferBufferAccessType, + transferBufferContainer->activeBufferHandle->vulkanBuffer + ); + + VULKAN_INTERNAL_BufferMemoryBarrier( + renderer, + vulkanCommandBuffer->commandBuffer, + originalGpuBufferAccessType, + gpuBufferContainer->activeBufferHandle->vulkanBuffer); + + VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer); + VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, gpuBufferContainer->activeBufferHandle->vulkanBuffer); + VULKAN_INTERNAL_TrackCopiedBuffer(renderer, vulkanCommandBuffer, gpuBufferContainer->activeBufferHandle->vulkanBuffer); + + fence = VULKAN_SubmitAndAcquireFence(driverData, (Refresh_CommandBuffer*) vulkanCommandBuffer); + VULKAN_WaitForFences(driverData, 1, 1, &fence); + VULKAN_ReleaseFence(driverData, fence); +} + /* Device instantiation */ static inline uint8_t CheckDeviceExtensions( diff --git a/visualc/Refresh.vcxproj b/visualc/Refresh.vcxproj index d73b03c..52fd6af 100644 --- a/visualc/Refresh.vcxproj +++ b/visualc/Refresh.vcxproj @@ -61,7 +61,7 @@ Level3 Disabled - REFRESH_DRIVER_VULKAN;%(PreprocessorDefinitions) + REFRESH_DRIVER_VULKAN;REFRESH_DRIVER_D3D11;%(PreprocessorDefinitions) DebugFull @@ -72,7 +72,7 @@ Level3 MaxSpeed - REFRESH_DRIVER_VULKAN;%(PreprocessorDefinitions) + REFRESH_DRIVER_VULKAN;REFRESH_DRIVER_D3D11;%(PreprocessorDefinitions) true true @@ -84,6 +84,7 @@ + @@ -91,6 +92,7 @@ + diff --git a/visualc/Refresh.vcxproj.filters b/visualc/Refresh.vcxproj.filters index 67135bd..769e63a 100644 --- a/visualc/Refresh.vcxproj.filters +++ b/visualc/Refresh.vcxproj.filters @@ -10,6 +10,9 @@ Source Files + + Source Files + @@ -24,6 +27,9 @@ Header Files + + Header Files +