#include <Core/Common/NonNullPtr.h>
#include <Core/HAL/DynLib/DynamicLibrary.h>
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Core/Memory/Allocator.h>
#include <Graphics/D3D12/D3D12Buffer.h>
#include <Graphics/D3D12/D3D12CommandList.h>
#include <Graphics/D3D12/D3D12DescriptorHeap.h>
#include <Graphics/D3D12/D3D12GraphicsDevice.h>
#include <Graphics/D3D12/D3D12GraphicsPipeline.h>
#include <Graphics/D3D12/D3D12Includes.h>
#include <Graphics/D3D12/D3D12InternalTests.h>
#include <Graphics/D3D12/D3D12RenderPass.h>
#include <Graphics/D3D12/D3D12Shader.h>
#include <Graphics/D3D12/D3D12SwapChain.h>
#include <Graphics/D3D12/D3D12Synchronization.h>
#include <Graphics/D3D12/D3D12Texture.h>
#include <Graphics/D3D12/D3D12Utils.h>
#include <Graphics/Graphics.h>
#include <Graphics/GraphicsDevice.h>

#define D3D12_DLL "d3d12.dll"
#define D3D12_CREATEDEVICE_FUNC "D3D12CreateDevice"
#define D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE_FUNC "D3D12SerializeVersionedRootSignature"

// TODO : Use LoadLibrary and not link to the lib. Allows failing earlier if Dx12 is not installed for some reason
// + Will load the dll when needed
// This will prevent us from using IID_ variables as they are defined in dxguid.lib
namespace Juliet::D3D12
{
    namespace
    {
        // Note: This is the highest my Gfx Card supports (5700XT)
        // https://en.wikipedia.org/wiki/Feature_levels_in_Direct3D#Direct3D_12
        // 12_2 Adds RayTracing and others feature supported by RDNA2 and greater and Gefore 20xx and greater
        constexpr D3D_FEATURE_LEVEL kD3DFeatureLevel    = D3D_FEATURE_LEVEL_12_1;
        constexpr auto              kD3DFeatureLevelStr = "12_1";

        bool CheckResourceTypeTier(ID3D12Device5* device)
        {
            D3D12_FEATURE_DATA_D3D12_OPTIONS options = {};
            HRESULT                          result =
                device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS));
            if (SUCCEEDED(result))
            {
                if (options.ResourceBindingTier < D3D12_RESOURCE_BINDING_TIER_3)
                {
                    Juliet::LogError(LogCategory::Graphics, "Resource Binding Tier 3 not supported. :(");
                    return false;
                }
                return true;
            }

