Skip to content

Commit e7f96cf

Browse files
AI coded support for VK_EXT_host_image_copy host-pointer
Also added a codex skill file for validating Vulkan extensions.
1 parent 2887de9 commit e7f96cf

6 files changed

Lines changed: 115 additions & 4 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
name: validate_extension
3+
description: Validate the implementation and support in lavatube for a given Vulkan extension
4+
metadata:
5+
short-description: Validate lavatube's support for a Vulkan extension
6+
---
7+
8+
1. Check that we were told which Vulkan extension we are supposed to check. If this was not specified, ask.
9+
2. If you are not familiar with the extension, download the extension text from
10+
`https://docs.vulkan.org/refpages/latest/refpages/source/<extension name>.html` and read it.
11+
3. For each new Vulkan command defined in the given extension, look for its generated implementation named
12+
`trace_<name>` in `generated/write_auto.cpp`. Review it for errors, then compare it to the corresponding
13+
read function named `retrace_<name>` in `generated/read_auto.cpp`. Verify that the serialized output is
14+
likely to be read back in correctly. If not found in generated code, they may have been hardcoded in code
15+
in the `src` directory instead.
16+
4. For each new Vulkan structure defined in the given extension, look for its generated implementation named
17+
`write_<name>` in `generated/struct_write_auto.cpp`. Review it for errors, then compare it to the
18+
corresponding read function named `read_<name>` in `generated/struct_read_auto.cpp`. Verify that the
19+
serialized output is likely to be read back in correctly. If not found in generated code, they may have
20+
been hardcoded in the `src/hardcode_*.cpp` files instead.
21+
5. Check if any new object types are defined in the given extension. If so, look at the Vulkan creation
22+
command for the object type, and see if there is any metadata there we should be tracking. If so, that
23+
should be in `src/lavatube.h` under its corresponding `trackable` child class. The trackable class is
24+
typically named the same as the object type but in all lower case and its `vk` prefix replaced with
25+
`tracked`.
26+
6. Check if the extension has been promoted to core (e.g. "Promotion to Vulkan 1.3"). If it has been
27+
promoted, and we have any hardcoded functions, check if these also have core equivalents (ie function
28+
name without the extension suffix). Warn if these are missing.

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,3 +592,4 @@ layer_test(pipeline_executable_properties pipeline_executable_properties)
592592
#layer_test(cooperative_matrix cooperative_matrix)
593593
layer_test(extended_dynamic_state3 extended_dynamic_state3)
594594
#layer_test(push_descriptor)
595+
layer_test(host_image_copy host_image_copy)

