Example #1
0
//==============================================================================
/// Given a string that defines blending return the GLenum
static GLenum blendToEnum(const CString& str)
{
// Dont make idiotic mistakes
#define TXT_AND_ENUM(x) \
	if(str == #x) \
	{ \
		return x; \
	}

	TXT_AND_ENUM(GL_ZERO)
	TXT_AND_ENUM(GL_ONE)
	TXT_AND_ENUM(GL_DST_COLOR)
	TXT_AND_ENUM(GL_ONE_MINUS_DST_COLOR)
	TXT_AND_ENUM(GL_SRC_ALPHA)
	TXT_AND_ENUM(GL_ONE_MINUS_SRC_ALPHA)
	TXT_AND_ENUM(GL_DST_ALPHA)
	TXT_AND_ENUM(GL_ONE_MINUS_DST_ALPHA)
	TXT_AND_ENUM(GL_SRC_ALPHA_SATURATE)
	TXT_AND_ENUM(GL_SRC_COLOR)
	TXT_AND_ENUM(GL_ONE_MINUS_SRC_COLOR);
	ANKI_ASSERT(0);
	throw ANKI_EXCEPTION("Incorrect blend enum");

#undef TXT_AND_ENUM
}
//==============================================================================
Bool ProgramPrePreprocessor::parseType(const PPPString& line)
{
	U i;
	Bool found = false;

	for(i = 0; i < static_cast<U>(ShaderType::COUNT); i++)
	{
		if(line.find(commands[i]) == 0)
		{
			found = true;
			break;
		}
	}

	if(found)
	{
		if(m_type != ShaderType::COUNT)
		{
			throw ANKI_EXCEPTION("The shader type is already set. Line %s",
				&line[0]);
		}

		m_type = static_cast<ShaderType>(i);
	}

	return found;
}
Example #3
0
//==============================================================================
void Material::load(const CString& filename, ResourceInitializer& init)
{
	try
	{
		m_vars = std::move(ResourceVector<MaterialVariable*>(init.m_alloc));

		Dictionary<MaterialVariable*> dict(10, 
			Dictionary<MaterialVariable*>::hasher(),
			Dictionary<MaterialVariable*>::key_equal(),
			init.m_alloc);
		m_varDict = std::move(dict);

		m_progs = 
			std::move(ResourceVector<ProgramResourcePointer>(init.m_alloc));
		m_pplines = 
			std::move(ResourceVector<GlProgramPipelineHandle>(init.m_alloc));

		XmlDocument doc;
		doc.loadFile(filename, init.m_tempAlloc);
		parseMaterialTag(doc.getChildElement("material"), init);
	}
	catch(std::exception& e)
	{
		throw ANKI_EXCEPTION("Failed to load material") << e;
	}
}
//==============================================================================
void NativeWindow::swapBuffers()
{
	ANKI_ASSERT(isCreated());
	if(eglSwapBuffers(impl->display, impl->surface) == EGL_FALSE)
	{
		throw ANKI_EXCEPTION("eglSwapBuffers() failed");
	}
}
//==============================================================================
String getHomeDirectory(HeapAllocator<U8>& alloc)
{
	const char* home = getenv("HOME");
	if(home == nullptr)
	{
		throw ANKI_EXCEPTION("HOME environment variable not set");
	}

	return String(CString(home), alloc);
}
//==============================================================================
void NativeWindow::swapBuffers()
{
	ANKI_COUNTER_START_TIMER(SWAP_BUFFERS_TIME);
	ANKI_ASSERT(isCreated());
	if(eglSwapBuffers(impl->display, impl->surface) == EGL_FALSE)
	{
		throw ANKI_EXCEPTION("eglSwapBuffers() failed");
	}
	ANKI_COUNTER_STOP_TIMER_INC(SWAP_BUFFERS_TIME);
}
Example #7
0
//==============================================================================
void Hdr::init(const ConfigSet& initializer)
{
	try
	{
		initInternal(initializer);
	}
	catch(const std::exception& e)
	{
		throw ANKI_EXCEPTION("Failed to init PPS HDR") << e;
	}
}
Example #8
0
//==============================================================================
void Pps::init(const ConfigSet& config)
{
	try
	{
		initInternal(config);
	}
	catch(const std::exception& e)
	{
		throw ANKI_EXCEPTION("Failed to init PPS") << e;
	}
}
//==============================================================================
void TextureResource::load(const CString& filename, ResourceInitializer& init)
{
	try
	{
		loadInternal(filename, init);
	}
	catch(std::exception& e)
	{
		throw ANKI_EXCEPTION("Failed to load texture") << e;
	}
}
//==============================================================================
void createDirectory(const CString& dir)
{
	if(directoryExists(dir))
	{
		return;
	}

	if(mkdir(dir.get(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH))
	{
		throw ANKI_EXCEPTION("%s : %s", strerror(errno), dir.get());
	}
}
//==============================================================================
void removeDirectory(const CString& dirname)
{
	DIR* dir;
	struct dirent* entry;
	char path[PATH_MAX];

	if(path == nullptr) 
	{
		throw ANKI_EXCEPTION("Out of memory error");
	}

	dir = opendir(dirname.get());
	if(dir == nullptr) 
	{
		throw ANKI_EXCEPTION("opendir() failed");
	}

	while((entry = readdir(dir)) != nullptr) 
	{
		if(strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) 
		{
			std::snprintf(
				path, (size_t)PATH_MAX, "%s/%s", dirname.get(), entry->d_name);

			if(entry->d_type == DT_DIR) 
			{
				removeDirectory(CString(path));
			}
			else
			{
				remove(path);
			}
		}

	}

	closedir(dir);
	remove(dirname.get());
}
Example #12
0
static MaterialVariable* newMaterialVariable(
	const GlProgramVariable& glvar, const MaterialProgramCreator::Input& in,
	ResourceAllocator<U8>& alloc, TempResourceAllocator<U8>& talloc)
{
	MaterialVariable* out = nullptr;

	if(in.m_value.size() > 0)
	{
		// Has values

		U floatsNeeded = glvar.getArraySize() * (sizeof(T) / sizeof(F32));

		if(in.m_value.size() != floatsNeeded)
		{
			throw ANKI_EXCEPTION("Incorrect number of values. Variable %s",
				&glvar.getName()[0]);
		}

		TempResourceVector<F32> floatvec(talloc);
		floatvec.resize(floatsNeeded);
		for(U i = 0; i < floatsNeeded; ++i)
		{
			floatvec[i] = in.m_value[i].toF64();
		}

		out = alloc.newInstance<MaterialVariableTemplate<T>>(
			&glvar, 
			in.m_instanced,
			(T*)&floatvec[0],
			glvar.getArraySize(),
			alloc);
	}
	else
	{
		// Buildin

		out = alloc.newInstance<MaterialVariableTemplate<T>>(
			&glvar, 
			in.m_instanced,
			nullptr,
			0,
			alloc);
	}

	return out;
}
//==============================================================================
void ProgramPrePreprocessor::parseFile(const CString& filename)
{
	try
	{
		auto alloc = m_shaderSource.getAllocator();

		// Parse files recursively
		parseFileForPragmas(
			PPPString(filename, alloc), 
			0);

		m_shaderSource = m_sourceLines.join("\n");
	}
	catch(Exception& e)
	{
		throw ANKI_EXCEPTION("Loading file failed: %s", &filename[0]) << e;
	}
}
//==============================================================================
/// Given a string return info about the shader
static void getShaderInfo(
	const CString& str, 
	GLenum& type, 
	GLbitfield& bit,
	U& idx)
{
	if(str == "vert"_cstr)
	{
		type = GL_VERTEX_SHADER;
		bit = GL_VERTEX_SHADER_BIT;
		idx = 0;
	}
	else if(str == "tesc"_cstr)
	{
		type = GL_TESS_CONTROL_SHADER;
		bit = GL_TESS_CONTROL_SHADER_BIT;
		idx = 1;
	}
	else if(str == "tese"_cstr)
	{
		type = GL_TESS_EVALUATION_SHADER;
		bit = GL_TESS_EVALUATION_SHADER_BIT;
		idx = 2;
	}
	else if(str == "geom"_cstr)
	{
		type = GL_GEOMETRY_SHADER;
		bit = GL_GEOMETRY_SHADER_BIT;
		idx = 3;
	}
	else if(str == "frag"_cstr)
	{
		type = GL_GEOMETRY_SHADER;
		bit = GL_GEOMETRY_SHADER_BIT;
		idx = 4;
	}
	else
	{
		throw ANKI_EXCEPTION("Incorrect type %s", &str[0]);
	}
}
//==============================================================================
void MaterialProgramCreator::parseProgramsTag(const XmlElement& el)
{
	//
	// First gather all the inputs
	//
	XmlElement programEl = el.getChildElement("program"_cstr);
	do
	{
		parseInputsTag(programEl);

		programEl = programEl.getNextSiblingElement("program"_cstr);
	} while(programEl);

	// Sort them by name to decrease the change of creating unique shaders
	std::sort(m_inputs.begin(), m_inputs.end(), InputSortFunctor());

	//
	// Then parse the includes, operations and other parts of the program
	//
	programEl = el.getChildElement("program"_cstr);
	do
	{
		parseProgramTag(programEl);

		programEl = programEl.getNextSiblingElement("program"_cstr);
	} while(programEl);

	//
	// Sanity checks
	//

	// Check that all input is referenced
	for(Input& in : m_inputs)
	{
		if(in.m_shaderDefinedMask != in.m_shaderReferencedMask)
		{
			throw ANKI_EXCEPTION("Variable not referenced or not defined %s", 
				&in.m_name[0]);
		}
	}
}
Example #16
0
//==============================================================================
void GlFramebuffer::createFbo(
	const Array<U, MAX_COLOR_ATTACHMENTS + 1>& layers,
	GLenum depthStencilBindingPoint)
{
	ANKI_ASSERT(!isCreated());
	glGenFramebuffers(1, &m_glName);
	ANKI_ASSERT(m_glName != 0);
	const GLenum target = GL_FRAMEBUFFER;
	glBindFramebuffer(target, m_glName);

	// Attach color
	for(U i = 0; i < MAX_COLOR_ATTACHMENTS; i++)
	{
		if(!m_attachments[i].isCreated())
		{
			continue;
		}
		
		const GlTexture& tex = m_attachments[i]._get();
		attachTextureInternal(GL_COLOR_ATTACHMENT0 + i, tex, layers[i]);
	}

	// Attach depth/stencil
	if(m_attachments[MAX_COLOR_ATTACHMENTS].isCreated())
	{
		ANKI_ASSERT(depthStencilBindingPoint != GL_NONE);

		const GlTexture& tex = m_attachments[MAX_COLOR_ATTACHMENTS]._get();
		attachTextureInternal(depthStencilBindingPoint, tex, 
			layers[MAX_COLOR_ATTACHMENTS]);
	}

	// Check completeness
	GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
	if(status != GL_FRAMEBUFFER_COMPLETE)
	{
		destroy();
		throw ANKI_EXCEPTION("FBO is incomplete");
	}
}
//==============================================================================
void NativeWindow::create(Initializer& init, HeapAllocator<U8>& alloc)
{
	m_alloc = alloc;
	m_impl = m_alloc.newInstance<NativeWindowImpl>();

	if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_EVENTS 
		| SDL_INIT_GAMECONTROLLER) != 0)
	{
		throw ANKI_EXCEPTION("SDL_Init() failed");
	}
	
	//
	// Set GL attributes
	//
	if(SDL_GL_SetAttribute(SDL_GL_RED_SIZE, init.m_rgbaBits[0]) != 0
		|| SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, init.m_rgbaBits[1]) != 0
		|| SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, init.m_rgbaBits[2]) != 0
		|| SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, init.m_rgbaBits[3]) != 0
		|| SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, init.m_depthBits) != 0
		|| SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, init.m_doubleBuffer) != 0
		|| SDL_GL_SetAttribute(
			SDL_GL_CONTEXT_MAJOR_VERSION, init.m_majorVersion) != 0
		|| SDL_GL_SetAttribute(
			SDL_GL_CONTEXT_MINOR_VERSION, init.m_minorVersion) != 0
		|| SDL_GL_SetAttribute(
			SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE) != 0)
	{
		throw ANKI_EXCEPTION("SDL_GL_SetAttribute() failed");
	}

	if(init.m_debugContext)
	{
		if(SDL_GL_SetAttribute(
			SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG) != 0)
		{
			throw ANKI_EXCEPTION("SDL_GL_SetAttribute() failed");
		}
	}

	//
	// Create window
	//
	U32 flags = SDL_WINDOW_OPENGL;

	if(init.m_fullscreenDesktopRez)
	{
		flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
	}

	m_impl->m_window = SDL_CreateWindow(
    	init.m_title.c_str(),
		SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 
		init.m_width, init.m_height, flags);

	if(m_impl->m_window == nullptr)
	{
		throw ANKI_EXCEPTION("SDL_CreateWindow() failed");
	}

	// Set the size after loading a fullscreen window
	if(init.m_fullscreenDesktopRez)
	{
		int w, h;
		SDL_GetWindowSize(m_impl->m_window, &w, &h);

		m_width = w;
		m_height = h;
	}
	else
	{
		m_width = init.m_width;
		m_height = init.m_height;
	}

	//
	// Create context
	//
	m_impl->m_context = SDL_GL_CreateContext(m_impl->m_window);

	if(m_impl->m_context == nullptr)
	{
		throw ANKI_EXCEPTION("SDL_GL_CreateContext() failed");
	}

	//
	// GLEW
	//
	glewExperimental = GL_TRUE;
	if(glewInit() != GLEW_OK)
	{
		throw ANKI_EXCEPTION("GLEW initialization failed");
	}
	glGetError();
}
Example #18
0
//==============================================================================
void Renderer::init(const ConfigSet& initializer)
{
	// Set from the initializer
	m_width = initializer.get("width");
	m_height = initializer.get("height");
	m_lodDistance = initializer.get("lodDistance");
	m_framesNum = 0;
	m_samples = initializer.get("samples");
	m_isOffscreen = initializer.get("offscreen");
	m_renderingQuality = initializer.get("renderingQuality");
	m_tilesCount.x() = initializer.get("tilesXCount");
	m_tilesCount.y() = initializer.get("tilesYCount");

	m_tessellation = initializer.get("tessellation");

	// A few sanity checks
	if(m_samples != 1 && m_samples != 4 && m_samples != 8 && m_samples != 16
		&& m_samples != 32)
	{
		throw ANKI_EXCEPTION("Incorrect samples");
	}

	if(m_width < 10 || m_height < 10)
	{
		throw ANKI_EXCEPTION("Incorrect sizes");
	}

	// quad setup
	static const F32 quadVertCoords[][2] = {{1.0, 1.0}, {-1.0, 1.0}, 
		{1.0, -1.0}, {-1.0, -1.0}};

	GlDevice& gl = GlDeviceSingleton::get();
	GlCommandBufferHandle jobs(&gl);

	GlClientBufferHandle tmpBuff = GlClientBufferHandle(jobs, 
		sizeof(quadVertCoords), (void*)&quadVertCoords[0][0]);

	m_quadPositionsBuff = GlBufferHandle(jobs, GL_ARRAY_BUFFER, 
		tmpBuff, 0);

	m_drawQuadVert.load("shaders/Quad.vert.glsl");

	// Init the stages. Careful with the order!!!!!!!!!!
	m_tiler.init(this);

	m_ms.init(initializer);;
	m_is.init(initializer);
	m_bs.init(initializer);
	m_pps.init(initializer);
	m_dbg.init(initializer);

	// Init the shaderPostProcessorString
	std::stringstream ss;
	ss << "#define RENDERING_WIDTH " << m_width << "\n"
		<< "#define RENDERING_HEIGHT " << m_height << "\n";

	m_shaderPostProcessorString = ss.str();

	// Default FB
	m_defaultFb = GlFramebufferHandle(jobs, {});

	jobs.finish();
}
Example #19
0
static int glf_program_populate_vars(glf_program *prog)
{
	int num;
	int num2;
	char name_[512];
	GLsizei length;
	GLint size;
	GLenum type;
	int i;
	GLint loc;

	assert(prog);
	assert(prog->id);
	assert(prog->vars == NULL);
	assert(prog->vars_num == 0);

	glGetProgramiv(prog->id, GL_ACTIVE_ATTRIBUTES, &num);
	glGetProgramiv(prog->id, GL_ACTIVE_UNIFORMS, &num2);

	/* make the allocations */
	prog->vars_num = num + num2;
	prog->vars = (glf_shader_variable*)malloc(
	                              sizeof(glf_shader_variable) * prog->vars_num);

	assert(prog->vars);
	memset(prog->vars, 0, sizeof(glf_shader_variable) * prog->vars_num);

	/* attrib locations */
	for(i = 0; i < num; i++)
	{
		glGetActiveAttrib(prog->id, i, sizeof(name_) / sizeof(char), &length,
			&size, &type, &name_[0]);
		name_[length] = '\0';

		/* check if its FFP location */
		loc = glGetAttribLocation(prog->id, &name_[0]);
		if(loc == -1) // if -1 it means that its an FFP var
		{

			GLF_LOGE("You are using FFP vertex attributes");

		}

		ShaderProgramAttributeVariable* var =
			new ShaderProgramAttributeVariable(loc, &name_[0], type, 
			size, this);

		vars.push_back(var);
		attribs.push_back(var);
		nameToVar[var->getName().c_str()] = var;
		nameToAttribVar[var->getName().c_str()] = var;
	}

	// uni locations
	glGetProgramiv(glId, GL_ACTIVE_UNIFORMS, &num);
	for(int i = 0; i < num; i++) // loop all uniforms
	{
		glGetActiveUniform(glId, i, sizeof(name_) / sizeof(char), &length,
			&size, &type, &name_[0]);
		name_[length] = '\0';

		// check if its FFP location
		int loc = glGetUniformLocation(glId, &name_[0]);
		if(loc == -1) // if -1 it means that its an FFP var
		{
			throw ANKI_EXCEPTION("You are using FFP vertex uniforms");
		}

		ShaderProgramUniformVariable* var =
			new ShaderProgramUniformVariable(loc, &name_[0], type, 
			size, this);

		vars.push_back(var);
		unis.push_back(var);
		nameToVar[var->getName().c_str()] = var;
		nameToUniVar[var->getName().c_str()] = var;
	}
}
//==============================================================================
void ProgramPrePreprocessor::parseFileForPragmas(
	const PPPString& filename, U32 depth)
{
	// first check the depth
	if(depth > MAX_DEPTH)
	{
		throw ANKI_EXCEPTION("The include depth is too high. "
			"Probably circular includance");
	}

	// load file in lines
	auto alloc = m_shaderSource.getAllocator();
	PPPString txt(alloc);
	PPPStringList lines(alloc);
	File file;
	file.open(filename.toCString(), File::OpenFlag::READ);
	file.readAllText(txt);
	lines = PPPStringList::splitString(txt.toCString(), '\n', alloc);
	if(lines.size() < 1)
	{
		throw ANKI_EXCEPTION("File is empty: %s", &filename[0]);
	}

	for(const PPPString& line : lines)
	{
		PtrSize npos = 0;

		if(line.find("#pragma anki") == 0)
		{
			Bool malformed = true;

			if(parseType(line))
			{
				malformed = false; // All OK
			}
			else if((npos = line.find(commands[6])) == 0)
			{
				// Include

				if(line.getLength() >= std::strlen(commands[6]) + 2)
				{
					PPPString filen(
						line.begin() + std::strlen(commands[6]), 
						line.end() - 1, 
						alloc);

					filen = m_manager->fixResourceFilename(filen.toCString());

					parseFileForPragmas(filen, depth + 1);

					malformed = false; // All OK
				}
			}

			if(malformed)
			{
				throw ANKI_EXCEPTION("Malformed pragma anki: %s", &line[0]);
			}
		}
		else
		{
			m_sourceLines.push_back(line);
		}
	}

	// Sanity checks
	if(m_type == ShaderType::COUNT)
	{
		throw ANKI_EXCEPTION("Shader is missing the type");
	}
}
//==============================================================================
void NativeWindowImpl::create(NativeWindowInitInfo& init)
{
	Array<EGLint, 256> attribs;
	U attr = 0;
	EGLint configsCount;
	EGLint format;
	EGLConfig config;

	ANKI_LOGI("Creating native window");

	ANKI_ASSERT(gAndroidApp);
	android_app& andApp = *gAndroidApp;
	loopUntilWindowIsReady(andApp);

	// EGL init
	//
	display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
	if(display == EGL_NO_DISPLAY)
	{
		throw ANKI_EXCEPTION("Failed to create display");
	}

	int major, minor;
	if(eglInitialize(display, &major, &minor) == EGL_FALSE)
	{
		throw ANKI_EXCEPTION("Failed to initialize EGL");
	}

	//
	// EGL config
	//
	attribs[attr++] = EGL_SURFACE_TYPE;
	attribs[attr++] = EGL_WINDOW_BIT;

	attribs[attr++] = EGL_RENDERABLE_TYPE;
	attribs[attr++] = EGL_OPENGL_ES2_BIT;

	if(init.samplesCount > 1)
	{
		attribs[attr++] = EGL_SAMPLES;
		attribs[attr++] = init.samplesCount;
	}

	attribs[attr++] = EGL_RED_SIZE;
	attribs[attr++] = init.rgbaBits[0];
	attribs[attr++] = EGL_GREEN_SIZE;
	attribs[attr++] = init.rgbaBits[1];
	attribs[attr++] = EGL_BLUE_SIZE;
	attribs[attr++] = init.rgbaBits[2];
	attribs[attr++] = EGL_ALPHA_SIZE;
	attribs[attr++] = init.rgbaBits[3];

	attribs[attr++] = EGL_DEPTH_SIZE;
	attribs[attr++] = init.depthBits;
	attribs[attr++] = EGL_STENCIL_SIZE;
	attribs[attr++] = init.stencilBits;

	attribs[attr++] = EGL_NONE;

	if(eglChooseConfig(display, &attribs[0], &config, 1, &configsCount)
		== EGL_FALSE)
	{
		throw ANKI_EXCEPTION("Failed to query required EGL configs");
	}

	if(configsCount == 0)
	{
		throw ANKI_EXCEPTION("No matching EGL configs found");
	}

	eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);

	ANKI_ASSERT(andApp.window);
	ANativeWindow_setBuffersGeometry(andApp.window, 0, 0, format);

	// Surface
	//
	surface = eglCreateWindowSurface(display, config, andApp.window, NULL);
	if(surface == EGL_NO_SURFACE)
	{
		throw ANKI_EXCEPTION("Cannot create surface");
	}

	// Context
	//
	EGLint ctxAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};

	context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctxAttribs);
	if(context == EGL_NO_CONTEXT)
	{
		throw ANKI_EXCEPTION("Cannot create context");
	}

	if(eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
	{
		throw ANKI_EXCEPTION("Cannot make the context current");
	}

	// Query width and height
	//
	EGLint w, h;
	eglQuerySurface(display, surface, EGL_WIDTH, &w);
	eglQuerySurface(display, surface, EGL_HEIGHT, &h);

	init.width = w;
	init.height = h;
}
Example #22
0
//==============================================================================
void Material::populateVariables(const MaterialProgramCreator& mspc)
{
	for(auto in : mspc.getInputVariables())
	{
		if(in.m_constant)
		{
			continue;
		}

		MaterialVariable* mtlvar = nullptr;
		const GlProgramVariable* glvar = nullptr;

		// Find the input variable in one of the programs
		for(const ProgramResourcePointer& progr : m_progs)
		{
			const GlProgramHandle& prog = progr->getGlProgram();

			glvar = prog.tryFindVariable(in.m_name.toCString());
			if(glvar)
			{
				break;
			}
		}

		// Check if variable found
		if(glvar == nullptr)
		{
			throw ANKI_EXCEPTION("Variable not found in "
				"at least one program: %s", &in.m_name[0]);
		}

		switch(glvar->getDataType())
		{
		// samplers
		case GL_SAMPLER_2D:
		case GL_SAMPLER_CUBE:
			{
				TextureResourcePointer tp;
				
				if(in.m_value.size() > 0)
				{
					tp = TextureResourcePointer(
						in.m_value[0].toCString(),
						m_resources);
				}

				auto alloc = m_resources->_getAllocator();
				mtlvar = alloc.newInstance<
					MaterialVariableTemplate<TextureResourcePointer>>(
					glvar, false, &tp, 1, alloc);
			}
			break;
		// F32
		case GL_FLOAT:
			mtlvar = newMaterialVariable<F32>(*glvar, in,
				m_resources->_getAllocator(), m_resources->_getTempAllocator());
			break;
		// vec2
		case GL_FLOAT_VEC2:
			mtlvar = newMaterialVariable<Vec2>(*glvar, in,
				m_resources->_getAllocator(), m_resources->_getTempAllocator());
			break;
		// vec3
		case GL_FLOAT_VEC3:
			mtlvar = newMaterialVariable<Vec3>(*glvar, in,
				m_resources->_getAllocator(), m_resources->_getTempAllocator());
			break;
		// vec4
		case GL_FLOAT_VEC4:
			mtlvar = newMaterialVariable<Vec4>(*glvar, in,
				m_resources->_getAllocator(), m_resources->_getTempAllocator());
			break;
		// mat3
		case GL_FLOAT_MAT3:
			mtlvar = newMaterialVariable<Mat3>(*glvar, in,
				m_resources->_getAllocator(), m_resources->_getTempAllocator());
			break;
		// mat4
		case GL_FLOAT_MAT4:
			mtlvar = newMaterialVariable<Mat4>(*glvar, in,
				m_resources->_getAllocator(), m_resources->_getTempAllocator());
			break;
		// default is error
		default:
			ANKI_ASSERT(0);
		}

		m_vars.push_back(mtlvar);
	}
}
//==============================================================================
void NativeWindowImpl::create(NativeWindowInitializer& init)
{
	// Create window
	//
	fbwin = (fbdev_window*)malloc(sizeof(fbdev_window));
	if(fbwin == NULL)
	{
		throw ANKI_EXCEPTION("malloc() failed");
	}
	
	fbwin->width = init.width;
	fbwin->height = init.height;
	
	// EGL init
	//
	display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
	if(display == EGL_NO_DISPLAY)
	{
		throw ANKI_EXCEPTION("Failed to create display");
	}

	int major, minor;
	if(eglInitialize(display, &major, &minor) == EGL_FALSE)
	{
		throw ANKI_EXCEPTION("Failed to initialize EGL");
	}

	int maxConfigs;
	if(eglGetConfigs(display, NULL, 0, &maxConfigs) == EGL_FALSE)
	{
		throw ANKI_EXCEPTION("Failed to query EGL configs");
	}

	if(maxConfigs < 1)
	{
		throw ANKI_EXCEPTION("Error in number of configs");
	}

	Vector<EGLConfig> configs(maxConfigs);

	Array<EGLint, 256> attribs;
	U attr = 0;
	attribs[attr++] = EGL_RENDERABLE_TYPE;
	attribs[attr++] = EGL_OPENGL_ES2_BIT;

	if(init.samplesCount > 1)
	{
		attribs[attr++] = EGL_SAMPLES;
		attribs[attr++] = init.samplesCount;
	}

	attribs[attr++] = EGL_RED_SIZE;
	attribs[attr++] = init.rgbaBits[0];
	attribs[attr++] = EGL_GREEN_SIZE;
	attribs[attr++] = init.rgbaBits[1];
	attribs[attr++] = EGL_BLUE_SIZE;
	attribs[attr++] = init.rgbaBits[2];
	attribs[attr++] = EGL_ALPHA_SIZE;
	attribs[attr++] = init.rgbaBits[3];

	attribs[attr++] = EGL_DEPTH_SIZE;
	attribs[attr++] = init.depthBits;
	attribs[attr++] = EGL_STENCIL_SIZE;
	attribs[attr++] = init.stencilBits;

	attribs[attr++] = EGL_NONE;

	EGLint configsCount;
	if(eglChooseConfig(display, &attribs[0], &configs[0], maxConfigs, 
		&configsCount) == EGL_FALSE)
	{
		throw ANKI_EXCEPTION("Failed to query required EGL configs");
	}

	if(configsCount == 0)
	{
		throw ANKI_EXCEPTION("No matching EGL configs found");
	}

	EGLConfig config_ = nullptr;
	for(EGLint i = 0; i < configsCount; i++)
	{
		EGLint value;
		EGLConfig config = configs[i];

		// Use this to explicitly check that the EGL config has the 
		// expected color depths
		eglGetConfigAttrib(display, config, EGL_RED_SIZE, &value);
		if(value != (EGLint)init.rgbaBits[0]) 
		{
			continue;
		}

		eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &value);
		if(value != (EGLint)init.rgbaBits[1]) 
		{
			continue;
		}

		eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &value);
	    if(value != (EGLint)init.rgbaBits[2])
		{
			continue;
		}

		eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &value);
		if(value != (EGLint)init.rgbaBits[3])
		{
			continue;
		}

		eglGetConfigAttrib(display, config, EGL_SAMPLES, &value);
		if(value != (EGLint)init.samplesCount)
		{
			continue;
		}

		eglGetConfigAttrib(display, config, EGL_DEPTH_SIZE, &value);
		if(value != (EGLint)init.depthBits)
		{
			continue;
		}

		eglGetConfigAttrib(display, config, EGL_STENCIL_SIZE, &value);
		if(value != (EGLint)init.stencilBits)
		{
			continue;
		}

		// We found what we wanted
		config_ = config;
		break;
	}

	if(config_ == nullptr)
	{
		throw ANKI_EXCEPTION("Unable to find suitable EGL config");
	}

	// Surface
	//
	surface = eglCreateWindowSurface(display, config_, 
		static_cast<EGLNativeWindowType>(fbwin), NULL);

	if(surface == EGL_NO_SURFACE)
	{
		throw ANKI_EXCEPTION("Cannot create surface");
	}

	// Context
	//
	EGLint ctxAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};

	context = eglCreateContext(display, config_, EGL_NO_CONTEXT, 
		ctxAttribs);

	if(context == EGL_NO_CONTEXT)
	{
		throw ANKI_EXCEPTION("Cannot create context");
	}

	if(eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
	{
		throw ANKI_EXCEPTION("Cannot make the context current");
	}
}
//==============================================================================
void MaterialProgramCreator::parseInputsTag(const XmlElement& programEl)
{
	XmlElement inputsEl = programEl.getChildElementOptional("inputs");
	if(!inputsEl)
	{
		return;
	}

	// Get shader type
	GLbitfield glshaderbit;
	GLenum glshader;
	U shaderidx;
	getShaderInfo(
		programEl.getChildElement("type").getText(), 
		glshader, glshaderbit, shaderidx);

	XmlElement inputEl = inputsEl.getChildElement("input");
	do
	{
		Input inpvar(m_alloc);

		// <name>
		inpvar.m_name = inputEl.getChildElement("name").getText();

		// <type>
		inpvar.m_type = inputEl.getChildElement("type").getText();

		// <value>
		XmlElement valueEl = inputEl.getChildElement("value");
		if(valueEl.getText())
		{
			inpvar.m_value = MPStringList::splitString(
				valueEl.getText(), ' ', m_alloc);
		}

		// <const>
		XmlElement constEl = inputEl.getChildElementOptional("const");
		inpvar.m_constant = (constEl) ? constEl.getInt() : false;

		// <arraySize>
		XmlElement arrSizeEl = inputEl.getChildElementOptional("arraySize");
		inpvar.m_arraySize = (arrSizeEl) ? arrSizeEl.getInt() : 0;

		// <instanced>
		if(inpvar.m_arraySize == 0)
		{
			XmlElement instancedEl = 
				inputEl.getChildElementOptional("instanced");

			inpvar.m_instanced = (instancedEl) ? instancedEl.getInt() : 0;

			// If one input var is instanced notify the whole program that 
			// it's instanced
			if(inpvar.m_instanced)
			{
				m_instanced = true;
			}
		}

		// Now you have the info to check if duplicate
		Input* duplicateInp = nullptr;
		for(Input& in : m_inputs)
		{
			if(in.m_name == inpvar.m_name)
			{
				duplicateInp = &in;
				break;
			}
		}

		if(duplicateInp != nullptr)
		{
			// Duplicate. Make sure it's the same as the other shader
			Bool same = duplicateInp->m_type == inpvar.m_type
				|| duplicateInp->m_value == inpvar.m_value
				|| duplicateInp->m_constant == inpvar.m_constant
				|| duplicateInp->m_arraySize == inpvar.m_arraySize
				|| duplicateInp->m_instanced == inpvar.m_instanced;

			if(!same)
			{
				throw ANKI_EXCEPTION("Variable defined differently between "
					"shaders: %s", &inpvar.m_name[0]);
			}

			duplicateInp->m_shaderDefinedMask |= glshaderbit;

			goto advance;
		}

		if(inpvar.m_constant == false)
		{
			// Handle NON-consts

			inpvar.m_line = inpvar.m_type + " " + inpvar.m_name;
				
			if(inpvar.m_arraySize > 1)
			{
				MPString tmp(MPString::toString(inpvar.m_arraySize, m_alloc));
				inpvar.m_line += "[" + tmp + "U]";
			}

			if(inpvar.m_instanced)
			{
				MPString tmp(
					MPString::toString(ANKI_GL_MAX_INSTANCES, m_alloc));
				inpvar.m_line += "[" +  tmp + "U]";
			}

			inpvar.m_line += ";";

			// Can put it block
			if(inpvar.m_type == "sampler2D" || inpvar.m_type == "samplerCube")
			{
				MPString tmp(
					MPString::toString(m_texBinding++, m_alloc));

				inpvar.m_line = ANKI_STRL("layout(binding = ") 
					+ tmp + ") uniform " + inpvar.m_line;
		
				inpvar.m_inBlock = false;
			}
			else
			{
				inpvar.m_inBlock = true;

				m_uniformBlock.push_back(inpvar.m_line);
				m_uniformBlockReferencedMask |= glshaderbit;
			}
		}
		else
		{
			// Handle consts

			if(inpvar.m_value.size() == 0)
			{
				throw ANKI_EXCEPTION("Empty value and const is illogical");
			}

			if(inpvar.m_arraySize > 0)
			{
				throw ANKI_EXCEPTION("Const arrays currently cannot "
					"be handled");
			}

			inpvar.m_inBlock = false;

			inpvar.m_line = ANKI_STRL("const ") 
				+ inpvar.m_type + " " + inpvar.m_name 
				+ " = " + inpvar.m_type + "(" + inpvar.m_value.join(", ") 
				+  ");";
		}

		inpvar.m_shaderDefinedMask = glshaderbit;
		m_inputs.push_back(inpvar);

advance:
		// Advance
		inputEl = inputEl.getNextSiblingElement("input");
	} while(inputEl);
}
//==============================================================================
void MaterialProgramCreator::parseOperationTag(
	const XmlElement& operationTag, GLenum glshader, GLbitfield glshaderbit,
	MPString& out)
{
	static const char OUT[] = {"out"};

	// <id></id>
	I id = operationTag.getChildElement("id").getInt();
	
	// <returnType></returnType>
	XmlElement retTypeEl = operationTag.getChildElement("returnType");
	MPString retType(retTypeEl.getText(), m_alloc);
	MPString operationOut(m_alloc);
	if(retType != "void")
	{
		MPString tmp(MPString::toString(id, m_alloc));
		operationOut = ANKI_STRL(OUT) + tmp;
	}
	
	// <function>functionName</function>
	MPString funcName(
		operationTag.getChildElement("function").getText(), m_alloc);
	
	// <arguments></arguments>
	XmlElement argsEl = operationTag.getChildElementOptional("arguments");
	MPStringList argsList(m_alloc);
	
	if(argsEl)
	{
		// Get all arguments
		XmlElement argEl = argsEl.getChildElement("argument");
		do
		{
			MPString arg(argEl.getText(), m_alloc);

			// Search for all the inputs and mark the appropriate
			Input* input = nullptr;
			for(Input& in : m_inputs)
			{
				// Check that the first part of the string is equal to the 
				// variable and the following char is '['
				if(in.m_name == arg)
				{
					input = &in;
					in.m_shaderReferencedMask = glshaderbit;
					break;
				}
			}

			// The argument should be an input variable or an outXX
			if(!(input != nullptr 
				|| std::strncmp(&arg[0], OUT, sizeof(OUT) - 1) == 0))
			{
				throw ANKI_EXCEPTION("Incorrect argument: %s", &arg[0]);
			}

			// Add to a list and do something special if instanced
			if(input && input->m_instanced)
			{
				if(glshader == GL_VERTEX_SHADER)
				{
					argsList.push_back(ANKI_STRL(argEl.getText()) 
						+ "[gl_InstanceID]");

					m_instanceIdMask |= glshaderbit;
				}
				else if(glshader == GL_TESS_CONTROL_SHADER)
				{
					argsList.push_back(ANKI_STRL(argEl.getText()) 
						+ "[vInstanceId[0]]");

					m_instanceIdMask |= glshaderbit;
				}
				else if(glshader == GL_TESS_EVALUATION_SHADER)
				{
					argsList.push_back(ANKI_STRL(argEl.getText()) 
						+ "[commonPatch.instanceId]");
					
					m_instanceIdMask |= glshaderbit;
				}
				else if(glshader == GL_FRAGMENT_SHADER)
				{
					argsList.push_back(ANKI_STRL(argEl.getText()) 
						+ "[vInstanceId]");
					
					m_instanceIdMask |= glshaderbit;
				}
				else
				{
					throw ANKI_EXCEPTION(
						"Cannot access the instance ID in all shaders");
				}
			}
			else
			{
				argsList.push_back(MPString(argEl.getText(), m_alloc));
			}

			// Advance
			argEl = argEl.getNextSiblingElement("argument");
		} while(argEl);
	}

	// Now write everything
	MPString lines(m_alloc);
	lines.reserve(256);
	lines += "#if defined(" + funcName + "_DEFINED)";

	// Write the defines for the operationOuts
	for(const MPString& arg : argsList)
	{
		if(arg.find(OUT) == 0)
		{
			lines += " && defined(" + arg + "_DEFINED)";
		}
	}
	lines += "\n";

	if(retType != "void")
	{
		lines += "#\tdefine " + operationOut + "_DEFINED\n\t"
			+ retTypeEl.getText() + " " + operationOut + " = ";
	}
	else
	{
		lines += "\t";
	}
	
	// write the blah = func(args...)
	lines += funcName + "(";
	lines += argsList.join(", ");
	lines += ");\n";
	lines += "#endif";

	// Done
	out = std::move(lines);
}