            Juliet::LogError(LogCategory::Graphics, "Couldn't fetch D3D12_FEATURE_D3D12_OPTIONS :(");
            return false;
        }

        bool CheckShaderModel(ID3D12Device5* device)
        {
            // Check Shader Model
            bool                            foundShaderModel   = false;
            D3D12_FEATURE_DATA_SHADER_MODEL shaderModel        = {};
            constexpr D3D_SHADER_MODEL      allModelVersions[] = {
#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 612)
                D3D_SHADER_MODEL_6_9,
#endif
#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 606)
                D3D_SHADER_MODEL_6_8,
#endif
#if defined(D3D12_SDK_VERSION) && (D3D12_SDK_VERSION >= 3)
                D3D_SHADER_MODEL_6_7,
#endif
                D3D_SHADER_MODEL_6_6, D3D_SHADER_MODEL_6_5, D3D_SHADER_MODEL_6_4, D3D_SHADER_MODEL_6_3,
                D3D_SHADER_MODEL_6_2, D3D_SHADER_MODEL_6_1, D3D_SHADER_MODEL_6_0, D3D_SHADER_MODEL_5_1
            };
            for (auto allModelVersion : allModelVersions)
            {
                shaderModel.HighestShaderModel = allModelVersion;
                HRESULT result                 = device->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &shaderModel,
                                                                             sizeof(D3D12_FEATURE_DATA_SHADER_MODEL));
                if (result != E_INVALIDARG)
                {
                    if (FAILED(result))
                    {
                        shaderModel.HighestShaderModel = static_cast<D3D_SHADER_MODEL>(0);
                    }
                    else
                    {
                        foundShaderModel = true;
                        break;
                    }
                }
            }

            if (!foundShaderModel)
            {
                shaderModel.HighestShaderModel = static_cast<D3D_SHADER_MODEL>(0);
            }

            if (shaderModel.HighestShaderModel < D3D_SHADER_MODEL_6_6)
            {
                Juliet::LogError(LogCategory::Graphics, "Shader Model 6.6 not supported. :(");
                return false;
            }
            return true;
        }

        bool CheckDriver()
        {
            // Can we Load D3D12.dll and the create device function
            DynamicLibrary* d3d12_dll = LoadDynamicLibrary(D3D12_DLL);
            if (d3d12_dll == nullptr)
            {
                Log(LogLevel::Warning, LogCategory::Graphics, "DX12: Couldn't find " D3D12_DLL);
                return false;
            }

            auto* D3D12CreateDeviceFuncPtr =
                TOD3D12FuncPtr(PFN_D3D12_CREATE_DEVICE, LoadFunction(d3d12_dll, D3D12_CREATEDEVICE_FUNC));
            if (D3D12CreateDeviceFuncPtr == nullptr)
            {
                Log(LogLevel::Warning, LogCategory::Graphics, "DX12: Couldn't find function " D3D12_CREATEDEVICE_FUNC " in " D3D12_DLL);
                UnloadDynamicLibrary(d3d12_dll);
                return false;
            }

            // Can create DXGI factory ?
            IDXGIFactory1* factory1 = nullptr;
            HRESULT        result   = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast<void**>(&factory1));
            if (FAILED(result))
            {
                Log(LogLevel::Warning, LogCategory::Graphics, "DX12: Cannot create DXGIFactory1");
                return false;
            }

            // Can query the 1.4 factory ?
            IDXGIFactory4* factory4 = nullptr;
            result                  = factory1->QueryInterface(IID_IDXGIFactory4, reinterpret_cast<void**>(&factory4));
            if (FAILED(result))
            {
                factory1->Release();
                Log(LogLevel::Warning, LogCategory::Graphics, "DX12: Failed to query DXGI1.4.");
                return false;
            }
            factory4->Release();

            // Check for 1.6. (It's not mandatory).
            IDXGIAdapter1* adapter  = nullptr;
            IDXGIFactory6* factory6 = nullptr;
            result                  = factory1->QueryInterface(IID_IDXGIFactory6, reinterpret_cast<void**>(&factory6));
            if (SUCCEEDED(result))
            {
                result = factory6->EnumAdapterByGpuPreference(0, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE,
                                                              IID_IDXGIAdapter1, reinterpret_cast<void**>(&adapter));
                factory6->Release();
            }
            else
            {
                result = factory1->EnumAdapters1(0, &adapter);
            }

            if (FAILED(result))
            {
                Log(LogLevel::Warning, LogCategory::Graphics, "DX12: Failed to find an adapter for D3D12.");

                factory1->Release();
                return false;
            }

            ID3D12Device5* device = nullptr;
            result = D3D12CreateDeviceFuncPtr(static_cast<IUnknown*>(adapter), kD3DFeatureLevel, IID_ID3D12Device5,
                                              reinterpret_cast<void**>(&device));

            bool driverIsValid = true;
            if (SUCCEEDED(result))
            {
                driverIsValid &= CheckShaderModel(device);
                driverIsValid &= CheckResourceTypeTier(device);

                device->Release();
            }
            else
            {
                Log(LogLevel::Warning, LogCategory::Graphics,
                    "DX12: Failed to create a D3D12Device with feature level %s.", kD3DFeatureLevelStr);
                driverIsValid = false;
            }
            adapter->Release();
            factory1->Release();

            return driverIsValid;
        }

        void DestroyGraphicsRootSignature(D3D12GraphicsRootSignature* rootSignature)
        {
            if (!rootSignature)
            {
                return;
            }
            if (rootSignature->Handle)
            {
                rootSignature->Handle->Release();
            }
            Free(rootSignature);
        }

        D3D12GraphicsRootSignature* CreateGraphicsRootSignature(NonNullPtr<D3D12Driver> d3d12Driver)
        {
            auto d3d12GraphicsRootSignature =
                static_cast<D3D12GraphicsRootSignature*>(Calloc(1, sizeof(D3D12GraphicsRootSignature)));
            if (!d3d12GraphicsRootSignature)
            {
                return nullptr;
            }

            D3D12_ROOT_PARAMETER1 parameters[ToUnderlying(RootParameters::Count)] = {};
            parameters[ToUnderlying(RootParameters::Constants32Bits)] = {
                .ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS,
                .Constants = {
                    .ShaderRegister = 0,
                    .RegisterSpace  = 0,
                    .Num32BitValues = 58, // 58 because each root cbv takes 2 uints, and the max is 64
                },
                .ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL
            };

            D3D12_STATIC_SAMPLER_DESC samplers[] = {
                {
                    // s_nearest
                    .Filter           = D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR,
                    .AddressU         = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
                    .AddressV         = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
                    .AddressW         = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
                    .MipLODBias       = 0.0f,
                    .MaxAnisotropy    = 0,
                    .ComparisonFunc   = D3D12_COMPARISON_FUNC_NONE,
                    .BorderColor      = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK,
                    .MinLOD           = 0.0f,
                    .MaxLOD           = D3D12_FLOAT32_MAX,
                    .ShaderRegister   = 0,
                    .RegisterSpace    = 0,
                    .ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL,
                },
            };

            D3D12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc = {
                .Version = D3D_ROOT_SIGNATURE_VERSION_1_1,
                .Desc_1_1 = {
                    .NumParameters     = ArraySize(parameters),
                    .pParameters       = parameters,
                    .NumStaticSamplers = ArraySize(samplers),
                    .pStaticSamplers   = samplers,
                    .Flags             = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
                                         D3D12_ROOT_SIGNATURE_FLAG_CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED |
                                         D3D12_ROOT_SIGNATURE_FLAG_SAMPLER_HEAP_DIRECTLY_INDEXED,
                },
            };

            // Serialize the root signature
            ID3DBlob* serializedRootSignature;
            ID3DBlob* errorBlob;
            HRESULT   res =
                d3d12Driver->D3D12SerializeVersionedRootSignatureFct(&rootSignatureDesc, &serializedRootSignature, &errorBlob);
            if (FAILED(res))
            {
                if (errorBlob)
                {
                    auto errorBuffer = errorBlob->GetBufferPointer();
                    LogError(LogCategory::Graphics, "Failed to serialize RootSignature: %s", errorBuffer);

                    errorBlob->Release();
                }
                DestroyGraphicsRootSignature(d3d12GraphicsRootSignature);
                return nullptr;
            }

            // Create the root signature
            ID3D12RootSignature* rootSignature;
            res = d3d12Driver->D3D12Device->CreateRootSignature(0, serializedRootSignature->GetBufferPointer(),
                                                                serializedRootSignature->GetBufferSize(), IID_ID3D12RootSignature,
                                                                reinterpret_cast<void**>(&rootSignature));
            if (FAILED(res))
            {
                if (errorBlob)
                {
                    LogError(LogCategory::Graphics, "Failed to create RootSignature: %s",
                             (const char*)errorBlob->GetBufferPointer());
                    errorBlob->Release();
                }
                DestroyGraphicsRootSignature(d3d12GraphicsRootSignature);
                return nullptr;
            }

            d3d12GraphicsRootSignature->Handle = rootSignature;
            return d3d12GraphicsRootSignature;
        }
#if JULIET_DEBUG

#ifdef IDXGIINFOQUEUE_SUPPORTED

