I want to write 2d renderer that allow user to draw some magic number vertices per draw call. If the magic number is exceeded, renderer executes another draw call. It works very well with glBufferSubData
, but not exactly want to work with persistent mapping. If I do synchronization once per every draw calls, only the last one gets rendered. If every draw calls has its own lock and unlock, rectangles are blinking every half a second.
Why it does not work the way like BufferSubData works? Is it even possible to achieve the effect I want?
Here's a small example, it tests almost the worst case, a rect per draw call.
typedef unsigned int uint32;
typedef unsigned long long uint;
struct Vertex2D
{
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()
{
if (persistentMapping) {
glUnmapNamedBuffer(vbo);
}
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
}
void Create()
{
shader = shader.Create("assets/shaders/renderer.glsl");
auto mapBit = GL_MAP_WRITE_BIT | (persistentMapping ? (GL_MAP_COHERENT_BIT | GL_MAP_PERSISTENT_BIT) : 0);
glCreateVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glNamedBufferStorage(vbo, bufferCount * quadVertexCount * sizeof(Vertex2D), nullptr, mapBit | (!persistentMapping ? GL_DYNAMIC_STORAGE_BIT : 0));
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));
if (persistentMapping) {
mappedVertex = (Vertex2D*)glMapNamedBufferRange(vbo, NULL, bufferCount * quadVertexCount * sizeof(Vertex2D), mapBit);
}
ranges[0].begin = 0;
ranges[1].begin = quadVertexCount;
ranges[2].begin = quadVertexCount * 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;
waitCount++;
}
}
}
void Begin()
{
vertices.clear();
}
void FillRect(float x, float y, float w, float h, uint32 color)
{
vertices.push_back(Vertex2D(x, y, color));
vertices.push_back(Vertex2D(x, y + h, color));
vertices.push_back(Vertex2D(x + w, y + h, color));
vertices.push_back(Vertex2D(x + w, y, color));
}
void Lock()
{
LockBuffer(ranges[rangeIndex].sync);
rangeIndex = (rangeIndex + 1) % bufferCount;
}
void RenderPresent()
{
if (persistentMapping) WaitBuffer(ranges[rangeIndex].sync);
//Clear old CPU data
Begin();
//Fill CPU storage with new data
FillRect(10.0f, 10.0f, 50.0f, 50.0f, 0xff00ffff);
//pass data to the GPU and draw
if (persistentMapping) {
memcpy(&mappedVertex[ranges[rangeIndex].begin], vertices.data(), sizeof(Vertex2D) * vertices.size());
}
else glNamedBufferSubData(vbo, 0, sizeof(Vertex2D) * vertices.size(), vertices.data());
//Draw
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
#define LOCK_ONCE_PER_DRAW_CALL 1
#if LOCK_ONCE_PER_DRAW_CALL 1
if (persistentMapping) Lock();
if (persistentMapping) WaitBuffer(ranges[rangeIndex].sync);
#endif
//Clear old CPU data
Begin();
//Fill CPU storage with new data
FillRect(10.0f, 70.0f, 50.0f, 50.0f, 0xffffffff);
//pass data to the GPU and draw
if (persistentMapping) {
memcpy(&mappedVertex[ranges[rangeIndex].begin], vertices.data(), sizeof(Vertex2D) * vertices.size());
}
else glNamedBufferSubData(vbo, 0, sizeof(Vertex2D) * vertices.size(), vertices.data());
//Draw
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
if (persistentMapping) Lock();
}
static const bool persistentMapping = true;
static const int bufferCount = 3;
static const int quadVertexCount = 4;
int rangeIndex = 0;
Range ranges[bufferCount];
int waitCount = 0;
Shader shader = nullptr;
GLuint vao;
GLuint vbo;
Vertex2D* mappedVertex = nullptr;
std::vector<Vertex2D> vertices;
};
int main()
{
//create window, gl context, init opengl .. other stuff
Renderer renderer;
renderer.Create();
bool quit = false;
while(!quit) {
glClear(GL_COLOR_BUFFER_BIT);
renderer.RenderPresent();
//swap buffers/windows
}
return 0;
}
User contributions licensed under CC BY-SA 3.0