|
|
@@ -12,6 +12,9 @@ import std.exception : collectException;
|
|
|
|
|
|
import std.experimental.logger;
|
|
|
|
|
|
+import three.gl.buffer;
|
|
|
+import three.gl.draw;
|
|
|
+import three.gl.sync;
|
|
|
|
|
|
enum maxVertices = 1024;
|
|
|
enum maxIndices = 1024;
|
|
|
@@ -22,246 +25,75 @@ enum kOneSecondInNanoSeconds = GLuint64(1000000000);
|
|
|
|
|
|
|
|
|
|
|
|
-//======================================================================================================================
|
|
|
-//
|
|
|
-//======================================================================================================================
|
|
|
-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.");
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void destruct(GlBufferTarget Target, T)(ref GlBuffer!(Target, T) buffer) {
|
|
|
- glCheck!glUnmapBuffer(Target);
|
|
|
- glCheck!glBindBuffer(Target, 0);
|
|
|
- glCheck!glDeleteBuffers(1, &buffer.handle);
|
|
|
- buffer = buffer.init;
|
|
|
-}
|
|
|
-
|
|
|
-void bind(GlBufferTarget Target, T)(ref GlBuffer!(Target, T) buffer) {
|
|
|
- glCheck!glBindBuffer(Target, buffer.handle);
|
|
|
-}
|
|
|
-
|
|
|
-void unbind(GlBufferTarget Target, T)(ref GlBuffer!(Target, T) buffer) {
|
|
|
- glCheck!glBindBuffer(Target, 0);
|
|
|
-}
|
|
|
-
|
|
|
-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;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-struct DrawParameter {
|
|
|
- Matrix4 transformationMatrix;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
//======================================================================================================================
|
|
|
//
|
|
|
//======================================================================================================================
|
|
|
struct Renderer {
|
|
|
uint width;
|
|
|
uint height;
|
|
|
- GLsync sync;
|
|
|
+ GlSyncManager syncManager;
|
|
|
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
|
|
|
-}
|
|
|
-
|
|
|
-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(bufferCount * maxVertices, createFlags, mapFlags);
|
|
|
- renderer.indexBuffer.construct(bufferCount * maxIndices, createFlags, mapFlags);
|
|
|
- renderer.perInstanceParamBuffer.construct(bufferCount * maxPerInstanceParams, createFlags, mapFlags);
|
|
|
- renderer.dispatchIndirectCommandBuffer.construct(bufferCount * maxIndirectCommands, createFlags, mapFlags);
|
|
|
-
|
|
|
- glCheck!glEnableVertexAttribArray(0);
|
|
|
- glCheck!glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, VertexData.sizeof, cast(GLvoid*)0 );
|
|
|
- glCheck!glEnableVertexAttribArray(1);
|
|
|
- glCheck!glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, VertexData.sizeof, cast(GLvoid*)3 );
|
|
|
- glCheck!glEnableVertexAttribArray(2);
|
|
|
- glCheck!glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, VertexData.sizeof, cast(GLvoid*)6 );
|
|
|
- glCheck!glEnableVertexAttribArray(3);
|
|
|
- glCheck!glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, VertexData.sizeof, cast(GLvoid*)10 );
|
|
|
-}
|
|
|
-
|
|
|
-void destruct(ref Renderer renderer) {
|
|
|
- renderer.vertexBuffer.destruct();
|
|
|
- renderer.indexBuffer.destruct();
|
|
|
- renderer.perInstanceParamBuffer.destruct();
|
|
|
- renderer.dispatchIndirectCommandBuffer.destruct();
|
|
|
- renderer = renderer.init;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-struct BufferLockManager(bool UseBusyCpu) {
|
|
|
- struct BufferRange
|
|
|
- {
|
|
|
- size_t startOffset;
|
|
|
- size_t length;
|
|
|
-
|
|
|
- bool overlaps(BufferRange rhs) const {
|
|
|
- return startOffset < (rhs.startOffset + rhs.length) && rhs.startOffset < (startOffset + length);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- struct BufferLock
|
|
|
- {
|
|
|
- BufferRange range;
|
|
|
- GLsync syncObj;
|
|
|
+ GlShaderStorageBuffer!GlDrawParameter perInstanceParamBuffer; // is filled with draw parameters for each instance each frame. shall be accessed as a ringbuffer
|
|
|
+ GlDispatchIndirectBuffer!GlDrawElementsIndirectCommand dispatchIndirectCommandBuffer; // is filled with DrawElementsIndirectCommand for each mesh each frame. shall be accessed as a ringbuffer
|
|
|
+
|
|
|
+ void construct(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;
|
|
|
+
|
|
|
+ this.vertexBuffer.construct(bufferCount * maxVertices, createFlags, mapFlags);
|
|
|
+ this.indexBuffer.construct(bufferCount * maxIndices, createFlags, mapFlags);
|
|
|
+ this.perInstanceParamBuffer.construct(bufferCount * maxPerInstanceParams, createFlags, mapFlags);
|
|
|
+ this.dispatchIndirectCommandBuffer.construct(bufferCount * maxIndirectCommands, createFlags, mapFlags);
|
|
|
+
|
|
|
+ glCheck!glEnableVertexAttribArray(0);
|
|
|
+ glCheck!glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, VertexData.sizeof, cast(GLvoid*)0 );
|
|
|
+ glCheck!glEnableVertexAttribArray(1);
|
|
|
+ glCheck!glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, VertexData.sizeof, cast(GLvoid*)3 );
|
|
|
+ glCheck!glEnableVertexAttribArray(2);
|
|
|
+ glCheck!glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, VertexData.sizeof, cast(GLvoid*)6 );
|
|
|
+ glCheck!glEnableVertexAttribArray(3);
|
|
|
+ glCheck!glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, VertexData.sizeof, cast(GLvoid*)10 );
|
|
|
}
|
|
|
|
|
|
- BufferLock[] bufferLocks;
|
|
|
-}
|
|
|
-
|
|
|
-void waitForLockedRange(bool UseBusyCpu)(ref BufferLockManager!UseBusyCpu bufferLockManager, size_t lockBeginOffset, size_t lockLength) {
|
|
|
- BufferRange testRange = BufferRange(lockBeginOffset, lockLength);
|
|
|
- BufferLock[] swapLocks;
|
|
|
-
|
|
|
- foreach(ref bl; bufferLockManager.bufferLocks) {
|
|
|
- if (testRange.overlaps(bl.range)) {
|
|
|
- static if(UseBusyCpu) {
|
|
|
- GLbitfield waitFlags = 0;
|
|
|
- GLuint64 waitDuration = 0;
|
|
|
- while(true) {
|
|
|
- GLenum waitRet = glCheck!glClientWaitSync(syncObj, waitFlags, waitDuration);
|
|
|
- if (waitRet == GL_ALREADY_SIGNALED || waitRet == GL_CONDITION_SATISFIED) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (waitRet == GL_WAIT_FAILED) {
|
|
|
- assert(!"Not sure what to do here. Probably raise an exception or something.");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // After the first time, need to start flushing, and wait for a looong time.
|
|
|
- waitFlags = GL_SYNC_FLUSH_COMMANDS_BIT;
|
|
|
- waitDuration = kOneSecondInNanoSeconds;
|
|
|
- }
|
|
|
- }
|
|
|
- else {
|
|
|
- glCheck!glWaitSync(syncObj, 0, GL_TIMEOUT_IGNORED);
|
|
|
- }
|
|
|
+ void destruct() {
|
|
|
+ this.vertexBuffer.destruct();
|
|
|
+ this.indexBuffer.destruct();
|
|
|
+ this.perInstanceParamBuffer.destruct();
|
|
|
+ this.dispatchIndirectCommandBuffer.destruct();
|
|
|
+ }
|
|
|
|
|
|
- glCheck!glDeleteSync(bl.syncObj);
|
|
|
- }
|
|
|
- else {
|
|
|
- swapLocks ~= bl;
|
|
|
+ void uploadModelData(GlArrayBuffer!VertexData vertexBuffer, GlElementArrayBuffer!IndexData indexBuffer, ModelData modelData) {
|
|
|
+ //TODO: wait for buffer range
|
|
|
+ //mBufferLockManager.WaitForLockedRange(mStartDestOffset, _vertices.size() * sizeof(Vec2));
|
|
|
+
|
|
|
+ // TODO: check if buffers are bound. they should always be bound here!
|
|
|
+
|
|
|
+ // we need to store all models in one giant vbo to use glMultiDrawElementsIndirect.
|
|
|
+ // TODO: implement triple buffering. -> use vertexBuffer and indexBuffer as giant ring buffers
|
|
|
+ GLuint vertexBufferOffset = 0;
|
|
|
+ GLuint indexBufferOffset = 0;
|
|
|
+
|
|
|
+ foreach(meshData; modelData.meshData) {
|
|
|
+ import std.c.string: memcpy;
|
|
|
+ //upload vertex data
|
|
|
+ assert(this.vertexBuffer.length >= meshData.vertexData.length);
|
|
|
+ memcpy(this.vertexBuffer.data + vertexBufferOffset, meshData.vertexData.ptr, meshData.vertexData.length * VertexData.sizeof);
|
|
|
+ vertexBufferOffset += meshData.vertexData.length * VertexData.sizeof;
|
|
|
+ //upload index data
|
|
|
+ assert(this.indexBuffer.length >= meshData.indexData.length);
|
|
|
+ memcpy(this.indexBuffer.data + indexBufferOffset, meshData.indexData.ptr, meshData.indexData.length * IndexData.sizeof);
|
|
|
+ indexBufferOffset += meshData.indexData.length * IndexData.sizeof;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- import std.algorithm : swap;
|
|
|
- swap(bufferLockManager.bufferLocks, swapLocks);
|
|
|
-}
|
|
|
-
|
|
|
-void lockRange(bool UseBusyCpu)(ref BufferLockManager!UseBusyCpu bufferLockManager, size_t lockBeginOffset, size_t lockLength) {
|
|
|
- BufferRange newRange = BufferRange(lockBeginOffset, lockLength);
|
|
|
- GLsync syncName = glCheck!glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
|
- BufferLock newLock = BufferLock(newRange, syncName);
|
|
|
- bufferLockManager.bufferLocks ~= newLock;
|
|
|
-}
|
|
|
-
|
|
|
+ void renderOneFrame(ref Scene scene, ref Camera camera, ref RenderTarget renderTarget, ref Viewport viewport) {
|
|
|
|
|
|
-//void lockBuffer(ref Renderer renderer) {
|
|
|
-// if(renderer.sync) {
|
|
|
-// glDeleteSync(renderer.sync);
|
|
|
-// }
|
|
|
-// renderer.sync = glCheck!glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
|
-//}
|
|
|
-//
|
|
|
-//void waitBuffer(ref Renderer renderer) {
|
|
|
-// if(renderer.sync) {
|
|
|
-// while(true) {
|
|
|
-// GLenum waitReturn = glClientWaitSync(renderer.sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1);
|
|
|
-// if (waitReturn == GL_ALREADY_SIGNALED || waitReturn == GL_CONDITION_SATISFIED) {
|
|
|
-// return;
|
|
|
-// }
|
|
|
-// }
|
|
|
-// }
|
|
|
-//}
|
|
|
+ // wait until GPU has finished rendereing from our desired buffer destination
|
|
|
+ //TODO: syncManager.WaitForLockedRange(mStartDestOffset, _vertices.size() * sizeof(Vec2));
|
|
|
|
|
|
-void uploadModelData(ref Renderer renderer, ModelData modelData) {
|
|
|
- //TODO: wait for buffer range
|
|
|
- //mBufferLockManager.WaitForLockedRange(mStartDestOffset, _vertices.size() * sizeof(Vec2));
|
|
|
-
|
|
|
- renderer.vertexBuffer.bind();
|
|
|
- renderer.indexBuffer.bind();
|
|
|
- // we need to store all models in one giant vbo to use glMultiDrawElementsIndirect.
|
|
|
- // TODO: implement triple buffering. -> use vertexBuffer and indexBuffer as giant ring buffers
|
|
|
- GLuint vertexBufferOffset = 0;
|
|
|
- GLuint indexBufferOffset = 0;
|
|
|
|
|
|
- foreach(meshData; modelData.meshData) {
|
|
|
- import std.c.string: memcpy;
|
|
|
- //upload vertex data
|
|
|
- assert(renderer.vertexBuffer.length >= meshData.vertexData.length);
|
|
|
- memcpy(renderer.vertexBuffer.data + vertexBufferOffset, meshData.vertexData.ptr, meshData.vertexData.length * VertexData.sizeof);
|
|
|
- vertexBufferOffset += meshData.vertexData.length * VertexData.sizeof;
|
|
|
- //upload index data
|
|
|
- assert(renderer.indexBuffer.length >= meshData.indexData.length);
|
|
|
- memcpy(renderer.indexBuffer.data + indexBufferOffset, meshData.indexData.ptr, meshData.indexData.length * IndexData.sizeof);
|
|
|
- indexBufferOffset += meshData.indexData.length * IndexData.sizeof;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-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
|
|
|
@@ -272,34 +104,38 @@ void renderOneFrame(ref Renderer renderer, ref Scene scene, ref Camera camera, r
|
|
|
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
|
|
|
-
|
|
|
- // write draw commmands
|
|
|
+
|
|
|
+ GLsizei meshCount = 0;
|
|
|
+
|
|
|
+ glCheck!glViewport(0, 0, this.width, this.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
|
|
|
+
|
|
|
+ // write draw commmands
|
|
|
+
|
|
|
+ // draw //TODO: pass offset (cast to ptr) into command buffer instead of null
|
|
|
+
|
|
|
+
|
|
|
+ this.vertexBuffer.bind();
|
|
|
+ this.indexBuffer.bind();
|
|
|
+ this.perInstanceParamBuffer.bind();
|
|
|
+ this.dispatchIndirectCommandBuffer.bind();
|
|
|
+
|
|
|
+ glCheck!glMultiDrawElementsIndirect(GL_TRIANGLES, toGlType!(this.indexBuffer.ValueType), null, meshCount, 0);
|
|
|
+
|
|
|
+ //TODO: syncManager.LockRange(mStartDestOffset, _vertices.size() * sizeof(Vec2));
|
|
|
+ }
|
|
|
|
|
|
- // draw //TODO: pass offset (cast to ptr) into command buffer instead of null
|
|
|
-
|
|
|
-
|
|
|
- renderer.vertexBuffer.bind();
|
|
|
- renderer.indexBuffer.bind();
|
|
|
- renderer.perInstanceParamBuffer.bind();
|
|
|
- renderer.dispatchIndirectCommandBuffer.bind();
|
|
|
+ debug {
|
|
|
+ void blitGBufferToScreen() {
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- glCheck!glMultiDrawElementsIndirect(GL_TRIANGLES, toGlType!(renderer.indexBuffer.ValueType), null, meshCount, 0);
|
|
|
|
|
|
- //TODO: lock buffer range
|
|
|
-}
|
|
|
|
|
|
-debug {
|
|
|
- void blitGBufferToScreen(ref Renderer renderer) {
|
|
|
- }
|
|
|
-}
|