// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#include <wsl/winadapter.h>

#include <iostream>
#include <directx/d3d12.h>
#include <directx/dxcore.h>
#include <directx/d3dx12.h>
#include "dxguids/dxguids.h"

// -----------------------------------------------------------------------------------------------------------------
// Helper Macros for verifying feature check results
// -----------------------------------------------------------------------------------------------------------------

// Verifies the correctness of a feature check that should not fail
#define VERIFY_FEATURE_CHECK_NO_DEFAULT(Feature) \
{ \
    if (features.Feature() != Data.Feature) \
    { \
        std::cout << "Verification failed: " << #Feature << std::endl \
                  << "Old API: " << Data.Feature << std::endl \
                  << "New API: " << features.Feature() << std::endl; \
        return -1; \
    } \
}

// Verifies the return value from a feature check with a different name from the old API that should not fail
#define VERIFY_RENAMED_FEATURE_CHECK_NO_DEFAULT(NewFeature, OldFeature) \
{ \
    if (features.NewFeature() != Data.OldFeature) \
    { \
        std::cout << "Verification failed: " << #NewFeature << std::endl \
                  << "Old API: " << Data.OldFeature << std::endl \
                  << "New API: " << features.NewFeature() << std::endl; \
        return -1; \
    } \
}

// Verifies if the feature check returns the correct default value upon failure
#define VERIFY_DEFAULT(Feature, Default) \
{ \
    if (features.Feature() != Default) \
    { \
        std::cout << "Verification failed: " << #Feature << std::endl \
                  << "Default: " << Default << std::endl \
                  << "Actual: " << features.Feature() << std::endl; \
        return -1; \
    } \
}

// Verifies the result of a feature check.
// If the feature check failed because it's not supported by runtime, ensures that it returns the default value
#define VERIFY_FEATURE_CHECK(Feature, Default) \
{ \
    if (FAILED(InitResult)) \
    { \
        VERIFY_DEFAULT(Feature, Default); \
    } \
    else \
    { \
        VERIFY_FEATURE_CHECK_NO_DEFAULT(Feature); \
    } \
}

// Verifies the result of a feature check. The requested capability may have a different name in the new API
// If the feature check failed because it's not supported by runtime, ensures that it returns the default value
#define VERIFY_RENAMED_FEATURE_CHECK(Feature, OldName, Default) \
{ \
    if (FAILED(InitResult)) \
    { \
        VERIFY_DEFAULT(Feature, Default); \
    } \
    else \
    { \
        VERIFY_RENAMED_FEATURE_CHECK_NO_DEFAULT(Feature, OldName); \
    } \
}

