diff --git a/examples/directx11_example/imgui_impl_dx11.cpp b/examples/directx11_example/imgui_impl_dx11.cpp index ba8d3d8b3..877606961 100644 --- a/examples/directx11_example/imgui_impl_dx11.cpp +++ b/examples/directx11_example/imgui_impl_dx11.cpp @@ -18,6 +18,7 @@ static HWND g_hWnd = 0; static ID3D11Device* g_pd3dDevice = NULL; static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; static ID3D11Buffer* g_pVB = NULL; +static ID3D11Buffer* g_pIB = NULL; static ID3D10Blob * g_pVertexShaderBlob = NULL; static ID3D11VertexShader* g_pVertexShader = NULL; static ID3D11InputLayout* g_pInputLayout = NULL; @@ -27,7 +28,8 @@ static ID3D11PixelShader* g_pPixelShader = NULL; static ID3D11SamplerState* g_pFontSampler = NULL; static ID3D11ShaderResourceView*g_pFontTextureView = NULL; static ID3D11BlendState* g_blendState = NULL; -static int VERTEX_BUFFER_SIZE = 30000; // TODO: Make vertex buffer smaller and grow dynamically as needed. +static int VERTEX_BUFFER_SIZE = 30000; // TODO: Make buffers smaller and grow dynamically as needed. +static int INDEX_BUFFER_SIZE = 30000; // TODO: Make buffers smaller and grow dynamically as needed. struct CUSTOMVERTEX { @@ -47,10 +49,13 @@ struct VERTEX_CONSTANT_BUFFER static void ImGui_ImplDX11_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count) { // Copy and convert all vertices into a single contiguous buffer - D3D11_MAPPED_SUBRESOURCE mappedResource; - if (g_pd3dDeviceContext->Map(g_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource) != S_OK) + D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource; + if (g_pd3dDeviceContext->Map(g_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK) return; - CUSTOMVERTEX* vtx_dst = (CUSTOMVERTEX*)mappedResource.pData; + if (g_pd3dDeviceContext->Map(g_pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK) + return; + CUSTOMVERTEX* vtx_dst = (CUSTOMVERTEX*)vtx_resource.pData; + ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData; for (int n = 0; n < cmd_lists_count; n++) { const ImDrawList* cmd_list = cmd_lists[n]; @@ -65,8 +70,11 @@ static void ImGui_ImplDX11_RenderDrawLists(ImDrawList** const cmd_lists, int cmd vtx_dst++; vtx_src++; } + memcpy(idx_dst, &cmd_list->idx_buffer[0], cmd_list->idx_buffer.size() * sizeof(ImDrawIdx)); + idx_dst += cmd_list->idx_buffer.size(); } g_pd3dDeviceContext->Unmap(g_pVB, 0); + g_pd3dDeviceContext->Unmap(g_pIB, 0); // Setup orthographic projection matrix into our constant buffer { @@ -108,6 +116,7 @@ static void ImGui_ImplDX11_RenderDrawLists(ImDrawList** const cmd_lists, int cmd unsigned int offset = 0; g_pd3dDeviceContext->IASetInputLayout(g_pInputLayout); g_pd3dDeviceContext->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset); + g_pd3dDeviceContext->IASetIndexBuffer(g_pIB, DXGI_FORMAT_R16_UINT, 0); g_pd3dDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); g_pd3dDeviceContext->VSSetShader(g_pVertexShader, NULL, 0); g_pd3dDeviceContext->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer); @@ -120,6 +129,7 @@ static void ImGui_ImplDX11_RenderDrawLists(ImDrawList** const cmd_lists, int cmd // Render command lists int vtx_offset = 0; + int idx_offset = 0; for (int n = 0; n < cmd_lists_count; n++) { const ImDrawList* cmd_list = cmd_lists[n]; @@ -135,10 +145,11 @@ static void ImGui_ImplDX11_RenderDrawLists(ImDrawList** const cmd_lists, int cmd const D3D11_RECT r = { (LONG)pcmd->clip_rect.x, (LONG)pcmd->clip_rect.y, (LONG)pcmd->clip_rect.z, (LONG)pcmd->clip_rect.w }; g_pd3dDeviceContext->PSSetShaderResources(0, 1, (ID3D11ShaderResourceView**)&pcmd->texture_id); g_pd3dDeviceContext->RSSetScissorRects(1, &r); - g_pd3dDeviceContext->Draw(pcmd->vtx_count, vtx_offset); + g_pd3dDeviceContext->DrawIndexed(pcmd->idx_count, idx_offset, vtx_offset); } - vtx_offset += pcmd->vtx_count; + idx_offset += pcmd->idx_count; } + vtx_offset += cmd_list->vtx_buffer.size(); } // Restore modified state @@ -362,11 +373,22 @@ bool ImGui_ImplDX11_CreateDeviceObjects() bufferDesc.ByteWidth = VERTEX_BUFFER_SIZE * sizeof(CUSTOMVERTEX); bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - bufferDesc.MiscFlags = 0; if (g_pd3dDevice->CreateBuffer(&bufferDesc, NULL, &g_pVB) < 0) return false; } + // Create the index buffer + { + D3D11_BUFFER_DESC bufferDesc; + memset(&bufferDesc, 0, sizeof(D3D11_BUFFER_DESC)); + bufferDesc.Usage = D3D11_USAGE_DYNAMIC; + bufferDesc.ByteWidth = INDEX_BUFFER_SIZE * sizeof(ImDrawIdx); + bufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; + bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + if (g_pd3dDevice->CreateBuffer(&bufferDesc, NULL, &g_pIB) < 0) + return false; + } + ImGui_ImplDX11_CreateFontsTexture(); return true; @@ -379,6 +401,7 @@ void ImGui_ImplDX11_InvalidateDeviceObjects() if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; } if (g_pFontTextureView) { g_pFontTextureView->Release(); ImGui::GetIO().Fonts->TexID = 0; } + if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } if (g_blendState) { g_blendState->Release(); g_blendState = NULL; } diff --git a/examples/directx9_example/imgui_impl_dx9.cpp b/examples/directx9_example/imgui_impl_dx9.cpp index c55ffebb4..ba749ef7b 100644 --- a/examples/directx9_example/imgui_impl_dx9.cpp +++ b/examples/directx9_example/imgui_impl_dx9.cpp @@ -15,7 +15,9 @@ static INT64 g_Time = 0; static INT64 g_TicksPerSecond = 0; static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; -static int VERTEX_BUFFER_SIZE = 30000; // TODO: Make vertex buffer smaller and grow dynamically as needed. +static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL; +static int VERTEX_BUFFER_SIZE = 30000; // TODO: Make buffers smaller and grow dynamically as needed. +static int INDEX_BUFFER_SIZE = 30000; // TODO: Make buffers smaller and grow dynamically as needed. struct CUSTOMVERTEX { @@ -31,15 +33,22 @@ struct CUSTOMVERTEX static void ImGui_ImplDX9_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count) { size_t total_vtx_count = 0; + size_t total_idx_count = 0; for (int n = 0; n < cmd_lists_count; n++) + { total_vtx_count += cmd_lists[n]->vtx_buffer.size(); + total_idx_count += cmd_lists[n]->idx_buffer.size(); + } if (total_vtx_count == 0) return; // Copy and convert all vertices into a single contiguous buffer CUSTOMVERTEX* vtx_dst; + ImDrawIdx* idx_dst; if (g_pVB->Lock(0, (UINT)total_vtx_count, (void**)&vtx_dst, D3DLOCK_DISCARD) < 0) return; + if (g_pIB->Lock(0, (UINT)total_idx_count, (void**)&idx_dst, D3DLOCK_DISCARD) < 0) + return; for (int n = 0; n < cmd_lists_count; n++) { const ImDrawList* cmd_list = cmd_lists[n]; @@ -55,9 +64,13 @@ static void ImGui_ImplDX9_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_ vtx_dst++; vtx_src++; } + memcpy(idx_dst, &cmd_list->idx_buffer[0], cmd_list->idx_buffer.size() * sizeof(ImDrawIdx)); + idx_dst += cmd_list->idx_buffer.size(); } g_pVB->Unlock(); + g_pIB->Unlock(); g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof( CUSTOMVERTEX ) ); + g_pd3dDevice->SetIndices( g_pIB ); g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX ); // Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing @@ -90,6 +103,7 @@ static void ImGui_ImplDX9_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_ // Render command lists int vtx_offset = 0; + int idx_offset = 0; for (int n = 0; n < cmd_lists_count; n++) { const ImDrawList* cmd_list = cmd_lists[n]; @@ -105,10 +119,11 @@ static void ImGui_ImplDX9_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_ const RECT r = { (LONG)pcmd->clip_rect.x, (LONG)pcmd->clip_rect.y, (LONG)pcmd->clip_rect.z, (LONG)pcmd->clip_rect.w }; g_pd3dDevice->SetTexture( 0, (LPDIRECT3DTEXTURE9)pcmd->texture_id ); g_pd3dDevice->SetScissorRect(&r); - g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, vtx_offset, pcmd->vtx_count/3); + g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, vtx_offset, 0, cmd_list->vtx_buffer.size(), idx_offset, pcmd->idx_count/3); } - vtx_offset += pcmd->vtx_count; + idx_offset += pcmd->idx_count; } + vtx_offset += cmd_list->vtx_buffer.size(); } } @@ -234,6 +249,9 @@ bool ImGui_ImplDX9_CreateDeviceObjects() if (g_pd3dDevice->CreateVertexBuffer(VERTEX_BUFFER_SIZE * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL) < 0) return false; + if (g_pd3dDevice->CreateIndexBuffer(INDEX_BUFFER_SIZE * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &g_pIB, NULL) < 0) + return false; + ImGui_ImplDX9_CreateFontsTexture(); return true; } @@ -247,6 +265,11 @@ void ImGui_ImplDX9_InvalidateDeviceObjects() g_pVB->Release(); g_pVB = NULL; } + if (g_pIB) + { + g_pIB->Release(); + g_pIB = NULL; + } if (LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)ImGui::GetIO().Fonts->TexID) { tex->Release(); diff --git a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp index 1cd2faf00..a852745f5 100644 --- a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp +++ b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp @@ -23,7 +23,7 @@ static GLuint g_FontTexture = 0; static int g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; static int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0; -static size_t g_VboMaxSize = 20000; +static size_t g_VboSize = 0; static unsigned int g_VboHandle = 0, g_VaoHandle = 0; // This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure) @@ -62,32 +62,33 @@ static void ImGui_ImplGlfwGL3_RenderDrawLists(ImDrawList** const cmd_lists, int for (int n = 0; n < cmd_lists_count; n++) total_vtx_count += cmd_lists[n]->vtx_buffer.size(); glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); - size_t neededBufferSize = total_vtx_count * sizeof(ImDrawVert); - if (neededBufferSize > g_VboMaxSize) + size_t needed_vtx_size = total_vtx_count * sizeof(ImDrawVert); + if (g_VboSize < needed_vtx_size) { - g_VboMaxSize = neededBufferSize + 5000; // Grow buffer - glBufferData(GL_ARRAY_BUFFER, g_VboMaxSize, NULL, GL_STREAM_DRAW); + g_VboSize = needed_vtx_size + 8192; // Grow buffer + glBufferData(GL_ARRAY_BUFFER, g_VboSize, NULL, GL_STREAM_DRAW); } // Copy and convert all vertices into a single contiguous buffer - unsigned char* buffer_data = (unsigned char*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - if (!buffer_data) + unsigned char* vtx_data = (unsigned char*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + if (!vtx_data) return; for (int n = 0; n < cmd_lists_count; n++) { const ImDrawList* cmd_list = cmd_lists[n]; - memcpy(buffer_data, &cmd_list->vtx_buffer[0], cmd_list->vtx_buffer.size() * sizeof(ImDrawVert)); - buffer_data += cmd_list->vtx_buffer.size() * sizeof(ImDrawVert); + memcpy(vtx_data, &cmd_list->vtx_buffer[0], cmd_list->vtx_buffer.size() * sizeof(ImDrawVert)); + vtx_data += cmd_list->vtx_buffer.size() * sizeof(ImDrawVert); } glUnmapBuffer(GL_ARRAY_BUFFER); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(g_VaoHandle); - int cmd_offset = 0; + int vtx_offset = 0; for (int n = 0; n < cmd_lists_count; n++) { const ImDrawList* cmd_list = cmd_lists[n]; - int vtx_offset = cmd_offset; + const ImDrawIdx* idx_buffer = (const unsigned short*)&cmd_list->idx_buffer.front(); + const ImDrawCmd* pcmd_end = cmd_list->commands.end(); for (const ImDrawCmd* pcmd = cmd_list->commands.begin(); pcmd != pcmd_end; pcmd++) { @@ -99,11 +100,11 @@ static void ImGui_ImplGlfwGL3_RenderDrawLists(ImDrawList** const cmd_lists, int { glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->texture_id); glScissor((int)pcmd->clip_rect.x, (int)(height - pcmd->clip_rect.w), (int)(pcmd->clip_rect.z - pcmd->clip_rect.x), (int)(pcmd->clip_rect.w - pcmd->clip_rect.y)); - glDrawArrays(GL_TRIANGLES, vtx_offset, pcmd->vtx_count); + glDrawElementsBaseVertex(GL_TRIANGLES, pcmd->idx_count, GL_UNSIGNED_SHORT, idx_buffer, vtx_offset); } - vtx_offset += pcmd->vtx_count; + idx_buffer += pcmd->idx_count; } - cmd_offset = vtx_offset; + vtx_offset += cmd_list->vtx_buffer.size(); } // Restore modified state @@ -217,8 +218,6 @@ bool ImGui_ImplGlfwGL3_CreateDeviceObjects() g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color"); glGenBuffers(1, &g_VboHandle); - glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); - glBufferData(GL_ARRAY_BUFFER, g_VboMaxSize, NULL, GL_DYNAMIC_DRAW); glGenVertexArrays(1, &g_VaoHandle); glBindVertexArray(g_VaoHandle); diff --git a/examples/opengl_example/imgui_impl_glfw.cpp b/examples/opengl_example/imgui_impl_glfw.cpp index 0243d513a..922d693e8 100644 --- a/examples/opengl_example/imgui_impl_glfw.cpp +++ b/examples/opengl_example/imgui_impl_glfw.cpp @@ -60,11 +60,11 @@ static void ImGui_ImplGlfw_RenderDrawLists(ImDrawList** const cmd_lists, int cmd { const ImDrawList* cmd_list = cmd_lists[n]; const unsigned char* vtx_buffer = (const unsigned char*)&cmd_list->vtx_buffer.front(); + const ImDrawIdx* idx_buffer = (const unsigned short*)&cmd_list->idx_buffer.front(); glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (void*)(vtx_buffer + OFFSETOF(ImDrawVert, pos))); glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (void*)(vtx_buffer + OFFSETOF(ImDrawVert, uv))); glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (void*)(vtx_buffer + OFFSETOF(ImDrawVert, col))); - int vtx_offset = 0; for (size_t cmd_i = 0; cmd_i < cmd_list->commands.size(); cmd_i++) { const ImDrawCmd* pcmd = &cmd_list->commands[cmd_i]; @@ -76,9 +76,9 @@ static void ImGui_ImplGlfw_RenderDrawLists(ImDrawList** const cmd_lists, int cmd { glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->texture_id); glScissor((int)pcmd->clip_rect.x, (int)(height - pcmd->clip_rect.w), (int)(pcmd->clip_rect.z - pcmd->clip_rect.x), (int)(pcmd->clip_rect.w - pcmd->clip_rect.y)); - glDrawArrays(GL_TRIANGLES, vtx_offset, pcmd->vtx_count); + glDrawElements(GL_TRIANGLES, pcmd->idx_count, GL_UNSIGNED_SHORT, idx_buffer); } - vtx_offset += pcmd->vtx_count; + idx_buffer += pcmd->idx_count; } } #undef OFFSETOF diff --git a/imgui.cpp b/imgui.cpp index 678c4f07a..139154eb2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1670,10 +1670,11 @@ static inline void AddDrawListToRenderList(ImVector& out_render_lis { if (!draw_list->commands.empty() && !draw_list->vtx_buffer.empty()) { - if (draw_list->commands.back().vtx_count == 0) + if (draw_list->commands.back().idx_count == 0) draw_list->commands.pop_back(); out_render_list.push_back(draw_list); GImGui->IO.MetricsRenderVertices += (int)draw_list->vtx_buffer.size(); + GImGui->IO.MetricsRenderIndices += (int)draw_list->idx_buffer.size(); } } @@ -2207,7 +2208,7 @@ void ImGui::Render() } // Gather windows to render - g.IO.MetricsRenderVertices = 0; + g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0; for (size_t i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++) g.RenderDrawLists[i].resize(0); for (size_t i = 0; i != g.Windows.size(); i++) @@ -7590,6 +7591,9 @@ void ImDrawList::Clear() commands.resize(0); vtx_buffer.resize(0); vtx_write = NULL; + vtx_current_idx = 0; + idx_buffer.resize(0); + idx_write = NULL; clip_rect_stack.resize(0); texture_id_stack.resize(0); } @@ -7599,6 +7603,9 @@ void ImDrawList::ClearFreeMemory() commands.clear(); vtx_buffer.clear(); vtx_write = NULL; + vtx_current_idx = 0; + idx_buffer.clear(); + idx_write = NULL; clip_rect_stack.clear(); texture_id_stack.clear(); } @@ -7606,7 +7613,7 @@ void ImDrawList::ClearFreeMemory() void ImDrawList::AddDrawCmd() { ImDrawCmd draw_cmd; - draw_cmd.vtx_count = 0; + draw_cmd.idx_count = 0; draw_cmd.clip_rect = clip_rect_stack.empty() ? GNullClipRect : clip_rect_stack.back(); draw_cmd.texture_id = texture_id_stack.empty() ? NULL : texture_id_stack.back(); draw_cmd.user_callback = NULL; @@ -7619,7 +7626,7 @@ void ImDrawList::AddDrawCmd() void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) { ImDrawCmd* current_cmd = commands.empty() ? NULL : &commands.back(); - if (!current_cmd || current_cmd->vtx_count != 0 || current_cmd->user_callback != NULL) + if (!current_cmd || current_cmd->idx_count != 0 || current_cmd->user_callback != NULL) { AddDrawCmd(); current_cmd = &commands.back(); @@ -7635,7 +7642,7 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) void ImDrawList::UpdateClipRect() { ImDrawCmd* current_cmd = commands.empty() ? NULL : &commands.back(); - if (!current_cmd || (current_cmd->vtx_count != 0) || current_cmd->user_callback != NULL) + if (!current_cmd || (current_cmd->idx_count != 0) || current_cmd->user_callback != NULL) { AddDrawCmd(); } @@ -7675,7 +7682,7 @@ void ImDrawList::UpdateTextureID() { ImDrawCmd* current_cmd = commands.empty() ? NULL : &commands.back(); const ImTextureID texture_id = texture_id_stack.empty() ? NULL : texture_id_stack.back(); - if (!current_cmd || (current_cmd->vtx_count != 0 && current_cmd->texture_id != texture_id) || current_cmd->user_callback != NULL) + if (!current_cmd || (current_cmd->idx_count != 0 && current_cmd->texture_id != texture_id) || current_cmd->user_callback != NULL) { AddDrawCmd(); } @@ -7698,23 +7705,30 @@ void ImDrawList::PopTextureID() UpdateTextureID(); } -void ImDrawList::PrimReserve(unsigned int vtx_count) +void ImDrawList::PrimReserve(unsigned int idx_count, unsigned int vtx_count) { ImDrawCmd& draw_cmd = commands.back(); - draw_cmd.vtx_count += vtx_count; - + draw_cmd.idx_count += idx_count; + size_t vtx_buffer_size = vtx_buffer.size(); vtx_buffer.resize(vtx_buffer_size + vtx_count); vtx_write = &vtx_buffer[vtx_buffer_size]; + + size_t idx_buffer_size = idx_buffer.size(); + idx_buffer.resize(idx_buffer_size + idx_count); + idx_write = &idx_buffer[idx_buffer_size]; } void ImDrawList::PrimTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col) { const ImVec2 uv = GImGui->FontTexUvWhitePixel; - vtx_write[0].pos = a; vtx_write[0].uv = uv; vtx_write[0].col = col; - vtx_write[1].pos = b; vtx_write[1].uv = uv; vtx_write[1].col = col; - vtx_write[2].pos = c; vtx_write[2].uv = uv; vtx_write[2].col = col; + idx_write[0] = vtx_current_idx; idx_write[1] = vtx_current_idx+1; idx_write[2] = vtx_current_idx+2; + vtx_write[0].pos = a; vtx_write[0].uv = uv; vtx_write[0].col = col; + vtx_write[1].pos = b; vtx_write[1].uv = uv; vtx_write[1].col = col; + vtx_write[2].pos = c; vtx_write[2].uv = uv; vtx_write[2].col = col; vtx_write += 3; + vtx_current_idx += 3; + idx_write += 3; } void ImDrawList::PrimRect(const ImVec2& a, const ImVec2& c, ImU32 col) @@ -7722,13 +7736,15 @@ void ImDrawList::PrimRect(const ImVec2& a, const ImVec2& c, ImU32 col) const ImVec2 uv = GImGui->FontTexUvWhitePixel; const ImVec2 b(c.x, a.y); const ImVec2 d(a.x, c.y); - vtx_write[0].pos = a; vtx_write[0].uv = uv; vtx_write[0].col = col; - vtx_write[1].pos = b; vtx_write[1].uv = uv; vtx_write[1].col = col; - vtx_write[2].pos = c; vtx_write[2].uv = uv; vtx_write[2].col = col; - vtx_write[3].pos = a; vtx_write[3].uv = uv; vtx_write[3].col = col; - vtx_write[4].pos = c; vtx_write[4].uv = uv; vtx_write[4].col = col; - vtx_write[5].pos = d; vtx_write[5].uv = uv; vtx_write[5].col = col; - vtx_write += 6; + idx_write[0] = vtx_current_idx; idx_write[1] = vtx_current_idx+1; idx_write[2] = vtx_current_idx+2; + idx_write[3] = vtx_current_idx; idx_write[4] = vtx_current_idx+2; idx_write[5] = vtx_current_idx+3; + vtx_write[0].pos = a; vtx_write[0].uv = uv; vtx_write[0].col = col; + vtx_write[1].pos = b; vtx_write[1].uv = uv; vtx_write[1].col = col; + vtx_write[2].pos = c; vtx_write[2].uv = uv; vtx_write[2].col = col; + vtx_write[3].pos = d; vtx_write[3].uv = uv; vtx_write[3].col = col; + vtx_write += 4; + vtx_current_idx += 4; + idx_write += 6; } void ImDrawList::PrimRectUV(const ImVec2& a, const ImVec2& c, const ImVec2& uv_a, const ImVec2& uv_c, ImU32 col) @@ -7737,25 +7753,29 @@ void ImDrawList::PrimRectUV(const ImVec2& a, const ImVec2& c, const ImVec2& uv_a const ImVec2 d(a.x, c.y); const ImVec2 uv_b(uv_c.x, uv_a.y); const ImVec2 uv_d(uv_a.x, uv_c.y); - vtx_write[0].pos = a; vtx_write[0].uv = uv_a; vtx_write[0].col = col; - vtx_write[1].pos = b; vtx_write[1].uv = uv_b; vtx_write[1].col = col; - vtx_write[2].pos = c; vtx_write[2].uv = uv_c; vtx_write[2].col = col; - vtx_write[3].pos = a; vtx_write[3].uv = uv_a; vtx_write[3].col = col; - vtx_write[4].pos = c; vtx_write[4].uv = uv_c; vtx_write[4].col = col; - vtx_write[5].pos = d; vtx_write[5].uv = uv_d; vtx_write[5].col = col; - vtx_write += 6; + idx_write[0] = vtx_current_idx; idx_write[1] = vtx_current_idx+1; idx_write[2] = vtx_current_idx+2; + idx_write[3] = vtx_current_idx; idx_write[4] = vtx_current_idx+2; idx_write[5] = vtx_current_idx+3; + vtx_write[0].pos = a; vtx_write[0].uv = uv_a; vtx_write[0].col = col; + vtx_write[1].pos = b; vtx_write[1].uv = uv_b; vtx_write[1].col = col; + vtx_write[2].pos = c; vtx_write[2].uv = uv_c; vtx_write[2].col = col; + vtx_write[3].pos = d; vtx_write[3].uv = uv_d; vtx_write[3].col = col; + vtx_write += 4; + vtx_current_idx += 4; + idx_write += 6; } void ImDrawList::PrimQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col) { const ImVec2 uv = GImGui->FontTexUvWhitePixel; - vtx_write[0].pos = a; vtx_write[0].uv = uv; vtx_write[0].col = col; - vtx_write[1].pos = b; vtx_write[1].uv = uv; vtx_write[1].col = col; - vtx_write[2].pos = c; vtx_write[2].uv = uv; vtx_write[2].col = col; - vtx_write[3].pos = a; vtx_write[3].uv = uv; vtx_write[3].col = col; - vtx_write[4].pos = c; vtx_write[4].uv = uv; vtx_write[4].col = col; - vtx_write[5].pos = d; vtx_write[5].uv = uv; vtx_write[5].col = col; - vtx_write += 6; + idx_write[0] = vtx_current_idx; idx_write[1] = vtx_current_idx+1; idx_write[2] = vtx_current_idx+2; + idx_write[3] = vtx_current_idx; idx_write[4] = vtx_current_idx+2; idx_write[5] = vtx_current_idx+3; + vtx_write[0].pos = a; vtx_write[0].uv = uv; vtx_write[0].col = col; + vtx_write[1].pos = b; vtx_write[1].uv = uv; vtx_write[1].col = col; + vtx_write[2].pos = c; vtx_write[2].uv = uv; vtx_write[2].col = col; + vtx_write[3].pos = d; vtx_write[3].uv = uv; vtx_write[3].col = col; + vtx_write += 4; + vtx_current_idx += 4; + idx_write += 6; } // FIXME-OPT: In many instances the caller could provide a normal. @@ -7774,7 +7794,7 @@ void ImDrawList::AddLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thic if ((col >> 24) == 0) return; - PrimReserve(6); + PrimReserve(6, 4); PrimLine(a, b, col, thickness); } @@ -7796,27 +7816,31 @@ void ImDrawList::AddArcFast(const ImVec2& center, float radius, ImU32 col, int a } circle_vtx_builds = true; } - - const ImVec2 uv = GImGui->FontTexUvWhitePixel; + if (filled) { - PrimReserve((unsigned int)(a_max-a_min) * 3); - for (int a0 = a_min; a0 < a_max; a0++) + PrimReserve((unsigned int)(a_max-a_min) * 3, (unsigned int)(a_max-a_min+1) + 1); + ImDrawIdx idx = vtx_current_idx; + for (int a = 0; a < a_max - a_min; a++) { - int a1 = (a0 + 1 == SAMPLES) ? 0 : a0 + 1; - PrimVtx(center + circle_vtx[a0] * radius, uv, col); - PrimVtx(center + circle_vtx[a1] * radius, uv, col); - PrimVtx(center + third_point_offset, uv, col); + PrimIdx(idx + 1 + a); + PrimIdx(idx + 1 + a + 1); + PrimIdx(idx); } + const ImVec2 uv = GImGui->FontTexUvWhitePixel; + PrimVtx(center + third_point_offset, uv, col); + for (int a = a_min; a < a_max+1; a++) + PrimVtx(center + circle_vtx[(a >= SAMPLES) ? a - SAMPLES : a] * radius, uv, col); } else { - PrimReserve((unsigned int)(a_max-a_min) * 6); + // FIXME-OPT: Wasting vertices. + PrimReserve((unsigned int)(a_max-a_min) * 6, (unsigned int)(a_max-a_min) * 4); for (int a0 = a_min; a0 < a_max; a0++) - { + { int a1 = (a0 + 1 == SAMPLES) ? 0 : a0 + 1; PrimLine(center + circle_vtx[a0] * radius, center + circle_vtx[a1] * radius, col); - } + } } } @@ -7831,7 +7855,7 @@ void ImDrawList::AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float roun if (r == 0.0f || rounding_corners == 0) { - PrimReserve(4*6); + PrimReserve(6*4, 4*4); PrimLine(ImVec2(a.x,a.y), ImVec2(b.x,a.y), col); PrimLine(ImVec2(b.x,a.y), ImVec2(b.x,b.y), col); PrimLine(ImVec2(b.x,b.y), ImVec2(a.x,b.y), col); @@ -7839,7 +7863,7 @@ void ImDrawList::AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float roun } else { - PrimReserve(4*6); + PrimReserve(6*4, 4*4); PrimLine(ImVec2(a.x + ((rounding_corners & 1)?r:0), a.y), ImVec2(b.x - ((rounding_corners & 2)?r:0), a.y), col); PrimLine(ImVec2(b.x, a.y + ((rounding_corners & 2)?r:0)), ImVec2(b.x, b.y - ((rounding_corners & 4)?r:0)), col); PrimLine(ImVec2(b.x - ((rounding_corners & 4)?r:0), b.y), ImVec2(a.x + ((rounding_corners & 8)?r:0), b.y), col); @@ -7861,16 +7885,15 @@ void ImDrawList::AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, floa r = ImMin(r, fabsf(b.x-a.x) * ( ((rounding_corners&(1|2))==(1|2)) || ((rounding_corners&(4|8))==(4|8)) ? 0.5f : 1.0f )); r = ImMin(r, fabsf(b.y-a.y) * ( ((rounding_corners&(1|8))==(1|8)) || ((rounding_corners&(2|4))==(2|4)) ? 0.5f : 1.0f )); - const ImVec2 uv = GImGui->FontTexUvWhitePixel; if (r == 0.0f || rounding_corners == 0) { // Use triangle so we can merge more draw calls together (at the cost of extra vertices) - PrimReserve(6); + PrimReserve(6, 4); PrimRect(a, b, col); } else { - PrimReserve(6+6*2); + PrimReserve(6*3, 4*3); PrimRect(ImVec2(a.x+r,a.y), ImVec2(b.x-r,b.y), col); float top_y = (rounding_corners & 1) ? a.y+r : a.y; @@ -7893,7 +7916,7 @@ void ImDrawList::AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec if ((col >> 24) == 0) return; - PrimReserve(3); + PrimReserve(3, 3); PrimTriangle(a, b, c, col); } @@ -7902,14 +7925,18 @@ void ImDrawList::AddCircle(const ImVec2& centre, float radius, ImU32 col, int nu if ((col >> 24) == 0) return; - PrimReserve((unsigned int)num_segments*6); + PrimReserve(num_segments * 6, num_segments * 4); + const float a_step = 2*PI/(float)num_segments; float a0 = 0.0f; + ImVec2 p0 = centre + ImVec2(cosf(a0), sinf(a0)) * radius; for (int i = 0; i < num_segments; i++) { const float a1 = (i + 1) == num_segments ? 0.0f : a0 + a_step; - PrimLine(centre + ImVec2(cosf(a0), sinf(a0))*radius, centre + ImVec2(cosf(a1), sinf(a1))*radius, col); + const ImVec2 p1 = centre + ImVec2(cosf(a1), sinf(a1)) * radius; + PrimLine(p0, p1, col); a0 = a1; + p0 = p1; } } @@ -7919,16 +7946,18 @@ void ImDrawList::AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, return; const ImVec2 uv = GImGui->FontTexUvWhitePixel; - PrimReserve((unsigned int)num_segments*3); + const ImDrawIdx idx = vtx_current_idx; + PrimReserve((unsigned int)(num_segments*3), (unsigned int)(1 + num_segments)); + const float a_step = 2*PI/(float)num_segments; float a0 = 0.0f; - for (int i = 0; i < num_segments; i++) + PrimVtx(centre, uv, col); + for (int i = 0; i < num_segments; i++, a0 += a_step) { - const float a1 = (i + 1) == num_segments ? 0.0f : a0 + a_step; - PrimVtx(centre + ImVec2(cosf(a0), sinf(a0))*radius, uv, col); - PrimVtx(centre + ImVec2(cosf(a1), sinf(a1))*radius, uv, col); - PrimVtx(centre, uv, col); - a0 = a1; + PrimVtx(centre + ImVec2(cosf(a0), sinf(a0)) * radius, uv, col); + PrimIdx(idx); + PrimIdx(idx + 1 + i); + PrimIdx(idx + 1 + ((i + 1 == num_segments) ? 0 : i + 1)); } } @@ -7946,17 +7975,24 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, // reserve vertices for worse case const unsigned int char_count = (unsigned int)(text_end - text_begin); - const unsigned int vtx_count_max = char_count * 6; + const unsigned int vtx_count_max = char_count * 4; + const unsigned int idx_count_max = char_count * 6; const size_t vtx_begin = vtx_buffer.size(); - PrimReserve(vtx_count_max); + const size_t idx_begin = idx_buffer.size(); + PrimReserve(idx_count_max, vtx_count_max); font->RenderText(font_size, pos, col, clip_rect_stack.back(), text_begin, text_end, this, wrap_width, cpu_clip_max); // give back unused vertices + // FIXME-OPT vtx_buffer.resize((size_t)(vtx_write - &vtx_buffer.front())); - const size_t vtx_count = vtx_buffer.size() - vtx_begin; - commands.back().vtx_count -= (unsigned int)(vtx_count_max - vtx_count); - vtx_write -= (vtx_count_max - vtx_count); + idx_buffer.resize((size_t)(idx_write - &idx_buffer.front())); + unsigned int vtx_unused = vtx_count_max - (unsigned int)(vtx_buffer.size() - vtx_begin); + unsigned int idx_unused = idx_count_max - (unsigned int)(idx_buffer.size() - idx_begin); + commands.back().idx_count -= idx_unused; + vtx_write -= vtx_unused; + idx_write -= idx_unused; + vtx_current_idx = (ImDrawIdx)vtx_buffer.size(); } void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0, const ImVec2& uv1, ImU32 col) @@ -7969,7 +8005,7 @@ void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& a, const Im if (push_texture_id) PushTextureID(user_texture_id); - PrimReserve(6); + PrimReserve(6, 4); PrimRectUV(a, b, uv0, uv1, col); if (push_texture_id) @@ -9010,7 +9046,9 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re float x = pos.x; float y = pos.y; - ImDrawVert* out_vertices = draw_list->vtx_write; + ImDrawVert* vtx_write = draw_list->vtx_write; + ImDrawIdx vtx_current_idx = draw_list->vtx_current_idx; + ImDrawIdx* idx_write = draw_list->idx_write; const char* s = text_begin; while (s < text_end) @@ -9100,26 +9138,18 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re } // NB: we are not calling PrimRectUV() here because non-inlined causes too much overhead in a debug build. - out_vertices[0].pos = ImVec2(x1, y1); - out_vertices[0].uv = ImVec2(u1, v1); - out_vertices[0].col = col; - - out_vertices[1].pos = ImVec2(x2, y1); - out_vertices[1].uv = ImVec2(u2, v1); - out_vertices[1].col = col; - - out_vertices[2].pos = ImVec2(x2, y2); - out_vertices[2].uv = ImVec2(u2, v2); - out_vertices[2].col = col; - - out_vertices[3] = out_vertices[0]; - out_vertices[4] = out_vertices[2]; - - out_vertices[5].pos = ImVec2(x1, y2); - out_vertices[5].uv = ImVec2(u1, v2); - out_vertices[5].col = col; - - out_vertices += 6; + // inlined: + { + idx_write[0] = vtx_current_idx; idx_write[1] = vtx_current_idx+1; idx_write[2] = vtx_current_idx+2; + idx_write[3] = vtx_current_idx; idx_write[4] = vtx_current_idx+2; idx_write[5] = vtx_current_idx+3; + vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1; + vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1; + vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2; + vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2; + vtx_write += 4; + vtx_current_idx += 4; + idx_write += 6; + } } } } @@ -9128,7 +9158,9 @@ void ImFont::RenderText(float size, ImVec2 pos, ImU32 col, const ImVec4& clip_re x += char_width; } - draw_list->vtx_write = out_vertices; + draw_list->vtx_write = vtx_write; + draw_list->vtx_current_idx = vtx_current_idx; + draw_list->idx_write = idx_write; } //----------------------------------------------------------------------------- @@ -10410,14 +10442,14 @@ void ImGui::ShowMetricsWindow(bool* opened) { ImGui::Text("ImGui %s", ImGui::GetVersion()); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); - ImGui::Text("%d vertices", ImGui::GetIO().MetricsRenderVertices); + ImGui::Text("%d vertices, %d triangles", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices / 3); ImGui::Separator(); struct Funcs { static void NodeDrawList(ImDrawList* draw_list, const char* label) { - bool opened = ImGui::TreeNode(draw_list, "%s: %d vtx, %d cmds", label, draw_list->vtx_buffer.size(), draw_list->commands.size()); + bool opened = ImGui::TreeNode(draw_list, "%s: %d vtx, %d indices, %d cmds", label, draw_list->vtx_buffer.size(), draw_list->idx_buffer.size(), draw_list->commands.size()); if (draw_list == ImGui::GetWindowDrawList()) { ImGui::SameLine(); @@ -10429,7 +10461,7 @@ void ImGui::ShowMetricsWindow(bool* opened) if (pcmd->user_callback) ImGui::BulletText("Callback %p, user_data %p", pcmd->user_callback, pcmd->user_callback_data); else - ImGui::BulletText("Draw %d vtx, tex = %p", pcmd->vtx_count, pcmd->texture_id); + ImGui::BulletText("Draw %d indexed vtx, tex = %p", pcmd->idx_count, pcmd->texture_id); ImGui::TreePop(); } diff --git a/imgui.h b/imgui.h index 1ff50c6e5..84fcee164 100644 --- a/imgui.h +++ b/imgui.h @@ -671,6 +671,7 @@ struct ImGuiIO bool WantCaptureKeyboard; // Widget is active (= ImGui will use your keyboard input) float Framerate; // Framerate estimation, in frame per second. Rolling average estimation based on IO.DeltaTime over 120 frames int MetricsRenderVertices; // Vertices processed during last call to Render() + int MetricsRenderIndices; // //------------------------------------------------------------------ // [Internal] ImGui will maintain those fields for you @@ -857,13 +858,16 @@ typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* c // Typically, 1 command = 1 gpu draw call (unless command is a callback) struct ImDrawCmd { - unsigned int vtx_count; // Number of vertices (multiple of 3) to be drawn as triangles. The vertices are stored in the callee ImDrawList's vtx_buffer[] array. + unsigned int idx_count; // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[]. ImVec4 clip_rect; // Clipping rectangle (x1, y1, x2, y2) ImTextureID texture_id; // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas. ImDrawCallback user_callback; // If != NULL, call the function instead of rendering the vertices. vtx_count will be 0. clip_rect and texture_id will be set normally. void* user_callback_data; // The draw callback code can access this. }; +// Vertex index +typedef unsigned short ImDrawIdx; + // Vertex layout #ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert @@ -891,11 +895,14 @@ struct ImDrawList // This is what you have to render ImVector commands; // Commands. Typically 1 command = 1 gpu draw call. ImVector vtx_buffer; // Vertex buffer. Each command consume ImDrawCmd::vtx_count of those + ImVector idx_buffer; // Index buffer. Each command consume ImDrawCmd::idx_count of those // [Internal to ImGui] ImVector clip_rect_stack; // [Internal] ImVector texture_id_stack; // [Internal] ImDrawVert* vtx_write; // [Internal] point within vtx_buffer after each add command (to avoid using the ImVector<> operators too much) + ImDrawIdx vtx_current_idx; // [Internal] == vtx_buffer.size() + ImDrawIdx* idx_write; // [Internal] point within idx_buffer after each add command (to avoid using the ImVector<> operators too much) ImDrawList() { Clear(); } IMGUI_API void Clear(); @@ -918,11 +925,11 @@ struct ImDrawList IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0, const ImVec2& uv1, ImU32 col = 0xFFFFFFFF); // Advanced - IMGUI_API void AddCallback(ImDrawCallback callback, void* callback_data); // Your rendering function must check for 'user_callback' in ImDrawCmd and call the function instead of rendering triangles. - IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible + IMGUI_API void AddCallback(ImDrawCallback callback, void* callback_data); // Your rendering function must check for 'user_callback' in ImDrawCmd and call the function instead of rendering triangles. + IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible // Internal helpers - IMGUI_API void PrimReserve(unsigned int vtx_count); + IMGUI_API void PrimReserve(unsigned int idx_count, unsigned int vtx_count); IMGUI_API void PrimTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col); IMGUI_API void PrimRect(const ImVec2& a, const ImVec2& b, ImU32 col); IMGUI_API void PrimRectUV(const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col); @@ -930,7 +937,8 @@ struct ImDrawList IMGUI_API void PrimLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness = 1.0f); IMGUI_API void UpdateClipRect(); IMGUI_API void UpdateTextureID(); - IMGUI_API void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { vtx_write->pos = pos; vtx_write->uv = uv; vtx_write->col = col; vtx_write++; } + IMGUI_API void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { vtx_write->pos = pos; vtx_write->uv = uv; vtx_write->col = col; vtx_write++; vtx_current_idx++; } + IMGUI_API void PrimIdx(unsigned int idx) { *idx_write++ = (ImDrawIdx)idx; } }; // Load and rasterize multiple TTF fonts into a same texture.