unit vDevice;

{$mode objfpc}{$H+}

interface

uses
  SysUtils,
  atomic,
  Vulkan;

type
 TExtensionNames=array of PChar;
 APhysicalDeviceProperties=array of TVkPhysicalDeviceProperties;

 t_vulkan_app_flags=Set of
   (va_debug_utils,                  //VK_EXT_debug_utils
    va_validation_layer,             //VK_LAYER_KHRONOS_validation
    //
    va_enable_gpu_assisted,          //VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT
    va_enable_gpu_assisted_reserve,  //VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT
    va_enable_best_practices,        //VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT
    va_enable_debug_printf,          //VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT
    va_enable_sync_validation,       //VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT
    //
    va_disable_shaders,              //VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT
    va_disable_thread_safety,        //VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT
    va_disable_api_params,           //VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT
    va_disable_obj_lifetimes,        //VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT
    va_disable_core_checks,          //VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT
    va_disable_unique_handles,       //VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT
    va_disable_shader_validation     //VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHE_EXT
   );

 TVulkanApp=class
  FInstance:TVkInstance;
  FPhysicalDevice:TVkPhysicalDevice;
  //
  FGFamily:TVkUInt32;
  FCFamily:TVkUInt32;
  FTFamily:TVkUInt32;
  //
  FGFamilyCount:TVkUInt32;
  FCFamilyCount:TVkUInt32;
  FTFamilyCount:TVkUInt32;
  //
  Constructor Create(flags:t_vulkan_app_flags);
  Destructor  Destroy; override;
  Procedure   LoadFamily; virtual;
  function    InstanceLayersIsExist(P:PChar):Boolean;
 end;

 TvDebugReport=class
  FHandle:TVkDebugUtilsMessengerEXT;
  //
  FStdOut:Text;
  //
  FCreateDebugUtilsMessenger :TvkCreateDebugUtilsMessengerEXT;
  FDestroyDebugUtilsMessenger:TvkDestroyDebugUtilsMessengerEXT;
  //
  FSetDebugUtilsObjectName   :TvkSetDebugUtilsObjectNameEXT;
  FCmdBeginDebugUtilsLabel   :TvkCmdBeginDebugUtilsLabelEXT;
  FCmdEndDebugUtilsLabel     :TvkCmdEndDebugUtilsLabelEXT;
  FCmdInsertDebugUtilsLabel  :TvkCmdInsertDebugUtilsLabelEXT;
  //
  Constructor Create;
  Destructor  Destroy; override;
  //
  procedure   SetObjectName (objectType:TVkObjectType;objectHandle:TVkUInt64;pObjectName:PVkChar);
  procedure   CmdBeginLabel (commandBuffer:TVkCommandBuffer;pLabelName:PVkChar);
  procedure   CmdEndLabel   (commandBuffer:TVkCommandBuffer);
  procedure   CmdInsertLabel(commandBuffer:TVkCommandBuffer;pLabelName:PVkChar);
  //
  procedure   ReportCallback(messageSeverity:TVkDebugUtilsMessageSeverityFlagBitsEXT;
                             messageTypes:TVkDebugUtilsMessageTypeFlagsEXT;
                             const pCallbackData:PVkDebugUtilsMessengerCallbackDataEXT
                            ); virtual;
  //
 end;

 TvSurface=class
  FHandle:TVkSurfaceKHR;
  FPFamily:TVkUInt32;
  Fformat:TVkSurfaceFormatKHR;
  FModes:array[0..2] of TVkPresentModeKHR;
  Constructor Create(Handle:THandle);
  Destructor  Destroy; override;
  Procedure   LoadFamily; virtual;
  Procedure   LoadFormat; virtual;
  Procedure   LoadModes; virtual;
  function    GetSize:TVkExtent2D; virtual;
 end;

 TSortQueueRec=record
  FIndex:TVkUInt32;
  pQueue:PVkQueue;
 end;

 PAbstractFeature=^TAbstractFeature;
 TAbstractFeature=record
  sType:TVkStructureType;
  pNext:PVkVoid;
 end;

 TvDeviceCreateInfo=class
  data:array of TSortQueueRec;
  exts:array of Pchar;
  pFeature:PAbstractFeature;
  procedure   add_queue(Index:TVkUInt32;Queue:PVkQueue);
  procedure   add_ext(P:Pchar);
  procedure   add_feature(P:PVkVoid);
 end;

 TvDevice=class
  FHandle:TVkDevice;
  FLock:System.TRTLCriticalSection;
  Constructor Create(Queues:TvDeviceCreateInfo);
  Destructor  Destroy; override;
  function    WaitIdle:TVkResult;
 end;

 TvQueue=class
  FHandle:TVkQueue;
  FFamily:TVkUInt32;
  FLock:System.TRTLCriticalSection;
  Constructor Create(Family:TVkUInt32);
  Destructor  Destroy; override;
  function    Submit(submitCount:TVkUInt32;const pSubmits:PVkSubmitInfo;fence:TVkFence):TVkResult;
  function    WaitIdle:TVkResult;
  function    PresentKHR(const pPresentInfo:PVkPresentInfoKHR):TVkResult;
 end;

 TvCustomCmdPool=class
  function    Alloc:TVkCommandBuffer;    virtual; abstract;
  procedure   Free(cmd:TVkCommandBuffer);virtual; abstract;
  procedure   Trim;                      virtual; abstract;
 end;

 TvCmdPool=class(TvCustomCmdPool)
  FHandle:TVkCommandPool;
  Constructor Create(FFamily:TVkUInt32);
  Destructor  Destroy;                    override;
  function    Alloc:TVkCommandBuffer;     override;
  procedure   Free(cmd:TVkCommandBuffer); override;
  procedure   Trim;                       override;
 end;

 TvFence=class
  FHandle:TVkFence;
  Constructor Create(signaled:Boolean);
  Destructor  Destroy; override;
  function    Reset:TVkResult;
  function    Wait(timeout:TVkUInt64):TVkResult;
  function    Status:TVkResult; //[VK_SUCCESS,VK_NOT_READY]
 end;

 TvSemaphore=class
  FHandle:TVkSemaphore;
  Constructor Create;
  Destructor  Destroy; override;
 end;

 TvEvent=class
  FHandle:TVkEvent;
  Constructor Create;
  Destructor  Destroy; override;
  function    SetEvent:TVkResult;
  function    ResetEvent:TVkResult;
  function    Status:TVkResult;
 end;

procedure PrintInstanceExtension;
procedure PrintDeviceExtension(physicalDevice:TVkPhysicalDevice);
procedure PrintQueueFamily(physicalDevice:TVkPhysicalDevice);

const
 SubresColor:TVkImageSubresourceRange=(
  aspectMask:TVkImageAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT);
  baseMipLevel:0;
  levelCount:1;
  baseArrayLayer:0;
  layerCount:1
 );

procedure vkImageMemoryBarrier(
	   cmdbuffer:TVkCommandBuffer;
	   image:TVkImage;
	   srcAccessMask:TVkAccessFlags;
	   dstAccessMask:TVkAccessFlags;
	   oldImageLayout:TVkImageLayout;
	   newImageLayout:TVkImageLayout;
	   srcStageMask:TVkPipelineStageFlags;
	   dstStageMask:TVkPipelineStageFlags;
	   subresourceRange:TVkImageSubresourceRange);

procedure vkBufferMemoryBarrier(
	   cmdbuffer:TVkCommandBuffer;
	   buffer:TVkBuffer;
	   srcAccessMask:TVkAccessFlags;
	   dstAccessMask:TVkAccessFlags;
           offset,size:TVkDeviceSize;
	   srcStageMask:TVkPipelineStageFlags;
	   dstStageMask:TVkPipelineStageFlags);

procedure vkMemoryBarrier(
	   cmdbuffer:TVkCommandBuffer;
	   srcAccessMask:TVkAccessFlags;
	   dstAccessMask:TVkAccessFlags;
	   srcStageMask:TVkPipelineStageFlags;
	   dstStageMask:TVkPipelineStageFlags);

{
procedure vkBarrier(
	   cmdbuffer:TVkCommandBuffer;
	   srcStageMask:TVkPipelineStageFlags;
	   dstStageMask:TVkPipelineStageFlags);
}

Procedure vkCmdBindVertexBuffer(commandBuffer:TVkCommandBuffer;
                                Binding:TVkUInt32;
                                Buffer:TVkBuffer;
                                Offset:TVkDeviceSize);

{
Procedure vkCmdBindDescriptorBuffer(commandBuffer:TVkCommandBuffer;
                                    Binding:TVkUInt32;
                                    Buffer:TVkBuffer;
                                    Offset:TVkDeviceSize);
}

Procedure vkCmdBindSB(cmd:TVkCommandBuffer;
                      point:TVkPipelineBindPoint;
                      layout:TVkPipelineLayout;
                      aSet,aBind,aElem:TVkUInt32;
                      buffer:TVkBuffer;
                      offset,range:TVkDeviceSize);

procedure vkCmdWaitEvent(commandBuffer:TVkCommandBuffer;
                         event:TVkEvent;
                         srcStageMask:TVkPipelineStageFlags;
                         dstStageMask:TVkPipelineStageFlags);


var
 VulkanDeviceGuid:TGUID;
 VulkanAppFlags  :t_vulkan_app_flags=[];

 VulkanApp:TVulkanApp;
 DebugReport:TVDebugReport;
 Device:TvDevice;

 FlipQueue:TvQueue;
 RenderQueue:TvQueue;

function  LoadVulkan:Boolean;
Procedure InitVulkan;
function  IsInitVulkan:Boolean;

function  GetPhysicalDeviceList:APhysicalDeviceProperties;

function  shaderStorageImageExtendedFormats:Boolean;
function  shaderStorageImageReadWithoutFormat:Boolean;
function  shaderStorageImageWriteWithoutFormat:Boolean;
function  shaderInt64:Boolean;
function  shaderInt16:Boolean;
function  shaderInt8:Boolean;
function  shaderFloat16:Boolean;
function  storageBuffer8Bit:Boolean;
function  uniformBuffer8Bit:Boolean;
function  storageBuffer16Bit:Boolean;
function  uniformBuffer16Bit:Boolean;
function  storageInputOutput16:Boolean;
function  sparseBinding:Boolean;
function  sparseResidencyAliased:Boolean;

