diff --git a/rugnux/RugnuxPreScan.cpp b/rugnux/RugnuxPreScan.cpp index c5020f201..e965f3ca7 100644 --- a/rugnux/RugnuxPreScan.cpp +++ b/rugnux/RugnuxPreScan.cpp @@ -459,6 +459,21 @@ void Rugnux::PreScan(int start_image, int images_to_process, int frame_count, Ru std::vector workers(nworkers); std::vector worker_built(nworkers, 0); + // fn(t) on threads of their own, thread t on card t % gpus as an image-loop worker is: worker + // t's engine is built on that card and must be used, and freed, with it current - and each + // tier runs on fresh threads. Which card finds a frame's spots does not change them. + const auto on_workers = [&](auto &&fn) { + std::vector> futures; + futures.reserve(nworkers); + for (size_t t = 0; t < nworkers; t++) + futures.emplace_back(std::async(std::launch::async, [&, t] { + if (const int32_t gpus = get_gpu_count(); gpus > 0) + pin_gpu(static_cast(t) % gpus); + fn(t); + })); + for (auto &f : futures) f.get(); + }; + bool width_settled = !want_width; // The bandwidth reads the same spots, but its slope needs more of them than the width does: // the tiers carry on past the width's for it alone, until the pool is large enough. @@ -467,40 +482,36 @@ void Rugnux::PreScan(int start_image, int images_to_process, int frame_count, Ru size_t phase_begin = 0; for (const size_t phase_end : tier_end) { std::atomic next{phase_begin}; - std::vector> futures; - futures.reserve(nworkers); - for (size_t t = 0; t < nworkers; t++) - futures.emplace_back(std::async(std::launch::async, [&, t] { - if (!worker_built[t]) { - workers[t] = make_worker(); - worker_built[t] = 1; + on_workers([&](size_t t) { + if (!worker_built[t]) { + workers[t] = make_worker(); + worker_built[t] = 1; + } + PreScanWorker &w = workers[t]; + for (size_t v = next.fetch_add(1); v < phase_end; v = next.fetch_add(1)) { + const size_t i = visit[v]; + const int ordinal = ordinals[i]; + const int image_idx = start_image + ordinal * config_.stride; + const bool for_width = !width_settled; + const bool for_shape = width_settled && !shapes_settled; + if (!(for_width || for_shape)) + continue; // nothing to measure on this frame + bool read = false; + try { + read = reader_.ReadRawImage(image_idx, w.raw_image); + } catch (const std::exception &e) { + if (IsFatalResourceError(e)) throw; + logger.Warning("Pre-scan: failed to load image {}: {}", image_idx, + e.what()); + continue; } - PreScanWorker &w = workers[t]; - for (size_t v = next.fetch_add(1); v < phase_end; v = next.fetch_add(1)) { - const size_t i = visit[v]; - const int ordinal = ordinals[i]; - const int image_idx = start_image + ordinal * config_.stride; - const bool for_width = !width_settled; - const bool for_shape = width_settled && !shapes_settled; - if (!(for_width || for_shape)) - continue; // nothing to measure on this frame - bool read = false; - try { - read = reader_.ReadRawImage(image_idx, w.raw_image); - } catch (const std::exception &e) { - if (IsFatalResourceError(e)) throw; - logger.Warning("Pre-scan: failed to load image {}: {}", image_idx, - e.what()); - continue; - } - if (!read) continue; + if (!read) continue; - if (for_shape) shape_only[i] = 1; - find_spots(w, w.raw_image.image, image_idx, for_width || for_shape, curves_of[i], - spot_q_of[i], footprint_of[i]); - } - })); - for (auto &f : futures) f.get(); + if (for_shape) shape_only[i] = 1; + find_spots(w, w.raw_image.image, image_idx, for_width || for_shape, curves_of[i], + spot_q_of[i], footprint_of[i]); + } + }); phase_begin = phase_end; // What this tier makes of the width, pooled in sample order so the answer does not @@ -522,6 +533,7 @@ void Rugnux::PreScan(int start_image, int images_to_process, int frame_count, Ru shape_curves.insert(shape_curves.end(), c.begin(), c.end()); shapes_settled = width_settled && shape_curves.size() >= spot_width::BANDWIDTH_POOL_SPOTS; } + on_workers([&](size_t t) { PreScanWorker released = std::move(workers[t]); }); for (size_t i = 0; i < ordinals.size(); i++) if (!shape_only[i])