#define DXGIDEBUG_DLL "dxgidebug.dll"
#define DXGI_GET_DEBUG_INTERFACE_FUNC "DXGIGetDebugInterface"

        void InitializeDXGIDebug(NonNullPtr<D3D12Driver> driver)
        {
            // See https://github.com/microsoft/DirectX-Graphics-Samples/blob/7aa24663f26e547a5bc437db028dfcfdb4b3c8f3/TechniqueDemos/D3D12MemoryManagement/src/Framework.cpp#L957
            // For win10 only we can just use dxgiGetDebugInterface1
            using LPDXGIGETDEBUGINTERFACE = HRESULT(WINAPI*)(REFIID, void**);

            driver->DXGIDebugDLL = LoadDynamicLibrary(DXGIDEBUG_DLL);
            if (driver->DXGIDebugDLL)
            {
                auto dxgiGetDebugInterface =
                    TOD3D12FuncPtr(LPDXGIGETDEBUGINTERFACE, LoadFunction(driver->DXGIDebugDLL, DXGI_GET_DEBUG_INTERFACE_FUNC));

                HRESULT result = dxgiGetDebugInterface(IID_IDXGIDebug, (void**)&driver->DXGIDebug);
                if (FAILED(result))
                {
                    Log(LogLevel::Warning, LogCategory::Graphics, "Could not get IDXGIDebug interface");
                }

                result = dxgiGetDebugInterface(IID_IDXGIInfoQueue, (void**)&driver->DXGIInfoQueue);
                if (FAILED(result))
                {
                    Log(LogLevel::Warning, LogCategory::Graphics, "Could not get IDXGIInfoQueue interface");
                }
                else
                {
                    driver->DXGIInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, TRUE);
                    driver->DXGIInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, TRUE);
                    driver->DXGIInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_WARNING, TRUE);
                }
            }
        }

        void ShutdownDXGIDebug(NonNullPtr<D3D12Driver> driver)
        {
            if (driver->DXGIDebug)
            {
                driver->DXGIDebug->ReportLiveObjects(DXGI_DEBUG_ALL, static_cast<DXGI_DEBUG_RLO_FLAGS>(
                                                                         DXGI_DEBUG_RLO_SUMMARY | DXGI_DEBUG_RLO_DETAIL));
                driver->DXGIDebug->Release();
                driver->DXGIDebug = nullptr;
            }

            if (driver->DXGIDebugDLL)
            {
                UnloadDynamicLibrary(driver->DXGIDebugDLL);
                driver->DXGIDebugDLL = nullptr;
            }
        }
#endif

#define D3D12_GET_DEBUG_INTERFACE_FUNC "D3D12GetDebugInterface"

        void InitializeD3D12DebugLayer(NonNullPtr<D3D12Driver> driver)
        {
            auto D3D12GetDebugInterfaceFunc =
                TOD3D12FuncPtr(PFN_D3D12_GET_DEBUG_INTERFACE, LoadFunction(driver->D3D12DLL, D3D12_GET_DEBUG_INTERFACE_FUNC));

            if (D3D12GetDebugInterfaceFunc == nullptr)
            {
                LogWarning(LogCategory::Graphics, "Could not load function: " D3D12_GET_DEBUG_INTERFACE_FUNC);
                return;
            }

            HRESULT result = D3D12GetDebugInterfaceFunc(IID_ID3D12Debug1, reinterpret_cast<void**>(&driver->D3D12Debug));
            if (FAILED(result))
            {
                LogWarning(LogCategory::Graphics, "Could not get ID3D12Debug interface");
                return;
            }

            driver->D3D12Debug->EnableDebugLayer();
        }

        bool InitializeD3D12DebugInfoQueue(NonNullPtr<D3D12Driver> driver)
        {
            ID3D12InfoQueue*       infoQueue    = nullptr;
            D3D12_MESSAGE_SEVERITY severities[] = { D3D12_MESSAGE_SEVERITY_INFO };

            HRESULT result = driver->D3D12Device->QueryInterface(IID_ID3D12InfoQueue, reinterpret_cast<void**>(&infoQueue));
            if (FAILED(result))
            {
                LogError(driver->D3D12Device, "Failed to convert ID3D12Device to ID3D12InfoQueue", result);
                return false;
            }

            D3D12_INFO_QUEUE_FILTER filter = {};
            filter.DenyList.NumSeverities  = 1;
            filter.DenyList.pSeverityList  = severities;
            infoQueue->PushStorageFilter(&filter);
            // infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
            infoQueue->Release();

            return true;
        }

        void WINAPI OnD3D12DebugInfoMsg(D3D12_MESSAGE_CATEGORY category, D3D12_MESSAGE_SEVERITY severity,
                                        D3D12_MESSAGE_ID id, LPCSTR description, void* /*context*/)
        {
            String catStr = WrapString("UNKNOWN");
            switch (category)
            {
            case D3D12_MESSAGE_CATEGORY_APPLICATION_DEFINED: catStr = WrapString("APPLICATION_DEFINED"); break;
            case D3D12_MESSAGE_CATEGORY_MISCELLANEOUS: catStr = WrapString("MISCELLANEOUS"); break;
            case D3D12_MESSAGE_CATEGORY_INITIALIZATION: catStr = WrapString("INITIALIZATION"); break;
            case D3D12_MESSAGE_CATEGORY_CLEANUP: catStr = WrapString("CLEANUP"); break;
            case D3D12_MESSAGE_CATEGORY_COMPILATION: catStr = WrapString("COMPILATION"); break;
            case D3D12_MESSAGE_CATEGORY_STATE_CREATION: catStr = WrapString("STATE_CREATION"); break;
            case D3D12_MESSAGE_CATEGORY_STATE_SETTING: catStr = WrapString("STATE_SETTING"); break;
            case D3D12_MESSAGE_CATEGORY_STATE_GETTING: catStr = WrapString("STATE_GETTING"); break;
            case D3D12_MESSAGE_CATEGORY_RESOURCE_MANIPULATION: catStr = WrapString("RESOURCE_MANIPULATION"); break;
            case D3D12_MESSAGE_CATEGORY_EXECUTION: catStr = WrapString("EXECUTION"); break;
            case D3D12_MESSAGE_CATEGORY_SHADER: catStr = WrapString("SHADER"); break;
            }

            String severityStr = WrapString("UNKNOWN");
            switch (severity)
            {
            case D3D12_MESSAGE_SEVERITY_CORRUPTION: severityStr = WrapString("CORRUPTION"); break;
            case D3D12_MESSAGE_SEVERITY_ERROR: severityStr = WrapString("ERROR"); break;
            case D3D12_MESSAGE_SEVERITY_WARNING: severityStr = WrapString("WARNING"); break;
            case D3D12_MESSAGE_SEVERITY_INFO: severityStr = WrapString("INFO"); break;
            case D3D12_MESSAGE_SEVERITY_MESSAGE: severityStr = WrapString("MESSAGE"); break;
            }

            if (severity <= D3D12_MESSAGE_SEVERITY_ERROR)
            {
                LogWarning(LogCategory::Graphics, "D3D12 ERROR: %s [%s %s #%d]", description, CStr(catStr), CStr(severityStr), id);
            }
            else
            {
                LogWarning(LogCategory::Graphics, "D3D12 WARNING: %s [%s %s #%d]", description, CStr(catStr),
                           CStr(severityStr), id);
            }
        }

        void InitializeD3D12DebugInfoLogger(NonNullPtr<D3D12Driver> driver)
        {
            // Only supported on Win 11 apparently
            ID3D12InfoQueue1* infoQueue = nullptr;
            HRESULT result = driver->D3D12Device->QueryInterface(IID_ID3D12InfoQueue1, reinterpret_cast<void**>(&infoQueue));
            if (FAILED(result))
            {
                return;
            }

            infoQueue->RegisterMessageCallback(OnD3D12DebugInfoMsg, D3D12_MESSAGE_CALLBACK_FLAG_NONE, nullptr, nullptr);
            infoQueue->Release();
            Log(LogLevel::Message, LogCategory::Graphics, "DX12: Debug Info Logger Initialized");
        }
