Skip to content

Instantly share code, notes, and snippets.

@roxlu
Last active September 11, 2020 15:48
Show Gist options
  • Save roxlu/6b0d2081675c24c607d0 to your computer and use it in GitHub Desktop.
Save roxlu/6b0d2081675c24c607d0 to your computer and use it in GitHub Desktop.

Revisions

  1. @roxlu revised this gist Nov 19, 2016. No changes.
  2. @roxlu revised this gist Nov 19, 2016. No changes.
  3. @roxlu revised this gist Oct 27, 2015. No changes.
  4. @roxlu revised this gist Oct 27, 2015. No changes.
  5. @roxlu created this gist Oct 27, 2015.
    82 changes: 82 additions & 0 deletions TestFboChangeBuffer.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,82 @@
    #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];
    }
    25 changes: 25 additions & 0 deletions TestFboChangeBuffer.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    #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
    87 changes: 87 additions & 0 deletions TestFboSwap.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,87 @@
    #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];
    }
    27 changes: 27 additions & 0 deletions TestFboSwap.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    #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
    105 changes: 105 additions & 0 deletions TestWorker.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,105 @@
    #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;
    }


    37 changes: 37 additions & 0 deletions TestWorker.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,37 @@
    /*
    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
    72 changes: 72 additions & 0 deletions TimerGpuGl.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,72 @@
    #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, &timestamp);
    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 */
    37 changes: 37 additions & 0 deletions TimerGpuGl.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,37 @@
    #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
    196 changes: 196 additions & 0 deletions test_fbo_performance.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,196 @@
    #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);
    }