// Initialize the feature support data structure to be used in the original CheckFeatureSupport API
#define INITIALIZE_FEATURE_SUPPORT_DATA(FeatureName) \
HRESULT InitResult; \
if (FAILED(InitResult = device->CheckFeatureSupport(D3D12_FEATURE_##FeatureName, &Data, sizeof(D3D12_FEATURE_DATA_##FeatureName)))) \
{ \
    std::cout << "Feature not supported by device: " << #FeatureName << std::endl; \
}

// Prints out the mismatched feature check result to stdout
#define PRINT_CHECK_FAILED_MESSAGE(FeatureName, OldFeature, NewFeature) \
std::cout << "Verification failed: " << #FeatureName << std::endl \
          << "Old API: " << OldFeature << std::endl \
          << "New API: " << NewFeature << std::endl;


using namespace Microsoft::WRL;

// To get property value from adapter
std::vector<char> get_adapter_property(ComPtr<IDXCoreAdapter> adapter, DXCoreAdapterProperty property)
{
    if (adapter->IsPropertySupported(property))
    {
        size_t len;
        if(FAILED(adapter->GetPropertySize(property, &len)))
        {
            return {};
        }
        std::vector<char> buf(len);
        if(FAILED(adapter->GetProperty(property, len, buf.data())))
        {
            return {};
        }
        return buf;
    }
    return {};
}

// To get driver description for display before test
std::string get_driver_description(ComPtr<IDXCoreAdapter> adapter)
{
    auto rs = get_adapter_property(adapter, DXCoreAdapterProperty::DriverDescription);
    std::string name(rs.data());
    return name;
}

// -----------------------------------------------------------------------------------------------------------------
// Run tests on a specific device which is created from the input adapter
// -----------------------------------------------------------------------------------------------------------------
int run_per_adapter(IUnknown* adapter)
{
    ID3D12Device *device = nullptr;

    if (FAILED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device)))) 
    {
        return -1;
    }

    // Initialize the new FeatureSupport class
    // Notice that it is shared among all feature checks
    CD3DX12FeatureSupport features;
    if (FAILED(features.Init(device)))
    {
        return -1;
    }

    UINT NodeCount = device->GetNodeCount();

    // Each feature comes with two code sections
    // The first section shows how to use the API's of the new FeautreSupport class
    // The second section contains tests that ensures the new API returns the same result as the old API
    // 0: D3D12_OPTIONS
    {
        BOOL DoublePrecisionFloatShaderOps = features.DoublePrecisionFloatShaderOps();
        BOOL OutputMergerLogicOp = features.OutputMergerLogicOp();
        D3D12_TILED_RESOURCES_TIER TiledResourceTier = features.TiledResourcesTier();
        D3D12_RESOURCE_BINDING_TIER ResourceBindingTier = features.ResourceBindingTier();
        BOOL PSSpecifiedStencilRefSupported = features.PSSpecifiedStencilRefSupported();
        BOOL TypedUAVLoadAdditionalFormats = features.TypedUAVLoadAdditionalFormats();
        BOOL ROVsSupported = features.ROVsSupported();
        D3D12_CONSERVATIVE_RASTERIZATION_TIER ConservativeRasterizationTier = features.ConservativeRasterizationTier();
        BOOL StandardSwizzle64KBSupported = features.StandardSwizzle64KBSupported();
        BOOL CrossAdapterRowMajorTextureSupported = features.CrossAdapterRowMajorTextureSupported();
        BOOL VPAndRTArrayIndexFromAnyShaderFeedingRasterizerSupportedWithoutGSEmulation = features.VPAndRTArrayIndexFromAnyShaderFeedingRasterizerSupportedWithoutGSEmulation();
        D3D12_RESOURCE_HEAP_TIER ResourceHeapTier = features.ResourceHeapTier();
        D3D12_CROSS_NODE_SHARING_TIER CrossNodeSharingTier = features.CrossNodeSharingTier();
        UINT MaxGPUVirtualAddressBitsPerResource = features.MaxGPUVirtualAddressBitsPerResource();

        D3D12_FEATURE_DATA_D3D12_OPTIONS Data;
        device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &Data, sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS));
        VERIFY_FEATURE_CHECK_NO_DEFAULT(DoublePrecisionFloatShaderOps);
        VERIFY_FEATURE_CHECK_NO_DEFAULT(OutputMergerLogicOp);
        VERIFY_FEATURE_CHECK_NO_DEFAULT(TiledResourcesTier);
        VERIFY_FEATURE_CHECK_NO_DEFAULT(ResourceBindingTier);
        VERIFY_FEATURE_CHECK_NO_DEFAULT(PSSpecifiedStencilRefSupported);
        VERIFY_FEATURE_CHECK_NO_DEFAULT(TypedUAVLoadAdditionalFormats);
        VERIFY_FEATURE_CHECK_NO_DEFAULT(ROVsSupported);
        VERIFY_FEATURE_CHECK_NO_DEFAULT(ConservativeRasterizationTier);
        VERIFY_FEATURE_CHECK_NO_DEFAULT(StandardSwizzle64KBSupported);
        VERIFY_FEATURE_CHECK_NO_DEFAULT(CrossAdapterRowMajorTextureSupported);
        VERIFY_FEATURE_CHECK_NO_DEFAULT(VPAndRTArrayIndexFromAnyShaderFeedingRasterizerSupportedWithoutGSEmulation);
        VERIFY_FEATURE_CHECK_NO_DEFAULT(ResourceHeapTier);
        VERIFY_FEATURE_CHECK_NO_DEFAULT(CrossNodeSharingTier);
        VERIFY_FEATURE_CHECK_NO_DEFAULT(MaxGPUVirtualAddressBitsPerResource);
    }

    // 2: Feature Levels
    {
        D3D_FEATURE_LEVEL HighestLevelSupported = features.MaxSupportedFeatureLevel();
        
        D3D12_FEATURE_DATA_FEATURE_LEVELS Data;
        D3D_FEATURE_LEVEL KnownFeatureLevels[] =
        {
            D3D_FEATURE_LEVEL_1_0_CORE,
            D3D_FEATURE_LEVEL_9_1,
            D3D_FEATURE_LEVEL_9_2,
            D3D_FEATURE_LEVEL_9_3,
            D3D_FEATURE_LEVEL_10_0,
            D3D_FEATURE_LEVEL_10_1,
            D3D_FEATURE_LEVEL_11_0,
            D3D_FEATURE_LEVEL_11_1,
            D3D_FEATURE_LEVEL_12_0,
            D3D_FEATURE_LEVEL_12_1,
            D3D_FEATURE_LEVEL_12_2
        };
        Data.pFeatureLevelsRequested = KnownFeatureLevels;
        Data.NumFeatureLevels = sizeof(KnownFeatureLevels) / sizeof(D3D_FEATURE_LEVEL);
        device->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &Data, sizeof(D3D12_FEATURE_DATA_FEATURE_LEVELS));
        VERIFY_FEATURE_CHECK_NO_DEFAULT(MaxSupportedFeatureLevel);
    }

    // 3: Format Support
    {
        D3D12_FORMAT_SUPPORT1 Support1;
        D3D12_FORMAT_SUPPORT2 Support2;
        if (FAILED(features.FormatSupport(DXGI_FORMAT_R16G16B16A16_FLOAT, Support1, Support2))) {
            std::cout << "Error: FormatSupport failed" << std::endl;
            return -1;
        }

        D3D12_FEATURE_DATA_FORMAT_SUPPORT Data;
        Data.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
        if (SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &Data, sizeof(D3D12_FEATURE_DATA_FORMAT_SUPPORT)))) 
        {
            if (Support1 != Data.Support1)
            {
                std::cout << "Verification failed: " << "FormatSupport1" << std::endl 
                          << "Old API: " << Data.Support1 << std::endl 
                          << "New API: " << Support1 << std::endl; 
                return -1;
            }

            if (Support2 != Data.Support2)
            {
                std::cout << "Verification failed: " << "FormatSupport2" << std::endl 
                          << "Old API: " << Data.Support2 << std::endl 
                          << "New API: " << Support2 << std::endl; 
                return -1;
            }
        }
    }

    // 4: Multisample Quality Support
    {
        UINT NumQualityLevels;
        if (FAILED(features.MultisampleQualityLevels(DXGI_FORMAT_R16G16B16A16_FLOAT, 1, D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE, NumQualityLevels))) {
            std::cout << "Error: MultisampleQualityLevels failed" << std::endl;
            return -1;
        }

        D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS Data;
        Data.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
        Data.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
        Data.SampleCount = 1;
        device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &Data, sizeof(D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS));
        if (NumQualityLevels != Data.NumQualityLevels)
        {
            std::cout << "Verification failed: " << "MultisampleQualityLevels" << std::endl 
                        << "Old API: " << Data.NumQualityLevels << std::endl 
                        << "New API: " << NumQualityLevels << std::endl; 
            return -1;
        }
    }

    // 5: Format Info
    {
        UINT8 PlaneCount;
        if (FAILED(features.FormatInfo(DXGI_FORMAT_R16G16B16A16_FLOAT, PlaneCount))) 
        {
            std::cout << "Error: FormatInfo failed" << std::endl;
            return -1;
        }

        D3D12_FEATURE_DATA_FORMAT_INFO Data;
        Data.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
        device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_INFO, &Data, sizeof(D3D12_FEATURE_DATA_FORMAT_INFO));
        if (PlaneCount != Data.PlaneCount)
        {
            PRINT_CHECK_FAILED_MESSAGE(FormatInfo, Data.PlaneCount, PlaneCount);
        }
    }

    // 6: GPU Virtual Address Support
    {
        UINT MaxGPUVABitsPerProcess = features.MaxGPUVirtualAddressBitsPerProcess();
        UINT MaxGPUVABitsPerResource = features.MaxGPUVirtualAddressBitsPerResource();

        D3D12_FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORT Data;
        device->CheckFeatureSupport(D3D12_FEATURE_GPU_VIRTUAL_ADDRESS_SUPPORT, &Data, sizeof(D3D12_FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORT));
        VERIFY_FEATURE_CHECK_NO_DEFAULT(MaxGPUVirtualAddressBitsPerProcess);
        VERIFY_FEATURE_CHECK_NO_DEFAULT(MaxGPUVirtualAddressBitsPerResource);
    }

    // 7: Shader Model
    {
        D3D_SHADER_MODEL HighestShaderModel = features.HighestShaderModel();

        D3D12_FEATURE_DATA_SHADER_MODEL Data;
        D3D_SHADER_MODEL KnownShaderModels[] =
        {
            D3D_SHADER_MODEL_6_7,
            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
        };
        UINT NumKnownShaderModels = sizeof(KnownShaderModels) / sizeof(D3D_SHADER_MODEL);
        for (UINT i = 0; i < NumKnownShaderModels; i++)
        {
            Data.HighestShaderModel = D3D_SHADER_MODEL_6_7; // Highest Known Shader Model
            if (SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &Data, sizeof(D3D12_FEATURE_DATA_SHADER_MODEL)))) 
            {
                VERIFY_FEATURE_CHECK_NO_DEFAULT(HighestShaderModel);
            }
        }
    }
    
    // 8: Options1
    {
        BOOL WaveOps = features.WaveOps();
        UINT WaveLaneCountMin = features.WaveLaneCountMin();
        UINT WaveLaneCountMax = features.WaveLaneCountMax();

        D3D12_FEATURE_DATA_D3D12_OPTIONS1 Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(D3D12_OPTIONS1);
        VERIFY_FEATURE_CHECK(WaveOps, false);
        VERIFY_FEATURE_CHECK(WaveLaneCountMax, 0);
        VERIFY_FEATURE_CHECK(WaveLaneCountMin, 0);
    }

    // 10: Protected Resource Session Support
    for (UINT NodeId = 0; NodeId < NodeCount; NodeId++)
    {
        D3D12_PROTECTED_RESOURCE_SESSION_SUPPORT_FLAGS ProtectedResourceSessionSupport = features.ProtectedResourceSessionSupport(NodeId);

        D3D12_FEATURE_DATA_PROTECTED_RESOURCE_SESSION_SUPPORT Data;
        Data.NodeIndex = NodeId;
        INITIALIZE_FEATURE_SUPPORT_DATA(PROTECTED_RESOURCE_SESSION_SUPPORT);
        VERIFY_RENAMED_FEATURE_CHECK(ProtectedResourceSessionSupport, Support, D3D12_PROTECTED_RESOURCE_SESSION_SUPPORT_FLAG_NONE);
    }

    // 12: Root Signature
    {
        D3D_ROOT_SIGNATURE_VERSION HighestRootSignatureVersion = features.HighestRootSignatureVersion();

        D3D12_FEATURE_DATA_ROOT_SIGNATURE Data;
        Data.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_1; // Highest Known Version
        INITIALIZE_FEATURE_SUPPORT_DATA(ROOT_SIGNATURE);
        VERIFY_RENAMED_FEATURE_CHECK(HighestRootSignatureVersion, HighestVersion, (D3D_ROOT_SIGNATURE_VERSION)0);
    }

    // 16: Architecture1
    for (UINT NodeId = 0; NodeId < NodeCount; NodeId++)
    {
        BOOL IsolatedMMU = features.IsolatedMMU(NodeId);
        BOOL TileBasedRenderer = features.TileBasedRenderer(NodeId);
        BOOL UMA = features.UMA(NodeId);
        BOOL CacheCoherentUMA = features.CacheCoherentUMA(NodeId);

        D3D12_FEATURE_DATA_ARCHITECTURE1 Data;
        Data.NodeIndex = NodeId;
        INITIALIZE_FEATURE_SUPPORT_DATA(ARCHITECTURE1);
        if (FAILED(InitResult)) 
        {
            D3D12_FEATURE_DATA_ARCHITECTURE Data;
            Data.NodeIndex = NodeId;
            INITIALIZE_FEATURE_SUPPORT_DATA(ARCHITECTURE);
            VERIFY_FEATURE_CHECK_NO_DEFAULT(TileBasedRenderer);
            VERIFY_FEATURE_CHECK_NO_DEFAULT(UMA);
            VERIFY_FEATURE_CHECK_NO_DEFAULT(CacheCoherentUMA);
        }
        else
        {
            VERIFY_FEATURE_CHECK(TileBasedRenderer, false);
            VERIFY_FEATURE_CHECK(UMA, false);
            VERIFY_FEATURE_CHECK(CacheCoherentUMA, false);
        }
        VERIFY_FEATURE_CHECK(IsolatedMMU, false);
    }

    // 18: Options2
    {
        BOOL DepthBoundsTestSupported = features.DepthBoundsTestSupported();
        D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER ProgrammableSamplePositionsTier = features.ProgrammableSamplePositionsTier();

        D3D12_FEATURE_DATA_D3D12_OPTIONS2 Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(D3D12_OPTIONS2);
        VERIFY_FEATURE_CHECK(DepthBoundsTestSupported, false);
        VERIFY_FEATURE_CHECK(ProgrammableSamplePositionsTier, D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_NOT_SUPPORTED);
    }

    // 19: Shader Cache
    {
        D3D12_SHADER_CACHE_SUPPORT_FLAGS ShaderCacheSupportFlags = features.ShaderCacheSupportFlags();

        D3D12_FEATURE_DATA_SHADER_CACHE Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(SHADER_CACHE);
        VERIFY_RENAMED_FEATURE_CHECK(ShaderCacheSupportFlags, SupportFlags, D3D12_SHADER_CACHE_SUPPORT_NONE);
    }

    // 20: Command Queue Prioirity
    {
        // Basic check
        BOOL CommandQueuePrioritySupportedPositive = features.CommandQueuePrioritySupported(D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
        // Check for Global Realtime support
        BOOL CommandQueuePrioritySupportedRealtime = features.CommandQueuePrioritySupported(D3D12_COMMAND_LIST_TYPE_COPY, D3D12_COMMAND_QUEUE_PRIORITY_GLOBAL_REALTIME);
        // Test on invalid inputs
        BOOL CommandQueuePrioritySupportedInvalid = features.CommandQueuePrioritySupported((D3D12_COMMAND_LIST_TYPE)7, 0);

        D3D12_FEATURE_DATA_COMMAND_QUEUE_PRIORITY Data;
        {
            Data.CommandListType = D3D12_COMMAND_LIST_TYPE_DIRECT;
            Data.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
            INITIALIZE_FEATURE_SUPPORT_DATA(COMMAND_QUEUE_PRIORITY);
            if (CommandQueuePrioritySupportedPositive != Data.PriorityForTypeIsSupported)
            {
                PRINT_CHECK_FAILED_MESSAGE(CommandQueuePriority, Data.PriorityForTypeIsSupported, CommandQueuePrioritySupportedPositive);
            }
        }

        {
            Data.CommandListType = D3D12_COMMAND_LIST_TYPE_COPY;
            Data.Priority = D3D12_COMMAND_QUEUE_PRIORITY_GLOBAL_REALTIME;
            INITIALIZE_FEATURE_SUPPORT_DATA(COMMAND_QUEUE_PRIORITY);
            if (CommandQueuePrioritySupportedRealtime != Data.PriorityForTypeIsSupported)
            {
                PRINT_CHECK_FAILED_MESSAGE(CommandQueuePriority, Data.PriorityForTypeIsSupported, CommandQueuePrioritySupportedRealtime);
            }
        }

        if (CommandQueuePrioritySupportedInvalid)
        {
            std::cout << "Error: Wrong result from Command Queue Priority - Invalid Test Case\n"
                      << "New API returned true on invalid Command List Types\n"
                      << "Check if the list of command list types has updated\n";
            return -1;
        }
    }

    // 21: Options3
    {
        BOOL CopyQueueTimestampQueriesSupported = features.CopyQueueTimestampQueriesSupported();
        BOOL CastingFullyTypedFormatSupported = features.CastingFullyTypedFormatSupported();
        D3D12_COMMAND_LIST_SUPPORT_FLAGS WriteBufferImmediateSupportFlags = features.WriteBufferImmediateSupportFlags();
        D3D12_VIEW_INSTANCING_TIER ViewInstancingTier = features.ViewInstancingTier();
        BOOL BarycentricsSupported = features.BarycentricsSupported();

        D3D12_FEATURE_DATA_D3D12_OPTIONS3 Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(D3D12_OPTIONS3);
        VERIFY_FEATURE_CHECK(CopyQueueTimestampQueriesSupported, false);
        VERIFY_FEATURE_CHECK(CastingFullyTypedFormatSupported, false);
        VERIFY_FEATURE_CHECK(WriteBufferImmediateSupportFlags, D3D12_COMMAND_LIST_SUPPORT_FLAG_NONE);
        VERIFY_FEATURE_CHECK(ViewInstancingTier, D3D12_VIEW_INSTANCING_TIER_NOT_SUPPORTED);
        VERIFY_FEATURE_CHECK(BarycentricsSupported, false);
    }

    // 22: Existing Heaps
    {
        BOOL ExistingHeapsSupported = features.ExistingHeapsSupported();
        
        D3D12_FEATURE_DATA_EXISTING_HEAPS Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(EXISTING_HEAPS);
        VERIFY_RENAMED_FEATURE_CHECK(ExistingHeapsSupported, Supported, false);
    }

    // 23: D3D12 Options4
    {
        BOOL MSAA64KBAlignedTextureSupported = features.MSAA64KBAlignedTextureSupported();
        D3D12_SHARED_RESOURCE_COMPATIBILITY_TIER SharedResourceCompatibilityTier = features.SharedResourceCompatibilityTier();
        BOOL Native16BitShaderOpsSupported = features.Native16BitShaderOpsSupported();

        D3D12_FEATURE_DATA_D3D12_OPTIONS4 Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(D3D12_OPTIONS4);
        VERIFY_FEATURE_CHECK(MSAA64KBAlignedTextureSupported, false);
        VERIFY_FEATURE_CHECK(SharedResourceCompatibilityTier, D3D12_SHARED_RESOURCE_COMPATIBILITY_TIER_0);
        VERIFY_FEATURE_CHECK(Native16BitShaderOpsSupported, false);
    }

    // 24: Serialization
    {
        D3D12_HEAP_SERIALIZATION_TIER HeapSerializationTier = features.HeapSerializationTier();
        D3D12_FEATURE_DATA_SERIALIZATION Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(SERIALIZATION);
        VERIFY_FEATURE_CHECK(HeapSerializationTier, D3D12_HEAP_SERIALIZATION_TIER_0);
    }

    // 25: Cross Node
    {
        BOOL CrossNodeAtomicShaderInstructions = features.CrossNodeAtomicShaderInstructions();
        D3D12_FEATURE_DATA_CROSS_NODE Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(CROSS_NODE);
        VERIFY_RENAMED_FEATURE_CHECK(CrossNodeAtomicShaderInstructions, AtomicShaderInstructions, false);
    }

    // 27: Options5
    {
        BOOL SRVOnlyTiledResourceTier3 = features.SRVOnlyTiledResourceTier3();
        D3D12_RENDER_PASS_TIER RenderPassesTier = features.RenderPassesTier();
        D3D12_RAYTRACING_TIER RaytracingTier = features.RaytracingTier();

        D3D12_FEATURE_DATA_D3D12_OPTIONS5 Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(D3D12_OPTIONS5);
        VERIFY_FEATURE_CHECK(SRVOnlyTiledResourceTier3, false);
        VERIFY_FEATURE_CHECK(RenderPassesTier, D3D12_RENDER_PASS_TIER_0);
        VERIFY_FEATURE_CHECK(RaytracingTier, D3D12_RAYTRACING_TIER_NOT_SUPPORTED);
    }

    // 28: Displayable
    {
        BOOL DisplayableTexture = features.DisplayableTexture();

        D3D12_FEATURE_DATA_DISPLAYABLE Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(DISPLAYABLE);
        VERIFY_FEATURE_CHECK(DisplayableTexture, false);
    }

    // 30: Options6
    {
        BOOL AdditionalShadingRatesSupported = features.AdditionalShadingRatesSupported();
        BOOL PerPrimitiveShadingRateSupportedWithViewportIndexing = features.PerPrimitiveShadingRateSupportedWithViewportIndexing();
        D3D12_VARIABLE_SHADING_RATE_TIER VariableShadingRateTier = features.VariableShadingRateTier();
        BOOL ShadingRateImageTileSize = features.ShadingRateImageTileSize();
        BOOL BackgroundProcessingSupported = features.BackgroundProcessingSupported();

        D3D12_FEATURE_DATA_D3D12_OPTIONS6 Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(D3D12_OPTIONS6);
        VERIFY_FEATURE_CHECK(AdditionalShadingRatesSupported, false);
        VERIFY_FEATURE_CHECK(PerPrimitiveShadingRateSupportedWithViewportIndexing, false);
        VERIFY_FEATURE_CHECK(VariableShadingRateTier, D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED);
        VERIFY_FEATURE_CHECK(ShadingRateImageTileSize, false);
        VERIFY_FEATURE_CHECK(BackgroundProcessingSupported, false);
    }

    // 31: Query Metacommand
    // The function is simply a forwarding call to the old API
    // Takes in a pointer to D3D12_FEATURE_DATA_QUERY_METACOMMAND as input parameter
    // No need to test specifically

    // 32: Options7
    {
        D3D12_MESH_SHADER_TIER MeshShaderTier = features.MeshShaderTier();
        D3D12_SAMPLER_FEEDBACK_TIER SamplerFeedbackTier = features.SamplerFeedbackTier();

        D3D12_FEATURE_DATA_D3D12_OPTIONS7 Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(D3D12_OPTIONS7);
        VERIFY_FEATURE_CHECK(MeshShaderTier, D3D12_MESH_SHADER_TIER_NOT_SUPPORTED);
        VERIFY_FEATURE_CHECK(SamplerFeedbackTier, D3D12_SAMPLER_FEEDBACK_TIER_NOT_SUPPORTED);
    }

    // 33: Session Type Count
    for (UINT NodeId = 0; NodeId < NodeCount; NodeId++)
    {
        UINT ProtectedResourceSessionTypeCount = features.ProtectedResourceSessionTypeCount(NodeId);

        D3D12_FEATURE_DATA_PROTECTED_RESOURCE_SESSION_TYPE_COUNT Data;
        Data.NodeIndex = NodeId;
        INITIALIZE_FEATURE_SUPPORT_DATA(PROTECTED_RESOURCE_SESSION_TYPE_COUNT);
        VERIFY_RENAMED_FEATURE_CHECK(ProtectedResourceSessionTypeCount, Count, 0);
    }
    
    // 34: Session Types
    for (UINT NodeId = 0; NodeId < NodeCount; NodeId++)
    {
        std::vector<GUID> ProtectedResourceSessionTypes = features.ProtectedResourceSessionTypes(NodeId);

        D3D12_FEATURE_DATA_PROTECTED_RESOURCE_SESSION_TYPE_COUNT CountData;
        CountData.NodeIndex = NodeId;
        if (FAILED(device->CheckFeatureSupport(D3D12_FEATURE_PROTECTED_RESOURCE_SESSION_TYPE_COUNT, &CountData, sizeof(D3D12_FEATURE_DATA_PROTECTED_RESOURCE_SESSION_TYPE_COUNT))))
        {
            if (ProtectedResourceSessionTypes.size() != 0)
            {
                std::cout << "Error: Wrong result from Protected Resource Session Types on Node " << NodeId << std::endl
                          << "Type count check failed but types vector is not empty\n";
                return -1;
            }
            continue;
        }
        
        if (CountData.Count != ProtectedResourceSessionTypes.size())
        {
            std::cout << "Error: Wrong result from Protected Resource Session Types on Node " << NodeId << std::endl
                      << "Types vector size does not equal type count\n";
            return -1;
        }

        D3D12_FEATURE_DATA_PROTECTED_RESOURCE_SESSION_TYPES Data;
        Data.NodeIndex = NodeId;
        Data.Count = CountData.Count;
        Data.pTypes = new GUID[Data.Count];
        INITIALIZE_FEATURE_SUPPORT_DATA(PROTECTED_RESOURCE_SESSION_TYPES);
        if (SUCCEEDED(InitResult))
        {
            for (unsigned int i = 0; i < Data.Count; i++)
            {
                if (ProtectedResourceSessionTypes[i] != Data.pTypes[i])
                {
                    std::cout << "Error: Result mismatch from Proteted Resource Session Types\n"
                              << "Node Index: " << NodeId << std::endl
                              << "Type Index: " << i << std::endl;
                    return -1;
                }
            }
        }
    }

    // 36: Options8
    {
        BOOL UnalignedBlockTexturesSupported = features.UnalignedBlockTexturesSupported();

        D3D12_FEATURE_DATA_D3D12_OPTIONS8 Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(D3D12_OPTIONS8);
        VERIFY_FEATURE_CHECK(UnalignedBlockTexturesSupported, false);
    }

    // 37: Options9
    {
        BOOL MeshShaderPipelineStatsSupported = features.MeshShaderPipelineStatsSupported();
        BOOL MeshShaderSupportsFullRangeRenderTargetArrayIndex = features.MeshShaderSupportsFullRangeRenderTargetArrayIndex();
        D3D12_WAVE_MMA_TIER WaveMMATier = features.WaveMMATier();

        D3D12_FEATURE_DATA_D3D12_OPTIONS9 Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(D3D12_OPTIONS9);
        VERIFY_FEATURE_CHECK(MeshShaderPipelineStatsSupported, false);
        VERIFY_FEATURE_CHECK(MeshShaderSupportsFullRangeRenderTargetArrayIndex, false);
        VERIFY_FEATURE_CHECK(WaveMMATier, D3D12_WAVE_MMA_TIER_NOT_SUPPORTED);
    }

    // 39: Options10
    {
        BOOL VariableRateShadingSumCombinerSupported = features.VariableRateShadingSumCombinerSupported();
        BOOL MeshShaderPerPrimitiveShadingRateSupported = features.MeshShaderPerPrimitiveShadingRateSupported();

        D3D12_FEATURE_DATA_D3D12_OPTIONS10 Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(D3D12_OPTIONS10);
        VERIFY_FEATURE_CHECK(VariableRateShadingSumCombinerSupported, false);
        VERIFY_FEATURE_CHECK(MeshShaderPerPrimitiveShadingRateSupported, false);
    }

    // 40: Options11
    {
        BOOL AtomicInt64OnDescriptorHeapResourceSupported = features.AtomicInt64OnDescriptorHeapResourceSupported();

        D3D12_FEATURE_DATA_D3D12_OPTIONS11 Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(D3D12_OPTIONS11);
        VERIFY_FEATURE_CHECK(AtomicInt64OnDescriptorHeapResourceSupported, false);
    }

    // 41: Options12
    {
        D3D12_TRI_STATE MSPrimitivesPipelineStatisticIncludesCulledPrimitives = features.MSPrimitivesPipelineStatisticIncludesCulledPrimitives();
        BOOL EnhancedBarriersSupported = features.EnhancedBarriersSupported();

        D3D12_FEATURE_DATA_D3D12_OPTIONS12 Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(D3D12_OPTIONS12);
        VERIFY_FEATURE_CHECK(MSPrimitivesPipelineStatisticIncludesCulledPrimitives, D3D12_TRI_STATE_UNKNOWN);
        VERIFY_FEATURE_CHECK(EnhancedBarriersSupported, false);
        VERIFY_FEATURE_CHECK(RelaxedFormatCastingSupported, false);
    }

    // 41: Options13
    {
        D3D12_FEATURE_DATA_D3D12_OPTIONS13 Data;
        INITIALIZE_FEATURE_SUPPORT_DATA(D3D12_OPTIONS13);
        VERIFY_FEATURE_CHECK(UnrestrictedBufferTextureCopyPitchSupported, false);
        VERIFY_FEATURE_CHECK(UnrestrictedVertexElementAlignmentSupported, false);
        VERIFY_FEATURE_CHECK(InvertedViewportHeightFlipsYSupported, false);
        VERIFY_FEATURE_CHECK(InvertedViewportDepthFlipsZSupported, false);
        VERIFY_FEATURE_CHECK(TextureCopyBetweenDimensionsSupported, false);
        VERIFY_FEATURE_CHECK(AlphaBlendFactorSupported, false);
    }

    std::cout << "Test completed with no errors." << std::endl;
    return 0;
}