#endif

        void DestroyDriver_Internal(NonNullPtr<D3D12Driver> driver)
        {
            // Destroy Descriptor pools
            for (uint32 i = 0; i < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; i += 1)
            {
                if (driver->StagingDescriptorPools[i])
                {
                    Internal::DestroyStagingDescriptorPool(driver->StagingDescriptorPools[i]);
                    driver->StagingDescriptorPools[i] = nullptr;
                }
            }

            Internal::DestroyDescriptorHeapPool(driver->SamplerHeapPool);

            // Release command buffers
            for (uint32 i = 0; i < driver->AvailableCommandListCount; i += 1)
            {
                if (driver->AvailableCommandLists[i])
                {
                    Internal::DestroyCommandList(driver->AvailableCommandLists[i]);
                    driver->AvailableCommandLists[i] = nullptr;
                }
            }

            // Release fences
            for (uint32 i = 0; i < driver->AvailableFenceCount; i += 1)
            {
                if (driver->AvailableFences[i])
                {
                    Internal::DestroyFence(driver->AvailableFences[i]);
                    driver->AvailableFences[i] = nullptr;
                }
            }

            DestroyGraphicsRootSignature(driver->BindlessRootSignature);

            Internal::DestroyDescriptorHeap(driver->BindlessDescriptorHeap);

            // Clean allocations

            SafeFree(driver->AvailableCommandLists);
            SafeFree(driver->SubmittedCommandLists);
            SafeFree(driver->GraphicsPipelinesToDispose);
            // Free(driver->WindowData); // TODO Should free the vector of WindowData, but we have only one for now
            SafeFree(driver->AvailableFences);

            if (driver->IndirectDrawCommandSignature)
            {
                driver->IndirectDrawCommandSignature->Release();
            }
            if (driver->IndirectIndexedDrawCommandSignature)
            {
                driver->IndirectIndexedDrawCommandSignature->Release();
                driver->IndirectIndexedDrawCommandSignature = nullptr;
            }
            if (driver->IndirectDispatchCommandSignature)
            {
                driver->IndirectDispatchCommandSignature->Release();
                driver->IndirectDispatchCommandSignature = nullptr;
            }

            if (driver->GraphicsQueue)
            {
                driver->GraphicsQueue->Release();
                driver->GraphicsQueue = nullptr;
            }

            if (driver->D3D12Device)
            {
                driver->D3D12Device->Release();
                driver->D3D12Device = nullptr;
            }

            if (driver->DXGIAdapter)
            {
                driver->DXGIAdapter->Release();
                driver->DXGIAdapter = nullptr;
            }

            if (driver->DXGIFactory)
            {
                driver->DXGIFactory->Release();
                driver->DXGIFactory = nullptr;
            }

#if JULIET_DEBUG
            ShutdownDXGIDebug(driver);
#endif

            if (driver->D3D12DLL)
            {
                UnloadDynamicLibrary(driver->D3D12DLL);
                driver->D3D12DLL = nullptr;
            }

            driver->D3D12SerializeVersionedRootSignatureFct = nullptr;

            ArenaRelease(driver->DriverArena);
            Free(driver.Get());
        }

        bool AttachToWindow(NonNullPtr<GPUDriver> driver, NonNullPtr<Window> window)
        {
            auto* d3d12Driver = static_cast<D3D12Driver*>(driver.Get());

            // TODO : Support more than one window
            if (d3d12Driver->WindowData)
            {
                Assert(false, "D3D12 renderer already attached to the window. Right now we handle only one Window.");
                return false;
            }

            auto* windowData = static_cast<D3D12WindowData*>(Calloc(1, sizeof(D3D12WindowData)));
            if (!windowData)
            {
                Log(LogLevel::Error, LogCategory::Graphics, "OOM: D3D12WindowData");
                return false;
            }
            d3d12Driver->WindowData = windowData;

            windowData->Window = window;

            if (!Internal::CreateSwapChain(d3d12Driver, windowData, SwapChainComposition::SDR, PresentMode::VSync))
            {
                Log(LogLevel::Error, LogCategory::Graphics, "AttachToWindow failure: Cannot create Swap Chain.");
                Free(windowData);
                return false;
            }

            d3d12Driver->WindowData = windowData;

            return true;
        }

        void DetachFromWindow(NonNullPtr<GPUDriver> driver, NonNullPtr<Window> /*window*/)
        {
            auto* d3d12Driver = static_cast<D3D12Driver*>(driver.Get());
            auto* windowData  = d3d12Driver->WindowData;
            Assert(windowData && "Trying to destroy a swapchain but no Window Data exists");

            WaitUntilGPUIsIdle(driver);

            for (uint32 idx = 0; idx < GPUDriver::kMaxFramesInFlight; idx += 1)
            {
                if (windowData->InFlightFences[idx] != nullptr)
                {
                    ReleaseFence(driver,
                                 windowData->InFlightFences[idx] JULIET_DEBUG_PARAM(ConstString("DeatchFromWindow")));
                    windowData->InFlightFences[idx] = nullptr;
                }
            }

            Internal::DestroySwapChain(d3d12Driver, d3d12Driver->WindowData);

            SafeFree(d3d12Driver->WindowData);
            d3d12Driver->WindowData = nullptr;
        }

        void DestroyGraphicsDevice(NonNullPtr<GraphicsDevice> device)
        {
            // Note: Its a down cast so clang suggest not to do it but we are totally sure about it.
            auto* driver = static_cast<D3D12Driver*>(device->Driver);
            DestroyDriver_Internal(driver);
            Free(device.Get());
        }

        void DestroyGraphicsPipeline(NonNullPtr<GPUDriver> driver, NonNullPtr<GraphicsPipeline> pipeline)
        {
            auto* d3d12Driver = static_cast<D3D12Driver*>(driver.Get());
            d3d12Driver->GraphicsPipelinesToDispose[d3d12Driver->GraphicsPipelinesToDisposeCount] =
                reinterpret_cast<D3D12GraphicsPipeline*>(pipeline.Get());
            d3d12Driver->GraphicsPipelinesToDisposeCount += 1;
            if (d3d12Driver->GraphicsPipelinesToDisposeCount >= d3d12Driver->GraphicsPipelinesToDisposeCapacity)
            {
                Internal::ReleaseGraphicsPipeline(reinterpret_cast<D3D12GraphicsPipeline*>(pipeline.Get()));
            }
        }

        void CopyBufferToTexture(NonNullPtr<CommandList> commandList, NonNullPtr<Texture> dst, NonNullPtr<GraphicsTransferBuffer> src)
        {
            auto* d3d12CommandList      = reinterpret_cast<D3D12CommandList*>(commandList.Get());
            auto* d3d12TextureContainer = reinterpret_cast<D3D12TextureContainer*>(dst.Get());
            auto* d3d12Texture          = d3d12TextureContainer->ActiveTexture;

            Internal::TextureTransitionFromDefaultUsage(d3d12CommandList, d3d12Texture, D3D12_RESOURCE_STATE_COPY_DEST);

            // Get resource desc using C++ API
            D3D12_RESOURCE_DESC desc = d3d12Texture->Resource->GetDesc();

            D3D12_TEXTURE_COPY_LOCATION dstLoc = {};
            dstLoc.pResource                   = d3d12Texture->Resource;
            dstLoc.Type                        = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
            dstLoc.SubresourceIndex            = 0;

            // Get buffer resource - D3D12Buffer is anonymous, access Handle directly
            // The GraphicsTransferBuffer IS a D3D12Buffer internally
            struct D3D12TransferBuffer
            {
                Internal::D3D12Descriptor Descriptor;
                ID3D12Resource*           Handle;
                D3D12_RESOURCE_STATES     CurrentState;
            };
            auto* d3d12BufferSrc = reinterpret_cast<D3D12TransferBuffer*>(src.Get());

            D3D12_TEXTURE_COPY_LOCATION srcLoc      = {};
            srcLoc.pResource                        = d3d12BufferSrc->Handle;
            srcLoc.Type                             = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
            srcLoc.PlacedFootprint.Offset           = 0;
            srcLoc.PlacedFootprint.Footprint.Format = desc.Format;
            srcLoc.PlacedFootprint.Footprint.Width  = (UINT)desc.Width;
            srcLoc.PlacedFootprint.Footprint.Height = desc.Height;
            srcLoc.PlacedFootprint.Footprint.Depth  = 1;

            uint32 rowPitch = (uint32)desc.Width * 4;
            rowPitch        = (rowPitch + 255u) & ~255u;

            srcLoc.PlacedFootprint.Footprint.RowPitch = rowPitch;

            d3d12CommandList->GraphicsCommandList.CommandList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr);

            Internal::TextureTransitionToDefaultUsage(d3d12CommandList, d3d12Texture, D3D12_RESOURCE_STATE_COPY_DEST);
        }

        uint32 GetDescriptorIndex(NonNullPtr<GraphicsDevice> device, NonNullPtr<GraphicsBuffer> buffer)
        {
            auto* driver = static_cast<D3D12Driver*>(device->Driver);
            return D3D12::GetDescriptorIndex(driver, buffer);
        }

        uint32 GetDescriptorIndexTexture(NonNullPtr<GraphicsDevice> /*device*/, NonNullPtr<Texture> texture)
        {
            auto* textureContainer = reinterpret_cast<D3D12TextureContainer*>(texture.Get());
            return textureContainer->ActiveTexture->SRVHandle.CpuHandleIndex;
        }

