Vulkan学习苦旅06:创建渲染通道(VkRenderPass)
对于一个复杂的图形应用程序,需要多个过程的配合,以生成图像的各个部分。通常,各个过程间存在着依赖关系,例如某个过程生成的图像(输出)被另一个过程使用(作为此过程的输入)。在Vulkan中,每个过程被称为一个子通道(subpass), 所有的子通道构成了一个渲染通道(VkRenderPass
).
在这篇博客中,我们将定义函数createRenderPass
用于创建一个渲染通道,且渲染通道中仅包含一个子通道。
1. 子通道的信息
在创建渲染通道前,我们需要每个子通道的信息,子通道的信息通过结构体VkSubpassDescription
描述,此结构体的定义如下:
typedef struct VkSubpassDescription {
VkSubpassDescriptionFlags flags;
VkPipelineBindPoint pipelineBindPoint;
uint32_t inputAttachmentCount;
const VkAttachmentReference* pInputAttachments;
uint32_t colorAttachmentCount;
const VkAttachmentReference* pColorAttachments;
const VkAttachmentReference* pResolveAttachments;
const VkAttachmentReference* pDepthStencilAttachment;
uint32_t preserveAttachmentCount;
const uint32_t* pPreserveAttachments;
} VkSubpassDescription;
什么是附件(Attachment)?
在上面的结构体中,多次出现attachment一词,这个单词的意思是附件。什么是附件呢?顾名思义,附件就是附加的资源。在渲染一副图像时,我们还需要一些额外的资源,例如深度缓冲、模板缓冲等等。(这些之后还会详细介绍)
打个可能不是太恰当的比方:在考试时,我们的任务是在试卷上(渲染的目标)作答,而打草稿用的草稿纸就起到了附件的作用。
此结构体中,
pipelineBindPoint
: 目前Vulkan仅支持图形渲染通道,因此只有一个取值VK_PIPELINE_BIND_POINT_GRAPHICS
;
inputAttachmentCount, pInputAttachments
: 输入附件,子通道从输入附件中读取数据;
colorAttachmentCount, pColorAttachments
: 颜色附件,子通道向此附件写入输出;
pResolveAttachments
: 解析附件,暂时不用管它;
pDepthStencilAttachment
: 深度-模板缓冲。在图形学API中,深度缓冲与模板缓冲紧密联系在一起,一个子通道只需要一个深度缓冲和一个模板缓冲(所以此成员名没有使用复数形式,当你知道它们是做什么的,自然就明白了为什么只需要一个即可)。画一个三角形不需要设置它们,因此暂时不用管它;
preserveAttachmentCount, pPreserveAttachments
: 暂时不用管它们。
目前,我们只在此结构体的颜色附件填充有效的信息(即colorAttachmentCount
和pColorAttachments
),但在此之前,还需要搞清楚pColorAttachments
指向的结构体VkAttachmentReferences
是什么,这个结构体的定义如下:
typedef struct VkAttachmentReference {
uint32_t attachment;
VkImageLayout layout;
} VkAttachmentReference;
其中,attachment
是附件的索引,是什么的索引呢?这个我们待会再说,暂时将其甚至为0; layout
表示子通道中附件的图像布局,这里暂时设置为VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
. 即:
VkAttachmentReference colorAttachmentReference{
0, // .attachment, 上述attachment description的索引
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // .layout
};
之后,我们就可以填充描述子通道的结构体:
VkSubpassDescription subpassDescription{
0, // .flags
VK_PIPELINE_BIND_POINT_GRAPHICS, // .pipelineBindPoint
0, // .inputAttachmentCount
nullptr, // .pInputAttachments
1, // .colorAttachmentCount
&colorAttachmentReference, // .pColorAttachments
nullptr, // .pResolveAttachments
nullptr, // .pDepthStencilAttachment
0, // .preserveAttachmentCount
nullptr, // .pPreserveAttachments
};
可以看到,除了一个颜色附件外,基本上没有填入什么有效的信息。
2. 创建渲染通道
创建渲染通道的结构体为VkRenderPassCreateInfo
, 其定义如下:
typedef struct VkRenderPassCreateInfo {
VkStructureType sType;
const void* pNext;
VkRenderPassCreateFlags flags;
uint32_t attachmentCount;
const VkAttachmentDescription* pAttachments;
uint32_t subpassCount;
const VkSubpassDescription* pSubpasses;
uint32_t dependencyCount;
const VkSubpassDependency* pDependencies;
} VkRenderPassCreateInfo;
可以看到,此结构体的关键在于传递三个数组:
attachmentCount, pAttachments
: 数组VkAttachmentDescription[]
;
subpassCount, pSubpasses
: 数组VkSubpassDescription[]
. 此数组正是上一节我们定义的子通道信息数组;
dependencyCount, pDependencies
: 数组VkSubpassDependency[]
.
2.1. 对附件的操作
VkAttachmentDescription[]
数组描述了对附件的操作,上一节中提到的索引正是用于索引这个数组。由于我们只定义了一个附件(颜色附件),所以只要设置如何操作此附件即可。结构体VkAttachmentDescription
的定义如下:
typedef struct VkAttachmentDescription {
VkAttachmentDescriptionFlags flags;
VkFormat format;
VkSampleCountFlagBits samples;
VkAttachmentLoadOp loadOp;
VkAttachmentStoreOp storeOp;
VkAttachmentLoadOp stencilLoadOp;
VkAttachmentStoreOp stencilStoreOp;
VkImageLayout initialLayout;
VkImageLayout finalLayout;
} VkAttachmentDescription;
其中,
format
:用于指定附件的格式,之前在创建交换链时我们指定了格式,稍后我们将修改代码,将格式作为VulkanApp类的一个成员;
samples
: 如果不使用多重采样,就可以设置为VK_SAMPLE_COUNT_1_BIT
;
loadOp, stencilLoadOp
: 在渲染通道开始时如何处理附件,如果附件是深度-模板缓冲,stencilLoadOp
会指定处理模板缓冲的方式。可选的值有:VK_ATTACHMENT_LOAD_OP_LOAD
(附件中已经保存了有效数据,继续对其操作)、VK_ATTACHMENT_LOAD_OP_CLEAR
(清空附件的内容)、VK_ATTACHMENT_LOAD_OP_DONT_CARE
(不关心附件里有什么);
storeOp, stencilStoreOp
: 在渲染通道结束时如何处理附件,如果附件是深度-模板缓冲,stencilStoreOp
会指定处理模板缓冲的方式。可选的值有:VK_ATTACHMENT_STORE_OP_STORE
(将附件写入内存)、VK_ATTACHMENT_STORE_OP_DONT_CARE
(不需要附件的内容);
initialLayout, finalLayout
: 渲染通道开始与结束时图像的布局,暂时不用管它们。
首先,在VulkanApp类中添加成员表示图像的格式:
VkFormat mSwapChainImageFormat; // 交换链的图像格式
之后,将交换链中选择的图像格式保存到上述成员中:
/* 获取物理设备对图像格式的支持 */
uint32_t formatCount = 0;
vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, mSurface, &formatCount, nullptr);
vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, mSurface, &formatCount, surfaceFormats.data());
mSwapChainImageFormat = surfaceFormats[0].format; // 函数createSwapChain中,选择的图像格式
这样,我们就可以填充结构体VkAttachmentDescription
:
VkAttachmentDescription colorAttachmentDescription{
0, // .flags
mSwapChainImageFormat, // .format
VK_SAMPLE_COUNT_1_BIT, // .samples
VK_ATTACHMENT_LOAD_OP_CLEAR, // .loadOp
VK_ATTACHMENT_STORE_OP_STORE, // .storeOp
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // .stencilLoadOp
VK_ATTACHMENT_STORE_OP_DONT_CARE, // .stencilStoreOp
VK_IMAGE_LAYOUT_UNDEFINED, // .initialLayout
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // .finalLayout
};
2.2. 子通道的依赖关系
子通道间的依赖关系通过结构体VkSubpassDependency
描述:
typedef struct VkSubpassDependency {
uint32_t srcSubpass;
uint32_t dstSubpass;
VkPipelineStageFlags srcStageMask;
VkPipelineStageFlags dstStageMask;
VkAccessFlags srcAccessMask;
VkAccessFlags dstAccessMask;
VkDependencyFlags dependencyFlags;
} VkSubpassDependency;
其中,
srcSubpass, dstSubpass
:渲染通道中,子通道数组的索引;
srcStageMask, dstStageMask
: 分别指定源子通道与目标子通道的哪些管线将使用数据;
srcAccessMask, dstAccessMask
: 分别指定源子通道与目标子通道如何访问数据。
按照如下方式填充结构体:
VkSubpassDependency subpassDependency{
VK_SUBPASS_EXTERNAL, // .srcSubpass
0, // .dstSubpass
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // .srcStageMask
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // .dstStageMask
0, // .srcAccessMask
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // .dstAccessMask
0, // dependencyFlags
};
2.3. 创建渲染通道
现在,就可以填充结构体VkRenderPassCreateInfo
了:
VkRenderPassCreateInfo renderPassCreateInfo{
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
nullptr,
0,
1, // .attachmentCount
&colorAttachmentDescription, // .pAttachments
1, // .subpassCount
&subpassDescription, // .pSubpasses
1, // dependencyCount
&subpassDependency, // .pDependencies
};
在VulkanApp类中定义成员mRenderPass
表示渲染通道:
VkRenderPass mRenderPass; // 渲染管线
最后,使用函数vkCreateRenderPass
创建渲染通道:
if_fail(
vkCreateRenderPass(mDevice, &renderPassCreateInfo, nullptr, &mRenderPass),
"failed to create render pass!"
);
Log("create render pass successfully");
不要忘记在析构函数中销毁渲染通道:
vkDestroyRenderPass(mDevice, mRenderPass, nullptr);
3. 到目前为止的完整代码
这一节中,出现了许多全新的概念,有的可能还没搞清楚,有的目前并没有那么重要。在下一篇文章中,我们将介绍整个图形管线(VkPipeline
)的创建,随着更加深入地学习,对于这些概念的认识也会更加清晰。
到目前为止的完整代码
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <iostream>
#include <vector>
using std::vector;
#include <cstring>
#define Log(message) std::cout << "[INFO] " << message << std::endl
#define Error(message) std::cerr << "[ERROR] " << message << std::endl; exit(-1)
static void if_fail(VkResult result, const char* message);
class VulkanApp {
public:
VulkanApp() {
glfwInit(); // 初始化glfw库
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // 禁用OpenGL相关的API
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // 禁止调整窗口大小
createInstance();
createSurface();
selectPhysicalDevice();
createDevice();
createSwapChain();
createRenderPass();
}
~VulkanApp() {
vkDestroyRenderPass(mDevice, mRenderPass, nullptr);
for (auto swapChainImageView : mSwapChainImageViews) {
vkDestroyImageView(mDevice, swapChainImageView, nullptr);
}
vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr);
vkDestroyDevice(mDevice, nullptr);
vkDestroySurfaceKHR(mInstance, mSurface, nullptr);
vkDestroyInstance(mInstance, nullptr);
glfwDestroyWindow(mWindow);
glfwTerminate();
}
void Run() {
while (!glfwWindowShouldClose(mWindow)) {
glfwPollEvents();
}
}
private:
const vector<const char*> mRequiredLayers = {
"VK_LAYER_KHRONOS_validation"
};
const vector<const char*> mRequiredExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME, // 等价于字符串"VK_KHR_swapchain"
};
VkInstance mInstance; // 实例
VkPhysicalDevice mPhysicalDevice; // 物理设备
int mGraphicsQueueFamilyIndex = -1; // 支持图形功能的队列族索引
int mPresentQueueFamilyIndex = -1; // 支持显示功能的队列族索引
int mWidth = 800; // 窗口宽度
int mHeight = 600; // 窗口高度
GLFWwindow* mWindow = nullptr; // glfw窗口指针
VkSurfaceKHR mSurface; // 表面
VkSwapchainKHR mSwapChain; // 交换链
vector<VkImage> mSwapChainImages; // 交换链的图像
vector<VkImageView> mSwapChainImageViews; // 交换链的图像视图
VkFormat mSwapChainImageFormat; // 交换链的图像格式
VkDevice mDevice; // (逻辑)设备
VkQueue mGraphicsQueue; // 支持图形的队列
VkQueue mPresentQueue; // 支持显示的队列
VkRenderPass mRenderPass; // 渲染管线
void createInstance() {
/* 填充VkApplicationInfo结构体 */
VkApplicationInfo appInfo{
VK_STRUCTURE_TYPE_APPLICATION_INFO, // .sType
nullptr, // .pNext
"I don't care", // .pApplicationName
VK_MAKE_VERSION(1, 0, 0), // .applicationVersion
"I don't care", // .pEngineName
VK_MAKE_VERSION(1, 0, 0), // .engineVersion
VK_API_VERSION_1_0, // .apiVersion
};
/* 获取glfw要求支持的扩展 */
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
/* 输出glfw所需的扩展 */
std::cout << "[INFO] glfw needs the following extensions:\n";
for (int i = 0; i < glfwExtensionCount; i++) {
std::cout << " " << glfwExtensions[i] << std::endl;
}
/* 填充VkInstanceCreateInfo结构体 */
VkInstanceCreateInfo instanceCreateInfo{
VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // .sType
nullptr, // .pNext
0, // .flags
&appInfo, // .pApplicationInfo
mRequiredLayers.size(), // .enabledLayerCount
mRequiredLayers.data(), // .ppEnabledLayerNames
glfwExtensionCount, // .enabledExtensioncount
glfwExtensions, // .ppEnabledExtensionNames
};
/* 如果创建实例失败,终止程序 */
if_fail(
vkCreateInstance(&instanceCreateInfo, nullptr, &mInstance),
"failed to create instance"
);
}
void createSurface() {
mWindow = glfwCreateWindow(mWidth, mHeight, "Vulkan App", nullptr, nullptr); // 创建glfw窗口
if (mWindow == nullptr) {
std::cerr << "failed to create window\n";
exit(-1);
}
/* 创建VkSurfaceKHR对象 */
if_fail(
glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface),
"failed to create surface"
);
}
void selectPhysicalDevice() {
/* 查找所有可选的物理设备 */
uint32_t physicalDeviceCount = 0;
vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, nullptr);
vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, physicalDevices.data());
mPhysicalDevice = VK_NULL_HANDLE;
for (VkPhysicalDevice physicalDevice : physicalDevices) {
/* 1. 检查物理设备是否支持扩展 */
/* 获取物理设备支持的扩展信息 */
uint32_t extensionCount = 0;
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr);
vector<VkExtensionProperties> availableExtensions(extensionCount);
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, availableExtensions.data());
bool isAllRequiredExtensionsSupported = true; // 检查此物理设备是否支持所有的扩展
for (const char* requiredExtensionName : mRequiredExtensions) {
bool isSupported = false;
for (const auto& availableExtension : availableExtensions) {
if (strcmp(requiredExtensionName, availableExtension.extensionName) == 0) {
isSupported = true;
break;
}
}
if (isSupported == false) {
isAllRequiredExtensionsSupported = false;
break;
}
}
if (isAllRequiredExtensionsSupported) {
Log("all required extensions are supported");
}
else {
continue;
}
/* 2. 检查物理设备是否支持几何着色器 */
VkPhysicalDeviceFeatures physicalDeviceFeatures;
vkGetPhysicalDeviceFeatures(physicalDevice, &physicalDeviceFeatures);
if (physicalDeviceFeatures.geometryShader) {
Log("geometry shader is supported");
}
else {
continue;
}
/* 获取队列族的信息 */
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);
vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data());
for (int i = 0; i < queueFamilyCount; i++) {
/* 5.3. 检查是否支持图形功能 */
if (mGraphicsQueueFamilyIndex < 0 && (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) {
Log("find graphics queue family index " << i);
mGraphicsQueueFamilyIndex = i; // 保留队列族的索引
}
/* 5.4. 检查是否支持显示功能 */
if (mPresentQueueFamilyIndex < 0) {
VkBool32 isPresentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, mSurface, &isPresentSupport);
if (isPresentSupport) {
mPresentQueueFamilyIndex = i;
Log("find present queue family index " << i);
}
else {
Log("present is not supported");
}
}
}
if (mGraphicsQueueFamilyIndex >= 0 && mPresentQueueFamilyIndex >= 0) {
mPhysicalDevice = physicalDevice;
/* 获取物理设备的属性 */
VkPhysicalDeviceProperties physicalDeviceProperties;
vkGetPhysicalDeviceProperties(mPhysicalDevice, &physicalDeviceProperties);
Log("select physical device: " << physicalDeviceProperties.deviceName);
}
}
/* 如果没找到合适的物理设备 */
if (mPhysicalDevice == VK_NULL_HANDLE) {
Error("can't find suitable physical device");
}
}
void createDevice() {
/* 填充VkDeviceQueueCreateInfo结构体 */
vector<VkDeviceQueueCreateInfo> deviceQueueCreateInfos;
float queuePriority = 1.0f; // 必须指定优先级,如果pQueuePriorities设置为nullptr会报错
VkDeviceQueueCreateInfo deviceGraphicsQueueCreateInfo{
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // .sType
nullptr, // .pNext
0, // .flags
mGraphicsQueueFamilyIndex, // .queueFamilyIndex
1, // .queueCount
&queuePriority, // .pQueuePriorities
};
deviceQueueCreateInfos.push_back(deviceGraphicsQueueCreateInfo);
if (mPresentQueueFamilyIndex != mGraphicsQueueFamilyIndex) {
VkDeviceQueueCreateInfo devicePresentQueueCreateInfo{
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // .sType
nullptr, // .pNext
0, // .flags
mPresentQueueFamilyIndex, // .queueFamilyIndex
1, // .queueCount
&queuePriority, // .pQueuePriorities
};
deviceQueueCreateInfos.push_back(devicePresentQueueCreateInfo);
}
/* 填充VkDeviceCreateInfo结构体 */
VkDeviceCreateInfo deviceCreateInfo{
VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // .sType
nullptr, // .pNext
0, // .flags
deviceQueueCreateInfos.size(), // .queueCreateInfoCount
deviceQueueCreateInfos.data(), // .pQueueCreateInfos
mRequiredLayers.size(), // .enabledLayerCount
mRequiredLayers.data(), // .ppEnabledLayerNames
mRequiredExtensions.size(), // .enabledExtensionCount
mRequiredExtensions.data(), // .ppEnabledExtensionNames
nullptr, // .pEnabledFeatureks
};
if_fail(
vkCreateDevice(mPhysicalDevice, &deviceCreateInfo, nullptr, &mDevice),
"failed to create device!"
);
Log("create device successfully");
vkGetDeviceQueue(mDevice, mGraphicsQueueFamilyIndex, 0, &mGraphicsQueue);
vkGetDeviceQueue(mDevice, mPresentQueueFamilyIndex, 0, &mPresentQueue);
}
void createSwapChain() {
/* 获取物理设备对图像大小、数量的支持 */
VkSurfaceCapabilitiesKHR surfaceCapabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDevice, mSurface, &surfaceCapabilities);
/* 获取物理设备对图像格式的支持 */
uint32_t formatCount = 0;
vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, mSurface, &formatCount, nullptr);
vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, mSurface, &formatCount, surfaceFormats.data());
mSwapChainImageFormat = surfaceFormats[0].format;
/* 填充交换链结构体 */
int imageCount = surfaceCapabilities.minImageCount + 1 <= surfaceCapabilities.maxImageCount ?
surfaceCapabilities.minImageCount + 1 : surfaceCapabilities.maxImageCount; // 设置图像的数量
VkSwapchainCreateInfoKHR swapchainCreateInfo{
VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, // .sType
nullptr, // .pNext
0, // .flags
mSurface, // .surface
imageCount, // .minImageCount
surfaceFormats[0].format, // .imageFormat
surfaceFormats[0].colorSpace, // .imageColorSpace
surfaceCapabilities.currentExtent, // imageExtent
1, // .imageArrayLayers
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, // .imageUsage
VK_SHARING_MODE_EXCLUSIVE, // .imageSharingMode
0, // .queueFamilyIndexCount
nullptr, // .pQueueFamilyIndices
surfaceCapabilities.currentTransform, // .preTransform
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, // .compositeAlpha
VK_PRESENT_MODE_FIFO_KHR, // .presentMode
VK_TRUE, // .clipped
VK_NULL_HANDLE, // .oldSwapChain
};
/* 如果图形队列和展示队列不是同一个队列 */
if (mGraphicsQueueFamilyIndex != mPresentQueueFamilyIndex) {
swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
swapchainCreateInfo.queueFamilyIndexCount = 2;
uint32_t queueFamilyIndices[] = {
mGraphicsQueueFamilyIndex, mPresentQueueFamilyIndex
};
swapchainCreateInfo.pQueueFamilyIndices = queueFamilyIndices;
}
/* 创建交换链 */
if_fail(
vkCreateSwapchainKHR(mDevice, &swapchainCreateInfo, nullptr, &mSwapChain),
"failed to create swapchain!"
);
Log("create swapchain successfully");
/* 获取交换链的图像 */
uint32_t swapChainImagesCount = 0;
vkGetSwapchainImagesKHR(mDevice, mSwapChain, &swapChainImagesCount, nullptr);
mSwapChainImages.resize(swapChainImagesCount);
vkGetSwapchainImagesKHR(mDevice, mSwapChain, &swapChainImagesCount, mSwapChainImages.data());
/* 为每张图像创建对应的图像视图 */
mSwapChainImageViews.resize(mSwapChainImages.size());
for (size_t i = 0; i < mSwapChainImages.size(); i++) {
VkImageViewCreateInfo imageViewCreateInfo{
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // .sType
nullptr, // .pNext
0, // .flags
mSwapChainImages[i], // .image
VK_IMAGE_VIEW_TYPE_2D, // .viewType
surfaceFormats[0].format, // .format
{
VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY,
}, // .components
{
VK_IMAGE_ASPECT_COLOR_BIT, // .aspectMask
0, // .baseMipLevel
1, // .levelCount
0, // .baseArrayLayer
1, // .layerCount
}, // .subresourceRange
};
if_fail(
vkCreateImageView(mDevice, &imageViewCreateInfo, nullptr, &mSwapChainImageViews[i]),
"failed to create image views!"
);
}
}
void createRenderPass() {
VkAttachmentReference colorAttachmentReference{
0, // .attachment, 上述attachment description的索引
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // .layout
};
VkSubpassDescription subpassDescription{
0, // .flags
VK_PIPELINE_BIND_POINT_GRAPHICS, // .pipelineBindPoint
0, // .inputAttachmentCount
nullptr, // .pInputAttachments
1, // .colorAttachmentCount
&colorAttachmentReference, // .pColorAttachments
nullptr, // .pResolveAttachments
nullptr, // .pDepthStencilAttachment
0, // .preserveAttachmentCount
nullptr, // .pPreserveAttachments
};
VkAttachmentDescription colorAttachmentDescription{
0, // .flags
mSwapChainImageFormat, // .format
VK_SAMPLE_COUNT_1_BIT, // .samples
VK_ATTACHMENT_LOAD_OP_CLEAR, // .loadOp
VK_ATTACHMENT_STORE_OP_STORE, // .storeOp
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // .stencilLoadOp
VK_ATTACHMENT_STORE_OP_DONT_CARE, // .stencilStoreOp
VK_IMAGE_LAYOUT_UNDEFINED, // .initialLayout
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // .finalLayout
};
VkSubpassDependency subpassDependency{
VK_SUBPASS_EXTERNAL, // .srcSubpass
0, // .dstSubpass
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // .srcStageMask
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // .dstStageMask
0, // .srcAccessMask
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // .dstAccessMask
0, // dependencyFlags
};
VkRenderPassCreateInfo renderPassCreateInfo{
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
nullptr,
0,
1, // .attachmentCount
&colorAttachmentDescription, // .pAttachments
1, // .subpassCount
&subpassDescription, // .pSubpasses
1, // dependencyCount
&subpassDependency, // .pDependencies
};
if_fail(
vkCreateRenderPass(mDevice, &renderPassCreateInfo, nullptr, &mRenderPass),
"failed to create render pass!"
);
Log("create render pass successfully");
}
};
int main() {
VulkanApp app;
app.Run();
}
static void if_fail(VkResult result, const char* message) {
if (result != VK_SUCCESS) {
std::cerr << "[error] " << message << std::endl;
exit(-1);
}
}
Vulkan学习苦旅06:创建渲染通道(VkRenderPass)的更多相关文章
- springmvc学习笔记---idea创建springmvc项目
前言: 真的是很久没搞java的web服务开发了, 最近一次搞还是读研的时候, 想来感慨万千. 英雄没落, Eclipse的盟主地位隐隐然有被IntelliJ IDEA超越的趋势. Spring从2. ...
- DirectX11 学习笔记3 - 创建一个立方体 和 轴
该方案将在进一步的程序 面向对象. 独立的模型类.更像是一个框架. 其中以超过遇到了一个非常有趣的问题,.获得一晚.我读了好几遍,以找到其他的列子.必须放在某些功能Render里面实时更新,而不是仅仅 ...
- 机器学习实战(Machine Learning in Action)学习笔记————06.k-均值聚类算法(kMeans)学习笔记
机器学习实战(Machine Learning in Action)学习笔记————06.k-均值聚类算法(kMeans)学习笔记 关键字:k-均值.kMeans.聚类.非监督学习作者:米仓山下时间: ...
- 关于Unity中混合模式、Alpha测试、深度测试、通道遮罩、面剔除的使用----渲染通道通用指令(二)
混合模式 着色完成后,需要把颜色混合到帧缓冲区里面,涉及到源和目标. 1:在所有计算完成后,决定当前的计算结果输出到帧缓冲区时,如何混合源和目标,通常用来绘制半透明的物体;2: Blend Off 关 ...
- iOS学习笔记06—Category和Extension
iOS学习笔记06—Category和Extension 一.概述 类别是一种为现有的类添加新方法的方式. 利用Objective-C的动态运行时分配机制,Category提供了一种比继承(inher ...
- Java学习笔记-多线程-创建线程的方式
创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...
- Netty学习之客户端创建
一.客户端开发时序图 图片来源:Netty权威指南(第2版) 二.Netty客户端开发步骤 使用Netty进行客户端开发主要有以下几个步骤: 1.用户线程创建Bootstrap Bootstrap b ...
- ASP.NET MVC 5 学习教程:创建连接字符串
原文 ASP.NET MVC 5 学习教程:创建连接字符串 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 ...
- 跟着刚哥学习Spring框架--创建HelloWorld项目(一)
1.Spring框架简介 Spring是一个开源框架,Spring是在2003年兴起的一个轻量级的开源框架,由Rod johnson创建.主要对JavaBean的生命周期进行管理的轻量级框架,Spri ...
- angular学习笔记(二)-创建angular模块
如果在页面的html标签(或任意标签)中添加ng-app,表示对整个页面应用angular来管理. 他是一个模块. 模块有助于把东西从全局命名空间中隔离. 今天学习如何自定义创建模块: <!DO ...
随机推荐
- vue setup响应式变量
setup响应式变量 一.非响应式变量 1 效果 开发中发现setup()中的变量居然不是响应式的,值得内容变成1了但是页面上还是0 2.源码 二.响应式变量 1.效果 使用ref()可以声明响应式的 ...
- 微前端qiankun
- java基础-java面向对象-02-day09
目录 1. 封装 2. 继承 2.1 什么是方法的重写 2.2 super 2.3 object详解 2.4 equals方法 3. 多态 4. final修饰符 5.抽象类 6. 接口 7. 内部类 ...
- 供应链投毒预警 | 恶意Py包仿冒tensorflow AI框架实施后门投毒攻击
概述 本周(2024年01月15号),悬镜供应链安全实验室在Pypi官方仓库(https://pypi.org/)中捕获1起Py包投毒事件,投毒者利用包名错误拼写(typo-squatting)的攻击 ...
- 02-python简介
python简介 python认知 1.python简介 89年开发的语言,创始人范罗苏姆(Guido van Rossum),别称:龟叔(Guido). python具有非常多并且强大的第三方库,使 ...
- CSS3之transition
随着css3不断地发展,越来越多的页面特效可以被实现. 例如当我们鼠标悬浮在某个tab上的时候,给它以1s的渐进变化增加一个背景颜色.渐进的变化可以让css样式变化得不那么突兀,也显得交互更加柔和. ...
- 【RTOS】基于RTOS的嵌入式系统看门狗策略
RTOS - high integrity systems 看门狗策略 Watchdog Strategies for RTOS enabled embedded systems 介绍 看门狗定时器就 ...
- [转帖]SQL Server索引的维护 - 索引碎片、填充因子
https://www.cnblogs.com/kissdodog/archive/2013/06/14/3135412.html 这两个问题都和页密度有关,虽然两者的表现形式在本质上有所区别,但是故 ...
- [转帖]Nacos使用2.0.1版本启动出现9848端口错误的解决方式(亲测有效)
目录 一.背景 二.报错如下 三.报错原因 四.解决方式 一.背景 nacos服务端和客户端都是 2.x版本. centos7使用原始安装nacos单机版,没有使用docker安装naocs集群. 二 ...
- [转帖]xsos - Summarize system info from sosreports
https://github.com/ryran/rsar I'M LOOKING FOR RSAR SCREEN SHOTS INTRO INSTALLATION EXAMPLES IN ACTIO ...