scripts/util.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,20 @@ def print_load(self, name, owner): # called for each parameter
515515
z.do('%s = reader.read_uint32_t();' % self.name)
516516
z.do('if (%s == LAVATUBE_VIRTUAL_QUEUE) %s = selected_queue_family_index;' % (self.name, self.name))
517517
if not is_root: z.do('%s = %s;' % (varname, self.name))
518-
elif (self.name == 'ppData' and self.funcname in ['vkMapMemory', 'vkMapMemory2KHR', 'vkMapMemory2']) or self.name == 'pHostPointer':
518+
elif self.name == 'pHostPointer':
519+
if self.funcname in ['VkMemoryToImageCopy', 'VkImageToMemoryCopy']:
520+
tmp_uuint64t = z.tmp('uint64_t')
521+
z.do('%s = reader.read_uint64_t();' % tmp_uuint64t)
522+
z.do('if (%s > 0)' % tmp_uuint64t)
523+
z.brace_begin()
524+
tmp_uuint8t_ptr = z.tmpmem('uint8_t', tmp_uuint64t)
525+
z.do('reader.read_array(%s, %s);' % (tmp_uuint8t_ptr, tmp_uuint64t))
526+
z.do('%s = %s;' % (varname, tmp_uuint8t_ptr))
527+
z.brace_end()
528+
z.do('else %s = nullptr;' % varname)
529+
else:
530+
z.decl('%s%s%s' % (self.mod, self.type, self.param_ptrstr), self.name)
531+
elif (self.name == 'ppData' and self.funcname in ['vkMapMemory', 'vkMapMemory2KHR', 'vkMapMemory2']):
519532
z.decl('%s%s%s' % (self.mod, self.type, self.param_ptrstr), self.name)
520533
elif self.name == 'pfnUserCallback' and self.funcname == 'VkDebugUtilsMessengerCreateInfoEXT':
521534
z.do('%s = messenger_callback; // hijacking this pointer with our own callback function' % varname)
@@ -834,10 +847,24 @@ def print_save(self, name, owner, postprocess = False): # called for each parame
834847
z.do('initialDataSize = 0;')
835848
elif (self.name == 'pInitialData' and self.funcname == 'VkPipelineCacheCreateInfo'):
836849
z.do('assert(false); // pInitialData')
837-
elif (self.name == 'ppData' and self.funcname in ['vkMapMemory', 'vkMapMemory2KHR', 'vkMapMemory2']) or self.name == 'pHostPointer':
850+
elif self.name == 'pHostPointer':
851+
if self.funcname in ['VkMemoryToImageCopy', 'VkImageToMemoryCopy']:
852+
z.decl('uint64_t', 'host_copy_size')
853+
z.do('host_copy_size = host_image_copy_size(writer.host_copy_format, &sptr->imageSubresource, &sptr->imageExtent, sptr->memoryRowLength, sptr->memoryImageHeight);')
854+
z.do('writer.write_uint64_t(host_copy_size);')
855+
z.do('if (host_copy_size > 0 && sptr->pHostPointer) writer.write_array(reinterpret_cast<const char*>(sptr->pHostPointer), host_copy_size);')
856+
else:
857+
pass
858+
elif (self.name == 'ppData' and self.funcname in ['vkMapMemory', 'vkMapMemory2KHR', 'vkMapMemory2']):
838859
pass
839860
elif self.structure:
840-
self.print_struct(self.type, varname, owner, size=self.length)
861+
if self.name == 'pRegions' and self.type in ['VkMemoryToImageCopy', 'VkImageToMemoryCopy'] and self.funcname in ['VkCopyMemoryToImageInfo', 'VkCopyImageToMemoryInfo']:
862+
z.decl('VkFormat', 'prev_host_copy_format', custom='writer.host_copy_format')
863+
z.do('writer.host_copy_format = image_data ? image_data->format : VK_FORMAT_UNDEFINED;')
864+
self.print_struct(self.type, varname, owner, size=self.length)
865+
z.do('writer.host_copy_format = prev_host_copy_format;')
866+
else:
867+
self.print_struct(self.type, varname, owner, size=self.length)
841868
elif self.funcname in ['VkDebugMarkerObjectNameInfoEXT', 'VkDebugMarkerObjectTagInfoEXT', 'vkDebugReportMessageEXT'] and self.name == 'object':
842869
z.do('auto* object_data = debug_object_trackable(writer.parent->records, %sobjectType, %s);' % (owner, varname))
843870
z.do('writer.write_handle(object_data);')

src/util.cpp

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "jsoncpp/json/writer.h"
33

44
#include <string.h>
5+
#include <vulkan/vulkan_format_traits.hpp>
56
#include <spirv/unified1/spirv.h>
67

78
#if defined(_GNU_SOURCE) || defined(__BIONIC__)
@@ -50,6 +51,58 @@ int get_env_int(const char* name, int v)
5051
return v;
5152
}
5253

54+
static VkFormat host_image_copy_plane_format(VkFormat format, VkImageAspectFlags aspect)
55+
{
56+
if ((aspect & (VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT | VK_IMAGE_ASPECT_PLANE_2_BIT)) == 0)
57+
{
58+
return format;
59+
}
60+
61+
uint32_t plane = 0;
62+
if (aspect & VK_IMAGE_ASPECT_PLANE_1_BIT) plane = 1;
63+
else if (aspect & VK_IMAGE_ASPECT_PLANE_2_BIT) plane = 2;
64+
const auto plane_format = VULKAN_HPP_NAMESPACE::planeCompatibleFormat(static_cast<VULKAN_HPP_NAMESPACE::Format>(format), plane);
65+
if (plane_format == VULKAN_HPP_NAMESPACE::Format::eUndefined)
66+
{
67+
return format;
68+
}
69+
return static_cast<VkFormat>(plane_format);
70+
}
71+
72+
uint64_t host_image_copy_size(VkFormat format, const VkImageSubresourceLayers* subresource, const VkExtent3D* extent, uint32_t memory_row_length, uint32_t memory_image_height)
73+
{
74+
if (!subresource || !extent || format == VK_FORMAT_UNDEFINED)
75+
{
76+
return 0;
77+
}
78+
79+
const VkFormat plane_format = host_image_copy_plane_format(format, subresource->aspectMask);
80+
const auto vk_format = static_cast<VULKAN_HPP_NAMESPACE::Format>(plane_format);
81+
const auto block_extent = VULKAN_HPP_NAMESPACE::blockExtent(vk_format);
82+
const uint32_t block_width = block_extent[0] ? block_extent[0] : 1;
83+
const uint32_t block_height = block_extent[1] ? block_extent[1] : 1;
84+
const uint32_t block_depth = block_extent[2] ? block_extent[2] : 1;
85+
const uint32_t block_size = VULKAN_HPP_NAMESPACE::blockSize(vk_format);
86+
if (block_size == 0)
87+
{
88+
WLOG("host_image_copy_size: unsupported format %u", static_cast<uint32_t>(plane_format));
89+
return 0;
90+
}
91+
92+
const uint32_t row_length = memory_row_length ? memory_row_length : extent->width;
93+
const uint32_t image_height = memory_image_height ? memory_image_height : extent->height;
94+
const uint32_t depth = extent->depth ? extent->depth : 1;
95+
if (row_length == 0 || image_height == 0 || depth == 0 || subresource->layerCount == 0)
96+
{
97+
return 0;
98+
}
99+
100+
const uint64_t row_blocks = (row_length + block_width - 1) / block_width;
101+
const uint64_t height_blocks = (image_height + block_height - 1) / block_height;
102+
const uint64_t depth_blocks = (depth + block_depth - 1) / block_depth;
103+
return row_blocks * height_blocks * depth_blocks * static_cast<uint64_t>(subresource->layerCount) * block_size;
104+
}
105+
53106
int get_env_bool(const char* name, int v)
54107
{
55108
const char* tmpstr = getenv(name);
@@ -375,7 +428,6 @@ const char* pretty_print_VkObjectType(VkObjectType val)
375428
case VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA: return "BufferCollectionFUCHSIA";
376429
case VK_OBJECT_TYPE_MICROMAP_EXT: return "Micromap";
377430
case VK_OBJECT_TYPE_OPTICAL_FLOW_SESSION_NV: return "OpticalFlowSessionNV";
378-
case VK_OBJECT_TYPE_MAX_ENUM: assert(false); return "Error";
379431
case VK_OBJECT_TYPE_VIDEO_SESSION_KHR: return "VideoSession";
380432
case VK_OBJECT_TYPE_VIDEO_SESSION_PARAMETERS_KHR: return "VideoSessionParameters";
381433
case VK_OBJECT_TYPE_SHADER_EXT: return "Shader";
@@ -386,6 +438,7 @@ const char* pretty_print_VkObjectType(VkObjectType val)
386438
case VK_OBJECT_TYPE_TENSOR_VIEW_ARM: return "Tensor view";
387439
case VK_OBJECT_TYPE_DATA_GRAPH_PIPELINE_SESSION_ARM: return "Data graph pipeline session";
388440
case VK_OBJECT_TYPE_EXTERNAL_COMPUTE_QUEUE_NV: return "External compute queue";
441+
case VK_OBJECT_TYPE_MAX_ENUM: assert(false); return "Error";
389442
}
390443
return "Error";
391444
}

src/util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ static inline std::string version_to_string(uint32_t v) { return _to_string(VK_V
169169
const char* errorString(const VkResult errorCode);
170170

171171
void check_retval(VkResult stored_retval, VkResult retval);
172+
uint64_t host_image_copy_size(VkFormat format, const VkImageSubresourceLayers* subresource, const VkExtent3D* extent, uint32_t memory_row_length, uint32_t memory_image_height);
172173

173174
static inline bool is_blackhole_mode() { return p__blackhole; }
174175
static inline int selected_gpu() { return p__gpu; }

src/write.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ class lava_file_writer : public file_writer
162162
VkDevice device = VK_NULL_HANDLE;
163163
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
164164
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
165+
VkFormat host_copy_format = VK_FORMAT_UNDEFINED;
165166

166167
inline void write_api_command(uint16_t id);
167168

0 commit comments

Comments
 (0)