Android update and small improvements

-fixed code do cross compiling to android
-fixed build with GCC (workaround)
-added little benchmark
-several small fixes
-updated meson build (demos building, working with GCC, LDC and DMD)
-added some meson options
-added ImGUI bind for OpenGL3
This commit is contained in:
Mergul 2020-06-01 11:24:50 +02:00
parent 86edfa4a57
commit 66860b9042
30 changed files with 1358 additions and 173 deletions

1
.gitignore vendored
View file

@ -10,3 +10,4 @@
!meson.build
!meson_options.txt
!compile_wasm.py
!compile_android.py

78
compile_android.py Normal file
View file

@ -0,0 +1,78 @@
import os
import ntpath
import sys
def compile(sources, output):
files = []
# r=root, d=directories, f = files
for path in sources:
for r, d, f in os.walk(path):
for file in f:
if ntpath.basename(file) != 'win_dll.d':
filename, file_extension = os.path.splitext(file)
if file_extension == '.d' and filename != 'package':
files.append(os.path.join(r, file))
ldc_path = 'ldc'
if 'LDC' in os.environ:
ldc_path = os.environ['LDC']
ldc_cmd = ldc_path + ' ' + ldc_flags + '-lib -mtriple=armv7-none-linux-androideabi -fvisibility=hidden -betterC -oq -od=obj/ --singleobj --of=' + output + ' '
for path in sources:
ldc_cmd += '-I' + path + ' '
for path in import_paths:
ldc_cmd += '-I' + path + ' '
for f in files:
ldc_cmd += f + ' '
print(ldc_cmd)
if os.system(ldc_cmd):
exit(0)
print()
clean = 0
ldc_flags = ''
import_paths = ['source','tests']
build_tests = 0
for arg in sys.argv[1:]:
if(arg == '-release'):
ldc_flags += '-release '
elif(arg == '-enable-inlining'):
ldc_flags += '-enable-inlining '
elif(arg == '-O3'):
ldc_flags += '-O3 '
elif(arg == '-O2'):
ldc_flags += '-O2 '
elif(arg == '-O1'):
ldc_flags += '-O1 '
elif(arg == '-O0'):
ldc_flags += '-O0 '
elif(arg == '-Os'):
ldc_flags += '-Os '
elif(arg == '-Oz'):
ldc_flags += '-Oz '
elif(arg == '-g'):
ldc_flags += '-g '
elif(arg == '-opt'):
ldc_flags += '-release -enable-inlining -O3 '
else:
print('unknown argument: ' + arg)
exit()
compile(['source'], 'ecs.a')
#export LDC_LIBS=/path/to/your/ldc-build-runtime.tmp/lib/
CC = os.environ['NDK'] + '/toolchains/llvm/prebuilt/linux-x86_64/bin/clang'
TOOLCHAIN = os.environ['NDK'] + '/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64'
SYSROOT = os.environ['NDK'] + '/platforms/android-21/arch-arm'
LDC_LIBS = ''#os.environ['LDC_LIBS'] + '/libphobos2-ldc.a ' + os.environ['LDC_LIBS'] + '/libdruntime-ldc.a'
os.system(CC + ' -Wl,-soname,libecs.so -shared --sysroot=' + SYSROOT + ' obj/*.o ' + LDC_LIBS + ' -lgcc -gcc-toolchain ' + TOOLCHAIN +
' -no-canonical-prefixes -fuse-ld=bfd -target armv7-none-linux-androideabi -fvisibility=hidden \
-Wl,--gc-sections -Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro \
-Wl,-z,now -mthumb -o libecs.so')

1
demos/.gitignore vendored
View file

@ -14,4 +14,5 @@
!cimgui.bc
!emscripten_shell.html
!emscripten_multi_shell.html
!compile_android.py
.dub

91
demos/compile_android.py Normal file
View file

@ -0,0 +1,91 @@
import os
import ntpath
import sys
def compile(sources, output):
files = []
# r=root, d=directories, f = files
for path in sources:
for r, d, f in os.walk(path):
for file in f:
if ntpath.basename(file) != 'win_dll.d':
filename, file_extension = os.path.splitext(file)
if file_extension == '.d':
files.append(os.path.join(r, file))
ldc_path = 'ldc'
if 'LDC' in os.environ:
ldc_path = os.environ['LDC']
ldc_cmd = ldc_path + ' ' + ldc_flags + '-lib -mtriple=armv7-none-linux-androideabi -fvisibility=hidden -betterC -oq -od=obj/ --singleobj --of=' + output + ' '
for path in sources:
ldc_cmd += '-I' + path + ' '
for path in import_paths:
ldc_cmd += '-I' + path + ' '
for f in files:
ldc_cmd += f + ' '
print(ldc_cmd)
if os.system(ldc_cmd):
print('some kind of error')
exit(0)
print()
clean = 0
ldc_flags = '--d-version=SDL_209 --d-version=BindSDL_Image --d-version=MM_USE_POSIX_THREADS '
#import_paths = ['source','tests']
import_paths = ['external','external/sources', 'external/imports', 'external/wasm_imports', '../source', 'utils/source', 'simple/source']
build_tests = 0
for arg in sys.argv[1:]:
if(arg == '-release'):
ldc_flags += '-release '
elif(arg == '-enable-inlining'):
ldc_flags += '-enable-inlining '
elif(arg == '-O3'):
ldc_flags += '-O3 '
elif(arg == '-O2'):
ldc_flags += '-O2 '
elif(arg == '-O1'):
ldc_flags += '-O1 '
elif(arg == '-O0'):
ldc_flags += '-O0 '
elif(arg == '-Os'):
ldc_flags += '-Os '
elif(arg == '-Oz'):
ldc_flags += '-Oz '
elif(arg == '-g'):
ldc_flags += '-g '
elif(arg == '-opt'):
ldc_flags += '-release -enable-inlining -O3 '
else:
print('unknown argument: ' + arg)
exit()
#compile(['source'], 'ecs.a')
compile(['external/wasm_imports/bindbc/sdl'], 'build/bindbc-sdl.a')
compile(['utils/source'], 'build/utils.a')
compile(['external/sources/mmutils'], 'build/mmutils.a')
compile(['external/sources/glad'], 'build/glad.a')
compile(['external/android/bindbc'], 'build/bindbc.a')
compile(['source'], 'build/demo.a')
#compile(['external/wasm_imports/bindbc/sdl','utils/source','external/sources/mmutils','external/sources/glad'], 'build/asd.a')
#export LDC_LIBS=/path/to/your/ldc-build-runtime.tmp/lib/
CC = os.environ['NDK'] + '/toolchains/llvm/prebuilt/linux-x86_64/bin/clang'
TOOLCHAIN = os.environ['NDK'] + '/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64'
SYSROOT = os.environ['NDK'] + '/platforms/android-21/arch-arm'
#LDC_LIBS = os.environ['LDC_LIBS'] + '/libphobos2-ldc.a ' + os.environ['LDC_LIBS'] + '/libdruntime-ldc.a'
LDC_LIBS = ''
LIBS = '-L/platforms/android-21/arch-arm/usr/lib'
os.system(CC + ' -Wl,-soname,libdemos.so -shared --sysroot=' + SYSROOT + ' ../obj/*.o obj/*.o ' + LDC_LIBS + ' -lgcc -gcc-toolchain ' + TOOLCHAIN +
' -no-canonical-prefixes -fuse-ld=bfd -target armv7-none-linux-androideabi -fvisibility=hidden \
-Wl,--gc-sections -Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro \
-Wl,-z,now -mthumb -lm -lc -Llibs/armeabi-v7a -lcimgui -o libdemos.so')

View file

