Files
Toastconcern--FrameStudio/third_party/qhe/core/parallel.h
T
ToastconcernandClaude Opus 5.5 6d0b4fcd2f v0.2.0: splats, copy/paste, faster homes, seat facing, and a full bug sweep
Frame Studio
- Gaussian splats (PLY, SPZ v2-v4), PLY vertex colours, panorama skies
- Copy, cut, paste and duplicate for objects, splats and world items
- Walkable-area rebake moved off the UI thread; viewport one frame sooner;
  refresh-locked pacing (FS_PACE=timer for the old one)
- Fixed: bundle thumbnail use-after-free crash, auto-save deleting
  environments, saves into scratch copies, edits lost on non-ASCII paths,
  unopenable manifests with inf/nan, bundle save data loss, opening a home
  marking it changed, gizmo NaN, long paths, many malformed-file crashes
- Updater verifies sha256, journals the swap and never leaves the exe missing

Frame Home
- Seats turn you to face their way
- Draw batching (Hand Tutorials up to ~20% less GPU time), cut-out EQUAL pass,
  late head-pose read before submit
- Proper queue-family hand-over to the compositor layer; layer descriptor
  generations and seqlocked slots; structural SPIR-V check
- Fixed: hangs/crashes on damaged .fenv files and shaders, render-pass
  validation error, sync hazard in the foveation smoothing pass, unverified
  update downloads, half-done installs

Tests: tests_loco/env_share/spv_check wired into CMake (FRAME_TESTS) and CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VsaXGU27gsrXjDoFtyFJP9
2026-10-25 13:15:07 -05:00

95 lines
3.3 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 <exception>
#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;
// An item that threw (a damaged file's bad_alloc, a parser's throw): rethrown here, once every thread is done
// with fn. Thrown on a pool thread it ended the process; on this one it left the pool running a dead fn.
if (error_) { std::exception_ptr e = error_; error_ = nullptr; g.unlock(); std::rethrow_exception(e); }
}
~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_;) {
try {
(*fn_)(i);
} catch (...) {
std::lock_guard<std::mutex> g(m_);
if (!error_) error_ = std::current_exception();
}
}
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;
std::exception_ptr error_; // the first item of this forEach that threw (under m_)
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