Index: trunk/src/gl/gl_context.cc
===================================================================
--- trunk/src/gl/gl_context.cc	(revision 500)
+++ trunk/src/gl/gl_context.cc	(revision 501)
@@ -66,5 +66,4 @@
 		}
 	}
-	glBindBuffer( GL_ARRAY_BUFFER, 0 );
 	glBindVertexArray( 0 );
 
@@ -104,7 +103,7 @@
 		{
 			if ( info->attr[i].owner ) 
-				m_device->release( info->attr[i].vbuffer );
-		}
-		if ( info->index.is_valid() && info->index_owner) m_device->release( info->index );
+				release( info->attr[i].vbuffer );
+		}
+		if ( info->index.is_valid() && info->index_owner) release( info->index );
 		glDeleteVertexArrays( 1, &info->glid );
 		m_vertex_arrays.destroy( va );
@@ -223,6 +222,4 @@
 		glBindFramebuffer( GL_DRAW_FRAMEBUFFER, tinfo ? tinfo->glid : 0 );
 		unsigned filter = mask == clear_state::COLOR_BUFFER ? GL_LINEAR : GL_NEAREST;
-		int remove_below;
-		filter = GL_NEAREST;
 		glBlitFramebuffer( src1.x, src1.y, src2.x, src2.y, dst1.x, dst1.y, dst2.x, dst2.y, clear_state_buffers_to_mask( mask ), filter );
 		glBindFramebuffer( GL_READ_FRAMEBUFFER, 0 );
@@ -270,4 +267,5 @@
 }
 
+
 void nv::gl_context::bind( buffer b, texture t )
 {
@@ -304,4 +302,5 @@
 			set_active_texture( slot );
 			glBindTexture( texture_type_to_enum( info->type ), info->glid );
+			m_bound_textures[slot] = t;
 		}
 	}
@@ -314,5 +313,6 @@
 	if ( info )
 	{
-		glUseProgram( info->glid );
+		if ( m_bound_program != p )
+			glUseProgram( info->glid );
 		gdevice->update_uniforms( info );
 		if ( !info->validated )
@@ -320,4 +320,5 @@
 			validate_program( p );
 		}
+		m_bound_program = p;
 	}
 }
@@ -369,5 +370,7 @@
 void nv::gl_context::unbind( program )
 {
-	glUseProgram( 0 );
+	if ( m_bound_program )
+		glUseProgram( 0 );
+	m_bound_program = program();
 }
 
@@ -375,5 +378,8 @@
 {
 	if ( slot != m_active_slot )
+	{
 		glActiveTexture( GL_TEXTURE0 + static_cast<GLenum>( slot ) );
+		m_active_slot = slot;
+	}
 }
 
@@ -419,11 +425,11 @@
 		unsigned     gl_type = texture_type_to_enum( info->type );
 
-		if ( m_bound_textures[ m_active_slot ] != t )
-			glBindTexture( gl_type, info->glid );
-		int this_should_be_subTexImage;
+		bind( t, texture_slot::TEXTURE_0 );
 		if ( info->type == TEXTURE_3D || info->type == TEXTURE_2D_ARRAY )
-			glTexImage3D( gl_type, 0, static_cast<GLint>( nv::image_format_to_internal_enum( format.format ) ), size.x, size.y, size.z, 0, nv::image_format_to_enum( format.format ), nv::datatype_to_gl_enum( format.type ), data );
+//			glTexImage3D( gl_type, 0, static_cast<GLint>( nv::image_format_to_internal_enum( format.format ) ), size.x, size.y, size.z, 0, nv::image_format_to_enum( format.format ), nv::datatype_to_gl_enum( format.type ), data );
+			glTexSubImage3D( gl_type, 0, 0, 0, 0, size.x, size.y, size.z, nv::image_format_to_enum( format.format ), nv::datatype_to_gl_enum( format.type ), data );
 		else
