Open GottemHams opened 10 months ago
I just tried to compile with clang++
. That works on Linux. So just that isn't the issue. Sorry got no idea why that happens and I have no Mac to test it out. The sim should be happy when SDL2 is found (which it should be as you're clearly at the build step already.
You could try to compile https://github.com/lvgl/lv_port_pc_eclipse/tree/dev-7.0 as this is the simulator InfiniSim is based on. Be sure to use the dev-7.0
branch, as InfiniTime uses lvgl v7 (instead of the current v8)
v7.0 of that repo doesn't even compile right from the very beginning, something about keyboard_handler
being undefined. The master branch compiles without issue though, and I can also run it properly (like scrolling etc actually works).
I decided to add -- VERBOSE=1
to the cmake --build
command. Also, the used compiler is /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
, for which --version
says:
Apple clang version 13.1.6 (clang-1316.0.21.2.5)
Target: arm64-apple-darwin21.6.0
Thread model: posix
Anyways, I noticed in the verbose output that all the lv_*
stuff is compiled into libsim-base.a
, which is in fact included in the eventual c++
command for compiling the main infinisim
executable. But somehow it's still unable to detect the symbols. So then I added the whole bunch of files like CMakeFiles/sim-base.dir/InfiniTime/src/libs/lvgl/src/lv_core/lv_disp.c.o
as inputs to the failing c++
command and ran it manually. It now compiles successfully.
For in the meantime I've scripted around the build process to compile a small dummy file for all those lvgl-related files (to pass the initial compiler check) and include them in CFLAGS
/CXXFLAGS
. Very dirty hack, but for now it works. Or well, sort of, once I also apply the patches mentioned in https://github.com/InfiniTimeOrg/InfiniSim/issues/25. At least then the simulator finally starts with the InfiniTime interface and keyboard commands do work, but not the mouse. The display in general seems to never update. I think the version of lvgl
that's used by v7.0 of the simulator's base is simply too old to work properly on newer macOS. ;_;
Alright so I looked into the differences between the latest version of lv_port_pc_eclipse and what InfiniSim uses. In the main loop it simply calls lv_timer_handler()
and usleep()
's for 1 millisecond instead of 20 or possibly 30. Then for lv_drivers, the monitor.c
that's present for InfiniSim actually doesn't seem to be in use any longer. Instead, the relevant code seems to have been moved to sdl_gpu.c
where it uses lv_timer_create()
instead of lv_task_create()
. I dunno if it means anything in this specific case but they do imply different things. Timers are basically hardware interrupt-based callbacks that run within the thread that created them, while threads run autonomously and they also have their own stack memory. If these are timers by that definition then it does make sense the crash doesn't happen anymore (runs on the main thread).
So anyways, time to compile the simulator with debug information (-g
) and check the backtrace:
* thread #8: tid = 0x2bad1e, 0x000000018ecfed98 libsystem_kernel.dylib`__pthread_kill + 8, name = 'displayapp', stop reason = signal SIGABRT
(lldb) bt
...
frame #15: 0x00000001000f8e64 infinisim`sdl_event_handler(t=0x00006000017263c8) at monitor.c:233:11
frame #16: 0x0000000100035258 infinisim`lv_task_exec(task=0x00006000017263c8) at lv_task.c:386:27
frame #17: 0x0000000100034ff4 infinisim`lv_task_handler at lv_task.c:134:20
frame #18: 0x00000001000e0144 infinisim`Pinetime::Applications::DisplayApp::Refresh(this=0x0000000100161ab8) at DisplayApp.cpp:195:22
frame #19: 0x00000001000e005c infinisim`Pinetime::Applications::DisplayApp::Process(instance=0x0000000100161ab8) at DisplayApp.cpp:129:10
frame #20: 0x00000001000fe350 infinisim`sdl_function_wrapper(instance=0x0000000100162b30) at task.cpp:61:3
...
The truncated parts are just calls to libsdl
etc, not much we can do about that regardless so I didn't include it.
Updating the simulator to work with the latest versions of its base components is probably still the best solution, but I wanted to get this working on a much shorter term. Here's a full diff against the current version of InfiniSim's repo (including submodules, yeah this is very dirty):
Submodule InfiniTime contains modified content
diff --git a/InfiniTime/src/components/datetime/DateTimeController.cpp b/InfiniTime/src/components/datetime/DateTimeController.cpp
index 9e9fb6e4..3cd761e2 100644
--- a/InfiniTime/src/components/datetime/DateTimeController.cpp
+++ b/InfiniTime/src/components/datetime/DateTimeController.cpp
@@ -69,7 +69,7 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
currentDateTime += std::chrono::seconds(correctedDelta);
uptime += std::chrono::seconds(correctedDelta);
- std::time_t currentTime = std::chrono::system_clock::to_time_t(currentDateTime);
+ std::time_t currentTime = std::chrono::system_clock::to_time_t(std::chrono::time_point_cast<std::chrono::system_clock::duration>(currentDateTime));
localTime = *std::localtime(¤tTime);
auto minute = Minutes();
Submodule lv_drivers contains modified content
diff --git a/lv_drivers/display/monitor.c b/lv_drivers/display/monitor.c
index 53534f4..84e1ef1 100644
--- a/lv_drivers/display/monitor.c
+++ b/lv_drivers/display/monitor.c
@@ -67,6 +67,7 @@ static void monitor_sdl_refr(lv_task_t * t);
/***********************
* GLOBAL PROTOTYPES
***********************/
+void sdl_event_handler_mainthread(void);
/**********************
* STATIC VARIABLES
@@ -95,7 +96,6 @@ static volatile bool sdl_quit_qry = false;
void monitor_init(void)
{
monitor_sdl_init();
- lv_task_create(sdl_event_handler, 10, LV_TASK_PRIO_HIGH, NULL);
}
/**
@@ -214,6 +214,14 @@ void monitor_flush2(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t
}
#endif
+/**
+ * Meant for calling sdl_event_handler() from other threads (can only be used from the main thread though)
+ */
+void sdl_event_handler_mainthread(void)
+{
+ sdl_event_handler(NULL);
+}
+
/**********************
* STATIC FUNCTIONS
**********************/
@@ -226,7 +234,8 @@ void monitor_flush2(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t
static void sdl_event_handler(lv_task_t * t)
{
- (void)t;
+ if(t)
+ (void)t;
/*Refresh handling*/
SDL_Event event;
diff --git a/main.cpp b/main.cpp
index 8070db7..59c7cb4 100644
--- a/main.cpp
+++ b/main.cpp
@@ -93,6 +93,7 @@ typedef struct {
#endif
}monitor_t;
extern monitor_t monitor;
+extern void sdl_event_handler_mainthread(void);
}
void saveScreenshot()
@@ -1030,11 +1031,17 @@ int main(int argc, char **argv)
// initialize the core of our Simulator
Framework fw(fw_status_window_visible, 240,240);
+ //uint8_t sdl_tick = 0;
while(1) {
fw.handle_keys(); // key event polling
fw.handle_touch_and_button();
fw.refresh();
- usleep(LV_DISP_DEF_REFR_PERIOD * 1000);
+ usleep(1000);
+ //sdl_tick++;
+ //if(sdl_tick >= 10) {
+ sdl_event_handler_mainthread();
+ // sdl_tick = 0;
+ //}
}
return 0;
So basically, remove the separate thread that was originally causing the crash and run its code directly on the main thread. The UI now actually responds to everything. I originally added the sdl_tick
to simulate the 10 ms interval from lv_task_create()
, but that actually makes the UI feel sluggish. Apparently I execute the click and drag faster than those 10 ms, because if I drag more slowly it does respond properly (I also noticed you can't move the cursor outside the program's window or it won't respond either).
Here's a screenshot showing a different OS name, I figured that was the easiest thing to edit for verification.
If we were to do a quick comparison against lv_port_pc_eclipse, then we "should" call lv_task_handler()
just before the usleep()
as well. But it's actually already handled through fw.refresh()
, which calls refresh_screen()
where finally lv_task_handler()
may be called.
Now, there are a few lingering minor issues but at least I can simulate the firmware.
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x328)
frame #0: 0x00000001000fdd64 infinisim`std::__1::__hash_iterator<std::__1::__hash_node<std::__1::__hash_value_type<void*, unsigned long>, void*>*> std::__1::__hash_table<std::__1::__hash_value_type<void*, unsigned long>, std::__1::__unordered_map_hasher<void*, std::__1::__hash_value_type<void*, unsigned long>, std::__1::hash<void*>, std::__1::equal_to<void*>, true>, std::__1::__unordered_map_equal<void*, std::__1::__hash_value_type<void*, unsigned long>, std::__1::equal_to<void*>, std::__1::hash<void*>, true>, std::__1::allocator<std::__1::__hash_value_type<void*, unsigned long> > >::find<void*>(this=0x0000000100160a10, __k=0x000000016fdf45d8) at __hash_table:2394:31
2391 if (__bc != 0)
2392 {
2393 size_t __chash = __constrain_hash(__hash, __bc);
-> 2394 __next_pointer __nd = __bucket_list_[__chash];
2395 if (__nd != nullptr)
2396 {
2397 for (__nd = __nd->__next_; __nd != nullptr &&
thread #7, name = 'displayapp', stop reason = EXC_BAD_ACCESS (code=1, address=0x8)
frame #0: 0x00000001000e0904 infinisim`Pinetime::Applications::Screens::Screen::IsRunning(this=0x0000000000000000) const at Screen.h:25:18
22 static void RefreshTaskCallback(lv_task_t* task);
23
24 bool IsRunning() const {
-> 25 return running;
26 }
27
28 /** @return false if the button hasn't been handled by the app, true if it has been handled */
Target 0: (infinisim) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x3f0)
* frame #0: 0x00000001000fdd5c infinisim`std::__1::__hash_iterator<std::__1::__hash_node<std::__1::__hash_value_type<void*, unsigned long>, void*>*> std::__1::__hash_table<std::__1::__hash_value_type<void*, unsigned long>, std::__1::__unordered_map_hasher<void*, std::__1::__hash_value_type<void*, unsigned long>, std::__1::hash<void*>, std::__1::equal_to<void*>, true>, std::__1::__unordered_map_equal<void*, std::__1::__hash_value_type<void*, unsigned long>, std::__1::equal_to<void*>, std::__1::hash<void*>, true>, std::__1::allocator<std::__1::__hash_value_type<void*, unsigned long> > >::find<void*>(this=0x0000000100160a10, __k=0x000000016fdf45f8) at __hash_table:2394:31
frame #1: 0x00000001000fdc80 infinisim`unsigned long std::__1::__hash_table<std::__1::__hash_value_type<void*, unsigned long>, std::__1::__unordered_map_hasher<void*, std::__1::__hash_value_type<void*, unsigned long>, std::__1::hash<void*>, std::__1::equal_to<void*>, true>, std::__1::__unordered_map_equal<void*, std::__1::__hash_value_type<void*, unsigned long>, std::__1::equal_to<void*>, std::__1::hash<void*>, true>, std::__1::allocator<std::__1::__hash_value_type<void*, unsigned long> > >::__erase_unique<void*>(this=0x0000000100160a10, __k=0x000000016fdf45f8) at __hash_table:2533:20
frame #2: 0x00000001000f9ce8 infinisim`std::__1::unordered_map<void*, unsigned long, std::__1::hash<void*>, std::__1::equal_to<void*>, std::__1::allocator<std::__1::pair<void* const, unsigned long> > >::erase(this=0x0000000100160a10 size=63, __k=0x000000016fdf45f8) at unordered_map:1272:59
frame #3: 0x00000001000f9cb4 infinisim`::vPortFree(pv=0x0000600000005480) at FreeRTOS.cpp:35:19
frame #4: 0x0000000100032a70 infinisim`lv_mem_free(data=0x0000600000005488) at lv_mem.c:261:5
frame #5: 0x00000001000156e0 infinisim`_lv_style_list_reset(list=0x00006000031004d0) at lv_style.c:295:29
frame #6: 0x000000010000acd0 infinisim`lv_obj_clean_style_list(obj=0x0000600003100488, part='\0') at lv_obj.c:1237:5
frame #7: 0x00000001000080e8 infinisim`lv_obj_signal(obj=0x0000600003100488, sign='\0', param=0x0000000000000000) at lv_obj.c:4033:9
frame #8: 0x00000001000526fc infinisim`lv_label_signal(label=0x0000600003100488, sign='\0', param=0x0000000000000000) at lv_label.c:1395:11
frame #9: 0x0000000100008c94 infinisim`obj_del_core(obj=0x0000600003100488) at lv_obj.c:3815:5
frame #10: 0x0000000100008a20 infinisim`lv_obj_del(obj=0x0000600003100488) at lv_obj.c:485:5
frame #11: 0x0000000100008dc4 infinisim`lv_obj_clean(obj=0x0000600003010008) at lv_obj.c:532:9
frame #12: 0x0000000100099528 infinisim`Pinetime::Applications::Screens::SystemInfo::~SystemInfo(this=0x0000000100d05b80) at SystemInfo.cpp:71:3
frame #13: 0x00000001000995dc infinisim`Pinetime::Applications::Screens::SystemInfo::~SystemInfo(this=0x0000000100d05b80) at SystemInfo.cpp:70:27
frame #14: 0x0000000100099608 infinisim`Pinetime::Applications::Screens::SystemInfo::~SystemInfo(this=0x0000000100d05b80) at SystemInfo.cpp:70:27
frame #15: 0x000000010006e354 infinisim`std::__1::default_delete<Pinetime::Applications::Screens::Screen>::operator(this=0x0000000100162ba8, __ptr=0x0000000100d05b80)(Pinetime::Applications::Screens::Screen*) const at unique_ptr.h:57:5
frame #16: 0x000000010006e2c8 infinisim`std::__1::unique_ptr<Pinetime::Applications::Screens::Screen, std::__1::default_delete<Pinetime::Applications::Screens::Screen> >::reset(this=0x0000000100162ba8, __p=0x0000000000000000) at unique_ptr.h:318:7
frame #17: 0x000000010006e258 infinisim`std::__1::unique_ptr<Pinetime::Applications::Screens::Screen, std::__1::default_delete<Pinetime::Applications::Screens::Screen> >::~unique_ptr(this=0x0000000100162ba8) at unique_ptr.h:272:19
frame #18: 0x000000010006e1fc infinisim`std::__1::unique_ptr<Pinetime::Applications::Screens::Screen, std::__1::default_delete<Pinetime::Applications::Screens::Screen> >::~unique_ptr(this=0x0000000100162ba8) at unique_ptr.h:272:17
frame #19: 0x000000010006e1b0 infinisim`Pinetime::Applications::DisplayApp::~DisplayApp(this=0x0000000100161ab8) at DisplayApp.h:48:11
frame #20: 0x000000010006c4d0 infinisim`Pinetime::Applications::DisplayApp::~DisplayApp(this=0x0000000100161ab8) at DisplayApp.h:48:11
frame #21: 0x000000018ec1fdd0 libsystem_c.dylib`__cxa_finalize_ranges + 464
frame #22: 0x000000018ec1fb74 libsystem_c.dylib`exit + 44
frame #23: 0x00000001000f91a8 infinisim`sdl_event_handler(t=0x0000000000000000) at monitor.c:274:9
frame #24: 0x00000001000f90d4 infinisim`sdl_event_handler_mainthread at monitor.c:222:5
frame #25: 0x000000010006c788 infinisim`main(argc=1, argv=0x000000016fdf4ce8) at main.cpp:1042:11
frame #26: 0x000000010049d08c dyld`start + 520
As you can see at the very bottom, the cause is the `exit()` call here:
```c
if(sdl_quit_qry) {
monitor_sdl_clean_up();
exit(0);
}
It looks like a use-after-free somehow (IsRunning(this=0x0000000000000000)
). I tried various things, including moving it to the top of the function and calling lv_task_del()
for all registered tasks just before exiting, but nothing changes. For now I just added a very dirty system('killall -9 infinisim'); sleep(3);
above the exit(0);
so that I can at least exit the program without macOS complaining Application quit unexpectedly
(it's a popup, so quite annoying). :DDDDD
2023-10-30 20:02:37.730857+0100 infinisim[55130:3911023] -[AGXG13XFamilyCommandBuffer blitCommandEncoderCommon:], line 258: error 'A command encoder is already encoding to this command buffer'
-[AGXG13XFamilyCommandBuffer blitCommandEncoderCommon:]:258: failed assertion `A command encoder is already encoding to this command buffer'
Process 55130 stopped
* thread #8, name = 'displayapp', stop reason = hit program assert
frame #4: 0x000000019772add8 Metal`MTLReportFailure.cold.1 + 56
Metal`bool MTLGetEnvCase<MTLErrorModeType>(char const*, MTLErrorModeType&, std::__1::vector<std::__1::pair<char const*, MTLErrorModeType>, std::__1::allocator<std::__1::pair<char const*, MTLErrorModeType> > > const&) (.cold.1):
-> 0x19772add8 <+0>: pacibsp
0x19772addc <+4>: sub sp, sp, #0x40
0x19772ade0 <+8>: stp x22, x21, [sp, #0x10]
0x19772ade4 <+12>: stp x20, x19, [sp, #0x20]
Target 0: (infinisim) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = hit program assert
frame #0: 0x000000018ecfed98 libsystem_kernel.dylib`__pthread_kill + 8
frame #1: 0x000000018ed33ee0 libsystem_pthread.dylib`pthread_kill + 288
frame #2: 0x000000018ec6e340 libsystem_c.dylib`abort + 168
frame #3: 0x000000018ec6d754 libsystem_c.dylib`__assert_rtn + 272
* frame #4: 0x000000019772add8 Metal`MTLReportFailure.cold.1 + 56
frame #5: 0x0000000197714790 Metal`MTLReportFailure + 480
frame #6: 0x00000001d5944a34 AGXMetalG13X`___lldb_unnamed_symbol956$$AGXMetalG13X + 620
frame #7: 0x000000010087e8ac libSDL2-2.0.0.dylib`METAL_UpdateTextureInternal + 460
frame #8: 0x000000010087c70c libSDL2-2.0.0.dylib`METAL_UpdateTexture + 120
frame #9: 0x000000010080031c libSDL2-2.0.0.dylib`SDL_UpdateTexture_REAL + 568
frame #10: 0x0000000100862d4c libSDL2-2.0.0.dylib`SDL_UpdateWindowTexture + 116
frame #11: 0x0000000100860498 libSDL2-2.0.0.dylib`SDL_UpdateWindowSurface_REAL + 108
frame #12: 0x00000001008053c0 libSDL2-2.0.0.dylib`SDL_RenderPresent_REAL + 104
frame #13: 0x00000001000f92ac infinisim`window_update(m=0x0000000100163248) at monitor.c:397:5
frame #14: 0x00000001000f915c infinisim`sdl_event_handler(t=0x0000000000000000) at monitor.c:262:21
frame #15: 0x00000001000f90b4 infinisim`sdl_event_handler_mainthread at monitor.c:224:5
frame #16: 0x000000010006c768 infinisim`main(argc=1, argv=0x000000016fdf4ce8) at main.cpp:1042:11
frame #17: 0x000000010049d08c dyld`start + 520
My guess is something is (now) causing it to update the display multiple times simultaneously with something else. This may have something to do with restarting the program too fast, or even the fact that I'm running it through lldb
. It does seem to be related to Apple's Metal. I'll just ignore this for the time being.
Awesome work! Please open a PR to update the simulator files/submodules as you've mentioned. Then it is easier for me to check on the Linux side if everything still works, and you'll get the attribution for the fix ;)
Some changes are in third-party libs tho (like https://github.com/lvgl/lv_drivers), how would we go about that? :> Afaik you can't patch them from the parent project.
Also I'll be going away for a week, leaving very early tomorrow. So whatever needs to be done, will happen after a week. =]
I've been trying to get the simulator working on my Mac (M1), but it keeps failing when linking the main
infinisim
executable to what I assume is thelvgl
library:In the readme it says:
I verified that the submodule is indeed checked out. Also, I can build the main firmware itself without problems. For the simulator's dependencies I just ran
npm install
from the repo root, since there's apackage.json
. For building I simply used the given instructions: