|
|
@ -19,44 +19,36 @@ auto Name() -> std::string; |
|
|
|
|
|
|
|
|
|
|
|
template <> |
|
|
|
template <> |
|
|
|
auto Name<Type::kUi>() -> std::string { |
|
|
|
auto Name<Type::kUi>() -> std::string { |
|
|
|
return "LVGL"; |
|
|
|
return "ui"; |
|
|
|
} |
|
|
|
} |
|
|
|
template <> |
|
|
|
template <> |
|
|
|
auto Name<Type::kUiFlush>() -> std::string { |
|
|
|
auto Name<Type::kAudioDecoder>() -> std::string { |
|
|
|
return "DISPLAY"; |
|
|
|
return "audio_dec"; |
|
|
|
} |
|
|
|
} |
|
|
|
template <> |
|
|
|
template <> |
|
|
|
auto Name<Type::kFileStreamer>() -> std::string { |
|
|
|
auto Name<Type::kAudioConverter>() -> std::string { |
|
|
|
return "FSTREAMER"; |
|
|
|
return "audio_conv"; |
|
|
|
} |
|
|
|
|
|
|
|
template <> |
|
|
|
|
|
|
|
auto Name<Type::kAudio>() -> std::string { |
|
|
|
|
|
|
|
return "AUDIO"; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
template <> |
|
|
|
|
|
|
|
auto Name<Type::kMixer>() -> std::string { |
|
|
|
|
|
|
|
return "MIXER"; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
template <> |
|
|
|
template <> |
|
|
|
auto Name<Type::kDatabase>() -> std::string { |
|
|
|
auto Name<Type::kDatabase>() -> std::string { |
|
|
|
return "DB"; |
|
|
|
return "db_fg"; |
|
|
|
} |
|
|
|
} |
|
|
|
template <> |
|
|
|
template <> |
|
|
|
auto Name<Type::kDatabaseBackground>() -> std::string { |
|
|
|
auto Name<Type::kDatabaseBackground>() -> std::string { |
|
|
|
return "DB_BG"; |
|
|
|
return "db_bg"; |
|
|
|
} |
|
|
|
} |
|
|
|
template <> |
|
|
|
template <> |
|
|
|
auto Name<Type::kNvsWriter>() -> std::string { |
|
|
|
auto Name<Type::kNvsWriter>() -> std::string { |
|
|
|
return "NVS"; |
|
|
|
return "nvs"; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <Type t> |
|
|
|
template <Type t> |
|
|
|
auto AllocateStack() -> cpp::span<StackType_t>; |
|
|
|
auto AllocateStack() -> cpp::span<StackType_t>; |
|
|
|
|
|
|
|
|
|
|
|
// Decoders run on the audio task, and these sometimes require a fairly large
|
|
|
|
// Decoders often require a very large amount of stack space, since they aren't
|
|
|
|
// amount of stack space.
|
|
|
|
// usually written with embedded use cases in mind.
|
|
|
|
template <> |
|
|
|
template <> |
|
|
|
auto AllocateStack<Type::kAudio>() -> cpp::span<StackType_t> { |
|
|
|
auto AllocateStack<Type::kAudioDecoder>() -> cpp::span<StackType_t> { |
|
|
|
std::size_t size = 64 * 1024; |
|
|
|
std::size_t size = 64 * 1024; |
|
|
|
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)), |
|
|
|
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)), |
|
|
|
size}; |
|
|
|
size}; |
|
|
@ -69,29 +61,15 @@ auto AllocateStack<Type::kUi>() -> cpp::span<StackType_t> { |
|
|
|
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)), |
|
|
|
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)), |
|
|
|
size}; |
|
|
|
size}; |
|
|
|
} |
|
|
|
} |
|
|
|
// UI flushes *must* be done from internal RAM. Thankfully, there is very little
|
|
|
|
|
|
|
|
// stack required to perform them, and the amount of stack needed is fixed.
|
|
|
|
|
|
|
|
template <> |
|
|
|
template <> |
|
|
|
auto AllocateStack<Type::kUiFlush>() -> cpp::span<StackType_t> { |
|
|
|
// PCM conversion and resampling uses a very small amount of stack. It works
|
|
|
|
std::size_t size = 1024; |
|
|
|
// entirely with PSRAM-allocated buffers, so no real speed gain from allocating
|
|
|
|
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_DEFAULT)), |
|
|
|
// it internally.
|
|
|
|
size}; |
|
|
|
auto AllocateStack<Type::kAudioConverter>() -> cpp::span<StackType_t> { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <> |
|
|
|
|
|
|
|
auto AllocateStack<Type::kFileStreamer>() -> cpp::span<StackType_t> { |
|
|
|
|
|
|
|
std::size_t size = 4 * 1024; |
|
|
|
std::size_t size = 4 * 1024; |
|
|
|
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)), |
|
|
|
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)), |
|
|
|
size}; |
|
|
|
size}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <> |
|
|
|
|
|
|
|
auto AllocateStack<Type::kMixer>() -> cpp::span<StackType_t> { |
|
|
|
|
|
|
|
std::size_t size = 4 * 1024; |
|
|
|
|
|
|
|
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)), |
|
|
|
|
|
|
|
size}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Leveldb is designed for non-embedded use cases, where stack space isn't so
|
|
|
|
// Leveldb is designed for non-embedded use cases, where stack space isn't so
|
|
|
|
// much of a concern. It therefore uses an eye-wateringly large amount of stack.
|
|
|
|
// much of a concern. It therefore uses an eye-wateringly large amount of stack.
|
|
|
|
template <> |
|
|
|
template <> |
|
|
@ -114,8 +92,8 @@ auto AllocateStack<Type::kNvsWriter>() -> cpp::span<StackType_t> { |
|
|
|
size}; |
|
|
|
size}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 2048 bytes in internal ram
|
|
|
|
// 2 KiB in internal ram
|
|
|
|
// 302 KiB in external ram.
|
|
|
|
// 612 KiB in external ram.
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Please keep the priorities below in descending order for better readability. |
|
|
|
* Please keep the priorities below in descending order for better readability. |
|
|
@ -124,39 +102,22 @@ auto AllocateStack<Type::kNvsWriter>() -> cpp::span<StackType_t> { |
|
|
|
template <Type t> |
|
|
|
template <Type t> |
|
|
|
auto Priority() -> UBaseType_t; |
|
|
|
auto Priority() -> UBaseType_t; |
|
|
|
|
|
|
|
|
|
|
|
// NVS writing requires suspending one of our cores, and disabling tasks with
|
|
|
|
// Realtime audio is the entire point of this device, so give these tasks the
|
|
|
|
// their stacks in PSRAM. Get it over and done with as soon as possible.
|
|
|
|
|
|
|
|
template <> |
|
|
|
|
|
|
|
auto Priority<Type::kNvsWriter>() -> UBaseType_t { |
|
|
|
|
|
|
|
return 13; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Realtime audio is the entire point of this device, so give this task the
|
|
|
|
|
|
|
|
// highest priority.
|
|
|
|
// highest priority.
|
|
|
|
template <> |
|
|
|
template <> |
|
|
|
auto Priority<Type::kMixer>() -> UBaseType_t { |
|
|
|
auto Priority<Type::kAudioDecoder>() -> UBaseType_t { |
|
|
|
return 12; |
|
|
|
return 18; |
|
|
|
} |
|
|
|
} |
|
|
|
template <> |
|
|
|
template <> |
|
|
|
auto Priority<Type::kAudio>() -> UBaseType_t { |
|
|
|
auto Priority<Type::kAudioConverter>() -> UBaseType_t { |
|
|
|
return 11; |
|
|
|
return 18; |
|
|
|
} |
|
|
|
} |
|
|
|
// After audio issues, UI jank is the most noticeable kind of scheduling-induced
|
|
|
|
// After audio issues, UI jank is the most noticeable kind of scheduling-induced
|
|
|
|
// slowness that the user is likely to notice or care about. Therefore we place
|
|
|
|
// slowness that the user is likely to notice or care about. Therefore we place
|
|
|
|
// this task directly below audio in terms of priority.
|
|
|
|
// this task directly below audio in terms of priority. Note that during audio
|
|
|
|
|
|
|
|
// playback, this priority will be downgraded.
|
|
|
|
template <> |
|
|
|
template <> |
|
|
|
auto Priority<Type::kUi>() -> UBaseType_t { |
|
|
|
auto Priority<Type::kUi>() -> UBaseType_t { |
|
|
|
return 9; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// UI flushing should use the same priority as the UI task, so as to maximise
|
|
|
|
|
|
|
|
// the chance of the happy case: one of our cores is writing to the screen,
|
|
|
|
|
|
|
|
// whilst the other is simultaneously preparing the next buffer to be flushed.
|
|
|
|
|
|
|
|
template <> |
|
|
|
|
|
|
|
auto Priority<Type::kUiFlush>() -> UBaseType_t { |
|
|
|
|
|
|
|
return 9; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <> |
|
|
|
|
|
|
|
auto Priority<Type::kFileStreamer>() -> UBaseType_t { |
|
|
|
|
|
|
|
return 10; |
|
|
|
return 10; |
|
|
|
} |
|
|
|
} |
|
|
|
// Database interactions are all inherently async already, due to their
|
|
|
|
// Database interactions are all inherently async already, due to their
|
|
|
@ -165,11 +126,18 @@ auto Priority<Type::kFileStreamer>() -> UBaseType_t { |
|
|
|
// priority.
|
|
|
|
// priority.
|
|
|
|
template <> |
|
|
|
template <> |
|
|
|
auto Priority<Type::kDatabase>() -> UBaseType_t { |
|
|
|
auto Priority<Type::kDatabase>() -> UBaseType_t { |
|
|
|
return 8; |
|
|
|
return 2; |
|
|
|
} |
|
|
|
} |
|
|
|
template <> |
|
|
|
template <> |
|
|
|
auto Priority<Type::kDatabaseBackground>() -> UBaseType_t { |
|
|
|
auto Priority<Type::kDatabaseBackground>() -> UBaseType_t { |
|
|
|
return 7; |
|
|
|
return 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// NVS writing requires suspending one of our cores, and disabling tasks with
|
|
|
|
|
|
|
|
// their stacks in PSRAM. Only do it when there's not more important work
|
|
|
|
|
|
|
|
// pending.
|
|
|
|
|
|
|
|
template <> |
|
|
|
|
|
|
|
auto Priority<Type::kNvsWriter>() -> UBaseType_t { |
|
|
|
|
|
|
|
return 2; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <Type t> |
|
|
|
template <Type t> |
|
|
@ -184,10 +152,6 @@ auto WorkerQueueSize<Type::kDatabaseBackground>() -> std::size_t { |
|
|
|
return 8; |
|
|
|
return 8; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <> |
|
|
|
|
|
|
|
auto WorkerQueueSize<Type::kUiFlush>() -> std::size_t { |
|
|
|
|
|
|
|
return 2; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
template <> |
|
|
|
template <> |
|
|
|
auto WorkerQueueSize<Type::kNvsWriter>() -> std::size_t { |
|
|
|
auto WorkerQueueSize<Type::kNvsWriter>() -> std::size_t { |
|
|
|
return 2; |
|
|
|
return 2; |
|
|
|