D3D11ShaderVariation.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. // Copyright (c) 2008-2023 the Urho3D project
  2. // License: MIT
  3. #include "../../Precompiled.h"
  4. #include "../../Graphics/Graphics.h"
  5. #include "../../GraphicsAPI/Direct3D11/D3D11GraphicsImpl.h"
  6. #include "../../GraphicsAPI/Shader.h"
  7. #include "../../GraphicsAPI/VertexBuffer.h"
  8. #include "../../IO/File.h"
  9. #include "../../IO/FileSystem.h"
  10. #include "../../IO/Log.h"
  11. #include "../../Resource/ResourceCache.h"
  12. #include <d3dcompiler.h>
  13. // https://github.com/urho3d/Urho3D/issues/2887
  14. #if defined(__MINGW32__) && !defined(D3D_COMPILER_VERSION)
  15. #error Please update MinGW
  16. #endif
  17. #include "../../DebugNew.h"
  18. namespace Urho3D
  19. {
  20. const char* ShaderVariation::elementSemanticNames_D3D11[] =
  21. {
  22. "POSITION",
  23. "NORMAL",
  24. "BINORMAL",
  25. "TANGENT",
  26. "TEXCOORD",
  27. "COLOR",
  28. "BLENDWEIGHT",
  29. "BLENDINDICES",
  30. "OBJECTINDEX"
  31. };
  32. void ShaderVariation::OnDeviceLost_D3D11()
  33. {
  34. // No-op on Direct3D11
  35. }
  36. bool ShaderVariation::Create_D3D11()
  37. {
  38. Release_D3D11();
  39. if (!graphics_)
  40. return false;
  41. if (!owner_)
  42. {
  43. compilerOutput_ = "Owner shader has expired";
  44. return false;
  45. }
  46. // Check for up-to-date bytecode on disk
  47. String path, name, extension;
  48. SplitPath(owner_->GetName(), path, name, extension);
  49. extension = type_ == VS ? ".vs4" : ".ps4";
  50. String binaryShaderName = graphics_->GetShaderCacheDir() + name + "_" + StringHash(defines_).ToString() + extension;
  51. if (!LoadByteCode_D3D11(binaryShaderName))
  52. {
  53. // Compile shader if don't have valid bytecode
  54. if (!Compile_D3D11())
  55. return false;
  56. // Save the bytecode after successful compile, but not if the source is from a package
  57. if (owner_->GetTimeStamp())
  58. SaveByteCode_D3D11(binaryShaderName);
  59. }
  60. // Then create shader from the bytecode
  61. ID3D11Device* device = graphics_->GetImpl_D3D11()->GetDevice();
  62. if (type_ == VS)
  63. {
  64. if (device && byteCode_.Size())
  65. {
  66. HRESULT hr = device->CreateVertexShader(&byteCode_[0], byteCode_.Size(), nullptr, (ID3D11VertexShader**)&object_.ptr_);
  67. if (FAILED(hr))
  68. {
  69. URHO3D_SAFE_RELEASE(object_.ptr_);
  70. compilerOutput_ = "Could not create vertex shader (HRESULT " + ToStringHex((unsigned)hr) + ")";
  71. }
  72. }
  73. else
  74. compilerOutput_ = "Could not create vertex shader, empty bytecode";
  75. }
  76. else
  77. {
  78. if (device && byteCode_.Size())
  79. {
  80. HRESULT hr = device->CreatePixelShader(&byteCode_[0], byteCode_.Size(), nullptr, (ID3D11PixelShader**)&object_.ptr_);
  81. if (FAILED(hr))
  82. {
  83. URHO3D_SAFE_RELEASE(object_.ptr_);
  84. compilerOutput_ = "Could not create pixel shader (HRESULT " + ToStringHex((unsigned)hr) + ")";
  85. }
  86. }
  87. else
  88. compilerOutput_ = "Could not create pixel shader, empty bytecode";
  89. }
  90. return object_.ptr_ != nullptr;
  91. }
  92. void ShaderVariation::Release_D3D11()
  93. {
  94. if (object_.ptr_)
  95. {
  96. if (!graphics_)
  97. return;
  98. graphics_->CleanupShaderPrograms_D3D11(this);
  99. if (type_ == VS)
  100. {
  101. if (graphics_->GetVertexShader() == this)
  102. graphics_->SetShaders(nullptr, nullptr);
  103. }
  104. else
  105. {
  106. if (graphics_->GetPixelShader() == this)
  107. graphics_->SetShaders(nullptr, nullptr);
  108. }
  109. URHO3D_SAFE_RELEASE(object_.ptr_);
  110. }
  111. compilerOutput_.Clear();
  112. for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
  113. useTextureUnits_[i] = false;
  114. for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
  115. constantBufferSizes_[i] = 0;
  116. parameters_.Clear();
  117. byteCode_.Clear();
  118. elementHash_ = 0;
  119. }
  120. void ShaderVariation::SetDefines_D3D11(const String& defines)
  121. {
  122. defines_ = defines;
  123. // Internal mechanism for appending the CLIPPLANE define, prevents runtime (every frame) string manipulation
  124. definesClipPlane_ = defines;
  125. if (!definesClipPlane_.EndsWith(" CLIPPLANE"))
  126. definesClipPlane_ += " CLIPPLANE";
  127. }
  128. bool ShaderVariation::LoadByteCode_D3D11(const String& binaryShaderName)
  129. {
  130. ResourceCache* cache = owner_->GetSubsystem<ResourceCache>();
  131. if (!cache->Exists(binaryShaderName))
  132. return false;
  133. FileSystem* fileSystem = owner_->GetSubsystem<FileSystem>();
  134. unsigned sourceTimeStamp = owner_->GetTimeStamp();
  135. // If source code is loaded from a package, its timestamp will be zero. Else check that binary is not older
  136. // than source
  137. if (sourceTimeStamp && fileSystem->GetLastModifiedTime(cache->GetResourceFileName(binaryShaderName)) < sourceTimeStamp)
  138. return false;
  139. SharedPtr<File> file = cache->GetFile(binaryShaderName);
  140. if (!file || file->ReadFileID() != "USHD")
  141. {
  142. URHO3D_LOGERROR(binaryShaderName + " is not a valid shader bytecode file");
  143. return false;
  144. }
  145. /// \todo Check that shader type and model match
  146. /*unsigned short shaderType = */file->ReadU16();
  147. /*unsigned short shaderModel = */file->ReadU16();
  148. elementHash_ = file->ReadU32();
  149. elementHash_ <<= 32;
  150. unsigned numParameters = file->ReadU32();
  151. for (unsigned i = 0; i < numParameters; ++i)
  152. {
  153. String name = file->ReadString();
  154. unsigned buffer = file->ReadU8();
  155. unsigned offset = file->ReadU32();
  156. unsigned size = file->ReadU32();
  157. parameters_[StringHash(name)] = ShaderParameter{type_, name, offset, size, buffer};
  158. }
  159. unsigned numTextureUnits = file->ReadU32();
  160. for (unsigned i = 0; i < numTextureUnits; ++i)
  161. {
  162. /*String unitName = */file->ReadString();
  163. unsigned reg = file->ReadU8();
  164. if (reg < MAX_TEXTURE_UNITS)
  165. useTextureUnits_[reg] = true;
  166. }
  167. unsigned byteCodeSize = file->ReadU32();
  168. if (byteCodeSize)
  169. {
  170. byteCode_.Resize(byteCodeSize);
  171. file->Read(&byteCode_[0], byteCodeSize);
  172. if (type_ == VS)
  173. URHO3D_LOGDEBUG("Loaded cached vertex shader " + GetFullName());
  174. else
  175. URHO3D_LOGDEBUG("Loaded cached pixel shader " + GetFullName());
  176. CalculateConstantBufferSizes_D3D11();
  177. return true;
  178. }
  179. else
  180. {
  181. URHO3D_LOGERROR(binaryShaderName + " has zero length bytecode");
  182. return false;
  183. }
  184. }
  185. bool ShaderVariation::Compile_D3D11()
  186. {
  187. const String& sourceCode = owner_->GetSourceCode(type_);
  188. Vector<String> defines = defines_.Split(' ');
  189. // Set the entrypoint, profile and flags according to the shader being compiled
  190. const char* entryPoint = nullptr;
  191. const char* profile = nullptr;
  192. unsigned flags = D3DCOMPILE_OPTIMIZATION_LEVEL3;
  193. defines.Push("D3D11");
  194. if (type_ == VS)
  195. {
  196. entryPoint = "VS";
  197. defines.Push("COMPILEVS");
  198. profile = "vs_4_0";
  199. }
  200. else
  201. {
  202. entryPoint = "PS";
  203. defines.Push("COMPILEPS");
  204. profile = "ps_4_0";
  205. flags |= D3DCOMPILE_PREFER_FLOW_CONTROL;
  206. }
  207. defines.Push("MAXBONES=" + String(Graphics::GetMaxBones()));
  208. // Collect defines into macros
  209. Vector<String> defineValues;
  210. Vector<D3D_SHADER_MACRO> macros;
  211. for (unsigned i = 0; i < defines.Size(); ++i)
  212. {
  213. i32 equalsPos = defines[i].Find('=');
  214. if (equalsPos != String::NPOS)
  215. {
  216. defineValues.Push(defines[i].Substring(equalsPos + 1));
  217. defines[i].Resize(equalsPos);
  218. }
  219. else
  220. defineValues.Push("1");
  221. }
  222. for (unsigned i = 0; i < defines.Size(); ++i)
  223. {
  224. D3D_SHADER_MACRO macro;
  225. macro.Name = defines[i].CString();
  226. macro.Definition = defineValues[i].CString();
  227. macros.Push(macro);
  228. // In debug mode, check that all defines are referenced by the shader code
  229. #ifdef _DEBUG
  230. if (sourceCode.Find(defines[i]) == String::NPOS)
  231. URHO3D_LOGWARNING("Shader " + GetFullName() + " does not use the define " + defines[i]);
  232. #endif
  233. }
  234. D3D_SHADER_MACRO endMacro;
  235. endMacro.Name = nullptr;
  236. endMacro.Definition = nullptr;
  237. macros.Push(endMacro);
  238. // Compile using D3DCompile
  239. ID3DBlob* shaderCode = nullptr;
  240. ID3DBlob* errorMsgs = nullptr;
  241. HRESULT hr = D3DCompile(sourceCode.CString(), sourceCode.Length(), owner_->GetName().CString(), &macros.Front(), nullptr,
  242. entryPoint, profile, flags, 0, &shaderCode, &errorMsgs);
  243. if (FAILED(hr))
  244. {
  245. // Do not include end zero unnecessarily
  246. compilerOutput_ = String((const char*)errorMsgs->GetBufferPointer(), (unsigned)errorMsgs->GetBufferSize() - 1);
  247. }
  248. else
  249. {
  250. if (type_ == VS)
  251. URHO3D_LOGDEBUG("Compiled vertex shader " + GetFullName());
  252. else
  253. URHO3D_LOGDEBUG("Compiled pixel shader " + GetFullName());
  254. unsigned char* bufData = (unsigned char*)shaderCode->GetBufferPointer();
  255. unsigned bufSize = (unsigned)shaderCode->GetBufferSize();
  256. // Use the original bytecode to reflect the parameters
  257. ParseParameters_D3D11(bufData, bufSize);
  258. CalculateConstantBufferSizes_D3D11();
  259. // Then strip everything not necessary to use the shader
  260. ID3DBlob* strippedCode = nullptr;
  261. D3DStripShader(bufData, bufSize,
  262. D3DCOMPILER_STRIP_REFLECTION_DATA | D3DCOMPILER_STRIP_DEBUG_INFO | D3DCOMPILER_STRIP_TEST_BLOBS, &strippedCode);
  263. byteCode_.Resize((unsigned)strippedCode->GetBufferSize());
  264. memcpy(&byteCode_[0], strippedCode->GetBufferPointer(), byteCode_.Size());
  265. strippedCode->Release();
  266. }
  267. URHO3D_SAFE_RELEASE(shaderCode);
  268. URHO3D_SAFE_RELEASE(errorMsgs);
  269. return !byteCode_.Empty();
  270. }
  271. void ShaderVariation::ParseParameters_D3D11(unsigned char* bufData, unsigned bufSize)
  272. {
  273. ID3D11ShaderReflection* reflection = nullptr;
  274. D3D11_SHADER_DESC shaderDesc;
  275. HRESULT hr = D3DReflect(bufData, bufSize, IID_ID3D11ShaderReflection, (void**)&reflection);
  276. if (FAILED(hr) || !reflection)
  277. {
  278. URHO3D_SAFE_RELEASE(reflection);
  279. URHO3D_LOGD3DERROR("Failed to reflect vertex shader's input signature", hr);
  280. return;
  281. }
  282. reflection->GetDesc(&shaderDesc);
  283. if (type_ == VS)
  284. {
  285. unsigned elementHash = 0;
  286. for (unsigned i = 0; i < shaderDesc.InputParameters; ++i)
  287. {
  288. D3D11_SIGNATURE_PARAMETER_DESC paramDesc;
  289. reflection->GetInputParameterDesc((UINT)i, &paramDesc);
  290. VertexElementSemantic semantic = (VertexElementSemantic)GetStringListIndex(paramDesc.SemanticName, elementSemanticNames_D3D11, MAX_VERTEX_ELEMENT_SEMANTICS, true);
  291. if (semantic != MAX_VERTEX_ELEMENT_SEMANTICS)
  292. {
  293. CombineHash(elementHash, semantic);
  294. CombineHash(elementHash, paramDesc.SemanticIndex);
  295. }
  296. }
  297. elementHash_ = elementHash;
  298. elementHash_ <<= 32;
  299. }
  300. HashMap<String, unsigned> cbRegisterMap;
  301. for (unsigned i = 0; i < shaderDesc.BoundResources; ++i)
  302. {
  303. D3D11_SHADER_INPUT_BIND_DESC resourceDesc;
  304. reflection->GetResourceBindingDesc(i, &resourceDesc);
  305. String resourceName(resourceDesc.Name);
  306. if (resourceDesc.Type == D3D_SIT_CBUFFER)
  307. cbRegisterMap[resourceName] = resourceDesc.BindPoint;
  308. else if (resourceDesc.Type == D3D_SIT_SAMPLER && resourceDesc.BindPoint < MAX_TEXTURE_UNITS)
  309. useTextureUnits_[resourceDesc.BindPoint] = true;
  310. }
  311. for (unsigned i = 0; i < shaderDesc.ConstantBuffers; ++i)
  312. {
  313. ID3D11ShaderReflectionConstantBuffer* cb = reflection->GetConstantBufferByIndex(i);
  314. D3D11_SHADER_BUFFER_DESC cbDesc;
  315. cb->GetDesc(&cbDesc);
  316. unsigned cbRegister = cbRegisterMap[String(cbDesc.Name)];
  317. for (unsigned j = 0; j < cbDesc.Variables; ++j)
  318. {
  319. ID3D11ShaderReflectionVariable* var = cb->GetVariableByIndex(j);
  320. D3D11_SHADER_VARIABLE_DESC varDesc;
  321. var->GetDesc(&varDesc);
  322. String varName(varDesc.Name);
  323. if (varName[0] == 'c')
  324. {
  325. varName = varName.Substring(1); // Strip the c to follow Urho3D constant naming convention
  326. parameters_[varName] = ShaderParameter{type_, varName, varDesc.StartOffset, varDesc.Size, cbRegister};
  327. }
  328. }
  329. }
  330. reflection->Release();
  331. }
  332. void ShaderVariation::SaveByteCode_D3D11(const String& binaryShaderName)
  333. {
  334. ResourceCache* cache = owner_->GetSubsystem<ResourceCache>();
  335. FileSystem* fileSystem = owner_->GetSubsystem<FileSystem>();
  336. // Filename may or may not be inside the resource system
  337. String fullName = binaryShaderName;
  338. if (!IsAbsolutePath(fullName))
  339. {
  340. // If not absolute, use the resource dir of the shader
  341. String shaderFileName = cache->GetResourceFileName(owner_->GetName());
  342. if (shaderFileName.Empty())
  343. return;
  344. fullName = shaderFileName.Substring(0, shaderFileName.Find(owner_->GetName())) + binaryShaderName;
  345. }
  346. String path = GetPath(fullName);
  347. if (!fileSystem->DirExists(path))
  348. fileSystem->CreateDir(path);
  349. SharedPtr<File> file(new File(owner_->GetContext(), fullName, FILE_WRITE));
  350. if (!file->IsOpen())
  351. return;
  352. file->WriteFileID("USHD");
  353. file->WriteI16((unsigned short)type_);
  354. file->WriteI16(4);
  355. file->WriteU32(elementHash_ >> 32);
  356. file->WriteU32(parameters_.Size());
  357. for (HashMap<StringHash, ShaderParameter>::ConstIterator i = parameters_.Begin(); i != parameters_.End(); ++i)
  358. {
  359. file->WriteString(i->second_.name_);
  360. file->WriteU8((unsigned char)i->second_.buffer_);
  361. file->WriteU32(i->second_.offset_);
  362. file->WriteU32(i->second_.size_);
  363. }
  364. unsigned usedTextureUnits = 0;
  365. for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
  366. {
  367. if (useTextureUnits_[i])
  368. ++usedTextureUnits;
  369. }
  370. file->WriteU32(usedTextureUnits);
  371. for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
  372. {
  373. if (useTextureUnits_[i])
  374. {
  375. file->WriteString(graphics_->GetTextureUnitName((TextureUnit)i));
  376. file->WriteU8((unsigned char)i);
  377. }
  378. }
  379. file->WriteU32(byteCode_.Size());
  380. if (byteCode_.Size())
  381. file->Write(&byteCode_[0], byteCode_.Size());
  382. }
  383. void ShaderVariation::CalculateConstantBufferSizes_D3D11()
  384. {
  385. for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
  386. constantBufferSizes_[i] = 0;
  387. for (HashMap<StringHash, ShaderParameter>::ConstIterator i = parameters_.Begin(); i != parameters_.End(); ++i)
  388. {
  389. if (i->second_.buffer_ < MAX_SHADER_PARAMETER_GROUPS)
  390. {
  391. unsigned oldSize = constantBufferSizes_[i->second_.buffer_];
  392. unsigned paramEnd = i->second_.offset_ + i->second_.size_;
  393. if (paramEnd > oldSize)
  394. constantBufferSizes_[i->second_.buffer_] = paramEnd;
  395. }
  396. }
  397. }
  398. }