From b59cadc061d2c9d3a11748b346c3c4045928422a Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 4 Nov 2024 23:50:52 +0800 Subject: [PATCH] add first pass vulkan implementation --- .../windows/rendering/vulkan/CMakeLists.txt | 45 ++ .../windows/rendering/vulkan/d3d_texture.cpp | 272 ++++++++++ .../windows/rendering/vulkan/d3d_texture.h | 51 ++ .../windows/rendering/vulkan/egl_context.h | 29 ++ .../windows/rendering/vulkan/main.cpp | 484 ++++++++++++++++++ .../rendering/vulkan/vulkan_context.cpp | 144 ++++++ 6 files changed, 1025 insertions(+) create mode 100644 thermion_flutter/thermion_flutter/windows/rendering/vulkan/CMakeLists.txt create mode 100644 thermion_flutter/thermion_flutter/windows/rendering/vulkan/d3d_texture.cpp create mode 100644 thermion_flutter/thermion_flutter/windows/rendering/vulkan/d3d_texture.h create mode 100644 thermion_flutter/thermion_flutter/windows/rendering/vulkan/egl_context.h create mode 100644 thermion_flutter/thermion_flutter/windows/rendering/vulkan/main.cpp create mode 100644 thermion_flutter/thermion_flutter/windows/rendering/vulkan/vulkan_context.cpp diff --git a/thermion_flutter/thermion_flutter/windows/rendering/vulkan/CMakeLists.txt b/thermion_flutter/thermion_flutter/windows/rendering/vulkan/CMakeLists.txt new file mode 100644 index 00000000..7ac0cfaf --- /dev/null +++ b/thermion_flutter/thermion_flutter/windows/rendering/vulkan/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.14) +set(PROJECT_NAME "thermion_egl") +project(${PROJECT_NAME} LANGUAGES C CXX) + +cmake_policy(VERSION 3.14...3.25) + +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +set(BUILD_SHARED_LIBS TRUE) +set(CMAKE_ENABLE_EXPORTS TRUE) + +add_definitions(-DVK_STATIC_DISPATCH) + +add_library(${PROJECT_NAME} SHARED + "d3d_texture.cpp" +) + +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20) +include_directories(${PROJECT_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/../../include" +) + +target_link_libraries(${PROJECT_NAME} PRIVATE + Shlwapi + dxgi + d3d11 +) + +# Test executable +add_executable(${PROJECT_NAME}_test + "main.cpp" +) + +target_include_directories(${PROJECT_NAME}_test PRIVATE + E:\\VulkanSDK\\1.3.296.0\\Include +) +target_link_directories(${PROJECT_NAME}_test PRIVATE + E:\\VulkanSDK\\1.3.296.0\\Lib +) +target_link_libraries(${PROJECT_NAME}_test PRIVATE + ${PROJECT_NAME} + vulkan-1 +) + +# Make sure the test depends on the library +add_dependencies(${PROJECT_NAME}_test ${PROJECT_NAME}) diff --git a/thermion_flutter/thermion_flutter/windows/rendering/vulkan/d3d_texture.cpp b/thermion_flutter/thermion_flutter/windows/rendering/vulkan/d3d_texture.cpp new file mode 100644 index 00000000..28579184 --- /dev/null +++ b/thermion_flutter/thermion_flutter/windows/rendering/vulkan/d3d_texture.cpp @@ -0,0 +1,272 @@ +#include "d3d_texture.h" + +#include +#include +#include +#include + +namespace thermion::windows::d3d { + +void D3DTexture::Flush() { + // glFlush(); // Ensure GL commands are completed + _D3D11DeviceContext->Flush(); +} + +HANDLE D3DTexture::GetTextureHandle() { + return _d3dTexture2DHandle; +} + +D3DTexture::~D3DTexture() { + _d3dTexture2D->Release(); +} + +bool IsNTHandleSupported(ID3D11Device* device) { + Microsoft::WRL::ComPtr device5; + return SUCCEEDED(device->QueryInterface(IID_PPV_ARGS(&device5))); +} + +D3DTexture::D3DTexture( + uint32_t width, uint32_t height, + std::function onResizeRequested + ) + : _width(width), _height(height), _onResizeRequested(onResizeRequested) { + + IDXGIAdapter *adapter_ = nullptr; + + auto feature_levels = { + D3D_FEATURE_LEVEL_12_0, + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + }; + + IDXGIFactory1 *dxgi = nullptr; + HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void **)&dxgi); + if (FAILED(hr)) { + std::cout << "Failed to create DXGI 1.1 factory" << std::endl; + return; + } + dxgi->EnumAdapters(0, &adapter_); + dxgi->Release(); + if (!adapter_) { + std::cout << "Failed to locate default D3D adapter" << std::endl; + return; + } + + Microsoft::WRL::ComPtr factory2; +if (SUCCEEDED(dxgi->QueryInterface(IID_PPV_ARGS(&factory2)))) { + std::cout << "DXGI 1.2 or higher supported" << std::endl; +} + + DXGI_ADAPTER_DESC adapter_desc_; + adapter_->GetDesc(&adapter_desc_); + std::wcout << L"D3D adapter description: " << adapter_desc_.Description + << std::endl; + + hr = ::D3D11CreateDevice( + adapter_, D3D_DRIVER_TYPE_UNKNOWN, 0, D3D11_CREATE_DEVICE_BGRA_SUPPORT, feature_levels.begin(), + static_cast(feature_levels.size()), D3D11_SDK_VERSION, + &_D3D11Device, 0, &_D3D11DeviceContext); + + if (FAILED(hr)) { + std::cout << "Failed to create D3D device" << std::endl; + return; + } + + Microsoft::WRL::ComPtr dxgi_device = nullptr; + auto dxgi_device_success = _D3D11Device->QueryInterface( + __uuidof(IDXGIDevice), (void **)&dxgi_device); + if (SUCCEEDED(dxgi_device_success) && dxgi_device != nullptr) { + dxgi_device->SetGPUThreadPriority(5); // Must be in interval [-7, 7]. + } + + if(IsNTHandleSupported(_D3D11Device)) { + std::cout << "NT HANDLE SUPPORT" << std::endl; + } else { + std::cout << "NT HANDLE NOT SUPPORT" << std::endl; + } + auto level = _D3D11Device->GetFeatureLevel(); + std::cout << "Direct3D Feature Level: " + << (((unsigned)level) >> 12) << "_" + << ((((unsigned)level) >> 8) & 0xf) << std::endl; + + // Create texture + auto d3d11_texture2D_desc = D3D11_TEXTURE2D_DESC{0}; + d3d11_texture2D_desc.Width = width; + d3d11_texture2D_desc.Height = height; + d3d11_texture2D_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + d3d11_texture2D_desc.MipLevels = 1; + d3d11_texture2D_desc.ArraySize = 1; + d3d11_texture2D_desc.SampleDesc.Count = 1; + d3d11_texture2D_desc.SampleDesc.Quality = 0; + d3d11_texture2D_desc.Usage = D3D11_USAGE_DEFAULT; + d3d11_texture2D_desc.BindFlags = + D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; + d3d11_texture2D_desc.CPUAccessFlags = 0; + d3d11_texture2D_desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; + + hr = _D3D11Device->CreateTexture2D(&d3d11_texture2D_desc, nullptr, &_d3dTexture2D); + if FAILED (hr) { + std::cout << "Failed to create D3D texture (" << hr << ")" << std::endl; + return; + } + auto resource = Microsoft::WRL::ComPtr{}; + hr = _d3dTexture2D.As(&resource); + + if FAILED (hr) { + std::cout << "Failed to create D3D texture" << std::endl; + return; + ; + } + hr = resource->GetSharedHandle(&_d3dTexture2DHandle); + if FAILED (hr) { + std::cout << "Failed to get shared handle to external D3D texture" << std::endl; + return; + ; + } + _d3dTexture2D->AddRef(); + + std::cout << "Created external D3D texture " << width << "x" << height << std::endl; + + // Create render target view of the texture + ID3D11RenderTargetView* rtv = nullptr; + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {}; + rtvDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + rtvDesc.Texture2D.MipSlice = 0; + + hr = _D3D11Device->CreateRenderTargetView(_d3dTexture2D.Get(), &rtvDesc, &rtv); + if (FAILED(hr)) { + std::cout << "Failed to create render target view" << std::endl; + return; + } + + // Clear the texture to blue + float blueColor[4] = { 0.0f, 0.0f, 1.0f, 1.0f }; // RGBA + _D3D11DeviceContext->ClearRenderTargetView(rtv, blueColor); + _D3D11DeviceContext->Flush(); + +} + +void D3DTexture::FillBlueAndSaveToBMP(const char* filename) { + // Create render target view of the texture + ID3D11RenderTargetView* rtv = nullptr; + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {}; + rtvDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + rtvDesc.Texture2D.MipSlice = 0; + + HRESULT hr = _D3D11Device->CreateRenderTargetView(_d3dTexture2D.Get(), &rtvDesc, &rtv); + if (FAILED(hr)) { + std::cout << "Failed to create render target view" << std::endl; + return; + } + + // Clear the texture to blue + float blueColor[4] = { 0.0f, 0.0f, 1.0f, 1.0f }; // RGBA + _D3D11DeviceContext->ClearRenderTargetView(rtv, blueColor); + + // Create staging texture for CPU read access + D3D11_TEXTURE2D_DESC stagingDesc = {}; + _d3dTexture2D->GetDesc(&stagingDesc); + stagingDesc.Usage = D3D11_USAGE_STAGING; + stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + stagingDesc.BindFlags = 0; + stagingDesc.MiscFlags = 0; + + ID3D11Texture2D* stagingTexture = nullptr; + hr = _D3D11Device->CreateTexture2D(&stagingDesc, nullptr, &stagingTexture); + if (FAILED(hr)) { + rtv->Release(); + std::cout << "Failed to create staging texture" << std::endl; + return; + } + + // Copy to staging texture + _D3D11DeviceContext->CopyResource(stagingTexture, _d3dTexture2D.Get()); + + // Save to BMP + bool success = SaveTextureAsBMP(stagingTexture, filename); + + // Cleanup + stagingTexture->Release(); + rtv->Release(); + + if (success) { + std::cout << "Successfully saved texture to " << filename << std::endl; + } +} + +bool D3DTexture::SaveTextureAsBMP(ID3D11Texture2D* texture, const char* filename) { + D3D11_TEXTURE2D_DESC desc; + texture->GetDesc(&desc); + + // Map texture to get pixel data + D3D11_MAPPED_SUBRESOURCE mappedResource; + HRESULT hr = _D3D11DeviceContext->Map(texture, 0, D3D11_MAP_READ, 0, &mappedResource); + if (FAILED(hr)) { + std::cout << "Failed to map texture" << std::endl; + return false; + } + + // BMP file header + #pragma pack(push, 1) + struct BMPHeader { + uint16_t signature; + uint32_t fileSize; + uint32_t reserved; + uint32_t dataOffset; + uint32_t headerSize; + int32_t width; + int32_t height; + uint16_t planes; + uint16_t bitsPerPixel; + uint32_t compression; + uint32_t imageSize; + int32_t xPixelsPerMeter; + int32_t yPixelsPerMeter; + uint32_t totalColors; + uint32_t importantColors; + }; + #pragma pack(pop) + + // Create and fill header + BMPHeader header = {}; + header.signature = 0x4D42; // 'BM' + header.fileSize = sizeof(BMPHeader) + desc.Width * desc.Height * 4; + header.dataOffset = sizeof(BMPHeader); + header.headerSize = 40; + header.width = desc.Width; + header.height = desc.Height; + header.planes = 1; + header.bitsPerPixel = 32; + header.compression = 0; + header.imageSize = desc.Width * desc.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) { + _D3D11DeviceContext->Unmap(texture, 0); + return false; + } + + fwrite(&header, sizeof(header), 1, file); + + // Write pixel data (need to flip rows as BMP is bottom-up) + uint8_t* srcData = reinterpret_cast(mappedResource.pData); + for (int y = desc.Height - 1; y >= 0; y--) { + uint8_t* rowData = srcData + y * mappedResource.RowPitch; + fwrite(rowData, desc.Width * 4, 1, file); + } + + fclose(file); + _D3D11DeviceContext->Unmap(texture, 0); + return true; +} + +} // namespace thermion_flutter \ No newline at end of file diff --git a/thermion_flutter/thermion_flutter/windows/rendering/vulkan/d3d_texture.h b/thermion_flutter/thermion_flutter/windows/rendering/vulkan/d3d_texture.h new file mode 100644 index 00000000..3989dca0 --- /dev/null +++ b/thermion_flutter/thermion_flutter/windows/rendering/vulkan/d3d_texture.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +#include +#include +#include // Add this line +#include +#include +#include + +typedef uint32_t GLuint; + +namespace thermion::windows::d3d { + +class D3DTexture { + public: + D3DTexture( + uint32_t width, + uint32_t height, + std::function onResizeRequested + ); + ~D3DTexture(); + + void Flush(); + HANDLE GetTextureHandle(); + + GLuint glTextureId = 0; + + void FillBlueAndSaveToBMP(const char* filename); + bool SaveTextureAsBMP(ID3D11Texture2D* texture, const char* filename); + // Device + ID3D11Device* _D3D11Device = nullptr; + + private: + bool _error = false; + uint32_t _width = 0; + uint32_t _height = 0; + bool logged = false; + std::function _onResizeRequested; + + + ID3D11DeviceContext* _D3D11DeviceContext = nullptr; + // Texture objects/shared handles + Microsoft::WRL::ComPtr _d3dTexture2D; + HANDLE _d3dTexture2DHandle = nullptr; + +}; + +} diff --git a/thermion_flutter/thermion_flutter/windows/rendering/vulkan/egl_context.h b/thermion_flutter/thermion_flutter/windows/rendering/vulkan/egl_context.h new file mode 100644 index 00000000..0e5d39cf --- /dev/null +++ b/thermion_flutter/thermion_flutter/windows/rendering/vulkan/egl_context.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#include "egl_texture.h" + +namespace thermion::windows::egl { + +class ThermionEGLContext { +public: + ThermionEGLContext(); + void* GetSharedContext(); + EGLTexture * CreateRenderingSurface(uint32_t width, uint32_t height, uint32_t left, uint32_t top); + void DestroyRenderingSurface(); + void ResizeRenderingSurface(uint32_t width, uint32_t height, uint32_t left, uint32_t top); + + EGLTexture * GetActiveTexture(); + +private: + void* _context = nullptr; + EGLConfig _eglConfig = NULL; + EGLDisplay _eglDisplay = NULL; + ID3D11Device* _D3D11Device = nullptr; + ID3D11DeviceContext* _D3D11DeviceContext = nullptr; + std::unique_ptr _active = nullptr; +}; + +} diff --git a/thermion_flutter/thermion_flutter/windows/rendering/vulkan/main.cpp b/thermion_flutter/thermion_flutter/windows/rendering/vulkan/main.cpp new file mode 100644 index 00000000..2f4847ab --- /dev/null +++ b/thermion_flutter/thermion_flutter/windows/rendering/vulkan/main.cpp @@ -0,0 +1,484 @@ +#include "d3d_texture.h" +#include +#include +#include +#include +#include +#include +#include + +#include "d3d_texture.h" + +#include +#include +#include +#include + +// 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"; + } +} + +// Modified memory type selection function with more detailed requirements checking +uint32_t findOptimalMemoryType(VkPhysicalDevice physicalDevice, + uint32_t typeFilter, + VkMemoryPropertyFlags requiredProperties, + VkMemoryPropertyFlags preferredProperties = 0) { + 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_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); +} + +// Consolidated function for creating logical device +VkResult createLogicalDevice(VkInstance instance, VkPhysicalDevice *physicalDevice, VkDevice *device) +{ + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + std::vector physicalDevices(deviceCount); + 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); +} + +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; +} + +int main() +{ + VkInstance instance = VK_NULL_HANDLE; + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + VkDevice device = VK_NULL_HANDLE; + VkImage image = VK_NULL_HANDLE; + HANDLE sharedHandle = nullptr; + + uint32_t height = 100; + uint32_t width = 100; + + std::cout << "[Step 1] Initializing D3D texture..." << std::endl; + thermion::windows::d3d::D3DTexture texture(width, height, [=](size_t width, size_t height) {}); + + sharedHandle = texture.GetTextureHandle(); + if (!sharedHandle) + { + std::cout << "[ERROR] Failed to get shared texture handle!" << std::endl; + return 1; + } + + texture.FillBlueAndSaveToBMP("output.bmp"); + std::cout << "[Info] Filled texture with blue and saved to output.bmp" << std::endl; + + // Create Vulkan instance + VkResult result = createVulkanInstance(&instance); + if (result != VK_SUCCESS) + { + std::cout << "[ERROR] Failed to create Vulkan instance! Error: " << VkResultToString(result) << std::endl; + return 1; + } + + // Create logical device + result = createLogicalDevice(instance, &physicalDevice, &device); + if (result != VK_SUCCESS) + { + std::cout << "[ERROR] Failed to create logical device! Error: " << VkResultToString(result) << std::endl; + vkDestroyInstance(instance, nullptr); + return 1; + } + + // In main(), after creating both D3D11 and Vulkan devices: + texture.FillBlueAndSaveToBMP("output.bmp"); + if (!checkD3D11VulkanInterop(physicalDevice, texture._D3D11Device)) + { + std::cout << "D3D11-Vulkan interop is not supported in this QEMU environment" << std::endl; + // Consider falling back to a different approach + return 1; + } + + VkPhysicalDeviceExternalImageFormatInfo externFormatInfo = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, + .pNext = nullptr, + .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, // Changed to LINEAR for VM compatibility + .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, // Simplified usage flags + .flags = 0}; + + VkExternalImageFormatProperties externFormatProps = { + .sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES}; + + VkImageFormatProperties2 formatProps = { + .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, + .pNext = &externFormatProps}; + + // Query supported features + result = vkGetPhysicalDeviceImageFormatProperties2( + physicalDevice, + &formatInfo, + &formatProps); + + if (result != VK_SUCCESS) + { + std::cout << "VM environment may not support required external memory features" << std::endl; + return 1; + } + + std::cout << "VM environment supports required external memory features" << std::endl; + + // Create image with external memory support + VkExternalMemoryImageCreateInfo extImageInfo = { + .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT}; + + VkImageCreateInfo imageInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = &extImageInfo, + .flags = 0, + .imageType = VK_IMAGE_TYPE_2D, + .format = VK_FORMAT_R8G8B8A8_UNORM, + .extent = {width, height, 1}, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, // Changed to LINEAR for VM + .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, // Simplified usage + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED}; + + result = vkCreateImage(device, &imageInfo, nullptr, &image); + + if (result != VK_SUCCESS) + { + std::cout << "Failed to create iamge " << std::endl; + } + else + { + std::cout << "Successfully created image " << std::endl; + } + + VkMemoryDedicatedRequirements MemoryDedicatedRequirements{ + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, + .pNext = nullptr}; + VkMemoryRequirements2 MemoryRequirements2{ + .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, + .pNext = &MemoryDedicatedRequirements}; + const VkImageMemoryRequirementsInfo2 ImageMemoryRequirementsInfo2{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, + .pNext = nullptr, + .image = image}; + // WARN: Memory access violation unless validation instance layer is enabled, otherwise success but... + vkGetImageMemoryRequirements2(device, &ImageMemoryRequirementsInfo2, &MemoryRequirements2); + // ... if we happen to be here, MemoryRequirements2 is empty + VkMemoryRequirements &MemoryRequirements = MemoryRequirements2.memoryRequirements; + + const VkMemoryDedicatedAllocateInfo MemoryDedicatedAllocateInfo{ + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, + .pNext = nullptr, + .image = image, + .buffer = VK_NULL_HANDLE}; + const VkImportMemoryWin32HandleInfoKHR ImportMemoryWin32HandleInfo{ + .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR, + .pNext = &MemoryDedicatedAllocateInfo, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT, + .handle = sharedHandle, + .name = nullptr}; + VkDeviceMemory ImageMemory = VK_NULL_HANDLE; + + // Find suitable memory type + uint32_t memoryTypeIndex = findOptimalMemoryType( + physicalDevice, + MemoryRequirements.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT // You might need to adjust these flags + ); + + VkMemoryAllocateInfo allocInfo{ + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = &ImportMemoryWin32HandleInfo, + .allocationSize = MemoryRequirements.size, + .memoryTypeIndex = memoryTypeIndex // Assuming 'properties' is your memory type index + }; + + VkResult allocResult = vkAllocateMemory(device, &allocInfo, nullptr, &ImageMemory); + if (allocResult != VK_SUCCESS || ImageMemory == VK_NULL_HANDLE) + { + std::cout << "IMAGE MEMORY ALLOCATION FAILED:" << std::endl; + std::cout << " Allocation size: " << MemoryRequirements.size << " bytes" << std::endl; + std::cout << " Memory type index: " << allocInfo.memoryTypeIndex << std::endl; + std::cout << " Error code: " << allocResult << std::endl; + + // Get more detailed error message based on VkResult + const char *errorMsg; + switch (allocResult) + { + case VK_ERROR_OUT_OF_HOST_MEMORY: + errorMsg = "VK_ERROR_OUT_OF_HOST_MEMORY: Out of host memory"; + break; + case VK_ERROR_OUT_OF_DEVICE_MEMORY: + errorMsg = "VK_ERROR_OUT_OF_DEVICE_MEMORY: Out of device memory"; + break; + case VK_ERROR_INVALID_EXTERNAL_HANDLE: + errorMsg = "VK_ERROR_INVALID_EXTERNAL_HANDLE: The external handle is invalid"; + break; + case VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS: + errorMsg = "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS: The requested address is not available"; + break; + default: + errorMsg = "Unknown error"; + } + std::cout << " Error message: " << errorMsg << std::endl; + + // Print shared handle info + std::cout << " Shared handle value: " << sharedHandle << std::endl; + + // Print memory requirements + std::cout << " Memory requirements:" << std::endl; + std::cout << " Size: " << MemoryRequirements.size << std::endl; + std::cout << " Alignment: " << MemoryRequirements.alignment << std::endl; + std::cout << " Memory type bits: 0x" << std::hex << MemoryRequirements.memoryTypeBits << std::dec << std::endl; + + return 1; + } + + // memAllocator->Allocate(MemoryRequirements, &ImageMemory, properties, &ImportMemoryWin32HandleInfo); + // if(ImageMemory == VK_NULL_HANDLE) { + // std::cout << "IMAGE MEMORY ALLOCATION FAILED" << std::endl; + // return; + // } + + const VkBindImageMemoryInfo bindImageMemoryInfo{ + .sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO, + .pNext = nullptr, + .image = image, + .memory = ImageMemory, + .memoryOffset = 0}; + + result = vkBindImageMemory2(device, 1, &bindImageMemoryInfo); + + if (result != VK_SUCCESS) + { + std::cout << "bindimagememory2 failed" << std::endl; + } + std::cout << "FIISHED" << std::endl; + + // Cleanup + std::cout << "\n[Step 6] Cleaning up resources..." << std::endl; + vkDestroyImage(device, image, nullptr); + // vkFreeMemory(device, memory, nullptr); + vkDestroyDevice(device, nullptr); + vkDestroyInstance(instance, nullptr); + std::cout << "[Complete] All resources cleaned up successfully" << std::endl; + + return 0; +} diff --git a/thermion_flutter/thermion_flutter/windows/rendering/vulkan/vulkan_context.cpp b/thermion_flutter/thermion_flutter/windows/rendering/vulkan/vulkan_context.cpp new file mode 100644 index 00000000..89e4355b --- /dev/null +++ b/thermion_flutter/thermion_flutter/windows/rendering/vulkan/vulkan_context.cpp @@ -0,0 +1,144 @@ +#include +#include +#include + +#include "egl_context.h" + +namespace thermion::windows::vulkan { + +ThermionEGLContext::ThermionEGLContext() { + + + + // ******************* + // * * + // * * + // * EGL starts here * + // * * + // * * + // * * + // ******************* + EGLBoolean bindAPI = eglBindAPI(EGL_OPENGL_ES_API); + if (!bindAPI) { + std::cout << "eglBindAPI EGL_OPENGL_ES_API failed" << std::endl; + return; + } + + _eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (_eglDisplay == EGL_NO_DISPLAY) { + std::cout << "eglBindAPI EGL_OPENGL_ES_API failed" << std::endl; + return; + } + + + EGLint major, minor; + EGLBoolean initialized = false; + + EGLDeviceEXT eglDevice; + EGLint numDevices; + + if (auto *getPlatformDisplay = + reinterpret_cast( + eglGetProcAddress("eglGetPlatformDisplayEXT"))) { + + EGLint kD3D11DisplayAttributes[] = { + EGL_PLATFORM_ANGLE_TYPE_ANGLE, + EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, + EGL_TRUE, + EGL_NONE, + }; + _eglDisplay = getPlatformDisplay( + EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, kD3D11DisplayAttributes); + initialized = eglInitialize(_eglDisplay, &major, &minor); + } + + std::cout << "Got major " << major << " and minor " << minor << std::endl; + + if (!initialized) { + std::cout << "eglInitialize failed" << std::endl; + return; + } + + // glext::importGLESExtensionsEntryPoints(); + + EGLint configsCount; + + EGLint configAttribs[] = {EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, EGL_DEPTH_SIZE, 24, + EGL_STENCIL_SIZE, 8, EGL_ALPHA_SIZE, 8, + EGL_NONE}; + + EGLint contextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE, + EGL_NONE, // reserved for EGL_CONTEXT_OPENGL_NO_ERROR_KHR below + EGL_NONE}; + + // find an opaque config + if (!eglChooseConfig(_eglDisplay, configAttribs, &_eglConfig, 1, + &configsCount)) { + std::cout << "Failed to find EGL config" << std::endl; + return; + } + + auto ctx = eglCreateContext(_eglDisplay, _eglConfig, EGL_NO_CONTEXT,contextAttribs); + _context = (void*)ctx; + + if (_context == EGL_NO_CONTEXT) { + return; + } +} + +EGLTexture* ThermionEGLContext::CreateRenderingSurface( + uint32_t width, uint32_t height, + uint32_t left, uint32_t top + ) { + + // glext::importGLESExtensionsEntryPoints(); + + if(left != 0 || top != 0) { + std::cout << "ERROR Rendering with EGL uses a Texture render target/Flutter widget and does not need a window offset." << std::endl; + return nullptr; + } + + //if (_active && _active.get()) { + // // result->Error("ERROR", + // // "Texture already exists. You must call destroyTexture before " + // // "attempting to create a new one."); + // return nullptr; + //} + + _active = std::make_unique( + width, height, + _D3D11Device, _D3D11DeviceContext, _eglConfig, _eglDisplay, _context, + [=](size_t width, size_t height) { + std::cout << "RESIZE" << std::endl; + std::vector list; + list.push_back((int64_t)width); + list.push_back((int64_t)height); + // auto val = std::make_unique(list); + // this->_channel->InvokeMethod("resize", std::move(val), nullptr); + }); + + return _active.get(); +} + +void* ThermionEGLContext::GetSharedContext() { + return (void*)_context; +} + +void ThermionEGLContext::ResizeRenderingSurface(uint32_t width, uint32_t height, uint32_t left, uint32_t top) { + +} + +void ThermionEGLContext::DestroyRenderingSurface() { + +} + +EGLTexture *ThermionEGLContext::GetActiveTexture() { + return _active.get(); +} + + +} +