blob: c38aa5a61aa3acafcf52b00dd78af1719a0c9fc9 [file] [log] [blame]
shannon.woods@transgaming.combdf2d802013-02-28 23:16:20 +00001#include "precompiled.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00002//
shannon.woods@transgaming.come16e6512013-02-28 23:19:02 +00003// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00004// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6//
7
8// Program.cpp: Implements the gl::Program class. Implements GL program objects
9// and related functionality. [OpenGL ES 2.0.24] section 2.10.3 page 28.
10
shannon.woods@transgaming.comd2811d62013-02-28 23:11:19 +000011#include "libGLESv2/Program.h"
apatrick@chromium.orgea09f9b2012-06-08 00:45:32 +000012#include "libGLESv2/ProgramBinary.h"
shannon.woods@transgaming.com486d9e92013-02-28 23:15:41 +000013#include "libGLESv2/ResourceManager.h"
daniel@transgaming.com87891f72011-06-01 15:28:35 +000014
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000015namespace gl
16{
apatrick@chromium.org253b8d22012-06-22 19:27:21 +000017const char * const g_fakepath = "C:\\fakepath";
18
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +000019AttributeBindings::AttributeBindings()
20{
21}
22
23AttributeBindings::~AttributeBindings()
24{
apatrick@chromium.org9a30b092012-06-06 20:21:55 +000025}
26
apatrick@chromium.org253b8d22012-06-22 19:27:21 +000027InfoLog::InfoLog() : mInfoLog(NULL)
28{
29}
30
31InfoLog::~InfoLog()
32{
33 delete[] mInfoLog;
34}
35
36
37int InfoLog::getLength() const
38{
39 if (!mInfoLog)
40 {
41 return 0;
42 }
43 else
44 {
45 return strlen(mInfoLog) + 1;
46 }
47}
48
49void InfoLog::getLog(GLsizei bufSize, GLsizei *length, char *infoLog)
50{
51 int index = 0;
52
53 if (bufSize > 0)
54 {
55 if (mInfoLog)
56 {
57 index = std::min(bufSize - 1, (int)strlen(mInfoLog));
58 memcpy(infoLog, mInfoLog, index);
59 }
60
61 infoLog[index] = '\0';
62 }
63
64 if (length)
65 {
66 *length = index;
67 }
68}
69
70// append a santized message to the program info log.
71// The D3D compiler includes a fake file path in some of the warning or error
72// messages, so lets remove all occurrences of this fake file path from the log.
73void InfoLog::appendSanitized(const char *message)
74{
75 std::string msg(message);
76
77 size_t found;
78 do
79 {
80 found = msg.find(g_fakepath);
81 if (found != std::string::npos)
82 {
83 msg.erase(found, strlen(g_fakepath));
84 }
85 }
86 while (found != std::string::npos);
87
shannon.woods@transgaming.come16e6512013-02-28 23:19:02 +000088 append("%s", msg.c_str());
apatrick@chromium.org253b8d22012-06-22 19:27:21 +000089}
90
91void InfoLog::append(const char *format, ...)
92{
93 if (!format)
94 {
95 return;
96 }
97
98 char info[1024];
99
100 va_list vararg;
101 va_start(vararg, format);
102 vsnprintf(info, sizeof(info), format, vararg);
103 va_end(vararg);
104
105 size_t infoLength = strlen(info);
106
107 if (!mInfoLog)
108 {
shannon.woods@transgaming.come16e6512013-02-28 23:19:02 +0000109 mInfoLog = new char[infoLength + 2];
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000110 strcpy(mInfoLog, info);
shannon.woods@transgaming.come16e6512013-02-28 23:19:02 +0000111 strcpy(mInfoLog + infoLength, "\n");
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000112 }
113 else
114 {
115 size_t logLength = strlen(mInfoLog);
shannon.woods@transgaming.come16e6512013-02-28 23:19:02 +0000116 char *newLog = new char[logLength + infoLength + 2];
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000117 strcpy(newLog, mInfoLog);
118 strcpy(newLog + logLength, info);
shannon.woods@transgaming.come16e6512013-02-28 23:19:02 +0000119 strcpy(newLog + logLength + infoLength, "\n");
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000120
121 delete[] mInfoLog;
122 mInfoLog = newLog;
123 }
124}
125
126void InfoLog::reset()
127{
128 if (mInfoLog)
129 {
130 delete [] mInfoLog;
131 mInfoLog = NULL;
132 }
133}
134
daniel@transgaming.com70062c92012-11-28 19:32:30 +0000135Program::Program(rx::Renderer *renderer, ResourceManager *manager, GLuint handle) : mResourceManager(manager), mHandle(handle)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000136{
137 mFragmentShader = NULL;
138 mVertexShader = NULL;
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000139 mProgramBinary.set(NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000140 mDeleteStatus = false;
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000141 mLinked = false;
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000142 mRefCount = 0;
daniel@transgaming.com70062c92012-11-28 19:32:30 +0000143 mRenderer = renderer;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000144}
145
146Program::~Program()
147{
148 unlink(true);
daniel@transgaming.com71cd8682010-04-29 03:35:25 +0000149
150 if (mVertexShader != NULL)
151 {
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000152 mVertexShader->release();
daniel@transgaming.com71cd8682010-04-29 03:35:25 +0000153 }
154
155 if (mFragmentShader != NULL)
156 {
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000157 mFragmentShader->release();
daniel@transgaming.com71cd8682010-04-29 03:35:25 +0000158 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000159}
160
161bool Program::attachShader(Shader *shader)
162{
163 if (shader->getType() == GL_VERTEX_SHADER)
164 {
165 if (mVertexShader)
166 {
167 return false;
168 }
169
170 mVertexShader = (VertexShader*)shader;
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000171 mVertexShader->addRef();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000172 }
173 else if (shader->getType() == GL_FRAGMENT_SHADER)
174 {
175 if (mFragmentShader)
176 {
177 return false;
178 }
179
180 mFragmentShader = (FragmentShader*)shader;
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000181 mFragmentShader->addRef();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000182 }
183 else UNREACHABLE();
184
185 return true;
186}
187
188bool Program::detachShader(Shader *shader)
189{
190 if (shader->getType() == GL_VERTEX_SHADER)
191 {
192 if (mVertexShader != shader)
193 {
194 return false;
195 }
196
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000197 mVertexShader->release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000198 mVertexShader = NULL;
199 }
200 else if (shader->getType() == GL_FRAGMENT_SHADER)
201 {
202 if (mFragmentShader != shader)
203 {
204 return false;
205 }
206
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000207 mFragmentShader->release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000208 mFragmentShader = NULL;
209 }
210 else UNREACHABLE();
211
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000212 return true;
213}
214
daniel@transgaming.comcba50572010-03-28 19:36:09 +0000215int Program::getAttachedShadersCount() const
216{
217 return (mVertexShader ? 1 : 0) + (mFragmentShader ? 1 : 0);
218}
219
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000220void AttributeBindings::bindAttributeLocation(GLuint index, const char *name)
221{
222 if (index < MAX_VERTEX_ATTRIBS)
223 {
224 for (int i = 0; i < MAX_VERTEX_ATTRIBS; i++)
225 {
226 mAttributeBinding[i].erase(name);
227 }
228
229 mAttributeBinding[index].insert(name);
230 }
apatrick@chromium.org9a30b092012-06-06 20:21:55 +0000231}
232
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000233void Program::bindAttributeLocation(GLuint index, const char *name)
234{
apatrick@chromium.org9a30b092012-06-06 20:21:55 +0000235 mAttributeBindings.bindAttributeLocation(index, name);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000236}
237
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000238// Links the HLSL code of the vertex and pixel shader by matching up their varyings,
239// compiling them into binaries, determining the attribute mappings, and collecting
240// a list of uniforms
daniel@transgaming.com4c962bf2012-07-24 18:37:02 +0000241bool Program::link()
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000242{
243 unlink(false);
244
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000245 mInfoLog.reset();
246
daniel@transgaming.com70062c92012-11-28 19:32:30 +0000247 mProgramBinary.set(new ProgramBinary(mRenderer));
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000248 mLinked = mProgramBinary->link(mInfoLog, mAttributeBindings, mFragmentShader, mVertexShader);
daniel@transgaming.com4c962bf2012-07-24 18:37:02 +0000249
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000250 return mLinked;
apatrick@chromium.org9a30b092012-06-06 20:21:55 +0000251}
252
apatrick@chromium.org9a30b092012-06-06 20:21:55 +0000253int AttributeBindings::getAttributeBinding(const std::string &name) const
daniel@transgaming.comb4ff1f82010-04-22 13:35:18 +0000254{
255 for (int location = 0; location < MAX_VERTEX_ATTRIBS; location++)
256 {
257 if (mAttributeBinding[location].find(name) != mAttributeBinding[location].end())
258 {
259 return location;
260 }
261 }
262
263 return -1;
264}
265
daniel@transgaming.comaa5e59b2011-10-04 18:43:12 +0000266// Returns the program object to an unlinked state, before re-linking, or at destruction
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000267void Program::unlink(bool destroy)
268{
269 if (destroy) // Object being destructed
270 {
271 if (mFragmentShader)
272 {
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000273 mFragmentShader->release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000274 mFragmentShader = NULL;
275 }
276
277 if (mVertexShader)
278 {
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000279 mVertexShader->release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000280 mVertexShader = NULL;
281 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000282 }
283
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000284 mProgramBinary.set(NULL);
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000285 mLinked = false;
286}
287
288bool Program::isLinked()
289{
290 return mLinked;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000291}
292
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000293ProgramBinary* Program::getProgramBinary()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000294{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000295 return mProgramBinary.get();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000296}
297
daniel@transgaming.com4c962bf2012-07-24 18:37:02 +0000298bool Program::setProgramBinary(const void *binary, GLsizei length)
apatrick@chromium.org3ce8dbc2012-06-08 17:52:30 +0000299{
300 unlink(false);
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000301
302 mInfoLog.reset();
303
daniel@transgaming.com70062c92012-11-28 19:32:30 +0000304 mProgramBinary.set(new ProgramBinary(mRenderer));
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000305 mLinked = mProgramBinary->load(mInfoLog, binary, length);
306 if (!mLinked)
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000307 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000308 mProgramBinary.set(NULL);
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000309 }
daniel@transgaming.com4c962bf2012-07-24 18:37:02 +0000310
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000311 return mLinked;
apatrick@chromium.org3ce8dbc2012-06-08 17:52:30 +0000312}
313
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000314void Program::release()
315{
316 mRefCount--;
317
318 if (mRefCount == 0 && mDeleteStatus)
319 {
320 mResourceManager->deleteProgram(mHandle);
321 }
322}
323
324void Program::addRef()
325{
326 mRefCount++;
327}
328
329unsigned int Program::getRefCount() const
330{
331 return mRefCount;
332}
333
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000334GLint Program::getProgramBinaryLength() const
335{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000336 ProgramBinary *programBinary = mProgramBinary.get();
337 if (programBinary)
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000338 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000339 return programBinary->getLength();
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000340 }
341 else
342 {
343 return 0;
344 }
345}
346
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000347int Program::getInfoLogLength() const
348{
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000349 return mInfoLog.getLength();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000350}
351
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000352void Program::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog)
353{
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000354 return mInfoLog.getLog(bufSize, length, infoLog);
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000355}
356
daniel@transgaming.com6c785212010-03-30 03:36:17 +0000357void Program::getAttachedShaders(GLsizei maxCount, GLsizei *count, GLuint *shaders)
358{
359 int total = 0;
360
361 if (mVertexShader)
362 {
363 if (total < maxCount)
364 {
365 shaders[total] = mVertexShader->getHandle();
366 }
367
368 total++;
369 }
370
371 if (mFragmentShader)
372 {
373 if (total < maxCount)
374 {
375 shaders[total] = mFragmentShader->getHandle();
376 }
377
378 total++;
379 }
380
381 if (count)
382 {
383 *count = total;
384 }
385}
386
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000387void Program::getActiveAttribute(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name)
388{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000389 ProgramBinary *programBinary = getProgramBinary();
390 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000391 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000392 programBinary->getActiveAttribute(index, bufsize, length, size, type, name);
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000393 }
394 else
395 {
396 if (bufsize > 0)
397 {
398 name[0] = '\0';
399 }
400
401 if (length)
402 {
403 *length = 0;
404 }
405
406 *type = GL_NONE;
407 *size = 1;
408 }
409}
410
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000411GLint Program::getActiveAttributeCount()
412{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000413 ProgramBinary *programBinary = getProgramBinary();
414 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000415 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000416 return programBinary->getActiveAttributeCount();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000417 }
418 else
419 {
420 return 0;
421 }
422}
423
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000424GLint Program::getActiveAttributeMaxLength()
425{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000426 ProgramBinary *programBinary = getProgramBinary();
427 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000428 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000429 return programBinary->getActiveAttributeMaxLength();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000430 }
431 else
432 {
433 return 0;
434 }
435}
436
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000437void Program::getActiveUniform(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name)
438{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000439 ProgramBinary *programBinary = getProgramBinary();
440 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000441 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000442 return programBinary->getActiveUniform(index, bufsize, length, size, type, name);
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000443 }
444 else
445 {
446 if (bufsize > 0)
447 {
448 name[0] = '\0';
449 }
450
451 if (length)
452 {
453 *length = 0;
454 }
455
456 *size = 0;
457 *type = GL_NONE;
458 }
459}
460
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000461GLint Program::getActiveUniformCount()
462{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000463 ProgramBinary *programBinary = getProgramBinary();
464 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000465 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000466 return programBinary->getActiveUniformCount();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000467 }
468 else
469 {
470 return 0;
471 }
472}
473
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000474GLint Program::getActiveUniformMaxLength()
475{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000476 ProgramBinary *programBinary = getProgramBinary();
477 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000478 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000479 return programBinary->getActiveUniformMaxLength();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000480 }
481 else
482 {
483 return 0;
484 }
485}
486
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000487void Program::flagForDeletion()
488{
489 mDeleteStatus = true;
490}
491
492bool Program::isFlaggedForDeletion() const
493{
494 return mDeleteStatus;
495}
daniel@transgaming.com86a7a132010-04-29 03:32:32 +0000496
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000497void Program::validate()
498{
499 mInfoLog.reset();
500
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000501 ProgramBinary *programBinary = getProgramBinary();
502 if (isLinked() && programBinary)
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000503 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000504 programBinary->validate(mInfoLog);
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000505 }
506 else
507 {
508 mInfoLog.append("Program has not been successfully linked.");
509 }
510}
511
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000512bool Program::isValidated() const
513{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000514 ProgramBinary *programBinary = mProgramBinary.get();
515 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000516 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000517 return programBinary->isValidated();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000518 }
519 else
520 {
521 return false;
522 }
523}
524
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000525}