module ecs_utils.gfx.renderer;

import bindbc.sdl;

import bubel.ecs.std;

//import ecs_utils.core : Backend;
import ecs_utils.gfx.buffer;
import ecs_utils.gfx.texture;
import ecs_utils.math.vector;

import bubel.ecs.block_allocator;
import bubel.ecs.vector;
version(WebAssembly)import glad.gl.gles2;
else version(Android)import glad.gl.gles2;
else import glad.gl.gl;

version = ver1;
/*version(ver5)version = vv2;
else version(ver6)version = vv2;*/

extern(C) float sinf(float);
extern(C) float cosf(float);

enum RenderTechnique
{
    simple,//1
    simple_array,//2
    vbo_batch,//3
    instanced_attrib_divisor,//4
    uniform_buffer,//5
    uniform_buffer_indexed,//6
    uniform_buffer_multi_draw,//7
    uniform_buffer_instanced,//8
    uniform_buffer_instanced_mapped_gl2,//9
    uniform_buffer_instanced_mapped,//10
    uniform_buffer_instanced_persistent_mapped,//11
    uniform_buffer_instanced_persistent_mapped_coherent,//12
    ssbo_instanced,//13
    uniform_buffer_draw_indirect,//14
    uniform_buffer_multi_draw_indirect,//15
    uniform_buffer_multi_draw_indirect_arb_draw_parameters//16
}

struct Renderer
{
    //static SDL_Renderer* main_sdl_renderer;
    BlockAllocator allocator;

    enum MaxObjects = 1024 * 64 * 4;
    enum BufferUsage = GL_DYNAMIC_DRAW;

    //SDL_Window* sdl_window;
    //SDL_Renderer* sdl_renderer;
    ivec2 resolution;
    vec2 dres;
    vec4 sdl_transform;
    vec2 view_pos = vec2(-1,-1);
    vec2 view_size = vec2(1,1);

    enum block_size = 2^^16;
    enum batch_size  = block_size/68;//963;//16_384;
    //uint[2] time_queries;

    struct VertexBlock
    {
        enum max_items = batch_size;//963;
        byte[] batch_vertices;
        ushort[] batch_indices;
        void* memory = null;
        uint items = 0;
        uint material_id;
        uint texture_id;
    }

    Mutex* get_block_mutex;
    Mutex* block_stack_mutex;

    VertexBlock getBlock()
    {
        VertexBlock block;
        get_block_mutex.lock();
        block.memory = allocator.getBlock();
        get_block_mutex.unlock();
        block.batch_vertices = (cast(byte*)block.memory)[0 .. VertexBlock.max_items * 4 * 14];
        block.batch_indices = (cast(ushort*)block.memory)[VertexBlock.max_items * 4 * 7 .. VertexBlock.max_items * (4 * 7 + 6)];
        return block;
    }

    Vector!VertexBlock blocks;
    uint current_block = 0;
    uint render_blocks = 0;

    void pushBlock(VertexBlock block)
    {
        block_stack_mutex.lock();
        prepared_items += block.items;
        blocks.add(block);
        render_blocks++;
        block_stack_mutex.unlock();
    }

    bool isRemainingBlocks()
    {
        if(render_blocks <= current_block)return false;
        return true;
    }

    VertexBlock fetchBlock()
    {
        block_stack_mutex.lock();
        VertexBlock block = blocks[current_block];
        current_block++;
        block_stack_mutex.unlock();
        return block;
    }

    void freeBlocks()
    {
        /*block_stack_mutex.lock();
        render_blocks = 0;
        current_block = 0;
        foreach(VertexBlock block; blocks)
        {
            allocator.freeBlock(block.memory);
        }
        blocks.clear;
        prepared_items=0;
        draw_list.clear();
        block_stack_mutex.unlock();*/
        foreach(ref Thread thread; threads)
        {
            foreach(VertexBlock block; thread.filled_blocks)
            {
                allocator.freeBlock(block.memory);
            }
            thread.filled_blocks.clear();
        }
        render_blocks = 0;
        current_block = 0;
        prepared_items = 0;
        draw_list.clear();
    }

    void pushData()
    {
        foreach(ref Thread thread; threads)
        {
            foreach(VertexBlock block; thread.filled_blocks)
            {
                uint items = block.items;
                if(items + item_id >= MaxObjects)items = MaxObjects - item_id;
                batch_vbo[0].bufferSubData(Buffer.BindTarget.array,items*4*14,item_id*4*14,block.batch_vertices.ptr);
                batch_ibo[0].bufferSubData(Buffer.BindTarget.element_array,items*2*6,item_id*2*6,block.batch_indices.ptr);
                draw_list.add(DrawCall(block.texture_id,block.material_id,item_id,items));
                item_id += items;
            }
            //thread.blocks.clear();
        }
        //if(!isRemainingBlocks())return;
       /* while(isRemainingBlocks())
        {
            VertexBlock block = fetchBlock();
            uint items = block.items;
            if(items + item_id >= MaxObjects)items = MaxObjects - item_id;
            batch_vbo[0].bufferSubData(Buffer.BindTarget.array,items*4*14,item_id*4*14,block.batch_vertices.ptr);
            batch_ibo[0].bufferSubData(Buffer.BindTarget.element_array,items*2*6,item_id*2*6,block.batch_indices.ptr);
            draw_list.add(DrawCall(item_id,items));
            item_id += items;
        }*/
    }

    void pushThreadsBlocks()
    {
        foreach(i, ref Thread thread; threads)
        {
            //pushBlock(thread.block);
            foreach(ref block; thread.blocks)
            {
                if(block.items > 0)
                {
                    thread.filled_blocks.add(block);
                    block.items = 0;
                }
            }
            //thread.blocks = getBlock();
        }
    }

    struct DrawData
    {
        Texture texture;
        vec2 position;
        vec2 size;
        vec4 coords;
        short depth = 0;
        uint color = uint.max;
        float angle = 0;
        uint material_id = 0;
        uint mesh_id = 0;
        uint thread_id = 0;
    }

