Index: trunk/nv/gfx/debug_draw.hh
===================================================================
--- trunk/nv/gfx/debug_draw.hh	(revision 302)
+++ trunk/nv/gfx/debug_draw.hh	(revision 303)
@@ -10,5 +10,4 @@
 #include <nv/common.hh>
 #include <nv/math.hh>
-#include <nv/interface/program.hh>
 #include <nv/interface/context.hh>
 
@@ -30,5 +29,5 @@
 		void update();
 		void reset();
-		program*      get_program()      { return m_program; }
+		program       get_program()      { return m_program; }
 		vertex_array  get_vertex_array() { return m_va; }
 		uint32        get_count()        { return m_data.size(); }
@@ -39,5 +38,5 @@
 	private:
 		device*                  m_device;
-		program*                 m_program;
+		program                  m_program;
 		vertex_array             m_va;
 		std::vector< debug_vtx > m_data;
Index: trunk/nv/gfx/keyframed_mesh.hh
===================================================================
--- trunk/nv/gfx/keyframed_mesh.hh	(revision 302)
+++ trunk/nv/gfx/keyframed_mesh.hh	(revision 303)
@@ -27,5 +27,5 @@
 		virtual mat4 get_node_matrix( uint32 node_id ) const;
 		virtual void update_animation( animation_entry*, uint32 a_anim_time );
-		virtual void update( program* a_program );
+		virtual void update( program a_program );
 		virtual ~keyframed_mesh();
 	protected:
@@ -74,5 +74,5 @@
 		keyframed_mesh_gpu( context* a_context, const mesh_data* a_data, const mesh_nodes_data* a_tag_map );
 		void update( uint32 ms );
-		virtual void update( program* a_program );
+		virtual void update( program a_program );
 	private:
 		int m_loc_next_position;
Index: trunk/nv/gfx/skeletal_mesh.hh
===================================================================
--- trunk/nv/gfx/skeletal_mesh.hh	(revision 302)
+++ trunk/nv/gfx/skeletal_mesh.hh	(revision 303)
@@ -101,5 +101,5 @@
 		skeletal_mesh_gpu( context* a_context, const mesh_data* a_mesh, const mesh_nodes_data* a_bone_data );
 		virtual size_t get_index_count() const { return m_index_count; }
-		virtual void update( program* a_program );
+		virtual void update( program a_program );
 		virtual void update_animation( animation_entry* a_anim, uint32 
 			a_anim_time );
Index: trunk/nv/gl/gl_context.hh
===================================================================
--- trunk/nv/gl/gl_context.hh	(revision 302)
+++ trunk/nv/gl/gl_context.hh	(revision 303)
@@ -24,8 +24,8 @@
 		~gl_context();
 		virtual void bind( texture t, texture_slot slot );
-		virtual void bind( program* p );
+		virtual void bind( program p );
 		virtual void bind( buffer b );
 		virtual void bind( vertex_array va );
-		virtual void unbind( program* p );
+		virtual void unbind( program p );
 		virtual void unbind( buffer b );
 		virtual void unbind( vertex_array va );
@@ -36,8 +36,10 @@
 		virtual void clear( const clear_state& cs );
 		// temporary
-		virtual void draw( primitive prim, const render_state& rs, program* p, vertex_array va, size_t count );
+		virtual void draw( primitive prim, const render_state& rs, program p, vertex_array va, size_t count );
 		virtual const ivec4& get_viewport();
 		virtual void set_viewport( const ivec4& viewport );
 		virtual void apply_render_state( const render_state& state );
+		virtual void apply_engine_uniforms( program p, const scene_state& s );
+
 	protected:
 		void force_apply_render_state( const render_state& state );
Index: trunk/nv/gl/gl_device.hh
===================================================================
--- trunk/nv/gl/gl_device.hh	(revision 302)
+++ trunk/nv/gl/gl_device.hh	(revision 303)
@@ -27,7 +27,16 @@
 	};
 
+	struct gl_program_info : public program_info
+	{
+		unsigned glid;
+		unsigned glidv;
+		unsigned glidf;
+	};
+
 	class gl_device : public device
 	{
 	public:
+		friend class gl_context;
+
 		gl_device();
 		virtual window* create_window( uint16 width, uint16 height, bool fullscreen );
@@ -35,5 +44,6 @@
 		virtual image_data* create_image_data( const std::string& filename ); // temporary
 
-		virtual program* create_program( const string& vs_source, const string& fs_source );
+		virtual program create_program( const string& vs_source, const string& fs_source );
+		virtual vertex_array create_vertex_array();
 		virtual buffer create_buffer( buffer_type type, buffer_hint hint, size_t size, const void* source = nullptr );
 		virtual texture create_texture( ivec2 size, image_format aformat, sampler asampler, void* data = nullptr );
@@ -43,12 +53,33 @@
 		virtual void release( buffer b );
 		virtual void release( texture t );
-		virtual const texture_info* get_texture_info( texture t );
-		virtual const buffer_info* get_buffer_info( buffer t );
+		virtual void release( vertex_array va );
+		virtual void release( program p );
+		virtual const texture_info* get_texture_info( texture t ) const;
+		virtual const buffer_info* get_buffer_info( buffer t ) const;
+		virtual const vertex_array_info* get_vertex_array_info( vertex_array va ) const;
 
+		virtual int get_attribute_location( program p, const string& name, bool fatal = true ) const;
+		virtual void prepare_program( program p );
 		virtual ~gl_device();
+	protected:
+		uniform_base* get_uniform( program p, const string& name, bool fatal = true ) const;
+
+		// TODO: remove
+		virtual vertex_array_info* get_vertex_array_info_mutable( vertex_array );
+	
 	private:
+		bool compile( gl_program_info* p, const string& vertex_program, const string& fragment_program );
+		void update_uniforms( gl_program_info* p );
+		void load_attributes( gl_program_info* p );
+		void load_uniforms( gl_program_info* p );
+		bool compile( uint32 sh_type, const std::string& shader_code, unsigned& glid );
+
+
+
 		const void* m_info;
-		entity_store< gl_texture_info, texture > m_textures;
-		entity_store< gl_buffer_info,  buffer >  m_buffers;
+		entity_store< gl_texture_info, texture >        m_textures;
+		entity_store< gl_buffer_info,  buffer >         m_buffers;
+		entity_store< vertex_array_info, vertex_array > m_vertex_arrays;
+		entity_store< gl_program_info, program >        m_programs;
 	};
 
Index: trunk/nv/gl/gl_program.hh
===================================================================
--- trunk/nv/gl/gl_program.hh	(revision 302)
+++ 	(revision )
@@ -1,66 +1,0 @@
-// Copyright (C) 2012-2013 ChaosForge / Kornel Kisielewicz
-// http://chaosforge.org/
-//
-// This file is part of NV Libraries.
-// For conditions of distribution and use, see copyright notice in nv.hh
-/**
- * @file gl_program.hh
- * @author Kornel Kisielewicz epyon@chaosforge.org
- * @brief Program class
- */
-
-#ifndef NV_GL_PROGRAM_HH
-#define NV_GL_PROGRAM_HH
-
-#include <nv/common.hh>
-#include <nv/interface/program.hh>
-#include <nv/string.hh>
-
-namespace nv
-{
-
-	class gl_shader
-	{
-	public:
-		explicit gl_shader( uint32 sh_type );
-		gl_shader( uint32 sh_type, const string& shader_code );
-		~gl_shader();
-
-		bool compile( const std::string& shader_code );
-
-		void generate();
-		void free();
-		bool validate();
-
-		bool is_valid() const { return object_id != 0; }
-		unsigned int get_id() const	{ return object_id;	}
-	private:
-		uint32   shader_type;
-		unsigned object_id;
-	};
-
-	class gl_program : public program
-	{
-	public:
-		friend class gl_context;
-
-		gl_program( const string& vertex_program, const string& fragment_program );
-		bool compile( const string& vertex_program, const string& fragment_program );
-
-		virtual bool is_valid() const;
-
-		~gl_program();
-	protected:
-		bool validate();
-		void update_uniforms();
-		void load_attributes();
-		void load_uniforms();
-
-		unsigned  glid;
-		gl_shader vertex_shader;
-		gl_shader fragment_shader;
-	};
-
-} // namespace nv
-
-#endif // NV_GL_PROGRAM_HH
Index: trunk/nv/interface/context.hh
===================================================================
--- trunk/nv/interface/context.hh	(revision 302)
+++ trunk/nv/interface/context.hh	(revision 303)
@@ -16,5 +16,4 @@
 #include <nv/interface/device.hh>
 #include <nv/interface/camera.hh>
-#include <nv/interface/program.hh>
 #include <nv/interface/vertex_buffer.hh>
 #include <nv/interface/clear_state.hh>
@@ -39,5 +38,5 @@
 		mesh_interface() {}
 		virtual void update( uint32 ) {}
-		virtual void update( program* ) {}
+		virtual void update( program ) {}
 		virtual nv::vertex_array get_vertex_array() const = 0;
 		virtual size_t get_index_count() const = 0;
