22
API without Secrets: Introduction to Vulkan* Part 6 Table of Contents Tutorial 6: Descriptor Sets—Using Textures in Shaders ......................................................................................................... 2 Creating an Image .............................................................................................................................................................. 2 Allocating Image Memory .............................................................................................................................................. 4 Creating Image View ...................................................................................................................................................... 6 Copying Data to an Image .............................................................................................................................................. 6 Creating a Sampler ........................................................................................................................................................... 10 Using Descriptor Sets ....................................................................................................................................................... 11 Creating a Descriptor Set Layout .................................................................................................................................. 12 Creating a Descriptor Pool............................................................................................................................................ 14 Allocating Descriptor Sets ............................................................................................................................................ 15 Updating Descriptor Sets.............................................................................................................................................. 15 Creating a Pipeline Layout ............................................................................................................................................ 17 Binding Descriptor Sets ................................................................................................................................................ 18 Accessing Descriptors in Shaders ................................................................................................................................. 19 Tutorial06 Execution ........................................................................................................................................................ 20 Cleaning Up ...................................................................................................................................................................... 20 Conclusion ........................................................................................................................................................................ 21

API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

  • Upload
    others

  • View
    11

  • Download
    0

Embed Size (px)

Citation preview

Page 1: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

APIwithoutSecrets:IntroductiontoVulkan*Part6

TableofContentsTutorial6:DescriptorSets—UsingTexturesinShaders.........................................................................................................2

CreatinganImage..............................................................................................................................................................2

AllocatingImageMemory..............................................................................................................................................4

CreatingImageView......................................................................................................................................................6

CopyingDatatoanImage..............................................................................................................................................6

CreatingaSampler...........................................................................................................................................................10

UsingDescriptorSets.......................................................................................................................................................11

CreatingaDescriptorSetLayout..................................................................................................................................12

CreatingaDescriptorPool............................................................................................................................................14

AllocatingDescriptorSets............................................................................................................................................15

UpdatingDescriptorSets..............................................................................................................................................15

CreatingaPipelineLayout............................................................................................................................................17

BindingDescriptorSets................................................................................................................................................18

AccessingDescriptorsinShaders.................................................................................................................................19

Tutorial06Execution........................................................................................................................................................20

CleaningUp......................................................................................................................................................................20

Conclusion........................................................................................................................................................................21

Page 2: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

Tutorial6:DescriptorSets—UsingTexturesinShadersWeknowhowtocreateagraphicspipelineandhowtouseshaderstodrawgeometryonscreen.Wehavealsolearned

howtocreatebuffersandusethemasasourceofvertexdata(vertexbuffers).Nowweneedtoknowhowtoprovidedatatoshaders—wewillseehowtouseresourceslikesamplersandimagesinsideshadersourcecodeandhowtosetupaninterfacebetweentheapplicationandtheprogrammableshaderstages.

Inthistutorial,wewillfocusonafunctionalitythatissimilartoOpenGL*textures.ButinVulkan*therearenosuchobjects.Wehaveonlytworesourcetypesinwhichwecanstoredata:buffersandimages(therearealsopushconstants,butwewill cover them inadedicated tutorial).Eachof themcanbeprovided toshaders, inwhichcasewecall suchresourcesdescriptors,butwecan’tprovidethemtoshadersdirectly.Instead,theyareaggregatedinwrapperorcontainerobjectscalleddescriptorsets.Wecanplacemultipleresourcesinasingledescriptorsetbutweneedtodoitaccordingtoapredefinedstructureofsuchset.Thisstructuredefinesthecontentsofasingledescriptorset—typesofresourcesthatareplacedinsideit,numberofeachoftheseresourcetypes,andtheirorder.Thisdescriptionisspecifiedinsideobjectsnameddescriptorsetlayouts.Similardescriptionsneedtobespecifiedwhenwewriteshaderprograms.TogethertheyformaninterfacebetweenAPI(ourapplication)andtheprogrammablepipeline(shaders).

Whenwehavepreparedalayout,andcreatedadescriptorset,wecanfillit;inthiswaywedefinespecificobjects(buffersand/orimages)thatwewanttouseinshaders.Afterthat,beforeissuingdrawingcommandsinsideacommandbuffer,weneedtobindsuchasettothecommandbuffer.Thisallowsustousetheresourcesfrominsidetheshadersourcecode;forexample,fetchdatafromasampledimage(atexture),orreadavalueofauniformvariablestoredinauniformbuffer.

Inthispartofthetutorial,wewillseehowtocreatedescriptorsetlayoutsanddescriptorsetsthemselves.Wewillalsoprepareasamplerandanimagesowecanmakethemavailableasatextureinsideshaders.Wewillalsolearnhowwecanusetheminsideshaders.

Asmentionedpreviously,thistutorialisbasedontheknowledgepresentedinallthepreviouspartsoftheAPIwithoutSecrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics arepresented.

CreatinganImageWestartbycreatinganimagethatwillactasourtexture.Imagesrepresentacontinuousareaofmemory,whichis

interpretedaccordingtotherulesdefinedduringimagecreation.InVulkan,wehaveonlythreebasicimagetypes:1D,2D,and3D.Imagesmayhavemipmaps(levelsofdetail),manyarraylayers(atleastoneisrequired),orsamplesperframe.All these parameters are specified during image creation. In the code sample, we create the most commonly usedtwo-dimensionalimage,withonesampleperpixelandthefourRGBAcomponents.

VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; nullptr, // const void *pNext 0, // VkImageCreateFlags flags VK_IMAGE_TYPE_2D, // VkImageType imageType VK_FORMAT_R8G8B8A8_UNORM, // VkFormat format { // VkExtent3D extent width, // uint32_t width height, // uint32_t height 1 // uint32_t depth }, 1, // uint32_t mipLevels 1, // uint32_t arrayLayers VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling VK_IMAGE_USAGE_TRANSFER_DST_BIT | // VkImageUsageFlags usage VK_IMAGE_USAGE_SAMPLED_BIT, VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode

Page 3: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

0, // uint32_t queueFamilyIndexCount

nullptr, // const uint32_t *pQueueFamilyIndices VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout }; return vkCreateImage( GetDevice(), &image_create_info, nullptr, image ) ==

VK_SUCCESS; 1. Tutorial06.cpp,functionCreateImage()

TocreateanimageweneedtoprepareastructureoftypeVkImageCreateInfo.Thisstructurecontainsthebasicsetofparametersrequiredtocreateanimage.Theseparametersarespecifiedthroughthefollowingmembers:

• sType–Typicaltypeofthestructure.ItmustbeequaltoaVK_STRUCTURE_TYPE_IMAGE_CREATE_INFOvalue.• pNext–Pointerreservedforextensions.• flags–Parameterthatdescribesadditionalpropertiesofanimage.Throughthisparameterwecanspecify

thattheimagecanbebackedbyasparsememory.ButamoreinterestingvalueisaVK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT,whichallowsustousetheimageasacubemap.Ifwedon’thaveadditionalrequirements,wecansetthisparameterto0.

• imageType–Basictype(numberofdimensions)oftheimage:1D,2D,or3D.• format–Formatoftheimage:numberofitscomponents,numberofbitsforeachcomponent,andadata