var
 limits:record

  VK_KHR_swapchain                       :Boolean;
  VK_EXT_external_memory_host            :Boolean;

  VK_EXT_vertex_input_dynamic_state      :Boolean;
  VK_KHR_imageless_framebuffer           :Boolean;
  VK_EXT_provoking_vertex                :Boolean;
  VK_KHR_image_format_list               :Boolean;
  VK_EXT_descriptor_indexing             :Boolean;

  VK_KHR_shader_float16_int8             :Boolean;
  VK_KHR_16bit_storage                   :Boolean;
  VK_KHR_8bit_storage                    :Boolean;
  VK_KHR_push_descriptor                 :Boolean;
  VK_KHR_shader_non_semantic_info        :Boolean;
  VK_EXT_index_type_uint8                :Boolean;
  VK_EXT_scalar_block_layout             :Boolean;
  VK_KHR_workgroup_memory_explicit_layout:Boolean;
  VK_EXT_robustness2                     :Boolean;
  VK_EXT_image_view_min_lod              :Boolean;
  VK_EXT_depth_clip_control              :Boolean;
  VK_EXT_depth_clip_enable               :Boolean;
  VK_EXT_primitive_topology_list_restart :Boolean;

  VK_AMD_device_coherent_memory          :Boolean;

  VK_EXT_memory_budget                   :Boolean;
  VK_EXT_memory_priority                 :Boolean;
  VK_EXT_device_fault                    :Boolean;
  VK_EXT_pageable_device_local_memory    :Boolean;

  DeviceFeature:TVkPhysicalDeviceFeatures;

  shaderFloat16:TVkBool32;
  shaderInt8   :TVkBool32;

  storageBuffer8BitAccess          :TVkBool32;
  uniformAndStorageBuffer8BitAccess:TVkBool32;
  storagePushConstant8             :TVkBool32;

  storageBuffer16BitAccess          :TVkBool32;
  uniformAndStorageBuffer16BitAccess:TVkBool32;
  storagePushConstant16             :TVkBool32;
  storageInputOutput16              :TVkBool32;

  primitiveTopologyListRestart      :TVkBool32;
  primitiveTopologyPatchListRestart :TVkBool32;

  workgroupMemoryExplicitLayout8BitAccess :TVkBool32;
  workgroupMemoryExplicitLayout16BitAccess:TVkBool32;

  robustBufferAccess2:TVkBool32;
  robustImageAccess2 :TVkBool32;
  nullDescriptor     :TVkBool32;

  DescriptorIndexingFeatures:TVkPhysicalDeviceDescriptorIndexingFeatures;

  maxUniformBufferRange:TVkUInt32;
  maxStorageBufferRange:TVkUInt32;
  maxPushConstantsSize :TVkUInt32;
  maxSamplerLodBias    :TVkFloat;
  maxSamplerAnisotropy :TVkFloat;

  minMemoryMapAlignment          :TVkSize;
  minTexelBufferOffsetAlignment  :TVkDeviceSize;
  minUniformBufferOffsetAlignment:TVkDeviceSize;
  minStorageBufferOffsetAlignment:TVkDeviceSize;

  framebufferColorSampleCounts  :TVkSampleCountFlags;
  framebufferDepthSampleCounts  :TVkSampleCountFlags;
  framebufferStencilSampleCounts:TVkSampleCountFlags;

  sampledImageColorSampleCounts  :TVkSampleCountFlags;
  sampledImageIntegerSampleCounts:TVkSampleCountFlags;
  sampledImageDepthSampleCounts  :TVkSampleCountFlags;
  sampledImageStencilSampleCounts:TVkSampleCountFlags;
  storageImageSampleCounts       :TVkSampleCountFlags;

  maxComputeWorkGroupInvocations:TVkUInt32;
  maxComputeWorkGroupSize       :TVkOffset3D;

  minImportedHostPointerAlignment:TVkDeviceSize;
 end;

implementation

uses
 kern_rwlock,
 vMemory;

function shaderStorageImageExtendedFormats:Boolean;
begin
 Result:=Boolean(limits.DeviceFeature.shaderStorageImageExtendedFormats);
end;

function shaderStorageImageReadWithoutFormat:Boolean;
begin
 Result:=Boolean(limits.DeviceFeature.shaderStorageImageReadWithoutFormat);
end;

function shaderStorageImageWriteWithoutFormat:Boolean;
begin
 Result:=Boolean(limits.DeviceFeature.shaderStorageImageWriteWithoutFormat);
end;

function shaderInt64:Boolean;
begin
 Result:=Boolean(limits.DeviceFeature.shaderInt64);
end;

function shaderInt16:Boolean;
begin
 Result:=Boolean(limits.DeviceFeature.shaderInt16);
end;

function shaderInt8:Boolean;
begin
 Result:=Boolean(limits.shaderInt8);
end;

function shaderFloat16:Boolean;
begin
 Result:=Boolean(limits.shaderFloat16);
end;

function storageBuffer8Bit:Boolean;
begin
 Result:=Boolean(limits.storageBuffer8BitAccess);
end;

function uniformBuffer8Bit:Boolean;
begin
 Result:=Boolean(limits.uniformAndStorageBuffer8BitAccess);
end;

function storageBuffer16Bit:Boolean;
begin
 Result:=Boolean(limits.storageBuffer16BitAccess);
end;

function uniformBuffer16Bit:Boolean;
begin
 Result:=Boolean(limits.uniformAndStorageBuffer16BitAccess);
end;

function storageInputOutput16:Boolean;
begin
 Result:=Boolean(limits.storageInputOutput16);
end;

function sparseBinding:Boolean;
begin
 Result:=Boolean(limits.DeviceFeature.sparseBinding);
end;

function sparseResidencyAliased:Boolean;
begin
 Result:=Boolean(limits.DeviceFeature.sparseResidencyAliased);
end;

procedure FillDeviceProperties(physicalDevice:TVkPhysicalDevice);
var
 prop:TVkPhysicalDeviceProperties2;
 memh:TVkPhysicalDeviceExternalMemoryHostPropertiesEXT;
begin
 prop:=Default(TVkPhysicalDeviceProperties2);
 prop.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
 prop.pNext:=@memh;

 memh:=Default(TVkPhysicalDeviceExternalMemoryHostPropertiesEXT);
 memh.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT;

 vkGetPhysicalDeviceProperties2(physicalDevice,@prop);

 limits.maxUniformBufferRange          :=prop.properties.limits.maxUniformBufferRange;
 limits.maxStorageBufferRange          :=prop.properties.limits.maxStorageBufferRange;
 limits.maxPushConstantsSize           :=prop.properties.limits.maxPushConstantsSize;
 limits.maxSamplerLodBias              :=prop.properties.limits.maxSamplerLodBias;
 limits.maxSamplerAnisotropy           :=prop.properties.limits.maxSamplerAnisotropy;

 limits.minMemoryMapAlignment          :=prop.properties.limits.minMemoryMapAlignment;
 limits.minTexelBufferOffsetAlignment  :=prop.properties.limits.minTexelBufferOffsetAlignment;
 limits.minUniformBufferOffsetAlignment:=prop.properties.limits.minUniformBufferOffsetAlignment;
 limits.minStorageBufferOffsetAlignment:=prop.properties.limits.minStorageBufferOffsetAlignment;

 limits.framebufferColorSampleCounts   :=prop.properties.limits.framebufferColorSampleCounts;
 limits.framebufferDepthSampleCounts   :=prop.properties.limits.framebufferDepthSampleCounts;
 limits.framebufferStencilSampleCounts :=prop.properties.limits.framebufferStencilSampleCounts;

 limits.sampledImageColorSampleCounts  :=prop.properties.limits.sampledImageColorSampleCounts;
 limits.sampledImageIntegerSampleCounts:=prop.properties.limits.sampledImageIntegerSampleCounts;
 limits.sampledImageDepthSampleCounts  :=prop.properties.limits.sampledImageDepthSampleCounts;
 limits.sampledImageStencilSampleCounts:=prop.properties.limits.sampledImageStencilSampleCounts;
 limits.storageImageSampleCounts       :=prop.properties.limits.storageImageSampleCounts;

 limits.maxComputeWorkGroupInvocations :=prop.properties.limits.maxComputeWorkGroupInvocations;
 limits.maxComputeWorkGroupSize        :=TVkOffset3D(prop.properties.limits.maxComputeWorkGroupSize);

 limits.minImportedHostPointerAlignment:=memh.minImportedHostPointerAlignment;
end;

procedure FillDeviceExtension(physicalDevice:TVkPhysicalDevice);
var
 i,count:TVkUInt32;
 pProperties:PVkExtensionProperties;
begin
 Writeln;
 count:=0;
 vkEnumerateDeviceExtensionProperties(physicalDevice,nil,@count,nil);
 if (count<>0) then
 begin
  pProperties:=GetMem(count*SizeOf(TVkExtensionProperties));
  vkEnumerateDeviceExtensionProperties(physicalDevice,nil,@count,pProperties);
  For i:=0 to count-1 do
  begin
   Case String(pProperties[i].extensionName) of
    VK_KHR_SWAPCHAIN_EXTENSION_NAME                       :limits.VK_KHR_swapchain                       :=True;
    VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME            :limits.VK_EXT_external_memory_host            :=True;

    VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME      :limits.VK_EXT_vertex_input_dynamic_state      :=True;
    VK_KHR_IMAGELESS_FRAMEBUFFER_EXTENSION_NAME           :limits.VK_KHR_imageless_framebuffer           :=True;
    VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME                :limits.VK_EXT_provoking_vertex                :=True;
    VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME               :limits.VK_KHR_image_format_list               :=True;
    VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME             :limits.VK_EXT_descriptor_indexing             :=True;

    VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME             :limits.VK_KHR_shader_float16_int8             :=True;
    VK_KHR_16BIT_STORAGE_EXTENSION_NAME                   :limits.VK_KHR_16bit_storage                   :=True;
    VK_KHR_8BIT_STORAGE_EXTENSION_NAME                    :limits.VK_KHR_8bit_storage                    :=True;
    VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME                 :limits.VK_KHR_push_descriptor                 :=True;
    VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME        :limits.VK_KHR_shader_non_semantic_info        :=True;
    VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME                :limits.VK_EXT_index_type_uint8                :=True;
    VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME             :limits.VK_EXT_scalar_block_layout             :=True;
    VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME:limits.VK_KHR_workgroup_memory_explicit_layout:=True;
    VK_EXT_ROBUSTNESS_2_EXTENSION_NAME                    :limits.VK_EXT_robustness2                     :=True;
    VK_EXT_IMAGE_VIEW_MIN_LOD_EXTENSION_NAME              :limits.VK_EXT_image_view_min_lod              :=True;
    VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME              :limits.VK_EXT_depth_clip_control              :=True;
    VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME               :limits.VK_EXT_depth_clip_enable               :=True;
    VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME :limits.VK_EXT_primitive_topology_list_restart :=True;

    VK_AMD_DEVICE_COHERENT_MEMORY_EXTENSION_NAME          :limits.VK_AMD_device_coherent_memory          :=True;

    VK_EXT_MEMORY_BUDGET_EXTENSION_NAME                   :limits.VK_EXT_memory_budget                   :=True;
    VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME                 :limits.VK_EXT_memory_priority                 :=True;
    VK_EXT_DEVICE_FAULT_EXTENSION_NAME                    :limits.VK_EXT_device_fault                    :=True;
    VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_EXTENSION_NAME    :limits.VK_EXT_pageable_device_local_memory    :=True;
   end;
  end;
  FreeMem(pProperties);
 end;
end;

//VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME
//VK_KHR_MAINTENANCE_2_EXTENSION_NAME
//VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME
//VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME
//VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME
//VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME
//VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME
//VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME
//VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME
//VK_EXT_SEPARATE_STENCIL_USAGE_EXTENSION_NAME
//VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME

type
 TSortIndex=object
  max:Integer;
  data:array of TVkDeviceQueueCreateInfo;
  procedure add(Index:TVkUInt32);
 end;

procedure PrintInstanceExtension;
var
 i,count:TVkUInt32;
 pProperties:PVkExtensionProperties;
begin
 Writeln;
 count:=0;
 vkEnumerateInstanceExtensionProperties(nil,@count,nil);
 if (count<>0) then
 begin
  pProperties:=GetMem(count*SizeOf(TVkExtensionProperties));
  vkEnumerateInstanceExtensionProperties(nil,@count,pProperties);
  For i:=0 to count-1 do
  begin
   Writeln(pProperties[i].extensionName);
  end;
  FreeMem(pProperties);
 end;
end;

procedure PrintDeviceExtension(physicalDevice:TVkPhysicalDevice);
var
 i,count:TVkUInt32;
 pProperties:PVkExtensionProperties;
begin
 Writeln;
 count:=0;
 vkEnumerateDeviceExtensionProperties(physicalDevice,nil,@count,nil);
 if (count<>0) then
 begin
  pProperties:=GetMem(count*SizeOf(TVkExtensionProperties));
  vkEnumerateDeviceExtensionProperties(physicalDevice,nil,@count,pProperties);
  For i:=0 to count-1 do
  begin
   Writeln(pProperties[i].extensionName);
  end;
  FreeMem(pProperties);
 end;
end;

function getstr_queueFlags(queueFlags:TVkQueueFlags):RawByteString;
begin
 Result:='';
 if (queueFlags and TVkQueueFlags(VK_QUEUE_GRAPHICS_BIT))<>0 then
  Result:=Result+' GRAPHICS';
 if (queueFlags and TVkQueueFlags(VK_QUEUE_COMPUTE_BIT))<>0 then
  Result:=Result+' COMPUTE';
 if (queueFlags and TVkQueueFlags(VK_QUEUE_TRANSFER_BIT))<>0 then
  Result:=Result+' TRANSFER';
 if (queueFlags and TVkQueueFlags(VK_QUEUE_SPARSE_BINDING_BIT))<>0 then
  Result:=Result+' SPARSE_BINDING';
 if (queueFlags and TVkQueueFlags(VK_QUEUE_PROTECTED_BIT))<>0 then
  Result:=Result+' PROTECTED';
 if (queueFlags and TVkQueueFlags(VK_QUEUE_VIDEO_DECODE_BIT_KHR))<>0 then
  Result:=Result+' VIDEO_DECODE';
 if (queueFlags and TVkQueueFlags(VK_QUEUE_VIDEO_ENCODE_BIT_KHR))<>0 then
  Result:=Result+' VIDEO_ENCODE';
end;

procedure PrintQueueFamily(physicalDevice:TVkPhysicalDevice);
var
 i,count:TVkUInt32;
 pFamily:PVkQueueFamilyProperties;
begin
 count:=0;
 vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice,@count,nil);
 if (count=0) then Exit;

 pFamily:=GetMem(count*SizeOf(TVkQueueFamilyProperties));
 vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice,@count,pFamily);

 For i:=0 to count-1 do
 begin
  Writeln(getstr_queueFlags(pFamily[i].queueFlags),':',pFamily[i].queueCount);
 end;

 FreeMem(pFamily);
end;

function MyDebugReportCallback(messageSeverity:TVkDebugUtilsMessageSeverityFlagBitsEXT;
                               messageTypes:TVkDebugUtilsMessageTypeFlagsEXT;
                               const pCallbackData:PVkDebugUtilsMessengerCallbackDataEXT;
                               pUserData:PVkVoid):TVkBool32; {$ifdef Windows}stdcall;{$else}{$ifdef Android}{$ifdef cpuarm}hardfloat;{$else}cdecl;{$endif}{$else}cdecl;{$endif}{$endif}
begin
 TVDebugReport(pUserData).ReportCallback(messageSeverity,messageTypes,pCallbackData);
 Result:=TVkBool32(False);
end;

Constructor TVDebugReport.Create;
var
 cinfo:TVkDebugUtilsMessengerCreateInfoEXT;
 r:TVkResult;
begin
 FStdOut:=StdOut;
 //
 Pointer(FCreateDebugUtilsMessenger ):=vkGetInstanceProcAddr(VulkanApp.FInstance,'vkCreateDebugUtilsMessengerEXT');
 Pointer(FDestroyDebugUtilsMessenger):=vkGetInstanceProcAddr(VulkanApp.FInstance,'vkDestroyDebugUtilsMessengerEXT');
 //
 Pointer(FSetDebugUtilsObjectName   ):=vkGetInstanceProcAddr(VulkanApp.FInstance,'vkSetDebugUtilsObjectNameEXT');
 Pointer(FCmdBeginDebugUtilsLabel   ):=vkGetInstanceProcAddr(VulkanApp.FInstance,'vkCmdBeginDebugUtilsLabelEXT');
 Pointer(FCmdEndDebugUtilsLabel     ):=vkGetInstanceProcAddr(VulkanApp.FInstance,'vkCmdEndDebugUtilsLabelEXT');
 Pointer(FCmdInsertDebugUtilsLabel  ):=vkGetInstanceProcAddr(VulkanApp.FInstance,'vkCmdInsertDebugUtilsLabelEXT');
 //
 if (FCreateDebugUtilsMessenger<>nil) then
 begin
  cinfo:=Default(TVkDebugUtilsMessengerCreateInfoEXT);
  cinfo.sType:=VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
  //
  cinfo.messageSeverity:=ord(VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) or
                         ord(VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT   ) or
                         ord(VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) or
                         ord(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT  );
  //
  cinfo.messageType:=ord(VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT    ) or
                     ord(VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT ){ or
                     ord(VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)};
  //
  cinfo.pfnUserCallback:=@MyDebugReportCallback;
  cinfo.pUserData:=Pointer(Self);
  //
  r:=FCreateDebugUtilsMessenger(VulkanApp.FInstance,@cinfo,nil,@FHandle);
  if (r<>VK_SUCCESS) then
  begin
   Writeln(StdErr,'CreateDebugReportCallback:',r);
   Exit;
  end;
 end;
end;

Destructor TVDebugReport.Destroy;
begin
 if (FDestroyDebugUtilsMessenger<>nil) then
 begin
  FDestroyDebugUtilsMessenger(VulkanApp.FInstance,FHandle,nil);
 end;
end;

procedure TVDebugReport.SetObjectName(objectType:TVkObjectType;objectHandle:TVkUInt64;pObjectName:PVkChar);
var
 info:TVkDebugUtilsObjectNameInfoEXT;
begin
 if (FSetDebugUtilsObjectName<>nil) and (objectHandle<>VK_NULL_HANDLE) then
 begin
  info:=Default(TVkDebugUtilsObjectNameInfoEXT);
  info.sType:=VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
  info.objectType  :=objectType;
  info.objectHandle:=objectHandle;
  info.pObjectName :=pObjectName;
  //
  FSetDebugUtilsObjectName(Device.FHandle,@info);
 end;
end;

procedure TVDebugReport.CmdBeginLabel(commandBuffer:TVkCommandBuffer;pLabelName:PVkChar);
var
 info:TVkDebugUtilsLabelEXT;
begin
 if (FCmdBeginDebugUtilsLabel<>nil) and (commandBuffer<>VK_NULL_HANDLE) then
 begin
  info:=Default(TVkDebugUtilsLabelEXT);
  info.sType:=VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
  info.pLabelName:=pLabelName;
  //
  FCmdBeginDebugUtilsLabel(commandBuffer,@info);
 end;
end;

procedure TVDebugReport.CmdEndLabel(commandBuffer:TVkCommandBuffer);
begin
 if (FCmdEndDebugUtilsLabel<>nil) and (commandBuffer<>VK_NULL_HANDLE) then
 begin
  FCmdEndDebugUtilsLabel(commandBuffer);
 end;
end;

procedure TVDebugReport.CmdInsertLabel(commandBuffer:TVkCommandBuffer;pLabelName:PVkChar);
var
 info:TVkDebugUtilsLabelEXT;
begin
 if (FCmdInsertDebugUtilsLabel<>nil) and (commandBuffer<>VK_NULL_HANDLE) then
 begin
  info:=Default(TVkDebugUtilsLabelEXT);
  info.sType:=VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
  info.pLabelName:=pLabelName;
  //
  FCmdInsertDebugUtilsLabel(commandBuffer,@info);
 end;
end;

procedure TVDebugReport.ReportCallback(messageSeverity:TVkDebugUtilsMessageSeverityFlagBitsEXT;
                                       messageTypes:TVkDebugUtilsMessageTypeFlagsEXT;
                                       const pCallbackData:PVkDebugUtilsMessengerCallbackDataEXT
                                      );
//var
 //i:Integer;
begin

 if Pos('not consumed by fragment shader',pCallbackData^.pMessage)<>0 then Exit;
 if Pos('fragment shader writes to output location 0 with no matching attachment',pCallbackData^.pMessage)<>0 then Exit;

 Writeln(FStdOut,pCallbackData^.pMessage);

 {
 sType:TVkStructureType;
 pNext:PVkVoid;
 flags:TVkDebugUtilsMessengerCallbackDataFlagsEXT;
 pMessageIdName:PVkChar;
 messageIdNumber:TVkInt32;
 pMessage:PVkChar;
 queueLabelCount:TVkUInt32;
 pQueueLabels:PVkDebugUtilsLabelEXT;
 cmdBufLabelCount:TVkUInt32;
 pCmdBufLabels:PVkDebugUtilsLabelEXT;
 objectCount:TVkUInt32;
 pObjects:PVkDebugUtilsObjectNameInfoEXT;
 }

 {
 Case objectType of

  VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT:
   Case DWORD(location) of
    $0609A13B:
     begin
      if Pos('not consumed by fragment shader',pMessage)<>0 then Exit;
      if Pos('fragment shader writes to output location 0 with no matching attachment',pMessage)<>0 then Exit;
     end;
   end;

  VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT:
   Case DWORD(location) of

    $A7BB8DB6:if Pos('(Float16)',pMessage)<>0 then Exit;

    $92394C89:
     begin
      i:=Pos('|',pMessage);
      if (i<>0) then
      begin
       pMessage:=@pMessage[i];
       i:=Pos('|',pMessage);
       if (i<>0) then
       begin
        pMessage:=@pMessage[i-1];
       end;
      end;
     end;

    else;
   end;
  else;
 end;

 Writeln(pMessage);
 }

end;

function vkGetPhysicalDevice4Type(pPhysicalDevices:PVkPhysicalDevice;count:TVkUInt32;deviceType:TVkPhysicalDeviceType):TVkPhysicalDevice;
var
 i:TVkUInt32;
 deviceProperties:TVkPhysicalDeviceProperties;