@@ -51,15 +50,12 @@
 		context( device* a_device )	
 		{ 
-			// TODO: this will pose a problem in case of multiple contexts
-			program::initialize_engine_uniforms();
 			m_device = a_device; 
 		}
 		virtual void bind( texture, texture_slot ) = 0;
 		virtual void bind( buffer ) = 0;
-		virtual void bind( program* ) = 0;
+		virtual void bind( program ) = 0;
 		virtual void bind( vertex_array ) = 0;
 		virtual void unbind( buffer ) = 0;
-		virtual void unbind( program* ) = 0;
-		virtual void unbind( vertex_array ) = 0;
+		virtual void unbind( program ) = 0;
 		virtual void update( texture, void* ) = 0;
 		virtual void update( buffer, const void*, size_t /*offset*/, size_t /*size*/ ) = 0;
@@ -67,20 +63,21 @@
 		virtual void clear( const clear_state& cs ) = 0;
 		// temporary
-		virtual void draw( primitive prim, const render_state& rs, program* p, vertex_array va, size_t count ) = 0;
+		virtual void draw( primitive prim, const render_state& rs, program p, vertex_array va, size_t count ) = 0;
 
-		virtual void draw( const render_state& rs, program* p, const mesh_interface* mesh )
+		void draw( const render_state& rs, program p, const mesh_interface* mesh )
 		{
 			draw( mesh->get_primitive_type(), rs, p, mesh->get_vertex_array(), mesh->get_index_count() );
 		}
-		virtual void draw( const render_state& rs, const scene_state& s, program* p, const mesh_interface* mesh )
+		void draw( const render_state& rs, const scene_state& s, program p, const mesh_interface* mesh )
 		{
-			p->apply_engine_uniforms( this, &s );
+			apply_engine_uniforms(p,s);
 			draw( rs, p, mesh );
 		}
-		virtual void draw( primitive prim, const render_state& rs, const scene_state& s, program* p, vertex_array va, size_t count )
+		void draw( primitive prim, const render_state& rs, const scene_state& s, program p, vertex_array va, size_t count )
 		{
-			p->apply_engine_uniforms( this, &s );
+			apply_engine_uniforms(p,s);
 			draw( prim, rs, p, va, count );
 		}
+		virtual void apply_engine_uniforms( program p, const scene_state& s ) = 0;
 
 		virtual void apply_render_state( const render_state& state ) = 0;
@@ -90,12 +87,6 @@
 		virtual ~context() 
 		{
-			// TODO: this will pose a problem in case of multiple contexts
-			program::destroy_engine_uniforms();
 		}
 	protected:
-		vertex_array_info* get_vertex_array_info( vertex_array va )
-		{
-			return m_device->m_vertex_arrays.get( va );
-		}
 
 		device*      m_device;
Index: trunk/nv/interface/device.hh
===================================================================
--- trunk/nv/interface/device.hh	(revision 302)
+++ trunk/nv/interface/device.hh	(revision 303)
@@ -16,4 +16,5 @@
 #include <nv/string.hh>
 #include <nv/handle.hh>
+#include <nv/interface/uniform.hh>
 #include <nv/interface/mesh_data.hh>
 #include <nv/interface/vertex_buffer.hh>
@@ -23,13 +24,39 @@
 {
 	class window;
-	class program;
+
+	enum texture_slot
+	{
+		TEX_DIFFUSE  = 0,
+		TEX_SPECULAR = 1,
+		TEX_NORMAL   = 2,
+		TEXTURE_0    = 0,
+		TEXTURE_1    = 1,
+		TEXTURE_2    = 2,
+		TEXTURE_3    = 3,
+		TEXTURE_4    = 4,
+		TEXTURE_5    = 5,
+		TEXTURE_6    = 6,
+		TEXTURE_7    = 7,
+	};
+
+
+	struct attribute
+	{
+		string   name;
+		int      location;
+		datatype type;
+		int      length;
+	};
+
+	typedef std::unordered_map< string, attribute >    attribute_map;
 
 	struct texture_tag {};
 	struct vertex_array_tag {};
 	struct buffer_tag {};
+	struct program_tag {};
 	typedef handle< uint32, 16, 16, buffer_tag >       buffer;
 	typedef handle< uint32, 16, 16, texture_tag >      texture;
 	typedef handle< uint32, 16, 16, vertex_array_tag > vertex_array;
-
+	typedef handle< uint32, 16, 16, program_tag >      program;
 
 	struct sampler
@@ -102,49 +129,40 @@
 	};
 
