#include "fiko/render/vulkan_aygit.hpp"

#include <windows.h>
#include <vulkan/vulkan.h>
#include <vulkan/vulkan_win32.h>

#include <chrono>
#include <cstdlib>
#include <cstring>
#include <cstring>
#include <exception>
#include <future>
#include <thread>

#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<VkLayerProperties> 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<VkDeviceFaultAddressInfoEXT> adresler(sayim.addressInfoCount);
  std::vector<VkDeviceFaultVendorInfoEXT> vendor(sayim.vendorInfoCount);
  std::vector<u8> 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;
}

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<VkResult()> 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<f64, std::milli>(
                       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<f64, std::milli>(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<const char*> 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<VkPhysicalDevice> 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<VkQueueFamilyProperties> 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<VkExtensionProperties> 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<const char*> 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<void(VkCommandBuffer)>& 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<f64, std::milli>(t1 - t0).count();
    const f64 bekleMs = std::chrono::duration<f64, std::milli>(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;
}

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_;
}

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<f64, std::milli>(t1 - t0).count();
      const f64 bekleMs = std::chrono::duration<f64, std::milli>(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;
}

void VulkanAygit::viewportGecisOlustur() {
  VkAttachmentDescription ekler[2]{};
  ekler[0].format = kViewportRenkFormat;
  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_SHADER_READ_ONLY_OPTIMAL;

  ekler[1].format = kViewportDerinlikFormat;
  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;
  // 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;

  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;

  // 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 = 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