type.• extent–Sizeoftheimage(numberoftexels/pixels)ineachdimension.• mipLevels–Numberoflevelsofdetail(mipmaps).• arrayLayers–Numberofarraylayers.• samples–Numberofpertexelsamples(onefornormalimagesandmorethanoneformultisampledimages).• tiling–Definestheinnermemorystructureoftheimage:linearoroptimal.• usage–Definesallthewaysinwhichwewanttouseanimageduringitsoveralllifetime.• sharingMode–Specifieswhetheranimagewillbeaccessedbyqueuesfrommultiplefamiliesatatime(the

sameasthesharingModeparameterusedduringswapchainorbuffercreation).• queueFamilyIndexCount–NumberofelementsinapQueueFamilyIndicesarray(usedonlywhenconcurrent

sharingmodeisspecified).• pQueueFamilyIndices–Arraywithindicesofallqueuefamiliesfromwhichqueueswillaccessanimage(used

onlywhenconcurrentsharingmodeisspecified).• initialLayout–Memorylayoutimagewillbecreatedwith.Wecanonlyprovideanundefinedorpreinitialized

layout.Wealsoneedtoperformalayouttransitionbeforewecanuseanimageinsidecommandbuffers.

Mostoftheparametersdefinedduringimagecreationarequiteself-explanatoryorsimilartoparametersusedduringcreationofotherresources.Butthreeparametersrequireadditionalexplanation.

Tilingdefinestheinnermemorystructureofanimage(butdon’tconfuseitwithalayout).Imagesmayhavelinearoroptimaltiling(buffersalwayshavelineartiling).Imageswithlineartilinghavetheirtexelslaidoutlinearly,onetexelafteranother,onerowafteranother,andsoon.Wecanqueryforalltherelevantimage’smemoryparameters(offsetandsize,row,array,anddepthstride).Thiswayweknowhowtheimage’scontentsarekeptinmemory.Suchtilingcanbeusedtocopydatatoanimagedirectly(bymappingtheimage’smemory).Unfortunately,therearesevererestrictionsonimageswith linear tiling.Forexample, theVulkanspecificationsays thatonly2D imagesmustsupport linear tiling.Hardwarevendorsmayimplementsupportforlineartilinginotherimagetypes,butthisisnotobligatory,andwecan’trelyonsuchsupport.But,what’smoreimportant,linearlytiledimagesmayhaveworseperformancethantheiroptimalcounterparts.

Whenwespecifyanoptimaltilingfor images, itmeansthatwedon’tknowhowtheirmemory isstructured.Eachplatform we execute our application on may keep an image’s contents in a totally different way, so it’s practically

Page 4: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

impossibletomapanimage’smemoryandcopyittoorfromtheCPUdirectly(weneedtouseastagingresource,abufferoran image).But thiswaywecancreatewhatever imageswewant (thereareno restrictions similar to linearly tiledimages)andourapplicationwillhavebetterperformance.That’swhyitisstronglysuggestedtoalwaysspecifyoptimaltilingforimages.

Nowlet’sfocusonaninitialLayoutparameter.Layout,asitwasdescribedinatutorialaboutswapchains,definesanimage’smemorylayoutandisstrictlyconnectedwiththewayinwhichwewanttouseanimage.Eachspecificusagehasitsownmemorylayout.Beforewecanuseanimageinagivenwayweneedtoperformalayouttransition.Forexample,swapchainimagescanbedisplayedonscreenonlyinVK_IMAGE_LAYOUT_PRESENT_SRC_KHRlayout.Whenwewanttorenderintoanimage,weneedtosetitsmemorylayouttoVK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL.Thereisalsoagenerallayoutthatallowsustouseimagesanywaywewantto,butasitimpactsperformance,it’suseisstronglydiscouraged(useonlywhenreallynecessary).

Now,whenwewanttochangethewayinwhichanimageisused,weneedtoperformtheabove-mentionedlayouttransition.Wemustspecifyacurrent(old)layoutandanewone.Theoldlayoutcanhaveoneoftwovalues:currentimagelayoutoranundefinedlayout.Whenwespecifythevalueofacurrentimage’slayout,theimagecontentsarepreservedduringtransition.Butwhenwedon’tneedanimage’scontents,wecanprovideanundefinedlayout.Inthiswaylayouttransitionmaybeperformedfaster.

And this is when the initialLayout parameter comes in. We can specify only two values for it—undefined orpreinitialized.Thepreinitialized layoutvalueallowsus topreservean image’scontentsduring the image’s first layouttransition.Thiswaywecancopydatatoanimagewithmemorymapping;butthisisquiteimpractical.Wecanonlycopydata directly (through memory mapping) to images with linear tiling, which have restrictions as mentioned above.Practicallyspeaking,theseimagescanonlybeusedasstagingresources—fortransferringdatabetweenGPUandCPU.Butforthispurposewecanalsousebuffers;that’swhyitismucheasiertocopydatausingabufferthanusinganimagewithlineartiling.

Allthisleadstotheconclusionthat,inmostcases,anundefinedlayoutcanbeusedforaninitialLayoutparameter.Insuchacase,animage’scontentscannotbeinitializeddirectly(bymappingitsmemory).Butifwewantto,wecancopydatatosuchanimagebyusingastagingbuffer.Thatapproachispresentedinthistutorial.

Onelastthingweneedtorememberistheusage.Similartobuffers,whenwecreateanimageweneedtodesignateALLthewaysinwhichweintendtousetheimage.Wecan’tchangeit laterandwecan’tusetheimageinawaythatwasn’tspecifiedduringitscreation.Here,wewanttouseanimageasatextureinsideshaders.ForthispurposewespecifytheVK_IMAGE_USAGE_SAMPLED_BITusage.Wealsoneedawaytouploaddatatotheimage.Wearegoingtoreaditfromanimagefileandcopyittotheimageobject.Thiscanbedonebytransferringdatausingastagingresource.Insucha case, the image will be a target of a transfer operation; that’s why we also specify theVK_IMAGE_USAGE_TRANSFER_DST_BITusage.

Now,whenwehave provided values for all the parameters,we can create an image. This is done by calling thevkCreateImage()functionforwhichweneedtoprovideahandleofalogicaldevice,apointertothestructuredescribedabove,andapointertoavariableoftypeVkImageinwhichthehandleofthecreatedimagewillbestored.

AllocatingImageMemorySimilartobuffers,imagesdon’thavetheirownmemory,sobeforewecanuseimagesweneedtobindmemoryto

them.Todothis,wefirstneedtoknowwhatthepropertiesofmemorythatcanbeboundtoanimageare.WedothisbycallingthevkGetImageMemoryRequirements()function.

VkMemoryRequirements image_memory_requirements; vkGetImageMemoryRequirements( GetDevice(), Vulkan.Image.Handle,

&image_memory_requirements ); 2. Tutorial06.cpp,functionAllocateImageMemory()

Page 5: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

Theabovecallstorestherequiredmemoryparametersinanimage_memory_requirementsvariable.Thistellsushowmuchmemoryweneedandwhichmemorytypesupportedbyagivenphysicaldevicecanbeusedforanimage’smemoryallocation.Ifwedon’tknowwhatmemorytypesaresupportedbyagivenphysicaldevicewecanlearnaboutthembycalling thevkGetPhysicalDeviceMemoryProperties() function. Thiswas covered in aprevious tutorial,whenwewereallocatingmemoryforabuffer.Next,wecaniterateoveravailablememorytypesandcheckwhicharecompatiblewithourimage.

for( uint32_t i = 0; i < memory_properties.memoryTypeCount; ++i ) { if( (image_memory_requirements.memoryTypeBits & (1 << i)) && (memory_properties.memoryTypes[i].propertyFlags & property) ) { VkMemoryAllocateInfo memory_allocate_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType nullptr, // const void *pNext image_memory_requirements.size, // VkDeviceSize allocationSize i // uint32_t memoryTypeIndex }; if( vkAllocateMemory( GetDevice(), &memory_allocate_info, nullptr, memory ) ==

VK_SUCCESS ) { return true; } } } return false;

