Files
VoxelForge/Standalone/Source/StandaloneCoreScheduler.h
T
Fr0zkaandClaude Opus 5.5 557f6aac30 standalone: VoxelForgeCore library -- the fast generator behind a clean C++20 API, output unchanged
- Standalone/Core: public header VoxelForgeCore.h (no Unreal types), static library target,
  ResolvedWorldSnapshot (from the schema-v1 JSON), World (compile, generateTile, density/sign,
  point/segment solid, classifyBox), host-supplied JobScheduler (the core starts no threads),
  sequenced, idempotent EditTransaction returning affected tiles per LOD.
- The viewer, benchmarks, edits and audits run on Core; StreamingWorld stays host code.
- Same as round 11: 11 hash rows at W1 and W8, routes 5642/5642, look/passage/far audits pass,
  bad-shell control fails, six fixed captures byte-identical; cold W8 1.025 s vs 1.041 s reference.
- SHARED_CPP_LIBRARY_PLAN.md rewritten for a UBT module compiling the same sources via shims.
Open issue before Unreal: Core's implementation still compiles the Compat stand-ins named like
Unreal types (TArray, FVector...); inside a UE binary that is an ODR clash. Must be namespaced.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-05 11:39:02 +02:00

53 lines
2.0 KiB
C++

#pragma once
#include "VoxelForgeCore/VoxelForgeCore.h"
#include <algorithm>
#include <atomic>
#include <chrono>
#include <thread>
#include <vector>
// This executor belongs to the standalone host. VoxelForgeCore submits indexed work but never
// creates or owns worker threads.
class FStandaloneCoreScheduler final : public VoxelForgeCore::JobScheduler
{
public:
double parallel_for(std::size_t count,std::size_t batchSize,std::size_t workerLimit,
void* context,Task task,double* workerActiveSeconds,
std::size_t workerActiveCapacity) const override
{
if(count==0)return 0.0;
const std::size_t workers=std::clamp<std::size_t>(workerLimit,1,
std::min<std::size_t>(8,std::max<std::size_t>(1,count)));
const std::size_t grain=std::max<std::size_t>(1,batchSize);
std::atomic<std::size_t> next{0};
std::vector<double> active(workers,0.0);
std::vector<std::thread> threads;
threads.reserve(workers);
for(std::size_t worker=0;worker<workers;++worker)
threads.emplace_back([&,worker]
{
const auto started=std::chrono::steady_clock::now();
for(;;)
{
const std::size_t first=next.fetch_add(grain,std::memory_order_relaxed);
if(first>=count)break;
const std::size_t end=first+std::min(grain,count-first);
for(std::size_t index=first;index<end;++index)task(context,index,worker);
}
active[worker]=std::chrono::duration<double>(
std::chrono::steady_clock::now()-started).count();
});
for(std::thread& thread:threads)thread.join();
double total=0.0;
for(std::size_t worker=0;worker<workers;++worker)
{
total+=active[worker];
if(workerActiveSeconds&&worker<workerActiveCapacity)
workerActiveSeconds[worker]=active[worker];
}
return total;
}
};