diff --git a/.gitmodules b/.gitmodules index d22a5f0..f9ba546 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ -[submodule "contrib/imgui"] - path = contrib/imgui - url = https://github.com/ocornut/imgui.git [submodule "contrib/assimp"] path = contrib/assimp url = https://github.com/assimp/assimp.git +[submodule "contrib/osre"] + path = contrib/osre + url = https://github.com/kimkulling/osre.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ff9b75..e753a65 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,14 +10,14 @@ set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_HOME_DIRECTORY}/lib ) set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_HOME_DIRECTORY}/lib ) set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_HOME_DIRECTORY}/bin ) -add_subdirectory(contrib/assimp) +add_subdirectory(contrib/osre) INCLUDE_DIRECTORIES( BEFORE - ${PROJECT_SOURCE_DIR}/contrib/imgui - ${PROJECT_SOURCE_DIR}/contrib/assimp/include ./ - ./src - ${SDL2_INCLUDE_DIRS} + src + contrib/osre/src/Engine + contrib/osre/contrib/cppcore/include + ${SDL2_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR} ) @@ -25,45 +25,17 @@ link_directories( ${SDL2_LIBRARIES} ) -set(assimpview_platform_backend - contrib/imgui/backends/imgui_impl_sdl2.cpp - contrib/imgui/backends/imgui_impl_sdl2.h - contrib/imgui/backends/imgui_impl_opengl3.cpp - contrib/imgui/backends/imgui_impl_opengl3.h -) - -set(assimpview_imgui_src - contrib/imgui/imconfig.h - contrib/imgui/imgui.cpp - contrib/imgui/imgui.h - contrib/imgui/imgui_demo.cpp - contrib/imgui/imgui_draw.cpp - contrib/imgui/imgui_internal.h - contrib/imgui/imgui_tables.cpp - contrib/imgui/imgui_widgets.cpp - contrib/imgui/imstb_rectpack.h - contrib/imgui/imstb_textedit.h - contrib/imgui/imstb_truetype.h - ${assimpview_platform_backend} -) - set(assimpview_main_src - src/MainRenderView.cpp - src/MainRenderView.h - src/AssimpViewerApp.cpp - src/AssimpViewerApp.h + src/ModelLoading.cpp src/SceneData.h - src/main.cpp ) -SOURCE_GROUP(ImGui\\ FILES ${assimpview_imgui_src}) SOURCE_GROUP(Main\\ FILES ${assimpview_main_src}) ADD_EXECUTABLE( assimp_view - ${assimpview_imgui_src} ${assimpview_main_src} ) target_link_libraries(assimp_view - PRIVATE SDL2::SDL2 assimp GL + PRIVATE SDL2::SDL2 assimp GL osre ) diff --git a/README.md b/README.md index 83026ec..bbf16e9 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ First, you have to check out the code Now change into the repo folder and start the build: -> cd osre +> cd assimp_view > cmake CMakeLists.txt > cmake --build . diff --git a/contrib/imgui b/contrib/imgui deleted file mode 160000 index 9ec299e..0000000 --- a/contrib/imgui +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9ec299ebe310678c77f5dd076cdf295db1e80ccb diff --git a/contrib/osre b/contrib/osre new file mode 160000 index 0000000..d9d7a07 --- /dev/null +++ b/contrib/osre @@ -0,0 +1 @@ +Subproject commit d9d7a07197d3e229e3dff82d871e36923ce22793 diff --git a/contrib/sokol/sokol_app.h b/contrib/sokol/sokol_app.h deleted file mode 100644 index 5455015..0000000 --- a/contrib/sokol/sokol_app.h +++ /dev/null @@ -1,11133 +0,0 @@ -#if defined(SOKOL_IMPL) && !defined(SOKOL_APP_IMPL) -#define SOKOL_APP_IMPL -#endif -#ifndef SOKOL_APP_INCLUDED -/* - sokol_app.h -- cross-platform application wrapper - - Project URL: https://github.com/floooh/sokol - - Do this: - #define SOKOL_IMPL or - #define SOKOL_APP_IMPL - before you include this file in *one* C or C++ file to create the - implementation. - - In the same place define one of the following to select the 3D-API - which should be initialized by sokol_app.h (this must also match - the backend selected for sokol_gfx.h if both are used in the same - project): - - #define SOKOL_GLCORE33 - #define SOKOL_GLES2 - #define SOKOL_GLES3 - #define SOKOL_D3D11 - #define SOKOL_METAL - #define SOKOL_WGPU - - Optionally provide the following defines with your own implementations: - - SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) - SOKOL_LOG(msg) - your own logging function (default: puts(msg)) - SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) - SOKOL_ABORT() - called after an unrecoverable error (default: abort()) - SOKOL_WIN32_FORCE_MAIN - define this on Win32 to use a main() entry point instead of WinMain - SOKOL_NO_ENTRY - define this if sokol_app.h shouldn't "hijack" the main() function - SOKOL_APP_API_DECL - public function declaration prefix (default: extern) - SOKOL_API_DECL - same as SOKOL_APP_API_DECL - SOKOL_API_IMPL - public function implementation prefix (default: -) - SOKOL_CALLOC - your own calloc function (default: calloc(n, s)) - SOKOL_FREE - your own free function (default: free(p)) - - Optionally define the following to force debug checks and validations - even in release mode: - - SOKOL_DEBUG - by default this is defined if _DEBUG is defined - - If sokol_app.h is compiled as a DLL, define the following before - including the declaration or implementation: - - SOKOL_DLL - - On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as __declspec(dllexport) - or __declspec(dllimport) as needed. - - For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp - - Portions of the Windows and Linux GL initialization, event-, icon- etc... code - have been taken from GLFW (http://www.glfw.org/) - - iOS onscreen keyboard support 'inspired' by libgdx. - - Link with the following system libraries: - - - on macOS with Metal: Cocoa, QuartzCore, Metal, MetalKit - - on macOS with GL: Cocoa, QuartzCore, OpenGL - - on iOS with Metal: Foundation, UIKit, Metal, MetalKit - - on iOS with GL: Foundation, UIKit, OpenGLES, GLKit - - on Linux: X11, Xi, Xcursor, GL, dl, pthread, m(?) - - on Android: GLESv3, EGL, log, android - - on Windows with the MSVC or Clang toolchains: no action needed, libs are defined in-source via pragma-comment-lib - - on Windows with MINGW/MSYS2 gcc: compile with '-mwin32' so that _WIN32 is defined - - link with the following libs: -lkernel32 -luser32 -lshell32 - - additionally with the GL backend: -lgdi32 - - additionally with the D3D11 backend: -ld3d11 -ldxgi - - On Linux, you also need to use the -pthread compiler and linker option, otherwise weird - things will happen, see here for details: https://github.com/floooh/sokol/issues/376 - - Building for UWP requires a recent Visual Studio toolchain and Windows SDK - (at least VS2019 and Windows SDK 10.0.19041.0). When the UWP backend is - selected, the sokol_app.h implementation must be compiled as C++17. - - On macOS and iOS, the implementation must be compiled as Objective-C. - - FEATURE OVERVIEW - ================ - sokol_app.h provides a minimalistic cross-platform API which - implements the 'application-wrapper' parts of a 3D application: - - - a common application entry function - - creates a window and 3D-API context/device with a 'default framebuffer' - - makes the rendered frame visible - - provides keyboard-, mouse- and low-level touch-events - - platforms: MacOS, iOS, HTML5, Win32, Linux, Android (TODO: RaspberryPi) - - 3D-APIs: Metal, D3D11, GL3.2, GLES2, GLES3, WebGL, WebGL2 - - FEATURE/PLATFORM MATRIX - ======================= - | Windows | macOS | Linux | iOS | Android | UWP | Raspi | HTML5 - --------------------+---------+-------+-------+-------+---------+------+-------+------- - gl 3.x | YES | YES | YES | --- | --- | --- | --- | --- - gles2/webgl | --- | --- | --- | YES | YES | --- | TODO | YES - gles3/webgl2 | --- | --- | --- | YES | YES | --- | --- | YES - metal | --- | YES | --- | YES | --- | --- | --- | --- - d3d11 | YES | --- | --- | --- | --- | YES | --- | --- - KEY_DOWN | YES | YES | YES | SOME | TODO | YES | TODO | YES - KEY_UP | YES | YES | YES | SOME | TODO | YES | TODO | YES - CHAR | YES | YES | YES | YES | TODO | YES | TODO | YES - MOUSE_DOWN | YES | YES | YES | --- | --- | YES | TODO | YES - MOUSE_UP | YES | YES | YES | --- | --- | YES | TODO | YES - MOUSE_SCROLL | YES | YES | YES | --- | --- | YES | TODO | YES - MOUSE_MOVE | YES | YES | YES | --- | --- | YES | TODO | YES - MOUSE_ENTER | YES | YES | YES | --- | --- | YES | TODO | YES - MOUSE_LEAVE | YES | YES | YES | --- | --- | YES | TODO | YES - TOUCHES_BEGAN | --- | --- | --- | YES | YES | TODO | --- | YES - TOUCHES_MOVED | --- | --- | --- | YES | YES | TODO | --- | YES - TOUCHES_ENDED | --- | --- | --- | YES | YES | TODO | --- | YES - TOUCHES_CANCELLED | --- | --- | --- | YES | YES | TODO | --- | YES - RESIZED | YES | YES | YES | YES | YES | YES | --- | YES - ICONIFIED | YES | YES | YES | --- | --- | YES | --- | --- - RESTORED | YES | YES | YES | --- | --- | YES | --- | --- - FOCUSED | YES | YES | YES | --- | --- | --- | --- | YES - UNFOCUSED | YES | YES | YES | --- | --- | --- | --- | YES - SUSPENDED | --- | --- | --- | YES | YES | YES | --- | TODO - RESUMED | --- | --- | --- | YES | YES | YES | --- | TODO - QUIT_REQUESTED | YES | YES | YES | --- | --- | --- | TODO | YES - UPDATE_CURSOR | YES | YES | TODO | --- | --- | TODO | --- | TODO - IME | TODO | TODO? | TODO | ??? | TODO | --- | ??? | ??? - key repeat flag | YES | YES | YES | --- | --- | YES | TODO | YES - windowed | YES | YES | YES | --- | --- | YES | TODO | YES - fullscreen | YES | YES | YES | YES | YES | YES | TODO | --- - mouse hide | YES | YES | YES | --- | --- | YES | TODO | TODO - mouse lock | YES | YES | YES | --- | --- | TODO | TODO | YES - screen keyboard | --- | --- | --- | YES | TODO | TODO | --- | YES - swap interval | YES | YES | YES | YES | TODO | --- | TODO | YES - high-dpi | YES | YES | TODO | YES | YES | YES | TODO | YES - clipboard | YES | YES | TODO | --- | --- | TODO | --- | YES - MSAA | YES | YES | YES | YES | YES | TODO | TODO | YES - drag'n'drop | YES | YES | YES | --- | --- | TODO | TODO | YES - window icon | YES | YES(1)| YES | --- | --- | TODO | TODO | YES - - (1) macOS has no regular window icons, instead the dock icon is changed - - STEP BY STEP - ============ - --- Add a sokol_main() function to your code which returns a sapp_desc structure - with initialization parameters and callback function pointers. This - function is called very early, usually at the start of the - platform's entry function (e.g. main or WinMain). You should do as - little as possible here, since the rest of your code might be called - from another thread (this depends on the platform): - - sapp_desc sokol_main(int argc, char* argv[]) { - return (sapp_desc) { - .width = 640, - .height = 480, - .init_cb = my_init_func, - .frame_cb = my_frame_func, - .cleanup_cb = my_cleanup_func, - .event_cb = my_event_func, - ... - }; - } - - There are many more setup parameters, but these are the most important. - For a complete list search for the sapp_desc structure declaration - below. - - DO NOT call any sokol-app function from inside sokol_main(), since - sokol-app will not be initialized at this point. - - The .width and .height parameters are the preferred size of the 3D - rendering canvas. The actual size may differ from this depending on - platform and other circumstances. Also the canvas size may change at - any time (for instance when the user resizes the application window, - or rotates the mobile device). - - All provided function callbacks will be called from the same thread, - but this may be different from the thread where sokol_main() was called. - - .init_cb (void (*)(void)) - This function is called once after the application window, - 3D rendering context and swap chain have been created. The - function takes no arguments and has no return value. - .frame_cb (void (*)(void)) - This is the per-frame callback, which is usually called 60 - times per second. This is where your application would update - most of its state and perform all rendering. - .cleanup_cb (void (*)(void)) - The cleanup callback is called once right before the application - quits. - .event_cb (void (*)(const sapp_event* event)) - The event callback is mainly for input handling, but is also - used to communicate other types of events to the application. Keep the - event_cb struct member zero-initialized if your application doesn't require - event handling. - .fail_cb (void (*)(const char* msg)) - The fail callback is called when a fatal error is encountered - during start which doesn't allow the program to continue. - Providing a callback here gives you a chance to show an error message - to the user. The default behaviour is SOKOL_LOG(msg) - - As you can see, those 'standard callbacks' don't have a user_data - argument, so any data that needs to be preserved between callbacks - must live in global variables. If keeping state in global variables - is not an option, there's an alternative set of callbacks with - an additional user_data pointer argument: - - .user_data (void*) - The user-data argument for the callbacks below - .init_userdata_cb (void (*)(void* user_data)) - .frame_userdata_cb (void (*)(void* user_data)) - .cleanup_userdata_cb (void (*)(void* user_data)) - .event_userdata_cb (void(*)(const sapp_event* event, void* user_data)) - .fail_userdata_cb (void(*)(const char* msg, void* user_data)) - These are the user-data versions of the callback functions. You - can mix those with the standard callbacks that don't have the - user_data argument. - - The function sapp_userdata() can be used to query the user_data - pointer provided in the sapp_desc struct. - - You can also call sapp_query_desc() to get a copy of the - original sapp_desc structure. - - NOTE that there's also an alternative compile mode where sokol_app.h - doesn't "hijack" the main() function. Search below for SOKOL_NO_ENTRY. - - --- Implement the initialization callback function (init_cb), this is called - once after the rendering surface, 3D API and swap chain have been - initialized by sokol_app. All sokol-app functions can be called - from inside the initialization callback, the most useful functions - at this point are: - - int sapp_width(void) - int sapp_height(void) - Returns the current width and height of the default framebuffer in pixels, - this may change from one frame to the next, and it may be different - from the initial size provided in the sapp_desc struct. - - float sapp_widthf(void) - float sapp_heightf(void) - These are alternatives to sapp_width() and sapp_height() which return - the default framebuffer size as float values instead of integer. This - may help to prevent casting back and forth between int and float - in more strongly typed languages than C and C++. - - int sapp_color_format(void) - int sapp_depth_format(void) - The color and depth-stencil pixelformats of the default framebuffer, - as integer values which are compatible with sokol-gfx's - sg_pixel_format enum (so that they can be plugged directly in places - where sg_pixel_format is expected). Possible values are: - - 23 == SG_PIXELFORMAT_RGBA8 - 27 == SG_PIXELFORMAT_BGRA8 - 41 == SG_PIXELFORMAT_DEPTH - 42 == SG_PIXELFORMAT_DEPTH_STENCIL - - int sapp_sample_count(void) - Return the MSAA sample count of the default framebuffer. - - bool sapp_gles2(void) - Returns true if a GLES2 or WebGL context has been created. This - is useful when a GLES3/WebGL2 context was requested but is not - available so that sokol_app.h had to fallback to GLES2/WebGL. - - const void* sapp_metal_get_device(void) - const void* sapp_metal_get_renderpass_descriptor(void) - const void* sapp_metal_get_drawable(void) - If the Metal backend has been selected, these functions return pointers - to various Metal API objects required for rendering, otherwise - they return a null pointer. These void pointers are actually - Objective-C ids converted with a (ARC) __bridge cast so that - the ids can be tunnel through C code. Also note that the returned - pointers to the renderpass-descriptor and drawable may change from one - frame to the next, only the Metal device object is guaranteed to - stay the same. - - const void* sapp_macos_get_window(void) - On macOS, get the NSWindow object pointer, otherwise a null pointer. - Before being used as Objective-C object, the void* must be converted - back with a (ARC) __bridge cast. - - const void* sapp_ios_get_window(void) - On iOS, get the UIWindow object pointer, otherwise a null pointer. - Before being used as Objective-C object, the void* must be converted - back with a (ARC) __bridge cast. - - const void* sapp_win32_get_hwnd(void) - On Windows, get the window's HWND, otherwise a null pointer. The - HWND has been cast to a void pointer in order to be tunneled - through code which doesn't include Windows.h. - - const void* sapp_d3d11_get_device(void) - const void* sapp_d3d11_get_device_context(void) - const void* sapp_d3d11_get_render_target_view(void) - const void* sapp_d3d11_get_depth_stencil_view(void) - Similar to the sapp_metal_* functions, the sapp_d3d11_* functions - return pointers to D3D11 API objects required for rendering, - only if the D3D11 backend has been selected. Otherwise they - return a null pointer. Note that the returned pointers to the - render-target-view and depth-stencil-view may change from one - frame to the next! - - const void* sapp_wgpu_get_device(void) - const void* sapp_wgpu_get_render_view(void) - const void* sapp_wgpu_get_resolve_view(void) - const void* sapp_wgpu_get_depth_stencil_view(void) - These are the WebGPU-specific functions to get the WebGPU - objects and values required for rendering. If sokol_app.h - is not compiled with SOKOL_WGPU, these functions return null. - - const void* sapp_android_get_native_activity(void); - On Android, get the native activity ANativeActivity pointer, otherwise - a null pointer. - - --- Implement the frame-callback function, this function will be called - on the same thread as the init callback, but might be on a different - thread than the sokol_main() function. Note that the size of - the rendering framebuffer might have changed since the frame callback - was called last. Call the functions sapp_width() and sapp_height() - each frame to get the current size. - - --- Optionally implement the event-callback to handle input events. - sokol-app provides the following type of input events: - - a 'virtual key' was pressed down or released - - a single text character was entered (provided as UTF-32 code point) - - a mouse button was pressed down or released (left, right, middle) - - mouse-wheel or 2D scrolling events - - the mouse was moved - - the mouse has entered or left the application window boundaries - - low-level, portable multi-touch events (began, moved, ended, cancelled) - - the application window was resized, iconified or restored - - the application was suspended or restored (on mobile platforms) - - the user or application code has asked to quit the application - - a string was pasted to the system clipboard - - one or more files have been dropped onto the application window - - To explicitly 'consume' an event and prevent that the event is - forwarded for further handling to the operating system, call - sapp_consume_event() from inside the event handler (NOTE that - this behaviour is currently only implemented for some HTML5 - events, support for other platforms and event types will - be added as needed, please open a github ticket and/or provide - a PR if needed). - - NOTE: Do *not* call any 3D API rendering functions in the event - callback function, since the 3D API context may not be active when the - event callback is called (it may work on some platforms and 3D APIs, - but not others, and the exact behaviour may change between - sokol-app versions). - - --- Implement the cleanup-callback function, this is called once - after the user quits the application (see the section - "APPLICATION QUIT" for detailed information on quitting - behaviour, and how to intercept a pending quit - for instance to show a - "Really Quit?" dialog box). Note that the cleanup-callback isn't - guaranteed to be called on the web and mobile platforms. - - MOUSE LOCK (AKA POINTER LOCK, AKA MOUSE CAPTURE) - ================================================ - In normal mouse mode, no mouse movement events are reported when the - mouse leaves the windows client area or hits the screen border (whether - it's one or the other depends on the platform), and the mouse move events - (SAPP_EVENTTYPE_MOUSE_MOVE) contain absolute mouse positions in - framebuffer pixels in the sapp_event items mouse_x and mouse_y, and - relative movement in framebuffer pixels in the sapp_event items mouse_dx - and mouse_dy. - - To get continuous mouse movement (also when the mouse leaves the window - client area or hits the screen border), activate mouse-lock mode - by calling: - - sapp_lock_mouse(true) - - When mouse lock is activated, the mouse pointer is hidden, the - reported absolute mouse position (sapp_event.mouse_x/y) appears - frozen, and the relative mouse movement in sapp_event.mouse_dx/dy - no longer has a direct relation to framebuffer pixels but instead - uses "raw mouse input" (what "raw mouse input" exactly means also - differs by platform). - - To deactivate mouse lock and return to normal mouse mode, call - - sapp_lock_mouse(false) - - And finally, to check if mouse lock is currently active, call - - if (sapp_mouse_locked()) { ... } - - On native platforms, the sapp_lock_mouse() and sapp_mouse_locked() - functions work as expected (mouse lock is activated or deactivated - immediately when sapp_lock_mouse() is called, and sapp_mouse_locked() - also immediately returns the new state after sapp_lock_mouse() - is called. - - On the web platform, sapp_lock_mouse() and sapp_mouse_locked() behave - differently, as dictated by the limitations of the HTML5 Pointer Lock API: - - - sapp_lock_mouse(true) can be called at any time, but it will - only take effect in a 'short-lived input event handler of a specific - type', meaning when one of the following events happens: - - SAPP_EVENTTYPE_MOUSE_DOWN - - SAPP_EVENTTYPE_MOUSE_UP - - SAPP_EVENTTYPE_MOUSE_SCROLL - - SAPP_EVENTYTPE_KEY_UP - - SAPP_EVENTTYPE_KEY_DOWN - - The mouse lock/unlock action on the web platform is asynchronous, - this means that sapp_mouse_locked() won't immediately return - the new status after calling sapp_lock_mouse(), instead the - reported status will only change when the pointer lock has actually - been activated or deactivated in the browser. - - On the web, mouse lock can be deactivated by the user at any time - by pressing the Esc key. When this happens, sokol_app.h behaves - the same as if sapp_lock_mouse(false) is called. - - For things like camera manipulation it's most straightforward to lock - and unlock the mouse right from the sokol_app.h event handler, for - instance the following code enters and leaves mouse lock when the - left mouse button is pressed and released, and then uses the relative - movement information to manipulate a camera (taken from the - cgltf-sapp.c sample in the sokol-samples repository - at https://github.com/floooh/sokol-samples): - - static void input(const sapp_event* ev) { - switch (ev->type) { - case SAPP_EVENTTYPE_MOUSE_DOWN: - if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) { - sapp_lock_mouse(true); - } - break; - - case SAPP_EVENTTYPE_MOUSE_UP: - if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) { - sapp_lock_mouse(false); - } - break; - - case SAPP_EVENTTYPE_MOUSE_MOVE: - if (sapp_mouse_locked()) { - cam_orbit(&state.camera, ev->mouse_dx * 0.25f, ev->mouse_dy * 0.25f); - } - break; - - default: - break; - } - } - - CLIPBOARD SUPPORT - ================= - Applications can send and receive UTF-8 encoded text data from and to the - system clipboard. By default, clipboard support is disabled and - must be enabled at startup via the following sapp_desc struct - members: - - sapp_desc.enable_clipboard - set to true to enable clipboard support - sapp_desc.clipboard_size - size of the internal clipboard buffer in bytes - - Enabling the clipboard will dynamically allocate a clipboard buffer - for UTF-8 encoded text data of the requested size in bytes, the default - size is 8 KBytes. Strings that don't fit into the clipboard buffer - (including the terminating zero) will be silently clipped, so it's - important that you provide a big enough clipboard size for your - use case. - - To send data to the clipboard, call sapp_set_clipboard_string() with - a pointer to an UTF-8 encoded, null-terminated C-string. - - NOTE that on the HTML5 platform, sapp_set_clipboard_string() must be - called from inside a 'short-lived event handler', and there are a few - other HTML5-specific caveats to workaround. You'll basically have to - tinker until it works in all browsers :/ (maybe the situation will - improve when all browsers agree on and implement the new - HTML5 navigator.clipboard API). - - To get data from the clipboard, check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED - event in your event handler function, and then call sapp_get_clipboard_string() - to obtain the pasted UTF-8 encoded text. - - NOTE that behaviour of sapp_get_clipboard_string() is slightly different - depending on platform: - - - on the HTML5 platform, the internal clipboard buffer will only be updated - right before the SAPP_EVENTTYPE_CLIPBOARD_PASTED event is sent, - and sapp_get_clipboard_string() will simply return the current content - of the clipboard buffer - - on 'native' platforms, the call to sapp_get_clipboard_string() will - update the internal clipboard buffer with the most recent data - from the system clipboard - - Portable code should check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED event, - and then call sapp_get_clipboard_string() right in the event handler. - - The SAPP_EVENTTYPE_CLIPBOARD_PASTED event will be generated by sokol-app - as follows: - - - on macOS: when the Cmd+V key is pressed down - - on HTML5: when the browser sends a 'paste' event to the global 'window' object - - on all other platforms: when the Ctrl+V key is pressed down - - DRAG AND DROP SUPPORT - ===================== - PLEASE NOTE: the drag'n'drop feature works differently on WASM/HTML5 - and on the native desktop platforms (Win32, Linux and macOS) because - of security-related restrictions in the HTML5 drag'n'drop API. The - WASM/HTML5 specifics are described at the end of this documentation - section: - - Like clipboard support, drag'n'drop support must be explicitly enabled - at startup in the sapp_desc struct. - - sapp_desc sokol_main() { - return (sapp_desc) { - .enable_dragndrop = true, // default is false - ... - }; - } - - You can also adjust the maximum number of files that are accepted - in a drop operation, and the maximum path length in bytes if needed: - - sapp_desc sokol_main() { - return (sapp_desc) { - .enable_dragndrop = true, // default is false - .max_dropped_files = 8, // default is 1 - .max_dropped_file_path_length = 8192, // in bytes, default is 2048 - ... - }; - } - - When drag'n'drop is enabled, the event callback will be invoked with an - event of type SAPP_EVENTTYPE_FILES_DROPPED whenever the user drops files on - the application window. - - After the SAPP_EVENTTYPE_FILES_DROPPED is received, you can query the - number of dropped files, and their absolute paths by calling separate - functions: - - void on_event(const sapp_event* ev) { - if (ev->type == SAPP_EVENTTYPE_FILES_DROPPED) { - - // the mouse position where the drop happened - float x = ev->mouse_x; - float y = ev->mouse_y; - - // get the number of files and their paths like this: - const int num_dropped_files = sapp_get_num_dropped_files(); - for (int i = 0; i < num_dropped_files; i++) { - const char* path = sapp_get_dropped_file_path(i); - ... - } - } - } - - The returned file paths are UTF-8 encoded strings. - - You can call sapp_get_num_dropped_files() and sapp_get_dropped_file_path() - anywhere, also outside the event handler callback, but be aware that the - file path strings will be overwritten with the next drop operation. - - In any case, sapp_get_dropped_file_path() will never return a null pointer, - instead an empty string "" will be returned if the drag'n'drop feature - hasn't been enabled, the last drop-operation failed, or the file path index - is out of range. - - Drag'n'drop caveats: - - - if more files are dropped in a single drop-action - than sapp_desc.max_dropped_files, the additional - files will be silently ignored - - if any of the file paths is longer than - sapp_desc.max_dropped_file_path_length (in number of bytes, after UTF-8 - encoding) the entire drop operation will be silently ignored (this - needs some sort of error feedback in the future) - - no mouse positions are reported while the drag is in - process, this may change in the future - - Drag'n'drop on HTML5/WASM: - - The HTML5 drag'n'drop API doesn't return file paths, but instead - black-box 'file objects' which must be used to load the content - of dropped files. This is the reason why sokol_app.h adds two - HTML5-specific functions to the drag'n'drop API: - - uint32_t sapp_html5_get_dropped_file_size(int index) - Returns the size in bytes of a dropped file. - - void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request) - Asynchronously loads the content of a dropped file into a - provided memory buffer (which must be big enough to hold - the file content) - - To start loading the first dropped file after an SAPP_EVENTTYPE_FILES_DROPPED - event is received: - - sapp_html5_fetch_dropped_file(&(sapp_html5_fetch_request){ - .dropped_file_index = 0, - .callback = fetch_cb - .buffer_ptr = buf, - .buffer_size = buf_size, - .user_data = ... - }); - - Make sure that the memory pointed to by 'buf' stays valid until the - callback function is called! - - As result of the asynchronous loading operation (no matter if succeeded or - failed) the 'fetch_cb' function will be called: - - void fetch_cb(const sapp_html5_fetch_response* response) { - // IMPORTANT: check if the loading operation actually succeeded: - if (response->succeeded) { - // the size of the loaded file: - const uint32_t num_bytes = response->fetched_size; - // and the pointer to the data (same as 'buf' in the fetch-call): - const void* ptr = response->buffer_ptr; - } - else { - // on error check the error code: - switch (response->error_code) { - case SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL: - ... - break; - case SAPP_HTML5_FETCH_ERROR_OTHER: - ... - break; - } - } - } - - Check the droptest-sapp example for a real-world example which works - both on native platforms and the web: - - https://github.com/floooh/sokol-samples/blob/master/sapp/droptest-sapp.c - - HIGH-DPI RENDERING - ================== - You can set the sapp_desc.high_dpi flag during initialization to request - a full-resolution framebuffer on HighDPI displays. The default behaviour - is sapp_desc.high_dpi=false, this means that the application will - render to a lower-resolution framebuffer on HighDPI displays and the - rendered content will be upscaled by the window system composer. - - In a HighDPI scenario, you still request the same window size during - sokol_main(), but the framebuffer sizes returned by sapp_width() - and sapp_height() will be scaled up according to the DPI scaling - ratio. You can also get a DPI scaling factor with the function - sapp_dpi_scale(). - - Here's an example on a Mac with Retina display: - - sapp_desc sokol_main() { - return (sapp_desc) { - .width = 640, - .height = 480, - .high_dpi = true, - ... - }; - } - - The functions sapp_width(), sapp_height() and sapp_dpi_scale() will - return the following values: - - sapp_width -> 1280 - sapp_height -> 960 - sapp_dpi_scale -> 2.0 - - If the high_dpi flag is false, or you're not running on a Retina display, - the values would be: - - sapp_width -> 640 - sapp_height -> 480 - sapp_dpi_scale -> 1.0 - - APPLICATION QUIT - ================ - Without special quit handling, a sokol_app.h application will quit - 'gracefully' when the user clicks the window close-button unless a - platform's application model prevents this (e.g. on web or mobile). - 'Graceful exit' means that the application-provided cleanup callback will - be called before the application quits. - - On native desktop platforms sokol_app.h provides more control over the - application-quit-process. It's possible to initiate a 'programmatic quit' - from the application code, and a quit initiated by the application user can - be intercepted (for instance to show a custom dialog box). - - This 'programmatic quit protocol' is implemented through 3 functions - and 1 event: - - - sapp_quit(): This function simply quits the application without - giving the user a chance to intervene. Usually this might - be called when the user clicks the 'Ok' button in a 'Really Quit?' - dialog box - - sapp_request_quit(): Calling sapp_request_quit() will send the - event SAPP_EVENTTYPE_QUIT_REQUESTED to the applications event handler - callback, giving the user code a chance to intervene and cancel the - pending quit process (for instance to show a 'Really Quit?' dialog - box). If the event handler callback does nothing, the application - will be quit as usual. To prevent this, call the function - sapp_cancel_quit() from inside the event handler. - - sapp_cancel_quit(): Cancels a pending quit request, either initiated - by the user clicking the window close button, or programmatically - by calling sapp_request_quit(). The only place where calling this - function makes sense is from inside the event handler callback when - the SAPP_EVENTTYPE_QUIT_REQUESTED event has been received. - - SAPP_EVENTTYPE_QUIT_REQUESTED: this event is sent when the user - clicks the window's close button or application code calls the - sapp_request_quit() function. The event handler callback code can handle - this event by calling sapp_cancel_quit() to cancel the quit. - If the event is ignored, the application will quit as usual. - - On the web platform, the quit behaviour differs from native platforms, - because of web-specific restrictions: - - A `programmatic quit` initiated by calling sapp_quit() or - sapp_request_quit() will work as described above: the cleanup callback is - called, platform-specific cleanup is performed (on the web - this means that JS event handlers are unregisters), and then - the request-animation-loop will be exited. However that's all. The - web page itself will continue to exist (e.g. it's not possible to - programmatically close the browser tab). - - On the web it's also not possible to run custom code when the user - closes a brower tab, so it's not possible to prevent this with a - fancy custom dialog box. - - Instead the standard "Leave Site?" dialog box can be activated (or - deactivated) with the following function: - - sapp_html5_ask_leave_site(bool ask); - - The initial state of the associated internal flag can be provided - at startup via sapp_desc.html5_ask_leave_site. - - This feature should only be used sparingly in critical situations - for - instance when the user would loose data - since popping up modal dialog - boxes is considered quite rude in the web world. Note that there's no way - to customize the content of this dialog box or run any code as a result - of the user's decision. Also note that the user must have interacted with - the site before the dialog box will appear. These are all security measures - to prevent fishing. - - The Dear ImGui HighDPI sample contains example code of how to - implement a 'Really Quit?' dialog box with Dear ImGui (native desktop - platforms only), and for showing the hardwired "Leave Site?" dialog box - when running on the web platform: - - https://floooh.github.io/sokol-html5/wasm/imgui-highdpi-sapp.html - - FULLSCREEN - ========== - If the sapp_desc.fullscreen flag is true, sokol-app will try to create - a fullscreen window on platforms with a 'proper' window system - (mobile devices will always use fullscreen). The implementation details - depend on the target platform, in general sokol-app will use a - 'soft approach' which doesn't interfere too much with the platform's - window system (for instance borderless fullscreen window instead of - a 'real' fullscreen mode). Such details might change over time - as sokol-app is adapted for different needs. - - The most important effect of fullscreen mode to keep in mind is that - the requested canvas width and height will be ignored for the initial - window size, calling sapp_width() and sapp_height() will instead return - the resolution of the fullscreen canvas (however the provided size - might still be used for the non-fullscreen window, in case the user can - switch back from fullscreen- to windowed-mode). - - To toggle fullscreen mode programmatically, call sapp_toggle_fullscreen(). - - To check if the application window is currently in fullscreen mode, - call sapp_is_fullscreen(). - - WINDOW ICON SUPPORT - =================== - Some sokol_app.h backends allow to change the window icon programmatically: - - - on Win32: the small icon in the window's title bar, and the - bigger icon in the task bar - - on Linux: highly dependent on the used window manager, but usually - the window's title bar icon and/or the task bar icon - - on HTML5: the favicon shown in the page's browser tab - - NOTE that it is not possible to set the actual application icon which is - displayed by the operating system on the desktop or 'home screen'. Those - icons must be provided 'traditionally' through operating-system-specific - resources which are associated with the application (sokol_app.h might - later support setting the window icon from platform specific resource data - though). - - There are two ways to set the window icon: - - - at application start in the sokol_main() function by initializing - the sapp_desc.icon nested struct - - or later by calling the function sapp_set_icon() - - As a convenient shortcut, sokol_app.h comes with a builtin default-icon - (a rainbow-colored 'S', which at least looks a bit better than the Windows - default icon for applications), which can be activated like this: - - At startup in sokol_main(): - - sapp_desc sokol_main(...) { - return (sapp_desc){ - ... - icon.sokol_default = true - }; - } - - Or later by calling: - - sapp_set_icon(&(sapp_icon_desc){ .sokol_default = true }); - - NOTE that a completely zero-initialized sapp_icon_desc struct will not - update the window icon in any way. This is an 'escape hatch' so that you - can handle the window icon update yourself (or if you do this already, - sokol_app.h won't get in your way, in this case just leave the - sapp_desc.icon struct zero-initialized). - - Providing your own icon images works exactly like in GLFW (down to the - data format): - - You provide one or more 'candidate images' in different sizes, and the - sokol_app.h platform backends pick the best match for the specific backend - and icon type. - - For each candidate image, you need to provide: - - - the width in pixels - - the height in pixels - - and the actual pixel data in RGBA8 pixel format (e.g. 0xFFCC8844 - on a little-endian CPU means: alpha=0xFF, blue=0xCC, green=0x88, red=0x44) - - For instance, if you have 3 candidate images (small, medium, big) of - sizes 16x16, 32x32 and 64x64 the corresponding sapp_icon_desc struct is setup - like this: - - // the actual pixel data (RGBA8, origin top-left) - const uint32_t small[16][16] = { ... }; - const uint32_t medium[32][32] = { ... }; - const uint32_t big[64][64] = { ... }; - - const sapp_icon_desc icon_desc = { - .images = { - { .width = 16, .height = 16, .pixels = SAPP_RANGE(small) }, - { .width = 32, .height = 32, .pixels = SAPP_RANGE(medium) }, - // ...or without the SAPP_RANGE helper macro: - { .width = 64, .height = 64, .pixels = { .ptr=big, .size=sizeof(big) } } - } - }; - - An sapp_icon_desc struct initialized like this can then either be applied - at application start in sokol_main: - - sapp_desc sokol_main(...) { - return (sapp_desc){ - ... - icon = icon_desc - }; - } - - ...or later by calling sapp_set_icon(): - - sapp_set_icon(&icon_desc); - - Some window icon caveats: - - - once the window icon has been updated, there's no way to go back to - the platform's default icon, this is because some platforms (Linux - and HTML5) don't switch the icon visual back to the default even if - the custom icon is deleted or removed - - on HTML5, if the sokol_app.h icon doesn't show up in the browser - tab, check that there's no traditional favicon 'link' element - is defined in the page's index.html, sokol_app.h will only - append a new favicon link element, but not delete any manually - defined favicon in the page - - For an example and test of the window icon feature, check out the the - 'icon-sapp' sample on the sokol-samples git repository. - - ONSCREEN KEYBOARD - ================= - On some platforms which don't provide a physical keyboard, sokol-app - can display the platform's integrated onscreen keyboard for text - input. To request that the onscreen keyboard is shown, call - - sapp_show_keyboard(true); - - Likewise, to hide the keyboard call: - - sapp_show_keyboard(false); - - Note that on the web platform, the keyboard can only be shown from - inside an input handler. On such platforms, sapp_show_keyboard() - will only work as expected when it is called from inside the - sokol-app event callback function. When called from other places, - an internal flag will be set, and the onscreen keyboard will be - called at the next 'legal' opportunity (when the next input event - is handled). - - OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY) - ====================================================== - In its default configuration, sokol_app.h "hijacks" the platform's - standard main() function. This was done because different platforms - have different main functions which are not compatible with - C's main() (for instance WinMain on Windows has completely different - arguments). However, this "main hijacking" posed a problem for - usage scenarios like integrating sokol_app.h with other languages than - C or C++, so an alternative SOKOL_NO_ENTRY mode has been added - in which the user code provides the platform's main function: - - - define SOKOL_NO_ENTRY before including the sokol_app.h implementation - - do *not* provide a sokol_main() function - - instead provide the standard main() function of the platform - - from the main function, call the function ```sapp_run()``` which - takes a pointer to an ```sapp_desc``` structure. - - ```sapp_run()``` takes over control and calls the provided init-, frame-, - shutdown- and event-callbacks just like in the default model, it - will only return when the application quits (or not at all on some - platforms, like emscripten) - - NOTE: SOKOL_NO_ENTRY is currently not supported on Android. - - WINDOWS CONSOLE OUTPUT - ====================== - On Windows, regular windowed applications don't show any stdout/stderr text - output, which can be a bit of a hassle for printf() debugging or generally - logging text to the console. Also, console output by default uses a local - codepage setting and thus international UTF-8 encoded text is printed - as garbage. - - To help with these issues, sokol_app.h can be configured at startup - via the following Windows-specific sapp_desc flags: - - sapp_desc.win32_console_utf8 (default: false) - When set to true, the output console codepage will be switched - to UTF-8 (and restored to the original codepage on exit) - - sapp_desc.win32_console_attach (default: false) - When set to true, stdout and stderr will be attached to the - console of the parent process (if the parent process actually - has a console). This means that if the application was started - in a command line window, stdout and stderr output will be printed - to the terminal, just like a regular command line program. But if - the application is started via double-click, it will behave like - a regular UI application, and stdout/stderr will not be visible. - - sapp_desc.win32_console_create (default: false) - When set to true, a new console window will be created and - stdout/stderr will be redirected to that console window. It - doesn't matter if the application is started from the command - line or via double-click. - - TEMP NOTE DUMP - ============== - - onscreen keyboard support on Android requires Java :(, should we even bother? - - sapp_desc needs a bool whether to initialize depth-stencil surface - - GL context initialization needs more control (at least what GL version to initialize) - - application icon - - the UPDATE_CURSOR event currently behaves differently between Win32 and OSX - (Win32 sends the event each frame when the mouse moves and is inside the window - client area, OSX sends it only once when the mouse enters the client area) - - the Android implementation calls cleanup_cb() and destroys the egl context in onDestroy - at the latest but should do it earlier, in onStop, as an app is "killable" after onStop - on Android Honeycomb and later (it can't be done at the moment as the app may be started - again after onStop and the sokol lifecycle does not yet handle context teardown/bringup) - - - LICENSE - ======= - zlib/libpng license - - Copyright (c) 2018 Andre Weissflog - - 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. -*/ -#define SOKOL_APP_INCLUDED (1) -#include // size_t -#include -#include - -#if defined(SOKOL_API_DECL) && !defined(SOKOL_APP_API_DECL) -#define SOKOL_APP_API_DECL SOKOL_API_DECL -#endif -#ifndef SOKOL_APP_API_DECL -#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_APP_IMPL) -#define SOKOL_APP_API_DECL __declspec(dllexport) -#elif defined(_WIN32) && defined(SOKOL_DLL) -#define SOKOL_APP_API_DECL __declspec(dllimport) -#else -#define SOKOL_APP_API_DECL extern -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* misc constants */ -enum { - SAPP_MAX_TOUCHPOINTS = 8, - SAPP_MAX_MOUSEBUTTONS = 3, - SAPP_MAX_KEYCODES = 512, - SAPP_MAX_ICONIMAGES = 8, -}; - -/* - sapp_event_type - - The type of event that's passed to the event handler callback - in the sapp_event.type field. These are not just "traditional" - input events, but also notify the application about state changes - or other user-invoked actions. -*/ -typedef enum sapp_event_type { - SAPP_EVENTTYPE_INVALID, - SAPP_EVENTTYPE_KEY_DOWN, - SAPP_EVENTTYPE_KEY_UP, - SAPP_EVENTTYPE_CHAR, - SAPP_EVENTTYPE_MOUSE_DOWN, - SAPP_EVENTTYPE_MOUSE_UP, - SAPP_EVENTTYPE_MOUSE_SCROLL, - SAPP_EVENTTYPE_MOUSE_MOVE, - SAPP_EVENTTYPE_MOUSE_ENTER, - SAPP_EVENTTYPE_MOUSE_LEAVE, - SAPP_EVENTTYPE_TOUCHES_BEGAN, - SAPP_EVENTTYPE_TOUCHES_MOVED, - SAPP_EVENTTYPE_TOUCHES_ENDED, - SAPP_EVENTTYPE_TOUCHES_CANCELLED, - SAPP_EVENTTYPE_RESIZED, - SAPP_EVENTTYPE_ICONIFIED, - SAPP_EVENTTYPE_RESTORED, - SAPP_EVENTTYPE_FOCUSED, - SAPP_EVENTTYPE_UNFOCUSED, - SAPP_EVENTTYPE_SUSPENDED, - SAPP_EVENTTYPE_RESUMED, - SAPP_EVENTTYPE_UPDATE_CURSOR, - SAPP_EVENTTYPE_QUIT_REQUESTED, - SAPP_EVENTTYPE_CLIPBOARD_PASTED, - SAPP_EVENTTYPE_FILES_DROPPED, - _SAPP_EVENTTYPE_NUM, - _SAPP_EVENTTYPE_FORCE_U32 = 0x7FFFFFFF -} sapp_event_type; - -/* - sapp_keycode - - The 'virtual keycode' of a KEY_DOWN or KEY_UP event in the - struct field sapp_event.key_code. - - Note that the keycode values are identical with GLFW. -*/ -typedef enum sapp_keycode { - SAPP_KEYCODE_INVALID = 0, - SAPP_KEYCODE_SPACE = 32, - SAPP_KEYCODE_APOSTROPHE = 39, /* ' */ - SAPP_KEYCODE_COMMA = 44, /* , */ - SAPP_KEYCODE_MINUS = 45, /* - */ - SAPP_KEYCODE_PERIOD = 46, /* . */ - SAPP_KEYCODE_SLASH = 47, /* / */ - SAPP_KEYCODE_0 = 48, - SAPP_KEYCODE_1 = 49, - SAPP_KEYCODE_2 = 50, - SAPP_KEYCODE_3 = 51, - SAPP_KEYCODE_4 = 52, - SAPP_KEYCODE_5 = 53, - SAPP_KEYCODE_6 = 54, - SAPP_KEYCODE_7 = 55, - SAPP_KEYCODE_8 = 56, - SAPP_KEYCODE_9 = 57, - SAPP_KEYCODE_SEMICOLON = 59, /* ; */ - SAPP_KEYCODE_EQUAL = 61, /* = */ - SAPP_KEYCODE_A = 65, - SAPP_KEYCODE_B = 66, - SAPP_KEYCODE_C = 67, - SAPP_KEYCODE_D = 68, - SAPP_KEYCODE_E = 69, - SAPP_KEYCODE_F = 70, - SAPP_KEYCODE_G = 71, - SAPP_KEYCODE_H = 72, - SAPP_KEYCODE_I = 73, - SAPP_KEYCODE_J = 74, - SAPP_KEYCODE_K = 75, - SAPP_KEYCODE_L = 76, - SAPP_KEYCODE_M = 77, - SAPP_KEYCODE_N = 78, - SAPP_KEYCODE_O = 79, - SAPP_KEYCODE_P = 80, - SAPP_KEYCODE_Q = 81, - SAPP_KEYCODE_R = 82, - SAPP_KEYCODE_S = 83, - SAPP_KEYCODE_T = 84, - SAPP_KEYCODE_U = 85, - SAPP_KEYCODE_V = 86, - SAPP_KEYCODE_W = 87, - SAPP_KEYCODE_X = 88, - SAPP_KEYCODE_Y = 89, - SAPP_KEYCODE_Z = 90, - SAPP_KEYCODE_LEFT_BRACKET = 91, /* [ */ - SAPP_KEYCODE_BACKSLASH = 92, /* \ */ - SAPP_KEYCODE_RIGHT_BRACKET = 93, /* ] */ - SAPP_KEYCODE_GRAVE_ACCENT = 96, /* ` */ - SAPP_KEYCODE_WORLD_1 = 161, /* non-US #1 */ - SAPP_KEYCODE_WORLD_2 = 162, /* non-US #2 */ - SAPP_KEYCODE_ESCAPE = 256, - SAPP_KEYCODE_ENTER = 257, - SAPP_KEYCODE_TAB = 258, - SAPP_KEYCODE_BACKSPACE = 259, - SAPP_KEYCODE_INSERT = 260, - SAPP_KEYCODE_DELETE = 261, - SAPP_KEYCODE_RIGHT = 262, - SAPP_KEYCODE_LEFT = 263, - SAPP_KEYCODE_DOWN = 264, - SAPP_KEYCODE_UP = 265, - SAPP_KEYCODE_PAGE_UP = 266, - SAPP_KEYCODE_PAGE_DOWN = 267, - SAPP_KEYCODE_HOME = 268, - SAPP_KEYCODE_END = 269, - SAPP_KEYCODE_CAPS_LOCK = 280, - SAPP_KEYCODE_SCROLL_LOCK = 281, - SAPP_KEYCODE_NUM_LOCK = 282, - SAPP_KEYCODE_PRINT_SCREEN = 283, - SAPP_KEYCODE_PAUSE = 284, - SAPP_KEYCODE_F1 = 290, - SAPP_KEYCODE_F2 = 291, - SAPP_KEYCODE_F3 = 292, - SAPP_KEYCODE_F4 = 293, - SAPP_KEYCODE_F5 = 294, - SAPP_KEYCODE_F6 = 295, - SAPP_KEYCODE_F7 = 296, - SAPP_KEYCODE_F8 = 297, - SAPP_KEYCODE_F9 = 298, - SAPP_KEYCODE_F10 = 299, - SAPP_KEYCODE_F11 = 300, - SAPP_KEYCODE_F12 = 301, - SAPP_KEYCODE_F13 = 302, - SAPP_KEYCODE_F14 = 303, - SAPP_KEYCODE_F15 = 304, - SAPP_KEYCODE_F16 = 305, - SAPP_KEYCODE_F17 = 306, - SAPP_KEYCODE_F18 = 307, - SAPP_KEYCODE_F19 = 308, - SAPP_KEYCODE_F20 = 309, - SAPP_KEYCODE_F21 = 310, - SAPP_KEYCODE_F22 = 311, - SAPP_KEYCODE_F23 = 312, - SAPP_KEYCODE_F24 = 313, - SAPP_KEYCODE_F25 = 314, - SAPP_KEYCODE_KP_0 = 320, - SAPP_KEYCODE_KP_1 = 321, - SAPP_KEYCODE_KP_2 = 322, - SAPP_KEYCODE_KP_3 = 323, - SAPP_KEYCODE_KP_4 = 324, - SAPP_KEYCODE_KP_5 = 325, - SAPP_KEYCODE_KP_6 = 326, - SAPP_KEYCODE_KP_7 = 327, - SAPP_KEYCODE_KP_8 = 328, - SAPP_KEYCODE_KP_9 = 329, - SAPP_KEYCODE_KP_DECIMAL = 330, - SAPP_KEYCODE_KP_DIVIDE = 331, - SAPP_KEYCODE_KP_MULTIPLY = 332, - SAPP_KEYCODE_KP_SUBTRACT = 333, - SAPP_KEYCODE_KP_ADD = 334, - SAPP_KEYCODE_KP_ENTER = 335, - SAPP_KEYCODE_KP_EQUAL = 336, - SAPP_KEYCODE_LEFT_SHIFT = 340, - SAPP_KEYCODE_LEFT_CONTROL = 341, - SAPP_KEYCODE_LEFT_ALT = 342, - SAPP_KEYCODE_LEFT_SUPER = 343, - SAPP_KEYCODE_RIGHT_SHIFT = 344, - SAPP_KEYCODE_RIGHT_CONTROL = 345, - SAPP_KEYCODE_RIGHT_ALT = 346, - SAPP_KEYCODE_RIGHT_SUPER = 347, - SAPP_KEYCODE_MENU = 348, -} sapp_keycode; - -/* - sapp_touchpoint - - Describes a single touchpoint in a multitouch event (TOUCHES_BEGAN, - TOUCHES_MOVED, TOUCHES_ENDED). - - Touch points are stored in the nested array sapp_event.touches[], - and the number of touches is stored in sapp_event.num_touches. -*/ -typedef struct sapp_touchpoint { - uintptr_t identifier; - float pos_x; - float pos_y; - bool changed; -} sapp_touchpoint; - -/* - sapp_mousebutton - - The currently pressed mouse button in the events MOUSE_DOWN - and MOUSE_UP, stored in the struct field sapp_event.mouse_button. -*/ -typedef enum sapp_mousebutton { - SAPP_MOUSEBUTTON_LEFT = 0x0, - SAPP_MOUSEBUTTON_RIGHT = 0x1, - SAPP_MOUSEBUTTON_MIDDLE = 0x2, - SAPP_MOUSEBUTTON_INVALID = 0x100, -} sapp_mousebutton; - -/* - These are currently pressed modifier keys (and mouse buttons) which are - passed in the event struct field sapp_event.modifiers. -*/ -enum { - SAPP_MODIFIER_SHIFT = 0x1, // left or right shift key - SAPP_MODIFIER_CTRL = 0x2, // left or right control key - SAPP_MODIFIER_ALT = 0x4, // left or right alt key - SAPP_MODIFIER_SUPER = 0x8, // left or right 'super' key - SAPP_MODIFIER_LMB = 0x100, // left mouse button - SAPP_MODIFIER_RMB = 0x200, // right mouse button - SAPP_MODIFIER_MMB = 0x400, // middle mouse button -}; - -/* - sapp_event - - This is an all-in-one event struct passed to the event handler - user callback function. Note that it depends on the event - type what struct fields actually contain useful values, so you - should first check the event type before reading other struct - fields. -*/ -typedef struct sapp_event { - uint64_t frame_count; // current frame counter, always valid, useful for checking if two events were issued in the same frame - sapp_event_type type; // the event type, always valid - sapp_keycode key_code; // the virtual key code, only valid in KEY_UP, KEY_DOWN - uint32_t char_code; // the UTF-32 character code, only valid in CHAR events - bool key_repeat; // true if this is a key-repeat event, valid in KEY_UP, KEY_DOWN and CHAR - uint32_t modifiers; // current modifier keys, valid in all key-, char- and mouse-events - sapp_mousebutton mouse_button; // mouse button that was pressed or released, valid in MOUSE_DOWN, MOUSE_UP - float mouse_x; // current horizontal mouse position in pixels, always valid except during mouse lock - float mouse_y; // current vertical mouse position in pixels, always valid except during mouse lock - float mouse_dx; // relative horizontal mouse movement since last frame, always valid - float mouse_dy; // relative vertical mouse movement since last frame, always valid - float scroll_x; // horizontal mouse wheel scroll distance, valid in MOUSE_SCROLL events - float scroll_y; // vertical mouse wheel scroll distance, valid in MOUSE_SCROLL events - int num_touches; // number of valid items in the touches[] array - sapp_touchpoint touches[SAPP_MAX_TOUCHPOINTS]; // current touch points, valid in TOUCHES_BEGIN, TOUCHES_MOVED, TOUCHES_ENDED - int window_width; // current window- and framebuffer sizes in pixels, always valid - int window_height; - int framebuffer_width; // = window_width * dpi_scale - int framebuffer_height; // = window_height * dpi_scale -} sapp_event; - -/* - sg_range - - A general pointer/size-pair struct and constructor macros for passing binary blobs - into sokol_app.h. -*/ -typedef struct sapp_range { - const void* ptr; - size_t size; -} sapp_range; -// disabling this for every includer isn't great, but the warnings are also quite pointless -#if defined(_MSC_VER) -#pragma warning(disable:4221) /* /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' */ -#pragma warning(disable:4204) /* VS2015: nonstandard extension used: non-constant aggregate initializer */ -#endif -#if defined(__cplusplus) -#define SAPP_RANGE(x) sapp_range{ &x, sizeof(x) } -#else -#define SAPP_RANGE(x) (sapp_range){ &x, sizeof(x) } -#endif - -/* - sapp_image_desc - - This is used to describe image data to sokol_app.h (at first, window - icons, later maybe cursor images). - - Note that the actual image pixel format depends on the use case: - - - window icon pixels are RGBA8 - - cursor images are ??? (FIXME) -*/ -typedef struct sapp_image_desc { - int width; - int height; - sapp_range pixels; -} sapp_image_desc; - -/* - sapp_icon_desc - - An icon description structure for use in sapp_desc.icon and - sapp_set_icon(). - - When setting a custom image, the application can provide a number of - candidates differing in size, and sokol_app.h will pick the image(s) - closest to the size expected by the platform's window system. - - To set sokol-app's default icon, set .sokol_default to true. - - Otherwise provide candidate images of different sizes in the - images[] array. - - If both the sokol_default flag is set to true, any image candidates - will be ignored and the sokol_app.h default icon will be set. -*/ -typedef struct sapp_icon_desc { - bool sokol_default; - sapp_image_desc images[SAPP_MAX_ICONIMAGES]; -} sapp_icon_desc; - - -typedef struct sapp_desc { - void (*init_cb)(void); // these are the user-provided callbacks without user data - void (*frame_cb)(void); - void (*cleanup_cb)(void); - void (*event_cb)(const sapp_event*); - void (*fail_cb)(const char*); - - void* user_data; // these are the user-provided callbacks with user data - void (*init_userdata_cb)(void*); - void (*frame_userdata_cb)(void*); - void (*cleanup_userdata_cb)(void*); - void (*event_userdata_cb)(const sapp_event*, void*); - void (*fail_userdata_cb)(const char*, void*); - - int width; // the preferred width of the window / canvas - int height; // the preferred height of the window / canvas - int sample_count; // MSAA sample count - int swap_interval; // the preferred swap interval (ignored on some platforms) - bool high_dpi; // whether the rendering canvas is full-resolution on HighDPI displays - bool fullscreen; // whether the window should be created in fullscreen mode - bool alpha; // whether the framebuffer should have an alpha channel (ignored on some platforms) - const char* window_title; // the window title as UTF-8 encoded string - bool user_cursor; // if true, user is expected to manage cursor image in SAPP_EVENTTYPE_UPDATE_CURSOR - bool enable_clipboard; // enable clipboard access, default is false - int clipboard_size; // max size of clipboard content in bytes - bool enable_dragndrop; // enable file dropping (drag'n'drop), default is false - int max_dropped_files; // max number of dropped files to process (default: 1) - int max_dropped_file_path_length; // max length in bytes of a dropped UTF-8 file path (default: 2048) - sapp_icon_desc icon; // the initial window icon to set - - /* backend-specific options */ - bool gl_force_gles2; // if true, setup GLES2/WebGL even if GLES3/WebGL2 is available - bool win32_console_utf8; // if true, set the output console codepage to UTF-8 - bool win32_console_create; // if true, attach stdout/stderr to a new console window - bool win32_console_attach; // if true, attach stdout/stderr to parent process - const char* html5_canvas_name; // the name (id) of the HTML5 canvas element, default is "canvas" - bool html5_canvas_resize; // if true, the HTML5 canvas size is set to sapp_desc.width/height, otherwise canvas size is tracked - bool html5_preserve_drawing_buffer; // HTML5 only: whether to preserve default framebuffer content between frames - bool html5_premultiplied_alpha; // HTML5 only: whether the rendered pixels use premultiplied alpha convention - bool html5_ask_leave_site; // initial state of the internal html5_ask_leave_site flag (see sapp_html5_ask_leave_site()) - bool ios_keyboard_resizes_canvas; // if true, showing the iOS keyboard shrinks the canvas -} sapp_desc; - -/* HTML5 specific: request and response structs for - asynchronously loading dropped-file content. -*/ -typedef enum sapp_html5_fetch_error { - SAPP_HTML5_FETCH_ERROR_NO_ERROR, - SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL, - SAPP_HTML5_FETCH_ERROR_OTHER, -} sapp_html5_fetch_error; - -typedef struct sapp_html5_fetch_response { - bool succeeded; /* true if the loading operation has succeeded */ - sapp_html5_fetch_error error_code; - int file_index; /* index of the dropped file (0..sapp_get_num_dropped_filed()-1) */ - uint32_t fetched_size; /* size in bytes of loaded data */ - void* buffer_ptr; /* pointer to user-provided buffer which contains the loaded data */ - uint32_t buffer_size; /* size of user-provided buffer (buffer_size >= fetched_size) */ - void* user_data; /* user-provided user data pointer */ -} sapp_html5_fetch_response; - -typedef struct sapp_html5_fetch_request { - int dropped_file_index; /* 0..sapp_get_num_dropped_files()-1 */ - void (*callback)(const sapp_html5_fetch_response*); /* response callback function pointer (required) */ - void* buffer_ptr; /* pointer to buffer to load data into */ - uint32_t buffer_size; /* size in bytes of buffer */ - void* user_data; /* optional userdata pointer */ -} sapp_html5_fetch_request; - -/* user-provided functions */ -extern sapp_desc sokol_main(int argc, char* argv[]); - -/* returns true after sokol-app has been initialized */ -SOKOL_APP_API_DECL bool sapp_isvalid(void); -/* returns the current framebuffer width in pixels */ -SOKOL_APP_API_DECL int sapp_width(void); -/* same as sapp_width(), but returns float */ -SOKOL_APP_API_DECL float sapp_widthf(void); -/* returns the current framebuffer height in pixels */ -SOKOL_APP_API_DECL int sapp_height(void); -/* same as sapp_height(), but returns float */ -SOKOL_APP_API_DECL float sapp_heightf(void); -/* get default framebuffer color pixel format */ -SOKOL_APP_API_DECL int sapp_color_format(void); -/* get default framebuffer depth pixel format */ -SOKOL_APP_API_DECL int sapp_depth_format(void); -/* get default framebuffer sample count */ -SOKOL_APP_API_DECL int sapp_sample_count(void); -/* returns true when high_dpi was requested and actually running in a high-dpi scenario */ -SOKOL_APP_API_DECL bool sapp_high_dpi(void); -/* returns the dpi scaling factor (window pixels to framebuffer pixels) */ -SOKOL_APP_API_DECL float sapp_dpi_scale(void); -/* show or hide the mobile device onscreen keyboard */ -SOKOL_APP_API_DECL void sapp_show_keyboard(bool show); -/* return true if the mobile device onscreen keyboard is currently shown */ -SOKOL_APP_API_DECL bool sapp_keyboard_shown(void); -/* query fullscreen mode */ -SOKOL_APP_API_DECL bool sapp_is_fullscreen(void); -/* toggle fullscreen mode */ -SOKOL_APP_API_DECL void sapp_toggle_fullscreen(void); -/* show or hide the mouse cursor */ -SOKOL_APP_API_DECL void sapp_show_mouse(bool show); -/* show or hide the mouse cursor */ -SOKOL_APP_API_DECL bool sapp_mouse_shown(void); -/* enable/disable mouse-pointer-lock mode */ -SOKOL_APP_API_DECL void sapp_lock_mouse(bool lock); -/* return true if in mouse-pointer-lock mode (this may toggle a few frames later) */ -SOKOL_APP_API_DECL bool sapp_mouse_locked(void); -/* return the userdata pointer optionally provided in sapp_desc */ -SOKOL_APP_API_DECL void* sapp_userdata(void); -/* return a copy of the sapp_desc structure */ -SOKOL_APP_API_DECL sapp_desc sapp_query_desc(void); -/* initiate a "soft quit" (sends SAPP_EVENTTYPE_QUIT_REQUESTED) */ -SOKOL_APP_API_DECL void sapp_request_quit(void); -/* cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received) */ -SOKOL_APP_API_DECL void sapp_cancel_quit(void); -/* initiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUSTED) */ -SOKOL_APP_API_DECL void sapp_quit(void); -/* call from inside event callback to consume the current event (don't forward to platform) */ -SOKOL_APP_API_DECL void sapp_consume_event(void); -/* get the current frame counter (for comparison with sapp_event.frame_count) */ -SOKOL_APP_API_DECL uint64_t sapp_frame_count(void); -/* write string into clipboard */ -SOKOL_APP_API_DECL void sapp_set_clipboard_string(const char* str); -/* read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED) */ -SOKOL_APP_API_DECL const char* sapp_get_clipboard_string(void); -/* set the window title (only on desktop platforms) */ -SOKOL_APP_API_DECL void sapp_set_window_title(const char* str); -/* set the window icon (only on Windows and Linux) */ -SOKOL_APP_API_DECL void sapp_set_icon(const sapp_icon_desc* icon_desc); -/* gets the total number of dropped files (after an SAPP_EVENTTYPE_FILES_DROPPED event) */ -SOKOL_APP_API_DECL int sapp_get_num_dropped_files(void); -/* gets the dropped file paths */ -SOKOL_APP_API_DECL const char* sapp_get_dropped_file_path(int index); - -/* special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) */ -SOKOL_APP_API_DECL void sapp_run(const sapp_desc* desc); - -/* GL: return true when GLES2 fallback is active (to detect fallback from GLES3) */ -SOKOL_APP_API_DECL bool sapp_gles2(void); - -/* HTML5: enable or disable the hardwired "Leave Site?" dialog box */ -SOKOL_APP_API_DECL void sapp_html5_ask_leave_site(bool ask); -/* HTML5: get byte size of a dropped file */ -SOKOL_APP_API_DECL uint32_t sapp_html5_get_dropped_file_size(int index); -/* HTML5: asynchronously load the content of a dropped file */ -SOKOL_APP_API_DECL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request); - -/* Metal: get bridged pointer to Metal device object */ -SOKOL_APP_API_DECL const void* sapp_metal_get_device(void); -/* Metal: get bridged pointer to this frame's renderpass descriptor */ -SOKOL_APP_API_DECL const void* sapp_metal_get_renderpass_descriptor(void); -/* Metal: get bridged pointer to current drawable */ -SOKOL_APP_API_DECL const void* sapp_metal_get_drawable(void); -/* macOS: get bridged pointer to macOS NSWindow */ -SOKOL_APP_API_DECL const void* sapp_macos_get_window(void); -/* iOS: get bridged pointer to iOS UIWindow */ -SOKOL_APP_API_DECL const void* sapp_ios_get_window(void); - -/* D3D11: get pointer to ID3D11Device object */ -SOKOL_APP_API_DECL const void* sapp_d3d11_get_device(void); -/* D3D11: get pointer to ID3D11DeviceContext object */ -SOKOL_APP_API_DECL const void* sapp_d3d11_get_device_context(void); -/* D3D11: get pointer to IDXGISwapChain object */ -SOKOL_APP_API_DECL const void* sapp_d3d11_get_swap_chain(void); -/* D3D11: get pointer to ID3D11RenderTargetView object */ -SOKOL_APP_API_DECL const void* sapp_d3d11_get_render_target_view(void); -/* D3D11: get pointer to ID3D11DepthStencilView */ -SOKOL_APP_API_DECL const void* sapp_d3d11_get_depth_stencil_view(void); -/* Win32: get the HWND window handle */ -SOKOL_APP_API_DECL const void* sapp_win32_get_hwnd(void); - -/* WebGPU: get WGPUDevice handle */ -SOKOL_APP_API_DECL const void* sapp_wgpu_get_device(void); -/* WebGPU: get swapchain's WGPUTextureView handle for rendering */ -SOKOL_APP_API_DECL const void* sapp_wgpu_get_render_view(void); -/* WebGPU: get swapchain's MSAA-resolve WGPUTextureView (may return null) */ -SOKOL_APP_API_DECL const void* sapp_wgpu_get_resolve_view(void); -/* WebGPU: get swapchain's WGPUTextureView for the depth-stencil surface */ -SOKOL_APP_API_DECL const void* sapp_wgpu_get_depth_stencil_view(void); - -/* Android: get native activity handle */ -SOKOL_APP_API_DECL const void* sapp_android_get_native_activity(void); - -#ifdef __cplusplus -} /* extern "C" */ - -/* reference-based equivalents for C++ */ -inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } - -#endif - -// this WinRT specific hack is required when wWinMain is in a static library -#if defined(_MSC_VER) && defined(UNICODE) -#include -#if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) -#pragma comment(linker, "/include:wWinMain") -#endif -#endif - -#endif // SOKOL_APP_INCLUDED - -/*-- IMPLEMENTATION ----------------------------------------------------------*/ -#ifdef SOKOL_APP_IMPL -#define SOKOL_APP_IMPL_INCLUDED (1) - -#include // memset -#include // size_t - -/* check if the config defines are alright */ -#if defined(__APPLE__) - // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting - #if !defined(__cplusplus) - #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) - #error "sokol_app.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" - #endif - #endif - #define _SAPP_APPLE (1) - #include - #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE - /* MacOS */ - #define _SAPP_MACOS (1) - #if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE33) - #error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL or SOKOL_GLCORE33") - #endif - #else - /* iOS or iOS Simulator */ - #define _SAPP_IOS (1) - #if !defined(SOKOL_METAL) && !defined(SOKOL_GLES3) - #error("sokol_app.h: unknown 3D API selected for iOS, must be SOKOL_METAL or SOKOL_GLES3") - #endif - #endif -#elif defined(__EMSCRIPTEN__) - /* emscripten (asm.js or wasm) */ - #define _SAPP_EMSCRIPTEN (1) - #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) && !defined(SOKOL_WGPU) - #error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3, SOKOL_GLES2 or SOKOL_WGPU") - #endif -#elif defined(_WIN32) - /* Windows (D3D11 or GL) */ - #include - #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) - #define _SAPP_UWP (1) - #if !defined(SOKOL_D3D11) - #error("sokol_app.h: unknown 3D API selected for UWP, must be SOKOL_D3D11") - #endif - #if !defined(__cplusplus) - #error("sokol_app.h: UWP bindings require C++/17") - #endif - #else - #define _SAPP_WIN32 (1) - #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE33) - #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11 or SOKOL_GLCORE33") - #endif - #endif -#elif defined(__ANDROID__) - /* Android */ - #define _SAPP_ANDROID (1) - #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) - #error("sokol_app.h: unknown 3D API selected for Android, must be SOKOL_GLES3 or SOKOL_GLES2") - #endif - #if defined(SOKOL_NO_ENTRY) - #error("sokol_app.h: SOKOL_NO_ENTRY is not supported on Android") - #endif -#elif defined(__linux__) || defined(__unix__) - /* Linux */ - #define _SAPP_LINUX (1) - #if !defined(SOKOL_GLCORE33) - #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33") - #endif -#else -#error "sokol_app.h: Unknown platform" -#endif - -#ifndef SOKOL_API_IMPL - #define SOKOL_API_IMPL -#endif -#ifndef SOKOL_DEBUG - #ifndef NDEBUG - #define SOKOL_DEBUG (1) - #endif -#endif -#ifndef SOKOL_ASSERT - #include - #define SOKOL_ASSERT(c) assert(c) -#endif -#ifndef SOKOL_UNREACHABLE - #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) -#endif -#if !defined(SOKOL_CALLOC) || !defined(SOKOL_FREE) - #include -#endif -#if !defined(SOKOL_CALLOC) - #define SOKOL_CALLOC(n,s) calloc(n,s) -#endif -#if !defined(SOKOL_FREE) - #define SOKOL_FREE(p) free(p) -#endif -#ifndef SOKOL_LOG - #ifdef SOKOL_DEBUG - #if defined(__ANDROID__) - #include - #define SOKOL_LOG(s) { SOKOL_ASSERT(s); __android_log_write(ANDROID_LOG_INFO, "SOKOL_APP", s); } - #else - #include - #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } - #endif - #else - #define SOKOL_LOG(s) - #endif -#endif -#ifndef SOKOL_ABORT - #include - #define SOKOL_ABORT() abort() -#endif -#ifndef _SOKOL_PRIVATE - #if defined(__GNUC__) || defined(__clang__) - #define _SOKOL_PRIVATE __attribute__((unused)) static - #else - #define _SOKOL_PRIVATE static - #endif -#endif -#ifndef _SOKOL_UNUSED - #define _SOKOL_UNUSED(x) (void)(x) -#endif - -/*== PLATFORM SPECIFIC INCLUDES AND DEFINES ==================================*/ -#if defined(_SAPP_APPLE) - #if defined(SOKOL_METAL) - #import - #import - #endif - #if defined(_SAPP_MACOS) - #if !defined(SOKOL_METAL) - #ifndef GL_SILENCE_DEPRECATION - #define GL_SILENCE_DEPRECATION - #endif - #include - #endif - #elif defined(_SAPP_IOS) - #import - #if !defined(SOKOL_METAL) - #import - #endif - #endif -#elif defined(_SAPP_EMSCRIPTEN) - #if defined(SOKOL_WGPU) - #include - #endif - #include - #include -#elif defined(_SAPP_WIN32) - #ifdef _MSC_VER - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ - #pragma warning(disable:4204) /* nonstandard extension used: non-constant aggregate initializer */ - #pragma warning(disable:4054) /* 'type cast': from function pointer */ - #pragma warning(disable:4055) /* 'type cast': from data pointer */ - #pragma warning(disable:4505) /* unreferenced local function has been removed */ - #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */ - #endif - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #ifndef NOMINMAX - #define NOMINMAX - #endif - #include - #include - #include - #if !defined(SOKOL_NO_ENTRY) // if SOKOL_NO_ENTRY is defined, it's the applications' responsibility to use the right subsystem - #if defined(SOKOL_WIN32_FORCE_MAIN) - #pragma comment (linker, "/subsystem:console") - #else - #pragma comment (linker, "/subsystem:windows") - #endif - #endif - #include /* freopen_s() */ - #include /* wcslen() */ - - #pragma comment (lib, "kernel32") - #pragma comment (lib, "user32") - #pragma comment (lib, "shell32") /* CommandLineToArgvW, DragQueryFileW, DragFinished */ - #pragma comment (lib, "gdi32") - #if defined(SOKOL_D3D11) - #pragma comment (lib, "dxgi") - #pragma comment (lib, "d3d11") - #endif - - #if defined(SOKOL_D3D11) - #ifndef D3D11_NO_HELPERS - #define D3D11_NO_HELPERS - #endif - #include - #include - // DXGI_SWAP_EFFECT_FLIP_DISCARD is only defined in newer Windows SDKs, so don't depend on it - #define _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD (4) - #endif - #ifndef WM_MOUSEHWHEEL /* see https://github.com/floooh/sokol/issues/138 */ - #define WM_MOUSEHWHEEL (0x020E) - #endif -#elif defined(_SAPP_UWP) - #ifndef NOMINMAX - #define NOMINMAX - #endif - #ifdef _MSC_VER - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ - #pragma warning(disable:4054) /* 'type cast': from function pointer */ - #pragma warning(disable:4055) /* 'type cast': from data pointer */ - #pragma warning(disable:4505) /* unreferenced local function has been removed */ - #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */ - #endif - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #include - #include - #include - - #pragma comment (lib, "WindowsApp") - #pragma comment (lib, "dxguid") -#elif defined(_SAPP_ANDROID) - #include - #include - #include - #include - #include -#elif defined(_SAPP_LINUX) - #define GL_GLEXT_PROTOTYPES - #include - #include - #include - #include - #include - #include - #include - #include - #include /* CARD32 */ - #include /* dlopen, dlsym, dlclose */ - #include /* LONG_MAX */ - #include /* only used a linker-guard, search for _sapp_linux_run() and see first comment */ -#endif - -/*== MACOS DECLARATIONS ======================================================*/ -#if defined(_SAPP_MACOS) -@interface _sapp_macos_app_delegate : NSObject -@end -@interface _sapp_macos_window : NSWindow -@end -@interface _sapp_macos_window_delegate : NSObject -@end -#if defined(SOKOL_METAL) - @interface _sapp_macos_view : MTKView - @end -#elif defined(SOKOL_GLCORE33) - @interface _sapp_macos_view : NSOpenGLView - - (void)timerFired:(id)sender; - @end -#endif // SOKOL_GLCORE33 - -typedef struct { - uint32_t flags_changed_store; - uint8_t mouse_buttons; - NSWindow* window; - NSTrackingArea* tracking_area; - _sapp_macos_app_delegate* app_dlg; - _sapp_macos_window_delegate* win_dlg; - _sapp_macos_view* view; - #if defined(SOKOL_METAL) - id mtl_device; - #endif -} _sapp_macos_t; - -#endif // _SAPP_MACOS - -/*== IOS DECLARATIONS ========================================================*/ -#if defined(_SAPP_IOS) - -@interface _sapp_app_delegate : NSObject -@end -@interface _sapp_textfield_dlg : NSObject -- (void)keyboardWasShown:(NSNotification*)notif; -- (void)keyboardWillBeHidden:(NSNotification*)notif; -- (void)keyboardDidChangeFrame:(NSNotification*)notif; -@end -#if defined(SOKOL_METAL) - @interface _sapp_ios_view : MTKView; - @end -#else - @interface _sapp_ios_view : GLKView - @end -#endif - -typedef struct { - UIWindow* window; - _sapp_ios_view* view; - UITextField* textfield; - _sapp_textfield_dlg* textfield_dlg; - #if defined(SOKOL_METAL) - UIViewController* view_ctrl; - id mtl_device; - #else - GLKViewController* view_ctrl; - EAGLContext* eagl_ctx; - #endif - bool suspended; -} _sapp_ios_t; - -#endif // _SAPP_IOS - -/*== EMSCRIPTEN DECLARATIONS =================================================*/ -#if defined(_SAPP_EMSCRIPTEN) - -#if defined(SOKOL_WGPU) -typedef struct { - int state; - WGPUDevice device; - WGPUSwapChain swapchain; - WGPUTextureFormat render_format; - WGPUTexture msaa_tex; - WGPUTexture depth_stencil_tex; - WGPUTextureView swapchain_view; - WGPUTextureView msaa_view; - WGPUTextureView depth_stencil_view; -} _sapp_wgpu_t; -#endif - -typedef struct { - bool textfield_created; - bool wants_show_keyboard; - bool wants_hide_keyboard; - bool mouse_lock_requested; - uint16_t mouse_buttons; - #if defined(SOKOL_WGPU) - _sapp_wgpu_t wgpu; - #endif -} _sapp_emsc_t; -#endif // _SAPP_EMSCRIPTEN - -/*== WIN32 DECLARATIONS ======================================================*/ -#if defined(SOKOL_D3D11) && (defined(_SAPP_WIN32) || defined(_SAPP_UWP)) -typedef struct { - ID3D11Device* device; - ID3D11DeviceContext* device_context; - ID3D11Texture2D* rt; - ID3D11RenderTargetView* rtv; - ID3D11Texture2D* msaa_rt; - ID3D11RenderTargetView* msaa_rtv; - ID3D11Texture2D* ds; - ID3D11DepthStencilView* dsv; - DXGI_SWAP_CHAIN_DESC swap_chain_desc; - IDXGISwapChain* swap_chain; -} _sapp_d3d11_t; -#endif - -/*== WIN32 DECLARATIONS ======================================================*/ -#if defined(_SAPP_WIN32) - -#ifndef DPI_ENUMS_DECLARED -typedef enum PROCESS_DPI_AWARENESS -{ - PROCESS_DPI_UNAWARE = 0, - PROCESS_SYSTEM_DPI_AWARE = 1, - PROCESS_PER_MONITOR_DPI_AWARE = 2 -} PROCESS_DPI_AWARENESS; -typedef enum MONITOR_DPI_TYPE { - MDT_EFFECTIVE_DPI = 0, - MDT_ANGULAR_DPI = 1, - MDT_RAW_DPI = 2, - MDT_DEFAULT = MDT_EFFECTIVE_DPI -} MONITOR_DPI_TYPE; -#endif /*DPI_ENUMS_DECLARED*/ - -typedef struct { - bool aware; - float content_scale; - float window_scale; - float mouse_scale; -} _sapp_win32_dpi_t; - -typedef struct { - HWND hwnd; - HDC dc; - HICON big_icon; - HICON small_icon; - UINT orig_codepage; - LONG mouse_locked_x, mouse_locked_y; - bool is_win10_or_greater; - bool in_create_window; - bool iconified; - bool mouse_tracked; - uint8_t mouse_capture_mask; - _sapp_win32_dpi_t dpi; - bool raw_input_mousepos_valid; - LONG raw_input_mousepos_x; - LONG raw_input_mousepos_y; - uint8_t raw_input_data[256]; -} _sapp_win32_t; - -#if defined(SOKOL_GLCORE33) -#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 -#define WGL_SUPPORT_OPENGL_ARB 0x2010 -#define WGL_DRAW_TO_WINDOW_ARB 0x2001 -#define WGL_PIXEL_TYPE_ARB 0x2013 -#define WGL_TYPE_RGBA_ARB 0x202b -#define WGL_ACCELERATION_ARB 0x2003 -#define WGL_NO_ACCELERATION_ARB 0x2025 -#define WGL_RED_BITS_ARB 0x2015 -#define WGL_GREEN_BITS_ARB 0x2017 -#define WGL_BLUE_BITS_ARB 0x2019 -#define WGL_ALPHA_BITS_ARB 0x201b -#define WGL_DEPTH_BITS_ARB 0x2022 -#define WGL_STENCIL_BITS_ARB 0x2023 -#define WGL_DOUBLE_BUFFER_ARB 0x2011 -#define WGL_SAMPLES_ARB 0x2042 -#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 -#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 -#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 -#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 -#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 -#define WGL_CONTEXT_FLAGS_ARB 0x2094 -#define ERROR_INVALID_VERSION_ARB 0x2095 -#define ERROR_INVALID_PROFILE_ARB 0x2096 -#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 -typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); -typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); -typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); -typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC); -typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*); -typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC); -typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC); -typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR); -typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void); -typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC); - -typedef struct { - HINSTANCE opengl32; - HGLRC gl_ctx; - PFN_wglCreateContext CreateContext; - PFN_wglDeleteContext DeleteContext; - PFN_wglGetProcAddress GetProcAddress; - PFN_wglGetCurrentDC GetCurrentDC; - PFN_wglMakeCurrent MakeCurrent; - PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT; - PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB; - PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT; - PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; - PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; - bool ext_swap_control; - bool arb_multisample; - bool arb_pixel_format; - bool arb_create_context; - bool arb_create_context_profile; - HWND msg_hwnd; - HDC msg_dc; -} _sapp_wgl_t; -#endif // SOKOL_GLCORE33 - -#endif // _SAPP_WIN32 - -/*== UWP DECLARATIONS ======================================================*/ -#if defined(_SAPP_UWP) - -typedef struct { - float content_scale; - float window_scale; - float mouse_scale; -} _sapp_uwp_dpi_t; - -typedef struct { - bool mouse_tracked; - uint8_t mouse_buttons; - _sapp_uwp_dpi_t dpi; -} _sapp_uwp_t; - -#endif // _SAPP_UWP - -/*== ANDROID DECLARATIONS ====================================================*/ - -#if defined(_SAPP_ANDROID) -typedef enum { - _SOKOL_ANDROID_MSG_CREATE, - _SOKOL_ANDROID_MSG_RESUME, - _SOKOL_ANDROID_MSG_PAUSE, - _SOKOL_ANDROID_MSG_FOCUS, - _SOKOL_ANDROID_MSG_NO_FOCUS, - _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW, - _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE, - _SOKOL_ANDROID_MSG_DESTROY, -} _sapp_android_msg_t; - -typedef struct { - pthread_t thread; - pthread_mutex_t mutex; - pthread_cond_t cond; - int read_from_main_fd; - int write_from_main_fd; -} _sapp_android_pt_t; - -typedef struct { - ANativeWindow* window; - AInputQueue* input; -} _sapp_android_resources_t; - -typedef struct { - ANativeActivity* activity; - _sapp_android_pt_t pt; - _sapp_android_resources_t pending; - _sapp_android_resources_t current; - ALooper* looper; - bool is_thread_started; - bool is_thread_stopping; - bool is_thread_stopped; - bool has_created; - bool has_resumed; - bool has_focus; - EGLConfig config; - EGLDisplay display; - EGLContext context; - EGLSurface surface; -} _sapp_android_t; - -#endif // _SAPP_ANDROID - -/*== LINUX DECLARATIONS ======================================================*/ -#if defined(_SAPP_LINUX) - -#define _SAPP_X11_XDND_VERSION (5) - -#define GLX_VENDOR 1 -#define GLX_RGBA_BIT 0x00000001 -#define GLX_WINDOW_BIT 0x00000001 -#define GLX_DRAWABLE_TYPE 0x8010 -#define GLX_RENDER_TYPE 0x8011 -#define GLX_DOUBLEBUFFER 5 -#define GLX_RED_SIZE 8 -#define GLX_GREEN_SIZE 9 -#define GLX_BLUE_SIZE 10 -#define GLX_ALPHA_SIZE 11 -#define GLX_DEPTH_SIZE 12 -#define GLX_STENCIL_SIZE 13 -#define GLX_SAMPLES 0x186a1 -#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 -#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 -#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 -#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 -#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 -#define GLX_CONTEXT_FLAGS_ARB 0x2094 - -typedef XID GLXWindow; -typedef XID GLXDrawable; -typedef struct __GLXFBConfig* GLXFBConfig; -typedef struct __GLXcontext* GLXContext; -typedef void (*__GLXextproc)(void); - -typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); -typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); -typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); -typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*); -typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext); -typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext); -typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable); -typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); -typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); -typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const char *procName); -typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); -typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); -typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); -typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); - -typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); -typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); - -typedef struct { - bool available; - int major_opcode; - int event_base; - int error_base; - int major; - int minor; -} _sapp_xi_t; - -typedef struct { - int version; - Window source; - Atom format; - Atom XdndAware; - Atom XdndEnter; - Atom XdndPosition; - Atom XdndStatus; - Atom XdndActionCopy; - Atom XdndDrop; - Atom XdndFinished; - Atom XdndSelection; - Atom XdndTypeList; - Atom text_uri_list; -} _sapp_xdnd_t; - -typedef struct { - uint8_t mouse_buttons; - Display* display; - int screen; - Window root; - Colormap colormap; - Window window; - Cursor hidden_cursor; - int window_state; - float dpi; - unsigned char error_code; - Atom UTF8_STRING; - Atom WM_PROTOCOLS; - Atom WM_DELETE_WINDOW; - Atom WM_STATE; - Atom NET_WM_NAME; - Atom NET_WM_ICON_NAME; - Atom NET_WM_ICON; - Atom NET_WM_STATE; - Atom NET_WM_STATE_FULLSCREEN; - _sapp_xi_t xi; - _sapp_xdnd_t xdnd; -} _sapp_x11_t; - -typedef struct { - void* libgl; - int major; - int minor; - int event_base; - int error_base; - GLXContext ctx; - GLXWindow window; - - // GLX 1.3 functions - PFNGLXGETFBCONFIGSPROC GetFBConfigs; - PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib; - PFNGLXGETCLIENTSTRINGPROC GetClientString; - PFNGLXQUERYEXTENSIONPROC QueryExtension; - PFNGLXQUERYVERSIONPROC QueryVersion; - PFNGLXDESTROYCONTEXTPROC DestroyContext; - PFNGLXMAKECURRENTPROC MakeCurrent; - PFNGLXSWAPBUFFERSPROC SwapBuffers; - PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString; - PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig; - PFNGLXCREATEWINDOWPROC CreateWindow; - PFNGLXDESTROYWINDOWPROC DestroyWindow; - - // GLX 1.4 and extension functions - PFNGLXGETPROCADDRESSPROC GetProcAddress; - PFNGLXGETPROCADDRESSPROC GetProcAddressARB; - PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT; - PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA; - PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; - - // extension availability - bool EXT_swap_control; - bool MESA_swap_control; - bool ARB_multisample; - bool ARB_create_context; - bool ARB_create_context_profile; -} _sapp_glx_t; - -#endif // _SAPP_LINUX - -/*== COMMON DECLARATIONS =====================================================*/ - -/* helper macros */ -#define _sapp_def(val, def) (((val) == 0) ? (def) : (val)) -#define _sapp_absf(a) (((a)<0.0f)?-(a):(a)) - -#define _SAPP_MAX_TITLE_LENGTH (128) -/* NOTE: the pixel format values *must* be compatible with sg_pixel_format */ -#define _SAPP_PIXELFORMAT_RGBA8 (23) -#define _SAPP_PIXELFORMAT_BGRA8 (27) -#define _SAPP_PIXELFORMAT_DEPTH (41) -#define _SAPP_PIXELFORMAT_DEPTH_STENCIL (42) - -#if defined(_SAPP_MACOS) || defined(_SAPP_IOS) - // this is ARC compatible - #if defined(__cplusplus) - #define _SAPP_CLEAR(type, item) { item = (type) { }; } - #else - #define _SAPP_CLEAR(type, item) { item = (type) { 0 }; } - #endif -#else - #define _SAPP_CLEAR(type, item) { memset(&item, 0, sizeof(item)); } -#endif - -typedef struct { - bool enabled; - int buf_size; - char* buffer; -} _sapp_clipboard_t; - -typedef struct { - bool enabled; - int max_files; - int max_path_length; - int num_files; - int buf_size; - char* buffer; -} _sapp_drop_t; - -typedef struct { - float x, y; - float dx, dy; - bool shown; - bool locked; - bool pos_valid; -} _sapp_mouse_t; - -typedef struct { - sapp_desc desc; - bool valid; - bool fullscreen; - bool gles2_fallback; - bool first_frame; - bool init_called; - bool cleanup_called; - bool quit_requested; - bool quit_ordered; - bool event_consumed; - bool html5_ask_leave_site; - bool onscreen_keyboard_shown; - int window_width; - int window_height; - int framebuffer_width; - int framebuffer_height; - int sample_count; - int swap_interval; - float dpi_scale; - uint64_t frame_count; - sapp_event event; - _sapp_mouse_t mouse; - _sapp_clipboard_t clipboard; - _sapp_drop_t drop; - sapp_icon_desc default_icon_desc; - uint32_t* default_icon_pixels; - #if defined(_SAPP_MACOS) - _sapp_macos_t macos; - #elif defined(_SAPP_IOS) - _sapp_ios_t ios; - #elif defined(_SAPP_EMSCRIPTEN) - _sapp_emsc_t emsc; - #elif defined(_SAPP_WIN32) - _sapp_win32_t win32; - #if defined(SOKOL_D3D11) - _sapp_d3d11_t d3d11; - #elif defined(SOKOL_GLCORE33) - _sapp_wgl_t wgl; - #endif - #elif defined(_SAPP_UWP) - _sapp_uwp_t uwp; - #if defined(SOKOL_D3D11) - _sapp_d3d11_t d3d11; - #endif - #elif defined(_SAPP_ANDROID) - _sapp_android_t android; - #elif defined(_SAPP_LINUX) - _sapp_x11_t x11; - _sapp_glx_t glx; - #endif - char html5_canvas_selector[_SAPP_MAX_TITLE_LENGTH]; - char window_title[_SAPP_MAX_TITLE_LENGTH]; /* UTF-8 */ - wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; /* UTF-32 or UCS-2 */ - sapp_keycode keycodes[SAPP_MAX_KEYCODES]; -} _sapp_t; -static _sapp_t _sapp; - -/*=== PRIVATE HELPER FUNCTIONS ===============================================*/ -_SOKOL_PRIVATE void _sapp_fail(const char* msg) { - if (_sapp.desc.fail_cb) { - _sapp.desc.fail_cb(msg); - } - else if (_sapp.desc.fail_userdata_cb) { - _sapp.desc.fail_userdata_cb(msg, _sapp.desc.user_data); - } - else { - SOKOL_LOG(msg); - } - SOKOL_ABORT(); -} - -_SOKOL_PRIVATE void _sapp_call_init(void) { - if (_sapp.desc.init_cb) { - _sapp.desc.init_cb(); - } - else if (_sapp.desc.init_userdata_cb) { - _sapp.desc.init_userdata_cb(_sapp.desc.user_data); - } - _sapp.init_called = true; -} - -_SOKOL_PRIVATE void _sapp_call_frame(void) { - if (_sapp.init_called && !_sapp.cleanup_called) { - if (_sapp.desc.frame_cb) { - _sapp.desc.frame_cb(); - } - else if (_sapp.desc.frame_userdata_cb) { - _sapp.desc.frame_userdata_cb(_sapp.desc.user_data); - } - } -} - -_SOKOL_PRIVATE void _sapp_call_cleanup(void) { - if (!_sapp.cleanup_called) { - if (_sapp.desc.cleanup_cb) { - _sapp.desc.cleanup_cb(); - } - else if (_sapp.desc.cleanup_userdata_cb) { - _sapp.desc.cleanup_userdata_cb(_sapp.desc.user_data); - } - _sapp.cleanup_called = true; - } -} - -_SOKOL_PRIVATE bool _sapp_call_event(const sapp_event* e) { - if (!_sapp.cleanup_called) { - if (_sapp.desc.event_cb) { - _sapp.desc.event_cb(e); - } - else if (_sapp.desc.event_userdata_cb) { - _sapp.desc.event_userdata_cb(e, _sapp.desc.user_data); - } - } - if (_sapp.event_consumed) { - _sapp.event_consumed = false; - return true; - } - else { - return false; - } -} - -_SOKOL_PRIVATE char* _sapp_dropped_file_path_ptr(int index) { - SOKOL_ASSERT(_sapp.drop.buffer); - SOKOL_ASSERT((index >= 0) && (index <= _sapp.drop.max_files)); - int offset = index * _sapp.drop.max_path_length; - SOKOL_ASSERT(offset < _sapp.drop.buf_size); - return &_sapp.drop.buffer[offset]; -} - -/* Copy a string into a fixed size buffer with guaranteed zero- - termination. - - Return false if the string didn't fit into the buffer and had to be clamped. - - FIXME: Currently UTF-8 strings might become invalid if the string - is clamped, because the last zero-byte might be written into - the middle of a multi-byte sequence. -*/ -_SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, int max_len) { - SOKOL_ASSERT(src && dst && (max_len > 0)); - char* const end = &(dst[max_len-1]); - char c = 0; - for (int i = 0; i < max_len; i++) { - c = *src; - if (c != 0) { - src++; - } - *dst++ = c; - } - /* truncated? */ - if (c != 0) { - *end = 0; - return false; - } - else { - return true; - } -} - -_SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* in_desc) { - sapp_desc desc = *in_desc; - desc.width = _sapp_def(desc.width, 640); - desc.height = _sapp_def(desc.height, 480); - desc.sample_count = _sapp_def(desc.sample_count, 1); - desc.swap_interval = _sapp_def(desc.swap_interval, 1); - desc.html5_canvas_name = _sapp_def(desc.html5_canvas_name, "canvas"); - desc.clipboard_size = _sapp_def(desc.clipboard_size, 8192); - desc.max_dropped_files = _sapp_def(desc.max_dropped_files, 1); - desc.max_dropped_file_path_length = _sapp_def(desc.max_dropped_file_path_length, 2048); - desc.window_title = _sapp_def(desc.window_title, "sokol_app"); - return desc; -} - -_SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) { - _SAPP_CLEAR(_sapp_t, _sapp); - _sapp.desc = _sapp_desc_defaults(desc); - _sapp.first_frame = true; - _sapp.window_width = _sapp.desc.width; - _sapp.window_height = _sapp.desc.height; - _sapp.framebuffer_width = _sapp.window_width; - _sapp.framebuffer_height = _sapp.window_height; - _sapp.sample_count = _sapp.desc.sample_count; - _sapp.swap_interval = _sapp.desc.swap_interval; - _sapp.html5_canvas_selector[0] = '#'; - _sapp_strcpy(_sapp.desc.html5_canvas_name, &_sapp.html5_canvas_selector[1], sizeof(_sapp.html5_canvas_selector) - 1); - _sapp.desc.html5_canvas_name = &_sapp.html5_canvas_selector[1]; - _sapp.html5_ask_leave_site = _sapp.desc.html5_ask_leave_site; - _sapp.clipboard.enabled = _sapp.desc.enable_clipboard; - if (_sapp.clipboard.enabled) { - _sapp.clipboard.buf_size = _sapp.desc.clipboard_size; - _sapp.clipboard.buffer = (char*) SOKOL_CALLOC(1, (size_t)_sapp.clipboard.buf_size); - } - _sapp.drop.enabled = _sapp.desc.enable_dragndrop; - if (_sapp.drop.enabled) { - _sapp.drop.max_files = _sapp.desc.max_dropped_files; - _sapp.drop.max_path_length = _sapp.desc.max_dropped_file_path_length; - _sapp.drop.buf_size = _sapp.drop.max_files * _sapp.drop.max_path_length; - _sapp.drop.buffer = (char*) SOKOL_CALLOC(1, (size_t)_sapp.drop.buf_size); - } - _sapp_strcpy(_sapp.desc.window_title, _sapp.window_title, sizeof(_sapp.window_title)); - _sapp.desc.window_title = _sapp.window_title; - _sapp.dpi_scale = 1.0f; - _sapp.fullscreen = _sapp.desc.fullscreen; - _sapp.mouse.shown = true; -} - -_SOKOL_PRIVATE void _sapp_discard_state(void) { - if (_sapp.clipboard.enabled) { - SOKOL_ASSERT(_sapp.clipboard.buffer); - SOKOL_FREE((void*)_sapp.clipboard.buffer); - } - if (_sapp.drop.enabled) { - SOKOL_ASSERT(_sapp.drop.buffer); - SOKOL_FREE((void*)_sapp.drop.buffer); - } - if (_sapp.default_icon_pixels) { - SOKOL_FREE((void*)_sapp.default_icon_pixels); - } - _SAPP_CLEAR(_sapp_t, _sapp); -} - -_SOKOL_PRIVATE void _sapp_init_event(sapp_event_type type) { - memset(&_sapp.event, 0, sizeof(_sapp.event)); - _sapp.event.type = type; - _sapp.event.frame_count = _sapp.frame_count; - _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; - _sapp.event.window_width = _sapp.window_width; - _sapp.event.window_height = _sapp.window_height; - _sapp.event.framebuffer_width = _sapp.framebuffer_width; - _sapp.event.framebuffer_height = _sapp.framebuffer_height; - _sapp.event.mouse_x = _sapp.mouse.x; - _sapp.event.mouse_y = _sapp.mouse.y; - _sapp.event.mouse_dx = _sapp.mouse.dx; - _sapp.event.mouse_dy = _sapp.mouse.dy; -} - -_SOKOL_PRIVATE bool _sapp_events_enabled(void) { - /* only send events when an event callback is set, and the init function was called */ - return (_sapp.desc.event_cb || _sapp.desc.event_userdata_cb) && _sapp.init_called; -} - -_SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) { - if ((scan_code >= 0) && (scan_code < SAPP_MAX_KEYCODES)) { - return _sapp.keycodes[scan_code]; - } - else { - return SAPP_KEYCODE_INVALID; - } -} - -_SOKOL_PRIVATE void _sapp_clear_drop_buffer(void) { - if (_sapp.drop.enabled) { - SOKOL_ASSERT(_sapp.drop.buffer); - memset(_sapp.drop.buffer, 0, (size_t)_sapp.drop.buf_size); - } -} - -_SOKOL_PRIVATE void _sapp_frame(void) { - if (_sapp.first_frame) { - _sapp.first_frame = false; - _sapp_call_init(); - } - _sapp_call_frame(); - _sapp.frame_count++; -} - -_SOKOL_PRIVATE bool _sapp_image_validate(const sapp_image_desc* desc) { - SOKOL_ASSERT(desc->width > 0); - SOKOL_ASSERT(desc->height > 0); - SOKOL_ASSERT(desc->pixels.ptr != 0); - SOKOL_ASSERT(desc->pixels.size > 0); - const size_t wh_size = (size_t)(desc->width * desc->height) * sizeof(uint32_t); - if (wh_size != desc->pixels.size) { - SOKOL_LOG("Image data size mismatch (must be width*height*4 bytes)\n"); - return false; - } - return true; -} - -_SOKOL_PRIVATE int _sapp_image_bestmatch(const sapp_image_desc image_descs[], int num_images, int width, int height) { - int least_diff = 0x7FFFFFFF; - int least_index = 0; - for (int i = 0; i < num_images; i++) { - int diff = (image_descs[i].width * image_descs[i].height) - (width * height); - if (diff < 0) { - diff = -diff; - } - if (diff < least_diff) { - least_diff = diff; - least_index = i; - } - } - return least_index; -} - -_SOKOL_PRIVATE int _sapp_icon_num_images(const sapp_icon_desc* desc) { - int index = 0; - for (; index < SAPP_MAX_ICONIMAGES; index++) { - if (0 == desc->images[index].pixels.ptr) { - break; - } - } - return index; -} - -_SOKOL_PRIVATE bool _sapp_validate_icon_desc(const sapp_icon_desc* desc, int num_images) { - SOKOL_ASSERT(num_images <= SAPP_MAX_ICONIMAGES); - for (int i = 0; i < num_images; i++) { - const sapp_image_desc* img_desc = &desc->images[i]; - if (!_sapp_image_validate(img_desc)) { - return false; - } - } - return true; -} - -_SOKOL_PRIVATE void _sapp_setup_default_icon(void) { - SOKOL_ASSERT(0 == _sapp.default_icon_pixels); - - const int num_icons = 3; - const int icon_sizes[3] = { 16, 32, 64 }; // must be multiple of 8! - - // allocate a pixel buffer for all icon pixels - int all_num_pixels = 0; - for (int i = 0; i < num_icons; i++) { - all_num_pixels += icon_sizes[i] * icon_sizes[i]; - } - _sapp.default_icon_pixels = (uint32_t*) SOKOL_CALLOC((size_t)all_num_pixels, sizeof(uint32_t)); - - // initialize default_icon_desc struct - uint32_t* dst = _sapp.default_icon_pixels; - const uint32_t* dst_end = dst + all_num_pixels; - (void)dst_end; // silence unused warning in release mode - for (int i = 0; i < num_icons; i++) { - const int dim = (int) icon_sizes[i]; - const int num_pixels = dim * dim; - sapp_image_desc* img_desc = &_sapp.default_icon_desc.images[i]; - img_desc->width = dim; - img_desc->height = dim; - img_desc->pixels.ptr = dst; - img_desc->pixels.size = (size_t)num_pixels * sizeof(uint32_t); - dst += num_pixels; - } - SOKOL_ASSERT(dst == dst_end); - - // Amstrad CPC font 'S' - const uint8_t tile[8] = { - 0x3C, - 0x66, - 0x60, - 0x3C, - 0x06, - 0x66, - 0x3C, - 0x00, - }; - // rainbow colors - const uint32_t colors[8] = { - 0xFF4370FF, - 0xFF26A7FF, - 0xFF58EEFF, - 0xFF57E1D4, - 0xFF65CC9C, - 0xFF6ABB66, - 0xFFF5A542, - 0xFFC2577E, - }; - dst = _sapp.default_icon_pixels; - const uint32_t blank = 0x00FFFFFF; - const uint32_t shadow = 0xFF000000; - for (int i = 0; i < num_icons; i++) { - const int dim = icon_sizes[i]; - SOKOL_ASSERT((dim % 8) == 0); - const int scale = dim / 8; - for (int ty = 0, y = 0; ty < 8; ty++) { - const uint32_t color = colors[ty]; - for (int sy = 0; sy < scale; sy++, y++) { - uint8_t bits = tile[ty]; - for (int tx = 0, x = 0; tx < 8; tx++, bits<<=1) { - uint32_t pixel = (0 == (bits & 0x80)) ? blank : color; - for (int sx = 0; sx < scale; sx++, x++) { - SOKOL_ASSERT(dst < dst_end); - *dst++ = pixel; - } - } - } - } - } - SOKOL_ASSERT(dst == dst_end); - - // right shadow - dst = _sapp.default_icon_pixels; - for (int i = 0; i < num_icons; i++) { - const int dim = icon_sizes[i]; - for (int y = 0; y < dim; y++) { - uint32_t prev_color = blank; - for (int x = 0; x < dim; x++) { - const int dst_index = y * dim + x; - const uint32_t cur_color = dst[dst_index]; - if ((cur_color == blank) && (prev_color != blank)) { - dst[dst_index] = shadow; - } - prev_color = cur_color; - } - } - dst += dim * dim; - } - SOKOL_ASSERT(dst == dst_end); - - // bottom shadow - dst = _sapp.default_icon_pixels; - for (int i = 0; i < num_icons; i++) { - const int dim = icon_sizes[i]; - for (int x = 0; x < dim; x++) { - uint32_t prev_color = blank; - for (int y = 0; y < dim; y++) { - const int dst_index = y * dim + x; - const uint32_t cur_color = dst[dst_index]; - if ((cur_color == blank) && (prev_color != blank)) { - dst[dst_index] = shadow; - } - prev_color = cur_color; - } - } - dst += dim * dim; - } - SOKOL_ASSERT(dst == dst_end); -} - -/*== MacOS/iOS ===============================================================*/ -#if defined(_SAPP_APPLE) - -#if __has_feature(objc_arc) -#define _SAPP_OBJC_RELEASE(obj) { obj = nil; } -#else -#define _SAPP_OBJC_RELEASE(obj) { [obj release]; obj = nil; } -#endif - -/*== MacOS ===================================================================*/ -#if defined(_SAPP_MACOS) - -_SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { - _sapp.keycodes[0x1D] = SAPP_KEYCODE_0; - _sapp.keycodes[0x12] = SAPP_KEYCODE_1; - _sapp.keycodes[0x13] = SAPP_KEYCODE_2; - _sapp.keycodes[0x14] = SAPP_KEYCODE_3; - _sapp.keycodes[0x15] = SAPP_KEYCODE_4; - _sapp.keycodes[0x17] = SAPP_KEYCODE_5; - _sapp.keycodes[0x16] = SAPP_KEYCODE_6; - _sapp.keycodes[0x1A] = SAPP_KEYCODE_7; - _sapp.keycodes[0x1C] = SAPP_KEYCODE_8; - _sapp.keycodes[0x19] = SAPP_KEYCODE_9; - _sapp.keycodes[0x00] = SAPP_KEYCODE_A; - _sapp.keycodes[0x0B] = SAPP_KEYCODE_B; - _sapp.keycodes[0x08] = SAPP_KEYCODE_C; - _sapp.keycodes[0x02] = SAPP_KEYCODE_D; - _sapp.keycodes[0x0E] = SAPP_KEYCODE_E; - _sapp.keycodes[0x03] = SAPP_KEYCODE_F; - _sapp.keycodes[0x05] = SAPP_KEYCODE_G; - _sapp.keycodes[0x04] = SAPP_KEYCODE_H; - _sapp.keycodes[0x22] = SAPP_KEYCODE_I; - _sapp.keycodes[0x26] = SAPP_KEYCODE_J; - _sapp.keycodes[0x28] = SAPP_KEYCODE_K; - _sapp.keycodes[0x25] = SAPP_KEYCODE_L; - _sapp.keycodes[0x2E] = SAPP_KEYCODE_M; - _sapp.keycodes[0x2D] = SAPP_KEYCODE_N; - _sapp.keycodes[0x1F] = SAPP_KEYCODE_O; - _sapp.keycodes[0x23] = SAPP_KEYCODE_P; - _sapp.keycodes[0x0C] = SAPP_KEYCODE_Q; - _sapp.keycodes[0x0F] = SAPP_KEYCODE_R; - _sapp.keycodes[0x01] = SAPP_KEYCODE_S; - _sapp.keycodes[0x11] = SAPP_KEYCODE_T; - _sapp.keycodes[0x20] = SAPP_KEYCODE_U; - _sapp.keycodes[0x09] = SAPP_KEYCODE_V; - _sapp.keycodes[0x0D] = SAPP_KEYCODE_W; - _sapp.keycodes[0x07] = SAPP_KEYCODE_X; - _sapp.keycodes[0x10] = SAPP_KEYCODE_Y; - _sapp.keycodes[0x06] = SAPP_KEYCODE_Z; - _sapp.keycodes[0x27] = SAPP_KEYCODE_APOSTROPHE; - _sapp.keycodes[0x2A] = SAPP_KEYCODE_BACKSLASH; - _sapp.keycodes[0x2B] = SAPP_KEYCODE_COMMA; - _sapp.keycodes[0x18] = SAPP_KEYCODE_EQUAL; - _sapp.keycodes[0x32] = SAPP_KEYCODE_GRAVE_ACCENT; - _sapp.keycodes[0x21] = SAPP_KEYCODE_LEFT_BRACKET; - _sapp.keycodes[0x1B] = SAPP_KEYCODE_MINUS; - _sapp.keycodes[0x2F] = SAPP_KEYCODE_PERIOD; - _sapp.keycodes[0x1E] = SAPP_KEYCODE_RIGHT_BRACKET; - _sapp.keycodes[0x29] = SAPP_KEYCODE_SEMICOLON; - _sapp.keycodes[0x2C] = SAPP_KEYCODE_SLASH; - _sapp.keycodes[0x0A] = SAPP_KEYCODE_WORLD_1; - _sapp.keycodes[0x33] = SAPP_KEYCODE_BACKSPACE; - _sapp.keycodes[0x39] = SAPP_KEYCODE_CAPS_LOCK; - _sapp.keycodes[0x75] = SAPP_KEYCODE_DELETE; - _sapp.keycodes[0x7D] = SAPP_KEYCODE_DOWN; - _sapp.keycodes[0x77] = SAPP_KEYCODE_END; - _sapp.keycodes[0x24] = SAPP_KEYCODE_ENTER; - _sapp.keycodes[0x35] = SAPP_KEYCODE_ESCAPE; - _sapp.keycodes[0x7A] = SAPP_KEYCODE_F1; - _sapp.keycodes[0x78] = SAPP_KEYCODE_F2; - _sapp.keycodes[0x63] = SAPP_KEYCODE_F3; - _sapp.keycodes[0x76] = SAPP_KEYCODE_F4; - _sapp.keycodes[0x60] = SAPP_KEYCODE_F5; - _sapp.keycodes[0x61] = SAPP_KEYCODE_F6; - _sapp.keycodes[0x62] = SAPP_KEYCODE_F7; - _sapp.keycodes[0x64] = SAPP_KEYCODE_F8; - _sapp.keycodes[0x65] = SAPP_KEYCODE_F9; - _sapp.keycodes[0x6D] = SAPP_KEYCODE_F10; - _sapp.keycodes[0x67] = SAPP_KEYCODE_F11; - _sapp.keycodes[0x6F] = SAPP_KEYCODE_F12; - _sapp.keycodes[0x69] = SAPP_KEYCODE_F13; - _sapp.keycodes[0x6B] = SAPP_KEYCODE_F14; - _sapp.keycodes[0x71] = SAPP_KEYCODE_F15; - _sapp.keycodes[0x6A] = SAPP_KEYCODE_F16; - _sapp.keycodes[0x40] = SAPP_KEYCODE_F17; - _sapp.keycodes[0x4F] = SAPP_KEYCODE_F18; - _sapp.keycodes[0x50] = SAPP_KEYCODE_F19; - _sapp.keycodes[0x5A] = SAPP_KEYCODE_F20; - _sapp.keycodes[0x73] = SAPP_KEYCODE_HOME; - _sapp.keycodes[0x72] = SAPP_KEYCODE_INSERT; - _sapp.keycodes[0x7B] = SAPP_KEYCODE_LEFT; - _sapp.keycodes[0x3A] = SAPP_KEYCODE_LEFT_ALT; - _sapp.keycodes[0x3B] = SAPP_KEYCODE_LEFT_CONTROL; - _sapp.keycodes[0x38] = SAPP_KEYCODE_LEFT_SHIFT; - _sapp.keycodes[0x37] = SAPP_KEYCODE_LEFT_SUPER; - _sapp.keycodes[0x6E] = SAPP_KEYCODE_MENU; - _sapp.keycodes[0x47] = SAPP_KEYCODE_NUM_LOCK; - _sapp.keycodes[0x79] = SAPP_KEYCODE_PAGE_DOWN; - _sapp.keycodes[0x74] = SAPP_KEYCODE_PAGE_UP; - _sapp.keycodes[0x7C] = SAPP_KEYCODE_RIGHT; - _sapp.keycodes[0x3D] = SAPP_KEYCODE_RIGHT_ALT; - _sapp.keycodes[0x3E] = SAPP_KEYCODE_RIGHT_CONTROL; - _sapp.keycodes[0x3C] = SAPP_KEYCODE_RIGHT_SHIFT; - _sapp.keycodes[0x36] = SAPP_KEYCODE_RIGHT_SUPER; - _sapp.keycodes[0x31] = SAPP_KEYCODE_SPACE; - _sapp.keycodes[0x30] = SAPP_KEYCODE_TAB; - _sapp.keycodes[0x7E] = SAPP_KEYCODE_UP; - _sapp.keycodes[0x52] = SAPP_KEYCODE_KP_0; - _sapp.keycodes[0x53] = SAPP_KEYCODE_KP_1; - _sapp.keycodes[0x54] = SAPP_KEYCODE_KP_2; - _sapp.keycodes[0x55] = SAPP_KEYCODE_KP_3; - _sapp.keycodes[0x56] = SAPP_KEYCODE_KP_4; - _sapp.keycodes[0x57] = SAPP_KEYCODE_KP_5; - _sapp.keycodes[0x58] = SAPP_KEYCODE_KP_6; - _sapp.keycodes[0x59] = SAPP_KEYCODE_KP_7; - _sapp.keycodes[0x5B] = SAPP_KEYCODE_KP_8; - _sapp.keycodes[0x5C] = SAPP_KEYCODE_KP_9; - _sapp.keycodes[0x45] = SAPP_KEYCODE_KP_ADD; - _sapp.keycodes[0x41] = SAPP_KEYCODE_KP_DECIMAL; - _sapp.keycodes[0x4B] = SAPP_KEYCODE_KP_DIVIDE; - _sapp.keycodes[0x4C] = SAPP_KEYCODE_KP_ENTER; - _sapp.keycodes[0x51] = SAPP_KEYCODE_KP_EQUAL; - _sapp.keycodes[0x43] = SAPP_KEYCODE_KP_MULTIPLY; - _sapp.keycodes[0x4E] = SAPP_KEYCODE_KP_SUBTRACT; -} - -_SOKOL_PRIVATE void _sapp_macos_discard_state(void) { - // NOTE: it's safe to call [release] on a nil object - _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); - _SAPP_OBJC_RELEASE(_sapp.macos.app_dlg); - _SAPP_OBJC_RELEASE(_sapp.macos.win_dlg); - _SAPP_OBJC_RELEASE(_sapp.macos.view); - #if defined(SOKOL_METAL) - _SAPP_OBJC_RELEASE(_sapp.macos.mtl_device); - #endif - _SAPP_OBJC_RELEASE(_sapp.macos.window); -} - -_SOKOL_PRIVATE void _sapp_macos_run(const sapp_desc* desc) { - _sapp_init_state(desc); - _sapp_macos_init_keytable(); - [NSApplication sharedApplication]; - // set the application dock icon as early as possible, otherwise - // the dummy icon will be visible for a short time - sapp_set_icon(&_sapp.desc.icon); - _sapp.macos.app_dlg = [[_sapp_macos_app_delegate alloc] init]; - NSApp.delegate = _sapp.macos.app_dlg; - [NSApp run]; - // NOTE: [NSApp run] never returns, instead cleanup code - // must be put into applicationWillTerminate -} - -/* MacOS entry function */ -#if !defined(SOKOL_NO_ENTRY) -int main(int argc, char* argv[]) { - sapp_desc desc = sokol_main(argc, argv); - _sapp_macos_run(&desc); - return 0; -} -#endif /* SOKOL_NO_ENTRY */ - -_SOKOL_PRIVATE uint32_t _sapp_macos_mods(NSEvent* ev) { - const NSEventModifierFlags f = ev.modifierFlags; - const NSUInteger b = NSEvent.pressedMouseButtons; - uint32_t m = 0; - if (f & NSEventModifierFlagShift) { - m |= SAPP_MODIFIER_SHIFT; - } - if (f & NSEventModifierFlagControl) { - m |= SAPP_MODIFIER_CTRL; - } - if (f & NSEventModifierFlagOption) { - m |= SAPP_MODIFIER_ALT; - } - if (f & NSEventModifierFlagCommand) { - m |= SAPP_MODIFIER_SUPER; - } - if (0 != (b & (1<<0))) { - m |= SAPP_MODIFIER_LMB; - } - if (0 != (b & (1<<1))) { - m |= SAPP_MODIFIER_RMB; - } - if (0 != (b & (1<<2))) { - m |= SAPP_MODIFIER_MMB; - } - return m; -} - -_SOKOL_PRIVATE void _sapp_macos_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mod) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - _sapp.event.mouse_button = btn; - _sapp.event.modifiers = mod; - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_macos_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mod) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - _sapp.event.key_code = key; - _sapp.event.key_repeat = repeat; - _sapp.event.modifiers = mod; - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - _sapp_call_event(&_sapp.event); - } -} - -/* NOTE: unlike the iOS version of this function, the macOS version - can dynamically update the DPI scaling factor when a window is moved - between HighDPI / LowDPI screens. -*/ -_SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) { - #if defined(SOKOL_METAL) - const NSRect fb_rect = [_sapp.macos.view bounds]; - _sapp.framebuffer_width = fb_rect.size.width * _sapp.dpi_scale; - _sapp.framebuffer_height = fb_rect.size.height * _sapp.dpi_scale; - #elif defined(SOKOL_GLCORE33) - const NSRect fb_rect = [_sapp.macos.view convertRectToBacking:[_sapp.macos.view frame]]; - _sapp.framebuffer_width = fb_rect.size.width; - _sapp.framebuffer_height = fb_rect.size.height; - #endif - const NSRect bounds = [_sapp.macos.view bounds]; - _sapp.window_width = bounds.size.width; - _sapp.window_height = bounds.size.height; - if (_sapp.framebuffer_width == 0) { - _sapp.framebuffer_width = 1; - } - if (_sapp.framebuffer_height == 0) { - _sapp.framebuffer_height = 1; - } - if (_sapp.window_width == 0) { - _sapp.window_width = 1; - } - if (_sapp.window_height == 0) { - _sapp.window_height = 1; - } - _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width; - - /* NOTE: _sapp_macos_update_dimensions() isn't called each frame, but only - when the window size actually changes, so resizing the MTKView's - in each call is fine even when MTKView doesn't ignore setting an - identical drawableSize. - */ - #if defined(SOKOL_METAL) - CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; - _sapp.macos.view.drawableSize = drawable_size; - #endif -} - -_SOKOL_PRIVATE void _sapp_macos_toggle_fullscreen(void) { - /* NOTE: the _sapp.fullscreen flag is also notified by the - windowDidEnterFullscreen / windowDidExitFullscreen - event handlers - */ - _sapp.fullscreen = !_sapp.fullscreen; - [_sapp.macos.window toggleFullScreen:nil]; -} - -_SOKOL_PRIVATE void _sapp_macos_set_clipboard_string(const char* str) { - @autoreleasepool { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil]; - [pasteboard setString:@(str) forType:NSPasteboardTypeString]; - } -} - -_SOKOL_PRIVATE const char* _sapp_macos_get_clipboard_string(void) { - SOKOL_ASSERT(_sapp.clipboard.buffer); - @autoreleasepool { - _sapp.clipboard.buffer[0] = 0; - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - if (![[pasteboard types] containsObject:NSPasteboardTypeString]) { - return _sapp.clipboard.buffer; - } - NSString* str = [pasteboard stringForType:NSPasteboardTypeString]; - if (!str) { - return _sapp.clipboard.buffer; - } - _sapp_strcpy([str UTF8String], _sapp.clipboard.buffer, _sapp.clipboard.buf_size); - } - return _sapp.clipboard.buffer; -} - -_SOKOL_PRIVATE void _sapp_macos_update_window_title(void) { - [_sapp.macos.window setTitle: [NSString stringWithUTF8String:_sapp.window_title]]; -} - -_SOKOL_PRIVATE void _sapp_macos_update_mouse(NSEvent* event) { - if (!_sapp.mouse.locked) { - const NSPoint mouse_pos = event.locationInWindow; - float new_x = mouse_pos.x * _sapp.dpi_scale; - float new_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1; - /* don't update dx/dy in the very first update */ - if (_sapp.mouse.pos_valid) { - _sapp.mouse.dx = new_x - _sapp.mouse.x; - _sapp.mouse.dy = new_y - _sapp.mouse.y; - } - _sapp.mouse.x = new_x; - _sapp.mouse.y = new_y; - _sapp.mouse.pos_valid = true; - } -} - -_SOKOL_PRIVATE void _sapp_macos_show_mouse(bool visible) { - /* NOTE: this function is only called when the mouse visibility actually changes */ - if (visible) { - CGDisplayShowCursor(kCGDirectMainDisplay); - } - else { - CGDisplayHideCursor(kCGDirectMainDisplay); - } -} - -_SOKOL_PRIVATE void _sapp_macos_lock_mouse(bool lock) { - if (lock == _sapp.mouse.locked) { - return; - } - _sapp.mouse.dx = 0.0f; - _sapp.mouse.dy = 0.0f; - _sapp.mouse.locked = lock; - /* - NOTE that this code doesn't warp the mouse cursor to the window - center as everybody else does it. This lead to a spike in the - *second* mouse-moved event after the warp happened. The - mouse centering doesn't seem to be required (mouse-moved events - are reported correctly even when the cursor is at an edge of the screen). - - NOTE also that the hide/show of the mouse cursor should properly - stack with calls to sapp_show_mouse() - */ - if (_sapp.mouse.locked) { - CGAssociateMouseAndMouseCursorPosition(NO); - CGDisplayHideCursor(kCGDirectMainDisplay); - } - else { - CGDisplayShowCursor(kCGDirectMainDisplay); - CGAssociateMouseAndMouseCursorPosition(YES); - } -} - -_SOKOL_PRIVATE void _sapp_macos_set_icon(const sapp_icon_desc* icon_desc, int num_images) { - NSDockTile* dock_tile = NSApp.dockTile; - const int wanted_width = (int) dock_tile.size.width; - const int wanted_height = (int) dock_tile.size.height; - const int img_index = _sapp_image_bestmatch(icon_desc->images, num_images, wanted_width, wanted_height); - const sapp_image_desc* img_desc = &icon_desc->images[img_index]; - - CGColorSpaceRef cg_color_space = CGColorSpaceCreateDeviceRGB(); - CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)img_desc->pixels.ptr, (CFIndex)img_desc->pixels.size); - CGDataProviderRef cg_data_provider = CGDataProviderCreateWithCFData(cf_data); - CGImageRef cg_img = CGImageCreate( - (size_t)img_desc->width, // width - (size_t)img_desc->height, // height - 8, // bitsPerComponent - 32, // bitsPerPixel - (size_t)img_desc->width * 4,// bytesPerRow - cg_color_space, // space - kCGImageAlphaLast | kCGImageByteOrderDefault, // bitmapInfo - cg_data_provider, // provider - NULL, // decode - false, // shouldInterpolate - kCGRenderingIntentDefault); - CFRelease(cf_data); - CGDataProviderRelease(cg_data_provider); - CGColorSpaceRelease(cg_color_space); - - NSImage* ns_image = [[NSImage alloc] initWithCGImage:cg_img size:dock_tile.size]; - dock_tile.contentView = [NSImageView imageViewWithImage:ns_image]; - [dock_tile display]; - _SAPP_OBJC_RELEASE(ns_image); - CGImageRelease(cg_img); -} - -_SOKOL_PRIVATE void _sapp_macos_frame(void) { - _sapp_frame(); - if (_sapp.quit_requested || _sapp.quit_ordered) { - [_sapp.macos.window performClose:nil]; - } -} - -@implementation _sapp_macos_app_delegate -- (void)applicationDidFinishLaunching:(NSNotification*)aNotification { - _SOKOL_UNUSED(aNotification); - if (_sapp.fullscreen) { - NSRect screen_rect = NSScreen.mainScreen.frame; - _sapp.window_width = screen_rect.size.width; - _sapp.window_height = screen_rect.size.height; - } - if (_sapp.desc.high_dpi) { - _sapp.framebuffer_width = 2 * _sapp.window_width; - _sapp.framebuffer_height = 2 * _sapp.window_height; - } - else { - _sapp.framebuffer_width = _sapp.window_width; - _sapp.framebuffer_height = _sapp.window_height; - } - _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width; - const NSUInteger style = - NSWindowStyleMaskTitled | - NSWindowStyleMaskClosable | - NSWindowStyleMaskMiniaturizable | - NSWindowStyleMaskResizable; - NSRect window_rect = NSMakeRect(0, 0, _sapp.window_width, _sapp.window_height); - _sapp.macos.window = [[_sapp_macos_window alloc] - initWithContentRect:window_rect - styleMask:style - backing:NSBackingStoreBuffered - defer:NO]; - _sapp.macos.window.releasedWhenClosed = NO; // this is necessary for proper cleanup in applicationWillTerminate - _sapp.macos.window.title = [NSString stringWithUTF8String:_sapp.window_title]; - _sapp.macos.window.acceptsMouseMovedEvents = YES; - _sapp.macos.window.restorable = YES; - - _sapp.macos.win_dlg = [[_sapp_macos_window_delegate alloc] init]; - _sapp.macos.window.delegate = _sapp.macos.win_dlg; - #if defined(SOKOL_METAL) - _sapp.macos.mtl_device = MTLCreateSystemDefaultDevice(); - _sapp.macos.view = [[_sapp_macos_view alloc] init]; - [_sapp.macos.view updateTrackingAreas]; - _sapp.macos.view.preferredFramesPerSecond = 60 / _sapp.swap_interval; - _sapp.macos.view.device = _sapp.macos.mtl_device; - _sapp.macos.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; - _sapp.macos.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; - _sapp.macos.view.sampleCount = (NSUInteger) _sapp.sample_count; - _sapp.macos.view.autoResizeDrawable = false; - _sapp.macos.window.contentView = _sapp.macos.view; - [_sapp.macos.window makeFirstResponder:_sapp.macos.view]; - _sapp.macos.view.layer.magnificationFilter = kCAFilterNearest; - #elif defined(SOKOL_GLCORE33) - NSOpenGLPixelFormatAttribute attrs[32]; - int i = 0; - attrs[i++] = NSOpenGLPFAAccelerated; - attrs[i++] = NSOpenGLPFADoubleBuffer; - attrs[i++] = NSOpenGLPFAOpenGLProfile; attrs[i++] = NSOpenGLProfileVersion3_2Core; - attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24; - attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8; - attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24; - attrs[i++] = NSOpenGLPFAStencilSize; attrs[i++] = 8; - if (_sapp.sample_count > 1) { - attrs[i++] = NSOpenGLPFAMultisample; - attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1; - attrs[i++] = NSOpenGLPFASamples; attrs[i++] = (NSOpenGLPixelFormatAttribute)_sapp.sample_count; - } - else { - attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 0; - } - attrs[i++] = 0; - NSOpenGLPixelFormat* glpixelformat_obj = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; - SOKOL_ASSERT(glpixelformat_obj != nil); - - _sapp.macos.view = [[_sapp_macos_view alloc] - initWithFrame:window_rect - pixelFormat:glpixelformat_obj]; - _SAPP_OBJC_RELEASE(glpixelformat_obj); - [_sapp.macos.view updateTrackingAreas]; - if (_sapp.desc.high_dpi) { - [_sapp.macos.view setWantsBestResolutionOpenGLSurface:YES]; - } - else { - [_sapp.macos.view setWantsBestResolutionOpenGLSurface:NO]; - } - - _sapp.macos.window.contentView = _sapp.macos.view; - [_sapp.macos.window makeFirstResponder:_sapp.macos.view]; - - NSTimer* timer_obj = [NSTimer timerWithTimeInterval:0.001 - target:_sapp.macos.view - selector:@selector(timerFired:) - userInfo:nil - repeats:YES]; - [[NSRunLoop currentRunLoop] addTimer:timer_obj forMode:NSDefaultRunLoopMode]; - timer_obj = nil; - #endif - _sapp.valid = true; - if (_sapp.fullscreen) { - /* on GL, this already toggles a rendered frame, so set the valid flag before */ - [_sapp.macos.window toggleFullScreen:self]; - } - else { - [_sapp.macos.window center]; - } - NSApp.activationPolicy = NSApplicationActivationPolicyRegular; - [NSApp activateIgnoringOtherApps:YES]; - [_sapp.macos.window makeKeyAndOrderFront:nil]; - _sapp_macos_update_dimensions(); - [NSEvent setMouseCoalescingEnabled:NO]; -} - -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { - _SOKOL_UNUSED(sender); - return YES; -} - -- (void)applicationWillTerminate:(NSNotification*)notification { - _SOKOL_UNUSED(notification); - _sapp_call_cleanup(); - _sapp_macos_discard_state(); - _sapp_discard_state(); -} -@end - -@implementation _sapp_macos_window_delegate -- (BOOL)windowShouldClose:(id)sender { - _SOKOL_UNUSED(sender); - /* only give user-code a chance to intervene when sapp_quit() wasn't already called */ - if (!_sapp.quit_ordered) { - /* if window should be closed and event handling is enabled, give user code - a chance to intervene via sapp_cancel_quit() - */ - _sapp.quit_requested = true; - _sapp_macos_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); - /* user code hasn't intervened, quit the app */ - if (_sapp.quit_requested) { - _sapp.quit_ordered = true; - } - } - if (_sapp.quit_ordered) { - return YES; - } - else { - return NO; - } -} - -- (void)windowDidResize:(NSNotification*)notification { - _SOKOL_UNUSED(notification); - _sapp_macos_update_dimensions(); - if (!_sapp.first_frame) { - _sapp_macos_app_event(SAPP_EVENTTYPE_RESIZED); - } -} - -- (void)windowDidMiniaturize:(NSNotification*)notification { - _SOKOL_UNUSED(notification); - _sapp_macos_app_event(SAPP_EVENTTYPE_ICONIFIED); -} - -- (void)windowDidDeminiaturize:(NSNotification*)notification { - _SOKOL_UNUSED(notification); - _sapp_macos_app_event(SAPP_EVENTTYPE_RESTORED); -} - -- (void)windowDidBecomeKey:(NSNotification*)notification { - _SOKOL_UNUSED(notification); - _sapp_macos_app_event(SAPP_EVENTTYPE_FOCUSED); -} - -- (void)windowDidResignKey:(NSNotification*)notification { - _SOKOL_UNUSED(notification); - _sapp_macos_app_event(SAPP_EVENTTYPE_UNFOCUSED); -} - -- (void)windowDidEnterFullScreen:(NSNotification*)notification { - _SOKOL_UNUSED(notification); - _sapp.fullscreen = true; -} - -- (void)windowDidExitFullScreen:(NSNotification*)notification { - _SOKOL_UNUSED(notification); - _sapp.fullscreen = false; -} -@end - -@implementation _sapp_macos_window -- (instancetype)initWithContentRect:(NSRect)contentRect - styleMask:(NSWindowStyleMask)style - backing:(NSBackingStoreType)backingStoreType - defer:(BOOL)flag { - if (self = [super initWithContentRect:contentRect styleMask:style backing:backingStoreType defer:flag]) { - #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 - [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]]; - #endif - } - return self; -} - -- (NSDragOperation)draggingEntered:(id)sender { - return NSDragOperationCopy; -} - -- (NSDragOperation)draggingUpdated:(id)sender { - return NSDragOperationCopy; -} - -- (BOOL)performDragOperation:(id)sender { - #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 - NSPasteboard *pboard = [sender draggingPasteboard]; - if ([pboard.types containsObject:NSPasteboardTypeFileURL]) { - _sapp_clear_drop_buffer(); - _sapp.drop.num_files = ((int)pboard.pasteboardItems.count > _sapp.drop.max_files) ? _sapp.drop.max_files : pboard.pasteboardItems.count; - bool drop_failed = false; - for (int i = 0; i < _sapp.drop.num_files; i++) { - NSURL *fileUrl = [NSURL fileURLWithPath:[pboard.pasteboardItems[(NSUInteger)i] stringForType:NSPasteboardTypeFileURL]]; - if (!_sapp_strcpy(fileUrl.standardizedURL.path.UTF8String, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { - SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n"); - drop_failed = true; - break; - } - } - if (!drop_failed) { - if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); - _sapp_call_event(&_sapp.event); - } - } - else { - _sapp_clear_drop_buffer(); - _sapp.drop.num_files = 0; - } - return YES; - } - #endif - return NO; -} -@end - -@implementation _sapp_macos_view -#if defined(SOKOL_GLCORE33) -/* NOTE: this is a hack/fix when the initial window size has been clipped by - macOS because it didn't fit on the screen, in that case the - frame size of the window is reported wrong if low-dpi rendering - was requested (instead the high-dpi dimensions are returned) - until the window is resized for the first time. - - Hooking into reshape and getting the frame dimensions seems to report - the correct dimensions. -*/ -- (void)reshape { - _sapp_macos_update_dimensions(); - [super reshape]; -} -- (void)timerFired:(id)sender { - _SOKOL_UNUSED(sender); - [self setNeedsDisplay:YES]; -} -- (void)prepareOpenGL { - [super prepareOpenGL]; - GLint swapInt = 1; - NSOpenGLContext* ctx = [_sapp.macos.view openGLContext]; - [ctx setValues:&swapInt forParameter:NSOpenGLContextParameterSwapInterval]; - [ctx makeCurrentContext]; -} -#endif - -_SOKOL_PRIVATE void _sapp_macos_poll_input_events() { - /* - - NOTE: late event polling temporarily out-commented to check if this - causes infrequent and almost impossible to reproduce probelms with the - window close events, see: - https://github.com/floooh/sokol/pull/483#issuecomment-805148815 - - - const NSEventMask mask = NSEventMaskLeftMouseDown | - NSEventMaskLeftMouseUp| - NSEventMaskRightMouseDown | - NSEventMaskRightMouseUp | - NSEventMaskMouseMoved | - NSEventMaskLeftMouseDragged | - NSEventMaskRightMouseDragged | - NSEventMaskMouseEntered | - NSEventMaskMouseExited | - NSEventMaskKeyDown | - NSEventMaskKeyUp | - NSEventMaskCursorUpdate | - NSEventMaskScrollWheel | - NSEventMaskTabletPoint | - NSEventMaskTabletProximity | - NSEventMaskOtherMouseDown | - NSEventMaskOtherMouseUp | - NSEventMaskOtherMouseDragged | - NSEventMaskPressure | - NSEventMaskDirectTouch; - @autoreleasepool { - for (;;) { - // NOTE: using NSDefaultRunLoopMode here causes stuttering in the GL backend, - // see: https://github.com/floooh/sokol/issues/486 - NSEvent* event = [NSApp nextEventMatchingMask:mask untilDate:nil inMode:NSEventTrackingRunLoopMode dequeue:YES]; - if (event == nil) { - break; - } - [NSApp sendEvent:event]; - } - } - */ -} - -- (void)drawRect:(NSRect)rect { - _SOKOL_UNUSED(rect); - /* Catch any last-moment input events */ - _sapp_macos_poll_input_events(); - @autoreleasepool { - _sapp_macos_frame(); - } - #if !defined(SOKOL_METAL) - [[_sapp.macos.view openGLContext] flushBuffer]; - #endif -} - -- (BOOL)isOpaque { - return YES; -} -- (BOOL)canBecomeKeyView { - return YES; -} -- (BOOL)acceptsFirstResponder { - return YES; -} -- (void)updateTrackingAreas { - if (_sapp.macos.tracking_area != nil) { - [self removeTrackingArea:_sapp.macos.tracking_area]; - _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); - } - const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | - NSTrackingActiveInKeyWindow | - NSTrackingEnabledDuringMouseDrag | - NSTrackingCursorUpdate | - NSTrackingInVisibleRect | - NSTrackingAssumeInside; - _sapp.macos.tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; - [self addTrackingArea:_sapp.macos.tracking_area]; - [super updateTrackingAreas]; -} -- (void)mouseEntered:(NSEvent*)event { - _sapp_macos_update_mouse(event); - /* don't send mouse enter/leave while dragging (so that it behaves the same as - on Windows while SetCapture is active - */ - if (0 == _sapp.macos.mouse_buttons) { - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event)); - } -} -- (void)mouseExited:(NSEvent*)event { - _sapp_macos_update_mouse(event); - if (0 == _sapp.macos.mouse_buttons) { - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event)); - } -} -- (void)mouseDown:(NSEvent*)event { - _sapp_macos_update_mouse(event); - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mods(event)); - _sapp.macos.mouse_buttons |= (1< 0.0f) || (_sapp_absf(dy) > 0.0f)) { - _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); - _sapp.event.modifiers = _sapp_macos_mods(event); - _sapp.event.scroll_x = dx; - _sapp.event.scroll_y = dy; - _sapp_call_event(&_sapp.event); - } - } -} -- (void)keyDown:(NSEvent*)event { - if (_sapp_events_enabled()) { - const uint32_t mods = _sapp_macos_mods(event); - /* NOTE: macOS doesn't send keyUp events while the Cmd key is pressed, - as a workaround, to prevent key presses from sticking we'll send - a keyup event following right after the keydown if SUPER is also pressed - */ - const sapp_keycode key_code = _sapp_translate_key(event.keyCode); - _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_DOWN, key_code, event.isARepeat, mods); - if (0 != (mods & SAPP_MODIFIER_SUPER)) { - _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, key_code, event.isARepeat, mods); - } - const NSString* chars = event.characters; - const NSUInteger len = chars.length; - if (len > 0) { - _sapp_init_event(SAPP_EVENTTYPE_CHAR); - _sapp.event.modifiers = mods; - for (NSUInteger i = 0; i < len; i++) { - const unichar codepoint = [chars characterAtIndex:i]; - if ((codepoint & 0xFF00) == 0xF700) { - continue; - } - _sapp.event.char_code = codepoint; - _sapp.event.key_repeat = event.isARepeat; - _sapp_call_event(&_sapp.event); - } - } - /* if this is a Cmd+V (paste), also send a CLIPBOARD_PASTE event */ - if (_sapp.clipboard.enabled && (mods == SAPP_MODIFIER_SUPER) && (key_code == SAPP_KEYCODE_V)) { - _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); - _sapp_call_event(&_sapp.event); - } - } -} -- (void)keyUp:(NSEvent*)event { - _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, - _sapp_translate_key(event.keyCode), - event.isARepeat, - _sapp_macos_mods(event)); -} -- (void)flagsChanged:(NSEvent*)event { - const uint32_t old_f = _sapp.macos.flags_changed_store; - const uint32_t new_f = event.modifierFlags; - _sapp.macos.flags_changed_store = new_f; - sapp_keycode key_code = SAPP_KEYCODE_INVALID; - bool down = false; - if ((new_f ^ old_f) & NSEventModifierFlagShift) { - key_code = SAPP_KEYCODE_LEFT_SHIFT; - down = 0 != (new_f & NSEventModifierFlagShift); - } - if ((new_f ^ old_f) & NSEventModifierFlagControl) { - key_code = SAPP_KEYCODE_LEFT_CONTROL; - down = 0 != (new_f & NSEventModifierFlagControl); - } - if ((new_f ^ old_f) & NSEventModifierFlagOption) { - key_code = SAPP_KEYCODE_LEFT_ALT; - down = 0 != (new_f & NSEventModifierFlagOption); - } - if ((new_f ^ old_f) & NSEventModifierFlagCommand) { - key_code = SAPP_KEYCODE_LEFT_SUPER; - down = 0 != (new_f & NSEventModifierFlagCommand); - } - if (key_code != SAPP_KEYCODE_INVALID) { - _sapp_macos_key_event(down ? SAPP_EVENTTYPE_KEY_DOWN : SAPP_EVENTTYPE_KEY_UP, - key_code, - false, - _sapp_macos_mods(event)); - } -} -- (void)cursorUpdate:(NSEvent*)event { - _SOKOL_UNUSED(event); - if (_sapp.desc.user_cursor) { - _sapp_macos_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR); - } -} -@end - -#endif /* MacOS */ - -/*== iOS =====================================================================*/ -#if defined(_SAPP_IOS) - -_SOKOL_PRIVATE void _sapp_ios_discard_state(void) { - // NOTE: it's safe to call [release] on a nil object - _SAPP_OBJC_RELEASE(_sapp.ios.textfield_dlg); - _SAPP_OBJC_RELEASE(_sapp.ios.textfield); - #if defined(SOKOL_METAL) - _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl); - _SAPP_OBJC_RELEASE(_sapp.ios.mtl_device); - #else - _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl); - _SAPP_OBJC_RELEASE(_sapp.ios.eagl_ctx); - #endif - _SAPP_OBJC_RELEASE(_sapp.ios.view); - _SAPP_OBJC_RELEASE(_sapp.ios.window); -} - -_SOKOL_PRIVATE void _sapp_ios_run(const sapp_desc* desc) { - _sapp_init_state(desc); - static int argc = 1; - static char* argv[] = { (char*)"sokol_app" }; - UIApplicationMain(argc, argv, nil, NSStringFromClass([_sapp_app_delegate class])); -} - -/* iOS entry function */ -#if !defined(SOKOL_NO_ENTRY) -int main(int argc, char* argv[]) { - sapp_desc desc = sokol_main(argc, argv); - _sapp_ios_run(&desc); - return 0; -} -#endif /* SOKOL_NO_ENTRY */ - -_SOKOL_PRIVATE void _sapp_ios_app_event(sapp_event_type type) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet* touches, UIEvent* event) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - NSEnumerator* enumerator = event.allTouches.objectEnumerator; - UITouch* ios_touch; - while ((ios_touch = [enumerator nextObject])) { - if ((_sapp.event.num_touches + 1) < SAPP_MAX_TOUCHPOINTS) { - CGPoint ios_pos = [ios_touch locationInView:_sapp.ios.view]; - sapp_touchpoint* cur_point = &_sapp.event.touches[_sapp.event.num_touches++]; - cur_point->identifier = (uintptr_t) ios_touch; - cur_point->pos_x = ios_pos.x * _sapp.dpi_scale; - cur_point->pos_y = ios_pos.y * _sapp.dpi_scale; - cur_point->changed = [touches containsObject:ios_touch]; - } - } - if (_sapp.event.num_touches > 0) { - _sapp_call_event(&_sapp.event); - } - } -} - -_SOKOL_PRIVATE void _sapp_ios_update_dimensions(void) { - CGRect screen_rect = UIScreen.mainScreen.bounds; - _sapp.framebuffer_width = (int)(screen_rect.size.width * _sapp.dpi_scale); - _sapp.framebuffer_height = (int)(screen_rect.size.height * _sapp.dpi_scale); - _sapp.window_width = (int)screen_rect.size.width; - _sapp.window_height = (int)screen_rect.size.height; - int cur_fb_width, cur_fb_height; - #if defined(SOKOL_METAL) - const CGSize fb_size = _sapp.ios.view.drawableSize; - cur_fb_width = (int) fb_size.width; - cur_fb_height = (int) fb_size.height; - #else - cur_fb_width = (int) _sapp.ios.view.drawableWidth; - cur_fb_height = (int) _sapp.ios.view.drawableHeight; - #endif - const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || - (_sapp.framebuffer_height != cur_fb_height); - if (dim_changed) { - #if defined(SOKOL_METAL) - const CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; - _sapp.ios.view.drawableSize = drawable_size; - #else - // nothing to do here, GLKView correctly respects the view's contentScaleFactor - #endif - if (!_sapp.first_frame) { - _sapp_ios_app_event(SAPP_EVENTTYPE_RESIZED); - } - } -} - -_SOKOL_PRIVATE void _sapp_ios_frame(void) { - _sapp_ios_update_dimensions(); - _sapp_frame(); -} - -_SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { - /* if not happened yet, create an invisible text field */ - if (nil == _sapp.ios.textfield) { - _sapp.ios.textfield_dlg = [[_sapp_textfield_dlg alloc] init]; - _sapp.ios.textfield = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 100, 50)]; - _sapp.ios.textfield.keyboardType = UIKeyboardTypeDefault; - _sapp.ios.textfield.returnKeyType = UIReturnKeyDefault; - _sapp.ios.textfield.autocapitalizationType = UITextAutocapitalizationTypeNone; - _sapp.ios.textfield.autocorrectionType = UITextAutocorrectionTypeNo; - _sapp.ios.textfield.spellCheckingType = UITextSpellCheckingTypeNo; - _sapp.ios.textfield.hidden = YES; - _sapp.ios.textfield.text = @"x"; - _sapp.ios.textfield.delegate = _sapp.ios.textfield_dlg; - [_sapp.ios.view_ctrl.view addSubview:_sapp.ios.textfield]; - - [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg - selector:@selector(keyboardWasShown:) - name:UIKeyboardDidShowNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg - selector:@selector(keyboardWillBeHidden:) - name:UIKeyboardWillHideNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg - selector:@selector(keyboardDidChangeFrame:) - name:UIKeyboardDidChangeFrameNotification object:nil]; - } - if (shown) { - /* setting the text field as first responder brings up the onscreen keyboard */ - [_sapp.ios.textfield becomeFirstResponder]; - } - else { - [_sapp.ios.textfield resignFirstResponder]; - } -} - -@implementation _sapp_app_delegate -- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { - CGRect screen_rect = UIScreen.mainScreen.bounds; - _sapp.ios.window = [[UIWindow alloc] initWithFrame:screen_rect]; - _sapp.window_width = screen_rect.size.width; - _sapp.window_height = screen_rect.size.height; - if (_sapp.desc.high_dpi) { - _sapp.dpi_scale = (float) UIScreen.mainScreen.nativeScale; - } - else { - _sapp.dpi_scale = 1.0f; - } - _sapp.framebuffer_width = _sapp.window_width * _sapp.dpi_scale; - _sapp.framebuffer_height = _sapp.window_height * _sapp.dpi_scale; - #if defined(SOKOL_METAL) - _sapp.ios.mtl_device = MTLCreateSystemDefaultDevice(); - _sapp.ios.view = [[_sapp_ios_view alloc] init]; - _sapp.ios.view.preferredFramesPerSecond = 60 / _sapp.swap_interval; - _sapp.ios.view.device = _sapp.ios.mtl_device; - _sapp.ios.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; - _sapp.ios.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; - _sapp.ios.view.sampleCount = (NSUInteger)_sapp.sample_count; - /* NOTE: iOS MTKView seems to ignore thew view's contentScaleFactor - and automatically renders at Retina resolution. We'll disable - autoResize and instead do the resizing in _sapp_ios_update_dimensions() - */ - _sapp.ios.view.autoResizeDrawable = false; - _sapp.ios.view.userInteractionEnabled = YES; - _sapp.ios.view.multipleTouchEnabled = YES; - _sapp.ios.view_ctrl = [[UIViewController alloc] init]; - _sapp.ios.view_ctrl.modalPresentationStyle = UIModalPresentationFullScreen; - _sapp.ios.view_ctrl.view = _sapp.ios.view; - _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl; - #else - if (_sapp.desc.gl_force_gles2) { - _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; - _sapp.gles2_fallback = true; - } - else { - _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; - if (_sapp.ios.eagl_ctx == nil) { - _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; - _sapp.gles2_fallback = true; - } - } - _sapp.ios.view = [[_sapp_ios_view alloc] initWithFrame:screen_rect]; - _sapp.ios.view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; - _sapp.ios.view.drawableDepthFormat = GLKViewDrawableDepthFormat24; - _sapp.ios.view.drawableStencilFormat = GLKViewDrawableStencilFormatNone; - GLKViewDrawableMultisample msaa = _sapp.sample_count > 1 ? GLKViewDrawableMultisample4X : GLKViewDrawableMultisampleNone; - _sapp.ios.view.drawableMultisample = msaa; - _sapp.ios.view.context = _sapp.ios.eagl_ctx; - _sapp.ios.view.enableSetNeedsDisplay = NO; - _sapp.ios.view.userInteractionEnabled = YES; - _sapp.ios.view.multipleTouchEnabled = YES; - // on GLKView, contentScaleFactor appears to work just fine! - if (_sapp.desc.high_dpi) { - _sapp.ios.view.contentScaleFactor = _sapp.dpi_scale; - } - else { - _sapp.ios.view.contentScaleFactor = 1.0; - } - _sapp.ios.view_ctrl = [[GLKViewController alloc] init]; - _sapp.ios.view_ctrl.view = _sapp.ios.view; - _sapp.ios.view_ctrl.preferredFramesPerSecond = 60 / _sapp.swap_interval; - _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl; - #endif - [_sapp.ios.window makeKeyAndVisible]; - - _sapp.valid = true; - return YES; -} - -- (void)applicationWillResignActive:(UIApplication *)application { - if (!_sapp.ios.suspended) { - _sapp.ios.suspended = true; - _sapp_ios_app_event(SAPP_EVENTTYPE_SUSPENDED); - } -} - -- (void)applicationDidBecomeActive:(UIApplication *)application { - if (_sapp.ios.suspended) { - _sapp.ios.suspended = false; - _sapp_ios_app_event(SAPP_EVENTTYPE_RESUMED); - } -} - -/* NOTE: this method will rarely ever be called, iOS application - which are terminated by the user are usually killed via signal 9 - by the operating system. -*/ -- (void)applicationWillTerminate:(UIApplication *)application { - _SOKOL_UNUSED(application); - _sapp_call_cleanup(); - _sapp_ios_discard_state(); - _sapp_discard_state(); -} -@end - -@implementation _sapp_textfield_dlg -- (void)keyboardWasShown:(NSNotification*)notif { - _sapp.onscreen_keyboard_shown = true; - /* query the keyboard's size, and modify the content view's size */ - if (_sapp.desc.ios_keyboard_resizes_canvas) { - NSDictionary* info = notif.userInfo; - CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; - CGRect view_frame = UIScreen.mainScreen.bounds; - view_frame.size.height -= kbd_h; - _sapp.ios.view.frame = view_frame; - } -} -- (void)keyboardWillBeHidden:(NSNotification*)notif { - _sapp.onscreen_keyboard_shown = false; - if (_sapp.desc.ios_keyboard_resizes_canvas) { - _sapp.ios.view.frame = UIScreen.mainScreen.bounds; - } -} -- (void)keyboardDidChangeFrame:(NSNotification*)notif { - /* this is for the case when the screen rotation changes while the keyboard is open */ - if (_sapp.onscreen_keyboard_shown && _sapp.desc.ios_keyboard_resizes_canvas) { - NSDictionary* info = notif.userInfo; - CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; - CGRect view_frame = UIScreen.mainScreen.bounds; - view_frame.size.height -= kbd_h; - _sapp.ios.view.frame = view_frame; - } -} -- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string { - if (_sapp_events_enabled()) { - const NSUInteger len = string.length; - if (len > 0) { - for (NSUInteger i = 0; i < len; i++) { - unichar c = [string characterAtIndex:i]; - if (c >= 32) { - /* ignore surrogates for now */ - if ((c < 0xD800) || (c > 0xDFFF)) { - _sapp_init_event(SAPP_EVENTTYPE_CHAR); - _sapp.event.char_code = c; - _sapp_call_event(&_sapp.event); - } - } - if (c <= 32) { - sapp_keycode k = SAPP_KEYCODE_INVALID; - switch (c) { - case 10: k = SAPP_KEYCODE_ENTER; break; - case 32: k = SAPP_KEYCODE_SPACE; break; - default: break; - } - if (k != SAPP_KEYCODE_INVALID) { - _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); - _sapp.event.key_code = k; - _sapp_call_event(&_sapp.event); - _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); - _sapp.event.key_code = k; - _sapp_call_event(&_sapp.event); - } - } - } - } - else { - /* this was a backspace */ - _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); - _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; - _sapp_call_event(&_sapp.event); - _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); - _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; - _sapp_call_event(&_sapp.event); - } - } - return NO; -} -@end - -@implementation _sapp_ios_view -- (void)drawRect:(CGRect)rect { - _SOKOL_UNUSED(rect); - @autoreleasepool { - _sapp_ios_frame(); - } -} -- (BOOL)isOpaque { - return YES; -} -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event { - _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_BEGAN, touches, event); -} -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent*)event { - _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_MOVED, touches, event); -} -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent*)event { - _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_ENDED, touches, event); -} -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent*)event { - _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_CANCELLED, touches, event); -} -@end -#endif /* TARGET_OS_IPHONE */ - -#endif /* _SAPP_APPLE */ - -/*== EMSCRIPTEN ==============================================================*/ -#if defined(_SAPP_EMSCRIPTEN) - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*_sapp_html5_fetch_callback) (const sapp_html5_fetch_response*); - -/* this function is called from a JS event handler when the user hides - the onscreen keyboard pressing the 'dismiss keyboard key' -*/ -EMSCRIPTEN_KEEPALIVE void _sapp_emsc_notify_keyboard_hidden(void) { - _sapp.onscreen_keyboard_shown = false; -} - -EMSCRIPTEN_KEEPALIVE void _sapp_emsc_onpaste(const char* str) { - if (_sapp.clipboard.enabled) { - _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); - if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); - _sapp_call_event(&_sapp.event); - } - } -} - -/* https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload */ -EMSCRIPTEN_KEEPALIVE int _sapp_html5_get_ask_leave_site(void) { - return _sapp.html5_ask_leave_site ? 1 : 0; -} - -EMSCRIPTEN_KEEPALIVE void _sapp_emsc_begin_drop(int num) { - if (!_sapp.drop.enabled) { - return; - } - if (num < 0) { - num = 0; - } - if (num > _sapp.drop.max_files) { - num = _sapp.drop.max_files; - } - _sapp.drop.num_files = num; - _sapp_clear_drop_buffer(); -} - -EMSCRIPTEN_KEEPALIVE void _sapp_emsc_drop(int i, const char* name) { - /* NOTE: name is only the filename part, not a path */ - if (!_sapp.drop.enabled) { - return; - } - if (0 == name) { - return; - } - SOKOL_ASSERT(_sapp.drop.num_files <= _sapp.drop.max_files); - if ((i < 0) || (i >= _sapp.drop.num_files)) { - return; - } - if (!_sapp_strcpy(name, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { - SOKOL_LOG("sokol_app.h: dropped file path too long!\n"); - _sapp.drop.num_files = 0; - } -} - -EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y) { - if (!_sapp.drop.enabled) { - return; - } - if (0 == _sapp.drop.num_files) { - /* there was an error copying the filenames */ - _sapp_clear_drop_buffer(); - return; - - } - if (_sapp_events_enabled()) { - _sapp.mouse.x = (float)x * _sapp.dpi_scale; - _sapp.mouse.y = (float)y * _sapp.dpi_scale; - _sapp.mouse.dx = 0.0f; - _sapp.mouse.dy = 0.0f; - _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); - _sapp_call_event(&_sapp.event); - } -} - -EMSCRIPTEN_KEEPALIVE void _sapp_emsc_invoke_fetch_cb(int index, int success, int error_code, _sapp_html5_fetch_callback callback, uint32_t fetched_size, void* buf_ptr, uint32_t buf_size, void* user_data) { - sapp_html5_fetch_response response; - memset(&response, 0, sizeof(response)); - response.succeeded = (0 != success); - response.error_code = (sapp_html5_fetch_error) error_code; - response.file_index = index; - response.fetched_size = fetched_size; - response.buffer_ptr = buf_ptr; - response.buffer_size = buf_size; - response.user_data = user_data; - callback(&response); -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -/* Javascript helper functions for mobile virtual keyboard input */ -EM_JS(void, sapp_js_create_textfield, (void), { - var _sapp_inp = document.createElement("input"); - _sapp_inp.type = "text"; - _sapp_inp.id = "_sokol_app_input_element"; - _sapp_inp.autocapitalize = "none"; - _sapp_inp.addEventListener("focusout", function(_sapp_event) { - __sapp_emsc_notify_keyboard_hidden() - - }); - document.body.append(_sapp_inp); -}); - -EM_JS(void, sapp_js_focus_textfield, (void), { - document.getElementById("_sokol_app_input_element").focus(); -}); - -EM_JS(void, sapp_js_unfocus_textfield, (void), { - document.getElementById("_sokol_app_input_element").blur(); -}); - -EM_JS(void, sapp_js_add_beforeunload_listener, (void), { - Module.sokol_beforeunload = function(event) { - if (__sapp_html5_get_ask_leave_site() != 0) { - event.preventDefault(); - event.returnValue = ' '; - } - }; - window.addEventListener('beforeunload', Module.sokol_beforeunload); -}); - -EM_JS(void, sapp_js_remove_beforeunload_listener, (void), { - window.removeEventListener('beforeunload', Module.sokol_beforeunload); -}); - -EM_JS(void, sapp_js_add_clipboard_listener, (void), { - Module.sokol_paste = function(event) { - var pasted_str = event.clipboardData.getData('text'); - ccall('_sapp_emsc_onpaste', 'void', ['string'], [pasted_str]); - }; - window.addEventListener('paste', Module.sokol_paste); -}); - -EM_JS(void, sapp_js_remove_clipboard_listener, (void), { - window.removeEventListener('paste', Module.sokol_paste); -}); - -EM_JS(void, sapp_js_write_clipboard, (const char* c_str), { - var str = UTF8ToString(c_str); - var ta = document.createElement('textarea'); - ta.setAttribute('autocomplete', 'off'); - ta.setAttribute('autocorrect', 'off'); - ta.setAttribute('autocapitalize', 'off'); - ta.setAttribute('spellcheck', 'false'); - ta.style.left = -100 + 'px'; - ta.style.top = -100 + 'px'; - ta.style.height = 1; - ta.style.width = 1; - ta.value = str; - document.body.appendChild(ta); - ta.select(); - document.execCommand('copy'); - document.body.removeChild(ta); -}); - -_SOKOL_PRIVATE void _sapp_emsc_set_clipboard_string(const char* str) { - sapp_js_write_clipboard(str); -} - -EM_JS(void, sapp_js_add_dragndrop_listeners, (const char* canvas_name_cstr), { - Module.sokol_drop_files = []; - var canvas_name = UTF8ToString(canvas_name_cstr); - var canvas = document.getElementById(canvas_name); - Module.sokol_dragenter = function(event) { - event.stopPropagation(); - event.preventDefault(); - }; - Module.sokol_dragleave = function(event) { - event.stopPropagation(); - event.preventDefault(); - }; - Module.sokol_dragover = function(event) { - event.stopPropagation(); - event.preventDefault(); - }; - Module.sokol_drop = function(event) { - event.stopPropagation(); - event.preventDefault(); - var files = event.dataTransfer.files; - Module.sokol_dropped_files = files; - __sapp_emsc_begin_drop(files.length); - var i; - for (i = 0; i < files.length; i++) { - ccall('_sapp_emsc_drop', 'void', ['number', 'string'], [i, files[i].name]); - } - // FIXME? see computation of targetX/targetY in emscripten via getClientBoundingRect - __sapp_emsc_end_drop(event.clientX, event.clientY); - }; - canvas.addEventListener('dragenter', Module.sokol_dragenter, false); - canvas.addEventListener('dragleave', Module.sokol_dragleave, false); - canvas.addEventListener('dragover', Module.sokol_dragover, false); - canvas.addEventListener('drop', Module.sokol_drop, false); -}); - -EM_JS(uint32_t, sapp_js_dropped_file_size, (int index), { - if ((index < 0) || (index >= Module.sokol_dropped_files.length)) { - return 0; - } - else { - return Module.sokol_dropped_files[index].size; - } -}); - -EM_JS(void, sapp_js_fetch_dropped_file, (int index, _sapp_html5_fetch_callback callback, void* buf_ptr, uint32_t buf_size, void* user_data), { - var reader = new FileReader(); - reader.onload = function(loadEvent) { - var content = loadEvent.target.result; - if (content.byteLength > buf_size) { - // SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL - __sapp_emsc_invoke_fetch_cb(index, 0, 1, callback, 0, buf_ptr, buf_size, user_data); - } - else { - HEAPU8.set(new Uint8Array(content), buf_ptr); - __sapp_emsc_invoke_fetch_cb(index, 1, 0, callback, content.byteLength, buf_ptr, buf_size, user_data); - } - }; - reader.onerror = function() { - // SAPP_HTML5_FETCH_ERROR_OTHER - __sapp_emsc_invoke_fetch_cb(index, 0, 2, callback, 0, buf_ptr, buf_size, user_data); - }; - reader.readAsArrayBuffer(Module.sokol_dropped_files[index]); -}); - -EM_JS(void, sapp_js_remove_dragndrop_listeners, (const char* canvas_name_cstr), { - var canvas_name = UTF8ToString(canvas_name_cstr); - var canvas = document.getElementById(canvas_name); - canvas.removeEventListener('dragenter', Module.sokol_dragenter); - canvas.removeEventListener('dragleave', Module.sokol_dragleave); - canvas.removeEventListener('dragover', Module.sokol_dragover); - canvas.removeEventListener('drop', Module.sokol_drop); -}); - -/* called from the emscripten event handler to update the keyboard visibility - state, this must happen from an JS input event handler, otherwise - the request will be ignored by the browser -*/ -_SOKOL_PRIVATE void _sapp_emsc_update_keyboard_state(void) { - if (_sapp.emsc.wants_show_keyboard) { - /* create input text field on demand */ - if (!_sapp.emsc.textfield_created) { - _sapp.emsc.textfield_created = true; - sapp_js_create_textfield(); - } - /* focus the text input field, this will bring up the keyboard */ - _sapp.onscreen_keyboard_shown = true; - _sapp.emsc.wants_show_keyboard = false; - sapp_js_focus_textfield(); - } - if (_sapp.emsc.wants_hide_keyboard) { - /* unfocus the text input field */ - if (_sapp.emsc.textfield_created) { - _sapp.onscreen_keyboard_shown = false; - _sapp.emsc.wants_hide_keyboard = false; - sapp_js_unfocus_textfield(); - } - } -} - -/* actually showing the onscreen keyboard must be initiated from a JS - input event handler, so we'll just keep track of the desired - state, and the actual state change will happen with the next input event -*/ -_SOKOL_PRIVATE void _sapp_emsc_show_keyboard(bool show) { - if (show) { - _sapp.emsc.wants_show_keyboard = true; - } - else { - _sapp.emsc.wants_hide_keyboard = true; - } -} - -EM_JS(void, sapp_js_pointer_init, (const char* c_str_target), { - // lookup and store canvas object by name - var target_str = UTF8ToString(c_str_target); - Module.sapp_emsc_target = document.getElementById(target_str); - if (!Module.sapp_emsc_target) { - console.log("sokol_app.h: invalid target:" + target_str); - } - if (!Module.sapp_emsc_target.requestPointerLock) { - console.log("sokol_app.h: target doesn't support requestPointerLock:" + target_str); - } -}); - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockchange_cb(int emsc_type, const EmscriptenPointerlockChangeEvent* emsc_event, void* user_data) { - _SOKOL_UNUSED(emsc_type); - _SOKOL_UNUSED(user_data); - _sapp.mouse.locked = emsc_event->isActive; - return EM_TRUE; -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockerror_cb(int emsc_type, const void* reserved, void* user_data) { - _SOKOL_UNUSED(emsc_type); - _SOKOL_UNUSED(reserved); - _SOKOL_UNUSED(user_data); - _sapp.mouse.locked = false; - _sapp.emsc.mouse_lock_requested = false; - return true; -} - -EM_JS(void, sapp_js_request_pointerlock, (void), { - if (Module.sapp_emsc_target) { - if (Module.sapp_emsc_target.requestPointerLock) { - Module.sapp_emsc_target.requestPointerLock(); - } - } -}); - -EM_JS(void, sapp_js_exit_pointerlock, (void), { - if (document.exitPointerLock) { - document.exitPointerLock(); - } -}); - -_SOKOL_PRIVATE void _sapp_emsc_lock_mouse(bool lock) { - if (lock) { - /* request mouse-lock during event handler invocation (see _sapp_emsc_update_mouse_lock_state) */ - _sapp.emsc.mouse_lock_requested = true; - } - else { - /* NOTE: the _sapp.mouse_locked state will be set in the pointerlockchange callback */ - _sapp.emsc.mouse_lock_requested = false; - sapp_js_exit_pointerlock(); - } -} - -/* called from inside event handlers to check if mouse lock had been requested, - and if yes, actually enter mouse lock. -*/ -_SOKOL_PRIVATE void _sapp_emsc_update_mouse_lock_state(void) { - if (_sapp.emsc.mouse_lock_requested) { - _sapp.emsc.mouse_lock_requested = false; - sapp_js_request_pointerlock(); - } -} - -/* JS helper functions to update browser tab favicon */ -EM_JS(void, sapp_js_clear_favicon, (void), { - var link = document.getElementById('sokol-app-favicon'); - if (link) { - document.head.removeChild(link); - } -}); - -EM_JS(void, sapp_js_set_favicon, (int w, int h, const uint8_t* pixels), { - var canvas = document.createElement('canvas'); - canvas.width = w; - canvas.height = h; - var ctx = canvas.getContext('2d'); - var img_data = ctx.createImageData(w, h); - img_data.data.set(HEAPU8.subarray(pixels, pixels + w*h*4)); - ctx.putImageData(img_data, 0, 0); - var new_link = document.createElement('link'); - new_link.id = 'sokol-app-favicon'; - new_link.rel = 'shortcut icon'; - new_link.href = canvas.toDataURL(); - document.head.appendChild(new_link); -}); - -_SOKOL_PRIVATE void _sapp_emsc_set_icon(const sapp_icon_desc* icon_desc, int num_images) { - SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); - sapp_js_clear_favicon(); - // find the best matching image candidate for 16x16 pixels - int img_index = _sapp_image_bestmatch(icon_desc->images, num_images, 16, 16); - const sapp_image_desc* img_desc = &icon_desc->images[img_index]; - sapp_js_set_favicon(img_desc->width, img_desc->height, (const uint8_t*) img_desc->pixels.ptr); -} - -#if defined(SOKOL_WGPU) -_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void); -_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_discard(void); -#endif - -_SOKOL_PRIVATE uint32_t _sapp_emsc_mouse_button_mods(uint16_t buttons) { - uint32_t m = 0; - if (0 != (buttons & (1<<0))) { m |= SAPP_MODIFIER_LMB; } - if (0 != (buttons & (1<<1))) { m |= SAPP_MODIFIER_RMB; } // not a bug - if (0 != (buttons & (1<<2))) { m |= SAPP_MODIFIER_MMB; } // not a bug - return m; -} - -_SOKOL_PRIVATE uint32_t _sapp_emsc_mouse_event_mods(const EmscriptenMouseEvent* ev) { - uint32_t m = 0; - if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; } - if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; } - if (ev->altKey) { m |= SAPP_MODIFIER_ALT; } - if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; } - m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons); - return m; -} - -_SOKOL_PRIVATE uint32_t _sapp_emsc_key_event_mods(const EmscriptenKeyboardEvent* ev) { - uint32_t m = 0; - if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; } - if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; } - if (ev->altKey) { m |= SAPP_MODIFIER_ALT; } - if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; } - m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons); - return m; -} - -_SOKOL_PRIVATE uint32_t _sapp_emsc_touch_event_mods(const EmscriptenTouchEvent* ev) { - uint32_t m = 0; - if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; } - if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; } - if (ev->altKey) { m |= SAPP_MODIFIER_ALT; } - if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; } - m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons); - return m; -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenUiEvent* ui_event, void* user_data) { - _SOKOL_UNUSED(event_type); - _SOKOL_UNUSED(user_data); - double w, h; - emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h); - /* The above method might report zero when toggling HTML5 fullscreen, - in that case use the window's inner width reported by the - emscripten event. This works ok when toggling *into* fullscreen - but doesn't properly restore the previous canvas size when switching - back from fullscreen. - - In general, due to the HTML5's fullscreen API's flaky nature it is - recommended to use 'soft fullscreen' (stretching the WebGL canvas - over the browser windows client rect) with a CSS definition like this: - - position: absolute; - top: 0px; - left: 0px; - margin: 0px; - border: 0; - width: 100%; - height: 100%; - overflow: hidden; - display: block; - */ - if (w < 1.0) { - w = ui_event->windowInnerWidth; - } - else { - _sapp.window_width = (int) w; - } - if (h < 1.0) { - h = ui_event->windowInnerHeight; - } - else { - _sapp.window_height = (int) h; - } - if (_sapp.desc.high_dpi) { - _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); - } - _sapp.framebuffer_width = (int) (w * _sapp.dpi_scale); - _sapp.framebuffer_height = (int) (h * _sapp.dpi_scale); - SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0)); - emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); - #if defined(SOKOL_WGPU) - /* on WebGPU: recreate size-dependent rendering surfaces */ - _sapp_emsc_wgpu_surfaces_discard(); - _sapp_emsc_wgpu_surfaces_create(); - #endif - if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_RESIZED); - _sapp_call_event(&_sapp.event); - } - return true; -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseEvent* emsc_event, void* user_data) { - _SOKOL_UNUSED(user_data); - _sapp.emsc.mouse_buttons = emsc_event->buttons; - if (_sapp.mouse.locked) { - _sapp.mouse.dx = (float) emsc_event->movementX; - _sapp.mouse.dy = (float) emsc_event->movementY; - } - else { - float new_x = emsc_event->targetX * _sapp.dpi_scale; - float new_y = emsc_event->targetY * _sapp.dpi_scale; - if (_sapp.mouse.pos_valid) { - _sapp.mouse.dx = new_x - _sapp.mouse.x; - _sapp.mouse.dy = new_y - _sapp.mouse.y; - } - _sapp.mouse.x = new_x; - _sapp.mouse.y = new_y; - _sapp.mouse.pos_valid = true; - } - if (_sapp_events_enabled() && (emsc_event->button >= 0) && (emsc_event->button < SAPP_MAX_MOUSEBUTTONS)) { - sapp_event_type type; - bool is_button_event = false; - switch (emsc_type) { - case EMSCRIPTEN_EVENT_MOUSEDOWN: - type = SAPP_EVENTTYPE_MOUSE_DOWN; - is_button_event = true; - break; - case EMSCRIPTEN_EVENT_MOUSEUP: - type = SAPP_EVENTTYPE_MOUSE_UP; - is_button_event = true; - break; - case EMSCRIPTEN_EVENT_MOUSEMOVE: - type = SAPP_EVENTTYPE_MOUSE_MOVE; - break; - case EMSCRIPTEN_EVENT_MOUSEENTER: - type = SAPP_EVENTTYPE_MOUSE_ENTER; - break; - case EMSCRIPTEN_EVENT_MOUSELEAVE: - type = SAPP_EVENTTYPE_MOUSE_LEAVE; - break; - default: - type = SAPP_EVENTTYPE_INVALID; - break; - } - if (type != SAPP_EVENTTYPE_INVALID) { - _sapp_init_event(type); - _sapp.event.modifiers = _sapp_emsc_mouse_event_mods(emsc_event); - if (is_button_event) { - switch (emsc_event->button) { - case 0: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_LEFT; break; - case 1: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_MIDDLE; break; - case 2: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_RIGHT; break; - default: _sapp.event.mouse_button = (sapp_mousebutton)emsc_event->button; break; - } - } - else { - _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; - } - _sapp_call_event(&_sapp.event); - } - /* mouse lock can only be activated in mouse button events (not in move, enter or leave) */ - if (is_button_event) { - _sapp_emsc_update_mouse_lock_state(); - } - } - _sapp_emsc_update_keyboard_state(); - return true; -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelEvent* emsc_event, void* user_data) { - _SOKOL_UNUSED(emsc_type); - _SOKOL_UNUSED(user_data); - _sapp.emsc.mouse_buttons = emsc_event->mouse.buttons; - if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); - _sapp.event.modifiers = _sapp_emsc_mouse_event_mods(&emsc_event->mouse); - /* see https://github.com/floooh/sokol/issues/339 */ - float scale; - switch (emsc_event->deltaMode) { - case DOM_DELTA_PIXEL: scale = -0.04f; break; - case DOM_DELTA_LINE: scale = -1.33f; break; - case DOM_DELTA_PAGE: scale = -10.0f; break; // FIXME: this is a guess - default: scale = -0.1f; break; // shouldn't happen - } - _sapp.event.scroll_x = scale * (float)emsc_event->deltaX; - _sapp.event.scroll_y = scale * (float)emsc_event->deltaY; - _sapp_call_event(&_sapp.event); - } - _sapp_emsc_update_keyboard_state(); - _sapp_emsc_update_mouse_lock_state(); - return true; -} - -static struct { - const char* str; - sapp_keycode code; -} _sapp_emsc_keymap[] = { - { "Backspace", SAPP_KEYCODE_BACKSPACE }, - { "Tab", SAPP_KEYCODE_TAB }, - { "Enter", SAPP_KEYCODE_ENTER }, - { "ShiftLeft", SAPP_KEYCODE_LEFT_SHIFT }, - { "ShiftRight", SAPP_KEYCODE_RIGHT_SHIFT }, - { "ControlLeft", SAPP_KEYCODE_LEFT_CONTROL }, - { "ControlRight", SAPP_KEYCODE_RIGHT_CONTROL }, - { "AltLeft", SAPP_KEYCODE_LEFT_ALT }, - { "AltRight", SAPP_KEYCODE_RIGHT_ALT }, - { "Pause", SAPP_KEYCODE_PAUSE }, - { "CapsLock", SAPP_KEYCODE_CAPS_LOCK }, - { "Escape", SAPP_KEYCODE_ESCAPE }, - { "Space", SAPP_KEYCODE_SPACE }, - { "PageUp", SAPP_KEYCODE_PAGE_UP }, - { "PageDown", SAPP_KEYCODE_PAGE_DOWN }, - { "End", SAPP_KEYCODE_END }, - { "Home", SAPP_KEYCODE_HOME }, - { "ArrowLeft", SAPP_KEYCODE_LEFT }, - { "ArrowUp", SAPP_KEYCODE_UP }, - { "ArrowRight", SAPP_KEYCODE_RIGHT }, - { "ArrowDown", SAPP_KEYCODE_DOWN }, - { "PrintScreen", SAPP_KEYCODE_PRINT_SCREEN }, - { "Insert", SAPP_KEYCODE_INSERT }, - { "Delete", SAPP_KEYCODE_DELETE }, - { "Digit0", SAPP_KEYCODE_0 }, - { "Digit1", SAPP_KEYCODE_1 }, - { "Digit2", SAPP_KEYCODE_2 }, - { "Digit3", SAPP_KEYCODE_3 }, - { "Digit4", SAPP_KEYCODE_4 }, - { "Digit5", SAPP_KEYCODE_5 }, - { "Digit6", SAPP_KEYCODE_6 }, - { "Digit7", SAPP_KEYCODE_7 }, - { "Digit8", SAPP_KEYCODE_8 }, - { "Digit9", SAPP_KEYCODE_9 }, - { "KeyA", SAPP_KEYCODE_A }, - { "KeyB", SAPP_KEYCODE_B }, - { "KeyC", SAPP_KEYCODE_C }, - { "KeyD", SAPP_KEYCODE_D }, - { "KeyE", SAPP_KEYCODE_E }, - { "KeyF", SAPP_KEYCODE_F }, - { "KeyG", SAPP_KEYCODE_G }, - { "KeyH", SAPP_KEYCODE_H }, - { "KeyI", SAPP_KEYCODE_I }, - { "KeyJ", SAPP_KEYCODE_J }, - { "KeyK", SAPP_KEYCODE_K }, - { "KeyL", SAPP_KEYCODE_L }, - { "KeyM", SAPP_KEYCODE_M }, - { "KeyN", SAPP_KEYCODE_N }, - { "KeyO", SAPP_KEYCODE_O }, - { "KeyP", SAPP_KEYCODE_P }, - { "KeyQ", SAPP_KEYCODE_Q }, - { "KeyR", SAPP_KEYCODE_R }, - { "KeyS", SAPP_KEYCODE_S }, - { "KeyT", SAPP_KEYCODE_T }, - { "KeyU", SAPP_KEYCODE_U }, - { "KeyV", SAPP_KEYCODE_V }, - { "KeyW", SAPP_KEYCODE_W }, - { "KeyX", SAPP_KEYCODE_X }, - { "KeyY", SAPP_KEYCODE_Y }, - { "KeyZ", SAPP_KEYCODE_Z }, - { "MetaLeft", SAPP_KEYCODE_LEFT_SUPER }, - { "MetaRight", SAPP_KEYCODE_RIGHT_SUPER }, - { "Numpad0", SAPP_KEYCODE_KP_0 }, - { "Numpad1", SAPP_KEYCODE_KP_1 }, - { "Numpad2", SAPP_KEYCODE_KP_2 }, - { "Numpad3", SAPP_KEYCODE_KP_3 }, - { "Numpad4", SAPP_KEYCODE_KP_4 }, - { "Numpad5", SAPP_KEYCODE_KP_5 }, - { "Numpad6", SAPP_KEYCODE_KP_6 }, - { "Numpad7", SAPP_KEYCODE_KP_7 }, - { "Numpad8", SAPP_KEYCODE_KP_8 }, - { "Numpad9", SAPP_KEYCODE_KP_9 }, - { "NumpadMultiply", SAPP_KEYCODE_KP_MULTIPLY }, - { "NumpadAdd", SAPP_KEYCODE_KP_ADD }, - { "NumpadSubtract", SAPP_KEYCODE_KP_SUBTRACT }, - { "NumpadDecimal", SAPP_KEYCODE_KP_DECIMAL }, - { "NumpadDivide", SAPP_KEYCODE_KP_DIVIDE }, - { "F1", SAPP_KEYCODE_F1 }, - { "F2", SAPP_KEYCODE_F2 }, - { "F3", SAPP_KEYCODE_F3 }, - { "F4", SAPP_KEYCODE_F4 }, - { "F5", SAPP_KEYCODE_F5 }, - { "F6", SAPP_KEYCODE_F6 }, - { "F7", SAPP_KEYCODE_F7 }, - { "F8", SAPP_KEYCODE_F8 }, - { "F9", SAPP_KEYCODE_F9 }, - { "F10", SAPP_KEYCODE_F10 }, - { "F11", SAPP_KEYCODE_F11 }, - { "F12", SAPP_KEYCODE_F12 }, - { "NumLock", SAPP_KEYCODE_NUM_LOCK }, - { "ScrollLock", SAPP_KEYCODE_SCROLL_LOCK }, - { "Semicolon", SAPP_KEYCODE_SEMICOLON }, - { "Equal", SAPP_KEYCODE_EQUAL }, - { "Comma", SAPP_KEYCODE_COMMA }, - { "Minus", SAPP_KEYCODE_MINUS }, - { "Period", SAPP_KEYCODE_PERIOD }, - { "Slash", SAPP_KEYCODE_SLASH }, - { "Backquote", SAPP_KEYCODE_GRAVE_ACCENT }, - { "BracketLeft", SAPP_KEYCODE_LEFT_BRACKET }, - { "Backslash", SAPP_KEYCODE_BACKSLASH }, - { "BracketRight", SAPP_KEYCODE_RIGHT_BRACKET }, - { "Quote", SAPP_KEYCODE_GRAVE_ACCENT }, // FIXME: ??? - { 0, SAPP_KEYCODE_INVALID }, -}; - -_SOKOL_PRIVATE sapp_keycode _sapp_emsc_translate_key(const char* str) { - int i = 0; - const char* keystr; - while (( keystr = _sapp_emsc_keymap[i].str )) { - if (0 == strcmp(str, keystr)) { - return _sapp_emsc_keymap[i].code; - } - i += 1; - } - return SAPP_KEYCODE_INVALID; -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboardEvent* emsc_event, void* user_data) { - _SOKOL_UNUSED(user_data); - bool retval = true; - if (_sapp_events_enabled()) { - sapp_event_type type; - switch (emsc_type) { - case EMSCRIPTEN_EVENT_KEYDOWN: - type = SAPP_EVENTTYPE_KEY_DOWN; - break; - case EMSCRIPTEN_EVENT_KEYUP: - type = SAPP_EVENTTYPE_KEY_UP; - break; - case EMSCRIPTEN_EVENT_KEYPRESS: - type = SAPP_EVENTTYPE_CHAR; - break; - default: - type = SAPP_EVENTTYPE_INVALID; - break; - } - if (type != SAPP_EVENTTYPE_INVALID) { - bool send_keyup_followup = false; - _sapp_init_event(type); - _sapp.event.key_repeat = emsc_event->repeat; - _sapp.event.modifiers = _sapp_emsc_key_event_mods(emsc_event); - if (type == SAPP_EVENTTYPE_CHAR) { - _sapp.event.char_code = emsc_event->charCode; - /* workaround to make Cmd+V work on Safari */ - if ((emsc_event->metaKey) && (emsc_event->charCode == 118)) { - retval = false; - } - } - else { - _sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->code); - /* Special hack for macOS: if the Super key is pressed, macOS doesn't - send keyUp events. As a workaround, to prevent keys from - "sticking", we'll send a keyup event following a keydown - when the SUPER key is pressed - */ - if ((type == SAPP_EVENTTYPE_KEY_DOWN) && - (_sapp.event.key_code != SAPP_KEYCODE_LEFT_SUPER) && - (_sapp.event.key_code != SAPP_KEYCODE_RIGHT_SUPER) && - (_sapp.event.modifiers & SAPP_MODIFIER_SUPER)) - { - send_keyup_followup = true; - } - /* only forward a certain key ranges to the browser */ - switch (_sapp.event.key_code) { - case SAPP_KEYCODE_WORLD_1: - case SAPP_KEYCODE_WORLD_2: - case SAPP_KEYCODE_ESCAPE: - case SAPP_KEYCODE_ENTER: - case SAPP_KEYCODE_TAB: - case SAPP_KEYCODE_BACKSPACE: - case SAPP_KEYCODE_INSERT: - case SAPP_KEYCODE_DELETE: - case SAPP_KEYCODE_RIGHT: - case SAPP_KEYCODE_LEFT: - case SAPP_KEYCODE_DOWN: - case SAPP_KEYCODE_UP: - case SAPP_KEYCODE_PAGE_UP: - case SAPP_KEYCODE_PAGE_DOWN: - case SAPP_KEYCODE_HOME: - case SAPP_KEYCODE_END: - case SAPP_KEYCODE_CAPS_LOCK: - case SAPP_KEYCODE_SCROLL_LOCK: - case SAPP_KEYCODE_NUM_LOCK: - case SAPP_KEYCODE_PRINT_SCREEN: - case SAPP_KEYCODE_PAUSE: - case SAPP_KEYCODE_F1: - case SAPP_KEYCODE_F2: - case SAPP_KEYCODE_F3: - case SAPP_KEYCODE_F4: - case SAPP_KEYCODE_F5: - case SAPP_KEYCODE_F6: - case SAPP_KEYCODE_F7: - case SAPP_KEYCODE_F8: - case SAPP_KEYCODE_F9: - case SAPP_KEYCODE_F10: - case SAPP_KEYCODE_F11: - case SAPP_KEYCODE_F12: - case SAPP_KEYCODE_F13: - case SAPP_KEYCODE_F14: - case SAPP_KEYCODE_F15: - case SAPP_KEYCODE_F16: - case SAPP_KEYCODE_F17: - case SAPP_KEYCODE_F18: - case SAPP_KEYCODE_F19: - case SAPP_KEYCODE_F20: - case SAPP_KEYCODE_F21: - case SAPP_KEYCODE_F22: - case SAPP_KEYCODE_F23: - case SAPP_KEYCODE_F24: - case SAPP_KEYCODE_F25: - case SAPP_KEYCODE_LEFT_SHIFT: - case SAPP_KEYCODE_LEFT_CONTROL: - case SAPP_KEYCODE_LEFT_ALT: - case SAPP_KEYCODE_LEFT_SUPER: - case SAPP_KEYCODE_RIGHT_SHIFT: - case SAPP_KEYCODE_RIGHT_CONTROL: - case SAPP_KEYCODE_RIGHT_ALT: - case SAPP_KEYCODE_RIGHT_SUPER: - case SAPP_KEYCODE_MENU: - /* consume the event */ - break; - default: - /* forward key to browser */ - retval = false; - break; - } - } - if (_sapp_call_event(&_sapp.event)) { - /* consume event via sapp_consume_event() */ - retval = true; - } - if (send_keyup_followup) { - _sapp.event.type = SAPP_EVENTTYPE_KEY_UP; - if (_sapp_call_event(&_sapp.event)) { - retval = true; - } - } - } - } - _sapp_emsc_update_keyboard_state(); - _sapp_emsc_update_mouse_lock_state(); - return retval; -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchEvent* emsc_event, void* user_data) { - _SOKOL_UNUSED(user_data); - bool retval = true; - if (_sapp_events_enabled()) { - sapp_event_type type; - switch (emsc_type) { - case EMSCRIPTEN_EVENT_TOUCHSTART: - type = SAPP_EVENTTYPE_TOUCHES_BEGAN; - break; - case EMSCRIPTEN_EVENT_TOUCHMOVE: - type = SAPP_EVENTTYPE_TOUCHES_MOVED; - break; - case EMSCRIPTEN_EVENT_TOUCHEND: - type = SAPP_EVENTTYPE_TOUCHES_ENDED; - break; - case EMSCRIPTEN_EVENT_TOUCHCANCEL: - type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; - break; - default: - type = SAPP_EVENTTYPE_INVALID; - retval = false; - break; - } - if (type != SAPP_EVENTTYPE_INVALID) { - _sapp_init_event(type); - _sapp.event.modifiers = _sapp_emsc_touch_event_mods(emsc_event); - _sapp.event.num_touches = emsc_event->numTouches; - if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) { - _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS; - } - for (int i = 0; i < _sapp.event.num_touches; i++) { - const EmscriptenTouchPoint* src = &emsc_event->touches[i]; - sapp_touchpoint* dst = &_sapp.event.touches[i]; - dst->identifier = (uintptr_t)src->identifier; - dst->pos_x = src->targetX * _sapp.dpi_scale; - dst->pos_y = src->targetY * _sapp.dpi_scale; - dst->changed = src->isChanged; - } - _sapp_call_event(&_sapp.event); - } - } - _sapp_emsc_update_keyboard_state(); - return retval; -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_focus_cb(int emsc_type, const EmscriptenFocusEvent* emsc_event, void* user_data) { - _SOKOL_UNUSED(emsc_type); - _SOKOL_UNUSED(emsc_event); - _SOKOL_UNUSED(user_data); - if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_FOCUSED); - _sapp_call_event(&_sapp.event); - } - return true; -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_blur_cb(int emsc_type, const EmscriptenFocusEvent* emsc_event, void* user_data) { - _SOKOL_UNUSED(emsc_type); - _SOKOL_UNUSED(emsc_event); - _SOKOL_UNUSED(user_data); - if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_UNFOCUSED); - _sapp_call_event(&_sapp.event); - } - return true; -} - -#if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_webgl_context_cb(int emsc_type, const void* reserved, void* user_data) { - _SOKOL_UNUSED(reserved); - _SOKOL_UNUSED(user_data); - sapp_event_type type; - switch (emsc_type) { - case EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST: type = SAPP_EVENTTYPE_SUSPENDED; break; - case EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED: type = SAPP_EVENTTYPE_RESUMED; break; - default: type = SAPP_EVENTTYPE_INVALID; break; - } - if (_sapp_events_enabled() && (SAPP_EVENTTYPE_INVALID != type)) { - _sapp_init_event(type); - _sapp_call_event(&_sapp.event); - } - return true; -} - -_SOKOL_PRIVATE void _sapp_emsc_webgl_init(void) { - EmscriptenWebGLContextAttributes attrs; - emscripten_webgl_init_context_attributes(&attrs); - attrs.alpha = _sapp.desc.alpha; - attrs.depth = true; - attrs.stencil = true; - attrs.antialias = _sapp.sample_count > 1; - attrs.premultipliedAlpha = _sapp.desc.html5_premultiplied_alpha; - attrs.preserveDrawingBuffer = _sapp.desc.html5_preserve_drawing_buffer; - attrs.enableExtensionsByDefault = true; - #if defined(SOKOL_GLES3) - if (_sapp.desc.gl_force_gles2) { - attrs.majorVersion = 1; - _sapp.gles2_fallback = true; - } - else { - attrs.majorVersion = 2; - } - #endif - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs); - if (!ctx) { - attrs.majorVersion = 1; - ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs); - _sapp.gles2_fallback = true; - } - emscripten_webgl_make_context_current(ctx); - - /* some WebGL extension are not enabled automatically by emscripten */ - emscripten_webgl_enable_extension(ctx, "WEBKIT_WEBGL_compressed_texture_pvrtc"); -} -#endif - -#if defined(SOKOL_WGPU) -#define _SAPP_EMSC_WGPU_STATE_INITIAL (0) -#define _SAPP_EMSC_WGPU_STATE_READY (1) -#define _SAPP_EMSC_WGPU_STATE_RUNNING (2) - -#if defined(__cplusplus) -extern "C" { -#endif -/* called when the asynchronous WebGPU device + swapchain init code in JS has finished */ -EMSCRIPTEN_KEEPALIVE void _sapp_emsc_wgpu_ready(int device_id, int swapchain_id, int swapchain_fmt) { - SOKOL_ASSERT(0 == _sapp.emsc.wgpu.device); - _sapp.emsc.wgpu.device = (WGPUDevice) device_id; - _sapp.emsc.wgpu.swapchain = (WGPUSwapChain) swapchain_id; - _sapp.emsc.wgpu.render_format = (WGPUTextureFormat) swapchain_fmt; - _sapp.emsc.wgpu.state = _SAPP_EMSC_WGPU_STATE_READY; -} -#if defined(__cplusplus) -} // extern "C" -#endif - -/* embedded JS function to handle all the asynchronous WebGPU setup */ -EM_JS(void, sapp_js_wgpu_init, (), { - WebGPU.initManagers(); - // FIXME: the extension activation must be more clever here - navigator.gpu.requestAdapter().then(function(adapter) { - console.log("wgpu adapter extensions: " + adapter.extensions); - adapter.requestDevice({ extensions: ["textureCompressionBC"]}).then(function(device) { - var gpuContext = document.getElementById("canvas").getContext("gpupresent"); - console.log("wgpu device extensions: " + adapter.extensions); - gpuContext.getSwapChainPreferredFormat(device).then(function(fmt) { - var swapChainDescriptor = { device: device, format: fmt }; - var swapChain = gpuContext.configureSwapChain(swapChainDescriptor); - var deviceId = WebGPU.mgrDevice.create(device); - var swapChainId = WebGPU.mgrSwapChain.create(swapChain); - var fmtId = WebGPU.TextureFormat.findIndex(function(elm) { return elm==fmt; }); - console.log("wgpu device: " + device); - console.log("wgpu swap chain: " + swapChain); - console.log("wgpu preferred format: " + fmt + " (" + fmtId + ")"); - __sapp_emsc_wgpu_ready(deviceId, swapChainId, fmtId); - }); - }); - }); -}); - -_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void) { - SOKOL_ASSERT(_sapp.emsc.wgpu.device); - SOKOL_ASSERT(_sapp.emsc.wgpu.swapchain); - SOKOL_ASSERT(0 == _sapp.emsc.wgpu.depth_stencil_tex); - SOKOL_ASSERT(0 == _sapp.emsc.wgpu.depth_stencil_view); - SOKOL_ASSERT(0 == _sapp.emsc.wgpu.msaa_tex); - SOKOL_ASSERT(0 == _sapp.emsc.wgpu.msaa_view); - - WGPUTextureDescriptor ds_desc; - memset(&ds_desc, 0, sizeof(ds_desc)); - ds_desc.usage = WGPUTextureUsage_OutputAttachment; - ds_desc.dimension = WGPUTextureDimension_2D; - ds_desc.size.width = (uint32_t) _sapp.framebuffer_width; - ds_desc.size.height = (uint32_t) _sapp.framebuffer_height; - ds_desc.size.depth = 1; - ds_desc.arrayLayerCount = 1; - ds_desc.format = WGPUTextureFormat_Depth24PlusStencil8; - ds_desc.mipLevelCount = 1; - ds_desc.sampleCount = _sapp.sample_count; - _sapp.emsc.wgpu.depth_stencil_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &ds_desc); - _sapp.emsc.wgpu.depth_stencil_view = wgpuTextureCreateView(_sapp.emsc.wgpu.depth_stencil_tex, 0); - - if (_sapp.sample_count > 1) { - WGPUTextureDescriptor msaa_desc; - memset(&msaa_desc, 0, sizeof(msaa_desc)); - msaa_desc.usage = WGPUTextureUsage_OutputAttachment; - msaa_desc.dimension = WGPUTextureDimension_2D; - msaa_desc.size.width = (uint32_t) _sapp.framebuffer_width; - msaa_desc.size.height = (uint32_t) _sapp.framebuffer_height; - msaa_desc.size.depth = 1; - msaa_desc.arrayLayerCount = 1; - msaa_desc.format = _sapp.emsc.wgpu.render_format; - msaa_desc.mipLevelCount = 1; - msaa_desc.sampleCount = _sapp.sample_count; - _sapp.emsc.wgpu.msaa_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &msaa_desc); - _sapp.emsc.wgpu.msaa_view = wgpuTextureCreateView(_sapp.emsc.wgpu.msaa_tex, 0); - } -} - -_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_discard(void) { - if (_sapp.emsc.wgpu.msaa_tex) { - wgpuTextureRelease(_sapp.emsc.wgpu.msaa_tex); - _sapp.emsc.wgpu.msaa_tex = 0; - } - if (_sapp.emsc.wgpu.msaa_view) { - wgpuTextureViewRelease(_sapp.emsc.wgpu.msaa_view); - _sapp.emsc.wgpu.msaa_view = 0; - } - if (_sapp.emsc.wgpu.depth_stencil_tex) { - wgpuTextureRelease(_sapp.emsc.wgpu.depth_stencil_tex); - _sapp.emsc.wgpu.depth_stencil_tex = 0; - } - if (_sapp.emsc.wgpu.depth_stencil_view) { - wgpuTextureViewRelease(_sapp.emsc.wgpu.depth_stencil_view); - _sapp.emsc.wgpu.depth_stencil_view = 0; - } -} - -_SOKOL_PRIVATE void _sapp_emsc_wgpu_next_frame(void) { - if (_sapp.emsc.wgpu.swapchain_view) { - wgpuTextureViewRelease(_sapp.emsc.wgpu.swapchain_view); - } - _sapp.emsc.wgpu.swapchain_view = wgpuSwapChainGetCurrentTextureView(_sapp.emsc.wgpu.swapchain); -} -#endif - -_SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) { - emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); - emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); - emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); - emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); - emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); - emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_wheel_cb); - emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); - emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); - emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); - emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); - emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); - emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); - emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); - emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockchange_cb); - emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockerror_cb); - emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_focus_cb); - emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_blur_cb); - sapp_js_add_beforeunload_listener(); - if (_sapp.clipboard.enabled) { - sapp_js_add_clipboard_listener(); - } - if (_sapp.drop.enabled) { - sapp_js_add_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); - } - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb); - emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb); - #endif -} - -_SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers() { - emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, 0); - emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, 0); - emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, 0); - emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, 0); - emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, 0); - emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, 0); - emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); - emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); - emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); - emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, 0); - emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, 0); - emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, 0); - emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, 0); - emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0); - emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0); - emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); - emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); - sapp_js_remove_beforeunload_listener(); - if (_sapp.clipboard.enabled) { - sapp_js_remove_clipboard_listener(); - } - if (_sapp.drop.enabled) { - sapp_js_remove_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); - } - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, 0); - emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, 0); - #endif -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) { - _SOKOL_UNUSED(time); - _SOKOL_UNUSED(userData); - - #if defined(SOKOL_WGPU) - /* - on WebGPU, the emscripten frame callback will already be called while - the asynchronous WebGPU device and swapchain initialization is still - in progress - */ - switch (_sapp.emsc.wgpu.state) { - case _SAPP_EMSC_WGPU_STATE_INITIAL: - /* async JS init hasn't finished yet */ - break; - case _SAPP_EMSC_WGPU_STATE_READY: - /* perform post-async init stuff */ - _sapp_emsc_wgpu_surfaces_create(); - _sapp.emsc.wgpu.state = _SAPP_EMSC_WGPU_STATE_RUNNING; - break; - case _SAPP_EMSC_WGPU_STATE_RUNNING: - /* a regular frame */ - _sapp_emsc_wgpu_next_frame(); - _sapp_frame(); - break; - } - #else - /* WebGL code path */ - _sapp_frame(); - #endif - - /* quit-handling */ - if (_sapp.quit_requested) { - _sapp_init_event(SAPP_EVENTTYPE_QUIT_REQUESTED); - _sapp_call_event(&_sapp.event); - if (_sapp.quit_requested) { - _sapp.quit_ordered = true; - } - } - if (_sapp.quit_ordered) { - _sapp_emsc_unregister_eventhandlers(); - _sapp_call_cleanup(); - _sapp_discard_state(); - return EM_FALSE; - } - return EM_TRUE; -} - -_SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) { - _sapp_init_state(desc); - sapp_js_pointer_init(&_sapp.html5_canvas_selector[1]); - double w, h; - if (_sapp.desc.html5_canvas_resize) { - w = (double) _sapp.desc.width; - h = (double) _sapp.desc.height; - } - else { - emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h); - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, false, _sapp_emsc_size_changed); - } - if (_sapp.desc.high_dpi) { - _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); - } - _sapp.window_width = (int) w; - _sapp.window_height = (int) h; - _sapp.framebuffer_width = (int) (w * _sapp.dpi_scale); - _sapp.framebuffer_height = (int) (h * _sapp.dpi_scale); - emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - _sapp_emsc_webgl_init(); - #elif defined(SOKOL_WGPU) - sapp_js_wgpu_init(); - #endif - _sapp.valid = true; - _sapp_emsc_register_eventhandlers(); - sapp_set_icon(&desc->icon); - - /* start the frame loop */ - emscripten_request_animation_frame_loop(_sapp_emsc_frame, 0); - - /* NOT A BUG: do not call _sapp_discard_state() here, instead this is - called in _sapp_emsc_frame() when the application is ordered to quit - */ -} - -#if !defined(SOKOL_NO_ENTRY) -int main(int argc, char* argv[]) { - sapp_desc desc = sokol_main(argc, argv); - _sapp_emsc_run(&desc); - return 0; -} -#endif /* SOKOL_NO_ENTRY */ -#endif /* _SAPP_EMSCRIPTEN */ - -/*== MISC GL SUPPORT FUNCTIONS ================================================*/ -#if defined(SOKOL_GLCORE33) -typedef struct { - int red_bits; - int green_bits; - int blue_bits; - int alpha_bits; - int depth_bits; - int stencil_bits; - int samples; - bool doublebuffer; - uintptr_t handle; -} _sapp_gl_fbconfig; - -_SOKOL_PRIVATE void _sapp_gl_init_fbconfig(_sapp_gl_fbconfig* fbconfig) { - memset(fbconfig, 0, sizeof(_sapp_gl_fbconfig)); - /* -1 means "don't care" */ - fbconfig->red_bits = -1; - fbconfig->green_bits = -1; - fbconfig->blue_bits = -1; - fbconfig->alpha_bits = -1; - fbconfig->depth_bits = -1; - fbconfig->stencil_bits = -1; - fbconfig->samples = -1; -} - -_SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* alternatives, int count) { - int missing, least_missing = 1000000; - int color_diff, least_color_diff = 10000000; - int extra_diff, least_extra_diff = 10000000; - const _sapp_gl_fbconfig* current; - const _sapp_gl_fbconfig* closest = 0; - for (int i = 0; i < count; i++) { - current = alternatives + i; - if (desired->doublebuffer != current->doublebuffer) { - continue; - } - missing = 0; - if (desired->alpha_bits > 0 && current->alpha_bits == 0) { - missing++; - } - if (desired->depth_bits > 0 && current->depth_bits == 0) { - missing++; - } - if (desired->stencil_bits > 0 && current->stencil_bits == 0) { - missing++; - } - if (desired->samples > 0 && current->samples == 0) { - /* Technically, several multisampling buffers could be - involved, but that's a lower level implementation detail and - not important to us here, so we count them as one - */ - missing++; - } - - /* These polynomials make many small channel size differences matter - less than one large channel size difference - Calculate color channel size difference value - */ - color_diff = 0; - if (desired->red_bits != -1) { - color_diff += (desired->red_bits - current->red_bits) * (desired->red_bits - current->red_bits); - } - if (desired->green_bits != -1) { - color_diff += (desired->green_bits - current->green_bits) * (desired->green_bits - current->green_bits); - } - if (desired->blue_bits != -1) { - color_diff += (desired->blue_bits - current->blue_bits) * (desired->blue_bits - current->blue_bits); - } - - /* Calculate non-color channel size difference value */ - extra_diff = 0; - if (desired->alpha_bits != -1) { - extra_diff += (desired->alpha_bits - current->alpha_bits) * (desired->alpha_bits - current->alpha_bits); - } - if (desired->depth_bits != -1) { - extra_diff += (desired->depth_bits - current->depth_bits) * (desired->depth_bits - current->depth_bits); - } - if (desired->stencil_bits != -1) { - extra_diff += (desired->stencil_bits - current->stencil_bits) * (desired->stencil_bits - current->stencil_bits); - } - if (desired->samples != -1) { - extra_diff += (desired->samples - current->samples) * (desired->samples - current->samples); - } - - /* Figure out if the current one is better than the best one found so far - Least number of missing buffers is the most important heuristic, - then color buffer size match and lastly size match for other buffers - */ - if (missing < least_missing) { - closest = current; - } - else if (missing == least_missing) { - if ((color_diff < least_color_diff) || - (color_diff == least_color_diff && extra_diff < least_extra_diff)) - { - closest = current; - } - } - if (current == closest) { - least_missing = missing; - least_color_diff = color_diff; - least_extra_diff = extra_diff; - } - } - return closest; -} -#endif - -/*== WINDOWS DESKTOP and UWP====================================================*/ -#if defined(_SAPP_WIN32) || defined(_SAPP_UWP) -_SOKOL_PRIVATE bool _sapp_win32_uwp_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) { - SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); - memset(dst, 0, (size_t)dst_num_bytes); - const int dst_chars = dst_num_bytes / (int)sizeof(wchar_t); - const int dst_needed = MultiByteToWideChar(CP_UTF8, 0, src, -1, 0, 0); - if ((dst_needed > 0) && (dst_needed < dst_chars)) { - MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, dst_chars); - return true; - } - else { - /* input string doesn't fit into destination buffer */ - return false; - } -} - -_SOKOL_PRIVATE void _sapp_win32_uwp_app_event(sapp_event_type type) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_win32_uwp_init_keytable(void) { - /* same as GLFW */ - _sapp.keycodes[0x00B] = SAPP_KEYCODE_0; - _sapp.keycodes[0x002] = SAPP_KEYCODE_1; - _sapp.keycodes[0x003] = SAPP_KEYCODE_2; - _sapp.keycodes[0x004] = SAPP_KEYCODE_3; - _sapp.keycodes[0x005] = SAPP_KEYCODE_4; - _sapp.keycodes[0x006] = SAPP_KEYCODE_5; - _sapp.keycodes[0x007] = SAPP_KEYCODE_6; - _sapp.keycodes[0x008] = SAPP_KEYCODE_7; - _sapp.keycodes[0x009] = SAPP_KEYCODE_8; - _sapp.keycodes[0x00A] = SAPP_KEYCODE_9; - _sapp.keycodes[0x01E] = SAPP_KEYCODE_A; - _sapp.keycodes[0x030] = SAPP_KEYCODE_B; - _sapp.keycodes[0x02E] = SAPP_KEYCODE_C; - _sapp.keycodes[0x020] = SAPP_KEYCODE_D; - _sapp.keycodes[0x012] = SAPP_KEYCODE_E; - _sapp.keycodes[0x021] = SAPP_KEYCODE_F; - _sapp.keycodes[0x022] = SAPP_KEYCODE_G; - _sapp.keycodes[0x023] = SAPP_KEYCODE_H; - _sapp.keycodes[0x017] = SAPP_KEYCODE_I; - _sapp.keycodes[0x024] = SAPP_KEYCODE_J; - _sapp.keycodes[0x025] = SAPP_KEYCODE_K; - _sapp.keycodes[0x026] = SAPP_KEYCODE_L; - _sapp.keycodes[0x032] = SAPP_KEYCODE_M; - _sapp.keycodes[0x031] = SAPP_KEYCODE_N; - _sapp.keycodes[0x018] = SAPP_KEYCODE_O; - _sapp.keycodes[0x019] = SAPP_KEYCODE_P; - _sapp.keycodes[0x010] = SAPP_KEYCODE_Q; - _sapp.keycodes[0x013] = SAPP_KEYCODE_R; - _sapp.keycodes[0x01F] = SAPP_KEYCODE_S; - _sapp.keycodes[0x014] = SAPP_KEYCODE_T; - _sapp.keycodes[0x016] = SAPP_KEYCODE_U; - _sapp.keycodes[0x02F] = SAPP_KEYCODE_V; - _sapp.keycodes[0x011] = SAPP_KEYCODE_W; - _sapp.keycodes[0x02D] = SAPP_KEYCODE_X; - _sapp.keycodes[0x015] = SAPP_KEYCODE_Y; - _sapp.keycodes[0x02C] = SAPP_KEYCODE_Z; - _sapp.keycodes[0x028] = SAPP_KEYCODE_APOSTROPHE; - _sapp.keycodes[0x02B] = SAPP_KEYCODE_BACKSLASH; - _sapp.keycodes[0x033] = SAPP_KEYCODE_COMMA; - _sapp.keycodes[0x00D] = SAPP_KEYCODE_EQUAL; - _sapp.keycodes[0x029] = SAPP_KEYCODE_GRAVE_ACCENT; - _sapp.keycodes[0x01A] = SAPP_KEYCODE_LEFT_BRACKET; - _sapp.keycodes[0x00C] = SAPP_KEYCODE_MINUS; - _sapp.keycodes[0x034] = SAPP_KEYCODE_PERIOD; - _sapp.keycodes[0x01B] = SAPP_KEYCODE_RIGHT_BRACKET; - _sapp.keycodes[0x027] = SAPP_KEYCODE_SEMICOLON; - _sapp.keycodes[0x035] = SAPP_KEYCODE_SLASH; - _sapp.keycodes[0x056] = SAPP_KEYCODE_WORLD_2; - _sapp.keycodes[0x00E] = SAPP_KEYCODE_BACKSPACE; - _sapp.keycodes[0x153] = SAPP_KEYCODE_DELETE; - _sapp.keycodes[0x14F] = SAPP_KEYCODE_END; - _sapp.keycodes[0x01C] = SAPP_KEYCODE_ENTER; - _sapp.keycodes[0x001] = SAPP_KEYCODE_ESCAPE; - _sapp.keycodes[0x147] = SAPP_KEYCODE_HOME; - _sapp.keycodes[0x152] = SAPP_KEYCODE_INSERT; - _sapp.keycodes[0x15D] = SAPP_KEYCODE_MENU; - _sapp.keycodes[0x151] = SAPP_KEYCODE_PAGE_DOWN; - _sapp.keycodes[0x149] = SAPP_KEYCODE_PAGE_UP; - _sapp.keycodes[0x045] = SAPP_KEYCODE_PAUSE; - _sapp.keycodes[0x146] = SAPP_KEYCODE_PAUSE; - _sapp.keycodes[0x039] = SAPP_KEYCODE_SPACE; - _sapp.keycodes[0x00F] = SAPP_KEYCODE_TAB; - _sapp.keycodes[0x03A] = SAPP_KEYCODE_CAPS_LOCK; - _sapp.keycodes[0x145] = SAPP_KEYCODE_NUM_LOCK; - _sapp.keycodes[0x046] = SAPP_KEYCODE_SCROLL_LOCK; - _sapp.keycodes[0x03B] = SAPP_KEYCODE_F1; - _sapp.keycodes[0x03C] = SAPP_KEYCODE_F2; - _sapp.keycodes[0x03D] = SAPP_KEYCODE_F3; - _sapp.keycodes[0x03E] = SAPP_KEYCODE_F4; - _sapp.keycodes[0x03F] = SAPP_KEYCODE_F5; - _sapp.keycodes[0x040] = SAPP_KEYCODE_F6; - _sapp.keycodes[0x041] = SAPP_KEYCODE_F7; - _sapp.keycodes[0x042] = SAPP_KEYCODE_F8; - _sapp.keycodes[0x043] = SAPP_KEYCODE_F9; - _sapp.keycodes[0x044] = SAPP_KEYCODE_F10; - _sapp.keycodes[0x057] = SAPP_KEYCODE_F11; - _sapp.keycodes[0x058] = SAPP_KEYCODE_F12; - _sapp.keycodes[0x064] = SAPP_KEYCODE_F13; - _sapp.keycodes[0x065] = SAPP_KEYCODE_F14; - _sapp.keycodes[0x066] = SAPP_KEYCODE_F15; - _sapp.keycodes[0x067] = SAPP_KEYCODE_F16; - _sapp.keycodes[0x068] = SAPP_KEYCODE_F17; - _sapp.keycodes[0x069] = SAPP_KEYCODE_F18; - _sapp.keycodes[0x06A] = SAPP_KEYCODE_F19; - _sapp.keycodes[0x06B] = SAPP_KEYCODE_F20; - _sapp.keycodes[0x06C] = SAPP_KEYCODE_F21; - _sapp.keycodes[0x06D] = SAPP_KEYCODE_F22; - _sapp.keycodes[0x06E] = SAPP_KEYCODE_F23; - _sapp.keycodes[0x076] = SAPP_KEYCODE_F24; - _sapp.keycodes[0x038] = SAPP_KEYCODE_LEFT_ALT; - _sapp.keycodes[0x01D] = SAPP_KEYCODE_LEFT_CONTROL; - _sapp.keycodes[0x02A] = SAPP_KEYCODE_LEFT_SHIFT; - _sapp.keycodes[0x15B] = SAPP_KEYCODE_LEFT_SUPER; - _sapp.keycodes[0x137] = SAPP_KEYCODE_PRINT_SCREEN; - _sapp.keycodes[0x138] = SAPP_KEYCODE_RIGHT_ALT; - _sapp.keycodes[0x11D] = SAPP_KEYCODE_RIGHT_CONTROL; - _sapp.keycodes[0x036] = SAPP_KEYCODE_RIGHT_SHIFT; - _sapp.keycodes[0x15C] = SAPP_KEYCODE_RIGHT_SUPER; - _sapp.keycodes[0x150] = SAPP_KEYCODE_DOWN; - _sapp.keycodes[0x14B] = SAPP_KEYCODE_LEFT; - _sapp.keycodes[0x14D] = SAPP_KEYCODE_RIGHT; - _sapp.keycodes[0x148] = SAPP_KEYCODE_UP; - _sapp.keycodes[0x052] = SAPP_KEYCODE_KP_0; - _sapp.keycodes[0x04F] = SAPP_KEYCODE_KP_1; - _sapp.keycodes[0x050] = SAPP_KEYCODE_KP_2; - _sapp.keycodes[0x051] = SAPP_KEYCODE_KP_3; - _sapp.keycodes[0x04B] = SAPP_KEYCODE_KP_4; - _sapp.keycodes[0x04C] = SAPP_KEYCODE_KP_5; - _sapp.keycodes[0x04D] = SAPP_KEYCODE_KP_6; - _sapp.keycodes[0x047] = SAPP_KEYCODE_KP_7; - _sapp.keycodes[0x048] = SAPP_KEYCODE_KP_8; - _sapp.keycodes[0x049] = SAPP_KEYCODE_KP_9; - _sapp.keycodes[0x04E] = SAPP_KEYCODE_KP_ADD; - _sapp.keycodes[0x053] = SAPP_KEYCODE_KP_DECIMAL; - _sapp.keycodes[0x135] = SAPP_KEYCODE_KP_DIVIDE; - _sapp.keycodes[0x11C] = SAPP_KEYCODE_KP_ENTER; - _sapp.keycodes[0x037] = SAPP_KEYCODE_KP_MULTIPLY; - _sapp.keycodes[0x04A] = SAPP_KEYCODE_KP_SUBTRACT; -} -#endif // _SAPP_WIN32 || _SAPP_UWP - -/*== WINDOWS DESKTOP===========================================================*/ -#if defined(_SAPP_WIN32) - -#if defined(SOKOL_D3D11) - -#if defined(__cplusplus) -#define _sapp_d3d11_Release(self) (self)->Release() -#else -#define _sapp_d3d11_Release(self) (self)->lpVtbl->Release(self) -#endif - -#define _SAPP_SAFE_RELEASE(obj) if (obj) { _sapp_d3d11_Release(obj); obj=0; } - -static const IID _sapp_IID_ID3D11Texture2D = { 0x6f15aaf2,0xd208,0x4e89,0x9a,0xb4,0x48,0x95,0x35,0xd3,0x4f,0x9c }; - -static inline HRESULT _sapp_dxgi_GetBuffer(IDXGISwapChain* self, UINT Buffer, REFIID riid, void** ppSurface) { - #if defined(__cplusplus) - return self->GetBuffer(Buffer, riid, ppSurface); - #else - return self->lpVtbl->GetBuffer(self, Buffer, riid, ppSurface); - #endif -} - -static inline HRESULT _sapp_d3d11_CreateRenderTargetView(ID3D11Device* self, ID3D11Resource *pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) { - #if defined(__cplusplus) - return self->CreateRenderTargetView(pResource, pDesc, ppRTView); - #else - return self->lpVtbl->CreateRenderTargetView(self, pResource, pDesc, ppRTView); - #endif -} - -static inline HRESULT _sapp_d3d11_CreateTexture2D(ID3D11Device* self, const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) { - #if defined(__cplusplus) - return self->CreateTexture2D(pDesc, pInitialData, ppTexture2D); - #else - return self->lpVtbl->CreateTexture2D(self, pDesc, pInitialData, ppTexture2D); - #endif -} - -static inline HRESULT _sapp_d3d11_CreateDepthStencilView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) { - #if defined(__cplusplus) - return self->CreateDepthStencilView(pResource, pDesc, ppDepthStencilView); - #else - return self->lpVtbl->CreateDepthStencilView(self, pResource, pDesc, ppDepthStencilView); - #endif -} - -static inline void _sapp_d3d11_ResolveSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, ID3D11Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format) { - #if defined(__cplusplus) - self->ResolveSubresource(pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); - #else - self->lpVtbl->ResolveSubresource(self, pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); - #endif -} - -static inline HRESULT _sapp_dxgi_ResizeBuffers(IDXGISwapChain* self, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags) { - #if defined(__cplusplus) - return self->ResizeBuffers(BufferCount, Width, Height, NewFormat, SwapChainFlags); - #else - return self->lpVtbl->ResizeBuffers(self, BufferCount, Width, Height, NewFormat, SwapChainFlags); - #endif -} - -static inline HRESULT _sapp_dxgi_Present(IDXGISwapChain* self, UINT SyncInterval, UINT Flags) { - #if defined(__cplusplus) - return self->Present(SyncInterval, Flags); - #else - return self->lpVtbl->Present(self, SyncInterval, Flags); - #endif -} - -_SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { - DXGI_SWAP_CHAIN_DESC* sc_desc = &_sapp.d3d11.swap_chain_desc; - sc_desc->BufferDesc.Width = (UINT)_sapp.framebuffer_width; - sc_desc->BufferDesc.Height = (UINT)_sapp.framebuffer_height; - sc_desc->BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; - sc_desc->BufferDesc.RefreshRate.Numerator = 60; - sc_desc->BufferDesc.RefreshRate.Denominator = 1; - sc_desc->OutputWindow = _sapp.win32.hwnd; - sc_desc->Windowed = true; - if (_sapp.win32.is_win10_or_greater) { - sc_desc->BufferCount = 2; - sc_desc->SwapEffect = (DXGI_SWAP_EFFECT) _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD; - } - else { - sc_desc->BufferCount = 1; - sc_desc->SwapEffect = DXGI_SWAP_EFFECT_DISCARD; - } - sc_desc->SampleDesc.Count = 1; - sc_desc->SampleDesc.Quality = 0; - sc_desc->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - UINT create_flags = D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT; - #if defined(SOKOL_DEBUG) - create_flags |= D3D11_CREATE_DEVICE_DEBUG; - #endif - D3D_FEATURE_LEVEL feature_level; - HRESULT hr = D3D11CreateDeviceAndSwapChain( - NULL, /* pAdapter (use default) */ - D3D_DRIVER_TYPE_HARDWARE, /* DriverType */ - NULL, /* Software */ - create_flags, /* Flags */ - NULL, /* pFeatureLevels */ - 0, /* FeatureLevels */ - D3D11_SDK_VERSION, /* SDKVersion */ - sc_desc, /* pSwapChainDesc */ - &_sapp.d3d11.swap_chain, /* ppSwapChain */ - &_sapp.d3d11.device, /* ppDevice */ - &feature_level, /* pFeatureLevel */ - &_sapp.d3d11.device_context); /* ppImmediateContext */ - _SOKOL_UNUSED(hr); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.swap_chain && _sapp.d3d11.device && _sapp.d3d11.device_context); -} - -_SOKOL_PRIVATE void _sapp_d3d11_destroy_device_and_swapchain(void) { - _SAPP_SAFE_RELEASE(_sapp.d3d11.swap_chain); - _SAPP_SAFE_RELEASE(_sapp.d3d11.device_context); - _SAPP_SAFE_RELEASE(_sapp.d3d11.device); -} - -_SOKOL_PRIVATE void _sapp_d3d11_create_default_render_target(void) { - SOKOL_ASSERT(0 == _sapp.d3d11.rt); - SOKOL_ASSERT(0 == _sapp.d3d11.rtv); - SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rt); - SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rtv); - SOKOL_ASSERT(0 == _sapp.d3d11.ds); - SOKOL_ASSERT(0 == _sapp.d3d11.dsv); - - HRESULT hr; - - /* view for the swapchain-created framebuffer */ - #ifdef __cplusplus - hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, _sapp_IID_ID3D11Texture2D, (void**)&_sapp.d3d11.rt); - #else - hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, &_sapp_IID_ID3D11Texture2D, (void**)&_sapp.d3d11.rt); - #endif - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rt); - hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.rt, NULL, &_sapp.d3d11.rtv); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rtv); - - /* common desc for MSAA and depth-stencil texture */ - D3D11_TEXTURE2D_DESC tex_desc; - memset(&tex_desc, 0, sizeof(tex_desc)); - tex_desc.Width = (UINT)_sapp.framebuffer_width; - tex_desc.Height = (UINT)_sapp.framebuffer_height; - tex_desc.MipLevels = 1; - tex_desc.ArraySize = 1; - tex_desc.Usage = D3D11_USAGE_DEFAULT; - tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; - tex_desc.SampleDesc.Count = (UINT) _sapp.sample_count; - tex_desc.SampleDesc.Quality = (UINT) (_sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); - - /* create MSAA texture and view if antialiasing requested */ - if (_sapp.sample_count > 1) { - tex_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; - hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.msaa_rt); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rt); - hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.msaa_rt, NULL, &_sapp.d3d11.msaa_rtv); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rtv); - } - - /* texture and view for the depth-stencil-surface */ - tex_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; - tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; - hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.ds); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.ds); - hr = _sapp_d3d11_CreateDepthStencilView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.ds, NULL, &_sapp.d3d11.dsv); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.dsv); -} - -_SOKOL_PRIVATE void _sapp_d3d11_destroy_default_render_target(void) { - _SAPP_SAFE_RELEASE(_sapp.d3d11.rt); - _SAPP_SAFE_RELEASE(_sapp.d3d11.rtv); - _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rt); - _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rtv); - _SAPP_SAFE_RELEASE(_sapp.d3d11.ds); - _SAPP_SAFE_RELEASE(_sapp.d3d11.dsv); -} - -_SOKOL_PRIVATE void _sapp_d3d11_resize_default_render_target(void) { - if (_sapp.d3d11.swap_chain) { - _sapp_d3d11_destroy_default_render_target(); - _sapp_dxgi_ResizeBuffers(_sapp.d3d11.swap_chain, _sapp.d3d11.swap_chain_desc.BufferCount, (UINT)_sapp.framebuffer_width, (UINT)_sapp.framebuffer_height, DXGI_FORMAT_B8G8R8A8_UNORM, 0); - _sapp_d3d11_create_default_render_target(); - } -} - -_SOKOL_PRIVATE void _sapp_d3d11_present(void) { - /* do MSAA resolve if needed */ - if (_sapp.sample_count > 1) { - SOKOL_ASSERT(_sapp.d3d11.rt); - SOKOL_ASSERT(_sapp.d3d11.msaa_rt); - _sapp_d3d11_ResolveSubresource(_sapp.d3d11.device_context, (ID3D11Resource*)_sapp.d3d11.rt, 0, (ID3D11Resource*)_sapp.d3d11.msaa_rt, 0, DXGI_FORMAT_B8G8R8A8_UNORM); - } - _sapp_dxgi_Present(_sapp.d3d11.swap_chain, (UINT)_sapp.swap_interval, 0); -} - -#endif /* SOKOL_D3D11 */ - -#if defined(SOKOL_GLCORE33) -_SOKOL_PRIVATE void _sapp_wgl_init(void) { - _sapp.wgl.opengl32 = LoadLibraryA("opengl32.dll"); - if (!_sapp.wgl.opengl32) { - _sapp_fail("Failed to load opengl32.dll\n"); - } - SOKOL_ASSERT(_sapp.wgl.opengl32); - _sapp.wgl.CreateContext = (PFN_wglCreateContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglCreateContext"); - SOKOL_ASSERT(_sapp.wgl.CreateContext); - _sapp.wgl.DeleteContext = (PFN_wglDeleteContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglDeleteContext"); - SOKOL_ASSERT(_sapp.wgl.DeleteContext); - _sapp.wgl.GetProcAddress = (PFN_wglGetProcAddress)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetProcAddress"); - SOKOL_ASSERT(_sapp.wgl.GetProcAddress); - _sapp.wgl.GetCurrentDC = (PFN_wglGetCurrentDC)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetCurrentDC"); - SOKOL_ASSERT(_sapp.wgl.GetCurrentDC); - _sapp.wgl.MakeCurrent = (PFN_wglMakeCurrent)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglMakeCurrent"); - SOKOL_ASSERT(_sapp.wgl.MakeCurrent); - - _sapp.wgl.msg_hwnd = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, - L"SOKOLAPP", - L"sokol-app message window", - WS_CLIPSIBLINGS|WS_CLIPCHILDREN, - 0, 0, 1, 1, - NULL, NULL, - GetModuleHandleW(NULL), - NULL); - if (!_sapp.wgl.msg_hwnd) { - _sapp_fail("Win32: failed to create helper window!\n"); - } - SOKOL_ASSERT(_sapp.wgl.msg_hwnd); - ShowWindow(_sapp.wgl.msg_hwnd, SW_HIDE); - MSG msg; - while (PeekMessageW(&msg, _sapp.wgl.msg_hwnd, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessageW(&msg); - } - _sapp.wgl.msg_dc = GetDC(_sapp.wgl.msg_hwnd); - if (!_sapp.wgl.msg_dc) { - _sapp_fail("Win32: failed to obtain helper window DC!\n"); - } -} - -_SOKOL_PRIVATE void _sapp_wgl_shutdown(void) { - SOKOL_ASSERT(_sapp.wgl.opengl32 && _sapp.wgl.msg_hwnd); - DestroyWindow(_sapp.wgl.msg_hwnd); _sapp.wgl.msg_hwnd = 0; - FreeLibrary(_sapp.wgl.opengl32); _sapp.wgl.opengl32 = 0; -} - -_SOKOL_PRIVATE bool _sapp_wgl_has_ext(const char* ext, const char* extensions) { - SOKOL_ASSERT(ext && extensions); - const char* start = extensions; - while (true) { - const char* where = strstr(start, ext); - if (!where) { - return false; - } - const char* terminator = where + strlen(ext); - if ((where == start) || (*(where - 1) == ' ')) { - if (*terminator == ' ' || *terminator == '\0') { - break; - } - } - start = terminator; - } - return true; -} - -_SOKOL_PRIVATE bool _sapp_wgl_ext_supported(const char* ext) { - SOKOL_ASSERT(ext); - if (_sapp.wgl.GetExtensionsStringEXT) { - const char* extensions = _sapp.wgl.GetExtensionsStringEXT(); - if (extensions) { - if (_sapp_wgl_has_ext(ext, extensions)) { - return true; - } - } - } - if (_sapp.wgl.GetExtensionsStringARB) { - const char* extensions = _sapp.wgl.GetExtensionsStringARB(_sapp.wgl.GetCurrentDC()); - if (extensions) { - if (_sapp_wgl_has_ext(ext, extensions)) { - return true; - } - } - } - return false; -} - -_SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) { - SOKOL_ASSERT(_sapp.wgl.msg_dc); - PIXELFORMATDESCRIPTOR pfd; - memset(&pfd, 0, sizeof(pfd)); - pfd.nSize = sizeof(pfd); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = 24; - if (!SetPixelFormat(_sapp.wgl.msg_dc, ChoosePixelFormat(_sapp.wgl.msg_dc, &pfd), &pfd)) { - _sapp_fail("WGL: failed to set pixel format for dummy context\n"); - } - HGLRC rc = _sapp.wgl.CreateContext(_sapp.wgl.msg_dc); - if (!rc) { - _sapp_fail("WGL: Failed to create dummy context\n"); - } - if (!_sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, rc)) { - _sapp_fail("WGL: Failed to make context current\n"); - } - _sapp.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringEXT"); - _sapp.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringARB"); - _sapp.wgl.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)(void*) _sapp.wgl.GetProcAddress("wglCreateContextAttribsARB"); - _sapp.wgl.SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglSwapIntervalEXT"); - _sapp.wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetPixelFormatAttribivARB"); - _sapp.wgl.arb_multisample = _sapp_wgl_ext_supported("WGL_ARB_multisample"); - _sapp.wgl.arb_create_context = _sapp_wgl_ext_supported("WGL_ARB_create_context"); - _sapp.wgl.arb_create_context_profile = _sapp_wgl_ext_supported("WGL_ARB_create_context_profile"); - _sapp.wgl.ext_swap_control = _sapp_wgl_ext_supported("WGL_EXT_swap_control"); - _sapp.wgl.arb_pixel_format = _sapp_wgl_ext_supported("WGL_ARB_pixel_format"); - _sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, 0); - _sapp.wgl.DeleteContext(rc); -} - -_SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) { - SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); - int value = 0; - if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, 1, &attrib, &value)) { - _sapp_fail("WGL: Failed to retrieve pixel format attribute\n"); - } - return value; -} - -_SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { - SOKOL_ASSERT(_sapp.win32.dc); - SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); - const _sapp_gl_fbconfig* closest; - - int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB); - _sapp_gl_fbconfig* usable_configs = (_sapp_gl_fbconfig*) SOKOL_CALLOC((size_t)native_count, sizeof(_sapp_gl_fbconfig)); - SOKOL_ASSERT(usable_configs); - int usable_count = 0; - for (int i = 0; i < native_count; i++) { - const int n = i + 1; - _sapp_gl_fbconfig* u = usable_configs + usable_count; - _sapp_gl_init_fbconfig(u); - if (!_sapp_wgl_attrib(n, WGL_SUPPORT_OPENGL_ARB) || !_sapp_wgl_attrib(n, WGL_DRAW_TO_WINDOW_ARB)) { - continue; - } - if (_sapp_wgl_attrib(n, WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) { - continue; - } - if (_sapp_wgl_attrib(n, WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) { - continue; - } - u->red_bits = _sapp_wgl_attrib(n, WGL_RED_BITS_ARB); - u->green_bits = _sapp_wgl_attrib(n, WGL_GREEN_BITS_ARB); - u->blue_bits = _sapp_wgl_attrib(n, WGL_BLUE_BITS_ARB); - u->alpha_bits = _sapp_wgl_attrib(n, WGL_ALPHA_BITS_ARB); - u->depth_bits = _sapp_wgl_attrib(n, WGL_DEPTH_BITS_ARB); - u->stencil_bits = _sapp_wgl_attrib(n, WGL_STENCIL_BITS_ARB); - if (_sapp_wgl_attrib(n, WGL_DOUBLE_BUFFER_ARB)) { - u->doublebuffer = true; - } - if (_sapp.wgl.arb_multisample) { - u->samples = _sapp_wgl_attrib(n, WGL_SAMPLES_ARB); - } - u->handle = (uintptr_t)n; - usable_count++; - } - SOKOL_ASSERT(usable_count > 0); - _sapp_gl_fbconfig desired; - _sapp_gl_init_fbconfig(&desired); - desired.red_bits = 8; - desired.green_bits = 8; - desired.blue_bits = 8; - desired.alpha_bits = 8; - desired.depth_bits = 24; - desired.stencil_bits = 8; - desired.doublebuffer = true; - desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0; - closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count); - int pixel_format = 0; - if (closest) { - pixel_format = (int) closest->handle; - } - SOKOL_FREE(usable_configs); - return pixel_format; -} - -_SOKOL_PRIVATE void _sapp_wgl_create_context(void) { - int pixel_format = _sapp_wgl_find_pixel_format(); - if (0 == pixel_format) { - _sapp_fail("WGL: Didn't find matching pixel format.\n"); - } - PIXELFORMATDESCRIPTOR pfd; - if (!DescribePixelFormat(_sapp.win32.dc, pixel_format, sizeof(pfd), &pfd)) { - _sapp_fail("WGL: Failed to retrieve PFD for selected pixel format!\n"); - } - if (!SetPixelFormat(_sapp.win32.dc, pixel_format, &pfd)) { - _sapp_fail("WGL: Failed to set selected pixel format!\n"); - } - if (!_sapp.wgl.arb_create_context) { - _sapp_fail("WGL: ARB_create_context required!\n"); - } - if (!_sapp.wgl.arb_create_context_profile) { - _sapp_fail("WGL: ARB_create_context_profile required!\n"); - } - const int attrs[] = { - WGL_CONTEXT_MAJOR_VERSION_ARB, 3, - WGL_CONTEXT_MINOR_VERSION_ARB, 3, - WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, - WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, - 0, 0 - }; - _sapp.wgl.gl_ctx = _sapp.wgl.CreateContextAttribsARB(_sapp.win32.dc, 0, attrs); - if (!_sapp.wgl.gl_ctx) { - const DWORD err = GetLastError(); - if (err == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) { - _sapp_fail("WGL: Driver does not support OpenGL version 3.3\n"); - } - else if (err == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) { - _sapp_fail("WGL: Driver does not support the requested OpenGL profile"); - } - else if (err == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) { - _sapp_fail("WGL: The share context is not compatible with the requested context"); - } - else { - _sapp_fail("WGL: Failed to create OpenGL context"); - } - } - _sapp.wgl.MakeCurrent(_sapp.win32.dc, _sapp.wgl.gl_ctx); - if (_sapp.wgl.ext_swap_control) { - /* FIXME: DwmIsCompositionEnabled() (see GLFW) */ - _sapp.wgl.SwapIntervalEXT(_sapp.swap_interval); - } -} - -_SOKOL_PRIVATE void _sapp_wgl_destroy_context(void) { - SOKOL_ASSERT(_sapp.wgl.gl_ctx); - _sapp.wgl.DeleteContext(_sapp.wgl.gl_ctx); - _sapp.wgl.gl_ctx = 0; -} - -_SOKOL_PRIVATE void _sapp_wgl_swap_buffers(void) { - SOKOL_ASSERT(_sapp.win32.dc); - /* FIXME: DwmIsCompositionEnabled? (see GLFW) */ - SwapBuffers(_sapp.win32.dc); -} -#endif /* SOKOL_GLCORE33 */ - -_SOKOL_PRIVATE bool _sapp_win32_wide_to_utf8(const wchar_t* src, char* dst, int dst_num_bytes) { - SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); - memset(dst, 0, (size_t)dst_num_bytes); - const int bytes_needed = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL); - if (bytes_needed <= dst_num_bytes) { - WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, dst_num_bytes, NULL, NULL); - return true; - } - else { - return false; - } -} - -_SOKOL_PRIVATE void _sapp_win32_toggle_fullscreen(void) { - HMONITOR monitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONEAREST); - MONITORINFO minfo; - memset(&minfo, 0, sizeof(minfo)); - minfo.cbSize = sizeof(MONITORINFO); - GetMonitorInfo(monitor, &minfo); - const RECT mr = minfo.rcMonitor; - const int monitor_w = mr.right - mr.left; - const int monitor_h = mr.bottom - mr.top; - - const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; - DWORD win_style; - RECT rect = { 0, 0, 0, 0 }; - - _sapp.fullscreen = !_sapp.fullscreen; - if (!_sapp.fullscreen) { - win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; - rect.right = (int) ((float)_sapp.desc.width * _sapp.win32.dpi.window_scale); - rect.bottom = (int) ((float)_sapp.desc.height * _sapp.win32.dpi.window_scale); - } - else { - win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE; - rect.right = monitor_w; - rect.bottom = monitor_h; - } - AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style); - int win_width = rect.right - rect.left; - int win_height = rect.bottom - rect.top; - if (!_sapp.fullscreen) { - rect.left = (monitor_w - win_width) / 2; - rect.top = (monitor_h - win_height) / 2; - } - - SetWindowLongPtr(_sapp.win32.hwnd, GWL_STYLE, win_style); - SetWindowPos(_sapp.win32.hwnd, HWND_TOP, mr.left + rect.left, mr.top + rect.top, win_width, win_height, SWP_SHOWWINDOW | SWP_FRAMECHANGED); -} - -_SOKOL_PRIVATE void _sapp_win32_show_mouse(bool visible) { - /* NOTE: this function is only called when the mouse visibility actually changes */ - ShowCursor((BOOL)visible); -} - -_SOKOL_PRIVATE void _sapp_win32_capture_mouse(uint8_t btn_mask) { - if (0 == _sapp.win32.mouse_capture_mask) { - SetCapture(_sapp.win32.hwnd); - } - _sapp.win32.mouse_capture_mask |= btn_mask; -} - -_SOKOL_PRIVATE void _sapp_win32_release_mouse(uint8_t btn_mask) { - if (0 != _sapp.win32.mouse_capture_mask) { - _sapp.win32.mouse_capture_mask &= ~btn_mask; - if (0 == _sapp.win32.mouse_capture_mask) { - ReleaseCapture(); - } - } -} - -_SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { - if (lock == _sapp.mouse.locked) { - return; - } - _sapp.mouse.dx = 0.0f; - _sapp.mouse.dy = 0.0f; - _sapp.mouse.locked = lock; - _sapp_win32_release_mouse(0xFF); - if (_sapp.mouse.locked) { - /* store the current mouse position, so it can be restored when unlocked */ - POINT pos; - BOOL res = GetCursorPos(&pos); - SOKOL_ASSERT(res); _SOKOL_UNUSED(res); - _sapp.win32.mouse_locked_x = pos.x; - _sapp.win32.mouse_locked_y = pos.y; - - /* while the mouse is locked, make the mouse cursor invisible and - confine the mouse movement to a small rectangle inside our window - (so that we dont miss any mouse up events) - */ - RECT client_rect = { - _sapp.win32.mouse_locked_x, - _sapp.win32.mouse_locked_y, - _sapp.win32.mouse_locked_x, - _sapp.win32.mouse_locked_y - }; - ClipCursor(&client_rect); - - /* make the mouse cursor invisible, this will stack with sapp_show_mouse() */ - ShowCursor(FALSE); - - /* enable raw input for mouse, starts sending WM_INPUT messages to WinProc (see GLFW) */ - const RAWINPUTDEVICE rid = { - 0x01, // usUsagePage: HID_USAGE_PAGE_GENERIC - 0x02, // usUsage: HID_USAGE_GENERIC_MOUSE - 0, // dwFlags - _sapp.win32.hwnd // hwndTarget - }; - if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { - SOKOL_LOG("RegisterRawInputDevices() failed (on mouse lock).\n"); - } - /* in case the raw mouse device only supports absolute position reporting, - we need to skip the dx/dy compution for the first WM_INPUT event - */ - _sapp.win32.raw_input_mousepos_valid = false; - } - else { - /* disable raw input for mouse */ - const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; - if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { - SOKOL_LOG("RegisterRawInputDevices() failed (on mouse unlock).\n"); - } - - /* let the mouse roam freely again */ - ClipCursor(NULL); - ShowCursor(TRUE); - - /* restore the 'pre-locked' mouse position */ - BOOL res = SetCursorPos(_sapp.win32.mouse_locked_x, _sapp.win32.mouse_locked_y); - SOKOL_ASSERT(res); _SOKOL_UNUSED(res); - } -} - -/* updates current window and framebuffer size from the window's client rect, returns true if size has changed */ -_SOKOL_PRIVATE bool _sapp_win32_update_dimensions(void) { - RECT rect; - if (GetClientRect(_sapp.win32.hwnd, &rect)) { - _sapp.window_width = (int)((float)(rect.right - rect.left) / _sapp.win32.dpi.window_scale); - _sapp.window_height = (int)((float)(rect.bottom - rect.top) / _sapp.win32.dpi.window_scale); - int fb_width = (int)((float)_sapp.window_width * _sapp.win32.dpi.content_scale); - int fb_height = (int)((float)_sapp.window_height * _sapp.win32.dpi.content_scale); - /* prevent a framebuffer size of 0 when window is minimized */ - if (0 == fb_width) { - fb_width = 1; - } - if (0 == fb_height) { - fb_height = 1; - } - if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) { - _sapp.framebuffer_width = fb_width; - _sapp.framebuffer_height = fb_height; - return true; - } - } - else { - _sapp.window_width = _sapp.window_height = 1; - _sapp.framebuffer_width = _sapp.framebuffer_height = 1; - } - return false; -} - -_SOKOL_PRIVATE uint32_t _sapp_win32_mods(void) { - uint32_t mods = 0; - if (GetKeyState(VK_SHIFT) & (1<<15)) { - mods |= SAPP_MODIFIER_SHIFT; - } - if (GetKeyState(VK_CONTROL) & (1<<15)) { - mods |= SAPP_MODIFIER_CTRL; - } - if (GetKeyState(VK_MENU) & (1<<15)) { - mods |= SAPP_MODIFIER_ALT; - } - if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1<<15)) { - mods |= SAPP_MODIFIER_SUPER; - } - const bool swapped = (TRUE == GetSystemMetrics(SM_SWAPBUTTON)); - if (GetAsyncKeyState(VK_LBUTTON)) { - mods |= swapped ? SAPP_MODIFIER_RMB : SAPP_MODIFIER_LMB; - } - if (GetAsyncKeyState(VK_RBUTTON)) { - mods |= swapped ? SAPP_MODIFIER_LMB : SAPP_MODIFIER_RMB; - } - if (GetAsyncKeyState(VK_MBUTTON)) { - mods |= SAPP_MODIFIER_MMB; - } - return mods; -} - -_SOKOL_PRIVATE void _sapp_win32_mouse_event(sapp_event_type type, sapp_mousebutton btn) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - _sapp.event.modifiers = _sapp_win32_mods(); - _sapp.event.mouse_button = btn; - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_win32_scroll_event(float x, float y) { - if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); - _sapp.event.modifiers = _sapp_win32_mods(); - _sapp.event.scroll_x = -x / 30.0f; - _sapp.event.scroll_y = y / 30.0f; - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_win32_key_event(sapp_event_type type, int vk, bool repeat) { - if (_sapp_events_enabled() && (vk < SAPP_MAX_KEYCODES)) { - _sapp_init_event(type); - _sapp.event.modifiers = _sapp_win32_mods(); - _sapp.event.key_code = _sapp.keycodes[vk]; - _sapp.event.key_repeat = repeat; - _sapp_call_event(&_sapp.event); - /* check if a CLIPBOARD_PASTED event must be sent too */ - if (_sapp.clipboard.enabled && - (type == SAPP_EVENTTYPE_KEY_DOWN) && - (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) && - (_sapp.event.key_code == SAPP_KEYCODE_V)) - { - _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); - _sapp_call_event(&_sapp.event); - } - } -} - -_SOKOL_PRIVATE void _sapp_win32_char_event(uint32_t c, bool repeat) { - if (_sapp_events_enabled() && (c >= 32)) { - _sapp_init_event(SAPP_EVENTTYPE_CHAR); - _sapp.event.modifiers = _sapp_win32_mods(); - _sapp.event.char_code = c; - _sapp.event.key_repeat = repeat; - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) { - if (!_sapp.drop.enabled) { - return; - } - _sapp_clear_drop_buffer(); - bool drop_failed = false; - const int count = (int) DragQueryFileW(hdrop, 0xffffffff, NULL, 0); - _sapp.drop.num_files = (count > _sapp.drop.max_files) ? _sapp.drop.max_files : count; - for (UINT i = 0; i < (UINT)_sapp.drop.num_files; i++) { - const UINT num_chars = DragQueryFileW(hdrop, i, NULL, 0) + 1; - WCHAR* buffer = (WCHAR*) SOKOL_CALLOC(num_chars, sizeof(WCHAR)); - DragQueryFileW(hdrop, i, buffer, num_chars); - if (!_sapp_win32_wide_to_utf8(buffer, _sapp_dropped_file_path_ptr((int)i), _sapp.drop.max_path_length)) { - SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n"); - drop_failed = true; - } - SOKOL_FREE(buffer); - } - DragFinish(hdrop); - if (!drop_failed) { - if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); - _sapp_call_event(&_sapp.event); - } - } - else { - _sapp_clear_drop_buffer(); - _sapp.drop.num_files = 0; - } -} - -_SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - if (!_sapp.win32.in_create_window) { - switch (uMsg) { - case WM_CLOSE: - /* only give user a chance to intervene when sapp_quit() wasn't already called */ - if (!_sapp.quit_ordered) { - /* if window should be closed and event handling is enabled, give user code - a change to intervene via sapp_cancel_quit() - */ - _sapp.quit_requested = true; - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); - /* if user code hasn't intervened, quit the app */ - if (_sapp.quit_requested) { - _sapp.quit_ordered = true; - } - } - if (_sapp.quit_ordered) { - PostQuitMessage(0); - } - return 0; - case WM_SYSCOMMAND: - switch (wParam & 0xFFF0) { - case SC_SCREENSAVE: - case SC_MONITORPOWER: - if (_sapp.fullscreen) { - /* disable screen saver and blanking in fullscreen mode */ - return 0; - } - break; - case SC_KEYMENU: - /* user trying to access menu via ALT */ - return 0; - } - break; - case WM_ERASEBKGND: - return 1; - case WM_SIZE: - { - const bool iconified = wParam == SIZE_MINIMIZED; - if (iconified != _sapp.win32.iconified) { - _sapp.win32.iconified = iconified; - if (iconified) { - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_ICONIFIED); - } - else { - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESTORED); - } - } - } - break; - case WM_SETFOCUS: - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_FOCUSED); - break; - case WM_KILLFOCUS: - /* if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock */ - if (_sapp.mouse.locked) { - _sapp_win32_lock_mouse(false); - } - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_UNFOCUSED); - break; - case WM_SETCURSOR: - if (_sapp.desc.user_cursor) { - if (LOWORD(lParam) == HTCLIENT) { - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR); - return 1; - } - } - break; - case WM_LBUTTONDOWN: - _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT); - _sapp_win32_capture_mouse(1<data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { - /* mouse only reports absolute position - NOTE: THIS IS UNTESTED, it's unclear from reading the - Win32 RawInput docs under which circumstances absolute - positions are sent. - */ - if (_sapp.win32.raw_input_mousepos_valid) { - LONG new_x = raw_mouse_data->data.mouse.lLastX; - LONG new_y = raw_mouse_data->data.mouse.lLastY; - _sapp.mouse.dx = (float) (new_x - _sapp.win32.raw_input_mousepos_x); - _sapp.mouse.dy = (float) (new_y - _sapp.win32.raw_input_mousepos_y); - _sapp.win32.raw_input_mousepos_x = new_x; - _sapp.win32.raw_input_mousepos_y = new_y; - _sapp.win32.raw_input_mousepos_valid = true; - } - } - else { - /* mouse reports movement delta (this seems to be the common case) */ - _sapp.mouse.dx = (float) raw_mouse_data->data.mouse.lLastX; - _sapp.mouse.dy = (float) raw_mouse_data->data.mouse.lLastY; - } - _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID); - } - break; - - case WM_MOUSELEAVE: - if (!_sapp.mouse.locked) { - _sapp.win32.mouse_tracked = false; - _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID); - } - break; - case WM_MOUSEWHEEL: - _sapp_win32_scroll_event(0.0f, (float)((SHORT)HIWORD(wParam))); - break; - case WM_MOUSEHWHEEL: - _sapp_win32_scroll_event((float)((SHORT)HIWORD(wParam)), 0.0f); - break; - case WM_CHAR: - _sapp_win32_char_event((uint32_t)wParam, !!(lParam&0x40000000)); - break; - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_DOWN, (int)(HIWORD(lParam)&0x1FF), !!(lParam&0x40000000)); - break; - case WM_KEYUP: - case WM_SYSKEYUP: - _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_UP, (int)(HIWORD(lParam)&0x1FF), false); - break; - case WM_ENTERSIZEMOVE: - SetTimer(_sapp.win32.hwnd, 1, USER_TIMER_MINIMUM, NULL); - break; - case WM_EXITSIZEMOVE: - KillTimer(_sapp.win32.hwnd, 1); - break; - case WM_TIMER: - _sapp_frame(); - #if defined(SOKOL_D3D11) - _sapp_d3d11_present(); - #endif - #if defined(SOKOL_GLCORE33) - _sapp_wgl_swap_buffers(); - #endif - /* NOTE: resizing the swap-chain during resize leads to a substantial - memory spike (hundreds of megabytes for a few seconds). - - if (_sapp_win32_update_dimensions()) { - #if defined(SOKOL_D3D11) - _sapp_d3d11_resize_default_render_target(); - #endif - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); - } - */ - break; - case WM_DROPFILES: - _sapp_win32_files_dropped((HDROP)wParam); - break; - default: - break; - } - } - return DefWindowProcW(hWnd, uMsg, wParam, lParam); -} - -_SOKOL_PRIVATE void _sapp_win32_create_window(void) { - WNDCLASSW wndclassw; - memset(&wndclassw, 0, sizeof(wndclassw)); - wndclassw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; - wndclassw.lpfnWndProc = (WNDPROC) _sapp_win32_wndproc; - wndclassw.hInstance = GetModuleHandleW(NULL); - wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW); - wndclassw.hIcon = LoadIcon(NULL, IDI_WINLOGO); - wndclassw.lpszClassName = L"SOKOLAPP"; - RegisterClassW(&wndclassw); - - DWORD win_style; - const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; - RECT rect = { 0, 0, 0, 0 }; - if (_sapp.fullscreen) { - win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE; - rect.right = GetSystemMetrics(SM_CXSCREEN); - rect.bottom = GetSystemMetrics(SM_CYSCREEN); - } - else { - win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; - rect.right = (int) ((float)_sapp.window_width * _sapp.win32.dpi.window_scale); - rect.bottom = (int) ((float)_sapp.window_height * _sapp.win32.dpi.window_scale); - } - AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style); - const int win_width = rect.right - rect.left; - const int win_height = rect.bottom - rect.top; - _sapp.win32.in_create_window = true; - _sapp.win32.hwnd = CreateWindowExW( - win_ex_style, /* dwExStyle */ - L"SOKOLAPP", /* lpClassName */ - _sapp.window_title_wide, /* lpWindowName */ - win_style, /* dwStyle */ - CW_USEDEFAULT, /* X */ - CW_USEDEFAULT, /* Y */ - win_width, /* nWidth */ - win_height, /* nHeight */ - NULL, /* hWndParent */ - NULL, /* hMenu */ - GetModuleHandle(NULL), /* hInstance */ - NULL); /* lParam */ - ShowWindow(_sapp.win32.hwnd, SW_SHOW); - _sapp.win32.in_create_window = false; - _sapp.win32.dc = GetDC(_sapp.win32.hwnd); - SOKOL_ASSERT(_sapp.win32.dc); - _sapp_win32_update_dimensions(); - - DragAcceptFiles(_sapp.win32.hwnd, 1); -} - -_SOKOL_PRIVATE void _sapp_win32_destroy_window(void) { - DestroyWindow(_sapp.win32.hwnd); _sapp.win32.hwnd = 0; - UnregisterClassW(L"SOKOLAPP", GetModuleHandleW(NULL)); -} - -_SOKOL_PRIVATE void _sapp_win32_destroy_icons(void) { - if (_sapp.win32.big_icon) { - DestroyIcon(_sapp.win32.big_icon); - _sapp.win32.big_icon = 0; - } - if (_sapp.win32.small_icon) { - DestroyIcon(_sapp.win32.small_icon); - _sapp.win32.small_icon = 0; - } -} - -_SOKOL_PRIVATE void _sapp_win32_init_console(void) { - if (_sapp.desc.win32_console_create || _sapp.desc.win32_console_attach) { - BOOL con_valid = FALSE; - if (_sapp.desc.win32_console_create) { - con_valid = AllocConsole(); - } - else if (_sapp.desc.win32_console_attach) { - con_valid = AttachConsole(ATTACH_PARENT_PROCESS); - } - if (con_valid) { - FILE* res_fp = 0; - errno_t err; - err = freopen_s(&res_fp, "CON", "w", stdout); - err = freopen_s(&res_fp, "CON", "w", stderr); - (void)err; - } - } - if (_sapp.desc.win32_console_utf8) { - _sapp.win32.orig_codepage = GetConsoleOutputCP(); - SetConsoleOutputCP(CP_UTF8); - } -} - -_SOKOL_PRIVATE void _sapp_win32_restore_console(void) { - if (_sapp.desc.win32_console_utf8) { - SetConsoleOutputCP(_sapp.win32.orig_codepage); - } -} - -_SOKOL_PRIVATE void _sapp_win32_init_dpi(void) { - - typedef BOOL(WINAPI * SETPROCESSDPIAWARE_T)(void); - typedef HRESULT(WINAPI * SETPROCESSDPIAWARENESS_T)(PROCESS_DPI_AWARENESS); - typedef HRESULT(WINAPI * GETDPIFORMONITOR_T)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); - - SETPROCESSDPIAWARE_T fn_setprocessdpiaware = 0; - SETPROCESSDPIAWARENESS_T fn_setprocessdpiawareness = 0; - GETDPIFORMONITOR_T fn_getdpiformonitor = 0; - HINSTANCE user32 = LoadLibraryA("user32.dll"); - if (user32) { - fn_setprocessdpiaware = (SETPROCESSDPIAWARE_T)(void*) GetProcAddress(user32, "SetProcessDPIAware"); - } - HINSTANCE shcore = LoadLibraryA("shcore.dll"); - if (shcore) { - fn_setprocessdpiawareness = (SETPROCESSDPIAWARENESS_T)(void*) GetProcAddress(shcore, "SetProcessDpiAwareness"); - fn_getdpiformonitor = (GETDPIFORMONITOR_T)(void*) GetProcAddress(shcore, "GetDpiForMonitor"); - } - if (fn_setprocessdpiawareness) { - /* if the app didn't request HighDPI rendering, let Windows do the upscaling */ - PROCESS_DPI_AWARENESS process_dpi_awareness = PROCESS_SYSTEM_DPI_AWARE; - _sapp.win32.dpi.aware = true; - if (!_sapp.desc.high_dpi) { - process_dpi_awareness = PROCESS_DPI_UNAWARE; - _sapp.win32.dpi.aware = false; - } - fn_setprocessdpiawareness(process_dpi_awareness); - } - else if (fn_setprocessdpiaware) { - fn_setprocessdpiaware(); - _sapp.win32.dpi.aware = true; - } - /* get dpi scale factor for main monitor */ - if (fn_getdpiformonitor && _sapp.win32.dpi.aware) { - POINT pt = { 1, 1 }; - HMONITOR hm = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); - UINT dpix, dpiy; - HRESULT hr = fn_getdpiformonitor(hm, MDT_EFFECTIVE_DPI, &dpix, &dpiy); - _SOKOL_UNUSED(hr); - SOKOL_ASSERT(SUCCEEDED(hr)); - /* clamp window scale to an integer factor */ - _sapp.win32.dpi.window_scale = (float)dpix / 96.0f; - } - else { - _sapp.win32.dpi.window_scale = 1.0f; - } - if (_sapp.desc.high_dpi) { - _sapp.win32.dpi.content_scale = _sapp.win32.dpi.window_scale; - _sapp.win32.dpi.mouse_scale = 1.0f; - } - else { - _sapp.win32.dpi.content_scale = 1.0f; - _sapp.win32.dpi.mouse_scale = 1.0f / _sapp.win32.dpi.window_scale; - } - _sapp.dpi_scale = _sapp.win32.dpi.content_scale; - if (user32) { - FreeLibrary(user32); - } - if (shcore) { - FreeLibrary(shcore); - } -} - -_SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) { - SOKOL_ASSERT(str); - SOKOL_ASSERT(_sapp.win32.hwnd); - SOKOL_ASSERT(_sapp.clipboard.enabled && (_sapp.clipboard.buf_size > 0)); - - wchar_t* wchar_buf = 0; - const SIZE_T wchar_buf_size = (SIZE_T)_sapp.clipboard.buf_size * sizeof(wchar_t); - HANDLE object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size); - if (!object) { - goto error; - } - wchar_buf = (wchar_t*) GlobalLock(object); - if (!wchar_buf) { - goto error; - } - if (!_sapp_win32_uwp_utf8_to_wide(str, wchar_buf, (int)wchar_buf_size)) { - goto error; - } - GlobalUnlock(wchar_buf); - wchar_buf = 0; - if (!OpenClipboard(_sapp.win32.hwnd)) { - goto error; - } - EmptyClipboard(); - SetClipboardData(CF_UNICODETEXT, object); - CloseClipboard(); - return true; - -error: - if (wchar_buf) { - GlobalUnlock(object); - } - if (object) { - GlobalFree(object); - } - return false; -} - -_SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) { - SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer); - SOKOL_ASSERT(_sapp.win32.hwnd); - if (!OpenClipboard(_sapp.win32.hwnd)) { - /* silently ignore any errors and just return the current - content of the local clipboard buffer - */ - return _sapp.clipboard.buffer; - } - HANDLE object = GetClipboardData(CF_UNICODETEXT); - if (!object) { - CloseClipboard(); - return _sapp.clipboard.buffer; - } - const wchar_t* wchar_buf = (const wchar_t*) GlobalLock(object); - if (!wchar_buf) { - CloseClipboard(); - return _sapp.clipboard.buffer; - } - if (!_sapp_win32_wide_to_utf8(wchar_buf, _sapp.clipboard.buffer, _sapp.clipboard.buf_size)) { - SOKOL_LOG("sokol_app.h: clipboard string didn't fit into clipboard buffer\n"); - } - GlobalUnlock(object); - CloseClipboard(); - return _sapp.clipboard.buffer; -} - -_SOKOL_PRIVATE void _sapp_win32_update_window_title(void) { - _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); - SetWindowTextW(_sapp.win32.hwnd, _sapp.window_title_wide); -} - -_SOKOL_PRIVATE HICON _sapp_win32_create_icon_from_image(const sapp_image_desc* desc) { - BITMAPV5HEADER bi; - memset(&bi, 0, sizeof(bi)); - bi.bV5Size = sizeof(bi); - bi.bV5Width = desc->width; - bi.bV5Height = -desc->height; // NOTE the '-' here to indicate that origin is top-left - bi.bV5Planes = 1; - bi.bV5BitCount = 32; - bi.bV5Compression = BI_BITFIELDS; - bi.bV5RedMask = 0x00FF0000; - bi.bV5GreenMask = 0x0000FF00; - bi.bV5BlueMask = 0x000000FF; - bi.bV5AlphaMask = 0xFF000000; - - uint8_t* target = 0; - const uint8_t* source = (const uint8_t*)desc->pixels.ptr; - - HDC dc = GetDC(NULL); - HBITMAP color = CreateDIBSection(dc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&target, NULL, (DWORD)0); - ReleaseDC(NULL, dc); - if (0 == color) { - return NULL; - } - SOKOL_ASSERT(target); - - HBITMAP mask = CreateBitmap(desc->width, desc->height, 1, 1, NULL); - if (0 == mask) { - DeleteObject(color); - return NULL; - } - - for (int i = 0; i < (desc->width*desc->height); i++) { - target[0] = source[2]; - target[1] = source[1]; - target[2] = source[0]; - target[3] = source[3]; - target += 4; - source += 4; - } - - ICONINFO icon_info; - memset(&icon_info, 0, sizeof(icon_info)); - icon_info.fIcon = true; - icon_info.xHotspot = 0; - icon_info.yHotspot = 0; - icon_info.hbmMask = mask; - icon_info.hbmColor = color; - HICON icon_handle = CreateIconIndirect(&icon_info); - DeleteObject(color); - DeleteObject(mask); - - return icon_handle; -} - -_SOKOL_PRIVATE void _sapp_win32_set_icon(const sapp_icon_desc* icon_desc, int num_images) { - SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); - - int big_img_index = _sapp_image_bestmatch(icon_desc->images, num_images, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON)); - int sml_img_index = _sapp_image_bestmatch(icon_desc->images, num_images, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); - HICON big_icon = _sapp_win32_create_icon_from_image(&icon_desc->images[big_img_index]); - HICON sml_icon = _sapp_win32_create_icon_from_image(&icon_desc->images[sml_img_index]); - - // if icon creation or lookup has failed for some reason, leave the currently set icon untouched - if (0 != big_icon) { - SendMessage(_sapp.win32.hwnd, WM_SETICON, ICON_BIG, (LPARAM) big_icon); - if (0 != _sapp.win32.big_icon) { - DestroyIcon(_sapp.win32.big_icon); - } - _sapp.win32.big_icon = big_icon; - } - if (0 != sml_icon) { - SendMessage(_sapp.win32.hwnd, WM_SETICON, ICON_SMALL, (LPARAM) sml_icon); - if (0 != _sapp.win32.small_icon) { - DestroyIcon(_sapp.win32.small_icon); - } - _sapp.win32.small_icon = sml_icon; - } -} - -/* don't laugh, but this seems to be the easiest and most robust - way to check if we're running on Win10 - - From: https://github.com/videolan/vlc/blob/232fb13b0d6110c4d1b683cde24cf9a7f2c5c2ea/modules/video_output/win32/d3d11_swapchain.c#L263 -*/ -_SOKOL_PRIVATE bool _sapp_win32_is_win10_or_greater(void) { - HMODULE h = GetModuleHandleW(L"kernel32.dll"); - if (NULL != h) { - return (NULL != GetProcAddress(h, "GetSystemCpuSetInformation")); - } - else { - return false; - } -} - -_SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { - _sapp_init_state(desc); - _sapp_win32_init_console(); - _sapp.win32.is_win10_or_greater = _sapp_win32_is_win10_or_greater(); - _sapp_win32_uwp_init_keytable(); - _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); - _sapp_win32_init_dpi(); - _sapp_win32_create_window(); - sapp_set_icon(&desc->icon); - #if defined(SOKOL_D3D11) - _sapp_d3d11_create_device_and_swapchain(); - _sapp_d3d11_create_default_render_target(); - #endif - #if defined(SOKOL_GLCORE33) - _sapp_wgl_init(); - _sapp_wgl_load_extensions(); - _sapp_wgl_create_context(); - #endif - _sapp.valid = true; - - bool done = false; - while (!(done || _sapp.quit_ordered)) { - MSG msg; - while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { - if (WM_QUIT == msg.message) { - done = true; - continue; - } - else { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - _sapp_frame(); - #if defined(SOKOL_D3D11) - _sapp_d3d11_present(); - if (IsIconic(_sapp.win32.hwnd)) { - Sleep((DWORD)(16 * _sapp.swap_interval)); - } - #endif - #if defined(SOKOL_GLCORE33) - _sapp_wgl_swap_buffers(); - #endif - /* check for window resized, this cannot happen in WM_SIZE as it explodes memory usage */ - if (_sapp_win32_update_dimensions()) { - #if defined(SOKOL_D3D11) - _sapp_d3d11_resize_default_render_target(); - #endif - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); - } - if (_sapp.quit_requested) { - PostMessage(_sapp.win32.hwnd, WM_CLOSE, 0, 0); - } - } - _sapp_call_cleanup(); - - #if defined(SOKOL_D3D11) - _sapp_d3d11_destroy_default_render_target(); - _sapp_d3d11_destroy_device_and_swapchain(); - #else - _sapp_wgl_destroy_context(); - _sapp_wgl_shutdown(); - #endif - _sapp_win32_destroy_window(); - _sapp_win32_destroy_icons(); - _sapp_win32_restore_console(); - _sapp_discard_state(); -} - -_SOKOL_PRIVATE char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_line, int* o_argc) { - int argc = 0; - char** argv = 0; - char* args; - - LPWSTR* w_argv = CommandLineToArgvW(w_command_line, &argc); - if (w_argv == NULL) { - _sapp_fail("Win32: failed to parse command line"); - } else { - size_t size = wcslen(w_command_line) * 4; - argv = (char**) SOKOL_CALLOC(1, ((size_t)argc + 1) * sizeof(char*) + size); - SOKOL_ASSERT(argv); - args = (char*) &argv[argc + 1]; - int n; - for (int i = 0; i < argc; ++i) { - n = WideCharToMultiByte(CP_UTF8, 0, w_argv[i], -1, args, (int)size, NULL, NULL); - if (n == 0) { - _sapp_fail("Win32: failed to convert all arguments to utf8"); - break; - } - argv[i] = args; - size -= (size_t)n; - args += n; - } - LocalFree(w_argv); - } - *o_argc = argc; - return argv; -} - -#if !defined(SOKOL_NO_ENTRY) -#if defined(SOKOL_WIN32_FORCE_MAIN) -int main(int argc, char* argv[]) { - sapp_desc desc = sokol_main(argc, argv); - _sapp_win32_run(&desc); - return 0; -} -#else -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { - _SOKOL_UNUSED(hInstance); - _SOKOL_UNUSED(hPrevInstance); - _SOKOL_UNUSED(lpCmdLine); - _SOKOL_UNUSED(nCmdShow); - int argc_utf8 = 0; - char** argv_utf8 = _sapp_win32_command_line_to_utf8_argv(GetCommandLineW(), &argc_utf8); - sapp_desc desc = sokol_main(argc_utf8, argv_utf8); - _sapp_win32_run(&desc); - SOKOL_FREE(argv_utf8); - return 0; -} -#endif /* SOKOL_WIN32_FORCE_MAIN */ -#endif /* SOKOL_NO_ENTRY */ - -#ifdef _MSC_VER - #pragma warning(pop) -#endif - -#endif /* _SAPP_WIN32 */ - -/*== UWP ================================================================*/ -#if defined(_SAPP_UWP) - -// Helper functions -_SOKOL_PRIVATE void _sapp_uwp_configure_dpi(float monitor_dpi) { - _sapp.uwp.dpi.window_scale = monitor_dpi / 96.0f; - if (_sapp.desc.high_dpi) { - _sapp.uwp.dpi.content_scale = _sapp.uwp.dpi.window_scale; - _sapp.uwp.dpi.mouse_scale = 1.0f * _sapp.uwp.dpi.window_scale; - } - else { - _sapp.uwp.dpi.content_scale = 1.0f; - _sapp.uwp.dpi.mouse_scale = 1.0f; - } - _sapp.dpi_scale = _sapp.uwp.dpi.content_scale; -} - -_SOKOL_PRIVATE void _sapp_uwp_show_mouse(bool visible) { - using namespace winrt::Windows::UI::Core; - - /* NOTE: this function is only called when the mouse visibility actually changes */ - CoreWindow::GetForCurrentThread().PointerCursor(visible ? - CoreCursor(CoreCursorType::Arrow, 0) : - CoreCursor(nullptr)); -} - -_SOKOL_PRIVATE uint32_t _sapp_uwp_mods(winrt::Windows::UI::Core::CoreWindow const& sender_window) { - using namespace winrt::Windows::System; - using namespace winrt::Windows::UI::Core; - - uint32_t mods = 0; - if ((sender_window.GetKeyState(VirtualKey::Shift) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { - mods |= SAPP_MODIFIER_SHIFT; - } - if ((sender_window.GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { - mods |= SAPP_MODIFIER_CTRL; - } - if ((sender_window.GetKeyState(VirtualKey::Menu) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { - mods |= SAPP_MODIFIER_ALT; - } - if (((sender_window.GetKeyState(VirtualKey::LeftWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) || - ((sender_window.GetKeyState(VirtualKey::RightWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down)) - { - mods |= SAPP_MODIFIER_SUPER; - } - if (0 != (_sapp.uwp.mouse_buttons & (1<= 32)) { - _sapp_init_event(SAPP_EVENTTYPE_CHAR); - _sapp.event.modifiers = _sapp_uwp_mods(sender_window); - _sapp.event.char_code = c; - _sapp.event.key_repeat = repeat; - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_uwp_toggle_fullscreen(void) { - auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); - _sapp.fullscreen = appView.IsFullScreenMode(); - if (!_sapp.fullscreen) { - appView.TryEnterFullScreenMode(); - } - else { - appView.ExitFullScreenMode(); - } - _sapp.fullscreen = appView.IsFullScreenMode(); -} - -namespace {/* Empty namespace to ensure internal linkage (same as _SOKOL_PRIVATE) */ - -// Controls all the DirectX device resources. -class DeviceResources { -public: - // Provides an interface for an application that owns DeviceResources to be notified of the device being lost or created. - interface IDeviceNotify { - virtual void OnDeviceLost() = 0; - virtual void OnDeviceRestored() = 0; - }; - - DeviceResources(); - ~DeviceResources(); - void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window); - void SetLogicalSize(winrt::Windows::Foundation::Size logicalSize); - void SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation); - void SetDpi(float dpi); - void ValidateDevice(); - void HandleDeviceLost(); - void RegisterDeviceNotify(IDeviceNotify* deviceNotify); - void Trim(); - void Present(); - -private: - - // Swapchain Rotation Matrices (Z-rotation) - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation0 = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation90 = { - 0.0f, 1.0f, 0.0f, 0.0f, - -1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation180 = { - -1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, -1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation270 = { - 0.0f, -1.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - - void CreateDeviceResources(); - void CreateWindowSizeDependentResources(); - void UpdateRenderTargetSize(); - DXGI_MODE_ROTATION ComputeDisplayRotation(); - bool SdkLayersAvailable(); - - // Direct3D objects. - winrt::com_ptr m_d3dDevice; - winrt::com_ptr m_d3dContext; - winrt::com_ptr m_swapChain; - - // Direct3D rendering objects. Required for 3D. - winrt::com_ptr m_d3dRenderTarget; - winrt::com_ptr m_d3dRenderTargetView; - winrt::com_ptr m_d3dMSAARenderTarget; - winrt::com_ptr m_d3dMSAARenderTargetView; - winrt::com_ptr m_d3dDepthStencil; - winrt::com_ptr m_d3dDepthStencilView; - D3D11_VIEWPORT m_screenViewport = { }; - - // Cached reference to the Window. - winrt::agile_ref< winrt::Windows::UI::Core::CoreWindow> m_window; - - // Cached device properties. - D3D_FEATURE_LEVEL m_d3dFeatureLevel = D3D_FEATURE_LEVEL_9_1; - winrt::Windows::Foundation::Size m_d3dRenderTargetSize = { }; - winrt::Windows::Foundation::Size m_outputSize = { }; - winrt::Windows::Foundation::Size m_logicalSize = { }; - winrt::Windows::Graphics::Display::DisplayOrientations m_nativeOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None; - winrt::Windows::Graphics::Display::DisplayOrientations m_currentOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None; - float m_dpi = -1.0f; - - // Transforms used for display orientation. - DirectX::XMFLOAT4X4 m_orientationTransform3D; - - // The IDeviceNotify can be held directly as it owns the DeviceResources. - IDeviceNotify* m_deviceNotify = nullptr; -}; - -// Main entry point for our app. Connects the app with the Windows shell and handles application lifecycle events. -struct App : winrt::implements { -public: - // IFrameworkViewSource Methods - winrt::Windows::ApplicationModel::Core::IFrameworkView CreateView() { return *this; } - - // IFrameworkView Methods. - virtual void Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView); - virtual void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window); - virtual void Load(winrt::hstring const& entryPoint); - virtual void Run(); - virtual void Uninitialize(); - -protected: - // Application lifecycle event handlers - void OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args); - void OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args); - void OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args); - - // Window event handlers - void OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args); - void OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args); - - // Navigation event handlers - void OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args); - - // Input event handlers - void OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args); - void OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args); - void OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args); - - // Pointer event handlers - void OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - - // DisplayInformation event handlers. - void OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); - void OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); - void OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); - -private: - std::unique_ptr m_deviceResources; - bool m_windowVisible = true; -}; - -DeviceResources::DeviceResources() { - CreateDeviceResources(); -} - -DeviceResources::~DeviceResources() { - // Cleanup Sokol Context - _sapp.d3d11.device = nullptr; - _sapp.d3d11.device_context = nullptr; -} - -void DeviceResources::CreateDeviceResources() { - // This flag adds support for surfaces with a different color channel ordering - // than the API default. It is required for compatibility with Direct2D. - UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; - - #if defined(_DEBUG) - if (SdkLayersAvailable()) { - // If the project is in a debug build, enable debugging via SDK Layers with this flag. - creationFlags |= D3D11_CREATE_DEVICE_DEBUG; - } - #endif - - // This array defines the set of DirectX hardware feature levels this app will support. - // Note the ordering should be preserved. - // Don't forget to declare your application's minimum required feature level in its - // description. All applications are assumed to support 9.1 unless otherwise stated. - D3D_FEATURE_LEVEL featureLevels[] = { - D3D_FEATURE_LEVEL_12_1, - D3D_FEATURE_LEVEL_12_0, - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1 - }; - - // Create the Direct3D 11 API device object and a corresponding context. - winrt::com_ptr device; - winrt::com_ptr context; - - HRESULT hr = D3D11CreateDevice( - nullptr, // Specify nullptr to use the default adapter. - D3D_DRIVER_TYPE_HARDWARE, // Create a device using the hardware graphics driver. - 0, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE. - creationFlags, // Set debug and Direct2D compatibility flags. - featureLevels, // List of feature levels this app can support. - ARRAYSIZE(featureLevels), // Size of the list above. - D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps. - device.put(), // Returns the Direct3D device created. - &m_d3dFeatureLevel, // Returns feature level of device created. - context.put() // Returns the device immediate context. - ); - - if (FAILED(hr)) { - // If the initialization fails, fall back to the WARP device. - // For more information on WARP, see: - // https://go.microsoft.com/fwlink/?LinkId=286690 - winrt::check_hresult( - D3D11CreateDevice( - nullptr, - D3D_DRIVER_TYPE_WARP, // Create a WARP device instead of a hardware device. - 0, - creationFlags, - featureLevels, - ARRAYSIZE(featureLevels), - D3D11_SDK_VERSION, - device.put(), - &m_d3dFeatureLevel, - context.put() - ) - ); - } - - // Store pointers to the Direct3D 11.3 API device and immediate context. - m_d3dDevice = device.as(); - m_d3dContext = context.as(); - - // Setup Sokol Context - _sapp.d3d11.device = m_d3dDevice.get(); - _sapp.d3d11.device_context = m_d3dContext.get(); -} - -void DeviceResources::CreateWindowSizeDependentResources() { - // Cleanup Sokol Context (these are non-owning raw pointers) - _sapp.d3d11.rt = nullptr; - _sapp.d3d11.rtv = nullptr; - _sapp.d3d11.msaa_rt = nullptr; - _sapp.d3d11.msaa_rtv = nullptr; - _sapp.d3d11.ds = nullptr; - _sapp.d3d11.dsv = nullptr; - - // Clear the previous window size specific context. - ID3D11RenderTargetView* nullViews[] = { nullptr }; - m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr); - // these are smart pointers, setting to nullptr will delete the objects - m_d3dRenderTarget = nullptr; - m_d3dRenderTargetView = nullptr; - m_d3dMSAARenderTarget = nullptr; - m_d3dMSAARenderTargetView = nullptr; - m_d3dDepthStencilView = nullptr; - m_d3dDepthStencil = nullptr; - m_d3dContext->Flush1(D3D11_CONTEXT_TYPE_ALL, nullptr); - - UpdateRenderTargetSize(); - - // The width and height of the swap chain must be based on the window's - // natively-oriented width and height. If the window is not in the native - // orientation, the dimensions must be reversed. - DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation(); - - bool swapDimensions = displayRotation == DXGI_MODE_ROTATION_ROTATE90 || displayRotation == DXGI_MODE_ROTATION_ROTATE270; - m_d3dRenderTargetSize.Width = swapDimensions ? m_outputSize.Height : m_outputSize.Width; - m_d3dRenderTargetSize.Height = swapDimensions ? m_outputSize.Width : m_outputSize.Height; - - if (m_swapChain != nullptr) { - // If the swap chain already exists, resize it. - HRESULT hr = m_swapChain->ResizeBuffers( - 2, // Double-buffered swap chain. - lround(m_d3dRenderTargetSize.Width), - lround(m_d3dRenderTargetSize.Height), - DXGI_FORMAT_B8G8R8A8_UNORM, - 0 - ); - - if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { - // If the device was removed for any reason, a new device and swap chain will need to be created. - HandleDeviceLost(); - - // Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method - // and correctly set up the new device. - return; - } - else { - winrt::check_hresult(hr); - } - } - else { - // Otherwise, create a new one using the same adapter as the existing Direct3D device. - DXGI_SCALING scaling = (_sapp.uwp.dpi.content_scale == _sapp.uwp.dpi.window_scale) ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH; - DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 }; - - swapChainDesc.Width = lround(m_d3dRenderTargetSize.Width); // Match the size of the window. - swapChainDesc.Height = lround(m_d3dRenderTargetSize.Height); - swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format. - swapChainDesc.Stereo = false; - swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling. - swapChainDesc.SampleDesc.Quality = 0; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency. - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Microsoft Store apps must use this SwapEffect. - swapChainDesc.Flags = 0; - swapChainDesc.Scaling = scaling; - swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; - - // This sequence obtains the DXGI factory that was used to create the Direct3D device above. - winrt::com_ptr dxgiDevice = m_d3dDevice.as(); - winrt::com_ptr dxgiAdapter; - winrt::check_hresult(dxgiDevice->GetAdapter(dxgiAdapter.put())); - winrt::com_ptr dxgiFactory; - winrt::check_hresult(dxgiAdapter->GetParent(__uuidof(IDXGIFactory4), dxgiFactory.put_void())); - winrt::com_ptr swapChain; - winrt::check_hresult(dxgiFactory->CreateSwapChainForCoreWindow(m_d3dDevice.get(), m_window.get().as<::IUnknown>().get(), &swapChainDesc, nullptr, swapChain.put())); - m_swapChain = swapChain.as(); - - // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and - // ensures that the application will only render after each VSync, minimizing power consumption. - winrt::check_hresult(dxgiDevice->SetMaximumFrameLatency(1)); - - // Setup Sokol Context - winrt::check_hresult(swapChain->GetDesc(&_sapp.d3d11.swap_chain_desc)); - _sapp.d3d11.swap_chain = m_swapChain.as().detach(); - } - - // Set the proper orientation for the swap chain, and generate 2D and - // 3D matrix transformations for rendering to the rotated swap chain. - // Note the rotation angle for the 2D and 3D transforms are different. - // This is due to the difference in coordinate spaces. Additionally, - // the 3D matrix is specified explicitly to avoid rounding errors. - switch (displayRotation) { - case DXGI_MODE_ROTATION_IDENTITY: - m_orientationTransform3D = m_rotation0; - break; - - case DXGI_MODE_ROTATION_ROTATE90: - m_orientationTransform3D = m_rotation270; - break; - - case DXGI_MODE_ROTATION_ROTATE180: - m_orientationTransform3D = m_rotation180; - break; - - case DXGI_MODE_ROTATION_ROTATE270: - m_orientationTransform3D = m_rotation90; - break; - } - winrt::check_hresult(m_swapChain->SetRotation(displayRotation)); - - // Create a render target view of the swap chain back buffer. - winrt::check_hresult(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&m_d3dRenderTarget))); - winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dRenderTarget.get(), nullptr, m_d3dRenderTargetView.put())); - - // Create MSAA texture and view if needed - if (_sapp.sample_count > 1) { - CD3D11_TEXTURE2D_DESC1 msaaTexDesc( - DXGI_FORMAT_B8G8R8A8_UNORM, - lround(m_d3dRenderTargetSize.Width), - lround(m_d3dRenderTargetSize.Height), - 1, // arraySize - 1, // mipLevels - D3D11_BIND_RENDER_TARGET, - D3D11_USAGE_DEFAULT, - 0, // cpuAccessFlags - _sapp.sample_count, - _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 - ); - winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&msaaTexDesc, nullptr, m_d3dMSAARenderTarget.put())); - winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dMSAARenderTarget.get(), nullptr, m_d3dMSAARenderTargetView.put())); - } - - // Create a depth stencil view for use with 3D rendering if needed. - CD3D11_TEXTURE2D_DESC1 depthStencilDesc( - DXGI_FORMAT_D24_UNORM_S8_UINT, - lround(m_d3dRenderTargetSize.Width), - lround(m_d3dRenderTargetSize.Height), - 1, // This depth stencil view has only one texture. - 1, // Use a single mipmap level. - D3D11_BIND_DEPTH_STENCIL, - D3D11_USAGE_DEFAULT, - 0, // cpuAccessFlag - _sapp.sample_count, - _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 - ); - winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&depthStencilDesc, nullptr, m_d3dDepthStencil.put())); - - CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D); - winrt::check_hresult(m_d3dDevice->CreateDepthStencilView(m_d3dDepthStencil.get(), nullptr, m_d3dDepthStencilView.put())); - - // Set sokol window and framebuffer sizes - _sapp.window_width = (int) m_logicalSize.Width; - _sapp.window_height = (int) m_logicalSize.Height; - _sapp.framebuffer_width = lround(m_d3dRenderTargetSize.Width); - _sapp.framebuffer_height = lround(m_d3dRenderTargetSize.Height); - - // Setup Sokol Context - _sapp.d3d11.rt = m_d3dRenderTarget.as().get(); - _sapp.d3d11.rtv = m_d3dRenderTargetView.as().get(); - _sapp.d3d11.ds = m_d3dDepthStencil.as().get(); - _sapp.d3d11.dsv = m_d3dDepthStencilView.get(); - if (_sapp.sample_count > 1) { - _sapp.d3d11.msaa_rt = m_d3dMSAARenderTarget.as().get(); - _sapp.d3d11.msaa_rtv = m_d3dMSAARenderTargetView.as().get(); - } - - // Sokol app is now valid - _sapp.valid = true; -} - -// Determine the dimensions of the render target and whether it will be scaled down. -void DeviceResources::UpdateRenderTargetSize() { - // Calculate the necessary render target size in pixels. - m_outputSize.Width = m_logicalSize.Width * _sapp.uwp.dpi.content_scale; - m_outputSize.Height = m_logicalSize.Height * _sapp.uwp.dpi.content_scale; - - // Prevent zero size DirectX content from being created. - m_outputSize.Width = std::max(m_outputSize.Width, 1.0f); - m_outputSize.Height = std::max(m_outputSize.Height, 1.0f); -} - -// This method is called when the CoreWindow is created (or re-created). -void DeviceResources::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) { - auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView(); - m_window = window; - m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height); - m_nativeOrientation = currentDisplayInformation.NativeOrientation(); - m_currentOrientation = currentDisplayInformation.CurrentOrientation(); - m_dpi = currentDisplayInformation.LogicalDpi(); - _sapp_uwp_configure_dpi(m_dpi); - CreateWindowSizeDependentResources(); -} - -// This method is called in the event handler for the SizeChanged event. -void DeviceResources::SetLogicalSize(winrt::Windows::Foundation::Size logicalSize) { - if (m_logicalSize != logicalSize) { - m_logicalSize = logicalSize; - CreateWindowSizeDependentResources(); - } -} - -// This method is called in the event handler for the DpiChanged event. -void DeviceResources::SetDpi(float dpi) { - if (dpi != m_dpi) { - m_dpi = dpi; - _sapp_uwp_configure_dpi(m_dpi); - // When the display DPI changes, the logical size of the window (measured in Dips) also changes and needs to be updated. - auto window = m_window.get(); - m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height); - CreateWindowSizeDependentResources(); - } -} - -// This method is called in the event handler for the OrientationChanged event. -void DeviceResources::SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation) { - if (m_currentOrientation != currentOrientation) { - m_currentOrientation = currentOrientation; - CreateWindowSizeDependentResources(); - } -} - -// This method is called in the event handler for the DisplayContentsInvalidated event. -void DeviceResources::ValidateDevice() { - // The D3D Device is no longer valid if the default adapter changed since the device - // was created or if the device has been removed. - - // First, get the information for the default adapter from when the device was created. - winrt::com_ptr dxgiDevice = m_d3dDevice.as< IDXGIDevice3>(); - winrt::com_ptr deviceAdapter; - winrt::check_hresult(dxgiDevice->GetAdapter(deviceAdapter.put())); - winrt::com_ptr deviceFactory; - winrt::check_hresult(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory))); - winrt::com_ptr previousDefaultAdapter; - winrt::check_hresult(deviceFactory->EnumAdapters1(0, previousDefaultAdapter.put())); - DXGI_ADAPTER_DESC1 previousDesc; - winrt::check_hresult(previousDefaultAdapter->GetDesc1(&previousDesc)); - - // Next, get the information for the current default adapter. - winrt::com_ptr currentFactory; - winrt::check_hresult(CreateDXGIFactory1(IID_PPV_ARGS(¤tFactory))); - winrt::com_ptr currentDefaultAdapter; - winrt::check_hresult(currentFactory->EnumAdapters1(0, currentDefaultAdapter.put())); - DXGI_ADAPTER_DESC1 currentDesc; - winrt::check_hresult(currentDefaultAdapter->GetDesc1(¤tDesc)); - - // If the adapter LUIDs don't match, or if the device reports that it has been removed, - // a new D3D device must be created. - if (previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart || - previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart || - FAILED(m_d3dDevice->GetDeviceRemovedReason())) - { - // Release references to resources related to the old device. - dxgiDevice = nullptr; - deviceAdapter = nullptr; - deviceFactory = nullptr; - previousDefaultAdapter = nullptr; - - // Create a new device and swap chain. - HandleDeviceLost(); - } -} - -// Recreate all device resources and set them back to the current state. -void DeviceResources::HandleDeviceLost() { - m_swapChain = nullptr; - if (m_deviceNotify != nullptr) { - m_deviceNotify->OnDeviceLost(); - } - CreateDeviceResources(); - CreateWindowSizeDependentResources(); - if (m_deviceNotify != nullptr) { - m_deviceNotify->OnDeviceRestored(); - } -} - -// Register our DeviceNotify to be informed on device lost and creation. -void DeviceResources::RegisterDeviceNotify(IDeviceNotify* deviceNotify) { - m_deviceNotify = deviceNotify; -} - -// Call this method when the app suspends. It provides a hint to the driver that the app -// is entering an idle state and that temporary buffers can be reclaimed for use by other apps. -void DeviceResources::Trim() { - m_d3dDevice.as()->Trim(); -} - -// Present the contents of the swap chain to the screen. -void DeviceResources::Present() { - - // MSAA resolve if needed - if (_sapp.sample_count > 1) { - m_d3dContext->ResolveSubresource(m_d3dRenderTarget.get(), 0, m_d3dMSAARenderTarget.get(), 0, DXGI_FORMAT_B8G8R8A8_UNORM); - m_d3dContext->DiscardView1(m_d3dMSAARenderTargetView.get(), nullptr, 0); - } - - // The first argument instructs DXGI to block until VSync, putting the application - // to sleep until the next VSync. This ensures we don't waste any cycles rendering - // frames that will never be displayed to the screen. - DXGI_PRESENT_PARAMETERS parameters = { 0 }; - HRESULT hr = m_swapChain->Present1(1, 0, ¶meters); - - // Discard the contents of the render target. - // This is a valid operation only when the existing contents will be entirely - // overwritten. If dirty or scroll rects are used, this call should be removed. - m_d3dContext->DiscardView1(m_d3dRenderTargetView.get(), nullptr, 0); - - // Discard the contents of the depth stencil. - m_d3dContext->DiscardView1(m_d3dDepthStencilView.get(), nullptr, 0); - - // If the device was removed either by a disconnection or a driver upgrade, we - // must recreate all device resources. - if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { - HandleDeviceLost(); - } - else { - winrt::check_hresult(hr); - } -} - -// This method determines the rotation between the display device's native orientation and the -// current display orientation. -DXGI_MODE_ROTATION DeviceResources::ComputeDisplayRotation() { - DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED; - - // Note: NativeOrientation can only be Landscape or Portrait even though - // the DisplayOrientations enum has other values. - switch (m_nativeOrientation) { - case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: - switch (m_currentOrientation) { - case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: - rotation = DXGI_MODE_ROTATION_IDENTITY; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: - rotation = DXGI_MODE_ROTATION_ROTATE270; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE180; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE90; - break; - } - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: - switch (m_currentOrientation) { - case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: - rotation = DXGI_MODE_ROTATION_ROTATE90; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: - rotation = DXGI_MODE_ROTATION_IDENTITY; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE270; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE180; - break; - } - break; - } - return rotation; -} - -// Check for SDK Layer support. -bool DeviceResources::SdkLayersAvailable() { - #if defined(_DEBUG) - HRESULT hr = D3D11CreateDevice( - nullptr, - D3D_DRIVER_TYPE_NULL, // There is no need to create a real hardware device. - 0, - D3D11_CREATE_DEVICE_DEBUG, // Check for the SDK layers. - nullptr, // Any feature level will do. - 0, - D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps. - nullptr, // No need to keep the D3D device reference. - nullptr, // No need to know the feature level. - nullptr // No need to keep the D3D device context reference. - ); - return SUCCEEDED(hr); - #else - return false; - #endif -} - -// The first method called when the IFrameworkView is being created. -void App::Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView) { - // Register event handlers for app lifecycle. This example includes Activated, so that we - // can make the CoreWindow active and start rendering on the window. - applicationView.Activated({ this, &App::OnActivated }); - - winrt::Windows::ApplicationModel::Core::CoreApplication::Suspending({ this, &App::OnSuspending }); - winrt::Windows::ApplicationModel::Core::CoreApplication::Resuming({ this, &App::OnResuming }); - - // At this point we have access to the device. - // We can create the device-dependent resources. - m_deviceResources = std::make_unique(); -} - -// Called when the CoreWindow object is created (or re-created). -void App::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) { - window.SizeChanged({ this, &App::OnWindowSizeChanged }); - window.VisibilityChanged({ this, &App::OnVisibilityChanged }); - - window.KeyDown({ this, &App::OnKeyDown }); - window.KeyUp({ this, &App::OnKeyUp }); - window.CharacterReceived({ this, &App::OnCharacterReceived }); - - window.PointerEntered({ this, &App::OnPointerEntered }); - window.PointerExited({ this, &App::OnPointerExited }); - window.PointerPressed({ this, &App::OnPointerPressed }); - window.PointerReleased({ this, &App::OnPointerReleased }); - window.PointerMoved({ this, &App::OnPointerMoved }); - window.PointerWheelChanged({ this, &App::OnPointerWheelChanged }); - - auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView(); - - currentDisplayInformation.DpiChanged({ this, &App::OnDpiChanged }); - currentDisplayInformation.OrientationChanged({ this, &App::OnOrientationChanged }); - winrt::Windows::Graphics::Display::DisplayInformation::DisplayContentsInvalidated({ this, &App::OnDisplayContentsInvalidated }); - - winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested({ this, &App::OnBackRequested }); - - m_deviceResources->SetWindow(window); -} - -// Initializes scene resources, or loads a previously saved app state. -void App::Load(winrt::hstring const& entryPoint) { - _SOKOL_UNUSED(entryPoint); -} - -// This method is called after the window becomes active. -void App::Run() { - // NOTE: UWP will simply terminate an application, it's not possible to detect when an application is being closed - while (true) { - if (m_windowVisible) { - winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent); - _sapp_frame(); - m_deviceResources->Present(); - } - else { - winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessOneAndAllPending); - } - } -} - -// Required for IFrameworkView. -// Terminate events do not cause Uninitialize to be called. It will be called if your IFrameworkView -// class is torn down while the app is in the foreground. -void App::Uninitialize() { - // empty -} - -// Application lifecycle event handlers. -void App::OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args) { - _SOKOL_UNUSED(args); - _SOKOL_UNUSED(applicationView); - auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); - auto targetSize = winrt::Windows::Foundation::Size((float)_sapp.desc.width, (float)_sapp.desc.height); - appView.SetPreferredMinSize(targetSize); - appView.TryResizeView(targetSize); - - // Disabling this since it can only append the title to the app name (Title - Appname). - // There's no way of just setting a string to be the window title. - //appView.Title(_sapp.window_title_wide); - - // Run() won't start until the CoreWindow is activated. - winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Activate(); - if (_sapp.desc.fullscreen) { - appView.TryEnterFullScreenMode(); - } - _sapp.fullscreen = appView.IsFullScreenMode(); -} - -void App::OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args) { - _SOKOL_UNUSED(sender); - _SOKOL_UNUSED(args); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_SUSPENDED); -} - -void App::OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args) { - _SOKOL_UNUSED(args); - _SOKOL_UNUSED(sender); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESUMED); -} - -void App::OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args) { - _SOKOL_UNUSED(args); - m_deviceResources->SetLogicalSize(winrt::Windows::Foundation::Size(sender.Bounds().Width, sender.Bounds().Height)); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); -} - -void App::OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args) { - _SOKOL_UNUSED(sender); - m_windowVisible = args.Visible(); - _sapp_win32_uwp_app_event(m_windowVisible ? SAPP_EVENTTYPE_RESTORED : SAPP_EVENTTYPE_ICONIFIED); -} - -void App::OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args) { - _SOKOL_UNUSED(sender); - args.Handled(true); -} - -void App::OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) { - auto status = args.KeyStatus(); - _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_DOWN, sender, args); -} - -void App::OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) { - auto status = args.KeyStatus(); - _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_UP, sender, args); -} - -void App::OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args) { - _sapp_uwp_char_event(args.KeyCode(), args.KeyStatus().WasKeyDown, sender); -} - -void App::OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _SOKOL_UNUSED(args); - _sapp.uwp.mouse_tracked = true; - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender); -} - -void App::OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _SOKOL_UNUSED(args); - _sapp.uwp.mouse_tracked = false; - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, sender); -} - -void App::OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _sapp_uwp_extract_mouse_button_events(sender, args); -} - -// NOTE: for some reason this event handler is never called?? -void App::OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _sapp_uwp_extract_mouse_button_events(sender, args); -} - -void App::OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - auto position = args.CurrentPoint().Position(); - const float new_x = (float)(int)(position.X * _sapp.uwp.dpi.mouse_scale + 0.5f); - const float new_y = (float)(int)(position.Y * _sapp.uwp.dpi.mouse_scale + 0.5f); - // don't update dx/dy in the very first event - if (_sapp.mouse.pos_valid) { - _sapp.mouse.dx = new_x - _sapp.mouse.x; - _sapp.mouse.dy = new_y - _sapp.mouse.y; - } - _sapp.mouse.x = new_x; - _sapp.mouse.y = new_y; - _sapp.mouse.pos_valid = true; - if (!_sapp.uwp.mouse_tracked) { - _sapp.uwp.mouse_tracked = true; - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender); - } - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, sender); - - // HACK for detecting multiple mouse button presses - _sapp_uwp_extract_mouse_button_events(sender, args); -} - -void App::OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - auto properties = args.CurrentPoint().Properties(); - _sapp_uwp_scroll_event((float)properties.MouseWheelDelta(), properties.IsHorizontalMouseWheel(), sender); -} - -void App::OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { - // NOTE: UNTESTED - _SOKOL_UNUSED(args); - m_deviceResources->SetDpi(sender.LogicalDpi()); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); -} - -void App::OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { - // NOTE: UNTESTED - _SOKOL_UNUSED(args); - m_deviceResources->SetCurrentOrientation(sender.CurrentOrientation()); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); -} - -void App::OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { - // NOTE: UNTESTED - _SOKOL_UNUSED(args); - _SOKOL_UNUSED(sender); - m_deviceResources->ValidateDevice(); -} - -} /* End empty namespace */ - -_SOKOL_PRIVATE void _sapp_uwp_run(const sapp_desc* desc) { - _sapp_init_state(desc); - _sapp_win32_uwp_init_keytable(); - _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); - winrt::Windows::ApplicationModel::Core::CoreApplication::Run(winrt::make()); -} - -#if !defined(SOKOL_NO_ENTRY) -#if defined(UNICODE) -int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { -#else -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { -#endif - _SOKOL_UNUSED(hInstance); - _SOKOL_UNUSED(hPrevInstance); - _SOKOL_UNUSED(lpCmdLine); - _SOKOL_UNUSED(nCmdShow); - sapp_desc desc = sokol_main(0, nullptr); - _sapp_uwp_run(&desc); - return 0; -} -#endif /* SOKOL_NO_ENTRY */ -#endif /* _SAPP_UWP */ - -/*== Android ================================================================*/ -#if defined(_SAPP_ANDROID) - -/* android loop thread */ -_SOKOL_PRIVATE bool _sapp_android_init_egl(void) { - SOKOL_ASSERT(_sapp.android.display == EGL_NO_DISPLAY); - SOKOL_ASSERT(_sapp.android.context == EGL_NO_CONTEXT); - - EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (display == EGL_NO_DISPLAY) { - return false; - } - if (eglInitialize(display, NULL, NULL) == EGL_FALSE) { - return false; - } - - EGLint alpha_size = _sapp.desc.alpha ? 8 : 0; - const EGLint cfg_attributes[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, alpha_size, - EGL_DEPTH_SIZE, 16, - EGL_STENCIL_SIZE, 0, - EGL_NONE, - }; - EGLConfig available_cfgs[32]; - EGLint cfg_count; - eglChooseConfig(display, cfg_attributes, available_cfgs, 32, &cfg_count); - SOKOL_ASSERT(cfg_count > 0); - SOKOL_ASSERT(cfg_count <= 32); - - /* find config with 8-bit rgb buffer if available, ndk sample does not trust egl spec */ - EGLConfig config; - bool exact_cfg_found = false; - for (int i = 0; i < cfg_count; ++i) { - EGLConfig c = available_cfgs[i]; - EGLint r, g, b, a, d; - if (eglGetConfigAttrib(display, c, EGL_RED_SIZE, &r) == EGL_TRUE && - eglGetConfigAttrib(display, c, EGL_GREEN_SIZE, &g) == EGL_TRUE && - eglGetConfigAttrib(display, c, EGL_BLUE_SIZE, &b) == EGL_TRUE && - eglGetConfigAttrib(display, c, EGL_ALPHA_SIZE, &a) == EGL_TRUE && - eglGetConfigAttrib(display, c, EGL_DEPTH_SIZE, &d) == EGL_TRUE && - r == 8 && g == 8 && b == 8 && (alpha_size == 0 || a == alpha_size) && d == 16) { - exact_cfg_found = true; - config = c; - break; - } - } - if (!exact_cfg_found) { - config = available_cfgs[0]; - } - - EGLint ctx_attributes[] = { - #if defined(SOKOL_GLES3) - EGL_CONTEXT_CLIENT_VERSION, _sapp.desc.gl_force_gles2 ? 2 : 3, - #else - EGL_CONTEXT_CLIENT_VERSION, 2, - #endif - EGL_NONE, - }; - EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctx_attributes); - if (context == EGL_NO_CONTEXT) { - return false; - } - - _sapp.android.config = config; - _sapp.android.display = display; - _sapp.android.context = context; - return true; -} - -_SOKOL_PRIVATE void _sapp_android_cleanup_egl(void) { - if (_sapp.android.display != EGL_NO_DISPLAY) { - eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (_sapp.android.surface != EGL_NO_SURFACE) { - SOKOL_LOG("Destroying egl surface"); - eglDestroySurface(_sapp.android.display, _sapp.android.surface); - _sapp.android.surface = EGL_NO_SURFACE; - } - if (_sapp.android.context != EGL_NO_CONTEXT) { - SOKOL_LOG("Destroying egl context"); - eglDestroyContext(_sapp.android.display, _sapp.android.context); - _sapp.android.context = EGL_NO_CONTEXT; - } - SOKOL_LOG("Terminating egl display"); - eglTerminate(_sapp.android.display); - _sapp.android.display = EGL_NO_DISPLAY; - } -} - -_SOKOL_PRIVATE bool _sapp_android_init_egl_surface(ANativeWindow* window) { - SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); - SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); - SOKOL_ASSERT(_sapp.android.surface == EGL_NO_SURFACE); - SOKOL_ASSERT(window); - - /* TODO: set window flags */ - /* ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0); */ - - /* create egl surface and make it current */ - EGLSurface surface = eglCreateWindowSurface(_sapp.android.display, _sapp.android.config, window, NULL); - if (surface == EGL_NO_SURFACE) { - return false; - } - if (eglMakeCurrent(_sapp.android.display, surface, surface, _sapp.android.context) == EGL_FALSE) { - return false; - } - _sapp.android.surface = surface; - return true; -} - -_SOKOL_PRIVATE void _sapp_android_cleanup_egl_surface(void) { - if (_sapp.android.display == EGL_NO_DISPLAY) { - return; - } - eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (_sapp.android.surface != EGL_NO_SURFACE) { - eglDestroySurface(_sapp.android.display, _sapp.android.surface); - _sapp.android.surface = EGL_NO_SURFACE; - } -} - -_SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - SOKOL_LOG("event_cb()"); - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool force_update) { - SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); - SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); - SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE); - SOKOL_ASSERT(window); - - const int32_t win_w = ANativeWindow_getWidth(window); - const int32_t win_h = ANativeWindow_getHeight(window); - SOKOL_ASSERT(win_w >= 0 && win_h >= 0); - const bool win_changed = (win_w != _sapp.window_width) || (win_h != _sapp.window_height); - _sapp.window_width = win_w; - _sapp.window_height = win_h; - if (win_changed || force_update) { - if (!_sapp.desc.high_dpi) { - const int32_t buf_w = win_w / 2; - const int32_t buf_h = win_h / 2; - EGLint format; - EGLBoolean egl_result = eglGetConfigAttrib(_sapp.android.display, _sapp.android.config, EGL_NATIVE_VISUAL_ID, &format); - SOKOL_ASSERT(egl_result == EGL_TRUE); - /* NOTE: calling ANativeWindow_setBuffersGeometry() with the same dimensions - as the ANativeWindow size results in weird display artefacts, that's - why it's only called when the buffer geometry is different from - the window size - */ - int32_t result = ANativeWindow_setBuffersGeometry(window, buf_w, buf_h, format); - SOKOL_ASSERT(result == 0); - } - } - - /* query surface size */ - EGLint fb_w, fb_h; - EGLBoolean egl_result_w = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_WIDTH, &fb_w); - EGLBoolean egl_result_h = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_HEIGHT, &fb_h); - SOKOL_ASSERT(egl_result_w == EGL_TRUE); - SOKOL_ASSERT(egl_result_h == EGL_TRUE); - const bool fb_changed = (fb_w != _sapp.framebuffer_width) || (fb_h != _sapp.framebuffer_height); - _sapp.framebuffer_width = fb_w; - _sapp.framebuffer_height = fb_h; - _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width; - if (win_changed || fb_changed || force_update) { - if (!_sapp.first_frame) { - SOKOL_LOG("SAPP_EVENTTYPE_RESIZED"); - _sapp_android_app_event(SAPP_EVENTTYPE_RESIZED); - } - } -} - -_SOKOL_PRIVATE void _sapp_android_cleanup(void) { - SOKOL_LOG("Cleaning up"); - if (_sapp.android.surface != EGL_NO_SURFACE) { - /* egl context is bound, cleanup gracefully */ - if (_sapp.init_called && !_sapp.cleanup_called) { - SOKOL_LOG("cleanup_cb()"); - _sapp_call_cleanup(); - } - } - /* always try to cleanup by destroying egl context */ - _sapp_android_cleanup_egl(); -} - -_SOKOL_PRIVATE void _sapp_android_shutdown(void) { - /* try to cleanup while we still have a surface and can call cleanup_cb() */ - _sapp_android_cleanup(); - /* request exit */ - ANativeActivity_finish(_sapp.android.activity); -} - -_SOKOL_PRIVATE void _sapp_android_frame(void) { - SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); - SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); - SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE); - _sapp_android_update_dimensions(_sapp.android.current.window, false); - _sapp_frame(); - eglSwapBuffers(_sapp.android.display, _sapp.android.surface); -} - -_SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) { - if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_MOTION) { - return false; - } - if (!_sapp_events_enabled()) { - return false; - } - int32_t action_idx = AMotionEvent_getAction(e); - int32_t action = action_idx & AMOTION_EVENT_ACTION_MASK; - sapp_event_type type = SAPP_EVENTTYPE_INVALID; - switch (action) { - case AMOTION_EVENT_ACTION_DOWN: - SOKOL_LOG("Touch: down"); - case AMOTION_EVENT_ACTION_POINTER_DOWN: - SOKOL_LOG("Touch: ptr down"); - type = SAPP_EVENTTYPE_TOUCHES_BEGAN; - break; - case AMOTION_EVENT_ACTION_MOVE: - type = SAPP_EVENTTYPE_TOUCHES_MOVED; - break; - case AMOTION_EVENT_ACTION_UP: - SOKOL_LOG("Touch: up"); - case AMOTION_EVENT_ACTION_POINTER_UP: - SOKOL_LOG("Touch: ptr up"); - type = SAPP_EVENTTYPE_TOUCHES_ENDED; - break; - case AMOTION_EVENT_ACTION_CANCEL: - SOKOL_LOG("Touch: cancel"); - type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; - break; - default: - break; - } - if (type == SAPP_EVENTTYPE_INVALID) { - return false; - } - int32_t idx = action_idx >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - _sapp_init_event(type); - _sapp.event.num_touches = (int)AMotionEvent_getPointerCount(e); - if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) { - _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS; - } - for (int32_t i = 0; i < _sapp.event.num_touches; i++) { - sapp_touchpoint* dst = &_sapp.event.touches[i]; - dst->identifier = (uintptr_t)AMotionEvent_getPointerId(e, (size_t)i); - dst->pos_x = (AMotionEvent_getRawX(e, (size_t)i) / _sapp.window_width) * _sapp.framebuffer_width; - dst->pos_y = (AMotionEvent_getRawY(e, (size_t)i) / _sapp.window_height) * _sapp.framebuffer_height; - - if (action == AMOTION_EVENT_ACTION_POINTER_DOWN || - action == AMOTION_EVENT_ACTION_POINTER_UP) { - dst->changed = (i == idx); - } else { - dst->changed = true; - } - } - _sapp_call_event(&_sapp.event); - return true; -} - -_SOKOL_PRIVATE bool _sapp_android_key_event(const AInputEvent* e) { - if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_KEY) { - return false; - } - if (AKeyEvent_getKeyCode(e) == AKEYCODE_BACK) { - /* FIXME: this should be hooked into a "really quit?" mechanism - so the app can ask the user for confirmation, this is currently - generally missing in sokol_app.h - */ - _sapp_android_shutdown(); - return true; - } - return false; -} - -_SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) { - if ((events & ALOOPER_EVENT_INPUT) == 0) { - SOKOL_LOG("_sapp_android_input_cb() encountered unsupported event"); - return 1; - } - SOKOL_ASSERT(_sapp.android.current.input); - AInputEvent* event = NULL; - while (AInputQueue_getEvent(_sapp.android.current.input, &event) >= 0) { - if (AInputQueue_preDispatchEvent(_sapp.android.current.input, event) != 0) { - continue; - } - int32_t handled = 0; - if (_sapp_android_touch_event(event) || _sapp_android_key_event(event)) { - handled = 1; - } - AInputQueue_finishEvent(_sapp.android.current.input, event, handled); - } - return 1; -} - -_SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { - if ((events & ALOOPER_EVENT_INPUT) == 0) { - SOKOL_LOG("_sapp_android_main_cb() encountered unsupported event"); - return 1; - } - - _sapp_android_msg_t msg; - if (read(fd, &msg, sizeof(msg)) != sizeof(msg)) { - SOKOL_LOG("Could not write to read_from_main_fd"); - return 1; - } - - pthread_mutex_lock(&_sapp.android.pt.mutex); - switch (msg) { - case _SOKOL_ANDROID_MSG_CREATE: - { - SOKOL_LOG("MSG_CREATE"); - SOKOL_ASSERT(!_sapp.valid); - bool result = _sapp_android_init_egl(); - SOKOL_ASSERT(result); - _sapp.valid = true; - _sapp.android.has_created = true; - } - break; - case _SOKOL_ANDROID_MSG_RESUME: - SOKOL_LOG("MSG_RESUME"); - _sapp.android.has_resumed = true; - _sapp_android_app_event(SAPP_EVENTTYPE_RESUMED); - break; - case _SOKOL_ANDROID_MSG_PAUSE: - SOKOL_LOG("MSG_PAUSE"); - _sapp.android.has_resumed = false; - _sapp_android_app_event(SAPP_EVENTTYPE_SUSPENDED); - break; - case _SOKOL_ANDROID_MSG_FOCUS: - SOKOL_LOG("MSG_FOCUS"); - _sapp.android.has_focus = true; - break; - case _SOKOL_ANDROID_MSG_NO_FOCUS: - SOKOL_LOG("MSG_NO_FOCUS"); - _sapp.android.has_focus = false; - break; - case _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW: - SOKOL_LOG("MSG_SET_NATIVE_WINDOW"); - if (_sapp.android.current.window != _sapp.android.pending.window) { - if (_sapp.android.current.window != NULL) { - _sapp_android_cleanup_egl_surface(); - } - if (_sapp.android.pending.window != NULL) { - SOKOL_LOG("Creating egl surface ..."); - if (_sapp_android_init_egl_surface(_sapp.android.pending.window)) { - SOKOL_LOG("... ok!"); - _sapp_android_update_dimensions(_sapp.android.pending.window, true); - } else { - SOKOL_LOG("... failed!"); - _sapp_android_shutdown(); - } - } - } - _sapp.android.current.window = _sapp.android.pending.window; - break; - case _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE: - SOKOL_LOG("MSG_SET_INPUT_QUEUE"); - if (_sapp.android.current.input != _sapp.android.pending.input) { - if (_sapp.android.current.input != NULL) { - AInputQueue_detachLooper(_sapp.android.current.input); - } - if (_sapp.android.pending.input != NULL) { - AInputQueue_attachLooper( - _sapp.android.pending.input, - _sapp.android.looper, - ALOOPER_POLL_CALLBACK, - _sapp_android_input_cb, - NULL); /* data */ - } - } - _sapp.android.current.input = _sapp.android.pending.input; - break; - case _SOKOL_ANDROID_MSG_DESTROY: - SOKOL_LOG("MSG_DESTROY"); - _sapp_android_cleanup(); - _sapp.valid = false; - _sapp.android.is_thread_stopping = true; - break; - default: - SOKOL_LOG("Unknown msg type received"); - break; - } - pthread_cond_broadcast(&_sapp.android.pt.cond); /* signal "received" */ - pthread_mutex_unlock(&_sapp.android.pt.mutex); - return 1; -} - -_SOKOL_PRIVATE bool _sapp_android_should_update(void) { - bool is_in_front = _sapp.android.has_resumed && _sapp.android.has_focus; - bool has_surface = _sapp.android.surface != EGL_NO_SURFACE; - return is_in_front && has_surface; -} - -_SOKOL_PRIVATE void _sapp_android_show_keyboard(bool shown) { - SOKOL_ASSERT(_sapp.valid); - /* This seems to be broken in the NDK, but there is (a very cumbersome) workaround... */ - if (shown) { - SOKOL_LOG("Showing keyboard"); - ANativeActivity_showSoftInput(_sapp.android.activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED); - } else { - SOKOL_LOG("Hiding keyboard"); - ANativeActivity_hideSoftInput(_sapp.android.activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS); - } -} - -_SOKOL_PRIVATE void* _sapp_android_loop(void* arg) { - _SOKOL_UNUSED(arg); - SOKOL_LOG("Loop thread started"); - - _sapp.android.looper = ALooper_prepare(0 /* or ALOOPER_PREPARE_ALLOW_NON_CALLBACKS*/); - ALooper_addFd(_sapp.android.looper, - _sapp.android.pt.read_from_main_fd, - ALOOPER_POLL_CALLBACK, - ALOOPER_EVENT_INPUT, - _sapp_android_main_cb, - NULL); /* data */ - - /* signal start to main thread */ - pthread_mutex_lock(&_sapp.android.pt.mutex); - _sapp.android.is_thread_started = true; - pthread_cond_broadcast(&_sapp.android.pt.cond); - pthread_mutex_unlock(&_sapp.android.pt.mutex); - - /* main loop */ - while (!_sapp.android.is_thread_stopping) { - /* sokol frame */ - if (_sapp_android_should_update()) { - _sapp_android_frame(); - } - - /* process all events (or stop early if app is requested to quit) */ - bool process_events = true; - while (process_events && !_sapp.android.is_thread_stopping) { - bool block_until_event = !_sapp.android.is_thread_stopping && !_sapp_android_should_update(); - process_events = ALooper_pollOnce(block_until_event ? -1 : 0, NULL, NULL, NULL) == ALOOPER_POLL_CALLBACK; - } - } - - /* cleanup thread */ - if (_sapp.android.current.input != NULL) { - AInputQueue_detachLooper(_sapp.android.current.input); - } - - /* the following causes heap corruption on exit, why?? - ALooper_removeFd(_sapp.android.looper, _sapp.android.pt.read_from_main_fd); - ALooper_release(_sapp.android.looper);*/ - - /* signal "destroyed" */ - pthread_mutex_lock(&_sapp.android.pt.mutex); - _sapp.android.is_thread_stopped = true; - pthread_cond_broadcast(&_sapp.android.pt.cond); - pthread_mutex_unlock(&_sapp.android.pt.mutex); - SOKOL_LOG("Loop thread done"); - return NULL; -} - -/* android main/ui thread */ -_SOKOL_PRIVATE void _sapp_android_msg(_sapp_android_msg_t msg) { - if (write(_sapp.android.pt.write_from_main_fd, &msg, sizeof(msg)) != sizeof(msg)) { - SOKOL_LOG("Could not write to write_from_main_fd"); - } -} - -_SOKOL_PRIVATE void _sapp_android_on_start(ANativeActivity* activity) { - SOKOL_LOG("NativeActivity onStart()"); -} - -_SOKOL_PRIVATE void _sapp_android_on_resume(ANativeActivity* activity) { - SOKOL_LOG("NativeActivity onResume()"); - _sapp_android_msg(_SOKOL_ANDROID_MSG_RESUME); -} - -_SOKOL_PRIVATE void* _sapp_android_on_save_instance_state(ANativeActivity* activity, size_t* out_size) { - SOKOL_LOG("NativeActivity onSaveInstanceState()"); - *out_size = 0; - return NULL; -} - -_SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activity, int has_focus) { - SOKOL_LOG("NativeActivity onWindowFocusChanged()"); - if (has_focus) { - _sapp_android_msg(_SOKOL_ANDROID_MSG_FOCUS); - } else { - _sapp_android_msg(_SOKOL_ANDROID_MSG_NO_FOCUS); - } -} - -_SOKOL_PRIVATE void _sapp_android_on_pause(ANativeActivity* activity) { - SOKOL_LOG("NativeActivity onPause()"); - _sapp_android_msg(_SOKOL_ANDROID_MSG_PAUSE); -} - -_SOKOL_PRIVATE void _sapp_android_on_stop(ANativeActivity* activity) { - SOKOL_LOG("NativeActivity onStop()"); -} - -_SOKOL_PRIVATE void _sapp_android_msg_set_native_window(ANativeWindow* window) { - pthread_mutex_lock(&_sapp.android.pt.mutex); - _sapp.android.pending.window = window; - _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW); - while (_sapp.android.current.window != window) { - pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); - } - pthread_mutex_unlock(&_sapp.android.pt.mutex); -} - -_SOKOL_PRIVATE void _sapp_android_on_native_window_created(ANativeActivity* activity, ANativeWindow* window) { - SOKOL_LOG("NativeActivity onNativeWindowCreated()"); - _sapp_android_msg_set_native_window(window); -} - -_SOKOL_PRIVATE void _sapp_android_on_native_window_destroyed(ANativeActivity* activity, ANativeWindow* window) { - SOKOL_LOG("NativeActivity onNativeWindowDestroyed()"); - _sapp_android_msg_set_native_window(NULL); -} - -_SOKOL_PRIVATE void _sapp_android_msg_set_input_queue(AInputQueue* input) { - pthread_mutex_lock(&_sapp.android.pt.mutex); - _sapp.android.pending.input = input; - _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_INPUT_QUEUE); - while (_sapp.android.current.input != input) { - pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); - } - pthread_mutex_unlock(&_sapp.android.pt.mutex); -} - -_SOKOL_PRIVATE void _sapp_android_on_input_queue_created(ANativeActivity* activity, AInputQueue* queue) { - SOKOL_LOG("NativeActivity onInputQueueCreated()"); - _sapp_android_msg_set_input_queue(queue); -} - -_SOKOL_PRIVATE void _sapp_android_on_input_queue_destroyed(ANativeActivity* activity, AInputQueue* queue) { - SOKOL_LOG("NativeActivity onInputQueueDestroyed()"); - _sapp_android_msg_set_input_queue(NULL); -} - -_SOKOL_PRIVATE void _sapp_android_on_config_changed(ANativeActivity* activity) { - SOKOL_LOG("NativeActivity onConfigurationChanged()"); - /* see android:configChanges in manifest */ -} - -_SOKOL_PRIVATE void _sapp_android_on_low_memory(ANativeActivity* activity) { - SOKOL_LOG("NativeActivity onLowMemory()"); -} - -_SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { - /* - * For some reason even an empty app using nativeactivity.h will crash (WIN DEATH) - * on my device (Moto X 2nd gen) when the app is removed from the task view - * (TaskStackView: onTaskViewDismissed). - * - * However, if ANativeActivity_finish() is explicitly called from for example - * _sapp_android_on_stop(), the crash disappears. Is this a bug in NativeActivity? - */ - SOKOL_LOG("NativeActivity onDestroy()"); - - /* send destroy msg */ - pthread_mutex_lock(&_sapp.android.pt.mutex); - _sapp_android_msg(_SOKOL_ANDROID_MSG_DESTROY); - while (!_sapp.android.is_thread_stopped) { - pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); - } - pthread_mutex_unlock(&_sapp.android.pt.mutex); - - /* clean up main thread */ - pthread_cond_destroy(&_sapp.android.pt.cond); - pthread_mutex_destroy(&_sapp.android.pt.mutex); - - close(_sapp.android.pt.read_from_main_fd); - close(_sapp.android.pt.write_from_main_fd); - - SOKOL_LOG("NativeActivity done"); - - /* this is a bit naughty, but causes a clean restart of the app (static globals are reset) */ - exit(0); -} - -JNIEXPORT -void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size_t saved_state_size) { - SOKOL_LOG("NativeActivity onCreate()"); - - sapp_desc desc = sokol_main(0, NULL); - _sapp_init_state(&desc); - - /* start loop thread */ - _sapp.android.activity = activity; - - int pipe_fd[2]; - if (pipe(pipe_fd) != 0) { - SOKOL_LOG("Could not create thread pipe"); - return; - } - _sapp.android.pt.read_from_main_fd = pipe_fd[0]; - _sapp.android.pt.write_from_main_fd = pipe_fd[1]; - - pthread_mutex_init(&_sapp.android.pt.mutex, NULL); - pthread_cond_init(&_sapp.android.pt.cond, NULL); - - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&_sapp.android.pt.thread, &attr, _sapp_android_loop, 0); - pthread_attr_destroy(&attr); - - /* wait until main loop has started */ - pthread_mutex_lock(&_sapp.android.pt.mutex); - while (!_sapp.android.is_thread_started) { - pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); - } - pthread_mutex_unlock(&_sapp.android.pt.mutex); - - /* send create msg */ - pthread_mutex_lock(&_sapp.android.pt.mutex); - _sapp_android_msg(_SOKOL_ANDROID_MSG_CREATE); - while (!_sapp.android.has_created) { - pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); - } - pthread_mutex_unlock(&_sapp.android.pt.mutex); - - /* register for callbacks */ - activity->callbacks->onStart = _sapp_android_on_start; - activity->callbacks->onResume = _sapp_android_on_resume; - activity->callbacks->onSaveInstanceState = _sapp_android_on_save_instance_state; - activity->callbacks->onWindowFocusChanged = _sapp_android_on_window_focus_changed; - activity->callbacks->onPause = _sapp_android_on_pause; - activity->callbacks->onStop = _sapp_android_on_stop; - activity->callbacks->onDestroy = _sapp_android_on_destroy; - activity->callbacks->onNativeWindowCreated = _sapp_android_on_native_window_created; - /* activity->callbacks->onNativeWindowResized = _sapp_android_on_native_window_resized; */ - /* activity->callbacks->onNativeWindowRedrawNeeded = _sapp_android_on_native_window_redraw_needed; */ - activity->callbacks->onNativeWindowDestroyed = _sapp_android_on_native_window_destroyed; - activity->callbacks->onInputQueueCreated = _sapp_android_on_input_queue_created; - activity->callbacks->onInputQueueDestroyed = _sapp_android_on_input_queue_destroyed; - /* activity->callbacks->onContentRectChanged = _sapp_android_on_content_rect_changed; */ - activity->callbacks->onConfigurationChanged = _sapp_android_on_config_changed; - activity->callbacks->onLowMemory = _sapp_android_on_low_memory; - - SOKOL_LOG("NativeActivity successfully created"); - - /* NOT A BUG: do NOT call sapp_discard_state() */ -} - -#endif /* _SAPP_ANDROID */ - -/*== LINUX ==================================================================*/ -#if defined(_SAPP_LINUX) - -/* see GLFW's xkb_unicode.c */ -static const struct _sapp_x11_codepair { - uint16_t keysym; - uint16_t ucs; -} _sapp_x11_keysymtab[] = { - { 0x01a1, 0x0104 }, - { 0x01a2, 0x02d8 }, - { 0x01a3, 0x0141 }, - { 0x01a5, 0x013d }, - { 0x01a6, 0x015a }, - { 0x01a9, 0x0160 }, - { 0x01aa, 0x015e }, - { 0x01ab, 0x0164 }, - { 0x01ac, 0x0179 }, - { 0x01ae, 0x017d }, - { 0x01af, 0x017b }, - { 0x01b1, 0x0105 }, - { 0x01b2, 0x02db }, - { 0x01b3, 0x0142 }, - { 0x01b5, 0x013e }, - { 0x01b6, 0x015b }, - { 0x01b7, 0x02c7 }, - { 0x01b9, 0x0161 }, - { 0x01ba, 0x015f }, - { 0x01bb, 0x0165 }, - { 0x01bc, 0x017a }, - { 0x01bd, 0x02dd }, - { 0x01be, 0x017e }, - { 0x01bf, 0x017c }, - { 0x01c0, 0x0154 }, - { 0x01c3, 0x0102 }, - { 0x01c5, 0x0139 }, - { 0x01c6, 0x0106 }, - { 0x01c8, 0x010c }, - { 0x01ca, 0x0118 }, - { 0x01cc, 0x011a }, - { 0x01cf, 0x010e }, - { 0x01d0, 0x0110 }, - { 0x01d1, 0x0143 }, - { 0x01d2, 0x0147 }, - { 0x01d5, 0x0150 }, - { 0x01d8, 0x0158 }, - { 0x01d9, 0x016e }, - { 0x01db, 0x0170 }, - { 0x01de, 0x0162 }, - { 0x01e0, 0x0155 }, - { 0x01e3, 0x0103 }, - { 0x01e5, 0x013a }, - { 0x01e6, 0x0107 }, - { 0x01e8, 0x010d }, - { 0x01ea, 0x0119 }, - { 0x01ec, 0x011b }, - { 0x01ef, 0x010f }, - { 0x01f0, 0x0111 }, - { 0x01f1, 0x0144 }, - { 0x01f2, 0x0148 }, - { 0x01f5, 0x0151 }, - { 0x01f8, 0x0159 }, - { 0x01f9, 0x016f }, - { 0x01fb, 0x0171 }, - { 0x01fe, 0x0163 }, - { 0x01ff, 0x02d9 }, - { 0x02a1, 0x0126 }, - { 0x02a6, 0x0124 }, - { 0x02a9, 0x0130 }, - { 0x02ab, 0x011e }, - { 0x02ac, 0x0134 }, - { 0x02b1, 0x0127 }, - { 0x02b6, 0x0125 }, - { 0x02b9, 0x0131 }, - { 0x02bb, 0x011f }, - { 0x02bc, 0x0135 }, - { 0x02c5, 0x010a }, - { 0x02c6, 0x0108 }, - { 0x02d5, 0x0120 }, - { 0x02d8, 0x011c }, - { 0x02dd, 0x016c }, - { 0x02de, 0x015c }, - { 0x02e5, 0x010b }, - { 0x02e6, 0x0109 }, - { 0x02f5, 0x0121 }, - { 0x02f8, 0x011d }, - { 0x02fd, 0x016d }, - { 0x02fe, 0x015d }, - { 0x03a2, 0x0138 }, - { 0x03a3, 0x0156 }, - { 0x03a5, 0x0128 }, - { 0x03a6, 0x013b }, - { 0x03aa, 0x0112 }, - { 0x03ab, 0x0122 }, - { 0x03ac, 0x0166 }, - { 0x03b3, 0x0157 }, - { 0x03b5, 0x0129 }, - { 0x03b6, 0x013c }, - { 0x03ba, 0x0113 }, - { 0x03bb, 0x0123 }, - { 0x03bc, 0x0167 }, - { 0x03bd, 0x014a }, - { 0x03bf, 0x014b }, - { 0x03c0, 0x0100 }, - { 0x03c7, 0x012e }, - { 0x03cc, 0x0116 }, - { 0x03cf, 0x012a }, - { 0x03d1, 0x0145 }, - { 0x03d2, 0x014c }, - { 0x03d3, 0x0136 }, - { 0x03d9, 0x0172 }, - { 0x03dd, 0x0168 }, - { 0x03de, 0x016a }, - { 0x03e0, 0x0101 }, - { 0x03e7, 0x012f }, - { 0x03ec, 0x0117 }, - { 0x03ef, 0x012b }, - { 0x03f1, 0x0146 }, - { 0x03f2, 0x014d }, - { 0x03f3, 0x0137 }, - { 0x03f9, 0x0173 }, - { 0x03fd, 0x0169 }, - { 0x03fe, 0x016b }, - { 0x047e, 0x203e }, - { 0x04a1, 0x3002 }, - { 0x04a2, 0x300c }, - { 0x04a3, 0x300d }, - { 0x04a4, 0x3001 }, - { 0x04a5, 0x30fb }, - { 0x04a6, 0x30f2 }, - { 0x04a7, 0x30a1 }, - { 0x04a8, 0x30a3 }, - { 0x04a9, 0x30a5 }, - { 0x04aa, 0x30a7 }, - { 0x04ab, 0x30a9 }, - { 0x04ac, 0x30e3 }, - { 0x04ad, 0x30e5 }, - { 0x04ae, 0x30e7 }, - { 0x04af, 0x30c3 }, - { 0x04b0, 0x30fc }, - { 0x04b1, 0x30a2 }, - { 0x04b2, 0x30a4 }, - { 0x04b3, 0x30a6 }, - { 0x04b4, 0x30a8 }, - { 0x04b5, 0x30aa }, - { 0x04b6, 0x30ab }, - { 0x04b7, 0x30ad }, - { 0x04b8, 0x30af }, - { 0x04b9, 0x30b1 }, - { 0x04ba, 0x30b3 }, - { 0x04bb, 0x30b5 }, - { 0x04bc, 0x30b7 }, - { 0x04bd, 0x30b9 }, - { 0x04be, 0x30bb }, - { 0x04bf, 0x30bd }, - { 0x04c0, 0x30bf }, - { 0x04c1, 0x30c1 }, - { 0x04c2, 0x30c4 }, - { 0x04c3, 0x30c6 }, - { 0x04c4, 0x30c8 }, - { 0x04c5, 0x30ca }, - { 0x04c6, 0x30cb }, - { 0x04c7, 0x30cc }, - { 0x04c8, 0x30cd }, - { 0x04c9, 0x30ce }, - { 0x04ca, 0x30cf }, - { 0x04cb, 0x30d2 }, - { 0x04cc, 0x30d5 }, - { 0x04cd, 0x30d8 }, - { 0x04ce, 0x30db }, - { 0x04cf, 0x30de }, - { 0x04d0, 0x30df }, - { 0x04d1, 0x30e0 }, - { 0x04d2, 0x30e1 }, - { 0x04d3, 0x30e2 }, - { 0x04d4, 0x30e4 }, - { 0x04d5, 0x30e6 }, - { 0x04d6, 0x30e8 }, - { 0x04d7, 0x30e9 }, - { 0x04d8, 0x30ea }, - { 0x04d9, 0x30eb }, - { 0x04da, 0x30ec }, - { 0x04db, 0x30ed }, - { 0x04dc, 0x30ef }, - { 0x04dd, 0x30f3 }, - { 0x04de, 0x309b }, - { 0x04df, 0x309c }, - { 0x05ac, 0x060c }, - { 0x05bb, 0x061b }, - { 0x05bf, 0x061f }, - { 0x05c1, 0x0621 }, - { 0x05c2, 0x0622 }, - { 0x05c3, 0x0623 }, - { 0x05c4, 0x0624 }, - { 0x05c5, 0x0625 }, - { 0x05c6, 0x0626 }, - { 0x05c7, 0x0627 }, - { 0x05c8, 0x0628 }, - { 0x05c9, 0x0629 }, - { 0x05ca, 0x062a }, - { 0x05cb, 0x062b }, - { 0x05cc, 0x062c }, - { 0x05cd, 0x062d }, - { 0x05ce, 0x062e }, - { 0x05cf, 0x062f }, - { 0x05d0, 0x0630 }, - { 0x05d1, 0x0631 }, - { 0x05d2, 0x0632 }, - { 0x05d3, 0x0633 }, - { 0x05d4, 0x0634 }, - { 0x05d5, 0x0635 }, - { 0x05d6, 0x0636 }, - { 0x05d7, 0x0637 }, - { 0x05d8, 0x0638 }, - { 0x05d9, 0x0639 }, - { 0x05da, 0x063a }, - { 0x05e0, 0x0640 }, - { 0x05e1, 0x0641 }, - { 0x05e2, 0x0642 }, - { 0x05e3, 0x0643 }, - { 0x05e4, 0x0644 }, - { 0x05e5, 0x0645 }, - { 0x05e6, 0x0646 }, - { 0x05e7, 0x0647 }, - { 0x05e8, 0x0648 }, - { 0x05e9, 0x0649 }, - { 0x05ea, 0x064a }, - { 0x05eb, 0x064b }, - { 0x05ec, 0x064c }, - { 0x05ed, 0x064d }, - { 0x05ee, 0x064e }, - { 0x05ef, 0x064f }, - { 0x05f0, 0x0650 }, - { 0x05f1, 0x0651 }, - { 0x05f2, 0x0652 }, - { 0x06a1, 0x0452 }, - { 0x06a2, 0x0453 }, - { 0x06a3, 0x0451 }, - { 0x06a4, 0x0454 }, - { 0x06a5, 0x0455 }, - { 0x06a6, 0x0456 }, - { 0x06a7, 0x0457 }, - { 0x06a8, 0x0458 }, - { 0x06a9, 0x0459 }, - { 0x06aa, 0x045a }, - { 0x06ab, 0x045b }, - { 0x06ac, 0x045c }, - { 0x06ae, 0x045e }, - { 0x06af, 0x045f }, - { 0x06b0, 0x2116 }, - { 0x06b1, 0x0402 }, - { 0x06b2, 0x0403 }, - { 0x06b3, 0x0401 }, - { 0x06b4, 0x0404 }, - { 0x06b5, 0x0405 }, - { 0x06b6, 0x0406 }, - { 0x06b7, 0x0407 }, - { 0x06b8, 0x0408 }, - { 0x06b9, 0x0409 }, - { 0x06ba, 0x040a }, - { 0x06bb, 0x040b }, - { 0x06bc, 0x040c }, - { 0x06be, 0x040e }, - { 0x06bf, 0x040f }, - { 0x06c0, 0x044e }, - { 0x06c1, 0x0430 }, - { 0x06c2, 0x0431 }, - { 0x06c3, 0x0446 }, - { 0x06c4, 0x0434 }, - { 0x06c5, 0x0435 }, - { 0x06c6, 0x0444 }, - { 0x06c7, 0x0433 }, - { 0x06c8, 0x0445 }, - { 0x06c9, 0x0438 }, - { 0x06ca, 0x0439 }, - { 0x06cb, 0x043a }, - { 0x06cc, 0x043b }, - { 0x06cd, 0x043c }, - { 0x06ce, 0x043d }, - { 0x06cf, 0x043e }, - { 0x06d0, 0x043f }, - { 0x06d1, 0x044f }, - { 0x06d2, 0x0440 }, - { 0x06d3, 0x0441 }, - { 0x06d4, 0x0442 }, - { 0x06d5, 0x0443 }, - { 0x06d6, 0x0436 }, - { 0x06d7, 0x0432 }, - { 0x06d8, 0x044c }, - { 0x06d9, 0x044b }, - { 0x06da, 0x0437 }, - { 0x06db, 0x0448 }, - { 0x06dc, 0x044d }, - { 0x06dd, 0x0449 }, - { 0x06de, 0x0447 }, - { 0x06df, 0x044a }, - { 0x06e0, 0x042e }, - { 0x06e1, 0x0410 }, - { 0x06e2, 0x0411 }, - { 0x06e3, 0x0426 }, - { 0x06e4, 0x0414 }, - { 0x06e5, 0x0415 }, - { 0x06e6, 0x0424 }, - { 0x06e7, 0x0413 }, - { 0x06e8, 0x0425 }, - { 0x06e9, 0x0418 }, - { 0x06ea, 0x0419 }, - { 0x06eb, 0x041a }, - { 0x06ec, 0x041b }, - { 0x06ed, 0x041c }, - { 0x06ee, 0x041d }, - { 0x06ef, 0x041e }, - { 0x06f0, 0x041f }, - { 0x06f1, 0x042f }, - { 0x06f2, 0x0420 }, - { 0x06f3, 0x0421 }, - { 0x06f4, 0x0422 }, - { 0x06f5, 0x0423 }, - { 0x06f6, 0x0416 }, - { 0x06f7, 0x0412 }, - { 0x06f8, 0x042c }, - { 0x06f9, 0x042b }, - { 0x06fa, 0x0417 }, - { 0x06fb, 0x0428 }, - { 0x06fc, 0x042d }, - { 0x06fd, 0x0429 }, - { 0x06fe, 0x0427 }, - { 0x06ff, 0x042a }, - { 0x07a1, 0x0386 }, - { 0x07a2, 0x0388 }, - { 0x07a3, 0x0389 }, - { 0x07a4, 0x038a }, - { 0x07a5, 0x03aa }, - { 0x07a7, 0x038c }, - { 0x07a8, 0x038e }, - { 0x07a9, 0x03ab }, - { 0x07ab, 0x038f }, - { 0x07ae, 0x0385 }, - { 0x07af, 0x2015 }, - { 0x07b1, 0x03ac }, - { 0x07b2, 0x03ad }, - { 0x07b3, 0x03ae }, - { 0x07b4, 0x03af }, - { 0x07b5, 0x03ca }, - { 0x07b6, 0x0390 }, - { 0x07b7, 0x03cc }, - { 0x07b8, 0x03cd }, - { 0x07b9, 0x03cb }, - { 0x07ba, 0x03b0 }, - { 0x07bb, 0x03ce }, - { 0x07c1, 0x0391 }, - { 0x07c2, 0x0392 }, - { 0x07c3, 0x0393 }, - { 0x07c4, 0x0394 }, - { 0x07c5, 0x0395 }, - { 0x07c6, 0x0396 }, - { 0x07c7, 0x0397 }, - { 0x07c8, 0x0398 }, - { 0x07c9, 0x0399 }, - { 0x07ca, 0x039a }, - { 0x07cb, 0x039b }, - { 0x07cc, 0x039c }, - { 0x07cd, 0x039d }, - { 0x07ce, 0x039e }, - { 0x07cf, 0x039f }, - { 0x07d0, 0x03a0 }, - { 0x07d1, 0x03a1 }, - { 0x07d2, 0x03a3 }, - { 0x07d4, 0x03a4 }, - { 0x07d5, 0x03a5 }, - { 0x07d6, 0x03a6 }, - { 0x07d7, 0x03a7 }, - { 0x07d8, 0x03a8 }, - { 0x07d9, 0x03a9 }, - { 0x07e1, 0x03b1 }, - { 0x07e2, 0x03b2 }, - { 0x07e3, 0x03b3 }, - { 0x07e4, 0x03b4 }, - { 0x07e5, 0x03b5 }, - { 0x07e6, 0x03b6 }, - { 0x07e7, 0x03b7 }, - { 0x07e8, 0x03b8 }, - { 0x07e9, 0x03b9 }, - { 0x07ea, 0x03ba }, - { 0x07eb, 0x03bb }, - { 0x07ec, 0x03bc }, - { 0x07ed, 0x03bd }, - { 0x07ee, 0x03be }, - { 0x07ef, 0x03bf }, - { 0x07f0, 0x03c0 }, - { 0x07f1, 0x03c1 }, - { 0x07f2, 0x03c3 }, - { 0x07f3, 0x03c2 }, - { 0x07f4, 0x03c4 }, - { 0x07f5, 0x03c5 }, - { 0x07f6, 0x03c6 }, - { 0x07f7, 0x03c7 }, - { 0x07f8, 0x03c8 }, - { 0x07f9, 0x03c9 }, - { 0x08a1, 0x23b7 }, - { 0x08a2, 0x250c }, - { 0x08a3, 0x2500 }, - { 0x08a4, 0x2320 }, - { 0x08a5, 0x2321 }, - { 0x08a6, 0x2502 }, - { 0x08a7, 0x23a1 }, - { 0x08a8, 0x23a3 }, - { 0x08a9, 0x23a4 }, - { 0x08aa, 0x23a6 }, - { 0x08ab, 0x239b }, - { 0x08ac, 0x239d }, - { 0x08ad, 0x239e }, - { 0x08ae, 0x23a0 }, - { 0x08af, 0x23a8 }, - { 0x08b0, 0x23ac }, - { 0x08bc, 0x2264 }, - { 0x08bd, 0x2260 }, - { 0x08be, 0x2265 }, - { 0x08bf, 0x222b }, - { 0x08c0, 0x2234 }, - { 0x08c1, 0x221d }, - { 0x08c2, 0x221e }, - { 0x08c5, 0x2207 }, - { 0x08c8, 0x223c }, - { 0x08c9, 0x2243 }, - { 0x08cd, 0x21d4 }, - { 0x08ce, 0x21d2 }, - { 0x08cf, 0x2261 }, - { 0x08d6, 0x221a }, - { 0x08da, 0x2282 }, - { 0x08db, 0x2283 }, - { 0x08dc, 0x2229 }, - { 0x08dd, 0x222a }, - { 0x08de, 0x2227 }, - { 0x08df, 0x2228 }, - { 0x08ef, 0x2202 }, - { 0x08f6, 0x0192 }, - { 0x08fb, 0x2190 }, - { 0x08fc, 0x2191 }, - { 0x08fd, 0x2192 }, - { 0x08fe, 0x2193 }, - { 0x09e0, 0x25c6 }, - { 0x09e1, 0x2592 }, - { 0x09e2, 0x2409 }, - { 0x09e3, 0x240c }, - { 0x09e4, 0x240d }, - { 0x09e5, 0x240a }, - { 0x09e8, 0x2424 }, - { 0x09e9, 0x240b }, - { 0x09ea, 0x2518 }, - { 0x09eb, 0x2510 }, - { 0x09ec, 0x250c }, - { 0x09ed, 0x2514 }, - { 0x09ee, 0x253c }, - { 0x09ef, 0x23ba }, - { 0x09f0, 0x23bb }, - { 0x09f1, 0x2500 }, - { 0x09f2, 0x23bc }, - { 0x09f3, 0x23bd }, - { 0x09f4, 0x251c }, - { 0x09f5, 0x2524 }, - { 0x09f6, 0x2534 }, - { 0x09f7, 0x252c }, - { 0x09f8, 0x2502 }, - { 0x0aa1, 0x2003 }, - { 0x0aa2, 0x2002 }, - { 0x0aa3, 0x2004 }, - { 0x0aa4, 0x2005 }, - { 0x0aa5, 0x2007 }, - { 0x0aa6, 0x2008 }, - { 0x0aa7, 0x2009 }, - { 0x0aa8, 0x200a }, - { 0x0aa9, 0x2014 }, - { 0x0aaa, 0x2013 }, - { 0x0aae, 0x2026 }, - { 0x0aaf, 0x2025 }, - { 0x0ab0, 0x2153 }, - { 0x0ab1, 0x2154 }, - { 0x0ab2, 0x2155 }, - { 0x0ab3, 0x2156 }, - { 0x0ab4, 0x2157 }, - { 0x0ab5, 0x2158 }, - { 0x0ab6, 0x2159 }, - { 0x0ab7, 0x215a }, - { 0x0ab8, 0x2105 }, - { 0x0abb, 0x2012 }, - { 0x0abc, 0x2329 }, - { 0x0abe, 0x232a }, - { 0x0ac3, 0x215b }, - { 0x0ac4, 0x215c }, - { 0x0ac5, 0x215d }, - { 0x0ac6, 0x215e }, - { 0x0ac9, 0x2122 }, - { 0x0aca, 0x2613 }, - { 0x0acc, 0x25c1 }, - { 0x0acd, 0x25b7 }, - { 0x0ace, 0x25cb }, - { 0x0acf, 0x25af }, - { 0x0ad0, 0x2018 }, - { 0x0ad1, 0x2019 }, - { 0x0ad2, 0x201c }, - { 0x0ad3, 0x201d }, - { 0x0ad4, 0x211e }, - { 0x0ad6, 0x2032 }, - { 0x0ad7, 0x2033 }, - { 0x0ad9, 0x271d }, - { 0x0adb, 0x25ac }, - { 0x0adc, 0x25c0 }, - { 0x0add, 0x25b6 }, - { 0x0ade, 0x25cf }, - { 0x0adf, 0x25ae }, - { 0x0ae0, 0x25e6 }, - { 0x0ae1, 0x25ab }, - { 0x0ae2, 0x25ad }, - { 0x0ae3, 0x25b3 }, - { 0x0ae4, 0x25bd }, - { 0x0ae5, 0x2606 }, - { 0x0ae6, 0x2022 }, - { 0x0ae7, 0x25aa }, - { 0x0ae8, 0x25b2 }, - { 0x0ae9, 0x25bc }, - { 0x0aea, 0x261c }, - { 0x0aeb, 0x261e }, - { 0x0aec, 0x2663 }, - { 0x0aed, 0x2666 }, - { 0x0aee, 0x2665 }, - { 0x0af0, 0x2720 }, - { 0x0af1, 0x2020 }, - { 0x0af2, 0x2021 }, - { 0x0af3, 0x2713 }, - { 0x0af4, 0x2717 }, - { 0x0af5, 0x266f }, - { 0x0af6, 0x266d }, - { 0x0af7, 0x2642 }, - { 0x0af8, 0x2640 }, - { 0x0af9, 0x260e }, - { 0x0afa, 0x2315 }, - { 0x0afb, 0x2117 }, - { 0x0afc, 0x2038 }, - { 0x0afd, 0x201a }, - { 0x0afe, 0x201e }, - { 0x0ba3, 0x003c }, - { 0x0ba6, 0x003e }, - { 0x0ba8, 0x2228 }, - { 0x0ba9, 0x2227 }, - { 0x0bc0, 0x00af }, - { 0x0bc2, 0x22a5 }, - { 0x0bc3, 0x2229 }, - { 0x0bc4, 0x230a }, - { 0x0bc6, 0x005f }, - { 0x0bca, 0x2218 }, - { 0x0bcc, 0x2395 }, - { 0x0bce, 0x22a4 }, - { 0x0bcf, 0x25cb }, - { 0x0bd3, 0x2308 }, - { 0x0bd6, 0x222a }, - { 0x0bd8, 0x2283 }, - { 0x0bda, 0x2282 }, - { 0x0bdc, 0x22a2 }, - { 0x0bfc, 0x22a3 }, - { 0x0cdf, 0x2017 }, - { 0x0ce0, 0x05d0 }, - { 0x0ce1, 0x05d1 }, - { 0x0ce2, 0x05d2 }, - { 0x0ce3, 0x05d3 }, - { 0x0ce4, 0x05d4 }, - { 0x0ce5, 0x05d5 }, - { 0x0ce6, 0x05d6 }, - { 0x0ce7, 0x05d7 }, - { 0x0ce8, 0x05d8 }, - { 0x0ce9, 0x05d9 }, - { 0x0cea, 0x05da }, - { 0x0ceb, 0x05db }, - { 0x0cec, 0x05dc }, - { 0x0ced, 0x05dd }, - { 0x0cee, 0x05de }, - { 0x0cef, 0x05df }, - { 0x0cf0, 0x05e0 }, - { 0x0cf1, 0x05e1 }, - { 0x0cf2, 0x05e2 }, - { 0x0cf3, 0x05e3 }, - { 0x0cf4, 0x05e4 }, - { 0x0cf5, 0x05e5 }, - { 0x0cf6, 0x05e6 }, - { 0x0cf7, 0x05e7 }, - { 0x0cf8, 0x05e8 }, - { 0x0cf9, 0x05e9 }, - { 0x0cfa, 0x05ea }, - { 0x0da1, 0x0e01 }, - { 0x0da2, 0x0e02 }, - { 0x0da3, 0x0e03 }, - { 0x0da4, 0x0e04 }, - { 0x0da5, 0x0e05 }, - { 0x0da6, 0x0e06 }, - { 0x0da7, 0x0e07 }, - { 0x0da8, 0x0e08 }, - { 0x0da9, 0x0e09 }, - { 0x0daa, 0x0e0a }, - { 0x0dab, 0x0e0b }, - { 0x0dac, 0x0e0c }, - { 0x0dad, 0x0e0d }, - { 0x0dae, 0x0e0e }, - { 0x0daf, 0x0e0f }, - { 0x0db0, 0x0e10 }, - { 0x0db1, 0x0e11 }, - { 0x0db2, 0x0e12 }, - { 0x0db3, 0x0e13 }, - { 0x0db4, 0x0e14 }, - { 0x0db5, 0x0e15 }, - { 0x0db6, 0x0e16 }, - { 0x0db7, 0x0e17 }, - { 0x0db8, 0x0e18 }, - { 0x0db9, 0x0e19 }, - { 0x0dba, 0x0e1a }, - { 0x0dbb, 0x0e1b }, - { 0x0dbc, 0x0e1c }, - { 0x0dbd, 0x0e1d }, - { 0x0dbe, 0x0e1e }, - { 0x0dbf, 0x0e1f }, - { 0x0dc0, 0x0e20 }, - { 0x0dc1, 0x0e21 }, - { 0x0dc2, 0x0e22 }, - { 0x0dc3, 0x0e23 }, - { 0x0dc4, 0x0e24 }, - { 0x0dc5, 0x0e25 }, - { 0x0dc6, 0x0e26 }, - { 0x0dc7, 0x0e27 }, - { 0x0dc8, 0x0e28 }, - { 0x0dc9, 0x0e29 }, - { 0x0dca, 0x0e2a }, - { 0x0dcb, 0x0e2b }, - { 0x0dcc, 0x0e2c }, - { 0x0dcd, 0x0e2d }, - { 0x0dce, 0x0e2e }, - { 0x0dcf, 0x0e2f }, - { 0x0dd0, 0x0e30 }, - { 0x0dd1, 0x0e31 }, - { 0x0dd2, 0x0e32 }, - { 0x0dd3, 0x0e33 }, - { 0x0dd4, 0x0e34 }, - { 0x0dd5, 0x0e35 }, - { 0x0dd6, 0x0e36 }, - { 0x0dd7, 0x0e37 }, - { 0x0dd8, 0x0e38 }, - { 0x0dd9, 0x0e39 }, - { 0x0dda, 0x0e3a }, - { 0x0ddf, 0x0e3f }, - { 0x0de0, 0x0e40 }, - { 0x0de1, 0x0e41 }, - { 0x0de2, 0x0e42 }, - { 0x0de3, 0x0e43 }, - { 0x0de4, 0x0e44 }, - { 0x0de5, 0x0e45 }, - { 0x0de6, 0x0e46 }, - { 0x0de7, 0x0e47 }, - { 0x0de8, 0x0e48 }, - { 0x0de9, 0x0e49 }, - { 0x0dea, 0x0e4a }, - { 0x0deb, 0x0e4b }, - { 0x0dec, 0x0e4c }, - { 0x0ded, 0x0e4d }, - { 0x0df0, 0x0e50 }, - { 0x0df1, 0x0e51 }, - { 0x0df2, 0x0e52 }, - { 0x0df3, 0x0e53 }, - { 0x0df4, 0x0e54 }, - { 0x0df5, 0x0e55 }, - { 0x0df6, 0x0e56 }, - { 0x0df7, 0x0e57 }, - { 0x0df8, 0x0e58 }, - { 0x0df9, 0x0e59 }, - { 0x0ea1, 0x3131 }, - { 0x0ea2, 0x3132 }, - { 0x0ea3, 0x3133 }, - { 0x0ea4, 0x3134 }, - { 0x0ea5, 0x3135 }, - { 0x0ea6, 0x3136 }, - { 0x0ea7, 0x3137 }, - { 0x0ea8, 0x3138 }, - { 0x0ea9, 0x3139 }, - { 0x0eaa, 0x313a }, - { 0x0eab, 0x313b }, - { 0x0eac, 0x313c }, - { 0x0ead, 0x313d }, - { 0x0eae, 0x313e }, - { 0x0eaf, 0x313f }, - { 0x0eb0, 0x3140 }, - { 0x0eb1, 0x3141 }, - { 0x0eb2, 0x3142 }, - { 0x0eb3, 0x3143 }, - { 0x0eb4, 0x3144 }, - { 0x0eb5, 0x3145 }, - { 0x0eb6, 0x3146 }, - { 0x0eb7, 0x3147 }, - { 0x0eb8, 0x3148 }, - { 0x0eb9, 0x3149 }, - { 0x0eba, 0x314a }, - { 0x0ebb, 0x314b }, - { 0x0ebc, 0x314c }, - { 0x0ebd, 0x314d }, - { 0x0ebe, 0x314e }, - { 0x0ebf, 0x314f }, - { 0x0ec0, 0x3150 }, - { 0x0ec1, 0x3151 }, - { 0x0ec2, 0x3152 }, - { 0x0ec3, 0x3153 }, - { 0x0ec4, 0x3154 }, - { 0x0ec5, 0x3155 }, - { 0x0ec6, 0x3156 }, - { 0x0ec7, 0x3157 }, - { 0x0ec8, 0x3158 }, - { 0x0ec9, 0x3159 }, - { 0x0eca, 0x315a }, - { 0x0ecb, 0x315b }, - { 0x0ecc, 0x315c }, - { 0x0ecd, 0x315d }, - { 0x0ece, 0x315e }, - { 0x0ecf, 0x315f }, - { 0x0ed0, 0x3160 }, - { 0x0ed1, 0x3161 }, - { 0x0ed2, 0x3162 }, - { 0x0ed3, 0x3163 }, - { 0x0ed4, 0x11a8 }, - { 0x0ed5, 0x11a9 }, - { 0x0ed6, 0x11aa }, - { 0x0ed7, 0x11ab }, - { 0x0ed8, 0x11ac }, - { 0x0ed9, 0x11ad }, - { 0x0eda, 0x11ae }, - { 0x0edb, 0x11af }, - { 0x0edc, 0x11b0 }, - { 0x0edd, 0x11b1 }, - { 0x0ede, 0x11b2 }, - { 0x0edf, 0x11b3 }, - { 0x0ee0, 0x11b4 }, - { 0x0ee1, 0x11b5 }, - { 0x0ee2, 0x11b6 }, - { 0x0ee3, 0x11b7 }, - { 0x0ee4, 0x11b8 }, - { 0x0ee5, 0x11b9 }, - { 0x0ee6, 0x11ba }, - { 0x0ee7, 0x11bb }, - { 0x0ee8, 0x11bc }, - { 0x0ee9, 0x11bd }, - { 0x0eea, 0x11be }, - { 0x0eeb, 0x11bf }, - { 0x0eec, 0x11c0 }, - { 0x0eed, 0x11c1 }, - { 0x0eee, 0x11c2 }, - { 0x0eef, 0x316d }, - { 0x0ef0, 0x3171 }, - { 0x0ef1, 0x3178 }, - { 0x0ef2, 0x317f }, - { 0x0ef3, 0x3181 }, - { 0x0ef4, 0x3184 }, - { 0x0ef5, 0x3186 }, - { 0x0ef6, 0x318d }, - { 0x0ef7, 0x318e }, - { 0x0ef8, 0x11eb }, - { 0x0ef9, 0x11f0 }, - { 0x0efa, 0x11f9 }, - { 0x0eff, 0x20a9 }, - { 0x13a4, 0x20ac }, - { 0x13bc, 0x0152 }, - { 0x13bd, 0x0153 }, - { 0x13be, 0x0178 }, - { 0x20ac, 0x20ac }, - { 0xfe50, '`' }, - { 0xfe51, 0x00b4 }, - { 0xfe52, '^' }, - { 0xfe53, '~' }, - { 0xfe54, 0x00af }, - { 0xfe55, 0x02d8 }, - { 0xfe56, 0x02d9 }, - { 0xfe57, 0x00a8 }, - { 0xfe58, 0x02da }, - { 0xfe59, 0x02dd }, - { 0xfe5a, 0x02c7 }, - { 0xfe5b, 0x00b8 }, - { 0xfe5c, 0x02db }, - { 0xfe5d, 0x037a }, - { 0xfe5e, 0x309b }, - { 0xfe5f, 0x309c }, - { 0xfe63, '/' }, - { 0xfe64, 0x02bc }, - { 0xfe65, 0x02bd }, - { 0xfe66, 0x02f5 }, - { 0xfe67, 0x02f3 }, - { 0xfe68, 0x02cd }, - { 0xfe69, 0xa788 }, - { 0xfe6a, 0x02f7 }, - { 0xfe6e, ',' }, - { 0xfe6f, 0x00a4 }, - { 0xfe80, 'a' }, /* XK_dead_a */ - { 0xfe81, 'A' }, /* XK_dead_A */ - { 0xfe82, 'e' }, /* XK_dead_e */ - { 0xfe83, 'E' }, /* XK_dead_E */ - { 0xfe84, 'i' }, /* XK_dead_i */ - { 0xfe85, 'I' }, /* XK_dead_I */ - { 0xfe86, 'o' }, /* XK_dead_o */ - { 0xfe87, 'O' }, /* XK_dead_O */ - { 0xfe88, 'u' }, /* XK_dead_u */ - { 0xfe89, 'U' }, /* XK_dead_U */ - { 0xfe8a, 0x0259 }, - { 0xfe8b, 0x018f }, - { 0xfe8c, 0x00b5 }, - { 0xfe90, '_' }, - { 0xfe91, 0x02c8 }, - { 0xfe92, 0x02cc }, - { 0xff80 /*XKB_KEY_KP_Space*/, ' ' }, - { 0xff95 /*XKB_KEY_KP_7*/, 0x0037 }, - { 0xff96 /*XKB_KEY_KP_4*/, 0x0034 }, - { 0xff97 /*XKB_KEY_KP_8*/, 0x0038 }, - { 0xff98 /*XKB_KEY_KP_6*/, 0x0036 }, - { 0xff99 /*XKB_KEY_KP_2*/, 0x0032 }, - { 0xff9a /*XKB_KEY_KP_9*/, 0x0039 }, - { 0xff9b /*XKB_KEY_KP_3*/, 0x0033 }, - { 0xff9c /*XKB_KEY_KP_1*/, 0x0031 }, - { 0xff9d /*XKB_KEY_KP_5*/, 0x0035 }, - { 0xff9e /*XKB_KEY_KP_0*/, 0x0030 }, - { 0xffaa /*XKB_KEY_KP_Multiply*/, '*' }, - { 0xffab /*XKB_KEY_KP_Add*/, '+' }, - { 0xffac /*XKB_KEY_KP_Separator*/, ',' }, - { 0xffad /*XKB_KEY_KP_Subtract*/, '-' }, - { 0xffae /*XKB_KEY_KP_Decimal*/, '.' }, - { 0xffaf /*XKB_KEY_KP_Divide*/, '/' }, - { 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 }, - { 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 }, - { 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 }, - { 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 }, - { 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 }, - { 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 }, - { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 }, - { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 }, - { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 }, - { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 }, - { 0xffbd /*XKB_KEY_KP_Equal*/, '=' } -}; - -_SOKOL_PRIVATE int _sapp_x11_error_handler(Display* display, XErrorEvent* event) { - _SOKOL_UNUSED(display); - _sapp.x11.error_code = event->error_code; - return 0; -} - -_SOKOL_PRIVATE void _sapp_x11_grab_error_handler(void) { - _sapp.x11.error_code = Success; - XSetErrorHandler(_sapp_x11_error_handler); -} - -_SOKOL_PRIVATE void _sapp_x11_release_error_handler(void) { - XSync(_sapp.x11.display, False); - XSetErrorHandler(NULL); -} - -_SOKOL_PRIVATE void _sapp_x11_init_extensions(void) { - _sapp.x11.UTF8_STRING = XInternAtom(_sapp.x11.display, "UTF8_STRING", False); - _sapp.x11.WM_PROTOCOLS = XInternAtom(_sapp.x11.display, "WM_PROTOCOLS", False); - _sapp.x11.WM_DELETE_WINDOW = XInternAtom(_sapp.x11.display, "WM_DELETE_WINDOW", False); - _sapp.x11.WM_STATE = XInternAtom(_sapp.x11.display, "WM_STATE", False); - _sapp.x11.NET_WM_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_NAME", False); - _sapp.x11.NET_WM_ICON_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_ICON_NAME", False); - _sapp.x11.NET_WM_ICON = XInternAtom(_sapp.x11.display, "_NET_WM_ICON", False); - _sapp.x11.NET_WM_STATE = XInternAtom(_sapp.x11.display, "_NET_WM_STATE", False); - _sapp.x11.NET_WM_STATE_FULLSCREEN = XInternAtom(_sapp.x11.display, "_NET_WM_STATE_FULLSCREEN", False); - if (_sapp.drop.enabled) { - _sapp.x11.xdnd.XdndAware = XInternAtom(_sapp.x11.display, "XdndAware", False); - _sapp.x11.xdnd.XdndEnter = XInternAtom(_sapp.x11.display, "XdndEnter", False); - _sapp.x11.xdnd.XdndPosition = XInternAtom(_sapp.x11.display, "XdndPosition", False); - _sapp.x11.xdnd.XdndStatus = XInternAtom(_sapp.x11.display, "XdndStatus", False); - _sapp.x11.xdnd.XdndActionCopy = XInternAtom(_sapp.x11.display, "XdndActionCopy", False); - _sapp.x11.xdnd.XdndDrop = XInternAtom(_sapp.x11.display, "XdndDrop", False); - _sapp.x11.xdnd.XdndFinished = XInternAtom(_sapp.x11.display, "XdndFinished", False); - _sapp.x11.xdnd.XdndSelection = XInternAtom(_sapp.x11.display, "XdndSelection", False); - _sapp.x11.xdnd.XdndTypeList = XInternAtom(_sapp.x11.display, "XdndTypeList", False); - _sapp.x11.xdnd.text_uri_list = XInternAtom(_sapp.x11.display, "text/uri-list", False); - } - - /* check Xi extension for raw mouse input */ - if (XQueryExtension(_sapp.x11.display, "XInputExtension", &_sapp.x11.xi.major_opcode, &_sapp.x11.xi.event_base, &_sapp.x11.xi.error_base)) { - _sapp.x11.xi.major = 2; - _sapp.x11.xi.minor = 0; - if (XIQueryVersion(_sapp.x11.display, &_sapp.x11.xi.major, &_sapp.x11.xi.minor) == Success) { - _sapp.x11.xi.available = true; - } - } -} - -_SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) { - /* from GLFW: - - NOTE: Default to the display-wide DPI as we don't currently have a policy - for which monitor a window is considered to be on - - _sapp.x11.dpi = DisplayWidth(_sapp.x11.display, _sapp.x11.screen) * - 25.4f / DisplayWidthMM(_sapp.x11.display, _sapp.x11.screen); - - NOTE: Basing the scale on Xft.dpi where available should provide the most - consistent user experience (matches Qt, Gtk, etc), although not - always the most accurate one - */ - char* rms = XResourceManagerString(_sapp.x11.display); - if (rms) { - XrmDatabase db = XrmGetStringDatabase(rms); - if (db) { - XrmValue value; - char* type = NULL; - if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) { - if (type && strcmp(type, "String") == 0) { - _sapp.x11.dpi = atof(value.addr); - } - } - XrmDestroyDatabase(db); - } - } -} - -_SOKOL_PRIVATE bool _sapp_glx_has_ext(const char* ext, const char* extensions) { - SOKOL_ASSERT(ext); - const char* start = extensions; - while (true) { - const char* where = strstr(start, ext); - if (!where) { - return false; - } - const char* terminator = where + strlen(ext); - if ((where == start) || (*(where - 1) == ' ')) { - if (*terminator == ' ' || *terminator == '\0') { - break; - } - } - start = terminator; - } - return true; -} - -_SOKOL_PRIVATE bool _sapp_glx_extsupported(const char* ext, const char* extensions) { - if (extensions) { - return _sapp_glx_has_ext(ext, extensions); - } - else { - return false; - } -} - -_SOKOL_PRIVATE void* _sapp_glx_getprocaddr(const char* procname) -{ - if (_sapp.glx.GetProcAddress) { - return (void*) _sapp.glx.GetProcAddress(procname); - } - else if (_sapp.glx.GetProcAddressARB) { - return (void*) _sapp.glx.GetProcAddressARB(procname); - } - else { - return dlsym(_sapp.glx.libgl, procname); - } -} - -_SOKOL_PRIVATE void _sapp_glx_init() { - const char* sonames[] = { "libGL.so.1", "libGL.so", 0 }; - for (int i = 0; sonames[i]; i++) { - _sapp.glx.libgl = dlopen(sonames[i], RTLD_LAZY|RTLD_GLOBAL); - if (_sapp.glx.libgl) { - break; - } - } - if (!_sapp.glx.libgl) { - _sapp_fail("GLX: failed to load libGL"); - } - _sapp.glx.GetFBConfigs = (PFNGLXGETFBCONFIGSPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigs"); - _sapp.glx.GetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigAttrib"); - _sapp.glx.GetClientString = (PFNGLXGETCLIENTSTRINGPROC) dlsym(_sapp.glx.libgl, "glXGetClientString"); - _sapp.glx.QueryExtension = (PFNGLXQUERYEXTENSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryExtension"); - _sapp.glx.QueryVersion = (PFNGLXQUERYVERSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryVersion"); - _sapp.glx.DestroyContext = (PFNGLXDESTROYCONTEXTPROC) dlsym(_sapp.glx.libgl, "glXDestroyContext"); - _sapp.glx.MakeCurrent = (PFNGLXMAKECURRENTPROC) dlsym(_sapp.glx.libgl, "glXMakeCurrent"); - _sapp.glx.SwapBuffers = (PFNGLXSWAPBUFFERSPROC) dlsym(_sapp.glx.libgl, "glXSwapBuffers"); - _sapp.glx.QueryExtensionsString = (PFNGLXQUERYEXTENSIONSSTRINGPROC) dlsym(_sapp.glx.libgl, "glXQueryExtensionsString"); - _sapp.glx.CreateWindow = (PFNGLXCREATEWINDOWPROC) dlsym(_sapp.glx.libgl, "glXCreateWindow"); - _sapp.glx.DestroyWindow = (PFNGLXDESTROYWINDOWPROC) dlsym(_sapp.glx.libgl, "glXDestroyWindow"); - _sapp.glx.GetProcAddress = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddress"); - _sapp.glx.GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddressARB"); - _sapp.glx.GetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) dlsym(_sapp.glx.libgl, "glXGetVisualFromFBConfig"); - if (!_sapp.glx.GetFBConfigs || - !_sapp.glx.GetFBConfigAttrib || - !_sapp.glx.GetClientString || - !_sapp.glx.QueryExtension || - !_sapp.glx.QueryVersion || - !_sapp.glx.DestroyContext || - !_sapp.glx.MakeCurrent || - !_sapp.glx.SwapBuffers || - !_sapp.glx.QueryExtensionsString || - !_sapp.glx.CreateWindow || - !_sapp.glx.DestroyWindow || - !_sapp.glx.GetProcAddress || - !_sapp.glx.GetProcAddressARB || - !_sapp.glx.GetVisualFromFBConfig) - { - _sapp_fail("GLX: failed to load required entry points"); - } - - if (!_sapp.glx.QueryExtension(_sapp.x11.display, &_sapp.glx.error_base, &_sapp.glx.event_base)) { - _sapp_fail("GLX: GLX extension not found"); - } - if (!_sapp.glx.QueryVersion(_sapp.x11.display, &_sapp.glx.major, &_sapp.glx.minor)) { - _sapp_fail("GLX: Failed to query GLX version"); - } - if (_sapp.glx.major == 1 && _sapp.glx.minor < 3) { - _sapp_fail("GLX: GLX version 1.3 is required"); - } - const char* exts = _sapp.glx.QueryExtensionsString(_sapp.x11.display, _sapp.x11.screen); - if (_sapp_glx_extsupported("GLX_EXT_swap_control", exts)) { - _sapp.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) _sapp_glx_getprocaddr("glXSwapIntervalEXT"); - _sapp.glx.EXT_swap_control = 0 != _sapp.glx.SwapIntervalEXT; - } - if (_sapp_glx_extsupported("GLX_MESA_swap_control", exts)) { - _sapp.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) _sapp_glx_getprocaddr("glXSwapIntervalMESA"); - _sapp.glx.MESA_swap_control = 0 != _sapp.glx.SwapIntervalMESA; - } - _sapp.glx.ARB_multisample = _sapp_glx_extsupported("GLX_ARB_multisample", exts); - if (_sapp_glx_extsupported("GLX_ARB_create_context", exts)) { - _sapp.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) _sapp_glx_getprocaddr("glXCreateContextAttribsARB"); - _sapp.glx.ARB_create_context = 0 != _sapp.glx.CreateContextAttribsARB; - } - _sapp.glx.ARB_create_context_profile = _sapp_glx_extsupported("GLX_ARB_create_context_profile", exts); -} - -_SOKOL_PRIVATE int _sapp_glx_attrib(GLXFBConfig fbconfig, int attrib) { - int value; - _sapp.glx.GetFBConfigAttrib(_sapp.x11.display, fbconfig, attrib, &value); - return value; -} - -_SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { - GLXFBConfig* native_configs; - _sapp_gl_fbconfig* usable_configs; - const _sapp_gl_fbconfig* closest; - int i, native_count, usable_count; - const char* vendor; - bool trust_window_bit = true; - - /* HACK: This is a (hopefully temporary) workaround for Chromium - (VirtualBox GL) not setting the window bit on any GLXFBConfigs - */ - vendor = _sapp.glx.GetClientString(_sapp.x11.display, GLX_VENDOR); - if (vendor && strcmp(vendor, "Chromium") == 0) { - trust_window_bit = false; - } - - native_configs = _sapp.glx.GetFBConfigs(_sapp.x11.display, _sapp.x11.screen, &native_count); - if (!native_configs || !native_count) { - _sapp_fail("GLX: No GLXFBConfigs returned"); - } - - usable_configs = (_sapp_gl_fbconfig*) SOKOL_CALLOC((size_t)native_count, sizeof(_sapp_gl_fbconfig)); - usable_count = 0; - for (i = 0; i < native_count; i++) { - const GLXFBConfig n = native_configs[i]; - _sapp_gl_fbconfig* u = usable_configs + usable_count; - _sapp_gl_init_fbconfig(u); - - /* Only consider RGBA GLXFBConfigs */ - if (0 == (_sapp_glx_attrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT)) { - continue; - } - /* Only consider window GLXFBConfigs */ - if (0 == (_sapp_glx_attrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT)) { - if (trust_window_bit) { - continue; - } - } - u->red_bits = _sapp_glx_attrib(n, GLX_RED_SIZE); - u->green_bits = _sapp_glx_attrib(n, GLX_GREEN_SIZE); - u->blue_bits = _sapp_glx_attrib(n, GLX_BLUE_SIZE); - u->alpha_bits = _sapp_glx_attrib(n, GLX_ALPHA_SIZE); - u->depth_bits = _sapp_glx_attrib(n, GLX_DEPTH_SIZE); - u->stencil_bits = _sapp_glx_attrib(n, GLX_STENCIL_SIZE); - if (_sapp_glx_attrib(n, GLX_DOUBLEBUFFER)) { - u->doublebuffer = true; - } - if (_sapp.glx.ARB_multisample) { - u->samples = _sapp_glx_attrib(n, GLX_SAMPLES); - } - u->handle = (uintptr_t) n; - usable_count++; - } - _sapp_gl_fbconfig desired; - _sapp_gl_init_fbconfig(&desired); - desired.red_bits = 8; - desired.green_bits = 8; - desired.blue_bits = 8; - desired.alpha_bits = 8; - desired.depth_bits = 24; - desired.stencil_bits = 8; - desired.doublebuffer = true; - desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0; - closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count); - GLXFBConfig result = 0; - if (closest) { - result = (GLXFBConfig) closest->handle; - } - XFree(native_configs); - SOKOL_FREE(usable_configs); - return result; -} - -_SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) { - GLXFBConfig native = _sapp_glx_choosefbconfig(); - if (0 == native) { - _sapp_fail("GLX: Failed to find a suitable GLXFBConfig"); - } - XVisualInfo* result = _sapp.glx.GetVisualFromFBConfig(_sapp.x11.display, native); - if (!result) { - _sapp_fail("GLX: Failed to retrieve Visual for GLXFBConfig"); - } - *visual = result->visual; - *depth = result->depth; - XFree(result); -} - -_SOKOL_PRIVATE void _sapp_glx_create_context(void) { - GLXFBConfig native = _sapp_glx_choosefbconfig(); - if (0 == native){ - _sapp_fail("GLX: Failed to find a suitable GLXFBConfig (2)"); - } - if (!(_sapp.glx.ARB_create_context && _sapp.glx.ARB_create_context_profile)) { - _sapp_fail("GLX: ARB_create_context and ARB_create_context_profile required"); - } - _sapp_x11_grab_error_handler(); - const int attribs[] = { - GLX_CONTEXT_MAJOR_VERSION_ARB, 3, - GLX_CONTEXT_MINOR_VERSION_ARB, 3, - GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, - GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, - 0, 0 - }; - _sapp.glx.ctx = _sapp.glx.CreateContextAttribsARB(_sapp.x11.display, native, NULL, True, attribs); - if (!_sapp.glx.ctx) { - _sapp_fail("GLX: failed to create GL context"); - } - _sapp_x11_release_error_handler(); - _sapp.glx.window = _sapp.glx.CreateWindow(_sapp.x11.display, native, _sapp.x11.window, NULL); - if (!_sapp.glx.window) { - _sapp_fail("GLX: failed to create window"); - } -} - -_SOKOL_PRIVATE void _sapp_glx_destroy_context(void) { - if (_sapp.glx.window) { - _sapp.glx.DestroyWindow(_sapp.x11.display, _sapp.glx.window); - _sapp.glx.window = 0; - } - if (_sapp.glx.ctx) { - _sapp.glx.DestroyContext(_sapp.x11.display, _sapp.glx.ctx); - _sapp.glx.ctx = 0; - } -} - -_SOKOL_PRIVATE void _sapp_glx_make_current(void) { - _sapp.glx.MakeCurrent(_sapp.x11.display, _sapp.glx.window, _sapp.glx.ctx); -} - -_SOKOL_PRIVATE void _sapp_glx_swap_buffers(void) { - _sapp.glx.SwapBuffers(_sapp.x11.display, _sapp.glx.window); -} - -_SOKOL_PRIVATE void _sapp_glx_swapinterval(int interval) { - _sapp_glx_make_current(); - if (_sapp.glx.EXT_swap_control) { - _sapp.glx.SwapIntervalEXT(_sapp.x11.display, _sapp.glx.window, interval); - } - else if (_sapp.glx.MESA_swap_control) { - _sapp.glx.SwapIntervalMESA(interval); - } -} - -_SOKOL_PRIVATE void _sapp_x11_send_event(Atom type, int a, int b, int c, int d, int e) { - XEvent event; - memset(&event, 0, sizeof(event)); - - event.type = ClientMessage; - event.xclient.window = _sapp.x11.window; - event.xclient.format = 32; - event.xclient.message_type = type; - event.xclient.data.l[0] = a; - event.xclient.data.l[1] = b; - event.xclient.data.l[2] = c; - event.xclient.data.l[3] = d; - event.xclient.data.l[4] = e; - - XSendEvent(_sapp.x11.display, _sapp.x11.root, - False, - SubstructureNotifyMask | SubstructureRedirectMask, - &event); -} - -_SOKOL_PRIVATE void _sapp_x11_query_window_size(void) { - XWindowAttributes attribs; - XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &attribs); - _sapp.window_width = attribs.width; - _sapp.window_height = attribs.height; - _sapp.framebuffer_width = _sapp.window_width; - _sapp.framebuffer_height = _sapp.window_height; -} - -_SOKOL_PRIVATE void _sapp_x11_set_fullscreen(bool enable) { - /* NOTE: this function must be called after XMapWindow (which happens in _sapp_x11_show_window()) */ - if (_sapp.x11.NET_WM_STATE && _sapp.x11.NET_WM_STATE_FULLSCREEN) { - if (enable) { - const int _NET_WM_STATE_ADD = 1; - _sapp_x11_send_event(_sapp.x11.NET_WM_STATE, - _NET_WM_STATE_ADD, - _sapp.x11.NET_WM_STATE_FULLSCREEN, - 0, 1, 0); - } - else { - const int _NET_WM_STATE_REMOVE = 0; - _sapp_x11_send_event(_sapp.x11.NET_WM_STATE, - _NET_WM_STATE_REMOVE, - _sapp.x11.NET_WM_STATE_FULLSCREEN, - 0, 1, 0); - } - } - XFlush(_sapp.x11.display); -} - -_SOKOL_PRIVATE void _sapp_x11_create_hidden_cursor(void) { - SOKOL_ASSERT(0 == _sapp.x11.hidden_cursor); - const int w = 16; - const int h = 16; - XcursorImage* img = XcursorImageCreate(w, h); - SOKOL_ASSERT(img && (img->width == 16) && (img->height == 16) && img->pixels); - img->xhot = 0; - img->yhot = 0; - const size_t num_bytes = (size_t)(w * h) * sizeof(XcursorPixel); - memset(img->pixels, 0, num_bytes); - _sapp.x11.hidden_cursor = XcursorImageLoadCursor(_sapp.x11.display, img); - XcursorImageDestroy(img); -} - -_SOKOL_PRIVATE void _sapp_x11_toggle_fullscreen(void) { - _sapp.fullscreen = !_sapp.fullscreen; - _sapp_x11_set_fullscreen(_sapp.fullscreen); - _sapp_x11_query_window_size(); -} - -_SOKOL_PRIVATE void _sapp_x11_show_mouse(bool show) { - if (show) { - XUndefineCursor(_sapp.x11.display, _sapp.x11.window); - } - else { - XDefineCursor(_sapp.x11.display, _sapp.x11.window, _sapp.x11.hidden_cursor); - } -} - -_SOKOL_PRIVATE void _sapp_x11_lock_mouse(bool lock) { - if (lock == _sapp.mouse.locked) { - return; - } - _sapp.mouse.dx = 0.0f; - _sapp.mouse.dy = 0.0f; - _sapp.mouse.locked = lock; - if (_sapp.mouse.locked) { - if (_sapp.x11.xi.available) { - XIEventMask em; - unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; // XIMaskLen is a macro - em.deviceid = XIAllMasterDevices; - em.mask_len = sizeof(mask); - em.mask = mask; - XISetMask(mask, XI_RawMotion); - XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1); - } - XGrabPointer(_sapp.x11.display, // display - _sapp.x11.window, // grab_window - True, // owner_events - ButtonPressMask | ButtonReleaseMask | PointerMotionMask, // event_mask - GrabModeAsync, // pointer_mode - GrabModeAsync, // keyboard_mode - _sapp.x11.window, // confine_to - _sapp.x11.hidden_cursor, // cursor - CurrentTime); // time - } - else { - if (_sapp.x11.xi.available) { - XIEventMask em; - unsigned char mask[] = { 0 }; - em.deviceid = XIAllMasterDevices; - em.mask_len = sizeof(mask); - em.mask = mask; - XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1); - } - XWarpPointer(_sapp.x11.display, None, _sapp.x11.window, 0, 0, 0, 0, (int) _sapp.mouse.x, _sapp.mouse.y); - XUngrabPointer(_sapp.x11.display, CurrentTime); - } - XFlush(_sapp.x11.display); -} - -_SOKOL_PRIVATE void _sapp_x11_update_window_title(void) { - Xutf8SetWMProperties(_sapp.x11.display, - _sapp.x11.window, - _sapp.window_title, _sapp.window_title, - NULL, 0, NULL, NULL, NULL); - XChangeProperty(_sapp.x11.display, _sapp.x11.window, - _sapp.x11.NET_WM_NAME, _sapp.x11.UTF8_STRING, 8, - PropModeReplace, - (unsigned char*)_sapp.window_title, - strlen(_sapp.window_title)); - XChangeProperty(_sapp.x11.display, _sapp.x11.window, - _sapp.x11.NET_WM_ICON_NAME, _sapp.x11.UTF8_STRING, 8, - PropModeReplace, - (unsigned char*)_sapp.window_title, - strlen(_sapp.window_title)); - XFlush(_sapp.x11.display); -} - -_SOKOL_PRIVATE void _sapp_x11_set_icon(const sapp_icon_desc* icon_desc, int num_images) { - SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); - int long_count = 0; - for (int i = 0; i < num_images; i++) { - const sapp_image_desc* img_desc = &icon_desc->images[i]; - long_count += 2 + (img_desc->width * img_desc->height); - } - long* icon_data = (long*) SOKOL_CALLOC((size_t)long_count, sizeof(long)); - SOKOL_ASSERT(icon_data); - long* dst = icon_data; - for (int img_index = 0; img_index < num_images; img_index++) { - const sapp_image_desc* img_desc = &icon_desc->images[img_index]; - const uint8_t* src = (const uint8_t*) img_desc->pixels.ptr; - *dst++ = img_desc->width; - *dst++ = img_desc->height; - const int num_pixels = img_desc->width * img_desc->height; - for (int pixel_index = 0; pixel_index < num_pixels; pixel_index++) { - *dst++ = ((long)(src[pixel_index * 4 + 0]) << 16) | - ((long)(src[pixel_index * 4 + 1]) << 8) | - ((long)(src[pixel_index * 4 + 2]) << 0) | - ((long)(src[pixel_index * 4 + 3]) << 24); - } - } - XChangeProperty(_sapp.x11.display, _sapp.x11.window, - _sapp.x11.NET_WM_ICON, - XA_CARDINAL, 32, - PropModeReplace, - (unsigned char*)icon_data, - long_count); - SOKOL_FREE(icon_data); - XFlush(_sapp.x11.display); -} - -_SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { - _sapp.x11.colormap = XCreateColormap(_sapp.x11.display, _sapp.x11.root, visual, AllocNone); - XSetWindowAttributes wa; - memset(&wa, 0, sizeof(wa)); - const uint32_t wamask = CWBorderPixel | CWColormap | CWEventMask; - wa.colormap = _sapp.x11.colormap; - wa.border_pixel = 0; - wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | - PointerMotionMask | ButtonPressMask | ButtonReleaseMask | - ExposureMask | FocusChangeMask | VisibilityChangeMask | - EnterWindowMask | LeaveWindowMask | PropertyChangeMask; - _sapp_x11_grab_error_handler(); - _sapp.x11.window = XCreateWindow(_sapp.x11.display, - _sapp.x11.root, - 0, 0, - (uint32_t)_sapp.window_width, - (uint32_t)_sapp.window_height, - 0, /* border width */ - depth, /* color depth */ - InputOutput, - visual, - wamask, - &wa); - _sapp_x11_release_error_handler(); - if (!_sapp.x11.window) { - _sapp_fail("X11: Failed to create window"); - } - Atom protocols[] = { - _sapp.x11.WM_DELETE_WINDOW - }; - XSetWMProtocols(_sapp.x11.display, _sapp.x11.window, protocols, 1); - - XSizeHints* hints = XAllocSizeHints(); - hints->flags |= PWinGravity; - hints->win_gravity = StaticGravity; - XSetWMNormalHints(_sapp.x11.display, _sapp.x11.window, hints); - XFree(hints); - - /* announce support for drag'n'drop */ - if (_sapp.drop.enabled) { - const Atom version = _SAPP_X11_XDND_VERSION; - XChangeProperty(_sapp.x11.display, _sapp.x11.window, _sapp.x11.xdnd.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*) &version, 1); - } - - _sapp_x11_update_window_title(); -} - -_SOKOL_PRIVATE void _sapp_x11_destroy_window(void) { - if (_sapp.x11.window) { - XUnmapWindow(_sapp.x11.display, _sapp.x11.window); - XDestroyWindow(_sapp.x11.display, _sapp.x11.window); - _sapp.x11.window = 0; - } - if (_sapp.x11.colormap) { - XFreeColormap(_sapp.x11.display, _sapp.x11.colormap); - _sapp.x11.colormap = 0; - } - XFlush(_sapp.x11.display); -} - -_SOKOL_PRIVATE bool _sapp_x11_window_visible(void) { - XWindowAttributes wa; - XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &wa); - return wa.map_state == IsViewable; -} - -_SOKOL_PRIVATE void _sapp_x11_show_window(void) { - if (!_sapp_x11_window_visible()) { - XMapWindow(_sapp.x11.display, _sapp.x11.window); - XRaiseWindow(_sapp.x11.display, _sapp.x11.window); - XFlush(_sapp.x11.display); - } -} - -_SOKOL_PRIVATE void _sapp_x11_hide_window(void) { - XUnmapWindow(_sapp.x11.display, _sapp.x11.window); - XFlush(_sapp.x11.display); -} - -_SOKOL_PRIVATE unsigned long _sapp_x11_get_window_property(Window window, Atom property, Atom type, unsigned char** value) { - Atom actualType; - int actualFormat; - unsigned long itemCount, bytesAfter; - XGetWindowProperty(_sapp.x11.display, - window, - property, - 0, - LONG_MAX, - False, - type, - &actualType, - &actualFormat, - &itemCount, - &bytesAfter, - value); - return itemCount; -} - -_SOKOL_PRIVATE int _sapp_x11_get_window_state(void) { - int result = WithdrawnState; - struct { - CARD32 state; - Window icon; - } *state = NULL; - - if (_sapp_x11_get_window_property(_sapp.x11.window, _sapp.x11.WM_STATE, _sapp.x11.WM_STATE, (unsigned char**)&state) >= 2) { - result = (int)state->state; - } - if (state) { - XFree(state); - } - return result; -} - -_SOKOL_PRIVATE uint32_t _sapp_x11_key_modifier_bit(sapp_keycode key) { - switch (key) { - case SAPP_KEYCODE_LEFT_SHIFT: - case SAPP_KEYCODE_RIGHT_SHIFT: - return SAPP_MODIFIER_SHIFT; - case SAPP_KEYCODE_LEFT_CONTROL: - case SAPP_KEYCODE_RIGHT_CONTROL: - return SAPP_MODIFIER_CTRL; - case SAPP_KEYCODE_LEFT_ALT: - case SAPP_KEYCODE_RIGHT_ALT: - return SAPP_MODIFIER_ALT; - case SAPP_KEYCODE_LEFT_SUPER: - case SAPP_KEYCODE_RIGHT_SUPER: - return SAPP_MODIFIER_SUPER; - default: - return 0; - } -} - -_SOKOL_PRIVATE uint32_t _sapp_x11_button_modifier_bit(sapp_mousebutton btn) { - switch (btn) { - case SAPP_MOUSEBUTTON_LEFT: return SAPP_MODIFIER_LMB; - case SAPP_MOUSEBUTTON_RIGHT: return SAPP_MODIFIER_RMB; - case SAPP_MOUSEBUTTON_MIDDLE: return SAPP_MODIFIER_MMB; - default: return 0; - } -} - -_SOKOL_PRIVATE uint32_t _sapp_x11_mods(uint32_t x11_mods) { - uint32_t mods = 0; - if (x11_mods & ShiftMask) { - mods |= SAPP_MODIFIER_SHIFT; - } - if (x11_mods & ControlMask) { - mods |= SAPP_MODIFIER_CTRL; - } - if (x11_mods & Mod1Mask) { - mods |= SAPP_MODIFIER_ALT; - } - if (x11_mods & Mod4Mask) { - mods |= SAPP_MODIFIER_SUPER; - } - if (x11_mods & Button1Mask) { - mods |= SAPP_MODIFIER_LMB; - } - if (x11_mods & Button2Mask) { - mods |= SAPP_MODIFIER_MMB; - } - if (x11_mods & Button3Mask) { - mods |= SAPP_MODIFIER_RMB; - } - return mods; -} - -_SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE sapp_mousebutton _sapp_x11_translate_button(const XEvent* event) { - switch (event->xbutton.button) { - case Button1: return SAPP_MOUSEBUTTON_LEFT; - case Button2: return SAPP_MOUSEBUTTON_MIDDLE; - case Button3: return SAPP_MOUSEBUTTON_RIGHT; - default: return SAPP_MOUSEBUTTON_INVALID; - } -} - -_SOKOL_PRIVATE void _sapp_x11_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mods) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - _sapp.event.mouse_button = btn; - _sapp.event.modifiers = mods; - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_x11_scroll_event(float x, float y, uint32_t mods) { - if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); - _sapp.event.modifiers = mods; - _sapp.event.scroll_x = x; - _sapp.event.scroll_y = y; - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_x11_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mods) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - _sapp.event.key_code = key; - _sapp.event.key_repeat = repeat; - _sapp.event.modifiers = mods; - _sapp_call_event(&_sapp.event); - /* check if a CLIPBOARD_PASTED event must be sent too */ - if (_sapp.clipboard.enabled && - (type == SAPP_EVENTTYPE_KEY_DOWN) && - (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) && - (_sapp.event.key_code == SAPP_KEYCODE_V)) - { - _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); - _sapp_call_event(&_sapp.event); - } - } -} - -_SOKOL_PRIVATE void _sapp_x11_char_event(uint32_t chr, bool repeat, uint32_t mods) { - if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_CHAR); - _sapp.event.char_code = chr; - _sapp.event.key_repeat = repeat; - _sapp.event.modifiers = mods; - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE sapp_keycode _sapp_x11_translate_key(int scancode) { - int dummy; - KeySym* keysyms = XGetKeyboardMapping(_sapp.x11.display, scancode, 1, &dummy); - SOKOL_ASSERT(keysyms); - KeySym keysym = keysyms[0]; - XFree(keysyms); - switch (keysym) { - case XK_Escape: return SAPP_KEYCODE_ESCAPE; - case XK_Tab: return SAPP_KEYCODE_TAB; - case XK_Shift_L: return SAPP_KEYCODE_LEFT_SHIFT; - case XK_Shift_R: return SAPP_KEYCODE_RIGHT_SHIFT; - case XK_Control_L: return SAPP_KEYCODE_LEFT_CONTROL; - case XK_Control_R: return SAPP_KEYCODE_RIGHT_CONTROL; - case XK_Meta_L: - case XK_Alt_L: return SAPP_KEYCODE_LEFT_ALT; - case XK_Mode_switch: /* Mapped to Alt_R on many keyboards */ - case XK_ISO_Level3_Shift: /* AltGr on at least some machines */ - case XK_Meta_R: - case XK_Alt_R: return SAPP_KEYCODE_RIGHT_ALT; - case XK_Super_L: return SAPP_KEYCODE_LEFT_SUPER; - case XK_Super_R: return SAPP_KEYCODE_RIGHT_SUPER; - case XK_Menu: return SAPP_KEYCODE_MENU; - case XK_Num_Lock: return SAPP_KEYCODE_NUM_LOCK; - case XK_Caps_Lock: return SAPP_KEYCODE_CAPS_LOCK; - case XK_Print: return SAPP_KEYCODE_PRINT_SCREEN; - case XK_Scroll_Lock: return SAPP_KEYCODE_SCROLL_LOCK; - case XK_Pause: return SAPP_KEYCODE_PAUSE; - case XK_Delete: return SAPP_KEYCODE_DELETE; - case XK_BackSpace: return SAPP_KEYCODE_BACKSPACE; - case XK_Return: return SAPP_KEYCODE_ENTER; - case XK_Home: return SAPP_KEYCODE_HOME; - case XK_End: return SAPP_KEYCODE_END; - case XK_Page_Up: return SAPP_KEYCODE_PAGE_UP; - case XK_Page_Down: return SAPP_KEYCODE_PAGE_DOWN; - case XK_Insert: return SAPP_KEYCODE_INSERT; - case XK_Left: return SAPP_KEYCODE_LEFT; - case XK_Right: return SAPP_KEYCODE_RIGHT; - case XK_Down: return SAPP_KEYCODE_DOWN; - case XK_Up: return SAPP_KEYCODE_UP; - case XK_F1: return SAPP_KEYCODE_F1; - case XK_F2: return SAPP_KEYCODE_F2; - case XK_F3: return SAPP_KEYCODE_F3; - case XK_F4: return SAPP_KEYCODE_F4; - case XK_F5: return SAPP_KEYCODE_F5; - case XK_F6: return SAPP_KEYCODE_F6; - case XK_F7: return SAPP_KEYCODE_F7; - case XK_F8: return SAPP_KEYCODE_F8; - case XK_F9: return SAPP_KEYCODE_F9; - case XK_F10: return SAPP_KEYCODE_F10; - case XK_F11: return SAPP_KEYCODE_F11; - case XK_F12: return SAPP_KEYCODE_F12; - case XK_F13: return SAPP_KEYCODE_F13; - case XK_F14: return SAPP_KEYCODE_F14; - case XK_F15: return SAPP_KEYCODE_F15; - case XK_F16: return SAPP_KEYCODE_F16; - case XK_F17: return SAPP_KEYCODE_F17; - case XK_F18: return SAPP_KEYCODE_F18; - case XK_F19: return SAPP_KEYCODE_F19; - case XK_F20: return SAPP_KEYCODE_F20; - case XK_F21: return SAPP_KEYCODE_F21; - case XK_F22: return SAPP_KEYCODE_F22; - case XK_F23: return SAPP_KEYCODE_F23; - case XK_F24: return SAPP_KEYCODE_F24; - case XK_F25: return SAPP_KEYCODE_F25; - - case XK_KP_Divide: return SAPP_KEYCODE_KP_DIVIDE; - case XK_KP_Multiply: return SAPP_KEYCODE_KP_MULTIPLY; - case XK_KP_Subtract: return SAPP_KEYCODE_KP_SUBTRACT; - case XK_KP_Add: return SAPP_KEYCODE_KP_ADD; - - case XK_KP_Insert: return SAPP_KEYCODE_KP_0; - case XK_KP_End: return SAPP_KEYCODE_KP_1; - case XK_KP_Down: return SAPP_KEYCODE_KP_2; - case XK_KP_Page_Down: return SAPP_KEYCODE_KP_3; - case XK_KP_Left: return SAPP_KEYCODE_KP_4; - case XK_KP_Begin: return SAPP_KEYCODE_KP_5; - case XK_KP_Right: return SAPP_KEYCODE_KP_6; - case XK_KP_Home: return SAPP_KEYCODE_KP_7; - case XK_KP_Up: return SAPP_KEYCODE_KP_8; - case XK_KP_Page_Up: return SAPP_KEYCODE_KP_9; - case XK_KP_Delete: return SAPP_KEYCODE_KP_DECIMAL; - case XK_KP_Equal: return SAPP_KEYCODE_KP_EQUAL; - case XK_KP_Enter: return SAPP_KEYCODE_KP_ENTER; - - case XK_a: return SAPP_KEYCODE_A; - case XK_b: return SAPP_KEYCODE_B; - case XK_c: return SAPP_KEYCODE_C; - case XK_d: return SAPP_KEYCODE_D; - case XK_e: return SAPP_KEYCODE_E; - case XK_f: return SAPP_KEYCODE_F; - case XK_g: return SAPP_KEYCODE_G; - case XK_h: return SAPP_KEYCODE_H; - case XK_i: return SAPP_KEYCODE_I; - case XK_j: return SAPP_KEYCODE_J; - case XK_k: return SAPP_KEYCODE_K; - case XK_l: return SAPP_KEYCODE_L; - case XK_m: return SAPP_KEYCODE_M; - case XK_n: return SAPP_KEYCODE_N; - case XK_o: return SAPP_KEYCODE_O; - case XK_p: return SAPP_KEYCODE_P; - case XK_q: return SAPP_KEYCODE_Q; - case XK_r: return SAPP_KEYCODE_R; - case XK_s: return SAPP_KEYCODE_S; - case XK_t: return SAPP_KEYCODE_T; - case XK_u: return SAPP_KEYCODE_U; - case XK_v: return SAPP_KEYCODE_V; - case XK_w: return SAPP_KEYCODE_W; - case XK_x: return SAPP_KEYCODE_X; - case XK_y: return SAPP_KEYCODE_Y; - case XK_z: return SAPP_KEYCODE_Z; - case XK_1: return SAPP_KEYCODE_1; - case XK_2: return SAPP_KEYCODE_2; - case XK_3: return SAPP_KEYCODE_3; - case XK_4: return SAPP_KEYCODE_4; - case XK_5: return SAPP_KEYCODE_5; - case XK_6: return SAPP_KEYCODE_6; - case XK_7: return SAPP_KEYCODE_7; - case XK_8: return SAPP_KEYCODE_8; - case XK_9: return SAPP_KEYCODE_9; - case XK_0: return SAPP_KEYCODE_0; - case XK_space: return SAPP_KEYCODE_SPACE; - case XK_minus: return SAPP_KEYCODE_MINUS; - case XK_equal: return SAPP_KEYCODE_EQUAL; - case XK_bracketleft: return SAPP_KEYCODE_LEFT_BRACKET; - case XK_bracketright: return SAPP_KEYCODE_RIGHT_BRACKET; - case XK_backslash: return SAPP_KEYCODE_BACKSLASH; - case XK_semicolon: return SAPP_KEYCODE_SEMICOLON; - case XK_apostrophe: return SAPP_KEYCODE_APOSTROPHE; - case XK_grave: return SAPP_KEYCODE_GRAVE_ACCENT; - case XK_comma: return SAPP_KEYCODE_COMMA; - case XK_period: return SAPP_KEYCODE_PERIOD; - case XK_slash: return SAPP_KEYCODE_SLASH; - case XK_less: return SAPP_KEYCODE_WORLD_1; /* At least in some layouts... */ - default: return SAPP_KEYCODE_INVALID; - } -} - -_SOKOL_PRIVATE int32_t _sapp_x11_keysym_to_unicode(KeySym keysym) { - int min = 0; - int max = sizeof(_sapp_x11_keysymtab) / sizeof(struct _sapp_x11_codepair) - 1; - int mid; - - /* First check for Latin-1 characters (1:1 mapping) */ - if ((keysym >= 0x0020 && keysym <= 0x007e) || - (keysym >= 0x00a0 && keysym <= 0x00ff)) - { - return keysym; - } - - /* Also check for directly encoded 24-bit UCS characters */ - if ((keysym & 0xff000000) == 0x01000000) { - return keysym & 0x00ffffff; - } - - /* Binary search in table */ - while (max >= min) { - mid = (min + max) / 2; - if (_sapp_x11_keysymtab[mid].keysym < keysym) { - min = mid + 1; - } - else if (_sapp_x11_keysymtab[mid].keysym > keysym) { - max = mid - 1; - } - else { - return _sapp_x11_keysymtab[mid].ucs; - } - } - - /* No matching Unicode value found */ - return -1; -} - -_SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { - SOKOL_ASSERT(src); - SOKOL_ASSERT(_sapp.drop.buffer); - - _sapp_clear_drop_buffer(); - _sapp.drop.num_files = 0; - - /* - src is (potentially percent-encoded) string made of one or multiple paths - separated by \r\n, each path starting with 'file://' - */ - bool err = false; - int src_count = 0; - char src_chr = 0; - char* dst_ptr = _sapp.drop.buffer; - const char* dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1); // room for terminating 0 - while (0 != (src_chr = *src++)) { - src_count++; - char dst_chr = 0; - /* check leading 'file://' */ - if (src_count <= 7) { - if (((src_count == 1) && (src_chr != 'f')) || - ((src_count == 2) && (src_chr != 'i')) || - ((src_count == 3) && (src_chr != 'l')) || - ((src_count == 4) && (src_chr != 'e')) || - ((src_count == 5) && (src_chr != ':')) || - ((src_count == 6) && (src_chr != '/')) || - ((src_count == 7) && (src_chr != '/'))) - { - SOKOL_LOG("sokol_app.h: dropped file URI doesn't start with file://"); - err = true; - break; - } - } - else if (src_chr == '\r') { - // skip - } - else if (src_chr == '\n') { - src_chr = 0; - src_count = 0; - _sapp.drop.num_files++; - // too many files is not an error - if (_sapp.drop.num_files >= _sapp.drop.max_files) { - break; - } - dst_ptr = _sapp.drop.buffer + _sapp.drop.num_files * _sapp.drop.max_path_length; - dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1); - } - else if ((src_chr == '%') && src[0] && src[1]) { - // a percent-encoded byte (most like UTF-8 multibyte sequence) - const char digits[3] = { src[0], src[1], 0 }; - src += 2; - dst_chr = (char) strtol(digits, 0, 16); - } - else { - dst_chr = src_chr; - } - if (dst_chr) { - // dst_end_ptr already has adjustment for terminating zero - if (dst_ptr < dst_end_ptr) { - *dst_ptr++ = dst_chr; - } - else { - SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)"); - err = true; - break; - } - } - } - if (err) { - _sapp_clear_drop_buffer(); - _sapp.drop.num_files = 0; - return false; - } - else { - return true; - } -} - -// XLib manual says keycodes are in the range [8, 255] inclusive. -// https://tronche.com/gui/x/xlib/input/keyboard-encoding.html -static bool _sapp_x11_keycodes[256]; - -_SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { - Bool filtered = XFilterEvent(event, None); - switch (event->type) { - case GenericEvent: - if (_sapp.mouse.locked && _sapp.x11.xi.available) { - if (event->xcookie.extension == _sapp.x11.xi.major_opcode) { - if (XGetEventData(_sapp.x11.display, &event->xcookie)) { - if (event->xcookie.evtype == XI_RawMotion) { - XIRawEvent* re = (XIRawEvent*) event->xcookie.data; - if (re->valuators.mask_len) { - const double* values = re->raw_values; - if (XIMaskIsSet(re->valuators.mask, 0)) { - _sapp.mouse.dx = (float) *values; - values++; - } - if (XIMaskIsSet(re->valuators.mask, 1)) { - _sapp.mouse.dy = (float) *values; - } - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state)); - } - } - XFreeEventData(_sapp.x11.display, &event->xcookie); - } - } - } - break; - case FocusIn: - // NOTE: ingnoring NotifyGrab and NotifyUngrab is same behaviour as GLFW - if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { - _sapp_x11_app_event(SAPP_EVENTTYPE_FOCUSED); - } - break; - case FocusOut: - /* if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock */ - if (_sapp.mouse.locked) { - _sapp_x11_lock_mouse(false); - } - // NOTE: ingnoring NotifyGrab and NotifyUngrab is same behaviour as GLFW - if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { - _sapp_x11_app_event(SAPP_EVENTTYPE_UNFOCUSED); - } - break; - case KeyPress: - { - int keycode = (int)event->xkey.keycode; - const sapp_keycode key = _sapp_x11_translate_key(keycode); - bool repeat = _sapp_x11_keycodes[keycode & 0xFF]; - _sapp_x11_keycodes[keycode & 0xFF] = true; - uint32_t mods = _sapp_x11_mods(event->xkey.state); - // X11 doesn't set modifier bit on key down, so emulate that - mods |= _sapp_x11_key_modifier_bit(key); - if (key != SAPP_KEYCODE_INVALID) { - _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_DOWN, key, repeat, mods); - } - KeySym keysym; - XLookupString(&event->xkey, NULL, 0, &keysym, NULL); - int32_t chr = _sapp_x11_keysym_to_unicode(keysym); - if (chr > 0) { - _sapp_x11_char_event((uint32_t)chr, repeat, mods); - } - } - break; - case KeyRelease: - { - int keycode = (int)event->xkey.keycode; - const sapp_keycode key = _sapp_x11_translate_key(keycode); - _sapp_x11_keycodes[keycode & 0xFF] = false; - if (key != SAPP_KEYCODE_INVALID) { - uint32_t mods = _sapp_x11_mods(event->xkey.state); - // X11 doesn't clear modifier bit on key up, so emulate that - mods &= ~_sapp_x11_key_modifier_bit(key); - _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_UP, key, false, mods); - } - } - break; - case ButtonPress: - { - const sapp_mousebutton btn = _sapp_x11_translate_button(event); - uint32_t mods = _sapp_x11_mods(event->xbutton.state); - // X11 doesn't set modifier bit on button down, so emulate that - mods |= _sapp_x11_button_modifier_bit(btn); - if (btn != SAPP_MOUSEBUTTON_INVALID) { - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, btn, mods); - _sapp.x11.mouse_buttons |= (1 << btn); - } - else { - /* might be a scroll event */ - switch (event->xbutton.button) { - case 4: _sapp_x11_scroll_event(0.0f, 1.0f, mods); break; - case 5: _sapp_x11_scroll_event(0.0f, -1.0f, mods); break; - case 6: _sapp_x11_scroll_event(1.0f, 0.0f, mods); break; - case 7: _sapp_x11_scroll_event(-1.0f, 0.0f, mods); break; - } - } - } - break; - case ButtonRelease: - { - const sapp_mousebutton btn = _sapp_x11_translate_button(event); - if (btn != SAPP_MOUSEBUTTON_INVALID) { - uint32_t mods = _sapp_x11_mods(event->xbutton.state); - // X11 doesn't clear modifier bit on button up, so emulate that - mods &= ~_sapp_x11_button_modifier_bit(btn); - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, btn, mods); - _sapp.x11.mouse_buttons &= ~(1 << btn); - } - } - break; - case EnterNotify: - /* don't send enter/leave events while mouse button held down */ - if (0 == _sapp.x11.mouse_buttons) { - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); - } - break; - case LeaveNotify: - if (0 == _sapp.x11.mouse_buttons) { - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); - } - break; - case MotionNotify: - if (!_sapp.mouse.locked) { - const float new_x = (float) event->xmotion.x; - const float new_y = (float) event->xmotion.y; - if (_sapp.mouse.pos_valid) { - _sapp.mouse.dx = new_x - _sapp.mouse.x; - _sapp.mouse.dy = new_y - _sapp.mouse.y; - } - _sapp.mouse.x = new_x; - _sapp.mouse.y = new_y; - _sapp.mouse.pos_valid = true; - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state)); - } - break; - case ConfigureNotify: - if ((event->xconfigure.width != _sapp.window_width) || (event->xconfigure.height != _sapp.window_height)) { - _sapp.window_width = event->xconfigure.width; - _sapp.window_height = event->xconfigure.height; - _sapp.framebuffer_width = _sapp.window_width; - _sapp.framebuffer_height = _sapp.window_height; - _sapp_x11_app_event(SAPP_EVENTTYPE_RESIZED); - } - break; - case PropertyNotify: - if (event->xproperty.state == PropertyNewValue) { - if (event->xproperty.atom == _sapp.x11.WM_STATE) { - const int state = _sapp_x11_get_window_state(); - if (state != _sapp.x11.window_state) { - _sapp.x11.window_state = state; - if (state == IconicState) { - _sapp_x11_app_event(SAPP_EVENTTYPE_ICONIFIED); - } - else if (state == NormalState) { - _sapp_x11_app_event(SAPP_EVENTTYPE_RESTORED); - } - } - } - } - break; - case ClientMessage: - if (filtered) { - return; - } - if (event->xclient.message_type == _sapp.x11.WM_PROTOCOLS) { - const Atom protocol = (Atom)event->xclient.data.l[0]; - if (protocol == _sapp.x11.WM_DELETE_WINDOW) { - _sapp.quit_requested = true; - } - } - else if (event->xclient.message_type == _sapp.x11.xdnd.XdndEnter) { - const bool is_list = 0 != (event->xclient.data.l[1] & 1); - _sapp.x11.xdnd.source = (Window)event->xclient.data.l[0]; - _sapp.x11.xdnd.version = event->xclient.data.l[1] >> 24; - _sapp.x11.xdnd.format = None; - if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { - return; - } - uint32_t count = 0; - Atom* formats = 0; - if (is_list) { - count = _sapp_x11_get_window_property(_sapp.x11.xdnd.source, _sapp.x11.xdnd.XdndTypeList, XA_ATOM, (unsigned char**)&formats); - } - else { - count = 3; - formats = (Atom*) event->xclient.data.l + 2; - } - for (uint32_t i = 0; i < count; i++) { - if (formats[i] == _sapp.x11.xdnd.text_uri_list) { - _sapp.x11.xdnd.format = _sapp.x11.xdnd.text_uri_list; - break; - } - } - if (is_list && formats) { - XFree(formats); - } - } - else if (event->xclient.message_type == _sapp.x11.xdnd.XdndDrop) { - if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { - return; - } - Time time = CurrentTime; - if (_sapp.x11.xdnd.format) { - if (_sapp.x11.xdnd.version >= 1) { - time = (Time)event->xclient.data.l[2]; - } - XConvertSelection(_sapp.x11.display, - _sapp.x11.xdnd.XdndSelection, - _sapp.x11.xdnd.format, - _sapp.x11.xdnd.XdndSelection, - _sapp.x11.window, - time); - } - else if (_sapp.x11.xdnd.version >= 2) { - XEvent reply; - memset(&reply, 0, sizeof(reply)); - reply.type = ClientMessage; - reply.xclient.window = _sapp.x11.window; - reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; - reply.xclient.format = 32; - reply.xclient.data.l[0] = (long)_sapp.x11.window; - reply.xclient.data.l[1] = 0; // drag was rejected - reply.xclient.data.l[2] = None; - XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); - XFlush(_sapp.x11.display); - } - } - else if (event->xclient.message_type == _sapp.x11.xdnd.XdndPosition) { - /* drag operation has moved over the window - FIXME: we could track the mouse position here, but - this isn't implemented on other platforms either so far - */ - if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { - return; - } - XEvent reply; - memset(&reply, 0, sizeof(reply)); - reply.type = ClientMessage; - reply.xclient.window = _sapp.x11.xdnd.source; - reply.xclient.message_type = _sapp.x11.xdnd.XdndStatus; - reply.xclient.format = 32; - reply.xclient.data.l[0] = (long)_sapp.x11.window; - if (_sapp.x11.xdnd.format) { - /* reply that we are ready to copy the dragged data */ - reply.xclient.data.l[1] = 1; // accept with no rectangle - if (_sapp.x11.xdnd.version >= 2) { - reply.xclient.data.l[4] = (long)_sapp.x11.xdnd.XdndActionCopy; - } - } - XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); - XFlush(_sapp.x11.display); - } - break; - case SelectionNotify: - if (event->xselection.property == _sapp.x11.xdnd.XdndSelection) { - char* data = 0; - uint32_t result = _sapp_x11_get_window_property(event->xselection.requestor, - event->xselection.property, - event->xselection.target, - (unsigned char**) &data); - if (_sapp.drop.enabled && result) { - if (_sapp_x11_parse_dropped_files_list(data)) { - if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); - _sapp_call_event(&_sapp.event); - } - } - } - if (_sapp.x11.xdnd.version >= 2) { - XEvent reply; - memset(&reply, 0, sizeof(reply)); - reply.type = ClientMessage; - reply.xclient.window = _sapp.x11.window; - reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; - reply.xclient.format = 32; - reply.xclient.data.l[0] = (long)_sapp.x11.window; - reply.xclient.data.l[1] = result; - reply.xclient.data.l[2] = (long)_sapp.x11.xdnd.XdndActionCopy; - XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); - XFlush(_sapp.x11.display); - } - } - break; - case DestroyNotify: - break; - } -} - -_SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { - /* The following lines are here to trigger a linker error instead of an - obscure runtime error if the user has forgotten to add -pthread to - the compiler or linker options. They have no other purpose. - */ - pthread_attr_t pthread_attr; - pthread_attr_init(&pthread_attr); - pthread_attr_destroy(&pthread_attr); - - _sapp_init_state(desc); - _sapp.x11.window_state = NormalState; - - XInitThreads(); - XrmInitialize(); - _sapp.x11.display = XOpenDisplay(NULL); - if (!_sapp.x11.display) { - _sapp_fail("XOpenDisplay() failed!\n"); - } - _sapp.x11.screen = DefaultScreen(_sapp.x11.display); - _sapp.x11.root = DefaultRootWindow(_sapp.x11.display); - XkbSetDetectableAutoRepeat(_sapp.x11.display, true, NULL); - _sapp_x11_query_system_dpi(); - _sapp.dpi_scale = _sapp.x11.dpi / 96.0f; - _sapp_x11_init_extensions(); - _sapp_x11_create_hidden_cursor(); - _sapp_glx_init(); - Visual* visual = 0; - int depth = 0; - _sapp_glx_choose_visual(&visual, &depth); - _sapp_x11_create_window(visual, depth); - _sapp_glx_create_context(); - sapp_set_icon(&desc->icon); - _sapp.valid = true; - _sapp_x11_show_window(); - if (_sapp.fullscreen) { - _sapp_x11_set_fullscreen(true); - } - _sapp_x11_query_window_size(); - _sapp_glx_swapinterval(_sapp.swap_interval); - XFlush(_sapp.x11.display); - while (!_sapp.quit_ordered) { - _sapp_glx_make_current(); - int count = XPending(_sapp.x11.display); - while (count--) { - XEvent event; - XNextEvent(_sapp.x11.display, &event); - _sapp_x11_process_event(&event); - } - _sapp_frame(); - _sapp_glx_swap_buffers(); - XFlush(_sapp.x11.display); - /* handle quit-requested, either from window or from sapp_request_quit() */ - if (_sapp.quit_requested && !_sapp.quit_ordered) { - /* give user code a chance to intervene */ - _sapp_x11_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); - /* if user code hasn't intervened, quit the app */ - if (_sapp.quit_requested) { - _sapp.quit_ordered = true; - } - } - } - _sapp_call_cleanup(); - _sapp_glx_destroy_context(); - _sapp_x11_destroy_window(); - XCloseDisplay(_sapp.x11.display); - _sapp_discard_state(); -} - -#if !defined(SOKOL_NO_ENTRY) -int main(int argc, char* argv[]) { - sapp_desc desc = sokol_main(argc, argv); - _sapp_linux_run(&desc); - return 0; -} -#endif /* SOKOL_NO_ENTRY */ -#endif /* _SAPP_LINUX */ - -/*== PUBLIC API FUNCTIONS ====================================================*/ -#if defined(SOKOL_NO_ENTRY) -SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { - SOKOL_ASSERT(desc); - #if defined(_SAPP_MACOS) - _sapp_macos_run(desc); - #elif defined(_SAPP_IOS) - _sapp_ios_run(desc); - #elif defined(_SAPP_EMSCRIPTEN) - _sapp_emsc_run(desc); - #elif defined(_SAPP_WIN32) - _sapp_win32_run(desc); - #elif defined(_SAPP_UWP) - _sapp_uwp_run(desc); - #elif defined(_SAPP_LINUX) - _sapp_linux_run(desc); - #else - // calling sapp_run() directly is not supported on Android) - _sapp_fail("sapp_run() not supported on this platform!"); - #endif -} - -/* this is just a stub so the linker doesn't complain */ -sapp_desc sokol_main(int argc, char* argv[]) { - _SOKOL_UNUSED(argc); - _SOKOL_UNUSED(argv); - sapp_desc desc; - memset(&desc, 0, sizeof(desc)); - return desc; -} -#else -/* likewise, in normal mode, sapp_run() is just an empty stub */ -SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { - _SOKOL_UNUSED(desc); -} -#endif - -SOKOL_API_IMPL bool sapp_isvalid(void) { - return _sapp.valid; -} - -SOKOL_API_IMPL void* sapp_userdata(void) { - return _sapp.desc.user_data; -} - -SOKOL_API_IMPL sapp_desc sapp_query_desc(void) { - return _sapp.desc; -} - -SOKOL_API_IMPL uint64_t sapp_frame_count(void) { - return _sapp.frame_count; -} - -SOKOL_API_IMPL int sapp_width(void) { - return (_sapp.framebuffer_width > 0) ? _sapp.framebuffer_width : 1; -} - -SOKOL_API_IMPL float sapp_widthf(void) { - return (float)sapp_width(); -} - -SOKOL_API_IMPL int sapp_height(void) { - return (_sapp.framebuffer_height > 0) ? _sapp.framebuffer_height : 1; -} - -SOKOL_API_IMPL float sapp_heightf(void) { - return (float)sapp_height(); -} - -SOKOL_API_IMPL int sapp_color_format(void) { - #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) - switch (_sapp.emsc.wgpu.render_format) { - case WGPUTextureFormat_RGBA8Unorm: - return _SAPP_PIXELFORMAT_RGBA8; - case WGPUTextureFormat_BGRA8Unorm: - return _SAPP_PIXELFORMAT_BGRA8; - default: - SOKOL_UNREACHABLE; - return 0; - } - #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) - return _SAPP_PIXELFORMAT_BGRA8; - #else - return _SAPP_PIXELFORMAT_RGBA8; - #endif -} - -SOKOL_API_IMPL int sapp_depth_format(void) { - return _SAPP_PIXELFORMAT_DEPTH_STENCIL; -} - -SOKOL_API_IMPL int sapp_sample_count(void) { - return _sapp.sample_count; -} - -SOKOL_API_IMPL bool sapp_high_dpi(void) { - return _sapp.desc.high_dpi && (_sapp.dpi_scale >= 1.5f); -} - -SOKOL_API_IMPL float sapp_dpi_scale(void) { - return _sapp.dpi_scale; -} - -SOKOL_API_IMPL bool sapp_gles2(void) { - return _sapp.gles2_fallback; -} - -SOKOL_API_IMPL void sapp_show_keyboard(bool show) { - #if defined(_SAPP_IOS) - _sapp_ios_show_keyboard(show); - #elif defined(_SAPP_EMSCRIPTEN) - _sapp_emsc_show_keyboard(show); - #elif defined(_SAPP_ANDROID) - _sapp_android_show_keyboard(show); - #else - _SOKOL_UNUSED(show); - #endif -} - -SOKOL_API_IMPL bool sapp_keyboard_shown(void) { - return _sapp.onscreen_keyboard_shown; -} - -SOKOL_API_IMPL bool sapp_is_fullscreen(void) { - return _sapp.fullscreen; -} - -SOKOL_API_IMPL void sapp_toggle_fullscreen(void) { - #if defined(_SAPP_MACOS) - _sapp_macos_toggle_fullscreen(); - #elif defined(_SAPP_WIN32) - _sapp_win32_toggle_fullscreen(); - #elif defined(_SAPP_UWP) - _sapp_uwp_toggle_fullscreen(); - #elif defined(_SAPP_LINUX) - _sapp_x11_toggle_fullscreen(); - #endif -} - -/* NOTE that sapp_show_mouse() does not "stack" like the Win32 or macOS API functions! */ -SOKOL_API_IMPL void sapp_show_mouse(bool show) { - if (_sapp.mouse.shown != show) { - #if defined(_SAPP_MACOS) - _sapp_macos_show_mouse(show); - #elif defined(_SAPP_WIN32) - _sapp_win32_show_mouse(show); - #elif defined(_SAPP_LINUX) - _sapp_x11_show_mouse(show); - #elif defined(_SAPP_UWP) - _sapp_uwp_show_mouse(show); - #endif - _sapp.mouse.shown = show; - } -} - -SOKOL_API_IMPL bool sapp_mouse_shown(void) { - return _sapp.mouse.shown; -} - -SOKOL_API_IMPL void sapp_lock_mouse(bool lock) { - #if defined(_SAPP_MACOS) - _sapp_macos_lock_mouse(lock); - #elif defined(_SAPP_EMSCRIPTEN) - _sapp_emsc_lock_mouse(lock); - #elif defined(_SAPP_WIN32) - _sapp_win32_lock_mouse(lock); - #elif defined(_SAPP_LINUX) - _sapp_x11_lock_mouse(lock); - #else - _sapp.mouse.locked = lock; - #endif -} - -SOKOL_API_IMPL bool sapp_mouse_locked(void) { - return _sapp.mouse.locked; -} - -SOKOL_API_IMPL void sapp_request_quit(void) { - _sapp.quit_requested = true; -} - -SOKOL_API_IMPL void sapp_cancel_quit(void) { - _sapp.quit_requested = false; -} - -SOKOL_API_IMPL void sapp_quit(void) { - _sapp.quit_ordered = true; -} - -SOKOL_API_IMPL void sapp_consume_event(void) { - _sapp.event_consumed = true; -} - -/* NOTE: on HTML5, sapp_set_clipboard_string() must be called from within event handler! */ -SOKOL_API_IMPL void sapp_set_clipboard_string(const char* str) { - SOKOL_ASSERT(_sapp.clipboard.enabled); - if (!_sapp.clipboard.enabled) { - return; - } - SOKOL_ASSERT(str); - #if defined(_SAPP_MACOS) - _sapp_macos_set_clipboard_string(str); - #elif defined(_SAPP_EMSCRIPTEN) - _sapp_emsc_set_clipboard_string(str); - #elif defined(_SAPP_WIN32) - _sapp_win32_set_clipboard_string(str); - #else - /* not implemented */ - #endif - _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); -} - -SOKOL_API_IMPL const char* sapp_get_clipboard_string(void) { - SOKOL_ASSERT(_sapp.clipboard.enabled); - if (!_sapp.clipboard.enabled) { - return ""; - } - #if defined(_SAPP_MACOS) - return _sapp_macos_get_clipboard_string(); - #elif defined(_SAPP_EMSCRIPTEN) - return _sapp.clipboard.buffer; - #elif defined(_SAPP_WIN32) - return _sapp_win32_get_clipboard_string(); - #else - /* not implemented */ - return _sapp.clipboard.buffer; - #endif -} - -SOKOL_API_IMPL void sapp_set_window_title(const char* title) { - SOKOL_ASSERT(title); - _sapp_strcpy(title, _sapp.window_title, sizeof(_sapp.window_title)); - #if defined(_SAPP_MACOS) - _sapp_macos_update_window_title(); - #elif defined(_SAPP_WIN32) - _sapp_win32_update_window_title(); - #elif defined(_SAPP_LINUX) - _sapp_x11_update_window_title(); - #endif -} - -SOKOL_API_IMPL void sapp_set_icon(const sapp_icon_desc* desc) { - SOKOL_ASSERT(desc); - if (desc->sokol_default) { - if (0 == _sapp.default_icon_pixels) { - _sapp_setup_default_icon(); - } - SOKOL_ASSERT(0 != _sapp.default_icon_pixels); - desc = &_sapp.default_icon_desc; - } - const int num_images = _sapp_icon_num_images(desc); - if (num_images == 0) { - return; - } - SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); - if (!_sapp_validate_icon_desc(desc, num_images)) { - return; - } - #if defined(_SAPP_MACOS) - _sapp_macos_set_icon(desc, num_images); - #elif defined(_SAPP_WIN32) - _sapp_win32_set_icon(desc, num_images); - #elif defined(_SAPP_LINUX) - _sapp_x11_set_icon(desc, num_images); - #elif defined(_SAPP_EMSCRIPTEN) - _sapp_emsc_set_icon(desc, num_images); - #endif -} - -SOKOL_API_IMPL int sapp_get_num_dropped_files(void) { - SOKOL_ASSERT(_sapp.drop.enabled); - return _sapp.drop.num_files; -} - -SOKOL_API_IMPL const char* sapp_get_dropped_file_path(int index) { - SOKOL_ASSERT(_sapp.drop.enabled); - SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files)); - SOKOL_ASSERT(_sapp.drop.buffer); - if (!_sapp.drop.enabled) { - return ""; - } - if ((index < 0) || (index >= _sapp.drop.max_files)) { - return ""; - } - return (const char*) _sapp_dropped_file_path_ptr(index); -} - -SOKOL_API_IMPL uint32_t sapp_html5_get_dropped_file_size(int index) { - SOKOL_ASSERT(_sapp.drop.enabled); - SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files)); - #if defined(_SAPP_EMSCRIPTEN) - if (!_sapp.drop.enabled) { - return 0; - } - return sapp_js_dropped_file_size(index); - #else - (void)index; - return 0; - #endif -} - -SOKOL_API_IMPL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request) { - SOKOL_ASSERT(_sapp.drop.enabled); - SOKOL_ASSERT(request); - SOKOL_ASSERT(request->callback); - SOKOL_ASSERT(request->buffer_ptr); - SOKOL_ASSERT(request->buffer_size > 0); - #if defined(_SAPP_EMSCRIPTEN) - const int index = request->dropped_file_index; - sapp_html5_fetch_error error_code = SAPP_HTML5_FETCH_ERROR_NO_ERROR; - if ((index < 0) || (index >= _sapp.drop.num_files)) { - error_code = SAPP_HTML5_FETCH_ERROR_OTHER; - } - if (sapp_html5_get_dropped_file_size(index) > request->buffer_size) { - error_code = SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL; - } - if (SAPP_HTML5_FETCH_ERROR_NO_ERROR != error_code) { - _sapp_emsc_invoke_fetch_cb(index, - false, // success - (int)error_code, - request->callback, - 0, // fetched_size - request->buffer_ptr, - request->buffer_size, - request->user_data); - } - else { - sapp_js_fetch_dropped_file(index, - request->callback, - request->buffer_ptr, - request->buffer_size, - request->user_data); - } - #else - (void)request; - #endif -} - -SOKOL_API_IMPL const void* sapp_metal_get_device(void) { - SOKOL_ASSERT(_sapp.valid); - #if defined(SOKOL_METAL) - #if defined(_SAPP_MACOS) - const void* obj = (__bridge const void*) _sapp.macos.mtl_device; - #else - const void* obj = (__bridge const void*) _sapp.ios.mtl_device; - #endif - SOKOL_ASSERT(obj); - return obj; - #else - return 0; - #endif -} - -SOKOL_API_IMPL const void* sapp_metal_get_renderpass_descriptor(void) { - SOKOL_ASSERT(_sapp.valid); - #if defined(SOKOL_METAL) - #if defined(_SAPP_MACOS) - const void* obj = (__bridge const void*) [_sapp.macos.view currentRenderPassDescriptor]; - #else - const void* obj = (__bridge const void*) [_sapp.ios.view currentRenderPassDescriptor]; - #endif - SOKOL_ASSERT(obj); - return obj; - #else - return 0; - #endif -} - -SOKOL_API_IMPL const void* sapp_metal_get_drawable(void) { - SOKOL_ASSERT(_sapp.valid); - #if defined(SOKOL_METAL) - #if defined(_SAPP_MACOS) - const void* obj = (__bridge const void*) [_sapp.macos.view currentDrawable]; - #else - const void* obj = (__bridge const void*) [_sapp.ios.view currentDrawable]; - #endif - SOKOL_ASSERT(obj); - return obj; - #else - return 0; - #endif -} - -SOKOL_API_IMPL const void* sapp_macos_get_window(void) { - #if defined(_SAPP_MACOS) - const void* obj = (__bridge const void*) _sapp.macos.window; - SOKOL_ASSERT(obj); - return obj; - #else - return 0; - #endif -} - -SOKOL_API_IMPL const void* sapp_ios_get_window(void) { - #if defined(_SAPP_IOS) - const void* obj = (__bridge const void*) _sapp.ios.window; - SOKOL_ASSERT(obj); - return obj; - #else - return 0; - #endif -} - -SOKOL_API_IMPL const void* sapp_d3d11_get_device(void) { - SOKOL_ASSERT(_sapp.valid); - #if defined(SOKOL_D3D11) - return _sapp.d3d11.device; - #else - return 0; - #endif -} - -SOKOL_API_IMPL const void* sapp_d3d11_get_device_context(void) { - SOKOL_ASSERT(_sapp.valid); - #if defined(SOKOL_D3D11) - return _sapp.d3d11.device_context; - #else - return 0; - #endif -} - -SOKOL_API_IMPL const void* sapp_d3d11_get_swap_chain(void) { - SOKOL_ASSERT(_sapp.valid); -#if defined(SOKOL_D3D11) - return _sapp.d3d11.swap_chain; -#else - return 0; -#endif -} - -SOKOL_API_IMPL const void* sapp_d3d11_get_render_target_view(void) { - SOKOL_ASSERT(_sapp.valid); - #if defined(SOKOL_D3D11) - if (_sapp.d3d11.msaa_rtv) { - return _sapp.d3d11.msaa_rtv; - } - else { - return _sapp.d3d11.rtv; - } - #else - return 0; - #endif -} - -SOKOL_API_IMPL const void* sapp_d3d11_get_depth_stencil_view(void) { - SOKOL_ASSERT(_sapp.valid); - #if defined(SOKOL_D3D11) - return _sapp.d3d11.dsv; - #else - return 0; - #endif -} - -SOKOL_API_IMPL const void* sapp_win32_get_hwnd(void) { - SOKOL_ASSERT(_sapp.valid); - #if defined(_SAPP_WIN32) - return _sapp.win32.hwnd; - #else - return 0; - #endif -} - -SOKOL_API_IMPL const void* sapp_wgpu_get_device(void) { - SOKOL_ASSERT(_sapp.valid); - #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) - return (const void*) _sapp.emsc.wgpu.device; - #else - return 0; - #endif -} - -SOKOL_API_IMPL const void* sapp_wgpu_get_render_view(void) { - SOKOL_ASSERT(_sapp.valid); - #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) - if (_sapp.sample_count > 1) { - return (const void*) _sapp.emsc.wgpu.msaa_view; - } - else { - return (const void*) _sapp.emsc.wgpu.swapchain_view; - } - #else - return 0; - #endif -} - -SOKOL_API_IMPL const void* sapp_wgpu_get_resolve_view(void) { - SOKOL_ASSERT(_sapp.valid); - #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) - if (_sapp.sample_count > 1) { - return (const void*) _sapp.emsc.wgpu.swapchain_view; - } - else { - return 0; - } - #else - return 0; - #endif -} - -SOKOL_API_IMPL const void* sapp_wgpu_get_depth_stencil_view(void) { - SOKOL_ASSERT(_sapp.valid); - #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) - return (const void*) _sapp.emsc.wgpu.depth_stencil_view; - #else - return 0; - #endif -} - -SOKOL_API_IMPL const void* sapp_android_get_native_activity(void) { - SOKOL_ASSERT(_sapp.valid); - #if defined(_SAPP_ANDROID) - return (void*)_sapp.android.activity; - #else - return 0; - #endif -} - -SOKOL_API_IMPL void sapp_html5_ask_leave_site(bool ask) { - _sapp.html5_ask_leave_site = ask; -} - -#endif /* SOKOL_APP_IMPL */ diff --git a/contrib/sokol/sokol_gfx.h b/contrib/sokol/sokol_gfx.h deleted file mode 100644 index 7005d7f..0000000 --- a/contrib/sokol/sokol_gfx.h +++ /dev/null @@ -1,16066 +0,0 @@ -#if defined(SOKOL_IMPL) && !defined(SOKOL_GFX_IMPL) -#define SOKOL_GFX_IMPL -#endif -#ifndef SOKOL_GFX_INCLUDED -/* - sokol_gfx.h -- simple 3D API wrapper - - Project URL: https://github.com/floooh/sokol - - Example code: https://github.com/floooh/sokol-samples - - Do this: - #define SOKOL_IMPL or - #define SOKOL_GFX_IMPL - before you include this file in *one* C or C++ file to create the - implementation. - - In the same place define one of the following to select the rendering - backend: - #define SOKOL_GLCORE33 - #define SOKOL_GLES2 - #define SOKOL_GLES3 - #define SOKOL_D3D11 - #define SOKOL_METAL - #define SOKOL_WGPU - #define SOKOL_DUMMY_BACKEND - - I.e. for the GL 3.3 Core Profile it should look like this: - - #include ... - #include ... - #define SOKOL_IMPL - #define SOKOL_GLCORE33 - #include "sokol_gfx.h" - - The dummy backend replaces the platform-specific backend code with empty - stub functions. This is useful for writing tests that need to run on the - command line. - - Optionally provide the following defines with your own implementations: - - SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) - SOKOL_MALLOC(s) - your own malloc function (default: malloc(s)) - SOKOL_FREE(p) - your own free function (default: free(p)) - SOKOL_LOG(msg) - your own logging function (default: puts(msg)) - SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) - SOKOL_GFX_API_DECL - public function declaration prefix (default: extern) - SOKOL_API_DECL - same as SOKOL_GFX_API_DECL - SOKOL_API_IMPL - public function implementation prefix (default: -) - SOKOL_TRACE_HOOKS - enable trace hook callbacks (search below for TRACE HOOKS) - SOKOL_EXTERNAL_GL_LOADER - indicates that you're using your own GL loader, in this case - sokol_gfx.h will not include any platform GL headers and disable - the integrated Win32 GL loader - - If sokol_gfx.h is compiled as a DLL, define the following before - including the declaration or implementation: - - SOKOL_DLL - - On Windows, SOKOL_DLL will define SOKOL_GFX_API_DECL as __declspec(dllexport) - or __declspec(dllimport) as needed. - - If you want to compile without deprecated structs and functions, - define: - - SOKOL_NO_DEPRECATED - - Optionally define the following to force debug checks and validations - even in release mode: - - SOKOL_DEBUG - by default this is defined if _DEBUG is defined - - sokol_gfx DOES NOT: - =================== - - create a window or the 3D-API context/device, you must do this - before sokol_gfx is initialized, and pass any required information - (like 3D device pointers) to the sokol_gfx initialization call - - - present the rendered frame, how this is done exactly usually depends - on how the window and 3D-API context/device was created - - - provide a unified shader language, instead 3D-API-specific shader - source-code or shader-bytecode must be provided (for the "official" - offline shader cross-compiler, see here: - https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md) - - STEP BY STEP - ============ - --- to initialize sokol_gfx, after creating a window and a 3D-API - context/device, call: - - sg_setup(const sg_desc*) - - --- create resource objects (at least buffers, shaders and pipelines, - and optionally images and passes): - - sg_buffer sg_make_buffer(const sg_buffer_desc*) - sg_image sg_make_image(const sg_image_desc*) - sg_shader sg_make_shader(const sg_shader_desc*) - sg_pipeline sg_make_pipeline(const sg_pipeline_desc*) - sg_pass sg_make_pass(const sg_pass_desc*) - - --- start rendering to the default frame buffer with: - - sg_begin_default_pass(const sg_pass_action* action, int width, int height) - - ...or alternatively with: - - sg_begin_default_passf(const sg_pass_action* action, float width, float height) - - ...which takes the framebuffer width and height as float values. - - --- or start rendering to an offscreen framebuffer with: - - sg_begin_pass(sg_pass pass, const sg_pass_action* action) - - --- set the pipeline state for the next draw call with: - - sg_apply_pipeline(sg_pipeline pip) - - --- fill an sg_bindings struct with the resource bindings for the next - draw call (1..N vertex buffers, 0 or 1 index buffer, 0..N image objects - to use as textures each on the vertex-shader- and fragment-shader-stage - and then call - - sg_apply_bindings(const sg_bindings* bindings) - - to update the resource bindings - - --- optionally update shader uniform data with: - - sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data) - - Read the section 'UNIFORM DATA LAYOUT' to learn about the expected memory layout - of the uniform data passed into sg_apply_uniforms(). - - --- kick off a draw call with: - - sg_draw(int base_element, int num_elements, int num_instances) - - The sg_draw() function unifies all the different ways to render primitives - in a single call (indexed vs non-indexed rendering, and instanced vs non-instanced - rendering). In case of indexed rendering, base_element and num_element specify - indices in the currently bound index buffer. In case of non-indexed rendering - base_element and num_elements specify vertices in the currently bound - vertex-buffer(s). To perform instanced rendering, the rendering pipeline - must be setup for instancing (see sg_pipeline_desc below), a separate vertex buffer - containing per-instance data must be bound, and the num_instances parameter - must be > 1. - - --- finish the current rendering pass with: - - sg_end_pass() - - --- when done with the current frame, call - - sg_commit() - - --- at the end of your program, shutdown sokol_gfx with: - - sg_shutdown() - - --- if you need to destroy resources before sg_shutdown(), call: - - sg_destroy_buffer(sg_buffer buf) - sg_destroy_image(sg_image img) - sg_destroy_shader(sg_shader shd) - sg_destroy_pipeline(sg_pipeline pip) - sg_destroy_pass(sg_pass pass) - - --- to set a new viewport rectangle, call - - sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) - - ...or if you want to specifiy the viewport rectangle with float values: - - sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) - - --- to set a new scissor rect, call: - - sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) - - ...or with float values: - - sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left) - - Both sg_apply_viewport() and sg_apply_scissor_rect() must be called - inside a rendering pass - - Note that sg_begin_default_pass() and sg_begin_pass() will reset both the - viewport and scissor rectangles to cover the entire framebuffer. - - --- to update (overwrite) the content of buffer and image resources, call: - - sg_update_buffer(sg_buffer buf, const sg_range* data) - sg_update_image(sg_image img, const sg_image_data* data) - - Buffers and images to be updated must have been created with - SG_USAGE_DYNAMIC or SG_USAGE_STREAM - - Only one update per frame is allowed for buffer and image resources when - using the sg_update_*() functions. The rationale is to have a simple - countermeasure to avoid the CPU scribbling over data the GPU is currently - using, or the CPU having to wait for the GPU - - Buffer and image updates can be partial, as long as a rendering - operation only references the valid (updated) data in the - buffer or image. - - --- to append a chunk of data to a buffer resource, call: - - int sg_append_buffer(sg_buffer buf, const sg_range* data) - - The difference to sg_update_buffer() is that sg_append_buffer() - can be called multiple times per frame to append new data to the - buffer piece by piece, optionally interleaved with draw calls referencing - the previously written data. - - sg_append_buffer() returns a byte offset to the start of the - written data, this offset can be assigned to - sg_bindings.vertex_buffer_offsets[n] or - sg_bindings.index_buffer_offset - - Code example: - - for (...) { - const void* data = ...; - const int num_bytes = ...; - int offset = sg_append_buffer(buf, &(sg_range) { .ptr=data, .size=num_bytes }); - bindings.vertex_buffer_offsets[0] = offset; - sg_apply_pipeline(pip); - sg_apply_bindings(&bindings); - sg_apply_uniforms(...); - sg_draw(...); - } - - A buffer to be used with sg_append_buffer() must have been created - with SG_USAGE_DYNAMIC or SG_USAGE_STREAM. - - If the application appends more data to the buffer then fits into - the buffer, the buffer will go into the "overflow" state for the - rest of the frame. - - Any draw calls attempting to render an overflown buffer will be - silently dropped (in debug mode this will also result in a - validation error). - - You can also check manually if a buffer is in overflow-state by calling - - bool sg_query_buffer_overflow(sg_buffer buf) - - NOTE: Due to restrictions in underlying 3D-APIs, appended chunks of - data will be 4-byte aligned in the destination buffer. This means - that there will be gaps in index buffers containing 16-bit indices - when the number of indices in a call to sg_append_buffer() is - odd. This isn't a problem when each call to sg_append_buffer() - is associated with one draw call, but will be problematic when - a single indexed draw call spans several appended chunks of indices. - - --- to check at runtime for optional features, limits and pixelformat support, - call: - - sg_features sg_query_features() - sg_limits sg_query_limits() - sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) - - --- if you need to call into the underlying 3D-API directly, you must call: - - sg_reset_state_cache() - - ...before calling sokol_gfx functions again - - --- you can inspect the original sg_desc structure handed to sg_setup() - by calling sg_query_desc(). This will return an sg_desc struct with - the default values patched in instead of any zero-initialized values - - --- you can inspect various internal resource attributes via: - - sg_buffer_info sg_query_buffer_info(sg_buffer buf) - sg_image_info sg_query_image_info(sg_image img) - sg_shader_info sg_query_shader_info(sg_shader shd) - sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip) - sg_pass_info sg_query_pass_info(sg_pass pass) - - ...please note that the returned info-structs are tied quite closely - to sokol_gfx.h internals, and may change more often than other - public API functions and structs. - - --- you can ask at runtime what backend sokol_gfx.h has been compiled - for, or whether the GLES3 backend had to fall back to GLES2 with: - - sg_backend sg_query_backend(void) - - --- you can query the default resource creation parameters through the functions - - sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) - sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) - sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) - sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) - sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc) - - These functions take a pointer to a desc structure which may contain - zero-initialized items for default values. These zero-init values - will be replaced with their concrete values in the returned desc - struct. - - ON INITIALIZATION: - ================== - When calling sg_setup(), a pointer to an sg_desc struct must be provided - which contains initialization options. These options provide two types - of information to sokol-gfx: - - (1) upper bounds and limits needed to allocate various internal - data structures: - - the max number of resources of each type that can - be alive at the same time, this is used for allocating - internal pools - - the max overall size of uniform data that can be - updated per frame, including a worst-case alignment - per uniform update (this worst-case alignment is 256 bytes) - - the max size of all dynamic resource updates (sg_update_buffer, - sg_append_buffer and sg_update_image) per frame - - the max number of entries in the texture sampler cache - (how many unique texture sampler can exist at the same time) - Not all of those limit values are used by all backends, but it is - good practice to provide them none-the-less. - - (2) 3D-API "context information" (sometimes also called "bindings"): - sokol_gfx.h doesn't create or initialize 3D API objects which are - closely related to the presentation layer (this includes the "rendering - device", the swapchain, and any objects which depend on the - swapchain). These API objects (or callback functions to obtain - them, if those objects might change between frames), must - be provided in a nested sg_context_desc struct inside the - sg_desc struct. If sokol_gfx.h is used together with - sokol_app.h, have a look at the sokol_glue.h header which provides - a convenience function to get a sg_context_desc struct filled out - with context information provided by sokol_app.h - - See the documention block of the sg_desc struct below for more information. - - - UNIFORM DATA LAYOUT: - ==================== - NOTE: if you use the sokol-shdc shader compiler tool, you don't need to worry - about the following details. - - The data that's passed into the sg_apply_uniforms() function must adhere to - specific layout rules so that the GPU shader finds the uniform block - items at the right offset. - - For the D3D11 and Metal backends, sokol-gfx only cares about the size of uniform - blocks, but not about the internal layout. The data will just be copied into - a uniform/constant buffer in a single operation and it's up you to arrange the - CPU-side layout so that it matches the GPU side layout. This also means that with - the D3D11 and Metal backends you are not limited to a 'cross-platform' subset - of uniform variable types. - - If you ever only use one of the D3D11, Metal *or* WebGPU backend, you can stop reading here. - - For the GL backends, the internal layout of uniform blocks matters though, - and you are limited to a small number of uniform variable types. This is - because sokol-gfx must be able to locate the uniform block members in order - to upload them to the GPU with glUniformXXX() calls. - - To describe the uniform block layout to sokol-gfx, the following information - must be passed to the sg_make_shader() call in the sg_shader_desc struct: - - - a hint about the used packing rule (either SG_UNIFORMLAYOUT_NATIVE or - SG_UNIFORMLAYOUT_STD140) - - a list of the uniform block members types in the correct order they - appear on the CPU side - - For example if the GLSL shader has the following uniform declarations: - - uniform mat4 mvp; - uniform vec2 offset0; - uniform vec2 offset1; - uniform vec2 offset2; - - ...and on the CPU side, there's a similar C struct: - - typedef struct { - float mvp[16]; - float offset0[2]; - float offset1[2]; - float offset2[2]; - } params_t; - - ...the uniform block description in the sg_shader_desc must look like this: - - sg_shader_desc desc = { - .vs.uniform_blocks[0] = { - .size = sizeof(params_t), - .layout = SG_UNIFORMLAYOUT_NATIVE, // this is the default and can be omitted - .uniforms = { - // order must be the same as in 'params_t': - [0] = { .name = "mvp", .type = SG_UNIFORMTYPE_MAT4 }, - [1] = { .name = "offset0", .type = SG_UNIFORMTYPE_VEC2 }, - [2] = { .name = "offset1", .type = SG_UNIFORMTYPE_VEC2 }, - [3] = { .name = "offset2", .type = SG_UNIFORMTYPE_VEC2 }, - } - } - }; - - With this information sokol-gfx can now compute the correct offsets of the data items - within the uniform block struct. - - The SG_UNIFORMLAYOUT_NATIVE packing rule works fine if only the GL backends are used, - but for proper D3D11/Metal/GL a subset of the std140 layout must be used which is - described in the next section: - - - CROSS-BACKEND COMMON UNIFORM DATA LAYOUT - ======================================== - For cross-platform / cross-3D-backend code it is important that the same uniform block - layout on the CPU side can be used for all sokol-gfx backends. To achieve this, - a common subset of the std140 layout must be used: - - - The uniform block layout hint in sg_shader_desc must be explicitely set to - SG_UNIFORMLAYOUT_STD140. - - Only the following GLSL uniform types can be used (with their associated sokol-gfx enums): - - float => SG_UNIFORMTYPE_FLOAT - - vec2 => SG_UNIFORMTYPE_FLOAT2 - - vec3 => SG_UNIFORMTYPE_FLOAT3 - - vec4 => SG_UNIFORMTYPE_FLOAT4 - - int => SG_UNIFORMTYPE_INT - - ivec2 => SG_UNIFORMTYPE_INT2 - - ivec3 => SG_UNIFORMTYPE_INT3 - - ivec4 => SG_UNIFORMTYPE_INT4 - - mat4 => SG_UNIFORMTYPE_MAT4 - - Alignment for those types must be as follows (in bytes): - - float => 4 - - vec2 => 8 - - vec3 => 16 - - vec4 => 16 - - int => 4 - - ivec2 => 8 - - ivec3 => 16 - - ivec4 => 16 - - mat4 => 16 - - Arrays are only allowed for the following types: vec4, int4, mat4. - - Note that the HLSL cbuffer layout rules are slightly different from the - std140 layout rules, this means that the cbuffer declarations in HLSL code - must be tweaked so that the layout is compatible with std140. - - The by far easiest way to tacke the common uniform block layout problem is - to use the sokol-shdc shader cross-compiler tool! - - - BACKEND-SPECIFIC TOPICS: - ======================== - --- The GL backends need to know about the internal structure of uniform - blocks, and the texture sampler-name and -type. The uniform layout details - are described in the UNIFORM DATA LAYOUT section above. - - // uniform block structure and texture image definition in sg_shader_desc: - sg_shader_desc desc = { - // uniform block description (size and internal structure) - .vs.uniform_blocks[0] = { - ... - }, - // one texture on the fragment-shader-stage, GLES2/WebGL needs name and image type - .fs.images[0] = { .name="tex", .type=SG_IMAGETYPE_ARRAY } - ... - }; - - --- the Metal and D3D11 backends only need to know the size of uniform blocks, - not their internal member structure, and they only need to know - the type of a texture sampler, not its name: - - sg_shader_desc desc = { - .vs.uniform_blocks[0].size = sizeof(params_t), - .fs.images[0].type = SG_IMAGETYPE_ARRAY, - ... - }; - - --- when creating a shader object, GLES2/WebGL need to know the vertex - attribute names as used in the vertex shader: - - sg_shader_desc desc = { - .attrs = { - [0] = { .name="position" }, - [1] = { .name="color1" } - } - }; - - The vertex attribute names provided when creating a shader will be - used later in sg_create_pipeline() for matching the vertex layout - to vertex shader inputs. - - --- on D3D11 you need to provide a semantic name and semantic index in the - shader description struct instead (see the D3D11 documentation on - D3D11_INPUT_ELEMENT_DESC for details): - - sg_shader_desc desc = { - .attrs = { - [0] = { .sem_name="POSITION", .sem_index=0 } - [1] = { .sem_name="COLOR", .sem_index=1 } - } - }; - - The provided semantic information will be used later in sg_create_pipeline() - to match the vertex layout to vertex shader inputs. - - --- on D3D11, and when passing HLSL source code (instead of byte code) to shader - creation, you can optionally define the shader model targets on the vertex - stage: - - sg_shader_Desc desc = { - .vs = { - ... - .d3d11_target = "vs_5_0" - }, - .fs = { - ... - .d3d11_target = "ps_5_0" - } - }; - - The default targets are "ps_4_0" and "fs_4_0". Note that those target names - are only used when compiling shaders from source. They are ignored when - creating a shader from bytecode. - - --- on Metal, GL 3.3 or GLES3/WebGL2, you don't need to provide an attribute - name or semantic name, since vertex attributes can be bound by their slot index - (this is mandatory in Metal, and optional in GL): - - sg_pipeline_desc desc = { - .layout = { - .attrs = { - [0] = { .format=SG_VERTEXFORMAT_FLOAT3 }, - [1] = { .format=SG_VERTEXFORMAT_FLOAT4 } - } - } - }; - - WORKING WITH CONTEXTS - ===================== - sokol-gfx allows to switch between different rendering contexts and - associate resource objects with contexts. This is useful to - create GL applications that render into multiple windows. - - A rendering context keeps track of all resources created while - the context is active. When the context is destroyed, all resources - "belonging to the context" are destroyed as well. - - A default context will be created and activated implicitly in - sg_setup(), and destroyed in sg_shutdown(). So for a typical application - which *doesn't* use multiple contexts, nothing changes, and calling - the context functions isn't necessary. - - Three functions have been added to work with contexts: - - --- sg_context sg_setup_context(): - This must be called once after a GL context has been created and - made active. - - --- void sg_activate_context(sg_context ctx) - This must be called after making a different GL context active. - Apart from 3D-API-specific actions, the call to sg_activate_context() - will internally call sg_reset_state_cache(). - - --- void sg_discard_context(sg_context ctx) - This must be called right before a GL context is destroyed and - will destroy all resources associated with the context (that - have been created while the context was active) The GL context must be - active at the time sg_discard_context(sg_context ctx) is called. - - Also note that resources (buffers, images, shaders and pipelines) must - only be used or destroyed while the same GL context is active that - was also active while the resource was created (an exception is - resource sharing on GL, such resources can be used while - another context is active, but must still be destroyed under - the same context that was active during creation). - - For more information, check out the multiwindow-glfw sample: - - https://github.com/floooh/sokol-samples/blob/master/glfw/multiwindow-glfw.c - - TRACE HOOKS: - ============ - sokol_gfx.h optionally allows to install "trace hook" callbacks for - each public API functions. When a public API function is called, and - a trace hook callback has been installed for this function, the - callback will be invoked with the parameters and result of the function. - This is useful for things like debugging- and profiling-tools, or - keeping track of resource creation and destruction. - - To use the trace hook feature: - - --- Define SOKOL_TRACE_HOOKS before including the implementation. - - --- Setup an sg_trace_hooks structure with your callback function - pointers (keep all function pointers you're not interested - in zero-initialized), optionally set the user_data member - in the sg_trace_hooks struct. - - --- Install the trace hooks by calling sg_install_trace_hooks(), - the return value of this function is another sg_trace_hooks - struct which contains the previously set of trace hooks. - You should keep this struct around, and call those previous - functions pointers from your own trace callbacks for proper - chaining. - - As an example of how trace hooks are used, have a look at the - imgui/sokol_gfx_imgui.h header which implements a realtime - debugging UI for sokol_gfx.h on top of Dear ImGui. - - A NOTE ON PORTABLE PACKED VERTEX FORMATS: - ========================================= - There are two things to consider when using packed - vertex formats like UBYTE4, SHORT2, etc which need to work - across all backends: - - - D3D11 can only convert *normalized* vertex formats to - floating point during vertex fetch, normalized formats - have a trailing 'N', and are "normalized" to a range - -1.0..+1.0 (for the signed formats) or 0.0..1.0 (for the - unsigned formats): - - - SG_VERTEXFORMAT_BYTE4N - - SG_VERTEXFORMAT_UBYTE4N - - SG_VERTEXFORMAT_SHORT2N - - SG_VERTEXFORMAT_USHORT2N - - SG_VERTEXFORMAT_SHORT4N - - SG_VERTEXFORMAT_USHORT4N - - D3D11 will not convert *non-normalized* vertex formats to floating point - vertex shader inputs, those can only be uses with the *ivecn* vertex shader - input types when D3D11 is used as backend (GL and Metal can use both formats) - - - SG_VERTEXFORMAT_BYTE4, - - SG_VERTEXFORMAT_UBYTE4 - - SG_VERTEXFORMAT_SHORT2 - - SG_VERTEXFORMAT_SHORT4 - - - WebGL/GLES2 cannot use integer vertex shader inputs (int or ivecn) - - - SG_VERTEXFORMAT_UINT10_N2 is not supported on WebGL/GLES2 - - So for a vertex input layout which works on all platforms, only use the following - vertex formats, and if needed "expand" the normalized vertex shader - inputs in the vertex shader by multiplying with 127.0, 255.0, 32767.0 or - 65535.0: - - - SG_VERTEXFORMAT_FLOAT, - - SG_VERTEXFORMAT_FLOAT2, - - SG_VERTEXFORMAT_FLOAT3, - - SG_VERTEXFORMAT_FLOAT4, - - SG_VERTEXFORMAT_BYTE4N, - - SG_VERTEXFORMAT_UBYTE4N, - - SG_VERTEXFORMAT_SHORT2N, - - SG_VERTEXFORMAT_USHORT2N - - SG_VERTEXFORMAT_SHORT4N, - - SG_VERTEXFORMAT_USHORT4N - - TODO: - ==== - - talk about asynchronous resource creation - - zlib/libpng license - - Copyright (c) 2018 Andre Weissflog - - 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. -*/ -#define SOKOL_GFX_INCLUDED (1) -#include // size_t -#include -#include - -#if defined(SOKOL_API_DECL) && !defined(SOKOL_GFX_API_DECL) -#define SOKOL_GFX_API_DECL SOKOL_API_DECL -#endif -#ifndef SOKOL_GFX_API_DECL -#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GFX_IMPL) -#define SOKOL_GFX_API_DECL __declspec(dllexport) -#elif defined(_WIN32) && defined(SOKOL_DLL) -#define SOKOL_GFX_API_DECL __declspec(dllimport) -#else -#define SOKOL_GFX_API_DECL extern -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* - Resource id typedefs: - - sg_buffer: vertex- and index-buffers - sg_image: textures and render targets - sg_shader: vertex- and fragment-shaders, uniform blocks - sg_pipeline: associated shader and vertex-layouts, and render states - sg_pass: a bundle of render targets and actions on them - sg_context: a 'context handle' for switching between 3D-API contexts - - Instead of pointers, resource creation functions return a 32-bit - number which uniquely identifies the resource object. - - The 32-bit resource id is split into a 16-bit pool index in the lower bits, - and a 16-bit 'unique counter' in the upper bits. The index allows fast - pool lookups, and combined with the unique-mask it allows to detect - 'dangling accesses' (trying to use an object which no longer exists, and - its pool slot has been reused for a new object) - - The resource ids are wrapped into a struct so that the compiler - can complain when the wrong resource type is used. -*/ -typedef struct sg_buffer { uint32_t id; } sg_buffer; -typedef struct sg_image { uint32_t id; } sg_image; -typedef struct sg_shader { uint32_t id; } sg_shader; -typedef struct sg_pipeline { uint32_t id; } sg_pipeline; -typedef struct sg_pass { uint32_t id; } sg_pass; -typedef struct sg_context { uint32_t id; } sg_context; - -/* - sg_range is a pointer-size-pair struct used to pass memory blobs into - sokol-gfx. When initialized from a value type (array or struct), you can - use the SG_RANGE() macro to build an sg_range struct. For functions which - take either a sg_range pointer, or a (C++) sg_range reference, use the - SG_RANGE_REF macro as a solution which compiles both in C and C++. -*/ -typedef struct sg_range { - const void* ptr; - size_t size; -} sg_range; - -// disabling this for every includer isn't great, but the warnings are also quite pointless -#if defined(_MSC_VER) -#pragma warning(disable:4221) /* /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' */ -#pragma warning(disable:4204) /* VS2015: nonstandard extension used: non-constant aggregate initializer */ -#endif -#if defined(__cplusplus) -#define SG_RANGE(x) sg_range{ &x, sizeof(x) } -#define SG_RANGE_REF(x) sg_range{ &x, sizeof(x) } -#else -#define SG_RANGE(x) (sg_range){ &x, sizeof(x) } -#define SG_RANGE_REF(x) &(sg_range){ &x, sizeof(x) } -#endif - -// various compile-time constants -enum { - SG_INVALID_ID = 0, - SG_NUM_SHADER_STAGES = 2, - SG_NUM_INFLIGHT_FRAMES = 2, - SG_MAX_COLOR_ATTACHMENTS = 4, - SG_MAX_SHADERSTAGE_BUFFERS = 8, - SG_MAX_SHADERSTAGE_IMAGES = 12, - SG_MAX_SHADERSTAGE_UBS = 4, - SG_MAX_UB_MEMBERS = 16, - SG_MAX_VERTEX_ATTRIBUTES = 16, /* NOTE: actual max vertex attrs can be less on GLES2, see sg_limits! */ - SG_MAX_MIPMAPS = 16, - SG_MAX_TEXTUREARRAY_LAYERS = 128 -}; - -/* - sg_color - - An RGBA color value. -*/ -typedef struct sg_color { float r, g, b, a; } sg_color; - -/* - sg_backend - - The active 3D-API backend, use the function sg_query_backend() - to get the currently active backend. - - NOTE that SG_BACKEND_GLES2 will be returned if sokol-gfx was - compiled with SOKOL_GLES3, but the runtime platform doesn't support - GLES3/WebGL2 and sokol-gfx had to fallback to GLES2/WebGL. -*/ -typedef enum sg_backend { - SG_BACKEND_GLCORE33, - SG_BACKEND_GLES2, - SG_BACKEND_GLES3, - SG_BACKEND_D3D11, - SG_BACKEND_METAL_IOS, - SG_BACKEND_METAL_MACOS, - SG_BACKEND_METAL_SIMULATOR, - SG_BACKEND_WGPU, - SG_BACKEND_DUMMY, -} sg_backend; - -/* - sg_pixel_format - - sokol_gfx.h basically uses the same pixel formats as WebGPU, since these - are supported on most newer GPUs. GLES2 and WebGL only supports a much - smaller subset of actually available pixel formats. Call - sg_query_pixelformat() to check at runtime if a pixel format supports the - desired features. - - A pixelformat name consist of three parts: - - - components (R, RG, RGB or RGBA) - - bit width per component (8, 16 or 32) - - component data type: - - unsigned normalized (no postfix) - - signed normalized (SN postfix) - - unsigned integer (UI postfix) - - signed integer (SI postfix) - - float (F postfix) - - Not all pixel formats can be used for everything, call sg_query_pixelformat() - to inspect the capabilities of a given pixelformat. The function returns - an sg_pixelformat_info struct with the following bool members: - - - sample: the pixelformat can be sampled as texture at least with - nearest filtering - - filter: the pixelformat can be samples as texture with linear - filtering - - render: the pixelformat can be used for render targets - - blend: blending is supported when using the pixelformat for - render targets - - msaa: multisample-antialiasing is supported when using the - pixelformat for render targets - - depth: the pixelformat can be used for depth-stencil attachments - - When targeting GLES2/WebGL, the only safe formats to use - as texture are SG_PIXELFORMAT_R8 and SG_PIXELFORMAT_RGBA8. For rendering - in GLES2/WebGL, only SG_PIXELFORMAT_RGBA8 is safe. All other formats - must be checked via sg_query_pixelformats(). - - The default pixel format for texture images is SG_PIXELFORMAT_RGBA8. - - The default pixel format for render target images is platform-dependent: - - for Metal and D3D11 it is SG_PIXELFORMAT_BGRA8 - - for GL backends it is SG_PIXELFORMAT_RGBA8 - - This is mainly because of the default framebuffer which is setup outside - of sokol_gfx.h. On some backends, using BGRA for the default frame buffer - allows more efficient frame flips. For your own offscreen-render-targets, - use whatever renderable pixel format is convenient for you. -*/ -typedef enum sg_pixel_format { - _SG_PIXELFORMAT_DEFAULT, /* value 0 reserved for default-init */ - SG_PIXELFORMAT_NONE, - - SG_PIXELFORMAT_R8, - SG_PIXELFORMAT_R8SN, - SG_PIXELFORMAT_R8UI, - SG_PIXELFORMAT_R8SI, - - SG_PIXELFORMAT_R16, - SG_PIXELFORMAT_R16SN, - SG_PIXELFORMAT_R16UI, - SG_PIXELFORMAT_R16SI, - SG_PIXELFORMAT_R16F, - SG_PIXELFORMAT_RG8, - SG_PIXELFORMAT_RG8SN, - SG_PIXELFORMAT_RG8UI, - SG_PIXELFORMAT_RG8SI, - - SG_PIXELFORMAT_R32UI, - SG_PIXELFORMAT_R32SI, - SG_PIXELFORMAT_R32F, - SG_PIXELFORMAT_RG16, - SG_PIXELFORMAT_RG16SN, - SG_PIXELFORMAT_RG16UI, - SG_PIXELFORMAT_RG16SI, - SG_PIXELFORMAT_RG16F, - SG_PIXELFORMAT_RGBA8, - SG_PIXELFORMAT_RGBA8SN, - SG_PIXELFORMAT_RGBA8UI, - SG_PIXELFORMAT_RGBA8SI, - SG_PIXELFORMAT_BGRA8, - SG_PIXELFORMAT_RGB10A2, - SG_PIXELFORMAT_RG11B10F, - - SG_PIXELFORMAT_RG32UI, - SG_PIXELFORMAT_RG32SI, - SG_PIXELFORMAT_RG32F, - SG_PIXELFORMAT_RGBA16, - SG_PIXELFORMAT_RGBA16SN, - SG_PIXELFORMAT_RGBA16UI, - SG_PIXELFORMAT_RGBA16SI, - SG_PIXELFORMAT_RGBA16F, - - SG_PIXELFORMAT_RGBA32UI, - SG_PIXELFORMAT_RGBA32SI, - SG_PIXELFORMAT_RGBA32F, - - SG_PIXELFORMAT_DEPTH, - SG_PIXELFORMAT_DEPTH_STENCIL, - - SG_PIXELFORMAT_BC1_RGBA, - SG_PIXELFORMAT_BC2_RGBA, - SG_PIXELFORMAT_BC3_RGBA, - SG_PIXELFORMAT_BC4_R, - SG_PIXELFORMAT_BC4_RSN, - SG_PIXELFORMAT_BC5_RG, - SG_PIXELFORMAT_BC5_RGSN, - SG_PIXELFORMAT_BC6H_RGBF, - SG_PIXELFORMAT_BC6H_RGBUF, - SG_PIXELFORMAT_BC7_RGBA, - SG_PIXELFORMAT_PVRTC_RGB_2BPP, - SG_PIXELFORMAT_PVRTC_RGB_4BPP, - SG_PIXELFORMAT_PVRTC_RGBA_2BPP, - SG_PIXELFORMAT_PVRTC_RGBA_4BPP, - SG_PIXELFORMAT_ETC2_RGB8, - SG_PIXELFORMAT_ETC2_RGB8A1, - SG_PIXELFORMAT_ETC2_RGBA8, - SG_PIXELFORMAT_ETC2_RG11, - SG_PIXELFORMAT_ETC2_RG11SN, - - _SG_PIXELFORMAT_NUM, - _SG_PIXELFORMAT_FORCE_U32 = 0x7FFFFFFF -} sg_pixel_format; - -/* - Runtime information about a pixel format, returned - by sg_query_pixelformat(). -*/ -typedef struct sg_pixelformat_info { - bool sample; // pixel format can be sampled in shaders - bool filter; // pixel format can be sampled with filtering - bool render; // pixel format can be used as render target - bool blend; // alpha-blending is supported - bool msaa; // pixel format can be used as MSAA render target - bool depth; // pixel format is a depth format - #if defined(SOKOL_ZIG_BINDINGS) - uint32_t __pad[3]; - #endif -} sg_pixelformat_info; - -/* - Runtime information about available optional features, - returned by sg_query_features() -*/ -typedef struct sg_features { - bool instancing; // hardware instancing supported - bool origin_top_left; // framebuffer and texture origin is in top left corner - bool multiple_render_targets; // offscreen render passes can have multiple render targets attached - bool msaa_render_targets; // offscreen render passes support MSAA antialiasing - bool imagetype_3d; // creation of SG_IMAGETYPE_3D images is supported - bool imagetype_array; // creation of SG_IMAGETYPE_ARRAY images is supported - bool image_clamp_to_border; // border color and clamp-to-border UV-wrap mode is supported - bool mrt_independent_blend_state; // multiple-render-target rendering can use per-render-target blend state - bool mrt_independent_write_mask; // multiple-render-target rendering can use per-render-target color write masks - #if defined(SOKOL_ZIG_BINDINGS) - uint32_t __pad[3]; - #endif -} sg_features; - -/* - Runtime information about resource limits, returned by sg_query_limit() -*/ -typedef struct sg_limits { - int max_image_size_2d; // max width/height of SG_IMAGETYPE_2D images - int max_image_size_cube; // max width/height of SG_IMAGETYPE_CUBE images - int max_image_size_3d; // max width/height/depth of SG_IMAGETYPE_3D images - int max_image_size_array; // max width/height of SG_IMAGETYPE_ARRAY images - int max_image_array_layers; // max number of layers in SG_IMAGETYPE_ARRAY images - int max_vertex_attrs; // <= SG_MAX_VERTEX_ATTRIBUTES or less (on some GLES2 impls) - int gl_max_vertex_uniform_vectors; // <= GL_MAX_VERTEX_UNIFORM_VECTORS (only on GL backends) -} sg_limits; - -/* - sg_resource_state - - The current state of a resource in its resource pool. - Resources start in the INITIAL state, which means the - pool slot is unoccupied and can be allocated. When a resource is - created, first an id is allocated, and the resource pool slot - is set to state ALLOC. After allocation, the resource is - initialized, which may result in the VALID or FAILED state. The - reason why allocation and initialization are separate is because - some resource types (e.g. buffers and images) might be asynchronously - initialized by the user application. If a resource which is not - in the VALID state is attempted to be used for rendering, rendering - operations will silently be dropped. - - The special INVALID state is returned in sg_query_xxx_state() if no - resource object exists for the provided resource id. -*/ -typedef enum sg_resource_state { - SG_RESOURCESTATE_INITIAL, - SG_RESOURCESTATE_ALLOC, - SG_RESOURCESTATE_VALID, - SG_RESOURCESTATE_FAILED, - SG_RESOURCESTATE_INVALID, - _SG_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF -} sg_resource_state; - -/* - sg_usage - - A resource usage hint describing the update strategy of - buffers and images. This is used in the sg_buffer_desc.usage - and sg_image_desc.usage members when creating buffers - and images: - - SG_USAGE_IMMUTABLE: the resource will never be updated with - new data, instead the content of the - resource must be provided on creation - SG_USAGE_DYNAMIC: the resource will be updated infrequently - with new data (this could range from "once - after creation", to "quite often but not - every frame") - SG_USAGE_STREAM: the resource will be updated each frame - with new content - - The rendering backends use this hint to prevent that the - CPU needs to wait for the GPU when attempting to update - a resource that might be currently accessed by the GPU. - - Resource content is updated with the functions sg_update_buffer() or - sg_append_buffer() for buffer objects, and sg_update_image() for image - objects. For the sg_update_*() functions, only one update is allowed per - frame and resource object, while sg_append_buffer() can be called - multiple times per frame on the same buffer. The application must update - all data required for rendering (this means that the update data can be - smaller than the resource size, if only a part of the overall resource - size is used for rendering, you only need to make sure that the data that - *is* used is valid). - - The default usage is SG_USAGE_IMMUTABLE. -*/ -typedef enum sg_usage { - _SG_USAGE_DEFAULT, /* value 0 reserved for default-init */ - SG_USAGE_IMMUTABLE, - SG_USAGE_DYNAMIC, - SG_USAGE_STREAM, - _SG_USAGE_NUM, - _SG_USAGE_FORCE_U32 = 0x7FFFFFFF -} sg_usage; - -/* - sg_buffer_type - - This indicates whether a buffer contains vertex- or index-data, - used in the sg_buffer_desc.type member when creating a buffer. - - The default value is SG_BUFFERTYPE_VERTEXBUFFER. -*/ -typedef enum sg_buffer_type { - _SG_BUFFERTYPE_DEFAULT, /* value 0 reserved for default-init */ - SG_BUFFERTYPE_VERTEXBUFFER, - SG_BUFFERTYPE_INDEXBUFFER, - _SG_BUFFERTYPE_NUM, - _SG_BUFFERTYPE_FORCE_U32 = 0x7FFFFFFF -} sg_buffer_type; - -/* - sg_index_type - - Indicates whether indexed rendering (fetching vertex-indices from an - index buffer) is used, and if yes, the index data type (16- or 32-bits). - This is used in the sg_pipeline_desc.index_type member when creating a - pipeline object. - - The default index type is SG_INDEXTYPE_NONE. -*/ -typedef enum sg_index_type { - _SG_INDEXTYPE_DEFAULT, /* value 0 reserved for default-init */ - SG_INDEXTYPE_NONE, - SG_INDEXTYPE_UINT16, - SG_INDEXTYPE_UINT32, - _SG_INDEXTYPE_NUM, - _SG_INDEXTYPE_FORCE_U32 = 0x7FFFFFFF -} sg_index_type; - -/* - sg_image_type - - Indicates the basic type of an image object (2D-texture, cubemap, - 3D-texture or 2D-array-texture). 3D- and array-textures are not supported - on the GLES2/WebGL backend (use sg_query_features().imagetype_3d and - sg_query_features().imagetype_array to check for support). The image type - is used in the sg_image_desc.type member when creating an image, and - in sg_shader_image_desc when describing a shader's texture sampler binding. - - The default image type when creating an image is SG_IMAGETYPE_2D. -*/ -typedef enum sg_image_type { - _SG_IMAGETYPE_DEFAULT, /* value 0 reserved for default-init */ - SG_IMAGETYPE_2D, - SG_IMAGETYPE_CUBE, - SG_IMAGETYPE_3D, - SG_IMAGETYPE_ARRAY, - _SG_IMAGETYPE_NUM, - _SG_IMAGETYPE_FORCE_U32 = 0x7FFFFFFF -} sg_image_type; - -/* - sg_sampler_type - - Indicates the basic data type of a shader's texture sampler which - can be float , unsigned integer or signed integer. The sampler - type is used in the sg_shader_image_desc to describe the - sampler type of a shader's texture sampler binding. - - The default sampler type is SG_SAMPLERTYPE_FLOAT. -*/ -typedef enum sg_sampler_type { - _SG_SAMPLERTYPE_DEFAULT, /* value 0 reserved for default-init */ - SG_SAMPLERTYPE_FLOAT, - SG_SAMPLERTYPE_SINT, - SG_SAMPLERTYPE_UINT, -} sg_sampler_type; - -/* - sg_cube_face - - The cubemap faces. Use these as indices in the sg_image_desc.content - array. -*/ -typedef enum sg_cube_face { - SG_CUBEFACE_POS_X, - SG_CUBEFACE_NEG_X, - SG_CUBEFACE_POS_Y, - SG_CUBEFACE_NEG_Y, - SG_CUBEFACE_POS_Z, - SG_CUBEFACE_NEG_Z, - SG_CUBEFACE_NUM, - _SG_CUBEFACE_FORCE_U32 = 0x7FFFFFFF -} sg_cube_face; - -/* - sg_shader_stage - - There are 2 shader stages: vertex- and fragment-shader-stage. - Each shader stage consists of: - - - one slot for a shader function (provided as source- or byte-code) - - SG_MAX_SHADERSTAGE_UBS slots for uniform blocks - - SG_MAX_SHADERSTAGE_IMAGES slots for images used as textures by - the shader function -*/ -typedef enum sg_shader_stage { - SG_SHADERSTAGE_VS, - SG_SHADERSTAGE_FS, - _SG_SHADERSTAGE_FORCE_U32 = 0x7FFFFFFF -} sg_shader_stage; - -/* - sg_primitive_type - - This is the common subset of 3D primitive types supported across all 3D - APIs. This is used in the sg_pipeline_desc.primitive_type member when - creating a pipeline object. - - The default primitive type is SG_PRIMITIVETYPE_TRIANGLES. -*/ -typedef enum sg_primitive_type { - _SG_PRIMITIVETYPE_DEFAULT, /* value 0 reserved for default-init */ - SG_PRIMITIVETYPE_POINTS, - SG_PRIMITIVETYPE_LINES, - SG_PRIMITIVETYPE_LINE_STRIP, - SG_PRIMITIVETYPE_TRIANGLES, - SG_PRIMITIVETYPE_TRIANGLE_STRIP, - _SG_PRIMITIVETYPE_NUM, - _SG_PRIMITIVETYPE_FORCE_U32 = 0x7FFFFFFF -} sg_primitive_type; - -/* - sg_filter - - The filtering mode when sampling a texture image. This is - used in the sg_image_desc.min_filter and sg_image_desc.mag_filter - members when creating an image object. - - The default filter mode is SG_FILTER_NEAREST. -*/ -typedef enum sg_filter { - _SG_FILTER_DEFAULT, /* value 0 reserved for default-init */ - SG_FILTER_NEAREST, - SG_FILTER_LINEAR, - SG_FILTER_NEAREST_MIPMAP_NEAREST, - SG_FILTER_NEAREST_MIPMAP_LINEAR, - SG_FILTER_LINEAR_MIPMAP_NEAREST, - SG_FILTER_LINEAR_MIPMAP_LINEAR, - _SG_FILTER_NUM, - _SG_FILTER_FORCE_U32 = 0x7FFFFFFF -} sg_filter; - -/* - sg_wrap - - The texture coordinates wrapping mode when sampling a texture - image. This is used in the sg_image_desc.wrap_u, .wrap_v - and .wrap_w members when creating an image. - - The default wrap mode is SG_WRAP_REPEAT. - - NOTE: SG_WRAP_CLAMP_TO_BORDER is not supported on all backends - and platforms. To check for support, call sg_query_features() - and check the "clamp_to_border" boolean in the returned - sg_features struct. - - Platforms which don't support SG_WRAP_CLAMP_TO_BORDER will silently fall back - to SG_WRAP_CLAMP_TO_EDGE without a validation error. - - Platforms which support clamp-to-border are: - - - all desktop GL platforms - - Metal on macOS - - D3D11 - - Platforms which do not support clamp-to-border: - - - GLES2/3 and WebGL/WebGL2 - - Metal on iOS -*/ -typedef enum sg_wrap { - _SG_WRAP_DEFAULT, /* value 0 reserved for default-init */ - SG_WRAP_REPEAT, - SG_WRAP_CLAMP_TO_EDGE, - SG_WRAP_CLAMP_TO_BORDER, - SG_WRAP_MIRRORED_REPEAT, - _SG_WRAP_NUM, - _SG_WRAP_FORCE_U32 = 0x7FFFFFFF -} sg_wrap; - -/* - sg_border_color - - The border color to use when sampling a texture, and the UV wrap - mode is SG_WRAP_CLAMP_TO_BORDER. - - The default border color is SG_BORDERCOLOR_OPAQUE_BLACK -*/ -typedef enum sg_border_color { - _SG_BORDERCOLOR_DEFAULT, /* value 0 reserved for default-init */ - SG_BORDERCOLOR_TRANSPARENT_BLACK, - SG_BORDERCOLOR_OPAQUE_BLACK, - SG_BORDERCOLOR_OPAQUE_WHITE, - _SG_BORDERCOLOR_NUM, - _SG_BORDERCOLOR_FORCE_U32 = 0x7FFFFFFF -} sg_border_color; - -/* - sg_vertex_format - - The data type of a vertex component. This is used to describe - the layout of vertex data when creating a pipeline object. -*/ -typedef enum sg_vertex_format { - SG_VERTEXFORMAT_INVALID, - SG_VERTEXFORMAT_FLOAT, - SG_VERTEXFORMAT_FLOAT2, - SG_VERTEXFORMAT_FLOAT3, - SG_VERTEXFORMAT_FLOAT4, - SG_VERTEXFORMAT_BYTE4, - SG_VERTEXFORMAT_BYTE4N, - SG_VERTEXFORMAT_UBYTE4, - SG_VERTEXFORMAT_UBYTE4N, - SG_VERTEXFORMAT_SHORT2, - SG_VERTEXFORMAT_SHORT2N, - SG_VERTEXFORMAT_USHORT2N, - SG_VERTEXFORMAT_SHORT4, - SG_VERTEXFORMAT_SHORT4N, - SG_VERTEXFORMAT_USHORT4N, - SG_VERTEXFORMAT_UINT10_N2, - _SG_VERTEXFORMAT_NUM, - _SG_VERTEXFORMAT_FORCE_U32 = 0x7FFFFFFF -} sg_vertex_format; - -/* - sg_vertex_step - - Defines whether the input pointer of a vertex input stream is advanced - 'per vertex' or 'per instance'. The default step-func is - SG_VERTEXSTEP_PER_VERTEX. SG_VERTEXSTEP_PER_INSTANCE is used with - instanced-rendering. - - The vertex-step is part of the vertex-layout definition - when creating pipeline objects. -*/ -typedef enum sg_vertex_step { - _SG_VERTEXSTEP_DEFAULT, /* value 0 reserved for default-init */ - SG_VERTEXSTEP_PER_VERTEX, - SG_VERTEXSTEP_PER_INSTANCE, - _SG_VERTEXSTEP_NUM, - _SG_VERTEXSTEP_FORCE_U32 = 0x7FFFFFFF -} sg_vertex_step; - -/* - sg_uniform_type - - The data type of a uniform block member. This is used to - describe the internal layout of uniform blocks when creating - a shader object. -*/ -typedef enum sg_uniform_type { - SG_UNIFORMTYPE_INVALID, - SG_UNIFORMTYPE_FLOAT, - SG_UNIFORMTYPE_FLOAT2, - SG_UNIFORMTYPE_FLOAT3, - SG_UNIFORMTYPE_FLOAT4, - SG_UNIFORMTYPE_INT, - SG_UNIFORMTYPE_INT2, - SG_UNIFORMTYPE_INT3, - SG_UNIFORMTYPE_INT4, - SG_UNIFORMTYPE_MAT4, - _SG_UNIFORMTYPE_NUM, - _SG_UNIFORMTYPE_FORCE_U32 = 0x7FFFFFFF -} sg_uniform_type; - -/* - sg_uniform_layout - - A hint for the interior memory layout of uniform blocks. This is - only really relevant for the GL backend where the internal layout - of uniform blocks must be known to sokol-gfx. For all other backends the - internal memory layout of uniform blocks doesn't matter, sokol-gfx - will just pass uniform data as a single memory blob to the - 3D backend. - - SG_UNIFORMLAYOUT_NATIVE (default) - Native layout means that a 'backend-native' memory layout - is used. For the GL backend this means that uniforms - are packed tightly in memory (e.g. there are no padding - bytes). - - SG_UNIFORMLAYOUT_STD140 - The memory layout is a subset of std140. Arrays are only - allowed for the FLOAT4, INT4 and MAT4. Alignment is as - is as follows: - - FLOAT, INT: 4 byte alignment - FLOAT2, INT2: 8 byte alignment - FLOAT3, INT3: 16 byte alignment(!) - FLOAT4, INT4: 16 byte alignment - MAT4: 16 byte alignment - FLOAT4[], INT4[]: 16 byte alignment - - The overall size of the uniform block must be a multiple - of 16. - - For more information search for 'UNIFORM DATA LAYOUT' in the documentation block - at the start of the header. -*/ -typedef enum sg_uniform_layout { - _SG_UNIFORMLAYOUT_DEFAULT, /* value 0 reserved for default-init */ - SG_UNIFORMLAYOUT_NATIVE, /* default: layout depends on currently active backend */ - SG_UNIFORMLAYOUT_STD140, /* std140: memory layout according to std140 */ - _SG_UNIFORMLAYOUT_NUM, - _SG_UNIFORMLAYOUT_FORCE_U32 = 0x7FFFFFFF -} sg_uniform_layout; - -/* - sg_cull_mode - - The face-culling mode, this is used in the - sg_pipeline_desc.cull_mode member when creating a - pipeline object. - - The default cull mode is SG_CULLMODE_NONE -*/ -typedef enum sg_cull_mode { - _SG_CULLMODE_DEFAULT, /* value 0 reserved for default-init */ - SG_CULLMODE_NONE, - SG_CULLMODE_FRONT, - SG_CULLMODE_BACK, - _SG_CULLMODE_NUM, - _SG_CULLMODE_FORCE_U32 = 0x7FFFFFFF -} sg_cull_mode; - -/* - sg_face_winding - - The vertex-winding rule that determines a front-facing primitive. This - is used in the member sg_pipeline_desc.face_winding - when creating a pipeline object. - - The default winding is SG_FACEWINDING_CW (clockwise) -*/ -typedef enum sg_face_winding { - _SG_FACEWINDING_DEFAULT, /* value 0 reserved for default-init */ - SG_FACEWINDING_CCW, - SG_FACEWINDING_CW, - _SG_FACEWINDING_NUM, - _SG_FACEWINDING_FORCE_U32 = 0x7FFFFFFF -} sg_face_winding; - -/* - sg_compare_func - - The compare-function for depth- and stencil-ref tests. - This is used when creating pipeline objects in the members: - - sg_pipeline_desc - .depth - .compare - .stencil - .front.compare - .back.compar - - The default compare func for depth- and stencil-tests is - SG_COMPAREFUNC_ALWAYS. -*/ -typedef enum sg_compare_func { - _SG_COMPAREFUNC_DEFAULT, /* value 0 reserved for default-init */ - SG_COMPAREFUNC_NEVER, - SG_COMPAREFUNC_LESS, - SG_COMPAREFUNC_EQUAL, - SG_COMPAREFUNC_LESS_EQUAL, - SG_COMPAREFUNC_GREATER, - SG_COMPAREFUNC_NOT_EQUAL, - SG_COMPAREFUNC_GREATER_EQUAL, - SG_COMPAREFUNC_ALWAYS, - _SG_COMPAREFUNC_NUM, - _SG_COMPAREFUNC_FORCE_U32 = 0x7FFFFFFF -} sg_compare_func; - -/* - sg_stencil_op - - The operation performed on a currently stored stencil-value when a - comparison test passes or fails. This is used when creating a pipeline - object in the members: - - sg_pipeline_desc - .stencil - .front - .fail_op - .depth_fail_op - .pass_op - .back - .fail_op - .depth_fail_op - .pass_op - - The default value is SG_STENCILOP_KEEP. -*/ -typedef enum sg_stencil_op { - _SG_STENCILOP_DEFAULT, /* value 0 reserved for default-init */ - SG_STENCILOP_KEEP, - SG_STENCILOP_ZERO, - SG_STENCILOP_REPLACE, - SG_STENCILOP_INCR_CLAMP, - SG_STENCILOP_DECR_CLAMP, - SG_STENCILOP_INVERT, - SG_STENCILOP_INCR_WRAP, - SG_STENCILOP_DECR_WRAP, - _SG_STENCILOP_NUM, - _SG_STENCILOP_FORCE_U32 = 0x7FFFFFFF -} sg_stencil_op; - -/* - sg_blend_factor - - The source and destination factors in blending operations. - This is used in the following members when creating a pipeline object: - - sg_pipeline_desc - .colors[i] - .blend - .src_factor_rgb - .dst_factor_rgb - .src_factor_alpha - .dst_factor_alpha - - The default value is SG_BLENDFACTOR_ONE for source - factors, and SG_BLENDFACTOR_ZERO for destination factors. -*/ -typedef enum sg_blend_factor { - _SG_BLENDFACTOR_DEFAULT, /* value 0 reserved for default-init */ - SG_BLENDFACTOR_ZERO, - SG_BLENDFACTOR_ONE, - SG_BLENDFACTOR_SRC_COLOR, - SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR, - SG_BLENDFACTOR_SRC_ALPHA, - SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, - SG_BLENDFACTOR_DST_COLOR, - SG_BLENDFACTOR_ONE_MINUS_DST_COLOR, - SG_BLENDFACTOR_DST_ALPHA, - SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA, - SG_BLENDFACTOR_SRC_ALPHA_SATURATED, - SG_BLENDFACTOR_BLEND_COLOR, - SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR, - SG_BLENDFACTOR_BLEND_ALPHA, - SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA, - _SG_BLENDFACTOR_NUM, - _SG_BLENDFACTOR_FORCE_U32 = 0x7FFFFFFF -} sg_blend_factor; - -/* - sg_blend_op - - Describes how the source and destination values are combined in the - fragment blending operation. It is used in the following members when - creating a pipeline object: - - sg_pipeline_desc - .colors[i] - .blend - .op_rgb - .op_alpha - - The default value is SG_BLENDOP_ADD. -*/ -typedef enum sg_blend_op { - _SG_BLENDOP_DEFAULT, /* value 0 reserved for default-init */ - SG_BLENDOP_ADD, - SG_BLENDOP_SUBTRACT, - SG_BLENDOP_REVERSE_SUBTRACT, - _SG_BLENDOP_NUM, - _SG_BLENDOP_FORCE_U32 = 0x7FFFFFFF -} sg_blend_op; - -/* - sg_color_mask - - Selects the active color channels when writing a fragment color to the - framebuffer. This is used in the members - sg_pipeline_desc.colors[i].write_mask when creating a pipeline object. - - The default colormask is SG_COLORMASK_RGBA (write all colors channels) - - NOTE: since the color mask value 0 is reserved for the default value - (SG_COLORMASK_RGBA), use SG_COLORMASK_NONE if all color channels - should be disabled. -*/ -typedef enum sg_color_mask { - _SG_COLORMASK_DEFAULT = 0, /* value 0 reserved for default-init */ - SG_COLORMASK_NONE = 0x10, /* special value for 'all channels disabled */ - SG_COLORMASK_R = 0x1, - SG_COLORMASK_G = 0x2, - SG_COLORMASK_RG = 0x3, - SG_COLORMASK_B = 0x4, - SG_COLORMASK_RB = 0x5, - SG_COLORMASK_GB = 0x6, - SG_COLORMASK_RGB = 0x7, - SG_COLORMASK_A = 0x8, - SG_COLORMASK_RA = 0x9, - SG_COLORMASK_GA = 0xA, - SG_COLORMASK_RGA = 0xB, - SG_COLORMASK_BA = 0xC, - SG_COLORMASK_RBA = 0xD, - SG_COLORMASK_GBA = 0xE, - SG_COLORMASK_RGBA = 0xF, - _SG_COLORMASK_FORCE_U32 = 0x7FFFFFFF -} sg_color_mask; - -/* - sg_action - - Defines what action should be performed at the start of a render pass: - - SG_ACTION_CLEAR: clear the render target image - SG_ACTION_LOAD: load the previous content of the render target image - SG_ACTION_DONTCARE: leave the render target image content undefined - - This is used in the sg_pass_action structure. - - The default action for all pass attachments is SG_ACTION_CLEAR, with the - clear color rgba = {0.5f, 0.5f, 0.5f, 1.0f], depth=1.0 and stencil=0. - - If you want to override the default behaviour, it is important to not - only set the clear color, but the 'action' field as well (as long as this - is in its _SG_ACTION_DEFAULT, the value fields will be ignored). -*/ -typedef enum sg_action { - _SG_ACTION_DEFAULT, - SG_ACTION_CLEAR, - SG_ACTION_LOAD, - SG_ACTION_DONTCARE, - _SG_ACTION_NUM, - _SG_ACTION_FORCE_U32 = 0x7FFFFFFF -} sg_action; - -/* - sg_pass_action - - The sg_pass_action struct defines the actions to be performed - at the start of a rendering pass in the functions sg_begin_pass() - and sg_begin_default_pass(). - - A separate action and clear values can be defined for each - color attachment, and for the depth-stencil attachment. - - The default clear values are defined by the macros: - - - SG_DEFAULT_CLEAR_RED: 0.5f - - SG_DEFAULT_CLEAR_GREEN: 0.5f - - SG_DEFAULT_CLEAR_BLUE: 0.5f - - SG_DEFAULT_CLEAR_ALPHA: 1.0f - - SG_DEFAULT_CLEAR_DEPTH: 1.0f - - SG_DEFAULT_CLEAR_STENCIL: 0 -*/ -typedef struct sg_color_attachment_action { - sg_action action; - sg_color value; -} sg_color_attachment_action; - -typedef struct sg_depth_attachment_action { - sg_action action; - float value; -} sg_depth_attachment_action; - -typedef struct sg_stencil_attachment_action { - sg_action action; - uint8_t value; -} sg_stencil_attachment_action; - -typedef struct sg_pass_action { - uint32_t _start_canary; - sg_color_attachment_action colors[SG_MAX_COLOR_ATTACHMENTS]; - sg_depth_attachment_action depth; - sg_stencil_attachment_action stencil; - uint32_t _end_canary; -} sg_pass_action; - -/* - sg_bindings - - The sg_bindings structure defines the resource binding slots - of the sokol_gfx render pipeline, used as argument to the - sg_apply_bindings() function. - - A resource binding struct contains: - - - 1..N vertex buffers - - 0..N vertex buffer offsets - - 0..1 index buffers - - 0..1 index buffer offsets - - 0..N vertex shader stage images - - 0..N fragment shader stage images - - The max number of vertex buffer and shader stage images - are defined by the SG_MAX_SHADERSTAGE_BUFFERS and - SG_MAX_SHADERSTAGE_IMAGES configuration constants. - - The optional buffer offsets can be used to put different unrelated - chunks of vertex- and/or index-data into the same buffer objects. -*/ -typedef struct sg_bindings { - uint32_t _start_canary; - sg_buffer vertex_buffers[SG_MAX_SHADERSTAGE_BUFFERS]; - int vertex_buffer_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; - sg_buffer index_buffer; - int index_buffer_offset; - sg_image vs_images[SG_MAX_SHADERSTAGE_IMAGES]; - sg_image fs_images[SG_MAX_SHADERSTAGE_IMAGES]; - uint32_t _end_canary; -} sg_bindings; - -/* - sg_buffer_desc - - Creation parameters for sg_buffer objects, used in the - sg_make_buffer() call. - - The default configuration is: - - .size: 0 (*must* be >0 for buffers without data) - .type: SG_BUFFERTYPE_VERTEXBUFFER - .usage: SG_USAGE_IMMUTABLE - .data.ptr 0 (*must* be valid for immutable buffers) - .data.size 0 (*must* be > 0 for immutable buffers) - .label 0 (optional string label for trace hooks) - - The label will be ignored by sokol_gfx.h, it is only useful - when hooking into sg_make_buffer() or sg_init_buffer() via - the sg_install_trace_hooks() function. - - For immutable buffers which are initialized with initial data, - keep the .size item zero-initialized, and set the size together with the - pointer to the initial data in the .data item. - - For mutable buffers without initial data, keep the .data item - zero-initialized, and set the buffer size in the .size item instead. - - You can also set both size values, but currently both size values must - be identical (this may change in the future when the dynamic resource - management may become more flexible). - - ADVANCED TOPIC: Injecting native 3D-API buffers: - - The following struct members allow to inject your own GL, Metal - or D3D11 buffers into sokol_gfx: - - .gl_buffers[SG_NUM_INFLIGHT_FRAMES] - .mtl_buffers[SG_NUM_INFLIGHT_FRAMES] - .d3d11_buffer - - You must still provide all other struct items except the .data item, and - these must match the creation parameters of the native buffers you - provide. For SG_USAGE_IMMUTABLE, only provide a single native 3D-API - buffer, otherwise you need to provide SG_NUM_INFLIGHT_FRAMES buffers - (only for GL and Metal, not D3D11). Providing multiple buffers for GL and - Metal is necessary because sokol_gfx will rotate through them when - calling sg_update_buffer() to prevent lock-stalls. - - Note that it is expected that immutable injected buffer have already been - initialized with content, and the .content member must be 0! - - Also you need to call sg_reset_state_cache() after calling native 3D-API - functions, and before calling any sokol_gfx function. -*/ -typedef struct sg_buffer_desc { - uint32_t _start_canary; - size_t size; - sg_buffer_type type; - sg_usage usage; - sg_range data; - const char* label; - /* GL specific */ - uint32_t gl_buffers[SG_NUM_INFLIGHT_FRAMES]; - /* Metal specific */ - const void* mtl_buffers[SG_NUM_INFLIGHT_FRAMES]; - /* D3D11 specific */ - const void* d3d11_buffer; - /* WebGPU specific */ - const void* wgpu_buffer; - uint32_t _end_canary; -} sg_buffer_desc; - -/* - sg_image_data - - Defines the content of an image through a 2D array of sg_range structs. - The first array dimension is the cubemap face, and the second array - dimension the mipmap level. -*/ -typedef struct sg_image_data { - sg_range subimage[SG_CUBEFACE_NUM][SG_MAX_MIPMAPS]; -} sg_image_data; - -/* - sg_image_desc - - Creation parameters for sg_image objects, used in the sg_make_image() - call. - - The default configuration is: - - .type: SG_IMAGETYPE_2D - .render_target: false - .width 0 (must be set to >0) - .height 0 (must be set to >0) - .num_slices 1 (3D textures: depth; array textures: number of layers) - .num_mipmaps: 1 - .usage: SG_USAGE_IMMUTABLE - .pixel_format: SG_PIXELFORMAT_RGBA8 for textures, or sg_desc.context.color_format for render targets - .sample_count: 1 for textures, or sg_desc.context.sample_count for render targets - .min_filter: SG_FILTER_NEAREST - .mag_filter: SG_FILTER_NEAREST - .wrap_u: SG_WRAP_REPEAT - .wrap_v: SG_WRAP_REPEAT - .wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D) - .border_color SG_BORDERCOLOR_OPAQUE_BLACK - .max_anisotropy 1 (must be 1..16) - .min_lod 0.0f - .max_lod FLT_MAX - .data an sg_image_data struct to define the initial content - .label 0 (optional string label for trace hooks) - - Q: Why is the default sample_count for render targets identical with the - "default sample count" from sg_desc.context.sample_count? - - A: So that it matches the default sample count in pipeline objects. Even - though it is a bit strange/confusing that offscreen render targets by default - get the same sample count as the default framebuffer, but it's better that - an offscreen render target created with default parameters matches - a pipeline object created with default parameters. - - NOTE: - - SG_IMAGETYPE_ARRAY and SG_IMAGETYPE_3D are not supported on WebGL/GLES2, - use sg_query_features().imagetype_array and - sg_query_features().imagetype_3d at runtime to check if array- and - 3D-textures are supported. - - Images with usage SG_USAGE_IMMUTABLE must be fully initialized by - providing a valid .data member which points to initialization data. - - ADVANCED TOPIC: Injecting native 3D-API textures: - - The following struct members allow to inject your own GL, Metal or D3D11 - textures into sokol_gfx: - - .gl_textures[SG_NUM_INFLIGHT_FRAMES] - .mtl_textures[SG_NUM_INFLIGHT_FRAMES] - .d3d11_texture - .d3d11_shader_resource_view - - For GL, you can also specify the texture target or leave it empty to use - the default texture target for the image type (GL_TEXTURE_2D for - SG_IMAGETYPE_2D etc) - - For D3D11, you can provide either a D3D11 texture, or a - shader-resource-view, or both. If only a texture is provided, a matching - shader-resource-view will be created. If only a shader-resource-view is - provided, the texture will be looked up from the shader-resource-view. - - The same rules apply as for injecting native buffers (see sg_buffer_desc - documentation for more details). -*/ -typedef struct sg_image_desc { - uint32_t _start_canary; - sg_image_type type; - bool render_target; - int width; - int height; - int num_slices; - int num_mipmaps; - sg_usage usage; - sg_pixel_format pixel_format; - int sample_count; - sg_filter min_filter; - sg_filter mag_filter; - sg_wrap wrap_u; - sg_wrap wrap_v; - sg_wrap wrap_w; - sg_border_color border_color; - uint32_t max_anisotropy; - float min_lod; - float max_lod; - sg_image_data data; - const char* label; - /* GL specific */ - uint32_t gl_textures[SG_NUM_INFLIGHT_FRAMES]; - uint32_t gl_texture_target; - /* Metal specific */ - const void* mtl_textures[SG_NUM_INFLIGHT_FRAMES]; - /* D3D11 specific */ - const void* d3d11_texture; - const void* d3d11_shader_resource_view; - /* WebGPU specific */ - const void* wgpu_texture; - uint32_t _end_canary; -} sg_image_desc; - -/* - sg_shader_desc - - The structure sg_shader_desc defines all creation parameters for shader - programs, used as input to the sg_make_shader() function: - - - reflection information for vertex attributes (vertex shader inputs): - - vertex attribute name (required for GLES2, optional for GLES3 and GL) - - a semantic name and index (required for D3D11) - - for each shader-stage (vertex and fragment): - - the shader source or bytecode - - an optional entry function name - - an optional compile target (only for D3D11 when source is provided, - defaults are "vs_4_0" and "ps_4_0") - - reflection info for each uniform block used by the shader stage: - - the size of the uniform block in bytes - - a memory layout hint (native vs std140, only required for GL backends) - - reflection info for each uniform block member (only required for GL backends): - - member name - - member type (SG_UNIFORMTYPE_xxx) - - if the member is an array, the number of array items - - reflection info for the texture images used by the shader stage: - - the image type (SG_IMAGETYPE_xxx) - - the sampler type (SG_SAMPLERTYPE_xxx, default is SG_SAMPLERTYPE_FLOAT) - - the name of the texture sampler (required for GLES2, optional everywhere else) - - For all GL backends, shader source-code must be provided. For D3D11 and Metal, - either shader source-code or byte-code can be provided. - - For D3D11, if source code is provided, the d3dcompiler_47.dll will be loaded - on demand. If this fails, shader creation will fail. When compiling HLSL - source code, you can provide an optional target string via - sg_shader_stage_desc.d3d11_target, the default target is "vs_4_0" for the - vertex shader stage and "ps_4_0" for the pixel shader stage. -*/ -typedef struct sg_shader_attr_desc { - const char* name; // GLSL vertex attribute name (only strictly required for GLES2) - const char* sem_name; // HLSL semantic name - int sem_index; // HLSL semantic index -} sg_shader_attr_desc; - -typedef struct sg_shader_uniform_desc { - const char* name; - sg_uniform_type type; - int array_count; -} sg_shader_uniform_desc; - -typedef struct sg_shader_uniform_block_desc { - size_t size; - sg_uniform_layout layout; - sg_shader_uniform_desc uniforms[SG_MAX_UB_MEMBERS]; -} sg_shader_uniform_block_desc; - -typedef struct sg_shader_image_desc { - const char* name; - sg_image_type image_type; - sg_sampler_type sampler_type; -} sg_shader_image_desc; - -typedef struct sg_shader_stage_desc { - const char* source; - sg_range bytecode; - const char* entry; - const char* d3d11_target; - sg_shader_uniform_block_desc uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; - sg_shader_image_desc images[SG_MAX_SHADERSTAGE_IMAGES]; -} sg_shader_stage_desc; - -typedef struct sg_shader_desc { - uint32_t _start_canary; - sg_shader_attr_desc attrs[SG_MAX_VERTEX_ATTRIBUTES]; - sg_shader_stage_desc vs; - sg_shader_stage_desc fs; - const char* label; - uint32_t _end_canary; -} sg_shader_desc; - -/* - sg_pipeline_desc - - The sg_pipeline_desc struct defines all creation parameters for an - sg_pipeline object, used as argument to the sg_make_pipeline() function: - - - the vertex layout for all input vertex buffers - - a shader object - - the 3D primitive type (points, lines, triangles, ...) - - the index type (none, 16- or 32-bit) - - all the fixed-function-pipeline state (depth-, stencil-, blend-state, etc...) - - If the vertex data has no gaps between vertex components, you can omit - the .layout.buffers[].stride and layout.attrs[].offset items (leave them - default-initialized to 0), sokol-gfx will then compute the offsets and - strides from the vertex component formats (.layout.attrs[].format). - Please note that ALL vertex attribute offsets must be 0 in order for the - automatic offset computation to kick in. - - The default configuration is as follows: - - .shader: 0 (must be initialized with a valid sg_shader id!) - .layout: - .buffers[]: vertex buffer layouts - .stride: 0 (if no stride is given it will be computed) - .step_func SG_VERTEXSTEP_PER_VERTEX - .step_rate 1 - .attrs[]: vertex attribute declarations - .buffer_index 0 the vertex buffer bind slot - .offset 0 (offsets can be omitted if the vertex layout has no gaps) - .format SG_VERTEXFORMAT_INVALID (must be initialized!) - .depth: - .pixel_format: sg_desc.context.depth_format - .compare: SG_COMPAREFUNC_ALWAYS - .write_enabled: false - .bias: 0.0f - .bias_slope_scale: 0.0f - .bias_clamp: 0.0f - .stencil: - .enabled: false - .front/back: - .compare: SG_COMPAREFUNC_ALWAYS - .depth_fail_op: SG_STENCILOP_KEEP - .pass_op: SG_STENCILOP_KEEP - .compare: SG_COMPAREFUNC_ALWAYS - .read_mask: 0 - .write_mask: 0 - .ref: 0 - .color_count 1 - .colors[0..color_count] - .pixel_format sg_desc.context.color_format - .write_mask: SG_COLORMASK_RGBA - .blend: - .enabled: false - .src_factor_rgb: SG_BLENDFACTOR_ONE - .dst_factor_rgb: SG_BLENDFACTOR_ZERO - .op_rgb: SG_BLENDOP_ADD - .src_factor_alpha: SG_BLENDFACTOR_ONE - .dst_factor_alpha: SG_BLENDFACTOR_ZERO - .op_alpha: SG_BLENDOP_ADD - .primitive_type: SG_PRIMITIVETYPE_TRIANGLES - .index_type: SG_INDEXTYPE_NONE - .cull_mode: SG_CULLMODE_NONE - .face_winding: SG_FACEWINDING_CW - .sample_count: sg_desc.context.sample_count - .blend_color: (sg_color) { 0.0f, 0.0f, 0.0f, 0.0f } - .alpha_to_coverage_enabled: false - .label 0 (optional string label for trace hooks) -*/ -typedef struct sg_buffer_layout_desc { - int stride; - sg_vertex_step step_func; - int step_rate; - #if defined(SOKOL_ZIG_BINDINGS) - uint32_t __pad[2]; - #endif -} sg_buffer_layout_desc; - -typedef struct sg_vertex_attr_desc { - int buffer_index; - int offset; - sg_vertex_format format; - #if defined(SOKOL_ZIG_BINDINGS) - uint32_t __pad[2]; - #endif -} sg_vertex_attr_desc; - -typedef struct sg_layout_desc { - sg_buffer_layout_desc buffers[SG_MAX_SHADERSTAGE_BUFFERS]; - sg_vertex_attr_desc attrs[SG_MAX_VERTEX_ATTRIBUTES]; -} sg_layout_desc; - -typedef struct sg_stencil_face_state { - sg_compare_func compare; - sg_stencil_op fail_op; - sg_stencil_op depth_fail_op; - sg_stencil_op pass_op; -} sg_stencil_face_state; - -typedef struct sg_stencil_state { - bool enabled; - sg_stencil_face_state front; - sg_stencil_face_state back; - uint8_t read_mask; - uint8_t write_mask; - uint8_t ref; -} sg_stencil_state; - -typedef struct sg_depth_state { - sg_pixel_format pixel_format; - sg_compare_func compare; - bool write_enabled; - float bias; - float bias_slope_scale; - float bias_clamp; -} sg_depth_state; - -typedef struct sg_blend_state { - bool enabled; - sg_blend_factor src_factor_rgb; - sg_blend_factor dst_factor_rgb; - sg_blend_op op_rgb; - sg_blend_factor src_factor_alpha; - sg_blend_factor dst_factor_alpha; - sg_blend_op op_alpha; -} sg_blend_state; - -typedef struct sg_color_state { - sg_pixel_format pixel_format; - sg_color_mask write_mask; - sg_blend_state blend; -} sg_color_state; - -typedef struct sg_pipeline_desc { - uint32_t _start_canary; - sg_shader shader; - sg_layout_desc layout; - sg_depth_state depth; - sg_stencil_state stencil; - int color_count; - sg_color_state colors[SG_MAX_COLOR_ATTACHMENTS]; - sg_primitive_type primitive_type; - sg_index_type index_type; - sg_cull_mode cull_mode; - sg_face_winding face_winding; - int sample_count; - sg_color blend_color; - bool alpha_to_coverage_enabled; - const char* label; - uint32_t _end_canary; -} sg_pipeline_desc; - -/* - sg_pass_desc - - Creation parameters for an sg_pass object, used as argument - to the sg_make_pass() function. - - A pass object contains 1..4 color-attachments and none, or one, - depth-stencil-attachment. Each attachment consists of - an image, and two additional indices describing - which subimage the pass will render to: one mipmap index, and - if the image is a cubemap, array-texture or 3D-texture, the - face-index, array-layer or depth-slice. - - Pass images must fulfill the following requirements: - - All images must have: - - been created as render target (sg_image_desc.render_target = true) - - the same size - - the same sample count - - In addition, all color-attachment images must have the same pixel format. -*/ -typedef struct sg_pass_attachment_desc { - sg_image image; - int mip_level; - int slice; /* cube texture: face; array texture: layer; 3D texture: slice */ -} sg_pass_attachment_desc; - -typedef struct sg_pass_desc { - uint32_t _start_canary; - sg_pass_attachment_desc color_attachments[SG_MAX_COLOR_ATTACHMENTS]; - sg_pass_attachment_desc depth_stencil_attachment; - const char* label; - uint32_t _end_canary; -} sg_pass_desc; - -/* - sg_trace_hooks - - Installable callback functions to keep track of the sokol-gfx calls, - this is useful for debugging, or keeping track of resource creation - and destruction. - - Trace hooks are installed with sg_install_trace_hooks(), this returns - another sg_trace_hooks struct with the previous set of - trace hook function pointers. These should be invoked by the - new trace hooks to form a proper call chain. -*/ -typedef struct sg_trace_hooks { - void* user_data; - void (*reset_state_cache)(void* user_data); - void (*make_buffer)(const sg_buffer_desc* desc, sg_buffer result, void* user_data); - void (*make_image)(const sg_image_desc* desc, sg_image result, void* user_data); - void (*make_shader)(const sg_shader_desc* desc, sg_shader result, void* user_data); - void (*make_pipeline)(const sg_pipeline_desc* desc, sg_pipeline result, void* user_data); - void (*make_pass)(const sg_pass_desc* desc, sg_pass result, void* user_data); - void (*destroy_buffer)(sg_buffer buf, void* user_data); - void (*destroy_image)(sg_image img, void* user_data); - void (*destroy_shader)(sg_shader shd, void* user_data); - void (*destroy_pipeline)(sg_pipeline pip, void* user_data); - void (*destroy_pass)(sg_pass pass, void* user_data); - void (*update_buffer)(sg_buffer buf, const sg_range* data, void* user_data); - void (*update_image)(sg_image img, const sg_image_data* data, void* user_data); - void (*append_buffer)(sg_buffer buf, const sg_range* data, int result, void* user_data); - void (*begin_default_pass)(const sg_pass_action* pass_action, int width, int height, void* user_data); - void (*begin_pass)(sg_pass pass, const sg_pass_action* pass_action, void* user_data); - void (*apply_viewport)(int x, int y, int width, int height, bool origin_top_left, void* user_data); - void (*apply_scissor_rect)(int x, int y, int width, int height, bool origin_top_left, void* user_data); - void (*apply_pipeline)(sg_pipeline pip, void* user_data); - void (*apply_bindings)(const sg_bindings* bindings, void* user_data); - void (*apply_uniforms)(sg_shader_stage stage, int ub_index, const sg_range* data, void* user_data); - void (*draw)(int base_element, int num_elements, int num_instances, void* user_data); - void (*end_pass)(void* user_data); - void (*commit)(void* user_data); - void (*alloc_buffer)(sg_buffer result, void* user_data); - void (*alloc_image)(sg_image result, void* user_data); - void (*alloc_shader)(sg_shader result, void* user_data); - void (*alloc_pipeline)(sg_pipeline result, void* user_data); - void (*alloc_pass)(sg_pass result, void* user_data); - void (*dealloc_buffer)(sg_buffer buf_id, void* user_data); - void (*dealloc_image)(sg_image img_id, void* user_data); - void (*dealloc_shader)(sg_shader shd_id, void* user_data); - void (*dealloc_pipeline)(sg_pipeline pip_id, void* user_data); - void (*dealloc_pass)(sg_pass pass_id, void* user_data); - void (*init_buffer)(sg_buffer buf_id, const sg_buffer_desc* desc, void* user_data); - void (*init_image)(sg_image img_id, const sg_image_desc* desc, void* user_data); - void (*init_shader)(sg_shader shd_id, const sg_shader_desc* desc, void* user_data); - void (*init_pipeline)(sg_pipeline pip_id, const sg_pipeline_desc* desc, void* user_data); - void (*init_pass)(sg_pass pass_id, const sg_pass_desc* desc, void* user_data); - void (*uninit_buffer)(sg_buffer buf_id, void* user_data); - void (*uninit_image)(sg_image img_id, void* user_data); - void (*uninit_shader)(sg_shader shd_id, void* user_data); - void (*uninit_pipeline)(sg_pipeline pip_id, void* user_data); - void (*uninit_pass)(sg_pass pass_id, void* user_data); - void (*fail_buffer)(sg_buffer buf_id, void* user_data); - void (*fail_image)(sg_image img_id, void* user_data); - void (*fail_shader)(sg_shader shd_id, void* user_data); - void (*fail_pipeline)(sg_pipeline pip_id, void* user_data); - void (*fail_pass)(sg_pass pass_id, void* user_data); - void (*push_debug_group)(const char* name, void* user_data); - void (*pop_debug_group)(void* user_data); - void (*err_buffer_pool_exhausted)(void* user_data); - void (*err_image_pool_exhausted)(void* user_data); - void (*err_shader_pool_exhausted)(void* user_data); - void (*err_pipeline_pool_exhausted)(void* user_data); - void (*err_pass_pool_exhausted)(void* user_data); - void (*err_context_mismatch)(void* user_data); - void (*err_pass_invalid)(void* user_data); - void (*err_draw_invalid)(void* user_data); - void (*err_bindings_invalid)(void* user_data); -} sg_trace_hooks; - -/* - sg_buffer_info - sg_image_info - sg_shader_info - sg_pipeline_info - sg_pass_info - - These structs contain various internal resource attributes which - might be useful for debug-inspection. Please don't rely on the - actual content of those structs too much, as they are quite closely - tied to sokol_gfx.h internals and may change more frequently than - the other public API elements. - - The *_info structs are used as the return values of the following functions: - - sg_query_buffer_info() - sg_query_image_info() - sg_query_shader_info() - sg_query_pipeline_info() - sg_query_pass_info() -*/ -typedef struct sg_slot_info { - sg_resource_state state; /* the current state of this resource slot */ - uint32_t res_id; /* type-neutral resource if (e.g. sg_buffer.id) */ - uint32_t ctx_id; /* the context this resource belongs to */ -} sg_slot_info; - -typedef struct sg_buffer_info { - sg_slot_info slot; /* resource pool slot info */ - uint32_t update_frame_index; /* frame index of last sg_update_buffer() */ - uint32_t append_frame_index; /* frame index of last sg_append_buffer() */ - int append_pos; /* current position in buffer for sg_append_buffer() */ - bool append_overflow; /* is buffer in overflow state (due to sg_append_buffer) */ - int num_slots; /* number of renaming-slots for dynamically updated buffers */ - int active_slot; /* currently active write-slot for dynamically updated buffers */ -} sg_buffer_info; - -typedef struct sg_image_info { - sg_slot_info slot; /* resource pool slot info */ - uint32_t upd_frame_index; /* frame index of last sg_update_image() */ - int num_slots; /* number of renaming-slots for dynamically updated images */ - int active_slot; /* currently active write-slot for dynamically updated images */ - int width; /* image width */ - int height; /* image height */ -} sg_image_info; - -typedef struct sg_shader_info { - sg_slot_info slot; /* resoure pool slot info */ -} sg_shader_info; - -typedef struct sg_pipeline_info { - sg_slot_info slot; /* resource pool slot info */ -} sg_pipeline_info; - -typedef struct sg_pass_info { - sg_slot_info slot; /* resource pool slot info */ -} sg_pass_info; - -/* - sg_desc - - The sg_desc struct contains configuration values for sokol_gfx, - it is used as parameter to the sg_setup() call. - - NOTE that all callback function pointers come in two versions, one without - a userdata pointer, and one with a userdata pointer. You would - either initialize one or the other depending on whether you pass data - to your callbacks. - - FIXME: explain the various configuration options - - The default configuration is: - - .buffer_pool_size 128 - .image_pool_size 128 - .shader_pool_size 32 - .pipeline_pool_size 64 - .pass_pool_size 16 - .context_pool_size 16 - .sampler_cache_size 64 - .uniform_buffer_size 4 MB (4*1024*1024) - .staging_buffer_size 8 MB (8*1024*1024) - - .context.color_format: default value depends on selected backend: - all GL backends: SG_PIXELFORMAT_RGBA8 - Metal and D3D11: SG_PIXELFORMAT_BGRA8 - WGPU: *no default* (must be queried from WGPU swapchain) - .context.depth_format SG_PIXELFORMAT_DEPTH_STENCIL - .context.sample_count 1 - - GL specific: - .context.gl.force_gles2 - if this is true the GL backend will act in "GLES2 fallback mode" even - when compiled with SOKOL_GLES3, this is useful to fall back - to traditional WebGL if a browser doesn't support a WebGL2 context - - Metal specific: - (NOTE: All Objective-C object references are transferred through - a bridged (const void*) to sokol_gfx, which will use a unretained - bridged cast (__bridged id) to retrieve the Objective-C - references back. Since the bridge cast is unretained, the caller - must hold a strong reference to the Objective-C object for the - duration of the sokol_gfx call! - - .context.metal.device - a pointer to the MTLDevice object - .context.metal.renderpass_descriptor_cb - .context.metal_renderpass_descriptor_userdata_cb - A C callback function to obtain the MTLRenderPassDescriptor for the - current frame when rendering to the default framebuffer, will be called - in sg_begin_default_pass(). - .context.metal.drawable_cb - .context.metal.drawable_userdata_cb - a C callback function to obtain a MTLDrawable for the current - frame when rendering to the default framebuffer, will be called in - sg_end_pass() of the default pass - .context.metal.user_data - optional user data pointer passed to the userdata versions of - callback functions - - D3D11 specific: - .context.d3d11.device - a pointer to the ID3D11Device object, this must have been created - before sg_setup() is called - .context.d3d11.device_context - a pointer to the ID3D11DeviceContext object - .context.d3d11.render_target_view_cb - .context.d3d11.render_target_view_userdata_cb - a C callback function to obtain a pointer to the current - ID3D11RenderTargetView object of the default framebuffer, - this function will be called in sg_begin_pass() when rendering - to the default framebuffer - .context.d3d11.depth_stencil_view_cb - .context.d3d11.depth_stencil_view_userdata_cb - a C callback function to obtain a pointer to the current - ID3D11DepthStencilView object of the default framebuffer, - this function will be called in sg_begin_pass() when rendering - to the default framebuffer - .context.metal.user_data - optional user data pointer passed to the userdata versions of - callback functions - - WebGPU specific: - .context.wgpu.device - a WGPUDevice handle - .context.wgpu.render_format - WGPUTextureFormat of the swap chain surface - .context.wgpu.render_view_cb - .context.wgpu.render_view_userdata_cb - callback to get the current WGPUTextureView of the swapchain's - rendering attachment (may be an MSAA surface) - .context.wgpu.resolve_view_cb - .context.wgpu.resolve_view_userdata_cb - callback to get the current WGPUTextureView of the swapchain's - MSAA-resolve-target surface, must return 0 if not MSAA rendering - .context.wgpu.depth_stencil_view_cb - .context.wgpu.depth_stencil_view_userdata_cb - callback to get current default-pass depth-stencil-surface WGPUTextureView - the pixel format of the default WGPUTextureView must be WGPUTextureFormat_Depth24Plus8 - .context.metal.user_data - optional user data pointer passed to the userdata versions of - callback functions - - When using sokol_gfx.h and sokol_app.h together, consider using the - helper function sapp_sgcontext() in the sokol_glue.h header to - initialize the sg_desc.context nested struct. sapp_sgcontext() returns - a completely initialized sg_context_desc struct with information - provided by sokol_app.h. -*/ -typedef struct sg_gl_context_desc { - bool force_gles2; -} sg_gl_context_desc; - -typedef struct sg_metal_context_desc { - const void* device; - const void* (*renderpass_descriptor_cb)(void); - const void* (*renderpass_descriptor_userdata_cb)(void*); - const void* (*drawable_cb)(void); - const void* (*drawable_userdata_cb)(void*); - void* user_data; -} sg_metal_context_desc; - -typedef struct sg_d3d11_context_desc { - const void* device; - const void* device_context; - const void* (*render_target_view_cb)(void); - const void* (*render_target_view_userdata_cb)(void*); - const void* (*depth_stencil_view_cb)(void); - const void* (*depth_stencil_view_userdata_cb)(void*); - void* user_data; -} sg_d3d11_context_desc; - -typedef struct sg_wgpu_context_desc { - const void* device; /* WGPUDevice */ - const void* (*render_view_cb)(void); /* returns WGPUTextureView */ - const void* (*render_view_userdata_cb)(void*); - const void* (*resolve_view_cb)(void); /* returns WGPUTextureView */ - const void* (*resolve_view_userdata_cb)(void*); - const void* (*depth_stencil_view_cb)(void); /* returns WGPUTextureView, must be WGPUTextureFormat_Depth24Plus8 */ - const void* (*depth_stencil_view_userdata_cb)(void*); - void* user_data; -} sg_wgpu_context_desc; - -typedef struct sg_context_desc { - sg_pixel_format color_format; - sg_pixel_format depth_format; - int sample_count; - sg_gl_context_desc gl; - sg_metal_context_desc metal; - sg_d3d11_context_desc d3d11; - sg_wgpu_context_desc wgpu; -} sg_context_desc; - -typedef struct sg_desc { - uint32_t _start_canary; - int buffer_pool_size; - int image_pool_size; - int shader_pool_size; - int pipeline_pool_size; - int pass_pool_size; - int context_pool_size; - int uniform_buffer_size; - int staging_buffer_size; - int sampler_cache_size; - sg_context_desc context; - uint32_t _end_canary; -} sg_desc; - -/* setup and misc functions */ -SOKOL_GFX_API_DECL void sg_setup(const sg_desc* desc); -SOKOL_GFX_API_DECL void sg_shutdown(void); -SOKOL_GFX_API_DECL bool sg_isvalid(void); -SOKOL_GFX_API_DECL void sg_reset_state_cache(void); -SOKOL_GFX_API_DECL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks); -SOKOL_GFX_API_DECL void sg_push_debug_group(const char* name); -SOKOL_GFX_API_DECL void sg_pop_debug_group(void); - -/* resource creation, destruction and updating */ -SOKOL_GFX_API_DECL sg_buffer sg_make_buffer(const sg_buffer_desc* desc); -SOKOL_GFX_API_DECL sg_image sg_make_image(const sg_image_desc* desc); -SOKOL_GFX_API_DECL sg_shader sg_make_shader(const sg_shader_desc* desc); -SOKOL_GFX_API_DECL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc); -SOKOL_GFX_API_DECL sg_pass sg_make_pass(const sg_pass_desc* desc); -SOKOL_GFX_API_DECL void sg_destroy_buffer(sg_buffer buf); -SOKOL_GFX_API_DECL void sg_destroy_image(sg_image img); -SOKOL_GFX_API_DECL void sg_destroy_shader(sg_shader shd); -SOKOL_GFX_API_DECL void sg_destroy_pipeline(sg_pipeline pip); -SOKOL_GFX_API_DECL void sg_destroy_pass(sg_pass pass); -SOKOL_GFX_API_DECL void sg_update_buffer(sg_buffer buf, const sg_range* data); -SOKOL_GFX_API_DECL void sg_update_image(sg_image img, const sg_image_data* data); -SOKOL_GFX_API_DECL int sg_append_buffer(sg_buffer buf, const sg_range* data); -SOKOL_GFX_API_DECL bool sg_query_buffer_overflow(sg_buffer buf); - -/* rendering functions */ -SOKOL_GFX_API_DECL void sg_begin_default_pass(const sg_pass_action* pass_action, int width, int height); -SOKOL_GFX_API_DECL void sg_begin_default_passf(const sg_pass_action* pass_action, float width, float height); -SOKOL_GFX_API_DECL void sg_begin_pass(sg_pass pass, const sg_pass_action* pass_action); -SOKOL_GFX_API_DECL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left); -SOKOL_GFX_API_DECL void sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left); -SOKOL_GFX_API_DECL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left); -SOKOL_GFX_API_DECL void sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left); -SOKOL_GFX_API_DECL void sg_apply_pipeline(sg_pipeline pip); -SOKOL_GFX_API_DECL void sg_apply_bindings(const sg_bindings* bindings); -SOKOL_GFX_API_DECL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data); -SOKOL_GFX_API_DECL void sg_draw(int base_element, int num_elements, int num_instances); -SOKOL_GFX_API_DECL void sg_end_pass(void); -SOKOL_GFX_API_DECL void sg_commit(void); - -/* getting information */ -SOKOL_GFX_API_DECL sg_desc sg_query_desc(void); -SOKOL_GFX_API_DECL sg_backend sg_query_backend(void); -SOKOL_GFX_API_DECL sg_features sg_query_features(void); -SOKOL_GFX_API_DECL sg_limits sg_query_limits(void); -SOKOL_GFX_API_DECL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt); -/* get current state of a resource (INITIAL, ALLOC, VALID, FAILED, INVALID) */ -SOKOL_GFX_API_DECL sg_resource_state sg_query_buffer_state(sg_buffer buf); -SOKOL_GFX_API_DECL sg_resource_state sg_query_image_state(sg_image img); -SOKOL_GFX_API_DECL sg_resource_state sg_query_shader_state(sg_shader shd); -SOKOL_GFX_API_DECL sg_resource_state sg_query_pipeline_state(sg_pipeline pip); -SOKOL_GFX_API_DECL sg_resource_state sg_query_pass_state(sg_pass pass); -/* get runtime information about a resource */ -SOKOL_GFX_API_DECL sg_buffer_info sg_query_buffer_info(sg_buffer buf); -SOKOL_GFX_API_DECL sg_image_info sg_query_image_info(sg_image img); -SOKOL_GFX_API_DECL sg_shader_info sg_query_shader_info(sg_shader shd); -SOKOL_GFX_API_DECL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip); -SOKOL_GFX_API_DECL sg_pass_info sg_query_pass_info(sg_pass pass); -/* get resource creation desc struct with their default values replaced */ -SOKOL_GFX_API_DECL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc); -SOKOL_GFX_API_DECL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc); -SOKOL_GFX_API_DECL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc); -SOKOL_GFX_API_DECL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc); -SOKOL_GFX_API_DECL sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc); - -/* separate resource allocation and initialization (for async setup) */ -SOKOL_GFX_API_DECL sg_buffer sg_alloc_buffer(void); -SOKOL_GFX_API_DECL sg_image sg_alloc_image(void); -SOKOL_GFX_API_DECL sg_shader sg_alloc_shader(void); -SOKOL_GFX_API_DECL sg_pipeline sg_alloc_pipeline(void); -SOKOL_GFX_API_DECL sg_pass sg_alloc_pass(void); -SOKOL_GFX_API_DECL void sg_dealloc_buffer(sg_buffer buf_id); -SOKOL_GFX_API_DECL void sg_dealloc_image(sg_image img_id); -SOKOL_GFX_API_DECL void sg_dealloc_shader(sg_shader shd_id); -SOKOL_GFX_API_DECL void sg_dealloc_pipeline(sg_pipeline pip_id); -SOKOL_GFX_API_DECL void sg_dealloc_pass(sg_pass pass_id); -SOKOL_GFX_API_DECL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc); -SOKOL_GFX_API_DECL void sg_init_image(sg_image img_id, const sg_image_desc* desc); -SOKOL_GFX_API_DECL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc); -SOKOL_GFX_API_DECL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc); -SOKOL_GFX_API_DECL void sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc); -SOKOL_GFX_API_DECL bool sg_uninit_buffer(sg_buffer buf_id); -SOKOL_GFX_API_DECL bool sg_uninit_image(sg_image img_id); -SOKOL_GFX_API_DECL bool sg_uninit_shader(sg_shader shd_id); -SOKOL_GFX_API_DECL bool sg_uninit_pipeline(sg_pipeline pip_id); -SOKOL_GFX_API_DECL bool sg_uninit_pass(sg_pass pass_id); -SOKOL_GFX_API_DECL void sg_fail_buffer(sg_buffer buf_id); -SOKOL_GFX_API_DECL void sg_fail_image(sg_image img_id); -SOKOL_GFX_API_DECL void sg_fail_shader(sg_shader shd_id); -SOKOL_GFX_API_DECL void sg_fail_pipeline(sg_pipeline pip_id); -SOKOL_GFX_API_DECL void sg_fail_pass(sg_pass pass_id); - -/* rendering contexts (optional) */ -SOKOL_GFX_API_DECL sg_context sg_setup_context(void); -SOKOL_GFX_API_DECL void sg_activate_context(sg_context ctx_id); -SOKOL_GFX_API_DECL void sg_discard_context(sg_context ctx_id); - -/* Backend-specific helper functions, these may come in handy for mixing - sokol-gfx rendering with 'native backend' rendering functions. - - This group of functions will be expanded as needed. -*/ - -/* D3D11: return ID3D11Device */ -SOKOL_GFX_API_DECL const void* sg_d3d11_device(void); - -/* Metal: return __bridge-casted MTLDevice */ -SOKOL_GFX_API_DECL const void* sg_mtl_device(void); - -/* Metal: return __bridge-casted MTLRenderCommandEncoder in current pass (or zero if outside pass) */ -SOKOL_GFX_API_DECL const void* sg_mtl_render_command_encoder(void); - -#ifdef __cplusplus -} /* extern "C" */ - -/* reference-based equivalents for c++ */ -inline void sg_setup(const sg_desc& desc) { return sg_setup(&desc); } - -inline sg_buffer sg_make_buffer(const sg_buffer_desc& desc) { return sg_make_buffer(&desc); } -inline sg_image sg_make_image(const sg_image_desc& desc) { return sg_make_image(&desc); } -inline sg_shader sg_make_shader(const sg_shader_desc& desc) { return sg_make_shader(&desc); } -inline sg_pipeline sg_make_pipeline(const sg_pipeline_desc& desc) { return sg_make_pipeline(&desc); } -inline sg_pass sg_make_pass(const sg_pass_desc& desc) { return sg_make_pass(&desc); } -inline void sg_update_image(sg_image img, const sg_image_data& data) { return sg_update_image(img, &data); } - -inline void sg_begin_default_pass(const sg_pass_action& pass_action, int width, int height) { return sg_begin_default_pass(&pass_action, width, height); } -inline void sg_begin_default_passf(const sg_pass_action& pass_action, float width, float height) { return sg_begin_default_passf(&pass_action, width, height); } -inline void sg_begin_pass(sg_pass pass, const sg_pass_action& pass_action) { return sg_begin_pass(pass, &pass_action); } -inline void sg_apply_bindings(const sg_bindings& bindings) { return sg_apply_bindings(&bindings); } -inline void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range& data) { return sg_apply_uniforms(stage, ub_index, &data); } - -inline sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc& desc) { return sg_query_buffer_defaults(&desc); } -inline sg_image_desc sg_query_image_defaults(const sg_image_desc& desc) { return sg_query_image_defaults(&desc); } -inline sg_shader_desc sg_query_shader_defaults(const sg_shader_desc& desc) { return sg_query_shader_defaults(&desc); } -inline sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc& desc) { return sg_query_pipeline_defaults(&desc); } -inline sg_pass_desc sg_query_pass_defaults(const sg_pass_desc& desc) { return sg_query_pass_defaults(&desc); } - -inline void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc& desc) { return sg_init_buffer(buf_id, &desc); } -inline void sg_init_image(sg_image img_id, const sg_image_desc& desc) { return sg_init_image(img_id, &desc); } -inline void sg_init_shader(sg_shader shd_id, const sg_shader_desc& desc) { return sg_init_shader(shd_id, &desc); } -inline void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc& desc) { return sg_init_pipeline(pip_id, &desc); } -inline void sg_init_pass(sg_pass pass_id, const sg_pass_desc& desc) { return sg_init_pass(pass_id, &desc); } - -inline void sg_update_buffer(sg_buffer buf_id, const sg_range& data) { return sg_update_buffer(buf_id, &data); } -inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_append_buffer(buf_id, &data); } -#endif -#endif // SOKOL_GFX_INCLUDED - -/*--- IMPLEMENTATION ---------------------------------------------------------*/ -#ifdef SOKOL_GFX_IMPL -#define SOKOL_GFX_IMPL_INCLUDED (1) - -#if !(defined(SOKOL_GLCORE33)||defined(SOKOL_GLES2)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_WGPU)||defined(SOKOL_DUMMY_BACKEND)) -#error "Please select a backend with SOKOL_GLCORE33, SOKOL_GLES2, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND" -#endif -#include /* memset */ -#include /* FLT_MAX */ - -#ifndef SOKOL_API_IMPL - #define SOKOL_API_IMPL -#endif -#ifndef SOKOL_DEBUG - #ifndef NDEBUG - #define SOKOL_DEBUG (1) - #endif -#endif -#ifndef SOKOL_ASSERT - #include - #define SOKOL_ASSERT(c) assert(c) -#endif -#ifndef SOKOL_VALIDATE_BEGIN - #define SOKOL_VALIDATE_BEGIN() _sg_validate_begin() -#endif -#ifndef SOKOL_VALIDATE - #define SOKOL_VALIDATE(cond, err) _sg_validate((cond), err) -#endif -#ifndef SOKOL_VALIDATE_END - #define SOKOL_VALIDATE_END() _sg_validate_end() -#endif -#ifndef SOKOL_UNREACHABLE - #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) -#endif -#ifndef SOKOL_MALLOC - #include - #define SOKOL_MALLOC(s) malloc(s) - #define SOKOL_FREE(p) free(p) -#endif -#ifndef SOKOL_LOG - #ifdef SOKOL_DEBUG - #include - #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } - #else - #define SOKOL_LOG(s) - #endif -#endif - -#ifndef _SOKOL_PRIVATE - #if defined(__GNUC__) || defined(__clang__) - #define _SOKOL_PRIVATE __attribute__((unused)) static - #else - #define _SOKOL_PRIVATE static - #endif -#endif - -#ifndef _SOKOL_UNUSED - #define _SOKOL_UNUSED(x) (void)(x) -#endif - -#if defined(SOKOL_TRACE_HOOKS) -#define _SG_TRACE_ARGS(fn, ...) if (_sg.hooks.fn) { _sg.hooks.fn(__VA_ARGS__, _sg.hooks.user_data); } -#define _SG_TRACE_NOARGS(fn) if (_sg.hooks.fn) { _sg.hooks.fn(_sg.hooks.user_data); } -#else -#define _SG_TRACE_ARGS(fn, ...) -#define _SG_TRACE_NOARGS(fn) -#endif - -/* default clear values */ -#ifndef SG_DEFAULT_CLEAR_RED -#define SG_DEFAULT_CLEAR_RED (0.5f) -#endif -#ifndef SG_DEFAULT_CLEAR_GREEN -#define SG_DEFAULT_CLEAR_GREEN (0.5f) -#endif -#ifndef SG_DEFAULT_CLEAR_BLUE -#define SG_DEFAULT_CLEAR_BLUE (0.5f) -#endif -#ifndef SG_DEFAULT_CLEAR_ALPHA -#define SG_DEFAULT_CLEAR_ALPHA (1.0f) -#endif -#ifndef SG_DEFAULT_CLEAR_DEPTH -#define SG_DEFAULT_CLEAR_DEPTH (1.0f) -#endif -#ifndef SG_DEFAULT_CLEAR_STENCIL -#define SG_DEFAULT_CLEAR_STENCIL (0) -#endif - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4115) /* named type definition in parentheses */ -#pragma warning(disable:4505) /* unreferenced local function has been removed */ -#pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union (needed by d3d11.h) */ -#pragma warning(disable:4054) /* 'type cast': from function pointer */ -#pragma warning(disable:4055) /* 'type cast': from data pointer */ -#endif - -#if defined(SOKOL_D3D11) - #ifndef D3D11_NO_HELPERS - #define D3D11_NO_HELPERS - #endif - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #ifndef NOMINMAX - #define NOMINMAX - #endif - #include - #include - #ifdef _MSC_VER - #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) - #pragma comment (lib, "WindowsApp") - #else - #pragma comment (lib, "kernel32") - #pragma comment (lib, "user32") - #pragma comment (lib, "dxgi") - #pragma comment (lib, "d3d11") - #endif - #endif -#elif defined(SOKOL_METAL) - // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting - #if !defined(__cplusplus) - #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) - #error "sokol_gfx.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" - #endif - #endif - #include - #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE - #define _SG_TARGET_MACOS (1) - #else - #define _SG_TARGET_IOS (1) - #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR - #define _SG_TARGET_IOS_SIMULATOR (1) - #endif - #endif - #import -#elif defined(SOKOL_WGPU) - #if defined(__EMSCRIPTEN__) - #include - #else - #include - #endif -#elif defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - #define _SOKOL_ANY_GL (1) - - // include platform specific GL headers (or on Win32: use an embedded GL loader) - #if !defined(SOKOL_EXTERNAL_GL_LOADER) - #if defined(_WIN32) - #if defined(SOKOL_GLCORE33) && !defined(SOKOL_EXTERNAL_GL_LOADER) - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #ifndef NOMINMAX - #define NOMINMAX - #endif - #include - #define _SOKOL_USE_WIN32_GL_LOADER (1) - #pragma comment (lib, "kernel32") // GetProcAddress() - #endif - #elif defined(__APPLE__) - #include - #ifndef GL_SILENCE_DEPRECATION - #define GL_SILENCE_DEPRECATION - #endif - #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE - #include - #else - #include - #include - #endif - #elif defined(__EMSCRIPTEN__) || defined(__ANDROID__) - #if defined(SOKOL_GLES3) - #include - #elif defined(SOKOL_GLES2) - #ifndef GL_EXT_PROTOTYPES - #define GL_GLEXT_PROTOTYPES - #endif - #include - #include - #endif - #elif defined(__linux__) || defined(__unix__) - #define GL_GLEXT_PROTOTYPES - #include - #endif - #endif - - // optional GL loader definitions (only on Win32) - #if defined(_SOKOL_USE_WIN32_GL_LOADER) - #define __gl_h_ 1 - #define __gl32_h_ 1 - #define __gl31_h_ 1 - #define __GL_H__ 1 - #define __glext_h_ 1 - #define __GLEXT_H_ 1 - #define __gltypes_h_ 1 - #define __glcorearb_h_ 1 - #define __gl_glcorearb_h_ 1 - #define GL_APIENTRY APIENTRY - - typedef unsigned int GLenum; - typedef unsigned int GLuint; - typedef int GLsizei; - typedef char GLchar; - typedef ptrdiff_t GLintptr; - typedef ptrdiff_t GLsizeiptr; - typedef double GLclampd; - typedef unsigned short GLushort; - typedef unsigned char GLubyte; - typedef unsigned char GLboolean; - typedef uint64_t GLuint64; - typedef double GLdouble; - typedef unsigned short GLhalf; - typedef float GLclampf; - typedef unsigned int GLbitfield; - typedef signed char GLbyte; - typedef short GLshort; - typedef void GLvoid; - typedef int64_t GLint64; - typedef float GLfloat; - typedef struct __GLsync * GLsync; - typedef int GLint; - #define GL_INT_2_10_10_10_REV 0x8D9F - #define GL_R32F 0x822E - #define GL_PROGRAM_POINT_SIZE 0x8642 - #define GL_STENCIL_ATTACHMENT 0x8D20 - #define GL_DEPTH_ATTACHMENT 0x8D00 - #define GL_COLOR_ATTACHMENT2 0x8CE2 - #define GL_COLOR_ATTACHMENT0 0x8CE0 - #define GL_R16F 0x822D - #define GL_COLOR_ATTACHMENT22 0x8CF6 - #define GL_DRAW_FRAMEBUFFER 0x8CA9 - #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 - #define GL_NUM_EXTENSIONS 0x821D - #define GL_INFO_LOG_LENGTH 0x8B84 - #define GL_VERTEX_SHADER 0x8B31 - #define GL_INCR 0x1E02 - #define GL_DYNAMIC_DRAW 0x88E8 - #define GL_STATIC_DRAW 0x88E4 - #define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 - #define GL_TEXTURE_CUBE_MAP 0x8513 - #define GL_FUNC_SUBTRACT 0x800A - #define GL_FUNC_REVERSE_SUBTRACT 0x800B - #define GL_CONSTANT_COLOR 0x8001 - #define GL_DECR_WRAP 0x8508 - #define GL_R8 0x8229 - #define GL_LINEAR_MIPMAP_LINEAR 0x2703 - #define GL_ELEMENT_ARRAY_BUFFER 0x8893 - #define GL_SHORT 0x1402 - #define GL_DEPTH_TEST 0x0B71 - #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 - #define GL_LINK_STATUS 0x8B82 - #define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 - #define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E - #define GL_RGBA16F 0x881A - #define GL_CONSTANT_ALPHA 0x8003 - #define GL_READ_FRAMEBUFFER 0x8CA8 - #define GL_TEXTURE0 0x84C0 - #define GL_TEXTURE_MIN_LOD 0x813A - #define GL_CLAMP_TO_EDGE 0x812F - #define GL_UNSIGNED_SHORT_5_6_5 0x8363 - #define GL_TEXTURE_WRAP_R 0x8072 - #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 - #define GL_NEAREST_MIPMAP_NEAREST 0x2700 - #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 - #define GL_SRC_ALPHA_SATURATE 0x0308 - #define GL_STREAM_DRAW 0x88E0 - #define GL_ONE 1 - #define GL_NEAREST_MIPMAP_LINEAR 0x2702 - #define GL_RGB10_A2 0x8059 - #define GL_RGBA8 0x8058 - #define GL_COLOR_ATTACHMENT1 0x8CE1 - #define GL_RGBA4 0x8056 - #define GL_RGB8 0x8051 - #define GL_ARRAY_BUFFER 0x8892 - #define GL_STENCIL 0x1802 - #define GL_TEXTURE_2D 0x0DE1 - #define GL_DEPTH 0x1801 - #define GL_FRONT 0x0404 - #define GL_STENCIL_BUFFER_BIT 0x00000400 - #define GL_REPEAT 0x2901 - #define GL_RGBA 0x1908 - #define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 - #define GL_DECR 0x1E03 - #define GL_FRAGMENT_SHADER 0x8B30 - #define GL_FLOAT 0x1406 - #define GL_TEXTURE_MAX_LOD 0x813B - #define GL_DEPTH_COMPONENT 0x1902 - #define GL_ONE_MINUS_DST_ALPHA 0x0305 - #define GL_COLOR 0x1800 - #define GL_TEXTURE_2D_ARRAY 0x8C1A - #define GL_TRIANGLES 0x0004 - #define GL_UNSIGNED_BYTE 0x1401 - #define GL_TEXTURE_MAG_FILTER 0x2800 - #define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 - #define GL_NONE 0 - #define GL_SRC_COLOR 0x0300 - #define GL_BYTE 0x1400 - #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A - #define GL_LINE_STRIP 0x0003 - #define GL_TEXTURE_3D 0x806F - #define GL_CW 0x0900 - #define GL_LINEAR 0x2601 - #define GL_RENDERBUFFER 0x8D41 - #define GL_GEQUAL 0x0206 - #define GL_COLOR_BUFFER_BIT 0x00004000 - #define GL_RGBA32F 0x8814 - #define GL_BLEND 0x0BE2 - #define GL_ONE_MINUS_SRC_ALPHA 0x0303 - #define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 - #define GL_TEXTURE_WRAP_T 0x2803 - #define GL_TEXTURE_WRAP_S 0x2802 - #define GL_TEXTURE_MIN_FILTER 0x2801 - #define GL_LINEAR_MIPMAP_NEAREST 0x2701 - #define GL_EXTENSIONS 0x1F03 - #define GL_NO_ERROR 0 - #define GL_REPLACE 0x1E01 - #define GL_KEEP 0x1E00 - #define GL_CCW 0x0901 - #define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 - #define GL_RGB 0x1907 - #define GL_TRIANGLE_STRIP 0x0005 - #define GL_FALSE 0 - #define GL_ZERO 0 - #define GL_CULL_FACE 0x0B44 - #define GL_INVERT 0x150A - #define GL_INT 0x1404 - #define GL_UNSIGNED_INT 0x1405 - #define GL_UNSIGNED_SHORT 0x1403 - #define GL_NEAREST 0x2600 - #define GL_SCISSOR_TEST 0x0C11 - #define GL_LEQUAL 0x0203 - #define GL_STENCIL_TEST 0x0B90 - #define GL_DITHER 0x0BD0 - #define GL_DEPTH_COMPONENT16 0x81A5 - #define GL_EQUAL 0x0202 - #define GL_FRAMEBUFFER 0x8D40 - #define GL_RGB5 0x8050 - #define GL_LINES 0x0001 - #define GL_DEPTH_BUFFER_BIT 0x00000100 - #define GL_SRC_ALPHA 0x0302 - #define GL_INCR_WRAP 0x8507 - #define GL_LESS 0x0201 - #define GL_MULTISAMPLE 0x809D - #define GL_FRAMEBUFFER_BINDING 0x8CA6 - #define GL_BACK 0x0405 - #define GL_ALWAYS 0x0207 - #define GL_FUNC_ADD 0x8006 - #define GL_ONE_MINUS_DST_COLOR 0x0307 - #define GL_NOTEQUAL 0x0205 - #define GL_DST_COLOR 0x0306 - #define GL_COMPILE_STATUS 0x8B81 - #define GL_RED 0x1903 - #define GL_COLOR_ATTACHMENT3 0x8CE3 - #define GL_DST_ALPHA 0x0304 - #define GL_RGB5_A1 0x8057 - #define GL_GREATER 0x0204 - #define GL_POLYGON_OFFSET_FILL 0x8037 - #define GL_TRUE 1 - #define GL_NEVER 0x0200 - #define GL_POINTS 0x0000 - #define GL_ONE_MINUS_SRC_COLOR 0x0301 - #define GL_MIRRORED_REPEAT 0x8370 - #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D - #define GL_R11F_G11F_B10F 0x8C3A - #define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B - #define GL_RGBA32UI 0x8D70 - #define GL_RGB32UI 0x8D71 - #define GL_RGBA16UI 0x8D76 - #define GL_RGB16UI 0x8D77 - #define GL_RGBA8UI 0x8D7C - #define GL_RGB8UI 0x8D7D - #define GL_RGBA32I 0x8D82 - #define GL_RGB32I 0x8D83 - #define GL_RGBA16I 0x8D88 - #define GL_RGB16I 0x8D89 - #define GL_RGBA8I 0x8D8E - #define GL_RGB8I 0x8D8F - #define GL_RED_INTEGER 0x8D94 - #define GL_RG 0x8227 - #define GL_RG_INTEGER 0x8228 - #define GL_R8 0x8229 - #define GL_R16 0x822A - #define GL_RG8 0x822B - #define GL_RG16 0x822C - #define GL_R16F 0x822D - #define GL_R32F 0x822E - #define GL_RG16F 0x822F - #define GL_RG32F 0x8230 - #define GL_R8I 0x8231 - #define GL_R8UI 0x8232 - #define GL_R16I 0x8233 - #define GL_R16UI 0x8234 - #define GL_R32I 0x8235 - #define GL_R32UI 0x8236 - #define GL_RG8I 0x8237 - #define GL_RG8UI 0x8238 - #define GL_RG16I 0x8239 - #define GL_RG16UI 0x823A - #define GL_RG32I 0x823B - #define GL_RG32UI 0x823C - #define GL_RGBA_INTEGER 0x8D99 - #define GL_R8_SNORM 0x8F94 - #define GL_RG8_SNORM 0x8F95 - #define GL_RGB8_SNORM 0x8F96 - #define GL_RGBA8_SNORM 0x8F97 - #define GL_R16_SNORM 0x8F98 - #define GL_RG16_SNORM 0x8F99 - #define GL_RGB16_SNORM 0x8F9A - #define GL_RGBA16_SNORM 0x8F9B - #define GL_RGBA16 0x805B - #define GL_MAX_TEXTURE_SIZE 0x0D33 - #define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C - #define GL_MAX_3D_TEXTURE_SIZE 0x8073 - #define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF - #define GL_MAX_VERTEX_ATTRIBS 0x8869 - #define GL_CLAMP_TO_BORDER 0x812D - #define GL_TEXTURE_BORDER_COLOR 0x1004 - #define GL_CURRENT_PROGRAM 0x8B8D - #define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB - #endif - - #ifndef GL_UNSIGNED_INT_2_10_10_10_REV - #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 - #endif - #ifndef GL_UNSIGNED_INT_24_8 - #define GL_UNSIGNED_INT_24_8 0x84FA - #endif - #ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT - #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE - #endif - #ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT - #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF - #endif - #ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 - #endif - #ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 - #endif - #ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 - #endif - #ifndef GL_COMPRESSED_RED_RGTC1 - #define GL_COMPRESSED_RED_RGTC1 0x8DBB - #endif - #ifndef GL_COMPRESSED_SIGNED_RED_RGTC1 - #define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC - #endif - #ifndef GL_COMPRESSED_RED_GREEN_RGTC2 - #define GL_COMPRESSED_RED_GREEN_RGTC2 0x8DBD - #endif - #ifndef GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2 - #define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2 0x8DBE - #endif - #ifndef GL_COMPRESSED_RGBA_BPTC_UNORM_ARB - #define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C - #endif - #ifndef GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB - #define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D - #endif - #ifndef GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB - #define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E - #endif - #ifndef GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB - #define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F - #endif - #ifndef GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG - #define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 - #endif - #ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG - #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 - #endif - #ifndef GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG - #define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 - #endif - #ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG - #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 - #endif - #ifndef GL_COMPRESSED_RGB8_ETC2 - #define GL_COMPRESSED_RGB8_ETC2 0x9274 - #endif - #ifndef GL_COMPRESSED_RGBA8_ETC2_EAC - #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 - #endif - #ifndef GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 - #define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 - #endif - #ifndef GL_COMPRESSED_RG11_EAC - #define GL_COMPRESSED_RG11_EAC 0x9272 - #endif - #ifndef GL_COMPRESSED_SIGNED_RG11_EAC - #define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 - #endif - #ifndef GL_DEPTH24_STENCIL8 - #define GL_DEPTH24_STENCIL8 0x88F0 - #endif - #ifndef GL_HALF_FLOAT - #define GL_HALF_FLOAT 0x140B - #endif - #ifndef GL_DEPTH_STENCIL - #define GL_DEPTH_STENCIL 0x84F9 - #endif - #ifndef GL_LUMINANCE - #define GL_LUMINANCE 0x1909 - #endif - - #ifdef SOKOL_GLES2 - #ifdef GL_ANGLE_instanced_arrays - #define _SOKOL_GL_INSTANCING_ENABLED - #define glDrawArraysInstanced(mode, first, count, instancecount) glDrawArraysInstancedANGLE(mode, first, count, instancecount) - #define glDrawElementsInstanced(mode, count, type, indices, instancecount) glDrawElementsInstancedANGLE(mode, count, type, indices, instancecount) - #define glVertexAttribDivisor(index, divisor) glVertexAttribDivisorANGLE(index, divisor) - #elif defined(GL_EXT_draw_instanced) && defined(GL_EXT_instanced_arrays) - #define _SOKOL_GL_INSTANCING_ENABLED - #define glDrawArraysInstanced(mode, first, count, instancecount) glDrawArraysInstancedEXT(mode, first, count, instancecount) - #define glDrawElementsInstanced(mode, count, type, indices, instancecount) glDrawElementsInstancedEXT(mode, count, type, indices, instancecount) - #define glVertexAttribDivisor(index, divisor) glVertexAttribDivisorEXT(index, divisor) - #else - #define _SOKOL_GLES2_INSTANCING_ERROR "Select GL_ANGLE_instanced_arrays or (GL_EXT_draw_instanced & GL_EXT_instanced_arrays) to enable instancing in GLES2" - #define glDrawArraysInstanced(mode, first, count, instancecount) SOKOL_ASSERT(0 && _SOKOL_GLES2_INSTANCING_ERROR) - #define glDrawElementsInstanced(mode, count, type, indices, instancecount) SOKOL_ASSERT(0 && _SOKOL_GLES2_INSTANCING_ERROR) - #define glVertexAttribDivisor(index, divisor) SOKOL_ASSERT(0 && _SOKOL_GLES2_INSTANCING_ERROR) - #endif - #else - #define _SOKOL_GL_INSTANCING_ENABLED - #endif - #define _SG_GL_CHECK_ERROR() { SOKOL_ASSERT(glGetError() == GL_NO_ERROR); } -#endif - -/*=== COMMON BACKEND STUFF ===================================================*/ - -/* resource pool slots */ -typedef struct { - uint32_t id; - uint32_t ctx_id; - sg_resource_state state; -} _sg_slot_t; - -/* constants */ -enum { - _SG_STRING_SIZE = 16, - _SG_SLOT_SHIFT = 16, - _SG_SLOT_MASK = (1<<_SG_SLOT_SHIFT)-1, - _SG_MAX_POOL_SIZE = (1<<_SG_SLOT_SHIFT), - _SG_DEFAULT_BUFFER_POOL_SIZE = 128, - _SG_DEFAULT_IMAGE_POOL_SIZE = 128, - _SG_DEFAULT_SHADER_POOL_SIZE = 32, - _SG_DEFAULT_PIPELINE_POOL_SIZE = 64, - _SG_DEFAULT_PASS_POOL_SIZE = 16, - _SG_DEFAULT_CONTEXT_POOL_SIZE = 16, - _SG_DEFAULT_SAMPLER_CACHE_CAPACITY = 64, - _SG_DEFAULT_UB_SIZE = 4 * 1024 * 1024, - _SG_DEFAULT_STAGING_SIZE = 8 * 1024 * 1024, -}; - -/* fixed-size string */ -typedef struct { - char buf[_SG_STRING_SIZE]; -} _sg_str_t; - -/* helper macros */ -#define _sg_def(val, def) (((val) == 0) ? (def) : (val)) -#define _sg_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) -#define _sg_min(a,b) (((a)<(b))?(a):(b)) -#define _sg_max(a,b) (((a)>(b))?(a):(b)) -#define _sg_clamp(v,v0,v1) (((v)<(v0))?(v0):(((v)>(v1))?(v1):(v))) -#define _sg_fequal(val,cmp,delta) ((((val)-(cmp))> -(delta))&&(((val)-(cmp))<(delta))) - -typedef struct { - int size; - int append_pos; - bool append_overflow; - sg_buffer_type type; - sg_usage usage; - uint32_t update_frame_index; - uint32_t append_frame_index; - int num_slots; - int active_slot; -} _sg_buffer_common_t; - -_SOKOL_PRIVATE void _sg_buffer_common_init(_sg_buffer_common_t* cmn, const sg_buffer_desc* desc) { - cmn->size = (int)desc->size; - cmn->append_pos = 0; - cmn->append_overflow = false; - cmn->type = desc->type; - cmn->usage = desc->usage; - cmn->update_frame_index = 0; - cmn->append_frame_index = 0; - cmn->num_slots = (cmn->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; - cmn->active_slot = 0; -} - -typedef struct { - sg_image_type type; - bool render_target; - int width; - int height; - int num_slices; - int num_mipmaps; - sg_usage usage; - sg_pixel_format pixel_format; - int sample_count; - sg_filter min_filter; - sg_filter mag_filter; - sg_wrap wrap_u; - sg_wrap wrap_v; - sg_wrap wrap_w; - sg_border_color border_color; - uint32_t max_anisotropy; - uint32_t upd_frame_index; - int num_slots; - int active_slot; -} _sg_image_common_t; - -_SOKOL_PRIVATE void _sg_image_common_init(_sg_image_common_t* cmn, const sg_image_desc* desc) { - cmn->type = desc->type; - cmn->render_target = desc->render_target; - cmn->width = desc->width; - cmn->height = desc->height; - cmn->num_slices = desc->num_slices; - cmn->num_mipmaps = desc->num_mipmaps; - cmn->usage = desc->usage; - cmn->pixel_format = desc->pixel_format; - cmn->sample_count = desc->sample_count; - cmn->min_filter = desc->min_filter; - cmn->mag_filter = desc->mag_filter; - cmn->wrap_u = desc->wrap_u; - cmn->wrap_v = desc->wrap_v; - cmn->wrap_w = desc->wrap_w; - cmn->border_color = desc->border_color; - cmn->max_anisotropy = desc->max_anisotropy; - cmn->upd_frame_index = 0; - cmn->num_slots = (cmn->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; - cmn->active_slot = 0; -} - -typedef struct { - size_t size; -} _sg_uniform_block_t; - -typedef struct { - sg_image_type image_type; - sg_sampler_type sampler_type; -} _sg_shader_image_t; - -typedef struct { - int num_uniform_blocks; - int num_images; - _sg_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; - _sg_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; -} _sg_shader_stage_t; - -typedef struct { - _sg_shader_stage_t stage[SG_NUM_SHADER_STAGES]; -} _sg_shader_common_t; - -_SOKOL_PRIVATE void _sg_shader_common_init(_sg_shader_common_t* cmn, const sg_shader_desc* desc) { - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; - _sg_shader_stage_t* stage = &cmn->stage[stage_index]; - SOKOL_ASSERT(stage->num_uniform_blocks == 0); - for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { - const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; - if (0 == ub_desc->size) { - break; - } - stage->uniform_blocks[ub_index].size = ub_desc->size; - stage->num_uniform_blocks++; - } - SOKOL_ASSERT(stage->num_images == 0); - for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { - const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; - if (img_desc->image_type == _SG_IMAGETYPE_DEFAULT) { - break; - } - stage->images[img_index].image_type = img_desc->image_type; - stage->images[img_index].sampler_type = img_desc->sampler_type; - stage->num_images++; - } - } -} - -typedef struct { - sg_shader shader_id; - sg_index_type index_type; - bool use_instanced_draw; - bool vertex_layout_valid[SG_MAX_SHADERSTAGE_BUFFERS]; - int color_attachment_count; - sg_pixel_format color_formats[SG_MAX_COLOR_ATTACHMENTS]; - sg_pixel_format depth_format; - int sample_count; - float depth_bias; - float depth_bias_slope_scale; - float depth_bias_clamp; - sg_color blend_color; -} _sg_pipeline_common_t; - -_SOKOL_PRIVATE void _sg_pipeline_common_init(_sg_pipeline_common_t* cmn, const sg_pipeline_desc* desc) { - SOKOL_ASSERT((desc->color_count >= 1) && (desc->color_count <= SG_MAX_COLOR_ATTACHMENTS)); - cmn->shader_id = desc->shader; - cmn->index_type = desc->index_type; - cmn->use_instanced_draw = false; - for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { - cmn->vertex_layout_valid[i] = false; - } - cmn->color_attachment_count = desc->color_count; - for (int i = 0; i < cmn->color_attachment_count; i++) { - cmn->color_formats[i] = desc->colors[i].pixel_format; - } - cmn->depth_format = desc->depth.pixel_format; - cmn->sample_count = desc->sample_count; - cmn->depth_bias = desc->depth.bias; - cmn->depth_bias_slope_scale = desc->depth.bias_slope_scale; - cmn->depth_bias_clamp = desc->depth.bias_clamp; - cmn->blend_color = desc->blend_color; -} - -typedef struct { - sg_image image_id; - int mip_level; - int slice; -} _sg_pass_attachment_common_t; - -typedef struct { - int num_color_atts; - _sg_pass_attachment_common_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_pass_attachment_common_t ds_att; -} _sg_pass_common_t; - -_SOKOL_PRIVATE void _sg_pass_common_init(_sg_pass_common_t* cmn, const sg_pass_desc* desc) { - const sg_pass_attachment_desc* att_desc; - _sg_pass_attachment_common_t* att; - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - att_desc = &desc->color_attachments[i]; - if (att_desc->image.id != SG_INVALID_ID) { - cmn->num_color_atts++; - att = &cmn->color_atts[i]; - att->image_id = att_desc->image; - att->mip_level = att_desc->mip_level; - att->slice = att_desc->slice; - } - } - att_desc = &desc->depth_stencil_attachment; - if (att_desc->image.id != SG_INVALID_ID) { - att = &cmn->ds_att; - att->image_id = att_desc->image; - att->mip_level = att_desc->mip_level; - att->slice = att_desc->slice; - } -} - -/*=== GENERIC SAMPLER CACHE ==================================================*/ - -/* - this is used by the Metal and WGPU backends to reduce the - number of sampler state objects created through the backend API -*/ -typedef struct { - sg_filter min_filter; - sg_filter mag_filter; - sg_wrap wrap_u; - sg_wrap wrap_v; - sg_wrap wrap_w; - sg_border_color border_color; - uint32_t max_anisotropy; - int min_lod; /* orig min/max_lod is float, this is int(min/max_lod*1000.0) */ - int max_lod; - uintptr_t sampler_handle; -} _sg_sampler_cache_item_t; - -typedef struct { - int capacity; - int num_items; - _sg_sampler_cache_item_t* items; -} _sg_sampler_cache_t; - -_SOKOL_PRIVATE void _sg_smpcache_init(_sg_sampler_cache_t* cache, int capacity) { - SOKOL_ASSERT(cache && (capacity > 0)); - memset(cache, 0, sizeof(_sg_sampler_cache_t)); - cache->capacity = capacity; - const size_t size = (size_t)cache->capacity * sizeof(_sg_sampler_cache_item_t); - cache->items = (_sg_sampler_cache_item_t*) SOKOL_MALLOC(size); - SOKOL_ASSERT(cache->items); - memset(cache->items, 0, size); -} - -_SOKOL_PRIVATE void _sg_smpcache_discard(_sg_sampler_cache_t* cache) { - SOKOL_ASSERT(cache && cache->items); - SOKOL_FREE(cache->items); - cache->items = 0; - cache->num_items = 0; - cache->capacity = 0; -} - -_SOKOL_PRIVATE int _sg_smpcache_minlod_int(float min_lod) { - return (int) (min_lod * 1000.0f); -} - -_SOKOL_PRIVATE int _sg_smpcache_maxlod_int(float max_lod) { - return (int) (_sg_clamp(max_lod, 0.0f, 1000.0f) * 1000.0f); -} - -_SOKOL_PRIVATE int _sg_smpcache_find_item(const _sg_sampler_cache_t* cache, const sg_image_desc* img_desc) { - /* return matching sampler cache item index or -1 */ - SOKOL_ASSERT(cache && cache->items); - SOKOL_ASSERT(img_desc); - const int min_lod = _sg_smpcache_minlod_int(img_desc->min_lod); - const int max_lod = _sg_smpcache_maxlod_int(img_desc->max_lod); - for (int i = 0; i < cache->num_items; i++) { - const _sg_sampler_cache_item_t* item = &cache->items[i]; - if ((img_desc->min_filter == item->min_filter) && - (img_desc->mag_filter == item->mag_filter) && - (img_desc->wrap_u == item->wrap_u) && - (img_desc->wrap_v == item->wrap_v) && - (img_desc->wrap_w == item->wrap_w) && - (img_desc->max_anisotropy == item->max_anisotropy) && - (img_desc->border_color == item->border_color) && - (min_lod == item->min_lod) && - (max_lod == item->max_lod)) - { - return i; - } - } - /* fallthrough: no matching cache item found */ - return -1; -} - -_SOKOL_PRIVATE void _sg_smpcache_add_item(_sg_sampler_cache_t* cache, const sg_image_desc* img_desc, uintptr_t sampler_handle) { - SOKOL_ASSERT(cache && cache->items); - SOKOL_ASSERT(img_desc); - SOKOL_ASSERT(cache->num_items < cache->capacity); - const int item_index = cache->num_items++; - _sg_sampler_cache_item_t* item = &cache->items[item_index]; - item->min_filter = img_desc->min_filter; - item->mag_filter = img_desc->mag_filter; - item->wrap_u = img_desc->wrap_u; - item->wrap_v = img_desc->wrap_v; - item->wrap_w = img_desc->wrap_w; - item->border_color = img_desc->border_color; - item->max_anisotropy = img_desc->max_anisotropy; - item->min_lod = _sg_smpcache_minlod_int(img_desc->min_lod); - item->max_lod = _sg_smpcache_maxlod_int(img_desc->max_lod); - item->sampler_handle = sampler_handle; -} - -_SOKOL_PRIVATE uintptr_t _sg_smpcache_sampler(_sg_sampler_cache_t* cache, int item_index) { - SOKOL_ASSERT(cache && cache->items); - SOKOL_ASSERT(item_index < cache->num_items); - return cache->items[item_index].sampler_handle; -} - -/*=== DUMMY BACKEND DECLARATIONS =============================================*/ -#if defined(SOKOL_DUMMY_BACKEND) -typedef struct { - _sg_slot_t slot; - _sg_buffer_common_t cmn; -} _sg_dummy_buffer_t; -typedef _sg_dummy_buffer_t _sg_buffer_t; - -typedef struct { - _sg_slot_t slot; - _sg_image_common_t cmn; -} _sg_dummy_image_t; -typedef _sg_dummy_image_t _sg_image_t; - -typedef struct { - _sg_slot_t slot; - _sg_shader_common_t cmn; -} _sg_dummy_shader_t; -typedef _sg_dummy_shader_t _sg_shader_t; - -typedef struct { - _sg_slot_t slot; - _sg_shader_t* shader; - _sg_pipeline_common_t cmn; -} _sg_dummy_pipeline_t; -typedef _sg_dummy_pipeline_t _sg_pipeline_t; - -typedef struct { - _sg_image_t* image; -} _sg_dummy_attachment_t; - -typedef struct { - _sg_slot_t slot; - _sg_pass_common_t cmn; - struct { - _sg_dummy_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_dummy_attachment_t ds_att; - } dmy; -} _sg_dummy_pass_t; -typedef _sg_dummy_pass_t _sg_pass_t; -typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; - -typedef struct { - _sg_slot_t slot; -} _sg_dummy_context_t; -typedef _sg_dummy_context_t _sg_context_t; - -/*== GL BACKEND DECLARATIONS =================================================*/ -#elif defined(_SOKOL_ANY_GL) -typedef struct { - _sg_slot_t slot; - _sg_buffer_common_t cmn; - struct { - GLuint buf[SG_NUM_INFLIGHT_FRAMES]; - bool ext_buffers; /* if true, external buffers were injected with sg_buffer_desc.gl_buffers */ - } gl; -} _sg_gl_buffer_t; -typedef _sg_gl_buffer_t _sg_buffer_t; - -typedef struct { - _sg_slot_t slot; - _sg_image_common_t cmn; - struct { - GLenum target; - GLuint depth_render_buffer; - GLuint msaa_render_buffer; - GLuint tex[SG_NUM_INFLIGHT_FRAMES]; - bool ext_textures; /* if true, external textures were injected with sg_image_desc.gl_textures */ - } gl; -} _sg_gl_image_t; -typedef _sg_gl_image_t _sg_image_t; - -typedef struct { - GLint gl_loc; - sg_uniform_type type; - uint16_t count; - uint16_t offset; -} _sg_gl_uniform_t; - -typedef struct { - int num_uniforms; - _sg_gl_uniform_t uniforms[SG_MAX_UB_MEMBERS]; -} _sg_gl_uniform_block_t; - -typedef struct { - int gl_tex_slot; -} _sg_gl_shader_image_t; - -typedef struct { - _sg_str_t name; -} _sg_gl_shader_attr_t; - -typedef struct { - _sg_gl_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; - _sg_gl_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; -} _sg_gl_shader_stage_t; - -typedef struct { - _sg_slot_t slot; - _sg_shader_common_t cmn; - struct { - GLuint prog; - _sg_gl_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; - _sg_gl_shader_stage_t stage[SG_NUM_SHADER_STAGES]; - } gl; -} _sg_gl_shader_t; -typedef _sg_gl_shader_t _sg_shader_t; - -typedef struct { - int8_t vb_index; /* -1 if attr is not enabled */ - int8_t divisor; /* -1 if not initialized */ - uint8_t stride; - uint8_t size; - uint8_t normalized; - int offset; - GLenum type; -} _sg_gl_attr_t; - -typedef struct { - _sg_slot_t slot; - _sg_pipeline_common_t cmn; - _sg_shader_t* shader; - struct { - _sg_gl_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; - sg_depth_state depth; - sg_stencil_state stencil; - sg_primitive_type primitive_type; - sg_blend_state blend; - sg_color_mask color_write_mask[SG_MAX_COLOR_ATTACHMENTS]; - sg_cull_mode cull_mode; - sg_face_winding face_winding; - int sample_count; - bool alpha_to_coverage_enabled; - } gl; -} _sg_gl_pipeline_t; -typedef _sg_gl_pipeline_t _sg_pipeline_t; - -typedef struct { - _sg_image_t* image; - GLuint gl_msaa_resolve_buffer; -} _sg_gl_attachment_t; - -typedef struct { - _sg_slot_t slot; - _sg_pass_common_t cmn; - struct { - GLuint fb; - _sg_gl_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_gl_attachment_t ds_att; - } gl; -} _sg_gl_pass_t; -typedef _sg_gl_pass_t _sg_pass_t; -typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; - -typedef struct { - _sg_slot_t slot; - #if !defined(SOKOL_GLES2) - GLuint vao; - #endif - GLuint default_framebuffer; -} _sg_gl_context_t; -typedef _sg_gl_context_t _sg_context_t; - -typedef struct { - _sg_gl_attr_t gl_attr; - GLuint gl_vbuf; -} _sg_gl_cache_attr_t; - -typedef struct { - GLenum target; - GLuint texture; -} _sg_gl_texture_bind_slot; - -typedef struct { - sg_depth_state depth; - sg_stencil_state stencil; - sg_blend_state blend; - sg_color_mask color_write_mask[SG_MAX_COLOR_ATTACHMENTS]; - sg_cull_mode cull_mode; - sg_face_winding face_winding; - bool polygon_offset_enabled; - int sample_count; - sg_color blend_color; - bool alpha_to_coverage_enabled; - _sg_gl_cache_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; - GLuint vertex_buffer; - GLuint index_buffer; - GLuint stored_vertex_buffer; - GLuint stored_index_buffer; - GLuint prog; - _sg_gl_texture_bind_slot textures[SG_MAX_SHADERSTAGE_IMAGES]; - _sg_gl_texture_bind_slot stored_texture; - int cur_ib_offset; - GLenum cur_primitive_type; - GLenum cur_index_type; - GLenum cur_active_texture; - _sg_pipeline_t* cur_pipeline; - sg_pipeline cur_pipeline_id; -} _sg_gl_state_cache_t; - -typedef struct { - bool valid; - bool gles2; - bool in_pass; - int cur_pass_width; - int cur_pass_height; - _sg_context_t* cur_context; - _sg_pass_t* cur_pass; - sg_pass cur_pass_id; - _sg_gl_state_cache_t cache; - bool ext_anisotropic; - GLint max_anisotropy; - GLint max_combined_texture_image_units; - #if _SOKOL_USE_WIN32_GL_LOADER - HINSTANCE opengl32_dll; - #endif -} _sg_gl_backend_t; - -/*== D3D11 BACKEND DECLARATIONS ==============================================*/ -#elif defined(SOKOL_D3D11) - -typedef struct { - _sg_slot_t slot; - _sg_buffer_common_t cmn; - struct { - ID3D11Buffer* buf; - } d3d11; -} _sg_d3d11_buffer_t; -typedef _sg_d3d11_buffer_t _sg_buffer_t; - -typedef struct { - _sg_slot_t slot; - _sg_image_common_t cmn; - struct { - DXGI_FORMAT format; - ID3D11Texture2D* tex2d; - ID3D11Texture3D* tex3d; - ID3D11Texture2D* texds; - ID3D11Texture2D* texmsaa; - ID3D11ShaderResourceView* srv; - ID3D11SamplerState* smp; - } d3d11; -} _sg_d3d11_image_t; -typedef _sg_d3d11_image_t _sg_image_t; - -typedef struct { - _sg_str_t sem_name; - int sem_index; -} _sg_d3d11_shader_attr_t; - -typedef struct { - ID3D11Buffer* cbufs[SG_MAX_SHADERSTAGE_UBS]; -} _sg_d3d11_shader_stage_t; - -typedef struct { - _sg_slot_t slot; - _sg_shader_common_t cmn; - struct { - _sg_d3d11_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; - _sg_d3d11_shader_stage_t stage[SG_NUM_SHADER_STAGES]; - ID3D11VertexShader* vs; - ID3D11PixelShader* fs; - void* vs_blob; - size_t vs_blob_length; - } d3d11; -} _sg_d3d11_shader_t; -typedef _sg_d3d11_shader_t _sg_shader_t; - -typedef struct { - _sg_slot_t slot; - _sg_pipeline_common_t cmn; - _sg_shader_t* shader; - struct { - UINT stencil_ref; - UINT vb_strides[SG_MAX_SHADERSTAGE_BUFFERS]; - D3D_PRIMITIVE_TOPOLOGY topology; - DXGI_FORMAT index_format; - ID3D11InputLayout* il; - ID3D11RasterizerState* rs; - ID3D11DepthStencilState* dss; - ID3D11BlendState* bs; - } d3d11; -} _sg_d3d11_pipeline_t; -typedef _sg_d3d11_pipeline_t _sg_pipeline_t; - -typedef struct { - _sg_image_t* image; - ID3D11RenderTargetView* rtv; -} _sg_d3d11_color_attachment_t; - -typedef struct { - _sg_image_t* image; - ID3D11DepthStencilView* dsv; -} _sg_d3d11_ds_attachment_t; - -typedef struct { - _sg_slot_t slot; - _sg_pass_common_t cmn; - struct { - _sg_d3d11_color_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_d3d11_ds_attachment_t ds_att; - } d3d11; -} _sg_d3d11_pass_t; -typedef _sg_d3d11_pass_t _sg_pass_t; -typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; - -typedef struct { - _sg_slot_t slot; -} _sg_d3d11_context_t; -typedef _sg_d3d11_context_t _sg_context_t; - -typedef struct { - bool valid; - ID3D11Device* dev; - ID3D11DeviceContext* ctx; - const void* (*rtv_cb)(void); - const void* (*rtv_userdata_cb)(void*); - const void* (*dsv_cb)(void); - const void* (*dsv_userdata_cb)(void*); - void* user_data; - bool in_pass; - bool use_indexed_draw; - bool use_instanced_draw; - int cur_width; - int cur_height; - int num_rtvs; - _sg_pass_t* cur_pass; - sg_pass cur_pass_id; - _sg_pipeline_t* cur_pipeline; - sg_pipeline cur_pipeline_id; - ID3D11RenderTargetView* cur_rtvs[SG_MAX_COLOR_ATTACHMENTS]; - ID3D11DepthStencilView* cur_dsv; - /* on-demand loaded d3dcompiler_47.dll handles */ - HINSTANCE d3dcompiler_dll; - bool d3dcompiler_dll_load_failed; - pD3DCompile D3DCompile_func; - /* global subresourcedata array for texture updates */ - D3D11_SUBRESOURCE_DATA subres_data[SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS]; -} _sg_d3d11_backend_t; - -/*=== METAL BACKEND DECLARATIONS =============================================*/ -#elif defined(SOKOL_METAL) - -#if defined(_SG_TARGET_MACOS) || defined(_SG_TARGET_IOS_SIMULATOR) -#define _SG_MTL_UB_ALIGN (256) -#else -#define _SG_MTL_UB_ALIGN (16) -#endif -#define _SG_MTL_INVALID_SLOT_INDEX (0) - -typedef struct { - uint32_t frame_index; /* frame index at which it is safe to release this resource */ - int slot_index; -} _sg_mtl_release_item_t; - -typedef struct { - NSMutableArray* pool; - int num_slots; - int free_queue_top; - int* free_queue; - int release_queue_front; - int release_queue_back; - _sg_mtl_release_item_t* release_queue; -} _sg_mtl_idpool_t; - -typedef struct { - _sg_slot_t slot; - _sg_buffer_common_t cmn; - struct { - int buf[SG_NUM_INFLIGHT_FRAMES]; /* index into _sg_mtl_pool */ - } mtl; -} _sg_mtl_buffer_t; -typedef _sg_mtl_buffer_t _sg_buffer_t; - -typedef struct { - _sg_slot_t slot; - _sg_image_common_t cmn; - struct { - int tex[SG_NUM_INFLIGHT_FRAMES]; - int depth_tex; - int msaa_tex; - int sampler_state; - } mtl; -} _sg_mtl_image_t; -typedef _sg_mtl_image_t _sg_image_t; - -typedef struct { - int mtl_lib; - int mtl_func; -} _sg_mtl_shader_stage_t; - -typedef struct { - _sg_slot_t slot; - _sg_shader_common_t cmn; - struct { - _sg_mtl_shader_stage_t stage[SG_NUM_SHADER_STAGES]; - } mtl; -} _sg_mtl_shader_t; -typedef _sg_mtl_shader_t _sg_shader_t; - -typedef struct { - _sg_slot_t slot; - _sg_pipeline_common_t cmn; - _sg_shader_t* shader; - struct { - MTLPrimitiveType prim_type; - int index_size; - MTLIndexType index_type; - MTLCullMode cull_mode; - MTLWinding winding; - uint32_t stencil_ref; - int rps; - int dss; - } mtl; -} _sg_mtl_pipeline_t; -typedef _sg_mtl_pipeline_t _sg_pipeline_t; - -typedef struct { - _sg_image_t* image; -} _sg_mtl_attachment_t; - -typedef struct { - _sg_slot_t slot; - _sg_pass_common_t cmn; - struct { - _sg_mtl_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_mtl_attachment_t ds_att; - } mtl; -} _sg_mtl_pass_t; -typedef _sg_mtl_pass_t _sg_pass_t; -typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; - -typedef struct { - _sg_slot_t slot; -} _sg_mtl_context_t; -typedef _sg_mtl_context_t _sg_context_t; - -/* resouce binding state cache */ -typedef struct { - const _sg_pipeline_t* cur_pipeline; - sg_pipeline cur_pipeline_id; - const _sg_buffer_t* cur_indexbuffer; - int cur_indexbuffer_offset; - sg_buffer cur_indexbuffer_id; - const _sg_buffer_t* cur_vertexbuffers[SG_MAX_SHADERSTAGE_BUFFERS]; - int cur_vertexbuffer_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; - sg_buffer cur_vertexbuffer_ids[SG_MAX_SHADERSTAGE_BUFFERS]; - const _sg_image_t* cur_vs_images[SG_MAX_SHADERSTAGE_IMAGES]; - sg_image cur_vs_image_ids[SG_MAX_SHADERSTAGE_IMAGES]; - const _sg_image_t* cur_fs_images[SG_MAX_SHADERSTAGE_IMAGES]; - sg_image cur_fs_image_ids[SG_MAX_SHADERSTAGE_IMAGES]; -} _sg_mtl_state_cache_t; - -typedef struct { - bool valid; - const void*(*renderpass_descriptor_cb)(void); - const void*(*renderpass_descriptor_userdata_cb)(void*); - const void*(*drawable_cb)(void); - const void*(*drawable_userdata_cb)(void*); - void* user_data; - uint32_t frame_index; - uint32_t cur_frame_rotate_index; - int ub_size; - int cur_ub_offset; - uint8_t* cur_ub_base_ptr; - bool in_pass; - bool pass_valid; - int cur_width; - int cur_height; - _sg_mtl_state_cache_t state_cache; - _sg_sampler_cache_t sampler_cache; - _sg_mtl_idpool_t idpool; - dispatch_semaphore_t sem; - id device; - id cmd_queue; - id cmd_buffer; - id cmd_encoder; - id uniform_buffers[SG_NUM_INFLIGHT_FRAMES]; -} _sg_mtl_backend_t; - -/*=== WGPU BACKEND DECLARATIONS ==============================================*/ -#elif defined(SOKOL_WGPU) - -#define _SG_WGPU_STAGING_ALIGN (256) -#define _SG_WGPU_STAGING_PIPELINE_SIZE (8) -#define _SG_WGPU_ROWPITCH_ALIGN (256) -#define _SG_WGPU_MAX_SHADERSTAGE_IMAGES (8) -#define _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE (1<<16) - -typedef struct { - _sg_slot_t slot; - _sg_buffer_common_t cmn; - struct { - WGPUBuffer buf; - } wgpu; -} _sg_wgpu_buffer_t; -typedef _sg_wgpu_buffer_t _sg_buffer_t; - -typedef struct { - _sg_slot_t slot; - _sg_image_common_t cmn; - struct { - WGPUTexture tex; - WGPUTextureView tex_view; - WGPUTexture msaa_tex; - WGPUSampler sampler; - } wgpu; -} _sg_wgpu_image_t; -typedef _sg_wgpu_image_t _sg_image_t; - -typedef struct { - WGPUShaderModule module; - WGPUBindGroupLayout bind_group_layout; - _sg_str_t entry; -} _sg_wgpu_shader_stage_t; - -typedef struct { - _sg_slot_t slot; - _sg_shader_common_t cmn; - struct { - _sg_wgpu_shader_stage_t stage[SG_NUM_SHADER_STAGES]; - } wgpu; -} _sg_wgpu_shader_t; -typedef _sg_wgpu_shader_t _sg_shader_t; - -typedef struct { - _sg_slot_t slot; - _sg_pipeline_common_t cmn; - _sg_shader_t* shader; - struct { - WGPURenderPipeline pip; - uint32_t stencil_ref; - } wgpu; -} _sg_wgpu_pipeline_t; -typedef _sg_wgpu_pipeline_t _sg_pipeline_t; - -typedef struct { - _sg_image_t* image; - WGPUTextureView render_tex_view; - WGPUTextureView resolve_tex_view; -} _sg_wgpu_attachment_t; - -typedef struct { - _sg_slot_t slot; - _sg_pass_common_t cmn; - struct { - _sg_wgpu_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_wgpu_attachment_t ds_att; - } wgpu; -} _sg_wgpu_pass_t; -typedef _sg_wgpu_pass_t _sg_pass_t; -typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; - -typedef struct { - _sg_slot_t slot; -} _sg_wgpu_context_t; -typedef _sg_wgpu_context_t _sg_context_t; - -/* a pool of per-frame uniform buffers */ -typedef struct { - WGPUBindGroupLayout bindgroup_layout; - uint32_t num_bytes; - uint32_t offset; /* current offset into current frame's mapped uniform buffer */ - uint32_t bind_offsets[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; - WGPUBuffer buf; /* the GPU-side uniform buffer */ - WGPUBindGroup bindgroup; - struct { - int num; - int cur; - WGPUBuffer buf[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* CPU-side staging buffers */ - uint8_t* ptr[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* if != 0, staging buffer currently mapped */ - } stage; -} _sg_wgpu_ubpool_t; - -/* ...a similar pool (like uniform buffer pool) of dynamic-resource staging buffers */ -typedef struct { - uint32_t num_bytes; - uint32_t offset; /* current offset into current frame's staging buffer */ - int num; /* number of staging buffers */ - int cur; /* this frame's staging buffer */ - WGPUBuffer buf[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* CPU-side staging buffers */ - uint8_t* ptr[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* if != 0, staging buffer currently mapped */ -} _sg_wgpu_stagingpool_t; - -/* the WGPU backend state */ -typedef struct { - bool valid; - bool in_pass; - bool draw_indexed; - int cur_width; - int cur_height; - WGPUDevice dev; - WGPUTextureView (*render_view_cb)(void); - WGPUTextureView (*render_view_userdata_cb)(void*); - WGPUTextureView (*resolve_view_cb)(void); - WGPUTextureView (*resolve_view_userdata_cb)(void*); - WGPUTextureView (*depth_stencil_view_cb)(void); - WGPUTextureView (*depth_stencil_view_userdata_cb)(void*); - void* user_data; - WGPUQueue queue; - WGPUCommandEncoder render_cmd_enc; - WGPUCommandEncoder staging_cmd_enc; - WGPURenderPassEncoder pass_enc; - WGPUBindGroup empty_bind_group; - const _sg_pipeline_t* cur_pipeline; - sg_pipeline cur_pipeline_id; - _sg_sampler_cache_t sampler_cache; - _sg_wgpu_ubpool_t ub; - _sg_wgpu_stagingpool_t staging; -} _sg_wgpu_backend_t; -#endif - -/*=== RESOURCE POOL DECLARATIONS =============================================*/ - -/* this *MUST* remain 0 */ -#define _SG_INVALID_SLOT_INDEX (0) - -typedef struct { - int size; - int queue_top; - uint32_t* gen_ctrs; - int* free_queue; -} _sg_pool_t; - -typedef struct { - _sg_pool_t buffer_pool; - _sg_pool_t image_pool; - _sg_pool_t shader_pool; - _sg_pool_t pipeline_pool; - _sg_pool_t pass_pool; - _sg_pool_t context_pool; - _sg_buffer_t* buffers; - _sg_image_t* images; - _sg_shader_t* shaders; - _sg_pipeline_t* pipelines; - _sg_pass_t* passes; - _sg_context_t* contexts; -} _sg_pools_t; - -/*=== VALIDATION LAYER DECLARATIONS ==========================================*/ -typedef enum { - /* special case 'validation was successful' */ - _SG_VALIDATE_SUCCESS, - - /* buffer creation */ - _SG_VALIDATE_BUFFERDESC_CANARY, - _SG_VALIDATE_BUFFERDESC_SIZE, - _SG_VALIDATE_BUFFERDESC_DATA, - _SG_VALIDATE_BUFFERDESC_DATA_SIZE, - _SG_VALIDATE_BUFFERDESC_NO_DATA, - - /* image data (for image creation and updating) */ - _SG_VALIDATE_IMAGEDATA_NODATA, - _SG_VALIDATE_IMAGEDATA_DATA_SIZE, - - /* image creation */ - _SG_VALIDATE_IMAGEDESC_CANARY, - _SG_VALIDATE_IMAGEDESC_WIDTH, - _SG_VALIDATE_IMAGEDESC_HEIGHT, - _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT, - _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, - _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT, - _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT, - _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE, - _SG_VALIDATE_IMAGEDESC_RT_NO_DATA, - _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA, - _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA, - _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE, - - /* shader creation */ - _SG_VALIDATE_SHADERDESC_CANARY, - _SG_VALIDATE_SHADERDESC_SOURCE, - _SG_VALIDATE_SHADERDESC_BYTECODE, - _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE, - _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, - _SG_VALIDATE_SHADERDESC_NO_CONT_UBS, - _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS, - _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS, - _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS, - _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME, - _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, - _SG_VALIDATE_SHADERDESC_UB_ARRAY_COUNT, - _SG_VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE, - _SG_VALIDATE_SHADERDESC_IMG_NAME, - _SG_VALIDATE_SHADERDESC_ATTR_NAMES, - _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS, - _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG, - - /* pipeline creation */ - _SG_VALIDATE_PIPELINEDESC_CANARY, - _SG_VALIDATE_PIPELINEDESC_SHADER, - _SG_VALIDATE_PIPELINEDESC_NO_ATTRS, - _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, - _SG_VALIDATE_PIPELINEDESC_ATTR_NAME, - _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, - - /* pass creation */ - _SG_VALIDATE_PASSDESC_CANARY, - _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS, - _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS, - _SG_VALIDATE_PASSDESC_IMAGE, - _SG_VALIDATE_PASSDESC_MIPLEVEL, - _SG_VALIDATE_PASSDESC_FACE, - _SG_VALIDATE_PASSDESC_LAYER, - _SG_VALIDATE_PASSDESC_SLICE, - _SG_VALIDATE_PASSDESC_IMAGE_NO_RT, - _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT, - _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT, - _SG_VALIDATE_PASSDESC_IMAGE_SIZES, - _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS, - - /* sg_begin_pass validation */ - _SG_VALIDATE_BEGINPASS_PASS, - _SG_VALIDATE_BEGINPASS_IMAGE, - - /* sg_apply_pipeline validation */ - _SG_VALIDATE_APIP_PIPELINE_VALID_ID, - _SG_VALIDATE_APIP_PIPELINE_EXISTS, - _SG_VALIDATE_APIP_PIPELINE_VALID, - _SG_VALIDATE_APIP_SHADER_EXISTS, - _SG_VALIDATE_APIP_SHADER_VALID, - _SG_VALIDATE_APIP_ATT_COUNT, - _SG_VALIDATE_APIP_COLOR_FORMAT, - _SG_VALIDATE_APIP_DEPTH_FORMAT, - _SG_VALIDATE_APIP_SAMPLE_COUNT, - - /* sg_apply_bindings validation */ - _SG_VALIDATE_ABND_PIPELINE, - _SG_VALIDATE_ABND_PIPELINE_EXISTS, - _SG_VALIDATE_ABND_PIPELINE_VALID, - _SG_VALIDATE_ABND_VBS, - _SG_VALIDATE_ABND_VB_EXISTS, - _SG_VALIDATE_ABND_VB_TYPE, - _SG_VALIDATE_ABND_VB_OVERFLOW, - _SG_VALIDATE_ABND_NO_IB, - _SG_VALIDATE_ABND_IB, - _SG_VALIDATE_ABND_IB_EXISTS, - _SG_VALIDATE_ABND_IB_TYPE, - _SG_VALIDATE_ABND_IB_OVERFLOW, - _SG_VALIDATE_ABND_VS_IMGS, - _SG_VALIDATE_ABND_VS_IMG_EXISTS, - _SG_VALIDATE_ABND_VS_IMG_TYPES, - _SG_VALIDATE_ABND_FS_IMGS, - _SG_VALIDATE_ABND_FS_IMG_EXISTS, - _SG_VALIDATE_ABND_FS_IMG_TYPES, - - /* sg_apply_uniforms validation */ - _SG_VALIDATE_AUB_NO_PIPELINE, - _SG_VALIDATE_AUB_NO_UB_AT_SLOT, - _SG_VALIDATE_AUB_SIZE, - - /* sg_update_buffer validation */ - _SG_VALIDATE_UPDATEBUF_USAGE, - _SG_VALIDATE_UPDATEBUF_SIZE, - _SG_VALIDATE_UPDATEBUF_ONCE, - _SG_VALIDATE_UPDATEBUF_APPEND, - - /* sg_append_buffer validation */ - _SG_VALIDATE_APPENDBUF_USAGE, - _SG_VALIDATE_APPENDBUF_SIZE, - _SG_VALIDATE_APPENDBUF_UPDATE, - - /* sg_update_image validation */ - _SG_VALIDATE_UPDIMG_USAGE, - _SG_VALIDATE_UPDIMG_NOTENOUGHDATA, - _SG_VALIDATE_UPDIMG_ONCE -} _sg_validate_error_t; - -/*=== GENERIC BACKEND STATE ==================================================*/ - -typedef struct { - bool valid; - sg_desc desc; /* original desc with default values patched in */ - uint32_t frame_index; - sg_context active_context; - sg_pass cur_pass; - sg_pipeline cur_pipeline; - bool pass_valid; - bool bindings_valid; - bool next_draw_valid; - #if defined(SOKOL_DEBUG) - _sg_validate_error_t validate_error; - #endif - _sg_pools_t pools; - sg_backend backend; - sg_features features; - sg_limits limits; - sg_pixelformat_info formats[_SG_PIXELFORMAT_NUM]; - #if defined(_SOKOL_ANY_GL) - _sg_gl_backend_t gl; - #elif defined(SOKOL_METAL) - _sg_mtl_backend_t mtl; - #elif defined(SOKOL_D3D11) - _sg_d3d11_backend_t d3d11; - #elif defined(SOKOL_WGPU) - _sg_wgpu_backend_t wgpu; - #endif - #if defined(SOKOL_TRACE_HOOKS) - sg_trace_hooks hooks; - #endif -} _sg_state_t; -static _sg_state_t _sg; - -/*-- helper functions --------------------------------------------------------*/ - -_SOKOL_PRIVATE bool _sg_strempty(const _sg_str_t* str) { - return 0 == str->buf[0]; -} - -_SOKOL_PRIVATE const char* _sg_strptr(const _sg_str_t* str) { - return &str->buf[0]; -} - -_SOKOL_PRIVATE void _sg_strcpy(_sg_str_t* dst, const char* src) { - SOKOL_ASSERT(dst); - if (src) { - #if defined(_MSC_VER) - strncpy_s(dst->buf, _SG_STRING_SIZE, src, (_SG_STRING_SIZE-1)); - #else - strncpy(dst->buf, src, _SG_STRING_SIZE); - #endif - dst->buf[_SG_STRING_SIZE-1] = 0; - } - else { - memset(dst->buf, 0, _SG_STRING_SIZE); - } -} - -_SOKOL_PRIVATE uint32_t _sg_align_u32(uint32_t val, uint32_t align) { - SOKOL_ASSERT((align > 0) && ((align & (align - 1)) == 0)); - return (val + (align - 1)) & ~(align - 1); -} - -/* return byte size of a vertex format */ -_SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { - switch (fmt) { - case SG_VERTEXFORMAT_FLOAT: return 4; - case SG_VERTEXFORMAT_FLOAT2: return 8; - case SG_VERTEXFORMAT_FLOAT3: return 12; - case SG_VERTEXFORMAT_FLOAT4: return 16; - case SG_VERTEXFORMAT_BYTE4: return 4; - case SG_VERTEXFORMAT_BYTE4N: return 4; - case SG_VERTEXFORMAT_UBYTE4: return 4; - case SG_VERTEXFORMAT_UBYTE4N: return 4; - case SG_VERTEXFORMAT_SHORT2: return 4; - case SG_VERTEXFORMAT_SHORT2N: return 4; - case SG_VERTEXFORMAT_USHORT2N: return 4; - case SG_VERTEXFORMAT_SHORT4: return 8; - case SG_VERTEXFORMAT_SHORT4N: return 8; - case SG_VERTEXFORMAT_USHORT4N: return 8; - case SG_VERTEXFORMAT_UINT10_N2: return 4; - case SG_VERTEXFORMAT_INVALID: return 0; - default: - SOKOL_UNREACHABLE; - return -1; - } -} - -_SOKOL_PRIVATE uint32_t _sg_uniform_alignment(sg_uniform_type type, int array_count, sg_uniform_layout ub_layout) { - if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) { - return 1; - } - else { - SOKOL_ASSERT(array_count > 0); - if (array_count == 1) { - switch (type) { - case SG_UNIFORMTYPE_FLOAT: - case SG_UNIFORMTYPE_INT: - return 4; - case SG_UNIFORMTYPE_FLOAT2: - case SG_UNIFORMTYPE_INT2: - return 8; - case SG_UNIFORMTYPE_FLOAT3: - case SG_UNIFORMTYPE_FLOAT4: - case SG_UNIFORMTYPE_INT3: - case SG_UNIFORMTYPE_INT4: - return 16; - case SG_UNIFORMTYPE_MAT4: - return 16; - default: - SOKOL_UNREACHABLE; - return 1; - } - } - else { - return 16; - } - } -} - -_SOKOL_PRIVATE uint32_t _sg_uniform_size(sg_uniform_type type, int array_count, sg_uniform_layout ub_layout) { - SOKOL_ASSERT(array_count > 0); - if (array_count == 1) { - switch (type) { - case SG_UNIFORMTYPE_FLOAT: - case SG_UNIFORMTYPE_INT: - return 4; - case SG_UNIFORMTYPE_FLOAT2: - case SG_UNIFORMTYPE_INT2: - return 8; - case SG_UNIFORMTYPE_FLOAT3: - case SG_UNIFORMTYPE_INT3: - return 12; - case SG_UNIFORMTYPE_FLOAT4: - case SG_UNIFORMTYPE_INT4: - return 16; - case SG_UNIFORMTYPE_MAT4: - return 64; - default: - SOKOL_UNREACHABLE; - return 0; - } - } - else { - if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) { - switch (type) { - case SG_UNIFORMTYPE_FLOAT: - case SG_UNIFORMTYPE_INT: - return 4 * (uint32_t)array_count; - case SG_UNIFORMTYPE_FLOAT2: - case SG_UNIFORMTYPE_INT2: - return 8 * (uint32_t)array_count; - case SG_UNIFORMTYPE_FLOAT3: - case SG_UNIFORMTYPE_INT3: - return 12 * (uint32_t)array_count; - case SG_UNIFORMTYPE_FLOAT4: - case SG_UNIFORMTYPE_INT4: - return 16 * (uint32_t)array_count; - case SG_UNIFORMTYPE_MAT4: - return 64 * (uint32_t)array_count; - default: - SOKOL_UNREACHABLE; - return 0; - } - } - else { - switch (type) { - case SG_UNIFORMTYPE_FLOAT: - case SG_UNIFORMTYPE_FLOAT2: - case SG_UNIFORMTYPE_FLOAT3: - case SG_UNIFORMTYPE_FLOAT4: - case SG_UNIFORMTYPE_INT: - case SG_UNIFORMTYPE_INT2: - case SG_UNIFORMTYPE_INT3: - case SG_UNIFORMTYPE_INT4: - return 16 * (uint32_t)array_count; - case SG_UNIFORMTYPE_MAT4: - return 64 * (uint32_t)array_count; - default: - SOKOL_UNREACHABLE; - return 0; - } - } - } -} - -/* return true if pixel format is a compressed format */ -_SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { - switch (fmt) { - case SG_PIXELFORMAT_BC1_RGBA: - case SG_PIXELFORMAT_BC2_RGBA: - case SG_PIXELFORMAT_BC3_RGBA: - case SG_PIXELFORMAT_BC4_R: - case SG_PIXELFORMAT_BC4_RSN: - case SG_PIXELFORMAT_BC5_RG: - case SG_PIXELFORMAT_BC5_RGSN: - case SG_PIXELFORMAT_BC6H_RGBF: - case SG_PIXELFORMAT_BC6H_RGBUF: - case SG_PIXELFORMAT_BC7_RGBA: - case SG_PIXELFORMAT_PVRTC_RGB_2BPP: - case SG_PIXELFORMAT_PVRTC_RGB_4BPP: - case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: - case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: - case SG_PIXELFORMAT_ETC2_RGB8: - case SG_PIXELFORMAT_ETC2_RGB8A1: - case SG_PIXELFORMAT_ETC2_RGBA8: - case SG_PIXELFORMAT_ETC2_RG11: - case SG_PIXELFORMAT_ETC2_RG11SN: - return true; - default: - return false; - } -} - -/* return true if pixel format is a valid render target format */ -_SOKOL_PRIVATE bool _sg_is_valid_rendertarget_color_format(sg_pixel_format fmt) { - const int fmt_index = (int) fmt; - SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); - return _sg.formats[fmt_index].render && !_sg.formats[fmt_index].depth; -} - -/* return true if pixel format is a valid depth format */ -_SOKOL_PRIVATE bool _sg_is_valid_rendertarget_depth_format(sg_pixel_format fmt) { - const int fmt_index = (int) fmt; - SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); - return _sg.formats[fmt_index].render && _sg.formats[fmt_index].depth; -} - -/* return true if pixel format is a depth-stencil format */ -_SOKOL_PRIVATE bool _sg_is_depth_stencil_format(sg_pixel_format fmt) { - return (SG_PIXELFORMAT_DEPTH_STENCIL == fmt); -} - -/* return the bytes-per-pixel for a pixel format */ -_SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { - switch (fmt) { - case SG_PIXELFORMAT_R8: - case SG_PIXELFORMAT_R8SN: - case SG_PIXELFORMAT_R8UI: - case SG_PIXELFORMAT_R8SI: - return 1; - - case SG_PIXELFORMAT_R16: - case SG_PIXELFORMAT_R16SN: - case SG_PIXELFORMAT_R16UI: - case SG_PIXELFORMAT_R16SI: - case SG_PIXELFORMAT_R16F: - case SG_PIXELFORMAT_RG8: - case SG_PIXELFORMAT_RG8SN: - case SG_PIXELFORMAT_RG8UI: - case SG_PIXELFORMAT_RG8SI: - return 2; - - case SG_PIXELFORMAT_R32UI: - case SG_PIXELFORMAT_R32SI: - case SG_PIXELFORMAT_R32F: - case SG_PIXELFORMAT_RG16: - case SG_PIXELFORMAT_RG16SN: - case SG_PIXELFORMAT_RG16UI: - case SG_PIXELFORMAT_RG16SI: - case SG_PIXELFORMAT_RG16F: - case SG_PIXELFORMAT_RGBA8: - case SG_PIXELFORMAT_RGBA8SN: - case SG_PIXELFORMAT_RGBA8UI: - case SG_PIXELFORMAT_RGBA8SI: - case SG_PIXELFORMAT_BGRA8: - case SG_PIXELFORMAT_RGB10A2: - case SG_PIXELFORMAT_RG11B10F: - return 4; - - case SG_PIXELFORMAT_RG32UI: - case SG_PIXELFORMAT_RG32SI: - case SG_PIXELFORMAT_RG32F: - case SG_PIXELFORMAT_RGBA16: - case SG_PIXELFORMAT_RGBA16SN: - case SG_PIXELFORMAT_RGBA16UI: - case SG_PIXELFORMAT_RGBA16SI: - case SG_PIXELFORMAT_RGBA16F: - return 8; - - case SG_PIXELFORMAT_RGBA32UI: - case SG_PIXELFORMAT_RGBA32SI: - case SG_PIXELFORMAT_RGBA32F: - return 16; - - default: - SOKOL_UNREACHABLE; - return 0; - } -} - -_SOKOL_PRIVATE int _sg_roundup(int val, int round_to) { - return (val+(round_to-1)) & ~(round_to-1); -} - -/* return row pitch for an image - - see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp - - For the special PVRTC pitch computation, see: - GL extension requirement (https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt) - - Quote: - - 6) How is the imageSize argument calculated for the CompressedTexImage2D - and CompressedTexSubImage2D functions. - - Resolution: For PVRTC 4BPP formats the imageSize is calculated as: - ( max(width, 8) * max(height, 8) * 4 + 7) / 8 - For PVRTC 2BPP formats the imageSize is calculated as: - ( max(width, 16) * max(height, 8) * 2 + 7) / 8 -*/ -_SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width, int row_align) { - int pitch; - switch (fmt) { - case SG_PIXELFORMAT_BC1_RGBA: - case SG_PIXELFORMAT_BC4_R: - case SG_PIXELFORMAT_BC4_RSN: - case SG_PIXELFORMAT_ETC2_RGB8: - case SG_PIXELFORMAT_ETC2_RGB8A1: - pitch = ((width + 3) / 4) * 8; - pitch = pitch < 8 ? 8 : pitch; - break; - case SG_PIXELFORMAT_BC2_RGBA: - case SG_PIXELFORMAT_BC3_RGBA: - case SG_PIXELFORMAT_BC5_RG: - case SG_PIXELFORMAT_BC5_RGSN: - case SG_PIXELFORMAT_BC6H_RGBF: - case SG_PIXELFORMAT_BC6H_RGBUF: - case SG_PIXELFORMAT_BC7_RGBA: - case SG_PIXELFORMAT_ETC2_RGBA8: - case SG_PIXELFORMAT_ETC2_RG11: - case SG_PIXELFORMAT_ETC2_RG11SN: - pitch = ((width + 3) / 4) * 16; - pitch = pitch < 16 ? 16 : pitch; - break; - case SG_PIXELFORMAT_PVRTC_RGB_4BPP: - case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: - pitch = (_sg_max(width, 8) * 4 + 7) / 8; - break; - case SG_PIXELFORMAT_PVRTC_RGB_2BPP: - case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: - pitch = (_sg_max(width, 16) * 2 + 7) / 8; - break; - default: - pitch = width * _sg_pixelformat_bytesize(fmt); - break; - } - pitch = _sg_roundup(pitch, row_align); - return pitch; -} - -/* compute the number of rows in a surface depending on pixel format */ -_SOKOL_PRIVATE int _sg_num_rows(sg_pixel_format fmt, int height) { - int num_rows; - switch (fmt) { - case SG_PIXELFORMAT_BC1_RGBA: - case SG_PIXELFORMAT_BC4_R: - case SG_PIXELFORMAT_BC4_RSN: - case SG_PIXELFORMAT_ETC2_RGB8: - case SG_PIXELFORMAT_ETC2_RGB8A1: - case SG_PIXELFORMAT_ETC2_RGBA8: - case SG_PIXELFORMAT_ETC2_RG11: - case SG_PIXELFORMAT_ETC2_RG11SN: - case SG_PIXELFORMAT_BC2_RGBA: - case SG_PIXELFORMAT_BC3_RGBA: - case SG_PIXELFORMAT_BC5_RG: - case SG_PIXELFORMAT_BC5_RGSN: - case SG_PIXELFORMAT_BC6H_RGBF: - case SG_PIXELFORMAT_BC6H_RGBUF: - case SG_PIXELFORMAT_BC7_RGBA: - num_rows = ((height + 3) / 4); - break; - case SG_PIXELFORMAT_PVRTC_RGB_4BPP: - case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: - case SG_PIXELFORMAT_PVRTC_RGB_2BPP: - case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: - /* NOTE: this is most likely not correct because it ignores any - PVCRTC block size, but multiplied with _sg_row_pitch() - it gives the correct surface pitch. - - See: https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt - */ - num_rows = ((_sg_max(height, 8) + 7) / 8) * 8; - break; - default: - num_rows = height; - break; - } - if (num_rows < 1) { - num_rows = 1; - } - return num_rows; -} - -/* return pitch of a 2D subimage / texture slice - see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp -*/ -_SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align) { - int num_rows = _sg_num_rows(fmt, height); - return num_rows * _sg_row_pitch(fmt, width, row_align); -} - -/* capability table pixel format helper functions */ -_SOKOL_PRIVATE void _sg_pixelformat_all(sg_pixelformat_info* pfi) { - pfi->sample = true; - pfi->filter = true; - pfi->blend = true; - pfi->render = true; - pfi->msaa = true; -} - -_SOKOL_PRIVATE void _sg_pixelformat_s(sg_pixelformat_info* pfi) { - pfi->sample = true; -} - -_SOKOL_PRIVATE void _sg_pixelformat_sf(sg_pixelformat_info* pfi) { - pfi->sample = true; - pfi->filter = true; -} - -_SOKOL_PRIVATE void _sg_pixelformat_sr(sg_pixelformat_info* pfi) { - pfi->sample = true; - pfi->render = true; -} - -_SOKOL_PRIVATE void _sg_pixelformat_srmd(sg_pixelformat_info* pfi) { - pfi->sample = true; - pfi->render = true; - pfi->msaa = true; - pfi->depth = true; -} - -_SOKOL_PRIVATE void _sg_pixelformat_srm(sg_pixelformat_info* pfi) { - pfi->sample = true; - pfi->render = true; - pfi->msaa = true; -} - -_SOKOL_PRIVATE void _sg_pixelformat_sfrm(sg_pixelformat_info* pfi) { - pfi->sample = true; - pfi->filter = true; - pfi->render = true; - pfi->msaa = true; -} -_SOKOL_PRIVATE void _sg_pixelformat_sbrm(sg_pixelformat_info* pfi) { - pfi->sample = true; - pfi->blend = true; - pfi->render = true; - pfi->msaa = true; -} - -_SOKOL_PRIVATE void _sg_pixelformat_sbr(sg_pixelformat_info* pfi) { - pfi->sample = true; - pfi->blend = true; - pfi->render = true; -} - -_SOKOL_PRIVATE void _sg_pixelformat_sfbr(sg_pixelformat_info* pfi) { - pfi->sample = true; - pfi->filter = true; - pfi->blend = true; - pfi->render = true; -} - -/* resolve pass action defaults into a new pass action struct */ -_SOKOL_PRIVATE void _sg_resolve_default_pass_action(const sg_pass_action* from, sg_pass_action* to) { - SOKOL_ASSERT(from && to); - *to = *from; - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (to->colors[i].action == _SG_ACTION_DEFAULT) { - to->colors[i].action = SG_ACTION_CLEAR; - to->colors[i].value.r = SG_DEFAULT_CLEAR_RED; - to->colors[i].value.g = SG_DEFAULT_CLEAR_GREEN; - to->colors[i].value.b = SG_DEFAULT_CLEAR_BLUE; - to->colors[i].value.a = SG_DEFAULT_CLEAR_ALPHA; - } - } - if (to->depth.action == _SG_ACTION_DEFAULT) { - to->depth.action = SG_ACTION_CLEAR; - to->depth.value = SG_DEFAULT_CLEAR_DEPTH; - } - if (to->stencil.action == _SG_ACTION_DEFAULT) { - to->stencil.action = SG_ACTION_CLEAR; - to->stencil.value = SG_DEFAULT_CLEAR_STENCIL; - } -} - -/*== DUMMY BACKEND IMPL ======================================================*/ -#if defined(SOKOL_DUMMY_BACKEND) - -_SOKOL_PRIVATE void _sg_dummy_setup_backend(const sg_desc* desc) { - SOKOL_ASSERT(desc); - _SOKOL_UNUSED(desc); - _sg.backend = SG_BACKEND_DUMMY; - for (int i = SG_PIXELFORMAT_R8; i < SG_PIXELFORMAT_BC1_RGBA; i++) { - _sg.formats[i].sample = true; - _sg.formats[i].filter = true; - _sg.formats[i].render = true; - _sg.formats[i].blend = true; - _sg.formats[i].msaa = true; - } - _sg.formats[SG_PIXELFORMAT_DEPTH].depth = true; - _sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL].depth = true; -} - -_SOKOL_PRIVATE void _sg_dummy_discard_backend(void) { - /* empty */ -} - -_SOKOL_PRIVATE void _sg_dummy_reset_state_cache(void) { - /* empty*/ -} - -_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_dummy_destroy_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); -} - -_SOKOL_PRIVATE void _sg_dummy_activate_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); -} - -_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { - SOKOL_ASSERT(buf && desc); - _sg_buffer_common_init(&buf->cmn, desc); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_dummy_destroy_buffer(_sg_buffer_t* buf) { - SOKOL_ASSERT(buf); - _SOKOL_UNUSED(buf); -} - -_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_image(_sg_image_t* img, const sg_image_desc* desc) { - SOKOL_ASSERT(img && desc); - _sg_image_common_init(&img->cmn, desc); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_dummy_destroy_image(_sg_image_t* img) { - SOKOL_ASSERT(img); - _SOKOL_UNUSED(img); -} - -_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { - SOKOL_ASSERT(shd && desc); - _sg_shader_common_init(&shd->cmn, desc); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_dummy_destroy_shader(_sg_shader_t* shd) { - SOKOL_ASSERT(shd); - _SOKOL_UNUSED(shd); -} - -_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { - SOKOL_ASSERT(pip && desc); - pip->shader = shd; - _sg_pipeline_common_init(&pip->cmn, desc); - for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { - break; - } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; - } - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_dummy_destroy_pipeline(_sg_pipeline_t* pip) { - SOKOL_ASSERT(pip); - _SOKOL_UNUSED(pip); -} - -_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { - SOKOL_ASSERT(pass && desc); - SOKOL_ASSERT(att_images && att_images[0]); - - _sg_pass_common_init(&pass->cmn, desc); - - const sg_pass_attachment_desc* att_desc; - for (int i = 0; i < pass->cmn.num_color_atts; i++) { - att_desc = &desc->color_attachments[i]; - SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); - SOKOL_ASSERT(0 == pass->dmy.color_atts[i].image); - SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->cmn.pixel_format)); - pass->dmy.color_atts[i].image = att_images[i]; - } - - SOKOL_ASSERT(0 == pass->dmy.ds_att.image); - att_desc = &desc->depth_stencil_attachment; - if (att_desc->image.id != SG_INVALID_ID) { - const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; - SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); - pass->dmy.ds_att.image = att_images[ds_img_index]; - } - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_dummy_destroy_pass(_sg_pass_t* pass) { - SOKOL_ASSERT(pass); - _SOKOL_UNUSED(pass); -} - -_SOKOL_PRIVATE _sg_image_t* _sg_dummy_pass_color_image(const _sg_pass_t* pass, int index) { - SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); - /* NOTE: may return null */ - return pass->dmy.color_atts[index].image; -} - -_SOKOL_PRIVATE _sg_image_t* _sg_dummy_pass_ds_image(const _sg_pass_t* pass) { - /* NOTE: may return null */ - SOKOL_ASSERT(pass); - return pass->dmy.ds_att.image; -} - -_SOKOL_PRIVATE void _sg_dummy_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { - SOKOL_ASSERT(action); - _SOKOL_UNUSED(pass); - _SOKOL_UNUSED(action); - _SOKOL_UNUSED(w); - _SOKOL_UNUSED(h); -} - -_SOKOL_PRIVATE void _sg_dummy_end_pass(void) { - /* empty */ -} - -_SOKOL_PRIVATE void _sg_dummy_commit(void) { - /* empty */ -} - -_SOKOL_PRIVATE void _sg_dummy_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { - _SOKOL_UNUSED(x); - _SOKOL_UNUSED(y); - _SOKOL_UNUSED(w); - _SOKOL_UNUSED(h); - _SOKOL_UNUSED(origin_top_left); -} - -_SOKOL_PRIVATE void _sg_dummy_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { - _SOKOL_UNUSED(x); - _SOKOL_UNUSED(y); - _SOKOL_UNUSED(w); - _SOKOL_UNUSED(h); - _SOKOL_UNUSED(origin_top_left); -} - -_SOKOL_PRIVATE void _sg_dummy_apply_pipeline(_sg_pipeline_t* pip) { - SOKOL_ASSERT(pip); - _SOKOL_UNUSED(pip); -} - -_SOKOL_PRIVATE void _sg_dummy_apply_bindings( - _sg_pipeline_t* pip, - _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, - _sg_buffer_t* ib, int ib_offset, - _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs) -{ - SOKOL_ASSERT(pip); - SOKOL_ASSERT(vbs && vb_offsets); - SOKOL_ASSERT(vs_imgs); - SOKOL_ASSERT(fs_imgs); - _SOKOL_UNUSED(pip); - _SOKOL_UNUSED(vbs); _SOKOL_UNUSED(vb_offsets); _SOKOL_UNUSED(num_vbs); - _SOKOL_UNUSED(ib); _SOKOL_UNUSED(ib_offset); - _SOKOL_UNUSED(vs_imgs); _SOKOL_UNUSED(num_vs_imgs); - _SOKOL_UNUSED(fs_imgs); _SOKOL_UNUSED(num_fs_imgs); -} - -_SOKOL_PRIVATE void _sg_dummy_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { - _SOKOL_UNUSED(stage_index); - _SOKOL_UNUSED(ub_index); - _SOKOL_UNUSED(data); -} - -_SOKOL_PRIVATE void _sg_dummy_draw(int base_element, int num_elements, int num_instances) { - _SOKOL_UNUSED(base_element); - _SOKOL_UNUSED(num_elements); - _SOKOL_UNUSED(num_instances); -} - -_SOKOL_PRIVATE void _sg_dummy_update_buffer(_sg_buffer_t* buf, const sg_range* data) { - SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); - _SOKOL_UNUSED(data); - if (++buf->cmn.active_slot >= buf->cmn.num_slots) { - buf->cmn.active_slot = 0; - } -} - -_SOKOL_PRIVATE int _sg_dummy_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { - SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); - _SOKOL_UNUSED(data); - if (new_frame) { - if (++buf->cmn.active_slot >= buf->cmn.num_slots) { - buf->cmn.active_slot = 0; - } - } - /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */ - return _sg_roundup((int)data->size, 4); -} - -_SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data* data) { - SOKOL_ASSERT(img && data); - _SOKOL_UNUSED(data); - if (++img->cmn.active_slot >= img->cmn.num_slots) { - img->cmn.active_slot = 0; - } -} - -/*== GL BACKEND ==============================================================*/ -#elif defined(_SOKOL_ANY_GL) - -/*=== OPTIONAL GL LOADER FOR WIN32 ===========================================*/ -#if defined(_SOKOL_USE_WIN32_GL_LOADER) - -// X Macro list of GL function names and signatures -#define _SG_GL_FUNCS \ - _SG_XMACRO(glBindVertexArray, void, (GLuint array)) \ - _SG_XMACRO(glFramebufferTextureLayer, void, (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer)) \ - _SG_XMACRO(glGenFramebuffers, void, (GLsizei n, GLuint * framebuffers)) \ - _SG_XMACRO(glBindFramebuffer, void, (GLenum target, GLuint framebuffer)) \ - _SG_XMACRO(glBindRenderbuffer, void, (GLenum target, GLuint renderbuffer)) \ - _SG_XMACRO(glGetStringi, const GLubyte *, (GLenum name, GLuint index)) \ - _SG_XMACRO(glClearBufferfi, void, (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)) \ - _SG_XMACRO(glClearBufferfv, void, (GLenum buffer, GLint drawbuffer, const GLfloat * value)) \ - _SG_XMACRO(glClearBufferuiv, void, (GLenum buffer, GLint drawbuffer, const GLuint * value)) \ - _SG_XMACRO(glClearBufferiv, void, (GLenum buffer, GLint drawbuffer, const GLint * value)) \ - _SG_XMACRO(glDeleteRenderbuffers, void, (GLsizei n, const GLuint * renderbuffers)) \ - _SG_XMACRO(glUniform1fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ - _SG_XMACRO(glUniform2fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ - _SG_XMACRO(glUniform3fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ - _SG_XMACRO(glUniform4fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ - _SG_XMACRO(glUniform1iv, void, (GLint location, GLsizei count, const GLint * value)) \ - _SG_XMACRO(glUniform2iv, void, (GLint location, GLsizei count, const GLint * value)) \ - _SG_XMACRO(glUniform3iv, void, (GLint location, GLsizei count, const GLint * value)) \ - _SG_XMACRO(glUniform4iv, void, (GLint location, GLsizei count, const GLint * value)) \ - _SG_XMACRO(glUniformMatrix4fv, void, (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)) \ - _SG_XMACRO(glUseProgram, void, (GLuint program)) \ - _SG_XMACRO(glShaderSource, void, (GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length)) \ - _SG_XMACRO(glLinkProgram, void, (GLuint program)) \ - _SG_XMACRO(glGetUniformLocation, GLint, (GLuint program, const GLchar * name)) \ - _SG_XMACRO(glGetShaderiv, void, (GLuint shader, GLenum pname, GLint * params)) \ - _SG_XMACRO(glGetProgramInfoLog, void, (GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ - _SG_XMACRO(glGetAttribLocation, GLint, (GLuint program, const GLchar * name)) \ - _SG_XMACRO(glDisableVertexAttribArray, void, (GLuint index)) \ - _SG_XMACRO(glDeleteShader, void, (GLuint shader)) \ - _SG_XMACRO(glDeleteProgram, void, (GLuint program)) \ - _SG_XMACRO(glCompileShader, void, (GLuint shader)) \ - _SG_XMACRO(glStencilFuncSeparate, void, (GLenum face, GLenum func, GLint ref, GLuint mask)) \ - _SG_XMACRO(glStencilOpSeparate, void, (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)) \ - _SG_XMACRO(glRenderbufferStorageMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)) \ - _SG_XMACRO(glDrawBuffers, void, (GLsizei n, const GLenum * bufs)) \ - _SG_XMACRO(glVertexAttribDivisor, void, (GLuint index, GLuint divisor)) \ - _SG_XMACRO(glBufferSubData, void, (GLenum target, GLintptr offset, GLsizeiptr size, const void * data)) \ - _SG_XMACRO(glGenBuffers, void, (GLsizei n, GLuint * buffers)) \ - _SG_XMACRO(glCheckFramebufferStatus, GLenum, (GLenum target)) \ - _SG_XMACRO(glFramebufferRenderbuffer, void, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)) \ - _SG_XMACRO(glCompressedTexImage2D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data)) \ - _SG_XMACRO(glCompressedTexImage3D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data)) \ - _SG_XMACRO(glActiveTexture, void, (GLenum texture)) \ - _SG_XMACRO(glTexSubImage3D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels)) \ - _SG_XMACRO(glRenderbufferStorage, void, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height)) \ - _SG_XMACRO(glGenTextures, void, (GLsizei n, GLuint * textures)) \ - _SG_XMACRO(glPolygonOffset, void, (GLfloat factor, GLfloat units)) \ - _SG_XMACRO(glDrawElements, void, (GLenum mode, GLsizei count, GLenum type, const void * indices)) \ - _SG_XMACRO(glDeleteFramebuffers, void, (GLsizei n, const GLuint * framebuffers)) \ - _SG_XMACRO(glBlendEquationSeparate, void, (GLenum modeRGB, GLenum modeAlpha)) \ - _SG_XMACRO(glDeleteTextures, void, (GLsizei n, const GLuint * textures)) \ - _SG_XMACRO(glGetProgramiv, void, (GLuint program, GLenum pname, GLint * params)) \ - _SG_XMACRO(glBindTexture, void, (GLenum target, GLuint texture)) \ - _SG_XMACRO(glTexImage3D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels)) \ - _SG_XMACRO(glCreateShader, GLuint, (GLenum type)) \ - _SG_XMACRO(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels)) \ - _SG_XMACRO(glClearDepth, void, (GLdouble depth)) \ - _SG_XMACRO(glFramebufferTexture2D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) \ - _SG_XMACRO(glCreateProgram, GLuint, (void)) \ - _SG_XMACRO(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ - _SG_XMACRO(glDeleteBuffers, void, (GLsizei n, const GLuint * buffers)) \ - _SG_XMACRO(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count)) \ - _SG_XMACRO(glDrawElementsInstanced, void, (GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount)) \ - _SG_XMACRO(glVertexAttribPointer, void, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer)) \ - _SG_XMACRO(glUniform1i, void, (GLint location, GLint v0)) \ - _SG_XMACRO(glDisable, void, (GLenum cap)) \ - _SG_XMACRO(glColorMask, void, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \ - _SG_XMACRO(glColorMaski, void, (GLuint buf, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \ - _SG_XMACRO(glBindBuffer, void, (GLenum target, GLuint buffer)) \ - _SG_XMACRO(glDeleteVertexArrays, void, (GLsizei n, const GLuint * arrays)) \ - _SG_XMACRO(glDepthMask, void, (GLboolean flag)) \ - _SG_XMACRO(glDrawArraysInstanced, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount)) \ - _SG_XMACRO(glClearStencil, void, (GLint s)) \ - _SG_XMACRO(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ - _SG_XMACRO(glGenRenderbuffers, void, (GLsizei n, GLuint * renderbuffers)) \ - _SG_XMACRO(glBufferData, void, (GLenum target, GLsizeiptr size, const void * data, GLenum usage)) \ - _SG_XMACRO(glBlendFuncSeparate, void, (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)) \ - _SG_XMACRO(glTexParameteri, void, (GLenum target, GLenum pname, GLint param)) \ - _SG_XMACRO(glGetIntegerv, void, (GLenum pname, GLint * data)) \ - _SG_XMACRO(glEnable, void, (GLenum cap)) \ - _SG_XMACRO(glBlitFramebuffer, void, (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)) \ - _SG_XMACRO(glStencilMask, void, (GLuint mask)) \ - _SG_XMACRO(glAttachShader, void, (GLuint program, GLuint shader)) \ - _SG_XMACRO(glGetError, GLenum, (void)) \ - _SG_XMACRO(glClearColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ - _SG_XMACRO(glBlendColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ - _SG_XMACRO(glTexParameterf, void, (GLenum target, GLenum pname, GLfloat param)) \ - _SG_XMACRO(glTexParameterfv, void, (GLenum target, GLenum pname, GLfloat* params)) \ - _SG_XMACRO(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ - _SG_XMACRO(glDepthFunc, void, (GLenum func)) \ - _SG_XMACRO(glStencilOp , void, (GLenum fail, GLenum zfail, GLenum zpass)) \ - _SG_XMACRO(glStencilFunc, void, (GLenum func, GLint ref, GLuint mask)) \ - _SG_XMACRO(glEnableVertexAttribArray, void, (GLuint index)) \ - _SG_XMACRO(glBlendFunc, void, (GLenum sfactor, GLenum dfactor)) \ - _SG_XMACRO(glReadBuffer, void, (GLenum src)) \ - _SG_XMACRO(glReadPixels, void, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * data)) \ - _SG_XMACRO(glClear, void, (GLbitfield mask)) \ - _SG_XMACRO(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels)) \ - _SG_XMACRO(glGenVertexArrays, void, (GLsizei n, GLuint * arrays)) \ - _SG_XMACRO(glFrontFace, void, (GLenum mode)) \ - _SG_XMACRO(glCullFace, void, (GLenum mode)) - -// generate GL function pointer typedefs -#define _SG_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args; -_SG_GL_FUNCS -#undef _SG_XMACRO - -// generate GL function pointers -#define _SG_XMACRO(name, ret, args) static PFN_ ## name name; -_SG_GL_FUNCS -#undef _SG_XMACRO - -// helper function to lookup GL functions in GL DLL -typedef PROC (WINAPI * _sg_wglGetProcAddress)(LPCSTR); -_SOKOL_PRIVATE void* _sg_gl_getprocaddr(const char* name, _sg_wglGetProcAddress wgl_getprocaddress) { - void* proc_addr = (void*) wgl_getprocaddress(name); - if (0 == proc_addr) { - proc_addr = (void*) GetProcAddress(_sg.gl.opengl32_dll, name); - } - SOKOL_ASSERT(proc_addr); - return proc_addr; -} - -// populate GL function pointers -_SOKOL_PRIVATE void _sg_gl_load_opengl(void) { - SOKOL_ASSERT(0 == _sg.gl.opengl32_dll); - _sg.gl.opengl32_dll = LoadLibraryA("opengl32.dll"); - SOKOL_ASSERT(_sg.gl.opengl32_dll); - _sg_wglGetProcAddress wgl_getprocaddress = (_sg_wglGetProcAddress) GetProcAddress(_sg.gl.opengl32_dll, "wglGetProcAddress"); - SOKOL_ASSERT(wgl_getprocaddress); - #define _SG_XMACRO(name, ret, args) name = (PFN_ ## name) _sg_gl_getprocaddr(#name, wgl_getprocaddress); - _SG_GL_FUNCS - #undef _SG_XMACRO -} - -_SOKOL_PRIVATE void _sg_gl_unload_opengl(void) { - SOKOL_ASSERT(_sg.gl.opengl32_dll); - FreeLibrary(_sg.gl.opengl32_dll); - _sg.gl.opengl32_dll = 0; -} -#endif // _SOKOL_USE_WIN32_GL_LOADER - -/*-- type translation --------------------------------------------------------*/ -_SOKOL_PRIVATE GLenum _sg_gl_buffer_target(sg_buffer_type t) { - switch (t) { - case SG_BUFFERTYPE_VERTEXBUFFER: return GL_ARRAY_BUFFER; - case SG_BUFFERTYPE_INDEXBUFFER: return GL_ELEMENT_ARRAY_BUFFER; - default: SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_texture_target(sg_image_type t) { - switch (t) { - case SG_IMAGETYPE_2D: return GL_TEXTURE_2D; - case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP; - #if !defined(SOKOL_GLES2) - case SG_IMAGETYPE_3D: return GL_TEXTURE_3D; - case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY; - #endif - default: SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_usage(sg_usage u) { - switch (u) { - case SG_USAGE_IMMUTABLE: return GL_STATIC_DRAW; - case SG_USAGE_DYNAMIC: return GL_DYNAMIC_DRAW; - case SG_USAGE_STREAM: return GL_STREAM_DRAW; - default: SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_shader_stage(sg_shader_stage stage) { - switch (stage) { - case SG_SHADERSTAGE_VS: return GL_VERTEX_SHADER; - case SG_SHADERSTAGE_FS: return GL_FRAGMENT_SHADER; - default: SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLint _sg_gl_vertexformat_size(sg_vertex_format fmt) { - switch (fmt) { - case SG_VERTEXFORMAT_FLOAT: return 1; - case SG_VERTEXFORMAT_FLOAT2: return 2; - case SG_VERTEXFORMAT_FLOAT3: return 3; - case SG_VERTEXFORMAT_FLOAT4: return 4; - case SG_VERTEXFORMAT_BYTE4: return 4; - case SG_VERTEXFORMAT_BYTE4N: return 4; - case SG_VERTEXFORMAT_UBYTE4: return 4; - case SG_VERTEXFORMAT_UBYTE4N: return 4; - case SG_VERTEXFORMAT_SHORT2: return 2; - case SG_VERTEXFORMAT_SHORT2N: return 2; - case SG_VERTEXFORMAT_USHORT2N: return 2; - case SG_VERTEXFORMAT_SHORT4: return 4; - case SG_VERTEXFORMAT_SHORT4N: return 4; - case SG_VERTEXFORMAT_USHORT4N: return 4; - case SG_VERTEXFORMAT_UINT10_N2: return 4; - default: SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_vertexformat_type(sg_vertex_format fmt) { - switch (fmt) { - case SG_VERTEXFORMAT_FLOAT: - case SG_VERTEXFORMAT_FLOAT2: - case SG_VERTEXFORMAT_FLOAT3: - case SG_VERTEXFORMAT_FLOAT4: - return GL_FLOAT; - case SG_VERTEXFORMAT_BYTE4: - case SG_VERTEXFORMAT_BYTE4N: - return GL_BYTE; - case SG_VERTEXFORMAT_UBYTE4: - case SG_VERTEXFORMAT_UBYTE4N: - return GL_UNSIGNED_BYTE; - case SG_VERTEXFORMAT_SHORT2: - case SG_VERTEXFORMAT_SHORT2N: - case SG_VERTEXFORMAT_SHORT4: - case SG_VERTEXFORMAT_SHORT4N: - return GL_SHORT; - case SG_VERTEXFORMAT_USHORT2N: - case SG_VERTEXFORMAT_USHORT4N: - return GL_UNSIGNED_SHORT; - case SG_VERTEXFORMAT_UINT10_N2: - return GL_UNSIGNED_INT_2_10_10_10_REV; - default: - SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLboolean _sg_gl_vertexformat_normalized(sg_vertex_format fmt) { - switch (fmt) { - case SG_VERTEXFORMAT_BYTE4N: - case SG_VERTEXFORMAT_UBYTE4N: - case SG_VERTEXFORMAT_SHORT2N: - case SG_VERTEXFORMAT_USHORT2N: - case SG_VERTEXFORMAT_SHORT4N: - case SG_VERTEXFORMAT_USHORT4N: - case SG_VERTEXFORMAT_UINT10_N2: - return GL_TRUE; - default: - return GL_FALSE; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_primitive_type(sg_primitive_type t) { - switch (t) { - case SG_PRIMITIVETYPE_POINTS: return GL_POINTS; - case SG_PRIMITIVETYPE_LINES: return GL_LINES; - case SG_PRIMITIVETYPE_LINE_STRIP: return GL_LINE_STRIP; - case SG_PRIMITIVETYPE_TRIANGLES: return GL_TRIANGLES; - case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; - default: SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_index_type(sg_index_type t) { - switch (t) { - case SG_INDEXTYPE_NONE: return 0; - case SG_INDEXTYPE_UINT16: return GL_UNSIGNED_SHORT; - case SG_INDEXTYPE_UINT32: return GL_UNSIGNED_INT; - default: SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_compare_func(sg_compare_func cmp) { - switch (cmp) { - case SG_COMPAREFUNC_NEVER: return GL_NEVER; - case SG_COMPAREFUNC_LESS: return GL_LESS; - case SG_COMPAREFUNC_EQUAL: return GL_EQUAL; - case SG_COMPAREFUNC_LESS_EQUAL: return GL_LEQUAL; - case SG_COMPAREFUNC_GREATER: return GL_GREATER; - case SG_COMPAREFUNC_NOT_EQUAL: return GL_NOTEQUAL; - case SG_COMPAREFUNC_GREATER_EQUAL: return GL_GEQUAL; - case SG_COMPAREFUNC_ALWAYS: return GL_ALWAYS; - default: SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_stencil_op(sg_stencil_op op) { - switch (op) { - case SG_STENCILOP_KEEP: return GL_KEEP; - case SG_STENCILOP_ZERO: return GL_ZERO; - case SG_STENCILOP_REPLACE: return GL_REPLACE; - case SG_STENCILOP_INCR_CLAMP: return GL_INCR; - case SG_STENCILOP_DECR_CLAMP: return GL_DECR; - case SG_STENCILOP_INVERT: return GL_INVERT; - case SG_STENCILOP_INCR_WRAP: return GL_INCR_WRAP; - case SG_STENCILOP_DECR_WRAP: return GL_DECR_WRAP; - default: SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_blend_factor(sg_blend_factor f) { - switch (f) { - case SG_BLENDFACTOR_ZERO: return GL_ZERO; - case SG_BLENDFACTOR_ONE: return GL_ONE; - case SG_BLENDFACTOR_SRC_COLOR: return GL_SRC_COLOR; - case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR; - case SG_BLENDFACTOR_SRC_ALPHA: return GL_SRC_ALPHA; - case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA; - case SG_BLENDFACTOR_DST_COLOR: return GL_DST_COLOR; - case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return GL_ONE_MINUS_DST_COLOR; - case SG_BLENDFACTOR_DST_ALPHA: return GL_DST_ALPHA; - case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA; - case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return GL_SRC_ALPHA_SATURATE; - case SG_BLENDFACTOR_BLEND_COLOR: return GL_CONSTANT_COLOR; - case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return GL_ONE_MINUS_CONSTANT_COLOR; - case SG_BLENDFACTOR_BLEND_ALPHA: return GL_CONSTANT_ALPHA; - case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return GL_ONE_MINUS_CONSTANT_ALPHA; - default: SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_blend_op(sg_blend_op op) { - switch (op) { - case SG_BLENDOP_ADD: return GL_FUNC_ADD; - case SG_BLENDOP_SUBTRACT: return GL_FUNC_SUBTRACT; - case SG_BLENDOP_REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT; - default: SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_filter(sg_filter f) { - switch (f) { - case SG_FILTER_NEAREST: return GL_NEAREST; - case SG_FILTER_LINEAR: return GL_LINEAR; - case SG_FILTER_NEAREST_MIPMAP_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; - case SG_FILTER_NEAREST_MIPMAP_LINEAR: return GL_NEAREST_MIPMAP_LINEAR; - case SG_FILTER_LINEAR_MIPMAP_NEAREST: return GL_LINEAR_MIPMAP_NEAREST; - case SG_FILTER_LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; - default: SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_wrap(sg_wrap w) { - switch (w) { - case SG_WRAP_CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE; - #if defined(SOKOL_GLCORE33) - case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER; - #else - case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_EDGE; - #endif - case SG_WRAP_REPEAT: return GL_REPEAT; - case SG_WRAP_MIRRORED_REPEAT: return GL_MIRRORED_REPEAT; - default: SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_teximage_type(sg_pixel_format fmt) { - switch (fmt) { - case SG_PIXELFORMAT_R8: - case SG_PIXELFORMAT_R8UI: - case SG_PIXELFORMAT_RG8: - case SG_PIXELFORMAT_RG8UI: - case SG_PIXELFORMAT_RGBA8: - case SG_PIXELFORMAT_RGBA8UI: - case SG_PIXELFORMAT_BGRA8: - return GL_UNSIGNED_BYTE; - case SG_PIXELFORMAT_R8SN: - case SG_PIXELFORMAT_R8SI: - case SG_PIXELFORMAT_RG8SN: - case SG_PIXELFORMAT_RG8SI: - case SG_PIXELFORMAT_RGBA8SN: - case SG_PIXELFORMAT_RGBA8SI: - return GL_BYTE; - case SG_PIXELFORMAT_R16: - case SG_PIXELFORMAT_R16UI: - case SG_PIXELFORMAT_RG16: - case SG_PIXELFORMAT_RG16UI: - case SG_PIXELFORMAT_RGBA16: - case SG_PIXELFORMAT_RGBA16UI: - return GL_UNSIGNED_SHORT; - case SG_PIXELFORMAT_R16SN: - case SG_PIXELFORMAT_R16SI: - case SG_PIXELFORMAT_RG16SN: - case SG_PIXELFORMAT_RG16SI: - case SG_PIXELFORMAT_RGBA16SN: - case SG_PIXELFORMAT_RGBA16SI: - return GL_SHORT; - case SG_PIXELFORMAT_R16F: - case SG_PIXELFORMAT_RG16F: - case SG_PIXELFORMAT_RGBA16F: - return GL_HALF_FLOAT; - case SG_PIXELFORMAT_R32UI: - case SG_PIXELFORMAT_RG32UI: - case SG_PIXELFORMAT_RGBA32UI: - return GL_UNSIGNED_INT; - case SG_PIXELFORMAT_R32SI: - case SG_PIXELFORMAT_RG32SI: - case SG_PIXELFORMAT_RGBA32SI: - return GL_INT; - case SG_PIXELFORMAT_R32F: - case SG_PIXELFORMAT_RG32F: - case SG_PIXELFORMAT_RGBA32F: - return GL_FLOAT; - #if !defined(SOKOL_GLES2) - case SG_PIXELFORMAT_RGB10A2: - return GL_UNSIGNED_INT_2_10_10_10_REV; - case SG_PIXELFORMAT_RG11B10F: - return GL_UNSIGNED_INT_10F_11F_11F_REV; - #endif - case SG_PIXELFORMAT_DEPTH: - return GL_UNSIGNED_SHORT; - case SG_PIXELFORMAT_DEPTH_STENCIL: - return GL_UNSIGNED_INT_24_8; - default: - SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { - switch (fmt) { - case SG_PIXELFORMAT_R8: - case SG_PIXELFORMAT_R8SN: - case SG_PIXELFORMAT_R16: - case SG_PIXELFORMAT_R16SN: - case SG_PIXELFORMAT_R16F: - case SG_PIXELFORMAT_R32F: - #if defined(SOKOL_GLES2) - return GL_LUMINANCE; - #else - if (_sg.gl.gles2) { - return GL_LUMINANCE; - } - else { - return GL_RED; - } - #endif - #if !defined(SOKOL_GLES2) - case SG_PIXELFORMAT_R8UI: - case SG_PIXELFORMAT_R8SI: - case SG_PIXELFORMAT_R16UI: - case SG_PIXELFORMAT_R16SI: - case SG_PIXELFORMAT_R32UI: - case SG_PIXELFORMAT_R32SI: - return GL_RED_INTEGER; - case SG_PIXELFORMAT_RG8: - case SG_PIXELFORMAT_RG8SN: - case SG_PIXELFORMAT_RG16: - case SG_PIXELFORMAT_RG16SN: - case SG_PIXELFORMAT_RG16F: - case SG_PIXELFORMAT_RG32F: - return GL_RG; - case SG_PIXELFORMAT_RG8UI: - case SG_PIXELFORMAT_RG8SI: - case SG_PIXELFORMAT_RG16UI: - case SG_PIXELFORMAT_RG16SI: - case SG_PIXELFORMAT_RG32UI: - case SG_PIXELFORMAT_RG32SI: - return GL_RG_INTEGER; - #endif - case SG_PIXELFORMAT_RGBA8: - case SG_PIXELFORMAT_RGBA8SN: - case SG_PIXELFORMAT_RGBA16: - case SG_PIXELFORMAT_RGBA16SN: - case SG_PIXELFORMAT_RGBA16F: - case SG_PIXELFORMAT_RGBA32F: - case SG_PIXELFORMAT_RGB10A2: - return GL_RGBA; - #if !defined(SOKOL_GLES2) - case SG_PIXELFORMAT_RGBA8UI: - case SG_PIXELFORMAT_RGBA8SI: - case SG_PIXELFORMAT_RGBA16UI: - case SG_PIXELFORMAT_RGBA16SI: - case SG_PIXELFORMAT_RGBA32UI: - case SG_PIXELFORMAT_RGBA32SI: - return GL_RGBA_INTEGER; - #endif - case SG_PIXELFORMAT_RG11B10F: - return GL_RGB; - case SG_PIXELFORMAT_DEPTH: - return GL_DEPTH_COMPONENT; - case SG_PIXELFORMAT_DEPTH_STENCIL: - return GL_DEPTH_STENCIL; - case SG_PIXELFORMAT_BC1_RGBA: - return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - case SG_PIXELFORMAT_BC2_RGBA: - return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - case SG_PIXELFORMAT_BC3_RGBA: - return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - case SG_PIXELFORMAT_BC4_R: - return GL_COMPRESSED_RED_RGTC1; - case SG_PIXELFORMAT_BC4_RSN: - return GL_COMPRESSED_SIGNED_RED_RGTC1; - case SG_PIXELFORMAT_BC5_RG: - return GL_COMPRESSED_RED_GREEN_RGTC2; - case SG_PIXELFORMAT_BC5_RGSN: - return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; - case SG_PIXELFORMAT_BC6H_RGBF: - return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; - case SG_PIXELFORMAT_BC6H_RGBUF: - return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; - case SG_PIXELFORMAT_BC7_RGBA: - return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; - case SG_PIXELFORMAT_PVRTC_RGB_2BPP: - return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; - case SG_PIXELFORMAT_PVRTC_RGB_4BPP: - return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; - case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: - return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; - case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: - return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; - case SG_PIXELFORMAT_ETC2_RGB8: - return GL_COMPRESSED_RGB8_ETC2; - case SG_PIXELFORMAT_ETC2_RGB8A1: - return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; - case SG_PIXELFORMAT_ETC2_RGBA8: - return GL_COMPRESSED_RGBA8_ETC2_EAC; - case SG_PIXELFORMAT_ETC2_RG11: - return GL_COMPRESSED_RG11_EAC; - case SG_PIXELFORMAT_ETC2_RG11SN: - return GL_COMPRESSED_SIGNED_RG11_EAC; - default: - SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { - #if defined(SOKOL_GLES2) - return _sg_gl_teximage_format(fmt); - #else - if (_sg.gl.gles2) { - return _sg_gl_teximage_format(fmt); - } - else { - switch (fmt) { - case SG_PIXELFORMAT_R8: return GL_R8; - case SG_PIXELFORMAT_R8SN: return GL_R8_SNORM; - case SG_PIXELFORMAT_R8UI: return GL_R8UI; - case SG_PIXELFORMAT_R8SI: return GL_R8I; - #if !defined(SOKOL_GLES3) - case SG_PIXELFORMAT_R16: return GL_R16; - case SG_PIXELFORMAT_R16SN: return GL_R16_SNORM; - #endif - case SG_PIXELFORMAT_R16UI: return GL_R16UI; - case SG_PIXELFORMAT_R16SI: return GL_R16I; - case SG_PIXELFORMAT_R16F: return GL_R16F; - case SG_PIXELFORMAT_RG8: return GL_RG8; - case SG_PIXELFORMAT_RG8SN: return GL_RG8_SNORM; - case SG_PIXELFORMAT_RG8UI: return GL_RG8UI; - case SG_PIXELFORMAT_RG8SI: return GL_RG8I; - case SG_PIXELFORMAT_R32UI: return GL_R32UI; - case SG_PIXELFORMAT_R32SI: return GL_R32I; - case SG_PIXELFORMAT_R32F: return GL_R32F; - #if !defined(SOKOL_GLES3) - case SG_PIXELFORMAT_RG16: return GL_RG16; - case SG_PIXELFORMAT_RG16SN: return GL_RG16_SNORM; - #endif - case SG_PIXELFORMAT_RG16UI: return GL_RG16UI; - case SG_PIXELFORMAT_RG16SI: return GL_RG16I; - case SG_PIXELFORMAT_RG16F: return GL_RG16F; - case SG_PIXELFORMAT_RGBA8: return GL_RGBA8; - case SG_PIXELFORMAT_RGBA8SN: return GL_RGBA8_SNORM; - case SG_PIXELFORMAT_RGBA8UI: return GL_RGBA8UI; - case SG_PIXELFORMAT_RGBA8SI: return GL_RGBA8I; - case SG_PIXELFORMAT_RGB10A2: return GL_RGB10_A2; - case SG_PIXELFORMAT_RG11B10F: return GL_R11F_G11F_B10F; - case SG_PIXELFORMAT_RG32UI: return GL_RG32UI; - case SG_PIXELFORMAT_RG32SI: return GL_RG32I; - case SG_PIXELFORMAT_RG32F: return GL_RG32F; - #if !defined(SOKOL_GLES3) - case SG_PIXELFORMAT_RGBA16: return GL_RGBA16; - case SG_PIXELFORMAT_RGBA16SN: return GL_RGBA16_SNORM; - #endif - case SG_PIXELFORMAT_RGBA16UI: return GL_RGBA16UI; - case SG_PIXELFORMAT_RGBA16SI: return GL_RGBA16I; - case SG_PIXELFORMAT_RGBA16F: return GL_RGBA16F; - case SG_PIXELFORMAT_RGBA32UI: return GL_RGBA32UI; - case SG_PIXELFORMAT_RGBA32SI: return GL_RGBA32I; - case SG_PIXELFORMAT_RGBA32F: return GL_RGBA32F; - case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT16; - case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH24_STENCIL8; - case SG_PIXELFORMAT_BC1_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - case SG_PIXELFORMAT_BC2_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - case SG_PIXELFORMAT_BC3_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - case SG_PIXELFORMAT_BC4_R: return GL_COMPRESSED_RED_RGTC1; - case SG_PIXELFORMAT_BC4_RSN: return GL_COMPRESSED_SIGNED_RED_RGTC1; - case SG_PIXELFORMAT_BC5_RG: return GL_COMPRESSED_RED_GREEN_RGTC2; - case SG_PIXELFORMAT_BC5_RGSN: return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; - case SG_PIXELFORMAT_BC6H_RGBF: return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; - case SG_PIXELFORMAT_BC6H_RGBUF: return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; - case SG_PIXELFORMAT_BC7_RGBA: return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; - case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; - case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; - case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; - case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; - case SG_PIXELFORMAT_ETC2_RGB8: return GL_COMPRESSED_RGB8_ETC2; - case SG_PIXELFORMAT_ETC2_RGB8A1: return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; - case SG_PIXELFORMAT_ETC2_RGBA8: return GL_COMPRESSED_RGBA8_ETC2_EAC; - case SG_PIXELFORMAT_ETC2_RG11: return GL_COMPRESSED_RG11_EAC; - case SG_PIXELFORMAT_ETC2_RG11SN: return GL_COMPRESSED_SIGNED_RG11_EAC; - default: SOKOL_UNREACHABLE; return 0; - } - } - #endif -} - -_SOKOL_PRIVATE GLenum _sg_gl_cubeface_target(int face_index) { - switch (face_index) { - case 0: return GL_TEXTURE_CUBE_MAP_POSITIVE_X; - case 1: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; - case 2: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; - case 3: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; - case 4: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; - case 5: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; - default: SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE GLenum _sg_gl_depth_attachment_format(sg_pixel_format fmt) { - switch (fmt) { - case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT16; - case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH24_STENCIL8; - default: SOKOL_UNREACHABLE; return 0; - } -} - -/* see: https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml */ -_SOKOL_PRIVATE void _sg_gl_init_pixelformats(bool has_bgra) { - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); - } - else { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8]); - } - #else - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8]); - #endif - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); - #if !defined(SOKOL_GLES3) - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); - #endif - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); - #if !defined(SOKOL_GLES3) - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); - #endif - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); - } - #endif - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); - } - #endif - if (has_bgra) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); - } - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); - #if !defined(SOKOL_GLES3) - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); - #endif - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); - } - #endif - // FIXME: WEBGL_depth_texture extension? - _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); - _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); -} - -/* FIXME: OES_half_float_blend */ -_SOKOL_PRIVATE void _sg_gl_init_pixelformats_half_float(bool has_colorbuffer_half_float, bool has_texture_half_float_linear) { - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - if (has_texture_half_float_linear) { - if (has_colorbuffer_half_float) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - else { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG16F]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - } - else { - if (has_colorbuffer_half_float) { - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R16F]); - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG16F]); - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - else { - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R16F]); - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG16F]); - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - } - } - else { - #endif - /* GLES2 can only render to RGBA, and there's no RG format */ - if (has_texture_half_float_linear) { - if (has_colorbuffer_half_float) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - else { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]); - } - else { - if (has_colorbuffer_half_float) { - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - else { - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R16F]); - } - #if !defined(SOKOL_GLES2) - } - #endif -} - -_SOKOL_PRIVATE void _sg_gl_init_pixelformats_float(bool has_colorbuffer_float, bool has_texture_float_linear, bool has_float_blend) { - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - if (has_texture_float_linear) { - if (has_colorbuffer_float) { - if (has_float_blend) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - else { - _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - } - else { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - } - else { - if (has_colorbuffer_float) { - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - else { - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - } - } - else { - #endif - /* GLES2 can only render to RGBA, and there's no RG format */ - if (has_texture_float_linear) { - if (has_colorbuffer_float) { - if (has_float_blend) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - else { - _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - } - else { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R32F]); - } - else { - if (has_colorbuffer_float) { - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - else { - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R32F]); - } - #if !defined(SOKOL_GLES2) - } - #endif -} - -_SOKOL_PRIVATE void _sg_gl_init_pixelformats_s3tc(void) { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); -} - -_SOKOL_PRIVATE void _sg_gl_init_pixelformats_rgtc(void) { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); -} - -_SOKOL_PRIVATE void _sg_gl_init_pixelformats_bptc(void) { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); -} - -_SOKOL_PRIVATE void _sg_gl_init_pixelformats_pvrtc(void) { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_2BPP]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_4BPP]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_2BPP]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_4BPP]); -} - -_SOKOL_PRIVATE void _sg_gl_init_pixelformats_etc2(void) { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11SN]); -} - -_SOKOL_PRIVATE void _sg_gl_init_limits(void) { - _SG_GL_CHECK_ERROR(); - GLint gl_int; - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl_int); - _SG_GL_CHECK_ERROR(); - _sg.limits.max_image_size_2d = gl_int; - _sg.limits.max_image_size_array = gl_int; - glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &gl_int); - _SG_GL_CHECK_ERROR(); - _sg.limits.max_image_size_cube = gl_int; - glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gl_int); - _SG_GL_CHECK_ERROR(); - if (gl_int > SG_MAX_VERTEX_ATTRIBUTES) { - gl_int = SG_MAX_VERTEX_ATTRIBUTES; - } - _sg.limits.max_vertex_attrs = gl_int; - glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &gl_int); - _SG_GL_CHECK_ERROR(); - _sg.limits.gl_max_vertex_uniform_vectors = gl_int; - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gl_int); - _SG_GL_CHECK_ERROR(); - _sg.limits.max_image_size_3d = gl_int; - glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gl_int); - _SG_GL_CHECK_ERROR(); - _sg.limits.max_image_array_layers = gl_int; - } - #endif - if (_sg.gl.ext_anisotropic) { - glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_int); - _SG_GL_CHECK_ERROR(); - _sg.gl.max_anisotropy = gl_int; - } - else { - _sg.gl.max_anisotropy = 1; - } - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gl_int); - _SG_GL_CHECK_ERROR(); - _sg.gl.max_combined_texture_image_units = gl_int; -} - -#if defined(SOKOL_GLCORE33) -_SOKOL_PRIVATE void _sg_gl_init_caps_glcore33(void) { - _sg.backend = SG_BACKEND_GLCORE33; - - _sg.features.origin_top_left = false; - _sg.features.instancing = true; - _sg.features.multiple_render_targets = true; - _sg.features.msaa_render_targets = true; - _sg.features.imagetype_3d = true; - _sg.features.imagetype_array = true; - _sg.features.image_clamp_to_border = true; - _sg.features.mrt_independent_blend_state = false; - _sg.features.mrt_independent_write_mask = true; - - /* scan extensions */ - bool has_s3tc = false; /* BC1..BC3 */ - bool has_rgtc = false; /* BC4 and BC5 */ - bool has_bptc = false; /* BC6H and BC7 */ - bool has_pvrtc = false; - bool has_etc2 = false; - GLint num_ext = 0; - glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext); - for (int i = 0; i < num_ext; i++) { - const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i); - if (ext) { - if (strstr(ext, "_texture_compression_s3tc")) { - has_s3tc = true; - } - else if (strstr(ext, "_texture_compression_rgtc")) { - has_rgtc = true; - } - else if (strstr(ext, "_texture_compression_bptc")) { - has_bptc = true; - } - else if (strstr(ext, "_texture_compression_pvrtc")) { - has_pvrtc = true; - } - else if (strstr(ext, "_ES3_compatibility")) { - has_etc2 = true; - } - else if (strstr(ext, "_texture_filter_anisotropic")) { - _sg.gl.ext_anisotropic = true; - } - } - } - - /* limits */ - _sg_gl_init_limits(); - - /* pixel formats */ - const bool has_bgra = false; /* not a bug */ - const bool has_colorbuffer_float = true; - const bool has_colorbuffer_half_float = true; - const bool has_texture_float_linear = true; /* FIXME??? */ - const bool has_texture_half_float_linear = true; - const bool has_float_blend = true; - _sg_gl_init_pixelformats(has_bgra); - _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); - _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float, has_texture_half_float_linear); - if (has_s3tc) { - _sg_gl_init_pixelformats_s3tc(); - } - if (has_rgtc) { - _sg_gl_init_pixelformats_rgtc(); - } - if (has_bptc) { - _sg_gl_init_pixelformats_bptc(); - } - if (has_pvrtc) { - _sg_gl_init_pixelformats_pvrtc(); - } - if (has_etc2) { - _sg_gl_init_pixelformats_etc2(); - } -} -#endif - -#if defined(SOKOL_GLES3) -_SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { - _sg.backend = SG_BACKEND_GLES3; - - _sg.features.origin_top_left = false; - _sg.features.instancing = true; - _sg.features.multiple_render_targets = true; - _sg.features.msaa_render_targets = true; - _sg.features.imagetype_3d = true; - _sg.features.imagetype_array = true; - _sg.features.image_clamp_to_border = false; - _sg.features.mrt_independent_blend_state = false; - _sg.features.mrt_independent_write_mask = false; - - bool has_s3tc = false; /* BC1..BC3 */ - bool has_rgtc = false; /* BC4 and BC5 */ - bool has_bptc = false; /* BC6H and BC7 */ - bool has_pvrtc = false; - #if defined(__EMSCRIPTEN__) - bool has_etc2 = false; - #else - bool has_etc2 = true; - #endif - bool has_colorbuffer_float = false; - bool has_colorbuffer_half_float = false; - bool has_texture_float_linear = false; - bool has_float_blend = false; - GLint num_ext = 0; - glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext); - for (int i = 0; i < num_ext; i++) { - const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i); - if (ext) { - if (strstr(ext, "_texture_compression_s3tc")) { - has_s3tc = true; - } - else if (strstr(ext, "_compressed_texture_s3tc")) { - has_s3tc = true; - } - else if (strstr(ext, "_texture_compression_rgtc")) { - has_rgtc = true; - } - else if (strstr(ext, "_texture_compression_bptc")) { - has_bptc = true; - } - else if (strstr(ext, "_texture_compression_pvrtc")) { - has_pvrtc = true; - } - else if (strstr(ext, "_compressed_texture_pvrtc")) { - has_pvrtc = true; - } - else if (strstr(ext, "_compressed_texture_etc")) { - has_etc2 = true; - } - else if (strstr(ext, "_color_buffer_float")) { - has_colorbuffer_float = true; - } - else if (strstr(ext, "_color_buffer_half_float")) { - has_colorbuffer_half_float = true; - } - else if (strstr(ext, "_texture_float_linear")) { - has_texture_float_linear = true; - } - else if (strstr(ext, "_float_blend")) { - has_float_blend = true; - } - else if (strstr(ext, "_texture_filter_anisotropic")) { - _sg.gl.ext_anisotropic = true; - } - } - } - - /* on WebGL2, color_buffer_float also includes 16-bit formats - see: https://developer.mozilla.org/en-US/docs/Web/API/EXT_color_buffer_float - */ - #if defined(__EMSCRIPTEN__) - has_colorbuffer_half_float = has_colorbuffer_float; - #endif - - /* limits */ - _sg_gl_init_limits(); - - /* pixel formats */ - const bool has_texture_half_float_linear = true; - const bool has_bgra = false; /* not a bug */ - _sg_gl_init_pixelformats(has_bgra); - _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); - _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float, has_texture_half_float_linear); - if (has_s3tc) { - _sg_gl_init_pixelformats_s3tc(); - } - if (has_rgtc) { - _sg_gl_init_pixelformats_rgtc(); - } - if (has_bptc) { - _sg_gl_init_pixelformats_bptc(); - } - if (has_pvrtc) { - _sg_gl_init_pixelformats_pvrtc(); - } - if (has_etc2) { - _sg_gl_init_pixelformats_etc2(); - } -} -#endif - -#if defined(SOKOL_GLES3) || defined(SOKOL_GLES2) -_SOKOL_PRIVATE void _sg_gl_init_caps_gles2(void) { - _sg.backend = SG_BACKEND_GLES2; - - bool has_s3tc = false; /* BC1..BC3 */ - bool has_rgtc = false; /* BC4 and BC5 */ - bool has_bptc = false; /* BC6H and BC7 */ - bool has_pvrtc = false; - bool has_etc2 = false; - bool has_texture_float = false; - bool has_texture_float_linear = false; - bool has_colorbuffer_float = false; - bool has_float_blend = false; - bool has_instancing = false; - const char* ext = (const char*) glGetString(GL_EXTENSIONS); - if (ext) { - has_s3tc = strstr(ext, "_texture_compression_s3tc") || strstr(ext, "_compressed_texture_s3tc"); - has_rgtc = strstr(ext, "_texture_compression_rgtc"); - has_bptc = strstr(ext, "_texture_compression_bptc"); - has_pvrtc = strstr(ext, "_texture_compression_pvrtc") || strstr(ext, "_compressed_texture_pvrtc"); - has_etc2 = strstr(ext, "_compressed_texture_etc"); - has_texture_float = strstr(ext, "_texture_float"); - has_texture_float_linear = strstr(ext, "_texture_float_linear"); - has_colorbuffer_float = strstr(ext, "_color_buffer_float"); - has_float_blend = strstr(ext, "_float_blend"); - /* don't bother with half_float support on WebGL1 - has_texture_half_float = strstr(ext, "_texture_half_float"); - has_texture_half_float_linear = strstr(ext, "_texture_half_float_linear"); - has_colorbuffer_half_float = strstr(ext, "_color_buffer_half_float"); - */ - has_instancing = strstr(ext, "_instanced_arrays"); - _sg.gl.ext_anisotropic = strstr(ext, "ext_anisotropic"); - } - - _sg.features.origin_top_left = false; - #if defined(_SOKOL_GL_INSTANCING_ENABLED) - _sg.features.instancing = has_instancing; - #endif - _sg.features.multiple_render_targets = false; - _sg.features.msaa_render_targets = false; - _sg.features.imagetype_3d = false; - _sg.features.imagetype_array = false; - _sg.features.image_clamp_to_border = false; - _sg.features.mrt_independent_blend_state = false; - _sg.features.mrt_independent_write_mask = false; - - /* limits */ - _sg_gl_init_limits(); - - /* pixel formats */ - const bool has_bgra = false; /* not a bug */ - const bool has_texture_half_float = false; - const bool has_texture_half_float_linear = false; - const bool has_colorbuffer_half_float = false; - _sg_gl_init_pixelformats(has_bgra); - if (has_texture_float) { - _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); - } - if (has_texture_half_float) { - _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float, has_texture_half_float_linear); - } - if (has_s3tc) { - _sg_gl_init_pixelformats_s3tc(); - } - if (has_rgtc) { - _sg_gl_init_pixelformats_rgtc(); - } - if (has_bptc) { - _sg_gl_init_pixelformats_bptc(); - } - if (has_pvrtc) { - _sg_gl_init_pixelformats_pvrtc(); - } - if (has_etc2) { - _sg_gl_init_pixelformats_etc2(); - } - /* GLES2 doesn't allow multi-sampled render targets at all */ - for (int i = 0; i < _SG_PIXELFORMAT_NUM; i++) { - _sg.formats[i].msaa = false; - } -} -#endif - -/*-- state cache implementation ----------------------------------------------*/ -_SOKOL_PRIVATE void _sg_gl_cache_clear_buffer_bindings(bool force) { - if (force || (_sg.gl.cache.vertex_buffer != 0)) { - glBindBuffer(GL_ARRAY_BUFFER, 0); - _sg.gl.cache.vertex_buffer = 0; - } - if (force || (_sg.gl.cache.index_buffer != 0)) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - _sg.gl.cache.index_buffer = 0; - } -} - -_SOKOL_PRIVATE void _sg_gl_cache_bind_buffer(GLenum target, GLuint buffer) { - SOKOL_ASSERT((GL_ARRAY_BUFFER == target) || (GL_ELEMENT_ARRAY_BUFFER == target)); - if (target == GL_ARRAY_BUFFER) { - if (_sg.gl.cache.vertex_buffer != buffer) { - _sg.gl.cache.vertex_buffer = buffer; - glBindBuffer(target, buffer); - } - } - else { - if (_sg.gl.cache.index_buffer != buffer) { - _sg.gl.cache.index_buffer = buffer; - glBindBuffer(target, buffer); - } - } -} - -_SOKOL_PRIVATE void _sg_gl_cache_store_buffer_binding(GLenum target) { - if (target == GL_ARRAY_BUFFER) { - _sg.gl.cache.stored_vertex_buffer = _sg.gl.cache.vertex_buffer; - } - else { - _sg.gl.cache.stored_index_buffer = _sg.gl.cache.index_buffer; - } -} - -_SOKOL_PRIVATE void _sg_gl_cache_restore_buffer_binding(GLenum target) { - if (target == GL_ARRAY_BUFFER) { - if (_sg.gl.cache.stored_vertex_buffer != 0) { - /* we only care restoring valid ids */ - _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_vertex_buffer); - _sg.gl.cache.stored_vertex_buffer = 0; - } - } - else { - if (_sg.gl.cache.stored_index_buffer != 0) { - /* we only care restoring valid ids */ - _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_index_buffer); - _sg.gl.cache.stored_index_buffer = 0; - } - } -} - -/* called when from _sg_gl_destroy_buffer() */ -_SOKOL_PRIVATE void _sg_gl_cache_invalidate_buffer(GLuint buf) { - if (buf == _sg.gl.cache.vertex_buffer) { - _sg.gl.cache.vertex_buffer = 0; - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - if (buf == _sg.gl.cache.index_buffer) { - _sg.gl.cache.index_buffer = 0; - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } - if (buf == _sg.gl.cache.stored_vertex_buffer) { - _sg.gl.cache.stored_vertex_buffer = 0; - } - if (buf == _sg.gl.cache.stored_index_buffer) { - _sg.gl.cache.stored_index_buffer = 0; - } - for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { - if (buf == _sg.gl.cache.attrs[i].gl_vbuf) { - _sg.gl.cache.attrs[i].gl_vbuf = 0; - } - } -} - -_SOKOL_PRIVATE void _sg_gl_cache_active_texture(GLenum texture) { - if (_sg.gl.cache.cur_active_texture != texture) { - _sg.gl.cache.cur_active_texture = texture; - glActiveTexture(texture); - } -} - -_SOKOL_PRIVATE void _sg_gl_cache_clear_texture_bindings(bool force) { - for (int i = 0; (i < SG_MAX_SHADERSTAGE_IMAGES) && (i < _sg.gl.max_combined_texture_image_units); i++) { - if (force || (_sg.gl.cache.textures[i].texture != 0)) { - GLenum gl_texture_slot = (GLenum) (GL_TEXTURE0 + i); - glActiveTexture(gl_texture_slot); - glBindTexture(GL_TEXTURE_2D, 0); - glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - glBindTexture(GL_TEXTURE_3D, 0); - glBindTexture(GL_TEXTURE_2D_ARRAY, 0); - } - #endif - _sg.gl.cache.textures[i].target = 0; - _sg.gl.cache.textures[i].texture = 0; - _sg.gl.cache.cur_active_texture = gl_texture_slot; - } - } -} - -_SOKOL_PRIVATE void _sg_gl_cache_bind_texture(int slot_index, GLenum target, GLuint texture) { - /* it's valid to call this function with target=0 and/or texture=0 - target=0 will unbind the previous binding, texture=0 will clear - the new binding - */ - SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); - if (slot_index >= _sg.gl.max_combined_texture_image_units) { - return; - } - _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.textures[slot_index]; - if ((slot->target != target) || (slot->texture != texture)) { - _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + slot_index)); - /* if the target has changed, clear the previous binding on that target */ - if ((target != slot->target) && (slot->target != 0)) { - glBindTexture(slot->target, 0); - } - /* apply new binding (texture can be 0 to unbind) */ - if (target != 0) { - glBindTexture(target, texture); - } - slot->target = target; - slot->texture = texture; - } -} - -_SOKOL_PRIVATE void _sg_gl_cache_store_texture_binding(int slot_index) { - SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); - _sg.gl.cache.stored_texture = _sg.gl.cache.textures[slot_index]; -} - -_SOKOL_PRIVATE void _sg_gl_cache_restore_texture_binding(int slot_index) { - SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); - _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.stored_texture; - if (slot->texture != 0) { - /* we only care restoring valid ids */ - SOKOL_ASSERT(slot->target != 0); - _sg_gl_cache_bind_texture(slot_index, slot->target, slot->texture); - slot->target = 0; - slot->texture = 0; - } -} - -/* called from _sg_gl_destroy_texture() */ -_SOKOL_PRIVATE void _sg_gl_cache_invalidate_texture(GLuint tex) { - for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { - _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.textures[i]; - if (tex == slot->texture) { - _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + i)); - glBindTexture(slot->target, 0); - slot->target = 0; - slot->texture = 0; - } - } - if (tex == _sg.gl.cache.stored_texture.texture) { - _sg.gl.cache.stored_texture.target = 0; - _sg.gl.cache.stored_texture.texture = 0; - } -} - -/* called from _sg_gl_destroy_shader() */ -_SOKOL_PRIVATE void _sg_gl_cache_invalidate_program(GLuint prog) { - if (prog == _sg.gl.cache.prog) { - _sg.gl.cache.prog = 0; - glUseProgram(0); - } -} - -/* called from _sg_gl_destroy_pipeline() */ -_SOKOL_PRIVATE void _sg_gl_cache_invalidate_pipeline(_sg_pipeline_t* pip) { - if (pip == _sg.gl.cache.cur_pipeline) { - _sg.gl.cache.cur_pipeline = 0; - _sg.gl.cache.cur_pipeline_id.id = SG_INVALID_ID; - } -} - -_SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { - if (_sg.gl.cur_context) { - _SG_GL_CHECK_ERROR(); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - glBindVertexArray(_sg.gl.cur_context->vao); - _SG_GL_CHECK_ERROR(); - } - #endif - memset(&_sg.gl.cache, 0, sizeof(_sg.gl.cache)); - _sg_gl_cache_clear_buffer_bindings(true); - _SG_GL_CHECK_ERROR(); - _sg_gl_cache_clear_texture_bindings(true); - _SG_GL_CHECK_ERROR(); - for (int i = 0; i < _sg.limits.max_vertex_attrs; i++) { - _sg_gl_attr_t* attr = &_sg.gl.cache.attrs[i].gl_attr; - attr->vb_index = -1; - attr->divisor = -1; - glDisableVertexAttribArray((GLuint)i); - _SG_GL_CHECK_ERROR(); - } - _sg.gl.cache.cur_primitive_type = GL_TRIANGLES; - - /* shader program */ - glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&_sg.gl.cache.prog); - _SG_GL_CHECK_ERROR(); - - /* depth and stencil state */ - _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS; - _sg.gl.cache.stencil.front.compare = SG_COMPAREFUNC_ALWAYS; - _sg.gl.cache.stencil.front.fail_op = SG_STENCILOP_KEEP; - _sg.gl.cache.stencil.front.depth_fail_op = SG_STENCILOP_KEEP; - _sg.gl.cache.stencil.front.pass_op = SG_STENCILOP_KEEP; - _sg.gl.cache.stencil.back.compare = SG_COMPAREFUNC_ALWAYS; - _sg.gl.cache.stencil.back.fail_op = SG_STENCILOP_KEEP; - _sg.gl.cache.stencil.back.depth_fail_op = SG_STENCILOP_KEEP; - _sg.gl.cache.stencil.back.pass_op = SG_STENCILOP_KEEP; - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_ALWAYS); - glDepthMask(GL_FALSE); - glDisable(GL_STENCIL_TEST); - glStencilFunc(GL_ALWAYS, 0, 0); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glStencilMask(0); - - /* blend state */ - _sg.gl.cache.blend.src_factor_rgb = SG_BLENDFACTOR_ONE; - _sg.gl.cache.blend.dst_factor_rgb = SG_BLENDFACTOR_ZERO; - _sg.gl.cache.blend.op_rgb = SG_BLENDOP_ADD; - _sg.gl.cache.blend.src_factor_alpha = SG_BLENDFACTOR_ONE; - _sg.gl.cache.blend.dst_factor_alpha = SG_BLENDFACTOR_ZERO; - _sg.gl.cache.blend.op_alpha = SG_BLENDOP_ADD; - glDisable(GL_BLEND); - glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); - glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); - glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); - - /* standalone state */ - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; - } - _sg.gl.cache.cull_mode = SG_CULLMODE_NONE; - _sg.gl.cache.face_winding = SG_FACEWINDING_CW; - _sg.gl.cache.sample_count = 1; - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glPolygonOffset(0.0f, 0.0f); - glDisable(GL_POLYGON_OFFSET_FILL); - glDisable(GL_CULL_FACE); - glFrontFace(GL_CW); - glCullFace(GL_BACK); - glEnable(GL_SCISSOR_TEST); - glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); - glEnable(GL_DITHER); - glDisable(GL_POLYGON_OFFSET_FILL); - #if defined(SOKOL_GLCORE33) - glEnable(GL_MULTISAMPLE); - glEnable(GL_PROGRAM_POINT_SIZE); - #endif - } -} - -_SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) { - /* assumes that _sg.gl is already zero-initialized */ - _sg.gl.valid = true; - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - _sg.gl.gles2 = desc->context.gl.force_gles2; - #else - _SOKOL_UNUSED(desc); - _sg.gl.gles2 = false; - #endif - - #if defined(_SOKOL_USE_WIN32_GL_LOADER) - _sg_gl_load_opengl(); - #endif - - /* clear initial GL error state */ - #if defined(SOKOL_DEBUG) - while (glGetError() != GL_NO_ERROR); - #endif - #if defined(SOKOL_GLCORE33) - _sg_gl_init_caps_glcore33(); - #elif defined(SOKOL_GLES3) - if (_sg.gl.gles2) { - _sg_gl_init_caps_gles2(); - } - else { - _sg_gl_init_caps_gles3(); - } - #else - _sg_gl_init_caps_gles2(); - #endif -} - -_SOKOL_PRIVATE void _sg_gl_discard_backend(void) { - SOKOL_ASSERT(_sg.gl.valid); - _sg.gl.valid = false; - #if defined(_SOKOL_USE_WIN32_GL_LOADER) - _sg_gl_unload_opengl(); - #endif -} - -_SOKOL_PRIVATE void _sg_gl_activate_context(_sg_context_t* ctx) { - SOKOL_ASSERT(_sg.gl.valid); - /* NOTE: ctx can be 0 to unset the current context */ - _sg.gl.cur_context = ctx; - _sg_gl_reset_state_cache(); -} - -/*-- GL backend resource creation and destruction ----------------------------*/ -_SOKOL_PRIVATE sg_resource_state _sg_gl_create_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - SOKOL_ASSERT(0 == ctx->default_framebuffer); - _SG_GL_CHECK_ERROR(); - glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&ctx->default_framebuffer); - _SG_GL_CHECK_ERROR(); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - SOKOL_ASSERT(0 == ctx->vao); - glGenVertexArrays(1, &ctx->vao); - glBindVertexArray(ctx->vao); - _SG_GL_CHECK_ERROR(); - } - #endif - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_gl_destroy_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - if (ctx->vao) { - glDeleteVertexArrays(1, &ctx->vao); - } - _SG_GL_CHECK_ERROR(); - } - #else - _SOKOL_UNUSED(ctx); - #endif -} - -_SOKOL_PRIVATE sg_resource_state _sg_gl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { - SOKOL_ASSERT(buf && desc); - _SG_GL_CHECK_ERROR(); - _sg_buffer_common_init(&buf->cmn, desc); - buf->gl.ext_buffers = (0 != desc->gl_buffers[0]); - GLenum gl_target = _sg_gl_buffer_target(buf->cmn.type); - GLenum gl_usage = _sg_gl_usage(buf->cmn.usage); - for (int slot = 0; slot < buf->cmn.num_slots; slot++) { - GLuint gl_buf = 0; - if (buf->gl.ext_buffers) { - SOKOL_ASSERT(desc->gl_buffers[slot]); - gl_buf = desc->gl_buffers[slot]; - } - else { - glGenBuffers(1, &gl_buf); - SOKOL_ASSERT(gl_buf); - _sg_gl_cache_store_buffer_binding(gl_target); - _sg_gl_cache_bind_buffer(gl_target, gl_buf); - glBufferData(gl_target, buf->cmn.size, 0, gl_usage); - if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { - SOKOL_ASSERT(desc->data.ptr); - glBufferSubData(gl_target, 0, buf->cmn.size, desc->data.ptr); - } - _sg_gl_cache_restore_buffer_binding(gl_target); - } - buf->gl.buf[slot] = gl_buf; - } - _SG_GL_CHECK_ERROR(); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_gl_destroy_buffer(_sg_buffer_t* buf) { - SOKOL_ASSERT(buf); - _SG_GL_CHECK_ERROR(); - for (int slot = 0; slot < buf->cmn.num_slots; slot++) { - if (buf->gl.buf[slot]) { - _sg_gl_cache_invalidate_buffer(buf->gl.buf[slot]); - if (!buf->gl.ext_buffers) { - glDeleteBuffers(1, &buf->gl.buf[slot]); - } - } - } - _SG_GL_CHECK_ERROR(); -} - -_SOKOL_PRIVATE bool _sg_gl_supported_texture_format(sg_pixel_format fmt) { - const int fmt_index = (int) fmt; - SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM)); - return _sg.formats[fmt_index].sample; -} - -_SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_image_desc* desc) { - SOKOL_ASSERT(img && desc); - _SG_GL_CHECK_ERROR(); - _sg_image_common_init(&img->cmn, desc); - img->gl.ext_textures = (0 != desc->gl_textures[0]); - - /* check if texture format is support */ - if (!_sg_gl_supported_texture_format(img->cmn.pixel_format)) { - SOKOL_LOG("texture format not supported by GL context\n"); - return SG_RESOURCESTATE_FAILED; - } - /* check for optional texture types */ - if ((img->cmn.type == SG_IMAGETYPE_3D) && !_sg.features.imagetype_3d) { - SOKOL_LOG("3D textures not supported by GL context\n"); - return SG_RESOURCESTATE_FAILED; - } - if ((img->cmn.type == SG_IMAGETYPE_ARRAY) && !_sg.features.imagetype_array) { - SOKOL_LOG("array textures not supported by GL context\n"); - return SG_RESOURCESTATE_FAILED; - } - - #if !defined(SOKOL_GLES2) - bool msaa = false; - if (!_sg.gl.gles2) { - msaa = (img->cmn.sample_count > 1) && (_sg.features.msaa_render_targets); - } - #endif - - if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { - /* special case depth-stencil-buffer? */ - SOKOL_ASSERT((img->cmn.usage == SG_USAGE_IMMUTABLE) && (img->cmn.num_slots == 1)); - SOKOL_ASSERT(!img->gl.ext_textures); /* cannot provide external texture for depth images */ - glGenRenderbuffers(1, &img->gl.depth_render_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, img->gl.depth_render_buffer); - GLenum gl_depth_format = _sg_gl_depth_attachment_format(img->cmn.pixel_format); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2 && msaa) { - glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_depth_format, img->cmn.width, img->cmn.height); - } - else - #endif - { - glRenderbufferStorage(GL_RENDERBUFFER, gl_depth_format, img->cmn.width, img->cmn.height); - } - } - else { - /* regular color texture */ - img->gl.target = _sg_gl_texture_target(img->cmn.type); - const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->cmn.pixel_format); - - /* if this is a MSAA render target, need to create a separate render buffer */ - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2 && img->cmn.render_target && msaa) { - glGenRenderbuffers(1, &img->gl.msaa_render_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, img->gl.msaa_render_buffer); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_internal_format, img->cmn.width, img->cmn.height); - } - #endif - - if (img->gl.ext_textures) { - /* inject externally GL textures */ - for (int slot = 0; slot < img->cmn.num_slots; slot++) { - SOKOL_ASSERT(desc->gl_textures[slot]); - img->gl.tex[slot] = desc->gl_textures[slot]; - } - if (desc->gl_texture_target) { - img->gl.target = (GLenum)desc->gl_texture_target; - } - } - else { - /* create our own GL texture(s) */ - const GLenum gl_format = _sg_gl_teximage_format(img->cmn.pixel_format); - const bool is_compressed = _sg_is_compressed_pixel_format(img->cmn.pixel_format); - for (int slot = 0; slot < img->cmn.num_slots; slot++) { - glGenTextures(1, &img->gl.tex[slot]); - SOKOL_ASSERT(img->gl.tex[slot]); - _sg_gl_cache_store_texture_binding(0); - _sg_gl_cache_bind_texture(0, img->gl.target, img->gl.tex[slot]); - GLenum gl_min_filter = _sg_gl_filter(img->cmn.min_filter); - GLenum gl_mag_filter = _sg_gl_filter(img->cmn.mag_filter); - glTexParameteri(img->gl.target, GL_TEXTURE_MIN_FILTER, (GLint)gl_min_filter); - glTexParameteri(img->gl.target, GL_TEXTURE_MAG_FILTER, (GLint)gl_mag_filter); - if (_sg.gl.ext_anisotropic && (img->cmn.max_anisotropy > 1)) { - GLint max_aniso = (GLint) img->cmn.max_anisotropy; - if (max_aniso > _sg.gl.max_anisotropy) { - max_aniso = _sg.gl.max_anisotropy; - } - glTexParameteri(img->gl.target, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso); - } - if (img->cmn.type == SG_IMAGETYPE_CUBE) { - glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - else { - glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_S, (GLint)_sg_gl_wrap(img->cmn.wrap_u)); - glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_T, (GLint)_sg_gl_wrap(img->cmn.wrap_v)); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2 && (img->cmn.type == SG_IMAGETYPE_3D)) { - glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_R, (GLint)_sg_gl_wrap(img->cmn.wrap_w)); - } - #endif - #if defined(SOKOL_GLCORE33) - float border[4]; - switch (img->cmn.border_color) { - case SG_BORDERCOLOR_TRANSPARENT_BLACK: - border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 0.0f; - break; - case SG_BORDERCOLOR_OPAQUE_WHITE: - border[0] = 1.0f; border[1] = 1.0f; border[2] = 1.0f; border[3] = 1.0f; - break; - default: - border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 1.0f; - break; - } - glTexParameterfv(img->gl.target, GL_TEXTURE_BORDER_COLOR, border); - #endif - } - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - /* GL spec has strange defaults for mipmap min/max lod: -1000 to +1000 */ - const float min_lod = _sg_clamp(desc->min_lod, 0.0f, 1000.0f); - const float max_lod = _sg_clamp(desc->max_lod, 0.0f, 1000.0f); - glTexParameterf(img->gl.target, GL_TEXTURE_MIN_LOD, min_lod); - glTexParameterf(img->gl.target, GL_TEXTURE_MAX_LOD, max_lod); - } - #endif - const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; - int data_index = 0; - for (int face_index = 0; face_index < num_faces; face_index++) { - for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, data_index++) { - GLenum gl_img_target = img->gl.target; - if (SG_IMAGETYPE_CUBE == img->cmn.type) { - gl_img_target = _sg_gl_cubeface_target(face_index); - } - const GLvoid* data_ptr = desc->data.subimage[face_index][mip_index].ptr; - int mip_width = img->cmn.width >> mip_index; - if (mip_width == 0) { - mip_width = 1; - } - int mip_height = img->cmn.height >> mip_index; - if (mip_height == 0) { - mip_height = 1; - } - if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { - if (is_compressed) { - const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; - glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format, - mip_width, mip_height, 0, data_size, data_ptr); - } - else { - const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); - glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format, - mip_width, mip_height, 0, gl_format, gl_type, data_ptr); - } - } - #if !defined(SOKOL_GLES2) - else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type))) { - int mip_depth = img->cmn.num_slices; - if (SG_IMAGETYPE_3D == img->cmn.type) { - mip_depth >>= mip_index; - } - if (mip_depth == 0) { - mip_depth = 1; - } - if (is_compressed) { - const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; - glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format, - mip_width, mip_height, mip_depth, 0, data_size, data_ptr); - } - else { - const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); - glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format, - mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); - } - } - #endif - } - } - _sg_gl_cache_restore_texture_binding(0); - } - } - } - _SG_GL_CHECK_ERROR(); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_gl_destroy_image(_sg_image_t* img) { - SOKOL_ASSERT(img); - _SG_GL_CHECK_ERROR(); - for (int slot = 0; slot < img->cmn.num_slots; slot++) { - if (img->gl.tex[slot]) { - _sg_gl_cache_invalidate_texture(img->gl.tex[slot]); - if (!img->gl.ext_textures) { - glDeleteTextures(1, &img->gl.tex[slot]); - } - } - } - if (img->gl.depth_render_buffer) { - glDeleteRenderbuffers(1, &img->gl.depth_render_buffer); - } - if (img->gl.msaa_render_buffer) { - glDeleteRenderbuffers(1, &img->gl.msaa_render_buffer); - } - _SG_GL_CHECK_ERROR(); -} - -_SOKOL_PRIVATE GLuint _sg_gl_compile_shader(sg_shader_stage stage, const char* src) { - SOKOL_ASSERT(src); - _SG_GL_CHECK_ERROR(); - GLuint gl_shd = glCreateShader(_sg_gl_shader_stage(stage)); - glShaderSource(gl_shd, 1, &src, 0); - glCompileShader(gl_shd); - GLint compile_status = 0; - glGetShaderiv(gl_shd, GL_COMPILE_STATUS, &compile_status); - if (!compile_status) { - /* compilation failed, log error and delete shader */ - GLint log_len = 0; - glGetShaderiv(gl_shd, GL_INFO_LOG_LENGTH, &log_len); - if (log_len > 0) { - GLchar* log_buf = (GLchar*) SOKOL_MALLOC((size_t)log_len); - glGetShaderInfoLog(gl_shd, log_len, &log_len, log_buf); - SOKOL_LOG(log_buf); - SOKOL_FREE(log_buf); - } - glDeleteShader(gl_shd); - gl_shd = 0; - } - _SG_GL_CHECK_ERROR(); - return gl_shd; -} - -_SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { - SOKOL_ASSERT(shd && desc); - SOKOL_ASSERT(!shd->gl.prog); - _SG_GL_CHECK_ERROR(); - - _sg_shader_common_init(&shd->cmn, desc); - - /* copy vertex attribute names over, these are required for GLES2, and optional for GLES3 and GL3.x */ - for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { - _sg_strcpy(&shd->gl.attrs[i].name, desc->attrs[i].name); - } - - GLuint gl_vs = _sg_gl_compile_shader(SG_SHADERSTAGE_VS, desc->vs.source); - GLuint gl_fs = _sg_gl_compile_shader(SG_SHADERSTAGE_FS, desc->fs.source); - if (!(gl_vs && gl_fs)) { - return SG_RESOURCESTATE_FAILED; - } - GLuint gl_prog = glCreateProgram(); - glAttachShader(gl_prog, gl_vs); - glAttachShader(gl_prog, gl_fs); - glLinkProgram(gl_prog); - glDeleteShader(gl_vs); - glDeleteShader(gl_fs); - _SG_GL_CHECK_ERROR(); - - GLint link_status; - glGetProgramiv(gl_prog, GL_LINK_STATUS, &link_status); - if (!link_status) { - GLint log_len = 0; - glGetProgramiv(gl_prog, GL_INFO_LOG_LENGTH, &log_len); - if (log_len > 0) { - GLchar* log_buf = (GLchar*) SOKOL_MALLOC((size_t)log_len); - glGetProgramInfoLog(gl_prog, log_len, &log_len, log_buf); - SOKOL_LOG(log_buf); - SOKOL_FREE(log_buf); - } - glDeleteProgram(gl_prog); - return SG_RESOURCESTATE_FAILED; - } - shd->gl.prog = gl_prog; - - /* resolve uniforms */ - _SG_GL_CHECK_ERROR(); - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; - _sg_gl_shader_stage_t* gl_stage = &shd->gl.stage[stage_index]; - for (int ub_index = 0; ub_index < shd->cmn.stage[stage_index].num_uniform_blocks; ub_index++) { - const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; - SOKOL_ASSERT(ub_desc->size > 0); - _sg_gl_uniform_block_t* ub = &gl_stage->uniform_blocks[ub_index]; - SOKOL_ASSERT(ub->num_uniforms == 0); - uint32_t cur_uniform_offset = 0; - for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { - const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; - if (u_desc->type == SG_UNIFORMTYPE_INVALID) { - break; - } - const uint32_t u_align = _sg_uniform_alignment(u_desc->type, u_desc->array_count, ub_desc->layout); - const uint32_t u_size = _sg_uniform_size(u_desc->type, u_desc->array_count, ub_desc->layout); - cur_uniform_offset = _sg_align_u32(cur_uniform_offset, u_align); - _sg_gl_uniform_t* u = &ub->uniforms[u_index]; - u->type = u_desc->type; - u->count = (uint16_t) u_desc->array_count; - u->offset = (uint16_t) cur_uniform_offset; - cur_uniform_offset += u_size; - if (u_desc->name) { - u->gl_loc = glGetUniformLocation(gl_prog, u_desc->name); - } - else { - u->gl_loc = u_index; - } - ub->num_uniforms++; - } - if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { - cur_uniform_offset = _sg_align_u32(cur_uniform_offset, 16); - } - SOKOL_ASSERT(ub_desc->size == (size_t)cur_uniform_offset); - _SOKOL_UNUSED(cur_uniform_offset); - } - } - - /* resolve image locations */ - _SG_GL_CHECK_ERROR(); - GLuint cur_prog = 0; - glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&cur_prog); - glUseProgram(gl_prog); - int gl_tex_slot = 0; - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; - _sg_gl_shader_stage_t* gl_stage = &shd->gl.stage[stage_index]; - for (int img_index = 0; img_index < shd->cmn.stage[stage_index].num_images; img_index++) { - const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; - SOKOL_ASSERT(img_desc->image_type != _SG_IMAGETYPE_DEFAULT); - _sg_gl_shader_image_t* gl_img = &gl_stage->images[img_index]; - GLint gl_loc = img_index; - if (img_desc->name) { - gl_loc = glGetUniformLocation(gl_prog, img_desc->name); - } - if (gl_loc != -1) { - gl_img->gl_tex_slot = gl_tex_slot++; - glUniform1i(gl_loc, gl_img->gl_tex_slot); - } - else { - gl_img->gl_tex_slot = -1; - } - } - } - /* it's legal to call glUseProgram with 0 */ - glUseProgram(cur_prog); - _SG_GL_CHECK_ERROR(); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_gl_destroy_shader(_sg_shader_t* shd) { - SOKOL_ASSERT(shd); - _SG_GL_CHECK_ERROR(); - if (shd->gl.prog) { - _sg_gl_cache_invalidate_program(shd->gl.prog); - glDeleteProgram(shd->gl.prog); - } - _SG_GL_CHECK_ERROR(); -} - -_SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { - SOKOL_ASSERT(pip && shd && desc); - SOKOL_ASSERT(!pip->shader && pip->cmn.shader_id.id == SG_INVALID_ID); - SOKOL_ASSERT(desc->shader.id == shd->slot.id); - SOKOL_ASSERT(shd->gl.prog); - pip->shader = shd; - _sg_pipeline_common_init(&pip->cmn, desc); - pip->gl.primitive_type = desc->primitive_type; - pip->gl.depth = desc->depth; - pip->gl.stencil = desc->stencil; - // FIXME: blend color and write mask per draw-buffer-attachment (requires GL4) - pip->gl.blend = desc->colors[0].blend; - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - pip->gl.color_write_mask[i] = desc->colors[i].write_mask; - } - pip->gl.cull_mode = desc->cull_mode; - pip->gl.face_winding = desc->face_winding; - pip->gl.sample_count = desc->sample_count; - pip->gl.alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled; - - /* resolve vertex attributes */ - for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - pip->gl.attrs[attr_index].vb_index = -1; - } - for (int attr_index = 0; attr_index < _sg.limits.max_vertex_attrs; attr_index++) { - const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { - break; - } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[a_desc->buffer_index]; - const sg_vertex_step step_func = l_desc->step_func; - const int step_rate = l_desc->step_rate; - GLint attr_loc = attr_index; - if (!_sg_strempty(&shd->gl.attrs[attr_index].name)) { - attr_loc = glGetAttribLocation(pip->shader->gl.prog, _sg_strptr(&shd->gl.attrs[attr_index].name)); - } - SOKOL_ASSERT(attr_loc < (GLint)_sg.limits.max_vertex_attrs); - if (attr_loc != -1) { - _sg_gl_attr_t* gl_attr = &pip->gl.attrs[attr_loc]; - SOKOL_ASSERT(gl_attr->vb_index == -1); - gl_attr->vb_index = (int8_t) a_desc->buffer_index; - if (step_func == SG_VERTEXSTEP_PER_VERTEX) { - gl_attr->divisor = 0; - } - else { - gl_attr->divisor = (int8_t) step_rate; - pip->cmn.use_instanced_draw = true; - } - SOKOL_ASSERT(l_desc->stride > 0); - gl_attr->stride = (uint8_t) l_desc->stride; - gl_attr->offset = a_desc->offset; - gl_attr->size = (uint8_t) _sg_gl_vertexformat_size(a_desc->format); - gl_attr->type = _sg_gl_vertexformat_type(a_desc->format); - gl_attr->normalized = _sg_gl_vertexformat_normalized(a_desc->format); - pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; - } - else { - SOKOL_LOG("Vertex attribute not found in shader: "); - SOKOL_LOG(_sg_strptr(&shd->gl.attrs[attr_index].name)); - } - } - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_gl_destroy_pipeline(_sg_pipeline_t* pip) { - SOKOL_ASSERT(pip); - _sg_gl_cache_invalidate_pipeline(pip); -} - -/* - _sg_create_pass - - att_imgs must point to a _sg_image* att_imgs[SG_MAX_COLOR_ATTACHMENTS+1] array, - first entries are the color attachment images (or nullptr), last entry - is the depth-stencil image (or nullptr). -*/ -_SOKOL_PRIVATE sg_resource_state _sg_gl_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { - SOKOL_ASSERT(pass && att_images && desc); - SOKOL_ASSERT(att_images && att_images[0]); - _SG_GL_CHECK_ERROR(); - - _sg_pass_common_init(&pass->cmn, desc); - - /* copy image pointers */ - const sg_pass_attachment_desc* att_desc; - for (int i = 0; i < pass->cmn.num_color_atts; i++) { - att_desc = &desc->color_attachments[i]; - SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); - SOKOL_ASSERT(0 == pass->gl.color_atts[i].image); - SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->cmn.pixel_format)); - pass->gl.color_atts[i].image = att_images[i]; - } - SOKOL_ASSERT(0 == pass->gl.ds_att.image); - att_desc = &desc->depth_stencil_attachment; - if (att_desc->image.id != SG_INVALID_ID) { - const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; - SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); - pass->gl.ds_att.image = att_images[ds_img_index]; - } - - /* store current framebuffer binding (restored at end of function) */ - GLuint gl_orig_fb; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&gl_orig_fb); - - /* create a framebuffer object */ - glGenFramebuffers(1, &pass->gl.fb); - glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); - - /* attach msaa render buffer or textures */ - const bool is_msaa = (0 != att_images[0]->gl.msaa_render_buffer); - if (is_msaa) { - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - const _sg_image_t* att_img = pass->gl.color_atts[i].image; - if (att_img) { - const GLuint gl_render_buffer = att_img->gl.msaa_render_buffer; - SOKOL_ASSERT(gl_render_buffer); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, (GLenum)(GL_COLOR_ATTACHMENT0+i), GL_RENDERBUFFER, gl_render_buffer); - } - } - } - else { - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - const _sg_image_t* att_img = pass->gl.color_atts[i].image; - const int mip_level = pass->cmn.color_atts[i].mip_level; - const int slice = pass->cmn.color_atts[i].slice; - if (att_img) { - const GLuint gl_tex = att_img->gl.tex[0]; - SOKOL_ASSERT(gl_tex); - const GLenum gl_att = (GLenum)(GL_COLOR_ATTACHMENT0 + i); - switch (att_img->cmn.type) { - case SG_IMAGETYPE_2D: - glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att, GL_TEXTURE_2D, gl_tex, mip_level); - break; - case SG_IMAGETYPE_CUBE: - glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att, _sg_gl_cubeface_target(slice), gl_tex, mip_level); - break; - default: - /* 3D- or array-texture */ - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_att, gl_tex, mip_level, slice); - } - #endif - break; - } - } - } - } - - /* attach depth-stencil buffer to framebuffer */ - if (pass->gl.ds_att.image) { - const GLuint gl_render_buffer = pass->gl.ds_att.image->gl.depth_render_buffer; - SOKOL_ASSERT(gl_render_buffer); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gl_render_buffer); - if (_sg_is_depth_stencil_format(pass->gl.ds_att.image->cmn.pixel_format)) { - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl_render_buffer); - } - } - - /* check if framebuffer is complete */ - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - SOKOL_LOG("Framebuffer completeness check failed!\n"); - return SG_RESOURCESTATE_FAILED; - } - - /* setup color attachments for the framebuffer */ - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - GLenum att[SG_MAX_COLOR_ATTACHMENTS] = { - GL_COLOR_ATTACHMENT0, - GL_COLOR_ATTACHMENT1, - GL_COLOR_ATTACHMENT2, - GL_COLOR_ATTACHMENT3 - }; - glDrawBuffers(pass->cmn.num_color_atts, att); - } - #endif - - /* create MSAA resolve framebuffers if necessary */ - if (is_msaa) { - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - _sg_gl_attachment_t* gl_att = &pass->gl.color_atts[i]; - _sg_pass_attachment_t* cmn_att = &pass->cmn.color_atts[i]; - if (gl_att->image) { - SOKOL_ASSERT(0 == gl_att->gl_msaa_resolve_buffer); - glGenFramebuffers(1, &gl_att->gl_msaa_resolve_buffer); - glBindFramebuffer(GL_FRAMEBUFFER, gl_att->gl_msaa_resolve_buffer); - const GLuint gl_tex = gl_att->image->gl.tex[0]; - SOKOL_ASSERT(gl_tex); - switch (gl_att->image->cmn.type) { - case SG_IMAGETYPE_2D: - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, gl_tex, cmn_att->mip_level); - break; - case SG_IMAGETYPE_CUBE: - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - _sg_gl_cubeface_target(cmn_att->slice), gl_tex, cmn_att->mip_level); - break; - default: - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, gl_tex, cmn_att->mip_level, cmn_att->slice); - } - #endif - break; - } - /* check if framebuffer is complete */ - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - SOKOL_LOG("Framebuffer completeness check failed (msaa resolve buffer)!\n"); - return SG_RESOURCESTATE_FAILED; - } - /* setup color attachments for the framebuffer */ - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - const GLenum gl_draw_bufs = GL_COLOR_ATTACHMENT0; - glDrawBuffers(1, &gl_draw_bufs); - } - #endif - } - } - } - - /* restore original framebuffer binding */ - glBindFramebuffer(GL_FRAMEBUFFER, gl_orig_fb); - _SG_GL_CHECK_ERROR(); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_gl_destroy_pass(_sg_pass_t* pass) { - SOKOL_ASSERT(pass); - SOKOL_ASSERT(pass != _sg.gl.cur_pass); - _SG_GL_CHECK_ERROR(); - if (0 != pass->gl.fb) { - glDeleteFramebuffers(1, &pass->gl.fb); - } - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (pass->gl.color_atts[i].gl_msaa_resolve_buffer) { - glDeleteFramebuffers(1, &pass->gl.color_atts[i].gl_msaa_resolve_buffer); - } - } - if (pass->gl.ds_att.gl_msaa_resolve_buffer) { - glDeleteFramebuffers(1, &pass->gl.ds_att.gl_msaa_resolve_buffer); - } - _SG_GL_CHECK_ERROR(); -} - -_SOKOL_PRIVATE _sg_image_t* _sg_gl_pass_color_image(const _sg_pass_t* pass, int index) { - SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); - /* NOTE: may return null */ - return pass->gl.color_atts[index].image; -} - -_SOKOL_PRIVATE _sg_image_t* _sg_gl_pass_ds_image(const _sg_pass_t* pass) { - /* NOTE: may return null */ - SOKOL_ASSERT(pass); - return pass->gl.ds_att.image; -} - -_SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { - /* FIXME: what if a texture used as render target is still bound, should we - unbind all currently bound textures in begin pass? */ - SOKOL_ASSERT(action); - SOKOL_ASSERT(!_sg.gl.in_pass); - _SG_GL_CHECK_ERROR(); - _sg.gl.in_pass = true; - _sg.gl.cur_pass = pass; /* can be 0 */ - if (pass) { - _sg.gl.cur_pass_id.id = pass->slot.id; - } - else { - _sg.gl.cur_pass_id.id = SG_INVALID_ID; - } - _sg.gl.cur_pass_width = w; - _sg.gl.cur_pass_height = h; - - /* number of color attachments */ - const int num_color_atts = pass ? pass->cmn.num_color_atts : 1; - - /* bind the render pass framebuffer */ - if (pass) { - /* offscreen pass */ - SOKOL_ASSERT(pass->gl.fb); - glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); - } - else { - /* default pass */ - SOKOL_ASSERT(_sg.gl.cur_context); - glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer); - } - glViewport(0, 0, w, h); - glScissor(0, 0, w, h); - - /* clear color and depth-stencil attachments if needed */ - bool clear_color = false; - for (int i = 0; i < num_color_atts; i++) { - if (SG_ACTION_CLEAR == action->colors[i].action) { - clear_color = true; - break; - } - } - const bool clear_depth = (action->depth.action == SG_ACTION_CLEAR); - const bool clear_stencil = (action->stencil.action == SG_ACTION_CLEAR); - - bool need_pip_cache_flush = false; - if (clear_color) { - bool need_color_mask_flush = false; - // NOTE: not a bug to iterate over all possible color attachments - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (SG_COLORMASK_RGBA != _sg.gl.cache.color_write_mask[i]) { - need_pip_cache_flush = true; - need_color_mask_flush = true; - _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; - } - } - if (need_color_mask_flush) { - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - } - } - if (clear_depth) { - if (!_sg.gl.cache.depth.write_enabled) { - need_pip_cache_flush = true; - _sg.gl.cache.depth.write_enabled = true; - glDepthMask(GL_TRUE); - } - if (_sg.gl.cache.depth.compare != SG_COMPAREFUNC_ALWAYS) { - need_pip_cache_flush = true; - _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS; - glDepthFunc(GL_ALWAYS); - } - } - if (clear_stencil) { - if (_sg.gl.cache.stencil.write_mask != 0xFF) { - need_pip_cache_flush = true; - _sg.gl.cache.stencil.write_mask = 0xFF; - glStencilMask(0xFF); - } - } - if (need_pip_cache_flush) { - /* we messed with the state cache directly, need to clear cached - pipeline to force re-evaluation in next sg_apply_pipeline() */ - _sg.gl.cache.cur_pipeline = 0; - _sg.gl.cache.cur_pipeline_id.id = SG_INVALID_ID; - } - bool use_mrt_clear = (0 != pass); - #if defined(SOKOL_GLES2) - use_mrt_clear = false; - #else - if (_sg.gl.gles2) { - use_mrt_clear = false; - } - #endif - if (!use_mrt_clear) { - GLbitfield clear_mask = 0; - if (clear_color) { - clear_mask |= GL_COLOR_BUFFER_BIT; - const sg_color c = action->colors[0].value; - glClearColor(c.r, c.g, c.b, c.a); - } - if (clear_depth) { - clear_mask |= GL_DEPTH_BUFFER_BIT; - #ifdef SOKOL_GLCORE33 - glClearDepth(action->depth.value); - #else - glClearDepthf(action->depth.value); - #endif - } - if (clear_stencil) { - clear_mask |= GL_STENCIL_BUFFER_BIT; - glClearStencil(action->stencil.value); - } - if (0 != clear_mask) { - glClear(clear_mask); - } - } - #if !defined SOKOL_GLES2 - else { - SOKOL_ASSERT(pass); - for (int i = 0; i < num_color_atts; i++) { - if (action->colors[i].action == SG_ACTION_CLEAR) { - glClearBufferfv(GL_COLOR, i, &action->colors[i].value.r); - } - } - if (pass->gl.ds_att.image) { - if (clear_depth && clear_stencil) { - glClearBufferfi(GL_DEPTH_STENCIL, 0, action->depth.value, action->stencil.value); - } - else if (clear_depth) { - glClearBufferfv(GL_DEPTH, 0, &action->depth.value); - } - else if (clear_stencil) { - GLint val = (GLint) action->stencil.value; - glClearBufferiv(GL_STENCIL, 0, &val); - } - } - } - #endif - _SG_GL_CHECK_ERROR(); -} - -_SOKOL_PRIVATE void _sg_gl_end_pass(void) { - SOKOL_ASSERT(_sg.gl.in_pass); - _SG_GL_CHECK_ERROR(); - - /* if this was an offscreen pass, and MSAA rendering was used, need - to resolve into the pass images */ - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2 && _sg.gl.cur_pass) { - /* check if the pass object is still valid */ - const _sg_pass_t* pass = _sg.gl.cur_pass; - SOKOL_ASSERT(pass->slot.id == _sg.gl.cur_pass_id.id); - bool is_msaa = (0 != _sg.gl.cur_pass->gl.color_atts[0].gl_msaa_resolve_buffer); - if (is_msaa) { - SOKOL_ASSERT(pass->gl.fb); - glBindFramebuffer(GL_READ_FRAMEBUFFER, pass->gl.fb); - SOKOL_ASSERT(pass->gl.color_atts[0].image); - const int w = pass->gl.color_atts[0].image->cmn.width; - const int h = pass->gl.color_atts[0].image->cmn.height; - for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { - const _sg_gl_attachment_t* gl_att = &pass->gl.color_atts[att_index]; - if (gl_att->image) { - SOKOL_ASSERT(gl_att->gl_msaa_resolve_buffer); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gl_att->gl_msaa_resolve_buffer); - glReadBuffer((GLenum)(GL_COLOR_ATTACHMENT0 + att_index)); - glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); - } - else { - break; - } - } - } - } - #endif - _sg.gl.cur_pass = 0; - _sg.gl.cur_pass_id.id = SG_INVALID_ID; - _sg.gl.cur_pass_width = 0; - _sg.gl.cur_pass_height = 0; - - SOKOL_ASSERT(_sg.gl.cur_context); - glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer); - _sg.gl.in_pass = false; - _SG_GL_CHECK_ERROR(); -} - -_SOKOL_PRIVATE void _sg_gl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { - SOKOL_ASSERT(_sg.gl.in_pass); - y = origin_top_left ? (_sg.gl.cur_pass_height - (y+h)) : y; - glViewport(x, y, w, h); -} - -_SOKOL_PRIVATE void _sg_gl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { - SOKOL_ASSERT(_sg.gl.in_pass); - y = origin_top_left ? (_sg.gl.cur_pass_height - (y+h)) : y; - glScissor(x, y, w, h); -} - -_SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { - SOKOL_ASSERT(pip); - SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); - _SG_GL_CHECK_ERROR(); - if ((_sg.gl.cache.cur_pipeline != pip) || (_sg.gl.cache.cur_pipeline_id.id != pip->slot.id)) { - _sg.gl.cache.cur_pipeline = pip; - _sg.gl.cache.cur_pipeline_id.id = pip->slot.id; - _sg.gl.cache.cur_primitive_type = _sg_gl_primitive_type(pip->gl.primitive_type); - _sg.gl.cache.cur_index_type = _sg_gl_index_type(pip->cmn.index_type); - - /* update depth state */ - { - const sg_depth_state* state_ds = &pip->gl.depth; - sg_depth_state* cache_ds = &_sg.gl.cache.depth; - if (state_ds->compare != cache_ds->compare) { - cache_ds->compare = state_ds->compare; - glDepthFunc(_sg_gl_compare_func(state_ds->compare)); - } - if (state_ds->write_enabled != cache_ds->write_enabled) { - cache_ds->write_enabled = state_ds->write_enabled; - glDepthMask(state_ds->write_enabled); - } - if (!_sg_fequal(state_ds->bias, cache_ds->bias, 0.000001f) || - !_sg_fequal(state_ds->bias_slope_scale, cache_ds->bias_slope_scale, 0.000001f)) - { - /* according to ANGLE's D3D11 backend: - D3D11 SlopeScaledDepthBias ==> GL polygonOffsetFactor - D3D11 DepthBias ==> GL polygonOffsetUnits - DepthBiasClamp has no meaning on GL - */ - cache_ds->bias = state_ds->bias; - cache_ds->bias_slope_scale = state_ds->bias_slope_scale; - glPolygonOffset(state_ds->bias_slope_scale, state_ds->bias); - bool po_enabled = true; - if (_sg_fequal(state_ds->bias, 0.0f, 0.000001f) && - _sg_fequal(state_ds->bias_slope_scale, 0.0f, 0.000001f)) - { - po_enabled = false; - } - if (po_enabled != _sg.gl.cache.polygon_offset_enabled) { - _sg.gl.cache.polygon_offset_enabled = po_enabled; - if (po_enabled) { - glEnable(GL_POLYGON_OFFSET_FILL); - } - else { - glDisable(GL_POLYGON_OFFSET_FILL); - } - } - } - } - - /* update stencil state */ - { - const sg_stencil_state* state_ss = &pip->gl.stencil; - sg_stencil_state* cache_ss = &_sg.gl.cache.stencil; - if (state_ss->enabled != cache_ss->enabled) { - cache_ss->enabled = state_ss->enabled; - if (state_ss->enabled) { - glEnable(GL_STENCIL_TEST); - } - else { - glDisable(GL_STENCIL_TEST); - } - } - if (state_ss->write_mask != cache_ss->write_mask) { - cache_ss->write_mask = state_ss->write_mask; - glStencilMask(state_ss->write_mask); - } - for (int i = 0; i < 2; i++) { - const sg_stencil_face_state* state_sfs = (i==0)? &state_ss->front : &state_ss->back; - sg_stencil_face_state* cache_sfs = (i==0)? &cache_ss->front : &cache_ss->back; - GLenum gl_face = (i==0)? GL_FRONT : GL_BACK; - if ((state_sfs->compare != cache_sfs->compare) || - (state_ss->read_mask != cache_ss->read_mask) || - (state_ss->ref != cache_ss->ref)) - { - cache_sfs->compare = state_sfs->compare; - glStencilFuncSeparate(gl_face, - _sg_gl_compare_func(state_sfs->compare), - state_ss->ref, - state_ss->read_mask); - } - if ((state_sfs->fail_op != cache_sfs->fail_op) || - (state_sfs->depth_fail_op != cache_sfs->depth_fail_op) || - (state_sfs->pass_op != cache_sfs->pass_op)) - { - cache_sfs->fail_op = state_sfs->fail_op; - cache_sfs->depth_fail_op = state_sfs->depth_fail_op; - cache_sfs->pass_op = state_sfs->pass_op; - glStencilOpSeparate(gl_face, - _sg_gl_stencil_op(state_sfs->fail_op), - _sg_gl_stencil_op(state_sfs->depth_fail_op), - _sg_gl_stencil_op(state_sfs->pass_op)); - } - } - cache_ss->read_mask = state_ss->read_mask; - cache_ss->ref = state_ss->ref; - } - - /* update blend state - FIXME: separate blend state per color attachment not support, needs GL4 - */ - { - const sg_blend_state* state_bs = &pip->gl.blend; - sg_blend_state* cache_bs = &_sg.gl.cache.blend; - if (state_bs->enabled != cache_bs->enabled) { - cache_bs->enabled = state_bs->enabled; - if (state_bs->enabled) { - glEnable(GL_BLEND); - } - else { - glDisable(GL_BLEND); - } - } - if ((state_bs->src_factor_rgb != cache_bs->src_factor_rgb) || - (state_bs->dst_factor_rgb != cache_bs->dst_factor_rgb) || - (state_bs->src_factor_alpha != cache_bs->src_factor_alpha) || - (state_bs->dst_factor_alpha != cache_bs->dst_factor_alpha)) - { - cache_bs->src_factor_rgb = state_bs->src_factor_rgb; - cache_bs->dst_factor_rgb = state_bs->dst_factor_rgb; - cache_bs->src_factor_alpha = state_bs->src_factor_alpha; - cache_bs->dst_factor_alpha = state_bs->dst_factor_alpha; - glBlendFuncSeparate(_sg_gl_blend_factor(state_bs->src_factor_rgb), - _sg_gl_blend_factor(state_bs->dst_factor_rgb), - _sg_gl_blend_factor(state_bs->src_factor_alpha), - _sg_gl_blend_factor(state_bs->dst_factor_alpha)); - } - if ((state_bs->op_rgb != cache_bs->op_rgb) || (state_bs->op_alpha != cache_bs->op_alpha)) { - cache_bs->op_rgb = state_bs->op_rgb; - cache_bs->op_alpha = state_bs->op_alpha; - glBlendEquationSeparate(_sg_gl_blend_op(state_bs->op_rgb), _sg_gl_blend_op(state_bs->op_alpha)); - } - } - - /* standalone state */ - for (GLuint i = 0; i < (GLuint)pip->cmn.color_attachment_count; i++) { - if (pip->gl.color_write_mask[i] != _sg.gl.cache.color_write_mask[i]) { - const sg_color_mask cm = pip->gl.color_write_mask[i]; - _sg.gl.cache.color_write_mask[i] = cm; - #ifdef SOKOL_GLCORE33 - glColorMaski(i, - (cm & SG_COLORMASK_R) != 0, - (cm & SG_COLORMASK_G) != 0, - (cm & SG_COLORMASK_B) != 0, - (cm & SG_COLORMASK_A) != 0); - #else - if (0 == i) { - glColorMask((cm & SG_COLORMASK_R) != 0, - (cm & SG_COLORMASK_G) != 0, - (cm & SG_COLORMASK_B) != 0, - (cm & SG_COLORMASK_A) != 0); - } - #endif - } - } - - if (!_sg_fequal(pip->cmn.blend_color.r, _sg.gl.cache.blend_color.r, 0.0001f) || - !_sg_fequal(pip->cmn.blend_color.g, _sg.gl.cache.blend_color.g, 0.0001f) || - !_sg_fequal(pip->cmn.blend_color.b, _sg.gl.cache.blend_color.b, 0.0001f) || - !_sg_fequal(pip->cmn.blend_color.a, _sg.gl.cache.blend_color.a, 0.0001f)) - { - sg_color c = pip->cmn.blend_color; - _sg.gl.cache.blend_color = c; - glBlendColor(c.r, c.g, c.b, c.a); - } - if (pip->gl.cull_mode != _sg.gl.cache.cull_mode) { - _sg.gl.cache.cull_mode = pip->gl.cull_mode; - if (SG_CULLMODE_NONE == pip->gl.cull_mode) { - glDisable(GL_CULL_FACE); - } - else { - glEnable(GL_CULL_FACE); - GLenum gl_mode = (SG_CULLMODE_FRONT == pip->gl.cull_mode) ? GL_FRONT : GL_BACK; - glCullFace(gl_mode); - } - } - if (pip->gl.face_winding != _sg.gl.cache.face_winding) { - _sg.gl.cache.face_winding = pip->gl.face_winding; - GLenum gl_winding = (SG_FACEWINDING_CW == pip->gl.face_winding) ? GL_CW : GL_CCW; - glFrontFace(gl_winding); - } - if (pip->gl.alpha_to_coverage_enabled != _sg.gl.cache.alpha_to_coverage_enabled) { - _sg.gl.cache.alpha_to_coverage_enabled = pip->gl.alpha_to_coverage_enabled; - if (pip->gl.alpha_to_coverage_enabled) { - glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); - } - else { - glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); - } - } - #ifdef SOKOL_GLCORE33 - if (pip->gl.sample_count != _sg.gl.cache.sample_count) { - _sg.gl.cache.sample_count = pip->gl.sample_count; - if (pip->gl.sample_count > 1) { - glEnable(GL_MULTISAMPLE); - } - else { - glDisable(GL_MULTISAMPLE); - } - } - #endif - - /* bind shader program */ - if (pip->shader->gl.prog != _sg.gl.cache.prog) { - _sg.gl.cache.prog = pip->shader->gl.prog; - glUseProgram(pip->shader->gl.prog); - } - } - _SG_GL_CHECK_ERROR(); -} - -_SOKOL_PRIVATE void _sg_gl_apply_bindings( - _sg_pipeline_t* pip, - _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, - _sg_buffer_t* ib, int ib_offset, - _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs) -{ - SOKOL_ASSERT(pip); - _SOKOL_UNUSED(num_fs_imgs); - _SOKOL_UNUSED(num_vs_imgs); - _SOKOL_UNUSED(num_vbs); - _SG_GL_CHECK_ERROR(); - - /* bind textures */ - _SG_GL_CHECK_ERROR(); - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index]; - const _sg_gl_shader_stage_t* gl_stage = &pip->shader->gl.stage[stage_index]; - _sg_image_t** imgs = (stage_index == SG_SHADERSTAGE_VS)? vs_imgs : fs_imgs; - SOKOL_ASSERT(((stage_index == SG_SHADERSTAGE_VS)? num_vs_imgs : num_fs_imgs) == stage->num_images); - for (int img_index = 0; img_index < stage->num_images; img_index++) { - const _sg_gl_shader_image_t* gl_shd_img = &gl_stage->images[img_index]; - if (gl_shd_img->gl_tex_slot != -1) { - _sg_image_t* img = imgs[img_index]; - const GLuint gl_tex = img->gl.tex[img->cmn.active_slot]; - SOKOL_ASSERT(img && img->gl.target); - SOKOL_ASSERT((gl_shd_img->gl_tex_slot != -1) && gl_tex); - _sg_gl_cache_bind_texture(gl_shd_img->gl_tex_slot, img->gl.target, gl_tex); - } - } - } - _SG_GL_CHECK_ERROR(); - - /* index buffer (can be 0) */ - const GLuint gl_ib = ib ? ib->gl.buf[ib->cmn.active_slot] : 0; - _sg_gl_cache_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, gl_ib); - _sg.gl.cache.cur_ib_offset = ib_offset; - - /* vertex attributes */ - for (GLuint attr_index = 0; attr_index < (GLuint)_sg.limits.max_vertex_attrs; attr_index++) { - _sg_gl_attr_t* attr = &pip->gl.attrs[attr_index]; - _sg_gl_cache_attr_t* cache_attr = &_sg.gl.cache.attrs[attr_index]; - bool cache_attr_dirty = false; - int vb_offset = 0; - GLuint gl_vb = 0; - if (attr->vb_index >= 0) { - /* attribute is enabled */ - SOKOL_ASSERT(attr->vb_index < num_vbs); - _sg_buffer_t* vb = vbs[attr->vb_index]; - SOKOL_ASSERT(vb); - gl_vb = vb->gl.buf[vb->cmn.active_slot]; - vb_offset = vb_offsets[attr->vb_index] + attr->offset; - if ((gl_vb != cache_attr->gl_vbuf) || - (attr->size != cache_attr->gl_attr.size) || - (attr->type != cache_attr->gl_attr.type) || - (attr->normalized != cache_attr->gl_attr.normalized) || - (attr->stride != cache_attr->gl_attr.stride) || - (vb_offset != cache_attr->gl_attr.offset) || - (cache_attr->gl_attr.divisor != attr->divisor)) - { - _sg_gl_cache_bind_buffer(GL_ARRAY_BUFFER, gl_vb); - glVertexAttribPointer(attr_index, attr->size, attr->type, - attr->normalized, attr->stride, - (const GLvoid*)(GLintptr)vb_offset); - #if defined(_SOKOL_GL_INSTANCING_ENABLED) - if (_sg.features.instancing) { - glVertexAttribDivisor(attr_index, (GLuint)attr->divisor); - } - #endif - cache_attr_dirty = true; - } - if (cache_attr->gl_attr.vb_index == -1) { - glEnableVertexAttribArray(attr_index); - cache_attr_dirty = true; - } - } - else { - /* attribute is disabled */ - if (cache_attr->gl_attr.vb_index != -1) { - glDisableVertexAttribArray(attr_index); - cache_attr_dirty = true; - } - } - if (cache_attr_dirty) { - cache_attr->gl_attr = *attr; - cache_attr->gl_attr.offset = vb_offset; - cache_attr->gl_vbuf = gl_vb; - } - } - _SG_GL_CHECK_ERROR(); -} - -_SOKOL_PRIVATE void _sg_gl_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { - SOKOL_ASSERT(_sg.gl.cache.cur_pipeline); - SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->slot.id == _sg.gl.cache.cur_pipeline_id.id); - SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->slot.id == _sg.gl.cache.cur_pipeline->cmn.shader_id.id); - SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks > ub_index); - SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size == data->size); - const _sg_gl_shader_stage_t* gl_stage = &_sg.gl.cache.cur_pipeline->shader->gl.stage[stage_index]; - const _sg_gl_uniform_block_t* gl_ub = &gl_stage->uniform_blocks[ub_index]; - for (int u_index = 0; u_index < gl_ub->num_uniforms; u_index++) { - const _sg_gl_uniform_t* u = &gl_ub->uniforms[u_index]; - SOKOL_ASSERT(u->type != SG_UNIFORMTYPE_INVALID); - if (u->gl_loc == -1) { - continue; - } - GLfloat* fptr = (GLfloat*) (((uint8_t*)data->ptr) + u->offset); - GLint* iptr = (GLint*) (((uint8_t*)data->ptr) + u->offset); - switch (u->type) { - case SG_UNIFORMTYPE_INVALID: - break; - case SG_UNIFORMTYPE_FLOAT: - glUniform1fv(u->gl_loc, u->count, fptr); - break; - case SG_UNIFORMTYPE_FLOAT2: - glUniform2fv(u->gl_loc, u->count, fptr); - break; - case SG_UNIFORMTYPE_FLOAT3: - glUniform3fv(u->gl_loc, u->count, fptr); - break; - case SG_UNIFORMTYPE_FLOAT4: - glUniform4fv(u->gl_loc, u->count, fptr); - break; - case SG_UNIFORMTYPE_INT: - glUniform1iv(u->gl_loc, u->count, iptr); - break; - case SG_UNIFORMTYPE_INT2: - glUniform2iv(u->gl_loc, u->count, iptr); - break; - case SG_UNIFORMTYPE_INT3: - glUniform3iv(u->gl_loc, u->count, iptr); - break; - case SG_UNIFORMTYPE_INT4: - glUniform4iv(u->gl_loc, u->count, iptr); - break; - case SG_UNIFORMTYPE_MAT4: - glUniformMatrix4fv(u->gl_loc, u->count, GL_FALSE, fptr); - break; - default: - SOKOL_UNREACHABLE; - break; - } - } -} - -_SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_instances) { - SOKOL_ASSERT(_sg.gl.cache.cur_pipeline); - const GLenum i_type = _sg.gl.cache.cur_index_type; - const GLenum p_type = _sg.gl.cache.cur_primitive_type; - if (0 != i_type) { - /* indexed rendering */ - const int i_size = (i_type == GL_UNSIGNED_SHORT) ? 2 : 4; - const int ib_offset = _sg.gl.cache.cur_ib_offset; - const GLvoid* indices = (const GLvoid*)(GLintptr)(base_element*i_size+ib_offset); - if (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw) { - if (_sg.features.instancing) { - glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances); - } - } - else { - glDrawElements(p_type, num_elements, i_type, indices); - } - } - else { - /* non-indexed rendering */ - if (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw) { - if (_sg.features.instancing) { - glDrawArraysInstanced(p_type, base_element, num_elements, num_instances); - } - } - else { - glDrawArrays(p_type, base_element, num_elements); - } - } -} - -_SOKOL_PRIVATE void _sg_gl_commit(void) { - SOKOL_ASSERT(!_sg.gl.in_pass); - /* "soft" clear bindings (only those that are actually bound) */ - _sg_gl_cache_clear_buffer_bindings(false); - _sg_gl_cache_clear_texture_bindings(false); -} - -_SOKOL_PRIVATE void _sg_gl_update_buffer(_sg_buffer_t* buf, const sg_range* data) { - SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); - /* only one update per buffer per frame allowed */ - if (++buf->cmn.active_slot >= buf->cmn.num_slots) { - buf->cmn.active_slot = 0; - } - GLenum gl_tgt = _sg_gl_buffer_target(buf->cmn.type); - SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); - GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot]; - SOKOL_ASSERT(gl_buf); - _SG_GL_CHECK_ERROR(); - _sg_gl_cache_store_buffer_binding(gl_tgt); - _sg_gl_cache_bind_buffer(gl_tgt, gl_buf); - glBufferSubData(gl_tgt, 0, (GLsizeiptr)data->size, data->ptr); - _sg_gl_cache_restore_buffer_binding(gl_tgt); - _SG_GL_CHECK_ERROR(); -} - -_SOKOL_PRIVATE int _sg_gl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { - SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); - if (new_frame) { - if (++buf->cmn.active_slot >= buf->cmn.num_slots) { - buf->cmn.active_slot = 0; - } - } - GLenum gl_tgt = _sg_gl_buffer_target(buf->cmn.type); - SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); - GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot]; - SOKOL_ASSERT(gl_buf); - _SG_GL_CHECK_ERROR(); - _sg_gl_cache_store_buffer_binding(gl_tgt); - _sg_gl_cache_bind_buffer(gl_tgt, gl_buf); - glBufferSubData(gl_tgt, buf->cmn.append_pos, (GLsizeiptr)data->size, data->ptr); - _sg_gl_cache_restore_buffer_binding(gl_tgt); - _SG_GL_CHECK_ERROR(); - /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */ - return _sg_roundup((int)data->size, 4); -} - -_SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* data) { - SOKOL_ASSERT(img && data); - /* only one update per image per frame allowed */ - if (++img->cmn.active_slot >= img->cmn.num_slots) { - img->cmn.active_slot = 0; - } - SOKOL_ASSERT(img->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); - SOKOL_ASSERT(0 != img->gl.tex[img->cmn.active_slot]); - _sg_gl_cache_store_texture_binding(0); - _sg_gl_cache_bind_texture(0, img->gl.target, img->gl.tex[img->cmn.active_slot]); - const GLenum gl_img_format = _sg_gl_teximage_format(img->cmn.pixel_format); - const GLenum gl_img_type = _sg_gl_teximage_type(img->cmn.pixel_format); - const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; - const int num_mips = img->cmn.num_mipmaps; - for (int face_index = 0; face_index < num_faces; face_index++) { - for (int mip_index = 0; mip_index < num_mips; mip_index++) { - GLenum gl_img_target = img->gl.target; - if (SG_IMAGETYPE_CUBE == img->cmn.type) { - gl_img_target = _sg_gl_cubeface_target(face_index); - } - const GLvoid* data_ptr = data->subimage[face_index][mip_index].ptr; - int mip_width = img->cmn.width >> mip_index; - if (mip_width == 0) { - mip_width = 1; - } - int mip_height = img->cmn.height >> mip_index; - if (mip_height == 0) { - mip_height = 1; - } - if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { - glTexSubImage2D(gl_img_target, mip_index, - 0, 0, - mip_width, mip_height, - gl_img_format, gl_img_type, - data_ptr); - } - #if !defined(SOKOL_GLES2) - else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type))) { - int mip_depth = img->cmn.num_slices >> mip_index; - if (mip_depth == 0) { - mip_depth = 1; - } - glTexSubImage3D(gl_img_target, mip_index, - 0, 0, 0, - mip_width, mip_height, mip_depth, - gl_img_format, gl_img_type, - data_ptr); - - } - #endif - } - } - _sg_gl_cache_restore_texture_binding(0); -} - -/*== D3D11 BACKEND IMPLEMENTATION ============================================*/ -#elif defined(SOKOL_D3D11) - -#if defined(__cplusplus) -#define _sg_d3d11_AddRef(self) (self)->AddRef() -#else -#define _sg_d3d11_AddRef(self) (self)->lpVtbl->AddRef(self) -#endif - -#if defined(__cplusplus) -#define _sg_d3d11_Release(self) (self)->Release() -#else -#define _sg_d3d11_Release(self) (self)->lpVtbl->Release(self) -#endif - -/*-- D3D11 C/C++ wrappers ----------------------------------------------------*/ -static inline HRESULT _sg_d3d11_CheckFormatSupport(ID3D11Device* self, DXGI_FORMAT Format, UINT* pFormatSupport) { - #if defined(__cplusplus) - return self->CheckFormatSupport(Format, pFormatSupport); - #else - return self->lpVtbl->CheckFormatSupport(self, Format, pFormatSupport); - #endif -} - -static inline void _sg_d3d11_OMSetRenderTargets(ID3D11DeviceContext* self, UINT NumViews, ID3D11RenderTargetView* const* ppRenderTargetViews, ID3D11DepthStencilView *pDepthStencilView) { - #if defined(__cplusplus) - self->OMSetRenderTargets(NumViews, ppRenderTargetViews, pDepthStencilView); - #else - self->lpVtbl->OMSetRenderTargets(self, NumViews, ppRenderTargetViews, pDepthStencilView); - #endif -} - -static inline void _sg_d3d11_RSSetState(ID3D11DeviceContext* self, ID3D11RasterizerState* pRasterizerState) { - #if defined(__cplusplus) - self->RSSetState(pRasterizerState); - #else - self->lpVtbl->RSSetState(self, pRasterizerState); - #endif -} - -static inline void _sg_d3d11_OMSetDepthStencilState(ID3D11DeviceContext* self, ID3D11DepthStencilState* pDepthStencilState, UINT StencilRef) { - #if defined(__cplusplus) - self->OMSetDepthStencilState(pDepthStencilState, StencilRef); - #else - self->lpVtbl->OMSetDepthStencilState(self, pDepthStencilState, StencilRef); - #endif -} - -static inline void _sg_d3d11_OMSetBlendState(ID3D11DeviceContext* self, ID3D11BlendState* pBlendState, const FLOAT BlendFactor[4], UINT SampleMask) { - #if defined(__cplusplus) - self->OMSetBlendState(pBlendState, BlendFactor, SampleMask); - #else - self->lpVtbl->OMSetBlendState(self, pBlendState, BlendFactor, SampleMask); - #endif -} - -static inline void _sg_d3d11_IASetVertexBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppVertexBuffers, const UINT* pStrides, const UINT* pOffsets) { - #if defined(__cplusplus) - self->IASetVertexBuffers(StartSlot, NumBuffers, ppVertexBuffers, pStrides, pOffsets); - #else - self->lpVtbl->IASetVertexBuffers(self, StartSlot, NumBuffers, ppVertexBuffers, pStrides, pOffsets); - #endif -} - -static inline void _sg_d3d11_IASetIndexBuffer(ID3D11DeviceContext* self, ID3D11Buffer* pIndexBuffer, DXGI_FORMAT Format, UINT Offset) { - #if defined(__cplusplus) - self->IASetIndexBuffer(pIndexBuffer, Format, Offset); - #else - self->lpVtbl->IASetIndexBuffer(self, pIndexBuffer, Format, Offset); - #endif -} - -static inline void _sg_d3d11_IASetInputLayout(ID3D11DeviceContext* self, ID3D11InputLayout* pInputLayout) { - #if defined(__cplusplus) - self->IASetInputLayout(pInputLayout); - #else - self->lpVtbl->IASetInputLayout(self, pInputLayout); - #endif -} - -static inline void _sg_d3d11_VSSetShader(ID3D11DeviceContext* self, ID3D11VertexShader* pVertexShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { - #if defined(__cplusplus) - self->VSSetShader(pVertexShader, ppClassInstances, NumClassInstances); - #else - self->lpVtbl->VSSetShader(self, pVertexShader, ppClassInstances, NumClassInstances); - #endif -} - -static inline void _sg_d3d11_PSSetShader(ID3D11DeviceContext* self, ID3D11PixelShader* pPixelShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { - #if defined(__cplusplus) - self->PSSetShader(pPixelShader, ppClassInstances, NumClassInstances); - #else - self->lpVtbl->PSSetShader(self, pPixelShader, ppClassInstances, NumClassInstances); - #endif -} - -static inline void _sg_d3d11_VSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { - #if defined(__cplusplus) - self->VSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers); - #else - self->lpVtbl->VSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers); - #endif -} - -static inline void _sg_d3d11_PSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { - #if defined(__cplusplus) - self->PSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers); - #else - self->lpVtbl->PSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers); - #endif -} - -static inline void _sg_d3d11_VSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { - #if defined(__cplusplus) - self->VSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews); - #else - self->lpVtbl->VSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews); - #endif -} - -static inline void _sg_d3d11_PSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { - #if defined(__cplusplus) - self->PSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews); - #else - self->lpVtbl->PSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews); - #endif -} - -static inline void _sg_d3d11_VSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { - #if defined(__cplusplus) - self->VSSetSamplers(StartSlot, NumSamplers, ppSamplers); - #else - self->lpVtbl->VSSetSamplers(self, StartSlot, NumSamplers, ppSamplers); - #endif -} - -static inline void _sg_d3d11_PSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { - #if defined(__cplusplus) - self->PSSetSamplers(StartSlot, NumSamplers, ppSamplers); - #else - self->lpVtbl->PSSetSamplers(self, StartSlot, NumSamplers, ppSamplers); - #endif -} - -static inline HRESULT _sg_d3d11_CreateBuffer(ID3D11Device* self, const D3D11_BUFFER_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Buffer** ppBuffer) { - #if defined(__cplusplus) - return self->CreateBuffer(pDesc, pInitialData, ppBuffer); - #else - return self->lpVtbl->CreateBuffer(self, pDesc, pInitialData, ppBuffer); - #endif -} - -static inline HRESULT _sg_d3d11_CreateTexture2D(ID3D11Device* self, const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) { - #if defined(__cplusplus) - return self->CreateTexture2D(pDesc, pInitialData, ppTexture2D); - #else - return self->lpVtbl->CreateTexture2D(self, pDesc, pInitialData, ppTexture2D); - #endif -} - -static inline HRESULT _sg_d3d11_CreateShaderResourceView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRView) { - #if defined(__cplusplus) - return self->CreateShaderResourceView(pResource, pDesc, ppSRView); - #else - return self->lpVtbl->CreateShaderResourceView(self, pResource, pDesc, ppSRView); - #endif -} - -static inline void _sg_d3d11_GetResource(ID3D11View* self, ID3D11Resource** ppResource) { - #if defined(__cplusplus) - self->GetResource(ppResource); - #else - self->lpVtbl->GetResource(self, ppResource); - #endif -} - -static inline HRESULT _sg_d3d11_CreateTexture3D(ID3D11Device* self, const D3D11_TEXTURE3D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture3D** ppTexture3D) { - #if defined(__cplusplus) - return self->CreateTexture3D(pDesc, pInitialData, ppTexture3D); - #else - return self->lpVtbl->CreateTexture3D(self, pDesc, pInitialData, ppTexture3D); - #endif -} - -static inline HRESULT _sg_d3d11_CreateSamplerState(ID3D11Device* self, const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState) { - #if defined(__cplusplus) - return self->CreateSamplerState(pSamplerDesc, ppSamplerState); - #else - return self->lpVtbl->CreateSamplerState(self, pSamplerDesc, ppSamplerState); - #endif -} - -static inline LPVOID _sg_d3d11_GetBufferPointer(ID3D10Blob* self) { - #if defined(__cplusplus) - return self->GetBufferPointer(); - #else - return self->lpVtbl->GetBufferPointer(self); - #endif -} - -static inline SIZE_T _sg_d3d11_GetBufferSize(ID3D10Blob* self) { - #if defined(__cplusplus) - return self->GetBufferSize(); - #else - return self->lpVtbl->GetBufferSize(self); - #endif -} - -static inline HRESULT _sg_d3d11_CreateVertexShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11VertexShader** ppVertexShader) { - #if defined(__cplusplus) - return self->CreateVertexShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader); - #else - return self->lpVtbl->CreateVertexShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader); - #endif -} - -static inline HRESULT _sg_d3d11_CreatePixelShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11PixelShader** ppPixelShader) { - #if defined(__cplusplus) - return self->CreatePixelShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader); - #else - return self->lpVtbl->CreatePixelShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader); - #endif -} - -static inline HRESULT _sg_d3d11_CreateInputLayout(ID3D11Device* self, const D3D11_INPUT_ELEMENT_DESC* pInputElementDescs, UINT NumElements, const void* pShaderBytecodeWithInputSignature, SIZE_T BytecodeLength, ID3D11InputLayout **ppInputLayout) { - #if defined(__cplusplus) - return self->CreateInputLayout(pInputElementDescs, NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout); - #else - return self->lpVtbl->CreateInputLayout(self, pInputElementDescs, NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout); - #endif -} - -static inline HRESULT _sg_d3d11_CreateRasterizerState(ID3D11Device* self, const D3D11_RASTERIZER_DESC* pRasterizerDesc, ID3D11RasterizerState** ppRasterizerState) { - #if defined(__cplusplus) - return self->CreateRasterizerState(pRasterizerDesc, ppRasterizerState); - #else - return self->lpVtbl->CreateRasterizerState(self, pRasterizerDesc, ppRasterizerState); - #endif -} - -static inline HRESULT _sg_d3d11_CreateDepthStencilState(ID3D11Device* self, const D3D11_DEPTH_STENCIL_DESC* pDepthStencilDesc, ID3D11DepthStencilState** ppDepthStencilState) { - #if defined(__cplusplus) - return self->CreateDepthStencilState(pDepthStencilDesc, ppDepthStencilState); - #else - return self->lpVtbl->CreateDepthStencilState(self, pDepthStencilDesc, ppDepthStencilState); - #endif -} - -static inline HRESULT _sg_d3d11_CreateBlendState(ID3D11Device* self, const D3D11_BLEND_DESC* pBlendStateDesc, ID3D11BlendState** ppBlendState) { - #if defined(__cplusplus) - return self->CreateBlendState(pBlendStateDesc, ppBlendState); - #else - return self->lpVtbl->CreateBlendState(self, pBlendStateDesc, ppBlendState); - #endif -} - -static inline HRESULT _sg_d3d11_CreateRenderTargetView(ID3D11Device* self, ID3D11Resource *pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) { - #if defined(__cplusplus) - return self->CreateRenderTargetView(pResource, pDesc, ppRTView); - #else - return self->lpVtbl->CreateRenderTargetView(self, pResource, pDesc, ppRTView); - #endif -} - -static inline HRESULT _sg_d3d11_CreateDepthStencilView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) { - #if defined(__cplusplus) - return self->CreateDepthStencilView(pResource, pDesc, ppDepthStencilView); - #else - return self->lpVtbl->CreateDepthStencilView(self, pResource, pDesc, ppDepthStencilView); - #endif -} - -static inline void _sg_d3d11_RSSetViewports(ID3D11DeviceContext* self, UINT NumViewports, const D3D11_VIEWPORT* pViewports) { - #if defined(__cplusplus) - self->RSSetViewports(NumViewports, pViewports); - #else - self->lpVtbl->RSSetViewports(self, NumViewports, pViewports); - #endif -} - -static inline void _sg_d3d11_RSSetScissorRects(ID3D11DeviceContext* self, UINT NumRects, const D3D11_RECT* pRects) { - #if defined(__cplusplus) - self->RSSetScissorRects(NumRects, pRects); - #else - self->lpVtbl->RSSetScissorRects(self, NumRects, pRects); - #endif -} - -static inline void _sg_d3d11_ClearRenderTargetView(ID3D11DeviceContext* self, ID3D11RenderTargetView* pRenderTargetView, const FLOAT ColorRGBA[4]) { - #if defined(__cplusplus) - self->ClearRenderTargetView(pRenderTargetView, ColorRGBA); - #else - self->lpVtbl->ClearRenderTargetView(self, pRenderTargetView, ColorRGBA); - #endif -} - -static inline void _sg_d3d11_ClearDepthStencilView(ID3D11DeviceContext* self, ID3D11DepthStencilView* pDepthStencilView, UINT ClearFlags, FLOAT Depth, UINT8 Stencil) { - #if defined(__cplusplus) - self->ClearDepthStencilView(pDepthStencilView, ClearFlags, Depth, Stencil); - #else - self->lpVtbl->ClearDepthStencilView(self, pDepthStencilView, ClearFlags, Depth, Stencil); - #endif -} - -static inline void _sg_d3d11_ResolveSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, ID3D11Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format) { - #if defined(__cplusplus) - self->ResolveSubresource(pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); - #else - self->lpVtbl->ResolveSubresource(self, pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); - #endif -} - -static inline void _sg_d3d11_IASetPrimitiveTopology(ID3D11DeviceContext* self, D3D11_PRIMITIVE_TOPOLOGY Topology) { - #if defined(__cplusplus) - self->IASetPrimitiveTopology(Topology); - #else - self->lpVtbl->IASetPrimitiveTopology(self, Topology); - #endif -} - -static inline void _sg_d3d11_UpdateSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch) { - #if defined(__cplusplus) - self->UpdateSubresource(pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch); - #else - self->lpVtbl->UpdateSubresource(self, pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch); - #endif -} - -static inline void _sg_d3d11_DrawIndexed(ID3D11DeviceContext* self, UINT IndexCount, UINT StartIndexLocation, INT BaseVertexLocation) { - #if defined(__cplusplus) - self->DrawIndexed(IndexCount, StartIndexLocation, BaseVertexLocation); - #else - self->lpVtbl->DrawIndexed(self, IndexCount, StartIndexLocation, BaseVertexLocation); - #endif -} - -static inline void _sg_d3d11_DrawIndexedInstanced(ID3D11DeviceContext* self, UINT IndexCountPerInstance, UINT InstanceCount, UINT StartIndexLocation, INT BaseVertexLocation, UINT StartInstanceLocation) { - #if defined(__cplusplus) - self->DrawIndexedInstanced(IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation); - #else - self->lpVtbl->DrawIndexedInstanced(self, IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation); - #endif -} - -static inline void _sg_d3d11_Draw(ID3D11DeviceContext* self, UINT VertexCount, UINT StartVertexLocation) { - #if defined(__cplusplus) - self->Draw(VertexCount, StartVertexLocation); - #else - self->lpVtbl->Draw(self, VertexCount, StartVertexLocation); - #endif -} - -static inline void _sg_d3d11_DrawInstanced(ID3D11DeviceContext* self, UINT VertexCountPerInstance, UINT InstanceCount, UINT StartVertexLocation, UINT StartInstanceLocation) { - #if defined(__cplusplus) - self->DrawInstanced(VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation); - #else - self->lpVtbl->DrawInstanced(self, VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation); - #endif -} - -static inline HRESULT _sg_d3d11_Map(ID3D11DeviceContext* self, ID3D11Resource* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource) { - #if defined(__cplusplus) - return self->Map(pResource, Subresource, MapType, MapFlags, pMappedResource); - #else - return self->lpVtbl->Map(self, pResource, Subresource, MapType, MapFlags, pMappedResource); - #endif -} - -static inline void _sg_d3d11_Unmap(ID3D11DeviceContext* self, ID3D11Resource* pResource, UINT Subresource) { - #if defined(__cplusplus) - self->Unmap(pResource, Subresource); - #else - self->lpVtbl->Unmap(self, pResource, Subresource); - #endif -} - -static inline void _sg_d3d11_ClearState(ID3D11DeviceContext* self) { - #if defined(__cplusplus) - self->ClearState(); - #else - self->lpVtbl->ClearState(self); - #endif -} - -/*-- enum translation functions ----------------------------------------------*/ -_SOKOL_PRIVATE D3D11_USAGE _sg_d3d11_usage(sg_usage usg) { - switch (usg) { - case SG_USAGE_IMMUTABLE: - return D3D11_USAGE_IMMUTABLE; - case SG_USAGE_DYNAMIC: - case SG_USAGE_STREAM: - return D3D11_USAGE_DYNAMIC; - default: - SOKOL_UNREACHABLE; - return (D3D11_USAGE) 0; - } -} - -_SOKOL_PRIVATE UINT _sg_d3d11_cpu_access_flags(sg_usage usg) { - switch (usg) { - case SG_USAGE_IMMUTABLE: - return 0; - case SG_USAGE_DYNAMIC: - case SG_USAGE_STREAM: - return D3D11_CPU_ACCESS_WRITE; - default: - SOKOL_UNREACHABLE; - return 0; - } -} - -_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_pixel_format(sg_pixel_format fmt) { - switch (fmt) { - case SG_PIXELFORMAT_R8: return DXGI_FORMAT_R8_UNORM; - case SG_PIXELFORMAT_R8SN: return DXGI_FORMAT_R8_SNORM; - case SG_PIXELFORMAT_R8UI: return DXGI_FORMAT_R8_UINT; - case SG_PIXELFORMAT_R8SI: return DXGI_FORMAT_R8_SINT; - case SG_PIXELFORMAT_R16: return DXGI_FORMAT_R16_UNORM; - case SG_PIXELFORMAT_R16SN: return DXGI_FORMAT_R16_SNORM; - case SG_PIXELFORMAT_R16UI: return DXGI_FORMAT_R16_UINT; - case SG_PIXELFORMAT_R16SI: return DXGI_FORMAT_R16_SINT; - case SG_PIXELFORMAT_R16F: return DXGI_FORMAT_R16_FLOAT; - case SG_PIXELFORMAT_RG8: return DXGI_FORMAT_R8G8_UNORM; - case SG_PIXELFORMAT_RG8SN: return DXGI_FORMAT_R8G8_SNORM; - case SG_PIXELFORMAT_RG8UI: return DXGI_FORMAT_R8G8_UINT; - case SG_PIXELFORMAT_RG8SI: return DXGI_FORMAT_R8G8_SINT; - case SG_PIXELFORMAT_R32UI: return DXGI_FORMAT_R32_UINT; - case SG_PIXELFORMAT_R32SI: return DXGI_FORMAT_R32_SINT; - case SG_PIXELFORMAT_R32F: return DXGI_FORMAT_R32_FLOAT; - case SG_PIXELFORMAT_RG16: return DXGI_FORMAT_R16G16_UNORM; - case SG_PIXELFORMAT_RG16SN: return DXGI_FORMAT_R16G16_SNORM; - case SG_PIXELFORMAT_RG16UI: return DXGI_FORMAT_R16G16_UINT; - case SG_PIXELFORMAT_RG16SI: return DXGI_FORMAT_R16G16_SINT; - case SG_PIXELFORMAT_RG16F: return DXGI_FORMAT_R16G16_FLOAT; - case SG_PIXELFORMAT_RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM; - case SG_PIXELFORMAT_RGBA8SN: return DXGI_FORMAT_R8G8B8A8_SNORM; - case SG_PIXELFORMAT_RGBA8UI: return DXGI_FORMAT_R8G8B8A8_UINT; - case SG_PIXELFORMAT_RGBA8SI: return DXGI_FORMAT_R8G8B8A8_SINT; - case SG_PIXELFORMAT_BGRA8: return DXGI_FORMAT_B8G8R8A8_UNORM; - case SG_PIXELFORMAT_RGB10A2: return DXGI_FORMAT_R10G10B10A2_UNORM; - case SG_PIXELFORMAT_RG11B10F: return DXGI_FORMAT_R11G11B10_FLOAT; - case SG_PIXELFORMAT_RG32UI: return DXGI_FORMAT_R32G32_UINT; - case SG_PIXELFORMAT_RG32SI: return DXGI_FORMAT_R32G32_SINT; - case SG_PIXELFORMAT_RG32F: return DXGI_FORMAT_R32G32_FLOAT; - case SG_PIXELFORMAT_RGBA16: return DXGI_FORMAT_R16G16B16A16_UNORM; - case SG_PIXELFORMAT_RGBA16SN: return DXGI_FORMAT_R16G16B16A16_SNORM; - case SG_PIXELFORMAT_RGBA16UI: return DXGI_FORMAT_R16G16B16A16_UINT; - case SG_PIXELFORMAT_RGBA16SI: return DXGI_FORMAT_R16G16B16A16_SINT; - case SG_PIXELFORMAT_RGBA16F: return DXGI_FORMAT_R16G16B16A16_FLOAT; - case SG_PIXELFORMAT_RGBA32UI: return DXGI_FORMAT_R32G32B32A32_UINT; - case SG_PIXELFORMAT_RGBA32SI: return DXGI_FORMAT_R32G32B32A32_SINT; - case SG_PIXELFORMAT_RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT; - case SG_PIXELFORMAT_DEPTH: return DXGI_FORMAT_D32_FLOAT; - case SG_PIXELFORMAT_DEPTH_STENCIL: return DXGI_FORMAT_D24_UNORM_S8_UINT; - case SG_PIXELFORMAT_BC1_RGBA: return DXGI_FORMAT_BC1_UNORM; - case SG_PIXELFORMAT_BC2_RGBA: return DXGI_FORMAT_BC2_UNORM; - case SG_PIXELFORMAT_BC3_RGBA: return DXGI_FORMAT_BC3_UNORM; - case SG_PIXELFORMAT_BC4_R: return DXGI_FORMAT_BC4_UNORM; - case SG_PIXELFORMAT_BC4_RSN: return DXGI_FORMAT_BC4_SNORM; - case SG_PIXELFORMAT_BC5_RG: return DXGI_FORMAT_BC5_UNORM; - case SG_PIXELFORMAT_BC5_RGSN: return DXGI_FORMAT_BC5_SNORM; - case SG_PIXELFORMAT_BC6H_RGBF: return DXGI_FORMAT_BC6H_SF16; - case SG_PIXELFORMAT_BC6H_RGBUF: return DXGI_FORMAT_BC6H_UF16; - case SG_PIXELFORMAT_BC7_RGBA: return DXGI_FORMAT_BC7_UNORM; - default: return DXGI_FORMAT_UNKNOWN; - }; -} - -_SOKOL_PRIVATE D3D11_PRIMITIVE_TOPOLOGY _sg_d3d11_primitive_topology(sg_primitive_type prim_type) { - switch (prim_type) { - case SG_PRIMITIVETYPE_POINTS: return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; - case SG_PRIMITIVETYPE_LINES: return D3D11_PRIMITIVE_TOPOLOGY_LINELIST; - case SG_PRIMITIVETYPE_LINE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP; - case SG_PRIMITIVETYPE_TRIANGLES: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; - case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; - default: SOKOL_UNREACHABLE; return (D3D11_PRIMITIVE_TOPOLOGY) 0; - } -} - -_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_index_format(sg_index_type index_type) { - switch (index_type) { - case SG_INDEXTYPE_NONE: return DXGI_FORMAT_UNKNOWN; - case SG_INDEXTYPE_UINT16: return DXGI_FORMAT_R16_UINT; - case SG_INDEXTYPE_UINT32: return DXGI_FORMAT_R32_UINT; - default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; - } -} - -_SOKOL_PRIVATE D3D11_FILTER _sg_d3d11_filter(sg_filter min_f, sg_filter mag_f, uint32_t max_anisotropy) { - if (max_anisotropy > 1) { - return D3D11_FILTER_ANISOTROPIC; - } - else if (mag_f == SG_FILTER_NEAREST) { - switch (min_f) { - case SG_FILTER_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - return D3D11_FILTER_MIN_MAG_MIP_POINT; - case SG_FILTER_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: - return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; - case SG_FILTER_NEAREST_MIPMAP_LINEAR: - return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; - case SG_FILTER_LINEAR_MIPMAP_LINEAR: - return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; - default: - SOKOL_UNREACHABLE; break; - } - } - else if (mag_f == SG_FILTER_LINEAR) { - switch (min_f) { - case SG_FILTER_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; - case SG_FILTER_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: - return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; - case SG_FILTER_NEAREST_MIPMAP_LINEAR: - return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR; - case SG_FILTER_LINEAR_MIPMAP_LINEAR: - return D3D11_FILTER_MIN_MAG_MIP_LINEAR; - default: - SOKOL_UNREACHABLE; break; - } - } - /* invalid value for mag filter */ - SOKOL_UNREACHABLE; - return D3D11_FILTER_MIN_MAG_MIP_POINT; -} - -_SOKOL_PRIVATE D3D11_TEXTURE_ADDRESS_MODE _sg_d3d11_address_mode(sg_wrap m) { - switch (m) { - case SG_WRAP_REPEAT: return D3D11_TEXTURE_ADDRESS_WRAP; - case SG_WRAP_CLAMP_TO_EDGE: return D3D11_TEXTURE_ADDRESS_CLAMP; - case SG_WRAP_CLAMP_TO_BORDER: return D3D11_TEXTURE_ADDRESS_BORDER; - case SG_WRAP_MIRRORED_REPEAT: return D3D11_TEXTURE_ADDRESS_MIRROR; - default: SOKOL_UNREACHABLE; return (D3D11_TEXTURE_ADDRESS_MODE) 0; - } -} - -_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_vertex_format(sg_vertex_format fmt) { - switch (fmt) { - case SG_VERTEXFORMAT_FLOAT: return DXGI_FORMAT_R32_FLOAT; - case SG_VERTEXFORMAT_FLOAT2: return DXGI_FORMAT_R32G32_FLOAT; - case SG_VERTEXFORMAT_FLOAT3: return DXGI_FORMAT_R32G32B32_FLOAT; - case SG_VERTEXFORMAT_FLOAT4: return DXGI_FORMAT_R32G32B32A32_FLOAT; - case SG_VERTEXFORMAT_BYTE4: return DXGI_FORMAT_R8G8B8A8_SINT; - case SG_VERTEXFORMAT_BYTE4N: return DXGI_FORMAT_R8G8B8A8_SNORM; - case SG_VERTEXFORMAT_UBYTE4: return DXGI_FORMAT_R8G8B8A8_UINT; - case SG_VERTEXFORMAT_UBYTE4N: return DXGI_FORMAT_R8G8B8A8_UNORM; - case SG_VERTEXFORMAT_SHORT2: return DXGI_FORMAT_R16G16_SINT; - case SG_VERTEXFORMAT_SHORT2N: return DXGI_FORMAT_R16G16_SNORM; - case SG_VERTEXFORMAT_USHORT2N: return DXGI_FORMAT_R16G16_UNORM; - case SG_VERTEXFORMAT_SHORT4: return DXGI_FORMAT_R16G16B16A16_SINT; - case SG_VERTEXFORMAT_SHORT4N: return DXGI_FORMAT_R16G16B16A16_SNORM; - case SG_VERTEXFORMAT_USHORT4N: return DXGI_FORMAT_R16G16B16A16_UNORM; - case SG_VERTEXFORMAT_UINT10_N2: return DXGI_FORMAT_R10G10B10A2_UNORM; - default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; - } -} - -_SOKOL_PRIVATE D3D11_INPUT_CLASSIFICATION _sg_d3d11_input_classification(sg_vertex_step step) { - switch (step) { - case SG_VERTEXSTEP_PER_VERTEX: return D3D11_INPUT_PER_VERTEX_DATA; - case SG_VERTEXSTEP_PER_INSTANCE: return D3D11_INPUT_PER_INSTANCE_DATA; - default: SOKOL_UNREACHABLE; return (D3D11_INPUT_CLASSIFICATION) 0; - } -} - -_SOKOL_PRIVATE D3D11_CULL_MODE _sg_d3d11_cull_mode(sg_cull_mode m) { - switch (m) { - case SG_CULLMODE_NONE: return D3D11_CULL_NONE; - case SG_CULLMODE_FRONT: return D3D11_CULL_FRONT; - case SG_CULLMODE_BACK: return D3D11_CULL_BACK; - default: SOKOL_UNREACHABLE; return (D3D11_CULL_MODE) 0; - } -} - -_SOKOL_PRIVATE D3D11_COMPARISON_FUNC _sg_d3d11_compare_func(sg_compare_func f) { - switch (f) { - case SG_COMPAREFUNC_NEVER: return D3D11_COMPARISON_NEVER; - case SG_COMPAREFUNC_LESS: return D3D11_COMPARISON_LESS; - case SG_COMPAREFUNC_EQUAL: return D3D11_COMPARISON_EQUAL; - case SG_COMPAREFUNC_LESS_EQUAL: return D3D11_COMPARISON_LESS_EQUAL; - case SG_COMPAREFUNC_GREATER: return D3D11_COMPARISON_GREATER; - case SG_COMPAREFUNC_NOT_EQUAL: return D3D11_COMPARISON_NOT_EQUAL; - case SG_COMPAREFUNC_GREATER_EQUAL: return D3D11_COMPARISON_GREATER_EQUAL; - case SG_COMPAREFUNC_ALWAYS: return D3D11_COMPARISON_ALWAYS; - default: SOKOL_UNREACHABLE; return (D3D11_COMPARISON_FUNC) 0; - } -} - -_SOKOL_PRIVATE D3D11_STENCIL_OP _sg_d3d11_stencil_op(sg_stencil_op op) { - switch (op) { - case SG_STENCILOP_KEEP: return D3D11_STENCIL_OP_KEEP; - case SG_STENCILOP_ZERO: return D3D11_STENCIL_OP_ZERO; - case SG_STENCILOP_REPLACE: return D3D11_STENCIL_OP_REPLACE; - case SG_STENCILOP_INCR_CLAMP: return D3D11_STENCIL_OP_INCR_SAT; - case SG_STENCILOP_DECR_CLAMP: return D3D11_STENCIL_OP_DECR_SAT; - case SG_STENCILOP_INVERT: return D3D11_STENCIL_OP_INVERT; - case SG_STENCILOP_INCR_WRAP: return D3D11_STENCIL_OP_INCR; - case SG_STENCILOP_DECR_WRAP: return D3D11_STENCIL_OP_DECR; - default: SOKOL_UNREACHABLE; return (D3D11_STENCIL_OP) 0; - } -} - -_SOKOL_PRIVATE D3D11_BLEND _sg_d3d11_blend_factor(sg_blend_factor f) { - switch (f) { - case SG_BLENDFACTOR_ZERO: return D3D11_BLEND_ZERO; - case SG_BLENDFACTOR_ONE: return D3D11_BLEND_ONE; - case SG_BLENDFACTOR_SRC_COLOR: return D3D11_BLEND_SRC_COLOR; - case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return D3D11_BLEND_INV_SRC_COLOR; - case SG_BLENDFACTOR_SRC_ALPHA: return D3D11_BLEND_SRC_ALPHA; - case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return D3D11_BLEND_INV_SRC_ALPHA; - case SG_BLENDFACTOR_DST_COLOR: return D3D11_BLEND_DEST_COLOR; - case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return D3D11_BLEND_INV_DEST_COLOR; - case SG_BLENDFACTOR_DST_ALPHA: return D3D11_BLEND_DEST_ALPHA; - case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return D3D11_BLEND_INV_DEST_ALPHA; - case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return D3D11_BLEND_SRC_ALPHA_SAT; - case SG_BLENDFACTOR_BLEND_COLOR: return D3D11_BLEND_BLEND_FACTOR; - case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return D3D11_BLEND_INV_BLEND_FACTOR; - case SG_BLENDFACTOR_BLEND_ALPHA: return D3D11_BLEND_BLEND_FACTOR; - case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return D3D11_BLEND_INV_BLEND_FACTOR; - default: SOKOL_UNREACHABLE; return (D3D11_BLEND) 0; - } -} - -_SOKOL_PRIVATE D3D11_BLEND_OP _sg_d3d11_blend_op(sg_blend_op op) { - switch (op) { - case SG_BLENDOP_ADD: return D3D11_BLEND_OP_ADD; - case SG_BLENDOP_SUBTRACT: return D3D11_BLEND_OP_SUBTRACT; - case SG_BLENDOP_REVERSE_SUBTRACT: return D3D11_BLEND_OP_REV_SUBTRACT; - default: SOKOL_UNREACHABLE; return (D3D11_BLEND_OP) 0; - } -} - -_SOKOL_PRIVATE UINT8 _sg_d3d11_color_write_mask(sg_color_mask m) { - UINT8 res = 0; - if (m & SG_COLORMASK_R) { - res |= D3D11_COLOR_WRITE_ENABLE_RED; - } - if (m & SG_COLORMASK_G) { - res |= D3D11_COLOR_WRITE_ENABLE_GREEN; - } - if (m & SG_COLORMASK_B) { - res |= D3D11_COLOR_WRITE_ENABLE_BLUE; - } - if (m & SG_COLORMASK_A) { - res |= D3D11_COLOR_WRITE_ENABLE_ALPHA; - } - return res; -} - -/* see: https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits#resource-limits-for-feature-level-11-hardware */ -_SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { - _sg.backend = SG_BACKEND_D3D11; - - _sg.features.instancing = true; - _sg.features.origin_top_left = true; - _sg.features.multiple_render_targets = true; - _sg.features.msaa_render_targets = true; - _sg.features.imagetype_3d = true; - _sg.features.imagetype_array = true; - _sg.features.image_clamp_to_border = true; - _sg.features.mrt_independent_blend_state = true; - _sg.features.mrt_independent_write_mask = true; - - _sg.limits.max_image_size_2d = 16 * 1024; - _sg.limits.max_image_size_cube = 16 * 1024; - _sg.limits.max_image_size_3d = 2 * 1024; - _sg.limits.max_image_size_array = 16 * 1024; - _sg.limits.max_image_array_layers = 2 * 1024; - _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; - - /* see: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_format_support */ - for (int fmt = (SG_PIXELFORMAT_NONE+1); fmt < _SG_PIXELFORMAT_NUM; fmt++) { - UINT dxgi_fmt_caps = 0; - const DXGI_FORMAT dxgi_fmt = _sg_d3d11_pixel_format((sg_pixel_format)fmt); - if (dxgi_fmt != DXGI_FORMAT_UNKNOWN) { - HRESULT hr = _sg_d3d11_CheckFormatSupport(_sg.d3d11.dev, dxgi_fmt, &dxgi_fmt_caps); - SOKOL_ASSERT(SUCCEEDED(hr) || (E_FAIL == hr)); - if (!SUCCEEDED(hr)) { - dxgi_fmt_caps = 0; - } - } - sg_pixelformat_info* info = &_sg.formats[fmt]; - info->sample = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_TEXTURE2D); - info->filter = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE); - info->render = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_RENDER_TARGET); - info->blend = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_BLENDABLE); - info->msaa = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET); - info->depth = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL); - if (info->depth) { - info->render = true; - } - } -} - -_SOKOL_PRIVATE void _sg_d3d11_setup_backend(const sg_desc* desc) { - /* assume _sg.d3d11 already is zero-initialized */ - SOKOL_ASSERT(desc); - SOKOL_ASSERT(desc->context.d3d11.device); - SOKOL_ASSERT(desc->context.d3d11.device_context); - SOKOL_ASSERT(desc->context.d3d11.render_target_view_cb || desc->context.d3d11.render_target_view_userdata_cb); - SOKOL_ASSERT(desc->context.d3d11.depth_stencil_view_cb || desc->context.d3d11.depth_stencil_view_userdata_cb); - _sg.d3d11.valid = true; - _sg.d3d11.dev = (ID3D11Device*) desc->context.d3d11.device; - _sg.d3d11.ctx = (ID3D11DeviceContext*) desc->context.d3d11.device_context; - _sg.d3d11.rtv_cb = desc->context.d3d11.render_target_view_cb; - _sg.d3d11.rtv_userdata_cb = desc->context.d3d11.render_target_view_userdata_cb; - _sg.d3d11.dsv_cb = desc->context.d3d11.depth_stencil_view_cb; - _sg.d3d11.dsv_userdata_cb = desc->context.d3d11.depth_stencil_view_userdata_cb; - _sg.d3d11.user_data = desc->context.d3d11.user_data; - _sg_d3d11_init_caps(); -} - -_SOKOL_PRIVATE void _sg_d3d11_discard_backend(void) { - SOKOL_ASSERT(_sg.d3d11.valid); - _sg.d3d11.valid = false; -} - -_SOKOL_PRIVATE void _sg_d3d11_clear_state(void) { - /* clear all the device context state, so that resource refs don't keep stuck in the d3d device context */ - _sg_d3d11_ClearState(_sg.d3d11.ctx); -} - -_SOKOL_PRIVATE void _sg_d3d11_reset_state_cache(void) { - /* just clear the d3d11 device context state */ - _sg_d3d11_clear_state(); -} - -_SOKOL_PRIVATE void _sg_d3d11_activate_context(_sg_context_t* ctx) { - _SOKOL_UNUSED(ctx); - _sg_d3d11_clear_state(); -} - -_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_d3d11_destroy_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); - /* empty */ -} - -_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { - SOKOL_ASSERT(buf && desc); - SOKOL_ASSERT(!buf->d3d11.buf); - _sg_buffer_common_init(&buf->cmn, desc); - const bool injected = (0 != desc->d3d11_buffer); - if (injected) { - buf->d3d11.buf = (ID3D11Buffer*) desc->d3d11_buffer; - _sg_d3d11_AddRef(buf->d3d11.buf); - } - else { - D3D11_BUFFER_DESC d3d11_desc; - memset(&d3d11_desc, 0, sizeof(d3d11_desc)); - d3d11_desc.ByteWidth = (UINT)buf->cmn.size; - d3d11_desc.Usage = _sg_d3d11_usage(buf->cmn.usage); - d3d11_desc.BindFlags = buf->cmn.type == SG_BUFFERTYPE_VERTEXBUFFER ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER; - d3d11_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(buf->cmn.usage); - D3D11_SUBRESOURCE_DATA* init_data_ptr = 0; - D3D11_SUBRESOURCE_DATA init_data; - memset(&init_data, 0, sizeof(init_data)); - if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { - SOKOL_ASSERT(desc->data.ptr); - init_data.pSysMem = desc->data.ptr; - init_data_ptr = &init_data; - } - HRESULT hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &d3d11_desc, init_data_ptr, &buf->d3d11.buf); - if (!(SUCCEEDED(hr) && buf->d3d11.buf)) { - SOKOL_LOG("failed to create D3D11 buffer\n"); - return SG_RESOURCESTATE_FAILED; - } - } - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_d3d11_destroy_buffer(_sg_buffer_t* buf) { - SOKOL_ASSERT(buf); - if (buf->d3d11.buf) { - _sg_d3d11_Release(buf->d3d11.buf); - } -} - -_SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_image_data* data) { - const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; - const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1; - int subres_index = 0; - for (int face_index = 0; face_index < num_faces; face_index++) { - for (int slice_index = 0; slice_index < num_slices; slice_index++) { - for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { - SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); - D3D11_SUBRESOURCE_DATA* subres_data = &_sg.d3d11.subres_data[subres_index]; - const int mip_width = ((img->cmn.width>>mip_index)>0) ? img->cmn.width>>mip_index : 1; - const int mip_height = ((img->cmn.height>>mip_index)>0) ? img->cmn.height>>mip_index : 1; - const sg_range* subimg_data = &(data->subimage[face_index][mip_index]); - const size_t slice_size = subimg_data->size / (size_t)num_slices; - const size_t slice_offset = slice_size * (size_t)slice_index; - const uint8_t* ptr = (const uint8_t*) subimg_data->ptr; - subres_data->pSysMem = ptr + slice_offset; - subres_data->SysMemPitch = (UINT)_sg_row_pitch(img->cmn.pixel_format, mip_width, 1); - if (img->cmn.type == SG_IMAGETYPE_3D) { - /* FIXME? const int mip_depth = ((img->depth>>mip_index)>0) ? img->depth>>mip_index : 1; */ - subres_data->SysMemSlicePitch = (UINT)_sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); - } - else { - subres_data->SysMemSlicePitch = 0; - } - } - } - } -} - -_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const sg_image_desc* desc) { - SOKOL_ASSERT(img && desc); - SOKOL_ASSERT(!img->d3d11.tex2d && !img->d3d11.tex3d && !img->d3d11.texds && !img->d3d11.texmsaa); - SOKOL_ASSERT(!img->d3d11.srv && !img->d3d11.smp); - HRESULT hr; - - _sg_image_common_init(&img->cmn, desc); - const bool injected = (0 != desc->d3d11_texture) || (0 != desc->d3d11_shader_resource_view); - const bool msaa = (img->cmn.sample_count > 1); - img->d3d11.format = _sg_d3d11_pixel_format(img->cmn.pixel_format); - - /* special case depth-stencil buffer? */ - if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { - /* create only a depth-texture */ - SOKOL_ASSERT(!injected); - if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { - SOKOL_LOG("trying to create a D3D11 depth-texture with unsupported pixel format\n"); - return SG_RESOURCESTATE_FAILED; - } - D3D11_TEXTURE2D_DESC d3d11_desc; - memset(&d3d11_desc, 0, sizeof(d3d11_desc)); - d3d11_desc.Width = (UINT)img->cmn.width; - d3d11_desc.Height = (UINT)img->cmn.height; - d3d11_desc.MipLevels = 1; - d3d11_desc.ArraySize = 1; - d3d11_desc.Format = img->d3d11.format; - d3d11_desc.Usage = D3D11_USAGE_DEFAULT; - d3d11_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; - d3d11_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; - d3d11_desc.SampleDesc.Quality = (UINT) (msaa ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); - hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_desc, NULL, &img->d3d11.texds); - if (!(SUCCEEDED(hr) && img->d3d11.texds)) { - SOKOL_LOG("failed to create D3D11 texture 2D\n"); - return SG_RESOURCESTATE_FAILED; - } - } - else { - /* create (or inject) color texture and shader-resource-view */ - - /* prepare initial content pointers */ - D3D11_SUBRESOURCE_DATA* init_data = 0; - if (!injected && (img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { - _sg_d3d11_fill_subres_data(img, &desc->data); - init_data = _sg.d3d11.subres_data; - } - if (img->cmn.type != SG_IMAGETYPE_3D) { - /* 2D-, cube- or array-texture */ - /* if this is an MSAA render target, the following texture will be the 'resolve-texture' */ - - /* first check for injected texture and/or resource view */ - if (injected) { - img->d3d11.tex2d = (ID3D11Texture2D*) desc->d3d11_texture; - img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; - if (img->d3d11.tex2d) { - _sg_d3d11_AddRef(img->d3d11.tex2d); - } - else { - /* if only a shader-resource-view was provided, but no texture, lookup - the texture from the shader-resource-view, this also bumps the refcount - */ - SOKOL_ASSERT(img->d3d11.srv); - _sg_d3d11_GetResource((ID3D11View*)img->d3d11.srv, (ID3D11Resource**)&img->d3d11.tex2d); - SOKOL_ASSERT(img->d3d11.tex2d); - } - if (img->d3d11.srv) { - _sg_d3d11_AddRef(img->d3d11.srv); - } - } - - /* if not injected, create texture */ - if (0 == img->d3d11.tex2d) { - D3D11_TEXTURE2D_DESC d3d11_tex_desc; - memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); - d3d11_tex_desc.Width = (UINT)img->cmn.width; - d3d11_tex_desc.Height = (UINT)img->cmn.height; - d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; - switch (img->cmn.type) { - case SG_IMAGETYPE_ARRAY: d3d11_tex_desc.ArraySize = (UINT)img->cmn.num_slices; break; - case SG_IMAGETYPE_CUBE: d3d11_tex_desc.ArraySize = 6; break; - default: d3d11_tex_desc.ArraySize = 1; break; - } - d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - d3d11_tex_desc.Format = img->d3d11.format; - if (img->cmn.render_target) { - d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; - if (!msaa) { - d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; - } - d3d11_tex_desc.CPUAccessFlags = 0; - } - else { - d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); - d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); - } - if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { - /* trying to create a texture format that's not supported by D3D */ - SOKOL_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); - return SG_RESOURCESTATE_FAILED; - } - d3d11_tex_desc.SampleDesc.Count = 1; - d3d11_tex_desc.SampleDesc.Quality = 0; - d3d11_tex_desc.MiscFlags = (img->cmn.type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; - - hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex2d); - if (!(SUCCEEDED(hr) && img->d3d11.tex2d)) { - SOKOL_LOG("failed to create D3D11 texture 2D\n"); - return SG_RESOURCESTATE_FAILED; - } - } - - /* ...and similar, if not injected, create shader-resource-view */ - if (0 == img->d3d11.srv) { - D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; - memset(&d3d11_srv_desc, 0, sizeof(d3d11_srv_desc)); - d3d11_srv_desc.Format = img->d3d11.format; - switch (img->cmn.type) { - case SG_IMAGETYPE_2D: - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - d3d11_srv_desc.Texture2D.MipLevels = (UINT)img->cmn.num_mipmaps; - break; - case SG_IMAGETYPE_CUBE: - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; - d3d11_srv_desc.TextureCube.MipLevels = (UINT)img->cmn.num_mipmaps; - break; - case SG_IMAGETYPE_ARRAY: - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; - d3d11_srv_desc.Texture2DArray.MipLevels = (UINT)img->cmn.num_mipmaps; - d3d11_srv_desc.Texture2DArray.ArraySize = (UINT)img->cmn.num_slices; - break; - default: - SOKOL_UNREACHABLE; break; - } - hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex2d, &d3d11_srv_desc, &img->d3d11.srv); - if (!(SUCCEEDED(hr) && img->d3d11.srv)) { - SOKOL_LOG("failed to create D3D11 resource view\n"); - return SG_RESOURCESTATE_FAILED; - } - } - } - else { - /* 3D texture - same procedure, first check if injected, than create non-injected */ - if (injected) { - img->d3d11.tex3d = (ID3D11Texture3D*) desc->d3d11_texture; - img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; - if (img->d3d11.tex3d) { - _sg_d3d11_AddRef(img->d3d11.tex3d); - } - else { - SOKOL_ASSERT(img->d3d11.srv); - _sg_d3d11_GetResource((ID3D11View*)img->d3d11.srv, (ID3D11Resource**)&img->d3d11.tex3d); - SOKOL_ASSERT(img->d3d11.tex3d); - } - if (img->d3d11.srv) { - _sg_d3d11_AddRef(img->d3d11.srv); - } - } - - if (0 == img->d3d11.tex3d) { - D3D11_TEXTURE3D_DESC d3d11_tex_desc; - memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); - d3d11_tex_desc.Width = (UINT)img->cmn.width; - d3d11_tex_desc.Height = (UINT)img->cmn.height; - d3d11_tex_desc.Depth = (UINT)img->cmn.num_slices; - d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; - d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - d3d11_tex_desc.Format = img->d3d11.format; - if (img->cmn.render_target) { - d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; - if (!msaa) { - d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; - } - d3d11_tex_desc.CPUAccessFlags = 0; - } - else { - d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); - d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); - } - if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { - /* trying to create a texture format that's not supported by D3D */ - SOKOL_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); - return SG_RESOURCESTATE_FAILED; - } - hr = _sg_d3d11_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex3d); - if (!(SUCCEEDED(hr) && img->d3d11.tex3d)) { - SOKOL_LOG("failed to create D3D11 texture 3D\n"); - return SG_RESOURCESTATE_FAILED; - } - } - - if (0 == img->d3d11.srv) { - D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; - memset(&d3d11_srv_desc, 0, sizeof(d3d11_srv_desc)); - d3d11_srv_desc.Format = img->d3d11.format; - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; - d3d11_srv_desc.Texture3D.MipLevels = (UINT)img->cmn.num_mipmaps; - hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex3d, &d3d11_srv_desc, &img->d3d11.srv); - if (!(SUCCEEDED(hr) && img->d3d11.srv)) { - SOKOL_LOG("failed to create D3D11 resource view\n"); - return SG_RESOURCESTATE_FAILED; - } - } - } - - /* also need to create a separate MSAA render target texture? */ - if (msaa) { - D3D11_TEXTURE2D_DESC d3d11_tex_desc; - memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); - d3d11_tex_desc.Width = (UINT)img->cmn.width; - d3d11_tex_desc.Height = (UINT)img->cmn.height; - d3d11_tex_desc.MipLevels = 1; - d3d11_tex_desc.ArraySize = 1; - d3d11_tex_desc.Format = img->d3d11.format; - d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; - d3d11_tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; - d3d11_tex_desc.CPUAccessFlags = 0; - d3d11_tex_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; - d3d11_tex_desc.SampleDesc.Quality = (UINT)D3D11_STANDARD_MULTISAMPLE_PATTERN; - hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, NULL, &img->d3d11.texmsaa); - if (!(SUCCEEDED(hr) && img->d3d11.texmsaa)) { - SOKOL_LOG("failed to create D3D11 texture 2D\n"); - return SG_RESOURCESTATE_FAILED; - } - } - - /* sampler state object, note D3D11 implements an internal shared-pool for sampler objects */ - D3D11_SAMPLER_DESC d3d11_smp_desc; - memset(&d3d11_smp_desc, 0, sizeof(d3d11_smp_desc)); - d3d11_smp_desc.Filter = _sg_d3d11_filter(img->cmn.min_filter, img->cmn.mag_filter, img->cmn.max_anisotropy); - d3d11_smp_desc.AddressU = _sg_d3d11_address_mode(img->cmn.wrap_u); - d3d11_smp_desc.AddressV = _sg_d3d11_address_mode(img->cmn.wrap_v); - d3d11_smp_desc.AddressW = _sg_d3d11_address_mode(img->cmn.wrap_w); - switch (img->cmn.border_color) { - case SG_BORDERCOLOR_TRANSPARENT_BLACK: - /* all 0.0f */ - break; - case SG_BORDERCOLOR_OPAQUE_WHITE: - for (int i = 0; i < 4; i++) { - d3d11_smp_desc.BorderColor[i] = 1.0f; - } - break; - default: - /* opaque black */ - d3d11_smp_desc.BorderColor[3] = 1.0f; - break; - } - d3d11_smp_desc.MaxAnisotropy = img->cmn.max_anisotropy; - d3d11_smp_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; - d3d11_smp_desc.MinLOD = desc->min_lod; - d3d11_smp_desc.MaxLOD = desc->max_lod; - hr = _sg_d3d11_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &img->d3d11.smp); - if (!(SUCCEEDED(hr) && img->d3d11.smp)) { - SOKOL_LOG("failed to create D3D11 sampler state\n"); - return SG_RESOURCESTATE_FAILED; - } - } - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_d3d11_destroy_image(_sg_image_t* img) { - SOKOL_ASSERT(img); - if (img->d3d11.tex2d) { - _sg_d3d11_Release(img->d3d11.tex2d); - } - if (img->d3d11.tex3d) { - _sg_d3d11_Release(img->d3d11.tex3d); - } - if (img->d3d11.texds) { - _sg_d3d11_Release(img->d3d11.texds); - } - if (img->d3d11.texmsaa) { - _sg_d3d11_Release(img->d3d11.texmsaa); - } - if (img->d3d11.srv) { - _sg_d3d11_Release(img->d3d11.srv); - } - if (img->d3d11.smp) { - _sg_d3d11_Release(img->d3d11.smp); - } -} - -_SOKOL_PRIVATE bool _sg_d3d11_load_d3dcompiler_dll(void) { - /* on UWP, don't do anything (not tested) */ - #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) - return true; - #else - /* load DLL on demand */ - if ((0 == _sg.d3d11.d3dcompiler_dll) && !_sg.d3d11.d3dcompiler_dll_load_failed) { - _sg.d3d11.d3dcompiler_dll = LoadLibraryA("d3dcompiler_47.dll"); - if (0 == _sg.d3d11.d3dcompiler_dll) { - /* don't attempt to load missing DLL in the future */ - SOKOL_LOG("failed to load d3dcompiler_47.dll!\n"); - _sg.d3d11.d3dcompiler_dll_load_failed = true; - return false; - } - /* look up function pointers */ - _sg.d3d11.D3DCompile_func = (pD3DCompile)(void*) GetProcAddress(_sg.d3d11.d3dcompiler_dll, "D3DCompile"); - SOKOL_ASSERT(_sg.d3d11.D3DCompile_func); - } - return 0 != _sg.d3d11.d3dcompiler_dll; - #endif -} - -#if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) -#define _sg_d3d11_D3DCompile D3DCompile -#else -#define _sg_d3d11_D3DCompile _sg.d3d11.D3DCompile_func -#endif - -_SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* stage_desc) { - if (!_sg_d3d11_load_d3dcompiler_dll()) { - return NULL; - } - SOKOL_ASSERT(stage_desc->d3d11_target); - ID3DBlob* output = NULL; - ID3DBlob* errors_or_warnings = NULL; - HRESULT hr = _sg_d3d11_D3DCompile( - stage_desc->source, /* pSrcData */ - strlen(stage_desc->source), /* SrcDataSize */ - NULL, /* pSourceName */ - NULL, /* pDefines */ - NULL, /* pInclude */ - stage_desc->entry ? stage_desc->entry : "main", /* pEntryPoint */ - stage_desc->d3d11_target, /* pTarget (vs_5_0 or ps_5_0) */ - D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3, /* Flags1 */ - 0, /* Flags2 */ - &output, /* ppCode */ - &errors_or_warnings); /* ppErrorMsgs */ - if (errors_or_warnings) { - SOKOL_LOG((LPCSTR)_sg_d3d11_GetBufferPointer(errors_or_warnings)); - _sg_d3d11_Release(errors_or_warnings); errors_or_warnings = NULL; - } - if (FAILED(hr)) { - /* just in case, usually output is NULL here */ - if (output) { - _sg_d3d11_Release(output); - output = NULL; - } - } - return output; -} - -_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { - SOKOL_ASSERT(shd && desc); - SOKOL_ASSERT(!shd->d3d11.vs && !shd->d3d11.fs && !shd->d3d11.vs_blob); - HRESULT hr; - - _sg_shader_common_init(&shd->cmn, desc); - - /* copy vertex attribute semantic names and indices */ - for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { - _sg_strcpy(&shd->d3d11.attrs[i].sem_name, desc->attrs[i].sem_name); - shd->d3d11.attrs[i].sem_index = desc->attrs[i].sem_index; - } - - /* shader stage uniform blocks and image slots */ - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; - _sg_d3d11_shader_stage_t* d3d11_stage = &shd->d3d11.stage[stage_index]; - for (int ub_index = 0; ub_index < cmn_stage->num_uniform_blocks; ub_index++) { - const _sg_uniform_block_t* ub = &cmn_stage->uniform_blocks[ub_index]; - - /* create a D3D constant buffer for each uniform block */ - SOKOL_ASSERT(0 == d3d11_stage->cbufs[ub_index]); - D3D11_BUFFER_DESC cb_desc; - memset(&cb_desc, 0, sizeof(cb_desc)); - cb_desc.ByteWidth = (UINT)_sg_roundup((int)ub->size, 16); - cb_desc.Usage = D3D11_USAGE_DEFAULT; - cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; - hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &cb_desc, NULL, &d3d11_stage->cbufs[ub_index]); - if (!(SUCCEEDED(hr) && d3d11_stage->cbufs[ub_index])) { - SOKOL_LOG("failed to create D3D11 buffer\n"); - return SG_RESOURCESTATE_FAILED; - } - } - } - - const void* vs_ptr = 0, *fs_ptr = 0; - SIZE_T vs_length = 0, fs_length = 0; - ID3DBlob* vs_blob = 0, *fs_blob = 0; - if (desc->vs.bytecode.ptr && desc->fs.bytecode.ptr) { - /* create from shader byte code */ - vs_ptr = desc->vs.bytecode.ptr; - fs_ptr = desc->fs.bytecode.ptr; - vs_length = desc->vs.bytecode.size; - fs_length = desc->fs.bytecode.size; - } - else { - /* compile from shader source code */ - vs_blob = _sg_d3d11_compile_shader(&desc->vs); - fs_blob = _sg_d3d11_compile_shader(&desc->fs); - if (vs_blob && fs_blob) { - vs_ptr = _sg_d3d11_GetBufferPointer(vs_blob); - vs_length = _sg_d3d11_GetBufferSize(vs_blob); - fs_ptr = _sg_d3d11_GetBufferPointer(fs_blob); - fs_length = _sg_d3d11_GetBufferSize(fs_blob); - } - } - sg_resource_state result = SG_RESOURCESTATE_FAILED; - if (vs_ptr && fs_ptr && (vs_length > 0) && (fs_length > 0)) { - /* create the D3D vertex- and pixel-shader objects */ - hr = _sg_d3d11_CreateVertexShader(_sg.d3d11.dev, vs_ptr, vs_length, NULL, &shd->d3d11.vs); - bool vs_succeeded = SUCCEEDED(hr) && shd->d3d11.vs; - hr = _sg_d3d11_CreatePixelShader(_sg.d3d11.dev, fs_ptr, fs_length, NULL, &shd->d3d11.fs); - bool fs_succeeded = SUCCEEDED(hr) && shd->d3d11.fs; - - /* need to store the vertex shader byte code, this is needed later in sg_create_pipeline */ - if (vs_succeeded && fs_succeeded) { - shd->d3d11.vs_blob_length = vs_length; - shd->d3d11.vs_blob = SOKOL_MALLOC((size_t)vs_length); - SOKOL_ASSERT(shd->d3d11.vs_blob); - memcpy(shd->d3d11.vs_blob, vs_ptr, vs_length); - result = SG_RESOURCESTATE_VALID; - } - } - if (vs_blob) { - _sg_d3d11_Release(vs_blob); vs_blob = 0; - } - if (fs_blob) { - _sg_d3d11_Release(fs_blob); fs_blob = 0; - } - return result; -} - -_SOKOL_PRIVATE void _sg_d3d11_destroy_shader(_sg_shader_t* shd) { - SOKOL_ASSERT(shd); - if (shd->d3d11.vs) { - _sg_d3d11_Release(shd->d3d11.vs); - } - if (shd->d3d11.fs) { - _sg_d3d11_Release(shd->d3d11.fs); - } - if (shd->d3d11.vs_blob) { - SOKOL_FREE(shd->d3d11.vs_blob); - } - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; - _sg_d3d11_shader_stage_t* d3d11_stage = &shd->d3d11.stage[stage_index]; - for (int ub_index = 0; ub_index < cmn_stage->num_uniform_blocks; ub_index++) { - if (d3d11_stage->cbufs[ub_index]) { - _sg_d3d11_Release(d3d11_stage->cbufs[ub_index]); - } - } - } -} - -_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { - SOKOL_ASSERT(pip && shd && desc); - SOKOL_ASSERT(desc->shader.id == shd->slot.id); - SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_VALID); - SOKOL_ASSERT(shd->d3d11.vs_blob && shd->d3d11.vs_blob_length > 0); - SOKOL_ASSERT(!pip->d3d11.il && !pip->d3d11.rs && !pip->d3d11.dss && !pip->d3d11.bs); - - pip->shader = shd; - _sg_pipeline_common_init(&pip->cmn, desc); - pip->d3d11.index_format = _sg_d3d11_index_format(pip->cmn.index_type); - pip->d3d11.topology = _sg_d3d11_primitive_topology(desc->primitive_type); - pip->d3d11.stencil_ref = desc->stencil.ref; - - /* create input layout object */ - HRESULT hr; - D3D11_INPUT_ELEMENT_DESC d3d11_comps[SG_MAX_VERTEX_ATTRIBUTES]; - memset(d3d11_comps, 0, sizeof(d3d11_comps)); - int attr_index = 0; - for (; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { - break; - } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[a_desc->buffer_index]; - const sg_vertex_step step_func = l_desc->step_func; - const int step_rate = l_desc->step_rate; - D3D11_INPUT_ELEMENT_DESC* d3d11_comp = &d3d11_comps[attr_index]; - d3d11_comp->SemanticName = _sg_strptr(&shd->d3d11.attrs[attr_index].sem_name); - d3d11_comp->SemanticIndex = (UINT)shd->d3d11.attrs[attr_index].sem_index; - d3d11_comp->Format = _sg_d3d11_vertex_format(a_desc->format); - d3d11_comp->InputSlot = (UINT)a_desc->buffer_index; - d3d11_comp->AlignedByteOffset = (UINT)a_desc->offset; - d3d11_comp->InputSlotClass = _sg_d3d11_input_classification(step_func); - if (SG_VERTEXSTEP_PER_INSTANCE == step_func) { - d3d11_comp->InstanceDataStepRate = (UINT)step_rate; - pip->cmn.use_instanced_draw = true; - } - pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; - } - for (int layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { - if (pip->cmn.vertex_layout_valid[layout_index]) { - const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[layout_index]; - SOKOL_ASSERT(l_desc->stride > 0); - pip->d3d11.vb_strides[layout_index] = (UINT)l_desc->stride; - } - else { - pip->d3d11.vb_strides[layout_index] = 0; - } - } - hr = _sg_d3d11_CreateInputLayout(_sg.d3d11.dev, - d3d11_comps, /* pInputElementDesc */ - (UINT)attr_index, /* NumElements */ - shd->d3d11.vs_blob, /* pShaderByteCodeWithInputSignature */ - shd->d3d11.vs_blob_length, /* BytecodeLength */ - &pip->d3d11.il); - if (!(SUCCEEDED(hr) && pip->d3d11.il)) { - SOKOL_LOG("failed to create D3D11 input layout\n"); - return SG_RESOURCESTATE_FAILED; - } - - /* create rasterizer state */ - D3D11_RASTERIZER_DESC rs_desc; - memset(&rs_desc, 0, sizeof(rs_desc)); - rs_desc.FillMode = D3D11_FILL_SOLID; - rs_desc.CullMode = _sg_d3d11_cull_mode(desc->cull_mode); - rs_desc.FrontCounterClockwise = desc->face_winding == SG_FACEWINDING_CCW; - rs_desc.DepthBias = (INT) pip->cmn.depth_bias; - rs_desc.DepthBiasClamp = pip->cmn.depth_bias_clamp; - rs_desc.SlopeScaledDepthBias = pip->cmn.depth_bias_slope_scale; - rs_desc.DepthClipEnable = TRUE; - rs_desc.ScissorEnable = TRUE; - rs_desc.MultisampleEnable = desc->sample_count > 1; - rs_desc.AntialiasedLineEnable = FALSE; - hr = _sg_d3d11_CreateRasterizerState(_sg.d3d11.dev, &rs_desc, &pip->d3d11.rs); - if (!(SUCCEEDED(hr) && pip->d3d11.rs)) { - SOKOL_LOG("failed to create D3D11 rasterizer state\n"); - return SG_RESOURCESTATE_FAILED; - } - - /* create depth-stencil state */ - D3D11_DEPTH_STENCIL_DESC dss_desc; - memset(&dss_desc, 0, sizeof(dss_desc)); - dss_desc.DepthEnable = TRUE; - dss_desc.DepthWriteMask = desc->depth.write_enabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; - dss_desc.DepthFunc = _sg_d3d11_compare_func(desc->depth.compare); - dss_desc.StencilEnable = desc->stencil.enabled; - dss_desc.StencilReadMask = desc->stencil.read_mask; - dss_desc.StencilWriteMask = desc->stencil.write_mask; - const sg_stencil_face_state* sf = &desc->stencil.front; - dss_desc.FrontFace.StencilFailOp = _sg_d3d11_stencil_op(sf->fail_op); - dss_desc.FrontFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sf->depth_fail_op); - dss_desc.FrontFace.StencilPassOp = _sg_d3d11_stencil_op(sf->pass_op); - dss_desc.FrontFace.StencilFunc = _sg_d3d11_compare_func(sf->compare); - const sg_stencil_face_state* sb = &desc->stencil.back; - dss_desc.BackFace.StencilFailOp = _sg_d3d11_stencil_op(sb->fail_op); - dss_desc.BackFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sb->depth_fail_op); - dss_desc.BackFace.StencilPassOp = _sg_d3d11_stencil_op(sb->pass_op); - dss_desc.BackFace.StencilFunc = _sg_d3d11_compare_func(sb->compare); - hr = _sg_d3d11_CreateDepthStencilState(_sg.d3d11.dev, &dss_desc, &pip->d3d11.dss); - if (!(SUCCEEDED(hr) && pip->d3d11.dss)) { - SOKOL_LOG("failed to create D3D11 depth stencil state\n"); - return SG_RESOURCESTATE_FAILED; - } - - /* create blend state */ - D3D11_BLEND_DESC bs_desc; - memset(&bs_desc, 0, sizeof(bs_desc)); - bs_desc.AlphaToCoverageEnable = desc->alpha_to_coverage_enabled; - bs_desc.IndependentBlendEnable = TRUE; - { - int i = 0; - for (i = 0; i < desc->color_count; i++) { - const sg_blend_state* src = &desc->colors[i].blend; - D3D11_RENDER_TARGET_BLEND_DESC* dst = &bs_desc.RenderTarget[i]; - dst->BlendEnable = src->enabled; - dst->SrcBlend = _sg_d3d11_blend_factor(src->src_factor_rgb); - dst->DestBlend = _sg_d3d11_blend_factor(src->dst_factor_rgb); - dst->BlendOp = _sg_d3d11_blend_op(src->op_rgb); - dst->SrcBlendAlpha = _sg_d3d11_blend_factor(src->src_factor_alpha); - dst->DestBlendAlpha = _sg_d3d11_blend_factor(src->dst_factor_alpha); - dst->BlendOpAlpha = _sg_d3d11_blend_op(src->op_alpha); - dst->RenderTargetWriteMask = _sg_d3d11_color_write_mask(desc->colors[i].write_mask); - } - for (; i < 8; i++) { - D3D11_RENDER_TARGET_BLEND_DESC* dst = &bs_desc.RenderTarget[i]; - dst->BlendEnable = FALSE; - dst->SrcBlend = dst->SrcBlendAlpha = D3D11_BLEND_ONE; - dst->DestBlend = dst->DestBlendAlpha = D3D11_BLEND_ZERO; - dst->BlendOp = dst->BlendOpAlpha = D3D11_BLEND_OP_ADD; - dst->RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; - } - } - hr = _sg_d3d11_CreateBlendState(_sg.d3d11.dev, &bs_desc, &pip->d3d11.bs); - if (!(SUCCEEDED(hr) && pip->d3d11.bs)) { - SOKOL_LOG("failed to create D3D11 blend state\n"); - return SG_RESOURCESTATE_FAILED; - } - - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_d3d11_destroy_pipeline(_sg_pipeline_t* pip) { - SOKOL_ASSERT(pip); - if (pip == _sg.d3d11.cur_pipeline) { - _sg.d3d11.cur_pipeline = 0; - _sg.d3d11.cur_pipeline_id.id = SG_INVALID_ID; - } - if (pip->d3d11.il) { - _sg_d3d11_Release(pip->d3d11.il); - } - if (pip->d3d11.rs) { - _sg_d3d11_Release(pip->d3d11.rs); - } - if (pip->d3d11.dss) { - _sg_d3d11_Release(pip->d3d11.dss); - } - if (pip->d3d11.bs) { - _sg_d3d11_Release(pip->d3d11.bs); - } -} - -_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { - SOKOL_ASSERT(pass && desc); - SOKOL_ASSERT(att_images && att_images[0]); - SOKOL_ASSERT(_sg.d3d11.dev); - - _sg_pass_common_init(&pass->cmn, desc); - - for (int i = 0; i < pass->cmn.num_color_atts; i++) { - const sg_pass_attachment_desc* att_desc = &desc->color_attachments[i]; - _SOKOL_UNUSED(att_desc); - SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); - _sg_image_t* att_img = att_images[i]; - SOKOL_ASSERT(att_img && (att_img->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_img->cmn.pixel_format)); - SOKOL_ASSERT(0 == pass->d3d11.color_atts[i].image); - pass->d3d11.color_atts[i].image = att_img; - - /* create D3D11 render-target-view */ - const _sg_pass_attachment_t* cmn_att = &pass->cmn.color_atts[i]; - SOKOL_ASSERT(0 == pass->d3d11.color_atts[i].rtv); - ID3D11Resource* d3d11_res = 0; - const bool is_msaa = att_img->cmn.sample_count > 1; - D3D11_RENDER_TARGET_VIEW_DESC d3d11_rtv_desc; - memset(&d3d11_rtv_desc, 0, sizeof(d3d11_rtv_desc)); - d3d11_rtv_desc.Format = att_img->d3d11.format; - if ((att_img->cmn.type == SG_IMAGETYPE_2D) || is_msaa) { - if (is_msaa) { - d3d11_res = (ID3D11Resource*) att_img->d3d11.texmsaa; - d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; - } - else { - d3d11_res = (ID3D11Resource*) att_img->d3d11.tex2d; - d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; - d3d11_rtv_desc.Texture2D.MipSlice = (UINT)cmn_att->mip_level; - } - } - else if ((att_img->cmn.type == SG_IMAGETYPE_CUBE) || (att_img->cmn.type == SG_IMAGETYPE_ARRAY)) { - d3d11_res = (ID3D11Resource*) att_img->d3d11.tex2d; - d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; - d3d11_rtv_desc.Texture2DArray.MipSlice = (UINT)cmn_att->mip_level; - d3d11_rtv_desc.Texture2DArray.FirstArraySlice = (UINT)cmn_att->slice; - d3d11_rtv_desc.Texture2DArray.ArraySize = 1; - } - else { - SOKOL_ASSERT(att_img->cmn.type == SG_IMAGETYPE_3D); - d3d11_res = (ID3D11Resource*) att_img->d3d11.tex3d; - d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; - d3d11_rtv_desc.Texture3D.MipSlice = (UINT)cmn_att->mip_level; - d3d11_rtv_desc.Texture3D.FirstWSlice = (UINT)cmn_att->slice; - d3d11_rtv_desc.Texture3D.WSize = 1; - } - SOKOL_ASSERT(d3d11_res); - HRESULT hr = _sg_d3d11_CreateRenderTargetView(_sg.d3d11.dev, d3d11_res, &d3d11_rtv_desc, &pass->d3d11.color_atts[i].rtv); - if (!(SUCCEEDED(hr) && pass->d3d11.color_atts[i].rtv)) { - SOKOL_LOG("failed to create D3D11 render target view\n"); - return SG_RESOURCESTATE_FAILED; - } - } - - /* optional depth-stencil image */ - SOKOL_ASSERT(0 == pass->d3d11.ds_att.image); - SOKOL_ASSERT(0 == pass->d3d11.ds_att.dsv); - if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { - const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; - const sg_pass_attachment_desc* att_desc = &desc->depth_stencil_attachment; - _SOKOL_UNUSED(att_desc); - _sg_image_t* att_img = att_images[ds_img_index]; - SOKOL_ASSERT(att_img && (att_img->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_img->cmn.pixel_format)); - SOKOL_ASSERT(0 == pass->d3d11.ds_att.image); - pass->d3d11.ds_att.image = att_img; - - /* create D3D11 depth-stencil-view */ - D3D11_DEPTH_STENCIL_VIEW_DESC d3d11_dsv_desc; - memset(&d3d11_dsv_desc, 0, sizeof(d3d11_dsv_desc)); - d3d11_dsv_desc.Format = att_img->d3d11.format; - const bool is_msaa = att_img->cmn.sample_count > 1; - if (is_msaa) { - d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; - } - else { - d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; - } - ID3D11Resource* d3d11_res = (ID3D11Resource*) att_img->d3d11.texds; - SOKOL_ASSERT(d3d11_res); - HRESULT hr = _sg_d3d11_CreateDepthStencilView(_sg.d3d11.dev, d3d11_res, &d3d11_dsv_desc, &pass->d3d11.ds_att.dsv); - if (!(SUCCEEDED(hr) && pass->d3d11.ds_att.dsv)) { - SOKOL_LOG("failed to create D3D11 depth stencil view\n"); - return SG_RESOURCESTATE_FAILED; - } - } - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_d3d11_destroy_pass(_sg_pass_t* pass) { - SOKOL_ASSERT(pass); - SOKOL_ASSERT(pass != _sg.d3d11.cur_pass); - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (pass->d3d11.color_atts[i].rtv) { - _sg_d3d11_Release(pass->d3d11.color_atts[i].rtv); - } - } - if (pass->d3d11.ds_att.dsv) { - _sg_d3d11_Release(pass->d3d11.ds_att.dsv); - } -} - -_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_pass_color_image(const _sg_pass_t* pass, int index) { - SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); - /* NOTE: may return null */ - return pass->d3d11.color_atts[index].image; -} - -_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_pass_ds_image(const _sg_pass_t* pass) { - /* NOTE: may return null */ - SOKOL_ASSERT(pass); - return pass->d3d11.ds_att.image; -} - -_SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { - SOKOL_ASSERT(action); - SOKOL_ASSERT(!_sg.d3d11.in_pass); - SOKOL_ASSERT(_sg.d3d11.rtv_cb || _sg.d3d11.rtv_userdata_cb); - SOKOL_ASSERT(_sg.d3d11.dsv_cb || _sg.d3d11.dsv_userdata_cb); - _sg.d3d11.in_pass = true; - _sg.d3d11.cur_width = w; - _sg.d3d11.cur_height = h; - if (pass) { - _sg.d3d11.cur_pass = pass; - _sg.d3d11.cur_pass_id.id = pass->slot.id; - _sg.d3d11.num_rtvs = 0; - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - _sg.d3d11.cur_rtvs[i] = pass->d3d11.color_atts[i].rtv; - if (_sg.d3d11.cur_rtvs[i]) { - _sg.d3d11.num_rtvs++; - } - } - _sg.d3d11.cur_dsv = pass->d3d11.ds_att.dsv; - } - else { - /* render to default frame buffer */ - _sg.d3d11.cur_pass = 0; - _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; - _sg.d3d11.num_rtvs = 1; - if (_sg.d3d11.rtv_cb) { - _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) _sg.d3d11.rtv_cb(); - } - else { - _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) _sg.d3d11.rtv_userdata_cb(_sg.d3d11.user_data); - } - for (int i = 1; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - _sg.d3d11.cur_rtvs[i] = 0; - } - if (_sg.d3d11.dsv_cb) { - _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) _sg.d3d11.dsv_cb(); - } - else { - _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) _sg.d3d11.dsv_userdata_cb(_sg.d3d11.user_data); - } - SOKOL_ASSERT(_sg.d3d11.cur_rtvs[0] && _sg.d3d11.cur_dsv); - } - /* apply the render-target- and depth-stencil-views */ - _sg_d3d11_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, _sg.d3d11.cur_rtvs, _sg.d3d11.cur_dsv); - - /* set viewport and scissor rect to cover whole screen */ - D3D11_VIEWPORT vp; - memset(&vp, 0, sizeof(vp)); - vp.Width = (FLOAT) w; - vp.Height = (FLOAT) h; - vp.MaxDepth = 1.0f; - _sg_d3d11_RSSetViewports(_sg.d3d11.ctx, 1, &vp); - D3D11_RECT rect; - rect.left = 0; - rect.top = 0; - rect.right = w; - rect.bottom = h; - _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); - - /* perform clear action */ - for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { - if (action->colors[i].action == SG_ACTION_CLEAR) { - _sg_d3d11_ClearRenderTargetView(_sg.d3d11.ctx, _sg.d3d11.cur_rtvs[i], &action->colors[i].value.r); - } - } - UINT ds_flags = 0; - if (action->depth.action == SG_ACTION_CLEAR) { - ds_flags |= D3D11_CLEAR_DEPTH; - } - if (action->stencil.action == SG_ACTION_CLEAR) { - ds_flags |= D3D11_CLEAR_STENCIL; - } - if ((0 != ds_flags) && _sg.d3d11.cur_dsv) { - _sg_d3d11_ClearDepthStencilView(_sg.d3d11.ctx, _sg.d3d11.cur_dsv, ds_flags, action->depth.value, action->stencil.value); - } -} - -/* D3D11CalcSubresource only exists for C++ */ -_SOKOL_PRIVATE UINT _sg_d3d11_calcsubresource(UINT mip_slice, UINT array_slice, UINT mip_levels) { - return mip_slice + array_slice * mip_levels; -} - -_SOKOL_PRIVATE void _sg_d3d11_end_pass(void) { - SOKOL_ASSERT(_sg.d3d11.in_pass && _sg.d3d11.ctx); - _sg.d3d11.in_pass = false; - - /* need to resolve MSAA render target into texture? */ - if (_sg.d3d11.cur_pass) { - SOKOL_ASSERT(_sg.d3d11.cur_pass->slot.id == _sg.d3d11.cur_pass_id.id); - for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { - _sg_pass_attachment_t* cmn_att = &_sg.d3d11.cur_pass->cmn.color_atts[i]; - _sg_image_t* att_img = _sg.d3d11.cur_pass->d3d11.color_atts[i].image; - SOKOL_ASSERT(att_img && (att_img->slot.id == cmn_att->image_id.id)); - if (att_img->cmn.sample_count > 1) { - /* FIXME: support MSAA resolve into 3D texture */ - SOKOL_ASSERT(att_img->d3d11.tex2d && att_img->d3d11.texmsaa && !att_img->d3d11.tex3d); - SOKOL_ASSERT(DXGI_FORMAT_UNKNOWN != att_img->d3d11.format); - UINT dst_subres = _sg_d3d11_calcsubresource((UINT)cmn_att->mip_level, (UINT)cmn_att->slice, (UINT)att_img->cmn.num_mipmaps); - _sg_d3d11_ResolveSubresource(_sg.d3d11.ctx, - (ID3D11Resource*) att_img->d3d11.tex2d, /* pDstResource */ - dst_subres, /* DstSubresource */ - (ID3D11Resource*) att_img->d3d11.texmsaa, /* pSrcResource */ - 0, /* SrcSubresource */ - att_img->d3d11.format); - } - } - } - _sg.d3d11.cur_pass = 0; - _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; - _sg.d3d11.cur_pipeline = 0; - _sg.d3d11.cur_pipeline_id.id = SG_INVALID_ID; - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - _sg.d3d11.cur_rtvs[i] = 0; - } - _sg.d3d11.cur_dsv = 0; - _sg_d3d11_clear_state(); -} - -_SOKOL_PRIVATE void _sg_d3d11_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { - SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(_sg.d3d11.in_pass); - D3D11_VIEWPORT vp; - vp.TopLeftX = (FLOAT) x; - vp.TopLeftY = (FLOAT) (origin_top_left ? y : (_sg.d3d11.cur_height - (y + h))); - vp.Width = (FLOAT) w; - vp.Height = (FLOAT) h; - vp.MinDepth = 0.0f; - vp.MaxDepth = 1.0f; - _sg_d3d11_RSSetViewports(_sg.d3d11.ctx, 1, &vp); -} - -_SOKOL_PRIVATE void _sg_d3d11_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { - SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(_sg.d3d11.in_pass); - D3D11_RECT rect; - rect.left = x; - rect.top = (origin_top_left ? y : (_sg.d3d11.cur_height - (y + h))); - rect.right = x + w; - rect.bottom = origin_top_left ? (y + h) : (_sg.d3d11.cur_height - y); - _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); -} - -_SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) { - SOKOL_ASSERT(pip); - SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); - SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(_sg.d3d11.in_pass); - SOKOL_ASSERT(pip->d3d11.rs && pip->d3d11.bs && pip->d3d11.dss && pip->d3d11.il); - - _sg.d3d11.cur_pipeline = pip; - _sg.d3d11.cur_pipeline_id.id = pip->slot.id; - _sg.d3d11.use_indexed_draw = (pip->d3d11.index_format != DXGI_FORMAT_UNKNOWN); - _sg.d3d11.use_instanced_draw = pip->cmn.use_instanced_draw; - - _sg_d3d11_RSSetState(_sg.d3d11.ctx, pip->d3d11.rs); - _sg_d3d11_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11.dss, pip->d3d11.stencil_ref); - _sg_d3d11_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11.bs, &pip->cmn.blend_color.r, 0xFFFFFFFF); - _sg_d3d11_IASetPrimitiveTopology(_sg.d3d11.ctx, pip->d3d11.topology); - _sg_d3d11_IASetInputLayout(_sg.d3d11.ctx, pip->d3d11.il); - _sg_d3d11_VSSetShader(_sg.d3d11.ctx, pip->shader->d3d11.vs, NULL, 0); - _sg_d3d11_VSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->d3d11.stage[SG_SHADERSTAGE_VS].cbufs); - _sg_d3d11_PSSetShader(_sg.d3d11.ctx, pip->shader->d3d11.fs, NULL, 0); - _sg_d3d11_PSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->d3d11.stage[SG_SHADERSTAGE_FS].cbufs); -} - -_SOKOL_PRIVATE void _sg_d3d11_apply_bindings( - _sg_pipeline_t* pip, - _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, - _sg_buffer_t* ib, int ib_offset, - _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs) -{ - SOKOL_ASSERT(pip); - SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(_sg.d3d11.in_pass); - - /* gather all the D3D11 resources into arrays */ - ID3D11Buffer* d3d11_ib = ib ? ib->d3d11.buf : 0; - ID3D11Buffer* d3d11_vbs[SG_MAX_SHADERSTAGE_BUFFERS]; - UINT d3d11_vb_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; - ID3D11ShaderResourceView* d3d11_vs_srvs[SG_MAX_SHADERSTAGE_IMAGES]; - ID3D11SamplerState* d3d11_vs_smps[SG_MAX_SHADERSTAGE_IMAGES]; - ID3D11ShaderResourceView* d3d11_fs_srvs[SG_MAX_SHADERSTAGE_IMAGES]; - ID3D11SamplerState* d3d11_fs_smps[SG_MAX_SHADERSTAGE_IMAGES]; - int i; - for (i = 0; i < num_vbs; i++) { - SOKOL_ASSERT(vbs[i]->d3d11.buf); - d3d11_vbs[i] = vbs[i]->d3d11.buf; - d3d11_vb_offsets[i] = (UINT)vb_offsets[i]; - } - for (; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { - d3d11_vbs[i] = 0; - d3d11_vb_offsets[i] = 0; - } - for (i = 0; i < num_vs_imgs; i++) { - SOKOL_ASSERT(vs_imgs[i]->d3d11.srv); - SOKOL_ASSERT(vs_imgs[i]->d3d11.smp); - d3d11_vs_srvs[i] = vs_imgs[i]->d3d11.srv; - d3d11_vs_smps[i] = vs_imgs[i]->d3d11.smp; - } - for (; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { - d3d11_vs_srvs[i] = 0; - d3d11_vs_smps[i] = 0; - } - for (i = 0; i < num_fs_imgs; i++) { - SOKOL_ASSERT(fs_imgs[i]->d3d11.srv); - SOKOL_ASSERT(fs_imgs[i]->d3d11.smp); - d3d11_fs_srvs[i] = fs_imgs[i]->d3d11.srv; - d3d11_fs_smps[i] = fs_imgs[i]->d3d11.smp; - } - for (; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { - d3d11_fs_srvs[i] = 0; - d3d11_fs_smps[i] = 0; - } - - _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_BUFFERS, d3d11_vbs, pip->d3d11.vb_strides, d3d11_vb_offsets); - _sg_d3d11_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, pip->d3d11.index_format, (UINT)ib_offset); - _sg_d3d11_VSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_srvs); - _sg_d3d11_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_smps); - _sg_d3d11_PSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_srvs); - _sg_d3d11_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_smps); -} - -_SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { - SOKOL_ASSERT(_sg.d3d11.ctx && _sg.d3d11.in_pass); - SOKOL_ASSERT(_sg.d3d11.cur_pipeline && _sg.d3d11.cur_pipeline->slot.id == _sg.d3d11.cur_pipeline_id.id); - SOKOL_ASSERT(_sg.d3d11.cur_pipeline->shader && _sg.d3d11.cur_pipeline->shader->slot.id == _sg.d3d11.cur_pipeline->cmn.shader_id.id); - SOKOL_ASSERT(ub_index < _sg.d3d11.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); - SOKOL_ASSERT(data->size == _sg.d3d11.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); - ID3D11Buffer* cb = _sg.d3d11.cur_pipeline->shader->d3d11.stage[stage_index].cbufs[ub_index]; - SOKOL_ASSERT(cb); - _sg_d3d11_UpdateSubresource(_sg.d3d11.ctx, (ID3D11Resource*)cb, 0, NULL, data->ptr, 0, 0); -} - -_SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_instances) { - SOKOL_ASSERT(_sg.d3d11.in_pass); - if (_sg.d3d11.use_indexed_draw) { - if (_sg.d3d11.use_instanced_draw) { - _sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0, 0); - } - else { - _sg_d3d11_DrawIndexed(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element, 0); - } - } - else { - if (_sg.d3d11.use_instanced_draw) { - _sg_d3d11_DrawInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0); - } - else { - _sg_d3d11_Draw(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element); - } - } -} - -_SOKOL_PRIVATE void _sg_d3d11_commit(void) { - SOKOL_ASSERT(!_sg.d3d11.in_pass); -} - -_SOKOL_PRIVATE void _sg_d3d11_update_buffer(_sg_buffer_t* buf, const sg_range* data) { - SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); - SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(buf->d3d11.buf); - D3D11_MAPPED_SUBRESOURCE d3d11_msr; - HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); - if (SUCCEEDED(hr)) { - memcpy(d3d11_msr.pData, data->ptr, data->size); - _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); - } else { - SOKOL_LOG("failed to map buffer while updating!\n"); - } -} - -_SOKOL_PRIVATE int _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { - SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); - SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(buf->d3d11.buf); - D3D11_MAP map_type = new_frame ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE; - D3D11_MAPPED_SUBRESOURCE d3d11_msr; - HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, map_type, 0, &d3d11_msr); - if (SUCCEEDED(hr)) { - uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData + buf->cmn.append_pos; - memcpy(dst_ptr, data->ptr, data->size); - _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); - } else { - SOKOL_LOG("failed to map buffer while appending!\n"); - } - /* NOTE: this alignment is a requirement from WebGPU, but we want identical behaviour across all backend */ - return _sg_roundup((int)data->size, 4); -} - -_SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data* data) { - SOKOL_ASSERT(img && data); - SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(img->d3d11.tex2d || img->d3d11.tex3d); - ID3D11Resource* d3d11_res = 0; - if (img->d3d11.tex3d) { - d3d11_res = (ID3D11Resource*) img->d3d11.tex3d; - } - else { - d3d11_res = (ID3D11Resource*) img->d3d11.tex2d; - } - SOKOL_ASSERT(d3d11_res); - const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; - const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1; - UINT subres_index = 0; - HRESULT hr; - D3D11_MAPPED_SUBRESOURCE d3d11_msr; - for (int face_index = 0; face_index < num_faces; face_index++) { - for (int slice_index = 0; slice_index < num_slices; slice_index++) { - for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { - SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); - const int mip_width = ((img->cmn.width>>mip_index)>0) ? img->cmn.width>>mip_index : 1; - const int mip_height = ((img->cmn.height>>mip_index)>0) ? img->cmn.height>>mip_index : 1; - const int src_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); - const sg_range* subimg_data = &(data->subimage[face_index][mip_index]); - const size_t slice_size = subimg_data->size / (size_t)num_slices; - const size_t slice_offset = slice_size * (size_t)slice_index; - const uint8_t* slice_ptr = ((const uint8_t*)subimg_data->ptr) + slice_offset; - hr = _sg_d3d11_Map(_sg.d3d11.ctx, d3d11_res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); - if (SUCCEEDED(hr)) { - /* FIXME: need to handle difference in depth-pitch for 3D textures as well! */ - if (src_pitch == (int)d3d11_msr.RowPitch) { - memcpy(d3d11_msr.pData, slice_ptr, slice_size); - } - else { - SOKOL_ASSERT(src_pitch < (int)d3d11_msr.RowPitch); - const uint8_t* src_ptr = slice_ptr; - uint8_t* dst_ptr = (uint8_t*) d3d11_msr.pData; - for (int row_index = 0; row_index < mip_height; row_index++) { - memcpy(dst_ptr, src_ptr, (size_t)src_pitch); - src_ptr += src_pitch; - dst_ptr += d3d11_msr.RowPitch; - } - } - _sg_d3d11_Unmap(_sg.d3d11.ctx, d3d11_res, subres_index); - } else { - SOKOL_LOG("failed to map texture!\n"); - } - } - } - } -} - -/*== METAL BACKEND IMPLEMENTATION ============================================*/ -#elif defined(SOKOL_METAL) - -#if __has_feature(objc_arc) -#define _SG_OBJC_RETAIN(obj) { } -#define _SG_OBJC_RELEASE(obj) { obj = nil; } -#define _SG_OBJC_RELEASE_WITH_NULL(obj) { obj = [NSNull null]; } -#else -#define _SG_OBJC_RETAIN(obj) { [obj retain]; } -#define _SG_OBJC_RELEASE(obj) { [obj release]; obj = nil; } -#define _SG_OBJC_RELEASE_WITH_NULL(obj) { [obj release]; obj = [NSNull null]; } -#endif - -/*-- enum translation functions ----------------------------------------------*/ -_SOKOL_PRIVATE MTLLoadAction _sg_mtl_load_action(sg_action a) { - switch (a) { - case SG_ACTION_CLEAR: return MTLLoadActionClear; - case SG_ACTION_LOAD: return MTLLoadActionLoad; - case SG_ACTION_DONTCARE: return MTLLoadActionDontCare; - default: SOKOL_UNREACHABLE; return (MTLLoadAction)0; - } -} - -_SOKOL_PRIVATE MTLResourceOptions _sg_mtl_buffer_resource_options(sg_usage usg) { - switch (usg) { - case SG_USAGE_IMMUTABLE: - #if defined(_SG_TARGET_MACOS) - return MTLResourceStorageModeManaged; - #else - return MTLResourceStorageModeShared; - #endif - case SG_USAGE_DYNAMIC: - case SG_USAGE_STREAM: - #if defined(_SG_TARGET_MACOS) - return MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeManaged; - #else - return MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeShared; - #endif - default: - SOKOL_UNREACHABLE; - return 0; - } -} - -_SOKOL_PRIVATE MTLVertexStepFunction _sg_mtl_step_function(sg_vertex_step step) { - switch (step) { - case SG_VERTEXSTEP_PER_VERTEX: return MTLVertexStepFunctionPerVertex; - case SG_VERTEXSTEP_PER_INSTANCE: return MTLVertexStepFunctionPerInstance; - default: SOKOL_UNREACHABLE; return (MTLVertexStepFunction)0; - } -} - -_SOKOL_PRIVATE MTLVertexFormat _sg_mtl_vertex_format(sg_vertex_format fmt) { - switch (fmt) { - case SG_VERTEXFORMAT_FLOAT: return MTLVertexFormatFloat; - case SG_VERTEXFORMAT_FLOAT2: return MTLVertexFormatFloat2; - case SG_VERTEXFORMAT_FLOAT3: return MTLVertexFormatFloat3; - case SG_VERTEXFORMAT_FLOAT4: return MTLVertexFormatFloat4; - case SG_VERTEXFORMAT_BYTE4: return MTLVertexFormatChar4; - case SG_VERTEXFORMAT_BYTE4N: return MTLVertexFormatChar4Normalized; - case SG_VERTEXFORMAT_UBYTE4: return MTLVertexFormatUChar4; - case SG_VERTEXFORMAT_UBYTE4N: return MTLVertexFormatUChar4Normalized; - case SG_VERTEXFORMAT_SHORT2: return MTLVertexFormatShort2; - case SG_VERTEXFORMAT_SHORT2N: return MTLVertexFormatShort2Normalized; - case SG_VERTEXFORMAT_USHORT2N: return MTLVertexFormatUShort2Normalized; - case SG_VERTEXFORMAT_SHORT4: return MTLVertexFormatShort4; - case SG_VERTEXFORMAT_SHORT4N: return MTLVertexFormatShort4Normalized; - case SG_VERTEXFORMAT_USHORT4N: return MTLVertexFormatUShort4Normalized; - case SG_VERTEXFORMAT_UINT10_N2: return MTLVertexFormatUInt1010102Normalized; - default: SOKOL_UNREACHABLE; return (MTLVertexFormat)0; - } -} - -_SOKOL_PRIVATE MTLPrimitiveType _sg_mtl_primitive_type(sg_primitive_type t) { - switch (t) { - case SG_PRIMITIVETYPE_POINTS: return MTLPrimitiveTypePoint; - case SG_PRIMITIVETYPE_LINES: return MTLPrimitiveTypeLine; - case SG_PRIMITIVETYPE_LINE_STRIP: return MTLPrimitiveTypeLineStrip; - case SG_PRIMITIVETYPE_TRIANGLES: return MTLPrimitiveTypeTriangle; - case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return MTLPrimitiveTypeTriangleStrip; - default: SOKOL_UNREACHABLE; return (MTLPrimitiveType)0; - } -} - -_SOKOL_PRIVATE MTLPixelFormat _sg_mtl_pixel_format(sg_pixel_format fmt) { - switch (fmt) { - case SG_PIXELFORMAT_R8: return MTLPixelFormatR8Unorm; - case SG_PIXELFORMAT_R8SN: return MTLPixelFormatR8Snorm; - case SG_PIXELFORMAT_R8UI: return MTLPixelFormatR8Uint; - case SG_PIXELFORMAT_R8SI: return MTLPixelFormatR8Sint; - case SG_PIXELFORMAT_R16: return MTLPixelFormatR16Unorm; - case SG_PIXELFORMAT_R16SN: return MTLPixelFormatR16Snorm; - case SG_PIXELFORMAT_R16UI: return MTLPixelFormatR16Uint; - case SG_PIXELFORMAT_R16SI: return MTLPixelFormatR16Sint; - case SG_PIXELFORMAT_R16F: return MTLPixelFormatR16Float; - case SG_PIXELFORMAT_RG8: return MTLPixelFormatRG8Unorm; - case SG_PIXELFORMAT_RG8SN: return MTLPixelFormatRG8Snorm; - case SG_PIXELFORMAT_RG8UI: return MTLPixelFormatRG8Uint; - case SG_PIXELFORMAT_RG8SI: return MTLPixelFormatRG8Sint; - case SG_PIXELFORMAT_R32UI: return MTLPixelFormatR32Uint; - case SG_PIXELFORMAT_R32SI: return MTLPixelFormatR32Sint; - case SG_PIXELFORMAT_R32F: return MTLPixelFormatR32Float; - case SG_PIXELFORMAT_RG16: return MTLPixelFormatRG16Unorm; - case SG_PIXELFORMAT_RG16SN: return MTLPixelFormatRG16Snorm; - case SG_PIXELFORMAT_RG16UI: return MTLPixelFormatRG16Uint; - case SG_PIXELFORMAT_RG16SI: return MTLPixelFormatRG16Sint; - case SG_PIXELFORMAT_RG16F: return MTLPixelFormatRG16Float; - case SG_PIXELFORMAT_RGBA8: return MTLPixelFormatRGBA8Unorm; - case SG_PIXELFORMAT_RGBA8SN: return MTLPixelFormatRGBA8Snorm; - case SG_PIXELFORMAT_RGBA8UI: return MTLPixelFormatRGBA8Uint; - case SG_PIXELFORMAT_RGBA8SI: return MTLPixelFormatRGBA8Sint; - case SG_PIXELFORMAT_BGRA8: return MTLPixelFormatBGRA8Unorm; - case SG_PIXELFORMAT_RGB10A2: return MTLPixelFormatRGB10A2Unorm; - case SG_PIXELFORMAT_RG11B10F: return MTLPixelFormatRG11B10Float; - case SG_PIXELFORMAT_RG32UI: return MTLPixelFormatRG32Uint; - case SG_PIXELFORMAT_RG32SI: return MTLPixelFormatRG32Sint; - case SG_PIXELFORMAT_RG32F: return MTLPixelFormatRG32Float; - case SG_PIXELFORMAT_RGBA16: return MTLPixelFormatRGBA16Unorm; - case SG_PIXELFORMAT_RGBA16SN: return MTLPixelFormatRGBA16Snorm; - case SG_PIXELFORMAT_RGBA16UI: return MTLPixelFormatRGBA16Uint; - case SG_PIXELFORMAT_RGBA16SI: return MTLPixelFormatRGBA16Sint; - case SG_PIXELFORMAT_RGBA16F: return MTLPixelFormatRGBA16Float; - case SG_PIXELFORMAT_RGBA32UI: return MTLPixelFormatRGBA32Uint; - case SG_PIXELFORMAT_RGBA32SI: return MTLPixelFormatRGBA32Sint; - case SG_PIXELFORMAT_RGBA32F: return MTLPixelFormatRGBA32Float; - case SG_PIXELFORMAT_DEPTH: return MTLPixelFormatDepth32Float; - case SG_PIXELFORMAT_DEPTH_STENCIL: return MTLPixelFormatDepth32Float_Stencil8; - #if defined(_SG_TARGET_MACOS) - case SG_PIXELFORMAT_BC1_RGBA: return MTLPixelFormatBC1_RGBA; - case SG_PIXELFORMAT_BC2_RGBA: return MTLPixelFormatBC2_RGBA; - case SG_PIXELFORMAT_BC3_RGBA: return MTLPixelFormatBC3_RGBA; - case SG_PIXELFORMAT_BC4_R: return MTLPixelFormatBC4_RUnorm; - case SG_PIXELFORMAT_BC4_RSN: return MTLPixelFormatBC4_RSnorm; - case SG_PIXELFORMAT_BC5_RG: return MTLPixelFormatBC5_RGUnorm; - case SG_PIXELFORMAT_BC5_RGSN: return MTLPixelFormatBC5_RGSnorm; - case SG_PIXELFORMAT_BC6H_RGBF: return MTLPixelFormatBC6H_RGBFloat; - case SG_PIXELFORMAT_BC6H_RGBUF: return MTLPixelFormatBC6H_RGBUfloat; - case SG_PIXELFORMAT_BC7_RGBA: return MTLPixelFormatBC7_RGBAUnorm; - #else - case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return MTLPixelFormatPVRTC_RGB_2BPP; - case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return MTLPixelFormatPVRTC_RGB_4BPP; - case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return MTLPixelFormatPVRTC_RGBA_2BPP; - case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return MTLPixelFormatPVRTC_RGBA_4BPP; - case SG_PIXELFORMAT_ETC2_RGB8: return MTLPixelFormatETC2_RGB8; - case SG_PIXELFORMAT_ETC2_RGB8A1: return MTLPixelFormatETC2_RGB8A1; - case SG_PIXELFORMAT_ETC2_RGBA8: return MTLPixelFormatEAC_RGBA8; - case SG_PIXELFORMAT_ETC2_RG11: return MTLPixelFormatEAC_RG11Unorm; - case SG_PIXELFORMAT_ETC2_RG11SN: return MTLPixelFormatEAC_RG11Snorm; - #endif - default: return MTLPixelFormatInvalid; - } -} - -_SOKOL_PRIVATE MTLColorWriteMask _sg_mtl_color_write_mask(sg_color_mask m) { - MTLColorWriteMask mtl_mask = MTLColorWriteMaskNone; - if (m & SG_COLORMASK_R) { - mtl_mask |= MTLColorWriteMaskRed; - } - if (m & SG_COLORMASK_G) { - mtl_mask |= MTLColorWriteMaskGreen; - } - if (m & SG_COLORMASK_B) { - mtl_mask |= MTLColorWriteMaskBlue; - } - if (m & SG_COLORMASK_A) { - mtl_mask |= MTLColorWriteMaskAlpha; - } - return mtl_mask; -} - -_SOKOL_PRIVATE MTLBlendOperation _sg_mtl_blend_op(sg_blend_op op) { - switch (op) { - case SG_BLENDOP_ADD: return MTLBlendOperationAdd; - case SG_BLENDOP_SUBTRACT: return MTLBlendOperationSubtract; - case SG_BLENDOP_REVERSE_SUBTRACT: return MTLBlendOperationReverseSubtract; - default: SOKOL_UNREACHABLE; return (MTLBlendOperation)0; - } -} - -_SOKOL_PRIVATE MTLBlendFactor _sg_mtl_blend_factor(sg_blend_factor f) { - switch (f) { - case SG_BLENDFACTOR_ZERO: return MTLBlendFactorZero; - case SG_BLENDFACTOR_ONE: return MTLBlendFactorOne; - case SG_BLENDFACTOR_SRC_COLOR: return MTLBlendFactorSourceColor; - case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor; - case SG_BLENDFACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha; - case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha; - case SG_BLENDFACTOR_DST_COLOR: return MTLBlendFactorDestinationColor; - case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor; - case SG_BLENDFACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha; - case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha; - case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return MTLBlendFactorSourceAlphaSaturated; - case SG_BLENDFACTOR_BLEND_COLOR: return MTLBlendFactorBlendColor; - case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return MTLBlendFactorOneMinusBlendColor; - case SG_BLENDFACTOR_BLEND_ALPHA: return MTLBlendFactorBlendAlpha; - case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return MTLBlendFactorOneMinusBlendAlpha; - default: SOKOL_UNREACHABLE; return (MTLBlendFactor)0; - } -} - -_SOKOL_PRIVATE MTLCompareFunction _sg_mtl_compare_func(sg_compare_func f) { - switch (f) { - case SG_COMPAREFUNC_NEVER: return MTLCompareFunctionNever; - case SG_COMPAREFUNC_LESS: return MTLCompareFunctionLess; - case SG_COMPAREFUNC_EQUAL: return MTLCompareFunctionEqual; - case SG_COMPAREFUNC_LESS_EQUAL: return MTLCompareFunctionLessEqual; - case SG_COMPAREFUNC_GREATER: return MTLCompareFunctionGreater; - case SG_COMPAREFUNC_NOT_EQUAL: return MTLCompareFunctionNotEqual; - case SG_COMPAREFUNC_GREATER_EQUAL: return MTLCompareFunctionGreaterEqual; - case SG_COMPAREFUNC_ALWAYS: return MTLCompareFunctionAlways; - default: SOKOL_UNREACHABLE; return (MTLCompareFunction)0; - } -} - -_SOKOL_PRIVATE MTLStencilOperation _sg_mtl_stencil_op(sg_stencil_op op) { - switch (op) { - case SG_STENCILOP_KEEP: return MTLStencilOperationKeep; - case SG_STENCILOP_ZERO: return MTLStencilOperationZero; - case SG_STENCILOP_REPLACE: return MTLStencilOperationReplace; - case SG_STENCILOP_INCR_CLAMP: return MTLStencilOperationIncrementClamp; - case SG_STENCILOP_DECR_CLAMP: return MTLStencilOperationDecrementClamp; - case SG_STENCILOP_INVERT: return MTLStencilOperationInvert; - case SG_STENCILOP_INCR_WRAP: return MTLStencilOperationIncrementWrap; - case SG_STENCILOP_DECR_WRAP: return MTLStencilOperationDecrementWrap; - default: SOKOL_UNREACHABLE; return (MTLStencilOperation)0; - } -} - -_SOKOL_PRIVATE MTLCullMode _sg_mtl_cull_mode(sg_cull_mode m) { - switch (m) { - case SG_CULLMODE_NONE: return MTLCullModeNone; - case SG_CULLMODE_FRONT: return MTLCullModeFront; - case SG_CULLMODE_BACK: return MTLCullModeBack; - default: SOKOL_UNREACHABLE; return (MTLCullMode)0; - } -} - -_SOKOL_PRIVATE MTLWinding _sg_mtl_winding(sg_face_winding w) { - switch (w) { - case SG_FACEWINDING_CW: return MTLWindingClockwise; - case SG_FACEWINDING_CCW: return MTLWindingCounterClockwise; - default: SOKOL_UNREACHABLE; return (MTLWinding)0; - } -} - -_SOKOL_PRIVATE MTLIndexType _sg_mtl_index_type(sg_index_type t) { - switch (t) { - case SG_INDEXTYPE_UINT16: return MTLIndexTypeUInt16; - case SG_INDEXTYPE_UINT32: return MTLIndexTypeUInt32; - default: SOKOL_UNREACHABLE; return (MTLIndexType)0; - } -} - -_SOKOL_PRIVATE int _sg_mtl_index_size(sg_index_type t) { - switch (t) { - case SG_INDEXTYPE_NONE: return 0; - case SG_INDEXTYPE_UINT16: return 2; - case SG_INDEXTYPE_UINT32: return 4; - default: SOKOL_UNREACHABLE; return 0; - } -} - -_SOKOL_PRIVATE MTLTextureType _sg_mtl_texture_type(sg_image_type t) { - switch (t) { - case SG_IMAGETYPE_2D: return MTLTextureType2D; - case SG_IMAGETYPE_CUBE: return MTLTextureTypeCube; - case SG_IMAGETYPE_3D: return MTLTextureType3D; - case SG_IMAGETYPE_ARRAY: return MTLTextureType2DArray; - default: SOKOL_UNREACHABLE; return (MTLTextureType)0; - } -} - -_SOKOL_PRIVATE bool _sg_mtl_is_pvrtc(sg_pixel_format fmt) { - switch (fmt) { - case SG_PIXELFORMAT_PVRTC_RGB_2BPP: - case SG_PIXELFORMAT_PVRTC_RGB_4BPP: - case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: - case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: - return true; - default: - return false; - } -} - -_SOKOL_PRIVATE MTLSamplerAddressMode _sg_mtl_address_mode(sg_wrap w) { - switch (w) { - case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat; - case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; - #if defined(_SG_TARGET_MACOS) - case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToBorderColor; - #else - /* clamp-to-border not supported on iOS, fall back to clamp-to-edge */ - case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToEdge; - #endif - case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; - default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0; - } -} - -#if defined(_SG_TARGET_MACOS) -_SOKOL_PRIVATE MTLSamplerBorderColor _sg_mtl_border_color(sg_border_color c) { - switch (c) { - case SG_BORDERCOLOR_TRANSPARENT_BLACK: return MTLSamplerBorderColorTransparentBlack; - case SG_BORDERCOLOR_OPAQUE_BLACK: return MTLSamplerBorderColorOpaqueBlack; - case SG_BORDERCOLOR_OPAQUE_WHITE: return MTLSamplerBorderColorOpaqueWhite; - default: SOKOL_UNREACHABLE; return (MTLSamplerBorderColor)0; - } -} -#endif - -_SOKOL_PRIVATE MTLSamplerMinMagFilter _sg_mtl_minmag_filter(sg_filter f) { - switch (f) { - case SG_FILTER_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_LINEAR: - return MTLSamplerMinMagFilterNearest; - case SG_FILTER_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: - case SG_FILTER_LINEAR_MIPMAP_LINEAR: - return MTLSamplerMinMagFilterLinear; - default: - SOKOL_UNREACHABLE; return (MTLSamplerMinMagFilter)0; - } -} - -_SOKOL_PRIVATE MTLSamplerMipFilter _sg_mtl_mip_filter(sg_filter f) { - switch (f) { - case SG_FILTER_NEAREST: - case SG_FILTER_LINEAR: - return MTLSamplerMipFilterNotMipmapped; - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: - return MTLSamplerMipFilterNearest; - case SG_FILTER_NEAREST_MIPMAP_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_LINEAR: - return MTLSamplerMipFilterLinear; - default: - SOKOL_UNREACHABLE; return (MTLSamplerMipFilter)0; - } -} - -/*-- a pool for all Metal resource objects, with deferred release queue -------*/ - -_SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { - _sg.mtl.idpool.num_slots = 2 * - ( - 2 * desc->buffer_pool_size + - 5 * desc->image_pool_size + - 4 * desc->shader_pool_size + - 2 * desc->pipeline_pool_size + - desc->pass_pool_size - ); - _sg.mtl.idpool.pool = [NSMutableArray arrayWithCapacity:(NSUInteger)_sg.mtl.idpool.num_slots]; - _SG_OBJC_RETAIN(_sg.mtl.idpool.pool); - NSNull* null = [NSNull null]; - for (int i = 0; i < _sg.mtl.idpool.num_slots; i++) { - [_sg.mtl.idpool.pool addObject:null]; - } - SOKOL_ASSERT([_sg.mtl.idpool.pool count] == (NSUInteger)_sg.mtl.idpool.num_slots); - /* a queue of currently free slot indices */ - _sg.mtl.idpool.free_queue_top = 0; - _sg.mtl.idpool.free_queue = (int*)SOKOL_MALLOC((size_t)_sg.mtl.idpool.num_slots * sizeof(int)); - /* pool slot 0 is reserved! */ - for (int i = _sg.mtl.idpool.num_slots-1; i >= 1; i--) { - _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = i; - } - /* a circular queue which holds release items (frame index - when a resource is to be released, and the resource's - pool index - */ - _sg.mtl.idpool.release_queue_front = 0; - _sg.mtl.idpool.release_queue_back = 0; - _sg.mtl.idpool.release_queue = (_sg_mtl_release_item_t*)SOKOL_MALLOC((size_t)_sg.mtl.idpool.num_slots * sizeof(_sg_mtl_release_item_t)); - for (int i = 0; i < _sg.mtl.idpool.num_slots; i++) { - _sg.mtl.idpool.release_queue[i].frame_index = 0; - _sg.mtl.idpool.release_queue[i].slot_index = _SG_MTL_INVALID_SLOT_INDEX; - } -} - -_SOKOL_PRIVATE void _sg_mtl_destroy_pool(void) { - SOKOL_FREE(_sg.mtl.idpool.release_queue); _sg.mtl.idpool.release_queue = 0; - SOKOL_FREE(_sg.mtl.idpool.free_queue); _sg.mtl.idpool.free_queue = 0; - _SG_OBJC_RELEASE(_sg.mtl.idpool.pool); -} - -/* get a new free resource pool slot */ -_SOKOL_PRIVATE int _sg_mtl_alloc_pool_slot(void) { - SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top > 0); - const int slot_index = _sg.mtl.idpool.free_queue[--_sg.mtl.idpool.free_queue_top]; - SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); - return slot_index; -} - -/* put a free resource pool slot back into the free-queue */ -_SOKOL_PRIVATE void _sg_mtl_free_pool_slot(int slot_index) { - SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top < _sg.mtl.idpool.num_slots); - SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); - _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = slot_index; -} - -/* add an MTLResource to the pool, return pool index or 0 if input was 'nil' */ -_SOKOL_PRIVATE int _sg_mtl_add_resource(id res) { - if (nil == res) { - return _SG_MTL_INVALID_SLOT_INDEX; - } - const int slot_index = _sg_mtl_alloc_pool_slot(); - SOKOL_ASSERT([NSNull null] == _sg.mtl.idpool.pool[(NSUInteger)slot_index]); - _sg.mtl.idpool.pool[(NSUInteger)slot_index] = res; - return slot_index; -} - -/* mark an MTLResource for release, this will put the resource into the - deferred-release queue, and the resource will then be released N frames later, - the special pool index 0 will be ignored (this means that a nil - value was provided to _sg_mtl_add_resource() -*/ -_SOKOL_PRIVATE void _sg_mtl_release_resource(uint32_t frame_index, int slot_index) { - if (slot_index == _SG_MTL_INVALID_SLOT_INDEX) { - return; - } - SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); - SOKOL_ASSERT([NSNull null] != _sg.mtl.idpool.pool[(NSUInteger)slot_index]); - int release_index = _sg.mtl.idpool.release_queue_front++; - if (_sg.mtl.idpool.release_queue_front >= _sg.mtl.idpool.num_slots) { - /* wrap-around */ - _sg.mtl.idpool.release_queue_front = 0; - } - /* release queue full? */ - SOKOL_ASSERT(_sg.mtl.idpool.release_queue_front != _sg.mtl.idpool.release_queue_back); - SOKOL_ASSERT(0 == _sg.mtl.idpool.release_queue[release_index].frame_index); - const uint32_t safe_to_release_frame_index = frame_index + SG_NUM_INFLIGHT_FRAMES + 1; - _sg.mtl.idpool.release_queue[release_index].frame_index = safe_to_release_frame_index; - _sg.mtl.idpool.release_queue[release_index].slot_index = slot_index; -} - -/* run garbage-collection pass on all resources in the release-queue */ -_SOKOL_PRIVATE void _sg_mtl_garbage_collect(uint32_t frame_index) { - while (_sg.mtl.idpool.release_queue_back != _sg.mtl.idpool.release_queue_front) { - if (frame_index < _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index) { - /* don't need to check further, release-items past this are too young */ - break; - } - /* safe to release this resource */ - const int slot_index = _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index; - SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); - SOKOL_ASSERT(_sg.mtl.idpool.pool[(NSUInteger)slot_index] != [NSNull null]); - _SG_OBJC_RELEASE_WITH_NULL(_sg.mtl.idpool.pool[(NSUInteger)slot_index]); - /* put the now free pool index back on the free queue */ - _sg_mtl_free_pool_slot(slot_index); - /* reset the release queue slot and advance the back index */ - _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index = 0; - _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index = _SG_MTL_INVALID_SLOT_INDEX; - _sg.mtl.idpool.release_queue_back++; - if (_sg.mtl.idpool.release_queue_back >= _sg.mtl.idpool.num_slots) { - /* wrap-around */ - _sg.mtl.idpool.release_queue_back = 0; - } - } -} - -_SOKOL_PRIVATE id _sg_mtl_id(int slot_index) { - return _sg.mtl.idpool.pool[(NSUInteger)slot_index]; -} - -_SOKOL_PRIVATE void _sg_mtl_init_sampler_cache(const sg_desc* desc) { - SOKOL_ASSERT(desc->sampler_cache_size > 0); - _sg_smpcache_init(&_sg.mtl.sampler_cache, desc->sampler_cache_size); -} - -/* destroy the sampler cache, and release all sampler objects */ -_SOKOL_PRIVATE void _sg_mtl_destroy_sampler_cache(uint32_t frame_index) { - SOKOL_ASSERT(_sg.mtl.sampler_cache.items); - SOKOL_ASSERT(_sg.mtl.sampler_cache.num_items <= _sg.mtl.sampler_cache.capacity); - for (int i = 0; i < _sg.mtl.sampler_cache.num_items; i++) { - _sg_mtl_release_resource(frame_index, (int)_sg_smpcache_sampler(&_sg.mtl.sampler_cache, i)); - } - _sg_smpcache_discard(&_sg.mtl.sampler_cache); -} - -/* - create and add an MTLSamplerStateObject and return its resource pool index, - reuse identical sampler state if one exists -*/ -_SOKOL_PRIVATE int _sg_mtl_create_sampler(id mtl_device, const sg_image_desc* img_desc) { - SOKOL_ASSERT(img_desc); - int index = _sg_smpcache_find_item(&_sg.mtl.sampler_cache, img_desc); - if (index >= 0) { - /* reuse existing sampler */ - return (int)_sg_smpcache_sampler(&_sg.mtl.sampler_cache, index); - } - else { - /* create a new Metal sampler state object and add to sampler cache */ - MTLSamplerDescriptor* mtl_desc = [[MTLSamplerDescriptor alloc] init]; - mtl_desc.sAddressMode = _sg_mtl_address_mode(img_desc->wrap_u); - mtl_desc.tAddressMode = _sg_mtl_address_mode(img_desc->wrap_v); - if (SG_IMAGETYPE_3D == img_desc->type) { - mtl_desc.rAddressMode = _sg_mtl_address_mode(img_desc->wrap_w); - } - #if defined(_SG_TARGET_MACOS) - mtl_desc.borderColor = _sg_mtl_border_color(img_desc->border_color); - #endif - mtl_desc.minFilter = _sg_mtl_minmag_filter(img_desc->min_filter); - mtl_desc.magFilter = _sg_mtl_minmag_filter(img_desc->mag_filter); - mtl_desc.mipFilter = _sg_mtl_mip_filter(img_desc->min_filter); - mtl_desc.lodMinClamp = img_desc->min_lod; - mtl_desc.lodMaxClamp = img_desc->max_lod; - mtl_desc.maxAnisotropy = img_desc->max_anisotropy; - mtl_desc.normalizedCoordinates = YES; - id mtl_sampler = [mtl_device newSamplerStateWithDescriptor:mtl_desc]; - _SG_OBJC_RELEASE(mtl_desc); - int sampler_handle = _sg_mtl_add_resource(mtl_sampler); - _sg_smpcache_add_item(&_sg.mtl.sampler_cache, img_desc, (uintptr_t)sampler_handle); - return sampler_handle; - } -} - -_SOKOL_PRIVATE void _sg_mtl_clear_state_cache(void) { - memset(&_sg.mtl.state_cache, 0, sizeof(_sg.mtl.state_cache)); -} - -/* https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */ -_SOKOL_PRIVATE void _sg_mtl_init_caps(void) { - #if defined(_SG_TARGET_MACOS) - _sg.backend = SG_BACKEND_METAL_MACOS; - #elif defined(_SG_TARGET_IOS) - #if defined(_SG_TARGET_IOS_SIMULATOR) - _sg.backend = SG_BACKEND_METAL_SIMULATOR; - #else - _sg.backend = SG_BACKEND_METAL_IOS; - #endif - #endif - _sg.features.instancing = true; - _sg.features.origin_top_left = true; - _sg.features.multiple_render_targets = true; - _sg.features.msaa_render_targets = true; - _sg.features.imagetype_3d = true; - _sg.features.imagetype_array = true; - #if defined(_SG_TARGET_MACOS) - _sg.features.image_clamp_to_border = true; - #else - _sg.features.image_clamp_to_border = false; - #endif - _sg.features.mrt_independent_blend_state = true; - _sg.features.mrt_independent_write_mask = true; - - #if defined(_SG_TARGET_MACOS) - _sg.limits.max_image_size_2d = 16 * 1024; - _sg.limits.max_image_size_cube = 16 * 1024; - _sg.limits.max_image_size_3d = 2 * 1024; - _sg.limits.max_image_size_array = 16 * 1024; - _sg.limits.max_image_array_layers = 2 * 1024; - #else - /* newer iOS devices support 16k textures */ - _sg.limits.max_image_size_2d = 8 * 1024; - _sg.limits.max_image_size_cube = 8 * 1024; - _sg.limits.max_image_size_3d = 2 * 1024; - _sg.limits.max_image_size_array = 8 * 1024; - _sg.limits.max_image_array_layers = 2 * 1024; - #endif - _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; - - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8SN]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); - #if defined(_SG_TARGET_MACOS) - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); - #else - _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_R16]); - _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_R16SN]); - #endif - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8SN]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); - #if defined(_SG_TARGET_MACOS) - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); - #else - _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_R32F]); - #endif - #if defined(_SG_TARGET_MACOS) - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); - #else - _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RG16]); - _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RG16SN]); - #endif - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); - #if defined(_SG_TARGET_MACOS) - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); - #else - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); - #endif - #if defined(_SG_TARGET_MACOS) - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); - #else - _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG32F]); - #endif - #if defined(_SG_TARGET_MACOS) - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); - #else - _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RGBA16]); - _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); - #endif - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - #if defined(_SG_TARGET_MACOS) - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - #else - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - #endif - _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); - _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); - #if defined(_SG_TARGET_MACOS) - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); - #else - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_2BPP]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_4BPP]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_2BPP]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_4BPP]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11SN]); - #endif -} - -/*-- main Metal backend state and functions ----------------------------------*/ -_SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) { - /* assume already zero-initialized */ - SOKOL_ASSERT(desc); - SOKOL_ASSERT(desc->context.metal.device); - SOKOL_ASSERT(desc->context.metal.renderpass_descriptor_cb || desc->context.metal.renderpass_descriptor_userdata_cb); - SOKOL_ASSERT(desc->context.metal.drawable_cb || desc->context.metal.drawable_userdata_cb); - SOKOL_ASSERT(desc->uniform_buffer_size > 0); - _sg_mtl_init_pool(desc); - _sg_mtl_init_sampler_cache(desc); - _sg_mtl_clear_state_cache(); - _sg.mtl.valid = true; - _sg.mtl.renderpass_descriptor_cb = desc->context.metal.renderpass_descriptor_cb; - _sg.mtl.renderpass_descriptor_userdata_cb = desc->context.metal.renderpass_descriptor_userdata_cb; - _sg.mtl.drawable_cb = desc->context.metal.drawable_cb; - _sg.mtl.drawable_userdata_cb = desc->context.metal.drawable_userdata_cb; - _sg.mtl.user_data = desc->context.metal.user_data; - _sg.mtl.frame_index = 1; - _sg.mtl.ub_size = desc->uniform_buffer_size; - _sg.mtl.sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES); - _sg.mtl.device = (__bridge id) desc->context.metal.device; - _sg.mtl.cmd_queue = [_sg.mtl.device newCommandQueue]; - for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { - _sg.mtl.uniform_buffers[i] = [_sg.mtl.device - newBufferWithLength:(NSUInteger)_sg.mtl.ub_size - options:MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeShared - ]; - } - _sg_mtl_init_caps(); -} - -_SOKOL_PRIVATE void _sg_mtl_discard_backend(void) { - SOKOL_ASSERT(_sg.mtl.valid); - /* wait for the last frame to finish */ - for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { - dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); - } - /* semaphore must be "relinquished" before destruction */ - for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { - dispatch_semaphore_signal(_sg.mtl.sem); - } - _sg_mtl_destroy_sampler_cache(_sg.mtl.frame_index); - _sg_mtl_garbage_collect(_sg.mtl.frame_index + SG_NUM_INFLIGHT_FRAMES + 2); - _sg_mtl_destroy_pool(); - _sg.mtl.valid = false; - - _SG_OBJC_RELEASE(_sg.mtl.sem); - _SG_OBJC_RELEASE(_sg.mtl.device); - _SG_OBJC_RELEASE(_sg.mtl.cmd_queue); - for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { - _SG_OBJC_RELEASE(_sg.mtl.uniform_buffers[i]); - } - /* NOTE: MTLCommandBuffer and MTLRenderCommandEncoder are auto-released */ - _sg.mtl.cmd_buffer = nil; - _sg.mtl.cmd_encoder = nil; -} - -_SOKOL_PRIVATE void _sg_mtl_bind_uniform_buffers(void) { - SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); - for (int slot = 0; slot < SG_MAX_SHADERSTAGE_UBS; slot++) { - [_sg.mtl.cmd_encoder - setVertexBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] - offset:0 - atIndex:(NSUInteger)slot]; - [_sg.mtl.cmd_encoder - setFragmentBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] - offset:0 - atIndex:(NSUInteger)slot]; - } -} - -_SOKOL_PRIVATE void _sg_mtl_reset_state_cache(void) { - _sg_mtl_clear_state_cache(); - - /* need to restore the uniform buffer binding (normally happens in - _sg_mtl_begin_pass() - */ - if (nil != _sg.mtl.cmd_encoder) { - _sg_mtl_bind_uniform_buffers(); - } -} - -_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_mtl_destroy_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); - /* empty */ -} - -_SOKOL_PRIVATE void _sg_mtl_activate_context(_sg_context_t* ctx) { - _SOKOL_UNUSED(ctx); - _sg_mtl_clear_state_cache(); -} - -_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { - SOKOL_ASSERT(buf && desc); - _sg_buffer_common_init(&buf->cmn, desc); - const bool injected = (0 != desc->mtl_buffers[0]); - MTLResourceOptions mtl_options = _sg_mtl_buffer_resource_options(buf->cmn.usage); - for (int slot = 0; slot < buf->cmn.num_slots; slot++) { - id mtl_buf; - if (injected) { - SOKOL_ASSERT(desc->mtl_buffers[slot]); - mtl_buf = (__bridge id) desc->mtl_buffers[slot]; - } - else { - if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { - SOKOL_ASSERT(desc->data.ptr); - mtl_buf = [_sg.mtl.device newBufferWithBytes:desc->data.ptr length:(NSUInteger)buf->cmn.size options:mtl_options]; - } - else { - mtl_buf = [_sg.mtl.device newBufferWithLength:(NSUInteger)buf->cmn.size options:mtl_options]; - } - } - buf->mtl.buf[slot] = _sg_mtl_add_resource(mtl_buf); - } - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_mtl_destroy_buffer(_sg_buffer_t* buf) { - SOKOL_ASSERT(buf); - for (int slot = 0; slot < buf->cmn.num_slots; slot++) { - /* it's valid to call release resource with '0' */ - _sg_mtl_release_resource(_sg.mtl.frame_index, buf->mtl.buf[slot]); - } -} - -_SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unretained id mtl_tex, const sg_image_data* data) { - const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; - const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices : 1; - for (int face_index = 0; face_index < num_faces; face_index++) { - for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { - SOKOL_ASSERT(data->subimage[face_index][mip_index].ptr); - SOKOL_ASSERT(data->subimage[face_index][mip_index].size > 0); - const uint8_t* data_ptr = (const uint8_t*)data->subimage[face_index][mip_index].ptr; - const int mip_width = _sg_max(img->cmn.width >> mip_index, 1); - const int mip_height = _sg_max(img->cmn.height >> mip_index, 1); - /* special case PVRTC formats: bytePerRow and bytesPerImage must be 0 */ - int bytes_per_row = 0; - int bytes_per_slice = 0; - if (!_sg_mtl_is_pvrtc(img->cmn.pixel_format)) { - bytes_per_row = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); - bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); - } - /* bytesPerImage special case: https://developer.apple.com/documentation/metal/mtltexture/1515679-replaceregion - - "Supply a nonzero value only when you copy data to a MTLTextureType3D type texture" - */ - MTLRegion region; - int bytes_per_image; - if (img->cmn.type == SG_IMAGETYPE_3D) { - const int mip_depth = _sg_max(img->cmn.num_slices >> mip_index, 1); - region = MTLRegionMake3D(0, 0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height, (NSUInteger)mip_depth); - bytes_per_image = bytes_per_slice; - /* FIXME: apparently the minimal bytes_per_image size for 3D texture - is 4 KByte... somehow need to handle this */ - } - else { - region = MTLRegionMake2D(0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height); - bytes_per_image = 0; - } - - for (int slice_index = 0; slice_index < num_slices; slice_index++) { - const int mtl_slice_index = (img->cmn.type == SG_IMAGETYPE_CUBE) ? face_index : slice_index; - const int slice_offset = slice_index * bytes_per_slice; - SOKOL_ASSERT((slice_offset + bytes_per_slice) <= (int)data->subimage[face_index][mip_index].size); - [mtl_tex replaceRegion:region - mipmapLevel:(NSUInteger)mip_index - slice:(NSUInteger)mtl_slice_index - withBytes:data_ptr + slice_offset - bytesPerRow:(NSUInteger)bytes_per_row - bytesPerImage:(NSUInteger)bytes_per_image]; - } - } - } -} - -/* - FIXME: METAL RESOURCE STORAGE MODE FOR macOS AND iOS - - For immutable textures on macOS, the recommended procedure is to create - a MTLStorageModeManaged texture with the immutable content first, - and then use the GPU to blit the content into a MTLStorageModePrivate - texture before the first use. - - On iOS use the same one-time-blit procedure, but from a - MTLStorageModeShared to a MTLStorageModePrivate texture. - - It probably makes sense to handle this in a separate 'resource manager' - with a recycable pool of blit-source-textures? -*/ - -/* initialize MTLTextureDescritor with common attributes */ -_SOKOL_PRIVATE bool _sg_mtl_init_texdesc_common(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { - mtl_desc.textureType = _sg_mtl_texture_type(img->cmn.type); - mtl_desc.pixelFormat = _sg_mtl_pixel_format(img->cmn.pixel_format); - if (MTLPixelFormatInvalid == mtl_desc.pixelFormat) { - SOKOL_LOG("Unsupported texture pixel format!\n"); - return false; - } - mtl_desc.width = (NSUInteger)img->cmn.width; - mtl_desc.height = (NSUInteger)img->cmn.height; - if (SG_IMAGETYPE_3D == img->cmn.type) { - mtl_desc.depth = (NSUInteger)img->cmn.num_slices; - } - else { - mtl_desc.depth = 1; - } - mtl_desc.mipmapLevelCount = (NSUInteger)img->cmn.num_mipmaps; - if (SG_IMAGETYPE_ARRAY == img->cmn.type) { - mtl_desc.arrayLength = (NSUInteger)img->cmn.num_slices; - } - else { - mtl_desc.arrayLength = 1; - } - mtl_desc.usage = MTLTextureUsageShaderRead; - MTLResourceOptions res_options = 0; - if (img->cmn.usage != SG_USAGE_IMMUTABLE) { - res_options |= MTLResourceCPUCacheModeWriteCombined; - } - #if defined(_SG_TARGET_MACOS) - /* macOS: use managed textures */ - res_options |= MTLResourceStorageModeManaged; - #else - /* iOS: use CPU/GPU shared memory */ - res_options |= MTLResourceStorageModeShared; - #endif - mtl_desc.resourceOptions = res_options; - return true; -} - -/* initialize MTLTextureDescritor with rendertarget attributes */ -_SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { - SOKOL_ASSERT(img->cmn.render_target); - _SOKOL_UNUSED(img); - /* render targets are only visible to the GPU */ - mtl_desc.resourceOptions = MTLResourceStorageModePrivate; - /* non-MSAA render targets are shader-readable */ - mtl_desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; -} - -/* initialize MTLTextureDescritor with MSAA attributes */ -_SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt_msaa(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { - SOKOL_ASSERT(img->cmn.sample_count > 1); - /* render targets are only visible to the GPU */ - mtl_desc.resourceOptions = MTLResourceStorageModePrivate; - /* MSAA render targets are not shader-readable (instead they are resolved) */ - mtl_desc.usage = MTLTextureUsageRenderTarget; - mtl_desc.textureType = MTLTextureType2DMultisample; - mtl_desc.depth = 1; - mtl_desc.arrayLength = 1; - mtl_desc.mipmapLevelCount = 1; - mtl_desc.sampleCount = (NSUInteger)img->cmn.sample_count; -} - -_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg_image_desc* desc) { - SOKOL_ASSERT(img && desc); - _sg_image_common_init(&img->cmn, desc); - const bool injected = (0 != desc->mtl_textures[0]); - const bool msaa = (img->cmn.sample_count > 1); - - /* first initialize all Metal resource pool slots to 'empty' */ - for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { - img->mtl.tex[i] = _sg_mtl_add_resource(nil); - } - img->mtl.sampler_state = _sg_mtl_add_resource(nil); - img->mtl.depth_tex = _sg_mtl_add_resource(nil); - img->mtl.msaa_tex = _sg_mtl_add_resource(nil); - - /* initialize a Metal texture descriptor with common attributes */ - MTLTextureDescriptor* mtl_desc = [[MTLTextureDescriptor alloc] init]; - if (!_sg_mtl_init_texdesc_common(mtl_desc, img)) { - _SG_OBJC_RELEASE(mtl_desc); - return SG_RESOURCESTATE_FAILED; - } - - /* special case depth-stencil-buffer? */ - if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { - /* depth-stencil buffer texture must always be a render target */ - SOKOL_ASSERT(img->cmn.render_target); - SOKOL_ASSERT(img->cmn.type == SG_IMAGETYPE_2D); - SOKOL_ASSERT(img->cmn.num_mipmaps == 1); - SOKOL_ASSERT(!injected); - if (msaa) { - _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); - } - else { - _sg_mtl_init_texdesc_rt(mtl_desc, img); - } - id tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; - SOKOL_ASSERT(nil != tex); - img->mtl.depth_tex = _sg_mtl_add_resource(tex); - } - else { - /* create the color texture - In case this is a render target without MSAA, add the relevant - render-target descriptor attributes. - In case this is a render target *with* MSAA, the color texture - will serve as MSAA-resolve target (not as render target), and rendering - will go into a separate render target texture of type - MTLTextureType2DMultisample. - */ - if (img->cmn.render_target && !msaa) { - _sg_mtl_init_texdesc_rt(mtl_desc, img); - } - for (int slot = 0; slot < img->cmn.num_slots; slot++) { - id tex; - if (injected) { - SOKOL_ASSERT(desc->mtl_textures[slot]); - tex = (__bridge id) desc->mtl_textures[slot]; - } - else { - tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; - if ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { - _sg_mtl_copy_image_data(img, tex, &desc->data); - } - } - img->mtl.tex[slot] = _sg_mtl_add_resource(tex); - } - - /* if MSAA color render target, create an additional MSAA render-surface texture */ - if (img->cmn.render_target && msaa) { - _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); - id tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; - img->mtl.msaa_tex = _sg_mtl_add_resource(tex); - } - - /* create (possibly shared) sampler state */ - img->mtl.sampler_state = _sg_mtl_create_sampler(_sg.mtl.device, desc); - } - _SG_OBJC_RELEASE(mtl_desc); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_mtl_destroy_image(_sg_image_t* img) { - SOKOL_ASSERT(img); - /* it's valid to call release resource with a 'null resource' */ - for (int slot = 0; slot < img->cmn.num_slots; slot++) { - _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl.tex[slot]); - } - _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl.depth_tex); - _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl.msaa_tex); - /* NOTE: sampler state objects are shared and not released until shutdown */ -} - -_SOKOL_PRIVATE id _sg_mtl_compile_library(const char* src) { - NSError* err = NULL; - id lib = [_sg.mtl.device - newLibraryWithSource:[NSString stringWithUTF8String:src] - options:nil - error:&err - ]; - if (err) { - SOKOL_LOG([err.localizedDescription UTF8String]); - } - return lib; -} - -_SOKOL_PRIVATE id _sg_mtl_library_from_bytecode(const void* ptr, size_t num_bytes) { - NSError* err = NULL; - dispatch_data_t lib_data = dispatch_data_create(ptr, num_bytes, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); - id lib = [_sg.mtl.device newLibraryWithData:lib_data error:&err]; - if (err) { - SOKOL_LOG([err.localizedDescription UTF8String]); - } - _SG_OBJC_RELEASE(lib_data); - return lib; -} - -_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { - SOKOL_ASSERT(shd && desc); - - _sg_shader_common_init(&shd->cmn, desc); - - /* create metal libray objects and lookup entry functions */ - id vs_lib; - id fs_lib; - id vs_func; - id fs_func; - const char* vs_entry = desc->vs.entry; - const char* fs_entry = desc->fs.entry; - if (desc->vs.bytecode.ptr && desc->fs.bytecode.ptr) { - /* separate byte code provided */ - vs_lib = _sg_mtl_library_from_bytecode(desc->vs.bytecode.ptr, desc->vs.bytecode.size); - fs_lib = _sg_mtl_library_from_bytecode(desc->fs.bytecode.ptr, desc->fs.bytecode.size); - if (nil == vs_lib || nil == fs_lib) { - return SG_RESOURCESTATE_FAILED; - } - vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; - fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; - } - else if (desc->vs.source && desc->fs.source) { - /* separate sources provided */ - vs_lib = _sg_mtl_compile_library(desc->vs.source); - fs_lib = _sg_mtl_compile_library(desc->fs.source); - if (nil == vs_lib || nil == fs_lib) { - return SG_RESOURCESTATE_FAILED; - } - vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; - fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; - } - else { - return SG_RESOURCESTATE_FAILED; - } - if (nil == vs_func) { - SOKOL_LOG("vertex shader entry function not found\n"); - return SG_RESOURCESTATE_FAILED; - } - if (nil == fs_func) { - SOKOL_LOG("fragment shader entry function not found\n"); - return SG_RESOURCESTATE_FAILED; - } - /* it is legal to call _sg_mtl_add_resource with a nil value, this will return a special 0xFFFFFFFF index */ - shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib = _sg_mtl_add_resource(vs_lib); - shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib = _sg_mtl_add_resource(fs_lib); - shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func = _sg_mtl_add_resource(vs_func); - shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func = _sg_mtl_add_resource(fs_func); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_mtl_destroy_shader(_sg_shader_t* shd) { - SOKOL_ASSERT(shd); - /* it is valid to call _sg_mtl_release_resource with a 'null resource' */ - _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); - _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib); - _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); - _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib); -} - -_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { - SOKOL_ASSERT(pip && shd && desc); - SOKOL_ASSERT(desc->shader.id == shd->slot.id); - - pip->shader = shd; - _sg_pipeline_common_init(&pip->cmn, desc); - - sg_primitive_type prim_type = desc->primitive_type; - pip->mtl.prim_type = _sg_mtl_primitive_type(prim_type); - pip->mtl.index_size = _sg_mtl_index_size(pip->cmn.index_type); - if (SG_INDEXTYPE_NONE != pip->cmn.index_type) { - pip->mtl.index_type = _sg_mtl_index_type(pip->cmn.index_type); - } - pip->mtl.cull_mode = _sg_mtl_cull_mode(desc->cull_mode); - pip->mtl.winding = _sg_mtl_winding(desc->face_winding); - pip->mtl.stencil_ref = desc->stencil.ref; - - /* create vertex-descriptor */ - MTLVertexDescriptor* vtx_desc = [MTLVertexDescriptor vertexDescriptor]; - for (NSUInteger attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { - break; - } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - vtx_desc.attributes[attr_index].format = _sg_mtl_vertex_format(a_desc->format); - vtx_desc.attributes[attr_index].offset = (NSUInteger)a_desc->offset; - vtx_desc.attributes[attr_index].bufferIndex = (NSUInteger)(a_desc->buffer_index + SG_MAX_SHADERSTAGE_UBS); - pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; - } - for (NSUInteger layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { - if (pip->cmn.vertex_layout_valid[layout_index]) { - const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[layout_index]; - const NSUInteger mtl_vb_slot = layout_index + SG_MAX_SHADERSTAGE_UBS; - SOKOL_ASSERT(l_desc->stride > 0); - vtx_desc.layouts[mtl_vb_slot].stride = (NSUInteger)l_desc->stride; - vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(l_desc->step_func); - vtx_desc.layouts[mtl_vb_slot].stepRate = (NSUInteger)l_desc->step_rate; - if (SG_VERTEXSTEP_PER_INSTANCE == l_desc->step_func) { - // NOTE: not actually used in _sg_mtl_draw() - pip->cmn.use_instanced_draw = true; - } - } - } - - /* render-pipeline descriptor */ - MTLRenderPipelineDescriptor* rp_desc = [[MTLRenderPipelineDescriptor alloc] init]; - rp_desc.vertexDescriptor = vtx_desc; - SOKOL_ASSERT(shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); - rp_desc.vertexFunction = _sg_mtl_id(shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); - SOKOL_ASSERT(shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); - rp_desc.fragmentFunction = _sg_mtl_id(shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); - rp_desc.sampleCount = (NSUInteger)desc->sample_count; - rp_desc.alphaToCoverageEnabled = desc->alpha_to_coverage_enabled; - rp_desc.alphaToOneEnabled = NO; - rp_desc.rasterizationEnabled = YES; - rp_desc.depthAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format); - if (desc->depth.pixel_format == SG_PIXELFORMAT_DEPTH_STENCIL) { - rp_desc.stencilAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format); - } - /* FIXME: this only works on macOS 10.13! - for (int i = 0; i < (SG_MAX_SHADERSTAGE_UBS+SG_MAX_SHADERSTAGE_BUFFERS); i++) { - rp_desc.vertexBuffers[i].mutability = MTLMutabilityImmutable; - } - for (int i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { - rp_desc.fragmentBuffers[i].mutability = MTLMutabilityImmutable; - } - */ - for (NSUInteger i = 0; i < (NSUInteger)desc->color_count; i++) { - SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); - const sg_color_state* cs = &desc->colors[i]; - rp_desc.colorAttachments[i].pixelFormat = _sg_mtl_pixel_format(cs->pixel_format); - rp_desc.colorAttachments[i].writeMask = _sg_mtl_color_write_mask(cs->write_mask); - rp_desc.colorAttachments[i].blendingEnabled = cs->blend.enabled; - rp_desc.colorAttachments[i].alphaBlendOperation = _sg_mtl_blend_op(cs->blend.op_alpha); - rp_desc.colorAttachments[i].rgbBlendOperation = _sg_mtl_blend_op(cs->blend.op_rgb); - rp_desc.colorAttachments[i].destinationAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.dst_factor_alpha); - rp_desc.colorAttachments[i].destinationRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.dst_factor_rgb); - rp_desc.colorAttachments[i].sourceAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_alpha); - rp_desc.colorAttachments[i].sourceRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_rgb); - } - NSError* err = NULL; - id mtl_rps = [_sg.mtl.device newRenderPipelineStateWithDescriptor:rp_desc error:&err]; - _SG_OBJC_RELEASE(rp_desc); - if (nil == mtl_rps) { - SOKOL_ASSERT(err); - SOKOL_LOG([err.localizedDescription UTF8String]); - return SG_RESOURCESTATE_FAILED; - } - - /* depth-stencil-state */ - MTLDepthStencilDescriptor* ds_desc = [[MTLDepthStencilDescriptor alloc] init]; - ds_desc.depthCompareFunction = _sg_mtl_compare_func(desc->depth.compare); - ds_desc.depthWriteEnabled = desc->depth.write_enabled; - if (desc->stencil.enabled) { - const sg_stencil_face_state* sb = &desc->stencil.back; - ds_desc.backFaceStencil = [[MTLStencilDescriptor alloc] init]; - ds_desc.backFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sb->fail_op); - ds_desc.backFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sb->depth_fail_op); - ds_desc.backFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sb->pass_op); - ds_desc.backFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sb->compare); - ds_desc.backFaceStencil.readMask = desc->stencil.read_mask; - ds_desc.backFaceStencil.writeMask = desc->stencil.write_mask; - const sg_stencil_face_state* sf = &desc->stencil.front; - ds_desc.frontFaceStencil = [[MTLStencilDescriptor alloc] init]; - ds_desc.frontFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sf->fail_op); - ds_desc.frontFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sf->depth_fail_op); - ds_desc.frontFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sf->pass_op); - ds_desc.frontFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sf->compare); - ds_desc.frontFaceStencil.readMask = desc->stencil.read_mask; - ds_desc.frontFaceStencil.writeMask = desc->stencil.write_mask; - } - id mtl_dss = [_sg.mtl.device newDepthStencilStateWithDescriptor:ds_desc]; - _SG_OBJC_RELEASE(ds_desc); - pip->mtl.rps = _sg_mtl_add_resource(mtl_rps); - pip->mtl.dss = _sg_mtl_add_resource(mtl_dss); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_mtl_destroy_pipeline(_sg_pipeline_t* pip) { - SOKOL_ASSERT(pip); - /* it's valid to call release resource with a 'null resource' */ - _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl.rps); - _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl.dss); -} - -_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { - SOKOL_ASSERT(pass && desc); - SOKOL_ASSERT(att_images && att_images[0]); - - _sg_pass_common_init(&pass->cmn, desc); - - /* copy image pointers */ - const sg_pass_attachment_desc* att_desc; - for (int i = 0; i < pass->cmn.num_color_atts; i++) { - att_desc = &desc->color_attachments[i]; - if (att_desc->image.id != SG_INVALID_ID) { - SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); - SOKOL_ASSERT(0 == pass->mtl.color_atts[i].image); - SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->cmn.pixel_format)); - pass->mtl.color_atts[i].image = att_images[i]; - } - } - SOKOL_ASSERT(0 == pass->mtl.ds_att.image); - att_desc = &desc->depth_stencil_attachment; - if (att_desc->image.id != SG_INVALID_ID) { - const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; - SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); - pass->mtl.ds_att.image = att_images[ds_img_index]; - } - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_mtl_destroy_pass(_sg_pass_t* pass) { - SOKOL_ASSERT(pass); - _SOKOL_UNUSED(pass); -} - -_SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_color_image(const _sg_pass_t* pass, int index) { - SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); - /* NOTE: may return null */ - return pass->mtl.color_atts[index].image; -} - -_SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_ds_image(const _sg_pass_t* pass) { - /* NOTE: may return null */ - SOKOL_ASSERT(pass); - return pass->mtl.ds_att.image; -} - -_SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { - SOKOL_ASSERT(action); - SOKOL_ASSERT(!_sg.mtl.in_pass); - SOKOL_ASSERT(_sg.mtl.cmd_queue); - SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); - SOKOL_ASSERT(_sg.mtl.renderpass_descriptor_cb || _sg.mtl.renderpass_descriptor_userdata_cb); - _sg.mtl.in_pass = true; - _sg.mtl.cur_width = w; - _sg.mtl.cur_height = h; - _sg_mtl_clear_state_cache(); - - /* if this is the first pass in the frame, create a command buffer */ - if (nil == _sg.mtl.cmd_buffer) { - /* block until the oldest frame in flight has finished */ - dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); - _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBufferWithUnretainedReferences]; - [_sg.mtl.cmd_buffer addCompletedHandler:^(id cmd_buffer) { - // NOTE: this code is called on a different thread! - _SOKOL_UNUSED(cmd_buffer); - dispatch_semaphore_signal(_sg.mtl.sem); - }]; - } - - /* if this is first pass in frame, get uniform buffer base pointer */ - if (0 == _sg.mtl.cur_ub_base_ptr) { - _sg.mtl.cur_ub_base_ptr = (uint8_t*)[_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] contents]; - } - - /* initialize a render pass descriptor */ - MTLRenderPassDescriptor* pass_desc = nil; - if (pass) { - /* offscreen render pass */ - pass_desc = [MTLRenderPassDescriptor renderPassDescriptor]; - } - else { - /* default render pass, call user-provided callback to provide render pass descriptor */ - if (_sg.mtl.renderpass_descriptor_cb) { - pass_desc = (__bridge MTLRenderPassDescriptor*) _sg.mtl.renderpass_descriptor_cb(); - } - else { - pass_desc = (__bridge MTLRenderPassDescriptor*) _sg.mtl.renderpass_descriptor_userdata_cb(_sg.mtl.user_data); - } - - } - if (pass_desc) { - _sg.mtl.pass_valid = true; - } - else { - /* default pass descriptor will not be valid if window is minimized, - don't do any rendering in this case */ - _sg.mtl.pass_valid = false; - return; - } - if (pass) { - /* setup pass descriptor for offscreen rendering */ - SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_VALID); - for (NSUInteger i = 0; i < (NSUInteger)pass->cmn.num_color_atts; i++) { - const _sg_pass_attachment_t* cmn_att = &pass->cmn.color_atts[i]; - const _sg_mtl_attachment_t* mtl_att = &pass->mtl.color_atts[i]; - const _sg_image_t* att_img = mtl_att->image; - SOKOL_ASSERT(att_img->slot.state == SG_RESOURCESTATE_VALID); - SOKOL_ASSERT(att_img->slot.id == cmn_att->image_id.id); - const bool is_msaa = (att_img->cmn.sample_count > 1); - pass_desc.colorAttachments[i].loadAction = _sg_mtl_load_action(action->colors[i].action); - pass_desc.colorAttachments[i].storeAction = is_msaa ? MTLStoreActionMultisampleResolve : MTLStoreActionStore; - sg_color c = action->colors[i].value; - pass_desc.colorAttachments[i].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); - if (is_msaa) { - SOKOL_ASSERT(att_img->mtl.msaa_tex != _SG_MTL_INVALID_SLOT_INDEX); - SOKOL_ASSERT(att_img->mtl.tex[mtl_att->image->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); - pass_desc.colorAttachments[i].texture = _sg_mtl_id(att_img->mtl.msaa_tex); - pass_desc.colorAttachments[i].resolveTexture = _sg_mtl_id(att_img->mtl.tex[att_img->cmn.active_slot]); - pass_desc.colorAttachments[i].resolveLevel = (NSUInteger)cmn_att->mip_level; - switch (att_img->cmn.type) { - case SG_IMAGETYPE_CUBE: - case SG_IMAGETYPE_ARRAY: - pass_desc.colorAttachments[i].resolveSlice = (NSUInteger)cmn_att->slice; - break; - case SG_IMAGETYPE_3D: - pass_desc.colorAttachments[i].resolveDepthPlane = (NSUInteger)cmn_att->slice; - break; - default: break; - } - } - else { - SOKOL_ASSERT(att_img->mtl.tex[att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); - pass_desc.colorAttachments[i].texture = _sg_mtl_id(att_img->mtl.tex[att_img->cmn.active_slot]); - pass_desc.colorAttachments[i].level = (NSUInteger)cmn_att->mip_level; - switch (att_img->cmn.type) { - case SG_IMAGETYPE_CUBE: - case SG_IMAGETYPE_ARRAY: - pass_desc.colorAttachments[i].slice = (NSUInteger)cmn_att->slice; - break; - case SG_IMAGETYPE_3D: - pass_desc.colorAttachments[i].depthPlane = (NSUInteger)cmn_att->slice; - break; - default: break; - } - } - } - const _sg_image_t* ds_att_img = pass->mtl.ds_att.image; - if (0 != ds_att_img) { - SOKOL_ASSERT(ds_att_img->slot.state == SG_RESOURCESTATE_VALID); - SOKOL_ASSERT(ds_att_img->slot.id == pass->cmn.ds_att.image_id.id); - SOKOL_ASSERT(ds_att_img->mtl.depth_tex != _SG_MTL_INVALID_SLOT_INDEX); - pass_desc.depthAttachment.texture = _sg_mtl_id(ds_att_img->mtl.depth_tex); - pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.action); - pass_desc.depthAttachment.clearDepth = action->depth.value; - if (_sg_is_depth_stencil_format(ds_att_img->cmn.pixel_format)) { - pass_desc.stencilAttachment.texture = _sg_mtl_id(ds_att_img->mtl.depth_tex); - pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.action); - pass_desc.stencilAttachment.clearStencil = action->stencil.value; - } - } - } - else { - /* setup pass descriptor for default rendering */ - pass_desc.colorAttachments[0].loadAction = _sg_mtl_load_action(action->colors[0].action); - sg_color c = action->colors[0].value; - pass_desc.colorAttachments[0].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); - pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.action); - pass_desc.depthAttachment.clearDepth = action->depth.value; - pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.action); - pass_desc.stencilAttachment.clearStencil = action->stencil.value; - } - - /* create a render command encoder, this might return nil if window is minimized */ - _sg.mtl.cmd_encoder = [_sg.mtl.cmd_buffer renderCommandEncoderWithDescriptor:pass_desc]; - if (nil == _sg.mtl.cmd_encoder) { - _sg.mtl.pass_valid = false; - return; - } - - /* bind the global uniform buffer, this only happens once per pass */ - _sg_mtl_bind_uniform_buffers(); -} - -_SOKOL_PRIVATE void _sg_mtl_end_pass(void) { - SOKOL_ASSERT(_sg.mtl.in_pass); - _sg.mtl.in_pass = false; - _sg.mtl.pass_valid = false; - if (nil != _sg.mtl.cmd_encoder) { - [_sg.mtl.cmd_encoder endEncoding]; - /* NOTE: MTLRenderCommandEncoder is autoreleased */ - _sg.mtl.cmd_encoder = nil; - } -} - -_SOKOL_PRIVATE void _sg_mtl_commit(void) { - SOKOL_ASSERT(!_sg.mtl.in_pass); - SOKOL_ASSERT(!_sg.mtl.pass_valid); - SOKOL_ASSERT(_sg.mtl.drawable_cb || _sg.mtl.drawable_userdata_cb); - SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); - SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer); - - /* present, commit and signal semaphore when done */ - id cur_drawable = nil; - if (_sg.mtl.drawable_cb) { - cur_drawable = (__bridge id) _sg.mtl.drawable_cb(); - } - else { - cur_drawable = (__bridge id) _sg.mtl.drawable_userdata_cb(_sg.mtl.user_data); - } - if (nil != cur_drawable) { - [_sg.mtl.cmd_buffer presentDrawable:cur_drawable]; - } - [_sg.mtl.cmd_buffer commit]; - - /* garbage-collect resources pending for release */ - _sg_mtl_garbage_collect(_sg.mtl.frame_index); - - /* rotate uniform buffer slot */ - if (++_sg.mtl.cur_frame_rotate_index >= SG_NUM_INFLIGHT_FRAMES) { - _sg.mtl.cur_frame_rotate_index = 0; - } - _sg.mtl.frame_index++; - _sg.mtl.cur_ub_offset = 0; - _sg.mtl.cur_ub_base_ptr = 0; - /* NOTE: MTLCommandBuffer is autoreleased */ - _sg.mtl.cmd_buffer = nil; -} - -_SOKOL_PRIVATE void _sg_mtl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { - SOKOL_ASSERT(_sg.mtl.in_pass); - if (!_sg.mtl.pass_valid) { - return; - } - SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); - MTLViewport vp; - vp.originX = (double) x; - vp.originY = (double) (origin_top_left ? y : (_sg.mtl.cur_height - (y + h))); - vp.width = (double) w; - vp.height = (double) h; - vp.znear = 0.0; - vp.zfar = 1.0; - [_sg.mtl.cmd_encoder setViewport:vp]; -} - -_SOKOL_PRIVATE void _sg_mtl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { - SOKOL_ASSERT(_sg.mtl.in_pass); - if (!_sg.mtl.pass_valid) { - return; - } - SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); - /* clip against framebuffer rect */ - x = _sg_min(_sg_max(0, x), _sg.mtl.cur_width-1); - y = _sg_min(_sg_max(0, y), _sg.mtl.cur_height-1); - if ((x + w) > _sg.mtl.cur_width) { - w = _sg.mtl.cur_width - x; - } - if ((y + h) > _sg.mtl.cur_height) { - h = _sg.mtl.cur_height - y; - } - w = _sg_max(w, 1); - h = _sg_max(h, 1); - - MTLScissorRect r; - r.x = (NSUInteger)x; - r.y = (NSUInteger) (origin_top_left ? y : (_sg.mtl.cur_height - (y + h))); - r.width = (NSUInteger)w; - r.height = (NSUInteger)h; - [_sg.mtl.cmd_encoder setScissorRect:r]; -} - -_SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) { - SOKOL_ASSERT(pip); - SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); - SOKOL_ASSERT(_sg.mtl.in_pass); - if (!_sg.mtl.pass_valid) { - return; - } - SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); - - if ((_sg.mtl.state_cache.cur_pipeline != pip) || (_sg.mtl.state_cache.cur_pipeline_id.id != pip->slot.id)) { - _sg.mtl.state_cache.cur_pipeline = pip; - _sg.mtl.state_cache.cur_pipeline_id.id = pip->slot.id; - sg_color c = pip->cmn.blend_color; - [_sg.mtl.cmd_encoder setBlendColorRed:c.r green:c.g blue:c.b alpha:c.a]; - [_sg.mtl.cmd_encoder setCullMode:pip->mtl.cull_mode]; - [_sg.mtl.cmd_encoder setFrontFacingWinding:pip->mtl.winding]; - [_sg.mtl.cmd_encoder setStencilReferenceValue:pip->mtl.stencil_ref]; - [_sg.mtl.cmd_encoder setDepthBias:pip->cmn.depth_bias slopeScale:pip->cmn.depth_bias_slope_scale clamp:pip->cmn.depth_bias_clamp]; - SOKOL_ASSERT(pip->mtl.rps != _SG_MTL_INVALID_SLOT_INDEX); - [_sg.mtl.cmd_encoder setRenderPipelineState:_sg_mtl_id(pip->mtl.rps)]; - SOKOL_ASSERT(pip->mtl.dss != _SG_MTL_INVALID_SLOT_INDEX); - [_sg.mtl.cmd_encoder setDepthStencilState:_sg_mtl_id(pip->mtl.dss)]; - } -} - -_SOKOL_PRIVATE void _sg_mtl_apply_bindings( - _sg_pipeline_t* pip, - _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, - _sg_buffer_t* ib, int ib_offset, - _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs) -{ - _SOKOL_UNUSED(pip); - SOKOL_ASSERT(_sg.mtl.in_pass); - if (!_sg.mtl.pass_valid) { - return; - } - SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); - - /* store index buffer binding, this will be needed later in sg_draw() */ - _sg.mtl.state_cache.cur_indexbuffer = ib; - _sg.mtl.state_cache.cur_indexbuffer_offset = ib_offset; - if (ib) { - SOKOL_ASSERT(pip->cmn.index_type != SG_INDEXTYPE_NONE); - _sg.mtl.state_cache.cur_indexbuffer_id.id = ib->slot.id; - } - else { - SOKOL_ASSERT(pip->cmn.index_type == SG_INDEXTYPE_NONE); - _sg.mtl.state_cache.cur_indexbuffer_id.id = SG_INVALID_ID; - } - - /* apply vertex buffers */ - NSUInteger slot; - for (slot = 0; slot < (NSUInteger)num_vbs; slot++) { - const _sg_buffer_t* vb = vbs[slot]; - if ((_sg.mtl.state_cache.cur_vertexbuffers[slot] != vb) || - (_sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] != vb_offsets[slot]) || - (_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id)) - { - _sg.mtl.state_cache.cur_vertexbuffers[slot] = vb; - _sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] = vb_offsets[slot]; - _sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id = vb->slot.id; - const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + slot; - SOKOL_ASSERT(vb->mtl.buf[vb->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); - [_sg.mtl.cmd_encoder setVertexBuffer:_sg_mtl_id(vb->mtl.buf[vb->cmn.active_slot]) - offset:(NSUInteger)vb_offsets[slot] - atIndex:mtl_slot]; - } - } - - /* apply vertex shader images */ - for (slot = 0; slot < (NSUInteger)num_vs_imgs; slot++) { - const _sg_image_t* img = vs_imgs[slot]; - if ((_sg.mtl.state_cache.cur_vs_images[slot] != img) || (_sg.mtl.state_cache.cur_vs_image_ids[slot].id != img->slot.id)) { - _sg.mtl.state_cache.cur_vs_images[slot] = img; - _sg.mtl.state_cache.cur_vs_image_ids[slot].id = img->slot.id; - SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); - [_sg.mtl.cmd_encoder setVertexTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; - SOKOL_ASSERT(img->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); - [_sg.mtl.cmd_encoder setVertexSamplerState:_sg_mtl_id(img->mtl.sampler_state) atIndex:slot]; - } - } - - /* apply fragment shader images */ - for (slot = 0; slot < (NSUInteger)num_fs_imgs; slot++) { - const _sg_image_t* img = fs_imgs[slot]; - if ((_sg.mtl.state_cache.cur_fs_images[slot] != img) || (_sg.mtl.state_cache.cur_fs_image_ids[slot].id != img->slot.id)) { - _sg.mtl.state_cache.cur_fs_images[slot] = img; - _sg.mtl.state_cache.cur_fs_image_ids[slot].id = img->slot.id; - SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); - [_sg.mtl.cmd_encoder setFragmentTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; - SOKOL_ASSERT(img->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); - [_sg.mtl.cmd_encoder setFragmentSamplerState:_sg_mtl_id(img->mtl.sampler_state) atIndex:slot]; - } - } -} - -_SOKOL_PRIVATE void _sg_mtl_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { - SOKOL_ASSERT(_sg.mtl.in_pass); - if (!_sg.mtl.pass_valid) { - return; - } - SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); - SOKOL_ASSERT(((size_t)_sg.mtl.cur_ub_offset + data->size) <= (size_t)_sg.mtl.ub_size); - SOKOL_ASSERT((_sg.mtl.cur_ub_offset & (_SG_MTL_UB_ALIGN-1)) == 0); - SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && _sg.mtl.state_cache.cur_pipeline->shader); - SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id); - SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->shader->slot.id == _sg.mtl.state_cache.cur_pipeline->cmn.shader_id.id); - SOKOL_ASSERT(ub_index < _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); - SOKOL_ASSERT(data->size <= _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); - - /* copy to global uniform buffer, record offset into cmd encoder, and advance offset */ - uint8_t* dst = &_sg.mtl.cur_ub_base_ptr[_sg.mtl.cur_ub_offset]; - memcpy(dst, data->ptr, data->size); - if (stage_index == SG_SHADERSTAGE_VS) { - [_sg.mtl.cmd_encoder setVertexBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; - } - else { - [_sg.mtl.cmd_encoder setFragmentBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; - } - _sg.mtl.cur_ub_offset = _sg_roundup(_sg.mtl.cur_ub_offset + (int)data->size, _SG_MTL_UB_ALIGN); -} - -_SOKOL_PRIVATE void _sg_mtl_draw(int base_element, int num_elements, int num_instances) { - SOKOL_ASSERT(_sg.mtl.in_pass); - if (!_sg.mtl.pass_valid) { - return; - } - SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); - SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && (_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id)); - if (SG_INDEXTYPE_NONE != _sg.mtl.state_cache.cur_pipeline->cmn.index_type) { - /* indexed rendering */ - SOKOL_ASSERT(_sg.mtl.state_cache.cur_indexbuffer && (_sg.mtl.state_cache.cur_indexbuffer->slot.id == _sg.mtl.state_cache.cur_indexbuffer_id.id)); - const _sg_buffer_t* ib = _sg.mtl.state_cache.cur_indexbuffer; - SOKOL_ASSERT(ib->mtl.buf[ib->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); - const NSUInteger index_buffer_offset = (NSUInteger) (_sg.mtl.state_cache.cur_indexbuffer_offset + base_element * _sg.mtl.state_cache.cur_pipeline->mtl.index_size); - [_sg.mtl.cmd_encoder drawIndexedPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl.prim_type - indexCount:(NSUInteger)num_elements - indexType:_sg.mtl.state_cache.cur_pipeline->mtl.index_type - indexBuffer:_sg_mtl_id(ib->mtl.buf[ib->cmn.active_slot]) - indexBufferOffset:index_buffer_offset - instanceCount:(NSUInteger)num_instances]; - } - else { - /* non-indexed rendering */ - [_sg.mtl.cmd_encoder drawPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl.prim_type - vertexStart:(NSUInteger)base_element - vertexCount:(NSUInteger)num_elements - instanceCount:(NSUInteger)num_instances]; - } -} - -_SOKOL_PRIVATE void _sg_mtl_update_buffer(_sg_buffer_t* buf, const sg_range* data) { - SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); - if (++buf->cmn.active_slot >= buf->cmn.num_slots) { - buf->cmn.active_slot = 0; - } - __unsafe_unretained id mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]); - void* dst_ptr = [mtl_buf contents]; - memcpy(dst_ptr, data->ptr, data->size); - #if defined(_SG_TARGET_MACOS) - [mtl_buf didModifyRange:NSMakeRange(0, data->size)]; - #endif -} - -_SOKOL_PRIVATE int _sg_mtl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { - SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); - if (new_frame) { - if (++buf->cmn.active_slot >= buf->cmn.num_slots) { - buf->cmn.active_slot = 0; - } - } - __unsafe_unretained id mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]); - uint8_t* dst_ptr = (uint8_t*) [mtl_buf contents]; - dst_ptr += buf->cmn.append_pos; - memcpy(dst_ptr, data->ptr, data->size); - #if defined(_SG_TARGET_MACOS) - [mtl_buf didModifyRange:NSMakeRange((NSUInteger)buf->cmn.append_pos, (NSUInteger)data->size)]; - #endif - /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backends */ - return _sg_roundup((int)data->size, 4); -} - -_SOKOL_PRIVATE void _sg_mtl_update_image(_sg_image_t* img, const sg_image_data* data) { - SOKOL_ASSERT(img && data); - if (++img->cmn.active_slot >= img->cmn.num_slots) { - img->cmn.active_slot = 0; - } - __unsafe_unretained id mtl_tex = _sg_mtl_id(img->mtl.tex[img->cmn.active_slot]); - _sg_mtl_copy_image_data(img, mtl_tex, data); -} - -/*== WEBGPU BACKEND IMPLEMENTATION ===========================================*/ -#elif defined(SOKOL_WGPU) - -_SOKOL_PRIVATE WGPUBufferUsageFlags _sg_wgpu_buffer_usage(sg_buffer_type t, sg_usage u) { - WGPUBufferUsageFlags res = 0; - if (SG_BUFFERTYPE_VERTEXBUFFER == t) { - res |= WGPUBufferUsage_Vertex; - } - else { - res |= WGPUBufferUsage_Index; - } - if (SG_USAGE_IMMUTABLE != u) { - res |= WGPUBufferUsage_CopyDst; - } - return res; -} - -_SOKOL_PRIVATE WGPULoadOp _sg_wgpu_load_op(sg_action a) { - switch (a) { - case SG_ACTION_CLEAR: - case SG_ACTION_DONTCARE: - return WGPULoadOp_Clear; - case SG_ACTION_LOAD: - return WGPULoadOp_Load; - default: - SOKOL_UNREACHABLE; - return (WGPULoadOp)0; - } -} - -_SOKOL_PRIVATE WGPUTextureViewDimension _sg_wgpu_tex_viewdim(sg_image_type t) { - switch (t) { - case SG_IMAGETYPE_2D: return WGPUTextureViewDimension_2D; - case SG_IMAGETYPE_CUBE: return WGPUTextureViewDimension_Cube; - case SG_IMAGETYPE_3D: return WGPUTextureViewDimension_3D; - case SG_IMAGETYPE_ARRAY: return WGPUTextureViewDimension_2DArray; - default: SOKOL_UNREACHABLE; return WGPUTextureViewDimension_Force32; - } -} - -_SOKOL_PRIVATE WGPUTextureComponentType _sg_wgpu_tex_comptype(sg_sampler_type t) { - switch (t) { - case SG_SAMPLERTYPE_FLOAT: return WGPUTextureComponentType_Float; - case SG_SAMPLERTYPE_SINT: return WGPUTextureComponentType_Sint; - case SG_SAMPLERTYPE_UINT: return WGPUTextureComponentType_Uint; - default: SOKOL_UNREACHABLE; return WGPUTextureComponentType_Force32; - } -} - -_SOKOL_PRIVATE WGPUTextureDimension _sg_wgpu_tex_dim(sg_image_type t) { - if (SG_IMAGETYPE_3D == t) { - return WGPUTextureDimension_3D; - } - else { - return WGPUTextureDimension_2D; - } -} - -_SOKOL_PRIVATE WGPUAddressMode _sg_wgpu_sampler_addrmode(sg_wrap m) { - switch (m) { - case SG_WRAP_REPEAT: - return WGPUAddressMode_Repeat; - case SG_WRAP_CLAMP_TO_EDGE: - case SG_WRAP_CLAMP_TO_BORDER: - return WGPUAddressMode_ClampToEdge; - case SG_WRAP_MIRRORED_REPEAT: - return WGPUAddressMode_MirrorRepeat; - default: - SOKOL_UNREACHABLE; - return WGPUAddressMode_Force32; - } -} - -_SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_minmagfilter(sg_filter f) { - switch (f) { - case SG_FILTER_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_LINEAR: - return WGPUFilterMode_Nearest; - case SG_FILTER_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: - case SG_FILTER_LINEAR_MIPMAP_LINEAR: - return WGPUFilterMode_Linear; - default: - SOKOL_UNREACHABLE; - return WGPUFilterMode_Force32; - } -} - -_SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_mipfilter(sg_filter f) { - switch (f) { - case SG_FILTER_NEAREST: - case SG_FILTER_LINEAR: - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: - return WGPUFilterMode_Nearest; - case SG_FILTER_NEAREST_MIPMAP_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_LINEAR: - return WGPUFilterMode_Linear; - default: - SOKOL_UNREACHABLE; - return WGPUFilterMode_Force32; - } -} - -_SOKOL_PRIVATE WGPUIndexFormat _sg_wgpu_indexformat(sg_index_type t) { - /* NOTE: there's no WGPUIndexFormat_None */ - return (t == SG_INDEXTYPE_UINT16) ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32; -} - -_SOKOL_PRIVATE WGPUInputStepMode _sg_wgpu_stepmode(sg_vertex_step s) { - return (s == SG_VERTEXSTEP_PER_VERTEX) ? WGPUInputStepMode_Vertex : WGPUInputStepMode_Instance; -} - -_SOKOL_PRIVATE WGPUVertexFormat _sg_wgpu_vertexformat(sg_vertex_format f) { - switch (f) { - case SG_VERTEXFORMAT_FLOAT: return WGPUVertexFormat_Float; - case SG_VERTEXFORMAT_FLOAT2: return WGPUVertexFormat_Float2; - case SG_VERTEXFORMAT_FLOAT3: return WGPUVertexFormat_Float3; - case SG_VERTEXFORMAT_FLOAT4: return WGPUVertexFormat_Float4; - case SG_VERTEXFORMAT_BYTE4: return WGPUVertexFormat_Char4; - case SG_VERTEXFORMAT_BYTE4N: return WGPUVertexFormat_Char4Norm; - case SG_VERTEXFORMAT_UBYTE4: return WGPUVertexFormat_UChar4; - case SG_VERTEXFORMAT_UBYTE4N: return WGPUVertexFormat_UChar4Norm; - case SG_VERTEXFORMAT_SHORT2: return WGPUVertexFormat_Short2; - case SG_VERTEXFORMAT_SHORT2N: return WGPUVertexFormat_Short2Norm; - case SG_VERTEXFORMAT_USHORT2N: return WGPUVertexFormat_UShort2Norm; - case SG_VERTEXFORMAT_SHORT4: return WGPUVertexFormat_Short4; - case SG_VERTEXFORMAT_SHORT4N: return WGPUVertexFormat_Short4Norm; - case SG_VERTEXFORMAT_USHORT4N: return WGPUVertexFormat_UShort4Norm; - /* FIXME! UINT10_N2 */ - case SG_VERTEXFORMAT_UINT10_N2: - default: - SOKOL_UNREACHABLE; - return WGPUVertexFormat_Force32; - } -} - -_SOKOL_PRIVATE WGPUPrimitiveTopology _sg_wgpu_topology(sg_primitive_type t) { - switch (t) { - case SG_PRIMITIVETYPE_POINTS: return WGPUPrimitiveTopology_PointList; - case SG_PRIMITIVETYPE_LINES: return WGPUPrimitiveTopology_LineList; - case SG_PRIMITIVETYPE_LINE_STRIP: return WGPUPrimitiveTopology_LineStrip; - case SG_PRIMITIVETYPE_TRIANGLES: return WGPUPrimitiveTopology_TriangleList; - case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return WGPUPrimitiveTopology_TriangleStrip; - default: SOKOL_UNREACHABLE; return WGPUPrimitiveTopology_Force32; - } -} - -_SOKOL_PRIVATE WGPUFrontFace _sg_wgpu_frontface(sg_face_winding fw) { - return (fw == SG_FACEWINDING_CCW) ? WGPUFrontFace_CCW : WGPUFrontFace_CW; -} - -_SOKOL_PRIVATE WGPUCullMode _sg_wgpu_cullmode(sg_cull_mode cm) { - switch (cm) { - case SG_CULLMODE_NONE: return WGPUCullMode_None; - case SG_CULLMODE_FRONT: return WGPUCullMode_Front; - case SG_CULLMODE_BACK: return WGPUCullMode_Back; - default: SOKOL_UNREACHABLE; return WGPUCullMode_Force32; - } -} - -_SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { - switch (p) { - case SG_PIXELFORMAT_NONE: return WGPUTextureFormat_Undefined; - case SG_PIXELFORMAT_R8: return WGPUTextureFormat_R8Unorm; - case SG_PIXELFORMAT_R8SN: return WGPUTextureFormat_R8Snorm; - case SG_PIXELFORMAT_R8UI: return WGPUTextureFormat_R8Uint; - case SG_PIXELFORMAT_R8SI: return WGPUTextureFormat_R8Sint; - case SG_PIXELFORMAT_R16UI: return WGPUTextureFormat_R16Uint; - case SG_PIXELFORMAT_R16SI: return WGPUTextureFormat_R16Sint; - case SG_PIXELFORMAT_R16F: return WGPUTextureFormat_R16Float; - case SG_PIXELFORMAT_RG8: return WGPUTextureFormat_RG8Unorm; - case SG_PIXELFORMAT_RG8SN: return WGPUTextureFormat_RG8Snorm; - case SG_PIXELFORMAT_RG8UI: return WGPUTextureFormat_RG8Uint; - case SG_PIXELFORMAT_RG8SI: return WGPUTextureFormat_RG8Sint; - case SG_PIXELFORMAT_R32UI: return WGPUTextureFormat_R32Uint; - case SG_PIXELFORMAT_R32SI: return WGPUTextureFormat_R32Sint; - case SG_PIXELFORMAT_R32F: return WGPUTextureFormat_R32Float; - case SG_PIXELFORMAT_RG16UI: return WGPUTextureFormat_RG16Uint; - case SG_PIXELFORMAT_RG16SI: return WGPUTextureFormat_RG16Sint; - case SG_PIXELFORMAT_RG16F: return WGPUTextureFormat_RG16Float; - case SG_PIXELFORMAT_RGBA8: return WGPUTextureFormat_RGBA8Unorm; - case SG_PIXELFORMAT_RGBA8SN: return WGPUTextureFormat_RGBA8Snorm; - case SG_PIXELFORMAT_RGBA8UI: return WGPUTextureFormat_RGBA8Uint; - case SG_PIXELFORMAT_RGBA8SI: return WGPUTextureFormat_RGBA8Sint; - case SG_PIXELFORMAT_BGRA8: return WGPUTextureFormat_BGRA8Unorm; - case SG_PIXELFORMAT_RGB10A2: return WGPUTextureFormat_RGB10A2Unorm; - case SG_PIXELFORMAT_RG11B10F: return WGPUTextureFormat_RG11B10Float; - case SG_PIXELFORMAT_RG32UI: return WGPUTextureFormat_RG32Uint; - case SG_PIXELFORMAT_RG32SI: return WGPUTextureFormat_RG32Sint; - case SG_PIXELFORMAT_RG32F: return WGPUTextureFormat_RG32Float; - case SG_PIXELFORMAT_RGBA16UI: return WGPUTextureFormat_RGBA16Uint; - case SG_PIXELFORMAT_RGBA16SI: return WGPUTextureFormat_RGBA16Sint; - case SG_PIXELFORMAT_RGBA16F: return WGPUTextureFormat_RGBA16Float; - case SG_PIXELFORMAT_RGBA32UI: return WGPUTextureFormat_RGBA32Uint; - case SG_PIXELFORMAT_RGBA32SI: return WGPUTextureFormat_RGBA32Sint; - case SG_PIXELFORMAT_RGBA32F: return WGPUTextureFormat_RGBA32Float; - case SG_PIXELFORMAT_DEPTH: return WGPUTextureFormat_Depth24Plus; - case SG_PIXELFORMAT_DEPTH_STENCIL: return WGPUTextureFormat_Depth24PlusStencil8; - case SG_PIXELFORMAT_BC1_RGBA: return WGPUTextureFormat_BC1RGBAUnorm; - case SG_PIXELFORMAT_BC2_RGBA: return WGPUTextureFormat_BC2RGBAUnorm; - case SG_PIXELFORMAT_BC3_RGBA: return WGPUTextureFormat_BC3RGBAUnorm; - case SG_PIXELFORMAT_BC4_R: return WGPUTextureFormat_BC4RUnorm; - case SG_PIXELFORMAT_BC4_RSN: return WGPUTextureFormat_BC4RSnorm; - case SG_PIXELFORMAT_BC5_RG: return WGPUTextureFormat_BC5RGUnorm; - case SG_PIXELFORMAT_BC5_RGSN: return WGPUTextureFormat_BC5RGSnorm; - case SG_PIXELFORMAT_BC6H_RGBF: return WGPUTextureFormat_BC6HRGBSfloat; - case SG_PIXELFORMAT_BC6H_RGBUF: return WGPUTextureFormat_BC6HRGBUfloat; - case SG_PIXELFORMAT_BC7_RGBA: return WGPUTextureFormat_BC7RGBAUnorm; - - /* NOT SUPPORTED */ - case SG_PIXELFORMAT_R16: - case SG_PIXELFORMAT_R16SN: - case SG_PIXELFORMAT_RG16: - case SG_PIXELFORMAT_RG16SN: - case SG_PIXELFORMAT_RGBA16: - case SG_PIXELFORMAT_RGBA16SN: - case SG_PIXELFORMAT_PVRTC_RGB_2BPP: - case SG_PIXELFORMAT_PVRTC_RGB_4BPP: - case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: - case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: - case SG_PIXELFORMAT_ETC2_RGB8: - case SG_PIXELFORMAT_ETC2_RGB8A1: - case SG_PIXELFORMAT_ETC2_RGBA8: - case SG_PIXELFORMAT_ETC2_RG11: - case SG_PIXELFORMAT_ETC2_RG11SN: - default: - SOKOL_UNREACHABLE; - return WGPUTextureFormat_Force32; - } -} - -/* -FIXME ??? this isn't needed anywhere? -_SOKOL_PRIVATE WGPUTextureAspect _sg_wgpu_texture_aspect(sg_pixel_format fmt) { - if (_sg_is_valid_rendertarget_depth_format(fmt)) { - if (!_sg_is_depth_stencil_format(fmt)) { - return WGPUTextureAspect_DepthOnly; - } - } - return WGPUTextureAspect_All; -} -*/ - -_SOKOL_PRIVATE WGPUCompareFunction _sg_wgpu_comparefunc(sg_compare_func f) { - switch (f) { - case SG_COMPAREFUNC_NEVER: return WGPUCompareFunction_Never; - case SG_COMPAREFUNC_LESS: return WGPUCompareFunction_Less; - case SG_COMPAREFUNC_EQUAL: return WGPUCompareFunction_Equal; - case SG_COMPAREFUNC_LESS_EQUAL: return WGPUCompareFunction_LessEqual; - case SG_COMPAREFUNC_GREATER: return WGPUCompareFunction_Greater; - case SG_COMPAREFUNC_NOT_EQUAL: return WGPUCompareFunction_NotEqual; - case SG_COMPAREFUNC_GREATER_EQUAL: return WGPUCompareFunction_GreaterEqual; - case SG_COMPAREFUNC_ALWAYS: return WGPUCompareFunction_Always; - default: SOKOL_UNREACHABLE; return WGPUCompareFunction_Force32; - } -} - -_SOKOL_PRIVATE WGPUStencilOperation _sg_wgpu_stencilop(sg_stencil_op op) { - switch (op) { - case SG_STENCILOP_KEEP: return WGPUStencilOperation_Keep; - case SG_STENCILOP_ZERO: return WGPUStencilOperation_Zero; - case SG_STENCILOP_REPLACE: return WGPUStencilOperation_Replace; - case SG_STENCILOP_INCR_CLAMP: return WGPUStencilOperation_IncrementClamp; - case SG_STENCILOP_DECR_CLAMP: return WGPUStencilOperation_DecrementClamp; - case SG_STENCILOP_INVERT: return WGPUStencilOperation_Invert; - case SG_STENCILOP_INCR_WRAP: return WGPUStencilOperation_IncrementWrap; - case SG_STENCILOP_DECR_WRAP: return WGPUStencilOperation_DecrementWrap; - default: SOKOL_UNREACHABLE; return WGPUStencilOperation_Force32; - } -} - -_SOKOL_PRIVATE WGPUBlendOperation _sg_wgpu_blendop(sg_blend_op op) { - switch (op) { - case SG_BLENDOP_ADD: return WGPUBlendOperation_Add; - case SG_BLENDOP_SUBTRACT: return WGPUBlendOperation_Subtract; - case SG_BLENDOP_REVERSE_SUBTRACT: return WGPUBlendOperation_ReverseSubtract; - default: SOKOL_UNREACHABLE; return WGPUBlendOperation_Force32; - } -} - -_SOKOL_PRIVATE WGPUBlendFactor _sg_wgpu_blendfactor(sg_blend_factor f) { - switch (f) { - case SG_BLENDFACTOR_ZERO: return WGPUBlendFactor_Zero; - case SG_BLENDFACTOR_ONE: return WGPUBlendFactor_One; - case SG_BLENDFACTOR_SRC_COLOR: return WGPUBlendFactor_SrcColor; - case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return WGPUBlendFactor_OneMinusSrcColor; - case SG_BLENDFACTOR_SRC_ALPHA: return WGPUBlendFactor_SrcAlpha; - case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return WGPUBlendFactor_OneMinusSrcAlpha; - case SG_BLENDFACTOR_DST_COLOR: return WGPUBlendFactor_DstColor; - case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return WGPUBlendFactor_OneMinusDstColor; - case SG_BLENDFACTOR_DST_ALPHA: return WGPUBlendFactor_DstAlpha; - case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return WGPUBlendFactor_OneMinusDstAlpha; - case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return WGPUBlendFactor_SrcAlphaSaturated; - case SG_BLENDFACTOR_BLEND_COLOR: return WGPUBlendFactor_BlendColor; - case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return WGPUBlendFactor_OneMinusBlendColor; - /* FIXME: separate blend alpha value not supported? */ - case SG_BLENDFACTOR_BLEND_ALPHA: return WGPUBlendFactor_BlendColor; - case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return WGPUBlendFactor_OneMinusBlendColor; - default: - SOKOL_UNREACHABLE; return WGPUBlendFactor_Force32; - } -} - -_SOKOL_PRIVATE WGPUColorWriteMaskFlags _sg_wgpu_colorwritemask(uint8_t m) { - WGPUColorWriteMaskFlags res = 0; - if (0 != (m & SG_COLORMASK_R)) { - res |= WGPUColorWriteMask_Red; - } - if (0 != (m & SG_COLORMASK_G)) { - res |= WGPUColorWriteMask_Green; - } - if (0 != (m & SG_COLORMASK_B)) { - res |= WGPUColorWriteMask_Blue; - } - if (0 != (m & SG_COLORMASK_A)) { - res |= WGPUColorWriteMask_Alpha; - } - return res; -} - -_SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { - _sg.backend = SG_BACKEND_WGPU; - _sg.features.instancing = true; - _sg.features.origin_top_left = true; - _sg.features.multiple_render_targets = true; - _sg.features.msaa_render_targets = true; - _sg.features.imagetype_3d = true; - _sg.features.imagetype_array = true; - _sg.features.image_clamp_to_border = false; - _sg.features.mrt_independent_blend_state = true; - _sg.features.mrt_independent_write_mask = true; - - /* FIXME: max images size??? */ - _sg.limits.max_image_size_2d = 8 * 1024; - _sg.limits.max_image_size_cube = 8 * 1024; - _sg.limits.max_image_size_3d = 2 * 1024; - _sg.limits.max_image_size_array = 8 * 1024; - _sg.limits.max_image_array_layers = 2 * 1024; - _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; - - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); - _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); - _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); - _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); - - /* FIXME FIXME FIXME: need to check if BC texture compression is - actually supported, currently the WebGPU C-API doesn't allow this - */ - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); -} - -/* - WGPU uniform buffer pool implementation: - - At start of frame, a mapped buffer is grabbed from the pool, - or a new buffer is created if there is no mapped buffer available. - - At end of frame, the current buffer is unmapped before queue submit, - and async-mapped immediately again. - - UNIFORM BUFFER FIXME: - - - As per WebGPU spec, it should be possible to create a Uniform|MapWrite - buffer, but this isn't currently allowed in Dawn. -*/ -_SOKOL_PRIVATE void _sg_wgpu_ubpool_init(const sg_desc* desc) { - - /* Add the max-uniform-update size (64 KB) to the requested buffer size, - this is to prevent validation errors in the WebGPU implementation - if the entire buffer size is used per frame. 64 KB is the allowed - max uniform update size on NVIDIA - */ - _sg.wgpu.ub.num_bytes = desc->uniform_buffer_size + _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE; - - WGPUBufferDescriptor ub_desc; - memset(&ub_desc, 0, sizeof(ub_desc)); - ub_desc.size = _sg.wgpu.ub.num_bytes; - ub_desc.usage = WGPUBufferUsage_Uniform|WGPUBufferUsage_CopyDst; - _sg.wgpu.ub.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &ub_desc); - SOKOL_ASSERT(_sg.wgpu.ub.buf); - - WGPUBindGroupLayoutBinding ub_bglb_desc[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; - memset(ub_bglb_desc, 0, sizeof(ub_bglb_desc)); - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - WGPUShaderStage vis = (stage_index == SG_SHADERSTAGE_VS) ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment; - for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { - int bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; - ub_bglb_desc[stage_index][ub_index].binding = bind_index; - ub_bglb_desc[stage_index][ub_index].visibility = vis; - ub_bglb_desc[stage_index][ub_index].type = WGPUBindingType_UniformBuffer; - ub_bglb_desc[stage_index][ub_index].hasDynamicOffset = true; - } - } - - WGPUBindGroupLayoutDescriptor ub_bgl_desc; - memset(&ub_bgl_desc, 0, sizeof(ub_bgl_desc)); - ub_bgl_desc.bindingCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; - ub_bgl_desc.bindings = &ub_bglb_desc[0][0]; - _sg.wgpu.ub.bindgroup_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &ub_bgl_desc); - SOKOL_ASSERT(_sg.wgpu.ub.bindgroup_layout); - - WGPUBindGroupBinding ub_bgb[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; - memset(ub_bgb, 0, sizeof(ub_bgb)); - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { - int bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; - ub_bgb[stage_index][ub_index].binding = bind_index; - ub_bgb[stage_index][ub_index].buffer = _sg.wgpu.ub.buf; - // FIXME FIXME FIXME FIXME: HACK FOR VALIDATION BUG IN DAWN - ub_bgb[stage_index][ub_index].size = (1<<16); - } - } - WGPUBindGroupDescriptor bg_desc; - memset(&bg_desc, 0, sizeof(bg_desc)); - bg_desc.layout = _sg.wgpu.ub.bindgroup_layout; - bg_desc.bindingCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; - bg_desc.bindings = &ub_bgb[0][0]; - _sg.wgpu.ub.bindgroup = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); - SOKOL_ASSERT(_sg.wgpu.ub.bindgroup); -} - -_SOKOL_PRIVATE void _sg_wgpu_ubpool_discard(void) { - if (_sg.wgpu.ub.buf) { - wgpuBufferRelease(_sg.wgpu.ub.buf); - _sg.wgpu.ub.buf = 0; - } - if (_sg.wgpu.ub.bindgroup) { - wgpuBindGroupRelease(_sg.wgpu.ub.bindgroup); - _sg.wgpu.ub.bindgroup = 0; - } - if (_sg.wgpu.ub.bindgroup_layout) { - wgpuBindGroupLayoutRelease(_sg.wgpu.ub.bindgroup_layout); - _sg.wgpu.ub.bindgroup_layout = 0; - } - for (int i = 0; i < _sg.wgpu.ub.stage.num; i++) { - if (_sg.wgpu.ub.stage.buf[i]) { - wgpuBufferRelease(_sg.wgpu.ub.stage.buf[i]); - _sg.wgpu.ub.stage.buf[i] = 0; - _sg.wgpu.ub.stage.ptr[i] = 0; - } - } -} - -_SOKOL_PRIVATE void _sg_wgpu_ubpool_mapped_callback(WGPUBufferMapAsyncStatus status, void* data, uint64_t data_len, void* user_data) { - if (!_sg.wgpu.valid) { - return; - } - /* FIXME: better handling for this */ - if (WGPUBufferMapAsyncStatus_Success != status) { - SOKOL_LOG("Mapping uniform buffer failed!\n"); - SOKOL_ASSERT(false); - } - SOKOL_ASSERT(data && (data_len == _sg.wgpu.ub.num_bytes)); - int index = (int)(intptr_t) user_data; - SOKOL_ASSERT(index < _sg.wgpu.ub.stage.num); - SOKOL_ASSERT(0 == _sg.wgpu.ub.stage.ptr[index]); - _sg.wgpu.ub.stage.ptr[index] = (uint8_t*) data; -} - -_SOKOL_PRIVATE void _sg_wgpu_ubpool_next_frame(bool first_frame) { - - /* immediately request a new mapping for the last frame's current staging buffer */ - if (!first_frame) { - WGPUBuffer ub_src = _sg.wgpu.ub.stage.buf[_sg.wgpu.ub.stage.cur]; - wgpuBufferMapWriteAsync(ub_src, _sg_wgpu_ubpool_mapped_callback, (void*)(intptr_t)_sg.wgpu.ub.stage.cur); - } - - /* rewind per-frame offsets */ - _sg.wgpu.ub.offset = 0; - memset(&_sg.wgpu.ub.bind_offsets, 0, sizeof(_sg.wgpu.ub.bind_offsets)); - - /* check if a mapped staging buffer is available, otherwise create one */ - for (int i = 0; i < _sg.wgpu.ub.stage.num; i++) { - if (_sg.wgpu.ub.stage.ptr[i]) { - _sg.wgpu.ub.stage.cur = i; - return; - } - } - - /* no mapped uniform buffer available, create one */ - SOKOL_ASSERT(_sg.wgpu.ub.stage.num < _SG_WGPU_STAGING_PIPELINE_SIZE); - _sg.wgpu.ub.stage.cur = _sg.wgpu.ub.stage.num++; - const int cur = _sg.wgpu.ub.stage.cur; - - WGPUBufferDescriptor desc; - memset(&desc, 0, sizeof(desc)); - desc.size = _sg.wgpu.ub.num_bytes; - desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_MapWrite; - WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &desc); - _sg.wgpu.ub.stage.buf[cur] = res.buffer; - _sg.wgpu.ub.stage.ptr[cur] = (uint8_t*) res.data; - SOKOL_ASSERT(_sg.wgpu.ub.stage.buf[cur]); - SOKOL_ASSERT(_sg.wgpu.ub.stage.ptr[cur]); - SOKOL_ASSERT(res.dataLength == _sg.wgpu.ub.num_bytes); -} - -_SOKOL_PRIVATE void _sg_wgpu_ubpool_flush(void) { - /* unmap staging buffer and copy to uniform buffer */ - const int cur = _sg.wgpu.ub.stage.cur; - SOKOL_ASSERT(_sg.wgpu.ub.stage.ptr[cur]); - _sg.wgpu.ub.stage.ptr[cur] = 0; - WGPUBuffer src_buf = _sg.wgpu.ub.stage.buf[cur]; - wgpuBufferUnmap(src_buf); - if (_sg.wgpu.ub.offset > 0) { - WGPUBuffer dst_buf = _sg.wgpu.ub.buf; - wgpuCommandEncoderCopyBufferToBuffer(_sg.wgpu.render_cmd_enc, src_buf, 0, dst_buf, 0, _sg.wgpu.ub.offset); - } -} - -/* helper function to compute number of bytes needed in staging buffer to copy image data */ -_SOKOL_PRIVATE uint32_t _sg_wgpu_image_data_buffer_size(const _sg_image_t* img) { - uint32_t num_bytes = 0; - const uint32_t num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; - const uint32_t num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices : 1; - for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { - const uint32_t mip_width = _sg_max(img->cmn.width >> mip_index, 1); - const uint32_t mip_height = _sg_max(img->cmn.height >> mip_index, 1); - /* row-pitch must be 256-aligend */ - const uint32_t bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, _SG_WGPU_ROWPITCH_ALIGN); - num_bytes += bytes_per_slice * num_slices * num_faces; - } - return num_bytes; -} - -/* helper function to copy image data into a texture via a staging buffer, returns number of - bytes copied -*/ -_SOKOL_PRIVATE uint32_t _sg_wgpu_copy_image_data(WGPUBuffer stg_buf, uint8_t* stg_base_ptr, uint32_t stg_base_offset, _sg_image_t* img, const sg_image_data* data) { - SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); - SOKOL_ASSERT(stg_buf && stg_base_ptr); - SOKOL_ASSERT(img); - SOKOL_ASSERT(data); - uint32_t stg_offset = stg_base_offset; - const uint32_t num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; - const uint32_t num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices : 1; - const sg_pixel_format fmt = img->cmn.pixel_format; - WGPUBufferCopyView src_view; - memset(&src_view, 0, sizeof(src_view)); - src_view.buffer = stg_buf; - WGPUTextureCopyView dst_view; - memset(&dst_view, 0, sizeof(dst_view)); - dst_view.texture = img->wgpu.tex; - WGPUExtent3D extent; - memset(&extent, 0, sizeof(extent)); - - for (uint32_t face_index = 0; face_index < num_faces; face_index++) { - for (uint32_t mip_index = 0; mip_index < (uint32_t)img->cmn.num_mipmaps; mip_index++) { - SOKOL_ASSERT(data->subimage[face_index][mip_index].ptr); - SOKOL_ASSERT(data->subimage[face_index][mip_index].size > 0); - const uint8_t* src_base_ptr = (const uint8_t*)data->subimage[face_index][mip_index].ptr; - SOKOL_ASSERT(src_base_ptr); - uint8_t* dst_base_ptr = stg_base_ptr + stg_offset; - - const uint32_t mip_width = _sg_max(img->cmn.width >> mip_index, 1); - const uint32_t mip_height = _sg_max(img->cmn.height >> mip_index, 1); - const uint32_t mip_depth = (img->cmn.type == SG_IMAGETYPE_3D) ? _sg_max(img->cmn.num_slices >> mip_index, 1) : 1; - const uint32_t num_rows = _sg_num_rows(fmt, mip_height); - const uint32_t src_bytes_per_row = _sg_row_pitch(fmt, mip_width, 1); - const uint32_t dst_bytes_per_row = _sg_row_pitch(fmt, mip_width, _SG_WGPU_ROWPITCH_ALIGN); - const uint32_t src_bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); - const uint32_t dst_bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, _SG_WGPU_ROWPITCH_ALIGN); - SOKOL_ASSERT((uint32_t)data->subimage[face_index][mip_index].size == (src_bytes_per_slice * num_slices)); - SOKOL_ASSERT(src_bytes_per_row <= dst_bytes_per_row); - SOKOL_ASSERT(src_bytes_per_slice == (src_bytes_per_row * num_rows)); - SOKOL_ASSERT(dst_bytes_per_slice == (dst_bytes_per_row * num_rows)); - _SOKOL_UNUSED(src_bytes_per_slice); - - /* copy data into mapped staging buffer */ - if (src_bytes_per_row == dst_bytes_per_row) { - /* can do a single memcpy */ - uint32_t num_bytes = data->subimage[face_index][mip_index].size; - memcpy(dst_base_ptr, src_base_ptr, num_bytes); - } - else { - /* src/dst pitch doesn't match, need to copy row by row */ - uint8_t* dst_ptr = dst_base_ptr; - const uint8_t* src_ptr = src_base_ptr; - for (uint32_t slice_index = 0; slice_index < num_slices; slice_index++) { - SOKOL_ASSERT(dst_ptr == dst_base_ptr + slice_index * dst_bytes_per_slice); - for (uint32_t row_index = 0; row_index < num_rows; row_index++) { - memcpy(dst_ptr, src_ptr, src_bytes_per_row); - src_ptr += src_bytes_per_row; - dst_ptr += dst_bytes_per_row; - } - } - } - - /* record the staging copy operation into command encoder */ - src_view.imageHeight = mip_height; - src_view.rowPitch = dst_bytes_per_row; - dst_view.mipLevel = mip_index; - extent.width = mip_width; - extent.height = mip_height; - extent.depth = mip_depth; - SOKOL_ASSERT((img->cmn.type != SG_IMAGETYPE_CUBE) || (num_slices == 1)); - for (uint32_t slice_index = 0; slice_index < num_slices; slice_index++) { - const uint32_t layer_index = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? slice_index : face_index; - src_view.offset = stg_offset; - dst_view.arrayLayer = layer_index; - wgpuCommandEncoderCopyBufferToTexture(_sg.wgpu.staging_cmd_enc, &src_view, &dst_view, &extent); - stg_offset += dst_bytes_per_slice; - SOKOL_ASSERT(stg_offset <= _sg.wgpu.staging.num_bytes); - } - } - } - SOKOL_ASSERT(stg_offset >= stg_base_offset); - return (stg_offset - stg_base_offset); -} - -/* - The WGPU staging buffer implementation: - - Very similar to the uniform buffer pool, there's a pool of big - per-frame staging buffers, each must be big enough to hold - all data uploaded to dynamic resources for one frame. - - Staging buffers are created on demand and reused, because the - 'frame pipeline depth' of WGPU isn't predictable. - - The difference to the uniform buffer system is that there isn't - a 1:1 relationship for source- and destination for the - data-copy operation. There's always one staging buffer as copy-source - per frame, but many copy-destinations (regular vertex/index buffers - or images). Instead of one big copy-operation at the end of the frame, - multiple copy-operations will be written throughout the frame. -*/ -_SOKOL_PRIVATE void _sg_wgpu_staging_init(const sg_desc* desc) { - SOKOL_ASSERT(desc && (desc->staging_buffer_size > 0)); - _sg.wgpu.staging.num_bytes = desc->staging_buffer_size; - /* there's actually nothing more to do here */ -} - -_SOKOL_PRIVATE void _sg_wgpu_staging_discard(void) { - for (int i = 0; i < _sg.wgpu.staging.num; i++) { - if (_sg.wgpu.staging.buf[i]) { - wgpuBufferRelease(_sg.wgpu.staging.buf[i]); - _sg.wgpu.staging.buf[i] = 0; - _sg.wgpu.staging.ptr[i] = 0; - } - } -} - -_SOKOL_PRIVATE void _sg_wgpu_staging_mapped_callback(WGPUBufferMapAsyncStatus status, void* data, uint64_t data_len, void* user_data) { - if (!_sg.wgpu.valid) { - return; - } - /* FIXME: better handling for this */ - if (WGPUBufferMapAsyncStatus_Success != status) { - SOKOL_ASSERT("Mapping staging buffer failed!\n"); - SOKOL_ASSERT(false); - } - SOKOL_ASSERT(data && (data_len == _sg.wgpu.staging.num_bytes)); - int index = (int)(intptr_t) user_data; - SOKOL_ASSERT(index < _sg.wgpu.staging.num); - SOKOL_ASSERT(0 == _sg.wgpu.staging.ptr[index]); - _sg.wgpu.staging.ptr[index] = (uint8_t*) data; -} - -_SOKOL_PRIVATE void _sg_wgpu_staging_next_frame(bool first_frame) { - - /* immediately request a new mapping for the last frame's current staging buffer */ - if (!first_frame) { - WGPUBuffer cur_buf = _sg.wgpu.staging.buf[_sg.wgpu.staging.cur]; - wgpuBufferMapWriteAsync(cur_buf, _sg_wgpu_staging_mapped_callback, (void*)(intptr_t)_sg.wgpu.staging.cur); - } - - /* rewind staging-buffer offset */ - _sg.wgpu.staging.offset = 0; - - /* check if mapped staging buffer is available, otherwise create one */ - for (int i = 0; i < _sg.wgpu.staging.num; i++) { - if (_sg.wgpu.staging.ptr[i]) { - _sg.wgpu.staging.cur = i; - return; - } - } - - /* no mapped buffer available, create one */ - SOKOL_ASSERT(_sg.wgpu.staging.num < _SG_WGPU_STAGING_PIPELINE_SIZE); - _sg.wgpu.staging.cur = _sg.wgpu.staging.num++; - const int cur = _sg.wgpu.staging.cur; - - WGPUBufferDescriptor desc; - memset(&desc, 0, sizeof(desc)); - desc.size = _sg.wgpu.staging.num_bytes; - desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_MapWrite; - WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &desc); - _sg.wgpu.staging.buf[cur] = res.buffer; - _sg.wgpu.staging.ptr[cur] = (uint8_t*) res.data; - SOKOL_ASSERT(_sg.wgpu.staging.buf[cur]); - SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); - SOKOL_ASSERT(res.dataLength == _sg.wgpu.staging.num_bytes); -} - -_SOKOL_PRIVATE uint32_t _sg_wgpu_staging_copy_to_buffer(WGPUBuffer dst_buf, uint32_t dst_buf_offset, const void* data, uint32_t data_num_bytes) { - /* Copy a chunk of data into the staging buffer, and record a blit-operation into - the command encoder, bump the offset for the next data chunk, return 0 if there - was not enough room in the staging buffer, return the number of actually - copied bytes on success. - - NOTE: that the number of staging bytes to be copied must be a multiple of 4. - - */ - SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); - SOKOL_ASSERT((dst_buf_offset & 3) == 0); - SOKOL_ASSERT(data_num_bytes > 0); - uint32_t copy_num_bytes = _sg_roundup(data_num_bytes, 4); - if ((_sg.wgpu.staging.offset + copy_num_bytes) >= _sg.wgpu.staging.num_bytes) { - SOKOL_LOG("WGPU: Per frame staging buffer full (in _sg_wgpu_staging_copy_to_buffer())!\n"); - return false; - } - const int cur = _sg.wgpu.staging.cur; - SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); - uint32_t stg_buf_offset = _sg.wgpu.staging.offset; - uint8_t* stg_ptr = _sg.wgpu.staging.ptr[cur] + stg_buf_offset; - memcpy(stg_ptr, data, data_num_bytes); - WGPUBuffer stg_buf = _sg.wgpu.staging.buf[cur]; - wgpuCommandEncoderCopyBufferToBuffer(_sg.wgpu.staging_cmd_enc, stg_buf, stg_buf_offset, dst_buf, dst_buf_offset, copy_num_bytes); - _sg.wgpu.staging.offset = stg_buf_offset + copy_num_bytes; - return copy_num_bytes; -} - -_SOKOL_PRIVATE bool _sg_wgpu_staging_copy_to_texture(_sg_image_t* img, const sg_image_data* data) { - /* similar to _sg_wgpu_staging_copy_to_buffer(), but with image data instead */ - SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); - uint32_t num_bytes = _sg_wgpu_image_data_buffer_size(img); - if ((_sg.wgpu.staging.offset + num_bytes) >= _sg.wgpu.staging.num_bytes) { - SOKOL_LOG("WGPU: Per frame staging buffer full (in _sg_wgpu_staging_copy_to_texture)!\n"); - return false; - } - const int cur = _sg.wgpu.staging.cur; - SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); - uint32_t stg_offset = _sg.wgpu.staging.offset; - uint8_t* stg_ptr = _sg.wgpu.staging.ptr[cur]; - WGPUBuffer stg_buf = _sg.wgpu.staging.buf[cur]; - uint32_t bytes_copied = _sg_wgpu_copy_image_data(stg_buf, stg_ptr, stg_offset, img, data); - _SOKOL_UNUSED(bytes_copied); - SOKOL_ASSERT(bytes_copied == num_bytes); - _sg.wgpu.staging.offset = _sg_roundup(stg_offset + num_bytes, _SG_WGPU_STAGING_ALIGN); - return true; -} - -_SOKOL_PRIVATE void _sg_wgpu_staging_unmap(void) { - /* called at end of frame before queue-submit */ - const int cur = _sg.wgpu.staging.cur; - SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); - _sg.wgpu.staging.ptr[cur] = 0; - wgpuBufferUnmap(_sg.wgpu.staging.buf[cur]); -} - -/*--- WGPU sampler cache functions ---*/ -_SOKOL_PRIVATE void _sg_wgpu_init_sampler_cache(const sg_desc* desc) { - SOKOL_ASSERT(desc->sampler_cache_size > 0); - _sg_smpcache_init(&_sg.wgpu.sampler_cache, desc->sampler_cache_size); -} - -_SOKOL_PRIVATE void _sg_wgpu_destroy_sampler_cache(void) { - SOKOL_ASSERT(_sg.wgpu.sampler_cache.items); - SOKOL_ASSERT(_sg.wgpu.sampler_cache.num_items <= _sg.wgpu.sampler_cache.capacity); - for (int i = 0; i < _sg.wgpu.sampler_cache.num_items; i++) { - wgpuSamplerRelease((WGPUSampler)_sg_smpcache_sampler(&_sg.wgpu.sampler_cache, i)); - } - _sg_smpcache_discard(&_sg.wgpu.sampler_cache); -} - -_SOKOL_PRIVATE WGPUSampler _sg_wgpu_create_sampler(const sg_image_desc* img_desc) { - SOKOL_ASSERT(img_desc); - int index = _sg_smpcache_find_item(&_sg.wgpu.sampler_cache, img_desc); - if (index >= 0) { - /* reuse existing sampler */ - return (WGPUSampler) _sg_smpcache_sampler(&_sg.wgpu.sampler_cache, index); - } - else { - /* create a new WGPU sampler and add to sampler cache */ - /* FIXME: anisotropic filtering not supported? */ - WGPUSamplerDescriptor smp_desc; - memset(&smp_desc, 0, sizeof(smp_desc)); - smp_desc.addressModeU = _sg_wgpu_sampler_addrmode(img_desc->wrap_u); - smp_desc.addressModeV = _sg_wgpu_sampler_addrmode(img_desc->wrap_v); - smp_desc.addressModeW = _sg_wgpu_sampler_addrmode(img_desc->wrap_w); - smp_desc.magFilter = _sg_wgpu_sampler_minmagfilter(img_desc->mag_filter); - smp_desc.minFilter = _sg_wgpu_sampler_minmagfilter(img_desc->min_filter); - smp_desc.mipmapFilter = _sg_wgpu_sampler_mipfilter(img_desc->min_filter); - smp_desc.lodMinClamp = img_desc->min_lod; - smp_desc.lodMaxClamp = img_desc->max_lod; - WGPUSampler smp = wgpuDeviceCreateSampler(_sg.wgpu.dev, &smp_desc); - SOKOL_ASSERT(smp); - _sg_smpcache_add_item(&_sg.wgpu.sampler_cache, img_desc, (uintptr_t)smp); - return smp; - } -} - -/*--- WGPU backend API functions ---*/ -_SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { - SOKOL_ASSERT(desc); - SOKOL_ASSERT(desc->context.wgpu.device); - SOKOL_ASSERT(desc->context.wgpu.render_view_cb || desc->context.wgpu.render_view_userdata_cb); - SOKOL_ASSERT(desc->context.wgpu.resolve_view_cb || desc->context.wgpu.resolve_view_userdata_cb); - SOKOL_ASSERT(desc->context.wgpu.depth_stencil_view_cb || desc->context.wgpu.depth_stencil_view_userdata_cb); - SOKOL_ASSERT(desc->uniform_buffer_size > 0); - SOKOL_ASSERT(desc->staging_buffer_size > 0); - _sg.backend = SG_BACKEND_WGPU; - _sg.wgpu.valid = true; - _sg.wgpu.dev = (WGPUDevice) desc->context.wgpu.device; - _sg.wgpu.render_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.render_view_cb; - _sg.wgpu.render_view_userdata_cb = (WGPUTextureView(*)(void*)) desc->context.wgpu.render_view_userdata_cb; - _sg.wgpu.resolve_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.resolve_view_cb; - _sg.wgpu.resolve_view_userdata_cb = (WGPUTextureView(*)(void*)) desc->context.wgpu.resolve_view_userdata_cb; - _sg.wgpu.depth_stencil_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.depth_stencil_view_cb; - _sg.wgpu.depth_stencil_view_userdata_cb = (WGPUTextureView(*)(void*)) desc->context.wgpu.depth_stencil_view_userdata_cb; - _sg.wgpu.user_data = desc->context.wgpu.user_data; - _sg.wgpu.queue = wgpuDeviceCreateQueue(_sg.wgpu.dev); - SOKOL_ASSERT(_sg.wgpu.queue); - - /* setup WebGPU features and limits */ - _sg_wgpu_init_caps(); - - /* setup the sampler cache, uniform and staging buffer pools */ - _sg_wgpu_init_sampler_cache(&_sg.desc); - _sg_wgpu_ubpool_init(desc); - _sg_wgpu_ubpool_next_frame(true); - _sg_wgpu_staging_init(desc); - _sg_wgpu_staging_next_frame(true); - - /* create an empty bind group for shader stages without bound images */ - WGPUBindGroupLayoutDescriptor bgl_desc; - memset(&bgl_desc, 0, sizeof(bgl_desc)); - WGPUBindGroupLayout empty_bgl = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &bgl_desc); - SOKOL_ASSERT(empty_bgl); - WGPUBindGroupDescriptor bg_desc; - memset(&bg_desc, 0, sizeof(bg_desc)); - bg_desc.layout = empty_bgl; - _sg.wgpu.empty_bind_group = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); - SOKOL_ASSERT(_sg.wgpu.empty_bind_group); - wgpuBindGroupLayoutRelease(empty_bgl); - - /* create initial per-frame command encoders */ - WGPUCommandEncoderDescriptor cmd_enc_desc; - memset(&cmd_enc_desc, 0, sizeof(cmd_enc_desc)); - _sg.wgpu.render_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); - SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); - _sg.wgpu.staging_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); - SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); -} - -_SOKOL_PRIVATE void _sg_wgpu_discard_backend(void) { - SOKOL_ASSERT(_sg.wgpu.valid); - SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); - SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); - _sg.wgpu.valid = false; - _sg_wgpu_ubpool_discard(); - _sg_wgpu_staging_discard(); - _sg_wgpu_destroy_sampler_cache(); - wgpuBindGroupRelease(_sg.wgpu.empty_bind_group); - wgpuCommandEncoderRelease(_sg.wgpu.render_cmd_enc); - _sg.wgpu.render_cmd_enc = 0; - wgpuCommandEncoderRelease(_sg.wgpu.staging_cmd_enc); - _sg.wgpu.staging_cmd_enc = 0; - if (_sg.wgpu.queue) { - wgpuQueueRelease(_sg.wgpu.queue); - _sg.wgpu.queue = 0; - } -} - -_SOKOL_PRIVATE void _sg_wgpu_reset_state_cache(void) { - SOKOL_LOG("_sg_wgpu_reset_state_cache: FIXME\n"); -} - -_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_wgpu_destroy_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); -} - -_SOKOL_PRIVATE void _sg_wgpu_activate_context(_sg_context_t* ctx) { - (void)ctx; - SOKOL_LOG("_sg_wgpu_activate_context: FIXME\n"); -} - -_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { - SOKOL_ASSERT(buf && desc); - const bool injected = (0 != desc->wgpu_buffer); - _sg_buffer_common_init(&buf->cmn, desc); - if (injected) { - buf->wgpu.buf = (WGPUBuffer) desc->wgpu_buffer; - wgpuBufferReference(buf->wgpu.buf); - } - else { - WGPUBufferDescriptor wgpu_buf_desc; - memset(&wgpu_buf_desc, 0, sizeof(wgpu_buf_desc)); - wgpu_buf_desc.usage = _sg_wgpu_buffer_usage(buf->cmn.type, buf->cmn.usage); - wgpu_buf_desc.size = buf->cmn.size; - if (SG_USAGE_IMMUTABLE == buf->cmn.usage) { - SOKOL_ASSERT(desc->data.ptr); - WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &wgpu_buf_desc); - buf->wgpu.buf = res.buffer; - SOKOL_ASSERT(res.data && (res.dataLength == buf->cmn.size)); - memcpy(res.data, desc->data.ptr, buf->cmn.size); - wgpuBufferUnmap(res.buffer); - } - else { - buf->wgpu.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &wgpu_buf_desc); - } - } - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_wgpu_destroy_buffer(_sg_buffer_t* buf) { - SOKOL_ASSERT(buf); - WGPUBuffer wgpu_buf = buf->wgpu.buf; - if (0 != wgpu_buf) { - wgpuBufferRelease(wgpu_buf); - } -} - -_SOKOL_PRIVATE void _sg_wgpu_init_texdesc_common(WGPUTextureDescriptor* wgpu_tex_desc, const sg_image_desc* desc) { - wgpu_tex_desc->usage = WGPUTextureUsage_Sampled|WGPUTextureUsage_CopyDst; - wgpu_tex_desc->dimension = _sg_wgpu_tex_dim(desc->type); - wgpu_tex_desc->size.width = desc->width; - wgpu_tex_desc->size.height = desc->height; - if (desc->type == SG_IMAGETYPE_3D) { - wgpu_tex_desc->size.depth = desc->num_slices; - wgpu_tex_desc->arrayLayerCount = 1; - } - else if (desc->type == SG_IMAGETYPE_CUBE) { - wgpu_tex_desc->size.depth = 1; - wgpu_tex_desc->arrayLayerCount = 6; - } - else { - wgpu_tex_desc->size.depth = 1; - wgpu_tex_desc->arrayLayerCount = desc->num_slices; - } - wgpu_tex_desc->format = _sg_wgpu_textureformat(desc->pixel_format); - wgpu_tex_desc->mipLevelCount = desc->num_mipmaps; - wgpu_tex_desc->sampleCount = 1; -} - -_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const sg_image_desc* desc) { - SOKOL_ASSERT(img && desc); - SOKOL_ASSERT(_sg.wgpu.dev); - SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); - - _sg_image_common_init(&img->cmn, desc); - - const bool injected = (0 != desc->wgpu_texture); - const bool is_msaa = desc->sample_count > 1; - WGPUTextureDescriptor wgpu_tex_desc; - memset(&wgpu_tex_desc, 0, sizeof(wgpu_tex_desc)); - _sg_wgpu_init_texdesc_common(&wgpu_tex_desc, desc); - if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { - SOKOL_ASSERT(img->cmn.render_target); - SOKOL_ASSERT(img->cmn.type == SG_IMAGETYPE_2D); - SOKOL_ASSERT(img->cmn.num_mipmaps == 1); - SOKOL_ASSERT(!injected); - /* NOTE: a depth-stencil texture will never be MSAA-resolved, so there - won't be a separate MSAA- and resolve-texture - */ - wgpu_tex_desc.usage = WGPUTextureUsage_OutputAttachment; - wgpu_tex_desc.sampleCount = desc->sample_count; - img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); - SOKOL_ASSERT(img->wgpu.tex); - } - else { - if (injected) { - img->wgpu.tex = (WGPUTexture) desc->wgpu_texture; - wgpuTextureReference(img->wgpu.tex); - } - else { - /* NOTE: in the MSAA-rendertarget case, both the MSAA texture *and* - the resolve texture need OutputAttachment usage - */ - if (img->cmn.render_target) { - wgpu_tex_desc.usage = WGPUTextureUsage_Sampled|WGPUTextureUsage_OutputAttachment; - } - img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); - SOKOL_ASSERT(img->wgpu.tex); - - /* copy content into texture via a throw-away staging buffer */ - if (desc->usage == SG_USAGE_IMMUTABLE && !desc->render_target) { - WGPUBufferDescriptor wgpu_buf_desc; - memset(&wgpu_buf_desc, 0, sizeof(wgpu_buf_desc)); - wgpu_buf_desc.size = _sg_wgpu_image_data_buffer_size(img); - wgpu_buf_desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_CopyDst; - WGPUCreateBufferMappedResult map = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &wgpu_buf_desc); - SOKOL_ASSERT(map.buffer && map.data); - uint32_t num_bytes = _sg_wgpu_copy_image_data(map.buffer, (uint8_t*)map.data, 0, img, &desc->data); - _SOKOL_UNUSED(num_bytes); - SOKOL_ASSERT(num_bytes == wgpu_buf_desc.size); - wgpuBufferUnmap(map.buffer); - wgpuBufferRelease(map.buffer); - } - } - - /* create texture view object */ - WGPUTextureViewDescriptor wgpu_view_desc; - memset(&wgpu_view_desc, 0, sizeof(wgpu_view_desc)); - wgpu_view_desc.dimension = _sg_wgpu_tex_viewdim(desc->type); - img->wgpu.tex_view = wgpuTextureCreateView(img->wgpu.tex, &wgpu_view_desc); - - /* if render target and MSAA, then a separate texture in MSAA format is needed - which will be resolved into the regular texture at the end of the - offscreen-render pass - */ - if (desc->render_target && is_msaa) { - wgpu_tex_desc.dimension = WGPUTextureDimension_2D; - wgpu_tex_desc.size.depth = 1; - wgpu_tex_desc.arrayLayerCount = 1; - wgpu_tex_desc.mipLevelCount = 1; - wgpu_tex_desc.usage = WGPUTextureUsage_OutputAttachment; - wgpu_tex_desc.sampleCount = desc->sample_count; - img->wgpu.msaa_tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); - SOKOL_ASSERT(img->wgpu.msaa_tex); - } - - /* create sampler via shared-sampler-cache */ - img->wgpu.sampler = _sg_wgpu_create_sampler(desc); - SOKOL_ASSERT(img->wgpu.sampler); - } - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_wgpu_destroy_image(_sg_image_t* img) { - SOKOL_ASSERT(img); - if (img->wgpu.tex) { - wgpuTextureRelease(img->wgpu.tex); - img->wgpu.tex = 0; - } - if (img->wgpu.tex_view) { - wgpuTextureViewRelease(img->wgpu.tex_view); - img->wgpu.tex_view = 0; - } - if (img->wgpu.msaa_tex) { - wgpuTextureRelease(img->wgpu.msaa_tex); - img->wgpu.msaa_tex = 0; - } - /* NOTE: do *not* destroy the sampler from the shared-sampler-cache */ - img->wgpu.sampler = 0; -} - -/* - How BindGroups work in WebGPU: - - - up to 4 bind groups can be bound simultaneously - - up to 16 bindings per bind group - - 'binding' slots are local per bind group - - in the shader: - layout(set=0, binding=1) corresponds to bind group 0, binding 1 - - Now how to map this to sokol-gfx's bind model: - - Reduce SG_MAX_SHADERSTAGE_IMAGES to 8, then: - - 1 bind group for all 8 uniform buffers - 1 bind group for vertex shader textures + samplers - 1 bind group for fragment shader textures + samples - - Alternatively: - - 1 bind group for 8 uniform buffer slots - 1 bind group for 8 vs images + 8 vs samplers - 1 bind group for 12 fs images - 1 bind group for 12 fs samplers - - I guess this means that we need to create BindGroups on the - fly during sg_apply_bindings() :/ -*/ -_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { - SOKOL_ASSERT(shd && desc); - SOKOL_ASSERT(desc->vs.bytecode.ptr && desc->fs.bytecode.ptr); - _sg_shader_common_init(&shd->cmn, desc); - - bool success = true; - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; - SOKOL_ASSERT((stage_desc->bytecode.size & 3) == 0); - - _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; - _sg_wgpu_shader_stage_t* wgpu_stage = &shd->wgpu.stage[stage_index]; - - _sg_strcpy(&wgpu_stage->entry, stage_desc->entry); - WGPUShaderModuleDescriptor wgpu_shdmod_desc; - memset(&wgpu_shdmod_desc, 0, sizeof(wgpu_shdmod_desc)); - wgpu_shdmod_desc.codeSize = stage_desc->bytecode.size >> 2; - wgpu_shdmod_desc.code = (const uint32_t*) stage_desc->bytecode.ptr; - wgpu_stage->module = wgpuDeviceCreateShaderModule(_sg.wgpu.dev, &wgpu_shdmod_desc); - if (0 == wgpu_stage->module) { - success = false; - } - - /* create image/sampler bind group for the shader stage */ - WGPUShaderStage vis = (stage_index == SG_SHADERSTAGE_VS) ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment; - int num_imgs = cmn_stage->num_images; - if (num_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { - num_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; - } - WGPUBindGroupLayoutBinding bglb_desc[_SG_WGPU_MAX_SHADERSTAGE_IMAGES * 2]; - memset(bglb_desc, 0, sizeof(bglb_desc)); - for (int img_index = 0; img_index < num_imgs; img_index++) { - /* texture- and sampler-bindings */ - WGPUBindGroupLayoutBinding* tex_desc = &bglb_desc[img_index*2 + 0]; - WGPUBindGroupLayoutBinding* smp_desc = &bglb_desc[img_index*2 + 1]; - - tex_desc->binding = img_index; - tex_desc->visibility = vis; - tex_desc->type = WGPUBindingType_SampledTexture; - tex_desc->textureDimension = _sg_wgpu_tex_viewdim(cmn_stage->images[img_index].image_type); - tex_desc->textureComponentType = _sg_wgpu_tex_comptype(cmn_stage->images[img_index].sampler_type); - - smp_desc->binding = img_index + _SG_WGPU_MAX_SHADERSTAGE_IMAGES; - smp_desc->visibility = vis; - smp_desc->type = WGPUBindingType_Sampler; - } - WGPUBindGroupLayoutDescriptor img_bgl_desc; - memset(&img_bgl_desc, 0, sizeof(img_bgl_desc)); - img_bgl_desc.bindingCount = num_imgs * 2; - img_bgl_desc.bindings = &bglb_desc[0]; - wgpu_stage->bind_group_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &img_bgl_desc); - SOKOL_ASSERT(wgpu_stage->bind_group_layout); - } - return success ? SG_RESOURCESTATE_VALID : SG_RESOURCESTATE_FAILED; -} - -_SOKOL_PRIVATE void _sg_wgpu_destroy_shader(_sg_shader_t* shd) { - SOKOL_ASSERT(shd); - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - _sg_wgpu_shader_stage_t* wgpu_stage = &shd->wgpu.stage[stage_index]; - if (wgpu_stage->module) { - wgpuShaderModuleRelease(wgpu_stage->module); - wgpu_stage->module = 0; - } - if (wgpu_stage->bind_group_layout) { - wgpuBindGroupLayoutRelease(wgpu_stage->bind_group_layout); - wgpu_stage->bind_group_layout = 0; - } - } -} - -_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { - SOKOL_ASSERT(pip && shd && desc); - SOKOL_ASSERT(desc->shader.id == shd->slot.id); - SOKOL_ASSERT(shd->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout); - SOKOL_ASSERT(shd->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout); - pip->shader = shd; - _sg_pipeline_common_init(&pip->cmn, desc); - pip->wgpu.stencil_ref = (uint32_t) desc->stencil.ref; - - WGPUBindGroupLayout pip_bgl[3] = { - _sg.wgpu.ub.bindgroup_layout, - shd->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout, - shd->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout - }; - WGPUPipelineLayoutDescriptor pl_desc; - memset(&pl_desc, 0, sizeof(pl_desc)); - pl_desc.bindGroupLayoutCount = 3; - pl_desc.bindGroupLayouts = &pip_bgl[0]; - WGPUPipelineLayout pip_layout = wgpuDeviceCreatePipelineLayout(_sg.wgpu.dev, &pl_desc); - - WGPUVertexBufferLayoutDescriptor vb_desc[SG_MAX_SHADERSTAGE_BUFFERS]; - memset(&vb_desc, 0, sizeof(vb_desc)); - WGPUVertexAttributeDescriptor va_desc[SG_MAX_SHADERSTAGE_BUFFERS][SG_MAX_VERTEX_ATTRIBUTES]; - memset(&va_desc, 0, sizeof(va_desc)); - int vb_idx = 0; - for (; vb_idx < SG_MAX_SHADERSTAGE_BUFFERS; vb_idx++) { - const sg_buffer_layout_desc* src_vb_desc = &desc->layout.buffers[vb_idx]; - if (0 == src_vb_desc->stride) { - break; - } - vb_desc[vb_idx].arrayStride = src_vb_desc->stride; - vb_desc[vb_idx].stepMode = _sg_wgpu_stepmode(src_vb_desc->step_func); - /* NOTE: WebGPU has no support for vertex step rate (because that's - not supported by Core Vulkan - */ - int va_idx = 0; - for (int va_loc = 0; va_loc < SG_MAX_VERTEX_ATTRIBUTES; va_loc++) { - const sg_vertex_attr_desc* src_va_desc = &desc->layout.attrs[va_loc]; - if (SG_VERTEXFORMAT_INVALID == src_va_desc->format) { - break; - } - pip->cmn.vertex_layout_valid[src_va_desc->buffer_index] = true; - if (vb_idx == src_va_desc->buffer_index) { - va_desc[vb_idx][va_idx].format = _sg_wgpu_vertexformat(src_va_desc->format); - va_desc[vb_idx][va_idx].offset = src_va_desc->offset; - va_desc[vb_idx][va_idx].shaderLocation = va_loc; - va_idx++; - } - } - vb_desc[vb_idx].attributeCount = va_idx; - vb_desc[vb_idx].attributes = &va_desc[vb_idx][0]; - } - WGPUVertexStateDescriptor vx_state_desc; - memset(&vx_state_desc, 0, sizeof(vx_state_desc)); - vx_state_desc.indexFormat = _sg_wgpu_indexformat(desc->index_type); - vx_state_desc.vertexBufferCount = vb_idx; - vx_state_desc.vertexBuffers = vb_desc; - - WGPURasterizationStateDescriptor rs_desc; - memset(&rs_desc, 0, sizeof(rs_desc)); - rs_desc.frontFace = _sg_wgpu_frontface(desc->face_winding); - rs_desc.cullMode = _sg_wgpu_cullmode(desc->cull_mode); - rs_desc.depthBias = (int32_t) desc->depth.bias; - rs_desc.depthBiasClamp = desc->depth.bias_clamp; - rs_desc.depthBiasSlopeScale = desc->depth.bias_slope_scale; - - WGPUDepthStencilStateDescriptor ds_desc; - memset(&ds_desc, 0, sizeof(ds_desc)); - ds_desc.format = _sg_wgpu_textureformat(desc->depth.pixel_format); - ds_desc.depthWriteEnabled = desc->depth.write_enabled; - ds_desc.depthCompare = _sg_wgpu_comparefunc(desc->depth.compare); - ds_desc.stencilReadMask = desc->stencil.read_mask; - ds_desc.stencilWriteMask = desc->stencil.write_mask; - ds_desc.stencilFront.compare = _sg_wgpu_comparefunc(desc->stencil.front.compare); - ds_desc.stencilFront.failOp = _sg_wgpu_stencilop(desc->stencil.front.fail_op); - ds_desc.stencilFront.depthFailOp = _sg_wgpu_stencilop(desc->stencil.front.depth_fail_op); - ds_desc.stencilFront.passOp = _sg_wgpu_stencilop(desc->stencil.front.pass_op); - ds_desc.stencilBack.compare = _sg_wgpu_comparefunc(desc->stencil.back.compare); - ds_desc.stencilBack.failOp = _sg_wgpu_stencilop(desc->stencil.back.fail_op); - ds_desc.stencilBack.depthFailOp = _sg_wgpu_stencilop(desc->stencil.back.depth_fail_op); - ds_desc.stencilBack.passOp = _sg_wgpu_stencilop(desc->stencil.back.pass_op); - - WGPUProgrammableStageDescriptor fs_desc; - memset(&fs_desc, 0, sizeof(fs_desc)); - fs_desc.module = shd->wgpu.stage[SG_SHADERSTAGE_FS].module; - fs_desc.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_VS].entry.buf; - - WGPUColorStateDescriptor cs_desc[SG_MAX_COLOR_ATTACHMENTS]; - memset(cs_desc, 0, sizeof(cs_desc)); - for (uint32_t i = 0; i < desc->color_count; i++) { - SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); - cs_desc[i].format = _sg_wgpu_textureformat(desc->colors[i].pixel_format); - cs_desc[i].colorBlend.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_rgb); - cs_desc[i].colorBlend.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_rgb); - cs_desc[i].colorBlend.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_rgb); - cs_desc[i].alphaBlend.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_alpha); - cs_desc[i].alphaBlend.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_alpha); - cs_desc[i].alphaBlend.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_alpha); - cs_desc[i].writeMask = _sg_wgpu_colorwritemask(desc->colors[i].write_mask); - } - - WGPURenderPipelineDescriptor pip_desc; - memset(&pip_desc, 0, sizeof(pip_desc)); - pip_desc.layout = pip_layout; - pip_desc.vertexStage.module = shd->wgpu.stage[SG_SHADERSTAGE_VS].module; - pip_desc.vertexStage.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_VS].entry.buf; - pip_desc.fragmentStage = &fs_desc; - pip_desc.vertexState = &vx_state_desc; - pip_desc.primitiveTopology = _sg_wgpu_topology(desc->primitive_type); - pip_desc.rasterizationState = &rs_desc; - pip_desc.sampleCount = desc->sample_count; - if (SG_PIXELFORMAT_NONE != desc->depth.pixel_format) { - pip_desc.depthStencilState = &ds_desc; - } - pip_desc.colorStateCount = desc->color_count; - pip_desc.colorStates = cs_desc; - pip_desc.sampleMask = 0xFFFFFFFF; /* FIXME: ??? */ - pip->wgpu.pip = wgpuDeviceCreateRenderPipeline(_sg.wgpu.dev, &pip_desc); - SOKOL_ASSERT(0 != pip->wgpu.pip); - wgpuPipelineLayoutRelease(pip_layout); - - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_wgpu_destroy_pipeline(_sg_pipeline_t* pip) { - SOKOL_ASSERT(pip); - if (pip == _sg.wgpu.cur_pipeline) { - _sg.wgpu.cur_pipeline = 0; - _Sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; - } - if (pip->wgpu.pip) { - wgpuRenderPipelineRelease(pip->wgpu.pip); - pip->wgpu.pip = 0; - } -} - -_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { - SOKOL_ASSERT(pass && desc); - SOKOL_ASSERT(att_images && att_images[0]); - _sg_pass_common_init(&pass->cmn, desc); - - /* copy image pointers and create render-texture views */ - const sg_pass_attachment_desc* att_desc; - for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { - att_desc = &desc->color_attachments[i]; - if (att_desc->image.id != SG_INVALID_ID) { - SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); - SOKOL_ASSERT(0 == pass->wgpu.color_atts[i].image); - _sg_image_t* img = att_images[i]; - SOKOL_ASSERT(img && (img->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format)); - pass->wgpu.color_atts[i].image = img; - /* create a render-texture-view to render into the right sub-surface */ - const bool is_msaa = img->cmn.sample_count > 1; - WGPUTextureViewDescriptor view_desc; - memset(&view_desc, 0, sizeof(view_desc)); - view_desc.baseMipLevel = is_msaa ? 0 : att_desc->mip_level; - view_desc.mipLevelCount = 1; - view_desc.baseArrayLayer = is_msaa ? 0 : att_desc->slice; - view_desc.arrayLayerCount = 1; - WGPUTexture wgpu_tex = is_msaa ? img->wgpu.msaa_tex : img->wgpu.tex; - SOKOL_ASSERT(wgpu_tex); - pass->wgpu.color_atts[i].render_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); - SOKOL_ASSERT(pass->wgpu.color_atts[i].render_tex_view); - /* ... and if needed a separate resolve texture view */ - if (is_msaa) { - view_desc.baseMipLevel = att_desc->mip_level; - view_desc.baseArrayLayer = att_desc->slice; - WGPUTexture wgpu_tex = img->wgpu.tex; - pass->wgpu.color_atts[i].resolve_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); - SOKOL_ASSERT(pass->wgpu.color_atts[i].resolve_tex_view); - } - } - } - SOKOL_ASSERT(0 == pass->wgpu.ds_att.image); - att_desc = &desc->depth_stencil_attachment; - if (att_desc->image.id != SG_INVALID_ID) { - const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; - SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); - _sg_image_t* ds_img = att_images[ds_img_index]; - pass->wgpu.ds_att.image = ds_img; - /* create a render-texture view */ - SOKOL_ASSERT(0 == att_desc->mip_level); - SOKOL_ASSERT(0 == att_desc->slice); - WGPUTextureViewDescriptor view_desc; - memset(&view_desc, 0, sizeof(view_desc)); - WGPUTexture wgpu_tex = ds_img->wgpu.tex; - SOKOL_ASSERT(wgpu_tex); - pass->wgpu.ds_att.render_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); - SOKOL_ASSERT(pass->wgpu.ds_att.render_tex_view); - } - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_wgpu_destroy_pass(_sg_pass_t* pass) { - SOKOL_ASSERT(pass); - for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { - if (pass->wgpu.color_atts[i].render_tex_view) { - wgpuTextureViewRelease(pass->wgpu.color_atts[i].render_tex_view); - pass->wgpu.color_atts[i].render_tex_view = 0; - } - if (pass->wgpu.color_atts[i].resolve_tex_view) { - wgpuTextureViewRelease(pass->wgpu.color_atts[i].resolve_tex_view); - pass->wgpu.color_atts[i].resolve_tex_view = 0; - } - } - if (pass->wgpu.ds_att.render_tex_view) { - wgpuTextureViewRelease(pass->wgpu.ds_att.render_tex_view); - pass->wgpu.ds_att.render_tex_view = 0; - } -} - -_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_pass_color_image(const _sg_pass_t* pass, int index) { - SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); - /* NOTE: may return null */ - return pass->wgpu.color_atts[index].image; -} - -_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_pass_ds_image(const _sg_pass_t* pass) { - /* NOTE: may return null */ - SOKOL_ASSERT(pass); - return pass->wgpu.ds_att.image; -} - -_SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { - SOKOL_ASSERT(action); - SOKOL_ASSERT(!_sg.wgpu.in_pass); - SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); - SOKOL_ASSERT(_sg.wgpu.dev); - SOKOL_ASSERT(_sg.wgpu.render_view_cb || _sg.wgpu.render_view_userdata_cb); - SOKOL_ASSERT(_sg.wgpu.resolve_view_cb || _sg.wgpu.resolve_view_userdata_cb); - SOKOL_ASSERT(_sg.wgpu.depth_stencil_view_cb || _sg.wgpu.depth_stencil_view_userdata_cb); - _sg.wgpu.in_pass = true; - _sg.wgpu.cur_width = w; - _sg.wgpu.cur_height = h; - _sg.wgpu.cur_pipeline = 0; - _sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; - - SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); - if (pass) { - WGPURenderPassDescriptor wgpu_pass_desc; - memset(&wgpu_pass_desc, 0, sizeof(wgpu_pass_desc)); - WGPURenderPassColorAttachmentDescriptor wgpu_color_att_desc[SG_MAX_COLOR_ATTACHMENTS]; - memset(&wgpu_color_att_desc, 0, sizeof(wgpu_color_att_desc)); - SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_VALID); - for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { - const _sg_wgpu_attachment_t* wgpu_att = &pass->wgpu.color_atts[i]; - wgpu_color_att_desc[i].loadOp = _sg_wgpu_load_op(action->colors[i].action); - wgpu_color_att_desc[i].storeOp = WGPUStoreOp_Store; - wgpu_color_att_desc[i].clearColor.r = action->colors[i].value.r; - wgpu_color_att_desc[i].clearColor.g = action->colors[i].value.g; - wgpu_color_att_desc[i].clearColor.b = action->colors[i].value.b; - wgpu_color_att_desc[i].clearColor.a = action->colors[i].value.a; - wgpu_color_att_desc[i].attachment = wgpu_att->render_tex_view; - if (wgpu_att->image->cmn.sample_count > 1) { - wgpu_color_att_desc[i].resolveTarget = wgpu_att->resolve_tex_view; - } - } - wgpu_pass_desc.colorAttachmentCount = pass->cmn.num_color_atts; - wgpu_pass_desc.colorAttachments = &wgpu_color_att_desc[0]; - if (pass->wgpu.ds_att.image) { - WGPURenderPassDepthStencilAttachmentDescriptor wgpu_ds_att_desc; - memset(&wgpu_ds_att_desc, 0, sizeof(wgpu_ds_att_desc)); - wgpu_ds_att_desc.depthLoadOp = _sg_wgpu_load_op(action->depth.action); - wgpu_ds_att_desc.clearDepth = action->depth.value; - wgpu_ds_att_desc.stencilLoadOp = _sg_wgpu_load_op(action->stencil.action); - wgpu_ds_att_desc.clearStencil = action->stencil.value; - wgpu_ds_att_desc.attachment = pass->wgpu.ds_att.render_tex_view; - wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att_desc; - _sg.wgpu.pass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.render_cmd_enc, &wgpu_pass_desc); - } - } - else { - /* default render pass */ - WGPUTextureView wgpu_render_view = _sg.wgpu.render_view_cb ? _sg.wgpu.render_view_cb() : _sg.wgpu.render_view_userdata_cb(_sg.wgpu.user_data); - WGPUTextureView wgpu_resolve_view = _sg.wgpu.resolve_view_cb ? _sg.wgpu.resolve_view_cb() : _sg.wgpu.resolve_view_userdata_cb(_sg.wgpu.user_data); - WGPUTextureView wgpu_depth_stencil_view = _sg.wgpu.depth_stencil_view_cb ? _sg.wgpu.depth_stencil_view_cb() : _sg.wgpu.depth_stencil_view_userdata_cb(_sg.wgpu.user_data); - - WGPURenderPassDescriptor pass_desc; - memset(&pass_desc, 0, sizeof(pass_desc)); - WGPURenderPassColorAttachmentDescriptor color_att_desc; - memset(&color_att_desc, 0, sizeof(color_att_desc)); - color_att_desc.loadOp = _sg_wgpu_load_op(action->colors[0].action); - color_att_desc.clearColor.r = action->colors[0].value.r; - color_att_desc.clearColor.g = action->colors[0].value.g; - color_att_desc.clearColor.b = action->colors[0].value.b; - color_att_desc.clearColor.a = action->colors[0].value.a; - color_att_desc.attachment = wgpu_render_view; - color_att_desc.resolveTarget = wgpu_resolve_view; /* null if no MSAA rendering */ - pass_desc.colorAttachmentCount = 1; - pass_desc.colorAttachments = &color_att_desc; - WGPURenderPassDepthStencilAttachmentDescriptor ds_att_desc; - memset(&ds_att_desc, 0, sizeof(ds_att_desc)); - ds_att_desc.attachment = wgpu_depth_stencil_view; - SOKOL_ASSERT(0 != ds_att_desc.attachment); - ds_att_desc.depthLoadOp = _sg_wgpu_load_op(action->depth.action); - ds_att_desc.clearDepth = action->depth.value; - ds_att_desc.stencilLoadOp = _sg_wgpu_load_op(action->stencil.action); - ds_att_desc.clearStencil = action->stencil.value; - pass_desc.depthStencilAttachment = &ds_att_desc; - _sg.wgpu.pass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.render_cmd_enc, &pass_desc); - } - SOKOL_ASSERT(_sg.wgpu.pass_enc); - - /* initial uniform buffer binding (required even if no uniforms are set in the frame) */ - wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, - 0, /* groupIndex 0 is reserved for uniform buffers */ - _sg.wgpu.ub.bindgroup, - SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, - &_sg.wgpu.ub.bind_offsets[0][0]); -} - -_SOKOL_PRIVATE void _sg_wgpu_end_pass(void) { - SOKOL_ASSERT(_sg.wgpu.in_pass); - SOKOL_ASSERT(_sg.wgpu.pass_enc); - _sg.wgpu.in_pass = false; - wgpuRenderPassEncoderEndPass(_sg.wgpu.pass_enc); - wgpuRenderPassEncoderRelease(_sg.wgpu.pass_enc); - _sg.wgpu.pass_enc = 0; -} - -_SOKOL_PRIVATE void _sg_wgpu_commit(void) { - SOKOL_ASSERT(!_sg.wgpu.in_pass); - SOKOL_ASSERT(_sg.wgpu.queue); - SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); - SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); - - /* finish and submit this frame's work */ - _sg_wgpu_ubpool_flush(); - _sg_wgpu_staging_unmap(); - - WGPUCommandBuffer cmd_bufs[2]; - - WGPUCommandBufferDescriptor cmd_buf_desc; - memset(&cmd_buf_desc, 0, sizeof(cmd_buf_desc)); - cmd_bufs[0] = wgpuCommandEncoderFinish(_sg.wgpu.staging_cmd_enc, &cmd_buf_desc); - SOKOL_ASSERT(cmd_bufs[0]); - wgpuCommandEncoderRelease(_sg.wgpu.staging_cmd_enc); - _sg.wgpu.staging_cmd_enc = 0; - - cmd_bufs[1] = wgpuCommandEncoderFinish(_sg.wgpu.render_cmd_enc, &cmd_buf_desc); - SOKOL_ASSERT(cmd_bufs[1]); - wgpuCommandEncoderRelease(_sg.wgpu.render_cmd_enc); - _sg.wgpu.render_cmd_enc = 0; - - wgpuQueueSubmit(_sg.wgpu.queue, 2, &cmd_bufs[0]); - - wgpuCommandBufferRelease(cmd_bufs[0]); - wgpuCommandBufferRelease(cmd_bufs[1]); - - /* create a new render- and staging-command-encoders for next frame */ - WGPUCommandEncoderDescriptor cmd_enc_desc; - memset(&cmd_enc_desc, 0, sizeof(cmd_enc_desc)); - _sg.wgpu.staging_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); - _sg.wgpu.render_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); - - /* grab new staging buffers for uniform- and vertex/image-updates */ - _sg_wgpu_ubpool_next_frame(false); - _sg_wgpu_staging_next_frame(false); -} - -_SOKOL_PRIVATE void _sg_wgpu_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { - SOKOL_ASSERT(_sg.wgpu.in_pass); - SOKOL_ASSERT(_sg.wgpu.pass_enc); - float xf = (float) x; - float yf = (float) (origin_top_left ? y : (_sg.wgpu.cur_height - (y + h))); - float wf = (float) w; - float hf = (float) h; - wgpuRenderPassEncoderSetViewport(_sg.wgpu.pass_enc, xf, yf, wf, hf, 0.0f, 1.0f); -} - -_SOKOL_PRIVATE void _sg_wgpu_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { - SOKOL_ASSERT(_sg.wgpu.in_pass); - SOKOL_ASSERT(_sg.wgpu.pass_enc); - SOKOL_ASSERT(_sg.wgpu.in_pass); - SOKOL_ASSERT(_sg.wgpu.pass_enc); - - /* clip against framebuffer rect */ - x = _sg_min(_sg_max(0, x), _sg.wgpu.cur_width-1); - y = _sg_min(_sg_max(0, y), _sg.wgpu.cur_height-1); - if ((x + w) > _sg.wgpu.cur_width) { - w = _sg.wgpu.cur_width - x; - } - if ((y + h) > _sg.wgpu.cur_height) { - h = _sg.wgpu.cur_height - y; - } - w = _sg_max(w, 1); - h = _sg_max(h, 1); - - uint32_t sx = (uint32_t) x; - uint32_t sy = origin_top_left ? y : (_sg.wgpu.cur_height - (y + h)); - uint32_t sw = w; - uint32_t sh = h; - wgpuRenderPassEncoderSetScissorRect(_sg.wgpu.pass_enc, sx, sy, sw, sh); -} - -_SOKOL_PRIVATE void _sg_wgpu_apply_pipeline(_sg_pipeline_t* pip) { - SOKOL_ASSERT(pip); - SOKOL_ASSERT(pip->wgpu.pip); - SOKOL_ASSERT(_sg.wgpu.in_pass); - SOKOL_ASSERT(_sg.wgpu.pass_enc); - _sg.wgpu.draw_indexed = (pip->cmn.index_type != SG_INDEXTYPE_NONE); - _sg.wgpu.cur_pipeline = pip; - _sg.wgpu.cur_pipeline_id.id = pip->slot.id; - wgpuRenderPassEncoderSetPipeline(_sg.wgpu.pass_enc, pip->wgpu.pip); - wgpuRenderPassEncoderSetBlendColor(_sg.wgpu.pass_enc, (WGPUColor*)&pip->cmn.blend_color); - wgpuRenderPassEncoderSetStencilReference(_sg.wgpu.pass_enc, pip->wgpu.stencil_ref); -} - -_SOKOL_PRIVATE WGPUBindGroup _sg_wgpu_create_images_bindgroup(WGPUBindGroupLayout bgl, _sg_image_t** imgs, int num_imgs) { - SOKOL_ASSERT(_sg.wgpu.dev); - SOKOL_ASSERT(num_imgs <= _SG_WGPU_MAX_SHADERSTAGE_IMAGES); - WGPUBindGroupBinding img_bgb[_SG_WGPU_MAX_SHADERSTAGE_IMAGES * 2]; - memset(&img_bgb, 0, sizeof(img_bgb)); - for (int img_index = 0; img_index < num_imgs; img_index++) { - WGPUBindGroupBinding* tex_bdg = &img_bgb[img_index*2 + 0]; - WGPUBindGroupBinding* smp_bdg = &img_bgb[img_index*2 + 1]; - tex_bdg->binding = img_index; - tex_bdg->textureView = imgs[img_index]->wgpu.tex_view; - smp_bdg->binding = img_index + _SG_WGPU_MAX_SHADERSTAGE_IMAGES; - smp_bdg->sampler = imgs[img_index]->wgpu.sampler; - } - WGPUBindGroupDescriptor bg_desc; - memset(&bg_desc, 0, sizeof(bg_desc)); - bg_desc.layout = bgl; - bg_desc.bindingCount = 2 * num_imgs; - bg_desc.bindings = &img_bgb[0]; - WGPUBindGroup bg = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); - SOKOL_ASSERT(bg); - return bg; -} - -_SOKOL_PRIVATE void _sg_wgpu_apply_bindings( - _sg_pipeline_t* pip, - _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, - _sg_buffer_t* ib, int ib_offset, - _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs) -{ - SOKOL_ASSERT(_sg.wgpu.in_pass); - SOKOL_ASSERT(_sg.wgpu.pass_enc); - SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); - - /* index buffer */ - if (ib) { - wgpuRenderPassEncoderSetIndexBuffer(_sg.wgpu.pass_enc, ib->wgpu.buf, ib_offset); - } - - /* vertex buffers */ - for (uint32_t slot = 0; slot < (uint32_t)num_vbs; slot++) { - wgpuRenderPassEncoderSetVertexBuffer(_sg.wgpu.pass_enc, slot, vbs[slot]->wgpu.buf, (uint64_t)vb_offsets[slot]); - } - - /* need to create throw-away bind groups for images */ - if (num_vs_imgs > 0) { - if (num_vs_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { - num_vs_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; - } - WGPUBindGroupLayout vs_bgl = pip->shader->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout; - SOKOL_ASSERT(vs_bgl); - WGPUBindGroup vs_img_bg = _sg_wgpu_create_images_bindgroup(vs_bgl, vs_imgs, num_vs_imgs); - wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 1, vs_img_bg, 0, 0); - wgpuBindGroupRelease(vs_img_bg); - } - else { - wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 1, _sg.wgpu.empty_bind_group, 0, 0); - } - if (num_fs_imgs > 0) { - if (num_fs_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { - num_fs_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; - } - WGPUBindGroupLayout fs_bgl = pip->shader->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout; - SOKOL_ASSERT(fs_bgl); - WGPUBindGroup fs_img_bg = _sg_wgpu_create_images_bindgroup(fs_bgl, fs_imgs, num_fs_imgs); - wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 2, fs_img_bg, 0, 0); - wgpuBindGroupRelease(fs_img_bg); - } - else { - wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 2, _sg.wgpu.empty_bind_group, 0, 0); - } -} - -_SOKOL_PRIVATE void _sg_wgpu_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { - SOKOL_ASSERT(_sg.wgpu.in_pass); - SOKOL_ASSERT(_sg.wgpu.pass_enc); - SOKOL_ASSERT((_sg.wgpu.ub.offset + data->size) <= _sg.wgpu.ub.num_bytes); - SOKOL_ASSERT((_sg.wgpu.ub.offset & (_SG_WGPU_STAGING_ALIGN-1)) == 0); - SOKOL_ASSERT(_sg.wgpu.cur_pipeline && _sg.wgpu.cur_pipeline->shader); - SOKOL_ASSERT(_sg.wgpu.cur_pipeline->slot.id == _sg.wgpu.cur_pipeline_id.id); - SOKOL_ASSERT(_sg.wgpu.cur_pipeline->shader->slot.id == _sg.wgpu.cur_pipeline->cmn.shader_id.id); - SOKOL_ASSERT(ub_index < _sg.wgpu.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); - SOKOL_ASSERT(data->size <= _sg.wgpu.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); - SOKOL_ASSERT(data->size <= _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE); - SOKOL_ASSERT(0 != _sg.wgpu.ub.stage.ptr[_sg.wgpu.ub.stage.cur]); - - uint8_t* dst_ptr = _sg.wgpu.ub.stage.ptr[_sg.wgpu.ub.stage.cur] + _sg.wgpu.ub.offset; - memcpy(dst_ptr, data->ptr, data->size); - _sg.wgpu.ub.bind_offsets[stage_index][ub_index] = _sg.wgpu.ub.offset; - wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, - 0, /* groupIndex 0 is reserved for uniform buffers */ - _sg.wgpu.ub.bindgroup, - SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, - &_sg.wgpu.ub.bind_offsets[0][0]); - _sg.wgpu.ub.offset = _sg_roundup(_sg.wgpu.ub.offset + data->size, _SG_WGPU_STAGING_ALIGN); -} - -_SOKOL_PRIVATE void _sg_wgpu_draw(int base_element, int num_elements, int num_instances) { - SOKOL_ASSERT(_sg.wgpu.in_pass); - SOKOL_ASSERT(_sg.wgpu.pass_enc); - if (_sg.wgpu.draw_indexed) { - wgpuRenderPassEncoderDrawIndexed(_sg.wgpu.pass_enc, num_elements, num_instances, base_element, 0, 0); - } - else { - wgpuRenderPassEncoderDraw(_sg.wgpu.pass_enc, num_elements, num_instances, base_element, 0); - } -} - -_SOKOL_PRIVATE void _sg_wgpu_update_buffer(_sg_buffer_t* buf, const sg_range* data) { - SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); - uint32_t copied_num_bytes = _sg_wgpu_staging_copy_to_buffer(buf->wgpu.buf, 0, data->ptr, data->size); - SOKOL_ASSERT(copied_num_bytes > 0); _SOKOL_UNUSED(copied_num_bytes); -} - -_SOKOL_PRIVATE int _sg_wgpu_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { - SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); - _SOKOL_UNUSED(new_frame); - uint32_t copied_num_bytes = _sg_wgpu_staging_copy_to_buffer(buf->wgpu.buf, buf->cmn.append_pos, data->ptr, data->size); - SOKOL_ASSERT(copied_num_bytes > 0); _SOKOL_UNUSED(copied_num_bytes); - return (int)copied_num_bytes; -} - -_SOKOL_PRIVATE void _sg_wgpu_update_image(_sg_image_t* img, const sg_image_data* data) { - SOKOL_ASSERT(img && data); - bool success = _sg_wgpu_staging_copy_to_texture(img, data); - SOKOL_ASSERT(success); - _SOKOL_UNUSED(success); -} -#endif - -/*== BACKEND API WRAPPERS ====================================================*/ -static inline void _sg_setup_backend(const sg_desc* desc) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_setup_backend(desc); - #elif defined(SOKOL_METAL) - _sg_mtl_setup_backend(desc); - #elif defined(SOKOL_D3D11) - _sg_d3d11_setup_backend(desc); - #elif defined(SOKOL_WGPU) - _sg_wgpu_setup_backend(desc); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_setup_backend(desc); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_discard_backend(void) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_discard_backend(); - #elif defined(SOKOL_METAL) - _sg_mtl_discard_backend(); - #elif defined(SOKOL_D3D11) - _sg_d3d11_discard_backend(); - #elif defined(SOKOL_WGPU) - _sg_wgpu_discard_backend(); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_discard_backend(); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_reset_state_cache(void) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_reset_state_cache(); - #elif defined(SOKOL_METAL) - _sg_mtl_reset_state_cache(); - #elif defined(SOKOL_D3D11) - _sg_d3d11_reset_state_cache(); - #elif defined(SOKOL_WGPU) - _sg_wgpu_reset_state_cache(); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_reset_state_cache(); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_activate_context(_sg_context_t* ctx) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_activate_context(ctx); - #elif defined(SOKOL_METAL) - _sg_mtl_activate_context(ctx); - #elif defined(SOKOL_D3D11) - _sg_d3d11_activate_context(ctx); - #elif defined(SOKOL_WGPU) - _sg_wgpu_activate_context(ctx); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_activate_context(ctx); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline sg_resource_state _sg_create_context(_sg_context_t* ctx) { - #if defined(_SOKOL_ANY_GL) - return _sg_gl_create_context(ctx); - #elif defined(SOKOL_METAL) - return _sg_mtl_create_context(ctx); - #elif defined(SOKOL_D3D11) - return _sg_d3d11_create_context(ctx); - #elif defined(SOKOL_WGPU) - return _sg_wgpu_create_context(ctx); - #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_create_context(ctx); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_destroy_context(_sg_context_t* ctx) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_destroy_context(ctx); - #elif defined(SOKOL_METAL) - _sg_mtl_destroy_context(ctx); - #elif defined(SOKOL_D3D11) - _sg_d3d11_destroy_context(ctx); - #elif defined(SOKOL_WGPU) - _sg_wgpu_destroy_context(ctx); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_destroy_context(ctx); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { - #if defined(_SOKOL_ANY_GL) - return _sg_gl_create_buffer(buf, desc); - #elif defined(SOKOL_METAL) - return _sg_mtl_create_buffer(buf, desc); - #elif defined(SOKOL_D3D11) - return _sg_d3d11_create_buffer(buf, desc); - #elif defined(SOKOL_WGPU) - return _sg_wgpu_create_buffer(buf, desc); - #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_create_buffer(buf, desc); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_destroy_buffer(_sg_buffer_t* buf) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_destroy_buffer(buf); - #elif defined(SOKOL_METAL) - _sg_mtl_destroy_buffer(buf); - #elif defined(SOKOL_D3D11) - _sg_d3d11_destroy_buffer(buf); - #elif defined(SOKOL_WGPU) - _sg_wgpu_destroy_buffer(buf); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_destroy_buffer(buf); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { - #if defined(_SOKOL_ANY_GL) - return _sg_gl_create_image(img, desc); - #elif defined(SOKOL_METAL) - return _sg_mtl_create_image(img, desc); - #elif defined(SOKOL_D3D11) - return _sg_d3d11_create_image(img, desc); - #elif defined(SOKOL_WGPU) - return _sg_wgpu_create_image(img, desc); - #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_create_image(img, desc); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_destroy_image(_sg_image_t* img) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_destroy_image(img); - #elif defined(SOKOL_METAL) - _sg_mtl_destroy_image(img); - #elif defined(SOKOL_D3D11) - _sg_d3d11_destroy_image(img); - #elif defined(SOKOL_WGPU) - _sg_wgpu_destroy_image(img); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_destroy_image(img); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { - #if defined(_SOKOL_ANY_GL) - return _sg_gl_create_shader(shd, desc); - #elif defined(SOKOL_METAL) - return _sg_mtl_create_shader(shd, desc); - #elif defined(SOKOL_D3D11) - return _sg_d3d11_create_shader(shd, desc); - #elif defined(SOKOL_WGPU) - return _sg_wgpu_create_shader(shd, desc); - #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_create_shader(shd, desc); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_destroy_shader(_sg_shader_t* shd) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_destroy_shader(shd); - #elif defined(SOKOL_METAL) - _sg_mtl_destroy_shader(shd); - #elif defined(SOKOL_D3D11) - _sg_d3d11_destroy_shader(shd); - #elif defined(SOKOL_WGPU) - _sg_wgpu_destroy_shader(shd); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_destroy_shader(shd); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { - #if defined(_SOKOL_ANY_GL) - return _sg_gl_create_pipeline(pip, shd, desc); - #elif defined(SOKOL_METAL) - return _sg_mtl_create_pipeline(pip, shd, desc); - #elif defined(SOKOL_D3D11) - return _sg_d3d11_create_pipeline(pip, shd, desc); - #elif defined(SOKOL_WGPU) - return _sg_wgpu_create_pipeline(pip, shd, desc); - #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_create_pipeline(pip, shd, desc); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_destroy_pipeline(_sg_pipeline_t* pip) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_destroy_pipeline(pip); - #elif defined(SOKOL_METAL) - _sg_mtl_destroy_pipeline(pip); - #elif defined(SOKOL_D3D11) - _sg_d3d11_destroy_pipeline(pip); - #elif defined(SOKOL_WGPU) - _sg_wgpu_destroy_pipeline(pip); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_destroy_pipeline(pip); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { - #if defined(_SOKOL_ANY_GL) - return _sg_gl_create_pass(pass, att_images, desc); - #elif defined(SOKOL_METAL) - return _sg_mtl_create_pass(pass, att_images, desc); - #elif defined(SOKOL_D3D11) - return _sg_d3d11_create_pass(pass, att_images, desc); - #elif defined(SOKOL_WGPU) - return _sg_wgpu_create_pass(pass, att_images, desc); - #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_create_pass(pass, att_images, desc); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_destroy_pass(_sg_pass_t* pass) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_destroy_pass(pass); - #elif defined(SOKOL_METAL) - _sg_mtl_destroy_pass(pass); - #elif defined(SOKOL_D3D11) - _sg_d3d11_destroy_pass(pass); - #elif defined(SOKOL_WGPU) - return _sg_wgpu_destroy_pass(pass); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_destroy_pass(pass); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline _sg_image_t* _sg_pass_color_image(const _sg_pass_t* pass, int index) { - #if defined(_SOKOL_ANY_GL) - return _sg_gl_pass_color_image(pass, index); - #elif defined(SOKOL_METAL) - return _sg_mtl_pass_color_image(pass, index); - #elif defined(SOKOL_D3D11) - return _sg_d3d11_pass_color_image(pass, index); - #elif defined(SOKOL_WGPU) - return _sg_wgpu_pass_color_image(pass, index); - #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_pass_color_image(pass, index); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline _sg_image_t* _sg_pass_ds_image(const _sg_pass_t* pass) { - #if defined(_SOKOL_ANY_GL) - return _sg_gl_pass_ds_image(pass); - #elif defined(SOKOL_METAL) - return _sg_mtl_pass_ds_image(pass); - #elif defined(SOKOL_D3D11) - return _sg_d3d11_pass_ds_image(pass); - #elif defined(SOKOL_WGPU) - return _sg_wgpu_pass_ds_image(pass); - #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_pass_ds_image(pass); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_begin_pass(pass, action, w, h); - #elif defined(SOKOL_METAL) - _sg_mtl_begin_pass(pass, action, w, h); - #elif defined(SOKOL_D3D11) - _sg_d3d11_begin_pass(pass, action, w, h); - #elif defined(SOKOL_WGPU) - _sg_wgpu_begin_pass(pass, action, w, h); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_begin_pass(pass, action, w, h); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_end_pass(void) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_end_pass(); - #elif defined(SOKOL_METAL) - _sg_mtl_end_pass(); - #elif defined(SOKOL_D3D11) - _sg_d3d11_end_pass(); - #elif defined(SOKOL_WGPU) - _sg_wgpu_end_pass(); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_end_pass(); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_apply_viewport(x, y, w, h, origin_top_left); - #elif defined(SOKOL_METAL) - _sg_mtl_apply_viewport(x, y, w, h, origin_top_left); - #elif defined(SOKOL_D3D11) - _sg_d3d11_apply_viewport(x, y, w, h, origin_top_left); - #elif defined(SOKOL_WGPU) - _sg_wgpu_apply_viewport(x, y, w, h, origin_top_left); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_apply_viewport(x, y, w, h, origin_top_left); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_apply_scissor_rect(x, y, w, h, origin_top_left); - #elif defined(SOKOL_METAL) - _sg_mtl_apply_scissor_rect(x, y, w, h, origin_top_left); - #elif defined(SOKOL_D3D11) - _sg_d3d11_apply_scissor_rect(x, y, w, h, origin_top_left); - #elif defined(SOKOL_WGPU) - _sg_wgpu_apply_scissor_rect(x, y, w, h, origin_top_left); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_apply_scissor_rect(x, y, w, h, origin_top_left); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_apply_pipeline(_sg_pipeline_t* pip) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_apply_pipeline(pip); - #elif defined(SOKOL_METAL) - _sg_mtl_apply_pipeline(pip); - #elif defined(SOKOL_D3D11) - _sg_d3d11_apply_pipeline(pip); - #elif defined(SOKOL_WGPU) - _sg_wgpu_apply_pipeline(pip); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_apply_pipeline(pip); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_apply_bindings( - _sg_pipeline_t* pip, - _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, - _sg_buffer_t* ib, int ib_offset, - _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs) -{ - #if defined(_SOKOL_ANY_GL) - _sg_gl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); - #elif defined(SOKOL_METAL) - _sg_mtl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); - #elif defined(SOKOL_D3D11) - _sg_d3d11_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); - #elif defined(SOKOL_WGPU) - _sg_wgpu_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_apply_uniforms(stage_index, ub_index, data); - #elif defined(SOKOL_METAL) - _sg_mtl_apply_uniforms(stage_index, ub_index, data); - #elif defined(SOKOL_D3D11) - _sg_d3d11_apply_uniforms(stage_index, ub_index, data); - #elif defined(SOKOL_WGPU) - _sg_wgpu_apply_uniforms(stage_index, ub_index, data); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_apply_uniforms(stage_index, ub_index, data); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_draw(int base_element, int num_elements, int num_instances) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_draw(base_element, num_elements, num_instances); - #elif defined(SOKOL_METAL) - _sg_mtl_draw(base_element, num_elements, num_instances); - #elif defined(SOKOL_D3D11) - _sg_d3d11_draw(base_element, num_elements, num_instances); - #elif defined(SOKOL_WGPU) - _sg_wgpu_draw(base_element, num_elements, num_instances); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_draw(base_element, num_elements, num_instances); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_commit(void) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_commit(); - #elif defined(SOKOL_METAL) - _sg_mtl_commit(); - #elif defined(SOKOL_D3D11) - _sg_d3d11_commit(); - #elif defined(SOKOL_WGPU) - _sg_wgpu_commit(); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_commit(); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_update_buffer(_sg_buffer_t* buf, const sg_range* data) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_update_buffer(buf, data); - #elif defined(SOKOL_METAL) - _sg_mtl_update_buffer(buf, data); - #elif defined(SOKOL_D3D11) - _sg_d3d11_update_buffer(buf, data); - #elif defined(SOKOL_WGPU) - _sg_wgpu_update_buffer(buf, data); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_update_buffer(buf, data); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline int _sg_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { - #if defined(_SOKOL_ANY_GL) - return _sg_gl_append_buffer(buf, data, new_frame); - #elif defined(SOKOL_METAL) - return _sg_mtl_append_buffer(buf, data, new_frame); - #elif defined(SOKOL_D3D11) - return _sg_d3d11_append_buffer(buf, data, new_frame); - #elif defined(SOKOL_WGPU) - return _sg_wgpu_append_buffer(buf, data, new_frame); - #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_append_buffer(buf, data, new_frame); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_update_image(_sg_image_t* img, const sg_image_data* data) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_update_image(img, data); - #elif defined(SOKOL_METAL) - _sg_mtl_update_image(img, data); - #elif defined(SOKOL_D3D11) - _sg_d3d11_update_image(img, data); - #elif defined(SOKOL_WGPU) - _sg_wgpu_update_image(img, data); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_update_image(img, data); - #else - #error("INVALID BACKEND"); - #endif -} - -/*== RESOURCE POOLS ==========================================================*/ - -_SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num) { - SOKOL_ASSERT(pool && (num >= 1)); - /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */ - pool->size = num + 1; - pool->queue_top = 0; - /* generation counters indexable by pool slot index, slot 0 is reserved */ - size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; - pool->gen_ctrs = (uint32_t*) SOKOL_MALLOC(gen_ctrs_size); - SOKOL_ASSERT(pool->gen_ctrs); - memset(pool->gen_ctrs, 0, gen_ctrs_size); - /* it's not a bug to only reserve 'num' here */ - pool->free_queue = (int*) SOKOL_MALLOC(sizeof(int) * (size_t)num); - SOKOL_ASSERT(pool->free_queue); - /* never allocate the zero-th pool item since the invalid id is 0 */ - for (int i = pool->size-1; i >= 1; i--) { - pool->free_queue[pool->queue_top++] = i; - } -} - -_SOKOL_PRIVATE void _sg_discard_pool(_sg_pool_t* pool) { - SOKOL_ASSERT(pool); - SOKOL_ASSERT(pool->free_queue); - SOKOL_FREE(pool->free_queue); - pool->free_queue = 0; - SOKOL_ASSERT(pool->gen_ctrs); - SOKOL_FREE(pool->gen_ctrs); - pool->gen_ctrs = 0; - pool->size = 0; - pool->queue_top = 0; -} - -_SOKOL_PRIVATE int _sg_pool_alloc_index(_sg_pool_t* pool) { - SOKOL_ASSERT(pool); - SOKOL_ASSERT(pool->free_queue); - if (pool->queue_top > 0) { - int slot_index = pool->free_queue[--pool->queue_top]; - SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); - return slot_index; - } - else { - /* pool exhausted */ - return _SG_INVALID_SLOT_INDEX; - } -} - -_SOKOL_PRIVATE void _sg_pool_free_index(_sg_pool_t* pool, int slot_index) { - SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); - SOKOL_ASSERT(pool); - SOKOL_ASSERT(pool->free_queue); - SOKOL_ASSERT(pool->queue_top < pool->size); - #ifdef SOKOL_DEBUG - /* debug check against double-free */ - for (int i = 0; i < pool->queue_top; i++) { - SOKOL_ASSERT(pool->free_queue[i] != slot_index); - } - #endif - pool->free_queue[pool->queue_top++] = slot_index; - SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); -} - -_SOKOL_PRIVATE void _sg_reset_slot(_sg_slot_t* slot) { - SOKOL_ASSERT(slot); - memset(slot, 0, sizeof(_sg_slot_t)); -} - -_SOKOL_PRIVATE void _sg_reset_buffer(_sg_buffer_t* buf) { - SOKOL_ASSERT(buf); - _sg_slot_t slot = buf->slot; - memset(buf, 0, sizeof(_sg_buffer_t)); - buf->slot = slot; - buf->slot.state = SG_RESOURCESTATE_ALLOC; -} - -_SOKOL_PRIVATE void _sg_reset_image(_sg_image_t* img) { - SOKOL_ASSERT(img); - _sg_slot_t slot = img->slot; - memset(img, 0, sizeof(_sg_image_t)); - img->slot = slot; - img->slot.state = SG_RESOURCESTATE_ALLOC; -} - -_SOKOL_PRIVATE void _sg_reset_shader(_sg_shader_t* shd) { - SOKOL_ASSERT(shd); - _sg_slot_t slot = shd->slot; - memset(shd, 0, sizeof(_sg_shader_t)); - shd->slot = slot; - shd->slot.state = SG_RESOURCESTATE_ALLOC; -} - -_SOKOL_PRIVATE void _sg_reset_pipeline(_sg_pipeline_t* pip) { - SOKOL_ASSERT(pip); - _sg_slot_t slot = pip->slot; - memset(pip, 0, sizeof(_sg_pipeline_t)); - pip->slot = slot; - pip->slot.state = SG_RESOURCESTATE_ALLOC; -} - -_SOKOL_PRIVATE void _sg_reset_pass(_sg_pass_t* pass) { - SOKOL_ASSERT(pass); - _sg_slot_t slot = pass->slot; - memset(pass, 0, sizeof(_sg_pass_t)); - pass->slot = slot; - pass->slot.state = SG_RESOURCESTATE_ALLOC; -} - -_SOKOL_PRIVATE void _sg_reset_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _sg_slot_t slot = ctx->slot; - memset(ctx, 0, sizeof(_sg_context_t)); - ctx->slot = slot; - ctx->slot.state = SG_RESOURCESTATE_ALLOC; -} - -_SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) { - SOKOL_ASSERT(p); - SOKOL_ASSERT(desc); - /* note: the pools here will have an additional item, since slot 0 is reserved */ - SOKOL_ASSERT((desc->buffer_pool_size > 0) && (desc->buffer_pool_size < _SG_MAX_POOL_SIZE)); - _sg_init_pool(&p->buffer_pool, desc->buffer_pool_size); - size_t buffer_pool_byte_size = sizeof(_sg_buffer_t) * (size_t)p->buffer_pool.size; - p->buffers = (_sg_buffer_t*) SOKOL_MALLOC(buffer_pool_byte_size); - SOKOL_ASSERT(p->buffers); - memset(p->buffers, 0, buffer_pool_byte_size); - - SOKOL_ASSERT((desc->image_pool_size > 0) && (desc->image_pool_size < _SG_MAX_POOL_SIZE)); - _sg_init_pool(&p->image_pool, desc->image_pool_size); - size_t image_pool_byte_size = sizeof(_sg_image_t) * (size_t)p->image_pool.size; - p->images = (_sg_image_t*) SOKOL_MALLOC(image_pool_byte_size); - SOKOL_ASSERT(p->images); - memset(p->images, 0, image_pool_byte_size); - - SOKOL_ASSERT((desc->shader_pool_size > 0) && (desc->shader_pool_size < _SG_MAX_POOL_SIZE)); - _sg_init_pool(&p->shader_pool, desc->shader_pool_size); - size_t shader_pool_byte_size = sizeof(_sg_shader_t) * (size_t)p->shader_pool.size; - p->shaders = (_sg_shader_t*) SOKOL_MALLOC(shader_pool_byte_size); - SOKOL_ASSERT(p->shaders); - memset(p->shaders, 0, shader_pool_byte_size); - - SOKOL_ASSERT((desc->pipeline_pool_size > 0) && (desc->pipeline_pool_size < _SG_MAX_POOL_SIZE)); - _sg_init_pool(&p->pipeline_pool, desc->pipeline_pool_size); - size_t pipeline_pool_byte_size = sizeof(_sg_pipeline_t) * (size_t)p->pipeline_pool.size; - p->pipelines = (_sg_pipeline_t*) SOKOL_MALLOC(pipeline_pool_byte_size); - SOKOL_ASSERT(p->pipelines); - memset(p->pipelines, 0, pipeline_pool_byte_size); - - SOKOL_ASSERT((desc->pass_pool_size > 0) && (desc->pass_pool_size < _SG_MAX_POOL_SIZE)); - _sg_init_pool(&p->pass_pool, desc->pass_pool_size); - size_t pass_pool_byte_size = sizeof(_sg_pass_t) * (size_t)p->pass_pool.size; - p->passes = (_sg_pass_t*) SOKOL_MALLOC(pass_pool_byte_size); - SOKOL_ASSERT(p->passes); - memset(p->passes, 0, pass_pool_byte_size); - - SOKOL_ASSERT((desc->context_pool_size > 0) && (desc->context_pool_size < _SG_MAX_POOL_SIZE)); - _sg_init_pool(&p->context_pool, desc->context_pool_size); - size_t context_pool_byte_size = sizeof(_sg_context_t) * (size_t)p->context_pool.size; - p->contexts = (_sg_context_t*) SOKOL_MALLOC(context_pool_byte_size); - SOKOL_ASSERT(p->contexts); - memset(p->contexts, 0, context_pool_byte_size); -} - -_SOKOL_PRIVATE void _sg_discard_pools(_sg_pools_t* p) { - SOKOL_ASSERT(p); - SOKOL_FREE(p->contexts); p->contexts = 0; - SOKOL_FREE(p->passes); p->passes = 0; - SOKOL_FREE(p->pipelines); p->pipelines = 0; - SOKOL_FREE(p->shaders); p->shaders = 0; - SOKOL_FREE(p->images); p->images = 0; - SOKOL_FREE(p->buffers); p->buffers = 0; - _sg_discard_pool(&p->context_pool); - _sg_discard_pool(&p->pass_pool); - _sg_discard_pool(&p->pipeline_pool); - _sg_discard_pool(&p->shader_pool); - _sg_discard_pool(&p->image_pool); - _sg_discard_pool(&p->buffer_pool); -} - -/* allocate the slot at slot_index: - - bump the slot's generation counter - - create a resource id from the generation counter and slot index - - set the slot's id to this id - - set the slot's state to ALLOC - - return the resource id -*/ -_SOKOL_PRIVATE uint32_t _sg_slot_alloc(_sg_pool_t* pool, _sg_slot_t* slot, int slot_index) { - /* FIXME: add handling for an overflowing generation counter, - for now, just overflow (another option is to disable - the slot) - */ - SOKOL_ASSERT(pool && pool->gen_ctrs); - SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); - SOKOL_ASSERT((slot->state == SG_RESOURCESTATE_INITIAL) && (slot->id == SG_INVALID_ID)); - uint32_t ctr = ++pool->gen_ctrs[slot_index]; - slot->id = (ctr<<_SG_SLOT_SHIFT)|(slot_index & _SG_SLOT_MASK); - slot->state = SG_RESOURCESTATE_ALLOC; - return slot->id; -} - -/* extract slot index from id */ -_SOKOL_PRIVATE int _sg_slot_index(uint32_t id) { - int slot_index = (int) (id & _SG_SLOT_MASK); - SOKOL_ASSERT(_SG_INVALID_SLOT_INDEX != slot_index); - return slot_index; -} - -/* returns pointer to resource by id without matching id check */ -_SOKOL_PRIVATE _sg_buffer_t* _sg_buffer_at(const _sg_pools_t* p, uint32_t buf_id) { - SOKOL_ASSERT(p && (SG_INVALID_ID != buf_id)); - int slot_index = _sg_slot_index(buf_id); - SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->buffer_pool.size)); - return &p->buffers[slot_index]; -} - -_SOKOL_PRIVATE _sg_image_t* _sg_image_at(const _sg_pools_t* p, uint32_t img_id) { - SOKOL_ASSERT(p && (SG_INVALID_ID != img_id)); - int slot_index = _sg_slot_index(img_id); - SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->image_pool.size)); - return &p->images[slot_index]; -} - -_SOKOL_PRIVATE _sg_shader_t* _sg_shader_at(const _sg_pools_t* p, uint32_t shd_id) { - SOKOL_ASSERT(p && (SG_INVALID_ID != shd_id)); - int slot_index = _sg_slot_index(shd_id); - SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->shader_pool.size)); - return &p->shaders[slot_index]; -} - -_SOKOL_PRIVATE _sg_pipeline_t* _sg_pipeline_at(const _sg_pools_t* p, uint32_t pip_id) { - SOKOL_ASSERT(p && (SG_INVALID_ID != pip_id)); - int slot_index = _sg_slot_index(pip_id); - SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pipeline_pool.size)); - return &p->pipelines[slot_index]; -} - -_SOKOL_PRIVATE _sg_pass_t* _sg_pass_at(const _sg_pools_t* p, uint32_t pass_id) { - SOKOL_ASSERT(p && (SG_INVALID_ID != pass_id)); - int slot_index = _sg_slot_index(pass_id); - SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pass_pool.size)); - return &p->passes[slot_index]; -} - -_SOKOL_PRIVATE _sg_context_t* _sg_context_at(const _sg_pools_t* p, uint32_t context_id) { - SOKOL_ASSERT(p && (SG_INVALID_ID != context_id)); - int slot_index = _sg_slot_index(context_id); - SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->context_pool.size)); - return &p->contexts[slot_index]; -} - -/* returns pointer to resource with matching id check, may return 0 */ -_SOKOL_PRIVATE _sg_buffer_t* _sg_lookup_buffer(const _sg_pools_t* p, uint32_t buf_id) { - if (SG_INVALID_ID != buf_id) { - _sg_buffer_t* buf = _sg_buffer_at(p, buf_id); - if (buf->slot.id == buf_id) { - return buf; - } - } - return 0; -} - -_SOKOL_PRIVATE _sg_image_t* _sg_lookup_image(const _sg_pools_t* p, uint32_t img_id) { - if (SG_INVALID_ID != img_id) { - _sg_image_t* img = _sg_image_at(p, img_id); - if (img->slot.id == img_id) { - return img; - } - } - return 0; -} - -_SOKOL_PRIVATE _sg_shader_t* _sg_lookup_shader(const _sg_pools_t* p, uint32_t shd_id) { - SOKOL_ASSERT(p); - if (SG_INVALID_ID != shd_id) { - _sg_shader_t* shd = _sg_shader_at(p, shd_id); - if (shd->slot.id == shd_id) { - return shd; - } - } - return 0; -} - -_SOKOL_PRIVATE _sg_pipeline_t* _sg_lookup_pipeline(const _sg_pools_t* p, uint32_t pip_id) { - SOKOL_ASSERT(p); - if (SG_INVALID_ID != pip_id) { - _sg_pipeline_t* pip = _sg_pipeline_at(p, pip_id); - if (pip->slot.id == pip_id) { - return pip; - } - } - return 0; -} - -_SOKOL_PRIVATE _sg_pass_t* _sg_lookup_pass(const _sg_pools_t* p, uint32_t pass_id) { - SOKOL_ASSERT(p); - if (SG_INVALID_ID != pass_id) { - _sg_pass_t* pass = _sg_pass_at(p, pass_id); - if (pass->slot.id == pass_id) { - return pass; - } - } - return 0; -} - -_SOKOL_PRIVATE _sg_context_t* _sg_lookup_context(const _sg_pools_t* p, uint32_t ctx_id) { - SOKOL_ASSERT(p); - if (SG_INVALID_ID != ctx_id) { - _sg_context_t* ctx = _sg_context_at(p, ctx_id); - if (ctx->slot.id == ctx_id) { - return ctx; - } - } - return 0; -} - -_SOKOL_PRIVATE void _sg_destroy_all_resources(_sg_pools_t* p, uint32_t ctx_id) { - /* this is a bit dumb since it loops over all pool slots to - find the occupied slots, on the other hand it is only ever - executed at shutdown - NOTE: ONLY EXECUTE THIS AT SHUTDOWN - ...because the free queues will not be reset - and the resource slots not be cleared! - */ - for (int i = 1; i < p->buffer_pool.size; i++) { - if (p->buffers[i].slot.ctx_id == ctx_id) { - sg_resource_state state = p->buffers[i].slot.state; - if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_destroy_buffer(&p->buffers[i]); - } - } - } - for (int i = 1; i < p->image_pool.size; i++) { - if (p->images[i].slot.ctx_id == ctx_id) { - sg_resource_state state = p->images[i].slot.state; - if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_destroy_image(&p->images[i]); - } - } - } - for (int i = 1; i < p->shader_pool.size; i++) { - if (p->shaders[i].slot.ctx_id == ctx_id) { - sg_resource_state state = p->shaders[i].slot.state; - if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_destroy_shader(&p->shaders[i]); - } - } - } - for (int i = 1; i < p->pipeline_pool.size; i++) { - if (p->pipelines[i].slot.ctx_id == ctx_id) { - sg_resource_state state = p->pipelines[i].slot.state; - if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_destroy_pipeline(&p->pipelines[i]); - } - } - } - for (int i = 1; i < p->pass_pool.size; i++) { - if (p->passes[i].slot.ctx_id == ctx_id) { - sg_resource_state state = p->passes[i].slot.state; - if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_destroy_pass(&p->passes[i]); - } - } - } -} - -/*== VALIDATION LAYER ========================================================*/ -#if defined(SOKOL_DEBUG) -/* return a human readable string for an _sg_validate_error */ -_SOKOL_PRIVATE const char* _sg_validate_string(_sg_validate_error_t err) { - switch (err) { - /* buffer creation validation errors */ - case _SG_VALIDATE_BUFFERDESC_CANARY: return "sg_buffer_desc not initialized"; - case _SG_VALIDATE_BUFFERDESC_SIZE: return "sg_buffer_desc.size cannot be 0"; - case _SG_VALIDATE_BUFFERDESC_DATA: return "immutable buffers must be initialized with data (sg_buffer_desc.data.ptr and sg_buffer_desc.data.size)"; - case _SG_VALIDATE_BUFFERDESC_DATA_SIZE: return "immutable buffer data size differs from buffer size"; - case _SG_VALIDATE_BUFFERDESC_NO_DATA: return "dynamic/stream usage buffers cannot be initialized with data"; - - /* image data (in image creation and updating) */ - case _SG_VALIDATE_IMAGEDATA_NODATA: return "sg_image_data: no data (.ptr and/or .size is zero)"; - case _SG_VALIDATE_IMAGEDATA_DATA_SIZE: return "sg_image_data: data size doesn't match expected surface size"; - - /* image creation validation errros */ - case _SG_VALIDATE_IMAGEDESC_CANARY: return "sg_image_desc not initialized"; - case _SG_VALIDATE_IMAGEDESC_WIDTH: return "sg_image_desc.width must be > 0"; - case _SG_VALIDATE_IMAGEDESC_HEIGHT: return "sg_image_desc.height must be > 0"; - case _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT: return "invalid pixel format for render-target image"; - case _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT: return "invalid pixel format for non-render-target image"; - case _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT: return "non-render-target images cannot be multisampled"; - case _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT: return "MSAA not supported for this pixel format"; - case _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE: return "render target images must be SG_USAGE_IMMUTABLE"; - case _SG_VALIDATE_IMAGEDESC_RT_NO_DATA: return "render target images cannot be initialized with data"; - case _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA: return "images with injected textures cannot be initialized with data"; - case _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA: return "dynamic/stream images cannot be initialized with data"; - case _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE: return "compressed images must be immutable"; - - /* shader creation */ - case _SG_VALIDATE_SHADERDESC_CANARY: return "sg_shader_desc not initialized"; - case _SG_VALIDATE_SHADERDESC_SOURCE: return "shader source code required"; - case _SG_VALIDATE_SHADERDESC_BYTECODE: return "shader byte code required"; - case _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE: return "shader source or byte code required"; - case _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE: return "shader byte code length (in bytes) required"; - case _SG_VALIDATE_SHADERDESC_NO_CONT_UBS: return "shader uniform blocks must occupy continuous slots"; - case _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS: return "uniform block members must occupy continuous slots"; - case _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS: return "GL backend requires uniform block member declarations"; - case _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME: return "uniform block member name missing"; - case _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH: return "size of uniform block members doesn't match uniform block size"; - case _SG_VALIDATE_SHADERDESC_UB_ARRAY_COUNT: return "uniform array count must be >= 1"; - case _SG_VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE: return "uniform arrays only allowed for FLOAT4, INT4, MAT4 in std140 layout"; - - case _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS: return "shader images must occupy continuous slots"; - case _SG_VALIDATE_SHADERDESC_IMG_NAME: return "GL backend requires uniform block member names"; - case _SG_VALIDATE_SHADERDESC_ATTR_NAMES: return "GLES2 backend requires vertex attribute names"; - case _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS: return "D3D11 backend requires vertex attribute semantics"; - case _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG: return "vertex attribute name/semantic string too long (max len 16)"; - - /* pipeline creation */ - case _SG_VALIDATE_PIPELINEDESC_CANARY: return "sg_pipeline_desc not initialized"; - case _SG_VALIDATE_PIPELINEDESC_SHADER: return "sg_pipeline_desc.shader missing or invalid"; - case _SG_VALIDATE_PIPELINEDESC_NO_ATTRS: return "sg_pipeline_desc.layout.attrs is empty or not continuous"; - case _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4: return "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4"; - case _SG_VALIDATE_PIPELINEDESC_ATTR_NAME: return "GLES2/WebGL missing vertex attribute name in shader"; - case _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS: return "D3D11 missing vertex attribute semantics in shader"; - - /* pass creation */ - case _SG_VALIDATE_PASSDESC_CANARY: return "sg_pass_desc not initialized"; - case _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS: return "sg_pass_desc.color_attachments[0] must be valid"; - case _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS: return "color attachments must occupy continuous slots"; - case _SG_VALIDATE_PASSDESC_IMAGE: return "pass attachment image is not valid"; - case _SG_VALIDATE_PASSDESC_MIPLEVEL: return "pass attachment mip level is bigger than image has mipmaps"; - case _SG_VALIDATE_PASSDESC_FACE: return "pass attachment image is cubemap, but face index is too big"; - case _SG_VALIDATE_PASSDESC_LAYER: return "pass attachment image is array texture, but layer index is too big"; - case _SG_VALIDATE_PASSDESC_SLICE: return "pass attachment image is 3d texture, but slice value is too big"; - case _SG_VALIDATE_PASSDESC_IMAGE_NO_RT: return "pass attachment image must be render targets"; - case _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT: return "pass color-attachment images must have a renderable pixel format"; - case _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT: return "pass depth-attachment image must have depth pixel format"; - case _SG_VALIDATE_PASSDESC_IMAGE_SIZES: return "all pass attachments must have the same size"; - case _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS: return "all pass attachments must have the same sample count"; - - /* sg_begin_pass */ - case _SG_VALIDATE_BEGINPASS_PASS: return "sg_begin_pass: pass must be valid"; - case _SG_VALIDATE_BEGINPASS_IMAGE: return "sg_begin_pass: one or more attachment images are not valid"; - - /* sg_apply_pipeline */ - case _SG_VALIDATE_APIP_PIPELINE_VALID_ID: return "sg_apply_pipeline: invalid pipeline id provided"; - case _SG_VALIDATE_APIP_PIPELINE_EXISTS: return "sg_apply_pipeline: pipeline object no longer alive"; - case _SG_VALIDATE_APIP_PIPELINE_VALID: return "sg_apply_pipeline: pipeline object not in valid state"; - case _SG_VALIDATE_APIP_SHADER_EXISTS: return "sg_apply_pipeline: shader object no longer alive"; - case _SG_VALIDATE_APIP_SHADER_VALID: return "sg_apply_pipeline: shader object not in valid state"; - case _SG_VALIDATE_APIP_ATT_COUNT: return "sg_apply_pipeline: number of pipeline color attachments doesn't match number of pass color attachments"; - case _SG_VALIDATE_APIP_COLOR_FORMAT: return "sg_apply_pipeline: pipeline color attachment pixel format doesn't match pass color attachment pixel format"; - case _SG_VALIDATE_APIP_DEPTH_FORMAT: return "sg_apply_pipeline: pipeline depth pixel_format doesn't match pass depth attachment pixel format"; - case _SG_VALIDATE_APIP_SAMPLE_COUNT: return "sg_apply_pipeline: pipeline MSAA sample count doesn't match render pass attachment sample count"; - - /* sg_apply_bindings */ - case _SG_VALIDATE_ABND_PIPELINE: return "sg_apply_bindings: must be called after sg_apply_pipeline"; - case _SG_VALIDATE_ABND_PIPELINE_EXISTS: return "sg_apply_bindings: currently applied pipeline object no longer alive"; - case _SG_VALIDATE_ABND_PIPELINE_VALID: return "sg_apply_bindings: currently applied pipeline object not in valid state"; - case _SG_VALIDATE_ABND_VBS: return "sg_apply_bindings: number of vertex buffers doesn't match number of pipeline vertex layouts"; - case _SG_VALIDATE_ABND_VB_EXISTS: return "sg_apply_bindings: vertex buffer no longer alive"; - case _SG_VALIDATE_ABND_VB_TYPE: return "sg_apply_bindings: buffer in vertex buffer slot is not a SG_BUFFERTYPE_VERTEXBUFFER"; - case _SG_VALIDATE_ABND_VB_OVERFLOW: return "sg_apply_bindings: buffer in vertex buffer slot is overflown"; - case _SG_VALIDATE_ABND_NO_IB: return "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer provided"; - case _SG_VALIDATE_ABND_IB: return "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer provided"; - case _SG_VALIDATE_ABND_IB_EXISTS: return "sg_apply_bindings: index buffer no longer alive"; - case _SG_VALIDATE_ABND_IB_TYPE: return "sg_apply_bindings: buffer in index buffer slot is not a SG_BUFFERTYPE_INDEXBUFFER"; - case _SG_VALIDATE_ABND_IB_OVERFLOW: return "sg_apply_bindings: buffer in index buffer slot is overflown"; - case _SG_VALIDATE_ABND_VS_IMGS: return "sg_apply_bindings: vertex shader image count doesn't match sg_shader_desc"; - case _SG_VALIDATE_ABND_VS_IMG_EXISTS: return "sg_apply_bindings: vertex shader image no longer alive"; - case _SG_VALIDATE_ABND_VS_IMG_TYPES: return "sg_apply_bindings: one or more vertex shader image types don't match sg_shader_desc"; - case _SG_VALIDATE_ABND_FS_IMGS: return "sg_apply_bindings: fragment shader image count doesn't match sg_shader_desc"; - case _SG_VALIDATE_ABND_FS_IMG_EXISTS: return "sg_apply_bindings: fragment shader image no longer alive"; - case _SG_VALIDATE_ABND_FS_IMG_TYPES: return "sg_apply_bindings: one or more fragment shader image types don't match sg_shader_desc"; - - /* sg_apply_uniforms */ - case _SG_VALIDATE_AUB_NO_PIPELINE: return "sg_apply_uniforms: must be called after sg_apply_pipeline()"; - case _SG_VALIDATE_AUB_NO_UB_AT_SLOT: return "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot"; - case _SG_VALIDATE_AUB_SIZE: return "sg_apply_uniforms: data size exceeds declared uniform block size"; - - /* sg_update_buffer */ - case _SG_VALIDATE_UPDATEBUF_USAGE: return "sg_update_buffer: cannot update immutable buffer"; - case _SG_VALIDATE_UPDATEBUF_SIZE: return "sg_update_buffer: update size is bigger than buffer size"; - case _SG_VALIDATE_UPDATEBUF_ONCE: return "sg_update_buffer: only one update allowed per buffer and frame"; - case _SG_VALIDATE_UPDATEBUF_APPEND: return "sg_update_buffer: cannot call sg_update_buffer and sg_append_buffer in same frame"; - - /* sg_append_buffer */ - case _SG_VALIDATE_APPENDBUF_USAGE: return "sg_append_buffer: cannot append to immutable buffer"; - case _SG_VALIDATE_APPENDBUF_SIZE: return "sg_append_buffer: overall appended size is bigger than buffer size"; - case _SG_VALIDATE_APPENDBUF_UPDATE: return "sg_append_buffer: cannot call sg_append_buffer and sg_update_buffer in same frame"; - - /* sg_update_image */ - case _SG_VALIDATE_UPDIMG_USAGE: return "sg_update_image: cannot update immutable image"; - case _SG_VALIDATE_UPDIMG_ONCE: return "sg_update_image: only one update allowed per image and frame"; - - default: return "unknown validation error"; - } -} -#endif /* defined(SOKOL_DEBUG) */ - -/*-- validation checks -------------------------------------------------------*/ -#if defined(SOKOL_DEBUG) -_SOKOL_PRIVATE void _sg_validate_begin(void) { - _sg.validate_error = _SG_VALIDATE_SUCCESS; -} - -_SOKOL_PRIVATE void _sg_validate(bool cond, _sg_validate_error_t err) { - if (!cond) { - _sg.validate_error = err; - SOKOL_LOG(_sg_validate_string(err)); - } -} - -_SOKOL_PRIVATE bool _sg_validate_end(void) { - if (_sg.validate_error != _SG_VALIDATE_SUCCESS) { - #if !defined(SOKOL_VALIDATE_NON_FATAL) - SOKOL_LOG("^^^^ SOKOL-GFX VALIDATION FAILED, TERMINATING ^^^^"); - SOKOL_ASSERT(false); - #endif - return false; - } - else { - return true; - } -} -#endif - -_SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) { - #if !defined(SOKOL_DEBUG) - _SOKOL_UNUSED(desc); - return true; - #else - SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); - SOKOL_VALIDATE(desc->size > 0, _SG_VALIDATE_BUFFERDESC_SIZE); - bool injected = (0 != desc->gl_buffers[0]) || - (0 != desc->mtl_buffers[0]) || - (0 != desc->d3d11_buffer) || - (0 != desc->wgpu_buffer); - if (!injected && (desc->usage == SG_USAGE_IMMUTABLE)) { - SOKOL_VALIDATE((0 != desc->data.ptr) && (desc->data.size > 0), _SG_VALIDATE_BUFFERDESC_DATA); - SOKOL_VALIDATE(desc->size == desc->data.size, _SG_VALIDATE_BUFFERDESC_DATA_SIZE); - } - else { - SOKOL_VALIDATE(0 == desc->data.ptr, _SG_VALIDATE_BUFFERDESC_NO_DATA); - } - return SOKOL_VALIDATE_END(); - #endif -} - -_SOKOL_PRIVATE void _sg_validate_image_data(const sg_image_data* data, sg_pixel_format fmt, int width, int height, int num_faces, int num_mips, int num_slices) { - #if !defined(SOKOL_DEBUG) - _SOKOL_UNUSED(data); - _SOKOL_UNUSED(fmt); - _SOKOL_UNUSED(width); - _SOKOL_UNUSED(height); - _SOKOL_UNUSED(num_faces); - _SOKOL_UNUSED(num_mips); - _SOKOL_UNUSED(num_slices); - #else - for (int face_index = 0; face_index < num_faces; face_index++) { - for (int mip_index = 0; mip_index < num_mips; mip_index++) { - const bool has_data = data->subimage[face_index][mip_index].ptr != 0; - const bool has_size = data->subimage[face_index][mip_index].size > 0; - SOKOL_VALIDATE(has_data && has_size, _SG_VALIDATE_IMAGEDATA_NODATA); - const int mip_width = _sg_max(width >> mip_index, 1); - const int mip_height = _sg_max(height >> mip_index, 1); - const int bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); - const int expected_size = bytes_per_slice * num_slices; - SOKOL_VALIDATE(expected_size == (int)data->subimage[face_index][mip_index].size, _SG_VALIDATE_IMAGEDATA_DATA_SIZE); - } - } - #endif -} - -_SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { - #if !defined(SOKOL_DEBUG) - _SOKOL_UNUSED(desc); - return true; - #else - SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); - SOKOL_VALIDATE(desc->width > 0, _SG_VALIDATE_IMAGEDESC_WIDTH); - SOKOL_VALIDATE(desc->height > 0, _SG_VALIDATE_IMAGEDESC_HEIGHT); - const sg_pixel_format fmt = desc->pixel_format; - const sg_usage usage = desc->usage; - const bool injected = (0 != desc->gl_textures[0]) || - (0 != desc->mtl_textures[0]) || - (0 != desc->d3d11_texture) || - (0 != desc->wgpu_texture); - if (desc->render_target) { - SOKOL_ASSERT(((int)fmt >= 0) && ((int)fmt < _SG_PIXELFORMAT_NUM)); - SOKOL_VALIDATE(_sg.formats[fmt].render, _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT); - /* on GLES2, sample count for render targets is completely ignored */ - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - if (!_sg.gl.gles2) { - #endif - if (desc->sample_count > 1) { - SOKOL_VALIDATE(_sg.features.msaa_render_targets && _sg.formats[fmt].msaa, _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT); - } - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - } - #endif - SOKOL_VALIDATE(usage == SG_USAGE_IMMUTABLE, _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE); - SOKOL_VALIDATE(desc->data.subimage[0][0].ptr==0, _SG_VALIDATE_IMAGEDESC_RT_NO_DATA); - } - else { - SOKOL_VALIDATE(desc->sample_count <= 1, _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT); - const bool valid_nonrt_fmt = !_sg_is_valid_rendertarget_depth_format(fmt); - SOKOL_VALIDATE(valid_nonrt_fmt, _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); - const bool is_compressed = _sg_is_compressed_pixel_format(desc->pixel_format); - const bool is_immutable = (usage == SG_USAGE_IMMUTABLE); - if (is_compressed) { - SOKOL_VALIDATE(is_immutable, _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE); - } - if (!injected && is_immutable) { - // image desc must have valid data - _sg_validate_image_data(&desc->data, - desc->pixel_format, - desc->width, - desc->height, - (desc->type == SG_IMAGETYPE_CUBE) ? 6 : 1, - desc->num_mipmaps, - desc->num_slices); - } - else { - // image desc must not have data - for (int face_index = 0; face_index < SG_CUBEFACE_NUM; face_index++) { - for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) { - const bool no_data = 0 == desc->data.subimage[face_index][mip_index].ptr; - const bool no_size = 0 == desc->data.subimage[face_index][mip_index].size; - if (injected) { - SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA); - } - if (!is_immutable) { - SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA); - } - } - } - } - } - return SOKOL_VALIDATE_END(); - #endif -} - -_SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { - #if !defined(SOKOL_DEBUG) - _SOKOL_UNUSED(desc); - return true; - #else - SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); - #if defined(SOKOL_GLES2) - SOKOL_VALIDATE(0 != desc->attrs[0].name, _SG_VALIDATE_SHADERDESC_ATTR_NAMES); - #elif defined(SOKOL_D3D11) - SOKOL_VALIDATE(0 != desc->attrs[0].sem_name, _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS); - #endif - #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - /* on GL, must provide shader source code */ - SOKOL_VALIDATE(0 != desc->vs.source, _SG_VALIDATE_SHADERDESC_SOURCE); - SOKOL_VALIDATE(0 != desc->fs.source, _SG_VALIDATE_SHADERDESC_SOURCE); - #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) - /* on Metal or D3D11, must provide shader source code or byte code */ - SOKOL_VALIDATE((0 != desc->vs.source)||(0 != desc->vs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); - SOKOL_VALIDATE((0 != desc->fs.source)||(0 != desc->fs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); - #elif defined(SOKOL_WGPU) - /* on WGPU byte code must be provided */ - SOKOL_VALIDATE((0 != desc->vs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_BYTECODE); - SOKOL_VALIDATE((0 != desc->fs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_BYTECODE); - #else - /* Dummy Backend, don't require source or bytecode */ - #endif - for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { - if (desc->attrs[i].name) { - SOKOL_VALIDATE(strlen(desc->attrs[i].name) < _SG_STRING_SIZE, _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); - } - if (desc->attrs[i].sem_name) { - SOKOL_VALIDATE(strlen(desc->attrs[i].sem_name) < _SG_STRING_SIZE, _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); - } - } - /* if shader byte code, the size must also be provided */ - if (0 != desc->vs.bytecode.ptr) { - SOKOL_VALIDATE(desc->vs.bytecode.size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); - } - if (0 != desc->fs.bytecode.ptr) { - SOKOL_VALIDATE(desc->fs.bytecode.size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); - } - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - const sg_shader_stage_desc* stage_desc = (stage_index == 0)? &desc->vs : &desc->fs; - bool uniform_blocks_continuous = true; - for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { - const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; - if (ub_desc->size > 0) { - SOKOL_VALIDATE(uniform_blocks_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UBS); - #if defined(_SOKOL_ANY_GL) - bool uniforms_continuous = true; - uint32_t uniform_offset = 0; - int num_uniforms = 0; - for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { - const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; - if (u_desc->type != SG_UNIFORMTYPE_INVALID) { - SOKOL_VALIDATE(uniforms_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS); - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - SOKOL_VALIDATE(0 != u_desc->name, _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME); - #endif - const int array_count = u_desc->array_count; - SOKOL_VALIDATE(array_count > 0, _SG_VALIDATE_SHADERDESC_UB_ARRAY_COUNT); - const uint32_t u_align = _sg_uniform_alignment(u_desc->type, array_count, ub_desc->layout); - const uint32_t u_size = _sg_uniform_size(u_desc->type, array_count, ub_desc->layout); - uniform_offset = _sg_align_u32(uniform_offset, u_align); - uniform_offset += u_size; - num_uniforms++; - // with std140, arrays are only allowed for FLOAT4, INT4, MAT4 - if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { - if (array_count > 1) { - SOKOL_VALIDATE((u_desc->type == SG_UNIFORMTYPE_FLOAT4) || (u_desc->type == SG_UNIFORMTYPE_INT4) || (u_desc->type == SG_UNIFORMTYPE_MAT4), _SG_VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE); - } - } - } - else { - uniforms_continuous = false; - } - } - if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { - uniform_offset = _sg_align_u32(uniform_offset, 16); - } - SOKOL_VALIDATE((size_t)uniform_offset == ub_desc->size, _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH); - SOKOL_VALIDATE(num_uniforms > 0, _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS); - #endif - } - else { - uniform_blocks_continuous = false; - } - } - bool images_continuous = true; - for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { - const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; - if (img_desc->image_type != _SG_IMAGETYPE_DEFAULT) { - SOKOL_VALIDATE(images_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS); - #if defined(SOKOL_GLES2) - SOKOL_VALIDATE(0 != img_desc->name, _SG_VALIDATE_SHADERDESC_IMG_NAME); - #endif - } - else { - images_continuous = false; - } - } - } - return SOKOL_VALIDATE_END(); - #endif -} - -_SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { - #if !defined(SOKOL_DEBUG) - _SOKOL_UNUSED(desc); - return true; - #else - SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); - SOKOL_VALIDATE(desc->shader.id != SG_INVALID_ID, _SG_VALIDATE_PIPELINEDESC_SHADER); - for (int buf_index = 0; buf_index < SG_MAX_SHADERSTAGE_BUFFERS; buf_index++) { - const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[buf_index]; - if (l_desc->stride == 0) { - continue; - } - SOKOL_VALIDATE((l_desc->stride & 3) == 0, _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); - } - SOKOL_VALIDATE(desc->layout.attrs[0].format != SG_VERTEXFORMAT_INVALID, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); - const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); - SOKOL_VALIDATE(0 != shd, _SG_VALIDATE_PIPELINEDESC_SHADER); - if (shd) { - SOKOL_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PIPELINEDESC_SHADER); - bool attrs_cont = true; - for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { - attrs_cont = false; - continue; - } - SOKOL_VALIDATE(attrs_cont, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - #if defined(SOKOL_GLES2) - /* on GLES2, vertex attribute names must be provided */ - SOKOL_VALIDATE(!_sg_strempty(&shd->gl.attrs[attr_index].name), _SG_VALIDATE_PIPELINEDESC_ATTR_NAME); - #elif defined(SOKOL_D3D11) - /* on D3D11, semantic names (and semantic indices) must be provided */ - SOKOL_VALIDATE(!_sg_strempty(&shd->d3d11.attrs[attr_index].sem_name), _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS); - #endif - } - } - return SOKOL_VALIDATE_END(); - #endif -} - -_SOKOL_PRIVATE bool _sg_validate_pass_desc(const sg_pass_desc* desc) { - #if !defined(SOKOL_DEBUG) - _SOKOL_UNUSED(desc); - return true; - #else - SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); - bool atts_cont = true; - int width = -1, height = -1, sample_count = -1; - for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { - const sg_pass_attachment_desc* att = &desc->color_attachments[att_index]; - if (att->image.id == SG_INVALID_ID) { - SOKOL_VALIDATE(att_index > 0, _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS); - atts_cont = false; - continue; - } - SOKOL_VALIDATE(atts_cont, _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS); - const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); - SOKOL_ASSERT(img); - SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); - SOKOL_VALIDATE(att->mip_level < img->cmn.num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); - if (img->cmn.type == SG_IMAGETYPE_CUBE) { - SOKOL_VALIDATE(att->slice < 6, _SG_VALIDATE_PASSDESC_FACE); - } - else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_LAYER); - } - else if (img->cmn.type == SG_IMAGETYPE_3D) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_SLICE); - } - SOKOL_VALIDATE(img->cmn.render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); - if (att_index == 0) { - width = img->cmn.width >> att->mip_level; - height = img->cmn.height >> att->mip_level; - sample_count = img->cmn.sample_count; - } - else { - SOKOL_VALIDATE(width == img->cmn.width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(height == img->cmn.height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(sample_count == img->cmn.sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); - } - SOKOL_VALIDATE(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format), _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT); - } - if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { - const sg_pass_attachment_desc* att = &desc->depth_stencil_attachment; - const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); - SOKOL_ASSERT(img); - SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); - SOKOL_VALIDATE(att->mip_level < img->cmn.num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); - if (img->cmn.type == SG_IMAGETYPE_CUBE) { - SOKOL_VALIDATE(att->slice < 6, _SG_VALIDATE_PASSDESC_FACE); - } - else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_LAYER); - } - else if (img->cmn.type == SG_IMAGETYPE_3D) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_SLICE); - } - SOKOL_VALIDATE(img->cmn.render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); - SOKOL_VALIDATE(width == img->cmn.width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(height == img->cmn.height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(sample_count == img->cmn.sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); - SOKOL_VALIDATE(_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format), _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT); - } - return SOKOL_VALIDATE_END(); - #endif -} - -_SOKOL_PRIVATE bool _sg_validate_begin_pass(_sg_pass_t* pass) { - #if !defined(SOKOL_DEBUG) - _SOKOL_UNUSED(pass); - return true; - #else - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(pass->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_PASS); - - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - const _sg_pass_attachment_t* att = &pass->cmn.color_atts[i]; - const _sg_image_t* img = _sg_pass_color_image(pass, i); - if (img) { - SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); - SOKOL_VALIDATE(img->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); - } - } - const _sg_image_t* ds_img = _sg_pass_ds_image(pass); - if (ds_img) { - const _sg_pass_attachment_t* att = &pass->cmn.ds_att; - SOKOL_VALIDATE(ds_img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); - SOKOL_VALIDATE(ds_img->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); - } - return SOKOL_VALIDATE_END(); - #endif -} - -_SOKOL_PRIVATE bool _sg_validate_apply_pipeline(sg_pipeline pip_id) { - #if !defined(SOKOL_DEBUG) - _SOKOL_UNUSED(pip_id); - return true; - #else - SOKOL_VALIDATE_BEGIN(); - /* the pipeline object must be alive and valid */ - SOKOL_VALIDATE(pip_id.id != SG_INVALID_ID, _SG_VALIDATE_APIP_PIPELINE_VALID_ID); - const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); - SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_APIP_PIPELINE_EXISTS); - if (!pip) { - return SOKOL_VALIDATE_END(); - } - SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_PIPELINE_VALID); - /* the pipeline's shader must be alive and valid */ - SOKOL_ASSERT(pip->shader); - SOKOL_VALIDATE(pip->shader->slot.id == pip->cmn.shader_id.id, _SG_VALIDATE_APIP_SHADER_EXISTS); - SOKOL_VALIDATE(pip->shader->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_SHADER_VALID); - /* check that pipeline attributes match current pass attributes */ - const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, _sg.cur_pass.id); - if (pass) { - /* an offscreen pass */ - SOKOL_VALIDATE(pip->cmn.color_attachment_count == pass->cmn.num_color_atts, _SG_VALIDATE_APIP_ATT_COUNT); - for (int i = 0; i < pip->cmn.color_attachment_count; i++) { - const _sg_image_t* att_img = _sg_pass_color_image(pass, i); - SOKOL_VALIDATE(pip->cmn.color_formats[i] == att_img->cmn.pixel_format, _SG_VALIDATE_APIP_COLOR_FORMAT); - SOKOL_VALIDATE(pip->cmn.sample_count == att_img->cmn.sample_count, _SG_VALIDATE_APIP_SAMPLE_COUNT); - } - const _sg_image_t* att_dsimg = _sg_pass_ds_image(pass); - if (att_dsimg) { - SOKOL_VALIDATE(pip->cmn.depth_format == att_dsimg->cmn.pixel_format, _SG_VALIDATE_APIP_DEPTH_FORMAT); - } - else { - SOKOL_VALIDATE(pip->cmn.depth_format == SG_PIXELFORMAT_NONE, _SG_VALIDATE_APIP_DEPTH_FORMAT); - } - } - else { - /* default pass */ - SOKOL_VALIDATE(pip->cmn.color_attachment_count == 1, _SG_VALIDATE_APIP_ATT_COUNT); - SOKOL_VALIDATE(pip->cmn.color_formats[0] == _sg.desc.context.color_format, _SG_VALIDATE_APIP_COLOR_FORMAT); - SOKOL_VALIDATE(pip->cmn.depth_format == _sg.desc.context.depth_format, _SG_VALIDATE_APIP_DEPTH_FORMAT); - SOKOL_VALIDATE(pip->cmn.sample_count == _sg.desc.context.sample_count, _SG_VALIDATE_APIP_SAMPLE_COUNT); - } - return SOKOL_VALIDATE_END(); - #endif -} - -_SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { - #if !defined(SOKOL_DEBUG) - _SOKOL_UNUSED(bindings); - return true; - #else - SOKOL_VALIDATE_BEGIN(); - - /* a pipeline object must have been applied */ - SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_ABND_PIPELINE); - const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); - SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_ABND_PIPELINE_EXISTS); - if (!pip) { - return SOKOL_VALIDATE_END(); - } - SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_ABND_PIPELINE_VALID); - SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); - - /* has expected vertex buffers, and vertex buffers still exist */ - for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { - if (bindings->vertex_buffers[i].id != SG_INVALID_ID) { - SOKOL_VALIDATE(pip->cmn.vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); - /* buffers in vertex-buffer-slots must be of type SG_BUFFERTYPE_VERTEXBUFFER */ - const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); - SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_VB_EXISTS); - if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(SG_BUFFERTYPE_VERTEXBUFFER == buf->cmn.type, _SG_VALIDATE_ABND_VB_TYPE); - SOKOL_VALIDATE(!buf->cmn.append_overflow, _SG_VALIDATE_ABND_VB_OVERFLOW); - } - } - else { - /* vertex buffer provided in a slot which has no vertex layout in pipeline */ - SOKOL_VALIDATE(!pip->cmn.vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); - } - } - - /* index buffer expected or not, and index buffer still exists */ - if (pip->cmn.index_type == SG_INDEXTYPE_NONE) { - /* pipeline defines non-indexed rendering, but index buffer provided */ - SOKOL_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, _SG_VALIDATE_ABND_IB); - } - else { - /* pipeline defines indexed rendering, but no index buffer provided */ - SOKOL_VALIDATE(bindings->index_buffer.id != SG_INVALID_ID, _SG_VALIDATE_ABND_NO_IB); - } - if (bindings->index_buffer.id != SG_INVALID_ID) { - /* buffer in index-buffer-slot must be of type SG_BUFFERTYPE_INDEXBUFFER */ - const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); - SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_IB_EXISTS); - if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(SG_BUFFERTYPE_INDEXBUFFER == buf->cmn.type, _SG_VALIDATE_ABND_IB_TYPE); - SOKOL_VALIDATE(!buf->cmn.append_overflow, _SG_VALIDATE_ABND_IB_OVERFLOW); - } - } - - /* has expected vertex shader images */ - for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { - _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; - if (bindings->vs_images[i].id != SG_INVALID_ID) { - SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); - const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->vs_images[i].id); - SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_VS_IMG_EXISTS); - if (img && img->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(img->cmn.type == stage->images[i].image_type, _SG_VALIDATE_ABND_VS_IMG_TYPES); - } - } - else { - SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); - } - } - - /* has expected fragment shader images */ - for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { - _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; - if (bindings->fs_images[i].id != SG_INVALID_ID) { - SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); - const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->fs_images[i].id); - SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_FS_IMG_EXISTS); - if (img && img->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(img->cmn.type == stage->images[i].image_type, _SG_VALIDATE_ABND_FS_IMG_TYPES); - } - } - else { - SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); - } - } - return SOKOL_VALIDATE_END(); - #endif -} - -_SOKOL_PRIVATE bool _sg_validate_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { - #if !defined(SOKOL_DEBUG) - _SOKOL_UNUSED(stage_index); - _SOKOL_UNUSED(ub_index); - _SOKOL_UNUSED(data); - return true; - #else - SOKOL_ASSERT((stage_index == SG_SHADERSTAGE_VS) || (stage_index == SG_SHADERSTAGE_FS)); - SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_AUB_NO_PIPELINE); - const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); - SOKOL_ASSERT(pip && (pip->slot.id == _sg.cur_pipeline.id)); - SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->cmn.shader_id.id)); - - /* check that there is a uniform block at 'stage' and 'ub_index' */ - const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index]; - SOKOL_VALIDATE(ub_index < stage->num_uniform_blocks, _SG_VALIDATE_AUB_NO_UB_AT_SLOT); - - /* check that the provided data size doesn't exceed the uniform block size */ - SOKOL_VALIDATE(data->size <= stage->uniform_blocks[ub_index].size, _SG_VALIDATE_AUB_SIZE); - - return SOKOL_VALIDATE_END(); - #endif -} - -_SOKOL_PRIVATE bool _sg_validate_update_buffer(const _sg_buffer_t* buf, const sg_range* data) { - #if !defined(SOKOL_DEBUG) - _SOKOL_UNUSED(buf); - _SOKOL_UNUSED(data); - return true; - #else - SOKOL_ASSERT(buf && data && data->ptr); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDATEBUF_USAGE); - SOKOL_VALIDATE(buf->cmn.size >= (int)data->size, _SG_VALIDATE_UPDATEBUF_SIZE); - SOKOL_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_ONCE); - SOKOL_VALIDATE(buf->cmn.append_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_APPEND); - return SOKOL_VALIDATE_END(); - #endif -} - -_SOKOL_PRIVATE bool _sg_validate_append_buffer(const _sg_buffer_t* buf, const sg_range* data) { - #if !defined(SOKOL_DEBUG) - _SOKOL_UNUSED(buf); - _SOKOL_UNUSED(data); - return true; - #else - SOKOL_ASSERT(buf && data && data->ptr); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_APPENDBUF_USAGE); - SOKOL_VALIDATE(buf->cmn.size >= (buf->cmn.append_pos + (int)data->size), _SG_VALIDATE_APPENDBUF_SIZE); - SOKOL_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, _SG_VALIDATE_APPENDBUF_UPDATE); - return SOKOL_VALIDATE_END(); - #endif -} - -_SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_image_data* data) { - #if !defined(SOKOL_DEBUG) - _SOKOL_UNUSED(img); - _SOKOL_UNUSED(data); - return true; - #else - SOKOL_ASSERT(img && data); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(img->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDIMG_USAGE); - SOKOL_VALIDATE(img->cmn.upd_frame_index != _sg.frame_index, _SG_VALIDATE_UPDIMG_ONCE); - _sg_validate_image_data(data, - img->cmn.pixel_format, - img->cmn.width, - img->cmn.height, - (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6 : 1, - img->cmn.num_mipmaps, - img->cmn.num_slices); - return SOKOL_VALIDATE_END(); - #endif -} - -/*== fill in desc default values =============================================*/ -_SOKOL_PRIVATE sg_buffer_desc _sg_buffer_desc_defaults(const sg_buffer_desc* desc) { - sg_buffer_desc def = *desc; - def.type = _sg_def(def.type, SG_BUFFERTYPE_VERTEXBUFFER); - def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); - if (def.size == 0) { - def.size = def.data.size; - } - else if (def.data.size == 0) { - def.data.size = def.size; - } - return def; -} - -_SOKOL_PRIVATE sg_image_desc _sg_image_desc_defaults(const sg_image_desc* desc) { - sg_image_desc def = *desc; - def.type = _sg_def(def.type, SG_IMAGETYPE_2D); - def.num_slices = _sg_def(def.num_slices, 1); - def.num_mipmaps = _sg_def(def.num_mipmaps, 1); - def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); - if (desc->render_target) { - def.pixel_format = _sg_def(def.pixel_format, _sg.desc.context.color_format); - def.sample_count = _sg_def(def.sample_count, _sg.desc.context.sample_count); - } - else { - def.pixel_format = _sg_def(def.pixel_format, SG_PIXELFORMAT_RGBA8); - def.sample_count = _sg_def(def.sample_count, 1); - } - def.min_filter = _sg_def(def.min_filter, SG_FILTER_NEAREST); - def.mag_filter = _sg_def(def.mag_filter, SG_FILTER_NEAREST); - def.wrap_u = _sg_def(def.wrap_u, SG_WRAP_REPEAT); - def.wrap_v = _sg_def(def.wrap_v, SG_WRAP_REPEAT); - def.wrap_w = _sg_def(def.wrap_w, SG_WRAP_REPEAT); - def.border_color = _sg_def(def.border_color, SG_BORDERCOLOR_OPAQUE_BLACK); - def.max_anisotropy = _sg_def(def.max_anisotropy, 1); - def.max_lod = _sg_def_flt(def.max_lod, FLT_MAX); - return def; -} - -_SOKOL_PRIVATE sg_shader_desc _sg_shader_desc_defaults(const sg_shader_desc* desc) { - sg_shader_desc def = *desc; - #if defined(SOKOL_METAL) - def.vs.entry = _sg_def(def.vs.entry, "_main"); - def.fs.entry = _sg_def(def.fs.entry, "_main"); - #else - def.vs.entry = _sg_def(def.vs.entry, "main"); - def.fs.entry = _sg_def(def.fs.entry, "main"); - #endif - #if defined(SOKOL_D3D11) - if (def.vs.source) { - def.vs.d3d11_target = _sg_def(def.vs.d3d11_target, "vs_4_0"); - } - if (def.fs.source) { - def.fs.d3d11_target = _sg_def(def.fs.d3d11_target, "ps_4_0"); - } - #endif - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &def.vs : &def.fs; - for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { - sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; - if (0 == ub_desc->size) { - break; - } - ub_desc->layout = _sg_def(ub_desc->layout, SG_UNIFORMLAYOUT_NATIVE); - for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { - sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; - if (u_desc->type == SG_UNIFORMTYPE_INVALID) { - break; - } - u_desc->array_count = _sg_def(u_desc->array_count, 1); - } - } - for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { - sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; - if (img_desc->image_type == _SG_IMAGETYPE_DEFAULT) { - break; - } - img_desc->sampler_type = _sg_def(img_desc->sampler_type, SG_SAMPLERTYPE_FLOAT); - } - } - return def; -} - -_SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_desc* desc) { - sg_pipeline_desc def = *desc; - - def.primitive_type = _sg_def(def.primitive_type, SG_PRIMITIVETYPE_TRIANGLES); - def.index_type = _sg_def(def.index_type, SG_INDEXTYPE_NONE); - def.cull_mode = _sg_def(def.cull_mode, SG_CULLMODE_NONE); - def.face_winding = _sg_def(def.face_winding, SG_FACEWINDING_CW); - def.sample_count = _sg_def(def.sample_count, _sg.desc.context.sample_count); - - def.stencil.front.compare = _sg_def(def.stencil.front.compare, SG_COMPAREFUNC_ALWAYS); - def.stencil.front.fail_op = _sg_def(def.stencil.front.fail_op, SG_STENCILOP_KEEP); - def.stencil.front.depth_fail_op = _sg_def(def.stencil.front.depth_fail_op, SG_STENCILOP_KEEP); - def.stencil.front.pass_op = _sg_def(def.stencil.front.pass_op, SG_STENCILOP_KEEP); - def.stencil.back.compare = _sg_def(def.stencil.back.compare, SG_COMPAREFUNC_ALWAYS); - def.stencil.back.fail_op = _sg_def(def.stencil.back.fail_op, SG_STENCILOP_KEEP); - def.stencil.back.depth_fail_op = _sg_def(def.stencil.back.depth_fail_op, SG_STENCILOP_KEEP); - def.stencil.back.pass_op = _sg_def(def.stencil.back.pass_op, SG_STENCILOP_KEEP); - - def.depth.compare = _sg_def(def.depth.compare, SG_COMPAREFUNC_ALWAYS); - def.depth.pixel_format = _sg_def(def.depth.pixel_format, _sg.desc.context.depth_format); - def.color_count = _sg_def(def.color_count, 1); - if (def.color_count > SG_MAX_COLOR_ATTACHMENTS) { - def.color_count = SG_MAX_COLOR_ATTACHMENTS; - } - for (int i = 0; i < def.color_count; i++) { - sg_color_state* cs = &def.colors[i]; - cs->pixel_format = _sg_def(cs->pixel_format, _sg.desc.context.color_format); - cs->write_mask = _sg_def(cs->write_mask, SG_COLORMASK_RGBA); - sg_blend_state* bs = &def.colors[i].blend; - bs->src_factor_rgb = _sg_def(bs->src_factor_rgb, SG_BLENDFACTOR_ONE); - bs->dst_factor_rgb = _sg_def(bs->dst_factor_rgb, SG_BLENDFACTOR_ZERO); - bs->op_rgb = _sg_def(bs->op_rgb, SG_BLENDOP_ADD); - bs->src_factor_alpha = _sg_def(bs->src_factor_alpha, SG_BLENDFACTOR_ONE); - bs->dst_factor_alpha = _sg_def(bs->dst_factor_alpha, SG_BLENDFACTOR_ZERO); - bs->op_alpha = _sg_def(bs->op_alpha, SG_BLENDOP_ADD); - } - - for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - sg_vertex_attr_desc* a_desc = &def.layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { - break; - } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - sg_buffer_layout_desc* b_desc = &def.layout.buffers[a_desc->buffer_index]; - b_desc->step_func = _sg_def(b_desc->step_func, SG_VERTEXSTEP_PER_VERTEX); - b_desc->step_rate = _sg_def(b_desc->step_rate, 1); - } - - /* resolve vertex layout strides and offsets */ - int auto_offset[SG_MAX_SHADERSTAGE_BUFFERS]; - memset(auto_offset, 0, sizeof(auto_offset)); - bool use_auto_offset = true; - for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - /* to use computed offsets, *all* attr offsets must be 0 */ - if (def.layout.attrs[attr_index].offset != 0) { - use_auto_offset = false; - } - } - for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - sg_vertex_attr_desc* a_desc = &def.layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { - break; - } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - if (use_auto_offset) { - a_desc->offset = auto_offset[a_desc->buffer_index]; - } - auto_offset[a_desc->buffer_index] += _sg_vertexformat_bytesize(a_desc->format); - } - /* compute vertex strides if needed */ - for (int buf_index = 0; buf_index < SG_MAX_SHADERSTAGE_BUFFERS; buf_index++) { - sg_buffer_layout_desc* l_desc = &def.layout.buffers[buf_index]; - if (l_desc->stride == 0) { - l_desc->stride = auto_offset[buf_index]; - } - } - - return def; -} - -_SOKOL_PRIVATE sg_pass_desc _sg_pass_desc_defaults(const sg_pass_desc* desc) { - /* FIXME: no values to replace in sg_pass_desc? */ - sg_pass_desc def = *desc; - return def; -} - -/*== allocate/initialize resource private functions ==========================*/ -_SOKOL_PRIVATE sg_buffer _sg_alloc_buffer(void) { - sg_buffer res; - int slot_index = _sg_pool_alloc_index(&_sg.pools.buffer_pool); - if (_SG_INVALID_SLOT_INDEX != slot_index) { - res.id = _sg_slot_alloc(&_sg.pools.buffer_pool, &_sg.pools.buffers[slot_index].slot, slot_index); - } - else { - /* pool is exhausted */ - res.id = SG_INVALID_ID; - } - return res; -} - -_SOKOL_PRIVATE sg_image _sg_alloc_image(void) { - sg_image res; - int slot_index = _sg_pool_alloc_index(&_sg.pools.image_pool); - if (_SG_INVALID_SLOT_INDEX != slot_index) { - res.id = _sg_slot_alloc(&_sg.pools.image_pool, &_sg.pools.images[slot_index].slot, slot_index); - } - else { - /* pool is exhausted */ - res.id = SG_INVALID_ID; - } - return res; -} - -_SOKOL_PRIVATE sg_shader _sg_alloc_shader(void) { - sg_shader res; - int slot_index = _sg_pool_alloc_index(&_sg.pools.shader_pool); - if (_SG_INVALID_SLOT_INDEX != slot_index) { - res.id = _sg_slot_alloc(&_sg.pools.shader_pool, &_sg.pools.shaders[slot_index].slot, slot_index); - } - else { - /* pool is exhausted */ - res.id = SG_INVALID_ID; - } - return res; -} - -_SOKOL_PRIVATE sg_pipeline _sg_alloc_pipeline(void) { - sg_pipeline res; - int slot_index = _sg_pool_alloc_index(&_sg.pools.pipeline_pool); - if (_SG_INVALID_SLOT_INDEX != slot_index) { - res.id =_sg_slot_alloc(&_sg.pools.pipeline_pool, &_sg.pools.pipelines[slot_index].slot, slot_index); - } - else { - /* pool is exhausted */ - res.id = SG_INVALID_ID; - } - return res; -} - -_SOKOL_PRIVATE sg_pass _sg_alloc_pass(void) { - sg_pass res; - int slot_index = _sg_pool_alloc_index(&_sg.pools.pass_pool); - if (_SG_INVALID_SLOT_INDEX != slot_index) { - res.id = _sg_slot_alloc(&_sg.pools.pass_pool, &_sg.pools.passes[slot_index].slot, slot_index); - } - else { - /* pool is exhausted */ - res.id = SG_INVALID_ID; - } - return res; -} - -_SOKOL_PRIVATE void _sg_dealloc_buffer(sg_buffer buf_id) { - SOKOL_ASSERT(buf_id.id != SG_INVALID_ID); - _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); - SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); - _sg_reset_slot(&buf->slot); - _sg_pool_free_index(&_sg.pools.buffer_pool, _sg_slot_index(buf_id.id)); -} - -_SOKOL_PRIVATE void _sg_dealloc_image(sg_image img_id) { - SOKOL_ASSERT(img_id.id != SG_INVALID_ID); - _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); - SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); - _sg_reset_slot(&img->slot); - _sg_pool_free_index(&_sg.pools.image_pool, _sg_slot_index(img_id.id)); -} - -_SOKOL_PRIVATE void _sg_dealloc_shader(sg_shader shd_id) { - SOKOL_ASSERT(shd_id.id != SG_INVALID_ID); - _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); - SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); - _sg_reset_slot(&shd->slot); - _sg_pool_free_index(&_sg.pools.shader_pool, _sg_slot_index(shd_id.id)); -} - -_SOKOL_PRIVATE void _sg_dealloc_pipeline(sg_pipeline pip_id) { - SOKOL_ASSERT(pip_id.id != SG_INVALID_ID); - _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); - SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); - _sg_reset_slot(&pip->slot); - _sg_pool_free_index(&_sg.pools.pipeline_pool, _sg_slot_index(pip_id.id)); -} - -_SOKOL_PRIVATE void _sg_dealloc_pass(sg_pass pass_id) { - SOKOL_ASSERT(pass_id.id != SG_INVALID_ID); - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); - _sg_reset_slot(&pass->slot); - _sg_pool_free_index(&_sg.pools.pass_pool, _sg_slot_index(pass_id.id)); -} - -_SOKOL_PRIVATE void _sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) { - SOKOL_ASSERT(buf_id.id != SG_INVALID_ID && desc); - _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); - SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); - buf->slot.ctx_id = _sg.active_context.id; - if (_sg_validate_buffer_desc(desc)) { - buf->slot.state = _sg_create_buffer(buf, desc); - } - else { - buf->slot.state = SG_RESOURCESTATE_FAILED; - } - SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID)||(buf->slot.state == SG_RESOURCESTATE_FAILED)); -} - -_SOKOL_PRIVATE void _sg_init_image(sg_image img_id, const sg_image_desc* desc) { - SOKOL_ASSERT(img_id.id != SG_INVALID_ID && desc); - _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); - SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); - img->slot.ctx_id = _sg.active_context.id; - if (_sg_validate_image_desc(desc)) { - img->slot.state = _sg_create_image(img, desc); - } - else { - img->slot.state = SG_RESOURCESTATE_FAILED; - } - SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID)||(img->slot.state == SG_RESOURCESTATE_FAILED)); -} - -_SOKOL_PRIVATE void _sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { - SOKOL_ASSERT(shd_id.id != SG_INVALID_ID && desc); - _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); - SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); - shd->slot.ctx_id = _sg.active_context.id; - if (_sg_validate_shader_desc(desc)) { - shd->slot.state = _sg_create_shader(shd, desc); - } - else { - shd->slot.state = SG_RESOURCESTATE_FAILED; - } - SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID)||(shd->slot.state == SG_RESOURCESTATE_FAILED)); -} - -_SOKOL_PRIVATE void _sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc) { - SOKOL_ASSERT(pip_id.id != SG_INVALID_ID && desc); - _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); - SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); - pip->slot.ctx_id = _sg.active_context.id; - if (_sg_validate_pipeline_desc(desc)) { - _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); - if (shd && (shd->slot.state == SG_RESOURCESTATE_VALID)) { - pip->slot.state = _sg_create_pipeline(pip, shd, desc); - } - else { - pip->slot.state = SG_RESOURCESTATE_FAILED; - } - } - else { - pip->slot.state = SG_RESOURCESTATE_FAILED; - } - SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID)||(pip->slot.state == SG_RESOURCESTATE_FAILED)); -} - -_SOKOL_PRIVATE void _sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { - SOKOL_ASSERT(pass_id.id != SG_INVALID_ID && desc); - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); - pass->slot.ctx_id = _sg.active_context.id; - if (_sg_validate_pass_desc(desc)) { - /* lookup pass attachment image pointers */ - _sg_image_t* att_imgs[SG_MAX_COLOR_ATTACHMENTS + 1]; - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (desc->color_attachments[i].image.id) { - att_imgs[i] = _sg_lookup_image(&_sg.pools, desc->color_attachments[i].image.id); - /* FIXME: this shouldn't be an assertion, but result in a SG_RESOURCESTATE_FAILED pass */ - SOKOL_ASSERT(att_imgs[i] && att_imgs[i]->slot.state == SG_RESOURCESTATE_VALID); - } - else { - att_imgs[i] = 0; - } - } - const int ds_att_index = SG_MAX_COLOR_ATTACHMENTS; - if (desc->depth_stencil_attachment.image.id) { - att_imgs[ds_att_index] = _sg_lookup_image(&_sg.pools, desc->depth_stencil_attachment.image.id); - /* FIXME: this shouldn't be an assertion, but result in a SG_RESOURCESTATE_FAILED pass */ - SOKOL_ASSERT(att_imgs[ds_att_index] && att_imgs[ds_att_index]->slot.state == SG_RESOURCESTATE_VALID); - } - else { - att_imgs[ds_att_index] = 0; - } - pass->slot.state = _sg_create_pass(pass, att_imgs, desc); - } - else { - pass->slot.state = SG_RESOURCESTATE_FAILED; - } - SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID)||(pass->slot.state == SG_RESOURCESTATE_FAILED)); -} - -_SOKOL_PRIVATE bool _sg_uninit_buffer(sg_buffer buf_id) { - _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); - if (buf) { - if (buf->slot.ctx_id == _sg.active_context.id) { - _sg_destroy_buffer(buf); - _sg_reset_buffer(buf); - return true; - } - else { - SOKOL_LOG("_sg_uninit_buffer: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); - } - } - return false; -} - -_SOKOL_PRIVATE bool _sg_uninit_image(sg_image img_id) { - _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); - if (img) { - if (img->slot.ctx_id == _sg.active_context.id) { - _sg_destroy_image(img); - _sg_reset_image(img); - return true; - } - else { - SOKOL_LOG("_sg_uninit_image: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); - } - } - return false; -} - -_SOKOL_PRIVATE bool _sg_uninit_shader(sg_shader shd_id) { - _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); - if (shd) { - if (shd->slot.ctx_id == _sg.active_context.id) { - _sg_destroy_shader(shd); - _sg_reset_shader(shd); - return true; - } - else { - SOKOL_LOG("_sg_uninit_shader: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); - } - } - return false; -} - -_SOKOL_PRIVATE bool _sg_uninit_pipeline(sg_pipeline pip_id) { - _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); - if (pip) { - if (pip->slot.ctx_id == _sg.active_context.id) { - _sg_destroy_pipeline(pip); - _sg_reset_pipeline(pip); - return true; - } - else { - SOKOL_LOG("_sg_uninit_pipeline: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); - } - } - return false; -} - -_SOKOL_PRIVATE bool _sg_uninit_pass(sg_pass pass_id) { - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - if (pass) { - if (pass->slot.ctx_id == _sg.active_context.id) { - _sg_destroy_pass(pass); - _sg_reset_pass(pass); - return true; - } - else { - SOKOL_LOG("_sg_uninit_pass: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); - } - } - return false; -} - -/*== PUBLIC API FUNCTIONS ====================================================*/ - -#if defined(SOKOL_METAL) - // this is ARC compatible - #if defined(__cplusplus) - #define _SG_CLEAR(type, item) { item = (type) { }; } - #else - #define _SG_CLEAR(type, item) { item = (type) { 0 }; } - #endif -#else - #define _SG_CLEAR(type, item) { memset(&item, 0, sizeof(item)); } -#endif - -SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { - SOKOL_ASSERT(desc); - SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); - _SG_CLEAR(_sg_state_t, _sg); - _sg.desc = *desc; - - /* replace zero-init items with their default values - NOTE: on WebGPU, the default color pixel format MUST be provided, - cannot be a default compile-time constant. - */ - #if defined(SOKOL_WGPU) - SOKOL_ASSERT(SG_PIXELFORMAT_NONE != _sg.desc.context.color_format); - #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) - _sg.desc.context.color_format = _sg_def(_sg.desc.context.color_format, SG_PIXELFORMAT_BGRA8); - #else - _sg.desc.context.color_format = _sg_def(_sg.desc.context.color_format, SG_PIXELFORMAT_RGBA8); - #endif - _sg.desc.context.depth_format = _sg_def(_sg.desc.context.depth_format, SG_PIXELFORMAT_DEPTH_STENCIL); - _sg.desc.context.sample_count = _sg_def(_sg.desc.context.sample_count, 1); - _sg.desc.buffer_pool_size = _sg_def(_sg.desc.buffer_pool_size, _SG_DEFAULT_BUFFER_POOL_SIZE); - _sg.desc.image_pool_size = _sg_def(_sg.desc.image_pool_size, _SG_DEFAULT_IMAGE_POOL_SIZE); - _sg.desc.shader_pool_size = _sg_def(_sg.desc.shader_pool_size, _SG_DEFAULT_SHADER_POOL_SIZE); - _sg.desc.pipeline_pool_size = _sg_def(_sg.desc.pipeline_pool_size, _SG_DEFAULT_PIPELINE_POOL_SIZE); - _sg.desc.pass_pool_size = _sg_def(_sg.desc.pass_pool_size, _SG_DEFAULT_PASS_POOL_SIZE); - _sg.desc.context_pool_size = _sg_def(_sg.desc.context_pool_size, _SG_DEFAULT_CONTEXT_POOL_SIZE); - _sg.desc.uniform_buffer_size = _sg_def(_sg.desc.uniform_buffer_size, _SG_DEFAULT_UB_SIZE); - _sg.desc.staging_buffer_size = _sg_def(_sg.desc.staging_buffer_size, _SG_DEFAULT_STAGING_SIZE); - _sg.desc.sampler_cache_size = _sg_def(_sg.desc.sampler_cache_size, _SG_DEFAULT_SAMPLER_CACHE_CAPACITY); - - _sg_setup_pools(&_sg.pools, &_sg.desc); - _sg.frame_index = 1; - _sg_setup_backend(&_sg.desc); - _sg.valid = true; - sg_setup_context(); -} - -SOKOL_API_IMPL void sg_shutdown(void) { - /* can only delete resources for the currently set context here, if multiple - contexts are used, the app code must take care of properly releasing them - (since only the app code can switch between 3D-API contexts) - */ - if (_sg.active_context.id != SG_INVALID_ID) { - _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, _sg.active_context.id); - if (ctx) { - _sg_destroy_all_resources(&_sg.pools, _sg.active_context.id); - _sg_destroy_context(ctx); - } - } - _sg_discard_backend(); - _sg_discard_pools(&_sg.pools); - _sg.valid = false; -} - -SOKOL_API_IMPL bool sg_isvalid(void) { - return _sg.valid; -} - -SOKOL_API_IMPL sg_desc sg_query_desc(void) { - SOKOL_ASSERT(_sg.valid); - return _sg.desc; -} - -SOKOL_API_IMPL sg_backend sg_query_backend(void) { - SOKOL_ASSERT(_sg.valid); - return _sg.backend; -} - -SOKOL_API_IMPL sg_features sg_query_features(void) { - SOKOL_ASSERT(_sg.valid); - return _sg.features; -} - -SOKOL_API_IMPL sg_limits sg_query_limits(void) { - SOKOL_ASSERT(_sg.valid); - return _sg.limits; -} - -SOKOL_API_IMPL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) { - SOKOL_ASSERT(_sg.valid); - int fmt_index = (int) fmt; - SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM)); - return _sg.formats[fmt_index]; -} - -SOKOL_API_IMPL sg_context sg_setup_context(void) { - SOKOL_ASSERT(_sg.valid); - sg_context res; - int slot_index = _sg_pool_alloc_index(&_sg.pools.context_pool); - if (_SG_INVALID_SLOT_INDEX != slot_index) { - res.id = _sg_slot_alloc(&_sg.pools.context_pool, &_sg.pools.contexts[slot_index].slot, slot_index); - _sg_context_t* ctx = _sg_context_at(&_sg.pools, res.id); - ctx->slot.state = _sg_create_context(ctx); - SOKOL_ASSERT(ctx->slot.state == SG_RESOURCESTATE_VALID); - _sg_activate_context(ctx); - } - else { - /* pool is exhausted */ - res.id = SG_INVALID_ID; - } - _sg.active_context = res; - return res; -} - -SOKOL_API_IMPL void sg_discard_context(sg_context ctx_id) { - SOKOL_ASSERT(_sg.valid); - _sg_destroy_all_resources(&_sg.pools, ctx_id.id); - _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, ctx_id.id); - if (ctx) { - _sg_destroy_context(ctx); - _sg_reset_context(ctx); - _sg_reset_slot(&ctx->slot); - _sg_pool_free_index(&_sg.pools.context_pool, _sg_slot_index(ctx_id.id)); - } - _sg.active_context.id = SG_INVALID_ID; - _sg_activate_context(0); -} - -SOKOL_API_IMPL void sg_activate_context(sg_context ctx_id) { - SOKOL_ASSERT(_sg.valid); - _sg.active_context = ctx_id; - _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, ctx_id.id); - /* NOTE: ctx can be 0 here if the context is no longer valid */ - _sg_activate_context(ctx); -} - -SOKOL_API_IMPL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(trace_hooks); - _SOKOL_UNUSED(trace_hooks); - #if defined(SOKOL_TRACE_HOOKS) - sg_trace_hooks old_hooks = _sg.hooks; - _sg.hooks = *trace_hooks; - #else - static sg_trace_hooks old_hooks; - SOKOL_LOG("sg_install_trace_hooks() called, but SG_TRACE_HOOKS is not defined!"); - #endif - return old_hooks; -} - -SOKOL_API_IMPL sg_buffer sg_alloc_buffer(void) { - SOKOL_ASSERT(_sg.valid); - sg_buffer res = _sg_alloc_buffer(); - _SG_TRACE_ARGS(alloc_buffer, res); - return res; -} - -SOKOL_API_IMPL sg_image sg_alloc_image(void) { - SOKOL_ASSERT(_sg.valid); - sg_image res = _sg_alloc_image(); - _SG_TRACE_ARGS(alloc_image, res); - return res; -} - -SOKOL_API_IMPL sg_shader sg_alloc_shader(void) { - SOKOL_ASSERT(_sg.valid); - sg_shader res = _sg_alloc_shader(); - _SG_TRACE_ARGS(alloc_shader, res); - return res; -} - -SOKOL_API_IMPL sg_pipeline sg_alloc_pipeline(void) { - SOKOL_ASSERT(_sg.valid); - sg_pipeline res = _sg_alloc_pipeline(); - _SG_TRACE_ARGS(alloc_pipeline, res); - return res; -} - -SOKOL_API_IMPL sg_pass sg_alloc_pass(void) { - SOKOL_ASSERT(_sg.valid); - sg_pass res = _sg_alloc_pass(); - _SG_TRACE_ARGS(alloc_pass, res); - return res; -} - -SOKOL_API_IMPL void sg_dealloc_buffer(sg_buffer buf_id) { - SOKOL_ASSERT(_sg.valid); - _sg_dealloc_buffer(buf_id); - _SG_TRACE_ARGS(dealloc_buffer, buf_id); -} - -SOKOL_API_IMPL void sg_dealloc_image(sg_image img_id) { - SOKOL_ASSERT(_sg.valid); - _sg_dealloc_image(img_id); - _SG_TRACE_ARGS(dealloc_image, img_id); -} - -SOKOL_API_IMPL void sg_dealloc_shader(sg_shader shd_id) { - SOKOL_ASSERT(_sg.valid); - _sg_dealloc_shader(shd_id); - _SG_TRACE_ARGS(dealloc_shader, shd_id); -} - -SOKOL_API_IMPL void sg_dealloc_pipeline(sg_pipeline pip_id) { - SOKOL_ASSERT(_sg.valid); - _sg_dealloc_pipeline(pip_id); - _SG_TRACE_ARGS(dealloc_pipeline, pip_id); -} - -SOKOL_API_IMPL void sg_dealloc_pass(sg_pass pass_id) { - SOKOL_ASSERT(_sg.valid); - _sg_dealloc_pass(pass_id); - _SG_TRACE_ARGS(dealloc_pass, pass_id); -} - -SOKOL_API_IMPL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) { - SOKOL_ASSERT(_sg.valid); - sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc); - _sg_init_buffer(buf_id, &desc_def); - _SG_TRACE_ARGS(init_buffer, buf_id, &desc_def); -} - -SOKOL_API_IMPL void sg_init_image(sg_image img_id, const sg_image_desc* desc) { - SOKOL_ASSERT(_sg.valid); - sg_image_desc desc_def = _sg_image_desc_defaults(desc); - _sg_init_image(img_id, &desc_def); - _SG_TRACE_ARGS(init_image, img_id, &desc_def); -} - -SOKOL_API_IMPL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { - SOKOL_ASSERT(_sg.valid); - sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); - _sg_init_shader(shd_id, &desc_def); - _SG_TRACE_ARGS(init_shader, shd_id, &desc_def); -} - -SOKOL_API_IMPL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc) { - SOKOL_ASSERT(_sg.valid); - sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc); - _sg_init_pipeline(pip_id, &desc_def); - _SG_TRACE_ARGS(init_pipeline, pip_id, &desc_def); -} - -SOKOL_API_IMPL void sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { - SOKOL_ASSERT(_sg.valid); - sg_pass_desc desc_def = _sg_pass_desc_defaults(desc); - _sg_init_pass(pass_id, &desc_def); - _SG_TRACE_ARGS(init_pass, pass_id, &desc_def); -} - -SOKOL_API_IMPL bool sg_uninit_buffer(sg_buffer buf_id) { - SOKOL_ASSERT(_sg.valid); - bool res = _sg_uninit_buffer(buf_id); - _SG_TRACE_ARGS(uninit_buffer, buf_id); - return res; -} - -SOKOL_API_IMPL bool sg_uninit_image(sg_image img_id) { - SOKOL_ASSERT(_sg.valid); - bool res = _sg_uninit_image(img_id); - _SG_TRACE_ARGS(uninit_image, img_id); - return res; -} - -SOKOL_API_IMPL bool sg_uninit_shader(sg_shader shd_id) { - SOKOL_ASSERT(_sg.valid); - bool res = _sg_uninit_shader(shd_id); - _SG_TRACE_ARGS(uninit_shader, shd_id); - return res; -} - -SOKOL_API_IMPL bool sg_uninit_pipeline(sg_pipeline pip_id) { - SOKOL_ASSERT(_sg.valid); - bool res = _sg_uninit_pipeline(pip_id); - _SG_TRACE_ARGS(uninit_pipeline, pip_id); - return res; -} - -SOKOL_API_IMPL bool sg_uninit_pass(sg_pass pass_id) { - SOKOL_ASSERT(_sg.valid); - bool res = _sg_uninit_pass(pass_id); - _SG_TRACE_ARGS(uninit_pass, pass_id); - return res; -} - -/*-- set allocated resource to failed state ----------------------------------*/ -SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(buf_id.id != SG_INVALID_ID); - _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); - SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); - buf->slot.ctx_id = _sg.active_context.id; - buf->slot.state = SG_RESOURCESTATE_FAILED; - _SG_TRACE_ARGS(fail_buffer, buf_id); -} - -SOKOL_API_IMPL void sg_fail_image(sg_image img_id) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(img_id.id != SG_INVALID_ID); - _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); - SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); - img->slot.ctx_id = _sg.active_context.id; - img->slot.state = SG_RESOURCESTATE_FAILED; - _SG_TRACE_ARGS(fail_image, img_id); -} - -SOKOL_API_IMPL void sg_fail_shader(sg_shader shd_id) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(shd_id.id != SG_INVALID_ID); - _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); - SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); - shd->slot.ctx_id = _sg.active_context.id; - shd->slot.state = SG_RESOURCESTATE_FAILED; - _SG_TRACE_ARGS(fail_shader, shd_id); -} - -SOKOL_API_IMPL void sg_fail_pipeline(sg_pipeline pip_id) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(pip_id.id != SG_INVALID_ID); - _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); - SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); - pip->slot.ctx_id = _sg.active_context.id; - pip->slot.state = SG_RESOURCESTATE_FAILED; - _SG_TRACE_ARGS(fail_pipeline, pip_id); -} - -SOKOL_API_IMPL void sg_fail_pass(sg_pass pass_id) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(pass_id.id != SG_INVALID_ID); - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); - pass->slot.ctx_id = _sg.active_context.id; - pass->slot.state = SG_RESOURCESTATE_FAILED; - _SG_TRACE_ARGS(fail_pass, pass_id); -} - -/*-- get resource state */ -SOKOL_API_IMPL sg_resource_state sg_query_buffer_state(sg_buffer buf_id) { - SOKOL_ASSERT(_sg.valid); - _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); - sg_resource_state res = buf ? buf->slot.state : SG_RESOURCESTATE_INVALID; - return res; -} - -SOKOL_API_IMPL sg_resource_state sg_query_image_state(sg_image img_id) { - SOKOL_ASSERT(_sg.valid); - _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); - sg_resource_state res = img ? img->slot.state : SG_RESOURCESTATE_INVALID; - return res; -} - -SOKOL_API_IMPL sg_resource_state sg_query_shader_state(sg_shader shd_id) { - SOKOL_ASSERT(_sg.valid); - _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); - sg_resource_state res = shd ? shd->slot.state : SG_RESOURCESTATE_INVALID; - return res; -} - -SOKOL_API_IMPL sg_resource_state sg_query_pipeline_state(sg_pipeline pip_id) { - SOKOL_ASSERT(_sg.valid); - _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); - sg_resource_state res = pip ? pip->slot.state : SG_RESOURCESTATE_INVALID; - return res; -} - -SOKOL_API_IMPL sg_resource_state sg_query_pass_state(sg_pass pass_id) { - SOKOL_ASSERT(_sg.valid); - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - sg_resource_state res = pass ? pass->slot.state : SG_RESOURCESTATE_INVALID; - return res; -} - -/*-- allocate and initialize resource ----------------------------------------*/ -SOKOL_API_IMPL sg_buffer sg_make_buffer(const sg_buffer_desc* desc) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(desc); - sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc); - sg_buffer buf_id = _sg_alloc_buffer(); - if (buf_id.id != SG_INVALID_ID) { - _sg_init_buffer(buf_id, &desc_def); - } - else { - SOKOL_LOG("buffer pool exhausted!"); - _SG_TRACE_NOARGS(err_buffer_pool_exhausted); - } - _SG_TRACE_ARGS(make_buffer, &desc_def, buf_id); - return buf_id; -} - -SOKOL_API_IMPL sg_image sg_make_image(const sg_image_desc* desc) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(desc); - sg_image_desc desc_def = _sg_image_desc_defaults(desc); - sg_image img_id = _sg_alloc_image(); - if (img_id.id != SG_INVALID_ID) { - _sg_init_image(img_id, &desc_def); - } - else { - SOKOL_LOG("image pool exhausted!"); - _SG_TRACE_NOARGS(err_image_pool_exhausted); - } - _SG_TRACE_ARGS(make_image, &desc_def, img_id); - return img_id; -} - -SOKOL_API_IMPL sg_shader sg_make_shader(const sg_shader_desc* desc) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(desc); - sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); - sg_shader shd_id = _sg_alloc_shader(); - if (shd_id.id != SG_INVALID_ID) { - _sg_init_shader(shd_id, &desc_def); - } - else { - SOKOL_LOG("shader pool exhausted!"); - _SG_TRACE_NOARGS(err_shader_pool_exhausted); - } - _SG_TRACE_ARGS(make_shader, &desc_def, shd_id); - return shd_id; -} - -SOKOL_API_IMPL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(desc); - sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc); - sg_pipeline pip_id = _sg_alloc_pipeline(); - if (pip_id.id != SG_INVALID_ID) { - _sg_init_pipeline(pip_id, &desc_def); - } - else { - SOKOL_LOG("pipeline pool exhausted!"); - _SG_TRACE_NOARGS(err_pipeline_pool_exhausted); - } - _SG_TRACE_ARGS(make_pipeline, &desc_def, pip_id); - return pip_id; -} - -SOKOL_API_IMPL sg_pass sg_make_pass(const sg_pass_desc* desc) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(desc); - sg_pass_desc desc_def = _sg_pass_desc_defaults(desc); - sg_pass pass_id = _sg_alloc_pass(); - if (pass_id.id != SG_INVALID_ID) { - _sg_init_pass(pass_id, &desc_def); - } - else { - SOKOL_LOG("pass pool exhausted!"); - _SG_TRACE_NOARGS(err_pass_pool_exhausted); - } - _SG_TRACE_ARGS(make_pass, &desc_def, pass_id); - return pass_id; -} - -/*-- destroy resource --------------------------------------------------------*/ -SOKOL_API_IMPL void sg_destroy_buffer(sg_buffer buf_id) { - SOKOL_ASSERT(_sg.valid); - _SG_TRACE_ARGS(destroy_buffer, buf_id); - if (_sg_uninit_buffer(buf_id)) { - _sg_dealloc_buffer(buf_id); - } -} - -SOKOL_API_IMPL void sg_destroy_image(sg_image img_id) { - SOKOL_ASSERT(_sg.valid); - _SG_TRACE_ARGS(destroy_image, img_id); - if (_sg_uninit_image(img_id)) { - _sg_dealloc_image(img_id); - } -} - -SOKOL_API_IMPL void sg_destroy_shader(sg_shader shd_id) { - SOKOL_ASSERT(_sg.valid); - _SG_TRACE_ARGS(destroy_shader, shd_id); - if (_sg_uninit_shader(shd_id)) { - _sg_dealloc_shader(shd_id); - } -} - -SOKOL_API_IMPL void sg_destroy_pipeline(sg_pipeline pip_id) { - SOKOL_ASSERT(_sg.valid); - _SG_TRACE_ARGS(destroy_pipeline, pip_id); - if (_sg_uninit_pipeline(pip_id)) { - _sg_dealloc_pipeline(pip_id); - } -} - -SOKOL_API_IMPL void sg_destroy_pass(sg_pass pass_id) { - SOKOL_ASSERT(_sg.valid); - _SG_TRACE_ARGS(destroy_pass, pass_id); - if (_sg_uninit_pass(pass_id)) { - _sg_dealloc_pass(pass_id); - } -} - -SOKOL_API_IMPL void sg_begin_default_pass(const sg_pass_action* pass_action, int width, int height) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(pass_action); - SOKOL_ASSERT((pass_action->_start_canary == 0) && (pass_action->_end_canary == 0)); - sg_pass_action pa; - _sg_resolve_default_pass_action(pass_action, &pa); - _sg.cur_pass.id = SG_INVALID_ID; - _sg.pass_valid = true; - _sg_begin_pass(0, &pa, width, height); - _SG_TRACE_ARGS(begin_default_pass, pass_action, width, height); -} - -SOKOL_API_IMPL void sg_begin_default_passf(const sg_pass_action* pass_action, float width, float height) { - sg_begin_default_pass(pass_action, (int)width, (int)height); -} - -SOKOL_API_IMPL void sg_begin_pass(sg_pass pass_id, const sg_pass_action* pass_action) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(pass_action); - SOKOL_ASSERT((pass_action->_start_canary == 0) && (pass_action->_end_canary == 0)); - _sg.cur_pass = pass_id; - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - if (pass && _sg_validate_begin_pass(pass)) { - _sg.pass_valid = true; - sg_pass_action pa; - _sg_resolve_default_pass_action(pass_action, &pa); - const _sg_image_t* img = _sg_pass_color_image(pass, 0); - SOKOL_ASSERT(img); - const int w = img->cmn.width; - const int h = img->cmn.height; - _sg_begin_pass(pass, &pa, w, h); - _SG_TRACE_ARGS(begin_pass, pass_id, pass_action); - } - else { - _sg.pass_valid = false; - _SG_TRACE_NOARGS(err_pass_invalid); - } -} - -SOKOL_API_IMPL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) { - SOKOL_ASSERT(_sg.valid); - if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); - return; - } - _sg_apply_viewport(x, y, width, height, origin_top_left); - _SG_TRACE_ARGS(apply_viewport, x, y, width, height, origin_top_left); -} - -SOKOL_API_IMPL void sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) { - sg_apply_viewport((int)x, (int)y, (int)width, (int)height, origin_top_left); -} - -SOKOL_API_IMPL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) { - SOKOL_ASSERT(_sg.valid); - if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); - return; - } - _sg_apply_scissor_rect(x, y, width, height, origin_top_left); - _SG_TRACE_ARGS(apply_scissor_rect, x, y, width, height, origin_top_left); -} - -SOKOL_API_IMPL void sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left) { - sg_apply_scissor_rect((int)x, (int)y, (int)width, (int)height, origin_top_left); -} - -SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { - SOKOL_ASSERT(_sg.valid); - _sg.bindings_valid = false; - if (!_sg_validate_apply_pipeline(pip_id)) { - _sg.next_draw_valid = false; - _SG_TRACE_NOARGS(err_draw_invalid); - return; - } - if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); - return; - } - _sg.cur_pipeline = pip_id; - _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); - SOKOL_ASSERT(pip); - _sg.next_draw_valid = (SG_RESOURCESTATE_VALID == pip->slot.state); - SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->cmn.shader_id.id)); - _sg_apply_pipeline(pip); - _SG_TRACE_ARGS(apply_pipeline, pip_id); -} - -SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(bindings); - SOKOL_ASSERT((bindings->_start_canary == 0) && (bindings->_end_canary==0)); - if (!_sg_validate_apply_bindings(bindings)) { - _sg.next_draw_valid = false; - _SG_TRACE_NOARGS(err_draw_invalid); - return; - } - _sg.bindings_valid = true; - - _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); - SOKOL_ASSERT(pip); - - _sg_buffer_t* vbs[SG_MAX_SHADERSTAGE_BUFFERS] = { 0 }; - int num_vbs = 0; - for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++, num_vbs++) { - if (bindings->vertex_buffers[i].id) { - vbs[i] = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); - SOKOL_ASSERT(vbs[i]); - _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vbs[i]->slot.state); - _sg.next_draw_valid &= !vbs[i]->cmn.append_overflow; - } - else { - break; - } - } - - _sg_buffer_t* ib = 0; - if (bindings->index_buffer.id) { - ib = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); - SOKOL_ASSERT(ib); - _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == ib->slot.state); - _sg.next_draw_valid &= !ib->cmn.append_overflow; - } - - _sg_image_t* vs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; - int num_vs_imgs = 0; - for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_vs_imgs++) { - if (bindings->vs_images[i].id) { - vs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->vs_images[i].id); - SOKOL_ASSERT(vs_imgs[i]); - _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vs_imgs[i]->slot.state); - } - else { - break; - } - } - - _sg_image_t* fs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; - int num_fs_imgs = 0; - for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_fs_imgs++) { - if (bindings->fs_images[i].id) { - fs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->fs_images[i].id); - SOKOL_ASSERT(fs_imgs[i]); - _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == fs_imgs[i]->slot.state); - } - else { - break; - } - } - if (_sg.next_draw_valid) { - const int* vb_offsets = bindings->vertex_buffer_offsets; - int ib_offset = bindings->index_buffer_offset; - _sg_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); - _SG_TRACE_ARGS(apply_bindings, bindings); - } - else { - _SG_TRACE_NOARGS(err_draw_invalid); - } -} - -SOKOL_API_IMPL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT((stage == SG_SHADERSTAGE_VS) || (stage == SG_SHADERSTAGE_FS)); - SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); - SOKOL_ASSERT(data && data->ptr && (data->size > 0)); - if (!_sg_validate_apply_uniforms(stage, ub_index, data)) { - _sg.next_draw_valid = false; - _SG_TRACE_NOARGS(err_draw_invalid); - return; - } - if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); - return; - } - if (!_sg.next_draw_valid) { - _SG_TRACE_NOARGS(err_draw_invalid); - return; - } - _sg_apply_uniforms(stage, ub_index, data); - _SG_TRACE_ARGS(apply_uniforms, stage, ub_index, data); -} - -SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instances) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(base_element >= 0); - SOKOL_ASSERT(num_elements >= 0); - SOKOL_ASSERT(num_instances >= 0); - #if defined(SOKOL_DEBUG) - if (!_sg.bindings_valid) { - SOKOL_LOG("attempting to draw without resource bindings"); - } - #endif - if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); - return; - } - if (!_sg.next_draw_valid) { - _SG_TRACE_NOARGS(err_draw_invalid); - return; - } - if (!_sg.bindings_valid) { - _SG_TRACE_NOARGS(err_bindings_invalid); - return; - } - /* attempting to draw with zero elements or instances is not technically an - error, but might be handled as an error in the backend API (e.g. on Metal) - */ - if ((0 == num_elements) || (0 == num_instances)) { - _SG_TRACE_NOARGS(err_draw_invalid); - return; - } - _sg_draw(base_element, num_elements, num_instances); - _SG_TRACE_ARGS(draw, base_element, num_elements, num_instances); -} - -SOKOL_API_IMPL void sg_end_pass(void) { - SOKOL_ASSERT(_sg.valid); - if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); - return; - } - _sg_end_pass(); - _sg.cur_pass.id = SG_INVALID_ID; - _sg.cur_pipeline.id = SG_INVALID_ID; - _sg.pass_valid = false; - _SG_TRACE_NOARGS(end_pass); -} - -SOKOL_API_IMPL void sg_commit(void) { - SOKOL_ASSERT(_sg.valid); - _sg_commit(); - _SG_TRACE_NOARGS(commit); - _sg.frame_index++; -} - -SOKOL_API_IMPL void sg_reset_state_cache(void) { - SOKOL_ASSERT(_sg.valid); - _sg_reset_state_cache(); - _SG_TRACE_NOARGS(reset_state_cache); -} - -SOKOL_API_IMPL void sg_update_buffer(sg_buffer buf_id, const sg_range* data) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(data && data->ptr && (data->size > 0)); - _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); - if ((data->size > 0) && buf && (buf->slot.state == SG_RESOURCESTATE_VALID)) { - if (_sg_validate_update_buffer(buf, data)) { - SOKOL_ASSERT(data->size <= (size_t)buf->cmn.size); - /* only one update allowed per buffer and frame */ - SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); - /* update and append on same buffer in same frame not allowed */ - SOKOL_ASSERT(buf->cmn.append_frame_index != _sg.frame_index); - _sg_update_buffer(buf, data); - buf->cmn.update_frame_index = _sg.frame_index; - } - } - _SG_TRACE_ARGS(update_buffer, buf_id, data); -} - -SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const sg_range* data) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(data && data->ptr); - _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); - int result; - if (buf) { - /* rewind append cursor in a new frame */ - if (buf->cmn.append_frame_index != _sg.frame_index) { - buf->cmn.append_pos = 0; - buf->cmn.append_overflow = false; - } - if ((buf->cmn.append_pos + _sg_roundup((int)data->size, 4)) > buf->cmn.size) { - buf->cmn.append_overflow = true; - } - const int start_pos = buf->cmn.append_pos; - if (buf->slot.state == SG_RESOURCESTATE_VALID) { - if (_sg_validate_append_buffer(buf, data)) { - if (!buf->cmn.append_overflow && (data->size > 0)) { - /* update and append on same buffer in same frame not allowed */ - SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); - int copied_num_bytes = _sg_append_buffer(buf, data, buf->cmn.append_frame_index != _sg.frame_index); - buf->cmn.append_pos += copied_num_bytes; - buf->cmn.append_frame_index = _sg.frame_index; - } - } - } - result = start_pos; - } - else { - /* FIXME: should we return -1 here? */ - result = 0; - } - _SG_TRACE_ARGS(append_buffer, buf_id, data, result); - return result; -} - -SOKOL_API_IMPL bool sg_query_buffer_overflow(sg_buffer buf_id) { - SOKOL_ASSERT(_sg.valid); - _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); - bool result = buf ? buf->cmn.append_overflow : false; - return result; -} - -SOKOL_API_IMPL void sg_update_image(sg_image img_id, const sg_image_data* data) { - SOKOL_ASSERT(_sg.valid); - _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); - if (img && img->slot.state == SG_RESOURCESTATE_VALID) { - if (_sg_validate_update_image(img, data)) { - SOKOL_ASSERT(img->cmn.upd_frame_index != _sg.frame_index); - _sg_update_image(img, data); - img->cmn.upd_frame_index = _sg.frame_index; - } - } - _SG_TRACE_ARGS(update_image, img_id, data); -} - -SOKOL_API_IMPL void sg_push_debug_group(const char* name) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(name); - _SOKOL_UNUSED(name); - _SG_TRACE_ARGS(push_debug_group, name); -} - -SOKOL_API_IMPL void sg_pop_debug_group(void) { - SOKOL_ASSERT(_sg.valid); - _SG_TRACE_NOARGS(pop_debug_group); -} - -SOKOL_API_IMPL sg_buffer_info sg_query_buffer_info(sg_buffer buf_id) { - SOKOL_ASSERT(_sg.valid); - sg_buffer_info info; - memset(&info, 0, sizeof(info)); - const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); - if (buf) { - info.slot.state = buf->slot.state; - info.slot.res_id = buf->slot.id; - info.slot.ctx_id = buf->slot.ctx_id; - info.update_frame_index = buf->cmn.update_frame_index; - info.append_frame_index = buf->cmn.append_frame_index; - info.append_pos = buf->cmn.append_pos; - info.append_overflow = buf->cmn.append_overflow; - #if defined(SOKOL_D3D11) - info.num_slots = 1; - info.active_slot = 0; - #else - info.num_slots = buf->cmn.num_slots; - info.active_slot = buf->cmn.active_slot; - #endif - } - return info; -} - -SOKOL_API_IMPL sg_image_info sg_query_image_info(sg_image img_id) { - SOKOL_ASSERT(_sg.valid); - sg_image_info info; - memset(&info, 0, sizeof(info)); - const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); - if (img) { - info.slot.state = img->slot.state; - info.slot.res_id = img->slot.id; - info.slot.ctx_id = img->slot.ctx_id; - info.upd_frame_index = img->cmn.upd_frame_index; - #if defined(SOKOL_D3D11) - info.num_slots = 1; - info.active_slot = 0; - #else - info.num_slots = img->cmn.num_slots; - info.active_slot = img->cmn.active_slot; - #endif - info.width = img->cmn.width; - info.height = img->cmn.height; - } - return info; -} - -SOKOL_API_IMPL sg_shader_info sg_query_shader_info(sg_shader shd_id) { - SOKOL_ASSERT(_sg.valid); - sg_shader_info info; - memset(&info, 0, sizeof(info)); - const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); - if (shd) { - info.slot.state = shd->slot.state; - info.slot.res_id = shd->slot.id; - info.slot.ctx_id = shd->slot.ctx_id; - } - return info; -} - -SOKOL_API_IMPL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip_id) { - SOKOL_ASSERT(_sg.valid); - sg_pipeline_info info; - memset(&info, 0, sizeof(info)); - const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); - if (pip) { - info.slot.state = pip->slot.state; - info.slot.res_id = pip->slot.id; - info.slot.ctx_id = pip->slot.ctx_id; - } - return info; -} - -SOKOL_API_IMPL sg_pass_info sg_query_pass_info(sg_pass pass_id) { - SOKOL_ASSERT(_sg.valid); - sg_pass_info info; - memset(&info, 0, sizeof(info)); - const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - if (pass) { - info.slot.state = pass->slot.state; - info.slot.res_id = pass->slot.id; - info.slot.ctx_id = pass->slot.ctx_id; - } - return info; -} - -SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) { - SOKOL_ASSERT(_sg.valid && desc); - return _sg_buffer_desc_defaults(desc); -} - -SOKOL_API_IMPL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) { - SOKOL_ASSERT(_sg.valid && desc); - return _sg_image_desc_defaults(desc); -} - -SOKOL_API_IMPL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) { - SOKOL_ASSERT(_sg.valid && desc); - return _sg_shader_desc_defaults(desc); -} - -SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) { - SOKOL_ASSERT(_sg.valid && desc); - return _sg_pipeline_desc_defaults(desc); -} - -SOKOL_API_IMPL sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc) { - SOKOL_ASSERT(_sg.valid && desc); - return _sg_pass_desc_defaults(desc); -} - -SOKOL_API_IMPL const void* sg_d3d11_device(void) { -#if defined(SOKOL_D3D11) - return (const void*) _sg.d3d11.dev; -#else - return 0; -#endif -} - -SOKOL_API_IMPL const void* sg_mtl_device(void) { -#if defined(SOKOL_METAL) - if (nil != _sg.mtl.device) { - return (__bridge const void*) _sg.mtl.device; - } - else { - return 0; - } -#else - return 0; -#endif -} - -SOKOL_API_IMPL const void* sg_mtl_render_command_encoder(void) { - #if defined(SOKOL_METAL) - if (nil != _sg.mtl.cmd_encoder) { - return (__bridge const void*) _sg.mtl.cmd_encoder; - } - else { - return 0; - } - #else - return 0; - #endif -} - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#endif /* SOKOL_GFX_IMPL */ diff --git a/contrib/sokol/sokol_glue.h b/contrib/sokol/sokol_glue.h deleted file mode 100644 index 74ecb7c..0000000 --- a/contrib/sokol/sokol_glue.h +++ /dev/null @@ -1,137 +0,0 @@ -#if defined(SOKOL_IMPL) && !defined(SOKOL_GLUE_IMPL) -#define SOKOL_GLUE_IMPL -#endif -#ifndef SOKOL_GLUE_INCLUDED -/* - sokol_glue.h -- glue helper functions for sokol headers - - Project URL: https://github.com/floooh/sokol - - Do this: - #define SOKOL_IMPL or - #define SOKOL_GLUE_IMPL - before you include this file in *one* C or C++ file to create the - implementation. - - ...optionally provide the following macros to override defaults: - - SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) - SOKOL_GLUE_API_DECL - public function declaration prefix (default: extern) - SOKOL_API_DECL - same as SOKOL_GLUE_API_DECL - SOKOL_API_IMPL - public function implementation prefix (default: -) - - If sokol_glue.h is compiled as a DLL, define the following before - including the declaration or implementation: - - SOKOL_DLL - - On Windows, SOKOL_DLL will define SOKOL_GLUE_API_DECL as __declspec(dllexport) - or __declspec(dllimport) as needed. - - OVERVIEW - ======== - The sokol core headers should not depend on each other, but sometimes - it's useful to have a set of helper functions as "glue" between - two or more sokol headers. - - This is what sokol_glue.h is for. Simply include the header after other - sokol headers (both for the implementation and declaration), and - depending on what headers have been included before, sokol_glue.h - will make available "glue functions". - - PROVIDED FUNCTIONS - ================== - - - if sokol_app.h and sokol_gfx.h is included: - - sg_context_desc sapp_sgcontext(void): - - Returns an initialized sg_context_desc function initialized - by calling sokol_app.h functions. - - LICENSE - ======= - zlib/libpng license - - Copyright (c) 2018 Andre Weissflog - - 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. -*/ -#define SOKOL_GLUE_INCLUDED - -#if defined(SOKOL_API_DECL) && !defined(SOKOL_GLUE_API_DECL) -#define SOKOL_GLUE_API_DECL SOKOL_API_DECL -#endif -#ifndef SOKOL_GLUE_API_DECL -#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GLUE_IMPL) -#define SOKOL_GLUE_API_DECL __declspec(dllexport) -#elif defined(_WIN32) && defined(SOKOL_DLL) -#define SOKOL_GLUE_API_DECL __declspec(dllimport) -#else -#define SOKOL_GLUE_API_DECL extern -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(SOKOL_GFX_INCLUDED) && defined(SOKOL_APP_INCLUDED) -SOKOL_GLUE_API_DECL sg_context_desc sapp_sgcontext(void); -#endif - -#ifdef __cplusplus -} /* extern "C" */ -#endif -#endif /* SOKOL_GLUE_INCLUDED */ - -/*-- IMPLEMENTATION ----------------------------------------------------------*/ -#ifdef SOKOL_GLUE_IMPL -#define SOKOL_GLUE_IMPL_INCLUDED (1) -#include /* memset */ - -#ifndef SOKOL_API_IMPL - #define SOKOL_API_IMPL -#endif - -#if defined(SOKOL_GFX_INCLUDED) && defined(SOKOL_APP_INCLUDED) -SOKOL_API_IMPL sg_context_desc sapp_sgcontext(void) { - sg_context_desc desc; - memset(&desc, 0, sizeof(desc)); - desc.color_format = (sg_pixel_format) sapp_color_format(); - desc.depth_format = (sg_pixel_format) sapp_depth_format(); - desc.sample_count = sapp_sample_count(); - desc.gl.force_gles2 = sapp_gles2(); - desc.metal.device = sapp_metal_get_device(); - desc.metal.renderpass_descriptor_cb = sapp_metal_get_renderpass_descriptor; - desc.metal.drawable_cb = sapp_metal_get_drawable; - desc.d3d11.device = sapp_d3d11_get_device(); - desc.d3d11.device_context = sapp_d3d11_get_device_context(); - desc.d3d11.render_target_view_cb = sapp_d3d11_get_render_target_view; - desc.d3d11.depth_stencil_view_cb = sapp_d3d11_get_depth_stencil_view; - desc.wgpu.device = sapp_wgpu_get_device(); - desc.wgpu.render_view_cb = sapp_wgpu_get_render_view; - desc.wgpu.resolve_view_cb = sapp_wgpu_get_resolve_view; - desc.wgpu.depth_stencil_view_cb = sapp_wgpu_get_depth_stencil_view; - return desc; -} -#endif - -#endif /* SOKOL_GLUE_IMPL */ diff --git a/contrib/sokol/sokol_time.h b/contrib/sokol/sokol_time.h deleted file mode 100644 index ba4651a..0000000 --- a/contrib/sokol/sokol_time.h +++ /dev/null @@ -1,323 +0,0 @@ -#if defined(SOKOL_IMPL) && !defined(SOKOL_TIME_IMPL) -#define SOKOL_TIME_IMPL -#endif -#ifndef SOKOL_TIME_INCLUDED -/* - sokol_time.h -- simple cross-platform time measurement - - Project URL: https://github.com/floooh/sokol - - Do this: - #define SOKOL_IMPL or - #define SOKOL_TIME_IMPL - before you include this file in *one* C or C++ file to create the - implementation. - - Optionally provide the following defines with your own implementations: - SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) - SOKOL_TIME_API_DECL - public function declaration prefix (default: extern) - SOKOL_API_DECL - same as SOKOL_TIME_API_DECL - SOKOL_API_IMPL - public function implementation prefix (default: -) - - If sokol_time.h is compiled as a DLL, define the following before - including the declaration or implementation: - - SOKOL_DLL - - On Windows, SOKOL_DLL will define SOKOL_TIME_API_DECL as __declspec(dllexport) - or __declspec(dllimport) as needed. - - void stm_setup(); - Call once before any other functions to initialize sokol_time - (this calls for instance QueryPerformanceFrequency on Windows) - - uint64_t stm_now(); - Get current point in time in unspecified 'ticks'. The value that - is returned has no relation to the 'wall-clock' time and is - not in a specific time unit, it is only useful to compute - time differences. - - uint64_t stm_diff(uint64_t new, uint64_t old); - Computes the time difference between new and old. This will always - return a positive, non-zero value. - - uint64_t stm_since(uint64_t start); - Takes the current time, and returns the elapsed time since start - (this is a shortcut for "stm_diff(stm_now(), start)") - - uint64_t stm_laptime(uint64_t* last_time); - This is useful for measuring frame time and other recurring - events. It takes the current time, returns the time difference - to the value in last_time, and stores the current time in - last_time for the next call. If the value in last_time is 0, - the return value will be zero (this usually happens on the - very first call). - - uint64_t stm_round_to_common_refresh_rate(uint64_t duration) - This oddly named function takes a measured frame time and - returns the closest "nearby" common display refresh rate frame duration - in ticks. If the input duration isn't close to any common display - refresh rate, the input duration will be returned unchanged as a fallback. - The main purpose of this function is to remove jitter/inaccuracies from - measured frame times, and instead use the display refresh rate as - frame duration. - - Use the following functions to convert a duration in ticks into - useful time units: - - double stm_sec(uint64_t ticks); - double stm_ms(uint64_t ticks); - double stm_us(uint64_t ticks); - double stm_ns(uint64_t ticks); - Converts a tick value into seconds, milliseconds, microseconds - or nanoseconds. Note that not all platforms will have nanosecond - or even microsecond precision. - - Uses the following time measurement functions under the hood: - - Windows: QueryPerformanceFrequency() / QueryPerformanceCounter() - MacOS/iOS: mach_absolute_time() - emscripten: performance.now() - Linux+others: clock_gettime(CLOCK_MONOTONIC) - - zlib/libpng license - - Copyright (c) 2018 Andre Weissflog - - 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. -*/ -#define SOKOL_TIME_INCLUDED (1) -#include - -#if defined(SOKOL_API_DECL) && !defined(SOKOL_TIME_API_DECL) -#define SOKOL_TIME_API_DECL SOKOL_API_DECL -#endif -#ifndef SOKOL_TIME_API_DECL -#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_TIME_IMPL) -#define SOKOL_TIME_API_DECL __declspec(dllexport) -#elif defined(_WIN32) && defined(SOKOL_DLL) -#define SOKOL_TIME_API_DECL __declspec(dllimport) -#else -#define SOKOL_TIME_API_DECL extern -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -SOKOL_TIME_API_DECL void stm_setup(void); -SOKOL_TIME_API_DECL uint64_t stm_now(void); -SOKOL_TIME_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks); -SOKOL_TIME_API_DECL uint64_t stm_since(uint64_t start_ticks); -SOKOL_TIME_API_DECL uint64_t stm_laptime(uint64_t* last_time); -SOKOL_TIME_API_DECL uint64_t stm_round_to_common_refresh_rate(uint64_t frame_ticks); -SOKOL_TIME_API_DECL double stm_sec(uint64_t ticks); -SOKOL_TIME_API_DECL double stm_ms(uint64_t ticks); -SOKOL_TIME_API_DECL double stm_us(uint64_t ticks); -SOKOL_TIME_API_DECL double stm_ns(uint64_t ticks); - -#ifdef __cplusplus -} /* extern "C" */ -#endif -#endif // SOKOL_TIME_INCLUDED - -/*-- IMPLEMENTATION ----------------------------------------------------------*/ -#ifdef SOKOL_TIME_IMPL -#define SOKOL_TIME_IMPL_INCLUDED (1) -#include /* memset */ - -#ifndef SOKOL_API_IMPL - #define SOKOL_API_IMPL -#endif -#ifndef SOKOL_ASSERT - #include - #define SOKOL_ASSERT(c) assert(c) -#endif -#ifndef _SOKOL_PRIVATE - #if defined(__GNUC__) || defined(__clang__) - #define _SOKOL_PRIVATE __attribute__((unused)) static - #else - #define _SOKOL_PRIVATE static - #endif -#endif - -#if defined(_WIN32) -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -typedef struct { - uint32_t initialized; - LARGE_INTEGER freq; - LARGE_INTEGER start; -} _stm_state_t; -#elif defined(__APPLE__) && defined(__MACH__) -#include -typedef struct { - uint32_t initialized; - mach_timebase_info_data_t timebase; - uint64_t start; -} _stm_state_t; -#elif defined(__EMSCRIPTEN__) -#include -typedef struct { - uint32_t initialized; - double start; -} _stm_state_t; -#else /* anything else, this will need more care for non-Linux platforms */ -#ifdef ESP8266 -// On the ESP8266, clock_gettime ignores the first argument and CLOCK_MONOTONIC isn't defined -#define CLOCK_MONOTONIC 0 -#endif -#include -typedef struct { - uint32_t initialized; - uint64_t start; -} _stm_state_t; -#endif -static _stm_state_t _stm; - -/* prevent 64-bit overflow when computing relative timestamp - see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3 -*/ -#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) -_SOKOL_PRIVATE int64_t int64_muldiv(int64_t value, int64_t numer, int64_t denom) { - int64_t q = value / denom; - int64_t r = value % denom; - return q * numer + r * numer / denom; -} -#endif - -#if defined(__EMSCRIPTEN__) -EM_JS(double, stm_js_perfnow, (void), { - return performance.now(); -}); -#endif - -SOKOL_API_IMPL void stm_setup(void) { - memset(&_stm, 0, sizeof(_stm)); - _stm.initialized = 0xABCDABCD; - #if defined(_WIN32) - QueryPerformanceFrequency(&_stm.freq); - QueryPerformanceCounter(&_stm.start); - #elif defined(__APPLE__) && defined(__MACH__) - mach_timebase_info(&_stm.timebase); - _stm.start = mach_absolute_time(); - #elif defined(__EMSCRIPTEN__) - _stm.start = stm_js_perfnow(); - #else - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - _stm.start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec; - #endif -} - -SOKOL_API_IMPL uint64_t stm_now(void) { - SOKOL_ASSERT(_stm.initialized == 0xABCDABCD); - uint64_t now; - #if defined(_WIN32) - LARGE_INTEGER qpc_t; - QueryPerformanceCounter(&qpc_t); - now = (uint64_t) int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart); - #elif defined(__APPLE__) && defined(__MACH__) - const uint64_t mach_now = mach_absolute_time() - _stm.start; - now = (uint64_t) int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom); - #elif defined(__EMSCRIPTEN__) - double js_now = stm_js_perfnow() - _stm.start; - SOKOL_ASSERT(js_now >= 0.0); - now = (uint64_t) (js_now * 1000000.0); - #else - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm.start; - #endif - return now; -} - -SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) { - if (new_ticks > old_ticks) { - return new_ticks - old_ticks; - } - else { - return 1; - } -} - -SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) { - return stm_diff(stm_now(), start_ticks); -} - -SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) { - SOKOL_ASSERT(last_time); - uint64_t dt = 0; - uint64_t now = stm_now(); - if (0 != *last_time) { - dt = stm_diff(now, *last_time); - } - *last_time = now; - return dt; -} - -// first number is frame duration in ns, second number is tolerance in ns, -// the resulting min/max values must not overlap! -static const uint64_t _stm_refresh_rates[][2] = { - { 16666667, 1000000 }, // 60 Hz: 16.6667 +- 1ms - { 13888889, 250000 }, // 72 Hz: 13.8889 +- 0.25ms - { 13333333, 250000 }, // 75 Hz: 13.3333 +- 0.25ms - { 11764706, 250000 }, // 85 Hz: 11.7647 +- 0.25 - { 11111111, 250000 }, // 90 Hz: 11.1111 +- 0.25ms - { 10000000, 500000 }, // 100 Hz: 10.0000 +- 0.5ms - { 8333333, 500000 }, // 120 Hz: 8.3333 +- 0.5ms - { 6944445, 500000 }, // 144 Hz: 6.9445 +- 0.5ms - { 4166667, 1000000 }, // 240 Hz: 4.1666 +- 1ms - { 0, 0 }, // keep the last element always at zero -}; - -SOKOL_API_IMPL uint64_t stm_round_to_common_refresh_rate(uint64_t ticks) { - uint64_t ns; - int i = 0; - while (0 != (ns = _stm_refresh_rates[i][0])) { - uint64_t tol = _stm_refresh_rates[i][1]; - if ((ticks > (ns - tol)) && (ticks < (ns + tol))) { - return ns; - } - i++; - } - // fallthough: didn't fit into any buckets - return ticks; -} - -SOKOL_API_IMPL double stm_sec(uint64_t ticks) { - return (double)ticks / 1000000000.0; -} - -SOKOL_API_IMPL double stm_ms(uint64_t ticks) { - return (double)ticks / 1000000.0; -} - -SOKOL_API_IMPL double stm_us(uint64_t ticks) { - return (double)ticks / 1000.0; -} - -SOKOL_API_IMPL double stm_ns(uint64_t ticks) { - return (double)ticks; -} -#endif /* SOKOL_TIME_IMPL */ diff --git a/src/AssimpViewerApp.h b/src/AssimpViewerApp.h deleted file mode 100644 index 886d95c..0000000 --- a/src/AssimpViewerApp.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "SceneData.h" - -struct SDL_Window; - -namespace AssimpViewer { - -class AssimpViewerApp { -public: - AssimpViewerApp(int argc, char *argv[]); - ~AssimpViewerApp(); - const aiScene *importAssimp(const std::string &path); - const aiScene *getScene() const; - -private: - std::string mTitle; - SDL_Window *mWindow; - SceneData mSceneData; -}; - -inline const aiScene *AssimpViewerApp::getScene() const { - return mSceneData.mScene; -} - -} // namespace AssimpViewer diff --git a/src/MainRenderView.cpp b/src/MainRenderView.cpp deleted file mode 100644 index 0634da5..0000000 --- a/src/MainRenderView.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/*----------------------------------------------------------------------------------------------- -The MIT License (MIT) - -Copyright (c) 2015-2023 OSRE ( Open Source Render Engine ) by Kim Kulling - -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 "MainRenderView.h" - -namespace AssimpViewer { - - - -MainRenderView::MainRenderView() { - // empty -} - -MainRenderView::~MainRenderView() { -} - - -} // namespace Editor diff --git a/src/MainRenderView.h b/src/MainRenderView.h deleted file mode 100644 index 4d8bcf7..0000000 --- a/src/MainRenderView.h +++ /dev/null @@ -1,58 +0,0 @@ -/*----------------------------------------------------------------------------------------------- -The MIT License (MIT) - -Copyright (c) 2015-2023 OSRE ( Open Source Render Engine ) by Kim Kulling - -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 - -namespace OSRE { - -namespace RenderBackend { - class Mesh; -} - -namespace App { - class RenderComponent; -} - -namespace Animation { - struct Skeleton; -} - -} // namespace OSRE - -namespace AssimpViewer { - -//------------------------------------------------------------------------------------------------- -/// @ingroup Editor -/// -/// @brief -//------------------------------------------------------------------------------------------------- -class MainRenderView { -public: - MainRenderView(); - ~MainRenderView(); - -private: -}; - -} // namespace AssimpViewer diff --git a/src/ModelLoading.cpp b/src/ModelLoading.cpp new file mode 100644 index 0000000..60f36b4 --- /dev/null +++ b/src/ModelLoading.cpp @@ -0,0 +1,200 @@ +/*----------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2015-2024 OSRE ( Open Source Render Engine ) by Kim Kulling + +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 "App/App.h" +#include "App/Scene.h" +#include "IO/Uri.h" +#include "Common/BaseMath.h" +#include "Platform/AbstractWindow.h" +#include "Platform/PlatformOperations.h" +#include "Properties/Settings.h" +#include "RenderBackend/RenderBackendService.h" +#include "RenderBackend/RenderCommon.h" + +#include + +using namespace ::OSRE; +using namespace ::OSRE::App; +using namespace ::OSRE::Common; +using namespace ::OSRE::RenderBackend; + +/// @brief The log-tag +static constexpr c8 Tag[] = "ModelLoadingApp"; + +//------------------------------------------------------------------------------------------------- +/// @ingroup Editor +/// +/// @brief +//------------------------------------------------------------------------------------------------- + +/// @brief The example application, will create the renderer and loads a model. +class ModelLoadingApp : public App::AppBase { + String mAssetFolder; ///< The asset folder, here we will locate our assets. + App::CameraComponent *mCamera; ///< The camera component. + TransformMatrixBlock mTransformMatrix; ///< The tansform block. + TransformComponent::NodePtr mModelNode; ///< The mode node. + int mIntention; + +public: + ModelLoadingApp(int argc, char *argv[]) : + AppBase(argc, (const char **)argv, "api", "The render API"), + mAssetFolder(), + mCamera(nullptr), + mTransformMatrix(), + mModelNode(), + mIntention(0) { + // empty + } + + ~ModelLoadingApp() override = default; + + bool hasModel() const { + return mModelNode.isValid(); + } + + void pushIntention() { + mIntention++; + } + + void popIntention() { + mIntention--; + } + + void checkName(String &name) { + if (name.empty()) { + name = "No Name"; + } + } + + void dumpNode(aiNode &node) { + String name = node.mName.C_Str(); + checkName(name); + for (int i=0; igetActiveWorld(0)); + if (!assimpWrapper.importAsset(modelLoc, 0)) { + return; + } + + RenderBackendService *rbSrv = ServiceProvider::getService(ServiceType::RenderService); + if (nullptr == rbSrv) { + return; + } + Platform::AbstractWindow *rootWindow = getRootWindow(); + if (nullptr == rootWindow) { + return; + } + + Rect2ui windowsRect; + rootWindow->getWindowsRect(windowsRect); + World *world = getStage()->addActiveWorld("model"); + Entity *entity = assimpWrapper.getEntity(); + Entity *camEntity = new Entity("camera", *getIdContainer(), world); + mCamera = (CameraComponent*)camEntity->createComponent(ComponentType::CameraComponentType); + mCamera->setProjectionParameters(60.f, (f32)windowsRect.width, (f32)windowsRect.height, 0.01f, 1000.f); + world->setActiveCamera(mCamera); + + world->addEntity(entity); + mCamera->observeBoundingBox(entity->getAABB()); + mModelNode = entity->getNode(); + + showStatistics(*assimpWrapper.getScene()); + } + + void onUpdate() override { + if (AppBase::isKeyPressed(Platform::KEY_O) || AppBase::isKeyPressed(Platform::KEY_o)) { + IO::Uri modelLoc; + Platform::PlatformOperations::getFileOpenDialog("Choose asset for import", "*", modelLoc); + if ( modelLoc.isValid()) { + loadAsset(modelLoc); + } + } + + glm::mat4 rot(1.0); + if (AppBase::isKeyPressed(Platform::KEY_A)) { + mTransformMatrix.mModel *= glm::rotate(rot, 0.01f, glm::vec3(1, 0, 0)); + + } + if (AppBase::isKeyPressed(Platform::KEY_S)) { + mTransformMatrix.mModel *= glm::rotate(rot, -0.01f, glm::vec3(1, 0, 0)); + } + + if (AppBase::isKeyPressed(Platform::KEY_W)) { + mTransformMatrix.mModel *= glm::rotate(rot, 0.01f, glm::vec3(0, 1, 0)); + } + + if (AppBase::isKeyPressed(Platform::KEY_D)) { + mTransformMatrix.mModel *= glm::rotate(rot, -0.01f, glm::vec3(0, 1, 0)); + } + RenderBackendService *rbSrv = ServiceProvider::getService(ServiceType::RenderService); + + rbSrv->beginPass(RenderPass::getPassNameById(RenderPassId)); + rbSrv->beginRenderBatch("b1"); + + rbSrv->setMatrix(MatrixType::Model, mTransformMatrix.mModel); + + rbSrv->endRenderBatch(); + rbSrv->endPass(); + + AppBase::onUpdate(); + } +}; + +int main(int argc, char *argv[]) { + ModelLoadingApp myApp(argc, argv); + if (!myApp.initWindow(10, 10, 1024, 768, "ModelLoader sample! Press o to import an Asset", false, false, App::RenderBackendType::OpenGLRenderBackend)) { + return 1; + } + + while (myApp.handleEvents()) { + myApp.update(); + myApp.requestNextFrame(); + } + + myApp.destroy(); + + return 0; +}