#include "fiko/render/secim_gpu.hpp" #include "fiko/render/pencere_yuzeyi.hpp" // yenidenKurmaKapisi (ortak resize debounce) #include #include #include "fiko/render/vertex.hpp" #include "fiko/render/vk_kontrol.hpp" #include "fiko/render/vulkan_baglam.hpp" #include "vk_mem_alloc.h" namespace fiko { // Global çalışma-zamanı pick ayarları (Ayarlar sayfası canlı yazar; kalite() ikizi). PickAyar& pickAyar() { static PickAyar a; return a; } namespace { constexpr VkFormat kimlikFormat = VK_FORMAT_R32G32_UINT; constexpr VkFormat derinlikFormat = VK_FORMAT_D32_SFLOAT; std::vector spvOku(const std::string& yol) { std::ifstream dosya(yol, std::ios::binary | std::ios::ate); FIKO_ASSERT(dosya.is_open(), "SPIR-V dosyasi acilamadi"); const auto bayt = (size_t)dosya.tellg(); std::vector veri(bayt / 4); dosya.seekg(0); dosya.read(reinterpret_cast(veri.data()), (std::streamsize)bayt); return veri; } VkShaderModule modul(VkDevice aygit, const std::vector& kod) { VkShaderModuleCreateInfo b{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; b.codeSize = kod.size() * 4; b.pCode = kod.data(); VkShaderModule m; VK_KONTROL(vkCreateShaderModule(aygit, &b, nullptr, &m)); return m; } // 64+64+16+64 = 208B (aygıt limiti 256; ModelItme zaten 256 istiyor). struct ItmeVeri { Mat4f mvp; Mat4f model; // kesit clip için dünya konumu (kesitSayi=0 iken shader okumaz) u32 kimlik[4]; // z: kesitSayi f32 kesit[16]; // 4 × vec4 (xyz normal, w d) }; } SecimGpu::SecimGpu(VulkanBaglam& vb, const std::string& shaderDizin) : vb_(vb) { VkDevice aygit = vb.aygit(); VkAttachmentDescription ekler[2]{}; ekler[0].format = kimlikFormat; 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_TRANSFER_SRC_OPTIMAL; ekler[1].format = derinlikFormat; 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; VkSubpassDependency bagimliliklar[2]{}; bagimliliklar[0].srcSubpass = VK_SUBPASS_EXTERNAL; bagimliliklar[0].dstSubpass = 0; // src YALNIZ readback (TRANSFER_READ) değil, ÖNCEKİ KARENİN bu hedefe yazması da // olmalı: ID-pick hedefi TEK görüntüdür ve ucusteKare kadar kare aynı anda uçar → // bu karenin loadOp/layout geçişi önceki karenin storeOp derinlik yazımıyla // senkronsuz kalıyordu (ölçüldü 07-21, doğrulama katmanı: SYNC-HAZARD-WAW ×10; // gerçek GPU'da resize timing'inde DEVICE_LOST kaynağı). viewportGecis ile aynı kalıp. bagimliliklar[0].srcStageMask = VK_PIPELINE_STAGE_TRANSFER_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_TRANSFER_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_TRANSFER_BIT; bagimliliklar[1].dstAccessMask = VK_ACCESS_TRANSFER_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, &gecis_)); VkPushConstantRange itme{}; itme.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; itme.size = sizeof(ItmeVeri); VkPipelineLayoutCreateInfo yl{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO}; yl.pushConstantRangeCount = 1; yl.pPushConstantRanges = &itme; VK_KONTROL(vkCreatePipelineLayout(aygit, &yl, nullptr, &yerlesim_)); // Pick sadece pos(loc0)+altKimlik ister; yuz icin altKimlik offset 36, cizgi icin 12. const VkVertexInputAttributeDescription yuzPick[2] = { {0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0}, {1, 0, VK_FORMAT_R32_UINT, 36}}; // Yüz pick'i DİNAMİK biasla geriye itilir (render ikizi) → kenar/nokta paylaşılan // kenarda öne çıkar (değer Ayarlar'dan canlı). yuzHat_ = pipelineKur(shaderDizin, sizeof(Vertex), yuzPick, 2, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 1.0f, true, true); const VkVertexInputAttributeDescription cizgiPick[2] = { {0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0}, {1, 0, VK_FORMAT_R32_UINT, 12}}; // Kenar/nokta pick: 7px kalın (mıknatıs) + DİNAMİK ileri derinlik biası (içbükey // dip kenarlar yüzeylerin önüne alınır → seçilebilir; değer Ayarlar'dan canlı). // Duvar arkası (büyük derinlik farkı) yine örtülü — sadece eş-derinlik/dip öne çıkar. kenarHat_ = pipelineKur(shaderDizin, sizeof(KenarVertex), cizgiPick, 2, VK_PRIMITIVE_TOPOLOGY_LINE_LIST, 7.0f, true, true); noktaHat_ = pipelineKur(shaderDizin, sizeof(KenarVertex), cizgiPick, 2, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, 1.0f, true, true); // Referans datum pick pipeline'ları — derinlik testi kapalı (occlusion-bağımsız // seçim: önlerinde solid olsa da tıklanır). Aksi tümüyle kenar/nokta ile aynı. refKenarHat_ = pipelineKur(shaderDizin, sizeof(KenarVertex), cizgiPick, 2, VK_PRIMITIVE_TOPOLOGY_LINE_LIST, 7.0f, false); refNoktaHat_ = pipelineKur(shaderDizin, sizeof(KenarVertex), cizgiPick, 2, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, 1.0f, false); for (u32 i = 0; i < ucusteKare; ++i) { VkBufferCreateInfo tb{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; tb.size = (VkDeviceSize)kPickPencere * kPickPencere * 8; // (2R+1)² × uvec2 tb.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; VmaAllocationCreateInfo ta{}; ta.usage = VMA_MEMORY_USAGE_AUTO; ta.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; VmaAllocationInfo tbilgi; VK_KONTROL(vmaCreateBuffer(vb.ayirici(), &tb, &ta, &okumaTampon_[i], &okumaTahsis_[i], &tbilgi)); okumaVeri_[i] = tbilgi.pMappedData; } hedefOlustur(); } VkPipeline SecimGpu::pipelineKur(const std::string& shaderDizin, u32 vertexAdim, const VkVertexInputAttributeDescription* nitelikler, u32 nitelikSayi, VkPrimitiveTopology topoloji, f32 cizgiKalinlik, bool derinlikTest, bool dinamikBias) { VkDevice aygit = vb_.aygit(); const VkShaderModule vert = modul(aygit, spvOku(shaderDizin + "secim.vert.spv")); const VkShaderModule frag = modul(aygit, spvOku(shaderDizin + "secim.frag.spv")); VkPipelineShaderStageCreateInfo asamalar[2]{}; asamalar[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; asamalar[0].stage = VK_SHADER_STAGE_VERTEX_BIT; asamalar[0].module = vert; asamalar[0].pName = "main"; asamalar[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; asamalar[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; asamalar[1].module = frag; asamalar[1].pName = "main"; VkVertexInputBindingDescription baglama{0, vertexAdim, VK_VERTEX_INPUT_RATE_VERTEX}; VkPipelineVertexInputStateCreateInfo girdi{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO}; girdi.vertexBindingDescriptionCount = 1; girdi.pVertexBindingDescriptions = &baglama; girdi.vertexAttributeDescriptionCount = nitelikSayi; girdi.pVertexAttributeDescriptions = nitelikler; VkPipelineInputAssemblyStateCreateInfo birlestir{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO}; birlestir.topology = topoloji; VkPipelineViewportStateCreateInfo gorunumAlan{VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO}; gorunumAlan.viewportCount = 1; gorunumAlan.scissorCount = 1; VkPipelineRasterizationStateCreateInfo tarama{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO}; tarama.polygonMode = VK_POLYGON_MODE_FILL; tarama.cullMode = VK_CULL_MODE_NONE; // secimde arka yuzler de sayilir tarama.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; tarama.lineWidth = cizgiKalinlik; // Dinamik derinlik biası: değerler vkCmdSetDepthBias ile canlı ayarlanır // (kenar/nokta öne, yüz arkaya → dipteki kesişen edge seçilebilir; Ayarlar'dan tunable). if (dinamikBias) tarama.depthBiasEnable = VK_TRUE; VkPipelineMultisampleStateCreateInfo ornekleme{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO}; ornekleme.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; VkPipelineDepthStencilStateCreateInfo derinlik{VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO}; // Referans datum pick'i derinlik testsiz (arkada kalsa da seçilir); diğerleri // normal derinlikli. Yazma yalnız test açıkken anlamlı. derinlik.depthTestEnable = derinlikTest ? VK_TRUE : VK_FALSE; derinlik.depthWriteEnable = derinlikTest ? VK_TRUE : VK_FALSE; // Kenar/nokta yuzeye esit derinlikte kazansin diye LEQUAL derinlik.depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL; // reverse-Z (yakın→1) VkPipelineColorBlendAttachmentState harman{}; harman.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT; VkPipelineColorBlendStateCreateInfo renk{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO}; renk.attachmentCount = 1; renk.pAttachments = &harman; const VkDynamicState dinamikler[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_DEPTH_BIAS}; VkPipelineDynamicStateCreateInfo dinamik{VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO}; dinamik.dynamicStateCount = dinamikBias ? 3u : 2u; dinamik.pDynamicStates = dinamikler; VkGraphicsPipelineCreateInfo bilgi{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO}; bilgi.stageCount = 2; bilgi.pStages = asamalar; bilgi.pVertexInputState = &girdi; bilgi.pInputAssemblyState = &birlestir; bilgi.pViewportState = &gorunumAlan; bilgi.pRasterizationState = &tarama; bilgi.pMultisampleState = &ornekleme; bilgi.pDepthStencilState = &derinlik; bilgi.pColorBlendState = &renk; bilgi.pDynamicState = &dinamik; bilgi.layout = yerlesim_; bilgi.renderPass = gecis_; VkPipeline hat; VK_KONTROL(vkCreateGraphicsPipelines(aygit, VK_NULL_HANDLE, 1, &bilgi, nullptr, &hat)); vkDestroyShaderModule(aygit, vert, nullptr); vkDestroyShaderModule(aygit, frag, nullptr); return hat; } void SecimGpu::hedefOlustur() { if (boyut_.width == 0) boyut_ = vb_.boyut(); VkDevice aygit = vb_.aygit(); VkImageCreateInfo gb{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; gb.imageType = VK_IMAGE_TYPE_2D; gb.format = kimlikFormat; gb.extent = {boyut_.width, boyut_.height, 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_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; VmaAllocationCreateInfo gt{}; gt.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; VK_KONTROL(vmaCreateImage(vb_.ayirici(), &gb, >, &kimlikGorsel_, &kimlikTahsis_, nullptr)); VkImageViewCreateInfo gv{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; gv.image = kimlikGorsel_; gv.viewType = VK_IMAGE_VIEW_TYPE_2D; gv.format = kimlikFormat; gv.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; VK_KONTROL(vkCreateImageView(aygit, &gv, nullptr, &kimlikGorunum_)); gb.format = derinlikFormat; gb.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; VK_KONTROL(vmaCreateImage(vb_.ayirici(), &gb, >, &derinlikGorsel_, &derinlikTahsis_, nullptr)); gv.image = derinlikGorsel_; gv.format = derinlikFormat; gv.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1}; VK_KONTROL(vkCreateImageView(aygit, &gv, nullptr, &derinlikGorunum_)); const VkImageView gorunumler[2] = {kimlikGorunum_, derinlikGorunum_}; VkFramebufferCreateInfo fb{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO}; fb.renderPass = gecis_; fb.attachmentCount = 2; fb.pAttachments = gorunumler; fb.width = boyut_.width; fb.height = boyut_.height; fb.layers = 1; VK_KONTROL(vkCreateFramebuffer(aygit, &fb, nullptr, &kareTampon_)); } void SecimGpu::hedefYik() { VkDevice aygit = vb_.aygit(); if (kareTampon_) vkDestroyFramebuffer(aygit, kareTampon_, nullptr); if (kimlikGorunum_) vkDestroyImageView(aygit, kimlikGorunum_, nullptr); if (kimlikGorsel_) vmaDestroyImage(vb_.ayirici(), kimlikGorsel_, kimlikTahsis_); if (derinlikGorunum_) vkDestroyImageView(aygit, derinlikGorunum_, nullptr); if (derinlikGorsel_) vmaDestroyImage(vb_.ayirici(), derinlikGorsel_, derinlikTahsis_); kareTampon_ = VK_NULL_HANDLE; kimlikGorunum_ = VK_NULL_HANDLE; kimlikGorsel_ = VK_NULL_HANDLE; derinlikGorunum_ = VK_NULL_HANDLE; derinlikGorsel_ = VK_NULL_HANDLE; } void SecimGpu::boyutAyarla(u32 g, u32 y) { g = std::max(g, 1u); y = std::max(y, 1u); if (g == boyut_.width && y == boyut_.height) return; // Kardeş ViewportHedef ile aynı resize debounce kapısı (sürüklerken yık/kur churn'ü). if (!PencereYuzeyi::yenidenKurmaKapisi(sonKurmaMs_)) return; // Boyut KARARLI olana dek yeniden kurma (render.hedef_kararlilik_ms; 0 = eski yol). static u32 sonG = 0, sonY = 0; static long long sonDegisim = 0; if (!PencereYuzeyi::boyutKararliMi(g, y, sonG, sonY, sonDegisim)) return; // Aygit kayipken resize gelirse kayip aygit uzerinde vmaCreateImage/vkCreateImageView // → VK_KONTROL abort. Kardes ViewportHedef::boyutla ile ayni koruma (viewport_hedef.cpp). if (!vb_.bekle("SecimGpu::boyutla")) return; hedefYik(); boyut_ = {g, y}; hedefOlustur(); okumaGecerli_[0] = okumaGecerli_[1] = false; } SecimGpu::~SecimGpu() { vb_.bekle(); hedefYik(); for (u32 i = 0; i < ucusteKare; ++i) if (okumaTampon_[i]) vmaDestroyBuffer(vb_.ayirici(), okumaTampon_[i], okumaTahsis_[i]); VkDevice aygit = vb_.aygit(); if (yuzHat_) vkDestroyPipeline(aygit, yuzHat_, nullptr); if (kenarHat_) vkDestroyPipeline(aygit, kenarHat_, nullptr); if (noktaHat_) vkDestroyPipeline(aygit, noktaHat_, nullptr); if (refKenarHat_) vkDestroyPipeline(aygit, refKenarHat_, nullptr); if (refNoktaHat_) vkDestroyPipeline(aygit, refNoktaHat_, nullptr); if (yerlesim_) vkDestroyPipelineLayout(aygit, yerlesim_, nullptr); if (gecis_) vkDestroyRenderPass(aygit, gecis_, nullptr); } void SecimGpu::sonucOku(u32 kareIndeks) { if (!okumaGecerli_[kareIndeks]) return; // Bu tampon zaten uygulanmış (yeni pick yok) → sonVurus_ dondur, ping-pong'un iki // eski sonucu arasında zıplama YOK. Yeni submit sadece daha yüksek sıra taşır. if (bufferSeq_[kareIndeks] <= uygulananSeq_) return; uygulananSeq_ = bufferSeq_[kareIndeks]; const u32* p = reinterpret_cast(okumaVeri_[kareIndeks]); const PencereBilgi pb = pencereBilgi_[kareIndeks]; // Pencereyi tara: öncelik nokta(3)>kenar(2)>yüz(1); eşitlikte merkeze en yakın. // Öncelik-sırası (düşük=iyi): nokta 0, kenar 1, yüz 2 → ince kenar/nokta, // merkezde yüz olsa bile yarıçap içinde kazanır (mıknatıslı + titreşimsiz). auto sira = [](u32 tur) -> int { return tur == TUR_NOKTA ? 0 : tur == TUR_KENAR ? 1 : tur == TUR_YUZ ? 2 : 3; }; int enSira = 4; i32 enMesafe = 0x7FFFFFFF; SecimVurusu en{}; for (u32 j = 0; j < pb.y; ++j) { for (u32 i = 0; i < pb.g; ++i) { const u32* px = p + (size_t)(j * pb.g + i) * 2; const u32 govde = px[0]; if (govde == 0) continue; const u32 tur = px[1] >> 28; const int s = sira(tur); if (s >= 3) continue; const i32 dx = (i32)i - pb.mDx, dy = (i32)j - pb.mDy; const i32 mes = dx * dx + dy * dy; if (s < enSira || (s == enSira && mes < enMesafe)) { enSira = s; enMesafe = mes; en = {govde, tur, px[1] & turMaske}; } } } sonVurus_ = en; } void SecimGpu::gecisBasla(VkCommandBuffer kb, i32 imlecX, i32 imlecY) { const i32 x = std::clamp(imlecX, 0, (i32)boyut_.width - 1); const i32 y = std::clamp(imlecY, 0, (i32)boyut_.height - 1); sonImlecX_ = x; sonImlecY_ = y; // İmleç çevresinde (2R+1)² pencere — R Ayarlar'dan (maks'a kırpılı); kenara/köşeye // kırpılır (pencere küçülür). const i32 R = std::clamp(pickAyar().yaricap, 0, (i32)kPickYaricapMax); const i32 x0 = std::max(0, x - R); const i32 y0 = std::max(0, y - R); const i32 x1 = std::min((i32)boyut_.width - 1, x + R); const i32 y1 = std::min((i32)boyut_.height - 1, y + R); pencereGuncel_ = {x0, y0, (u32)(x1 - x0 + 1), (u32)(y1 - y0 + 1), x - x0, y - y0}; VkClearValue temizle[2]{}; temizle[0].color.uint32[0] = 0; temizle[0].color.uint32[1] = 0; temizle[1].depthStencil = {0.0f, 0}; // reverse-Z: uzak = 0 VkRenderPassBeginInfo rp{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO}; rp.renderPass = gecis_; rp.framebuffer = kareTampon_; // renderArea'yı imleç scissor penceresine daralt (framebuffer tam boyutta kalır): CLEAR // yalnız (2R+1)² küçük bölgeye uygulanır → her hover'da tam-ekran pick buffer clear // bandwidth'i (2560×1440) israfı biter. Scissor zaten aynı bölge → çizim değişmez. rp.renderArea = {{x0, y0}, {pencereGuncel_.g, pencereGuncel_.y}}; rp.clearValueCount = 2; rp.pClearValues = temizle; vkCmdBeginRenderPass(kb, &rp, VK_SUBPASS_CONTENTS_INLINE); const VkViewport gorunumAlan{0.0f, 0.0f, (float)boyut_.width, (float)boyut_.height, 0.0f, 1.0f}; const VkRect2D makas{{x0, y0}, {pencereGuncel_.g, pencereGuncel_.y}}; vkCmdSetViewport(kb, 0, 1, &gorunumAlan); vkCmdSetScissor(kb, 0, 1, &makas); } void SecimGpu::yuzBagla(VkCommandBuffer kb) const { vkCmdBindPipeline(kb, VK_PIPELINE_BIND_POINT_GRAPHICS, yuzHat_); vkCmdSetDepthBias(kb, pickAyar().yuzBias, 0.0f, pickAyar().yuzBias); } void SecimGpu::kenarBagla(VkCommandBuffer kb) const { vkCmdBindPipeline(kb, VK_PIPELINE_BIND_POINT_GRAPHICS, kenarHat_); vkCmdSetDepthBias(kb, pickAyar().kenarBias, 0.0f, pickAyar().kenarBias); } void SecimGpu::noktaBagla(VkCommandBuffer kb) const { vkCmdBindPipeline(kb, VK_PIPELINE_BIND_POINT_GRAPHICS, noktaHat_); vkCmdSetDepthBias(kb, pickAyar().kenarBias, 0.0f, pickAyar().kenarBias); } void SecimGpu::refKenarBagla(VkCommandBuffer kb) const { vkCmdBindPipeline(kb, VK_PIPELINE_BIND_POINT_GRAPHICS, refKenarHat_); } void SecimGpu::refNoktaBagla(VkCommandBuffer kb) const { vkCmdBindPipeline(kb, VK_PIPELINE_BIND_POINT_GRAPHICS, refNoktaHat_); } void SecimGpu::itmeYaz(VkCommandBuffer kb, const Mat4f& mvp, u32 govde, u32 tur, const Mat4f& model, const f32* kesit, u32 kesitSayi) const { ItmeVeri v; v.mvp = mvp; v.model = model; v.kimlik[0] = govde; v.kimlik[1] = tur << 28; v.kimlik[2] = kesit ? std::min(kesitSayi, 4u) : 0u; v.kimlik[3] = 0; for (u32 i = 0; i < v.kimlik[2] * 4; ++i) v.kesit[i] = kesit[i]; for (u32 i = v.kimlik[2] * 4; i < 16; ++i) v.kesit[i] = 0.0f; vkCmdPushConstants(kb, yerlesim_, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(v), &v); } void SecimGpu::gecisBitir(VkCommandBuffer kb) { vkCmdEndRenderPass(kb); const u32 kareIndeks = vb_.kareIndeks(); // Pencere bölgesini (satır-major) geri oku; meta bu kare-indeksine yazılır → // gecikmeli sonucOku aynı geometriyle çözer. VkBufferImageCopy kopya{}; kopya.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; kopya.imageOffset = {pencereGuncel_.x0, pencereGuncel_.y0, 0}; kopya.imageExtent = {pencereGuncel_.g, pencereGuncel_.y, 1}; vkCmdCopyImageToBuffer(kb, kimlikGorsel_, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, okumaTampon_[kareIndeks], 1, &kopya); pencereBilgi_[kareIndeks] = pencereGuncel_; bufferSeq_[kareIndeks] = ++gonderSeq_; // taze pick submit'i (idle-freeze için) okumaGecerli_[kareIndeks] = true; } }