-
-
Save stagas/2343ed26ef5676b74da2e5d1205fe837 to your computer and use it in GitHub Desktop.
Comparing the performance of different ping/pong techniques when using FBOs. One version uses two fbos and swaps between these, the other switches the draw and read buffer. See these results https://docs.google.com/spreadsheets/d/1ZyTQGkjYQajQtu8OvgRyaXJl46F4rZJStBCEK2kebgE/edit?usp=sharing for a comparison. It seems that switching read / draw b…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <stdio.h> | |
#define ROXLU_USE_MATH | |
#define ROXLU_USE_OPENGL | |
#define ROXLU_IMPLEMENTATION | |
#include <glad/glad.h> | |
#include <GLFW/glfw3.h> | |
#include <tinylib.h> | |
#include <poly/Log.h> | |
#include <poly/Timer.h> | |
#include <TimerGpuGl.h> | |
#include <TestFboSwap.h> | |
#include <TestFboChangeBuffer.h> | |
#include <TestWorker.h> | |
#define TEST_1 | |
//#define TEST_2 | |
using namespace poly; | |
void button_callback(GLFWwindow* win, int bt, int action, int mods); | |
void cursor_callback(GLFWwindow* win, double x, double y); | |
void key_callback(GLFWwindow* win, int key, int scancode, int action, int mods); | |
void char_callback(GLFWwindow* win, unsigned int key); | |
void error_callback(int err, const char* desc); | |
void resize_callback(GLFWwindow* window, int width, int height); | |
int main() { | |
glfwSetErrorCallback(error_callback); | |
if(!glfwInit()) { | |
printf("Error: cannot setup glfw.\n"); | |
exit(EXIT_FAILURE); | |
} | |
glfwWindowHint(GLFW_SAMPLES, 4); | |
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); | |
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); | |
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); | |
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | |
GLFWwindow* win = NULL; | |
int w = 1280; | |
int h = 720; | |
win = glfwCreateWindow(w, h, "FBO performance", NULL, NULL); | |
if(!win) { | |
glfwTerminate(); | |
exit(EXIT_FAILURE); | |
} | |
glfwSetFramebufferSizeCallback(win, resize_callback); | |
glfwSetKeyCallback(win, key_callback); | |
glfwSetCharCallback(win, char_callback); | |
glfwSetCursorPosCallback(win, cursor_callback); | |
glfwSetMouseButtonCallback(win, button_callback); | |
glfwMakeContextCurrent(win); | |
glfwSwapInterval(1); | |
if (!gladLoadGL()) { | |
printf("Cannot load GL.\n"); | |
exit(1); | |
} | |
// ---------------------------------------------------------------- | |
// THIS IS WHERE YOU START CALLING OPENGL FUNCTIONS, NOT EARLIER!! | |
// ---------------------------------------------------------------- | |
poly_log_init(); | |
#if defined(TEST_1) | |
TestFboSwap fbo_test; | |
if (0 != fbo_test.init()) { | |
exit(EXIT_FAILURE); | |
} | |
#endif | |
#if defined(TEST_2) | |
TestFboChangeBuffer fbo_test; | |
if (0 != fbo_test.init()) { | |
exit(EXIT_FAILURE); | |
} | |
#endif | |
TestWorker worker; | |
if (0 != worker.init()) { | |
exit(EXIT_FAILURE); | |
} | |
TimerGpuGlQuery timer; | |
if (0 != timer.init("perf")) { | |
exit(EXIT_FAILURE); | |
} | |
uint64_t dt = 0; | |
uint64_t s = 0; | |
uint64_t d = 0; | |
int r = 0; | |
uint64_t total_cpu = 0; | |
uint64_t total_gpu = 0; | |
uint64_t max_runs = 10000; | |
while(!glfwWindowShouldClose(win)) { | |
glClearColor(1.0f, 1.0f, 1.0f, 1.0f); | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
for (int i = 0; i < 25; ++i) { | |
for (uint64_t run = 0; run < max_runs; ++run) { | |
s = nanos(); | |
timer.begin(); | |
{ | |
fbo_test.activate(); | |
worker.performWork(fbo_test.getReadTexture()); | |
fbo_test.swap(); | |
} | |
r = timer.end(dt); | |
glFinish(); | |
d = nanos() - s; | |
if (r == 0) { | |
total_gpu += dt; | |
total_cpu += d; | |
} | |
if (run == (max_runs-1)) { | |
SX_WARNING("GPU time, %llu, ns, %f, ms, " | |
"CPU time, %llu, ns, %f, ms, ", | |
total_gpu, double(total_gpu) / 1e6, | |
total_cpu, double(total_cpu) / 1e6); | |
total_gpu = 0; | |
total_cpu = 0; | |
break; | |
} | |
} | |
} | |
/* | |
SX_VERBOSE("GPU: % 8llu ns., %f ms., CPU: % 8llu ns., %f ms., r: %d", | |
dt, double(dt)/1e6, | |
d, double(d)/1e6, | |
r); | |
*/ | |
break; | |
//glfwSwapBuffers(win); | |
glfwPollEvents(); | |
} | |
glfwTerminate(); | |
return EXIT_SUCCESS; | |
} | |
void char_callback(GLFWwindow* win, unsigned int key) { | |
} | |
void key_callback(GLFWwindow* win, int key, int scancode, int action, int mods) { | |
if(action != GLFW_PRESS) { | |
return; | |
} | |
switch(key) { | |
case GLFW_KEY_ESCAPE: { | |
glfwSetWindowShouldClose(win, GL_TRUE); | |
break; | |
} | |
}; | |
} | |
void resize_callback(GLFWwindow* window, int width, int height) { | |
} | |
void cursor_callback(GLFWwindow* win, double x, double y) { | |
} | |
void button_callback(GLFWwindow* win, int bt, int action, int mods) { | |
} | |
void error_callback(int err, const char* desc) { | |
printf("GLFW error: %s (%d)\n", desc, err); | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <poly/Log.h> | |
#include <TestFboChangeBuffer.h> | |
using namespace poly; | |
TestFboChangeBuffer::TestFboChangeBuffer() | |
:fbo(0) | |
,dx(0) | |
{ | |
tex[0] = 0; | |
tex[1] = 0; | |
} | |
TestFboChangeBuffer::~TestFboChangeBuffer() { | |
} | |
int TestFboChangeBuffer::init() { | |
if (0 != fbo) { | |
SX_ERROR("Cannot initialize because we're already initialized."); | |
return -1; | |
} | |
glGenTextures(2, tex); | |
glBindTexture(GL_TEXTURE_2D, tex[0]); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
glBindTexture(GL_TEXTURE_2D, tex[1]); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
glBindTexture(GL_TEXTURE_2D, 0); | |
glGenFramebuffers(1, &fbo); | |
glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex[0], 0); | |
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex[1], 0); | |
if (GL_FRAMEBUFFER_COMPLETE != glCheckFramebufferStatus(GL_FRAMEBUFFER)) { | |
SX_ERROR("Framebuffer not yet complete."); | |
glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
return -1; | |
} | |
glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
return 0; | |
} | |
void TestFboChangeBuffer::activate() { | |
glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
if (0 == dx) { | |
glDrawBuffer(GL_COLOR_ATTACHMENT1); | |
glReadBuffer(GL_COLOR_ATTACHMENT0); | |
} | |
else { | |
glDrawBuffer(GL_COLOR_ATTACHMENT0); | |
glReadBuffer(GL_COLOR_ATTACHMENT1); | |
} | |
glViewport(0, 0, 1024, 1024); | |
} | |
void TestFboChangeBuffer::swap() { | |
dx = 1 - dx; | |
} | |
GLuint TestFboChangeBuffer::getReadTexture() { | |
return tex[dx]; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef TEST_FBO_CHANGE_BUFFER_H | |
#define TEST_FBO_CHANGE_BUFFER_H | |
#define ROXLU_USE_MATH | |
#define ROXLU_USE_OPENGL | |
#include <glad/glad.h> | |
#include <tinylib.h> | |
class TestFboChangeBuffer { | |
public: | |
TestFboChangeBuffer(); | |
~TestFboChangeBuffer(); | |
int init(); | |
void activate(); | |
void swap(); | |
GLuint getReadTexture(); | |
public: | |
GLuint fbo; | |
GLuint tex[2]; | |
int dx; | |
}; | |
#endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <TestFboSwap.h> | |
#include <poly/Log.h> | |
using namespace poly; | |
TestFboSwap::TestFboSwap() | |
:dx(0) | |
{ | |
fbo[0] = 0; | |
fbo[1] = 0; | |
tex[0] = 0; | |
tex[1] = 0; | |
} | |
TestFboSwap::~TestFboSwap() { | |
} | |
int TestFboSwap::init() { | |
if (0 != fbo[0]) { | |
SX_ERROR("Alredy initialized."); | |
return -1; | |
} | |
if (0 != createFbo(fbo[0], tex[0])) { | |
return -2; | |
} | |
if (0 != createFbo(fbo[1], tex[1])) { | |
return -3; | |
} | |
return 0; | |
} | |
int TestFboSwap::createFbo(GLuint& fboOut, GLuint& texOut) { | |
if (0 != fboOut) { | |
SX_ERROR("Given FBO is not 0, already created?"); | |
return -1; | |
} | |
if (0 != texOut) { | |
SX_ERROR("Given TEX is not 0, already created?"); | |
return -2; | |
} | |
glGenTextures(1, &texOut); | |
glBindTexture(GL_TEXTURE_2D, texOut); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
glBindTexture(GL_TEXTURE_2D, 0); | |
glGenFramebuffers(1, &fboOut); | |
glBindFramebuffer(GL_FRAMEBUFFER, fboOut); | |
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texOut, 0); | |
if (GL_FRAMEBUFFER_COMPLETE != glCheckFramebufferStatus(GL_FRAMEBUFFER)) { | |
SX_ERROR("Framebuffer not yet complete."); | |
glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
return -3; | |
} | |
glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
return 0; | |
} | |
void TestFboSwap::activate() { | |
// glWaitSync(); | |
glBindFramebuffer(GL_FRAMEBUFFER, fbo[dx]); | |
glDrawBuffer(GL_COLOR_ATTACHMENT0); | |
glViewport(0, 0, 1024, 1024); | |
} | |
void TestFboSwap::swap() { | |
dx = 1 - dx; | |
} | |
GLuint TestFboSwap::getReadTexture() { | |
return tex[dx - 1]; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef TEST_FBO_SWAP_H | |
#define TEST_FBO_SWAP_H | |
#define ROXLU_USE_MATH | |
#define ROXLU_USE_OPENGL | |
#include <glad/glad.h> | |
#include <tinylib.h> | |
class TestFboSwap { | |
public: | |
TestFboSwap(); | |
~TestFboSwap(); | |
int init(); | |
void activate(); | |
void swap(); | |
GLuint getReadTexture(); /* texture from which we can read; so which isn't rendered into. */ | |
private: | |
int createFbo(GLuint& fboOut, GLuint& texOut); | |
public: | |
GLuint fbo[2]; | |
GLuint tex[2]; | |
int dx; | |
}; | |
#endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <poly/Log.h> | |
#include <TestWorker.h> | |
using namespace poly; | |
static const char* WORKER_VS = "" | |
"#version 330\n" | |
"" | |
" const vec2[] pos = vec2[4](" | |
" vec2(-1.0, 1.0)," | |
" vec2(-1.0, -1.0)," | |
" vec2(1.0, 1.0)," | |
" vec2(1.0, -1.0)" | |
" );" | |
"" | |
"const vec2 tex[] = vec2[4](" | |
" vec2(0.0, 1.0), " | |
" vec2(0.0, 0.0), " | |
" vec2(1.0, 1.0), " | |
" vec2(1.0, 0.0) " | |
");" | |
"" | |
"out vec2 v_tex;" | |
"" | |
"void main() { " | |
" gl_Position = vec4(pos[gl_VertexID], 0.0, 1.0);" | |
" v_tex = tex[gl_VertexID];" | |
"};" | |
""; | |
static const char* WORKER_FS = "" | |
"#version 330\n" | |
"" | |
"uniform sampler2D u_tex;" | |
"in vec2 v_tex;" | |
"layout (location = 0) out vec4 fragcolor;" | |
"" | |
"void main() {" | |
"" | |
" vec4 src = texture(u_tex, v_tex); " | |
" src.r += 0.01;" | |
" src.a = 1.0;" | |
" fragcolor = src;" | |
"}" | |
""; | |
TestWorker::TestWorker() | |
:vert(0) | |
,frag(0) | |
,prog(0) | |
,vao(0) | |
,u_tex(-1) | |
{ | |
} | |
int TestWorker::init() { | |
if (0 != vert) { | |
SX_ERROR("Cannot initialize the TestWorker because it's already initialized."); | |
return -1; | |
} | |
vert = rx_create_shader(GL_VERTEX_SHADER, WORKER_VS); | |
frag = rx_create_shader(GL_FRAGMENT_SHADER, WORKER_FS); | |
prog = rx_create_program(vert, frag, true); | |
glUseProgram(prog); | |
u_tex = glGetUniformLocation(prog, "u_tex"); | |
if (-1 == u_tex) { | |
SX_ERROR("u_tex < 0, not used in shader?"); | |
return -2; | |
} | |
glUniform1i(u_tex, 0); | |
glGenVertexArrays(1, &vao); | |
if (0 == vao) { | |
SX_ERROR("Failed to create the vao."); | |
return -3; | |
} | |
return 0; | |
} | |
int TestWorker::performWork(GLuint sourceTexture) { | |
if (0 == sourceTexture) { | |
SX_ERROR("Given texture id is invald."); | |
return -1; | |
} | |
glActiveTexture(GL_TEXTURE0); | |
glBindTexture(GL_TEXTURE_2D, sourceTexture); | |
glUseProgram(prog); | |
glBindVertexArray(vao); | |
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | |
return 0; | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Test Worker | |
=========== | |
Simple class that just does some OpenGL | |
calls and applies a shader. This is used while | |
doing the Fbo* tests. | |
We create a shader and we draw a full viewport | |
rectangle to make sure the GPU does some work. | |
*/ | |
#ifndef TEST_WORKER_H | |
#define TEST_WORKER_H | |
#define ROXLU_USE_MATH | |
#define ROXLU_USE_OPENGL | |
#include <glad/glad.h> | |
#include <tinylib.h> | |
class TestWorker { | |
public: | |
TestWorker(); | |
int init(); | |
int performWork(GLuint sourceTexture); | |
public: | |
GLuint vert; | |
GLuint frag; | |
GLuint prog; | |
GLuint vao; | |
GLint u_tex; | |
}; | |
#endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <poly/Log.h> | |
#include <TimerGpuGl.h> | |
namespace poly { | |
TimerGpuGlQuery::TimerGpuGlQuery() | |
:write_num(0) | |
,write_dx(0) | |
,prev_timestamp(0) | |
{ | |
memset((char*)id, 0x00, sizeof(GLuint) * NUM_GPU_GL_TIMERS); | |
} | |
TimerGpuGlQuery::~TimerGpuGlQuery() { | |
// SX_ERROR("Remove GL objects. "); | |
} | |
int TimerGpuGlQuery::init(const std::string& name) { | |
if (0 != id[0]) { | |
SX_ERROR("Cannot initialize the timer query because it's already created."); | |
return -1; | |
} | |
glGenQueries(NUM_GPU_GL_TIMERS, id); | |
return 0; | |
} | |
int TimerGpuGlQuery::begin() { | |
if (0 == id[0]) { | |
SX_ERROR("Cannot beging the GPU timer query because we're not initialized."); | |
return -1; | |
} | |
glQueryCounter(id[write_dx], GL_TIMESTAMP); | |
write_num++; | |
write_dx = write_num % NUM_GPU_GL_TIMERS; | |
return 0; | |
} | |
int TimerGpuGlQuery::end(uint64_t& result) { | |
GLuint timer_available = GL_FALSE; | |
uint64_t timestamp = 0; | |
if (write_num > NUM_GPU_GL_TIMERS) { | |
glGetQueryObjectuiv(id[write_dx], GL_QUERY_RESULT_AVAILABLE, &timer_available); | |
if (GL_TRUE == timer_available) { | |
glGetQueryObjectui64v(id[write_dx], GL_QUERY_RESULT, ×tamp); | |
result = timestamp - prev_timestamp; | |
prev_timestamp = timestamp; | |
return 0; | |
} | |
else { | |
return -1; | |
} | |
} | |
else { | |
/* still filling the buffer. */ | |
result = 0; | |
return -1; | |
} | |
return 0; | |
} | |
} /* namespace poly */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef POLY_TIMER_GPU_GL_H | |
#define POLY_TIMER_GPU_GL_H | |
#include <string> | |
#include <stdint.h> | |
#include <glad/glad.h> | |
#define NUM_GPU_GL_TIMERS 6 | |
namespace poly { | |
class TimerGpuGlQuery { | |
public: | |
TimerGpuGlQuery(); | |
~TimerGpuGlQuery(); | |
int init(const std::string& name); | |
int begin(); | |
int end(uint64_t& time); | |
public: | |
std::string name; | |
GLuint id[NUM_GPU_GL_TIMERS]; | |
uint64_t prev_timestamp; | |
uint64_t write_num; | |
uint8_t write_dx; | |
}; | |
class TimerGpuGl { | |
public: | |
TimerGpuGl(); | |
~TimerGpuGl(); | |
}; | |
} /* namespace poly */ | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment