successfully creating D3D texture with D3D11_RESOURCE_MISC_SHARED_NTHANDLE;
successfully allocating with VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT working copying vulkan texture successfully passing D3D texture back to Flutter chore: Dart/Windows sample project: remove unnnecessary InvalidateRect from update() chore: Dart/Windows sample project: add generated bindings successfully blitting from Vulkan swapchain to D3D texture working Vulkan texture integration with Flutter refactor to allow disposal of resources in destructors handle destroyTexture correctly correctly implement surface resizing/destruction move Windows engine to Vulkan backend and flush after creating swapchain add vulkan + vkshaders to Windows libs update materials with Vulkan move Vulkan implementation to thermion_Dart remove extras folder thermion_flutter plugin updates update build hook to copy .lib file to output directory and use -vulkan lib zip file thermion_flutter cleanup reinstate stereoscopic on Windows add dxgi and d3d11.lib to windows header pragma update cli_windows sample project copy filament/vulkan headers to output directory. This was originally added to facilitate linking on Windows (where thermion_flutter_plugin.cpp needs the Vulkan-related headers), but this doesn't actually solve the problem because there's no way that I've found to get the directory structure correct in the Dart native_assets build directory unless you explicitly address each inidivual file. The current approach is therefore to just keep a permanent copy of the headers in the thermion_filament directory (meaning these will need to be updated manually if the Filament version changes). However, I decided to keep the changes to build.dart because it doesn't have much negative impact and may be helpful in future. disable stereoscopic on Windows and disable handle use after free checks use filament headers for thermion_flutter throw Exception for MSAA on Windows (note that passing msaa:true for setAntiAliasing doesn't actually set MSAA on other platforms, but at least it won't cause the engine to crash) change header include path for Windows/Vulkan change header include path for Windows/Vulkan add filament/vulkan headers for flutter (Windows) ensure destroyTexture platform methods accept an integer rather than a list handle Android/Windows swapchain creation separately
This commit is contained in:
@@ -127,8 +127,7 @@ namespace thermion
|
||||
FilamentViewer::FilamentViewer(const void *sharedContext, const ResourceLoaderWrapperImpl *const resourceLoader, void *const platform, const char *uberArchivePath)
|
||||
: _resourceLoaderWrapper(resourceLoader)
|
||||
{
|
||||
Log("Creating engine wiht sharedContext %d", sharedContext);
|
||||
|
||||
|
||||
ASSERT_POSTCONDITION(_resourceLoaderWrapper != nullptr, "Resource loader must be non-null");
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
@@ -142,11 +141,12 @@ namespace thermion
|
||||
#elif defined(_WIN32)
|
||||
Engine::Config config;
|
||||
config.stereoscopicEyeCount = 1;
|
||||
_engine = Engine::create(Engine::Backend::OPENGL, (backend::Platform *)platform, (void *)sharedContext, &config);
|
||||
config.disableHandleUseAfterFreeCheck = true;
|
||||
_engine = Engine::create(Engine::Backend::VULKAN, (backend::Platform *)platform, (void *)sharedContext, &config);
|
||||
#else
|
||||
_engine = Engine::create(Engine::Backend::OPENGL, (backend::Platform *)platform, (void *)sharedContext, nullptr);
|
||||
#endif
|
||||
Log("Engine created");
|
||||
#endif
|
||||
|
||||
_engine->setAutomaticInstancingEnabled(true);
|
||||
|
||||
_renderer = _engine->createRenderer();
|
||||
@@ -685,6 +685,7 @@ namespace thermion
|
||||
SwapChain *swapChain;
|
||||
swapChain = _engine->createSwapChain(width, height, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE | filament::SwapChain::CONFIG_HAS_STENCIL_BUFFER);
|
||||
_swapChains.push_back(swapChain);
|
||||
_engine->flushAndWait();
|
||||
return swapChain;
|
||||
}
|
||||
|
||||
@@ -767,7 +768,7 @@ namespace thermion
|
||||
view->setAmbientOcclusionOptions({.enabled = false});
|
||||
view->setDynamicResolutionOptions({.enabled = false});
|
||||
#if defined(_WIN32)
|
||||
view->setStereoscopicOptions({.enabled = true});
|
||||
view->setStereoscopicOptions({.enabled = false});
|
||||
#endif
|
||||
|
||||
// bloom can be a bit glitchy (some Intel iGPUs won't render when postprocessing is enabled and bloom is disabled,
|
||||
|
||||
67
thermion_dart/native/src/windows/vulkan/CMakeLists.txt
Normal file
67
thermion_dart/native/src/windows/vulkan/CMakeLists.txt
Normal file
@@ -0,0 +1,67 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
set(PROJECT_NAME "thermion_vulkan")
|
||||
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(-DWIN32_LEAN_AND_MEAN)
|
||||
|
||||
add_compile_options(/FS)
|
||||
# Add Windows-specific compile definitions
|
||||
add_definitions(-DWIN32)
|
||||
add_definitions(-D_WINDOWS)
|
||||
|
||||
# Ensure UNICODE is defined
|
||||
add_definitions(-DUNICODE)
|
||||
add_definitions(-D_UNICODE)
|
||||
|
||||
add_library(${PROJECT_NAME} SHARED
|
||||
"d3d_context.cpp"
|
||||
"vulkan_context.cpp"
|
||||
"utils.cpp"
|
||||
"d3d_texture.cpp"
|
||||
"vulkan_texture.cpp"
|
||||
)
|
||||
|
||||
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20)
|
||||
include_directories(${PROJECT_NAME} INTERFACE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../../include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/"
|
||||
)
|
||||
target_link_directories(${PROJECT_NAME} PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../../../.dart_tool/thermion_dart/lib/v1.51.2/windows/debug
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
Shlwapi
|
||||
dxgi
|
||||
d3d11
|
||||
bluevk
|
||||
vulkan-1
|
||||
)
|
||||
|
||||
#add_executable(${PROJECT_NAME}_test
|
||||
# "main.cpp"
|
||||
#)
|
||||
|
||||
#target_compile_features(${PROJECT_NAME}_test PUBLIC cxx_std_20)
|
||||
|
||||
#target_include_directories(${PROJECT_NAME}_test PRIVATE
|
||||
# E:\\thermion\\thermion_dart\\native\\include\\vulkan
|
||||
## E:\\thermion\\thermion_dart\\native\\include
|
||||
# E:\\thermion\\thermion_dart\\native\\include\\filament
|
||||
#)
|
||||
#target_link_directories(${PROJECT_NAME}_test PRIVATE
|
||||
# E:\\thermion\\thermion_dart\\.dart_tool\\thermion_dart\\lib\\v1.51.2\\windows\\debug
|
||||
# E:\\VulkanSDK\\1.3.296.0\\Lib
|
||||
#)
|
||||
|
||||
#target_link_libraries(${PROJECT_NAME}_test PRIVATE
|
||||
# ${PROJECT_NAME}
|
||||
#)
|
||||
|
||||
#add_dependencies(${PROJECT_NAME}_test ${PROJECT_NAME})
|
||||
134
thermion_dart/native/src/windows/vulkan/d3d_context.cpp
Normal file
134
thermion_dart/native/src/windows/vulkan/d3d_context.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "d3d_context.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace thermion::windows::d3d
|
||||
{
|
||||
|
||||
D3DContext::D3DContext()
|
||||
{
|
||||
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<IDXGIFactory2> 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<UINT>(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<IDXGIDevice> 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].
|
||||
}
|
||||
|
||||
auto level = _D3D11Device->GetFeatureLevel();
|
||||
std::cout << "Direct3D Feature Level: "
|
||||
<< (((unsigned)level) >> 12) << "_"
|
||||
<< ((((unsigned)level) >> 8) & 0xf) << std::endl;
|
||||
}
|
||||
|
||||
D3DContext::~D3DContext() {
|
||||
if (_D3D11DeviceContext) {
|
||||
_D3D11DeviceContext->Release();
|
||||
}
|
||||
if (_D3D11Device) {
|
||||
_D3D11Device->Release();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<D3DTexture> D3DContext::CreateTexture(uint32_t width, uint32_t height)
|
||||
{
|
||||
// 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_B8G8R8A8_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 | D3D11_RESOURCE_MISC_SHARED_NTHANDLE;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D11Texture2D> _d3dTexture2D;
|
||||
|
||||
|
||||
auto hr = _D3D11Device->CreateTexture2D(&d3d11_texture2D_desc, nullptr, &_d3dTexture2D);
|
||||
if FAILED (hr)
|
||||
{
|
||||
std::cout << "Failed to create D3D texture (" << hr << ")" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
auto resource = Microsoft::WRL::ComPtr<IDXGIResource1>{};
|
||||
hr = _d3dTexture2D.As(&resource);
|
||||
|
||||
if FAILED (hr)
|
||||
{
|
||||
std::cout << "Failed to create D3D texture" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
HANDLE _d3dTexture2DHandle = nullptr;
|
||||
// hr = resource->GetSharedHandle(&_d3dTexture2DHandle);
|
||||
hr = resource->CreateSharedHandle(nullptr, GENERIC_ALL, nullptr, &_d3dTexture2DHandle);
|
||||
if FAILED (hr)
|
||||
{
|
||||
std::cout << "Failed to get shared handle to external D3D texture" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_d3dTexture2D->AddRef();
|
||||
|
||||
std::cout << "Created external D3D texture " << width << "x" << height << std::endl;
|
||||
return std::make_unique<D3DTexture>(_d3dTexture2D, _d3dTexture2DHandle, width, height);
|
||||
}
|
||||
|
||||
void D3DContext::Flush()
|
||||
{
|
||||
_D3D11DeviceContext->Flush();
|
||||
}
|
||||
|
||||
}
|
||||
128
thermion_dart/native/src/windows/vulkan/d3d_texture.cpp
Normal file
128
thermion_dart/native/src/windows/vulkan/d3d_texture.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "d3d_texture.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
namespace thermion::windows::d3d
|
||||
{
|
||||
|
||||
bool IsNTHandleSupported(ID3D11Device *device)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<ID3D11Device5> device5;
|
||||
return SUCCEEDED(device->QueryInterface(IID_PPV_ARGS(&device5)));
|
||||
}
|
||||
|
||||
D3DTexture::D3DTexture(
|
||||
Microsoft::WRL::ComPtr<ID3D11Texture2D> d3dTexture2D,
|
||||
HANDLE d3dTexture2DHandle, uint32_t width, uint32_t height) : _d3dTexture2D(d3dTexture2D), _d3dTexture2DHandle(d3dTexture2DHandle), _width(width), _height(height)
|
||||
{
|
||||
}
|
||||
|
||||
D3DTexture::~D3DTexture() {
|
||||
if (_d3dTexture2DHandle) {
|
||||
CloseHandle(_d3dTexture2DHandle);
|
||||
_d3dTexture2DHandle = nullptr;
|
||||
}
|
||||
if (_d3dTexture2D) {
|
||||
_d3dTexture2D->Release();
|
||||
_d3dTexture2D = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE D3DTexture::GetTextureHandle() {
|
||||
return _d3dTexture2DHandle;
|
||||
}
|
||||
|
||||
void D3DTexture::SaveToBMP(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;
|
||||
// }
|
||||
|
||||
// // 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;
|
||||
// } else {
|
||||
// std::cout << "Texture save failed to " << filename << std::endl;
|
||||
// }
|
||||
}
|
||||
|
||||
bool D3DTexture::SaveTextureAsBMP(ID3D11Texture2D *texture, const char *filename)
|
||||
{
|
||||
return false;
|
||||
// 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;
|
||||
// }
|
||||
|
||||
// auto success = SavePixelsAsBMP(reinterpret_cast<uint8_t*>(mappedResource.pData), desc.Width, desc.Height, mappedResource.RowPitch, filename);
|
||||
|
||||
// if(!success) {
|
||||
// std::cout << "BMP write failed" << std::endl;
|
||||
// }
|
||||
|
||||
// _D3D11DeviceContext->Unmap(texture, 0);
|
||||
// return success;
|
||||
}
|
||||
|
||||
} // namespace thermion_flutter
|
||||
|
||||
// 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);
|
||||
95
thermion_dart/native/src/windows/vulkan/main.cpp
Normal file
95
thermion_dart/native/src/windows/vulkan/main.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "ThermionWin32.h"
|
||||
|
||||
#include "vulkan_context.h"
|
||||
|
||||
#include "d3d_texture.h"
|
||||
|
||||
#include "filament/backend/platforms/VulkanPlatform.h"
|
||||
#include "filament/Engine.h"
|
||||
#include "filament/Renderer.h"
|
||||
#include "filament/View.h"
|
||||
#include "filament/Viewport.h"
|
||||
#include "filament/Scene.h"
|
||||
#include "filament/SwapChain.h"
|
||||
#include "filament/Texture.h"
|
||||
#include "utils/EntityManager.h"
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
auto ctx = new thermion::windows::vulkan::ThermionVulkanContext();
|
||||
uint32_t width = 100;
|
||||
uint32_t height = 100;
|
||||
auto handle = ctx->CreateRenderingSurface(width, height, 0, 0);
|
||||
std::cout << "CREATED" << std::endl;
|
||||
ctx->DestroyRenderingSurface(handle);
|
||||
std::cout << "FINISHED" << std::endl;
|
||||
|
||||
// auto *engine = filament::Engine::create(filament::Engine::Backend::VULKAN, ctx->GetPlatform(), nullptr, nullptr);
|
||||
// auto *swapChain = engine->createSwapChain(width,height, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE | filament::SwapChain::CONFIG_HAS_STENCIL_BUFFER);
|
||||
// engine->flushAndWait();
|
||||
|
||||
// if(engine->isValid(reinterpret_cast<filament::SwapChain*>(swapChain))) {
|
||||
// std::cout << "VALID SWAPCHIAN" << std::endl;
|
||||
// } else {
|
||||
// std::cout << "INVALID SWAPCHIAN" << std::endl;
|
||||
// }
|
||||
|
||||
// auto renderer = engine->createRenderer();
|
||||
// filament::Renderer::ClearOptions clearOptions;
|
||||
// clearOptions.clear = true;
|
||||
// clearOptions.clearColor = { 1.0f, 0.0f, 0.5f, 1.0f };
|
||||
// renderer->setClearOptions(clearOptions);
|
||||
// auto scene = engine->createScene();
|
||||
// auto *view = engine->createView();
|
||||
// view->setViewport(filament::Viewport {0,0, width,height});
|
||||
// view->setBlendMode(filament::View::BlendMode::TRANSLUCENT);
|
||||
// view->setScene(scene);
|
||||
|
||||
// auto camera = engine->createCamera(utils::EntityManager::get().create());
|
||||
// view->setCamera(camera);
|
||||
|
||||
// engine->flushAndWait();
|
||||
// size_t pixelBufferSize = width * height * 4;
|
||||
// auto out = new uint8_t[pixelBufferSize];
|
||||
// auto pbd = filament::Texture::PixelBufferDescriptor(
|
||||
// out, pixelBufferSize,
|
||||
// filament::Texture::Format::RGBA,
|
||||
// filament::Texture::Type::UBYTE, nullptr, nullptr, nullptr);
|
||||
// renderer->beginFrame(swapChain);
|
||||
// renderer->render(view);
|
||||
// renderer->readPixels(0, 0, width, height, std::move(pbd));
|
||||
// renderer->endFrame();
|
||||
|
||||
// engine->flushAndWait();
|
||||
// std::cout << "FLUSHED" << std::endl;
|
||||
|
||||
// if(!SavePixelsAsBMP(out, width, height, width, "savepixels.bmp")) {
|
||||
// std::cout << "FAILED TO SAVE PIXELS" << std::endl;
|
||||
// }
|
||||
|
||||
// // ctx->GetTexture()->Flush();
|
||||
// ctx->BlitFromSwapchain(width,height);
|
||||
// ctx->GetTexture()->SaveToBMP("d3d_texture.bmp");
|
||||
|
||||
// std::vector<uint8_t> outPixels(width * height * 4);
|
||||
|
||||
// ctx->readPixelsFromImage(width, height, outPixels);
|
||||
|
||||
// std::cout << "READBACK FROM VULKAN COMPLETE " << std::endl;
|
||||
|
||||
// thermion::windows::d3d::D3DTexture::SavePixelsAsBMP(outPixels.data(), width, height, width, "vulkan_readback.bmp");
|
||||
|
||||
}
|
||||
|
||||
773
thermion_dart/native/src/windows/vulkan/utils.cpp
Normal file
773
thermion_dart/native/src/windows/vulkan/utils.cpp
Normal file
@@ -0,0 +1,773 @@
|
||||
#include "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_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;
|
||||
|
||||
}
|
||||
511
thermion_dart/native/src/windows/vulkan/vulkan_context.cpp
Normal file
511
thermion_dart/native/src/windows/vulkan/vulkan_context.cpp
Normal file
@@ -0,0 +1,511 @@
|
||||
|
||||
#define THERMION_WIN32_KHR_BUILD
|
||||
#include "vulkan_context.h"
|
||||
|
||||
#include "ThermionWin32.h"
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "filament/backend/platforms/VulkanPlatform.h"
|
||||
#include "filament/Engine.h"
|
||||
#include "filament/Renderer.h"
|
||||
#include "filament/View.h"
|
||||
#include "filament/Viewport.h"
|
||||
#include "filament/Scene.h"
|
||||
#include "filament/SwapChain.h"
|
||||
#include "filament/Texture.h"
|
||||
|
||||
#include "Log.hpp"
|
||||
|
||||
namespace thermion::windows::vulkan {
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
ThermionVulkanContext::ThermionVulkanContext() {
|
||||
bluevk::initialize();
|
||||
|
||||
// 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;
|
||||
}
|
||||
bluevk::bindInstance(instance);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
uint32_t queueFamilyIndex;
|
||||
|
||||
createDeviceWithGraphicsQueue(physicalDevice,queueFamilyIndex, &device);
|
||||
|
||||
CommandResources cmdResources = createCommandResources(device, physicalDevice);
|
||||
|
||||
commandPool = cmdResources.commandPool;
|
||||
queue = cmdResources.queue;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
_platform = new TVulkanPlatform();
|
||||
|
||||
_d3dContext = std::make_unique<thermion::windows::d3d::D3DContext>();
|
||||
|
||||
}
|
||||
|
||||
HANDLE ThermionVulkanContext::CreateRenderingSurface(uint32_t width, uint32_t height, uint32_t left, uint32_t top) {
|
||||
|
||||
Log("Creating Vulkan texture %dx%d", width, height);
|
||||
|
||||
// creates the D3D texture
|
||||
auto d3dTexture = _d3dContext->CreateTexture(width, height);
|
||||
auto d3dTextureHandle = d3dTexture->GetTextureHandle();
|
||||
auto vkTexture = VulkanTexture::create(device, physicalDevice, width, height, d3dTextureHandle);
|
||||
|
||||
|
||||
// fillImageWithColor(device, commandPool, queue, image, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, // Current image layout
|
||||
// { width, height, 1 }, // Image extent
|
||||
// 0.0f, 1.0f, 0.0f, 1.0f); // Red color (RGBA))
|
||||
|
||||
_d3dTextures.push_back(std::move(d3dTexture));
|
||||
_vulkanTextures.push_back(std::move(vkTexture));
|
||||
return d3dTextureHandle;
|
||||
}
|
||||
|
||||
void ThermionVulkanContext::ResizeRenderingSurface(uint32_t width, uint32_t height, uint32_t left, uint32_t top) {
|
||||
|
||||
}
|
||||
|
||||
void ThermionVulkanContext::DestroyRenderingSurface(HANDLE handle) {
|
||||
|
||||
_vulkanTextures.erase(std::remove_if(_vulkanTextures.begin(), _vulkanTextures.end(), [=](auto&& vkTexture) {
|
||||
return vkTexture->GetD3DTextureHandle() == handle;
|
||||
}));
|
||||
|
||||
_d3dTextures.erase(std::remove_if(_d3dTextures.begin(), _d3dTextures.end(), [=](auto&& d3dTexture) {
|
||||
return d3dTexture->GetTextureHandle() == handle;
|
||||
}));
|
||||
}
|
||||
|
||||
void ThermionVulkanContext::Flush() {
|
||||
// ?? what to do here
|
||||
}
|
||||
|
||||
// Function to perform the blit operation
|
||||
void ThermionVulkanContext::BlitFromSwapchain() {
|
||||
std::lock_guard lock(_platform->mutex);
|
||||
if(!_platform->_current || _d3dTextures.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
auto&& vkTexture = _vulkanTextures.back();
|
||||
auto image = vkTexture->GetImage();
|
||||
|
||||
auto&& texture = _d3dTextures.back();
|
||||
|
||||
auto height = texture->GetHeight();
|
||||
auto width = texture->GetWidth();
|
||||
|
||||
auto bundle = _platform->getSwapChainBundle(_platform->_current);
|
||||
VkImage swapchainImage = bundle.colors[_platform->_currentColorIndex];
|
||||
// Command buffer allocation
|
||||
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 cmd;
|
||||
VkResult result = bluevk::vkAllocateCommandBuffers(device, &cmdBufInfo, &cmd);
|
||||
if (result != VK_SUCCESS) {
|
||||
std::cout << "Failed to allocate command buffer: " << result << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 = bluevk::vkBeginCommandBuffer(cmd, &beginInfo);
|
||||
if (result != VK_SUCCESS) {
|
||||
std::cout << "Failed to begin command buffer: " << result << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// std::cout << "Starting blit operation..." << std::endl;
|
||||
|
||||
// Pre-transition barriers
|
||||
VkImageMemoryBarrier srcBarrier{};
|
||||
srcBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
srcBarrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
||||
srcBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
srcBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
srcBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
srcBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
srcBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
srcBarrier.image = swapchainImage;
|
||||
srcBarrier.subresourceRange = {
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
0, 1, 0, 1
|
||||
};
|
||||
|
||||
VkImageMemoryBarrier dstBarrier{};
|
||||
dstBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
dstBarrier.srcAccessMask = 0;
|
||||
dstBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
dstBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
dstBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
dstBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
dstBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
dstBarrier.image = image;
|
||||
dstBarrier.subresourceRange = {
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
0, 1, 0, 1
|
||||
};
|
||||
|
||||
// std::cout << "Transitioning images to transfer layouts..." << std::endl;
|
||||
|
||||
// Pre-blit barriers
|
||||
VkImageMemoryBarrier preBlitBarriers[] = {srcBarrier, dstBarrier};
|
||||
vkCmdPipelineBarrier(
|
||||
cmd,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, // Changed from TRANSFER_BIT for better sync
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0,
|
||||
0, nullptr,
|
||||
0, nullptr,
|
||||
2, preBlitBarriers
|
||||
);
|
||||
|
||||
// Define blit region with bounds checking
|
||||
VkImageBlit blit{};
|
||||
blit.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
|
||||
blit.srcOffsets[0] = {0, 0, 0};
|
||||
blit.srcOffsets[1] = {static_cast<int32_t>(width), static_cast<int32_t>(height), 1};
|
||||
blit.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
|
||||
blit.dstOffsets[0] = {0, 0, 0};
|
||||
blit.dstOffsets[1] = {static_cast<int32_t>(width), static_cast<int32_t>(height), 1};
|
||||
|
||||
// std::cout << "Executing blit command..." << std::endl;
|
||||
// std::cout << "Source dimensions: " << width << "x" << height << std::endl;
|
||||
// std::cout << "Destination dimensions: " << width << "x" << height << std::endl;
|
||||
|
||||
// Perform blit with validation
|
||||
bluevk::vkCmdBlitImage(
|
||||
cmd,
|
||||
swapchainImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1, &blit,
|
||||
VK_FILTER_NEAREST // Changed to NEAREST for debugging
|
||||
);
|
||||
|
||||
// Post-transition barriers
|
||||
srcBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
srcBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
||||
srcBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
srcBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
|
||||
dstBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
dstBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
dstBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
dstBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
// std::cout << "Transitioning images back to original layouts..." << std::endl;
|
||||
|
||||
// Post-blit barriers
|
||||
VkImageMemoryBarrier postBlitBarriers[] = {srcBarrier, dstBarrier};
|
||||
bluevk::vkCmdPipelineBarrier(
|
||||
cmd,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, // Changed for better sync
|
||||
0,
|
||||
0, nullptr,
|
||||
0, nullptr,
|
||||
2, postBlitBarriers
|
||||
);
|
||||
|
||||
// End command buffer
|
||||
result = bluevk::vkEndCommandBuffer(cmd);
|
||||
if (result != VK_SUCCESS) {
|
||||
std::cout << "Failed to end command buffer: " << result << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create fence for synchronization
|
||||
// VkFenceCreateInfo fenceInfo{};
|
||||
// fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||
|
||||
// VkFence fence;
|
||||
// result = bluevk::vkCreateFence(device, &fenceInfo, nullptr, &fence);
|
||||
// if (result != VK_SUCCESS) {
|
||||
// std::cout << "Failed to create fence: " << result << std::endl;
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Submit with fence
|
||||
VkSubmitInfo submitInfo{};
|
||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = &cmd;
|
||||
|
||||
result = bluevk::vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); //fence);
|
||||
if (result != VK_SUCCESS) {
|
||||
std::cout << "Failed to submit queue: " << result << std::endl;
|
||||
// bluevk::vkDestroyFence(device, fence, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// // Wait for fence with timeout
|
||||
// result = bluevk::vkWaitForFences(device, 1, &fence, VK_TRUE, 5000000000); // 5 second timeout
|
||||
// if (result != VK_SUCCESS) {
|
||||
// std::cout << "Failed to wait for fence: " << result << std::endl;
|
||||
// vkDestroyFence(device, fence, nullptr);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// std::cout << "Blit operation completed successfully" << std::endl;
|
||||
|
||||
// // Cleanup
|
||||
// bluevk::vkDestroyFence(device, fence, nullptr);
|
||||
// bluevk::vkFreeCommandBuffers(device, commandPool, 1, &cmd);
|
||||
}
|
||||
|
||||
void ThermionVulkanContext::readPixelsFromImage(
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
std::vector<uint8_t>& outPixels
|
||||
) {
|
||||
|
||||
auto&& vkTexture = _vulkanTextures.back();
|
||||
auto image = vkTexture->GetImage();
|
||||
|
||||
VkDeviceSize bufferSize = width * height * 4; // RGBA8 format
|
||||
|
||||
// Create staging buffer
|
||||
VkBuffer stagingBuffer;
|
||||
VkDeviceMemory stagingBufferMemory;
|
||||
|
||||
VkBufferCreateInfo bufferInfo{};
|
||||
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
bufferInfo.size = bufferSize;
|
||||
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
||||
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
||||
VkResult result = bluevk::vkCreateBuffer(device, &bufferInfo, nullptr, &stagingBuffer);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create staging buffer");
|
||||
}
|
||||
|
||||
// Get memory requirements and allocate
|
||||
VkMemoryRequirements memRequirements;
|
||||
bluevk::vkGetBufferMemoryRequirements(device, stagingBuffer, &memRequirements);
|
||||
|
||||
VkMemoryAllocateInfo allocInfo{};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
allocInfo.allocationSize = memRequirements.size;
|
||||
allocInfo.memoryTypeIndex = findMemoryType(
|
||||
memRequirements.memoryTypeBits,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||
physicalDevice
|
||||
);
|
||||
|
||||
result = bluevk::vkAllocateMemory(device, &allocInfo, nullptr, &stagingBufferMemory);
|
||||
if (result != VK_SUCCESS) {
|
||||
bluevk::vkDestroyBuffer(device, stagingBuffer, nullptr);
|
||||
throw std::runtime_error("Failed to allocate staging buffer memory");
|
||||
}
|
||||
|
||||
result = bluevk::vkBindBufferMemory(device, stagingBuffer, stagingBufferMemory, 0);
|
||||
if (result != VK_SUCCESS) {
|
||||
vkFreeMemory(device, stagingBufferMemory, nullptr);
|
||||
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
||||
throw std::runtime_error("Failed to bind buffer memory");
|
||||
}
|
||||
|
||||
// Create command buffer
|
||||
VkCommandBufferAllocateInfo cmdBufAllocInfo{};
|
||||
cmdBufAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
cmdBufAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
cmdBufAllocInfo.commandPool = commandPool;
|
||||
cmdBufAllocInfo.commandBufferCount = 1;
|
||||
|
||||
VkCommandBuffer commandBuffer;
|
||||
result = bluevk::vkAllocateCommandBuffers(device, &cmdBufAllocInfo, &commandBuffer);
|
||||
if (result != VK_SUCCESS) {
|
||||
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 = bluevk::vkBeginCommandBuffer(commandBuffer, &beginInfo);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to begin command buffer");
|
||||
}
|
||||
|
||||
// Transition image layout for transfer with proper sync
|
||||
VkImageMemoryBarrier barrier{};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
barrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // Assuming this is the current layout
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_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;
|
||||
|
||||
bluevk::vkCmdPipelineBarrier(
|
||||
commandBuffer,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0,
|
||||
0, nullptr,
|
||||
0, nullptr,
|
||||
1, &barrier
|
||||
);
|
||||
|
||||
// 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};
|
||||
|
||||
bluevk::vkCmdCopyImageToBuffer(
|
||||
commandBuffer,
|
||||
image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
stagingBuffer,
|
||||
1,
|
||||
®ion
|
||||
);
|
||||
|
||||
// Transition image layout back
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
bluevk::vkCmdPipelineBarrier(
|
||||
commandBuffer,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
0,
|
||||
0, nullptr,
|
||||
0, nullptr,
|
||||
1, &barrier
|
||||
);
|
||||
|
||||
result = bluevk::vkEndCommandBuffer(commandBuffer);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to end command buffer");
|
||||
}
|
||||
|
||||
// Submit command buffer with fence for synchronization
|
||||
VkFenceCreateInfo fenceInfo{};
|
||||
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||
|
||||
VkFence fence;
|
||||
result = bluevk::vkCreateFence(device, &fenceInfo, nullptr, &fence);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create fence");
|
||||
}
|
||||
|
||||
VkSubmitInfo submitInfo{};
|
||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = &commandBuffer;
|
||||
|
||||
result = bluevk::vkQueueSubmit(queue, 1, &submitInfo, fence);
|
||||
if (result != VK_SUCCESS) {
|
||||
bluevk::vkDestroyFence(device, fence, nullptr);
|
||||
throw std::runtime_error("Failed to submit queue");
|
||||
}
|
||||
|
||||
// Wait for the command buffer to complete with timeout
|
||||
result = bluevk::vkWaitForFences(device, 1, &fence, VK_TRUE, 5000000000); // 5 second timeout
|
||||
if (result != VK_SUCCESS) {
|
||||
bluevk::vkDestroyFence(device, fence, nullptr);
|
||||
throw std::runtime_error("Failed to wait for fence");
|
||||
}
|
||||
|
||||
// Map memory and copy data
|
||||
void* data;
|
||||
result = bluevk::vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to map memory");
|
||||
}
|
||||
|
||||
outPixels.resize(bufferSize);
|
||||
memcpy(outPixels.data(), data, bufferSize);
|
||||
bluevk::vkUnmapMemory(device, stagingBufferMemory);
|
||||
|
||||
// Cleanup
|
||||
bluevk::vkDestroyFence(device, fence, nullptr);
|
||||
vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
|
||||
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
||||
vkFreeMemory(device, stagingBufferMemory, nullptr);
|
||||
|
||||
std::cout << "Successfully completed readPixelsFromImage" << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
158
thermion_dart/native/src/windows/vulkan/vulkan_texture.cpp
Normal file
158
thermion_dart/native/src/windows/vulkan/vulkan_texture.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
#include "vulkan_texture.h"
|
||||
#include "bluevk/BlueVK.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace thermion::windows::vulkan
|
||||
{
|
||||
|
||||
VulkanTexture::VulkanTexture(VkImage image, VkDevice device, VkDeviceMemory imageMemory, uint32_t width, uint32_t height, HANDLE d3dTextureHandle) : _image(image), _device(device), _imageMemory(imageMemory), _width(width), _height(height), _d3dTextureHandle(d3dTextureHandle) {};
|
||||
|
||||
VulkanTexture::~VulkanTexture() {
|
||||
bluevk::vkDeviceWaitIdle(_device);
|
||||
if(_image != VK_NULL_HANDLE) {
|
||||
bluevk::vkDestroyImage(_device, _image, nullptr);
|
||||
} else {
|
||||
std::cout << "Warning : no vkImage found" << std::endl;
|
||||
}
|
||||
|
||||
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImportMemoryWin32HandleInfoKHR.html
|
||||
// imageMemory has been imported from D3D without transferring ownership
|
||||
// therefore we don't need to release
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<VulkanTexture> VulkanTexture::create(VkDevice device, VkPhysicalDevice physicalDevice, uint32_t width, uint32_t height, HANDLE d3dTextureHandle)
|
||||
{
|
||||
// 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_B8G8R8A8_UNORM,
|
||||
.extent = {width, height, 1},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED};
|
||||
|
||||
VkImage image;
|
||||
|
||||
VkResult result = bluevk::vkCreateImage(device, &imageInfo, nullptr, &image);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
std::cout << "Failed to create iamge " << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::cout << "Created vkImage " << (int64_t)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...
|
||||
bluevk::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 = d3dTextureHandle,
|
||||
.name = nullptr};
|
||||
|
||||
// Find suitable memory type
|
||||
uint32_t memoryTypeIndex = findOptimalMemoryType(
|
||||
physicalDevice,
|
||||
MemoryRequirements.memoryTypeBits,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
|
||||
);
|
||||
|
||||
VkMemoryAllocateInfo allocInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = &ImportMemoryWin32HandleInfo,
|
||||
.allocationSize = MemoryRequirements.size,
|
||||
.memoryTypeIndex = memoryTypeIndex
|
||||
};
|
||||
|
||||
VkDeviceMemory imageMemory = VK_NULL_HANDLE;
|
||||
|
||||
VkResult allocResult = bluevk::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 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 nullptr;
|
||||
}
|
||||
|
||||
const VkBindImageMemoryInfo bindImageMemoryInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO,
|
||||
.pNext = nullptr,
|
||||
.image = image,
|
||||
.memory = imageMemory,
|
||||
.memoryOffset = 0};
|
||||
|
||||
result = bluevk::vkBindImageMemory2(device, 1, &bindImageMemoryInfo);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
std::cout << "vkBindImageMemory2 failed" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_unique<VulkanTexture>(image, device, imageMemory, width, height, d3dTextureHandle);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user