3. Tutorial06.cpp,functionAllocateImageMemory()

Eachmemorytypehasaspecificsetofproperties.Whenwewanttobindmemorytoanimage,wecanhaveourownspecificrequirementstoo.Forexample,wemayneedtoaccessmemorydirectly,bymappingit,sosuchmemorymustbehost-visible.Ifwehaveadditionalrequirementswecancomparethemwiththepropertiesofeachavailablememorytype.When we find the match, we can use a given memory type and allocate a memory object from it by calling thevkAllocateMemory()function.

Afterthat,weneedtobindsuchmemorytoourimage.WedothisbycallingthevkBindImageMemory()functionandprovidingthehandleofanimagetowhichwewanttobindmemory,ahandleofamemoryobject,andanoffsetfromthebeginningofthememoryobject,likethis:

if( vkBindImageMemory( GetDevice(), Vulkan.Image.Handle, Vulkan.Image.Memory, 0 ) != VK_SUCCESS ) {

std::cout << "Could not bind memory to an image!" << std::endl; return false; }

4. Tutorial06.cpp,functionCreateTexture()

Offsetvalueisveryimportantwhenwebindmemorytoanobject.ResourcesinVulkanhavespecificrequirementsformemoryoffsetalignment.Informationabouttherequirementsisalsoavailableintheimage_memory_requirementsvariable.Theoffsetthatweprovidewhenwebindamemorymustbeamultipleofthevariable’salignmentmember.Zeroisalwaysavalidoffsetvalue.

Ofcourse,whenwewanttobindamemorytoanimage,wedon’tneedtocreateanewmemoryobjecteachtime.Itismoreoptimaltocreateasmallnumberoflargermemoryobjectsandbindpartsofthembyprovidingaproperoffsetvalue.

Page 6: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

CreatingImageViewWhenwewanttouseanimageinourapplicationwerarelyprovidetheimage’shandle.Imageviewsareusuallyused

instead.Theyprovideanadditionallayerthatinterpretsthecontentsofanimageforthepurposeofusingitinaspecificcontext.Forexample,wemayhaveamultilayerimage(2Darray)andwewanttorenderonlytoaspecificarraylayer.Todothiswecreateanimageviewinwhichwedefinethelayerwewanttouse.Anotherexampleisanimagewithsixarraylayers.Usingimageviews,wecaninterpretitasacubemap.

CreationofimageviewswasdescribedinIntroductiontoVulkanPart3:FirstTriangle,soIwillprovideonlythesourcecodeusedinthispart.

VkImageViewCreateInfo image_view_create_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType nullptr, // const void *pNext 0, // VkImageViewCreateFlags flags image_parameters.Handle, // VkImage image VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType VK_FORMAT_R8G8B8A8_UNORM, // VkFormat format { // VkComponentMapping components VK_COMPONENT_SWIZZLE_IDENTITY, // VkComponentSwizzle r VK_COMPONENT_SWIZZLE_IDENTITY, // VkComponentSwizzle g VK_COMPONENT_SWIZZLE_IDENTITY, // VkComponentSwizzle b VK_COMPONENT_SWIZZLE_IDENTITY // VkComponentSwizzle a }, { // VkImageSubresourceRange

subresourceRange VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask 0, // uint32_t

baseMipLevel 1, // uint32_t levelCount 0, // uint32_t

baseArrayLayer 1 // uint32_t layerCount } }; return vkCreateImageView( GetDevice(), &image_view_create_info, nullptr,

&image_parameters.View ) == VK_SUCCESS; 5. Tutorial06.cpp,functionCreateImageView()

CopyingDatatoanImageNowweneedtocopydatatoourimage.Wedothisbyusingastagingbuffer.Wefirstcreateabufferbigenoughto

holdourimagedata.Next,weallocatememorythatishost-visible(thatcanbemapped),andbindittothebuffer.Then,wecopydatatothebuffer’smemorylikethis:

// Prepare data in staging buffer void *staging_buffer_memory_pointer; if( vkMapMemory( GetDevice(), Vulkan.StagingBuffer.Memory, 0, data_size, 0,

&staging_buffer_memory_pointer ) != VK_SUCCESS ) { std::cout << "Could not map memory and upload texture data to a staging buffer!" <<

std::endl; return false; } memcpy( staging_buffer_memory_pointer, texture_data, data_size ); VkMappedMemoryRange flush_range = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, // VkStructureType sType nullptr, // const void *pNext Vulkan.StagingBuffer.Memory, // VkDeviceMemory memory

Page 7: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

0, // VkDeviceSize offset data_size // VkDeviceSize size }; vkFlushMappedMemoryRanges( GetDevice(), 1, &flush_range ); vkUnmapMemory( GetDevice(), Vulkan.StagingBuffer.Memory );

6. Tutorial06.cpp,functionCopyTextureData()

Wemapthebuffer’smemory.ThisoperationgivesusapointerthatcanbeusedthewaythatallotherC++pointersareused.Wecopytexturedatatoitandinformthedriverwhichpartsofthebuffer’smemorywerechangedduringthisoperation(weflushthememory).Attheend,weunmapthememory,butthisisnotnecessary.

Imagedataisreadfromafilewiththefollowingcode:

int width = 0, height = 0, data_size = 0; std::vector<char> texture_data = Tools::GetImageData( "Data06/texture.png", 4,

&width, &height, nullptr, &data_size ); if( texture_data.size() == 0 ) { return false; } if( !CopyTextureData( &texture_data[0], data_size, width, height ) ) { std::cout << "Could not upload texture data to device memory!" << std::endl; return false; }

7. Tutorial06.cpp,functionCreateTexture()

Forthepurposeofthistutorialwewillusethefollowingimageasatexture:

Theoperationofcopyingdatafromabuffertoanimagerequiresrecordingacommandbufferandsubmittingittoaqueue.CallingthevkBeginCommandBuffer()functionstartstherecordingoperation:

// Prepare command buffer to copy data from staging buffer to a vertex buffer VkCommandBufferBeginInfo command_buffer_begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // VkStructureType

sType nullptr, // const void

*pNext VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, // VkCommandBufferUsageFlags

flags

Page 8: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

nullptr // const VkCommandBufferInheritanceInfo *pInheritanceInfo

}; VkCommandBuffer command_buffer = Vulkan.RenderingResources[0].CommandBuffer; vkBeginCommandBuffer( command_buffer, &command_buffer_begin_info);

8. Tutorial06.cpp,functionCopyTextureData()

Atthebeginningofthecommandbufferrecordingweneedtoperformalayouttransitiononourimage.WewanttocopydatatotheimagesoweneedtochangeitslayouttoaVK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL.WeneedtodothisexplicitlyusinganimagememorybarrierandcallingthevkCmdPipelineBarrier()function:

VkImageSubresourceRange image_subresource_range = { VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask 0, // uint32_t baseMipLevel 1, // uint32_t levelCount 0, // uint32_t baseArrayLayer 1 // uint32_t layerCount }; VkImageMemoryBarrier image_memory_barrier_from_undefined_to_transfer_dst = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType nullptr, // const void *pNext 0, // VkAccessFlags srcAccessMask VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout VK_QUEUE_FAMILY_IGNORED, // uint32_t

srcQueueFamilyIndex VK_QUEUE_FAMILY_IGNORED, // uint32_t

dstQueueFamilyIndex Vulkan.Image.Handle, // VkImage image image_subresource_range // VkImageSubresourceRange

subresourceRange }; vkCmdPipelineBarrier( command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,

VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier_from_undefined_to_transfer_dst);

9. Tutorial06.cpp,functionCopyTextureData()