    struct Thread
    {
        //Vector!VertexBlock block;
        RenderData[] render_list;
        VertexBlock[] blocks;
        Vector!VertexBlock filled_blocks;
    }
    Thread[] threads;


    Buffer[2] ubos;
    int block_alignment = 1;
    int block_max_size = 16384;

    struct IndirectDraw
    {
        uint count = 6;
        uint instances = 1;
        uint first_index = 0;
        uint base_vertex = 0;
        uint base_instance = 0;
    }

    Buffer[2] batch_vbo;
    Buffer[2] batch_ibo;

    ubyte[] batch_vertices;
    ushort[] batch_indices;

    Buffer indirect_buffer;
    IndirectDraw[] indirect_block;

    Buffer id_buffer;

    int data_offset = 48;
    int data_index;
    ubyte[] uniform_block;

    struct RenderData
    {
        Texture texture;
        uint material_id;
        uint mesh_id;
    }

    struct DrawCall
    {
        uint texture_id;
        uint material_id;
        uint start;
        uint count;
    }

    Vector!DrawCall draw_list;

    RenderData[] render_list;
    uint item_id;
    uint prepared_items;

    uint[] multi_count;
    uint[] multi_offset;

    alias Technique = RenderTechnique;

    __gshared Technique technique = Technique.vbo_batch;
    void* data_ptr;

    //import ecs_utils.core : RenderTechnique;

    

    void initialize()
    {
        import ecs_utils.gfx.config;
        //this.technique = __ecs_used_technique;
        __initialize(this);

        get_block_mutex = Mallocator.make!Mutex();
        block_stack_mutex = Mallocator.make!Mutex();
        get_block_mutex.initialize();
        block_stack_mutex.initialize();


        threads = Mallocator.makeArray!Thread(32);
        foreach(ref Thread thread;threads)
        {
            //thread.blocks = getBlock();
            thread.blocks = Mallocator.makeArray!VertexBlock(GfxConfig.materials.length);
        }
    }

    private static void __initialize_gl(ref Renderer this_)
    {
        with(this_)
        {
            //glGenQueries(2, time_queries.ptr);

            version(WebAssembly)
            {
                
            }
            else
            {
                glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &block_max_size);
                glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &block_alignment);
            }   
            
            //ubos[0].bufferStorage(1,64*MaxObjects,null);

            switch(technique)
            {
                case Technique.simple:
                    uniform_block = Mallocator.makeArray!ubyte(64*MaxObjects);
                    data_ptr = uniform_block.ptr;
                    data_offset = cast(ushort)((data_offset + block_alignment - 1) & (-cast(int) block_alignment));
                    break;
                case Technique.simple_array:
                    goto case(Technique.simple);
                case Technique.vbo_batch:
                    batch_vbo[0].create();
                    batch_ibo[0].create();
                    batch_vbo[0].bufferData(Buffer.BindTarget.array,14,4*MaxObjects,BufferUsage,null);
                    batch_ibo[0].bufferData(Buffer.BindTarget.element_array,2,6*MaxObjects,BufferUsage,null);

                    batch_vbo[1].create();
                    batch_ibo[1].create();
                    batch_vbo[1].bufferData(Buffer.BindTarget.array,14,4*MaxObjects,BufferUsage,null);
                    batch_ibo[1].bufferData(Buffer.BindTarget.element_array,2,6*MaxObjects,BufferUsage,null);

                    //batch_vertices = Mallocator.makeArray!ubyte(14*4*MaxObjects);
                    //batch_indices = Mallocator.makeArray!ushort(6*MaxObjects);
                    break;
                case Technique.instanced_attrib_divisor:
                    goto case(Technique.uniform_buffer_indexed);
                case Technique.uniform_buffer:
                    ubos[0].create();
                    ubos[0].bufferData(Buffer.BindTarget.uniform,1,64*MaxObjects,BufferUsage,null);
                    ubos[1].create();
                    ubos[1].bufferData(Buffer.BindTarget.uniform,1,64*MaxObjects,BufferUsage,null);
                    goto case(Technique.simple);
                case Technique.uniform_buffer_indexed:
                    ubos[0].create();
                    ubos[0].bufferData(Buffer.BindTarget.uniform,1,64*MaxObjects,BufferUsage,null);
                    ubos[1].create();
                    ubos[1].bufferData(Buffer.BindTarget.uniform,1,64*MaxObjects,BufferUsage,null);
                    uniform_block = Mallocator.makeArray!ubyte(64*MaxObjects);
                    data_ptr = uniform_block.ptr;
                    break;
                /*case Technique.uniform_buffer_multi_draw:
                    multi_count = Mallocator.makeArray!uint(992,6);
                    multi_offset = Mallocator.makeArray!uint(992,0);

                    {
                        uint[] indices = Mallocator.makeArray!uint(992);
                        scope(exit)Mallocator.dispose(indices);
                        foreach(i;0..992)indices[i]=i;
                        id_buffer.create();
                        id_buffer.bufferData(uint.sizeof,992,BufferUsage,indices.ptr);
                    }
                    goto case(Technique.uniform_buffer_multi_draw_indirect_arb_draw_parameters);*/
                case Technique.uniform_buffer_instanced:
                    goto case(Technique.uniform_buffer_indexed);
                case Technique.uniform_buffer_instanced_mapped_gl2:
                    ubos[0].create();
                    ubos[0].bufferData(Buffer.BindTarget.uniform,1,512*MaxObjects,BufferUsage,null);
                    ubos[0].map(Buffer.BindTarget.uniform);
                    ubos[1].create();
                    ubos[1].bufferData(Buffer.BindTarget.uniform,1,512*MaxObjects,BufferUsage,null);
                    ubos[1].map(Buffer.BindTarget.uniform);
                    data_ptr = ubos[0].mappedPointer();
                    break;
                case Technique.uniform_buffer_instanced_mapped:
                    ubos[0].create();
                    ubos[0].bufferData(Buffer.BindTarget.uniform,1,64*MaxObjects,BufferUsage,null);
                    ubos[0].map(0, 64*MaxObjects, Buffer.BindTarget.uniform);
                    ubos[1].create();
                    ubos[1].bufferData(Buffer.BindTarget.uniform,1,64*MaxObjects,BufferUsage,null);
                    ubos[1].map(0, 64*MaxObjects, Buffer.BindTarget.uniform);
                    data_ptr = ubos[0].mappedPointer();
                    break;
                /*case Technique.uniform_buffer_instanced_persistent_mapped:
                    ubos[0].create();
                    ubos[0].bufferStorage(1,64*MaxObjects,null,Buffer.StorageFlagBits.write|Buffer.StorageFlagBits.persistent);
                    ubos[0].map(0, 64*MaxObjects, Buffer.BindTarget.uniform, Buffer.MapFlagBits.write | Buffer.MapFlagBits.persistent | Buffer.MapFlagBits.flush_explict);
                    data_ptr = ubos[0].mappedPointer();
                    break;
                case Technique.uniform_buffer_instanced_persistent_mapped_coherent:
                    ubos[0].create();
                    ubos[0].bufferStorage(1,64*MaxObjects,null,Buffer.StorageFlagBits.write|Buffer.StorageFlagBits.persistent|Buffer.StorageFlagBits.coherent);
                    ubos[0].map(0, 64*MaxObjects, Buffer.BindTarget.uniform, Buffer.MapFlagBits.write | Buffer.MapFlagBits.persistent | Buffer.MapFlagBits.flush_explict | Buffer.MapFlagBits.coherent);
                    ubos[1].create();
                    ubos[1].bufferStorage(1,64*MaxObjects,null,Buffer.StorageFlagBits.write|Buffer.StorageFlagBits.persistent|Buffer.StorageFlagBits.coherent);
                    ubos[1].map(0, 64*MaxObjects, Buffer.BindTarget.uniform, Buffer.MapFlagBits.write | Buffer.MapFlagBits.persistent | Buffer.MapFlagBits.flush_explict | Buffer.MapFlagBits.coherent);
                    data_ptr = ubos[0].mappedPointer();
                    break;
                case Technique.ssbo_instanced:
                    goto case(Technique.uniform_buffer_indexed);
                case Technique.uniform_buffer_draw_indirect:
                    indirect_block = Mallocator.makeArray!IndirectDraw(1);
                    indirect_buffer.create();
                    indirect_buffer.bufferData(IndirectDraw.sizeof,1,BufferUsage,indirect_block.ptr);
                    indirect_buffer.bind(Buffer.BindTarget.indirect);
                    goto case(Technique.uniform_buffer);
                case Technique.uniform_buffer_multi_draw_indirect:
                    goto case(Technique.uniform_buffer_multi_draw_indirect_arb_draw_parameters);
                case Technique.uniform_buffer_multi_draw_indirect_arb_draw_parameters:
                    indirect_block = Mallocator.makeArray!IndirectDraw(992);
                    foreach(i;0..992)
                    {
                        IndirectDraw* idraw = &indirect_block[i];
                        idraw.base_instance = i;
                    }
                    indirect_buffer.create();
                    indirect_buffer.bufferData(IndirectDraw.sizeof,992,BufferUsage,indirect_block.ptr);
                    indirect_buffer.bind(Buffer.BindTarget.indirect);
                    goto case(Technique.uniform_buffer_indexed);*/
                default:break;
            }//*/

