yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
43d0c2100
master
1// main.cpp 2 3// Using Parameter Blocks With Reflection 4// ====================================== 5// 6// This example program is a companion to the article 7// Using Slang Parameter Blocks, and specifically 8// the section of that article called Using Parameter 9// Blocks With Reflection. 10// 11// Where possible, the code is presented in the 12// same order as the code in the article, so that the 13// two can be read in parallel. When code relates to 14// a sub-section of the article, a comment will be used 15// to reference the relevant section. 16// 17// Boilerplate 18// =========== 19// 20// As is typical for our example programs, this one starts 21// with a certain amount of boilerplate that isn't especially 22// interesting to discuss. 23 24#include "core/slang-basic.h" 25#include "examples/example-base/example-base.h" 26#include "slang-com-ptr.h" 27#include "slang-rhi.h" 28#include "slang.h" 29 30typedef SlangResult Result ; 31using Slang ::ComPtr ; 32using Slang ::String ; 33using Slang ::List ; 34using namespace rhi ; 35 36// The example code currently only supports Vulkan, but the 37// code is factored with the intention that it could be extended 38// to support D3D12 as well. 39 40#define ENABLE_VULKAN 1 41#define ENABLE_D3D12 0 42 43#if ENABLE_VULKAN 44#include "vulkan-api.h" 45#endif 46 47static const ExampleResources resourceBase ("reflection-parameter-blocks" ); 48static const char * kSourceFileName = "shader.slang" ; 49 50struct PipelineLayoutReflectionContext 51{ 52IDevice * _rhiDevice = nullptr ; 53 slang::ISession * _slangSession = nullptr ; 54 slang::ProgramLayout * _slangProgramLayout = nullptr ; 55 slang::IBlob * _slangCompiledProgramBlob = nullptr ; 56}; 57 58struct PipelineLayoutReflectionContext_Vulkan :PipelineLayoutReflectionContext 59{ 60// What Goes Into a Pipeline Layout? 61// ================================= 62 63struct PipelineLayoutBuilder 64 { 65 std::vector < VkDescriptorSetLayout > descriptorSetLayouts ; 66 std::vector < VkPushConstantRange > pushConstantRanges ; 67 }; 68 69// Unlike how things are presented in the document, we do not 70// nest most of the functions under the `*Builder` types, in 71// order to allow for more flexibility in the order of 72// presentation. For example, instead of a 73// `PipelineLayoutBuilder::finishBuilding()` method, we instead 74// have a `finishBuildingPipelineLayout` function: 75 76Result finishBuildingPipelineLayout ( 77PipelineLayoutBuilder & builder , 78VkPipelineLayout * outPipelineLayout ) 79 { 80filterOutEmptyDescriptorSets (builder ); 81 82VkPipelineLayoutCreateInfo pipelineLayoutInfo = { 83VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; 84 85pipelineLayoutInfo .setLayoutCount = builder .descriptorSetLayouts .size (); 86pipelineLayoutInfo .pSetLayouts = builder .descriptorSetLayouts .data (); 87 88pipelineLayoutInfo .pushConstantRangeCount = builder .pushConstantRanges .size (); 89pipelineLayoutInfo .pPushConstantRanges = builder .pushConstantRanges .data (); 90 91VkPipelineLayout pipelineLayout = VK_NULL_HANDLE ; 92vkAPI .vkCreatePipelineLayout (vkAPI .device ,& pipelineLayoutInfo ,nullptr ,& pipelineLayout ); 93 94* outPipelineLayout = pipelineLayout ; 95return SLANG_OK ; 96 } 97 98// What Goes Into a Descriptor Set Layout? 99// ======================================= 100 101struct DescriptorSetLayoutBuilder 102 { 103 std::vector < VkDescriptorSetLayoutBinding > descriptorRanges ; 104 105int setIndex = -1 ; 106 }; 107 108 109// Once we are done traversing the contents of a parameter 110// block to collect bindings into a `DescriptorSetLayoutBuilder`, 111// it is a simple matter to create a descriptor set layout using 112// the Vulkan API, and to install it into the `setLayouts` array 113// at the index that was reserved. 114// 115void finishBuildingDescriptorSetLayout ( 116PipelineLayoutBuilder & pipelineLayoutBuilder , 117DescriptorSetLayoutBuilder & descriptorSetLayoutBuilder ) 118 { 119if (descriptorSetLayoutBuilder .descriptorRanges .empty ()) 120return ; 121 122VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { 123VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; 124 125descriptorSetLayoutInfo .bindingCount = descriptorSetLayoutBuilder .descriptorRanges .size (); 126descriptorSetLayoutInfo .pBindings = descriptorSetLayoutBuilder .descriptorRanges .data (); 127 128VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE ; 129vkAPI .vkCreateDescriptorSetLayout ( 130vkAPI .device , 131& descriptorSetLayoutInfo , 132nullptr , 133& descriptorSetLayout ); 134 135pipelineLayoutBuilder .descriptorSetLayouts [descriptorSetLayoutBuilder .setIndex ]= 136descriptorSetLayout ; 137 } 138 139// Parameter Blocks 140// ================ 141 142void addDescriptorSetForParameterBlock ( 143PipelineLayoutBuilder & pipelineLayoutBuilder , 144 slang::TypeLayoutReflection * parameterBlockTypeLayout ) 145 { 146DescriptorSetLayoutBuilder descriptorSetLayoutBuilder ; 147startBuildingDescriptorSetLayout (pipelineLayoutBuilder ,descriptorSetLayoutBuilder ); 148 149addRangesForParameterBlockElement ( 150pipelineLayoutBuilder , 151descriptorSetLayoutBuilder , 152parameterBlockTypeLayout -> getElementTypeLayout ()); 153 154finishBuildingDescriptorSetLayout (pipelineLayoutBuilder ,descriptorSetLayoutBuilder ); 155 } 156 157// Automatically-Introduced Uniform Buffer 158// --------------------------------------- 159 160void addRangesForParameterBlockElement ( 161PipelineLayoutBuilder & pipelineLayoutBuilder , 162DescriptorSetLayoutBuilder & descriptorSetLayoutBuilder , 163 slang::TypeLayoutReflection * elementTypeLayout ) 164 { 165if (elementTypeLayout -> getSize ()> 0 ) 166 { 167addAutomaticallyIntroducedUniformBuffer (descriptorSetLayoutBuilder ); 168 } 169 170// Once we have accounted for the possibility of an implicitly-introduced 171// constant buffer, we can move on and add bindings based on whatever 172// non-ordinary data (textures, buffers, etc.) is in the element type: 173// 174addRanges (pipelineLayoutBuilder ,descriptorSetLayoutBuilder ,elementTypeLayout ); 175 } 176 177void addAutomaticallyIntroducedUniformBuffer ( 178DescriptorSetLayoutBuilder & descriptorSetLayoutBuilder ) 179 { 180auto vulkanBindingIndex = descriptorSetLayoutBuilder .descriptorRanges .size (); 181 182VkDescriptorSetLayoutBinding binding = {}; 183binding .stageFlags = VK_SHADER_STAGE_ALL ; 184binding .binding = vulkanBindingIndex ; 185binding .descriptorCount = 1 ; 186binding .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ; 187 188descriptorSetLayoutBuilder .descriptorRanges .push_back (binding ); 189 } 190 191// Ordering of Nested Parameter Blocks 192// ----------------------------------- 193 194void startBuildingDescriptorSetLayout ( 195PipelineLayoutBuilder & pipelineLayoutBuilder , 196DescriptorSetLayoutBuilder & descriptorSetLayoutBuilder ) 197 { 198descriptorSetLayoutBuilder .setIndex = pipelineLayoutBuilder .descriptorSetLayouts .size (); 199pipelineLayoutBuilder .descriptorSetLayouts .push_back (VK_NULL_HANDLE ); 200 } 201 202// Empty Ranges 203// ------------ 204 205void filterOutEmptyDescriptorSets (PipelineLayoutBuilder & builder ) 206 { 207 std::vector < VkDescriptorSetLayout > filteredDescriptorSetLayouts ; 208for (auto descriptorSetLayout :builder .descriptorSetLayouts ) 209 { 210if (!descriptorSetLayout ) 211continue ; 212filteredDescriptorSetLayouts .push_back (descriptorSetLayout ); 213 } 214 std::swap (builder .descriptorSetLayouts ,filteredDescriptorSetLayouts ); 215 } 216 217// Descritpor Ranges 218// ================= 219 220void addDescriptorRanges ( 221DescriptorSetLayoutBuilder & descriptorSetLayoutBuilder , 222 slang::TypeLayoutReflection * typeLayout ) 223 { 224int relativeSetIndex = 0 ; 225int rangeCount = typeLayout -> getDescriptorSetDescriptorRangeCount (relativeSetIndex ); 226 227for (int rangeIndex = 0 ;rangeIndex < rangeCount ;++ rangeIndex ) 228 { 229addDescriptorRange ( 230descriptorSetLayoutBuilder , 231typeLayout , 232relativeSetIndex , 233rangeIndex ); 234 } 235 } 236 237void addDescriptorRange ( 238DescriptorSetLayoutBuilder & descriptorSetLayoutBuilder , 239 slang::TypeLayoutReflection * typeLayout , 240int relativeSetIndex , 241int rangeIndex ) 242 { 243 slang::BindingType bindingType = 244typeLayout -> getDescriptorSetDescriptorRangeType (relativeSetIndex ,rangeIndex ); 245auto descriptorCount = typeLayout -> getDescriptorSetDescriptorRangeDescriptorCount ( 246relativeSetIndex , 247rangeIndex ); 248 249// Some Ranges Need to Be Skipped 250// ------------------------------ 251// 252switch (bindingType ) 253 { 254default : 255break ; 256 257case slang::BindingType ::PushConstant : 258return ; 259 } 260 261auto bindingIndex = descriptorSetLayoutBuilder .descriptorRanges .size (); 262 263VkDescriptorSetLayoutBinding vulkanBindingRange = {}; 264vulkanBindingRange .binding = bindingIndex ; 265vulkanBindingRange .descriptorCount = descriptorCount ; 266vulkanBindingRange .stageFlags = _currentStageFlags ; 267vulkanBindingRange .descriptorType = mapSlangBindingTypeToVulkanDescriptorType (bindingType ); 268 269descriptorSetLayoutBuilder .descriptorRanges .push_back (vulkanBindingRange ); 270 } 271 272VkDescriptorType mapSlangBindingTypeToVulkanDescriptorType (slang::BindingType bindingType ) 273 { 274switch (bindingType ) 275 { 276#define CASE (FROM ,TO ) \ 277 case slang::BindingType::FROM: \ 278 return VK_DESCRIPTOR_TYPE_##TO 279 280CASE (Sampler ,SAMPLER ); 281CASE (CombinedTextureSampler ,COMBINED_IMAGE_SAMPLER ); 282CASE (Texture ,SAMPLED_IMAGE ); 283CASE (MutableTexture ,STORAGE_IMAGE ); 284CASE (TypedBuffer ,UNIFORM_TEXEL_BUFFER ); 285CASE (MutableTypedBuffer ,STORAGE_TEXEL_BUFFER ); 286CASE (ConstantBuffer ,UNIFORM_BUFFER ); 287CASE (RawBuffer ,STORAGE_BUFFER ); 288CASE (MutableRawBuffer ,STORAGE_BUFFER ); 289CASE (InputRenderTarget ,INPUT_ATTACHMENT ); 290CASE (InlineUniformData ,INLINE_UNIFORM_BLOCK ); 291CASE (RayTracingAccelerationStructure ,ACCELERATION_STRUCTURE_KHR ); 292 293#undef CASE 294 295default : 296return VkDescriptorType (-1 ); 297 } 298 } 299 300// Sub-Object Ranges 301// ================= 302 303void addRanges ( 304PipelineLayoutBuilder & pipelineLayoutBuilder , 305DescriptorSetLayoutBuilder & descriptorSetLayoutBuilder , 306 slang::TypeLayoutReflection * typeLayout ) 307 { 308addDescriptorRanges (descriptorSetLayoutBuilder ,typeLayout ); 309addSubObjectRanges (pipelineLayoutBuilder ,typeLayout ); 310 } 311 312void addSubObjectRanges ( 313PipelineLayoutBuilder & pipelineLayoutBuilder , 314 slang::TypeLayoutReflection * typeLayout ) 315 { 316int subObjectRangeCount = typeLayout -> getSubObjectRangeCount (); 317for (int subObjectRangeIndex = 0 ;subObjectRangeIndex < subObjectRangeCount ; 318++ subObjectRangeIndex ) 319 { 320addSubObjectRange (pipelineLayoutBuilder ,typeLayout ,subObjectRangeIndex ); 321 } 322 } 323 324void addSubObjectRange ( 325PipelineLayoutBuilder & pipelineLayoutBuilder , 326 slang::TypeLayoutReflection * typeLayout , 327int subObjectRangeIndex ) 328 { 329auto bindingRangeIndex = 330typeLayout -> getSubObjectRangeBindingRangeIndex (subObjectRangeIndex ); 331auto bindingType = typeLayout -> getBindingRangeType (bindingRangeIndex ); 332switch (bindingType ) 333 { 334default : 335return ; 336 337// Nested Parameter Blocks 338// ----------------------- 339 340case slang::BindingType ::ParameterBlock : 341 { 342auto parameterBlockTypeLayout = 343typeLayout -> getBindingRangeLeafTypeLayout (bindingRangeIndex ); 344addDescriptorSetForParameterBlock (pipelineLayoutBuilder ,parameterBlockTypeLayout ); 345 } 346break ; 347 348// Push-Constant Ranges 349// -------------------- 350 351case slang::BindingType ::PushConstant : 352 { 353auto constantBufferTypeLayout = 354typeLayout -> getBindingRangeLeafTypeLayout (bindingRangeIndex ); 355addPushConstantRangeForConstantBuffer ( 356pipelineLayoutBuilder , 357constantBufferTypeLayout ); 358 } 359break ; 360 } 361 } 362 363void addPushConstantRangeForConstantBuffer ( 364PipelineLayoutBuilder & pipelineLayoutBuilder , 365 slang::TypeLayoutReflection * pushConstantBufferTypeLayout ) 366 { 367auto elementTypeLayout = pushConstantBufferTypeLayout -> getElementTypeLayout (); 368auto elementSize = elementTypeLayout -> getSize (); 369 370if (elementSize == 0 ) 371return ; 372 373VkPushConstantRange pushConstantRange = {}; 374pushConstantRange .stageFlags = _currentStageFlags ; 375pushConstantRange .offset = 0 ; 376pushConstantRange .size = elementSize ; 377 378pipelineLayoutBuilder .pushConstantRanges .push_back (pushConstantRange ); 379 } 380 381// Creating a Pipeline Layout for a Program 382// ======================================== 383 384Result createPipelineLayout ( 385 slang::ProgramLayout * programLayout , 386VkPipelineLayout * outPipelineLayout ) 387 { 388PipelineLayoutBuilder pipelineLayoutBuilder ; 389 390DescriptorSetLayoutBuilder defaultDescriptorSetLayoutBuilder ; 391startBuildingDescriptorSetLayout (pipelineLayoutBuilder ,defaultDescriptorSetLayoutBuilder ); 392 393addGlobalScopeParameters ( 394pipelineLayoutBuilder , 395defaultDescriptorSetLayoutBuilder , 396programLayout ); 397 398addEntryPointParameters ( 399pipelineLayoutBuilder , 400defaultDescriptorSetLayoutBuilder , 401programLayout ); 402 403finishBuildingDescriptorSetLayout (pipelineLayoutBuilder ,defaultDescriptorSetLayoutBuilder ); 404finishBuildingPipelineLayout (pipelineLayoutBuilder ,outPipelineLayout ); 405 406return SLANG_OK ; 407 } 408 409// Global Scope 410// ------------ 411 412void addGlobalScopeParameters ( 413PipelineLayoutBuilder & pipelineLayoutBuilder , 414DescriptorSetLayoutBuilder & descriptorSetLayoutBuilder , 415 slang::ProgramLayout * programLayout ) 416 { 417_currentStageFlags = VK_SHADER_STAGE_ALL ; 418addRangesForParameterBlockElement ( 419pipelineLayoutBuilder , 420descriptorSetLayoutBuilder , 421programLayout -> getGlobalParamsTypeLayout ()); 422 } 423 424// Entry Points 425// ------------ 426 427void addEntryPointParameters ( 428PipelineLayoutBuilder & pipelineLayoutBuilder , 429DescriptorSetLayoutBuilder & descriptorSetLayoutBuilder , 430 slang::ProgramLayout * programLayout ) 431 { 432int entryPointCount = _slangProgramLayout -> getEntryPointCount (); 433for (int i = 0 ;i < entryPointCount ;++ i ) 434 { 435auto entryPointLayout = _slangProgramLayout -> getEntryPointByIndex (i ); 436addEntryPointParameters ( 437pipelineLayoutBuilder , 438descriptorSetLayoutBuilder , 439entryPointLayout ); 440 } 441 } 442 443void addEntryPointParameters ( 444PipelineLayoutBuilder & pipelineLayoutBuilder , 445DescriptorSetLayoutBuilder & descriptorSetLayoutBuilder , 446 slang::EntryPointLayout * entryPointLayout ) 447 { 448_currentStageFlags = getShaderStageFlags (entryPointLayout -> getStage ()); 449addRangesForParameterBlockElement ( 450pipelineLayoutBuilder , 451descriptorSetLayoutBuilder , 452entryPointLayout -> getTypeLayout ()); 453 } 454 455VkShaderStageFlags _currentStageFlags = VK_SHADER_STAGE_ALL ; 456VkShaderStageFlags getShaderStageFlags (SlangStage stage ) 457 { 458switch (stage ) 459 { 460#define CASE (FROM ,TO ) \ 461 case SLANG_STAGE_##FROM: \ 462 return VK_SHADER_STAGE_##TO 463 464CASE (VERTEX ,VERTEX_BIT ); 465CASE (HULL ,TESSELLATION_CONTROL_BIT ); 466CASE (DOMAIN ,TESSELLATION_EVALUATION_BIT ); 467CASE (GEOMETRY ,GEOMETRY_BIT ); 468CASE (FRAGMENT ,FRAGMENT_BIT ); 469CASE (COMPUTE ,COMPUTE_BIT ); 470CASE (RAY_GENERATION ,RAYGEN_BIT_KHR ); 471CASE (ANY_HIT ,ANY_HIT_BIT_KHR ); 472CASE (CLOSEST_HIT ,CLOSEST_HIT_BIT_KHR ); 473CASE (MISS ,MISS_BIT_KHR ); 474CASE (INTERSECTION ,INTERSECTION_BIT_KHR ); 475CASE (CALLABLE ,CALLABLE_BIT_KHR ); 476CASE (MESH ,MESH_BIT_EXT ); 477CASE (AMPLIFICATION ,TASK_BIT_EXT ); 478 479#undef CASE 480default : 481return VK_SHADER_STAGE_ALL ; 482 } 483 } 484 485// Validation 486// ========== 487// 488// The published article covers how to create a pipeline layout 489// using the reflection API, but for the purposes of an example 490// program, we should make sure that we validate that the layout 491// that results from that code is *actually* compatible with the 492// shader program. 493// 494// The remaining operations inside this type provide the support 495// code to create and validate a pipeline layout based on a 496// particular compiled compute program. Mismatches between the 497// pipeline layout and the program should be diagnosed by the 498// Vulkan validation layer when we attempt to create a pipeline 499// that uses the two together. 500 501Result validatePipelineLayout (VkPipelineLayout pipelineLayout ) 502 { 503VkShaderModuleCreateInfo shaderModuleInfo = {VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; 504shaderModuleInfo .pCode = (uint32_t const * )_slangCompiledProgramBlob -> getBufferPointer (); 505shaderModuleInfo .codeSize = _slangCompiledProgramBlob -> getBufferSize (); 506 507VkShaderModule vkShaderModule ; 508vkAPI .vkCreateShaderModule (vkAPI .device ,& shaderModuleInfo ,nullptr ,& vkShaderModule ); 509 510VkComputePipelineCreateInfo pipelineInfo = {VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; 511pipelineInfo .layout = pipelineLayout ; 512pipelineInfo .stage .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO ; 513pipelineInfo .stage .module = vkShaderModule ; 514pipelineInfo .stage .pName = "main" ; 515pipelineInfo .stage .stage = VK_SHADER_STAGE_COMPUTE_BIT ; 516 517VkPipeline pipeline ; 518vkAPI .vkCreateComputePipelines ( 519vkAPI .device , 520VK_NULL_HANDLE , 5211 , 522& pipelineInfo , 523nullptr , 524& pipeline ); 525 526vkAPI .vkDestroyPipeline (vkAPI .device ,pipeline ,nullptr ); 527 528return SLANG_OK ; 529 } 530 531Result createAndValidatePipelineLayout () 532 { 533// Here we do a little bit of complicated interaction with 534// the `slang-rhi` library to allow us to call raw Vulkan API 535// functions on the same device that `slang-rhi` kindly set up 536// for us. 537// 538DeviceNativeHandles handle ; 539SLANG_RETURN_ON_FAIL (_rhiDevice -> getNativeDeviceHandles (& handle )); 540 541vkAPI .instance = (VkInstance )handle .handles [0 ].value ; 542vkAPI .physicalDevice = (VkPhysicalDevice )handle .handles [1 ].value ; 543vkAPI .device = (VkDevice )handle .handles [2 ].value ; 544 545vkAPI .initGlobalProcs (); 546vkAPI .initInstanceProcs (); 547vkAPI .initDeviceProcs (); 548 549// Once the setup is dealt with, we can go ahead and 550// create the pipeline layout, before validating that 551// it can be used together with the compiled SPIR-V 552// binary for the program. 553// 554VkPipelineLayout pipelineLayout ; 555SLANG_RETURN_ON_FAIL (createPipelineLayout (_slangProgramLayout ,& pipelineLayout )); 556SLANG_RETURN_ON_FAIL (validatePipelineLayout (pipelineLayout )); 557 558vkAPI .vkDestroyPipelineLayout (vkAPI .device ,pipelineLayout ,nullptr ); 559 560return SLANG_OK ; 561 } 562 563VulkanAPI vkAPI ; 564}; 565 566// More Boilerplate 567// ================ 568// 569// The logic below this point is just about setting up the necessary state 570// in the example application for the code above to be run on a simple 571// shader. Nothing here is especially relevant to the task of creating 572// a pipeline layout from Slang reflection information. 573 574struct ReflectionParameterBlocksExampleApp :public TestBase 575{ 576Result execute (int argc ,char ** argv ) 577 { 578parseOption (argc ,argv ); 579 580// We start by initializing the `slang-rhi` system, so that 581// it can handle most of the details of getting a 582// Vulkan device up and running. 583 584DeviceDesc deviceDesc = {}; 585deviceDesc .deviceType = DeviceType ::Vulkan ; 586 587ComPtr < IDevice > rhiDevice = getRHI ()-> createDevice (deviceDesc ); 588if (!rhiDevice ) 589return SLANG_FAIL ; 590 591// The `slang-rhi` library also creates a Slang session as 592// part of its startup, so we will use the session 593// it already created for the compilation in 594// this example. 595// 596auto slangSession = rhiDevice -> getSlangSession (); 597 598// Next we go through the fairly routine steps needed to 599// compile a Slang program from source. 600// 601ComPtr < slang::IBlob > diagnostics ; 602Result result = SLANG_OK ; 603 604// We load the source file as a module of Slang code. 605// 606String sourceFilePath = resourceBase .resolveResource (kSourceFileName ); 607ComPtr < slang::IModule > module ; 608module = slangSession -> loadModule (sourceFilePath .getBuffer (),diagnostics .writeRef ()); 609diagnoseIfNeeded (diagnostics ); 610if (!module ) 611return SLANG_FAIL ; 612 613// Next we will collect all of the entry points defined in the module, 614// to form the list of components we want to link together to form 615// a program. 616// 617List < ComPtr < slang::IComponentType >> componentsToLink ; 618int definedEntryPointCount = module -> getDefinedEntryPointCount (); 619for (int i = 0 ;i < definedEntryPointCount ;i ++ ) 620 { 621ComPtr < slang::IEntryPoint > entryPoint ; 622SLANG_RETURN_ON_FAIL (module -> getDefinedEntryPoint (i ,entryPoint .writeRef ())); 623componentsToLink .add (ComPtr < slang::IComponentType > (entryPoint .get ())); 624 } 625 626// Once we've collected the list of entry points we want to compose, 627// we use the Slang compilation API to compose them. 628// 629ComPtr < slang::IComponentType > composed ; 630result = slangSession -> createCompositeComponentType ( 631 (slang::IComponentType ** )componentsToLink .getBuffer (), 632componentsToLink .getCount (), 633composed .writeRef (), 634diagnostics .writeRef ()); 635diagnoseIfNeeded (diagnostics ); 636SLANG_RETURN_ON_FAIL (result ); 637 638// As the final compilation step, we will use the compilation API 639// to link the composed code. Think of this as equivalent to 640// applying the linker to a bunch of `.o` and/or `.a` files to 641// produce a binary (executable or shared library). 642// 643ComPtr < slang::IComponentType > program ; 644result = composed -> link (program .writeRef (),diagnostics .writeRef ()); 645diagnoseIfNeeded (diagnostics ); 646SLANG_RETURN_ON_FAIL (result ); 647 648// Once the program has been compiled succcessfully, we can 649// go ahead and grab reflection data from the program. 650// 651int targetIndex = 0 ; 652 slang::ProgramLayout * programLayout = 653program -> getLayout (targetIndex ,diagnostics .writeRef ()); 654diagnoseIfNeeded (diagnostics ); 655if (!programLayout ) 656 { 657return SLANG_FAIL ; 658 } 659 660// The compiled program can also have binary code (either 661// for individual entry points, or the entire program) 662// generated for it. 663// 664ComPtr < slang::IBlob > programBinary ; 665result = program -> getEntryPointCode (0 ,0 ,programBinary .writeRef (),diagnostics .writeRef ()); 666diagnoseIfNeeded (diagnostics ); 667if (SLANG_FAILED (result )) 668return result ; 669 670// Finally, once all of the initialization work is dealt with, 671// we hand control over to the actual logic of the example. 672// 673PipelineLayoutReflectionContext_Vulkan context ; 674 675context ._rhiDevice = rhiDevice ; 676context ._slangSession = slangSession ; 677context ._slangProgramLayout = programLayout ; 678context ._slangCompiledProgramBlob = programBinary ; 679 680SLANG_RETURN_ON_FAIL (context .createAndValidatePipelineLayout ()); 681 682return SLANG_OK ; 683 } 684}; 685 686int main (int argc ,char * argv []) 687{ 688ReflectionParameterBlocksExampleApp app ; 689if (SLANG_FAILED (app .execute (argc ,argv ))) 690 { 691return -1 ; 692 } 693return 0 ; 694}