#include "vulkan_utils.h" #include #include #include #include #include #include #include #include #include #include #include "ThermionWin32.h" #include 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 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 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(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 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(&header), sizeof(header)); // Convert RGBA to BGR and write pixel data uint8_t* pixels = reinterpret_cast(data); std::vector 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(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 physicalDevices(deviceCount); bluevk::vkEnumeratePhysicalDevices(instance, &deviceCount, physicalDevices.data()); if (deviceCount == 0) { return VK_ERROR_INITIALIZATION_FAILED; } *physicalDevice = physicalDevices[0]; std::vector 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(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; }