Next,wecancopythedataitself.Todothisweneedtoprovideparametersdescribingbothasourceandadestinationfor the data:which parts of the imagewewant to update (imageSubresourcemember), a specific regionwithin theprovidedpart(imageOffset),andthetotalsizeoftheimage.Forthesourceofthedataweneedtoprovideanoffsetfromthebeginningofabuffer’smemorywherethedatastarts,andhowthisdataisstructured,andthesizeofanimaginaryimageinsidethebuffer(thesizeofitsrowsandcolumns).Fortunately,wecanstoreourdatainsuchawaythatitfitsourimage.Thisallowsustosetazerovalueforbothparameters(bufferRowLengthandbufferImageHeight),specifyingthatthedataistightlypackedaccordingtotheimagesize.

VkBufferImageCopy buffer_image_copy_info = { 0, // VkDeviceSize bufferOffset 0, // uint32_t bufferRowLength 0, // uint32_t bufferImageHeight { // VkImageSubresourceLayers imageSubresource VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask 0, // uint32_t mipLevel 0, // uint32_t baseArrayLayer 1 // uint32_t layerCount

Page 9: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

}, { // VkOffset3D imageOffset 0, // int32_t x 0, // int32_t y 0 // int32_t z }, { // VkExtent3D imageExtent width, // uint32_t width height, // uint32_t height 1 // uint32_t depth } }; vkCmdCopyBufferToImage( command_buffer, Vulkan.StagingBuffer.Handle,

Vulkan.Image.Handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_image_copy_info ); 10. Tutorial06.cpp,functionCopyTextureData()

Onelastthingistoperformanotherlayouttransition.Ourimagewillbeusedasatextureinsideshaders,soweneedto transition it toaVK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL layout.After that,wecanendourcommandbuffer,submitittoaqueue,andwaitforthetransfertocomplete(inareal-lifeapplication,weshouldskipwaitingandsynchronizeoperationsinsomeotherway;forexample,usingsemaphores,toavoidunnecessarypipelinestalls).

VkImageMemoryBarrier image_memory_barrier_from_transfer_to_shader_read = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType nullptr, // const void *pNext VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags

srcAccessMask VK_ACCESS_SHADER_READ_BIT, // VkAccessFlags

dstAccessMask VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, // VkImageLayout newLayout VK_QUEUE_FAMILY_IGNORED, // uint32_t

srcQueueFamilyIndex VK_QUEUE_FAMILY_IGNORED, // uint32_t

dstQueueFamilyIndex Vulkan.Image.Handle, // VkImage image image_subresource_range // VkImageSubresourceRange

subresourceRange }; vkCmdPipelineBarrier( command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT,

VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier_from_transfer_to_shader_read);

vkEndCommandBuffer( command_buffer ); // Submit command buffer and copy data from staging buffer to a vertex buffer VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType nullptr, // const void *pNext 0, // uint32_t

waitSemaphoreCount nullptr, // const VkSemaphore

*pWaitSemaphores nullptr, // const VkPipelineStageFlags

*pWaitDstStageMask; 1, // uint32_t

commandBufferCount &command_buffer, // const VkCommandBuffer

*pCommandBuffers 0, // uint32_t

signalSemaphoreCount

Page 10: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

nullptr // const VkSemaphore *pSignalSemaphores

}; if( vkQueueSubmit( GetGraphicsQueue().Handle, 1, &submit_info, VK_NULL_HANDLE ) !=

VK_SUCCESS ) { return false; } vkDeviceWaitIdle( GetDevice() );

11. Tutorial06.cpp,functionCopyTextureData()

Nowourimageiscreatedandfullyinitialized(containsproperdata).Butwearenotyetdonepreparingourtexture.

CreatingaSamplerInOpenGL,whenwecreateda texture,both the imageand its samplingparametershad tobe specified. In later

versionsofOpenGLwecouldalsocreateseparatesamplerobjects.Insideashader,weusuallycreatedvariablesoftypesampler2D,whichalsocombinedbothimagesandtheirsamplingparameters(samplers).InVulkan,weneedtocreateimagesandsamplersseparately.

Samplersdefinethewayinwhichimagedataisreadinsideshaders:whetherfilteringisenabled,whetherwewanttousemipmaps(ormaybeaspecificsubrangeofmipmaps),orwhatkindofaddressingmodewewanttouse(clampingorwrapping).

VkSamplerCreateInfo sampler_create_info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, // VkStructureType sType nullptr, // const void* pNext 0, // VkSamplerCreateFlags flags VK_FILTER_LINEAR, // VkFilter magFilter VK_FILTER_LINEAR, // VkFilter minFilter VK_SAMPLER_MIPMAP_MODE_NEAREST, // VkSamplerMipmapMode mipmapMode VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeU VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeV VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeW 0.0f, // float mipLodBias VK_FALSE, // VkBool32 anisotropyEnable 1.0f, // float maxAnisotropy VK_FALSE, // VkBool32 compareEnable VK_COMPARE_OP_ALWAYS, // VkCompareOp compareOp 0.0f, // float minLod 0.0f, // float maxLod VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,// VkBorderColor borderColor VK_FALSE // VkBool32

unnormalizedCoordinates }; return vkCreateSampler( GetDevice(), &sampler_create_info, nullptr, sampler ) ==

VK_SUCCESS; 12. Tutorial06.cpp,functionCreateSampler()

AlltheaboveparametersaredefinedthroughvariablesoftypeVkSamplerCreateInfo.Ithasmanymembers:

• sType–Typeofthestructure.ItshouldbeequaltoaVK_STRUCTURE_TYPE_SAMPLER_CREATE_INFOvalue.• pNext–Pointerreservedforextensions.• flags–Mustbesettozero.Thisparameterisreservedforfutureuse.• magFilter–Typeoffiltering(nearestorlinear)usedformagnification.• minFilter–Typeoffiltering(nearestorlinear)usedforminification.

Page 11: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

• mipmapMode–Typeoffiltering(nearestorlinear)usedformipmaplookup.• addressModeU–AddressingmodeforUcoordinatesthatareoutsideofa<0.0;1.0>range.• addressModeV–AddressingmodeforVcoordinatesthatareoutsideofa<0.0;1.0>range.• addressModeW–AddressingmodeforWcoordinatesthatareoutsideofa<0.0;1.0>range.• mipLodBias–Valueofbiasaddedtomipmap’slevelofdetailcalculations.Ifwewanttooffsetfetchingdata

fromaspecificmipmap,wecanprovideavalueotherthan0.0.• anisotropyEnable–Parameterdefiningwhetheranisotropicfilteringshouldbeused.• maxAnisotropy–Maximalallowedvalueusedforanisotropicfiltering(clampingvalue).• compareEnable–Enablescomparisonagainstareferencevalueduringtexturelookups.• compareOp–TypeofcomparisonperformedduringlookupsifthecompareEnableparameterissettotrue.• minLod–Minimalallowedlevelofdetailusedduringdatafetching.Ifcalculatedlevelofdetail(mipmaplevel)

islowerthanthisvalue,itwillbeclamped.• maxLod–Maximalallowedlevelofdetailusedduringdatafetching.Ifthecalculatedlevelofdetail(mipmap

level)isgreaterthanthisvalue,itwillbeclamped.• borderColor–Specifiespredefinedcolorofborderpixels.Bordercolorisusedwhenaddressmodeincludes

clampingtobordercolors.• unnormalizedCoordinates–Usually(whenthisparameterissettofalse)weprovidetexturecoordinatesusing

anormalized<0.0;1.0>range.Whenset to true, thisparameterallowsus tospecify thatwewant touseunnormalized coordinates and address texture using texels (in a <0; texturedimension> range, similar toOpenGL’srectangletextures).

