revamped makefiles, overhauled memory pool allocator (instantiator works without memory pooling as a byproduct)

This commit is contained in:
ecker 2026-04-29 19:04:49 -05:00
parent 3965d3f719
commit b523878912
24 changed files with 971 additions and 724 deletions

510
Makefile
View File

@ -1,419 +1,96 @@
ARCH = $(shell cat "./makefiles/default/arch")
CC = $(shell cat "./makefiles/default/cc")
RENDERER = $(shell cat "./makefiles/default/renderer")
TARGET_NAME = program
TARGET_EXTENSION = .exe
DLIB_EXTENSION = .dll
SLIB_EXTENSION = .a
PREFIX = $(ARCH).$(CC)
# Defaults
ARCH = $(shell cat "./makefiles/default/arch")
CC = $(shell cat "./makefiles/default/cc")
RENDERER = $(shell cat "./makefiles/default/renderer")
TARGET_NAME = program
TARGET_EXTENSION = .exe
DLIB_EXTENSION = .dll
SLIB_EXTENSION = .a
PREFIX = $(ARCH).$(CC).$(RENDERER)
include makefiles/$(PREFIX).make
# Basic Paths
7Z ?= /c/Program\ Files/7-Zip/7z.exe
CXX := $(CDIR)$(CXX)
BIN_DIR += ./bin
PREFIX = $(ARCH).$(CC).$(RENDERER)
PREFIX_PATH = $(ARCH)/$(CC)/$(RENDERER)
ENGINE_SRC_DIR += ./engine/src
ENGINE_INC_DIR += ./engine/inc
ENGINE_LIB_DIR += ./engine/lib
DEP_SRC_DIR += ./dep/src
EXT_SRC_DIR += ./ext
CLIENT_SRC_DIR += ./client
.PHONY: $(PREFIX)
@echo "Setting defaults: $(ARCH).$(CC).$(RENDERER)"
# Base Flags
OPTIMIZATIONS = -O3 -fstrict-aliasing -DUF_NO_EXCEPTIONS
WARNINGS = -Wall -Wno-unknown-pragmas -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-reorder -Wno-sign-compare -Wno-unused-but-set-variable -Wno-ignored-attributes -Wno-narrowing -Wno-misleading-indentation
FLAGS += -std=c++2b $(OPTIMIZATIONS) $(WARNINGS) -fdiagnostics-color=always -DUF_DEV_ENV
# Base Library Definitions
LIB_NAME += uf
EXT_LIB_NAME += ext
PREFIX_PATH = $(ARCH)/$(CC)/$(RENDERER)
INC_DIR += $(ENGINE_INC_DIR)
LIB_DIR += $(ENGINE_LIB_DIR)
INCS += -I$(ENGINE_INC_DIR) -I./dep/include/
LIBS += -L$(ENGINE_LIB_DIR) -L$(LIB_DIR)/$(PREFIX_PATH)/ -L$(LIB_DIR)/$(ARCH)/$(CC)/ -L$(LIB_DIR)/$(ARCH)/
LINKS += $(UF_LIBS) $(EXT_LIBS) $(DEPS)
# DLL
SRCS_DLL := $(shell find $(ENGINE_SRC_DIR) -name "*.cpp") $(shell find $(DEP_SRC_DIR) -name "*.cpp")
OBJS_DLL += $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS_DLL))
BASE_DLL += lib$(LIB_NAME)
IM_DLL += $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_DLL)$(DLIB_EXTENSION)
EX_DLL += $(BIN_DIR)/exe/lib/$(PREFIX_PATH)/$(BASE_DLL)$(DLIB_EXTENSION)
EXT_IM_DLL += $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_EXT_DLL)$(DLIB_EXTENSION)
EXT_EX_DLL += $(BIN_DIR)/exe/lib/$(PREFIX_PATH)/$(BASE_EXT_DLL)$(DLIB_EXTENSION)
SRCS_EXT_DLL := $(shell find $(EXT_SRC_DIR) -name "*.cpp")
OBJS_EXT_DLL += $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS_EXT_DLL))
BASE_EXT_DLL += lib$(EXT_LIB_NAME)
EXT_DEPS += -l$(LIB_NAME) $(DEPS)
EXT_INC_DIR += $(INC_DIR)
EXT_INCS += $(INCS)
EXT_LIBS += $(LIBS)
EXT_LINKS += $(UF_LIBS) $(EXT_LIBS) $(EXT_DEPS)
# Executable
SRCS := $(shell find $(CLIENT_SRC_DIR) -name "*.cpp")
OBJS += $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS))
TARGET += $(BIN_DIR)/exe/$(TARGET_NAME).$(PREFIX)$(TARGET_EXTENSION)
# Shaders
SRCS_SHADERS := $(shell find bin/data/shaders/ -name "*.glsl")
TARGET_SHADERS += $(patsubst %.glsl,%.spv,$(SRCS_SHADERS))
.DEFAULT_GOAL := $(PREFIX)
.PHONY: $(PREFIX) clean run run-debug clean-shaders backup
.FORCE:
CXX := $(CDIR)$(CXX)
BIN_DIR += ./bin
ENGINE_SRC_DIR += ./engine/src
ENGINE_INC_DIR += ./engine/inc
ENGINE_LIB_DIR += ./engine/lib
DEP_SRC_DIR += ./dep/src
EXT_SRC_DIR += ./ext
CLIENT_SRC_DIR += ./client
UF_LIBS +=
EXT_LIBS +=
FLAGS +=
LIB_NAME += uf
EXT_LIB_NAME += ext
7Z += /c/Program\ Files/7-Zip/7z.exe
include makefiles/platforms/$(ARCH).$(CC).mk
ifneq (,$(findstring win64,$(ARCH)))
VULKAN_SDK_PATH = /c/VulkanSDK/1.4.321.1/
GLSLC = $(VULKAN_SDK_PATH)/Bin/glslc
SPV_OPTIMIZER = $(VULKAN_SDK_PATH)/Bin/spirv-opt
SPV_LINTER = $(VULKAN_SDK_PATH)/Bin/spirv-lint
else
VULKAN_SDK_PATH = /
GLSLC = glslc
SPV_OPTIMIZER = spirv-opt
SPV_LINTER = spirv-lint
endif
# Base Engine's DLL
INC_DIR += $(ENGINE_INC_DIR)
LIB_DIR += $(ENGINE_LIB_DIR)
INCS += -I$(ENGINE_INC_DIR) -I./dep/include/
LIBS += -L$(ENGINE_LIB_DIR) -L$(LIB_DIR)/$(PREFIX_PATH)/ -L$(LIB_DIR)/$(ARCH)/$(CC)/ -L$(LIB_DIR)/$(ARCH)/
LINKS += $(UF_LIBS) $(EXT_LIBS) $(DEPS)
DEPS +=
FLAGS += -DUF_DEV_ENV
ifneq (,$(findstring win64,$(ARCH)))
ifneq (,$(findstring -DUF_DEV_ENV,$(FLAGS)))
REQ_DEPS += meshoptimizer toml xatlas curl ffx:sdk dc:texconv # vall_e cpptrace # openvr # ncurses draco discord bullet ultralight-ux
FLAGS += -march=native -g # -flto # -g
endif
REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit r:eactphysics simd ctti gltf imgui fmt freetype openal ogg wav
FLAGS += -DUF_ENV_WINDOWS -DUF_ENV_WIN64 -DWIN32_LEAN_AND_MEAN
DEPS += -lgdi32 -ldwmapi
LINKS += #-Wl,-subsystem,windows
INCS := -I./dep/master/include $(INCS)
include makefiles/platforms/win64.mk
else ifneq (,$(findstring linux,$(ARCH)))
ifneq (,$(findstring -DUF_DEV_ENV,$(FLAGS)))
REQ_DEPS += toml xatlas curl dc:texconv # meshoptimizer ffx:fsr cpptrace vall_e # ncurses openvr draco discord bullet ultralight-ux
FLAGS += -march=native -g # -flto # -g
endif
REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit r:eactphysics simd ctti gltf imgui fmt freetype openal ogg wav
FLAGS += -DUF_ENV_LINUX -fPIC
DEPS += -pthread -ldl -lX11 -lXrandr
INCS := -I./dep/master/include $(INCS)
include makefiles/platforms/linux.mk
else ifneq (,$(findstring dreamcast,$(ARCH)))
FLAGS += -DUF_ENV_DREAMCAST # -DUF_LEAN_AND_MEAN # this apparently crashes
REQ_DEPS += opengl gldc json:nlohmann zlib lua r:eactphysics simd ctti fmt freetype openal aldc ogg wav png # imgui
INCS := -I./dep/dreamcast/include $(INCS)
include makefiles/platforms/dreamcast.mk
endif
ifneq (,$(findstring ffx:sdk,$(REQ_DEPS)))
ifneq (,$(findstring vulkan,$(REQ_DEPS)))
FLAGS += -DUF_USE_FFX_SDK=3
#INCS += -I./dep/include/ffx_fsr2/
#DEPS += -lffx_fsr3_x64drel -lffx_fsr2_x64drel -lffx_fsr3upscaler_x64drel -lffx_frameinterpolation_x64drel -lffx_opticalflow_x64drel -lffx_backend_vk_x64drel -lamd_fidelityfx_vkdrel
DEPS += -lffx_fsr3_x64 -lffx_fsr2_x64 -lffx_fsr3upscaler_x64 -lffx_frameinterpolation_x64 -lffx_opticalflow_x64 -lffx_backend_vk_x64 -lamd_fidelityfx_vk
endif
endif
ifneq (,$(findstring vulkan,$(REQ_DEPS)))
FLAGS += -DUF_USE_VULKAN
DEPS += -lspirv-cross-core -lspirv-cross-cpp #-lVulkanMemoryAllocator
INCS += -I$(VULKAN_SDK_PATH)/include -I./dep/include/spirv_cross/
LIBS += -L$(VULKAN_SDK_PATH)/Lib
ifneq (,$(findstring linux,$(ARCH)))
DEPS += -lvulkan
FLAGS += -DVK_USE_PLATFORM_XLIB_KHR # VK_USE_PLATFORM_XCB_KHR
else
DEPS += -lvulkan-1
FLAGS += -DVK_USE_PLATFORM_WIN32_KHR
endif
endif
ifneq (,$(findstring opengl,$(REQ_DEPS)))
FLAGS += -DUF_USE_OPENGL -DUF_USE_OPENGL_FIXED_FUNCTION
include makefiles/dependencies.mk
ifneq (,$(findstring dreamcast,$(ARCH)))
ifneq (,$(findstring gldc,$(REQ_DEPS)))
DEPS += -lGL # kos-ports version saves it as libGL instead of libGLdc
FLAGS += -DUF_USE_OPENGL_GLDC
else
DEPS += -lGL
endif
else
# software rendering version through GLdc
ifneq (,$(findstring gldc,$(REQ_DEPS)))
DEPS += -lGLdc -lSDL2
FLAGS += -DUF_USE_OPENGL_GLDC
# OpenGL through GLEW
else
FLAGS += -DUF_USE_GLEW
ifneq (,$(findstring linux,$(ARCH)))
DEPS += -lGLU -lglut -lGLEW
else
DEPS += -lglew32 -lopengl32 -lglu32
endif
endif
endif
# Build Rules
endif
ifneq (,$(findstring fmt,$(REQ_DEPS)))
FLAGS += -DUF_USE_FMT
ifneq (,$(findstring dreamcast,$(ARCH)))
# dreamcast uses a header only version because reasons
else
DEPS += -lfmt
endif
endif
ifneq (,$(findstring ffx:fsr,$(REQ_DEPS)))
ifneq (,$(findstring vulkan,$(REQ_DEPS)))
FLAGS += -DUF_USE_FFX_FSR
#INCS += -I./dep/include/ffx_fsr2/
DEPS += -lffx_fsr2_api_ -lffx_fsr2_api_vk_
endif
endif
ifneq (,$(findstring imgui,$(REQ_DEPS)))
FLAGS += -DUF_USE_IMGUI
INCS += -I./dep/include/imgui/
INCS += -I./dep/include/imgui/backends
endif
ifneq (,$(findstring cpptrace,$(REQ_DEPS)))
FLAGS += -DUF_USE_CPPTRACE
DEPS += -lcpptrace
endif
ifneq (,$(findstring json,$(REQ_DEPS)))
FLAGS += -DUF_USE_JSON
DEPS +=
ifneq (,$(findstring nlohmann,$(REQ_DEPS)))
FLAGS += -DUF_JSON_USE_NLOHMANN
endif
endif
ifneq (,$(findstring gltf,$(REQ_DEPS)))
FLAGS += -DUF_USE_GLTF
INCS += -I./dep/include/stb/ # saves having to edit the file
INCS += -I./dep/include/nlohmann/ # saves having to edit the file
endif
ifneq (,$(findstring dc:texconv,$(REQ_DEPS)))
FLAGS += -DUF_USE_DC_TEXCONV
endif
ifneq (,$(findstring openal,$(REQ_DEPS)))
FLAGS += -DUF_USE_OPENAL -DUF_USE_ALUT
ifneq (,$(findstring dreamcast,$(ARCH)))
ifneq (,$(findstring aldc,$(REQ_DEPS)))
DEPS += -lAL -lpthread # kos-ports version saves it as libAL instead of libALdc
FLAGS += -DUF_USE_OPENAL_ALDC -DUF_USE_ALUT
else
DEPS += -lAL
endif
else
DEPS += -lopenal -lalut
endif
endif
ifneq (,$(findstring ogg,$(REQ_DEPS)))
FLAGS += -DUF_USE_VORBIS
ifneq (,$(findstring dreamcast,$(ARCH)))
FLAGS += -DUF_USE_TREMOR
DEPS += -ltremor
else
DEPS += -lvorbis -lvorbisfile -logg
endif
endif
ifneq (,$(findstring wav,$(REQ_DEPS)))
FLAGS += -DUF_USE_WAV
endif
ifneq (,$(findstring zlib,$(REQ_DEPS)))
FLAGS += -DUF_USE_ZLIB
DEPS += -lz
endif
ifneq (,$(findstring freetype,$(REQ_DEPS)))
FLAGS += -DUF_USE_FREETYPE
DEPS += -lfreetype -lbz2
endif
ifneq (,$(findstring curl,$(REQ_DEPS)))
FLAGS += -DUF_USE_CURL
DEPS += -lcurl
endif
ifneq (,$(findstring openvr,$(REQ_DEPS)))
FLAGS += -DUF_USE_OPENVR -DUSE_OPENVR_MINGW
DEPS += -lopenvr_api
endif
ifneq (,$(findstring lua,$(REQ_DEPS)))
FLAGS += -DUF_USE_LUA
ifneq (,$(findstring luajit,$(REQ_DEPS)))
FLAGS += -DUF_USE_LUAJIT
DEPS += -lluajit-5.1
# sol directly includes <luajit.h>
ifneq (,$(findstring linux,$(ARCH)))
INCS += -I/usr/include/luajit-2.1
else
ifneq (,$(findstring clang,$(CC)))
INCS += -I/clang64/include/luajit-2.1
else
INCS += -I/mingw64/include/luajit-2.1
endif
endif
else
ifneq (,$(findstring dreamcast,$(ARCH)))
DEPS += -llua
INCS += -I/opt/dreamcast/kos-ports/include/lua
endif
endif
endif
ifneq (,$(findstring reactphysics,$(REQ_DEPS)))
FLAGS += -DUF_USE_REACTPHYSICS
DEPS += -lreactphysics3d
endif
ifneq (,$(findstring simd,$(REQ_DEPS)))
ifneq (,$(findstring dreamcast,$(ARCH)))
FLAGS += -DUF_ENV_DREAMCAST_SIMD
else
FLAGS += -DUF_USE_SIMD -DUF_ALIGN_FOR_SIMD -DUF_MATRIX_ALIGNED #-DUF_VECTOR_ALIGNED #-march=native
endif
endif
ifneq (,$(findstring meshoptimizer,$(REQ_DEPS)))
FLAGS += -DUF_USE_MESHOPT
DEPS += -lmeshoptimizer
endif
ifneq (,$(findstring xatlas,$(REQ_DEPS)))
FLAGS += -DUF_USE_XATLAS
endif
ifneq (,$(findstring ctti,$(REQ_DEPS)))
FLAGS += -DUF_CTTI -fno-rtti
else
FLAGS += -DUF_RTTI -rtti
endif
ifneq (,$(findstring toml,$(REQ_DEPS)))
FLAGS += -DUF_USE_TOML
endif
ifneq (,$(findstring vall_e,$(REQ_DEPS)))
FLAGS += -DUF_USE_VALL_E
INCS += -I./dep/include/vall_e.cpp/
DEPS += -lvall_e
endif
ifneq (,$(findstring draco,$(REQ_DEPS)))
FLAGS += -DUF_USE_DRACO
DEPS += -ldraco
endif
ifneq (,$(findstring ultralight-ux,$(REQ_DEPS)))
FLAGS += -DUF_USE_ULTRALIGHT_UX
DEPS += -lUltralight -lUltralightCore -lWebCore -lAppCore
endif
ifneq (,$(findstring discord,$(REQ_DEPS)))
FLAGS += -DUF_USE_DISCORD
DEPS += -ldiscord_game_sdk
endif
ifneq (,$(findstring ncurses,$(REQ_DEPS)))
FLAGS += -DUF_USE_NCURSES
DEPS += -lncursesw
endif
ifneq (,$(findstring png,$(REQ_DEPS)))
FLAGS += -DUF_USE_PNG
DEPS += -lpng
endif
ifneq (,$(findstring lz4,$(REQ_DEPS)))
FLAGS += -DUF_USE_LZ4
DEPS += -llz4
endif
ifneq (,$(findstring xz,$(REQ_DEPS)))
FLAGS += -DUF_USE_XZ -DUF_USE_LZMA
DEPS += -lxz -llzma
endif
SRCS_DLL := $(shell find $(ENGINE_SRC_DIR) -name "*.cpp") $(shell find $(DEP_SRC_DIR) -name "*.cpp")
OBJS_DLL += $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS_DLL))
BASE_DLL += lib$(LIB_NAME)
IM_DLL += $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_DLL)$(DLIB_EXTENSION)
EX_DLL += $(BIN_DIR)/exe/lib/$(PREFIX_PATH)/$(BASE_DLL)$(DLIB_EXTENSION)
# External Engine's DLL
EXT_INC_DIR += $(INC_DIR)
EXT_LB_FLAGS += $(LIB_DIR)
EXT_DEPS += -l$(LIB_NAME) $(DEPS)
EXT_LINKS += $(UF_LIBS) $(EXT_LIBS) $(EXT_DEPS)
EXT_LIB_DIR += $(ENGINE_LIB_DIR)/$(ARCH)
EXT_INCS += $(INCS)
EXT_LIBS += $(LIBS)
#SRCS_EXT_DLL += $(wildcard $(EXT_SRC_DIR)/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*/*/*.cpp) $(wildcard $(EXT_SRC_DIR)/*/*/*/*/*.cpp)
SRCS_EXT_DLL := $(shell find $(EXT_SRC_DIR) -name "*.cpp")
OBJS_EXT_DLL += $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS_EXT_DLL))
BASE_EXT_DLL += lib$(EXT_LIB_NAME)
EXT_IM_DLL += $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_EXT_DLL)$(DLIB_EXTENSION)
EXT_EX_DLL += $(BIN_DIR)/exe/lib/$(PREFIX_PATH)/$(BASE_EXT_DLL)$(DLIB_EXTENSION)
# Client EXE
#SRCS += $(wildcard $(CLIENT_SRC_DIR)/*.cpp) $(wildcard $(CLIENT_SRC_DIR)/*/*.cpp)
SRCS := $(shell find $(CLIENT_SRC_DIR) -name "*.cpp")
OBJS += $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS))
TARGET += $(BIN_DIR)/exe/$(TARGET_NAME).$(PREFIX)$(TARGET_EXTENSION)
# Shaders
#SRCS_SHADERS += $(wildcard bin/data/shaders/*.glsl) $(wildcard bin/data/shaders/*/*.glsl) $(wildcard bin/data/shaders/*/*/*.glsl)
SRCS_SHADERS := $(shell find bin/data/shaders/ -name "*.glsl")
TARGET_SHADERS += $(patsubst %.glsl,%.spv,$(SRCS_SHADERS))
ifneq (,$(findstring dreamcast,$(ARCH)))
#$(PREFIX): $(EX_DLL) $(EXT_EX_DLL) $(TARGET) ./bin/dreamcast/$(TARGET_NAME).cdi
$(PREFIX): $(TARGET) ./bin/dreamcast/$(TARGET_NAME).cdi
#SRCS_DLL = $(wildcard $(ENGINE_SRC_DIR)/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*/*/*.cpp) $(wildcard $(ENGINE_SRC_DIR)/*/*/*/*/*.cpp)
SRCS_DLL = $(shell find $(ENGINE_SRC_DIR) -name "*.cpp") $(shell find $(DEP_SRC_DIR) -name "*.cpp")
OBJS_DLL = $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS_DLL))
OBJS = $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS_DLL)) $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS_EXT_DLL)) $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS))
DEPS += -lkallisti -lc -lm -lgcc -lstdc++ # -l$(LIB_NAME) -l$(EXT_LIB_NAME)
INCS += -I$(KOS_PORTS)/include
LIBS += -I$(KOS_PORTS)/lib
%.$(PREFIX).o: %.cpp
$(CXX) $(FLAGS) $(INCS) -c $< -o $@
$(EX_DLL): FLAGS += -DUF_EXPORTS
$(EX_DLL): $(OBJS_DLL)
$(KOS_AR) cru $@ $^
$(KOS_RANLIB) $@
cp $@ $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_DLL)
$(EXT_EX_DLL): FLAGS += -DEXT_EXPORTS
$(EXT_EX_DLL): $(OBJS_EXT_DLL)
$(KOS_AR) cru $@ $^
$(KOS_RANLIB) $@
cp $@ $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_EXT_DLL)
./bin/dreamcast/romdisk.img:
$(KOS_GENROMFS) -f ./bin/dreamcast/romdisk.img -d ./bin/dreamcast/romdisk/ -v
./bin/dreamcast/romdisk.o: ./bin/dreamcast/romdisk.img
$(KOS_BASE)/utils/bin2o/bin2o ./bin/dreamcast/romdisk.img romdisk ./bin/dreamcast/romdisk.o
$(TARGET): $(OBJS) #./bin/dreamcast/romdisk.o
$(CXX) $(FLAGS) $(INCS) -D_arch_dreamcast -D_arch_sub_pristine -Wall -fno-builtin -ml -Wl,-Ttext=0x8c010000 -T/opt/dreamcast/kos/utils/ldscripts/shlelf.xc -nodefaultlibs $(KOS_LIB_PATHS) $(LIBS) -o $(TARGET) $(OBJS) -Wl,--start-group $(DEPS) -Wl,--end-group
cp $(TARGET) $(TARGET).unstripped
$(KOS_STRIP) --strip-unneeded $(TARGET)
./bin/dreamcast/$(TARGET_NAME).cdi: $(TARGET)
cd ./bin/dreamcast/; ./elf2cdi.sh $(TARGET_NAME)
cdi:
cd ./bin/dreamcast/; ./elf2cdi.sh $(TARGET_NAME)
else
$(PREFIX): $(EX_DLL) $(EXT_EX_DLL) $(TARGET) $(TARGET_SHADERS)
%.$(PREFIX).o: %.cpp
$(CXX) $(FLAGS) $(INCS) -c $< -o $@
ifneq (,$(findstring linux,$(ARCH)))
#$(EX_DLL): FLAGS += -DUF_EXPORTS -DEXT_EXPORTS
$(EX_DLL): FLAGS += -DUF_EXPORTS
$(EX_DLL): $(OBJS_DLL)
$(CXX) $(FLAGS) -shared -Wl,-soname,$(BASE_DLL)$(DLIB_EXTENSION) $(OBJS_DLL) $(LIBS) $(INCS) $(LINKS) -o $(EX_DLL)
cp $(EX_DLL) $(IM_DLL)
@echo -n $(ARCH) > "./bin/exe/default/arch"
@echo -n $(CC) > "./bin/exe/default/cc"
@echo -n $(RENDERER) > "./bin/exe/default/renderer"
@echo "Setting defaults: $(ARCH).$(CC).$(RENDERER)"
$(EXT_EX_DLL): FLAGS += -DEXT_EXPORTS
$(EXT_EX_DLL): $(OBJS_EXT_DLL)
$(CXX) $(FLAGS) -shared -Wl,-soname,$(BASE_EXT_DLL)$(DLIB_EXTENSION) $(OBJS_EXT_DLL) $(EXT_LIBS) $(EXT_INCS) $(EXT_LINKS) -o $(EXT_EX_DLL)
cp $(EXT_EX_DLL) $(EXT_IM_DLL)
else
#$(EX_DLL): FLAGS += -DUF_EXPORTS -DEXT_EXPORTS
$(EX_DLL): FLAGS += -DUF_EXPORTS
$(EX_DLL): $(OBJS_DLL)
$(CXX) $(FLAGS) -shared -o $(EX_DLL) -Wl,--out-implib=$(IM_DLL)$(SLIB_EXTENSION) $(OBJS_DLL) $(LIBS) $(INCS) $(LINKS)
@echo -n $(ARCH) > "./bin/exe/default/arch"
@echo -n $(CC) > "./bin/exe/default/cc"
@echo -n $(RENDERER) > "./bin/exe/default/renderer"
@echo "Setting defaults: $(ARCH).$(CC).$(RENDERER)"
$(EXT_EX_DLL): FLAGS += -DEXT_EXPORTS
$(EXT_EX_DLL): $(OBJS_EXT_DLL)
$(CXX) $(FLAGS) -shared -o $(EXT_EX_DLL) -Wl,--out-implib=$(EXT_IM_DLL)$(SLIB_EXTENSION) $(OBJS_EXT_DLL) $(EXT_LIBS) $(EXT_INCS) $(EXT_LINKS)
endif
ifneq ($(ARCH),dreamcast)
$(TARGET): $(OBJS)
$(CXX) $(FLAGS) $(OBJS) $(LIBS) $(INCS) $(LINKS) -l$(LIB_NAME) -l$(EXT_LIB_NAME) -o $(TARGET)
endif
@ -434,55 +111,20 @@ clean:
@-rm -f $(OBJS_EXT_DLL)
@-rm -f $(OBJS)
#@-rm $(shell find $(ENGINE_SRC_DIR) -name "*.$(PREFIX).o") $(shell find $(EXT_SRC_DIR) -name "*.$(PREFIX).o") $(shell find $(DEP_SRC_DIR) -name "*.$(PREFIX).o")
ifneq (,$(findstring dreamcast,$(ARCH)))
@-rm ./bin/dreamcast/build/*
@-rm ./bin/dreamcast/romdisk.*
@-rm ./bin/dreamcast/$(TARGET_NAME).*
endif
clean-shaders:
@-rm -f $(TARGET_SHADERS)
run:
ifneq (,$(findstring dreamcast,$(ARCH)))
$(KOS_EMU) ./bin/dreamcast/$(TARGET_NAME).cdi
else
@echo -n $(ARCH) > "./bin/exe/default/arch"
@echo -n $(CC) > "./bin/exe/default/cc"
@echo -n $(RENDERER) > "./bin/exe/default/renderer"
@echo "Setting defaults: $(ARCH).$(CC).$(RENDERER)"
./program.sh
endif
run-debug:
ifneq (,$(findstring dreamcast,$(ARCH)))
$(KOS_EMU_DEBUG) ./bin/dreamcast/$(TARGET_NAME).cdi
else
@echo -n $(ARCH) > "./bin/exe/default/arch"
@echo -n $(CC) > "./bin/exe/default/cc"
@echo -n $(RENDERER) > "./bin/exe/default/renderer"
@echo "Setting defaults: $(ARCH).$(CC).$(RENDERER)"
./debug.sh
endif
clean-zips:
@-find ./bin/data/ -name "*.gz" -type f -delete
clean-uf:
@-rm $(EX_DLL)
@-rm -f $(OBJS_DLL)
clean-exf:
@-rm $(EXT_EX_DLL)
@-rm -f $(OBJS_EXT_DLL)
clean-exe:
-rm $(EX_DLL)
-rm $(EXT_EX_DLL)
-rm $(TARGET)
-rm $(TARGET_SHADERS)
clean-shaders:
-rm $(TARGET_SHADERS)
backup:
@-rm $(shell find $(ENGINE_SRC_DIR) -name "*.o") $(shell find $(EXT_SRC_DIR) -name "*.o") $(shell find $(DEP_SRC_DIR) -name "*.o")

View File

@ -85,8 +85,8 @@ int main(int argc, char** argv){
signal(SIGSEGV, ::handlers::segv);
client::initialize();
ext::initialize();
uf::initialize();
ext::initialize();
// For Multithreaded initialization
while ( !client::ready || !uf::ready ) {

View File

@ -47,6 +47,7 @@ namespace uf {
extern UF_API pod::NamedTypes<pod::Instantiator>* objects;
// extern UF_API pod::NamedTypes<pod::Behavior>* behaviors;
extern UF_API uf::stl::unordered_map<uf::stl::string, pod::Behavior>* behaviors;
extern UF_API uf::stl::vector<uf::Entity*> queue;
uf::Entity* UF_API alloc( size_t );
template<typename T> T* alloc();
@ -66,6 +67,8 @@ namespace uf {
template<typename T> T& instantiate();
template<typename T> T* _instantiate();
void UF_API queueDeletion( uf::Entity& );
void UF_API bind( const uf::stl::string&, uf::Entity& );
template<typename T> void bind( uf::Entity& );

View File

@ -6,12 +6,15 @@
#include <uf/utils/memory/string.h>
#include <mutex>
#define UF_MEMORYPOOL_MUTEX 1
#define UF_MEMORYPOOL_INVALID_MALLOC 1
#define UF_MEMORYPOOL_INVALID_FREE 1
#define UF_MEMORYPOOL_CACHED_ALLOCATIONS 0
#define UF_MEMORYPOOL_STORE_ORPHANS 0
#define UF_MAX_BUCKETS 4096 // E.g., 16, 32, 64, 128, 256, 512, 1024, 2048 bytes
namespace pod {
struct UF_API Userdata;
@ -20,22 +23,52 @@ namespace pod {
uintptr_t pointer = 0;
};
struct UF_API MemoryPool {
typedef std::vector<pod::Allocation, uf::Mallocator<pod::Allocation>> allocations_t;
struct MemoryPool {
// in order of complexity
enum Strategy {
LINEAR,
POOL,
SEGREGATED,
BUDDY
};
void* memory = nullptr;
size_t size = 0;
void* memory = 0;
size_t used = 0;
Strategy strategy = Strategy::BUDDY;
union State {
// linear
struct { size_t offset; } linear;
// pool
struct { void* freeListHead; size_t fixedChunkSize; } pool;
// segregated
struct {
void* freeListHeads[UF_MAX_BUCKETS];
size_t offset; // To carve new memory when a bucket is empty
size_t minChunkSize; // Base size, e.g., 16 bytes
} segregated;
// buddy
struct {
void* freeLists[32]; // Max 32 levels for powers of two up to 4GB
uint8_t* splitBlockBitset; // Tracks if a block was split
size_t maxLevel;
size_t minBlockSize;
} buddy;
} state;
typedef std::vector<pod::Allocation, uf::Mallocator<pod::Allocation>> allocations_t;
#if UF_MEMORYPOOL_MUTEX
std::mutex mutex;
allocations_t allocations;
#if UF_MEMORYPOOL_CACHED_ALLOCATIONS
allocations_t cachedFreeAllocations;
#endif
#if UF_MEMORYPOOL_STORE_ORPHANS
allocations_t orphaned;
#endif
};
}
namespace uf {
@ -46,7 +79,7 @@ namespace uf {
size_t UF_API size( const pod::MemoryPool& );
size_t UF_API allocated( const pod::MemoryPool& );
uf::stl::string UF_API stats( const pod::MemoryPool& );
void UF_API initialize( pod::MemoryPool&, size_t );
void UF_API initialize( pod::MemoryPool&, size_t, pod::MemoryPool::Strategy = pod::MemoryPool::Strategy::BUDDY, size_t = 0 );
void UF_API destroy( pod::MemoryPool& );
// pod::Allocation UF_API allocate( pod::MemoryPool&, void*, size_t, size_t alignment = uf::memoryPool::alignment );
@ -79,7 +112,7 @@ namespace uf {
inline size_t size() const;
inline size_t allocated() const;
inline uf::stl::string stats() const;
inline void initialize( size_t size );
inline void initialize( size_t size, pod::MemoryPool::Strategy = pod::MemoryPool::Strategy::BUDDY, size_t = 0 );
inline void destroy();
// inline pod::Allocation allocate( void* data, size_t size/*, size_t alignment = uf::memoryPool::alignment*/ );
@ -89,7 +122,7 @@ namespace uf {
inline void* alloc( size_t size/*, size_t alignment = uf::memoryPool::alignment*/ );
inline pod::Allocation& fetch( void* data, size_t size = 0 );
inline bool exists( void* data, size_t size = 0 );
inline bool free( void* data, size_t size = 0 );
inline bool free( void* data, size_t size );
inline const pod::MemoryPool::allocations_t& allocations() const;
inline pod::MemoryPool& data();

View File

@ -27,13 +27,23 @@ bool uf::memoryPool::exists( pod::MemoryPool& pool, const T& data ) {
}
template<typename T>
bool uf::memoryPool::free( pod::MemoryPool& pool, const T& data ) {
return std::is_pointer<T>::value ? uf::memoryPool::free( pool, (void*) data ) : uf::memoryPool::free( pool, (void*) &data, sizeof(data) );
#if __cplusplus >= 201703L
if constexpr (std::is_pointer_v<T>) {
return uf::memoryPool::free( pool, (void*) data, sizeof(std::remove_pointer_t<T>) );
} else {
return uf::memoryPool::free( pool, (void*) &data, sizeof(T) );
}
#else
return std::is_pointer<T>::value
? uf::memoryPool::free( pool, (void*) data, sizeof(typename std::remove_pointer<T>::type) )
: uf::memoryPool::free( pool, (void*) &data, sizeof(T) );
#endif
}
size_t uf::MemoryPool::size() const { return uf::memoryPool::size( m_pod ); }
size_t uf::MemoryPool::allocated() const { return uf::memoryPool::allocated( m_pod ); }
uf::stl::string uf::MemoryPool::stats() const { return uf::memoryPool::stats( m_pod ); }
void uf::MemoryPool::initialize( size_t size ) { return uf::memoryPool::initialize( m_pod, size ); }
void uf::MemoryPool::initialize( size_t size, pod::MemoryPool::Strategy strategy, size_t chunkSize ) { return uf::memoryPool::initialize( m_pod, size, strategy, chunkSize ); }
void uf::MemoryPool::destroy() { return uf::memoryPool::destroy( m_pod ); }
//pod::Allocation uf::MemoryPool::allocate( void* data, size_t size/*, size_t alignment*/ ) { return uf::memoryPool::allocate( m_pod, data, size/*, alignment*/ ); }

View File

@ -425,7 +425,7 @@ void UF_API uf::initialize() {
{
size_t size = deduceSize( configMemoryPoolJson["pools"]["entity"] );
UF_MSG_DEBUG("Requesting {} bytes for entity memory pool: {}", (int) size, (void*) &uf::Entity::memoryPool);
uf::Entity::memoryPool.initialize( size );
uf::Entity::memoryPool.initialize( size, pod::MemoryPool::Strategy::POOL, sizeof(uf::Entity) );
}
}
uf::allocator::override = configMemoryPoolJson["override"].as( uf::allocator::override );

View File

@ -4,12 +4,18 @@
#include <uf/engine/scene/scene.h>
#include <assert.h>
namespace {
std::mutex queueMutex;
}
pod::NamedTypes<pod::Instantiator>* uf::instantiator::objects = NULL;
//pod::NamedTypes<pod::Behavior>* uf::instantiator::behaviors = NULL;
uf::stl::unordered_map<uf::stl::string, pod::Behavior>* uf::instantiator::behaviors = NULL;
uf::stl::vector<uf::Entity*> uf::instantiator::queue;
uf::Entity* uf::instantiator::reuse( size_t size ) {
uf::Entity* laxed = NULL;
/*
auto& allocations = uf::Entity::memoryPool.allocations();
for ( auto& allocation : allocations ) {
uf::Entity* e = (uf::Entity*) (allocation.pointer);
@ -31,14 +37,16 @@ uf::Entity* uf::instantiator::reuse( size_t size ) {
if ( allocation.size == size ) return e;
if ( allocation.size > size ) laxed = e;
}
*/
return laxed;
}
size_t uf::instantiator::collect( uint8_t level ) {
/*
uf::stl::vector<uf::Entity*> queued;
size_t collected = 0;
auto& allocations = uf::Entity::memoryPool.allocations();
auto& scene = uf::scene::getCurrentScene();
uf::stl::vector<uf::Entity*> queued;
for ( auto& allocation : allocations ) {
uf::Entity* e = (uf::Entity*) (allocation.pointer);
// no scenes
@ -58,6 +66,12 @@ size_t uf::instantiator::collect( uint8_t level ) {
// UF_MSG_DEBUG("Freeing: {}", uf::string::toString(e->as<uf::Object>()));
queued.emplace_back( (uf::Entity*) allocation.pointer );
}
*/
// mutex
::queueMutex.lock();
uf::stl::vector<uf::Entity*> queued = std::move( uf::instantiator::queue );
::queueMutex.unlock();
for ( auto& p : queued ) {
if ( p->hasComponent<uf::SceneBehavior::Metadata>() ) continue;
// UF_MSG_DEBUG("Destroying: {}", uf::string::toString(*p));
@ -68,6 +82,7 @@ size_t uf::instantiator::collect( uint8_t level ) {
// UF_MSG_DEBUG("Destroying scene: {}", uf::string::toString(*p));
uf::instantiator::free( p );
}
return queued.size();
}
@ -148,6 +163,12 @@ uf::Entity& uf::instantiator::instantiate( const uf::stl::string& name ) {
return entity;
}
void UF_API uf::instantiator::queueDeletion( uf::Entity& entity ) {
::queueMutex.lock();
uf::instantiator::queue.emplace_back(&entity);
::queueMutex.unlock();
}
void uf::instantiator::bind( const uf::stl::string& name, uf::Entity& entity ) {
// was actually a behavior name, single bind
if ( !uf::instantiator::objects->has( name, false ) ) {

View File

@ -41,7 +41,10 @@ void uf::Object::queueDeletion() {
if ( this->hasParent() ) this->getParent().removeChild(*this);
auto& metadata = this->getComponent<uf::ObjectBehavior::Metadata>();
// mark for destruction
metadata.system.markedForDeletion = true;
uf::instantiator::queueDeletion( *this );
}
uf::Hooks::return_t uf::Object::callHook( const uf::stl::string& name ) {

View File

@ -252,6 +252,8 @@ namespace {
}
}
UF_ASSERT( !bounds.empty() );
// recursively build BVH from indices
if ( uf::physics::impl::settings.useBvhSahMeshes ) ::buildBVHNode_SAH( bvh, bounds, 0, bvh.indices.size(), capacity );
else ::buildBVHNode( bvh, bounds, 0, bvh.indices.size(), capacity );

View File

@ -146,7 +146,11 @@ void uf::physics::impl::step( pod::World& world, float dt ) {
// bodies with meshes already reorient the normal to the triangle's center
// do not do it for meshes because it'll reorient to the mesh's origin
if ( a.collider.type != pod::ShapeType::MESH && b.collider.type != pod::ShapeType::MESH ) {
// do not do it for planes
bool shouldReorient = true;
if ( a.collider.type == pod::ShapeType::MESH || b.collider.type == pod::ShapeType::MESH ) shouldReorient = false;
if ( a.collider.type == pod::ShapeType::PLANE || b.collider.type == pod::ShapeType::PLANE ) shouldReorient = false;
if ( shouldReorient ) {
for ( auto& c : manifold.points ) c.normal = ::orientNormalToAB( a, b, c.normal );
}
// retrieve accumulated impulses

View File

@ -32,7 +32,7 @@ namespace {
b.velocity += impulse * b.inverseMass;
//b.angularVelocity += (uf::vector::cross(rB, impulse)) * b.inverseInertiaTensor;
pod::Matrix3f invIb = computeWorldInverseInertia( b );
a.angularVelocity += uf::matrix::multiply( invIb, uf::vector::cross(rB, impulse) );
b.angularVelocity += uf::matrix::multiply( invIb, uf::vector::cross(rB, impulse) );
}
}

View File

@ -22,6 +22,7 @@ namespace {
//0, 1, 2, 3, 4, 5
0, 2, 1, 3, 5, 4
});
mesh.updateDescriptor();
return mesh;
}
@ -75,7 +76,7 @@ TEST(RaySphere_Hit, {
body.transform->position = {0,0,0};
body.bounds = ::computeAABB( body );
::buildBroadphaseBVH( world.bvh, world.bodies );
::buildBroadphaseBVH( world.dynamicBvh, world.bodies );
pod::Ray ray{ {0,0,-5}, uf::vector::normalize(pod::Vector3f{0,0,1}) };
pod::RayQuery hit = uf::physics::impl::rayCast(ray, world, 100.0f);
@ -183,7 +184,7 @@ TEST(RayAabb_Miss, {
box.transform->position = {0,0,0};
box.bounds = ::computeAABB( box );
::buildBroadphaseBVH( world.bvh, world.bodies );
::buildBroadphaseBVH( world.dynamicBvh, world.bodies );
pod::Ray ray{{5,5,5}, uf::vector::normalize(pod::Vector3f{1,0,0})};
auto hit = uf::physics::impl::rayCast(ray, world, 100.0f);
@ -320,7 +321,7 @@ TEST(RaySphere_OriginInside, {
body.transform->position = {0,0,0};
body.bounds = ::computeAABB( body );
::buildBroadphaseBVH( world.bvh, world.bodies );
::buildBroadphaseBVH( world.dynamicBvh, world.bodies );
pod::Ray ray{ {0,0,0}, {1,0,0} }; // starts inside
auto q = uf::physics::impl::rayCast(ray, world, 100.0f);
@ -857,7 +858,7 @@ TEST(RayMesh_Hit, {
auto& bodyA = uf::physics::impl::create(world, objMesh, mesh, 0.0f);
bodyA.transform->position = {0,0,0};
::buildBroadphaseBVH( world.bvh, world.bodies );
::buildBroadphaseBVH( world.dynamicBvh, world.bodies );
pod::Ray ray{ {0,1,0}, {0,-1,0} }; // from above, pointing down
pod::RayQuery hit = uf::physics::impl::rayCast(ray, world, 100.0f);
@ -874,7 +875,7 @@ TEST(RayMesh_Miss, {
auto& bodyA = uf::physics::impl::create(world, objMesh, mesh, 0.0f);
bodyA.transform->position = {0,0,0};
::buildBroadphaseBVH( world.bvh, world.bodies );
::buildBroadphaseBVH( world.dynamicBvh, world.bodies );
pod::Ray ray{ {0,2,0}, {1,0,0} }; // parallel, goes sideways
pod::RayQuery hit = uf::physics::impl::rayCast(ray, world, 100.0f);

View File

@ -10,63 +10,124 @@
#include <uf/utils/thread/perthread.h>
#include <uf/utils/io/iostream.h>
#define UF_MEMORYPOOL_MUTEX 1
#define UF_MEMORYPOOL_FETCH_STL_FIND 1
#define UF_MEMORYPOOL_LAZY 0
#define UF_MEMORYPOOL_TEST 0
#define UF_MEMORYPOOL_CACHED_ALLOCATIONS 0
#define UF_MEMORYPOOL_STORE_ORPHANS 0
#define UF_MEMORYPOOL_INVALID_FREE 0
namespace {
inline size_t getBucketIndex( size_t size, size_t currentSize, size_t levels = UF_MAX_BUCKETS - 1 ) {
size_t index = 0;
while ( currentSize < size && index < levels ) {
currentSize <<= 1;
index++;
}
return index;
}
inline size_t getTargetLevel( size_t size, size_t& currentSize, size_t levels = UF_MAX_BUCKETS - 1 ) {
size_t index = 0;
while ( currentSize < size && index < levels ) {
currentSize <<= 1;
index++;
}
return index;
}
#define DEBUG_PRINT 0
#if DEBUG_PRINT
#define UF_MSG_CONDITIONAL_PRINT(...) UF_MSG_DEBUG(__VA_ARGS__)
#else
#define UF_MSG_CONDITIONAL_PRINT(...)
#endif
inline void* getBuddy(void* block, size_t blockSize, void* poolStart) {
uintptr_t offset = (uintptr_t)block - (uintptr_t) poolStart; // offset from start of the pool
uintptr_t buddyOffset = offset ^ blockSize; // XOR the offset with the block size to find the buddy's offset
return (void*)((uintptr_t)poolStart + buddyOffset);
}
inline size_t getTreeNodeIndex(size_t offset, size_t level, size_t maxLevel, size_t minChunkSize) {
size_t indexInLevel = offset / (minChunkSize << level);
size_t firstIndexOfLevel = (1 << (maxLevel - level)) - 1;
return firstIndexOfLevel + indexInLevel;
}
inline void setBit(uint8_t* bitset, size_t index) {
bitset[index / 8] |= (1 << (index % 8));
}
inline void clearBit(uint8_t* bitset, size_t index) {
bitset[index / 8] &= ~(1 << (index % 8));
}
inline bool getBit(uint8_t* bitset, size_t index) {
return (bitset[index / 8] & (1 << (index % 8))) != 0;
}
}
bool uf::memoryPool::subPool = true;
uint8_t uf::memoryPool::alignment = 64;
uf::MemoryPool uf::memoryPool::global;
size_t uf::memoryPool::size( const pod::MemoryPool& pool ) {
return pool.size;
}
size_t uf::memoryPool::allocated( const pod::MemoryPool& pool ) {
size_t allocated = 0;
for ( auto& allocation : pool.allocations ) allocated += allocation.size;
return allocated;
}
uf::stl::string uf::memoryPool::stats( const pod::MemoryPool& pool ) {
uf::Serializer metadata;
size_t size = uf::memoryPool::size( pool );
size_t allocated = uf::memoryPool::allocated( pool );
uf::stl::stringstream ss; ss << std::hex << (void*) pool.memory;
metadata["size"] = size;
metadata["used"] = allocated;
metadata["free"] = size - allocated;
metadata["objects"] = pool.allocations.size();
metadata["pool"] = ss.str();
return metadata;
}
void uf::memoryPool::initialize( pod::MemoryPool& pool, size_t size ) {
void uf::memoryPool::initialize( pod::MemoryPool& pool, size_t size, pod::MemoryPool::Strategy strategy, size_t chunkSize ) {
if ( size <= 0 ) return;
if ( uf::memoryPool::size( pool ) > 0 ) uf::memoryPool::destroy( pool );
pool.allocations.reserve(64);
if ( uf::memoryPool::subPool && uf::memoryPool::global.size() > 0 && &pool != &uf::memoryPool::global.data() ) {
pool.memory = uf::memoryPool::global.alloc( size );
} else {
pool.memory = uf::allocator::malloc_m( size );
}
UF_ASSERT( pool.memory );
memset( pool.memory, 0, size );
pool.size = size;
pool.strategy = strategy;
pool.memory = uf::allocator::malloc_m(size);
UF_ASSERT( pool.memory );
switch ( pool.strategy ) {
case pod::MemoryPool::Strategy::LINEAR: {
pool.state.linear.offset = 0;
break;
}
case pod::MemoryPool::Strategy::POOL: {
UF_ASSERT(chunkSize > 0 && chunkSize >= sizeof(void*));
pool.state.pool.fixedChunkSize = chunkSize;
pool.state.pool.freeListHead = pool.memory;
size_t numChunks = size / chunkSize;
uint8_t* ptr = static_cast<uint8_t*>(pool.memory);
for (size_t i = 0; i < numChunks - 1; ++i) {
void** currentChunk = reinterpret_cast<void**>(ptr + (i * chunkSize));
*currentChunk = ptr + ((i + 1) * chunkSize);
}
void** lastChunk = reinterpret_cast<void**>(ptr + ((numChunks - 1) * chunkSize));
*lastChunk = nullptr;
break;
}
case pod::MemoryPool::Strategy::SEGREGATED: {
UF_ASSERT(chunkSize >= sizeof(void*));
pool.state.segregated.minChunkSize = chunkSize;
pool.state.segregated.offset = 0;
for (int i = 0; i < UF_MAX_BUCKETS; ++i) {
pool.state.segregated.freeListHeads[i] = nullptr;
}
break;
}
case pod::MemoryPool::Strategy::BUDDY: {
// to-do: pick a better fallback
if ( !chunkSize ) chunkSize = 128;
UF_ASSERT(chunkSize >= sizeof(void*));
UF_ASSERT((size & (size - 1)) == 0); // is power of 2
pool.state.buddy.minBlockSize = chunkSize;
for (int i = 0; i < 32; ++i) pool.state.buddy.freeLists[i] = nullptr;
// calculate max level
size_t s = size;
size_t maxLevel = 0;
while ( s > chunkSize ) { s >>= 1; maxLevel++; }
pool.state.buddy.maxLevel = maxLevel;
pool.state.buddy.freeLists[maxLevel] = pool.memory; // set highest level as one free block
// allocate a small bitset to track splits
// to-do: make it at the end of the allocation instead
size_t totalNodes = (1 << (maxLevel + 1)) - 1;
size_t bitsetSize = (totalNodes + 7) / 8; // round up to nearest byte
pool.state.buddy.splitBlockBitset = (uint8_t*) uf::allocator::malloc_m(bitsetSize);
memset(pool.state.buddy.splitBlockBitset, 0, bitsetSize);
break;
}
default: {
UF_EXCEPTION("invalid strategy: {}", pool.strategy);
break;
}
}
}
void uf::memoryPool::destroy( pod::MemoryPool& pool ) {
if ( uf::memoryPool::size( pool ) <= 0 ) goto CLEAR;
@ -75,262 +136,295 @@ void uf::memoryPool::destroy( pod::MemoryPool& pool ) {
} else {
uf::allocator::free_m(pool.memory);
}
// per-pool destruction
switch ( pool.strategy ) {
case pod::MemoryPool::Strategy::BUDDY: {
uf::allocator::free_m(pool.state.buddy.splitBlockBitset);
pool.state.buddy.splitBlockBitset = NULL;
break;
}
}
CLEAR:
pool.size = 0;
pool.used = 0;
pool.memory = NULL;
}
pod::Allocation uf::memoryPool::allocate( pod::MemoryPool& pool, size_t size, size_t alignment ) {
// alignment = MIN( alignment, uf::memoryPool::alignment );
// alignment = uf::memoryPool::alignment;
pod::Allocation alloc;
#if UF_MEMORYPOOL_MUTEX
pool.mutex.lock();
std::lock_guard<std::mutex> lock(pool.mutex);
#endif
// find next available allocation
pod::Allocation allocation;
uintptr_t pointer = (uintptr_t) pool.memory;
// realign pointer
if ( 0 < alignment ) {
uintptr_t a = uf::alignment( (void*) pointer, alignment );
pointer += a == 0 ? 0 : alignment - a;
}
switch ( pool.strategy ) {
case pod::MemoryPool::Strategy::LINEAR: {
// get next free space
uintptr_t currentPtr = (uintptr_t) pool.memory + pool.state.linear.offset;
size_t len = uf::memoryPool::size( pool );
size_t padding = 0;
// pool not initialized
if ( len <= 0 ) {
UF_MSG_CONDITIONAL_PRINT("cannot malloc, pool not initialized: {} bytes", size);
goto MANUAL_MALLOC;
}
#if UF_MEMORYPOOL_CACHED_ALLOCATIONS
// an optimization by quickly reusing freed allocations seemed like a good idea
// but still have to iterate through allocation information
// to keep allocation information in order
// check our cache of first
if ( !pool.cachedFreeAllocations.empty() ) {
auto it = pool.cachedFreeAllocations.begin();
// check if any recently freed allocation is big enough for our new allocation
for ( ; it != pool.cachedFreeAllocations.end(); ++it ) {
// is aligned
if ( 0 < alignment && !uf::aligned( (void*) it->pointer, alignment ) ) continue;
// sized adequately
if ( it->size < size ) break;
}
// found a suitable allocation, use it
if ( it != pool.cachedFreeAllocations.end() ) {
pointer = it->pointer;
// find where to insert in our allocation table
auto next = pool.allocations.begin();
while ( next != pool.allocations.end() ) {
if ( pointer + size + padding < next->pointer ) break;
++next;
// realign
size_t padding = 0;
if ( alignment > 0 ) {
uintptr_t a = uf::alignment((void*) currentPtr, alignment);
padding = a == 0 ? 0 : alignment - a;
}
// check if it was actually valid
if ( next != pool.allocations.end() ) {
// remove from cache
pool.cachedFreeAllocations.erase(it);
// initialize allocation info
void* p = (void*) pointer;
allocation.size = size;
allocation.pointer = pointer;
// security
// if ( data ) memcpy( p, data, size );
// else memset( p, 0, size );
memset( p, 0, size );
// oom
if ( pool.state.linear.offset + padding + size > pool.size ) {
goto MANUAL_MALLOC;
}
// register as allocated
pool.allocations.insert(next, allocation);
// allocate
alloc.pointer = currentPtr + padding;
alloc.size = size;
// move offset forward
pool.state.linear.offset += padding + size;
goto RETURN;
}
case pod::MemoryPool::Strategy::POOL: {
// oom
if ( size > pool.state.pool.fixedChunkSize || pool.state.pool.freeListHead == nullptr ) {
goto MANUAL_MALLOC;
}
// allocate
alloc.pointer = (uintptr_t) pool.state.pool.freeListHead;
alloc.size = pool.state.pool.fixedChunkSize;
// move head to next free spot
pool.state.pool.freeListHead = *reinterpret_cast<void**>(pool.state.pool.freeListHead); // ???
goto RETURN;
}
case pod::MemoryPool::Strategy::SEGREGATED: {
// find bucket level
size_t bucketIdx = getBucketIndex(size, pool.state.segregated.minChunkSize);
size_t bucketSize = pool.state.segregated.minChunkSize << bucketIdx;
// search within the free list first
if ( pool.state.segregated.freeListHeads[bucketIdx] != nullptr ) {
alloc.pointer = (uintptr_t)pool.state.segregated.freeListHeads[bucketIdx];
alloc.size = bucketSize;
pool.state.segregated.freeListHeads[bucketIdx] = *reinterpret_cast<void**>(alloc.pointer);
goto RETURN;
}
}
}
#endif
{
// find any availble spots in-between existing allocations
auto next = pool.allocations.begin();
// no allocations;
if ( pool.allocations.empty() ) {
// beginning is big enough to fit
} else if ( pointer + size < next->pointer ) {
} else {
for ( auto it = next; it != pool.allocations.end(); ++it ) {
// ignore invalid indexes
if ( pool.size == 0 ) continue;
// point to end of allocation we're looking at
pointer = it->pointer + it->size + padding;
// realign our index if requested
if ( 0 < alignment ) {
uintptr_t a = uf::alignment( (void*) pointer, alignment );
pointer += a == 0 ? 0 : alignment - a;
}
// hit the end of our allocations, break
if ( ++next == pool.allocations.end() ) break;
// target index is behind next allocated space, use it
if ( pointer + size < next->pointer ) break;
// oom
if (pool.state.segregated.offset + bucketSize > pool.size) {
goto MANUAL_MALLOC;
}
}
// no allocation found, OOM
if ( (uintptr_t) pool.memory + len <= pointer + size + padding ) {
UF_MSG_ERROR("MemoryPool: {}: out of memory", (void*) &pool);
UF_MSG_ERROR("Trying to request {} bytes of memory", size);
UF_MSG_ERROR("Stats: {}", uf::memoryPool::stats( pool ));
goto MANUAL_MALLOC;
}
// initialize allocation info
void* p = (void*) pointer;
allocation.size = size;
allocation.pointer = pointer;
// security
// if ( data ) memcpy( p, data, size );
// else memset( p, 0, size );
memset( p, 0, size );
// allocate
alloc.pointer = (uintptr_t) pool.memory + pool.state.segregated.offset;
alloc.size = bucketSize;
// move offset to next free spot
pool.state.segregated.offset += bucketSize;
goto RETURN;
}
case pod::MemoryPool::Strategy::BUDDY: {
// realign
size_t requestedSize = size;
if ( alignment > 0 && size < alignment ) requestedSize = alignment;
// overrides if we're overloading global new/delete
// register as allocated
pool.allocations.insert(next, allocation);
// find target level
size_t currentSize = pool.state.buddy.minBlockSize;
size_t targetLevel = getTargetLevel(requestedSize, currentSize, pool.state.buddy.maxLevel);
// find a free block at target level or higher
size_t allocLevel = targetLevel;
while ( allocLevel <= pool.state.buddy.maxLevel && pool.state.buddy.freeLists[allocLevel] == nullptr ) {
allocLevel++;
}
// oom
if ( allocLevel > pool.state.buddy.maxLevel ) {
goto MANUAL_MALLOC;
}
// pop the block from the higher level
void* block = pool.state.buddy.freeLists[allocLevel];
pool.state.buddy.freeLists[allocLevel] = *reinterpret_cast<void**>(block);
// split downwards to the target level
while (allocLevel > targetLevel) {
// mark the current block as split before dropping down
size_t offset = (uintptr_t) block - (uintptr_t) pool.memory;
size_t nodeIndex = getTreeNodeIndex( offset, allocLevel, pool.state.buddy.maxLevel, pool.state.buddy.minBlockSize );
setBit(pool.state.buddy.splitBlockBitset, nodeIndex);
allocLevel--;
size_t halfSize = pool.state.buddy.minBlockSize << allocLevel;
void* buddy = (void*) ((uintptr_t) block + halfSize); // right half
// push buddy to the free list of this lower level
*reinterpret_cast<void**>(buddy) = pool.state.buddy.freeLists[allocLevel];
pool.state.buddy.freeLists[allocLevel] = buddy;
}
// allocate
alloc.pointer = (uintptr_t) block;
alloc.size = currentSize;
goto RETURN;
}
default: {
UF_EXCEPTION("invalid strategy: {}", pool.strategy);
break;
}
}
goto RETURN;
MANUAL_MALLOC:
#if UF_MEMORYPOOL_INVALID_MALLOC
allocation.size = 0;
allocation.pointer = (uintptr_t) uf::allocator::malloc_m(size);
alloc.size = 0; // orphaned
alloc.pointer = (uintptr_t) uf::allocator::malloc_m(size);
UF_MSG_DEBUG("allocating orphan: {} bytes at {}", size, (void*) alloc.pointer);
#if UF_MEMORYPOOL_STORE_ORPHANS
pool.orphaned.emplace_back(allocation);
pool.orphaned.emplace_back( alloc );
#endif
#else
UF_EXCEPTION("invalid malloc");
UF_EXCEPTION("cannot malloc: {}", size );
#endif
RETURN:
pool.used += alloc.size;
UF_ASSERT(alloc.pointer);
return alloc;
}
bool uf::memoryPool::free( pod::MemoryPool& pool, void* pointer, size_t size ) {
if (!pointer) return false;
#if UF_MEMORYPOOL_MUTEX
pool.mutex.unlock();
std::lock_guard<std::mutex> lock(pool.mutex);
#endif
// UF_MSG_CONDITIONAL_PRINT((uintptr_t) allocation.pointer - (uintptr_t) pool.memory << " -> " << (uintptr_t) allocation.pointer + allocation.size - (uintptr_t) pool.memory - 1 );
UF_ASSERT(allocation.pointer);
return allocation;
bool oob = !exists( pool, pointer, size );
if ( oob ) goto MANUAL_FREE;
switch (pool.strategy) {
case pod::MemoryPool::Strategy::LINEAR: {
UF_EXCEPTION("cannot free individual allocation");
return false;
}
case pod::MemoryPool::Strategy::POOL: {
void** chunk = reinterpret_cast<void**>(pointer);
*chunk = pool.state.pool.freeListHead; // point freed chunk to current head
pool.state.pool.freeListHead = pointer; // freed chunk is now the new head
goto RETURN;
}
case pod::MemoryPool::Strategy::SEGREGATED: {
UF_ASSERT(size > 0);
size_t bucketIdx = getBucketIndex(size, pool.state.segregated.minChunkSize);
void** chunk = reinterpret_cast<void**>(pointer);
*chunk = pool.state.segregated.freeListHeads[bucketIdx]; // point freed chunk to current head
pool.state.segregated.freeListHeads[bucketIdx] = pointer; // freed chunk is now the new head
goto RETURN;
}
case pod::MemoryPool::Strategy::BUDDY: {
UF_ASSERT(size > 0);
void* block = pointer;
// attempt to merge with buddies
size_t currentSize = pool.state.buddy.minBlockSize;
size_t level = getTargetLevel(size, currentSize, pool.state.buddy.maxLevel);
while (level < pool.state.buddy.maxLevel) {
void* buddy = getBuddy(block, currentSize, pool.memory);
// search for buddy in the current level's free list
void** current = &pool.state.buddy.freeLists[level];
bool buddyIsFree = false;
while ( *current != nullptr ) {
// buddy is free, remove from free list
if ( *current == buddy ) {
*current = *reinterpret_cast<void**>(buddy);
buddyIsFree = true;
break;
}
current = reinterpret_cast<void**>(*current);
}
// buddy is allocated or split, stop merging
if ( !buddyIsFree ) break;
// merge; new block pointer is the minimum of the two
block = (block < buddy) ? block : buddy;
currentSize <<= 1;
level++;
}
// push final block onto the appropriate free list
*reinterpret_cast<void**>(block) = pool.state.buddy.freeLists[level];
pool.state.buddy.freeLists[level] = block;
goto RETURN;
}
default: {
UF_EXCEPTION("invalid strategy: {}", pool.strategy);
break;
}
}
MANUAL_FREE:
#if UF_MEMORYPOOL_STORE_ORPHANS
if ( oob ) {
auto it = pool.orphaned.begin();
for ( ; it != pool.orphaned.end(); ++it ) {
if ( (uintptr_t) pointer == it->pointer && ((size > 0 && it->size == size) || (size == 0)) ) break;
}
if ( it != pool.orphaned.end() ) {
UF_MSG_DEBUG("manually freeing orphan {}", pointer);
uf::allocator::free_m( pointer );
pool.orphaned.erase(it);
return true;
}
}
#endif
#if UF_MEMORYPOOL_INVALID_FREE
UF_MSG_DEBUG("manually freeing {}", pointer);
uf::allocator::free_m(pointer);
return true;
#else
UF_EXCEPTION("cannot free: {}", pointer);
return false;
#endif
RETURN:
pool.used -= size;
return true;
}
size_t uf::memoryPool::size( const pod::MemoryPool& pool ) {
return pool.size;
}
void* uf::memoryPool::alloc( pod::MemoryPool& pool, size_t size, size_t alignment ) {
auto allocation = uf::memoryPool::allocate( pool, size, alignment );
return (void*) allocation.pointer;
}
pod::Allocation& uf::memoryPool::fetch( pod::MemoryPool& pool, void* pointer, size_t size ) {
static pod::Allocation missing;
#if UF_MEMORYPOOL_FETCH_STL_FIND
auto it = std::find_if( pool.allocations.begin(), pool.allocations.end(), [pointer, size]( const pod::Allocation& a ){
return (uintptr_t) pointer == a.pointer && ((size > 0 && a.size == size) || (size == 0));
});
return it != pool.allocations.end() ? *it : missing;
#else
for ( auto& allocation : pool.allocations ) {
if ( (uintptr_t) pointer == allocation.pointer && ((size > 0 && allocation.size == size) || (size == 0)) ) return allocation;
}
return missing;
#endif
}
bool uf::memoryPool::exists( pod::MemoryPool& pool, void* pointer, size_t size ) {
// bound check
#if UF_MEMORYPOOL_LAZY
// if pointer lies before the start of the pool, or if it lies after the end of the pool
return pool.memory <= pointer && pointer < (void*) ((uintptr_t) pool.memory + pool.size);
#else
if ( !(pool.memory <= pointer && pointer < (void*) ((uintptr_t) pool.memory + pool.size)) ) return false;
auto& allocation = uf::memoryPool::fetch( pool, pointer, size );
return allocation.pointer == (uintptr_t) pointer && ((size > 0 && allocation.size == size) || (size == 0));
#endif
}
bool uf::memoryPool::free( pod::MemoryPool& pool, void* pointer, size_t size ) {
// skip freeing, we're already a deallocated pool
// this comes up because of how backasswards C++ static initialization/destruction order is
if ( !pool.memory ) return false;
// passed a NULL, for some reason
if ( !pointer ) return false;
#if UF_MEMORYPOOL_MUTEX
pool.mutex.lock();
#endif
bool oob = pool.size <= 0 && !(pool.memory <= pointer && pointer < (void*) ((uintptr_t) pool.memory + pool.size));
#if UF_MEMORYPOOL_INVALID_MALLOC && UF_MEMORYPOOL_STORE_ORPHANS
// if pointer is out of bounds
if ( oob ) {
// check if our pointer was an orphaned one
#if UF_MEMORYPOOL_FETCH_STL_FIND
auto it = std::find_if( pool.orphaned.begin(), pool.orphaned.end(), [pointer, size]( const pod::Allocation& a ){
return (uintptr_t) pointer == a.pointer && ((size > 0 && a.size == size) || (size == 0));
});
#else
auto it = pool.orphaned.begin();
for ( ; it != pool.orphaned.end(); ++it ) if ( pointer == it->pointer && ((size > 0 && it->size == size) || (size == 0)) ) break;
#endif
// orphaned pointer, just free it
if ( it != pool.orphaned.end() ) {
uf::allocator::free_m( pointer );
pool.orphaned.erase(it);
}
#if UF_MEMORYPOOL_MUTEX
pool.mutex.unlock();
#endif
return true;
}
#endif
// fail if uninitialized or pointer is outside of our pool
if ( oob ) {
// UF_MSG_CONDITIONAL_PRINT("cannot free: " << pointer << " | " << (pool.size <= 0) << " " << (pointer < pool.memory) << " " << (pointer >= (void*) ((uintptr_t) pool.memory + pool.size)));
goto MANUAL_FREE;
}
{
// pointer arithmatic
auto it = pool.allocations.begin();
pod::Allocation allocation{};
// find our allocation in the allocation pool
for ( ; it != pool.allocations.end(); ++it ) {
if ( it->pointer != (uintptr_t) pointer ) continue;
allocation = *it;
break;
}
// pointer isn't actually allocated
if ( allocation.pointer != (uintptr_t) pointer ) {
UF_MSG_ERROR("cannot free, allocation not found: {}", pointer);
goto MANUAL_FREE;
}
// size validation mismatch, do not free
if (0 < size && allocation.size != size) {
UF_MSG_ERROR("cannot free, mismatched sized: {} ({} != {})", pointer, size, allocation.size);
goto MANUAL_FREE;
}
// UF_MSG_CONDITIONAL_PRINT(" " << (uintptr_t) allocation.pointer - (uintptr_t) pool.memory << " -> " << (uintptr_t) allocation.pointer + allocation.size - (uintptr_t) pool.memory - 1 );
// security
memset( pointer, 0, size );
// remove from our allocation table...
pool.allocations.erase(it);
#if UF_MEMORYPOOL_CACHED_ALLOCATIONS
// ...but add it to our freed allocation cache
pool.cachedFreeAllocations.push_back(allocation);
#endif
#if UF_MEMORYPOOL_MUTEX
pool.mutex.unlock();
#endif
return true;
}
MANUAL_FREE:
#if UF_MEMORYPOOL_MUTEX
pool.mutex.unlock();
#endif
#if UF_MEMORYPOOL_INVALID_FREE
uf::allocator::free_m(pointer);
#endif
return false;
}
size_t uf::memoryPool::allocated( const pod::MemoryPool& pool ) {
return pool.used;
}
uf::stl::string uf::memoryPool::stats( const pod::MemoryPool& pool ) {
uf::Serializer metadata;
metadata["size"] = pool.size;
metadata["used"] = pool.used;
metadata["free"] = pool.size - pool.used;
metadata["strategy"] = (int) pool.strategy;
uf::stl::stringstream ss; ss << std::hex << (void*) pool.memory;
metadata["pool"] = ss.str();
return metadata;
}
pod::Allocation& uf::memoryPool::fetch( pod::MemoryPool& pool, void* pointer, size_t size ) {
UF_EXCEPTION("unimplemented");
}
const pod::MemoryPool::allocations_t& uf::memoryPool::allocations( const pod::MemoryPool& pool ) {
return pool.allocations;
UF_EXCEPTION("unimplemented")
}
//
uf::MemoryPool::MemoryPool( size_t size ) {
@ -338,4 +432,8 @@ uf::MemoryPool::MemoryPool( size_t size ) {
}
uf::MemoryPool::~MemoryPool( ) {
this->destroy();
}
}
#if UF_MEMORYPOOL_TEST
#include "tests.inl"
#endif

View File

@ -0,0 +1,82 @@
#include <uf/utils/tests/tests.h>
TEST(MemoryPool_LinearStrategy, {
pod::MemoryPool pool;
uf::memoryPool::initialize(pool, 1024, pod::MemoryPool::Strategy::LINEAR, 0);
auto alloc1 = uf::memoryPool::allocate(pool, 100, 0);
EXPECT_TRUE(alloc1.pointer != 0);
EXPECT_EQ(alloc1.size, 100);
auto alloc2 = uf::memoryPool::allocate(pool, 200, 0);
EXPECT_TRUE(alloc2.pointer != 0);
EXPECT_EQ(alloc2.size, 200);
EXPECT_EQ(alloc2.pointer, alloc1.pointer + 100);
auto oom_alloc = uf::memoryPool::allocate(pool, 800, 0);
EXPECT_TRUE(oom_alloc.size == 0);
EXPECT_TRUE(oom_alloc.pointer != 0);
uf::memoryPool::destroy(pool);
})
TEST(MemoryPool_PoolStrategy, {
pod::MemoryPool pool;
uf::memoryPool::initialize(pool, 1024, pod::MemoryPool::Strategy::POOL, 64);
auto alloc1 = uf::memoryPool::allocate(pool, 64, 0);
EXPECT_TRUE(alloc1.pointer != 0);
EXPECT_EQ(alloc1.size, 64);
auto alloc2 = uf::memoryPool::allocate(pool, 64, 0);
EXPECT_TRUE(alloc2.pointer != 0);
bool freed = uf::memoryPool::free(pool, (void*)alloc1.pointer, 64);
EXPECT_TRUE(freed);
auto alloc3 = uf::memoryPool::allocate(pool, 64, 0);
EXPECT_EQ(alloc3.pointer, alloc1.pointer);
uf::memoryPool::destroy(pool);
})
TEST(MemoryPool_SegregatedStrategy, {
pod::MemoryPool pool;
uf::memoryPool::initialize(pool, 1024, pod::MemoryPool::Strategy::SEGREGATED, 16);
auto alloc1 = uf::memoryPool::allocate(pool, 20, 0);
EXPECT_EQ(alloc1.size, 32);
auto alloc2 = uf::memoryPool::allocate(pool, 60, 0);
EXPECT_EQ(alloc2.size, 64);
EXPECT_TRUE(uf::memoryPool::free(pool, (void*)alloc1.pointer, 32));
auto alloc3 = uf::memoryPool::allocate(pool, 30, 0);
EXPECT_EQ(alloc3.size, 32);
EXPECT_EQ(alloc3.pointer, alloc1.pointer);
uf::memoryPool::destroy(pool);
})
TEST(MemoryPool_BuddyStrategy, {
pod::MemoryPool pool;
uf::memoryPool::initialize(pool, 1024, pod::MemoryPool::Strategy::BUDDY, 64);
auto alloc1 = uf::memoryPool::allocate(pool, 100, 0);
EXPECT_EQ(alloc1.size, 128);
auto alloc2 = uf::memoryPool::allocate(pool, 128, 0);
EXPECT_EQ(alloc2.size, 128);
EXPECT_TRUE(uf::memoryPool::free(pool, (void*)alloc1.pointer, 128));
EXPECT_TRUE(uf::memoryPool::free(pool, (void*)alloc2.pointer, 128));
auto alloc3 = uf::memoryPool::allocate(pool, 256, 0);
EXPECT_EQ(alloc3.size, 256);
EXPECT_EQ(alloc3.pointer, alloc1.pointer);
uf::memoryPool::destroy(pool);
})

View File

@ -127,13 +127,13 @@ void uf::thread::batchWorkers_Async( const uf::stl::vector<pod::Thread::function
*/
/*
void uf::thread::add( pod::Thread& thread, bool queued, const pod::Thread::function_t& function ) {
if ( thread.mutex != NULL ) thread.mutex->lock();
std::unique_lock<std::mutex> lock(*thread.mutex);
queue ? thread.queue.push( function ) : thread.container.push_back( function );
if ( thread.mutex != NULL ) thread.mutex->unlock();
}
*/
void uf::thread::add( pod::Thread& thread, const pod::Thread::function_t& function ) {
if ( thread.mutex != NULL ) thread.mutex->lock();
std::unique_lock<std::mutex> lock(*thread.mutex);
thread.container.emplace_back( function );
if ( thread.mutex != NULL ) thread.mutex->unlock();
}
@ -145,7 +145,7 @@ void uf::thread::queue( const pod::Thread::function_t& function ) {
return uf::thread::queue( uf::thread::fetchWorker(), function );
}
void uf::thread::queue( pod::Thread& thread, const pod::Thread::function_t& function ) {
if ( thread.mutex != NULL ) thread.mutex->lock();
std::unique_lock<std::mutex> lock(*thread.mutex);
thread.queue.emplace( function );
thread.conditions.queued.notify_one();
thread.pending.fetch_add(1);
@ -233,6 +233,7 @@ void uf::thread::terminate() {
}
}
pod::Thread& uf::thread::create( const uf::stl::string& name, bool start, bool locks ) {
locks = true; // test
if ( name == uf::thread::mainThreadName ) start = false;
pod::Thread* pointer = NULL;

177
makefiles/dependencies.mk Normal file
View File

@ -0,0 +1,177 @@
ifneq (,$(findstring ffx:sdk,$(REQ_DEPS)))
ifneq (,$(findstring vulkan,$(REQ_DEPS)))
FLAGS += -DUF_USE_FFX_SDK=3
DEPS := -lffx_fsr3_x64 -lffx_fsr2_x64 -lffx_fsr3upscaler_x64 -lffx_frameinterpolation_x64 -lffx_opticalflow_x64 -lffx_backend_vk_x64 -lamd_fidelityfx_vk $(DEPS)
endif
endif
ifneq (,$(findstring vulkan,$(REQ_DEPS)))
FLAGS += -DUF_USE_VULKAN
DEPS += -lspirv-cross-core -lspirv-cross-cpp
INCS += -I$(VULKAN_SDK_PATH)/include -I./dep/include/spirv_cross/
LIBS += -L$(VULKAN_SDK_PATH)/Lib
endif
ifneq (,$(findstring opengl,$(REQ_DEPS)))
FLAGS += -DUF_USE_OPENGL -DUF_USE_OPENGL_FIXED_FUNCTION
ifeq (,$(findstring gldc,$(REQ_DEPS)))
FLAGS += -DUF_USE_GLEW
endif
endif
ifneq (,$(findstring fmt,$(REQ_DEPS)))
FLAGS += -DUF_USE_FMT
ifeq (,$(findstring dreamcast,$(ARCH)))
DEPS += -lfmt
endif
endif
ifneq (,$(findstring ffx:fsr,$(REQ_DEPS)))
ifneq (,$(findstring vulkan,$(REQ_DEPS)))
FLAGS += -DUF_USE_FFX_FSR
DEPS += -lffx_fsr2_api_ -lffx_fsr2_api_vk_
endif
endif
ifneq (,$(findstring imgui,$(REQ_DEPS)))
FLAGS += -DUF_USE_IMGUI
INCS += -I./dep/include/imgui/ -I./dep/include/imgui/backends
endif
ifneq (,$(findstring cpptrace,$(REQ_DEPS)))
FLAGS += -DUF_USE_CPPTRACE
DEPS += -lcpptrace
endif
ifneq (,$(findstring json,$(REQ_DEPS)))
FLAGS += -DUF_USE_JSON
ifneq (,$(findstring nlohmann,$(REQ_DEPS)))
FLAGS += -DUF_JSON_USE_NLOHMANN
endif
endif
ifneq (,$(findstring gltf,$(REQ_DEPS)))
FLAGS += -DUF_USE_GLTF
INCS += -I./dep/include/stb/ -I./dep/include/nlohmann/
endif
ifneq (,$(findstring dc:texconv,$(REQ_DEPS)))
FLAGS += -DUF_USE_DC_TEXCONV
endif
ifneq (,$(findstring openal,$(REQ_DEPS)))
FLAGS += -DUF_USE_OPENAL -DUF_USE_ALUT
ifeq (,$(findstring dreamcast,$(ARCH)))
DEPS += -lopenal -lalut
endif
endif
ifneq (,$(findstring ogg,$(REQ_DEPS)))
FLAGS += -DUF_USE_VORBIS
ifeq (,$(findstring dreamcast,$(ARCH)))
DEPS += -lvorbis -lvorbisfile -logg
endif
endif
ifneq (,$(findstring wav,$(REQ_DEPS)))
FLAGS += -DUF_USE_WAV
endif
ifneq (,$(findstring zlib,$(REQ_DEPS)))
FLAGS += -DUF_USE_ZLIB
DEPS += -lz
endif
ifneq (,$(findstring freetype,$(REQ_DEPS)))
FLAGS += -DUF_USE_FREETYPE
DEPS += -lfreetype -lbz2
endif
ifneq (,$(findstring curl,$(REQ_DEPS)))
FLAGS += -DUF_USE_CURL
DEPS += -lcurl
endif
ifneq (,$(findstring openvr,$(REQ_DEPS)))
FLAGS += -DUF_USE_OPENVR -DUSE_OPENVR_MINGW
DEPS += -lopenvr_api
endif
ifneq (,$(findstring lua,$(REQ_DEPS)))
FLAGS += -DUF_USE_LUA
ifneq (,$(findstring luajit,$(REQ_DEPS)))
FLAGS += -DUF_USE_LUAJIT
DEPS += -lluajit-5.1
endif
endif
ifneq (,$(findstring reactphysics,$(REQ_DEPS)))
FLAGS += -DUF_USE_REACTPHYSICS
DEPS += -lreactphysics3d
endif
ifneq (,$(findstring simd,$(REQ_DEPS)))
ifeq (,$(findstring dreamcast,$(ARCH)))
FLAGS += -DUF_USE_SIMD -DUF_ALIGN_FOR_SIMD -DUF_MATRIX_ALIGNED
endif
endif
ifneq (,$(findstring meshoptimizer,$(REQ_DEPS)))
FLAGS += -DUF_USE_MESHOPT
DEPS += -lmeshoptimizer
endif
ifneq (,$(findstring xatlas,$(REQ_DEPS)))
FLAGS += -DUF_USE_XATLAS
endif
ifneq (,$(findstring ctti,$(REQ_DEPS)))
FLAGS += -DUF_CTTI -fno-rtti
else
FLAGS += -DUF_RTTI -rtti
endif
ifneq (,$(findstring toml,$(REQ_DEPS)))
FLAGS += -DUF_USE_TOML
endif
ifneq (,$(findstring vall_e,$(REQ_DEPS)))
FLAGS += -DUF_USE_VALL_E
INCS += -I./dep/include/vall_e.cpp/
DEPS += -lvall_e
endif
ifneq (,$(findstring draco,$(REQ_DEPS)))
FLAGS += -DUF_USE_DRACO
DEPS += -ldraco
endif
ifneq (,$(findstring ultralight-ux,$(REQ_DEPS)))
FLAGS += -DUF_USE_ULTRALIGHT_UX
DEPS += -lUltralight -lUltralightCore -lWebCore -lAppCore
endif
ifneq (,$(findstring discord,$(REQ_DEPS)))
FLAGS += -DUF_USE_DISCORD
DEPS += -ldiscord_game_sdk
endif
ifneq (,$(findstring ncurses,$(REQ_DEPS)))
FLAGS += -DUF_USE_NCURSES
DEPS += -lncursesw
endif
ifneq (,$(findstring png,$(REQ_DEPS)))
FLAGS += -DUF_USE_PNG
DEPS += -lpng
endif
ifneq (,$(findstring lz4,$(REQ_DEPS)))
FLAGS += -DUF_USE_LZ4
DEPS += -llz4
endif
ifneq (,$(findstring xz,$(REQ_DEPS)))
FLAGS += -DUF_USE_XZ -DUF_USE_LZMA
DEPS += -lxz -llzma
endif

View File

@ -10,5 +10,5 @@ FLAGS += $(KOS_CPPFLAGS) -m4-single -std=c++2b $(OPTIMIZATIONS) $(WARNINGS)
TARGET_EXTENSION = .elf
# KOS
INCS += $(KOS_INC_PATHS) -I/opt/dreamcast/sh-elf/sh-elf/include
LIBS += $(KOS_LIB_PATHS) -L/opt/dreamcast/sh-elf/sh-elf/lib
INCS := $(KOS_INC_PATHS) -I/opt/dreamcast/sh-elf/sh-elf/include $(INCS)
LIBS := $(KOS_LIB_PATHS) -L/opt/dreamcast/sh-elf/sh-elf/lib $(LIBS)

View File

@ -0,0 +1,77 @@
FLAGS += -DUF_ENV_DREAMCAST
REQ_DEPS += opengl gldc json:nlohmann zlib lua r:eactphysics simd ctti fmt freetype openal aldc ogg wav png
INCS := -I./dep/dreamcast/include $(INCS)
# Dependency Overrides
ifneq (,$(findstring gldc,$(REQ_DEPS)))
DEPS += -lGL
FLAGS += -DUF_USE_OPENGL_GLDC
endif
ifneq (,$(findstring aldc,$(REQ_DEPS)))
DEPS += -lAL -lpthread
FLAGS += -DUF_USE_OPENAL_ALDC -DUF_USE_ALUT
endif
ifneq (,$(findstring ogg,$(REQ_DEPS)))
FLAGS += -DUF_USE_TREMOR
DEPS += -ltremor
endif
ifneq (,$(findstring lua,$(REQ_DEPS)))
DEPS += -llua
INCS += -I/opt/dreamcast/kos-ports/include/lua
endif
ifneq (,$(findstring simd,$(REQ_DEPS)))
FLAGS += -DUF_ENV_DREAMCAST_SIMD
endif
DEPS += -lkallisti -lc -lm -lgcc -lstdc++
SRCS_DLL = $(shell find $(ENGINE_SRC_DIR) -name "*.cpp") $(shell find $(DEP_SRC_DIR) -name "*.cpp")
OBJS_DLL = $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS_DLL))
OBJS = $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS_DLL)) $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS_EXT_DLL)) $(patsubst %.cpp,%.$(PREFIX).o,$(SRCS))
$(PREFIX): $(TARGET) ./bin/dreamcast/$(TARGET_NAME).cdi
$(EX_DLL): FLAGS += -DUF_EXPORTS
$(EX_DLL): $(OBJS_DLL)
$(KOS_AR) cru $@ $^
$(KOS_RANLIB) $@
cp $@ $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_DLL)
$(EXT_EX_DLL): FLAGS += -DEXT_EXPORTS
$(EXT_EX_DLL): $(OBJS_EXT_DLL)
$(KOS_AR) cru $@ $^
$(KOS_RANLIB) $@
cp $@ $(ENGINE_LIB_DIR)/$(PREFIX_PATH)/$(BASE_EXT_DLL)
./bin/dreamcast/romdisk.img:
$(KOS_GENROMFS) -f ./bin/dreamcast/romdisk.img -d ./bin/dreamcast/romdisk/ -v
./bin/dreamcast/romdisk.o: ./bin/dreamcast/romdisk.img
$(KOS_BASE)/utils/bin2o/bin2o ./bin/dreamcast/romdisk.img romdisk ./bin/dreamcast/romdisk.o
$(TARGET): $(OBJS) #./bin/dreamcast/romdisk.o
$(CXX) $(FLAGS) $(INCS) -D_arch_dreamcast -D_arch_sub_pristine -Wall -fno-builtin -ml -Wl,-Ttext=0x8c010000 -T/opt/dreamcast/kos/utils/ldscripts/shlelf.xc -nodefaultlibs $(KOS_LIB_PATHS) $(LIBS) -o $(TARGET) $(OBJS) -Wl,--start-group $(DEPS) -Wl,--end-group
cp $(TARGET) $(TARGET).unstripped
$(KOS_STRIP) --strip-unneeded $(TARGET)
./bin/dreamcast/$(TARGET_NAME).cdi: $(TARGET)
cd ./bin/dreamcast/; ./elf2cdi.sh $(TARGET_NAME)
cdi:
cd ./bin/dreamcast/; ./elf2cdi.sh $(TARGET_NAME)
run-dreamcast:
$(KOS_EMU) ./bin/dreamcast/$(TARGET_NAME).cdi
run-debug-dreamcast:
$(KOS_EMU_DEBUG) ./bin/dreamcast/$(TARGET_NAME).cdi
clean-dreamcast:
@-rm ./bin/dreamcast/build/*
@-rm ./bin/dreamcast/romdisk.*
@-rm ./bin/dreamcast/$(TARGET_NAME).*

View File

@ -0,0 +1,45 @@
ifneq (,$(findstring -DUF_DEV_ENV,$(FLAGS)))
REQ_DEPS += toml xatlas curl dc:texconv # meshoptimizer ffx:fsr cpptrace vall_e # ncurses openvr draco discord bullet ultralight-ux
FLAGS += -march=native -g # -flto # -g
endif
REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit r:eactphysics simd ctti gltf imgui fmt freetype openal ogg wav
FLAGS += -DUF_ENV_LINUX -fPIC
DEPS += -pthread -ldl -lX11 -lXrandr
INCS := -I./dep/master/include $(INCS)
# Vulkan Paths
VULKAN_SDK_PATH ?= /
GLSLC ?= glslc
SPV_OPTIMIZER ?= spirv-opt
SPV_LINTER ?= spirv-lint
# Dependency Overrides
ifneq (,$(findstring vulkan,$(REQ_DEPS)))
DEPS += -lvulkan
FLAGS += -DVK_USE_PLATFORM_XLIB_KHR
endif
ifneq (,$(findstring opengl,$(REQ_DEPS)))
ifeq (,$(findstring gldc,$(REQ_DEPS)))
DEPS += -lGLU -lglut -lGLEW
endif
endif
ifneq (,$(findstring luajit,$(REQ_DEPS)))
INCS += -I/usr/include/luajit-2.1
endif
# Target Compilation Overrides
$(EX_DLL): FLAGS += -DUF_EXPORTS
$(EX_DLL): $(OBJS_DLL)
$(CXX) $(FLAGS) -shared -Wl,-soname,$(BASE_DLL)$(DLIB_EXTENSION) $(OBJS_DLL) $(LIBS) $(INCS) $(LINKS) -o $(EX_DLL)
cp $(EX_DLL) $(IM_DLL)
@echo -n $(ARCH) > "./bin/exe/default/arch"
@echo -n $(CC) > "./bin/exe/default/cc"
@echo -n $(RENDERER) > "./bin/exe/default/renderer"
$(EXT_EX_DLL): FLAGS += -DEXT_EXPORTS
$(EXT_EX_DLL): $(OBJS_EXT_DLL)
$(CXX) $(FLAGS) -shared -Wl,-soname,$(BASE_EXT_DLL)$(DLIB_EXTENSION) $(OBJS_EXT_DLL) $(EXT_LIBS) $(EXT_INCS) $(EXT_LINKS) -o $(EXT_EX_DLL)
cp $(EXT_EX_DLL) $(EXT_IM_DLL)

View File

@ -0,0 +1,48 @@
ifneq (,$(findstring -DUF_DEV_ENV,$(FLAGS)))
REQ_DEPS += meshoptimizer toml xatlas curl ffx:sdk dc:texconv # vall_e cpptrace # openvr # ncurses draco discord bullet ultralight-ux
FLAGS += -march=native -g # -flto # -g
endif
REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit r:eactphysics simd ctti gltf imgui fmt freetype openal ogg wav
FLAGS += -DUF_ENV_WINDOWS -DUF_ENV_WIN64 -DWIN32_LEAN_AND_MEAN
DEPS += -lgdi32 -ldwmapi
LINKS += #-Wl,-subsystem,windows
INCS := -I./dep/master/include $(INCS)
# Vulkan Paths
VULKAN_SDK_PATH ?= /c/VulkanSDK/1.4.321.1/
GLSLC ?= $(VULKAN_SDK_PATH)/Bin/glslc
SPV_OPTIMIZER ?= $(VULKAN_SDK_PATH)/Bin/spirv-opt
SPV_LINTER ?= $(VULKAN_SDK_PATH)/Bin/spirv-lint
# Dependency Overrides
ifneq (,$(findstring vulkan,$(REQ_DEPS)))
DEPS += -lvulkan-1
FLAGS += -DVK_USE_PLATFORM_WIN32_KHR
endif
ifneq (,$(findstring opengl,$(REQ_DEPS)))
ifeq (,$(findstring gldc,$(REQ_DEPS)))
DEPS += -lglew32 -lopengl32 -lglu32
endif
endif
ifneq (,$(findstring luajit,$(REQ_DEPS)))
ifneq (,$(findstring clang,$(CC)))
INCS += -I/clang64/include/luajit-2.1
else
INCS += -I/mingw64/include/luajit-2.1
endif
endif
# Target Compilation Overrides
$(EX_DLL): FLAGS += -DUF_EXPORTS
$(EX_DLL): $(OBJS_DLL)
$(CXX) $(FLAGS) -shared -o $(EX_DLL) -Wl,--out-implib=$(IM_DLL)$(SLIB_EXTENSION) $(OBJS_DLL) $(LIBS) $(INCS) $(LINKS)
@echo -n $(ARCH) > "./bin/exe/default/arch"
@echo -n $(CC) > "./bin/exe/default/cc"
@echo -n $(RENDERER) > "./bin/exe/default/renderer"
$(EXT_EX_DLL): FLAGS += -DEXT_EXPORTS
$(EXT_EX_DLL): $(OBJS_EXT_DLL)
$(CXX) $(FLAGS) -shared -o $(EXT_EX_DLL) -Wl,--out-implib=$(EXT_IM_DLL)$(SLIB_EXTENSION) $(OBJS_EXT_DLL) $(EXT_LIBS) $(EXT_INCS) $(EXT_LINKS)