// Copyright (C) 2015 Jérôme Leclercq // This file is part of the "Nazara Engine - Renderer module" // For conditions of distribution and use, see copyright notice in Config.hpp #include #include #include #include #include #include #include #include namespace Nz { RenderWindow::RenderWindow() : RenderTarget(), Window(), m_surface(Nz::Vulkan::GetInstance()), m_forcedPhysicalDevice(nullptr), m_depthStencilFormat(VK_FORMAT_MAX_ENUM) { } RenderWindow::RenderWindow(VideoMode mode, const String& title, UInt32 style) : RenderWindow() { ErrorFlags flags(ErrorFlag_ThrowException, true); Create(mode, title, style); } RenderWindow::RenderWindow(WindowHandle handle) : RenderWindow() { ErrorFlags flags(ErrorFlag_ThrowException, true); Create(handle); } RenderWindow::~RenderWindow() { // Nécessaire si Window::Destroy est appelé par son destructeur OnWindowDestroy(); } bool RenderWindow::Acquire(UInt32* imageIndex) const { if (!m_swapchain.AcquireNextImage(std::numeric_limits::max(), m_imageReadySemaphore, VK_NULL_HANDLE, imageIndex)) { NazaraError("Failed to acquire next image"); return false; } return true; } void RenderWindow::BuildPreRenderCommands(UInt32 imageIndex, Vk::CommandBuffer& commandBuffer) { commandBuffer.SetImageLayout(m_swapchain.GetBuffer(imageIndex).image, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // Temporary if (m_depthBufferView != VK_FORMAT_MAX_ENUM) { VkImageSubresourceRange imageRange = { VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, // VkImageAspectFlags aspectMask 0, // uint32_t baseMipLevel 1, // uint32_t levelCount 0, // uint32_t baseArrayLayer 1 // uint32_t layerCount }; commandBuffer.SetImageLayout(m_depthBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, imageRange); } } void RenderWindow::BuildPostRenderCommands(UInt32 imageIndex, Vk::CommandBuffer& commandBuffer) { commandBuffer.SetImageLayout(m_swapchain.GetBuffer(imageIndex).image, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); } const Vk::Framebuffer& RenderWindow::GetFrameBuffer(UInt32 imageIndex) const { return m_frameBuffers[imageIndex]; } UInt32 RenderWindow::GetFramebufferCount() const { return static_cast(m_frameBuffers.size()); } bool RenderWindow::Create(VideoMode mode, const String& title, UInt32 style) { return Window::Create(mode, title, style); } bool RenderWindow::Create(WindowHandle handle) { return Window::Create(handle); } const Vk::DeviceHandle& RenderWindow::GetDevice() const { return m_device; } UInt32 RenderWindow::GetPresentableFamilyQueue() const { return m_presentableFamilyQueue; } const Vk::Surface& RenderWindow::GetSurface() const { return m_surface; } const Vk::Swapchain& RenderWindow::GetSwapchain() const { return m_swapchain; } void RenderWindow::Present(UInt32 imageIndex) { NazaraAssert(imageIndex < m_frameBuffers.size(), "Invalid image index"); m_presentQueue.Present(m_swapchain, imageIndex); } bool RenderWindow::IsValid() const { return m_impl != nullptr; } void RenderWindow::SetDepthStencilFormats(std::vector pixelFormat) { m_wantedDepthStencilFormats = std::move(pixelFormat); } void RenderWindow::SetPhysicalDevice(VkPhysicalDevice device) { m_forcedPhysicalDevice = device; } bool RenderWindow::OnWindowCreated() { OnRenderTargetSizeChange(this); #if defined(NAZARA_PLATFORM_WINDOWS) HWND handle = reinterpret_cast(GetHandle()); HINSTANCE instance = reinterpret_cast(GetWindowLongPtrW(handle, GWLP_HINSTANCE)); bool success = m_surface.Create(instance, handle); #else #error This OS is not supported by Vulkan #endif if (!success) { NazaraError("Failed to create Vulkan surface"); return false; } m_device = Vulkan::SelectDevice(m_forcedPhysicalDevice, m_surface, &m_presentableFamilyQueue); if (!m_device) { NazaraError("Failed to get compatible Vulkan device"); return false; } m_presentQueue = m_device->GetQueue(m_presentableFamilyQueue, 0); std::vector surfaceFormats; if (!m_surface.GetFormats(m_forcedPhysicalDevice, &surfaceFormats)) { NazaraError("Failed to query supported surface formats"); return false; } if (surfaceFormats.size() == 1 && surfaceFormats[0].format == VK_FORMAT_UNDEFINED) m_colorFormat = VK_FORMAT_B8G8R8A8_UNORM; else m_colorFormat = surfaceFormats[0].format; m_colorSpace = surfaceFormats[0].colorSpace; if (!m_wantedDepthStencilFormats.empty()) { const Vk::PhysicalDevice& deviceInfo = Vulkan::GetPhysicalDeviceInfo(m_forcedPhysicalDevice); for (PixelFormatType format : m_wantedDepthStencilFormats) { switch (format) { case PixelFormatType_Depth16: m_depthStencilFormat = VK_FORMAT_D16_UNORM; break; case PixelFormatType_Depth24: case PixelFormatType_Depth24Stencil8: m_depthStencilFormat = VK_FORMAT_D24_UNORM_S8_UINT; break; case PixelFormatType_Depth32: m_depthStencilFormat = VK_FORMAT_D32_SFLOAT; break; case PixelFormatType_Stencil1: case PixelFormatType_Stencil4: case PixelFormatType_Stencil8: m_depthStencilFormat = VK_FORMAT_S8_UINT; break; case PixelFormatType_Stencil16: m_depthStencilFormat = VK_FORMAT_MAX_ENUM; break; default: { PixelFormatContent formatContent = PixelFormat::GetContent(format); if (formatContent != PixelFormatContent_DepthStencil && formatContent != PixelFormatContent_Stencil) NazaraWarning("Invalid format " + PixelFormat::GetName(format) + " for depth-stencil attachment"); m_depthStencilFormat = VK_FORMAT_MAX_ENUM; break; } } if (m_depthStencilFormat != VK_FORMAT_MAX_ENUM) { VkFormatProperties formatProperties = m_device->GetInstance().GetPhysicalDeviceFormatProperties(m_forcedPhysicalDevice, m_depthStencilFormat); if (formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) break; //< Found it m_depthStencilFormat = VK_FORMAT_MAX_ENUM; } } } if (!SetupSwapchain()) { NazaraError("Failed to create swapchain"); return false; } if (m_depthStencilFormat != VK_FORMAT_MAX_ENUM && !SetupDepthBuffer()) { NazaraError("Failed to create depth buffer"); return false; } if (!SetupRenderPass()) { NazaraError("Failed to create render pass"); return false; } UInt32 imageCount = m_swapchain.GetBufferCount(); // Framebuffers m_frameBuffers.resize(imageCount); for (UInt32 i = 0; i < imageCount; ++i) { std::array attachments = {m_swapchain.GetBuffer(i).view, m_depthBufferView}; VkFramebufferCreateInfo frameBufferCreate = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0, // VkFramebufferCreateFlags flags; m_renderPass, // VkRenderPass renderPass; (attachments[1] != VK_NULL_HANDLE) ? 2U : 1U, // uint32_t attachmentCount; attachments.data(), // const VkImageView* pAttachments; GetWidth(), // uint32_t width; GetHeight(), // uint32_t height; 1U // uint32_t layers; }; if (!m_frameBuffers[i].Create(m_device, frameBufferCreate)) { NazaraError("Failed to create framebuffer for image #" + String::Number(i)); return false; } } m_imageReadySemaphore.Create(m_device); m_clock.Restart(); return true; } void RenderWindow::OnWindowDestroy() { m_device->WaitForIdle(); m_frameBuffers.clear(); m_renderPass.Destroy(); m_swapchain.Destroy(); m_surface.Destroy(); } void RenderWindow::OnWindowResized() { OnRenderTargetSizeChange(this); } bool RenderWindow::SetupDepthBuffer() { VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0U, // VkImageCreateFlags flags; VK_IMAGE_TYPE_2D, // VkImageType imageType; m_depthStencilFormat, // VkFormat format; {GetWidth(), GetHeight(), 1U}, // VkExtent3D extent; 1U, // uint32_t mipLevels; 1U, // uint32_t arrayLayers; VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, // VkImageUsageFlags usage; VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; 0U, // uint32_t queueFamilyIndexCount; nullptr, // const uint32_t* pQueueFamilyIndices; VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; }; if (!m_depthBuffer.Create(m_device, imageCreateInfo)) { NazaraError("Failed to create depth buffer"); return false; } VkMemoryRequirements memoryReq = m_depthBuffer.GetMemoryRequirements(); if (!m_depthBufferMemory.Create(m_device, memoryReq.size, memoryReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { NazaraError("Failed to allocate depth buffer memory"); return false; } if (!m_depthBuffer.BindImageMemory(m_depthBufferMemory)) { NazaraError("Failed to bind depth buffer to buffer"); return false; } VkImageViewCreateInfo imageViewCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0, // VkImageViewCreateFlags flags; m_depthBuffer, // VkImage image; VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType; m_depthStencilFormat, // VkFormat format; { // VkComponentMapping components; VK_COMPONENT_SWIZZLE_R, // VkComponentSwizzle .r; VK_COMPONENT_SWIZZLE_G, // VkComponentSwizzle .g; VK_COMPONENT_SWIZZLE_B, // VkComponentSwizzle .b; VK_COMPONENT_SWIZZLE_A // VkComponentSwizzle .a; }, { // VkImageSubresourceRange subresourceRange; VK_IMAGE_ASPECT_DEPTH_BIT, // VkImageAspectFlags .aspectMask; 0, // uint32_t .baseMipLevel; 1, // uint32_t .levelCount; 0, // uint32_t .baseArrayLayer; 1 // uint32_t .layerCount; } }; if (!m_depthBufferView.Create(m_device, imageViewCreateInfo)) { NazaraError("Failed to create depth buffer view"); return false; } return true; } bool RenderWindow::SetupRenderPass() { std::array attachments = { { { 0, // VkAttachmentDescriptionFlags flags; m_colorFormat, // VkFormat format; VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp loadOp; VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp; VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp; VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp; VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout initialLayout; VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout finalLayout; }, { 0, // VkAttachmentDescriptionFlags flags; m_depthStencilFormat, // VkFormat format; VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp; VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp storeOp; VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp; VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp; VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, // VkImageLayout initialLayout; VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL // VkImageLayout finalLayout; }, } }; VkAttachmentReference colorReference = { 0, // uint32_t attachment; VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout layout; }; VkAttachmentReference depthReference = { 1, // uint32_t attachment; VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL // VkImageLayout layout; }; VkSubpassDescription subpass = { 0, // VkSubpassDescriptionFlags flags; VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint; 0U, // uint32_t inputAttachmentCount; nullptr, // const VkAttachmentReference* pInputAttachments; 1U, // uint32_t colorAttachmentCount; &colorReference, // const VkAttachmentReference* pColorAttachments; nullptr, // const VkAttachmentReference* pResolveAttachments; (m_depthStencilFormat != VK_FORMAT_MAX_ENUM) ? &depthReference : nullptr, // const VkAttachmentReference* pDepthStencilAttachment; 0U, // uint32_t preserveAttachmentCount; nullptr // const uint32_t* pPreserveAttachments; }; VkRenderPassCreateInfo createInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0, // VkRenderPassCreateFlags flags; (m_depthStencilFormat != VK_FORMAT_MAX_ENUM) ? 2U : 1U, // uint32_t attachmentCount; attachments.data(), // const VkAttachmentDescription* pAttachments; 1U, // uint32_t subpassCount; &subpass, // const VkSubpassDescription* pSubpasses; 0U, // uint32_t dependencyCount; nullptr // const VkSubpassDependency* pDependencies; }; return m_renderPass.Create(m_device, createInfo); } bool RenderWindow::SetupSwapchain() { VkSurfaceCapabilitiesKHR surfaceCapabilities; if (!m_surface.GetCapabilities(m_forcedPhysicalDevice, &surfaceCapabilities)) { NazaraError("Failed to query surface capabilities"); return false; } Nz::UInt32 imageCount = surfaceCapabilities.minImageCount + 1; if (surfaceCapabilities.maxImageCount > 0 && imageCount > surfaceCapabilities.maxImageCount) imageCount = surfaceCapabilities.maxImageCount; VkExtent2D extent; if (surfaceCapabilities.currentExtent.width == -1) { extent.width = Nz::Clamp(GetWidth(), surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width); extent.height = Nz::Clamp(GetHeight(), surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height); } else extent = surfaceCapabilities.currentExtent; std::vector presentModes; if (!m_surface.GetPresentModes(m_forcedPhysicalDevice, &presentModes)) { NazaraError("Failed to query supported present modes"); return false; } VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; for (VkPresentModeKHR presentMode : presentModes) { if (presentMode == VK_PRESENT_MODE_MAILBOX_KHR) { swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; break; } if (presentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; } VkSwapchainCreateInfoKHR swapchainInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, nullptr, 0, m_surface, imageCount, m_colorFormat, m_colorSpace, extent, 1, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_SHARING_MODE_EXCLUSIVE, 0, nullptr, surfaceCapabilities.currentTransform, VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, swapchainPresentMode, VK_TRUE, 0 }; if (!m_swapchain.Create(m_device, swapchainInfo)) { NazaraError("Failed to create swapchain"); return false; } return true; } }