#pragma once // Copyright(c) 2019, NVIDIA CORPORATION. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #if defined( _MSC_VER ) // no need to ignore any warnings with MSVC #elif defined( __clang__ ) # pragma clang diagnostic ignored "-Wmissing-braces" #elif defined( __GNUC__ ) // no need to ignore any warnings with GCC #else // unknown compiler... just ignore the warnings for yourselves ;) #endif #include "../../samples/utils/utils.hpp" #include "vulkan/vulkan_raii.hpp" #include namespace vk { namespace raii { namespace su { vk::raii::DeviceMemory allocateDeviceMemory( vk::raii::Device const & device, vk::PhysicalDeviceMemoryProperties const & memoryProperties, vk::MemoryRequirements const & memoryRequirements, vk::MemoryPropertyFlags memoryPropertyFlags ) { uint32_t memoryTypeIndex = vk::su::findMemoryType( memoryProperties, memoryRequirements.memoryTypeBits, memoryPropertyFlags ); vk::MemoryAllocateInfo memoryAllocateInfo( memoryRequirements.size, memoryTypeIndex ); return vk::raii::DeviceMemory( device, memoryAllocateInfo ); } template void copyToDevice( vk::raii::DeviceMemory const & deviceMemory, T const * pData, size_t count, vk::DeviceSize stride = sizeof( T ) ) { assert( sizeof( T ) <= stride ); uint8_t * deviceData = static_cast( deviceMemory.mapMemory( 0, count * stride ) ); if ( stride == sizeof( T ) ) { memcpy( deviceData, pData, count * sizeof( T ) ); } else { for ( size_t i = 0; i < count; i++ ) { memcpy( deviceData, &pData[i], sizeof( T ) ); deviceData += stride; } } deviceMemory.unmapMemory(); } template void copyToDevice( vk::raii::DeviceMemory const & deviceMemory, T const & data ) { copyToDevice( deviceMemory, &data, 1 ); } template std::unique_ptr make_unique( Args &&... args ) { #if ( 14 <= VULKAN_HPP_CPP_VERSION ) return std::make_unique( std::forward( args )... ); #else return std::unique_ptr( new T( std::forward( args )... ) ); #endif } template void oneTimeSubmit( vk::raii::CommandBuffer const & commandBuffer, vk::raii::Queue const & queue, Func const & func ) { commandBuffer.begin( vk::CommandBufferBeginInfo( vk::CommandBufferUsageFlagBits::eOneTimeSubmit ) ); func( commandBuffer ); commandBuffer.end(); vk::SubmitInfo submitInfo( nullptr, nullptr, *commandBuffer ); queue.submit( submitInfo, nullptr ); queue.waitIdle(); } template void oneTimeSubmit( vk::raii::Device const & device, vk::raii::CommandPool const & commandPool, vk::raii::Queue const & queue, Func const & func ) { vk::raii::CommandBuffers commandBuffers( device, { *commandPool, vk::CommandBufferLevel::ePrimary, 1 } ); oneTimeSubmit( commandBuffers.front(), queue, func ); } void setImageLayout( vk::raii::CommandBuffer const & commandBuffer, vk::Image image, vk::Format format, vk::ImageLayout oldImageLayout, vk::ImageLayout newImageLayout ) { vk::AccessFlags sourceAccessMask; switch ( oldImageLayout ) { case vk::ImageLayout::eTransferDstOptimal: sourceAccessMask = vk::AccessFlagBits::eTransferWrite; break; case vk::ImageLayout::ePreinitialized: sourceAccessMask = vk::AccessFlagBits::eHostWrite; break; case vk::ImageLayout::eGeneral: // sourceAccessMask is empty case vk::ImageLayout::eUndefined: break; default: assert( false ); break; } vk::PipelineStageFlags sourceStage; switch ( oldImageLayout ) { case vk::ImageLayout::eGeneral: case vk::ImageLayout::ePreinitialized: sourceStage = vk::PipelineStageFlagBits::eHost; break; case vk::ImageLayout::eTransferDstOptimal: sourceStage = vk::PipelineStageFlagBits::eTransfer; break; case vk::ImageLayout::eUndefined: sourceStage = vk::PipelineStageFlagBits::eTopOfPipe; break; default: assert( false ); break; } vk::AccessFlags destinationAccessMask; switch ( newImageLayout ) { case vk::ImageLayout::eColorAttachmentOptimal: destinationAccessMask = vk::AccessFlagBits::eColorAttachmentWrite; break; case vk::ImageLayout::eDepthStencilAttachmentOptimal: destinationAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite; break; case vk::ImageLayout::eGeneral: // empty destinationAccessMask case vk::ImageLayout::ePresentSrcKHR: break; case vk::ImageLayout::eShaderReadOnlyOptimal: destinationAccessMask = vk::AccessFlagBits::eShaderRead; break; case vk::ImageLayout::eTransferSrcOptimal: destinationAccessMask = vk::AccessFlagBits::eTransferRead; break; case vk::ImageLayout::eTransferDstOptimal: destinationAccessMask = vk::AccessFlagBits::eTransferWrite; break; default: assert( false ); break; } vk::PipelineStageFlags destinationStage; switch ( newImageLayout ) { case vk::ImageLayout::eColorAttachmentOptimal: destinationStage = vk::PipelineStageFlagBits::eColorAttachmentOutput; break; case vk::ImageLayout::eDepthStencilAttachmentOptimal: destinationStage = vk::PipelineStageFlagBits::eEarlyFragmentTests; break; case vk::ImageLayout::eGeneral: destinationStage = vk::PipelineStageFlagBits::eHost; break; case vk::ImageLayout::ePresentSrcKHR: destinationStage = vk::PipelineStageFlagBits::eBottomOfPipe; break; case vk::ImageLayout::eShaderReadOnlyOptimal: destinationStage = vk::PipelineStageFlagBits::eFragmentShader; break; case vk::ImageLayout::eTransferDstOptimal: case vk::ImageLayout::eTransferSrcOptimal: destinationStage = vk::PipelineStageFlagBits::eTransfer; break; default: assert( false ); break; } vk::ImageAspectFlags aspectMask; if ( newImageLayout == vk::ImageLayout::eDepthStencilAttachmentOptimal ) { aspectMask = vk::ImageAspectFlagBits::eDepth; if ( format == vk::Format::eD32SfloatS8Uint || format == vk::Format::eD24UnormS8Uint ) { aspectMask |= vk::ImageAspectFlagBits::eStencil; } } else { aspectMask = vk::ImageAspectFlagBits::eColor; } vk::ImageSubresourceRange imageSubresourceRange( aspectMask, 0, 1, 0, 1 ); vk::ImageMemoryBarrier imageMemoryBarrier( sourceAccessMask, destinationAccessMask, oldImageLayout, newImageLayout, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, image, imageSubresourceRange ); return commandBuffer.pipelineBarrier( sourceStage, destinationStage, {}, nullptr, nullptr, imageMemoryBarrier ); } struct BufferData { BufferData( vk::raii::PhysicalDevice const & physicalDevice, vk::raii::Device const & device, vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags propertyFlags = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent ) : buffer( device, vk::BufferCreateInfo( {}, size, usage ) ) , deviceMemory( vk::raii::su::allocateDeviceMemory( device, physicalDevice.getMemoryProperties(), buffer.getMemoryRequirements(), propertyFlags ) ) #if !defined( NDEBUG ) , m_size( size ) , m_usage( usage ) , m_propertyFlags( propertyFlags ) #endif { buffer.bindMemory( *deviceMemory, 0 ); } BufferData( std::nullptr_t ) {} template void upload( DataType const & data ) const { assert( ( m_propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent ) && ( m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible ) ); assert( sizeof( DataType ) <= m_size ); void * dataPtr = deviceMemory.mapMemory( 0, sizeof( DataType ) ); memcpy( dataPtr, &data, sizeof( DataType ) ); deviceMemory.unmapMemory(); } template void upload( std::vector const & data, size_t stride = 0 ) const { assert( m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible ); size_t elementSize = stride ? stride : sizeof( DataType ); assert( sizeof( DataType ) <= elementSize ); copyToDevice( deviceMemory, data.data(), data.size(), elementSize ); } template void upload( vk::raii::PhysicalDevice const & physicalDevice, vk::raii::Device const & device, vk::raii::CommandPool const & commandPool, vk::raii::Queue const & queue, std::vector const & data, size_t stride ) const { assert( m_usage & vk::BufferUsageFlagBits::eTransferDst ); assert( m_propertyFlags & vk::MemoryPropertyFlagBits::eDeviceLocal ); size_t elementSize = stride ? stride : sizeof( DataType ); assert( sizeof( DataType ) <= elementSize ); size_t dataSize = data.size() * elementSize; assert( dataSize <= m_size ); vk::raii::su::BufferData stagingBuffer( physicalDevice, device, dataSize, vk::BufferUsageFlagBits::eTransferSrc ); copyToDevice( stagingBuffer.deviceMemory, data.data(), data.size(), elementSize ); vk::raii::su::oneTimeSubmit( device, commandPool, queue, [&]( vk::raii::CommandBuffer const & commandBuffer ) { commandBuffer.copyBuffer( *stagingBuffer.buffer, *this->buffer, vk::BufferCopy( 0, 0, dataSize ) ); } ); } // the order of buffer and deviceMemory here is important to get the constructor running ! vk::raii::Buffer buffer = nullptr; vk::raii::DeviceMemory deviceMemory = nullptr; #if !defined( NDEBUG ) private: vk::DeviceSize m_size; vk::BufferUsageFlags m_usage; vk::MemoryPropertyFlags m_propertyFlags; #endif }; struct ImageData { ImageData( vk::raii::PhysicalDevice const & physicalDevice, vk::raii::Device const & device, vk::Format format_, vk::Extent2D const & extent, vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::ImageLayout initialLayout, vk::MemoryPropertyFlags memoryProperties, vk::ImageAspectFlags aspectMask ) : format( format_ ) , image( device, { vk::ImageCreateFlags(), vk::ImageType::e2D, format, vk::Extent3D( extent, 1 ), 1, 1, vk::SampleCountFlagBits::e1, tiling, usage | vk::ImageUsageFlagBits::eSampled, vk::SharingMode::eExclusive, {}, initialLayout } ) , deviceMemory( vk::raii::su::allocateDeviceMemory( device, physicalDevice.getMemoryProperties(), image.getMemoryRequirements(), memoryProperties ) ) { image.bindMemory( *deviceMemory, 0 ); imageView = vk::raii::ImageView( device, vk::ImageViewCreateInfo( {}, *image, vk::ImageViewType::e2D, format, {}, { aspectMask, 0, 1, 0, 1 } ) ); } ImageData( std::nullptr_t ) {} vk::Format format; vk::raii::Image image = nullptr; vk::raii::DeviceMemory deviceMemory = nullptr; vk::raii::ImageView imageView = nullptr; }; struct DepthBufferData : public ImageData { DepthBufferData( vk::raii::PhysicalDevice const & physicalDevice, vk::raii::Device const & device, vk::Format format, vk::Extent2D const & extent ) : ImageData( physicalDevice, device, format, extent, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eDepthStencilAttachment, vk::ImageLayout::eUndefined, vk::MemoryPropertyFlagBits::eDeviceLocal, vk::ImageAspectFlagBits::eDepth ) { } }; struct SurfaceData { SurfaceData( vk::raii::Instance const & instance, std::string const & windowName, vk::Extent2D const & extent_ ) : extent( extent_ ), window( vk::su::createWindow( windowName, extent ) ) { VkSurfaceKHR _surface; VkResult err = glfwCreateWindowSurface( static_cast( *instance ), window.handle, nullptr, &_surface ); if ( err != VK_SUCCESS ) throw std::runtime_error( "Failed to create window!" ); surface = vk::raii::SurfaceKHR( instance, _surface ); } vk::Extent2D extent; vk::su::WindowData window; vk::raii::SurfaceKHR surface = nullptr; }; struct SwapChainData { SwapChainData( vk::raii::PhysicalDevice const & physicalDevice, vk::raii::Device const & device, vk::raii::SurfaceKHR const & surface, vk::Extent2D const & extent, vk::ImageUsageFlags usage, vk::raii::SwapchainKHR const * pOldSwapchain, uint32_t graphicsQueueFamilyIndex, uint32_t presentQueueFamilyIndex ) { vk::SurfaceFormatKHR surfaceFormat = vk::su::pickSurfaceFormat( physicalDevice.getSurfaceFormatsKHR( *surface ) ); colorFormat = surfaceFormat.format; vk::SurfaceCapabilitiesKHR surfaceCapabilities = physicalDevice.getSurfaceCapabilitiesKHR( *surface ); vk::Extent2D swapchainExtent; if ( surfaceCapabilities.currentExtent.width == std::numeric_limits::max() ) { // If the surface size is undefined, the size is set to the size of the images requested. swapchainExtent.width = vk::su::clamp( extent.width, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width ); swapchainExtent.height = vk::su::clamp( extent.height, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height ); } else { // If the surface size is defined, the swap chain size must match swapchainExtent = surfaceCapabilities.currentExtent; } vk::SurfaceTransformFlagBitsKHR preTransform = ( surfaceCapabilities.supportedTransforms & vk::SurfaceTransformFlagBitsKHR::eIdentity ) ? vk::SurfaceTransformFlagBitsKHR::eIdentity : surfaceCapabilities.currentTransform; vk::CompositeAlphaFlagBitsKHR compositeAlpha = ( surfaceCapabilities.supportedCompositeAlpha & vk::CompositeAlphaFlagBitsKHR::ePreMultiplied ) ? vk::CompositeAlphaFlagBitsKHR::ePreMultiplied : ( surfaceCapabilities.supportedCompositeAlpha & vk::CompositeAlphaFlagBitsKHR::ePostMultiplied ) ? vk::CompositeAlphaFlagBitsKHR::ePostMultiplied : ( surfaceCapabilities.supportedCompositeAlpha & vk::CompositeAlphaFlagBitsKHR::eInherit ) ? vk::CompositeAlphaFlagBitsKHR::eInherit : vk::CompositeAlphaFlagBitsKHR::eOpaque; vk::PresentModeKHR presentMode = vk::su::pickPresentMode( physicalDevice.getSurfacePresentModesKHR( *surface ) ); vk::SwapchainCreateInfoKHR swapChainCreateInfo( {}, *surface, surfaceCapabilities.minImageCount, colorFormat, surfaceFormat.colorSpace, swapchainExtent, 1, usage, vk::SharingMode::eExclusive, {}, preTransform, compositeAlpha, presentMode, true, pOldSwapchain ? **pOldSwapchain : nullptr ); if ( graphicsQueueFamilyIndex != presentQueueFamilyIndex ) { uint32_t queueFamilyIndices[2] = { graphicsQueueFamilyIndex, presentQueueFamilyIndex }; // If the graphics and present queues are from different queue families, we either have to explicitly // transfer ownership of images between the queues, or we have to create the swapchain with imageSharingMode // as vk::SharingMode::eConcurrent swapChainCreateInfo.imageSharingMode = vk::SharingMode::eConcurrent; swapChainCreateInfo.queueFamilyIndexCount = 2; swapChainCreateInfo.pQueueFamilyIndices = queueFamilyIndices; } swapChain = vk::raii::SwapchainKHR( device, swapChainCreateInfo ); images = swapChain.getImages(); imageViews.reserve( images.size() ); vk::ImageViewCreateInfo imageViewCreateInfo( {}, {}, vk::ImageViewType::e2D, colorFormat, {}, { vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 } ); for ( auto image : images ) { imageViewCreateInfo.image = static_cast( image ); imageViews.emplace_back( device, imageViewCreateInfo ); } } vk::Format colorFormat; vk::raii::SwapchainKHR swapChain = nullptr; std::vector images; std::vector imageViews; }; struct TextureData { TextureData( vk::raii::PhysicalDevice const & physicalDevice, vk::raii::Device const & device, vk::Extent2D const & extent_ = { 256, 256 }, vk::ImageUsageFlags usageFlags = {}, vk::FormatFeatureFlags formatFeatureFlags = {}, bool anisotropyEnable = false, bool forceStaging = false ) : format( vk::Format::eR8G8B8A8Unorm ) , extent( extent_ ) , sampler( device, { {}, vk::Filter::eLinear, vk::Filter::eLinear, vk::SamplerMipmapMode::eLinear, vk::SamplerAddressMode::eRepeat, vk::SamplerAddressMode::eRepeat, vk::SamplerAddressMode::eRepeat, 0.0f, anisotropyEnable, 16.0f, false, vk::CompareOp::eNever, 0.0f, 0.0f, vk::BorderColor::eFloatOpaqueBlack } ) { vk::FormatProperties formatProperties = physicalDevice.getFormatProperties( format ); formatFeatureFlags |= vk::FormatFeatureFlagBits::eSampledImage; needsStaging = forceStaging || ( ( formatProperties.linearTilingFeatures & formatFeatureFlags ) != formatFeatureFlags ); vk::ImageTiling imageTiling; vk::ImageLayout initialLayout; vk::MemoryPropertyFlags requirements; if ( needsStaging ) { assert( ( formatProperties.optimalTilingFeatures & formatFeatureFlags ) == formatFeatureFlags ); stagingBufferData = BufferData( physicalDevice, device, extent.width * extent.height * 4, vk::BufferUsageFlagBits::eTransferSrc ); imageTiling = vk::ImageTiling::eOptimal; usageFlags |= vk::ImageUsageFlagBits::eTransferDst; initialLayout = vk::ImageLayout::eUndefined; } else { imageTiling = vk::ImageTiling::eLinear; initialLayout = vk::ImageLayout::ePreinitialized; requirements = vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eHostVisible; } imageData = ImageData( physicalDevice, device, format, extent, imageTiling, usageFlags | vk::ImageUsageFlagBits::eSampled, initialLayout, requirements, vk::ImageAspectFlagBits::eColor ); } template void setImage( vk::raii::CommandBuffer const & commandBuffer, ImageGenerator const & imageGenerator ) { void * data = needsStaging ? stagingBufferData.deviceMemory.mapMemory( 0, stagingBufferData.buffer.getMemoryRequirements().size ) : imageData.deviceMemory.mapMemory( 0, imageData.image.getMemoryRequirements().size ); imageGenerator( data, extent ); needsStaging ? stagingBufferData.deviceMemory.unmapMemory() : imageData.deviceMemory.unmapMemory(); if ( needsStaging ) { // Since we're going to blit to the texture image, set its layout to eTransferDstOptimal vk::raii::su::setImageLayout( commandBuffer, *imageData.image, imageData.format, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal ); vk::BufferImageCopy copyRegion( 0, extent.width, extent.height, vk::ImageSubresourceLayers( vk::ImageAspectFlagBits::eColor, 0, 0, 1 ), vk::Offset3D( 0, 0, 0 ), vk::Extent3D( extent, 1 ) ); commandBuffer.copyBufferToImage( *stagingBufferData.buffer, *imageData.image, vk::ImageLayout::eTransferDstOptimal, copyRegion ); // Set the layout for the texture image from eTransferDstOptimal to eShaderReadOnlyOptimal vk::raii::su::setImageLayout( commandBuffer, *imageData.image, imageData.format, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal ); } else { // If we can use the linear tiled image as a texture, just do it vk::raii::su::setImageLayout( commandBuffer, *imageData.image, imageData.format, vk::ImageLayout::ePreinitialized, vk::ImageLayout::eShaderReadOnlyOptimal ); } } vk::Format format; vk::Extent2D extent; bool needsStaging; BufferData stagingBufferData = nullptr; ImageData imageData = nullptr; vk::raii::Sampler sampler; }; std::pair findGraphicsAndPresentQueueFamilyIndex( vk::raii::PhysicalDevice const & physicalDevice, vk::raii::SurfaceKHR const & surface ) { std::vector queueFamilyProperties = physicalDevice.getQueueFamilyProperties(); assert( queueFamilyProperties.size() < std::numeric_limits::max() ); uint32_t graphicsQueueFamilyIndex = vk::su::findGraphicsQueueFamilyIndex( queueFamilyProperties ); if ( physicalDevice.getSurfaceSupportKHR( graphicsQueueFamilyIndex, *surface ) ) { return std::make_pair( graphicsQueueFamilyIndex, graphicsQueueFamilyIndex ); // the first graphicsQueueFamilyIndex does also support presents } // the graphicsQueueFamilyIndex doesn't support present -> look for an other family index that supports both // graphics and present for ( size_t i = 0; i < queueFamilyProperties.size(); i++ ) { if ( ( queueFamilyProperties[i].queueFlags & vk::QueueFlagBits::eGraphics ) && physicalDevice.getSurfaceSupportKHR( static_cast( i ), *surface ) ) { return std::make_pair( static_cast( i ), static_cast( i ) ); } } // there's nothing like a single family index that supports both graphics and present -> look for an other // family index that supports present for ( size_t i = 0; i < queueFamilyProperties.size(); i++ ) { if ( physicalDevice.getSurfaceSupportKHR( static_cast( i ), *surface ) ) { return std::make_pair( graphicsQueueFamilyIndex, static_cast( i ) ); } } throw std::runtime_error( "Could not find queues for both graphics or present -> terminating" ); } vk::raii::CommandBuffer makeCommandBuffer( vk::raii::Device const & device, vk::raii::CommandPool const & commandPool ) { vk::CommandBufferAllocateInfo commandBufferAllocateInfo( *commandPool, vk::CommandBufferLevel::ePrimary, 1 ); return std::move( vk::raii::CommandBuffers( device, commandBufferAllocateInfo ).front() ); } vk::raii::DescriptorPool makeDescriptorPool( vk::raii::Device const & device, std::vector const & poolSizes ) { assert( !poolSizes.empty() ); uint32_t maxSets = std::accumulate( poolSizes.begin(), poolSizes.end(), 0, []( uint32_t sum, vk::DescriptorPoolSize const & dps ) { return sum + dps.descriptorCount; } ); assert( 0 < maxSets ); vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo( vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, maxSets, poolSizes ); return vk::raii::DescriptorPool( device, descriptorPoolCreateInfo ); } vk::raii::DescriptorSetLayout makeDescriptorSetLayout( vk::raii::Device const & device, std::vector> const & bindingData, vk::DescriptorSetLayoutCreateFlags flags = {} ) { std::vector bindings( bindingData.size() ); for ( size_t i = 0; i < bindingData.size(); i++ ) { bindings[i] = vk::DescriptorSetLayoutBinding( vk::su::checked_cast( i ), std::get<0>( bindingData[i] ), std::get<1>( bindingData[i] ), std::get<2>( bindingData[i] ) ); } vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo( flags, bindings ); return vk::raii::DescriptorSetLayout( device, descriptorSetLayoutCreateInfo ); } vk::raii::Device makeDevice( vk::raii::PhysicalDevice const & physicalDevice, uint32_t queueFamilyIndex, std::vector const & extensions = {}, vk::PhysicalDeviceFeatures const * physicalDeviceFeatures = nullptr, void const * pNext = nullptr ) { std::vector enabledExtensions; enabledExtensions.reserve( extensions.size() ); for ( auto const & ext : extensions ) { enabledExtensions.push_back( ext.data() ); } float queuePriority = 0.0f; vk::DeviceQueueCreateInfo deviceQueueCreateInfo( vk::DeviceQueueCreateFlags(), queueFamilyIndex, 1, &queuePriority ); vk::DeviceCreateInfo deviceCreateInfo( vk::DeviceCreateFlags(), deviceQueueCreateInfo, {}, enabledExtensions, physicalDeviceFeatures, pNext ); return vk::raii::Device( physicalDevice, deviceCreateInfo ); } std::vector makeFramebuffers( vk::raii::Device const & device, vk::raii::RenderPass & renderPass, std::vector const & imageViews, vk::raii::ImageView const * pDepthImageView, vk::Extent2D const & extent ) { vk::ImageView attachments[2]; attachments[1] = pDepthImageView ? **pDepthImageView : vk::ImageView(); vk::FramebufferCreateInfo framebufferCreateInfo( vk::FramebufferCreateFlags(), *renderPass, pDepthImageView ? 2 : 1, attachments, extent.width, extent.height, 1 ); std::vector framebuffers; framebuffers.reserve( imageViews.size() ); for ( auto const & imageView : imageViews ) { attachments[0] = *imageView; framebuffers.push_back( vk::raii::Framebuffer( device, framebufferCreateInfo ) ); } return framebuffers; } vk::raii::Pipeline makeGraphicsPipeline( vk::raii::Device const & device, vk::raii::PipelineCache const & pipelineCache, vk::raii::ShaderModule const & vertexShaderModule, vk::SpecializationInfo const * vertexShaderSpecializationInfo, vk::raii::ShaderModule const & fragmentShaderModule, vk::SpecializationInfo const * fragmentShaderSpecializationInfo, uint32_t vertexStride, std::vector> const & vertexInputAttributeFormatOffset, vk::FrontFace frontFace, bool depthBuffered, vk::raii::PipelineLayout const & pipelineLayout, vk::raii::RenderPass const & renderPass ) { std::array pipelineShaderStageCreateInfos = { vk::PipelineShaderStageCreateInfo( {}, vk::ShaderStageFlagBits::eVertex, *vertexShaderModule, "main", vertexShaderSpecializationInfo ), vk::PipelineShaderStageCreateInfo( {}, vk::ShaderStageFlagBits::eFragment, *fragmentShaderModule, "main", fragmentShaderSpecializationInfo ) }; std::vector vertexInputAttributeDescriptions; vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo; vk::VertexInputBindingDescription vertexInputBindingDescription( 0, vertexStride ); if ( 0 < vertexStride ) { vertexInputAttributeDescriptions.reserve( vertexInputAttributeFormatOffset.size() ); for ( uint32_t i = 0; i < vertexInputAttributeFormatOffset.size(); i++ ) { vertexInputAttributeDescriptions.emplace_back( i, 0, vertexInputAttributeFormatOffset[i].first, vertexInputAttributeFormatOffset[i].second ); } pipelineVertexInputStateCreateInfo.setVertexBindingDescriptions( vertexInputBindingDescription ); pipelineVertexInputStateCreateInfo.setVertexAttributeDescriptions( vertexInputAttributeDescriptions ); } vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo( vk::PipelineInputAssemblyStateCreateFlags(), vk::PrimitiveTopology::eTriangleList ); vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo( vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr ); vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo( vk::PipelineRasterizationStateCreateFlags(), false, false, vk::PolygonMode::eFill, vk::CullModeFlagBits::eBack, frontFace, false, 0.0f, 0.0f, 0.0f, 1.0f ); vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo( {}, vk::SampleCountFlagBits::e1 ); vk::StencilOpState stencilOpState( vk::StencilOp::eKeep, vk::StencilOp::eKeep, vk::StencilOp::eKeep, vk::CompareOp::eAlways ); vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo( vk::PipelineDepthStencilStateCreateFlags(), depthBuffered, depthBuffered, vk::CompareOp::eLessOrEqual, false, false, stencilOpState, stencilOpState ); vk::ColorComponentFlags colorComponentFlags( vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA ); vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState( false, vk::BlendFactor::eZero, vk::BlendFactor::eZero, vk::BlendOp::eAdd, vk::BlendFactor::eZero, vk::BlendFactor::eZero, vk::BlendOp::eAdd, colorComponentFlags ); vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo( vk::PipelineColorBlendStateCreateFlags(), false, vk::LogicOp::eNoOp, pipelineColorBlendAttachmentState, { { 1.0f, 1.0f, 1.0f, 1.0f } } ); std::array dynamicStates = { vk::DynamicState::eViewport, vk::DynamicState::eScissor }; vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo( vk::PipelineDynamicStateCreateFlags(), dynamicStates ); vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo( vk::PipelineCreateFlags(), pipelineShaderStageCreateInfos, &pipelineVertexInputStateCreateInfo, &pipelineInputAssemblyStateCreateInfo, nullptr, &pipelineViewportStateCreateInfo, &pipelineRasterizationStateCreateInfo, &pipelineMultisampleStateCreateInfo, &pipelineDepthStencilStateCreateInfo, &pipelineColorBlendStateCreateInfo, &pipelineDynamicStateCreateInfo, *pipelineLayout, *renderPass ); return vk::raii::Pipeline( device, pipelineCache, graphicsPipelineCreateInfo ); } vk::raii::Image makeImage( vk::raii::Device const & device ) { vk::ImageCreateInfo imageCreateInfo( {}, vk::ImageType::e2D, vk::Format::eB8G8R8A8Unorm, vk::Extent3D( 640, 640, 1 ), 1, 1, vk::SampleCountFlagBits::e1, vk::ImageTiling::eLinear, vk::ImageUsageFlagBits::eTransferSrc ); return vk::raii::Image( device, imageCreateInfo ); } vk::raii::Instance makeInstance( vk::raii::Context const & context, std::string const & appName, std::string const & engineName, std::vector const & layers = {}, std::vector const & extensions = {}, uint32_t apiVersion = VK_API_VERSION_1_0 ) { vk::ApplicationInfo applicationInfo( appName.c_str(), 1, engineName.c_str(), 1, apiVersion ); std::vector enabledLayers = vk::su::gatherLayers( layers #if !defined( NDEBUG ) , context.enumerateInstanceLayerProperties() #endif ); std::vector enabledExtensions = vk::su::gatherExtensions( extensions #if !defined( NDEBUG ) , context.enumerateInstanceExtensionProperties() #endif ); #if defined( NDEBUG ) vk::StructureChain #else vk::StructureChain #endif instanceCreateInfoChain = vk::su::makeInstanceCreateInfoChain( applicationInfo, enabledLayers, enabledExtensions ); return vk::raii::Instance( context, instanceCreateInfoChain.get() ); } vk::raii::RenderPass makeRenderPass( vk::raii::Device const & device, vk::Format colorFormat, vk::Format depthFormat, vk::AttachmentLoadOp loadOp = vk::AttachmentLoadOp::eClear, vk::ImageLayout colorFinalLayout = vk::ImageLayout::ePresentSrcKHR ) { std::vector attachmentDescriptions; assert( colorFormat != vk::Format::eUndefined ); attachmentDescriptions.emplace_back( vk::AttachmentDescriptionFlags(), colorFormat, vk::SampleCountFlagBits::e1, loadOp, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare, vk::ImageLayout::eUndefined, colorFinalLayout ); if ( depthFormat != vk::Format::eUndefined ) { attachmentDescriptions.emplace_back( vk::AttachmentDescriptionFlags(), depthFormat, vk::SampleCountFlagBits::e1, loadOp, vk::AttachmentStoreOp::eDontCare, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare, vk::ImageLayout::eUndefined, vk::ImageLayout::eDepthStencilAttachmentOptimal ); } vk::AttachmentReference colorAttachment( 0, vk::ImageLayout::eColorAttachmentOptimal ); vk::AttachmentReference depthAttachment( 1, vk::ImageLayout::eDepthStencilAttachmentOptimal ); vk::SubpassDescription subpassDescription( vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, {}, colorAttachment, {}, ( depthFormat != vk::Format::eUndefined ) ? &depthAttachment : nullptr ); vk::RenderPassCreateInfo renderPassCreateInfo( vk::RenderPassCreateFlags(), attachmentDescriptions, subpassDescription ); return vk::raii::RenderPass( device, renderPassCreateInfo ); } vk::Format pickDepthFormat( vk::raii::PhysicalDevice const & physicalDevice ) { std::vector candidates = { vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint }; for ( vk::Format format : candidates ) { vk::FormatProperties props = physicalDevice.getFormatProperties( format ); if ( props.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment ) { return format; } } throw std::runtime_error( "failed to find supported format!" ); } void submitAndWait( vk::raii::Device const & device, vk::raii::Queue const & queue, vk::raii::CommandBuffer const & commandBuffer ) { vk::raii::Fence fence( device, vk::FenceCreateInfo() ); queue.submit( vk::SubmitInfo( nullptr, nullptr, *commandBuffer ), *fence ); while ( vk::Result::eTimeout == device.waitForFences( { *fence }, VK_TRUE, vk::su::FenceTimeout ) ) ; } void updateDescriptorSets( vk::raii::Device const & device, vk::raii::DescriptorSet const & descriptorSet, std::vector> const & bufferData, vk::raii::su::TextureData const & textureData, uint32_t bindingOffset = 0 ) { std::vector bufferInfos; bufferInfos.reserve( bufferData.size() ); std::vector writeDescriptorSets; writeDescriptorSets.reserve( bufferData.size() + 1 ); uint32_t dstBinding = bindingOffset; for ( auto const & bhd : bufferData ) { bufferInfos.emplace_back( *std::get<1>( bhd ), 0, VK_WHOLE_SIZE ); vk::BufferView bufferView; if ( std::get<2>( bhd ) ) { bufferView = **std::get<2>( bhd ); } writeDescriptorSets.emplace_back( *descriptorSet, dstBinding++, 0, 1, std::get<0>( bhd ), nullptr, &bufferInfos.back(), std::get<2>( bhd ) ? &bufferView : nullptr ); } vk::DescriptorImageInfo imageInfo( *textureData.sampler, *textureData.imageData.imageView, vk::ImageLayout::eShaderReadOnlyOptimal ); writeDescriptorSets.emplace_back( *descriptorSet, dstBinding, 0, vk::DescriptorType::eCombinedImageSampler, imageInfo, nullptr, nullptr ); device.updateDescriptorSets( writeDescriptorSets, nullptr ); } void updateDescriptorSets( vk::raii::Device const & device, vk::raii::DescriptorSet const & descriptorSet, std::vector> const & bufferData, std::vector const & textureData, uint32_t bindingOffset = 0 ) { std::vector bufferInfos; bufferInfos.reserve( bufferData.size() ); std::vector writeDescriptorSets; writeDescriptorSets.reserve( bufferData.size() + ( textureData.empty() ? 0 : 1 ) ); uint32_t dstBinding = bindingOffset; for ( auto const & bhd : bufferData ) { bufferInfos.emplace_back( *std::get<1>( bhd ), 0, VK_WHOLE_SIZE ); vk::BufferView bufferView; if ( std::get<2>( bhd ) ) { bufferView = **std::get<2>( bhd ); } writeDescriptorSets.emplace_back( *descriptorSet, dstBinding++, 0, 1, std::get<0>( bhd ), nullptr, &bufferInfos.back(), std::get<2>( bhd ) ? &bufferView : nullptr ); } std::vector imageInfos; if ( !textureData.empty() ) { imageInfos.reserve( textureData.size() ); for ( auto const & thd : textureData ) { imageInfos.emplace_back( *thd.sampler, *thd.imageData.imageView, vk::ImageLayout::eShaderReadOnlyOptimal ); } writeDescriptorSets.emplace_back( *descriptorSet, dstBinding, 0, vk::su::checked_cast( imageInfos.size() ), vk::DescriptorType::eCombinedImageSampler, imageInfos.data(), nullptr, nullptr ); } device.updateDescriptorSets( writeDescriptorSets, nullptr ); } } // namespace su } // namespace raii } // namespace vk