unit uvkuImage; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Vulkan, uvkuBase, uvkuDeviceMemory; type ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TvkuImage = class(TvkuAllocHandle) private fHandle: VkImage; fDeviceCommands: TVkDeviceCommands; fMemory: VkDeviceMemory; fImageMemory: TvkuDeviceMemory; procedure CreateHandle(const aCreateInfo: TVkImageCreateInfo); public property Handle: VkImage read fHandle; property DeviceCommands: TVkDeviceCommands read fDeviceCommands; property Memory: VkDeviceMemory read fMemory; function GetMemoryBarrier( const aAspectMask: VkImageAspectFlags; const aOldLayout: TVkImageLayout; const aNewLayout: TVkImageLayout): TVkImageMemoryBarrier; procedure BindMemory( const aMemory: VkDeviceMemory; const aOffset: VkDeviceSize); function GetMemoryRequirements: TVkMemoryRequirements; procedure AllocateAndBindImageMemory( const aMemoryProperties: TVkPhysicalDeviceMemoryProperties; aAllocCallbacks: PVkAllocationCallbacks = nil); constructor Create( const aCreateInfo: TVkImageCreateInfo; const aDeviceCommands: TVkDeviceCommands; const aAllocCallbacks: PVkAllocationCallbacks = nil); constructor Create( const aHandle: VkImage; const aOwnsHandle: Boolean; const aDeviceCommands: TVkDeviceCommands; const aAllocCallbacks: PVkAllocationCallbacks = nil); destructor Destroy; override; end; implementation uses uvkuUtils; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TvkuImage////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure TvkuImage.CreateHandle(const aCreateInfo: TVkImageCreateInfo); var err: TVkResult; begin err := fDeviceCommands.vkCreateImage(fDeviceCommands.Device, @aCreateInfo, AllocCallbacks, @fHandle); if (err <> VK_SUCCESS) then TvkuErrorException.Create('unable to create image', err); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TvkuImage.GetMemoryBarrier( const aAspectMask: VkImageAspectFlags; const aOldLayout: TVkImageLayout; const aNewLayout: TVkImageLayout): TVkImageMemoryBarrier; var range: TVkImageSubresourceRange; begin FillByte(range, SizeOf(range), 0); range.aspectMask := aAspectMask; range.baseMipLevel := 0; range.levelCount := 1; range.baseArrayLayer := 0; range.layerCount := 1; FillByte(result, SizeOf(result), 0); result.sType := VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; result.pNext := nil; result.srcAccessMask := []; result.dstAccessMask := []; result.oldLayout := aOldLayout; result.newLayout := aNewLayout; result.srcQueueFamilyIndex := VK_QUEUE_FAMILY_IGNORED; result.dstQueueFamilyIndex := VK_QUEUE_FAMILY_IGNORED; result.image := fHandle; result.subresourceRange := range; case aOldLayout of // Undefined layout: // Note: Only allowed as initial layout! // Note: Make sure any writes to the image have been finished VK_IMAGE_LAYOUT_UNDEFINED: begin result.srcAccessMask := [ VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT ]; end; // Old layout is color attachment: // Note: Make sure any writes to the color buffer have been finished VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: begin result.srcAccessMask := [ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ]; end; // Old layout is transfer source: // Note: Make sure any reads from the image have been finished VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: begin result.srcAccessMask := [ VK_ACCESS_TRANSFER_READ_BIT ]; end; // Old layout is shader read (sampler, input attachment): // Note: Make sure any shader reads from the image have been finished VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: begin result.srcAccessMask := [ VK_ACCESS_SHADER_READ_BIT ]; end; end; case aNewLayout of // New layout is transfer destination (copy, blit): // Note: Make sure any copyies to the image have been finished VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: begin result.dstAccessMask := [ VK_ACCESS_TRANSFER_WRITE_BIT ]; end; // New layout is transfer source (copy, blit): // Note: Make sure any reads from and writes to the image have been finished VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: begin result.srcAccessMask := result.srcAccessMask + [ VK_ACCESS_TRANSFER_READ_BIT ]; result.dstAccessMask := [ VK_ACCESS_TRANSFER_READ_BIT ]; end; // New layout is color attachment: // Note: Make sure any writes to the color buffer hav been finished VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: begin result.dstAccessMask := [ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ]; result.srcAccessMask := [ VK_ACCESS_TRANSFER_READ_BIT ]; end; // New layout is depth attachment: // Note: Make sure any writes to depth/stencil buffer have been finished VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: begin result.dstAccessMask := result.dstAccessMask + [ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT ]; end; // New layout is shader read (sampler, input attachment): // Note: Make sure any writes to the image have been finished VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: begin result.srcAccessMask := [ VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT ]; result.dstAccessMask := [ VK_ACCESS_SHADER_READ_BIT ]; end; end; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure TvkuImage.BindMemory(const aMemory: VkDeviceMemory; const aOffset: VkDeviceSize); var err: TVkResult; begin err := fDeviceCommands.vkBindImageMemory(fDeviceCommands.Device, fHandle, aMemory, aOffset); if (err <> VK_SUCCESS) then TvkuErrorException.Create('unable to bind image memory', err); fMemory := aMemory; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TvkuImage.GetMemoryRequirements: TVkMemoryRequirements; begin fDeviceCommands.vkGetImageMemoryRequirements(fDeviceCommands.Device, fHandle, @result); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure TvkuImage.AllocateAndBindImageMemory(const aMemoryProperties: TVkPhysicalDeviceMemoryProperties; aAllocCallbacks: PVkAllocationCallbacks); var i: Integer; bits: VkUint32; MemReq: TVkMemoryRequirements; Mem: VkDeviceMemory; MemAllocInfo: TVkMemoryAllocateInfo; err: TVkResult; begin MemReq := GetMemoryRequirements; FillByte(MemAllocInfo, SizeOf(MemAllocInfo), 0); MemAllocInfo.sType := VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; MemAllocInfo.pNext := nil; MemAllocInfo.allocationSize := MemReq.size; MemAllocInfo.memoryTypeIndex := vkuGetMemoryTypeIndex( aMemoryProperties, MemReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);; if not Assigned(aAllocCallbacks) then aAllocCallbacks := AllocCallbacks; err := fDeviceCommands.vkAllocateMemory(fDeviceCommands.Device, @MemAllocInfo, aAllocCallbacks, @Mem); if (err <> VK_SUCCESS) then raise TvkuErrorException.Create('unable to allocate image memory', err); fImageMemory := TvkuDeviceMemory.Create(Mem, true, fDeviceCommands, aAllocCallbacks); BindMemory(fImageMemory.Handle, 0); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor TvkuImage.Create(const aCreateInfo: TVkImageCreateInfo; const aDeviceCommands: TVkDeviceCommands; const aAllocCallbacks: PVkAllocationCallbacks); begin inherited Create; fDeviceCommands := aDeviceCommands; SetAllocCallbacks(aAllocCallbacks); CreateHandle(aCreateInfo); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor TvkuImage.Create(const aHandle: VkImage; const aOwnsHandle: Boolean; const aDeviceCommands: TVkDeviceCommands; const aAllocCallbacks: PVkAllocationCallbacks); begin inherited Create; fHandle := aHandle; fDeviceCommands := aDeviceCommands; OwnsHandle := aOwnsHandle; SetAllocCallbacks(aAllocCallbacks); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// destructor TvkuImage.Destroy; begin if OwnsHandle and (fHandle <> VK_INVALID_NDP_HANDLE) then fDeviceCommands.vkDestroyImage(fDeviceCommands.Device, fHandle, AllocCallbacks); fHandle := VK_INVALID_NDP_HANDLE; FreeAndNil(fImageMemory); inherited Destroy; end; end.