diff --git a/CMakeLists.txt b/CMakeLists.txt index 7412c9c2..16bd57a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,20 +15,47 @@ file(GLOB_RECURSE SRC_FILES ${CMAKE_SOURCE_DIR}/src/*.h ${CMAKE_SOURCE_DIR}/src/*.cpp ) -file(GLOB_RECURSE EXT_FILES +file(GLOB EXT_FILES ${CMAKE_SOURCE_DIR}/ext/*.h ${CMAKE_SOURCE_DIR}/ext/*.cpp ${CMAKE_SOURCE_DIR}/ext/gl3w/GL/*.c + ${CMAKE_SOURCE_DIR}/ext/enkiTS-C-11/src/*.cpp +${CMAKE_SOURCE_DIR}/ext/NativeFileDialog/src/nfd_common.c ) + +if(WIN32) +set(NFD_FILES ${CMAKE_SOURCE_DIR}/ext/NativeFileDialog/src/nfd_win.cpp) +set(PLATFORM_LIBS "libtcc") +else() +set(NFD_FILES ${CMAKE_SOURCE_DIR}/ext/NativeFileDialog/src/nfd_gtk.c +) +# Use the package PkgConfig to detect GTK+ headers/library files +FIND_PACKAGE(PkgConfig REQUIRED) +PKG_CHECK_MODULES(GTK3 REQUIRED gtk+-3.0) + +# Setup CMake to use GTK+, tell the compiler where to look for headers +# and to the linker where to look for libraries +INCLUDE_DIRECTORIES(${GTK3_INCLUDE_DIRS}) +LINK_DIRECTORIES(${GTK3_LIBRARY_DIRS}) + +# Add other flags to the compiler +ADD_DEFINITIONS(${GTK3_CFLAGS_OTHER}) +set(PLATFORM_LIBS dl pthread ${GTK3_LIBRARIES} libtcc.a) +endif() + include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/ext ${CMAKE_CURRENT_SOURCE_DIR}/ext/SDL2-2.0.8/include ${CMAKE_CURRENT_SOURCE_DIR}/ext/gl3w + ${CMAKE_CURRENT_SOURCE_DIR}/ext/tcc-0.9.27/ + ${CMAKE_CURRENT_SOURCE_DIR}/ext/NativeFileDialog/src/include/ + ${CMAKE_CURRENT_SOURCE_DIR}/ext/enkiTS-C-11/src/ ) link_directories( ${CMAKE_CURRENT_SOURCE_DIR}/ext/SDL2-2.0.8/lib/x64 + ${CMAKE_CURRENT_SOURCE_DIR}/ext/tcc-0.9.27/libtcc ) find_package(OpenGL) @@ -81,9 +108,9 @@ endif() SET(LINK_OPTIONS " ") SET(EXE_NAME "Imogen") -ADD_EXECUTABLE(${EXE_NAME} ${SRC_FILES} ${EXT_FILES} ) +ADD_EXECUTABLE(${EXE_NAME} ${SRC_FILES} ${EXT_FILES} ${NFD_FILES}) -TARGET_LINK_LIBRARIES(${EXE_NAME} ${SDL2_LIBS} ${OPENGL_LIBRARIES}) +TARGET_LINK_LIBRARIES(${EXE_NAME} ${SDL2_LIBS} ${OPENGL_LIBRARIES} ${PLATFORM_LIBS}) #-------------------------------------------------------------------- # preproc @@ -94,8 +121,10 @@ add_definitions(-DBX_CONFIG_ENABLE_MSVC_LEVEL4_WARNINGS=1) add_definitions(-D__STDC_LIMIT_MACROS) add_definitions(-D__STDC_CONSTANT_MACROS) add_definitions(-DIMGUI_DISABLE_OBSOLETE_FUNCTIONS) +if(WINDOWS) add_definitions(-DWIN32) add_definitions(-D_WIN32) +endif() add_definitions(-DUSE_DL_PREFIX) #-------------------------------------------------------------------- # output dirs diff --git a/bin/C/ImageRead.c b/bin/C/ImageRead.c new file mode 100644 index 00000000..83ee2114 --- /dev/null +++ b/bin/C/ImageRead.c @@ -0,0 +1,20 @@ +#include "Imogen.h" + +typedef struct ImageRead_t +{ + char filename[1024]; +} ImageRead; + +int main(ImageRead *param, Evaluation *evaluation) +{ + Image image; + if (ReadImage(param->filename, &image) == EVAL_OK) + { + if (SetEvaluationImage(evaluation->targetIndex, &image) == EVAL_OK) + { + FreeImage(&image); + return EVAL_OK; + } + } + return EVAL_ERR; +} \ No newline at end of file diff --git a/bin/C/ImageWrite.c b/bin/C/ImageWrite.c new file mode 100644 index 00000000..3de6aaaf --- /dev/null +++ b/bin/C/ImageWrite.c @@ -0,0 +1,42 @@ + +#include "Imogen.h" + +typedef struct ImageWrite_t +{ + char filename[1024]; + int format; + int quality; + int width, height; +}ImageWrite; + +int main(ImageWrite *param, Evaluation *evaluation) +{ + char *stockImages[5] = {"Stock/jpg-icon.png", "Stock/png-icon.png", "Stock/tga-icon.png", "Stock/bmp-icon.png", "Stock/hdr-icon.png"}; + Image image; + + // set info stock image + if (ReadImage(stockImages[param->format], &image) == EVAL_OK) + { + if (SetEvaluationImage(evaluation->targetIndex, &image) == EVAL_OK) + { + FreeImage(&image); + } + } + + if (!evaluation->forcedDirty) + return EVAL_OK; + + if (Evaluate(evaluation->inputIndices[0], 256<width, 256<height, &image) == EVAL_OK) + { + if (WriteImage(param->filename, &image, param->format, param->quality) == EVAL_OK) + { + FreeImage(&image); + Log("Image %s saved.\n", param->filename); + return EVAL_OK; + } + else + Log("Unable to write image : %s\n", param->filename); + } + + return EVAL_ERR; +} \ No newline at end of file diff --git a/bin/C/Imogen.h b/bin/C/Imogen.h new file mode 100644 index 00000000..b007a81b --- /dev/null +++ b/bin/C/Imogen.h @@ -0,0 +1,39 @@ +int Log(const char *szFormat, ...); + +typedef struct Image_t +{ + int width, height; + int components; + void *bits; +} Image; + +typedef struct Evaluation_t +{ + int targetIndex; + int inputIndices[8]; + int forcedDirty; +} Evaluation; + +// call FreeImage when done +int ReadImage(char *filename, Image *image); +// writes an allocated image +int WriteImage(char *filename, Image *image, int format, int quality); +// call FreeImage when done +int GetEvaluationImage(int target, Image *image); +// +int SetEvaluationImage(int target, Image *image); +// call FreeImage when done +// set the bits pointer with an allocated memory +int AllocateImage(Image *image); +int FreeImage(Image *image); + +// Image resize +// Image thumbnail +int SetThumbnailImage(Image *image); + +// force evaluation of a target with a specified size +// no guarantee that the resulting Image will have that size. +int Evaluate(int target, int width, int height, Image *image); + +#define EVAL_OK 0 +#define EVAL_ERR 1 \ No newline at end of file diff --git a/bin/C/Thumbnail.c b/bin/C/Thumbnail.c new file mode 100644 index 00000000..a7b6bd90 --- /dev/null +++ b/bin/C/Thumbnail.c @@ -0,0 +1,28 @@ +#include "Imogen.h" + +int main(void *param, Evaluation *evaluation) +{ + Image image; + + if (ReadImage("Stock/thumbnail-icon.png", &image) == EVAL_OK) + { + if (SetEvaluationImage(evaluation->targetIndex, &image) == EVAL_OK) + { + FreeImage(&image); + } + } + + if (!evaluation->forcedDirty) + return EVAL_OK; + + if (GetEvaluationImage(evaluation->inputIndices[0], &image) == EVAL_OK) + { + if (SetThumbnailImage(&image) == EVAL_OK) + { + FreeImage(&image); + return EVAL_OK; + } + } + + return EVAL_ERR; +} diff --git a/bin/GLSL/LambertMaterial.glsl b/bin/GLSL/LambertMaterial.glsl index e32345e7..1feba240 100644 --- a/bin/GLSL/LambertMaterial.glsl +++ b/bin/GLSL/LambertMaterial.glsl @@ -52,7 +52,7 @@ vec4 LambertMaterial() // sphere center vec3 sc = vec3(0.0,1.0,0.0); - vec3 col = texture(equiRectEnvSampler, envMapEquirect(rd)).xyz; + vec3 col = texture(Sampler1, envMapEquirect(rd)).xyz; if (castRay(ro, rd)<10.0) { diff --git a/bin/GLSL/PBR.glsl b/bin/GLSL/PBR.glsl index 736d158a..457bc37a 100644 --- a/bin/GLSL/PBR.glsl +++ b/bin/GLSL/PBR.glsl @@ -246,7 +246,7 @@ float camHeight = -0.2; vec3 lightColor = vec3( 2. ); vec3 lightDir = normalize( vec3( .7, .9, -.2 ) ); - vec3 col = texture(equiRectEnvSampler, envMapEquirect(rd)).xyz; + vec3 col = texture(Sampler4, envMapEquirect(rd)).xyz; float t = CastRay( ro, rd, localToWorld ); if ( t > 0.0 ) { @@ -274,7 +274,7 @@ float camHeight = -0.2; vec3 envSpecularColor = EnvBRDFApprox( specularColor, roughnessE, ndotv ); - vec3 env = textureLod(equiRectEnvSampler, envMapEquirect(refl),roughnessE*12.0).xyz; + vec3 env = textureLod(Sampler4, envMapEquirect(refl),roughnessE*12.0).xyz; diffuse += diffuseColor * EnvRemap(env); specular += envSpecularColor * env; diff --git a/bin/GLSL/Shader.glsl b/bin/GLSL/Shader.glsl index 150f4250..418891f6 100644 --- a/bin/GLSL/Shader.glsl +++ b/bin/GLSL/Shader.glsl @@ -24,7 +24,10 @@ uniform sampler2D Sampler0; uniform sampler2D Sampler1; uniform sampler2D Sampler2; uniform sampler2D Sampler3; -uniform sampler2D equiRectEnvSampler; +uniform sampler2D Sampler4; +uniform sampler2D Sampler5; +uniform sampler2D Sampler6; +uniform sampler2D Sampler7; vec2 Rotate2D(vec2 v, float a) { diff --git a/bin/GLSL/Transform.glsl b/bin/GLSL/Transform.glsl index 545cefe3..4e0739d1 100644 --- a/bin/GLSL/Transform.glsl +++ b/bin/GLSL/Transform.glsl @@ -1,14 +1,16 @@ layout (std140) uniform TransformBlock { vec2 translate; + vec2 scale; float rotate; - float scale; -} TransformParam; +}; vec4 Transform() { - vec2 rs = (vUV+TransformParam.translate) * TransformParam.scale; - vec2 ro = vec2(rs.x*cos(TransformParam.rotate) - rs.y * sin(TransformParam.rotate), rs.x*sin(TransformParam.rotate) + rs.y * cos(TransformParam.rotate)); + vec2 rs = (vUV+translate) * scale; + rs -= 0.5; + vec2 ro = vec2(rs.x*cos(rotate) - rs.y * sin(rotate), rs.x*sin(rotate) + rs.y * cos(rotate)); + ro += 0.5; vec2 nuv = ro;//fract(ro); vec4 tex = texture(Sampler0, nuv); return tex; diff --git a/bin/Imogen.exe b/bin/Imogen.exe index 95d55ede..810c1d5f 100644 Binary files a/bin/Imogen.exe and b/bin/Imogen.exe differ diff --git a/bin/Outputs/GamekultLogo_4K.jpg b/bin/Outputs/GamekultLogo_4K.jpg new file mode 100644 index 00000000..531b785d Binary files /dev/null and b/bin/Outputs/GamekultLogo_4K.jpg differ diff --git a/bin/Outputs/JapaneseTile_4K.jpg b/bin/Outputs/JapaneseTile_4K.jpg new file mode 100644 index 00000000..cb3c78a6 Binary files /dev/null and b/bin/Outputs/JapaneseTile_4K.jpg differ diff --git a/bin/Outputs/PartyCatDeformed.jpg b/bin/Outputs/PartyCatDeformed.jpg new file mode 100644 index 00000000..d1ea81e5 Binary files /dev/null and b/bin/Outputs/PartyCatDeformed.jpg differ diff --git a/bin/Pictures/PartyCat.jpg b/bin/Pictures/PartyCat.jpg new file mode 100644 index 00000000..63395471 Binary files /dev/null and b/bin/Pictures/PartyCat.jpg differ diff --git a/bin/Stock/bmp-icon.png b/bin/Stock/bmp-icon.png new file mode 100644 index 00000000..9426f5d2 Binary files /dev/null and b/bin/Stock/bmp-icon.png differ diff --git a/bin/Stock/hdr-icon.png b/bin/Stock/hdr-icon.png new file mode 100644 index 00000000..db1ef102 Binary files /dev/null and b/bin/Stock/hdr-icon.png differ diff --git a/bin/Stock/jpg-icon.png b/bin/Stock/jpg-icon.png new file mode 100644 index 00000000..21f85ca4 Binary files /dev/null and b/bin/Stock/jpg-icon.png differ diff --git a/bin/Stock/png-icon.png b/bin/Stock/png-icon.png new file mode 100644 index 00000000..b5e41527 Binary files /dev/null and b/bin/Stock/png-icon.png differ diff --git a/bin/Stock/tga-icon.png b/bin/Stock/tga-icon.png new file mode 100644 index 00000000..9a38b302 Binary files /dev/null and b/bin/Stock/tga-icon.png differ diff --git a/bin/Stock/thumbnail-icon.png b/bin/Stock/thumbnail-icon.png new file mode 100644 index 00000000..76fcfb29 Binary files /dev/null and b/bin/Stock/thumbnail-icon.png differ diff --git a/bin/library.dat b/bin/library.dat index 5d49d7b1..5e3da470 100644 Binary files a/bin/library.dat and b/bin/library.dat differ diff --git a/bin/libtcc.dll b/bin/libtcc.dll new file mode 100644 index 00000000..0a5d8179 Binary files /dev/null and b/bin/libtcc.dll differ diff --git a/changelog.txt b/changelog.txt index eb06738a..5cdd4412 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,18 @@ -Imogen 0.1.1 - codename Pegasus Seiya +------------------------------------------------------------------------------- +Imogen 0.2 - codename Dragon Shiryu + +New: +- C CPU nodes. edit and run C code on the fly +- image reader/writer with jpg, png, tga, bmp, partial HDR support +- thumbnail node: node, library, display +- save and restore docking layout +- Export button in node graph dock that force every export in the graph -WIP +Changed: +- Separate scale X and Y values in Transform node + +------------------------------------------------------------------------------- +Imogen 0.1.1 - codename Pegasus Seiya New: - SDL 2 instead of custom Windows only ImApp.h (one big step toward Linux port) diff --git a/ext/NativeFileDialog/LICENSE b/ext/NativeFileDialog/LICENSE new file mode 100644 index 00000000..3ab103c5 --- /dev/null +++ b/ext/NativeFileDialog/LICENSE @@ -0,0 +1,16 @@ +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + diff --git a/ext/NativeFileDialog/README.md b/ext/NativeFileDialog/README.md new file mode 100644 index 00000000..8505d94e --- /dev/null +++ b/ext/NativeFileDialog/README.md @@ -0,0 +1,152 @@ +# Native File Dialog # + +A tiny, neat C library that portably invokes native file open, folder select and save dialogs. Write dialog code once and have it pop up native dialogs on all supported platforms. Avoid linking large dependencies like wxWidgets and qt. + +Features: + + - Lean C API, static library -- no ObjC, no C++, no STL. + - Zlib licensed. + - Consistent UTF-8 support on all platforms. + - Simple universal file filter syntax. + - Paid support available. + - Multiple file selection support. + - 64-bit and 32-bit friendly. + - GCC, Clang, Xcode, Mingw and Visual Studio supported. + - No third party dependencies for building or linking. + - Support for Vista's modern `IFileDialog` on Windows. + - Support for non-deprecated Cocoa APIs on OS X. + - GTK+3 dialog on Linux. + - Tested, works alongside [http://www.libsdl.org](SDL2) on all platforms, for the game developers out there. + +# Example Usage # + +```C +#include +#include +#include + +int main( void ) +{ + nfdchar_t *outPath = NULL; + nfdresult_t result = NFD_OpenDialog( NULL, NULL, &outPath ); + + if ( result == NFD_OKAY ) { + puts("Success!"); + puts(outPath); + free(outPath); + } + else if ( result == NFD_CANCEL ) { + puts("User pressed cancel."); + } + else { + printf("Error: %s\n", NFD_GetError() ); + } + + return 0; +} +``` + +See [NFD.h](src/include/nfd.h) for more options. + +# Screenshots # + +![Windows 8 rendering an IFileOpenDialog](screens/open_win8.png?raw=true) +![GTK3 on Linux](screens/open_gtk3.png?raw=true) +![Cocoa on Yosemite](screens/open_cocoa.png?raw=true) + +## Changelog ## + +The current version is 1.1.1 + +release | what's new | date +--------|-----------------------------|--------- +1.0.0 | initial | oct 2014 +1.1.0 | premake5; scons deprecated | aug 2016 +1.1.1 | mingw support, build fixes | aug 2016 +1.1.2 | test_pickfolder() added | aug 2016 + +## Building ## + +NFD uses [Premake5](https://premake.github.io/download.html) generated Makefiles and IDE project files. The generated project files are checked in under `build/` so you don't have to download and use Premake in most cases. + +If you need to run Premake5 directly, further [build documentation](docs/build.md) is available. + +Previously, NFD used SCons to build. It still works, but is now deprecated; updates to it are discouraged. Opt to use the native build system where possible. + +`nfd.a` will be built for release builds, and `nfd_d.a` will be built for debug builds. + +### Makefiles ### + +The makefile offers four options, with `release_x64` as the default. + + make config=release_x86 + make config=release_x64 + make config=debug_x86 + make config=debug_x64 + +### Compiling Your Programs ### + + 1. Add `src/include` to your include search path. + 2. Add `nfd.lib` or `nfd_d.lib` to the list of list of static libraries to link against (for release or debug, respectively). + 3. Add `build//` to the library search path. + +On Linux, you must compile and link against GTK+. Recommend use of `pkg-config --cflags --libs gtk+-3.0`. + +On Mac OS X, add `AppKit` to the list of frameworks. + +On Windows, ensure you are building against `comctl32.lib`. + +## Usage ## + +See `NFD.h` for API calls. See `tests/*.c` for example code. + +After compiling, `build/bin` contains compiled test programs. + +## File Filter Syntax ## + +There is a form of file filtering in every file dialog API, but no consistent means of supporting it. NFD provides support for filtering files by groups of extensions, providing its own descriptions (where applicable) for the extensions. + +A wildcard filter is always added to every dialog. + +### Separators ### + + - `;` Begin a new filter. + - `,` Add a separate type to the filter. + +#### Examples #### + +`txt` The default filter is for text files. There is a wildcard option in a dropdown. + +`png,jpg;psd` The default filter is for png and jpg files. A second filter is available for psd files. There is a wildcard option in a dropdown. + +`NULL` Wildcard only. + +## Iterating Over PathSets ## + +See [test_opendialogmultiple.c](test/test_opendialogmultiple.c). + +# Known Limitations # + +I accept quality code patches, or will resolve these and other matters through support. See [submitting pull requests](docs/submitting_pull_requests.md) for details. + + - No support for Windows XP's legacy dialogs such as `GetOpenFileName`. + - No support for file filter names -- ex: "Image Files" (*.png, *.jpg). Nameless filters are supported, however. + - On Linux, GTK+ cannot be uninitialized to save memory. Launching a file dialog costs memory. I am open to accepting an alternative `nfd_zenity.c` implementation which uses Zenity and pipes. + +# Copyright and Credit # + +Copyright © 2014-2016 [Frogtoss Games](http://www.frogtoss.com), Inc. +File [LICENSE](LICENSE) covers all files in this repo. + +Native File Dialog by Michael Labbe + + +Tomasz Konojacki for [microutf8](http://puszcza.gnu.org.ua/software/microutf8/) + +[Denis Kolodin](https://github.com/DenisKolodin) for mingw support. + +## Support ## + +Directed support for this work is available from the original author under a paid agreement. + +[Contact Frogtoss Games](http://www.frogtoss.com/pages/contact.html). diff --git a/ext/NativeFileDialog/src/SConstruct b/ext/NativeFileDialog/src/SConstruct new file mode 100644 index 00000000..b1a9e488 --- /dev/null +++ b/ext/NativeFileDialog/src/SConstruct @@ -0,0 +1,102 @@ +# +# Native File Dialog +# +# Scons build script -- GCC, Clang, Visual Studio +# Does not build test +# +# SCons builds are deprecated -- see README.md for details. + +import os + + +# target arch is build arch -- extend here for OS cross compiling +target_os=str(Platform()) + +# Corresponds to TARGET_ARCH set to environ. +target_arch = ARGUMENTS.get('target_arch', None) + +# visual studio does not import from environment +if target_os != 'win32': + IMPORT_FROM_ENV =['CC', 'CXX', 'CFLAGS', 'CXXFLAGS', 'ARFLAGS'] +else: + IMPORT_FROM_ENV =[] + + +debug = int(ARGUMENTS.get( 'debug', 0 )) + +nfd_files = ['nfd_common.c'] + +# Due to a Scons limitation, TARGET_ARCH cannot be appended to an existing environment. +if target_arch != None: + nfd_env = Environment( TARGET_ARCH=target_arch ) +else: + nfd_env = Environment() + +# import specific environment variables from the command line, overriding +# Scons environment defaults +for env_key in IMPORT_FROM_ENV: + if env_key in os.environ: + print "Making %s => %s" % ( env_key, os.environ[env_key] ) + nfd_env[env_key] = os.environ[env_key] + +# Windows runtime library types +win_rtl = {'debug': '/MDd', + 'release': '/MD'} + +def set_debug(env): + if target_os == 'win32': + env.Append( CCFLAGS=['/Z7', # obj contains full symbols + win_rtl['debug'] + ]) + else: + env.Append( CFLAGS=['-g'] ) + + +def set_release(env): + if target_os == 'win32': + env.Append( CCFLAGS=[win_rtl['release'], + '/O2'] ) + else: + env.Append( CFLAGS=['-O3'] ) + + +def set_warnings(env): + if target_os == 'win32': + env.Append( CCFLAGS=['/W3'], + CPPDEFINES=['_CRT_SECURE_NO_WARNINGS'] ) + else: + env.Append( CFLAGS=['-Wall', '-pedantic'] ) + + +def get_lib_name(base, is_debug): + if is_debug: + return base + '_d' + else: + return base + + +# Cocoa OS X builds - clang +if target_os == 'darwin': + nfd_files.append('nfd_cocoa.m') + nfd_env.CC='clang -fcolor-diagnostics' + +# Linux GTK+ 3 builds - GCC +elif target_os == 'posix': + nfd_files.append('nfd_gtk.c') + nfd_env.ParseConfig( 'pkg-config --cflags gtk+-3.0' ) + +# Windows builds - Visual Studio +elif target_os == 'win32': + nfd_files.append('nfd_win.cpp') + +if debug: + set_debug(nfd_env) +else: + set_release(nfd_env) + +set_warnings(nfd_env) + +nfd_env.Append( CPPPATH=['.','./include'] ) +nfd_env.StaticLibrary( get_lib_name('nfd', debug), nfd_files ) + +print "*** Scons builds are deprecated! See README.md for details." diff --git a/ext/NativeFileDialog/src/common.h b/ext/NativeFileDialog/src/common.h new file mode 100644 index 00000000..7745d323 --- /dev/null +++ b/ext/NativeFileDialog/src/common.h @@ -0,0 +1,21 @@ +/* + Native File Dialog + + Internal, common across platforms + + http://www.frogtoss.com/labs + */ + + +#ifndef _NFD_COMMON_H +#define _NFD_COMMON_H + +#define NFD_MAX_STRLEN 256 +#define _NFD_UNUSED(x) ((void)x) + +void *NFDi_Malloc( size_t bytes ); +void NFDi_Free( void *ptr ); +void NFDi_SetError( const char *msg ); +void NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy ); + +#endif diff --git a/ext/NativeFileDialog/src/include/nfd.h b/ext/NativeFileDialog/src/include/nfd.h new file mode 100644 index 00000000..74c92743 --- /dev/null +++ b/ext/NativeFileDialog/src/include/nfd.h @@ -0,0 +1,74 @@ +/* + Native File Dialog + + User API + + http://www.frogtoss.com/labs + */ + + +#ifndef _NFD_H +#define _NFD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* denotes UTF-8 char */ +typedef char nfdchar_t; + +/* opaque data structure -- see NFD_PathSet_* */ +typedef struct { + nfdchar_t *buf; + size_t *indices; /* byte offsets into buf */ + size_t count; /* number of indices into buf */ +}nfdpathset_t; + +typedef enum { + NFD_ERROR, /* programmatic error */ + NFD_OKAY, /* user pressed okay, or successful return */ + NFD_CANCEL /* user pressed cancel */ +}nfdresult_t; + + +/* nfd_.c */ + +/* single file open dialog */ +nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ); + +/* multiple file open dialog */ +nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdpathset_t *outPaths ); + +/* save dialog */ +nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ); + + +/* select folder dialog */ +nfdresult_t NFD_PickFolder( const nfdchar_t *defaultPath, + nfdchar_t **outPath); + +/* nfd_common.c */ + +/* get last error -- set when nfdresult_t returns NFD_ERROR */ +const char *NFD_GetError( void ); +/* get the number of entries stored in pathSet */ +size_t NFD_PathSet_GetCount( const nfdpathset_t *pathSet ); +/* Get the UTF-8 path at offset index */ +nfdchar_t *NFD_PathSet_GetPath( const nfdpathset_t *pathSet, size_t index ); +/* Free the pathSet */ +void NFD_PathSet_Free( nfdpathset_t *pathSet ); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext/NativeFileDialog/src/nfd_cocoa.m b/ext/NativeFileDialog/src/nfd_cocoa.m new file mode 100644 index 00000000..d3fb4834 --- /dev/null +++ b/ext/NativeFileDialog/src/nfd_cocoa.m @@ -0,0 +1,276 @@ +/* + Native File Dialog + + http://www.frogtoss.com/labs + */ + +#include +#include "nfd.h" +#include "nfd_common.h" + +static NSArray *BuildAllowedFileTypes( const char *filterList ) +{ + // Commas and semicolons are the same thing on this platform + + NSMutableArray *buildFilterList = [[NSMutableArray alloc] init]; + + char typebuf[NFD_MAX_STRLEN] = {0}; + + size_t filterListLen = strlen(filterList); + char *p_typebuf = typebuf; + for ( size_t i = 0; i < filterListLen+1; ++i ) + { + if ( filterList[i] == ',' || filterList[i] == ';' || filterList[i] == '\0' ) + { + ++p_typebuf; + *p_typebuf = '\0'; + NSString *thisType = [NSString stringWithUTF8String: typebuf]; + [buildFilterList addObject:thisType]; + p_typebuf = typebuf; + *p_typebuf = '\0'; + } + else + { + *p_typebuf = filterList[i]; + ++p_typebuf; + + } + } + + NSArray *returnArray = [NSArray arrayWithArray:buildFilterList]; + + [buildFilterList release]; + return returnArray; +} + +static void AddFilterListToDialog( NSSavePanel *dialog, const char *filterList ) +{ + if ( !filterList || strlen(filterList) == 0 ) + return; + + NSArray *allowedFileTypes = BuildAllowedFileTypes( filterList ); + if ( [allowedFileTypes count] != 0 ) + { + [dialog setAllowedFileTypes:allowedFileTypes]; + } +} + +static void SetDefaultPath( NSSavePanel *dialog, const nfdchar_t *defaultPath ) +{ + if ( !defaultPath || strlen(defaultPath) == 0 ) + return; + + NSString *defaultPathString = [NSString stringWithUTF8String: defaultPath]; + NSURL *url = [NSURL fileURLWithPath:defaultPathString isDirectory:YES]; + [dialog setDirectoryURL:url]; +} + + +/* fixme: pathset should be pathSet */ +static nfdresult_t AllocPathSet( NSArray *urls, nfdpathset_t *pathset ) +{ + assert(pathset); + assert([urls count]); + + pathset->count = (size_t)[urls count]; + pathset->indices = NFDi_Malloc( sizeof(size_t)*pathset->count ); + if ( !pathset->indices ) + { + return NFD_ERROR; + } + + // count the total space needed for buf + size_t bufsize = 0; + for ( NSURL *url in urls ) + { + NSString *path = [url path]; + bufsize += [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; + } + + pathset->buf = NFDi_Malloc( sizeof(nfdchar_t) * bufsize ); + if ( !pathset->buf ) + { + return NFD_ERROR; + } + + // fill buf + nfdchar_t *p_buf = pathset->buf; + size_t count = 0; + for ( NSURL *url in urls ) + { + NSString *path = [url path]; + const nfdchar_t *utf8Path = [path UTF8String]; + size_t byteLen = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; + memcpy( p_buf, utf8Path, byteLen ); + + ptrdiff_t index = p_buf - pathset->buf; + assert( index >= 0 ); + pathset->indices[count] = (size_t)index; + + p_buf += byteLen; + ++count; + } + + return NFD_OKAY; +} + +/* public */ + + +nfdresult_t NFD_OpenDialog( const char *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow]; + NSOpenPanel *dialog = [NSOpenPanel openPanel]; + [dialog setAllowsMultipleSelection:NO]; + + // Build the filter list + AddFilterListToDialog(dialog, filterList); + + // Set the starting directory + SetDefaultPath(dialog, defaultPath); + + nfdresult_t nfdResult = NFD_CANCEL; + if ( [dialog runModal] == NSModalResponseOK ) + { + NSURL *url = [dialog URL]; + const char *utf8Path = [[url path] UTF8String]; + + // byte count, not char count + size_t len = strlen(utf8Path);//NFDi_UTF8_Strlen(utf8Path); + + *outPath = NFDi_Malloc( len+1 ); + if ( !*outPath ) + { + [pool release]; + return NFD_ERROR; + } + memcpy( *outPath, utf8Path, len+1 ); /* copy null term */ + nfdResult = NFD_OKAY; + } + [pool release]; + + [keyWindow makeKeyAndOrderFront:nil]; + return nfdResult; +} + + +nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdpathset_t *outPaths ) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSOpenPanel *dialog = [NSOpenPanel openPanel]; + [dialog setAllowsMultipleSelection:YES]; + + // Build the fiter list. + AddFilterListToDialog(dialog, filterList); + + // Set the starting directory + SetDefaultPath(dialog, defaultPath); + + nfdresult_t nfdResult = NFD_CANCEL; + if ( [dialog runModal] == NSModalResponseOK ) + { + NSArray *urls = [dialog URLs]; + + if ( [urls count] == 0 ) + { + [pool release]; + return NFD_CANCEL; + } + + if ( AllocPathSet( urls, outPaths ) == NFD_ERROR ) + { + [pool release]; + return NFD_ERROR; + } + + nfdResult = NFD_OKAY; + } + [pool release]; + + return nfdResult; +} + + +nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSSavePanel *dialog = [NSSavePanel savePanel]; + [dialog setExtensionHidden:NO]; + + // Build the filter list. + AddFilterListToDialog(dialog, filterList); + + // Set the starting directory + SetDefaultPath(dialog, defaultPath); + + nfdresult_t nfdResult = NFD_CANCEL; + if ( [dialog runModal] == NSModalResponseOK ) + { + NSURL *url = [dialog URL]; + const char *utf8Path = [[url path] UTF8String]; + + size_t byteLen = [url.path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; + + *outPath = NFDi_Malloc( byteLen ); + if ( !*outPath ) + { + [pool release]; + return NFD_ERROR; + } + memcpy( *outPath, utf8Path, byteLen ); + nfdResult = NFD_OKAY; + } + + [pool release]; + + return nfdResult; +} + +nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath, + nfdchar_t **outPath) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow]; + NSOpenPanel *dialog = [NSOpenPanel openPanel]; + [dialog setAllowsMultipleSelection:NO]; + [dialog setCanChooseDirectories:YES]; + [dialog setCanCreateDirectories:YES]; + [dialog setCanChooseFiles:NO]; + + // Set the starting directory + SetDefaultPath(dialog, defaultPath); + + nfdresult_t nfdResult = NFD_CANCEL; + if ( [dialog runModal] == NSModalResponseOK ) + { + NSURL *url = [dialog URL]; + const char *utf8Path = [[url path] UTF8String]; + + // byte count, not char count + size_t len = strlen(utf8Path);//NFDi_UTF8_Strlen(utf8Path); + + *outPath = NFDi_Malloc( len+1 ); + if ( !*outPath ) + { + [pool release]; + return NFD_ERROR; + } + memcpy( *outPath, utf8Path, len+1 ); /* copy null term */ + nfdResult = NFD_OKAY; + } + [pool release]; + + [keyWindow makeKeyAndOrderFront:nil]; + return nfdResult; +} diff --git a/ext/NativeFileDialog/src/nfd_common.c b/ext/NativeFileDialog/src/nfd_common.c new file mode 100644 index 00000000..269fbd21 --- /dev/null +++ b/ext/NativeFileDialog/src/nfd_common.c @@ -0,0 +1,142 @@ +/* + Native File Dialog + + http://www.frogtoss.com/labs + */ + +#include +#include +#include +#include "nfd_common.h" + +static char g_errorstr[NFD_MAX_STRLEN] = {0}; + +/* public routines */ + +const char *NFD_GetError( void ) +{ + return g_errorstr; +} + +size_t NFD_PathSet_GetCount( const nfdpathset_t *pathset ) +{ + assert(pathset); + return pathset->count; +} + +nfdchar_t *NFD_PathSet_GetPath( const nfdpathset_t *pathset, size_t num ) +{ + assert(pathset); + assert(num < pathset->count); + + return pathset->buf + pathset->indices[num]; +} + +void NFD_PathSet_Free( nfdpathset_t *pathset ) +{ + assert(pathset); + NFDi_Free( pathset->indices ); + NFDi_Free( pathset->buf ); +} + +/* internal routines */ + +void *NFDi_Malloc( size_t bytes ) +{ + void *ptr = malloc(bytes); + if ( !ptr ) + NFDi_SetError("NFDi_Malloc failed."); + + return ptr; +} + +void NFDi_Free( void *ptr ) +{ + assert(ptr); + free(ptr); +} + +void NFDi_SetError( const char *msg ) +{ + int bTruncate = NFDi_SafeStrncpy( g_errorstr, msg, NFD_MAX_STRLEN ); + assert( !bTruncate ); _NFD_UNUSED(bTruncate); +} + + +int NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy ) +{ + size_t n = maxCopy; + char *d = dst; + + assert( src ); + assert( dst ); + + while ( n > 0 && *src != '\0' ) + { + *d++ = *src++; + --n; + } + + /* Truncation case - + terminate string and return true */ + if ( n == 0 ) + { + dst[maxCopy-1] = '\0'; + return 1; + } + + /* No truncation. Append a single NULL and return. */ + *d = '\0'; + return 0; +} + + +/* adapted from microutf8 */ +size_t NFDi_UTF8_Strlen( const nfdchar_t *str ) +{ + /* This function doesn't properly check validity of UTF-8 character + sequence, it is supposed to use only with valid UTF-8 strings. */ + + size_t character_count = 0; + size_t i = 0; /* Counter used to iterate over string. */ + nfdchar_t maybe_bom[4]; + + /* If there is UTF-8 BOM ignore it. */ + if (strlen(str) > 2) + { + strncpy(maybe_bom, str, 3); + maybe_bom[3] = 0; + if (strcmp(maybe_bom, (nfdchar_t*)NFD_UTF8_BOM) == 0) + i += 3; + } + + while(str[i]) + { + if (str[i] >> 7 == 0) + { + /* If bit pattern begins with 0 we have ascii character. */ + ++character_count; + } + else if (str[i] >> 6 == 3) + { + /* If bit pattern begins with 11 it is beginning of UTF-8 byte sequence. */ + ++character_count; + } + else if (str[i] >> 6 == 2) + ; /* If bit pattern begins with 10 it is middle of utf-8 byte sequence. */ + else + { + /* In any other case this is not valid UTF-8. */ + return -1; + } + ++i; + } + + return character_count; +} + +int NFDi_IsFilterSegmentChar( char ch ) +{ + return (ch==','||ch==';'||ch=='\0'); +} + diff --git a/ext/NativeFileDialog/src/nfd_common.h b/ext/NativeFileDialog/src/nfd_common.h new file mode 100644 index 00000000..a3f6b4ad --- /dev/null +++ b/ext/NativeFileDialog/src/nfd_common.h @@ -0,0 +1,37 @@ +/* + Native File Dialog + + Internal, common across platforms + + http://www.frogtoss.com/labs + */ + + +#ifndef _NFD_COMMON_H +#define _NFD_COMMON_H + +#include "nfd.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define NFD_MAX_STRLEN 256 +#define _NFD_UNUSED(x) ((void)x) + +#define NFD_UTF8_BOM "\xEF\xBB\xBF" + + +void *NFDi_Malloc( size_t bytes ); +void NFDi_Free( void *ptr ); +void NFDi_SetError( const char *msg ); +int NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy ); +size_t NFDi_UTF8_Strlen( const nfdchar_t *str ); +int NFDi_IsFilterSegmentChar( char ch ); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/ext/NativeFileDialog/src/nfd_gtk.c b/ext/NativeFileDialog/src/nfd_gtk.c new file mode 100644 index 00000000..65bc41da --- /dev/null +++ b/ext/NativeFileDialog/src/nfd_gtk.c @@ -0,0 +1,379 @@ +/* + Native File Dialog + + http://www.frogtoss.com/labs +*/ + +#include +#include +#include +#include +#include "nfd.h" +#include "nfd_common.h" + + +const char INIT_FAIL_MSG[] = "gtk_init_check failed to initilaize GTK+"; + + +static void AddTypeToFilterName( const char *typebuf, char *filterName, size_t bufsize ) +{ + const char SEP[] = ", "; + + size_t len = strlen(filterName); + if ( len != 0 ) + { + strncat( filterName, SEP, bufsize - len - 1 ); + len += strlen(SEP); + } + + strncat( filterName, typebuf, bufsize - len - 1 ); +} + +static void AddFiltersToDialog( GtkWidget *dialog, const char *filterList ) +{ + GtkFileFilter *filter; + char typebuf[NFD_MAX_STRLEN] = {0}; + const char *p_filterList = filterList; + char *p_typebuf = typebuf; + char filterName[NFD_MAX_STRLEN] = {0}; + + if ( !filterList || strlen(filterList) == 0 ) + return; + + filter = gtk_file_filter_new(); + while ( 1 ) + { + + if ( NFDi_IsFilterSegmentChar(*p_filterList) ) + { + char typebufWildcard[NFD_MAX_STRLEN]; + /* add another type to the filter */ + assert( strlen(typebuf) > 0 ); + assert( strlen(typebuf) < NFD_MAX_STRLEN-1 ); + + snprintf( typebufWildcard, NFD_MAX_STRLEN, "*.%s", typebuf ); + AddTypeToFilterName( typebuf, filterName, NFD_MAX_STRLEN ); + + gtk_file_filter_add_pattern( filter, typebufWildcard ); + + p_typebuf = typebuf; + memset( typebuf, 0, sizeof(char) * NFD_MAX_STRLEN ); + } + + if ( *p_filterList == ';' || *p_filterList == '\0' ) + { + /* end of filter -- add it to the dialog */ + + gtk_file_filter_set_name( filter, filterName ); + gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter ); + + filterName[0] = '\0'; + + if ( *p_filterList == '\0' ) + break; + + filter = gtk_file_filter_new(); + } + + if ( !NFDi_IsFilterSegmentChar( *p_filterList ) ) + { + *p_typebuf = *p_filterList; + p_typebuf++; + } + + p_filterList++; + } + + /* always append a wildcard option to the end*/ + + filter = gtk_file_filter_new(); + gtk_file_filter_set_name( filter, "*.*" ); + gtk_file_filter_add_pattern( filter, "*" ); + gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter ); +} + +static void SetDefaultPath( GtkWidget *dialog, const char *defaultPath ) +{ + if ( !defaultPath || strlen(defaultPath) == 0 ) + return; + + /* GTK+ manual recommends not specifically setting the default path. + We do it anyway in order to be consistent across platforms. + + If consistency with the native OS is preferred, this is the line + to comment out. -ml */ + gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), defaultPath ); +} + +static nfdresult_t AllocPathSet( GSList *fileList, nfdpathset_t *pathSet ) +{ + size_t bufSize = 0; + GSList *node; + nfdchar_t *p_buf; + size_t count = 0; + + assert(fileList); + assert(pathSet); + + pathSet->count = (size_t)g_slist_length( fileList ); + assert( pathSet->count > 0 ); + + pathSet->indices = NFDi_Malloc( sizeof(size_t)*pathSet->count ); + if ( !pathSet->indices ) + { + return NFD_ERROR; + } + + /* count the total space needed for buf */ + for ( node = fileList; node; node = node->next ) + { + assert(node->data); + bufSize += strlen( (const gchar*)node->data ) + 1; + } + + pathSet->buf = NFDi_Malloc( sizeof(nfdchar_t) * bufSize ); + + /* fill buf */ + p_buf = pathSet->buf; + for ( node = fileList; node; node = node->next ) + { + nfdchar_t *path = (nfdchar_t*)(node->data); + size_t byteLen = strlen(path)+1; + ptrdiff_t index; + + memcpy( p_buf, path, byteLen ); + g_free(node->data); + + index = p_buf - pathSet->buf; + assert( index >= 0 ); + pathSet->indices[count] = (size_t)index; + + p_buf += byteLen; + ++count; + } + + g_slist_free( fileList ); + + return NFD_OKAY; +} + +static void WaitForCleanup(void) +{ + while (gtk_events_pending()) + gtk_main_iteration(); +} + +/* public */ + +nfdresult_t NFD_OpenDialog( const char *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ) +{ + GtkWidget *dialog; + nfdresult_t result; + + if ( !gtk_init_check( NULL, NULL ) ) + { + NFDi_SetError(INIT_FAIL_MSG); + return NFD_ERROR; + } + + dialog = gtk_file_chooser_dialog_new( "Open File", + NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Open", GTK_RESPONSE_ACCEPT, + NULL ); + + /* Build the filter list */ + AddFiltersToDialog(dialog, filterList); + + /* Set the default path */ + SetDefaultPath(dialog, defaultPath); + + result = NFD_CANCEL; + if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) + { + char *filename; + + filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); + + { + size_t len = strlen(filename); + *outPath = NFDi_Malloc( len + 1 ); + memcpy( *outPath, filename, len + 1 ); + if ( !*outPath ) + { + g_free( filename ); + gtk_widget_destroy(dialog); + return NFD_ERROR; + } + } + g_free( filename ); + + result = NFD_OKAY; + } + + WaitForCleanup(); + gtk_widget_destroy(dialog); + WaitForCleanup(); + + return result; +} + + +nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdpathset_t *outPaths ) +{ + GtkWidget *dialog; + nfdresult_t result; + + if ( !gtk_init_check( NULL, NULL ) ) + { + NFDi_SetError(INIT_FAIL_MSG); + return NFD_ERROR; + } + + dialog = gtk_file_chooser_dialog_new( "Open Files", + NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Open", GTK_RESPONSE_ACCEPT, + NULL ); + gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER(dialog), TRUE ); + + /* Build the filter list */ + AddFiltersToDialog(dialog, filterList); + + /* Set the default path */ + SetDefaultPath(dialog, defaultPath); + + result = NFD_CANCEL; + if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) + { + GSList *fileList = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER(dialog) ); + if ( AllocPathSet( fileList, outPaths ) == NFD_ERROR ) + { + gtk_widget_destroy(dialog); + return NFD_ERROR; + } + + result = NFD_OKAY; + } + + WaitForCleanup(); + gtk_widget_destroy(dialog); + WaitForCleanup(); + + return result; +} + +nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ) +{ + GtkWidget *dialog; + nfdresult_t result; + + if ( !gtk_init_check( NULL, NULL ) ) + { + NFDi_SetError(INIT_FAIL_MSG); + return NFD_ERROR; + } + + dialog = gtk_file_chooser_dialog_new( "Save File", + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Save", GTK_RESPONSE_ACCEPT, + NULL ); + gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), TRUE ); + + /* Build the filter list */ + AddFiltersToDialog(dialog, filterList); + + /* Set the default path */ + SetDefaultPath(dialog, defaultPath); + + result = NFD_CANCEL; + if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) + { + char *filename; + filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); + + { + size_t len = strlen(filename); + *outPath = NFDi_Malloc( len + 1 ); + memcpy( *outPath, filename, len + 1 ); + if ( !*outPath ) + { + g_free( filename ); + gtk_widget_destroy(dialog); + return NFD_ERROR; + } + } + g_free(filename); + + result = NFD_OKAY; + } + + WaitForCleanup(); + gtk_widget_destroy(dialog); + WaitForCleanup(); + + return result; +} + +nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath, + nfdchar_t **outPath) +{ + GtkWidget *dialog; + nfdresult_t result; + + if (!gtk_init_check(NULL, NULL)) + { + NFDi_SetError(INIT_FAIL_MSG); + return NFD_ERROR; + } + + dialog = gtk_file_chooser_dialog_new( "Select folder", + NULL, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Select", GTK_RESPONSE_ACCEPT, + NULL ); + gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), TRUE ); + + + /* Set the default path */ + SetDefaultPath(dialog, defaultPath); + + result = NFD_CANCEL; + if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) + { + char *filename; + filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); + + { + size_t len = strlen(filename); + *outPath = NFDi_Malloc( len + 1 ); + memcpy( *outPath, filename, len + 1 ); + if ( !*outPath ) + { + g_free( filename ); + gtk_widget_destroy(dialog); + return NFD_ERROR; + } + } + g_free(filename); + + result = NFD_OKAY; + } + + WaitForCleanup(); + gtk_widget_destroy(dialog); + WaitForCleanup(); + + return result; +} diff --git a/ext/NativeFileDialog/src/nfd_win.cpp b/ext/NativeFileDialog/src/nfd_win.cpp new file mode 100644 index 00000000..a73fd8a4 --- /dev/null +++ b/ext/NativeFileDialog/src/nfd_win.cpp @@ -0,0 +1,753 @@ +/* + Native File Dialog + + http://www.frogtoss.com/labs + */ + +/* only locally define UNICODE in this compilation unit */ +#ifndef UNICODE +#define UNICODE +#endif + +#ifdef __MINGW32__ +// Explicitly setting NTDDI version, this is necessary for the MinGW compiler +#define NTDDI_VERSION NTDDI_VISTA +#define _WIN32_WINNT _WIN32_WINNT_VISTA +#endif + +#include +#include +#include +#include +#include +#include "nfd_common.h" + + +// allocs the space in outPath -- call free() +static void CopyWCharToNFDChar( const wchar_t *inStr, nfdchar_t **outStr ) +{ + int inStrCharacterCount = static_cast(wcslen(inStr)); + int bytesNeeded = WideCharToMultiByte( CP_UTF8, 0, + inStr, inStrCharacterCount, + NULL, 0, NULL, NULL ); + assert( bytesNeeded ); + bytesNeeded += 1; + + *outStr = (nfdchar_t*)NFDi_Malloc( bytesNeeded ); + if ( !*outStr ) + return; + + int bytesWritten = WideCharToMultiByte( CP_UTF8, 0, + inStr, -1, + *outStr, bytesNeeded, + NULL, NULL ); + assert( bytesWritten ); _NFD_UNUSED( bytesWritten ); +} + +/* includes NULL terminator byte in return */ +static size_t GetUTF8ByteCountForWChar( const wchar_t *str ) +{ + int bytesNeeded = WideCharToMultiByte( CP_UTF8, 0, + str, -1, + NULL, 0, NULL, NULL ); + assert( bytesNeeded ); + return bytesNeeded+1; +} + +// write to outPtr -- no free() necessary. No memory stomp tests are done -- they must be done +// before entering this function. +static int CopyWCharToExistingNFDCharBuffer( const wchar_t *inStr, nfdchar_t *outPtr ) +{ + int inStrCharacterCount = static_cast(wcslen(inStr)); + int bytesNeeded = static_cast(GetUTF8ByteCountForWChar( inStr )); + + /* invocation copies null term */ + int bytesWritten = WideCharToMultiByte( CP_UTF8, 0, + inStr, -1, + outPtr, bytesNeeded, + NULL, 0 ); + assert( bytesWritten ); + + return bytesWritten; + +} + + +// allocs the space in outStr -- call free() +static void CopyNFDCharToWChar( const nfdchar_t *inStr, wchar_t **outStr ) +{ + int inStrByteCount = static_cast(strlen(inStr)); + int charsNeeded = MultiByteToWideChar(CP_UTF8, 0, + inStr, inStrByteCount, + NULL, 0 ); + assert( charsNeeded ); + assert( !*outStr ); + charsNeeded += 1; // terminator + + *outStr = (wchar_t*)NFDi_Malloc( charsNeeded * sizeof(wchar_t) ); + if ( !*outStr ) + return; + + int ret = MultiByteToWideChar(CP_UTF8, 0, + inStr, inStrByteCount, + *outStr, charsNeeded); + (*outStr)[charsNeeded-1] = '\0'; + +#ifdef _DEBUG + int inStrCharacterCount = static_cast(NFDi_UTF8_Strlen(inStr)); + assert( ret == inStrCharacterCount ); +#else + _NFD_UNUSED(ret); +#endif +} + + +/* ext is in format "jpg", no wildcards or separators */ +static int AppendExtensionToSpecBuf( const char *ext, char *specBuf, size_t specBufLen ) +{ + const char SEP[] = ";"; + assert( specBufLen > strlen(ext)+3 ); + + if ( strlen(specBuf) > 0 ) + { + strncat( specBuf, SEP, specBufLen - strlen(specBuf) - 1 ); + specBufLen += strlen(SEP); + } + + char extWildcard[NFD_MAX_STRLEN]; + int bytesWritten = sprintf_s( extWildcard, NFD_MAX_STRLEN, "*.%s", ext ); + assert( bytesWritten == strlen(ext)+2 ); + + strncat( specBuf, extWildcard, specBufLen - strlen(specBuf) - 1 ); + + return NFD_OKAY; +} + +static nfdresult_t AddFiltersToDialog( ::IFileDialog *fileOpenDialog, const char *filterList ) +{ + const wchar_t EMPTY_WSTR[] = L""; + const wchar_t WILDCARD[] = L"*.*"; + + if ( !filterList || strlen(filterList) == 0 ) + return NFD_OKAY; + + // Count rows to alloc + UINT filterCount = 1; /* guaranteed to have one filter on a correct, non-empty parse */ + const char *p_filterList; + for ( p_filterList = filterList; *p_filterList; ++p_filterList ) + { + if ( *p_filterList == ';' ) + ++filterCount; + } + + assert(filterCount); + if ( !filterCount ) + { + NFDi_SetError("Error parsing filters."); + return NFD_ERROR; + } + + /* filterCount plus 1 because we hardcode the *.* wildcard after the while loop */ + COMDLG_FILTERSPEC *specList = (COMDLG_FILTERSPEC*)NFDi_Malloc( sizeof(COMDLG_FILTERSPEC) * (filterCount + 1) ); + if ( !specList ) + { + return NFD_ERROR; + } + for (size_t i = 0; i < filterCount+1; ++i ) + { + specList[i].pszName = NULL; + specList[i].pszSpec = NULL; + } + + size_t specIdx = 0; + p_filterList = filterList; + char typebuf[NFD_MAX_STRLEN] = {0}; /* one per comma or semicolon */ + char *p_typebuf = typebuf; + char filterName[NFD_MAX_STRLEN] = {0}; + + char specbuf[NFD_MAX_STRLEN] = {0}; /* one per semicolon */ + + while ( 1 ) + { + if ( NFDi_IsFilterSegmentChar(*p_filterList) ) + { + /* append a type to the specbuf (pending filter) */ + AppendExtensionToSpecBuf( typebuf, specbuf, NFD_MAX_STRLEN ); + + p_typebuf = typebuf; + memset( typebuf, 0, sizeof(char)*NFD_MAX_STRLEN ); + } + + if ( *p_filterList == ';' || *p_filterList == '\0' ) + { + /* end of filter -- add it to specList */ + + // Empty filter name -- Windows describes them by extension. + specList[specIdx].pszName = EMPTY_WSTR; + CopyNFDCharToWChar( specbuf, (wchar_t**)&specList[specIdx].pszSpec ); + + memset( specbuf, 0, sizeof(char)*NFD_MAX_STRLEN ); + ++specIdx; + if ( specIdx == filterCount ) + break; + } + + if ( !NFDi_IsFilterSegmentChar( *p_filterList )) + { + *p_typebuf = *p_filterList; + ++p_typebuf; + } + + ++p_filterList; + } + + /* Add wildcard */ + specList[specIdx].pszSpec = WILDCARD; + specList[specIdx].pszName = EMPTY_WSTR; + + fileOpenDialog->SetFileTypes( filterCount+1, specList ); + + /* free speclist */ + for ( size_t i = 0; i < filterCount; ++i ) + { + NFDi_Free( (void*)specList[i].pszSpec ); + } + NFDi_Free( specList ); + + return NFD_OKAY; +} + +static nfdresult_t AllocPathSet( IShellItemArray *shellItems, nfdpathset_t *pathSet ) +{ + const char ERRORMSG[] = "Error allocating pathset."; + + assert(shellItems); + assert(pathSet); + + // How many items in shellItems? + DWORD numShellItems; + HRESULT result = shellItems->GetCount(&numShellItems); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError(ERRORMSG); + return NFD_ERROR; + } + + pathSet->count = static_cast(numShellItems); + assert( pathSet->count > 0 ); + + pathSet->indices = (size_t*)NFDi_Malloc( sizeof(size_t)*pathSet->count ); + if ( !pathSet->indices ) + { + return NFD_ERROR; + } + + /* count the total bytes needed for buf */ + size_t bufSize = 0; + for ( DWORD i = 0; i < numShellItems; ++i ) + { + ::IShellItem *shellItem; + result = shellItems->GetItemAt(i, &shellItem); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError(ERRORMSG); + return NFD_ERROR; + } + + // Confirm SFGAO_FILESYSTEM is true for this shellitem, or ignore it. + SFGAOF attribs; + result = shellItem->GetAttributes( SFGAO_FILESYSTEM, &attribs ); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError(ERRORMSG); + return NFD_ERROR; + } + if ( !(attribs & SFGAO_FILESYSTEM) ) + continue; + + LPWSTR name; + shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name); + + // Calculate length of name with UTF-8 encoding + bufSize += GetUTF8ByteCountForWChar( name ); + } + + assert(bufSize); + + pathSet->buf = (nfdchar_t*)NFDi_Malloc( sizeof(nfdchar_t) * bufSize ); + memset( pathSet->buf, 0, sizeof(nfdchar_t) * bufSize ); + + /* fill buf */ + nfdchar_t *p_buf = pathSet->buf; + for (DWORD i = 0; i < numShellItems; ++i ) + { + ::IShellItem *shellItem; + result = shellItems->GetItemAt(i, &shellItem); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError(ERRORMSG); + return NFD_ERROR; + } + + // Confirm SFGAO_FILESYSTEM is true for this shellitem, or ignore it. + SFGAOF attribs; + result = shellItem->GetAttributes( SFGAO_FILESYSTEM, &attribs ); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError(ERRORMSG); + return NFD_ERROR; + } + if ( !(attribs & SFGAO_FILESYSTEM) ) + continue; + + LPWSTR name; + shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name); + + int bytesWritten = CopyWCharToExistingNFDCharBuffer(name, p_buf); + + ptrdiff_t index = p_buf - pathSet->buf; + assert( index >= 0 ); + pathSet->indices[i] = static_cast(index); + + p_buf += bytesWritten; + } + + return NFD_OKAY; +} + + +static nfdresult_t SetDefaultPath( IFileDialog *dialog, const char *defaultPath ) +{ + if ( !defaultPath || strlen(defaultPath) == 0 ) + return NFD_OKAY; + + wchar_t *defaultPathW = {0}; + CopyNFDCharToWChar( defaultPath, &defaultPathW ); + + IShellItem *folder; + HRESULT result = SHCreateItemFromParsingName( defaultPathW, NULL, IID_PPV_ARGS(&folder) ); + + // Valid non results. + if ( result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || result == HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE) ) + { + NFDi_Free( defaultPathW ); + return NFD_OKAY; + } + + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Error creating ShellItem"); + NFDi_Free( defaultPathW ); + return NFD_ERROR; + } + + // Could also call SetDefaultFolder(), but this guarantees defaultPath -- more consistency across API. + dialog->SetFolder( folder ); + + NFDi_Free( defaultPathW ); + folder->Release(); + + return NFD_OKAY; +} + +/* public */ + + +nfdresult_t NFD_OpenDialog( const char *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ) +{ + nfdresult_t nfdResult = NFD_ERROR; + + // Init COM library. + HRESULT result = ::CoInitializeEx(NULL, + ::COINIT_APARTMENTTHREADED | + ::COINIT_DISABLE_OLE1DDE ); + + ::IFileOpenDialog *fileOpenDialog(NULL); + + if ( !SUCCEEDED(result)) + { + NFDi_SetError("Could not initialize COM."); + goto end; + } + + // Create dialog + result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL, + CLSCTX_ALL, ::IID_IFileOpenDialog, + reinterpret_cast(&fileOpenDialog) ); + + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not create dialog."); + goto end; + } + + // Build the filter list + if ( !AddFiltersToDialog( fileOpenDialog, filterList ) ) + { + goto end; + } + + // Set the default path + if ( !SetDefaultPath( fileOpenDialog, defaultPath ) ) + { + goto end; + } + + // Show the dialog. + result = fileOpenDialog->Show(NULL); + if ( SUCCEEDED(result) ) + { + // Get the file name + ::IShellItem *shellItem(NULL); + result = fileOpenDialog->GetResult(&shellItem); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not get shell item from dialog."); + goto end; + } + wchar_t *filePath(NULL); + result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not get file path for selected."); + goto end; + } + + CopyWCharToNFDChar( filePath, outPath ); + CoTaskMemFree(filePath); + if ( !*outPath ) + { + /* error is malloc-based, error message would be redundant */ + goto end; + } + + nfdResult = NFD_OKAY; + shellItem->Release(); + } + else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) ) + { + nfdResult = NFD_CANCEL; + } + else + { + NFDi_SetError("File dialog box show failed."); + nfdResult = NFD_ERROR; + } + + end: + ::CoUninitialize(); + + return nfdResult; +} + +nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdpathset_t *outPaths ) +{ + nfdresult_t nfdResult = NFD_ERROR; + + // Init COM library. + HRESULT result = ::CoInitializeEx(NULL, + ::COINIT_APARTMENTTHREADED | + ::COINIT_DISABLE_OLE1DDE ); + if ( !SUCCEEDED(result)) + { + NFDi_SetError("Could not initialize COM."); + return NFD_ERROR; + } + + ::IFileOpenDialog *fileOpenDialog(NULL); + + // Create dialog + result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL, + CLSCTX_ALL, ::IID_IFileOpenDialog, + reinterpret_cast(&fileOpenDialog) ); + + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not create dialog."); + goto end; + } + + // Build the filter list + if ( !AddFiltersToDialog( fileOpenDialog, filterList ) ) + { + goto end; + } + + // Set the default path + if ( !SetDefaultPath( fileOpenDialog, defaultPath ) ) + { + goto end; + } + + // Set a flag for multiple options + DWORD dwFlags; + result = fileOpenDialog->GetOptions(&dwFlags); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not get options."); + goto end; + } + result = fileOpenDialog->SetOptions(dwFlags | FOS_ALLOWMULTISELECT); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not set options."); + goto end; + } + + // Show the dialog. + result = fileOpenDialog->Show(NULL); + if ( SUCCEEDED(result) ) + { + IShellItemArray *shellItems; + result = fileOpenDialog->GetResults( &shellItems ); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not get shell items."); + goto end; + } + + if ( AllocPathSet( shellItems, outPaths ) == NFD_ERROR ) + { + goto end; + } + + shellItems->Release(); + nfdResult = NFD_OKAY; + } + else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) ) + { + nfdResult = NFD_CANCEL; + } + else + { + NFDi_SetError("File dialog box show failed."); + nfdResult = NFD_ERROR; + } + + end: + ::CoUninitialize(); + + return nfdResult; +} + +nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ) +{ + nfdresult_t nfdResult = NFD_ERROR; + + // Init COM library. + HRESULT result = ::CoInitializeEx(NULL, + ::COINIT_APARTMENTTHREADED | + ::COINIT_DISABLE_OLE1DDE ); + if ( !SUCCEEDED(result)) + { + NFDi_SetError("Could not initialize COM."); + return NFD_ERROR; + } + + ::IFileSaveDialog *fileSaveDialog(NULL); + + // Create dialog + result = ::CoCreateInstance(::CLSID_FileSaveDialog, NULL, + CLSCTX_ALL, ::IID_IFileSaveDialog, + reinterpret_cast(&fileSaveDialog) ); + + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not create dialog."); + goto end; + } + + // Build the filter list + if ( !AddFiltersToDialog( fileSaveDialog, filterList ) ) + { + goto end; + } + + // Set the default path + if ( !SetDefaultPath( fileSaveDialog, defaultPath ) ) + { + goto end; + } + + // Show the dialog. + result = fileSaveDialog->Show(NULL); + if ( SUCCEEDED(result) ) + { + // Get the file name + ::IShellItem *shellItem; + result = fileSaveDialog->GetResult(&shellItem); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not get shell item from dialog."); + goto end; + } + wchar_t *filePath(NULL); + result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not get file path for selected."); + goto end; + } + + CopyWCharToNFDChar( filePath, outPath ); + CoTaskMemFree(filePath); + if ( !*outPath ) + { + /* error is malloc-based, error message would be redundant */ + goto end; + } + + nfdResult = NFD_OKAY; + shellItem->Release(); + } + else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) ) + { + nfdResult = NFD_CANCEL; + } + else + { + NFDi_SetError("File dialog box show failed."); + nfdResult = NFD_ERROR; + } + + end: + ::CoUninitialize(); + + return nfdResult; +} + +class AutoCoInit +{ +public: + AutoCoInit() + { + mResult = ::CoInitializeEx(NULL, + ::COINIT_APARTMENTTHREADED | + ::COINIT_DISABLE_OLE1DDE); + } + + ~AutoCoInit() + { + if (SUCCEEDED(mResult)) + { + ::CoUninitialize(); + } + } + + HRESULT Result() const { return mResult; } +private: + HRESULT mResult; +}; + +// VS2010 hasn't got a copy of CComPtr - this was first added in the 2003 SDK, so we make our own small CComPtr instead +template +class ComPtr +{ +public: + ComPtr() : mPtr(NULL) { } + ~ComPtr() + { + if (mPtr) + { + mPtr->Release(); + } + } + + T* Ptr() const { return mPtr; } + T** operator&() { return &mPtr; } + T* operator->() const { return mPtr; } +private: + // Don't allow copy or assignment + ComPtr(const ComPtr&); + ComPtr& operator = (const ComPtr&) const; + T* mPtr; +}; + +nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath, + nfdchar_t **outPath) +{ + // Init COM + AutoCoInit autoCoInit; + if (!SUCCEEDED(autoCoInit.Result())) + { + NFDi_SetError("CoInitializeEx failed."); + return NFD_ERROR; + } + + // Create the file dialog COM object + ComPtr pFileDialog; + if (!SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog, + NULL, + CLSCTX_ALL, + IID_PPV_ARGS(&pFileDialog)))) + { + NFDi_SetError("CoCreateInstance for CLSID_FileOpenDialog failed."); + return NFD_ERROR; + } + + // Set the default path + if (SetDefaultPath(pFileDialog.Ptr(), defaultPath) != NFD_OKAY) + { + NFDi_SetError("SetDefaultPath failed."); + return NFD_ERROR; + } + + // Get the dialogs options + DWORD dwOptions = 0; + if (!SUCCEEDED(pFileDialog->GetOptions(&dwOptions))) + { + NFDi_SetError("GetOptions for IFileDialog failed."); + return NFD_ERROR; + } + + // Add in FOS_PICKFOLDERS which hides files and only allows selection of folders + if (!SUCCEEDED(pFileDialog->SetOptions(dwOptions | FOS_PICKFOLDERS))) + { + NFDi_SetError("SetOptions for IFileDialog failed."); + return NFD_ERROR; + } + + // Show the dialog to the user + const HRESULT result = pFileDialog->Show(NULL); + if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED)) + { + return NFD_CANCEL; + } + else if (!SUCCEEDED(result)) + { + NFDi_SetError("Show for IFileDialog failed."); + return NFD_ERROR; + } + + // Get the shell item result + ComPtr pShellItem; + if (!SUCCEEDED(pFileDialog->GetResult(&pShellItem))) + { + return NFD_OKAY; + } + + // Finally get the path + wchar_t *path = NULL; + if (!SUCCEEDED(pShellItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &path))) + { + NFDi_SetError("GetDisplayName for IShellItem failed."); + return NFD_ERROR; + } + + // Convert string + CopyWCharToNFDChar(path, outPath); + CoTaskMemFree(path); + if (!*outPath) + { + // error is malloc-based, error message would be redundant + return NFD_ERROR; + } + + return NFD_OKAY; +} diff --git a/ext/enkiTS-C-11/License.txt b/ext/enkiTS-C-11/License.txt new file mode 100644 index 00000000..76264b00 --- /dev/null +++ b/ext/enkiTS-C-11/License.txt @@ -0,0 +1,17 @@ +Copyright (c) 2013 Doug Binks + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgement in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. \ No newline at end of file diff --git a/ext/enkiTS-C-11/README.md b/ext/enkiTS-C-11/README.md new file mode 100644 index 00000000..6fc8dba3 --- /dev/null +++ b/ext/enkiTS-C-11/README.md @@ -0,0 +1,163 @@ +# enkiTS +[![Build Status for branch: C++11](https://travis-ci.org/dougbinks/enkiTS.svg?branch=C%2B%2B11)](https://travis-ci.org/dougbinks/enkiTS) + +## enki Task Scheduler + +A permissively licensed C and C++ Task Scheduler for creating parallel programs. + +* [C API via src/TaskScheduler_c.h](src/TaskScheduler_c.h) +* [C++ API via src/TaskScheduler.h](src/TaskScheduler.h) +* [C++ 11 version on Branch C++11](https://github.com/dougbinks/enkiTS/tree/C++11) +* [User thread version on Branch UserThread](https://github.com/dougbinks/enkiTS/tree/UserThread) for running enkiTS on other tasking / threading systems, so it can be used as in other engines as well as standalone for example. +* [C++ 11 version of user threads on Branch UserThread_C++11](https://github.com/dougbinks/enkiTS/tree/UserThread_C++11) + + +Note - this is a work in progress conversion from my code for [enkisoftware](http://www.enkisoftware.com/)'s Avoyd codebase, with [RuntimeCompiledC++](https://github.com/RuntimeCompiledCPlusPlus/RuntimeCompiledCPlusPlus) removed along with the removal of profiling code. + +As this was originally written before widespread decent C++11 support for atomics and threads, these are implemented here per-platform supporting Windows, Linux and OSX on Intel x86 / x64 (also works on ARM iOS and Android but not as well tested). [A separate C++11 branch exists](https://github.com/dougbinks/enkiTS/tree/C++11) for those who would like to use it, but this currently has slightly slower performance under very high task throughput when there is low work per task. + +The example code requires C++ 11 for chrono (and for [C++ 11 features in the C++11 branch C++11](https://github.com/dougbinks/enkiTS/tree/C++11) ) + +For further examples, see https://github.com/dougbinks/enkiTSExamples + +## Building + +Building enkiTS is simple, just add the files in enkiTS/src to your build system (_c.* files can be ignored if you only need C++ interface), and add enkiTS/src to your include path. Unix / Linux builds will require the pthreads library. + +For cmake, on Windows / Mac OS X / Linux with cmake installed, open a prompt in the enkiTS directory and: + +1. `mkdir build` +2. `cmake ..` +3. either run `make` or open `enkiTS.sln` + +## Project Features + +1. *Lightweight* - enkiTS is designed to be lean so you can use it anywhere easily, and understand it. +1. *Fast, then scalable* - enkiTS is designed for consumer devices first, so performance on a low number of threads is important, followed by scalability. +1. *Braided parallelism* - enkiTS can issue tasks from another task as well as from the thread which created the Task System. +1. *Up-front Allocation friendly* - enkiTS is designed for zero allocations during scheduling. +1. **NEW** - *Can pin tasks to a given thread* - enkiTS can schedule a task which will only be run on the specified thread. + +## Usage + +C usage: +```C +#include "TaskScheduler_c.h" + +enkiTaskScheduler* g_pTS; + +void ParalleTaskSetFunc( uint32_t start_, uint32_t end, uint32_t threadnum_, void* pArgs_ ) { + /* Do something here, can issue tasks with g_pTS */ +} + +int main(int argc, const char * argv[]) { + enkiTaskSet* pTask; + g_pTS = enkiNewTaskScheduler(); + enkiInitTaskScheduler( g_pTS ); + + // create a task, can re-use this to get allocation occurring on startup + pTask = enkiCreateTaskSet( g_pTS, ParalleTaskSetFunc ); + + enkiAddTaskSetToPipe( g_pTS, pTask, NULL, 1); // NULL args, setsize of 1 + + // wait for task set (running tasks if they exist) - since we've just added it and it has no range we'll likely run it. + enkiWaitForTaskSet( g_pTS, pTask ); + + enkiDeleteTaskSet( pTask ); + + enkiDeleteTaskScheduler( g_pTS ); + + return 0; +} +``` + +C++ usage: +```C +#include "TaskScheduler.h" + +enki::TaskScheduler g_TS; + +// define a task set, can ignore range if we only do one thing +struct ParallelTaskSet : enki::ITaskSet { + virtual void ExecuteRange( enki::TaskSetPartition range, uint32_t threadnum ) { + // do something here, can issue tasks with g_TS + } +}; + +int main(int argc, const char * argv[]) { + g_TS.Initialize(); + ParallelTaskSet task; // default constructor has a set size of 1 + g_TS.AddTaskSetToPipe( &task ); + + // wait for task set (running tasks if they exist) - since we've just added it and it has no range we'll likely run it. + g_TS.WaitforTask( &task ); + return 0; +} +``` + +C++ 11 usage (currently requires [C++11 branch](https://github.com/dougbinks/enkiTS/tree/C++11), or define own lambda wrapper taskset interface. +```C +#include "TaskScheduler.h" + +enki::TaskScheduler g_TS; + +int main(int argc, const char * argv[]) { + g_TS.Initialize(); + + enki::TaskSet task( 1, []( enki::TaskSetPartition range, uint32_t threadnum ) { + // do something here + } ); + + g_TS.AddTaskSetToPipe( &task ); + g_TS.WaitforTask( &task ); + return 0; +} +``` + +Pinned Tasks usage in C++ (see example/PinnedTask_c.c for C example). +```C +#include "TaskScheduler.h" + +enki::TaskScheduler g_TS; + +// define a task set, can ignore range if we only do one thing +struct PinnedTask : enki::IPinnedTask { + virtual void Execute() { + // do something here, can issue tasks with g_TS + } +}; + +int main(int argc, const char * argv[]) { + g_TS.Initialize(); + PinnedTask task; //default constructor sets thread for pinned task to 0 (main thread) + g_TS.AddPinnedTask( &task ); + + // RunPinnedTasks must be called on main thread to run any pinned tasks for that thread. + // Tasking threads automatically do this in their task loop. + g_TS.RunPinnedTasks(); + // wait for task set (running tasks if they exist) - since we've just added it and it has no range we'll likely run it. + g_TS.WaitforTask( &task ); + return 0; +} +``` + + +## License (zlib) + +Copyright (c) 2013 Doug Binks + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgement in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff --git a/ext/enkiTS-C-11/src/LockLessMultiReadPipe.h b/ext/enkiTS-C-11/src/LockLessMultiReadPipe.h new file mode 100644 index 00000000..81649ac5 --- /dev/null +++ b/ext/enkiTS-C-11/src/LockLessMultiReadPipe.h @@ -0,0 +1,284 @@ +// Copyright (c) 2013 Doug Binks +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgement in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +#pragma once + +#include +#include + +#include +#include + + +namespace enki +{ + // LockLessMultiReadPipe - Single writer, multiple reader thread safe pipe using (semi) lockless programming + // Readers can only read from the back of the pipe + // The single writer can write to the front of the pipe, and read from both ends (a writer can be a reader) + // for many of the principles used here, see http://msdn.microsoft.com/en-us/library/windows/desktop/ee418650(v=vs.85).aspx + // Note: using log2 sizes so we do not need to clamp (multi-operation) + // T is the contained type + // Note this is not true lockless as the use of flags as a form of lock state. + template class LockLessMultiReadPipe + { + public: + LockLessMultiReadPipe(); + ~LockLessMultiReadPipe() {} + + // ReaderTryReadBack returns false if we were unable to read + // This is thread safe for both multiple readers and the writer + bool ReaderTryReadBack( T* pOut ); + + // WriterTryReadFront returns false if we were unable to read + // This is thread safe for the single writer, but should not be called by readers + bool WriterTryReadFront( T* pOut ); + + // WriterTryWriteFront returns false if we were unable to write + // This is thread safe for the single writer, but should not be called by readers + bool WriterTryWriteFront( const T& in ); + + // IsPipeEmpty() is a utility function, not intended for general use + // Should only be used very prudently. + bool IsPipeEmpty() const + { + return 0 == m_WriteIndex.load( std::memory_order_relaxed ) - m_ReadCount.load( std::memory_order_relaxed ); + } + + void Clear() + { + m_WriteIndex = 0; + m_ReadIndex = 0; + m_ReadCount = 0; + memset( (void*)m_Flags, 0, sizeof( m_Flags ) ); + } + + private: + const static uint32_t ms_cSize = ( 1 << cSizeLog2 ); + const static uint32_t ms_cIndexMask = ms_cSize - 1; + const static uint32_t FLAG_INVALID = 0xFFFFFFFF; // 32bit for CAS + const static uint32_t FLAG_CAN_WRITE = 0x00000000; // 32bit for CAS + const static uint32_t FLAG_CAN_READ = 0x11111111; // 32bit for CAS + + T m_Buffer[ ms_cSize ]; + + // read and write indexes allow fast access to the pipe, but actual access + // controlled by the access flags. + std::atomic m_WriteIndex; + std::atomic m_ReadCount; + std::atomic m_Flags[ ms_cSize ]; + std::atomic m_ReadIndex; + }; + + template inline + LockLessMultiReadPipe::LockLessMultiReadPipe() + : m_WriteIndex(0) + , m_ReadIndex(0) + , m_ReadCount(0) + { + assert( cSizeLog2 < 32 ); + memset( (void*)m_Flags, 0, sizeof( m_Flags ) ); + } + + template inline + bool LockLessMultiReadPipe::ReaderTryReadBack( T* pOut ) + { + + uint32_t actualReadIndex; + uint32_t readCount = m_ReadCount.load( std::memory_order_relaxed ); + + // We get hold of read index for consistency + // and do first pass starting at read count + uint32_t readIndexToUse = readCount; + while(true) + { + + uint32_t writeIndex = m_WriteIndex.load( std::memory_order_relaxed ); + // power of two sizes ensures we can use a simple calc without modulus + uint32_t numInPipe = writeIndex - readCount; + if( 0 == numInPipe ) + { + return false; + } + if( readIndexToUse >= writeIndex ) + { + readIndexToUse = m_ReadIndex.load( std::memory_order_relaxed ); + } + + // power of two sizes ensures we can perform AND for a modulus + actualReadIndex = readIndexToUse & ms_cIndexMask; + + // Multiple potential readers mean we should check if the data is valid, + // using an atomic compare exchange + uint32_t previous = FLAG_CAN_READ; + m_Flags[ actualReadIndex ].compare_exchange_weak( previous, FLAG_INVALID, std::memory_order_relaxed ); + if( FLAG_CAN_READ == previous ) + { + break; + } + ++readIndexToUse; + + // Update read count + readCount = m_ReadCount.load( std::memory_order_relaxed ); + } + + // we update the read index using an atomic add, as we've only read one piece of data. + // this ensure consistency of the read index, and the above loop ensures readers + // only read from unread data + m_ReadCount.fetch_add(1, std::memory_order_relaxed ); + + std::atomic_thread_fence( std::memory_order_acquire ); + // now read data, ensuring we do so after above reads & CAS + *pOut = m_Buffer[ actualReadIndex ]; + + m_Flags[ actualReadIndex ].store( FLAG_CAN_WRITE, std::memory_order_relaxed ); + + return true; + } + + template inline + bool LockLessMultiReadPipe::WriterTryReadFront( T* pOut ) + { + uint32_t writeIndex = m_WriteIndex.load( std::memory_order_relaxed ); + uint32_t frontReadIndex = writeIndex; + + // Multiple potential readers mean we should check if the data is valid, + // using an atomic compare exchange - which acts as a form of lock (so not quite lockless really). + uint32_t actualReadIndex = 0; + while(true) + { + uint32_t readCount = m_ReadCount.load( std::memory_order_relaxed ); + // power of two sizes ensures we can use a simple calc without modulus + uint32_t numInPipe = writeIndex - readCount; + if( 0 == numInPipe ) + { + m_ReadIndex.store( readCount, std::memory_order_release ); + return false; + } + --frontReadIndex; + actualReadIndex = frontReadIndex & ms_cIndexMask; + uint32_t previous = FLAG_CAN_READ; + bool success = m_Flags[ actualReadIndex ].compare_exchange_weak( previous, FLAG_INVALID, std::memory_order_relaxed ); + if( success ) + { + break; + } + else if( m_ReadIndex.load( std::memory_order_acquire ) >= frontReadIndex ) + { + return false; + } + } + + std::atomic_thread_fence( std::memory_order_acquire ); + // now read data, ensuring we do so after above reads & CAS + *pOut = m_Buffer[ actualReadIndex ]; + + m_Flags[ actualReadIndex ].store( FLAG_CAN_WRITE, std::memory_order_relaxed ); + + m_WriteIndex.store(writeIndex-1, std::memory_order_relaxed); + return true; + } + + + template inline + bool LockLessMultiReadPipe::WriterTryWriteFront( const T& in ) + { + // The writer 'owns' the write index, and readers can only reduce + // the amount of data in the pipe. + // We get hold of both values for consistency and to reduce false sharing + // impacting more than one access + uint32_t writeIndex = m_WriteIndex; + + // power of two sizes ensures we can perform AND for a modulus + uint32_t actualWriteIndex = writeIndex & ms_cIndexMask; + + // a reader may still be reading this item, as there are multiple readers + if( m_Flags[ actualWriteIndex ].load(std::memory_order_relaxed) != FLAG_CAN_WRITE ) + { + return false; // still being read, so have caught up with tail. + } + + // as we are the only writer we can update the data without atomics + // whilst the write index has not been updated + m_Buffer[ actualWriteIndex ] = in; + m_Flags[ actualWriteIndex ].store( FLAG_CAN_READ, std::memory_order_relaxed ); + + // We need to ensure the above writes occur prior to updating the write index, + // otherwise another thread might read before it's finished + std::atomic_thread_fence( std::memory_order_release ); + + m_WriteIndex.fetch_add(1, std::memory_order_relaxed); + return true; + } + + + // Lockless multiwriter intrusive list + // Type T must implement T* volatile pNext; + template class LocklessMultiWriteIntrusiveList + { + + std::atomic pHead; + T tail; + public: + LocklessMultiWriteIntrusiveList() : pHead( &tail ) + { + tail.pNext = NULL; + } + + bool IsListEmpty() const + { + return pHead == &tail; + } + + // Add - safe to perform from any thread + void WriterWriteFront( T* pNode_ ) + { + assert( pNode_ ); + pNode_->pNext = NULL; + T* pPrev = pHead.exchange( pNode_ ); + pPrev->pNext = pNode_; + } + + // Remove - only thread safe for owner + T* ReaderReadBack() + { + T* pTailPlus1 = tail.pNext; + if( pTailPlus1 ) + { + T* pTailPlus2 = pTailPlus1->pNext; + if( pTailPlus2 ) + { + //not head + tail.pNext = pTailPlus2; + } + else + { + // pTailPlus1 is the head, attempt swap with tail + tail.pNext = NULL; + if( !pHead.compare_exchange_weak( pTailPlus1, &tail ) ) + { + // pTailPlus1 is no longer the head, so pTailPlus1->pNext should be non NULL + //assert( pTailPlus1->pNext ); + tail.pNext = pTailPlus1->pNext.load(); + } + } + } + return pTailPlus1; + } + }; + +} diff --git a/ext/enkiTS-C-11/src/TaskScheduler.cpp b/ext/enkiTS-C-11/src/TaskScheduler.cpp new file mode 100644 index 00000000..8142b45f --- /dev/null +++ b/ext/enkiTS-C-11/src/TaskScheduler.cpp @@ -0,0 +1,496 @@ +// Copyright (c) 2013 Doug Binks +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgement in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +#include + +#include "TaskScheduler.h" +#include "LockLessMultiReadPipe.h" + + +#if defined __i386__ || defined __x86_64__ +#include "x86intrin.h" +#elif defined _WIN32 +#include +#endif + +using namespace enki; + + +static const uint32_t PIPESIZE_LOG2 = 8; +static const uint32_t SPIN_COUNT = 100; +static const uint32_t SPIN_BACKOFF_MULTIPLIER = 10; +static const uint32_t MAX_NUM_INITIAL_PARTITIONS = 8; + +// thread_local not well supported yet by C++11 compilers. +#ifdef _MSC_VER + #if _MSC_VER <= 1800 + #define thread_local __declspec(thread) + #endif +#elif __APPLE__ + // Apple thread_local currently not implemented despite it being in Clang. + #define thread_local __thread +#endif + + +// each software thread gets it's own copy of gtl_threadNum, so this is safe to use as a static variable +static thread_local uint32_t gtl_threadNum = 0; + +namespace enki +{ + struct SubTaskSet + { + ITaskSet* pTask; + TaskSetPartition partition; + }; + + // we derive class TaskPipe rather than typedef to get forward declaration working easily + class TaskPipe : public LockLessMultiReadPipe {}; + + struct ThreadArgs + { + uint32_t threadNum; + TaskScheduler* pTaskScheduler; + }; + + class PinnedTaskList : public LocklessMultiWriteIntrusiveList {}; +} + +namespace +{ + SubTaskSet SplitTask( SubTaskSet& subTask_, uint32_t rangeToSplit_ ) + { + SubTaskSet splitTask = subTask_; + uint32_t rangeLeft = subTask_.partition.end - subTask_.partition.start; + + if( rangeToSplit_ > rangeLeft ) + { + rangeToSplit_ = rangeLeft; + } + splitTask.partition.end = subTask_.partition.start + rangeToSplit_; + subTask_.partition.start = splitTask.partition.end; + return splitTask; + } + + #if ( defined _WIN32 && ( defined _M_IX86 || defined _M_X64 ) ) || ( defined __i386__ || defined __x86_64__ ) + static void SpinWait( uint32_t spinCount_ ) + { + uint64_t end = __rdtsc() + spinCount_; + while( __rdtsc() < end ) + { + _mm_pause(); + } + } + #else + static void SpinWait( uint32_t spinCount_ ) + { + while( spinCount_ ) + { + // TODO: may have NOP or yield equiv + --spinCount_; + } + } + #endif +} + +static void SafeCallback(ProfilerCallbackFunc func_, uint32_t threadnum_) +{ + if( func_ != nullptr ) + { + func_(threadnum_); + } +} + +ProfilerCallbacks* TaskScheduler::GetProfilerCallbacks() +{ + return &m_ProfilerCallbacks; +} + + +void TaskScheduler::TaskingThreadFunction( const ThreadArgs& args_ ) +{ + uint32_t threadNum = args_.threadNum; + TaskScheduler* pTS = args_.pTaskScheduler; + gtl_threadNum = threadNum; + + SafeCallback( pTS->m_ProfilerCallbacks.threadStart, threadNum ); + + uint32_t spinCount = 0; + uint32_t hintPipeToCheck_io = threadNum + 1; // does not need to be clamped. + while( pTS->m_bRunning.load( std::memory_order_relaxed ) ) + { + if(!pTS->TryRunTask( threadNum, hintPipeToCheck_io ) ) + { + // no tasks, will spin then wait + ++spinCount; + if( spinCount > SPIN_COUNT ) + { + pTS->WaitForTasks( threadNum ); + spinCount = 0; + } + else + { + // Note: see https://software.intel.com/en-us/articles/a-common-construct-to-avoid-the-contention-of-threads-architecture-agnostic-spin-wait-loops + uint32_t spinBackoffCount = spinCount * SPIN_BACKOFF_MULTIPLIER; + SpinWait( spinBackoffCount ); + } + } + } + + pTS->m_NumThreadsRunning.fetch_sub( 1, std::memory_order_release ); + SafeCallback( pTS->m_ProfilerCallbacks.threadStop, threadNum ); + return; + +} + + +void TaskScheduler::StartThreads() +{ + if( m_bHaveThreads ) + { + return; + } + m_bRunning = 1; + + // we create one less thread than m_NumThreads as the main thread counts as one + m_pThreadArgStore = new ThreadArgs[m_NumThreads]; + m_pThreads = new std::thread*[m_NumThreads]; + m_pThreadArgStore[0].threadNum = 0; + m_pThreadArgStore[0].pTaskScheduler = this; + m_NumThreadsRunning = 1; // account for main thread + for( uint32_t thread = 1; thread < m_NumThreads; ++thread ) + { + m_pThreadArgStore[thread].threadNum = thread; + m_pThreadArgStore[thread].pTaskScheduler = this; + m_pThreads[thread] = new std::thread( TaskingThreadFunction, m_pThreadArgStore[thread] ); + ++m_NumThreadsRunning; + } + + // ensure we have sufficient tasks to equally fill either all threads including main + // or just the threads we've launched, this is outside the firstinit as we want to be able + // to runtime change it + if( 1 == m_NumThreads ) + { + m_NumPartitions = 1; + m_NumInitialPartitions = 1; + } + else + { + m_NumPartitions = m_NumThreads * (m_NumThreads - 1); + m_NumInitialPartitions = m_NumThreads - 1; + if( m_NumInitialPartitions > MAX_NUM_INITIAL_PARTITIONS ) + { + m_NumInitialPartitions = MAX_NUM_INITIAL_PARTITIONS; + } + } + + m_bHaveThreads = true; +} + +void TaskScheduler::StopThreads( bool bWait_ ) +{ + if( m_bHaveThreads ) + { + // wait for them threads quit before deleting data + m_bRunning = 0; + while( bWait_ && m_NumThreadsRunning > 1) + { + // keep firing event to ensure all threads pick up state of m_bRunning + m_NewTaskEvent.notify_all(); + } + + for( uint32_t thread = 1; thread < m_NumThreads; ++thread ) + { + m_pThreads[thread]->detach(); + delete m_pThreads[thread]; + } + + m_NumThreads = 0; + delete[] m_pThreadArgStore; + delete[] m_pThreads; + m_pThreadArgStore = 0; + m_pThreads = 0; + + m_bHaveThreads = false; + m_NumThreadsWaiting = 0; + m_NumThreadsRunning = 0; + } +} + +bool TaskScheduler::TryRunTask( uint32_t threadNum, uint32_t& hintPipeToCheck_io_ ) +{ + // Run any tasks for this thread + RunPinnedTasks( threadNum ); + + // check for tasks + SubTaskSet subTask; + bool bHaveTask = m_pPipesPerThread[ threadNum ].WriterTryReadFront( &subTask ); + + uint32_t threadToCheck = hintPipeToCheck_io_; + uint32_t checkCount = 0; + while( !bHaveTask && checkCount < m_NumThreads ) + { + threadToCheck = ( hintPipeToCheck_io_ + checkCount ) % m_NumThreads; + if( threadToCheck != threadNum ) + { + bHaveTask = m_pPipesPerThread[ threadToCheck ].ReaderTryReadBack( &subTask ); + } + ++checkCount; + } + + if( bHaveTask ) + { + // update hint, will preserve value unless actually got task from another thread. + hintPipeToCheck_io_ = threadToCheck; + + uint32_t partitionSize = subTask.partition.end - subTask.partition.start; + if( subTask.pTask->m_RangeToRun < partitionSize ) + { + SubTaskSet taskToRun = SplitTask( subTask, subTask.pTask->m_RangeToRun ); + SplitAndAddTask( threadNum, subTask, subTask.pTask->m_RangeToRun ); + taskToRun.pTask->ExecuteRange( taskToRun.partition, threadNum ); + taskToRun.pTask->m_RunningCount.fetch_sub(1,std::memory_order_release ); + + } + else + { + // the task has already been divided up by AddTaskSetToPipe, so just run it + subTask.pTask->ExecuteRange( subTask.partition, threadNum ); + subTask.pTask->m_RunningCount.fetch_sub(1,std::memory_order_release ); + } + } + + return bHaveTask; + +} + +void TaskScheduler::WaitForTasks( uint32_t threadNum ) +{ + // We incrememt the number of threads waiting here in order + // to ensure that the check for tasks occurs after the increment + // to prevent a task being added after a check, then the thread waiting. + // This will occasionally result in threads being mistakenly awoken, + // but they will then go back to sleep. + m_NumThreadsWaiting.fetch_add( 1, std::memory_order_acquire ); + + bool bHaveTasks = false; + for( uint32_t thread = 0; thread < m_NumThreads; ++thread ) + { + if( !m_pPipesPerThread[ thread ].IsPipeEmpty() ) + { + bHaveTasks = true; + break; + } + } + if( !bHaveTasks && !m_pPinnedTaskListPerThread[ threadNum ].IsListEmpty() ) + { + bHaveTasks = true; + } + if( !bHaveTasks ) + { + SafeCallback( m_ProfilerCallbacks.waitStart, threadNum ); + std::unique_lock lk( m_NewTaskEventMutex ); + m_NewTaskEvent.wait( lk ); + SafeCallback( m_ProfilerCallbacks.waitStop, threadNum ); + } + + m_NumThreadsWaiting.fetch_sub( 1, std::memory_order_release ); +} + +void TaskScheduler::WakeThreads() +{ + if( m_NumThreadsWaiting.load( std::memory_order_relaxed ) ) + { + m_NewTaskEvent.notify_all(); + } +} + +void TaskScheduler::SplitAndAddTask( uint32_t threadNum_, SubTaskSet subTask_, uint32_t rangeToSplit_ ) +{ + int32_t numAdded = 0; + while( subTask_.partition.start != subTask_.partition.end ) + { + SubTaskSet taskToAdd = SplitTask( subTask_, rangeToSplit_ ); + + // add the partition to the pipe + ++numAdded; + subTask_.pTask->m_RunningCount.fetch_add( 1, std::memory_order_acquire ); + if( !m_pPipesPerThread[ threadNum_ ].WriterTryWriteFront( taskToAdd ) ) + { + if( numAdded > 1 ) + { + WakeThreads(); + } + numAdded = 0; + // alter range to run the appropriate fraction + if( taskToAdd.pTask->m_RangeToRun < rangeToSplit_ ) + { + taskToAdd.partition.end = taskToAdd.partition.start + taskToAdd.pTask->m_RangeToRun; + subTask_.partition.start = taskToAdd.partition.end; + } + taskToAdd.pTask->ExecuteRange( taskToAdd.partition, threadNum_ ); + subTask_.pTask->m_RunningCount.fetch_sub( 1, std::memory_order_release ); + } + } + + WakeThreads(); +} + +void TaskScheduler::AddTaskSetToPipe( ITaskSet* pTaskSet ) +{ + pTaskSet->m_RunningCount.store( 0, std::memory_order_relaxed ); + + // divide task up and add to pipe + pTaskSet->m_RangeToRun = pTaskSet->m_SetSize / m_NumPartitions; + if( pTaskSet->m_RangeToRun < pTaskSet->m_MinRange ) { pTaskSet->m_RangeToRun = pTaskSet->m_MinRange; } + + uint32_t rangeToSplit = pTaskSet->m_SetSize / m_NumInitialPartitions; + if( rangeToSplit < pTaskSet->m_MinRange ) { rangeToSplit = pTaskSet->m_MinRange; } + + SubTaskSet subTask; + subTask.pTask = pTaskSet; + subTask.partition.start = 0; + subTask.partition.end = pTaskSet->m_SetSize; + SplitAndAddTask( gtl_threadNum, subTask, rangeToSplit ); +} + +void TaskScheduler::AddPinnedTask( IPinnedTask* pTask_ ) +{ + pTask_->m_RunningCount = 1; + m_pPinnedTaskListPerThread[ pTask_->threadNum ].WriterWriteFront( pTask_ ); + WakeThreads(); +} + +void TaskScheduler::RunPinnedTasks() +{ + uint32_t threadNum = gtl_threadNum; + RunPinnedTasks( threadNum ); +} + +void TaskScheduler::RunPinnedTasks( uint32_t threadNum ) +{ + IPinnedTask* pPinnedTaskSet = NULL; + do + { + pPinnedTaskSet = m_pPinnedTaskListPerThread[ threadNum ].ReaderReadBack(); + if( pPinnedTaskSet ) + { + pPinnedTaskSet->Execute(); + pPinnedTaskSet->m_RunningCount = 0; + } + } while( pPinnedTaskSet ); +} + +void TaskScheduler::WaitforTask( const ICompletable* pCompletable_ ) +{ + uint32_t hintPipeToCheck_io = gtl_threadNum + 1; // does not need to be clamped. + if( pCompletable_ ) + { + while( !pCompletable_->GetIsComplete() ) + { + TryRunTask( gtl_threadNum, hintPipeToCheck_io ); + // should add a spin then wait for task completion event. + } + } + else + { + TryRunTask( gtl_threadNum, hintPipeToCheck_io ); + } +} + +void TaskScheduler::WaitforAll() +{ + bool bHaveTasks = true; + uint32_t hintPipeToCheck_io = gtl_threadNum + 1; // does not need to be clamped. + int32_t numThreadsRunning = m_NumThreadsRunning.load( std::memory_order_relaxed ) - 1; // account for this thread + while( bHaveTasks || m_NumThreadsWaiting.load( std::memory_order_relaxed ) < numThreadsRunning ) + { + bHaveTasks = TryRunTask( gtl_threadNum, hintPipeToCheck_io ); + if( !bHaveTasks ) + { + for( uint32_t thread = 0; thread < m_NumThreads; ++thread ) + { + if( !m_pPipesPerThread[ thread ].IsPipeEmpty() ) + { + bHaveTasks = true; + break; + } + } + } + } +} + +void TaskScheduler::WaitforAllAndShutdown() +{ + WaitforAll(); + StopThreads(true); + delete[] m_pPipesPerThread; + m_pPipesPerThread = 0; + + delete[] m_pPinnedTaskListPerThread; + m_pPinnedTaskListPerThread = 0; +} + +uint32_t TaskScheduler::GetNumTaskThreads() const +{ + return m_NumThreads; +} + +TaskScheduler::TaskScheduler() + : m_pPipesPerThread(NULL) + , m_NumThreads(0) + , m_pThreadArgStore(NULL) + , m_pThreads(NULL) + , m_bRunning(0) + , m_NumThreadsRunning(0) + , m_NumThreadsWaiting(0) + , m_NumPartitions(0) + , m_bHaveThreads(false) +{ + memset(&m_ProfilerCallbacks, 0, sizeof(m_ProfilerCallbacks)); +} + +TaskScheduler::~TaskScheduler() +{ + StopThreads( true ); // Stops threads, waiting for them. + + delete[] m_pPipesPerThread; + m_pPipesPerThread = NULL; + + delete[] m_pPinnedTaskListPerThread; + m_pPinnedTaskListPerThread = NULL; +} + +void TaskScheduler::Initialize( uint32_t numThreads_ ) +{ + assert( numThreads_ ); + StopThreads( true ); // Stops threads, waiting for them. + delete[] m_pPipesPerThread; + delete[] m_pPinnedTaskListPerThread; + + m_NumThreads = numThreads_; + + m_pPipesPerThread = new TaskPipe[ m_NumThreads ]; + m_pPinnedTaskListPerThread = new PinnedTaskList[ m_NumThreads ]; + + StartThreads(); +} + +void TaskScheduler::Initialize() +{ + Initialize( std::thread::hardware_concurrency() ); +} \ No newline at end of file diff --git a/ext/enkiTS-C-11/src/TaskScheduler.h b/ext/enkiTS-C-11/src/TaskScheduler.h new file mode 100644 index 00000000..1b54c3fd --- /dev/null +++ b/ext/enkiTS-C-11/src/TaskScheduler.h @@ -0,0 +1,239 @@ +// Copyright (c) 2013 Doug Binks +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgement in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace enki +{ + + struct TaskSetPartition + { + uint32_t start; + uint32_t end; + }; + + class TaskScheduler; + class TaskPipe; + class PinnedTaskList; + struct ThreadArgs; + struct SubTaskSet; + + // ICompletable is a base class used to check for completion. + // Do not use this class directly, instead derive from ITaskSet or IPinnedTask. + class ICompletable + { + public: + ICompletable() : m_RunningCount(0) {} + bool GetIsComplete() const { + return 0 == m_RunningCount.load( std::memory_order_acquire ); + } + private: + friend class TaskScheduler; + std::atomic m_RunningCount; + }; + + // Subclass ITaskSet to create tasks. + // TaskSets can be re-used, but check completion first. + class ITaskSet : public ICompletable + { + public: + ITaskSet() + : m_SetSize(1) + , m_MinRange(1) + , m_RangeToRun(1) + {} + + ITaskSet( uint32_t setSize_ ) + : m_SetSize( setSize_ ) + , m_MinRange(1) + , m_RangeToRun(1) + {} + + ITaskSet( uint32_t setSize_, uint32_t minRange_ ) + : m_SetSize( setSize_ ) + , m_MinRange( minRange_ ) + , m_RangeToRun(minRange_) + {} + + // Execute range should be overloaded to process tasks. It will be called with a + // range_ where range.start >= 0; range.start < range.end; and range.end < m_SetSize; + // The range values should be mapped so that linearly processing them in order is cache friendly + // i.e. neighbouring values should be close together. + // threadnum should not be used for changing processing of data, it's intended purpose + // is to allow per-thread data buckets for output. + virtual void ExecuteRange( TaskSetPartition range, uint32_t threadnum ) = 0; + + // Size of set - usually the number of data items to be processed, see ExecuteRange. Defaults to 1 + uint32_t m_SetSize; + + // Minimum size of of TaskSetPartition range when splitting a task set into partitions. + // This should be set to a value which results in computation effort of at least 10k + // clock cycles to minimize tast scheduler overhead. + // NOTE: The last partition will be smaller than m_MinRange if m_SetSize is not a multiple + // of m_MinRange. + // Also known as grain size in literature. + uint32_t m_MinRange; + + private: + friend class TaskScheduler; + uint32_t m_RangeToRun; + }; + + // Subclass IPinnedTask to create tasks which cab be run on a given thread only. + class IPinnedTask : public ICompletable + { + public: + IPinnedTask() : threadNum(0), pNext(NULL) {} // default is to run a task on main thread + IPinnedTask( uint32_t threadNum_ ) : threadNum(threadNum_), pNext(NULL) {} // default is to run a task on main thread + + + // IPinnedTask needs to be non abstract for intrusive list functionality. + // Should never be called as should be overridden. + virtual void Execute() { assert(false); } + + + uint32_t threadNum; // thread to run this pinned task on + std::atomic pNext; // Do not use. For intrusive list only. + }; + + // A utility task set for creating tasks based on std::func. + typedef std::function TaskSetFunction; + class TaskSet : public ITaskSet + { + public: + TaskSet() = default; + TaskSet( TaskSetFunction func_ ) : m_Function( func_ ) {} + TaskSet( uint32_t setSize_, TaskSetFunction func_ ) : ITaskSet( setSize_ ), m_Function( func_ ) {} + + + virtual void ExecuteRange( TaskSetPartition range, uint32_t threadnum ) + { + m_Function( range, threadnum ); + } + + TaskSetFunction m_Function; + }; + + // TaskScheduler implements several callbacks intended for profilers + typedef std::function ProfilerCallbackFunc; + struct ProfilerCallbacks + { + ProfilerCallbackFunc threadStart; + ProfilerCallbackFunc threadStop; + ProfilerCallbackFunc waitStart; + ProfilerCallbackFunc waitStop; + }; + + class TaskScheduler + { + public: + TaskScheduler(); + ~TaskScheduler(); + + // Call either Initialize() or Initialize( numThreads_ ) before adding tasks. + + // Initialize() will create GetNumHardwareThreads()-1 threads, which is + // sufficient to fill the system when including the main thread. + // Initialize can be called multiple times - it will wait for completion + // before re-initializing. + void Initialize(); + + // Initialize( numThreads_ ) - numThreads_ (must be > 0) + // will create numThreads_-1 threads, as thread 0 is + // the thread on which the initialize was called. + void Initialize( uint32_t numThreads_ ); + + + // Adds the TaskSet to pipe and returns if the pipe is not full. + // If the pipe is full, pTaskSet is run. + // should only be called from main thread, or within a task + void AddTaskSetToPipe( ITaskSet* pTaskSet ); + + // Thread 0 is main thread, otherwise use threadNum + void AddPinnedTask( IPinnedTask* pTask_ ); + + // This function will run any IPinnedTask* for current thread, but not run other + // Main thread should call this or use a wait to ensure it's tasks are run. + void RunPinnedTasks(); + + // Runs the TaskSets in pipe until true == pTaskSet->GetIsComplete(); + // should only be called from thread which created the taskscheduler , or within a task + // if called with 0 it will try to run tasks, and return if none available. + void WaitforTask( const ICompletable* pCompletable_ ); + + // WaitforTaskSet, deprecated interface use WaitforTask + inline void WaitforTaskSet( const ICompletable* pCompletable_ ) { WaitforTask( pCompletable_ ); } + + // Waits for all task sets to complete - not guaranteed to work unless we know we + // are in a situation where tasks aren't being continuosly added. + void WaitforAll(); + + // Waits for all task sets to complete and shutdown threads - not guaranteed to work unless we know we + // are in a situation where tasks aren't being continuosly added. + void WaitforAllAndShutdown(); + + // Returns the number of threads created for running tasks + 1 + // to account for the main thread. + uint32_t GetNumTaskThreads() const; + + // Returns the ProfilerCallbacks structure so that it can be modified to + // set the callbacks. + ProfilerCallbacks* GetProfilerCallbacks(); + + private: + static void TaskingThreadFunction( const ThreadArgs& args_ ); + void WaitForTasks( uint32_t threadNum ); + void RunPinnedTasks( uint32_t threadNum ); + bool TryRunTask( uint32_t threadNum, uint32_t& hintPipeToCheck_io_ ); + void StartThreads(); + void StopThreads( bool bWait_ ); + void SplitAndAddTask( uint32_t threadNum_, SubTaskSet subTask_, uint32_t rangeToSplit_ ); + void WakeThreads(); + + TaskPipe* m_pPipesPerThread; + PinnedTaskList* m_pPinnedTaskListPerThread; + + uint32_t m_NumThreads; + ThreadArgs* m_pThreadArgStore; + std::thread** m_pThreads; + std::atomic m_bRunning; + std::atomic m_NumThreadsRunning; + std::atomic m_NumThreadsWaiting; + uint32_t m_NumPartitions; + std::condition_variable m_NewTaskEvent; + std::mutex m_NewTaskEventMutex; + uint32_t m_NumInitialPartitions; + bool m_bHaveThreads; + ProfilerCallbacks m_ProfilerCallbacks; + + TaskScheduler( const TaskScheduler& nocopy ); + TaskScheduler& operator=( const TaskScheduler& nocopy ); + }; + + inline uint32_t GetNumHardwareThreads() + { + return std::thread::hardware_concurrency(); + } +} \ No newline at end of file diff --git a/ext/enkiTS-C-11/src/TaskScheduler_c.cpp b/ext/enkiTS-C-11/src/TaskScheduler_c.cpp new file mode 100644 index 00000000..f40480c0 --- /dev/null +++ b/ext/enkiTS-C-11/src/TaskScheduler_c.cpp @@ -0,0 +1,168 @@ +// Copyright (c) 2013 Doug Binks +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgement in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +#include "TaskScheduler_c.h" +#include "TaskScheduler.h" + +#include + +using namespace enki; + +struct enkiTaskScheduler : TaskScheduler +{ +}; + +struct enkiTaskSet : ITaskSet +{ + enkiTaskSet( enkiTaskExecuteRange taskFun_ ) : taskFun(taskFun_), pArgs(NULL) {} + + virtual void ExecuteRange( TaskSetPartition range, uint32_t threadnum ) + { + taskFun( range.start, range.end, threadnum, pArgs ); + } + + enkiTaskExecuteRange taskFun; + void* pArgs; +}; + +struct enkiPinnedTask : IPinnedTask +{ + enkiPinnedTask( enkiPinnedTaskExecute taskFun_, uint32_t threadNum_ ) + : IPinnedTask( threadNum_ ), taskFun(taskFun_), pArgs(NULL) {} + + virtual void Execute() + { + taskFun( pArgs ); + } + + enkiPinnedTaskExecute taskFun; + void* pArgs; +}; + +enkiTaskScheduler* enkiNewTaskScheduler() +{ + enkiTaskScheduler* pETS = new enkiTaskScheduler(); + return pETS; +} + +void enkiInitTaskScheduler( enkiTaskScheduler* pETS_ ) +{ + pETS_->Initialize(); +} + +void enkiInitTaskSchedulerNumThreads( enkiTaskScheduler* pETS_, uint32_t numThreads_ ) +{ + pETS_->Initialize( numThreads_ ); +} + +void enkiDeleteTaskScheduler( enkiTaskScheduler* pETS_ ) +{ + delete pETS_; +} + +enkiTaskSet* enkiCreateTaskSet( enkiTaskScheduler* pETS_, enkiTaskExecuteRange taskFunc_ ) +{ + return new enkiTaskSet( taskFunc_ ); +} + +void enkiDeleteTaskSet( enkiTaskSet* pTaskSet_ ) +{ + delete pTaskSet_; +} + +void enkiAddTaskSetToPipe( enkiTaskScheduler* pETS_, enkiTaskSet* pTaskSet_, void* pArgs_, uint32_t setSize_ ) +{ + assert( pTaskSet_ ); + assert( pTaskSet_->taskFun ); + + pTaskSet_->m_SetSize = setSize_; + pTaskSet_->pArgs = pArgs_; + pETS_->AddTaskSetToPipe( pTaskSet_ ); +} + +void enkiAddTaskSetToPipeMinRange(enkiTaskScheduler * pETS_, enkiTaskSet * pTaskSet_, void * pArgs_, uint32_t setSize_, uint32_t minRange_) +{ + assert( pTaskSet_ ); + assert( pTaskSet_->taskFun ); + + pTaskSet_->m_SetSize = setSize_; + pTaskSet_->m_MinRange = minRange_; + pTaskSet_->pArgs = pArgs_; + pETS_->AddTaskSetToPipe( pTaskSet_ ); +} + +int enkiIsTaskSetComplete( enkiTaskScheduler* pETS_, enkiTaskSet* pTaskSet_ ) +{ + assert( pTaskSet_ ); + return ( pTaskSet_->GetIsComplete() ) ? 1 : 0; +} + +enkiPinnedTask* enkiCreatePinnedTask(enkiTaskScheduler * pETS_, enkiPinnedTaskExecute taskFunc_, uint32_t threadNum_) +{ + return new enkiPinnedTask( taskFunc_, threadNum_ ); +} + +void enkiDeletePinnedTask(enkiPinnedTask * pTaskSet_) +{ + delete pTaskSet_; +} + +void enkiAddPinnedTask(enkiTaskScheduler * pETS_, enkiPinnedTask * pTask_, void * pArgs_) +{ + assert( pTask_ ); + pTask_->pArgs = pArgs_; + pETS_->AddPinnedTask( pTask_ ); +} + +void enkiRunPinnedTasks(enkiTaskScheduler * pETS_) +{ + pETS_->RunPinnedTasks(); +} + +int enkiIsPinnedTaskComplete(enkiTaskScheduler * pETS_, enkiPinnedTask * pTask_) +{ + assert( pTask_ ); + return ( pTask_->GetIsComplete() ) ? 1 : 0; +} + +void enkiWaitForTaskSet( enkiTaskScheduler* pETS_, enkiTaskSet* pTaskSet_ ) +{ + pETS_->WaitforTask( pTaskSet_ ); +} + +void enkiWaitForPinnedTask( enkiTaskScheduler* pETS_, enkiPinnedTask* pTask_ ) +{ + pETS_->WaitforTask( pTask_ ); +} + +void enkiWaitForAll( enkiTaskScheduler* pETS_ ) +{ + pETS_->WaitforAll(); +} + +uint32_t enkiGetNumTaskThreads( enkiTaskScheduler* pETS_ ) +{ + return pETS_->GetNumTaskThreads(); +} + +enkiProfilerCallbacks* enkiGetProfilerCallbacks( enkiTaskScheduler* pETS_ ) +{ + assert( sizeof(enkiProfilerCallbacks) == sizeof(enki::ProfilerCallbacks) ); + return (enkiProfilerCallbacks*)pETS_->GetProfilerCallbacks(); +} + diff --git a/ext/enkiTS-C-11/src/TaskScheduler_c.h b/ext/enkiTS-C-11/src/TaskScheduler_c.h new file mode 100644 index 00000000..66617580 --- /dev/null +++ b/ext/enkiTS-C-11/src/TaskScheduler_c.h @@ -0,0 +1,126 @@ +// Copyright (c) 2013 Doug Binks +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgement in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct enkiTaskScheduler enkiTaskScheduler; +typedef struct enkiTaskSet enkiTaskSet; +typedef struct enkiPinnedTask enkiPinnedTask; + +typedef void (* enkiTaskExecuteRange)( uint32_t start_, uint32_t end, uint32_t threadnum_, void* pArgs_ ); +typedef void (* enkiPinnedTaskExecute)( void* pArgs_ ); + + +// Create a new task scheduler +enkiTaskScheduler* enkiNewTaskScheduler(); + +// Initialize task scheduler - will create GetNumHardwareThreads()-1 threads, which is +// sufficient to fill the system when including the main thread. +// Initialize can be called multiple times - it will wait for completion +// before re-initializing. +void enkiInitTaskScheduler( enkiTaskScheduler* pETS_ ); + +// Initialize a task scheduler with numThreads_ (must be > 0) +// will create numThreads_-1 threads, as thread 0 is +// the thread on which the initialize was called. +void enkiInitTaskSchedulerNumThreads( enkiTaskScheduler* pETS_, uint32_t numThreads_ ); + + +// Delete a task scheduler +void enkiDeleteTaskScheduler( enkiTaskScheduler* pETS_ ); + +// Create a task set. +enkiTaskSet* enkiCreateTaskSet( enkiTaskScheduler* pETS_, enkiTaskExecuteRange taskFunc_ ); + +// Delete a task set. +void enkiDeleteTaskSet( enkiTaskSet* pTaskSet_ ); + +// Schedule the task +void enkiAddTaskSetToPipe( enkiTaskScheduler* pETS_, enkiTaskSet* pTaskSet_, + void* pArgs_, uint32_t setSize_ ); + +// Schedule the task with a minimum range. +// This should be set to a value which results in computation effort of at least 10k +// clock cycles to minimize tast scheduler overhead. +// NOTE: The last partition will be smaller than m_MinRange if m_SetSize is not a multiple +// of m_MinRange. +// Also known as grain size in literature. +void enkiAddTaskSetToPipeMinRange( enkiTaskScheduler* pETS_, enkiTaskSet* pTaskSet_, + void* pArgs_, uint32_t setSize_, uint32_t minRange_ ); + + +// Check if TaskSet is complete. Doesn't wait. Returns 1 if complete, 0 if not. +int enkiIsTaskSetComplete( enkiTaskScheduler* pETS_, enkiTaskSet* pTaskSet_ ); + +// Create a pinned task. +enkiPinnedTask* enkiCreatePinnedTask( enkiTaskScheduler* pETS_, enkiPinnedTaskExecute taskFunc_, uint32_t threadNum_ ); + +// Delete a pinned task. +void enkiDeletePinnedTask( enkiPinnedTask* pTaskSet_ ); + +// Schedule a pinned task +void enkiAddPinnedTask( enkiTaskScheduler* pETS_, enkiPinnedTask* pTask_, + void* pArgs_ ); + +// This function will run any enkiPinnedTask* for current thread, but not run other +// Main thread should call this or use a wait to ensure it's tasks are run. +void enkiRunPinnedTasks( enkiTaskScheduler * pETS_ ); + +// Check if enkiPinnedTask is complete. Doesn't wait. Returns 1 if complete, 0 if not. +int enkiIsPinnedTaskComplete( enkiTaskScheduler* pETS_, enkiPinnedTask* pTask_ ); + +// Wait for a given task. +// should only be called from thread which created the taskscheduler , or within a task +// if called with 0 it will try to run tasks, and return if none available. +void enkiWaitForTaskSet( enkiTaskScheduler* pETS_, enkiTaskSet* pTaskSet_ ); + +// Wait for a given pinned task. +// should only be called from thread which created the taskscheduler , or within a task +// if called with 0 it will try to run tasks, and return if none available. +void enkiWaitForPinnedTask( enkiTaskScheduler* pETS_, enkiPinnedTask* pTask_ ); + +// Waits for all task sets to complete - not guaranteed to work unless we know we +// are in a situation where tasks aren't being continuosly added. +void enkiWaitForAll( enkiTaskScheduler* pETS_ ); + + +// get number of threads +uint32_t enkiGetNumTaskThreads( enkiTaskScheduler* pETS_ ); + +// TaskScheduler implements several callbacks intended for profilers +typedef void (*enkiProfilerCallbackFunc)( uint32_t threadnum_ ); +struct enkiProfilerCallbacks +{ + enkiProfilerCallbackFunc threadStart; + enkiProfilerCallbackFunc threadStop; + enkiProfilerCallbackFunc waitStart; + enkiProfilerCallbackFunc waitStop; +}; + +// Get the callback structure so it can be set +struct enkiProfilerCallbacks* enkiGetProfilerCallbacks( enkiTaskScheduler* pETS_ ); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/ext/hdrloader.cpp b/ext/hdrloader.cpp deleted file mode 100644 index 4fff0a3e..00000000 --- a/ext/hdrloader.cpp +++ /dev/null @@ -1,191 +0,0 @@ - -/*********************************************************************************** - Created: 17:9:2002 - FileName: hdrloader.cpp - Author: Igor Kravtchenko - - Info: Load HDR image and convert to a set of float32 RGB triplet. -************************************************************************************/ - -#include "hdrloader.h" - -#include -#include -#include - -typedef unsigned char RGBE[4]; -#define R 0 -#define G 1 -#define B 2 -#define E 3 - -#define MINELEN 8 // minimum scanline length for encoding -#define MAXELEN 0x7fff // maximum scanline length for encoding - -static void workOnRGBE(RGBE *scan, int len, float *cols); -static bool decrunch(RGBE *scanline, int len, FILE *file); -static bool oldDecrunch(RGBE *scanline, int len, FILE *file); - -bool HDRLoader::load(const char *fileName, HDRLoaderResult &res) -{ - int i; - char str[200]; - FILE *file; - - file = fopen(fileName, "rb"); - if (!file) - return false; - - fread(str, 10, 1, file); - if (memcmp(str, "#?RADIANCE", 10)) { - fclose(file); - return false; - } - - fseek(file, 1, SEEK_CUR); - - char cmd[200]; - i = 0; - char c = 0, oldc; - while(true) { - oldc = c; - c = fgetc(file); - if (c == 0xa && oldc == 0xa) - break; - cmd[i++] = c; - } - - char reso[200]; - i = 0; - while(true) { - c = fgetc(file); - reso[i++] = c; - if (c == 0xa) - break; - } - - int w, h; - if (!sscanf(reso, "-Y %ld +X %ld", &h, &w)) { - fclose(file); - return false; - } - - res.width = w; - res.height = h; - - float *cols = new float[w * h * 3]; - res.cols = cols; - - RGBE *scanline = new RGBE[w]; - if (!scanline) { - fclose(file); - return false; - } - - // convert image - for (int y = h - 1; y >= 0; y--) { - if (decrunch(scanline, w, file) == false) - break; - workOnRGBE(scanline, w, cols); - cols += w * 3; - } - - delete [] scanline; - fclose(file); - - return true; -} - -float convertComponent(int expo, int val) -{ - float v = val / 256.0f; - float d = (float) powf(2.f, float(expo)); - return v * d; -} - -void workOnRGBE(RGBE *scan, int len, float *cols) -{ - while (len-- > 0) { - int expo = scan[0][E] - 128; - cols[0] = convertComponent(expo, scan[0][R]); - cols[1] = convertComponent(expo, scan[0][G]); - cols[2] = convertComponent(expo, scan[0][B]); - cols += 3; - scan++; - } -} - -bool decrunch(RGBE *scanline, int len, FILE *file) -{ - int i, j; - - if (len < MINELEN || len > MAXELEN) - return oldDecrunch(scanline, len, file); - - i = fgetc(file); - if (i != 2) { - fseek(file, -1, SEEK_CUR); - return oldDecrunch(scanline, len, file); - } - - scanline[0][G] = fgetc(file); - scanline[0][B] = fgetc(file); - i = fgetc(file); - - if (scanline[0][G] != 2 || scanline[0][B] & 128) { - scanline[0][R] = 2; - scanline[0][E] = i; - return oldDecrunch(scanline + 1, len - 1, file); - } - - // read each component - for (i = 0; i < 4; i++) { - for (j = 0; j < len; ) { - unsigned char code = fgetc(file); - if (code > 128) { // run - code &= 127; - unsigned char val = fgetc(file); - while (code--) - scanline[j++][i] = val; - } - else { // non-run - while(code--) - scanline[j++][i] = fgetc(file); - } - } - } - - return feof(file) ? false : true; -} - -bool oldDecrunch(RGBE *scanline, int len, FILE *file) -{ - int i; - int rshift = 0; - - while (len > 0) { - scanline[0][R] = fgetc(file); - scanline[0][G] = fgetc(file); - scanline[0][B] = fgetc(file); - scanline[0][E] = fgetc(file); - if (feof(file)) - return false; - - if (scanline[0][R] == 1 && - scanline[0][G] == 1 && - scanline[0][B] == 1) { - for (i = scanline[0][E] << rshift; i > 0; i--) { - memcpy(&scanline[0][0], &scanline[-1][0], 4); - scanline++; - len--; - } - rshift += 8; - } - else { - scanline++; - len--; - rshift = 0; - } - } - return true; -} diff --git a/ext/hdrloader.h b/ext/hdrloader.h deleted file mode 100644 index b6c43bca..00000000 --- a/ext/hdrloader.h +++ /dev/null @@ -1,21 +0,0 @@ - -/*********************************************************************************** - Created: 17:9:2002 - FileName: hdrloader.h - Author: Igor Kravtchenko - - Info: Load HDR image and convert to a set of float32 RGB triplet. -************************************************************************************/ - -class HDRLoaderResult { -public: - int width, height; - // each pixel takes 3 float32, each component can be of any value... - float *cols; -}; - -class HDRLoader { -public: - static bool load(const char *fileName, HDRLoaderResult &res); -}; - diff --git a/ext/imgui_dock.cpp b/ext/imgui_dock.cpp index dc8c2351..e595e324 100644 --- a/ext/imgui_dock.cpp +++ b/ext/imgui_dock.cpp @@ -1347,7 +1347,7 @@ bool ImGui::BeginDock(const char* label, bool* opened, ImGuiWindowFlags extra_fl DockContext& context = g_docklist[cur_dock_panel]; char new_label[128]; - sprintf_s( new_label , "%s##%s" , label , cur_dock_panel ); + sprintf( new_label , "%s##%s" , label , cur_dock_panel ); return context.begin( new_label , opened , extra_flags ); } @@ -1388,4 +1388,4 @@ void ImGui::InitDock() ini_handler.ReadLineFn = readLine; ini_handler.WriteAllFn = writeAll; g.SettingsHandlers.push_front(ini_handler); -} \ No newline at end of file +} diff --git a/ext/imgui_impl_sdl.cpp b/ext/imgui_impl_sdl.cpp index 0189468c..3b7b299a 100644 --- a/ext/imgui_impl_sdl.cpp +++ b/ext/imgui_impl_sdl.cpp @@ -38,8 +38,9 @@ // SDL // (the multi-viewports feature requires SDL features supported from SDL 2.0.5+) #include +#ifdef WIN32 #include - +#endif #define SDL_HAS_CAPTURE_MOUSE SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) #define SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH SDL_VERSION_ATLEAST(2,0,5) diff --git a/ext/tcc-0.9.27/lib/gdi32.def b/ext/tcc-0.9.27/lib/gdi32.def new file mode 100644 index 00000000..02766da4 --- /dev/null +++ b/ext/tcc-0.9.27/lib/gdi32.def @@ -0,0 +1,337 @@ +LIBRARY gdi32.dll + +EXPORTS +AbortDoc +AbortPath +AddFontResourceA +AddFontResourceW +AngleArc +AnimatePalette +Arc +ArcTo +BeginPath +BitBlt +ByeByeGDI +CancelDC +CheckColorsInGamut +ChoosePixelFormat +Chord +CloseEnhMetaFile +CloseFigure +CloseMetaFile +ColorCorrectPalette +ColorMatchToTarget +CombineRgn +CombineTransform +CopyEnhMetaFileA +CopyEnhMetaFileW +CopyMetaFileA +CopyMetaFileW +CreateBitmap +CreateBitmapIndirect +CreateBrushIndirect +CreateColorSpaceA +CreateColorSpaceW +CreateCompatibleBitmap +CreateCompatibleDC +CreateDCA +CreateDCW +CreateDIBPatternBrush +CreateDIBPatternBrushPt +CreateDIBSection +CreateDIBitmap +CreateDiscardableBitmap +CreateEllipticRgn +CreateEllipticRgnIndirect +CreateEnhMetaFileA +CreateEnhMetaFileW +CreateFontA +CreateFontIndirectA +CreateFontIndirectW +CreateFontW +CreateHalftonePalette +CreateHatchBrush +CreateICA +CreateICW +CreateMetaFileA +CreateMetaFileW +CreatePalette +CreatePatternBrush +CreatePen +CreatePenIndirect +CreatePolyPolygonRgn +CreatePolygonRgn +CreateRectRgn +CreateRectRgnIndirect +CreateRoundRectRgn +CreateScalableFontResourceA +CreateScalableFontResourceW +CreateSolidBrush +DPtoLP +DeleteColorSpace +DeleteDC +DeleteEnhMetaFile +DeleteMetaFile +DeleteObject +DescribePixelFormat +DeviceCapabilitiesEx +DeviceCapabilitiesExA +DeviceCapabilitiesExW +DrawEscape +Ellipse +EnableEUDC +EndDoc +EndPage +EndPath +EnumEnhMetaFile +EnumFontFamiliesA +EnumFontFamiliesExA +EnumFontFamiliesExW +EnumFontFamiliesW +EnumFontsA +EnumFontsW +EnumICMProfilesA +EnumICMProfilesW +EnumMetaFile +EnumObjects +EqualRgn +Escape +ExcludeClipRect +ExtCreatePen +ExtCreateRegion +ExtEscape +ExtFloodFill +ExtSelectClipRgn +ExtTextOutA +ExtTextOutW +FillPath +FillRgn +FixBrushOrgEx +FlattenPath +FloodFill +FrameRgn +GdiComment +GdiFlush +GdiGetBatchLimit +GdiPlayDCScript +GdiPlayJournal +GdiPlayScript +GdiSetBatchLimit +GetArcDirection +GetAspectRatioFilterEx +GetBitmapBits +GetBitmapDimensionEx +GetBkColor +GetBkMode +GetBoundsRect +GetBrushOrgEx +GetCharABCWidthsA +GetCharABCWidthsFloatA +GetCharABCWidthsFloatW +GetCharABCWidthsW +GetCharWidth32A +GetCharWidth32W +GetCharWidthA +GetCharWidthFloatA +GetCharWidthFloatW +GetCharWidthW +GetCharacterPlacementA +GetCharacterPlacementW +GetClipBox +GetClipRgn +GetColorAdjustment +GetColorSpace +GetCurrentObject +GetCurrentPositionEx +GetDCOrgEx +GetDIBColorTable +GetDIBits +GetDeviceCaps +GetDeviceGammaRamp +GetEnhMetaFileA +GetEnhMetaFileBits +GetEnhMetaFileDescriptionA +GetEnhMetaFileDescriptionW +GetEnhMetaFileHeader +GetEnhMetaFilePaletteEntries +GetEnhMetaFileW +GetFontData +GetFontLanguageInfo +GetFontResourceInfo +GetGlyphOutline +GetGlyphOutlineA +GetGlyphOutlineW +GetGraphicsMode +GetICMProfileA +GetICMProfileW +GetKerningPairs +GetKerningPairsA +GetKerningPairsW +GetLayout +GetLogColorSpaceA +GetLogColorSpaceW +GetMapMode +GetMetaFileA +GetMetaFileBitsEx +GetMetaFileW +GetMetaRgn +GetMiterLimit +GetNearestColor +GetNearestPaletteIndex +GetObjectA +GetObjectType +GetObjectW +GetOutlineTextMetricsA +GetOutlineTextMetricsW +GetPaletteEntries +GetPath +GetPixel +GetPixelFormat +GetPolyFillMode +GetROP2 +GetRandomRgn +GetRasterizerCaps +GetRegionData +GetRgnBox +GetStockObject +GetStretchBltMode +GetSystemPaletteEntries +GetSystemPaletteUse +GetTextAlign +GetTextCharacterExtra +GetTextCharset +GetTextCharsetInfo +GetTextColor +GetTextExtentExPointA +GetTextExtentExPointW +GetTextExtentPoint32A +GetTextExtentPoint32W +GetTextExtentPointA +GetTextExtentPointW +GetTextFaceA +GetTextFaceW +GetTextMetricsA +GetTextMetricsW +GetViewportExtEx +GetViewportOrgEx +GetWinMetaFileBits +GetWindowExtEx +GetWindowOrgEx +GetWorldTransform +IntersectClipRect +InvertRgn +LPtoDP +LineDDA +LineTo +MaskBlt +ModifyWorldTransform +MoveToEx +OffsetClipRgn +OffsetRgn +OffsetViewportOrgEx +OffsetWindowOrgEx +PaintRgn +PatBlt +PathToRegion +Pie +PlayEnhMetaFile +PlayEnhMetaFileRecord +PlayMetaFile +PlayMetaFileRecord +PlgBlt +PolyBezier +PolyBezierTo +PolyDraw +PolyPolygon +PolyPolyline +PolyTextOutA +PolyTextOutW +Polygon +Polyline +PolylineTo +PtInRegion +PtVisible +RealizePalette +RectInRegion +RectVisible +Rectangle +RemoveFontResourceA +RemoveFontResourceW +ResetDCA +ResetDCW +ResizePalette +RestoreDC +RoundRect +SaveDC +ScaleViewportExtEx +ScaleWindowExtEx +SelectClipPath +SelectClipRgn +SelectObject +SelectPalette +SetAbortProc +SetArcDirection +SetBitmapBits +SetBitmapDimensionEx +SetBkColor +SetBkMode +SetBoundsRect +SetBrushOrgEx +SetColorAdjustment +SetColorSpace +SetDIBColorTable +SetDIBits +SetDIBitsToDevice +SetDeviceGammaRamp +SetEnhMetaFileBits +SetFontEnumeration +SetGraphicsMode +SetICMMode +SetICMProfileA +SetICMProfileW +SetLayout +SetMagicColors +SetMapMode +SetMapperFlags +SetMetaFileBitsEx +SetMetaRgn +SetMiterLimit +SetObjectOwner +SetPaletteEntries +SetPixel +SetPixelFormat +SetPixelV +SetPolyFillMode +SetROP2 +SetRectRgn +SetStretchBltMode +SetSystemPaletteUse +SetTextAlign +SetTextCharacterExtra +SetTextColor +SetTextJustification +SetViewportExtEx +SetViewportOrgEx +SetWinMetaFileBits +SetWindowExtEx +SetWindowOrgEx +SetWorldTransform +StartDocA +StartDocW +StartPage +StretchBlt +StretchDIBits +StrokeAndFillPath +StrokePath +SwapBuffers +TextOutA +TextOutW +TranslateCharsetInfo +UnrealizeObject +UpdateColors +UpdateICMRegKeyA +UpdateICMRegKeyW +WidenPath +gdiPlaySpoolStream +pfnRealizePalette +pfnSelectPalette diff --git a/ext/tcc-0.9.27/lib/kernel32.def b/ext/tcc-0.9.27/lib/kernel32.def new file mode 100644 index 00000000..f03e17ba --- /dev/null +++ b/ext/tcc-0.9.27/lib/kernel32.def @@ -0,0 +1,770 @@ +LIBRARY kernel32.dll + +EXPORTS +AddAtomA +AddAtomW +AllocConsole +AllocLSCallback +AllocSLCallback +AreFileApisANSI +BackupRead +BackupSeek +BackupWrite +Beep +BeginUpdateResourceA +BeginUpdateResourceW +BuildCommDCBA +BuildCommDCBAndTimeoutsA +BuildCommDCBAndTimeoutsW +BuildCommDCBW +CallNamedPipeA +CallNamedPipeW +Callback12 +Callback16 +Callback20 +Callback24 +Callback28 +Callback32 +Callback36 +Callback4 +Callback40 +Callback44 +Callback48 +Callback52 +Callback56 +Callback60 +Callback64 +Callback8 +CancelDeviceWakeupRequest +CancelIo +CancelWaitableTimer +ClearCommBreak +ClearCommError +CloseHandle +CloseProfileUserMapping +CloseSystemHandle +CommConfigDialogA +CommConfigDialogW +CompareFileTime +CompareStringA +CompareStringW +ConnectNamedPipe +ContinueDebugEvent +ConvertDefaultLocale +ConvertThreadToFiber +ConvertToGlobalHandle +CopyFileA +CopyFileExA +CopyFileExW +CopyFileW +CreateConsoleScreenBuffer +CreateDirectoryA +CreateDirectoryExA +CreateDirectoryExW +CreateDirectoryW +CreateEventA +CreateEventW +CreateFiber +CreateFileA +CreateFileMappingA +CreateFileMappingW +CreateFileW +CreateIoCompletionPort +CreateKernelThread +CreateMailslotA +CreateMailslotW +CreateMutexA +CreateMutexW +CreateNamedPipeA +CreateNamedPipeW +CreatePipe +CreateProcessA +CreateProcessW +CreateRemoteThread +CreateSemaphoreA +CreateSemaphoreW +CreateSocketHandle +CreateTapePartition +CreateThread +CreateToolhelp32Snapshot +CreateWaitableTimerA +CreateWaitableTimerW +DebugActiveProcess +DebugBreak +DefineDosDeviceA +DefineDosDeviceW +DeleteAtom +DeleteCriticalSection +DeleteFiber +DeleteFileA +DeleteFileW +DeviceIoControl +DisableThreadLibraryCalls +DisconnectNamedPipe +DosDateTimeToFileTime +DuplicateHandle +EndUpdateResourceA +EndUpdateResourceW +EnterCriticalSection +EnumCalendarInfoA +EnumCalendarInfoExA +EnumCalendarInfoExW +EnumCalendarInfoW +EnumDateFormatsA +EnumDateFormatsExA +EnumDateFormatsExW +EnumDateFormatsW +EnumLanguageGroupLocalesA +EnumLanguageGroupLocalesW +EnumResourceLanguagesA +EnumResourceLanguagesW +EnumResourceNamesA +EnumResourceNamesW +EnumResourceTypesA +EnumResourceTypesW +EnumSystemCodePagesA +EnumSystemCodePagesW +EnumSystemGeoID +EnumSystemLanguageGroupsA +EnumSystemLanguageGroupsW +EnumSystemLocalesA +EnumSystemLocalesW +EnumTimeFormatsA +EnumTimeFormatsW +EnumUILanguagesA +EnumUILanguagesW +EraseTape +EscapeCommFunction +ExitProcess +ExitThread +ExpandEnvironmentStringsA +ExpandEnvironmentStringsW +FT_Exit0 +FT_Exit12 +FT_Exit16 +FT_Exit20 +FT_Exit24 +FT_Exit28 +FT_Exit32 +FT_Exit36 +FT_Exit4 +FT_Exit40 +FT_Exit44 +FT_Exit48 +FT_Exit52 +FT_Exit56 +FT_Exit8 +FT_Prolog +FT_Thunk +FatalAppExitA +FatalAppExitW +FatalExit +FileTimeToDosDateTime +FileTimeToLocalFileTime +FileTimeToSystemTime +FillConsoleOutputAttribute +FillConsoleOutputCharacterA +FillConsoleOutputCharacterW +FindAtomA +FindAtomW +FindClose +FindCloseChangeNotification +FindFirstChangeNotificationA +FindFirstChangeNotificationW +FindFirstFileA +FindFirstFileExA +FindFirstFileExW +FindFirstFileW +FindNextChangeNotification +FindNextFileA +FindNextFileW +FindResourceA +FindResourceExA +FindResourceExW +FindResourceW +FlushConsoleInputBuffer +FlushFileBuffers +FlushInstructionCache +FlushViewOfFile +FoldStringA +FoldStringW +FormatMessageA +FormatMessageW +FreeConsole +FreeEnvironmentStringsA +FreeEnvironmentStringsW +FreeLSCallback +FreeLibrary +FreeLibraryAndExitThread +FreeResource +FreeSLCallback +GenerateConsoleCtrlEvent +GetACP +GetAtomNameA +GetAtomNameW +GetBinaryType +GetBinaryTypeA +GetBinaryTypeW +GetCPInfo +GetCPInfoExA +GetCPInfoExW +GetCalendarInfoA +GetCalendarInfoW +GetCommConfig +GetCommMask +GetCommModemStatus +GetCommProperties +GetCommState +GetCommTimeouts +GetCommandLineA +GetCommandLineW +GetCompressedFileSizeA +GetCompressedFileSizeW +GetComputerNameA +GetComputerNameW +GetConsoleCP +GetConsoleCursorInfo +GetConsoleMode +GetConsoleOutputCP +GetConsoleScreenBufferInfo +GetConsoleTitleA +GetConsoleTitleW +GetCurrencyFormatA +GetCurrencyFormatW +GetCurrentDirectoryA +GetCurrentDirectoryW +GetCurrentProcess +GetCurrentProcessId +GetCurrentThread +GetCurrentThreadId +GetDateFormatA +GetDateFormatW +GetDaylightFlag +GetDefaultCommConfigA +GetDefaultCommConfigW +GetDevicePowerState +GetDiskFreeSpaceA +GetDiskFreeSpaceExA +GetDiskFreeSpaceExW +GetDiskFreeSpaceW +GetDriveTypeA +GetDriveTypeW +GetEnvironmentStrings +GetEnvironmentStringsA +GetEnvironmentStringsW +GetEnvironmentVariableA +GetEnvironmentVariableW +GetErrorMode +GetExitCodeProcess +GetExitCodeThread +GetFileAttributesA +GetFileAttributesExA +GetFileAttributesExW +GetFileAttributesW +GetFileInformationByHandle +GetFileSize +GetFileTime +GetFileType +GetFullPathNameA +GetFullPathNameW +GetGeoInfoA +GetGeoInfoW +GetHandleContext +GetHandleInformation +GetLSCallbackTarget +GetLSCallbackTemplate +GetLargestConsoleWindowSize +GetLastError +GetLocalTime +GetLocaleInfoA +GetLocaleInfoW +GetLogicalDriveStringsA +GetLogicalDriveStringsW +GetLogicalDrives +GetLongPathNameA +GetLongPathNameW +GetMailslotInfo +GetModuleFileNameA +GetModuleFileNameW +GetModuleHandleA +GetModuleHandleW +GetModuleHandleExA +GetModuleHandleExW +GetNamedPipeHandleStateA +GetNamedPipeHandleStateW +GetNamedPipeInfo +GetNumberFormatA +GetNumberFormatW +GetNumberOfConsoleInputEvents +GetNumberOfConsoleMouseButtons +GetOEMCP +GetOverlappedResult +GetPriorityClass +GetPrivateProfileIntA +GetPrivateProfileIntW +GetPrivateProfileSectionA +GetPrivateProfileSectionNamesA +GetPrivateProfileSectionNamesW +GetPrivateProfileSectionW +GetPrivateProfileStringA +GetPrivateProfileStringW +GetPrivateProfileStructA +GetPrivateProfileStructW +GetProcAddress +GetProcessAffinityMask +GetProcessFlags +GetProcessHeap +GetProcessHeaps +GetProcessPriorityBoost +GetProcessShutdownParameters +GetProcessTimes +GetProcessVersion +GetProcessWorkingSetSize +GetProductName +GetProfileIntA +GetProfileIntW +GetProfileSectionA +GetProfileSectionW +GetProfileStringA +GetProfileStringW +GetQueuedCompletionStatus +GetSLCallbackTarget +GetSLCallbackTemplate +GetShortPathNameA +GetShortPathNameW +GetStartupInfoA +GetStartupInfoW +GetStdHandle +GetStringTypeA +GetStringTypeExA +GetStringTypeExW +GetStringTypeW +GetSystemDefaultLCID +GetSystemDefaultLangID +GetSystemDefaultUILanguage +GetSystemDirectoryA +GetSystemDirectoryW +GetSystemInfo +GetSystemPowerStatus +GetSystemTime +GetSystemTimeAdjustment +GetSystemTimeAsFileTime +GetTapeParameters +GetTapePosition +GetTapeStatus +GetTempFileNameA +GetTempFileNameW +GetTempPathA +GetTempPathW +GetThreadContext +GetThreadLocale +GetThreadPriority +GetThreadPriorityBoost +GetThreadSelectorEntry +GetThreadTimes +GetTickCount +GetTimeFormatA +GetTimeFormatW +GetTimeZoneInformation +GetUserDefaultLCID +GetUserDefaultLangID +GetUserDefaultUILanguage +GetUserGeoID +GetVersion +GetVersionExA +GetVersionExW +GetVolumeInformationA +GetVolumeInformationW +GetWindowsDirectoryA +GetWindowsDirectoryW +GetWriteWatch +GlobalAddAtomA +GlobalAddAtomW +GlobalAlloc +GlobalCompact +GlobalDeleteAtom +GlobalFindAtomA +GlobalFindAtomW +GlobalFix +GlobalFlags +GlobalFree +GlobalGetAtomNameA +GlobalGetAtomNameW +GlobalHandle +GlobalLock +GlobalMemoryStatus +GlobalReAlloc +GlobalSize +GlobalUnWire +GlobalUnfix +GlobalUnlock +GlobalWire +Heap32First +Heap32ListFirst +Heap32ListNext +Heap32Next +HeapAlloc +HeapCompact +HeapCreate +HeapDestroy +HeapFree +HeapLock +HeapReAlloc +HeapSetFlags +HeapSize +HeapUnlock +HeapValidate +HeapWalk +InitAtomTable +InitializeCriticalSection +InitializeCriticalSectionAndSpinCount +InterlockedCompareExchange +InterlockedDecrement +InterlockedExchange +InterlockedExchangeAdd +InterlockedIncrement +InvalidateNLSCache +IsBadCodePtr +IsBadHugeReadPtr +IsBadHugeWritePtr +IsBadReadPtr +IsBadStringPtrA +IsBadStringPtrW +IsBadWritePtr +IsDBCSLeadByte +IsDBCSLeadByteEx +IsDebuggerPresent +IsLSCallback +IsProcessorFeaturePresent +IsSLCallback +IsSystemResumeAutomatic +IsValidCodePage +IsValidLanguageGroup +IsValidLocale +K32Thk1632Epilog +K32Thk1632Prolog +K32_NtCreateFile +K32_RtlNtStatusToDosError +LCMapStringA +LCMapStringW +LeaveCriticalSection +LoadLibraryA +LoadLibraryExA +LoadLibraryExW +LoadLibraryW +LoadModule +LoadResource +LocalAlloc +LocalCompact +LocalFileTimeToFileTime +LocalFlags +LocalFree +LocalHandle +LocalLock +LocalReAlloc +LocalShrink +LocalSize +LocalUnlock +LockFile +LockFileEx +LockResource +MakeCriticalSectionGlobal +MapHInstLS +MapHInstLS_PN +MapHInstSL +MapHInstSL_PN +MapHModuleLS +MapHModuleSL +MapLS +MapSL +MapSLFix +MapViewOfFile +MapViewOfFileEx +Module32First +Module32Next +MoveFileA +MoveFileExA +MoveFileExW +MoveFileW +MulDiv +MultiByteToWideChar +NotifyNLSUserCache +OpenEventA +OpenEventW +OpenFile +OpenFileMappingA +OpenFileMappingW +OpenMutexA +OpenMutexW +OpenProcess +OpenProfileUserMapping +OpenSemaphoreA +OpenSemaphoreW +OpenThread +OpenVxDHandle +OpenWaitableTimerA +OpenWaitableTimerW +OutputDebugStringA +OutputDebugStringW +PeekConsoleInputA +PeekConsoleInputW +PeekNamedPipe +PostQueuedCompletionStatus +PrepareTape +Process32First +Process32Next +PulseEvent +PurgeComm +QT_Thunk +QueryDosDeviceA +QueryDosDeviceW +QueryNumberOfEventLogRecords +QueryOldestEventLogRecord +QueryPerformanceCounter +QueryPerformanceFrequency +QueueUserAPC +RaiseException +ReadConsoleA +ReadConsoleInputA +ReadConsoleInputW +ReadConsoleOutputA +ReadConsoleOutputAttribute +ReadConsoleOutputCharacterA +ReadConsoleOutputCharacterW +ReadConsoleOutputW +ReadConsoleW +ReadDirectoryChangesW +ReadFile +ReadFileEx +ReadFileScatter +ReadProcessMemory +RegisterServiceProcess +RegisterSysMsgHandler +ReinitializeCriticalSection +ReleaseMutex +ReleaseSemaphore +RemoveDirectoryA +RemoveDirectoryW +RequestDeviceWakeup +RequestWakeupLatency +ResetEvent +ResetNLSUserInfoCache +ResetWriteWatch +ResumeThread +RtlAddFunctionTable +RtlDeleteFunctionTable +RtlFillMemory +RtlInstallFunctionTableCallback +RtlMoveMemory +RtlUnwind +RtlUnwindEx +RtlZeroMemory +SMapLS +SMapLS_IP_EBP_12 +SMapLS_IP_EBP_16 +SMapLS_IP_EBP_20 +SMapLS_IP_EBP_24 +SMapLS_IP_EBP_28 +SMapLS_IP_EBP_32 +SMapLS_IP_EBP_36 +SMapLS_IP_EBP_40 +SMapLS_IP_EBP_8 +SUnMapLS +SUnMapLS_IP_EBP_12 +SUnMapLS_IP_EBP_16 +SUnMapLS_IP_EBP_20 +SUnMapLS_IP_EBP_24 +SUnMapLS_IP_EBP_28 +SUnMapLS_IP_EBP_32 +SUnMapLS_IP_EBP_36 +SUnMapLS_IP_EBP_40 +SUnMapLS_IP_EBP_8 +ScrollConsoleScreenBufferA +ScrollConsoleScreenBufferW +SearchPathA +SearchPathW +SetCalendarInfoA +SetCalendarInfoW +SetCommBreak +SetCommConfig +SetCommMask +SetCommState +SetCommTimeouts +SetComputerNameA +SetComputerNameW +SetConsoleActiveScreenBuffer +SetConsoleCP +SetConsoleCtrlHandler +SetConsoleCursorInfo +SetConsoleCursorPosition +SetConsoleMode +SetConsoleOutputCP +SetConsoleScreenBufferSize +SetConsoleTextAttribute +SetConsoleTitleA +SetConsoleTitleW +SetConsoleWindowInfo +SetCriticalSectionSpinCount +SetCurrentDirectoryA +SetCurrentDirectoryW +SetDaylightFlag +SetDefaultCommConfigA +SetDefaultCommConfigW +SetEndOfFile +SetEnvironmentVariableA +SetEnvironmentVariableW +SetErrorMode +SetEvent +SetFileApisToANSI +SetFileApisToOEM +SetFileAttributesA +SetFileAttributesW +SetFilePointer +SetFilePointerEx +SetFileTime +SetHandleContext +SetHandleCount +SetHandleInformation +SetLastError +SetLocalTime +SetLocaleInfoA +SetLocaleInfoW +SetMailslotInfo +SetMessageWaitingIndicator +SetNamedPipeHandleState +SetPriorityClass +SetProcessAffinityMask +SetProcessPriorityBoost +SetProcessShutdownParameters +SetProcessWorkingSetSize +SetStdHandle +SetSystemPowerState +SetSystemTime +SetSystemTimeAdjustment +SetTapeParameters +SetTapePosition +SetThreadAffinityMask +SetThreadContext +SetThreadExecutionState +SetThreadIdealProcessor +SetThreadLocale +SetThreadPriority +SetThreadPriorityBoost +SetTimeZoneInformation +SetUnhandledExceptionFilter +SetUserGeoID +SetVolumeLabelA +SetVolumeLabelW +SetWaitableTimer +SetupComm +SignalObjectAndWait +SignalSysMsgHandlers +SizeofResource +Sleep +SleepEx +SuspendThread +SwitchToFiber +SwitchToThread +SystemTimeToFileTime +SystemTimeToTzSpecificLocalTime +TerminateProcess +TerminateThread +Thread32First +Thread32Next +ThunkConnect32 +TlsAlloc +TlsAllocInternal +TlsFree +TlsFreeInternal +TlsGetValue +TlsSetValue +Toolhelp32ReadProcessMemory +TransactNamedPipe +TransmitCommChar +TryEnterCriticalSection +UTRegister +UTUnRegister +UnMapLS +UnMapSLFixArray +UnhandledExceptionFilter +UninitializeCriticalSection +UnlockFile +UnlockFileEx +UnmapViewOfFile +UpdateResourceA +UpdateResourceW +VerLanguageNameA +VerLanguageNameW +VirtualAlloc +VirtualAllocEx +VirtualFree +VirtualFreeEx +VirtualLock +VirtualProtect +VirtualProtectEx +VirtualQuery +VirtualQueryEx +VirtualUnlock +WaitCommEvent +WaitForDebugEvent +WaitForMultipleObjects +WaitForMultipleObjectsEx +WaitForSingleObject +WaitForSingleObjectEx +WaitNamedPipeA +WaitNamedPipeW +WideCharToMultiByte +WinExec +WriteConsoleA +WriteConsoleInputA +WriteConsoleInputW +WriteConsoleOutputA +WriteConsoleOutputAttribute +WriteConsoleOutputCharacterA +WriteConsoleOutputCharacterW +WriteConsoleOutputW +WriteConsoleW +WriteFile +WriteFileEx +WriteFileGather +WritePrivateProfileSectionA +WritePrivateProfileSectionW +WritePrivateProfileStringA +WritePrivateProfileStringW +WritePrivateProfileStructA +WritePrivateProfileStructW +WriteProcessMemory +WriteProfileSectionA +WriteProfileSectionW +WriteProfileStringA +WriteProfileStringW +WriteTapemark +_DebugOut +_DebugPrintf +_hread +_hwrite +_lclose +_lcreat +_llseek +_lopen +_lread +_lwrite +dprintf +lstrcat +lstrcatA +lstrcatW +lstrcmp +lstrcmpA +lstrcmpW +lstrcmpi +lstrcmpiA +lstrcmpiW +lstrcpy +lstrcpyA +lstrcpyW +lstrcpyn +lstrcpynA +lstrcpynW +lstrlen +lstrlenA +lstrlenW diff --git a/ext/tcc-0.9.27/lib/msvcrt.def b/ext/tcc-0.9.27/lib/msvcrt.def new file mode 100644 index 00000000..742acb8b --- /dev/null +++ b/ext/tcc-0.9.27/lib/msvcrt.def @@ -0,0 +1,1399 @@ +LIBRARY msvcrt.dll + +EXPORTS +$I10_OUTPUT +??0__non_rtti_object@@QAE@ABV0@@Z +??0__non_rtti_object@@QAE@PBD@Z +??0bad_cast@@AAE@PBQBD@Z +??0bad_cast@@QAE@ABQBD@Z +??0bad_cast@@QAE@ABV0@@Z +??0bad_cast@@QAE@PBD@Z +??0bad_typeid@@QAE@ABV0@@Z +??0bad_typeid@@QAE@PBD@Z +??0exception@@QAE@ABQBD@Z +??0exception@@QAE@ABQBDH@Z +??0exception@@QAE@ABV0@@Z +??0exception@@QAE@XZ +??1__non_rtti_object@@UAE@XZ +??1bad_cast@@UAE@XZ +??1bad_typeid@@UAE@XZ +??1exception@@UAE@XZ +??1type_info@@UAE@XZ +??2@YAPAXI@Z +??2@YAPAXIHPBDH@Z +??3@YAXPAX@Z +??4__non_rtti_object@@QAEAAV0@ABV0@@Z +??4bad_cast@@QAEAAV0@ABV0@@Z +??4bad_typeid@@QAEAAV0@ABV0@@Z +??4exception@@QAEAAV0@ABV0@@Z +??8type_info@@QBEHABV0@@Z +??9type_info@@QBEHABV0@@Z +??_7__non_rtti_object@@6B@ +??_7bad_cast@@6B@ +??_7bad_typeid@@6B@ +??_7exception@@6B@ +??_E__non_rtti_object@@UAEPAXI@Z +??_Ebad_cast@@UAEPAXI@Z +??_Ebad_typeid@@UAEPAXI@Z +??_Eexception@@UAEPAXI@Z +??_Fbad_cast@@QAEXXZ +??_Fbad_typeid@@QAEXXZ +??_G__non_rtti_object@@UAEPAXI@Z +??_Gbad_cast@@UAEPAXI@Z +??_Gbad_typeid@@UAEPAXI@Z +??_Gexception@@UAEPAXI@Z +??_U@YAPAXI@Z +??_U@YAPAXIHPBDH@Z +??_V@YAXPAX@Z +?_query_new_handler@@YAP6AHI@ZXZ +?_query_new_mode@@YAHXZ +?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z +?_set_new_mode@@YAHH@Z +?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZP6AXI0@Z@Z +?before@type_info@@QBEHABV1@@Z +?name@type_info@@QBEPBDXZ +?raw_name@type_info@@QBEPBDXZ +?set_new_handler@@YAP6AXXZP6AXXZ@Z +?set_terminate@@YAP6AXXZP6AXXZ@Z +?set_unexpected@@YAP6AXXZP6AXXZ@Z +?terminate@@YAXXZ +?unexpected@@YAXXZ +?what@exception@@UBEPBDXZ +_CIacos +_CIasin +_CIatan +_CIatan2 +_CIcos +_CIcosh +_CIexp +_CIfmod +_CIlog +_CIlog10 +_CIpow +_CIsin +_CIsinh +_CIsqrt +_CItan +_CItanh +_CrtCheckMemory +_CrtDbgBreak +_CrtDbgReport +_CrtDbgReportV +_CrtDbgReportW +_CrtDbgReportWV +_CrtDoForAllClientObjects +_CrtDumpMemoryLeaks +_CrtIsMemoryBlock +_CrtIsValidHeapPointer +_CrtIsValidPointer +_CrtMemCheckpoint +_CrtMemDifference +_CrtMemDumpAllObjectsSince +_CrtMemDumpStatistics +_CrtReportBlockType +_CrtSetAllocHook +_CrtSetBreakAlloc +_CrtSetDbgBlockType +_CrtSetDbgFlag +_CrtSetDumpClient +_CrtSetReportFile +_CrtSetReportHook +_CrtSetReportHook2 +_CrtSetReportMode +_CxxThrowException +_EH_prolog +_Getdays +_Getmonths +_Gettnames +_HUGE +_Strftime +_XcptFilter +__CppXcptFilter +__CxxCallUnwindDelDtor +__CxxCallUnwindDtor +__CxxCallUnwindVecDtor +__CxxDetectRethrow +__CxxExceptionFilter +__CxxFrameHandler +__CxxFrameHandler2 +__CxxFrameHandler3 +__CxxLongjmpUnwind +__CxxQueryExceptionSize +__CxxRegisterExceptionObject +__CxxUnregisterExceptionObject +__DestructExceptionObject +__RTCastToVoid +__RTDynamicCast +__RTtypeid +__STRINGTOLD +___lc_codepage_func +___lc_collate_cp_func +___lc_handle_func +___mb_cur_max_func +___setlc_active_func +___unguarded_readlc_active_add_func +__argc +__argv +__badioinfo +__crtCompareStringA +__crtCompareStringW +__crtGetLocaleInfoW +__crtGetStringTypeW +__crtLCMapStringA +__crtLCMapStringW +__daylight +__dllonexit +__doserrno +__dstbias +__fpecode +__getmainargs +__initenv +__iob_func +__isascii +__iscsym +__iscsymf +__lc_codepage +__lc_collate_cp +__lc_handle +__lconv_init +__libm_sse2_acos +__libm_sse2_acosf +__libm_sse2_asin +__libm_sse2_asinf +__libm_sse2_atan +__libm_sse2_atan2 +__libm_sse2_atanf +__libm_sse2_cos +__libm_sse2_cosf +__libm_sse2_exp +__libm_sse2_expf +__libm_sse2_log +__libm_sse2_log10 +__libm_sse2_log10f +__libm_sse2_logf +__libm_sse2_pow +__libm_sse2_powf +__libm_sse2_sin +__libm_sse2_sinf +__libm_sse2_tan +__libm_sse2_tanf +__mb_cur_max +__p___argc +__p___argv +__p___initenv +__p___mb_cur_max +__p___wargv +__p___winitenv +__p__acmdln +__p__amblksiz +__p__commode +__p__daylight +__p__dstbias +__p__environ +__p__fileinfo +__p__fmode +__p__iob +__p__mbcasemap +__p__mbctype +__p__osver +__p__pctype +__p__pgmptr +__p__pwctype +__p__timezone +__p__tzname +__p__wcmdln +__p__wenviron +__p__winmajor +__p__winminor +__p__winver +__p__wpgmptr +__pctype_func +__pioinfo +__pwctype_func +__pxcptinfoptrs +__set_app_type +__setlc_active +__setusermatherr +__strncnt +__threadhandle +__threadid +__toascii +__unDName +__unDNameEx +__uncaught_exception +__unguarded_readlc_active +__wargv +__wcserror +__wcserror_s +__wcsncnt +__wgetmainargs +__winitenv +_abnormal_termination +_abs64 +_access +_access_s +_acmdln +_adj_fdiv_m16i +_adj_fdiv_m32 +_adj_fdiv_m32i +_adj_fdiv_m64 +_adj_fdiv_r +_adj_fdivr_m16i +_adj_fdivr_m32 +_adj_fdivr_m32i +_adj_fdivr_m64 +_adj_fpatan +_adj_fprem +_adj_fprem1 +_adj_fptan +_adjust_fdiv +_aexit_rtn +_aligned_free +_aligned_free_dbg +_aligned_malloc +_aligned_malloc_dbg +_aligned_offset_malloc +_aligned_offset_malloc_dbg +_aligned_offset_realloc +_aligned_offset_realloc_dbg +_aligned_realloc +_aligned_realloc_dbg +_amsg_exit +_assert +_atodbl +_atodbl_l +_atof_l +_atoflt_l +_atoi64 +_atoi64_l +_atoi_l +_atol_l +_atoldbl +_atoldbl_l +_beep +_beginthread +_beginthreadex +_c_exit +_cabs +_callnewh +_calloc_dbg +_cexit +_cgets +_cgets_s +_cgetws +_cgetws_s +_chdir +_chdrive +_chgsign +_chkesp +_chmod +_chsize +_chsize_s +_chvalidator +_chvalidator_l +_clearfp +_close +_commit +_commode +_control87 +_controlfp +_controlfp_s +_copysign +_cprintf +_cprintf_l +_cprintf_p +_cprintf_p_l +_cprintf_s +_cprintf_s_l +_cputs +_cputws +_creat +_crtAssertBusy +_crtBreakAlloc +_crtDbgFlag +_cscanf +_cscanf_l +_cscanf_s +_cscanf_s_l +_ctime32 +_ctime32_s +_ctime64 +_ctime64_s +_ctype +_cwait +_cwprintf +_cwprintf_l +_cwprintf_p +_cwprintf_p_l +_cwprintf_s +_cwprintf_s_l +_cwscanf +_cwscanf_l +_cwscanf_s +_cwscanf_s_l +_daylight +_difftime32 +_difftime64 +_dstbias +_dup +_dup2 +_ecvt +_ecvt_s +_endthread +_endthreadex +_environ +_eof +_errno +_except_handler2 +_except_handler3 +_except_handler4_common +_execl +_execle +_execlp +_execlpe +_execv +_execve +_execvp +_execvpe +_exit +_expand +_expand_dbg +_fcloseall +_fcvt +_fcvt_s +_fdopen +_fgetchar +_fgetwchar +_filbuf +_fileinfo +_filelength +_filelengthi64 +_fileno +_findclose +_findfirst +_findfirst64 +_findfirsti64 +_findnext +_findnext64 +_findnexti64 +_finite +_flsbuf +_flushall +_fmode +_fpclass +_fpieee_flt +_fpreset +_fprintf_l +_fprintf_p +_fprintf_p_l +_fprintf_s_l +_fputchar +_fputwchar +_free_dbg +_freea +_freea_s +_fscanf_l +_fscanf_s_l +_fseeki64 +_fsopen +_fstat +_fstat64 +_fstati64 +_ftime +_ftime32 +_ftime32_s +_ftime64 +_ftime64_s +_ftol +_ftol2 +_ftol2_sse +_ftol2_sse_excpt +_fullpath +_fullpath_dbg +_futime +_futime32 +_futime64 +_fwprintf_l +_fwprintf_p +_fwprintf_p_l +_fwprintf_s_l +_fwscanf_l +_fwscanf_s_l +_gcvt +_gcvt_s +_get_doserrno +_get_environ +_get_errno +_get_fileinfo +_get_fmode +_get_heap_handle +_get_osfhandle +_get_osplatform +_get_osver +_get_output_format +_get_pgmptr +_get_sbh_threshold +_get_wenviron +_get_winmajor +_get_winminor +_get_winver +_get_wpgmptr +_getch +_getche +_getcwd +_getdcwd +_getdiskfree +_getdllprocaddr +_getdrive +_getdrives +_getmaxstdio +_getmbcp +_getpid +_getsystime +_getw +_getwch +_getwche +_getws +_global_unwind2 +_gmtime32 +_gmtime32_s +_gmtime64 +_gmtime64_s +_heapadd +_heapchk +_heapmin +_heapset +_heapused +_heapwalk +_hypot +_i64toa +_i64toa_s +_i64tow +_i64tow_s +_initterm +_initterm_e +_inp +_inpd +_inpw +_invalid_parameter +_iob +_isalnum_l +_isalpha_l +_isatty +_iscntrl_l +_isctype +_isctype_l +_isdigit_l +_isgraph_l +_isleadbyte_l +_islower_l +_ismbbalnum +_ismbbalnum_l +_ismbbalpha +_ismbbalpha_l +_ismbbgraph +_ismbbgraph_l +_ismbbkalnum +_ismbbkalnum_l +_ismbbkana +_ismbbkana_l +_ismbbkprint +_ismbbkprint_l +_ismbbkpunct +_ismbbkpunct_l +_ismbblead +_ismbblead_l +_ismbbprint +_ismbbprint_l +_ismbbpunct +_ismbbpunct_l +_ismbbtrail +_ismbbtrail_l +_ismbcalnum +_ismbcalnum_l +_ismbcalpha +_ismbcalpha_l +_ismbcdigit +_ismbcdigit_l +_ismbcgraph +_ismbcgraph_l +_ismbchira +_ismbchira_l +_ismbckata +_ismbckata_l +_ismbcl0 +_ismbcl0_l +_ismbcl1 +_ismbcl1_l +_ismbcl2 +_ismbcl2_l +_ismbclegal +_ismbclegal_l +_ismbclower +_ismbclower_l +_ismbcprint +_ismbcprint_l +_ismbcpunct +_ismbcpunct_l +_ismbcspace +_ismbcspace_l +_ismbcsymbol +_ismbcsymbol_l +_ismbcupper +_ismbcupper_l +_ismbslead +_ismbslead_l +_ismbstrail +_ismbstrail_l +_isnan +_isprint_l +_isspace_l +_isupper_l +_iswalnum_l +_iswalpha_l +_iswcntrl_l +_iswctype_l +_iswdigit_l +_iswgraph_l +_iswlower_l +_iswprint_l +_iswpunct_l +_iswspace_l +_iswupper_l +_iswxdigit_l +_isxdigit_l +_itoa +_itoa_s +_itow +_itow_s +_j0 +_j1 +_jn +_kbhit +_lfind +_lfind_s +_loaddll +_local_unwind2 +_local_unwind4 +_localtime32 +_localtime32_s +_localtime64 +_localtime64_s +_lock +_locking +_logb +_longjmpex +_lrotl +_lrotr +_lsearch +_lsearch_s +_lseek +_lseeki64 +_ltoa +_ltoa_s +_ltow +_ltow_s +_makepath +_makepath_s +_malloc_dbg +_mbbtombc +_mbbtombc_l +_mbbtype +_mbcasemap +_mbccpy +_mbccpy_l +_mbccpy_s +_mbccpy_s_l +_mbcjistojms +_mbcjistojms_l +_mbcjmstojis +_mbcjmstojis_l +_mbclen +_mbclen_l +_mbctohira +_mbctohira_l +_mbctokata +_mbctokata_l +_mbctolower +_mbctolower_l +_mbctombb +_mbctombb_l +_mbctoupper +_mbctoupper_l +_mbctype +_mblen_l +_mbsbtype +_mbsbtype_l +_mbscat +_mbscat_s +_mbscat_s_l +_mbschr +_mbschr_l +_mbscmp +_mbscmp_l +_mbscoll +_mbscoll_l +_mbscpy +_mbscpy_s +_mbscpy_s_l +_mbscspn +_mbscspn_l +_mbsdec +_mbsdec_l +_mbsdup +_mbsicmp +_mbsicmp_l +_mbsicoll +_mbsicoll_l +_mbsinc +_mbsinc_l +_mbslen +_mbslen_l +_mbslwr +_mbslwr_l +_mbslwr_s +_mbslwr_s_l +_mbsnbcat +_mbsnbcat_l +_mbsnbcat_s +_mbsnbcat_s_l +_mbsnbcmp +_mbsnbcmp_l +_mbsnbcnt +_mbsnbcnt_l +_mbsnbcoll +_mbsnbcoll_l +_mbsnbcpy +_mbsnbcpy_l +_mbsnbcpy_s +_mbsnbcpy_s_l +_mbsnbicmp +_mbsnbicmp_l +_mbsnbicoll +_mbsnbicoll_l +_mbsnbset +_mbsnbset_l +_mbsnbset_s +_mbsnbset_s_l +_mbsncat +_mbsncat_l +_mbsncat_s +_mbsncat_s_l +_mbsnccnt +_mbsnccnt_l +_mbsncmp +_mbsncmp_l +_mbsncoll +_mbsncoll_l +_mbsncpy +_mbsncpy_l +_mbsncpy_s +_mbsncpy_s_l +_mbsnextc +_mbsnextc_l +_mbsnicmp +_mbsnicmp_l +_mbsnicoll +_mbsnicoll_l +_mbsninc +_mbsninc_l +_mbsnlen +_mbsnlen_l +_mbsnset +_mbsnset_l +_mbsnset_s +_mbsnset_s_l +_mbspbrk +_mbspbrk_l +_mbsrchr +_mbsrchr_l +_mbsrev +_mbsrev_l +_mbsset +_mbsset_l +_mbsset_s +_mbsset_s_l +_mbsspn +_mbsspn_l +_mbsspnp +_mbsspnp_l +_mbsstr +_mbsstr_l +_mbstok +_mbstok_l +_mbstok_s +_mbstok_s_l +_mbstowcs_l +_mbstowcs_s_l +_mbstrlen +_mbstrlen_l +_mbstrnlen +_mbstrnlen_l +_mbsupr +_mbsupr_l +_mbsupr_s +_mbsupr_s_l +_mbtowc_l +_memccpy +_memicmp +_memicmp_l +_mkdir +_mkgmtime +_mkgmtime32 +_mkgmtime64 +_mktemp +_mktemp_s +_mktime32 +_mktime64 +_msize +_msize_debug +_nextafter +_onexit +_open +_open_osfhandle +_osplatform +_osver +_outp +_outpd +_outpw +_pclose +_pctype +_pgmptr +_pipe +_popen +_printf_l +_printf_p +_printf_p_l +_printf_s_l +_purecall +_putch +_putenv +_putenv_s +_putw +_putwch +_putws +_pwctype +_read +_realloc_dbg +_resetstkoflw +_rmdir +_rmtmp +_rotl +_rotl64 +_rotr +_rotr64 +_safe_fdiv +_safe_fdivr +_safe_fprem +_safe_fprem1 +_scalb +_scanf_l +_scanf_s_l +_scprintf +_scprintf_l +_scprintf_p_l +_scwprintf +_scwprintf_l +_scwprintf_p_l +_searchenv +_searchenv_s +_seh_longjmp_unwind +_seh_longjmp_unwind4 +_set_SSE2_enable +_set_controlfp +_set_doserrno +_set_errno +_set_error_mode +_set_fileinfo +_set_fmode +_set_output_format +_set_sbh_threshold +_seterrormode +_setjmp +_setjmp3 +_setmaxstdio +_setmbcp +_setmode +_setsystime +_sleep +_snprintf +_snprintf_c +_snprintf_c_l +_snprintf_l +_snprintf_s +_snprintf_s_l +_snscanf +_snscanf_l +_snscanf_s +_snscanf_s_l +_snwprintf +_snwprintf_l +_snwprintf_s +_snwprintf_s_l +_snwscanf +_snwscanf_l +_snwscanf_s +_snwscanf_s_l +_sopen +_sopen_s +_spawnl +_spawnle +_spawnlp +_spawnlpe +_spawnv +_spawnve +_spawnvp +_spawnvpe +_splitpath +_splitpath_s +_sprintf_l +_sprintf_p_l +_sprintf_s_l +_sscanf_l +_sscanf_s_l +_stat +_stat64 +_stati64 +_statusfp +_strcmpi +_strcoll_l +_strdate +_strdate_s +_strdup +_strdup_dbg +_strerror +_strerror_s +_stricmp +_stricmp_l +_stricoll +_stricoll_l +_strlwr +_strlwr_l +_strlwr_s +_strlwr_s_l +_strncoll +_strncoll_l +_strnicmp +_strnicmp_l +_strnicoll +_strnicoll_l +_strnset +_strnset_s +_strrev +_strset +_strset_s +_strtime +_strtime_s +_strtod_l +_strtoi64 +_strtoi64_l +_strtol_l +_strtoui64 +_strtoui64_l +_strtoul_l +_strupr +_strupr_l +_strupr_s +_strupr_s_l +_strxfrm_l +_swab +_swprintf +_swprintf_c +_swprintf_c_l +_swprintf_p_l +_swprintf_s_l +_swscanf_l +_swscanf_s_l +_sys_errlist +_sys_nerr +_tell +_telli64 +_tempnam +_tempnam_dbg +_time32 +_time64 +_timezone +_tolower +_tolower_l +_toupper +_toupper_l +_towlower_l +_towupper_l +_tzname +_tzset +_ui64toa +_ui64toa_s +_ui64tow +_ui64tow_s +_ultoa +_ultoa_s +_ultow +_ultow_s +_umask +_umask_s +_ungetch +_ungetwch +_unlink +_unloaddll +_unlock +_utime +_utime32 +_utime64 +_vcprintf +_vcprintf_l +_vcprintf_p +_vcprintf_p_l +_vcprintf_s +_vcprintf_s_l +_vcwprintf +_vcwprintf_l +_vcwprintf_p +_vcwprintf_p_l +_vcwprintf_s +_vcwprintf_s_l +_vfprintf_l +_vfprintf_p +_vfprintf_p_l +_vfprintf_s_l +_vfwprintf_l +_vfwprintf_p +_vfwprintf_p_l +_vfwprintf_s_l +_vprintf_l +_vprintf_p +_vprintf_p_l +_vprintf_s_l +_vscprintf +_vscprintf_l +_vscprintf_p_l +_vscwprintf +_vscwprintf_l +_vscwprintf_p_l +_vsnprintf +_vsnprintf_c +_vsnprintf_c_l +_vsnprintf_l +_vsnprintf_s +_vsnprintf_s_l +_vsnwprintf +_vsnwprintf_l +_vsnwprintf_s +_vsnwprintf_s_l +_vsprintf_l +_vsprintf_p +_vsprintf_p_l +_vsprintf_s_l +_vswprintf +_vswprintf_c +_vswprintf_c_l +_vswprintf_l +_vswprintf_p_l +_vswprintf_s_l +_vwprintf_l +_vwprintf_p +_vwprintf_p_l +_vwprintf_s_l +_waccess +_waccess_s +_wasctime +_wasctime_s +_wassert +_wchdir +_wchmod +_wcmdln +_wcreat +_wcscoll_l +_wcsdup +_wcsdup_dbg +_wcserror +_wcserror_s +_wcsftime_l +_wcsicmp +_wcsicmp_l +_wcsicoll +_wcsicoll_l +_wcslwr +_wcslwr_l +_wcslwr_s +_wcslwr_s_l +_wcsncoll +_wcsncoll_l +_wcsnicmp +_wcsnicmp_l +_wcsnicoll +_wcsnicoll_l +_wcsnset +_wcsnset_s +_wcsrev +_wcsset +_wcsset_s +_wcstoi64 +_wcstoi64_l +_wcstol_l +_wcstombs_l +_wcstombs_s_l +_wcstoui64 +_wcstoui64_l +_wcstoul_l +_wcsupr +_wcsupr_l +_wcsupr_s +_wcsupr_s_l +_wcsxfrm_l +_wctime +_wctime32 +_wctime32_s +_wctime64 +_wctime64_s +_wctomb_l +_wctomb_s_l +_wctype +_wenviron +_wexecl +_wexecle +_wexeclp +_wexeclpe +_wexecv +_wexecve +_wexecvp +_wexecvpe +_wfdopen +_wfindfirst +_wfindfirst64 +_wfindfirsti64 +_wfindnext +_wfindnext64 +_wfindnexti64 +_wfopen +_wfopen_s +_wfreopen +_wfreopen_s +_wfsopen +_wfullpath +_wfullpath_dbg +_wgetcwd +_wgetdcwd +_wgetenv +_wgetenv_s +_winmajor +_winminor +_winput_s +_winver +_wmakepath +_wmakepath_s +_wmkdir +_wmktemp +_wmktemp_s +_wopen +_woutput_s +_wperror +_wpgmptr +_wpopen +_wprintf_l +_wprintf_p +_wprintf_p_l +_wprintf_s_l +_wputenv +_wputenv_s +_wremove +_wrename +_write +_wrmdir +_wscanf_l +_wscanf_s_l +_wsearchenv +_wsearchenv_s +_wsetlocale +_wsopen +_wsopen_s +_wspawnl +_wspawnle +_wspawnlp +_wspawnlpe +_wspawnv +_wspawnve +_wspawnvp +_wspawnvpe +_wsplitpath +_wsplitpath_s +_wstat +_wstat64 +_wstati64 +_wstrdate +_wstrdate_s +_wstrtime +_wstrtime_s +_wsystem +_wtempnam +_wtempnam_dbg +_wtmpnam +_wtmpnam_s +_wtof +_wtof_l +_wtoi +_wtoi64 +_wtoi64_l +_wtoi_l +_wtol +_wtol_l +_wunlink +_wutime +_wutime32 +_wutime64 +_y0 +_y1 +_yn +abort +abs +acos +asctime +asctime_s +asin +atan +atan2 +atexit +atof +atoi +atol +bsearch +bsearch_s +btowc +calloc +ceil +clearerr +clearerr_s +clock +cos +cosh +ctime +difftime +div +exit +exp +fabs +fclose +feof +ferror +fflush +fgetc +fgetpos +fgets +fgetwc +fgetws +floor +fmod +fopen +fopen_s +fprintf +fprintf_s +fputc +fputs +fputwc +fputws +fread +free +freopen +freopen_s +frexp +fscanf +fscanf_s +fseek +fsetpos +ftell +fwprintf +fwprintf_s +fwrite +fwscanf +fwscanf_s +getc +getchar +getenv +getenv_s +gets +getwc +getwchar +gmtime +is_wctype +isalnum +isalpha +iscntrl +isdigit +isgraph +isleadbyte +islower +isprint +ispunct +isspace +isupper +iswalnum +iswalpha +iswascii +iswcntrl +iswctype +iswdigit +iswgraph +iswlower +iswprint +iswpunct +iswspace +iswupper +iswxdigit +isxdigit +labs +ldexp +ldiv +localeconv +localtime +log +log10 +longjmp +malloc +mblen +mbrlen +mbrtowc +mbsdup_dbg +mbsrtowcs +mbsrtowcs_s +mbstowcs +mbstowcs_s +mbtowc +memchr +memcmp +memcpy +memcpy_s +memmove +memmove_s +memset +mktime +modf +perror +pow +printf +printf_s +putc +putchar +puts +putwc +putwchar +qsort +qsort_s +raise +rand +rand_s +realloc +remove +rename +rewind +scanf +scanf_s +setbuf +setlocale +setvbuf +signal +sin +sinh +sprintf +sprintf_s +sqrt +srand +sscanf +sscanf_s +strcat +strcat_s +strchr +strcmp +strcoll +strcpy +strcpy_s +strcspn +strerror +strerror_s +strftime +strlen +strncat +strncat_s +strncmp +strncpy +strncpy_s +strnlen +strpbrk +strrchr +strspn +strstr +strtod +strtok +strtok_s +strtol +strtoul +strxfrm +swprintf +swprintf_s +swscanf +swscanf_s +system +tan +tanh +time +tmpfile +tmpfile_s +tmpnam +tmpnam_s +tolower +toupper +towlower +towupper +ungetc +ungetwc +utime +vfprintf +vfprintf_s +vfwprintf +vfwprintf_s +vprintf +vprintf_s +vsnprintf +vsprintf +vsprintf_s +vswprintf +vswprintf_s +vwprintf +vwprintf_s +wcrtomb +wcrtomb_s +wcscat +wcscat_s +wcschr +wcscmp +wcscoll +wcscpy +wcscpy_s +wcscspn +wcsftime +wcslen +wcsncat +wcsncat_s +wcsncmp +wcsncpy +wcsncpy_s +wcsnlen +wcspbrk +wcsrchr +wcsrtombs +wcsrtombs_s +wcsspn +wcsstr +wcstod +wcstok +wcstok_s +wcstol +wcstombs +wcstombs_s +wcstoul +wcsxfrm +wctob +wctomb +wctomb_s +wprintf +wprintf_s +wscanf +wscanf_s diff --git a/ext/tcc-0.9.27/lib/user32.def b/ext/tcc-0.9.27/lib/user32.def new file mode 100644 index 00000000..a034dac2 --- /dev/null +++ b/ext/tcc-0.9.27/lib/user32.def @@ -0,0 +1,658 @@ +LIBRARY user32.dll + +EXPORTS +ActivateKeyboardLayout +AdjustWindowRect +AdjustWindowRectEx +AlignRects +AllowSetForegroundWindow +AnimateWindow +AnyPopup +AppendMenuA +AppendMenuW +ArrangeIconicWindows +AttachThreadInput +BeginDeferWindowPos +BeginPaint +BlockInput +BringWindowToTop +BroadcastSystemMessage +BroadcastSystemMessageA +BroadcastSystemMessageW +CalcChildScroll +CallMsgFilter +CallMsgFilterA +CallMsgFilterW +CallNextHookEx +CallWindowProcA +CallWindowProcW +CascadeChildWindows +CascadeWindows +ChangeClipboardChain +ChangeDisplaySettingsA +ChangeDisplaySettingsExA +ChangeDisplaySettingsExW +ChangeDisplaySettingsW +ChangeMenuA +ChangeMenuW +CharLowerA +CharLowerBuffA +CharLowerBuffW +CharLowerW +CharNextA +CharNextExA +CharNextExW +CharNextW +CharPrevA +CharPrevExA +CharPrevExW +CharPrevW +CharToOemA +CharToOemBuffA +CharToOemBuffW +CharToOemW +CharUpperA +CharUpperBuffA +CharUpperBuffW +CharUpperW +CheckDlgButton +CheckMenuItem +CheckMenuRadioItem +CheckRadioButton +ChildWindowFromPoint +ChildWindowFromPointEx +ClientThreadConnect +ClientToScreen +ClipCursor +CloseClipboard +CloseDesktop +CloseWindow +CloseWindowStation +CopyAcceleratorTableA +CopyAcceleratorTableW +CopyIcon +CopyImage +CopyRect +CountClipboardFormats +CreateAcceleratorTableA +CreateAcceleratorTableW +CreateCaret +CreateCursor +CreateDesktopA +CreateDesktopW +CreateDialogIndirectParamA +CreateDialogIndirectParamW +CreateDialogParamA +CreateDialogParamW +CreateIcon +CreateIconFromResource +CreateIconFromResourceEx +CreateIconIndirect +CreateMDIWindowA +CreateMDIWindowW +CreateMenu +CreatePopupMenu +CreateWindowExA +CreateWindowExW +CreateWindowStationA +CreateWindowStationW +DdeAbandonTransaction +DdeAccessData +DdeAddData +DdeClientTransaction +DdeCmpStringHandles +DdeConnect +DdeConnectList +DdeCreateDataHandle +DdeCreateStringHandleA +DdeCreateStringHandleW +DdeDisconnect +DdeDisconnectList +DdeEnableCallback +DdeFreeDataHandle +DdeFreeStringHandle +DdeGetData +DdeGetLastError +DdeImpersonateClient +DdeInitializeA +DdeInitializeW +DdeKeepStringHandle +DdeNameService +DdePostAdvise +DdeQueryConvInfo +DdeQueryNextServer +DdeQueryStringA +DdeQueryStringW +DdeReconnect +DdeSetQualityOfService +DdeSetUserHandle +DdeUnaccessData +DdeUninitialize +DefDlgProcA +DefDlgProcW +DefFrameProcA +DefFrameProcW +DefMDIChildProcA +DefMDIChildProcW +DefWindowProcA +DefWindowProcW +DeferWindowPos +DeleteMenu +DestroyAcceleratorTable +DestroyCaret +DestroyCursor +DestroyIcon +DestroyMenu +DestroyWindow +DialogBoxIndirectParamA +DialogBoxIndirectParamW +DialogBoxParamA +DialogBoxParamW +DispatchMessageA +DispatchMessageW +DlgDirListA +DlgDirListComboBoxA +DlgDirListComboBoxW +DlgDirListW +DlgDirSelectComboBoxExA +DlgDirSelectComboBoxExW +DlgDirSelectExA +DlgDirSelectExW +DragDetect +DragObject +DrawAnimatedRects +DrawCaption +DrawCaptionTempA +DrawCaptionTempW +DrawEdge +DrawFocusRect +DrawFrame +DrawFrameControl +DrawIcon +DrawIconEx +DrawMenuBar +DrawMenuBarTemp +DrawStateA +DrawStateW +DrawTextA +DrawTextExA +DrawTextExW +DrawTextW +EditWndProc +EmptyClipboard +EnableMenuItem +EnableScrollBar +EnableWindow +EndDeferWindowPos +EndDialog +EndMenu +EndPaint +EndTask +EnumChildWindows +EnumClipboardFormats +EnumDesktopWindows +EnumDesktopsA +EnumDesktopsW +EnumDisplayDevicesA +EnumDisplayDevicesW +EnumDisplayMonitors +EnumDisplaySettingsA +EnumDisplaySettingsExA +EnumDisplaySettingsExW +EnumDisplaySettingsW +EnumPropsA +EnumPropsExA +EnumPropsExW +EnumPropsW +EnumThreadWindows +EnumWindowStationsA +EnumWindowStationsW +EnumWindows +EqualRect +ExcludeUpdateRgn +ExitWindowsEx +FillRect +FindWindowA +FindWindowExA +FindWindowExW +FindWindowW +FlashWindow +FlashWindowEx +FrameRect +FreeDDElParam +GetActiveWindow +GetAltTabInfo +GetAncestor +GetAsyncKeyState +GetCapture +GetCaretBlinkTime +GetCaretPos +GetClassInfoA +GetClassInfoExA +GetClassInfoExW +GetClassInfoW +GetClassLongA +GetClassLongW +GetClassNameA +GetClassNameW +GetClassWord +GetClientRect +GetClipCursor +GetClipboardData +GetClipboardFormatNameA +GetClipboardFormatNameW +GetClipboardOwner +GetClipboardSequenceNumber +GetClipboardViewer +GetComboBoxInfo +GetCursor +GetCursorInfo +GetCursorPos +GetDC +GetDCEx +GetDesktopWindow +GetDialogBaseUnits +GetDlgCtrlID +GetDlgItem +GetDlgItemInt +GetDlgItemTextA +GetDlgItemTextW +GetDoubleClickTime +GetFocus +GetForegroundWindow +GetGUIThreadInfo +GetGuiResources +GetIconInfo +GetInputDesktop +GetInputState +GetInternalWindowPos +GetKBCodePage +GetKeyNameTextA +GetKeyNameTextW +GetKeyState +GetKeyboardLayout +GetKeyboardLayoutList +GetKeyboardLayoutNameA +GetKeyboardLayoutNameW +GetKeyboardState +GetKeyboardType +GetLastActivePopup +GetListBoxInfo +GetMenu +GetMenuBarInfo +GetMenuCheckMarkDimensions +GetMenuContextHelpId +GetMenuDefaultItem +GetMenuInfo +GetMenuItemCount +GetMenuItemID +GetMenuItemInfoA +GetMenuItemInfoW +GetMenuItemRect +GetMenuState +GetMenuStringA +GetMenuStringW +GetMessageA +GetMessageExtraInfo +GetMessagePos +GetMessageTime +GetMessageW +GetMonitorInfoA +GetMonitorInfoW +GetMouseMovePoints +GetMouseMovePointsEx +GetNextDlgGroupItem +GetNextDlgTabItem +GetNextQueueWindow +GetOpenClipboardWindow +GetParent +GetPriorityClipboardFormat +GetProcessDefaultLayout +GetProcessWindowStation +GetPropA +GetPropW +GetQueueStatus +GetScrollBarInfo +GetScrollInfo +GetScrollPos +GetScrollRange +GetShellWindow +GetSubMenu +GetSysColor +GetSysColorBrush +GetSystemMenu +GetSystemMetrics +GetTabbedTextExtentA +GetTabbedTextExtentW +GetThreadDesktop +GetTitleBarInfo +GetTopWindow +GetUpdateRect +GetUpdateRgn +GetUserObjectInformationA +GetUserObjectInformationW +GetUserObjectSecurity +GetWindow +GetWindowContextHelpId +GetWindowDC +GetWindowInfo +GetWindowLongPtrA +GetWindowLongPtrW +SetWindowLongPtrA +SetWindowLongPtrW +GetWindowLongA +GetWindowLongW +GetWindowModuleFileNameA +GetWindowModuleFileNameW +GetWindowPlacement +GetWindowRect +GetWindowRgn +GetWindowTextA +GetWindowTextLengthA +GetWindowTextLengthW +GetWindowTextW +GetWindowThreadProcessId +GetWindowWord +GrayStringA +GrayStringW +HasSystemSleepStarted +HideCaret +HiliteMenuItem +IMPGetIMEA +IMPGetIMEW +IMPQueryIMEA +IMPQueryIMEW +IMPSetIMEA +IMPSetIMEW +ImpersonateDdeClientWindow +InSendMessage +InSendMessageEx +InflateRect +InitSharedTable +InitTask +InsertMenuA +InsertMenuItemA +InsertMenuItemW +InsertMenuW +InternalGetWindowText +IntersectRect +InvalidateRect +InvalidateRgn +InvertRect +IsCharAlphaA +IsCharAlphaNumericA +IsCharAlphaNumericW +IsCharAlphaW +IsCharLowerA +IsCharLowerW +IsCharUpperA +IsCharUpperW +IsChild +IsClipboardFormatAvailable +IsDialogMessage +IsDialogMessageA +IsDialogMessageW +IsDlgButtonChecked +IsHungThread +IsIconic +IsMenu +IsRectEmpty +IsWindow +IsWindowEnabled +IsWindowUnicode +IsWindowVisible +IsZoomed +KillTimer +LoadAcceleratorsA +LoadAcceleratorsW +LoadBitmapA +LoadBitmapW +LoadCursorA +LoadCursorFromFileA +LoadCursorFromFileW +LoadCursorW +LoadIconA +LoadIconW +LoadImageA +LoadImageW +LoadKeyboardLayoutA +LoadKeyboardLayoutW +LoadMenuA +LoadMenuIndirectA +LoadMenuIndirectW +LoadMenuW +LoadStringA +LoadStringW +LockSetForegroundWindow +LockWindowStation +LockWindowUpdate +LookupIconIdFromDirectory +LookupIconIdFromDirectoryEx +MapDialogRect +MapVirtualKeyA +MapVirtualKeyExA +MapVirtualKeyExW +MapVirtualKeyW +MapWindowPoints +MenuItemFromPoint +MessageBeep +MessageBoxA +MessageBoxExA +MessageBoxExW +MessageBoxIndirectA +MessageBoxIndirectW +MessageBoxW +ModifyAccess +ModifyMenuA +ModifyMenuW +MonitorFromPoint +MonitorFromRect +MonitorFromWindow +MoveWindow +MsgWaitForMultipleObjects +MsgWaitForMultipleObjectsEx +NotifyWinEvent +OemKeyScan +OemToCharA +OemToCharBuffA +OemToCharBuffW +OemToCharW +OffsetRect +OpenClipboard +OpenDesktopA +OpenDesktopW +OpenIcon +OpenInputDesktop +OpenWindowStationA +OpenWindowStationW +PackDDElParam +PaintDesktop +PeekMessageA +PeekMessageW +PlaySoundEvent +PostMessageA +PostMessageW +PostQuitMessage +PostThreadMessageA +PostThreadMessageW +PtInRect +RealChildWindowFromPoint +RealGetWindowClass +RedrawWindow +RegisterClassA +RegisterClassExA +RegisterClassExW +RegisterClassW +RegisterClipboardFormatA +RegisterClipboardFormatW +RegisterDeviceNotificationA +RegisterDeviceNotificationW +RegisterHotKey +RegisterLogonProcess +RegisterNetworkCapabilities +RegisterSystemThread +RegisterTasklist +RegisterWindowMessageA +RegisterWindowMessageW +ReleaseCapture +ReleaseDC +RemoveMenu +RemovePropA +RemovePropW +ReplyMessage +ReuseDDElParam +ScreenToClient +ScrollDC +ScrollWindow +ScrollWindowEx +SendDlgItemMessageA +SendDlgItemMessageW +SendIMEMessageExA +SendIMEMessageExW +SendInput +SendMessageA +SendMessageCallbackA +SendMessageCallbackW +SendMessageTimeoutA +SendMessageTimeoutW +SendMessageW +SendNotifyMessageA +SendNotifyMessageW +SetActiveWindow +SetCapture +SetCaretBlinkTime +SetCaretPos +SetClassLongA +SetClassLongW +SetClassWord +SetClipboardData +SetClipboardViewer +SetCursor +SetCursorPos +SetDebugErrorLevel +SetDeskWallpaper +SetDesktopBitmap +SetDlgItemInt +SetDlgItemTextA +SetDlgItemTextW +SetDoubleClickTime +SetFocus +SetForegroundWindow +SetInternalWindowPos +SetKeyboardState +SetLastErrorEx +SetLogonNotifyWindow +SetMenu +SetMenuContextHelpId +SetMenuDefaultItem +SetMenuInfo +SetMenuItemBitmaps +SetMenuItemInfoA +SetMenuItemInfoW +SetMessageExtraInfo +SetMessageQueue +SetParent +SetProcessDefaultLayout +SetProcessWindowStation +SetPropA +SetPropW +SetRect +SetRectEmpty +SetScrollInfo +SetScrollPos +SetScrollRange +SetShellWindow +SetSysColors +SetSysColorsTemp +SetSystemCursor +SetThreadDesktop +SetTimer +SetUserObjectInformationA +SetUserObjectInformationW +SetUserObjectSecurity +SetWinEventHook +SetWindowContextHelpId +SetWindowFullScreenState +SetWindowLongA +SetWindowLongW +SetWindowPlacement +SetWindowPos +SetWindowRgn +SetWindowTextA +SetWindowTextW +SetWindowWord +SetWindowsHookA +SetWindowsHookExA +SetWindowsHookExW +SetWindowsHookW +ShowCaret +ShowCursor +ShowOwnedPopups +ShowScrollBar +ShowWindow +ShowWindowAsync +SubtractRect +SwapMouseButton +SwitchDesktop +SwitchToThisWindow +SysErrorBox +SystemParametersInfoA +SystemParametersInfoW +TabbedTextOutA +TabbedTextOutW +TileChildWindows +TileWindows +ToAscii +ToAsciiEx +ToUnicode +ToUnicodeEx +TrackMouseEvent +TrackPopupMenu +TrackPopupMenuEx +TranslateAccelerator +TranslateAcceleratorA +TranslateAcceleratorW +TranslateMDISysAccel +TranslateMessage +UnhookWinEvent +UnhookWindowsHook +UnhookWindowsHookEx +UnionRect +UnloadKeyboardLayout +UnlockWindowStation +UnpackDDElParam +UnregisterClassA +UnregisterClassW +UnregisterDeviceNotification +UnregisterHotKey +UpdateWindow +UserClientDllInitialize +UserIsSystemResumeAutomatic +UserSetDeviceHoldState +UserSignalProc +UserTickleTimer +ValidateRect +ValidateRgn +VkKeyScanA +VkKeyScanExA +VkKeyScanExW +VkKeyScanW +WINNLSEnableIME +WINNLSGetEnableStatus +WINNLSGetIMEHotkey +WNDPROC_CALLBACK +WaitForInputIdle +WaitMessage +WinHelpA +WinHelpW +WinOldAppHackoMatic +WindowFromDC +WindowFromPoint +YieldTask +_SetProcessDefaultLayout +keybd_event +mouse_event +wsprintfA +wsprintfW +wvsprintfA +wvsprintfW diff --git a/ext/tcc-0.9.27/libtcc/libtcc.def b/ext/tcc-0.9.27/libtcc/libtcc.def new file mode 100644 index 00000000..7f391f99 --- /dev/null +++ b/ext/tcc-0.9.27/libtcc/libtcc.def @@ -0,0 +1,35 @@ +EXPORTS +tcc_add_file +tcc_add_include_path +tcc_add_library +tcc_add_library_err +tcc_add_library_path +tcc_add_symbol +tcc_add_sysinclude_path +tcc_basename +tcc_compile_string +tcc_define_symbol +tcc_delete +tcc_error +tcc_error_noabort +tcc_fileextension +tcc_free +tcc_get_dllexports +tcc_get_symbol +tcc_malloc +tcc_mallocz +tcc_memcheck +tcc_new +tcc_output_file +tcc_parse_args +tcc_print_stats +tcc_realloc +tcc_relocate +tcc_run +tcc_set_error_func +tcc_set_lib_path +tcc_set_options +tcc_set_output_type +tcc_strdup +tcc_undefine_symbol +tcc_warning diff --git a/ext/tcc-0.9.27/libtcc/libtcc.h b/ext/tcc-0.9.27/libtcc/libtcc.h new file mode 100644 index 00000000..a1b31e30 --- /dev/null +++ b/ext/tcc-0.9.27/libtcc/libtcc.h @@ -0,0 +1,100 @@ +#ifndef LIBTCC_H +#define LIBTCC_H + +#ifndef LIBTCCAPI +# define LIBTCCAPI +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct TCCState; + +typedef struct TCCState TCCState; + +/* create a new TCC compilation context */ +LIBTCCAPI TCCState *tcc_new(void); + +/* free a TCC compilation context */ +LIBTCCAPI void tcc_delete(TCCState *s); + +/* set CONFIG_TCCDIR at runtime */ +LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path); + +/* set error/warning display callback */ +LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque, + void (*error_func)(void *opaque, const char *msg)); + +/* set options as from command line (multiple supported) */ +LIBTCCAPI void tcc_set_options(TCCState *s, const char *str); + +/*****************************/ +/* preprocessor */ + +/* add include path */ +LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname); + +/* add in system include path */ +LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname); + +/* define preprocessor symbol 'sym'. Can put optional value */ +LIBTCCAPI void tcc_define_symbol(TCCState *s, const char *sym, const char *value); + +/* undefine preprocess symbol 'sym' */ +LIBTCCAPI void tcc_undefine_symbol(TCCState *s, const char *sym); + +/*****************************/ +/* compiling */ + +/* add a file (C file, dll, object, library, ld script). Return -1 if error. */ +LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename); + +/* compile a string containing a C source. Return -1 if error. */ +LIBTCCAPI int tcc_compile_string(TCCState *s, const char *buf); + +/*****************************/ +/* linking commands */ + +/* set output type. MUST BE CALLED before any compilation */ +LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type); +#define TCC_OUTPUT_MEMORY 1 /* output will be run in memory (default) */ +#define TCC_OUTPUT_EXE 2 /* executable file */ +#define TCC_OUTPUT_DLL 3 /* dynamic library */ +#define TCC_OUTPUT_OBJ 4 /* object file */ +#define TCC_OUTPUT_PREPROCESS 5 /* only preprocess (used internally) */ + +/* equivalent to -Lpath option */ +LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname); + +/* the library name is the same as the argument of the '-l' option */ +LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname); + +/* add a symbol to the compiled program */ +LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val); + +/* output an executable, library or object file. DO NOT call + tcc_relocate() before. */ +LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename); + +/* link and run main() function and return its value. DO NOT call + tcc_relocate() before. */ +LIBTCCAPI int tcc_run(TCCState *s, int argc, char **argv); + +/* do all relocations (needed before using tcc_get_symbol()) */ +LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr); +/* possible values for 'ptr': + - TCC_RELOCATE_AUTO : Allocate and manage memory internally + - NULL : return required memory size for the step below + - memory address : copy code to memory passed by the caller + returns -1 if error. */ +#define TCC_RELOCATE_AUTO (void*)1 + +/* return symbol value or NULL if not found */ +LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext/tcc-0.9.27/libtcc/libtcc.lib b/ext/tcc-0.9.27/libtcc/libtcc.lib new file mode 100644 index 00000000..d541d9c3 Binary files /dev/null and b/ext/tcc-0.9.27/libtcc/libtcc.lib differ diff --git a/ext/tinydir.h b/ext/tinydir.h new file mode 100644 index 00000000..324f9f69 --- /dev/null +++ b/ext/tinydir.h @@ -0,0 +1,816 @@ +/* +Copyright (c) 2013-2018, tinydir authors: +- Cong Xu +- Lautis Sun +- Baudouin Feildel +- Andargor +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef TINYDIR_H +#define TINYDIR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if ((defined _UNICODE) && !(defined UNICODE)) +#define UNICODE +#endif + +#if ((defined UNICODE) && !(defined _UNICODE)) +#define _UNICODE +#endif + +#include +#include +#include +#ifdef _MSC_VER +# define WIN32_LEAN_AND_MEAN +# include +# include +# pragma warning(push) +# pragma warning (disable : 4996) +#else +# include +# include +# include +# include +#endif +#ifdef __MINGW32__ +# include +#endif + + +/* types */ + +/* Windows UNICODE wide character support */ +#if defined _MSC_VER || defined __MINGW32__ +# define _tinydir_char_t TCHAR +# define TINYDIR_STRING(s) _TEXT(s) +# define _tinydir_strlen _tcslen +# define _tinydir_strcpy _tcscpy +# define _tinydir_strcat _tcscat +# define _tinydir_strcmp _tcscmp +# define _tinydir_strrchr _tcsrchr +# define _tinydir_strncmp _tcsncmp +#else +# define _tinydir_char_t char +# define TINYDIR_STRING(s) s +# define _tinydir_strlen strlen +# define _tinydir_strcpy strcpy +# define _tinydir_strcat strcat +# define _tinydir_strcmp strcmp +# define _tinydir_strrchr strrchr +# define _tinydir_strncmp strncmp +#endif + +#if (defined _MSC_VER || defined __MINGW32__) +# include +# define _TINYDIR_PATH_MAX MAX_PATH +#elif defined __linux__ +# include +# define _TINYDIR_PATH_MAX PATH_MAX +#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +# include +# if defined(BSD) +# include +# define _TINYDIR_PATH_MAX PATH_MAX +# endif +#endif + +#ifndef _TINYDIR_PATH_MAX +#define _TINYDIR_PATH_MAX 4096 +#endif + +#ifdef _MSC_VER +/* extra chars for the "\\*" mask */ +# define _TINYDIR_PATH_EXTRA 2 +#else +# define _TINYDIR_PATH_EXTRA 0 +#endif + +#define _TINYDIR_FILENAME_MAX 256 + +#if (defined _MSC_VER || defined __MINGW32__) +#define _TINYDIR_DRIVE_MAX 3 +#endif + +#ifdef _MSC_VER +# define _TINYDIR_FUNC static __inline +#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L +# define _TINYDIR_FUNC static __inline__ +#else +# define _TINYDIR_FUNC static inline +#endif + +/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */ +#ifdef TINYDIR_USE_READDIR_R + +/* readdir_r is a POSIX-only function, and may not be available under various + * environments/settings, e.g. MinGW. Use readdir fallback */ +#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\ + _POSIX_SOURCE +# define _TINYDIR_HAS_READDIR_R +#endif +#if _POSIX_C_SOURCE >= 200112L +# define _TINYDIR_HAS_FPATHCONF +# include +#endif +#if _BSD_SOURCE || _SVID_SOURCE || \ + (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700) +# define _TINYDIR_HAS_DIRFD +# include +#endif +#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\ + defined _PC_NAME_MAX +# define _TINYDIR_USE_FPATHCONF +#endif +#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\ + !(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX) +# define _TINYDIR_USE_READDIR +#endif + +/* Use readdir by default */ +#else +# define _TINYDIR_USE_READDIR +#endif + +/* MINGW32 has two versions of dirent, ASCII and UNICODE*/ +#ifndef _MSC_VER +#if (defined __MINGW32__) && (defined _UNICODE) +#define _TINYDIR_DIR _WDIR +#define _tinydir_dirent _wdirent +#define _tinydir_opendir _wopendir +#define _tinydir_readdir _wreaddir +#define _tinydir_closedir _wclosedir +#else +#define _TINYDIR_DIR DIR +#define _tinydir_dirent dirent +#define _tinydir_opendir opendir +#define _tinydir_readdir readdir +#define _tinydir_closedir closedir +#endif +#endif + +/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */ +#if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE) +#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE) +#else +#error "Either define both alloc and free or none of them!" +#endif + +#if !defined(_TINYDIR_MALLOC) + #define _TINYDIR_MALLOC(_size) malloc(_size) + #define _TINYDIR_FREE(_ptr) free(_ptr) +#endif /* !defined(_TINYDIR_MALLOC) */ + +typedef struct tinydir_file +{ + _tinydir_char_t path[_TINYDIR_PATH_MAX]; + _tinydir_char_t name[_TINYDIR_FILENAME_MAX]; + _tinydir_char_t *extension; + int is_dir; + int is_reg; + +#ifndef _MSC_VER +#ifdef __MINGW32__ + struct _stat _s; +#else + struct stat _s; +#endif +#endif +} tinydir_file; + +typedef struct tinydir_dir +{ + _tinydir_char_t path[_TINYDIR_PATH_MAX]; + int has_next; + size_t n_files; + + tinydir_file *_files; +#ifdef _MSC_VER + HANDLE _h; + WIN32_FIND_DATA _f; +#else + _TINYDIR_DIR *_d; + struct _tinydir_dirent *_e; +#ifndef _TINYDIR_USE_READDIR + struct _tinydir_dirent *_ep; +#endif +#endif +} tinydir_dir; + + +/* declarations */ + +_TINYDIR_FUNC +int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path); +_TINYDIR_FUNC +int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path); +_TINYDIR_FUNC +void tinydir_close(tinydir_dir *dir); + +_TINYDIR_FUNC +int tinydir_next(tinydir_dir *dir); +_TINYDIR_FUNC +int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file); +_TINYDIR_FUNC +int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i); +_TINYDIR_FUNC +int tinydir_open_subdir_n(tinydir_dir *dir, size_t i); + +_TINYDIR_FUNC +int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path); +_TINYDIR_FUNC +void _tinydir_get_ext(tinydir_file *file); +_TINYDIR_FUNC +int _tinydir_file_cmp(const void *a, const void *b); +#ifndef _MSC_VER +#ifndef _TINYDIR_USE_READDIR +_TINYDIR_FUNC +size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp); +#endif +#endif + + +/* definitions*/ + +_TINYDIR_FUNC +int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path) +{ +#ifndef _MSC_VER +#ifndef _TINYDIR_USE_READDIR + int error; + int size; /* using int size */ +#endif +#else + _tinydir_char_t path_buf[_TINYDIR_PATH_MAX]; +#endif + _tinydir_char_t *pathp; + + if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0) + { + errno = EINVAL; + return -1; + } + if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) + { + errno = ENAMETOOLONG; + return -1; + } + + /* initialise dir */ + dir->_files = NULL; +#ifdef _MSC_VER + dir->_h = INVALID_HANDLE_VALUE; +#else + dir->_d = NULL; +#ifndef _TINYDIR_USE_READDIR + dir->_ep = NULL; +#endif +#endif + tinydir_close(dir); + + _tinydir_strcpy(dir->path, path); + /* Remove trailing slashes */ + pathp = &dir->path[_tinydir_strlen(dir->path) - 1]; + while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/'))) + { + *pathp = TINYDIR_STRING('\0'); + pathp++; + } +#ifdef _MSC_VER + _tinydir_strcpy(path_buf, dir->path); + _tinydir_strcat(path_buf, TINYDIR_STRING("\\*")); +#if (defined WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP) + dir->_h = FindFirstFileEx(path_buf, FindExInfoStandard, &dir->_f, FindExSearchNameMatch, NULL, 0); +#else + dir->_h = FindFirstFile(path_buf, &dir->_f); +#endif + if (dir->_h == INVALID_HANDLE_VALUE) + { + errno = ENOENT; +#else + dir->_d = _tinydir_opendir(path); + if (dir->_d == NULL) + { +#endif + goto bail; + } + + /* read first file */ + dir->has_next = 1; +#ifndef _MSC_VER +#ifdef _TINYDIR_USE_READDIR + dir->_e = _tinydir_readdir(dir->_d); +#else + /* allocate dirent buffer for readdir_r */ + size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */ + if (size == -1) return -1; + dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size); + if (dir->_ep == NULL) return -1; + + error = readdir_r(dir->_d, dir->_ep, &dir->_e); + if (error != 0) return -1; +#endif + if (dir->_e == NULL) + { + dir->has_next = 0; + } +#endif + + return 0; + +bail: + tinydir_close(dir); + return -1; +} + +_TINYDIR_FUNC +int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path) +{ + /* Count the number of files first, to pre-allocate the files array */ + size_t n_files = 0; + if (tinydir_open(dir, path) == -1) + { + return -1; + } + while (dir->has_next) + { + n_files++; + if (tinydir_next(dir) == -1) + { + goto bail; + } + } + tinydir_close(dir); + + if (n_files == 0 || tinydir_open(dir, path) == -1) + { + return -1; + } + + dir->n_files = 0; + dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files); + if (dir->_files == NULL) + { + goto bail; + } + while (dir->has_next) + { + tinydir_file *p_file; + dir->n_files++; + + p_file = &dir->_files[dir->n_files - 1]; + if (tinydir_readfile(dir, p_file) == -1) + { + goto bail; + } + + if (tinydir_next(dir) == -1) + { + goto bail; + } + + /* Just in case the number of files has changed between the first and + second reads, terminate without writing into unallocated memory */ + if (dir->n_files == n_files) + { + break; + } + } + + qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp); + + return 0; + +bail: + tinydir_close(dir); + return -1; +} + +_TINYDIR_FUNC +void tinydir_close(tinydir_dir *dir) +{ + if (dir == NULL) + { + return; + } + + memset(dir->path, 0, sizeof(dir->path)); + dir->has_next = 0; + dir->n_files = 0; + _TINYDIR_FREE(dir->_files); + dir->_files = NULL; +#ifdef _MSC_VER + if (dir->_h != INVALID_HANDLE_VALUE) + { + FindClose(dir->_h); + } + dir->_h = INVALID_HANDLE_VALUE; +#else + if (dir->_d) + { + _tinydir_closedir(dir->_d); + } + dir->_d = NULL; + dir->_e = NULL; +#ifndef _TINYDIR_USE_READDIR + _TINYDIR_FREE(dir->_ep); + dir->_ep = NULL; +#endif +#endif +} + +_TINYDIR_FUNC +int tinydir_next(tinydir_dir *dir) +{ + if (dir == NULL) + { + errno = EINVAL; + return -1; + } + if (!dir->has_next) + { + errno = ENOENT; + return -1; + } + +#ifdef _MSC_VER + if (FindNextFile(dir->_h, &dir->_f) == 0) +#else +#ifdef _TINYDIR_USE_READDIR + dir->_e = _tinydir_readdir(dir->_d); +#else + if (dir->_ep == NULL) + { + return -1; + } + if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0) + { + return -1; + } +#endif + if (dir->_e == NULL) +#endif + { + dir->has_next = 0; +#ifdef _MSC_VER + if (GetLastError() != ERROR_SUCCESS && + GetLastError() != ERROR_NO_MORE_FILES) + { + tinydir_close(dir); + errno = EIO; + return -1; + } +#endif + } + + return 0; +} + +_TINYDIR_FUNC +int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file) +{ + if (dir == NULL || file == NULL) + { + errno = EINVAL; + return -1; + } +#ifdef _MSC_VER + if (dir->_h == INVALID_HANDLE_VALUE) +#else + if (dir->_e == NULL) +#endif + { + errno = ENOENT; + return -1; + } + if (_tinydir_strlen(dir->path) + + _tinydir_strlen( +#ifdef _MSC_VER + dir->_f.cFileName +#else + dir->_e->d_name +#endif + ) + 1 + _TINYDIR_PATH_EXTRA >= + _TINYDIR_PATH_MAX) + { + /* the path for the file will be too long */ + errno = ENAMETOOLONG; + return -1; + } + if (_tinydir_strlen( +#ifdef _MSC_VER + dir->_f.cFileName +#else + dir->_e->d_name +#endif + ) >= _TINYDIR_FILENAME_MAX) + { + errno = ENAMETOOLONG; + return -1; + } + + _tinydir_strcpy(file->path, dir->path); + _tinydir_strcat(file->path, TINYDIR_STRING("/")); + _tinydir_strcpy(file->name, +#ifdef _MSC_VER + dir->_f.cFileName +#else + dir->_e->d_name +#endif + ); + _tinydir_strcat(file->path, file->name); +#ifndef _MSC_VER +#ifdef __MINGW32__ + if (_tstat( +#else + if (stat( +#endif + file->path, &file->_s) == -1) + { + return -1; + } +#endif + _tinydir_get_ext(file); + + file->is_dir = +#ifdef _MSC_VER + !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); +#else + S_ISDIR(file->_s.st_mode); +#endif + file->is_reg = +#ifdef _MSC_VER + !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || + ( + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) && + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) && +#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) && +#endif +#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) && +#endif + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) && + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY)); +#else + S_ISREG(file->_s.st_mode); +#endif + + return 0; +} + +_TINYDIR_FUNC +int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i) +{ + if (dir == NULL || file == NULL) + { + errno = EINVAL; + return -1; + } + if (i >= dir->n_files) + { + errno = ENOENT; + return -1; + } + + memcpy(file, &dir->_files[i], sizeof(tinydir_file)); + _tinydir_get_ext(file); + + return 0; +} + +_TINYDIR_FUNC +int tinydir_open_subdir_n(tinydir_dir *dir, size_t i) +{ + _tinydir_char_t path[_TINYDIR_PATH_MAX]; + if (dir == NULL) + { + errno = EINVAL; + return -1; + } + if (i >= dir->n_files || !dir->_files[i].is_dir) + { + errno = ENOENT; + return -1; + } + + _tinydir_strcpy(path, dir->_files[i].path); + tinydir_close(dir); + if (tinydir_open_sorted(dir, path) == -1) + { + return -1; + } + + return 0; +} + +/* Open a single file given its path */ +_TINYDIR_FUNC +int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path) +{ + tinydir_dir dir; + int result = 0; + int found = 0; + _tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX]; + _tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX]; + _tinydir_char_t *dir_name; + _tinydir_char_t *base_name; +#if (defined _MSC_VER || defined __MINGW32__) + _tinydir_char_t drive_buf[_TINYDIR_PATH_MAX]; + _tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX]; +#endif + + if (file == NULL || path == NULL || _tinydir_strlen(path) == 0) + { + errno = EINVAL; + return -1; + } + if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) + { + errno = ENAMETOOLONG; + return -1; + } + + /* Get the parent path */ +#if (defined _MSC_VER || defined __MINGW32__) +#if ((defined _MSC_VER) && (_MSC_VER >= 1400)) + errno = _tsplitpath_s( + path, + drive_buf, _TINYDIR_DRIVE_MAX, + dir_name_buf, _TINYDIR_FILENAME_MAX, + file_name_buf, _TINYDIR_FILENAME_MAX, + ext_buf, _TINYDIR_FILENAME_MAX); +#else + _tsplitpath( + path, + drive_buf, + dir_name_buf, + file_name_buf, + ext_buf); +#endif + +if (errno) +{ + return -1; +} + +/* _splitpath_s not work fine with only filename and widechar support */ +#ifdef _UNICODE + if (drive_buf[0] == L'\xFEFE') + drive_buf[0] = '\0'; + if (dir_name_buf[0] == L'\xFEFE') + dir_name_buf[0] = '\0'; +#endif + + /* Emulate the behavior of dirname by returning "." for dir name if it's + empty */ + if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0') + { + _tinydir_strcpy(dir_name_buf, TINYDIR_STRING(".")); + } + /* Concatenate the drive letter and dir name to form full dir name */ + _tinydir_strcat(drive_buf, dir_name_buf); + dir_name = drive_buf; + /* Concatenate the file name and extension to form base name */ + _tinydir_strcat(file_name_buf, ext_buf); + base_name = file_name_buf; +#else + _tinydir_strcpy(dir_name_buf, path); + dir_name = dirname(dir_name_buf); + _tinydir_strcpy(file_name_buf, path); + base_name =basename(file_name_buf); +#endif + + /* Open the parent directory */ + if (tinydir_open(&dir, dir_name) == -1) + { + return -1; + } + + /* Read through the parent directory and look for the file */ + while (dir.has_next) + { + if (tinydir_readfile(&dir, file) == -1) + { + result = -1; + goto bail; + } + if (_tinydir_strcmp(file->name, base_name) == 0) + { + /* File found */ + found = 1; + break; + } + tinydir_next(&dir); + } + if (!found) + { + result = -1; + errno = ENOENT; + } + +bail: + tinydir_close(&dir); + return result; +} + +_TINYDIR_FUNC +void _tinydir_get_ext(tinydir_file *file) +{ + _tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.')); + if (period == NULL) + { + file->extension = &(file->name[_tinydir_strlen(file->name)]); + } + else + { + file->extension = period + 1; + } +} + +_TINYDIR_FUNC +int _tinydir_file_cmp(const void *a, const void *b) +{ + const tinydir_file *fa = (const tinydir_file *)a; + const tinydir_file *fb = (const tinydir_file *)b; + if (fa->is_dir != fb->is_dir) + { + return -(fa->is_dir - fb->is_dir); + } + return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX); +} + +#ifndef _MSC_VER +#ifndef _TINYDIR_USE_READDIR +/* +The following authored by Ben Hutchings +from https://womble.decadent.org.uk/readdir_r-advisory.html +*/ +/* Calculate the required buffer size (in bytes) for directory * +* entries read from the given directory handle. Return -1 if this * +* this cannot be done. * +* * +* This code does not trust values of NAME_MAX that are less than * +* 255, since some systems (including at least HP-UX) incorrectly * +* define it to be a smaller value. */ +_TINYDIR_FUNC +size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp) +{ + long name_max; + size_t name_end; + /* parameter may be unused */ + (void)dirp; + +#if defined _TINYDIR_USE_FPATHCONF + name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX); + if (name_max == -1) +#if defined(NAME_MAX) + name_max = (NAME_MAX > 255) ? NAME_MAX : 255; +#else + return (size_t)(-1); +#endif +#elif defined(NAME_MAX) + name_max = (NAME_MAX > 255) ? NAME_MAX : 255; +#else +#error "buffer size for readdir_r cannot be determined" +#endif + name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1; + return (name_end > sizeof(struct _tinydir_dirent) ? + name_end : sizeof(struct _tinydir_dirent)); +} +#endif +#endif + +#ifdef __cplusplus +} +#endif + +# if defined (_MSC_VER) +# pragma warning(pop) +# endif + +#endif diff --git a/src/Evaluation.cpp b/src/Evaluation.cpp index 4e490cc5..e495be14 100644 --- a/src/Evaluation.cpp +++ b/src/Evaluation.cpp @@ -1,473 +1,250 @@ -#include // Initialize with gl3wInit() +// https://github.com/CedricGuillemet/Imogen +// +// The MIT License(MIT) +// +// Copyright(c) 2018 Cedric Guillemet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + #include "Evaluation.h" #include #include -#include -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "stb_image_write.h" -#include "hdrloader.h" #include -#include -#include - -extern int Log(const char *szFormat, ...); -static const int SemUV0 = 0; -static const unsigned int wrap[] = { GL_REPEAT, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER, GL_MIRRORED_REPEAT }; -static const unsigned int filter[] = { GL_LINEAR, GL_NEAREST }; -inline void TexParam(TextureID MinFilter, TextureID MagFilter, TextureID WrapS, TextureID WrapT, TextureID texMode) -{ - glTexParameteri(texMode, GL_TEXTURE_MIN_FILTER, MinFilter); - glTexParameteri(texMode, GL_TEXTURE_MAG_FILTER, MagFilter); - glTexParameteri(texMode, GL_TEXTURE_WRAP_S, WrapS); - glTexParameteri(texMode, GL_TEXTURE_WRAP_T, WrapT); -} - -void RenderTarget::bindAsTarget() const -{ - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glViewport(0, 0, mWidth, mHeight); -} -void RenderTarget::destroy() +std::string Evaluation::GetEvaluator(const std::string& filename) { - if (mGLTexID) - glDeleteTextures(1, &mGLTexID); - if (fbo) - glDeleteFramebuffers(1, &fbo); - if (depthbuffer) - glDeleteRenderbuffers(1, &depthbuffer); - fbo = depthbuffer = 0; - mWidth = mHeight = 0; - mGLTexID = 0; + return mEvaluatorScripts[filename].mText; } -void RenderTarget::clear() +Evaluation::Evaluation() : mDirtyCount(0), mEvaluationMode(-1) { - glClear(GL_COLOR_BUFFER_BIT | (depthbuffer ? GL_DEPTH_BUFFER_BIT : 0)); + } -void RenderTarget::initBuffer(int width, int height, bool hasZBuffer) +void Evaluation::Init() { - if ((width == mWidth) && (mHeight == height) && (!(hasZBuffer ^ (depthbuffer != 0)))) - return; - destroy(); - - mWidth = width; - mHeight = height; - - glGenFramebuffers(1, &fbo); - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - - // diffuse - glGenTextures(1, &mGLTexID); - glBindTexture(GL_TEXTURE_2D, mGLTexID); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - TexParam(GL_NEAREST, GL_NEAREST, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_TEXTURE_2D); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mGLTexID, 0); - /* - if (hasZBuffer) - { - // Z - glGenTextures(1, &mGLTexID2); - glBindTexture(GL_TEXTURE_2D, mGLTexID2); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); - TexParam(GL_NEAREST, GL_NEAREST, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_TEXTURE_2D); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, mGLTexID2, 0); - } - */ - static const GLenum DrawBuffers[] = { GL_COLOR_ATTACHMENT0 }; - glDrawBuffers(sizeof(DrawBuffers) / sizeof(GLenum), DrawBuffers); - - checkFBO(); + APIInit(); } -void RenderTarget::checkFBO() +void Evaluation::Finish() { - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - - int status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - switch (status) - { - case GL_FRAMEBUFFER_COMPLETE: - //Log("Framebuffer complete.\n"); - break; - - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - Log("[ERROR] Framebuffer incomplete: Attachment is NOT complete."); - break; - - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - Log("[ERROR] Framebuffer incomplete: No image is attached to FBO."); - break; -/* - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: - Log("[ERROR] Framebuffer incomplete: Attached images have different dimensions."); - break; - - case GL_FRAMEBUFFER_INCOMPLETE_FORMATS: - Log("[ERROR] Framebuffer incomplete: Color attached images have different internal formats."); - break; -*/ - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - Log("[ERROR] Framebuffer incomplete: Draw buffer.\n"); - break; - - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - Log("[ERROR] Framebuffer incomplete: Read buffer.\n"); - break; - - case GL_FRAMEBUFFER_UNSUPPORTED: - Log("[ERROR] Unsupported by FBO implementation.\n"); - break; - default: - Log("[ERROR] Unknow error.\n"); - break; - } - - glBindFramebuffer(GL_FRAMEBUFFER, 0); } -class FullScreenTriangle +size_t Evaluation::AddEvaluation(size_t nodeType, const std::string& nodeName) { -public: - FullScreenTriangle() : mGLFullScreenVertexArrayName(-1) + auto iter = mEvaluatorScripts.find(nodeName+".glsl"); + if (iter != mEvaluatorScripts.end()) { + EvaluationStage evaluation; + evaluation.mTarget = new RenderTarget; + mAllocatedRenderTargets.push_back(evaluation.mTarget); + evaluation.mUseCountByOthers = 0; + evaluation.mbDirty = true; + evaluation.mbForceEval = false; + evaluation.mNodeType = nodeType; + evaluation.mParametersBuffer = 0; + evaluation.mEvaluationType = 0; + iter->second.mNodeType = int(nodeType); + mEvaluatorPerNodeType[nodeType].mGLSLProgram = iter->second.mProgram; + mDirtyCount++; + mEvaluations.push_back(evaluation); + return mEvaluations.size() - 1; } - ~FullScreenTriangle() + iter = mEvaluatorScripts.find(nodeName + ".c"); + if (iter != mEvaluatorScripts.end()) { + EvaluationStage evaluation; + evaluation.mTarget = NULL; + evaluation.mUseCountByOthers = 0; + evaluation.mbDirty = true; + evaluation.mbForceEval = false; + evaluation.mNodeType = nodeType; + evaluation.mParametersBuffer = 0; + evaluation.mEvaluationType = 1; + iter->second.mNodeType = int(nodeType); + mEvaluatorPerNodeType[nodeType].mCFunction = iter->second.mCFunction; + mEvaluatorPerNodeType[nodeType].mMem = iter->second.mMem; + mDirtyCount++; + mEvaluations.push_back(evaluation); + return mEvaluations.size() - 1; } - void Init(); - void Render(); -protected: - TextureID mGLFullScreenVertexArrayName; -}; - -void FullScreenTriangle::Init() -{ - TextureID fsVA; - - float fsVts[] = { 0.f,0.f, 2.f,0.f, 0.f,2.f }; - glGenBuffers(1, &fsVA); - glBindBuffer(GL_ARRAY_BUFFER, fsVA); - glBufferData(GL_ARRAY_BUFFER, 3 * sizeof(float) * 2, fsVts, GL_STATIC_DRAW); - - glGenVertexArrays(1, &mGLFullScreenVertexArrayName); - glBindVertexArray(mGLFullScreenVertexArrayName); - glBindBuffer(GL_ARRAY_BUFFER, fsVA); - glVertexAttribPointer(SemUV0, 2, GL_FLOAT, GL_FALSE, 0, 0); - glEnableVertexAttribArray(SemUV0); - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + Log("Could not find node name \"%s\" \n", nodeName.c_str()); + return -1; } -void FullScreenTriangle::Render() -{ - glBindVertexArray(mGLFullScreenVertexArrayName); - glDrawArrays(GL_TRIANGLES, 0, 3); - glBindVertexArray(0); -} - -unsigned int LoadShader(const std::string &shaderString, const char *fileName) +void Evaluation::DelEvaluationTarget(size_t target) { - TextureID programObject = glCreateProgram(); - if (programObject == 0) - return 0; - - GLint compiled; - const char *shaderTypeStrings[] = { "\n#version 430 core\n#define VERTEX_SHADER\n", "\n#version 430 core\n#define FRAGMENT_SHADER\n" }; - TextureID shaderTypes[] = { GL_VERTEX_SHADER, GL_FRAGMENT_SHADER }; - TextureID compiledShader[2]; - - for (int i = 0; i<2; i++) - { - // Create the shader object - int shader = glCreateShader(shaderTypes[i]); - - if (shader == 0) - return false; - - int stringsCount = 2; - const char ** strings = (const char**)malloc(sizeof(char*) * stringsCount); //new const char*[stringsCount]; - int * stringLength = (int*)malloc(sizeof(int) * stringsCount); //new int[stringsCount]; - strings[0] = shaderTypeStrings[i]; - stringLength[0] = int(strlen(shaderTypeStrings[i])); - strings[stringsCount - 1] = shaderString.c_str(); - stringLength[stringsCount - 1] = int(shaderString.length()); - - // Load and compile the shader source - glShaderSource(shader, stringsCount, strings, stringLength); - glCompileShader(shader); - - - free(stringLength); - free(strings); - - // Check the compile status - glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); - if (compiled == 0) - { - GLint info_len = 0; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_len); - if (info_len > 1) - { - char* info_log = (char*)malloc(sizeof(char) * info_len); - glGetShaderInfoLog(shader, info_len, NULL, info_log); - Log("Error compiling shader: %s \n", fileName); - Log(info_log); - Log("\n"); - free(info_log); - } - glDeleteShader(shader); - return 0; - } - compiledShader[i] = shader; - } - - - - GLint linked; - - for (int i = 0; i<2; i++) - glAttachShader(programObject, compiledShader[i]); - - - // Link the program - glLinkProgram(programObject); - - glBindAttribLocation(programObject, SemUV0, "inUV"); + SetTargetDirty(target); + EvaluationStage& ev = mEvaluations[target]; + ev.Clear(); + mEvaluations.erase(mEvaluations.begin() + target); - // Check the link status - glGetProgramiv(programObject, GL_LINK_STATUS, &linked); - if (linked == 0) + // shift all connections + for (auto& evaluation : mEvaluations) { - GLint info_len = 0; - glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &info_len); - if (info_len > 1) + for (auto& inp : evaluation.mInput.mInputs) { - char* info_log = (char*)malloc(sizeof(char) * info_len); - glGetProgramInfoLog(programObject, info_len, NULL, info_log); - Log("Error linking program:\n"); - Log(info_log); - free(info_log); + if (inp >= int(target)) + inp--; } - glDeleteProgram(programObject); - return 0; } - - // Delete these here because they are attached to the program object. - for (int i = 0; i<2; i++) - glDeleteShader(compiledShader[i]); - - // attributes - return programObject; } -FullScreenTriangle mFSQuad; -static const char* samplerName[] = { "Sampler0", "Sampler1", "Sampler2", "Sampler3", "Sampler4", "Sampler5", "Sampler6", "Sampler7" }; - -std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) +unsigned int Evaluation::GetEvaluationTexture(size_t target) { - size_t start_pos = 0; - while ((start_pos = str.find(from, start_pos)) != std::string::npos) { - str.replace(start_pos, from.length(), to); - start_pos += to.length(); // Handles case where 'to' is a substring of 'from' - } - return str; + return mEvaluations[target].mTarget->mGLTexID; } - -void Evaluation::SetEvaluationGLSL(const std::vector& filenames) +void Evaluation::SetEvaluationParameters(size_t target, void *parameters, size_t parametersSize) { - // clear - for (auto& glslShader : mProgramPerNodeType) - { - if (glslShader) - glDeleteProgram(glslShader); - } + EvaluationStage& stage = mEvaluations[target]; + stage.mParameters = parameters; + stage.mParametersSize = parametersSize; - mProgramPerNodeType.clear(); - mProgramPerNodeType.resize(filenames.size(), 0); + if (stage.mEvaluationType == EVALUATOR_GLSL) + BindGLSLParameters(stage); - for (auto& filename : filenames) + SetTargetDirty(target); +} + +void Evaluation::PerformEvaluationForNode(size_t index, int width, int height, bool force) +{ + EvaluationStage& evaluation = mEvaluations[index]; + + if (force) { - std::ifstream t(std::string("GLSL/")+filename); - if (t.good()) - { - std::string str((std::istreambuf_iterator(t)), std::istreambuf_iterator()); - if (mGLSLs.find(filename) == mGLSLs.end()) - mGLSLs[filename] = { str, 0, -1 }; - else - mGLSLs[filename].mShaderText = str; - } + evaluation.mbForceEval = true; + SetTargetDirty(index); } - std::string baseShader = mGLSLs["Shader.glsl"].mShaderText; - for (auto& filename : filenames) + switch (evaluation.mEvaluationType) { - if (filename == "Shader.glsl") - continue; - - Shader& shader = mGLSLs[filename]; - std::string shaderText = ReplaceAll(baseShader, "__NODE__", shader.mShaderText); - std::string nodeName = ReplaceAll(filename, ".glsl", ""); - shaderText = ReplaceAll(shaderText, "__FUNCTION__", nodeName +"()"); - - unsigned int program = LoadShader(shaderText, filename.c_str()); - unsigned int parameterBlockIndex = glGetUniformBlockIndex(program, (nodeName +"Block").c_str()); - glUniformBlockBinding(program, parameterBlockIndex, 1); - shader.mProgram = program; - if (shader.mNodeType != -1) - mProgramPerNodeType[shader.mNodeType] = program; + case 0: // GLSL + EvaluateGLSL(evaluation); + break; + case 1: // C + EvaluateC(evaluation, index); + break; } -/* // refresh stages - for (auto& evaluation : mEvaluations) - { - evaluation.m - }*/ } -std::string Evaluation::GetEvaluationGLSL(const std::string& filename) +void Evaluation::SetEvaluationMemoryMode(int evaluationMode) { - return mGLSLs[filename].mShaderText; -} + if (evaluationMode == mEvaluationMode) + return; -Evaluation::Evaluation() : mDirtyCount(0) -{ - mFSQuad.Init(); -} + mEvaluationMode = evaluationMode; + // free previously allocated RT -size_t Evaluation::AddEvaluationTarget(size_t nodeType, const std::string& nodeName) -{ - auto iter = mGLSLs.find(nodeName+".glsl"); - if (iter == mGLSLs.end()) + for (auto* rt : mAllocatedRenderTargets) { - Log("Could not find node name \"%s\" \n", nodeName.c_str()); - return -1; + rt->destroy(); + delete rt; } - EvaluationStage evaluation; - evaluation.mTarget.initBuffer(256, 256, false); - evaluation.mbDirty = true; - evaluation.mNodeType = nodeType; - evaluation.mParametersBuffer = 0; - - iter->second.mNodeType = int(nodeType); - mProgramPerNodeType[nodeType] = iter->second.mProgram; - mDirtyCount++; - mEvaluations.push_back(evaluation); - return mEvaluations.size() - 1; -} - -void Evaluation::EvaluationStage::Clear() -{ - glDeleteBuffers(1, &mParametersBuffer); - mTarget.destroy(); -} - -void Evaluation::DelEvaluationTarget(size_t target) -{ - SetTargetDirty(target); - EvaluationStage& ev = mEvaluations[target]; - ev.Clear(); - mEvaluations.erase(mEvaluations.begin() + target); + mAllocatedRenderTargets.clear(); - // shift all connections - for (auto& evaluation : mEvaluations) + if (evaluationMode == 0) // edit mode { - for (auto& inp : evaluation.mInput.mInputs) + for (size_t i = 0; i < mEvaluations.size(); i++) { - if (inp >= int(target)) - inp--; + EvaluationStage& evaluation = mEvaluations[i]; + switch (evaluation.mEvaluationType) + { + case 0: // glsl + evaluation.mTarget = new RenderTarget; + mAllocatedRenderTargets.push_back(evaluation.mTarget); + break; + case 1: // C + evaluation.mTarget = 0; + break; + } } } -} + else // baking mode + { + std::vector freeRenderTargets; + std::vector useCount(mEvaluations.size(), 0); + for (size_t i = 0; i < mEvaluations.size(); i++) + { + useCount[i] = mEvaluations[i].mUseCountByOthers; + } -unsigned int Evaluation::GetEvaluationTexture(size_t target) -{ - return mEvaluations[target].mTarget.mGLTexID; -} + for (size_t i = 0; i < mEvaluationOrderList.size(); i++) + { + size_t index = mEvaluationOrderList[i]; -void Evaluation::SetEvaluationParameters(size_t target, void *parameters, size_t parametersSize) -{ - EvaluationStage& stage = mEvaluations[target]; - stage.mParameters = parameters; - stage.mParametersSize = parametersSize; + EvaluationStage& evaluation = mEvaluations[index]; + if (!evaluation.mUseCountByOthers) + continue; - if (!stage.mParametersBuffer) - { - glGenBuffers(1, &stage.mParametersBuffer); - glBindBuffer(GL_UNIFORM_BUFFER, stage.mParametersBuffer); + if (freeRenderTargets.empty()) + { + evaluation.mTarget = new RenderTarget(); + mAllocatedRenderTargets.push_back(evaluation.mTarget); + } + else + { + evaluation.mTarget = freeRenderTargets.back(); + freeRenderTargets.pop_back(); + } - glBufferData(GL_UNIFORM_BUFFER, parametersSize, parameters, GL_DYNAMIC_DRAW); - glBindBufferBase(GL_UNIFORM_BUFFER, 0, stage.mParametersBuffer); - glBindBuffer(GL_UNIFORM_BUFFER, 0); - } - else - { - glBindBuffer(GL_UNIFORM_BUFFER, stage.mParametersBuffer); - glBufferSubData(GL_UNIFORM_BUFFER, 0, parametersSize, parameters); - glBindBuffer(GL_UNIFORM_BUFFER, 0); + const Input& input = evaluation.mInput; + for (size_t inputIndex = 0; inputIndex < 8; inputIndex++) + { + int targetIndex = input.mInputs[inputIndex]; + if (targetIndex == -1) + continue; + + useCount[targetIndex]--; + if (!useCount[targetIndex]) + { + freeRenderTargets.push_back(mEvaluations[targetIndex].mTarget); + } + } + } + } - SetTargetDirty(target); + //Log("Using %d allocated buffers.\n", mAllocatedRenderTargets.size()); } -void Evaluation::RunEvaluation() +void Evaluation::RunEvaluation(int width, int height, bool forceEvaluation) { if (mEvaluationOrderList.empty()) return; - if (!mDirtyCount) + if (!mDirtyCount && !forceEvaluation) return; - for (size_t i = 0; i < mEvaluationOrderList.size(); i++) { - const EvaluationStage& evaluation = mEvaluations[mEvaluationOrderList[i]]; - if (!evaluation.mbDirty) - continue; - - const Input& input = evaluation.mInput; - const RenderTarget &tg = evaluation.mTarget; - tg.bindAsTarget(); - unsigned int program = mProgramPerNodeType[evaluation.mNodeType]; + size_t index = mEvaluationOrderList[i]; - glUseProgram(program); + EvaluationStage& evaluation = mEvaluations[index]; + if (!evaluation.mbDirty && !forceEvaluation) + continue; - glBindBufferBase(GL_UNIFORM_BUFFER, 1, evaluation.mParametersBuffer); + if (evaluation.mTarget && !evaluation.mTarget->mGLTexID) + evaluation.mTarget->initBuffer(width, height, false); - int samplerIndex = 0; - for (size_t inputIndex = 0; inputIndex < 8; inputIndex++) - { - unsigned int parameter = glGetUniformLocation(program, samplerName[inputIndex]); - if (parameter == 0xFFFFFFFF) - continue; - glUniform1i(parameter, samplerIndex); - glActiveTexture(GL_TEXTURE0 + samplerIndex); - samplerIndex++; - int targetIndex = input.mInputs[inputIndex]; - if (targetIndex < 0) - { - glBindTexture(GL_TEXTURE_2D, 0); - continue; - } - //assert(!mEvaluations[targetIndex].mbDirty); - glBindTexture(GL_TEXTURE_2D, mEvaluations[targetIndex].mTarget.mGLTexID); - - const InputSampler& inputSampler = evaluation.mInputSamplers[inputIndex]; - TexParam(filter[inputSampler.mFilterMin], filter[inputSampler.mFilterMag], wrap[inputSampler.mWrapU], wrap[inputSampler.mWrapV], GL_TEXTURE_2D); - } - // - unsigned int parameter = glGetUniformLocation(program, "equiRectEnvSampler"); - if (parameter != 0xFFFFFFFF) - { - glUniform1i(parameter, samplerIndex); - glActiveTexture(GL_TEXTURE0 + samplerIndex); - glBindTexture(GL_TEXTURE_2D, equiRectTexture); - } - mFSQuad.Render(); + PerformEvaluationForNode(index, width, height, false); } for (auto& evaluation : mEvaluations) @@ -475,12 +252,12 @@ void Evaluation::RunEvaluation() if (evaluation.mbDirty) { evaluation.mbDirty = false; + evaluation.mbForceEval = false; mDirtyCount--; } } -// assert(mDirtyCount == 0); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glUseProgram(0); + + FinishEvaluation(); } void Evaluation::SetEvaluationSampler(size_t target, const std::vector& inputSamplers) @@ -492,11 +269,13 @@ void Evaluation::SetEvaluationSampler(size_t target, const std::vector mFreeTargets; -int mTransientTextureMaxCount; -static TransientTarget* GetTransientTarget(int width, int height, int useCount) -{ - if (mFreeTargets.empty()) - { - TransientTarget *res = new TransientTarget; - res->mTarget.initBuffer(width, height, false); - res->mUseCount = useCount; - mTransientTextureMaxCount++; - return res; - } - TransientTarget *res = mFreeTargets.back(); - res->mUseCount = useCount; - mFreeTargets.pop_back(); - return res; -} - -static void LoseTransientTarget(TransientTarget *transientTarget) -{ - transientTarget->mUseCount--; - if (transientTarget->mUseCount <= 0) - mFreeTargets.push_back(transientTarget); -} - -void Evaluation::Bake(const char *szFilename, size_t target, int width, int height) -{ - if (mEvaluationOrderList.empty()) - return; - - if (target < 0 || target >= (int)mEvaluations.size()) - return; - - mTransientTextureMaxCount = 0; - std::vector evaluationUseCount(mEvaluationOrderList.size(), 0); // use count of texture by others - std::vector evaluationTransientTargets(mEvaluationOrderList.size(), NULL); - for (auto& evaluation : mEvaluations) - { - for (int targetIndex : evaluation.mInput.mInputs) - if (targetIndex != -1) - evaluationUseCount[targetIndex]++; - } - if (evaluationUseCount[target] < 1) - evaluationUseCount[target] = 1; - - // todo : revert pass. dec use count for parent nodes whose children have 0 use count - - for (size_t i = 0; i < mEvaluationOrderList.size(); i++) - { - size_t nodeIndex = mEvaluationOrderList[i]; - if (!evaluationUseCount[nodeIndex]) - continue; - const EvaluationStage& evaluation = mEvaluations[nodeIndex]; - - const Input& input = evaluation.mInput; - - TransientTarget* transientTarget = GetTransientTarget(width, height, evaluationUseCount[nodeIndex]); - evaluationTransientTargets[nodeIndex] = transientTarget; - transientTarget->mTarget.bindAsTarget(); - unsigned int program = mProgramPerNodeType[evaluation.mNodeType]; - glUseProgram(program); - int samplerIndex = 0; - for (size_t inputIndex = 0; inputIndex < 8; inputIndex++) - { - unsigned int parameter = glGetUniformLocation(program, samplerName[inputIndex]); - if (parameter == 0xFFFFFFFF) - continue; - glUniform1i(parameter, samplerIndex); - glActiveTexture(GL_TEXTURE0 + samplerIndex); - samplerIndex++; - int targetIndex = input.mInputs[inputIndex]; - if (targetIndex < 0) - { - glBindTexture(GL_TEXTURE_2D, 0); - continue; - } - - glBindTexture(GL_TEXTURE_2D, evaluationTransientTargets[targetIndex]->mTarget.mGLTexID); - LoseTransientTarget(evaluationTransientTargets[targetIndex]); - const InputSampler& inputSampler = evaluation.mInputSamplers[inputIndex]; - TexParam(filter[inputSampler.mFilterMin], filter[inputSampler.mFilterMag], wrap[inputSampler.mWrapU], wrap[inputSampler.mWrapV], GL_TEXTURE_2D); - } - // - unsigned int parameter = glGetUniformLocation(program, "equiRectEnvSampler"); - if (parameter != 0xFFFFFFFF) - { - glUniform1i(parameter, samplerIndex); - glActiveTexture(GL_TEXTURE0 + samplerIndex); - glBindTexture(GL_TEXTURE_2D, equiRectTexture); - } - mFSQuad.Render(); - if (nodeIndex == target) - break; - } - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glUseProgram(0); - - // save - unsigned char *imgBits = new unsigned char[width * height * 4]; - glBindTexture(GL_TEXTURE_2D, evaluationTransientTargets[target]->mTarget.mGLTexID); - glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, imgBits); - stbi_write_png(szFilename, width, height, 4, imgBits, width * 4); - delete[] imgBits; - - // free transient textures - for (auto transientTarget : mFreeTargets) - { - assert(transientTarget->mUseCount <= 0); - transientTarget->mTarget.destroy(); - } - - Log("Texture %s saved. Using %d textures.\n", szFilename, mTransientTextureMaxCount); -} - -void Evaluation::LoadEquiRectHDREnvLight(const std::string& filepath) -{ - HDRLoaderResult result; - bool ret = HDRLoader::load(filepath.c_str(), result); - if (!ret) - return; - glGenTextures(1, &equiRectTexture); - glBindTexture(GL_TEXTURE_2D, equiRectTexture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, result.width, result.height, 0, GL_RGB, GL_FLOAT, result.cols); - glGenerateMipmap(GL_TEXTURE_2D); - TexParam(GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT, GL_TEXTURE_2D); - -} - -void Evaluation::LoadEquiRect(const std::string& filepath) -{ - int x, y, comp; - stbi_uc * uc = stbi_load(filepath.c_str(), &x, &y, &comp, 3); - - glGenTextures(1, &equiRectTexture); - glBindTexture(GL_TEXTURE_2D, equiRectTexture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, x, y, 0, GL_RGB, GL_UNSIGNED_BYTE, uc); - glGenerateMipmap(GL_TEXTURE_2D); - TexParam(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT, GL_TEXTURE_2D); - - stbi_image_free(uc); -} - void Evaluation::Clear() { for (auto& ev : mEvaluations) @@ -691,4 +323,5 @@ void Evaluation::Clear() mEvaluations.clear(); mEvaluationOrderList.clear(); -} \ No newline at end of file +} + diff --git a/src/Evaluation.h b/src/Evaluation.h index 84729a86..788babae 100644 --- a/src/Evaluation.h +++ b/src/Evaluation.h @@ -1,10 +1,60 @@ +// https://github.com/CedricGuillemet/Imogen +// +// The MIT License(MIT) +// +// Copyright(c) 2018 Cedric Guillemet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + #pragma once #include #include #include #include "Library.h" +#include "libtcc/libtcc.h" +#include "Imogen.h" +#include +#include + +extern int Log(const char *szFormat, ...); typedef unsigned int TextureID; +enum EvaluationStatus +{ + EVAL_OK, + EVAL_ERR, +}; + +struct EvaluationInfo +{ + int targetIndex; + int inputIndices[8]; + int forcedDirty; +}; + +typedef struct Image_t +{ + int width, height; + int components; + void *bits; +} Image; class RenderTarget { @@ -15,6 +65,7 @@ class RenderTarget fbo = 0; depthbuffer = 0; mWidth = mHeight = 0; + mRefCount = 0; } void initBuffer(int width, int height, bool hasZBuffer); @@ -27,7 +78,7 @@ class RenderTarget int mWidth, mHeight; TextureID fbo; TextureID depthbuffer; - + int mRefCount; void destroy(); void checkFBO(); @@ -39,38 +90,73 @@ struct Evaluation { Evaluation(); - void SetEvaluationGLSL(const std::vector& filenames); - std::string GetEvaluationGLSL(const std::string& filename); + void Init(); + void Finish(); + void SetEvaluators(const std::vector& evaluatorfilenames); + std::string GetEvaluator(const std::string& filename); - void LoadEquiRectHDREnvLight(const std::string& filepath); - void LoadEquiRect(const std::string& filepath); + size_t AddEvaluation(size_t nodeType, const std::string& nodeName); - size_t AddEvaluationTarget(size_t nodeType, const std::string& nodeName); void DelEvaluationTarget(size_t target); unsigned int GetEvaluationTexture(size_t target); void SetEvaluationParameters(size_t target, void *parameters, size_t parametersSize); + void PerformEvaluationForNode(size_t index, int width, int height, bool force); void SetEvaluationSampler(size_t target, const std::vector& inputSamplers); void AddEvaluationInput(size_t target, int slot, int source); void DelEvaluationInput(size_t target, int slot); - void RunEvaluation(); + void RunEvaluation(int width, int height, bool forceEvaluation); void SetEvaluationOrder(const std::vector nodeOrderList); void SetTargetDirty(size_t target); - void Bake(const char *szFilename, size_t target, int width, int height); void Clear(); + + // API + static int ReadImage(const char *filename, Image *image); + static int WriteImage(const char *filename, Image *image, int format, int quality); + static int GetEvaluationImage(int target, Image *image); + static int SetEvaluationImage(int target, Image *image); + static int SetThumbnailImage(Image *image); + static int AllocateImage(Image *image); + static int FreeImage(Image *image); + static unsigned int UploadImage(Image *image); + static int Evaluate(int target, int width, int height, Image *image); + + // synchronous texture cache + // use for simple textures(stock) or to replace with a more efficient one + unsigned int GetTexture(const std::string& filename); protected: - + void APIInit(); + std::map mSynchronousTextureCache; + + int mEvaluationMode; + //int mAllocatedTargets; unsigned int equiRectTexture; int mDirtyCount; - std::vector mProgramPerNodeType; - struct Shader + + void ClearEvaluators(); + struct Evaluator + { + Evaluator() : mGLSLProgram(0), mCFunction(0), mMem(0) {} + unsigned int mGLSLProgram; + int(*mCFunction)(void *parameters, void *evaluationInfo); + void *mMem; + }; + + std::vector mEvaluatorPerNodeType; + + struct EvaluatorScript { - std::string mShaderText; + EvaluatorScript() : mProgram(0), mCFunction(0), mMem(0), mNodeType(-1) {} + EvaluatorScript(const std::string & text) : mText(text), mProgram(0), mCFunction(0), mMem(0), mNodeType(-1) {} + std::string mText; unsigned int mProgram; + int(*mCFunction)(void *parameters, void *evaluationInfo); + void *mMem; int mNodeType; }; - std::map mGLSLs; + + std::map mEvaluatorScripts; struct Input { @@ -83,7 +169,7 @@ struct Evaluation struct EvaluationStage { - RenderTarget mTarget; + RenderTarget *mTarget; size_t mNodeType; unsigned int mParametersBuffer; void *mParameters; @@ -91,10 +177,23 @@ struct Evaluation Input mInput; std::vector mInputSamplers; bool mbDirty; - + bool mbForceEval; + int mEvaluationType; // 0 = GLSL. 1 = CPU C + int mUseCountByOthers; void Clear(); }; + std::vector mEvaluations; std::vector mEvaluationOrderList; -}; \ No newline at end of file + + void BindGLSLParameters(EvaluationStage& stage); + void EvaluateGLSL(EvaluationStage& evaluation); + void EvaluateC(EvaluationStage& evaluation, size_t index); + void FinishEvaluation(); + + std::vector mAllocatedRenderTargets; + void SetEvaluationMemoryMode(int mode); + void RecurseGetUse(size_t target, std::vector& usedNodes); + +}; diff --git a/src/EvaluationAPI.cpp b/src/EvaluationAPI.cpp new file mode 100644 index 00000000..ab994bcd --- /dev/null +++ b/src/EvaluationAPI.cpp @@ -0,0 +1,741 @@ +// https://github.com/CedricGuillemet/Imogen +// +// The MIT License(MIT) +// +// Copyright(c) 2018 Cedric Guillemet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#include // Initialize with gl3wInit() +#include "Evaluation.h" +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" +#include +#include + + +static const int SemUV0 = 0; +static const unsigned int wrap[] = { GL_REPEAT, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER, GL_MIRRORED_REPEAT }; +static const unsigned int filter[] = { GL_LINEAR, GL_NEAREST }; +static const char* samplerName[] = { "Sampler0", "Sampler1", "Sampler2", "Sampler3", "Sampler4", "Sampler5", "Sampler6", "Sampler7" }; + +inline void TexParam(TextureID MinFilter, TextureID MagFilter, TextureID WrapS, TextureID WrapT, TextureID texMode) +{ + glTexParameteri(texMode, GL_TEXTURE_MIN_FILTER, MinFilter); + glTexParameteri(texMode, GL_TEXTURE_MAG_FILTER, MagFilter); + glTexParameteri(texMode, GL_TEXTURE_WRAP_S, WrapS); + glTexParameteri(texMode, GL_TEXTURE_WRAP_T, WrapT); +} + +void RenderTarget::bindAsTarget() const +{ + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glViewport(0, 0, mWidth, mHeight); +} + +void RenderTarget::destroy() +{ + if (mGLTexID) + glDeleteTextures(1, &mGLTexID); + if (fbo) + glDeleteFramebuffers(1, &fbo); + if (depthbuffer) + glDeleteRenderbuffers(1, &depthbuffer); + fbo = depthbuffer = 0; + mWidth = mHeight = 0; + mGLTexID = 0; +} + +void RenderTarget::clear() +{ + glClear(GL_COLOR_BUFFER_BIT | (depthbuffer ? GL_DEPTH_BUFFER_BIT : 0)); +} + +void RenderTarget::initBuffer(int width, int height, bool hasZBuffer) +{ + if ((width == mWidth) && (mHeight == height) && (!(hasZBuffer ^ (depthbuffer != 0)))) + return; + destroy(); + + mWidth = width; + mHeight = height; + + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + // diffuse + glGenTextures(1, &mGLTexID); + glBindTexture(GL_TEXTURE_2D, mGLTexID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + TexParam(GL_NEAREST, GL_NEAREST, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_TEXTURE_2D); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mGLTexID, 0); + /* + if (hasZBuffer) + { + // Z + glGenTextures(1, &mGLTexID2); + glBindTexture(GL_TEXTURE_2D, mGLTexID2); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + TexParam(GL_NEAREST, GL_NEAREST, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_TEXTURE_2D); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, mGLTexID2, 0); + } + */ + static const GLenum DrawBuffers[] = { GL_COLOR_ATTACHMENT0 }; + glDrawBuffers(sizeof(DrawBuffers) / sizeof(GLenum), DrawBuffers); + + checkFBO(); +} + +void RenderTarget::checkFBO() +{ + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + int status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + switch (status) + { + case GL_FRAMEBUFFER_COMPLETE: + //Log("Framebuffer complete.\n"); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + Log("[ERROR] Framebuffer incomplete: Attachment is NOT complete."); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + Log("[ERROR] Framebuffer incomplete: No image is attached to FBO."); + break; + /* + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + Log("[ERROR] Framebuffer incomplete: Attached images have different dimensions."); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_FORMATS: + Log("[ERROR] Framebuffer incomplete: Color attached images have different internal formats."); + break; + */ + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + Log("[ERROR] Framebuffer incomplete: Draw buffer.\n"); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + Log("[ERROR] Framebuffer incomplete: Read buffer.\n"); + break; + + case GL_FRAMEBUFFER_UNSUPPORTED: + Log("[ERROR] Unsupported by FBO implementation.\n"); + break; + + default: + Log("[ERROR] Unknow error.\n"); + break; + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +class FullScreenTriangle +{ +public: + FullScreenTriangle() : mGLFullScreenVertexArrayName(-1) + { + } + ~FullScreenTriangle() + { + } + void Init(); + void Render(); +protected: + TextureID mGLFullScreenVertexArrayName; +}; + +void FullScreenTriangle::Init() +{ + TextureID fsVA; + + float fsVts[] = { 0.f,0.f, 2.f,0.f, 0.f,2.f }; + glGenBuffers(1, &fsVA); + glBindBuffer(GL_ARRAY_BUFFER, fsVA); + glBufferData(GL_ARRAY_BUFFER, 3 * sizeof(float) * 2, fsVts, GL_STATIC_DRAW); + + glGenVertexArrays(1, &mGLFullScreenVertexArrayName); + glBindVertexArray(mGLFullScreenVertexArrayName); + glBindBuffer(GL_ARRAY_BUFFER, fsVA); + glVertexAttribPointer(SemUV0, 2, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(SemUV0); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +void FullScreenTriangle::Render() +{ + glBindVertexArray(mGLFullScreenVertexArrayName); + glDrawArrays(GL_TRIANGLES, 0, 3); + glBindVertexArray(0); +} + +unsigned int LoadShader(const std::string &shaderString, const char *fileName) +{ + TextureID programObject = glCreateProgram(); + if (programObject == 0) + return 0; + + GLint compiled; + const char *shaderTypeStrings[] = { "\n#version 430 core\n#define VERTEX_SHADER\n", "\n#version 430 core\n#define FRAGMENT_SHADER\n" }; + TextureID shaderTypes[] = { GL_VERTEX_SHADER, GL_FRAGMENT_SHADER }; + TextureID compiledShader[2]; + + for (int i = 0; i<2; i++) + { + // Create the shader object + int shader = glCreateShader(shaderTypes[i]); + + if (shader == 0) + return false; + + int stringsCount = 2; + const char ** strings = (const char**)malloc(sizeof(char*) * stringsCount); //new const char*[stringsCount]; + int * stringLength = (int*)malloc(sizeof(int) * stringsCount); //new int[stringsCount]; + strings[0] = shaderTypeStrings[i]; + stringLength[0] = int(strlen(shaderTypeStrings[i])); + strings[stringsCount - 1] = shaderString.c_str(); + stringLength[stringsCount - 1] = int(shaderString.length()); + + // Load and compile the shader source + glShaderSource(shader, stringsCount, strings, stringLength); + glCompileShader(shader); + + + free(stringLength); + free(strings); + + // Check the compile status + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (compiled == 0) + { + GLint info_len = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_len); + if (info_len > 1) + { + char* info_log = (char*)malloc(sizeof(char) * info_len); + glGetShaderInfoLog(shader, info_len, NULL, info_log); + Log("Error compiling shader: %s \n", fileName); + Log(info_log); + Log("\n"); + free(info_log); + } + glDeleteShader(shader); + return 0; + } + compiledShader[i] = shader; + } + + + + GLint linked; + + for (int i = 0; i<2; i++) + glAttachShader(programObject, compiledShader[i]); + + + // Link the program + glLinkProgram(programObject); + + glBindAttribLocation(programObject, SemUV0, "inUV"); + + // Check the link status + glGetProgramiv(programObject, GL_LINK_STATUS, &linked); + if (linked == 0) + { + GLint info_len = 0; + glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &info_len); + if (info_len > 1) + { + char* info_log = (char*)malloc(sizeof(char) * info_len); + glGetProgramInfoLog(programObject, info_len, NULL, info_log); + Log("Error linking program:\n"); + Log(info_log); + free(info_log); + } + glDeleteProgram(programObject); + return 0; + } + + // Delete these here because they are attached to the program object. + for (int i = 0; i<2; i++) + glDeleteShader(compiledShader[i]); + + // attributes + return programObject; +} + +FullScreenTriangle mFSQuad; + +void Evaluation::APIInit() +{ + mFSQuad.Init(); +} + +static void libtccErrorFunc(void *opaque, const char *msg) +{ + Log(msg); + Log("\n"); +} + +struct EValuationFunction +{ + const char *szFunctionName; + void *function; +}; + +/* +typedef struct Evaluation_t +{ +int targetIndex; +int inputIndices[8]; +int forcedDirty; +} Evaluation; +*/ + +extern Evaluation gEvaluation; + +int Evaluation::ReadImage(const char *filename, Image *image) +{ + unsigned char *data = stbi_load(filename, &image->width, &image->height, &image->components, 0); + if (!data) + return EVAL_ERR; + image->bits = data; + return EVAL_OK; +} + +int Evaluation::WriteImage(const char *filename, Image *image, int format, int quality) +{ + switch (format) + { + case 0: + if (!stbi_write_jpg(filename, image->width, image->height, image->components, image->bits, quality)) + return EVAL_ERR; + break; + case 1: + if (!stbi_write_png(filename, image->width, image->height, image->components, image->bits, image->width * image->components)) + return EVAL_ERR; + break; + case 2: + if (!stbi_write_tga(filename, image->width, image->height, image->components, image->bits)) + return EVAL_ERR; + break; + case 3: + if (!stbi_write_bmp(filename, image->width, image->height, image->components, image->bits)) + return EVAL_ERR; + break; + case 4: + //if (stbi_write_hdr(filename, image->width, image->height, image->components, image->bits)) + return EVAL_ERR; + break; + } + return EVAL_OK; +} + +int Evaluation::GetEvaluationImage(int target, Image *image) +{ + if (target == -1 || target >= gEvaluation.mEvaluations.size()) + return EVAL_ERR; + + Evaluation::EvaluationStage &evaluation = gEvaluation.mEvaluations[target]; + RenderTarget& tgt = *evaluation.mTarget; + image->components = 4; + image->width = tgt.mWidth; + image->height = tgt.mHeight; + image->bits = malloc(tgt.mWidth * tgt.mHeight * 4); + glBindTexture(GL_TEXTURE_2D, tgt.mGLTexID); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->bits); + + return EVAL_OK; +} + +int Evaluation::SetEvaluationImage(int target, Image *image) +{ + Evaluation::EvaluationStage &evaluation = gEvaluation.mEvaluations[target]; + //gEvaluation.UnreferenceRenderTarget(&evaluation.mTarget); + if (!evaluation.mTarget) + { + evaluation.mTarget = new RenderTarget; + } + evaluation.mTarget->initBuffer(image->width, image->height, false); + + glBindTexture(GL_TEXTURE_2D, evaluation.mTarget->mGLTexID); + switch (image->components) + { + case 3: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image->width, image->height, 0, GL_RGB, GL_UNSIGNED_BYTE, image->bits); + break; + case 4: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->width, image->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->bits); + break; + default: + Log("SetEvaluationImage: unsupported component format.\n"); + return EVAL_ERR; + } + //gEvaluation.UnreferenceRenderTarget(&evaluation.mTarget); + return EVAL_OK; +} + +int Evaluation::AllocateImage(Image *image) +{ + return EVAL_OK; +} + +int Evaluation::FreeImage(Image *image) +{ + free(image->bits); + return EVAL_OK; +} + +int Evaluation::SetThumbnailImage(Image *image) +{ + int outlen; + unsigned char *bits = stbi_write_png_to_mem((unsigned char*)image->bits, image->width * image->components, image->width, image->height, image->components, &outlen); + if (!bits) + return EVAL_ERR; + std::vector pngImage(outlen); + memcpy(pngImage.data(), bits, outlen); + + extern Library library; + extern Imogen imogen; + + int materialIndex = imogen.GetCurrentMaterialIndex(); + Material & material = library.mMaterials[materialIndex]; + material.mThumbnail = pngImage; + material.mThumbnailTextureId = 0; + return EVAL_OK; +} + +void Evaluation::RecurseGetUse(size_t target, std::vector& usedNodes) +{ + EvaluationStage& evaluation = mEvaluations[target]; + const Input& input = evaluation.mInput; + + std::vector usingTargets; + for (size_t inputIndex = 0; inputIndex < 8; inputIndex++) + { + int targetIndex = input.mInputs[inputIndex]; + if (targetIndex == -1) + continue; + RecurseGetUse(targetIndex, usedNodes); + } + + if (std::find(usedNodes.begin(), usedNodes.end(), target) == usedNodes.end()) + usedNodes.push_back(target); +} + +int Evaluation::Evaluate(int target, int width, int height, Image *image) +{ + std::vector svgEvalList = gEvaluation.mEvaluationOrderList; + gEvaluation.mEvaluationOrderList.clear(); + + gEvaluation.RecurseGetUse(target, gEvaluation.mEvaluationOrderList); + + gEvaluation.SetEvaluationMemoryMode(1); + + gEvaluation.RunEvaluation(width, height, true); + GetEvaluationImage(target, image); + gEvaluation.SetEvaluationMemoryMode(0); + + gEvaluation.mEvaluationOrderList = svgEvalList; + + gEvaluation.RunEvaluation(256, 256, true); + return EVAL_OK; +} + +static const EValuationFunction evaluationFunctions[] = { + { "Log", (void*)Log }, + { "ReadImage", (void*)Evaluation::ReadImage }, + { "WriteImage", (void*)Evaluation::WriteImage }, + { "GetEvaluationImage", (void*)Evaluation::GetEvaluationImage }, + { "SetEvaluationImage", (void*)Evaluation::SetEvaluationImage }, + { "AllocateImage", (void*)Evaluation::AllocateImage }, + { "FreeImage", (void*)Evaluation::FreeImage }, + { "SetThumbnailImage", (void*)Evaluation::SetThumbnailImage }, + { "Evaluate", (void*)Evaluation::Evaluate} +}; + +std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) +{ + size_t start_pos = 0; + while ((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); // Handles case where 'to' is a substring of 'from' + } + return str; +} + + +void Evaluation::ClearEvaluators() +{ + // clear + for (auto& program : mEvaluatorPerNodeType) + { + if (program.mGLSLProgram) + glDeleteProgram(program.mGLSLProgram); + if (program.mMem) + free(program.mMem); + } +} + +void Evaluation::SetEvaluators(const std::vector& evaluatorfilenames) +{ + ClearEvaluators(); + + mEvaluatorPerNodeType.clear(); + mEvaluatorPerNodeType.resize(evaluatorfilenames.size(), Evaluator()); + + // GLSL + for (auto& file : evaluatorfilenames) + { + if (file.mEvaluatorType != EVALUATOR_GLSL) + continue; + const std::string filename = file.mFilename; + + std::ifstream t(file.mDirectory + filename); + if (t.good()) + { + std::string str((std::istreambuf_iterator(t)), std::istreambuf_iterator()); + if (mEvaluatorScripts.find(filename) == mEvaluatorScripts.end()) + mEvaluatorScripts[filename] = EvaluatorScript(str); + else + mEvaluatorScripts[filename].mText = str; + } + } + + std::string baseShader = mEvaluatorScripts["Shader.glsl"].mText; + for (auto& file : evaluatorfilenames) + { + if (file.mEvaluatorType != EVALUATOR_GLSL) + continue; + const std::string filename = file.mFilename; + + if (filename == "Shader.glsl") + continue; + + EvaluatorScript& shader = mEvaluatorScripts[filename]; + std::string shaderText = ReplaceAll(baseShader, "__NODE__", shader.mText); + std::string nodeName = ReplaceAll(filename, ".glsl", ""); + shaderText = ReplaceAll(shaderText, "__FUNCTION__", nodeName + "()"); + + unsigned int program = LoadShader(shaderText, filename.c_str()); + unsigned int parameterBlockIndex = glGetUniformBlockIndex(program, (nodeName + "Block").c_str()); + glUniformBlockBinding(program, parameterBlockIndex, 1); + shader.mProgram = program; + if (shader.mNodeType != -1) + mEvaluatorPerNodeType[shader.mNodeType].mGLSLProgram = program; + } + + // C + for (auto& file : evaluatorfilenames) + { + if (file.mEvaluatorType != EVALUATOR_C) + continue; + const std::string filename = file.mFilename; + + std::ifstream t(file.mDirectory + filename); + if (!t.good()) + { + Log("%s - Unable to load file.\n", filename.c_str()); + continue; + } + std::string str((std::istreambuf_iterator(t)), std::istreambuf_iterator()); + if (mEvaluatorScripts.find(filename) == mEvaluatorScripts.end()) + mEvaluatorScripts[filename] = EvaluatorScript(str); + else + mEvaluatorScripts[filename].mText = str; + + EvaluatorScript& program = mEvaluatorScripts[filename]; + TCCState *s = tcc_new(); + + int *noLib = (int*)s; + noLib[2] = 1; // no stdlib + + tcc_set_error_func(s, 0, libtccErrorFunc); + tcc_add_include_path(s, "C\\"); + tcc_set_output_type(s, TCC_OUTPUT_MEMORY); + + if (tcc_compile_string(s, program.mText.c_str()) != 0) + { + Log("%s - Compilation error!\n", filename.c_str()); + continue; + } + + for (auto& evaluationFunction : evaluationFunctions) + tcc_add_symbol(s, evaluationFunction.szFunctionName, evaluationFunction.function); + + int size = tcc_relocate(s, NULL); + if (size == -1) + { + Log("%s - Libtcc unable to relocate program!\n", filename.c_str()); + continue; + } + program.mMem = malloc(size); + tcc_relocate(s, program.mMem); + + *(void**)(&program.mCFunction) = tcc_get_symbol(s, "main"); + if (!program.mCFunction) + { + Log("%s - No main function!\n", filename.c_str()); + } + tcc_delete(s); + + if (program.mNodeType != -1) + { + mEvaluatorPerNodeType[program.mNodeType].mCFunction = program.mCFunction; + mEvaluatorPerNodeType[program.mNodeType].mMem = program.mMem; + } + + } +} + +void Evaluation::BindGLSLParameters(EvaluationStage& stage) +{ + if (!stage.mParametersBuffer) + { + glGenBuffers(1, &stage.mParametersBuffer); + glBindBuffer(GL_UNIFORM_BUFFER, stage.mParametersBuffer); + + glBufferData(GL_UNIFORM_BUFFER, stage.mParametersSize, stage.mParameters, GL_DYNAMIC_DRAW); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, stage.mParametersBuffer); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } + else + { + glBindBuffer(GL_UNIFORM_BUFFER, stage.mParametersBuffer); + glBufferSubData(GL_UNIFORM_BUFFER, 0, stage.mParametersSize, stage.mParameters); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } +} + +void Evaluation::EvaluateGLSL(EvaluationStage& evaluation) +{ + const Input& input = evaluation.mInput; + + evaluation.mTarget->bindAsTarget(); + unsigned int program = mEvaluatorPerNodeType[evaluation.mNodeType].mGLSLProgram; + + glUseProgram(program); + + glBindBufferBase(GL_UNIFORM_BUFFER, 1, evaluation.mParametersBuffer); + + int samplerIndex = 0; + for (size_t inputIndex = 0; inputIndex < 8; inputIndex++) + { + unsigned int parameter = glGetUniformLocation(program, samplerName[inputIndex]); + if (parameter == 0xFFFFFFFF) + continue; + glUniform1i(parameter, samplerIndex); + glActiveTexture(GL_TEXTURE0 + samplerIndex); + samplerIndex++; + int targetIndex = input.mInputs[inputIndex]; + if (targetIndex < 0) + { + glBindTexture(GL_TEXTURE_2D, 0); + continue; + } + //assert(!mEvaluations[targetIndex].mbDirty); + glBindTexture(GL_TEXTURE_2D, mEvaluations[targetIndex].mTarget->mGLTexID); + + const InputSampler& inputSampler = evaluation.mInputSamplers[inputIndex]; + TexParam(filter[inputSampler.mFilterMin], filter[inputSampler.mFilterMag], wrap[inputSampler.mWrapU], wrap[inputSampler.mWrapV], GL_TEXTURE_2D); + } + // + mFSQuad.Render(); +} + +void Evaluation::EvaluateC(EvaluationStage& evaluation, size_t index) +{ + const Input& input = evaluation.mInput; + + EvaluationInfo evaluationInfo; + evaluationInfo.targetIndex = int(index); + memcpy(evaluationInfo.inputIndices, input.mInputs, sizeof(evaluationInfo.inputIndices)); + evaluationInfo.forcedDirty = evaluation.mbForceEval ? 1 : 0; + try // todo: find a better solution than a try catch + { + mEvaluatorPerNodeType[evaluation.mNodeType].mCFunction(evaluation.mParameters, &evaluationInfo); + } + catch (...) + { + + } +} + +void Evaluation::EvaluationStage::Clear() +{ + if (mEvaluationType == EVALUATOR_GLSL) + glDeleteBuffers(1, &mParametersBuffer); + //gEvaluation.UnreferenceRenderTarget(&mTarget); +} + +void Evaluation::FinishEvaluation() +{ + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glUseProgram(0); +} + +unsigned int Evaluation::UploadImage(Image *image) +{ + unsigned int textureId; + glGenTextures(1, &textureId); + glBindTexture(GL_TEXTURE_2D, textureId); + switch (image->components) + { + case 3: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image->width, image->height, 0, GL_RGB, GL_UNSIGNED_BYTE, image->bits); + break; + case 4: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->width, image->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->bits); + break; + default: + Log("Texture cache : unsupported component format.\n"); + return EVAL_ERR; + } + TexParam(GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_TEXTURE_2D); + return textureId; +} + +unsigned int Evaluation::GetTexture(const std::string& filename) +{ + auto iter = mSynchronousTextureCache.find(filename); + if (iter != mSynchronousTextureCache.end()) + return iter->second; + + Image image; + unsigned int textureId = 0; + if (ReadImage(filename.c_str(), &image) == EVAL_OK) + { + textureId = UploadImage(&image); + } + + mSynchronousTextureCache[filename] = textureId; + return textureId; +} diff --git a/src/Imogen.cpp b/src/Imogen.cpp index 9e30eaca..92d5ebf0 100644 --- a/src/Imogen.cpp +++ b/src/Imogen.cpp @@ -1,3 +1,28 @@ +// https://github.com/CedricGuillemet/Imogen +// +// The MIT License(MIT) +// +// Copyright(c) 2018 Cedric Guillemet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + #include #include "imgui.h" #include "Imogen.h" @@ -8,10 +33,12 @@ #include "Evaluation.h" #include "NodesDelegate.h" #include "Library.h" -#ifdef WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#endif +#include "TaskScheduler.h" +#include "stb_image.h" +#include "tinydir.h" + +extern enki::TaskScheduler g_TS; + struct ImguiAppLog { ImguiAppLog() @@ -88,20 +115,21 @@ void DebugLogText(const char *szText) void Imogen::HandleEditor(TextEditor &editor, TileNodeEditGraphDelegate &nodeGraphDelegate, Evaluation& evaluation) { static int currentShaderIndex = -1; + if (currentShaderIndex == -1) { currentShaderIndex = 0; - editor.SetText(evaluation.GetEvaluationGLSL(shaderFileNames[currentShaderIndex])); + editor.SetText(evaluation.GetEvaluator(mEvaluatorFiles[currentShaderIndex].mFilename)); } auto cpos = editor.GetCursorPosition(); ImGui::BeginChild(13, ImVec2(250, 800)); - for (size_t i = 0; i < shaderFileNames.size(); i++) + for (size_t i = 0; i < mEvaluatorFiles.size(); i++) { bool selected = i == currentShaderIndex; - if (ImGui::Selectable(shaderFileNames[i].c_str(), &selected)) + if (ImGui::Selectable(mEvaluatorFiles[i].mFilename.c_str(), &selected)) { currentShaderIndex = int(i); - editor.SetText(evaluation.GetEvaluationGLSL(shaderFileNames[currentShaderIndex])); + editor.SetText(evaluation.GetEvaluator(mEvaluatorFiles[currentShaderIndex].mFilename)); editor.SetSelection(TextEditor::Coordinates(), TextEditor::Coordinates()); } } @@ -113,11 +141,12 @@ void Imogen::HandleEditor(TextEditor &editor, TileNodeEditGraphDelegate &nodeGra { auto textToSave = editor.GetText(); - std::ofstream t("GLSL/" + shaderFileNames[currentShaderIndex], std::ofstream::out); + std::ofstream t(mEvaluatorFiles[currentShaderIndex].mDirectory + mEvaluatorFiles[currentShaderIndex].mFilename, std::ofstream::out); t << textToSave; t.close(); - evaluation.SetEvaluationGLSL(shaderFileNames); + // TODO + evaluation.SetEvaluators(mEvaluatorFiles); nodeGraphDelegate.InvalidateParameters(); } @@ -206,7 +235,63 @@ std::string GetName(const std::string &name) return name; } -template bool TVRes(std::vector& res, const char *szName, int &selection, int index) +struct PinnedTaskUploadImage : enki::IPinnedTask +{ + PinnedTaskUploadImage(Image image, std::pair identifier) + : enki::IPinnedTask(0) // set pinned thread to 0 + , mImage(image) + , mIdentifier(identifier) + { + } + virtual void Execute() + { + unsigned int textureId = Evaluation::UploadImage(&mImage); + if (library.mMaterials.size() > mIdentifier.first && library.mMaterials[mIdentifier.first].mRuntimeUniqueId == mIdentifier.second) + { + library.mMaterials[mIdentifier.first].mThumbnailTextureId = textureId; + } + else + { + // find identifier + for (auto& material : library.mMaterials) + { + if (material.mRuntimeUniqueId == mIdentifier.second) + { + material.mThumbnailTextureId = textureId; + break; + } + } + } + + } + Image mImage; + std::pair mIdentifier; +}; + +struct DecodeThumbnailTaskSet : enki::ITaskSet +{ + DecodeThumbnailTaskSet(std::vector *src, std::pair identifier) : enki::ITaskSet(), mIdentifier(identifier), mSrc(src) + { + } + virtual void ExecuteRange(enki::TaskSetPartition range, uint32_t threadnum) + { + Image image; + unsigned char *data = stbi_load_from_memory(mSrc->data(), mSrc->size(), &image.width, &image.height, &image.components, 0); + if (data) + { + image.bits = data; + PinnedTaskUploadImage uploadTexTask(image, mIdentifier); + g_TS.AddPinnedTask(&uploadTexTask); + g_TS.WaitforTask(&uploadTexTask); + Evaluation::FreeImage(&image); + } + delete this; + } + std::pair mIdentifier; + std::vector *mSrc; +}; + +template bool TVRes(std::vector& res, const char *szName, int &selection, int index, Evaluation& evaluation) { bool ret = false; if (!ImGui::TreeNodeEx(szName, ImGuiTreeNodeFlags_FramePadding | ImGuiTreeNodeFlags_DefaultOpen)) @@ -217,6 +302,7 @@ template bool TVRes(std::vector& res, const cha std::vector> sortedResources; SortedResource::ComputeSortedResources(res, sortedResources); + unsigned int defaultTextureId = evaluation.GetTexture("Stock/thumbnail-icon.png"); for (const auto& sortedRes : sortedResources) //unsigned int i = 0; i < res.size(); i++) { @@ -245,14 +331,25 @@ template bool TVRes(std::vector& res, const cha } else if (currentGroupIsSkipped) continue; + ImGui::BeginGroup(); - ImGui::TreeNodeEx(GetName(res[indexInRes].mName).c_str(), node_flags); - - if (ImGui::IsItemClicked()) + T& resource = res[indexInRes]; + if (!resource.mThumbnailTextureId) + { + resource.mThumbnailTextureId = defaultTextureId; + g_TS.AddTaskSetToPipe(new DecodeThumbnailTaskSet(&resource.mThumbnail, std::make_pair(indexInRes,resource.mRuntimeUniqueId))); + } + ImGui::Image(ImTextureID(resource.mThumbnailTextureId), ImVec2(64, 64)); + bool clicked = ImGui::IsItemClicked(); + ImGui::SameLine(); + ImGui::TreeNodeEx(GetName(resource.mName).c_str(), node_flags); + clicked |= ImGui::IsItemClicked(); + if (clicked) { selection = (index << 16) + indexInRes; ret = true; } + ImGui::EndGroup(); } if (currentGroup.length() && !currentGroupIsSkipped) @@ -282,7 +379,10 @@ inline void GuiString(const char*label, std::string* str, int stringId, bool mul } static int selectedMaterial = -1; - +int Imogen::GetCurrentMaterialIndex() +{ + return selectedMaterial; +} void ValidateMaterial(Library& library, TileNodeEditGraphDelegate &nodeGraphDelegate, int materialIndex) { if (materialIndex == -1) @@ -321,7 +421,11 @@ void LibraryEdit(Library& library, TileNodeEditGraphDelegate &nodeGraphDelegate, if (ImGui::Button("New Material")) { library.mMaterials.push_back(Material()); - library.mMaterials.back().mName = "New"; + Material& back = library.mMaterials.back(); + back.mName = "New"; + back.mThumbnailTextureId = 0; + back.mRuntimeUniqueId = GetRuntimeId(); + if (previousSelection != -1) { ValidateMaterial(library, nodeGraphDelegate, previousSelection); @@ -333,7 +437,7 @@ void LibraryEdit(Library& library, TileNodeEditGraphDelegate &nodeGraphDelegate, NodeGraphClear(); } ImGui::BeginChild("TV", ImVec2(250, -1)); - if (TVRes(library.mMaterials, "Materials", selectedMaterial, 0)) + if (TVRes(library.mMaterials, "Materials", selectedMaterial, 0, evaluation)) { nodeGraphDelegate.mSelectedNodeIndex = -1; // save previous @@ -370,12 +474,6 @@ void LibraryEdit(Library& library, TileNodeEditGraphDelegate &nodeGraphDelegate, Material& material = library.mMaterials[selectedMaterial]; GuiString("Name", &material.mName, 100, false); GuiString("Comment", &material.mComment, 101, true); - /* to add: - - bake dir - - bake size - - preview size - - load equirect ibl - */ if (ImGui::Button("Delete Material")) { library.mMaterials.erase(library.mMaterials.begin() + selectedMaterial); @@ -398,6 +496,15 @@ void Imogen::Show(Library& library, TileNodeEditGraphDelegate &nodeGraphDelegate ImGui::SetNextDock("Imogen", ImGuiDockSlot_Tab); if (ImGui::BeginDock("Nodes")) { + ImGui::PushItemWidth(60); + static int previewSize = 0; + //ImGui::Combo("Preview size", &previewSize, " 128\0 256\0 512\0 1024\0 2048\0 4096\0"); + //ImGui::SameLine(); + if (ImGui::Button("Export")) + { + nodeGraphDelegate.DoForce(); + } + ImGui::PopItemWidth(); NodeGraph(&nodeGraphDelegate, selectedMaterial != -1); } ImGui::EndDock(); @@ -437,32 +544,46 @@ void Imogen::Show(Library& library, TileNodeEditGraphDelegate &nodeGraphDelegate ValidateMaterial(library, nodeGraphDelegate, selectedMaterial); } -void Imogen::DiscoverShaders() +void Imogen::DiscoverNodes(const char *extension, const char *directory, EVALUATOR_TYPE evaluatorType, std::vector& files) { -#ifdef WIN32 - HANDLE hFind; - WIN32_FIND_DATA FindFileData; + tinydir_dir dir; + tinydir_open(&dir, directory); - if ((hFind = FindFirstFile("GLSL/*.glsl", &FindFileData)) != INVALID_HANDLE_VALUE) + while (dir.has_next) { - do + tinydir_file file; + tinydir_readfile(&dir, &file); + + if (!file.is_dir && !strcmp(file.extension, extension)) { - //printf("%s\n", FindFileData.cFileName); - shaderFileNames.push_back(std::string(FindFileData.cFileName)); - } while (FindNextFile(hFind, &FindFileData)); - FindClose(hFind); + files.push_back({ directory, file.name, evaluatorType }); + } + + tinydir_next(&dir); } -#endif + + tinydir_close(&dir); } Imogen::Imogen() +{ +} + +Imogen::~Imogen() +{ + +} + +void Imogen::Init() { ImGui::InitDock(); editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL()); - DiscoverShaders(); + + DiscoverNodes("glsl", "GLSL/", EVALUATOR_GLSL, mEvaluatorFiles); + DiscoverNodes("c", "C/", EVALUATOR_C, mEvaluatorFiles); } -Imogen::~Imogen() +void Imogen::Finish() { ImGui::ShutdownDock(); } \ No newline at end of file diff --git a/src/Imogen.h b/src/Imogen.h index 6da14b9f..d28e7aef 100644 --- a/src/Imogen.h +++ b/src/Imogen.h @@ -1,23 +1,65 @@ +// https://github.com/CedricGuillemet/Imogen +// +// The MIT License(MIT) +// +// Copyright(c) 2018 Cedric Guillemet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + #pragma once #include +#include struct TileNodeEditGraphDelegate; struct Evaluation; class TextEditor; struct Library; +enum EVALUATOR_TYPE +{ + EVALUATOR_GLSL, + EVALUATOR_C, +}; + +struct EvaluatorFile +{ + std::string mDirectory; + std::string mFilename; + EVALUATOR_TYPE mEvaluatorType; +}; struct Imogen { Imogen(); ~Imogen(); + + void Init(); + void Finish(); void Show(Library& library, TileNodeEditGraphDelegate &nodeGraphDelegate, Evaluation& evaluation); - void DiscoverShaders(); + void DiscoverNodes(const char *extension, const char *directory, EVALUATOR_TYPE evaluatorType, std::vector& files); - std::vector shaderFileNames; + std::vector mEvaluatorFiles; + int GetCurrentMaterialIndex(); protected: void HandleEditor(TextEditor &editor, TileNodeEditGraphDelegate &nodeGraphDelegate, Evaluation& evaluation); }; -void DebugLogText(const char *szText); \ No newline at end of file +void DebugLogText(const char *szText); diff --git a/src/Library.cpp b/src/Library.cpp index 196c27d1..3440eca2 100644 --- a/src/Library.cpp +++ b/src/Library.cpp @@ -1,9 +1,35 @@ +// https://github.com/CedricGuillemet/Imogen +// +// The MIT License(MIT) +// +// Copyright(c) 2018 Cedric Guillemet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + #include "Library.h" enum : uint32_t { v_initial, v_materialComment, + v_thumbnail, v_lastVersion }; #define ADD(_fieldAdded, _fieldName) if (dataVersion >= _fieldAdded){ Ser(_fieldName); } @@ -103,6 +129,7 @@ template struct Serialize ADD(v_materialComment, material->mComment); ADD(v_initial, material->mMaterialNodes); ADD(v_initial, material->mMaterialConnections); + ADD(v_thumbnail, material->mThumbnail); } bool Ser(Library *library) { @@ -126,9 +153,21 @@ typedef Serialize SerializeRead; void LoadLib(Library *library, const char *szFilename) { SerializeRead(szFilename).Ser(library); + + for (auto& material : library->mMaterials) + { + material.mThumbnailTextureId = 0; + material.mRuntimeUniqueId = GetRuntimeId(); + } } void SaveLib(Library *library, const char *szFilename) { SerializeWrite(szFilename).Ser(library); } + +unsigned int GetRuntimeId() +{ + static unsigned int runtimeId = 0; + return ++runtimeId; +} \ No newline at end of file diff --git a/src/Library.h b/src/Library.h index f20eedcd..bee6bcc2 100644 --- a/src/Library.h +++ b/src/Library.h @@ -1,3 +1,28 @@ +// https://github.com/CedricGuillemet/Imogen +// +// The MIT License(MIT) +// +// Copyright(c) 2018 Cedric Guillemet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + #pragma once #include @@ -35,6 +60,11 @@ struct Material std::string mComment; std::vector mMaterialNodes; std::vector mMaterialConnections; + std::vector mThumbnail; + + //run time + unsigned int mThumbnailTextureId; + unsigned int mRuntimeUniqueId; }; struct Library { @@ -43,3 +73,6 @@ struct Library void LoadLib(Library *library, const char *szFilename); void SaveLib(Library *library, const char *szFilename); + +unsigned int GetRuntimeId(); +extern Library library; \ No newline at end of file diff --git a/src/Nodes.cpp b/src/Nodes.cpp index c06b25ae..d0c1ef85 100644 --- a/src/Nodes.cpp +++ b/src/Nodes.cpp @@ -1,13 +1,31 @@ -// Creating a node graph editor for ImGui -// Quick demo, not production code! This is more of a demo of how to use ImGui to create custom stuff. -// Better version by @daniel_collin here https://gist.github.com/emoon/b8ff4b4ce4f1b43e79f2 -// See https://github.com/ocornut/imgui/issues/306 -// v0.03: fixed grid offset issue, inverted sign of 'scrolling' -// Animated gif: https://cloud.githubusercontent.com/assets/8225057/9472357/c0263c04-4b4c-11e5-9fdf-2cd4f33f6582.gif +// https://github.com/CedricGuillemet/Imogen +// +// The MIT License(MIT) +// +// Copyright(c) 2018 Cedric Guillemet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// #include "imgui.h" #include "imgui_internal.h" -#include // fmodf +#include #include "Nodes.h" #include #include @@ -504,16 +522,6 @@ void NodeGraph(NodeGraphDelegate *delegate, bool enabled) NodeGraphUpdateEvaluationOrder(delegate); node_selected = -1; } - if (ImGui::MenuItem("Bake", NULL, false)) - { - delegate->Bake(node_selected); - } - /* - if (ImGui::MenuItem("Set as target", NULL, false)) - { - delegate->mBakeTargetIndex = node_selected; - } - */ } else { diff --git a/src/Nodes.h b/src/Nodes.h index a2749171..a1a54423 100644 --- a/src/Nodes.h +++ b/src/Nodes.h @@ -1,3 +1,28 @@ +// https://github.com/CedricGuillemet/Imogen +// +// The MIT License(MIT) +// +// Copyright(c) 2018 Cedric Guillemet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + #pragma once #include #include @@ -11,7 +36,6 @@ struct NodeGraphDelegate int mCategoriesCount; const char ** mCategories; - virtual void Bake(size_t index) = 0; virtual void UpdateEvaluationList(const std::vector nodeOrderList) = 0; virtual void AddLink(int InputIdx, int InputSlot, int OutputIdx, int OutputSlot) = 0; virtual void DelLink(int index, int slot) = 0; @@ -24,6 +48,7 @@ struct NodeGraphDelegate // node deleted virtual void DeleteNode(size_t index) = 0; + virtual void DoForce() = 0; virtual unsigned char *GetParamBlock(size_t index, size_t& paramBlockSize) = 0; virtual void SetParamBlock(size_t index, unsigned char* paramBlock) = 0; static const int MaxCon = 32; diff --git a/src/NodesDelegate.h b/src/NodesDelegate.h index 5e53ccd0..d486b124 100644 --- a/src/NodesDelegate.h +++ b/src/NodesDelegate.h @@ -1,22 +1,49 @@ +// https://github.com/CedricGuillemet/Imogen +// +// The MIT License(MIT) +// +// Copyright(c) 2018 Cedric Guillemet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + #pragma once #include "Nodes.h" #include "Evaluation.h" #include "curve.h" #include "Library.h" +#include "nfd.h" struct TileNodeEditGraphDelegate : public NodeGraphDelegate { TileNodeEditGraphDelegate(Evaluation& evaluation) : mEvaluation(evaluation) { - mCategoriesCount = 6; + mCategoriesCount = 7; static const char *categories[] = { "Transform", "Generator", "Material", "Blend", "Filter", - "Noise" }; + "Noise", + "File"}; mCategories = categories; } @@ -47,6 +74,9 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate Con_Angle4, Con_Enum, Con_Structure, + Con_FilenameRead, + Con_FilenameWrite, + Con_ForceEvaluate, Con_Any, }; @@ -86,7 +116,8 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate size_t index = mNodes.size(); ImogenNode node; - node.mEvaluationTarget = mEvaluation.AddEvaluationTarget(type, metaNodes[type].mName); + node.mEvaluationTarget = mEvaluation.AddEvaluation(type, metaNodes[type].mName); + node.mType = type; size_t paramsSize = ComputeParamMemSize(type); node.mParameters = malloc(paramsSize); @@ -124,6 +155,7 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate node.mEvaluationTarget--; } } + virtual const MetaNode* GetMetaNodes(int &metaNodeCount) { static const uint32_t hcTransform = IM_COL32(200, 200, 200, 255); @@ -133,9 +165,11 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate static const uint32_t hcFilter = IM_COL32(200, 200, 150, 255); static const uint32_t hcNoise = IM_COL32(150, 250, 150, 255); - metaNodeCount = 23; - static const MetaNode metaNodes[23] = { + metaNodeCount = 26; + + static const MetaNode metaNodes[26] = { + { "Circle", hcGenerator, 1 ,{ {} } @@ -147,7 +181,7 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate "Transform", hcTransform, 0 ,{ { "", (int)Con_Float4 } } ,{ { "", (int)Con_Float4 } } - ,{ { "Translate", (int)Con_Float2, 1.f,0.f,1.f,0.f, true },{ "Rotation", (int)Con_Angle },{ "Scale", (int)Con_Float } } + ,{ { "Translate", (int)Con_Float2, 1.f,0.f,1.f,0.f, true },{ "Scale", (int)Con_Float2 },{ "Rotation", (int)Con_Angle } } } , { @@ -206,7 +240,7 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate , { "LambertMaterial", hcMaterial, 2 - ,{ { "Diffuse", (int)Con_Float4 },{ "Normal", (int)Con_Float4 } } + ,{ { "Diffuse", (int)Con_Float4 },{ "Equirect sky", (int)Con_Float4 } } ,{ { "", (int)Con_Float4 } } ,{ { "view", (int)Con_Float2, 1.f,0.f,0.f,1.f } } } @@ -295,7 +329,7 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate , { "PBR", hcMaterial, 2 - ,{ { "Diffuse", (int)Con_Float4 },{ "Normal", (int)Con_Float4 },{ "Roughness", (int)Con_Float4 },{ "Displacement", (int)Con_Float4 } } + ,{ { "Diffuse", (int)Con_Float4 },{ "Normal", (int)Con_Float4 },{ "Roughness", (int)Con_Float4 },{ "Displacement", (int)Con_Float4 }, { "Equirect sky", (int)Con_Float4 } } ,{ { "", (int)Con_Float4 } } ,{ { "view", (int)Con_Float2, 1.f,0.f,0.f,1.f, true } } } @@ -317,6 +351,32 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate ,{ { "Min", (int)Con_Float4}, { "Max", (int)Con_Float4 } } } + , + { + "ImageRead", hcFilter, 6 + ,{ } + ,{ { "", (int)Con_Float4 } } + ,{ { "File name", (int)Con_FilenameRead } } + } + + , + { + "ImageWrite", hcFilter, 6 + ,{ { "", (int)Con_Float4 } } + ,{ } + ,{ { "File name", (int)Con_FilenameWrite },{ "Format", (int)Con_Enum, 0.f,0.f,0.f,0.f, false, "JPEG\0PNG\0TGA\0BMP\0HDR\0"},{ "Quality", (int)Con_Int } + ,{ "Width", (int)Con_Enum, 0.f,0.f,0.f,0.f, false, " 256\0 512\0 1024\0 2048\0 4096\0" } + ,{ "Height", (int)Con_Enum, 0.f,0.f,0.f,0.f, false, " 256\0 512\0 1024\0 2048\0 4096\0" } + ,{ "Export", (int)Con_ForceEvaluate } } + } + + , + { + "Thumbnail", hcFilter, 6 + ,{ { "", (int)Con_Float4 } } + ,{} + ,{ { "Make", (int)Con_ForceEvaluate } } + } }; return metaNodes; @@ -332,6 +392,7 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate int metaNodeCount; const MetaNode* metaNodes = GetMetaNodes(metaNodeCount); bool dirty = false; + bool forceEval = false; bool samplerDirty = false; ImogenNode& node = mNodes[index]; const MetaNode& currentMeta = metaNodes[node.mType]; @@ -440,9 +501,33 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate ((float*)paramBuffer)[2] = DegToRad(((float*)paramBuffer)[2]); ((float*)paramBuffer)[3] = DegToRad(((float*)paramBuffer)[3]); break; + case Con_FilenameWrite: + case Con_FilenameRead: + dirty |= ImGui::InputText("", (char*)paramBuffer, 1024); + ImGui::SameLine(); + if (ImGui::Button("...")) + { + nfdchar_t *outPath = NULL; + nfdresult_t result = (param->mType == Con_FilenameRead) ? NFD_OpenDialog(NULL, NULL, &outPath) : NFD_SaveDialog(NULL, NULL, &outPath); + + if (result == NFD_OKAY) + { + strcpy((char*)paramBuffer, outPath); + free(outPath); + dirty = true; + } + } + break; case Con_Enum: dirty |= ImGui::Combo(param->mName, (int*)paramBuffer, param->mEnumList); break; + case Con_ForceEvaluate: + if (ImGui::Button(param->mName)) + { + dirty = true; + forceEval = true; + } + break; } paramBuffer += ComputeParamMemSize(param->mType); } @@ -450,6 +535,38 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate //ImGui::End(); if (dirty) mEvaluation.SetEvaluationParameters(node.mEvaluationTarget, node.mParameters, node.mParametersSize); + if (forceEval) + mEvaluation.PerformEvaluationForNode(node.mEvaluationTarget, 256, 256, true); + + } + + virtual void DoForce() + { + int metaNodeCount; + const MetaNode* metaNodes = GetMetaNodes(metaNodeCount); + bool dirty = false; + bool forceEval = false; + bool samplerDirty = false; + for (size_t index = 0; index < mNodes.size(); index++) + { + ImogenNode& node = mNodes[index]; + const MetaNode& currentMeta = metaNodes[node.mType]; + + const NodeGraphDelegate::Con * param = currentMeta.mParams; + unsigned char *paramBuffer = (unsigned char*)node.mParameters; + for (int i = 0; i < MaxCon; i++, param++) + { + if (!param->mName) + break; + if (param->mType == Con_ForceEvaluate) + { + dirty = true; + forceEval = true; + } + } + if (forceEval) + mEvaluation.PerformEvaluationForNode(node.mEvaluationTarget, 256, 256, true); + } } void InvalidateParameters() @@ -543,7 +660,13 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate case Con_Int: res += sizeof(int); break; - + case Con_FilenameRead: + case Con_FilenameWrite: + res += 1024; + break; + case Con_ForceEvaluate: + res += 0; + break; } return res; } @@ -552,10 +675,5 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate { mEvaluation.SetEvaluationOrder(nodeOrderList); } - - virtual void Bake(size_t index) - { - mEvaluation.Bake("bakedTexture.png", index, 4096, 4096); - } }; diff --git a/src/main.cpp b/src/main.cpp index f586f2ac..57ff9ed3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,16 +1,39 @@ +// https://github.com/CedricGuillemet/Imogen +// +// The MIT License(MIT) +// +// Copyright(c) 2018 Cedric Guillemet +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + #include "imgui.h" #include "imgui_internal.h" #include "imgui_impl_sdl.h" #include "imgui_impl_opengl3.h" -#include #include -#include // Initialize with gl3wInit() -#include -#include +#include #include "Nodes.h" #include "NodesDelegate.h" #include "Evaluation.h" #include "Imogen.h" +#include "TaskScheduler.h" int Log(const char *szFormat, ...) { @@ -31,8 +54,15 @@ int Log(const char *szFormat, ...) return 0; } +Evaluation gEvaluation; +Library library; +Imogen imogen; +enki::TaskScheduler g_TS; + int main(int, char**) { + g_TS.Initialize(); + // Setup SDL if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) { @@ -94,16 +124,15 @@ int main(int, char**) ImGui::StyleColorsDark(); static const char* libraryFilename = "library.dat"; - Library library; + LoadLib(&library, libraryFilename); - Evaluation evaluation; - Imogen imogen; + imogen.Init(); - evaluation.SetEvaluationGLSL(imogen.shaderFileNames); - evaluation.LoadEquiRect("studio017PoT.png"); + gEvaluation.Init(); + gEvaluation.SetEvaluators(imogen.mEvaluatorFiles); - TileNodeEditGraphDelegate nodeGraphDelegate(evaluation); + TileNodeEditGraphDelegate nodeGraphDelegate(gEvaluation); // Main loop bool done = false; @@ -124,32 +153,36 @@ int main(int, char**) ImGui_ImplSDL2_NewFrame(window); ImGui::NewFrame(); - imogen.Show(library, nodeGraphDelegate, evaluation); + imogen.Show(library, nodeGraphDelegate, gEvaluation); - evaluation.RunEvaluation(); + gEvaluation.RunEvaluation(256, 256, false); // render everything - glClearColor(0.45f, 0.4f, 0.4f, 1.f); + glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); + glClearColor(0., 0., 0., 0.); glClear(GL_COLOR_BUFFER_BIT); - ImGui::Render(); + ImGui::Render(); SDL_GL_MakeCurrent(window, gl_context); - glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); - glClearColor(0.,0.,0.,0.); - glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + g_TS.RunPinnedTasks(); SDL_GL_SwapWindow(window); } SaveLib(&library, libraryFilename); + gEvaluation.Finish(); + // Cleanup ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); + imogen.Finish(); // keep dock being saved + SDL_GL_DeleteContext(gl_context); SDL_DestroyWindow(window); SDL_Quit(); + g_TS.WaitforAllAndShutdown(); return 0; } \ No newline at end of file