#include "vulkan_bridge.h" #include "desktop/foreign_surface.h" #include "desktop/shared_pixels.h" #include #include namespace raven { struct VulkanBridge::ForeignSwapchain { uint64_t device_token=0,window=0; VkDevice device=VK_NULL_HANDLE; VkSwapchainKHR swapchain=VK_NULL_HANDLE; VkFormat format=VK_FORMAT_UNDEFINED; uint32_t width=0,height=0,family=UINT32_MAX; VkBuffer buffer=VK_NULL_HANDLE; VkDeviceMemory memory=VK_NULL_HANDLE; void *mapped=nullptr; VkCommandPool pool=VK_NULL_HANDLE; VkCommandBuffer command=VK_NULL_HANDLE; std::vector images; std::shared_ptr surface; ~ForeignSwapchain() { if (pool) vkDestroyCommandPool(device,pool,nullptr); if (mapped) vkUnmapMemory(device,memory); if (buffer) vkDestroyBuffer(device,buffer,nullptr); if (memory) vkFreeMemory(device,memory,nullptr); } }; VkResult VulkanBridge::prepare_foreign_swapchain(uint64_t token,uint64_t device_token,VkSwapchainKHR swapchain, const VkSwapchainCreateInfoKHR &info,uint64_t window) { if (info.imageFormat!=VK_FORMAT_B8G8R8A8_UNORM && info.imageFormat!=VK_FORMAT_B8G8R8A8_SRGB && info.imageFormat!=VK_FORMAT_R8G8B8A8_UNORM && info.imageFormat!=VK_FORMAT_R8G8B8A8_SRGB) return VK_ERROR_FORMAT_NOT_SUPPORTED; const auto size=foreign_window_size(window); if (!size) return VK_ERROR_SURFACE_LOST_KHR; if (size->first!=info.imageExtent.width || size->second!=info.imageExtent.height) return VK_ERROR_OUT_OF_DATE_KHR; const uint64_t bytes=uint64_t(info.imageExtent.width)*info.imageExtent.height*4; if (!bytes || bytes>256ULL*1024*1024 || info.imageArrayLayers!=1) return VK_ERROR_OUT_OF_DEVICE_MEMORY; auto target=std::make_shared(); target->device_token=device_token; target->window=window; target->device=devices.at(device_token).handle; target->swapchain=swapchain; target->format=info.imageFormat; target->width=info.imageExtent.width; target->height=info.imageExtent.height; target->surface=ForeignSurface::open_for_drawing(window,target->width,target->height); if (!target->surface) return VK_ERROR_OUT_OF_HOST_MEMORY; uint32_t count=0; VkResult result=vkGetSwapchainImagesKHR(target->device,swapchain,&count,nullptr); if (result!=VK_SUCCESS) return result; target->images.resize(count); result=vkGetSwapchainImagesKHR(target->device,swapchain,&count,target->images.data()); if (result!=VK_SUCCESS) return result; VkBufferCreateInfo buffer{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; buffer.size=bytes; buffer.usage=VK_BUFFER_USAGE_TRANSFER_DST_BIT; buffer.sharingMode=VK_SHARING_MODE_EXCLUSIVE; result=vkCreateBuffer(target->device,&buffer,nullptr,&target->buffer); if (result!=VK_SUCCESS) return result; VkMemoryRequirements requirements{}; vkGetBufferMemoryRequirements(target->device,target->buffer,&requirements); VkPhysicalDeviceMemoryProperties memory{}; vkGetPhysicalDeviceMemoryProperties(physical.at(devices.at(device_token).physical).handle,&memory); uint32_t type=0; const auto flags=VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; for (;typedevice,&allocate,nullptr,&target->memory); if (result!=VK_SUCCESS) return result; result=vkBindBufferMemory(target->device,target->buffer,target->memory,0); if (result!=VK_SUCCESS) return result; result=vkMapMemory(target->device,target->memory,0,VK_WHOLE_SIZE,0,&target->mapped); if (result!=VK_SUCCESS) return result; foreign_swapchains.emplace(token,std::move(target)); return VK_SUCCESS; } void VulkanBridge::release_foreign_swapchains(uint64_t device) { std::erase_if(foreign_swapchains,[&](const auto &item) { return item.second->device_token==device; }); } std::optional VulkanBridge::present_foreign(uint64_t queue_token,const VkPresentInfoKHR &info) { struct Frame { std::shared_ptr target; uint32_t index; }; std::vector frames; for (uint32_t i=0;iswapchain==info.pSwapchains[i]) { if (info.pImageIndices[i]>=target->images.size()) return VK_ERROR_OUT_OF_DATE_KHR; frames.push_back({target,info.pImageIndices[i]}); break; } if (frames.empty()) return std::nullopt; const auto queue=queues.at(queue_token); // The copy consumes the present's waits once, including waits shared with local swapchains in the same present. // The host present follows the completed copies without waiting on those binary semaphores a second time. bool presented=false; const auto status=detached([&]() -> VkResult { std::vector commands; for (const auto &frame:frames) { auto &target=*frame.target; if (target.device_token!=queue.device) return VK_ERROR_DEVICE_LOST; if (!target.pool || target.family!=queue.family) { if (target.pool) vkDestroyCommandPool(target.device,target.pool,nullptr); target.pool=VK_NULL_HANDLE; VkCommandPoolCreateInfo pool{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,nullptr,VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,queue.family}; auto result=vkCreateCommandPool(target.device,&pool,nullptr,&target.pool); if (result!=VK_SUCCESS) return result; VkCommandBufferAllocateInfo allocate{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,nullptr,target.pool,VK_COMMAND_BUFFER_LEVEL_PRIMARY,1}; result=vkAllocateCommandBuffers(target.device,&allocate,&target.command); if (result!=VK_SUCCESS) return result; target.family=queue.family; } auto result=vkResetCommandBuffer(target.command,0); if (result!=VK_SUCCESS) return result; VkCommandBufferBeginInfo begin{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,nullptr,VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; result=vkBeginCommandBuffer(target.command,&begin); if (result!=VK_SUCCESS) return result; VkImageMemoryBarrier image{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,nullptr,VK_ACCESS_MEMORY_WRITE_BIT,VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,VK_QUEUE_FAMILY_IGNORED,VK_QUEUE_FAMILY_IGNORED, target.images[frame.index],{VK_IMAGE_ASPECT_COLOR_BIT,0,1,0,1}}; vkCmdPipelineBarrier(target.command,VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,VK_PIPELINE_STAGE_TRANSFER_BIT,0,0,nullptr,0,nullptr,1,&image); VkBufferImageCopy copy{}; copy.imageSubresource={VK_IMAGE_ASPECT_COLOR_BIT,0,0,1}; copy.imageExtent={target.width,target.height,1}; vkCmdCopyImageToBuffer(target.command,target.images[frame.index],VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,target.buffer,1,©); image.srcAccessMask=VK_ACCESS_TRANSFER_READ_BIT; image.dstAccessMask=0; image.oldLayout=VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; image.newLayout=VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; vkCmdPipelineBarrier(target.command,VK_PIPELINE_STAGE_TRANSFER_BIT,VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,0,0,nullptr,0,nullptr,1,&image); VkMemoryBarrier host{VK_STRUCTURE_TYPE_MEMORY_BARRIER,nullptr,VK_ACCESS_TRANSFER_WRITE_BIT,VK_ACCESS_HOST_READ_BIT}; vkCmdPipelineBarrier(target.command,VK_PIPELINE_STAGE_TRANSFER_BIT,VK_PIPELINE_STAGE_HOST_BIT,0,1,&host,0,nullptr,0,nullptr); result=vkEndCommandBuffer(target.command); if (result!=VK_SUCCESS) return result; commands.push_back(target.command); } std::vector stages(info.waitSemaphoreCount,VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); VkSubmitInfo submit{VK_STRUCTURE_TYPE_SUBMIT_INFO,nullptr,info.waitSemaphoreCount,info.pWaitSemaphores,stages.data(), uint32_t(commands.size()),commands.data(),0,nullptr}; auto result=vkQueueSubmit(queue.handle,1,&submit,VK_NULL_HANDLE); if (result!=VK_SUCCESS) return result; result=vkQueueWaitIdle(queue.handle); if (result!=VK_SUCCESS) return result; for (const auto &frame:frames) { auto &target=*frame.target; const auto *source=static_cast(target.mapped); auto *destination=target.surface->pixels->data(); const size_t bytes=size_t(target.width)*target.height*4; if (target.format==VK_FORMAT_R8G8B8A8_UNORM || target.format==VK_FORMAT_R8G8B8A8_SRGB) for (size_t i=0;ipublish_frame(); } VkPresentInfoKHR present=info; present.waitSemaphoreCount=0; present.pWaitSemaphores=nullptr; presented=true; return vkQueuePresentKHR(queue.handle,&present); }); if (!presented && info.pResults) std::fill_n(info.pResults,info.swapchainCount,status); return status; } }