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