diff --git a/Makefile b/Makefile index f22dd4a5..d4011189 100644 --- a/Makefile +++ b/Makefile @@ -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 - 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") diff --git a/client/main.cpp b/client/main.cpp index f3fcb072..a39bcafd 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -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 ) { diff --git a/engine/inc/uf/engine/instantiator/instantiator.h b/engine/inc/uf/engine/instantiator/instantiator.h index ad45c10e..b0e6946e 100644 --- a/engine/inc/uf/engine/instantiator/instantiator.h +++ b/engine/inc/uf/engine/instantiator/instantiator.h @@ -47,6 +47,7 @@ namespace uf { extern UF_API pod::NamedTypes* objects; // extern UF_API pod::NamedTypes* behaviors; extern UF_API uf::stl::unordered_map* behaviors; + extern UF_API uf::stl::vector queue; uf::Entity* UF_API alloc( size_t ); template T* alloc(); @@ -66,6 +67,8 @@ namespace uf { template T& instantiate(); template T* _instantiate(); + void UF_API queueDeletion( uf::Entity& ); + void UF_API bind( const uf::stl::string&, uf::Entity& ); template void bind( uf::Entity& ); diff --git a/engine/inc/uf/utils/memory/pool.h b/engine/inc/uf/utils/memory/pool.h index 52808d69..582f68a1 100644 --- a/engine/inc/uf/utils/memory/pool.h +++ b/engine/inc/uf/utils/memory/pool.h @@ -6,12 +6,15 @@ #include #include +#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> 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> 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(); diff --git a/engine/inc/uf/utils/memory/pool.inl b/engine/inc/uf/utils/memory/pool.inl index 98c0a31b..186d96df 100644 --- a/engine/inc/uf/utils/memory/pool.inl +++ b/engine/inc/uf/utils/memory/pool.inl @@ -27,13 +27,23 @@ bool uf::memoryPool::exists( pod::MemoryPool& pool, const T& data ) { } template bool uf::memoryPool::free( pod::MemoryPool& pool, const T& data ) { - return std::is_pointer::value ? uf::memoryPool::free( pool, (void*) data ) : uf::memoryPool::free( pool, (void*) &data, sizeof(data) ); +#if __cplusplus >= 201703L + if constexpr (std::is_pointer_v) { + return uf::memoryPool::free( pool, (void*) data, sizeof(std::remove_pointer_t) ); + } else { + return uf::memoryPool::free( pool, (void*) &data, sizeof(T) ); + } +#else + return std::is_pointer::value + ? uf::memoryPool::free( pool, (void*) data, sizeof(typename std::remove_pointer::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*/ ); } diff --git a/engine/src/engine/ext/ext.cpp b/engine/src/engine/ext/ext.cpp index d79b2c89..67841ca7 100644 --- a/engine/src/engine/ext/ext.cpp +++ b/engine/src/engine/ext/ext.cpp @@ -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 ); diff --git a/engine/src/engine/instantiator/instantiator.cpp b/engine/src/engine/instantiator/instantiator.cpp index 439a275e..957b0111 100644 --- a/engine/src/engine/instantiator/instantiator.cpp +++ b/engine/src/engine/instantiator/instantiator.cpp @@ -4,12 +4,18 @@ #include #include +namespace { + std::mutex queueMutex; +} + pod::NamedTypes* uf::instantiator::objects = NULL; //pod::NamedTypes* uf::instantiator::behaviors = NULL; uf::stl::unordered_map* uf::instantiator::behaviors = NULL; +uf::stl::vector 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 queued; size_t collected = 0; auto& allocations = uf::Entity::memoryPool.allocations(); auto& scene = uf::scene::getCurrentScene(); - uf::stl::vector 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())); queued.emplace_back( (uf::Entity*) allocation.pointer ); } +*/ + // mutex + ::queueMutex.lock(); + uf::stl::vector queued = std::move( uf::instantiator::queue ); + ::queueMutex.unlock(); + for ( auto& p : queued ) { if ( p->hasComponent() ) 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 ) ) { diff --git a/engine/src/engine/object/object.cpp b/engine/src/engine/object/object.cpp index 6f6677ac..83a41125 100644 --- a/engine/src/engine/object/object.cpp +++ b/engine/src/engine/object/object.cpp @@ -41,7 +41,10 @@ void uf::Object::queueDeletion() { if ( this->hasParent() ) this->getParent().removeChild(*this); auto& metadata = this->getComponent(); + + // mark for destruction metadata.system.markedForDeletion = true; + uf::instantiator::queueDeletion( *this ); } uf::Hooks::return_t uf::Object::callHook( const uf::stl::string& name ) { diff --git a/engine/src/utils/math/physics/bvh.inl b/engine/src/utils/math/physics/bvh.inl index 7f66ea73..90c7230d 100644 --- a/engine/src/utils/math/physics/bvh.inl +++ b/engine/src/utils/math/physics/bvh.inl @@ -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 ); diff --git a/engine/src/utils/math/physics/impl.cpp b/engine/src/utils/math/physics/impl.cpp index 31fff4e7..d72a8e29 100644 --- a/engine/src/utils/math/physics/impl.cpp +++ b/engine/src/utils/math/physics/impl.cpp @@ -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 diff --git a/engine/src/utils/math/physics/integration.inl b/engine/src/utils/math/physics/integration.inl index cbd115f1..2f35bc73 100644 --- a/engine/src/utils/math/physics/integration.inl +++ b/engine/src/utils/math/physics/integration.inl @@ -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) ); } } diff --git a/engine/src/utils/math/physics/tests.inl b/engine/src/utils/math/physics/tests.inl index 7ff7267a..0d2e258a 100644 --- a/engine/src/utils/math/physics/tests.inl +++ b/engine/src/utils/math/physics/tests.inl @@ -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); diff --git a/engine/src/utils/memory/pool.cpp b/engine/src/utils/memory/pool.cpp index 1fc76ee3..f9d5867e 100644 --- a/engine/src/utils/memory/pool.cpp +++ b/engine/src/utils/memory/pool.cpp @@ -10,63 +10,124 @@ #include #include -#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(pool.memory); + for (size_t i = 0; i < numChunks - 1; ++i) { + void** currentChunk = reinterpret_cast(ptr + (i * chunkSize)); + *currentChunk = ptr + ((i + 1) * chunkSize); + } + void** lastChunk = reinterpret_cast(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 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(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(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(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(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 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(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(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(buddy); + buddyIsFree = true; + break; + } + current = reinterpret_cast(*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(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(); -} \ No newline at end of file +} + +#if UF_MEMORYPOOL_TEST + #include "tests.inl" +#endif \ No newline at end of file diff --git a/engine/src/utils/memory/tests.inl b/engine/src/utils/memory/tests.inl new file mode 100644 index 00000000..78390ea5 --- /dev/null +++ b/engine/src/utils/memory/tests.inl @@ -0,0 +1,82 @@ +#include + +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); +}) \ No newline at end of file diff --git a/engine/src/utils/thread/thread.cpp b/engine/src/utils/thread/thread.cpp index d3339c40..cb2e2246 100644 --- a/engine/src/utils/thread/thread.cpp +++ b/engine/src/utils/thread/thread.cpp @@ -127,13 +127,13 @@ void uf::thread::batchWorkers_Async( const uf::stl::vectorlock(); + std::unique_lock 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 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 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; diff --git a/makefiles/dependencies.mk b/makefiles/dependencies.mk new file mode 100644 index 00000000..e61cc755 --- /dev/null +++ b/makefiles/dependencies.mk @@ -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 \ No newline at end of file diff --git a/makefiles/dreamcast.gcc.make b/makefiles/platforms/dreamcast.gcc.mk similarity index 82% rename from makefiles/dreamcast.gcc.make rename to makefiles/platforms/dreamcast.gcc.mk index ffebb3fb..32819884 100644 --- a/makefiles/dreamcast.gcc.make +++ b/makefiles/platforms/dreamcast.gcc.mk @@ -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 \ No newline at end of file +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) \ No newline at end of file diff --git a/makefiles/platforms/dreamcast.mk b/makefiles/platforms/dreamcast.mk new file mode 100644 index 00000000..c06eef0d --- /dev/null +++ b/makefiles/platforms/dreamcast.mk @@ -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).* \ No newline at end of file diff --git a/makefiles/linux.clang.make b/makefiles/platforms/linux.clang.mk similarity index 100% rename from makefiles/linux.clang.make rename to makefiles/platforms/linux.clang.mk diff --git a/makefiles/linux.gcc.make b/makefiles/platforms/linux.gcc.mk similarity index 100% rename from makefiles/linux.gcc.make rename to makefiles/platforms/linux.gcc.mk diff --git a/makefiles/platforms/linux.mk b/makefiles/platforms/linux.mk new file mode 100644 index 00000000..10925e22 --- /dev/null +++ b/makefiles/platforms/linux.mk @@ -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) \ No newline at end of file diff --git a/makefiles/win64.clang.make b/makefiles/platforms/win64.clang.mk similarity index 100% rename from makefiles/win64.clang.make rename to makefiles/platforms/win64.clang.mk diff --git a/makefiles/win64.gcc.make b/makefiles/platforms/win64.gcc.mk similarity index 100% rename from makefiles/win64.gcc.make rename to makefiles/platforms/win64.gcc.mk diff --git a/makefiles/platforms/win64.mk b/makefiles/platforms/win64.mk new file mode 100644 index 00000000..19fb9d51 --- /dev/null +++ b/makefiles/platforms/win64.mk @@ -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) \ No newline at end of file