#if ALLOW_SHADER_HOT_RELOAD
        bool UpdateGraphicsPipelineShaders(NonNullPtr<GPUDriver> /*driver*/, NonNullPtr<GraphicsPipeline> /*graphicsPipeline*/,
                                           Shader* /*optional_vertexShader*/, Shader* /*optional_fragmentShader*/)
        {
            // Missing implementation, skipping for now
            return false;
        }
#endif

        GraphicsDevice* CreateGraphicsDevice(bool enableDebug)
        {
            auto driver = static_cast<D3D12Driver*>(Calloc(1, sizeof(D3D12Driver)));

            // TODO : Convert everything to arena
            driver->DriverArena = ArenaAllocate({} JULIET_DEBUG_ONLY(, "D3D12 Driver"));

#if JULIET_DEBUG
#ifdef IDXGIINFOQUEUE_SUPPORTED
            if (enableDebug)
            {
                InitializeDXGIDebug(driver);
            }
#endif
#endif
            IDXGIFactory1* factory1 = nullptr;
            HRESULT        result   = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast<void**>(&factory1));
            if (FAILED(result))
            {
                DestroyDriver_Internal(driver);
                Assert(false, "DX12: Cannot create DXGIFactory1");
                return nullptr;
            }

            result = factory1->QueryInterface(IID_IDXGIFactory4, reinterpret_cast<void**>(&driver->DXGIFactory));
            if (FAILED(result))
            {
                DestroyDriver_Internal(driver);
                Assert(false, "DX12: Cannot create DXGIFactory4. Need DXGI1.4 support. Weird because it has been "
                              "checked in CheckDriver");
                return nullptr;
            }
            factory1->Release();

            // Query DXGI1.5 and check for monitor Tearing support
            IDXGIFactory5* factory5 = nullptr;
            result = driver->DXGIFactory->QueryInterface(IID_IDXGIFactory5, reinterpret_cast<void**>(&factory5));
            if (SUCCEEDED(result))
            {
                bool isTearingSupported = false;
                result = factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &isTearingSupported,
                                                       sizeof(isTearingSupported));
                driver->IsTearingSupported = isTearingSupported;
                if (FAILED(result))
                {
                    driver->IsTearingSupported = false;
                }
                factory5->Release();
            }

            // If available use DXGI1.6 to fetch the good graphics card.
            // 1.6 should be available on most Win10 PC if they didnt their windows update.
            // Lets support not having it for now...
            IDXGIFactory6* factory6 = nullptr;
            result = driver->DXGIFactory->QueryInterface(IID_IDXGIFactory6, reinterpret_cast<void**>(&factory6));
            if (SUCCEEDED(result))
            {
                // TODO: Put into the config
                static constexpr bool useLowPower = false;
                result = factory6->EnumAdapterByGpuPreference(0, useLowPower ? DXGI_GPU_PREFERENCE_MINIMUM_POWER : DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE,
                                                              IID_IDXGIAdapter1, reinterpret_cast<void**>(&driver->DXGIAdapter));
                factory6->Release();
            }
            else
            {
                result = driver->DXGIFactory->EnumAdapters1(0, &driver->DXGIAdapter);
            }

            if (FAILED(result))
            {
                DestroyDriver_Internal(driver);
                Assert(false, "Could not find adapter for D3D12Device");
                return nullptr;
            }

            // Adapter is setup, get all the relevant info in the descriptor
            DXGI_ADAPTER_DESC1 adapterDesc;
            result = driver->DXGIAdapter->GetDesc1(&adapterDesc);
            if (FAILED(result))
            {
                DestroyDriver_Internal(driver);
                Assert(false, "Could not get DXGIAdapter description");
                return nullptr;
            }

            // Driver version
            LARGE_INTEGER umdVersion;
            result = driver->DXGIAdapter->CheckInterfaceSupport(IID_IDXGIDevice, &umdVersion);
            if (FAILED(result))
            {
                DestroyDriver_Internal(driver);
                Assert(false, "Could not get DXGIAdapter driver version");
                return nullptr;
            }

            Log(LogLevel::Message, LogCategory::Graphics, "D3D12 Driver Infos:");
            Log(LogLevel::Message, LogCategory::Graphics, "D3D12 Adapter: %S", adapterDesc.Description);
            Log(LogLevel::Message, LogCategory::Graphics, "D3D12 Driver Version: %d.%d.%d.%d", HIWORD(umdVersion.HighPart),
                LOWORD(umdVersion.HighPart), HIWORD(umdVersion.LowPart), LOWORD(umdVersion.LowPart));

            driver->D3D12DLL = LoadDynamicLibrary(D3D12_DLL);
            if (driver->D3D12DLL == nullptr)
            {
                Log(LogLevel::Error, LogCategory::Graphics, "DX12: Couldn't find " D3D12_DLL);
                return nullptr;
            }

            auto* D3D12CreateDeviceFuncPtr =
                TOD3D12FuncPtr(PFN_D3D12_CREATE_DEVICE, LoadFunction(driver->D3D12DLL, D3D12_CREATEDEVICE_FUNC));
            if (D3D12CreateDeviceFuncPtr == nullptr)
            {
                Log(LogLevel::Error, LogCategory::Graphics, "DX12: Couldn't Load function " D3D12_CREATEDEVICE_FUNC " in " D3D12_DLL);
                DestroyDriver_Internal(driver);
                return nullptr;
            }

            driver->D3D12SerializeVersionedRootSignatureFct =
                TOD3D12FuncPtr(PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE,
                               LoadFunction(driver->D3D12DLL, D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE_FUNC));
            if (driver->D3D12SerializeVersionedRootSignatureFct == nullptr)
            {
                Log(LogLevel::Error, LogCategory::Graphics,
                    "DX12: Couldn't Load function " D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE_FUNC " in " D3D12_DLL);
                DestroyDriver_Internal(driver);
                return nullptr;
            }

