inline void VL_glGetProgramBinary(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary)
 {
   if (glGetProgramBinary)
     glGetProgramBinary(program, bufSize, length, binaryFormat, binary);
   else
     VL_UNSUPPORTED_FUNC();
 }
void gfx::ShaderProgram::SaveProgramBinary(){
	if(m_Handle <= 0){
		return;
	}
	GLint formatCount;
	glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS,&formatCount);

	GLint* formats = new GLint[formatCount];
	glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, formats);

	GLint length;
	glGetProgramiv(m_Handle,GL_PROGRAM_BINARY_LENGTH, &length);

	GLenum* binaryFormats = new GLenum();
	GLbyte* programBinary = new GLbyte[length];
	glGetProgramBinary(m_Handle,length,nullptr, binaryFormats,programBinary);

	ShaderProgramBinary header;
	header.Size = length;
	strcpy(header.Version,(char*)glGetString(GL_VERSION));

	std::stringstream ss;
	ss << m_Filename;
	ss << ".bin";
	FILE* file = fopen(ss.str().c_str(),"wb");
	fwrite(&header,sizeof(ShaderProgramBinary),1,file);

	fwrite(programBinary,length,1,file);
	fclose(file);
	Logger::Log("Saved program binary", "ShaderProgram", LogSeverity::INFO_MSG);
	delete [] programBinary;
	delete binaryFormats;
	delete formats;
}
Exemple #3
0
static void
linkAndCheck(GLuint program)
{
  GLint programBinaryLength;

  glLinkProgram(program);
  if (!checkProgram(program, "render")) {
    exit(1);
  }
  programBinaryLength = 0;
  glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &programBinaryLength);
  if (programBinaryLength > 0) {
    void* buf;
    GLenum format;
    GLsizei length;
    FILE* file;

    printf("programBinaryLength: %d.\n", programBinaryLength);
    buf = malloc(programBinaryLength);
    glGetProgramBinary(program, programBinaryLength, &length, &format, buf);
    printf("programBinaryFormat: %x.\n", format);
    file = fopen("p_binary.dat", "wb");
    if (file) {
      printf("write binary to p_binary.dat, length: %d.\n", length);
      fwrite(buf, 1, length, file);
      fclose(file);
    }
  }
}
JNIEXPORT void JNICALL Java_org_lwjgl_opengl_GL41_nglGetProgramBinary(JNIEnv *env, jclass clazz, jint program, jint bufSize, jlong length, jlong binaryFormat, jlong binary, jlong function_pointer) {
	GLsizei *length_address = (GLsizei *)(intptr_t)length;
	GLenum *binaryFormat_address = (GLenum *)(intptr_t)binaryFormat;
	GLvoid *binary_address = (GLvoid *)(intptr_t)binary;
	glGetProgramBinaryPROC glGetProgramBinary = (glGetProgramBinaryPROC)((intptr_t)function_pointer);
	glGetProgramBinary(program, bufSize, length_address, binaryFormat_address, binary_address);
}
Exemple #5
0
void Shader::exportAssembly(const string & fileName)
{
	if(!glGetProgramBinary)
	{
		LOG("glGetProgramBinary not supported");
		return;
	}

	// Max file size
	const size_t MAX_SIZE = 1 << 24;

	// Get binary program
	char *binary = new char[MAX_SIZE];
	GLenum format;
	GLint length;
	glGetProgramBinary(m_id, MAX_SIZE, &length, &format, binary);

	// Copy to string
	string content;
	content.resize(length);
	memcpy(&content[0], binary, length);

	// Write to file
	FileSystem::WriteFile(fileName, content);

	// Clean up
	delete[] binary;
}
Exemple #6
0
    std::pair<GLenum,std::vector<char>> Program::getBinary() const
    {
      GLint binaryLength(0);
      glGetProgramiv( getGLId(), GL_PROGRAM_BINARY_LENGTH, &binaryLength );

      GLenum binaryFormat(0);
      std::vector<char> binary(binaryLength);
      glGetProgramBinary( getGLId(), binaryLength, nullptr, &binaryFormat, binary.data() );

      return( std::make_pair( binaryFormat, binary ) );
    }
