#include "fiko/render/compute_baglam.hpp" #include #include #include #include #include "fiko/core/gunluk.hpp" #include "fiko/platform/sistem.hpp" #include "fiko/render/vk_kontrol.hpp" #include "fiko/render/vulkan_baglam.hpp" #include "vk_mem_alloc.h" namespace fiko { namespace { // Sonsuz bekleme yasak (vulkan_baglam FIX ile ayni sinir): 2 sn (ns). constexpr u64 bekleZamanAsimi = 2'000'000'000ull; std::vector spvOku(const std::string& yol) { // Göreli yol → exe dizinine göre çöz (render shader'ları gibi; CWD'den bağımsız). // Aksi halde uygulama farklı dizinden başlatılınca compute pipeline sessizce // CPU'ya düşer (SPV bulunamaz). Mutlak yol olduğu gibi kullanılır. std::string tamYol = yol; if (std::filesystem::path(yol).is_relative()) tamYol = sistem::calistirilabilirDizin() + yol; std::ifstream dosya(tamYol, std::ios::binary | std::ios::ate); if (!dosya.is_open()) { gunluk::hata("Compute SPIR-V acilamadi: {}", tamYol); return {}; } const auto bayt = static_cast(dosya.tellg()); std::vector veri(bayt / 4); dosya.seekg(0); dosya.read(reinterpret_cast(veri.data()), static_cast(bayt)); return veri; } } // namespace // =========================================================================== // SsboTampon // =========================================================================== SsboTampon::SsboTampon(VulkanBaglam& vb, u64 bayt, VkBufferUsageFlags ekKullanim, bool hostErisim) : vb_(&vb), bayt_(bayt) { VkBufferCreateInfo bilgi{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; bilgi.size = bayt; bilgi.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | ekKullanim; VmaAllocationCreateInfo tahsis{}; if (hostErisim) { // HOST_VISIBLE|COHERENT: kalici map ile kilitsiz oku/yaz (readback yolu). tahsis.usage = VMA_MEMORY_USAGE_AUTO; tahsis.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; } else { tahsis.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; } VmaAllocationInfo aBilgi{}; const VkResult r = vmaCreateBuffer(vb.ayirici(), &bilgi, &tahsis, &tampon_, &tahsis_, &aBilgi); if (r != VK_SUCCESS) { gunluk::hata("SSBO ayrilamadi ({} bayt) sonuc={}", bayt, static_cast(r)); tampon_ = VK_NULL_HANDLE; return; } haritali_ = hostErisim ? aBilgi.pMappedData : nullptr; } SsboTampon& SsboTampon::operator=(SsboTampon&& o) noexcept { if (this != &o) { if (tampon_) vmaDestroyBuffer(vb_->ayirici(), tampon_, tahsis_); vb_ = o.vb_; tampon_ = std::exchange(o.tampon_, VK_NULL_HANDLE); tahsis_ = std::exchange(o.tahsis_, nullptr); bayt_ = std::exchange(o.bayt_, 0); haritali_ = std::exchange(o.haritali_, nullptr); } return *this; } SsboTampon::~SsboTampon() { if (tampon_) vmaDestroyBuffer(vb_->ayirici(), tampon_, tahsis_); } void SsboTampon::yaz(const void* veri, u64 bayt, u64 ofset) { if (!haritali_ || ofset + bayt > bayt_) return; std::memcpy(static_cast(haritali_) + ofset, veri, bayt); } void SsboTampon::oku(void* hedef, u64 bayt, u64 ofset) const { if (!haritali_ || ofset + bayt > bayt_) return; std::memcpy(hedef, static_cast(haritali_) + ofset, bayt); } // =========================================================================== // ComputeBoruHatti // =========================================================================== ComputeBoruHatti::ComputeBoruHatti(VulkanBaglam& vb, const std::string& spvYol, u32 ssboSayisi, u32 itmeBayt) : vb_(&vb), aygit_(vb.aygit()), ssboSayisi_(ssboSayisi) { const std::vector kod = spvOku(spvYol); if (kod.empty()) return; // gecersiz -> boru_ NULL (fabrika CPU'ya duser) VkShaderModuleCreateInfo mBilgi{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; mBilgi.codeSize = kod.size() * 4; mBilgi.pCode = kod.data(); if (vkCreateShaderModule(aygit_, &mBilgi, nullptr, &modul_) != VK_SUCCESS) { modul_ = VK_NULL_HANDLE; return; } // N adet STORAGE_BUFFER binding (0..N-1). std::vector baglar(ssboSayisi); for (u32 i = 0; i < ssboSayisi; ++i) { baglar[i].binding = i; baglar[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; baglar[i].descriptorCount = 1; baglar[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; } VkDescriptorSetLayoutCreateInfo dslBilgi{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO}; dslBilgi.bindingCount = ssboSayisi; dslBilgi.pBindings = baglar.data(); VK_KONTROL(vkCreateDescriptorSetLayout(aygit_, &dslBilgi, nullptr, &dsYerlesim_)); VkPushConstantRange itme{}; itme.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; itme.size = itmeBayt; VkPipelineLayoutCreateInfo plBilgi{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO}; plBilgi.setLayoutCount = 1; plBilgi.pSetLayouts = &dsYerlesim_; if (itmeBayt > 0) { plBilgi.pushConstantRangeCount = 1; plBilgi.pPushConstantRanges = &itme; } VK_KONTROL(vkCreatePipelineLayout(aygit_, &plBilgi, nullptr, &yerlesim_)); VkPipelineShaderStageCreateInfo asama{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO}; asama.stage = VK_SHADER_STAGE_COMPUTE_BIT; asama.module = modul_; asama.pName = "main"; VkComputePipelineCreateInfo cpBilgi{VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO}; cpBilgi.stage = asama; cpBilgi.layout = yerlesim_; VK_KONTROL(vkCreateComputePipelines(aygit_, VK_NULL_HANDLE, 1, &cpBilgi, nullptr, &boru_)); // Descriptor havuzu + tek set. VkDescriptorPoolSize havuzBoyut{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, ssboSayisi}; VkDescriptorPoolCreateInfo dpBilgi{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO}; dpBilgi.maxSets = 1; dpBilgi.poolSizeCount = 1; dpBilgi.pPoolSizes = &havuzBoyut; VK_KONTROL(vkCreateDescriptorPool(aygit_, &dpBilgi, nullptr, &havuz_)); VkDescriptorSetAllocateInfo dsTahsis{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO}; dsTahsis.descriptorPool = havuz_; dsTahsis.descriptorSetCount = 1; dsTahsis.pSetLayouts = &dsYerlesim_; VK_KONTROL(vkAllocateDescriptorSets(aygit_, &dsTahsis, &ds_)); } ComputeBoruHatti::~ComputeBoruHatti() { if (!aygit_) return; if (havuz_) vkDestroyDescriptorPool(aygit_, havuz_, nullptr); if (boru_) vkDestroyPipeline(aygit_, boru_, nullptr); if (yerlesim_) vkDestroyPipelineLayout(aygit_, yerlesim_, nullptr); if (dsYerlesim_) vkDestroyDescriptorSetLayout(aygit_, dsYerlesim_, nullptr); if (modul_) vkDestroyShaderModule(aygit_, modul_, nullptr); } void ComputeBoruHatti::bagla(const std::vector& ssbolar) { if (!ds_ || ssbolar.empty()) return; const u32 n = static_cast(ssbolar.size()); std::vector bilgiler(n); std::vector yazmalar(n); for (u32 i = 0; i < n; ++i) { bilgiler[i] = {ssbolar[i], 0, VK_WHOLE_SIZE}; yazmalar[i] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; yazmalar[i].dstSet = ds_; yazmalar[i].dstBinding = i; yazmalar[i].descriptorCount = 1; yazmalar[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; yazmalar[i].pBufferInfo = &bilgiler[i]; } vkUpdateDescriptorSets(aygit_, n, yazmalar.data(), 0, nullptr); } void ComputeBoruHatti::dispatchKaydet(VkCommandBuffer kb, const void* itme, u32 itmeBayt, u32 gx, u32 gy, u32 gz) const { vkCmdBindPipeline(kb, VK_PIPELINE_BIND_POINT_COMPUTE, boru_); vkCmdBindDescriptorSets(kb, VK_PIPELINE_BIND_POINT_COMPUTE, yerlesim_, 0, 1, &ds_, 0, nullptr); if (itmeBayt > 0 && itme) vkCmdPushConstants(kb, yerlesim_, VK_SHADER_STAGE_COMPUTE_BIT, 0, itmeBayt, itme); vkCmdDispatch(kb, gx, gy, gz); } void ComputeBoruHatti::ssboBariyeri(VkCommandBuffer kb) { // SSBO write -> sonraki dispatch read (spec §1: her adimda barrier). VkMemoryBarrier engel{VK_STRUCTURE_TYPE_MEMORY_BARRIER}; engel.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; engel.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; vkCmdPipelineBarrier(kb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, &engel, 0, nullptr, 0, nullptr); } // =========================================================================== // ComputeBaglam // =========================================================================== ComputeBaglam::ComputeBaglam(VulkanBaglam& vb) : vb_(&vb), aygit_(vb.aygit()) { VkCommandPoolCreateInfo hBilgi{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO}; hBilgi.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; hBilgi.queueFamilyIndex = vb.grafikAile(); VK_KONTROL(vkCreateCommandPool(aygit_, &hBilgi, nullptr, &havuz_)); VkFenceCreateInfo cBilgi{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO}; VK_KONTROL(vkCreateFence(aygit_, &cBilgi, nullptr, &cit_)); } ComputeBaglam::~ComputeBaglam() { if (!aygit_) return; if (cit_) vkDestroyFence(aygit_, cit_, nullptr); if (havuz_) vkDestroyCommandPool(aygit_, havuz_, nullptr); } bool ComputeBaglam::calis(const std::function& kayit) { if (!aygit_ || vb_->aygitKayipMi()) return false; VkCommandBufferAllocateInfo tahsis{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO}; tahsis.commandPool = havuz_; tahsis.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; tahsis.commandBufferCount = 1; VkCommandBuffer kb; VK_KONTROL(vkAllocateCommandBuffers(aygit_, &tahsis, &kb)); 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)); vkResetFences(aygit_, 1, &cit_); VkSubmitInfo gonder{VK_STRUCTURE_TYPE_SUBMIT_INFO}; gonder.commandBufferCount = 1; gonder.pCommandBuffers = &kb; VK_KONTROL(vkQueueSubmit(vb_->grafikKuyruk(), 1, &gonder, cit_)); // Timeout'lu bekleme: GPU hung ise VK_TIMEOUT -> device-lost say, sonsuz asilma yok. const VkResult f = vkWaitForFences(aygit_, 1, &cit_, VK_TRUE, bekleZamanAsimi); vkFreeCommandBuffers(aygit_, havuz_, 1, &kb); if (f != VK_SUCCESS) { // GPU hung/kayip: sonsuz asilma yok. Cagiran CPU fallback'e gecer; grafik // dongusunun kendi bekle() FIX'i device-lost'u tek noktadan yonetir. gunluk::hata("Compute dispatch zaman asimi/device-lost sonuc={}", static_cast(f)); return false; } return true; } } // namespace fiko