Dynamic window resizing
This commit is contained in:
BIN
VulkanTest
BIN
VulkanTest
Binary file not shown.
@@ -75,25 +75,18 @@ namespace cve {
|
||||
vertexInputInfo.pVertexBindingDescriptions = bindingDescriptions.data();
|
||||
vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewportInfo{};
|
||||
viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
viewportInfo.viewportCount = 1;
|
||||
viewportInfo.pViewports = &configInfo.viewport;
|
||||
viewportInfo.scissorCount = 1;
|
||||
viewportInfo.pScissors = &configInfo.scissor;
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineInfo{};
|
||||
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
pipelineInfo.stageCount = 2;
|
||||
pipelineInfo.pStages = shaderStages;
|
||||
pipelineInfo.pVertexInputState = &vertexInputInfo;
|
||||
pipelineInfo.pInputAssemblyState = &configInfo.inputAssemblyInfo;
|
||||
pipelineInfo.pViewportState = &viewportInfo;
|
||||
pipelineInfo.pViewportState = &configInfo.viewportInfo;
|
||||
pipelineInfo.pRasterizationState = &configInfo.rasterizationInfo;
|
||||
pipelineInfo.pMultisampleState = &configInfo.multisampleInfo;
|
||||
pipelineInfo.pColorBlendState = &configInfo.colorBlendInfo;
|
||||
pipelineInfo.pDepthStencilState = &configInfo.depthStencilInfo;
|
||||
pipelineInfo.pDynamicState = nullptr;
|
||||
pipelineInfo.pDynamicState = &configInfo.dynamicStateInfo;
|
||||
|
||||
pipelineInfo.layout = configInfo.pipelineLayout;
|
||||
pipelineInfo.renderPass = configInfo.renderPass;
|
||||
@@ -123,22 +116,16 @@ namespace cve {
|
||||
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
|
||||
}
|
||||
|
||||
PipelineConfigInfo CvePipeline::defaultPipelineConfigInfo(uint32_t width, uint32_t height) {
|
||||
PipelineConfigInfo configInfo{};
|
||||
|
||||
void CvePipeline::defaultPipelineConfigInfo(PipelineConfigInfo& configInfo) {
|
||||
configInfo.inputAssemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
configInfo.inputAssemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
configInfo.inputAssemblyInfo.primitiveRestartEnable = VK_FALSE;
|
||||
|
||||
configInfo.viewport.x = 0.0f;
|
||||
configInfo.viewport.y = 0.0f;
|
||||
configInfo.viewport.width = static_cast<float>(width);
|
||||
configInfo.viewport.height = static_cast<float>(height);
|
||||
configInfo.viewport.minDepth = 0.0f;
|
||||
configInfo.viewport.maxDepth = 1.0f;
|
||||
|
||||
configInfo.scissor.offset = {0, 0};
|
||||
configInfo.scissor.extent = {width, height};
|
||||
configInfo.viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
configInfo.viewportInfo.viewportCount = 1;
|
||||
configInfo.viewportInfo.pViewports = nullptr;
|
||||
configInfo.viewportInfo.scissorCount = 1;
|
||||
configInfo.viewportInfo.pScissors = nullptr;
|
||||
|
||||
configInfo.rasterizationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
configInfo.rasterizationInfo.depthClampEnable = VK_FALSE;
|
||||
@@ -192,6 +179,11 @@ namespace cve {
|
||||
configInfo.depthStencilInfo.front = {}; // Optional
|
||||
configInfo.depthStencilInfo.back = {}; // Optional
|
||||
|
||||
return configInfo;
|
||||
configInfo.dynamicStateEnables = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
|
||||
configInfo.dynamicStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
configInfo.dynamicStateInfo.pDynamicStates = configInfo.dynamicStateEnables.data();
|
||||
configInfo.dynamicStateInfo.dynamicStateCount =
|
||||
static_cast<uint32_t>(configInfo.dynamicStateEnables.size());
|
||||
configInfo.dynamicStateInfo.flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,18 @@
|
||||
|
||||
namespace cve {
|
||||
struct PipelineConfigInfo {
|
||||
VkViewport viewport;
|
||||
VkRect2D scissor;
|
||||
PipelineConfigInfo(const PipelineConfigInfo&) = delete;
|
||||
PipelineConfigInfo& operator=(const PipelineConfigInfo&) = delete;
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewportInfo;
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo;
|
||||
VkPipelineRasterizationStateCreateInfo rasterizationInfo;
|
||||
VkPipelineMultisampleStateCreateInfo multisampleInfo;
|
||||
VkPipelineColorBlendAttachmentState colorBlendAttachment;
|
||||
VkPipelineColorBlendStateCreateInfo colorBlendInfo;
|
||||
VkPipelineDepthStencilStateCreateInfo depthStencilInfo;
|
||||
std::vector<VkDynamicState> dynamicStateEnables;
|
||||
VkPipelineDynamicStateCreateInfo dynamicStateInfo;
|
||||
VkPipelineLayout pipelineLayout = nullptr;
|
||||
VkRenderPass renderPass = nullptr;
|
||||
uint32_t subpass = 0;
|
||||
@@ -29,7 +33,7 @@ namespace cve {
|
||||
CvePipeline& operator=(const CvePipeline&) = delete;
|
||||
|
||||
void bind(VkCommandBuffer commandBuffer);
|
||||
static PipelineConfigInfo defaultPipelineConfigInfo(uint32_t width, uint32_t height);
|
||||
static void defaultPipelineConfigInfo(PipelineConfigInfo& configInfo);
|
||||
|
||||
private:
|
||||
static std::vector<char> readFile(const std::string& filepath);
|
||||
|
||||
@@ -13,6 +13,18 @@ namespace cve {
|
||||
|
||||
CveSwapChain::CveSwapChain(CveDevice &deviceRef, VkExtent2D extent)
|
||||
: device{deviceRef}, windowExtent{extent} {
|
||||
init();
|
||||
}
|
||||
|
||||
CveSwapChain::CveSwapChain(CveDevice &deviceRef, VkExtent2D extent, std::shared_ptr<CveSwapChain> previous)
|
||||
: device{deviceRef}, windowExtent{extent}, oldSwapChain{previous} {
|
||||
init();
|
||||
|
||||
// Clean up old swap chain
|
||||
oldSwapChain = nullptr;
|
||||
}
|
||||
|
||||
void CveSwapChain::init() {
|
||||
createSwapChain();
|
||||
createImageViews();
|
||||
createRenderPass();
|
||||
@@ -162,7 +174,7 @@ void CveSwapChain::createSwapChain() {
|
||||
createInfo.presentMode = presentMode;
|
||||
createInfo.clipped = VK_TRUE;
|
||||
|
||||
createInfo.oldSwapchain = VK_NULL_HANDLE;
|
||||
createInfo.oldSwapchain = oldSwapChain ? oldSwapChain->swapChain : VK_NULL_HANDLE;
|
||||
|
||||
if (vkCreateSwapchainKHR(device.device(), &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create swap chain!");
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
// std lib headers
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -16,6 +17,7 @@ class CveSwapChain {
|
||||
static constexpr int MAX_FRAMES_IN_FLIGHT = 2;
|
||||
|
||||
CveSwapChain(CveDevice &deviceRef, VkExtent2D windowExtent);
|
||||
CveSwapChain(CveDevice &deviceRef, VkExtent2D windowExtent, std::shared_ptr<CveSwapChain> previous);
|
||||
~CveSwapChain();
|
||||
|
||||
CveSwapChain(const CveSwapChain &) = delete;
|
||||
@@ -39,6 +41,7 @@ class CveSwapChain {
|
||||
VkResult submitCommandBuffers(const VkCommandBuffer *buffers, uint32_t *imageIndex);
|
||||
|
||||
private:
|
||||
void init();
|
||||
void createSwapChain();
|
||||
void createImageViews();
|
||||
void createDepthResources();
|
||||
@@ -69,6 +72,7 @@ class CveSwapChain {
|
||||
VkExtent2D windowExtent;
|
||||
|
||||
VkSwapchainKHR swapChain;
|
||||
std::shared_ptr<CveSwapChain> oldSwapChain;
|
||||
|
||||
std::vector<VkSemaphore> imageAvailableSemaphores;
|
||||
std::vector<VkSemaphore> renderFinishedSemaphores;
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace cve {
|
||||
|
||||
window = glfwCreateWindow(width, height,
|
||||
windowName.c_str(), nullptr, nullptr);
|
||||
glfwSetWindowUserPointer(window, this);
|
||||
glfwSetFramebufferSizeCallback(window, framebufferResizedCallback);
|
||||
}
|
||||
|
||||
void CveWindow::createWindowSurface(VkInstance instance, VkSurfaceKHR *surface) {
|
||||
@@ -27,4 +29,11 @@ namespace cve {
|
||||
throw std::runtime_error("Failed to create window surface");
|
||||
}
|
||||
}
|
||||
|
||||
void CveWindow::framebufferResizedCallback(GLFWwindow *window, int width, int height) {
|
||||
auto cveWindow = reinterpret_cast<CveWindow *>(glfwGetWindowUserPointer(window));
|
||||
cveWindow->framebufferResized = true;
|
||||
cveWindow->width = width;
|
||||
cveWindow->height = height;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,17 @@ class CveWindow {
|
||||
|
||||
bool shouldClose() { return glfwWindowShouldClose(window); };
|
||||
VkExtent2D getExtent() { return { static_cast<uint32_t>(width), static_cast<uint32_t>(height) }; };
|
||||
bool wasWindowResized() { return framebufferResized; };
|
||||
void resetWindowResizedFlag() { framebufferResized = false; };
|
||||
|
||||
void createWindowSurface(VkInstance instance, VkSurfaceKHR *surface);
|
||||
private:
|
||||
static void framebufferResizedCallback(GLFWwindow *window, int width, int height);
|
||||
void initWindow();
|
||||
|
||||
const int width;
|
||||
const int height;
|
||||
int width;
|
||||
int height;
|
||||
bool framebufferResized = false;
|
||||
|
||||
std::string windowName;
|
||||
GLFWwindow *window;
|
||||
|
||||
117
first_app.cpp
117
first_app.cpp
@@ -7,7 +7,7 @@ namespace cve {
|
||||
FirstApp::FirstApp() {
|
||||
loadModels();
|
||||
createPipelineLayout();
|
||||
createPipeline();
|
||||
recreateSwapChain();
|
||||
createCommandBuffers();
|
||||
}
|
||||
|
||||
@@ -48,8 +48,12 @@ namespace cve {
|
||||
}
|
||||
|
||||
void FirstApp::createPipeline() {
|
||||
auto pipelineConfig = CvePipeline::defaultPipelineConfigInfo(cveSwapChain.width(), cveSwapChain.height());
|
||||
pipelineConfig.renderPass = cveSwapChain.getRenderPass();
|
||||
assert(cveSwapChain != nullptr && "Cannot create pipeline before swap chain");
|
||||
assert(pipelineLayout != nullptr && "Cannot create pipeline before pipeline layout");
|
||||
|
||||
PipelineConfigInfo pipelineConfig{};
|
||||
CvePipeline::defaultPipelineConfigInfo(pipelineConfig);
|
||||
pipelineConfig.renderPass = cveSwapChain->getRenderPass();
|
||||
pipelineConfig.pipelineLayout = pipelineLayout;
|
||||
cvePipeline = std::make_unique<CvePipeline>(
|
||||
cveDevice,
|
||||
@@ -59,8 +63,31 @@ namespace cve {
|
||||
);
|
||||
}
|
||||
|
||||
void FirstApp::recreateSwapChain() {
|
||||
auto extent = cveWindow.getExtent();
|
||||
while (extent.width == 0 || extent.height == 0) {
|
||||
extent = cveWindow.getExtent();
|
||||
glfwWaitEvents();
|
||||
}
|
||||
|
||||
vkDeviceWaitIdle(cveDevice.device());
|
||||
|
||||
if (cveSwapChain == nullptr) {
|
||||
cveSwapChain = std::make_unique<CveSwapChain>(cveDevice, extent);
|
||||
} else {
|
||||
cveSwapChain = std::make_unique<CveSwapChain>(cveDevice, extent, std::move(cveSwapChain));
|
||||
if (cveSwapChain->imageCount() != commandBuffers.size()) {
|
||||
freeCommandBuffers();
|
||||
createCommandBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if render pass is compatible
|
||||
createPipeline();
|
||||
}
|
||||
|
||||
void FirstApp::createCommandBuffers() {
|
||||
commandBuffers.resize(cveSwapChain.imageCount());
|
||||
commandBuffers.resize(cveSwapChain->imageCount());
|
||||
|
||||
VkCommandBufferAllocateInfo allocInfo{};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
@@ -72,50 +99,78 @@ namespace cve {
|
||||
VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to allocate command buffers");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i < commandBuffers.size(); i++) {
|
||||
VkCommandBufferBeginInfo beginInfo{};
|
||||
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
void FirstApp::freeCommandBuffers() {
|
||||
vkFreeCommandBuffers(cveDevice.device(), cveDevice.getCommandPool(),
|
||||
static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
|
||||
commandBuffers.clear();
|
||||
}
|
||||
|
||||
if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to begin command buffer recording");
|
||||
}
|
||||
void FirstApp::recordCommandBuffer(int imageIndex) {
|
||||
VkCommandBufferBeginInfo beginInfo{};
|
||||
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
|
||||
VkRenderPassBeginInfo renderPassInfo{};
|
||||
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
renderPassInfo.renderPass = cveSwapChain.getRenderPass();
|
||||
renderPassInfo.framebuffer = cveSwapChain.getFrameBuffer(i);
|
||||
if (vkBeginCommandBuffer(commandBuffers[imageIndex], &beginInfo) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to begin command buffer recording");
|
||||
}
|
||||
|
||||
renderPassInfo.renderArea.offset = {0, 0};
|
||||
renderPassInfo.renderArea.extent = cveSwapChain.getSwapChainExtent();
|
||||
VkRenderPassBeginInfo renderPassInfo{};
|
||||
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
renderPassInfo.renderPass = cveSwapChain->getRenderPass();
|
||||
renderPassInfo.framebuffer = cveSwapChain->getFrameBuffer(imageIndex);
|
||||
|
||||
std::array<VkClearValue, 2> clearValues{};
|
||||
clearValues[0].color = {0.1f, 0.1f, 0.1f, 1.0f};
|
||||
clearValues[1].depthStencil = {1.0f, 0};
|
||||
renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
|
||||
renderPassInfo.pClearValues = clearValues.data();
|
||||
renderPassInfo.renderArea.offset = {0, 0};
|
||||
renderPassInfo.renderArea.extent = cveSwapChain->getSwapChainExtent();
|
||||
|
||||
vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
std::array<VkClearValue, 2> clearValues{};
|
||||
clearValues[0].color = {0.1f, 0.1f, 0.1f, 1.0f};
|
||||
clearValues[1].depthStencil = {1.0f, 0};
|
||||
renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
|
||||
renderPassInfo.pClearValues = clearValues.data();
|
||||
|
||||
cvePipeline->bind(commandBuffers[i]);
|
||||
cveModel->bind(commandBuffers[i]);
|
||||
cveModel->draw(commandBuffers[i]);
|
||||
vkCmdBeginRenderPass(commandBuffers[imageIndex], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
vkCmdEndRenderPass(commandBuffers[i]);
|
||||
if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to record command buffer!");
|
||||
}
|
||||
VkViewport viewport{};
|
||||
viewport.x = 0.0f;
|
||||
viewport.y = 0.0f;
|
||||
viewport.width = static_cast<float>(cveSwapChain->getSwapChainExtent().width);
|
||||
viewport.height = static_cast<float>(cveSwapChain->getSwapChainExtent().height);
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
VkRect2D scissor{{0, 0}, cveSwapChain->getSwapChainExtent()};
|
||||
vkCmdSetViewport(commandBuffers[imageIndex], 0, 1, &viewport);
|
||||
vkCmdSetScissor(commandBuffers[imageIndex], 0, 1, &scissor);
|
||||
|
||||
cvePipeline->bind(commandBuffers[imageIndex]);
|
||||
cveModel->bind(commandBuffers[imageIndex]);
|
||||
cveModel->draw(commandBuffers[imageIndex]);
|
||||
|
||||
vkCmdEndRenderPass(commandBuffers[imageIndex]);
|
||||
if (vkEndCommandBuffer(commandBuffers[imageIndex]) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to record command buffer!");
|
||||
}
|
||||
}
|
||||
|
||||
void FirstApp::drawFrame() {
|
||||
uint32_t imageIndex;
|
||||
auto result = cveSwapChain.acquireNextImage(&imageIndex);
|
||||
auto result = cveSwapChain->acquireNextImage(&imageIndex);
|
||||
|
||||
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
|
||||
recreateSwapChain();
|
||||
return;
|
||||
}
|
||||
if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
|
||||
throw std::runtime_error("Failed to acquire swap chain image!");
|
||||
}
|
||||
|
||||
result = cveSwapChain.submitCommandBuffers(&commandBuffers[imageIndex], &imageIndex);
|
||||
recordCommandBuffer(imageIndex);
|
||||
result = cveSwapChain->submitCommandBuffers(&commandBuffers[imageIndex], &imageIndex);
|
||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || cveWindow.wasWindowResized()) {
|
||||
cveWindow.resetWindowResizedFlag();
|
||||
recreateSwapChain();
|
||||
return;
|
||||
}
|
||||
if (result != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to present swap chain image!");
|
||||
}
|
||||
|
||||
@@ -27,11 +27,14 @@ namespace cve {
|
||||
void createPipelineLayout();
|
||||
void createPipeline();
|
||||
void createCommandBuffers();
|
||||
void freeCommandBuffers();
|
||||
void drawFrame();
|
||||
void recreateSwapChain();
|
||||
void recordCommandBuffer(int imageIndex);
|
||||
|
||||
CveWindow cveWindow{WIDTH, HEIGHT, "Hello Vulkan!"};
|
||||
CveDevice cveDevice{cveWindow};
|
||||
CveSwapChain cveSwapChain{cveDevice, cveWindow.getExtent()};
|
||||
std::unique_ptr<CveSwapChain> cveSwapChain;
|
||||
std::unique_ptr<CvePipeline> cvePipeline;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
std::vector<VkCommandBuffer> commandBuffers;
|
||||
|
||||
Reference in New Issue
Block a user