<commit_before>// renderer.cpp
// Copyright 2014 - 2019 <NAME>.
// License: https://github.com/polymonster/pmtech/blob/master/license.md

#define GL_SILENCE_DEPRECATION
#define GLES_SILENCE_DEPRECATION

#include "console.h"
#include "data_struct.h"
#include "hash.h"
#include "memory.h"
#include "pen.h"
#include "pen_string.h"
#include "renderer.h"
#include "renderer_shared.h"
#include "threads.h"
#include "timer.h"

#include "str/Str.h"

#include <stdlib.h>
#include <vector>

extern pen::window_creation_params pen_window;

// these are required for platform specific gl implementation calls.
extern void pen_make_gl_context_current();
extern void pen_gl_swap_buffers();

namespace
{
    u64   s_frame = 0;
    GLint s_backbuffer_fbo = -1;

#define GL_DEBUG_LEVEL 0

#if GL_DEBUG_LEVEL > 1
#define GL_ASSERT(V) PEN_ASSERT(V)
#else
#define GL_ASSERT(V) (void)V
#endif

    void gl_error_break(GLenum err)
    {
        switch (err)
        {
            case GL_INVALID_ENUM:
                printf("invalid enum\n");
                GL_ASSERT(0);
                break;
            case GL_INVALID_VALUE:
                printf("gl invalid value\n");
                GL_ASSERT(0);
                break;
            case GL_INVALID_OPERATION:
                printf("gl invalid operation\n");
                GL_ASSERT(0);
                break;
            case GL_INVALID_FRAMEBUFFER_OPERATION:
                printf("gl invalid frame buffer operation\n");
                GL_ASSERT(0);
                break;
            case GL_OUT_OF_MEMORY:
                printf("gl out of memory\n");
                GL_ASSERT(0);
                break;
            default:
                break;
        }
    }

#if GL_DEBUG_LEVEL > 2
#define CHECK_GL_ERROR                                                                                                       \
    {                                                                                                                        \
        GLenum err = glGetError();                                                                                           \
        if (err != GL_NO_ERROR)                                                                                              \
            gl_error_break(err);                                                                                             \
    }
#define CHECK_CALL(C)                                                                                                        \
    C;                                                                                                                       \
    CHECK_GL_ERROR;                                                                                                          \
    printf(#C "\n");
#elif GL_DEBUG_LEVEL > 0
#define CHECK_GL_ERROR                                                                                                       \
    {                                                                                                                        \
        GLenum err = glGetError();                                                                                           \
        if (err != GL_NO_ERROR)                                                                                              \
            gl_error_break(err);                                                                                             \
    }
#define CHECK_CALL(C)                                                                                                        \
    C;                                                                                                                       \
    CHECK_GL_ERROR;
#else
#define CHECK_GL_ERROR
#define CHECK_CALL(C) C
#endif

#ifdef PEN_GLES3
#define PEN_SET_BASE_VERTEX(BV) g_bound_state.base_vertex = BV
#else
#define PEN_SET_BASE_VERTEX(BV) g_bound_state.base_vertex = 0;
#endif

    u32 to_gl_shader_type(u32 pen_shader_type)
    {
        switch(pen_shader_type)
        {
            case PEN_SHADER_TYPE_VS:
                return GL_VERTEX_SHADER;
            case PEN_SHADER_TYPE_PS:
                return GL_FRAGMENT_SHADER;
            case PEN_SHADER_TYPE_GS:
                return GL_GEOMETRY_SHADER;
             case PEN_SHADER_TYPE_SO:
                return GL_VERTEX_SHADER;           
        }
        // unimplemented cs
        PEN_ASSERT(0);
        return 0;
    }

    u32 to_gl_polygon_mode(u32 pen_polygon_mode)
    {
        switch (pen_polygon_mode)
        {
            case PEN_FILL_SOLID:
                return GL_FILL;
            case PEN_FILL_WIREFRAME:
                return GL_LINE;
        }
        PEN_ASSERT(0);
        return GL_FILL;
    }

    u32 to_gl_cull_mode(u32 pen_cull_mode)
    {
        switch (pen_cull_mode)
        {
            case PEN_CULL_NONE:
                return 0;
            case PEN_CULL_FRONT:
                return GL_FRONT;
            case PEN_CULL_BACK:
                return GL_BACK;
        }
        PEN_ASSERT(0);
        return PEN_CULL_NONE;
    }

    u32 to_gl_clear_flags(u32 pen_clear_flags)
    {
        u32 res = 0;
        if(pen_clear_flags & PEN_CLEAR_COLOUR_BUFFER)
            res |= GL_COLOR_BUFFER_BIT;
        if(pen_clear_flags & PEN_CLEAR_DEPTH_BUFFER)
            res |= GL_DEPTH_BUFFER_BIT;
        if(pen_clear_flags & PEN_CLEAR_STENCIL_BUFFER)
            res |= GL_STENCIL_BUFFER_BIT;
        return res;
    }

    u32 to_gl_primitive_topology(u32 pen_primitive_topology)
    {
        switch (pen_primitive_topology)
        {
            case PEN_PT_POINTLIST:
                return GL_POINTS;
            case PEN_PT_LINELIST:
                return GL_LINES;
            case PEN_PT_LINESTRIP:
                return GL_LINE_STRIP;
            case PEN_PT_TRIANGLELIST:
                return GL_TRIANGLES;
            case PEN_PT_TRIANGLESTRIP:
                return GL_TRIANGLE_STRIP;
        }
        PEN_ASSERT(0);
        return GL_TRIANGLES;
    }

    u32 to_gl_index_format(u32 pen_index_format)
    {
        switch (pen_index_format)
        {
            case PEN_FORMAT_R16_UINT:
                return GL_UNSIGNED_SHORT;
            case PEN_FORMAT_R32_UINT:
                return GL_UNSIGNED_INT;
        }
        PEN_ASSERT(0);
        return GL_UNSIGNED_SHORT;
    }

    u32 to_gl_usage(u32 pen_usage)
    {
        switch (pen_usage)
        {
            case PEN_USAGE_DEFAULT:
                return GL_STATIC_DRAW;
            case PEN_USAGE_IMMUTABLE:
                return GL_STATIC_DRAW;
            case PEN_USAGE_DYNAMIC:
                return GL_DYNAMIC_DRAW;
            case PEN_USAGE_STAGING:
                return GL_DYNAMIC_DRAW;
        }
        PEN_ASSERT(0);
        return GL_STATIC_DRAW;
    }

    u32 to_gl_bind_flags(u32 pen_bind_flags)
    {
        switch (pen_bind_flags)
        {
            case PEN_BIND_SHADER_RESOURCE:
                return 0;
            case PEN_BIND_VERTEX_BUFFER:
                return GL_ARRAY_BUFFER;
            case PEN_BIND_INDEX_BUFFER:
                return GL_ELEMENT_ARRAY_BUFFER;
            case PEN_BIND_CONSTANT_BUFFER:
                return GL_UNIFORM_BUFFER;
            case PEN_BIND_RENDER_TARGET:
                return GL_FRAMEBUFFER;
            case PEN_BIND_DEPTH_STENCIL:
                return 1;
            case PEN_BIND_SHADER_WRITE:
                return 2;
            case PEN_STREAM_OUT_VERTEX_BUFFER:
                return GL_ARRAY_BUFFER;
        }
        PEN_ASSERT(0);
        return 0;
    }

    u32 to_gl_cpu_access_flags(u32 pen_access_flags)
    {
        switch (pen_access_flags)
        {
            case PEN_CPU_ACCESS_WRITE:
                return GL_MAP_WRITE_BIT;
            case PEN_CPU_ACCESS_READ:
                return GL_MAP_READ_BIT;
        }
        PEN_ASSERT(0);
        return 0;
    }

    u32 to_gl_texture_address_mode(u32 pen_texture_address_mode)
    {
        switch (pen_texture_address_mode)
        {
            case PEN_TEXTURE_ADDRESS_WRAP:
                return GL_REPEAT;
            case PEN_TEXTURE_ADDRESS_MIRROR:
                return GL_MIRRORED_REPEAT;
            case PEN_TEXTURE_ADDRESS_CLAMP:
                return GL_CLAMP_TO_EDGE;
            case PEN_TEXTURE_ADDRESS_BORDER:
                return GL_CLAMP_TO_BORDER;
            case PEN_TEXTURE_ADDRESS_MIRROR_ONCE:
            #if GL_EXT_texture_mirror_clamp
                return GL_MIRROR_CLAMP_EXT;
            #else
                return GL_MIRRORED_REPEAT;
            #endif
        }
        PEN_ASSERT(0);
        return GL_REPEAT;
    }

    void to_gl_filter_mode(u32 pen_filter_mode, u32* min_filter, u32* mag_filter)
    {
        switch (pen_filter_mode)
        {
            case PEN_FILTER_MIN_MAG_MIP_LINEAR:
            {
                *min_filter = GL_LINEAR_MIPMAP_LINEAR;
                *mag_filter = GL_LINEAR;
            }
            return;
            case PEN_FILTER_MIN_MAG_MIP_POINT:
            {
                *min_filter = GL_NEAREST_MIPMAP_NEAREST;
                *mag_filter = GL_NEAREST;

            }
            return;
            case PEN_FILTER_LINEAR:
            {
                *min_filter = GL_LINEAR;
                *mag_filter = GL_LINEAR;
            }
            return;
            case PEN_FILTER_POINT:
            {
                *min_filter = GL_NEAREST;
                *mag_filter = GL_NEAREST;
            }
            return;
        }
        PEN_ASSERT(0);
        *min_filter = GL_LINEAR;
        *mag_filter = GL_LINEAR;
    }

    u32 to_gl_comparison(u32 pen_comparison)
    {
        switch (pen_comparison)
        {
            case PEN_COMPARISON_NEVER:
                return GL_NEVER;
            case PEN_COMPARISON_LESS:
                return GL_LESS;
            case PEN_COMPARISON_EQUAL:
                return GL_EQUAL;
            case PEN_COMPARISON_LESS_EQUAL:
                return GL_LEQUAL;
            case PEN_COMPARISON_GREATER:
                return GL_GREATER;
            case PEN_COMPARISON_NOT_EQUAL:
                return GL_NOTEQUAL;
            case PEN_COMPARISON_GREATER_EQUAL:
                return GL_GEQUAL;
            case PEN_COMPARISON_ALWAYS:
                return GL_ALWAYS;
        }
        PEN_ASSERT(0);
        return GL_ALWAYS;
    }

    struct tex_format_map
    {
        u32 pen_format;
        u32 sized_format;
        u32 format;
        u32 type;
    };

    const tex_format_map k_tex_format_map[] = {
        {PEN_TEX_FORMAT_BGRA8_UNORM, GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE},
        {PEN_TEX_FORMAT_RGBA8_UNORM, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE},
        {PEN_TEX_FORMAT_D24_UNORM_S8_UINT, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8},
        {PEN_TEX_FORMAT_R32G32B32A32_FLOAT, GL_RGBA32F, GL_RGBA, GL_FLOAT},
        {PEN_TEX_FORMAT_R16G16B16A16_FLOAT, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT},
        {PEN_TEX_FORMAT_R32G32_FLOAT, GL_RG32F, GL_RG, GL_FLOAT},
        {PEN_TEX_FORMAT_R32_FLOAT, GL_R32F, GL_RED, GL_FLOAT},
        {PEN_TEX_FORMAT_R16_FLOAT, GL_R16F, GL_RED, GL_HALF_FLOAT},
        {PEN_TEX_FORMAT_R32_UINT, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT},
        {PEN_TEX_FORMAT_R8_UNORM, GL_R8, GL_RED, GL_UNSIGNED_BYTE},
        {PEN_TEX_FORMAT_BC1_UNORM, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_TEXTURE_COMPRESSED},
        {PEN_TEX_FORMAT_BC2_UNORM, 0, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_TEXTURE_COMPRESSED},
        {PEN_TEX_FORMAT_BC3_UNORM, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_TEXTURE_COMPRESSED}
    };
    const u32 k_num_tex_maps = sizeof(k_tex_format_map) / sizeof(k_tex_format_map[0]);

    void to_gl_texture_format(u32 pen_format, u32& sized_format, u32& format, u32& type)
    {
        for (s32 i = 0; i < k_num_tex_maps; ++i)
        {
            if (k_tex_format_map[i].pen_format == pen_format)
            {
                sized_format = k_tex_format_map[i].sized_format;
                format = k_tex_format_map[i].format;
                type = k_tex_format_map[i].type;
                return;
            }
        }
        // unsupported / unhandled texture format
        PEN_ASSERT(0);
    }

    struct vertex_format_map
    {
        u32 pen_format;
        u32 format;
        u32 num_elements;
    };

    const vertex_format_map k_vertex_format_map[] = {
        {PEN_VERTEX_FORMAT_FLOAT1, GL_FLOAT, 1},
        {PEN_VERTEX_FORMAT_FLOAT2, GL_FLOAT, 2},
        {PEN_VERTEX_FORMAT_FLOAT3, GL_FLOAT, 3},
        {PEN_VERTEX_FORMAT_FLOAT4, GL_FLOAT, 4},
        {PEN_VERTEX_FORMAT_UNORM1, GL_UNSIGNED_BYTE, 1},
        {PEN_VERTEX_FORMAT_UNORM2, GL_UNSIGNED_BYTE, 2},
        {PEN_VERTEX_FORMAT_UNORM4, GL_UNSIGNED_BYTE, 4}
    };
    const u32 k_num_vertex_format_maps = sizeof(k_vertex_format_map) / sizeof(k_vertex_format_map[0]);

    vertex_format_map to_gl_vertex_format(u32 pen_format)
    {
        for (s32 i = 0; i < k_num_vertex_format_maps; ++i)
            if (k_vertex_format_map[i].pen_format == pen_format)
                return k_vertex_format_map[i];
        PEN_ASSERT(0);
        return { 0 };
    }

} // namespace

namespace pen
{
    a_u64 g_gpu_total;

