Ejemplo n.º 1
0
/**
 * @param nm: output name in the fragment shader, ex: "fragColor"
 */
void shader_add_fragment_output(shader_t *s, const char *nm)
{
    struct shader_fragment_output_t *f_out = &s->outputs[s->noutputs];
    GLuint loc = glGetFragDataLocation(s->program, nm);
    f_out->attachment = GL_COLOR_ATTACHMENT0 + loc;
    s->noutputs++;
}
Ejemplo n.º 2
0
static void
validate_location(GLuint prog, GLenum iface, const char *name, GLuint value)
{
	GLuint loc = glGetProgramResourceLocation(prog, iface, name);

	/* Validate result against old API. */
	switch (iface) {
	case GL_UNIFORM:
		if (glGetUniformLocation(prog, name) != loc)
			piglit_report_result(PIGLIT_FAIL);
		break;
	case GL_PROGRAM_INPUT:
		if (glGetAttribLocation(prog, name) != loc)
			piglit_report_result(PIGLIT_FAIL);
		break;
	case GL_PROGRAM_OUTPUT:
		if (glGetFragDataLocation(prog, name) != loc)
			piglit_report_result(PIGLIT_FAIL);
		break;
	}

	/* Expected value. */
	if (loc != value) {
		fprintf(stderr, "got value %d for %s, expected %d\n",
			loc, name, value);
		piglit_report_result(PIGLIT_FAIL);
	}

	/* No errors should have happened. */
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);
}
Ejemplo n.º 3
0
GLuint dake::gl::program::frag(const char *identifier)
{
    check_valid();

    if (!linked) {
        link();
    }
    return glGetFragDataLocation(id, identifier);
}
Ejemplo n.º 4
0
  GLuint Shader::getColorLocation(const std::string& name)
  {
    std::map<std::string, ShaderColorData>::iterator it = _colorLocations.find(name);
    if(it != _colorLocations.end()){
      return it->second.location;
    }

    GLint loc = glGetFragDataLocation(_programInfo->glID, name.c_str());
    if(loc > 0)
      _colorLocations.at(name).location = loc;
    return loc;
  }