-			glTexImage2D( gl_type, 0, static_cast<GLint>( nv::image_format_to_internal_enum(format.format) ), size.x, size.y, 0, nv::image_format_to_enum(format.format), nv::datatype_to_gl_enum(format.type), data );
+//			glTexImage2D( gl_type, 0, static_cast<GLint>( nv::image_format_to_internal_enum(format.format) ), size.x, size.y, 0, nv::image_format_to_enum(format.format), nv::datatype_to_gl_enum(format.type), data );
+			glTexSubImage2D( gl_type, 0, 0, 0, size.x, size.y, nv::image_format_to_enum( format.format ), nv::datatype_to_gl_enum( format.type ), data );
 	}
 }
@@ -518,7 +524,10 @@
 void gl_context::set_viewport( const ivec4& viewport )
 {
-	NV_ASSERT_ALWAYS( viewport.z > 0 && viewport.w > 0, "viewport dimensions must be greater than zero!" );
-	m_viewport = viewport;
-	glViewport( viewport.x, viewport.y, viewport.z, viewport.w );
+	if ( m_viewport != viewport )
+	{
+		NV_ASSERT_ALWAYS( viewport.z > 0 && viewport.w > 0, "viewport dimensions must be greater than zero!" );
+		m_viewport = viewport;
+		glViewport( viewport.x, viewport.y, viewport.z, viewport.w );
+	}
 }
 
@@ -860,4 +869,144 @@
 }
 
