1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22 
23 #include "../Precompiled.h"
24 
25 #include "../Core/Context.h"
26 #include "../Core/ProcessUtils.h"
27 #include "../Core/Profiler.h"
28 #include "../Graphics/Graphics.h"
29 #include "../Graphics/Technique.h"
30 #include "../Graphics/ShaderVariation.h"
31 #include "../IO/Log.h"
32 #include "../Resource/ResourceCache.h"
33 #include "../Resource/XMLFile.h"
34 
35 #include "../DebugNew.h"
36 
37 namespace Urho3D
38 {
39 
40 extern const char* cullModeNames[];
41 
42 const char* blendModeNames[] =
43 {
44     "replace",
45     "add",
46     "multiply",
47     "alpha",
48     "addalpha",
49     "premulalpha",
50     "invdestalpha",
51     "subtract",
52     "subtractalpha",
53     0
54 };
55 
56 static const char* compareModeNames[] =
57 {
58     "always",
59     "equal",
60     "notequal",
61     "less",
62     "lessequal",
63     "greater",
64     "greaterequal",
65     0
66 };
67 
68 static const char* lightingModeNames[] =
69 {
70     "unlit",
71     "pervertex",
72     "perpixel",
73     0
74 };
75 
Pass(const String & name)76 Pass::Pass(const String& name) :
77     blendMode_(BLEND_REPLACE),
78     cullMode_(MAX_CULLMODES),
79     depthTestMode_(CMP_LESSEQUAL),
80     lightingMode_(LIGHTING_UNLIT),
81     shadersLoadedFrameNumber_(0),
82     alphaToCoverage_(false),
83     depthWrite_(true),
84     isDesktop_(false)
85 {
86     name_ = name.ToLower();
87     index_ = Technique::GetPassIndex(name_);
88 
89     // Guess default lighting mode from pass name
90     if (index_ == Technique::basePassIndex || index_ == Technique::alphaPassIndex || index_ == Technique::materialPassIndex ||
91         index_ == Technique::deferredPassIndex)
92         lightingMode_ = LIGHTING_PERVERTEX;
93     else if (index_ == Technique::lightPassIndex || index_ == Technique::litBasePassIndex || index_ == Technique::litAlphaPassIndex)
94         lightingMode_ = LIGHTING_PERPIXEL;
95 }
96 
~Pass()97 Pass::~Pass()
98 {
99 }
100 
SetBlendMode(BlendMode mode)101 void Pass::SetBlendMode(BlendMode mode)
102 {
103     blendMode_ = mode;
104 }
105 
SetCullMode(CullMode mode)106 void Pass::SetCullMode(CullMode mode)
107 {
108     cullMode_ = mode;
109 }
110 
SetDepthTestMode(CompareMode mode)111 void Pass::SetDepthTestMode(CompareMode mode)
112 {
113     depthTestMode_ = mode;
114 }
115 
SetLightingMode(PassLightingMode mode)116 void Pass::SetLightingMode(PassLightingMode mode)
117 {
118     lightingMode_ = mode;
119 }
120 
SetDepthWrite(bool enable)121 void Pass::SetDepthWrite(bool enable)
122 {
123     depthWrite_ = enable;
124 }
125 
SetAlphaToCoverage(bool enable)126 void Pass::SetAlphaToCoverage(bool enable)
127 {
128     alphaToCoverage_ = enable;
129 }
130 
131 
SetIsDesktop(bool enable)132 void Pass::SetIsDesktop(bool enable)
133 {
134     isDesktop_ = enable;
135 }
136 
SetVertexShader(const String & name)137 void Pass::SetVertexShader(const String& name)
138 {
139     vertexShaderName_ = name;
140     ReleaseShaders();
141 }
142 
SetPixelShader(const String & name)143 void Pass::SetPixelShader(const String& name)
144 {
145     pixelShaderName_ = name;
146     ReleaseShaders();
147 }
148 
SetVertexShaderDefines(const String & defines)149 void Pass::SetVertexShaderDefines(const String& defines)
150 {
151     vertexShaderDefines_ = defines;
152     ReleaseShaders();
153 }
154 
SetPixelShaderDefines(const String & defines)155 void Pass::SetPixelShaderDefines(const String& defines)
156 {
157     pixelShaderDefines_ = defines;
158     ReleaseShaders();
159 }
160 
SetVertexShaderDefineExcludes(const String & excludes)161 void Pass::SetVertexShaderDefineExcludes(const String& excludes)
162 {
163     vertexShaderDefineExcludes_ = excludes;
164     ReleaseShaders();
165 }
166 
SetPixelShaderDefineExcludes(const String & excludes)167 void Pass::SetPixelShaderDefineExcludes(const String& excludes)
168 {
169     pixelShaderDefineExcludes_ = excludes;
170     ReleaseShaders();
171 }
172 
ReleaseShaders()173 void Pass::ReleaseShaders()
174 {
175     vertexShaders_.Clear();
176     pixelShaders_.Clear();
177     extraVertexShaders_.Clear();
178     extraPixelShaders_.Clear();
179 }
180 
MarkShadersLoaded(unsigned frameNumber)181 void Pass::MarkShadersLoaded(unsigned frameNumber)
182 {
183     shadersLoadedFrameNumber_ = frameNumber;
184 }
185 
GetEffectiveVertexShaderDefines() const186 String Pass::GetEffectiveVertexShaderDefines() const
187 {
188     // Prefer to return just the original defines if possible
189     if (vertexShaderDefineExcludes_.Empty())
190         return vertexShaderDefines_;
191 
192     Vector<String> vsDefines = vertexShaderDefines_.Split(' ');
193     Vector<String> vsExcludes = vertexShaderDefineExcludes_.Split(' ');
194     for (unsigned i = 0; i < vsExcludes.Size(); ++i)
195         vsDefines.Remove(vsExcludes[i]);
196 
197     return String::Joined(vsDefines, " ");
198 }
199 
GetEffectivePixelShaderDefines() const200 String Pass::GetEffectivePixelShaderDefines() const
201 {
202     // Prefer to return just the original defines if possible
203     if (pixelShaderDefineExcludes_.Empty())
204         return pixelShaderDefines_;
205 
206     Vector<String> psDefines = pixelShaderDefines_.Split(' ');
207     Vector<String> psExcludes = pixelShaderDefineExcludes_.Split(' ');
208     for (unsigned i = 0; i < psExcludes.Size(); ++i)
209         psDefines.Remove(psExcludes[i]);
210 
211     return String::Joined(psDefines, " ");
212 }
213 
GetVertexShaders(const StringHash & extraDefinesHash)214 Vector<SharedPtr<ShaderVariation> >& Pass::GetVertexShaders(const StringHash& extraDefinesHash)
215 {
216     // If empty hash, return the base shaders
217     if (!extraDefinesHash.Value())
218         return vertexShaders_;
219     else
220         return extraVertexShaders_[extraDefinesHash];
221 }
222 
GetPixelShaders(const StringHash & extraDefinesHash)223 Vector<SharedPtr<ShaderVariation> >& Pass::GetPixelShaders(const StringHash& extraDefinesHash)
224 {
225     if (!extraDefinesHash.Value())
226         return pixelShaders_;
227     else
228         return extraPixelShaders_[extraDefinesHash];
229 }
230 
231 unsigned Technique::basePassIndex = 0;
232 unsigned Technique::alphaPassIndex = 0;
233 unsigned Technique::materialPassIndex = 0;
234 unsigned Technique::deferredPassIndex = 0;
235 unsigned Technique::lightPassIndex = 0;
236 unsigned Technique::litBasePassIndex = 0;
237 unsigned Technique::litAlphaPassIndex = 0;
238 unsigned Technique::shadowPassIndex = 0;
239 
240 HashMap<String, unsigned> Technique::passIndices;
241 
Technique(Context * context)242 Technique::Technique(Context* context) :
243     Resource(context),
244     isDesktop_(false)
245 {
246 #ifdef DESKTOP_GRAPHICS
247     desktopSupport_ = true;
248 #else
249     desktopSupport_ = false;
250 #endif
251 }
252 
~Technique()253 Technique::~Technique()
254 {
255 }
256 
RegisterObject(Context * context)257 void Technique::RegisterObject(Context* context)
258 {
259     context->RegisterFactory<Technique>();
260 }
261 
BeginLoad(Deserializer & source)262 bool Technique::BeginLoad(Deserializer& source)
263 {
264     passes_.Clear();
265     cloneTechniques_.Clear();
266 
267     SetMemoryUse(sizeof(Technique));
268 
269     SharedPtr<XMLFile> xml(new XMLFile(context_));
270     if (!xml->Load(source))
271         return false;
272 
273     XMLElement rootElem = xml->GetRoot();
274     if (rootElem.HasAttribute("desktop"))
275         isDesktop_ = rootElem.GetBool("desktop");
276 
277     String globalVS = rootElem.GetAttribute("vs");
278     String globalPS = rootElem.GetAttribute("ps");
279     String globalVSDefines = rootElem.GetAttribute("vsdefines");
280     String globalPSDefines = rootElem.GetAttribute("psdefines");
281     // End with space so that the pass-specific defines can be appended
282     if (!globalVSDefines.Empty())
283         globalVSDefines += ' ';
284     if (!globalPSDefines.Empty())
285         globalPSDefines += ' ';
286 
287     XMLElement passElem = rootElem.GetChild("pass");
288     while (passElem)
289     {
290         if (passElem.HasAttribute("name"))
291         {
292             Pass* newPass = CreatePass(passElem.GetAttribute("name"));
293 
294             if (passElem.HasAttribute("desktop"))
295                 newPass->SetIsDesktop(passElem.GetBool("desktop"));
296 
297             // Append global defines only when pass does not redefine the shader
298             if (passElem.HasAttribute("vs"))
299             {
300                 newPass->SetVertexShader(passElem.GetAttribute("vs"));
301                 newPass->SetVertexShaderDefines(passElem.GetAttribute("vsdefines"));
302             }
303             else
304             {
305                 newPass->SetVertexShader(globalVS);
306                 newPass->SetVertexShaderDefines(globalVSDefines + passElem.GetAttribute("vsdefines"));
307             }
308             if (passElem.HasAttribute("ps"))
309             {
310                 newPass->SetPixelShader(passElem.GetAttribute("ps"));
311                 newPass->SetPixelShaderDefines(passElem.GetAttribute("psdefines"));
312             }
313             else
314             {
315                 newPass->SetPixelShader(globalPS);
316                 newPass->SetPixelShaderDefines(globalPSDefines + passElem.GetAttribute("psdefines"));
317             }
318 
319             newPass->SetVertexShaderDefineExcludes(passElem.GetAttribute("vsexcludes"));
320             newPass->SetPixelShaderDefineExcludes(passElem.GetAttribute("psexcludes"));
321 
322             if (passElem.HasAttribute("lighting"))
323             {
324                 String lighting = passElem.GetAttributeLower("lighting");
325                 newPass->SetLightingMode((PassLightingMode)GetStringListIndex(lighting.CString(), lightingModeNames,
326                     LIGHTING_UNLIT));
327             }
328 
329             if (passElem.HasAttribute("blend"))
330             {
331                 String blend = passElem.GetAttributeLower("blend");
332                 newPass->SetBlendMode((BlendMode)GetStringListIndex(blend.CString(), blendModeNames, BLEND_REPLACE));
333             }
334 
335             if (passElem.HasAttribute("cull"))
336             {
337                 String cull = passElem.GetAttributeLower("cull");
338                 newPass->SetCullMode((CullMode)GetStringListIndex(cull.CString(), cullModeNames, MAX_CULLMODES));
339             }
340 
341             if (passElem.HasAttribute("depthtest"))
342             {
343                 String depthTest = passElem.GetAttributeLower("depthtest");
344                 if (depthTest == "false")
345                     newPass->SetDepthTestMode(CMP_ALWAYS);
346                 else
347                     newPass->SetDepthTestMode((CompareMode)GetStringListIndex(depthTest.CString(), compareModeNames, CMP_LESS));
348             }
349 
350             if (passElem.HasAttribute("depthwrite"))
351                 newPass->SetDepthWrite(passElem.GetBool("depthwrite"));
352 
353             if (passElem.HasAttribute("alphatocoverage"))
354                 newPass->SetAlphaToCoverage(passElem.GetBool("alphatocoverage"));
355         }
356         else
357             URHO3D_LOGERROR("Missing pass name");
358 
359         passElem = passElem.GetNext("pass");
360     }
361 
362     return true;
363 }
364 
SetIsDesktop(bool enable)365 void Technique::SetIsDesktop(bool enable)
366 {
367     isDesktop_ = enable;
368 }
369 
ReleaseShaders()370 void Technique::ReleaseShaders()
371 {
372     for (Vector<SharedPtr<Pass> >::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
373     {
374         Pass* pass = i->Get();
375         if (pass)
376             pass->ReleaseShaders();
377     }
378 }
379 
Clone(const String & cloneName) const380 SharedPtr<Technique> Technique::Clone(const String& cloneName) const
381 {
382     SharedPtr<Technique> ret(new Technique(context_));
383     ret->SetIsDesktop(isDesktop_);
384     ret->SetName(cloneName);
385 
386     // Deep copy passes
387     for (Vector<SharedPtr<Pass> >::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
388     {
389         Pass* srcPass = i->Get();
390         if (!srcPass)
391             continue;
392 
393         Pass* newPass = ret->CreatePass(srcPass->GetName());
394         newPass->SetBlendMode(srcPass->GetBlendMode());
395         newPass->SetDepthTestMode(srcPass->GetDepthTestMode());
396         newPass->SetLightingMode(srcPass->GetLightingMode());
397         newPass->SetDepthWrite(srcPass->GetDepthWrite());
398         newPass->SetAlphaToCoverage(srcPass->GetAlphaToCoverage());
399         newPass->SetIsDesktop(srcPass->IsDesktop());
400         newPass->SetVertexShader(srcPass->GetVertexShader());
401         newPass->SetPixelShader(srcPass->GetPixelShader());
402         newPass->SetVertexShaderDefines(srcPass->GetVertexShaderDefines());
403         newPass->SetPixelShaderDefines(srcPass->GetPixelShaderDefines());
404         newPass->SetVertexShaderDefineExcludes(srcPass->GetVertexShaderDefineExcludes());
405         newPass->SetPixelShaderDefineExcludes(srcPass->GetPixelShaderDefineExcludes());
406     }
407 
408     return ret;
409 }
410 
CreatePass(const String & name)411 Pass* Technique::CreatePass(const String& name)
412 {
413     Pass* oldPass = GetPass(name);
414     if (oldPass)
415         return oldPass;
416 
417     SharedPtr<Pass> newPass(new Pass(name));
418     unsigned passIndex = newPass->GetIndex();
419     if (passIndex >= passes_.Size())
420         passes_.Resize(passIndex + 1);
421     passes_[passIndex] = newPass;
422 
423     // Calculate memory use now
424     SetMemoryUse((unsigned)(sizeof(Technique) + GetNumPasses() * sizeof(Pass)));
425 
426     return newPass;
427 }
428 
RemovePass(const String & name)429 void Technique::RemovePass(const String& name)
430 {
431     HashMap<String, unsigned>::ConstIterator i = passIndices.Find(name.ToLower());
432     if (i == passIndices.End())
433         return;
434     else if (i->second_ < passes_.Size() && passes_[i->second_].Get())
435     {
436         passes_[i->second_].Reset();
437         SetMemoryUse((unsigned)(sizeof(Technique) + GetNumPasses() * sizeof(Pass)));
438     }
439 }
440 
HasPass(const String & name) const441 bool Technique::HasPass(const String& name) const
442 {
443     HashMap<String, unsigned>::ConstIterator i = passIndices.Find(name.ToLower());
444     return i != passIndices.End() ? HasPass(i->second_) : false;
445 }
446 
GetPass(const String & name) const447 Pass* Technique::GetPass(const String& name) const
448 {
449     HashMap<String, unsigned>::ConstIterator i = passIndices.Find(name.ToLower());
450     return i != passIndices.End() ? GetPass(i->second_) : 0;
451 }
452 
GetSupportedPass(const String & name) const453 Pass* Technique::GetSupportedPass(const String& name) const
454 {
455     HashMap<String, unsigned>::ConstIterator i = passIndices.Find(name.ToLower());
456     return i != passIndices.End() ? GetSupportedPass(i->second_) : 0;
457 }
458 
GetNumPasses() const459 unsigned Technique::GetNumPasses() const
460 {
461     unsigned ret = 0;
462 
463     for (Vector<SharedPtr<Pass> >::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
464     {
465         if (i->Get())
466             ++ret;
467     }
468 
469     return ret;
470 }
471 
GetPassNames() const472 Vector<String> Technique::GetPassNames() const
473 {
474     Vector<String> ret;
475 
476     for (Vector<SharedPtr<Pass> >::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
477     {
478         Pass* pass = i->Get();
479         if (pass)
480             ret.Push(pass->GetName());
481     }
482 
483     return ret;
484 }
485 
GetPasses() const486 PODVector<Pass*> Technique::GetPasses() const
487 {
488     PODVector<Pass*> ret;
489 
490     for (Vector<SharedPtr<Pass> >::ConstIterator i = passes_.Begin(); i != passes_.End(); ++i)
491     {
492         Pass* pass = i->Get();
493         if (pass)
494             ret.Push(pass);
495     }
496 
497     return ret;
498 }
499 
CloneWithDefines(const String & vsDefines,const String & psDefines)500 SharedPtr<Technique> Technique::CloneWithDefines(const String& vsDefines, const String& psDefines)
501 {
502     // Return self if no actual defines
503     if (vsDefines.Empty() && psDefines.Empty())
504         return SharedPtr<Technique>(this);
505 
506     Pair<StringHash, StringHash> key = MakePair(StringHash(vsDefines), StringHash(psDefines));
507 
508     // Return existing if possible
509     HashMap<Pair<StringHash, StringHash>, SharedPtr<Technique> >::Iterator i = cloneTechniques_.Find(key);
510     if (i != cloneTechniques_.End())
511         return i->second_;
512 
513     // Set same name as the original for the clones to ensure proper serialization of the material. This should not be a problem
514     // since the clones are never stored to the resource cache
515     i = cloneTechniques_.Insert(MakePair(key, Clone(GetName())));
516 
517     for (Vector<SharedPtr<Pass> >::ConstIterator j = i->second_->passes_.Begin(); j != i->second_->passes_.End(); ++j)
518     {
519         Pass* pass = (*j);
520         if (!pass)
521             continue;
522 
523         if (!vsDefines.Empty())
524             pass->SetVertexShaderDefines(pass->GetVertexShaderDefines() + " " + vsDefines);
525         if (!psDefines.Empty())
526             pass->SetPixelShaderDefines(pass->GetPixelShaderDefines() + " " + psDefines);
527     }
528 
529     return i->second_;
530 }
531 
GetPassIndex(const String & passName)532 unsigned Technique::GetPassIndex(const String& passName)
533 {
534     // Initialize built-in pass indices on first call
535     if (passIndices.Empty())
536     {
537         basePassIndex = passIndices["base"] = 0;
538         alphaPassIndex = passIndices["alpha"] = 1;
539         materialPassIndex = passIndices["material"] = 2;
540         deferredPassIndex = passIndices["deferred"] = 3;
541         lightPassIndex = passIndices["light"] = 4;
542         litBasePassIndex = passIndices["litbase"] = 5;
543         litAlphaPassIndex = passIndices["litalpha"] = 6;
544         shadowPassIndex = passIndices["shadow"] = 7;
545     }
546 
547     String nameLower = passName.ToLower();
548     HashMap<String, unsigned>::Iterator i = passIndices.Find(nameLower);
549     if (i != passIndices.End())
550         return i->second_;
551     else
552     {
553         unsigned newPassIndex = passIndices.Size();
554         passIndices[nameLower] = newPassIndex;
555         return newPassIndex;
556     }
557 }
558 
559 }
560