SamplerobjectiscreatedbycallingthevkCreateSampler()function,forwhichweprovideapointertothestructuredescribedabove.

UsingDescriptorSetsWecreatedanimage,boundamemorytoit,andweevenuploadeddatatotheimage.Wealsocreatedasamplerto

setupsamplingparameters forourtexture.Nowwewanttousethetexture.Howcanwedothis?Wedo it throughdescriptorsets.

Asmentionedatthebeginning,resourcesusedinsideshadersarecalleddescriptors.InVulkanwehave11typesofdescriptors:

• Samplers–Definethewayimagedataisread.Insideshaders,samplerscanbeusedwithmultipleimages.• Sampledimages–Defineimagesfromwhichwecanreaddatainsideshaders.Wecanreaddatafromasingle

imageusingdifferentsamplers.• Combinedimagesamplers–Thesedescriptorscombinebothsamplerandsampledimageasoneobject.From

theAPIperspective (ourapplication),westillneedtocreatebothasamplerandan image,but inside theshadertheyappearasasingleobject.Usingthemmaybemoreoptimal(mayhavebetterperformance)thanusingseparatesamplersandsampledimages.

• Storageimages–Thisdescriptorallowsustobothreadandstoredatainsideanimage.• Inputattachments–Thisaspecificusageofrenderpass’sattachments.Whenwewanttoreaddatafroman

imagewhich is used as an attachment inside the same render pass, we can only do it through an inputattachment.Thiswaywedonotneedtoendarenderpassandstartanotherone,butwearerestrictedtoonly fragment shaders, and to only a single location per fragment shader instance (a given instance of afragmentshadercanreaddatafromcoordinatesassociatedwiththefragmentshader’scoordinates).

• Uniformbuffers(andtheirdynamicvariation)–Uniformbuffersallowustoreaddatafromuniformvariables.InVulkan,suchvariablescannotbeplacedinsidetheglobalscope;weneedtouseuniformbuffers.

• Storagebuffers(andtheirdynamicvariation)–Storagebuffersallowustobothreadandstoredatainsidevariables.

Page 12: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

• Uniformtexelbuffers–Theseallowthecontentsofbufferstobetreatedasiftheycontaintexturedata,theyareinterpretedastexelswithaselectednumberofcomponentsandformat.Inthisway,wecanaccessverylargearraysofdata(muchlargerthanuniformbuffers).

• Storagetexelbuffers–Thesearesimilartouniformtexelbuffers.Notonlycantheybeusedforreading,buttheycanalsobeusedforstoringdata.

Alloftheabovedescriptorsarecreatedfromsamplers,images,orbuffers.Thedifferenceisinthewaythatweusethem and access inside shaders. All additional parameters of such access may have performance implications. Forexample,withstoragebufferswecanonlyreaddata,butreadingdataisprobablymuchfasterthanstoringdatainsidestoragebuffers.Similarly,texelbuffersallowustoaccessmoreelementsthanwithuniformbuffers,butthismayalsocomewiththecostofworseperformance.Weshouldremembertoselectadescriptorthatfitsourneeds.

Inthistutorialwewanttouseatexture.Forthispurposewecreatedanimageandasampler.Wewillusebothtoprepareacombinedimagesamplerdescriptor.

CreatingaDescriptorSetLayoutPreparing resources tobeusedby shaders shouldbeginwith creatingadescriptor set layout.Descriptor setsare

opaqueobjectsinwhichwestorehandlesofresources.Layoutsdefinethestructureofdescriptorsets—whattypesofdescriptorstheycontain,howmanydescriptorsofeachtypethereare,andwhattheirorderis.

Descriptorsetlayoutcreationstartsbydefiningtheparametersofalldescriptorsavailableinagivenset.ThisisdonebyfillingastructurevariableoftypeVkDescriptorSetLayoutBinding:

VkDescriptorSetLayoutBinding layout_binding = { 0, // uint32_t binding VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // VkDescriptorType descriptorType 1, // uint32_t descriptorCount VK_SHADER_STAGE_FRAGMENT_BIT, // VkShaderStageFlags stageFlags nullptr // const VkSampler

*pImmutableSamplers };

13. Tutorial06.cpp,functionCreateDescriptorSetLayout()

Page 13: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

Theabovedescriptioncontainsthefollowingmembers:

• binding–Indexofadescriptorwithinagivenset.Alldescriptorsfromasinglelayout(andset)musthaveauniquebinding.Thissamebindingisalsousedinsideshaderstoaccessadescriptor.

• descriptorType–Thetypeofadescriptor(sampler,uniformbuffer,andsoon.)• descriptorCount–Numberofdescriptorsofaselectedtypeaccessedasanarray.Forasingledescriptor,1

valueshouldbeused.• stageFlags – Set of flags defining all shader stages thatwill have access to a given descriptor. For better

performance,weshouldspecifyonlythosestagesthatwillaccessthegivenresource.• pImmutableSamplers–Affectsonlysamplersthatshouldbepermanentlyboundintothelayout(andcannot

bechangedlater).Butwedon’thavetoworryaboutthisparameter,andwecanbindsamplersasanyotherdescriptorsbysettingthisparametertonull.

Inourexample,wewanttouseonlyonedescriptorofacombinedimagesampler,whichwillbeaccessedonlybyafragmentshader.Itwillbethefirst(bindingzero)descriptorinagivenlayout.Toavoidwastingmemory,weshouldkeepbindingsascompactlyaspossible(asclosetozeroaspossible),becausedriversmayallocatememoryfordescriptorslotseveniftheyarenotused.

Wecanpreparesimilarparametersforotherdescriptorsaccessedfromasingleset.Then,pointerstosuchvariablesareprovidedtoavariableoftypeVkDescriptorSetLayoutCreateInfo:

VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, // VkStructureType

sType nullptr, // const void

*pNext 0, //

VkDescriptorSetLayoutCreateFlags flags 1, // uint32_t

bindingCount &layout_binding // const

VkDescriptorSetLayoutBinding *pBindings }; if( vkCreateDescriptorSetLayout( GetDevice(), &descriptor_set_layout_create_info,

nullptr, &Vulkan.DescriptorSet.Layout ) != VK_SUCCESS ) { std::cout << "Could not create descriptor set layout!" << std::endl; return false; }

14. Tutorial06.cpp,functionCreateDescriptorSetLayout()

Thisstructurecontainsjustafewmembers:

• sType – The type of the structure. It should be equal toVK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO.

• pNext–Pointerreservedforextensions.• flags–Thisparameterallowsustoprovideadditionaloptionsforlayoutcreation.Butastheyareconnected

withusingextensions,wecansetthisparametertozero.• bindingCount–Thenumberofbindings,elementsinthepBindingsarray.• pBindings–Apointertoanarraywithdescriptionsofallresourcesinagivenlayout.Thisarraymustbeno

smallerthanthevalueofthebindingCountparameter.

Afterwehavefilledinthestructure,wecancallthevkCreateDescriptorSetLayout()functiontocreateadescriptorsetlayout.Wewillneedthislayoutlater,multipletimes.

Page 14: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

CreatingaDescriptorPoolNextstepistoprepareadescriptorset.Descriptorsets,similartocommandbuffers,arenotcreateddirectly;theyare

insteadallocatedfrompools.Beforewecanallocateadescriptorset,weneedtocreateadescriptorpool.

VkDescriptorPoolSize pool_size = { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // VkDescriptorType

type 1 // uint32_t

descriptorCount }; VkDescriptorPoolCreateInfo descriptor_pool_create_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, // VkStructureType

sType nullptr, // const void

*pNext 0, // VkDescriptorPoolCreateFlags

