1 // Copyright 2020 The SwiftShader Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef vk_Context_hpp 16 #define vk_Context_hpp 17 18 #include "Config.hpp" 19 #include "Memset.hpp" 20 #include "Stream.hpp" 21 #include "System/Types.hpp" 22 #include "Vulkan/VkDescriptorSet.hpp" 23 #include "Vulkan/VkFormat.hpp" 24 25 #include <vector> 26 27 namespace vk { 28 29 class Buffer; 30 class Device; 31 class ImageView; 32 class PipelineLayout; 33 class RenderPass; 34 35 struct InputsDynamicStateFlags 36 { 37 bool dynamicVertexInputBindingStride : 1; 38 bool dynamicVertexInput : 1; 39 }; 40 41 // Note: The split between Inputs and VertexInputInterfaceState is mostly superficial. The state 42 // (be it dynamic or static) in Inputs should have been mostly a part of VertexInputInterfaceState. 43 // Changing that requires some surgery. 44 struct VertexInputInterfaceDynamicStateFlags 45 { 46 bool dynamicPrimitiveRestartEnable : 1; 47 bool dynamicPrimitiveTopology : 1; 48 }; 49 50 struct PreRasterizationDynamicStateFlags 51 { 52 bool dynamicLineWidth : 1; 53 bool dynamicDepthBias : 1; 54 bool dynamicDepthBiasEnable : 1; 55 bool dynamicCullMode : 1; 56 bool dynamicFrontFace : 1; 57 bool dynamicViewport : 1; 58 bool dynamicScissor : 1; 59 bool dynamicViewportWithCount : 1; 60 bool dynamicScissorWithCount : 1; 61 bool dynamicRasterizerDiscardEnable : 1; 62 }; 63 64 struct FragmentDynamicStateFlags 65 { 66 bool dynamicDepthTestEnable : 1; 67 bool dynamicDepthWriteEnable : 1; 68 bool dynamicDepthBoundsTestEnable : 1; 69 bool dynamicDepthBounds : 1; 70 bool dynamicDepthCompareOp : 1; 71 bool dynamicStencilTestEnable : 1; 72 bool dynamicStencilOp : 1; 73 bool dynamicStencilCompareMask : 1; 74 bool dynamicStencilWriteMask : 1; 75 bool dynamicStencilReference : 1; 76 }; 77 78 struct FragmentOutputInterfaceDynamicStateFlags 79 { 80 bool dynamicBlendConstants : 1; 81 }; 82 83 struct DynamicStateFlags 84 { 85 // Note: InputsDynamicStateFlags is kept local to Inputs 86 VertexInputInterfaceDynamicStateFlags vertexInputInterface; 87 PreRasterizationDynamicStateFlags preRasterization; 88 FragmentDynamicStateFlags fragment; 89 FragmentOutputInterfaceDynamicStateFlags fragmentOutputInterface; 90 }; 91 92 struct VertexInputBinding 93 { 94 Buffer *buffer = nullptr; 95 VkDeviceSize offset = 0; 96 VkDeviceSize size = 0; 97 }; 98 99 struct IndexBuffer 100 { getIndexTypevk::IndexBuffer101 inline VkIndexType getIndexType() const { return indexType; } 102 void setIndexBufferBinding(const VertexInputBinding &indexBufferBinding, VkIndexType type); 103 void getIndexBuffers(VkPrimitiveTopology topology, uint32_t count, uint32_t first, bool indexed, bool hasPrimitiveRestartEnable, std::vector<std::pair<uint32_t, void *>> *indexBuffers) const; 104 105 private: 106 uint32_t bytesPerIndex() const; 107 108 VertexInputBinding binding; 109 VkIndexType indexType; 110 }; 111 112 struct Attachments 113 { 114 ImageView *colorBuffer[sw::MAX_COLOR_BUFFERS] = {}; 115 ImageView *depthBuffer = nullptr; 116 ImageView *stencilBuffer = nullptr; 117 118 // VK_KHR_dynamic_rendering_local_read allows color locations to be mapped to the render 119 // pass attachments, but blend and other state is not affected by this map. The image views 120 // placed in colorBuffer are indexed by "location" (i.e the decoration in the shader), and 121 // the following maps facilitate the association between the attachment-specific state and 122 // the location-indexed color buffers. 123 uint32_t indexToLocation[sw::MAX_COLOR_BUFFERS] = {}; 124 uint32_t locationToIndex[sw::MAX_COLOR_BUFFERS] = {}; 125 126 VkFormat colorFormat(int location) const; 127 VkFormat depthFormat() const; 128 VkFormat depthStencilFormat() const; 129 }; 130 131 struct DynamicState; 132 struct Inputs 133 { 134 void initialize(const VkPipelineVertexInputStateCreateInfo *vertexInputState, const VkPipelineDynamicStateCreateInfo *dynamicStateCreateInfo); 135 136 void updateDescriptorSets(const DescriptorSet::Array &dso, 137 const DescriptorSet::Bindings &ds, 138 const DescriptorSet::DynamicOffsets &ddo); getDescriptorSetObjectsvk::Inputs139 inline const DescriptorSet::Array &getDescriptorSetObjects() const { return descriptorSetObjects; } getDescriptorSetsvk::Inputs140 inline const DescriptorSet::Bindings &getDescriptorSets() const { return descriptorSets; } getDescriptorDynamicOffsetsvk::Inputs141 inline const DescriptorSet::DynamicOffsets &getDescriptorDynamicOffsets() const { return descriptorDynamicOffsets; } getStreamvk::Inputs142 inline const sw::Stream &getStream(uint32_t i) const { return stream[i]; } 143 144 void bindVertexInputs(int firstInstance); 145 void setVertexInputBinding(const VertexInputBinding vertexInputBindings[], const DynamicState &dynamicState); 146 void advanceInstanceAttributes(); 147 VkDeviceSize getVertexStride(uint32_t i) const; 148 VkDeviceSize getInstanceStride(uint32_t i) const; 149 150 private: 151 InputsDynamicStateFlags dynamicStateFlags = {}; 152 VertexInputBinding vertexInputBindings[MAX_VERTEX_INPUT_BINDINGS] = {}; 153 DescriptorSet::Array descriptorSetObjects = {}; 154 DescriptorSet::Bindings descriptorSets = {}; 155 DescriptorSet::DynamicOffsets descriptorDynamicOffsets = {}; 156 sw::Stream stream[sw::MAX_INTERFACE_COMPONENTS / 4]; 157 }; 158 159 struct MultisampleState 160 { 161 bool sampleShadingEnable = false; 162 bool alphaToCoverage = false; 163 164 int sampleCount = 0; 165 unsigned int multiSampleMask = 0; 166 float minSampleShading = 0.0f; 167 168 void set(const VkPipelineMultisampleStateCreateInfo *multisampleState); 169 }; 170 171 struct BlendState : sw::Memset<BlendState> 172 { BlendStatevk::BlendState173 BlendState() 174 : Memset(this, 0) 175 {} 176 BlendStatevk::BlendState177 BlendState(bool alphaBlendEnable, 178 VkBlendFactor sourceBlendFactor, 179 VkBlendFactor destBlendFactor, 180 VkBlendOp blendOperation, 181 VkBlendFactor sourceBlendFactorAlpha, 182 VkBlendFactor destBlendFactorAlpha, 183 VkBlendOp blendOperationAlpha) 184 : Memset(this, 0) 185 , alphaBlendEnable(alphaBlendEnable) 186 , sourceBlendFactor(sourceBlendFactor) 187 , destBlendFactor(destBlendFactor) 188 , blendOperation(blendOperation) 189 , sourceBlendFactorAlpha(sourceBlendFactorAlpha) 190 , destBlendFactorAlpha(destBlendFactorAlpha) 191 , blendOperationAlpha(blendOperationAlpha) 192 {} 193 194 bool alphaBlendEnable; 195 VkBlendFactor sourceBlendFactor; 196 VkBlendFactor destBlendFactor; 197 VkBlendOp blendOperation; 198 VkBlendFactor sourceBlendFactorAlpha; 199 VkBlendFactor destBlendFactorAlpha; 200 VkBlendOp blendOperationAlpha; 201 }; 202 203 struct DynamicVertexInputBindingState 204 { 205 VkVertexInputRate inputRate = VK_VERTEX_INPUT_RATE_VERTEX; 206 VkDeviceSize stride = 0; 207 unsigned int divisor = 0; 208 }; 209 210 struct DynamicVertexInputAttributeState 211 { 212 VkFormat format = VK_FORMAT_UNDEFINED; 213 unsigned int offset = 0; 214 unsigned int binding = 0; 215 }; 216 217 struct DynamicState 218 { 219 VkViewport viewport = {}; 220 VkRect2D scissor = {}; 221 sw::float4 blendConstants = {}; 222 float depthBiasConstantFactor = 0.0f; 223 float depthBiasClamp = 0.0f; 224 float depthBiasSlopeFactor = 0.0f; 225 float minDepthBounds = 0.0f; 226 float maxDepthBounds = 0.0f; 227 float lineWidth = 0.0f; 228 229 VkCullModeFlags cullMode = VK_CULL_MODE_NONE; 230 VkBool32 depthBoundsTestEnable = VK_FALSE; 231 VkCompareOp depthCompareOp = VK_COMPARE_OP_NEVER; 232 VkBool32 depthTestEnable = VK_FALSE; 233 VkBool32 depthWriteEnable = VK_FALSE; 234 VkFrontFace frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; 235 VkPrimitiveTopology primitiveTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST; 236 uint32_t scissorCount = 0; 237 VkRect2D scissors[vk::MAX_VIEWPORTS] = {}; 238 VkStencilFaceFlags faceMask = (VkStencilFaceFlags)0; 239 VkStencilOpState frontStencil = {}; 240 VkStencilOpState backStencil = {}; 241 VkBool32 stencilTestEnable = VK_FALSE; 242 uint32_t viewportCount = 0; 243 VkViewport viewports[vk::MAX_VIEWPORTS] = {}; 244 VkBool32 rasterizerDiscardEnable = VK_FALSE; 245 VkBool32 depthBiasEnable = VK_FALSE; 246 VkBool32 primitiveRestartEnable = VK_FALSE; 247 DynamicVertexInputBindingState vertexInputBindings[MAX_VERTEX_INPUT_BINDINGS]; 248 DynamicVertexInputAttributeState vertexInputAttributes[sw::MAX_INTERFACE_COMPONENTS / 4]; 249 }; 250 251 struct VertexInputInterfaceState 252 { 253 void initialize(const VkPipelineVertexInputStateCreateInfo *vertexInputState, 254 const VkPipelineInputAssemblyStateCreateInfo *inputAssemblyState, 255 const DynamicStateFlags &allDynamicStateFlags); 256 257 void applyState(const DynamicState &dynamicState); 258 getTopologyvk::VertexInputInterfaceState259 inline VkPrimitiveTopology getTopology() const { return topology; } hasPrimitiveRestartEnablevk::VertexInputInterfaceState260 inline bool hasPrimitiveRestartEnable() const { return primitiveRestartEnable; } 261 hasDynamicTopologyvk::VertexInputInterfaceState262 inline bool hasDynamicTopology() const { return dynamicStateFlags.dynamicPrimitiveTopology; } hasDynamicPrimitiveRestartEnablevk::VertexInputInterfaceState263 inline bool hasDynamicPrimitiveRestartEnable() const { return dynamicStateFlags.dynamicPrimitiveRestartEnable; } 264 265 bool isDrawPoint(bool polygonModeAware, VkPolygonMode polygonMode) const; 266 bool isDrawLine(bool polygonModeAware, VkPolygonMode polygonMode) const; 267 bool isDrawTriangle(bool polygonModeAware, VkPolygonMode polygonMode) const; 268 269 private: 270 VertexInputInterfaceDynamicStateFlags dynamicStateFlags = {}; 271 272 bool primitiveRestartEnable = false; 273 274 VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST; 275 }; 276 277 struct PreRasterizationState 278 { 279 void initialize(const vk::Device *device, 280 const PipelineLayout *layout, 281 const VkPipelineViewportStateCreateInfo *viewportState, 282 const VkPipelineRasterizationStateCreateInfo *rasterizationState, 283 const vk::RenderPass *renderPass, uint32_t subpassIndex, 284 const VkPipelineRenderingCreateInfo *rendering, 285 const DynamicStateFlags &allDynamicStateFlags); 286 getPipelineLayoutvk::PreRasterizationState287 inline const PipelineLayout *getPipelineLayout() const { return pipelineLayout; } overridePipelineLayoutvk::PreRasterizationState288 inline void overridePipelineLayout(const PipelineLayout *linkedLayout) { pipelineLayout = linkedLayout; } 289 290 void applyState(const DynamicState &dynamicState); 291 getCullModevk::PreRasterizationState292 inline VkCullModeFlags getCullMode() const { return cullMode; } getFrontFacevk::PreRasterizationState293 inline VkFrontFace getFrontFace() const { return frontFace; } getPolygonModevk::PreRasterizationState294 inline VkPolygonMode getPolygonMode() const { return polygonMode; } getProvokingVertexModevk::PreRasterizationState295 inline VkProvokingVertexModeEXT getProvokingVertexMode() const { return provokingVertexMode; } getLineRasterizationModevk::PreRasterizationState296 inline VkLineRasterizationModeEXT getLineRasterizationMode() const { return lineRasterizationMode; } 297 hasRasterizerDiscardvk::PreRasterizationState298 inline bool hasRasterizerDiscard() const { return rasterizerDiscard; } 299 getConstantDepthBiasvk::PreRasterizationState300 inline float getConstantDepthBias() const { return depthBiasEnable ? constantDepthBias : 0; } getSlopeDepthBiasvk::PreRasterizationState301 inline float getSlopeDepthBias() const { return depthBiasEnable ? slopeDepthBias : 0; } getDepthBiasClampvk::PreRasterizationState302 inline float getDepthBiasClamp() const { return depthBiasEnable ? depthBiasClamp : 0; } 303 hasDepthRangeUnrestrictedvk::PreRasterizationState304 inline bool hasDepthRangeUnrestricted() const { return depthRangeUnrestricted; } getDepthClampEnablevk::PreRasterizationState305 inline bool getDepthClampEnable() const { return depthClampEnable; } getDepthClipEnablevk::PreRasterizationState306 inline bool getDepthClipEnable() const { return depthClipEnable; } getDepthClipNegativeOneToOnevk::PreRasterizationState307 inline bool getDepthClipNegativeOneToOne() const { return depthClipNegativeOneToOne; } 308 getLineWidthvk::PreRasterizationState309 inline float getLineWidth() const { return lineWidth; } 310 getScissorvk::PreRasterizationState311 inline const VkRect2D &getScissor() const { return scissor; } getViewportvk::PreRasterizationState312 inline const VkViewport &getViewport() const { return viewport; } 313 314 private: 315 const PipelineLayout *pipelineLayout = nullptr; 316 317 PreRasterizationDynamicStateFlags dynamicStateFlags = {}; 318 319 bool rasterizerDiscard = false; 320 bool depthClampEnable = false; 321 bool depthClipEnable = false; 322 bool depthClipNegativeOneToOne = false; 323 bool depthBiasEnable = false; 324 bool depthRangeUnrestricted = false; 325 326 VkCullModeFlags cullMode = 0; 327 VkFrontFace frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; 328 VkPolygonMode polygonMode = VK_POLYGON_MODE_FILL; 329 VkProvokingVertexModeEXT provokingVertexMode = VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT; 330 VkLineRasterizationModeEXT lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT; 331 332 float depthBiasClamp = 0.0f; 333 float constantDepthBias = 0.0f; 334 float slopeDepthBias = 0.0f; 335 336 float lineWidth = 0.0f; 337 338 VkRect2D scissor = {}; 339 VkViewport viewport = {}; 340 }; 341 342 struct FragmentState 343 { 344 void initialize(const PipelineLayout *layout, 345 const VkPipelineDepthStencilStateCreateInfo *depthStencilState, 346 const vk::RenderPass *renderPass, uint32_t subpassIndex, 347 const VkPipelineRenderingCreateInfo *rendering, 348 const DynamicStateFlags &allDynamicStateFlags); 349 getPipelineLayoutvk::FragmentState350 inline const PipelineLayout *getPipelineLayout() const { return pipelineLayout; } overridePipelineLayoutvk::FragmentState351 inline void overridePipelineLayout(const PipelineLayout *linkedLayout) { pipelineLayout = linkedLayout; } 352 353 void applyState(const DynamicState &dynamicState); 354 getFrontStencilvk::FragmentState355 inline VkStencilOpState getFrontStencil() const { return frontStencil; } getBackStencilvk::FragmentState356 inline VkStencilOpState getBackStencil() const { return backStencil; } 357 getMinDepthBoundsvk::FragmentState358 inline float getMinDepthBounds() const { return minDepthBounds; } getMaxDepthBoundsvk::FragmentState359 inline float getMaxDepthBounds() const { return maxDepthBounds; } 360 getDepthCompareModevk::FragmentState361 inline VkCompareOp getDepthCompareMode() const { return depthCompareMode; } 362 363 bool depthWriteActive(const Attachments &attachments) const; 364 bool depthTestActive(const Attachments &attachments) const; 365 bool stencilActive(const Attachments &attachments) const; 366 bool depthBoundsTestActive(const Attachments &attachments) const; 367 368 private: 369 void setDepthStencilState(const VkPipelineDepthStencilStateCreateInfo *depthStencilState); 370 371 const PipelineLayout *pipelineLayout = nullptr; 372 373 FragmentDynamicStateFlags dynamicStateFlags = {}; 374 375 bool depthTestEnable = false; 376 bool depthWriteEnable = false; 377 bool depthBoundsTestEnable = false; 378 bool stencilEnable = false; 379 380 float minDepthBounds = 0.0f; 381 float maxDepthBounds = 0.0f; 382 383 VkCompareOp depthCompareMode = VK_COMPARE_OP_NEVER; 384 385 VkStencilOpState frontStencil = {}; 386 VkStencilOpState backStencil = {}; 387 388 // Note: if a pipeline library is created with the fragment state only, and sample shading 389 // is enabled or a render pass is provided, VkPipelineMultisampleStateCreateInfo must be 390 // provided. This must identically match with the one provided for the fragment output 391 // interface library. 392 // 393 // Currently, SwiftShader can always use the copy provided and stored in 394 // FragmentOutputInterfaceState. If a future optimization requires access to this state in 395 // a pipeline library without fragment output interface, a copy of MultisampleState can be 396 // placed here and initialized under the above condition. 397 // 398 // Ref: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap10.html#pipeline-graphics-subsets 399 }; 400 401 struct FragmentOutputInterfaceState 402 { 403 void initialize(const VkPipelineColorBlendStateCreateInfo *colorBlendState, 404 const VkPipelineMultisampleStateCreateInfo *multisampleState, 405 const vk::RenderPass *renderPass, uint32_t subpassIndex, 406 const VkPipelineRenderingCreateInfo *rendering, 407 const DynamicStateFlags &allDynamicStateFlags); 408 409 void applyState(const DynamicState &dynamicState); 410 getMultiSampleMaskvk::FragmentOutputInterfaceState411 inline unsigned int getMultiSampleMask() const { return multisample.multiSampleMask; } getSampleCountvk::FragmentOutputInterfaceState412 inline int getSampleCount() const { return multisample.sampleCount; } hasSampleShadingEnabledvk::FragmentOutputInterfaceState413 inline bool hasSampleShadingEnabled() const { return multisample.sampleShadingEnable; } getMinSampleShadingvk::FragmentOutputInterfaceState414 inline float getMinSampleShading() const { return multisample.minSampleShading; } hasAlphaToCoveragevk::FragmentOutputInterfaceState415 inline bool hasAlphaToCoverage() const { return multisample.alphaToCoverage; } 416 getBlendConstantsvk::FragmentOutputInterfaceState417 inline const sw::float4 &getBlendConstants() const { return blendConstants; } 418 419 // The following take the attachment "location", which may not be the same as the index in 420 // the attachment list with VK_KHR_dynamic_rendering_local_read. 421 BlendState getBlendState(int location, const Attachments &attachments, bool fragmentContainsKill) const; 422 int colorWriteActive(int location, const Attachments &attachments) const; 423 424 private: 425 void setColorBlendState(const VkPipelineColorBlendStateCreateInfo *colorBlendState); 426 427 VkBlendFactor blendFactor(VkBlendOp blendOperation, VkBlendFactor blendFactor) const; 428 VkBlendOp blendOperation(VkBlendOp blendOperation, VkBlendFactor sourceBlendFactor, VkBlendFactor destBlendFactor, vk::Format format) const; 429 430 bool alphaBlendActive(int location, const Attachments &attachments, bool fragmentContainsKill) const; 431 bool colorWriteActive(const Attachments &attachments) const; 432 433 int colorWriteMask[sw::MAX_COLOR_BUFFERS] = {}; // RGBA 434 435 FragmentOutputInterfaceDynamicStateFlags dynamicStateFlags = {}; 436 437 sw::float4 blendConstants = {}; 438 BlendState blendState[sw::MAX_COLOR_BUFFERS] = {}; 439 440 MultisampleState multisample; 441 }; 442 443 struct GraphicsState 444 { 445 GraphicsState(const Device *device, const VkGraphicsPipelineCreateInfo *pCreateInfo, const PipelineLayout *layout); 446 447 GraphicsState combineStates(const DynamicState &dynamicState) const; 448 hasVertexInputInterfaceStatevk::GraphicsState449 bool hasVertexInputInterfaceState() const 450 { 451 return (validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT) != 0; 452 } hasPreRasterizationStatevk::GraphicsState453 bool hasPreRasterizationState() const 454 { 455 return (validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) != 0; 456 } hasFragmentStatevk::GraphicsState457 bool hasFragmentState() const 458 { 459 return (validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) != 0; 460 } hasFragmentOutputInterfaceStatevk::GraphicsState461 bool hasFragmentOutputInterfaceState() const 462 { 463 return (validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT) != 0; 464 } 465 getVertexInputInterfaceStatevk::GraphicsState466 const VertexInputInterfaceState &getVertexInputInterfaceState() const 467 { 468 ASSERT(hasVertexInputInterfaceState()); 469 return vertexInputInterfaceState; 470 } getPreRasterizationStatevk::GraphicsState471 const PreRasterizationState &getPreRasterizationState() const 472 { 473 ASSERT(hasPreRasterizationState()); 474 return preRasterizationState; 475 } getFragmentStatevk::GraphicsState476 const FragmentState &getFragmentState() const 477 { 478 ASSERT(hasFragmentState()); 479 return fragmentState; 480 } getFragmentOutputInterfaceStatevk::GraphicsState481 const FragmentOutputInterfaceState &getFragmentOutputInterfaceState() const 482 { 483 ASSERT(hasFragmentOutputInterfaceState()); 484 return fragmentOutputInterfaceState; 485 } 486 487 private: 488 // The four subsets of a graphics pipeline as described in the spec. With 489 // VK_EXT_graphics_pipeline_library, a number of these may be valid. 490 VertexInputInterfaceState vertexInputInterfaceState; 491 PreRasterizationState preRasterizationState; 492 FragmentState fragmentState; 493 FragmentOutputInterfaceState fragmentOutputInterfaceState; 494 495 VkGraphicsPipelineLibraryFlagsEXT validSubset = 0; 496 }; 497 498 } // namespace vk 499 500 #endif // vk_Context_hpp 501