    struct gpu_perf_result
    {
        u64 elapsed;
        u32 depth;
        u64 frame;
        Str name;
    };
    static gpu_perf_result* s_perf_results = nullptr;

    struct perf_marker
    {
        u32       query = 0;
        u64       frame = 0;
        const c8* name = nullptr;
        bool      issued = false;
        u32       depth = 0;
        bool      pad = false;
        GLuint64  result = 0;
    };

    struct perf_marker_set
    {
        static const u32 num_marker_buffers = 2;
        perf_marker*     markers[num_marker_buffers] = {0};
        u32              pos[num_marker_buffers] = {0};

        u32 buf = 0;
        u32 depth = 0;
    };
    static perf_marker_set s_perf;

    void insert_marker(const c8* name, bool pad = false)
    {
#ifdef GL_TIME_ELAPSED
        u32& buf = s_perf.buf;
        u32& pos = s_perf.pos[buf];
        u32& depth = s_perf.depth;

        if (pos >= sb_count(s_perf.markers[buf]))
        {
            // push a new marker
            perf_marker m;
            glGenQueries(1, &m.query);

            sb_push(s_perf.markers[buf], m);
        }

        // queries have taken longer than 1 frame to obtain results
        // increase num_marker_buffers to avoid losing data
        PEN_ASSERT(!s_perf.markers[buf][pos].issued);

        CHECK_CALL(glBeginQuery(GL_TIME_ELAPSED, s_perf.markers[buf][pos].query));
        s_perf.markers[buf][pos].issued = true;
        s_perf.markers[buf][pos].depth = depth;
        s_perf.markers[buf][pos].frame = s_frame;
        s_perf.markers[buf][pos].pad = pad;
        s_perf.markers[buf][pos].name = name;

        ++pos;
#endif
    }

