Add preliminary Win32 Vulkan support

This commit is contained in:
Jack Andersen 2016-07-17 11:15:57 -10:00
parent 02c1004d67
commit 3076c0525d
5 changed files with 330 additions and 10 deletions

View File

@ -33,6 +33,19 @@ list(APPEND PLAT_HDRS
endif()
if(WIN32)
unset(VULKAN_SDK_DIRS CACHE)
get_filename_component(VULKAN_SDK_DIRS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\LunarG\\VulkanSDK;VK_SDK_PATHs]" ABSOLUTE CACHE)
if (NOT ${VULKAN_SDK_DIRS} STREQUAL "/registry")
message(STATUS "Enabling Vulkan support")
list(GET VULKAN_SDK_DIRS 0 VULKAN_SDK_DIR)
include_directories("${VULKAN_SDK_DIR}/Include")
list(APPEND _BOO_SYS_DEFINES -DBOO_HAS_VULKAN=1)
list(APPEND _BOO_SYS_INCLUDES "${VULKAN_SDK_DIR}/Include")
list(APPEND PLAT_SRCS lib/graphicsdev/Vulkan.cpp
lib/graphicsdev/VulkanDispatchTable.cpp)
endif()
list(APPEND PLAT_SRCS
lib/win/ApplicationWin32.cpp
lib/win/WindowWin32.cpp
@ -182,6 +195,7 @@ list(APPEND _BOO_SYS_LIBS glslang HLSL soxr OSDependent OGLCompiler SPIRV glslan
set(BOO_SYS_LIBS ${_BOO_SYS_LIBS} CACHE PATH "boo system libraries" FORCE)
set(BOO_SYS_DEFINES ${_BOO_SYS_DEFINES} CACHE PATH "boo system defines" FORCE)
set(BOO_SYS_INCLUDES ${_BOO_SYS_INCLUDES} CACHE PATH "boo system includes" FORCE)
add_definitions(${_BOO_SYS_DEFINES})
include_directories(include glslang soxr/src)

View File

@ -1,6 +1,7 @@
#include "boo/graphicsdev/Vulkan.hpp"
#include "boo/IGraphicsContext.hpp"
#include <vector>
#include <array>
#include <cmath>
#include <glslang/Public/ShaderLang.h>
#include <StandAlone/ResourceLimits.h>
@ -230,7 +231,11 @@ void VulkanContext::initVulkan(const char* appName)
* entries loaded into the data pointer - in case the number
* of layers went down or is smaller than the size given.
*/
#ifdef _WIN32
_putenv("VK_LAYER_PATH=\\VulkanSDK\\1.0.17.0\\Bin");
#else
setenv("VK_LAYER_PATH", "/usr/share/vulkan/explicit_layer.d", 1);
#endif
do {
ThrowIfFailed(vk::EnumerateInstanceLayerProperties(&instanceLayerCount, nullptr));
@ -304,7 +309,15 @@ void VulkanContext::initVulkan(const char* appName)
instInfo.enabledExtensionCount = m_instanceExtensionNames.size();
instInfo.ppEnabledExtensionNames = m_instanceExtensionNames.data();
ThrowIfFailed(vk::CreateInstance(&instInfo, nullptr, &m_instance));
VkResult instRes = vk::CreateInstance(&instInfo, nullptr, &m_instance);
if (instRes != VK_SUCCESS)
{
MessageBoxW(nullptr, L"Error creating Vulkan instance\n\n"
L"The Vulkan runtime is installed, but there are no supported "
L"hardware vendor interfaces present",
L"Vulkan Error", MB_OK | MB_ICONERROR);
exit(1);
}
#ifndef NDEBUG
VkDebugReportCallbackEXT debugReportCallback;
@ -877,7 +890,7 @@ class VulkanTextureS : public ITextureS
texCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
texCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
texCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
texCreateInfo.extent = { m_width, m_height, 1 };
texCreateInfo.extent = { uint32_t(m_width), uint32_t(m_height), 1 };
texCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
ThrowIfFailed(vk::CreateImage(ctx->m_dev, &texCreateInfo, nullptr, &m_gpuTex));
@ -1070,7 +1083,7 @@ class VulkanTextureSA : public ITextureSA
texCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
texCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
texCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
texCreateInfo.extent = { m_width, m_height, 1 };
texCreateInfo.extent = { uint32_t(m_width), uint32_t(m_height), 1 };
texCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
ThrowIfFailed(vk::CreateImage(ctx->m_dev, &texCreateInfo, nullptr, &m_gpuTex));

View File

@ -18,6 +18,10 @@
#include "boo/graphicsdev/D3D.hpp"
#include "logvisor/logvisor.hpp"
#if BOO_HAS_VULKAN
#include <vulkan/vulkan.h>
#endif
DWORD g_mainThreadId = 0;
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
@ -60,7 +64,8 @@ namespace boo
static logvisor::Module Log("boo::ApplicationWin32");
Win32Cursors WIN32_CURSORS;
IWindow* _WindowWin32New(const SystemString& title, Boo3DAppContext& d3dCtx, uint32_t sampleCount);
IWindow* _WindowWin32New(const SystemString& title, Boo3DAppContext& d3dCtx,
void* vulkanHandle, uint32_t sampleCount);
class ApplicationWin32 final : public IApplication
{
@ -73,6 +78,7 @@ class ApplicationWin32 final : public IApplication
bool m_singleInstance;
Boo3DAppContext m_3dCtx;
PFN_vkGetInstanceProcAddr m_getVkProc = nullptr;
void _deletedWindow(IWindow* window)
{
@ -105,12 +111,25 @@ public:
bool no12 = false;
bool noD3d = false;
#if BOO_HAS_VULKAN
bool useVulkan = true;
#endif
for (const SystemString& arg : args)
{
if (!arg.compare(L"--d3d11"))
no12 = true;
#if BOO_HAS_VULKAN
if (!arg.compare(L"--vulkan"))
noD3d = true;
if (!arg.compare(L"--gl"))
{
noD3d = true;
useVulkan = false;
}
#else
if (!arg.compare(L"--gl"))
noD3d = true;
#endif
}
#if _WIN32_WINNT_WIN10
@ -227,6 +246,27 @@ public:
return;
}
#if BOO_HAS_VULKAN
if (useVulkan)
{
HMODULE vulkanLib = LoadLibraryW(L"vulkan-1.dll");
if (vulkanLib)
{
m_getVkProc = (PFN_vkGetInstanceProcAddr)GetProcAddress(vulkanLib, "vkGetInstanceProcAddr");
if (m_getVkProc)
{
/* Obtain DXGI Factory */
HRESULT hr = MyCreateDXGIFactory1(__uuidof(IDXGIFactory1), &m_3dCtx.m_vulkanDxFactory);
if (FAILED(hr))
Log.report(logvisor::Fatal, "unable to create DXGI factory");
Log.report(logvisor::Info, "initialized Vulkan renderer");
return;
}
}
}
#endif
/* Finally try OpenGL */
{
/* Obtain DXGI Factory */
@ -383,7 +423,7 @@ public:
return m_mwret;
}
IWindow* window = _WindowWin32New(title, m_3dCtx, sampleCount);
IWindow* window = _WindowWin32New(title, m_3dCtx, m_getVkProc, sampleCount);
HWND hwnd = HWND(window->getPlatformHandle());
m_allWindows[hwnd] = window;
return window;

View File

@ -103,6 +103,7 @@ struct Boo3DAppContext
D3D12Context m_ctx12;
#endif
OGLContext m_ctxOgl;
ComPtr<IDXGIFactory1> m_vulkanDxFactory;
void resize(boo::IWindow* window, size_t width, size_t height)
{

View File

@ -1,5 +1,6 @@
#include "Win32Common.hpp"
#include <Windowsx.h>
#include "boo/IApplication.hpp"
#include "boo/IWindow.hpp"
#include "boo/IGraphicsContext.hpp"
#include "logvisor/logvisor.hpp"
@ -9,6 +10,10 @@
#include "boo/graphicsdev/glew.h"
#include "boo/graphicsdev/wglew.h"
#if BOO_HAS_VULKAN
#include "boo/graphicsdev/Vulkan.hpp"
#endif
static const int ContextAttribs[] =
{
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
@ -30,6 +35,11 @@ IGraphicsDataFactory* _NewD3D12DataFactory(D3D12Context* ctx, IGraphicsContext*
IGraphicsCommandQueue* _NewD3D11CommandQueue(D3D11Context* ctx, D3D11Context::Window* windowCtx, IGraphicsContext* parent);
IGraphicsDataFactory* _NewD3D11DataFactory(D3D11Context* ctx, IGraphicsContext* parent, uint32_t sampleCount);
IGraphicsCommandQueue* _NewGLCommandQueue(IGraphicsContext* parent);
#if BOO_HAS_VULKAN
IGraphicsCommandQueue* _NewVulkanCommandQueue(VulkanContext* ctx,
VulkanContext::Window* windowCtx,
IGraphicsContext* parent);
#endif
struct GraphicsContextWin32 : IGraphicsContext
{
@ -370,6 +380,239 @@ public:
}
};
#if BOO_HAS_VULKAN
struct GraphicsContextWin32Vulkan : GraphicsContextWin32
{
HINSTANCE m_appInstance;
HWND m_hwnd;
VulkanContext* m_ctx;
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
VkFormat m_format;
VkColorSpaceKHR m_colorspace;
uint32_t m_sampleCount;
IGraphicsCommandQueue* m_commandQueue = nullptr;
IGraphicsDataFactory* m_dataFactory = nullptr;
std::thread m_vsyncThread;
bool m_vsyncRunning;
static void ThrowIfFailed(VkResult res)
{
if (res != VK_SUCCESS)
Log.report(logvisor::Fatal, "%d\n", res);
}
public:
IWindowCallback* m_callback;
GraphicsContextWin32Vulkan(IWindow* parentWindow, HINSTANCE appInstance, HWND hwnd,
VulkanContext* ctx, Boo3DAppContext& b3dCtx, uint32_t drawSamples)
: GraphicsContextWin32(EGraphicsAPI::Vulkan, parentWindow, b3dCtx),
m_appInstance(appInstance), m_hwnd(hwnd), m_ctx(ctx), m_sampleCount(drawSamples)
{
HMONITOR testMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
ComPtr<IDXGIAdapter1> adapter;
ComPtr<IDXGIOutput> foundOut;
int i=0;
while (b3dCtx.m_vulkanDxFactory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND)
{
int j=0;
ComPtr<IDXGIOutput> out;
while (adapter->EnumOutputs(j, &out) != DXGI_ERROR_NOT_FOUND)
{
DXGI_OUTPUT_DESC desc;
out->GetDesc(&desc);
if (desc.Monitor == testMon)
{
out.As<IDXGIOutput>(&m_output);
break;
}
++j;
}
if (m_output)
break;
++i;
}
if (!m_output)
Log.report(logvisor::Fatal, "unable to find window's IDXGIOutput");
}
void destroy()
{
VulkanContext::Window& m_windowCtx = *m_ctx->m_windows[m_parentWindow];
m_windowCtx.m_swapChains[0].destroy(m_ctx->m_dev);
m_windowCtx.m_swapChains[1].destroy(m_ctx->m_dev);
//vk::DestroySurfaceKHR(m_ctx->m_instance, m_surface, nullptr);
if (m_vsyncRunning)
{
m_vsyncRunning = false;
m_vsyncThread.join();
}
m_ctx->m_windows.erase(m_parentWindow);
}
~GraphicsContextWin32Vulkan() {destroy();}
VulkanContext::Window* m_windowCtx = nullptr;
void resized(SWindowRect& rect)
{
if (m_windowCtx)
m_ctx->resizeSwapChain(*m_windowCtx, m_surface, m_format, m_colorspace);
}
void _setCallback(IWindowCallback* cb)
{
m_callback = cb;
}
EGraphicsAPI getAPI() const
{
return m_api;
}
EPixelFormat getPixelFormat() const
{
return m_pf;
}
void setPixelFormat(EPixelFormat pf)
{
if (pf > EPixelFormat::RGBAF32_Z24)
return;
m_pf = pf;
}
void initializeContext(void* getVkProc)
{
vk::init_dispatch_table_top(PFN_vkGetInstanceProcAddr(getVkProc));
if (m_ctx->m_instance == VK_NULL_HANDLE)
{
const SystemString& appName = APP->getUniqueName();
int len = WideCharToMultiByte(CP_UTF8, 0, appName.c_str(), appName.size(), nullptr, 0, nullptr, nullptr);
std::string utf8(len, '\0');
WideCharToMultiByte(CP_UTF8, 0, appName.c_str(), appName.size(), &utf8[0], len, nullptr, nullptr);
m_ctx->initVulkan(utf8.c_str());
}
vk::init_dispatch_table_middle(m_ctx->m_instance, false);
m_ctx->enumerateDevices();
m_windowCtx =
m_ctx->m_windows.emplace(std::make_pair(m_parentWindow,
std::make_unique<VulkanContext::Window>())).first->second.get();
VkWin32SurfaceCreateInfoKHR surfaceInfo = {};
surfaceInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
surfaceInfo.hinstance = m_appInstance;
surfaceInfo.hwnd = m_hwnd;
ThrowIfFailed(vk::CreateWin32SurfaceKHR(m_ctx->m_instance, &surfaceInfo, nullptr, &m_surface));
/* Iterate over each queue to learn whether it supports presenting */
VkBool32 *supportsPresent = (VkBool32*)malloc(m_ctx->m_queueCount * sizeof(VkBool32));
for (uint32_t i=0 ; i<m_ctx->m_queueCount ; ++i)
vk::GetPhysicalDeviceSurfaceSupportKHR(m_ctx->m_gpus[0], i, m_surface, &supportsPresent[i]);
/* Search for a graphics queue and a present queue in the array of queue
* families, try to find one that supports both */
if (m_ctx->m_graphicsQueueFamilyIndex == UINT32_MAX)
{
/* First window, init device */
for (uint32_t i=0 ; i<m_ctx->m_queueCount; ++i)
{
if ((m_ctx->m_queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
{
if (supportsPresent[i] == VK_TRUE)
{
m_ctx->m_graphicsQueueFamilyIndex = i;
}
}
}
/* Generate error if could not find a queue that supports both a graphics
* and present */
if (m_ctx->m_graphicsQueueFamilyIndex == UINT32_MAX)
Log.report(logvisor::Fatal,
"Could not find a queue that supports both graphics and present");
m_ctx->initDevice();
}
else
{
/* Subsequent window, verify present */
if (supportsPresent[m_ctx->m_graphicsQueueFamilyIndex] == VK_FALSE)
Log.report(logvisor::Fatal, "subsequent surface doesn't support present");
}
free(supportsPresent);
vk::init_dispatch_table_bottom(m_ctx->m_instance, m_ctx->m_dev);
if (!vk::GetPhysicalDeviceWin32PresentationSupportKHR(m_ctx->m_gpus[0], m_ctx->m_graphicsQueueFamilyIndex))
{
Log.report(logvisor::Fatal, "Win32 doesn't support vulkan present");
return;
}
/* Get the list of VkFormats that are supported */
uint32_t formatCount;
ThrowIfFailed(vk::GetPhysicalDeviceSurfaceFormatsKHR(m_ctx->m_gpus[0], m_surface, &formatCount, nullptr));
VkSurfaceFormatKHR* surfFormats = (VkSurfaceFormatKHR*)malloc(formatCount * sizeof(VkSurfaceFormatKHR));
ThrowIfFailed(vk::GetPhysicalDeviceSurfaceFormatsKHR(m_ctx->m_gpus[0], m_surface, &formatCount, surfFormats));
/* If the format list includes just one entry of VK_FORMAT_UNDEFINED,
* the surface has no preferred format. Otherwise, at least one
* supported format will be returned. */
if (formatCount >= 1)
{
if (surfFormats[0].format == VK_FORMAT_UNDEFINED)
m_format = VK_FORMAT_B8G8R8A8_UNORM;
else
m_format = surfFormats[0].format;
m_colorspace = surfFormats[0].colorSpace;
}
else
Log.report(logvisor::Fatal, "no surface formats available for Vulkan swapchain");
m_ctx->initSwapChain(*m_windowCtx, m_surface, m_format, m_colorspace);
m_dataFactory = new class VulkanDataFactory(this, m_ctx, m_sampleCount);
m_commandQueue = _NewVulkanCommandQueue(m_ctx, m_ctx->m_windows[m_parentWindow].get(), this);
}
void makeCurrent() {}
void postInit() {}
IGraphicsCommandQueue* getCommandQueue()
{
return m_commandQueue;
}
IGraphicsDataFactory* getDataFactory()
{
return m_dataFactory;
}
IGraphicsDataFactory* getMainContextDataFactory()
{
return getDataFactory();
}
IGraphicsDataFactory* getLoadContextDataFactory()
{
return getDataFactory();
}
void present() {}
};
#endif
static void genFrameDefault(MONITORINFO* screen, int& xOut, int& yOut, int& wOut, int& hOut)
{
float width = screen->rcMonitor.right * 2.0 / 3.0;
@ -709,11 +952,12 @@ class WindowWin32 : public IWindow
public:
WindowWin32(const SystemString& title, Boo3DAppContext& b3dCtx, uint32_t sampleCount)
WindowWin32(const SystemString& title, Boo3DAppContext& b3dCtx, void* vulkanHandle, uint32_t sampleCount)
{
m_hwnd = CreateWindowW(L"BooWindow", title.c_str(), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, NULL, NULL);
HINSTANCE wndInstance = HINSTANCE(GetWindowLongPtr(m_hwnd, GWLP_HINSTANCE));
m_imc = ImmGetContext(m_hwnd);
IGraphicsContext::EGraphicsAPI api = IGraphicsContext::EGraphicsAPI::D3D11;
#if _WIN32_WINNT_WIN10
@ -726,6 +970,13 @@ public:
this, m_hwnd, b3dCtx, sampleCount));
return;
}
else if (b3dCtx.m_vulkanDxFactory)
{
m_gfxCtx.reset(new GraphicsContextWin32Vulkan(this, wndInstance, m_hwnd, &g_VulkanContext,
b3dCtx, sampleCount));
m_gfxCtx->initializeContext(vulkanHandle);
return;
}
m_gfxCtx.reset(new GraphicsContextWin32D3D(api, this, m_hwnd, b3dCtx, sampleCount));
}
@ -1291,9 +1542,10 @@ public:
};
IWindow* _WindowWin32New(const SystemString& title, Boo3DAppContext& d3dCtx, uint32_t sampleCount)
IWindow* _WindowWin32New(const SystemString& title, Boo3DAppContext& d3dCtx,
void* vulkanHandle, uint32_t sampleCount)
{
return new WindowWin32(title, d3dCtx, sampleCount);
return new WindowWin32(title, d3dCtx, vulkanHandle, sampleCount);
}
}