Files
Toastconcern--FrameStudio/third_party/qhe/core/parallel.h
T
ToastconcernandClaude Opus 5.5 f7345a5ac3 Frame Studio and Frame Home: cross-platform builds
Frame Studio now builds for Windows (x64, x86, ARM64), Linux (x64, x86, ARM64)
and macOS (Apple Silicon, Intel):
- studio/platform.h with Win32 and POSIX implementations (dialogs through
  tinyfiledialogs on Linux/macOS), a GLFW window beside the Win32 one, and a
  platform-neutral frame pacer
- MoltenVK support in the renderer (portability enumeration) and bundled in
  the macOS app
- 32-bit builds: large-address-aware, LZMA packing within a 32-bit address space
- unaligned file reads in the loaders go through loadUnaligned (no UB)
- scripts/build-studio.sh, studio/build_windows.bat [x64|x86|arm64],
  cmake/llvm-mingw.cmake, and a GitHub Actions workflow building every target

Frame Home installs without Studio: frame/install-frame-home.{sh,ps1} and
"Install Frame Home.bat" (packaged as FrameHome-installer.zip).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 12:03:25 -05:00

83 lines
2.6 KiB
C++

#pragma once
// A small persistent thread pool for per-frame work split across cores: the CPU animation's skinning and the
// renderer's vertex rebuilds. forEach(n, fn) runs fn(i) for every i < n on the pool and the calling thread, and
// returns when all are done. Items are handed out one at a time (they differ a lot in size). Not re-entrant: a
// forEach inside fn runs serially.
#include <algorithm>
#include <atomic>
#include <condition_variable>
#include <cstddef>
#include <functional>
#include <mutex>
#include <thread>
#include <vector>
namespace par {
class Pool {
public:
static Pool& get() { static Pool p; return p; }
void forEach(size_t n, const std::function<void(size_t)>& fn) {
if (n == 0) return;
if (n == 1 || threads_.empty() || inside_) { for (size_t i = 0; i < n; ++i) fn(i); return; }
std::unique_lock<std::mutex> busy(run_); // one forEach at a time (from any thread)
{
std::lock_guard<std::mutex> g(m_);
fn_ = &fn; n_ = n; next_.store(0); left_ = threads_.size();
++gen_;
}
cv_.notify_all();
work();
std::unique_lock<std::mutex> g(m_);
done_.wait(g, [&] { return left_ == 0; });
fn_ = nullptr;
}
~Pool() {
{ std::lock_guard<std::mutex> g(m_); stop_ = true; ++gen_; }
cv_.notify_all();
for (auto& t : threads_) t.join();
}
private:
Pool() {
const unsigned hw = std::thread::hardware_concurrency();
const unsigned n = hw > 2 ? std::min(hw - 2, 6u) : 0; // leave a core for the render thread's other work
for (unsigned i = 0; i < n; ++i) threads_.emplace_back([this] { loop(); });
}
void work() {
inside_ = true;
for (size_t i; (i = next_.fetch_add(1)) < n_;) (*fn_)(i);
inside_ = false;
}
void loop() {
uint64_t seen = 0;
for (;;) {
{
std::unique_lock<std::mutex> g(m_);
cv_.wait(g, [&] { return gen_ != seen; });
seen = gen_;
if (stop_) return;
}
work();
std::lock_guard<std::mutex> g(m_);
if (--left_ == 0) done_.notify_one();
}
}
std::vector<std::thread> threads_;
std::mutex m_, run_;
std::condition_variable cv_, done_;
const std::function<void(size_t)>* fn_ = nullptr;
size_t n_ = 0, left_ = 0;
std::atomic<size_t> next_{0};
uint64_t gen_ = 0;
bool stop_ = false;
static inline thread_local bool inside_ = false;
};
inline void forEach(size_t n, const std::function<void(size_t)>& fn) { Pool::get().forEach(n, fn); }
} // namespace par