flags 1, // uint32_t

maxSets 1, // uint32_t

poolSizeCount &pool_size // const VkDescriptorPoolSize

*pPoolSizes }; if( vkCreateDescriptorPool( GetDevice(), &descriptor_pool_create_info, nullptr,

&Vulkan.DescriptorSet.Pool ) != VK_SUCCESS ) { std::cout << "Could not create descriptor pool!" << std::endl; return false; }

15. Tutorial06.cpp,functionCreateDescriptorPool()

Creatingadescriptorpoolinvolvesspecifyinghowmanydescriptorsetscanbeallocatedfromit.Atthesametime,wealsoneedtospecifywhattypesofdescriptors,andhowmanyofthemcanbeallocatedfromthepoolacrossallsets.Forexample,let’simaginethatwewanttoallocateasinglesampledimageandasinglestoragebufferfromagivenpool,andthatwecanallocatetwodescriptorsets fromthepool.Whendoingthis, ifweallocateonedescriptorsetwithasampledimage,theseconddescriptorcancontainonlyastoragebuffer.Ifasingledescriptorsetallocatedfromthatpoolcontainsbothresources,wecan’tallocateanothersetbecauseitwouldhavetobeempty.Duringdescriptorpoolcreationwedefinethetotalnumberofdescriptorsandtotalnumberofsetsthatcanbeallocatedfromit.Thisisdoneintwosteps.

First,wepreparevariablesoftypeVkDescriptorPoolSizethatspecifythetypeofadescriptorandthetotalnumberofdescriptorsofaselectedtypethatcanbeallocatedfromthepool.Next,weprovideanarrayofsuchvariablestoavariableoftypeVkDescriptorPoolCreateInfo.Itcontainsthefollowingmembers:

• sType – The type of the structure. In this case it should be set toVK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO.

• pNext–Pointerreservedforextensions.• flags – This parameter defines (when using a VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT

flag)whetherindividualsetsallocatedfromthepoolcanbefreedorresetseparately.Ifthisparameterissettozero,alldescriptorsetsallocatedfromthepoolcanonlyberesetatonce(inbulk)byresettingthewholepool.

• maxSets–Isthetotalnumberofsetsthatcanbeallocatedfromthepool.• poolSizeCount–DefinesthenumberofelementsinthepPoolSizesarray.

Page 15: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

• pPoolSizes–IsapointertoanarraycontainingnolessthanpoolSizeCountelementscontainingdescriptortypesandatotalnumberofdescriptorsofthattypethatcanbeallocatedfromthepool.

Inourexamplewewanttoallocateonlyasingledescriptorsetwithonlyonedescriptorofacombinedimagesamplertype. We prepare parameters according to our example and create a descriptor pool by calling thevkCreateDescriptorPool()function.

AllocatingDescriptorSetsNowwearereadytoallocatethedescriptorsetitself.Codethatdoesthisisquiteshort:

VkDescriptorSetAllocateInfo descriptor_set_allocate_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType

sType nullptr, // const void

*pNext Vulkan.DescriptorSet.Pool, // VkDescriptorPool

descriptorPool 1, // uint32_t

descriptorSetCount &Vulkan.DescriptorSet.Layout // const VkDescriptorSetLayout

*pSetLayouts }; if( vkAllocateDescriptorSets( GetDevice(), &descriptor_set_allocate_info,

&Vulkan.DescriptorSet.Handle ) != VK_SUCCESS ) { std::cout << "Could not allocate descriptor set!" << std::endl; return false; }

16. Tutorial06.cpp,functionAllocateDescriptorSet()

ToallocateadescriptorsetweneedtoprepareavariableofVkDescriptorSetAllocateInfotype,whichhasthefollowingmembers:

• sType – Standard type of the structure. For the purpose of descriptor set allocationwe need to set thismembertoavalueofVK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO.

• pNext–Pointerreservedforextensions.• descriptorPool–Handleofadescriptorpoolfromwhichthecommandbuffershouldbeallocated.• descriptorSetCount – Number of descriptor sets we want to allocate (and number of elements in the

pSetLayoutsmember).• pSetLayouts–PointertoanarraywithatleastdescriptorSetCountelements.Eachelementofthisarraymust

containadescriptorsetlayoutthatdefinestheinnerstructureoftheallocateddescriptorset(elementsmayrepeat;forexample,wecanallocatefivedescriptorsetsatonce,allwiththesamelayout).

Aswecanseeintheabovestructure,weneedtoprovidedescriptorsetlayouts.That’swhyweneededtocreatethemearlier.ToallocateaselectednumberofdescriptorsetsfromaprovidedpoolweneedtoprovideapointertotheabovestructuretothevkAllocateDescriptorSets()function.

UpdatingDescriptorSetsWepreparedadescriptorset,butitisempty;it’suninitialized.Nowweneedtofillitorupdateit.Thismeansthatwe

tellthedriverwhichresourcesshouldbeusedfordescriptorsinsidetheset.

Wecanupdateadescriptorsetintwoways:

• Bywritingtothedescriptorset—thiswayweprovidenewresources.

Page 16: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

• Bycopyingdatafromanotherdescriptorset—ifwehaveapreviouslyupdateddescriptorsetandifwealsowanttousesomeofitsdescriptorsinanotherdescriptorwecancopythem;thisapproachcanbemuchfasterthanwritingdescriptorsetsdirectlyfromtheCPU.

Aswedon’thaveanotherdescriptorset,weneedtowritetooursingledescriptorsetdirectly.Foreachdescriptortypeweneedtopreparetwostructures.One,commonforalldescriptortypes,istheVkWriteDescriptorSetstructure.Itcontainsthefollowingmembers:

• sType–Typeofthestructure.WeneedtouseaVK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SETvalue.• pNext–Pointerreservedforextensions.• dstSet–Handleofadescriptorsetthatwewanttoupdate(fillwithspecificresources).• dstBinding–Indexwithinthedescriptorsetthatwewanttoupdate.Wemustprovideoneofthebindings

specified during descriptor set layout creation.What’smore, the selected bindingmust correspond to aprovidedtypeofthedescriptor.

• dstArrayElement– Specifies the first array indexwewant toupdate.Using a singleVkWriteDescriptorSetstructure we can updatemultiple elements of a single array. Let’s say we have a four-element array ofsamplersandwewanttoupdatethelasttwo(withindices2and3);wecanprovidetwosamplersandupdatethearraystartingfromindex2.

• descriptorCount – Number of descriptors we want to update (number of elements in pImageInfo orpBufferInfo,orpTexelBufferViewarray).Forordinarydescriptorswesetthevaluetoone.Butforarrayswecanprovidelargervalues.

• descriptorType–Typeofthedescriptorwearegoingtoupdate.Itmustbethesameasthedescriptortypeprovidedduringdescriptorsetlayoutcreationwiththesamebinding(indexwithinadescriptorset).

• pImageInfo–PointertoanarraywithatleastdescriptorCountelementsoftypeVkDescriptorImageInfo.Eachsuch element must contain handles of specific resources when we want to updateVK_DESCRIPTOR_TYPE_SAMPLER, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, orVK_DESCRIPTOR_TYPE_INPUT_ATTACHMENTdescriptors.

• pBufferInfo–PointertoanarraywithatleastdescriptorCountelementsoftypeVkDescriptorBufferInfo.Eachsuch element must contain handles of specific resources when we want to updateVK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, orVK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMICdescriptors.

• pTexelBufferView–Arraywithat leastdescriptorCountVkBufferViewhandles.Thisarray isusedwhenwewant to update VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, orVK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMICdescriptors.

