blob: 2a72e02c11eed98696ef78fcd209c5359fb6a11c [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.com70062c92012-11-28 19:32:30 +0000139Program::Program(rx::Renderer *renderer, 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.com70062c92012-11-28 19:32:30 +0000147 mRenderer = renderer;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000148}
149
150Program::~Program()
151{
152 unlink(true);
daniel@transgaming.com71cd8682010-04-29 03:35:25 +0000153
154 if (mVertexShader != NULL)
155 {
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000156 mVertexShader->release();
daniel@transgaming.com71cd8682010-04-29 03:35:25 +0000157 }
158
159 if (mFragmentShader != NULL)
160 {
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000161 mFragmentShader->release();
daniel@transgaming.com71cd8682010-04-29 03:35:25 +0000162 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000163}
164
165bool Program::attachShader(Shader *shader)
166{
167 if (shader->getType() == GL_VERTEX_SHADER)
168 {
169 if (mVertexShader)
170 {
171 return false;
172 }
173
174 mVertexShader = (VertexShader*)shader;
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000175 mVertexShader->addRef();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000176 }
177 else if (shader->getType() == GL_FRAGMENT_SHADER)
178 {
179 if (mFragmentShader)
180 {
181 return false;
182 }
183
184 mFragmentShader = (FragmentShader*)shader;
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000185 mFragmentShader->addRef();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000186 }
187 else UNREACHABLE();
188
189 return true;
190}
191
192bool Program::detachShader(Shader *shader)
193{
194 if (shader->getType() == GL_VERTEX_SHADER)
195 {
196 if (mVertexShader != shader)
197 {
198 return false;
199 }
200
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000201 mVertexShader->release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000202 mVertexShader = NULL;
203 }
204 else if (shader->getType() == GL_FRAGMENT_SHADER)
205 {
206 if (mFragmentShader != shader)
207 {
208 return false;
209 }
210
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000211 mFragmentShader->release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000212 mFragmentShader = NULL;
213 }
214 else UNREACHABLE();
215
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000216 return true;
217}
218
daniel@transgaming.comcba50572010-03-28 19:36:09 +0000219int Program::getAttachedShadersCount() const
220{
221 return (mVertexShader ? 1 : 0) + (mFragmentShader ? 1 : 0);
222}
223
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000224void AttributeBindings::bindAttributeLocation(GLuint index, const char *name)
225{
226 if (index < MAX_VERTEX_ATTRIBS)
227 {
228 for (int i = 0; i < MAX_VERTEX_ATTRIBS; i++)
229 {
230 mAttributeBinding[i].erase(name);
231 }
232
233 mAttributeBinding[index].insert(name);
234 }
apatrick@chromium.org9a30b092012-06-06 20:21:55 +0000235}
236
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000237void Program::bindAttributeLocation(GLuint index, const char *name)
238{
apatrick@chromium.org9a30b092012-06-06 20:21:55 +0000239 mAttributeBindings.bindAttributeLocation(index, name);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000240}
241
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000242// Links the HLSL code of the vertex and pixel shader by matching up their varyings,
243// compiling them into binaries, determining the attribute mappings, and collecting
244// a list of uniforms
daniel@transgaming.com4c962bf2012-07-24 18:37:02 +0000245bool Program::link()
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000246{
247 unlink(false);
248
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000249 mInfoLog.reset();
250
daniel@transgaming.com70062c92012-11-28 19:32:30 +0000251 mProgramBinary.set(new ProgramBinary(mRenderer));
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000252 mLinked = mProgramBinary->link(mInfoLog, mAttributeBindings, mFragmentShader, mVertexShader);
daniel@transgaming.com4c962bf2012-07-24 18:37:02 +0000253
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000254 return mLinked;
apatrick@chromium.org9a30b092012-06-06 20:21:55 +0000255}
256
apatrick@chromium.org9a30b092012-06-06 20:21:55 +0000257int AttributeBindings::getAttributeBinding(const std::string &name) const
daniel@transgaming.comb4ff1f82010-04-22 13:35:18 +0000258{
259 for (int location = 0; location < MAX_VERTEX_ATTRIBS; location++)
260 {
261 if (mAttributeBinding[location].find(name) != mAttributeBinding[location].end())
262 {
263 return location;
264 }
265 }
266
267 return -1;
268}
269
daniel@transgaming.comaa5e59b2011-10-04 18:43:12 +0000270// Returns the program object to an unlinked state, before re-linking, or at destruction
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000271void Program::unlink(bool destroy)
272{
273 if (destroy) // Object being destructed
274 {
275 if (mFragmentShader)
276 {
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000277 mFragmentShader->release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000278 mFragmentShader = NULL;
279 }
280
281 if (mVertexShader)
282 {
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000283 mVertexShader->release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000284 mVertexShader = NULL;
285 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000286 }
287
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000288 mProgramBinary.set(NULL);
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000289 mLinked = false;
290}
291
292bool Program::isLinked()
293{
294 return mLinked;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000295}
296
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000297ProgramBinary* Program::getProgramBinary()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000298{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000299 return mProgramBinary.get();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000300}
301
daniel@transgaming.com4c962bf2012-07-24 18:37:02 +0000302bool Program::setProgramBinary(const void *binary, GLsizei length)
apatrick@chromium.org3ce8dbc2012-06-08 17:52:30 +0000303{
304 unlink(false);
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000305
306 mInfoLog.reset();
307
daniel@transgaming.com70062c92012-11-28 19:32:30 +0000308 mProgramBinary.set(new ProgramBinary(mRenderer));
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000309 mLinked = mProgramBinary->load(mInfoLog, binary, length);
310 if (!mLinked)
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000311 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000312 mProgramBinary.set(NULL);
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000313 }
daniel@transgaming.com4c962bf2012-07-24 18:37:02 +0000314
daniel@transgaming.com716056c2012-07-24 18:38:59 +0000315 return mLinked;
apatrick@chromium.org3ce8dbc2012-06-08 17:52:30 +0000316}
317
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +0000318void Program::release()
319{
320 mRefCount--;
321
322 if (mRefCount == 0 && mDeleteStatus)
323 {
324 mResourceManager->deleteProgram(mHandle);
325 }
326}
327
328void Program::addRef()
329{
330 mRefCount++;
331}
332
333unsigned int Program::getRefCount() const
334{
335 return mRefCount;
336}
337
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000338GLint Program::getProgramBinaryLength() const
339{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000340 ProgramBinary *programBinary = mProgramBinary.get();
341 if (programBinary)
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000342 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000343 return programBinary->getLength();
apatrick@chromium.org90080e32012-07-09 22:15:33 +0000344 }
345 else
346 {
347 return 0;
348 }
349}
350
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000351int Program::getInfoLogLength() const
352{
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000353 return mInfoLog.getLength();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000354}
355
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000356void Program::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog)
357{
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000358 return mInfoLog.getLog(bufSize, length, infoLog);
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000359}
360
daniel@transgaming.com6c785212010-03-30 03:36:17 +0000361void Program::getAttachedShaders(GLsizei maxCount, GLsizei *count, GLuint *shaders)
362{
363 int total = 0;
364
365 if (mVertexShader)
366 {
367 if (total < maxCount)
368 {
369 shaders[total] = mVertexShader->getHandle();
370 }
371
372 total++;
373 }
374
375 if (mFragmentShader)
376 {
377 if (total < maxCount)
378 {
379 shaders[total] = mFragmentShader->getHandle();
380 }
381
382 total++;
383 }
384
385 if (count)
386 {
387 *count = total;
388 }
389}
390
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000391void Program::getActiveAttribute(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name)
392{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000393 ProgramBinary *programBinary = getProgramBinary();
394 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000395 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000396 programBinary->getActiveAttribute(index, bufsize, length, size, type, name);
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000397 }
398 else
399 {
400 if (bufsize > 0)
401 {
402 name[0] = '\0';
403 }
404
405 if (length)
406 {
407 *length = 0;
408 }
409
410 *type = GL_NONE;
411 *size = 1;
412 }
413}
414
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000415GLint Program::getActiveAttributeCount()
416{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000417 ProgramBinary *programBinary = getProgramBinary();
418 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000419 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000420 return programBinary->getActiveAttributeCount();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000421 }
422 else
423 {
424 return 0;
425 }
426}
427
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000428GLint Program::getActiveAttributeMaxLength()
429{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000430 ProgramBinary *programBinary = getProgramBinary();
431 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000432 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000433 return programBinary->getActiveAttributeMaxLength();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000434 }
435 else
436 {
437 return 0;
438 }
439}
440
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000441void Program::getActiveUniform(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name)
442{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000443 ProgramBinary *programBinary = getProgramBinary();
444 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000445 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000446 return programBinary->getActiveUniform(index, bufsize, length, size, type, name);
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000447 }
448 else
449 {
450 if (bufsize > 0)
451 {
452 name[0] = '\0';
453 }
454
455 if (length)
456 {
457 *length = 0;
458 }
459
460 *size = 0;
461 *type = GL_NONE;
462 }
463}
464
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000465GLint Program::getActiveUniformCount()
466{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000467 ProgramBinary *programBinary = getProgramBinary();
468 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000469 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000470 return programBinary->getActiveUniformCount();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000471 }
472 else
473 {
474 return 0;
475 }
476}
477
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000478GLint Program::getActiveUniformMaxLength()
479{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000480 ProgramBinary *programBinary = getProgramBinary();
481 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000482 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000483 return programBinary->getActiveUniformMaxLength();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000484 }
485 else
486 {
487 return 0;
488 }
489}
490
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000491void Program::flagForDeletion()
492{
493 mDeleteStatus = true;
494}
495
496bool Program::isFlaggedForDeletion() const
497{
498 return mDeleteStatus;
499}
daniel@transgaming.com86a7a132010-04-29 03:32:32 +0000500
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000501void Program::validate()
502{
503 mInfoLog.reset();
504
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000505 ProgramBinary *programBinary = getProgramBinary();
506 if (isLinked() && programBinary)
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000507 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000508 programBinary->validate(mInfoLog);
apatrick@chromium.org253b8d22012-06-22 19:27:21 +0000509 }
510 else
511 {
512 mInfoLog.append("Program has not been successfully linked.");
513 }
514}
515
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000516bool Program::isValidated() const
517{
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000518 ProgramBinary *programBinary = mProgramBinary.get();
519 if (programBinary)
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000520 {
daniel@transgaming.com989c1c82012-07-24 18:40:38 +0000521 return programBinary->isValidated();
apatrick@chromium.orge2a59bb2012-06-07 21:09:53 +0000522 }
523 else
524 {
525 return false;
526 }
527}
528
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000529}