diff options
Diffstat (limited to 'src/main.cpp')
-rw-r--r-- | src/main.cpp | 915 |
1 files changed, 915 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..a3b3724 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,915 @@ +#include <glad.h> + +#include <stdio.h> +#if WINDOWS +#include <windows.h> +#else +#include <sys/mman.h> +#include <unistd.h> +#endif + +#if ARM +#include <arm_neon.h> +#include <arm_sve.h> +#else +#include <smmintrin.h> +#endif + +#include "imgui.h" +#include "imgui_impl_sdl.h" +#include "imgui_impl_opengl3.h" +#include <SDL.h> +#if defined(IMGUI_IMPL_OPENGL_ES2) +#include <SDL_opengles2.h> +#else +#include <SDL_opengl.h> +#endif + +#define STB_IMAGE_IMPLEMENTATION +#define STBI_FAILURE_USERMSG +#include "stb_image.h" + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" + +// TODO(fox): Used for thumbnails. The renderer could be expanded to do this +// much more efficiently. +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include "stb_image_resize.h" + +extern "C" { +#include <libavcodec/avcodec.h> +#include <libavformat/avformat.h> +#include <libavformat/avio.h> +#include <libavutil/avutil.h> +#include <libswscale/swscale.h> +#include <libswresample/swresample.h> +} + + +#include "defines.h" +#include "my_math.h" +#include "structs.h" +#if STABLE +#include "stable_diffusion.h" +#endif +#include "memory.h" +#include "main.h" +#include "ffmpeg_backend.h" + +#include "layer.h" +#include "debug.h" +#include "functions.h" + +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; + +uint32 BitmapFill = 0x00000001; + +#if STABLE +#include "base64.c" +#include <curl/curl.h> +#endif + +#include "memory.cpp" +#include "undo.cpp" +#include "io.cpp" +#include "sorted.cpp" +#include "layer.cpp" +#include "strings.cpp" +#include "threading.cpp" +#include "createcalls.cpp" +#if STABLE +#include "stable_diffusion.cpp" +#endif +#include "ffmpeg_backend.cpp" +#include "imgui_ui.cpp" +#include "prenderer.cpp" +#include "gl_calls.cpp" +#include "bezier.cpp" +#include "effects_gl_shader.cpp" +#include "effects.cpp" +#include "effects_constructors.cpp" + +static void +Main_RenderUI(ImGuiIO io, ImVec4 clear_color, SDL_Window *window) +{ + 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); +} + +static void +Main_InputTest(project_data *File, project_state *State, memory *Memory, sorted_file Sorted, 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 (State->Warp_WantSetPos) { + ImGui::GetIO().WantSetMousePos = true; + io.MousePos = State->Warp_PositionToSet; + } + + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + + ImGui::NewFrame(); + + if (State->Warp_WantSetPos) { + ImGui_WarpMouseFinish(State, io.MousePos); + io.MouseDelta = {}; + State->Warp_WantSetPos = false; + } + + if (!io.WantCaptureKeyboard) + ImGui_ProcessInputs(File, State, UI, Memory, io, Sorted); + + ImGui::DockSpaceOverViewport(); + +#if DEBUG + if (Debug.ToggleWindow) { + ImGui::ShowDemoWindow(); + ImGui_DebugRenderQueue(State); + ImGui_DebugMemoryViewer(Memory, State); + ImGui_DebugUndoTree(Memory, State); + } +#endif + + ImGui_Viewport(File, State, UI, Memory, io, textureID, Sorted.CompArray, Sorted.LayerArray, Sorted.PropertyArray); + ImGui_Timeline(File, State, Memory, UI, io, Sorted.CompArray, Sorted.LayerArray, Sorted.PropertyStart, Sorted.PropertyArray); + ImGui_File(File, State, Memory, io, Sorted.CompArray, Sorted.LayerArray); + ImGui_PropertiesPanel(File, State, UI, Memory, io, Sorted.CompArray, Sorted.LayerArray, Sorted.PropertyStart, Sorted.PropertyArray); + ImGui_ColorPanel(File, State, UI, Memory, io); + ImGui_EffectsPanel(File, State, Memory, UI, io); +#if STABLE + if (UI->StableEnabled) { + ImGui_SD_Prompt(File, State, UI, Memory, io, Sorted.CompArray, Sorted.LayerArray); + ImGui_SD_Thumbnail(File, State, UI, Memory, io, Sorted.CompArray, Sorted.LayerArray, Sorted.SourceArray, Sorted.TempSourceCount); + } +#endif + ImGui_Menu(File, State, UI, Memory, io); + ImGui_Popups(File, State, UI, Memory, io); + +#if DEBUG + Debug.Temp = {}; +#endif + + ImGui::EndFrame(); + + if (Memory->PurgeCache) { + Memory_Cache_Purge(File, State, Memory); + Memory->PurgeCache = false; + } +} + +static void +Render_Main(project_data *File, project_state *State, memory *Memory, sorted_file Sorted, ui *UI, SDL_Window *window, GLuint textureID, + void *Data, void *OutputBuffer, render_type RenderType, rectangle RenderRegion) +{ + bool32 IsRendering = true; + Renderer_Start(Data, OutputBuffer, RenderType, RenderRegion); + while (IsRendering) { + Main_InputTest(File, State, Memory, Sorted, UI, window, textureID); + // ImGuiIO& io = ImGui::GetIO(); + // Main_RenderUI(io, ImVec4(0.45f, 0.55f, 0.60f, 1.00f), window); + Renderer_Check(&IsRendering, RenderType); + } +} + +static void +Layer_UpdateAllKeyframes(project_data *File, project_state *State, memory *Memory, block_layer *Layer, uint16 Index_Physical, + sorted_property_array *SortedProperty, uint16 *SortedKeyframe, uint16 Frame_Current) +{ + int32 Offset = (State->Interact_Active == interact_type_keyframe_move) ? (int32)State->Interact_Offset[0] : 0; + int h = 0, c = 0, p = 0; + property_channel *Property = NULL; + while (Layer_LoopChannels(State, Memory, &SortedProperty, &SortedKeyframe, Layer, &Property, NULL, &h, &c, &p)) + { + Assert(Property); + if (Property->Block_Bezier_Count) { + real32 MinY, MaxY; + Property_MinMax_Y(Memory, State, Property, SortedProperty, &MinY, &MaxY); + real32 Y_Increment = 1 / (MaxY - MinY); + v2 FirstPointPos[3]; + bezier_point *FirstPointAddress = Bezier_LookupAddress(Memory, Property, SortedKeyframe[0]); + Bezier_Interact_Evaluate(State, FirstPointAddress, FirstPointPos); + v2 LastPointPos[3]; + bezier_point *LastPointAddress = Bezier_LookupAddress(Memory, Property, SortedKeyframe[Property->Keyframe_Count - 1]); + Bezier_Interact_Evaluate(State, LastPointAddress, LastPointPos); + if (FirstPointPos[0].x >= Frame_Current) { + Property->CurrentValue = FirstPointPos[0].y; + } else if (LastPointPos[0].x <= Frame_Current) { + Property->CurrentValue = LastPointPos[0].y; + } else { + int KeyframeIndex = 0; + for (;;) { + v2 PointPos[3]; + bezier_point *PointAddress = Bezier_LookupAddress(Memory, Property, SortedKeyframe[KeyframeIndex + 1]); + Bezier_Interact_Evaluate(State, PointAddress, PointPos, 1, Y_Increment); + if (PointPos[0].x >= Frame_Current) + break; + KeyframeIndex++; + } + v2 PointPos[3]; + bezier_point *PointAddress = Bezier_LookupAddress(Memory, Property, SortedKeyframe[KeyframeIndex]); + Bezier_Interact_Evaluate(State, PointAddress, PointPos, 1, Y_Increment); + v2 NextPointPos[3]; + bezier_point *NextPointAddress = Bezier_LookupAddress(Memory, Property, SortedKeyframe[KeyframeIndex + 1]); + Bezier_Interact_Evaluate(State, NextPointAddress, NextPointPos, 1, Y_Increment); + if (PointAddress->Type == interpolation_type_hold) { + Property->CurrentValue = PointPos[0].y; + } else if (PointAddress->Type == interpolation_type_linear && NextPointAddress->Type == interpolation_type_linear) { + real32 Ratio = (Frame_Current - PointPos[0].x) / (NextPointPos[0].x - PointPos[0].x); + Property->CurrentValue = PointPos[0].y + ((NextPointPos[0].y - PointPos[0].y) * Ratio); + } else { + Property->CurrentValue = Bezier_SolveYForX(PointPos[0], PointPos[0] + PointPos[2], NextPointPos[0] + NextPointPos[1], NextPointPos[0], Frame_Current); + } + } + } + } +} + +static av_info * +AV_Retrieve(project_state *State, memory *Memory, uint32 SourceIndex) +{ + av_info *AV = NULL; + int h = 0, c = 0, i = 0; + while (Block_Loop(Memory, P_AVInfo, State->AVCount, &h, &c, &i)) { + av_info *TestAV = (av_info *)Memory_Block_AddressAtIndex(Memory, P_AVInfo, i); + if (TestAV->Block_Source_Index == SourceIndex) { + AV = TestAV; + break; + } + } + return AV; +} + +static void +File_RetrieveBitmap(project_data *File, project_state *State, memory *Memory, block_source *Source, int32 FrameToSeek, void *Bitmap, uint32 LayerIndex) +{ +} + +static void * +Render_Comp(project_data *File, project_state *State, memory *Memory, sorted_file Sorted, ui *UI, SDL_Window *window, GLuint textureID, ImGuiIO io, + sorted_comp_array *SortedCompArray, sorted_layer_array *SortedLayerArray, + sorted_property_array *SortedPropertyStart, uint16 *SortedKeyframeArray, uint32 CompIndex, int32 Frame_Current) +{ + block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, CompIndex); + cache_entry *Entry_Main = Memory_Cache_Search(State, Memory, cache_entry_type_comp, CompIndex, Frame_Current); + void *CompBuffer = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry_Main->Block_StartIndex); + uint64 Size = Comp->Width * Comp->Height * Comp->BytesPerPixel; + + if (Entry_Main->IsCached) + return CompBuffer; + + Arbitrary_Zero((uint8 *)CompBuffer, Size); + + uint64 Comp_TimeStart = GetCPUTime(); + + sorted_comp_array *SortedCompStart = &SortedCompArray[CompIndex]; + sorted_layer_array *SortedLayerStart = Sorted_GetLayerStart(SortedLayerArray, SortedCompArray, CompIndex); + + for (int i = 0; i < SortedCompStart->LayerCount; i++) { + sorted_layer_array SortEntry = SortedLayerStart[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 <= Frame_Current && + Layer->Frame_End > Frame_Current && Layer->IsVisible) + { + if (State->UpdateKeyframes) { + sorted_property_array *SortedLayerProperties = SortedPropertyStart + SortEntry.SortedPropertyStart; + uint16 *SortedLayerKeyframes = SortedKeyframeArray + SortEntry.SortedKeyframeStart; + Layer_UpdateAllKeyframes(File, State, Memory, Layer, Index_Physical, SortedLayerProperties, SortedLayerKeyframes, Frame_Current); + } + + layer_bitmap_state *BitmapState = &State->Render.Bitmap[Index_Physical]; + void *BitmapAddress = NULL; + void *RenderAddress = NULL; // result of masking, effects, and intermediate paint stroke + int Width = 0, Height = 0, BytesPerPixel = 0; + + if (!Layer->IsPrecomp) { + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); + av_info *AV = NULL; + if (Source->Type == source_type_file) { + AV = AV_Retrieve(State, Memory, Layer->Block_Source_Index); + if (!AV) { + AV = (av_info *)Memory_Block_AllocateAddress(Memory, P_AVInfo); + AV->Occupied = 1; + AV->Block_Source_Index = Layer->Block_Source_Index; + AV_Init(Source, AV, Memory); + State->AVCount++; + } + } + if (Source->HasAudio && !Source->HasVideo) { + Assert(Source->Type == source_type_file); + if (State->AudioLayerIndex == -1) + State->AudioLayerIndex = Index_Physical; + continue; + } + // if ((State->PreviewSource != -1) && Layer->IsSelected) { + // Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, State->PreviewSource); + // } else { + // } + if (Source->Type == source_type_principal || Source->Type == source_type_principal_temp) { + BitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index, 0); + } else { + cache_entry *CacheEntry = Memory_Cache_Search(State, Memory, cache_entry_type_source, Layer->Block_Source_Index, State->Frame_Current); + BitmapAddress = Memory_Block_Bitmap_AddressAtIndex(Memory, CacheEntry->Block_StartIndex); + if (!CacheEntry->IsCached) { + int FrameToSeek = State->Frame_Current - Layer->Frame_Start; + AV_LoadVideoFrame(Memory, Source, AV, FrameToSeek, BitmapAddress); + CacheEntry->IsCached = true; + } + /* + cache_entry *Entry = Memory_Cache_Search(State, Memory, cache_entry_type_source, Layer->Block_Source_Index, 0); + if (!Entry->IsCached) { + uint64 Src_TimeStart = GetCPUTime(); + 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); + Memory_Copy((uint8 *)Source_Address, (uint8 *)temp, Size); + Bitmap_SRGBToLinear(Source_Address, Source->Width, Source->Height, Source->BytesPerPixel, 1); + stbi_image_free(temp); + BitmapState->ToUpdate = false; + BitmapState->CurrentFrame = 0; + Entry->CycleTime = GetCPUTime() - Src_TimeStart; + Layer->x.CurrentValue = Comp->Width/2; + Layer->y.CurrentValue = Comp->Height/2; + Entry->IsCached = true; + } + BitmapAddress = Memory_Block_Bitmap_AddressAtIndex(Memory, Entry->Block_StartIndex); + */ + } + Width = Source->Width; + Height = Source->Height; + BytesPerPixel = Source->BytesPerPixel; + } else { + block_composition *Precomp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, Layer->Block_Source_Index); + BitmapAddress = Render_Comp(File, State, Memory, Sorted, UI, window, textureID, io, SortedCompArray, SortedLayerArray, + SortedPropertyStart, SortedKeyframeArray, Layer->Block_Source_Index, (int32)Layer->time.CurrentValue); + Width = Precomp->Width; + Height = Precomp->Height; + BytesPerPixel = Precomp->BytesPerPixel; + } + + Assert(BitmapAddress); + + uint64 ScratchSize = Width * Height * BytesPerPixel; + RenderAddress = Memory_PushScratch(Memory, ScratchSize); + Memory_Copy((uint8 *)RenderAddress, (uint8 *)BitmapAddress, ScratchSize); + + if (State->Interact_Active == interact_type_brush && State->Brush.LayerToPaint_Index == Index_Physical) { + // TODO(fox): Do all these extra precomputes really make a difference in the renderer? + rectangle RenderRegion = { 0, 0, Width, Height }; + direct_info Info = { (real32)Width, (real32)Height, (real32)BytesPerPixel, (real32)Width * BytesPerPixel, + Bitmap_ByteInfo(BytesPerPixel), UI->Color.a, blend_normal, + RenderRegion, State->Brush.TransientBitmap, 0, 0}; + Render_Main(File, State, Memory, Sorted, UI, window, textureID, (void *)&Info, RenderAddress, render_type_notransform, State->Brush.CacheBounds); + } + if (Layer->Block_Effect_Count || Layer->Block_Mask_Count) { + Layer_UpdateMasksEffects(State, Layer, Memory, RenderAddress, Width, Height, BytesPerPixel); + } + + Assert(Width && Width <= 2048); + Assert(Height && Height <= 2048); + transform_info T = Transform_Calculate(State, Memory, File, Layer, Comp, Width, Height, BytesPerPixel); + T.SourceBuffer = RenderAddress; + rectangle RenderRegion = {0, 0, Comp->Width, Comp->Height}; + + Render_Main(File, State, Memory, Sorted, UI, window, textureID, (void *)&T, CompBuffer, render_type_main, RenderRegion); + Memory_PopScratch(Memory, ScratchSize); + } + } + Entry_Main->CycleTime = GetCPUTime() - Comp_TimeStart; + Entry_Main->IsCached = true; + + if (CompIndex == File->PrincipalCompIndex) + State->CachedFrameCount++; + + return CompBuffer; +} + +static void +Render_Paint(project_data *File, project_state *State, memory *Memory, sorted_file Sorted, ui *UI, SDL_Window *window, GLuint textureID, ImGuiIO io, v2 LayerPos) +{ + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, State->Brush.LayerToPaint_Index); + layer_transforms T_Layer = Layer_GetTransforms(Layer); + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); + void *SourceBitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index, 0); + + brush_info B; + Brush_Info(&B, &State->Brush, Source, SourceBitmapAddress, LayerPos, UI->Color); + if (State->Brush.Size >= 128) { + Render_Main(File, State, Memory, Sorted, UI, window, textureID, (void *)&B, State->Brush.TransientBitmap, render_type_brush, B.LayerBounds); + } else { + PaintTest(B, State->Brush.TransientBitmap, B.LayerBounds); + } +} + +static void +Render_Blit(project_data *File, project_state *State, memory *Memory, sorted_file Sorted, ui *UI, SDL_Window *window, GLuint textureID, ImGuiIO io, v2 LayerPos) +{ + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, State->Brush.LayerToPaint_Index); + layer_transforms T_Layer = Layer_GetTransforms(Layer); + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, Layer->Block_Source_Index); + void *SourceBitmapAddress = Memory_Block_AddressAtIndex(Memory, F_PrincipalBitmaps, Source->Bitmap_Index, 0); + rectangle RenderRegion = { 0, 0, Source->Width, Source->Height }; + direct_info Info = { (real32)Source->Width, (real32)Source->Height, (real32)Source->BytesPerPixel, (real32)Source->Width * Source->BytesPerPixel, + Bitmap_ByteInfo(Source->BytesPerPixel), UI->Color.a, blend_normal, + RenderRegion, State->Brush.TransientBitmap, 1, 0}; + Render_Main(File, State, Memory, Sorted, UI, window, textureID, (void *)&Info, SourceBitmapAddress, render_type_notransform_swap, State->Brush.CacheBounds); + uint64 BitmapSize = Source->Width * Source->Height * Source->BytesPerPixel; + History_Entry_Commit(Memory, "Paintbrush stroke"); + History_Action_BitmapPaint(Memory, BitmapSize, SourceBitmapAddress, State->Brush.TransientBitmap, Source->BytesPerPixel); + History_Entry_End(Memory); + State->Brush.LayerToPaint_Index = -1; + State->Brush.PrevPos = V2(-4000, -4000); + State->Brush.CacheBounds = { 99999, 99999, -99999, -99999 }; + State->Interact_Active = interact_type_none; + State->Interact_Modifier = 0; + State->UpdateFrame = true; +} + +static void +Main_Renderer(project_data *File, project_state *State, memory *Memory, sorted_file Sorted, ui *UI, SDL_Window *window, GLuint textureID, ImGuiIO io) +{ + State->UpdateFrame = false; + + block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex); + + void *MainCompBuffer = Render_Comp(File, State, Memory, Sorted, UI, window, textureID, io, Sorted.CompArray, Sorted.LayerArray, + Sorted.PropertyStart, Sorted.PropertyArray, File->PrincipalCompIndex, State->Frame_Current); + // Bitmap_SRGBToLinear(MainCompBuffer, MainComp->Width, MainComp->Height, MainComp->BytesPerPixel, 0); + glBindTexture(GL_TEXTURE_2D, textureID); + + int ByteFlag2 = (MainComp->BytesPerPixel == 4) ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT; + if (State->FirstFrame) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MainComp->Width, MainComp->Height, 0, GL_RGBA, ByteFlag2, MainCompBuffer); + State->FirstFrame = false; + } + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, MainComp->Width, MainComp->Height, GL_RGBA, ByteFlag2, MainCompBuffer); + + // TODO(fox): garbage collect AV state! + + State->UpdateKeyframes = false; + +} + + +int main(int argc, char *argv[]) { + + global_memory GlobalMemory = {}; + + GlobalMemory.Size = ((uint64)1 * 1024 * 1024 * 1024); + GlobalMemory.CurrentPosition = 0; + +#if WINDOWS + GlobalMemory.Address = VirtualAlloc(0, GlobalMemory.Size, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); +#else + GlobalMemory.Address = mmap(0, GlobalMemory.Size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0); +#endif + + // 1 meg per block + BitmapBlockSize = 1024 * 1024; + + memory Memory = {}; + + Memory_InitTable(&GlobalMemory, &Memory, 1 * 1024 * 1024, P_AVInfo, "FFmpeg state", sizeof(av_info)); + Memory_InitTable(&GlobalMemory, &Memory, 10 * 1024 * 1024, P_UndoBuffer, "Undo buffer"); + 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, 1 * 1024 * 1024, F_Precomps, "Precomps", sizeof(block_composition)); + Memory_InitTable(&GlobalMemory, &Memory, 2 * 1024 * 1024, F_Layers, "Layers", sizeof(block_layer)); + Memory_InitTable(&GlobalMemory, &Memory, 1 * 1024 * 1024, F_Sources, "Sources", sizeof(block_source)); + Memory_InitTable(&GlobalMemory, &Memory, 2 * 1024 * 1024, F_Effects, "Properties", sizeof(block_effect)); + Memory_InitTable(&GlobalMemory, &Memory, 2 * 1024 * 1024, F_Properties, "Properties", sizeof(property_channel)); + Memory_InitTable(&GlobalMemory, &Memory, 4 * 1024 * 1024, F_Bezier, "Bezier paths (keyframes, masks)", sizeof(block_bezier)); + Memory_InitTable(&GlobalMemory, &Memory, 4 * 1024 * 1024, F_Strings, "Strings", sizeof(block_string)); + Memory_InitTable(&GlobalMemory, &Memory, (uint64)100 * 1024 * 1024, F_PrincipalBitmaps, "Principal bitmap data", BitmapBlockSize); + + Memory_InitTable(&GlobalMemory, &Memory, (uint64)5 * 1024 * 1024, B_Thumbnails, "Thumbnails"); + Memory_InitTable(&GlobalMemory, &Memory, (uint64)64 * 1024 * 1024, B_ScratchSpace, "Scratch"); + // Memory_InitTable(&GlobalMemory, &Memory, (uint64)1 * 1024 * 1024, B_CacheEntries, "Cache entries", sizeof(cache_entry)); + Memory_InitTable(&GlobalMemory, &Memory, (uint64)700 * 1024 * 1024, B_CachedBitmaps, "Cached bitmap buffer"); + + +#if ARM + InstructionMode = instruction_mode_neon; +#else + // if (SDL_HasSSE2()) { + // InstructionMode = instruction_mode_sse; + // } + if (SDL_HasAVX2()) { + InstructionMode = instruction_mode_avx; + } +#endif + + int SamplesPerSecond = 48000; + int BytesPerSample = sizeof(int16) * 2; + + project_state State_ = {}; + project_state *State = &State_; + project_data *File = (project_data *)Memory_Block_AllocateAddress(&Memory, F_File); + *File = {}; + File->Occupied = 1; + + // NOTE(fox): Right now I'm just gonna throw all dynamic allocs that can't + // be simplified to the push/pop model here; will worry about how to best + // use RAM later. + + State->Brush.PaintBuffer = (void *)((uint8 *)Memory.Slot[B_ScratchSpace].Address + Memory.ScratchPos); + uint64 ScratchPaintSize = 2048*2048*4; + Memory.ScratchPos += ScratchPaintSize; + State->Brush.TransientBitmap = (void *)((uint8 *)Memory.Slot[B_ScratchSpace].Address + Memory.ScratchPos); + ScratchPaintSize = 2048*2048*4; + Memory.ScratchPos += ScratchPaintSize; + State->Dump1 = (void *)((uint8 *)Memory.Slot[B_ScratchSpace].Address + Memory.ScratchPos); + Memory.ScratchPos += ScratchPaintSize; + State->Dump2 = (void *)((uint8 *)Memory.Slot[B_ScratchSpace].Address + Memory.ScratchPos); + uint32 AudioBufferSize = 48000 * sizeof(uint16) * 60; + void *AudioData = (void *)((uint8 *)Memory.Slot[B_ScratchSpace].Address + Memory.ScratchPos); + Memory.ScratchPos += AudioBufferSize; + + State->ClipboardBuffer = (void *)((uint8 *)Memory.Slot[B_ScratchSpace].Address + Memory.ScratchPos); + State->ClipboardSize = 1024*1024; + Memory.ScratchPos += State->ClipboardSize; + + State->Test = ImDrawListSplitter(); + + block_composition *MainComp = (block_composition *)Memory_Block_AllocateAddress(&Memory, F_Precomps); + + MainComp->Width = 1280; + MainComp->Height = 720; + MainComp->FPS = 60; + MainComp->BytesPerPixel = 4; + MainComp->Frame_Count = 80; + MainComp->Frame_End = 80; + MainComp->Occupied = 1; + MainComp->Name_String_Index = String_AddToFile(&Memory, "Main comp"); + + File->Comp_Count = 1; + + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); + + SDL_AudioSpec Audio = {0}; + + //AUDIO + Audio.freq = 48000; + Audio.format = AUDIO_S16LSB; + Audio.channels = 2; + Audio.samples = SamplesPerSecond * BytesPerSample / 60; + + SDL_OpenAudio(&Audio, 0); + + Assert(Audio.format == AUDIO_S16LSB); + + SDL_PauseAudio(1); + + Semaphore = SDL_CreateSemaphore(0); + + int Index[7]; + for (int i = 0; i < 7; i++) { + Index[i] = i; + Thread[i] = SDL_CreateThread(TestThread, "thread", (void *)&Index[i]); + } + + // 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"; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#elif defined(__APPLE__) + // GL 3.2 Core + GLSL 150 + const char* glsl_version = "#version 150"; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); +#else + // GL 3.0 + GLSL 130 + const char* glsl_version = "#version 130"; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#endif + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + // SDL_RenderSetScale(renderer, 2, 2) +#if DEBUG +#if ARM + uint32 ScreenSize[2] = {(uint32)(2560/1.2), (uint32)(1600/1.2)}; +#else + real32 ScreenSize[2] = {3840/1.2, 2160/1.2}; +#endif +#else + real32 ScreenSize[2]; + SDL_DisplayMode current; + int windowtest = SDL_GetCurrentDisplayMode(0, ¤t); + if (windowtest == 0) { + ScreenSize[0] = current.w; + ScreenSize[1] = current.h; + } else { + ScreenSize[0] = 1920; + ScreenSize[1] = 1080; + } +#endif + SDL_Window* window = SDL_CreateWindow("Event Tester", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, ScreenSize[0], ScreenSize[1], window_flags); + SDL_GLContext gl_context = SDL_GL_CreateContext(window); + SDL_GL_MakeCurrent(window, gl_context); + SDL_GL_SetSwapInterval(1); // Enable vsync + + if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) + { + printf("Failed to initialize GLAD"); + return -1; + } + + GL_InitDefaultShader(); + GL_InitDefaultVerts(); + + Effect_InitEntries(State); + + SDL_GL_MakeCurrent(window, gl_context); + + SDL_Event Event; + + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_NavEnableSetMousePos; + (void)io; + + // NOTE(fox): Instead of constructing the position of the windows on + // startup with Docking API calls (which is experimental and incomplete) + // I'm loading the window positions from this convenient tool. ImGui by + // default saves window position to an external .ini file, which can be + // loaded from disk or memory. + // io.IniFilename = NULL; + // ImGui::LoadIniSettingsFromMemory(ImGuiPrefs); + // ImGui::SaveIniSettingsToDisk("imgui.ini"); + + ImGui::StyleColorsDark(); + + ImGui::PushStyleColor(ImGuiCol_ModalWindowDimBg, + ImGui::ColorConvertFloat4ToU32(ImVec4(0.0f, 0.0f, 0.0f, 0.1f))); + + ImGui_ImplSDL2_InitForOpenGL(window, gl_context); + ImGui_ImplOpenGL3_Init(glsl_version); + + GLuint textureID; + glGenTextures(1, &textureID); + glBindTexture(GL_TEXTURE_2D, textureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + Brush_CalcBitmapAlphaFromSize(&Memory, &State->Brush, 4); + State_BindBrushTexture(&Memory, &State->Brush, 4); + +#if STABLE + curl_global_init(CURL_GLOBAL_ALL); + curl_state MainHandle = {}; + curl_state ProgHandle = {}; +#endif + +#if DEBUG +#if 1 + sprintf(State->DummyName, "test"); + File_Open(File, State, &Memory, State->DummyName); + State->UpdateFrame = true; + State->MostRecentlySelectedLayer = 0; + uint16 SourceIndex = Source_Generate(File, State, &Memory, (void *)"../asset/24.mp4"); + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(&Memory, F_Sources, SourceIndex); + Source->IsSelected = true; + Source_UICreateButton(File, State, &Memory); +#endif +#endif + + while (State->IsRunning) + { + uint64 PerfFrequency = SDL_GetPerformanceFrequency(); + uint64 PerfStart = SDL_GetPerformanceCounter(); + +#if STABLE + if (State->CurlActive) { + Curl_Main(File, State, &Memory, &MainHandle, &ProgHandle); + } +#endif + + // NOTE(fox): These commands affect sorting, and should not be executed + // in any UI or the renderer. + if (State->HotkeyInput) { + switch (State->HotkeyInput) { + case hotkey_none: + { + Assert(0); + } break; + // case hotkey_effect_indexup: + // { + // History_Entry_Commit(Memory, "Swap effect order"); + // History_Action_Swap(Memory, F_Effects, sizeof(Effect->Index), &Effect->Index); + // Effect->Index += 1; + // uint16 NextEffectIdx = Layer->Block_Effect_Index[h - AmountOf(Layer->Property) + 1]; + // block_effect *NextEffect = (block_effect *)Memory_Block_AddressAtIndex(Memory, F_Effects, NextEffectIdx, 0); + // History_Action_Swap(Memory, F_Effects, sizeof(NextEffect->Index), &NextEffect->Index); + // NextEffect->Index -= 1; + // History_Entry_End(Memory); + // } break; + case hotkey_newpaintlayer: + { + Project_PaintLayer_New(File, State, &Memory); + } break; + case hotkey_newlayerfromsource: + { + Source_UICreateButton(File, State, &Memory); + State->UpdateKeyframes = true; + } break; + case hotkey_deletelayer: + { + Project_Layer_Delete(File, State, &Memory); + } break; + case hotkey_undo: + { + History_Undo(&Memory); + Memory_Cache_Purge(File, State, &Memory); + State->UpdateFrame = true; + } break; + case hotkey_redo: + { + History_Redo(&Memory); + Memory_Cache_Purge(File, State, &Memory); + State->UpdateFrame = true; + } break; + default: + { + Assert(0); + } break; + } + State->HotkeyInput = hotkey_none; + } + + sorted_file Sorted = File_Sort_Push(File, State, &Memory); + + Main_InputTest(File, State, &Memory, Sorted, &File->UI, window, textureID); + + if (State->IsPlaying) { + block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(&Memory, F_Precomps, File->PrincipalCompIndex); + Playhead_Increment(&State->Frame_Current, MainComp->Frame_Start, MainComp->Frame_End, 1); + State->UpdateFrame = true; + State->UpdateKeyframes = true; + } + + if ((State->AudioLayerIndex != -1) && + (State->CachedFrameCount == (MainComp->Frame_End - MainComp->Frame_Start))) + { + block_layer *AudioLayer = (block_layer *)Memory_Block_AddressAtIndex(&Memory, F_Layers, State->AudioLayerIndex); + // block_source *AudioSource = (block_source *)Memory_Block_AddressAtIndex(&Memory, F_Sources, SourceIndex); + av_info *AV = AV_Retrieve(State, &Memory, AudioLayer->Block_Source_Index); + int32 LayerPos = AudioLayer->Frame_Start; + + if (State->Frame_Current >= LayerPos) { + if (State->Frame_Current == LayerPos) { + SDL_ClearQueuedAudio(1); + AV_SeekAudio(AV, 60, 0); + } + if (State->HotFramePerf == 1) { + SDL_ClearQueuedAudio(1); + int32 FrameToSeek = State->Frame_Current - LayerPos; + if (FrameToSeek > -1) + AV_SeekAudio(AV, 60, FrameToSeek); + } + + uint32 QueuedAudioSize = SDL_GetQueuedAudioSize(1); + int TargetAudioSize = SamplesPerSecond * BytesPerSample / 2; + if (QueuedAudioSize < (TargetAudioSize / 2)) + { + int BytesToWrite = TargetAudioSize - QueuedAudioSize; + printf("%i bytes in queue.\n", QueuedAudioSize); + int BytePlayhead = 0; + while (BytePlayhead < BytesToWrite) { + uint8 *Data = (uint8 *)AudioData + BytePlayhead; + int SampleCount = AV_AudioTest(AV, Data, AudioBufferSize - BytePlayhead); + int Size = SampleCount * BytesPerSample; + BytePlayhead += Size; + } + SDL_QueueAudio(1, AudioData, BytePlayhead); + printf("Queued %i bytes.\n", BytePlayhead); + } + } else if (SDL_GetQueuedAudioSize(1)) { + SDL_ClearQueuedAudio(1); + } + } + + if (State->UpdateFrame) { + // Default queue item type simply calls the renderer on the current + // frame, so no additional info is needed + State->Queue.CurrentIdx++; + } + for (int i = 0; i < State->Queue.CurrentIdx; i++) { + render_queue_item Item = State->Queue.Item[i]; + State->Queue.Playhead = i; + if (Item.Type == 0) { + if (State->Interact_Active) { + Memory_Cache_Purge(File, State, &Memory, State->Frame_Current); + } + Main_Renderer(File, State, &Memory, Sorted, &File->UI, window, textureID, io); + } else if (Item.Type == 1) { + Assert(State->Interact_Active == interact_type_brush); + Render_Paint(File, State, &Memory, Sorted, &File->UI, window, textureID, io, Item.Pos); + } else if (Item.Type == 2) { + Assert(State->Interact_Active == interact_type_brush); + Render_Blit(File, State, &Memory, Sorted, &File->UI, window, textureID, io, Item.Pos); + } else { + Assert(0); + } + } + State->Queue.CurrentIdx = 0; + State->Queue.Playhead = 0; + Arbitrary_Zero((uint8 *)State->Queue.Item, sizeof(State->Queue.Item)); + + File_Sort_Pop(&Memory, Sorted.Layer_SortSize, Sorted.Property_SortSize, Sorted.Source_SortSize); + + Assert(Debug.ScratchState == 0); + + if (State->IsPlaying && State->HotFramePerf > 1) { + uint64 RenderTime = SDL_GetPerformanceCounter() - State->HotFramePerf; + real64 FrameMS = (1000.0f * (real64)RenderTime) / (real64)PerfFrequency; + real64 TargetMS = (1000.0f / MainComp->FPS); + if (TargetMS > FrameMS) + SDL_Delay((uint64)(TargetMS - FrameMS)); + Main_RenderUI(io, clear_color, window); + State->HotFramePerf = 1; + } else { + Main_RenderUI(io, clear_color, window); + } + + if (State->HotFramePerf == 1) { + State->HotFramePerf = SDL_GetPerformanceCounter(); + } + + if (State->Initializing) + State->Initializing--; + + uint64 PerfEnd = SDL_GetPerformanceCounter(); + uint64 PerfTime = PerfEnd - PerfStart; + real64 FrameMS = (1000.0f * (real64)PerfTime) / (real64)PerfFrequency; + real64 FPS = PerfFrequency / PerfTime; + // printf("%.02f ms/f, %.02f frames\n", FrameMS, FPS); + } + + for (int i = 0; i < 7; i++) { + SDL_DetachThread(Thread[i]); + } + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + ImGui::DestroyContext(); + SDL_Quit(); + + return 0; +} |