vulakn教程--Drawing a Triangle--Presentation--Window surface
原文链接: Vulkan-tutorial
Window surface
因为Vulkan是平台(platform)无关的,它不能直接与平台窗体系统(window system)进行通信,为了连接Vulkan和窗体系统,使得被渲染后的结果显示到屏幕上,我们需要使用WSI扩展(Window System Integration extensions),在这个章节我们将使用VK_KHR_surface
,它提供的VkSurfaceKHR
是对surface的一个抽象,使得我们能够将渲染后的结果放到VkSurfaceKHR
上。还记得我们在之前使用GLFW创建的window吗,window将支持VkSurfaceKHR的创建。
VK_KHR_surface
是一个Instance
级别的扩展,我们在创建Instance时已经通过glfwGetRequiredInstanceExtensions
允许了这个扩展。
事实上,window surface的创建应该在Instance
创建之后就应该完成,因为它会影响Physical Device的选取,之所以推迟到现在才讲,是因为window surface是关于渲染目标和显示的(render targets and presentation)一个比较大的话题,它会扰乱你对其他概念的理解。而且你要明白,如果你只是需要off-screen rendering,那么window surface对于Vulkan来说只是一个可选的扩展。
创建VkSurfaceKHR
声明:
1 VDeleter surface{instance, vkDestroySurfaceKHR};
如果我们在Windows上创建VkSurfaceKHR ,我们需要两个句柄: HWND 和HMODULE,并需要VK_KHR_win32_surface扩展,其实我们已经通过glfwGetRequiredInstanceExtensions允许了这个扩展,然后我们需要填充下面这个结构 :
1234 VkWin32SurfaceCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
createInfo.hwnd = glfwGetWin32Window(window);
createInfo.hinstance = GetModuleHandle(nullptr);
然后创建基于windows的surface:
123456 auto CreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) vkGetInstanceProcAddr(instance,
"vkCreateWin32SurfaceKHR"
);
if
(!CreateWin32SurfaceKHR || CreateWin32SurfaceKHR(instance, &createInfo,
nullptr, &surface) != VK_SUCCESS) {
throw
std::runtime_error(
"failed to create window surface!"
);
}
但是我们不会这么犯傻,因为我们用的可是GLFW啊(GLFW是跨平台的),我们没有必要去写一个基于特定平台的代码,而且这简直毫无道理。事实上,GLFW提供了glfwCreateWindowSurface方法,它自动为我们解决平台的差异性。
所以surface的创建应该是这样的 :
12345 void
createSurface() {
if
(glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {
throw
std::runtime_error(
"failed to create window surface!"
);
}
}
就是这么简单优雅!
确定显卡支持WSI(请求显示支持)
尽管Vulkan的实现可能支持WSI ,但并不代表你平台上的所有显卡也支持,它是指Physical Device 中存在一种将images
提交到Surface
上的命令队列。因此我们需要扩展下面这个函数:
12345 bool
isDeviceSuitable(VkPhysicalDevice device) {
QueueFamilyIndices indices = findQueueFamilies(device);
return
indices.isComplete();
}
注:这个函数其实并未修改。
我们修改isDeviceSuitable(…)的目的是确保能够将渲染好的图片(images)提交(present)到我们所创建的surface上。又因为显示(presentation)是基于队列的,那么问题就转换为:从显卡里寻找一种具有将渲染结果提交(presenting)到surface
上的命令的队列(queue family)。
绘画命令和显示命令可能不重叠在一种队列,所以我们需要修改一下结构:
12345678 struct
QueueFamilyIndices {
int
graphicsFamily = -1;
int
presentFamily = -1;
bool
isComplete() {
return
graphicsFamily >= 0 && presentFamily >= 0;
}
};
接下来,为了检测队列是否支持将渲染结果提交(presenting)到surface
上,我们使用:
12345 VkResult vkGetPhysicalDeviceSurfaceSupportKHR(
VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
VkSurfaceKHR surface,
VkBool32* pSupported);
这里不做解释,参数已经见名知意了。
联合以上思想,findQueueFamilies(…) 将变成下面这个样子:
1234567891011121314151617181920212223242526272829 QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
QueueFamilyIndices indices;
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
std::vector queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
int
i = 0;
for
(
const
auto& queueFamily : queueFamilies) {
if
(queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
indices.graphicsFamily = i;
}
VkBool32 presentSupport =
false
;
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
if
(queueFamily.queueCount > 0 && presentSupport) {
indices.presentFamily = i;
}
if
(indices.isComplete()) {
break
;
}
i++;
}
return
indices;
}
注意,我们在创建Logical Device时已经创建了一个队列用于支持图形处理的graphicsQueue
,现在我们变更了需求,又多了一个用于将渲染结果提交(presenting)到surface
上的队列。 那么,Logical Device的创建过程也需要改变。
获取presentQueue
:
VkQueue presentQueue; //声明
1 VkQueue presentQueue;
//声明
createLogicalDevice(…)
的改变部分:
1234567891011121314151617181920212223242526 void
createLogicalDevice() {
...
std::vector queueCreateInfos;
std::
set
<
int
> uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily};
float
queuePriority = 1.0f;
for
(
int
queueFamily : uniqueQueueFamilies) {
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = queueFamily;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.push_back(queueCreateInfo);
}
...
VkDeviceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.pQueueCreateInfos = queueCreateInfos.data();
createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size();
createInfo.pEnabledFeatures = &deviceFeatures;
createInfo.enabledExtensionCount = 0;
...
...
vkGetDeviceQueue(device, indices.presentFamily, 0, &presentQueue);
}
int
>
如果presentFamily
和graphicsFamily
是同一种队列,presentQueue
和graphicsQueue
将指向同一个对象。
注:在我的平台上,它们两个是一个队列。
源码:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 #define GLFW_INCLUDE_VULKAN
#include
#include
#include
#include
#include
#include
#include
const
int
WIDTH = 800;
const
int
HEIGHT = 600;
const
std::vector<
const
char
*=
""
> validationLayers = {
"VK_LAYER_LUNARG_standard_validation"
};
#ifdef NDEBUG
const
bool
enableValidationLayers =
false
;
#else
const
bool
enableValidationLayers =
true
;
#endif
VkResult CreateDebugReportCallbackEXT(VkInstance instance,
const
VkDebugReportCallbackCreateInfoEXT* pCreateInfo,
const
VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) {
auto func = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr(instance,
"vkCreateDebugReportCallbackEXT"
);
if
(func != nullptr) {
return
func(instance, pCreateInfo, pAllocator, pCallback);
}
else
{
return
VK_ERROR_EXTENSION_NOT_PRESENT;
}
}
void
DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback,
const
VkAllocationCallbacks* pAllocator) {
auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance,
"vkDestroyDebugReportCallbackEXT"
);
if
(func != nullptr) {
func(instance, callback, pAllocator);
}
}
template ""
>
class
VDeleter {
public
:
VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {}
VDeleter(std::function<
void
(t, vkallocationcallbacks*)=
""
> deletef) {
this
->deleter = [=](T obj) { deletef(obj, nullptr); };
}
VDeleter(
const
VDeleter& instance, std::function<
void
(vkinstance, t,=
""
vkallocationcallbacks*)=
""
> deletef) {
this
->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); };
}
VDeleter(
const
VDeleter& device, std::function<
void
(vkdevice, t,=
""
vkallocationcallbacks*)=
""
> deletef) {
this
->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); };
}
~VDeleter() {
cleanup();
}
T*
operator
&() {
cleanup();
return
&
object
;
}
operator
T()
const
{
return
object
;
}
private
:
T
object
{VK_NULL_HANDLE};
std::function<
void
(t)> deleter;
void
cleanup() {
if
(
object
!= VK_NULL_HANDLE) {
deleter(
object
);
}
object
= VK_NULL_HANDLE;
}
};
struct
QueueFamilyIndices {
int
graphicsFamily = -1;
int
presentFamily = -1;
bool
isComplete() {
return
graphicsFamily >= 0 && presentFamily >= 0;
}
};
class
HelloTriangleApplication {
public
:
void
run() {
initWindow();
initVulkan();
mainLoop();
}
private
:
GLFWwindow* window;
VDeleter instance{vkDestroyInstance};
VDeleter callback{instance, DestroyDebugReportCallbackEXT};
VDeleter surface{instance, vkDestroySurfaceKHR};
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VDeleter device{vkDestroyDevice};
VkQueue graphicsQueue;
VkQueue presentQueue;
void
initWindow() {
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(WIDTH, HEIGHT,
"Vulkan"
, nullptr, nullptr);
}
void
initVulkan() {
createInstance();
setupDebugCallback();
createSurface();
pickPhysicalDevice();
createLogicalDevice();
}
void
mainLoop() {
while
(!glfwWindowShouldClose(window)) {
glfwPollEvents();
}
}
void
createInstance() {
if
(enableValidationLayers && !checkValidationLayerSupport()) {
throw
std::runtime_error(
"validation layers requested, but not available!"
);
}
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName =
"Hello Triangle"
;
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_0;
VkInstanceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
auto extensions = getRequiredExtensions();
createInfo.enabledExtensionCount = extensions.size();
createInfo.ppEnabledExtensionNames = extensions.data();
if
(enableValidationLayers) {
createInfo.enabledLayerCount = validationLayers.size();
createInfo.ppEnabledLayerNames = validationLayers.data();
}
else
{
createInfo.enabledLayerCount = 0;
}
if
(vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
throw
std::runtime_error(
"failed to create instance!"
);
}
}
void
setupDebugCallback() {
if
(!enableValidationLayers)
return
;
VkDebugReportCallbackCreateInfoEXT createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
createInfo.pfnCallback = debugCallback;
if
(CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) {
throw
std::runtime_error(
"failed to set up debug callback!"
);
}
}
void
createSurface() {
if
(glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {
throw
std::runtime_error(
"failed to create window surface!"
);
}
}
void
pickPhysicalDevice() {
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
if
(deviceCount == 0) {
throw
std::runtime_error(
"failed to find GPUs with Vulkan support!"
);
}
std::vector devices(deviceCount);
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
for
(
const
auto& device : devices) {
if
(isDeviceSuitable(device)) {
physicalDevice = device;
break
;
}
}
if
(physicalDevice == VK_NULL_HANDLE) {
throw
std::runtime_error(
"failed to find a suitable GPU!"
);
}
}
void
createLogicalDevice() {
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
std::vector queueCreateInfos;
std::
set
<
int
> uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily};
float
queuePriority = 1.0f;
for
(
int
queueFamily : uniqueQueueFamilies) {
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = queueFamily;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.push_back(queueCreateInfo);
}
VkPhysicalDeviceFeatures deviceFeatures = {};
VkDeviceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.pQueueCreateInfos = queueCreateInfos.data();
createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size();
createInfo.pEnabledFeatures = &deviceFeatures;
createInfo.enabledExtensionCount = 0;
if
(enableValidationLayers) {
createInfo.enabledLayerCount = validationLayers.size();
createInfo.ppEnabledLayerNames = validationLayers.data();
}
else
{
createInfo.enabledLayerCount = 0;
}
if
(vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
throw
std::runtime_error(
"failed to create logical device!"
);
}
vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue);
vkGetDeviceQueue(device, indices.presentFamily, 0, &presentQueue);
}
bool
isDeviceSuitable(VkPhysicalDevice device) {
QueueFamilyIndices indices = findQueueFamilies(device);
return
indices.isComplete();
}
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
QueueFamilyIndices indices;
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
std::vector queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
int
i = 0;
for
(
const
auto& queueFamily : queueFamilies) {
if
(queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
indices.graphicsFamily = i;
}
VkBool32 presentSupport =
false
;
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
if
(queueFamily.queueCount > 0 && presentSupport) {
indices.presentFamily = i;
}
if
(indices.isComplete()) {
break
;
}
i++;
}
return
indices;
}
std::vector<
const
char
*=
""
> getRequiredExtensions() {
std::vector<
const
char
*=
""
> extensions;
unsigned
int
glfwExtensionCount = 0;
const
char
** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
for
(unsigned
int
i = 0; i < glfwExtensionCount; i++) {
extensions.push_back(glfwExtensions[i]);
}
if
(enableValidationLayers) {
extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
}
return
extensions;
}
bool
checkValidationLayerSupport() {
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
std::vector availableLayers(layerCount);
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
for
(
const
char
* layerName : validationLayers) {
bool
layerFound =
false
;
for
(
const
auto& layerProperties : availableLayers) {
if
(strcmp(layerName, layerProperties.layerName) == 0) {
layerFound =
true
;
break
;
}
}
if
(!layerFound) {
return
false
;
}
}
return
true
;
}
static
VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code,
const
char
* layerPrefix,
const
char
* msg,
void
* userData) {
std::cerr <<
"validation layer: "
<< msg << std::endl;
return
VK_FALSE;
}
};
int
main() {
HelloTriangleApplication app;
try
{
app.run();
}
catch
(
const
std::runtime_error& e) {
std::cerr << e.what() << std::endl;
return
EXIT_FAILURE;
}
return
EXIT_SUCCESS;
}
const
>
const
>
int
>
void
(t)>
void
(vkdevice,>
void
(vkinstance,>
void
(t,>
const
>
set
>