776 lines
28 KiB
C++
776 lines
28 KiB
C++
#include "vulkan_utils.h"
|
|
|
|
#include <fstream>
|
|
|
|
#include <iostream>
|
|
#include <thread>
|
|
#include <chrono>
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <thread>
|
|
|
|
#include "ThermionWin32.h"
|
|
#include <Windows.h>
|
|
|
|
using namespace bluevk;
|
|
|
|
// Helper function to convert VkResult to string for error reporting
|
|
const char *VkResultToString(VkResult result)
|
|
{
|
|
switch (result)
|
|
{
|
|
case VK_SUCCESS:
|
|
return "VK_SUCCESS";
|
|
case VK_ERROR_OUT_OF_HOST_MEMORY:
|
|
return "VK_ERROR_OUT_OF_HOST_MEMORY";
|
|
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
|
|
return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
|
|
case VK_ERROR_INITIALIZATION_FAILED:
|
|
return "VK_ERROR_INITIALIZATION_FAILED";
|
|
case VK_ERROR_LAYER_NOT_PRESENT:
|
|
return "VK_ERROR_LAYER_NOT_PRESENT";
|
|
case VK_ERROR_EXTENSION_NOT_PRESENT:
|
|
return "VK_ERROR_EXTENSION_NOT_PRESENT";
|
|
default:
|
|
return "UNKNOWN_ERROR";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// bool checkD3D11VulkanInterop(VkPhysicalDevice physicalDevice, ID3D11Device *d3dDevice)
|
|
// {
|
|
// std::cout << "\n=== Checking D3D11-Vulkan Interop Support in QEMU ===" << std::endl;
|
|
|
|
// // Check Vulkan external memory capabilities
|
|
// VkPhysicalDeviceExternalImageFormatInfo externFormatInfo = {
|
|
// .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
|
|
// .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT};
|
|
|
|
// VkPhysicalDeviceImageFormatInfo2 formatInfo = {
|
|
// .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
|
|
// .pNext = &externFormatInfo,
|
|
// .format = VK_FORMAT_R8G8B8A8_UNORM,
|
|
// .type = VK_IMAGE_TYPE_2D,
|
|
// .tiling = VK_IMAGE_TILING_OPTIMAL,
|
|
// .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
|
// .flags = 0};
|
|
|
|
// VkExternalImageFormatProperties externFormatProps = {
|
|
// .sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES};
|
|
|
|
// VkImageFormatProperties2 formatProps = {
|
|
// .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2,
|
|
// .pNext = &externFormatProps};
|
|
|
|
// // Check device properties
|
|
// VkPhysicalDeviceProperties deviceProps;
|
|
// vkGetPhysicalDeviceProperties(physicalDevice, &deviceProps);
|
|
|
|
// std::cout << "Vulkan Device: " << deviceProps.deviceName << std::endl;
|
|
// std::cout << "Driver Version: " << deviceProps.driverVersion << std::endl;
|
|
// std::cout << "API Version: " << VK_VERSION_MAJOR(deviceProps.apiVersion) << "." << VK_VERSION_MINOR(deviceProps.apiVersion) << "." << VK_VERSION_PATCH(deviceProps.apiVersion) << std::endl;
|
|
|
|
// // Check D3D11 device capabilities
|
|
// D3D11_FEATURE_DATA_D3D11_OPTIONS3 featureData = {};
|
|
// HRESULT hr = d3dDevice->CheckFeatureSupport(
|
|
// D3D11_FEATURE_D3D11_OPTIONS3,
|
|
// &featureData,
|
|
// sizeof(featureData));
|
|
|
|
// std::cout << "\nChecking D3D11 Device:" << std::endl;
|
|
|
|
// // Get D3D11 device information
|
|
// IDXGIDevice *dxgiDevice = nullptr;
|
|
// hr = d3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void **)&dxgiDevice);
|
|
// if (SUCCEEDED(hr))
|
|
// {
|
|
// IDXGIAdapter *adapter = nullptr;
|
|
// hr = dxgiDevice->GetAdapter(&adapter);
|
|
// if (SUCCEEDED(hr))
|
|
// {
|
|
// DXGI_ADAPTER_DESC desc;
|
|
// adapter->GetDesc(&desc);
|
|
// std::wcout << L"D3D11 Adapter: " << desc.Description << std::endl;
|
|
// adapter->Release();
|
|
// }
|
|
// dxgiDevice->Release();
|
|
// }
|
|
|
|
// // Check for external memory support
|
|
// VkResult result = vkGetPhysicalDeviceImageFormatProperties2(
|
|
// physicalDevice,
|
|
// &formatInfo,
|
|
// &formatProps);
|
|
|
|
// std::cout << "\nInterop Support Details:" << std::endl;
|
|
|
|
// // Check external memory extension
|
|
// uint32_t extensionCount = 0;
|
|
// vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr);
|
|
// std::vector<VkExtensionProperties> extensions(extensionCount);
|
|
// vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, extensions.data());
|
|
|
|
// bool hasExternalMemoryExt = false;
|
|
// bool hasWin32Ext = false;
|
|
|
|
// for (const auto &ext : extensions)
|
|
// {
|
|
// if (strcmp(ext.extensionName, VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME) == 0)
|
|
// {
|
|
// hasExternalMemoryExt = true;
|
|
// }
|
|
// if (strcmp(ext.extensionName, VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME) == 0)
|
|
// {
|
|
// hasWin32Ext = true;
|
|
// }
|
|
// }
|
|
|
|
// std::cout << "External Memory Extension: " << (hasExternalMemoryExt ? "Yes" : "No") << std::endl;
|
|
// std::cout << "Win32 External Memory Extension: " << (hasWin32Ext ? "Yes" : "No") << std::endl;
|
|
// std::cout << "Format Properties Check: " << (result == VK_SUCCESS ? "Passed" : "Failed") << std::endl;
|
|
|
|
// // Check memory properties
|
|
// VkPhysicalDeviceMemoryProperties memProps;
|
|
// vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps);
|
|
|
|
// std::cout << "\nMemory Types Available:" << std::endl;
|
|
// for (uint32_t i = 0; i < memProps.memoryTypeCount; i++)
|
|
// {
|
|
// VkMemoryPropertyFlags flags = memProps.memoryTypes[i].propertyFlags;
|
|
// std::cout << "Type " << i << ": ";
|
|
// if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
|
|
// std::cout << "Device Local ";
|
|
// if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
|
|
// std::cout << "Host Visible ";
|
|
// if (flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
|
|
// std::cout << "Host Coherent ";
|
|
// if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
|
|
// std::cout << "Host Cached ";
|
|
// std::cout << std::endl;
|
|
// }
|
|
|
|
// // Check if all required features are available
|
|
// bool supportsInterop =
|
|
// hasExternalMemoryExt &&
|
|
// hasWin32Ext &&
|
|
// result == VK_SUCCESS;
|
|
|
|
// std::cout << "\nFinal Result: " << (supportsInterop ? "Interop Supported" : "Interop Not Supported") << std::endl;
|
|
// std::cout << "================================================" << std::endl;
|
|
|
|
// return supportsInterop;
|
|
// }
|
|
|
|
// Helper function to find suitable memory type
|
|
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties, VkPhysicalDevice physicalDevice) {
|
|
VkPhysicalDeviceMemoryProperties memProperties;
|
|
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
|
|
|
|
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
|
|
if ((typeFilter & (1 << i)) &&
|
|
(memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
throw std::runtime_error("Failed to find suitable memory type");
|
|
}
|
|
|
|
|
|
// Modified memory type selection function with more detailed requirements checking
|
|
uint32_t findOptimalMemoryType(VkPhysicalDevice physicalDevice,
|
|
uint32_t typeFilter,
|
|
VkMemoryPropertyFlags requiredProperties,
|
|
VkMemoryPropertyFlags preferredProperties) {
|
|
VkPhysicalDeviceMemoryProperties memProperties;
|
|
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
|
|
|
|
// First try to find memory type with all preferred properties
|
|
if (preferredProperties != 0) {
|
|
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
|
|
if ((typeFilter & (1 << i)) &&
|
|
(memProperties.memoryTypes[i].propertyFlags & (requiredProperties | preferredProperties)) ==
|
|
(requiredProperties | preferredProperties)) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fall back to just required properties
|
|
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
|
|
if ((typeFilter & (1 << i)) &&
|
|
(memProperties.memoryTypes[i].propertyFlags & requiredProperties) == requiredProperties) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return UINT32_MAX;
|
|
}
|
|
|
|
// Consolidated function for creating Vulkan instance
|
|
VkResult createVulkanInstance(VkInstance *instance)
|
|
{
|
|
std::vector<const char *> instanceExtensions = {
|
|
VK_KHR_SURFACE_EXTENSION_NAME,
|
|
VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
|
|
VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
|
|
// VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
|
|
// VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME
|
|
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME};
|
|
|
|
VkApplicationInfo appInfo = {};
|
|
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
|
appInfo.pApplicationName = "Vulkan-D3D11 Interop";
|
|
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
|
|
appInfo.pEngineName = "No Engine";
|
|
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
|
|
appInfo.apiVersion = VK_API_VERSION_1_1;
|
|
|
|
VkInstanceCreateInfo createInfo = {};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
|
createInfo.pApplicationInfo = &appInfo;
|
|
createInfo.enabledExtensionCount = static_cast<uint32_t>(instanceExtensions.size());
|
|
createInfo.ppEnabledExtensionNames = instanceExtensions.data();
|
|
|
|
return vkCreateInstance(&createInfo, nullptr, instance);
|
|
}
|
|
|
|
// Helper function to find a queue family that supports graphics operations
|
|
uint32_t findGraphicsQueueFamily(VkPhysicalDevice physicalDevice) {
|
|
uint32_t queueFamilyCount = 0;
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);
|
|
|
|
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data());
|
|
|
|
// Find a queue family that supports graphics operations
|
|
for (uint32_t i = 0; i < queueFamilyCount; i++) {
|
|
if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
throw std::runtime_error("Failed to find graphics queue family");
|
|
}
|
|
|
|
|
|
CommandResources createCommandResources(VkDevice device, VkPhysicalDevice physicalDevice) {
|
|
CommandResources resources{};
|
|
|
|
// 1. Find a suitable queue family
|
|
resources.queueFamilyIndex = findGraphicsQueueFamily(physicalDevice);
|
|
|
|
// 2. Get the queue handle
|
|
vkGetDeviceQueue(device,
|
|
resources.queueFamilyIndex,
|
|
0, // First queue in family
|
|
&resources.queue);
|
|
|
|
// 3. Create command pool
|
|
VkCommandPoolCreateInfo poolInfo{};
|
|
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; // Allow resetting individual command buffers
|
|
poolInfo.queueFamilyIndex = resources.queueFamilyIndex;
|
|
|
|
if (vkCreateCommandPool(device, &poolInfo, nullptr, &resources.commandPool) != VK_SUCCESS) {
|
|
throw std::runtime_error("Failed to create command pool");
|
|
}
|
|
|
|
return resources;
|
|
}
|
|
|
|
void readVkImageToBitmap(
|
|
VkPhysicalDevice physicalDevice,
|
|
VkDevice device,
|
|
VkCommandPool commandPool,
|
|
VkQueue queue,
|
|
VkImage sourceImage,
|
|
uint32_t width,
|
|
uint32_t height,
|
|
const char* outputPath
|
|
) {
|
|
// Create staging buffer for reading pixel data
|
|
VkBufferCreateInfo bufferInfo{};
|
|
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
bufferInfo.size = width * height * 4; // Assuming RGBA8 format
|
|
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
VkBuffer stagingBuffer;
|
|
VkResult result = vkCreateBuffer(device, &bufferInfo, nullptr, &stagingBuffer);
|
|
if (result != VK_SUCCESS) {
|
|
throw std::runtime_error("Failed to create staging buffer");
|
|
}
|
|
|
|
// Get memory requirements and properties
|
|
VkMemoryRequirements memRequirements;
|
|
vkGetBufferMemoryRequirements(device, stagingBuffer, &memRequirements);
|
|
|
|
// Get physical device memory properties
|
|
VkPhysicalDeviceMemoryProperties memProperties;
|
|
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
|
|
|
|
// Find suitable memory type index
|
|
uint32_t memoryTypeIndex = -1;
|
|
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
|
|
if ((memRequirements.memoryTypeBits & (1 << i)) &&
|
|
(memProperties.memoryTypes[i].propertyFlags &
|
|
(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
|
|
(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
|
|
memoryTypeIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (memoryTypeIndex == -1) {
|
|
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
|
throw std::runtime_error("Failed to find suitable memory type");
|
|
}
|
|
|
|
// Allocate memory
|
|
VkMemoryAllocateInfo allocInfo{};
|
|
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
allocInfo.allocationSize = memRequirements.size;
|
|
allocInfo.memoryTypeIndex = memoryTypeIndex;
|
|
|
|
VkDeviceMemory stagingMemory;
|
|
result = vkAllocateMemory(device, &allocInfo, nullptr, &stagingMemory);
|
|
if (result != VK_SUCCESS) {
|
|
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
|
throw std::runtime_error("Failed to allocate staging memory");
|
|
}
|
|
|
|
// Bind memory to buffer
|
|
result = vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0);
|
|
if (result != VK_SUCCESS) {
|
|
vkFreeMemory(device, stagingMemory, nullptr);
|
|
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
|
throw std::runtime_error("Failed to bind buffer memory");
|
|
}
|
|
|
|
// Create command buffer
|
|
VkCommandBufferAllocateInfo cmdBufInfo{};
|
|
cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
cmdBufInfo.commandPool = commandPool;
|
|
cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
cmdBufInfo.commandBufferCount = 1;
|
|
|
|
VkCommandBuffer cmdBuffer;
|
|
result = vkAllocateCommandBuffers(device, &cmdBufInfo, &cmdBuffer);
|
|
if (result != VK_SUCCESS) {
|
|
vkFreeMemory(device, stagingMemory, nullptr);
|
|
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
|
throw std::runtime_error("Failed to allocate command buffer");
|
|
}
|
|
|
|
// Begin command buffer
|
|
VkCommandBufferBeginInfo beginInfo{};
|
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
|
|
result = vkBeginCommandBuffer(cmdBuffer, &beginInfo);
|
|
if (result != VK_SUCCESS) {
|
|
vkFreeCommandBuffers(device, commandPool, 1, &cmdBuffer);
|
|
vkFreeMemory(device, stagingMemory, nullptr);
|
|
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
|
throw std::runtime_error("Failed to begin command buffer");
|
|
}
|
|
|
|
// Transition image layout for transfer
|
|
VkImageMemoryBarrier imageBarrier{};
|
|
imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Adjust based on current layout
|
|
imageBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
|
imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
imageBarrier.image = sourceImage;
|
|
imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
imageBarrier.subresourceRange.baseMipLevel = 0;
|
|
imageBarrier.subresourceRange.levelCount = 1;
|
|
imageBarrier.subresourceRange.baseArrayLayer = 0;
|
|
imageBarrier.subresourceRange.layerCount = 1;
|
|
imageBarrier.srcAccessMask = 0;
|
|
imageBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
|
|
|
vkCmdPipelineBarrier(
|
|
cmdBuffer,
|
|
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
0,
|
|
0, nullptr,
|
|
0, nullptr,
|
|
1, &imageBarrier
|
|
);
|
|
|
|
// Copy image to buffer
|
|
VkBufferImageCopy region{};
|
|
region.bufferOffset = 0;
|
|
region.bufferRowLength = 0;
|
|
region.bufferImageHeight = 0;
|
|
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
region.imageSubresource.mipLevel = 0;
|
|
region.imageSubresource.baseArrayLayer = 0;
|
|
region.imageSubresource.layerCount = 1;
|
|
region.imageOffset = { 0, 0, 0 };
|
|
region.imageExtent = { width, height, 1 };
|
|
|
|
vkCmdCopyImageToBuffer(
|
|
cmdBuffer,
|
|
sourceImage,
|
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
stagingBuffer,
|
|
1,
|
|
®ion
|
|
);
|
|
|
|
// Add memory barrier to ensure the transfer is complete before reading
|
|
VkMemoryBarrier memBarrier{};
|
|
memBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
|
|
memBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
memBarrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
|
|
|
|
vkCmdPipelineBarrier(
|
|
cmdBuffer,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
VK_PIPELINE_STAGE_HOST_BIT,
|
|
0,
|
|
1, &memBarrier,
|
|
0, nullptr,
|
|
0, nullptr
|
|
);
|
|
|
|
// End command buffer
|
|
result = vkEndCommandBuffer(cmdBuffer);
|
|
if (result != VK_SUCCESS) {
|
|
vkFreeCommandBuffers(device, commandPool, 1, &cmdBuffer);
|
|
vkFreeMemory(device, stagingMemory, nullptr);
|
|
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
|
throw std::runtime_error("Failed to end command buffer");
|
|
}
|
|
|
|
// Submit command buffer
|
|
VkSubmitInfo submitInfo{};
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submitInfo.commandBufferCount = 1;
|
|
submitInfo.pCommandBuffers = &cmdBuffer;
|
|
|
|
// Create fence to ensure command buffer has finished executing
|
|
VkFenceCreateInfo fenceInfo{};
|
|
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
|
|
VkFence fence;
|
|
result = vkCreateFence(device, &fenceInfo, nullptr, &fence);
|
|
if (result != VK_SUCCESS) {
|
|
vkFreeCommandBuffers(device, commandPool, 1, &cmdBuffer);
|
|
vkFreeMemory(device, stagingMemory, nullptr);
|
|
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
|
throw std::runtime_error("Failed to create fence");
|
|
}
|
|
|
|
// Submit with fence
|
|
result = vkQueueSubmit(queue, 1, &submitInfo, fence);
|
|
if (result != VK_SUCCESS) {
|
|
vkDestroyFence(device, fence, nullptr);
|
|
vkFreeCommandBuffers(device, commandPool, 1, &cmdBuffer);
|
|
vkFreeMemory(device, stagingMemory, nullptr);
|
|
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
|
throw std::runtime_error("Failed to submit queue");
|
|
}
|
|
|
|
// Wait for the command buffer to complete execution
|
|
result = vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX);
|
|
if (result != VK_SUCCESS) {
|
|
vkDestroyFence(device, fence, nullptr);
|
|
vkFreeCommandBuffers(device, commandPool, 1, &cmdBuffer);
|
|
vkFreeMemory(device, stagingMemory, nullptr);
|
|
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
|
throw std::runtime_error("Failed to wait for fence");
|
|
}
|
|
|
|
// Now safe to map memory and read data
|
|
void* data;
|
|
result = vkMapMemory(device, stagingMemory, 0, bufferInfo.size, 0, &data);
|
|
if (result != VK_SUCCESS) {
|
|
vkDestroyFence(device, fence, nullptr);
|
|
vkFreeCommandBuffers(device, commandPool, 1, &cmdBuffer);
|
|
vkFreeMemory(device, stagingMemory, nullptr);
|
|
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
|
throw std::runtime_error("Failed to map memory");
|
|
}
|
|
|
|
// Create bitmap header
|
|
BMPHeader header{};
|
|
header.signature = 0x4D42; // "BM"
|
|
header.fileSize = sizeof(BMPHeader) + width * height * 3; // 3 bytes per pixel (BGR)
|
|
header.dataOffset = sizeof(BMPHeader);
|
|
header.headerSize = 40;
|
|
header.width = width;
|
|
header.height = height;
|
|
header.planes = 1;
|
|
header.bitsPerPixel = 24;
|
|
header.compression = 0;
|
|
header.imageSize = width * height * 3;
|
|
|
|
|
|
//// Write to file
|
|
std::ofstream file(outputPath, std::ios::binary);
|
|
if (!file.is_open()) {
|
|
vkUnmapMemory(device, stagingMemory);
|
|
vkDestroyFence(device, fence, nullptr);
|
|
vkFreeCommandBuffers(device, commandPool, 1, &cmdBuffer);
|
|
vkFreeMemory(device, stagingMemory, nullptr);
|
|
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
|
throw std::runtime_error("Failed to open output file");
|
|
}
|
|
|
|
file.write(reinterpret_cast<char*>(&header), sizeof(header));
|
|
|
|
// Convert RGBA to BGR and write pixel data
|
|
uint8_t* pixels = reinterpret_cast<uint8_t*>(data);
|
|
std::vector<uint8_t> bgrData(width * height * 3);
|
|
|
|
for (uint32_t y = 0; y < height; y++) {
|
|
for (uint32_t x = 0; x < width; x++) {
|
|
uint32_t srcIdx = (y * width + x) * 4; // RGBA has 4 components
|
|
uint32_t dstIdx = ((height - 1 - y) * width + x) * 3; // Flip vertically
|
|
|
|
// RGBA to BGR conversion
|
|
bgrData[dstIdx + 0] = pixels[srcIdx + 0];
|
|
bgrData[dstIdx + 1] = pixels[srcIdx + 1];
|
|
bgrData[dstIdx + 2] = pixels[srcIdx + 2];
|
|
}
|
|
}
|
|
|
|
file.write(reinterpret_cast<char*>(bgrData.data()), bgrData.size());
|
|
file.close();
|
|
|
|
// Cleanup
|
|
vkUnmapMemory(device, stagingMemory);
|
|
vkDestroyFence(device, fence, nullptr);
|
|
vkFreeCommandBuffers(device, commandPool, 1, &cmdBuffer);
|
|
vkFreeMemory(device, stagingMemory, nullptr);
|
|
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
|
}
|
|
|
|
// Consolidated function for creating logical device
|
|
VkResult createLogicalDevice(VkInstance instance, VkPhysicalDevice *physicalDevice, VkDevice *device)
|
|
{
|
|
uint32_t deviceCount = 0;
|
|
bluevk::vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
|
|
std::vector<VkPhysicalDevice> physicalDevices(deviceCount);
|
|
bluevk::vkEnumeratePhysicalDevices(instance, &deviceCount, physicalDevices.data());
|
|
|
|
if (deviceCount == 0)
|
|
{
|
|
return VK_ERROR_INITIALIZATION_FAILED;
|
|
}
|
|
|
|
*physicalDevice = physicalDevices[0];
|
|
|
|
std::vector<const char *> deviceExtensions = {
|
|
VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
|
|
VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME};
|
|
|
|
float queuePriority = 1.0f;
|
|
VkDeviceQueueCreateInfo queueCreateInfo = {};
|
|
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
queueCreateInfo.queueFamilyIndex = 0;
|
|
queueCreateInfo.queueCount = 1;
|
|
queueCreateInfo.pQueuePriorities = &queuePriority;
|
|
|
|
VkDeviceCreateInfo deviceCreateInfo = {};
|
|
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
|
deviceCreateInfo.queueCreateInfoCount = 1;
|
|
deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
|
|
deviceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
|
|
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data();
|
|
|
|
return vkCreateDevice(*physicalDevice, &deviceCreateInfo, nullptr, device);
|
|
}
|
|
|
|
// Example usage with device creation
|
|
void createDeviceWithGraphicsQueue(VkPhysicalDevice physicalDevice, uint32_t& queueFamilyIndex, VkDevice* device) {
|
|
// Find queue family index
|
|
queueFamilyIndex = findGraphicsQueueFamily(physicalDevice);
|
|
|
|
// Specify queue creation
|
|
float queuePriority = 1.0f;
|
|
VkDeviceQueueCreateInfo queueCreateInfo{};
|
|
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
queueCreateInfo.queueFamilyIndex = queueFamilyIndex;
|
|
queueCreateInfo.queueCount = 1;
|
|
queueCreateInfo.pQueuePriorities = &queuePriority;
|
|
|
|
// Specify device features
|
|
VkPhysicalDeviceFeatures deviceFeatures{};
|
|
|
|
// Create logical device
|
|
VkDeviceCreateInfo createInfo{};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
|
createInfo.pQueueCreateInfos = &queueCreateInfo;
|
|
createInfo.queueCreateInfoCount = 1;
|
|
createInfo.pEnabledFeatures = &deviceFeatures;
|
|
|
|
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device) != VK_SUCCESS) {
|
|
throw std::runtime_error("Failed to create logical device");
|
|
}
|
|
}
|
|
|
|
void fillImageWithColor(
|
|
VkDevice device,
|
|
VkCommandPool commandPool,
|
|
VkQueue queue,
|
|
VkImage image,
|
|
VkFormat format,
|
|
VkImageLayout currentLayout,
|
|
VkExtent3D extent,
|
|
float r, float g, float b, float a
|
|
) {
|
|
// Create command buffer
|
|
VkCommandBufferAllocateInfo allocInfo = {};
|
|
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
allocInfo.commandPool = commandPool;
|
|
allocInfo.commandBufferCount = 1;
|
|
|
|
VkCommandBuffer commandBuffer;
|
|
vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer);
|
|
|
|
// Begin command buffer
|
|
VkCommandBufferBeginInfo beginInfo = {};
|
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
vkBeginCommandBuffer(commandBuffer, &beginInfo);
|
|
|
|
// Transition image layout to TRANSFER_DST_OPTIMAL if needed
|
|
if (currentLayout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
|
|
VkImageMemoryBarrier barrier = {};
|
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
barrier.oldLayout = currentLayout;
|
|
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrier.image = image;
|
|
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
barrier.subresourceRange.baseMipLevel = 0;
|
|
barrier.subresourceRange.levelCount = 1;
|
|
barrier.subresourceRange.baseArrayLayer = 0;
|
|
barrier.subresourceRange.layerCount = 1;
|
|
barrier.srcAccessMask = 0;
|
|
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
|
|
vkCmdPipelineBarrier(
|
|
commandBuffer,
|
|
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
0,
|
|
0, nullptr,
|
|
0, nullptr,
|
|
1, &barrier
|
|
);
|
|
}
|
|
|
|
// Clear the image
|
|
VkClearColorValue clearColor = {{r, g, b, a}};
|
|
VkImageSubresourceRange range = {};
|
|
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
range.baseMipLevel = 0;
|
|
range.levelCount = 1;
|
|
range.baseArrayLayer = 0;
|
|
range.layerCount = 1;
|
|
|
|
vkCmdClearColorImage(
|
|
commandBuffer,
|
|
image,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
&clearColor,
|
|
1,
|
|
&range
|
|
);
|
|
|
|
// Transition back to original layout if needed
|
|
if (currentLayout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
|
|
VkImageMemoryBarrier barrier = {};
|
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
barrier.newLayout = currentLayout;
|
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrier.image = image;
|
|
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
barrier.subresourceRange.baseMipLevel = 0;
|
|
barrier.subresourceRange.levelCount = 1;
|
|
barrier.subresourceRange.baseArrayLayer = 0;
|
|
barrier.subresourceRange.layerCount = 1;
|
|
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
barrier.dstAccessMask = 0;
|
|
|
|
vkCmdPipelineBarrier(
|
|
commandBuffer,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
|
0,
|
|
0, nullptr,
|
|
0, nullptr,
|
|
1, &barrier
|
|
);
|
|
}
|
|
|
|
// End and submit command buffer
|
|
vkEndCommandBuffer(commandBuffer);
|
|
|
|
VkSubmitInfo submitInfo = {};
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submitInfo.commandBufferCount = 1;
|
|
submitInfo.pCommandBuffers = &commandBuffer;
|
|
|
|
vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
|
|
vkQueueWaitIdle(queue);
|
|
|
|
// Cleanup
|
|
vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
|
|
}
|
|
|
|
bool SavePixelsAsBMP(uint8_t* pixels, uint32_t width, uint32_t height, int rowPitch, const char* filename) {
|
|
// Create and fill header
|
|
BMPHeader header = {};
|
|
header.signature = 0x4D42; // 'BM'
|
|
header.fileSize = sizeof(BMPHeader) + width * height * 4;
|
|
header.dataOffset = sizeof(BMPHeader);
|
|
header.headerSize = 40;
|
|
header.width = width;
|
|
header.height = height;
|
|
header.planes = 1;
|
|
header.bitsPerPixel = 32;
|
|
header.compression = 0;
|
|
header.imageSize = width * height * 4;
|
|
header.xPixelsPerMeter = 2835; // 72 DPI
|
|
header.yPixelsPerMeter = 2835; // 72 DPI
|
|
|
|
// Write to file
|
|
FILE* file = nullptr;
|
|
fopen_s(&file, filename, "wb");
|
|
|
|
if (!file) {
|
|
std::cout << "Couldn't open file for pixels" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
fwrite(&header, sizeof(header), 1, file);
|
|
|
|
// Write pixel data (need to flip rows as BMP is bottom-up)
|
|
for (int y = height - 1; y >= 0; y--) {
|
|
uint8_t* rowData = pixels + y * rowPitch;
|
|
fwrite(rowData, width * 4, 1, file);
|
|
}
|
|
|
|
fclose(file);
|
|
return true;
|
|
|
|
}
|