begin
 Result:=VK_NULL_HANDLE;
 if (count<>0) then
 For i:=0 to count-1 do
 begin
  deviceProperties:=Default(TVkPhysicalDeviceProperties);
  vkGetPhysicalDeviceProperties(pPhysicalDevices[i],@deviceProperties);
  if (deviceProperties.deviceType=deviceType) then Exit(pPhysicalDevices[i]);
 end;
end;

function vkGetPhysicalDevice4Guid(pPhysicalDevices:PVkPhysicalDevice;count:TVkUInt32;Guid:TGUID):TVkPhysicalDevice;
var
 i:TVkUInt32;
 deviceProperties:TVkPhysicalDeviceProperties;
begin
 Result:=VK_NULL_HANDLE;
 if (count<>0) then
 For i:=0 to count-1 do
 begin
  deviceProperties:=Default(TVkPhysicalDeviceProperties);
  vkGetPhysicalDeviceProperties(pPhysicalDevices[i],@deviceProperties);
  if CompareByte(deviceProperties.pipelineCacheUUID,Guid,SizeOf(TGUID))=0 then
  begin
   Exit(pPhysicalDevices[i]);
  end;
 end;
end;

function vkGetPhysicalDevice(vkInstance:TVkInstance):TVkPhysicalDevice;
var
 i,count:TVkUInt32;
 pPhysicalDevices:PVkPhysicalDevice;
 deviceProperties:TVkPhysicalDeviceProperties;
begin
 Result:=VK_NULL_HANDLE;
 count:=0;
 vkEnumeratePhysicalDevices(vkInstance,@count,nil);
 if (count=0) then Exit;
 //
 pPhysicalDevices:=GetMem(count*SizeOf(TVkPhysicalDevice));
 vkEnumeratePhysicalDevices(vkInstance,@count,pPhysicalDevices);
 //
 For i:=0 to count-1 do
 begin
  deviceProperties:=Default(TVkPhysicalDeviceProperties);
  vkGetPhysicalDeviceProperties(pPhysicalDevices[i],@deviceProperties);
  Writeln(deviceProperties.deviceName);
  Writeln('apiVersion:',VK_VERSION_MAJOR(deviceProperties.apiVersion),'.',
                        VK_VERSION_MINOR(deviceProperties.apiVersion),'.',
                        VK_VERSION_PATCH(deviceProperties.apiVersion));
  Writeln('-----------');
 end;
 //
 Result:=vkGetPhysicalDevice4Guid(pPhysicalDevices,count,VulkanDeviceGuid);
 //
 if (Result=VK_NULL_HANDLE) then
 begin
  Result:=vkGetPhysicalDevice4Type(pPhysicalDevices,count,VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU);
  //
  if (Result=VK_NULL_HANDLE) then
  begin
   Result:=vkGetPhysicalDevice4Type(pPhysicalDevices,count,VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU);
  end;
 end;
 //
 if (Result=VK_NULL_HANDLE) then
 if (count>0) then
 begin
  Result:=pPhysicalDevices[0];
 end;
 //
 FreeMem(pPhysicalDevices);

 Writeln('Select GPU:');
 deviceProperties:=Default(TVkPhysicalDeviceProperties);
 vkGetPhysicalDeviceProperties(Result,@deviceProperties);
 Writeln(' ',deviceProperties.deviceName);
 Writeln(' apiVersion:',VK_VERSION_MAJOR(deviceProperties.apiVersion),'.',
                        VK_VERSION_MINOR(deviceProperties.apiVersion),'.',
                        VK_VERSION_PATCH(deviceProperties.apiVersion));

end;

function GetInstanceExtensionNames:TExtensionNames;
begin
 Result:=nil;
 SetLength(Result,4);
 Result[0]:=VK_KHR_SURFACE_EXTENSION_NAME;
 Result[1]:=VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
 Result[2]:=VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
 Result[3]:=VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME;
end;

function GetVkApplicationInfo:TVkApplicationInfo;
begin
 Result:=Default(TVkApplicationInfo);
 Result.sType             :=VK_STRUCTURE_TYPE_APPLICATION_INFO;
 Result.pApplicationName  :='fpPS4';
 Result.applicationVersion:=VK_MAKE_VERSION(0, 0, 1);
 Result.pEngineName       :='fpPS4';
 Result.engineVersion     :=VK_MAKE_VERSION(0, 0, 1);
 Result.apiVersion        :=VK_MAKE_API_VERSION(0, 1, 1, 0); //VK_API_VERSION_1_1
end;

function GetPhysicalDeviceList(vkInstance:TVkInstance):APhysicalDeviceProperties;
var
 i,count:TVkUInt32;
 pPhysicalDevices:PVkPhysicalDevice;
begin
 Result:=nil;
 count:=0;
 vkEnumeratePhysicalDevices(vkInstance,@count,nil);
 if (count=0) then Exit;
 //
 pPhysicalDevices:=GetMem(count*SizeOf(TVkPhysicalDevice));
 SetLength(Result,count);
 //
 vkEnumeratePhysicalDevices(vkInstance,@count,pPhysicalDevices);
 For i:=0 to count-1 do
 begin
  Result[i]:=Default(TVkPhysicalDeviceProperties);
  vkGetPhysicalDeviceProperties(pPhysicalDevices[i],@Result[i]);
 end;
 //
 FreeMem(pPhysicalDevices);
end;

function GetPhysicalDeviceList:APhysicalDeviceProperties;
var
 FInstance:TVkInstance;
 vkApp    :TVkApplicationInfo;
 vkExtList:TExtensionNames;
 vkCInfo  :TVkInstanceCreateInfo;
 r:TVkResult;
begin
 Result:=nil;

 FInstance:=VK_NULL_HANDLE;

 vkApp:=GetVkApplicationInfo;

 vkExtList:=GetInstanceExtensionNames;

 vkCInfo:=Default(TVkInstanceCreateInfo);
 vkCInfo.sType:=VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
 vkCInfo.pApplicationInfo:=@vkApp;

 vkCInfo.enabledExtensionCount  :=Length(vkExtList);
 vkCInfo.ppEnabledExtensionNames:=@vkExtList[0];

 Writeln('vkCreateInstance->');
 r:=vkCreateInstance(@vkCInfo,nil,@FInstance);
 if (r<>VK_SUCCESS) then
 begin
  Writeln(StdErr,'vkCreateInstance:',r);
  Exit;
 end;
 Writeln('<-vkCreateInstance');

 Result:=GetPhysicalDeviceList(FInstance);

 if (FInstance<>VK_NULL_HANDLE) then
 begin
  vkDestroyInstance(FInstance,nil);
 end;

end;

type
 AVkValidationFeatureEnable =array of TVkValidationFeatureEnableEXT;
 AVkValidationFeatureDisable=array of TVkValidationFeatureDisableEXT;

function GetEnabledFeatures(flags:t_vulkan_app_flags):AVkValidationFeatureEnable;
begin
 Result:=nil;
 if (va_enable_gpu_assisted         in flags) then Insert(VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT                     ,Result,Length(Result));
 if (va_enable_gpu_assisted_reserve in flags) then Insert(VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT,Result,Length(Result));
 if (va_enable_best_practices       in flags) then Insert(VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT                   ,Result,Length(Result));
 if (va_enable_debug_printf         in flags) then Insert(VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT                     ,Result,Length(Result));
 if (va_enable_sync_validation      in flags) then Insert(VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT       ,Result,Length(Result));
end;

function GetDisabledFeatures(flags:t_vulkan_app_flags):AVkValidationFeatureDisable;
begin
 Result:=nil;
 if (va_disable_shaders           in flags) then Insert(VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT                ,Result,Length(Result));
 if (va_disable_thread_safety     in flags) then Insert(VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT          ,Result,Length(Result));
 if (va_disable_api_params        in flags) then Insert(VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT         ,Result,Length(Result));
 if (va_disable_obj_lifetimes     in flags) then Insert(VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT       ,Result,Length(Result));
 if (va_disable_core_checks       in flags) then Insert(VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT            ,Result,Length(Result));
 if (va_disable_unique_handles    in flags) then Insert(VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT         ,Result,Length(Result));
 if (va_disable_shader_validation in flags) then Insert(VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHE_EXT,Result,Length(Result));
end;

Constructor TVulkanApp.Create(flags:t_vulkan_app_flags);
const
 dlayer='VK_LAYER_KHRONOS_validation';
var
 vkApp     :TVkApplicationInfo;
 vkExtList :TExtensionNames;
 vkLayer   :array[0..0] of PChar;
 vkCInfo   :TVkInstanceCreateInfo;
 vkEnabled :AVkValidationFeatureEnable;
 vkDisabled:AVkValidationFeatureDisable;
 vkFeatures:TVkValidationFeaturesEXT;
 Features2 :TVkPhysicalDeviceFeatures2;
 F16_8     :TVkPhysicalDeviceShaderFloat16Int8Features;
 FSF_8     :TVkPhysicalDevice8BitStorageFeatures;
 FSF16     :TVkPhysicalDevice16BitStorageFeatures;
 FPTL      :TVkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT;
 FWMEL     :TVkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR;
 FRF       :TVkPhysicalDeviceRobustness2FeaturesEXT;
 FDI       :TVkPhysicalDeviceDescriptorIndexingFeatures;
 r:TVkResult;