Ejemplo n.º 5
0
void ShaderCallback::test( unsigned long event, void *cbo)
{
	std::cout << " START TEST" << std::endl;
	if(event == vtkCommand::UpdateShaderEvent)
	{
		std::cout << "--- START UpdateShaderEvent" << std::endl;
		vtkOpenGLHelper *cellBO = reinterpret_cast<vtkOpenGLHelper*>(cbo);
		report_gl_error();

		if(cellBO && cellBO->VAO)
		{
			report_gl_error();

			vtkShaderProgram *program = cellBO->Program;
			vtkOpenGLVertexArrayObject *vao = cellBO->VAO;

			std::cout << "Program is compiled? " << program->GetCompiled() << std::endl;
			std::cout << "Program is bound? " << program->isBound() << std::endl;
			std::cout << "IBO index count " << cellBO->IBO->IndexCount << std::endl;
			report_gl_error();


			GLint color_frag_out_index = glGetFragDataLocation(cellBO->Program->GetHandle(), "color");
			std::cout << "color index " << color_frag_out_index << std::endl;
			glBindFragDataLocation(cellBO->Program->GetHandle(), color_frag_out_index, "color"); //setting output of fragment shader


			std::cout << "ADDING ATTRIBUTE ARRAY" << std::endl;
			/*
			vao->Bind();
			if (!vao->AddAttributeArray(program, vbo.Get(), "vertexMC", 0, sizeof(float)*3, VTK_FLOAT, 3, false))
			{
				vtkGenericWarningMacro(<< "Error setting 'vertexMC' in shader VAO.");
			}
			 */
			vao->Bind();
			//Input color
			int vec_size = 3;
			if (!vao->AddAttributeArray(
						program,		//vtkShaderProgram
						mColorBufferObject.Get(),	//vtkOpenGLBufferObject
						"COLOR_VSIN",	//std::string
						0,				//int (offset)				where to start reading g_color_buffer_data, offset != 0 == discard some of the first values
						sizeof(float)*3,				//size_t (stride)			If stride is 0, the generic vertex attributes are understood to be tightly packed in the array.
						VTK_FLOAT,		//int (elementType)			Specifies the data type of each component in the array
						vec_size,				//int (elementTupleSize)	Specifies the number of components per generic vertex attribute. Must be 1, 2, 3, 4.
						false			//bool (normalize)
						))
			{
				vtkGenericWarningMacro(<< "Error setting 'COLOR' in shader VAO.");
			}
Ejemplo n.º 6
0
void ShaderCallback::bindFSOutputVariable(vtkShaderProgram *program)
{
	GLint color_frag_out_index = glGetFragDataLocation(program->GetHandle(), FS_Out_Vec4_Color.c_str());

	if(color_frag_out_index != -1)
	{
		glBindFragDataLocation(program->GetHandle(), color_frag_out_index, FS_Out_Vec4_Color.c_str()); //setting output of fragment shader
		//CX_LOG_DEBUG() << "Binding fragmentshader output to " << FS_Out_Vec4_Color << " at index "<< color_frag_out_index;
	}
	else
	{
		CX_LOG_ERROR() << "Could not find glGetFragDataLocation for " << FS_Out_Vec4_Color;
	}
}
Ejemplo n.º 7
0
void FrameBufferObject::textureAttach()
{
	GLenum FBOstatus;
	glBindFramebuffer(GL_FRAMEBUFFER, FBOHandle);

	glReadBuffer(GL_NONE);
	vector<GLint> loc;

	for (int j = 0; j < shaderOut.size(); ++j)
	{
		GLint location = glGetFragDataLocation(shaderProg, (const GLchar*)shaderOut[j]);
		loc.push_back(location);
	}

	int numTextures = textures.size();
	GLenum* drawBufs = new GLenum[numTextures];

	for (int k = 0; k < texAttachLoc.size(); ++k)
	{
		GLint location = loc[k];
		drawBufs[location] = texAttachLoc[k];
	}

	glDrawBuffers(numTextures, drawBufs);

	for (int m = 0; m < numTextures; ++m)
	{
		glBindTexture(GL_TEXTURE_2D, textures[m]);
		glFramebufferTexture2D(GL_FRAMEBUFFER, drawBufs[loc[m]], GL_TEXTURE_2D, textures[m], 0);
	}

	FBOstatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);

	if (FBOstatus != GL_FRAMEBUFFER_COMPLETE)
	{
		printf("GL_FRAMEBUFFER_COMPLETE FAILED\n");
		checkFrameBufferStatus(FBOstatus);
	}

	delete drawBufs;
}
Ejemplo n.º 8
0
void piglit_init(int argc, char **argv)
{
	GLint max_draw_buffers;
	GLuint prog;
	GLuint vs;
	GLuint fs;
	GLint loc;

	piglit_require_gl_version(30);

	/* This test needs some number of draw buffers, so make sure the
	 * implementation isn't broken.  This enables the test to generate a
	 * useful failure message.
	 */
	glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
	if (max_draw_buffers < 8) {
		fprintf(stderr,
			"OpenGL 3.0 requires GL_MAX_DRAW_BUFFERS >= 8.  "
			"Only got %d!\n",
			max_draw_buffers);
		piglit_report_result(PIGLIT_FAIL);
	}

	prog = glCreateProgram();
	vs = piglit_compile_shader_text(GL_VERTEX_SHADER, vs_text);
	fs = piglit_compile_shader_text(GL_FRAGMENT_SHADER, fs_text);
	glAttachShader(prog, vs);
	glAttachShader(prog, fs);
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	if (!piglit_khr_no_error) {
		/* Page 237 (page 253 of the PDF) of the OpenGL 3.0 spec says:
		 *
		 *     "If program has not been successfully linked, the error
		 *     INVALID OPERATION is generated. If name is not a varying
		 *     out variable, or if an error occurs, -1 will be
		 *     returned."
		 */
		printf("Querying location before linking...\n");
		loc = glGetFragDataLocation(prog, "v");
		if (!piglit_check_gl_error(GL_INVALID_OPERATION))
			piglit_report_result(PIGLIT_FAIL);

		if (loc != -1) {
			fprintf(stderr, "Expected location = -1, got %d\n", loc);
			piglit_report_result(PIGLIT_FAIL);
		}
	}

	glLinkProgram(prog);
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	if (!piglit_link_check_status(prog)) {
		piglit_report_result(PIGLIT_FAIL);
	}

	printf("Querying location of nonexistent variable...\n");
	loc = glGetFragDataLocation(prog, "waldo");
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	if (loc != -1) {
		fprintf(stderr, "Expected location = -1, got %d\n", loc);
		piglit_report_result(PIGLIT_FAIL);
	}

	/* Page 236 (page 252 of the PDF) of the OpenGL 3.0 spec says:
	 *
	 *     "BindFragDataLocation has no effect until the program is
	 *     linked. In particular, it doesn’t modify the bindings of
	 *     varying out variables in a program that has already been
	 *     linked."
	 */
	glBindFragDataLocation(prog, 0, "v");
	glBindFragDataLocation(prog, 1, "a");
	glLinkProgram(prog);
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	if (!piglit_link_check_status(prog)) {
		piglit_report_result(PIGLIT_FAIL);
	}

	printf("Querying locations after binding and linking...\n");
	loc = glGetFragDataLocation(prog, "v");
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	if (loc != 0) {
		fprintf(stderr, "Expected location = 0, got %d\n", loc);
		piglit_report_result(PIGLIT_FAIL);
	}

	loc = glGetFragDataLocation(prog, "a");
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	if (loc != 1) {
		fprintf(stderr, "Expected location = 1, got %d\n", loc);
		piglit_report_result(PIGLIT_FAIL);
	}

	printf("Querying locations after just binding...\n");
	glBindFragDataLocation(prog, 2, "v");
	glBindFragDataLocation(prog, 0, "a");
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	loc = glGetFragDataLocation(prog, "v");
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	if (loc != 0) {
		fprintf(stderr, "Expected location = 0, got %d\n", loc);
		piglit_report_result(PIGLIT_FAIL);
	}

	loc = glGetFragDataLocation(prog, "a");
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	if (loc != 1) {
		fprintf(stderr, "Expected location = 1, got %d\n", loc);
		piglit_report_result(PIGLIT_FAIL);
	}

	piglit_report_result(PIGLIT_PASS);
}
Ejemplo n.º 9
0
void ShadersLoader::LoadShaderFBO(Shader* shader, TiXmlElement *root)
{
	TiXmlElement *rootFBO = root->FirstChildElement("OutputFrame");
	if (!rootFBO)
	{
		Logger::Log() << "[INFO] No FBO is available ... \n";
		return;
	}
	// Get the depth buffer type
	std::string typeDepthString;
	TinyXMLGetAttributeValue<std::string>(rootFBO, "depthType",
			&typeDepthString);
	Logger::Log() << "   * Depth buffer type " << typeDepthString << "\n";
	FBODepthType typeDepth;
	if (typeDepthString == "None")
	{
		typeDepth = FBODEPTH_NONE;
	}
	else if (typeDepthString == "RenderTarget")
	{
		typeDepth = FBODEPTH_RENDERTARGET;
	}
	else if (typeDepthString == "Texture")
	{
		typeDepth = FBODEPTH_TEXTURE;
	}
	else
		throw CException("invalid buffer type");
	// Get the size of buffer
	std::string typeSize;
	int X = 0;
	int Y = 0;
	TinyXMLGetAttributeValue<std::string>(rootFBO, "size", &typeSize);
	if (typeSize == "Screen")
	{
		Math::TVector2I sizeScreen =
				CSettingsManager::Instance().GetSizeRenderingWindow();
		X = sizeScreen.x;
		Y = sizeScreen.y;
	}
	else if (typeSize == "HalfScreen")
	{
		Math::TVector2I sizeScreen =
				CSettingsManager::Instance().GetSizeRenderingWindow();
		X = sizeScreen.x / 2;
		Y = sizeScreen.y / 2;
	}
	else if (typeSize == "Custom")
	{
		TinyXMLGetAttributeValue<int>(rootFBO, "width", &X);
		TinyXMLGetAttributeValue<int>(rootFBO, "height", &Y);
	}
	else
	{
		throw CException("unknow type of size");
	}
	// Get all buffers
	std::map<std::string, FBOTextureBufferParam> buffers;
	Logger::Log() << "   * Chargement des differents buffers .... \n";
	TiXmlElement *frameNode = rootFBO->FirstChildElement("Frame");
	shader->Begin();
	while (frameNode)
	{
		std::string name;
		std::string typeString;
		TinyXMLGetAttributeValue<std::string>(frameNode, "name", &name);
		TinyXMLGetAttributeValue<std::string>(frameNode, "type", &typeString);
		Logger::Log() << "      * Create buffer : " << name << " ( "
				<< typeString << " ) \n";
		FBOTextureBufferParam param;
		// Mipmapping parsing

		bool mipmapping = false;
		frameNode->Attribute("mipmapping", &mipmapping);
		if (mipmapping)
		{
			Logger::Log() << "        * Mipmapping enable \n";
			param.GenerateMipMapping = true;
			param.MinFiltering = GL_LINEAR_MIPMAP_NEAREST;
			param.MaxFiltering = GL_LINEAR;
		}

		bool bilinear = false;
		frameNode->Attribute("bilinear", &bilinear);
		if(bilinear)
		{
			Logger::Log() << "        * Bilinear enable \n";
			param.MinFiltering = GL_LINEAR;
			param.MaxFiltering = GL_LINEAR;
		}

		if(bilinear && mipmapping)
			throw CException("Impossible to activate Bilinear & mipmapping on the same texture");

		// Parse the Image format
		if (typeString == "RGBA")
		{
			// Nothing to do
		}
		else if (typeString == "RGB16")
		{
			param.InternalFormat = GL_RGB16F;
			param.ExternalFormat = GL_RGB;
		}
		else if (typeString == "RGB32")
		{
			param.InternalFormat = GL_RGB32F;
			param.ExternalFormat = GL_RGB;
		}
		else if (typeString == "RGBA16")
		{
			param.InternalFormat = GL_RGBA16F;
			param.ExternalFormat = GL_RGBA;
		}
		else if (typeString == "RGBA32")
		{
			param.InternalFormat = GL_RGBA32F;
			param.ExternalFormat = GL_RGBA;
		}
		else if (typeString == "L")
		{
			param.InternalFormat = GL_LUMINANCE32F_ARB;
			param.ExternalFormat = GL_LUMINANCE;
		}
		else
			throw CException("unknow buffer type");
		param.Attachment = glGetFragDataLocation(shader->GetProgramObject(),
				name.c_str());
		Logger::Log() << "           * Attachment : " << param.Attachment
				<< "\n";
		buffers[name] = param;
		frameNode = frameNode->NextSiblingElement("Frame");
	}
	shader->End();
	FBODepthBufferParam bufferDepth;
	FBO* fbo = new FBO(Math::TVector2I(X, Y), buffers, typeDepth, bufferDepth); // FIXME: Inversion des tailles ???
	shader->SetFBO(fbo);
}
Ejemplo n.º 10
0
GLint Program::getFragDataLocation(const std::string & name) const
{
    return glGetFragDataLocation(id(), name.c_str());
}
Ejemplo n.º 11
0
GLint
VSShaderLib::getProgramOutput(std::string name) {

	return glGetFragDataLocation(pProgram, name.c_str());
}
Ejemplo n.º 12
0
void piglit_init(int argc, char **argv)
{
	GLint max_draw_buffers;
	GLuint prog;
	GLuint vs;
	GLuint fs;
	GLint loc;

	piglit_require_gl_version(30);

	/* This test needs some number of draw buffers, so make sure the
	 * implementation isn't broken.  This enables the test to generate a
	 * useful failure message.
	 */
	glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
	if (max_draw_buffers < 8) {
		fprintf(stderr,
			"OpenGL 3.0 requires GL_MAX_DRAW_BUFFERS >= 8.  "
			"Only got %d!\n",
			max_draw_buffers);
		piglit_report_result(PIGLIT_FAIL);
	}

	prog = glCreateProgram();
	vs = piglit_compile_shader_text(GL_VERTEX_SHADER, vs_text);
	fs = piglit_compile_shader_text(GL_FRAGMENT_SHADER, fs_text);
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	/* First, verify that the program will link without making any
	 * location assignments through the API.
	 */
	printf("Basic test...\n");

	glAttachShader(prog, vs);
	glAttachShader(prog, fs);
	glLinkProgram(prog);
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	if (!piglit_link_check_status(prog)) {
		piglit_report_result(PIGLIT_FAIL);
	}

	/* Page 237 (page 253 of the PDF) of the OpenGL 3.0 spec says:
	 *
	 *     "Assigned bindings for variables that do not exist are
	 *     ignored."
	 */
	printf("Binding `unicorn' to a non-conflicting location...\n");

	glBindFragDataLocation(prog, 0, "v");
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);
	glBindFragDataLocation(prog, 1, "unicorn");
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	glLinkProgram(prog);
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	if (!piglit_link_check_status(prog)) {
		fprintf(stderr,
			"Linking failed when it should have been "
			"successful.\n");
		piglit_report_result(PIGLIT_FAIL);
	}

	loc = glGetFragDataLocation(prog, "unicorn");
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	if (loc != -1) {
		fprintf(stderr, "Expected location = -1, got %d\n", loc);
		piglit_report_result(PIGLIT_FAIL);
	}

	printf("Binding `unicorn' to a conflicting location...\n");

	glBindFragDataLocation(prog, 0, "v");
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);
	glBindFragDataLocation(prog, 0, "unicorn");
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	glLinkProgram(prog);
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	if (!piglit_link_check_status(prog)) {
		fprintf(stderr,
			"Linking failed when it should have been "
			"successful.\n");
		piglit_report_result(PIGLIT_FAIL);
	}

	loc = glGetFragDataLocation(prog, "unicorn");
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	if (loc != -1) {
		fprintf(stderr, "Expected location = -1, got %d\n", loc);
		piglit_report_result(PIGLIT_FAIL);
	}

	piglit_report_result(PIGLIT_PASS);
}
Ejemplo n.º 13
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];
}