Diligent Engine API Reference
D3DShaderResourceLoader.h
1 /* Copyright 2015-2018 Egor Yusov
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
12  *
13  * In no event and under no legal theory, whether in tort (including negligence),
14  * contract, or otherwise, unless required by applicable law (such as deliberate
15  * and grossly negligent acts) or agreed to in writing, shall any Contributor be
16  * liable for any damages, including any direct, indirect, special, incidental,
17  * or consequential damages of any character arising as a result of this License or
18  * out of the use or inability to use the software (including but not limited to damages
19  * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
20  * all other commercial damages or losses), even if such Contributor has been advised
21  * of the possibility of such damages.
22  */
23 
24 #pragma once
25 
26 #include "Shader.h"
27 #include "StringTools.h"
28 
31 
32 namespace Diligent
33 {
34  template<typename D3D_SHADER_DESC,
35  typename D3D_SHADER_INPUT_BIND_DESC,
36  typename TShaderReflection,
37 
38  typename TOnResourcesCounted,
39  typename TOnNewCB,
40  typename TOnNewTexUAV,
41  typename TOnNewBuffUAV,
42  typename TOnNewBuffSRV,
43  typename TOnNewSampler,
44  typename TOnNewTexSRV>
45  void LoadD3DShaderResources(ID3DBlob *pShaderByteCode,
46 
47  TOnResourcesCounted OnResourcesCounted,
48  TOnNewCB OnNewCB,
49  TOnNewTexUAV OnNewTexUAV,
50  TOnNewBuffUAV OnNewBuffUAV,
51  TOnNewBuffSRV OnNewBuffSRV,
52  TOnNewSampler OnNewSampler,
53  TOnNewTexSRV OnNewTexSRV,
54 
55  const ShaderDesc &ShdrDesc,
56  const Char *SamplerSuffix)
57  {
58  CComPtr<TShaderReflection> pShaderReflection;
59  CHECK_D3D_RESULT_THROW( D3DReflect( pShaderByteCode->GetBufferPointer(), pShaderByteCode->GetBufferSize(), __uuidof(pShaderReflection), reinterpret_cast<void**>(static_cast<TShaderReflection**>(&pShaderReflection)) ),
60  "Failed to get the shader reflection" );
61 
62  D3D_SHADER_DESC shaderDesc = {};
63  pShaderReflection->GetDesc( &shaderDesc );
64 
65  std::vector<D3DShaderResourceAttribs, STDAllocatorRawMem<D3DShaderResourceAttribs> > Resources( STD_ALLOCATOR_RAW_MEM(D3DShaderResourceAttribs, GetRawAllocator(), "Allocator for vector<D3DShaderResourceAttribs>") );
66  Resources.reserve(shaderDesc.BoundResources);
67 
68  Uint32 NumCBs = 0, NumTexSRVs = 0, NumTexUAVs = 0, NumBufSRVs = 0, NumBufUAVs = 0, NumSamplers = 0;
69  // Number of resources to skip (used for array resources)
70  UINT SkipCount = 1;
71  for( UINT Res = 0; Res < shaderDesc.BoundResources; Res += SkipCount )
72  {
73  D3D_SHADER_INPUT_BIND_DESC BindingDesc = {};
74  pShaderReflection->GetResourceBindingDesc( Res, &BindingDesc );
75 
76  String Name(BindingDesc.Name);
77 
78  SkipCount = 1;
79 
80  UINT BindCount = BindingDesc.BindCount;
81 
82  // Handle arrays
83  // For shader models 5_0 and before, every resource array element is enumerated individually.
84  // For instance, if the following texture array is defined in the shader:
85  //
86  // Texture2D<float3> g_tex2DDiffuse[4];
87  //
88  // The reflection system will enumerate 4 resources with the following names:
89  // "g_tex2DDiffuse[0]"
90  // "g_tex2DDiffuse[1]"
91  // "g_tex2DDiffuse[2]"
92  // "g_tex2DDiffuse[3]"
93  //
94  // Notice that if some array element is not used by the shader, it will not be enumerated
95 
96  auto OpenBracketPos = Name.find('[');
97  if (String::npos != OpenBracketPos)
98  {
99  VERIFY(BindCount == 1, "When array elements are enumerated individually, BindCount is expected to always be 1");
100 
101  // Name == "g_tex2DDiffuse[0]"
102  // ^
103  // OpenBracketPos
104  Name.erase(OpenBracketPos, Name.length() - OpenBracketPos);
105  // Name == "g_tex2DDiffuse"
106  VERIFY_EXPR(Name.length() == OpenBracketPos);
107 #ifdef _DEBUG
108  for (const auto &ExistingRes : Resources)
109  {
110  VERIFY(ExistingRes.Name != Name, "Resource with the same name has already been enumerated. All array elements are expected to be enumerated one after another");
111  }
112 #endif
113  for( UINT ArrElem = Res+1; ArrElem < shaderDesc.BoundResources; ++ArrElem)
114  {
115  D3D_SHADER_INPUT_BIND_DESC ArrElemBindingDesc = {};
116  pShaderReflection->GetResourceBindingDesc( ArrElem, &ArrElemBindingDesc );
117 
118  // Make sure this case is handled correctly:
119  // "g_tex2DDiffuse[.]" != "g_tex2DDiffuse2[.]"
120  if ( strncmp(Name.c_str(), ArrElemBindingDesc.Name, OpenBracketPos) == 0 && ArrElemBindingDesc.Name[OpenBracketPos] == '[')
121  {
122  //g_tex2DDiffuse[2]
123  // ^
124  UINT Ind = atoi(ArrElemBindingDesc.Name+OpenBracketPos+1);
125  BindCount = std::max(BindCount, Ind+1);
126  VERIFY(ArrElemBindingDesc.BindPoint == BindingDesc.BindPoint + Ind,
127  "Array elements are expected to use contigous bind points.\n",
128  BindingDesc.Name, " uses slot ", BindingDesc.BindPoint, ", so ", ArrElemBindingDesc.Name, " is expected to use slot ", BindingDesc.BindPoint + Ind, " while ", ArrElemBindingDesc.BindPoint, " is actually used" );
129 
130  // Note that skip count may not necessarily be the same as BindCount.
131  // If some array elements are not used by the shader, the reflection system skips them
132  ++SkipCount;
133  }
134  else
135  {
136  break;
137  }
138  }
139  }
140 
141 
143  bool IsStaticSampler = false;
144  if (BindingDesc.Type == D3D_SIT_SAMPLER)
145  {
146  for (Uint32 s = 0; s < ShdrDesc.NumStaticSamplers; ++s)
147  {
148  if( StrCmpSuff(Name.c_str(), ShdrDesc.StaticSamplers[s].TextureName, SamplerSuffix) )
149  {
150  IsStaticSampler = true;
151  break;
152  }
153  }
154  // Use texture name to derive sampler type
155  VarType = GetShaderVariableType(ShdrDesc.DefaultVariableType, ShdrDesc.VariableDesc, ShdrDesc.NumVariables,
156  [&](const char *TexName)
157  {
158  return StrCmpSuff(Name.c_str(), TexName, SamplerSuffix);
159  }
160  );
161  }
162  else
163  {
164  VarType = GetShaderVariableType(Name.c_str(), ShdrDesc.DefaultVariableType, ShdrDesc.VariableDesc, ShdrDesc.NumVariables);
165  }
166 
167 
168  switch( BindingDesc.Type )
169  {
170  case D3D_SIT_CBUFFER: ++NumCBs; break;
171  case D3D_SIT_TBUFFER: UNSUPPORTED( "TBuffers are not supported" ); break;
172  case D3D_SIT_TEXTURE: ++(BindingDesc.Dimension == D3D_SRV_DIMENSION_BUFFER ? NumBufSRVs : NumTexSRVs); break;
173  case D3D_SIT_SAMPLER: ++NumSamplers; break;
174  case D3D_SIT_UAV_RWTYPED: ++(BindingDesc.Dimension == D3D_SRV_DIMENSION_BUFFER ? NumBufUAVs : NumTexUAVs); break;
175  case D3D_SIT_STRUCTURED: ++NumBufSRVs; break;
176  case D3D_SIT_UAV_RWSTRUCTURED: ++NumBufUAVs; break;
177  case D3D_SIT_BYTEADDRESS: UNSUPPORTED( "Byte address buffers are not supported" ); break;
178  case D3D_SIT_UAV_RWBYTEADDRESS: ++NumBufUAVs; break;
179  case D3D_SIT_UAV_APPEND_STRUCTURED: UNSUPPORTED( "Append structured buffers are not supported" ); break;
180  case D3D_SIT_UAV_CONSUME_STRUCTURED: UNSUPPORTED( "Consume structured buffers are not supported" ); break;
181  case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER: UNSUPPORTED( "RW structured buffers with counter are not supported" ); break;
182  default: UNEXPECTED("Unexpected resource type");
183  }
184  Resources.emplace_back(std::move(Name), BindingDesc.BindPoint, BindCount, BindingDesc.Type, VarType, BindingDesc.Dimension, D3DShaderResourceAttribs::InvalidSamplerId, IsStaticSampler);
185  }
186 
187 
188 #ifdef _DEBUG
189  if(ShdrDesc.NumVariables != 0 || ShdrDesc.NumStaticSamplers != 0 )
190  {
191  for (Uint32 v = 0; v < ShdrDesc.NumVariables; ++v)
192  {
193  bool VariableFound = false;
194  const auto *VarName = ShdrDesc.VariableDesc[v].Name;
195 
196  for (const auto& Res : Resources)
197  {
198  // Skip samplers as they are not handled as independent variables
199  if (Res.GetInputType() != D3D_SIT_SAMPLER && Res.Name.compare(VarName) == 0)
200  {
201  VariableFound = true;
202  break;
203  }
204  }
205  if(!VariableFound)
206  {
207  LOG_WARNING_MESSAGE("Variable \"", VarName, "\" not found in shader \"", ShdrDesc.Name, '\"');
208  }
209  }
210 
211  for (Uint32 s = 0; s < ShdrDesc.NumStaticSamplers; ++s)
212  {
213  bool TextureFound = false;
214  const auto *TexName = ShdrDesc.StaticSamplers[s].TextureName;
215 
216  for (const auto& Res : Resources)
217  {
218  if ( Res.GetInputType() == D3D_SIT_TEXTURE && Res.GetSRVDimension() != D3D_SRV_DIMENSION_BUFFER && Res.Name.compare(TexName) == 0)
219  {
220  TextureFound = true;
221  break;
222  }
223  }
224  if(!TextureFound)
225  {
226  LOG_WARNING_MESSAGE("Static sampler specifies a texture \"", TexName, "\" that is not found in shader \"", ShdrDesc.Name, '\"');
227  }
228  }
229  }
230 #endif
231 
232  OnResourcesCounted(NumCBs, NumTexSRVs, NumTexUAVs, NumBufSRVs, NumBufUAVs, NumSamplers);
233 
234  std::vector<D3DShaderResourceAttribs, STDAllocatorRawMem<D3DShaderResourceAttribs> > TextureSRVs( STD_ALLOCATOR_RAW_MEM(D3DShaderResourceAttribs, GetRawAllocator(), "Allocator for vector<D3DShaderResourceAttribs>") );
235  TextureSRVs.reserve(NumTexSRVs);
236 
237  for(auto &Res : Resources)
238  {
239  switch( Res.GetInputType() )
240  {
241  case D3D_SIT_CBUFFER:
242  {
243  OnNewCB( std::move(Res) );
244  break;
245  }
246 
247  case D3D_SIT_TBUFFER:
248  {
249  UNSUPPORTED( "TBuffers are not supported" );
250  break;
251  }
252 
253  case D3D_SIT_TEXTURE:
254  {
255  if( Res.GetSRVDimension() == D3D_SRV_DIMENSION_BUFFER )
256  {
257  OnNewBuffSRV( std::move(Res) );
258  }
259  else
260  {
261  TextureSRVs.emplace_back( std::move(Res) );
262  }
263  break;
264  }
265 
266  case D3D_SIT_SAMPLER:
267  {
268  OnNewSampler( std::move(Res) );
269  break;
270  }
271 
272  case D3D_SIT_UAV_RWTYPED:
273  {
274  if( Res.GetSRVDimension() == D3D_SRV_DIMENSION_BUFFER )
275  {
276  OnNewBuffUAV( std::move(Res) );
277  }
278  else
279  {
280  OnNewTexUAV( std::move(Res) );
281  }
282  break;
283  }
284 
285  case D3D_SIT_STRUCTURED:
286  {
287  OnNewBuffSRV( std::move(Res) );
288  break;
289  }
290 
291  case D3D_SIT_UAV_RWSTRUCTURED:
292  {
293  OnNewBuffUAV( std::move(Res) );
294  break;
295  }
296 
297  case D3D_SIT_BYTEADDRESS:
298  {
299  UNSUPPORTED( "Byte address buffers are not supported" );
300  break;
301  }
302 
303  case D3D_SIT_UAV_RWBYTEADDRESS:
304  {
305  OnNewBuffUAV( std::move(Res) );
306  break;
307  }
308 
309  case D3D_SIT_UAV_APPEND_STRUCTURED:
310  {
311  UNSUPPORTED( "Append structured buffers are not supported" );
312  break;
313  }
314 
315  case D3D_SIT_UAV_CONSUME_STRUCTURED:
316  {
317  UNSUPPORTED( "Consume structured buffers are not supported" );
318  break;
319  }
320 
321  case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER:
322  {
323  UNSUPPORTED( "RW structured buffers with counter are not supported" );
324  break;
325  }
326  }
327  }
328 
329  // Process texture SRVs. We need to do this after all samplers are initialized
330  for( auto &Tex : TextureSRVs )
331  {
332  OnNewTexSRV( std::move(Tex) );
333  }
334  }
335 }
Graphics engine namespace.
Definition: AdaptiveFixedBlockAllocator.h:30
IMemoryAllocator & GetRawAllocator()
Returns raw memory allocator.
Definition: EngineMemory.cpp:46
SHADER_VARIABLE_TYPE
Describes shader variable type that is used by ShaderVariableDesc.
Definition: Shader.h:100
std::basic_string< Char > String
String variable.
Definition: BasicTypes.h:51
uint32_t Uint32
32-bit unsigned integer
Definition: BasicTypes.h:39
Total number of shader variable types.
Definition: Shader.h:117