copyMemoryToImage function added#1079
Conversation
- Add hostImageCopy as MOVE_TO_LIMIT in device_features.json. - Query and enable VkPhysicalDeviceHostImageCopyFeaturesEXT through limits.hostImageCopy. - Add EUF_HOST_TRANSFER_BIT and map it to VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT. - Wire VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT into hostImageTransfer format usage. - Add host image copy flags and SMemoryToImageCopy. - Add ILogicalDevice::copyMemoryToImage validation and CVulkanLogicalDevice backend. - Validate host-transfer image creation and EHICF_MEMCPY_BIT full-subresource rules.
| //Not sure to remove this? | ||
| // retval.hostImageTransfer = anyFlag(features, VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT); | ||
| retval.hostImageTransfer = anyFlag(features, VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT); |
There was a problem hiding this comment.
we need to report because not every format suports
…ImageCopyProperties`, including `identicalMemoryTypeRequirements`, `copySrcLayouts`, and `copyDstLayouts`. - Updated `ILogicalDevice` to validate source and destination image layouts for host image copy operations in `copyMemoryToImage`, `copyImageToMemory`, `copyImageToImage`, and `transitionImageLayout`. - Extended `device_limits.json` to include new fields for `hostImageCopySrcLayouts`, `hostImageCopyDstLayouts`, and `identicalMemoryTypeRequirements`.
| { | ||
| "comment": [ | ||
| "VK_EXT_host_image_copy", | ||
| "HostImageCopyPropertiesEXT" | ||
| ], | ||
| "entries": [ | ||
| { | ||
| "type": "uint16_t", | ||
| "name": "hostImageCopySrcLayouts", | ||
| "value": 0 | ||
| }, | ||
| { | ||
| "type": "uint16_t", | ||
| "name": "hostImageCopyDstLayouts", | ||
| "value": 0 | ||
| }, | ||
| { | ||
| "type": "bool", | ||
| "name": "identicalMemoryTypeRequirements", | ||
| "value": false | ||
| } | ||
| ] | ||
| }, |
There was a problem hiding this comment.
Ok so this is wrong:
it's not a "limit" per se, or an extension/feature.
limits are either vulkan limits or vulkan features promoted to limits.
this fits neither. it's a physical device property.
Similar to SMemoryProperties you should have a SHostImageCopyProperties and function to query it, and member functions to fill.
it should be equivalent to https://docs.vulkan.org/refpages/latest/refpages/source/VkPhysicalDeviceHostImageCopyProperties.html
Look at how these work:
SMemoryPropertiesSFormatImageUsagesSQueueFamilyProperties- or basically anything in
SInitData
| /* VK_EXT_host_image_copy */ | ||
| uint8_t optimalTilingLayoutUUID[VK_UUID_SIZE]; |
There was a problem hiding this comment.
nope, should be in dedicate SHostImageCopyProperties
| layouts.copySrcLayoutCount = hostImageCopyProperties.copySrcLayoutCount; | ||
| layouts.pCopySrcLayouts = copySrcLayouts.data(); | ||
| layouts.copyDstLayoutCount = hostImageCopyProperties.copyDstLayoutCount; | ||
| layouts.pCopyDstLayouts = copyDstLayouts.data(); |
There was a problem hiding this comment.
bug,
vectors go out of scope and you're holding onto stale pointer.
if you fix this previous issue first you'll realize why we used smart_refctd_dynamic_array for these types of things. it's basically a fixed sized dynamic allocation (no resize like vector + consistent pointers). it's also refcounted to ensure the memory doesn't get freed.
There was a problem hiding this comment.
ok I mistunderstood, It's ok.
but try to use smart_refctd_dynamic_array
| properties.limits.minAccelerationStructureScratchOffsetAlignment = accelerationStructureProperties.minAccelerationStructureScratchOffsetAlignment; | ||
| } | ||
|
|
||
| if (isExtensionSupported(VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME)) |
There was a problem hiding this comment.
this whole thing needs to fill initData's host image copy properties
(don't forget getters for this property)
| if (regions.size() > 0xffff'ffffull) | ||
| { | ||
| NBL_LOG_ERROR("`regions.size()` exceeds `uint32_t` range"); | ||
| return false; | ||
| } |
There was a problem hiding this comment.
ugh, I feel like this is the super protective stuff that AI generates, cool but we'd be having to do it everywhere if it was a real issue
| if (regions.empty()) | ||
| { | ||
| NBL_LOG_ERROR("`regions` must not be empty"); | ||
| return false; | ||
| } |
There was a problem hiding this comment.
why return false?
user wants to copy 0 regions? done, you're good to go :D
There was a problem hiding this comment.
move that to the first check
| for (uint32_t i=0u; i<=static_cast<uint32_t>(layout_t::SHARED_PRESENT); i++) | ||
| { | ||
| const VkImageLayout vkLayout = getVkImageLayoutFromImageLayout(static_cast<layout_t>(i)); | ||
| for (const VkImageLayout l : copySrcLayouts) | ||
| if (l==vkLayout) { properties.limits.hostImageCopySrcLayouts |= 1u<<i; break; } | ||
| for (const VkImageLayout l : copyDstLayouts) | ||
| if (l==vkLayout) { properties.limits.hostImageCopyDstLayouts |= 1u<<i; break; } | ||
| } |
There was a problem hiding this comment.
ok I see what you're doing, instead of an array like vulkan's, you're creating a bitflag. that's actually a good idea for faster checks.
This double for loop seems weird af, you're iterating on all allowed src/dst layouts, per layout.
Why not do this instead:
loop over all copy[Src/Dst]Layouts, getImageLayout from Vk layout. check whether it's != UNDEFINED, and then put it in the correct place in the bitfield uint64_t seems good enough for the bitfield, we won't be dealing with 64+ layouts.
If there is no getImageLayout from Vk layout, implement it
There was a problem hiding this comment.
also good to assert that whatever you're doing with the bitshifts are less than the size of the storage. (e.g. i < 64)
There was a problem hiding this comment.
btw be careful about UNDEFINED because it is 0 in the enum and 1 << 0 is 1.
so we should never see the first bit set. because that would indicate host image copy is valid to/from undefined layout?
| VkPhysicalDeviceProperties2 layouts2 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,&layouts }; | ||
| vkGetPhysicalDeviceProperties2(vk_physicalDevice,&layouts2); | ||
| using layout_t = asset::IImage::LAYOUT; | ||
| for (uint32_t i=0u; i<=static_cast<uint32_t>(layout_t::SHARED_PRESENT); i++) |
There was a problem hiding this comment.
very risky, what if someone added a new layout to image layout, then SHARED_PRESENT wouldn't be the last item.
but anyways, this whole thing could be done with a single for loop
| bool validLayout = false; | ||
| switch (dstImageLayout) | ||
| { | ||
| case IGPUImage::LAYOUT::GENERAL: | ||
| case IGPUImage::LAYOUT::READ_ONLY_OPTIMAL: | ||
| case IGPUImage::LAYOUT::ATTACHMENT_OPTIMAL: | ||
| case IGPUImage::LAYOUT::TRANSFER_SRC_OPTIMAL: | ||
| case IGPUImage::LAYOUT::TRANSFER_DST_OPTIMAL: | ||
| case IGPUImage::LAYOUT::PRESENT_SRC: | ||
| case IGPUImage::LAYOUT::SHARED_PRESENT: | ||
| validLayout = true; | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| if (!validLayout) | ||
| { | ||
| NBL_LOG_ERROR("Invalid `dstImageLayout`"); | ||
| return false; | ||
| } |
There was a problem hiding this comment.
vulkan has to make this validation because their flags are simple integers. we have explicit type for LAYOUT.
Just check if it's != UNDEFINED ? no need for switch case
| if ((flags.value & ~static_cast<decltype(flags.value)>(IGPUImage::EHICF_MEMCPY_BIT)) != 0u) | ||
| { | ||
| NBL_LOG_ERROR("Invalid host image copy flags"); | ||
| return false; | ||
| } |
There was a problem hiding this comment.
again, no need for this, vulkan has to do these types of validations, cause their flags are just uint32_t's
typedef uint32_t VkFlags;
typedef VkFlags VkFormatFeatureFlags;
you don't have to make this validation. we have explicit enum class type for host image copy. and we shouldn't be resposnible for user doing anything weird with it. (we just translate this to vulkan flag and pass it down)
if vulkan gives validation error about this, we know it's us who did the translation incorrectly and passed incorrect flags
| return false; | ||
| } | ||
| // device `pCopyDstLayouts` membership: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-VkCopyMemoryToImageInfo-dstImageLayout-09060 | ||
| if (!(getPhysicalDevice()->getLimits().hostImageCopyDstLayouts & (1u<<static_cast<uint32_t>(dstImageLayout)))) |
There was a problem hiding this comment.
good check against bitflag, but it needs to change to physdev->gethostimagecopyproperties
Description
Testing
TODO list: