summaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp915
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, &current);
+ 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;
+}