begin
 vkApp:=GetVkApplicationInfo;

 vkExtList:=GetInstanceExtensionNames;

 vkCInfo:=Default(TVkInstanceCreateInfo);
 vkCInfo.sType:=VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
 vkCInfo.pApplicationInfo:=@vkApp;

 if (va_debug_utils in flags) then
 begin
  //VK_EXT_debug_utils
  Insert(VK_EXT_DEBUG_UTILS_EXTENSION_NAME,vkExtList,Length(vkExtList));

  if (va_validation_layer in flags) and InstanceLayersIsExist(dlayer) then
  begin
   vkLayer[0]:=dlayer;
   vkCInfo.enabledLayerCount  :=1;
   vkCInfo.ppEnabledLayerNames:=@vkLayer;
  end;
 end;

 vkCInfo.enabledExtensionCount  :=Length(vkExtList);
 vkCInfo.ppEnabledExtensionNames:=@vkExtList[0];

 if (va_debug_utils in flags) then
 begin
  vkEnabled :=GetEnabledFeatures (flags);
  vkDisabled:=GetDisabledFeatures(flags);

  if (Length(vkEnabled)<>0) or
     (Length(vkDisabled)<>0) then
  begin
   vkFeatures:=Default(TVkValidationFeaturesEXT);
   vkFeatures.sType:=VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;

   vkFeatures.enabledValidationFeatureCount :=Length(vkEnabled);
   vkFeatures.pEnabledValidationFeatures    :=@vkEnabled[0];
   vkFeatures.disabledValidationFeatureCount:=Length(vkDisabled);
   vkFeatures.pDisabledValidationFeatures   :=@vkDisabled[0];

   vkCInfo.pNext:=@vkFeatures;
  end;
 end;

 Writeln('vkCreateInstance->');
 r:=vkCreateInstance(@vkCInfo,nil,@FInstance);
 if (r<>VK_SUCCESS) then
 begin
  Writeln(StdErr,'vkCreateInstance:',r);
  Exit;
 end;
 Writeln('<-vkCreateInstance');

 FPhysicalDevice:=vkGetPhysicalDevice(FInstance);
 if (FPhysicalDevice=VK_NULL_HANDLE) then
 begin
  Writeln(StdErr,'failed to choice vulkan GPU');
  halt;
 end;

 limits.DeviceFeature:=Default(TVkPhysicalDeviceFeatures);
 F16_8:=Default(TVkPhysicalDeviceShaderFloat16Int8Features);
 FSF_8:=Default(TVkPhysicalDevice8BitStorageFeatures);
 FSF16:=Default(TVkPhysicalDevice16BitStorageFeatures);
 FWMEL:=Default(TVkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR);
 FRF  :=Default(TVkPhysicalDeviceRobustness2FeaturesEXT);
 FDI  :=Default(TVkPhysicalDeviceDescriptorIndexingFeatures);

 if (vkGetPhysicalDeviceFeatures2<>nil) then
 begin
  Features2:=Default(TVkPhysicalDeviceFeatures2);

  Features2.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
  Features2.pNext:=@F16_8;

  F16_8.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES;
  F16_8.pNext:=@FSF_8;

  FSF_8.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES;
  FSF_8.pNext:=@FSF16;

  FSF16.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
  FSF16.pNext:=@FPTL;

  FPTL.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT;
  FPTL.pNext:=@FWMEL;

  FWMEL.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR;
  FWMEL.pNext:=@FRF;

  FRF.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT;
  FRF.pNext:=@FDI;

  FDI.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES;

  vkGetPhysicalDeviceFeatures2(FPhysicalDevice,@Features2);

  limits.DeviceFeature:=Features2.features;

 end else
 begin
  vkGetPhysicalDeviceFeatures(FPhysicalDevice,@limits.DeviceFeature);
 end;

 limits.shaderFloat16                           :=F16_8.shaderFloat16;
 limits.shaderInt8                              :=F16_8.shaderInt8;

 limits.storageBuffer8BitAccess                 :=FSF_8.storageBuffer8BitAccess;
 limits.uniformAndStorageBuffer8BitAccess       :=FSF_8.uniformAndStorageBuffer8BitAccess;
 limits.storagePushConstant8                    :=FSF_8.storagePushConstant8;

 limits.storageBuffer16BitAccess                :=FSF16.storageBuffer16BitAccess;
 limits.uniformAndStorageBuffer16BitAccess      :=FSF16.uniformAndStorageBuffer16BitAccess;
 limits.storagePushConstant16                   :=FSF16.storagePushConstant16;
 limits.storageInputOutput16                    :=FSF16.storageInputOutput16;

 limits.primitiveTopologyListRestart            :=FPTL.primitiveTopologyListRestart;
 limits.primitiveTopologyPatchListRestart       :=FPTL.primitiveTopologyPatchListRestart;

 limits.workgroupMemoryExplicitLayout8BitAccess :=FWMEL.workgroupMemoryExplicitLayout8BitAccess;
 limits.workgroupMemoryExplicitLayout16BitAccess:=FWMEL.workgroupMemoryExplicitLayout16BitAccess;

 limits.robustBufferAccess2                     :=FRF.robustBufferAccess2;
 limits.robustImageAccess2                      :=FRF.robustImageAccess2;
 limits.nullDescriptor                          :=FRF.nullDescriptor;

 FDI.pNext:=nil;
 limits.DescriptorIndexingFeatures              :=FDI;

 LoadFamily;
end;

function TVulkanApp.InstanceLayersIsExist(P:PChar):Boolean;
var
 l1,l2,i,count:TVkUInt32;
 pProperties:PVkLayerProperties;
begin
 Result:=False;
 l1:=StrLen(P);
 count:=0;
 vkEnumerateInstanceLayerProperties(@count,nil);
 if (l1<>0) and (count<>0) then
 begin
  pProperties:=GetMem(count*SizeOf(TVkLayerProperties));
  vkEnumerateInstanceLayerProperties(@count,pProperties);
  For i:=0 to count-1 do
  begin
   l2:=StrLen(pProperties[i].layerName);
   if (l1=l2) and (CompareByte(pProperties[i].layerName,P^,l1)=0) then
   begin
    FreeMem(pProperties);
    Exit(true);
   end;
  end;
  FreeMem(pProperties);
 end;
end;

Destructor TVulkanApp.Destroy;
begin
 vkDestroyInstance(FInstance,nil);
end;

Procedure TVulkanApp.LoadFamily;
var
 i,count:TVkUInt32;
 pQueue:PVkQueueFamilyProperties;
 bLoaded:Set of (gLoad,cLoad,tLoad);
begin
 count:=0;
 vkGetPhysicalDeviceQueueFamilyProperties(FPhysicalDevice,@count,nil);
 if (count=0) then Exit;
 bLoaded:=[];
 pQueue:=GetMem(count*SizeOf(TVkQueueFamilyProperties));
 vkGetPhysicalDeviceQueueFamilyProperties(FPhysicalDevice,@count,pQueue);
 For i:=0 to count-1 do
 begin
  if (pQueue[i].queueFlags and ord(VK_QUEUE_GRAPHICS_BIT))<>0 then
  begin
   if not (gLoad in bLoaded) then
   begin
    FGFamily:=i;
    FGFamilyCount:=pQueue[i].queueCount;
    bLoaded:=bLoaded+[gLoad];
   end;
  end else
  if (pQueue[i].queueFlags and ord(VK_QUEUE_COMPUTE_BIT))<>0 then
  begin
   if not (cLoad in bLoaded) then
   begin
    FCFamily:=i;
    FCFamilyCount:=pQueue[i].queueCount;
    bLoaded:=bLoaded+[cLoad];
   end;
  end else
  if (pQueue[i].queueFlags and ord(VK_QUEUE_TRANSFER_BIT))<>0 then
  begin
   if not (tLoad in bLoaded) then
   begin
    FTFamily:=i;
    FTFamilyCount:=pQueue[i].queueCount;
    bLoaded:=bLoaded+[tLoad];
   end;
  end;
 end;
 if not (cLoad in bLoaded) then
 begin
  For i:=0 to count-1 do
  if (pQueue[i].queueFlags and ord(VK_QUEUE_COMPUTE_BIT))<>0 then
  begin
   FCFamily:=i;
   FCFamilyCount:=pQueue[i].queueCount;
   Break;
  end;
 end;
 if not (tLoad in bLoaded) then
 begin
  For i:=0 to count-1 do
  if (pQueue[i].queueFlags and ord(VK_QUEUE_TRANSFER_BIT))<>0 then
  begin
   FTFamily:=i;
   FTFamilyCount:=pQueue[i].queueCount;
   Break;
  end;
 end;
 FreeMem(pQueue);
end;

function vkGetQueuePresentFamily(physicalDevice:TVkPhysicalDevice;Surface:TVkSurfaceKHR;var Family:TVkUInt32):Boolean;
var
 i,count:TVkUInt32;
 presentSupport:TVkBool32;
begin
 Result:=False;
 count:=0;
 vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice,@count,nil);
 if (count=0) then Exit;
 For i:=0 to count-1 do
 begin
  presentSupport:=ord(false);
  vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice,i,Surface,@presentSupport);
  if boolean(presentSupport) then
  begin
   Family:=i;
   Exit(True);
  end;
 end;
end;

Constructor TVSurface.Create(Handle:THandle);
var
 ci:TVkWin32SurfaceCreateInfoKHR;
 r:TVkResult;
begin
 ci:=Default(TVkWin32SurfaceCreateInfoKHR);
 ci.sType     :=VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
 ci.hwnd_     :=TVkHWND(Handle);
 ci.hinstance_:=System.HINSTANCE;
 r:=vkCreateWin32SurfaceKHR(VulkanApp.FInstance,@ci,nil,@FHandle);
 if (r<>VK_SUCCESS) then
 begin
  Writeln(StdErr,'vkCreateWin32SurfaceKHR:',r);
  Exit;
 end;
 LoadFamily;
 LoadFormat;
 LoadModes;
end;

Destructor TVSurface.Destroy;
begin
 vkDestroySurfaceKHR(VulkanApp.FInstance,FHandle,nil);
end;

Procedure TVSurface.LoadFamily;
begin
 FPFamily:=0;
 if not vkGetQueuePresentFamily(VulkanApp.FPhysicalDevice,FHandle,FPFamily) then
 begin
  Writeln(StdErr,'failed to chouse QueuePresentFamily');
  Exit;
 end;
end;

Procedure TVSurface.LoadFormat;
var
 count:TVkUInt32;
 formats:PVkSurfaceFormatKHR;
 r:TVkResult;

 function TryFind(format:TVkFormat;var Fformat:TVkSurfaceFormatKHR):Boolean;
 var
  i:TVkUInt32;
 begin
  Result:=false;
  For i:=0 to count-1 do
  begin
   if (formats[i].format=format) then
   begin
    Fformat:=formats[i];
    Exit(true);
   end;
  end;
 end;

begin
 count:=0;
 r:=vkGetPhysicalDeviceSurfaceFormatsKHR(VulkanApp.FPhysicalDevice,FHandle,@count,nil);
 if (r=VK_SUCCESS) and (count<>0) then
 begin
  repeat
   formats:=AllocMem(count*SizeOf(TVkSurfaceFormatKHR));
   vkGetPhysicalDeviceSurfaceFormatsKHR(VulkanApp.FPhysicalDevice,FHandle,@count,formats);

   if TryFind(VK_FORMAT_B8G8R8A8_SRGB        ,Fformat) then Break;
   if TryFind(VK_FORMAT_B8G8R8A8_UNORM       ,Fformat) then Break;

   if TryFind(VK_FORMAT_R8G8B8A8_SRGB        ,Fformat) then Break;
   if TryFind(VK_FORMAT_R8G8B8A8_UNORM       ,Fformat) then Break;

   if TryFind(VK_FORMAT_A8B8G8R8_SRGB_PACK32 ,Fformat) then Break;
   if TryFind(VK_FORMAT_A8B8G8R8_UNORM_PACK32,Fformat) then Break;

   if (Fformat.format=VK_FORMAT_UNDEFINED) then
   begin
    Fformat:=formats[0];
   end;

  until true;
  FreeMem(formats);
  Writeln('VSurfaceFormat:',Fformat.format);
 end;
end;

Procedure TVSurface.LoadModes;
var
 i,count:TVkUInt32;
 presentModes:PVkPresentModeKHR;
 r:TVkResult;