    void direct::renderer_push_perf_marker(const c8* name)
    {
#ifdef GL_TIME_ELAPSED
        u32& depth = s_perf.depth;

        if (depth > 0)
        {
            CHECK_CALL(glEndQuery(GL_TIME_ELAPSED));
        }

        insert_marker(name);

        ++depth;
#endif
    }

    void direct::renderer_pop_perf_marker()
    {
#ifdef GL_TIME_ELAPSED
        u32& depth = s_perf.depth;
        --depth;

        CHECK_CALL(glEndQuery(GL_TIME_ELAPSED));

        if (depth > 0)
        {
            insert_marker("pad_marker", true);
        }
#endif
    }

    void gather_perf_markers()
    {
#ifdef GL_TIME_ELAPSED
        // unbalance push pop in perf markers
        PEN_ASSERT(s_perf.depth == 0);

        // swap buffers
        u32 bb = (s_perf.buf + 1) % s_perf.num_marker_buffers;

        u32 complete_count = 0;
        for (u32 i = 0; i < s_perf.pos[bb]; ++i)
        {
            perf_marker& m = s_perf.markers[bb][i];
            if (m.issued)
            {
                s32 avail = 0;
                CHECK_CALL(glGetQueryObjectiv(m.query, GL_QUERY_RESULT_AVAILABLE, &avail));

                if (avail)
                {
                    complete_count++;
                    CHECK_CALL(glGetQueryObjectui64v(m.query, GL_QUERY_RESULT, &m.result));
                }
            }
        }

        if (complete_count == s_perf.pos[bb])
        {
            // gather results into a better view
            sb_free(s_perf_results);

            u32 num_timers = s_perf.pos[bb];
            for (u32 i = 0; i < num_timers; ++i)
            {
                perf_marker& m = s_perf.markers[bb][i];

                if (!m.issued)
                    continue;

                // ready for the next frame
                m.issued = false;

                if (m.pad)
                    continue;

                gpu_perf_result p;
                p.name = m.name;
                p.depth = m.depth;
                p.frame = m.frame;

                if (m.name)
                {
                    // release mem that was allocated by the command buffer
                    memory_free((void*)m.name);
                    m.name = nullptr;
                }

                // gather up times from nested calls
                p.elapsed = 0;
                u32 nest_iter = 0;
                for (;;)
                {
                    perf_marker& n = s_perf.markers[bb][i + nest_iter];

                    if (n.depth < p.depth)
                        break;

                    if (n.pad && p.depth == n.depth)
                        break;

                    p.elapsed += n.result;

                    ++nest_iter;
                    if (nest_iter >= num_timers)
                        break;
                }

                Str desc;
                for (u32 j = 0; j < p.depth; ++j)
                    desc.append('\t');

                desc.append(p.name.c_str());

                desc.append(" : ");
                desc.appendf("%llu", p.elapsed);

                if (i == 0)
                    g_gpu_total = p.elapsed;
            }

            s_perf.pos[bb] = 0;
        }

        s_perf.buf = bb;
#endif
    }

//--------------------------------------------------------------------------------------
//  COMMON API
//--------------------------------------------------------------------------------------
#define MAX_VERTEX_BUFFERS 4
#define MAX_VERTEX_ATTRIBUTES 16
#define MAX_UNIFORM_BUFFERS 32
#define MAX_SHADER_TEXTURES 32

    struct context_state
    {
        context_state()
        {
        }

        u32 backbuffer_colour;
        u32 backbuffer_depth;

        u32 active_colour_target;
        u32 active_depth_target;
    };

    struct clear_state_internal
    {
        f32 rgba[4];
        f32 depth;
        u8  stencil;
        u32 flags;

        mrt_clear mrt[MAX_MRT];
        u32       num_colour_targets;
    };

    struct vertex_attribute
    {
        u32    location;
        u32    type;
        u32    stride;
        size_t offset;
        u32    num_elements;
        u32    input_slot;
        u32    step_rate;
    };

    struct input_layout
    {
        vertex_attribute* attributes;
        GLuint            vertex_array_handle = 0;
        GLuint            vb_handle = 0;
    };

    struct raster_state
    {
        GLenum cull_face;
        GLenum polygon_mode;
        bool   front_ccw;
        bool   culling_enabled;
        bool   depth_clip_enabled;
        bool   scissor_enabled;
    };

    struct texture_info
    {
        GLuint handle;
        u32    max_mip_level;
        u32    target;
    };

    struct render_target
    {
        texture_info             texture;
        texture_info             texture_msaa;
        GLuint                   w, h;
        u32                      uid;
        u32                      collection_type;
        texture_creation_params* tcp;
        u32                      invalidate;
    };

    struct framebuffer
    {
        hash_id hash;
        GLuint  _framebuffer;
    };
    static framebuffer* s_framebuffers;

    enum resource_type : s32
    {
        RES_TEXTURE = 0,
        RES_TEXTURE_3D,
        RES_RENDER_TARGET,
        RES_RENDER_TARGET_MSAA
    };

#define INVALID_LOC 255
    struct shader_program
    {
        u32    vs;
        u32    ps;
        u32    gs;
        u32    so;
        GLuint program;
        GLuint vflip_uniform;
        u8     uniform_block_location[MAX_UNIFORM_BUFFERS];
        u8     texture_location[MAX_SHADER_TEXTURES];
    };
    static shader_program* s_shader_programs;

    struct gl_sampler : public sampler_creation_params
    {
        u32 min_filter;
        u32 mag_filter;
    };

    struct resource_allocation
    {
        u8     asigned_flag;
        GLuint type;
        union {
            clear_state_internal           clear_state;
            pen::input_layout*             input_layout;
            pen::raster_state              raster_state;
            depth_stencil_creation_params* depth_stencil;
            blend_creation_params*         blend_state;
            GLuint                         handle;
            texture_info                   texture;
            pen::render_target             render_target;
            gl_sampler*                    sampler_state;
            pen::shader_program*           shader_program;
        };
    };
    static res_pool<resource_allocation> _res_pool;

    struct active_state
    {
        u32  vertex_buffer[MAX_VERTEX_BUFFERS] = {0};
        u32  vertex_buffer_stride[MAX_VERTEX_BUFFERS] = {0};
        u32  vertex_buffer_offset[MAX_VERTEX_BUFFERS] = {0};
        u32  num_bound_vertex_buffers = 0;
        u32  index_buffer = 0;
        u32  stream_out_buffer = 0;
        u32  input_layout = 0;
        u32  vertex_shader = 0;
        u32  pixel_shader = 0;
        u32  stream_out_shader = 0;
        u32  raster_state = 0;
        u32  base_vertex = 0;
        bool backbuffer_bound = false;
        bool v_flip = false;
        u8   constant_buffer_bindings[MAX_UNIFORM_BUFFERS] = {0};
        u32  index_format = GL_UNSIGNED_SHORT;
        u32  depth_stencil_state;
        u8   stencil_ref;
    };

