Buildsystem: Removed include/basalt_window.h. See prior commit #cac50b01
Library: Added logging and memory modules. The logging module provides a flexible interface for adding and removing logging streams (think stdout, stderr, a file) and supports ANSI colouring and including the date and time of each log call. Its header also provides several definitions for assertions, each corresponding to a specific log level and some other side effects such as debug_break(), returning, exiting or doing nothing. The memory module wraps (_)aligned_alloc and free functions and includes memory tagging by class and location and the ability to fetch a formatted string containing this information. This can allow you to narrow down where memory leaks and double frees occur to particular sections within a module. Memory can be aligned to any, 32, 64 or 4096 byte boundaries using this module.
This commit is contained in:
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
#define GLFW_INCLUDE_VULKAN
|
||||
#include "GLFW/glfw3.h"
|
||||
|
||||
namespace basalt
|
||||
{
|
||||
class Window
|
||||
{
|
||||
public:
|
||||
Window(const Window& src) = delete;
|
||||
Window& operator =(const Window& src) = delete;
|
||||
Window(Window&& other) noexcept;
|
||||
Window& operator =(Window&& other) noexcept;
|
||||
|
||||
Window(uint16_t width, uint16_t height, const char* title);
|
||||
~Window(void) noexcept;
|
||||
|
||||
void swap(Window& other);
|
||||
operator GLFWwindow* (void) const noexcept;
|
||||
|
||||
const bool should_close(void) const noexcept;
|
||||
|
||||
protected:
|
||||
GLFWwindow* window = nullptr;
|
||||
const char* window_title = "N/A";
|
||||
uint16_t width = 0;
|
||||
uint16_t height = 0;
|
||||
};
|
||||
}
|
||||
94
include/core/basalt_logger.h
Normal file
94
include/core/basalt_logger.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
#include "basalt_defines.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef BASALT_LOGGER_MAX_STREAMS
|
||||
#define BASALT_LOGGER_MAX_STREAMS 7
|
||||
#endif
|
||||
|
||||
typedef enum LOG_LEVEL {
|
||||
LOG_FATAL = 1,
|
||||
LOG_ERROR = 2,
|
||||
LOG_WARN = 4,
|
||||
LOG_INFO = 8,
|
||||
LOG_OK = 16,
|
||||
LOG_WEIRD = 32,
|
||||
LOG_DEBUG = 64,
|
||||
LOG_TRACE = 128,
|
||||
|
||||
LOG_MASK_ERRORS = LOG_FATAL | LOG_ERROR,
|
||||
LOG_MASK_DEBUGGING = LOG_WEIRD | LOG_DEBUG | LOG_TRACE,
|
||||
LOG_MASK_INFO = LOG_WARN | LOG_INFO | LOG_OK,
|
||||
LOG_MASK_ALL = LOG_MASK_ERRORS | LOG_MASK_DEBUGGING | LOG_MASK_INFO,
|
||||
LOG_MASK_DEFAULT = LOG_MASK_ERRORS | LOG_MASK_INFO,
|
||||
} LOG_LEVEL;
|
||||
|
||||
typedef enum LOG_STREAM_FLAGS {
|
||||
// Ignore this flag - it is internal to the library
|
||||
LOG_STREAM_FLAG_INTERNAL_ALLOCATED_BIT = 1,
|
||||
LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT = 2,
|
||||
LOG_STREAM_FLAG_INCLUDE_DATETIME_BIT = 4,
|
||||
LOG_STREAM_FLAG_DISABLE_PREFIXES = 8,
|
||||
} LOG_STREAM_BIT_FLAGS;
|
||||
|
||||
typedef struct logger_stream_t {
|
||||
u64 stream : 48;
|
||||
u64 mask : 8 ;
|
||||
u64 flags : 8 ;
|
||||
} logger_stream_t;
|
||||
|
||||
void initialize_logger();
|
||||
void terminate_logger ();
|
||||
|
||||
FILE* logger_stream_get_file(logger_stream_t* state);
|
||||
u8 logger_add_stream(FILE* output, LOG_LEVEL mask, LOG_STREAM_FLAGS flags);
|
||||
void logger_remove_stream(u8 index);
|
||||
u8 logger_find_stream(FILE* target);
|
||||
|
||||
void basalt_log(const LOG_LEVEL level, const char* msg, ...);
|
||||
void basalt_write(const LOG_LEVEL level, const char* msg, ...);
|
||||
|
||||
#define BFATAL(msg, ...) basalt_log(LOG_FATAL, msg, ##__VA_ARGS__);
|
||||
#define BERROR(msg, ...) basalt_log(LOG_ERROR, msg, ##__VA_ARGS__);
|
||||
#define BWARN(msg, ...) basalt_log(LOG_WARN, msg, ##__VA_ARGS__);
|
||||
#define BINFO(msg, ...) basalt_log(LOG_INFO, msg, ##__VA_ARGS__);
|
||||
#define BOK(msg, ...) basalt_log(LOG_OK, msg, ##__VA_ARGS__);
|
||||
#define BWEIRD(msg, ...) basalt_log(LOG_WEIRD, msg, ##__VA_ARGS__);
|
||||
#define BDEBUG(msg, ...) basalt_log(LOG_DEBUG, msg, ##__VA_ARGS__);
|
||||
#define BTRACE(msg, ...) basalt_log(LOG_TRACE, msg, ##__VA_ARGS__);
|
||||
|
||||
#define BASSERT_FATAL(cond, msg, ...) \
|
||||
if (cond){} \
|
||||
else { \
|
||||
BFATAL(msg, #cond, __FILE__, __LINE__, ##__VA_ARGS__); \
|
||||
debug_break(); \
|
||||
exit(-1); \
|
||||
}
|
||||
#define BASSERT_ERROR(cond, ret, msg, ...) \
|
||||
if (cond){} \
|
||||
else { \
|
||||
BERROR(msg, #cond, __FILE__, __LINE__, ##__VA_ARGS__); \
|
||||
ret \
|
||||
}
|
||||
#define BASSERT_WARN(cond, msg, ...) \
|
||||
if (cond){} \
|
||||
else { \
|
||||
BWARN(msg, #cond, __FILE__, __LINE__, ##__VA_ARGS__); \
|
||||
}
|
||||
#ifdef BDEBUG
|
||||
#define BASSERT_WEIRD(cond, msg, ...) \
|
||||
if (cond){} \
|
||||
else { \
|
||||
BWEIRD(msg, #cond, __FILE__, __LINE__, ##__VA_ARGS__); \
|
||||
}
|
||||
#define BASSERT_DEBUG(cond, msg, ...) \
|
||||
if (cond){} \
|
||||
else { \
|
||||
BFATAL(msg, #cond, __FILE__, __LINE__, ##__VA_ARGS__); \
|
||||
debug_break(); \
|
||||
}
|
||||
#else
|
||||
#define BASSERT_WEIRD(cond, msg, ...)
|
||||
#define BASSERT_DEBUG(cond, msg, ...)
|
||||
#endif
|
||||
86
include/core/basalt_memory.h
Normal file
86
include/core/basalt_memory.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
#include "basalt_defines.h"
|
||||
|
||||
typedef u16 MEMORY_TAG;
|
||||
typedef enum MEMORY_TAGS {
|
||||
MEMORY_TAG_BITWIDTH_CLASS = 5,
|
||||
MEMORY_TAG_BITWIDTH_ZONE = 4,
|
||||
MEMORY_TAG_BITWIDTH_ALIGN = 2,
|
||||
MEMORY_TAG_SHIFT_CLASS = 0,
|
||||
MEMORY_TAG_SHIFT_ZONE = MEMORY_TAG_SHIFT_CLASS + MEMORY_TAG_BITWIDTH_CLASS,
|
||||
MEMORY_TAG_SHIFT_ALIGN = MEMORY_TAG_SHIFT_ZONE + MEMORY_TAG_BITWIDTH_ZONE,
|
||||
MEMORY_TAG_MASK_CLASS = ((1 << MEMORY_TAG_BITWIDTH_CLASS) - 1) << MEMORY_TAG_SHIFT_CLASS,
|
||||
MEMORY_TAG_MASK_ZONE = ((1 << MEMORY_TAG_BITWIDTH_ZONE) - 1) << MEMORY_TAG_SHIFT_ZONE,
|
||||
MEMORY_TAG_MASK_ALIGN = ((1 << MEMORY_TAG_BITWIDTH_ALIGN) - 1) << MEMORY_TAG_SHIFT_ALIGN,
|
||||
|
||||
MEMORY_TAG_CLASS_UNKNOWN = 0 << MEMORY_TAG_SHIFT_CLASS,
|
||||
MEMORY_TAG_CLASS_ARRAY = 1 << MEMORY_TAG_SHIFT_CLASS,
|
||||
MEMORY_TAG_CLASS_DYNARRAY = 2 << MEMORY_TAG_SHIFT_CLASS,
|
||||
MEMORY_TAG_CLASS_STRING = 3 << MEMORY_TAG_SHIFT_CLASS,
|
||||
MEMORY_TAG_CLASS_CIRCULAR_BUFFER = 4 << MEMORY_TAG_SHIFT_CLASS,
|
||||
MEMORY_TAG_CLASS_DICT = 5 << MEMORY_TAG_SHIFT_CLASS,
|
||||
MEMORY_TAG_CLASS_BINTREE = 6 << MEMORY_TAG_SHIFT_CLASS,
|
||||
MEMORY_TAG_CLASS_OCTTREE = 7 << MEMORY_TAG_SHIFT_CLASS,
|
||||
MEMORY_TAG_CLASS_TEXTURE = 9 << MEMORY_TAG_SHIFT_CLASS,
|
||||
MEMORY_TAG_CLASS_JOB = 9 << MEMORY_TAG_SHIFT_CLASS,
|
||||
MEMORY_TAG_CLASS_TRANSFORM = 10 << MEMORY_TAG_SHIFT_CLASS,
|
||||
MEMORY_TAG_CLASS_RENDER_OBJECT = 11 << MEMORY_TAG_SHIFT_CLASS,
|
||||
MEMORY_TAG_CLASS_MATERIAL = 12 << MEMORY_TAG_SHIFT_CLASS,
|
||||
MEMORY_TAG_CLASS_MAX_BUILTIN = (MEMORY_TAG_CLASS_MATERIAL >> MEMORY_TAG_SHIFT_CLASS) + 1,
|
||||
MEMORY_TAG_CLASS_MAX = (1 << MEMORY_TAG_BITWIDTH_CLASS) - 1,
|
||||
|
||||
MEMORY_TAG_ZONE_UNKNOWN = 0 << MEMORY_TAG_SHIFT_ZONE,
|
||||
MEMORY_TAG_ZONE_SCENE = 1 << MEMORY_TAG_SHIFT_ZONE,
|
||||
MEMORY_TAG_ZONE_APPLICATION = 2 << MEMORY_TAG_SHIFT_ZONE,
|
||||
MEMORY_TAG_ZONE_RENDERER = 3 << MEMORY_TAG_SHIFT_ZONE,
|
||||
MEMORY_TAG_ZONE_ENGINE = 4 << MEMORY_TAG_SHIFT_ZONE,
|
||||
MEMORY_TAG_ZONE_EVENT = 5 << MEMORY_TAG_SHIFT_ZONE,
|
||||
MEMORY_TAG_ZONE_DEBUG = 6 << MEMORY_TAG_SHIFT_ZONE,
|
||||
MEMORY_TAG_ZONE_AUDIO = 7 << MEMORY_TAG_SHIFT_ZONE,
|
||||
MEMORY_TAG_ZONE_MAX_BUILTIN = (MEMORY_TAG_ZONE_AUDIO >> MEMORY_TAG_SHIFT_ZONE) + 1,
|
||||
MEMORY_TAG_ZONE_MAX = (1 << MEMORY_TAG_BITWIDTH_ZONE) - 1,
|
||||
|
||||
MEMORY_TAG_ALIGN_ANY = 0 << MEMORY_TAG_SHIFT_ALIGN,
|
||||
MEMORY_TAG_ALIGN_32 = 1 << MEMORY_TAG_SHIFT_ALIGN,
|
||||
MEMORY_TAG_ALIGN_64 = 2 << MEMORY_TAG_SHIFT_ALIGN,
|
||||
MEMORY_TAG_ALIGN_PAGE = 3 << MEMORY_TAG_SHIFT_ALIGN,
|
||||
MEMORY_TAG_ALIGN_MAX_BUILTIN = (MEMORY_TAG_ALIGN_PAGE >> MEMORY_TAG_SHIFT_ALIGN) + 1,
|
||||
MEMORY_TAG_ALIGN_MAX = (1 << MEMORY_TAG_BITWIDTH_ALIGN) - 1,
|
||||
} MEMORY_TAGS;
|
||||
|
||||
namespace basalt
|
||||
{
|
||||
namespace mem
|
||||
{
|
||||
void initialize_memory(void);
|
||||
void terminate_memory (void);
|
||||
|
||||
template <typename T>
|
||||
T* allocT(u64 num_elements, MEMORY_TAG tag);
|
||||
void* alloc(u64 num_bytes, MEMORY_TAG tag);
|
||||
void dealloc(void* ptr, u64 num_bytes, MEMORY_TAG tag);
|
||||
template <typename T>
|
||||
T* setzeroT(T* dst, u64 num_elements);
|
||||
void* setzero(void* dst, u64 num_bytes);
|
||||
|
||||
void changetag(u64 nbytes, MEMORY_TAG prev, MEMORY_TAG next);
|
||||
|
||||
i64 get_total_memory_usage(void);
|
||||
i64 get_memory_usage_for_class(MEMORY_TAG memory_class, i64 out_per_zone[MEMORY_TAG_CLASS_MAX]);
|
||||
i64 get_memory_usage_for_zone(MEMORY_TAG memory_zone, i64 out_per_class[MEMORY_TAG_ZONE_MAX]);
|
||||
|
||||
// Must be freed by user via dealloc with the tags;
|
||||
// MEMORY_TAG_CLASS_STRING | MEMORY_TAG_ZONE_DEBUG | MEMORY_TAG_ALIGN_ANY
|
||||
char* get_memory_usage_string(void);
|
||||
|
||||
i64 get_memory_tag_class_name(MEMORY_TAG memory_class, char* out_buf, u64 out_buf_size);
|
||||
i64 get_memory_tag_zone_name(MEMORY_TAG zone_class, char* out_buf, u64 out_buf_size);
|
||||
|
||||
template<typename T>
|
||||
T* allocT(u64 num_elements, MEMORY_TAG tag)
|
||||
{ return alloc(num_elements*sizeof(T), tag); }
|
||||
template<typename T>
|
||||
T* setzeroT(T * dst, u64 num_elements)
|
||||
{ return setzero(dst, num_elements*sizeof(T)); }
|
||||
}
|
||||
}
|
||||
185
src/core/basalt_logger.cpp
Normal file
185
src/core/basalt_logger.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
#include <stdarg.h>
|
||||
#include <immintrin.h>
|
||||
#include <time.h>
|
||||
#include "core/basalt_logger.h"
|
||||
#include "memory.h"
|
||||
|
||||
static struct {
|
||||
logger_stream_t streams[BASALT_LOGGER_MAX_STREAMS];
|
||||
u8 global_mask;
|
||||
u8 num_streams;
|
||||
u8 initialized;
|
||||
u8 _padding[5];// Unused - Signifies how many bytes aren't being used
|
||||
} logger_state;
|
||||
|
||||
void initialize_logger()
|
||||
{
|
||||
if (logger_state.initialized != 0x00)
|
||||
return;
|
||||
memset(&logger_state, 0, sizeof(logger_state));
|
||||
#if defined(DEBUG) || defined(_DEBUG)
|
||||
logger_state.global_mask = LOG_MASK_ALL;
|
||||
#else
|
||||
logger_state.global_mask = LOG_MASK_DEFAULT;
|
||||
#endif
|
||||
logger_state.num_streams = 2;
|
||||
logger_add_stream(stdout, LOG_MASK_INFO | LOG_MASK_DEBUGGING, LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT | LOG_STREAM_FLAG_INCLUDE_DATETIME_BIT);
|
||||
logger_add_stream(stderr, LOG_MASK_ERRORS, LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT | LOG_STREAM_FLAG_INCLUDE_DATETIME_BIT);
|
||||
logger_state.initialized = 0xff;
|
||||
}
|
||||
|
||||
void terminate_logger()
|
||||
{
|
||||
if (logger_state.initialized != 0xff)
|
||||
return;
|
||||
for (u8 i = 0; i < BASALT_LOGGER_MAX_STREAMS; ++i)
|
||||
logger_remove_stream(i);
|
||||
memset(&logger_state, 0, sizeof(logger_state));
|
||||
logger_state.initialized = 0x00;
|
||||
}
|
||||
|
||||
inline FILE* logger_stream_get_file(logger_stream_t* state)
|
||||
{
|
||||
return (FILE*)((state->stream & 0xffffffffffff) | ((u64)stdout & 0xffff0000000000));
|
||||
}
|
||||
|
||||
u8 logger_add_stream(FILE* output, LOG_LEVEL mask, LOG_STREAM_FLAGS flags)
|
||||
{
|
||||
if (logger_state.num_streams == BASALT_LOGGER_MAX_STREAMS)
|
||||
return -1;
|
||||
if ((((u64)output) & 0xffffffffffff) != 0)
|
||||
__builtin_trap();
|
||||
for (u8 i = 0; i < BASALT_LOGGER_MAX_STREAMS; ++i)
|
||||
{
|
||||
if ((logger_state.streams[i].flags & LOG_STREAM_FLAG_INTERNAL_ALLOCATED_BIT) == 0)
|
||||
{
|
||||
logger_state.streams[i].stream = (u64)output;
|
||||
logger_state.streams[i].flags = flags;
|
||||
logger_state.streams[i].mask = mask;
|
||||
logger_state.num_streams++;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void logger_remove_stream(u8 index)
|
||||
{
|
||||
if (index >= sizeof(logger_state.streams) / sizeof(logger_state.streams[0]))
|
||||
return;
|
||||
logger_stream_t* stream = logger_state.streams+index;
|
||||
FILE* f = logger_stream_get_file(stream);
|
||||
if (f != stdout && f != stderr)
|
||||
fclose(f);
|
||||
stream->flags = 0;
|
||||
stream->mask = 0;
|
||||
stream->stream = (u64)nullptr;
|
||||
logger_state.num_streams--;
|
||||
}
|
||||
|
||||
u8 logger_find_stream(FILE* target)
|
||||
{
|
||||
for (size_t i = 0; i < BASALT_LOGGER_MAX_STREAMS; ++i)
|
||||
{
|
||||
if (logger_stream_get_file(logger_state.streams + i) == target &&
|
||||
(logger_state.streams[i].flags & LOG_STREAM_FLAG_INTERNAL_ALLOCATED_BIT) != 0)
|
||||
return i;
|
||||
}
|
||||
return (u8)(-1);
|
||||
}
|
||||
|
||||
void basalt_log(const LOG_LEVEL level, const char* msg, ...)
|
||||
{
|
||||
const char log_prefixes[8][6] = { "FATAL", "ERROR", "WARN ", "INFO ", "OK ", "WEIRD", "DEBUG", "TRACE" };
|
||||
const char* log_ansi_prefixes[8] = { "\033[38;5;0m\033[48;5;9m", "\033[38;5;9m", "\033[38;5;11m", "\033[38;5;15m", "\033[38;5;10m", "\033[38;5;13m", "\033[38;5;12m", "\033[38;5;6m" };
|
||||
if (level == 0)
|
||||
return;
|
||||
const u8 level = (u8)__builtin_ctz(level);
|
||||
if (level > 8)
|
||||
{
|
||||
debug_break();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((level & logger_state.global_mask) == 0)
|
||||
return;
|
||||
|
||||
u8 valid_streams = 0;
|
||||
for (u8 i = 0; i < BASALT_LOGGER_MAX_STREAMS; ++i)
|
||||
{
|
||||
logger_stream_t* stream = logger_state.streams + i;
|
||||
if ((stream->flags & LOG_STREAM_FLAG_INTERNAL_ALLOCATED_BIT) == 0)
|
||||
continue;
|
||||
valid_streams++;
|
||||
if ((stream->mask & level) == 0)
|
||||
continue;
|
||||
FILE* out = logger_stream_get_file(stream);
|
||||
if (stream->flags & LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT)
|
||||
fprintf(out, log_ansi_prefixes[level]);
|
||||
if ((stream->flags & LOG_STREAM_FLAG_DISABLE_PREFIXES) == 0)
|
||||
{
|
||||
if (stream->flags & LOG_STREAM_FLAG_INCLUDE_DATETIME_BIT)
|
||||
{
|
||||
char time_buffer[32];
|
||||
const time_t timer = time(NULL);
|
||||
struct tm tm_info;
|
||||
errno_t err = localtime_s(&tm_info, &timer);
|
||||
if (!err)
|
||||
{
|
||||
int n_chars = (int)strftime(time_buffer, sizeof(time_buffer), "%d-%m-%y %R:%S", &tm_info);
|
||||
fprintf(out, "[%*s %*s]: ", sizeof(log_prefixes[0]), log_prefixes[level], n_chars, time_buffer);
|
||||
}
|
||||
else
|
||||
fprintf(out, "[%*s]: ", sizeof(log_prefixes[0]), log_prefixes[level]);
|
||||
}
|
||||
else
|
||||
fprintf(out, "[%*s]: ", sizeof(log_prefixes[0]), log_prefixes[level]);
|
||||
}
|
||||
__builtin_va_list argstart;
|
||||
va_start(argstart, msg);
|
||||
vfprintf(out, msg, argstart);
|
||||
va_end(argstart);
|
||||
|
||||
if ((stream->flags & LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT) != 0)
|
||||
fprintf(out, "\033[m");
|
||||
}
|
||||
}
|
||||
|
||||
void basalt_write(const LOG_LEVEL level, const char* msg, ...)
|
||||
{
|
||||
const char log_prefixes[8][6] = { "FATAL", "ERROR", "WARN ", "INFO ", "OK ", "WEIRD", "DEBUG", "TRACE" };
|
||||
const char* log_ansi_prefixes[8] = { "\033[38;5;0m\033[48;5;9m", "\033[38;5;9m", "\033[38;5;11m", "\033[38;5;15m", "\033[38;5;10m", "\033[38;5;13m", "\033[38;5;12m", "\033[38;5;6m" };
|
||||
if (level == 0)
|
||||
return;
|
||||
const u8 level = (u8)__builtin_ctz(level);
|
||||
if (level > 8)
|
||||
{
|
||||
debug_break();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((level & logger_state.global_mask) == 0)
|
||||
return;
|
||||
|
||||
u8 valid_streams = 0;
|
||||
for (u8 i = 0; i < BASALT_LOGGER_MAX_STREAMS; ++i)
|
||||
{
|
||||
logger_stream_t* stream = logger_state.streams + i;
|
||||
if ((stream->flags & LOG_STREAM_FLAG_INTERNAL_ALLOCATED_BIT) == 0)
|
||||
continue;
|
||||
valid_streams++;
|
||||
if ((stream->mask & level) == 0)
|
||||
continue;
|
||||
FILE* out = logger_stream_get_file(stream);
|
||||
if (stream->flags & LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT)
|
||||
fprintf(out, log_ansi_prefixes[level]);
|
||||
|
||||
__builtin_va_list argstart;
|
||||
va_start(argstart, msg);
|
||||
vfprintf(out, msg, argstart);
|
||||
va_end(argstart);
|
||||
|
||||
if ((stream->flags & LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT) != 0)
|
||||
fprintf(out, "\033[m");
|
||||
}
|
||||
}
|
||||
235
src/core/basalt_memory.cpp
Normal file
235
src/core/basalt_memory.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
#include "core/basalt_logger.h"
|
||||
#include "core/basalt_memory.h"
|
||||
|
||||
#include <immintrin.h>
|
||||
#include <memory.h>
|
||||
#include <string.h>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
static struct {
|
||||
i64 alloc_total;
|
||||
i64 class_alloc[MEMORY_TAG_CLASS_MAX+1];
|
||||
i64 zone_alloc[MEMORY_TAG_ZONE_MAX + 1];
|
||||
} basalt_memory_state;
|
||||
|
||||
static const char* basalt_memory_class_names[MEMORY_TAG_CLASS_MAX_BUILTIN];
|
||||
static const char* basalt_memory_zone_names[MEMORY_TAG_ZONE_MAX_BUILTIN];
|
||||
static u8 basalt_memory_class_name_lengths[MEMORY_TAG_CLASS_MAX_BUILTIN];
|
||||
static u8 basalt_memory_zone_name_lengths[MEMORY_TAG_ZONE_MAX_BUILTIN];
|
||||
|
||||
void basalt::mem::initialize_memory(void)
|
||||
{
|
||||
memset(&basalt_memory_state, 0, sizeof(basalt_memory_state));
|
||||
for (size_t i = 0; i < MEMORY_TAG_CLASS_MAX_BUILTIN; ++i)
|
||||
basalt_memory_class_name_lengths[i] = strnlen(basalt_memory_class_names[i], 64);
|
||||
for (size_t i = 0; i < MEMORY_TAG_ZONE_MAX_BUILTIN; ++i)
|
||||
basalt_memory_zone_name_lengths[i] = strnlen(basalt_memory_zone_names[i], 64);
|
||||
}
|
||||
|
||||
void basalt::mem::terminate_memory(void) {}
|
||||
|
||||
void* basalt::mem::alloc(u64 num_bytes, MEMORY_TAG tag)
|
||||
{
|
||||
void* ptr = nullptr;
|
||||
if ((tag & MEMORY_TAG_MASK_ALIGN) == MEMORY_TAG_ALIGN_ANY)
|
||||
ptr = malloc(num_bytes);
|
||||
else if ((tag & MEMORY_TAG_MASK_ALIGN) == MEMORY_TAG_ALIGN_32)
|
||||
ptr = _mm_malloc(num_bytes, 32);
|
||||
else if ((tag & MEMORY_TAG_MASK_ALIGN) == MEMORY_TAG_ALIGN_64)
|
||||
ptr = _mm_malloc(num_bytes, 64);
|
||||
else if ((tag & MEMORY_TAG_MASK_ALIGN) == MEMORY_TAG_ALIGN_PAGE)
|
||||
ptr = _mm_malloc(num_bytes, 4096);
|
||||
if (ptr != nullptr)
|
||||
{
|
||||
basalt_memory_state.alloc_total += num_bytes;
|
||||
basalt_memory_state.class_alloc[(MEMORY_TAG_MASK_CLASS & tag) >> MEMORY_TAG_SHIFT_CLASS] += num_bytes;
|
||||
basalt_memory_state.zone_alloc[(MEMORY_TAG_MASK_ZONE & tag) >> MEMORY_TAG_SHIFT_ZONE] += num_bytes;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void basalt::mem::dealloc(void* ptr, u64 num_bytes, MEMORY_TAG tag)
|
||||
{
|
||||
if (ptr == nullptr)
|
||||
return;
|
||||
_mm_free(ptr);
|
||||
basalt_memory_state.alloc_total += num_bytes;
|
||||
basalt_memory_state.class_alloc[(MEMORY_TAG_MASK_CLASS & tag) >> MEMORY_TAG_SHIFT_CLASS] += num_bytes;
|
||||
basalt_memory_state.zone_alloc[(MEMORY_TAG_MASK_ZONE & tag) >> MEMORY_TAG_SHIFT_ZONE] += num_bytes;
|
||||
}
|
||||
|
||||
void* basalt::mem::setzero(void* dst, u64 num_bytes)
|
||||
{ return memset(dst, 0, num_bytes); }
|
||||
|
||||
void basalt::mem::changetag(u64 nbytes, MEMORY_TAG prev, MEMORY_TAG next)
|
||||
{
|
||||
BASSERT_FATAL((prev & MEMORY_TAG_MASK_ALIGN) == (next & MEMORY_TAG_MASK_ALIGN), "Assertion %s failed at %s:%d\n\tCan not change memory alignment tag after allocation!\n");
|
||||
if ((prev & MEMORY_TAG_MASK_CLASS) != (next & MEMORY_TAG_MASK_CLASS))
|
||||
{
|
||||
basalt_memory_state.class_alloc[(MEMORY_TAG_MASK_CLASS & prev) >> MEMORY_TAG_SHIFT_CLASS] -= nbytes;
|
||||
basalt_memory_state.class_alloc[(MEMORY_TAG_MASK_CLASS & next) >> MEMORY_TAG_SHIFT_CLASS] += nbytes;
|
||||
}
|
||||
if ((prev & MEMORY_TAG_MASK_ZONE) != (next & MEMORY_TAG_MASK_ZONE))
|
||||
{
|
||||
basalt_memory_state.zone_alloc[(MEMORY_TAG_MASK_ZONE & prev) >> MEMORY_TAG_SHIFT_ZONE] -= nbytes;
|
||||
basalt_memory_state.zone_alloc[(MEMORY_TAG_MASK_ZONE & next) >> MEMORY_TAG_SHIFT_ZONE] += nbytes;
|
||||
}
|
||||
}
|
||||
|
||||
i64 basalt::mem::get_total_memory_usage(void)
|
||||
{ return basalt_memory_state.alloc_total; }
|
||||
|
||||
i64 basalt::mem::get_memory_usage_for_class(MEMORY_TAG memory_class, i64 out_per_class[MEMORY_TAG_CLASS_MAX])
|
||||
{
|
||||
if (out_per_class != nullptr)
|
||||
memcpy(out_per_class, basalt_memory_state.class_alloc, sizeof(i64)*MEMORY_TAG_CLASS_MAX);
|
||||
return basalt_memory_state.class_alloc[(memory_class & MEMORY_TAG_MASK_CLASS) >> MEMORY_TAG_SHIFT_CLASS];
|
||||
}
|
||||
|
||||
i64 basalt::mem::get_memory_usage_for_zone(MEMORY_TAG memory_zone, i64 out_per_zone[MEMORY_TAG_ZONE_MAX])
|
||||
{
|
||||
if (out_per_zone != nullptr)
|
||||
memcpy(out_per_zone, basalt_memory_state.zone_alloc, sizeof(i64) * MEMORY_TAG_ZONE_MAX);
|
||||
return basalt_memory_state.zone_alloc[(memory_zone & MEMORY_TAG_MASK_ZONE) >> MEMORY_TAG_SHIFT_ZONE];
|
||||
}
|
||||
|
||||
f64 get_eng_unit(f64 x, char* unit)
|
||||
{
|
||||
constexpr i8 order_index_offset = 5;
|
||||
const char units[] = {'f', 'p', 'n', 'u', 'm', ' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Y'};
|
||||
i8 order = 0;
|
||||
while (x > 1000.0 && (order < (sizeof(units) - sizeof(char) * (order_index_offset-1))))
|
||||
{
|
||||
order++;
|
||||
x /= 1000.0;
|
||||
}
|
||||
while (x < 1.0 && (order+order_index_offset) > 0)
|
||||
{
|
||||
order--;
|
||||
x *= 1000.0;
|
||||
}
|
||||
*unit = units[order + order_index_offset];
|
||||
}
|
||||
|
||||
char* basalt::mem::get_memory_usage_string(void)
|
||||
{
|
||||
constexpr size_t buffer_size = 2048;
|
||||
|
||||
static i32 class_pad_amt = 0;
|
||||
if (class_pad_amt)
|
||||
{
|
||||
for (u32 i = 0; i < MEMORY_TAG_CLASS_MAX_BUILTIN; ++i)
|
||||
class_pad_amt = class_pad_amt < basalt_memory_class_name_lengths[i] ? basalt_memory_class_name_lengths[i] : class_pad_amt;
|
||||
const u32 num_class_user_digits = (u32)ceilf(log10f(MEMORY_TAG_CLASS_MAX - MEMORY_TAG_CLASS_MAX_BUILTIN));
|
||||
class_pad_amt = class_pad_amt < num_class_user_digits ? num_class_user_digits : class_pad_amt;
|
||||
}
|
||||
static i32 zone_pad_amt = 0;
|
||||
if (zone_pad_amt)
|
||||
{
|
||||
for (u32 i = 0; i < MEMORY_TAG_ZONE_MAX_BUILTIN; ++i)
|
||||
zone_pad_amt = zone_pad_amt < basalt_memory_zone_name_lengths[i] ? basalt_memory_zone_name_lengths[i] : zone_pad_amt;
|
||||
const u32 num_zone_user_digits = (u32)ceilf(log10f(MEMORY_TAG_ZONE_MAX - MEMORY_TAG_ZONE_MAX_BUILTIN));
|
||||
zone_pad_amt = zone_pad_amt < num_zone_user_digits ? num_zone_user_digits : zone_pad_amt;
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
char* ret = (char*)basalt::mem::alloc(buffer_size*sizeof(char),
|
||||
MEMORY_TAG_CLASS_STRING | MEMORY_TAG_ZONE_DEBUG | MEMORY_TAG_ALIGN_ANY);
|
||||
char name_buf[256];
|
||||
|
||||
char unit = ' ';
|
||||
f64 val = get_eng_unit(basalt_memory_state.alloc_total, &unit);
|
||||
offset += snprintf(ret+offset, buffer_size - offset - 1, "Total memory usage: %6.2f %cB\nMemory usage by class:\n", val, unit);
|
||||
for (u32 i = 0; i <= MEMORY_TAG_CLASS_MAX; ++i)
|
||||
{
|
||||
val = get_eng_unit(basalt_memory_state.class_alloc[i], &unit);
|
||||
name_buf[get_memory_tag_class_name(i << MEMORY_TAG_SHIFT_CLASS, name_buf, sizeof(name_buf)-1)] = '\0';
|
||||
offset += snprintf(ret+offset, buffer_size - offset - 1, "%-*s: %6.2f %cB\n", class_pad_amt, name_buf, val, unit);
|
||||
}
|
||||
offset += snprintf(ret+offset, buffer_size - offset - 1, "Memory usage by location:\n");
|
||||
for (u32 i = 0; i <= MEMORY_TAG_CLASS_MAX; ++i)
|
||||
{
|
||||
val = get_eng_unit(basalt_memory_state.zone_alloc[i], &unit);
|
||||
name_buf[get_memory_tag_zone_name(i << MEMORY_TAG_SHIFT_ZONE, name_buf, sizeof(name_buf) - 1)] = '\0';
|
||||
offset += snprintf(ret + offset, buffer_size - offset - 1, "%-*s: %6.2f %cB\n", zone_pad_amt, name_buf, val, unit);
|
||||
}
|
||||
ret[offset] = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char* basalt_memory_class_names[MEMORY_TAG_CLASS_MAX_BUILTIN] = {
|
||||
"UNKNOWN",
|
||||
"ARRAY",
|
||||
"DYNARRAY",
|
||||
"STRING",
|
||||
"CIRCULAR_BUFFER",
|
||||
"DICT",
|
||||
"BINTREE",
|
||||
"OCTTREE",
|
||||
"TEXTURE",
|
||||
"JOB",
|
||||
"TRANSFORM",
|
||||
"RENDER_OBJECT",
|
||||
"MATERIAL"
|
||||
};
|
||||
static const char* basalt_memory_zone_names[MEMORY_TAG_ZONE_MAX_BUILTIN] = {
|
||||
"UNKNOWN",
|
||||
"SCENE",
|
||||
"APPLICATION",
|
||||
"RENDERER",
|
||||
"ENGINE",
|
||||
"EVENT",
|
||||
"DEBUG",
|
||||
"AUDIO"
|
||||
};
|
||||
|
||||
i64 basalt::mem::get_memory_tag_class_name(MEMORY_TAG memory_class, char* out_buf, u64 out_buf_size)
|
||||
{
|
||||
memory_class = (memory_class & MEMORY_TAG_MASK_CLASS) >> MEMORY_TAG_SHIFT_CLASS;
|
||||
if (memory_class < MEMORY_TAG_CLASS_MAX_BUILTIN)
|
||||
{
|
||||
if (out_buf_size < basalt_memory_class_name_lengths[memory_class])
|
||||
return 0;
|
||||
memcpy(out_buf, basalt_memory_class_names[memory_class], basalt_memory_class_name_lengths[memory_class]);
|
||||
}
|
||||
|
||||
// Calculate the user postfix value
|
||||
constexpr u16 user_index_max = MEMORY_TAG_CLASS_MAX - MEMORY_TAG_CLASS_MAX_BUILTIN + 1;
|
||||
u16 user_index = ((memory_class & MEMORY_TAG_ZONE_MAX) >> MEMORY_TAG_SHIFT_ZONE);
|
||||
if (user_index > user_index_max)
|
||||
return 0;
|
||||
user_index = user_index_max - user_index;
|
||||
|
||||
// Calculate the number of digits the number will contain
|
||||
u16 ndigits = (u16)ceil(log10l(user_index));
|
||||
if ((ndigits + 7) > out_buf_size)
|
||||
return 0;
|
||||
// Convert the string to an int and prefix it with USER_
|
||||
snprintf(out_buf, out_buf_size, "USER_%u", user_index);
|
||||
return 7+ndigits*sizeof(char);
|
||||
}
|
||||
|
||||
i64 basalt::mem::get_memory_tag_zone_name(MEMORY_TAG zone_class, char* out_buf, u64 out_buf_size)
|
||||
{
|
||||
zone_class = (zone_class & MEMORY_TAG_MASK_ZONE) >> MEMORY_TAG_SHIFT_ZONE;
|
||||
if (zone_class < MEMORY_TAG_ZONE_MAX_BUILTIN)
|
||||
{
|
||||
if (out_buf_size < basalt_memory_zone_name_lengths[zone_class])
|
||||
return 0;
|
||||
memcpy(out_buf, basalt_memory_zone_names[zone_class], basalt_memory_zone_name_lengths[zone_class]);
|
||||
}
|
||||
constexpr u16 user_index_max = MEMORY_TAG_CLASS_MAX - MEMORY_TAG_CLASS_MAX_BUILTIN + 1;
|
||||
|
||||
u16 user_index = ((zone_class & MEMORY_TAG_ZONE_MAX) >> MEMORY_TAG_SHIFT_ZONE);
|
||||
if (user_index > user_index_max)
|
||||
return 0;
|
||||
user_index = user_index_max - user_index;
|
||||
|
||||
u16 ndigits = (u16)ceil(log10l(user_index));
|
||||
if ((ndigits + 7) > out_buf_size)
|
||||
return 0;
|
||||
snprintf(out_buf, out_buf_size, "USER_%u", user_index);
|
||||
return 7 + ndigits * sizeof(char);
|
||||
}
|
||||
Reference in New Issue
Block a user