diff --git a/CMakeLists.txt b/CMakeLists.txt index 924d470fa..5231403c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) @@ -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) @@ -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) diff --git a/Makefile b/Makefile index c749f82c7..d90b71212 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/src/desktop/main.c b/src/desktop/main.c index 0b0df3af4..e1a948744 100644 --- a/src/desktop/main.c +++ b/src/desktop/main.c @@ -13,6 +13,10 @@ #ifdef _WIN32 #include #include +#include +#endif +#ifdef __APPLE__ +#include #endif #ifdef __GLIBC__ #include @@ -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 #endif @@ -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; @@ -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; } @@ -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; @@ -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(); @@ -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; @@ -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); @@ -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 diff --git a/src/desktop/overlay.c b/src/desktop/overlay.c new file mode 100644 index 000000000..7b95eb439 --- /dev/null +++ b/src/desktop/overlay.c @@ -0,0 +1,125 @@ +#include "overlay.h" + +#include "profiler.h" +#include "utils.h" + +#include "stb_ds.h" +#include "stdio_compat.h" +#include +#include +#include + +#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; + if (renderer->vtable->drawDebugText) renderer->vtable->drawDebugText(renderer, text, x, y, scale, scale, 0.0f, -1.0f); + 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); +} diff --git a/src/desktop/overlay.h b/src/desktop/overlay.h new file mode 100644 index 000000000..99be06a00 --- /dev/null +++ b/src/desktop/overlay.h @@ -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 diff --git a/src/gl/gl_renderer.c b/src/gl/gl_renderer.c index 48b0b4480..22d83968d 100644 --- a/src/gl/gl_renderer.c +++ b/src/gl/gl_renderer.c @@ -1,6 +1,7 @@ #include "gl_renderer.h" #include "matrix_math.h" #include "text_utils.h" +#include "debug_font/debug_font.h" #if defined(__EMSCRIPTEN__) || defined(__ANDROID__) #include @@ -603,6 +604,7 @@ static void glDestroy(Renderer* renderer) { GLRenderer* gl = (GLRenderer*) renderer; glDeleteTextures(1, &gl->whiteTexture); + if (gl->debugFontTexture != 0) glDeleteTextures(1, &gl->debugFontTexture); repeat(gl->gmlShaderCount, i) { freeShader(&gl->gmlShaders[i]); @@ -1672,6 +1674,15 @@ static bool glResolveGlyph(GLRenderer* gl, DataWin* dw, GlFontState* state, Font return true; } +static void debugFontDrawModern(void* user, GLuint texture, + float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, + float u0, float v0, float u1, float v1, + uint8_t r, uint8_t g, uint8_t b, float alpha) +{ + GLRenderer* gl = (GLRenderer*) user; + emitTexturedQuad(gl, texture, x0, y0, x1, y1, x2, y2, x3, y3, u0, v0, u1, v1, r, g, b, r, g, b, r, g, b, r, g, b, alpha); +} + static void drawText( Renderer* renderer, const char* text, @@ -1861,6 +1872,16 @@ static void glDrawText(Renderer* renderer, const char* text, float x, float y, f ); } +static void glDrawDebugText(Renderer* renderer, const char* text, float x, float y, float xscale, float yscale, float angleDeg, float lineSeparation) { + (void)lineSeparation; + GLRenderer* gl = (GLRenderer*) renderer; + GLCommon_ensureDebugFontTexture(&gl->debugFontTexture); + uint8_t cr = (uint8_t) BGR_R(renderer->drawColor); + uint8_t cg = (uint8_t) BGR_G(renderer->drawColor); + uint8_t cb = (uint8_t) BGR_B(renderer->drawColor); + GLCommon_drawDebugFontText(gl->debugFontTexture, text, x, y, xscale, yscale, angleDeg, cr, cg, cb, renderer->drawAlpha, gl, debugFontDrawModern); +} + static void glDrawTextColor(Renderer* renderer, const char* text, float x, float y, float xscale, float yscale, float angleDeg, int32_t _c1, int32_t _c2, int32_t _c3, int32_t _c4, float alpha, float lineSeparation) { drawText( renderer, @@ -2693,6 +2714,7 @@ Renderer* GLRenderer_create(void) { glVtable.drawTriangle = glDrawTriangle; glVtable.drawText = glDrawText; glVtable.drawTextColor = glDrawTextColor; + glVtable.drawDebugText = glDrawDebugText; glVtable.flush = glRendererFlush; glVtable.clearScreen = glClearScreen; glVtable.createSpriteFromSurface = glCreateSpriteFromSurface; diff --git a/src/gl/gl_renderer.h b/src/gl/gl_renderer.h index 26a5a7178..388c6a109 100644 --- a/src/gl/gl_renderer.h +++ b/src/gl/gl_renderer.h @@ -64,6 +64,7 @@ typedef struct { uint32_t textureCount; GLuint whiteTexture; // 1x1 white pixel for drawing primitives (rectangles, lines, etc.) + GLuint debugFontTexture; // texture for debug font atlas int32_t windowW; // stored from beginFrame for endFrame blit int32_t windowH; diff --git a/src/gl_common/gl_common.c b/src/gl_common/gl_common.c index a13b4e52e..3ea2a3f1c 100644 --- a/src/gl_common/gl_common.c +++ b/src/gl_common/gl_common.c @@ -3,10 +3,13 @@ #include "stdio_compat.h" #include #include +#include #include "runner.h" #include "utils.h" #include "renderer.h" // for bm_* constants +#include "matrix_math.h" +#include "debug_font.h" // ===[ Letterbox blit ]=== @@ -216,3 +219,70 @@ GLenum GLCommon_blendModeToDFactor(int mode) { case bm_max: return GL_ONE_MINUS_SRC_COLOR; } } + +// ===[ Debug font helpers ]=== + +void GLCommon_ensureDebugFontTexture(GLuint* outTexture) { + if (*outTexture != 0) return; + glGenTextures(1, outTexture); + glBindTexture(GL_TEXTURE_2D, *outTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + uint8_t* rgba = (uint8_t*) safeMalloc((size_t) DEBUGFONT_ATLAS_W * DEBUGFONT_ATLAS_H * 4); + for (int i = 0; i < DEBUGFONT_ATLAS_W * DEBUGFONT_ATLAS_H; i++) { + uint8_t v = debugFontPixels[i]; + rgba[i * 4 + 0] = 0xFF; + rgba[i * 4 + 1] = 0xFF; + rgba[i * 4 + 2] = 0xFF; + rgba[i * 4 + 3] = v; + } + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, DEBUGFONT_ATLAS_W, DEBUGFONT_ATLAS_H, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba); + free(rgba); +} + +void GLCommon_drawDebugFontText(GLuint texture, const char* text, float x, float y, float xscale, float yscale, float angleDeg, uint8_t cr, uint8_t cg, uint8_t cb, float alpha, void* user, DebugFontDrawFn drawFn) { + if (text == NULL) return; + int32_t len = (int32_t) strlen(text); + float angleRad = -angleDeg * ((float) M_PI / 180.0f); + Matrix4f transform; + Matrix4f_setTransform2D(&transform, x, y, xscale, yscale, angleRad); + float cursorY = 0; + int32_t lineStart = 0; + + for (int32_t i = 0; len >= i; i++) { + if (i == len || text[i] == '\n') { + int32_t lineLen = i - lineStart; + float pen = 0; + for (int32_t j = 0; j < lineLen; j++) { + const DebugFontGlyphEntry* glyph; + uint8_t c = (uint8_t) text[lineStart + j]; + if (DEBUGFONT_FIRST_CP > c || c > DEBUGFONT_LAST_CP) glyph = NULL; + else glyph = &debugFontGlyphs[c - DEBUGFONT_FIRST_CP]; + if (glyph == NULL) continue; + if (glyph->w > 0 && glyph->h > 0) { + float localX0 = pen + (float) glyph->xoffset; + float localY0 = cursorY + (float) glyph->yoffset; + float localX1 = localX0 + (float) glyph->w; + float localY1 = localY0 + (float) glyph->h; + float u0 = (float) glyph->x / (float) DEBUGFONT_ATLAS_W; + float v0 = (float) glyph->y / (float) DEBUGFONT_ATLAS_H; + float u1 = (float) (glyph->x + glyph->w) / (float) DEBUGFONT_ATLAS_W; + float v1 = (float) (glyph->y + glyph->h) / (float) DEBUGFONT_ATLAS_H; + float sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3; + Matrix4f_transformPoint(&transform, localX0, localY0, &sx0, &sy0); + Matrix4f_transformPoint(&transform, localX1, localY0, &sx1, &sy1); + Matrix4f_transformPoint(&transform, localX1, localY1, &sx2, &sy2); + Matrix4f_transformPoint(&transform, localX0, localY1, &sx3, &sy3); + drawFn(user, texture, sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3, u0, v0, u1, v1, cr, cg, cb, alpha); + } + pen += (float) glyph->xadvance; + } + cursorY += (float) DEBUGFONT_LINE_HEIGHT; + lineStart = i + 1; + } + } +} + + diff --git a/src/gl_common/gl_common.h b/src/gl_common/gl_common.h index 5a66b86f9..a86adf0bf 100644 --- a/src/gl_common/gl_common.h +++ b/src/gl_common/gl_common.h @@ -58,6 +58,8 @@ GLenum GLCommon_blendModeToSFactor(int mode); // Maps a bm_* mode constant to its conventional destination blend factor. GLenum GLCommon_blendModeToDFactor(int mode); +#include "debug_font.h" + #ifndef PLATFORM_PS3 // ===[ GL version queries ]=== @@ -73,4 +75,15 @@ GLVer GLCommon_getGLVersion(void); #endif +// Creates a GL texture from the debug font atlas if *outTexture is 0. Idempotent. +void GLCommon_ensureDebugFontTexture(GLuint* outTexture); + +// Callback for GLCommon_drawDebugFontText: draws a single quad. +// The four (x,y) pairs are screen-space corners (TL, TR, BR, BL) after transform. +typedef void (*DebugFontDrawFn)(void* user, GLuint texture, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float u0, float v0, float u1, float v1, uint8_t r, uint8_t g, uint8_t b, float alpha); + +// Iterates the debug-font text, computes glyph positions, transforms them, +// and calls drawFn for each visible glyph. +void GLCommon_drawDebugFontText(GLuint texture, const char* text, float x, float y, float xscale, float yscale, float angleDeg, uint8_t cr, uint8_t cg, uint8_t cb, float alpha, void* user, DebugFontDrawFn drawFn); + #endif /* _BS_GL_COMMON_H_ */ diff --git a/src/gl_legacy/gl_legacy_renderer.c b/src/gl_legacy/gl_legacy_renderer.c index 26ba2c531..59a3e008f 100644 --- a/src/gl_legacy/gl_legacy_renderer.c +++ b/src/gl_legacy/gl_legacy_renderer.c @@ -2,6 +2,7 @@ #include "matrix_math.h" #include "text_utils.h" #include "gl_wrappers.h" +#include "debug_font.h" #ifdef PLATFORM_PS3 @@ -217,6 +218,7 @@ static void glDestroy(Renderer* renderer) { GLLegacyRenderer* gl = (GLLegacyRenderer*) renderer; glDeleteTextures(1, &gl->whiteTexture); + if (gl->debugFontTexture != 0) glDeleteTextures(1, &gl->debugFontTexture); glDeleteTextures((GLsizei) gl->textureCount, gl->glTextures); @@ -1037,6 +1039,29 @@ static bool glResolveGlyph(GLLegacyRenderer* gl, DataWin* dw, GlFontState* state return true; } +static void debugFontDrawLegacy(void* user, GLuint texture, + float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, + float u0, float v0, float u1, float v1, + uint8_t r, uint8_t g, uint8_t b, float alpha) +{ + (void)user; + glBindTexture(GL_TEXTURE_2D, texture); + glBegin(GL_QUADS); + glColor4ub(r, g, b, (uint8_t)(alpha * 255.0f)); + glTexCoord2f(u0, v0); + glVertex2f(x0, y0); + glColor4ub(r, g, b, (uint8_t)(alpha * 255.0f)); + glTexCoord2f(u1, v0); + glVertex2f(x1, y1); + glColor4ub(r, g, b, (uint8_t)(alpha * 255.0f)); + glTexCoord2f(u1, v1); + glVertex2f(x2, y2); + glColor4ub(r, g, b, (uint8_t)(alpha * 255.0f)); + glTexCoord2f(u0, v1); + glVertex2f(x3, y3); + glEnd(); +} + static void glDrawText(Renderer* renderer, const char* text, float x, float y, float xscale, float yscale, float angleDeg, float lineSeparation) { GLLegacyRenderer* gl = (GLLegacyRenderer*) renderer; DataWin* dw = renderer->dataWin; @@ -1178,6 +1203,18 @@ static void glDrawText(Renderer* renderer, const char* text, float x, float y, f } } +static void glDrawDebugText(Renderer* renderer, const char* text, float x, float y, float xscale, float yscale, float angleDeg, float lineSeparation) { + (void)lineSeparation; + GLLegacyRenderer* gl = (GLLegacyRenderer*) renderer; + GLCommon_ensureDebugFontTexture(&gl->debugFontTexture); + uint32_t color = renderer->drawColor; + float dfAlpha = renderer->drawAlpha; + uint8_t cr = (uint8_t) BGR_R(color); + uint8_t cg = (uint8_t) BGR_G(color); + uint8_t cb = (uint8_t) BGR_B(color); + GLCommon_drawDebugFontText(gl->debugFontTexture, text, x, y, xscale, yscale, angleDeg, cr, cg, cb, dfAlpha, NULL, debugFontDrawLegacy); +} + static void glDrawTextColor(Renderer* renderer, const char* text, float x, float y, float xscale, float yscale, float angleDeg, int32_t _c1, int32_t _c2, int32_t _c3, int32_t _c4, float alpha, float lineSeparation) { GLLegacyRenderer* gl = (GLLegacyRenderer*) renderer; DataWin* dw = renderer->dataWin; @@ -1929,6 +1966,7 @@ Renderer* GLLegacyRenderer_create(void) { glVtable.drawTriangle = glDrawTriangle; glVtable.drawText = glDrawText; glVtable.drawTextColor = glDrawTextColor; + glVtable.drawDebugText = glDrawDebugText; glVtable.flush = glRendererFlush; glVtable.clearScreen = glClearScreen; glVtable.createSpriteFromSurface = glCreateSpriteFromSurface; diff --git a/src/gl_legacy/gl_legacy_renderer.h b/src/gl_legacy/gl_legacy_renderer.h index a423bf4bf..76cff65d5 100644 --- a/src/gl_legacy/gl_legacy_renderer.h +++ b/src/gl_legacy/gl_legacy_renderer.h @@ -23,6 +23,7 @@ typedef struct { uint32_t textureCount; GLuint whiteTexture; // 1x1 white pixel for drawing primitives (rectangles, lines, etc.) + GLuint debugFontTexture; // texture for debug font atlas int32_t windowW; // stored from beginFrame for endFrame blit int32_t windowH; diff --git a/src/renderer.h b/src/renderer.h index 83759888c..626c44de4 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -100,6 +100,7 @@ typedef struct { void (*drawLineColor)(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uint32_t color1, uint32_t color2, float alpha); void (*drawText)(Renderer* renderer, const char* text, float x, float y, float xscale, float yscale, float angleDeg, float lineSeparation); void (*drawTextColor)(Renderer* renderer, const char* text, float x, float y, float xscale, float yscale, float angleDeg, int32_t c1, int32_t c2, int32_t c3, int32_t c4, float alpha, float lineSeparation); + void (*drawDebugText)(Renderer* renderer, const char* text, float x, float y, float xscale, float yscale, float angleDeg, float lineSeparation); void (*flush)(Renderer* renderer); void (*clearScreen)(Renderer* renderer, uint32_t color, float alpha); int32_t (*createSpriteFromSurface)(Renderer* renderer, int32_t surfaceID, int32_t x, int32_t y, int32_t w, int32_t h, bool removeback, bool smooth, int32_t xorig, int32_t yorig);