1//
2// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
3// 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
10#include "libGLESv2/Program.h"
11#include "libGLESv2/ProgramBinary.h"
12#include "libGLESv2/ResourceManager.h"
13#include "libGLESv2/renderer/Renderer.h"
14
15namespace gl
16{
17const char * const g_fakepath = "C:\\fakepath";
18
19AttributeBindings::AttributeBindings()
20{
21}
22
23AttributeBindings::~AttributeBindings()
24{
25}
26
27InfoLog::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
88    append("%s", msg.c_str());
89}
90
91void InfoLog::append(const char *format, ...)
92{
93    if (!format)
94    {
95        return;
96    }
97
98    va_list vararg;
99    va_start(vararg, format);
100    size_t infoLength = vsnprintf(NULL, 0, format, vararg);
101    va_end(vararg);
102
103    char *logPointer = NULL;
104
105    if (!mInfoLog)
106    {
107        mInfoLog = new char[infoLength + 2];
108        logPointer = mInfoLog;
109    }
110    else
111    {
112        size_t currentlogLength = strlen(mInfoLog);
113        char *newLog = new char[currentlogLength + infoLength + 2];
114        strcpy(newLog, mInfoLog);
115
116        delete[] mInfoLog;
117        mInfoLog = newLog;
118
119        logPointer = mInfoLog + currentlogLength;
120    }
121
122    va_start(vararg, format);
123    vsnprintf(logPointer, infoLength, format, vararg);
124    va_end(vararg);
125
126    logPointer[infoLength] = 0;
127    strcpy(logPointer + infoLength, "\n");
128}
129
130void InfoLog::reset()
131{
132    if (mInfoLog)
133    {
134        delete [] mInfoLog;
135        mInfoLog = NULL;
136    }
137}
138
139Program::Program(rx::Renderer *renderer, ResourceManager *manager, GLuint handle) : mResourceManager(manager), mHandle(handle)
140{
141    mFragmentShader = NULL;
142    mVertexShader = NULL;
143    mProgramBinary.set(NULL);
144    mDeleteStatus = false;
145    mLinked = false;
146    mRefCount = 0;
147    mRenderer = renderer;
148
149    resetUniformBlockBindings();
150}
151
152Program::~Program()
153{
154    unlink(true);
155
156    if (mVertexShader != NULL)
157    {
158        mVertexShader->release();
159    }
160
161    if (mFragmentShader != NULL)
162    {
163        mFragmentShader->release();
164    }
165}
166
167bool Program::attachShader(Shader *shader)
168{
169    if (shader->getType() == GL_VERTEX_SHADER)
170    {
171        if (mVertexShader)
172        {
173            return false;
174        }
175
176        mVertexShader = shader;
177        mVertexShader->addRef();
178    }
179    else if (shader->getType() == GL_FRAGMENT_SHADER)
180    {
181        if (mFragmentShader)
182        {
183            return false;
184        }
185
186        mFragmentShader = shader;
187        mFragmentShader->addRef();
188    }
189    else UNREACHABLE();
190
191    return true;
192}
193
194bool Program::detachShader(Shader *shader)
195{
196    if (shader->getType() == GL_VERTEX_SHADER)
197    {
198        if (mVertexShader != shader)
199        {
200            return false;
201        }
202
203        mVertexShader->release();
204        mVertexShader = NULL;
205    }
206    else if (shader->getType() == GL_FRAGMENT_SHADER)
207    {
208        if (mFragmentShader != shader)
209        {
210            return false;
211        }
212
213        mFragmentShader->release();
214        mFragmentShader = NULL;
215    }
216    else UNREACHABLE();
217
218    return true;
219}
220
221int Program::getAttachedShadersCount() const
222{
223    return (mVertexShader ? 1 : 0) + (mFragmentShader ? 1 : 0);
224}
225
226void AttributeBindings::bindAttributeLocation(GLuint index, const char *name)
227{
228    if (index < MAX_VERTEX_ATTRIBS)
229    {
230        for (int i = 0; i < MAX_VERTEX_ATTRIBS; i++)
231        {
232            mAttributeBinding[i].erase(name);
233        }
234
235        mAttributeBinding[index].insert(name);
236    }
237}
238
239void Program::bindAttributeLocation(GLuint index, const char *name)
240{
241    mAttributeBindings.bindAttributeLocation(index, name);
242}
243
244// Links the HLSL code of the vertex and pixel shader by matching up their varyings,
245// compiling them into binaries, determining the attribute mappings, and collecting
246// a list of uniforms
247bool Program::link(const Caps &caps)
248{
249    unlink(false);
250
251    mInfoLog.reset();
252    resetUniformBlockBindings();
253
254    mProgramBinary.set(new ProgramBinary(mRenderer->createProgram()));
255    mLinked = mProgramBinary->link(mInfoLog, mAttributeBindings, mFragmentShader, mVertexShader,
256                                   mTransformFeedbackVaryings, mTransformFeedbackBufferMode, caps);
257
258    return mLinked;
259}
260
261int AttributeBindings::getAttributeBinding(const std::string &name) const
262{
263    for (int location = 0; location < MAX_VERTEX_ATTRIBS; location++)
264    {
265        if (mAttributeBinding[location].find(name) != mAttributeBinding[location].end())
266        {
267            return location;
268        }
269    }
270
271    return -1;
272}
273
274// Returns the program object to an unlinked state, before re-linking, or at destruction
275void Program::unlink(bool destroy)
276{
277    if (destroy)   // Object being destructed
278    {
279        if (mFragmentShader)
280        {
281            mFragmentShader->release();
282            mFragmentShader = NULL;
283        }
284
285        if (mVertexShader)
286        {
287            mVertexShader->release();
288            mVertexShader = NULL;
289        }
290    }
291
292    mProgramBinary.set(NULL);
293    mLinked = false;
294}
295
296bool Program::isLinked()
297{
298    return mLinked;
299}
300
301ProgramBinary* Program::getProgramBinary() const
302{
303    return mProgramBinary.get();
304}
305
306bool Program::setProgramBinary(GLenum binaryFormat, const void *binary, GLsizei length)
307{
308    unlink(false);
309
310    mInfoLog.reset();
311
312    mProgramBinary.set(new ProgramBinary(mRenderer->createProgram()));
313    mLinked = mProgramBinary->load(mInfoLog, binaryFormat, binary, length);
314
315    if (!mLinked)
316    {
317        mProgramBinary.set(NULL);
318    }
319
320    return mLinked;
321}
322
323void Program::release()
324{
325    mRefCount--;
326
327    if (mRefCount == 0 && mDeleteStatus)
328    {
329        mResourceManager->deleteProgram(mHandle);
330    }
331}
332
333void Program::addRef()
334{
335    mRefCount++;
336}
337
338unsigned int Program::getRefCount() const
339{
340    return mRefCount;
341}
342
343GLint Program::getProgramBinaryLength() const
344{
345    ProgramBinary *programBinary = mProgramBinary.get();
346    if (programBinary)
347    {
348        return programBinary->getLength();
349    }
350    else
351    {
352        return 0;
353    }
354}
355
356int Program::getInfoLogLength() const
357{
358    return mInfoLog.getLength();
359}
360
361void Program::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog)
362{
363    return mInfoLog.getLog(bufSize, length, infoLog);
364}
365
366void Program::getAttachedShaders(GLsizei maxCount, GLsizei *count, GLuint *shaders)
367{
368    int total = 0;
369
370    if (mVertexShader)
371    {
372        if (total < maxCount)
373        {
374            shaders[total] = mVertexShader->getHandle();
375        }
376
377        total++;
378    }
379
380    if (mFragmentShader)
381    {
382        if (total < maxCount)
383        {
384            shaders[total] = mFragmentShader->getHandle();
385        }
386
387        total++;
388    }
389
390    if (count)
391    {
392        *count = total;
393    }
394}
395
396void Program::getActiveAttribute(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name)
397{
398    ProgramBinary *programBinary = getProgramBinary();
399    if (programBinary)
400    {
401        programBinary->getActiveAttribute(index, bufsize, length, size, type, name);
402    }
403    else
404    {
405        if (bufsize > 0)
406        {
407            name[0] = '\0';
408        }
409
410        if (length)
411        {
412            *length = 0;
413        }
414
415        *type = GL_NONE;
416        *size = 1;
417    }
418}
419
420GLint Program::getActiveAttributeCount()
421{
422    ProgramBinary *programBinary = getProgramBinary();
423    if (programBinary)
424    {
425        return programBinary->getActiveAttributeCount();
426    }
427    else
428    {
429        return 0;
430    }
431}
432
433GLint Program::getActiveAttributeMaxLength()
434{
435    ProgramBinary *programBinary = getProgramBinary();
436    if (programBinary)
437    {
438        return programBinary->getActiveAttributeMaxLength();
439    }
440    else
441    {
442        return 0;
443    }
444}
445
446void Program::getActiveUniform(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name)
447{
448    ProgramBinary *programBinary = getProgramBinary();
449    if (programBinary)
450    {
451        return programBinary->getActiveUniform(index, bufsize, length, size, type, name);
452    }
453    else
454    {
455        if (bufsize > 0)
456        {
457            name[0] = '\0';
458        }
459
460        if (length)
461        {
462            *length = 0;
463        }
464
465        *size = 0;
466        *type = GL_NONE;
467    }
468}
469
470GLint Program::getActiveUniformCount()
471{
472    ProgramBinary *programBinary = getProgramBinary();
473    if (programBinary)
474    {
475        return programBinary->getActiveUniformCount();
476    }
477    else
478    {
479        return 0;
480    }
481}
482
483GLint Program::getActiveUniformMaxLength()
484{
485    ProgramBinary *programBinary = getProgramBinary();
486    if (programBinary)
487    {
488        return programBinary->getActiveUniformMaxLength();
489    }
490    else
491    {
492        return 0;
493    }
494}
495
496void Program::flagForDeletion()
497{
498    mDeleteStatus = true;
499}
500
501bool Program::isFlaggedForDeletion() const
502{
503    return mDeleteStatus;
504}
505
506void Program::validate(const Caps &caps)
507{
508    mInfoLog.reset();
509
510    ProgramBinary *programBinary = getProgramBinary();
511    if (isLinked() && programBinary)
512    {
513        programBinary->validate(mInfoLog, caps);
514    }
515    else
516    {
517        mInfoLog.append("Program has not been successfully linked.");
518    }
519}
520
521bool Program::isValidated() const
522{
523    ProgramBinary *programBinary = mProgramBinary.get();
524    if (programBinary)
525    {
526        return programBinary->isValidated();
527    }
528    else
529    {
530        return false;
531    }
532}
533
534GLint Program::getActiveUniformBlockCount()
535{
536    ProgramBinary *programBinary = getProgramBinary();
537    if (programBinary)
538    {
539        return static_cast<GLint>(programBinary->getActiveUniformBlockCount());
540    }
541    else
542    {
543        return 0;
544    }
545}
546
547GLint Program::getActiveUniformBlockMaxLength()
548{
549    ProgramBinary *programBinary = getProgramBinary();
550    if (programBinary)
551    {
552        return static_cast<GLint>(programBinary->getActiveUniformBlockMaxLength());
553    }
554    else
555    {
556        return 0;
557    }
558}
559
560void Program::bindUniformBlock(GLuint uniformBlockIndex, GLuint uniformBlockBinding)
561{
562    mUniformBlockBindings[uniformBlockIndex] = uniformBlockBinding;
563}
564
565GLuint Program::getUniformBlockBinding(GLuint uniformBlockIndex) const
566{
567    return mUniformBlockBindings[uniformBlockIndex];
568}
569
570void Program::resetUniformBlockBindings()
571{
572    for (unsigned int blockId = 0; blockId < IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS; blockId++)
573    {
574        mUniformBlockBindings[blockId] = 0;
575    }
576}
577
578void Program::setTransformFeedbackVaryings(GLsizei count, const GLchar *const *varyings, GLenum bufferMode)
579{
580    mTransformFeedbackVaryings.resize(count);
581    for (GLsizei i = 0; i < count; i++)
582    {
583        mTransformFeedbackVaryings[i] = varyings[i];
584    }
585
586    mTransformFeedbackBufferMode = bufferMode;
587}
588
589void Program::getTransformFeedbackVarying(GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name) const
590{
591    ProgramBinary *programBinary = getProgramBinary();
592    if (programBinary && index < programBinary->getTransformFeedbackVaryingCount())
593    {
594        const LinkedVarying &varying = programBinary->getTransformFeedbackVarying(index);
595        GLsizei lastNameIdx = std::min(bufSize - 1, static_cast<GLsizei>(varying.name.length()));
596        if (length)
597        {
598            *length = lastNameIdx;
599        }
600        if (size)
601        {
602            *size = varying.size;
603        }
604        if (type)
605        {
606            *type = varying.type;
607        }
608        if (name)
609        {
610            memcpy(name, varying.name.c_str(), lastNameIdx);
611            name[lastNameIdx] = '\0';
612        }
613    }
614}
615
616GLsizei Program::getTransformFeedbackVaryingCount() const
617{
618    ProgramBinary *programBinary = getProgramBinary();
619    if (programBinary)
620    {
621        return static_cast<GLsizei>(programBinary->getTransformFeedbackVaryingCount());
622    }
623    else
624    {
625        return 0;
626    }
627}
628
629GLsizei Program::getTransformFeedbackVaryingMaxLength() const
630{
631    ProgramBinary *programBinary = getProgramBinary();
632    if (programBinary)
633    {
634        GLsizei maxSize = 0;
635        for (size_t i = 0; i < programBinary->getTransformFeedbackVaryingCount(); i++)
636        {
637            const LinkedVarying &varying = programBinary->getTransformFeedbackVarying(i);
638            maxSize = std::max(maxSize, static_cast<GLsizei>(varying.name.length() + 1));
639        }
640
641        return maxSize;
642    }
643    else
644    {
645        return 0;
646    }
647}
648
649GLenum Program::getTransformFeedbackBufferMode() const
650{
651    ProgramBinary *programBinary = getProgramBinary();
652    if (programBinary)
653    {
654        return programBinary->getTransformFeedbackBufferMode();
655    }
656    else
657    {
658        return mTransformFeedbackBufferMode;
659    }
660}
661
662}
663