Explorar o código

worked an implementing an AZDO render pipeline

Zoadian %!s(int64=11) %!d(string=hai) anos
pai
achega
0fe41d466f
Modificáronse 6 ficheiros con 914 adicións e 224 borrados
  1. 2 15
      README.md
  2. 429 1
      source/app.d
  3. 51 1
      source/three/common.d
  4. 15 0
      source/three/mesh.d
  5. 134 207
      source/three/renderer.d
  6. 283 0
      source/three/renderer_.d

+ 2 - 15
README.md

@@ -5,25 +5,12 @@ Three.d is high performance 3D graphics engine written in the D Programming Lang
 
 # Techniques
 * Data-Oriented Design
-* Virtual Textures (Megatextures)
+* Approaching Zero Driver Overhead (AZDO)
 * Tiled Deferred Rendering
-* 
-
-# Passes
-
-## Geometry Pass
-
-## Lightning Pass
-
-### Point Light Pass
-
-### Spot Light Pass
+* Virtual Textures (Megatextures)
 
-### Directional Light Pass
 
-### Ambient Light Pass
 
-## Finalizing Pass
 
 License: 
 --------

+ 429 - 1
source/app.d

@@ -9,6 +9,394 @@ import std.experimental.logger;
 
 
 
+
+
+
+
+
+import three.window;
+import three.viewport;
+import three.camera;
+import three.renderTarget;
+import three.scene;
+import three.renderer;
+
+
+void main() {
+	Window window;	
+	Viewport viewport;
+	Scene scene;
+	Camera camera;
+	RenderTarget renderTarget;
+	//	OpenGlTiledDeferredRenderer renderer;
+	Renderer renderer;
+	bool keepRunning = true;
+
+	//------------------------------------------------
+	// Load and Construct Rendering Pipeline
+	//------------------------------------------------
+	DerelictGL3.load();
+	DerelictGLFW3.load();
+	DerelictFI.load();
+	//	DerelictFT.load();
+	DerelictASSIMP3.load();
+	DerelictAntTweakBar.load();
+	if(!glfwInit()) throw new Exception("Initialising GLFW failed"); scope(exit) glfwTerminate();
+
+	construct(window, "Three.d", 1600, 900); scope(exit) destruct(window);
+	
+	try {
+		GLVersion glVersion = DerelictGL3.reload();
+		import std.conv : to;
+		writeln("Reloaded OpenGL Version: ", to!string(glVersion)); 
+	} catch(Exception e) {
+		writeln("Reloading OpenGl failed: " ~ e.msg);
+	}
+
+	//	static FT_Library _s_freeTypeLibrary
+	//	if(!FT_Init_FreeType(&_s_freeTypeLibrary)) throw new Exception("Initialising FreeType failed"); scope(exit) FT_Done_FreeType(_s_freeTypeLibrary);
+	if(TwInit(TW_OPENGL_CORE, null) == 0) throw new Exception("Initialising AntTweakBar failed"); scope(exit) TwTerminate();
+
+
+	construct(viewport); scope(exit) destruct(viewport);
+	construct(scene); scope(exit) destruct(scene);
+	construct(camera); scope(exit) destruct(camera);
+	construct(renderTarget, window.width, window.height); scope(exit) destruct(renderTarget);
+	construct(renderer, window.width, window.height); scope(exit) destruct(renderer);
+
+	//------------------------------------------------
+	// Create Scene
+	//------------------------------------------------
+	scene.mesh.loadModel("C:/Coding/models/Collada/duck.dae");
+	
+	
+	//------------------------------------------------
+	// Generate TweakBar
+	//------------------------------------------------
+	TwWindowSize(window.width, window.height);
+	auto tweakBar = TwNewBar("TweakBar");
+	
+	
+	//------------------------------------------------
+	// Connect Window callbacks
+	//------------------------------------------------
+	window.onKey = (ref Window rWindow, Key key, ScanCode scanCode, KeyAction action, KeyMod keyMod) {
+		if(window is window && action == KeyAction.Pressed) {
+			if(key == Key.Escape) {
+				keepRunning = false;
+			}
+		}
+	};
+	
+	window.onClose = (ref Window rWindow) {
+		keepRunning = false;
+	};
+	
+	window.onSize = (ref Window rWindow, int width, int height) {
+		TwWindowSize(width, height);		
+	};
+	
+	window.onPosition = (ref Window rWindow, int x, int y) {
+	};
+	
+	window.onButton = (ref Window rWindow , int button, ButtonAction action) {
+		TwMouseAction twaction = action == ButtonAction.Pressed ? TW_MOUSE_PRESSED : TW_MOUSE_RELEASED;
+		TwMouseButtonID twbutton;
+		
+		switch(button) {
+			default:
+			case GLFW_MOUSE_BUTTON_LEFT: twbutton = TW_MOUSE_LEFT; break;
+			case GLFW_MOUSE_BUTTON_RIGHT: twbutton = TW_MOUSE_RIGHT; break;
+			case GLFW_MOUSE_BUTTON_MIDDLE: twbutton = TW_MOUSE_MIDDLE; break;			
+		}
+		
+		TwMouseButton(twaction, twbutton);		
+	};
+	
+	window.onCursorPos = (ref Window rWindow, double x, double y) {
+		TwMouseMotion(cast(int)x, window.height - cast(int)y);
+	};
+	
+
+
+	//------------------------------------------------
+	// Main Loop
+	//------------------------------------------------
+	ulong frameCount = 0;
+	glfwSetTime(0);
+	while(keepRunning) {
+		window.pollEvents();
+		
+		window.makeAktiveRenderWindow();
+		
+		glCheck!glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+		glCheck!glClearDepth(1.0f);
+		glCheck!glClearColor(0, 0.3, 0, 1);
+				
+		renderer.renderOneFrame(scene, camera, renderTarget, viewport);
+		
+		TwDraw();
+		
+		debug{ renderer.blitGBufferToScreen(); }
+		
+		window.swapBuffers();
+		
+		++frameCount;
+		if(frameCount % 1000 == 0) {
+			auto fps = cast(double)frameCount / glfwGetTime();
+			log("FPS: ", fps);
+			frameCount = 0;
+			glfwSetTime(0);
+		}
+	}
+}
+
+
+
+
+
+
+
+/+++++++
+
+//======================================================================================================================
+// 
+//======================================================================================================================
+struct Slice(T) {
+	T* data;
+	size_t length;
+}
+
+
+
+
+//======================================================================================================================
+// 
+//======================================================================================================================
+struct PersistentlyMappedBuffer(T) {
+	GLuint bo;
+	GLenum target;
+	Slice!T data;
+	alias Atom = T;
+}
+
+void create(T)(out PersistentlyMappedBuffer!T pmb, GLuint count, GLenum target, GLbitfield createFlags, GLbitfield mapFlags) {
+	pmb.target = target;
+	glCheck!glGenBuffers(1, &pmb.bo);
+	glCheck!glBindBuffer(target, pmb.bo);
+	glCheck!glBufferStorage(target, T.sizeof * count, null, createFlags);
+	pmb.data.data = cast(T*)glMapBufferRange(target, 0, T.sizeof * count, mapFlags);
+	pmb.data.length = count;
+	if (!pmb.data.data) {
+		throw new Exception("glMapBufferRange failed, probable bug.");
+	}
+}
+
+void destroy(T)(ref PersistentlyMappedBuffer!T pmb) {
+	glCheck!glBindBuffer(pmb.target, mName);
+	glCheck!glUnmapBuffer(pmb.target);
+	glCheck!glDeleteBuffers(1, &mName);
+
+	pmb = GlPersistentlyMappedBuffer!T.init;
+}
+
+
+//
+////======================================================================================================================
+//// 
+////======================================================================================================================
+//struct GlCircularBufferView(T) {
+//	GlPersistentlyMappedBuffer!T _buffer;
+//	GLsizeiptr _head;
+//}
+
+
+
+
+
+
+
+
+//======================================================================================================================
+// 
+//======================================================================================================================
+struct DrawArraysIndirectCommand {
+	GLuint vertexCount;
+	GLuint instanceCount;
+	GLuint firstVertex;
+	GLuint baseInstance;
+}
+
+struct DrawElementsIndirectCommand {
+	GLuint count;
+	GLuint instanceCount;
+	GLuint firstIndex;
+	GLuint baseVertex;
+	GLuint baseInstance;
+}
+
+struct PerInstanceRenderData {
+}
+
+struct MeshDataRef {
+}
+
+struct PerInstanceDataRef {
+}
+
+struct Mesh {
+	size_t meshDataRef;
+	InstanceData[] instanceData;
+}
+
+struct MeshInstance {
+	size_t meshRef;
+	size_t meshInstanceRef;
+}
+
+struct Renderer {
+	MeshData[] meshData;
+	Mesh[] meshes;
+	MeshInstance[] meshInstances;
+
+
+
+
+
+
+	PersistentlyMappedBuffer!DrawElementsIndirectCommand commandBuffer;
+	PersistentlyMappedBuffer!TransformationMatrix perInstanceBuffer;
+
+	void renderOneFrame(Instances instances) {
+
+
+		auto instanceCount = 0;
+		// write draw commands
+		DrawElementsIndirectCommand* cmds = commandBuffer.reserve(instanceCount);
+		foreach(size_t i = 0; i < instanceCount; ++i) {
+			DrawElementsIndirectCommand* cmd = &cmds[u];
+			cmd->count = mIndexCount;
+			cmd->instanceCount = 1;
+			cmd->firstIndex = 0;
+			cmd->baseVertex = 0;
+			cmd->baseInstance = mUseShaderDrawParameters ? 0 : u;
+		}
+
+		// write per instance 
+		perInstanceBuffer.reserve(instances.length);
+		foreach() {
+		}
+
+		glCheck!glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, mCommands.GetHeadOffset(), xformCount, 0);
+	}
+}
+
+
+
+
+
+
+
+
+/++++
+
+
+
+
+
+
+
+
+
+
+struct GlPeristentBuffer {
+}
+
+
+
+
+
+
+
+struct GlRingBuffer {
+	GLuint bo;
+}
+
+struct ParamPerInstanceRingBuffer {
+	GLuint bo;
+}
+
+
+struct Renderer {
+	CommandRingBuffer commandRb;
+	ParamPerInstanceRingBuffer paramPerInstanceRb;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+struct MeshStorageManager(MeshLayout = DefaultVertexLayout) {
+	struct Slice {
+		size_t offset;
+		size_t length;
+	}
+
+	struct MeshStorage {
+		Slice vertexSlice;
+		Slice indexSlice;
+	}
+
+	GLuint vao;
+	GLuint vbo;
+	GLuint ibo;
+	MeshStorage[] meshes;
+}
+
+
+struct MeshInstance {
+}
+
+
+
+
+
+
+
+
+struct MappedPersistentBuffer {
+	GLuint vbo;
+	void* data;
+}
+
+void create(out MappedPersistentBuffer mpb, GLsizeiptr bufferSize) {
+	glCheck!glGenVertexArrays(1, &mpb.vbo);
+
+	GLbitfield mapFlags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
+	GLbitfield createFlags = mapFlags | GL_MAP_DYNAMIC_STORAGE_BIT;
+
+	glCheck!glBindBuffer(GL_ARRAY_BUFFER, mpb.vbo);
+	glCheck!glBufferStorage(GL_ARRAY_BUFFER, bufferSize, null, createFlags);
+	mpb.data = glCheck!glMapBufferRange(GL_ARRAY_BUFFER, 0, bufferSize, mapFlags);
+}
+
+
+
+
+++++/
+
+
+
+
+
+
 //======================================================================================================================
 // 
 //======================================================================================================================
@@ -73,6 +461,27 @@ void main() {
 	renderer.construct(window.width, window.height); scope(exit) renderer.destruct();
 
 
+
+	//############################
+	//############################
+	//############################
+	//############################
+
+
+	GlPersistentlyMappedBuffer!DefaultVertexData vertexBuffer;
+
+	vertexBuffer.create(1024, GL_ARRAY_BUFFER, 0, 0);
+
+
+	//############################
+	//############################
+	//############################
+	//############################
+
+
+
+
+
 	//------------------------------------------------
 	// Create Scene
 	//------------------------------------------------
@@ -141,6 +550,20 @@ void main() {
 		glCheck!glClearDepth(1.0f);
 		glCheck!glClearColor(0, 0.3, 0, 1);
 
+		/*
+		foreach(renderTarget) //framebuffer
+		foreach(pass)
+		foreach(material) //shaders
+		foreach(materialInstance) //textures
+		foreach(vertexFormat) //vertex buffers
+		foreach(object) {
+							write uniform data;
+							glDrawElementsBaseVertex
+						}
+		*/		
+
+
+
 		renderer.renderOneFrame(scene, camera, renderTarget, viewport);
 
 		TwDraw();
@@ -543,4 +966,9 @@ void main() {
 }
 
 
-+++/
++++/
+
+
+
+
++++++++/

+ 51 - 1
source/three/common.d

@@ -61,4 +61,54 @@ string glErrorString(GLenum error) pure @safe nothrow @nogc {
 		case GL_OUT_OF_MEMORY: return "out of memory";
 	}
 	assert(false, "invalid enum");
-}
+}
+
+
+//==============================================================================
+///
+template toGlType(T) {
+	static if(is(T == byte)) {
+		enum toGlType = GL_BYTE;
+	} else static if(is(T == ubyte)) {
+		enum toGlType = GL_UNSIGNED_BYTE;
+	} else static if(is(T == short)) {
+		enum toGlType = GL_SHORT;
+	} else static if(is(T == ushort)) {
+		enum toGlType = GL_UNSIGNED_SHORT;
+	} else static if(is(T == int)) {
+		enum toGlType = GL_INT;
+	} else static if(is(T == uint)) {
+		enum toGlType = GL_UNSIGNED_INT;
+	} else static if(is(T == float)) {
+		enum toGlType = GL_FLOAT;
+	} else static if(is(T == double)) {
+		enum toGlType = GL_DOUBLE;
+	} else {
+		static assert(false, T.stringof ~ " cannot be represented as GLenum");
+	}
+}
+
+
+//==============================================================================
+///
+template sizeofGlType(GLenum t) {
+	static if(t == GL_BYTE) {
+		enum sizeofGlType = byte.sizeof;
+	} else static if(t == GL_UNSIGNED_BYTE) {
+		enum sizeofGlType = ubyte.sizeof;
+	} else static if(t == GL_SHORT) {
+		enum sizeofGlType = short.sizeof;
+	} else static if(t == GL_UNSIGNED_SHORT) {
+		enum sizeofGlType = ushort.sizeof;
+	} else static if(t == GL_INT) {
+		enum sizeofGlType = int.sizeof;
+	} else static if(t == GL_UNSIGNED_INT) {
+		enum sizeofGlType = uint.sizeof;
+	} else static if(t == GL_FLOAT) {
+		enum sizeofGlType = float.sizeof;
+	} else static if(t == GL_DOUBLE) {
+		enum sizeofGlType = double.sizeof;
+	} else {
+		static assert(false, T.stringof ~ " cannot be represented as D-Type");
+	}
+}

+ 15 - 0
source/three/mesh.d

@@ -2,6 +2,21 @@
 
 import three.common;
 
+struct DrawArraysIndirectCommand {
+	GLuint vertexCount;
+	GLuint instanceCount;
+	GLuint firstVertex;
+	GLuint baseInstance;
+}
+
+struct DrawElementsIndirectCommand {
+	GLuint count;
+	GLuint instanceCount;
+	GLuint firstIndex;
+	GLuint baseVertex;
+	GLuint baseInstance;
+}
+
 struct SOAMesh {
 	SoA!GLuint vao;
 	SoA!GLuint vboVertices;

+ 134 - 207
source/three/renderer.d

@@ -9,249 +9,176 @@ import three.viewport;
 import std.string : toStringz;
 import std.exception : collectException;
 
-enum deferredRendererVertexShaderSource = "
-	#version 420 core
-
-	layout(location = 0) in vec3 in_position;
-	layout(location = 1) in vec3 in_normal;
-	layout(location = 2) in vec2 in_texcoord;
-	layout(location = 3) in vec4 in_color;
-
-	//==============
-	out vec2 _normal;
-	out vec2 _texture;
-	out vec3 _color;
-	//==============
-
-	out gl_PerVertex 
-	{
-    	vec4 gl_Position;
- 	};
-
-	vec2 encode(vec3 n)
-	{
-	    float f = sqrt(8*n.z+8);
-	    return n.xy / f + 0.5;
-	}
-	
-	void main() 
-	{        				
-		gl_Position = vec4(0.005 * in_position.x, 0.005 * in_position.y, 0.005* in_position.z, 1.0);
-		_normal = encode(in_normal);
-		_texture = in_texcoord;
-		_color = in_color.xyz;
-	};
-";
-
-enum deferredRendererFragmentShaderSource = "
-	#version 420 core
-
-	//==============
-	in vec2 _normal;
-	in vec2 _texture;
-	in vec3 _color;
-	//==============
-
-	layout(location = 0) out float depth;
-	layout(location = 1) out vec4 normal;
-	layout(location = 2) out vec4 color;
-
-	void main()
-	{
-		depth = gl_FragCoord.z;
-		normal.xy = _normal.xy;
-		color.xyz = _color;
+import std.experimental.logger;
+
+
+enum GlBufferTarget {
+	Array = GL_ARRAY_BUFFER, 
+	AtomicCounter = GL_ATOMIC_COUNTER_BUFFER, 
+	CopyRead= GL_COPY_READ_BUFFER, 
+	CopyWrite = GL_COPY_WRITE_BUFFER, 
+	DrawIndirect = GL_DRAW_INDIRECT_BUFFER, 
+	DispatchIndirect = GL_DISPATCH_INDIRECT_BUFFER,
+	ElementArray = GL_ELEMENT_ARRAY_BUFFER, 
+	PixelPack = GL_PIXEL_PACK_BUFFER, 
+	PixelUnpack = GL_PIXEL_UNPACK_BUFFER, 
+	QueryBuffer = GL_QUERY_BUFFER,
+	ShaderStorage = GL_SHADER_STORAGE_BUFFER,
+	Texture = GL_TEXTURE_BUFFER, 
+	TransformFeedback = GL_TRANSFORM_FEEDBACK_BUFFER,
+	Uniform = GL_UNIFORM_BUFFER
+}
+
+struct GlBuffer(GlBufferTarget Target, T) {
+	alias BufferTarget = Target;
+	alias ValueType = T;
+	GLuint handle;
+	GLuint length;
+	T* data;
+}
+
+void construct(GlBufferTarget Target, T)(out GlBuffer!(Target, T) buffer, GLuint length, GLbitfield createFlags, GLbitfield mapFlags) {
+	glCheck!glGenBuffers(1, &buffer.handle);
+	glCheck!glBindBuffer(Target, buffer.handle);
+	glCheck!glBufferStorage(Target, length * T.sizeof, null, createFlags);
+	buffer.data = cast(T*)glCheck!glMapBufferRange(Target, 0, length * T.sizeof, mapFlags);
+	buffer.length = length;
+	if (buffer.data is null) {
+		throw new Exception("glMapBufferRange failed, probable bug.");
 	}
-";
+}
 
-struct GBuffer {
-	uint width;
-	uint height;
-	GLuint texturePosition;
-	GLuint textureNormal;
-	GLuint textureColor;
-	GLuint textureDepthStencil;
-	GLuint fbo;
+void destruct(GlBufferTarget Target, T)(ref GlBuffer!(Target, T) buffer) {
+	glCheck!glBindBuffer(Target, buffer.handle);
+	glCheck!glUnmapBuffer(Target);
+	glCheck!glDeleteBuffers(1, &buffer.handle);
+	buffer = buffer.init;
 }
 
-void construct(out GBuffer gBuffer, uint width, uint height) nothrow {
-	gBuffer.width = width;
-	gBuffer.height = height;
-	
-	glCheck!glGenTextures(1, &gBuffer.texturePosition);		
-	glCheck!glBindTexture(GL_TEXTURE_2D, gBuffer.texturePosition);
-	glCheck!glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-	glCheck!glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-	//	glCheck!glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
-	//	glCheck!glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
-	//	glCheck!glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
-	glCheck!glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height);
-	glCheck!glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_FLOAT, null);	
-	glCheck!glBindTexture(GL_TEXTURE_2D, 0);
-	
-	glCheck!glGenTextures(1, &gBuffer.textureNormal);
-	glCheck!glBindTexture(GL_TEXTURE_2D, gBuffer.textureNormal);
-	glCheck!glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-	glCheck!glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);	
-	//	glCheck!glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
-	//	glCheck!glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
-	glCheck!glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB10_A2, width, height);
-	glCheck!glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_FLOAT, null);	
-	glCheck!glBindTexture(GL_TEXTURE_2D, 0);
-	
-	glCheck!glGenTextures(1, &gBuffer.textureColor);
-	glCheck!glBindTexture(GL_TEXTURE_2D, gBuffer.textureColor);
-	glCheck!glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
-	glCheck!glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_FLOAT, null);	
-	glCheck!glBindTexture(GL_TEXTURE_2D, 0);
-	
-	glCheck!glGenTextures(1, &gBuffer.textureDepthStencil);
-	glCheck!glBindTexture(GL_TEXTURE_2D, gBuffer.textureDepthStencil);
-	glCheck!glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, width, height);
-	glCheck!glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, null);	
-	glCheck!glBindTexture(GL_TEXTURE_2D, 0);
-	
-	glCheck!glGenFramebuffers(1, &gBuffer.fbo);
-	glCheck!glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gBuffer.fbo);
-	
-	glCheck!glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 0, GL_TEXTURE_2D, gBuffer.texturePosition, 0);
-	glCheck!glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 1, GL_TEXTURE_2D, gBuffer.textureNormal, 0);
-	glCheck!glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 2, GL_TEXTURE_2D, gBuffer.textureColor, 0);
-	glCheck!glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, gBuffer.textureDepthStencil, 0);
-	
-	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+void bind(GlBufferTarget Target, T)(ref GlBuffer!(Target, T) buffer) {
+	glCheck!glBindBuffer(Target, buffer.handle);
 }
 
