Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,13 @@ if(PLATFORM STREQUAL "desktop")
if(ENABLE_MODERN_GL)
file(GLOB GL_SOURCES ${GL_SOURCES} src/gl/*.c)
endif()
target_sources(butterscotch PRIVATE ${GL_SOURCES})
file(GLOB DEBUG_FONT_SOURCES src/debug_font/*.c)
target_sources(butterscotch PRIVATE ${GL_SOURCES} ${DEBUG_FONT_SOURCES})
target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/debug_font)
target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl)
target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl_common)
target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/image)

if (MSVC)
target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/compat/getopt)
add_compile_definitions(NO_STRTOK_R)
Expand Down Expand Up @@ -326,7 +329,9 @@ elseif(PLATFORM STREQUAL "web")
message(FATAL_ERROR "Web requires modern gl!")
endif()
file(GLOB GL_SOURCES src/gl/*.c src/gl_common/*.c src/image/*.c)
target_sources(butterscotch PRIVATE ${GL_SOURCES})
file(GLOB DEBUG_FONT_SOURCES src/debug_font/*.c)
target_sources(butterscotch PRIVATE ${GL_SOURCES} ${DEBUG_FONT_SOURCES})
target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/debug_font)
target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl)
target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl_common)
target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/image)
Expand Down Expand Up @@ -384,7 +389,9 @@ elseif(PLATFORM STREQUAL "android")
message(FATAL_ERROR "Android requires modern gl!")
endif()
file(GLOB GL_SOURCES src/gl/*.c src/gl_common/*.c src/image/*.c)
target_sources(butterscotch PRIVATE ${GL_SOURCES})
file(GLOB DEBUG_FONT_SOURCES src/debug_font/*.c)
target_sources(butterscotch PRIVATE ${GL_SOURCES} ${DEBUG_FONT_SOURCES})
target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/debug_font)
target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl)
target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl_common)
target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/image)
Expand Down
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ INCLUDES += $(INCLUDE). \
$(INCLUDE)vendor/md5 \
$(INCLUDE)vendor/sha1 \
$(INCLUDE)vendor/base64 \
$(INCLUDE)vendor/bzip2
$(INCLUDE)vendor/bzip2 \
$(INCLUDE)src/debug_font

HEADERS := $(wildcard src/*.h) $(shell find vendor -name '*.h')
SRCS := $(wildcard src/*.c) $(wildcard src/image/*.c) $(wildcard vendor/bzip2/*.c) vendor/md5/md5.c vendor/sha1/sha1.c vendor/base64/base64.c
SRCS := $(wildcard src/*.c) $(wildcard src/debug_font/*.c) $(wildcard src/image/*.c) $(wildcard vendor/bzip2/*.c) vendor/md5/md5.c vendor/sha1/sha1.c vendor/base64/base64.c

DESKTOP_BACKEND := glfw3
AUDIO_BACKEND := miniaudio
Expand Down
59 changes: 58 additions & 1 deletion src/desktop/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
#ifdef _WIN32
#include <windows.h>
#include <mmsystem.h>
#include <psapi.h>
#endif
#ifdef __APPLE__
#include <mach/mach.h>
#endif
#ifdef __GLIBC__
#include <malloc.h>
Expand All @@ -27,6 +31,7 @@
#include "runner.h"
#include "input_recording.h"
#include "debug_overlay.h"
#include "overlay.h"
#if defined(ENABLE_LEGACY_GL) || defined(ENABLE_MODERN_GL) || ((defined(USE_GLFW3) || defined(USE_GLFW2)) && defined(ENABLE_SW_RENDERER))
#include <glad/glad.h>
#endif
Expand Down Expand Up @@ -75,7 +80,7 @@ const GLuint *hostFramebuffer;
#endif

static size_t get_used_memory(void) {
#ifdef __linux__
#if defined(__linux__)
int fd = open("/proc/self/smaps_rollup", O_RDONLY);
if (fd < 0)
return 0;
Expand Down Expand Up @@ -103,6 +108,32 @@ static size_t get_used_memory(void) {
if (*p)
p++;
}
#elif defined(__APPLE__)
task_basic_info_data_t info;
mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
if (task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &count) == KERN_SUCCESS) {
return info.resident_size;
}
#elif defined(_WIN32)
typedef BOOL (WINAPI *GetProcessMemoryInfo_t)(HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD);
static GetProcessMemoryInfo_t func = NULL;
static bool initialized = false;

if (!initialized) {
initialized = true;
HMODULE dll = LoadLibrary("psapi.dll");
if (dll) {
FARPROC p = GetProcAddress(dll, "GetProcessMemoryInfo");
memcpy(&func, &p, sizeof(func));
}
}

if (func) {
PROCESS_MEMORY_COUNTERS pmc;
pmc.cb = sizeof(pmc);
if (func(GetCurrentProcess(), &pmc, sizeof(pmc)))
return pmc.WorkingSetSize;
}
#endif
return 0;
}
Expand Down Expand Up @@ -1413,6 +1444,7 @@ int main(int argc, char* argv[]) {
#endif
}
}
Overlay_init(args.debug ? OVERLAY_STATS_ENABLED : OVERLAY_STATS_DISABLED);
runner->debugMode = args.debug;
runner->osType = args.osType;
runner->setWindowSize = platformSetWindowSize;
Expand Down Expand Up @@ -1469,6 +1501,7 @@ int main(int argc, char* argv[]) {
// Main loop
bool debugPaused = false;
bool debugShowCollisionMasks = false;
OverlayState overlayState = OVERLAY_STATS_ENABLED;
bool freeCamActive = false;
bool actuallyShuttingDown = false;
uint64_t lastFrameTime = nowNanos();
Expand Down Expand Up @@ -1576,6 +1609,13 @@ int main(int argc, char* argv[]) {
free(json);
}

// Toggle the debug info overlay
if (RunnerKeyboard_checkPressed(runner->keyboard, VK_F1)) {
Overlay_toggle(runner);
overlayState = Overlay_getState();
fprintf(stderr, "Debug: Stats overlay %s!\n", overlayState == OVERLAY_STATS_DISABLED ? "disabled" : overlayState == OVERLAY_STATS_ENABLED ? "enabled" : "enabled with profiler");
}

// Toggle the collision mask debug overlay
if (RunnerKeyboard_checkPressed(runner->keyboard, VK_F2)) {
debugShowCollisionMasks = !debugShowCollisionMasks;
Expand Down Expand Up @@ -1756,6 +1796,22 @@ int main(int argc, char* argv[]) {
renderer->vtable->endFrameEnd(renderer);
Runner_drawGUI(runner, fbWidth, fbHeight, gameW, gameH);

// Draw the debug overlay if enabled
if (overlayState != OVERLAY_STATS_DISABLED) {
size_t memBytes = get_used_memory();
static uint32_t frameCount = 0;
static uint32_t fps = 0;
static uint64_t then = 0;
++frameCount;

if (lastFrameStartTime - then > 1000000000) {
then = lastFrameStartTime;
fps = frameCount;
frameCount = 0;
}
Overlay_draw(runner, fps, fbWidth, fbHeight, memBytes);
}

#if defined(ENABLE_LEGACY_GL) || defined(ENABLE_MODERN_GL)
// Capture screenshot if this frame matches a requested frame
bool shouldScreenshot = hmget(args.screenshotFrames, runner->frameCount);
Expand Down Expand Up @@ -1836,6 +1892,7 @@ int main(int argc, char* argv[]) {
platformInitialized = false;
}

Overlay_deinit();
Runner_free(runner);
OverlayFileSystem_destroy(overlayFs);
#ifdef ENABLE_VM_OPCODE_PROFILER
Expand Down
125 changes: 125 additions & 0 deletions src/desktop/overlay.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#include "overlay.h"

#include "profiler.h"
#include "utils.h"

#include "stb_ds.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define DEBUGFONT_LINE_HEIGHT 43
#define OVERLAY_FONT_SCALE 0.5f
#define PROFILER_SCALE 0.4f
#define PROFILER_WINDOW_FRAMES 60

typedef struct {
bool initialized;
OverlayState state;
int profilerFramesInWindow;
#ifdef ENABLE_VM_GML_PROFILER
char profilerOverlayText[4096];
#endif
} Overlay;

static Overlay gOverlay = { 0 };

void Overlay_init(OverlayState initialState) {
if (gOverlay.initialized) return;
gOverlay.state = initialState;
gOverlay.profilerFramesInWindow = 0;
gOverlay.initialized = true;
}

void Overlay_deinit(void) {
memset(&gOverlay, 0, sizeof(gOverlay));
}

OverlayState Overlay_getState(void) {
if (!gOverlay.initialized) return OVERLAY_STATS_DISABLED;
return gOverlay.state;
}

void Overlay_toggle(Runner* runner) {
if (!gOverlay.initialized) return;
gOverlay.state = (OverlayState)((gOverlay.state + 1) % OVERLAY_STATS_MAX);

#ifdef ENABLE_VM_GML_PROFILER
Profiler_setEnabled(&runner->vmContext->profiler, gOverlay.state == OVERLAY_STATS_ENABLED_WITH_PROFILER);
gOverlay.profilerFramesInWindow = 0;
gOverlay.profilerOverlayText[0] = '\0';
#endif
}

static void drawOverlayText(Renderer* renderer, float x, float y, float scale, uint32_t color, float alpha, const char* text) {
if (text == NULL) return;
uint32_t prevColor = renderer->drawColor;
float prevAlpha = renderer->drawAlpha;
int32_t prevFont = renderer->drawFont;
renderer->drawColor = color;
renderer->drawAlpha = alpha;
renderer->drawFont = -1;
renderer->vtable->drawText(renderer, text, x, y, scale, scale, 0.0f, -1.0f, true);
renderer->drawColor = prevColor;
renderer->drawAlpha = prevAlpha;
renderer->drawFont = prevFont;
}

void Overlay_draw(Runner* runner, uint32_t fps, int32_t fbWidth, int32_t fbHeight, size_t memBytes) {
if (!gOverlay.initialized) return;
if (gOverlay.state == OVERLAY_STATS_DISABLED) return;

Renderer* renderer = runner->renderer;

const char* roomName = runner->currentRoom != NULL && runner->currentRoom->name != NULL ? runner->currentRoom->name : "?";

char debugText[512];
char memLine[64];
if (memBytes > 0) {
snprintf(memLine, sizeof(memLine), "\nMemory: %.2f MB", (double) memBytes / (1024.0 * 1024.0));
} else {
memLine[0] = '\0';
}
snprintf(debugText, sizeof(debugText),
"Room: %s\nFPS: %u\nInstances: %d\nStructs: %d%s",
roomName, fps,
(int) arrlen(runner->instances), (int) arrlen(runner->structInstances),
memLine
);

bool blendWas = renderer->vtable->gpuGetBlendEnable(renderer);
renderer->vtable->gpuSetBlendEnable(renderer, true);
renderer->vtable->gpuSetBlendMode(renderer, bm_normal);

renderer->vtable->beginGUI(renderer, fbWidth, fbHeight, 0, 0, fbWidth, fbHeight, RENDER_TARGET_HOST_FRAMEBUFFER);

drawOverlayText(renderer, 10.0f, 10.0f, OVERLAY_FONT_SCALE, 0xFFFFFF, 1.0f, debugText);

if (gOverlay.state == OVERLAY_STATS_ENABLED_WITH_PROFILER) {
float profilerY = 10.0f + ((float) DEBUGFONT_LINE_HEIGHT * OVERLAY_FONT_SCALE * 5.0f);

#ifdef ENABLE_VM_GML_PROFILER
gOverlay.profilerFramesInWindow++;
if (gOverlay.profilerFramesInWindow >= PROFILER_WINDOW_FRAMES) {
char* profilerReport = Profiler_createReport(runner->vmContext->profiler, 25, gOverlay.profilerFramesInWindow);
if (profilerReport != NULL) {
snprintf(gOverlay.profilerOverlayText, sizeof(gOverlay.profilerOverlayText), "%s", profilerReport);
free(profilerReport);
}
Profiler_reset(runner->vmContext->profiler);
gOverlay.profilerFramesInWindow = 0;
}
const char* profilerDisplay = gOverlay.profilerOverlayText[0] != '\0' ? gOverlay.profilerOverlayText : "GML Profiler (collecting...)";
drawOverlayText(renderer, 10.0f, profilerY, PROFILER_SCALE, 0xFFFFFF, 1.0f, profilerDisplay);
#else
drawOverlayText(renderer, 10.0f, profilerY, PROFILER_SCALE, 0xFFFFFF, 1.0f, "Butterscotch GML Profiler is disabled on this build :(");
#endif
}

renderer->vtable->flush(renderer);
renderer->vtable->endGUI(renderer);

renderer->vtable->gpuSetBlendMode(renderer, bm_normal);
renderer->vtable->gpuSetBlendEnable(renderer, blendWas);
}
19 changes: 19 additions & 0 deletions src/desktop/overlay.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef _BS_OVERLAY_H_
#define _BS_OVERLAY_H_

#include "runner.h"

typedef enum {
OVERLAY_STATS_ENABLED = 0,
OVERLAY_STATS_ENABLED_WITH_PROFILER = 1,
OVERLAY_STATS_DISABLED = 2,
OVERLAY_STATS_MAX
} OverlayState;

void Overlay_init(OverlayState initialState);
void Overlay_deinit(void);
OverlayState Overlay_getState(void);
void Overlay_toggle(Runner* runner);
void Overlay_draw(Runner* runner, uint32_t fps, int32_t fbWidth, int32_t fbHeight, size_t memBytes);

#endif
Loading
Loading