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