mirror of
				https://github.com/Drezil/imgui.git
				synced 2025-10-31 21:21:06 +01:00 
			
		
		
		
	ImDrawList: Store header/current ImDrawCmd in instance to simplify merging code. Amend 0320e72, toward #3163, #3129
				
					
				
			This commit is contained in:
		
							
								
								
									
										102
									
								
								imgui_draw.cpp
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								imgui_draw.cpp
									
									
									
									
									
								
							| @@ -379,11 +379,17 @@ void ImDrawListSharedData::SetCircleSegmentMaxError(float max_error) | ||||
| // Initialize before use in a new frame. We always have a command ready in the buffer. | ||||
| void ImDrawList::ResetForNewFrame() | ||||
| { | ||||
|     // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory. | ||||
|     // (those should be IM_STATIC_ASSERT() in theory but with our pre C++11 setup the whole check doesn't compile with GCC) | ||||
|     IM_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0); | ||||
|     IM_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4)); | ||||
|     IM_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID)); | ||||
|  | ||||
|     CmdBuffer.resize(0); | ||||
|     IdxBuffer.resize(0); | ||||
|     VtxBuffer.resize(0); | ||||
|     Flags = _Data->InitialFlags; | ||||
|     _VtxCurrentOffset = 0; | ||||
|     memset(&_CmdHeader, 0, sizeof(_CmdHeader)); | ||||
|     _VtxCurrentIdx = 0; | ||||
|     _VtxWritePtr = NULL; | ||||
|     _IdxWritePtr = NULL; | ||||
| @@ -400,7 +406,6 @@ void ImDrawList::ClearFreeMemory() | ||||
|     IdxBuffer.clear(); | ||||
|     VtxBuffer.clear(); | ||||
|     Flags = ImDrawListFlags_None; | ||||
|     _VtxCurrentOffset = 0; | ||||
|     _VtxCurrentIdx = 0; | ||||
|     _VtxWritePtr = NULL; | ||||
|     _IdxWritePtr = NULL; | ||||
| @@ -420,16 +425,12 @@ ImDrawList* ImDrawList::CloneOutput() const | ||||
|     return dst; | ||||
| } | ||||
|  | ||||
| // Using macros because C++ is a terrible language, we want guaranteed inline, no code in header, and no overhead in Debug builds | ||||
| #define GetCurrentClipRect()    (_ClipRectStack.Size ? _ClipRectStack.Data[_ClipRectStack.Size-1]  : _Data->ClipRectFullscreen) | ||||
| #define GetCurrentTextureId()   (_TextureIdStack.Size ? _TextureIdStack.Data[_TextureIdStack.Size-1] : (ImTextureID)NULL) | ||||
|  | ||||
| void ImDrawList::AddDrawCmd() | ||||
| { | ||||
|     ImDrawCmd draw_cmd; | ||||
|     draw_cmd.ClipRect = GetCurrentClipRect(); | ||||
|     draw_cmd.TextureId = GetCurrentTextureId(); | ||||
|     draw_cmd.VtxOffset = _VtxCurrentOffset; | ||||
|     draw_cmd.ClipRect = _CmdHeader.ClipRect;    // Same as calling ImDrawCmd_HeaderCopy() | ||||
|     draw_cmd.TextureId = _CmdHeader.TextureId; | ||||
|     draw_cmd.VtxOffset = _CmdHeader.VtxOffset; | ||||
|     draw_cmd.IdxOffset = IdxBuffer.Size; | ||||
|  | ||||
|     IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w); | ||||
| @@ -450,68 +451,62 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) | ||||
|     AddDrawCmd(); // Force a new command after us (see comment below) | ||||
| } | ||||
|  | ||||
| // Compare ClipRect, TextureId and VtxOffset with a single memcmp() | ||||
| #define ImDrawCmd_HeaderSize                        (IM_OFFSETOF(ImDrawCmd, VtxOffset) + sizeof(unsigned int)) | ||||
| #define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS)   (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize))    // Compare ClipRect, TextureId, VtxOffset | ||||
| #define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC)      (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize))    // Copy ClipRect, TextureId, VtxOffset | ||||
|  | ||||
| // Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack. | ||||
| // The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only. | ||||
| void ImDrawList::UpdateClipRect() | ||||
| { | ||||
|     // If current command is used with different settings we need to add a new command | ||||
|     const ImVec4 curr_clip_rect = GetCurrentClipRect(); | ||||
|     ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; | ||||
|     if ((curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) != 0) || curr_cmd->UserCallback != NULL) | ||||
|     if ((curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &_CmdHeader.ClipRect, sizeof(ImVec4)) != 0) || curr_cmd->UserCallback != NULL) | ||||
|     { | ||||
|         AddDrawCmd(); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // Try to merge with previous command if it matches, else use current command | ||||
|     if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1) | ||||
|     ImDrawCmd* prev_cmd = curr_cmd - 1; | ||||
|     if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && prev_cmd->UserCallback == NULL) | ||||
|     { | ||||
|         ImDrawCmd* prev_cmd = curr_cmd - 1; | ||||
|         if (memcmp(&prev_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) == 0 && prev_cmd->VtxOffset == _VtxCurrentOffset && prev_cmd->TextureId == GetCurrentTextureId() && prev_cmd->UserCallback == NULL) | ||||
|         { | ||||
|             CmdBuffer.pop_back(); | ||||
|             return; | ||||
|         } | ||||
|         CmdBuffer.pop_back(); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     curr_cmd->ClipRect = curr_clip_rect; | ||||
|     curr_cmd->ClipRect = _CmdHeader.ClipRect; | ||||
| } | ||||
|  | ||||
| void ImDrawList::UpdateTextureID() | ||||
| { | ||||
|     // If current command is used with different settings we need to add a new command | ||||
|     const ImTextureID curr_texture_id = GetCurrentTextureId(); | ||||
|     ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; | ||||
|     if ((curr_cmd->ElemCount != 0 && curr_cmd->TextureId != curr_texture_id) || curr_cmd->UserCallback != NULL) | ||||
|     if ((curr_cmd->ElemCount != 0 && curr_cmd->TextureId != _CmdHeader.TextureId) || curr_cmd->UserCallback != NULL) | ||||
|     { | ||||
|         AddDrawCmd(); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // Try to merge with previous command if it matches, else use current command | ||||
|     if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1) | ||||
|     ImDrawCmd* prev_cmd = curr_cmd - 1; | ||||
|     if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && prev_cmd->UserCallback == NULL) | ||||
|     { | ||||
|         ImDrawCmd* prev_cmd = curr_cmd - 1; | ||||
|         if (prev_cmd->TextureId == curr_texture_id && memcmp(&prev_cmd->ClipRect, &GetCurrentClipRect(), sizeof(ImVec4)) == 0 && prev_cmd->VtxOffset == _VtxCurrentOffset && prev_cmd->UserCallback == NULL) | ||||
|         { | ||||
|             CmdBuffer.pop_back(); | ||||
|             return; | ||||
|         } | ||||
|         CmdBuffer.pop_back(); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     curr_cmd->TextureId = curr_texture_id; | ||||
|     curr_cmd->TextureId = _CmdHeader.TextureId; | ||||
| } | ||||
|  | ||||
| #undef GetCurrentClipRect | ||||
| #undef GetCurrentTextureId | ||||
|  | ||||
| // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) | ||||
| void ImDrawList::PushClipRect(ImVec2 cr_min, ImVec2 cr_max, bool intersect_with_current_clip_rect) | ||||
| { | ||||
|     ImVec4 cr(cr_min.x, cr_min.y, cr_max.x, cr_max.y); | ||||
|     if (intersect_with_current_clip_rect && _ClipRectStack.Size) | ||||
|     if (intersect_with_current_clip_rect) | ||||
|     { | ||||
|         ImVec4 current = _ClipRectStack.Data[_ClipRectStack.Size-1]; | ||||
|         ImVec4 current = _CmdHeader.ClipRect; | ||||
|         if (cr.x < current.x) cr.x = current.x; | ||||
|         if (cr.y < current.y) cr.y = current.y; | ||||
|         if (cr.z > current.z) cr.z = current.z; | ||||
| @@ -521,6 +516,7 @@ void ImDrawList::PushClipRect(ImVec2 cr_min, ImVec2 cr_max, bool intersect_with_ | ||||
|     cr.w = ImMax(cr.y, cr.w); | ||||
|  | ||||
|     _ClipRectStack.push_back(cr); | ||||
|     _CmdHeader.ClipRect = cr; | ||||
|     UpdateClipRect(); | ||||
| } | ||||
|  | ||||
| @@ -531,21 +527,22 @@ void ImDrawList::PushClipRectFullScreen() | ||||
|  | ||||
| void ImDrawList::PopClipRect() | ||||
| { | ||||
|     IM_ASSERT(_ClipRectStack.Size > 0); | ||||
|     _ClipRectStack.pop_back(); | ||||
|     _CmdHeader.ClipRect = (_ClipRectStack.Size == 0) ? _Data->ClipRectFullscreen : _ClipRectStack.Data[_ClipRectStack.Size - 1]; | ||||
|     UpdateClipRect(); | ||||
| } | ||||
|  | ||||
| void ImDrawList::PushTextureID(ImTextureID texture_id) | ||||
| { | ||||
|     _TextureIdStack.push_back(texture_id); | ||||
|     _CmdHeader.TextureId = texture_id; | ||||
|     UpdateTextureID(); | ||||
| } | ||||
|  | ||||
| void ImDrawList::PopTextureID() | ||||
| { | ||||
|     IM_ASSERT(_TextureIdStack.Size > 0); | ||||
|     _TextureIdStack.pop_back(); | ||||
|     _CmdHeader.TextureId = (_TextureIdStack.Size == 0) ? (ImTextureID)NULL : _TextureIdStack.Data[_TextureIdStack.Size - 1]; | ||||
|     UpdateTextureID(); | ||||
| } | ||||
|  | ||||
| @@ -558,7 +555,7 @@ void ImDrawList::PrimReserve(int idx_count, int vtx_count) | ||||
|     IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0); | ||||
|     if (sizeof(ImDrawIdx) == 2 && (_VtxCurrentIdx + vtx_count >= (1 << 16)) && (Flags & ImDrawListFlags_AllowVtxOffset)) | ||||
|     { | ||||
|         _VtxCurrentOffset = VtxBuffer.Size; | ||||
|         _CmdHeader.VtxOffset = VtxBuffer.Size; | ||||
|         _VtxCurrentIdx = 0; | ||||
|         AddDrawCmd(); | ||||
|     } | ||||
| @@ -1235,9 +1232,9 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, | ||||
|     if (font_size == 0.0f) | ||||
|         font_size = _Data->FontSize; | ||||
|  | ||||
|     IM_ASSERT(font->ContainerAtlas->TexID == _TextureIdStack.back());  // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. | ||||
|     IM_ASSERT(font->ContainerAtlas->TexID == _CmdHeader.TextureId);  // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. | ||||
|  | ||||
|     ImVec4 clip_rect = _ClipRectStack.back(); | ||||
|     ImVec4 clip_rect = _CmdHeader.ClipRect; | ||||
|     if (cpu_fine_clip_rect) | ||||
|     { | ||||
|         clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect->x); | ||||
| @@ -1258,7 +1255,7 @@ void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& p_min, cons | ||||
|     if ((col & IM_COL32_A_MASK) == 0) | ||||
|         return; | ||||
|  | ||||
|     const bool push_texture_id = _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back(); | ||||
|     const bool push_texture_id = user_texture_id != _CmdHeader.TextureId; | ||||
|     if (push_texture_id) | ||||
|         PushTextureID(user_texture_id); | ||||
|  | ||||
| @@ -1274,7 +1271,7 @@ void ImDrawList::AddImageQuad(ImTextureID user_texture_id, const ImVec2& p1, con | ||||
|     if ((col & IM_COL32_A_MASK) == 0) | ||||
|         return; | ||||
|  | ||||
|     const bool push_texture_id = _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back(); | ||||
|     const bool push_texture_id = user_texture_id != _CmdHeader.TextureId; | ||||
|     if (push_texture_id) | ||||
|         PushTextureID(user_texture_id); | ||||
|  | ||||
| @@ -1357,19 +1354,12 @@ void ImDrawListSplitter::Split(ImDrawList* draw_list, int channels_count) | ||||
|         if (_Channels[i]._CmdBuffer.Size == 0) | ||||
|         { | ||||
|             ImDrawCmd draw_cmd; | ||||
|             draw_cmd.ClipRect = draw_list->_ClipRectStack.back(); | ||||
|             draw_cmd.TextureId = draw_list->_TextureIdStack.back(); | ||||
|             draw_cmd.VtxOffset = draw_list->_VtxCurrentOffset; | ||||
|             ImDrawCmd_HeaderCopy(&draw_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TextureId, VtxOffset | ||||
|             _Channels[i]._CmdBuffer.push_back(draw_cmd); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static inline bool CanMergeDrawCommands(ImDrawCmd* a, ImDrawCmd* b) | ||||
| { | ||||
|     return memcmp(&a->ClipRect, &b->ClipRect, sizeof(a->ClipRect)) == 0 && a->TextureId == b->TextureId && a->VtxOffset == b->VtxOffset && !a->UserCallback && !b->UserCallback; | ||||
| } | ||||
|  | ||||
| void ImDrawListSplitter::Merge(ImDrawList* draw_list) | ||||
| { | ||||
|     // Note that we never use or rely on channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use. | ||||
| @@ -1390,12 +1380,16 @@ void ImDrawListSplitter::Merge(ImDrawList* draw_list) | ||||
|         ImDrawChannel& ch = _Channels[i]; | ||||
|         if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0) | ||||
|             ch._CmdBuffer.pop_back(); | ||||
|         if (ch._CmdBuffer.Size > 0 && last_cmd != NULL && CanMergeDrawCommands(last_cmd, &ch._CmdBuffer[0])) | ||||
|         if (ch._CmdBuffer.Size > 0 && last_cmd != NULL) | ||||
|         { | ||||
|             // Merge previous channel last draw command with current channel first draw command if matching. | ||||
|             last_cmd->ElemCount += ch._CmdBuffer[0].ElemCount; | ||||
|             idx_offset += ch._CmdBuffer[0].ElemCount; | ||||
|             ch._CmdBuffer.erase(ch._CmdBuffer.Data); // FIXME-OPT: Improve for multiple merges. | ||||
|             ImDrawCmd* next_cmd = &ch._CmdBuffer[0]; | ||||
|             if (ImDrawCmd_HeaderCompare(last_cmd, next_cmd) == 0 && last_cmd->UserCallback == NULL && next_cmd->UserCallback == NULL) | ||||
|             { | ||||
|                 // Merge previous channel last draw command with current channel first draw command if matching. | ||||
|                 last_cmd->ElemCount += next_cmd->ElemCount; | ||||
|                 idx_offset += next_cmd->ElemCount; | ||||
|                 ch._CmdBuffer.erase(ch._CmdBuffer.Data); // FIXME-OPT: Improve for multiple merges. | ||||
|             } | ||||
|         } | ||||
|         if (ch._CmdBuffer.Size > 0) | ||||
|             last_cmd = &ch._CmdBuffer.back(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user