    active_state g_bound_state;
    active_state g_current_state;
    viewport     g_current_vp;

    void clear_resource_table()
    {
        // reserve resource 0 for NULL binding.
        _res_pool[0].asigned_flag |= 0xff;
    }

    context_state g_context;

    void _set_depth_stencil_state(u32 dss, u8 ref)
    {
        resource_allocation& res = _res_pool[dss];

        // depth
        if (res.depth_stencil->depth_enable)
        {
            CHECK_CALL(glEnable(GL_DEPTH_TEST));
        }
        else
        {
            CHECK_CALL(glDisable(GL_DEPTH_TEST));
        }

        CHECK_CALL(glDepthFunc(res.depth_stencil->depth_func));
        CHECK_CALL(glDepthMask(res.depth_stencil->depth_write_mask));

        // stencil
        if (res.depth_stencil->stencil_enable)
        {
            CHECK_CALL(glEnable(GL_STENCIL_TEST));
            CHECK_CALL(glStencilMask(res.depth_stencil->stencil_write_mask));

            // front
            CHECK_CALL(glStencilOpSeparate(GL_FRONT, res.depth_stencil->front_face.stencil_failop,
                                           res.depth_stencil->front_face.stencil_depth_failop,
                                           res.depth_stencil->front_face.stencil_passop));

            CHECK_CALL(glStencilFuncSeparate(GL_FRONT, res.depth_stencil->front_face.stencil_func, ref,
                                             res.depth_stencil->stencil_read_mask));

            // back
            CHECK_CALL(glStencilOpSeparate(GL_BACK, res.depth_stencil->back_face.stencil_failop,
                                           res.depth_stencil->back_face.stencil_depth_failop,
                                           res.depth_stencil->back_face.stencil_passop));

            CHECK_CALL(glStencilFuncSeparate(GL_BACK, res.depth_stencil->back_face.stencil_func, ref,
                                             res.depth_stencil->stencil_read_mask));
        }
        else
        {
            CHECK_CALL(glDisable(GL_STENCIL_TEST));
        }
    }

    void direct::renderer_create_clear_state(const clear_state& cs, u32 resource_slot)
    {
        _res_pool.grow(resource_slot);

        _res_pool[resource_slot].clear_state.rgba[0] = cs.r;
        _res_pool[resource_slot].clear_state.rgba[1] = cs.g;
        _res_pool[resource_slot].clear_state.rgba[2] = cs.b;
        _res_pool[resource_slot].clear_state.rgba[3] = cs.a;
        _res_pool[resource_slot].clear_state.depth = cs.depth;
        _res_pool[resource_slot].clear_state.stencil = cs.stencil;
        _res_pool[resource_slot].clear_state.flags = to_gl_clear_flags(cs.flags);

        _res_pool[resource_slot].clear_state.num_colour_targets = cs.num_colour_targets;
        memcpy(&_res_pool[resource_slot].clear_state.mrt, cs.mrt, sizeof(mrt_clear) * MAX_MRT);
    }

    u32 link_program_internal(u32 vs, u32 ps, const shader_link_params* params = nullptr)
    {
        // link the shaders
        GLuint program_id = CHECK_CALL(glCreateProgram());

        GLuint so = 0;

        if (params)
        {
            // this path is for a proper link with reflection info
            vs = _res_pool[params->vertex_shader].handle;
            ps = _res_pool[params->pixel_shader].handle;
            so = _res_pool[params->stream_out_shader].handle;

            if (vs)
            {
                CHECK_CALL(glAttachShader(program_id, vs));
            }

            if (ps)
            {
                CHECK_CALL(glAttachShader(program_id, ps));
            }

            if (so)
            {
                CHECK_CALL(glAttachShader(program_id, so));
                CHECK_CALL(glTransformFeedbackVaryings(program_id, params->num_stream_out_names,
                                                       (const c8**)params->stream_out_names, GL_INTERLEAVED_ATTRIBS));
            }
        }
        else
        {
            // on the fly link for bound vs and ps which have not been explicity linked
            // to emulate d3d behaviour of set vs set ps etc
            CHECK_CALL(glAttachShader(program_id, vs));
            CHECK_CALL(glAttachShader(program_id, ps));
        }

        CHECK_CALL(glLinkProgram(program_id));

        // Check the program
        GLint result = GL_FALSE;
        int   info_log_length = 0;

        CHECK_CALL(glGetProgramiv(program_id, GL_LINK_STATUS, &result));
        CHECK_CALL(glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_log_length));

        if (result == GL_FALSE && info_log_length > 0)
        {
            char* info_log_buf = (char*)memory_alloc(info_log_length + 1);

            CHECK_CALL(glGetShaderInfoLog(program_id, info_log_length, NULL, &info_log_buf[0]));
            info_log_buf[info_log_length] = '\0';

            output_debug("%s", info_log_buf);
            memory_free(info_log_buf);
        }

        shader_program program;
        program.vs = vs;
        program.ps = ps;
        program.so = so;
        program.program = program_id;
        program.vflip_uniform = glGetUniformLocation(program.program, "v_flip");

        for (s32 i = 0; i < MAX_UNIFORM_BUFFERS; ++i)
        {
            program.texture_location[i] = INVALID_LOC;
            program.uniform_block_location[i] = INVALID_LOC;
        }

        sb_push(s_shader_programs, program);

        return sb_count(s_shader_programs) - 1;
    }

    void direct::renderer_sync()
    {
        // unused for opengl, required to sync for metal
    }

    void direct::renderer_new_frame()
    {
        pen_make_gl_context_current();
        _renderer_new_frame();

        // if we resize render targets, delete frame buffers to invalidate and recreate them
        static u32 rs = 0;
        if (_renderer_resize_index() != rs)
        {
            rs = _renderer_resize_index();
            sb_free(s_framebuffers);
            s_framebuffers = nullptr;
        }
    }

    void direct::renderer_end_frame()
    {
        // unused on this platform
    }