static void cache_program_binary(GLuint program, const SCP_string& hash) {
	if (!do_shader_caching()) {
		return;
	}
	
	GR_DEBUG_SCOPE("Saving shader binary");

	GLint size;
	glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &size);

	if (size <= 0) {
		// No binary available (I'm looking at you Mesa...)
		return;
	}

	SCP_vector<uint8_t> binary;
	binary.resize((size_t) size);
	GLenum binary_fmt;
	GLsizei length;
	glGetProgramBinary(program, (GLsizei) binary.size(), &length, &binary_fmt, binary.data());
	if (length == 0) {
		return;
	}

	auto base_filename = SCP_string("ogl_shader-") + hash;

	auto metadata_name = base_filename + ".json";
	auto binary_name = base_filename + ".bin";

	auto metadata_fp = cfopen(metadata_name.c_str(), "wb", CFILE_NORMAL, CF_TYPE_CACHE, false,
	                          CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
	if (!metadata_fp) {
		mprintf(("Could not open shader cache metadata file!\n"));
		return;
	}

	auto metadata = json_pack("{sI}", "format", (json_int_t)binary_fmt);
	if (json_dump_callback(metadata, json_write_callback, metadata_fp, 0) != 0) {
		mprintf(("Failed to write shader cache metadata file!\n"));
		cfclose(metadata_fp);
		return;
	}
	cfclose(metadata_fp);
	json_decref(metadata);

	auto binary_fp = cfopen(binary_name.c_str(), "wb", CFILE_NORMAL, CF_TYPE_CACHE, false,
	                        CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
	if (!binary_fp) {
		mprintf(("Could not open shader cache binary file!\n"));
		return;
	}
	cfwrite(binary.data(), 1, (int) binary.size(), binary_fp);
	cfclose(binary_fp);
}
void ProgramShaderCache::Shutdown()
{
	// store all shaders in cache on disk
	if (g_ogl_config.bSupportsGLSLCache && !g_Config.bEnableShaderDebugging)
	{
		for (auto& entry : pshaders)
		{
			// Clear any prior error code
			glGetError();

			if (entry.second.in_cache)
			{
				continue;
			}

			GLint link_status = GL_FALSE, delete_status = GL_TRUE, binary_size = 0;
			glGetProgramiv(entry.second.shader.glprogid, GL_LINK_STATUS, &link_status);
			glGetProgramiv(entry.second.shader.glprogid, GL_DELETE_STATUS, &delete_status);
			glGetProgramiv(entry.second.shader.glprogid, GL_PROGRAM_BINARY_LENGTH, &binary_size);
			if (glGetError() != GL_NO_ERROR || link_status == GL_FALSE || delete_status == GL_TRUE || !binary_size)
			{
				continue;
			}

			std::vector<u8> data(binary_size + sizeof(GLenum));
			u8* binary = &data[sizeof(GLenum)];
			GLenum* prog_format = (GLenum*)&data[0];
			glGetProgramBinary(entry.second.shader.glprogid, binary_size, nullptr, prog_format, binary);
			if (glGetError() != GL_NO_ERROR)
			{
				continue;
			}

			g_program_disk_cache.Append(entry.first, &data[0], binary_size + sizeof(GLenum));
		}

		g_program_disk_cache.Sync();
		g_program_disk_cache.Close();
	}

	glUseProgram(0);

	for (auto& entry : pshaders)
	{
		entry.second.Destroy();
	}
	pshaders.clear();

	pixel_uid_checker.Invalidate();
	vertex_uid_checker.Invalidate();

	delete s_buffer;
	s_buffer = nullptr;
}
Exemple #9
0
/*
 *
 * Core in:
 * OpenGL    : 4.1
 * OpenGLES  : 3.1
 */
void rglGetProgramBinary( 	GLuint program,
  	GLsizei bufsize,
  	GLsizei *length,
  	GLenum *binaryFormat,
  	void *binary)
{
#if !defined(HAVE_OPENGLES) || defined(HAVE_OPENGLES) && defined(HAVE_OPENGLES3)
   glGetProgramBinary(program, bufsize, length, binaryFormat, binary);
#else
   printf("WARNING! Not implemented.\n");
#endif
}
Exemple #10
0
// This function will get the binary program. Normally it must be used a caching
// solution but Nvidia also incorporates the ASM dump. Asm is nice because it allow
// to have an overview of the program performance based on the instruction number
// Note: initially I was using cg offline compiler but it doesn't support latest
// GLSL improvement (unfortunately).
int GSShaderOGL::DumpAsm(const std::string& file, GLuint p)
{
	if (!GLLoader::nvidia_buggy_driver) return 0;

	GLint   binaryLength;
	glGetProgramiv(p, GL_PROGRAM_BINARY_LENGTH, &binaryLength);

	char* binary = new char[binaryLength+4];
	GLenum binaryFormat;
	glGetProgramBinary(p, binaryLength, NULL, &binaryFormat, binary);

	FILE* outfile = fopen(file.c_str(), "w");
	ASSERT(outfile);

	// Search the magic number "!!"
	int asm_ = 0;
	while (asm_ < binaryLength && (binary[asm_] != '!' || binary[asm_+1] != '!')) {
		asm_ += 1;
	}

	int instructions = -1;
	if (asm_ < binaryLength) {
		// Now print asm as text
		char* asm_txt = strtok(&binary[asm_], "\n");
		while (asm_txt != NULL && (strncmp(asm_txt, "END", 3) || !strncmp(asm_txt, "ENDIF", 5))) {
			if (!strncmp(asm_txt, "OUT", 3) || !strncmp(asm_txt, "TEMP", 4) || !strncmp(asm_txt, "LONG", 4)) {
				instructions = 0;
			} else if (instructions >= 0) {
				if (instructions == 0)
					fprintf(outfile, "\n");
				instructions++;
			}

			fprintf(outfile, "%s\n", asm_txt);
			asm_txt = strtok(NULL, "\n");
		}
		fprintf(outfile, "\nFound %d instructions\n", instructions);
	}
	fclose(outfile);

	if (instructions < 0) {
		// RAW dump in case of error
		fprintf(stderr, "Error: failed to find the number of instructions!\n");
		outfile = fopen(file.c_str(), "wb");
		fwrite(binary, binaryLength, 1, outfile);
		fclose(outfile);
		ASSERT(0);
	}

	delete[] binary;

	return instructions;
}
	bool saveProgram(GLuint ProgramName, std::string const & String)
	{
		GLint Size(0);
		GLenum Format(0);

		glGetProgramiv(ProgramName, GL_PROGRAM_BINARY_LENGTH, &Size);
		std::vector<glm::byte> Data(Size);
		glGetProgramBinary(ProgramName, Size, nullptr, &Format, &Data[0]);
		saveBinary(String, Format, Data, Size);

		return this->checkError("saveProgram");
	}
Exemple #12
0
static void fgm_program_to_binary(GLuint program, fe_iov *binfile) {
#ifndef FE_TARGET_EMSCRIPTEN
    GLsizei binlen;
    glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binlen);
    binfile->len = HEADER_SIZE + binlen;
    binfile->base = fe_mem_heapalloc(binfile->len, char, "fe_gl prog binary");
    GLenum binfmt;
    glGetProgramBinary(program, binlen, NULL, &binfmt, ((char*)binfile->base)+HEADER_SIZE); // >= ES 3.0
    *(fe_timestamp*)binfile->base = fe_hw_swap64_host_to_net(fe_timestamp_get_now());
    *(GLenum*)(((char*)binfile->base)+sizeof(fe_timestamp)) = fe_hw_swap32_host_to_net(binfmt);
    fe_logv(TAG, "Saved binary with format 0x%x.\n", binfmt);
#endif /* FE_TARGET_EMSCRIPTEN */
}
ProgramBinary * ProgramBinaryImplementation_GetProgramBinaryARB::getProgramBinary(const Program * program) const
{
    int length = program->get(GL_PROGRAM_BINARY_LENGTH);

    if (length == 0)
        return nullptr;

    GLenum format;
    std::vector<char> binary(length);

    glGetProgramBinary(program->id(), length, nullptr, &format, binary.data());

    return new ProgramBinary(format, binary);
}
Exemple #14
0
    std::pair<GLenum, std::unique_ptr<char[]>> ShaderProgram::getBinary() const
    {
        if(ext::programBinary())
        {
            GLenum format = 0;
            std::unique_ptr<char[]> binary(new char[getBinarySize()]);

            glCheck(glGetProgramBinary(program_, getBinarySize(), nullptr, &format, void_cast(binary)));

            return std::make_pair(format, std::move(binary));
        }
        else
            return std::make_pair(0, nullptr);
    }
