
// stats.cpp - rendering statistics for hairline

#include <assert.h>
#include <string.h>  // for memset
#include <stdio.h>

#include "stats.hpp"

#include "countof.h"

GLQueryObjects::GLQueryObjects()
    : vertices_submitted_query(gl_query_objects[0])
    , primitives_generated_query(gl_query_objects[1])
    , tess_control_shader_patches_query(gl_query_objects[2])
    , tess_evaluation_shader_invocations_query(gl_query_objects[3])
    , geometry_shader_invocations_query(gl_query_objects[4])
    , geometry_primitives_emitted_query(gl_query_objects[5])
    , clipping_input_primitives_query(gl_query_objects[6])
    , clipping_output_primitives_query(gl_query_objects[7])
{
    memset(gl_query_objects, 0, sizeof(gl_query_objects));
}

void GLQueryObjects::init()
{
    glGenQueries(countof(gl_query_objects), gl_query_objects);
}

void GLQueryObjects::ensureInitialized()
{
    if (gl_query_objects[0] == 0) {
        init();
    }
}

void GLQueryObjects::beginQueries()
{
    glBeginQuery(GL_VERTICES_SUBMITTED_ARB, vertices_submitted_query);  // 4.6 or ARB_pipeline_statistics_query
    glBeginQuery(GL_PRIMITIVES_SUBMITTED_ARB, primitives_generated_query);
    glBeginQuery(GL_TESS_CONTROL_SHADER_PATCHES_ARB, tess_control_shader_patches_query);
    glBeginQuery(GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB, tess_evaluation_shader_invocations_query);
    glBeginQuery(GL_GEOMETRY_SHADER_INVOCATIONS, geometry_shader_invocations_query);
    glBeginQuery(GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB, geometry_primitives_emitted_query);
    glBeginQuery(GL_CLIPPING_INPUT_PRIMITIVES_ARB, clipping_input_primitives_query);
    glBeginQuery(GL_CLIPPING_OUTPUT_PRIMITIVES_ARB, clipping_output_primitives_query);
}

void GLQueryObjects::endQueries()
{
    glEndQuery(GL_PRIMITIVES_SUBMITTED_ARB);
    glEndQuery(GL_TESS_CONTROL_SHADER_PATCHES_ARB);
    glEndQuery(GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB);
    glEndQuery(GL_VERTICES_SUBMITTED_ARB);
    glEndQuery(GL_GEOMETRY_SHADER_INVOCATIONS);
    glEndQuery(GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB);
    glEndQuery(GL_CLIPPING_INPUT_PRIMITIVES_ARB);
    glEndQuery(GL_CLIPPING_OUTPUT_PRIMITIVES_ARB);
}

LinkRenderStats::LinkRenderStats()
    : links_rendered(0)
    , links_trivially_culled(0)
    , quads_generated(0)
{}

void LinkRenderStats::incrementLinksTriviallyCulled()
{
    links_trivially_culled++;
}

void LinkRenderStats::incrementLinksRendered()
{
    links_rendered++;
}

void LinkRenderStats::addQuadsGenerated(unsigned int quads)
{
    quads_generated += quads;
}

bool LinkRenderStats::operator ==(const LinkRenderStats& other) const
{
    if (links_rendered != other.links_rendered)
        return false;
    if (links_trivially_culled != other.links_trivially_culled)
        return false;
    if (quads_generated != other.quads_generated)
        return false;
    return true;
}

unsigned int LinkRenderStats::LinksRendered() const
{
    return links_rendered;
}

unsigned int LinkRenderStats::LinksTriviallyCulled() const
{
    return links_trivially_culled;
}

unsigned int LinkRenderStats::LinksEvaluated() const
{
    assert(links_trivially_culled <= links_rendered);
    return links_rendered - links_trivially_culled;
}

unsigned int LinkRenderStats::QuadsGenerated() const
{
    return quads_generated;
}

GLLinkRenderStats::GLLinkRenderStats(GLQueryObjects& objs)
    : gl_query_objects(objs)
    , gl_vertices_submitted(0)
    , gl_primitives_generated(0)
    , gl_tess_control_shader_patches(0)
    , gl_tess_evaluation_shader_invocations(0)
    , gl_geometry_shader_invocations(0)
    , gl_geometry_primitives_emitted(0)
    , gl_clipping_input_primitives(0)
    , gl_clipping_output_primitives(0)
{}

void GLLinkRenderStats::beginQueries()
{
    gl_query_objects.beginQueries();
}

void GLLinkRenderStats::endQueries()
{
    gl_query_objects.endQueries();
}

void GLLinkRenderStats::queryResults()
{
    glGetQueryObjectuiv(gl_query_objects.vertices_submitted_query, GL_QUERY_RESULT, &gl_vertices_submitted);
    glGetQueryObjectuiv(gl_query_objects.primitives_generated_query, GL_QUERY_RESULT, &gl_primitives_generated);
    glGetQueryObjectuiv(gl_query_objects.tess_control_shader_patches_query, GL_QUERY_RESULT, &gl_tess_control_shader_patches);
    glGetQueryObjectuiv(gl_query_objects.tess_evaluation_shader_invocations_query, GL_QUERY_RESULT, &gl_tess_evaluation_shader_invocations);
    glGetQueryObjectuiv(gl_query_objects.geometry_shader_invocations_query, GL_QUERY_RESULT, &gl_geometry_shader_invocations);
    glGetQueryObjectuiv(gl_query_objects.geometry_primitives_emitted_query, GL_QUERY_RESULT, &gl_geometry_primitives_emitted);
    glGetQueryObjectuiv(gl_query_objects.clipping_input_primitives_query, GL_QUERY_RESULT, &gl_clipping_input_primitives);
    glGetQueryObjectuiv(gl_query_objects.clipping_output_primitives_query, GL_QUERY_RESULT, &gl_clipping_output_primitives);
}

void GLLinkRenderStats::printResults() const
{
    printf("query results...\n");
    printf("  gl_vertices_submitted = %u\n", gl_vertices_submitted);
    printf("  gl_primitives_generated = %u\n", gl_primitives_generated);
    printf("  gl_tess_control_shader_patches = %u\n", gl_tess_control_shader_patches);
    printf("  gl_tess_evaluation_shader_invocations = %u\n", gl_tess_evaluation_shader_invocations);
    printf("  gl_geometry_shader_invocations = %u\n", gl_geometry_shader_invocations);
    printf("  gl_geometry_primitives_emitted = %u\n", gl_geometry_primitives_emitted);
    printf("  gl_clipping_input_primitives = %u\n", gl_clipping_input_primitives);
    printf("  gl_clipping_output_primitives = %u\n", gl_clipping_output_primitives);
}