@ -19,7 +19,8 @@
"libs-linux-x86_64": ["cimgui","SDL2","SDL2_image"],
"lflags-linux-x86_64": ["-rpath=libs/linux/x64/","-Llibs/linux/x64/"],
"dflags-ldc" : [
"--ffast-math"
"--ffast-math",
"-enable-cross-module-inlining"
],
"configurations" : [
{

View file

@ -0,0 +1,11 @@
// Copyright Michael D. Parker 2018.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
module bindbc.loader;
public
import bindbc.loader.sharedlib,
bindbc.loader.system;

View file

@ -0,0 +1,335 @@
// Copyright Michael D. Parker 2018.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
module bindbc.loader.sharedlib;
import core.stdc.stdlib;
import core.stdc.string;
/// Handle to a shared library
struct SharedLib {
private void* _handle;
}
/// Indicates an uninitialized or unassigned handle.
enum invalidHandle = SharedLib.init;
// Contains information about shared library and symbol load failures.
struct ErrorInfo {
private:
char[32] _error;
char[96] _message;
public @nogc nothrow @property:
/**
Returns the string "Missing Symbol" to indicate a symbol load failure, and
the name of a library to indicate a library load failure.
*/
const(char)* error() const { return _error.ptr; }
/**
Returns a symbol name for symbol load failures, and a system-specific error
message for library load failures.
*/
const(char)* message() const { return _message.ptr; }
}
private {
__gshared ErrorInfo[] _errors;
__gshared size_t _errorCount;
}
@nogc nothrow:
/**
Returns an slice containing all errors that have been accumulated by the
`load` and `bindSymbol` functions since the last call to `resetErrors`.
*/
const(ErrorInfo)[] errors()
{
return _errors[0 .. _errorCount];
}
/**
Returns the total number of errors that have been accumulated by the
`load` and `bindSymbol` functions since the last call to `resetErrors`.
*/
size_t errorCount()
{
return _errorCount;
}
/**
Sets the error count to 0 and erases all accumulated errors. This function
does not release any memory allocated for the error list.
*/
void resetErrors()
{
_errorCount = 0;
memset(_errors.ptr, 0, _errors.length * ErrorInfo.sizeof);
}
/*
void freeErrors()
{
free(_errors.ptr);
_errors.length = _errorCount = 0;
}
*/
/**
Loads a symbol from a shared library and assigns it to a caller-supplied pointer.
Params:
lib = a valid handle to a shared library loaded via the `load` function.
ptr = a pointer to a function or variable pointer whose declaration is
appropriate for the symbol being bound (it is up to the caller to
verify the types match).
symbolName = the name of the symbol to bind.
*/
void bindSymbol(SharedLib lib, void** ptr, const(char)* symbolName)
{
// Without this, DMD can hang in release builds
pragma(inline, false);
assert(lib._handle);
auto sym = loadSymbol(lib._handle, symbolName);
if(sym) {
*ptr = sym;
}
else {
addErr("Missing Symbol", symbolName);
}
}
/**
Formats a symbol using the Windows stdcall mangling if necessary before passing it on to
bindSymbol.
Params:
lib = a valid handle to a shared library loaded via the `load` function.
ptr = a pointer to a function or variable pointer whose declaration is
appropriate for the symbol being bound (it is up to the caller to
verify the types match).
symbolName = the name of the symbol to bind.
*/
void bindSymbol_stdcall(Func)(SharedLib lib, ref Func f, const(char)* symbolName)
{
import bindbc.loader.system : bindWindows, bind32;
static if(bindWindows && bind32) {
import core.stdc.stdio : snprintf;
import std.traits : ParameterTypeTuple;
uint paramSize(A...)(A args)
{
size_t sum = 0;
foreach(arg; args) {
sum += arg.sizeof;
// Align on 32-bit stack
if((sum & 3) != 0) {
sum += 4 - (sum & 3);
}
}
return sum;
}
ParameterTypeTuple!f params;
char[128] mangled;
snprintf(mangled.ptr, mangled.length, "_%s@%d", symbolName, paramSize(params));
symbolName = mangled.ptr;
}
bindSymbol(lib, cast(void**)&f, symbolName);
}
/**
Loads a shared library from disk, using the system-specific API and search rules.
libName = the name of the library to load. May include the full or relative
path for the file.
*/
SharedLib load(const(char)* libName)
{
auto handle = loadLib(libName);
if(handle) return SharedLib(handle);
else {
addErr(libName, null);
return invalidHandle;
}
}
/**
Unloads a shared library from process memory.
Generally, it is not necessary to call this function at program exit, as the system will ensure
any shared libraries loaded by the process will be unloaded then. However, any loaded shared
libraries that are no longer needed by the program during runtime, such as those that are part
of a "hot swap" mechanism, should be unloaded to free up resources.
*/
void unload(ref SharedLib lib) {
if(lib._handle) {
unloadLib(lib._handle);
lib = invalidHandle;
}
}
private:
void allocErrs() {
size_t newSize = _errorCount == 0 ? 16 : _errors.length * 2;
auto errs = cast(ErrorInfo*)malloc(ErrorInfo.sizeof * newSize);
if(!errs) exit(EXIT_FAILURE);
if(_errorCount > 0) {
memcpy(errs, _errors.ptr, ErrorInfo.sizeof * _errors.length);
free(_errors.ptr);
}
_errors = errs[0 .. newSize];
}
void addErr(const(char)* errstr, const(char)* message)
{
if(_errors.length == 0 || _errorCount >= _errors.length) {
allocErrs();
}
auto pinfo = &_errors[_errorCount];
strcpy(pinfo._error.ptr, errstr);
if(message) {
strncpy(pinfo._message.ptr, message, pinfo._message.length);
pinfo._message[pinfo._message.length - 1] = 0;
}
else {
sysError(pinfo._message.ptr, pinfo._message.length);
}
++_errorCount;
}
version(Windows)
{
import core.sys.windows.windows;
extern(Windows) @nogc nothrow alias pSetDLLDirectory = BOOL function(const(char)*);
pSetDLLDirectory setDLLDirectory;
void* loadLib(const(char)* name)
{
return LoadLibraryA(name);
}
void unloadLib(void* lib)
{
FreeLibrary(lib);
}
void* loadSymbol(void* lib, const(char)* symbolName)
{
return GetProcAddress(lib, symbolName);
}
void sysError(char* buf, size_t len)
{
char* msgBuf;
enum uint langID = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
null,
GetLastError(),
langID,
cast(char*)&msgBuf,
0,
null
);
if(msgBuf) {
strncpy(buf, msgBuf, len);
buf[len - 1] = 0;
LocalFree(msgBuf);
}
else strncpy(buf, "Unknown Error\0", len);
}
/**
Adds a path to the default search path on Windows, replacing the path set in a previous
call to the same function.
Any path added to this function will be added to the default DLL search path as documented at
https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setdlldirectoryw.
Generally, when loading DLLs on a path that is not on the search path, e.g., from a subdirectory
of the application, the path should be prepended to the DLL name passed to the load function,
e.g., "dlls\\SDL2.dll". If `setCustomLoaderSearchPath(".\\dlls")` is called first, then the subdirectory
will become part of the DLL search path and the path may be omitted from the load function. (Be
aware that ".\\dlls" is relative to the current working directory, which may not be the application
directory, so the path should be built appropriately.)
Some DLLs may depend on other DLLs, perhaps even attempting to load them dynamically at run time
(e.g., SDL2_image only loads dependencies such as libpng if it is initialized at run time with
PNG support). In this case, if the DLL and its dependencies are placed in a subdirectory and
loaded as e.g., "dlls\\SDL2_image.dll", then it will not be able to find its dependencies; the
system loader will look for them on the regular DLL search path. When that happens, the solution
is to call `setCustomLoaderSearchPath` with the subdirectory before initializing the library.
Calling this function with `null` as the argument will reset the default search path.
When the function returns `false`, the relevant `ErrorInfo` is added to the global error list and can
be retrieved by looping through the array returned by the `errors` function.
When placing DLLs in a subdirectory of the application, it should be considered good practice to
call `setCustomLoaderSearchPath` to ensure all DLLs load properly. It should also be considered good
practice to reset the default search path once all DLLs are loaded.
This function is only available on Windows, so any usage of it should be preceded with
`version(Windows)`.
Params:
path = the path to add to the DLL search path, or `null` to reset the default.
Returns:
`true` if the path was successfully added to the DLL search path, otherwise `false`.
*/
public
bool setCustomLoaderSearchPath(const(char)* path)
{
if(!setDLLDirectory) {
auto lib = load("Kernel32.dll");
if(lib == invalidHandle) return false;
lib.bindSymbol(cast(void**)&setDLLDirectory, "SetDllDirectoryA");
if(!setDLLDirectory) return false;
}
return setDLLDirectory(path) != 0;
}
}
else version(Posix) {
import core.sys.posix.dlfcn;
void* loadLib(const(char)* name)
{
return dlopen(name, RTLD_NOW);
}
void unloadLib(void* lib)
{
dlclose(lib);
}
void* loadSymbol(void* lib, const(char)* symbolName)
{
return dlsym(lib, symbolName);
}
void sysError(char* buf, size_t len)
{
const (char)* msg = dlerror();
strncpy(buf, msg != null ? msg : "Unknown Error", len);
buf[len - 1] = 0;
}
}
else static assert(0, "bindbc-loader is not implemented on this platform.");

View file

@ -0,0 +1,55 @@
// Copyright Michael D. Parker 2018.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
module bindbc.loader.system;
static if((void*).sizeof == 8) {
enum bind64 = true;
enum bind32 = false;
}
else {
enum bind64 = false;
enum bind32 = true;
}
version(Windows) enum bindWindows = true;
else enum bindWindows = false;
version(OSX) enum bindMac = true;
else enum bindMac = false;
version(linux) enum bindLinux = true;
else enum bindLinux = false;
version(Posix) enum bindPosix = true;
else enum bindPosix = false;
version(Android) enum bindAndroid = true;
else enum bindAndroid = false;
enum bindIOS = false;
enum bindWinRT = false;
version(FreeBSD) {
enum bindBSD = true;
enum bindFreeBSD = true;
enum bindOpenBSD = false;
}
else version(OpenBSD) {
enum bindBSD = true;
enum bindFreeBSD = false;
enum bindOpenBSD = true;
}
else version(BSD) {
enum bindBSD = true;
enum bindFreeBSD = false;
enum bindOpenBSD = false;
}
else {
enum bindBSD = false;
enum bindFreeBSD = false;
enum bindOpenBSD = false;
}

View file

@ -201,6 +201,21 @@ void instructionPause()
static assert(0);
}
}
else version (Android)
{
version(LDC)
{
import ldc.attributes;
@optStrategy("none")
static void nop()
{
int i;
i++;
}
nop();
}
else static assert(0);
}
else version(WebAssembly)
{
version(LDC)

View file

@ -14,8 +14,8 @@ import bindbc.sdl.config,
bindbc.sdl.bind;
private {
SharedLib lib;
SDLSupport loadedVersion;
__gshared SharedLib lib;
__gshared SDLSupport loadedVersion;
}
void unloadSDL()
@ -664,14 +664,14 @@ SDLSupport loadSDL(const(char)* libName)
lib.bindSymbol(cast(void**)&SDL_HasColorKey, "SDL_HasColorKey");
lib.bindSymbol(cast(void**)&SDL_GetDisplayOrientation, "SDL_GetDisplayOrientation");
version(linux) {
lib.bindSymbol(cast(void**)&SDL_LinuxSetThreadPriority, "SDL_LinuxSetThreadPriority");
}
else version(Android) {
version(Android) {
lib.bindSymbol(cast(void**)&SDL_IsChromebook, "SDL_IsChromebook");
lib.bindSymbol(cast(void**)&SDL_IsDeXMode, "SDL_IsDeXMode");
lib.bindSymbol(cast(void**)&SDL_AndroidBackButton, "SDL_AndroidBackButton");
}
else version(linux) {
lib.bindSymbol(cast(void**)&SDL_LinuxSetThreadPriority, "SDL_LinuxSetThreadPriority");
}
if(errorCount() != errCount) return SDLSupport.badLibrary;
else loadedVersion = SDLSupport.sdl209;

View file

@ -229,8 +229,8 @@ else {
}
private {
SharedLib lib;
SDLImageSupport loadedVersion;
__gshared SharedLib lib;
__gshared SDLImageSupport loadedVersion;
}
void unloadSDLImage()

Binary file not shown.

View file

@ -203,12 +203,10 @@ void mainLoop(void* arg)
temp_fps = 0;
}
SDL_Event event;
while (SDL_PollEvent(&event))
{
version(WebAssembly)ImGui_ImplSDL2_ProcessEvent(&event);
else ImGui_ImplSDL2_ProcessEvent(&event);
ImGui_ImplSDL2_ProcessEvent(&event);
if(launcher.event)launcher.event(&event);
if (event.type == SDL_QUIT || (event.type == SDL_KEYDOWN && event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)) {
quit();
@ -278,7 +276,8 @@ void mainLoop(void* arg)
}
else
{
ImGuiImplOpenGL2NewFrame();
//ImGuiImplOpenGL2NewFrame();
ImGui_ImplOpenGL3_NewFrame();
ImGuiImplSDL2NewFrame(launcher.window);
}
@ -632,7 +631,9 @@ void mainLoop(void* arg)
igRender();
version(WebAssembly)ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData());
else ImGuiImplOpenGL2RenderDrawData(igGetDrawData());
else version(Android)ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData());
else ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData());
//ImGuiImplOpenGL2RenderDrawData(igGetDrawData());
//launcher.renderer.clear();
//launcher.renderer.present();
@ -652,15 +653,48 @@ void quit()
version(WebAssembly)emscripten_cancel_main_loop();
}
int main(int argc, char** argv)
version(Android)
{
export extern (C) int SDL_main(int argc, char** args)
{
return app_main(argc,args);
}
import ldc.attributes;
extern (C) __gshared
{
@section(".tdata")
int _tlsstart = 0;
@section(".tcommon")
int _tlsend = 0;
}
}
else
{
extern (C) int main(int argc, char** argv)
{
return app_main(argc,argv);
}
}
int app_main(int argc, char** argv)
//int main(int argc, char** argv)
{
version(BindSDL_Static){}
else
{
loadSDL();
loadSDLImage();
}
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
printf("SDL could not initialize! SDL_Error: %s", SDL_GetError());
return -1;
}
SDL_version sdl_version;
SDL_GetVersion(&sdl_version);
printf("SDL version: %u.%u.%u\n",cast(uint)sdl_version.major,cast(uint)sdl_version.minor,cast(uint)sdl_version.patch);
@ -692,6 +726,21 @@ int main(int argc, char** argv)
return -3;
}
}
else version(Android)
{
//gladLoadGL();
gladLoadGLES2(x => SDL_GL_GetProcAddress(x));
if(!ImGuiImplSDL2InitForOpenGL(launcher.window,launcher.gl_context))
{
printf("ImGui initialization failed!");
return -2;
}
if(!ImGui_ImplOpenGL3_Init("#version 100"))
{
printf("ImGui OpenGL initialization failed!");
return -3;
}
}
else
{
gladLoadGL();
@ -700,7 +749,8 @@ int main(int argc, char** argv)
printf("ImGui initialization failed!");
return -2;
}
if(!ImGuiImplOpenGL2Init())
//if(!ImGuiImplOpenGL2Init())
if(!ImGui_ImplOpenGL3_Init("#version 120"))
{
printf("ImGui OpenGL initialization failed!");
return -3;

View file

@ -1392,7 +1392,7 @@ struct UpgradeCollisionSystem
if(space_invaders.shoot_grid.test(id, data.location[i], cast(ubyte)(0xFF)))
{
Entity* entity = launcher.manager.getEntity(id);
if(entity.hasComponent(CShip.component_id))
if(entity && entity.hasComponent(CShip.component_id))
{
launcher.manager.sendEvent(id, EUpgrade());
launcher.manager.removeEntity(data.entity[i].id);

View file

@ -37,12 +37,17 @@ struct ECSJobUpdater
//pool.unregistExternalThread(thread_data);
if(jobs)Mallocator.dispose(jobs);
version(WebAssembly)pthread_key_delete(tls_key);
else version(Android)pthread_key_delete(tls_key);
}
version(WebAssembly)
{
__gshared pthread_key_t tls_key;
}
else version(Android)
{
__gshared pthread_key_t tls_key;
}
else static uint thread_id = 0;
ThreadPool pool;
@ -105,6 +110,7 @@ struct ECSJobUpdater
void onCreate(uint threads_count)
{
version(WebAssembly)pthread_key_create(&tls_key, null);
else version(Android)pthread_key_create(&tls_key, null);
pool.initialize();
thread_data = pool.registerExternalThread();
@ -116,6 +122,7 @@ struct ECSJobUpdater
uint getThreadID() @nogc nothrow
{
version(WebAssembly)return cast(int)pthread_getspecific(tls_key);
else version(Android)return cast(int)pthread_getspecific(tls_key);
else return thread_id;
}
@ -200,6 +207,11 @@ struct ECSJobUpdater
}
else job.execute();
}
else version(Android)
{
pthread_setspecific(tls_key, cast(void*)th_data.threadId);
job.execute();
}
else
{
updater.thread_id = th_data.threadId;

View file

@ -15,14 +15,13 @@
"../external/sources"
],
"dependencies": {
"bindbc-sdl":"0.13.0",
"bindbc-sdl":"0.19.0",
"ecs":{"path":"../../"}
},
"versions": [
"BindSDL_Image",
"SDL_2010"
],
"configurations" : [
{
"name" : "default",

View file

@ -2,8 +2,12 @@ module ecs_utils.gfx.buffer;
import bubel.ecs.std;
import glad.gl.gl;
import glad.gl.gles2;
//import glad.gl.gl;
//import glad.gl.gles2;
version(WebAssembly)import glad.gl.gles2;
else version(Android)import glad.gl.gles2;
else import glad.gl.gl;
extern(C):
@ -64,13 +68,13 @@ struct Buffer
void map(BindTarget target) nothrow
{
bind(target);
data.map_ptr = glMapBuffer(target,GL_WRITE_ONLY);
version(Android){}else data.map_ptr = glMapBuffer(target,GL_WRITE_ONLY);
}
void map(uint offset, uint size, BindTarget target, uint flags = MapFlagBits.write | MapFlagBits.flush_explict | MapFlagBits.invalidate_buffer) nothrow
{
bind(target);
data.map_ptr = glMapBufferRange(target,offset,size,flags);
version(Android){}else data.map_ptr = glMapBufferRange(target,offset,size,flags);
}
void flush(uint offset, uint size, BindTarget target) nothrow

View file

@ -6,7 +6,9 @@ import bubel.ecs.std;
import ecs_utils.gfx.shader;
import glad.gl.gl;
version(WebAssembly)import glad.gl.gles2;
else version(Android)import glad.gl.gles2;
else import glad.gl.gl;
//import mutils.serializer.json;

View file

@ -6,7 +6,9 @@ import bubel.ecs.std;
import ecs_utils.gfx.buffer;
import glad.gl.gl;
version(WebAssembly)import glad.gl.gles2;
else version(Android)import glad.gl.gles2;
else import glad.gl.gl;
//import mutils.serializer.json;
extern(C):

View file

@ -12,6 +12,7 @@ import ecs_utils.math.vector;
import bubel.ecs.block_allocator;
import bubel.ecs.vector;
version(WebAssembly)import glad.gl.gles2;
else version(Android)import glad.gl.gles2;
else import glad.gl.gl;
version = ver1;
@ -637,6 +638,7 @@ struct Renderer
if(threads[thread_id].block.items >= VertexBlock.max_items)
{
//pushBlock(threads[thread_id].block);
prepared_items += threads[thread_id].block.items;
threads[thread_id].blocks.add(threads[thread_id].block);
threads[thread_id].block = getBlock();
}
@ -667,6 +669,10 @@ struct Renderer
{
glDepthRangef(0,1);
}
else version(Android)
{
glDepthRangef(0,1);
}
else
{
glDepthRange(0,1);

View file

@ -4,7 +4,9 @@ import bindbc.sdl;
import bubel.ecs.std;
import glad.gl.gl;
version(WebAssembly)import glad.gl.gles2;
else version(Android)import glad.gl.gles2;
else import glad.gl.gl;
//version = ver1;
@ -67,10 +69,12 @@ struct Shader
}
version(WebAssembly)const char* glsl = "#version 100\n";
else version(Android)const char* glsl = "#version 100\n";
else const char* glsl = "#version 330\n";
const char* buffer = data.code.ptr;
char* ver;
version(WebAssembly)ver = cast(char*)"#define ver1 1\n#define GLES\n".ptr;
else version(Android)ver = cast(char*)"#define ver1 1\n#define GLES\n".ptr;
else ver = cast(char*)"#define ver1 1\n".ptr;
/*switch(__ecs_used_technique)
{

View file

@ -6,7 +6,9 @@ import bubel.ecs.std;
import ecs_utils.math.vector;
import glad.gl.gl;
version(WebAssembly)import glad.gl.gles2;
else version(Android)import glad.gl.gles2;
else import glad.gl.gl;
extern(C):

View file

@ -22,7 +22,9 @@ else :
import bindbc.sdl;
import glad.gl.gl;
version(WebAssembly)import glad.gl.gles2;
else version(Android)import glad.gl.gles2;
else import glad.gl.gl;
import cimgui.cimgui;
@ -210,7 +212,7 @@ static void ImGui_ImplSDL2_UpdateMousePosAndButtons()
// SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger the OS window resize cursor.
// The function is only supported from SDL 2.0.4 (released Jan 2016)
bool any_mouse_button_down = ImGui::IsAnyMouseDown();
bool any_mouse_button_down = ImGui.IsAnyMouseDown();
SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE);
//#else*/
if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS)
@ -291,10 +293,11 @@ void ImGuiImplSDL2NewFrame(SDL_Window* window)
int w, h;
int display_w, display_h;
SDL_GetWindowSize(window, &w, &h);
SDL_GL_GetDrawableSize(window, &display_w, &display_h);
// SDL_GL_GetDrawableSize(window, &display_w, &display_h); FIXME: you see
io.DisplaySize = ImVec2(cast(float)w, cast(float)h);
if (w > 0 && h > 0)
io.DisplayFramebufferScale = ImVec2(cast(float)display_w / w, cast(float)display_h / h);
// if (w > 0 && h > 0)
// io.DisplayFramebufferScale = ImVec2(cast(float)display_w / w, cast(float)display_h / h);
io.DisplayFramebufferScale = ImVec2(1,1);
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
frequency = SDL_GetPerformanceFrequency();
@ -310,89 +313,460 @@ void ImGuiImplSDL2NewFrame(SDL_Window* window)
}
__gshared GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
__gshared char[32] g_GlslVersionString = ""; // Specified by user or detected based on compile time GL settings.
//__gshared GLuint g_FontTexture = 0;
__gshared GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
__gshared int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location
__gshared int g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location
__gshared uint g_VboHandle = 0, g_ElementsHandle = 0;
bool ImGuiImplOpenGL2Init()
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
// Setup back-end capabilities flags
ImGuiIO* io = igGetIO();
io.BackendRendererName = "imgui_impl_opengl2";
io.BackendRendererName = "imgui_impl_opengl3";
// Store GLSL version string so we can refer to it later in case we recreate shaders.
// Note: GLSL version is NOT the same as GL version. Leave this to null if unsure.
/*#if defined(IMGUI_IMPL_OPENGL_ES2)
if (glsl_version == null)
glsl_version = "#version 100";
#elif defined(IMGUI_IMPL_OPENGL_ES3)
if (glsl_version == null)
glsl_version = "#version 300 es";
#elif defined(__APPLE__)
if (glsl_version == null)
glsl_version = "#version 150";
#else
if (glsl_version == null)
glsl_version = "#version 130";
#endif
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));*/
//const (char)*glsl_version = "#version 100";
import core.stdc.string;
strcpy(g_GlslVersionString.ptr, glsl_version);
strcat(g_GlslVersionString.ptr, "\n");
// Dummy construct to make it easily visible in the IDE and debugger which GL loader has been selected.
// The code actually never uses the 'gl_loader' variable! It is only here so you can read it!
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
// you are likely to get a crash below.
// You can explicitly select a loader by using '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
/*const char* gl_loader = "Unknown";
IM_UNUSED(gl_loader);
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
gl_loader = "GL3W";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
gl_loader = "GLEW";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
gl_loader = "GLAD";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
gl_loader = "glbinding2";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
gl_loader = "glbinding3";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
gl_loader = "custom";
#else
gl_loader = "none";
#endif*/
// Make a dummy GL call (we don't actually need the result)
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
/*GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);*/
return true;
}
void ImGuiImplOpenGL2Shutdown()
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
{
ImGuiImplOpenGL2DestroyDeviceObjects();
}
void ImGuiImplOpenGL2NewFrame()
{
if (!g_FontTexture)
ImGuiImplOpenGL2CreateDeviceObjects();
}
static void ImGuiImplOpenGL2SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height)
{
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill.
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
//glDisable(GL_LIGHTING);
//glDisable(GL_COLOR_MATERIAL);
glEnable(GL_SCISSOR_TEST);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnable(GL_TEXTURE_2D);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// #ifdef GL_POLYGON_MODE
// glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// #endif
// If you are using this code with non-legacy OpenGL header/contexts (which you should not, prefer using imgui_impl_opengl3.cpp!!),
// you may need to backup/reset/restore current shader using the lines below. DO NOT MODIFY THIS FILE! Add the code in your calling function:
// GLint last_program;
// glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
// glUseProgram(0);
// ImGui_ImplOpenGL2_RenderDrawData(...);
// glUseProgram(last_program)
// Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
bool clip_origin_lower_left = true;
// #if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__)
// GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&current_clip_origin);
// if (current_clip_origin == GL_UPPER_LEFT)
// clip_origin_lower_left = false;
// #endif
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data.DisplayPos (top left) to draw_data.DisplayPos+data_data.DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
glViewport(0, 0, cast(GLsizei)fb_width, cast(GLsizei)fb_height);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(draw_data.DisplayPos.x, draw_data.DisplayPos.x + draw_data.DisplaySize.x, draw_data.DisplayPos.y + draw_data.DisplaySize.y, draw_data.DisplayPos.y, -1.0f, +1.0f);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
float L = draw_data.DisplayPos.x;
float R = draw_data.DisplayPos.x + draw_data.DisplaySize.x;
float T = draw_data.DisplayPos.y;
float B = draw_data.DisplayPos.y + draw_data.DisplaySize.y;
if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left
const float[4][4] ortho_projection =
[
[ 2.0f/(R-L), 0.0f, 0.0f, 0.0f ],
[ 0.0f, 2.0f/(T-B), 0.0f, 0.0f ],
[ 0.0f, 0.0f, -1.0f, 0.0f ],
[ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f ],
];
glUseProgram(g_ShaderHandle);
glUniform1i(g_AttribLocationTex, 0);
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
// #ifdef GL_SAMPLER_BINDING
// glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
// #endif
// (void)vertex_array_object;
// #ifndef IMGUI_IMPL_OPENGL_ES2
// glBindVertexArray(vertex_array_object);
// #endif
// Bind vertex/index buffers and setup attributes for ImDrawVert
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
glEnableVertexAttribArray(g_AttribLocationVtxPos);
glEnableVertexAttribArray(g_AttribLocationVtxUV);
glEnableVertexAttribArray(g_AttribLocationVtxColor);
glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, ImDrawVert.sizeof, cast(GLvoid*)ImDrawVert.pos.offsetof);
glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, ImDrawVert.sizeof, cast(GLvoid*)ImDrawVert.uv.offsetof);
glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, ImDrawVert.sizeof, cast(GLvoid*)ImDrawVert.col.offsetof);
}
// OpenGL2 Render function.
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so.
void ImGuiImplOpenGL2RenderDrawData(ImDrawData* draw_data)
void ImGui_ImplOpenGL3_Shutdown()
{
ImGui_ImplOpenGL3_DestroyDeviceObjects();
}
void ImGui_ImplOpenGL3_NewFrame()
{
if (!g_ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects();
}
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
{
// Backup GL state
GLint last_texture, last_array_buffer;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
// #ifndef IMGUI_IMPL_OPENGL_ES2
// GLint last_vertex_array;
// glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
// #endif
// Parse GLSL version string
import core.stdc.stdio;
int glsl_version = 130;
sscanf(g_GlslVersionString.ptr, "#version %d", &glsl_version);
const GLchar* vertex_shader_glsl_120 =
"uniform mat4 ProjMtx;\n
attribute vec2 Position;\n
attribute vec2 UV;\n
attribute vec4 Color;\n
varying vec2 Frag_UV;\n
varying vec4 Frag_Color;\n
void main()\n
{\n
Frag_UV = UV;\n
Frag_Color = Color;\n
gl_Position = ProjMtx * vec4(Position.xy,0,1);\n
}\n";
const GLchar* vertex_shader_glsl_130 =
"uniform mat4 ProjMtx;\n
in vec2 Position;\n
in vec2 UV;\n
in vec4 Color;\n
out vec2 Frag_UV;\n
out vec4 Frag_Color;\n
void main()\n
{\n
Frag_UV = UV;\n
Frag_Color = Color;\n
gl_Position = ProjMtx * vec4(Position.xy,0,1);\n
}\n";
const GLchar* vertex_shader_glsl_300_es =
"precision mediump float;\n
layout (location = 0) in vec2 Position;\n
layout (location = 1) in vec2 UV;\n
layout (location = 2) in vec4 Color;\n
uniform mat4 ProjMtx;\n
out vec2 Frag_UV;\n
out vec4 Frag_Color;\n
void main()\n
{\n
Frag_UV = UV;\n
Frag_Color = Color;\n
gl_Position = ProjMtx * vec4(Position.xy,0,1);\n
}\n";
const GLchar* vertex_shader_glsl_410_core =
"layout (location = 0) in vec2 Position;\n
layout (location = 1) in vec2 UV;\n
layout (location = 2) in vec4 Color;\n
uniform mat4 ProjMtx;\n
out vec2 Frag_UV;\n
out vec4 Frag_Color;\n
void main()\n
{\n
Frag_UV = UV;\n
Frag_Color = Color;\n
gl_Position = ProjMtx * vec4(Position.xy,0,1);\n
}\n";
const GLchar* fragment_shader_glsl_120 =
"#ifdef GL_ES\n
precision mediump float;\n
#endif\n
uniform sampler2D Texture;\n
varying vec2 Frag_UV;\n
varying vec4 Frag_Color;\n
void main()\n
{\n
gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n
}\n";
const GLchar* fragment_shader_glsl_130 =
"uniform sampler2D Texture;\n
in vec2 Frag_UV;\n
in vec4 Frag_Color;\n
out vec4 Out_Color;\n
void main()\n
{\n
Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n
}\n";
const GLchar* fragment_shader_glsl_300_es =
"precision mediump float;\n
uniform sampler2D Texture;\n
in vec2 Frag_UV;\n
in vec4 Frag_Color;\n
layout (location = 0) out vec4 Out_Color;\n
void main()\n
{\n
Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n
}\n";
const GLchar* fragment_shader_glsl_410_core =
"in vec2 Frag_UV;\n
in vec4 Frag_Color;\n
uniform sampler2D Texture;\n
layout (location = 0) out vec4 Out_Color;\n
void main()\n
{\n
Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n
}\n";
// Select shaders matching our GLSL versions
const (char)* vertex_shader = null;
const (char)* fragment_shader = null;
if (glsl_version < 130)
{
vertex_shader = vertex_shader_glsl_120;
fragment_shader = fragment_shader_glsl_120;
}
else if (glsl_version >= 410)
{
vertex_shader = vertex_shader_glsl_410_core;
fragment_shader = fragment_shader_glsl_410_core;
}
else if (glsl_version == 300)
{
vertex_shader = vertex_shader_glsl_300_es;
fragment_shader = fragment_shader_glsl_300_es;
}
else
{
vertex_shader = vertex_shader_glsl_130;
fragment_shader = fragment_shader_glsl_130;
}
// Create shaders
const (char)*[2] vertex_shader_with_version = [ g_GlslVersionString.ptr, vertex_shader ];
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(g_VertHandle, 2, vertex_shader_with_version.ptr, null);
glCompileShader(g_VertHandle);
CheckShader(g_VertHandle, "vertex shader");
const (char)*[2] fragment_shader_with_version = [ g_GlslVersionString.ptr, fragment_shader ];
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(g_FragHandle, 2, fragment_shader_with_version.ptr, null);
glCompileShader(g_FragHandle);
CheckShader(g_FragHandle, "fragment shader");
g_ShaderHandle = glCreateProgram();
glAttachShader(g_ShaderHandle, g_VertHandle);
glAttachShader(g_ShaderHandle, g_FragHandle);
glLinkProgram(g_ShaderHandle);
CheckProgram(g_ShaderHandle, "shader program");
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
g_AttribLocationVtxPos = glGetAttribLocation(g_ShaderHandle, "Position");
g_AttribLocationVtxUV = glGetAttribLocation(g_ShaderHandle, "UV");
g_AttribLocationVtxColor = glGetAttribLocation(g_ShaderHandle, "Color");
// Create buffers
glGenBuffers(1, &g_VboHandle);
glGenBuffers(1, &g_ElementsHandle);
ImGui_ImplOpenGL3_CreateFontsTexture();
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
// #ifndef IMGUI_IMPL_OPENGL_ES2
// glBindVertexArray(last_vertex_array);
// #endif
return true;
}
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
{
if (g_VboHandle) { glDeleteBuffers(1, &g_VboHandle); g_VboHandle = 0; }
if (g_ElementsHandle) { glDeleteBuffers(1, &g_ElementsHandle); g_ElementsHandle = 0; }
if (g_ShaderHandle && g_VertHandle) { glDetachShader(g_ShaderHandle, g_VertHandle); }
if (g_ShaderHandle && g_FragHandle) { glDetachShader(g_ShaderHandle, g_FragHandle); }
if (g_VertHandle) { glDeleteShader(g_VertHandle); g_VertHandle = 0; }
if (g_FragHandle) { glDeleteShader(g_FragHandle); g_FragHandle = 0; }
if (g_ShaderHandle) { glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; }
ImGui_ImplOpenGL3_DestroyFontsTexture();
}
static bool CheckShader(GLuint handle, const char* desc)
{
GLint status = 0, log_length = 0;
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
/*if (cast(GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize(cast(int)(log_length + 1));
glGetShaderInfoLog(handle, log_length, null, cast(GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}*/
return cast(GLboolean)status == GL_TRUE;
}
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
static bool CheckProgram(GLuint handle, const char* desc)
{
GLint status = 0, log_length = 0;
glGetProgramiv(handle, GL_LINK_STATUS, &status);
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
/*if (cast(GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize(cast(int)(log_length + 1));
glGetProgramInfoLog(handle, log_length, null, cast(GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}*/
return cast(GLboolean)status == GL_TRUE;
}
bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO* io = igGetIO();
ubyte* pixels;
int width, height, bpp;
ImFontAtlas_GetTexDataAsRGBA32(io.Fonts,&pixels, &width, &height, &bpp);
//io.Fonts.GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &g_FontTexture);
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// #ifdef GL_UNPACK_ROW_LENGTH
// glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
// #endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts.TexID = cast(ImTextureID)cast(sizediff_t)g_FontTexture;
// Restore state
glBindTexture(GL_TEXTURE_2D, last_texture);
return true;
}
void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
if (g_FontTexture)
{
ImGuiIO* io = igGetIO();
glDeleteTextures(1, &g_FontTexture);
io.Fonts.TexID = null;
g_FontTexture = 0;
}
}
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = cast(int)(draw_data.DisplaySize.x * draw_data.FramebufferScale.x);
int fb_height = cast(int)(draw_data.DisplaySize.y * draw_data.FramebufferScale.y);
if (fb_width == 0 || fb_height == 0)
if (fb_width <= 0 || fb_height <= 0)
return;
// Backup GL state
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, cast(GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
GLint[2] last_polygon_mode; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode.ptr);
// #ifdef GL_SAMPLER_BINDING
// GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler);
// #endif
GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
// #ifndef IMGUI_IMPL_OPENGL_ES2
// GLint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array_object);
// #endif
// #ifdef GL_POLYGON_MODE
// GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
// #endif
GLint[4] last_viewport; glGetIntegerv(GL_VIEWPORT, last_viewport.ptr);
GLint[4] last_scissor_box; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box.ptr);
glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT);
GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, cast(GLint*)&last_blend_src_rgb);
GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, cast(GLint*)&last_blend_dst_rgb);
GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, cast(GLint*)&last_blend_src_alpha);
GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, cast(GLint*)&last_blend_dst_alpha);
GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, cast(GLint*)&last_blend_equation_rgb);
GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, cast(GLint*)&last_blend_equation_alpha);
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
// Setup desired GL state
ImGuiImplOpenGL2SetupRenderState(draw_data, fb_width, fb_height);
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
GLuint vertex_array_object = 0;
// #ifndef IMGUI_IMPL_OPENGL_ES2
// glGenVertexArrays(1, &vertex_array_object);
// #endif
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data.DisplayPos; // (0,0) unless using multi-viewports
@ -401,23 +775,22 @@ void ImGuiImplOpenGL2RenderDrawData(ImDrawData* draw_data)
// Render command lists
for (int n = 0; n < draw_data.CmdListsCount; n++)
{
ImDrawList* cmd_list = draw_data.CmdLists[n];
ImDrawVert* vtx_buffer = cmd_list.VtxBuffer.Data;
ImDrawIdx* idx_buffer = cmd_list.IdxBuffer.Data;
glVertexPointer(2, GL_FLOAT, ImDrawVert.sizeof, cast(const GLvoid*)(cast(const char*)vtx_buffer + ImDrawVert.pos.offsetof));
glTexCoordPointer(2, GL_FLOAT, ImDrawVert.sizeof, cast(const GLvoid*)(cast(const char*)vtx_buffer + ImDrawVert.uv.offsetof));
glColorPointer(4, GL_UNSIGNED_BYTE, ImDrawVert.sizeof, cast(const GLvoid*)(cast(const char*)vtx_buffer + ImDrawVert.col.offsetof));
const ImDrawList* cmd_list = draw_data.CmdLists[n];
// Upload vertex/index buffers
glBufferData(GL_ARRAY_BUFFER, cast(GLsizeiptr)cmd_list.VtxBuffer.Size * ImDrawVert.sizeof, cast(const GLvoid*)cmd_list.VtxBuffer.Data, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, cast(GLsizeiptr)cmd_list.IdxBuffer.Size * ImDrawIdx.sizeof, cast(const GLvoid*)cmd_list.IdxBuffer.Data, GL_STREAM_DRAW);
for (int cmd_i = 0; cmd_i < cmd_list.CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list.CmdBuffer.Data[cmd_i];
if (pcmd.UserCallback)
if (pcmd.UserCallback != null)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
/*if (pcmd.UserCallback == &ImDrawCallback_ResetRenderState)
ImGui_ImplOpenGL2_SetupRenderState(draw_data, fb_width, fb_height);
else*/
// if (pcmd.UserCallback == ImDrawCallback_ResetRenderState)
// ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
// else
pcmd.UserCallback(cmd_list, pcmd);
}
else
@ -435,74 +808,43 @@ void ImGuiImplOpenGL2RenderDrawData(ImDrawData* draw_data)
glScissor(cast(int)clip_rect.x, cast(int)(fb_height - clip_rect.w), cast(int)(clip_rect.z - clip_rect.x), cast(int)(clip_rect.w - clip_rect.y));
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, cast(GLuint)pcmd.TextureId);
glDrawElements(GL_TRIANGLES, cast(GLsizei)pcmd.ElemCount, ImDrawIdx.sizeof == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer);
glBindTexture(GL_TEXTURE_2D, cast(GLuint)cast(sizediff_t)pcmd.TextureId);
// #if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
// if (g_GlVersion >= 320)
// glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd.ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd.IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd.VtxOffset);
// else
// #endif
glDrawElements(GL_TRIANGLES, cast(GLsizei)pcmd.ElemCount, ImDrawIdx.sizeof == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, cast(void*)cast(sizediff_t)(pcmd.IdxOffset * ImDrawIdx.sizeof));
}
}
idx_buffer += pcmd.ElemCount;
}
}
// Destroy the temporary VAO
// #ifndef IMGUI_IMPL_OPENGL_ES2
// glDeleteVertexArrays(1, &vertex_array_object);
// #endif
// Restore modified GL state
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glBindTexture(GL_TEXTURE_2D, cast(GLuint)last_texture);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glPopAttrib();
glPolygonMode(GL_FRONT, cast(GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, cast(GLenum)last_polygon_mode[1]);
glUseProgram(last_program);
glBindTexture(GL_TEXTURE_2D, last_texture);
// #ifdef GL_SAMPLER_BINDING
// glBindSampler(0, last_sampler);
// #endif
glActiveTexture(last_active_texture);
// #ifndef IMGUI_IMPL_OPENGL_ES2
// glBindVertexArray(last_vertex_array_object);
// #endif
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
// #ifdef GL_POLYGON_MODE
// glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
// #endif
glViewport(last_viewport[0], last_viewport[1], cast(GLsizei)last_viewport[2], cast(GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], cast(GLsizei)last_scissor_box[2], cast(GLsizei)last_scissor_box[3]);
}
bool ImGuiImplOpenGL2CreateFontsTexture()
{
// Build texture atlas
ImGuiIO* io = igGetIO();
ubyte* pixels;
int width, height;
int bpp;
ImFontAtlas_GetTexDataAsRGBA32(io.Fonts, &pixels, &width, &height, &bpp); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &g_FontTexture);
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts.TexID = cast(ImTextureID)g_FontTexture;
// Restore state
glBindTexture(GL_TEXTURE_2D, last_texture);
return true;
}
void ImGuiImplOpenGL2DestroyFontsTexture()
{
if (g_FontTexture)
{
ImGuiIO* io = igGetIO();
glDeleteTextures(1, &g_FontTexture);
io.Fonts.TexID = null;
g_FontTexture = 0;
}
}
bool ImGuiImplOpenGL2CreateDeviceObjects()
{
return ImGuiImplOpenGL2CreateFontsTexture();
}
void ImGuiImplOpenGL2DestroyDeviceObjects()
{
ImGuiImplOpenGL2DestroyFontsTexture();
}

