Optimize GarbageCollect and DebugWindowUpdate, bring back profiler
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
- What precise workflow do I need for me to be viable to use this?
|
||||
- From a user (novice) point of view, how does it look like?
|
||||
|
||||
- Close all (ask to save)
|
||||
- Close buffer instead of window (ask to save)
|
||||
- Open all in the folder and ctrl + p like in VSCode (without special buffers)
|
||||
- ctrl + p like in VSCode (without special buffers)
|
||||
- Guide on the first page for new users with links to configs, tutorials
|
||||
|
||||
- Window, View, Buffer + flags design (or is completely new kind based approach needed for Windows/Views?)
|
||||
|
||||
@@ -1147,4 +1147,4 @@ API void CloseStdin(Process *process) {
|
||||
|
||||
API double GetTimeSeconds() {
|
||||
return GetTimeMicros() / 1000000.0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1436,6 +1436,18 @@ bool IsNull(Buffer *buffer) {
|
||||
return buffer->id.id == 0;
|
||||
}
|
||||
|
||||
void Close(BufferID id) {
|
||||
Buffer *buffer = GetBuffer(id, NULL);
|
||||
if (buffer) {
|
||||
if (buffer->id.id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
buffer->close = true;
|
||||
RunGCThisFrame = true;
|
||||
}
|
||||
}
|
||||
|
||||
Buffer *CreateBuffer(Allocator allocator, String name, Int size) {
|
||||
Buffer *result = AllocBuffer(allocator, name, size);
|
||||
Add(&Buffers, result);
|
||||
@@ -1467,15 +1479,13 @@ void InitBuffers() {
|
||||
Assert(null_buffer->id == NullBufferID && null_view->id == NullViewID);
|
||||
TraceBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(WorkDir, "trace"));
|
||||
TraceBuffer->special = true;
|
||||
TraceView = CreateView(TraceBuffer->id);
|
||||
TraceBuffer->no_history = true;
|
||||
GCInfoBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(WorkDir, "gc"));
|
||||
GCInfoBuffer->special = true;
|
||||
GCInfoBuffer->no_history = true;
|
||||
EventBuffer = CreateBuffer(sys_allocator, GetUniqueBufferName(WorkDir, "events"));
|
||||
EventBuffer->no_history = true;
|
||||
EventBuffer->special = true;
|
||||
ScratchBuffer = BufferOpenFile(GetUniqueBufferName(WorkDir, "scratch"));
|
||||
ScratchBuffer->special = true;
|
||||
}
|
||||
|
||||
Int ConvertUTF8ToUTF16UnixLine(String string, char16_t *buffer, Int buffer_cap) {
|
||||
@@ -1558,7 +1568,7 @@ bool BufferIsReferenced(BufferID buffer_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (FindView(buffer_id)) {
|
||||
if (FindView(buffer_id, NULL)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ struct Buffer {
|
||||
unsigned changed_on_disk : 1;
|
||||
unsigned garbage : 1;
|
||||
unsigned dont_try_to_save_in_bulk_ops : 1;
|
||||
unsigned kill : 1;
|
||||
unsigned close : 1;
|
||||
unsigned special : 1;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -248,7 +248,7 @@ void ReportWarningf(const char *fmt, ...) {
|
||||
void ReportDebugf(const char *fmt, ...) {
|
||||
Scratch scratch;
|
||||
STRING_FORMAT(scratch, fmt, string);
|
||||
Appendf(TraceView, "%S\n", string);
|
||||
RawAppendf(TraceBuffer, "%S\n", string);
|
||||
}
|
||||
|
||||
void MoveCursorByPageSize(Window *window, int direction, bool shift = false) {
|
||||
@@ -427,29 +427,6 @@ void NewDir(Window *window, String name = "") {
|
||||
Open(name);
|
||||
}
|
||||
|
||||
String CodeEndings[] = { ".c", ".cpp", ".h", ".hpp", ".py", ".lua", ".cxx", ".hxx", };
|
||||
|
||||
void ListFilesRecursive(Buffer *buffer, String dir) {
|
||||
Scratch scratch(buffer->line_starts.allocator);
|
||||
for (FileIter it = IterateFiles(scratch, dir); IsValid(it); Advance(&it)) {
|
||||
if (it.filename == ".git") {
|
||||
continue;
|
||||
}
|
||||
if (it.is_directory) {
|
||||
ListFilesRecursive(buffer, it.absolute_path);
|
||||
} else {
|
||||
bool match = false;
|
||||
ForItem (ending, CodeEndings) {
|
||||
if (EndsWith(it.absolute_path, ending)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) RawAppendf(buffer, "%-80S || %S\n", it.filename, it.absolute_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
View *ExecHidden(String buffer_name, String cmd, String working_dir) {
|
||||
View *view = OpenBufferView(buffer_name);
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
@@ -668,14 +645,66 @@ void Command_SetWorkDir() {
|
||||
WorkDir = ChopLastSlash(main.buffer->name);
|
||||
} RegisterCommand(Command_SetWorkDir, "");
|
||||
|
||||
String CodeEndings[] = { ".c", ".cpp", ".h", ".hpp", ".py", ".lua", ".cxx", ".hxx", };
|
||||
|
||||
void ListFilesRecursive(Buffer *buffer, String dir) {
|
||||
Scratch scratch(buffer->line_starts.allocator);
|
||||
for (FileIter it = IterateFiles(scratch, dir); IsValid(it); Advance(&it)) {
|
||||
if (it.filename == ".git") {
|
||||
continue;
|
||||
}
|
||||
if (it.is_directory) {
|
||||
ListFilesRecursive(buffer, it.absolute_path);
|
||||
} else {
|
||||
bool match = false;
|
||||
ForItem (ending, CodeEndings) {
|
||||
if (EndsWith(it.absolute_path, ending)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) RawAppendf(buffer, "%-80S || %S\n", it.filename, it.absolute_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenCodeRecursive(String dir) {
|
||||
Scratch scratch;
|
||||
for (FileIter it = IterateFiles(scratch, dir); IsValid(it); Advance(&it)) {
|
||||
if (it.filename == ".git") {
|
||||
continue;
|
||||
}
|
||||
if (it.filename == "SDL") {
|
||||
continue;
|
||||
}
|
||||
if (it.is_directory) {
|
||||
OpenCodeRecursive(it.absolute_path);
|
||||
} else {
|
||||
bool match = false;
|
||||
ForItem (ending, CodeEndings) {
|
||||
if (EndsWith(it.absolute_path, ending)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
BufferOpenFile(it.absolute_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Command_OpenCode() {
|
||||
OpenCodeRecursive(WorkDir);
|
||||
} RegisterCommand(Command_OpenCode, "");
|
||||
|
||||
void Command_KillProcess() {
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
KillProcess(main.view);
|
||||
} RegisterCommand(Command_KillProcess, "");
|
||||
|
||||
void Command_CloseWindow() {
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
main.window->kill = true;
|
||||
Close(LastActiveLayoutWindowID);
|
||||
} RegisterCommand(Command_CloseWindow, "");
|
||||
|
||||
SaveResult TrySavingBuffer(Buffer *buffer) {
|
||||
@@ -717,7 +746,7 @@ void Command_Close() {
|
||||
}
|
||||
|
||||
main.window->active_view = FindInactiveView();
|
||||
main.view->kill = true;
|
||||
Close(main.view->id);
|
||||
|
||||
bool ref = false;
|
||||
For (Views) {
|
||||
@@ -731,7 +760,7 @@ void Command_Close() {
|
||||
}
|
||||
|
||||
if (!ref) {
|
||||
main.buffer->kill = true;
|
||||
Close(main.buffer->id);
|
||||
}
|
||||
} RegisterCommand(Command_Close, "ctrl-w");
|
||||
|
||||
@@ -747,13 +776,13 @@ void Command_CloseAll() {
|
||||
if (it->special) {
|
||||
continue;
|
||||
}
|
||||
it->kill = true;
|
||||
Close(it->id);
|
||||
}
|
||||
For (Buffers) {
|
||||
if (it->special) {
|
||||
continue;
|
||||
}
|
||||
it->kill = true;
|
||||
Close(it->id);
|
||||
}
|
||||
}
|
||||
} RegisterCommand(Command_CloseAll, "");
|
||||
|
||||
@@ -17,6 +17,7 @@ mco_coro *CoAdd(CoroutineProc *proc) {
|
||||
}
|
||||
|
||||
void CoUpdate(Event *event) {
|
||||
ProfileFunction();
|
||||
IterRemove(ActiveCoroutines) {
|
||||
IterRemovePrepare(ActiveCoroutines);
|
||||
|
||||
@@ -45,4 +46,4 @@ Event *CoYield(mco_coro *co) {
|
||||
Assert(ok == MCO_SUCCESS);
|
||||
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ void DrawUnderline(Window *window, View *view, Buffer *buffer, Range range, Colo
|
||||
}
|
||||
|
||||
void DrawWindow(Window *window, Event &event) {
|
||||
ProfileFunction();
|
||||
View *view = GetView(window->active_view);
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
Rect2 screen_rect = Rect0Size(event.xwindow, event.ywindow);
|
||||
|
||||
@@ -5,6 +5,7 @@ int FullScreenPositionX, FullScreenPositionY;
|
||||
bool Testing = false;
|
||||
bool AppIsRunning = true;
|
||||
bool WaitForEvents = true;
|
||||
bool RunGCThisFrame;
|
||||
|
||||
WindowID WindowIDs;
|
||||
ViewID ViewIDs;
|
||||
@@ -40,7 +41,6 @@ Caret DocumentAnchor;
|
||||
|
||||
Buffer *GCInfoBuffer;
|
||||
Buffer *EventBuffer;
|
||||
Buffer *ScratchBuffer;
|
||||
Buffer *TraceBuffer;
|
||||
View *TraceView;
|
||||
|
||||
@@ -50,8 +50,8 @@ Array<Event> EventPlayback;
|
||||
BlockArena Perm;
|
||||
|
||||
// clipboard
|
||||
BlockArena ClipboardArena;
|
||||
String16 SavedClipboardString;
|
||||
BlockArena ClipboardArena;
|
||||
String16 SavedClipboardString;
|
||||
Array<String16> SavedClipboardCarets = {SysAllocator};
|
||||
|
||||
struct InternTable {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// 'less' program which errors out and doesn't print anything
|
||||
// @todo: maybe I should ask someone smarter about this!
|
||||
void UpdateProcesses() {
|
||||
ProfileFunction();
|
||||
IterRemove(ActiveProcesses) {
|
||||
IterRemovePrepare(ActiveProcesses);
|
||||
Scratch scratch;
|
||||
@@ -70,4 +71,4 @@ bool ProcessIsActive(ViewID view) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,373 @@
|
||||
#if 0
|
||||
#include "spall.h"
|
||||
#if 1
|
||||
// SPDX-FileCopyrightText: © 2023 Phillip Trudeau-Tavara <pmttavara@protonmail.com>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/*
|
||||
|
||||
TODO: Optional Helper APIs:
|
||||
|
||||
- Compression API: would require a mutexed lockable context (yuck...)
|
||||
- Either using a ZIP library, a name cache + TIDPID cache, or both (but ZIP is likely more than enough!!!)
|
||||
- begin()/end() writes compressed chunks to a caller-determined destination
|
||||
- The destination can be the buffered-writing API or a custom user destination
|
||||
- Ultimately need to take a lock with some granularity... can that be the caller's responsibility?
|
||||
|
||||
- Counter Event: should allow tracking arbitrary named values with a single event, for memory and frame profiling
|
||||
|
||||
- Ring-buffer API
|
||||
spall_ring_init
|
||||
spall_ring_emit_begin
|
||||
spall_ring_emit_end
|
||||
spall_ring_flush
|
||||
*/
|
||||
|
||||
#ifndef SPALL_H
|
||||
#define SPALL_H
|
||||
|
||||
#if !defined(_MSC_VER) || defined(__clang__)
|
||||
#define SPALL_NOINSTRUMENT __attribute__((no_instrument_function))
|
||||
#define SPALL_FORCEINLINE __attribute__((always_inline))
|
||||
#else
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#define SPALL_NOINSTRUMENT // Can't noinstrument on MSVC!
|
||||
#define SPALL_FORCEINLINE __forceinline
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define SPALL_FN static inline SPALL_NOINSTRUMENT
|
||||
#define SPALL_MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define SPALL_MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef struct SpallHeader {
|
||||
uint64_t magic_header; // = 0x0BADF00D
|
||||
uint64_t version; // = 3
|
||||
double timestamp_unit;
|
||||
uint64_t must_be_0;
|
||||
} SpallHeader;
|
||||
|
||||
typedef enum {
|
||||
SpallEventType_Invalid = 0,
|
||||
SpallEventType_Custom_Data = 1, // Basic readers can skip this.
|
||||
SpallEventType_StreamOver = 2,
|
||||
|
||||
SpallEventType_Begin = 3,
|
||||
SpallEventType_End = 4,
|
||||
SpallEventType_Instant = 5,
|
||||
|
||||
SpallEventType_Overwrite_Timestamp = 6, // Retroactively change timestamp units - useful for incrementally improving RDTSC frequency.
|
||||
SpallEventType_Pad_Skip = 7,
|
||||
|
||||
SpallEventType_NameProcess = 8,
|
||||
SpallEventType_NameThread = 9,
|
||||
} SpallEventType;
|
||||
|
||||
typedef struct SpallBufferHeader {
|
||||
uint32_t size;
|
||||
uint32_t tid;
|
||||
uint32_t pid;
|
||||
uint64_t first_ts;
|
||||
} SpallBufferHeader;
|
||||
|
||||
typedef struct SpallBeginEvent {
|
||||
uint8_t type; // = SpallEventType_Begin
|
||||
uint64_t when;
|
||||
|
||||
uint8_t name_length;
|
||||
uint8_t args_length;
|
||||
} SpallBeginEvent;
|
||||
|
||||
typedef struct SpallBeginEventMax {
|
||||
SpallBeginEvent event;
|
||||
char name_bytes[255];
|
||||
char args_bytes[255];
|
||||
} SpallBeginEventMax;
|
||||
|
||||
typedef struct SpallEndEvent {
|
||||
uint8_t type; // = SpallEventType_End
|
||||
uint64_t when;
|
||||
} SpallEndEvent;
|
||||
|
||||
typedef struct SpallPadSkipEvent {
|
||||
uint8_t type; // = SpallEventType_Pad_Skip
|
||||
uint32_t size;
|
||||
} SpallPadSkipEvent;
|
||||
|
||||
typedef struct SpallNameContainerEvent {
|
||||
uint8_t type; // = SpallEventType_NameThread/Process
|
||||
uint8_t name_length;
|
||||
} SpallNameContainerEvent;
|
||||
|
||||
typedef struct SpallNameContainerEventMax {
|
||||
SpallNameContainerEvent event;
|
||||
char name_bytes[255];
|
||||
} SpallNameContainerEventMax;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef struct SpallProfile SpallProfile;
|
||||
|
||||
// Important!: If you define your own callbacks, mark them SPALL_NOINSTRUMENT!
|
||||
typedef bool (*SpallWriteCallback)(SpallProfile *self, const void *data, size_t length);
|
||||
typedef bool (*SpallFlushCallback)(SpallProfile *self);
|
||||
typedef void (*SpallCloseCallback)(SpallProfile *self);
|
||||
|
||||
struct SpallProfile {
|
||||
double timestamp_unit;
|
||||
SpallWriteCallback write;
|
||||
SpallFlushCallback flush;
|
||||
SpallCloseCallback close;
|
||||
void *data;
|
||||
};
|
||||
|
||||
// Important!: If you are writing Begin/End events, then do NOT write
|
||||
// events for the same PID + TID pair on different buffers!!!
|
||||
typedef struct SpallBuffer {
|
||||
void *data;
|
||||
size_t length;
|
||||
uint32_t tid;
|
||||
uint32_t pid;
|
||||
|
||||
// Internal data - don't assign this
|
||||
size_t head;
|
||||
uint64_t first_ts;
|
||||
} SpallBuffer;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
SPALL_FN SPALL_FORCEINLINE bool spall__file_write(SpallProfile *ctx, const void *p, size_t n) {
|
||||
if (fwrite(p, n, 1, (FILE *)ctx->data) != 1) return false;
|
||||
return true;
|
||||
}
|
||||
SPALL_FN bool spall__file_flush(SpallProfile *ctx) {
|
||||
if (fflush((FILE *)ctx->data)) return false;
|
||||
return true;
|
||||
}
|
||||
SPALL_FN void spall__file_close(SpallProfile *ctx) {
|
||||
fclose((FILE *)ctx->data);
|
||||
ctx->data = NULL;
|
||||
}
|
||||
|
||||
SPALL_FN SPALL_FORCEINLINE bool spall__buffer_flush(SpallProfile *ctx, SpallBuffer *wb, uint64_t ts) {
|
||||
wb->first_ts = SPALL_MAX(wb->first_ts, ts);
|
||||
|
||||
SpallBufferHeader hdr;
|
||||
hdr.size = (uint32_t)(wb->head - sizeof(SpallBufferHeader));
|
||||
hdr.pid = wb->pid;
|
||||
hdr.tid = wb->tid;
|
||||
hdr.first_ts = wb->first_ts;
|
||||
|
||||
memcpy(wb->data, &hdr, sizeof(hdr));
|
||||
|
||||
if (!ctx->write(ctx, wb->data, wb->head)) return false;
|
||||
if (!ctx->flush(ctx)) return false;
|
||||
wb->head = sizeof(SpallBufferHeader);
|
||||
return true;
|
||||
}
|
||||
|
||||
SPALL_FN bool spall_buffer_flush(SpallProfile *ctx, SpallBuffer *wb) {
|
||||
if (!spall__buffer_flush(ctx, wb, 0)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
SPALL_FN bool spall_buffer_quit(SpallProfile *ctx, SpallBuffer *wb) {
|
||||
if (!spall_buffer_flush(ctx, wb)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
SPALL_FN size_t spall_build_header(void *buffer, size_t rem_size, double timestamp_unit) {
|
||||
size_t header_size = sizeof(SpallHeader);
|
||||
if (header_size > rem_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SpallHeader *header = (SpallHeader *)buffer;
|
||||
header->magic_header = 0x0BADF00D;
|
||||
header->version = 3;
|
||||
header->timestamp_unit = timestamp_unit;
|
||||
header->must_be_0 = 0;
|
||||
return header_size;
|
||||
}
|
||||
SPALL_FN SPALL_FORCEINLINE size_t spall_build_begin(void *buffer, size_t rem_size, const char *name, int32_t name_len, const char *args, int32_t args_len, uint64_t when) {
|
||||
SpallBeginEventMax *ev = (SpallBeginEventMax *)buffer;
|
||||
uint8_t trunc_name_len = (uint8_t)SPALL_MIN(name_len, 255); // will be interpreted as truncated in the app (?)
|
||||
uint8_t trunc_args_len = (uint8_t)SPALL_MIN(args_len, 255); // will be interpreted as truncated in the app (?)
|
||||
|
||||
size_t ev_size = sizeof(SpallBeginEvent) + trunc_name_len + trunc_args_len;
|
||||
if (ev_size > rem_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ev->event.type = SpallEventType_Begin;
|
||||
ev->event.when = when;
|
||||
ev->event.name_length = trunc_name_len;
|
||||
ev->event.args_length = trunc_args_len;
|
||||
memcpy(ev->name_bytes, name, trunc_name_len);
|
||||
memcpy(ev->name_bytes + trunc_name_len, args, trunc_args_len);
|
||||
|
||||
return ev_size;
|
||||
}
|
||||
SPALL_FN SPALL_FORCEINLINE size_t spall_build_end(void *buffer, size_t rem_size, uint64_t when) {
|
||||
size_t ev_size = sizeof(SpallEndEvent);
|
||||
if (ev_size > rem_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SpallEndEvent *ev = (SpallEndEvent *)buffer;
|
||||
ev->type = SpallEventType_End;
|
||||
ev->when = when;
|
||||
|
||||
return ev_size;
|
||||
}
|
||||
SPALL_FN SPALL_FORCEINLINE size_t spall_build_name(void *buffer, size_t rem_size, const char *name, int32_t name_len, SpallEventType type) {
|
||||
SpallNameContainerEventMax *ev = (SpallNameContainerEventMax *)buffer;
|
||||
uint8_t trunc_name_len = (uint8_t)SPALL_MIN(name_len, 255); // will be interpreted as truncated in the app (?)
|
||||
|
||||
size_t ev_size = sizeof(SpallNameContainerEvent) + trunc_name_len;
|
||||
if (ev_size > rem_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ev->event.type = type;
|
||||
ev->event.name_length = trunc_name_len;
|
||||
memcpy(ev->name_bytes, name, trunc_name_len);
|
||||
|
||||
return ev_size;
|
||||
}
|
||||
|
||||
SPALL_FN void spall_quit(SpallProfile *ctx) {
|
||||
if (!ctx) return;
|
||||
if (ctx->close) ctx->close(ctx);
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
}
|
||||
|
||||
SPALL_FN bool spall_init_callbacks(double timestamp_unit,
|
||||
SpallWriteCallback write,
|
||||
SpallFlushCallback flush,
|
||||
SpallCloseCallback close,
|
||||
void *userdata,
|
||||
SpallProfile *ctx) {
|
||||
|
||||
if (timestamp_unit < 0) return false;
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->timestamp_unit = timestamp_unit;
|
||||
ctx->data = userdata;
|
||||
ctx->write = write;
|
||||
ctx->flush = flush;
|
||||
ctx->close = close;
|
||||
|
||||
SpallHeader header;
|
||||
size_t len = spall_build_header(&header, sizeof(header), timestamp_unit);
|
||||
if (!ctx->write(ctx, &header, len)) {
|
||||
spall_quit(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SPALL_FN bool spall_init_file(const char* filename, double timestamp_unit, SpallProfile *ctx) {
|
||||
if (!filename) return false;
|
||||
|
||||
FILE *f = fopen(filename, "wb"); // TODO: handle utf8 and long paths on windows
|
||||
if (f) { // basically freopen() but we don't want to force users to lug along another macro define
|
||||
fclose(f);
|
||||
f = fopen(filename, "ab");
|
||||
}
|
||||
if (!f) { return false; }
|
||||
|
||||
return spall_init_callbacks(timestamp_unit, spall__file_write, spall__file_flush, spall__file_close, (void *)f, ctx);
|
||||
}
|
||||
|
||||
SPALL_FN bool spall_flush(SpallProfile *ctx) {
|
||||
if (!ctx->flush(ctx)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
SPALL_FN bool spall_buffer_init(SpallProfile *ctx, SpallBuffer *wb) {
|
||||
// Fails if buffer is not big enough to contain at least one event!
|
||||
if (wb->length < sizeof(SpallBufferHeader) + sizeof(SpallBeginEventMax)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wb->head = sizeof(SpallBufferHeader);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SPALL_FN SPALL_FORCEINLINE bool spall_buffer_begin_args(SpallProfile *ctx, SpallBuffer *wb, const char *name, int32_t name_len, const char *args, int32_t args_len, uint64_t when) {
|
||||
if ((wb->head + sizeof(SpallBeginEventMax)) > wb->length) {
|
||||
if (!spall__buffer_flush(ctx, wb, when)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
wb->head += spall_build_begin((char *)wb->data + wb->head, wb->length - wb->head, name, name_len, args, args_len, when);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SPALL_FN bool spall_buffer_begin(SpallProfile *ctx, SpallBuffer *wb, const char *name, int32_t name_len, uint64_t when) {
|
||||
return spall_buffer_begin_args(ctx, wb, name, name_len, "", 0, when);
|
||||
}
|
||||
|
||||
SPALL_FN bool spall_buffer_end(SpallProfile *ctx, SpallBuffer *wb, uint64_t when) {
|
||||
if ((wb->head + sizeof(SpallEndEvent)) > wb->length) {
|
||||
if (!spall__buffer_flush(ctx, wb, when)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
wb->head += spall_build_end((char *)wb->data + wb->head, wb->length - wb->head, when);
|
||||
return true;
|
||||
}
|
||||
|
||||
SPALL_FN bool spall_buffer_name_thread(SpallProfile *ctx, SpallBuffer *wb, const char *name, int32_t name_len) {
|
||||
if ((wb->head + sizeof(SpallNameContainerEvent)) > wb->length) {
|
||||
if (!spall__buffer_flush(ctx, wb, 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
wb->head += spall_build_name((char *)wb->data + wb->head, wb->length - wb->head, name, name_len, SpallEventType_NameThread);
|
||||
return true;
|
||||
}
|
||||
|
||||
SPALL_FN bool spall_buffer_name_process(SpallProfile *ctx, SpallBuffer *wb, const char *name, int32_t name_len) {
|
||||
if ((wb->head + sizeof(SpallNameContainerEvent)) > wb->length) {
|
||||
if (!spall__buffer_flush(ctx, wb, 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
wb->head += spall_build_name((char *)wb->data + wb->head, wb->length - wb->head, name, name_len, SpallEventType_NameProcess);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SPALL_H
|
||||
|
||||
|
||||
static SpallProfile spall_ctx;
|
||||
static SpallBuffer spall_buffer;
|
||||
double GetTimeMicros(void);
|
||||
|
||||
void BeginProfiler() {
|
||||
spall_ctx = spall_init_file("hello_world.spall", 1);
|
||||
spall_init_file("te.spall", 1, &spall_ctx);
|
||||
|
||||
int buffer_size = 1 * 1024 * 1024;
|
||||
unsigned char *buffer = (unsigned char *)malloc(buffer_size);
|
||||
@@ -25,14 +386,14 @@ void _BeginProfileScope(const char *name, int len) {
|
||||
spall_buffer_begin(&spall_ctx, &spall_buffer,
|
||||
name, // name of your name
|
||||
len, // name len minus the null terminator
|
||||
GetTimeMicros() // timestamp in microseconds -- start of your timing block
|
||||
(uint64_t)GetTimeMicros() // timestamp in microseconds -- start of your timing block
|
||||
);
|
||||
}
|
||||
#define BeginProfileScope(name) _BeginProfileScope(#name, sizeof(#name) - 1)
|
||||
|
||||
void EndProfileScope() {
|
||||
spall_buffer_end(&spall_ctx, &spall_buffer,
|
||||
GetTimeMicros() // timestamp in microseconds -- end of your timing block
|
||||
(uint64_t)GetTimeMicros() // timestamp in microseconds -- end of your timing block
|
||||
);
|
||||
}
|
||||
#define ProfileScope(name) ProfileScope_ PROFILE_SCOPE_VAR_##name(#name, sizeof(#name) - 1)
|
||||
@@ -48,4 +409,4 @@ struct ProfileScope_ {
|
||||
#define EndProfiler()
|
||||
#define BeginProfileScope(name)
|
||||
#define EndProfileScope()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -412,32 +412,20 @@ void OnCommand(Event event) {
|
||||
if (event.kind == EVENT_QUIT) {
|
||||
Command_Quit();
|
||||
}
|
||||
|
||||
IF_DEBUG(AssertRanges(main.view->carets));
|
||||
IF_DEBUG(AssertRanges(active.view->carets));
|
||||
}
|
||||
|
||||
void GarbageCollect() {
|
||||
if (RunGCThisFrame == false) {
|
||||
return;
|
||||
}
|
||||
RunGCThisFrame = false;
|
||||
|
||||
ProfileFunction();
|
||||
Allocator sys_allocator = GetSystemAllocator();
|
||||
|
||||
For (Views) {
|
||||
IF_DEBUG(AssertRanges(it->carets));
|
||||
}
|
||||
|
||||
For (Windows) {
|
||||
if (it->sync_visibility_with_focus) {
|
||||
if (it->id == ActiveWindowID) {
|
||||
it->visible = true;
|
||||
} else {
|
||||
it->visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Window *window = GetWindow(ActiveWindowID);
|
||||
if (ActiveWindowID.id != LastActiveLayoutWindowID.id) {
|
||||
if (window->layout) {
|
||||
LastActiveLayoutWindowID = ActiveWindowID;
|
||||
}
|
||||
}
|
||||
|
||||
For(Buffers) {
|
||||
if (it->file_mod_time) {
|
||||
int64_t new_file_mod_time = GetFileModTime(it->name);
|
||||
@@ -451,8 +439,12 @@ void GarbageCollect() {
|
||||
IterRemove(Views) {
|
||||
IterRemovePrepare(Views);
|
||||
|
||||
if (it->close && it->id.id == 0) {
|
||||
InvalidCodepath();
|
||||
}
|
||||
|
||||
Buffer *buffer = GetBuffer(it->active_buffer);
|
||||
if (it->kill == 0) {
|
||||
if (!it->close) {
|
||||
if (!buffer->garbage) {
|
||||
continue;
|
||||
}
|
||||
@@ -472,13 +464,11 @@ void GarbageCollect() {
|
||||
IterRemove(Buffers) {
|
||||
IterRemovePrepare(Buffers);
|
||||
|
||||
|
||||
if (it->id.id == 0) {
|
||||
it->kill = 0;
|
||||
continue;
|
||||
if (it->close && it->id.id == 0) {
|
||||
InvalidCodepath();
|
||||
}
|
||||
|
||||
if (it->kill == 0) {
|
||||
if (!it->close) {
|
||||
if (!it->garbage) {
|
||||
continue;
|
||||
}
|
||||
@@ -496,7 +486,11 @@ void GarbageCollect() {
|
||||
|
||||
IterRemove(Windows) {
|
||||
IterRemovePrepare(Windows);
|
||||
if (it->kill) {
|
||||
if (it->close && it->id.id == 0) {
|
||||
InvalidCodepath();
|
||||
}
|
||||
|
||||
if (it->close) {
|
||||
RawAppendf(GCInfoBuffer, "Wind %d %S\n", (int)it->id.id);
|
||||
Dealloc(&it->goto_history);
|
||||
Dealloc(&it->goto_redo);
|
||||
@@ -512,10 +506,12 @@ void Update(Event event) {
|
||||
Scratch scratch;
|
||||
Array<Window *> order = GetWindowZOrder(scratch);
|
||||
For(order) {
|
||||
if (!it->visible) continue;
|
||||
View *view = GetView(it->active_view);
|
||||
if (!it->visible) {
|
||||
continue;
|
||||
}
|
||||
View *view = GetView(it->active_view);
|
||||
view->main_caret_on_begin_frame = view->carets[0];
|
||||
view->update_scroll = true;
|
||||
view->update_scroll = true;
|
||||
}
|
||||
|
||||
OnCommand(event);
|
||||
@@ -524,7 +520,27 @@ void Update(Event event) {
|
||||
CommandWindowUpdate();
|
||||
UpdateProcesses();
|
||||
CoUpdate(&event);
|
||||
GarbageCollect();
|
||||
|
||||
{
|
||||
ProfileScope(WindowEndOfFrameVisibilityAndLastActive);
|
||||
For (Windows) {
|
||||
if (it->sync_visibility_with_focus) {
|
||||
if (it->id == ActiveWindowID) {
|
||||
it->visible = true;
|
||||
} else {
|
||||
it->visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Window *window = GetWindow(ActiveWindowID);
|
||||
if (ActiveWindowID.id != LastActiveLayoutWindowID.id) {
|
||||
if (window->layout) {
|
||||
LastActiveLayoutWindowID = ActiveWindowID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
For(IterateInReverse(&order)) {
|
||||
if (!it->visible) continue;
|
||||
@@ -532,6 +548,8 @@ void Update(Event event) {
|
||||
View *view = GetView(it->active_view);
|
||||
UpdateScroll(it, !AreEqual(view->main_caret_on_begin_frame, view->carets[0]) && view->update_scroll);
|
||||
}
|
||||
|
||||
GarbageCollect();
|
||||
}
|
||||
|
||||
void Windows_SetupVCVarsall(mco_coro *co) {
|
||||
@@ -564,9 +582,9 @@ void Windows_SetupVCVarsall(mco_coro *co) {
|
||||
}
|
||||
|
||||
void MainLoop() {
|
||||
ProfileFunction();
|
||||
Scratch scratch;
|
||||
FrameID += 1;
|
||||
|
||||
Scratch scratch;
|
||||
Array<Event> frame_events = GetEventsForFrame(scratch);
|
||||
Serializer ser = {EventBuffer};
|
||||
For(frame_events) {
|
||||
|
||||
@@ -80,6 +80,17 @@ API bool ViewIsReferenced(ViewID view) {
|
||||
return ViewIsActive(view);
|
||||
}
|
||||
|
||||
void Close(ViewID id) {
|
||||
View *view = GetView(id);
|
||||
if (view) {
|
||||
if (view->id.id == 0) {
|
||||
return;
|
||||
}
|
||||
view->close = true;
|
||||
RunGCThisFrame = true;
|
||||
}
|
||||
}
|
||||
|
||||
ViewID FindInactiveView(ViewID default_view = NullViewID) {
|
||||
For (Views) {
|
||||
if (!ViewIsActive(it->id)) return it->id;
|
||||
|
||||
@@ -13,7 +13,7 @@ struct View {
|
||||
|
||||
String16 prev_search_line;
|
||||
struct {
|
||||
unsigned kill : 1;
|
||||
unsigned close : 1;
|
||||
unsigned special : 1;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -18,13 +18,24 @@ Window *CreateWind() {
|
||||
return w;
|
||||
}
|
||||
|
||||
inline Window *GetWindow(WindowID id, Window *default_window = Windows[0]) {
|
||||
Window *GetWindow(WindowID id, Window *default_window = Windows[0]) {
|
||||
For(Windows) {
|
||||
if (it->id == id) return it;
|
||||
}
|
||||
return default_window;
|
||||
}
|
||||
|
||||
void Close(WindowID id) {
|
||||
Window *window = GetWindow(id, NULL);
|
||||
if (window) {
|
||||
if (window->id.id == 0) {
|
||||
return;
|
||||
}
|
||||
window->close = true;
|
||||
RunGCThisFrame = true;
|
||||
}
|
||||
}
|
||||
|
||||
Window *FindWindow(ViewID view_id, Window *default_window = NULL) {
|
||||
For(Windows) {
|
||||
if (it->active_view == view_id) {
|
||||
@@ -36,7 +47,7 @@ Window *FindWindow(ViewID view_id, Window *default_window = NULL) {
|
||||
|
||||
Window *FindWindow(String buffer_name, Window *default_window = NULL) {
|
||||
For(Windows) {
|
||||
View *it_view = GetView(it->active_view);
|
||||
View *it_view = GetView(it->active_view);
|
||||
Buffer *it_buffer = GetBuffer(it_view->active_buffer);
|
||||
if (it_buffer->name == buffer_name) {
|
||||
return it;
|
||||
@@ -136,6 +147,7 @@ double WindowCalcEvenResizerValue(Int screen_size_x, Int *out_count = NULL) {
|
||||
}
|
||||
|
||||
void LayoutWindows(int16_t wx, int16_t wy) {
|
||||
ProfileFunction();
|
||||
Rect2I screen_rect = RectI0Size(wx, wy);
|
||||
|
||||
CommandWindowLayout(&screen_rect, wx, wy);
|
||||
@@ -237,8 +249,10 @@ void CheckpointBeforeGoto(Window *window) {
|
||||
GotoCrumb GetCrumb(Array<GotoCrumb> *cr) {
|
||||
for (; cr->len;) {
|
||||
GotoCrumb c = Pop(cr);
|
||||
View *view = FindView(c.view_id);
|
||||
if (view) return c;
|
||||
View *view = GetView(c.view_id);
|
||||
if (view) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ struct Window {
|
||||
bool draw_line_highlight : 1;
|
||||
bool visible : 1;
|
||||
bool layout : 1;
|
||||
bool kill : 1;
|
||||
bool close : 1;
|
||||
bool sync_visibility_with_focus : 1;
|
||||
bool lose_focus_on_escape : 1;
|
||||
bool jump_history : 1;
|
||||
@@ -60,4 +60,4 @@ void StatusWindowInit();
|
||||
void StatusWindowLayout(Rect2I *rect, Int wx, Int wy);
|
||||
|
||||
void DebugWindowInit();
|
||||
void DebugWindowLayout(Rect2I *rect, Int wx, Int wy);
|
||||
void DebugWindowLayout(Rect2I *rect, Int wx, Int wy);
|
||||
|
||||
@@ -28,6 +28,7 @@ void CommandWindowLayout(Rect2I *rect, Int wx, Int wy) {
|
||||
}
|
||||
|
||||
void CommandWindowUpdate() {
|
||||
ProfileFunction();
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
if (active.window->id == CommandBarWindowID) {
|
||||
if (!ProcessIsActive(active.view->id)) {
|
||||
|
||||
@@ -31,46 +31,51 @@ void DebugWindowLayout(Rect2I *rect, Int wx, Int wy) {
|
||||
}
|
||||
|
||||
void DebugWindowUpdate() {
|
||||
Buffer *buffer = GetBuffer(DebugBufferID);
|
||||
ProfileFunction();
|
||||
BSet set = GetBSet(DebugWindowID);
|
||||
if (!set.window->visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
Window *window = GetActiveWind();
|
||||
View *view = GetView(window->active_view);
|
||||
if (view->active_buffer.id == buffer->id.id) return;
|
||||
BSet active = GetBSet(ActiveWindowID);
|
||||
if (active.buffer->id.id == set.buffer->id.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
BSet main = GetBSet(LastActiveLayoutWindowID);
|
||||
|
||||
Scratch scratch;
|
||||
String s = Format(scratch, "wid: %d\nvid: %d\nbid: %d\nframe: %lld\n", (int)main.window->id.id, (int)main.view->id.id, (int)main.buffer->id.id, (long long)FrameID);
|
||||
String16 string = ToString16(scratch, s);
|
||||
RawReplaceText(buffer, GetRange(buffer), string);
|
||||
RawReplaceText(set.buffer, GetRange(set.buffer), string);
|
||||
|
||||
float xmouse, ymouse;
|
||||
SDL_GetMouseState(&xmouse, &ymouse);
|
||||
RawAppendf(buffer, "mouse: [%f, %f]\n", roundf(DPIScale * xmouse), roundf(DPIScale * ymouse));
|
||||
RawAppendf(set.buffer, "mouse: [%f, %f]\n", roundf(DPIScale * xmouse), roundf(DPIScale * ymouse));
|
||||
|
||||
RawAppendf(buffer, "BufferID id = %d\n", main.buffer->id.id);
|
||||
RawAppendf(buffer, "String name = %S\n", main.buffer->name);
|
||||
RawAppendf(buffer, "Int change_id = %lld\n", (long long)main.buffer->change_id);
|
||||
RawAppendf(buffer, "Int user_change_id = %lld\n", (long long)main.buffer->user_change_id);
|
||||
RawAppendf(buffer, "Int file_mod_time = %lld\n", (long long)main.buffer->file_mod_time);
|
||||
RawAppendf(buffer, "\n");
|
||||
RawAppendf(set.buffer, "BufferID id = %d\n", main.buffer->id.id);
|
||||
RawAppendf(set.buffer, "String name = %S\n", main.buffer->name);
|
||||
RawAppendf(set.buffer, "Int change_id = %lld\n", (long long)main.buffer->change_id);
|
||||
RawAppendf(set.buffer, "Int user_change_id = %lld\n", (long long)main.buffer->user_change_id);
|
||||
RawAppendf(set.buffer, "Int file_mod_time = %lld\n", (long long)main.buffer->file_mod_time);
|
||||
RawAppendf(set.buffer, "\n");
|
||||
|
||||
RawAppendf(buffer, "U16 *data = %zu\n", main.buffer->data);
|
||||
RawAppendf(buffer, "Int len = %lld\n", (long long)main.buffer->len);
|
||||
RawAppendf(buffer, "Int cap = %lld\n", (long long)main.buffer->cap);
|
||||
RawAppendf(buffer, "Array<Int> line_starts = {len = %lld, cap = %lld, data = %zu}\n", (long long)main.buffer->line_starts.len, (long long)main.buffer->line_starts.cap, main.buffer->line_starts.data);
|
||||
RawAppendf(buffer, "\n");
|
||||
RawAppendf(set.buffer, "U16 *data = %zu\n", main.buffer->data);
|
||||
RawAppendf(set.buffer, "Int len = %lld\n", (long long)main.buffer->len);
|
||||
RawAppendf(set.buffer, "Int cap = %lld\n", (long long)main.buffer->cap);
|
||||
RawAppendf(set.buffer, "Array<Int> line_starts = {len = %lld, cap = %lld, data = %zu}\n", (long long)main.buffer->line_starts.len, (long long)main.buffer->line_starts.cap, main.buffer->line_starts.data);
|
||||
RawAppendf(set.buffer, "\n");
|
||||
|
||||
RawAppendf(buffer, "Array<HistoryEntry> undo_stack = {len = %lld, cap = %lld, data = %zu}\n", (long long)main.buffer->undo_stack.len, (long long)main.buffer->undo_stack.cap, main.buffer->undo_stack.data);
|
||||
RawAppendf(buffer, "Array<HistoryEntry> redo_stack = {len = %lld, cap = %lld, data = %zu}\n", (long long)main.buffer->redo_stack.len, (long long)main.buffer->redo_stack.cap, main.buffer->redo_stack.data);
|
||||
RawAppendf(buffer, "int edit_phase = %d", main.buffer->edit_phase);
|
||||
RawAppendf(buffer, "\n");
|
||||
RawAppendf(set.buffer, "Array<HistoryEntry> undo_stack = {len = %lld, cap = %lld, data = %zu}\n", (long long)main.buffer->undo_stack.len, (long long)main.buffer->undo_stack.cap, main.buffer->undo_stack.data);
|
||||
RawAppendf(set.buffer, "Array<HistoryEntry> redo_stack = {len = %lld, cap = %lld, data = %zu}\n", (long long)main.buffer->redo_stack.len, (long long)main.buffer->redo_stack.cap, main.buffer->redo_stack.data);
|
||||
RawAppendf(set.buffer, "int edit_phase = %d", main.buffer->edit_phase);
|
||||
RawAppendf(set.buffer, "\n");
|
||||
|
||||
RawAppendf(buffer, "int no_history = %d\n", main.buffer->no_history);
|
||||
RawAppendf(buffer, "int no_line_starts = %d\n", main.buffer->no_line_starts);
|
||||
RawAppendf(buffer, "int dirty = %d\n", main.buffer->dirty);
|
||||
RawAppendf(buffer, "int changed_on_disk = %d\n", main.buffer->changed_on_disk);
|
||||
RawAppendf(buffer, "int garbage = %d\n", main.buffer->garbage);
|
||||
RawAppendf(set.buffer, "int no_history = %d\n", main.buffer->no_history);
|
||||
RawAppendf(set.buffer, "int no_line_starts = %d\n", main.buffer->no_line_starts);
|
||||
RawAppendf(set.buffer, "int dirty = %d\n", main.buffer->dirty);
|
||||
RawAppendf(set.buffer, "int changed_on_disk = %d\n", main.buffer->changed_on_disk);
|
||||
RawAppendf(set.buffer, "int garbage = %d\n", main.buffer->garbage);
|
||||
}
|
||||
|
||||
void Command_ToggleDebug() {
|
||||
|
||||
@@ -25,6 +25,7 @@ void StatusWindowLayout(Rect2I *rect, Int wx, Int wy) {
|
||||
}
|
||||
|
||||
void StatusWindowUpdate() {
|
||||
ProfileFunction();
|
||||
Window *status_bar_window = GetWindow(StatusBarWindowID, NULL);
|
||||
if (status_bar_window == NULL) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user