+	struct program_info
+	{
+		attribute_map       m_attribute_map;
+		uniform_map	        m_uniform_map;
+		engine_uniform_list m_engine_uniforms;
+	};
+
 	class device
 	{
 		friend class context;
 	public:
+		device()
+		{
+			initialize_engine_uniforms();
+		}
 		virtual window* create_window( uint16 width, uint16 height, bool fullscreen ) = 0;
 		virtual window* adopt_window( void* sys_w_handle, void* sys_dc ) = 0;
-		virtual program* create_program( const string& vs_source, const string& fs_source ) = 0;
+		virtual program create_program( const string& vs_source, const string& fs_source ) = 0;
 		virtual buffer create_buffer( buffer_type type, buffer_hint hint, size_t size, const void* source = nullptr ) = 0;
 		virtual image_data* create_image_data( const std::string& filename ) = 0; // temporary
 		virtual texture create_texture( ivec2 size, image_format aformat, sampler asampler, void* data = nullptr ) = 0;
+		virtual vertex_array create_vertex_array() = 0;
 		virtual void release( texture ) = 0;
 		virtual void release( buffer ) = 0;
-		virtual const texture_info* get_texture_info( texture ) = 0;
-		virtual const buffer_info* get_buffer_info( buffer ) = 0;
+		virtual void release( vertex_array ) = 0;
+		virtual void release( program ) = 0;
+		virtual const texture_info* get_texture_info( texture ) const = 0;
+		virtual const buffer_info* get_buffer_info( buffer ) const = 0;
+		virtual const vertex_array_info* get_vertex_array_info( vertex_array ) const = 0;
 		virtual uint32 get_ticks() = 0;
 		virtual void delay( uint32 ms ) = 0;
+
 		virtual texture create_texture( image_data* data, sampler asampler ) 
 		{
 			return create_texture( data->get_size(), data->get_format(), asampler, (void*)data->get_data() );
 		}
-
-		virtual vertex_array create_vertex_array()
-		{
-			vertex_array result = m_vertex_arrays.create();
-			vertex_array_info* info = m_vertex_arrays.get( result );
-			info->count       = 0;
-			info->index       = buffer();
-			info->index_owner = false;
-			info->index_type  = USHORT;
-			return result;
-		}
-
-		virtual void release( vertex_array va )
-		{
-			vertex_array_info* info = m_vertex_arrays.get( va );
-			if ( info )
-			{
-				for ( uint32 i = 0; i < info->count; ++i )
-				{
-					if ( info->attr[i].owner ) release( info->attr[i].vbuffer );
-				}
-				if ( info->index.is_valid() && info->index_owner) release( info->index );
-				m_vertex_arrays.destroy( va );
-			}
-		};
 
 		template < typename VTX, slot SLOT >
@@ -222,5 +240,5 @@
 		void replace_vertex_buffer( vertex_array va, buffer vb, bool owner )
 		{
-			vertex_array_info* info = m_vertex_arrays.get( va );
+			vertex_array_info* info = get_vertex_array_info_mutable( va );
 			if ( info )
 			{
@@ -237,5 +255,5 @@
 		void replace_vertex_buffer( vertex_array va, buffer vb, slot location, bool owner )
 		{
-			vertex_array_info* info = m_vertex_arrays.get( va );
+			vertex_array_info* info = get_vertex_array_info_mutable( va );
 			if ( info )
 			{
@@ -255,5 +273,5 @@
 		void update_attribute_offset( vertex_array va, slot location, size_t offset ) 
 		{
-			vertex_array_info* info = m_vertex_arrays.get( va );
+			vertex_array_info* info = get_vertex_array_info_mutable( va );
 			if ( info )
 			{
@@ -270,5 +288,5 @@
 		void set_index_buffer( vertex_array va, buffer b, datatype datatype, bool owner ) 
 		{
-			vertex_array_info* info = m_vertex_arrays.get( va );
+			vertex_array_info* info = get_vertex_array_info_mutable( va );
 			if ( info )
 			{
@@ -284,7 +302,7 @@
 		}
 
-		void add_vertex_buffer( vertex_array va, slot location, buffer buf, datatype datatype, size_t components, size_t offset = 0, size_t stride = 0, bool owner = true ) 
-		{
-			vertex_array_info* info = m_vertex_arrays.get( va );
+		virtual void add_vertex_buffer( vertex_array va, slot location, buffer buf, datatype datatype, size_t components, size_t offset = 0, size_t stride = 0, bool owner = true )
+		{
+			vertex_array_info* info = get_vertex_array_info_mutable( va );
 			if ( info )
 			{
@@ -304,5 +322,5 @@
 		buffer find_buffer( vertex_array va, slot location )
 		{
-			vertex_array_info* info = m_vertex_arrays.get( va );
+			const vertex_array_info* info = get_vertex_array_info( va );
 			if ( info )
 			{
@@ -344,13 +362,126 @@
 		}
 
+		int try_get_attribute_location( program p, const string& name ) const
+		{
+			return get_attribute_location( p, name, false );
+		}
+
+		virtual int get_attribute_location( program p, const string& name, bool fatal = true ) const = 0;
+
+		template < typename T >
+		void set_uniform_array( program p, const string& name, const T* value, uint32 count, bool fatal = true )
+		{
+			uniform_base* base = get_uniform( p, name, fatal );
+			if ( base != nullptr )
+			{
+				if ( base->type_check( type_to_enum<T>::type ) )
+				{
+					// TODO: nicer check
+					NV_ASSERT( (int)count <= base->get_length(), "LENGTH CHECK FAIL" );
+					((uniform<T>*)( base ))->set_value( value, count );
+				}
+			}
+		}
+
+		template < typename T >
+		void set_uniform_array( const string& name, const std::vector<T>& value )
+		{
+			set_uniform_array( program p, name, (const T*)value.data(), value.size() );
+		}
+
+		template < typename T >
+		void set_opt_uniform_array( program p, const string& name, const T* value, uint32 count )
+		{
+			set_uniform_array( p, name, value, count, false );
+		}
+
+		template < typename T >
+		void set_opt_uniform_array( program p, const string& name, const std::vector<T>& value )
+		{
+			set_uniform_array( p, name, (const T*)value.data(), value.size(), false );
+		}
+
+
+		template < typename T >
+		void set_uniform( program p, const string& name, const T& value, bool fatal = true )
+		{
+			uniform_base* base = get_uniform( p, name, fatal );
+			if ( base != nullptr )
+			{
+				if ( base->type_check( type_to_enum<T>::type ) )
+				{
+					((uniform<T>*)( base ))->set_value( value );
+				}
+			}
+		}
+
+		template < typename T >
+		void set_opt_uniform( program p, const string& name, const T& value )
+		{
+			set_uniform( p, name, value, false );
+		}
 
 		virtual ~device()
 		{
-			// TODO: releases buffers via gl_context which is destoryed!
-			while ( m_vertex_arrays.size() > 0 )
-				release( m_vertex_arrays.get_handle(0) );
-		}
+			destroy_engine_uniforms();
+		}
+
+		// This is done this way to avoid compilation unit creation
+		static engine_uniform_factory_map& get_uniform_factory()
+		{
+			static engine_uniform_factory_map s_engine_uniform_factory_map;
+			return s_engine_uniform_factory_map;
+		}
+
+		// This is done this way to avoid compilation unit creation
+		static engine_link_uniform_factory_map& get_link_uniform_factory()
+		{
+			static engine_link_uniform_factory_map s_engine_link_uniform_factory_map;
+			return s_engine_link_uniform_factory_map;
+		}
+
+		virtual void prepare_program( program p ) = 0;
+
 	protected:
-		entity_store< vertex_array_info, vertex_array > m_vertex_arrays;
+		virtual uniform_base* get_uniform( program p, const string& name, bool fatal = true ) const = 0;
+
+		// TODO: remove
+		virtual vertex_array_info* get_vertex_array_info_mutable( vertex_array ) = 0;
+
+		void initialize_engine_uniforms()
+		{
+			engine_uniform_factory_map& factory_map = get_uniform_factory();
+			factory_map[ "nv_m_view" ]       = new engine_uniform_factory< engine_uniform_m_view >();
+			factory_map[ "nv_m_view_inv" ]   = new engine_uniform_factory< engine_uniform_m_view_inv >();
+			factory_map[ "nv_m_model" ]      = new engine_uniform_factory< engine_uniform_m_model >();
+			factory_map[ "nv_m_model_inv" ]  = new engine_uniform_factory< engine_uniform_m_model_inv >();
+			factory_map[ "nv_m_modelview" ]  = new engine_uniform_factory< engine_uniform_m_modelview >();
+			factory_map[ "nv_m_projection" ] = new engine_uniform_factory< engine_uniform_m_projection >();
+			factory_map[ "nv_m_normal" ]     = new engine_uniform_factory< engine_uniform_m_normal >();
+			factory_map[ "nv_m_mvp" ]        = new engine_uniform_factory< engine_uniform_m_mvp >();
+			factory_map[ "nv_v_camera_position" ]  = new engine_uniform_factory< engine_uniform_v_camera_position >();
+			factory_map[ "nv_v_camera_direction" ] = new engine_uniform_factory< engine_uniform_v_camera_direction >();
+
+			engine_link_uniform_factory_map& factory_link_map = get_link_uniform_factory();
+			factory_link_map[ "nv_texture_0" ] = new engine_link_uniform_int<0>();
+			factory_link_map[ "nv_texture_1" ] = new engine_link_uniform_int<1>();
+			factory_link_map[ "nv_texture_2" ] = new engine_link_uniform_int<2>();
+			factory_link_map[ "nv_texture_3" ] = new engine_link_uniform_int<3>();
+			factory_link_map[ "nv_texture_4" ] = new engine_link_uniform_int<4>();
+			factory_link_map[ "nv_texture_5" ] = new engine_link_uniform_int<5>();
+			factory_link_map[ "nv_texture_6" ] = new engine_link_uniform_int<6>();
+			factory_link_map[ "nv_texture_7" ] = new engine_link_uniform_int<7>();
+			factory_link_map[ "nv_t_diffuse" ] = new engine_link_uniform_int<0>();
+			factory_link_map[ "nv_t_specular"] = new engine_link_uniform_int<1>();
+			factory_link_map[ "nv_t_normal"  ] = new engine_link_uniform_int<2>();
+		}
+		void destroy_engine_uniforms()
+		{
+			for ( auto& i : get_uniform_factory() ) delete i.second;
+			for ( auto& i : get_link_uniform_factory() ) delete i.second;
+			get_uniform_factory().clear();
+			get_link_uniform_factory().clear();
+		}
+
 	};
 
Index: trunk/nv/interface/program.hh
===================================================================
--- trunk/nv/interface/program.hh	(revision 302)
+++ 	(revision )
@@ -1,333 +1,0 @@
-// Copyright (C) 2012-2014 ChaosForge / Kornel Kisielewicz
-// http://chaosforge.org/
-//
-// This file is part of NV Libraries.
-// For conditions of distribution and use, see copyright notice in nv.hh
-/**
- * @file program.hh
- * @author Kornel Kisielewicz epyon@chaosforge.org
- * @brief Program class
- */
-
-#ifndef NV_PROGRAM_HH
-#define NV_PROGRAM_HH
-
-#include <unordered_map>
-#include <nv/interface/uniform.hh>
-#include <nv/interface/vertex.hh>
-#include <nv/logging.hh>
-#include <nv/exception.hh>
-#include <nv/common.hh>
-#include <nv/string.hh>
-#include <nv/math.hh>
-
-namespace nv
-{
-	class camera;
-
-	enum texture_slot
-	{
-		TEX_DIFFUSE  = 0,
-		TEX_SPECULAR = 1,
-		TEX_NORMAL   = 2,
-		TEXTURE_0    = 0,
-		TEXTURE_1    = 1,
-		TEXTURE_2    = 2,
-		TEXTURE_3    = 3,
-		TEXTURE_4    = 4,
-		TEXTURE_5    = 5,
-		TEXTURE_6    = 6,
-		TEXTURE_7    = 7,
-	};
-
-
-	class attribute
-	{
-	public:
-		attribute( const string& name, int location, datatype type,	int length ) :
-			m_name( name ),	m_location( location ),	m_type( type ), m_length( length ) {}
-		const string& get_name() const { return m_name; }
-		int get_location() const { return m_location; }
-		datatype get_type() const { return m_type; }
-		int get_length() const { return m_length; }
-
-	protected:
-		string   m_name;
-		int      m_location;
-		datatype m_type;
-		int      m_length;
-	};
-
-	typedef std::unordered_map< string, attribute* >    attribute_map;
-
-	class program
-	{
-	public:
-		virtual bool is_valid() const = 0;
-		
-		virtual ~program()
-		{
-			for ( auto& i : m_attribute_map )   delete i.second;
-			for ( auto& i : m_uniform_map )     delete i.second;
-		}
-
-		const attribute_map& get_attributes() const { return m_attribute_map; }
-		const uniform_map& get_uniforms() const { return m_uniform_map; }
-
-		attribute* try_get_attribute( const string& name ) const
-		{
-			attribute_map::const_iterator i = m_attribute_map.find( name );
-			if ( i != m_attribute_map.end() )
-			{
-				return i->second;
-			}
-			return nullptr;
-		}
-
-		int try_get_attribute_location( const string& name ) const
-		{
-			attribute_map::const_iterator i = m_attribute_map.find( name );
-			if ( i != m_attribute_map.end() )
-			{
-				return i->second->get_location();
-			}
-			return -1;
-		}
-
-		attribute* get_attribute( const string& name ) const
-		{
-			attribute_map::const_iterator i = m_attribute_map.find( name );
-			if ( i != m_attribute_map.end() )
-			{
-				return i->second;
-			}
-			NV_LOG( LOG_ERROR, "Attribute '" << name << "' not found in program!" );
-			NV_THROW( runtime_error, ( "Attribute '"+name+"' not found!" ) );
-		}
-
-		uniform_base* try_get_uniform( const string& name ) const
-		{
-			uniform_map::const_iterator i = m_uniform_map.find( name );
-			if ( i != m_uniform_map.end() )
-			{
-				return i->second;
-			}
-			return nullptr;
-		}
-
-		uniform_base* get_uniform( const string& name ) const
-		{
-			uniform_map::const_iterator i = m_uniform_map.find( name );
-			if ( i != m_uniform_map.end() )
-			{
-				return i->second;
-			}
-			NV_LOG( LOG_ERROR, "Uniform '" << name << "' not found in program!" );
-			NV_THROW( runtime_error, ( "Uniform '"+name+"' not found!" ) );
-		}
-
-		template < typename T >
-		void set_uniform_array( const string& name, const T* value, uint32 count )
-		{
-			uniform_base* base = get_uniform( name );
-			// restore typechecking, but remember to accept int for float!
-			// if ( /* base->get_type() != type_to_enum<T>::type */ )
-			// {
-			//		NV_LOG( LOG_ERROR, "Uniform '" << name << "' not found in program!" );
-			//		return;
-			// }
-			// TODO: nicer check
-			NV_ASSERT( (int)count <= base->get_length(), "LENGTH CHECK FAIL" );
-			((uniform<T>*)( base ))->set_value( value, count );
-		}
-
-		template < typename T >
-		void set_uniform_array( const string& name, const std::vector<T>& value )
-		{
-			uniform_base* base = get_uniform( name );
-			// restore typechecking, but remember to accept int for float!
-			// if ( /* base->get_type() != type_to_enum<T>::type */ )
-			// {
-			//		NV_LOG( LOG_ERROR, "Uniform '" << name << "' not found in program!" );
-			//		return;
-			// }
-			// TODO: nicer check
-			NV_ASSERT( (int)value.size() <= base->get_length(), "LENGTH CHECK FAIL" );
-			((uniform<T>*)( base ))->set_value( value.data(), value.size() );
-		}
-
-		template < typename T >
-		void set_opt_uniform_array( const string& name, const T* value, uint32 count )
-		{
-			uniform_base* base = try_get_uniform( name );
-			if (!base) return;
-			// restore typechecking, but remember to accept int for float!
-			// if ( /* base->get_type() != type_to_enum<T>::type */ )
-			// {
-			//		NV_LOG( LOG_ERROR, "Uniform '" << name << "' not found in program!" );
-			//		return;
-			// }
-			// TODO: nicer check
-			NV_ASSERT( (int)count <= base->get_length(), "LENGTH CHECK FAIL" );
-			((uniform<T>*)( base ))->set_value( value, count );
-		}
-
-		template < typename T >
-		void set_opt_uniform_array( const string& name, const std::vector<T>& value )
-		{
-			uniform_base* base = try_get_uniform( name );
-			if (!base) return;
-			// restore typechecking, but remember to accept int for float!
-			// if ( /* base->get_type() != type_to_enum<T>::type */ )
-			// {
-			//		NV_LOG( LOG_ERROR, "Uniform '" << name << "' not found in program!" );
-			//		return;
-			// }
-			// TODO: nicer check
-			NV_ASSERT( (int)value.size() <= base->get_length(), "LENGTH CHECK FAIL" );
-			((uniform<T>*)( base ))->set_value( value.data(), value.size() );
-		}
-
-
-		template < typename T >
-		void set_uniform( const string& name, const T& value )
-		{
-			uniform_base* base = try_get_uniform( name );
-			// restore typechecking, but remember to accept int for float!
-			// if ( /* base->get_type() != type_to_enum<T>::type */ )
-			// {
-			//		NV_LOG( LOG_ERROR, "Uniform '" << name << "' not found in program!" );
-			//		return;
-			// }
-			((uniform<T>*)( base ))->set_value( value );
-		}
-
-		template < typename T >
-		void set_opt_uniform( const string& name, const T& value )
-		{
-			uniform_base* base = try_get_uniform( name );
-			if ( base != nullptr )
-			{
-				// restore typechecking, but remember to accept int for float!
-				// if ( /* base->get_type() != type_to_enum<T>::type */ )
-				// {
-				//		NV_LOG( LOG_ERROR, "Uniform '" << name << "' not found in program!" );
-				//		return;
-				// }
-				((uniform<T>*)( base ))->set_value( value );
-			}
-		}
-
-		void apply_engine_uniforms( const context* ctx, const scene_state* s )
-		{
-			for ( auto u : m_engine_uniforms )
-			{
-				u->set( ctx, s );
-			}
-		}
-
-		void apply_link_engine_uniforms()
-		{
-			engine_link_uniform_factory_map& factory_map = get_link_uniform_factory();
-			for ( auto& i : m_uniform_map )
-			{
-				auto j = factory_map.find( i.first );
-				if ( j != factory_map.end() )
-				{
-					j->second->set( i.second );
-				}				
-			}
-		}
-
-		void bind_engine_uniforms()
-		{
-			engine_uniform_factory_map& factory_map = get_uniform_factory();
-			for ( auto& i : m_uniform_map )
-			{
-				auto j = factory_map.find( i.first );
-				if ( j != factory_map.end() )
-				{
-					m_engine_uniforms.push_back( j->second->create( i.second ) );
-				}				
-			}
-		}
-
-		// This is done this way to avoid compilation unit creation
-		static engine_uniform_factory_map& get_uniform_factory()
-		{
-			static engine_uniform_factory_map s_engine_uniform_factory_map;
-			return s_engine_uniform_factory_map;
-		}
-
-		// This is done this way to avoid compilation unit creation
-		static engine_link_uniform_factory_map& get_link_uniform_factory()
-		{
-			static engine_link_uniform_factory_map s_engine_link_uniform_factory_map;
-			return s_engine_link_uniform_factory_map;
-		}
-
-		static void initialize_engine_uniforms()
-		{
-			engine_uniform_factory_map& factory_map = get_uniform_factory();
-			factory_map[ "nv_m_view" ]       = new engine_uniform_factory< engine_uniform_m_view >();
-			factory_map[ "nv_m_view_inv" ]   = new engine_uniform_factory< engine_uniform_m_view_inv >();
-			factory_map[ "nv_m_model" ]      = new engine_uniform_factory< engine_uniform_m_model >();
-			factory_map[ "nv_m_model_inv" ]  = new engine_uniform_factory< engine_uniform_m_model_inv >();
-			factory_map[ "nv_m_modelview" ]  = new engine_uniform_factory< engine_uniform_m_modelview >();
-			factory_map[ "nv_m_projection" ] = new engine_uniform_factory< engine_uniform_m_projection >();
-			factory_map[ "nv_m_normal" ]     = new engine_uniform_factory< engine_uniform_m_normal >();
-			factory_map[ "nv_m_mvp" ]        = new engine_uniform_factory< engine_uniform_m_mvp >();
-			factory_map[ "nv_v_camera_position" ]  = new engine_uniform_factory< engine_uniform_v_camera_position >();
-			factory_map[ "nv_v_camera_direction" ] = new engine_uniform_factory< engine_uniform_v_camera_direction >();
-
-			engine_link_uniform_factory_map& factory_link_map = get_link_uniform_factory();
-			factory_link_map[ "nv_texture_0" ] = new engine_link_uniform_int<0>();
-			factory_link_map[ "nv_texture_1" ] = new engine_link_uniform_int<1>();
-			factory_link_map[ "nv_texture_2" ] = new engine_link_uniform_int<2>();
-			factory_link_map[ "nv_texture_3" ] = new engine_link_uniform_int<3>();
-			factory_link_map[ "nv_texture_4" ] = new engine_link_uniform_int<4>();
-			factory_link_map[ "nv_texture_5" ] = new engine_link_uniform_int<5>();
-			factory_link_map[ "nv_texture_6" ] = new engine_link_uniform_int<6>();
-			factory_link_map[ "nv_texture_7" ] = new engine_link_uniform_int<7>();
-			factory_link_map[ "nv_t_diffuse" ] = new engine_link_uniform_int<0>();
-			factory_link_map[ "nv_t_specular"] = new engine_link_uniform_int<1>();
-			factory_link_map[ "nv_t_normal"  ] = new engine_link_uniform_int<2>();
-		}
-
-		static void destroy_engine_uniforms()
-		{
-			for ( auto& i : get_uniform_factory() ) delete i.second;
-			for ( auto& i : get_link_uniform_factory() ) delete i.second;
-			get_uniform_factory().clear();
-			get_link_uniform_factory().clear();
-		}
-
-	protected:
-
-		uniform_base* create_uniform( datatype utype, const string& name, int location, int length )
-		{
-			switch( utype )
-			{
-			case FLOAT          : return new uniform< enum_to_type< FLOAT          >::type >( name, location, length );
-			case INT            : return new uniform< enum_to_type< INT            >::type >( name, location, length );
-			case FLOAT_VECTOR_2 : return new uniform< enum_to_type< FLOAT_VECTOR_2 >::type >( name, location, length );
-			case FLOAT_VECTOR_3 : return new uniform< enum_to_type< FLOAT_VECTOR_3 >::type >( name, location, length );
-			case FLOAT_VECTOR_4 : return new uniform< enum_to_type< FLOAT_VECTOR_4 >::type >( name, location, length );
-			case INT_VECTOR_2   : return new uniform< enum_to_type< INT_VECTOR_2   >::type >( name, location, length );
-			case INT_VECTOR_3   : return new uniform< enum_to_type< INT_VECTOR_3   >::type >( name, location, length );
-			case INT_VECTOR_4   : return new uniform< enum_to_type< INT_VECTOR_4   >::type >( name, location, length );
-			case FLOAT_MATRIX_2 : return new uniform< enum_to_type< FLOAT_MATRIX_2 >::type >( name, location, length );
-			case FLOAT_MATRIX_3 : return new uniform< enum_to_type< FLOAT_MATRIX_3 >::type >( name, location, length );
-			case FLOAT_MATRIX_4 : return new uniform< enum_to_type< FLOAT_MATRIX_4 >::type >( name, location, length );
-			default     : return nullptr;
-			}
-		}
-
-		attribute_map                     m_attribute_map;
-		uniform_map	                      m_uniform_map;
-		engine_uniform_list               m_engine_uniforms;
-	};
-
-} // namespace nv
-
-#endif // NV_PROGRAM_HH
Index: trunk/nv/interface/uniform.hh
===================================================================
--- trunk/nv/interface/uniform.hh	(revision 302)
+++ trunk/nv/interface/uniform.hh	(revision 303)
@@ -27,4 +27,15 @@
 		uniform_base( const string& name, datatype type, int location, int length ) 
 			: m_name( name ), m_type( type ), m_location(location), m_length( length ), m_dirty( true ) {}
+		bool try_type_check( datatype )
+		{
+			// TODO: proper typecheck with type decay
+			return true;
+		}
+		bool type_check( datatype otype )
+		{
+			if ( try_type_check(otype) ) return true;
+			// TODO: report error
+			return false;
+		}
 		datatype get_type() const { return m_type; }
 		int get_location() const { return m_location; }
@@ -32,4 +43,5 @@
 		bool is_dirty() const { return m_dirty; }
 		void clean() { m_dirty = false; }
+		static uniform_base* create( datatype utype, const string& name, int location, int length );
 		virtual ~uniform_base() {}
 	protected:
@@ -217,4 +229,22 @@
 	};
 
+	inline uniform_base* uniform_base::create( datatype utype, const string& name, int location, int length )
+	{
+		switch( utype )
+		{
+		case FLOAT          : return new uniform< enum_to_type< FLOAT          >::type >( name, location, length );
+		case INT            : return new uniform< enum_to_type< INT            >::type >( name, location, length );
+		case FLOAT_VECTOR_2 : return new uniform< enum_to_type< FLOAT_VECTOR_2 >::type >( name, location, length );
+		case FLOAT_VECTOR_3 : return new uniform< enum_to_type< FLOAT_VECTOR_3 >::type >( name, location, length );
+		case FLOAT_VECTOR_4 : return new uniform< enum_to_type< FLOAT_VECTOR_4 >::type >( name, location, length );
+		case INT_VECTOR_2   : return new uniform< enum_to_type< INT_VECTOR_2   >::type >( name, location, length );
+		case INT_VECTOR_3   : return new uniform< enum_to_type< INT_VECTOR_3   >::type >( name, location, length );
+		case INT_VECTOR_4   : return new uniform< enum_to_type< INT_VECTOR_4   >::type >( name, location, length );
+		case FLOAT_MATRIX_2 : return new uniform< enum_to_type< FLOAT_MATRIX_2 >::type >( name, location, length );
+		case FLOAT_MATRIX_3 : return new uniform< enum_to_type< FLOAT_MATRIX_3 >::type >( name, location, length );
+		case FLOAT_MATRIX_4 : return new uniform< enum_to_type< FLOAT_MATRIX_4 >::type >( name, location, length );
+		default     : return nullptr;
+		}
+	}
 
 
Index: trunk/src/gfx/debug_draw.cc
===================================================================
--- trunk/src/gfx/debug_draw.cc	(revision 302)
+++ trunk/src/gfx/debug_draw.cc	(revision 303)
@@ -29,5 +29,5 @@
 
 nv::debug_data::debug_data( device* a_device )
-	: m_device( a_device ), m_program( nullptr ), m_va()
+	: m_device( a_device ), m_program(), m_va()
 {
 	m_program = m_device->create_program( nv_debug_draw_vertex_shader, nv_debug_draw_fragment_shader );
@@ -76,4 +76,4 @@
 {
 	m_device->release( m_va );
-	delete m_program;
+	m_device->release( m_program );
 }
Index: trunk/src/gfx/keyframed_mesh.cc
===================================================================
--- trunk/src/gfx/keyframed_mesh.cc	(revision 302)
+++ trunk/src/gfx/keyframed_mesh.cc	(revision 303)
@@ -98,7 +98,7 @@
 
 
-void nv::keyframed_mesh::update( program* a_program )
-{
-	a_program->set_opt_uniform( "nv_interpolate", m_interpolation );
+void nv::keyframed_mesh::update( program a_program )
+{
+	m_context->get_device()->set_opt_uniform( a_program, "nv_interpolate", m_interpolation );
 }
 
@@ -165,14 +165,14 @@
 }
 
-void nv::keyframed_mesh_gpu::update( program* a_program )
+void nv::keyframed_mesh_gpu::update( program a_program )
 {
 	if ( m_loc_next_position == -1 )
 	{
-		m_loc_next_position = a_program->get_attribute( "nv_next_position" )->get_location();
-		m_loc_next_normal   = a_program->get_attribute( "nv_next_normal" )->get_location();
+		device* dev = m_context->get_device();
+		m_loc_next_position = dev->get_attribute_location( a_program, "nv_next_position" );
+		m_loc_next_normal   = dev->get_attribute_location( a_program, "nv_next_normal" );
 		if ( m_has_tangent )
-			m_loc_next_tangent  = a_program->get_attribute( "nv_next_tangent" )->get_location();
-
-		device* dev = m_context->get_device();
+			m_loc_next_tangent  = dev->get_attribute_location( a_program, "nv_next_tangent" );
+
 		dev->add_vertex_buffer( m_va, (slot)m_loc_next_position, m_pbuffer, FLOAT, 3, 0, m_vsize, false );
 		dev->add_vertex_buffer( m_va, (slot)m_loc_next_normal,   m_pbuffer, FLOAT, 3, sizeof( vec3 ), m_vsize, false );
Index: trunk/src/gfx/skeletal_mesh.cc
===================================================================
--- trunk/src/gfx/skeletal_mesh.cc	(revision 302)
+++ trunk/src/gfx/skeletal_mesh.cc	(revision 303)
@@ -213,8 +213,8 @@
 }
 
-void nv::skeletal_mesh_gpu::update( program* a_program )
+void nv::skeletal_mesh_gpu::update( program a_program )
 {
 	if ( m_bone_data )
-		a_program->set_opt_uniform_array( "nv_m_bones", m_transform, m_bone_data->get_count() );
+		m_context->get_device()->set_opt_uniform_array( a_program, "nv_m_bones", m_transform, m_bone_data->get_count() );
 }
 
Index: trunk/src/gl/gl_context.cc
===================================================================
--- trunk/src/gl/gl_context.cc	(revision 302)
+++ trunk/src/gl/gl_context.cc	(revision 303)
@@ -9,5 +9,4 @@
 #include "nv/lib/sdl.hh"
 #include "nv/gl/gl_device.hh"
-#include "nv/gl/gl_program.hh"
 
 using namespace nv;
@@ -23,9 +22,12 @@
 }
 
-void nv::gl_context::bind( program* p )
-{
-	gl_program* glp = static_cast< gl_program* >( p );
-	glUseProgram( glp->glid );
-	glp->update_uniforms();
+void nv::gl_context::bind( program p )
+{
+	gl_program_info* info = ((gl_device*)m_device)->m_programs.get( p );
+	if ( info )
+	{
+		glUseProgram( info->glid );
+		((gl_device*)m_device)->update_uniforms( info );
+	}
 }
 
@@ -41,5 +43,5 @@
 void nv::gl_context::bind( vertex_array va )
 {
-	vertex_array_info* info = get_vertex_array_info( va );
+	const vertex_array_info* info = m_device->get_vertex_array_info( va );
 	if ( info )
 	{
@@ -68,5 +70,5 @@
 }
 
-void nv::gl_context::unbind( program* )
+void nv::gl_context::unbind( program )
 {
 	glUseProgram( 0 );
@@ -84,5 +86,5 @@
 void nv::gl_context::unbind( vertex_array va )
 {
-	vertex_array_info* info = get_vertex_array_info( va );
+	const vertex_array_info* info = m_device->get_vertex_array_info( va );
 	if ( info )
 	{
@@ -483,8 +485,20 @@
 }
 
-void gl_context::draw( primitive prim, const render_state& rs, program* p, vertex_array va, size_t count )
+void nv::gl_context::apply_engine_uniforms( program p, const scene_state& s )
+{
+	gl_program_info* info = ((gl_device*)m_device)->m_programs.get( p );
+	if ( info )
+	{
+		for ( auto u : info->m_engine_uniforms )
+		{
+			u->set( this, &s );
+		}
+	}
+}
+
+void gl_context::draw( primitive prim, const render_state& rs, program p, vertex_array va, size_t count )
 {
 	apply_render_state( rs );
-	vertex_array_info* info = get_vertex_array_info( va );
+	const vertex_array_info* info = m_device->get_vertex_array_info( va );
 	if ( count > 0 && info )
 	{
Index: trunk/src/gl/gl_device.cc
===================================================================
--- trunk/src/gl/gl_device.cc	(revision 302)
+++ trunk/src/gl/gl_device.cc	(revision 303)
@@ -6,5 +6,4 @@
 
 #include "nv/gl/gl_window.hh"
-#include "nv/gl/gl_program.hh"
 #include "nv/logging.hh"
 #include "nv/lib/sdl.hh"
@@ -50,7 +49,13 @@
 }
 
-program* gl_device::create_program( const string& vs_source, const string& fs_source )
-{
-	return new gl_program( vs_source, fs_source );
+program gl_device::create_program( const string& vs_source, const string& fs_source )
+{
+	program result = m_programs.create();
+	gl_program_info* info = m_programs.get( result );
+
+	info->glid = glCreateProgram();
+	compile( info, vs_source, fs_source );
+	prepare_program( result );
+	return result;
 }
 
@@ -85,8 +90,12 @@
 gl_device::~gl_device()
 {
+	while ( m_vertex_arrays.size() > 0 )
+		release( m_vertex_arrays.get_handle(0) );
 	while ( m_textures.size() > 0 )
 		release( m_textures.get_handle(0) );
 	while ( m_buffers.size() > 0 )
 		release( m_buffers.get_handle(0) );
+	while ( m_programs.size() > 0 )
+		release( m_programs.get_handle(0) );
 
 	SDL_Quit();
@@ -154,5 +163,5 @@
 }
 
-const texture_info* nv::gl_device::get_texture_info( texture t )
+const texture_info* nv::gl_device::get_texture_info( texture t ) const
 {
 	return m_textures.get( t );
@@ -178,6 +187,301 @@
 }
 
-const buffer_info* nv::gl_device::get_buffer_info( buffer t )
+const buffer_info* nv::gl_device::get_buffer_info( buffer t ) const
 {
 	return m_buffers.get( t );
 }
+
+nv::vertex_array nv::gl_device::create_vertex_array()
+{
+	vertex_array result = m_vertex_arrays.create();
+	vertex_array_info* info = m_vertex_arrays.get( result );
+	info->count       = 0;
+	info->index       = buffer();
+	info->index_owner = false;
+	info->index_type  = USHORT;
+	return result;
+}
+
+void nv::gl_device::release( vertex_array va )
+{
+	vertex_array_info* info = m_vertex_arrays.get( va );
+	if ( info )
+	{
+		for ( uint32 i = 0; i < info->count; ++i )
+		{
+			if ( info->attr[i].owner ) release( info->attr[i].vbuffer );
+		}
+		if ( info->index.is_valid() && info->index_owner) release( info->index );
+		m_vertex_arrays.destroy( va );
+	}
+}
+
+void nv::gl_device::release( program p )
+{
+	gl_program_info* info = m_programs.get( p );
+	if ( info )
+	{
+		for ( auto& i : info->m_uniform_map )
+			delete i.second;
+
+		glDetachShader( info->glid, info->glidv );
+		glDetachShader( info->glid, info->glidf );
+		glDeleteShader( info->glidv );
+		glDeleteShader( info->glidf );
+		glDeleteProgram( info->glid );
+
+		m_programs.destroy( p );
+	}
+}
+
+const vertex_array_info* nv::gl_device::get_vertex_array_info( vertex_array va ) const 
+{
+	return m_vertex_arrays.get( va );
+}
+
+vertex_array_info* nv::gl_device::get_vertex_array_info_mutable( vertex_array va ) 
+{
+	return m_vertex_arrays.get( va );
+}
+
+void nv::gl_device::prepare_program( program p )
+{
+	gl_program_info* info = m_programs.get( p );
+	if ( info )
+	{
+		auto& map  = get_uniform_factory();
+		auto& lmap = get_link_uniform_factory();
+
+		for ( auto& i : info->m_uniform_map )
+		{
+			auto j = lmap.find( i.first );
+			if ( j != lmap.end() )
+			{
+				j->second->set( i.second );
+			}			
+
+			auto k = map.find( i.first );
+			if ( k != map.end() )
+			{
+				info->m_engine_uniforms.push_back( k->second->create( i.second ) );
+			}				
+		}
+	}
+}
+
+uniform_base* nv::gl_device::get_uniform( program p, const string& name, bool fatal /*= true */ ) const
+{
+	const gl_program_info* info = m_programs.get( p );
+	{
+		uniform_map::const_iterator i = info->m_uniform_map.find( name );
+		if ( i != info->m_uniform_map.end() )
+		{
+			return i->second;
+		}
+		if ( fatal )
+		{
+			NV_LOG( LOG_ERROR, "Uniform '" << name << "' not found in program!" );
+			NV_THROW( runtime_error, ( "Uniform '"+name+"' not found!" ) );
+		}
+	}
+	return nullptr;
+}
+
+int nv::gl_device::get_attribute_location( program p, const string& name, bool fatal /*= true */ ) const
+{
+	const gl_program_info* info = m_programs.get( p );
+	if ( info )
+	{
+		attribute_map::const_iterator i = info->m_attribute_map.find( name );
+		if ( i != info->m_attribute_map.end() )
+		{
+			return i->second.location;
+		}
+		if ( fatal )
+		{
+			NV_LOG( LOG_ERROR, "Attribute '" << name << "' not found in program!" );
+			NV_THROW( runtime_error, ( "Attribute '"+name+"' not found!" ) );
+		}
+	}
+	return -1;
+}
+
+bool nv::gl_device::compile( gl_program_info* p, const string& vertex_program, const string& fragment_program )
+{
+	if (!compile( GL_VERTEX_SHADER,   vertex_program, p->glidv ))   { return false; }
+	if (!compile( GL_FRAGMENT_SHADER, fragment_program, p->glidf )) { return false; }
+
+	glBindAttribLocation( p->glid, static_cast<GLuint>( slot::POSITION   ), "nv_position"  );
+	glBindAttribLocation( p->glid, static_cast<GLuint>( slot::TEXCOORD   ), "nv_texcoord"  );
+	glBindAttribLocation( p->glid, static_cast<GLuint>( slot::NORMAL     ), "nv_normal"    );
+	glBindAttribLocation( p->glid, static_cast<GLuint>( slot::COLOR      ), "nv_color"     );
+	glBindAttribLocation( p->glid, static_cast<GLuint>( slot::TANGENT    ), "nv_tangent"   );
+	glBindAttribLocation( p->glid, static_cast<GLuint>( slot::BONEINDEX  ), "nv_boneindex" );
+	glBindAttribLocation( p->glid, static_cast<GLuint>( slot::BONEWEIGHT ), "nv_boneweight");
+
+	glAttachShader( p->glid, p->glidf );
+	glAttachShader( p->glid, p->glidv );
+	glLinkProgram( p->glid );
+
+	const uint32 buffer_size = 2048;
+	char buffer[ buffer_size ] = { 0 };
+	int length;
+	int status;
+
+	glGetProgramiv( p->glid, GL_LINK_STATUS, &status );
+	glGetProgramInfoLog( p->glid, buffer_size, &length, buffer );
+
+	NV_LOG( LOG_INFO, "Program #" << p->glid << (status == GL_FALSE ? " failed to compile!" : " compiled successfully.") );
+
+	if ( length > 0 )
+	{
+		NV_LOG( LOG_INFO, "Program #" << p->glid << " log: " << buffer );
+	}
+
+	if ( status == GL_FALSE ) 
+	{
+		return false;
+	}
+
+	glValidateProgram( p->glid );
+	glGetProgramiv( p->glid, GL_VALIDATE_STATUS, &status );
+
+	if ( status == GL_FALSE )
+	{
+		glGetProgramInfoLog( p->glid, buffer_size, &length, buffer );
+		NV_LOG( LOG_ERROR, "Program #" << p->glid << " validation error : " << buffer );
+		return false;
+	}
+	load_attributes( p );
+	load_uniforms( p );
+	return true;
+}
+
+void nv::gl_device::update_uniforms( gl_program_info* p )
+{
+	for ( uniform_map::iterator i = p->m_uniform_map.begin(); 	i != p->m_uniform_map.end(); ++i ) 
+	{
+		uniform_base* ubase = i->second;
+		if ( ubase->is_dirty() )
+		{
+			int uloc = ubase->get_location();
+			switch( ubase->get_type() )
+			{
+			case FLOAT          : glUniform1fv( uloc, ubase->get_length(), ((uniform< enum_to_type< FLOAT >::type >*)( ubase ))->get_value() ); break;
+			case INT            : glUniform1iv( uloc, ubase->get_length(), ((uniform< enum_to_type< INT >::type >*)( ubase ))->get_value() ); break;
+			case FLOAT_VECTOR_2 : glUniform2fv( uloc, ubase->get_length(), (GLfloat*)((uniform< enum_to_type< FLOAT_VECTOR_2 >::type >*)( ubase ))->get_value()); break;
+			case FLOAT_VECTOR_3 : glUniform3fv( uloc, ubase->get_length(), (GLfloat*)((uniform< enum_to_type< FLOAT_VECTOR_3 >::type >*)( ubase ))->get_value()); break;
+			case FLOAT_VECTOR_4 : glUniform4fv( uloc, ubase->get_length(), (GLfloat*)((uniform< enum_to_type< FLOAT_VECTOR_4 >::type >*)( ubase ))->get_value()); break;
+			case INT_VECTOR_2   : glUniform2iv( uloc, ubase->get_length(), (GLint*)((uniform< enum_to_type< INT_VECTOR_2 >::type >*)( ubase ))->get_value()); break;
+			case INT_VECTOR_3   : glUniform3iv( uloc, ubase->get_length(), (GLint*)((uniform< enum_to_type< INT_VECTOR_3 >::type >*)( ubase ))->get_value()); break;
+			case INT_VECTOR_4   : glUniform4iv( uloc, ubase->get_length(), (GLint*)((uniform< enum_to_type< INT_VECTOR_4 >::type >*)( ubase ))->get_value()); break;
+			case FLOAT_MATRIX_2 : glUniformMatrix2fv( uloc, ubase->get_length(), GL_FALSE, (GLfloat*)((uniform< enum_to_type< FLOAT_MATRIX_2 >::type >*)( ubase ))->get_value()); break;
+			case FLOAT_MATRIX_3 : glUniformMatrix3fv( uloc, ubase->get_length(), GL_FALSE, (GLfloat*)((uniform< enum_to_type< FLOAT_MATRIX_3 >::type >*)( ubase ))->get_value()); break;
+			case FLOAT_MATRIX_4 : glUniformMatrix4fv( uloc, ubase->get_length(), GL_FALSE, (GLfloat*)((uniform< enum_to_type< FLOAT_MATRIX_4 >::type >*)( ubase ))->get_value()); break;
+			default : break; // error?
+			}
+			ubase->clean();
+		}
+	}
+}
+
+void nv::gl_device::load_attributes( gl_program_info* p )
+{
+	int params;
+	glGetProgramiv( p->glid, GL_ACTIVE_ATTRIBUTES, &params );
+
+	for ( unsigned i = 0; i < (unsigned)params; ++i )
+	{
+		int attr_nlen;
+		int attr_len;
+		unsigned attr_type;
+		char name_buffer[128];
+
+		glGetActiveAttrib( p->glid, i, 128, &attr_nlen, &attr_len, &attr_type, name_buffer );
+
+		string name( name_buffer, size_t(attr_nlen) );
+
+		// skip built-ins
+		if ( name.substr(0,3) == "gl_" ) continue;
+
+		int attr_loc = glGetAttribLocation( p->glid, name.c_str() );
+
+		attribute& attr = p->m_attribute_map[ name ];
+		attr.name     = name;
+		attr.location = attr_loc;
+		attr.type     = gl_enum_to_datatype( attr_type );
+		attr.length   = attr_len;
+	}
+}
+
+void nv::gl_device::load_uniforms( gl_program_info* p )
+{
+	int params;
+	glGetProgramiv( p->glid, GL_ACTIVE_UNIFORMS, &params );
+
+	for ( unsigned i = 0; i < size_t(params); ++i )
+	{
+		int uni_nlen;
+		int uni_len;
+		unsigned uni_type;
+		char name_buffer[128];
+
+		glGetActiveUniform( p->glid, i, 128, &uni_nlen, &uni_len, &uni_type, name_buffer );
+
+		string name( name_buffer, size_t(uni_nlen) );
+
+		// skip built-ins
+		if ( name.substr(0,3) == "gl_" ) continue;
+
+		int uni_loc = glGetUniformLocation( p->glid, name.c_str() );
+		datatype utype = gl_enum_to_datatype( uni_type );
+
+		// check for array
+		string::size_type arrchar = name.find('[');
+		if ( arrchar != string::npos )
+		{
+			name = name.substr( 0, arrchar );
+		}
+
+		uniform_base* u = uniform_base::create( utype, name, uni_loc, uni_len );
+		NV_ASSERT( u, "Unknown uniform type!" );
+		p->m_uniform_map[ name ] = u;
+	}
+}
+
+bool nv::gl_device::compile( uint32 sh_type, const std::string& shader_code, unsigned& glid )
+{
+	glid = glCreateShader( sh_type );
+
+	const char* pc = shader_code.c_str();
+
+	glShaderSource( glid,   1, &pc, 0 );
+	glCompileShader( glid );
+
+	const uint32 buffer_size = 1024;
+	char buffer[ buffer_size ] = { 0 };
+	int length;
+	int compile_ok = GL_FALSE;
+	glGetShaderiv(glid, GL_COMPILE_STATUS, &compile_ok);
+	glGetShaderInfoLog( glid, buffer_size, &length, buffer );
+
+	if ( length > 0 )
+	{
+		if ( compile_ok == 0 )
+		{
+			NV_LOG( LOG_ERROR, "Shader #" << glid << " error: " << buffer );
+		}
+		else
+		{
+			NV_LOG( LOG_INFO, "Shader #" << glid << " compiled successfully: " << buffer );
+		}
+	}
+	else
+	{
+		NV_LOG( LOG_INFO, "Shader #" << glid << " compiled successfully." );
+	}
+	return compile_ok != 0;
+
+}
+
+
Index: trunk/src/gl/gl_program.cc
===================================================================
--- trunk/src/gl/gl_program.cc	(revision 302)
+++ 	(revision )
@@ -1,263 +1,0 @@
-// Copyright (C) 2012-2013 Kornel Kisielewicz
-// This file is part of NV Libraries.
-// For conditions of distribution and use, see copyright notice in nv.hh
-
-#include "nv/gl/gl_program.hh"
-
-#include "nv/gl/gl_enum.hh"
-#include "nv/logging.hh"
-#include "nv/lib/gl.hh"
-
-#include <glm/glm.hpp>
-#include <glm/gtc/type_ptr.hpp>
-
-using namespace nv;
-
-gl_shader::gl_shader( uint32 sh_type ) 
-	: shader_type( sh_type ), object_id(0)
-{
-	// no op
-}
-
-gl_shader::gl_shader( uint32 sh_type, const string& shader_code )
-	: shader_type( sh_type ), object_id(0)
-{
-	compile( shader_code );
-}
-
-gl_shader::~gl_shader()
-{
-	free();
-}
-
-bool gl_shader::compile( const string& shader_code )
-{
-	generate();
-
-	const char* pc = shader_code.c_str();
-
-	glShaderSource( object_id,   1, &pc, 0 );
-	glCompileShader( object_id );
-
-	return validate();
-}
-
-void gl_shader::generate()
-{
-	if ( is_valid() )
-	{
-		free();
-	}
-	object_id = glCreateShader( shader_type );
-}
-
-void gl_shader::free()
-{
-	glDeleteShader( object_id );
-	object_id = 0;
-}
-
-bool gl_shader::validate()
-{
-	const uint32 buffer_size = 1024;
-	char buffer[ buffer_size ] = { 0 };
-	int length;
-	int compile_ok = GL_FALSE;
-	glGetShaderiv(object_id, GL_COMPILE_STATUS, &compile_ok);
-	glGetShaderInfoLog( object_id, buffer_size, &length, buffer );
-
-	if ( length > 0 )
-	{
-		if ( compile_ok == 0 )
-		{
-			NV_LOG( LOG_ERROR, "Shader #" << object_id << " error: " << buffer );
-		}
-		else
-		{
-			NV_LOG( LOG_INFO, "Shader #" << object_id << " compiled successfully: " << buffer );
-		}
-	}
-	else
-	{
-		NV_LOG( LOG_INFO, "Shader #" << object_id << " compiled successfully." );
-	}
-	return compile_ok != 0;
-}
-
-gl_program::gl_program( const string& vertex_program, const string& fragment_program )
-	: vertex_shader( GL_VERTEX_SHADER ), fragment_shader( GL_FRAGMENT_SHADER )
-{
-	glid = glCreateProgram();
-	compile( vertex_program, fragment_program );
-}
-
-gl_program::~gl_program()
-{
-	if ( glid != 0 )
-	{
-		// Detach the shaders from the program
-		glDetachShader( glid, vertex_shader.get_id() );
-		glDetachShader( glid, fragment_shader.get_id() );
-		glDeleteProgram( glid );
-	}
-}
-
-bool gl_program::compile( const string& vertex_program, const string& fragment_program )
-{
-	if (!vertex_shader.compile( vertex_program )) { return false; }
-	if (!fragment_shader.compile( fragment_program )) { return false; }
-
-	glBindAttribLocation( glid, static_cast<GLuint>( slot::POSITION   ), "nv_position"  );
-	glBindAttribLocation( glid, static_cast<GLuint>( slot::TEXCOORD   ), "nv_texcoord"  );
-	glBindAttribLocation( glid, static_cast<GLuint>( slot::NORMAL     ), "nv_normal"    );
-	glBindAttribLocation( glid, static_cast<GLuint>( slot::COLOR      ), "nv_color"     );
-	glBindAttribLocation( glid, static_cast<GLuint>( slot::TANGENT    ), "nv_tangent"   );
-	glBindAttribLocation( glid, static_cast<GLuint>( slot::BONEINDEX  ), "nv_boneindex" );
-	glBindAttribLocation( glid, static_cast<GLuint>( slot::BONEWEIGHT ), "nv_boneweight");
-
-	glAttachShader( glid, fragment_shader.get_id() );
-	glAttachShader( glid, vertex_shader.get_id() );
-	glLinkProgram( glid );
-
-	if (!validate())
-	{
-		return false;
-	}
-	load_attributes();
-	load_uniforms();
-	return true;
-}
-
-bool gl_program::is_valid() const
-{
-	return glid != 0;
-}
-
-void gl_program::load_attributes()
-{
-	int params;
-	glGetProgramiv( glid, GL_ACTIVE_ATTRIBUTES, &params );
-
-	for ( unsigned i = 0; i < (unsigned)params; ++i )
-	{
-		int attr_nlen;
-		int attr_len;
-		unsigned attr_type;
-		char name_buffer[128];
-
-		glGetActiveAttrib( glid, i, 128, &attr_nlen, &attr_len, &attr_type, name_buffer );
-
-		string name( name_buffer, size_t(attr_nlen) );
-
-		// skip built-ins
-		if ( name.substr(0,3) == "gl_" ) continue;
-
-		int attr_loc = glGetAttribLocation( glid, name.c_str() );
-
-		m_attribute_map[ name ] = new attribute( name, attr_loc, gl_enum_to_datatype( attr_type ), attr_len );
-	}
-}
-
-void gl_program::load_uniforms()
-{
-	int params;
-	glGetProgramiv( glid, GL_ACTIVE_UNIFORMS, &params );
-
-	for ( unsigned i = 0; i < size_t(params); ++i )
-	{
-		int uni_nlen;
-		int uni_len;
-		unsigned uni_type;
-		char name_buffer[128];
-
-		glGetActiveUniform( glid, i, 128, &uni_nlen, &uni_len, &uni_type, name_buffer );
-
-		string name( name_buffer, size_t(uni_nlen) );
-		
-		// skip built-ins
-		if ( name.substr(0,3) == "gl_" ) continue;
-
-		int uni_loc = glGetUniformLocation( glid, name.c_str() );
-		datatype utype = gl_enum_to_datatype( uni_type );
-		
-		// check for array
-		string::size_type arrchar = name.find('[');
-		if ( arrchar != string::npos )
-		{
-			name = name.substr( 0, arrchar );
-		}
-
-		uniform_base* u = create_uniform( utype, name, uni_loc, uni_len );
-		NV_ASSERT( u, "Unknown uniform type!" );
-		m_uniform_map[ name ] = u;
-	}
-
-	apply_link_engine_uniforms();
-	bind_engine_uniforms();
-}
-
-void gl_program::update_uniforms()
-{
-	for ( uniform_map::iterator i = m_uniform_map.begin(); 	i != m_uniform_map.end(); ++i ) 
-	{
-		uniform_base* ubase = i->second;
-		if ( ubase->is_dirty() )
-		{
-			int uloc = ubase->get_location();
-			switch( ubase->get_type() )
-			{
-			case FLOAT          : glUniform1fv( uloc, ubase->get_length(), ((uniform< enum_to_type< FLOAT >::type >*)( ubase ))->get_value() ); break;
-			case INT            : glUniform1iv( uloc, ubase->get_length(), ((uniform< enum_to_type< INT >::type >*)( ubase ))->get_value() ); break;
-			case FLOAT_VECTOR_2 : glUniform2fv( uloc, ubase->get_length(), (GLfloat*)((uniform< enum_to_type< FLOAT_VECTOR_2 >::type >*)( ubase ))->get_value()); break;
-			case FLOAT_VECTOR_3 : glUniform3fv( uloc, ubase->get_length(), (GLfloat*)((uniform< enum_to_type< FLOAT_VECTOR_3 >::type >*)( ubase ))->get_value()); break;
-			case FLOAT_VECTOR_4 : glUniform4fv( uloc, ubase->get_length(), (GLfloat*)((uniform< enum_to_type< FLOAT_VECTOR_4 >::type >*)( ubase ))->get_value()); break;
-			case INT_VECTOR_2   : glUniform2iv( uloc, ubase->get_length(), (GLint*)((uniform< enum_to_type< INT_VECTOR_2 >::type >*)( ubase ))->get_value()); break;
-			case INT_VECTOR_3   : glUniform3iv( uloc, ubase->get_length(), (GLint*)((uniform< enum_to_type< INT_VECTOR_3 >::type >*)( ubase ))->get_value()); break;
-			case INT_VECTOR_4   : glUniform4iv( uloc, ubase->get_length(), (GLint*)((uniform< enum_to_type< INT_VECTOR_4 >::type >*)( ubase ))->get_value()); break;
-			case FLOAT_MATRIX_2 : glUniformMatrix2fv( uloc, ubase->get_length(), GL_FALSE, (GLfloat*)((uniform< enum_to_type< FLOAT_MATRIX_2 >::type >*)( ubase ))->get_value()); break;
-			case FLOAT_MATRIX_3 : glUniformMatrix3fv( uloc, ubase->get_length(), GL_FALSE, (GLfloat*)((uniform< enum_to_type< FLOAT_MATRIX_3 >::type >*)( ubase ))->get_value()); break;
-			case FLOAT_MATRIX_4 : glUniformMatrix4fv( uloc, ubase->get_length(), GL_FALSE, (GLfloat*)((uniform< enum_to_type< FLOAT_MATRIX_4 >::type >*)( ubase ))->get_value()); break;
-			default : break; // error?
-			}
-			ubase->clean();
-		}
-	}
-
-}
-
-
-bool gl_program::validate()
-{
-	const uint32 buffer_size = 2048;
-	char buffer[ buffer_size ] = { 0 };
-	int length;
-	int status;
-
-	glGetProgramiv( glid, GL_LINK_STATUS, &status );
-	glGetProgramInfoLog( glid, buffer_size, &length, buffer );
-
-	NV_LOG( LOG_INFO, "Program #" << glid << (status == GL_FALSE ? " failed to compile!" : " compiled successfully.") );
-
-	if ( length > 0 )
-	{
-		NV_LOG( LOG_INFO, "Program #" << glid << " log: " << buffer );
-	}
-
-	if ( status == GL_FALSE ) 
-	{
-		return false;
-	}
-
-	glValidateProgram( glid );
-	glGetProgramiv( glid, GL_VALIDATE_STATUS, &status );
-
-	if ( status == GL_FALSE )
-	{
-		glGetProgramInfoLog( glid, buffer_size, &length, buffer );
-		NV_LOG( LOG_ERROR, "Program #" << glid << " validation error : " << buffer );
-		return false;
-	}
-	return true;
-}
-
-
Index: trunk/src/gui/gui_renderer.cc
===================================================================
--- trunk/src/gui/gui_renderer.cc	(revision 302)
+++ trunk/src/gui/gui_renderer.cc	(revision 303)
@@ -66,5 +66,5 @@
 public:
 	screen_render_data( context* actx, size_t initial_size )
-		: buffer( actx, VERTEX_BUFFER, DYNAMIC_DRAW, initial_size ), ctx( actx ), varray(), shader(nullptr)
+		: buffer( actx, VERTEX_BUFFER, DYNAMIC_DRAW, initial_size ), ctx( actx ), varray(), shader()
 	{
 
@@ -72,5 +72,5 @@
 	~screen_render_data() 
 	{ 
-		delete shader; 
+		ctx->get_device()->release( shader ); 
 		ctx->get_device()->release( varray ); 
 	}
@@ -80,5 +80,5 @@
 	nv::texture       tex;
 	nv::vertex_array  varray;
-	nv::program*      shader;
+	nv::program       shader;
 };
 
