#include "fiko/render/pencere_yuzeyi.hpp" #include #include #include #include #include #include "fiko/core/gunluk.hpp" #include "fiko/platform/pencere.hpp" #include "fiko/render/vk_kontrol.hpp" #include "fiko/render/vulkan_aygit.hpp" #include "vk_mem_alloc.h" namespace fiko { namespace { constexpr VkFormat derinlikFormat = VK_FORMAT_D32_SFLOAT; } // namespace PencereYuzeyi::PencereYuzeyi(VulkanAygit& aygit, Pencere& pencere) : aygit_(aygit), pencere_(pencere) { VkWin32SurfaceCreateInfoKHR bilgi{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR}; bilgi.hinstance = (HINSTANCE)pencere_.dogalOrnek(); bilgi.hwnd = (HWND)pencere_.dogalTutamac(); VK_KONTROL(vkCreateWin32SurfaceKHR(aygit_.instance(), &bilgi, nullptr, &yuzey_)); senkronOlustur(); takasZinciriOlustur(); } PencereYuzeyi::~PencereYuzeyi() { const VkDevice a = aygit_.aygit(); if (a) { // In-flight fence'leri timeout'la bekle → device bu yüzeyin işini bitirdi. VkFence citler[ucusteKare]; u32 sayi = 0; for (u32 i = 0; i < ucusteKare; ++i) if (kareCiti_[i]) citler[sayi++] = kareCiti_[i]; aygit_.bekle(citler, sayi); for (u32 i = 0; i < ucusteKare; ++i) { if (gorselHazir_[i]) vkDestroySemaphore(a, gorselHazir_[i], nullptr); if (kareCiti_[i]) vkDestroyFence(a, kareCiti_[i], nullptr); if (komutTamponlar_[i]) vkFreeCommandBuffers(a, aygit_.komutHavuzu(), 1, &komutTamponlar_[i]); } for (auto s : sunumHazir_) vkDestroySemaphore(a, s, nullptr); for (auto fb : kareTamponlar_) vkDestroyFramebuffer(a, fb, nullptr); for (auto g : derinlikGorunumler_) vkDestroyImageView(a, g, nullptr); for (size_t i = 0; i < derinlikGorseller_.size(); ++i) vmaDestroyImage(aygit_.ayirici(), derinlikGorseller_[i], derinlikTahsisler_[i]); for (auto g : takasGorunumler_) vkDestroyImageView(a, g, nullptr); if (takasZinciri_) vkDestroySwapchainKHR(a, takasZinciri_, nullptr); } if (yuzey_) vkDestroySurfaceKHR(aygit_.instance(), yuzey_, nullptr); } void PencereYuzeyi::senkronOlustur() { const VkDevice a = aygit_.aygit(); VkCommandBufferAllocateInfo tahsis{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO}; tahsis.commandPool = aygit_.komutHavuzu(); tahsis.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; tahsis.commandBufferCount = ucusteKare; VK_KONTROL(vkAllocateCommandBuffers(a, &tahsis, komutTamponlar_)); VkSemaphoreCreateInfo sem{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; VkFenceCreateInfo cit{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO}; cit.flags = VK_FENCE_CREATE_SIGNALED_BIT; for (u32 i = 0; i < ucusteKare; ++i) { VK_KONTROL(vkCreateSemaphore(a, &sem, nullptr, &gorselHazir_[i])); VK_KONTROL(vkCreateFence(a, &cit, nullptr, &kareCiti_[i])); } // sunumHazir_ görsel başına → takasZinciriOlustur içinde kurulur. } void PencereYuzeyi::takasZinciriOlustur(VkSwapchainKHR eski) { const VkDevice a = aygit_.aygit(); const VkPhysicalDevice fa = aygit_.fizikselAygit(); const VmaAllocator ay = aygit_.ayirici(); VkSurfaceCapabilitiesKHR yetenek; VK_KONTROL(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(fa, yuzey_, &yetenek)); u32 formatSayi = 0; VK_KONTROL(vkGetPhysicalDeviceSurfaceFormatsKHR(fa, yuzey_, &formatSayi, nullptr)); std::vector formatlar(formatSayi); VK_KONTROL(vkGetPhysicalDeviceSurfaceFormatsKHR(fa, yuzey_, &formatSayi, formatlar.data())); // Bos format listesi (headless/ekran-degisimi ani/sürücü tuhafligi): formatlar[0] OOB // cokme olurdu. Guvenli varsayilana dus (B8G8R8A8_SRGB tum WSI sürücülerinde desteklenir); // asagidaki for bos vektorde donmez, secilen korunur. VkSurfaceFormatKHR secilen = formatlar.empty() ? VkSurfaceFormatKHR{VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR} : formatlar[0]; for (const auto& f : formatlar) { if (f.format == VK_FORMAT_B8G8R8A8_SRGB && f.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { secilen = f; break; } } takasFormat_ = secilen.format; takasBoyut_ = yetenek.currentExtent; if (takasBoyut_.width == 0xFFFFFFFF) takasBoyut_ = {pencere_.genislik(), pencere_.yukseklik()}; takasBoyut_.width = std::clamp(takasBoyut_.width, yetenek.minImageExtent.width, yetenek.maxImageExtent.width); takasBoyut_.height = std::clamp(takasBoyut_.height, yetenek.minImageExtent.height, yetenek.maxImageExtent.height); u32 gorselSayi = yetenek.minImageCount + 1; if (yetenek.maxImageCount > 0) gorselSayi = std::min(gorselSayi, yetenek.maxImageCount); // Present modu: FIFO (VSync) — GPU present'i ekran yenileme hızına (60/144 Hz) // senkronlar. MAILBOX (eski varsayılan) VSync'siz çalışıp CAD boştayken bile GPU'yu // 1000+ FPS'te maksimuma zorluyordu (güç/ısı israfı); FIFO tavanı monitöre sabitler. // Düşük-gecikme gerekirse ileride ayardan MAILBOX'a geçilebilir (idle-farkında). // AyarSistemi `render.sunum_kipi`: 0=FIFO (varsayılan, eski davranış) · 1=MAILBOX · // 2=IMMEDIATE. Resize donmasının present kuyruğundan gelip gelmediğini ÖLÇMEK için // açıldı (07-21): FIFO'da sürüklerken kare tamamlanması ~945 ms'ye çıkıyor. // Desteklenmeyen kip istenirse sessizce FIFO'ya düşülür (FIFO her zaman destekli). VkPresentModeKHR sunum = VK_PRESENT_MODE_FIFO_KHR; if (sunumKipi() != 0) { u32 kipSayi = 0; vkGetPhysicalDeviceSurfacePresentModesKHR(aygit_.fizikselAygit(), yuzey_, &kipSayi, nullptr); std::vector kipler(kipSayi); vkGetPhysicalDeviceSurfacePresentModesKHR(aygit_.fizikselAygit(), yuzey_, &kipSayi, kipler.data()); const VkPresentModeKHR istenen = sunumKipi() == 1 ? VK_PRESENT_MODE_MAILBOX_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR; for (VkPresentModeKHR k : kipler) if (k == istenen) sunum = istenen; } VkSwapchainCreateInfoKHR bilgi{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR}; bilgi.surface = yuzey_; bilgi.minImageCount = gorselSayi; bilgi.imageFormat = takasFormat_; bilgi.imageColorSpace = secilen.colorSpace; bilgi.imageExtent = takasBoyut_; bilgi.imageArrayLayers = 1; bilgi.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; bilgi.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; bilgi.preTransform = yetenek.currentTransform; bilgi.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; bilgi.presentMode = sunum; bilgi.clipped = VK_TRUE; // ESKI ZINCIRI DEVRET: eski zinciri once yikip sonra yenisini kurmak, sunum motoru // hala eski gorselleri tutarken surucude aygit kaybina yol aciyordu (olculdu 07-21: // 400 adimlik resize firtinasi -> VK_ERROR_DEVICE_LOST). oldSwapchain ile WSI kaynaklari // duzgun devreder; eski zincir yeni zincir kurulduktan SONRA yikilir. bilgi.oldSwapchain = eski; VK_KONTROL(vkCreateSwapchainKHR(a, &bilgi, nullptr, &takasZinciri_)); if (eski != VK_NULL_HANDLE) vkDestroySwapchainKHR(a, eski, nullptr); u32 sayi = 0; vkGetSwapchainImagesKHR(a, takasZinciri_, &sayi, nullptr); takasGorseller_.resize(sayi); vkGetSwapchainImagesKHR(a, takasZinciri_, &sayi, takasGorseller_.data()); takasGorunumler_.resize(sayi); for (u32 i = 0; i < sayi; ++i) { VkImageViewCreateInfo gv{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; gv.image = takasGorseller_[i]; gv.viewType = VK_IMAGE_VIEW_TYPE_2D; gv.format = takasFormat_; gv.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; VK_KONTROL(vkCreateImageView(a, &gv, nullptr, &takasGorunumler_[i])); } // Derinlik tamponları: görsel başına bir tane (kare-arası hazard yok). derinlikGorseller_.resize(sayi); derinlikTahsisler_.resize(sayi); derinlikGorunumler_.resize(sayi); for (u32 i = 0; i < sayi; ++i) { VkImageCreateInfo dg{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; dg.imageType = VK_IMAGE_TYPE_2D; dg.format = derinlikFormat; dg.extent = {takasBoyut_.width, takasBoyut_.height, 1}; dg.mipLevels = 1; dg.arrayLayers = 1; dg.samples = VK_SAMPLE_COUNT_1_BIT; dg.tiling = VK_IMAGE_TILING_OPTIMAL; dg.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; VmaAllocationCreateInfo dt{}; dt.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; VK_KONTROL(vmaCreateImage(ay, &dg, &dt, &derinlikGorseller_[i], &derinlikTahsisler_[i], nullptr)); // TEŞHİS: adres izleme açıkken hangi VkImage'ın PENCERE DERİNLİĞİ olduğunu yaz → // device_fault'un verdiği nesne handle'ı ile eşleştirilir (suçlu görseli adlandır). if (VulkanAygit::adresIzleme()) gunluk::bilgi("[teshis] pencere derinlik gorsel[{}] = 0x{:x} ({}x{})", i, (unsigned long long)(u64)derinlikGorseller_[i], takasBoyut_.width, takasBoyut_.height); VkImageViewCreateInfo dgv{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; dgv.image = derinlikGorseller_[i]; dgv.viewType = VK_IMAGE_VIEW_TYPE_2D; dgv.format = derinlikFormat; dgv.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1}; VK_KONTROL(vkCreateImageView(a, &dgv, nullptr, &derinlikGorunumler_[i])); } // Paylaşılan swapchain render pass (VulkanAygit lazy oluşturur/cache'ler). renderPass_ = aygit_.swapchainGecis(takasFormat_); kareTamponlar_.resize(sayi); for (u32 i = 0; i < sayi; ++i) { const VkImageView gorunumler[2] = {takasGorunumler_[i], derinlikGorunumler_[i]}; VkFramebufferCreateInfo fb{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO}; fb.renderPass = renderPass_; fb.attachmentCount = 2; fb.pAttachments = gorunumler; fb.width = takasBoyut_.width; fb.height = takasBoyut_.height; fb.layers = 1; VK_KONTROL(vkCreateFramebuffer(a, &fb, nullptr, &kareTamponlar_[i])); } // Sunum semaforları görsel başına (takas zinciri yeniden kurulunca yenilenir). for (auto s : sunumHazir_) vkDestroySemaphore(a, s, nullptr); sunumHazir_.resize(sayi); VkSemaphoreCreateInfo sem{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; for (u32 i = 0; i < sayi; ++i) VK_KONTROL(vkCreateSemaphore(a, &sem, nullptr, &sunumHazir_[i])); } int& PencereYuzeyi::hedefKararlilikMs() { // ÖLÇÜLDÜ 07-21 (60 adımlık, 100 ms aralıklı yeniden boyutlandırma; 6 tohum): // 0 ms (eski davranış) → 6 koşumun 5'i AYGIT KAYBIYLA ÖLDÜ (#46,#2,#2,#11,#1) // 250 ms → 6/6 SAĞLAM // Görsel bedel: sürükleme boyunca 3B görüntü kısa süre eski çözünürlükte gerilir // (gerçek pencere sürüklemesinde `boyutlaniyor_` ile zaten olan davranış). static int ms = 250; return ms; } bool PencereYuzeyi::boyutKararliMi(u32 g, u32 y, u32& sonG, u32& sonY, long long& sonDegisimMs) { const int esik = hedefKararlilikMs(); const auto simdi = std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()) .count(); if (g != sonG || y != sonY) { // boyut değişti → sayaç sıfırla sonG = g; sonY = y; sonDegisimMs = simdi; if (esik <= 0) return true; // eski davranış: beklemeden kur return false; } if (esik <= 0) return true; return simdi - sonDegisimMs >= esik; // aynı boyut yeterince durdu → kur } int& PencereYuzeyi::sunumKipi() { static int k = 0; // 0 = FIFO (VSync) — eski davranış return k; } bool PencereYuzeyi::bekle(const char* nerede) { VkFence citler[ucusteKare]; u32 sayi = 0; for (u32 i = 0; i < ucusteKare; ++i) if (kareCiti_[i]) citler[sayi++] = kareCiti_[i]; return aygit_.bekle(citler, sayi, nerede); } void PencereYuzeyi::zamanHavuzuKur() { if (zamanHavuzu_) return; VkPhysicalDeviceProperties oz{}; vkGetPhysicalDeviceProperties(aygit_.fizikselAygit(), &oz); if (oz.limits.timestampComputeAndGraphics == VK_FALSE || oz.limits.timestampPeriod <= 0.0f) return; // donanım desteklemiyor → teşhis sessizce kapalı VkQueryPoolCreateInfo qb{VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO}; qb.queryType = VK_QUERY_TYPE_TIMESTAMP; qb.queryCount = ucusteKare * 2; if (vkCreateQueryPool(aygit_.aygit(), &qb, nullptr, &zamanHavuzu_) != VK_SUCCESS) return; zamanDamgasiVar_ = true; } void PencereYuzeyi::takasZinciriYikVeKur() { // device kayıpsa yeniden kurma (GPU çağrıları güvensiz). Pencere fence'lerini bekle. if (!bekle("takasZinciriYikVeKur")) return; const VkDevice a = aygit_.aygit(); const VmaAllocator ay = aygit_.ayirici(); // ACQUIRE SEMAFORLARINI YENİDEN KUR (device idle iken): vkAcquireNextImageKHR // SUBOPTIMAL dönünce (resize'da sık) görsel acquire edilir + gorselHazir_ SIGNAL // edilir ama kare atlanır → semaphore beklenmeden signaled kalır. Sonraki acquire // aynı signaled semaphore'u kullanamaz (signal-signal hazard → GPU hang/device-lost). // Recreate'te hepsini taze (unsignaled) kur → resize device-lost kök çözümü. { VkSemaphoreCreateInfo sem{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; for (auto& s : gorselHazir_) { vkDestroySemaphore(a, s, nullptr); VK_KONTROL(vkCreateSemaphore(a, &sem, nullptr, &s)); } } for (auto fb : kareTamponlar_) vkDestroyFramebuffer(a, fb, nullptr); kareTamponlar_.clear(); for (auto g : derinlikGorunumler_) vkDestroyImageView(a, g, nullptr); for (size_t i = 0; i < derinlikGorseller_.size(); ++i) vmaDestroyImage(ay, derinlikGorseller_[i], derinlikTahsisler_[i]); derinlikGorunumler_.clear(); derinlikGorseller_.clear(); derinlikTahsisler_.clear(); for (auto g : takasGorunumler_) vkDestroyImageView(a, g, nullptr); takasGorunumler_.clear(); // Eski zinciri BURADA yikma: oldSwapchain olarak devredilir (yikim icerde, kurulum sonrasi). const VkSwapchainKHR eskiZincir = takasZinciri_; takasZinciri_ = VK_NULL_HANDLE; takasZinciriOlustur(eskiZincir); } int& PencereYuzeyi::kurmaAralikMs() { static int ms = 60; // ~16 kurulum/sn tavanı — sürüklerken görsel olarak fark edilmez return ms; } // Aralık doldu mu? Dolduysa damgayı ilerletir (kurulum yapılacak demektir). bool PencereYuzeyi::yenidenKurmaKapisi(long long& sonMs) { const int aralik = kurmaAralikMs(); if (aralik <= 0) return true; const long long simdi = std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()).count(); if (sonMs != 0 && simdi - sonMs < aralik) return false; sonMs = simdi; return true; } bool PencereYuzeyi::kurmaZamaniGeldi() { return yenidenKurmaKapisi(sonKurmaMs_); } VkCommandBuffer PencereYuzeyi::kareBasla() { if (pencere_.simgeDurumundaMi() || aygit_.aygitKayipMi()) return VK_NULL_HANDLE; const VkDevice a = aygit_.aygit(); // Sonsuz bekleme yok: GPU önceki kareyi bitiremiyorsa (hung) VK_TIMEOUT → kareyi // atla, device-lost say. const auto bekleT0 = std::chrono::steady_clock::now(); const VkResult bekleSonuc = vkWaitForFences(a, 1, &kareCiti_[kareIndeks_], VK_TRUE, VulkanAygit::bekleZamanAsimi); { // TEŞHİS: GPU'nun ÖNCEKİ kareyi bitirmesi ne kadar sürdü? (donmanın ölçüsü) const f64 ms = std::chrono::duration( std::chrono::steady_clock::now() - bekleT0).count(); static gunluk::Seyrek seyrek{2000}; // sıcak yol: kare-başı spam YASAK if (ms > 100.0 && seyrek.gecer()) gunluk::uyari("[perf] GPU onceki kareyi {:.0f} ms'de bitirdi (kare={} gorsel={}, " "bastirilan {})", ms, kareIndeks_, aktifGorsel_, seyrek.bastirilanAl()); } if (bekleSonuc != VK_SUCCESS) { // Tek bir gecikme (yoğun resize / ağır kare) aygıt kaybı DEĞİL: kareyi atla, dene. // Tolerans art arda aşılırsa zamanAsimiTolere aygıtı kayıp işaretler (eski davranış). aygit_.zamanAsimiTolere("kareBasla/fence", bekleSonuc); return VK_NULL_HANDLE; } aygit_.zamanAsimiSifirla(); // başarılı bekleme → art arda sayacı sıfırla // Monoton kare sayacı + emekli tampon süzme (fence beklendi → eski tamponlar boşta). aygit_.kareIlerlet(); if (yenidenBoyutlandir_) { // Debounce: aralık dolmadıysa bu kareyi ATLA (bayrak duruyor, sonraki karede kurulur). if (!kurmaZamaniGeldi()) return VK_NULL_HANDLE; yenidenBoyutlandir_ = false; aygit_.izKaydet("recreate", (int)kareIndeks_, (int)aktifGorsel_, 0); takasZinciriYikVeKur(); if (aygit_.aygitKayipMi()) return VK_NULL_HANDLE; } const auto acqT0 = std::chrono::steady_clock::now(); const VkResult sonuc = vkAcquireNextImageKHR(a, takasZinciri_, VulkanAygit::bekleZamanAsimi, gorselHazir_[kareIndeks_], VK_NULL_HANDLE, &aktifGorsel_); { // TEŞHİS: acquire CPU'da ne kadar bekletti? (sunum motoru mu, kuyruk mu?) const f64 ms = std::chrono::duration( std::chrono::steady_clock::now() - acqT0).count(); static gunluk::Seyrek seyrekAcq{2000}; if (ms > 100.0 && seyrekAcq.gecer()) gunluk::uyari("[perf] acquire {:.0f} ms bekledi (sonuc={} gorsel={}, bastirilan {})", ms, (int)sonuc, aktifGorsel_, seyrekAcq.bastirilanAl()); } aygit_.izKaydet("acquire", (int)kareIndeks_, (int)aktifGorsel_, (int)sonuc); // SUBOPTIMAL ≠ OUT_OF_DATE: görsel BAŞARIYLA alındı ve gorselHazir_[kareIndeks_] // SİNYALLENDİ. Eskiden bu durumda kare atlanıyordu → semafor beklenmeden sinyalli // kalıyor, sonraki acquire AYNI sinyalli semaforu kullanıyordu (signal-signal hazard // = sürücüde GPU hang/DEVICE_LOST). Doğru davranış: bu kareyi NORMAL çiz (görüntü // bir kare hafif gerilir), yeniden kurmayı yalnızca İŞARETLE — semafor düzgün beklenir. if (sonuc == VK_SUBOPTIMAL_KHR) { yenidenBoyutlandir_ = true; // sonraki karede (debounce kapısıyla) kurulur } else if (sonuc == VK_ERROR_OUT_OF_DATE_KHR) { // Görsel ALINMADI, semafor sinyallenmedi → çizemeyiz. Debounce kapısı dolduysa kur. yenidenBoyutlandir_ = true; if (kurmaZamaniGeldi()) { yenidenBoyutlandir_ = false; takasZinciriYikVeKur(); } return VK_NULL_HANDLE; } else if (sonuc == VK_TIMEOUT || sonuc == VK_NOT_READY) { return VK_NULL_HANDLE; // kareyi atla (semafor sinyallenmedi) } else if (sonuc != VK_SUCCESS) { aygit_.aygitKaybiIsaretle("kareBasla/acquire", sonuc); return VK_NULL_HANDLE; } vkResetFences(a, 1, &kareCiti_[kareIndeks_]); VkCommandBuffer kb = komutTamponlar_[kareIndeks_]; vkResetCommandBuffer(kb, 0); VkCommandBufferBeginInfo baslat{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO}; VK_KONTROL(vkBeginCommandBuffer(kb, &baslat)); zamanHavuzuKur(); if (zamanDamgasiVar_) { // Bu slotun ÖNCEKİ karesinin GPU süresi (fence beklendi → sonuç hazır). u64 damga[2] = {0, 0}; if (vkGetQueryPoolResults(a, zamanHavuzu_, kareIndeks_ * 2, 2, sizeof(damga), damga, sizeof(u64), VK_QUERY_RESULT_64_BIT) == VK_SUCCESS && damga[1] > damga[0]) { VkPhysicalDeviceProperties oz{}; vkGetPhysicalDeviceProperties(aygit_.fizikselAygit(), &oz); const f64 gpuMs = (f64)(damga[1] - damga[0]) * (f64)oz.limits.timestampPeriod / 1e6; static gunluk::Seyrek seyrekGpu{2000}; if (gpuMs > 20.0 && seyrekGpu.gecer()) gunluk::uyari("[perf] GERCEK GPU yurutme {:.1f} ms (kare={}, bastirilan {})", gpuMs, kareIndeks_, seyrekGpu.bastirilanAl()); } vkCmdResetQueryPool(kb, zamanHavuzu_, kareIndeks_ * 2, 2); vkCmdWriteTimestamp(kb, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, zamanHavuzu_, kareIndeks_ * 2); } kareAcik_ = true; return kb; } void PencereYuzeyi::anaGecisBasla(float r, float g, float b) { VkCommandBuffer kb = komutTamponlar_[kareIndeks_]; VkClearValue temizle[2]{}; temizle[0].color = {{r, g, b, 1.0f}}; temizle[1].depthStencil = {0.0f, 0}; // reverse-Z: uzak = 0 VkRenderPassBeginInfo rp{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO}; rp.renderPass = renderPass_; rp.framebuffer = kareTamponlar_[aktifGorsel_]; rp.renderArea = {{0, 0}, takasBoyut_}; rp.clearValueCount = 2; rp.pClearValues = temizle; vkCmdBeginRenderPass(kb, &rp, VK_SUBPASS_CONTENTS_INLINE); const VkViewport gorunumAlan{0.0f, 0.0f, (float)takasBoyut_.width, (float)takasBoyut_.height, 0.0f, 1.0f}; const VkRect2D makas{{0, 0}, takasBoyut_}; vkCmdSetViewport(kb, 0, 1, &gorunumAlan); vkCmdSetScissor(kb, 0, 1, &makas); } void PencereYuzeyi::kareBitir() { if (!kareAcik_) return; kareAcik_ = false; VkCommandBuffer kb = komutTamponlar_[kareIndeks_]; vkCmdEndRenderPass(kb); if (zamanDamgasiVar_) // kare sonu damgası (GPU yürütme süresi teşhisi) vkCmdWriteTimestamp(kb, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, zamanHavuzu_, kareIndeks_ * 2 + 1); VK_KONTROL(vkEndCommandBuffer(kb)); const VkPipelineStageFlags bekleAsama = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo gonder{VK_STRUCTURE_TYPE_SUBMIT_INFO}; gonder.waitSemaphoreCount = 1; gonder.pWaitSemaphores = &gorselHazir_[kareIndeks_]; gonder.pWaitDstStageMask = &bekleAsama; gonder.commandBufferCount = 1; gonder.pCommandBuffers = &kb; gonder.signalSemaphoreCount = 1; gonder.pSignalSemaphores = &sunumHazir_[aktifGorsel_]; const VkResult gSonuc = vkQueueSubmit(aygit_.grafikKuyruk(), 1, &gonder, kareCiti_[kareIndeks_]); if (gSonuc != VK_SUCCESS) { aygit_.aygitKaybiIsaretle("kareBitir/submit", gSonuc); return; } VkPresentInfoKHR sunum{VK_STRUCTURE_TYPE_PRESENT_INFO_KHR}; sunum.waitSemaphoreCount = 1; sunum.pWaitSemaphores = &sunumHazir_[aktifGorsel_]; sunum.swapchainCount = 1; sunum.pSwapchains = &takasZinciri_; sunum.pImageIndices = &aktifGorsel_; aygit_.izKaydet("present-once", (int)kareIndeks_, (int)aktifGorsel_, 0); const VkResult sonuc = vkQueuePresentKHR(aygit_.grafikKuyruk(), &sunum); aygit_.izKaydet("present", (int)kareIndeks_, (int)aktifGorsel_, (int)sonuc); if (sonuc == VK_ERROR_OUT_OF_DATE_KHR || sonuc == VK_SUBOPTIMAL_KHR) { yenidenBoyutlandir_ = true; } else if (sonuc != VK_SUCCESS) { aygit_.aygitKaybiIsaretle("kareBitir/present", sonuc); return; } kareIndeks_ = (kareIndeks_ + 1) % ucusteKare; } } // namespace fiko