Made nodes selectable

This commit is contained in:
Peter Slattery 2019-11-02 13:17:23 -07:00
parent e9a6bdd376
commit 6d893433c4
11 changed files with 221 additions and 102 deletions

View File

@ -625,7 +625,8 @@ UPDATE_AND_RENDER(UpdateAndRender)
DEBUG_IF(GlobalDebugServices->Interface.RenderSculpture) // DebugServices RenderSculpture Toggle
{
s32 JobsNeeded = IntegerDivideRoundUp(State->TotalLEDsCount, LED_BUFFER_SIZE);
s32 JobsNeeded = PLATFORM_THREAD_COUNT;
s32 LEDBufferSize = IntegerDivideRoundUp(State->TotalLEDsCount, JobsNeeded);
draw_leds_job_data* JobDataBank = PushArray(State->Transient, draw_leds_job_data, JobsNeeded);
s32 JobDataBankUsed = 0;
@ -645,7 +646,7 @@ UPDATE_AND_RENDER(UpdateAndRender)
JobData->LEDs = LEDBuffer->LEDs;
JobData->Colors = LEDBuffer->Colors;
JobData->StartIndex = LEDBufferLEDsAssignedToJobs;
JobData->OnePastLastIndex = GSMin(JobData->StartIndex + LED_BUFFER_SIZE, LEDBuffer->Count);
JobData->OnePastLastIndex = GSMin(JobData->StartIndex + LEDBufferSize, LEDBuffer->Count);
LEDBufferLEDsAssignedToJobs += JobData->OnePastLastIndex - JobData->StartIndex;

View File

@ -6,7 +6,6 @@
#include "foldhaus_network_ordering.h"
#include "foldhaus_sacn.h"
#define LED_BUFFER_SIZE 256
struct led
{
s32 Index;

View File

@ -48,7 +48,9 @@ struct debug_services
debug_timing_proc* GetWallClock;
debug_histogram_entry ScopeHistogram[SCOPE_HISTOGRAM_SIZE];
debug_histogram_entry* ScopeHistogramUnsorted;
debug_histogram_entry* ScopeHistogramSorted;
s32 ScopeHistogramUsed;
};
@ -70,6 +72,9 @@ InitDebugServices (debug_services* Services, u8* Memory, s32 MemorySize, s32 Tra
Services->Interface.RenderSculpture = true;
Services->Interface.SendSACNData = false;
Services->ScopeHistogramUnsorted = PushArray(&Services->DebugStorage, debug_histogram_entry, SCOPE_HISTOGRAM_SIZE);
Services->ScopeHistogramSorted = PushArray(&Services->DebugStorage, debug_histogram_entry, SCOPE_HISTOGRAM_SIZE);
Services->ScopeHistogramUsed = 0;
}
@ -79,7 +84,7 @@ DEBUGFindScopeHistogram (debug_services* Services, string Name)
s32 Result = -1;
for (s32 i = 0; i < SCOPE_HISTOGRAM_SIZE; i++)
{
if (StringsEqual(Services->ScopeHistogram[i].ScopeName, Name))
if (StringsEqual(Services->ScopeHistogramUnsorted[i].ScopeName, Name))
{
Result = i;
break;
@ -95,7 +100,7 @@ DEBUGAddScopeHistogram (debug_services* Services, scope_time_record Record)
s32 Result = Services->ScopeHistogramUsed++;
debug_histogram_entry* Entry = Services->ScopeHistogram + Result;
debug_histogram_entry* Entry = Services->ScopeHistogramUnsorted + Result;
Entry->ScopeName = MakeString(Entry->ScopeName_, 256);
Entry->CurrentFrame = 0;
@ -112,7 +117,7 @@ DEBUGAddScopeHistogram (debug_services* Services, scope_time_record Record)
internal void
DEBUGRecordScopeInHistogram (debug_services* Services, s32 Index, scope_time_record Record)
{
debug_histogram_entry* Entry = Services->ScopeHistogram + Index;
debug_histogram_entry* Entry = Services->ScopeHistogramUnsorted + Index;
s32 FrameIndex = Entry->CurrentFrame;
if (FrameIndex >= 0 && FrameIndex < HISTOGRAM_DEPTH)
{
@ -138,6 +143,43 @@ DEBUGCacheScopeHistogramValues (debug_histogram_entry* Histogram)
Histogram->Average_CallCount = (Histogram->Total_CallCount / HISTOGRAM_DEPTH);
}
internal void
DEBUGSortedHistogramInsert(debug_histogram_entry Source, debug_histogram_entry* DestList, s32 DestCount, s32 Max)
{
s32 CurrentFrame0 = Source.CurrentFrame;
s32 V0 = Source.Average_Cycles; //PerFrame_Cycles[CurrentFrame0];
if (DestCount > 0)
{
for (s32 i = DestCount - 1; i >= 0; i--)
{
s32 CurrentFrame1 = DestList[i].CurrentFrame;
s32 V1 = DestList[i].Average_Cycles; //PerFrame_Cycles[CurrentFrame1];
if (V0 < V1)
{
DestList[i + 1] = Source;
break;
}
else
{
DestList[i + 1] = DestList[i];
}
}
}
else
{
DestList[0] = Source;
}
}
internal void
DEBUGSortHistogram(debug_histogram_entry* Source, debug_histogram_entry* Dest, s32 Count, s32 Max)
{
for (s32 i = 0; i < Count; i++)
{
DEBUGSortedHistogramInsert(Source[i], Dest, i, Max);
}
}
internal void
DEBUGCollateScopeRecords (debug_services* Services)
{
@ -155,8 +197,13 @@ DEBUGCollateScopeRecords (debug_services* Services)
for (s32 h = 0; h < Services->ScopeHistogramUsed; h++)
{
DEBUGCacheScopeHistogramValues(Services->ScopeHistogram + h);
DEBUGCacheScopeHistogramValues(Services->ScopeHistogramUnsorted + h);
}
DEBUGSortHistogram(Services->ScopeHistogramUnsorted,
Services->ScopeHistogramSorted,
Services->ScopeHistogramUsed,
SCOPE_HISTOGRAM_SIZE);
}
internal void
@ -169,14 +216,14 @@ EndDebugFrame (debug_services* Services)
for (s32 i = 0; i < Services->ScopeHistogramUsed; i++)
{
s32 NewFrame = Services->ScopeHistogram[i].CurrentFrame + 1;
s32 NewFrame = Services->ScopeHistogramUnsorted[i].CurrentFrame + 1;
if (NewFrame >= HISTOGRAM_DEPTH)
{
NewFrame = 0;
}
Services->ScopeHistogram[i].CurrentFrame = NewFrame;
Services->ScopeHistogram[i].PerFrame_Cycles[NewFrame] = 0;
Services->ScopeHistogram[i].PerFrame_CallCount[NewFrame] = 0;
Services->ScopeHistogramUnsorted[i].CurrentFrame = NewFrame;
Services->ScopeHistogramUnsorted[i].PerFrame_Cycles[NewFrame] = 0;
Services->ScopeHistogramUnsorted[i].PerFrame_CallCount[NewFrame] = 0;
}
}

View File

@ -119,22 +119,22 @@ DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_c
{
v2 Register = v2{ColumnsStartX, TopOfScreenLinePos.y};
s32 CurrentFrame = GlobalDebugServices->ScopeHistogram[i].CurrentFrame - 1;
s32 CurrentFrame = GlobalDebugServices->ScopeHistogramSorted[i].CurrentFrame - 1;
if (CurrentFrame < 0) { CurrentFrame = HISTOGRAM_DEPTH - 1; }
u64 CyclesPerHit = GlobalDebugServices->ScopeHistogram[i].PerFrame_Cycles[CurrentFrame];
u64 CyclesPerHit = GlobalDebugServices->ScopeHistogramSorted[i].PerFrame_Cycles[CurrentFrame];
r32 SecondsPerHit = (r32)CyclesPerHit / (r32)GlobalDebugServices->PerformanceCountFrequency;
// Column 1
PrintF(&DebugString, "%.*s",
GlobalDebugServices->ScopeHistogram[i].ScopeName.Length,
GlobalDebugServices->ScopeHistogram[i].ScopeName.Memory);
GlobalDebugServices->ScopeHistogramSorted[i].ScopeName.Length,
GlobalDebugServices->ScopeHistogramSorted[i].ScopeName.Memory);
r32 ColumnOneX = DrawString(RenderBuffer, DebugString, Interface.Font, Interface.FontSize,
Register, WhiteV4).x;
Register.x += GSMax(ColumnOneX - Register.x, 250.f);
// Column 2
PrintF(&DebugString, "%d hits", GlobalDebugServices->ScopeHistogram[i].PerFrame_CallCount[CurrentFrame]);
PrintF(&DebugString, "%d hits", GlobalDebugServices->ScopeHistogramSorted[i].PerFrame_CallCount[CurrentFrame]);
r32 ColumnTwoX = DrawString(RenderBuffer, DebugString, Interface.Font, Interface.FontSize,
Register, WhiteV4).x;
Register.x += GSMax(ColumnTwoX - Register.x, 150.f);

View File

@ -413,17 +413,18 @@ BeginDraggingNode(app_state* State, node_interaction Interaction)
struct node_view_operation_state
{
node_offset SelectedNodeOffset;
};
FOLDHAUS_INPUT_COMMAND_PROC(NodeViewBeginMouseDragInteraction)
{
node_view_operation_state* OpState = GetCurrentOperationState(State->Modes, node_view_operation_state);
node_offset Node = GetNodeUnderPoint(State->NodeList, Mouse.DownPos, State->NodeRenderSettings);
if (Node.Node)
node_offset NodeOffset = GetNodeUnderPoint(State->NodeList, Mouse.DownPos, State->NodeRenderSettings);
if (NodeOffset.Node)
{
node_interaction NewInteraction = GetNodeInteractionType(Node.Node,
Node.Offset,
node_interaction NewInteraction = GetNodeInteractionType(NodeOffset.Node,
NodeOffset.Offset,
Mouse.Pos,
State->NodeRenderSettings);
if (IsDraggingNodePort(NewInteraction))
@ -439,9 +440,14 @@ FOLDHAUS_INPUT_COMMAND_PROC(NodeViewBeginMouseDragInteraction)
}
else // IsDraggingNode
{
OpState->SelectedNodeOffset = NodeOffset;
BeginDraggingNode(State, NewInteraction);
}
}
else
{
OpState->SelectedNodeOffset = InvalidNodeOffset();
}
}
FOLDHAUS_INPUT_COMMAND_PROC(NodeViewBeginMouseSelectInteraction)
@ -475,7 +481,108 @@ FOLDHAUS_INPUT_COMMAND_PROC(NodeViewBeginMouseSelectInteraction)
OPERATION_RENDER_PROC(RenderNodeView)
{
node_view_operation_state* OpState = (node_view_operation_state*)Operation.OpStateMemory;
RenderNodeList(State->NodeList, State->NodeRenderSettings, RenderBuffer);
DEBUG_TRACK_FUNCTION;
node_list_iterator NodeIter = GetNodeListIterator(*State->NodeList);
while (NodeIteratorIsValid(NodeIter))
{
interface_node* Node = NodeIter.At;
Node->Min = Node->MinAfterUpdate;
rect NodeBounds = CalculateNodeBounds(Node, State->NodeRenderSettings);
b32 DrawFields = PointIsInRect(Mouse.Pos, NodeBounds);
if (Node == OpState->SelectedNodeOffset.Node)
{
PushRenderQuad2D(RenderBuffer, NodeBounds.Min - v2{2, 2}, NodeBounds.Max + v2{2, 2}, WhiteV4);
}
PushRenderQuad2D(RenderBuffer, NodeBounds.Min, NodeBounds.Max, v4{.5f, .5f, .5f, 1.f});
DrawString(RenderBuffer, Node->Name, State->NodeRenderSettings.Font, State->NodeRenderSettings.Font->PixelHeight,
v2{NodeBounds.Min.x + 5, NodeBounds.Max.y - (State->NodeRenderSettings.Font->PixelHeight + NODE_HEADER_HEIGHT + 5)},
WhiteV4);
for (s32 Connection = 0; Connection < Node->ConnectionsCount; Connection++)
{
v4 PortColor = State->NodeRenderSettings.PortColors[Node->Connections[Connection].Type];
// Inputs
if (ConnectionIsInput(Node, Connection))
{
rect PortBounds = CalculateNodeInputPortBounds(Node, Connection, State->NodeRenderSettings);
DrawPort(RenderBuffer, PortBounds, PortColor);
//
// TODO(Peter): I don't like excluding OutputNode, feels too much like a special case
// but I don't want to get in to the meta programming right now.
// We should just generate a spec and struct member types for NodeType_OutputNode
//
// :ExcludingOutputNodeSpecialCase
//
if (Node->Type != NodeType_OutputNode && DrawFields)
{
node_specification Spec = NodeSpecifications[Node->Type - 1];
node_struct_member Member = Spec.MemberList[Connection];
DrawString(RenderBuffer, MakeString(Member.Name),
State->NodeRenderSettings.Font, State->NodeRenderSettings.Font->PixelHeight,
v2{PortBounds.Min.x - 32, PortBounds.Min.y}, WhiteV4);
}
rect ValueBounds = CalculateNodeInputValueBounds(Node, Connection, State->NodeRenderSettings);
DrawValueDisplay(RenderBuffer, ValueBounds, Node->Connections[Connection], State->NodeRenderSettings.Font);
// NOTE(Peter): its way easier to draw the connection on the input port b/c its a 1:1 relationship,
// whereas output ports might have many connections, they really only know about the most recent one
// Not sure if this is a problem. We mostly do everything backwards here, starting at the
// most downstream node and working back up to find dependencies.
if (ConnectionHasUpstreamConnection(Node, Connection))
{
rect ConnectedPortBounds = GetBoundsOfPortConnectedToInput(Node, Connection, State->NodeList, State->NodeRenderSettings);
v2 InputCenter = CalculateRectCenter(PortBounds);
v2 OutputCenter = CalculateRectCenter(ConnectedPortBounds);
PushRenderLine2D(RenderBuffer, OutputCenter, InputCenter, 1, WhiteV4);
}
}
// Outputs
if (ConnectionIsOutput(Node, Connection))
{
rect PortBounds = CalculateNodeOutputPortBounds(Node, Connection, State->NodeRenderSettings);
DrawPort(RenderBuffer, PortBounds, PortColor);
if (DrawFields)
{
node_specification Spec = NodeSpecifications[Node->Type - 1];
node_struct_member Member = Spec.MemberList[Connection];
DrawString(RenderBuffer, MakeString(Member.Name),
State->NodeRenderSettings.Font, State->NodeRenderSettings.Font->PixelHeight,
v2{PortBounds.Max.x + 8, PortBounds.Min.y}, WhiteV4);
}
rect ValueBounds = CalculateNodeOutputValueBounds(Node, Connection, State->NodeRenderSettings);
DrawValueDisplay(RenderBuffer, ValueBounds, Node->Connections[Connection], State->NodeRenderSettings.Font);
}
for (s32 Button = 0; Button < 3; Button++)
{
rect ButtonRect = CalculateNodeDragHandleBounds(NodeBounds, Button, State->NodeRenderSettings);
PushRenderQuad2D(RenderBuffer, ButtonRect.Min, ButtonRect.Max, DragButtonColors[Button]);
}
}
Next(&NodeIter);
}
}
FOLDHAUS_INPUT_COMMAND_PROC(NodeViewDeleteNode)
{
/*node_view_operation_state* OpState = (node_view_operation_state*)Operation.OpStateMemory;
if (IsValidOffset(OpState->SelectedNodeOffset))
{
}*/
}
FOLDHAUS_INPUT_COMMAND_PROC(CloseNodeView)
@ -488,6 +595,7 @@ input_command NodeViewCommands [] = {
{ KeyCode_A, KeyCode_Invalid, Command_Began, OpenNodeLister},
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, NodeViewBeginMouseDragInteraction},
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, NodeViewBeginMouseSelectInteraction},
{ KeyCode_X, KeyCode_Invalid, Command_Began, NodeViewDeleteNode},
};
FOLDHAUS_INPUT_COMMAND_PROC(OpenNodeView)
@ -498,6 +606,7 @@ FOLDHAUS_INPUT_COMMAND_PROC(OpenNodeView)
node_view_operation_state* OpState = CreateOperationState(NodeViewMode,
&State->Modes,
node_view_operation_state);
OpState->SelectedNodeOffset = InvalidNodeOffset();
}
////////////////////////////////////////

