From 7804c6a96d26c2e757d09f1864eb73fb81eb280f Mon Sep 17 00:00:00 2001 From: Fox Caminiti Date: Thu, 20 Oct 2022 20:45:37 -0400 Subject: precomp recursion! --- main.cpp | 447 ++++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 284 insertions(+), 163 deletions(-) (limited to 'main.cpp') diff --git a/main.cpp b/main.cpp index d0349fa..d6f9e71 100644 --- a/main.cpp +++ b/main.cpp @@ -44,20 +44,21 @@ #include "functions.h" // #include "sharebuffer.h" -SDL_atomic_t Render_Interrupt; -// SDL_atomic_t CurrentEntry; -// SDL_atomic_t QueuedEntries; -// SDL_atomic_t CompletedEntries; -static bool32 IsRendering = false; +SDL_Thread *Thread[8]; +SDL_sem *Semaphore; + +SDL_atomic_t CurrentEntry; +SDL_atomic_t QueuedEntries; +SDL_atomic_t CompletedEntries; + +render_entry Entries[256]; + +static uint64 BitmapBlockSize; static instruction_mode InstructionMode = instruction_mode_scalar; static uint32 RandomGlobalIncrement = 0; // render_entry Entries[256]; -// SDL_Thread *thread[8]; -SDL_Thread *MainRenderThread; -SDL_sem *Semaphore; - #include "memory.cpp" #include "undo.cpp" #include "strings.cpp" @@ -67,47 +68,217 @@ SDL_sem *Semaphore; #include "createcalls.cpp" #include "ffmpeg_backend.cpp" #include "my_imgui_widgets.cpp" +#include "prenderer.cpp" #include "gl_calls.cpp" #if 0 #include "effects.cpp" #include "keyframes.cpp" #include "layer.cpp" #include "bezier.cpp" -#include "prenderer.cpp" #include "bitmap_calls.cpp" #endif +static void +Main_RenderUI(ImGuiIO io, ImVec4 clear_color, SDL_Window *window) +{ + printf("Call ImGui::Render\n"); + ImGui::Render(); + glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); + glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + printf("Call GL renderer\n"); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + printf("Call window swap\n"); + SDL_GL_SwapWindow(window); +} + +static void +Main_InputTest(project_data *File, project_state *State, memory *Memory, ui *UI, SDL_Window *window, GLuint textureID) +{ + ImGuiIO& io = ImGui::GetIO(); + SDL_Event event = {}; + while (SDL_PollEvent(&event)) + { + ImGui_ImplSDL2_ProcessEvent(&event); + if (event.type == SDL_DROPFILE) { + char *DropFile = event.drop.file; + Source_Generate(File, State, Memory, DropFile); + SDL_free(DropFile); + } + if (event.type == SDL_QUIT) + State->IsRunning = false; + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) + State->IsRunning = false; + } + + if (UI->Warp_WantSetPos) { + ImGui::GetIO().WantSetMousePos = true; + io.MousePos = UI->Warp_PositionToSet; + } + + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + + ImGui::NewFrame(); + + if (UI->Warp_WantSetPos) { + ImGui_WarpMouseFinish(UI, io.MousePos); + io.MouseDelta = {}; + UI->Warp_WantSetPos = false; + } + + ImGui::DockSpaceOverViewport(); + + if (!io.WantCaptureKeyboard) + ImGui_ProcessInputs(State, io); + #if 0 + + ImGui_Viewport(File, &State, &UI, &Memory, CompBuffer, io, textureID); + + ImGui_File(&File, &State, &Memory, &UI, io); + + ImGui_EffectsPanel(&File, &State, &Memory, &UI, io); + + ImGui_PropertiesPanel(&File, &State, &UI, &Memory, io); + + // ImGui_Graph(&File, &State, &Memory, &UI, io); + +#if DEBUG + ImGui_DebugUndoTree(&File, &Memory); + if (Debug.ToggleWindow) { + ImGui::ShowDemoWindow(); + ImGui_DebugMemoryViewer(&File, &Memory); + } +#endif + +#endif + + ImGui_Viewport(File, State, UI, Memory, io, textureID); + ImGui_Timeline(File, State, Memory, UI, io); + ImGui_File(File, State, Memory, io); + + ImGui_DebugMemoryViewer(State); + // ImGui::ShowDemoWindow(); + +#if DEBUG + Debug.Temp = {}; +#endif + + ImGui::EndFrame(); +} + static void -MainFunction(main_sdl *Main, memory *Memory, - project_state *State, project_data *File, - comp_buffer *CompBuffer) +Render_Comp(project_data *File, project_state *State, memory *Memory, ImGuiIO io, sorted_comp_info *SortedCompArray, sorted_layer *SortedLayerArray, uint32 CompIndex) { - Bitmap_Clear(CompBuffer->PackedBuffer, CompBuffer->Width, CompBuffer->Height, CompBuffer->BytesPerPixel); - Bitmap_Clear(CompBuffer->UnpackedBuffer, CompBuffer->Width, CompBuffer->Height, CompBuffer->BytesPerPixel); - for (int i = 0; i < File->NumberOfLayers; i++) { - project_layer *Layer = File->Layer[i]; - if (Layer->StartFrame <= File->CurrentFrame && - Layer->EndFrame >= File->CurrentFrame) + block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, CompIndex); + cache_entry *Entry_Main = Memory_Cache_Search(State, Memory, State->Render.Entry, cache_entry_type_comp, CompIndex, State->Frame_Current); + void *CompBuffer = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry_Main->Block_StartIndex); + sorted_comp_info *SortedCompInfo = &SortedCompArray[CompIndex]; + sorted_layer *SortedLayerInfo = Layer_GetSortedArray(SortedLayerArray, SortedCompArray, CompIndex); + for (int i = 0; i < SortedCompInfo->LayerCount; i++) { + sorted_layer SortEntry = SortedLayerInfo[i]; + uint32 Index_Physical = SortEntry.Block_Layer_Index; + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); + if (Layer->Frame_Start <= State->Frame_Current && + Layer->Frame_End >= State->Frame_Current && Layer->IsVisible) { - if (State->UpdateKeyframes) { - for (int p = 0; p < Layer->NumberOfEffects; p++) { - for (int o = 0; o < Layer->Effect[p]->NumberOfProperties; o++) { - CalculateKeyframesLinearly(File->CurrentFrame, &Layer->Effect[p]->Property[o]); - } + layer_bitmap_state *BitmapState = &State->Render.Bitmap[Index_Physical]; + void *BitmapAddress = NULL; + if (!Layer->IsPrecomp) { + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); + cache_entry *Entry = Memory_Cache_Search(State, Memory, State->Render.Entry, cache_entry_type_source, Layer->Block_Source_Index, 0); + + Assert(Source->Type == source_type_image); + + if (!Entry->IsCached) { + uint64 Src_TimeStart = GetTime(); + block_string *Name = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Source->Path_String_Index); + int w = 0, h = 0; + void *temp = stbi_load(Name->Char, &w, &h, NULL, 4); + Source->Width = w; + Source->Height = h; + Source->BytesPerPixel = 4; + uint64 Size = Source->Width * Source->Height * Source->BytesPerPixel; + void *Source_Address = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry->Block_StartIndex); + Arbitrary_WriteInto((uint8 *)temp, (uint8 *)Source_Address, Size); + stbi_image_free(temp); + BitmapState->ToUpdate = false; + BitmapState->CurrentFrame = 0; + Entry->CycleTime = GetTime() - Src_TimeStart; + Layer->x.CurrentValue = (Layer->Block_Source_Index == 0) ? 200 : Comp->Width/2; + Layer->y.CurrentValue = Comp->Height/2; } - for (int r = 0; r < AmountOf(Layer->Property); r++) { - CalculateKeyframesLinearly(File->CurrentFrame, &Layer->Property[r]); + BitmapAddress = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry->Block_StartIndex); + } else { + block_composition *Precomp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, Layer->Block_Source_Index); + cache_entry *Entry = Memory_Cache_Search(State, Memory, State->Render.Entry, cache_entry_type_comp, Layer->Block_Source_Index, State->Frame_Current); + if (!Entry->IsCached) { + uint64 Src_TimeStart = GetTime(); + Render_Comp(File, State, Memory, io, SortedCompArray, SortedLayerArray, Layer->Block_Source_Index); + Layer->x.CurrentValue = (Layer->Block_Source_Index == 0) ? 200 : Comp->Width/2; + Layer->y.CurrentValue = Comp->Height/2; + Entry->CycleTime = GetTime() - Src_TimeStart; } + BitmapAddress = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry->Block_StartIndex); + } + Assert(BitmapAddress); + + // for (int a = 0; a < Layer->Block_Mask_Count; a++) { + // } + // for (int a = 0; a < Layer->Block_Effect_Count; a++) { + // } + + transform_info T = Transform_Calculate(State, Memory, File, Layer, Comp); + T.SourceBuffer = BitmapAddress; + rectangle RenderRegion = {0, 0, Comp->Width, Comp->Height}; + + bool32 IsRendering = true; + Renderer_Start((void *)&T, CompBuffer, RenderRegion); + while (IsRendering) { + SDL_Delay(2); + Renderer_Check(&IsRendering); + // TODO(fox): Make interruptable if the render time gets too long. } - Layer_UpdateBitmap(File, Layer, Memory, File->CurrentFrame); } } - State->UpdateKeyframes = false; - QueueCurrentFrame(File, CompBuffer, State); } -#endif + +static void +Main_Renderer(project_data *File, project_state *State, memory *Memory, SDL_Window *window, GLuint textureID, ImGuiIO io) +{ + State->UpdateFrame = false; + + uint64 Comp_TimeStart = GetTime(); + + block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex); + cache_entry *Entry_Main = Memory_Cache_Search(State, Memory, State->Render.Entry, cache_entry_type_comp, File->PrincipalCompIndex, State->Frame_Current); + void *MainCompBuffer = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry_Main->Block_StartIndex); + + // NOTE(fox): All layers are given a slot here + uint64 SortSize = (sizeof(sorted_comp_info) * File->Comp_Count) + (sizeof(sorted_layer) * File->Layer_Count); + void *SortedArray = Memory_PushScratch(Memory, SortSize); + Arbitrary_Zero((uint8 *)SortedArray, SortSize); + sorted_comp_info *SortedCompArray = (sorted_comp_info *)SortedArray; + sorted_layer *SortedLayerArray = (sorted_layer *)((uint8 *)SortedArray + (sizeof(sorted_comp_info) * File->Comp_Count)); + Layer_SortAll(Memory, SortedLayerArray, SortedCompArray, File->Layer_Count, File->Comp_Count); + + Render_Comp(File, State, Memory, io, SortedCompArray, SortedLayerArray, File->PrincipalCompIndex); + + Memory_PopScratch(Memory, SortSize); + + glBindTexture(GL_TEXTURE_2D, textureID); + + int ByteFlag2 = (MainComp->BytesPerPixel == 4) ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT; + if (!Entry_Main->CycleTime) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MainComp->Width, MainComp->Height, 0, GL_RGBA, ByteFlag2, MainCompBuffer); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, MainComp->Width, MainComp->Height, GL_RGBA, ByteFlag2, MainCompBuffer); + + Entry_Main->CycleTime = GetTime() - Comp_TimeStart; + + // TODO(fox): garbage collect AV state! +} int main(int argc, char *argv[]) { @@ -131,7 +302,7 @@ int main(int argc, char *argv[]) { Memory_InitTable(&GlobalMemory, &Memory, 1 * 1024 * 1024, P_AVInfo, "Image/video headers"); Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, P_UndoBuffer, "Undo buffer"); - Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, P_MiscCache, "Misc persistent"); + Memory_InitTable(&GlobalMemory, &Memory, 40 * 1024 * 1024, P_MiscCache, "Misc persistent"); Memory_InitTable(&GlobalMemory, &Memory, sizeof(project_data), F_File, "File", sizeof(project_data)); Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, F_Precomps, "Precomps", sizeof(block_composition)); @@ -142,7 +313,8 @@ int main(int argc, char *argv[]) { Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, F_Strings, "Strings", sizeof(block_string)); Memory_InitTable(&GlobalMemory, &Memory, (uint64)64 * 1024 * 1024, B_ScratchSpace, "Scratch"); - // Memory_InitTable(&GlobalMemory, &Memory, (uint64)200 * 1024 * 1024, B_CachedBitmaps, "Cached bitmap buffer"); + // Memory_InitTable(&GlobalMemory, &Memory, (uint64)1 * 1024 * 1024, B_CachedBitmapInfo, "Cached bitmap info"); + Memory_InitTable(&GlobalMemory, &Memory, (uint64)50 * 1024 * 1024, B_CachedBitmaps, "Cached bitmap buffer"); #if ARM InstructionMode = instruction_mode_neon; @@ -160,45 +332,98 @@ int main(int argc, char *argv[]) { project_data *File = (project_data *)Memory_Block_AllocateAddress(&Memory, F_File); *File = {}; File->Occupied = 1; + + ui UI = {}; + block_composition *MainComp = (block_composition *)Memory_Block_AllocateAddress(&Memory, F_Precomps); + // MainComp->Width = 3840; + // MainComp->Height = 2160; MainComp->Width = 1280; MainComp->Height = 720; MainComp->FPS = 24; MainComp->BytesPerPixel = 4; - MainComp->Frame_Count = 48; MainComp->Frame_End = 48; + MainComp->Occupied = 1; + MainComp->Name_String_Index = String_AddToFile(&Memory, "Main comp"); + + block_composition *Comp2 = (block_composition *)Memory_Block_AllocateAddress(&Memory, F_Precomps); + Comp2->Width = 500; + Comp2->Height = 500; + Comp2->FPS = 24; + Comp2->BytesPerPixel = 4; + Comp2->Frame_Count = 48; + Comp2->Frame_End = 48; + Comp2->Occupied = 1; + Comp2->Name_String_Index = String_AddToFile(&Memory, "Another comp"); + + File->Comp_Count = 2; + + // 1 MB for 4, 2 MB for 8 + BitmapBlockSize = (MainComp->BytesPerPixel / 4) * 1024 * 1024; { uint16 SourceIndex = Source_Generate(File, State, &Memory, (void *)"../asset/a.jpg"); block_source *Source = (block_source *)Memory_Block_AddressAtIndex(&Memory, F_Sources, 0); Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End); - block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, 0); + } + + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, 0); - History_Undo(&Memory); - History_Redo(&Memory); + { + uint16 SourceIndex = Source_Generate(File, State, &Memory, (void *)"../asset/b.jpg"); + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(&Memory, F_Sources, 1); + + Layer_CreateFromSource(File, State, &Memory, 1, MainComp->Frame_End); + Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End); } - State->Render.MainCompBuffer = (void *)((uint8 *)Memory.Slot[P_MiscCache].Address + sizeof(project_state)); + { + uint16 SourceIndex = Source_Generate(File, State, &Memory, (void *)"../asset/c.jpg"); + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(&Memory, F_Sources, 2); + + Layer_CreateFromSource(File, State, &Memory, SourceIndex, MainComp->Frame_End); + } + + block_layer *Layer1 = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, 0); + Layer1->Vertical_Offset = 0; + + block_layer *Layer2 = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, 1); + Layer2->IsPrecomp = true; + Layer2->Vertical_Offset = 1; + Layer2->Col[0] = 1; + + block_layer *Layer3 = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, 2); + Layer3->Vertical_Offset = 0; + Layer3->Col[1] = 1; + Layer3->Block_Composition_Index = 1; + + block_layer *Layer4 = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, 3); + Layer4->Vertical_Offset = 1; + Layer4->Col[2] = 1; + Layer4->Block_Composition_Index = 1; + + + // History_Undo(&Memory); + // History_Redo(&Memory); SDL_Init(SDL_INIT_VIDEO); -#if 0 -#if THREADED - thread_info ThreadInfo[7]; + Semaphore = SDL_CreateSemaphore(0); + +#if THREADED + int Index[7]; for (int i = 0; i < 7; i++) { - char str[256]; - ThreadInfo[i].Index = i; - ThreadInfo[i].RenderInfo = &RenderInfo; - thread[i] = SDL_CreateThread(TestThread, str, &ThreadInfo[i]); + Index[i] = i; + Thread[i] = SDL_CreateThread(TestThread, "thread", (void *)&Index[i]); } -#endif #endif // Decide GL+GLSL versions + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); #if defined(IMGUI_IMPL_OPENGL_ES2) // GL ES 2.0 + GLSL 100 const char* glsl_version = "#version 100"; @@ -281,141 +506,37 @@ int main(int argc, char *argv[]) { ImGui_ImplSDL2_InitForOpenGL(window, gl_context); ImGui_ImplOpenGL3_Init(glsl_version); - int64 i = 0; - while (i < MainComp->Width*MainComp->Height) { - *((uint32 *)State->Render.MainCompBuffer + i++) = 0xFF2F0000; - } - GLuint textureID; glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_2D, textureID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MainComp->Width, MainComp->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, State->Render.MainCompBuffer); ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - // Semaphore = SDL_CreateSemaphore(0); - // MainRenderThread = SDL_CreateThread(MainRenderer, "Main render thread", &State->Render); - while (State->IsRunning) { - SDL_Event event; - while (SDL_PollEvent(&event)) - { - ImGui_ImplSDL2_ProcessEvent(&event); - if (event.type == SDL_DROPFILE) { - char *DropFile = event.drop.file; - Source_Generate(File, State, &Memory, DropFile); - SDL_free(DropFile); - } - if (event.type == SDL_QUIT) - State->IsRunning = false; - if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) - State->IsRunning = false; - } - - // if (UI.WantSetPos) { - // ImGui::GetIO().WantSetMousePos = true; - // io.MousePos = UI.SetPos; - // } - - ImGui_ImplOpenGL3_NewFrame(); - - ImGui_ImplSDL2_NewFrame(); - - ImGui::NewFrame(); - - // if (UI.WantSetPos) { - // ImGui_WrapMouseFinish(&UI, io.MousePos); - // io.MouseDelta = {}; - // UI.WantSetPos = false; - // } + printf("Call UI\n"); + Main_InputTest(File, State, &Memory, &UI, window, textureID); - ImGui::DockSpaceOverViewport(); - -#if 0 - if (!io.WantCaptureKeyboard) - ImGui_ProcessInputs(&File, &State, &CompBuffer, &Memory, &UI, io); - - ImGui_Viewport(File, &State, &UI, &Memory, CompBuffer, io, textureID); - - ImGui_File(&File, &State, &Memory, &UI, io); - - ImGui_EffectsPanel(&File, &State, &Memory, &UI, io); - - ImGui_PropertiesPanel(&File, &State, &UI, &Memory, io); - - ImGui_Timeline(&File, &State, &Memory, &UI, io); - // ImGui_Graph(&File, &State, &Memory, &UI, io); -#endif - - ImGui_Viewport(MainComp, textureID); - ImGui::ShowDemoWindow(); - -#if 0 -#if DEBUG - ImGui_DebugUndoTree(&File, &Memory); - if (Debug.ToggleWindow) { - ImGui::ShowDemoWindow(); - ImGui_DebugMemoryViewer(&File, &Memory); - } -#endif - - if (UI.TemporaryUpdateOverride) { - UI.TemporaryUpdateOverride = 0; - State.UpdateFrame = 1; + if (State->UpdateFrame) { + printf("Call renderer\n"); + Main_Renderer(File, State, &Memory, window, textureID, io); } - if (UI.Initializing) - UI.Initializing--; + Assert(Debug.ScratchState == 0); - // if (File.CurrentFrame != shmp->shared_framenumber) { - // File.CurrentFrame = shmp->shared_framenumber; - // } + printf("Call render UI\n"); + Main_RenderUI(io, clear_color, window); - // Right now IsRendering does nothing. I have it here if we want to - // completely detatch the rendering updater onto its own thread so the - // UI never lags. -#endif - -#if 0 - if (State.UpdateFrame && !IsRendering) { - MainFunction(0, &Memory, &State, &File, &CompBuffer); - State.UpdateFrame = 0; - } - -#if THREADED - uint32 C = SDL_AtomicGet(&CompletedEntries); - if (IsRendering) { - while (C != 16) { - C = SDL_AtomicGet(&CompletedEntries); - CheckQueue(RenderInfo, 8); - } - C = SDL_AtomicGet(&CompletedEntries); - if (C == 16) { - FinishRenderAndUpload(&State, &CompBuffer, textureID); - } - } -#else - if (IsRendering) { - FinishRenderAndUpload(&State, &CompBuffer, textureID); - } -#endif -#endif - - ImGui::Render(); - glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); - glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); - glClear(GL_COLOR_BUFFER_BIT); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - SDL_GL_SwapWindow(window); + // TODO(fox): Fix things that rely on this. + if (State->Initializing) + State->Initializing--; } - // for (int i = 0; i < 7; i++) { - // SDL_DetachThread(thread[i]); - // } - // shm_unlink("/testl"); + for (int i = 0; i < 7; i++) { + SDL_DetachThread(Thread[i]); + } ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); -- cgit v1.2.3