{"id":8383,"date":"2023-08-14T13:00:33","date_gmt":"2023-08-14T20:00:33","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/directx\/?p=8383"},"modified":"2023-08-14T13:14:11","modified_gmt":"2023-08-14T20:14:11","slug":"hlsl-2021-migration-guide","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/directx\/hlsl-2021-migration-guide\/","title":{"rendered":"HLSL 2021 Migration Guide"},"content":{"rendered":"<p>The DirectX Shader Compiler Summer 2023 release is <a href=\"https:\/\/github.com\/microsoft\/DirectXShaderCompiler\/releases\/tag\/v1.7.2308\">live<\/a>! The most significant change in this release is the enabling of the HLSL 2021 language version by default. HLSL 2021 includes templates, operator overloads, bitfields, short-circuiting, and modernizations to loop variable scope and implicit struct casting. You can read about the details of these features in <a href=\"https:\/\/devblogs.microsoft.com\/directx\/announcing-hlsl-2021\/\">Chris Bieneman&#8217;s dog-themed blog post<\/a>.<\/p>\n<p>This post documents the gotchas that you might encounter when compiling your existing shaders with HLSL 2021 to benefit from all this goodness. Specifically, it will focus on:<\/p>\n<ul>\n<li>Changes to implicit casting that affect assignments and how overloaded functions are matched to calls.<\/li>\n<li>Changes to binary logic operators disallowing vectors and introducing short-circuiting to scalars.<\/li>\n<li>Changes to the scope of loop variables that will prevent using them outside the loop.<\/li>\n<\/ul>\n<p>To explain these changes and how they might create specific scenarios that impact your shaders, we have provided:<\/p>\n<ul>\n<li><strong>Example code<\/strong> to illustrate what kind of pattern might have caused the problem.<\/li>\n<li><strong>Background<\/strong> about the change to the language in HLSL 2021 that caused them.<\/li>\n<li><strong>Quick fixes<\/strong> to get your shader compiling and running as it did before (for better or worse).<\/li>\n<li><strong>Potential lurking bugs<\/strong> that these failures might indicate which might make you reconsider the quick fix.<\/li>\n<li><strong>Ideal fixes<\/strong> if you want to spend the time.<\/li>\n<\/ul>\n<p>Each scenario is meant to be self-contained so you can select the ones that apply to you and get all the information you need. However, you can feel free to read through the whole document as well and learn all there is to know about migrating to HLSL 2021.<\/p>\n<h3>My shaders compile successfully! Hooray!<\/h3>\n<h4>Hang on! Double check your loop variable scoping and usage.<\/h4>\n<p>Congrats! Nevertheless, there is something you should try to ensure you don&#8217;t see any subtle behavior differences. You might have problems with using a loop variable that shadows another and uses it after the loop like this code does:<\/p>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">bool<\/span> allPositive(float arr[3]) {<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">  uint<\/span> i = 3;<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">  for (<span style=\"color: #3366ff;\">uint<\/span> i = 0; i &lt; 3; i++) {<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">    if (arr[i]&lt; 0.0)<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">      break<\/span>;<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">  }<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">  if (i &gt;= 3) <span style=\"color: #008000;\">\/\/ Wait a minute. . . which i is that?<\/span> <\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">    return<\/span> false;<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">  return<\/span> true;<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">}<\/span><\/pre>\n<h5>The background<\/h5>\n<p>HLSL 2021 limits the scope of loop variables to the body of the loop itself. Previously, it conformed to an older language standard which extended the scope of such variables to the scope the loop was contained in. Because of the new limited scope, any usage of the variables outside the loop will refer only to variables in the outer scope, which is a change in behavior.<\/p>\n<h5>The ideal fix<\/h5>\n<p>In previous versions of HLSL, loop variables that shadowed outer scope variables produced a warning: <em>&#8220;redefinition of &#8216;i&#8217; shadows declaration in the outer scope; most recent declaration will be used&#8221;<\/em>. It may be worth it to enable HLSL 2018 with the <em>-HV 2018<\/em> flag and carefully examine such warnings to ensure that the change in scope won&#8217;t change the shader result. Alternately, you might enable all shadow warnings with\u00a0<em>-Wshadow<\/em> and examine them all. There may be even more, but they may indicate other lurking bugs.<\/p>\n<p>&nbsp;<\/p>\n<h3>My struct assignment is failing!<\/h3>\n<h4>error: cannot implicitly convert from &lt;struct1&gt; to &lt;struct2&gt;<\/h4>\n<p>You are trying to assign a variable of a different type to another variable with identical internal layout like this code does:<\/p>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">struct<\/span> color { <span style=\"color: #3366ff;\">float<\/span> r, g, b, a; };<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">struct<\/span> vec4 {<span style=\"color: #3366ff;\">float<\/span> x, y, z, w; };<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">color<\/span> blue = { <span style=\"color: #99cc00;\">0.0<\/span>, <span style=\"color: #99cc00;\">0.0<\/span>, <span style=\"color: #99cc00;\">1.0<\/span>, <span style=\"color: #99cc00;\">1.0<\/span> }; <span style=\"color: #008000;\">\/\/ my favorite color!<\/span><\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">vec4<\/span> vtxinfo[<span style=\"color: #99cc00;\">2<\/span>]; <span style=\"color: #008000;\">\/\/ color and position info in one!<\/span><\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">vtxinfo[<span style=\"color: #99cc00;\">0<\/span>] = blue; <span style=\"color: #008000;\">\/\/ error: cannot implicitly convert from 'color' to 'vec4' doh!<\/span><\/span><\/pre>\n<h5>The background<\/h5>\n<p>You are running into a change in implicit casting. Previously, structs were considered to match for the purposes of implicit casts if their members had the same types in the same places.<\/p>\n<p>In HLSL 2018, this assignment would have copied the internal values of the <em>blue color<\/em> value into the generic <em>vec4<\/em> struct. In HLSL 2021, the names of the struct are also required to match similar to C++ and other languages.<\/p>\n<h5>The quick fix<\/h5>\n<p>You can still cast one compatible struct to another. Any structs that run into this error are definitely compatible, so you can just add a cast to the failing assignment:<\/p>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\">vtxinfo[<span style=\"color: #99cc00;\">0<\/span>] = (<span style=\"color: #3366ff;\">vec4<\/span>)blue; <span style=\"color: #008000;\">\/\/ No errors!<\/span><\/span><\/pre>\n<h5>The potential lurking bug<\/h5>\n<p>The situation above showed where this was clearly intended. There may be cases where this shows a mistaken assignment of two values that aren&#8217;t meant to have the same types. Examining the code carefully with the knowledge of its context might be necessary to ensure that this assignment is really what you want. Different types with the same internal representations are meant to prevent mistakes. HLSL 2021 gives the ability to identify these mistakes.<\/p>\n<h5>The (possibly) ideal fix<\/h5>\n<p>In the situation above, you might choose to replace the array of vec4s with a struct containing both color and position information. If you were to actually write this shader, you would probably want to use float4s. The structs containing only a list of floats were used to make a convenient example.<\/p>\n<h3>My function call can&#8217;t find its implementation!<\/h3>\n<h4>No matching function for call to &lt;myfunction&gt;<\/h4>\n<p>You have a function call that passes a different, but internally identical struct to the defined function like this code does:<\/p>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">struct <\/span>LinearRGB {<span style=\"color: #3366ff;\">float3 <\/span>RGB;};<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">struct <\/span>LinearYCoCg {<span style=\"color: #3366ff;\">float3<\/span> YCoCg;};<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">void<\/span> InitColor(inout <span style=\"color: #3366ff;\">LinearRGB<\/span> V) {<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"> \u00a0\u00a0 V.RGB= 0.0;<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">}<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">...<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">\u00a0\u00a0\u00a0 <span style=\"color: #3366ff;\">LinearYCoCg<\/span> V;<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"> \u00a0\u00a0 InitColor(V); <span style=\"color: #008000;\">\/\/ error: no matching function for call to 'InitColor'\r\n<\/span><\/span><\/pre>\n<h5>The background<\/h5>\n<p>It might not be initially obvious, but you are running into a change in implicit casting. Previously, structs were considered to match for the purposes of implicit casts if their members had the same types in the same places, so functions that took structs as parameters could be called with structs that had identical internals.<\/p>\n<p>In HLSL 2018, the call to <em>InitColor<\/em> would have identified <em>V<\/em> as having the same internal representation as the parameter to the defined function and included an implicit cast and then used that implementation for the call. However, HLSL 2021 requires the name of the struct casts to match as well much as C++ and other languages do.<\/p>\n<h5>The quick fix<\/h5>\n<p>You can still cast one compatible struct to another. Any structs that run into this error are definitely compatible, so you can just add a cast to the parameter of the failing function call:<\/p>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\">\u00a0\u00a0 InitColor((<span style=\"color: #3366ff;\">LinearRGB<\/span>)V); <span style=\"color: #008000;\">\/\/ no errors!<\/span><\/span><\/pre>\n<h5>The potential lurking bug<\/h5>\n<p>The code above represents a scenario where the call does an adequate job of initializing both RGB and YCoCg values. However, this quirk of HLSL allowed for some erroneous code to be accepted and run fine. If instead of initializing to 0.0, the code tried to initialize it to blue, the correct YCoCg initialization would have to be very different from the RGB initialization. What&#8217;s worse, these overloads weren&#8217;t possible for structs with identical internals because the compiler didn&#8217;t see any difference between them. It&#8217;s worth taking a minute to consider if these calls are intended.<\/p>\n<h5>The ideal fix<\/h5>\n<p>While it&#8217;s not unusual that the same operation would be needed for variables of different types, there is a preferred way of doing that is more useful than relying on structs that happen to have the same internals and it&#8217;s templates. Fortunately, templates are included in HLSL 2021! What&#8217;s more, operator overloads expand the utility of templates even more.<\/p>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">struct<\/span> LinearRGB {<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">  float3<\/span> RGB;<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">  void<\/span> assign(<span style=\"color: #3366ff;\">float3<\/span> f) { RGB = f; }<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">};<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">struct<\/span> LinearYCoCg {<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">  float3<\/span> YCoCg;<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">  void<\/span> assign(<span style=\"color: #3366ff;\">float3<\/span> f) { YCoCg = f; }<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">};<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">template<\/span>&lt;<span style=\"color: #3366ff;\">class<\/span> T&gt;<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">void<\/span> InitColor(inout T V) {<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">  V.assign(0.0);<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">}\r\n<\/span>...\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">LinearYCoCg<\/span> V;<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">InitColor(V); <span style=\"color: #339966;\">\/\/ no errors!\r\n<\/span><\/span><\/pre>\n<h3>My logic operator fails!<\/h3>\n<h4>error: operands for short-circuiting logical binary operator must be scalar<\/h4>\n<h4>error: condition for short-circuiting logical ternary operator must be scalar<\/h4>\n<p>You have a binary or ternary logic operator that has vectors as operands like this code does<\/p>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">float4<\/span> main(<span style=\"color: #3366ff;\">int3<\/span> i3: I3, <span style=\"color: #3366ff;\">int3<\/span> j3: J3): SV_Target {<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">bool3<\/span> b3 = i3 || j3; <span style=\"color: #339966;\">\/\/ error: operands for short-circuiting logical binary operator must be scalar<\/span><\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">int4<\/span> res = b3?i3:j3; <span style=\"color: #339966;\">\/\/ error: condition for short-circuiting ternary operator must be scalar<\/span><\/span><\/pre>\n<h5>The background<\/h5>\n<p>In order to enable short-circuiting for logical operations involving scalars, vectors had to be disallowed from using those same operations. Uniform short-circuiting for a group of values contained in a vector or matrix isn&#8217;t possible as different values within might have different results. This is why, prior to HLSL 2021, short-circuiting was not supported at all. In order to enable it, non-scalars had to be excluded.<\/p>\n<h5>The quick fix<\/h5>\n<p>HLSL 2021 adds new built-in functions that maintain the non-short-circuiting behavior of previous language versions. These are\u00a0<em>and(c1,c2)<\/em>,\u00a0<em>or(c1,c2) <\/em>and\u00a0<em>select(c,v1,v2) <\/em>which replace\u00a0<em>&amp;&amp;,\u00a0<\/em><em>||\u00a0<\/em>and\u00a0<em>?:\u00a0<\/em>respectively. Substituting these built-in functions for the operators for non-scalars will silence the errors and maintain previous behavior:<\/p>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">float4<\/span> main(<span style=\"color: #3366ff;\">int3<\/span> i3: I3, <span style=\"color: #3366ff;\">int3<\/span> j3: J3): SV_Target {<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">bool3<\/span> b3 = or(i3, j3); <span style=\"color: #339966;\">\/\/ no errors!<\/span><\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">int<\/span> i4 = select(b3,i3,j3); <span style=\"color: #339966;\">\/\/ no errors!\r\n<\/span><\/span><\/pre>\n<p>Note that although they are intended for use with non-scalars,\u00a0<em>and(), or()\u00a0<\/em>and\u00a0<em>select()<\/em> can be used with scalar values to perform non-sort-circuiting logical evaluations as well.<\/p>\n<h5>The potentially lurking bug<\/h5>\n<p>With the exception of the conditional argument for the\u00a0<em>?:\u00a0<\/em>ternary operator, boolean vectors can&#8217;t be directly used by the usual conditional control flow mechanisms in HLSL. As such, it may be the case that the vectors were being used as operands to these logic ops in error. If applied directly to conditional statements, they would be rejected, but if assigned to a scalar boolean value, the boolean vector would be implicitly truncated with a warning that might have been ignored. It&#8217;s unlikely that calculation that throws out the rest of the values was intended. It may be worth examining these errors to ensure that the vectors were intended to be applied to these logic operators.<\/p>\n<h5>The ideal fix<\/h5>\n<p>In cases where non-scalars were intended to be evaluated for logic calculations, it may be that the intent was to:<\/p>\n<ul>\n<li>evaluate something about a specific aggregate element<\/li>\n<li>evaluate something about any of the aggregate&#8217;s elements<\/li>\n<li>evaluate something about all of the aggregate&#8217;s elements<\/li>\n<\/ul>\n<p>In these cases, rather than preserve the possibly unintended behavior, it would be better to respectively:<\/p>\n<ul>\n<li>Reference the specific element: i3.x || j3.x<\/li>\n<li>Use the\u00a0<em>any()<\/em> built-in function: any(i3) || any(j3)<\/li>\n<li>Use the\u00a0<em>all()<\/em> built-in function: all(i3) || all(j3)<\/li>\n<\/ul>\n<h3>My loop variable is undeclared!<\/h3>\n<h4>error: use of undeclared identifier &#8216;i&#8217;<\/h4>\n<p>You have a variable declared in the loop construct that is used after the end of the loop scope like this code does<\/p>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\">for (<span style=\"color: #3366ff;\">uint<\/span> i = 0; i &lt; ARR_SIZE; i++) {<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">  if (arr[i] &gt; 0.0)<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">    break;<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">}<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\">if (i &gt;= ARR_SIZE)<\/span>\r\n<span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">  return<\/span> false;<\/span><\/pre>\n<h5>The background<\/h5>\n<p>HLSL 2021 limits the scope of loop variables to the body of the loop itself. Previously, it conformed to an older language standard which extended the scope of such variables to the scope the loop was contained in. Because of the new limited scope, any usage of the variables outside the loop will fail.<\/p>\n<h5>The quick fix<\/h5>\n<p>By declaring the variable just before the loop, it will be in the same scope as it was previously and accessible after end of the loop scope:<\/p>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\">uint<\/span> i;\r\nfor (i = 0; i &lt; ARR_SIZE; i++) {\r\n<\/span> <span style=\"font-family: 'courier new', courier, monospace;\"> if (arr[i] &gt; 0.0)\r\n<\/span> <span style=\"font-family: 'courier new', courier, monospace;\"> break;\r\n<\/span><span style=\"font-family: 'courier new', courier, monospace;\">}\r\n<\/span><span style=\"font-family: 'courier new', courier, monospace;\">if (i &gt;= ARR_SIZE)\r\n<\/span> <span style=\"font-family: 'courier new', courier, monospace;\"><span style=\"color: #3366ff;\"> return<\/span> false;<\/span><\/pre>\n<h5>The potentially lurking bug<\/h5>\n<p>In the event that the loop variable shadows a variable in the outer scope and is used after the loop scope, the results of the computation might change. In previous versions of HLSL, such variables produced a warning: <em>&#8220;redefinition of &#8216;i&#8217; shadows declaration in the outer scope; most recent declaration will be used&#8221;<\/em>. It may be worth it to enable HLSL 2018 with the <em>-HV 2018<\/em> flag and carefully examine such warnings to ensure that the change in scope won&#8217;t change the shader result.<\/p>\n<h5>The ideal fix<\/h5>\n<p>Barring shader-specific details, in this case, the quick fix is also the ideal fix!<\/p>\n<h3>I have a problem that isn&#8217;t listed here!<\/h3>\n<p>Sorry about that! Please<a href=\"https:\/\/github.com\/microsoft\/DirectXShaderCompiler\/issues\/new\/choose\"> file an issue<\/a> in our <a href=\"https:\/\/github.com\/microsoft\/DirectXShaderCompiler\">GitHub repo<\/a> using the appropriate template. If you include the prefix [HLSL2021] it will help us identify it faster.<\/p>\n<p>You can also reach out to us at askhlsl@microsoft.com<\/p>\n<h3>Conclusion<\/h3>\n<p>Thanks for reading! We hope your migration to HLSL 2021 is smooth and it enables you to build great things!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The DirectX Shader Compiler Summer 2023 release is live! The most significant change in this release is the enabling of the HLSL 2021 language version by default. HLSL 2021 includes templates, operator overloads, bitfields, short-circuiting, and modernizations to loop variable scope and implicit struct casting. You can read about the details of these features in [&hellip;]<\/p>\n","protected":false},"author":45155,"featured_media":12651,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1,1203],"tags":[],"class_list":["post-8383","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-directx","category-hlsl"],"acf":[],"blog_post_summary":"<p>The DirectX Shader Compiler Summer 2023 release is live! The most significant change in this release is the enabling of the HLSL 2021 language version by default. HLSL 2021 includes templates, operator overloads, bitfields, short-circuiting, and modernizations to loop variable scope and implicit struct casting. You can read about the details of these features in [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/directx\/wp-json\/wp\/v2\/posts\/8383","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\/45155"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/directx\/wp-json\/wp\/v2\/comments?post=8383"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/directx\/wp-json\/wp\/v2\/posts\/8383\/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=8383"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/directx\/wp-json\/wp\/v2\/categories?post=8383"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/directx\/wp-json\/wp\/v2\/tags?post=8383"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}