I am writing a 2d renderer. In a real world example one draw call can handle 600 000 vertices.
When buffer is bigger than 600 000, the renderer execute another draw call and so on.
Problems occur when there are 2 or more draw calls. There are 2 cases I tested:
synchronization per draw call - primitives are drawn, but are blinking (only when draw calls count > 1, when there is 1 draw call it works without any problems). synchronization once per all draw calls - only the last draw call renders primitive Example code tests a basic case, 4 vertices per draw call
This is how the renderer looks like:
struct Vertex2D
{
Vertex2D() = default;
Vertex2D(float x, float y, uint32 color)
{
position = { x, y };
this->color = color;
}
Vector2<float> position;
uint32 color;
};
struct Range
{
uint begin = 0;
GLsync sync = 0;
};
struct Renderer
{
Renderer() = default;
~Renderer()
{
glUnmapNamedBuffer(vbo);
glDeleteBuffers(1, &vbo);
glUnmapNamedBuffer(ebo);
glDeleteBuffers(1, &ebo);
glDeleteVertexArrays(1, &vao);
}
void Create()
{
shader.Create("shader.glsl");
vertices.resize(MaxVertexCount);
elements.resize(MaxElementCount);
auto mapBit = GL_MAP_WRITE_BIT | GL_MAP_COHERENT_BIT | GL_MAP_PERSISTENT_BIT;
glCreateVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glNamedBufferStorage(vbo, BufferCount * MaxVertexCount * sizeof(Vertex2D), nullptr, mapBit);
mappedVertex = (Vertex2D*)glMapNamedBufferRange(vbo, NULL, BufferCount * MaxVertexCount * sizeof(Vertex2D), mapBit);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(Vertex2D), (const void*)offsetof(Vertex2D, position));
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(Vertex2D), (const void*)offsetof(Vertex2D, color));
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glNamedBufferStorage(ebo, BufferCount * MaxElementCount * sizeof(uint32), nullptr, mapBit);
mappedElements = (uint32*)glMapNamedBufferRange(ebo, NULL, BufferCount * MaxElementCount * sizeof(uint32), mapBit);
vertexRanges[0].begin = 0;
vertexRanges[1].begin = MaxVertexCount;
vertexRanges[2].begin = MaxVertexCount * 2;
elementsRanges[0].begin = 0;
elementsRanges[1].begin = MaxElementCount;
elementsRanges[2].begin = MaxElementCount * 2;
}
void LockBuffer(GLsync& syncObject)
{
if (syncObject) {
glDeleteSync(syncObject);
}
syncObject = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
void WaitBuffer(GLsync& syncObject)
{
if (syncObject) {
while (true) {
auto waitReturn = glClientWaitSync(syncObject, GL_SYNC_FLUSH_COMMANDS_BIT, 1);
if (waitReturn == GL_ALREADY_SIGNALED || waitReturn == GL_CONDITION_SATISFIED)
return;
}
}
}
void FillRect(float x, float y, float w, float h, uint32 color)
{
vertices[0] = Vertex2D(x, y, color);
vertices[1] = Vertex2D(x, y + h, color);
vertices[2] = Vertex2D(x + w, y + h, color);
vertices[3] = Vertex2D(x + w, y, color);
elements[0] = 0;
elements[1] = 1;
elements[2] = 2;
elements[3] = 2;
elements[4] = 3;
elements[5] = 0;
}
void Lock()
{
WaitBuffer(vertexRanges[rangeIndex].sync);
WaitBuffer(elementsRanges[rangeIndex].sync);
}
void RenderPresent()
{
Lock();
memcpy(&mappedVertex[vertexRanges[rangeIndex].begin], vertices.data(), (sizeof(Vertex2D) * vertices.size()));
memcpy(&mappedElements[elementsRanges[rangeIndex].begin], elements.data(), (sizeof(MiEuint32) * elements.size()));
glDrawElements(GL_TRIANGLES, elements.size(), GL_UNSIGNED_INT, nullptr);
Unlock();
}
void Unlock()
{
LockBuffer(vertexRanges[rangeIndex].sync);
LockBuffer(elementsRanges[rangeIndex].sync);
rangeIndex = (rangeIndex + 1) % BufferCount;
}
enum
{
BufferCount = 3,
MaxVertexCount = 4,
MaxElementCount = (MaxVertexCount - 2) * 3
};
int rangeIndex = 0;
Range vertexRanges[BufferCount];
Range elementsRanges[BufferCount];
Shader shader;
GLuint vao;
GLuint vbo;
GLuint ebo;
Vertex2D* mappedVertex = nullptr;
uint32* mappedElements = nullptr;
std::vector<Vertex2D> vertices;
std::vector<uint32> elements;
};
int main()
{
//Create window, gl context, init gl, etc. etc.
Renderer renderer;
while(!quit) {
glClear(GL_COLOR_BUFFER_BIT);
renderer.FillRect(10.0f, 70.0f, 50.0f, 50.0f, 0xffffffff);
renderer.RenderPresent();
//Pretend we are out of max vertex count range, allocate another draw call with render present;
renderer.FillRect(70.0f, 70.0f, 50.0f, 50.0f, 0xff0000ff);
renderer.RenderPresent();
//swap windows
}
}
void RenderPresent()
{
//Lock();
memcpy(&mappedVertex[vertexRanges[rangeIndex].begin], vertices.data(), (sizeof(Vertex2D) * vertices.size()));
memcpy(&mappedElements[elementsRanges[rangeIndex].begin], elements.data(), (sizeof(MiEuint32) * elements.size()));
glDrawElements(GL_TRIANGLES, elements.size(), GL_UNSIGNED_INT, nullptr);
//Unlock();
}
//---------------
while(!quit) {
glClear(GL_COLOR_BUFFER_BIT);
renderer.Lock();
renderer.FillRect(10.0f, 70.0f, 50.0f, 50.0f, 0xffffffff);
renderer.RenderPresent();
//Pretend we are out of max vertex count range, allocate another draw call with render present;
renderer.FillRect(70.0f, 70.0f, 50.0f, 50.0f, 0xff0000ff);
renderer.RenderPresent();
renderer.Unlock();
//swap windows
}
Why those problems occur? How to fix it?
User contributions licensed under CC BY-SA 3.0