#include "kutuk_async.hpp" #include #include #include "fiko/core/gunluk.hpp" namespace fiko { using namespace std::chrono; namespace { // Stok grisi (kutukMeshYenile ile ayni — gorsel tutarlilik). constexpr f32 GRI[3] = {0.62f, 0.64f, 0.68f}; // kutuk::MeshVeri (duz float) → render MeshVeri (Vertex, gri). Worker thread'de // yapilir → main-thread'e yalniz GPU'ya hazir veri gecer (donusum maliyeti worker'da). void meshDonustur(const kutuk::MeshVeri& km, MeshVeri& veri) { const u32 vsay = km.vertexSayi(); veri.vertexler.clear(); veri.vertexler.resize(vsay); for (u32 i = 0; i < vsay; ++i) { Vertex& v = veri.vertexler[i]; v.konum[0] = km.pozisyon[i * 3 + 0]; v.konum[1] = km.pozisyon[i * 3 + 1]; v.konum[2] = km.pozisyon[i * 3 + 2]; v.normal[0] = km.normal[i * 3 + 0]; v.normal[1] = km.normal[i * 3 + 1]; v.normal[2] = km.normal[i * 3 + 2]; v.renk[0] = GRI[0]; v.renk[1] = GRI[1]; v.renk[2] = GRI[2]; v.altKimlik = 0; } veri.indeksler = km.indeks; veri.sinir = km.sinir; } } // namespace KutukAsyncCarve::~KutukAsyncCarve() { durdur(); } void KutukAsyncCarve::baslat(std::unique_ptr motor, std::vector noktalar, f32 batchMs, f32 meshMs, int kip) { durdur(); // varsa eskiyi kapat (idempotent) if (!motor || noktalar.empty()) return; motor_ = std::move(motor); noktalar_ = std::move(noktalar); batchMs_ = batchMs; meshMs_ = meshMs; kip_ = kip; dur_.store(false); hedef_.store(0); oyulan_.store(0); sifirla_.store(false); hacim_.store(0.0); { std::lock_guard lk(ucKilit_); ucVar_ = false; } kontroller_.clear(); undoTokenlar_.clear(); const int son = (int)noktalar_.size() - 1; // Undo token aralığı (İNCE geri-scrub, anında): motor undo destekliyorsa her ~200 // hamlede bir token. Geri-scrub en yakın token'a undoGeri → yalnız o kadar voxel geri. undoAralik_ = (motor_->undoDestekli() && son > 0) ? std::max(1, std::min(200, son)) : 0; // Checkpoint aralığı: undo taşarsa fallback anchor (kaba, bellek bütçeli). kontrolAralik_ = 0; const size_t bayt = motor_->durumBayt(); if (bayt > 0 && son > 0) { int maks = (int)std::min(8, kontrolBellekBucesi_ / std::max(bayt, 1)); if (maks >= 2) kontrolAralik_ = std::max(1, son / maks); } { std::lock_guard lk(meshKilit_); meshHazir_ = false; hazirMesh_ = {}; } calisiyor_ = true; isci_ = std::thread([this] { dongu(); }); } void KutukAsyncCarve::durdur() { if (!calisiyor_) return; dur_.store(true); { std::lock_guard lk(uyanKilit_); } uyandir_.notify_all(); if (isci_.joinable()) isci_.join(); calisiyor_ = false; motor_.reset(); noktalar_.clear(); noktalar_.shrink_to_fit(); kontroller_.clear(); undoTokenlar_.clear(); std::lock_guard lk(meshKilit_); hazirMesh_ = {}; meshHazir_ = false; } void KutukAsyncCarve::hedefAta(int poz, Vec3d ucPoz, bool kismiVar) { if (!calisiyor_) return; { std::lock_guard lk(ucKilit_); ucPoz_ = ucPoz; ucVar_ = kismiVar; } const int mevcut = hedef_.load(std::memory_order_relaxed); hedef_.store(poz, std::memory_order_relaxed); if (poz == mevcut && !kismiVar) return; // değişim yok → uyandırma { std::lock_guard lk(uyanKilit_); } uyandir_.notify_all(); } void KutukAsyncCarve::sifirlaVeHedef(int poz) { if (!calisiyor_) return; hedef_.store(poz, std::memory_order_relaxed); sifirla_.store(true, std::memory_order_relaxed); { std::lock_guard lk(ucKilit_); ucVar_ = false; } { std::lock_guard lk(uyanKilit_); } uyandir_.notify_all(); } void KutukAsyncCarve::keskinAta(bool v) { keskin_.store(v, std::memory_order_relaxed); // worker mesh döngüsünde uygular + re-mesh if (!calisiyor_) return; { std::lock_guard lk(uyanKilit_); } uyandir_.notify_all(); } bool KutukAsyncCarve::meshDevral(MeshVeri& disari) { std::lock_guard lk(meshKilit_); if (!meshHazir_) return false; disari = std::move(hazirMesh_); hazirMesh_ = {}; meshHazir_ = false; return true; } void KutukAsyncCarve::dongu() { const int son = (int)noktalar_.size() - 1; int oyulan = 0; bool sonNoktaVar = false; Vec3d sonNokta{}; Vec3d sonUcPoz{}; bool sonUcVar = false; bool sonKeskin = false; auto sonMesh = steady_clock::now(); const char* kipAd[] = {"Dexel3", "ZBuffer6", "SDF+MC", "TriDexelPro"}; auto olcPencere = steady_clock::now(); f64 carveBirikMs = 0.0, meshBirikMs = 0.0, hamleBirik = 0.0; std::vector chunk; // batch carve tamponu (yeniden kullanılır) // Committed konumu checkpoint'le (aralık multiple'ında; grid partial'sız net). auto kontrolKaydet = [&](int poz) { if (kontrolAralik_ <= 0 || poz <= 0) return; if (!kontroller_.empty() && poz - kontroller_.back().first < kontrolAralik_) return; if (auto s = motor_->durumKaydet()) kontroller_.emplace_back(poz, std::move(s)); }; // İnce undo token (ucuz): geri-scrub'ta en yakınına undoGeri → anında. Chunk // sonlarında çağrılır → min-aralık dedup (modulo değil). auto undoTokenKaydet = [&](int poz) { if (undoAralik_ <= 0 || poz <= 0) return; if (!motor_->undoDestekli()) return; // jurnal taştıysa token alma if (!undoTokenlar_.empty() && poz - undoTokenlar_.back().first < undoAralik_) return; undoTokenlar_.emplace_back(poz, motor_->undoIsaret()); }; while (!dur_.load(std::memory_order_relaxed)) { // HARD reset (kutukSifirla / yeni yol): grid + checkpoint + token temizle. if (sifirla_.exchange(false, std::memory_order_relaxed)) { motor_->sifirla(); oyulan = 0; sonNoktaVar = false; kontroller_.clear(); undoTokenlar_.clear(); hacim_.store(0.0, std::memory_order_relaxed); oyulan_.store(0, std::memory_order_relaxed); } int hedef = hedef_.load(std::memory_order_relaxed); if (hedef > son) hedef = son; if (hedef < 0) hedef = 0; // GERİ-SCRUB: hedef committed'in gerisinde. ÖNCE undo (ince, ANINDA — yalnız // değişen voxelleri geri al, grid kopyası yok); erişemezse checkpoint fallback. if (hedef < oyulan) { int yuklPoz = -1; // 1) UNDO — en yakın ≤ hedef token'a geri al. if (undoAralik_ > 0 && motor_->undoDestekli() && !undoTokenlar_.empty()) { for (auto it = undoTokenlar_.rbegin(); it != undoTokenlar_.rend(); ++it) if (it->first <= hedef) { if (motor_->undoGeri(it->second)) yuklPoz = it->first; break; } if (yuklPoz >= 0) // jurnal kısaldı → ileri token'lar geçersiz while (!undoTokenlar_.empty() && undoTokenlar_.back().first > yuklPoz) undoTokenlar_.pop_back(); } // 2) CHECKPOINT fallback (undo yok/erişemedi): en yakın full snapshot. if (yuklPoz < 0) { bool yuklendi = false; for (auto it = kontroller_.rbegin(); it != kontroller_.rend(); ++it) if (it->first <= hedef) { yuklendi = motor_->durumYukle(it->second); if (yuklendi) yuklPoz = it->first; break; } if (!yuklendi) { motor_->sifirla(); yuklPoz = 0; } undoTokenlar_.clear(); // checkpoint/reset jurnali sıfırladı } while (!kontroller_.empty() && kontroller_.back().first > yuklPoz) kontroller_.pop_back(); oyulan = yuklPoz; sonNoktaVar = (oyulan >= 1); if (oyulan >= 0 && oyulan <= son) sonNokta = noktalar_[(size_t)oyulan].p; hacim_.store(motor_->kaldirilanHacimMm3(), std::memory_order_relaxed); oyulan_.store(oyulan, std::memory_order_relaxed); } // İleri carve — CHUNK'lar hâlinde PARALEL batchUygula (union commutativity → sonuç // seri ile BİREBİR; motor k-slab çok çekirdek). "İleri oyma yavaş" darboğazı burada. const auto t0 = steady_clock::now(); const auto butce = microseconds((long long)(batchMs_ * 1000.0f)); int islenen = 0; constexpr int CHUNK = 512; // batch başına hamle (spawn maliyetini amorti + paralellik) while (oyulan < hedef && !dur_.load(std::memory_order_relaxed) && !sifirla_.load(std::memory_order_relaxed) && steady_clock::now() - t0 < butce) { const int chunkSon = std::min(hedef, oyulan + CHUNK); chunk.clear(); Vec3d prevP = sonNokta; bool prevVar = sonNoktaVar; for (int i = oyulan + 1; i <= chunkSon; ++i) { const KarveNokta& n = noktalar_[(size_t)i]; kutuk::KutukSimMotoru::KarveHamle hm; hm.takim = n.tg; if (!prevVar) { hm.ilk = true; hm.b = n.p; hm.kesme = true; } // ilk damga else { hm.ilk = false; hm.a = prevP; hm.b = n.p; hm.kesme = n.kesme; } chunk.push_back(hm); prevP = n.p; prevVar = true; } motor_->batchUygula(chunk.data(), chunk.size()); islenen += chunkSon - oyulan; oyulan = chunkSon; sonNokta = prevP; sonNoktaVar = true; undoTokenKaydet(oyulan); // token (chunk sonu; geri-scrub anında undo) kontrolKaydet(oyulan); // kaba checkpoint (undo taşma fallback) } if (islenen > 0) { hacim_.store(motor_->kaldirilanHacimMm3(), std::memory_order_relaxed); oyulan_.store(oyulan, std::memory_order_relaxed); } carveBirikMs += duration(steady_clock::now() - t0).count(); hamleBirik += islenen; // İNTERPOLASYON: takım çizgi üzerinde kayarken point[oyulan]→ucPoz kısmi swept // (committed'a EKLENMEZ — union removal idempotent; commit sonrası absorbe olur). // "Kütük boşaltma interpolasyonu takip etmiyor" bunu çözer. Vec3d ucPoz; bool ucVar; { std::lock_guard lk(ucKilit_); ucPoz = ucPoz_; ucVar = ucVar_; } bool partialYapildi = false; const bool ucDegisti = ucVar != sonUcVar || ucPoz.x != sonUcPoz.x || ucPoz.y != sonUcPoz.y || ucPoz.z != sonUcPoz.z; if (ucVar && sonNoktaVar && oyulan < son && (ucDegisti || islenen > 0)) { const KarveNokta& seg = noktalar_[(size_t)(oyulan + 1)]; if (seg.kesme) { motor_->sweptUygula(sonNokta, ucPoz, seg.tg); partialYapildi = true; hacim_.store(motor_->kaldirilanHacimMm3(), std::memory_order_relaxed); } } sonUcPoz = ucPoz; sonUcVar = ucVar; // Mesh çıkarma eşiği (O(toplam) birleştir+dönüştür+upload sık olmasın): // • carve geride (backlog) → adaptif (carve'e öncelik; maks 2×) // • gliding (caught-up + kısmi) → 33ms (30 FPS; her 6ms tik'te değil → O(N) 5× az) // • carve bitti (caught-up, gliding yok) → hemen (son görsel net) const int backlog = hedef - oyulan; const bool caughtUp = oyulan >= hedef; // KESKIN (DC) YALNIZ caught-up'ta: DC seri/tam-grid → yavaş. Carve ederken MC // (hızlı cached-slab) kullan → worker thrash'lemez, kütük akıcı gelir; oyma durunca // keskin görünüm çıkar. keskin_=false ise hep MC. const bool keskinEtkin = keskin_.load(std::memory_order_relaxed) && caughtUp && !ucVar; bool keskinDegisti = false; if (keskinEtkin != sonKeskin) { motor_->keskinMeshAta(keskinEtkin); sonKeskin = keskinEtkin; keskinDegisti = true; } const f32 esikMs = !caughtUp ? meshMs_ * (1.0f + std::min(backlog / 20000.0f, 1.0f)) : (ucVar ? 33.0f : 0.0f); const f32 gecenMs = duration(steady_clock::now() - sonMesh).count(); const bool degisti = islenen > 0 || partialYapildi || keskinDegisti; if (degisti && (keskinDegisti || gecenMs >= esikMs)) { const auto tMesh = steady_clock::now(); kutuk::MeshVeri km; motor_->meshCikar(km); if (km.vertexSayi() > 0 && km.sinir.gecerliMi()) { MeshVeri veri; meshDonustur(km, veri); std::lock_guard lk(meshKilit_); hazirMesh_ = std::move(veri); meshHazir_ = true; } meshBirikMs += duration(steady_clock::now() - tMesh).count(); sonMesh = steady_clock::now(); gunluk::bilgi("[sim-async] kütük[{}]: {} vertex, hacim={:.1f} mm³, işlenen={}/{} (backlog {})", kipAd[kip_ < 4 ? kip_ : 0], km.vertexSayi(), hacim_.load(), oyulan, son, backlog); } // ÖLÇÜM penceresini 1 sn'de bir yayınla (Perf HUD atomikleri). const f64 pencMs = duration(steady_clock::now() - olcPencere).count(); if (pencMs >= 1000.0) { const f64 sn = pencMs / 1000.0; carveMsSn_.store(carveBirikMs / sn, std::memory_order_relaxed); meshMsSn_.store(meshBirikMs / sn, std::memory_order_relaxed); hamleSn_.store(hamleBirik / sn, std::memory_order_relaxed); carveBirikMs = meshBirikMs = hamleBirik = 0.0; olcPencere = steady_clock::now(); } // Yakaladı + kısmi hareket yok → bekle (busy-spin yok). Gliding varsa kısa uyku // (kısmi ~kare hızında güncellensin, ama CPU'yu yakma). if (oyulan >= hedef && !ucVar) { std::unique_lock lk(uyanKilit_); uyandir_.wait_for(lk, milliseconds(10), [&] { return dur_.load(std::memory_order_relaxed) || sifirla_.load(std::memory_order_relaxed) || hedef_.load(std::memory_order_relaxed) != oyulan; }); } else if (oyulan >= hedef && ucVar) { std::unique_lock lk(uyanKilit_); uyandir_.wait_for(lk, milliseconds(6)); // gliding: kısmi güncelleme temposu } } } } // namespace fiko