- 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
53 lines
2.0 KiB
C++
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;
|
|
}
|
|
};
|