From 1109e3ce2d38e5ba60f72f37889b2368bac44026 Mon Sep 17 00:00:00 2001 From: Bartosz Taudul Date: Thu, 19 Sep 2024 19:56:52 +0200 Subject: [PATCH] Add html5.h emscripten backend. --- profiler/CMakeLists.txt | 6 +- profiler/src/BackendEmscripten.cpp | 303 +++++++++++++++++++++++++++++ profiler/src/BackendGlfw.cpp | 31 +-- profiler/wasm/index.html | 13 -- 4 files changed, 310 insertions(+), 43 deletions(-) create mode 100644 profiler/src/BackendEmscripten.cpp diff --git a/profiler/CMakeLists.txt b/profiler/CMakeLists.txt index 5564138b..97dadaba 100644 --- a/profiler/CMakeLists.txt +++ b/profiler/CMakeLists.txt @@ -145,6 +145,10 @@ if(USE_WAYLAND) PROTOCOL ${wayland-protocols_SOURCE_DIR}/unstable/tablet/tablet-unstable-v2.xml BASENAME tablet ) +elseif(EMSCRIPTEN) + set(PROFILER_FILES ${PROFILER_FILES} + src/BackendEmscripten.cpp + ) else() set(PROFILER_FILES ${PROFILER_FILES} src/BackendGlfw.cpp @@ -191,7 +195,7 @@ if(NOT EMSCRIPTEN) endif() if(EMSCRIPTEN) - target_link_options(${PROJECT_NAME} PRIVATE -pthread -sASSERTIONS=0 -sUSE_GLFW=3 -sINITIAL_MEMORY=384mb -sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=4gb -sSTACK_SIZE=1048576 -sWASM_BIGINT=1 -sPTHREAD_POOL_SIZE=8 -sEXPORTED_FUNCTIONS=_main,_nativeResize,_nativeOpenFile -sEXPORTED_RUNTIME_METHODS=ccall -sENVIRONMENT=web,worker --preload-file embed.tracy) + target_link_options(${PROJECT_NAME} PRIVATE -pthread -sASSERTIONS=0 -sINITIAL_MEMORY=384mb -sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=4gb -sSTACK_SIZE=1048576 -sWASM_BIGINT=1 -sPTHREAD_POOL_SIZE=8 -sEXPORTED_FUNCTIONS=_main,_nativeOpenFile -sEXPORTED_RUNTIME_METHODS=ccall -sENVIRONMENT=web,worker --preload-file embed.tracy) file(DOWNLOAD https://share.nereid.pl/i/embed.tracy ${CMAKE_CURRENT_BINARY_DIR}/embed.tracy EXPECTED_MD5 ca0fa4f01e7b8ca5581daa16b16c768d) file(COPY ${CMAKE_CURRENT_LIST_DIR}/wasm/index.html DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/profiler/src/BackendEmscripten.cpp b/profiler/src/BackendEmscripten.cpp new file mode 100644 index 00000000..896ebe3a --- /dev/null +++ b/profiler/src/BackendEmscripten.cpp @@ -0,0 +1,303 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "imgui/imgui_impl_opengl3.h" + +#include "Backend.hpp" +#include "RunQueue.hpp" +#include "profiler/TracyImGui.hpp" + +static std::function s_redraw; +static std::function s_scaleChanged; +static std::function s_isBusy; +static RunQueue* s_mainThreadTasks; + +static EGLDisplay s_eglDpy; +static EGLContext s_eglCtx; +static EGLSurface s_eglSurf; + +static float s_prevScale; +static int s_width, s_height; +static uint64_t s_time; + +static ImGuiKey TranslateKeyCode( const char* code ) +{ + if( strcmp( code, "Backquote" ) == 0 ) return ImGuiKey_GraveAccent; + if( strcmp( code, "Backslash" ) == 0 ) return ImGuiKey_Backslash; + if( strcmp( code, "BracketLeft" ) == 0 ) return ImGuiKey_LeftBracket; + if( strcmp( code, "BracketRight" ) == 0 ) return ImGuiKey_RightBracket; + if( strcmp( code, "Comma" ) == 0 ) return ImGuiKey_Comma; + if( strcmp( code, "Digit0" ) == 0 ) return ImGuiKey_0; + if( strcmp( code, "Digit1" ) == 0 ) return ImGuiKey_1; + if( strcmp( code, "Digit2" ) == 0 ) return ImGuiKey_2; + if( strcmp( code, "Digit3" ) == 0 ) return ImGuiKey_3; + if( strcmp( code, "Digit4" ) == 0 ) return ImGuiKey_4; + if( strcmp( code, "Digit5" ) == 0 ) return ImGuiKey_5; + if( strcmp( code, "Digit6" ) == 0 ) return ImGuiKey_6; + if( strcmp( code, "Digit7" ) == 0 ) return ImGuiKey_7; + if( strcmp( code, "Digit8" ) == 0 ) return ImGuiKey_8; + if( strcmp( code, "Digit9" ) == 0 ) return ImGuiKey_9; + if( strcmp( code, "Equal" ) == 0 ) return ImGuiKey_Equal; + if( strcmp( code, "IntlBackslash" ) == 0 ) return ImGuiKey_Backslash; + if( strcmp( code, "IntlRo" ) == 0 ) return ImGuiKey_Backslash; + if( strcmp( code, "IntlYen" ) == 0 ) return ImGuiKey_Backslash; + if( strcmp( code, "KeyA" ) == 0 ) return ImGuiKey_A; + if( strcmp( code, "KeyB" ) == 0 ) return ImGuiKey_B; + if( strcmp( code, "KeyC" ) == 0 ) return ImGuiKey_C; + if( strcmp( code, "KeyD" ) == 0 ) return ImGuiKey_D; + if( strcmp( code, "KeyE" ) == 0 ) return ImGuiKey_E; + if( strcmp( code, "KeyF" ) == 0 ) return ImGuiKey_F; + if( strcmp( code, "KeyG" ) == 0 ) return ImGuiKey_G; + if( strcmp( code, "KeyH" ) == 0 ) return ImGuiKey_H; + if( strcmp( code, "KeyI" ) == 0 ) return ImGuiKey_I; + if( strcmp( code, "KeyJ" ) == 0 ) return ImGuiKey_J; + if( strcmp( code, "KeyK" ) == 0 ) return ImGuiKey_K; + if( strcmp( code, "KeyL" ) == 0 ) return ImGuiKey_L; + if( strcmp( code, "KeyM" ) == 0 ) return ImGuiKey_M; + if( strcmp( code, "KeyN" ) == 0 ) return ImGuiKey_N; + if( strcmp( code, "KeyO" ) == 0 ) return ImGuiKey_O; + if( strcmp( code, "KeyP" ) == 0 ) return ImGuiKey_P; + if( strcmp( code, "KeyQ" ) == 0 ) return ImGuiKey_Q; + if( strcmp( code, "KeyR" ) == 0 ) return ImGuiKey_R; + if( strcmp( code, "KeyS" ) == 0 ) return ImGuiKey_S; + if( strcmp( code, "KeyT" ) == 0 ) return ImGuiKey_T; + if( strcmp( code, "KeyU" ) == 0 ) return ImGuiKey_U; + if( strcmp( code, "KeyV" ) == 0 ) return ImGuiKey_V; + if( strcmp( code, "KeyW" ) == 0 ) return ImGuiKey_W; + if( strcmp( code, "KeyX" ) == 0 ) return ImGuiKey_X; + if( strcmp( code, "KeyY" ) == 0 ) return ImGuiKey_Y; + if( strcmp( code, "KeyZ" ) == 0 ) return ImGuiKey_Z; + if( strcmp( code, "Minus" ) == 0 ) return ImGuiKey_Minus; + if( strcmp( code, "Period" ) == 0 ) return ImGuiKey_Period; + if( strcmp( code, "Quote" ) == 0 ) return ImGuiKey_Apostrophe; + if( strcmp( code, "Semicolon" ) == 0 ) return ImGuiKey_Semicolon; + if( strcmp( code, "Slash" ) == 0 ) return ImGuiKey_Slash; + if( strcmp( code, "AltLeft" ) == 0 ) return ImGuiKey_LeftAlt; + if( strcmp( code, "AltRight" ) == 0 ) return ImGuiKey_RightAlt; + if( strcmp( code, "Backspace" ) == 0 ) return ImGuiKey_Backspace; + if( strcmp( code, "CapsLock" ) == 0 ) return ImGuiKey_CapsLock; + if( strcmp( code, "ContextMenu" ) == 0 ) return ImGuiKey_Menu; + if( strcmp( code, "ControlLeft" ) == 0 ) return ImGuiKey_LeftCtrl; + if( strcmp( code, "ControlRight" ) == 0 ) return ImGuiKey_RightCtrl; + if( strcmp( code, "Enter" ) == 0 ) return ImGuiKey_Enter; + if( strcmp( code, "MetaLeft" ) == 0 ) return ImGuiKey_LeftSuper; + if( strcmp( code, "MetaRight" ) == 0 ) return ImGuiKey_RightSuper; + if( strcmp( code, "ShiftLeft" ) == 0 ) return ImGuiKey_LeftShift; + if( strcmp( code, "ShiftRight" ) == 0 ) return ImGuiKey_RightShift; + if( strcmp( code, "Space" ) == 0 ) return ImGuiKey_Space; + if( strcmp( code, "Tab" ) == 0 ) return ImGuiKey_Tab; + if( strcmp( code, "Delete" ) == 0 ) return ImGuiKey_Delete; + if( strcmp( code, "End" ) == 0 ) return ImGuiKey_End; + if( strcmp( code, "Home" ) == 0 ) return ImGuiKey_Home; + if( strcmp( code, "Insert" ) == 0 ) return ImGuiKey_Insert; + if( strcmp( code, "PageDown" ) == 0 ) return ImGuiKey_PageDown; + if( strcmp( code, "PageUp" ) == 0 ) return ImGuiKey_PageUp; + if( strcmp( code, "ArrowDown" ) == 0 ) return ImGuiKey_DownArrow; + if( strcmp( code, "ArrowLeft" ) == 0 ) return ImGuiKey_LeftArrow; + if( strcmp( code, "ArrowRight" ) == 0 ) return ImGuiKey_RightArrow; + if( strcmp( code, "ArrowUp" ) == 0 ) return ImGuiKey_UpArrow; + if( strcmp( code, "NumLock" ) == 0 ) return ImGuiKey_NumLock; + if( strcmp( code, "Numpad0" ) == 0 ) return ImGuiKey_Keypad0; + if( strcmp( code, "Numpad1" ) == 0 ) return ImGuiKey_Keypad1; + if( strcmp( code, "Numpad2" ) == 0 ) return ImGuiKey_Keypad2; + if( strcmp( code, "Numpad3" ) == 0 ) return ImGuiKey_Keypad3; + if( strcmp( code, "Numpad4" ) == 0 ) return ImGuiKey_Keypad4; + if( strcmp( code, "Numpad5" ) == 0 ) return ImGuiKey_Keypad5; + if( strcmp( code, "Numpad6" ) == 0 ) return ImGuiKey_Keypad6; + if( strcmp( code, "Numpad7" ) == 0 ) return ImGuiKey_Keypad7; + if( strcmp( code, "Numpad8" ) == 0 ) return ImGuiKey_Keypad8; + if( strcmp( code, "Numpad9" ) == 0 ) return ImGuiKey_Keypad9; + if( strcmp( code, "NumpadAdd" ) == 0 ) return ImGuiKey_KeypadAdd; + if( strcmp( code, "NumpadBackspace" ) == 0 ) return ImGuiKey_Backspace; + if( strcmp( code, "NumpadComma" ) == 0 ) return ImGuiKey_KeypadDecimal; + if( strcmp( code, "NumpadDecimal" ) == 0 ) return ImGuiKey_KeypadDecimal; + if( strcmp( code, "NumpadDivide" ) == 0 ) return ImGuiKey_KeypadDivide; + if( strcmp( code, "NumpadEnter" ) == 0 ) return ImGuiKey_KeypadEnter; + if( strcmp( code, "NumpadEqual" ) == 0 ) return ImGuiKey_KeypadEqual; + if( strcmp( code, "NumpadMultiply" ) == 0 ) return ImGuiKey_KeypadMultiply; + if( strcmp( code, "NumpadSubtract" ) == 0 ) return ImGuiKey_KeypadSubtract; + if( strcmp( code, "Escape" ) == 0 ) return ImGuiKey_Escape; + if( strcmp( code, "F1" ) == 0 ) return ImGuiKey_F1; + if( strcmp( code, "F2" ) == 0 ) return ImGuiKey_F2; + if( strcmp( code, "F3" ) == 0 ) return ImGuiKey_F3; + if( strcmp( code, "F4" ) == 0 ) return ImGuiKey_F4; + if( strcmp( code, "F5" ) == 0 ) return ImGuiKey_F5; + if( strcmp( code, "F6" ) == 0 ) return ImGuiKey_F6; + if( strcmp( code, "F7" ) == 0 ) return ImGuiKey_F7; + if( strcmp( code, "F8" ) == 0 ) return ImGuiKey_F8; + if( strcmp( code, "F9" ) == 0 ) return ImGuiKey_F9; + if( strcmp( code, "F10" ) == 0 ) return ImGuiKey_F10; + // F11 is browser fullscreen, F12 is browser dev tools, omitting them + if( strcmp( code, "ScrollLock" ) == 0 ) return ImGuiKey_ScrollLock; + if( strcmp( code, "Pause" ) == 0 ) return ImGuiKey_Pause; + return ImGuiKey_None; +} + +Backend::Backend( const char* title, const std::function& redraw, const std::function& scaleChanged, const std::function& isBusy, RunQueue* mainThreadTasks ) +{ + constexpr EGLint eglConfigAttrib[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_NONE + }; + + s_eglDpy = eglGetDisplay( EGL_DEFAULT_DISPLAY ); + EGLBoolean res; + res = eglInitialize( s_eglDpy, nullptr, nullptr ); + if( res != EGL_TRUE ) { fprintf( stderr, "Cannot initialize EGL!\n" ); exit( 1 ); } + + EGLint count; + EGLConfig eglConfig; + res = eglChooseConfig( s_eglDpy, eglConfigAttrib, &eglConfig, 1, &count ); + if( res != EGL_TRUE || count != 1 ) { fprintf( stderr, "No suitable EGL config found!\n" ); exit( 1 ); } + + s_eglSurf = eglCreateWindowSurface( s_eglDpy, eglConfig, 0, nullptr ); + + constexpr EGLint eglCtxAttrib[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + s_eglCtx = eglCreateContext( s_eglDpy, eglConfig, EGL_NO_CONTEXT, eglCtxAttrib ); + if( !s_eglCtx ) { fprintf( stderr, "Cannot create OpenGL 3.2 Core Profile context!\n" ); exit( 1 ); } + res = eglMakeCurrent( s_eglDpy, s_eglSurf, s_eglSurf, s_eglCtx ); + if( res != EGL_TRUE ) { fprintf( stderr, "Cannot make EGL context current!\n" ); exit( 1 ); } + + ImGui_ImplOpenGL3_Init( "#version 100" ); + + EM_ASM( document.title = UTF8ToString($0), title ); + + s_redraw = redraw; + s_scaleChanged = scaleChanged; + s_isBusy = isBusy; + s_mainThreadTasks = mainThreadTasks; + + ImGuiIO& io = ImGui::GetIO(); + io.BackendPlatformName = "wasm (tracy profiler)"; + + emscripten_set_mousedown_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenMouseEvent* e, void* ) -> EM_BOOL { + ImGui::GetIO().AddMouseButtonEvent( e->button == 0 ? 0 : 3 - e->button, true ); + tracy::s_wasActive = true; + return EM_TRUE; + } ); + emscripten_set_mouseup_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenMouseEvent* e, void* ) -> EM_BOOL { + ImGui::GetIO().AddMouseButtonEvent( e->button == 0 ? 0 : 3 - e->button, false ); + tracy::s_wasActive = true; + return EM_TRUE; + } ); + emscripten_set_mousemove_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenMouseEvent* e, void* ) -> EM_BOOL { + const auto scale = EM_ASM_DOUBLE( { return window.devicePixelRatio; } ); + ImGui::GetIO().AddMousePosEvent( e->targetX * scale, e->targetY * scale ); + tracy::s_wasActive = true; + return EM_TRUE; + } ); + emscripten_set_wheel_callback( "#canvas", nullptr, EM_TRUE, []( int, const EmscriptenWheelEvent* e, void* ) -> EM_BOOL { + ImGui::GetIO().AddMouseWheelEvent( e->deltaX, -e->deltaY ); + tracy::s_wasActive = true; + return EM_TRUE; + } ); + emscripten_set_keydown_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE, [] ( int, const EmscriptenKeyboardEvent* e, void* ) -> EM_BOOL { + printf( "key down: %s\n", e->code ); + const auto code = TranslateKeyCode( e->code ); + if( code == ImGuiKey_None ) return EM_FALSE; + ImGui::GetIO().AddKeyEvent( code, true ); + if( e->key[0] && !e->key[1] ) ImGui::GetIO().AddInputCharacter( *e->key ); + return EM_TRUE; + } ); + emscripten_set_keyup_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE, [] ( int, const EmscriptenKeyboardEvent* e, void* ) -> EM_BOOL { + const auto code = TranslateKeyCode( e->code ); + if( code == ImGuiKey_None ) return EM_FALSE; + ImGui::GetIO().AddKeyEvent( code, false ); + return EM_TRUE; + } ); + + s_prevScale = GetDpiScale(); + s_time = std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); +} + +Backend::~Backend() +{ +} + +void Backend::Show() +{ +} + +void Backend::Run() +{ + emscripten_set_main_loop( []() { + s_redraw(); + s_mainThreadTasks->Run(); + }, 0, 1 ); +} + +void Backend::Attention() +{ +} + +void Backend::NewFrame( int& w, int& h ) +{ + const auto scale = GetDpiScale(); + if( scale != s_prevScale ) + { + s_prevScale = scale; + s_scaleChanged( scale ); + } + + w = EM_ASM_INT( { return window.innerWidth; } ) * scale; + h = EM_ASM_INT( { return window.innerHeight; } ) * scale; + + if( s_width != w || s_height != h ) + { + EM_ASM( Module.canvas.style.width = window.innerWidth + 'px'; Module.canvas.style.height = window.innerHeight + 'px' ); + EM_ASM( Module.canvas.width = $0; Module.canvas.height = $1, w, h ); + + s_width = w; + s_height = h; + + glViewport( 0, 0, s_width, s_height ); + tracy::s_wasActive = true; + } + + ImGuiIO& io = ImGui::GetIO(); + io.DisplaySize = ImVec2( w, h ); + io.DisplayFramebufferScale = ImVec2( 1, 1 ); + + ImGui_ImplOpenGL3_NewFrame(); + + uint64_t time = std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); + io.DeltaTime = std::min( 0.1f, ( time - s_time ) / 1000000.f ); + s_time = time; +} + +void Backend::EndFrame() +{ + const ImVec4 clear_color = ImColor( 20, 20, 17 ); + + ImGui::Render(); + glClearColor( clear_color.x, clear_color.y, clear_color.z, clear_color.w ); + glClear( GL_COLOR_BUFFER_BIT ); + ImGui_ImplOpenGL3_RenderDrawData( ImGui::GetDrawData() ); +} + +void Backend::SetIcon( uint8_t* data, int w, int h ) +{ +} + +void Backend::SetTitle( const char* title ) +{ + EM_ASM( document.title = UTF8ToString($0), title ); +} + +float Backend::GetDpiScale() +{ + return EM_ASM_DOUBLE( { return window.devicePixelRatio; } ); +} diff --git a/profiler/src/BackendGlfw.cpp b/profiler/src/BackendGlfw.cpp index ca114f69..7daf27d4 100644 --- a/profiler/src/BackendGlfw.cpp +++ b/profiler/src/BackendGlfw.cpp @@ -1,11 +1,6 @@ #include "imgui/imgui_impl_glfw.h" #include "imgui/imgui_impl_opengl3.h" -#ifdef __EMSCRIPTEN__ -# include -# include -#else -# include "imgui/imgui_impl_opengl3_loader.h" -#endif +#include "imgui/imgui_impl_opengl3_loader.h" #include #include @@ -97,11 +92,7 @@ Backend::Backend( const char* title, const std::function& redraw, const glfwSetWindowRefreshCallback( s_window, []( GLFWwindow* ) { tracy::s_wasActive = true; s_redraw(); } ); ImGui_ImplGlfw_InitForOpenGL( s_window, true ); -#ifdef __EMSCRIPTEN__ - ImGui_ImplOpenGL3_Init( "#version 100" ); -#else ImGui_ImplOpenGL3_Init( "#version 150" ); -#endif s_redraw = redraw; s_mainThreadTasks = mainThreadTasks; @@ -133,13 +124,6 @@ void Backend::Show() void Backend::Run() { -#ifdef __EMSCRIPTEN__ - emscripten_set_main_loop( []() { - glfwPollEvents(); - s_redraw(); - s_mainThreadTasks->Run(); - }, 0, 1 ); -#else while( !glfwWindowShouldClose( s_window ) ) { if( s_iconified ) @@ -154,7 +138,6 @@ void Backend::Run() s_mainThreadTasks->Run(); } } -#endif } void Backend::Attention() @@ -206,9 +189,7 @@ void Backend::SetTitle( const char* title ) float Backend::GetDpiScale() { -#ifdef __EMSCRIPTEN__ - return EM_ASM_DOUBLE( { return window.devicePixelRatio; } ); -#elif GLFW_VERSION_MAJOR > 3 || ( GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 3 ) +#if GLFW_VERSION_MAJOR > 3 || ( GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 3 ) auto monitor = glfwGetWindowMonitor( s_window ); if( !monitor ) monitor = glfwGetPrimaryMonitor(); if( monitor ) @@ -220,11 +201,3 @@ float Backend::GetDpiScale() #endif return 1; } - -#ifdef __EMSCRIPTEN__ -extern "C" int nativeResize( int width, int height ) -{ - glfwSetWindowSize( s_window, width, height ); - return 0; -} -#endif diff --git a/profiler/wasm/index.html b/profiler/wasm/index.html index 74261e53..88a0712d 100644 --- a/profiler/wasm/index.html +++ b/profiler/wasm/index.html @@ -89,16 +89,6 @@ var spinnerElement = document.getElementById('spinner'); var preloadElement = document.getElementById('preload'); - function resizeHandler() { - let w = Math.floor(Math.floor(window.innerWidth * window.devicePixelRatio) / window.devicePixelRatio); - let h = Math.floor(Math.floor(window.innerHeight * window.devicePixelRatio) / window.devicePixelRatio); - if(preloadElement.hidden === true) { - Module.ccall('nativeResize', 'number', ['number', 'number'], [w * window.devicePixelRatio, h * window.devicePixelRatio]); - } - Module.canvas.style.width = w + 'px'; - Module.canvas.style.height = h + 'px'; - } - var Module = { preRun: [], postRun: [], @@ -139,7 +129,6 @@ progressElement.hidden = true; preloadElement.hidden = true; spinnerElement.style.display = 'none'; - resizeHandler(); } statusElement.innerHTML = text; }, @@ -158,8 +147,6 @@ if (text) console.error('[post-exception status] ' + text); }; }; - addEventListener('resize', resizeHandler); - resizeHandler();