Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve webgl render fps #2379

Merged
merged 1 commit into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmake/Modules/AXBuildHelpers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ set(AX_WASM_SHELL_FILE "${_AX_ROOT}/core/platform/wasm/shell_minimal.html" CACHE

option(AX_WASM_ENABLE_DEVTOOLS "Enable wasm devtools" ON)

set(_AX_WASM_EXPORTS "_main,_axmol_wgl_context_lost,_axmol_wgl_context_restored,_axmol_hdoc_visibilitychange")
set(_AX_WASM_EXPORTS "_main,_axmol_webglcontextlost,_axmol_webglcontextrestored,_axmol_hdoc_visibilitychange")
if(AX_WASM_ENABLE_DEVTOOLS)
string(APPEND _AX_WASM_EXPORTS ",_axmol_dev_pause,_axmol_dev_resume,_axmol_dev_step")
endif()
Expand Down
15 changes: 12 additions & 3 deletions core/base/JobSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
#include <functional>
#include <stdexcept>

#if defined(__EMSCRIPTEN__)
# include <emscripten/emscripten.h>
#endif

namespace ax
{

Expand Down Expand Up @@ -135,15 +139,20 @@ static int clampThreads(int nThreads)
{
if (nThreads <= 0)
{
#if !defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__)
#if !defined(__EMSCRIPTEN__)
# if defined(AX_PLATFORM_PC)
nThreads = (std::max)(static_cast<int>(std::thread::hardware_concurrency() * 3 / 2), 2);
# else
nThreads = (std::clamp)(static_cast<int>(std::thread::hardware_concurrency()) - 2, 2, 8);
# endif
#else
AXLOGW("The emscripten pthread not enabled, JobSystem not working");
# if defined(__EMSCRIPTEN_PTHREADS__)
nThreads = EM_ASM_INT(return PThread.unusedWorkers.length);
AXLOGI("The emscripten pthread enabled, unused workers count:{}", nThreads);
# else
nThreads = 0;
AXLOGW("The emscripten pthread not enabled, JobSystem not working");
# endif
#endif
}

Expand Down Expand Up @@ -235,4 +244,4 @@ void JobSystem::enqueue(std::function<void()> task, std::function<void()> done)

#pragma endregion

}
} // namespace ax
2 changes: 1 addition & 1 deletion core/platform/android/Application-android.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Application::~Application()

int Application::run()
{
// Initialize instance and cocos2d.
// Initialize instance and axmol.
if (!applicationDidFinishLaunching())
{
return 0;
Expand Down
152 changes: 91 additions & 61 deletions core/platform/wasm/Application-wasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@ THE SOFTWARE.
# include "base/Director.h"
# include "base/Utils.h"
# include "platform/FileUtils.h"
# include "yasio/utils.hpp"
# include <emscripten/emscripten.h>

extern void axmol_wasm_app_exit();

extern "C" {
//
void axmol_hdoc_visibilitychange(bool hidden)
Expand All @@ -47,13 +50,13 @@ void axmol_hdoc_visibilitychange(bool hidden)
}

// webglcontextlost
void axmol_wgl_context_lost()
void axmol_webglcontextlost()
{
AXLOGI("receive event: webglcontextlost");
}

// webglcontextrestored
void axmol_wgl_context_restored()
void axmol_webglcontextrestored()
{
AXLOGI("receive event: webglcontextrestored");

Expand Down Expand Up @@ -90,7 +93,76 @@ namespace ax
// sharedApplication pointer
Application* Application::sm_pSharedApplication = nullptr;

Application::Application() : _animationSpeed(60)
static int64_t NANOSECONDSPERSECOND = 1000000000LL;
static int64_t NANOSECONDSPERMICROSECOND = 1000000LL;
static int64_t FPS_CONTROL_THRESHOLD = static_cast<int64_t>(1.0f / 1200.0f * NANOSECONDSPERSECOND);

static int64_t s_animationInterval = static_cast<int64_t>(1/60.0 * NANOSECONDSPERSECOND);

static Director* __director;
static int64_t mLastTickInNanoSeconds = 0;

static void renderFrame() {
auto director = __director;
auto glview = director->getGLView();

director->mainLoop();
glview->pollEvents();

if (glview->windowShouldClose())
{
AXLOGI("shuting down axmol wasm app ...");
emscripten_cancel_main_loop(); // Cancel current loop and set the cleanup one.

if (glview->isOpenGLReady())
{
director->end();
director->mainLoop();
}
glview->release();

axmol_wasm_app_exit();
}
}

static void updateFrame(void)
{
renderFrame();

/*
* No need to use algorithm in default(60,90,120... FPS) situation,
* since onDrawFrame() was called by system 60 times per second by default.
*/
if (s_animationInterval > FPS_CONTROL_THRESHOLD) {
auto interval = yasio::xhighp_clock() - mLastTickInNanoSeconds;

if (interval < s_animationInterval) {
std::this_thread::sleep_for(std::chrono::nanoseconds(s_animationInterval - interval));
}

mLastTickInNanoSeconds = yasio::xhighp_clock();
}
}

static void getCurrentLangISO2(char buf[16]) {
// clang-format off
EM_ASM_ARGS(
{
var lang = localStorage.getItem('localization_language');
if (lang == null)
{
stringToUTF8(window.navigator.language.replace(/-.*/, ""), $0, 16);
}
else
{
stringToUTF8(lang, $0, 16);
}
},
buf);
// clang-format on
}

Application::Application()
{
AX_ASSERT(!sm_pSharedApplication);
sm_pSharedApplication = this;
Expand All @@ -102,15 +174,6 @@ Application::~Application()
sm_pSharedApplication = nullptr;
}

extern "C" void mainLoopIter(void)
{
auto director = Director::getInstance();
auto glview = director->getGLView();

director->mainLoop();
glview->pollEvents();
}

int Application::run()
{
initGLContextAttrs();
Expand All @@ -120,33 +183,27 @@ int Application::run()
return 1;
}

auto director = Director::getInstance();
auto glview = director->getGLView();
__director = Director::getInstance();

// Retain glview to avoid glview being released in the while loop
glview->retain();

// emscripten_set_main_loop(&mainLoopIter, 0, 1);
emscripten_set_main_loop(&mainLoopIter, _animationSpeed, 1);
// TODO: ? does these cleanup really run?
/* Only work on Desktop
* Director::mainLoop is really one frame logic
* when we want to close the window, we should call Director::end();
* then call Director::mainLoop to do release of internal resources
*/
if (glview->isOpenGLReady())
{
director->end();
director->mainLoop();
director = nullptr;
}
glview->release();
__director->getGLView()->retain();

/*
The JavaScript environment will call that function at a specified number
of frames per second. If called on the main browser thread, setting 0 or
a negative value as the fps will use the browser’s requestAnimationFrame mechanism
o call the main loop function. This is HIGHLY recommended if you are doing rendering,
as the browser’s requestAnimationFrame will make sure you render at a proper smooth rate
that lines up properly with the browser and monitor.
*/
emscripten_set_main_loop(updateFrame, -1, false);

return 0;
}

void Application::setAnimationInterval(float interval)
{
_animationSpeed = 1.0f / interval;
s_animationInterval = static_cast<int64_t>(interval * NANOSECONDSPERSECOND);
}

void Application::setResourceRootPath(const std::string& rootResDir)
Expand Down Expand Up @@ -203,20 +260,7 @@ const char* Application::getCurrentLanguageCode()
{
static char code[3] = {0};
char pLanguageName[16];

EM_ASM_ARGS(
{
var lang = localStorage.getItem('localization_language');
if (lang == null)
{
stringToUTF8(window.navigator.language.replace(/ - .*/, ""), $0, 16);
}
else
{
stringToUTF8(lang, $0, 16);
}
},
pLanguageName);
getCurrentLangISO2(pLanguageName);
strncpy(code, pLanguageName, 2);
code[2] = '\0';
return code;
Expand All @@ -225,21 +269,7 @@ const char* Application::getCurrentLanguageCode()
LanguageType Application::getCurrentLanguage()
{
char pLanguageName[16];

EM_ASM_ARGS(
{
var lang = localStorage.getItem('localization_language');
if (lang == null)
{
stringToUTF8(window.navigator.language.replace(/ - .*/, ""), $0, 16);
}
else
{
stringToUTF8(lang, $0, 16);
}
},
pLanguageName);

getCurrentLangISO2(pLanguageName);
return utils::getLanguageTypeByISO2(pLanguageName);
}

Expand Down
1 change: 0 additions & 1 deletion core/platform/wasm/Application-wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ class Application : public ApplicationBase
*/
virtual Platform getTargetPlatform() override;
protected:
long _animationSpeed; // micro second
std::string _resourceRootPath;

static Application * sm_pSharedApplication;
Expand Down
4 changes: 2 additions & 2 deletions core/platform/wasm/shell_minimal.html
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
// application robust, you may want to override this behavior before shipping!
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
canvas.addEventListener("webglcontextlost", function(e) { Module.ccall('axmol_wgl_context_lost'); alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
canvas.addEventListener("webglcontextrestored", function(e) { Module.ccall('axmol_wgl_context_restored'); e.preventDefault(); }, false);
canvas.addEventListener("webglcontextlost", function(e) { Module.ccall('axmol_webglcontextlost'); alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
canvas.addEventListener("webglcontextrestored", function(e) { Module.ccall('axmol_webglcontextrestored'); e.preventDefault(); }, false);

document.addEventListener("visibilitychange", () => { Module.ccall('axmol_hdoc_visibilitychange', document.hidden); });

Expand Down
17 changes: 10 additions & 7 deletions templates/common/proj.wasm/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,23 @@

using namespace ax;

int axmol_main()
namespace
{
// create the application instance
AppDelegate app;
return Application::getInstance()->run();
std::unique_ptr<AppDelegate> appDelegate;
}

int main(int argc, char** argv)
void axmol_wasm_app_exit()
{
auto result = axmol_main();
appDelegate = nullptr;

#if AX_OBJECT_LEAK_DETECTION
Object::printLeaks();
#endif
}

return result;
int main(int argc, char** argv)
{
// create the application instance
appDelegate.reset(new AppDelegate());
return Application::getInstance()->run();
}
2 changes: 2 additions & 0 deletions tests/cpp-tests/Source/AppDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ AppDelegate::AppDelegate() : _testController(nullptr) {}

AppDelegate::~AppDelegate()
{

AXLOGI("AppDelegate::~AppDelegate");
// SimpleAudioEngine::end();
// TODO: minggo
// cocostudio::ArmatureDataManager::destroyInstance();
Expand Down
12 changes: 11 additions & 1 deletion tests/cpp-tests/proj.wasm/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,19 @@

using namespace ax;

namespace
{
std::unique_ptr<AppDelegate> appDelegate;
}

void axmol_wasm_app_exit()
{
appDelegate = nullptr;
}

int main(int argc, char** argv)
{
// create the application instance
AppDelegate app;
appDelegate.reset(new AppDelegate());
return Application::getInstance()->run();
}
12 changes: 11 additions & 1 deletion tests/fairygui-tests/proj.wasm/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,19 @@

using namespace ax;

namespace
{
std::unique_ptr<AppDelegate> appDelegate;
}

void axmol_wasm_app_exit()
{
appDelegate = nullptr;
}

int main(int argc, char** argv)
{
// create the application instance
AppDelegate app;
appDelegate.reset(new AppDelegate());
return Application::getInstance()->run();
}
12 changes: 11 additions & 1 deletion tests/live2d-tests/proj.wasm/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,19 @@

using namespace ax;

namespace
{
std::unique_ptr<AppDelegate> appDelegate;
}

void axmol_wasm_app_exit()
{
appDelegate = nullptr;
}

int main(int argc, char** argv)
{
// create the application instance
AppDelegate app;
appDelegate.reset(new AppDelegate());
return Application::getInstance()->run();
}
Loading