Index: trunk/tests/cachebuf_test/cachebuf.frag
===================================================================
--- trunk/tests/cachebuf_test/cachebuf.frag	(revision 102)
+++ trunk/tests/cachebuf_test/cachebuf.frag	(revision 102)
@@ -0,0 +1,7 @@
+#version 120
+varying vec4 f_color;
+//uniform sampler2D tex;
+ 
+void main(void) {
+	gl_FragColor = f_color;
+}
Index: trunk/tests/cachebuf_test/cachebuf.vert
===================================================================
--- trunk/tests/cachebuf_test/cachebuf.vert	(revision 102)
+++ trunk/tests/cachebuf_test/cachebuf.vert	(revision 102)
@@ -0,0 +1,10 @@
+#version 120
+attribute vec2 coord;
+attribute vec4 color;
+uniform mat4 projection;
+varying vec4 f_color;
+
+void main(void) {
+	f_color = color;
+	gl_Position = projection * vec4( coord.x, coord.y, 0.0, 1.0 );
+}
Index: trunk/tests/cachebuf_test/cachebuf_test.lua
===================================================================
--- trunk/tests/cachebuf_test/cachebuf_test.lua	(revision 102)
+++ trunk/tests/cachebuf_test/cachebuf_test.lua	(revision 102)
@@ -0,0 +1,7 @@
+project "nv_cachebuf_test"
+	kind "ConsoleApp"
+	files { "nv_cachebuf_test.cc" }
+	includedirs { "../../" }
+	targetname "nv_gui_test"
+	links { "nv" }
+ 
Index: trunk/tests/cachebuf_test/nv_cachebuf_test.cc
===================================================================
--- trunk/tests/cachebuf_test/nv_cachebuf_test.cc	(revision 102)
+++ trunk/tests/cachebuf_test/nv_cachebuf_test.cc	(revision 102)
@@ -0,0 +1,259 @@
+#include <nv/interface/vertex_buffer.hh>
+#include <nv/gl/gl_device.hh>
+#include <nv/gfx/image.hh>
+#include <nv/interface/context.hh>
+#include <nv/interface/window.hh>
+#include <nv/interface/program.hh>
+#include <nv/interface/texture2d.hh>
+#include <nv/logging.hh>
+#include <nv/logger.hh>
+#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtc/type_ptr.hpp>
+#include <nv/string.hh>
+#include <nv/types.hh>
+#include <nv/interface/mesh.hh>
+#include <cstdlib> // rand
+#include <ctime> // time
+
+#include <nv/gfx/cached_buffer.hh>
+
+struct vertex
+{
+	nv::ivec2 coord;
+	nv::vec4  color;
+	vertex() {}
+	vertex( const nv::ivec2& coord, const nv::vec4& color )
+		: coord( coord ), color( color ) {}
+};
+
+struct quad
+{
+	vertex vtx[6];
+	quad( const nv::ivec2& coorda, const nv::ivec2& coordb, const nv::vec4& color )
+	{
+		vtx[0].color = color;
+		vtx[1].color = color;
+		vtx[2].color = color;
+		vtx[3].color = color;
+		vtx[4].color = color;
+		vtx[5].color = color;
+		vtx[0].coord = coorda;
+		vtx[1].coord = nv::ivec2( coorda.x, coordb.y );
+		vtx[2].coord = coordb;
+		vtx[3].coord = coordb;
+		vtx[4].coord = nv::ivec2( coordb.x, coorda.y );
+		vtx[5].coord = coorda;
+	}
+	quad( const nv::ivec2& coorda, const nv::ivec2& coordb, const nv::ivec2& coordc, const nv::ivec2& coordd, const nv::vec4& color )
+	{
+		vtx[0].color = color;
+		vtx[1].color = color;
+		vtx[2].color = color;
+		vtx[3].color = color;
+		vtx[4].color = color;
+		vtx[5].color = color;
+		vtx[0].coord = coorda;
+		vtx[1].coord = coordb;
+		vtx[2].coord = coordc;
+		vtx[3].coord = coordc;
+		vtx[4].coord = coordd;
+		vtx[5].coord = coorda;
+	}
+
+};
+
+struct app_window
+{
+	app_window( nv::cached_buffer<quad>* cache, const glm::ivec2& a, const glm::ivec2& b, const glm::vec4& color )
+		: m_slice( cache )
+	{
+		glm::vec4 dcolor( color.x * 0.5, color.y * 0.5, color.z * 0.5, 1.0 );
+		std::vector<quad>& v = m_slice.lock();
+		nv::ivec2 a2( a.x, b.y );
+		nv::ivec2 b2( b.x, a.y );
+		nv::ivec2 t1( 10, 10 );
+		nv::ivec2 t2( -10, 10 );
+		v.emplace_back( a - t1, a,       b2, b2 - t2, dcolor );
+		v.emplace_back( a - t1, a2 + t2, a2, a,       dcolor );
+		v.emplace_back( a2 + t2, b + t1, b, a2, dcolor );
+		v.emplace_back( b + t1, b2 - t2, b2, b, dcolor );
+		v.emplace_back( a, b, color );
+	}
+
+	void change_color( const glm::vec4& color )
+	{
+		glm::vec4 dcolor( color.x * 0.5, color.y * 0.5, color.z * 0.5, 1.0 );
+		std::vector<quad>& v = m_slice.lock();
+		vertex* vtx = (vertex*)v.data();
+		for (size_t i = 0; i < (v.size() - 1) * 6; ++i )
+			vtx[i].color = color;
+		for (size_t i = (v.size() - 1) * 6; i < (v.size()) * 6; ++i )
+			vtx[i].color = dcolor;
+	}
+
+	void draw()
+	{
+		m_slice.commit();
+	}
+
+	nv::buffer_slice<quad> m_slice;
+};
+
+class application
+{
+public:
+	application();
+	bool initialize();
+	bool run();
+	void kill_window();
+	void spawn_window();
+	void recolor_window();
+	~application();
+protected:
+	nv::device* m_device;
+	nv::window* m_window;
+	nv::clear_state m_clear_state;
+	nv::render_state m_render_state;
+	
+	nv::cached_buffer<quad>* m_quad_cache;
+	std::vector<app_window>  m_windows;
+
+	nv::program* m_program;
+	nv::vertex_array* m_va;
+	unsigned int m_count;
+	int m_coord_loc;
+	int m_color_loc;
+};
+
+application::application()
+{
+	m_device = new nv::gl_device();
+	m_window = m_device->create_window( 800, 600 );
+
+	m_clear_state.buffers = nv::clear_state::COLOR_AND_DEPTH_BUFFER;
+	m_render_state.depth_test.enabled = false;
+	m_render_state.culling.enabled    = false;
+	m_render_state.blending.enabled   = false;
+	m_render_state.blending.src_rgb_factor   = nv::blending::SRC_ALPHA;
+	m_render_state.blending.dst_rgb_factor   = nv::blending::ONE_MINUS_SRC_ALPHA;
+	m_render_state.blending.src_alpha_factor = nv::blending::SRC_ALPHA;
+	m_render_state.blending.dst_alpha_factor = nv::blending::ONE_MINUS_SRC_ALPHA;
+}
+
+bool application::initialize()
+{
+	{ 
+		m_program = m_device->create_program( nv::slurp( "cachebuf.vert" ), nv::slurp( "cachebuf.frag" ) );
+		m_va      = m_device->create_vertex_array();
+
+		m_quad_cache = new nv::cached_buffer<quad>( m_device, nv::DYNAMIC_DRAW, 20, true );
+		m_coord_loc  = m_program->get_attribute("coord")->get_location();
+		m_color_loc  = m_program->get_attribute("color")->get_location();
+
+		m_va->add_vertex_buffer( m_coord_loc, (nv::vertex_buffer*)m_quad_cache->get_buffer(), nv::INT,   2, 0, sizeof( vertex ), false );
+		m_va->add_vertex_buffer( m_color_loc, (nv::vertex_buffer*)m_quad_cache->get_buffer(), nv::FLOAT, 4, offset_of( &vertex::color ), sizeof( vertex ), false );
+	}
+	return true;
+}
+
+bool application::run()
+{
+	int keypress = 0;
+	m_program->bind();
+	glm::mat4 projection = glm::ortho( 0.0f, 800.0f, 600.0f, 0.0f, -1.0f, 1.0f );
+	m_program->set_uniform( "projection", glm::mat4(projection) );
+
+	while(!keypress) 
+	{
+		for ( auto& w : m_windows )
+		{
+			w.draw();
+		}
+		m_quad_cache->commit();
+		m_va->update_vertex_buffer( m_coord_loc, (nv::vertex_buffer*)m_quad_cache->get_buffer(), false );
+		m_va->update_vertex_buffer( m_color_loc, (nv::vertex_buffer*)m_quad_cache->get_buffer(), false );
+
+		m_window->get_context()->clear( m_clear_state );
+		m_program->bind();
+//		m_program->set_uniform( "tex", 0 );
+		m_window->get_context()->draw( nv::TRIANGLES, m_render_state, m_program, m_va, m_windows.size() * 5 * 6 );
+		m_window->swap_buffers();
+
+		nv::io_event event;
+		while(m_window->poll_event(event)) 
+		{      
+			switch (event.type) 
+			{
+			case nv::EV_QUIT:
+				keypress = 1;
+				break;
+			case nv::EV_KEY:
+				if (event.key.pressed)
+				{
+					switch (event.key.code) 
+					{
+					case nv::KEY_N      : spawn_window(); break;
+					case nv::KEY_C      : recolor_window(); break;
+					case nv::KEY_K      : kill_window(); break;
+					case nv::KEY_ESCAPE : keypress = 1; break;
+					}
+				}
+				break;
+			}
+		}
+	}
+	return true;
+}
+
+void application::spawn_window()
+{
+	glm::ivec2 a( std::rand() % 600, std::rand() % 400 );
+	glm::ivec2 b( std::rand() % 200, std::rand() % 200 );
+	NV_LOG( nv::LOG_INFO, "Spawn (" << a.x << "," << a.y << "x" << b.x << "," << b.y << ")" );
+	m_windows.emplace_back( m_quad_cache, a, a + b, glm::vec4( 0, 0, 1, 1 ) );
+}
+
+void application::kill_window()
+{
+	if ( m_windows.size() == 0 ) return;
+	size_t index = rand() % m_windows.size();
+	m_windows.erase( m_windows.begin() + index );
+}
+void application::recolor_window()
+{
+	if ( m_windows.size() == 0 ) return;
+	size_t index = rand() % m_windows.size();
+	m_windows[ index ].change_color( nv::vec4( (float)rand() / float(RAND_MAX), (float)rand() / float(RAND_MAX), (float)rand() / float(RAND_MAX), 1.0 ) );
+}
+
+application::~application()
+{
+	m_windows.clear();
+	delete m_quad_cache;
+
+	delete m_program;
+	delete m_va;
+	delete m_window;
+	delete m_device;
+}
+
+
+int main(int, char* [])
+{
+	std::srand((unsigned int) std::time(0) );
+	nv::logger log(nv::LOG_TRACE);
+	log.add_sink( new nv::log_file_sink("log.txt"), nv::LOG_TRACE );
+	log.add_sink( new nv::log_console_sink(), nv::LOG_TRACE );
+	
+	NV_LOG( nv::LOG_NOTICE, "Logging started" );
+	application app;
+	if ( app.initialize() )
+	{
+		app.run();
+	}
+	NV_LOG( nv::LOG_NOTICE, "Logging stopped" );
+
+	return 0;
+}
+
Index: trunk/tests/cachebuf_test/premake4.lua
===================================================================
--- trunk/tests/cachebuf_test/premake4.lua	(revision 102)
+++ trunk/tests/cachebuf_test/premake4.lua	(revision 102)
@@ -0,0 +1,27 @@
+solution "nv_cachebuf_test"
+	configurations { "debug", "release" }
+
+  	language "C++"
+	flags { "ExtraWarnings", "NoPCH" }
+
+	configuration "gmake"	
+		buildoptions "-std=c++11"
+
+	configuration "debug"
+		defines { "DEBUG" }
+		flags { "Symbols", "StaticRuntime" }
+		objdir (_ACTION.."/debug")
+
+	configuration "release"
+		defines { "NDEBUG" }
+		flags { "Optimize", "StaticRuntime" }
+		objdir (_ACTION.."/release")
+
+	dofile("cachebuf_test.lua")
+	dofile("../../nv.lua")
+
+if _ACTION == "clean" then
+	for action in premake.action.each() do
+		os.rmdir(action.trigger)
+	end
+end