+nv::texture nv::gl_context::create_texture( texture_type type, ivec2 size, image_format aformat, sampler asampler, const void* data /*= nullptr */ )
+{
+	texture result = create_texture( type, aformat.format );
+	gl_texture_info* info = static_cast<gl_device*>( m_device )->get_full_texture_info( result );
+	bind( result, texture_slot::TEXTURE_0 );
+	unsigned glid = info->glid;
+	unsigned gl_type = texture_type_to_enum( type );
+	GLenum gl_internal = GLenum( image_format_to_internal_enum( aformat.format ) );
+	unsigned gl_enum = image_format_to_enum( aformat.format );
+
+	bool is_depth = aformat.format == DEPTH16 || aformat.format == DEPTH24 || aformat.format == DEPTH32;
+
+	// Detect if mipmapping was requested
+	// 	if ( gl_type == GL_TEXTURE_2D && gl_enum != GL_RED_INTEGER && asampler.filter_min != sampler::LINEAR && asampler.filter_min != sampler::NEAREST )
+	// 	{
+	// 		// TODO: This should not be done if we use framebuffers!
+	// 		glTexParameteri( gl_type, GL_GENERATE_MIPMAP, GL_TRUE);
+	// 	}
+
+	if ( asampler.filter_max != sampler::NEAREST )
+	{
+		asampler.filter_max = sampler::LINEAR;
+	}
+
+	if ( gl_type != GL_TEXTURE_2D_MULTISAMPLE )
+	{
+		glTexParameteri( gl_type, GL_TEXTURE_MIN_FILTER, GLint( nv::sampler_filter_to_enum( asampler.filter_min ) ) );
+		glTexParameteri( gl_type, GL_TEXTURE_MAG_FILTER, GLint( nv::sampler_filter_to_enum( asampler.filter_max ) ) );
+	}
+
+	if ( gl_type != GL_TEXTURE_2D_MULTISAMPLE && gl_enum != GL_RED_INTEGER )
+	{
+		glTexParameteri( gl_type, GL_TEXTURE_WRAP_S, GLint( nv::sampler_wrap_to_enum( asampler.wrap_s ) ) );
+		glTexParameteri( gl_type, GL_TEXTURE_WRAP_T, GLint( nv::sampler_wrap_to_enum( asampler.wrap_t ) ) );
+	}
+
+	if ( is_depth )
+	{
+		//		glTexParameteri( gl_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+		//		glTexParameteri( gl_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+
+		// This is to allow usage of shadow2DProj function in the shader
+		glTexParameteri( gl_type, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE );
+		glTexParameteri( gl_type, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL );
+	}
+
+	// #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
+	// #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
+	// 
+	// 	float aniso = 0.0f;
+	// 	glGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &aniso );
+	// 	NV_LOG_INFO( "Anisotropy at ", aniso, " (", int( aniso ), " ) " );
+	// 	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso );
+
+	if ( gl_type != GL_TEXTURE_2D_MULTISAMPLE )
+		glTexImage2D( gl_type, 0, gl_internal, size.x, size.y, 0, gl_enum, nv::datatype_to_gl_enum( aformat.type ), data );
+	else
+		glTexImage2DMultisample( gl_type, 4, gl_internal, size.x, size.y, 1 );
+
+	if ( gl_type == GL_TEXTURE_2D && gl_enum != GL_RED_INTEGER && asampler.filter_min != sampler::LINEAR && asampler.filter_min != sampler::NEAREST )
+	{
+		// TODO: This should not be done if we use framebuffers!
+		glGenerateMipmap( gl_type );
+	}
+
+	bind( texture(), texture_slot::TEXTURE_0 );
+
+	info->type = type;
+	info->format = aformat;
+	info->tsampler = asampler;
+	info->size = ivec3( size.x, size.y, 1 );
+	info->glid = glid;
+
+	return result;
+}
+
+nv::texture nv::gl_context::create_texture( texture_type type, ivec3 size, image_format aformat, sampler asampler, const void* data /*= nullptr */ )
+{
+	texture result = create_texture( type, aformat.format );
+	gl_texture_info* info = static_cast<gl_device*>( m_device )->get_full_texture_info( result );
+	bind( result, texture_slot::TEXTURE_0 );
+	unsigned glid = info->glid;
+
+	unsigned gl_type = texture_type_to_enum( type );
+
+	bool is_depth = aformat.format == DEPTH16 || aformat.format == DEPTH24 || aformat.format == DEPTH32;
+
+	if ( asampler.filter_max != sampler::NEAREST )
+	{
+		asampler.filter_max = sampler::LINEAR;
+	}
+
+	glTexParameteri( gl_type, GL_TEXTURE_MIN_FILTER, GLint( nv::sampler_filter_to_enum( asampler.filter_min ) ) );
+	glTexParameteri( gl_type, GL_TEXTURE_MAG_FILTER, GLint( nv::sampler_filter_to_enum( asampler.filter_max ) ) );
+	glTexParameteri( gl_type, GL_TEXTURE_WRAP_S, GLint( nv::sampler_wrap_to_enum( asampler.wrap_s ) ) );
+	glTexParameteri( gl_type, GL_TEXTURE_WRAP_T, GLint( nv::sampler_wrap_to_enum( asampler.wrap_t ) ) );
+
+	if ( is_depth )
+	{
+		glTexParameteri( gl_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+		glTexParameteri( gl_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+
+		// This is to allow usage of shadow2DProj function in the shader
+		glTexParameteri( gl_type, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE );
+		glTexParameteri( gl_type, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL );
+	}
+
+	//glTexStorage3D( GL_TEXTURE_2D_ARRAY, mipLevelCount, GL_RGBA8, width, height, layerCount );
+	glTexImage3D( gl_type, 0, GLint( nv::image_format_to_internal_enum( aformat.format ) ), size.x, size.y, size.z, 0, nv::image_format_to_enum( aformat.format ), nv::datatype_to_gl_enum( aformat.type ), data );
+
+	bind( texture(), texture_slot::TEXTURE_0 );
+
+	info->type = type;
+	info->format = aformat;
+	info->tsampler = asampler;
+	info->size = size;
+	info->glid = glid;
+
+	return result;
+}
+
+nv::buffer nv::gl_context::create_buffer( buffer_type type, buffer_hint hint, size_t size, const void* source /*= nullptr */ )
+{
+	buffer result = static_cast<gl_device*>( m_device )->create_buffer( type, hint );
+	if ( size > 0 ) create_buffer( result, size, source );
+	return result;
+}
+
+void nv::gl_context::create_buffer( buffer b, size_t size, const void* source /*= nullptr */ )
+{
+	gl_buffer_info* info = static_cast<gl_device*>( m_device )->get_full_buffer_info( b );
+	if ( info )
+	{
+		unsigned glenum = buffer_type_to_enum( info->type );
+		glBindBuffer( glenum, info->glid );
+		glBufferData( glenum, GLsizeiptr( size ), source, buffer_hint_to_enum( info->hint ) );
+		glBindBuffer( glenum, 0 );
+	}
+}
+
 void nv::gl_context::set_draw_buffers( uint32 count, const output_slot* slots )
 {