View File

@ -1,3 +1,20 @@
inline node_offset
InvalidNodeOffset()
{
node_offset Result = {};
// NOTE(Peter): I'm not sure this is actually invalid. Should be.
// If it is valid, it implies you can have offset from somewhere other than the beginning of
// the array, but where that point is isn't captured here so it seems like that shouldn't be
// correct
Result.Offset = -1;
Result.Node = 0; // NOTE(Peter): Pretty sure this is invalid tho ;)
return Result;
}
#define IsValidOffset(off) ((off.Node != 0) && (off.Offset >= 0))
#define IsInvalidOffset(off) ((off.Node == 0) && (off.Offset < 0))
inline s32
GetNodeMemorySize (interface_node Node)
@ -136,6 +153,7 @@ CalculateNodeHeight (s32 Members)
internal void
PushNodeOnListFromSpecification (node_list* List, node_specification Spec, v2 Min, memory_arena* Storage)
{
// :NodesDontNeedToKnowTheirBounds
r32 NodeHeight = CalculateNodeHeight (Spec.MemberListLength);
interface_node* Node = PushNodeOnList(List,
Spec.NameLength,
@ -1045,72 +1063,6 @@ DrawPort (render_command_buffer* RenderBuffer, rect Bounds, v4 Color)
PushRenderQuad2D(RenderBuffer, Bounds.Min, Bounds.Max, Color);
}
internal void
RenderNodeList (node_list* NodeList, node_render_settings RenderSettings, render_command_buffer* RenderBuffer)
{
DEBUG_TRACK_FUNCTION;
node_list_iterator NodeIter = GetNodeListIterator(*NodeList);
while (NodeIteratorIsValid(NodeIter))
{
interface_node* Node = NodeIter.At;
Node->Min = Node->MinAfterUpdate;
rect NodeBounds = CalculateNodeBounds(Node, RenderSettings);
PushRenderQuad2D(RenderBuffer, NodeBounds.Min, NodeBounds.Max, v4{.5f, .5f, .5f, 1.f});
DrawString(RenderBuffer, Node->Name, RenderSettings.Font, RenderSettings.Font->PixelHeight,
v2{NodeBounds.Min.x + 5, NodeBounds.Max.y - (RenderSettings.Font->PixelHeight + NODE_HEADER_HEIGHT + 5)},
WhiteV4);
for (s32 Connection = 0; Connection < Node->ConnectionsCount; Connection++)
{
// Inputs
if (ConnectionIsInput(Node, Connection))
{
rect PortBounds = CalculateNodeInputPortBounds(Node, Connection, RenderSettings);
v4 PortColor = RenderSettings.PortColors[Node->Connections[Connection].Type];
DrawPort(RenderBuffer, PortBounds, PortColor);
rect ValueBounds = CalculateNodeInputValueBounds(Node, Connection, RenderSettings);
DrawValueDisplay(RenderBuffer, ValueBounds, Node->Connections[Connection], RenderSettings.Font);
// NOTE(Peter): its way easier to draw the connection on the input port b/c its a 1:1 relationship,
// whereas output ports might have many connections, they really only know about the most recent one
// Not sure if this is a problem. We mostly do everything backwards here, starting at the
// most downstream node and working back up to find dependencies.
if (ConnectionHasUpstreamConnection(Node, Connection))
{
rect ConnectedPortBounds = GetBoundsOfPortConnectedToInput(Node, Connection, NodeList, RenderSettings);
v2 InputCenter = CalculateRectCenter(PortBounds);
v2 OutputCenter = CalculateRectCenter(ConnectedPortBounds);
PushRenderLine2D(RenderBuffer, OutputCenter, InputCenter, 1, WhiteV4);
}
}
// Outputs
if (ConnectionIsOutput(Node, Connection))
{
rect PortBounds = CalculateNodeOutputPortBounds(Node, Connection, RenderSettings);
v4 PortColor = RenderSettings.PortColors[Node->Connections[Connection].Type];
DrawPort(RenderBuffer, PortBounds, PortColor);
rect ValueBounds = CalculateNodeOutputValueBounds(Node, Connection, RenderSettings);
DrawValueDisplay(RenderBuffer, ValueBounds, Node->Connections[Connection], RenderSettings.Font);
}
for (s32 Button = 0; Button < 3; Button++)
{
rect ButtonRect = CalculateNodeDragHandleBounds(NodeBounds, Button, RenderSettings);
PushRenderQuad2D(RenderBuffer, ButtonRect.Min, ButtonRect.Max, DragButtonColors[Button]);
}
}
Next(&NodeIter);
}
}
internal void
ResetNodesUpdateState (node_list* NodeList)
{

View File

@ -57,6 +57,8 @@ typedef DRAW_FONT_CODEPOINT(platform_draw_font_codepoint);
// Worker Threads
#define PLATFORM_THREAD_COUNT 4
#define THREADED_WORK_PROC(name) void name(s32 ThreadID, void* Data)
typedef THREADED_WORK_PROC(threaded_work_proc);

View File

@ -18,6 +18,8 @@ DrawCharacter (render_quad_batch_constructor* BatchConstructor, char C, bitmap_f
return v2{Position.x + CodepointInfo.Width * FontScale, Position.y};
}
// TODO(Peter): Why are we passing in both the bitmap_font and the point size when everywhere, we just
// use the native point size of the font???
internal v2
DrawString (render_command_buffer* RenderBuffer, string String, bitmap_font* Font, s32 PointSize, v2 Position, v4 Color)
{

View File

@ -533,22 +533,22 @@ INT NCmdShow
// Set up worker threads
//
const s32 WorkerThreadCount = 2;
const s32 WorkerThreadCount = PLATFORM_THREAD_COUNT;
worker_thread_info* WorkerThreads = 0;
if (WorkerThreadCount > 0)
if (PLATFORM_THREAD_COUNT > 0)
{
WorkerThreads = (worker_thread_info*)malloc(sizeof(worker_thread_info) * WorkerThreadCount);
WorkerThreads = (worker_thread_info*)malloc(sizeof(worker_thread_info) * PLATFORM_THREAD_COUNT);
}
work_queue WorkQueue = {};
WorkQueue.SemaphoreHandle = CreateSemaphoreEx(0, 0, WorkerThreadCount, 0, 0, SEMAPHORE_ALL_ACCESS);
WorkQueue.SemaphoreHandle = CreateSemaphoreEx(0, 0, PLATFORM_THREAD_COUNT, 0, 0, SEMAPHORE_ALL_ACCESS);
WorkQueue.JobsMax = 256;
WorkQueue.NextJobIndex = 0;
WorkQueue.PushWorkOnQueue = Win32PushWorkOnQueue;
WorkQueue.DoQueueWorkUntilDone = Win32DoQueueWorkUntilDone;
WorkQueue.ResetWorkQueue = ResetWorkQueue;
for (s32 i = 0; i < WorkerThreadCount; i++)
for (s32 i = 0; i < PLATFORM_THREAD_COUNT; i++)
{
// ID = 0 is reserved for this thread
WorkerThreads[i].ID = i + 1;
@ -670,7 +670,7 @@ INT NCmdShow
CleanupResult = WSACleanup();
}while(CleanupResult == SOCKET_ERROR);
for (s32 Thread = 0; Thread < WorkerThreadCount; Thread++)
for (s32 Thread = 0; Thread < PLATFORM_THREAD_COUNT; Thread++)
{
TerminateThread(WorkerThreads[Thread].Handle, 0);
}

View File

@ -8,27 +8,32 @@ Hardening
- Then we want to think about separating out mode render functions from mode update functions. Not sure its necessary but having something that operates like an update funciton but is called render is weird. Might want some sort of coroutine functionality in place, where modes can add and remove optional, parallel
update functions
- memory visualization
- - Log memory allocations
- separate rendering thread
- cache led positions. Only update if they are moving
- - shift drag to 10x drag speed
- select nodes -> delete nodes
- remove node connections
UI Improvements
- highlight node field under active edit
- print node field names on hover or on the node
- draw cursor in node field under active edit
x print node field names on hover or on the node
- Mouse Held Commands
- Actual cursor states
API Improvements
- Clean up DrawString... good lord
- Add Text Alignment to DrawString
- Investigate why we're giving nodes bounds :NodesDontNeedToKnowTheirBounds
- - Cant we just calculate this at render time?
Application
- More efficient HSV <-> RGB
- Save and load a session
- - Serialize Nodes
- Don't render if the window isn't visible
Development
- Fix your scope time tracker to account for threads.
- Nest scope times so you can see totals/dig in
- Log memory allocations
Interface
- fullscreen
@ -40,10 +45,11 @@ Interface
- Update the text system - use system fonts
Switch To Nodes
- evaluation step (one node at a time)
- selector node (has a list of connections that it can switch between)
- serialize
- delete nodes
- remove node connections
- Serialize Nodes
- evaluation step (one node at a time)
Structure
- motion

View File

@ -20,6 +20,7 @@ x decided I don't like storing NodeRenderSettings in each operation mode. Probab
/Debug
x Make debug scope tracking thread safe - was throwing an error in stringsequal but that stopped.
x Keep an eye out.
x Sort Scope Tracking
Switch To Nodes
x basic node elements