#include "fiko/render/golge_harita.hpp" #include #include #include #include #include "fiko/core/gunluk.hpp" #include "fiko/render/vertex.hpp" #include "fiko/render/vk_kontrol.hpp" #include "fiko/render/vulkan_baglam.hpp" #include "vk_mem_alloc.h" namespace fiko { namespace { // UBO düzeni — analiz.frag'daki ikizi ile BİREBİR eşleşmeli (std140). struct GolgeUbo { Mat4f vp; f32 par[4]; // x: açık mı (>0.5), y: derinlik eşiği, z/w: rezerv }; struct GolgeItme { Mat4f mvp; }; } // namespace // ---- Vulkan kaynakları (saf kamera matematiği: golge_kamera.cpp) ---- GolgeHarita::GolgeHarita(VulkanBaglam& vb, u32 cozunurluk, const std::string& shaderDizin) : vb_(vb) { cozunurluk_ = std::clamp(cozunurluk, 256u, 16384u); const VkDevice aygit = vb_.aygit(); // Derinlik-only geçiş: renk eki YOK, storeOp STORE (sonra örneklenecek), // finalLayout READ_ONLY → fragment shader sampler'ı doğrudan okur. VkAttachmentDescription ek{}; ek.format = kGolgeDerinlikFormat; ek.samples = VK_SAMPLE_COUNT_1_BIT; ek.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; ek.storeOp = VK_ATTACHMENT_STORE_OP_STORE; ek.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; ek.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; ek.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; ek.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; VkAttachmentReference derinlikRef{0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL}; VkSubpassDescription alt{}; alt.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; alt.colorAttachmentCount = 0; alt.pDepthStencilAttachment = &derinlikRef; // Geçiş sonrası fragment shader okuması (analiz.frag) için bariyer. VkSubpassDependency bagimliliklar[2]{}; bagimliliklar[0].srcSubpass = VK_SUBPASS_EXTERNAL; bagimliliklar[0].dstSubpass = 0; // ÖNCEKİ KARENİN DERİNLİK YAZIMI da kapsanmalı: gölge haritası TEK görüntüdür ve // ucusteKare kadar kare aynı anda uçar → bu karenin giriş layout geçişi, önceki // karenin storeOp derinlik yazımıyla (LATE_FRAGMENT_TESTS) senkronsuz kalıyordu. // Ölçüldü 07-21 (doğrulama katmanı, resize fırtınası): SYNC-HAZARD-WRITE-AFTER-WRITE. bagimliliklar[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_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_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; bagimliliklar[0].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; bagimliliklar[0].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; bagimliliklar[1].srcSubpass = 0; bagimliliklar[1].dstSubpass = VK_SUBPASS_EXTERNAL; bagimliliklar[1].srcStageMask = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; bagimliliklar[1].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_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 = 1; rp.pAttachments = &ek; rp.subpassCount = 1; rp.pSubpasses = &alt; rp.dependencyCount = 2; rp.pDependencies = bagimliliklar; VK_KONTROL(vkCreateRenderPass(aygit, &rp, nullptr, &gecis_)); // Sampler: NEAREST — derinlik degerini lineer filtrelemek anlamsiz deger uretir. VkSamplerCreateInfo sb{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; sb.magFilter = VK_FILTER_NEAREST; sb.minFilter = VK_FILTER_NEAREST; sb.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; sb.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; sb.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; // Reverse-Z: 0 = UZAK = "engel yok". Harita disina dusen uv → ORTULU DEGIL. sb.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK; sb.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; VK_KONTROL(vkCreateSampler(aygit, &sb, nullptr, &ornekleyici_)); // Descriptor: binding 0 = UBO (golgeVp + par), binding 1 = derinlik sampler. VkDescriptorSetLayoutBinding baglar[2]{}; baglar[0].binding = 0; baglar[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; baglar[0].descriptorCount = 1; baglar[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; baglar[1].binding = 1; baglar[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; baglar[1].descriptorCount = 1; baglar[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; VkDescriptorSetLayoutCreateInfo dsl{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO}; dsl.bindingCount = 2; dsl.pBindings = baglar; VK_KONTROL(vkCreateDescriptorSetLayout(aygit, &dsl, nullptr, &dsYerlesim_)); VkDescriptorPoolSize havuzlar[2]{}; havuzlar[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; havuzlar[0].descriptorCount = 1; havuzlar[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; havuzlar[1].descriptorCount = 1; VkDescriptorPoolCreateInfo hb{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO}; hb.maxSets = 1; hb.poolSizeCount = 2; hb.pPoolSizes = havuzlar; VK_KONTROL(vkCreateDescriptorPool(aygit, &hb, nullptr, &dsHavuz_)); VkDescriptorSetAllocateInfo da{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO}; da.descriptorPool = dsHavuz_; da.descriptorSetCount = 1; da.pSetLayouts = &dsYerlesim_; VK_KONTROL(vkAllocateDescriptorSets(aygit, &da, &ds_)); // UBO: cihaz-yerel + TRANSFER_DST — yazim vkCmdUpdateBuffer ile (GPU siralamasi). VkBufferCreateInfo bb{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; bb.size = sizeof(GolgeUbo); bb.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; VmaAllocationCreateInfo bt{}; bt.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; VK_KONTROL(vmaCreateBuffer(vb_.ayirici(), &bb, &bt, &ubo_, &uboT_, nullptr)); hedefKur(); // Derinlik-only pipeline: renk eki YOK, ayıklama YOK (web DoubleSide ikizi → // arka yüzler de engel: kapalı gövdede iç duvarlar da örter). BoruHattiAyar ay; ay.vertexSpv = shaderDizin + "golge.vert.spv"; ay.fragmentSpv = shaderDizin + "golge.frag.spv"; ay.itmeSabitBayt = sizeof(GolgeItme); ay.vertexAdim = sizeof(Vertex); // ince + kaba mesh ayni duzen (konum loc0, offset 0) ay.nitelikler = {{0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0}}; ay.gecis = gecis_; ay.renkEkiSayisi = 0; ay.ayiklama = VK_CULL_MODE_NONE; ay.derinlikTest = true; ay.derinlikYaz = true; ay.renkYaz = false; hat_ = BoruHatti(vb_, ay); } void GolgeHarita::hedefKur() { const VkDevice aygit = vb_.aygit(); VkImageCreateInfo gb{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; gb.imageType = VK_IMAGE_TYPE_2D; gb.format = kGolgeDerinlikFormat; gb.extent = {cozunurluk_, cozunurluk_, 1}; gb.mipLevels = 1; gb.arrayLayers = 1; gb.samples = VK_SAMPLE_COUNT_1_BIT; gb.tiling = VK_IMAGE_TILING_OPTIMAL; gb.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; VmaAllocationCreateInfo gt{}; gt.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; VK_KONTROL(vmaCreateImage(vb_.ayirici(), &gb, >, &gor_, &gorT_, nullptr)); VkImageViewCreateInfo gv{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; gv.image = gor_; gv.viewType = VK_IMAGE_VIEW_TYPE_2D; gv.format = kGolgeDerinlikFormat; // TEK aspect (DEPTH) — sampled image cok-aspect'li view kabul etmez (D32S8 bu // yuzden kullanilamaz; kViewportDerinlikFormat'i taklit ETME). gv.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1}; VK_KONTROL(vkCreateImageView(aygit, &gv, nullptr, &gorunum_)); VkFramebufferCreateInfo fb{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO}; fb.renderPass = gecis_; fb.attachmentCount = 1; fb.pAttachments = &gorunum_; fb.width = cozunurluk_; fb.height = cozunurluk_; fb.layers = 1; VK_KONTROL(vkCreateFramebuffer(aygit, &fb, nullptr, &kareTampon_)); // Sampler + UBO descriptor'ı tazele (görüntü yeniden kuruldu). VkDescriptorBufferInfo ubi{ubo_, 0, sizeof(GolgeUbo)}; VkDescriptorImageInfo gi{ornekleyici_, gorunum_, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL}; VkWriteDescriptorSet yaz[2]{}; yaz[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; yaz[0].dstSet = ds_; yaz[0].dstBinding = 0; yaz[0].descriptorCount = 1; yaz[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; yaz[0].pBufferInfo = &ubi; yaz[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; yaz[1].dstSet = ds_; yaz[1].dstBinding = 1; yaz[1].descriptorCount = 1; yaz[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; yaz[1].pImageInfo = &gi; vkUpdateDescriptorSets(aygit, 2, yaz, 0, nullptr); } void GolgeHarita::hedefYik() { const VkDevice aygit = vb_.aygit(); if (kareTampon_) vkDestroyFramebuffer(aygit, kareTampon_, nullptr); if (gorunum_) vkDestroyImageView(aygit, gorunum_, nullptr); if (gor_) vmaDestroyImage(vb_.ayirici(), gor_, gorT_); kareTampon_ = VK_NULL_HANDLE; gorunum_ = VK_NULL_HANDLE; gor_ = VK_NULL_HANDLE; } GolgeHarita::~GolgeHarita() { const VkDevice aygit = vb_.aygit(); vb_.bekle(); hedefYik(); hat_ = BoruHatti(); // pipeline + layout yikimi (RAII) if (dsHavuz_) vkDestroyDescriptorPool(aygit, dsHavuz_, nullptr); if (dsYerlesim_) vkDestroyDescriptorSetLayout(aygit, dsYerlesim_, nullptr); if (ornekleyici_) vkDestroySampler(aygit, ornekleyici_, nullptr); if (gecis_) vkDestroyRenderPass(aygit, gecis_, nullptr); if (ubo_) vmaDestroyBuffer(vb_.ayirici(), ubo_, uboT_); } void GolgeHarita::cozunurlukAyarla(u32 c) { c = std::clamp(c, 256u, 16384u); if (c == cozunurluk_) return; vb_.bekle(); hedefYik(); cozunurluk_ = c; hedefKur(); } void GolgeHarita::uboYaz(VkCommandBuffer kb, const Mat4f& golgeVp, bool acik, f32 esik, f32 kip, bool overlayVar) { GolgeUbo u{}; u.vp = golgeVp; u.par[0] = acik ? 1.0f : 0.0f; u.par[1] = esik; u.par[2] = kip; u.par[3] = overlayVar ? 1.0f : 0.0f; vkCmdUpdateBuffer(kb, ubo_, 0, sizeof(u), &u); // Transfer → fragment shader okumasi (analiz.frag) bariyeri. VkBufferMemoryBarrier b{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER}; b.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; // WRITE_AFTER_WRITE de kapsanmali: UBO TEK tampon, ucusteKare kadar kare ayni anda // ucuyor → bir sonraki karenin vkCmdUpdateBuffer'i (TRANSFER_WRITE) onceki karenin // yazimiyla senkronsuz kaliyordu. Olculdu 07-21 (dogrulama katmani, resize firtinasi): // 10 x SYNC-HAZARD-WRITE-AFTER-WRITE. dst'ye TRANSFER_WRITE + TRANSFER asamasi eklendi. b.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; b.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; b.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; b.buffer = ubo_; b.size = VK_WHOLE_SIZE; vkCmdPipelineBarrier(kb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, &b, 0, nullptr); } void GolgeHarita::basla(VkCommandBuffer kb) { VkClearValue temizle{}; temizle.depthStencil.depth = 0.0f; // REVERSE-Z: uzak = 0 (viewport_hedef ile ayni) VkRenderPassBeginInfo rb{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO}; rb.renderPass = gecis_; rb.framebuffer = kareTampon_; rb.renderArea = {{0, 0}, {cozunurluk_, cozunurluk_}}; rb.clearValueCount = 1; rb.pClearValues = &temizle; vkCmdBeginRenderPass(kb, &rb, VK_SUBPASS_CONTENTS_INLINE); const VkViewport vp{0, 0, (f32)cozunurluk_, (f32)cozunurluk_, 0, 1}; const VkRect2D makas{{0, 0}, {cozunurluk_, cozunurluk_}}; vkCmdSetViewport(kb, 0, 1, &vp); vkCmdSetScissor(kb, 0, 1, &makas); hat_.bagla(kb); } void GolgeHarita::bitir(VkCommandBuffer kb) { vkCmdEndRenderPass(kb); } void GolgeHarita::ciz(VkCommandBuffer kb, const Mat4f& mvp, VkBuffer vertex, VkBuffer indeks, u32 indeksSayi) { if (!vertex || !indeks || indeksSayi == 0) return; // NULL bind = DEVICE_LOST const GolgeItme it{mvp}; hat_.itmeSabitYaz(kb, &it, sizeof(it)); const VkDeviceSize ofs = 0; vkCmdBindVertexBuffers(kb, 0, 1, &vertex, &ofs); vkCmdBindIndexBuffer(kb, indeks, 0, VK_INDEX_TYPE_UINT32); vkCmdDrawIndexed(kb, indeksSayi, 1, 0, 0, 0); } } // namespace fiko