Dependingonthetypeofdescriptorwewanttoupdate,weneedtoprepareavariable(oranarrayofvariables)oftype VkDescriptorImageInfo, VkDescriptorBufferInfo, or VkBufferView. Here, we want to update a combined imagesamplerdescriptor,soweneedtoprepareavariableoftypeVkDescriptorImageInfo.Itcontainsthefollowingmembers:

• sampler–Handleofasamplerobject.• imageView–Handleofanimageview.• imageLayout –Hereweprovide a layout that the imagewill havewhen thedescriptor is accessed inside

shaders.

Inthisstructureweprovideparametersofspecificresources;wepointtocreatedandvalidresourcesthatwewanttouseinsideshaders.Membersofthisstructureareinitializedbasedonthedescriptortype.Forexample,ifweupdateasampler,weneedtoprovideonlythehandleofasampler.Ifwewanttoupdateasampledimage,weneedtoprovideanimageview’shandleandan image’s layout.But imagewon’tbetransitionedtothis layoutautomatically(as inrender

Page 17: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

passes).Weneedtoperformthetransitiontothislayoutourselves,explicitlythroughpipelinebarriersor,incaseofinputattachments,throughrenderpasses.What’smore,weneedtoprovidealayoutthatcorrespondstoagivenusage.

In our examplewewant to use a texture.We can do this either by using separate sampler and sampled imagedescriptorsorbyusingacombinedimagesamplerdescriptor(asintypicalOpenGLapplications).Thelatterapproachcanbemoreoptimal(somehardwareplatformsmaysampledatafromcombinedimagesamplersfasterthanfromseparatesamplersandsampledimages),andwepresentthatapproachhere.Whenwewanttoupdateacombinedimagesampler,weneedtoprovideallthreemembersoftheVkDescriptorImageInfostructure:

VkDescriptorImageInfo image_info = { Vulkan.Image.Sampler, // VkSampler

sampler Vulkan.Image.View, // VkImageView

imageView VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL // VkImageLayout

imageLayout }; VkWriteDescriptorSet descriptor_writes = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // VkStructureType sType nullptr, // const void *pNext Vulkan.DescriptorSet.Handle, // VkDescriptorSet

dstSet 0, // uint32_t

dstBinding 0, // uint32_t

dstArrayElement 1, // uint32_t

descriptorCount VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // VkDescriptorType

descriptorType &image_info, // const VkDescriptorImageInfo

*pImageInfo nullptr, // const VkDescriptorBufferInfo

*pBufferInfo nullptr // const VkBufferView

*pTexelBufferView }; vkUpdateDescriptorSets( GetDevice(), 1, &descriptor_writes, 0, nullptr );

17. Tutorial06.cpp,functionUpdateDescriptorSet()

ApointertoavariableoftypeVkDescriptorImageInfoisthenprovidedinavariableoftypeVkWriteDescriptorSet.Aswe update only one descriptor, we need only one instance of both structures. But of course we can update moredescriptors at a time, in which case we need to prepare more variables, which are then provided to thevkUpdateDescriptorSets()function.

CreatingaPipelineLayoutWearenotyetdone.Whenwewanttousedescriptors,allocatingandupdatingdescriptorsetsisnottheonlyjobwe

needtoperform.Wehavepreparedspecificresourcesthatarealmostreadytobeusedinsideshaders,butdescriptorsetsareusedtostorehandlesofspecificresources.Thesehandlesareprovidedduringcommandbufferrecording.Weneedtoprepareinformationfortheothersideofthebarricade:thedriveralsoneedstoknowwhattypesofresourcesthegivenpipelineneedsaccessto.Thisinformationiscrucialwhenwecreateapipelineasitmayimpactitsinternalstructureormaybeevenashadercompilation.Andthisinformationisprovidedinaso-calledpipelinelayout.

Thepipelinelayoutstoresinformationaboutresourcetypesthatthegivenpipelinehasaccessto.Theseresourcesinvolvedescriptorsandpushconstantranges.Fornowwecanskippushconstantsandfocusonlyondescriptors.

Page 18: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

Tocreateapipelinelayoutandprepareinformationaboutthetypesofresourcesaccessedbythepipeline,weneedto provide an array of descriptor set layouts. This is done through the following members of a variable of typeVkPipelineLayoutCreateInfo:

• sType–Typeofthestructure.AVK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFOvalueshouldbeusedinthiscase.

• pNext–Pointerreservedforextensions.• flags–Thisparameterisreservedforfutureuse.• setLayoutCount–NumberofelementsinthepSetLayoutsmemberandnumberofseparatedescriptorsets

thatcanbeusedwiththispipeline.• pSetLayouts–Arraywithdescriptorsetlayouts.• pushConstantRangeCount–Numberofseparatepushconstantranges.• pPushConstantRanges–Arraywithelementsdescribingpushconstantranges.

And this is when descriptor set layouts are used again. The single descriptor set layout defines resource typescontainedwithinasingledescriptorset.Andanarrayoftheselayoutsdefinesresourcetypesthatthegivenpipelineneedsaccessto.

TocreateapipelinelayoutwejustcallthevkCreatePipelineLayout()function.WedidthisinIntroductiontoVulkanPart3:FirstTriangle.Buttherewecreatedanempty layout(withnopushconstantsandwithnoaccesstodescriptorresources).Here,wecreateamoretypicalpipelinelayout.

VkPipelineLayoutCreateInfo layout_create_info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType

sType nullptr, // const void

*pNext 0, // VkPipelineLayoutCreateFlags

flags 1, // uint32_t

setLayoutCount &Vulkan.DescriptorSet.Layout, // const VkDescriptorSetLayout

*pSetLayouts 0, // uint32_t

pushConstantRangeCount nullptr // const VkPushConstantRange

*pPushConstantRanges }; if( vkCreatePipelineLayout( GetDevice(), &layout_create_info, nullptr,

&Vulkan.PipelineLayout ) != VK_SUCCESS ) { std::cout << "Could not create pipeline layout!" << std::endl; return false; } return true;

18. Tutorial06.cpp,functionCreatePipelineLayout()

Suchlayoutisthenprovidedduringpipelinecreation.Wealsoneedtousethislayoutwhenwebinddescriptorsetsduringcommandbufferrecording.Soweneedtostorethepipelinelayouthandle.

BindingDescriptorSetsOne last thing is tobinddescriptor sets to thecommandbufferduring recording.Wecanhavemultipledifferent

descriptor sets ormultiple, similar descriptor sets (with the same layouts), but theymay contain different resourcehandles.Whichofthesedescriptorsareusedduringrenderingisdefinedduringcommandbufferrecording.Beforewe

Page 19: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

candrawanything,weneedtosetupavalidstate(accordingtothedrawingparameters).Foreachcommandbufferwerecordweneedtodoitfromscratch.

Drawingoperations requiresus touse renderpasses andpipelines. If a pipelineusesdescriptor resources (whenshadersaccessimagesorbuffers),weneedtobinddescriptorsetsbycallingthevkCmdBindDescriptorSets()function.Forthisfunctionwemustprovideahandleofthepipelinelayoutandanarrayofdescriptorsethandles.Webinddescriptorsetstospecificindices.Thegivenindexwebindadescriptorsettomustcorrespondtoitslayoutprovidedatthesameindexduringpipelinecreation.

vkCmdBeginRenderPass( command_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE );

vkCmdBindPipeline( command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,

Vulkan.GraphicsPipeline ); // ... vkCmdBindDescriptorSets( command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,

Vulkan.PipelineLayout, 0, 1, &Vulkan.DescriptorSet.Handle, 0, nullptr ); vkCmdDraw( command_buffer, 4, 1, 0, 0 ); vkCmdEndRenderPass( command_buffer );