#if JULIET_DEBUG
            if (enableDebug)
            {
                InitializeD3D12DebugLayer(driver);
            }
#endif

            result = D3D12CreateDeviceFuncPtr(static_cast<IUnknown*>(driver->DXGIAdapter), kD3DFeatureLevel,
                                              IID_ID3D12Device5, reinterpret_cast<void**>(&driver->D3D12Device));
            if (FAILED(result))
            {
                DestroyDriver_Internal(driver);
                Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create D3D12Device5");
                return nullptr;
            }

            Log(LogLevel::Message, LogCategory::Graphics, "DX12: D3D12Device Created: %p", (void*)driver->D3D12Device);

#if JULIET_DEBUG
            if (enableDebug)
            {
                if (!InitializeD3D12DebugInfoQueue(driver))
                {
                    return nullptr;
                }
                InitializeD3D12DebugInfoLogger(driver);
            }
#endif

            // Check if UMA (unified memory architecture) is available. Used on APU i think ??
            D3D12_FEATURE_DATA_ARCHITECTURE architecture;
            architecture.NodeIndex = 0;
            result                 = driver->D3D12Device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, &architecture,
                                                                              sizeof(D3D12_FEATURE_DATA_ARCHITECTURE));
            if (FAILED(result))
            {
                DestroyDriver_Internal(driver);
                Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not get the device architecture");
                return nullptr;
            }
            driver->IsUMAAvailable     = architecture.UMA;
            driver->IsUMACacheCoherent = architecture.CacheCoherentUMA;

            // Check "GPU Upload Heap" support (for fast uniform buffers. Not supported on my 5700xt
            D3D12_FEATURE_DATA_D3D12_OPTIONS16 options16;
            driver->GPUUploadHeapSupported = false;
            result = driver->D3D12Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS16, &options16, sizeof(options16));
            if (SUCCEEDED(result))
            {
                driver->GPUUploadHeapSupported = options16.GPUUploadHeapSupported;
            }

            // Create bindless root signature
            driver->BindlessRootSignature = CreateGraphicsRootSignature(driver);

            // Command Queues
            // Graphics Queue only for now
            D3D12_COMMAND_QUEUE_DESC queueDesc = {};
            queueDesc.Flags                    = D3D12_COMMAND_QUEUE_FLAG_NONE;
            queueDesc.Type                     = D3D12_COMMAND_LIST_TYPE_DIRECT;
            queueDesc.NodeMask                 = 0;
            queueDesc.Priority                 = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
            result = driver->D3D12Device->CreateCommandQueue(&queueDesc, IID_ID3D12CommandQueue,
                                                             reinterpret_cast<void**>(&driver->GraphicsQueue));
            if (FAILED(result))
            {
                DestroyDriver_Internal(driver);
                Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create D3D12CommandQueue: Graphics");
                return nullptr;
            }
            driver->GraphicsQueue->SetName(L"GRAPHICS_QUEUE");
            driver->QueueDesc[ToUnderlying(QueueType::Graphics)] = queueDesc;

            queueDesc.Type                                      = D3D12_COMMAND_LIST_TYPE_COMPUTE;
            driver->QueueDesc[ToUnderlying(QueueType::Compute)] = queueDesc;

            queueDesc.Type                                   = D3D12_COMMAND_LIST_TYPE_COPY;
            driver->QueueDesc[ToUnderlying(QueueType::Copy)] = queueDesc;

            // Indirect Commands
            D3D12_COMMAND_SIGNATURE_DESC commandSignatureDesc;
            D3D12_INDIRECT_ARGUMENT_DESC indirectArgumentDesc;
            ZeroStruct(indirectArgumentDesc);

            indirectArgumentDesc.Type             = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;
            commandSignatureDesc.NodeMask         = 0;
            commandSignatureDesc.ByteStride       = sizeof(IndirectDrawCommand);
            commandSignatureDesc.NumArgumentDescs = 1;
            commandSignatureDesc.pArgumentDescs   = &indirectArgumentDesc;
            result = driver->D3D12Device->CreateCommandSignature(&commandSignatureDesc, nullptr, IID_ID3D12CommandSignature,
                                                                 reinterpret_cast<void**>(&driver->IndirectDrawCommandSignature));
            if (FAILED(result))
            {
                DestroyDriver_Internal(driver);
                Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create indirect draw command signature");
                return nullptr;
            }

            indirectArgumentDesc.Type           = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
            commandSignatureDesc.ByteStride     = sizeof(IndexedIndirectDrawCommand);
            commandSignatureDesc.pArgumentDescs = &indirectArgumentDesc;

            result = driver->D3D12Device->CreateCommandSignature(&commandSignatureDesc, nullptr, IID_ID3D12CommandSignature,
                                                                 reinterpret_cast<void**>(&driver->IndirectIndexedDrawCommandSignature));
            if (FAILED(result))
            {

                DestroyDriver_Internal(driver);
                Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create INDEXED Indirect draw command signature");
                return nullptr;
            }

            indirectArgumentDesc.Type           = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
            commandSignatureDesc.ByteStride     = sizeof(IndirectDispatchCommand);
            commandSignatureDesc.pArgumentDescs = &indirectArgumentDesc;

            result = driver->D3D12Device->CreateCommandSignature(&commandSignatureDesc, nullptr, IID_ID3D12CommandSignature,
                                                                 reinterpret_cast<void**>(&driver->IndirectDispatchCommandSignature));
            if (FAILED(result))
            {
                DestroyDriver_Internal(driver);
                Log(LogLevel::Error, LogCategory::Graphics, "DX12: Could not create Indirect dispatch command signature");
                return nullptr;
            }

            // Create Pools
            driver->SubmittedCommandListCapacity = 4;
            driver->SubmittedCommandListCount    = 0;
            driver->SubmittedCommandLists =
                static_cast<D3D12CommandList**>(Calloc(driver->SubmittedCommandListCapacity, sizeof(D3D12CommandList*)));
            if (!driver->SubmittedCommandLists)
            {
                DestroyDriver_Internal(driver);
                return nullptr;
            }

            driver->AvailableFenceCapacity = 4;
            driver->AvailableFenceCount    = 0;
            driver->AvailableFences = static_cast<D3D12Fence**>(Calloc(driver->AvailableFenceCapacity, sizeof(D3D12Fence*)));
            if (!driver->AvailableFences)
            {
                DestroyDriver_Internal(driver);
                return nullptr;
            }

            // Staging descriptor pools
            for (uint32 i = 0; i < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; i += 1)
            {
                driver->StagingDescriptorPools[i] =
                    Internal::CreateStagingDescriptorPool(driver, static_cast<D3D12_DESCRIPTOR_HEAP_TYPE>(i));

                if (driver->StagingDescriptorPools[i] == nullptr)
                {
                    DestroyDriver_Internal(driver);
                    return nullptr;
                }
            }

            CreateDescriptorHeapPool(driver, driver->SamplerHeapPool, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
                                     GPUDriver::kSampler_HeapDescriptorCount);

            // Deferred dispose vectors
            driver->GraphicsPipelinesToDisposeCapacity = 4;
            driver->GraphicsPipelinesToDisposeCount    = 0;
            driver->GraphicsPipelinesToDispose         = static_cast<D3D12GraphicsPipeline**>(
                Calloc(driver->GraphicsPipelinesToDisposeCapacity, sizeof(D3D12GraphicsPipeline*)));
            if (!driver->GraphicsPipelinesToDispose)
            {
                DestroyDriver_Internal(driver);
                return nullptr;
            }

            driver->Semantic       = WrapString("TEXCOORD");
            driver->FramesInFlight = 2;

            auto device = static_cast<GraphicsDevice*>(Calloc(1, sizeof(GraphicsDevice)));
            if (!device)
            {
                DestroyDriver_Internal(driver);
                return nullptr;
            }

            // Assign Functions to the device
            device->DestroyDevice                  = DestroyGraphicsDevice;
            device->AttachToWindow                 = AttachToWindow;
            device->DetachFromWindow               = DetachFromWindow;
            device->AcquireSwapChainTexture        = AcquireSwapChainTexture;
            device->WaitAndAcquireSwapChainTexture = WaitAndAcquireSwapChainTexture;
            device->GetSwapChainTextureFormat      = GetSwapChainTextureFormat;
            device->AcquireCommandList             = AcquireCommandList;
            device->SubmitCommandLists             = SubmitCommandLists;
            device->BeginRenderPass                = BeginRenderPass;
            device->EndRenderPass                  = EndRenderPass;
            device->SetViewPort                    = SetViewPort;
            device->SetScissorRect                 = SetScissorRect;
            device->SetBlendConstants              = SetBlendConstants;
            device->SetStencilReference            = SetStencilReference;
            device->BindGraphicsPipeline           = BindGraphicsPipeline;
            device->DrawPrimitives                 = DrawPrimitives;
            device->DrawIndexedPrimitives          = DrawIndexedPrimitives;
            device->SetIndexBuffer                 = SetIndexBuffer;
            device->WaitUntilGPUIsIdle             = WaitUntilGPUIsIdle;
            device->SetPushConstants               = SetPushConstants;
            device->QueryFence                     = QueryFence;
            device->ReleaseFence                   = ReleaseFence;
            device->CreateShader                   = CreateShader;
            device->DestroyShader                  = DestroyShader;
            device->CreateGraphicsPipeline         = CreateGraphicsPipeline;
            device->DestroyGraphicsPipeline        = DestroyGraphicsPipeline;
            device->CreateGraphicsBuffer         = CreateGraphicsBuffer;
            device->DestroyGraphicsBuffer        = DestroyGraphicsBuffer;
            device->MapGraphicsBuffer            = MapBuffer;
            device->UnmapGraphicsBuffer          = UnmapBuffer;
            device->CreateGraphicsTransferBuffer   = CreateGraphicsTransferBuffer;
            device->DestroyGraphicsTransferBuffer  = DestroyGraphicsTransferBuffer;
            device->MapGraphicsTransferBuffer      = MapBuffer;
            device->UnmapGraphicsTransferBuffer    = UnmapBuffer;
            device->CopyBuffer                     = CopyBuffer;
            device->CopyBufferToTexture            = CopyBufferToTexture;
            device->TransitionBufferToReadable     = TransitionBufferToReadable;
            device->GetDescriptorIndex             = GetDescriptorIndex;
            device->GetDescriptorIndexTexture      = GetDescriptorIndexTexture;
            device->CreateTexture                  = CreateTexture;
            device->DestroyTexture                 = DestroyTexture;

