blob: 5f53a1f5817fcf3ad8a6ffcbc4929119f288ecdb [file] [log] [blame]
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001//
daniel@transgaming.comcde6a612012-02-17 18:01:10 +00002// Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00003// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// Program.cpp: Implements the gl::Program class. Implements GL program objects
8// and related functionality. [OpenGL ES 2.0.24] section 2.10.3 page 28.
9
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000010#include "libGLESv2/Program.h"
apatrick@chromium.orgea09f9b2012-06-08 00:45:32 +000011#include "libGLESv2/ProgramBinary.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000012
alokp@chromium.orgea0e1af2010-03-22 19:33:14 +000013#include "common/debug.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000014
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000015#include "libGLESv2/main.h"
16#include "libGLESv2/Shader.h"
daniel@transgaming.com1b3a8152010-04-22 13:35:37 +000017#include "libGLESv2/utilities.h"
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000018
daniel@transgaming.com87891f72011-06-01 15:28:35 +000019#include <string>
20
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000021namespace gl
22{
apatrick@chromium.org253b8d22012-06-22 19:27:21 +000023const char * const g_fakepath = "C:\\fakepath";
24
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +000025AttributeBindings::AttributeBindings()
26{
27}
28
29AttributeBindings::~AttributeBindings()
30{
apatrick@chromium.org9a30b092012-06-06 20:21:55 +000031}
32
apatrick@chromium.org253b8d22012-06-22 19:27:21 +000033InfoLog::InfoLog() : mInfoLog(NULL)
34{
35}
36
37InfoLog::~InfoLog()
38{
39 delete[] mInfoLog;
40}
41
42
43int InfoLog::getLength() const
44{
45 if (!mInfoLog)
46 {
47 return 0;
48 }
49 else
50 {
51 return strlen(mInfoLog) + 1;
52 }
53}
54
55void InfoLog::getLog(GLsizei bufSize, GLsizei *length, char *infoLog)
56{
57 int index = 0;
58
59 if (bufSize > 0)
60 {
61 if (mInfoLog)
62 {
63 index = std::min(bufSize - 1, (int)strlen(mInfoLog));
64 memcpy(infoLog, mInfoLog, index);
65 }
66
67 infoLog[index] = '\0';
68 }
69
70 if (length)
71 {
72 *length = index;
73 }
74}
75
76// append a santized message to the program info log.
77// The D3D compiler includes a fake file path in some of the warning or error
78// messages, so lets remove all occurrences of this fake file path from the log.
79void InfoLog::appendSanitized(const char *message)
80{
81 std::string msg(message);
82
83 size_t found;
84 do
85 {
86 found = msg.find(g_fakepath);
87 if (found != std::string::npos)
88 {
89 msg.erase(found, strlen(g_fakepath));
90 }
91 }
92 while (found != std::string::npos);
93
94 append("%s\n", msg.c_str());
95}
96
97void InfoLog::append(const char *format, ...)
98{
99 if (!format)
100 {
101 return;
102 }
103
104 char info[1024];
105
106 va_list vararg;
107 va_start(vararg, format);
108 vsnprintf(info, sizeof(info), format, vararg);
109 va_end(vararg);
110
111 size_t infoLength = strlen(info);
112
113 if (!mInfoLog)
114 {
115 mInfoLog = new char[infoLength + 1];
116 strcpy(mInfoLog, info);
117 }
118 else
119 {
120 size_t logLength = strlen(mInfoLog);
121 char *newLog = new char[logLength + infoLength + 1];
122 strcpy(newLog, mInfoLog);
123 strcpy(newLog + logLength, info);
124
125 delete[] mInfoLog;
126 mInfoLog = newLog;
127 }
128}
129
130void InfoLog::reset()
131{
132 if (mInfoLog)
133 {
134 delete [] mInfoLog;
135 mInfoLog = NULL;
136 }
137}
138
daniel@transgaming.com39c9d952012-07-24 18:32:29 +0000139Program::Program(ResourceManager *manager, GLuint handle) : mResourceManager(manager), mHandle(handle)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000140{
141 mFragmentShader = NULL;
142 mVertexShader = NULL;
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000143 mProgramBinary.set(NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000144 mDeleteStatus = false;
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000145 mLinked = false;
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000146 mRefCount = 0;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000147}
148
149Program::~Program()
150{
151 unlink(true);
daniel@transgaming.com71cd8682010-04-29 03:35:25 +0000152
153 if (mVertexShader != NULL)
154 {
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000155 mVertexShader->release();
daniel@transgaming.com71cd8682010-04-29 03:35:25 +0000156 }
157
158 if (mFragmentShader != NULL)
159 {
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000160 mFragmentShader->release();
daniel@transgaming.com71cd8682010-04-29 03:35:25 +0000161 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000162}
163
164bool Program::attachShader(Shader *shader)
165{
166 if (shader->getType() == GL_VERTEX_SHADER)
167 {
168 if (mVertexShader)
169 {
170 return false;
171 }
172
173 mVertexShader = (VertexShader*)shader;
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000174 mVertexShader->addRef();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000175 }
176 else if (shader->getType() == GL_FRAGMENT_SHADER)
177 {
178 if (mFragmentShader)
179 {
180 return false;
181 }
182
183 mFragmentShader = (FragmentShader*)shader;
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000184 mFragmentShader->addRef();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000185 }
186 else UNREACHABLE();
187
188 return true;
189}
190
191bool Program::detachShader(Shader *shader)
192{
193 if (shader->getType() == GL_VERTEX_SHADER)
194 {
195 if (mVertexShader != shader)
196 {
197 return false;
198 }
199
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000200 mVertexShader->release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000201 mVertexShader = NULL;
202 }
203 else if (shader->getType() == GL_FRAGMENT_SHADER)
204 {
205 if (mFragmentShader != shader)
206 {
207 return false;
208 }
209
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000210 mFragmentShader->release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000211 mFragmentShader = NULL;
212 }
213 else UNREACHABLE();
214
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000215 return true;
216}
217
daniel@transgaming.comcba50572010-03-28 19:36:09 +0000218int Program::getAttachedShadersCount() const
219{
220 return (mVertexShader ? 1 : 0) + (mFragmentShader ? 1 : 0);
221}
222
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000223void AttributeBindings::bindAttributeLocation(GLuint index, const char *name)
224{
225 if (index < MAX_VERTEX_ATTRIBS)
226 {
227 for (int i = 0; i < MAX_VERTEX_ATTRIBS; i++)
228 {
229 mAttributeBinding[i].erase(name);
230 }
231
232 mAttributeBinding[index].insert(name);
233 }
apatrick@chromium.org9a30b092012-06-06 20:21:55 +0000234}
235
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000236void Program::bindAttributeLocation(GLuint index, const char *name)
237{
apatrick@chromium.org9a30b092012-06-06 20:21:55 +0000238 mAttributeBindings.bindAttributeLocation(index, name);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000239}
240
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000241// Links the HLSL code of the vertex and pixel shader by matching up their varyings,
242// compiling them into binaries, determining the attribute mappings, and collecting
243// a list of uniforms
daniel@transgaming.com4c962bf2012-07-24 18:37:02 +0000244bool Program::link()
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000245{
246 unlink(false);
247
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000248 mInfoLog.reset();
249
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000250 mProgramBinary.set(new ProgramBinary());
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000251 mLinked = mProgramBinary->link(mInfoLog, mAttributeBindings, mFragmentShader, mVertexShader);
daniel@transgaming.com4c962bf2012-07-24 18:37:02 +0000252
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000253 return mLinked;
apatrick@chromium.org9a30b092012-06-06 20:21:55 +0000254}
255
apatrick@chromium.org9a30b092012-06-06 20:21:55 +0000256int AttributeBindings::getAttributeBinding(const std::string &name) const
daniel@transgaming.comb4ff1f82010-04-22 13:35:18 +0000257{
258 for (int location = 0; location < MAX_VERTEX_ATTRIBS; location++)
259 {
260 if (mAttributeBinding[location].find(name) != mAttributeBinding[location].end())
261 {
262 return location;
263 }
264 }
265
266 return -1;
267}
268
daniel@transgaming.comaa5e59b2011-10-04 18:43:12 +0000269// Returns the program object to an unlinked state, before re-linking, or at destruction
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000270void Program::unlink(bool destroy)
271{
272 if (destroy) // Object being destructed
273 {
274 if (mFragmentShader)
275 {
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000276 mFragmentShader->release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000277 mFragmentShader = NULL;
278 }
279
280 if (mVertexShader)
281 {
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000282 mVertexShader->release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000283 mVertexShader = NULL;
284 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000285 }
286
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000287 mProgramBinary.set(NULL);
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000288 mLinked = false;
289}
290
291bool Program::isLinked()
292{
293 return mLinked;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000294}
295
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000296ProgramBinary* Program::getProgramBinary()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000297{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000298 return mProgramBinary.get();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000299}
300
daniel@transgaming.com4c962bf2012-07-24 18:37:02 +0000301bool Program::setProgramBinary(const void *binary, GLsizei length)
apatrick@chromium.org3ce8dbc2012-06-08 17:52:30 +0000302{
303 unlink(false);
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000304
305 mInfoLog.reset();
306
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000307 mProgramBinary.set(new ProgramBinary());
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000308 mLinked = mProgramBinary->load(mInfoLog, binary, length);
309 if (!mLinked)
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000310 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000311 mProgramBinary.set(NULL);
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000312 }
daniel@transgaming.com4c962bf2012-07-24 18:37:02 +0000313
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000314 return mLinked;
apatrick@chromium.org3ce8dbc2012-06-08 17:52:30 +0000315}
316
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000317void Program::release()
318{
319 mRefCount--;
320
321 if (mRefCount == 0 && mDeleteStatus)
322 {
323 mResourceManager->deleteProgram(mHandle);
324 }
325}
326
327void Program::addRef()
328{
329 mRefCount++;
330}
331
332unsigned int Program::getRefCount() const
333{
334 return mRefCount;
335}
336
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000337GLint Program::getProgramBinaryLength() const
338{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000339 ProgramBinary *programBinary = mProgramBinary.get();
340 if (programBinary)
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000341 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000342 return programBinary->getLength();
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000343 }
344 else
345 {
346 return 0;
347 }
348}
349
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000350int Program::getInfoLogLength() const
351{
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000352 return mInfoLog.getLength();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000353}
354
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000355void Program::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog)
356{
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000357 return mInfoLog.getLog(bufSize, length, infoLog);
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000358}
359
daniel@transgaming.com6c785212010-03-30 03:36:17 +0000360void Program::getAttachedShaders(GLsizei maxCount, GLsizei *count, GLuint *shaders)
361{
362 int total = 0;
363
364 if (mVertexShader)
365 {
366 if (total < maxCount)
367 {
368 shaders[total] = mVertexShader->getHandle();
369 }
370
371 total++;
372 }
373
374 if (mFragmentShader)
375 {
376 if (total < maxCount)
377 {
378 shaders[total] = mFragmentShader->getHandle();
379 }
380
381 total++;
382 }
383
384 if (count)
385 {
386 *count = total;
387 }
388}
389
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000390void Program::getActiveAttribute(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name)
391{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000392 ProgramBinary *programBinary = getProgramBinary();
393 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000394 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000395 programBinary->getActiveAttribute(index, bufsize, length, size, type, name);
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000396 }
397 else
398 {
399 if (bufsize > 0)
400 {
401 name[0] = '\0';
402 }
403
404 if (length)
405 {
406 *length = 0;
407 }
408
409 *type = GL_NONE;
410 *size = 1;
411 }
412}
413
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000414GLint Program::getActiveAttributeCount()
415{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000416 ProgramBinary *programBinary = getProgramBinary();
417 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000418 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000419 return programBinary->getActiveAttributeCount();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000420 }
421 else
422 {
423 return 0;
424 }
425}
426
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000427GLint Program::getActiveAttributeMaxLength()
428{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000429 ProgramBinary *programBinary = getProgramBinary();
430 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000431 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000432 return programBinary->getActiveAttributeMaxLength();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000433 }
434 else
435 {
436 return 0;
437 }
438}
439
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000440void Program::getActiveUniform(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name)
441{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000442 ProgramBinary *programBinary = getProgramBinary();
443 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000444 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000445 return programBinary->getActiveUniform(index, bufsize, length, size, type, name);
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000446 }
447 else
448 {
449 if (bufsize > 0)
450 {
451 name[0] = '\0';
452 }
453
454 if (length)
455 {
456 *length = 0;
457 }
458
459 *size = 0;
460 *type = GL_NONE;
461 }
462}
463
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000464GLint Program::getActiveUniformCount()
465{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000466 ProgramBinary *programBinary = getProgramBinary();
467 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000468 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000469 return programBinary->getActiveUniformCount();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000470 }
471 else
472 {
473 return 0;
474 }
475}
476
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000477GLint Program::getActiveUniformMaxLength()
478{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000479 ProgramBinary *programBinary = getProgramBinary();
480 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000481 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000482 return programBinary->getActiveUniformMaxLength();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000483 }
484 else
485 {
486 return 0;
487 }
488}
489
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000490void Program::flagForDeletion()
491{
492 mDeleteStatus = true;
493}
494
495bool Program::isFlaggedForDeletion() const
496{
497 return mDeleteStatus;
498}
daniel@transgaming.com86a7a132010-04-29 03:32:32 +0000499
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000500void Program::validate()
501{
502 mInfoLog.reset();
503
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000504 ProgramBinary *programBinary = getProgramBinary();
505 if (isLinked() && programBinary)
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000506 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000507 programBinary->validate(mInfoLog);
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000508 }
509 else
510 {
511 mInfoLog.append("Program has not been successfully linked.");
512 }
513}
514
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000515bool Program::isValidated() const
516{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000517 ProgramBinary *programBinary = mProgramBinary.get();
518 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000519 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000520 return programBinary->isValidated();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000521 }
522 else
523 {
524 return false;
525 }
526}
527
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000528}