mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-10-30 21:35:06 +00:00
168 lines
8.7 KiB
C++
168 lines
8.7 KiB
C++
#include "vulkan_bridge.h"
|
|
#include "desktop/foreign_surface.h"
|
|
#include "desktop/shared_pixels.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
|
|
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<VkImage> images;
|
|
std::shared_ptr<ForeignSurface> 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<ForeignSwapchain>();
|
|
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 (;type<memory.memoryTypeCount;++type)
|
|
if ((requirements.memoryTypeBits & (1U<<type)) && (memory.memoryTypes[type].propertyFlags & flags)==flags) break;
|
|
if (type==memory.memoryTypeCount) return VK_ERROR_MEMORY_MAP_FAILED;
|
|
VkMemoryAllocateInfo allocate{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,nullptr,requirements.size,type};
|
|
result=vkAllocateMemory(target->device,&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<VkResult> VulkanBridge::present_foreign(uint64_t queue_token,const VkPresentInfoKHR &info)
|
|
{
|
|
struct Frame { std::shared_ptr<ForeignSwapchain> target; uint32_t index; };
|
|
std::vector<Frame> frames;
|
|
for (uint32_t i=0;i<info.swapchainCount;++i)
|
|
for (const auto &[token,target]:foreign_swapchains)
|
|
if (target->swapchain==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<VkCommandBuffer> 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<VkPipelineStageFlags> 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<const uint8_t *>(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;i<bytes;i+=4) {
|
|
destination[i]=source[i+2]; destination[i+1]=source[i+1];
|
|
destination[i+2]=source[i]; destination[i+3]=source[i+3];
|
|
}
|
|
else std::memcpy(destination,source,bytes);
|
|
target.surface->publish_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;
|
|
}
|
|
|
|
}
|