#if ALLOW_SHADER_HOT_RELOAD
            device->UpdateGraphicsPipelineShaders = UpdateGraphicsPipelineShaders;
#endif

            device->Driver       = driver;
            device->DebugEnabled = enableDebug;

            driver->GraphicsDevice = device;

            // Create Global Bindless Heap that stays alive for the driver whole lifetime
            driver->BindlessDescriptorHeap =
                Internal::CreateDescriptorHeap(driver, driver->DriverArena, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
                                               GPUDriver::kCBV_SRV_UAV_HeapDescriptorCount, false);

            return device;
        }
    } // namespace

    namespace Internal
    {
        void DisposePendingResourcces(NonNullPtr<D3D12Driver> driver)
        {
            // TODO Destroy anything (buffer, texture, etc...)
            uint32 idx = 0;
            while (idx < driver->GraphicsPipelinesToDisposeCount)
            {
                if (driver->GraphicsPipelinesToDispose[idx]->ReferenceCount == 0)
                {
                    ReleaseGraphicsPipeline(driver->GraphicsPipelinesToDispose[idx]);

                    driver->GraphicsPipelinesToDispose[idx] =
                        driver->GraphicsPipelinesToDispose[driver->GraphicsPipelinesToDisposeCount - 1];
                    driver->GraphicsPipelinesToDisposeCount -= 1;
                }
                else
                {
                    idx++;
                }
            }
        }
    } // namespace Internal
} // namespace Juliet::D3D12

namespace Juliet
{
    // clang-format off
    GraphicsDeviceFactory DX12DeviceFactory = {
        .Name="DirectX12",
        .Type=DriverType::DX12,
        .CheckDriver = D3D12::CheckDriver,
        .CreateGraphicsDevice = D3D12::CreateGraphicsDevice };
    // clang-format on
} // namespace Juliet
