diff --git a/.gitignore b/.gitignore index e595637..636ea36 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ !skeleton.html !meson.build !meson_options.txt -!compile_wasm.py \ No newline at end of file +!compile_wasm.py +!compile_android.py \ No newline at end of file diff --git a/compile_android.py b/compile_android.py new file mode 100644 index 0000000..15f04ea --- /dev/null +++ b/compile_android.py @@ -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') diff --git a/demos/.gitignore b/demos/.gitignore index 1ac2a4a..974bbb2 100644 --- a/demos/.gitignore +++ b/demos/.gitignore @@ -14,4 +14,5 @@ !cimgui.bc !emscripten_shell.html !emscripten_multi_shell.html +!compile_android.py .dub \ No newline at end of file diff --git a/demos/compile_android.py b/demos/compile_android.py new file mode 100644 index 0000000..2e3f967 --- /dev/null +++ b/demos/compile_android.py @@ -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') + diff --git a/demos/dub.json b/demos/dub.json index 1c1fe65..417b831 100644 --- a/demos/dub.json +++ b/demos/dub.json @@ -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" : [ { diff --git a/demos/external/android/bindbc/loader/package.d b/demos/external/android/bindbc/loader/package.d new file mode 100644 index 0000000..3ed4e31 --- /dev/null +++ b/demos/external/android/bindbc/loader/package.d @@ -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; \ No newline at end of file diff --git a/demos/external/android/bindbc/loader/sharedlib.d b/demos/external/android/bindbc/loader/sharedlib.d new file mode 100644 index 0000000..ab6c444 --- /dev/null +++ b/demos/external/android/bindbc/loader/sharedlib.d @@ -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."); \ No newline at end of file diff --git a/demos/external/android/bindbc/loader/system.d b/demos/external/android/bindbc/loader/system.d new file mode 100644 index 0000000..c721100 --- /dev/null +++ b/demos/external/android/bindbc/loader/system.d @@ -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; +} \ No newline at end of file diff --git a/demos/external/sources/mmutils/thread_pool.d b/demos/external/sources/mmutils/thread_pool.d index 52136d4..b830026 100644 --- a/demos/external/sources/mmutils/thread_pool.d +++ b/demos/external/sources/mmutils/thread_pool.d @@ -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) diff --git a/demos/external/wasm_imports/bindbc/sdl/dynload.d b/demos/external/wasm_imports/bindbc/sdl/dynload.d index 0aa3b84..3a15c97 100644 --- a/demos/external/wasm_imports/bindbc/sdl/dynload.d +++ b/demos/external/wasm_imports/bindbc/sdl/dynload.d @@ -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; diff --git a/demos/external/wasm_imports/bindbc/sdl/image.d b/demos/external/wasm_imports/bindbc/sdl/image.d index 8cf160e..8af6e81 100644 --- a/demos/external/wasm_imports/bindbc/sdl/image.d +++ b/demos/external/wasm_imports/bindbc/sdl/image.d @@ -229,8 +229,8 @@ else { } private { - SharedLib lib; - SDLImageSupport loadedVersion; + __gshared SharedLib lib; + __gshared SDLImageSupport loadedVersion; } void unloadSDLImage() diff --git a/demos/libs/armeabi-v7a/libcimgui.so b/demos/libs/armeabi-v7a/libcimgui.so new file mode 100755 index 0000000..5dca812 Binary files /dev/null and b/demos/libs/armeabi-v7a/libcimgui.so differ diff --git a/demos/source/app.d b/demos/source/app.d index ede93b5..003922b 100644 --- a/demos/source/app.d +++ b/demos/source/app.d @@ -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,13 +749,14 @@ 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; } } - + ImFontConfig* config = ImFontConfig_ImFontConfig(); ImGuiIO* io = igGetIO(); const ushort* font_ranges = ImFontAtlas_GetGlyphRangesDefault(io.Fonts); diff --git a/demos/source/demos/space_invaders.d b/demos/source/demos/space_invaders.d index 5615f0d..45dd06d 100644 --- a/demos/source/demos/space_invaders.d +++ b/demos/source/demos/space_invaders.d @@ -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); diff --git a/demos/source/game_core/job_updater.d b/demos/source/game_core/job_updater.d index 47d2423..025da6e 100644 --- a/demos/source/game_core/job_updater.d +++ b/demos/source/game_core/job_updater.d @@ -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; diff --git a/demos/utils/dub.json b/demos/utils/dub.json index d8ecfb1..5302fd8 100644 --- a/demos/utils/dub.json +++ b/demos/utils/dub.json @@ -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", diff --git a/demos/utils/source/ecs_utils/gfx/buffer.d b/demos/utils/source/ecs_utils/gfx/buffer.d index b11cb85..bfdad62 100644 --- a/demos/utils/source/ecs_utils/gfx/buffer.d +++ b/demos/utils/source/ecs_utils/gfx/buffer.d @@ -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 diff --git a/demos/utils/source/ecs_utils/gfx/material.d b/demos/utils/source/ecs_utils/gfx/material.d index fbd88b9..1bde9c8 100644 --- a/demos/utils/source/ecs_utils/gfx/material.d +++ b/demos/utils/source/ecs_utils/gfx/material.d @@ -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; diff --git a/demos/utils/source/ecs_utils/gfx/mesh.d b/demos/utils/source/ecs_utils/gfx/mesh.d index 20d5855..e953778 100644 --- a/demos/utils/source/ecs_utils/gfx/mesh.d +++ b/demos/utils/source/ecs_utils/gfx/mesh.d @@ -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): diff --git a/demos/utils/source/ecs_utils/gfx/renderer.d b/demos/utils/source/ecs_utils/gfx/renderer.d index fcd6a88..93d009e 100644 --- a/demos/utils/source/ecs_utils/gfx/renderer.d +++ b/demos/utils/source/ecs_utils/gfx/renderer.d @@ -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); diff --git a/demos/utils/source/ecs_utils/gfx/shader.d b/demos/utils/source/ecs_utils/gfx/shader.d index 5e24d9a..e941289 100644 --- a/demos/utils/source/ecs_utils/gfx/shader.d +++ b/demos/utils/source/ecs_utils/gfx/shader.d @@ -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) { diff --git a/demos/utils/source/ecs_utils/gfx/texture.d b/demos/utils/source/ecs_utils/gfx/texture.d index d4e2089..7affed3 100644 --- a/demos/utils/source/ecs_utils/gfx/texture.d +++ b/demos/utils/source/ecs_utils/gfx/texture.d @@ -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): diff --git a/demos/utils/source/ecs_utils/imgui_bind.d b/demos/utils/source/ecs_utils/imgui_bind.d index b6572cb..a65b6b2 100644 --- a/demos/utils/source/ecs_utils/imgui_bind.d +++ b/demos/utils/source/ecs_utils/imgui_bind.d @@ -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, ¤t_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*)¤t_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 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 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(); } \ No newline at end of file diff --git a/demos/utils/source/ecs_utils/utils.d b/demos/utils/source/ecs_utils/utils.d index 8ce5cd1..144d24f 100644 --- a/demos/utils/source/ecs_utils/utils.d +++ b/demos/utils/source/ecs_utils/utils.d @@ -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) { diff --git a/meson.build b/meson.build index c31b93a..94b9c64 100644 --- a/meson.build +++ b/meson.build @@ -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 - args += '-betterC' - link_args += '-betterC' + 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 diff --git a/meson_options.txt b/meson_options.txt index fdf13a4..0ea2df9 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1 +1,3 @@ -option('betterC', type: 'boolean', value: false) \ No newline at end of file +option('betterC', type: 'boolean', value: false) +option('BuildDemos', type: 'boolean', value: false) +option('LTO', type: 'boolean', value: false) \ No newline at end of file diff --git a/source/bubel/ecs/id_manager.d b/source/bubel/ecs/id_manager.d index aaeef30..db15b26 100644 --- a/source/bubel/ecs/id_manager.d +++ b/source/bubel/ecs/id_manager.d @@ -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) diff --git a/source/bubel/ecs/manager.d b/source/bubel/ecs/manager.d index 0982531..19068cb 100644 --- a/source/bubel/ecs/manager.d +++ b/source/bubel/ecs/manager.d @@ -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(); diff --git a/source/bubel/ecs/std.d b/source/bubel/ecs/std.d index a7dd846..cac07b2 100644 --- a/source/bubel/ecs/std.d +++ b/source/bubel/ecs/std.d @@ -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)) diff --git a/tests/tests.d b/tests/tests.d index d923116..9f69987 100644 --- a/tests/tests.d +++ b/tests/tests.d @@ -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;