    void direct::renderer_clear(u32 clear_state_index, u32 colour_face, u32 depth_face)
    {
        resource_allocation&  rc = _res_pool[clear_state_index];
        clear_state_internal& cs = rc.clear_state;

        CHECK_CALL(glClearDepth(cs.depth));
        CHECK_CALL(glClearStencil(cs.stencil));

        if(rc.clear_state.flags & GL_DEPTH_BUFFER_BIT)
        {
            // we must enable depth writes when clearing..
            CHECK_CALL(glEnable(GL_DEPTH_TEST));
            CHECK_CALL(glDepthMask(true));
        }

        if(rc.clear_state.flags & GL_STENCIL_BUFFER)
        {
            CHECK_CALL(glEnable(GL_STENCIL));
            CHECK_CALL(glStencilMask(0xff));
        }

        if (cs.num_colour_targets == 0)
        {
            CHECK_CALL(
                glClearColor(rc.clear_state.rgba[0], rc.clear_state.rgba[1], rc.clear_state.rgba[2], rc.clear_state.rgba[3]));

            CHECK_CALL(glClear(rc.clear_state.flags));

            return;
        }<commit_msg>- compile fix<commit_after>// renderer.cpp
// Copyright 2014 - 2019 <NAME>.
// License: https://github.com/polymonster/pmtech/blob/master/license.md

#define GL_SILENCE_DEPRECATION
#define GLES_SILENCE_DEPRECATION

#include "console.h"
#include "data_struct.h"
#include "hash.h"
#include "memory.h"
#include "pen.h"
#include "pen_string.h"
#include "renderer.h"
#include "renderer_shared.h"
#include "threads.h"
#include "timer.h"

#include "str/Str.h"

#include <stdlib.h>
#include <vector>

extern pen::window_creation_params pen_window;

// these are required for platform specific gl implementation calls.
extern void pen_make_gl_context_current();
extern void pen_gl_swap_buffers();

namespace
{
    u64   s_frame = 0;
    GLint s_backbuffer_fbo = -1;

#define GL_DEBUG_LEVEL 0

#if GL_DEBUG_LEVEL > 1
#define GL_ASSERT(V) PEN_ASSERT(V)
#else
#define GL_ASSERT(V) (void)V
#endif

    void gl_error_break(GLenum err)
    {
        switch (err)
        {
            case GL_INVALID_ENUM:
                printf("invalid enum\n");
                GL_ASSERT(0);
                break;
            case GL_INVALID_VALUE:
                printf("gl invalid value\n");
                GL_ASSERT(0);
                break;
            case GL_INVALID_OPERATION:
                printf("gl invalid operation\n");
                GL_ASSERT(0);
                break;
            case GL_INVALID_FRAMEBUFFER_OPERATION:
                printf("gl invalid frame buffer operation\n");
                GL_ASSERT(0);
                break;
            case GL_OUT_OF_MEMORY:
                printf("gl out of memory\n");
                GL_ASSERT(0);
                break;
            default:
                break;
        }
    }

#if GL_DEBUG_LEVEL > 2
#define CHECK_GL_ERROR                                                                                                       \
    {                                                                                                                        \
        GLenum err = glGetError();                                                                                           \
        if (err != GL_NO_ERROR)                                                                                              \
            gl_error_break(err);                                                                                             \
    }
#define CHECK_CALL(C)                                                                                                        \
    C;                                                                                                                       \
    CHECK_GL_ERROR;                                                                                                          \
    printf(#C "\n");
#elif GL_DEBUG_LEVEL > 0
#define CHECK_GL_ERROR                                                                                                       \
    {                                                                                                                        \
        GLenum err = glGetError();                                                                                           \
        if (err != GL_NO_ERROR)                                                                                              \
            gl_error_break(err);                                                                                             \
    }
#define CHECK_CALL(C)                                                                                                        \
    C;                                                                                                                       \
    CHECK_GL_ERROR;
#else
#define CHECK_GL_ERROR
#define CHECK_CALL(C) C
#endif

#ifdef PEN_GLES3
#define PEN_SET_BASE_VERTEX(BV) g_bound_state.base_vertex = BV
#else
#define PEN_SET_BASE_VERTEX(BV) g_bound_state.base_vertex = 0;
#endif

    u32 to_gl_shader_type(u32 pen_shader_type)
    {
        switch(pen_shader_type)
        {
            case PEN_SHADER_TYPE_VS:
                return GL_VERTEX_SHADER;
            case PEN_SHADER_TYPE_PS:
                return GL_FRAGMENT_SHADER;
            case PEN_SHADER_TYPE_GS:
                return GL_GEOMETRY_SHADER;
             case PEN_SHADER_TYPE_SO:
                return GL_VERTEX_SHADER;           
        }
        // unimplemented cs
        PEN_ASSERT(0);
        return 0;
    }

    u32 to_gl_polygon_mode(u32 pen_polygon_mode)
    {
        switch (pen_polygon_mode)
        {
            case PEN_FILL_SOLID:
                return GL_FILL;
            case PEN_FILL_WIREFRAME:
                return GL_LINE;
        }
        PEN_ASSERT(0);
        return GL_FILL;
    }

    u32 to_gl_cull_mode(u32 pen_cull_mode)
    {
        switch (pen_cull_mode)
        {
            case PEN_CULL_NONE:
                return 0;
            case PEN_CULL_FRONT:
                return GL_FRONT;
            case PEN_CULL_BACK:
                return GL_BACK;
        }
        PEN_ASSERT(0);
        return PEN_CULL_NONE;
    }

    u32 to_gl_clear_flags(u32 pen_clear_flags)
    {
        u32 res = 0;
        if(pen_clear_flags & PEN_CLEAR_COLOUR_BUFFER)
            res |= GL_COLOR_BUFFER_BIT;
        if(pen_clear_flags & PEN_CLEAR_DEPTH_BUFFER)
            res |= GL_DEPTH_BUFFER_BIT;
        if(pen_clear_flags & PEN_CLEAR_STENCIL_BUFFER)
            res |= GL_STENCIL_BUFFER_BIT;
        return res;
    }

    u32 to_gl_primitive_topology(u32 pen_primitive_topology)
    {
        switch (pen_primitive_topology)
        {
            case PEN_PT_POINTLIST:
                return GL_POINTS;
            case PEN_PT_LINELIST:
                return GL_LINES;
            case PEN_PT_LINESTRIP:
                return GL_LINE_STRIP;
            case PEN_PT_TRIANGLELIST:
                return GL_TRIANGLES;
            case PEN_PT_TRIANGLESTRIP:
                return GL_TRIANGLE_STRIP;
        }
        PEN_ASSERT(0);
        return GL_TRIANGLES;
    }

    u32 to_gl_index_format(u32 pen_index_format)
    {
        switch (pen_index_format)
        {
            case PEN_FORMAT_R16_UINT:
                return GL_UNSIGNED_SHORT;
            case PEN_FORMAT_R32_UINT:
                return GL_UNSIGNED_INT;
        }
        PEN_ASSERT(0);
        return GL_UNSIGNED_SHORT;
    }

    u32 to_gl_usage(u32 pen_usage)
    {
        switch (pen_usage)
        {
            case PEN_USAGE_DEFAULT:
                return GL_STATIC_DRAW;
            case PEN_USAGE_IMMUTABLE:
                return GL_STATIC_DRAW;
            case PEN_USAGE_DYNAMIC:
                return GL_DYNAMIC_DRAW;
            case PEN_USAGE_STAGING:
                return GL_DYNAMIC_DRAW;
        }
        PEN_ASSERT(0);
        return GL_STATIC_DRAW;
    }

    u32 to_gl_bind_flags(u32 pen_bind_flags)
    {
        switch (pen_bind_flags)
        {
            case PEN_BIND_SHADER_RESOURCE:
                return 0;
            case PEN_BIND_VERTEX_BUFFER:
                return GL_ARRAY_BUFFER;
            case PEN_BIND_INDEX_BUFFER:
                return GL_ELEMENT_ARRAY_BUFFER;
            case PEN_BIND_CONSTANT_BUFFER:
                return GL_UNIFORM_BUFFER;
            case PEN_BIND_RENDER_TARGET:
                return GL_FRAMEBUFFER;
            case PEN_BIND_DEPTH_STENCIL:
                return 1;
            case PEN_BIND_SHADER_WRITE:
                return 2;
            case PEN_STREAM_OUT_VERTEX_BUFFER:
                return GL_ARRAY_BUFFER;
        }
        PEN_ASSERT(0);
        return 0;
    }

    u32 to_gl_cpu_access_flags(u32 pen_access_flags)
    {
        switch (pen_access_flags)
        {
            case PEN_CPU_ACCESS_WRITE:
                return GL_MAP_WRITE_BIT;
            case PEN_CPU_ACCESS_READ:
                return GL_MAP_READ_BIT;
        }
        PEN_ASSERT(0);
        return 0;
    }

    u32 to_gl_texture_address_mode(u32 pen_texture_address_mode)
    {
        switch (pen_texture_address_mode)
        {
            case PEN_TEXTURE_ADDRESS_WRAP:
                return GL_REPEAT;
            case PEN_TEXTURE_ADDRESS_MIRROR:
                return GL_MIRRORED_REPEAT;
            case PEN_TEXTURE_ADDRESS_CLAMP:
                return GL_CLAMP_TO_EDGE;
            case PEN_TEXTURE_ADDRESS_BORDER:
                return GL_CLAMP_TO_BORDER;
            case PEN_TEXTURE_ADDRESS_MIRROR_ONCE:
            #if GL_EXT_texture_mirror_clamp
                return GL_MIRROR_CLAMP_EXT;
            #else
                return GL_MIRRORED_REPEAT;
            #endif
        }
        PEN_ASSERT(0);
        return GL_REPEAT;
    }

    void to_gl_filter_mode(u32 pen_filter_mode, u32* min_filter, u32* mag_filter)
    {
        switch (pen_filter_mode)
        {
            case PEN_FILTER_MIN_MAG_MIP_LINEAR:
            {
                *min_filter = GL_LINEAR_MIPMAP_LINEAR;
                *mag_filter = GL_LINEAR;
            }
            return;
            case PEN_FILTER_MIN_MAG_MIP_POINT:
            {
                *min_filter = GL_NEAREST_MIPMAP_NEAREST;
                *mag_filter = GL_NEAREST;

            }
            return;
            case PEN_FILTER_LINEAR:
            {
                *min_filter = GL_LINEAR;
                *mag_filter = GL_LINEAR;
            }
            return;
            case PEN_FILTER_POINT:
            {
                *min_filter = GL_NEAREST;
                *mag_filter = GL_NEAREST;
            }
            return;
        }
        PEN_ASSERT(0);
        *min_filter = GL_LINEAR;
        *mag_filter = GL_LINEAR;
    }

    u32 to_gl_comparison(u32 pen_comparison)
    {
        switch (pen_comparison)
        {
            case PEN_COMPARISON_NEVER:
                return GL_NEVER;
            case PEN_COMPARISON_LESS:
                return GL_LESS;
            case PEN_COMPARISON_EQUAL:
                return GL_EQUAL;
            case PEN_COMPARISON_LESS_EQUAL:
                return GL_LEQUAL;
            case PEN_COMPARISON_GREATER:
                return GL_GREATER;
            case PEN_COMPARISON_NOT_EQUAL:
                return GL_NOTEQUAL;
            case PEN_COMPARISON_GREATER_EQUAL:
                return GL_GEQUAL;
            case PEN_COMPARISON_ALWAYS:
                return GL_ALWAYS;
        }
        PEN_ASSERT(0);
        return GL_ALWAYS;
    }

    struct tex_format_map
    {
        u32 pen_format;
        u32 sized_format;
        u32 format;
        u32 type;
    };

    const tex_format_map k_tex_format_map[] = {
        {PEN_TEX_FORMAT_BGRA8_UNORM, GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE},
        {PEN_TEX_FORMAT_RGBA8_UNORM, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE},
        {PEN_TEX_FORMAT_D24_UNORM_S8_UINT, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8},
        {PEN_TEX_FORMAT_R32G32B32A32_FLOAT, GL_RGBA32F, GL_RGBA, GL_FLOAT},
        {PEN_TEX_FORMAT_R16G16B16A16_FLOAT, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT},
        {PEN_TEX_FORMAT_R32G32_FLOAT, GL_RG32F, GL_RG, GL_FLOAT},
        {PEN_TEX_FORMAT_R32_FLOAT, GL_R32F, GL_RED, GL_FLOAT},
        {PEN_TEX_FORMAT_R16_FLOAT, GL_R16F, GL_RED, GL_HALF_FLOAT},
        {PEN_TEX_FORMAT_R32_UINT, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT},
        {PEN_TEX_FORMAT_R8_UNORM, GL_R8, GL_RED, GL_UNSIGNED_BYTE},
        {PEN_TEX_FORMAT_BC1_UNORM, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_TEXTURE_COMPRESSED},
        {PEN_TEX_FORMAT_BC2_UNORM, 0, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_TEXTURE_COMPRESSED},
        {PEN_TEX_FORMAT_BC3_UNORM, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_TEXTURE_COMPRESSED}
    };
    const u32 k_num_tex_maps = sizeof(k_tex_format_map) / sizeof(k_tex_format_map[0]);

    void to_gl_texture_format(u32 pen_format, u32& sized_format, u32& format, u32& type)
    {
        for (s32 i = 0; i < k_num_tex_maps; ++i)
        {
            if (k_tex_format_map[i].pen_format == pen_format)
            {
                sized_format = k_tex_format_map[i].sized_format;
                format = k_tex_format_map[i].format;
                type = k_tex_format_map[i].type;
                return;
            }
        }
        // unsupported / unhandled texture format
        PEN_ASSERT(0);
    }

    struct vertex_format_map
    {
        u32 pen_format;
        u32 format;
        u32 num_elements;
    };

    const vertex_format_map k_vertex_format_map[] = {
        {PEN_VERTEX_FORMAT_FLOAT1, GL_FLOAT, 1},
        {PEN_VERTEX_FORMAT_FLOAT2, GL_FLOAT, 2},
        {PEN_VERTEX_FORMAT_FLOAT3, GL_FLOAT, 3},
        {PEN_VERTEX_FORMAT_FLOAT4, GL_FLOAT, 4},
        {PEN_VERTEX_FORMAT_UNORM1, GL_UNSIGNED_BYTE, 1},
        {PEN_VERTEX_FORMAT_UNORM2, GL_UNSIGNED_BYTE, 2},
        {PEN_VERTEX_FORMAT_UNORM4, GL_UNSIGNED_BYTE, 4}
    };
    const u32 k_num_vertex_format_maps = sizeof(k_vertex_format_map) / sizeof(k_vertex_format_map[0]);

    vertex_format_map to_gl_vertex_format(u32 pen_format)
    {
        for (s32 i = 0; i < k_num_vertex_format_maps; ++i)
            if (k_vertex_format_map[i].pen_format == pen_format)
                return k_vertex_format_map[i];
        PEN_ASSERT(0);
        return { 0 };
    }

} // namespace

namespace pen
{
    a_u64 g_gpu_total;

    struct gpu_perf_result
    {
        u64 elapsed;
        u32 depth;
        u64 frame;
        Str name;
    };
    static gpu_perf_result* s_perf_results = nullptr;

    struct perf_marker
    {
        u32       query = 0;
        u64       frame = 0;
        const c8* name = nullptr;
        bool      issued = false;
        u32       depth = 0;
        bool      pad = false;
        GLuint64  result = 0;
    };

    struct perf_marker_set
    {
        static const u32 num_marker_buffers = 2;
        perf_marker*     markers[num_marker_buffers] = {0};
        u32              pos[num_marker_buffers] = {0};

        u32 buf = 0;
        u32 depth = 0;
    };
    static perf_marker_set s_perf;

    void insert_marker(const c8* name, bool pad = false)
    {
#ifdef GL_TIME_ELAPSED
        u32& buf = s_perf.buf;
        u32& pos = s_perf.pos[buf];
        u32& depth = s_perf.depth;

        if (pos >= sb_count(s_perf.markers[buf]))
        {
            // push a new marker
            perf_marker m;
            glGenQueries(1, &m.query);

            sb_push(s_perf.markers[buf], m);
        }

        // queries have taken longer than 1 frame to obtain results
        // increase num_marker_buffers to avoid losing data
        PEN_ASSERT(!s_perf.markers[buf][pos].issued);

        CHECK_CALL(glBeginQuery(GL_TIME_ELAPSED, s_perf.markers[buf][pos].query));
        s_perf.markers[buf][pos].issued = true;
        s_perf.markers[buf][pos].depth = depth;
        s_perf.markers[buf][pos].frame = s_frame;
        s_perf.markers[buf][pos].pad = pad;
        s_perf.markers[buf][pos].name = name;

        ++pos;
#endif
    }

    void direct::renderer_push_perf_marker(const c8* name)
    {
#ifdef GL_TIME_ELAPSED
        u32& depth = s_perf.depth;

        if (depth > 0)
        {
            CHECK_CALL(glEndQuery(GL_TIME_ELAPSED));
        }

        insert_marker(name);

        ++depth;
#endif
    }

    void direct::renderer_pop_perf_marker()
    {
#ifdef GL_TIME_ELAPSED
        u32& depth = s_perf.depth;
        --depth;

        CHECK_CALL(glEndQuery(GL_TIME_ELAPSED));

        if (depth > 0)
        {
            insert_marker("pad_marker", true);
        }
#endif
    }

    void gather_perf_markers()
    {
#ifdef GL_TIME_ELAPSED
        // unbalance push pop in perf markers
        PEN_ASSERT(s_perf.depth == 0);

        // swap buffers
        u32 bb = (s_perf.buf + 1) % s_perf.num_marker_buffers;

        u32 complete_count = 0;
        for (u32 i = 0; i < s_perf.pos[bb]; ++i)
        {
            perf_marker& m = s_perf.markers[bb][i];
            if (m.issued)
            {
                s32 avail = 0;
                CHECK_CALL(glGetQueryObjectiv(m.query, GL_QUERY_RESULT_AVAILABLE, &avail));

                if (avail)
                {
                    complete_count++;
                    CHECK_CALL(glGetQueryObjectui64v(m.query, GL_QUERY_RESULT, &m.result));
                }
            }
        }

        if (complete_count == s_perf.pos[bb])
        {
            // gather results into a better view
            sb_free(s_perf_results);

            u32 num_timers = s_perf.pos[bb];
            for (u32 i = 0; i < num_timers; ++i)
            {
                perf_marker& m = s_perf.markers[bb][i];

                if (!m.issued)
                    continue;

                // ready for the next frame
                m.issued = false;

                if (m.pad)
                    continue;

                gpu_perf_result p;
                p.name = m.name;
                p.depth = m.depth;
                p.frame = m.frame;

                if (m.name)
                {
                    // release mem that was allocated by the command buffer
                    memory_free((void*)m.name);
                    m.name = nullptr;
                }

                // gather up times from nested calls
                p.elapsed = 0;
                u32 nest_iter = 0;
                for (;;)
                {
                    perf_marker& n = s_perf.markers[bb][i + nest_iter];

                    if (n.depth < p.depth)
                        break;

                    if (n.pad && p.depth == n.depth)
                        break;

                    p.elapsed += n.result;

                    ++nest_iter;
                    if (nest_iter >= num_timers)
                        break;
                }

                Str desc;
                for (u32 j = 0; j < p.depth; ++j)
                    desc.append('\t');

                desc.append(p.name.c_str());

                desc.append(" : ");
                desc.appendf("%llu", p.elapsed);

                if (i == 0)
                    g_gpu_total = p.elapsed;
            }

            s_perf.pos[bb] = 0;
        }

        s_perf.buf = bb;
#endif
    }

//--------------------------------------------------------------------------------------
//  COMMON API
//--------------------------------------------------------------------------------------
#define MAX_VERTEX_BUFFERS 4
#define MAX_VERTEX_ATTRIBUTES 16
#define MAX_UNIFORM_BUFFERS 32
#define MAX_SHADER_TEXTURES 32

    struct context_state
    {
        context_state()
        {
        }

        u32 backbuffer_colour;
        u32 backbuffer_depth;

        u32 active_colour_target;
        u32 active_depth_target;
    };

    struct clear_state_internal
    {
        f32 rgba[4];
        f32 depth;
        u8  stencil;
        u32 flags;

        mrt_clear mrt[MAX_MRT];
        u32       num_colour_targets;
    };

    struct vertex_attribute
    {
        u32    location;
        u32    type;
        u32    stride;
        size_t offset;
        u32    num_elements;
        u32    input_slot;
        u32    step_rate;
    };

    struct input_layout
    {
        vertex_attribute* attributes;
        GLuint            vertex_array_handle = 0;
        GLuint            vb_handle = 0;
    };

    struct raster_state
    {
        GLenum cull_face;
        GLenum polygon_mode;
        bool   front_ccw;
        bool   culling_enabled;
        bool   depth_clip_enabled;
        bool   scissor_enabled;
    };

    struct texture_info
    {
        GLuint handle;
        u32    max_mip_level;
        u32    target;
    };

    struct render_target
    {
        texture_info             texture;
        texture_info             texture_msaa;
        GLuint                   w, h;
        u32                      uid;
        u32                      collection_type;
        texture_creation_params* tcp;
        u32                      invalidate;
    };

    struct framebuffer
    {
        hash_id hash;
        GLuint  _framebuffer;
    };
    static framebuffer* s_framebuffers;

    enum resource_type : s32
    {
        RES_TEXTURE = 0,
        RES_TEXTURE_3D,
        RES_RENDER_TARGET,
        RES_RENDER_TARGET_MSAA
    };

#define INVALID_LOC 255
    struct shader_program
    {
        u32    vs;
        u32    ps;
        u32    gs;
        u32    so;
        GLuint program;
        GLuint vflip_uniform;
        u8     uniform_block_location[MAX_UNIFORM_BUFFERS];
        u8     texture_location[MAX_SHADER_TEXTURES];
    };
    static shader_program* s_shader_programs;

    struct gl_sampler : public sampler_creation_params
    {
        u32 min_filter;
        u32 mag_filter;
    };

    struct resource_allocation
    {
        u8     asigned_flag;
        GLuint type;
        union {
            clear_state_internal           clear_state;
            pen::input_layout*             input_layout;
            pen::raster_state              raster_state;
            depth_stencil_creation_params* depth_stencil;
            blend_creation_params*         blend_state;
            GLuint                         handle;
            texture_info                   texture;
            pen::render_target             render_target;
            gl_sampler*                    sampler_state;
            pen::shader_program*           shader_program;
        };
    };
    static res_pool<resource_allocation> _res_pool;

    struct active_state
    {
        u32  vertex_buffer[MAX_VERTEX_BUFFERS] = {0};
        u32  vertex_buffer_stride[MAX_VERTEX_BUFFERS] = {0};
        u32  vertex_buffer_offset[MAX_VERTEX_BUFFERS] = {0};
        u32  num_bound_vertex_buffers = 0;
        u32  index_buffer = 0;
        u32  stream_out_buffer = 0;
        u32  input_layout = 0;
        u32  vertex_shader = 0;
        u32  pixel_shader = 0;
        u32  stream_out_shader = 0;
        u32  raster_state = 0;
        u32  base_vertex = 0;
        bool backbuffer_bound = false;
        bool v_flip = false;
        u8   constant_buffer_bindings[MAX_UNIFORM_BUFFERS] = {0};
        u32  index_format = GL_UNSIGNED_SHORT;
        u32  depth_stencil_state;
        u8   stencil_ref;
    };

    active_state g_bound_state;
    active_state g_current_state;
    viewport     g_current_vp;

    void clear_resource_table()
    {
        // reserve resource 0 for NULL binding.
        _res_pool[0].asigned_flag |= 0xff;
    }

    context_state g_context;

    void _set_depth_stencil_state(u32 dss, u8 ref)
    {
        resource_allocation& res = _res_pool[dss];

        // depth
        if (res.depth_stencil->depth_enable)
        {
            CHECK_CALL(glEnable(GL_DEPTH_TEST));
        }
        else
        {
            CHECK_CALL(glDisable(GL_DEPTH_TEST));
        }

        CHECK_CALL(glDepthFunc(res.depth_stencil->depth_func));
        CHECK_CALL(glDepthMask(res.depth_stencil->depth_write_mask));

        // stencil
        if (res.depth_stencil->stencil_enable)
        {
            CHECK_CALL(glEnable(GL_STENCIL_TEST));
            CHECK_CALL(glStencilMask(res.depth_stencil->stencil_write_mask));

            // front
            CHECK_CALL(glStencilOpSeparate(GL_FRONT, res.depth_stencil->front_face.stencil_failop,
                                           res.depth_stencil->front_face.stencil_depth_failop,
                                           res.depth_stencil->front_face.stencil_passop));

            CHECK_CALL(glStencilFuncSeparate(GL_FRONT, res.depth_stencil->front_face.stencil_func, ref,
                                             res.depth_stencil->stencil_read_mask));

            // back
            CHECK_CALL(glStencilOpSeparate(GL_BACK, res.depth_stencil->back_face.stencil_failop,
                                           res.depth_stencil->back_face.stencil_depth_failop,
                                           res.depth_stencil->back_face.stencil_passop));

            CHECK_CALL(glStencilFuncSeparate(GL_BACK, res.depth_stencil->back_face.stencil_func, ref,
                                             res.depth_stencil->stencil_read_mask));
        }
        else
        {
            CHECK_CALL(glDisable(GL_STENCIL_TEST));
        }
    }

    void direct::renderer_create_clear_state(const clear_state& cs, u32 resource_slot)
    {
        _res_pool.grow(resource_slot);

        _res_pool[resource_slot].clear_state.rgba[0] = cs.r;
        _res_pool[resource_slot].clear_state.rgba[1] = cs.g;
        _res_pool[resource_slot].clear_state.rgba[2] = cs.b;
        _res_pool[resource_slot].clear_state.rgba[3] = cs.a;
        _res_pool[resource_slot].clear_state.depth = cs.depth;
        _res_pool[resource_slot].clear_state.stencil = cs.stencil;
        _res_pool[resource_slot].clear_state.flags = to_gl_clear_flags(cs.flags);

        _res_pool[resource_slot].clear_state.num_colour_targets = cs.num_colour_targets;
        memcpy(&_res_pool[resource_slot].clear_state.mrt, cs.mrt, sizeof(mrt_clear) * MAX_MRT);
    }

    u32 link_program_internal(u32 vs, u32 ps, const shader_link_params* params = nullptr)
    {
        // link the shaders
        GLuint program_id = CHECK_CALL(glCreateProgram());

        GLuint so = 0;

        if (params)
        {
            // this path is for a proper link with reflection info
            vs = _res_pool[params->vertex_shader].handle;
            ps = _res_pool[params->pixel_shader].handle;
            so = _res_pool[params->stream_out_shader].handle;

            if (vs)
            {
                CHECK_CALL(glAttachShader(program_id, vs));
            }

            if (ps)
            {
                CHECK_CALL(glAttachShader(program_id, ps));
            }

            if (so)
            {
                CHECK_CALL(glAttachShader(program_id, so));
                CHECK_CALL(glTransformFeedbackVaryings(program_id, params->num_stream_out_names,
                                                       (const c8**)params->stream_out_names, GL_INTERLEAVED_ATTRIBS));
            }
        }
        else
        {
            // on the fly link for bound vs and ps which have not been explicity linked
            // to emulate d3d behaviour of set vs set ps etc
            CHECK_CALL(glAttachShader(program_id, vs));
            CHECK_CALL(glAttachShader(program_id, ps));
        }

        CHECK_CALL(glLinkProgram(program_id));

        // Check the program
        GLint result = GL_FALSE;
        int   info_log_length = 0;

        CHECK_CALL(glGetProgramiv(program_id, GL_LINK_STATUS, &result));
        CHECK_CALL(glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_log_length));

        if (result == GL_FALSE && info_log_length > 0)
        {
            char* info_log_buf = (char*)memory_alloc(info_log_length + 1);

            CHECK_CALL(glGetShaderInfoLog(program_id, info_log_length, NULL, &info_log_buf[0]));
            info_log_buf[info_log_length] = '\0';

            output_debug("%s", info_log_buf);
            memory_free(info_log_buf);
        }

        shader_program program;
        program.vs = vs;
        program.ps = ps;
        program.so = so;
        program.program = program_id;
        program.vflip_uniform = glGetUniformLocation(program.program, "v_flip");

        for (s32 i = 0; i < MAX_UNIFORM_BUFFERS; ++i)
        {
            program.texture_location[i] = INVALID_LOC;
            program.uniform_block_location[i] = INVALID_LOC;
        }

        sb_push(s_shader_programs, program);

        return sb_count(s_shader_programs) - 1;
    }

    void direct::renderer_sync()
    {
        // unused for opengl, required to sync for metal
    }

    void direct::renderer_new_frame()
    {
        pen_make_gl_context_current();
        _renderer_new_frame();

        // if we resize render targets, delete frame buffers to invalidate and recreate them
        static u32 rs = 0;
        if (_renderer_resize_index() != rs)
        {
            rs = _renderer_resize_index();
            sb_free(s_framebuffers);
            s_framebuffers = nullptr;
        }
    }

    void direct::renderer_end_frame()
    {
        // unused on this platform
    }

    void direct::renderer_clear(u32 clear_state_index, u32 colour_face, u32 depth_face)
    {
        resource_allocation&  rc = _res_pool[clear_state_index];
        clear_state_internal& cs = rc.clear_state;

        CHECK_CALL(glClearDepth(cs.depth));
        CHECK_CALL(glClearStencil(cs.stencil));

        if(rc.clear_state.flags & GL_DEPTH_BUFFER_BIT)
        {
            // we must enable depth writes when clearing..
            CHECK_CALL(glEnable(GL_DEPTH_TEST));
            CHECK_CALL(glDepthMask(true));
        }

        if(rc.clear_state.flags & GL_STENCIL_BUFFER)
        {
            CHECK_CALL(glEnable(GL_STENCIL_TEST));
            CHECK_CALL(glStencilMask(0xff));
        }

        if (cs.num_colour_targets == 0)
        {
            CHECK_CALL(
                glClearColor(rc.clear_state.rgba[0], rc.clear_state.rgba[1], rc.clear_state.rgba[2], rc.clear_state.rgba[3]));

            CHECK_CALL(glClear(rc.clear_state.flags));

            return;
        }
{'fraction_non_alphanumeric': 0.08002689979825152, 'fraction_numerical': 0.015657062059214043, 'mean_word_length': 1.4447348452912907, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 67, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 156, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5098059', 'n_tokens_mistral': 18721, 'n_tokens_neox': 17624, 'n_words': 3048}