Support for serializing a linked program to binary.

The format has a text section followed by a binary section. The binary section contains an image of the device caps and the two shader executables. The text section has everything else newline deliminated.

Ran WebGL conformance tests with temporary change to glLinkProgram that round trips all linked programs through glGetProgramBinary and glProgramBinary. No regressions.
Review URL: https://codereview.appspot.com/6295092

git-svn-id: https://angleproject.googlecode.com/svn/trunk@1199 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/common/version.h b/src/common/version.h
index e3d0157..043e377 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -1,7 +1,7 @@
 #define MAJOR_VERSION 1
 #define MINOR_VERSION 0
 #define BUILD_VERSION 0
-#define BUILD_REVISION 1196
+#define BUILD_REVISION 1199
 
 #define STRINGIFY(x) #x
 #define MACRO_STRINGIFY(x) STRINGIFY(x)
diff --git a/src/libGLESv2/Program.cpp b/src/libGLESv2/Program.cpp
index f28a9d8..97d372a 100644
--- a/src/libGLESv2/Program.cpp
+++ b/src/libGLESv2/Program.cpp
@@ -298,10 +298,18 @@
     return mProgramBinary;
 }
 
-void Program::setProgramBinary(ProgramBinary *programBinary)
+void Program::setProgramBinary(const void *binary, GLsizei length)
 {
     unlink(false);
-    mProgramBinary = programBinary;
+
+    mInfoLog.reset();
+
+    mProgramBinary = new ProgramBinary;
+    if (!mProgramBinary->load(mInfoLog, binary, length))
+    {
+        delete mProgramBinary;
+        mProgramBinary = NULL;
+    }
 }
 
 void Program::release()
@@ -329,6 +337,18 @@
     return mSerial;
 }
 
+GLint Program::getProgramBinaryLength() const
+{
+    if (mProgramBinary)
+    {
+        return mProgramBinary->getLength();
+    }
+    else
+    {
+        return 0;
+    }
+}
+
 unsigned int Program::issueSerial()
 {
     return mCurrentSerial++;
diff --git a/src/libGLESv2/Program.h b/src/libGLESv2/Program.h
index 244731d..3adc9be 100644
--- a/src/libGLESv2/Program.h
+++ b/src/libGLESv2/Program.h
@@ -69,7 +69,7 @@
     void bindAttributeLocation(GLuint index, const char *name);
 
     void link();
-    void setProgramBinary(ProgramBinary *programBinary);
+    void setProgramBinary(const void *binary, GLsizei length);
     ProgramBinary *getProgramBinary();
 
     int getInfoLogLength() const;
@@ -95,6 +95,8 @@
 
     unsigned int getSerial() const;
 
+    GLint getProgramBinaryLength() const;
+
   private:
     DISALLOW_COPY_AND_ASSIGN(Program);
 
diff --git a/src/libGLESv2/ProgramBinary.cpp b/src/libGLESv2/ProgramBinary.cpp
index 182e204..b5fb4e5 100644
--- a/src/libGLESv2/ProgramBinary.cpp
+++ b/src/libGLESv2/ProgramBinary.cpp
@@ -7,10 +7,13 @@
 // Program.cpp: Implements the gl::Program class. Implements GL program objects
 // and related functionality. [OpenGL ES 2.0.24] section 2.10.3 page 28.
 
+#include <sstream>
+
 #include "libGLESv2/Program.h"
 #include "libGLESv2/ProgramBinary.h"
 
 #include "common/debug.h"
+#include "common/version.h"
 
 #include "libGLESv2/main.h"
 #include "libGLESv2/Shader.h"
@@ -1585,6 +1588,291 @@
     return true;
 }
 
