{"id":4950,"date":"2021-08-10T16:46:27","date_gmt":"2021-08-10T23:46:27","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/directx\/?p=4950"},"modified":"2021-08-10T17:03:53","modified_gmt":"2021-08-11T00:03:53","slug":"introducing-a-new-api-for-checking-feature-support-in-direct3d-12","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/directx\/introducing-a-new-api-for-checking-feature-support-in-direct3d-12\/","title":{"rendered":"Introducing a New API for Checking Feature Support in Direct3D 12"},"content":{"rendered":"<h3>Background<\/h3>\n<p>Direct3D can perform a lot of different operations. We use \u201ccapability\u201d or \u201ccap\u201d to describe what the library can do. Direct3D 12 has a single API, <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/d3d12\/nf-d3d12-id3d12device-checkfeaturesupport\">ID3D12Device::CheckFeatureSupport<\/a>, which tells us whether a cap is supported and its level of support in the runtime environment. Here&#8217;s the function signature:<\/p>\n<pre>HRESULT CheckFeatureSupport(\r\n    D3D12_FEATURE Feature,\r\n    void *pFeatureSupportData,\r\n    UINT FeatureSupportDataSize\r\n);\r\n\r\n<\/pre>\n<p>To make use of this API, you must provide a D3D12_FEATURE enum to identify the feature group you&#8217;re querying. You must also provide a pointer to one of the D3D12_FEATURE_DATA_* data structures specific to that feature and its size.<\/p>\n<p>You may find this API hard to use for the following reasons:<\/p>\n<ul>\n<li>Unless you memorize the cap locations, you must first look up which feature group contains the caps. Making things worse, <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/d3d12\/ne-d3d12-d3d12_feature\">the documentation<\/a> is indexed by features, not caps. It feels like using the value to find the key in a dictionary.<\/li>\n<li>The call routine is complicated. After determining the feature, you have to create an empty feature data struct and initialize its input fields and arrays (if any). You then call the API and check if the return code is S_OK. Finally, you read the cap information from the feature data struct.<\/li>\n<li>If two caps belong to two different feature groups, you\u2019ll need to repeat the whole process again.<\/li>\n<li>The query process for some features, for example FEATURE_LEVELS is not intuitive.<\/li>\n<\/ul>\n<h3>Solution: The new \u201cCD3DX12FeatureSupport\u201d class<\/h3>\n<p>To improve your experience with feature checking, we have added a wrapper API with a simpler call routine and more friendly interface. Actually, it\u2019s not a single API but an entire class. Allow me to introduce the new \u201cCD3DX12FeatureSupport\u201d class, located in <a href=\"https:\/\/github.com\/microsoft\/DirectX-Headers\/blob\/main\/include\/directx\/d3dx12.h#L4041-L4186\">d3dx12.h<\/a> and available from the <a href=\"https:\/\/github.com\/microsoft\/DirectX-Headers\">DirectX-Headers repo on GitHub<\/a>.<\/p>\n<p>It\u2019s hard to explain why this class is better the original API. Let\u2019s first walk through two examples.<\/p>\n<h4>Case 1: Multiple queries from different feature groups<\/h4>\n<p>In the first example, we\u2019d like to check three different caps: \u201cROVs Supported\u201d, \u201cWave Ops\u201d, and Raytracing Tier. With the old API, we must figure out which features include these three caps. After looking up from the documentation, we found that they come from three different feature groups: D3D12_OPTIONS, D3D12_OPTIONS1, and D3D12_OPTIONS5. This is bad news since we\u2019ll need to create three separate \u201cD3D12_FEATURE_DATA\u201d structs. What\u2019s worse, it\u2019s very easy to mistake one feature for another because all three have similar names. The final code looks like this:<\/p>\n<pre>\/\/ Old API\r\n\r\nD3D12_FEATURE_DATA_D3D12_OPTIONS dOptions;\r\ndevice-&gt;CheckFeatureSupport(\r\n    D3D12_FEATURE_D3D12_OPTIONS,\r\n    &amp;dOptions,\r\n    sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS)\r\n);\r\nBOOL ROVsSupported = dOptions.ROVsSupported;\r\n\r\nD3D12_FEATURE_DATA_D3D12_OPTIONS1 dOptions1;\r\ndevice-&gt;CheckFeatureSupport(\r\n    D3D12_FEATURE_D3D12_OPTIONS1,\r\n    &amp;dOptions1,\r\n    sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS1)\r\n);\r\nBOOL WaveOps = dOptions1.WaveOps;\r\n\r\nD3D12_FEATURE_DATA_D3D12_OPTIONS5 dOptions5;\r\ndevice-&gt;CheckFeatureSupport(\r\n    D3D12_FEATURE_D3D12_OPTIONS5,\r\n    &amp;dOptions5,\r\n    sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS5)\r\n);\r\nD3D12_RAYTRACING_TIER RaytracingTier = dOptions5.RaytracingTier;\r\n\r\n<\/pre>\n<p>Also notice that the API calls above are not safe. If this code is running on a very old version of D3D12 runtime, some of these checks may fail and the data fields may be invalid. You should enclose the \u201cCheckFeatureSupport\u201d calls in a \u201cFAILED()\u201d macro to determine whether the check succeeds.<\/p>\n<p>Now let\u2019s see how it\u2019s done with the new API:<\/p>\n<pre>\/\/ New API\r\n\r\nCD3DX12FeatureSupport features;\r\nfeatures.Init(device);\r\nBOOL ROVsSupported = features.ROVsSupported();\r\nBOOL WaveOps = features.WaveOps();\r\nD3D12_RAYTRACING_TIER RaytracingTier = features.RaytracingTier();<\/pre>\n<p>Immediately you\u2019ll find the whole process a lot easier. There\u2019s no need to set up multiple instances to check different features, nor do you need to reinitialize between cap query calls. Plus, you don\u2019t need to find out which feature groups contain the caps you\u2019d like to query about. With a single instance of CD3DX12FeatureSupport and one single initialization, you can query any cap directly by calling a function of the same name.<\/p>\n<p>What if an old version of D3D12 runtime doesn\u2019t recognize a feature group and the feature check failed? The cap function will return a default \u201cunsupported\u201d value, which is usually 0 or false. It makes sense since the D3D12 runtime cannot support a cap or feature unless it knows about it.<\/p>\n<h4>Case 2: Querying the more complex FEATURE_LEVELS<\/h4>\n<p>In the second example we deal with FEATURE_LEVELS, which used to have a complicated check routine. With the old API, you need to pass in an array of \u201cD3D_FEATURE_LEVEL\u201d enums, along with the size of that array. The API then picks the highest supported level in the current runtime from the array.<\/p>\n<pre>\/\/ Old API\r\n\r\nD3D12_FEATURE_DATA_FEATURE_LEVELS dLevels;\r\nD3D_FEATURE_LEVEL aLevels [] =\r\n{\r\n    D3D_FEATURE_LEVEL_12_2,\r\n    D3D_FEATURE_LEVEL_12_1,\r\n    D3D_FEATURE_LEVEL_12_0,\r\n    ...\r\n};\r\ndLevels.NumFeatureLevels = sizeof(aLevels) \/ sizeof(D3D_FEATURE_LEVEL);\r\ndLevels.pFeatureLevelsRequested = aLevels;\r\ndevice-&gt;CheckFeatureSupport(\r\n    D3D12_FEATURE_FEATURE_LEVELS,\r\n    &amp;dLevels,\r\n    sizeof(D3D12_FEATURE_DATA_FEATURE_LEVELS)\r\n);\r\nD3D_FEATURE_LEVEL MaxFeatureLevel = dLevels.MaxSupportedFeatureLevel;\r\n\r\n<\/pre>\n<p>That works but sounds counter-intuitive. We know the feature level enums are listed in increasing order where a newer level has a larger enum. We also know that the feature levels are backwards compatible. So why don\u2019t we just obtain the feature level of the runtime and compare with what we want? The new API allows you to do exactly that:<\/p>\n<pre>\/\/ New API\r\n\r\nCD3DX12FeatureSupport features;\r\nfeatures.Init(device);\r\nD3D_FEATURE_LEVEL MaxFeatureLevel = features.MaxSupportedFeatureLevel();\r\n\r\n<\/pre>\n<h4>Summary:<\/h4>\n<p>The above examples illustrated the key characteristics of the new CD3DX12FeatureSupport class:<\/p>\n<ul>\n<li>Single entry point. The new API class can query any cap from any feature group. No need to use separate feature data structs for different caps.<\/li>\n<li>Simpler call routine. After a single initialization, you may query any cap by calling a member function of the same name and read the result from the returned values.<\/li>\n<li>Multiple use. You can query multiple caps from one instance of CD3DX12FeatureSupport without setting up a new instance or re-initializing.<\/li>\n<li>Failure protection. If a cap query failed because the runtime doesn\u2019t recognize its feature enum (probably due to an old D3D12 runtime), the function returns a default value, usually 0 or false, instead failing. There\u2019s no need to capture error codes or exceptions from the query APIs.<\/li>\n<\/ul>\n<h3>Usage and Resources<\/h3>\n<p>We\u2019ve demonstrated the general steps for using the new API:<\/p>\n<ol>\n<li>Include the \u201cd3dx12.h\u201d header. (See the <a href=\"https:\/\/github.com\/microsoft\/DirectX-Headers\">DirectX-Headers GitHub repo<\/a>)<\/li>\n<li>Create a CD3DX12FeatureSupport instance through the default constructor.<\/li>\n<li>Call \u201cInit()\u201d on the instance to bind it to a device. If the initialization succeeds, the function returns \u201cS_OK\u201d. Otherwise, it returns the error code from the last fatal initialization failure. We recommend you enclose this function call with the \u201cFAILED\u201d macro.<\/li>\n<li>For each cap you\u2019d like to query, call the member function using its name. If a feature check requires inputs, you can pass them in as function arguments.<\/li>\n<\/ol>\n<p>There are some special cases. Some caps are renamed to be more distinguishable in the new API class (for example, \u201cSupported\u201d from \u201cD3D12_FEATURE_DATA_EXISTING_HEAPS\u201d becomes \u201cExistingHeapsSupported()\u201d). Other cap query functions may have different signatures due to their complicated in\/out patterns.<\/p>\n<p>Before the official documentation comes out, we have some resources to help you get started with this new API. Since the CD3DX12FeatureSupport class is part of the open-source <a href=\"https:\/\/github.com\/microsoft\/DirectX-Headers\">DirectX-Headers repo<\/a> available on GitHub, you have access to all its definition and implementation. This can give you a clear idea on how the new API is implemented and what it\u2019s doing behind the scenes. We\u2019ve also provided a program in the same repo (<a href=\"https:\/\/github.com\/microsoft\/DirectX-Headers\/blob\/main\/test\/feature_check_test.cpp\">test\/feature_check_test.cpp<\/a>) that contains examples of how to query each cap with the new APIs. It also comes with correctness checks; you can compile and run it to make sure the new API returns the same results as the old API.<\/p>\n<p>We hope this new API provides a better experience for checking feature support in Direct3D!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Background Direct3D can perform a lot of different operations. We use \u201ccapability\u201d or \u201ccap\u201d to describe what the library can do. Direct3D 12 has a single API, ID3D12Device::CheckFeatureSupport, which tells us whether a cap is supported and its level of support in the runtime environment. Here&#8217;s the function signature: HRESULT CheckFeatureSupport( D3D12_FEATURE Feature, void *pFeatureSupportData, [&hellip;]<\/p>\n","protected":false},"author":67959,"featured_media":12651,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-4950","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-directx"],"acf":[],"blog_post_summary":"<p>Background Direct3D can perform a lot of different operations. We use \u201ccapability\u201d or \u201ccap\u201d to describe what the library can do. Direct3D 12 has a single API, ID3D12Device::CheckFeatureSupport, which tells us whether a cap is supported and its level of support in the runtime environment. Here&#8217;s the function signature: HRESULT CheckFeatureSupport( D3D12_FEATURE Feature, void *pFeatureSupportData, [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/directx\/wp-json\/wp\/v2\/posts\/4950","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/directx\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/directx\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/directx\/wp-json\/wp\/v2\/users\/67959"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/directx\/wp-json\/wp\/v2\/comments?post=4950"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/directx\/wp-json\/wp\/v2\/posts\/4950\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/directx\/wp-json\/wp\/v2\/media\/12651"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/directx\/wp-json\/wp\/v2\/media?parent=4950"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/directx\/wp-json\/wp\/v2\/categories?post=4950"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/directx\/wp-json\/wp\/v2\/tags?post=4950"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}