-void destruct(ref GBuffer gBuffer) nothrow {
-	glCheck!glDeleteFramebuffers(1, &gBuffer.fbo);
-	glCheck!glDeleteTextures(1, &gBuffer.textureDepthStencil);
-	glCheck!glDeleteTextures(1, &gBuffer.textureColor);
-	glCheck!glDeleteTextures(1, &gBuffer.textureNormal);
-	glCheck!glDeleteTextures(1, &gBuffer.texturePosition);
-	gBuffer = GBuffer.init;
+void unbind(GlBufferTarget Target, T)(ref GlBuffer!(Target, T) buffer) {
+	glCheck!glBindBuffer(Target, 0);
 }
 
-struct Pipeline {
-	GLuint pipeline;
-	GLuint vertexShaderGeometryPass;
-	GLuint fragmentShaderGeometryPass;
+alias GlArrayBuffer(T) = GlBuffer!(GlBufferTarget.Array, T);
+alias GlElementArrayBuffer(T) = GlBuffer!(GlBufferTarget.ElementArray, T);
+alias GlShaderStorageBuffer(T) = GlBuffer!(GlBufferTarget.ShaderStorage, T);
+alias GlDispatchIndirectBuffer(T) = GlBuffer!(GlBufferTarget.DispatchIndirect, T);
+alias GlTextureBuffer(T) = GlBuffer!(GlBufferTarget.Texture, T);
+alias GlUniformBuffer(T) = GlBuffer!(GlBufferTarget.Uniform, T);
+
+struct DrawElementsIndirectCommand {
+	GLuint count;
+	GLuint instanceCount;
+	GLuint firstIndex;
+	GLuint baseVertex;
+	GLuint baseInstance;
 }
 
-void construct(out Pipeline pipeline) nothrow {
-	glCheck!glGenProgramPipelines(1, &pipeline.pipeline); 
-
-	auto szVertexSource = [deferredRendererVertexShaderSource.toStringz()];
-	pipeline.vertexShaderGeometryPass = glCheck!glCreateShaderProgramv(GL_VERTEX_SHADER, 1, szVertexSource.ptr);
-	int len;
-	glCheck!glGetProgramiv(pipeline.vertexShaderGeometryPass, GL_INFO_LOG_LENGTH , &len);
-	if (len > 1) {
-		char[] msg = new char[len];
-		glCheck!glGetProgramInfoLog(pipeline.vertexShaderGeometryPass, len, null, cast(char*) msg);
-		log(cast(string)msg).collectException;
-	}
 
-	auto szFragmentSource = [deferredRendererFragmentShaderSource.toStringz()];
-	pipeline.fragmentShaderGeometryPass = glCheck!glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, szFragmentSource.ptr);
-//	int len;
-	glCheck!glGetProgramiv(pipeline.fragmentShaderGeometryPass, GL_INFO_LOG_LENGTH , &len);
-	if (len > 1) {
-		char[] msg = new char[len];
-		glCheck!glGetProgramInfoLog(pipeline.fragmentShaderGeometryPass, len, null, cast(char*) msg);
-		log(cast(string)msg).collectException;
-	}
 
-	glCheck!glUseProgramStages(pipeline.pipeline, GL_VERTEX_SHADER_BIT, pipeline.vertexShaderGeometryPass);
-	glCheck!glUseProgramStages(pipeline.pipeline, GL_FRAGMENT_SHADER_BIT, pipeline.fragmentShaderGeometryPass);
+struct Position {
+	float x, y, z;
+}
 
-	glCheck!glValidateProgramPipeline(pipeline.pipeline);
-	GLint status;
-	glCheck!glGetProgramPipelineiv(pipeline.pipeline, GL_VALIDATE_STATUS, &status);
-	//TODO: add error handling
-	assert(status != 0);
+struct Normal {
+	float x, y, z;
 }
 
-void destruct(ref Pipeline pipeline) nothrow {
-	glDeleteProgramPipelines(1, &pipeline.pipeline);
-	pipeline = Pipeline.init;
+struct Color {
+	float r, g, b, a;
 }
 
+struct Matrix4 {
+	float[4*4] data;
+}
 
-struct OpenGlTiledDeferredRenderer {
-	GBuffer gBuffer;
-	Pipeline pipeline;
+struct VertexData {
+	Position position;
+	Normal normal;
+	Color color;
 }
 
-void construct(out OpenGlTiledDeferredRenderer deferredRenderer, uint width, uint height) nothrow {
-	deferredRenderer.gBuffer.construct(width, height);
-	deferredRenderer.pipeline.construct();
+alias IndexData = uint;
+
+struct DrawParameter {
+	Matrix4 transformationMatrix;
 }
 
-void destruct(ref OpenGlTiledDeferredRenderer deferredRenderer) nothrow {
-	deferredRenderer.pipeline.destruct();
-	deferredRenderer.gBuffer.destruct();
-	deferredRenderer = OpenGlTiledDeferredRenderer.init;
+
+
+
+struct Renderer {
+	uint width;
+	uint height;
+	GlArrayBuffer!VertexData vertexBuffer; // vertex data for all meshes
+	GlElementArrayBuffer!IndexData indexBuffer; //index data for all meshes
+	GlShaderStorageBuffer!DrawParameter perInstanceParamBuffer; // is filled with draw parameters for each instance each frame. shall be accessed as a ringbuffer
+	GlDispatchIndirectBuffer!DrawElementsIndirectCommand dispatchIndirectCommandBuffer; // is filled with DrawElementsIndirectCommand for each mesh each frame. shall be accessed as a ringbuffer
 }
 
-// draw the scene supersampled with renderer's with+height onto renderTarget at position+size of viewport
-void renderOneFrame(ref OpenGlTiledDeferredRenderer renderer, ref Scene scene, ref Camera camera, ref RenderTarget renderTarget, ref Viewport viewport) {
-	// 1. Render the (opaque) geometry into the G-Buffers.	
-	// 2. Construct a screen space grid, covering the frame buffer, with some fixed tile
-	//    size, t = (x, y), e.g. 32 × 32 pixels.	
-	// 3. For each light: find the screen space extents of the light volume and append the
-	//    light ID to each affected grid cell.	
-	// 4. For each fragment in the frame buffer, with location f = (x, y).
-	//    (a) sample the G-Buffers at f.
-	//    (b) accumulate light contributions from all lights in tile at ⌊f /t⌋
-	//    (c) output total light contributions to frame buffer at f
-
-	with(renderer.gBuffer) glCheck!glViewport(0, 0, width, height);
+void construct(out Renderer renderer, uint width, uint height) {
+	GLbitfield createFlags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;//TODO: ?? | GL_MAP_DYNAMIC_STORAGE_BIT;
+	GLbitfield mapFlags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
+	
+	renderer.vertexBuffer.construct(1024, createFlags, mapFlags);
+	renderer.indexBuffer.construct(1024, createFlags, mapFlags);
+	renderer.perInstanceParamBuffer.construct(1024, createFlags, mapFlags);
+	renderer.dispatchIndirectCommandBuffer.construct(1024, createFlags, mapFlags);
+}
+
+void destruct(ref Renderer renderer) {
+	renderer.vertexBuffer.destruct();
+	renderer.indexBuffer.destruct();
+	renderer.perInstanceParamBuffer.destruct();
+	renderer.dispatchIndirectCommandBuffer.destruct();
+	renderer = renderer.init;
+}
+
+void renderOneFrame(ref Renderer renderer, ref Scene scene, ref Camera camera, ref RenderTarget renderTarget, ref Viewport viewport) {
+	
+	/*
+		foreach(renderTarget) //framebuffer
+		foreach(pass)
+		foreach(material) //shaders
+		foreach(materialInstance) //textures
+		foreach(vertexFormat) //vertex buffers
+		foreach(object) {
+							write uniform data;
+							glDrawElementsBaseVertex
+						}
+		*/	
+
+	GLsizei meshCount = 0;
+
+	glCheck!glViewport(0, 0, renderer.width, renderer.height);
 	
 	//enable depth mask _before_ glClear ing the depth buffer!
 	glCheck!glDepthMask(GL_TRUE); scope(exit) glCheck!glDepthMask(GL_FALSE);
 	glCheck!glEnable(GL_DEPTH_TEST); scope(exit) glCheck!glDisable(GL_DEPTH_TEST);
 	glCheck!glDepthFunc(GL_LEQUAL);
+
+	// write draw parameters
 	
-	//bind gBuffer
-	glCheck!glBindFramebuffer(GL_DRAW_FRAMEBUFFER, renderer.gBuffer.fbo); scope(exit) glCheck!glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 
-	GLenum[] drawBuffers = [GL_COLOR_ATTACHMENT0 + 0, GL_COLOR_ATTACHMENT0 + 1, GL_COLOR_ATTACHMENT0 + 2];
-	glCheck!glDrawBuffers(drawBuffers.length, drawBuffers.ptr); scope(exit) glCheck!glDrawBuffer(GL_NONE);
-	glCheck!glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
-
-//	glCheck!glClearColor(0, 0, 0.3, 1);
-	//bind pipeline
-	glCheck!glBindProgramPipeline(renderer.pipeline.pipeline); scope(exit) glCheck!glBindProgramPipeline(0);
+	// write draw commmands
 	
-	{// Draw Geometry
-		scope(exit) glCheck!glBindVertexArray(0);
-		scope(exit) glCheck!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // TODO: GL_ELEMENT_ARRAY_BUFFER should be vao state, but bugs might make this necessary
-		
-		for(size_t meshIdx = 0; meshIdx < scene.mesh.cnt; ++meshIdx) {
-			glCheck!glBindVertexArray(scene.mesh.vao[meshIdx]); 
-			
-			glCheck!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, scene.mesh.vboIndices[meshIdx]); // TODO: GL_ELEMENT_ARRAY_BUFFER should be vao state, but bugs might make this necessary
-
-			glCheck!glDrawElements(GL_TRIANGLES, scene.mesh.cntIndices[meshIdx], GL_UNSIGNED_INT, null);
-		}
-	}
-}
+	//draw //TODO: pass offset (cast to ptr) into command buffer instead of null
 
 
-debug {
-	void blitGBufferToScreen(ref OpenGlTiledDeferredRenderer renderer) {
-		glCheck!glBindFramebuffer(GL_READ_FRAMEBUFFER, renderer.gBuffer.fbo); scope(exit) glCheck!glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); 
-
-		GLsizei width = renderer.gBuffer.width;
-		GLsizei height = renderer.gBuffer.height;
+	renderer.vertexBuffer.bind();
+	renderer.indexBuffer.bind();
+	renderer.perInstanceParamBuffer.bind();
+	renderer.dispatchIndirectCommandBuffer.bind();
 
-		scope(exit) glCheck!glReadBuffer(GL_NONE);
+	glCheck!glMultiDrawElementsIndirect(GL_TRIANGLES, toGlType!(renderer.indexBuffer.ValueType), null, meshCount, 0);
 
-		glCheck!glReadBuffer(GL_COLOR_ATTACHMENT0 + 0);
-		glCheck!glBlitFramebuffer(0, 0, width, height, 0, height-300, 400, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
 
-		glCheck!glReadBuffer(GL_COLOR_ATTACHMENT0 + 1);
-		glCheck!glBlitFramebuffer(0, 0, width, height, 0, 0, 400, 300, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+}
 
-		glCheck!glReadBuffer(GL_COLOR_ATTACHMENT0 + 2);
-		glCheck!glBlitFramebuffer(0, 0, width, height, width-400, height-300, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+debug {
+	void blitGBufferToScreen(ref Renderer renderer) {
 	}
 }

+ 283 - 0
source/three/renderer_.d

@@ -0,0 +1,283 @@
+module three.rendererx;
+
+import three.common;
+import three.scene;
+import three.camera;
+import three.renderTarget;
+import three.viewport;
+
+import std.string : toStringz;
+import std.exception : collectException;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+enum deferredRendererVertexShaderSource = "
+	#version 420 core
+
+	layout(location = 0) in vec3 in_position;
+	layout(location = 1) in vec3 in_normal;
+	layout(location = 2) in vec2 in_texcoord;
+	layout(location = 3) in vec4 in_color;
+
+	//==============
+	out vec2 _normal;
+	out vec2 _texture;
+	out vec3 _color;
+	//==============
+
+	out gl_PerVertex 
+	{
+    	vec4 gl_Position;
+ 	};
+
+	vec2 encode(vec3 n)
+	{
+	    float f = sqrt(8*n.z+8);
+	    return n.xy / f + 0.5;
+	}
+	
+	void main() 
+	{        				
+		gl_Position = vec4(0.005 * in_position.x, 0.005 * in_position.y, 0.005* in_position.z, 1.0);
+		_normal = encode(in_normal);
+		_texture = in_texcoord;
+		_color = in_color.xyz;
+	};
+";
+
+enum deferredRendererFragmentShaderSource = "
+	#version 420 core
+
+	//==============
+	in vec2 _normal;
+	in vec2 _texture;
+	in vec3 _color;
+	//==============
+
+	layout(location = 0) out float depth;
+	layout(location = 1) out vec4 normal;
+	layout(location = 2) out vec4 color;
+
+	void main()
+	{
+		depth = gl_FragCoord.z;
+		normal.xy = _normal.xy;
+		color.xyz = _color;
+	}
+";
+
+struct GBuffer {
+	uint width;
+	uint height;
+	GLuint texturePosition;
+	GLuint textureNormal;
+	GLuint textureColor;
+	GLuint textureDepthStencil;
+	GLuint fbo;
+}
+
+void construct(out GBuffer gBuffer, uint width, uint height) nothrow {
+	gBuffer.width = width;
+	gBuffer.height = height;
+	
+	glCheck!glGenTextures(1, &gBuffer.texturePosition);		
+	glCheck!glBindTexture(GL_TEXTURE_2D, gBuffer.texturePosition);
+	glCheck!glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+	glCheck!glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+	//	glCheck!glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+	//	glCheck!glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+	//	glCheck!glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
+	glCheck!glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height);
+	glCheck!glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_FLOAT, null);	
+	glCheck!glBindTexture(GL_TEXTURE_2D, 0);
+	
+	glCheck!glGenTextures(1, &gBuffer.textureNormal);
+	glCheck!glBindTexture(GL_TEXTURE_2D, gBuffer.textureNormal);
+	glCheck!glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	glCheck!glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);	
+	//	glCheck!glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+	//	glCheck!glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+	glCheck!glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB10_A2, width, height);
+	glCheck!glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_FLOAT, null);	
+	glCheck!glBindTexture(GL_TEXTURE_2D, 0);
+	
+	glCheck!glGenTextures(1, &gBuffer.textureColor);
+	glCheck!glBindTexture(GL_TEXTURE_2D, gBuffer.textureColor);
+	glCheck!glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
+	glCheck!glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_FLOAT, null);	
+	glCheck!glBindTexture(GL_TEXTURE_2D, 0);
+	
+	glCheck!glGenTextures(1, &gBuffer.textureDepthStencil);
+	glCheck!glBindTexture(GL_TEXTURE_2D, gBuffer.textureDepthStencil);
+	glCheck!glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, width, height);
+	glCheck!glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, null);	
+	glCheck!glBindTexture(GL_TEXTURE_2D, 0);
+	
+	glCheck!glGenFramebuffers(1, &gBuffer.fbo);
+	glCheck!glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gBuffer.fbo);
+	
+	glCheck!glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 0, GL_TEXTURE_2D, gBuffer.texturePosition, 0);
+	glCheck!glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 1, GL_TEXTURE_2D, gBuffer.textureNormal, 0);
+	glCheck!glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 2, GL_TEXTURE_2D, gBuffer.textureColor, 0);
+	glCheck!glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, gBuffer.textureDepthStencil, 0);
+	
+	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+}
+
+void destruct(ref GBuffer gBuffer) nothrow {
+	glCheck!glDeleteFramebuffers(1, &gBuffer.fbo);
+	glCheck!glDeleteTextures(1, &gBuffer.textureDepthStencil);
+	glCheck!glDeleteTextures(1, &gBuffer.textureColor);
+	glCheck!glDeleteTextures(1, &gBuffer.textureNormal);
+	glCheck!glDeleteTextures(1, &gBuffer.texturePosition);
+	gBuffer = GBuffer.init;
+}
+
+struct Pipeline {
+	GLuint pipeline;
+	GLuint vertexShaderGeometryPass;
+	GLuint fragmentShaderGeometryPass;
+}
+
+void construct(out Pipeline pipeline) nothrow {
+	glCheck!glGenProgramPipelines(1, &pipeline.pipeline); 
+
+	auto szVertexSource = [deferredRendererVertexShaderSource.toStringz()];
+	pipeline.vertexShaderGeometryPass = glCheck!glCreateShaderProgramv(GL_VERTEX_SHADER, 1, szVertexSource.ptr);
+	int len;
+	glCheck!glGetProgramiv(pipeline.vertexShaderGeometryPass, GL_INFO_LOG_LENGTH , &len);
+	if (len > 1) {
+		char[] msg = new char[len];
+		glCheck!glGetProgramInfoLog(pipeline.vertexShaderGeometryPass, len, null, cast(char*) msg);
+		log(cast(string)msg).collectException;
+	}
+
+	auto szFragmentSource = [deferredRendererFragmentShaderSource.toStringz()];
+	pipeline.fragmentShaderGeometryPass = glCheck!glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, szFragmentSource.ptr);
+//	int len;
+	glCheck!glGetProgramiv(pipeline.fragmentShaderGeometryPass, GL_INFO_LOG_LENGTH , &len);
+	if (len > 1) {
+		char[] msg = new char[len];
+		glCheck!glGetProgramInfoLog(pipeline.fragmentShaderGeometryPass, len, null, cast(char*) msg);
+		log(cast(string)msg).collectException;
+	}
+
+	glCheck!glUseProgramStages(pipeline.pipeline, GL_VERTEX_SHADER_BIT, pipeline.vertexShaderGeometryPass);
+	glCheck!glUseProgramStages(pipeline.pipeline, GL_FRAGMENT_SHADER_BIT, pipeline.fragmentShaderGeometryPass);
+
+	glCheck!glValidateProgramPipeline(pipeline.pipeline);
+	GLint status;
+	glCheck!glGetProgramPipelineiv(pipeline.pipeline, GL_VALIDATE_STATUS, &status);
+	//TODO: add error handling
+	assert(status != 0);
+}
+
+void destruct(ref Pipeline pipeline) nothrow {
+	glDeleteProgramPipelines(1, &pipeline.pipeline);
+	pipeline = Pipeline.init;
+}
+
+
+struct OpenGlTiledDeferredRenderer {
+	GBuffer gBuffer;
+	Pipeline pipeline;
+}
+
+void construct(out OpenGlTiledDeferredRenderer deferredRenderer, uint width, uint height) nothrow {
+	deferredRenderer.gBuffer.construct(width, height);
+	deferredRenderer.pipeline.construct();
+}
+
+void destruct(ref OpenGlTiledDeferredRenderer deferredRenderer) nothrow {
+	deferredRenderer.pipeline.destruct();
+	deferredRenderer.gBuffer.destruct();
+	deferredRenderer = OpenGlTiledDeferredRenderer.init;
+}
+
+// draw the scene supersampled with renderer's with+height onto renderTarget at position+size of viewport
+void renderOneFrame(ref OpenGlTiledDeferredRenderer renderer, ref Scene scene, ref Camera camera, ref RenderTarget renderTarget, ref Viewport viewport) {
+	// 1. Render the (opaque) geometry into the G-Buffers.	
+	// 2. Construct a screen space grid, covering the frame buffer, with some fixed tile
+	//    size, t = (x, y), e.g. 32 × 32 pixels.	
+	// 3. For each light: find the screen space extents of the light volume and append the
+	//    light ID to each affected grid cell.	
+	// 4. For each fragment in the frame buffer, with location f = (x, y).
+	//    (a) sample the G-Buffers at f.
+	//    (b) accumulate light contributions from all lights in tile at ⌊f /t⌋
+	//    (c) output total light contributions to frame buffer at f
+
+	with(renderer.gBuffer) glCheck!glViewport(0, 0, width, height);
+	
+	//enable depth mask _before_ glClear ing the depth buffer!
+	glCheck!glDepthMask(GL_TRUE); scope(exit) glCheck!glDepthMask(GL_FALSE);
+	glCheck!glEnable(GL_DEPTH_TEST); scope(exit) glCheck!glDisable(GL_DEPTH_TEST);
+	glCheck!glDepthFunc(GL_LEQUAL);
+	
+	//bind gBuffer
+	glCheck!glBindFramebuffer(GL_DRAW_FRAMEBUFFER, renderer.gBuffer.fbo); scope(exit) glCheck!glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 
+	GLenum[] drawBuffers = [GL_COLOR_ATTACHMENT0 + 0, GL_COLOR_ATTACHMENT0 + 1, GL_COLOR_ATTACHMENT0 + 2];
+	glCheck!glDrawBuffers(drawBuffers.length, drawBuffers.ptr); scope(exit) glCheck!glDrawBuffer(GL_NONE);
+	glCheck!glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+//	glCheck!glClearColor(0, 0, 0.3, 1);
+	//bind pipeline
+	glCheck!glBindProgramPipeline(renderer.pipeline.pipeline); scope(exit) glCheck!glBindProgramPipeline(0);
+	
+	{// Draw Geometry
+		scope(exit) glCheck!glBindVertexArray(0);
+		scope(exit) glCheck!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // TODO: GL_ELEMENT_ARRAY_BUFFER should be vao state, but bugs might make this necessary
+		
+		for(size_t meshIdx = 0; meshIdx < scene.mesh.cnt; ++meshIdx) {
+			glCheck!glBindVertexArray(scene.mesh.vao[meshIdx]); 
+			
+			glCheck!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, scene.mesh.vboIndices[meshIdx]); // TODO: GL_ELEMENT_ARRAY_BUFFER should be vao state, but bugs might make this necessary
+
+//			DrawElementsIndirectCommand
+
+//			glCheck!glcommand
+
+			version(none) { //inefficient. use glMultiDrawElementsIndirect instead
+				glCheck!glDrawElements(GL_TRIANGLES, scene.mesh.cntIndices[meshIdx], GL_UNSIGNED_INT, null);
+			}
+		}
+
+//		glCheck!glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, null, scene.mesh.cnt, 0);
+	}
+}
+
+
+debug {
+	void blitGBufferToScreen(ref OpenGlTiledDeferredRenderer renderer) {
+		glCheck!glBindFramebuffer(GL_READ_FRAMEBUFFER, renderer.gBuffer.fbo); scope(exit) glCheck!glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); 
+
+		GLsizei width = renderer.gBuffer.width;
+		GLsizei height = renderer.gBuffer.height;
+
+		scope(exit) glCheck!glReadBuffer(GL_NONE);
+
+		glCheck!glReadBuffer(GL_COLOR_ATTACHMENT0 + 0);
+		glCheck!glBlitFramebuffer(0, 0, width, height, 0, height-300, 400, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+
+		glCheck!glReadBuffer(GL_COLOR_ATTACHMENT0 + 1);
+		glCheck!glBlitFramebuffer(0, 0, width, height, 0, 0, 400, 300, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+
+		glCheck!glReadBuffer(GL_COLOR_ATTACHMENT0 + 2);
+		glCheck!glBlitFramebuffer(0, 0, width, height, width-400, height-300, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+	}
+}