Technique.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. // Copyright (c) 2008-2023 the Urho3D project
  2. // License: MIT
  3. #include "../Precompiled.h"
  4. #include "../Core/Context.h"
  5. #include "../Core/ProcessUtils.h"
  6. #include "../Core/Profiler.h"
  7. #include "../Graphics/Graphics.h"
  8. #include "../Graphics/Technique.h"
  9. #include "../GraphicsAPI/ShaderVariation.h"
  10. #include "../IO/Log.h"
  11. #include "../Resource/ResourceCache.h"
  12. #include "../Resource/XMLFile.h"
  13. #include "../DebugNew.h"
  14. namespace Urho3D
  15. {
  16. extern const char* cullModeNames[];
  17. const char* blendModeNames[] =
  18. {
  19. "replace",
  20. "add",
  21. "multiply",
  22. "alpha",
  23. "addalpha",
  24. "premulalpha",
  25. "invdestalpha",
  26. "subtract",
  27. "subtractalpha",
  28. nullptr
  29. };
  30. static const char* compareModeNames[] =
  31. {
  32. "always",
  33. "equal",
  34. "notequal",
  35. "less",
  36. "lessequal",
  37. "greater",
  38. "greaterequal",
  39. nullptr
  40. };
  41. static const char* lightingModeNames[] =
  42. {
  43. "unlit",
  44. "pervertex",
  45. "perpixel",
  46. nullptr
  47. };
  48. Pass::Pass(const String& name) :
  49. blendMode_(BLEND_REPLACE),
  50. cullMode_(MAX_CULLMODES),
  51. depthTestMode_(CMP_LESSEQUAL),
  52. lightingMode_(LIGHTING_UNLIT),
  53. shadersLoadedFrameNumber_(0),
  54. alphaToCoverage_(false),
  55. depthWrite_(true),
  56. isDesktop_(false)
  57. {
  58. name_ = name.ToLower();
  59. index_ = Technique::GetPassIndex(name_);
  60. // Guess default lighting mode from pass name
  61. if (index_ == Technique::basePassIndex || index_ == Technique::alphaPassIndex || index_ == Technique::materialPassIndex ||
  62. index_ == Technique::deferredPassIndex)
  63. lightingMode_ = LIGHTING_PERVERTEX;
  64. else if (index_ == Technique::lightPassIndex || index_ == Technique::litBasePassIndex || index_ == Technique::litAlphaPassIndex)
  65. lightingMode_ = LIGHTING_PERPIXEL;
  66. }
  67. Pass::~Pass() = default;
  68. void Pass::SetBlendMode(BlendMode mode)
  69. {
  70. blendMode_ = mode;
  71. }
  72. void Pass::SetCullMode(CullMode mode)
  73. {
  74. cullMode_ = mode;
  75. }
  76. void Pass::SetDepthTestMode(CompareMode mode)
  77. {
  78. depthTestMode_ = mode;
  79. }
  80. void Pass::SetLightingMode(PassLightingMode mode)
  81. {
  82. lightingMode_ = mode;
  83. }
  84. void Pass::SetDepthWrite(bool enable)
  85. {
  86. depthWrite_ = enable;
  87. }
  88. void Pass::SetAlphaToCoverage(bool enable)
  89. {
  90. alphaToCoverage_ = enable;
  91. }
  92. void Pass::SetIsDesktop(bool enable)
  93. {
  94. isDesktop_ = enable;
  95. }
  96. void Pass::SetVertexShader(const String& name)
  97. {
  98. vertexShaderName_ = name;
  99. ReleaseShaders();
  100. }
  101. void Pass::SetPixelShader(const String& name)
  102. {
  103. pixelShaderName_ = name;
  104. ReleaseShaders();
  105. }
  106. void Pass::SetVertexShaderDefines(const String& defines)
  107. {
  108. vertexShaderDefines_ = defines;
  109. ReleaseShaders();
  110. }
  111. void Pass::SetPixelShaderDefines(const String& defines)
  112. {
  113. pixelShaderDefines_ = defines;
  114. ReleaseShaders();
  115. }
  116. void Pass::SetVertexShaderDefineExcludes(const String& excludes)
  117. {
  118. vertexShaderDefineExcludes_ = excludes;
  119. ReleaseShaders();
  120. }
  121. void Pass::SetPixelShaderDefineExcludes(const String& excludes)
  122. {
  123. pixelShaderDefineExcludes_ = excludes;
  124. ReleaseShaders();
  125. }
  126. void Pass::ReleaseShaders()
  127. {
  128. vertexShaders_.Clear();
  129. pixelShaders_.Clear();
  130. extraVertexShaders_.Clear();
  131. extraPixelShaders_.Clear();
  132. }
  133. void Pass::MarkShadersLoaded(i32 frameNumber)
  134. {
  135. assert(frameNumber > 0);
  136. shadersLoadedFrameNumber_ = frameNumber;
  137. }
  138. String Pass::GetEffectiveVertexShaderDefines() const
  139. {
  140. // Prefer to return just the original defines if possible
  141. if (vertexShaderDefineExcludes_.Empty())
  142. return vertexShaderDefines_;
  143. Vector<String> vsDefines = vertexShaderDefines_.Split(' ');
  144. Vector<String> vsExcludes = vertexShaderDefineExcludes_.Split(' ');
  145. for (const String& vsExclude : vsExcludes)
  146. vsDefines.Remove(vsExclude);
  147. return String::Joined(vsDefines, " ");
  148. }
  149. String Pass::GetEffectivePixelShaderDefines() const
  150. {
  151. // Prefer to return just the original defines if possible
  152. if (pixelShaderDefineExcludes_.Empty())
  153. return pixelShaderDefines_;
  154. Vector<String> psDefines = pixelShaderDefines_.Split(' ');
  155. Vector<String> psExcludes = pixelShaderDefineExcludes_.Split(' ');
  156. for (const String& psExclude : psExcludes)
  157. psDefines.Remove(psExclude);
  158. return String::Joined(psDefines, " ");
  159. }
  160. Vector<SharedPtr<ShaderVariation>>& Pass::GetVertexShaders(const StringHash& extraDefinesHash)
  161. {
  162. // If empty hash, return the base shaders
  163. if (!extraDefinesHash.Value())
  164. return vertexShaders_;
  165. else
  166. return extraVertexShaders_[extraDefinesHash];
  167. }
  168. Vector<SharedPtr<ShaderVariation>>& Pass::GetPixelShaders(const StringHash& extraDefinesHash)
  169. {
  170. if (!extraDefinesHash.Value())
  171. return pixelShaders_;
  172. else
  173. return extraPixelShaders_[extraDefinesHash];
  174. }
  175. i32 Technique::basePassIndex = 0;
  176. i32 Technique::alphaPassIndex = 0;
  177. i32 Technique::materialPassIndex = 0;
  178. i32 Technique::deferredPassIndex = 0;
  179. i32 Technique::lightPassIndex = 0;
  180. i32 Technique::litBasePassIndex = 0;
  181. i32 Technique::litAlphaPassIndex = 0;
  182. i32 Technique::shadowPassIndex = 0;
  183. HashMap<String, i32> Technique::passIndices;
  184. Technique::Technique(Context* context) :
  185. Resource(context),
  186. isDesktop_(false)
  187. {
  188. #ifdef DESKTOP_GRAPHICS
  189. desktopSupport_ = true;
  190. #else
  191. desktopSupport_ = false;
  192. #endif
  193. }
  194. Technique::~Technique() = default;
  195. void Technique::RegisterObject(Context* context)
  196. {
  197. context->RegisterFactory<Technique>();
  198. }
  199. bool Technique::BeginLoad(Deserializer& source)
  200. {
  201. passes_.Clear();
  202. cloneTechniques_.Clear();
  203. SetMemoryUse(sizeof(Technique));
  204. SharedPtr<XMLFile> xml(new XMLFile(context_));
  205. if (!xml->Load(source))
  206. return false;
  207. XMLElement rootElem = xml->GetRoot();
  208. if (rootElem.HasAttribute("desktop"))
  209. isDesktop_ = rootElem.GetBool("desktop");
  210. String globalVS = rootElem.GetAttribute("vs");
  211. String globalPS = rootElem.GetAttribute("ps");
  212. String globalVSDefines = rootElem.GetAttribute("vsdefines");
  213. String globalPSDefines = rootElem.GetAttribute("psdefines");
  214. // End with space so that the pass-specific defines can be appended
  215. if (!globalVSDefines.Empty())
  216. globalVSDefines += ' ';
  217. if (!globalPSDefines.Empty())
  218. globalPSDefines += ' ';
  219. XMLElement passElem = rootElem.GetChild("pass");
  220. while (passElem)
  221. {
  222. if (passElem.HasAttribute("name"))
  223. {
  224. Pass* newPass = CreatePass(passElem.GetAttribute("name"));
  225. if (passElem.HasAttribute("desktop"))
  226. newPass->SetIsDesktop(passElem.GetBool("desktop"));
  227. // Append global defines only when pass does not redefine the shader
  228. if (passElem.HasAttribute("vs"))
  229. {
  230. newPass->SetVertexShader(passElem.GetAttribute("vs"));
  231. newPass->SetVertexShaderDefines(passElem.GetAttribute("vsdefines"));
  232. }
  233. else
  234. {
  235. newPass->SetVertexShader(globalVS);
  236. newPass->SetVertexShaderDefines(globalVSDefines + passElem.GetAttribute("vsdefines"));
  237. }
  238. if (passElem.HasAttribute("ps"))
  239. {
  240. newPass->SetPixelShader(passElem.GetAttribute("ps"));
  241. newPass->SetPixelShaderDefines(passElem.GetAttribute("psdefines"));
  242. }
  243. else
  244. {
  245. newPass->SetPixelShader(globalPS);
  246. newPass->SetPixelShaderDefines(globalPSDefines + passElem.GetAttribute("psdefines"));
  247. }
  248. newPass->SetVertexShaderDefineExcludes(passElem.GetAttribute("vsexcludes"));
  249. newPass->SetPixelShaderDefineExcludes(passElem.GetAttribute("psexcludes"));
  250. if (passElem.HasAttribute("lighting"))
  251. {
  252. String lighting = passElem.GetAttributeLower("lighting");
  253. newPass->SetLightingMode((PassLightingMode)GetStringListIndex(lighting.CString(), lightingModeNames,
  254. LIGHTING_UNLIT));
  255. }
  256. if (passElem.HasAttribute("blend"))
  257. {
  258. String blend = passElem.GetAttributeLower("blend");
  259. newPass->SetBlendMode((BlendMode)GetStringListIndex(blend.CString(), blendModeNames, BLEND_REPLACE));
  260. }
  261. if (passElem.HasAttribute("cull"))
  262. {
  263. String cull = passElem.GetAttributeLower("cull");
  264. newPass->SetCullMode((CullMode)GetStringListIndex(cull.CString(), cullModeNames, MAX_CULLMODES));
  265. }
  266. if (passElem.HasAttribute("depthtest"))
  267. {
  268. String depthTest = passElem.GetAttributeLower("depthtest");
  269. if (depthTest == "false")
  270. newPass->SetDepthTestMode(CMP_ALWAYS);
  271. else
  272. newPass->SetDepthTestMode((CompareMode)GetStringListIndex(depthTest.CString(), compareModeNames, CMP_LESS));
  273. }
  274. if (passElem.HasAttribute("depthwrite"))
  275. newPass->SetDepthWrite(passElem.GetBool("depthwrite"));
  276. if (passElem.HasAttribute("alphatocoverage"))
  277. newPass->SetAlphaToCoverage(passElem.GetBool("alphatocoverage"));
  278. }
  279. else
  280. URHO3D_LOGERROR("Missing pass name");
  281. passElem = passElem.GetNext("pass");
  282. }
  283. return true;
  284. }
  285. void Technique::SetIsDesktop(bool enable)
  286. {
  287. isDesktop_ = enable;
  288. }
  289. void Technique::ReleaseShaders()
  290. {
  291. for (Vector<SharedPtr<Pass>>::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
  292. {
  293. Pass* pass = i->Get();
  294. if (pass)
  295. pass->ReleaseShaders();
  296. }
  297. }
  298. SharedPtr<Technique> Technique::Clone(const String& cloneName) const
  299. {
  300. SharedPtr<Technique> ret(new Technique(context_));
  301. ret->SetIsDesktop(isDesktop_);
  302. ret->SetName(cloneName);
  303. // Deep copy passes
  304. for (Vector<SharedPtr<Pass>>::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
  305. {
  306. Pass* srcPass = i->Get();
  307. if (!srcPass)
  308. continue;
  309. Pass* newPass = ret->CreatePass(srcPass->GetName());
  310. newPass->SetCullMode(srcPass->GetCullMode());
  311. newPass->SetBlendMode(srcPass->GetBlendMode());
  312. newPass->SetDepthTestMode(srcPass->GetDepthTestMode());
  313. newPass->SetLightingMode(srcPass->GetLightingMode());
  314. newPass->SetDepthWrite(srcPass->GetDepthWrite());
  315. newPass->SetAlphaToCoverage(srcPass->GetAlphaToCoverage());
  316. newPass->SetIsDesktop(srcPass->IsDesktop());
  317. newPass->SetVertexShader(srcPass->GetVertexShader());
  318. newPass->SetPixelShader(srcPass->GetPixelShader());
  319. newPass->SetVertexShaderDefines(srcPass->GetVertexShaderDefines());
  320. newPass->SetPixelShaderDefines(srcPass->GetPixelShaderDefines());
  321. newPass->SetVertexShaderDefineExcludes(srcPass->GetVertexShaderDefineExcludes());
  322. newPass->SetPixelShaderDefineExcludes(srcPass->GetPixelShaderDefineExcludes());
  323. }
  324. return ret;
  325. }
  326. Pass* Technique::CreatePass(const String& name)
  327. {
  328. Pass* oldPass = GetPass(name);
  329. if (oldPass)
  330. return oldPass;
  331. SharedPtr<Pass> newPass(new Pass(name));
  332. unsigned passIndex = newPass->GetIndex();
  333. if (passIndex >= passes_.Size())
  334. passes_.Resize(passIndex + 1);
  335. passes_[passIndex] = newPass;
  336. // Calculate memory use now
  337. SetMemoryUse((unsigned)(sizeof(Technique) + GetNumPasses() * sizeof(Pass)));
  338. return newPass;
  339. }
  340. void Technique::RemovePass(const String& name)
  341. {
  342. HashMap<String, i32>::ConstIterator i = passIndices.Find(name.ToLower());
  343. if (i == passIndices.End())
  344. return;
  345. else if (i->second_ < passes_.Size() && passes_[i->second_].Get())
  346. {
  347. passes_[i->second_].Reset();
  348. SetMemoryUse((unsigned)(sizeof(Technique) + GetNumPasses() * sizeof(Pass)));
  349. }
  350. }
  351. bool Technique::HasPass(const String& name) const
  352. {
  353. HashMap<String, i32>::ConstIterator i = passIndices.Find(name.ToLower());
  354. return i != passIndices.End() ? HasPass(i->second_) : false;
  355. }
  356. Pass* Technique::GetPass(const String& name) const
  357. {
  358. HashMap<String, i32>::ConstIterator i = passIndices.Find(name.ToLower());
  359. return i != passIndices.End() ? GetPass(i->second_) : nullptr;
  360. }
  361. Pass* Technique::GetSupportedPass(const String& name) const
  362. {
  363. HashMap<String, i32>::ConstIterator i = passIndices.Find(name.ToLower());
  364. return i != passIndices.End() ? GetSupportedPass(i->second_) : nullptr;
  365. }
  366. i32 Technique::GetNumPasses() const
  367. {
  368. i32 ret = 0;
  369. for (Vector<SharedPtr<Pass>>::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
  370. {
  371. if (i->Get())
  372. ++ret;
  373. }
  374. return ret;
  375. }
  376. Vector<String> Technique::GetPassNames() const
  377. {
  378. Vector<String> ret;
  379. for (Vector<SharedPtr<Pass>>::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
  380. {
  381. Pass* pass = i->Get();
  382. if (pass)
  383. ret.Push(pass->GetName());
  384. }
  385. return ret;
  386. }
  387. Vector<Pass*> Technique::GetPasses() const
  388. {
  389. Vector<Pass*> ret;
  390. for (Vector<SharedPtr<Pass>>::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
  391. {
  392. Pass* pass = i->Get();
  393. if (pass)
  394. ret.Push(pass);
  395. }
  396. return ret;
  397. }
  398. SharedPtr<Technique> Technique::CloneWithDefines(const String& vsDefines, const String& psDefines)
  399. {
  400. // Return self if no actual defines
  401. if (vsDefines.Empty() && psDefines.Empty())
  402. return SharedPtr<Technique>(this);
  403. Pair<StringHash, StringHash> key = MakePair(StringHash(vsDefines), StringHash(psDefines));
  404. // Return existing if possible
  405. HashMap<Pair<StringHash, StringHash>, SharedPtr<Technique>>::Iterator i = cloneTechniques_.Find(key);
  406. if (i != cloneTechniques_.End())
  407. return i->second_;
  408. // Set same name as the original for the clones to ensure proper serialization of the material. This should not be a problem
  409. // since the clones are never stored to the resource cache
  410. i = cloneTechniques_.Insert(MakePair(key, Clone(GetName())));
  411. for (Vector<SharedPtr<Pass>>::ConstIterator j = i->second_->passes_.Begin(); j != i->second_->passes_.End(); ++j)
  412. {
  413. Pass* pass = (*j);
  414. if (!pass)
  415. continue;
  416. if (!vsDefines.Empty())
  417. pass->SetVertexShaderDefines(pass->GetVertexShaderDefines() + " " + vsDefines);
  418. if (!psDefines.Empty())
  419. pass->SetPixelShaderDefines(pass->GetPixelShaderDefines() + " " + psDefines);
  420. }
  421. return i->second_;
  422. }
  423. i32 Technique::GetPassIndex(const String& passName)
  424. {
  425. // Initialize built-in pass indices on first call
  426. if (passIndices.Empty())
  427. {
  428. basePassIndex = passIndices["base"] = 0;
  429. alphaPassIndex = passIndices["alpha"] = 1;
  430. materialPassIndex = passIndices["material"] = 2;
  431. deferredPassIndex = passIndices["deferred"] = 3;
  432. lightPassIndex = passIndices["light"] = 4;
  433. litBasePassIndex = passIndices["litbase"] = 5;
  434. litAlphaPassIndex = passIndices["litalpha"] = 6;
  435. shadowPassIndex = passIndices["shadow"] = 7;
  436. }
  437. String nameLower = passName.ToLower();
  438. HashMap<String, i32>::Iterator i = passIndices.Find(nameLower);
  439. if (i != passIndices.End())
  440. {
  441. return i->second_;
  442. }
  443. else
  444. {
  445. i32 newPassIndex = passIndices.Size();
  446. passIndices[nameLower] = newPassIndex;
  447. return newPassIndex;
  448. }
  449. }
  450. }