#include "fiko/render/vulkan_aygit.hpp" #include #include #include #include #include #include #include #include #include #include #include "fiko/core/gunluk.hpp" #include "fiko/platform/pencere.hpp" #include "fiko/render/viewport_hedef.hpp" // kViewport*Format #include "fiko/render/vk_kontrol.hpp" #include "vk_mem_alloc.h" namespace fiko { namespace { bool katmanVarMi(const char* ad) { u32 sayi = 0; vkEnumerateInstanceLayerProperties(&sayi, nullptr); std::vector katmanlar(sayi); vkEnumerateInstanceLayerProperties(&sayi, katmanlar.data()); for (const auto& k : katmanlar) if (std::strcmp(k.layerName, ad) == 0) return true; return false; } } // namespace VulkanAygit::VulkanAygit(Pencere& ilkPencere) { instanceOlustur(); // Device seçimi present-uyumlu kuyruk ailesi ister → geçici surface (yalnız // aygitSec için; PencereYuzeyi kendi kalıcısını kurar). VkWin32SurfaceCreateInfoKHR yb{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR}; yb.hinstance = (HINSTANCE)ilkPencere.dogalOrnek(); yb.hwnd = (HWND)ilkPencere.dogalTutamac(); VkSurfaceKHR geciciYuzey = VK_NULL_HANDLE; VK_KONTROL(vkCreateWin32SurfaceKHR(instance_, &yb, nullptr, &geciciYuzey)); aygitSec(geciciYuzey); vkDestroySurfaceKHR(instance_, geciciYuzey, nullptr); aygitOlustur(); ayiriciOlustur(); komutVeTransferOlustur(); viewportGecisOlustur(); gunluk::bilgi("Vulkan aygit hazir: {}", aygitAdi_); } VulkanAygit::~VulkanAygit() { // Debug messenger instance'a bağlı → instance'tan ÖNCE yık (her iki çıkış yolu). if (hataElcisi_) { auto yik = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( instance_, "vkDestroyDebugUtilsMessengerEXT"); if (yik) yik(instance_, hataElcisi_, nullptr); hataElcisi_ = VK_NULL_HANDLE; } if (!aygit_) { if (instance_) vkDestroyInstance(instance_, nullptr); return; } // Pencere yüzeyleri (fence bekleyerek) çağıran tarafından ÖNCE yıkıldı → device // idle. Yine de aygitKayip_ değilse bekle (citsiz: idle anlık döner); hung ise atla. bekle(); if (aygitKayip_) { // GPU wedged/kayip: bekle() zaman asti; detach edilmis vkDeviceWaitIdle hala aygit_ // uzerinde olabilir. vkDestroy* cagirmak yeniden donabilir/UB → yikimi TAMAMEN atla. // Process cikisinda surucu tum GPU + instance kaynaklarini geri alir (yikimda sizinti // kabul; unkillable process riskinden kesinlikle kacin). return; } emekliTemizle(true); // bekle başarısız olsa da handle'lar yıkılır (temiz çıkış) if (transferCiti_) vkDestroyFence(aygit_, transferCiti_, nullptr); if (vpOrnekleyici_) vkDestroySampler(aygit_, vpOrnekleyici_, nullptr); if (vpGecis_) vkDestroyRenderPass(aygit_, vpGecis_, nullptr); if (swapchainGecis_) vkDestroyRenderPass(aygit_, swapchainGecis_, nullptr); if (komutHavuzu_) vkDestroyCommandPool(aygit_, komutHavuzu_, nullptr); if (ayirici_) vmaDestroyAllocator(ayirici_); vkDestroyDevice(aygit_, nullptr); if (instance_) vkDestroyInstance(instance_, nullptr); } void VulkanAygit::izKaydet(const char* etiket, int kare, int gorsel, int sonuc) { iz_[izSira_ % kIzSayi] = {etiket, kare, gorsel, sonuc, izSira_}; ++izSira_; } void VulkanAygit::izDok() { if (izSira_ == 0) return; const u64 bas = izSira_ > kIzSayi ? izSira_ - kIzSayi : 0; gunluk::hata("--- WSI iz halkasi (son {} olay; en eski once) ---", (unsigned)(izSira_ - bas)); for (u64 i = bas; i < izSira_; ++i) { const WsiIz& z = iz_[i % kIzSayi]; if (!z.etiket) continue; gunluk::hata(" [{}] {} kare={} gorsel={} sonuc={}", (unsigned)z.sira, z.etiket, z.kare, z.gorsel, z.sonuc); } } // Sürücüden aygıt hata raporu (VK_EXT_device_fault). Yalnız gerçek DEVICE_LOST'ta // anlamlıdır; desteklenmiyorsa/başarısızsa sessizce döner. void VulkanAygit::aygitHatasiDok() { if (!aygitHataUzantisi_ || !aygit_) return; auto fn = (PFN_vkGetDeviceFaultInfoEXT)vkGetDeviceProcAddr(aygit_, "vkGetDeviceFaultInfoEXT"); if (!fn) return; VkDeviceFaultCountsEXT sayim{VK_STRUCTURE_TYPE_DEVICE_FAULT_COUNTS_EXT}; if (fn(aygit_, &sayim, nullptr) != VK_SUCCESS) return; std::vector adresler(sayim.addressInfoCount); std::vector vendor(sayim.vendorInfoCount); std::vector vendorIkili(sayim.vendorBinarySize); VkDeviceFaultInfoEXT bilgi{VK_STRUCTURE_TYPE_DEVICE_FAULT_INFO_EXT}; bilgi.pAddressInfos = adresler.empty() ? nullptr : adresler.data(); bilgi.pVendorInfos = vendor.empty() ? nullptr : vendor.data(); bilgi.pVendorBinaryData = vendorIkili.empty() ? nullptr : vendorIkili.data(); if (fn(aygit_, &sayim, &bilgi) != VK_SUCCESS) return; gunluk::hata("--- VK_EXT_device_fault: {} ---", bilgi.description); for (u32 i = 0; i < sayim.addressInfoCount; ++i) { gunluk::hata(" adres[{}] tur={} adres=0x{:x} bosluk=0x{:x}", i, (int)adresler[i].addressType, (unsigned long long)adresler[i].reportedAddress, (unsigned long long)adresler[i].addressPrecision); adresAra(adresler[i].reportedAddress); } for (u32 i = 0; i < sayim.vendorInfoCount; ++i) gunluk::hata(" vendor[{}] {} kod=0x{:x} veri=0x{:x}", i, vendor[i].description, (unsigned long long)vendor[i].vendorFaultCode, (unsigned long long)vendor[i].vendorFaultData); } bool& VulkanAygit::adresIzleme() { static bool a = [] { char* d = nullptr; std::size_t n = 0; const bool var = _dupenv_s(&d, &n, "FIKO_ADRES_IZ") == 0 && d && d[0] == '1'; if (d) std::free(d); return var; }(); return a; } void VulkanAygit::adresKaydet(u64 taban, u64 boyut, int nesneTur, u64 nesne, bool bagla) { if (adresIz_.size() < kAdresSayi) adresIz_.resize(kAdresSayi); adresIz_[adresSira_ % kAdresSayi] = {taban, boyut, nesne, nesneTur, bagla, adresSira_}; ++adresSira_; } void VulkanAygit::adresAra(u64 adres) { if (adresIz_.empty()) return; gunluk::hata("--- adres 0x{:x} icin bagli/cozulmus nesne aramasi ---", (unsigned long long)adres); int bulundu = 0; const u64 bas = adresSira_ > kAdresSayi ? adresSira_ - kAdresSayi : 0; for (u64 i = bas; i < adresSira_; ++i) { const AdresIz& z = adresIz_[i % kAdresSayi]; if (!z.boyut) continue; if (adres < z.taban || adres >= z.taban + z.boyut) continue; gunluk::hata(" [{}] {} taban=0x{:x} boyut=0x{:x} nesneTur={} nesne=0x{:x}", (unsigned)z.sira, z.bagla ? "BAGLANDI" : "COZULDU", (unsigned long long)z.taban, (unsigned long long)z.boyut, z.nesneTur, (unsigned long long)z.nesne); ++bulundu; } if (!bulundu) gunluk::hata(" (izde eslesme yok — halka {} kayit tutuyor)", kAdresSayi); } void VulkanAygit::aygitKaybiIsaretle(const char* nerede, VkResult sonuc) { if (aygitKayip_) return; aygitKayip_ = true; gunluk::hata("GPU aygiti kayip/yanitsiz ({}) sonuc={} — temiz kapanisa geciliyor", nerede, (int)sonuc); izDok(); // yarışı görebilmek için son WSI olayları if (sonuc == VK_ERROR_DEVICE_LOST) aygitHatasiDok(); } int& VulkanAygit::zamanAsimiTolerans() { static int t = 3; // 3 × 2 sn = 6 sn art arda takılma → gerçek hang say return t; } VkSampleCountFlagBits& VulkanAygit::msaaOrnekSayisi() { static VkSampleCountFlagBits n = VK_SAMPLE_COUNT_1_BIT; return n; } bool VulkanAygit::zamanAsimiTolere(const char* nerede, VkResult sonuc) { if (sonuc != VK_TIMEOUT) { // DEVICE_LOST / diğer hatalar tolere EDİLMEZ aygitKaybiIsaretle(nerede, sonuc); return false; } if (++zamanAsimiUstUste_ > zamanAsimiTolerans()) { aygitKaybiIsaretle(nerede, sonuc); return false; } static gunluk::Seyrek seyrek{2000}; // sıcak yol: takılma sürerken her karede yazma if (seyrek.gecer()) gunluk::uyari("GPU karesi gecikti ({}) — {}/{} tolere edildi, kare atlandi (bastirilan {})", nerede, zamanAsimiUstUste_, zamanAsimiTolerans(), seyrek.bastirilanAl()); return true; } bool VulkanAygit::bekle(const VkFence* citler, u32 citSayi, const char* nerede) { if (!aygit_ || aygitKayip_) return false; // In-flight fence'leri timeout'la bekle (vkDeviceWaitIdle'in timeout'suz sonsuz // beklemesinden kaçın). Fence'ler sinyalliyken device-idle anlık döner. if (citSayi > 0 && citler) { const VkResult f = vkWaitForFences(aygit_, citSayi, citler, VK_TRUE, bekleZamanAsimi); if (f != VK_SUCCESS) { // Geçici gecikme tolere edilir (tolerans dolmadıysa) → çağıran işlemi erteler. zamanAsimiTolere("bekle/fence", f); return false; } } // vkDeviceWaitIdle TIMEOUT ALMAZ: GPU wedged/device-lost ise SONSUZ bloke olur ve // process TerminateProcess'e bile yanit vermez (kernel surucu wait = unkillable). // Ayri thread'de zaman-asimli calistir; bekleZamanAsimi asilirsa aygiti kayip say + // geri don (detach thread sizar ama process cikiyor — sonsuz donmadan iyidir). const auto idleT0 = std::chrono::steady_clock::now(); const VkDevice ay = aygit_; // deger-yakala: detach thread dangling `this`e dokunmaz std::packaged_task idleGorev([ay] { return vkDeviceWaitIdle(ay); }); auto idleGelecek = idleGorev.get_future(); std::thread(std::move(idleGorev)).detach(); if (idleGelecek.wait_for(std::chrono::nanoseconds(bekleZamanAsimi)) != std::future_status::ready) { aygitKaybiIsaretle("bekle/deviceIdle-donma", VK_ERROR_DEVICE_LOST); return false; } const VkResult r = idleGelecek.get(); if (r != VK_SUCCESS) { aygitKaybiIsaretle("bekle/deviceIdle", r); return false; } { // TEŞHİS: device-idle ne kadar sürdü, kim çağırdı? (donmanın adresi) const f64 ms = std::chrono::duration( std::chrono::steady_clock::now() - idleT0).count(); static gunluk::Seyrek seyrekIdle{2000}; if (ms > 100.0 && seyrekIdle.gecer()) gunluk::uyari("[perf] deviceWaitIdle {:.0f} ms — cagiran: {} (bastirilan {})", ms, nerede, seyrekIdle.bastirilanAl()); } emekliTemizle(true); // device boşta → bekleyen tamponların tümü güvenle yıkılır return true; } void VulkanAygit::kareIlerlet() { ++kareSayac_; sonKareZamani_ = std::chrono::steady_clock::now(); // teşhis: yavaş transfer kimin arkasında? emekliTemizle(false); } f64 VulkanAygit::sonKaredenBeriMs() const { if (sonKareZamani_.time_since_epoch().count() == 0) return -1.0; // henüz kare yok return std::chrono::duration(std::chrono::steady_clock::now() - sonKareZamani_) .count(); } void VulkanAygit::tamponEmekliEt(VkBuffer tampon, VmaAllocation tahsis) { if (!tampon) return; emekli_.push_back({tampon, tahsis, kareSayac_}); } void VulkanAygit::emekliTemizle(bool hepsi) { if (emekli_.empty()) return; // ucusteKare+1 kare geçtiyse ilgili fence'ler beklenmiş → GPU tamponu bıraktı. std::erase_if(emekli_, [&](const EmekliTampon& e) { if (!hepsi && kareSayac_ - e.kare <= ucusteKare) return false; vmaDestroyBuffer(ayirici_, e.tampon, e.tahsis); return true; }); } void VulkanAygit::instanceOlustur() { VkApplicationInfo uygulama{VK_STRUCTURE_TYPE_APPLICATION_INFO}; uygulama.pApplicationName = "FikoYT Engineering Platform"; uygulama.applicationVersion = VK_MAKE_VERSION(0, 2, 0); uygulama.pEngineName = "FikoMotor"; uygulama.engineVersion = VK_MAKE_VERSION(0, 2, 0); uygulama.apiVersion = VK_API_VERSION_1_3; std::vector uzantilar{VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME}; VkInstanceCreateInfo bilgi{VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO}; bilgi.pApplicationInfo = &uygulama; #ifndef NDEBUG const char* dogrulama = "VK_LAYER_KHRONOS_validation"; const bool dogrulamaVar = katmanVarMi(dogrulama); if (dogrulamaVar) { bilgi.enabledLayerCount = 1; bilgi.ppEnabledLayerNames = &dogrulama; // Validation mesajlarını yakalamak için debug_utils uzantısı (S2). Katman // varken bu uzantı da vardır (aynı SDK paketi sağlar) → messenger kurulur. uzantilar.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); gunluk::bilgi("Vulkan dogrulama katmani etkin"); } #endif // Adres izleme (teşhis) messenger ister → debug_utils RELEASE'te de gerekir. if (adresIzleme()) { bool var = false; for (const char* u : uzantilar) if (std::strcmp(u, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0) var = true; if (!var) uzantilar.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } bilgi.enabledExtensionCount = (u32)uzantilar.size(); bilgi.ppEnabledExtensionNames = uzantilar.data(); VK_KONTROL(vkCreateInstance(&bilgi, nullptr, &instance_)); #ifndef NDEBUG if (dogrulamaVar) hataElcisiKur(); #endif if (adresIzleme()) adresElcisiKur(); } namespace { // Adres-bağlama raporu geri çağrımı: pNext'te VkDeviceAddressBindingCallbackDataEXT // gelir. Sürücü GPU bellek aralığını hangi nesnenin tuttuğunu/bıraktığını söyler. VulkanAygit* g_adresAygit = nullptr; VKAPI_ATTR VkBool32 VKAPI_CALL adresGeriCagri( VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT, const VkDebugUtilsMessengerCallbackDataEXT* veri, void*) { if (!veri || !g_adresAygit) return VK_FALSE; for (const VkBaseInStructure* p = (const VkBaseInStructure*)veri->pNext; p; p = p->pNext) { if (p->sType != VK_STRUCTURE_TYPE_DEVICE_ADDRESS_BINDING_CALLBACK_DATA_EXT) continue; const auto* d = (const VkDeviceAddressBindingCallbackDataEXT*)p; const bool bagla = d->bindingType == VK_DEVICE_ADDRESS_BINDING_TYPE_BIND_EXT; const int tur = veri->objectCount ? (int)veri->pObjects[0].objectType : -1; const u64 nesne = veri->objectCount ? veri->pObjects[0].objectHandle : 0; g_adresAygit->adresKaydet(d->baseAddress, d->size, tur, nesne, bagla); } return VK_FALSE; } } // namespace void VulkanAygit::adresElcisiKur() { auto olustur = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( instance_, "vkCreateDebugUtilsMessengerEXT"); if (!olustur) return; g_adresAygit = this; VkDebugUtilsMessengerCreateInfoEXT b{VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT}; b.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; b.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT; b.pfnUserCallback = adresGeriCagri; olustur(instance_, &b, nullptr, &adresElci_); gunluk::bilgi("[teshis] adres izleme etkin (VK_EXT_device_address_binding_report)"); } #ifndef NDEBUG namespace { // Validation → gunluk köprüsü. Yalnız WARNING/ERROR (INFO/VERBOSE spam'i kısılır). VKAPI_ATTR VkBool32 VKAPI_CALL dogrulamaGeriCagri( VkDebugUtilsMessageSeverityFlagBitsEXT siddet, VkDebugUtilsMessageTypeFlagsEXT /*tur*/, const VkDebugUtilsMessengerCallbackDataEXT* veri, void* /*kullanici*/) { if (veri && veri->pMessage) { if (siddet & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) gunluk::hata("[vk-validation] {}", veri->pMessage); else if (siddet & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) gunluk::uyari("[vk-validation] {}", veri->pMessage); } return VK_FALSE; // çağrıyı iptal etme, yalnız raporla } } // namespace void VulkanAygit::hataElcisiKur() { auto olustur = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( instance_, "vkCreateDebugUtilsMessengerEXT"); if (!olustur) return; // uzantı yoksa sessizce geç (release davranışı) VkDebugUtilsMessengerCreateInfoEXT b{ VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT}; b.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; b.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; b.pfnUserCallback = dogrulamaGeriCagri; if (olustur(instance_, &b, nullptr, &hataElcisi_) != VK_SUCCESS) hataElcisi_ = VK_NULL_HANDLE; else gunluk::bilgi("Vulkan debug messenger kuruldu (validation uyarilari gunluge)"); } #else void VulkanAygit::hataElcisiKur() {} #endif void VulkanAygit::aygitSec(VkSurfaceKHR geciciYuzey) { u32 sayi = 0; VK_KONTROL(vkEnumeratePhysicalDevices(instance_, &sayi, nullptr)); FIKO_ASSERT(sayi > 0, "Vulkan destekleyen GPU bulunamadi"); std::vector aygitlar(sayi); VK_KONTROL(vkEnumeratePhysicalDevices(instance_, &sayi, aygitlar.data())); int enIyiPuan = -1; for (auto a : aygitlar) { u32 aileSayi = 0; vkGetPhysicalDeviceQueueFamilyProperties(a, &aileSayi, nullptr); std::vector aileler(aileSayi); vkGetPhysicalDeviceQueueFamilyProperties(a, &aileSayi, aileler.data()); int uygunAile = -1; for (u32 i = 0; i < aileSayi; ++i) { VkBool32 sunum = VK_FALSE; vkGetPhysicalDeviceSurfaceSupportKHR(a, i, geciciYuzey, &sunum); if ((aileler[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && sunum) { uygunAile = (int)i; break; } } if (uygunAile < 0) continue; VkPhysicalDeviceProperties oz; vkGetPhysicalDeviceProperties(a, &oz); int puan = (oz.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) ? 1000 : 100; if (puan > enIyiPuan) { enIyiPuan = puan; fizikselAygit_ = a; grafikAile_ = (u32)uygunAile; strncpy_s(aygitAdi_, oz.deviceName, sizeof(aygitAdi_) - 1); } } FIKO_ASSERT(fizikselAygit_ != VK_NULL_HANDLE, "Uygun GPU kuyrugu bulunamadi"); } void VulkanAygit::aygitOlustur() { const float oncelik = 1.0f; VkDeviceQueueCreateInfo kuyruk{VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO}; kuyruk.queueFamilyIndex = grafikAile_; kuyruk.queueCount = 1; kuyruk.pQueuePriorities = &oncelik; u32 uzantiSayi = 0; vkEnumerateDeviceExtensionProperties(fizikselAygit_, nullptr, &uzantiSayi, nullptr); std::vector mevcutlar(uzantiSayi); vkEnumerateDeviceExtensionProperties(fizikselAygit_, nullptr, &uzantiSayi, mevcutlar.data()); const auto uzantiVar = [&](const char* ad) { for (const auto& u : mevcutlar) if (std::strcmp(u.extensionName, ad) == 0) return true; return false; }; std::vector uzantilar{VK_KHR_SWAPCHAIN_EXTENSION_NAME}; // VK_EXT_device_fault: aygıt kaybında SÜRÜCÜDEN hata bilgisi (adres/neden/vendor) // sorulabilir. A maddesi (resize'da VK_ERROR_DEVICE_LOST) doğrulama katmanına // görünmüyor ve stokastik → tek somut kanıt kaynağı bu. Destek yoksa sessizce atlanır. aygitHataUzantisi_ = uzantiVar("VK_EXT_device_fault"); if (aygitHataUzantisi_) uzantilar.push_back("VK_EXT_device_fault"); adresUzantisi_ = adresIzleme() && uzantiVar("VK_EXT_device_address_binding_report"); if (adresUzantisi_) uzantilar.push_back("VK_EXT_device_address_binding_report"); dxInterop_ = uzantiVar(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME) && uzantiVar(VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME); if (dxInterop_) { uzantilar.push_back(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME); uzantilar.push_back(VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME); } // Anizotropik filtre: malzeme dokularinin egik yuzeyde bulaniklasmamasi icin // (DokuDizisi ornekleyicisi). Destek YOKSA acilmaz — ornekleyici 1.0'a duser. VkPhysicalDeviceFeatures destek{}; vkGetPhysicalDeviceFeatures(fizikselAygit_, &destek); anizotropiVar_ = destek.samplerAnisotropy == VK_TRUE; if (anizotropiVar_) { VkPhysicalDeviceProperties oz; vkGetPhysicalDeviceProperties(fizikselAygit_, &oz); enBuyukAnizotropi_ = oz.limits.maxSamplerAnisotropy; } VkPhysicalDeviceFeatures ozellikler{}; ozellikler.fillModeNonSolid = VK_TRUE; ozellikler.wideLines = VK_TRUE; ozellikler.largePoints = VK_TRUE; ozellikler.samplerAnisotropy = anizotropiVar_ ? VK_TRUE : VK_FALSE; // Descriptor indexing (Vulkan 1.2 cekirdek): malzeme doku dizisi slot numarasini // SSBO'dan okur → indeks fragment'ten fragment'e degisir (dinamik-uniform DEGIL). // nonUniform ornekleme ozelligi acilmazsa davranis TANIMSIZDIR; bu yuzden destek // sorgulanir ve YOKSA malzeme dokulari kapatilir (renk yolu calismaya devam eder). VkPhysicalDeviceVulkan12Features oz12{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES}; VkPhysicalDeviceFeatures2 oz2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; oz2.pNext = &oz12; vkGetPhysicalDeviceFeatures2(fizikselAygit_, &oz2); dokuDizisiVar_ = oz12.shaderSampledImageArrayNonUniformIndexing == VK_TRUE; VkPhysicalDeviceVulkan12Features acilan12{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES}; acilan12.shaderSampledImageArrayNonUniformIndexing = dokuDizisiVar_ ? VK_TRUE : VK_FALSE; // VK_EXT_device_fault: uzantıyı listeye eklemek YETMEZ, ÖZELLİĞİ de açmak gerekir // (ölçüldü 07-21: yalnız uzantı açıkken vkGetDeviceFaultInfoEXT hiçbir şey vermedi; // vulkaninfo `deviceFault = true` diyordu). Zincire pNext ile takılır. VkPhysicalDeviceFaultFeaturesEXT hataOz{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT}; hataOz.deviceFault = VK_TRUE; hataOz.deviceFaultVendorBinary = VK_FALSE; // ikili döküm gerekmiyor (metin rapor yeter) VkPhysicalDeviceAddressBindingReportFeaturesEXT adresOz{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ADDRESS_BINDING_REPORT_FEATURES_EXT}; adresOz.reportAddressBinding = VK_TRUE; VkDeviceCreateInfo bilgi{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO}; bilgi.pNext = dokuDizisiVar_ ? (void*)&acilan12 : nullptr; if (adresUzantisi_) { adresOz.pNext = (void*)bilgi.pNext; bilgi.pNext = &adresOz; } if (aygitHataUzantisi_) { // zincirin BAŞINA ekle (acilan12 varsa onu takip eder) hataOz.pNext = (void*)bilgi.pNext; bilgi.pNext = &hataOz; } bilgi.queueCreateInfoCount = 1; bilgi.pQueueCreateInfos = &kuyruk; bilgi.enabledExtensionCount = (u32)uzantilar.size(); bilgi.ppEnabledExtensionNames = uzantilar.data(); bilgi.pEnabledFeatures = &ozellikler; VK_KONTROL(vkCreateDevice(fizikselAygit_, &bilgi, nullptr, &aygit_)); vkGetDeviceQueue(aygit_, grafikAile_, 0, &grafikKuyruk_); } void VulkanAygit::ayiriciOlustur() { VmaAllocatorCreateInfo bilgi{}; bilgi.physicalDevice = fizikselAygit_; bilgi.device = aygit_; bilgi.instance = instance_; bilgi.vulkanApiVersion = VK_API_VERSION_1_3; VK_KONTROL(vmaCreateAllocator(&bilgi, &ayirici_)); } VkRenderPass VulkanAygit::swapchainGecis(VkFormat format) { if (swapchainGecis_ != VK_NULL_HANDLE) { FIKO_ASSERT(format == swapchainGecisFormat_, "Tüm pencereler aynı swapchain formatını paylaşmalı"); return swapchainGecis_; } swapchainGecisFormat_ = format; VkAttachmentDescription ekler[2]{}; ekler[0].format = format; ekler[0].samples = VK_SAMPLE_COUNT_1_BIT; ekler[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; ekler[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; ekler[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; ekler[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; ekler[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; ekler[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; ekler[1].format = VK_FORMAT_D32_SFLOAT; ekler[1].samples = VK_SAMPLE_COUNT_1_BIT; ekler[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; ekler[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; ekler[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; ekler[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; ekler[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; ekler[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkAttachmentReference renkRef{0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; VkAttachmentReference derinlikRef{1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL}; VkSubpassDescription alt{}; alt.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; alt.colorAttachmentCount = 1; alt.pColorAttachments = &renkRef; alt.pDepthStencilAttachment = &derinlikRef; // Iki yonlu bagimlilik: onceki karenin renk/derinlik yazmalari bitmeden bu kare // temizleme/yazma yapmaz (WAW ve WAR hazardlari kapatilir). VkSubpassDependency bagimliliklar[2]{}; bagimliliklar[0].srcSubpass = VK_SUBPASS_EXTERNAL; bagimliliklar[0].dstSubpass = 0; // Depth write EARLY ve LATE fragment testlerinde olur; önceki karenin depth-store'u + // bu karenin loadOp CLEAR/layout-transition'ı arası WRITE-AFTER-WRITE'ı serialize etmek // için HER İKİ stage'i de src ve dst'te kapsa (aksi halde iki in-flight kare aynı depth // buffer'a → SYNC-HAZARD-WAW, gerçek GPU'da potansiyel device-lost). bagimliliklar[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; bagimliliklar[0].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; bagimliliklar[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; bagimliliklar[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; bagimliliklar[1].srcSubpass = 0; bagimliliklar[1].dstSubpass = VK_SUBPASS_EXTERNAL; bagimliliklar[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; bagimliliklar[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; bagimliliklar[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; bagimliliklar[1].dstAccessMask = 0; VkRenderPassCreateInfo rp{VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO}; rp.attachmentCount = 2; rp.pAttachments = ekler; rp.subpassCount = 1; rp.pSubpasses = &alt; rp.dependencyCount = 2; rp.pDependencies = bagimliliklar; VK_KONTROL(vkCreateRenderPass(aygit_, &rp, nullptr, &swapchainGecis_)); return swapchainGecis_; } void VulkanAygit::komutVeTransferOlustur() { VkCommandPoolCreateInfo havuz{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO}; havuz.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; havuz.queueFamilyIndex = grafikAile_; VK_KONTROL(vkCreateCommandPool(aygit_, &havuz, nullptr, &komutHavuzu_)); // Kalıcı transfer command buffer + fence (anindaGonder/toplu yeniden kullanır). VkCommandBufferAllocateInfo tKb{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO}; tKb.commandPool = komutHavuzu_; tKb.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; tKb.commandBufferCount = 1; VK_KONTROL(vkAllocateCommandBuffers(aygit_, &tKb, &transferKb_)); VkFenceCreateInfo tCit{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO}; VK_KONTROL(vkCreateFence(aygit_, &tCit, nullptr, &transferCiti_)); } void VulkanAygit::anindaGonder(const std::function& kayit) { // Toplu oturum açıkken paylaşılan transferKb_ çakışmasın: önce flush. if (topluAktif_) topluYuklemeBitir(); VkCommandBuffer kb = transferKb_; vkResetCommandBuffer(kb, 0); VkCommandBufferBeginInfo baslat{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO}; baslat.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; VK_KONTROL(vkBeginCommandBuffer(kb, &baslat)); kayit(kb); VK_KONTROL(vkEndCommandBuffer(kb)); VkSubmitInfo gonder{VK_STRUCTURE_TYPE_SUBMIT_INFO}; gonder.commandBufferCount = 1; gonder.pCommandBuffers = &kb; vkResetFences(aygit_, 1, &transferCiti_); const auto t0 = std::chrono::steady_clock::now(); VK_KONTROL(vkQueueSubmit(grafikKuyruk_, 1, &gonder, transferCiti_)); const auto t1 = std::chrono::steady_clock::now(); // ZAMAN AŞIMI ≠ AYGIT KAYBI. Eskiden her VK_SUCCESS-dışı sonuç doğrudan "aygıt kayıp" // sayılıyordu; VK_TIMEOUT (=2) da buna dahildi → GPU yalnızca MEŞGULKEN uygulama // kendini kapatıyordu. ÖLÇÜLDÜ 07-21: MCP'den 50'şer kutu eklerken 5. turda (~250 // düğüm) `tamponlariYukle` 999 ms, sonra 2998 ms sürdü ve "GPU aygiti kayip // (anindaGonder) sonuc=2" ile TEMİZ KAPANIŞA geçildi — gerçek aygıt kaybı YOKTU. // Kare yolu bunu zaten `zamanAsimiTolere` ile yönetiyordu; aynı merkez buraya da // bağlandı: tolerans bitene dek BEKLEMEYE DEVAM edilir (transferKb_ hâlâ uçuşta // olduğu için beklemeden dönmek tampon bozulması demektir). VkResult f = vkWaitForFences(aygit_, 1, &transferCiti_, VK_TRUE, bekleZamanAsimi); while (f == VK_TIMEOUT && zamanAsimiTolere("anindaGonder", f)) f = vkWaitForFences(aygit_, 1, &transferCiti_, VK_TRUE, bekleZamanAsimi); const auto t2 = std::chrono::steady_clock::now(); { // TEŞHİS: anlık transfer neden yavaş — gönderim mi, fence beklemesi mi? const f64 gonderimMs = std::chrono::duration(t1 - t0).count(); const f64 bekleMs = std::chrono::duration(t2 - t1).count(); static gunluk::Seyrek seyrek{2000}; // sıcak yol: kare-başı spam YASAK if (gonderimMs + bekleMs > 5.0 && seyrek.gecer()) gunluk::uyari("[perf] anindaGonder yavas: submit {:.1f} ms + fence bekleme {:.1f} ms " "(kare {}, son kareden beri {:.0f} ms, bastirilan {})", gonderimMs, bekleMs, kareSayac_, sonKaredenBeriMs(), seyrek.bastirilanAl()); } if (f == VK_SUCCESS) zamanAsimiSifirla(); // geçici gecikme atlatıldı → sayaç sıfır else if (!aygitKayip_) aygitKaybiIsaretle("anindaGonder", f); } void VulkanAygit::topluYuklemeBasla() { if (topluAktif_ || aygitKayip_) return; vkResetCommandBuffer(transferKb_, 0); VkCommandBufferBeginInfo baslat{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO}; baslat.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; VK_KONTROL(vkBeginCommandBuffer(transferKb_, &baslat)); topluAktif_ = true; topluKopyaSayi_ = 0; topluStagingBayt_ = 0; } void VulkanAygit::topluKopyaKaydet(VkBuffer kaynak, VmaAllocation kaynakTahsis, VkBuffer hedef, u64 bayt) { const VkBufferCopy kopya{0, 0, bayt}; vkCmdCopyBuffer(transferKb_, kaynak, hedef, 1, &kopya); topluStaging_.emplace_back(kaynak, kaynakTahsis); // bitirene dek yaşar ++topluKopyaSayi_; topluStagingBayt_ += bayt; // ARA-FLUSH: eşik aşılınca mevcut oturumu bitir (submit+fence+staging temizliği) ve // hemen yenisini aç. Çağıran taraf (gpu_tampon.cpp) topluYuklemeAktif() true kaldığı // için farkına varmaz — tek dev bekleme yerine birkaç küçük bekleme olur. if (topluKopyaSayi_ >= kTopluAraFlushKopya || topluStagingBayt_ >= kTopluAraFlushBayt) { topluYuklemeBitir(); topluYuklemeBasla(); } } void VulkanAygit::topluYuklemeBitir() { if (!topluAktif_) return; topluAktif_ = false; VK_KONTROL(vkEndCommandBuffer(transferKb_)); if (topluKopyaSayi_ > 0 && !aygitKayip_) { VkSubmitInfo gonder{VK_STRUCTURE_TYPE_SUBMIT_INFO}; gonder.commandBufferCount = 1; gonder.pCommandBuffers = &transferKb_; vkResetFences(aygit_, 1, &transferCiti_); const auto t0 = std::chrono::steady_clock::now(); VK_KONTROL(vkQueueSubmit(grafikKuyruk_, 1, &gonder, transferCiti_)); const auto t1 = std::chrono::steady_clock::now(); // ZAMAN AŞIMI ≠ AYGIT KAYBI — `anindaGonder`'deki T41 düzeltmesinin ikizi burada // EKSİKTİ: toplu yükleme sırasında GPU yalnızca meşgulse (VK_TIMEOUT) uygulama // kendini "aygıt kayıp" sayıp kapatıyordu. Aynı merkezi tolerans bağlandı. VkResult f = vkWaitForFences(aygit_, 1, &transferCiti_, VK_TRUE, bekleZamanAsimi); while (f == VK_TIMEOUT && zamanAsimiTolere("topluYuklemeBitir", f)) f = vkWaitForFences(aygit_, 1, &transferCiti_, VK_TRUE, bekleZamanAsimi); const auto t2 = std::chrono::steady_clock::now(); { // TEŞHİS: toplu yükleme neden yavaş — gönderim mi, fence beklemesi mi? const f64 gonderimMs = std::chrono::duration(t1 - t0).count(); const f64 bekleMs = std::chrono::duration(t2 - t1).count(); static gunluk::Seyrek seyrek{2000}; // sıcak yol: kare-başı spam YASAK if (gonderimMs + bekleMs > 5.0 && seyrek.gecer()) gunluk::uyari("[perf] topluYuklemeBitir yavas: {} kopya, submit {:.1f} ms + fence " "bekleme {:.1f} ms (kare {}, son kareden beri {:.0f} ms, bastirilan {})", topluKopyaSayi_, gonderimMs, bekleMs, kareSayac_, sonKaredenBeriMs(), seyrek.bastirilanAl()); } if (f == VK_SUCCESS) zamanAsimiSifirla(); else if (!aygitKayip_) aygitKaybiIsaretle("topluYuklemeBitir", f); } for (auto& [buf, tah] : topluStaging_) vmaDestroyBuffer(ayirici_, buf, tah); topluStaging_.clear(); topluKopyaSayi_ = 0; topluStagingBayt_ = 0; } void VulkanAygit::viewportGecisOlustur() { // İstenen MSAA örnek sayısını cihazın GERÇEKTEN desteklediği en yakın değere kenetle // (renk VE derinlik formatı aynı anda desteklemeli — framebuffer'ın iki eki de aynı // örnek sayısında olmak zorunda). Etkin sonuç geri yazılır: ViewportHedef/BoruHatti // aynı fonksiyonu okuyup HER ZAMAN geçerli/desteklenen bir değer görür. VkSampleCountFlagBits istenen = msaaOrnekSayisi(); if (istenen != VK_SAMPLE_COUNT_1_BIT) { VkPhysicalDeviceProperties ozellik{}; vkGetPhysicalDeviceProperties(fizikselAygit_, &ozellik); const VkSampleCountFlags destek = ozellik.limits.framebufferColorSampleCounts & ozellik.limits.framebufferDepthSampleCounts; while (istenen != VK_SAMPLE_COUNT_1_BIT && !(destek & istenen)) istenen = (VkSampleCountFlagBits)(istenen >> 1); // 8→4→2→1: en yakın desteklenene düş } msaaOrnekSayisi() = istenen; const bool msaa = istenen != VK_SAMPLE_COUNT_1_BIT; // msaa KAPALI: eski 2-ekli geçiş (renk + derinlik, ikisi de tek-örnekli, birebir eski). // msaa AÇIK: 3-ekli geçiş — [0] çok-örnekli renk, [1] çok-örnekli derinlik, // [2] tek-örnekli ÇÖZÜNÜRLEME (resolve) hedefi — ImGui/PNG readback hep [2]'yi okur. VkAttachmentDescription ekler[3]{}; ekler[0].format = kViewportRenkFormat; ekler[0].samples = istenen; ekler[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // msaa açıkken çok-örnekli renk pass dışında hiç okunmaz (yalnız resolve kaynağı) → // STORE gereksiz (bant genişliği tasarrufu). Kapalıyken eskisi gibi STORE (ImGui okur). ekler[0].storeOp = msaa ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE; ekler[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; ekler[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; ekler[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; ekler[0].finalLayout = msaa ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; ekler[1].format = kViewportDerinlikFormat; ekler[1].samples = istenen; ekler[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; ekler[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; // Stencil: GPU kesit kapağı için kare başı temizlenir (pass içinde kullanılıp atılır). ekler[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; ekler[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; ekler[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; ekler[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; // [2] yalnız msaa AÇIKKEN var: tek-örnekli çözünürleme hedefi (eski [0]'ın yerini alır). ekler[2].format = kViewportRenkFormat; ekler[2].samples = VK_SAMPLE_COUNT_1_BIT; ekler[2].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // resolve tamamen üzerine yazar ekler[2].storeOp = VK_ATTACHMENT_STORE_OP_STORE; ekler[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; ekler[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; ekler[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; ekler[2].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkAttachmentReference renkRef{0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; VkAttachmentReference derinlikRef{1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL}; VkAttachmentReference resolveRef{2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; VkSubpassDescription alt{}; alt.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; alt.colorAttachmentCount = 1; alt.pColorAttachments = &renkRef; alt.pDepthStencilAttachment = &derinlikRef; alt.pResolveAttachments = msaa ? &resolveRef : nullptr; // Gecisten sonra renk fragment shader'da (ImGui) ornek olarak okunur. VkSubpassDependency bagimliliklar[2]{}; bagimliliklar[0].srcSubpass = VK_SUBPASS_EXTERNAL; bagimliliklar[0].dstSubpass = 0; // Viewport hedefi TEK texture (kare başına değil) → iki in-flight kare aynı renk/derinlik // attachment'ına yazar. src'ye SADECE ImGui örnek-okuması (SHADER_READ) değil, ÖNCEKİ // karenin bu hedefe YAZMASINI (COLOR/DEPTH WRITE) de dahil et → kareler arası WRITE-AFTER- // WRITE hazard'ı serialize edilir (aynı kuyruk submission timeline). Bu eklenmezse gerçek // GPU'da (özellikle resize timing'inde) race → DEVICE_LOST. (SYNC-HAZARD-WAW ile kanıtlı.) bagimliliklar[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; bagimliliklar[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; bagimliliklar[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; bagimliliklar[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; bagimliliklar[1].srcSubpass = 0; bagimliliklar[1].dstSubpass = VK_SUBPASS_EXTERNAL; bagimliliklar[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; bagimliliklar[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; bagimliliklar[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; bagimliliklar[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; VkRenderPassCreateInfo rp{VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO}; rp.attachmentCount = msaa ? 3 : 2; rp.pAttachments = ekler; rp.subpassCount = 1; rp.pSubpasses = &alt; rp.dependencyCount = 2; rp.pDependencies = bagimliliklar; VK_KONTROL(vkCreateRenderPass(aygit_, &rp, nullptr, &vpGecis_)); VkSamplerCreateInfo s{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; s.magFilter = VK_FILTER_LINEAR; s.minFilter = VK_FILTER_LINEAR; s.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; s.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; s.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; s.maxLod = 1.0f; VK_KONTROL(vkCreateSampler(aygit_, &s, nullptr, &vpOrnekleyici_)); } } // namespace fiko