begin
 count:=0;
 FModes[0]:=VK_PRESENT_MODE_IMMEDIATE_KHR;
 FModes[1]:=VK_PRESENT_MODE_FIFO_KHR;
 FModes[2]:=VK_PRESENT_MODE_FIFO_KHR;
 r:=vkGetPhysicalDeviceSurfacePresentModesKHR(VulkanApp.FPhysicalDevice,FHandle,@count,nil);
 if (r=VK_SUCCESS) and (count<>0) then
 begin
  presentModes:=GetMem(count*SizeOf(TVkPresentModeKHR));
  vkGetPhysicalDeviceSurfacePresentModesKHR(VulkanApp.FPhysicalDevice,FHandle,@count,presentModes);
  For i:=0 to count-1 do
  begin
   if (presentModes[i]=VK_PRESENT_MODE_MAILBOX_KHR) then
   begin
    FModes[2]:=VK_PRESENT_MODE_MAILBOX_KHR;
    Break;
   end;
  end;
  FreeMem(presentModes);
 end;
end;

function TVSurface.GetSize:TVkExtent2D;
var
 Fcap:TVkSurfaceCapabilitiesKHR;
begin
 Fcap:=Default(TVkSurfaceCapabilitiesKHR);
 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VulkanApp.FPhysicalDevice,FHandle,@Fcap);
 Result:=Fcap.currentExtent;
end;

procedure TvDeviceCreateInfo.add_queue(Index:TVkUInt32;Queue:PVkQueue);
var
 i,count:Integer;
 r:Boolean;
begin
 count:=Length(data);
 if (count<>0) then
 begin
  r:=false;
  For i:=0 to count-1 do
  begin
   if (data[i].FIndex=Index) then
   begin
    r:=true;
   end else
   if r then
   begin
    SetLength(data,count+1);
    Move(data[i],data[i+1],SizeOf(TSortQueueRec)*(count-i));
    data[i].FIndex:=Index;
    data[i].pQueue:=Queue;
    Exit;
   end;
  end;
 end;
 SetLength(data,count+1);
 data[count].FIndex:=Index;
 data[count].pQueue:=Queue;
end;

procedure TvDeviceCreateInfo.add_ext(P:Pchar);
var
 i:Integer;
begin
 i:=Length(exts);
 SetLength(exts,i+1);
 exts[i]:=P;
end;

procedure TvDeviceCreateInfo.add_feature(P:PVkVoid);
begin
 PAbstractFeature(P)^.pNext:=pFeature;
 pFeature:=P;
end;

procedure TSortIndex.add(Index:TVkUInt32);
var
 i,count:Integer;
begin
 count:=Length(data);
 if (count<>0) then
  For i:=0 to count-1 do
   if (data[i].queueFamilyIndex=Index) then
   begin
    Inc(data[i].queueCount);
    if (max<data[i].queueCount) then max:=data[i].queueCount;
    Exit;
   end;
 SetLength(data,count+1);
 data[count]:=Default(TVkDeviceQueueCreateInfo);
 data[count].sType           :=VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
 data[count].queueFamilyIndex:=Index;
 data[count].queueCount      :=1;
 if (max<1) then max:=1;
end;

Constructor TvDevice.Create(Queues:TvDeviceCreateInfo);
Var
 DeviceFeature:TVkPhysicalDeviceFeatures;
 SortIndex:TSortIndex;
 Priority:array of Single;
 DeviceInfo:TVkDeviceCreateInfo;
 r:TVkResult;
 i,p,w:Integer;
begin
 System.InitCriticalSection(FLock);

 DeviceFeature:=limits.DeviceFeature;
 //DeviceFeature.robustBufferAccess:=VK_FALSE;

 DeviceInfo:=Default(TVkDeviceCreateInfo);
 DeviceInfo.sType:=VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
 DeviceInfo.pEnabledFeatures:=@DeviceFeature;
 DeviceInfo.pNext:=Queues.pFeature;

 DeviceInfo.enabledExtensionCount:=Length(Queues.exts);
 if (DeviceInfo.enabledExtensionCount<>0) then
 begin
  DeviceInfo.ppEnabledExtensionNames:=@Queues.exts[0];
 end;

 SortIndex:=Default(TSortIndex);
 For i:=0 to Length(Queues.data)-1 do
 begin
  SortIndex.add(Queues.data[i].FIndex);
 end;

 DeviceInfo.queueCreateInfoCount:=Length(SortIndex.data);
 DeviceInfo.pQueueCreateInfos:=@SortIndex.data[0];

 Priority:=nil;
 SetLength(Priority,SortIndex.max);
 For i:=0 to High(Priority) do Priority[i]:=1;

 For i:=0 to High(SortIndex.data) do
 begin
  SortIndex.data[i].pQueuePriorities:=@Priority[0];
 end;

 r:=vkCreateDevice(VulkanApp.FPhysicalDevice,@DeviceInfo,nil,@FHandle);
 if (r<>VK_SUCCESS) then
 begin
  Writeln(StdErr,'vkCreateDevice:',r);
  Exit;
 end;

 p:=0;
 For i:=0 to DeviceInfo.queueCreateInfoCount-1 do
  For w:=0 to SortIndex.data[i].queueCount-1 do
  begin
   vkGetDeviceQueue(FHandle,SortIndex.data[i].queueFamilyIndex,w,Queues.data[p].pQueue);
   Inc(p);
  end;
end;

Destructor TvDevice.Destroy;
begin
 System.DoneCriticalSection(FLock);
 vkDestroyDevice(FHandle,nil);
 inherited;
end;

function TvDevice.WaitIdle:TVkResult;
begin
 System.EnterCriticalSection(FLock);
 Result:=vkDeviceWaitIdle(FHandle);
 System.LeaveCriticalSection(FLock);
end;

//

Constructor TvQueue.Create(Family:TVkUInt32);
begin
 FFamily:=Family;
 System.InitCriticalSection(FLock);
end;

Destructor TvQueue.Destroy;
begin
 System.DoneCriticalSection(FLock);
 inherited;
end;

function TvQueue.Submit(submitCount:TVkUInt32;const pSubmits:PVkSubmitInfo;fence:TVkFence):TVkResult;
begin
 System.EnterCriticalSection(FLock);
 Result:=vkQueueSubmit(FHandle,submitCount,pSubmits,fence);
 System.LeaveCriticalSection(FLock);
end;

function TvQueue.WaitIdle:TVkResult;
begin
 System.EnterCriticalSection(FLock);
 Result:=vkQueueWaitIdle(FHandle);
 System.LeaveCriticalSection(FLock);
end;

function TvQueue.PresentKHR(const pPresentInfo:PVkPresentInfoKHR):TVkResult;
begin
 System.EnterCriticalSection(FLock);
 Result:=vkQueuePresentKHR(FHandle,pPresentInfo);
 System.LeaveCriticalSection(FLock);
end;

//

Constructor TvCmdPool.Create(FFamily:TVkUInt32);
var
 cinfo:TVkCommandPoolCreateInfo;
 r:TVkResult;
begin
 cinfo:=Default(TVkCommandPoolCreateInfo);
 cinfo.sType           :=VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
 cinfo.queueFamilyIndex:=FFamily;
 cinfo.flags:=ord(VK_COMMAND_POOL_CREATE_TRANSIENT_BIT){ or
              ord(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT)};
 r:=vkCreateCommandPool(Device.FHandle,@cinfo,nil,@FHandle);
 if (r<>VK_SUCCESS) then
 begin
  Writeln(StdErr,'vkCreateCommandPool:',r);
  exit;
 end;
end;

Destructor TvCmdPool.Destroy;
begin
 vkDestroyCommandPool(Device.FHandle,FHandle,nil);
end;

function TvCmdPool.Alloc:TVkCommandBuffer;
var
 ainfo:TVkCommandBufferAllocateInfo;
 r:TVkResult;
begin
 ainfo:=Default(TVkCommandBufferAllocateInfo);
 ainfo.sType      :=VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
 ainfo.commandPool:=FHandle;
 ainfo.level      :=VK_COMMAND_BUFFER_LEVEL_PRIMARY;
 ainfo.commandBufferCount:=1;
 Result:=VK_NULL_HANDLE;
 r:=vkAllocateCommandBuffers(Device.FHandle,@ainfo,@Result);
 if (r<>VK_SUCCESS) then
 begin
  Writeln(StdErr,'vkAllocateCommandBuffers:',r);
  Exit;
 end;
end;

procedure TvCmdPool.Free(cmd:TVkCommandBuffer);
begin
 if (cmd=VK_NULL_HANDLE) then Exit;
 {
  It is recommended to use vkQueueWaitIdle,
   it seems that vkWaitForFences does not always ensure
   the safety of free the cmd buffer
 }
 vkFreeCommandBuffers(Device.FHandle,FHandle,1,@cmd);
end;

procedure TvCmdPool.Trim;
begin
 vkTrimCommandPool(Device.FHandle,FHandle,0);
end;

//

Constructor TvFence.Create(signaled:Boolean);
var
 cinfo:TVkFenceCreateInfo;
 r:TVkResult;
begin
 cinfo:=Default(TVkFenceCreateInfo);
 cinfo.sType:=VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
 if signaled then cinfo.flags:=ord(VK_FENCE_CREATE_SIGNALED_BIT);
 r:=vkCreateFence(Device.FHandle,@cinfo,nil,@FHandle);
 if (r<>VK_SUCCESS) then
 begin
  Writeln(StdErr,'vkCreateFence:',r);
  Exit;
 end;
end;

Destructor TvFence.Destroy;
begin
 vkDestroyFence(Device.FHandle,FHandle,nil);
end;

function TvFence.Reset:TVkResult;
begin
 Result:=vkResetFences(Device.FHandle,1,@FHandle);
end;

function TvFence.Wait(timeout:TVkUInt64):TVkResult;
begin
 Result:=vkWaitForFences(Device.FHandle,1,@FHandle,VK_TRUE,timeout);
end;

function TvFence.Status:TVkResult;
begin
 Result:=vkGetFenceStatus(Device.FHandle,FHandle);
end;

//

Constructor TvSemaphore.Create;
var
 cinfo:TVkSemaphoreCreateInfo;
begin
 cinfo:=Default(TVkSemaphoreCreateInfo);
 cinfo.sType:=VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
 vkCreateSemaphore(Device.FHandle,@cinfo,nil,@FHandle);
end;

Destructor TvSemaphore.Destroy;
begin
 vkDestroySemaphore(Device.FHandle,FHandle,nil);
end;

//

Constructor TvEvent.Create;
var
 cinfo:TVkEventCreateInfo;
begin
 cinfo:=Default(TVkEventCreateInfo);
 cinfo.sType:=VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
 vkCreateEvent(Device.FHandle,@cinfo,nil,@FHandle);
end;

Destructor TvEvent.Destroy;
begin
 vkDestroyEvent(Device.FHandle,FHandle,nil);
end;

function TvEvent.SetEvent:TVkResult;
begin
 Result:=vkSetEvent(Device.FHandle,FHandle);
end;

function TvEvent.ResetEvent:TVkResult;
begin
 Result:=vkResetEvent(Device.FHandle,FHandle);
end;

function TvEvent.Status:TVkResult;
begin
 Result:=vkGetEventStatus(Device.FHandle,FHandle);
end;

//