+bool ProgramBinary::load(InfoLog &infoLog, const void *binary, GLsizei length)
+{
+    std::istringstream stream((const char*) binary, length);
+    stream >> std::boolalpha;
+
+    int format = 0;
+    stream >> format;
+    if (format != GL_PROGRAM_BINARY_ANGLE)
+    {
+        infoLog.append("Invalid program binary format.");
+        return false;
+    }
+
+    int version = 0;
+    stream >> version;
+    if (version != BUILD_REVISION)
+    {
+        infoLog.append("Invalid program binary version.");
+        return false;
+    }
+
+    for (int i = 0; i < MAX_VERTEX_ATTRIBS; ++i)
+    {
+        stream >> mLinkedAttribute[i].type;
+        std::string name;
+        stream >> name;
+        mLinkedAttribute[i].name = name.substr(1, name.length() - 2);
+        stream >> mSemanticIndex[i];
+    }
+
+    for (unsigned int i = 0; i < MAX_TEXTURE_IMAGE_UNITS; ++i)
+    {
+        stream >> mSamplersPS[i].active;
+        stream >> mSamplersPS[i].logicalTextureUnit;
+        
+        int textureType;
+        stream >> textureType;
+        mSamplersPS[i].textureType = (TextureType) textureType;
+    }
+
+    for (unsigned int i = 0; i < MAX_VERTEX_TEXTURE_IMAGE_UNITS_VTF; ++i)
+    {
+        stream >> mSamplersVS[i].active;
+        stream >> mSamplersVS[i].logicalTextureUnit;
+        
+        int textureType;
+        stream >> textureType;
+        mSamplersVS[i].textureType = (TextureType) textureType;
+    }
+
+    stream >> mUsedVertexSamplerRange;
+    stream >> mUsedPixelSamplerRange;
+
+    unsigned int size;
+    stream >> size;
+    if (!stream.good())
+    {
+        infoLog.append("Invalid program binary.");
+        return false;
+    }
+
+    mUniforms.resize(size);
+    for (unsigned int i = 0; i < size; ++i)
+    {
+        GLenum type;
+        std::string _name;
+        unsigned int arraySize;
+
+        stream >> type >> _name >> arraySize;
+
+        mUniforms[i] = new Uniform(type, _name.substr(1, _name.length() - 2), arraySize);
+        
+        stream >> mUniforms[i]->ps.float4Index;
+        stream >> mUniforms[i]->ps.samplerIndex;
+        stream >> mUniforms[i]->ps.boolIndex;
+        stream >> mUniforms[i]->ps.registerCount;
+
+        stream >> mUniforms[i]->vs.float4Index;
+        stream >> mUniforms[i]->vs.samplerIndex;
+        stream >> mUniforms[i]->vs.boolIndex;
+        stream >> mUniforms[i]->vs.registerCount;
+    }
+
+    stream >> size;
+    if (!stream.good())
+    {
+        infoLog.append("Invalid program binary.");
+        return false;
+    }
+
+    mUniformIndex.resize(size);
+    for (unsigned int i = 0; i < size; ++i)
+    {
+        std::string name;
+        stream >> name;
+        mUniformIndex[i].name = name.substr(1, name.length() - 2);
+        stream >> mUniformIndex[i].element;
+        stream >> mUniformIndex[i].index;
+    }
+
+    stream >> mDxDepthRangeLocation;
+    stream >> mDxDepthLocation;
+    stream >> mDxCoordLocation;
+    stream >> mDxHalfPixelSizeLocation;
+    stream >> mDxFrontCCWLocation;
+    stream >> mDxPointsOrLinesLocation;
+
+    unsigned int pixelShaderSize;
+    stream >> pixelShaderSize;
+
+    unsigned int vertexShaderSize;
+    stream >> vertexShaderSize;
+
+    // Skip final newline.
+    stream.ignore(1);
+
+    const char *ptr = (const char*) binary + stream.tellg();
+
+    const D3DCAPS9 *binaryIdentifier = (const D3DCAPS9*) ptr;
+    ptr += sizeof(GUID);
+
+    D3DADAPTER_IDENTIFIER9 *currentIdentifier = getDisplay()->getAdapterIdentifier();
+    if (memcmp(&currentIdentifier->DeviceIdentifier, binaryIdentifier, sizeof(GUID)) != 0)
+    {
+        infoLog.append("Invalid program binary.");
+        return false;
+    }
+
+    const char *pixelShaderFunction = ptr;
+    ptr += pixelShaderSize;
+
+    const char *vertexShaderFunction = ptr;
+    ptr += vertexShaderSize;
+
+    HRESULT result = mDevice->CreatePixelShader(reinterpret_cast<const DWORD*>(pixelShaderFunction), &mPixelExecutable);
+    if (FAILED(result))
+    {
+        infoLog.append("Could not create pixel shader.");
+        return false;
+    }
+
+    result = mDevice->CreateVertexShader(reinterpret_cast<const DWORD*>(vertexShaderFunction), &mVertexExecutable);
+    if (FAILED(result))
+    {
+        infoLog.append("Could not create vertex shader.");
+        mPixelExecutable->Release();
+        mPixelExecutable = NULL;
+        return false;
+    }
+
+    return true;
+}
+
+bool ProgramBinary::save(void* binary, GLsizei bufSize, GLsizei *length)
+{
+    std::ostringstream stream;
+    stream << std::boolalpha;
+
+    stream << GL_PROGRAM_BINARY_ANGLE << std::endl;
+    stream << BUILD_REVISION << std::endl;
+
+    for (unsigned int i = 0; i < MAX_VERTEX_ATTRIBS; ++i)
+    {
+        stream << mLinkedAttribute[i].type << std::endl;
+        stream << "\"" << mLinkedAttribute[i].name << "\"" << std::endl;
+        stream << mSemanticIndex[i] << std::endl << std::endl;
+    }
+
+    for (unsigned int i = 0; i < MAX_TEXTURE_IMAGE_UNITS; ++i)
+    {
+        stream << mSamplersPS[i].active << std::endl;
+        stream << mSamplersPS[i].logicalTextureUnit << std::endl;
+        stream << (int) mSamplersPS[i].textureType << std::endl << std::endl;
+    }
+
+    for (unsigned int i = 0; i < MAX_VERTEX_TEXTURE_IMAGE_UNITS_VTF; ++i)
+    {
+        stream << mSamplersVS[i].active << std::endl;
+        stream << mSamplersVS[i].logicalTextureUnit << std::endl;
+        stream << (int) mSamplersVS[i].textureType << std::endl << std::endl;
+    }
+
+    stream << mUsedVertexSamplerRange << std::endl;
+    stream << mUsedPixelSamplerRange << std::endl << std::endl;
+
+    stream << mUniforms.size() << std::endl;
+    for (unsigned int i = 0; i < mUniforms.size(); ++i)
+    {
+        stream << mUniforms[i]->type << std::endl;
+        stream << "\"" << mUniforms[i]->_name << "\"" << std::endl;
+        stream << mUniforms[i]->arraySize << std::endl << std::endl;
+
+        stream << mUniforms[i]->ps.float4Index << std::endl;
+        stream << mUniforms[i]->ps.samplerIndex << std::endl;
+        stream << mUniforms[i]->ps.boolIndex << std::endl;
+        stream << mUniforms[i]->ps.registerCount << std::endl;
+
+        stream << mUniforms[i]->vs.float4Index << std::endl;
+        stream << mUniforms[i]->vs.samplerIndex << std::endl;
+        stream << mUniforms[i]->vs.boolIndex << std::endl;
+        stream << mUniforms[i]->vs.registerCount << std::endl;
+    }
+
+    stream << mUniformIndex.size() << std::endl;
+    for (unsigned int i = 0; i < mUniformIndex.size(); ++i)
+    {
+        stream << "\"" << mUniformIndex[i].name << "\"" << std::endl;
+        stream << mUniformIndex[i].element << std::endl;
+        stream << mUniformIndex[i].index << std::endl << std::endl;
+    }
+
+    stream << mDxDepthRangeLocation << std::endl;
+    stream << mDxDepthLocation << std::endl;
+    stream << mDxCoordLocation << std::endl;
+    stream << mDxHalfPixelSizeLocation << std::endl;
+    stream << mDxFrontCCWLocation << std::endl;
+    stream << mDxPointsOrLinesLocation << std::endl;
+
+    UINT pixelShaderSize;
+    HRESULT result = mPixelExecutable->GetFunction(NULL, &pixelShaderSize);
+    ASSERT(SUCCEEDED(result));
+    stream << pixelShaderSize << std::endl;
+
+    UINT vertexShaderSize;
+    result = mVertexExecutable->GetFunction(NULL, &vertexShaderSize);
+    ASSERT(SUCCEEDED(result));
+    stream << vertexShaderSize << std::endl;
+
+    std::string text = stream.str();
+
+    D3DADAPTER_IDENTIFIER9 *identifier = getDisplay()->getAdapterIdentifier();
+
+    GLsizei totalLength = text.length() + sizeof(GUID) + pixelShaderSize + vertexShaderSize;
+    if (totalLength > bufSize)
+    {
+        if (length)
+        {
+            *length = 0;
+        }
+
+        return false;
+    }
+
+    if (binary)
+    {
+        char *ptr = (char*) binary;
+
+        memcpy(ptr, text.c_str(), text.length());
+        ptr += text.length();
+
+        memcpy(ptr, &identifier->DeviceIdentifier, sizeof(GUID));
+        ptr += sizeof(GUID);
+
+        result = mPixelExecutable->GetFunction(ptr, &pixelShaderSize);
+        ASSERT(SUCCEEDED(result));
+        ptr += pixelShaderSize;
+
+        result = mVertexExecutable->GetFunction(ptr, &vertexShaderSize);
+        ASSERT(SUCCEEDED(result));
+        ptr += vertexShaderSize;
+
+        ASSERT(ptr - totalLength == binary);
+    }
+
+    if (length)
+    {
+        *length = totalLength;
+    }
+
+    return true;
+}
+
+GLint ProgramBinary::getLength()
+{
+    GLint length;
+    if (save(NULL, INT_MAX, &length))
+    {
+        return length;
+    }
+    else
+    {
+        return 0;
+    }
+}
+
 bool ProgramBinary::link(InfoLog &infoLog, const AttributeBindings &attributeBindings, FragmentShader *fragmentShader, VertexShader *vertexShader)
 {
     if (!fragmentShader || !fragmentShader->isCompiled())
@@ -2477,4 +2765,8 @@
     return mDxPointsOrLinesLocation;
 }
 
