Skip to content

Commit

Permalink
sokol_imgui.h: rewrite rendering code to only do one buffer update pe…
Browse files Browse the repository at this point in the history
…r frame (fixes #399)
  • Loading branch information
floooh committed Feb 14, 2021
1 parent 75734a4 commit 5034be8
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 36 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

> NOTE: this list will usually only be updated with changes that affect the public APIs
- **13-Fev-2021**: A new utility header [sokol_nuklear.h](https://github.com/floooh/sokol/blob/master/util/sokol_nuklear.h)
- **14-Feb-2021**: The Dear ImGui rendering backend in [sokol_imgui.h](https://github.com/floooh/sokol/blob/master/util/sokol_imgui.h) has been rewritten to only do a single
buffer-update per frame each for vertex- and index-data. This addresses performance-problems
with sg_append_buffer() in the GL backend on some platforms (see [this issue](https://github.com/floooh/sokol/issues/399) for details.

- **13-Feb-2021**: A new utility header [sokol_nuklear.h](https://github.com/floooh/sokol/blob/master/util/sokol_nuklear.h)
has been added which implements a rendering backend for [Nuklear](https://github.com/Immediate-Mode-UI/Nuklear)
on top of sokol_gfx.h. Also see the new sample [nuklear-sapp](https://floooh.github.io/sokol-html5/nuklear-sapp.html).
Many thanks to **@wmerrifield** for the PR!
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Simple
[STB-style](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt)
cross-platform libraries for C and C++, written in C.

[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**12-Feb-2021**: new utility header **sokol_nuklear.h**)
[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**14-Feb-2021**: more efficient vertex- and index-data updating in sokol_imgui.h)

## Examples and Related Projects

Expand Down
122 changes: 88 additions & 34 deletions util/sokol_imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
to override defaults:
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
SOKOL_MALLOC(s) - your own malloc function (default: malloc(s))
SOKOL_FREE(p) - your own free function (default: free(p))
SOKOL_IMGUI_API_DECL- public function declaration prefix (default: extern)
SOKOL_API_DECL - same as SOKOL_IMGUI_API_DECL
SOKOL_API_IMPL - public function implementation prefix (default: -)
Expand Down Expand Up @@ -286,6 +288,11 @@ inline void simgui_setup(const simgui_desc_t& desc) { return simgui_setup(&desc)
#include <assert.h>
#define SOKOL_ASSERT(c) assert(c)
#endif
#ifndef SOKOL_MALLOC
#include <stdlib.h>
#define SOKOL_MALLOC(s) malloc(s)
#define SOKOL_FREE(p) free(p)
#endif
#ifndef _SOKOL_PRIVATE
#if defined(__GNUC__) || defined(__clang__)
#define _SOKOL_PRIVATE __attribute__((unused)) static
Expand All @@ -312,6 +319,10 @@ typedef struct {
sg_shader shd;
sg_pipeline pip;
bool is_osx; // return true if running on OSX (or HTML5 OSX), needed for copy/paste

sg_range vertices;
sg_range indices;

#if !defined(SOKOL_IMGUI_NO_SOKOL_APP)
bool btn_down[SAPP_MAX_MOUSEBUTTONS];
bool btn_up[SAPP_MAX_MOUSEBUTTONS];
Expand Down Expand Up @@ -1617,6 +1628,15 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) {
since sokol_gfx.h will do its own default-value handling
*/

/* allocate an intermediate vertex- and index-buffer */
SOKOL_ASSERT(_simgui.desc.max_vertices > 0);
_simgui.vertices.size = (size_t)_simgui.desc.max_vertices * sizeof(ImDrawVert);
_simgui.vertices.ptr = SOKOL_MALLOC(_simgui.vertices.size);
SOKOL_ASSERT(_simgui.vertices.ptr);
_simgui.indices.size = (size_t)_simgui.desc.max_vertices * 3 * sizeof(ImDrawIdx);
_simgui.indices.ptr = SOKOL_MALLOC(_simgui.indices.size);
SOKOL_ASSERT(_simgui.indices.ptr);

/* initialize Dear ImGui */
#if defined(__cplusplus)
ImGui::CreateContext();
Expand Down Expand Up @@ -1672,15 +1692,15 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) {
sg_buffer_desc vb_desc;
memset(&vb_desc, 0, sizeof(vb_desc));
vb_desc.usage = SG_USAGE_STREAM;
vb_desc.size = (size_t)_simgui.desc.max_vertices * sizeof(ImDrawVert);
vb_desc.size = _simgui.vertices.size;
vb_desc.label = "sokol-imgui-vertices";
_simgui.vbuf = sg_make_buffer(&vb_desc);

sg_buffer_desc ib_desc;
memset(&ib_desc, 0, sizeof(ib_desc));
ib_desc.type = SG_BUFFERTYPE_INDEXBUFFER;
ib_desc.usage = SG_USAGE_STREAM;
ib_desc.size = (size_t)_simgui.desc.max_vertices * 3 * sizeof(uint16_t);
ib_desc.size = _simgui.indices.size;
ib_desc.label = "sokol-imgui-indices";
_simgui.ibuf = sg_make_buffer(&ib_desc);

Expand Down Expand Up @@ -1814,6 +1834,10 @@ SOKOL_API_IMPL void simgui_shutdown(void) {
sg_destroy_buffer(_simgui.ibuf);
sg_destroy_buffer(_simgui.vbuf);
sg_pop_debug_group();
SOKOL_ASSERT(_simgui.vertices.ptr);
SOKOL_FREE((void*)_simgui.vertices.ptr);
SOKOL_ASSERT(_simgui.indices.ptr);
SOKOL_FREE((void*)_simgui.indices.ptr);
}

#if !defined(SOKOL_IMGUI_NO_SOKOL_APP)
Expand Down Expand Up @@ -1887,10 +1911,60 @@ SOKOL_API_IMPL void simgui_render(void) {
if (draw_data->CmdListsCount == 0) {
return;
}
/* copy vertices and indices into an intermediate buffer so that
they can be updated with a single sg_update_buffer() call each
(sg_append_buffer() has performance problems on some GL platforms)
*/
size_t all_vtx_size = 0;
size_t all_idx_size = 0;
int cl_index = 0;
for (; cl_index < draw_data->CmdListsCount; cl_index++) {
ImDrawList* cl = draw_data->CmdLists[cl_index];
#if defined(__cplusplus)
const size_t vtx_size = cl->VtxBuffer.size() * sizeof(ImDrawVert);
const size_t idx_size = cl->IdxBuffer.size() * sizeof(ImDrawIdx);
const ImDrawVert* vtx_ptr = &cl->VtxBuffer.front();
const ImDrawIdx* idx_ptr = &cl->IdxBuffer.front();
#else
const size_t vtx_size = (size_t)cl->VtxBuffer.Size * sizeof(ImDrawVert);
const size_t idx_size = (size_t)cl->IdxBuffer.Size * sizeof(ImDrawIdx);
const ImDrawVert* vtx_ptr = cl->VtxBuffer.Data;
const ImDrawIdx* idx_ptr = cl->IdxBuffer.Data;
#endif

/* render the ImGui command list */
/* check for buffer overflow */
if (((all_vtx_size + vtx_size) > _simgui.vertices.size) ||
((all_idx_size + idx_size) > _simgui.indices.size))
{
break;
}

/* copy vertices and indices into common buffers */
void* dst_vtx_ptr = (void*) (((uint8_t*)_simgui.vertices.ptr) + all_vtx_size);
void* dst_idx_ptr = (void*) (((uint8_t*)_simgui.indices.ptr) + all_idx_size);
memcpy(dst_vtx_ptr, vtx_ptr, vtx_size);
memcpy(dst_idx_ptr, idx_ptr, idx_size);
all_vtx_size += vtx_size;
all_idx_size += idx_size;
}
/* ...in case there was a buffer overflow, restrict the number of
command list items that are actually rendered
*/
const int cmd_list_count = cl_index;
if (0 == cmd_list_count) {
return;
}

/* update the sokol-gfx vertex- and index-buffer */
sg_push_debug_group("sokol-imgui");
sg_range vtx_data = _simgui.vertices;
vtx_data.size = all_vtx_size;
sg_range idx_data = _simgui.indices;
idx_data.size = all_idx_size;
sg_update_buffer(_simgui.vbuf, &vtx_data);
sg_update_buffer(_simgui.ibuf, &idx_data);

/* render the ImGui command list */
const float dpi_scale = _simgui.desc.dpi_scale;
const int fb_width = (int) (io->DisplaySize.x * dpi_scale);
const int fb_height = (int) (io->DisplaySize.y * dpi_scale);
Expand All @@ -1911,38 +1985,9 @@ SOKOL_API_IMPL void simgui_render(void) {
bind.fs_images[0].id = (uint32_t)(uintptr_t)tex_id;
int vb_offset = 0;
int ib_offset = 0;
for (int cl_index = 0; cl_index < draw_data->CmdListsCount; cl_index++) {
ImDrawList* cl = draw_data->CmdLists[cl_index];
for (int cl_index = 0; cl_index < cmd_list_count; cl_index++) {
const ImDrawList* cl = draw_data->CmdLists[cl_index];

/* append vertices and indices to buffers, record start offsets in draw state */
#if defined(__cplusplus)
const size_t vtx_size = cl->VtxBuffer.size() * sizeof(ImDrawVert);
const size_t idx_size = cl->IdxBuffer.size() * sizeof(ImDrawIdx);
const ImDrawVert* vtx_ptr = &cl->VtxBuffer.front();
const ImDrawIdx* idx_ptr = &cl->IdxBuffer.front();
#else
const size_t vtx_size = (size_t)cl->VtxBuffer.Size * sizeof(ImDrawVert);
const size_t idx_size = (size_t)cl->IdxBuffer.Size * sizeof(ImDrawIdx);
const ImDrawVert* vtx_ptr = cl->VtxBuffer.Data;
const ImDrawIdx* idx_ptr = cl->IdxBuffer.Data;
#endif
if (vtx_ptr) {
const sg_range vtx_range = { vtx_ptr, vtx_size };
vb_offset = sg_append_buffer(bind.vertex_buffers[0], &vtx_range);
}
if (idx_ptr) {
const sg_range idx_range = { idx_ptr, idx_size };
ib_offset = sg_append_buffer(bind.index_buffer, &idx_range);
}
/* don't render anything if the buffer is in overflow state (this is also
checked internally in sokol_gfx, draw calls that attempt to draw with
overflowed buffers will be silently dropped)
*/
if (sg_query_buffer_overflow(bind.vertex_buffers[0]) ||
sg_query_buffer_overflow(bind.index_buffer))
{
break;
}
bind.vertex_buffer_offsets[0] = vb_offset;
bind.index_buffer_offset = ib_offset;
sg_apply_bindings(&bind);
Expand Down Expand Up @@ -1981,6 +2026,15 @@ SOKOL_API_IMPL void simgui_render(void) {
}
base_element += (int)pcmd->ElemCount;
}
#if defined(__cplusplus)
const size_t vtx_size = cl->VtxBuffer.size() * sizeof(ImDrawVert);
const size_t idx_size = cl->IdxBuffer.size() * sizeof(ImDrawIdx);
#else
const size_t vtx_size = (size_t)cl->VtxBuffer.Size * sizeof(ImDrawVert);
const size_t idx_size = (size_t)cl->IdxBuffer.Size * sizeof(ImDrawIdx);
#endif
vb_offset += vtx_size;
ib_offset += idx_size;
}
sg_apply_viewport(0, 0, fb_width, fb_height, true);
sg_apply_scissor_rect(0, 0, fb_width, fb_height, true);
Expand Down

0 comments on commit 5034be8

Please sign in to comment.