procedure vkImageMemoryBarrier(
	   cmdbuffer:TVkCommandBuffer;
	   image:TVkImage;
	   srcAccessMask:TVkAccessFlags;
	   dstAccessMask:TVkAccessFlags;
	   oldImageLayout:TVkImageLayout;
	   newImageLayout:TVkImageLayout;
	   srcStageMask:TVkPipelineStageFlags;
	   dstStageMask:TVkPipelineStageFlags;
	   subresourceRange:TVkImageSubresourceRange);
var
 info:TVkImageMemoryBarrier;
begin
 info:=Default(TVkImageMemoryBarrier);
 info.sType           :=VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
 info.srcAccessMask   :=srcAccessMask;
 info.dstAccessMask   :=dstAccessMask;
 info.oldLayout       :=oldImageLayout;
 info.newLayout       :=newImageLayout;
 info.image           :=image;
 info.subresourceRange:=subresourceRange;

 vkCmdPipelineBarrier(
 	cmdbuffer,
 	srcStageMask,
 	dstStageMask,
 	0,
 	0, nil,
 	0, nil,
 	1, @info);
end;

procedure vkBufferMemoryBarrier(
	   cmdbuffer:TVkCommandBuffer;
	   buffer:TVkBuffer;
	   srcAccessMask:TVkAccessFlags;
	   dstAccessMask:TVkAccessFlags;
           offset,size:TVkDeviceSize;
	   srcStageMask:TVkPipelineStageFlags;
	   dstStageMask:TVkPipelineStageFlags);
var
 info:TVkBufferMemoryBarrier;
begin
 info:=Default(TVkBufferMemoryBarrier);
 info.sType:=VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
 info.srcAccessMask:=srcAccessMask;
 info.dstAccessMask:=dstAccessMask;
 //info.srcQueueFamilyIndex:TVkUInt32;
 //info.dstQueueFamilyIndex:TVkUInt32;
 info.buffer:=buffer;
 info.offset:=offset;
 info.size:=size;

 vkCmdPipelineBarrier(
 	cmdbuffer,
 	srcStageMask,
 	dstStageMask,
 	0,
        0, nil,
 	1, @info,
 	0, nil);

end;

procedure vkMemoryBarrier(
	   cmdbuffer:TVkCommandBuffer;
	   srcAccessMask:TVkAccessFlags;
	   dstAccessMask:TVkAccessFlags;
	   srcStageMask:TVkPipelineStageFlags;
	   dstStageMask:TVkPipelineStageFlags);
var
 info:TVkMemoryBarrier;
begin
 info:=Default(TVkMemoryBarrier);
 info.sType:=VK_STRUCTURE_TYPE_MEMORY_BARRIER;
 info.srcAccessMask:=srcAccessMask;
 info.dstAccessMask:=dstAccessMask;

 vkCmdPipelineBarrier(cmdbuffer,
  srcStageMask,
  dstStageMask,
  0,
  1,
  @info,
  0,
  nil,
  0,
  nil);
end;

procedure vkBarrier(
	   cmdbuffer:TVkCommandBuffer;
	   srcStageMask:TVkPipelineStageFlags;
	   dstStageMask:TVkPipelineStageFlags);
begin
 vkCmdPipelineBarrier(cmdbuffer,
  srcStageMask,
  dstStageMask,
  0,
  0,
  nil,
  0,
  nil,
  0,
  nil);
end;

Procedure vkCmdBindVertexBuffer(commandBuffer:TVkCommandBuffer;
                                Binding:TVkUInt32;
                                Buffer:TVkBuffer;
                                Offset:TVkDeviceSize);
begin
 vkCmdBindVertexBuffers(commandBuffer,Binding,1,@Buffer,@Offset);
end;

{
Procedure vkCmdBindDescriptorBuffer(commandBuffer:TVkCommandBuffer;
                                    Binding:TVkUInt32;
                                    Buffer:TVkBuffer;
                                    Offset:TVkDeviceSize);
var
 info:TVkWriteDescriptorSet;
begin
 info:=Default(TVkWriteDescriptorSet);
 info.sType:=VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;

end;
}

Procedure vkCmdBindSB(cmd:TVkCommandBuffer;
                      point:TVkPipelineBindPoint;
                      layout:TVkPipelineLayout;
                      aSet,aBind,aElem:TVkUInt32;
                      buffer:TVkBuffer;
                      offset,range:TVkDeviceSize);
var
 dwrite:TVkWriteDescriptorSet;
 buf:TVkDescriptorBufferInfo;
begin
 dwrite:=Default(TVkWriteDescriptorSet);
 dwrite.sType          :=VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
 dwrite.dstBinding     :=aBind;
 dwrite.dstArrayElement:=aElem;
 dwrite.descriptorType :=VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
 dwrite.descriptorCount:=1;
 dwrite.pBufferInfo    :=@buf;

 buf:=Default(TVkDescriptorBufferInfo);
 buf.buffer:=buffer;
 buf.offset:=offset;
 buf.range :=range ;

 if (vkCmdPushDescriptorSetKHR=nil) then
 begin
  TPFN_vkVoidFunction(vkCmdPushDescriptorSetKHR):=vkGetInstanceProcAddr(VulkanApp.FInstance,'vkCmdPushDescriptorSetKHR');
 end;

 Assert(vkCmdPushDescriptorSetKHR<>nil);

 vkCmdPushDescriptorSetKHR(cmd,point,layout,aSet,1,@dwrite);
end;

procedure vkCmdWaitEvent(commandBuffer:TVkCommandBuffer;
                         event:TVkEvent;
                         srcStageMask:TVkPipelineStageFlags;
                         dstStageMask:TVkPipelineStageFlags);
begin
 vkCmdWaitEvents(commandBuffer,
  1,
  @event,
  srcStageMask,
  dstStageMask,
  0,
  nil,
  0,
  nil,
  0,
  nil);
end;

var
 _lazy_init:Integer=0;
 _lazy_wait:Pointer=nil;

function IsInitVulkan:Boolean;
begin
 Result:=(load_acq_rel(_lazy_init)=2);
end;

Function TestFFF(F:TVkFormatFeatureFlags):RawByteString;
begin
 Result:='';
 if (ord(F) and ord(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT                 ))<>0 then Result:=Result+'|SAMPLED_IMAGE';
 if (ord(F) and ord(VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT                 ))<>0 then Result:=Result+'|STORAGE_IMAGE';
 if (ord(F) and ord(VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT          ))<>0 then Result:=Result+'|STORAGE_IMAGE_ATOMIC';
 if (ord(F) and ord(VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT          ))<>0 then Result:=Result+'|UNIFORM_TEXEL_BUFFER';
 if (ord(F) and ord(VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT          ))<>0 then Result:=Result+'|STORAGE_TEXEL_BUFFER';
 if (ord(F) and ord(VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT   ))<>0 then Result:=Result+'|STORAGE_TEXEL_BUFFER_ATOMIC';
 if (ord(F) and ord(VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT                 ))<>0 then Result:=Result+'|VERTEX_BUFFER';
 if (ord(F) and ord(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT              ))<>0 then Result:=Result+'|COLOR_ATTACHMENT';
 if (ord(F) and ord(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT        ))<>0 then Result:=Result+'|COLOR_ATTACHMENT_BLEND';
 if (ord(F) and ord(VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT      ))<>0 then Result:=Result+'|DEPTH_STENCIL_ATTACHMENT';
 if (ord(F) and ord(VK_FORMAT_FEATURE_BLIT_SRC_BIT                      ))<>0 then Result:=Result+'|BLIT_SRC';
 if (ord(F) and ord(VK_FORMAT_FEATURE_BLIT_DST_BIT                      ))<>0 then Result:=Result+'|BLIT_DST';
 if (ord(F) and ord(VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT   ))<>0 then Result:=Result+'|SAMPLED_IMAGE_FILTER_LINEAR';
 if (ord(F) and ord(VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG))<>0 then Result:=Result+'|SAMPLED_IMAGE_FILTER_CUBIC';
 if (ord(F) and ord(VK_FORMAT_FEATURE_TRANSFER_SRC_BIT                  ))<>0 then Result:=Result+'|TRANSFER_SRC';
 if (ord(F) and ord(VK_FORMAT_FEATURE_TRANSFER_DST_BIT                  ))<>0 then Result:=Result+'|TRANSFER_DST';
end;

function LoadVulkan:Boolean;
begin
 rw_wlock(_lazy_wait);

 if (load_acq_rel(_lazy_init)<>0) then
 begin
  rw_wunlock(_lazy_wait);
  Exit(True);
 end;

 Result:=LoadVulkanLibrary;

 if Result then
 begin
  Result:=LoadVulkanGlobalCommands;
 end;

 XCHG(_lazy_init,1);

 rw_wunlock(_lazy_wait);
end;

Procedure InitVulkan;
var
 DeviceInfo:TvDeviceCreateInfo;
 //ImgProp:TVkFormatProperties;

 FVIDS:TVkPhysicalDeviceVertexInputDynamicStateFeaturesEXT;
 FILFB:TVkPhysicalDeviceImagelessFramebufferFeatures;
 FDPVF:TVkPhysicalDeviceProvokingVertexFeaturesEXT;

 F16_8:TVkPhysicalDeviceShaderFloat16Int8Features;
 FSF_8:TVkPhysicalDevice8BitStorageFeatures;
 FSF16:TVkPhysicalDevice16BitStorageFeatures;
 FRF  :TVkPhysicalDeviceRobustness2FeaturesEXT;
 FDI  :TVkPhysicalDeviceDescriptorIndexingFeatures;
 FIVML:TVkPhysicalDeviceImageViewMinLodFeaturesEXT;
 FDCC :TVkPhysicalDeviceDepthClipControlFeaturesEXT;
 FDCE :TVkPhysicalDeviceDepthClipEnableFeaturesEXT;
 FPTL :TVkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT;

 FPDLM:TVkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT;

 FScalar:TVkPhysicalDeviceScalarBlockLayoutFeatures;
 FWorkgroupLayout:TVkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR;

 FCoherent:TVkPhysicalDeviceCoherentMemoryFeaturesAMD;

