revolver

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

opengl.c (34755B)


      1 //////////////////////////////////////////////////////////////////
      2 // opengl.c
      3 
      4 #define ogl_safe_set(ptr, hndl_arg) ((ptr) ? ((ptr)->hndl_arg.u) : (0))
      5 
      6 RV_INTERNAL GLenum rv_usage_to_gl(rv_buffer_usage_t usage) {
      7     GLenum mode = GL_STATIC_DRAW;
      8     switch (usage) {
      9     default: rv_unreachable();
     10     case RV_BUFFER_USAGE_STATIC:  mode = GL_STATIC_DRAW;  break;
     11     case RV_BUFFER_USAGE_DYNAMIC: mode = GL_DYNAMIC_DRAW; break;
     12     case RV_BUFFER_USAGE_STREAM:  mode = GL_STREAM_DRAW;  break;
     13     }
     14     return mode;
     15 }
     16 
     17 RV_INTERNAL GLenum rv_shader_to_gl(rv_shader_type_t type) {
     18     GLenum mode = 0;
     19     switch (type) {
     20     default: rv_unreachable();
     21     case RV_SHADER_TYPE_VERTEX:   mode = GL_VERTEX_SHADER; break;
     22     case RV_SHADER_TYPE_FRAGMENT: mode = GL_FRAGMENT_SHADER; break;
     23     }
     24     return mode;
     25 }
     26 
     27 RV_INTERNAL GLenum rv_primitive_to_gl(rv_primitive_type_t type) {
     28     GLenum res = 0;
     29     switch (type) {
     30     default: rv_unreachable();
     31     case RV_PRIMITIVE_TYPE_TRIANGLES: res = GL_TRIANGLES; break;
     32     }
     33     return res;
     34 }
     35 
     36 RV_INTERNAL s64 rv_vattr_type_size(rv_vattr_type_t type) {
     37     s64 size = 0;
     38     switch (type) {
     39     case RV_VATTR_TYPE_FLOAT4:   { size = sizeof(f32) * 4; } break;
     40     case RV_VATTR_TYPE_FLOAT3:   { size = sizeof(f32) * 3; } break;
     41     case RV_VATTR_TYPE_FLOAT2:   { size = sizeof(f32) * 2; } break;
     42     case RV_VATTR_TYPE_FLOAT:    { size = sizeof(f32) * 1; } break;
     43     case RV_VATTR_TYPE_UINT4:    { size = sizeof(u32) * 4; } break;
     44     case RV_VATTR_TYPE_UINT3:    { size = sizeof(u32) * 3; } break;
     45     case RV_VATTR_TYPE_UINT2:    { size = sizeof(u32) * 2; } break;
     46     case RV_VATTR_TYPE_UINT:     { size = sizeof(u32) * 1; } break;
     47     case RV_VATTR_TYPE_BYTE4:    { size = sizeof(u8 ) * 4; } break;
     48     case RV_VATTR_TYPE_BYTE3:    { size = sizeof(u8 ) * 3; } break;
     49     case RV_VATTR_TYPE_BYTE2:    { size = sizeof(u8 ) * 2; } break;
     50     case RV_VATTR_TYPE_BYTE:     { size = sizeof(u8 ) * 1; } break;
     51     }
     52     return size;
     53 }
     54 
     55 
     56 RV_INTERNAL GLenum rv_tex_wrap_to_gl(rv_texture_wrap_t type) {
     57     GLenum wrap = GL_REPEAT;
     58     switch (type) {
     59     default: rv_unreachable();
     60     case RV_TEXTURE_WRAP_REPEAT:           wrap = GL_REPEAT;           break;
     61     case RV_TEXTURE_WRAP_MIRRORED_REPEAT:  wrap = GL_MIRRORED_REPEAT;  break;
     62     case RV_TEXTURE_WRAP_CLAMP_TO_EDGE:    wrap = GL_CLAMP_TO_EDGE;    break;
     63     };
     64 
     65     return wrap;
     66 }
     67 
     68 
     69 GLenum rv_blend_equation_to_gl(rv_blend_equation_t eq)
     70 {
     71     GLenum beq = GL_FUNC_ADD;
     72     switch (eq) {
     73     default: rv_unreachable();
     74     case RV_BLEND_EQUATION_ADD:                beq = GL_FUNC_ADD; break;
     75     case RV_BLEND_EQUATION_SUBTRACT:           beq = GL_FUNC_SUBTRACT; break;
     76     case RV_BLEND_EQUATION_REVERSE_SUBTRACT:   beq = GL_FUNC_REVERSE_SUBTRACT; break;
     77     case RV_BLEND_EQUATION_MIN:                beq = GL_MIN; break;
     78     case RV_BLEND_EQUATION_MAX:                beq = GL_MAX; break;
     79     };
     80 
     81     return beq;
     82 }
     83 
     84 GLenum rv_blend_mode_to_gl(rv_blend_mode_t type, GLenum def)
     85 {
     86     GLenum mode = def;
     87     switch (type) {
     88     default: rv_unreachable();
     89     case RV_BLEND_MODE_DEFAULT: mode = def; break;
     90     case RV_BLEND_MODE_ZERO: mode = GL_ZERO; break;
     91     case RV_BLEND_MODE_ONE: mode = GL_ONE; break;
     92     case RV_BLEND_MODE_SRC_COLOR: mode = GL_SRC_COLOR; break;
     93     case RV_BLEND_MODE_ONE_MINUS_SRC_COLOR: mode = GL_ONE_MINUS_SRC_COLOR; break;
     94     case RV_BLEND_MODE_DST_COLOR: mode = GL_DST_COLOR; break;
     95     case RV_BLEND_MODE_ONE_MINUS_DST_COLOR: mode = GL_ONE_MINUS_DST_COLOR; break;
     96     case RV_BLEND_MODE_SRC_ALPHA: mode = GL_SRC_ALPHA; break;
     97     case RV_BLEND_MODE_ONE_MINUS_SRC_ALPHA: mode = GL_ONE_MINUS_SRC_ALPHA; break;
     98     case RV_BLEND_MODE_DST_ALPHA: mode = GL_DST_ALPHA; break;
     99     case RV_BLEND_MODE_ONE_MINUS_DST_ALPHA: mode = GL_ONE_MINUS_DST_ALPHA; break;
    100     case RV_BLEND_MODE_CONSTANT_COLOR: mode = GL_CONSTANT_COLOR; break;
    101     case RV_BLEND_MODE_ONE_MINUS_CONSTANT_COLOR: mode = GL_ONE_MINUS_CONSTANT_COLOR; break;
    102     case RV_BLEND_MODE_CONSTANT_ALPHA: mode = GL_CONSTANT_ALPHA; break;
    103     case RV_BLEND_MODE_ONE_MINUS_CONSTANT_ALPHA: mode = GL_ONE_MINUS_CONSTANT_ALPHA; break;
    104     }
    105     return mode;
    106 }
    107 
    108 RV_INTERNAL struct {
    109     GLuint vao;
    110     bool32 is_init;
    111 
    112     rv_texture_t default_tex;
    113 
    114     rv_ibo_t* bound_ibo;
    115     rv_pipeline_t* bound_pip;
    116     rv_framebuffer_t* bound_fbuf;
    117 } ogl_ctx;
    118 
    119 void ogl_bind_fbuf(rv_framebuffer_t* fbuf) {
    120     ogl_ctx.bound_fbuf = fbuf;
    121     glBindFramebuffer(GL_FRAMEBUFFER, ogl_safe_set(fbuf, handle));
    122     if (fbuf) {
    123         s32 r = 0;
    124         for (rv_texture_node_t* c = fbuf->color_attachement_first; c; c = c->next, r++) {
    125             if (c->tex) {
    126                 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + r, GL_TEXTURE_2D, c->tex->handle.u, 0);
    127             } else {
    128                 rv_unreachable();
    129             }
    130         }
    131     }
    132 }
    133 
    134 
    135 RV_INTERNAL void ogl_handle_command(rv_command_t* c);
    136 
    137 RV_GLOBAL void rv_window_render_commit(rv_window_handle_t* window, rv_render_pass_list_t* passes)
    138 {
    139     rv_window_render_begin(window);
    140 
    141     rv_temp_arena scratch = rv_scratch_begin(0, 0);
    142 
    143     rv_render_pass_t* initial_rpass = passes->first;
    144 
    145     if (!ogl_ctx.is_init) {
    146         ogl_ctx.is_init = true;
    147 
    148         glGenVertexArrays(1, &ogl_ctx.vao);
    149         glBindVertexArray(ogl_ctx.vao);
    150 
    151 
    152 
    153         rv_render_pass_t* extra_initial_rpass = rv_push(scratch.arena, rv_render_pass_t);
    154         extra_initial_rpass->next = initial_rpass;
    155         initial_rpass = extra_initial_rpass;
    156 
    157         ogl_ctx.default_tex = (rv_texture_t){};
    158         rv_get_default_texture(&ogl_ctx.default_tex.data, &ogl_ctx.default_tex.size);
    159         rv_cmd_push_obj(scratch.arena, &extra_initial_rpass->commands, RV_COMMAND_OBJ_TEXTURE,  RV_RENDER_OBJ_OP_CREATE, &ogl_ctx.default_tex);
    160     }
    161 
    162 
    163     for (rv_render_pass_t* p = initial_rpass; p; p = p->next) {
    164         { // unbind everything
    165 
    166             if (ogl_ctx.bound_ibo) {
    167                 ogl_ctx.bound_ibo = NULL;
    168                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    169             }
    170 
    171             if (ogl_ctx.bound_pip) {
    172                 ogl_ctx.bound_pip = NULL;
    173                 glUseProgram(0);
    174             }
    175 
    176             if (ogl_ctx.bound_fbuf || p->framebuffer) {
    177                 ogl_bind_fbuf(p->framebuffer);
    178             }
    179         }
    180 
    181         for (rv_command_t* c = p->commands.first; c; c = c->next) {
    182             ogl_handle_command(c);
    183         }
    184     }
    185 
    186     rv_scratch_end(scratch);
    187 
    188     rv_window_render_end(window);
    189 }
    190 
    191 RV_INTERNAL void ogl_handle_command(rv_command_t* c) {
    192     switch (c->type) {
    193     case RV_COMMAND_INVALID: {
    194         rv_unreachable();
    195     } break;
    196 
    197     case RV_COMMAND_CUSTOM: {
    198         rv_assert(c->custom.func);
    199         if (c->custom.func) {
    200             c->custom.func(c->custom.data);
    201         }
    202     } break;
    203 
    204     case RV_COMMAND_COMMAND_LIST: {
    205         for (rv_command_t* cc = c->command_list.first; cc; cc = cc->next) {
    206             ogl_handle_command(cc);
    207         }
    208     } break;
    209 
    210     case RV_COMMAND_CLEAR: {
    211         for (rv_render_clear_desc_t* clear = c->clear.first; clear; clear = clear->next) {
    212             if (clear->flags == RV_RENDER_CLEAR_FLAG_NONE) continue;
    213 
    214             int opengl_clear_mask = 0;
    215             if (clear->flags & RV_RENDER_CLEAR_FLAG_COLOR)   {
    216                 glClearColor(clear->color[0], clear->color[1], clear->color[2], clear->color[3]);
    217                 opengl_clear_mask |= GL_COLOR_BUFFER_BIT;
    218             }
    219             if (clear->flags & RV_RENDER_CLEAR_FLAG_DEPTH)   opengl_clear_mask |= GL_DEPTH_BUFFER_BIT;
    220             if (clear->flags & RV_RENDER_CLEAR_FLAG_STENCIL) opengl_clear_mask |= GL_STENCIL_BUFFER_BIT;
    221 
    222             glClear(opengl_clear_mask);
    223         }
    224     } break;
    225 
    226     case RV_COMMAND_SET_VIEWPORT: {
    227         glViewport(c->viewport.x, c->viewport.y, c->viewport.z, c->viewport.w);
    228     } break;
    229 
    230     case RV_COMMAND_OBJ_VERTEX: {
    231         rv_vbo_t* vbo = c->obj.vbo;
    232         switch (c->obj.operation) {
    233         case RV_RENDER_OBJ_OP_CREATE: {
    234             rv_assert(!ogl_ctx.bound_pip);
    235 
    236             glGenBuffers(1, &vbo->handle.u);
    237             glBindBuffer(GL_ARRAY_BUFFER, vbo->handle.u);
    238             glBufferData(GL_ARRAY_BUFFER, vbo->size, vbo->data, rv_usage_to_gl(vbo->usage));
    239             glBindBuffer(GL_ARRAY_BUFFER, 0);
    240         } break;
    241 
    242         case RV_RENDER_OBJ_OP_UPDATE: {
    243             rv_assert(!ogl_ctx.bound_pip);
    244 
    245             glBindBuffer(GL_ARRAY_BUFFER, vbo->handle.u);
    246             glBufferData(GL_ARRAY_BUFFER, c->obj.vbo_update.size, c->obj.vbo_update.data, rv_usage_to_gl(vbo->usage));
    247             glBindBuffer(GL_ARRAY_BUFFER, 0);
    248         } break;
    249 
    250         case RV_RENDER_OBJ_OP_BIND: {
    251             if (ogl_ctx.bound_pip) {
    252                 s32 bind_index = c->obj.vbo_bind.bind_index;
    253                 s32 base_offset = c->obj.vbo_bind.base_offset;
    254                 rv_vattr_bind_t* vattr_bind_override_list = c->obj.vbo_bind.vattr_override_first;
    255                 glBindBuffer(GL_ARRAY_BUFFER, vbo->handle.u);
    256 
    257                 // set vattrs
    258                 s32 auto_stride = 0;
    259                 for (rv_vattr_t* v = ogl_ctx.bound_pip->vattr_first; v; v = v->next) {
    260                     if (v->bind_index == bind_index) {
    261                         bool32 found_override = false;
    262                         for (rv_vattr_bind_t* vo = vattr_bind_override_list; vo; vo = vo->next) {
    263                             if (vo->desc == v) {
    264                                 found_override = true;
    265                                 break;
    266                             }
    267                         }
    268                         if (!found_override)
    269                             auto_stride += rv_vattr_type_size(v->type);
    270                     }
    271                 }
    272                 s32 auto_offset = 0;
    273                 s32 i = 0;
    274                 for (rv_vattr_t* v = ogl_ctx.bound_pip->vattr_first; v; v = v->next, i++) {
    275                     if (v->bind_index == bind_index) {
    276                         glEnableVertexAttribArray(i);
    277                         s32 offset = auto_offset;
    278                         s32 stride = auto_stride;
    279                         s32 divisor = v->divisor;
    280                         bool32 found_override = false;
    281                         for (rv_vattr_bind_t* vo = vattr_bind_override_list; vo; vo = vo->next) {
    282                             if (vo->desc == v) {
    283                                 offset = vo->offset;
    284                                 stride = vo->stride;
    285                                 divisor = vo->divisor;
    286                                 break;
    287                             }
    288                         }
    289                         switch (v->type) {
    290                         case RV_VATTR_TYPE_FLOAT4: glVertexAttribPointer(i,  4, GL_FLOAT,        GL_FALSE, stride, (void*)(s64)(base_offset + offset)); break;
    291                         case RV_VATTR_TYPE_FLOAT3: glVertexAttribPointer(i,  3, GL_FLOAT,        GL_FALSE, stride, (void*)(s64)(base_offset + offset)); break;
    292                         case RV_VATTR_TYPE_FLOAT2: glVertexAttribPointer(i,  2, GL_FLOAT,        GL_FALSE, stride, (void*)(s64)(base_offset + offset)); break;
    293                         case RV_VATTR_TYPE_FLOAT:  glVertexAttribPointer(i,  1, GL_FLOAT,        GL_FALSE, stride, (void*)(s64)(base_offset + offset)); break;
    294                         case RV_VATTR_TYPE_UINT4:  glVertexAttribIPointer(i, 4, GL_UNSIGNED_INT,           stride, (void*)(s64)(base_offset + offset)); break;
    295                         case RV_VATTR_TYPE_UINT3:  glVertexAttribIPointer(i, 3, GL_UNSIGNED_INT,           stride, (void*)(s64)(base_offset + offset)); break;
    296                         case RV_VATTR_TYPE_UINT2:  glVertexAttribIPointer(i, 2, GL_UNSIGNED_INT,           stride, (void*)(s64)(base_offset + offset)); break;
    297                         case RV_VATTR_TYPE_UINT:   glVertexAttribIPointer(i, 1, GL_UNSIGNED_INT,           stride, (void*)(s64)(base_offset + offset)); break;
    298                         case RV_VATTR_TYPE_BYTE:   glVertexAttribPointer(i,  1, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)(s64)(base_offset + offset)); break;
    299                         case RV_VATTR_TYPE_BYTE2:  glVertexAttribPointer(i,  2, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)(s64)(base_offset + offset)); break;
    300                         case RV_VATTR_TYPE_BYTE3:  glVertexAttribPointer(i,  3, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)(s64)(base_offset + offset)); break;
    301                         case RV_VATTR_TYPE_BYTE4:  glVertexAttribPointer(i,  4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void*)(s64)(base_offset + offset)); break;
    302                         default: rv_unreachable(); break;
    303                         }
    304                         if (!found_override)
    305                             auto_offset += rv_vattr_type_size(v->type);
    306                         if (v->divisor) {
    307                             glVertexAttribDivisor(i, v->divisor);
    308                         }
    309                     }
    310                 }
    311             } else {
    312                 rv_unreachable();
    313             }
    314         } break;
    315 
    316         case RV_RENDER_OBJ_OP_DESTROY: {
    317             rv_assert(!ogl_ctx.bound_pip);
    318 
    319             glDeleteBuffers(1, &vbo->handle.u);
    320             vbo->handle.u = 0;
    321         } break;
    322         }
    323     } break;
    324 
    325     case RV_COMMAND_OBJ_INDEX: {
    326         rv_ibo_t* ibo = c->obj.ibo;
    327         switch (c->obj.operation) {
    328         case RV_RENDER_OBJ_OP_CREATE: {
    329             rv_assert(!ogl_ctx.bound_pip);
    330 
    331             glGenBuffers(1, &ibo->handle.u);
    332             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo->handle.u);
    333             glBufferData(GL_ELEMENT_ARRAY_BUFFER, ibo->size, ibo->data, rv_usage_to_gl(ibo->usage));
    334 
    335             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ogl_safe_set(ogl_ctx.bound_ibo, handle));
    336         } break;
    337 
    338         case RV_RENDER_OBJ_OP_UPDATE: {
    339             rv_assert(!ogl_ctx.bound_pip);
    340 
    341             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo->handle.u);
    342             glBufferData(GL_ELEMENT_ARRAY_BUFFER, c->obj.ibo_update.size, c->obj.ibo_update.data, rv_usage_to_gl(ibo->usage));
    343 
    344             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ogl_safe_set(ogl_ctx.bound_ibo, handle));
    345         } break;
    346 
    347         case RV_RENDER_OBJ_OP_BIND: {
    348             ogl_ctx.bound_ibo = ibo;
    349             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ogl_safe_set(ogl_ctx.bound_ibo, handle));
    350         } break;
    351 
    352         case RV_RENDER_OBJ_OP_DESTROY: {
    353             rv_assert(!ogl_ctx.bound_pip);
    354 
    355             glDeleteBuffers(1, &ibo->handle.u);
    356             ibo->handle.u = 0;
    357             if (ogl_ctx.bound_ibo == ibo) {
    358                 ogl_ctx.bound_ibo = NULL;
    359                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    360             }
    361         } break;
    362         }
    363     } break;
    364 
    365     case RV_COMMAND_OBJ_SHADER: {
    366         rv_shader_t* shader = c->obj.shader;
    367         switch (c->obj.operation) {
    368         case RV_RENDER_OBJ_OP_CREATE: {
    369             shader->handle.u = glCreateShader(rv_shader_to_gl(shader->type));
    370             glShaderSource(shader->handle.u, 1, (const char* const*)&shader->source.str, (s32*)&shader->source.len);
    371             glCompileShader(shader->handle.u);
    372 
    373             { // check for errors
    374                 int  shader_compile_success;
    375                 glGetShaderiv(shader->handle.u, GL_COMPILE_STATUS, &shader_compile_success);
    376 
    377                 if (!shader_compile_success) {
    378                     char info_log[512];
    379                     glGetShaderInfoLog(shader->handle.u, 512, NULL, info_log);
    380                     fprintf(stderr, "-------------ERROR------------\n"
    381                             "::OpenGL Failed to compile shader::\n%s\n", info_log);
    382                     fprintf(stderr, "-------------SOURCE------------\n");
    383                     fprintf(stderr, "%.*s\n", rv_s8v(shader->source));
    384                     fprintf(stderr, "\n------------END_SOURCE----------\n");
    385                     rv_assert(shader_compile_success);
    386                 }
    387             }
    388         } break;
    389 
    390         case RV_RENDER_OBJ_OP_UPDATE: {
    391             rv_unreachable();
    392         } break;
    393 
    394         case RV_RENDER_OBJ_OP_BIND: {
    395             rv_unreachable();
    396         } break;
    397 
    398         case RV_RENDER_OBJ_OP_DESTROY: {
    399             glDeleteShader(shader->handle.u);
    400             shader->handle.u = 0;
    401         } break;
    402         }
    403     } break;
    404 
    405     case RV_COMMAND_OBJ_PIPELINE: {
    406         rv_pipeline_t* pipeline = c->obj.pipeline;
    407         switch (c->obj.operation) {
    408         case RV_RENDER_OBJ_OP_CREATE: {
    409             pipeline->handle.u = glCreateProgram();
    410             for (rv_shader_node_t* s = pipeline->shader_first; s; s = s->next) {
    411                 glAttachShader(pipeline->handle.u, s->shader->handle.u);
    412             }
    413             glLinkProgram(pipeline->handle.u);
    414 
    415             {
    416                 int  program_link_success;
    417                 glGetProgramiv(pipeline->handle.u, GL_LINK_STATUS, &program_link_success);
    418 
    419                 if (!program_link_success) {
    420                     char info_log[512];
    421                     glGetProgramInfoLog(pipeline->handle.u, 512, NULL, info_log);
    422                     fprintf(stderr, "-------------ERROR------------\n"
    423                             "::OpenGL Failed to link shader program::\n%s\n", info_log);
    424                     rv_assert(program_link_success);
    425                 }
    426             }
    427 
    428             // find uniform locations
    429             for (rv_uniform_t* u = pipeline->uniform_desc_first; u; u = u->next) {
    430                 u->pipeline_uniform_handle.u = glGetUniformLocation(pipeline->handle.u, (const char*)u->name_null_terminated.str);
    431             }
    432 
    433             glUseProgram(pipeline->handle.u);
    434 
    435             // set up sampler channels
    436             s32 sampler_channel = 0;
    437             for (rv_uniform_t* u = pipeline->uniform_desc_first; u; u = u->next) {
    438                 if (u->type == RV_UNIFORM_TEX) {
    439                     glUniform1i(u->pipeline_uniform_handle.u, sampler_channel);
    440                     sampler_channel++;
    441                 }
    442             }
    443             // turn back to previous state
    444             glUseProgram(ogl_safe_set(ogl_ctx.bound_pip, handle));
    445         } break;
    446 
    447         case RV_RENDER_OBJ_OP_UPDATE: {
    448             rv_unreachable();
    449         } break;
    450 
    451         case RV_RENDER_OBJ_OP_BIND: {
    452             ogl_ctx.bound_pip = pipeline;
    453             if (pipeline) {
    454                 glUseProgram(pipeline->handle.u);
    455 
    456                 if (pipeline->disable_blend) {
    457                     glDisable(GL_BLEND);
    458                 } else {
    459                     glEnable(GL_BLEND);
    460                     glBlendEquation(rv_blend_equation_to_gl(pipeline->blend_equation));
    461                     glBlendFunc(rv_blend_mode_to_gl(pipeline->blend_src, GL_SRC_ALPHA), rv_blend_mode_to_gl(pipeline->blend_dst, GL_ONE_MINUS_SRC_ALPHA));
    462                 }
    463 
    464                 // activate textures and sample2d, if any
    465                 s32 sampler_channel = 0;
    466                 for (rv_uniform_t* u = pipeline->uniform_desc_first; u; u = u->next) {
    467                     if (u->type == RV_UNIFORM_TEX) {
    468                         bool32 has_queued_update = false;
    469                         {
    470                             for (rv_command_t* c_future = c->next; c_future; c_future = c_future->next) {
    471                                 if (c_future->type == RV_COMMAND_DRAW || c_future->type == RV_COMMAND_OBJ_PIPELINE) {
    472                                     // reached end...
    473                                     break;
    474                                 }
    475                                 if (c_future->type == RV_COMMAND_UNIFORM_UPDATE) {
    476                                     if (c_future->uniform_update.desc == u) {
    477                                         // found something that updates this uniform
    478                                         has_queued_update = true;
    479                                         break;
    480                                     }
    481                                 }
    482                             }
    483                         }
    484                         if (!has_queued_update) {
    485                             glActiveTexture(GL_TEXTURE0 + sampler_channel);
    486                             if (u->last_val.v_tex) {
    487                                 glBindTexture(GL_TEXTURE_2D, u->last_val.v_tex->handle.u);
    488                             } else {
    489                                 glBindTexture(GL_TEXTURE_2D, ogl_ctx.default_tex.handle.u);
    490                             }
    491                         }
    492                         sampler_channel++;
    493                     }
    494                 }
    495             } else {
    496                 glUseProgram(0);
    497             }
    498         } break;
    499 
    500         case RV_RENDER_OBJ_OP_DESTROY: {
    501             glDeleteProgram(pipeline->handle.u);
    502             pipeline->handle.u = 0;
    503             for (rv_uniform_t* u = pipeline->uniform_desc_first; u; u = u->next) {
    504                 u->pipeline_uniform_handle.u = 0;
    505             }
    506             if (ogl_ctx.bound_pip == pipeline) {
    507                 ogl_ctx.bound_pip = NULL;
    508                 glUseProgram(0);
    509             }
    510         } break;
    511         }
    512     } break;
    513 
    514     case RV_COMMAND_OBJ_TEXTURE: {
    515         rv_texture_t* tex = c->obj.tex;
    516         switch (c->obj.operation) {
    517         case RV_RENDER_OBJ_OP_CREATE: {
    518             glGenTextures(1, &tex->handle.u);
    519 
    520             s32 sampler_channel = 0;
    521             if (ogl_ctx.bound_pip) {
    522                 // need to update the existing bound sampler
    523                 // ... or make sure we don't override a bound one
    524                 for (rv_uniform_t* u = ogl_ctx.bound_pip->uniform_desc_first; u; u = u->next) {
    525                     if (u->type == RV_UNIFORM_TEX) {
    526                         if (u->last_val.v_tex == tex) {
    527                             // found our texture
    528                             break;
    529                         }
    530                         sampler_channel++;
    531                     }
    532                 }
    533             }
    534             glActiveTexture(GL_TEXTURE0 + sampler_channel);
    535             glBindTexture(GL_TEXTURE_2D, tex->handle.u);
    536 
    537             switch(tex->format)
    538             {
    539             case RV_TEXTURE_FORMAT_A8:                 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA,              tex->size.x, tex->size.y, 0, GL_ALPHA,           GL_UNSIGNED_BYTE,                tex->data); break;
    540             case RV_TEXTURE_FORMAT_R8:                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RED,                tex->size.x, tex->size.y, 0, GL_RED,             GL_UNSIGNED_BYTE,                tex->data); break;
    541             case RV_TEXTURE_FORMAT_RGB8:               glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8,               tex->size.x, tex->size.y, 0, GL_RGB,             GL_UNSIGNED_BYTE,                tex->data); break;
    542             case RV_TEXTURE_FORMAT_RGBA8:              glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,              tex->size.x, tex->size.y, 0, GL_RGBA,            GL_UNSIGNED_BYTE,                tex->data); break;
    543             case RV_TEXTURE_FORMAT_RGBA16F:            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F,            tex->size.x, tex->size.y, 0, GL_RGBA,            GL_FLOAT,                        tex->data); break;
    544             case RV_TEXTURE_FORMAT_RGBA32F:            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F,            tex->size.x, tex->size.y, 0, GL_RGBA,            GL_FLOAT,                        tex->data); break;
    545             case RV_TEXTURE_FORMAT_DEPTH8:             glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,    tex->size.x, tex->size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT,                        tex->data); break;
    546             case RV_TEXTURE_FORMAT_DEPTH16:            glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16,  tex->size.x, tex->size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT,                        tex->data); break;
    547             case RV_TEXTURE_FORMAT_DEPTH24:            glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24,  tex->size.x, tex->size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT,                        tex->data); break;
    548             case RV_TEXTURE_FORMAT_DEPTH32F:           glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, tex->size.x, tex->size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT,                        tex->data); break;
    549             case RV_TEXTURE_FORMAT_DEPTH24_STENCIL8:   glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8,   tex->size.x, tex->size.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8,              tex->data); break;
    550             case RV_TEXTURE_FORMAT_DEPTH32F_STENCIL8:  glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH32F_STENCIL8,  tex->size.x, tex->size.y, 0, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, tex->data); break;
    551             default: rv_unreachable(); break;
    552             }
    553 
    554             GLenum mag_filter = tex->mag_filter == RV_TEXTURE_FILTER_NEAREST ? GL_NEAREST : GL_LINEAR;
    555             GLenum min_filter = tex->min_filter == RV_TEXTURE_FILTER_NEAREST ? GL_NEAREST : GL_LINEAR;
    556 
    557             if (tex->num_mips) {
    558                 if (tex->min_filter == RV_TEXTURE_FILTER_NEAREST) {
    559                     min_filter = tex->mip_filter == RV_TEXTURE_FILTER_NEAREST ? GL_NEAREST_MIPMAP_NEAREST :
    560                         GL_NEAREST_MIPMAP_LINEAR;
    561                 }
    562                 else {
    563                     min_filter = tex->mip_filter == RV_TEXTURE_FILTER_NEAREST ? GL_LINEAR_MIPMAP_NEAREST :
    564                         GL_NEAREST_MIPMAP_LINEAR;
    565                 }
    566             }
    567 
    568             GLenum texture_wrap_s = rv_tex_wrap_to_gl(tex->wrap_s);
    569             GLenum texture_wrap_t = rv_tex_wrap_to_gl(tex->wrap_t);
    570 
    571             if (tex->num_mips) {
    572                 glGenerateMipmap(GL_TEXTURE_2D);
    573             }
    574 
    575             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texture_wrap_s);
    576             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texture_wrap_t);
    577             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
    578             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
    579 
    580         } break;
    581 
    582         case RV_RENDER_OBJ_OP_UPDATE: {
    583             s32 sampler_channel = 0;
    584             if (ogl_ctx.bound_pip) {
    585                 // need to update the existing bound sampler
    586                 // ... or make sure we don't override a bound one
    587                 for (rv_uniform_t* u = ogl_ctx.bound_pip->uniform_desc_first; u; u = u->next) {
    588                     if (u->type == RV_UNIFORM_TEX) {
    589                         if (u ->last_val.v_tex == tex) {
    590                             // found our texture
    591                             break;
    592                         }
    593                         sampler_channel++;
    594                     }
    595                 }
    596             }
    597             glActiveTexture(GL_TEXTURE0 + sampler_channel);
    598             glBindTexture(GL_TEXTURE_2D, tex->handle.u);
    599 
    600             switch(tex->format)
    601             {
    602             case RV_TEXTURE_FORMAT_A8:                 glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h,  GL_ALPHA,           GL_UNSIGNED_BYTE,                c->obj.tex_update.data); break;
    603             case RV_TEXTURE_FORMAT_R8:                 glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h,  GL_RED,             GL_UNSIGNED_BYTE,                c->obj.tex_update.data); break;
    604             case RV_TEXTURE_FORMAT_RGB8:               glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h,  GL_RGB,             GL_UNSIGNED_BYTE,                c->obj.tex_update.data); break;
    605             case RV_TEXTURE_FORMAT_RGBA8:              glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h,  GL_RGBA,            GL_UNSIGNED_BYTE,                c->obj.tex_update.data); break;
    606             case RV_TEXTURE_FORMAT_RGBA16F:            glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h,  GL_RGBA,            GL_FLOAT,                        c->obj.tex_update.data); break;
    607             case RV_TEXTURE_FORMAT_RGBA32F:            glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h,  GL_RGBA,            GL_FLOAT,                        c->obj.tex_update.data); break;
    608             case RV_TEXTURE_FORMAT_DEPTH8:             glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h,  GL_DEPTH_COMPONENT, GL_FLOAT,                        c->obj.tex_update.data); break;
    609             case RV_TEXTURE_FORMAT_DEPTH16:            glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h,  GL_DEPTH_COMPONENT, GL_FLOAT,                        c->obj.tex_update.data); break;
    610             case RV_TEXTURE_FORMAT_DEPTH24:            glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h,  GL_DEPTH_COMPONENT, GL_FLOAT,                        c->obj.tex_update.data); break;
    611             case RV_TEXTURE_FORMAT_DEPTH32F:           glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h,  GL_DEPTH_COMPONENT, GL_FLOAT,                        c->obj.tex_update.data); break;
    612             case RV_TEXTURE_FORMAT_DEPTH24_STENCIL8:   glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h,  GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8,              c->obj.tex_update.data); break;
    613             case RV_TEXTURE_FORMAT_DEPTH32F_STENCIL8:  glTexSubImage2D(GL_TEXTURE_2D, 0, c->obj.tex_update.sub_part.x, c->obj.tex_update.sub_part.y, c->obj.tex_update.sub_part.w, c->obj.tex_update.sub_part.h,  GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, c->obj.tex_update.data); break;
    614             default: rv_unreachable(); break;
    615             }
    616         } break;
    617 
    618         case RV_RENDER_OBJ_OP_BIND: {
    619             rv_unreachable(); // textures are bound automatically by uniforms
    620         } break;
    621 
    622         case RV_RENDER_OBJ_OP_DESTROY: {
    623             glDeleteTextures(1, &tex->handle.u);
    624             tex->handle.u = 0;
    625         } break;
    626         }
    627     } break;
    628 
    629     case RV_COMMAND_UNIFORM_UPDATE: {
    630         if (ogl_ctx.bound_pip) {
    631             rv_uniform_t* desc = c->uniform_update.desc;
    632             rv_uniform_variant_t* val = &c->uniform_update.value;
    633             if (desc) {
    634                 // try to be smart about not re-uploading uniforms that haven't changed
    635                 if (!desc->has_been_set || !rv_mem_eq(&desc->last_val, val, sizeof(*val))) {
    636                     desc->last_val = *val;
    637                     desc->has_been_set = true;
    638 
    639                     switch (desc->type) {
    640                     case RV_UNIFORM_F32:  glUniform1fv(desc->pipeline_uniform_handle.u, 1, &val->v_f32); break;
    641                     case RV_UNIFORM_VEC2: glUniform2fv(desc->pipeline_uniform_handle.u, 1, val->v_vec2.xy); break;
    642                     case RV_UNIFORM_VEC3: glUniform3fv(desc->pipeline_uniform_handle.u, 1, val->v_vec3.xyz); break;
    643                     case RV_UNIFORM_VEC4: glUniform4fv(desc->pipeline_uniform_handle.u, 1, val->v_vec4.xyzw); break;
    644                     case RV_UNIFORM_MAT4: glUniformMatrix4fv(desc->pipeline_uniform_handle.u, 1, GL_FALSE, val->v_mat4.elements); break;
    645                     case RV_UNIFORM_TEX:      {
    646                         s32 sampler_channel = 0;
    647                         for (rv_uniform_t* u = ogl_ctx.bound_pip->uniform_desc_first; u; u = u->next) {
    648                             if (u->type == RV_UNIFORM_TEX) {
    649                                 if (u == desc) {
    650                                     // found our uniform, bind it
    651                                     glActiveTexture(GL_TEXTURE0 + sampler_channel);
    652                                     if (u->last_val.v_tex) {
    653                                         glBindTexture(GL_TEXTURE_2D, u->last_val.v_tex->handle.u);
    654                                     } else {
    655                                         glBindTexture(GL_TEXTURE_2D, ogl_ctx.default_tex.handle.u);
    656                                     }
    657                                     break;
    658                                 }
    659                                 sampler_channel++;
    660                             }
    661                         }
    662 
    663                     } break;
    664                     case RV_UNIFORM_INVALID: rv_unreachable(); break;
    665                     }
    666                 }
    667             } else {
    668                 rv_unreachable();
    669             }
    670         } else {
    671             rv_unreachable();
    672         }
    673     } break;
    674 
    675     case RV_COMMAND_DRAW: {
    676         if (ogl_ctx.bound_pip) {
    677             GLenum prim = rv_primitive_to_gl(c->draw.primitive);
    678             // TODO(Samdal): instancing...
    679             if (ogl_ctx.bound_ibo) {
    680 
    681                 GLenum itype = GL_UNSIGNED_INT;
    682                 switch (ogl_ctx.bound_ibo->elem_size) {
    683                 default: rv_unreachable();
    684                 case 4: itype = GL_UNSIGNED_INT;   break;
    685                 case 2: itype = GL_UNSIGNED_SHORT; break;
    686                 case 1: itype = GL_UNSIGNED_BYTE;  break;
    687                 }
    688                 if (c->draw.instances) {
    689                     glDrawElementsInstanced(prim, c->draw.count, itype, (void*)(c->draw.first * ogl_ctx.bound_ibo->elem_size), c->draw.instances);
    690                 } else {
    691                     glDrawElements(prim, c->draw.count, itype, (void*)(c->draw.first * ogl_ctx.bound_ibo->elem_size));
    692                 }
    693             } else {
    694                 if (c->draw.instances) {
    695                     glDrawArraysInstanced(prim, c->draw.first, c->draw.count, c->draw.instances);
    696                 } else {
    697                     glDrawArrays(prim, c->draw.first, c->draw.count);
    698                 }
    699             }
    700         } else {
    701             rv_unreachable();
    702         }
    703     } break;
    704 
    705     case RV_COMMAND_OBJ_FRAMEBUFFER: {
    706         rv_framebuffer_t* fbuf = c->obj.framebuffer;
    707         switch (c->obj.operation) {
    708         case RV_RENDER_OBJ_OP_CREATE: {
    709             glGenFramebuffers(1, &fbuf->handle.u);
    710         } break;
    711 
    712         case RV_RENDER_OBJ_OP_UPDATE: {
    713             rv_unreachable();
    714         } break;
    715 
    716         case RV_RENDER_OBJ_OP_BIND: {
    717             ogl_bind_fbuf(fbuf);
    718         } break;
    719 
    720         case RV_RENDER_OBJ_OP_DESTROY: {
    721             glDeleteFramebuffers(1, &fbuf->handle.u);
    722             if (ogl_ctx.bound_fbuf == fbuf) {
    723                 ogl_ctx.bound_fbuf = NULL;
    724                 glBindFramebuffer(GL_FRAMEBUFFER, 0);
    725             }
    726         } break;
    727         }
    728     } break;
    729     }
    730 }