#ifdef __MINGW32__
STDAPI
DXCoreCreateAdapterFactory(
    REFIID riid,
    _COM_Outptr_ void** ppvFactory
) {
    return 0;
}
#endif

int main()
{
    ComPtr<IDXCoreAdapterFactory> adapter_factory;
    ComPtr<IDXCoreAdapterList> adapter_list;
    GUID dx_must_attr[1]{ DXCORE_ADAPTER_ATTRIBUTE_D3D12_CORE_COMPUTE };

    if(FAILED(DXCoreCreateAdapterFactory(IID_PPV_ARGS(&adapter_factory))))
    {
        return -1;
    }

    if(FAILED(adapter_factory->CreateAdapterList(_countof(dx_must_attr), dx_must_attr, IID_PPV_ARGS(&adapter_list))))
    {
        return -1;
    }

    // Test all adapters
    for (uint32_t i = 0; i < adapter_list->GetAdapterCount(); i++)
    {
        ComPtr<IDXCoreAdapter> adapter;
        if(FAILED(adapter_list->GetAdapter(i, IID_PPV_ARGS(&adapter))))
        {
            std::cout << "Cannot get number " << i << " adapter." << std::endl;
            continue;
        }

        std::string driver_desc_str = get_driver_description(adapter);
        if(driver_desc_str.empty())
        {
            std::cout << "Cannot get number " << i << " adapter's driver description." << std::endl;
        }
        std::cout << "Test on device driver: " << driver_desc_str << std::endl;

        if(FAILED(run_per_adapter(adapter.Get())))
        {
            std::cout << "Some tests failed with error on number " << i << " adapter." << std::endl;
        }
    }
    return 0;
}