19. Tutorial06.cpp,functionPrepareFrame()

AccessingDescriptorsinShadersOnemorething.Weneedtowritepropershaders.Inthisexample,weaccessatextureinsideafragmentshaderonly,

soonlythefragmentshaderwillbepresented.

Fromthebeginningof this tutorialwehavebeenreferring todescriptorsets,bindingswithindescriptorsets,andaboutbindingdescriptorsetsthemselves.Atthesametime,wemayhavemultipledescriptorsetsboundtoacommandbuffer.Eachdescriptorsetmaycontainmultipleresources.Thisdataconformstoaspecificaddressthatweuseinsideshaders.Thisaddressisdefinedthroughalayout()specifierlikethis:

layout(set=S, binding=B) uniform <variable type> <variable name>

Setdefinesan indexthatthegivendescriptorsetwasboundtothroughthevkCmdBindDescriptorSets() function.Bindingspecifiestheindexofaresourcewithintheprovidedsetandcorrespondstothebindingdefinedduringdescriptorsetlayoutcreation.Inourcase,wehaveonlyonedescriptorsetprovidedatindexzero,withonlyonecombinedimagesampler at binding zero. Combined image samplers are accessed inside shaders through sampler1D, sampler2D, orsampler3Dvariables.Soourfragmentshader’ssourcecodelookslikethis:

#version 450 layout(set=0, binding=0) uniform sampler2D u_Texture; layout(location = 0) in vec2 v_Texcoord; layout(location = 0) out vec4 o_Color; void main() { o_Color = texture( u_Texture, v_Texcoord ); }

20. shader.frag,-

Page 20: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

Tutorial06ExecutionWecanseebelowhowthefinalimagegeneratedbythesampleprogramshouldlook:

Werenderaquadthathasatextureappliedtoitssurface.Thequadshouldadjustitssize(andaspect)tomatchthewindow’ssizeandshape(ifwestretchthewindow,thequadandtheimagewillbestretchedtoo).

CleaningUpBeforewecanendourapplication,weshouldperformacleanup.

// ... if( Vulkan.GraphicsPipeline != VK_NULL_HANDLE ) { vkDestroyPipeline( GetDevice(), Vulkan.GraphicsPipeline, nullptr ); Vulkan.GraphicsPipeline = VK_NULL_HANDLE; } if( Vulkan.PipelineLayout != VK_NULL_HANDLE ) { vkDestroyPipelineLayout( GetDevice(), Vulkan.PipelineLayout, nullptr ); Vulkan.PipelineLayout = VK_NULL_HANDLE; } // ... if( Vulkan.DescriptorSet.Pool != VK_NULL_HANDLE ) { vkDestroyDescriptorPool( GetDevice(), Vulkan.DescriptorSet.Pool, nullptr ); Vulkan.DescriptorSet.Pool = VK_NULL_HANDLE; } if( Vulkan.DescriptorSet.Layout != VK_NULL_HANDLE ) { vkDestroyDescriptorSetLayout( GetDevice(), Vulkan.DescriptorSet.Layout, nullptr ); Vulkan.DescriptorSet.Layout = VK_NULL_HANDLE; } if( Vulkan.Image.Sampler != VK_NULL_HANDLE ) { vkDestroySampler( GetDevice(), Vulkan.Image.Sampler, nullptr ); Vulkan.Image.Sampler = VK_NULL_HANDLE; }

Page 21: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

if( Vulkan.Image.View != VK_NULL_HANDLE ) { vkDestroyImageView( GetDevice(), Vulkan.Image.View, nullptr ); Vulkan.Image.View = VK_NULL_HANDLE; } if( Vulkan.Image.Handle != VK_NULL_HANDLE ) { vkDestroyImage( GetDevice(), Vulkan.Image.Handle, nullptr ); Vulkan.Image.Handle = VK_NULL_HANDLE; } if( Vulkan.Image.Memory != VK_NULL_HANDLE ) { vkFreeMemory( GetDevice(), Vulkan.Image.Memory, nullptr ); Vulkan.Image.Memory = VK_NULL_HANDLE; }

21. Tutorial06.cpp,functiondestructor

WedestroybothpipelineanditslayoutbycallingthevkDestroyPipeline()andvkDestroyPipelineLayout()functions.Next,wedestroythedescriptorpoolwiththevkDestroyDescriptorPool()functionandthedescriptorsetlayoutwiththevkDestroyDescriptorSetLayout()function.Weofcoursedestroyotherresources,butwealreadyknowhowtodothis.Youmaynoticethatwedon’tfreeadescriptorset.Wecanfreeeachdescriptorsetseparatelyifaproperflagwasprovidedduringdescriptorpoolcreation.Butwedon’thaveto—whenwedestroyadescriptorpoolallsetsallocatedfromthispoolarealsofreed.

ConclusionThispartofthetutorialpresentedawaytousetextures(combinedimagesamplers,infact)insideshaders.Todothis

wecreatedanimageandallocatedandboundamemorytoit.Wealsocreatedanimageview.Next,wecopieddatafromastagingbuffertotheimagetoinitializeitscontents.Wealsocreatedasamplerobjectthatdefinedawayinwhichimagedatawasreadinsideshaders.

Next,wepreparedadescriptorset.First,wecreatedadescriptorsetlayout.Afterthat,adescriptorpoolwascreatedfromwhichasingledescriptorsetwasallocated.Weupdatedthissetwiththesamplerandtheimageviewhandles.

Thedescriptorsetlayoutwasalsousedtodefineresourcestowhichourgraphicspipelinehadaccess.Thiswasdoneduringpipelinelayoutcreation.Thislayoutwasthenusedwhenweboundthedescriptorsetstoacommandbuffer.

Wealsolearnedhowtoprepareashadercodethataccessedthecombinedimagesamplertoreaditsdata(tosampleitasatexture).Itwasdoneinsideafragmentshaderthatwasusedduringrenderingofoursimplegeometry.Thiswayweappliedatexturetothesurfaceofthisgeometry.

Inthenexttutorialwewillseehowwecanuseuniformbuffersinsideshaders.

Notices

Intel technologies’ features and benefits depend on system configuration and may require enabled hardware,software or service activation. Performance varies depending on system configuration. Check with your systemmanufacturerorretailerorlearnmoreatintel.com.

No license (express or implied, by estoppel or otherwise) to any intellectual property rights is granted by thisdocument.

Intel disclaims all express and implied warranties, including without limitation, the implied warranties ofmerchantability,fitnessforaparticularpurpose,andnon-infringement,aswellasanywarrantyarisingfromcourseofperformance,courseofdealing,orusageintrade.

Page 22: API without Secrets: Introduction to Vulkan* Part 6 · Secrets: Introduction to Vulkan tutorials, and only the differences and parts important for the described topics are presented

Thisdocumentcontainsinformationonproducts,servicesand/orprocessesindevelopment.Allinformationprovidedhere is subject to change without notice. Contact your Intel representative to obtain the latest forecast, schedule,specificationsandroadmaps.

Theproductsandservicesdescribedmaycontaindefectsorerrorsknownaserratawhichmaycausedeviationsfrompublishedspecifications.Currentcharacterizederrataareavailableonrequest.

Copiesofdocumentswhichhaveanordernumberandarereferencedinthisdocumentmaybeobtainedbycalling1-800-548-4725orbyvisitingwww.intel.com/design/literature.htm.

ThissamplesourcecodeisreleasedundertheIntelSampleSourceCodeLicenseAgreement.

IntelandtheIntellogoaretrademarksofIntelCorporationintheU.S.and/orothercountries.

*Othernamesandbrandsmaybeclaimedasthepropertyofothers.

©2017IntelCorporation