View file

@ -58,7 +58,15 @@ version(D_BetterC)
}
}
version(Android)
{
alias pthread_key_t = uint;
extern (C) int pthread_key_create(pthread_key_t *, void* function(void *)) @nogc nothrow;
extern (C) int pthread_key_delete(pthread_key_t) @nogc nothrow;
extern (C) void* pthread_getspecific(pthread_key_t) @nogc nothrow;
extern (C) int pthread_setspecific(pthread_key_t, const void *) @nogc nothrow;
}
version(WebAssembly)
{

View file

@ -23,6 +23,8 @@ tests_src = [
]
betterC_opt = get_option('betterC')
BuildDemos_opt = get_option('BuildDemos')
LTO_otp = get_option('LTO')
comp = meson.get_compiler('d')
@ -31,16 +33,41 @@ comp_id = comp.get_id()
args = []
link_args = []
if comp_id == 'gcc'
args += '-pthread'
link_args += '-pthread'
endif
if LTO_otp
if comp_id == 'gcc'
args += '-flto'
link_args += '-flto'
elif comp_id == 'llvm'
args += '-flto=thin'
link_args += '-flto=thin'
else
message('LTO don\'t work with DMD')
endif
endif
if betterC_opt
if comp_id == 'gcc'
args += ['-flto','-fno-druntime']
link_args += ['-flto','-fno-druntime']
else
args += '-betterC'
link_args += '-betterC'
endif
endif
inc = include_directories('source/')
tests_inc = include_directories('source/')
ecs_lib = shared_library('ecs', src, include_directories : [tests_inc, inc], d_args: args, link_args: link_args)
ecs_lib = library('ecs', src, include_directories : [tests_inc, inc], d_args: args, link_args: link_args)
executable('tests', tests_src, include_directories : [tests_inc, inc], d_args: args, link_args: link_args, link_with: ecs_lib)
if BuildDemos_opt
subdir('demos/utils')
subdir('demos')
endif

View file

@ -1 +1,3 @@
option('betterC', type: 'boolean', value: false)
option('BuildDemos', type: 'boolean', value: false)
option('LTO', type: 'boolean', value: false)

View file

@ -15,7 +15,7 @@ struct IDManager
/************************************************************************************************************************
Get new ID.
*/
pragma(inline, false) EntityID getNewID() nothrow @nogc
EntityID getNewID() nothrow @nogc
{
int current = m_stack_top.atomicOp!"-="(1) + 1;
if (current < 0)

View file

@ -906,7 +906,8 @@ export struct EntityManager
input_data.thread_id = cast(typeof(input_data.thread_id))threadID();
}//*/
static foreach (iii, comp_info; components_info.req)
///FIXME: should be "components_info.req()" but it's not compile with GCC
static foreach (iii, comp_info; components_info.m_req[0 .. components_info.m_req_counter])
{
__traits(getMember, input_data, comp_info.name) = (cast(ForeachType!(typeof(__traits(getMember,
Sys.EntitiesData, comp_info.name)))*)(
@ -914,7 +915,7 @@ export struct EntityManager
.. entities_count];
}
static foreach (iii, comp_info; components_info.optional)
static foreach (iii, comp_info; components_info.m_optional[0 .. components_info.m_optional_counter])
{
if (system.m_optional_components[iii] < info.deltas.length
&& info.deltas[system.m_optional_components[iii]] != 0)
@ -1344,9 +1345,9 @@ export struct EntityManager
"Can't call function with system which hasn't EntitesData structure.");
static assert(__traits(hasMember, Sys, "onUpdate"),
"Can't call function with system which hasn't onUpdate function callback.");
static assert(is(SetFunctionAttributes!(T, functionLinkage!(s.onUpdate),
functionAttributes!(s.onUpdate)) == typeof(&s.onUpdate)),
"Function must match system update function.");
// static assert(is(SetFunctionAttributes!(T, functionLinkage!(s.onUpdate),
// functionAttributes!(s.onUpdate)) == typeof(&s.onUpdate)),
// "Function must match system update function."); FIXME: It's lead to crash on android build
static assert(__traits(hasMember, Sys, "system_id"), "Sys must be system type.");
System* system = getSystem(Sys.system_id);
@ -3105,7 +3106,7 @@ export struct EntityManager
// has_work |= removeEntities();
has_work |= updateEvents();
//id_manager.optimize();
id_manager.optimize();
has_work |= updateBlocks();
has_work |= changeEntities();
has_work |= removeEntities();

View file

@ -112,7 +112,7 @@ else version (D_BetterC)
{
private const uint max_alloca = 10000;
private __gshared byte[max_alloca] alloca_array;
private uint alloca_pos = 0;
private __gshared uint alloca_pos = 0;
export extern (C) void* __alloca(size_t length) @nogc nothrow
{
if (alloca_pos + length > max_alloca)
@ -158,7 +158,32 @@ static struct Mallocator
static if (__traits(isPOD, T))
{
static immutable T init = T.init;
__gshared immutable T init = T.init;
foreach (i; 0 .. ret.length)
{
memcpy(&ret[i], &init, T.sizeof);
}
}
else
{
static import std.conv;
foreach (i; 0 .. ret.length)
{
std.conv.emplace(&ret[i]);
}
}
return ret;
}
static T[] alignMakeArray(T)(size_t length, size_t alignment) nothrow @nogc
{
T[] ret = (cast(T*) alignAlloc(T.sizeof * length, alignment))[0 .. length];
static if (__traits(isPOD, T))
{
__gshared immutable T init = T.init;
foreach (i; 0 .. ret.length)
{
@ -206,7 +231,7 @@ static struct Mallocator
static if (__traits(isPOD, T))
{
static immutable T init = T.init;
__gshared immutable T init = T.init;
memcpy(ret, &init, T.sizeof);
}
else static if (is(T == struct))

View file

@ -93,6 +93,13 @@ struct TestEvent2
float a;
}
static struct CPosition
{
mixin ECS.Component;
float x;
float y;
}
static struct TestComp
{
mixin ECS.Component; //__gshared ushort component_id;
@ -186,6 +193,52 @@ static struct TestComp5
}
}
struct EverySystem
{
mixin ECS.System;
struct EntitiesData
{
uint length;
Entity[] entity;
CPosition[] pos;
}
void onUpdate(EntitiesData data)
{
foreach(i;0..data.length)
{
data.pos[i].x++;
data.pos[i].y++;
}
}
void iterate(EntitiesData data)
{
foreach(i;0..data.length)
{
data.pos[i].x++;
data.pos[i].y++;
}
}
void free(EntitiesData data)
{
foreach(i;0..data.length)
{
gEM.removeEntity(data.entity[i].id);
}
}
void addOne(EntitiesData data)
{
foreach(i;0..data.length)
{
gEM.addComponents(data.entity[i].id, TestComp2());
}
}
}
struct ChangeTestSystem
{
mixin ECS.System!16; //__gshared ushort system_id;
@ -648,6 +701,7 @@ else:
gEM.registerComponent!TestComp;
gEM.registerComponent!TestComp3;
gEM.registerComponent!TestComp5;
gEM.registerComponent!CPosition;
gEM.registerEvent!TestEvent;
gEM.registerEvent!TestEvent2;
@ -669,6 +723,7 @@ else:
gEM.registerSystem!EmptySystem(2);
gEM.registerSystem!EmptyEventSystem(2);
gEM.registerSystem!EventSystem(2);
gEM.registerSystem!EverySystem(0);
//gEM.registerSystem!TestSystemWithHighPriority(100);
//gEM.registerSystem!TestSystem2(0);
gEM.endRegister();
@ -693,6 +748,62 @@ else:
//dur = (MonoTime.currTime - time).total!"usecs";
//writeln("Template allocating: ", dur, " usecs");
printf("Template allocating: %f usecs\n", cast(float)(Time.getUSecTime() - time));
time = Time.getUSecTime();
ushort[1] empty_ids = [CPosition.component_id];
EntityTemplate* tmpl_empty = gEM.allocateTemplate(empty_ids);
gEM.commit();
time = Time.getUSecTime();
foreach(i;0..4_000_000)gEM.addEntity(tmpl_empty);
gEM.commit();
foreach(i;0..4_000_000)gEM.addEntity(tmpl_empty);
gEM.commit();
foreach(i;0..2_000_000)gEM.addEntity(tmpl_empty);
gEM.commit();
printf("Adding 1M entities: %f usecs\n", cast(float)(Time.getUSecTime() - time));
gEM.commit();
time = Time.getUSecTime();
gEM.callEntitiesFunction!EverySystem(&gEM.getSystem!EverySystem().iterate);
printf("Iterate 1M entities: %f usecs\n", cast(float)(Time.getUSecTime() - time));
gEM.begin();
time = Time.getUSecTime();
gEM.update();
printf("Iterate 1M entities (update): %f usecs\n", cast(float)(Time.getUSecTime() - time));
gEM.end();
time = Time.getUSecTime();
gEM.callEntitiesFunction!EverySystem(&gEM.getSystem!EverySystem().free);
gEM.commit();
printf("Deleting 1M entities: %f usecs\n", cast(float)(Time.getUSecTime() - time));
time = Time.getUSecTime();
foreach(i;0..4_000_000)gEM.addEntity(tmpl_empty);
gEM.commit();
foreach(i;0..4_000_000)gEM.addEntity(tmpl_empty);
gEM.commit();
foreach(i;0..2_000_000)gEM.addEntity(tmpl_empty);
gEM.commit();
printf("Adding 1M entities (prealloc): %f usecs\n", cast(float)(Time.getUSecTime() - time));
gEM.commit();
time = Time.getUSecTime();
gEM.callEntitiesFunction!EverySystem(&gEM.getSystem!EverySystem().addOne);
gEM.commit();
printf("Adding 1M component: %f usecs\n", cast(float)(Time.getUSecTime() - time));
gEM.commit();
gEM.callEntitiesFunction!EverySystem(&gEM.getSystem!EverySystem().free);
gEM.commit();
time = Time.getUSecTime();
EntityID entity;