void ProgramShaderCache::Shutdown()
{
	// store all shaders in cache on disk
	if (g_ogl_config.bSupportsGLSLCache && !g_Config.bEnableShaderDebugging)
	{
		for (auto& entry : pshaders)
		{
			if (entry.second.in_cache)
			{
				continue;
			}

			GLint binary_size;
			glGetProgramiv(entry.second.shader.glprogid, GL_PROGRAM_BINARY_LENGTH, &binary_size);
			if (!binary_size)
			{
				continue;
			}

			u8 *data = new u8[binary_size+sizeof(GLenum)];
			u8 *binary = data + sizeof(GLenum);
			GLenum *prog_format = (GLenum*)data;
			glGetProgramBinary(entry.second.shader.glprogid, binary_size, nullptr, prog_format, binary);

			g_program_disk_cache.Append(entry.first, data, binary_size+sizeof(GLenum));
			delete [] data;
		}

		g_program_disk_cache.Sync();
		g_program_disk_cache.Close();
	}

	glUseProgram(0);

	for (auto& entry : pshaders)
	{
		entry.second.Destroy();
	}
	pshaders.clear();

	pixel_uid_checker.Invalidate();
	vertex_uid_checker.Invalidate();

	delete s_buffer;
	s_buffer = nullptr;
}
void ProgramShaderCache::Shutdown(void)
{
	// store all shaders in cache on disk
	if (g_ogl_config.bSupportsGLSLCache)
	{
		PCache::iterator iter = pshaders.begin();
		for (; iter != pshaders.end(); ++iter)
		{
			if(iter->second.in_cache) continue;
			
			GLint binary_size;
			glGetProgramiv(iter->second.shader.glprogid, GL_PROGRAM_BINARY_LENGTH, &binary_size);
			if(!binary_size) continue;
			
			u8 *data = new u8[binary_size+sizeof(GLenum)];
			u8 *binary = data + sizeof(GLenum);
			GLenum *prog_format = (GLenum*)data;
			glGetProgramBinary(iter->second.shader.glprogid, binary_size, NULL, prog_format, binary);
			
			g_program_disk_cache.Append(iter->first, data, binary_size+sizeof(GLenum));
			delete [] data;
		}

		g_program_disk_cache.Sync();
		g_program_disk_cache.Close();
	}

	glUseProgram(0);
	
	PCache::iterator iter = pshaders.begin();
	for (; iter != pshaders.end(); ++iter)
		iter->second.Destroy();
	pshaders.clear();

	pixel_uid_checker.Invalidate();
	vertex_uid_checker.Invalidate();

	if (g_ActiveConfig.backend_info.bSupportsGLSLUBO)
	{
		delete s_buffer;
		s_buffer = 0;
		delete [] s_ubo_buffer;
		s_ubo_buffer = 0;
	}
}
Exemple #17
0
	int gl_GetProgramBinary(State & state){
		GLuint program = (GLuint)state.stack->to<int>(1);
		GLint maxLength;
		GLsizei length;

		glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &maxLength);

		GLenum binaryFormat;
		GLchar * binary = new char[maxLength];

		glGetProgramBinary(program, maxLength, &length, &binaryFormat, binary);
		
		state.stack->push<int>(binaryFormat);
		state.stack->pushLString(binary, length);

		delete[] binary;
		return 2;
	}
Exemple #18
0
	ByteArray Shader::GetBinary() const
	{
		ByteArray byteArray;

		Context::EnsureContext();

		GLint binaryLength = 0;
		glGetProgramiv(m_program, GL_PROGRAM_BINARY_LENGTH, &binaryLength);

		if (binaryLength > 0)
		{
			byteArray.Reserve(sizeof(UInt64) + binaryLength);

			UInt8* buffer = byteArray.GetBuffer();

			GLenum binaryFormat;
			glGetProgramBinary(m_program, binaryLength, nullptr, &binaryFormat, &buffer[sizeof(UInt64)]);

			// On stocke le format au début du binaire
			*reinterpret_cast<UInt64*>(&buffer[0]) = binaryFormat;
		}

		return byteArray;
	}