begin
 rw_wlock(_lazy_wait);

 if (load_acq_rel(_lazy_init)<>1) then
 begin
  rw_wunlock(_lazy_wait);
  Exit;
 end;

 VulkanApp:=TVulkanApp.Create(VulkanAppFlags);

 DebugReport:=TVDebugReport.Create;

 FillDeviceExtension (VulkanApp.FPhysicalDevice);
 FillDeviceProperties(VulkanApp.FPhysicalDevice);

 DeviceInfo:=TvDeviceCreateInfo.Create;

 if (VulkanApp.FGFamilyCount>1) then
 begin
  FlipQueue  :=TvQueue.Create(VulkanApp.FGFamily);
  RenderQueue:=TvQueue.Create(VulkanApp.FGFamily);
  DeviceInfo.add_queue(VulkanApp.FGFamily,@FlipQueue  .FHandle);
  DeviceInfo.add_queue(VulkanApp.FGFamily,@RenderQueue.FHandle);
 end else
 begin
  FlipQueue  :=TvQueue.Create(VulkanApp.FGFamily);
  RenderQueue:=FlipQueue;
  DeviceInfo.add_queue(VulkanApp.FGFamily,@FlipQueue  .FHandle);
 end;

 if limits.VK_KHR_swapchain then
 begin
  DeviceInfo.add_ext(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
 end else
 begin
  Writeln(stderr,'VK_KHR_swapchain not support!');
  //raise Exception.Create('VK_KHR_swapchain not support!');
 end;

 if limits.VK_EXT_external_memory_host then
 begin
  DeviceInfo.add_ext(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
  DeviceInfo.add_ext(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME);
 end else
 begin
  Writeln(stderr,'VK_EXT_external_memory_host not support!');
  //raise Exception.Create('VK_EXT_external_memory_host not support!');
 end;

 if limits.VK_AMD_device_coherent_memory then
 begin
  DeviceInfo.add_ext(VK_AMD_DEVICE_COHERENT_MEMORY_EXTENSION_NAME);

  FCoherent:=Default(TVkPhysicalDeviceCoherentMemoryFeaturesAMD);
  FCoherent.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COHERENT_MEMORY_FEATURES_AMD;
  FCoherent.deviceCoherentMemory:=VK_TRUE;

  DeviceInfo.add_feature(@FCoherent);
 end;

 if limits.VK_EXT_memory_budget then
 begin
  DeviceInfo.add_ext(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME);
 end;

 if limits.VK_EXT_memory_priority then
 begin
  DeviceInfo.add_ext(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME);
 end;

 if limits.VK_EXT_device_fault then
 begin
  DeviceInfo.add_ext(VK_EXT_DEVICE_FAULT_EXTENSION_NAME);
 end;

 if limits.VK_EXT_pageable_device_local_memory then
 begin
  DeviceInfo.add_ext(VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_EXTENSION_NAME);

  FPDLM:=Default(TVkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT);
  FPDLM.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PAGEABLE_DEVICE_LOCAL_MEMORY_FEATURES_EXT;
  FPDLM.pageableDeviceLocalMemory:=VK_TRUE;

  DeviceInfo.add_feature(@FPDLM);
 end;

 if limits.VK_EXT_vertex_input_dynamic_state then
 begin
  DeviceInfo.add_ext(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);

  FVIDS:=Default(TVkPhysicalDeviceVertexInputDynamicStateFeaturesEXT);
  FVIDS.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT;
  FVIDS.vertexInputDynamicState:=VK_TRUE;

  DeviceInfo.add_feature(@FVIDS);
 end;

 if limits.VK_KHR_imageless_framebuffer then
 begin
  DeviceInfo.add_ext(VK_KHR_IMAGELESS_FRAMEBUFFER_EXTENSION_NAME);

  FILFB:=Default(TVkPhysicalDeviceImagelessFramebufferFeatures);
  FILFB.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES;
  FILFB.imagelessFramebuffer:=VK_TRUE;

  DeviceInfo.add_feature(@FILFB);
 end;

 if limits.VK_EXT_provoking_vertex then
 begin
  DeviceInfo.add_ext(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);

  FDPVF:=Default(TVkPhysicalDeviceProvokingVertexFeaturesEXT);
  FDPVF.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT;
  FDPVF.provokingVertexLast:=VK_TRUE;

  DeviceInfo.add_feature(@FDPVF);
 end;

 if limits.VK_KHR_image_format_list then
 begin
  DeviceInfo.add_ext(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
 end;

 if limits.VK_EXT_descriptor_indexing then
 begin
  DeviceInfo.add_ext(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);

  FDI:=limits.DescriptorIndexingFeatures;
  FDI.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES;

  DeviceInfo.add_feature(@FDI);
 end;

 //if limits.VK_KHR_push_descriptor then
 //begin
 // DeviceQueues.add_ext(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
 //end;

 if limits.VK_KHR_shader_non_semantic_info then
 begin
  DeviceInfo.add_ext(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME);
 end;

 if limits.VK_EXT_scalar_block_layout then
 begin
  DeviceInfo.add_ext(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME);

  FScalar:=Default(TVkPhysicalDeviceScalarBlockLayoutFeatures);
  FScalar.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES;
  FScalar.scalarBlockLayout:=VK_TRUE;

  DeviceInfo.add_feature(@FScalar);
 end;

 if limits.VK_KHR_workgroup_memory_explicit_layout then
 begin
  DeviceInfo.add_ext(VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);

  FWorkgroupLayout:=Default(TVkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR);
  FWorkgroupLayout.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR;

  FWorkgroupLayout.workgroupMemoryExplicitLayout                 :=VK_TRUE;
  FWorkgroupLayout.workgroupMemoryExplicitLayoutScalarBlockLayout:=VK_TRUE;
  FWorkgroupLayout.workgroupMemoryExplicitLayout8BitAccess       :=limits.workgroupMemoryExplicitLayout8BitAccess;
  FWorkgroupLayout.workgroupMemoryExplicitLayout16BitAccess      :=limits.workgroupMemoryExplicitLayout16BitAccess;

  DeviceInfo.add_feature(@FWorkgroupLayout);
 end;

 if limits.VK_KHR_shader_float16_int8 then
 begin
  DeviceInfo.add_ext(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
 end;

 if (limits.shaderInt8<>0) or
    (limits.shaderFloat16<>0) then
 begin
  F16_8:=Default(TVkPhysicalDeviceShaderFloat16Int8Features);
  F16_8.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES;
  F16_8.shaderFloat16:=limits.shaderFloat16;
  F16_8.shaderInt8   :=limits.shaderInt8;

  DeviceInfo.add_feature(@F16_8);
 end;

 if limits.VK_KHR_8bit_storage then
 begin
  DeviceInfo.add_ext(VK_KHR_8BIT_STORAGE_EXTENSION_NAME);
 end;

 if (limits.storageBuffer8BitAccess<>0) or
    (limits.uniformAndStorageBuffer8BitAccess<>0) then
 begin
  FSF_8:=Default(TVkPhysicalDevice8BitStorageFeaturesKHR);
  FSF_8.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES;
  FSF_8.storageBuffer8BitAccess          :=limits.storageBuffer8BitAccess;
  FSF_8.uniformAndStorageBuffer8BitAccess:=limits.uniformAndStorageBuffer8BitAccess;
  //limits.storagePushConstant8

  DeviceInfo.add_feature(@FSF_8);
 end;

 if limits.VK_KHR_16bit_storage then
 begin
  DeviceInfo.add_ext(VK_KHR_16BIT_STORAGE_EXTENSION_NAME);
 end;

 if (limits.storageBuffer16BitAccess<>0) or
    (limits.uniformAndStorageBuffer16BitAccess<>0) or
    (limits.storageInputOutput16<>0) then
 begin
  FSF16:=Default(TVkPhysicalDevice16BitStorageFeatures);
  FSF16.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
  FSF16.storageBuffer16BitAccess          :=limits.storageBuffer16BitAccess;
  FSF16.uniformAndStorageBuffer16BitAccess:=limits.uniformAndStorageBuffer16BitAccess;
  //FSF16.storagePushConstant16
  FSF16.storageInputOutput16              :=limits.storageInputOutput16;

  DeviceInfo.add_feature(@FSF16);
 end;

 if limits.VK_EXT_robustness2 then
 begin
  DeviceInfo.add_ext(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);

  FRF:=Default(TVkPhysicalDeviceRobustness2FeaturesEXT);
  FRF.sType              :=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT;
  FRF.robustBufferAccess2:=limits.robustBufferAccess2;
  FRF.nullDescriptor     :=limits.nullDescriptor;

  DeviceInfo.add_feature(@FRF);
 end;

 if limits.VK_EXT_image_view_min_lod then
 begin
  DeviceInfo.add_ext(VK_EXT_IMAGE_VIEW_MIN_LOD_EXTENSION_NAME);

  FIVML:=Default(TVkPhysicalDeviceImageViewMinLodFeaturesEXT);
  FIVML.sType :=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_MIN_LOD_FEATURES_EXT;
  FIVML.minLod:=VK_TRUE;

  DeviceInfo.add_feature(@FIVML);
 end;

 if limits.VK_EXT_depth_clip_control then
 begin
  DeviceInfo.add_ext(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);

  FDCC:=Default(TVkPhysicalDeviceDepthClipControlFeaturesEXT);
  FDCC.sType           :=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT;
  FDCC.depthClipControl:=VK_TRUE;

  DeviceInfo.add_feature(@FDCC);
 end;

 if limits.VK_EXT_depth_clip_enable then
 begin
  DeviceInfo.add_ext(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME);

  FDCE:=Default(TVkPhysicalDeviceDepthClipEnableFeaturesEXT);
  FDCE.sType          :=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT;
  FDCE.depthClipEnable:=VK_TRUE;

  DeviceInfo.add_feature(@FDCE);
 end;

 if limits.VK_EXT_primitive_topology_list_restart then
 begin
  DeviceInfo.add_ext(VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME);

  FPTL:=Default(TVkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT);
  FPTL.sType                            :=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT;
  FPTL.primitiveTopologyListRestart     :=limits.primitiveTopologyListRestart;
  FPTL.primitiveTopologyPatchListRestart:=limits.primitiveTopologyPatchListRestart;

  DeviceInfo.add_feature(@FPTL);
 end;

 Device:=TvDevice.Create(DeviceInfo);
 DeviceInfo.Free;

 MemManager:=TvMemManager.Create;

 XCHG(_lazy_init,2);

 //ImgProp:=Default(TVkFormatProperties);
 //vkGetPhysicalDeviceFormatProperties(VulkanApp.FPhysicalDevice,VK_FORMAT_R8G8B8A8_UNORM,@ImgProp);
 //Writeln('R8G8B8A8_UNORM:',TestFFF(ImgProp.optimalTilingFeatures));
 //writeln;
 //vkGetPhysicalDeviceFormatProperties(VulkanApp.FPhysicalDevice,VK_FORMAT_R8G8B8A8_SRGB,@ImgProp);
 //Writeln('R8G8B8A8_SRGB:',TestFFF(ImgProp.optimalTilingFeatures));
 //writeln;
 //vkGetPhysicalDeviceFormatProperties(VulkanApp.FPhysicalDevice,VK_FORMAT_B8G8R8A8_SRGB,@ImgProp);
 //Writeln('B8G8R8A8_SRGB:',TestFFF(ImgProp.optimalTilingFeatures));
 //writeln;
 //vkGetPhysicalDeviceFormatProperties(VulkanApp.FPhysicalDevice,VK_FORMAT_A8B8G8R8_SRGB_PACK32,@ImgProp);
 //Writeln('A8B8G8R8_SRGB:',TestFFF(ImgProp.optimalTilingFeatures));




 //PrintQueueFamily(VulkanApp.FPhysicalDevice);
 //writeln;
 //PrintInstanceExtension;
 //writeln;
 //PrintDeviceExtension(VulkanApp.FPhysicalDevice);
 //writeln;

 rw_wunlock(_lazy_wait);
end;

//initialization
// SetExceptionMask([exInvalidOp, exDenormalized, exPrecision, exUnderflow]);

end.

