Index: trunk/src/gl/gl_context.cc
===================================================================
--- trunk/src/gl/gl_context.cc	(revision 472)
+++ trunk/src/gl/gl_context.cc	(revision 473)
@@ -205,4 +205,16 @@
 	{
 		glBindFramebuffer( framebuffer_slot_to_enum(ft), 0 );
+	}
+}
+
+void nv::gl_context::bind( buffer b, uint32 index, size_t offset /*= 0*/, size_t size /*= 0 */ )
+{
+	const gl_buffer_info* info = static_cast< const gl_buffer_info* >( m_device->get_buffer_info( b ) );
+	if ( info )
+	{
+		if ( size == 0 )
+			glBindBufferBase( buffer_type_to_enum( info->type ), index, info->glid );
+		else
+			glBindBufferRange( buffer_type_to_enum( info->type ), index, info->glid, offset, size );
 	}
 }
@@ -339,4 +351,18 @@
 		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 );
+	}
+}
+
+void nv::gl_context::update( buffer b, uint32 index, const void* data, size_t offset, size_t size )
+{
+	const gl_buffer_info* info = static_cast<const gl_buffer_info*>( m_device->get_buffer_info( b ) );
+	if ( info )
+	{
+		GLenum glenum = buffer_type_to_enum( info->type );
+		if ( size == 0 )
+			glBindBufferBase( glenum, index, info->glid );
+		else
+			glBindBufferRange( glenum, index, info->glid, offset, size );
+		glBufferSubData( glenum, GLintptr( offset ), GLsizeiptr( size ), data );
 	}
 }
@@ -750,5 +776,5 @@
 }
 
-void gl_context::draw( primitive prim, const render_state& rs, program p, vertex_array va, nv::size_t count )
+void gl_context::draw( primitive prim, const render_state& rs, program p, vertex_array va, nv::size_t count, nv::size_t first )
 {
 	apply_render_state( rs );
@@ -760,9 +786,9 @@
 		if ( info->index.is_valid() )
 		{
-			glDrawElements( primitive_to_enum(prim), static_cast<GLsizei>( count ), datatype_to_gl_enum( info->index_type ), 0 );
+			glDrawElements( primitive_to_enum(prim), static_cast<GLsizei>( count ), datatype_to_gl_enum( info->index_type ), reinterpret_cast< const void* >( get_datatype_info( info->index_type ).size * first ) );
 		}
 		else
 		{
-			glDrawArrays( primitive_to_enum(prim), 0, static_cast<GLsizei>( count ) );
+			glDrawArrays( primitive_to_enum(prim), first, static_cast<GLsizei>( count ) );
 		}
 		unbind( va );
Index: trunk/src/gl/gl_device.cc
===================================================================
--- trunk/src/gl/gl_device.cc	(revision 472)
+++ trunk/src/gl/gl_device.cc	(revision 473)
@@ -337,4 +337,9 @@
 		}
 	}
+	return -1;
+}
+
+int nv::gl_device::get_block_location( program p, const string_view& name, bool fatal /*= true */ ) const
+{
 	return -1;
 }
@@ -450,5 +455,7 @@
 {
 	int params;
+	int bparams;
 	glGetProgramiv( p->glid, GL_ACTIVE_UNIFORMS, &params );
+	glGetProgramiv( p->glid, GL_ACTIVE_UNIFORM_BLOCKS, &bparams );
 
 	for ( unsigned i = 0; i < unsigned( params ); ++i )
@@ -479,4 +486,13 @@
 		NV_ASSERT( u, "Unknown uniform type!" );
 		(*p->m_uniform_map)[ name ] = u;
+	}
+
+	for ( unsigned i = 0; i < unsigned( bparams ); ++i )
+	{
+		int uni_len;
+		char name_buffer[128];
+
+		glGetActiveUniformBlockName( p->glid, i, 128, &uni_len, name_buffer );
+		NV_LOG_INFO( string_view( name_buffer, size_t( uni_len ) ) );
 	}
 }
Index: trunk/src/gl/gl_enum.cc
===================================================================
--- trunk/src/gl/gl_enum.cc	(revision 472)
+++ trunk/src/gl/gl_enum.cc	(revision 473)
@@ -168,6 +168,7 @@
 	switch( type )
 	{
-	case VERTEX_BUFFER : return GL_ARRAY_BUFFER;
-	case INDEX_BUFFER  : return GL_ELEMENT_ARRAY_BUFFER;
+	case VERTEX_BUFFER  : return GL_ARRAY_BUFFER;
+	case INDEX_BUFFER   : return GL_ELEMENT_ARRAY_BUFFER;
+	case UNIFORM_BUFFER : return GL_UNIFORM_BUFFER;
 		NV_RETURN_COVERED_DEFAULT( 0 );
 	}
