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 "../../Graphics/Graphics.h"
26 #include "../../Graphics/GraphicsImpl.h"
27 #include "../../Graphics/Shader.h"
28 #include "../../Graphics/ShaderProgram.h"
29 #include "../../Graphics/ShaderVariation.h"
30 #include "../../IO/Log.h"
31 
32 #include "../../DebugNew.h"
33 
34 namespace Urho3D
35 {
36 
37 const char* ShaderVariation::elementSemanticNames[] =
38 {
39     "POS",
40     "NORMAL",
41     "BINORMAL",
42     "TANGENT",
43     "TEXCOORD",
44     "COLOR",
45     "BLENDWEIGHT",
46     "BLENDINDICES",
47     "OBJECTINDEX"
48 };
49 
OnDeviceLost()50 void ShaderVariation::OnDeviceLost()
51 {
52     GPUObject::OnDeviceLost();
53 
54     compilerOutput_.Clear();
55 }
56 
Release()57 void ShaderVariation::Release()
58 {
59     if (object_.name_)
60     {
61         if (!graphics_)
62             return;
63 
64         if (!graphics_->IsDeviceLost())
65         {
66             if (type_ == VS)
67             {
68                 if (graphics_->GetVertexShader() == this)
69                     graphics_->SetShaders(0, 0);
70             }
71             else
72             {
73                 if (graphics_->GetPixelShader() == this)
74                     graphics_->SetShaders(0, 0);
75             }
76 
77             glDeleteShader(object_.name_);
78         }
79 
80         object_.name_ = 0;
81         graphics_->CleanupShaderPrograms(this);
82     }
83 
84     compilerOutput_.Clear();
85 }
86 
Create()87 bool ShaderVariation::Create()
88 {
89     Release();
90 
91     if (!owner_)
92     {
93         compilerOutput_ = "Owner shader has expired";
94         return false;
95     }
96 
97     object_.name_ = glCreateShader(type_ == VS ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER);
98     if (!object_.name_)
99     {
100         compilerOutput_ = "Could not create shader object";
101         return false;
102     }
103 
104     const String& originalShaderCode = owner_->GetSourceCode(type_);
105     String shaderCode;
106 
107     // Check if the shader code contains a version define
108     unsigned verStart = originalShaderCode.Find('#');
109     unsigned verEnd = 0;
110     if (verStart != String::NPOS)
111     {
112         if (originalShaderCode.Substring(verStart + 1, 7) == "version")
113         {
114             verEnd = verStart + 9;
115             while (verEnd < originalShaderCode.Length())
116             {
117                 if (IsDigit((unsigned)originalShaderCode[verEnd]))
118                     ++verEnd;
119                 else
120                     break;
121             }
122             // If version define found, insert it first
123             String versionDefine = originalShaderCode.Substring(verStart, verEnd - verStart);
124             shaderCode += versionDefine + "\n";
125         }
126     }
127     // Force GLSL version 150 if no version define and GL3 is being used
128     if (!verEnd && Graphics::GetGL3Support())
129         shaderCode += "#version 150\n";
130 
131     // Distinguish between VS and PS compile in case the shader code wants to include/omit different things
132     shaderCode += type_ == VS ? "#define COMPILEVS\n" : "#define COMPILEPS\n";
133 
134     // Add define for the maximum number of supported bones
135     shaderCode += "#define MAXBONES " + String(Graphics::GetMaxBones()) + "\n";
136 
137     // Prepend the defines to the shader code
138     Vector<String> defineVec = defines_.Split(' ');
139     for (unsigned i = 0; i < defineVec.Size(); ++i)
140     {
141         // Add extra space for the checking code below
142         String defineString = "#define " + defineVec[i].Replaced('=', ' ') + " \n";
143         shaderCode += defineString;
144 
145         // In debug mode, check that all defines are referenced by the shader code
146 #ifdef _DEBUG
147         String defineCheck = defineString.Substring(8, defineString.Find(' ', 8) - 8);
148         if (originalShaderCode.Find(defineCheck) == String::NPOS)
149             URHO3D_LOGWARNING("Shader " + GetFullName() + " does not use the define " + defineCheck);
150 #endif
151     }
152 
153 #ifdef RPI
154     if (type_ == VS)
155         shaderCode += "#define RPI\n";
156 #endif
157 #ifdef __EMSCRIPTEN__
158     shaderCode += "#define WEBGL\n";
159 #endif
160     if (Graphics::GetGL3Support())
161         shaderCode += "#define GL3\n";
162 
163     // When version define found, do not insert it a second time
164     if (verEnd > 0)
165         shaderCode += (originalShaderCode.CString() + verEnd);
166     else
167         shaderCode += originalShaderCode;
168 
169     const char* shaderCStr = shaderCode.CString();
170     glShaderSource(object_.name_, 1, &shaderCStr, 0);
171     glCompileShader(object_.name_);
172 
173     int compiled, length;
174     glGetShaderiv(object_.name_, GL_COMPILE_STATUS, &compiled);
175     if (!compiled)
176     {
177         glGetShaderiv(object_.name_, GL_INFO_LOG_LENGTH, &length);
178         compilerOutput_.Resize((unsigned)length);
179         int outLength;
180         glGetShaderInfoLog(object_.name_, length, &outLength, &compilerOutput_[0]);
181         glDeleteShader(object_.name_);
182         object_.name_ = 0;
183     }
184     else
185         compilerOutput_.Clear();
186 
187     return object_.name_ != 0;
188 }
189 
SetDefines(const String & defines)190 void ShaderVariation::SetDefines(const String& defines)
191 {
192     defines_ = defines;
193 }
194 
195 }
196