#include "malzeme_gpu.hpp" #include #include #include #include "belge_ciz.hpp" // perfGosterge — malzeme tazeleme ÖLÇÜMÜ #include "belge_ciz.hpp" // perfGosterge — malzeme tazeleme ÖLÇÜMÜ #include "fiko/core/gunluk.hpp" #include "fiko/render/vulkan_baglam.hpp" #include "fiko/sahne/sahne.hpp" namespace fiko { int g_dokuTavan = 4096; f32 g_anizotropi = 16.0f; int malzemeDokuTavanAl() { return g_dokuTavan; } void malzemeDokuTavanAta(int v) { g_dokuTavan = v < 64 ? 64 : (v > 8192 ? 8192 : v); } f32 malzemeAnizotropiAl() { return g_anizotropi; } void malzemeAnizotropiAta(f32 v) { g_anizotropi = v < 1.0f ? 1.0f : (v > 16.0f ? 16.0f : v); } namespace { // 64-bit karıştırıcı (splitmix) — imza hesabı; çakışma olasılığı ihmal edilebilir. inline u64 karistir(u64 h, u64 v) { h ^= v + 0x9E3779B97F4A7C15ull + (h << 6) + (h >> 2); return h; } } // namespace MalzemeGpuKaynak::MalzemeGpuKaynak(VulkanBaglam& vb) : vb_(vb), dokular_(vb, malzemeDokuTavanAl(), malzemeAnizotropiAl()) { const VkDevice aygit = vb_.aygit(); VkDescriptorSetLayoutBinding bag[4]{}; bag[0].binding = 0; // malzeme tablosu bag[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; bag[0].descriptorCount = 1; bag[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; bag[1].binding = 1; // global yüz → malzeme bag[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; bag[1].descriptorCount = 1; bag[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; bag[2].binding = 2; // doku dizisi bag[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bag[2].descriptorCount = DokuDizisi::KAPASITE; bag[2].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; bag[3].binding = 3; // gövde ters model matrisleri (yerel doku izdüşümü) bag[3].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; bag[3].descriptorCount = 1; bag[3].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; VkDescriptorSetLayoutCreateInfo dsl{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO}; dsl.bindingCount = 4; dsl.pBindings = bag; if (vkCreateDescriptorSetLayout(aygit, &dsl, nullptr, &dsYerlesim_) != VK_SUCCESS) { gunluk::hata("Malzeme GPU: descriptor yerlesimi kurulamadi"); return; } VkDescriptorPoolSize havuz[2]{}; havuz[0] = {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3 * HALKA}; havuz[1] = {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, DokuDizisi::KAPASITE * HALKA}; VkDescriptorPoolCreateInfo hb{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO}; hb.maxSets = HALKA; hb.poolSizeCount = 2; hb.pPoolSizes = havuz; if (vkCreateDescriptorPool(aygit, &hb, nullptr, &dsHavuz_) != VK_SUCCESS) return; VkDescriptorSetLayout yerlesimler[HALKA]; for (u32 i = 0; i < HALKA; ++i) yerlesimler[i] = dsYerlesim_; VkDescriptorSetAllocateInfo da{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO}; da.descriptorPool = dsHavuz_; da.descriptorSetCount = HALKA; da.pSetLayouts = yerlesimler; if (vkAllocateDescriptorSets(aygit, &da, ds_) != VK_SUCCESS) { for (u32 i = 0; i < HALKA; ++i) ds_[i] = VK_NULL_HANDLE; return; } // Kukla (1 kayıt) tamponlarla başla: shader set'i STATİK referans veriyor, // malzeme yokken de geçerli tampon bağlı olmalı (NULL bind YASAK). for (u32 i = 0; i < HALKA; ++i) { malzemeSsbo_[i] = SsboTampon(vb_, sizeof(malzeme::MalzemeGpu)); yuzSsbo_[i] = SsboTampon(vb_, 16); govdeSsbo_[i] = SsboTampon(vb_, sizeof(Mat4f)); descriptorYaz(i); descSurum_[i] = gerekenDesc_; } dokuSurum_ = dokular_.surum(); } MalzemeGpuKaynak::~MalzemeGpuKaynak() { const VkDevice aygit = vb_.aygit(); if (dsHavuz_) vkDestroyDescriptorPool(aygit, dsHavuz_, nullptr); if (dsYerlesim_) vkDestroyDescriptorSetLayout(aygit, dsYerlesim_, nullptr); } void MalzemeGpuKaynak::descriptorYaz(u32 halka) { if (!ds_[halka]) return; VkDescriptorBufferInfo mb{malzemeSsbo_[halka].tampon(), 0, VK_WHOLE_SIZE}; VkDescriptorBufferInfo yb{yuzSsbo_[halka].tampon(), 0, VK_WHOLE_SIZE}; VkDescriptorBufferInfo gb{govdeSsbo_[halka].tampon(), 0, VK_WHOLE_SIZE}; const auto& gorseller = dokular_.gorselBilgileri(); VkWriteDescriptorSet yaz[4]{}; yaz[0] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; yaz[0].dstSet = ds_[halka]; yaz[0].dstBinding = 0; yaz[0].descriptorCount = 1; yaz[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; yaz[0].pBufferInfo = &mb; yaz[1] = yaz[0]; yaz[1].dstBinding = 1; yaz[1].pBufferInfo = &yb; yaz[2] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; yaz[2].dstSet = ds_[halka]; yaz[2].dstBinding = 2; yaz[2].descriptorCount = (u32)gorseller.size(); yaz[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; yaz[2].pImageInfo = gorseller.data(); yaz[3] = yaz[0]; yaz[3].dstBinding = 3; yaz[3].pBufferInfo = &gb; vkUpdateDescriptorSets(vb_.aygit(), 4, yaz, 0, nullptr); } bool MalzemeGpuKaynak::dokulariBagla(malzeme::MalzemeKutuphanesi& kut) { bool degisti = false; for (const malzeme::MalzemeTanim& sabit : kut.hepsi()) { malzeme::MalzemeTanim* m = kut.bul(sabit.kimlik); if (!m) continue; for (u32 k = 0; k < malzeme::DOKU_KANAL_SAYI; ++k) { malzeme::DokuKatman& d = m->dokular[k]; if (!d.acik()) { if (d.gpuSlot) { dokular_.slotBirak(d.gpuSlot); d.gpuSlot = 0; degisti = true; } continue; } // Zaten aynı dosyaya bağlıysa dokunma (dosya I/O tekrarı yok). if (d.gpuSlot && dokular_.slotYolu(d.gpuSlot) == d.yol) continue; if (d.gpuSlot) dokular_.slotBirak(d.gpuSlot); const bool srgb = !malzeme::kanalLineerMi(static_cast(k)); d.gpuSlot = dokular_.slotAl(d.yol, srgb); degisti = true; } } // Sürüm YALNIZ gerçek değişimde artar: aksi halde tazele → dokulariBagla → // degisti() → tazele sonsuz döngüye girerdi. if (degisti) kut.degisti(); return degisti; } void MalzemeGpuKaynak::tazele(malzeme::MalzemeKutuphanesi& kut, const malzeme::MalzemeAtama& atama, Sahne& sahne) { if (!gecerli()) return; // Doku yolları GPU slotlarına bağlı mı? Yalnız kütüphane sürümü değişince // (malzeme düzenlendi / belge yüklendi / undo) koşar → kare-başı dosya I/O YOK. if (kut.surum() != dokuBagliSurum_) { dokulariBagla(kut); dokuBagliSurum_ = kut.surum(); } // ── İmza: içerik değişmediyse tek bayt yazma (sıcak yol) ── u64 imza = karistir(0x517CC1B727220A95ull, kut.surum()); imza = karistir(imza, atama.surum()); imza = karistir(imza, sahne.yuzAralikTepe()); // Yüz aralığı düzeni (gövde → yuzBaz) değişirse tablo yeniden kurulmalı. // Atama VARSA gövde DÖNÜŞÜMÜ de imzaya girer: ters matris tablosu bayatlamasın // (parça taşınınca doku onunla gitmeli). Atama yoksa bu maliyet HİÇ ödenmez. const bool atamaVar = !atama.bosMu(); for (const Dugum& d : sahne.dugumler()) { if (!d.yuzSayi_) continue; imza = karistir(imza, ((u64)d.kimlik << 32) ^ ((u64)d.yuzBaz_ << 8) ^ d.yuzSayi_); if (!atamaVar) continue; const Vec3d& k = d.donusum.konum; const Quatd& y = d.donusum.yonelim; imza = karistir(imza, (u64)(k.x * 4096.0) ^ ((u64)(k.y * 4096.0) << 21) ^ ((u64)(k.z * 4096.0) << 42)); imza = karistir(imza, (u64)(y.x * 4096.0) ^ ((u64)(y.y * 4096.0) << 16) ^ ((u64)(y.z * 4096.0) << 32) ^ ((u64)(y.w * 4096.0) << 48)); imza = karistir(imza, (u64)(d.donusum.olcek.x * 4096.0) ^ ((u64)(d.donusum.olcek.y * 4096.0) << 21) ^ ((u64)(d.donusum.olcek.z * 4096.0) << 42)); } const u32 halka = static_cast(vb_.kareSayac() % HALKA); aktif_ = halka; if (imza_[halka] == imza && descSurum_[halka] == gerekenDesc_ && dokular_.surum() == dokuSurum_) return; // Buradan sonrası GERÇEK kurulum (imza değişti) — ölç. const auto olcumBas = std::chrono::steady_clock::now(); // ── Malzeme tablosu ── const std::vector tablo = kut.gpuTablo(); malzemeSayi_ = (u32)tablo.size(); const u64 mBayt = tablo.size() * sizeof(malzeme::MalzemeGpu); if (malzemeSsbo_[halka].bayt() < mBayt) { // Tampon büyütme NADİR (malzeme eklendiğinde). Eski tampon uçuşta olabilir → // yıkmadan önce aygıt boşalmalı; sonra descriptor bu halka için tazelenir. vb_.bekle(); malzemeSsbo_[halka] = SsboTampon(vb_, mBayt + sizeof(malzeme::MalzemeGpu) * 8); ++gerekenDesc_; } malzemeSsbo_[halka].yaz(tablo.data(), mBayt); // ── Global yüz → malzeme tablosu ── // global = dugum.yuzBaz_ + (yuzOrdinali-1); 0. giriş "malzeme yok". const u32 tepe = std::max(sahne.yuzAralikTepe(), 1); yuzTablo_.assign(tepe, 0u); govdeTers_.clear(); saydamGovde_.clear(); const auto saydamTanim = [&kut](u32 malzemeKimlik) { const malzeme::MalzemeTanim* t = kut.bul(malzemeKimlik); return t && t->saydamMi(); }; for (const Dugum& d : sahne.dugumler()) { if (!d.yuzSayi_) continue; bool saydam = saydamTanim(atama.govdeninMalzemesi(d.kimlik)); const u32 govdeMalzeme = kut.gpuIndeks(atama.govdeninMalzemesi(d.kimlik)); // Gövde slotu (1-tabanlı, üst 16 bit): fragment yerel konumu ters matrisle // çözer → mega-partide (model=birim, vertex dünyada) da doku parçaya bağlı. // Slot YALNIZ malzemeli gövdelere ayrılır (tablo küçük kalır). u32 slot = 0; const auto slotAyir = [&] { if (!slot) { govdeTers_.push_back(d.matris().ters()); slot = static_cast(govdeTers_.size()); // 1-tabanlı if (slot > 0xFFFFu) slot = 0; // 65535 gövde tavanı (pratikte erişilmez) } return slot << 16; }; if (govdeMalzeme) { const u32 girdi = slotAyir() | govdeMalzeme; const u32 son = std::min(d.yuzBaz_ + d.yuzSayi_, tepe); for (u32 g = d.yuzBaz_; g < son; ++g) yuzTablo_[g] = girdi; } for (const auto& [yuz, malzemeKimlik] : atama.govdeninYuzleri(d.kimlik)) { if (!yuz || yuz > d.yuzSayi_) continue; // ordinal kaymış → sessizce atla const u32 mi = kut.gpuIndeks(malzemeKimlik); const u32 g = d.yuzBaz_ + (yuz - 1); if (g < tepe) yuzTablo_[g] = mi ? (slotAyir() | mi) : 0u; saydam = saydam || saydamTanim(malzemeKimlik); } if (saydam) saydamGovde_.push_back(d.kimlik); } std::sort(saydamGovde_.begin(), saydamGovde_.end()); yuzTabloBoyut_ = tepe; const u64 yBayt = (u64)tepe * sizeof(u32); if (yuzSsbo_[halka].bayt() < yBayt) { // Büyütmede %50 pay bırak → her yüz eklemede yeniden tahsis olmasın. vb_.bekle(); yuzSsbo_[halka] = SsboTampon(vb_, yBayt + yBayt / 2 + 64); ++gerekenDesc_; } yuzSsbo_[halka].yaz(yuzTablo_.data(), yBayt); // Gövde ters matris tablosu (yerel doku izdüşümü). Boşsa 1 kukla kayıt kalır — // shader statik referans veriyor, NULL/boş tampon bağlanamaz. if (govdeTers_.empty()) govdeTers_.push_back(Mat4f::birim()); const u64 gBayt = govdeTers_.size() * sizeof(Mat4f); if (govdeSsbo_[halka].bayt() < gBayt) { vb_.bekle(); govdeSsbo_[halka] = SsboTampon(vb_, gBayt + sizeof(Mat4f) * 16); ++gerekenDesc_; } govdeSsbo_[halka].yaz(govdeTers_.data(), gBayt); if (dokular_.surum() != dokuSurum_) { dokuSurum_ = dokular_.surum(); ++gerekenDesc_; // doku dizisi değişti → üç halkanın da descriptor'ı bayat } if (descSurum_[halka] != gerekenDesc_) { descriptorYaz(halka); descSurum_[halka] = gerekenDesc_; } imza_[halka] = imza; // ÖLÇÜM (tahmin değil): gerçek kurulum süresi + kaçıncı kurulum + tablo boyutu. // HUD'da sayaç sabit kalıyorsa sıcak yol temizdir. const f64 ms = std::chrono::duration(std::chrono::steady_clock::now() - olcumBas).count(); perfGosterge().malzemeTazeleMs = ms; ++perfGosterge().malzemeTazeleSayac; perfGosterge().malzemeYuzTablo = yuzTabloBoyut_; } bool MalzemeGpuKaynak::saydamMi(u32 govde) const { return std::binary_search(saydamGovde_.begin(), saydamGovde_.end(), govde); } } // namespace fiko