           // if(batching)data_offset = cast(ushort)((data_offset + block_alignment - 1) & (-cast(int) block_alignment));
            //data_offset = cast(ushort)((data_offset + block_alignment - 1) & (-cast(int) block_alignment));

            /*version(ver4){}
            else version(ver5){}
            else version(ver6){}
            else data_offset = cast(ushort)((data_offset + block_alignment - 1) & (-cast(int) block_alignment));//*/
            //data_offset = (data_offset + block_alignment - 1) - data_offset % block_alignment;

            render_list = Mallocator.makeArray!RenderData(MaxObjects);

            SDL_Log("Uniform block alignment: %u",block_alignment);
            SDL_Log("Uniform block max size: %u",block_max_size);
            SDL_Log("Data offset: %u",data_offset);

            allocator = BlockAllocator(block_size, 32);
        }
    }

    private static void __initialize_sdl(ref Renderer this_)
    {

    }

    //void draw(Texture tex, vec2 pos, vec2 size, vec4 coords, short depth = 0, uint color = uint.max, float angle = 0, uint material_id = 0, uint mesh_id = 0, uint thread_id = 0)
    void draw(scope ref const(DrawData) data)
    {
        if(prepared_items >= MaxObjects)return;
        __draw(this,data);//tex,pos,size,coords,depth,color,angle,material_id,mesh_id,thread_id);
    }

    //private static void __draw_sdl(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id)
    private static void __draw_sdl(ref Renderer this_, scope ref const(DrawData) data)
    {
        /*with(this_)
        {
            SDL_Rect rect = SDL_Rect(cast(int)(coords.x*tex.data.size.x),cast(int)(coords.y*tex.data.size.y),cast(int)(coords.z*tex.data.size.x),cast(int)(coords.w*tex.data.size.y));
            SDL_Rect rect2 = SDL_Rect(cast(int)((pos.x-size.x*0.5)),
                                    cast(int)(resolution.y - pos.y - size.y*0.5),
                                    cast(int)(size.x),
                                    cast(int)(size.y));

            SDL_RenderCopyEx(sdl_renderer,
                        tex.data.texture,
                        &rect,
                        &rect2,
                        angle*360,
                        null,
                        SDL_FLIP_NONE);
        }*/
    }

    // private static void __draw_gl(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id)
    private static void __draw_gl(ref Renderer this_, scope ref const(DrawData) data)
    {
        //import core.stdc.string;
        with(this_)
        {
            //pos += view_pos;
            vec2 pos = void;
            vec2 size = void;
            size.x = data.size.x * view_size.x;
            size.y = data.size.y * view_size.y;
            pos.x = data.position.x * view_size.x + view_pos.x;
            pos.y = data.position.y * view_size.y + view_pos.y;//*/

            /*version(ver6)void* ptr = ubos[0].mappedPointer() + data_index;
            else void* ptr = uniform_block.ptr + data_index;*/
            if(data_ptr is null)return;
            void* ptr = data_ptr + data_index;
            if(data.angle == 0)
            {
                *cast(float*)ptr = size.x;
                *cast(float*)(ptr+4) = 0;
                *cast(float*)(ptr+8) = 0;
                *cast(float*)(ptr+12) = size.y;
            }
            else
            {
                //import core.stdc.math;
                float sinn = sinf(data.angle);
                float coss = cosf(data.angle);
                *cast(float*)ptr = coss * size.x;
                *cast(float*)(ptr+4) = -sinn * size.y;
                *cast(float*)(ptr+8) = sinn * size.x;
                *cast(float*)(ptr+12) = coss * size.y;
            }
            
            //memcpy(ptr,);
            memcpy(ptr+16,pos.data.ptr,8);
            memcpy(ptr+32,data.coords.data.ptr,16);

            //render_list[item_id] = RenderData(tex,material_id,mesh_id);
            render_list[item_id].texture = *cast(Texture*)&data.texture;
            render_list[item_id].material_id = data.material_id;
            render_list[item_id].mesh_id = data.mesh_id;
            
            data_index += data_offset;
            item_id++;
            prepared_items++;
        }
    }

    // private static void __draw_gl_vbo_batch(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id = 0)
    private static void __draw_gl_vbo_batch(ref Renderer this_, scope ref const(DrawData) data)
    {
        import ecs_utils.gfx.config;
        
        //import core.stdc.string;
        with(this_)
        {
            uint thread_id = data.thread_id;
            //if(item_id >= MaxObjects)return;
            //pos += view_pos;
            Thread* thread = &threads[thread_id];
            VertexBlock* block;
            assert(thread.blocks.length > data.material_id);
            block = &thread.blocks[data.material_id];
            if(block.items == 0)
            {
                thread.blocks[data.material_id] = getBlock();
                block = &thread.blocks[data.material_id];
                block.material_id = data.material_id;
            }
            else if(block.items >= VertexBlock.max_items)
            {
                //pushBlock(thread.block);
                prepared_items += block.items;
                thread.filled_blocks.add(*block);
                thread.blocks[data.material_id] = getBlock();
                block = &thread.blocks[data.material_id];
                block.material_id = data.material_id;
            }

            short[3] mem = [data.depth, *cast(short*)&data.color, *(cast(short*)&data.color + 1)];
            
            vec2 pos = void;
            vec2 size = void;
            pos.x = data.position.x * view_size.x + view_pos.x;
            pos.y = data.position.y * view_size.y + view_pos.y;//*/

            /*void* ptr = data_ptr + data_index;
            *cast(float*)ptr = size.x;
            *cast(float*)(ptr+4) = 0;
            *cast(float*)(ptr+8) = 0;
            *cast(float*)(ptr+12) = size.y;
            //memcpy(ptr,);
            memcpy(ptr+16,pos.data.ptr,8);
            memcpy(ptr+32,coords.data.ptr,16);*/

            short[] verts = (cast(short*)block.batch_vertices.ptr)[0..block.batch_vertices.length>>1];
            uint item_id = block.items;

            uint mesh_id = data.mesh_id;
            vec4 coords = data.coords;

            if(data.angle == 0)
            {
                size.x = data.size.x * view_size.x;
                size.y = data.size.y * view_size.y;

                verts[item_id*28]    = cast(short)((GfxConfig.meshes[mesh_id].vertices[0] * size.x + pos.x) * 8191);
                verts[item_id*28+1]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[1] * size.y + pos.y) * 8191);
                verts[item_id*28+2]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x)*32767);
                verts[item_id*28+3]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y)*32767);
                memcpy(verts.ptr+item_id*28+4,mem.ptr,6);


                verts[item_id*28+7]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[4] * size.x + pos.x) * 8191);
                verts[item_id*28+8]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[5] * size.y + pos.y) * 8191);
                verts[item_id*28+9]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[6] * coords.z + coords.x)*32767);
                verts[item_id*28+10]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[7] * coords.w + coords.y)*32767);
                memcpy(verts.ptr+item_id*28+11,mem.ptr,6);


                verts[item_id*28+14]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[8] * size.x + pos.x) * 8191);
                verts[item_id*28+15]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[9] * size.y + pos.y) * 8191);
                verts[item_id*28+16] = cast(short)((GfxConfig.meshes[mesh_id].vertices[10] * coords.z + coords.x)*32767);
                verts[item_id*28+17] = cast(short)((GfxConfig.meshes[mesh_id].vertices[11] * coords.w + coords.y)*32767);
                memcpy(verts.ptr+item_id*28+18,mem.ptr,6);


                verts[item_id*28+21] = cast(short)((GfxConfig.meshes[mesh_id].vertices[12] * size.x + pos.x) * 8191);
                verts[item_id*28+22] = cast(short)((GfxConfig.meshes[mesh_id].vertices[13] * size.y + pos.y) * 8191);
                verts[item_id*28+23] = cast(short)((GfxConfig.meshes[mesh_id].vertices[14] * coords.z + coords.x)*32767);
                verts[item_id*28+24] = cast(short)((GfxConfig.meshes[mesh_id].vertices[15] * coords.w + coords.y)*32767);
                memcpy(verts.ptr+item_id*28+25,mem.ptr,6);
            }
            else
            {
                //import core.stdc.math;
                float angle = data.angle;
                float sinx = sinf(angle) * data.size.x * view_size.y;
                float cosx = cosf(angle) * data.size.x * view_size.x;
                float siny = sinf(angle) * data.size.y * view_size.x;
                float cosy = cosf(angle) * data.size.y * view_size.y;

                /*batch_vertices[item_id*28]    = GfxConfig.meshes[mesh_id].vertices[0]  * size.x;
                batch_vertices[item_id*28+1]  = GfxConfig.meshes[mesh_id].vertices[1]  * size.y;
                batch_vertices[item_id*28+4]  = GfxConfig.meshes[mesh_id].vertices[4]  * size.x;
                batch_vertices[item_id*28+5]  = GfxConfig.meshes[mesh_id].vertices[5]  * size.y;
                batch_vertices[item_id*28+8]  = GfxConfig.meshes[mesh_id].vertices[8]  * size.x;
                batch_vertices[item_id*28+9]  = GfxConfig.meshes[mesh_id].vertices[9]  * size.y;
                batch_vertices[item_id*28+12] = GfxConfig.meshes[mesh_id].vertices[12] * size.x;
                batch_vertices[item_id*28+13] = GfxConfig.meshes[mesh_id].vertices[13] * size.y;*/

                verts[item_id*28]    = cast(short)(((GfxConfig.meshes[mesh_id].vertices[0]  * cosx + GfxConfig.meshes[mesh_id].vertices[1]  * siny) + pos.x) * 8191);
                verts[item_id*28+1]  = cast(short)(((GfxConfig.meshes[mesh_id].vertices[1]  * cosy - GfxConfig.meshes[mesh_id].vertices[0]  * sinx) + pos.y) * 8191);
                verts[item_id*28+2]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x)*32767);
                verts[item_id*28+3]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y)*32767);
                memcpy(verts.ptr+item_id*28+4,mem.ptr,6);


                verts[item_id*28+7]  = cast(short)(((GfxConfig.meshes[mesh_id].vertices[4]  * cosx + GfxConfig.meshes[mesh_id].vertices[5]  * siny) + pos.x) * 8191);
                verts[item_id*28+8]  = cast(short)(((GfxConfig.meshes[mesh_id].vertices[5]  * cosy - GfxConfig.meshes[mesh_id].vertices[4]  * sinx) + pos.y) * 8191);
                verts[item_id*28+9]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[6] * coords.z + coords.x)*32767);
                verts[item_id*28+10]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[7] * coords.w + coords.y)*32767);
                memcpy(verts.ptr+item_id*28+11,mem.ptr,6);


                verts[item_id*28+14]  = cast(short)(((GfxConfig.meshes[mesh_id].vertices[8]  * cosx + GfxConfig.meshes[mesh_id].vertices[9]  * siny) + pos.x) * 8191);
                verts[item_id*28+15]  = cast(short)(((GfxConfig.meshes[mesh_id].vertices[9]  * cosy - GfxConfig.meshes[mesh_id].vertices[8]  * sinx) + pos.y) * 8191);
                verts[item_id*28+16] = cast(short)((GfxConfig.meshes[mesh_id].vertices[10] * coords.z + coords.x)*32767);
                verts[item_id*28+17] = cast(short)((GfxConfig.meshes[mesh_id].vertices[11] * coords.w + coords.y)*32767);
                memcpy(verts.ptr+item_id*28+18,mem.ptr,6);


                verts[item_id*28+21] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[12] * cosx + GfxConfig.meshes[mesh_id].vertices[13] * siny) + pos.x) * 8191);
                verts[item_id*28+22] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[13] * cosy - GfxConfig.meshes[mesh_id].vertices[12] * sinx) + pos.y) * 8191);
                verts[item_id*28+23] = cast(short)((GfxConfig.meshes[mesh_id].vertices[14] * coords.z + coords.x)*32767);
                verts[item_id*28+24] = cast(short)((GfxConfig.meshes[mesh_id].vertices[15] * coords.w + coords.y)*32767);
                memcpy(verts.ptr+item_id*28+25,mem.ptr,6);
            }

            /*verts[item_id*28+2]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x)*32767);
            verts[item_id*28+3]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y)*32767);
            verts[item_id*28+9]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[6] * coords.z + coords.x)*32767);
            verts[item_id*28+10]  = cast(short)((GfxConfig.meshes[mesh_id].vertices[7] * coords.w + coords.y)*32767);
            verts[item_id*28+16] = cast(short)((GfxConfig.meshes[mesh_id].vertices[10] * coords.z + coords.x)*32767);
            verts[item_id*28+17] = cast(short)((GfxConfig.meshes[mesh_id].vertices[11] * coords.w + coords.y)*32767);
            verts[item_id*28+23] = cast(short)((GfxConfig.meshes[mesh_id].vertices[14] * coords.z + coords.x)*32767);
            verts[item_id*28+24] = cast(short)((GfxConfig.meshes[mesh_id].vertices[15] * coords.w + coords.y)*32767);*/

            /*verts[item_id*28+4] = depth;
            verts[item_id*28+11] = depth;
            verts[item_id*28+18] = depth;
            verts[item_id*28+25] = depth;

            *cast(uint*)&verts[item_id*28+5] = color;
            *cast(uint*)&verts[item_id*28+12] = color;
            *cast(uint*)&verts[item_id*28+19] = color;
            *cast(uint*)&verts[item_id*28+26] = color;
            
            memcpy(verts.ptr+item_id*28+4,mem.ptr,6);
            memcpy(verts.ptr+item_id*28+11,mem.ptr,6);
            memcpy(verts.ptr+item_id*28+18,mem.ptr,6);
            memcpy(verts.ptr+item_id*28+25,mem.ptr,6);*/

            uint ind_id = (item_id % batch_size)*4;

            ushort[] indices = block.batch_indices;

            indices[item_id*6]   = cast(ushort)(GfxConfig.meshes[mesh_id].indices[0] + ind_id);
            indices[item_id*6+1] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[1] + ind_id);
            indices[item_id*6+2] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[2] + ind_id);
            indices[item_id*6+3] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[3] + ind_id);
            indices[item_id*6+4] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[4] + ind_id);
            indices[item_id*6+5] = cast(ushort)(GfxConfig.meshes[mesh_id].indices[5] + ind_id);

            //render_list[item_id] = RenderData(tex,material_id,mesh_id);
            //render_list[item_id].texture = tex;
            //render_list[item_id].material_id = material_id;
            //render_list[item_id].mesh_id = mesh_id;
            
            //data_index += 1;//data_offset;
            block.items++;
        }
    }

    void clear()
    {
        __clear(this);
    }

    private static void __clear_sdl(ref Renderer this_)
    {
        //SDL_RenderClear(this_.sdl_renderer);
    }

    private static void __clear_gl(ref Renderer this_)
    {
        glClearColor(0,0,0,0);
        glViewport(0,0,this_.resolution.x,this_.resolution.y);
        glDepthMask(1);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        //glDisable(GL_DEPTH_TEST);
        glEnable(GL_DEPTH_TEST);
        glDisable(GL_CULL_FACE);
        glDepthFunc(GL_LESS);

        version(WebAssembly)
        {
            glDepthRangef(0,1);
        }
        else version(Android)
        {
            glDepthRangef(0,1);
        }
        else
        {
            glDepthRange(0,1);
        }
        //glDepthRange(0,1);
        //glClearDepth(1);
    }

    void present()
    {
        __present(this);
    }

    private static void __present_sdl(ref Renderer this_)
    {
        //+SDL_RenderPresent(this_.sdl_renderer);
    }

    private static void __present_gl(ref Renderer this_)
    {

        this_.pushThreadsBlocks();
        this_.pushData();

        glViewport(0,0,this_.resolution.x,this_.resolution.y);
        //glEnable(GL_ALPHA_TEST);
        //glAlphaFunc(GL_GREATER, 0.01);
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);
        import ecs_utils.gfx.config;
        with(this_)
        {
            bool instanced = false;
            bool indirect = false;
            bool multi_draw = false;
            Buffer.BindTarget buffer_target = Buffer.BindTarget.uniform;

            switch(technique)
            {
                case Technique.simple:
                    break;
                case Technique.simple_array:
                    break;
                case Technique.vbo_batch:
                //if(data_index){
                    //batch_vbo[0].bufferSubData(Buffer.BindTarget.array,item_id*4*14,0,batch_vertices.ptr);
                    //batch_ibo[0].bufferSubData(Buffer.BindTarget.element_array,item_id*6*2,0,batch_indices.ptr);

                    batch_vbo[0].bind(Buffer.BindTarget.array);
                    batch_ibo[0].bind(Buffer.BindTarget.element_array);

                    //glVertexAttribPointer(0,2,GL_SHORT,true,14,null);
                    //glVertexAttribPointer(1,2,GL_SHORT,true,14,cast(void*)4);//}
                    glEnableVertexAttribArray(2);
                    glEnableVertexAttribArray(3);
                    //glVertexAttribPointer(2,1,GL_SHORT,true,14,cast(void*)6);//}
                    break;
                case Technique.instanced_attrib_divisor:
                    ubos[0].bufferSubData(Buffer.BindTarget.uniform,data_index,0,uniform_block.ptr);
                    ubos[0].bind(Buffer.BindTarget.uniform);
                    glEnableVertexAttribArray(2);
                    glEnableVertexAttribArray(3);
                    glEnableVertexAttribArray(4);

                    glVertexAttribPointer(2,4,GL_FLOAT,false,48,null);
                    glVertexAttribPointer(3,4,GL_FLOAT,false,48,cast(void*)16);
                    glVertexAttribPointer(4,4,GL_FLOAT,false,48,cast(void*)32);
                    glVertexAttribDivisor(2,1);
                    glVertexAttribDivisor(3,1);
                    glVertexAttribDivisor(4,1);
                    //ubos[0].bindRange(Buffer.BindTarget.uniform,0,0,block_max_size);
                    break;
                case Technique.uniform_buffer:
                    //ubos[0].bufferData(1,64*MaxObjects,BufferUsage,null);
                    /*if(data_index)*/ubos[0].bufferSubData(Buffer.BindTarget.uniform,data_index,0,uniform_block.ptr);
                    break;
                case Technique.uniform_buffer_indexed:
                    ubos[0].bindRange(Buffer.BindTarget.uniform,0,0,block_max_size);
                    goto case(Technique.uniform_buffer);
                case Technique.uniform_buffer_multi_draw:
                    id_buffer.bind(Buffer.BindTarget.array);
                    glEnableVertexAttribArray(2);

                    glVertexAttribIPointer(2,1,GL_UNSIGNED_INT,cast(uint)uint.sizeof,cast(void*)0);
                    glVertexAttribDivisor(2,1);
                    multi_draw = true;
                    goto case(Technique.uniform_buffer_instanced);
                case Technique.uniform_buffer_instanced:
                    instanced = true;
                    goto case(Technique.uniform_buffer);
                case Technique.uniform_buffer_instanced_mapped_gl2:
                    instanced = true;
                    ubos[0].unmap(Buffer.BindTarget.uniform);
                    break;
                case Technique.uniform_buffer_instanced_mapped:
                    instanced = true;
                    ubos[0].flush(0,data_index,Buffer.BindTarget.uniform);
                    ubos[0].unmap(Buffer.BindTarget.uniform);
                    break;
                /*case Technique.uniform_buffer_instanced_persistent_mapped:
                    instanced = true;
                    ubos[0].flush(0,data_index,Buffer.BindTarget.uniform);
                    //glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
                    break;
                case Technique.uniform_buffer_instanced_persistent_mapped_coherent:
                    instanced = true;
                    break;
                    //ubos[0].flush(0,data_index,Buffer.BindTarget.uniform);
                    //goto case(Technique.uniform_buffer_instanced_mapped);
                case Technique.ssbo_instanced:
                    //buffer_target = Buffer.BindTarget.shader_storage;
                    ubos[0].bindRange(Buffer.BindTarget.shader_storage,0,0,48*MaxObjects);
                    goto case(Technique.uniform_buffer_instanced);
                case Technique.uniform_buffer_draw_indirect:
                    goto case(Technique.uniform_buffer);
                case Technique.uniform_buffer_multi_draw_indirect:
                    indirect_buffer.bind(Buffer.BindTarget.array);
                    glEnableVertexAttribArray(2);

                    glVertexAttribIPointer(2,1,GL_UNSIGNED_INT,cast(uint)IndirectDraw.sizeof,cast(void*)(4*uint.sizeof));
                    glVertexAttribDivisor(2,1);
                    goto case(Technique.uniform_buffer_multi_draw_indirect_arb_draw_parameters);
                case Technique.uniform_buffer_multi_draw_indirect_arb_draw_parameters:
                    indirect = true;
                    goto case(Technique.uniform_buffer_instanced);*/
                default:break;
            }

            data_index = 0;

            int mesh_id = -1;
            int material_id = -1;
            int ubo_start = -1;
            Texture texture;
            uint item_ubo_id = 0;

            /*Buffer tmpb = ubos[0];
            ubos[0] = ubos[1];
            ubos[1] = tmpb;

            tmpb = batch_vbo[0];
            batch_vbo[0] = batch_vbo[1];
            batch_vbo[1] = tmpb;

            tmpb = batch_ibo[0];
            batch_ibo[0] = batch_ibo[1];
            batch_ibo[1] = tmpb;//*/
            //glFinish();

            //glBeginQuery(GL_TIME_ELAPSED, time_queries[0]);
            if(technique == Technique.vbo_batch)
            {
                //uint items = item_id/batch_size+1;
                foreach(i; 0..draw_list.length)
                {
                    if(material_id != draw_list[i].material_id)
                    {
                        material_id = draw_list[i].material_id;
                        GfxConfig.materials[material_id].bind();
                        float[3*4] data = [1,0,0,1,0,0,0,0,0,0,1,1];
                        GfxConfig.materials[material_id].pushUniforms(data.ptr);
                    }
                    if(texture.data != render_list[i].texture.data)
                    {
                        texture.data = render_list[i].texture.data;
                        render_list[i].texture.bind();
                    }

                    /*uint instance_count = batch_size;
                    if((i+1)*batch_size > item_id)
                    {
                        instance_count = item_id%batch_size;
                    }*/

                    // glVertexAttribPointer(0,2,GL_FLOAT,false,16,cast(void*)(i*batch_size*4*16));
                    // glVertexAttribPointer(1,2,GL_FLOAT,false,16,cast(void*)(i*batch_size*4*16+8));
                    glVertexAttribPointer(0,2,GL_SHORT,true,14,cast(void*)(draw_list[i].start*4*14));
                    glVertexAttribPointer(1,2,GL_SHORT,true,14,cast(void*)(draw_list[i].start*4*14+4));
                    glVertexAttribPointer(2,1,GL_SHORT,true,14,cast(void*)(draw_list[i].start*4*14+8));
                    glVertexAttribPointer(3,4,GL_UNSIGNED_BYTE,true,14,cast(void*)(draw_list[i].start*4*14+10));

                    glDrawElements(GL_TRIANGLES,draw_list[i].count*6,GL_UNSIGNED_SHORT,cast(void*)(draw_list[i].start*6*2));

                    //glDrawElementsBaseVertex(GL_TRIANGLES,instance_count*6,GL_UNSIGNED_SHORT,cast(void*)(i*16_384*6*2),i*16_384*4);
                }
            }
            else if(technique == Technique.ssbo_instanced || technique == Technique.instanced_attrib_divisor)
            {
                if(mesh_id != render_list[0].mesh_id)
                {
                    mesh_id = render_list[0].mesh_id;
                    GfxConfig.meshes[mesh_id].bind();
                }
                if(material_id != render_list[0].material_id)
                {
                    material_id = render_list[0].material_id;
                    GfxConfig.materials[material_id].bind();
                }
                if(texture.data != render_list[0].texture.data)
                {
                    texture.data = render_list[0].texture.data;
                    render_list[0].texture.bind();
                }
                glDrawArraysInstanced(GL_TRIANGLE_STRIP,0,4,item_id);
                //glDrawElementsInstanced(GL_TRIANGLES,6,GL_UNSIGNED_SHORT,null,item_id);
            }
            else if(instanced)
            {
                uint items = item_id/992+1;
                foreach(i; 0..items)
                {
                    if(mesh_id != render_list[i].mesh_id)
                    {
                        mesh_id = render_list[i].mesh_id;
                        GfxConfig.meshes[mesh_id].bind();
                    }
                    if(material_id != render_list[i].material_id)
                    {
                        material_id = render_list[i].material_id;
                        GfxConfig.materials[material_id].bind();
                    }
                    if(texture.data != render_list[i].texture.data)
                    {
                        texture.data = render_list[0].texture.data;
                        render_list[i].texture.bind();
                    }
                    ubos[0].bindRange(buffer_target,0,data_index,block_max_size);

                    uint instance_count = 992;
                    if(i*992 > item_id)
                    {
                        instance_count = i*992 - item_id;
                    }

                    /*if(indirect)glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, cast(void*)0, instance_count, 0);
                    else if(multi_draw)glMultiDrawElements(GL_TRIANGLES,cast(int*)multi_count.ptr,GL_UNSIGNED_SHORT,cast(void**)multi_offset.ptr,instance_count); 
                        //glMultiDrawElementsBaseVertex(GL_TRIANGLES,cast(int*)multi_count.ptr,GL_UNSIGNED_SHORT,cast(void**)multi_offset.ptr,instance_count,cast(int*)multi_offset.ptr);
                    else */glDrawElementsInstanced(GL_TRIANGLES,6,GL_UNSIGNED_SHORT,null,instance_count);
                    //glDrawArraysInstanced(GL_TRIANGLES,0,6,instance_count);
                    data_index += data_offset * 992;
                }
            }
            else
            foreach(item; render_list[0..item_id])
            {
                if(mesh_id != item.mesh_id)
                {
                    mesh_id = item.mesh_id;
                    GfxConfig.meshes[mesh_id].bind();
                }
                if(material_id != item.material_id)
                {
                    material_id = item.material_id;
                    GfxConfig.materials[material_id].bind();
                    GfxConfig.materials[material_id].pushBindings();
                }
                if(texture.data != item.texture.data)
                {
                    texture.data = render_list[0].texture.data;
                    item.texture.bind();
                }
                
                switch(technique)
                {
                    case Technique.simple:
                        /*glUniform4f(0, *cast(float*)&uniform_block[data_index], *cast(float*)&uniform_block[data_index+4], *cast(float*)&uniform_block[data_index+8], *cast(float*)&uniform_block[data_index+12]);
                        glUniform4f(1, *cast(float*)&uniform_block[data_index+16], *cast(float*)&uniform_block[data_index+20], *cast(float*)&uniform_block[data_index+24], *cast(float*)&uniform_block[data_index+28]);
                        glUniform4f(2, *cast(float*)&uniform_block[data_index+32], *cast(float*)&uniform_block[data_index+36], *cast(float*)&uniform_block[data_index+40], *cast(float*)&uniform_block[data_index+44]);
                        */
                            GfxConfig.materials[material_id].pushUniforms(&uniform_block[data_index]);break;
                    case Technique.simple_array:
                        glUniform4fv(0,12,cast(float*)(uniform_block.ptr+data_index));
                        break;
                    case Technique.uniform_buffer:
                        ubos[0].bindRange(Buffer.BindTarget.uniform,0,data_index,data_offset);
                        break;
                    /*case Technique.uniform_buffer_draw_indirect:
                         ubos[0].bindRange(Buffer.BindTarget.uniform,0,data_index,data_offset);
                         glDrawElementsIndirect(GL_TRIANGLES,GL_UNSIGNED_SHORT,null);
                         data_index += data_offset;
                         continue;*/
                    case Technique.uniform_buffer_indexed:
                        if(item_ubo_id >= 992)
                        {
                            item_ubo_id = 0;
                            ubo_start = data_index;
                            ubos[0].bindRange(Buffer.BindTarget.uniform,0,ubo_start,block_max_size);
                        }
                        glUniform1i(0,item_ubo_id++);
                        break;
                    default:break;
                }//*/

                /*version(ver3)ubos[0].bindRange(Buffer.BindTarget.uniform,0,data_index,data_offset);
                else version(ver1)
                {
                    glUniform4f(0, *cast(float*)&uniform_block[data_index], *cast(float*)&uniform_block[data_index+4], *cast(float*)&uniform_block[data_index+8], *cast(float*)&uniform_block[data_index+12]);
                    glUniform4f(1, *cast(float*)&uniform_block[data_index+16], *cast(float*)&uniform_block[data_index+20], *cast(float*)&uniform_block[data_index+24], *cast(float*)&uniform_block[data_index+28]);
                    glUniform4f(2, *cast(float*)&uniform_block[data_index+32], *cast(float*)&uniform_block[data_index+36], *cast(float*)&uniform_block[data_index+40], *cast(float*)&uniform_block[data_index+44]);
                }
                else version(ver2)glUniform4fv(0,12,cast(float*)(uniform_block.ptr+data_index));
                else version(ver4)
                {
                    if(item_ubo_id >= 992)
                    {
                        item_ubo_id = 0;
                        ubo_start = data_index;
                        ubos[0].bindRange(Buffer.BindTarget.uniform,0,ubo_start,block_max_size);
                    }
                    glUniform1i(0,item_ubo_id++);
                }//*/
                
                glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_SHORT,null);
                //glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
                data_index += data_offset;
            }
            //glEndQuery(GL_TIME_ELAPSED);
            //uint tmpq = time_queries[0];
            //time_queries[0] = time_queries[1];
            //time_queries[1] = tmpq;
            /*Buffer tmpb = ubos[0];
            ubos[0] = ubos[1];
            ubos[1] = tmpb;//*/

            data_index = 0;
            //data_offset = 0;
            item_id = 0;
            //SDL_GL_SwapWindow(sdl_window);
            //glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
            //version(ver6)ubos[0].map(0, 64*MaxObjects, Buffer.BindTarget.uniform);
            //ubos[0].map(Buffer.BindTarget.uniform);

            switch(technique)
            {
                case Technique.uniform_buffer_instanced_mapped_gl2:
                    ubos[0].map(Buffer.BindTarget.uniform);
                    //data_ptr = ubos[0].mappedPointer();
                    break;
                case Technique.uniform_buffer_instanced_mapped:
                    ubos[0].map(0, 64*MaxObjects, Buffer.BindTarget.uniform);
                    //data_ptr = ubos[0].mappedPointer();
                    break;
                default:break;
            }

            if(ubos[0].data && ubos[0].mappedPointer)
            {
                data_ptr = ubos[0].mappedPointer;
            }

            /*switch(technique)
            {
                case Technique.simple:
                case Technique.simple_array:
                case Technique.uniform_buffer:
                case Technique.uniform_buffer_indexed:
                case Technique.uniform_buffer_instanced:
                case Technique.uniform_buffer_instanced_mapped:
                case Technique.uniform_buffer_instanced_persistent_mapped:
                default:break;
            }*/
        }
        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
        glDisableVertexAttribArray(2);
        glDisableVertexAttribArray(3);
        this_.freeBlocks();
        /*glUseProgram(0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);*/
        //glDisable(GL_ALPHA_TEST);
    }

    void resize(ivec2 size)
    {
        resolution = size;
        dres = vec2(1.0/cast(float)size.x,1.0/cast(float)size.y);
    }

    void view(vec2 pos, vec2 size)
    {
        //view_pos = pos * size - 1;
        view_size = vec2(2/size.x,2/size.y);
        sdl_transform = vec4(0,0,1.0/size.x,1.0/size.y);
        view_pos = (pos - size * 0.5) * view_size;
    }

    // __gshared void function(ref Renderer this_, Texture tex, vec2 pos, vec2 size, vec4 coords, short depth, uint color, float angle, uint material_id, uint mesh_id, uint thread_id) __draw;
    __gshared void function(ref Renderer this_, scope ref const(DrawData) data) __draw;
    __gshared void function(ref Renderer this_) __present;
    __gshared void function(ref Renderer this_) __clear;
    __gshared void function(ref Renderer this_) __initialize;

    static void __loadBackend() 
    {
        //this.technique = __ecs_used_technique;
        if(technique == Technique.vbo_batch)__draw = &__draw_gl_vbo_batch;
        else __draw = &__draw_gl;
        __present = &__present_gl;
        __clear = &__clear_gl;
        __initialize = &__initialize_gl;
    }

}