Exemple #19
0
bool
HdStGLSLProgram::Link()
{
    HD_TRACE_FUNCTION();
    HF_MALLOC_TAG_FUNCTION();

    if (!glLinkProgram) return false; // glew initialized

    GLuint program = _program.GetId();
    if (program == 0) {
        TF_CODING_ERROR("At least one shader has to be compiled before linking.");
        return false;
    }

    bool dumpShaderBinary = TfDebug::IsEnabled(HD_DUMP_SHADER_BINARY);

    if (dumpShaderBinary) {
        // set RETRIEVABLE_HINT to true for getting program binary length.
        // note: Actually the GL driver may recompile the program dynamically on
        // some state changes, so the size of program could be inaccurate.
        glProgramParameteri(program, GL_PROGRAM_BINARY_RETRIEVABLE_HINT,
                            GL_TRUE);
    }

    // link
    glLinkProgram(program);

    std::string logString;
    bool success = true;
    if (!HdStGLUtils::GetProgramLinkStatus(program, &logString)) {
        // XXX:validation
        TF_WARN("Failed to link shader: %s", logString.c_str());
        success = false;
    }

    // initial program size
    GLint size;
    glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &size);

    // update the program resource allocation
    _program.SetAllocation(program, size);

    // create an uniform buffer
    GLuint uniformBuffer = _uniformBuffer.GetId();
    if (uniformBuffer == 0) {
        glGenBuffers(1, &uniformBuffer);
        _uniformBuffer.SetAllocation(uniformBuffer, 0);
    }

    // binary dump out
    if (dumpShaderBinary) {
        std::vector<char> bin(size);
        GLsizei len;
        GLenum format;
        glGetProgramBinary(program, size, &len, &format, &bin[0]);
        static int id = 0;
        std::stringstream fname;
        fname << "program" << id++ << ".bin";

        std::fstream output(fname.str().c_str(), std::ios::out|std::ios::binary);
        output.write(&bin[0], size);
        output.close();

        std::cout << "Write " << fname.str() << " (size=" << size << ")\n";
    }

    return success;
}
    void GLSLProgramPipeline::compileAndLink()
	{        
        OGRE_CHECK_GL_ERROR(glGenProgramPipelines(1, &mGLProgramPipelineHandle));
        OGRE_CHECK_GL_ERROR(glBindProgramPipeline(mGLProgramPipelineHandle));

        mVertexArrayObject = new GL3PlusVertexArrayObject();
        mVertexArrayObject->bind();

        compileIndividualProgram(mVertexProgram);
        compileIndividualProgram(mFragmentProgram);
        compileIndividualProgram(mGeometryProgram);
        compileIndividualProgram(mDomainProgram);
        compileIndividualProgram(mHullProgram);
        compileIndividualProgram(mComputeProgram);

		if(mLinked)
		{
			if ( GpuProgramManager::getSingleton().getSaveMicrocodesToCache() )
			{
				// Add to the microcode to the cache
				String name;
				name = getCombinedName();

				// Get buffer size
				GLint binaryLength = 0;

				OGRE_CHECK_GL_ERROR(glGetProgramiv(mGLProgramPipelineHandle, GL_PROGRAM_BINARY_LENGTH, &binaryLength));

                // Create microcode
                GpuProgramManager::Microcode newMicrocode = 
                    GpuProgramManager::getSingleton().createMicrocode(binaryLength + sizeof(GLenum));

				// Get binary
				OGRE_CHECK_GL_ERROR(glGetProgramBinary(mGLProgramPipelineHandle, binaryLength, NULL, (GLenum *)newMicrocode->getPtr(), newMicrocode->getPtr() + sizeof(GLenum)));

        		// Add to the microcode to the cache
				GpuProgramManager::getSingleton().addMicrocodeToCache(name, newMicrocode);
			}
            if(mVertexProgram && mVertexProgram->isLinked())
            {
                OGRE_CHECK_GL_ERROR(glUseProgramStages(mGLProgramPipelineHandle, GL_VERTEX_SHADER_BIT, mVertexProgram->getGLSLProgram()->getGLProgramHandle()));
            }
            if(mFragmentProgram && mFragmentProgram->isLinked())
            {
                OGRE_CHECK_GL_ERROR(glUseProgramStages(mGLProgramPipelineHandle, GL_FRAGMENT_SHADER_BIT, mFragmentProgram->getGLSLProgram()->getGLProgramHandle()));
            }
            if(mGeometryProgram && mGeometryProgram->isLinked())
            {
                OGRE_CHECK_GL_ERROR(glUseProgramStages(mGLProgramPipelineHandle, GL_GEOMETRY_SHADER_BIT, mGeometryProgram->getGLSLProgram()->getGLProgramHandle()));
            }
            if(mDomainProgram && mDomainProgram->isLinked())
            {
                OGRE_CHECK_GL_ERROR(glUseProgramStages(mGLProgramPipelineHandle, GL_TESS_EVALUATION_SHADER_BIT, mDomainProgram->getGLSLProgram()->getGLProgramHandle()));
            }
            if(mHullProgram && mHullProgram->isLinked())
            {
                OGRE_CHECK_GL_ERROR(glUseProgramStages(mGLProgramPipelineHandle, GL_TESS_CONTROL_SHADER_BIT, mHullProgram->getGLSLProgram()->getGLProgramHandle()));
            }
            if(mComputeProgram && mComputeProgram->isLinked())
            {
                OGRE_CHECK_GL_ERROR(glUseProgramStages(mGLProgramPipelineHandle, GL_COMPUTE_SHADER_BIT, mComputeProgram->getGLSLProgram()->getGLProgramHandle()));
            }

            // Validate pipeline
            logObjectInfo( getCombinedName() + String("GLSL program pipeline result : "), mGLProgramPipelineHandle );

//            if(getGLSupport()->checkExtension("GL_KHR_debug") || gl3wIsSupported(4, 3))
//                glObjectLabel(GL_PROGRAM_PIPELINE, mGLProgramPipelineHandle, 0,
//                                 (mVertexProgram->getName() + "/" + mFragmentProgram->getName()).c_str());
		}
	}
    void GLSLSeparableProgram::loadIndividualProgram(GLSLShader *program)
    {
        if (program && !program->isLinked())
        {
            GLint linkStatus = 0;

            String programName = program->getName();

            GLuint programHandle = program->getGLProgramHandle();

            OGRE_CHECK_GL_ERROR(glProgramParameteri(programHandle, GL_PROGRAM_SEPARABLE, GL_TRUE));
            //if (GpuProgramManager::getSingleton().getSaveMicrocodesToCache())
            OGRE_CHECK_GL_ERROR(glProgramParameteri(programHandle, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE));

            // Use precompiled program if possible.
            bool microcodeAvailableInCache = GpuProgramManager::getSingleton().isMicrocodeAvailableInCache(programName);
            if (microcodeAvailableInCache)
            {
                GpuProgramManager::Microcode cacheMicrocode =
                    GpuProgramManager::getSingleton().getMicrocodeFromCache(programName);
                cacheMicrocode->seek(0);

                GLenum binaryFormat = 0;
                cacheMicrocode->read(&binaryFormat, sizeof(GLenum));

                GLint binaryLength = cacheMicrocode->size() - sizeof(GLenum);

                OGRE_CHECK_GL_ERROR(glProgramBinary(programHandle,
                                                    binaryFormat,
                                                    cacheMicrocode->getPtr() + sizeof(GLenum),
                                                    binaryLength));

                OGRE_CHECK_GL_ERROR(glGetProgramiv(programHandle, GL_LINK_STATUS, &linkStatus));
                if (!linkStatus)
                    logObjectInfo("Could not use cached binary " + programName, programHandle);
            }

            // Compilation needed if precompiled program is
            // unavailable or failed to link.
            if (!linkStatus)
            {
                try
                {
                    program->compile(true);
                }
                catch (Exception& e)
                {
                    LogManager::getSingleton().stream() << e.getDescription();
                    mTriedToLinkAndFailed = true;
                    return;
                }

                program->attachToProgramObject(programHandle);
                OGRE_CHECK_GL_ERROR(glLinkProgram(programHandle));
                OGRE_CHECK_GL_ERROR(glGetProgramiv(programHandle, GL_LINK_STATUS, &linkStatus));

                // Binary cache needs an update.
                microcodeAvailableInCache = false;
            }

            program->setLinked(linkStatus);
            mLinked = linkStatus;

            mTriedToLinkAndFailed = !linkStatus;

            logObjectInfo( getCombinedName() + String("GLSL program result : "), programHandle );

            if (program->getType() == GPT_VERTEX_PROGRAM)
                setSkeletalAnimationIncluded(program->isSkeletalAnimationIncluded());

            // Add the microcode to the cache.
            if (!microcodeAvailableInCache && mLinked &&
                GpuProgramManager::getSingleton().getSaveMicrocodesToCache() )
            {
                // Get buffer size.
                GLint binaryLength = 0;

                OGRE_CHECK_GL_ERROR(glGetProgramiv(programHandle, GL_PROGRAM_BINARY_LENGTH, &binaryLength));

                // Create microcode.
                GpuProgramManager::Microcode newMicrocode =
                    GpuProgramManager::getSingleton().createMicrocode((unsigned long)binaryLength + sizeof(GLenum));

                // Get binary.
                OGRE_CHECK_GL_ERROR(glGetProgramBinary(programHandle, binaryLength, NULL, (GLenum *)newMicrocode->getPtr(), newMicrocode->getPtr() + sizeof(GLenum)));

                // std::vector<uchar> buffer(binaryLength);
                // GLenum format(0);
                // OGRE_CHECK_GL_ERROR(glGetProgramBinary(programHandle, binaryLength, NULL, &format, &buffer[0]));

                // GLenum binaryFormat = 0;
                // std::vector<uchar> binaryData(binaryLength);
                // newMicrocode->read(&binaryFormat, sizeof(GLenum));
                // newMicrocode->read(&binaryData[0], binaryLength);

                GpuProgramManager::getSingleton().addMicrocodeToCache(programName, newMicrocode);
            }
        }
    }
	//-----------------------------------------------------------------------
	void GLSLLinkProgram::compileAndLink()
	{
        mVertexArrayObject = new GL3PlusVertexArrayObject();
        mVertexArrayObject->bind();

		// Compile and attach Vertex Program
		if (mVertexProgram)
        {
            if (!mVertexProgram->getGLSLProgram()->compile(true))
            {
                mTriedToLinkAndFailed = true;
                return;
            }
            mVertexProgram->getGLSLProgram()->attachToProgramObject(mGLProgramHandle);
            setSkeletalAnimationIncluded(mVertexProgram->isSkeletalAnimationIncluded());
        }

		// Compile and attach Fragment Program
		if (mFragmentProgram)
        {
            if (!mFragmentProgram->getGLSLProgram()->compile(true))
            {
                mTriedToLinkAndFailed = true;
                return;
            }
            mFragmentProgram->getGLSLProgram()->attachToProgramObject(mGLProgramHandle);
        }

        // Compile and attach Geometry Program
		if (mGeometryProgram)
		{
			if (!mGeometryProgram->getGLSLProgram()->compile(true))
			{
				return;
			}

			mGeometryProgram->getGLSLProgram()->attachToProgramObject(mGLProgramHandle);
		}

        // Compile and attach Tessellation Control Program
        if (mHullProgram)
		{
			if (!mHullProgram->getGLSLProgram()->compile(true))
			{
				return;
			}

			mHullProgram->getGLSLProgram()->attachToProgramObject(mGLProgramHandle);
		}

        // Compile and attach Tessellation Evaluation Program
        if (mDomainProgram)
		{
			if (!mDomainProgram->getGLSLProgram()->compile(true))
			{
				return;
			}

			mDomainProgram->getGLSLProgram()->attachToProgramObject(mGLProgramHandle);
		}

        // Compile and attach Compute Program
        if (mComputeProgram)
		{
			if (!mComputeProgram->getGLSLProgram()->compile(true))
			{
				return;
			}

			mComputeProgram->getGLSLProgram()->attachToProgramObject(mGLProgramHandle);
		}

		// the link
		OGRE_CHECK_GL_ERROR(glLinkProgram( mGLProgramHandle ));
        OGRE_CHECK_GL_ERROR(glGetProgramiv( mGLProgramHandle, GL_LINK_STATUS, &mLinked ));

		mTriedToLinkAndFailed = !mLinked;

		logObjectInfo( getCombinedName() + String(" GLSL link result : "), mGLProgramHandle );

        if(glIsProgram(mGLProgramHandle))
        {
            OGRE_CHECK_GL_ERROR(glValidateProgram(mGLProgramHandle));
        }
		logObjectInfo( getCombinedName() + String(" GLSL validation result : "), mGLProgramHandle );

		if(mLinked)
		{
			if ( GpuProgramManager::getSingleton().getSaveMicrocodesToCache() )
			{
				// add to the microcode to the cache
				String name;
				name = getCombinedName();

				// get buffer size
				GLint binaryLength = 0;
				OGRE_CHECK_GL_ERROR(glGetProgramiv(mGLProgramHandle, GL_PROGRAM_BINARY_LENGTH, &binaryLength));

                // create microcode
                GpuProgramManager::Microcode newMicrocode = 
                    GpuProgramManager::getSingleton().createMicrocode(binaryLength + sizeof(GLenum));

				// get binary
				OGRE_CHECK_GL_ERROR(glGetProgramBinary(mGLProgramHandle, binaryLength, NULL, (GLenum *)newMicrocode->getPtr(), newMicrocode->getPtr() + sizeof(GLenum)));

        		// add to the microcode to the cache
				GpuProgramManager::getSingleton().addMicrocodeToCache(name, newMicrocode);
			}
		}
	}
