// Copyright (C) 2022 RozK // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "../render.hpp" #include "render_opengles.hpp" #include "../display/display_glx.hpp" #include #include typedef void (*rk_DrawElementsInstancedBaseInstanceFunc)(rk_uint, rk_uint, rk_uint, const void *, rk_uint, rk_uint); typedef void (*rk_MultiDrawElementsIndirectFunc)(rk_uint, rk_uint, const void *, rk_uint, rk_uint); static rk_DrawElementsInstancedBaseInstanceFunc rk_DrawElementsInstancedBaseInstance = nullptr; static rk_MultiDrawElementsIndirectFunc rk_MultiDrawElementsIndirect = nullptr; static rk_shader const * rk_current_shader = nullptr; static rk_vertices const * rk_current_vertices = nullptr; static void rk_gl_printf(char const * message) { printf("[GL] %s\n", message); } static void rk_printf(char const * message) { printf("[RK] %s\n", message); } #define rk_gl_error(_message) { if (glGetError() != GL_NO_ERROR) { printf("[GL] %s\n", (_message)); } } static void rk_debug_message_callback( GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, GLchar const * message, void const * userParam) { printf("[GL] (id=%d) %s\n", id, message); } void rk_render_initialize() { GLubyte const * const vendor = glGetString(GL_VENDOR); GLubyte const * const renderer = glGetString(GL_RENDERER); printf("[GL] vendor: %s, renderer: %s\n", vendor, renderer); GLubyte const * const version = glGetString(GL_VERSION); GLubyte const * const language = glGetString(GL_SHADING_LANGUAGE_VERSION); printf("[GL] version: %s, language: %s\n", version, language); glDebugMessageCallback(rk_debug_message_callback, nullptr); glEnable(GL_DEBUG_OUTPUT); char const * const gl_exts = reinterpret_cast(glGetString(GL_EXTENSIONS)); // printf("[GL] %s\n", gl_exts); rk_DrawElementsInstancedBaseInstance = reinterpret_cast( rk_resolve_extension(gl_exts, "GL_EXT_base_instance", "DrawElementsInstancedBaseInstance")); if (rk_DrawElementsInstancedBaseInstance) { rk_gl_printf("Using extension GL_EXT_base_instance::DrawElementsInstancedBaseInstance."); rk_MultiDrawElementsIndirect = reinterpret_cast( rk_resolve_extension(gl_exts, "GL_EXT_multi_draw_indirect", "MultiDrawElementsIndirectEXT")); if (rk_MultiDrawElementsIndirect) { rk_gl_printf("Using extension GL_EXT_multi_draw_indirect::MultiDrawElementsIndirectEXT."); } } glDisable(GL_BLEND); glEnable(GL_DITHER); glEnable(GL_DEPTH_TEST); glDisable(GL_SCISSOR_TEST); glDisable(GL_STENCIL_TEST); glEnable(GL_CULL_FACE); glFrontFace(GL_CCW); glCullFace(GL_BACK); glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); } void rk_render_terminate() { } static void rk_print_shader_infolog(GLuint shader) { GLsizei length; char infolog[1024]; glGetShaderInfoLog(shader, sizeof(infolog), &length, infolog); if (length > 0) { rk_printf(infolog); } } static void rk_print_program_infolog(GLuint program) { GLsizei length; char infolog[1024]; glGetProgramInfoLog(program, sizeof(infolog), &length, infolog); if (length > 0) { rk_printf(infolog); } } //TODO: error handling rk_shader_t rk_load_shader( rk_uint const vert_nlines, char const ** const vert_lines, rk_uint const frag_nlines, char const ** const frag_lines) { rk_shader * const shader = new rk_shader; shader->vertex = glCreateShader(GL_VERTEX_SHADER); shader->fragment = glCreateShader(GL_FRAGMENT_SHADER); shader->program = glCreateProgram(); if (!vert_nlines || !vert_lines) { rk_printf("Missing vertex shader."); return nullptr; } if (!frag_nlines || !frag_lines) { rk_printf("Missing fragment shader."); return nullptr; } rk_printf("Compiling vertex shader..."); glShaderSource(shader->vertex, vert_nlines, vert_lines, nullptr); glCompileShader(shader->vertex); rk_gl_error("glCompileShader() failed."); rk_print_shader_infolog(shader->vertex); rk_printf("Compiling fragment shader..."); glShaderSource(shader->fragment, frag_nlines, frag_lines, nullptr); glCompileShader(shader->fragment); rk_gl_error("glCompileShader() failed."); rk_print_shader_infolog(shader->fragment); rk_printf("Linking program..."); glAttachShader(shader->program, shader->vertex); glAttachShader(shader->program, shader->fragment); glLinkProgram(shader->program); rk_gl_error("glLinkProgram() failed."); rk_print_program_infolog(shader->program); rk_printf("Done."); glReleaseShaderCompiler(); return shader; } rk_input_t rk_resolve_input( rk_shader_t _shader, char const * name) { rk_shader const * const shader = reinterpret_cast(_shader); if (!shader || !name) { return nullptr; } GLint const uniform = glGetUniformLocation(shader->program, name); return reinterpret_cast(uniform + 1); } rk_param_t rk_resolve_param( rk_shader_t _shader, char const * name) { rk_shader const * const shader = reinterpret_cast(_shader); if (!shader || !name) { return nullptr; } GLint const location = glGetAttribLocation(shader->program, name); return reinterpret_cast(location + 1); } rk_texture_t rk_create_texture( rk_texture_format format, rk_uint width, rk_uint height, rk_uint nlevels, rk_texture_flags flags, void const * pixels) { if (!width || !height || !pixels) { return nullptr; } GLint internal_format; GLenum source_format; GLenum source_type; switch (format) { case RK_TEXTURE_FORMAT_SRGB8_A8: internal_format = GL_SRGB8_ALPHA8; source_format = GL_RGBA; source_type = GL_UNSIGNED_BYTE; break; case RK_TEXTURE_FORMAT_RGBA8: internal_format = GL_RGBA8; source_format = GL_RGBA; source_type = GL_UNSIGNED_BYTE; break; case RK_TEXTURE_FORMAT_RGB10_A2: internal_format = GL_RGB10_A2; source_format = GL_RGBA; source_type = GL_UNSIGNED_INT_2_10_10_10_REV; break; case RK_TEXTURE_FORMAT_FLOAT_32: internal_format = GL_R32F; source_format = GL_RED; source_type = GL_FLOAT; break; default: return nullptr; break; } rk_texture * const texture = new rk_texture; glGenTextures(1, &texture->texture); GLenum target; if (nlevels) { if (flags & RK_TEXTURE_FLAG_3D) { target = GL_TEXTURE_3D; } else { target = GL_TEXTURE_2D_ARRAY; } glBindTexture(target, texture->texture); //TODO: glTexStorage3D glTexImage3D(target, 0, internal_format, width, height, nlevels, 0, source_format, source_type, pixels); } else { target = GL_TEXTURE_2D; glBindTexture(target, texture->texture); //TODO: glTexStorage2D glTexImage2D(target, 0, internal_format, width, height, 0, source_format, source_type, pixels); } if (flags & RK_TEXTURE_FLAG_MIPMAPS) { if (flags & RK_TEXTURE_FLAG_MIN_LINEAR) { glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } else { glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); } } else { if (flags & RK_TEXTURE_FLAG_MIN_LINEAR) { glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } else { glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } } if (flags & RK_TEXTURE_FLAG_MAG_LINEAR) { glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT); if (flags & RK_TEXTURE_FLAG_MIPMAPS) { glGenerateMipmap(target); } texture->nlevels = nlevels; glBindTexture(target, 0); return texture; } rk_triangles_t rk_create_triangles( rk_uint nvertices, rk_vec3 const * vertices) { if (!nvertices || !vertices) { return nullptr; } rk_triangles * const triangles = new rk_triangles; triangles->size = nvertices; glGenVertexArrays(1, &triangles->array); glBindVertexArray(triangles->array); glGenBuffers(1, &triangles->vertices); glBindBuffer(GL_ARRAY_BUFFER, triangles->vertices); glBufferData(GL_ARRAY_BUFFER, nvertices * sizeof(rk_vec3), vertices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); return triangles; } rk_vertices_t rk_create_vertices( rk_vertex_format const * format, rk_uint nvertices, void const * _vertices, rk_uint nindices, rk_ushort const * indices) { if (!format || !nvertices || !_vertices || !nindices || !indices) { return nullptr; } unsigned vertex_size = 0; unsigned nattribs = 0; for (rk_vertex_format const * f = format; *f; ++f) { switch (*f & RK_VERTEX_FORMAT_MASK) { case RK_VERTEX_FORMAT_VEC3_FLOAT: vertex_size += sizeof(rk_vec3_float); nattribs += 1; break; case RK_VERTEX_FORMAT_VEC3_INT10: vertex_size += sizeof(rk_vec3_int10); nattribs += 1; break; case RK_VERTEX_FORMAT_VEC3_UINT10: vertex_size += sizeof(rk_vec3_uint10); nattribs += 1; break; default: rk_printf("rk_create_vertices(): invalid format."); return nullptr; break; } } if (!vertex_size) { rk_printf("rk_create_vertices(): empty format."); return nullptr; } rk_vertices * const vertices = new rk_vertices; vertices->vertex_size = vertex_size; glGenVertexArrays(1, &vertices->array); glBindVertexArray(vertices->array); glGenBuffers(1, &vertices->vertices); glBindBuffer(GL_ARRAY_BUFFER, vertices->vertices); glBufferData(GL_ARRAY_BUFFER, nvertices * vertex_size, _vertices, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexBuffer(RK_VERTICES_BINDING, vertices->vertices, 0, vertices->vertex_size); for (unsigned attrib = 0; attrib < nattribs; ++attrib) { glEnableVertexAttribArray(attrib); } vertices->layout = 0; unsigned offset = 0; for (rk_vertex_format const * f = format; *f; ++f) { GLboolean const norm = (*f & RK_VERTEX_FORMAT_NORMALIZE) != 0; switch (*f & RK_VERTEX_FORMAT_MASK) { case RK_VERTEX_FORMAT_VEC3_FLOAT: glVertexAttribFormat(vertices->layout++, 3, GL_FLOAT, GL_FALSE, offset); offset += sizeof(rk_vec3_float); break; case RK_VERTEX_FORMAT_VEC3_INT10: glVertexAttribFormat(vertices->layout++, 4, GL_INT_2_10_10_10_REV, norm, offset); offset += sizeof(rk_vec3_int10); break; case RK_VERTEX_FORMAT_VEC3_UINT10: glVertexAttribFormat(vertices->layout++, 4, GL_UNSIGNED_INT_2_10_10_10_REV, norm, offset); offset += sizeof(rk_vec3_uint10); break; } } for (unsigned attrib = 0; attrib < nattribs; ++attrib) { glVertexAttribBinding(attrib, RK_VERTICES_BINDING); } glGenBuffers(1, &vertices->indices); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertices->indices); glBufferData(GL_ELEMENT_ARRAY_BUFFER, nindices * sizeof(rk_ushort), indices, GL_STATIC_DRAW); glBindVertexArray(0); return vertices; } static void rk_pack_vec3_float( rk_pack_dst const dst, rk_pack_src const src) { *dst.vec3_float = *src.vec3_float; } static void rk_pack_vec3_short( rk_pack_dst const dst, rk_pack_src const src) { dst.vec3_short->x = static_cast(src.vec3_float->x); dst.vec3_short->y = static_cast(src.vec3_float->y); dst.vec3_short->z = static_cast(src.vec3_float->z); } static void rk_pack_vec3_short_norm( rk_pack_dst const dst, rk_pack_src const src) { #define _convert(s) (static_cast((s) * ((s) < 0.f ? 32768.f : 32767.f))) dst.vec3_short->x = _convert(src.vec3_float->x); dst.vec3_short->y = _convert(src.vec3_float->y); dst.vec3_short->z = _convert(src.vec3_float->z); #undef _convert } static void rk_pack_vec3_int10( rk_pack_dst const dst, rk_pack_src const src) { #define _convert(s) (static_cast((s)) & 1023) *dst.vec3_int10 = _convert(src.vec3_float->x) | (_convert(src.vec3_float->y) << 10) | (_convert(src.vec3_float->z) << 20); #undef _convert } static void rk_pack_vec3_int10_norm( rk_pack_dst const dst, rk_pack_src const src) { #define _convert(s) (static_cast((s) * ((s) < 0.f ? 512.f : 511.f)) & 1023) *dst.vec3_int10 = _convert(src.vec3_float->x) | (_convert(src.vec3_float->y) << 10) | (_convert(src.vec3_float->z) << 20); #undef _convert } static void rk_pack_mat3_float( rk_pack_dst const dst, rk_pack_src const src) { *dst.mat3_float = *src.mat3_float; } static void rk_pack_mat3_int10( rk_pack_dst const dst, rk_pack_src const src) { #define _convert(s) (static_cast((s)) & 1023) dst.mat3_int10->x = _convert(src.mat3_float->x.x) | (_convert(src.mat3_float->x.y) << 10) | (_convert(src.mat3_float->x.z) << 20); dst.mat3_int10->y = _convert(src.mat3_float->y.x) | (_convert(src.mat3_float->y.y) << 10) | (_convert(src.mat3_float->y.z) << 20); dst.mat3_int10->z = _convert(src.mat3_float->z.x) | (_convert(src.mat3_float->z.y) << 10) | (_convert(src.mat3_float->z.z) << 20); #undef _convert } static void rk_pack_mat3_int10_norm( rk_pack_dst const dst, rk_pack_src const src) { #define _convert(s) (static_cast((s) * ((s) < 0.f ? 512.f : 511.f)) & 1023) dst.mat3_int10->x = _convert(src.mat3_float->x.x) | (_convert(src.mat3_float->x.y) << 10) | (_convert(src.mat3_float->x.z) << 20); dst.mat3_int10->y = _convert(src.mat3_float->y.x) | (_convert(src.mat3_float->y.y) << 10) | (_convert(src.mat3_float->y.z) << 20); dst.mat3_int10->z = _convert(src.mat3_float->z.x) | (_convert(src.mat3_float->z.y) << 10) | (_convert(src.mat3_float->z.z) << 20); #undef _convert } //TODO: multiple batches per vertices rk_batch_t rk_create_batch( rk_vertices_t _vertices, rk_uint max_size, rk_param_format const * params_format) { rk_vertices const * const vertices = reinterpret_cast(_vertices); if (!vertices || !max_size || !params_format || max_size > RK_BATCH_MAX_SIZE) { rk_printf("rk_create_batch(): invalid parameters."); return nullptr; } unsigned nparams = 0; unsigned params_size = 0; unsigned packed_size = 0; unsigned nattribs = 0; for (rk_param_format const * f = params_format; *f; ++f, ++nparams) { switch (*f & RK_PARAM_FORMAT_MASK) { case RK_PARAM_FORMAT_VEC3_FLOAT: params_size += sizeof(rk_vec3_float); packed_size += sizeof(rk_vec3_float); nattribs += 1; break; case RK_PARAM_FORMAT_VEC3_SHORT: params_size += sizeof(rk_vec3_float); packed_size += sizeof(rk_vec3_short); nattribs += 1; break; case RK_PARAM_FORMAT_VEC3_INT10: params_size += sizeof(rk_vec3_float); packed_size += sizeof(rk_vec3_int10); nattribs += 1; break; case RK_PARAM_FORMAT_MAT3_FLOAT: params_size += sizeof(rk_mat3_float); packed_size += sizeof(rk_mat3_float); nattribs += 3; break; case RK_PARAM_FORMAT_MAT3_INT10: params_size += sizeof(rk_mat3_float); packed_size += sizeof(rk_mat3_int10); nattribs += 3; break; default: rk_printf("rk_create_batch(): invalid param format."); return nullptr; break; } } glBindVertexArray(vertices->array); rk_batch * batch = new rk_batch; batch->size = max_size; batch->nparams = nparams; batch->params_size = params_size; batch->packed_size = packed_size; batch->indices = new rk_ushort[max_size]; batch->commands = new rk_command[max_size * sizeof(rk_command)]; memset(batch->commands, 0, max_size * sizeof(rk_command)); if (rk_MultiDrawElementsIndirect) { glGenBuffers(1, &batch->commands_buffer); glBindBuffer(GL_DRAW_INDIRECT_BUFFER, batch->commands_buffer); glBufferData(GL_DRAW_INDIRECT_BUFFER, max_size * sizeof(rk_command), batch->commands, GL_DYNAMIC_DRAW); glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); } else { batch->commands_buffer = 0; } batch->packers = nullptr; batch->params = nullptr; if (nparams) { batch->packers = new rk_packer[nparams]; batch->params = new rk_ubyte[max_size * packed_size]; memset(batch->params, 0, max_size * packed_size); glGenBuffers(1, &batch->params_buffer); glBindBuffer(GL_ARRAY_BUFFER, batch->params_buffer); glBufferData(GL_ARRAY_BUFFER, max_size * batch->packed_size, batch->params, GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexBuffer(RK_PARAMS_BINDING, batch->params_buffer, 0, batch->packed_size); for (unsigned attrib = vertices->layout; attrib < vertices->layout + nattribs; ++attrib) { glEnableVertexAttribArray(attrib); } rk_packer * packer = batch->packers; unsigned layout = vertices->layout; unsigned offset = 0; for (rk_param_format const * f = params_format; *f; ++f, ++packer) { GLboolean const norm = (*f & RK_PARAM_FORMAT_NORMALIZE) != 0; switch (*f & RK_PARAM_FORMAT_MASK) { case RK_PARAM_FORMAT_VEC3_FLOAT: glVertexAttribFormat(layout++, 3, GL_FLOAT, GL_FALSE, offset); packer->pack = rk_pack_vec3_float; packer->src_incr = sizeof(rk_vec3_float); packer->dst_incr = sizeof(rk_vec3_float); break; case RK_PARAM_FORMAT_VEC3_SHORT: glVertexAttribFormat(layout++, 3, GL_SHORT, norm, offset); if (norm) { packer->pack = rk_pack_vec3_short_norm; } else { packer->pack = rk_pack_vec3_short; } packer->src_incr = sizeof(rk_vec3_float); packer->dst_incr = sizeof(rk_vec3_short); break; case RK_PARAM_FORMAT_VEC3_INT10: glVertexAttribFormat(layout++, 4, GL_INT_2_10_10_10_REV, norm, offset); if (norm) { packer->pack = rk_pack_vec3_int10_norm; } else { packer->pack = rk_pack_vec3_int10; } packer->src_incr = sizeof(rk_vec3_float); packer->dst_incr = sizeof(rk_vec3_int10); break; case RK_PARAM_FORMAT_MAT3_FLOAT: glVertexAttribFormat(layout++, 3, GL_FLOAT, GL_FALSE, offset + offsetof(rk_mat3_float, x)); glVertexAttribFormat(layout++, 3, GL_FLOAT, GL_FALSE, offset + offsetof(rk_mat3_float, y)); glVertexAttribFormat(layout++, 3, GL_FLOAT, GL_FALSE, offset + offsetof(rk_mat3_float, z)); packer->pack = rk_pack_mat3_float; packer->src_incr = sizeof(rk_mat3_float); packer->dst_incr = sizeof(rk_mat3_float); break; case RK_PARAM_FORMAT_MAT3_INT10: glVertexAttribFormat(layout++, 4, GL_INT_2_10_10_10_REV, norm, offset + offsetof(rk_mat3_int10, x)); glVertexAttribFormat(layout++, 4, GL_INT_2_10_10_10_REV, norm, offset + offsetof(rk_mat3_int10, y)); glVertexAttribFormat(layout++, 4, GL_INT_2_10_10_10_REV, norm, offset + offsetof(rk_mat3_int10, z)); if (norm) { packer->pack = rk_pack_mat3_int10_norm; } else { packer->pack = rk_pack_mat3_int10; } packer->src_incr = sizeof(rk_mat3_float); packer->dst_incr = sizeof(rk_mat3_int10); break; } offset += packer->dst_incr; } for (unsigned attrib = vertices->layout; attrib < vertices->layout + nattribs; ++attrib) { glVertexAttribBinding(attrib, RK_PARAMS_BINDING); } glVertexBindingDivisor(RK_PARAMS_BINDING, 1); } else { batch->params_buffer = 0; } glBindVertexArray(0); return batch; } void rk_begin_frame() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } void rk_select_shader( rk_shader_t _shader) { rk_shader * const shader = reinterpret_cast(_shader); if (shader) { rk_current_shader = shader; glUseProgram(shader->program); } } void rk_set_input_float( rk_input_t _input, float value) { GLint const input = reinterpret_cast(_input) - 1; if (rk_current_shader && input > -1) { glUniform1f(input, value); } } void rk_set_input_vec3( rk_input_t _input, rk_vec3 const & value) { GLint const input = reinterpret_cast(_input) - 1; if (rk_current_shader && input > -1) { glUniform3fv(input, 1, glm::value_ptr(value)); } } void rk_set_input_mat3( rk_input_t _input, rk_mat3 const & value) { GLint const input = reinterpret_cast(_input) - 1; if (rk_current_shader && input > -1) { glUniformMatrix3fv(input, 1, GL_FALSE, glm::value_ptr(value)); } } void rk_set_input_mat4( rk_input_t _input, rk_mat4 const & value) { GLint const input = reinterpret_cast(_input) - 1; if (rk_current_shader && input > -1) { glUniformMatrix4fv(input, 1, GL_FALSE, glm::value_ptr(value)); } } void rk_set_param_vec3( rk_param_t _param, rk_vec3 const & value) { GLint const param = reinterpret_cast(_param) - 1; if (rk_current_shader && param > -1) { glVertexAttrib3fv(param, glm::value_ptr(value)); } } void rk_set_param_mat3( rk_param_t _param, rk_mat3 const & value) { GLint const param = reinterpret_cast(_param) - 1; if (rk_current_shader && param > -1) { glVertexAttrib3fv(param + 0, glm::value_ptr(value[0])); glVertexAttrib3fv(param + 1, glm::value_ptr(value[1])); glVertexAttrib3fv(param + 2, glm::value_ptr(value[2])); } } void rk_select_texture( rk_uint slot, rk_texture_t _texture, rk_input_t _sampler) { rk_texture const * const texture = reinterpret_cast(_texture); GLint const sampler = reinterpret_cast(_sampler) - 1; if (texture && sampler > -1 && rk_current_shader) { glActiveTexture(GL_TEXTURE0 + slot); if (texture->nlevels) { glBindTexture(GL_TEXTURE_2D_ARRAY, texture->texture); } else { glBindTexture(GL_TEXTURE_2D, texture->texture); } glUniform1i(sampler, slot); } } RK_EXPORT void rk_draw_triangles( rk_triangles_t _triangles) { rk_triangles const * const triangles = reinterpret_cast(_triangles); if (triangles && rk_current_shader && !rk_current_vertices) { glBindVertexArray(triangles->array); glDrawArrays(GL_TRIANGLES, 0, triangles->size); glBindVertexArray(0); } } void rk_select_vertices( rk_vertices_t _vertices) { rk_vertices * const vertices = reinterpret_cast(_vertices); if (vertices) { glBindVertexArray(vertices->array); rk_current_vertices = vertices; } } static unsigned rk_batch_filter( rk_batch & batch, unsigned const size, rk_instance_flags const * flags) { rk_ushort * indices = batch.indices; for (unsigned index = 0; index < size; ++index, ++flags) { if ((*flags & RK_INSTANCE_FLAGS_SPAWNED_VISIBLE) == RK_INSTANCE_FLAGS_SPAWNED_VISIBLE) { *indices++ = static_cast(index); } } return indices - batch.indices; } static unsigned rk_batch_build_commands( rk_batch & batch, unsigned const ninstances, rk_mesh const * const meshes) { rk_command * commands = batch.commands; rk_ushort * base = batch.indices; rk_ushort * const last = batch.indices + ninstances; for (rk_ushort * first = batch.indices; first < last; base = first, ++commands) { rk_mesh const & mesh = meshes[*first++]; for ( ; first < last && meshes[*first].packed == mesh.packed; ++first) { } for (rk_ushort * second = first; second < last; ++second) { unsigned const index = *second; if (meshes[index].packed == mesh.packed) { *second = *first; *first++ = static_cast(index); } } commands->nvertices = static_cast(mesh.ntriangles) * 3; commands->ninstances = first - base; commands->base_index = mesh.base_index; commands->base_vertex = 0; commands->base_instance = base - batch.indices; } return commands - batch.commands; } static void rk_batch_pack( rk_batch & batch, unsigned const ninstances, rk_ubyte const * const params) { rk_pack_dst dst(batch.params); rk_ushort const * const last_index = batch.indices + ninstances; rk_packer const * const last_packer = batch.packers + batch.nparams; for (rk_ushort const * index = batch.indices; index < last_index; ++index) { rk_pack_src src(¶ms[batch.params_size * (*index)]); for (rk_packer const * packer = batch.packers; packer < last_packer; ++packer) { packer->pack(dst, src); src.ptr += packer->src_incr; dst.ptr += packer->dst_incr; } } } void rk_draw_batch( rk_batch_t _batch, rk_uint size, rk_instance_flags const * flags, rk_mesh const * meshes, rk_ubyte const * params) { rk_batch & batch = *reinterpret_cast(_batch); if (!size || size > batch.size || !flags || !meshes || !rk_current_shader || !rk_current_vertices) { return; } unsigned const ninstances = rk_batch_filter(batch, size, flags); if (!ninstances) { return; } if (rk_MultiDrawElementsIndirect) { glBindBuffer(GL_DRAW_INDIRECT_BUFFER, batch.commands_buffer); } if (batch.nparams) { glBindBuffer(GL_ARRAY_BUFFER, batch.params_buffer); } unsigned const ncommands = rk_batch_build_commands(batch, ninstances, meshes); if (rk_MultiDrawElementsIndirect) { glBufferSubData(GL_DRAW_INDIRECT_BUFFER, 0, ncommands * sizeof(rk_command), batch.commands); } if (batch.nparams) { rk_batch_pack(batch, ninstances, params); glBufferSubData(GL_ARRAY_BUFFER, 0, ninstances * batch.packed_size, batch.params); } if (rk_DrawElementsInstancedBaseInstance) { if (rk_MultiDrawElementsIndirect) { rk_MultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, nullptr, ncommands, sizeof(rk_command)); } else { rk_command const * const last_command = batch.commands + ncommands; for (rk_command const * command = batch.commands; command < last_command; ++command) { rk_DrawElementsInstancedBaseInstance( GL_TRIANGLES, command->nvertices, GL_UNSIGNED_SHORT, reinterpret_cast(command->base_index << 1), command->ninstances, command->base_instance); } } } else { unsigned params_offset = 0; rk_command const * const last_command = batch.commands + ncommands; for (rk_command const * command = batch.commands; command < last_command; ++command) { if (batch.nparams) { glBindVertexBuffer(RK_PARAMS_BINDING, batch.params_buffer, params_offset, batch.packed_size); params_offset += command->ninstances * batch.packed_size; } glDrawElementsInstanced( GL_TRIANGLES, command->nvertices, GL_UNSIGNED_SHORT, reinterpret_cast(command->base_index << 1), command->ninstances); } } if (rk_MultiDrawElementsIndirect) { glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); } if (batch.nparams) { glBindBuffer(GL_ARRAY_BUFFER, 0); } } void rk_unselect_vertices( rk_vertices_t _vertices) { rk_current_vertices = nullptr; glBindVertexArray(0); } void rk_unselect_texture( rk_uint slot, rk_texture_t _texture) { rk_texture const * const texture = reinterpret_cast(_texture); if (texture) { glActiveTexture(GL_TEXTURE0 + slot); if (texture->nlevels) { glBindTexture(GL_TEXTURE_2D, 0); } else { glBindTexture(GL_TEXTURE_2D_ARRAY, 0); } } } void rk_unselect_shader( rk_shader_t _shader) { rk_current_shader = nullptr; glUseProgram(0); } void rk_end_frame() { } void rk_destroy_batch( rk_batch_t _batch) { rk_batch * const batch = reinterpret_cast(_batch); if (batch) { delete[] batch->indices; delete[] batch->commands; if (batch->nparams) { delete[] batch->packers; delete[] batch->params; glDeleteBuffers(1, &batch->params_buffer); } if (rk_MultiDrawElementsIndirect) { glDeleteBuffers(1, &batch->commands_buffer); } delete batch; } } void rk_destroy_triangles( rk_triangles_t _triangles) { rk_triangles * const triangles = reinterpret_cast(_triangles); if (triangles) { glDeleteBuffers(1, &triangles->vertices); glDeleteVertexArrays(1, &triangles->array); delete triangles; } } void rk_destroy_vertices( rk_vertices_t _vertices) { rk_vertices * const vertices = reinterpret_cast(_vertices); if (vertices) { glDeleteBuffers(1, &vertices->indices); glDeleteBuffers(1, &vertices->vertices); glDeleteVertexArrays(1, &vertices->array); delete vertices; } } void rk_destroy_texture( rk_texture_t _texture) { rk_texture * const texture = reinterpret_cast(_texture); if (texture) { glDeleteTextures(1, &texture->texture); delete texture; } } void rk_destroy_shader( rk_shader_t _shader) { rk_shader * const shader = reinterpret_cast(_shader); if (shader) { glDeleteShader(shader->vertex); glDeleteShader(shader->fragment); glDeleteProgram(shader->program); delete shader; } }