#include "fiko/ui/seffaf_pencere.hpp" #include #include #include #include #include #include #include "fiko/core/gunluk.hpp" #include "fiko/render/vk_kontrol.hpp" #include "fiko/render/vulkan_baglam.hpp" #include "imgui_impl_vulkan.h" #include "imgui_internal.h" #include "vk_mem_alloc.h" namespace fiko::ui { namespace { VulkanBaglam* g_vb = nullptr; bool g_devreDisi = false; // keyed-mutex protokolü bozulursa oturum boyu kapat template void birak(T*& p) { if (p) { p->Release(); p = nullptr; } } bool* g_solTik = nullptr; // aktif overlay'in tık mandalları (WndProc → kareBasla arası) bool* g_sagTik = nullptr; // Overlay HWND fare tıklarını YEREL mandallara yazar; ImGui io'ya İLETİLMEZ — // aksi halde ImGui aynı tıkı imlecin altındaki kendi penceresine de uygular // (arkadaki butona tıklama bug'ı). Odak asla alınmaz. LRESULT CALLBACK seffafWndProc(HWND hwnd, UINT mesaj, WPARAM w, LPARAM l) { switch (mesaj) { case WM_MOUSEACTIVATE: return MA_NOACTIVATE; case WM_LBUTTONDOWN: if (g_solTik) *g_solTik = true; return 0; case WM_RBUTTONDOWN: if (g_sagTik) *g_sagTik = true; return 0; case WM_LBUTTONUP: case WM_RBUTTONUP: return 0; default: break; } return DefWindowProcW(hwnd, mesaj, w, l); } } // namespace struct SeffafPencere::Ic { int g = 0, y = 0; bool gorunur = false; bool solTik = false, sagTik = false; // WndProc mandalları (tüketilerek okunur) ImVec2 poz{}; HWND hwnd = nullptr; // D3D11 + DirectComposition ID3D11Device* d3dAygit = nullptr; ID3D11DeviceContext* d3dBaglam = nullptr; IDXGISwapChain1* takas = nullptr; IDCompositionDevice* dcAygit = nullptr; IDCompositionTarget* dcHedef = nullptr; IDCompositionVisual* dcGorsel = nullptr; ID3D11Texture2D* paylasilan = nullptr; IDXGIKeyedMutex* kilit = nullptr; HANDLE tutamac = nullptr; // Vulkan tarafı VkImage paylasilanVk = VK_NULL_HANDLE; // D3D dokusunun içe aktarımı (UNORM) VkDeviceMemory paylasilanBellek = VK_NULL_HANDLE; VkImage renk = VK_NULL_HANDLE; // render hedefi (ana pass formatı) VmaAllocation renkT = nullptr; VkImageView renkGorunum = VK_NULL_HANDLE; VkImage derinlik = VK_NULL_HANDLE; VmaAllocation derinlikT = nullptr; VkImageView derinlikGorunum = VK_NULL_HANDLE; VkRenderPass gecis = VK_NULL_HANDLE; // ana pass ile UYUMLU (aynı formatlar) VkFramebuffer fb = VK_NULL_HANDLE; VkCommandPool havuz = VK_NULL_HANDLE; VkCommandBuffer kb = VK_NULL_HANDLE; VkFence cit = VK_NULL_HANDLE; ImDrawList* dl = nullptr; ImDrawData veri; }; // ---- Yaşam döngüsü ---- SeffafPencere& SeffafPencere::tekil() { static SeffafPencere t; return t; } void SeffafPencere::altyapiKur(VulkanBaglam& vb) { g_vb = &vb; } void SeffafPencere::altyapiKapat() { tekil().yik(); g_vb = nullptr; } bool SeffafPencere::destekleniyor() { return g_vb && g_vb->dxInterop() && !g_devreDisi; } bool SeffafPencere::gorunurMu() const { return ic_ && ic_->gorunur; } ImVec2 SeffafPencere::konum() const { return ic_ ? ic_->poz : ImVec2{}; } ImVec2 SeffafPencere::boyut() const { return ic_ ? ImVec2{(float)ic_->g, (float)ic_->y} : ImVec2{}; } void SeffafPencere::yik() { if (!ic_) return; Ic& c = *ic_; if (g_vb && !g_vb->aygitKayipMi()) { const VkDevice a = g_vb->aygit(); if (c.cit) vkWaitForFences(a, 1, &c.cit, VK_TRUE, 200'000'000ull); if (c.fb) vkDestroyFramebuffer(a, c.fb, nullptr); if (c.gecis) vkDestroyRenderPass(a, c.gecis, nullptr); if (c.renkGorunum) vkDestroyImageView(a, c.renkGorunum, nullptr); if (c.renk) vmaDestroyImage(g_vb->ayirici(), c.renk, c.renkT); if (c.derinlikGorunum) vkDestroyImageView(a, c.derinlikGorunum, nullptr); if (c.derinlik) vmaDestroyImage(g_vb->ayirici(), c.derinlik, c.derinlikT); if (c.paylasilanVk) vkDestroyImage(a, c.paylasilanVk, nullptr); if (c.paylasilanBellek) vkFreeMemory(a, c.paylasilanBellek, nullptr); if (c.cit) vkDestroyFence(a, c.cit, nullptr); if (c.havuz) vkDestroyCommandPool(a, c.havuz, nullptr); } if (g_solTik == &c.solTik) g_solTik = nullptr; if (g_sagTik == &c.sagTik) g_sagTik = nullptr; if (c.tutamac) CloseHandle(c.tutamac); birak(c.kilit); birak(c.paylasilan); birak(c.dcGorsel); birak(c.dcHedef); birak(c.dcAygit); birak(c.takas); birak(c.d3dBaglam); birak(c.d3dAygit); if (c.hwnd) DestroyWindow(c.hwnd); IM_DELETE(c.dl); delete ic_; ic_ = nullptr; } // ---- Kurulum ---- bool SeffafPencere::hazirla(int genislik, int yukseklik) { if (!destekleniyor()) return false; if (ic_ && ic_->g >= genislik && ic_->y >= yukseklik) return true; yik(); // Ana pass formatının DXGI karşılığı (kanal sırası kopyada korunmalı). const VkFormat anaFormat = g_vb->takasFormati(); DXGI_FORMAT dxgiFormat; VkFormat vkUnorm; if (anaFormat == VK_FORMAT_B8G8R8A8_SRGB || anaFormat == VK_FORMAT_B8G8R8A8_UNORM) { dxgiFormat = DXGI_FORMAT_B8G8R8A8_UNORM; vkUnorm = VK_FORMAT_B8G8R8A8_UNORM; } else if (anaFormat == VK_FORMAT_R8G8B8A8_SRGB || anaFormat == VK_FORMAT_R8G8B8A8_UNORM) { dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM; vkUnorm = VK_FORMAT_R8G8B8A8_UNORM; } else { return false; // beklenmedik swapchain formatı → cam panel fallback } ic_ = new Ic; Ic& c = *ic_; c.g = genislik; c.y = yukseklik; bool tamam = false; const VkDevice aygit = g_vb->aygit(); do { // 1) Pencere: redirection surface YOK (DComp görseli tek içerik → gerçek alfa). static bool sinifKayitli = false; if (!sinifKayitli) { WNDCLASSW wc{}; wc.lpfnWndProc = seffafWndProc; wc.hInstance = GetModuleHandleW(nullptr); wc.hCursor = LoadCursorW(nullptr, IDC_ARROW); wc.lpszClassName = L"FikoSeffafPencere"; RegisterClassW(&wc); sinifKayitli = true; } c.hwnd = CreateWindowExW( WS_EX_NOREDIRECTIONBITMAP | WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE, L"FikoSeffafPencere", L"", WS_POPUP, 0, 0, c.g, c.y, nullptr, nullptr, GetModuleHandleW(nullptr), nullptr); if (!c.hwnd) break; g_solTik = &c.solTik; g_sagTik = &c.sagTik; // 2) D3D11 + composition swapchain (premultiplied alfa) + DComp ağacı. const D3D_FEATURE_LEVEL seviyeler[] = {D3D_FEATURE_LEVEL_11_0}; if (FAILED(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, seviyeler, 1, D3D11_SDK_VERSION, &c.d3dAygit, nullptr, &c.d3dBaglam))) break; IDXGIDevice* dxgiAygit = nullptr; if (FAILED(c.d3dAygit->QueryInterface(IID_PPV_ARGS(&dxgiAygit)))) break; IDXGIAdapter* adaptor = nullptr; IDXGIFactory2* fabrika = nullptr; dxgiAygit->GetAdapter(&adaptor); if (adaptor) adaptor->GetParent(IID_PPV_ARGS(&fabrika)); birak(adaptor); if (!fabrika) { birak(dxgiAygit); break; } DXGI_SWAP_CHAIN_DESC1 td{}; td.Width = (UINT)c.g; td.Height = (UINT)c.y; td.Format = dxgiFormat; td.SampleDesc = {1, 0}; td.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; td.BufferCount = 2; td.Scaling = DXGI_SCALING_STRETCH; td.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; td.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; const HRESULT tr = fabrika->CreateSwapChainForComposition(c.d3dAygit, &td, nullptr, &c.takas); birak(fabrika); if (FAILED(tr)) { birak(dxgiAygit); break; } if (FAILED(DCompositionCreateDevice(dxgiAygit, IID_PPV_ARGS(&c.dcAygit)))) { birak(dxgiAygit); break; } birak(dxgiAygit); if (FAILED(c.dcAygit->CreateTargetForHwnd(c.hwnd, TRUE, &c.dcHedef))) break; if (FAILED(c.dcAygit->CreateVisual(&c.dcGorsel))) break; c.dcGorsel->SetContent(c.takas); c.dcHedef->SetRoot(c.dcGorsel); c.dcAygit->Commit(); // 3) Keyed-mutex'li paylaşımlı doku + NT tutamacı. D3D11_TEXTURE2D_DESC pd{}; pd.Width = (UINT)c.g; pd.Height = (UINT)c.y; pd.MipLevels = 1; pd.ArraySize = 1; pd.Format = dxgiFormat; pd.SampleDesc = {1, 0}; pd.Usage = D3D11_USAGE_DEFAULT; pd.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; pd.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX | D3D11_RESOURCE_MISC_SHARED_NTHANDLE; if (FAILED(c.d3dAygit->CreateTexture2D(&pd, nullptr, &c.paylasilan))) break; if (FAILED(c.paylasilan->QueryInterface(IID_PPV_ARGS(&c.kilit)))) break; IDXGIResource1* kaynak = nullptr; if (FAILED(c.paylasilan->QueryInterface(IID_PPV_ARGS(&kaynak)))) break; const HRESULT pr = kaynak->CreateSharedHandle( nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, &c.tutamac); birak(kaynak); if (FAILED(pr)) break; // 4) Paylaşımlı dokuyu Vulkan'a içe aktar (dedicated import). VkExternalMemoryImageCreateInfo dis{VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO}; dis.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT; VkImageCreateInfo gb{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; gb.pNext = &dis; gb.imageType = VK_IMAGE_TYPE_2D; gb.format = vkUnorm; gb.extent = {(u32)c.g, (u32)c.y, 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_TRANSFER_DST_BIT; if (vkCreateImage(aygit, &gb, nullptr, &c.paylasilanVk) != VK_SUCCESS) break; VkMemoryRequirements gereksinim; vkGetImageMemoryRequirements(aygit, c.paylasilanVk, &gereksinim); VkPhysicalDeviceMemoryProperties bellekOzellik; vkGetPhysicalDeviceMemoryProperties(g_vb->fizikselAygit(), &bellekOzellik); u32 tip = UINT32_MAX; for (u32 i = 0; i < bellekOzellik.memoryTypeCount; ++i) if ((gereksinim.memoryTypeBits & (1u << i)) && (bellekOzellik.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { tip = i; break; } if (tip == UINT32_MAX) break; VkMemoryDedicatedAllocateInfo adanmis{VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO}; adanmis.image = c.paylasilanVk; VkImportMemoryWin32HandleInfoKHR ice{VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR}; ice.pNext = &adanmis; ice.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT; ice.handle = c.tutamac; VkMemoryAllocateInfo tahsis{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; tahsis.pNext = &ice; tahsis.allocationSize = gereksinim.size; tahsis.memoryTypeIndex = tip; if (vkAllocateMemory(aygit, &tahsis, nullptr, &c.paylasilanBellek) != VK_SUCCESS) break; if (vkBindImageMemory(aygit, c.paylasilanVk, c.paylasilanBellek, 0) != VK_SUCCESS) break; // 5) Render hedefi (ana pass formatı) + derinlik + UYUMLU render pass + fb. VkImageCreateInfo rb{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; rb.imageType = VK_IMAGE_TYPE_2D; rb.format = anaFormat; rb.extent = {(u32)c.g, (u32)c.y, 1}; rb.mipLevels = 1; rb.arrayLayers = 1; rb.samples = VK_SAMPLE_COUNT_1_BIT; rb.tiling = VK_IMAGE_TILING_OPTIMAL; rb.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; VmaAllocationCreateInfo rt{}; rt.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; if (vmaCreateImage(g_vb->ayirici(), &rb, &rt, &c.renk, &c.renkT, nullptr) != VK_SUCCESS) break; VkImageViewCreateInfo rv{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; rv.image = c.renk; rv.viewType = VK_IMAGE_VIEW_TYPE_2D; rv.format = anaFormat; rv.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; if (vkCreateImageView(aygit, &rv, nullptr, &c.renkGorunum) != VK_SUCCESS) break; rb.format = VK_FORMAT_D32_SFLOAT; rb.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; if (vmaCreateImage(g_vb->ayirici(), &rb, &rt, &c.derinlik, &c.derinlikT, nullptr) != VK_SUCCESS) break; rv.image = c.derinlik; rv.format = VK_FORMAT_D32_SFLOAT; rv.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; if (vkCreateImageView(aygit, &rv, nullptr, &c.derinlikGorunum) != VK_SUCCESS) break; // Uyumluluk kuralı: format + örnek sayısı ana pass ile birebir → ImGui // backend'inin ANA pipeline'ı bu pass içinde geçerli (yeni pipeline gerekmez). VkAttachmentDescription ekler[2]{}; ekler[0].format = anaFormat; 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] = ekler[0]; ekler[1].format = VK_FORMAT_D32_SFLOAT; ekler[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; ekler[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkAttachmentReference renkRef{0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; VkAttachmentReference derinRef{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 = &derinRef; VkRenderPassCreateInfo rp{VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO}; rp.attachmentCount = 2; rp.pAttachments = ekler; rp.subpassCount = 1; rp.pSubpasses = &alt; if (vkCreateRenderPass(aygit, &rp, nullptr, &c.gecis) != VK_SUCCESS) break; const VkImageView gorunumler[2] = {c.renkGorunum, c.derinlikGorunum}; VkFramebufferCreateInfo fb{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO}; fb.renderPass = c.gecis; fb.attachmentCount = 2; fb.pAttachments = gorunumler; fb.width = (u32)c.g; fb.height = (u32)c.y; fb.layers = 1; if (vkCreateFramebuffer(aygit, &fb, nullptr, &c.fb) != VK_SUCCESS) break; // 6) Komut kaynakları. VkCommandPoolCreateInfo hb{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO}; hb.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; hb.queueFamilyIndex = g_vb->grafikAile(); if (vkCreateCommandPool(aygit, &hb, nullptr, &c.havuz) != VK_SUCCESS) break; VkCommandBufferAllocateInfo kba{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO}; kba.commandPool = c.havuz; kba.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; kba.commandBufferCount = 1; if (vkAllocateCommandBuffers(aygit, &kba, &c.kb) != VK_SUCCESS) break; VkFenceCreateInfo cb{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO}; cb.flags = VK_FENCE_CREATE_SIGNALED_BIT; if (vkCreateFence(aygit, &cb, nullptr, &c.cit) != VK_SUCCESS) break; tamam = true; } while (false); if (!tamam) { gunluk::bilgi("SeffafPencere kurulamadı — cam panel fallback kullanılacak"); yik(); return false; } return true; } // ---- Kare akışı ---- void SeffafPencere::goster(float ekranX, float ekranY) { if (!ic_) return; ic_->poz = {ekranX, ekranY}; SetWindowPos(ic_->hwnd, HWND_TOPMOST, (int)ekranX, (int)ekranY, ic_->g, ic_->y, SWP_NOACTIVATE | SWP_SHOWWINDOW); ic_->gorunur = true; } void SeffafPencere::gizle() { if (!ic_) return; ShowWindow(ic_->hwnd, SW_HIDE); ic_->gorunur = false; ic_->solTik = ic_->sagTik = false; } bool SeffafPencere::solTikAl() { if (!ic_ || !ic_->solTik) return false; ic_->solTik = false; return true; } bool SeffafPencere::sagTikAl() { if (!ic_ || !ic_->sagTik) return false; ic_->sagTik = false; return true; } ImDrawList* SeffafPencere::kareBasla() { if (!ic_) return nullptr; Ic& c = *ic_; if (!c.dl) c.dl = IM_NEW(ImDrawList)(ImGui::GetDrawListSharedData()); c.dl->_ResetForNewFrame(); c.dl->PushClipRect(c.poz, {c.poz.x + (float)c.g, c.poz.y + (float)c.y}, false); c.dl->PushTexture(ImGui::GetIO().Fonts->TexRef); return c.dl; } void SeffafPencere::kareBitir() { if (!ic_ || !g_vb || g_vb->aygitKayipMi()) return; Ic& c = *ic_; const VkDevice aygit = g_vb->aygit(); if (vkWaitForFences(aygit, 1, &c.cit, VK_TRUE, 200'000'000ull) != VK_SUCCESS) return; vkResetFences(aygit, 1, &c.cit); // Çizim verisi: DisplayPos = pencere sol-üst → ekran-uzayı koordinat korunur. c.veri = {}; c.veri.Valid = true; c.veri.CmdLists.resize(0); c.veri.CmdLists.push_back(c.dl); c.veri.CmdListsCount = 1; c.veri.TotalVtxCount = c.dl->VtxBuffer.Size; c.veri.TotalIdxCount = c.dl->IdxBuffer.Size; c.veri.DisplayPos = c.poz; c.veri.DisplaySize = {(float)c.g, (float)c.y}; c.veri.FramebufferScale = {1.0f, 1.0f}; c.veri.OwnerViewport = ImGui::GetMainViewport(); VkCommandBufferBeginInfo basla{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO}; basla.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; VK_KONTROL(vkBeginCommandBuffer(c.kb, &basla)); VkClearValue temizle[2]{}; temizle[0].color = {{0, 0, 0, 0}}; // tam şeffaf zemin temizle[1].depthStencil = {1.0f, 0}; VkRenderPassBeginInfo rp{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO}; rp.renderPass = c.gecis; rp.framebuffer = c.fb; rp.renderArea = {{0, 0}, {(u32)c.g, (u32)c.y}}; rp.clearValueCount = 2; rp.pClearValues = temizle; vkCmdBeginRenderPass(c.kb, &rp, VK_SUBPASS_CONTENTS_INLINE); ImGui_ImplVulkan_RenderDrawData(&c.veri, c.kb); // ana pipeline (uyumlu pass) vkCmdEndRenderPass(c.kb); // Paylaşımlı dokuya kopya (içerik her kare tamamen yazılır → UNDEFINED discard). VkImageMemoryBarrier b{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; b.srcQueueFamilyIndex = b.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; b.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; b.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; b.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; b.image = c.paylasilanVk; b.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; vkCmdPipelineBarrier(c.kb, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &b); VkImageCopy kopya{}; kopya.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; kopya.dstSubresource = kopya.srcSubresource; kopya.extent = {(u32)c.g, (u32)c.y, 1}; vkCmdCopyImage(c.kb, c.renk, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, c.paylasilanVk, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &kopya); b.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; b.newLayout = VK_IMAGE_LAYOUT_GENERAL; // D3D erişimi öncesi b.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; b.dstAccessMask = 0; vkCmdPipelineBarrier(c.kb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &b); VK_KONTROL(vkEndCommandBuffer(c.kb)); // Keyed mutex: Vulkan 0'ı alır → 1'i bırakır; D3D 1'i alır → 0'ı bırakır. // Eşleşme AYNI çağrı içinde tamamlanır (aşağıda AcquireSync CPU'yu bloklar) → // anahtar dizisi kareler arasında durum taşımaz. const u64 alKey = 0, birakKey = 1; const u32 zamanAsimMs = 1000; VkWin32KeyedMutexAcquireReleaseInfoKHR km{ VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHR}; km.acquireCount = 1; km.pAcquireSyncs = &c.paylasilanBellek; km.pAcquireKeys = &alKey; km.pAcquireTimeouts = &zamanAsimMs; km.releaseCount = 1; km.pReleaseSyncs = &c.paylasilanBellek; km.pReleaseKeys = &birakKey; VkSubmitInfo gonder{VK_STRUCTURE_TYPE_SUBMIT_INFO}; gonder.pNext = &km; gonder.commandBufferCount = 1; gonder.pCommandBuffers = &c.kb; VK_KONTROL(vkQueueSubmit(g_vb->grafikKuyruk(), 1, &gonder, c.cit)); // D3D: kilidi al (Vulkan bitene dek bloklar), arka tampona kopyala, sun. // DİKKAT: yalnız S_OK kabul edilir — WAIT_TIMEOUT (0x102) SUCCEEDED() sayılır // ama kilit alınmamıştır; sahiplenmeden ReleaseSync anahtar dizisini bozar ve // sonraki her Vulkan submit'i grafik kuyruğunu 1sn bloklar (uygulama donması). const HRESULT al = c.kilit->AcquireSync(1, 1000); if (al == S_OK) { ID3D11Texture2D* arka = nullptr; if (SUCCEEDED(c.takas->GetBuffer(0, IID_PPV_ARGS(&arka)))) { c.d3dBaglam->CopyResource(arka, c.paylasilan); birak(arka); } c.kilit->ReleaseSync(0); c.takas->Present(0, 0); } else { // Protokol artık güvenilmez: overlay'i oturum boyu kapat (cam panel devralır). gunluk::hata("SeffafPencere keyed-mutex zaman aşımı — overlay devre dışı"); g_devreDisi = true; gizle(); } } } // namespace fiko::ui