Exemple #23
0
/*! adds a shader object
 */
shader_object* shader::add_shader_src(const string& identifier, const string& option, ext::GLSL_VERSION glsl_version, const char* vs_text, const char* gs_text, const char* fs_text) {
	// success flag (if it's 1 (true), we successfully created a shader object)
	int success;
	GLchar info_log[A2E_SHADER_LOG_SIZE];
	
	if(gs_text != nullptr && strcmp(gs_text, "") == 0) gs_text = nullptr;
	
	// create a new shader object if none exists for this identifier
	if(shaders.count(identifier) == 0) {
		shaders[identifier] = new shader_object(identifier);
	}
	
	// add a new program object to this shader
	shaders[identifier]->programs.push_back(new shader_object::internal_shader_object());
	if(option != "") {
		shaders[identifier]->options[option] = shaders[identifier]->programs.back();
	}
	shader_object::internal_shader_object& shd_obj = *shaders[identifier]->programs.back();
	shaders[identifier]->glsl_version = std::max(shaders[identifier]->glsl_version, glsl_version);
	
	// create the vertex shader object
	shd_obj.vertex_shader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(shd_obj.vertex_shader, 1, (GLchar const**)&vs_text, nullptr);
	glCompileShader(shd_obj.vertex_shader);
	glGetShaderiv(shd_obj.vertex_shader, GL_COMPILE_STATUS, &success);
	if(!success) {
		glGetShaderInfoLog(shd_obj.vertex_shader, A2E_SHADER_LOG_SIZE, nullptr, info_log);
		log_error("Error in vertex shader \"%s/%s\" compilation!", identifier, option);
		log_pretty_print(info_log, vs_text);
		return 0;
	}
	
#if !defined(FLOOR_IOS)
	// create the geometry shader object
	if(gs_text != nullptr && strcmp(gs_text, "") != 0) {
		shd_obj.geometry_shader = glCreateShader(GL_GEOMETRY_SHADER);
		glShaderSource(shd_obj.geometry_shader, 1, (GLchar const**)&gs_text, nullptr);
		glCompileShader(shd_obj.geometry_shader);
		glGetShaderiv(shd_obj.geometry_shader, GL_COMPILE_STATUS, &success);
		if(!success) {
			glGetShaderInfoLog(shd_obj.geometry_shader, A2E_SHADER_LOG_SIZE, nullptr, info_log);
			log_error("Error in geometry shader \"%s/%s\" compilation!", identifier, option);
			log_pretty_print(info_log, gs_text);
			return 0;
		}
	}
	else shd_obj.geometry_shader = 0;
#else
	if(gs_text != nullptr && strcmp(gs_text, "") != 0) {
		log_error("geometry shaders are not supported in OpenGL ES 2.0 or 3.0!");
	}
	shd_obj.geometry_shader = 0;
#endif
	
	// create the fragment shader object
	shd_obj.fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(shd_obj.fragment_shader, 1, (GLchar const**)&fs_text, nullptr);
	glCompileShader(shd_obj.fragment_shader);
	glGetShaderiv(shd_obj.fragment_shader, GL_COMPILE_STATUS, &success);
	if(!success) {
		glGetShaderInfoLog(shd_obj.fragment_shader, A2E_SHADER_LOG_SIZE, nullptr, info_log);
		log_error("Error in fragment shader \"%s/%s\" compilation!", identifier, option);
		log_pretty_print(info_log, fs_text);
		return 0;
	}
	
	// create the program object
	shd_obj.program = glCreateProgram();
	// attach the vertex and fragment shader progam to it
	glAttachShader(shd_obj.program, shd_obj.vertex_shader);
	glAttachShader(shd_obj.program, shd_obj.fragment_shader);
	if(gs_text != nullptr) {
		glAttachShader(shd_obj.program, shd_obj.geometry_shader);
	}

	// WIP: program binary
#if defined(A2E_DEBUG_PROGRAM_BINARY)
#if defined(__APPLE__)
	{
		fstream progcode_vs("/tmp/a2e_shd_code_vs.glsl", fstream::out);
		progcode_vs << vs_text << endl;
		progcode_vs.close();
		if(shd_obj.geometry_shader != 0) {
			fstream progcode_gs("/tmp/a2e_shd_code_gs.glsl", fstream::out);
			progcode_gs << gs_text << endl;
			progcode_gs.close();
		}
		fstream progcode_fs("/tmp/a2e_shd_code_fs.glsl", fstream::out);
		progcode_fs << fs_text << endl;
		progcode_fs.close();
		
		string output_vs = "", output_gs = "", output_fs = "";
		core::system("cgc -profile gp4vp -strict -oglsl /tmp/a2e_shd_code_vs.glsl 2>&1", output_vs);
		if(shd_obj.geometry_shader != 0) {
			core::system("cgc -profile gp4gp -strict -oglsl -po POINT /tmp/a2e_shd_code_gs.glsl 2>&1", output_gs);
		}
		core::system("cgc -profile gp4fp -strict -oglsl /tmp/a2e_shd_code_fs.glsl 2>&1", output_fs);
		
		system("rm /tmp/a2e_shd_code_vs.glsl");
		if(shd_obj.geometry_shader != 0) {
			system("rm /tmp/a2e_shd_code_gs.glsl");
		}
		system("rm /tmp/a2e_shd_code_fs.glsl");
		
		//
		shader_debug::add(identifier, option, output_vs, output_gs, output_fs);
	}
#else
	if(exts->is_ext_supported("GL_ARB_get_program_binary")) glProgramParameteri(shd_obj.program, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
#endif
#endif
	
	// now link the program object
	glLinkProgram(shd_obj.program);
	glGetProgramiv(shd_obj.program, GL_LINK_STATUS, &success);
	if(!success) {
		glGetProgramInfoLog(shd_obj.program, A2E_SHADER_LOG_SIZE, nullptr, info_log);
		log_error("Error in program \"%s/%s\" linkage!\nInfo log: %s", identifier, option, info_log);
		return 0;
	}
	glUseProgram(shd_obj.program);
	
	// bind frag data locations (frag_color, frag_color_2, frag_color_3, ...)
	bool fd_relink = false;
#if !defined(FLOOR_IOS)
	const unsigned int max_draw_buffers = exts->get_max_draw_buffers();
	for(unsigned int i = 0; i < max_draw_buffers; i++) {
		string name = "frag_color";
		if(i >= 1) name += "_"+to_string(i+1);
		const GLint location = glGetFragDataLocation(shd_obj.program, name.c_str());
		
		// check if the frag color exists and must be bound to a different location
		if(location >= 0 && i != (GLuint)location) {
			// if so, bind it to the correct location
			glBindFragDataLocation(shd_obj.program, i, name.c_str());
			fd_relink = true;
		}
	}
#else
	// NOTE: MRTs are not support in OpenGL ES 2.0
	// in OpenGL ES 3.0 the locations are already set in the shader
#endif
	if(fd_relink) {
		// program must be linked again after the frag data locations were modified
		// (double-linkage sucks, but there's no other way in opengl 3.2 ...)
		glLinkProgram(shd_obj.program);
	}
	
	// WIP: program binary
#if defined(A2E_DEBUG_PROGRAM_BINARY)
#if !defined(__APPLE__)
	if(exts->is_ext_supported("GL_ARB_get_program_binary")) {
		GLint binary_length = 0;
		glGetProgramiv(shd_obj.program, GL_PROGRAM_BINARY_LENGTH, &binary_length);

		unsigned char* binary = new unsigned char[binary_length];
		GLenum binary_format = 0;
		glGetProgramBinary(shd_obj.program, binary_length, nullptr, &binary_format, binary);

		string binary_fname = "shader_binary_"+identifier+"_"+to_string(shaders[identifier]->programs.size()-1)+".dat";
		f->open_file(binary_fname.c_str(), file_io::OT_WRITE_BINARY);
		f->write_block((const char*)binary, binary_length, false);
		f->close_file();

		delete [] binary;
	}
#endif
#endif
	
	// grab number and names of all attributes and uniforms and get their locations (needs to be done before validation, b/c we have to set sampler locations)
	GLint attr_count = 0, uni_count = 0, max_attr_len = 0, max_uni_len = 0;
	GLint var_location = 0;
	GLint var_size = 0;
	GLenum var_type = 0;
	
	glGetProgramiv(shd_obj.program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_attr_len);
	glGetProgramiv(shd_obj.program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uni_len);
	glGetProgramiv(shd_obj.program, GL_ACTIVE_ATTRIBUTES, &attr_count);
	glGetProgramiv(shd_obj.program, GL_ACTIVE_UNIFORMS, &uni_count);
	max_attr_len+=2;
	max_uni_len+=2;
	
#if !defined(FLOOR_IOS) || defined(PLATFORM_X64)
	GLint uni_block_count = 0, max_uni_block_len = 0;
	glGetProgramiv(shd_obj.program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_uni_block_len);
	glGetProgramiv(shd_obj.program, GL_ACTIVE_UNIFORM_BLOCKS, &uni_block_count);
	max_uni_block_len+=2;
#endif
		
	// note: this may report weird attribute/uniform names (and locations), if uniforms/attributes are optimized away by the compiler
	
	bool print_debug_info = false;
	//if(identifier == "SIMPLE") {
	/*if(identifier == "FONT") {
		print_debug_info = true;
		cout << endl << "## " << identifier << "::" << option << endl;
	}*/
	
	GLchar* attr_name = new GLchar[(size_t)max_attr_len];
	if(print_debug_info) log_undecorated("## shader: %s", identifier);
	if(print_debug_info) log_undecorated("GL_ACTIVE_ATTRIBUTES: %u", attr_count);
	for(GLuint attr = 0; attr < (GLuint)attr_count; attr++) {
		memset(attr_name, 0, (size_t)max_attr_len);
		glGetActiveAttrib(shd_obj.program, attr, max_attr_len-1, nullptr, &var_size, &var_type, attr_name);
		var_location = glGetAttribLocation(shd_obj.program, attr_name);
		if(var_location < 0) {
			if(print_debug_info) log_error("Warning: could not get location for attribute \"%s\" in shader #%s/%s!", attr_name, identifier, option);
			continue;
		}
		if(print_debug_info) log_undecorated("attribute #%u: %s", var_location, attr_name);
		
		string attribute_name = attr_name;
		if(attribute_name.find("[") != string::npos) attribute_name = attribute_name.substr(0, attribute_name.find("["));
		shd_obj.attributes.insert(make_pair(attribute_name,
											shader_object::internal_shader_object::shader_variable((size_t)var_location, (size_t)var_size, var_type)));
	}
	delete [] attr_name;
	
	GLchar* uni_name = new GLchar[(size_t)max_uni_len];
	if(print_debug_info) log_undecorated("GL_ACTIVE_UNIFORMS: %u", uni_count);
	for(GLuint uniform = 0; uniform < (GLuint)uni_count; uniform++) {
		memset(uni_name, 0, (size_t)max_uni_len);
		glGetActiveUniform(shd_obj.program, uniform, max_uni_len-1, nullptr, &var_size, &var_type, uni_name);
		var_location = glGetUniformLocation(shd_obj.program, uni_name);
		if(var_location < 0) {
			if(print_debug_info) log_error("Warning: could not get location for uniform \"%s\" in shader #%s/%s!", uni_name, identifier, option);
			continue;
		}
		if(print_debug_info) log_undecorated("uniform #%u: %s", var_location, uni_name);
		string uniform_name = uni_name;
		if(uniform_name.find("[") != string::npos) uniform_name = uniform_name.substr(0, uniform_name.find("["));
		shd_obj.uniforms.insert(make_pair(uniform_name,
										  shader_object::internal_shader_object::shader_variable((size_t)var_location, (size_t)var_size, var_type)));
		
		// if the uniform is a sampler, add it to the sampler mapping (with increasing id/num)
		if(shader_class::is_gl_sampler_type(var_type)) {
			shd_obj.samplers.insert(make_pair(uniform_name, shd_obj.samplers.size()));
			
			// while we are at it, also set the sampler location to a dummy value (this has to be done to satisfy program validation)
			glUniform1i(var_location, (GLint)shd_obj.samplers.size()-1);
		}
	}
	delete [] uni_name;
	
#if !defined(FLOOR_IOS) || defined(PLATFORM_X64)
	GLchar* uni_block_name = new GLchar[(size_t)max_uni_block_len];
	if(print_debug_info) log_undecorated("GL_ACTIVE_UNIFORM_BLOCKS: %u", uni_block_count);
	for(GLuint block = 0; block < (GLuint)uni_block_count; block++) {
		memset(uni_block_name, 0, (size_t)max_uni_block_len);
		glGetActiveUniformBlockName(shd_obj.program, (GLuint)block, max_uni_block_len-1, nullptr, uni_block_name);
		
		GLuint block_index = glGetUniformBlockIndex(shd_obj.program, uni_block_name);
		if(block_index == GL_INVALID_INDEX) {
			if(print_debug_info) log_error("Warning: could not get index for uniform block \"%s\" in shader #%s/%s!", uni_block_name, identifier, option);
			continue;
		}
		
		GLint data_size = 0;
		glGetActiveUniformBlockiv(shd_obj.program, block_index, GL_UNIFORM_BLOCK_DATA_SIZE, &data_size);
		if(print_debug_info) log_undecorated("uniform block #%u (size: %u): %s",
									 block_index, data_size, uni_block_name);
		const string uniform_block_name = uni_block_name;
		
		shd_obj.blocks.insert(make_pair(uniform_block_name,
										shader_object::internal_shader_object::shader_variable(block_index, (size_t)data_size, GL_UNIFORM_BUFFER)));
		
		// TODO: handle samplers?
	}
	delete [] uni_block_name;
#endif
	
	// validate the program object
	glValidateProgram(shd_obj.program);
	glGetProgramiv(shd_obj.program, GL_VALIDATE_STATUS, &success);
	if(!success) {
		glGetProgramInfoLog(shd_obj.program, A2E_SHADER_LOG_SIZE, nullptr, info_log);
		log_error("Error in program \"%s/%s\" validation!\nInfo log: %s", identifier, option, info_log);
		return 0;
	}
	else {
		glGetProgramInfoLog(shd_obj.program, A2E_SHADER_LOG_SIZE, nullptr, info_log);
		
		// check if shader will run in software (if so, print out a debug message)
		if(strstr((const char*)info_log, (const char*)"software") != nullptr) {
			log_debug("program \"%s/%s\" validation: %s", identifier, option, info_log);
		}
	}
	
	//
	glUseProgram(0);

	return shaders[identifier];
}
int JNICALL Java_com_gomdev_gles_GLESShader_nRetrieveProgramBinary
(JNIEnv * env, jobject obj, jint program, jstring str)
{
    GLint binaryLength;
    GLvoid* binary;
    FILE* outfile;
    GLenum binaryFormat;

    const char* fileName = env->GetStringUTFChars(str, NULL);
    if(fileName != NULL)
    {
        glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binaryLength);

        checkGLError("retrieve() glGetProgramiv");

#ifdef DEBUG
        LOGI("retrieve() binaryLength=%d", binaryLength);
#endif
        binary = (GLvoid*)malloc(binaryLength);
        if(binary == NULL)
        {
            LOGE("nRetrieveProgramBinary() malloc fail");
        }

//        if (glGetProgramBinaryOES == NULL) {
//            glGetProgramBinaryOES = (PFNGLGETPROGRAMBINARYOESPROC) eglGetProcAddress("glGetProgramBinaryOES");
//        }
        glGetProgramBinary(program, binaryLength, NULL, &binaryFormat, binary);

        checkGLError("retrieve() glGetProgramBinaryOES");

        outfile = fopen(fileName, "wb");
        if(outfile == NULL)
        {
            LOGE("nRetrieveProgramBinary() fileName=%s", fileName);
            LOGE("nRetrieveProgramBinary() fopen error");
            free(binary);
            return 0;
        }
        fwrite(binary, binaryLength, 1, outfile);
        fclose(outfile);

        // if binary is already cached, remove cached binary
        char* name = copyFileName(fileName);

        BinaryInfo* info = find(name);
        if (info != NULL) {
            removeBinaryFromList(info, true);
        }

        BinaryInfo* binaryInfo = (BinaryInfo*)malloc(sizeof(BinaryInfo));
        if (binaryInfo != NULL ) {
            binaryInfo->name = name;
            binaryInfo->length = binaryLength;
            binaryInfo->binary = binary;

            add(binaryInfo);
        }

        env->ReleaseStringUTFChars(str, fileName);
    }

    sBinaryFormat = binaryFormat;

    return binaryFormat;
}
static inline void
dumpProgram(StateWriter &writer, Context &context, GLint program)
{
    if (program <= 0) {
        return;
    }

    GLint attached_shaders = 0;
    glGetProgramiv(program, GL_ATTACHED_SHADERS, &attached_shaders);
    if (!attached_shaders) {
        return;
    }

    ShaderMap shaderMap;

    GLuint *shaders = new GLuint[attached_shaders];
    GLsizei count = 0;
    glGetAttachedShaders(program, attached_shaders, &count, shaders);
    std::sort(shaders, shaders + count);
    for (GLsizei i = 0; i < count; ++ i) {
        getShaderSource(shaderMap, shaders[i]);
    }
    delete [] shaders;

    for (ShaderMap::const_iterator it = shaderMap.begin(); it != shaderMap.end(); ++it) {
        writer.beginMember(it->first);
        writer.writeString(it->second);
        writer.endMember();
    }

    // Dump NVIDIA GPU programs via GL_ARB_get_program_binary
    if (context.ARB_get_program_binary) {
        GLint binaryLength = 0;
        glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binaryLength);
        if (binaryLength > 0) {
            std::vector<char> binary(binaryLength);
            GLenum format = GL_NONE;
            glGetProgramBinary(program, binaryLength, NULL, &format, &binary[0]);
            if (format == 0x8e21) {
                if (0) {
                    FILE *fp = fopen("program.bin", "wb");
                    if (fp) {
                        fwrite(&binary[0], 1, binaryLength, fp);
                        fclose(fp);
                    }
                }

                // Extract NVIDIA GPU programs
                std::string str(binary.begin(), binary.end());
                size_t end = 0;
                while (true) {
                    // Each program starts with a !!NV header token
                    size_t start = str.find("!!NV", end);
                    if (start == std::string::npos) {
                        break;
                    }

                    // And is preceeded by a length DWORD
                    assert(start >= end + 4);
                    if (start < end + 4) {
                        break;
                    }
                    uint32_t length;
                    str.copy(reinterpret_cast<char *>(&length), 4, start - 4);
                    assert(start + length <= binaryLength);
                    if (start + length > binaryLength) {
                        break;
                    }

                    std::string nvProg = str.substr(start, length);

                    size_t eol = nvProg.find('\n');
                    std::string nvHeader = nvProg.substr(2, eol - 2);

                    writer.beginMember(nvHeader);
                    writer.writeString(nvProg);
                    writer.endMember();

                    end = start + length;
                }
            }
        }
    }
}
/*!****************************************************************************
 @Function		saveBinaryProgram
 @Return		bool	True if save succeeded.
 @Description	This function takes as input the ID of a shader program object
				which should have been created prior to calling this function,
				as well as a filename to save the binary program to.
				The function will save out a file storing the binary shader
				program, and the enum value determining its format.
******************************************************************************/
bool OGLES3BinaryShader::saveBinaryProgram(const char* Filename, GLuint &ProgramObjectID)
{
	//Quick check to make sure that the program actually exists.
	GLint linked;
	glGetProgramiv(ProgramObjectID, GL_LINK_STATUS, &linked);
	if (!linked)
	{
		//Shaders not linked correctly, no binary to retrieve.
		return false;
	}

	// Get the length of the shader binary program in memory.
	// Doing this ensures that a sufficient amount of memory is allocated for storing the binary program you retrieve.
	GLsizei length=0;
	glGetProgramiv(ProgramObjectID,GL_PROGRAM_BINARY_LENGTH, &length);

	// Pointer to the binary shader program in memory, needs to be allocated with the right size.
	GLvoid* ShaderBinary = (GLvoid*)malloc(length);

	// The format that the binary is retrieved in.
	GLenum binaryFormat=0;

	// Error checking variable - this should be greater than 0 after glGetProgramBinaryOES, otherwise there was an error.
	GLsizei lengthWritten=0;

	// Get the program binary from GL and save it out.
	glGetProgramBinary(ProgramObjectID,length,&lengthWritten,&binaryFormat,ShaderBinary);

	if(!lengthWritten)
	{
		// Save failed. Insufficient memory allocated to write binary shader.
		return false;
	}

	// Cache the program binary for future runs
	FILE* outfile = fopen(Filename, "wb");

	if(!outfile)
	{
		PVRShellOutputDebug("Failed to open %s for writing to.\n", Filename);
		return false;
	}

	// Save the binary format.
	if(!fwrite((void*)&binaryFormat,sizeof(GLenum),1,outfile)) 
	{
		fclose(outfile);
		return false; // Couldn't write binary format to file.
	}

	// Save the actual binary program.
	if(!fwrite(ShaderBinary, length,1,outfile))
	{
		fclose(outfile);
		return false;				 // Couldn't write binary data to file.
	}

	// Close the file.
	fclose(outfile);

	// Free the memory used by Shader Binary.
	free(ShaderBinary);
	return true;
}