+ProgramBinary::Sampler::Sampler() : active(false), logicalTextureUnit(0), textureType(TEXTURE_2D)
+{
+}
+
 }
diff --git a/src/libGLESv2/ProgramBinary.h b/src/libGLESv2/ProgramBinary.h
index 1e9c5e2..8a587b9 100644
--- a/src/libGLESv2/ProgramBinary.h
+++ b/src/libGLESv2/ProgramBinary.h
@@ -10,6 +10,10 @@
 #ifndef LIBGLESV2_PROGRAM_BINARY_H_
 #define LIBGLESV2_PROGRAM_BINARY_H_
 
+#define GL_APICALL
+#include <gles2/gl2.h>
+#include <gles2/gl2ext.h>
+
 #include <d3dx9.h>
 #include <d3dcompiler.h>
 #include <string>
@@ -78,6 +82,10 @@
 // Struct used for correlating uniforms/elements of uniform arrays to handles
 struct UniformLocation
 {
+    UniformLocation()
+    {
+    }
+
     UniformLocation(const std::string &_name, unsigned int element, unsigned int index);
 
     std::string name;
@@ -128,6 +136,10 @@
     void dirtyAllUniforms();
     void applyUniforms();
 
+    bool load(InfoLog &infoLog, const void *binary, GLsizei length);
+    bool save(void* binary, GLsizei bufSize, GLsizei *length);
+    GLint getLength();
+
     bool link(InfoLog &infoLog, const AttributeBindings &attributeBindings, FragmentShader *fragmentShader, VertexShader *vertexShader);
     void getAttachedShaders(GLsizei maxCount, GLsizei *count, GLuint *shaders);
 
@@ -182,6 +194,8 @@
 
     struct Sampler
     {
+        Sampler();
+
         bool active;
         GLint logicalTextureUnit;
         TextureType textureType;
diff --git a/src/libGLESv2/libGLESv2.cpp b/src/libGLESv2/libGLESv2.cpp
index fba253d..ca4ddff 100644
--- a/src/libGLESv2/libGLESv2.cpp
+++ b/src/libGLESv2/libGLESv2.cpp
@@ -3422,7 +3422,7 @@
                 *params = programObject->getActiveUniformMaxLength();
                 return;
               case GL_PROGRAM_BINARY_LENGTH_OES:
-                *params = 0;
+                *params = programObject->getProgramBinaryLength();
                 return;
               default:
                 return error(GL_INVALID_ENUM);
@@ -6818,7 +6818,7 @@
 void __stdcall glGetProgramBinaryOES(GLuint program, GLsizei bufSize, GLsizei *length, 
                                      GLenum *binaryFormat, void *binary)
 {
-    EVENT("(GLenum program = 0x%X, bufSize = %s, length = 0x%0.8p, binaryFormat = 0x%0.8p, binary = 0x%0.8p)",
+    EVENT("(GLenum program = 0x%X, bufSize = %d, length = 0x%0.8p, binaryFormat = 0x%0.8p, binary = 0x%0.8p)",
           program, bufSize, length, binaryFormat, binary);
 
     try
@@ -6841,12 +6841,12 @@
                 return error(GL_INVALID_OPERATION);
             }
 
-            *binaryFormat = GL_PROGRAM_BINARY_ANGLE;
-
-            if (length)
+            if (!programBinary->save(binary, bufSize, length))
             {
-                *length = 0;
+                return error(GL_INVALID_OPERATION);
             }
+
+            *binaryFormat = GL_PROGRAM_BINARY_ANGLE;
         }
     }
     catch(std::bad_alloc&)
@@ -6879,7 +6879,7 @@
                 return error(GL_INVALID_OPERATION);
             }
 
-            programObject->setProgramBinary(NULL);
+            programObject->setProgramBinary(binary, length);
         }
     }
     catch(std::bad_alloc&)
diff --git a/src/libGLESv2/libGLESv2.def b/src/libGLESv2/libGLESv2.def
index ea7769b..5f935c3 100644
--- a/src/libGLESv2/libGLESv2.def
+++ b/src/libGLESv2/libGLESv2.def
@@ -170,6 +170,8 @@
     glVertexAttribDivisorANGLE      @172
     glDrawArraysInstancedANGLE      @173
     glDrawElementsInstancedANGLE    @174
+    glProgramBinaryOES              @175
+    glGetProgramBinaryOES           @176
 
     ; EGL dependencies
     glCreateContext                 @144 NONAME