diff --git a/tools/venus/layer/draw.c b/tools/venus/layer/draw.c new file mode 100644 index 000000000..d121e933b --- /dev/null +++ b/tools/venus/layer/draw.c @@ -0,0 +1,411 @@ +/* + * The Mali compatibility layer's draw and descriptor emulation (added to David Cimo's layer): + * + * - multiDrawIndirect: an indirect draw with drawCount > 1 is recorded as drawCount single draws + * at offset + i * stride. Mali-G57 (Bifrost/Valhall v9) has none, and DXVK refuses the device. + * - robustness2's nullDescriptor: VK_NULL_HANDLE buffers, image views and vertex buffers in + * descriptor writes (plain, template and push) and vertex-buffer binds are replaced with small + * stand-ins the layer owns. robustBufferAccess2 itself maps to robustBufferAccess (layer.c). + * A texel-buffer view stays null: its format is not known here. + */ +#include +#include + +#include "mali_compat.h" + +/* --- multiDrawIndirect --- */ + +VKAPI_ATTR void VKAPI_CALL CmdDrawIndirect(VkCommandBuffer cmd, VkBuffer buffer, VkDeviceSize offset, + uint32_t draw_count, uint32_t stride) +{ + struct device *dev = device_from_handle(cmd); + if (draw_count <= 1) { + dev->CmdDrawIndirect(cmd, buffer, offset, draw_count, stride); + return; + } + for (uint32_t i = 0; i < draw_count; i++) + dev->CmdDrawIndirect(cmd, buffer, offset + (VkDeviceSize)i * stride, 1, stride); +} + +VKAPI_ATTR void VKAPI_CALL CmdDrawIndexedIndirect(VkCommandBuffer cmd, VkBuffer buffer, VkDeviceSize offset, + uint32_t draw_count, uint32_t stride) +{ + struct device *dev = device_from_handle(cmd); + if (draw_count <= 1) { + dev->CmdDrawIndexedIndirect(cmd, buffer, offset, draw_count, stride); + return; + } + for (uint32_t i = 0; i < draw_count; i++) + dev->CmdDrawIndexedIndirect(cmd, buffer, offset + (VkDeviceSize)i * stride, 1, stride); +} + +/* --- nullDescriptor stand-ins --- */ + +/* A template's entries, to find the null handles in the data it is given. */ +struct template_info { + uint32_t count; + VkDescriptorUpdateTemplateEntry *entries; + size_t size; /* bytes the data spans */ +}; + +#define NULL_BUFFER_SIZE 65536 + +static uint32_t memory_type(const struct device *dev, uint32_t bits, VkMemoryPropertyFlags want) +{ + for (uint32_t i = 0; i < dev->memory_properties.memoryTypeCount; i++) + if ((bits & (1u << i)) && (dev->memory_properties.memoryTypes[i].propertyFlags & want) == want) + return i; + for (uint32_t i = 0; i < dev->memory_properties.memoryTypeCount; i++) + if (bits & (1u << i)) + return i; + return 0; +} + +/* Made once per device, under its lock, the first time a null descriptor is seen. */ +static bool null_resources(struct device *dev) +{ + if (dev->null_ready) + return dev->null_ready > 0; + dev->null_ready = -1; + + VkBufferCreateInfo binfo = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = NULL_BUFFER_SIZE, + .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + }; + if (dev->CreateBuffer(dev->handle, &binfo, NULL, &dev->null_buffer) != VK_SUCCESS) + return false; + VkMemoryRequirements breq; + dev->GetBufferMemoryRequirements(dev->handle, dev->null_buffer, &breq); + VkMemoryAllocateInfo balloc = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = breq.size, + .memoryTypeIndex = memory_type(dev, breq.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT), + }; + if (dev->AllocateMemory(dev->handle, &balloc, NULL, &dev->null_buffer_memory) != VK_SUCCESS) + return false; + dev->BindBufferMemory(dev->handle, dev->null_buffer, dev->null_buffer_memory, 0); + /* A null buffer reads as zeros. */ + void *data = NULL; + if ((dev->memory_properties.memoryTypes[balloc.memoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && + dev->MapMemory(dev->handle, dev->null_buffer_memory, 0, VK_WHOLE_SIZE, 0, &data) == VK_SUCCESS) { + memset(data, 0, (size_t)breq.size); + dev->UnmapMemory(dev->handle, dev->null_buffer_memory); + } + + VkImageCreateInfo iinfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .format = VK_FORMAT_R8G8B8A8_UNORM, + .extent = {1, 1, 1}, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + }; + if (dev->CreateImage(dev->handle, &iinfo, NULL, &dev->null_image) != VK_SUCCESS) + return false; + VkMemoryRequirements ireq; + dev->GetImageMemoryRequirements(dev->handle, dev->null_image, &ireq); + VkMemoryAllocateInfo ialloc = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = ireq.size, + .memoryTypeIndex = memory_type(dev, ireq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT), + }; + if (dev->AllocateMemory(dev->handle, &ialloc, NULL, &dev->null_image_memory) != VK_SUCCESS) + return false; + dev->BindImageMemory(dev->handle, dev->null_image, dev->null_image_memory, 0); + VkImageViewCreateInfo vinfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = dev->null_image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = VK_FORMAT_R8G8B8A8_UNORM, + .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}, + }; + if (dev->CreateImageView(dev->handle, &vinfo, NULL, &dev->null_view) != VK_SUCCESS) + return false; + dev->null_ready = 1; + layer_log("nullDescriptor: stand-in buffer and 1x1 image ready"); + return true; +} + +void null_resources_destroy(struct device *dev) +{ + for (uint32_t i = 0; i < dev->templates.capacity; i++) { + struct template_info *t = dev->templates.values ? dev->templates.values[i] : NULL; + if (t) { + free(t->entries); + free(t); + } + } + map_free(&dev->templates); + if (dev->null_view) dev->DestroyImageView(dev->handle, dev->null_view, NULL); + if (dev->null_image) dev->DestroyImage(dev->handle, dev->null_image, NULL); + if (dev->null_image_memory) dev->FreeMemory(dev->handle, dev->null_image_memory, NULL); + if (dev->null_buffer) dev->DestroyBuffer(dev->handle, dev->null_buffer, NULL); + if (dev->null_buffer_memory) dev->FreeMemory(dev->handle, dev->null_buffer_memory, NULL); +} + +static bool is_buffer_type(VkDescriptorType t) +{ + return t == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || t == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || + t == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || t == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; +} + +static bool is_image_type(VkDescriptorType t) +{ + return t == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || t == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || + t == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || t == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; +} + +static void fix_buffer(struct device *dev, VkDescriptorBufferInfo *info) +{ + if (info->buffer != VK_NULL_HANDLE || !null_resources(dev)) + return; + info->buffer = dev->null_buffer; + info->offset = 0; + info->range = VK_WHOLE_SIZE; +} + +static void fix_image(struct device *dev, VkDescriptorImageInfo *info) +{ + if (info->imageView != VK_NULL_HANDLE || !null_resources(dev)) + return; + info->imageView = dev->null_view; +} + +/* Copies of the writes with stand-ins for null handles; NULL when nothing needed changing. */ +static VkWriteDescriptorSet *fix_writes(struct device *dev, uint32_t count, const VkWriteDescriptorSet *writes, + void ***scratch, uint32_t *scratch_count) +{ + VkWriteDescriptorSet *copies = NULL; + for (uint32_t i = 0; i < count; i++) { + const VkWriteDescriptorSet *w = &writes[i]; + bool null_found = false; + for (uint32_t j = 0; j < w->descriptorCount && !null_found; j++) { + if (is_buffer_type(w->descriptorType) && w->pBufferInfo && w->pBufferInfo[j].buffer == VK_NULL_HANDLE) + null_found = true; + if (is_image_type(w->descriptorType) && w->pImageInfo && w->pImageInfo[j].imageView == VK_NULL_HANDLE && + w->descriptorType != VK_DESCRIPTOR_TYPE_SAMPLER) + null_found = true; + } + if (!null_found) + continue; + if (!copies) { + copies = malloc(count * sizeof(*copies)); + memcpy(copies, writes, count * sizeof(*copies)); + *scratch = calloc(count, sizeof(void *)); + } + if (is_buffer_type(w->descriptorType)) { + VkDescriptorBufferInfo *b = malloc(w->descriptorCount * sizeof(*b)); + memcpy(b, w->pBufferInfo, w->descriptorCount * sizeof(*b)); + for (uint32_t j = 0; j < w->descriptorCount; j++) + fix_buffer(dev, &b[j]); + copies[i].pBufferInfo = b; + (*scratch)[(*scratch_count)++] = b; + } else { + VkDescriptorImageInfo *im = malloc(w->descriptorCount * sizeof(*im)); + memcpy(im, w->pImageInfo, w->descriptorCount * sizeof(*im)); + for (uint32_t j = 0; j < w->descriptorCount; j++) + fix_image(dev, &im[j]); + copies[i].pImageInfo = im; + (*scratch)[(*scratch_count)++] = im; + } + } + return copies; +} + +static void free_scratch(void **scratch, uint32_t count) +{ + for (uint32_t i = 0; i < count; i++) + free(scratch[i]); + free(scratch); +} + +VKAPI_ATTR void VKAPI_CALL UpdateDescriptorSets(VkDevice device, uint32_t write_count, const VkWriteDescriptorSet *writes, + uint32_t copy_count, const VkCopyDescriptorSet *copies) +{ + struct device *dev = device_from_handle(device); + pthread_mutex_lock(&dev->lock); + void **scratch = NULL; + uint32_t scratch_count = 0; + VkWriteDescriptorSet *fixed = fix_writes(dev, write_count, writes, &scratch, &scratch_count); + pthread_mutex_unlock(&dev->lock); + dev->UpdateDescriptorSets(device, write_count, fixed ? fixed : writes, copy_count, copies); + free(fixed); + free_scratch(scratch, scratch_count); +} + +VKAPI_ATTR void VKAPI_CALL CmdPushDescriptorSet(VkCommandBuffer cmd, VkPipelineBindPoint bind_point, + VkPipelineLayout layout, uint32_t set, uint32_t write_count, + const VkWriteDescriptorSet *writes) +{ + struct device *dev = device_from_handle(cmd); + pthread_mutex_lock(&dev->lock); + void **scratch = NULL; + uint32_t scratch_count = 0; + VkWriteDescriptorSet *fixed = fix_writes(dev, write_count, writes, &scratch, &scratch_count); + pthread_mutex_unlock(&dev->lock); + dev->CmdPushDescriptorSet(cmd, bind_point, layout, set, write_count, fixed ? fixed : writes); + free(fixed); + free_scratch(scratch, scratch_count); +} + +static size_t entry_size(VkDescriptorType t) +{ + if (is_buffer_type(t)) + return sizeof(VkDescriptorBufferInfo); + if (is_image_type(t) || t == VK_DESCRIPTOR_TYPE_SAMPLER) + return sizeof(VkDescriptorImageInfo); + if (t == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + return 1; + return sizeof(uint64_t); +} + +VKAPI_ATTR VkResult VKAPI_CALL CreateDescriptorUpdateTemplate(VkDevice device, + const VkDescriptorUpdateTemplateCreateInfo *info, + const VkAllocationCallbacks *allocator, + VkDescriptorUpdateTemplate *tmpl) +{ + struct device *dev = device_from_handle(device); + VkResult result = dev->CreateDescriptorUpdateTemplate(device, info, allocator, tmpl); + if (result != VK_SUCCESS) + return result; + struct template_info *t = calloc(1, sizeof(*t)); + t->count = info->descriptorUpdateEntryCount; + t->entries = malloc(t->count * sizeof(*t->entries)); + memcpy(t->entries, info->pDescriptorUpdateEntries, t->count * sizeof(*t->entries)); + for (uint32_t i = 0; i < t->count; i++) { + const VkDescriptorUpdateTemplateEntry *e = &t->entries[i]; + size_t end = e->descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK + ? e->offset + e->descriptorCount + : e->offset + (e->descriptorCount ? (e->descriptorCount - 1) * e->stride : 0) + entry_size(e->descriptorType); + if (end > t->size) + t->size = end; + } + pthread_mutex_lock(&dev->lock); + map_put(&dev->templates, (uint64_t)*tmpl, t); + pthread_mutex_unlock(&dev->lock); + return VK_SUCCESS; +} + +VKAPI_ATTR void VKAPI_CALL DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate tmpl, + const VkAllocationCallbacks *allocator) +{ + struct device *dev = device_from_handle(device); + pthread_mutex_lock(&dev->lock); + struct template_info *t = map_remove(&dev->templates, (uint64_t)tmpl); + pthread_mutex_unlock(&dev->lock); + if (t) { + free(t->entries); + free(t); + } + dev->DestroyDescriptorUpdateTemplate(device, tmpl, allocator); +} + +/* A copy of the template's data with stand-ins for null handles; NULL when nothing changed. */ +static void *fix_template_data(struct device *dev, VkDescriptorUpdateTemplate tmpl, const void *data) +{ + pthread_mutex_lock(&dev->lock); + struct template_info *t = map_get(&dev->templates, (uint64_t)tmpl); + void *copy = NULL; + if (t && data) { + for (uint32_t i = 0; i < t->count; i++) { + const VkDescriptorUpdateTemplateEntry *e = &t->entries[i]; + bool buffer = is_buffer_type(e->descriptorType); + bool image = is_image_type(e->descriptorType); + if (!buffer && !image) + continue; + for (uint32_t j = 0; j < e->descriptorCount; j++) { + size_t at = e->offset + (size_t)j * e->stride; + const char *src = (const char *)(copy ? copy : data) + at; + bool null_handle = buffer ? ((const VkDescriptorBufferInfo *)src)->buffer == VK_NULL_HANDLE + : ((const VkDescriptorImageInfo *)src)->imageView == VK_NULL_HANDLE; + if (!null_handle) + continue; + if (!copy) { + copy = malloc(t->size); + memcpy(copy, data, t->size); + } + if (buffer) + fix_buffer(dev, (VkDescriptorBufferInfo *)((char *)copy + at)); + else + fix_image(dev, (VkDescriptorImageInfo *)((char *)copy + at)); + } + } + } + pthread_mutex_unlock(&dev->lock); + return copy; +} + +VKAPI_ATTR void VKAPI_CALL UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet set, + VkDescriptorUpdateTemplate tmpl, const void *data) +{ + struct device *dev = device_from_handle(device); + void *fixed = fix_template_data(dev, tmpl, data); + dev->UpdateDescriptorSetWithTemplate(device, set, tmpl, fixed ? fixed : data); + free(fixed); +} + +VKAPI_ATTR void VKAPI_CALL CmdPushDescriptorSetWithTemplate(VkCommandBuffer cmd, VkDescriptorUpdateTemplate tmpl, + VkPipelineLayout layout, uint32_t set, const void *data) +{ + struct device *dev = device_from_handle(cmd); + void *fixed = fix_template_data(dev, tmpl, data); + dev->CmdPushDescriptorSetWithTemplate(cmd, tmpl, layout, set, fixed ? fixed : data); + free(fixed); +} + +/* Null vertex buffers read as zeros: bound to the stand-in at offset 0. */ +VKAPI_ATTR void VKAPI_CALL CmdBindVertexBuffers(VkCommandBuffer cmd, uint32_t first, uint32_t count, + const VkBuffer *buffers, const VkDeviceSize *offsets) +{ + struct device *dev = device_from_handle(cmd); + VkBuffer fixed[64]; + VkDeviceSize fixed_offsets[64]; + bool any = false; + for (uint32_t i = 0; i < count && i < 64; i++) any |= buffers[i] == VK_NULL_HANDLE; + if (!any || count > 64) { + dev->CmdBindVertexBuffers(cmd, first, count, buffers, offsets); + return; + } + pthread_mutex_lock(&dev->lock); + bool ready = null_resources(dev); + pthread_mutex_unlock(&dev->lock); + for (uint32_t i = 0; i < count; i++) { + fixed[i] = buffers[i] == VK_NULL_HANDLE && ready ? dev->null_buffer : buffers[i]; + fixed_offsets[i] = buffers[i] == VK_NULL_HANDLE ? 0 : offsets[i]; + } + dev->CmdBindVertexBuffers(cmd, first, count, fixed, fixed_offsets); +} + +VKAPI_ATTR void VKAPI_CALL CmdBindVertexBuffers2(VkCommandBuffer cmd, uint32_t first, uint32_t count, + const VkBuffer *buffers, const VkDeviceSize *offsets, + const VkDeviceSize *sizes, const VkDeviceSize *strides) +{ + struct device *dev = device_from_handle(cmd); + VkBuffer fixed[64]; + VkDeviceSize fixed_offsets[64], fixed_sizes[64]; + bool any = false; + for (uint32_t i = 0; i < count && i < 64; i++) any |= buffers[i] == VK_NULL_HANDLE; + if (!any || count > 64) { + dev->CmdBindVertexBuffers2(cmd, first, count, buffers, offsets, sizes, strides); + return; + } + pthread_mutex_lock(&dev->lock); + bool ready = null_resources(dev); + pthread_mutex_unlock(&dev->lock); + for (uint32_t i = 0; i < count; i++) { + bool null_buffer = buffers[i] == VK_NULL_HANDLE; + fixed[i] = null_buffer && ready ? dev->null_buffer : buffers[i]; + fixed_offsets[i] = null_buffer ? 0 : offsets[i]; + fixed_sizes[i] = sizes ? (null_buffer ? NULL_BUFFER_SIZE : sizes[i]) : 0; + } + dev->CmdBindVertexBuffers2(cmd, first, count, fixed, fixed_offsets, sizes ? fixed_sizes : NULL, strides); +} diff --git a/tools/venus/layer/layer.c b/tools/venus/layer/layer.c index 98e2dc17d..9b2d34e03 100644 --- a/tools/venus/layer/layer.c +++ b/tools/venus/layer/layer.c @@ -190,6 +190,14 @@ uint32_t physical_device_gaps(struct instance *inst, VkPhysicalDevice physical_d gaps |= GAP_BC; if (robustness2 && !robust.robustBufferAccess2) gaps |= GAP_ROBUSTNESS2; + if (!features.features.multiDrawIndirect) + gaps |= GAP_MULTI_DRAW; + if (!has_extension(inst, physical_device, VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME)) + gaps |= GAP_DEPTH_CLIP; + /* No robustness2 at all: the extension is offered with robustBufferAccess2 (as robustBufferAccess) + * and nullDescriptor (stand-ins, draw.c). */ + if (!robustness2) + gaps |= GAP_ROBUSTNESS2 | GAP_NULL_DESCRIPTOR; pthread_mutex_lock(&inst->lock); if (inst->physical_device_count < sizeof(inst->physical_devices) / sizeof(inst->physical_devices[0])) { @@ -200,6 +208,48 @@ uint32_t physical_device_gaps(struct instance *inst, VkPhysicalDevice physical_d return gaps; } +/* Structures of extensions the driver does not have are taken out of a chain for the call down and + * put back after it: the driver (and Venus) must not see them. */ +struct unlinked { + VkBaseOutStructure *prev, *node; +}; + +static uint32_t unlink_types(void *head, const VkStructureType *types, uint32_t type_count, + struct unlinked *out, uint32_t max) +{ + uint32_t n = 0; + VkBaseOutStructure *prev = head; + while (prev && prev->pNext && n < max) { + VkBaseOutStructure *s = prev->pNext; + bool drop = false; + for (uint32_t i = 0; i < type_count; i++) + drop |= s->sType == types[i]; + if (drop) { + out[n++] = (struct unlinked){prev, s}; + prev->pNext = s->pNext; + } else { + prev = s; + } + } + return n; +} + +static void relink(struct unlinked *list, uint32_t n) +{ + while (n--) + list[n].prev->pNext = list[n].node; +} + +static uint32_t foreign_types(uint32_t gaps, VkStructureType *types) +{ + uint32_t n = 0; + if (gaps & GAP_DEPTH_CLIP) + types[n++] = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT; + if (gaps & GAP_NULL_DESCRIPTOR) + types[n++] = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT; + return n; +} + static void report_features(VkPhysicalDeviceFeatures *features, uint32_t gaps) { if (gaps & GAP_FILL_MODE) @@ -212,6 +262,8 @@ static void report_features(VkPhysicalDeviceFeatures *features, uint32_t gaps) features->shaderCullDistance = VK_TRUE; if (gaps & GAP_BC) features->textureCompressionBC = VK_TRUE; + if (gaps & GAP_MULTI_DRAW) + features->multiDrawIndirect = VK_TRUE; } /* Which made-up features the application asked for, and off with them before the driver sees them. */ @@ -238,6 +290,10 @@ static uint32_t take_features(VkPhysicalDeviceFeatures *features, uint32_t gaps) requested |= GAP_BC; features->textureCompressionBC = VK_FALSE; } + if ((gaps & GAP_MULTI_DRAW) && features->multiDrawIndirect) { + requested |= GAP_MULTI_DRAW; + features->multiDrawIndirect = VK_FALSE; + } return requested; } @@ -245,6 +301,8 @@ static void report_limits(VkPhysicalDeviceLimits *limits, uint32_t gaps) { if (gaps & GAP_MULTI_VIEWPORT) limits->maxViewports = 16; + if ((gaps & GAP_MULTI_DRAW) && limits->maxDrawIndirectCount < (1u << 30)) + limits->maxDrawIndirectCount = 1u << 30; if (gaps & GAPS_SHADER) { limits->maxClipDistances = 8; limits->maxCullDistances = 8; @@ -264,12 +322,21 @@ static VKAPI_ATTR void VKAPI_CALL GetPhysicalDeviceFeatures2(VkPhysicalDevice ph VkPhysicalDeviceFeatures2 *features) { struct instance *inst = instance_from_handle(physical_device); - inst->GetPhysicalDeviceFeatures2(physical_device, features); uint32_t gaps = physical_device_gaps(inst, physical_device); + VkStructureType types[4]; + struct unlinked dropped[8]; + uint32_t dropped_count = unlink_types(features, types, foreign_types(gaps, types), dropped, 8); + inst->GetPhysicalDeviceFeatures2(physical_device, features); + relink(dropped, dropped_count); report_features(&features->features, gaps); for (VkBaseOutStructure *s = features->pNext; s; s = s->pNext) { - if (s->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT && (gaps & GAP_ROBUSTNESS2)) + if (s->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT && (gaps & GAP_ROBUSTNESS2)) { ((VkPhysicalDeviceRobustness2FeaturesEXT *)s)->robustBufferAccess2 = VK_TRUE; + if (gaps & GAP_NULL_DESCRIPTOR) + ((VkPhysicalDeviceRobustness2FeaturesEXT *)s)->nullDescriptor = VK_TRUE; + } + if (s->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT && (gaps & GAP_DEPTH_CLIP)) + ((VkPhysicalDeviceDepthClipEnableFeaturesEXT *)s)->depthClipEnable = VK_TRUE; } } @@ -289,6 +356,40 @@ static VKAPI_ATTR void VKAPI_CALL GetPhysicalDeviceProperties2(VkPhysicalDevice report_limits(&properties->properties.limits, physical_device_gaps(inst, physical_device)); } +/* The extensions the layer stands in for, added to the driver's list. */ +static VKAPI_ATTR VkResult VKAPI_CALL EnumerateDeviceExtensionProperties(VkPhysicalDevice physical_device, + const char *layer_name, uint32_t *count, + VkExtensionProperties *properties) +{ + struct instance *inst = instance_from_handle(physical_device); + if (layer_name) + return inst->EnumerateDeviceExtensionProperties(physical_device, layer_name, count, properties); + uint32_t gaps = physical_device_gaps(inst, physical_device); + VkExtensionProperties extra[2]; + uint32_t extra_count = 0; + if (gaps & GAP_DEPTH_CLIP) + extra[extra_count++] = (VkExtensionProperties){VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME, 1}; + if (gaps & GAP_NULL_DESCRIPTOR) + extra[extra_count++] = (VkExtensionProperties){VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, 1}; + uint32_t own = 0; + VkResult result = inst->EnumerateDeviceExtensionProperties(physical_device, NULL, &own, NULL); + if (result != VK_SUCCESS) + return result; + if (!properties) { + *count = own + extra_count; + return VK_SUCCESS; + } + VkExtensionProperties *all = calloc(own + extra_count, sizeof(*all)); + inst->EnumerateDeviceExtensionProperties(physical_device, NULL, &own, all); + memcpy(all + own, extra, extra_count * sizeof(*extra)); + uint32_t total = own + extra_count; + uint32_t n = *count < total ? *count : total; + memcpy(properties, all, n * sizeof(*all)); + free(all); + *count = n; + return n < total ? VK_INCOMPLETE : VK_SUCCESS; +} + static VKAPI_ATTR VkResult VKAPI_CALL CreateInstance(const VkInstanceCreateInfo *info, const VkAllocationCallbacks *allocator, VkInstance *instance) { @@ -410,8 +511,35 @@ static VKAPI_ATTR VkResult VKAPI_CALL CreateDevice(VkPhysicalDevice physical_dev enabled->robustBufferAccess = VK_TRUE; } + /* The extensions the layer stands in for leave the list, and their structures the chain. */ + const char **names = NULL; + if (gaps & (GAP_DEPTH_CLIP | GAP_NULL_DESCRIPTOR)) { + names = malloc((info->enabledExtensionCount ? info->enabledExtensionCount : 1) * sizeof(*names)); + copy.enabledExtensionCount = 0; + for (uint32_t i = 0; i < info->enabledExtensionCount; i++) { + const char *name = info->ppEnabledExtensionNames[i]; + if ((gaps & GAP_DEPTH_CLIP) && !strcmp(name, VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME)) { + requested |= GAP_DEPTH_CLIP; + continue; + } + if ((gaps & GAP_NULL_DESCRIPTOR) && !strcmp(name, VK_EXT_ROBUSTNESS_2_EXTENSION_NAME)) { + requested |= GAP_NULL_DESCRIPTOR; + if (enabled) + enabled->robustBufferAccess = VK_TRUE; + continue; + } + names[copy.enabledExtensionCount++] = name; + } + copy.ppEnabledExtensionNames = names; + } + VkStructureType foreign[4]; + struct unlinked dropped[8]; + uint32_t dropped_count = unlink_types(©, foreign, foreign_types(gaps, foreign), dropped, 8); + VkResult result = create(physical_device, ©, allocator, device); + relink(dropped, dropped_count); + free(names); if (features2 && !info->pEnabledFeatures) features2->features = saved_features; if (robust) @@ -447,12 +575,15 @@ static VKAPI_ATTR VkResult VKAPI_CALL CreateDevice(VkPhysicalDevice physical_dev pthread_mutex_unlock(&global_lock); if (requested) { - layer_log("emulating%s%s%s%s%s%s for %s", (requested & GAP_FILL_MODE) ? " fillModeNonSolid" : "", + layer_log("emulating%s%s%s%s%s%s%s%s%s for %s", (requested & GAP_FILL_MODE) ? " fillModeNonSolid" : "", (requested & GAP_MULTI_VIEWPORT) ? " multiViewport" : "", (requested & GAP_CLIP_DISTANCE) ? " shaderClipDistance" : "", (requested & GAP_CULL_DISTANCE) ? " shaderCullDistance" : "", (requested & GAP_ROBUSTNESS2) ? " robustBufferAccess2" : "", - (requested & GAP_BC) ? " textureCompressionBC" : "", properties.deviceName); + (requested & GAP_BC) ? " textureCompressionBC" : "", + (requested & GAP_MULTI_DRAW) ? " multiDrawIndirect" : "", + (requested & GAP_DEPTH_CLIP) ? " depthClipEnable" : "", + (requested & GAP_NULL_DESCRIPTOR) ? " nullDescriptor" : "", properties.deviceName); } return VK_SUCCESS; } @@ -471,6 +602,7 @@ static VKAPI_ATTR void VKAPI_CALL DestroyDevice(VkDevice device, const VkAllocat if (!dev) return; bc_destroy_device(dev); + null_resources_destroy(dev); dev->DestroyDevice(device, allocator); for (uint32_t i = 0; i < dev->shader_modules.capacity; i++) { struct spirv_code *code = dev->shader_modules.values[i]; @@ -538,6 +670,22 @@ static const struct hook device_hooks[] = { HOOK(ResetCommandBuffer, GAP_BC), HOOK(ResetCommandPool, GAP_BC), HOOK(DestroyCommandPool, GAP_BC), + HOOK(CmdDrawIndirect, GAP_MULTI_DRAW), + HOOK(CmdDrawIndexedIndirect, GAP_MULTI_DRAW), + HOOK(UpdateDescriptorSets, GAP_NULL_DESCRIPTOR), + HOOK(CmdPushDescriptorSet, GAP_NULL_DESCRIPTOR), + HOOK_ALIAS(CmdPushDescriptorSet, CmdPushDescriptorSetKHR, GAP_NULL_DESCRIPTOR), + HOOK(CreateDescriptorUpdateTemplate, GAP_NULL_DESCRIPTOR), + HOOK_ALIAS(CreateDescriptorUpdateTemplate, CreateDescriptorUpdateTemplateKHR, GAP_NULL_DESCRIPTOR), + HOOK(DestroyDescriptorUpdateTemplate, GAP_NULL_DESCRIPTOR), + HOOK_ALIAS(DestroyDescriptorUpdateTemplate, DestroyDescriptorUpdateTemplateKHR, GAP_NULL_DESCRIPTOR), + HOOK(UpdateDescriptorSetWithTemplate, GAP_NULL_DESCRIPTOR), + HOOK_ALIAS(UpdateDescriptorSetWithTemplate, UpdateDescriptorSetWithTemplateKHR, GAP_NULL_DESCRIPTOR), + HOOK(CmdPushDescriptorSetWithTemplate, GAP_NULL_DESCRIPTOR), + HOOK_ALIAS(CmdPushDescriptorSetWithTemplate, CmdPushDescriptorSetWithTemplateKHR, GAP_NULL_DESCRIPTOR), + HOOK(CmdBindVertexBuffers, GAP_NULL_DESCRIPTOR), + HOOK(CmdBindVertexBuffers2, GAP_NULL_DESCRIPTOR), + HOOK_ALIAS(CmdBindVertexBuffers2, CmdBindVertexBuffers2EXT, GAP_NULL_DESCRIPTOR), }; static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetDeviceProcAddr(VkDevice device, const char *name); @@ -562,6 +710,7 @@ static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetDeviceProcAddr(VkDevice devic } static const struct hook physical_device_hooks[] = { + HOOK(EnumerateDeviceExtensionProperties, 0), HOOK(GetPhysicalDeviceFeatures, 0), HOOK(GetPhysicalDeviceFeatures2, 0), HOOK_ALIAS(GetPhysicalDeviceFeatures2, GetPhysicalDeviceFeatures2KHR, 0), diff --git a/tools/venus/layer/mali_compat.h b/tools/venus/layer/mali_compat.h index 4ab5e12ca..ff4ed53e6 100644 --- a/tools/venus/layer/mali_compat.h +++ b/tools/venus/layer/mali_compat.h @@ -25,10 +25,14 @@ enum gap { GAP_CULL_DISTANCE = 1u << 3, GAP_ROBUSTNESS2 = 1u << 4, GAP_BC = 1u << 5, + /* draw.c and pipeline.c (added for Mali-G57 and drivers without these, on top of David Cimo's six) */ + GAP_MULTI_DRAW = 1u << 6, /* multiDrawIndirect: indirect draws looped one by one */ + GAP_DEPTH_CLIP = 1u << 7, /* VK_EXT_depth_clip_enable: depthClipEnable off becomes depth clamp */ + GAP_NULL_DESCRIPTOR = 1u << 8, /* VK_EXT_robustness2 missing entirely: nullDescriptor stand-ins */ }; #define GAPS_SHADER (GAP_CLIP_DISTANCE | GAP_CULL_DISTANCE) -#define GAPS_PIPELINE (GAP_FILL_MODE | GAP_MULTI_VIEWPORT | GAPS_SHADER) +#define GAPS_PIPELINE (GAP_FILL_MODE | GAP_MULTI_VIEWPORT | GAPS_SHADER | GAP_DEPTH_CLIP) void layer_log(const char *format, ...) __attribute__((format(printf, 1, 2))); @@ -109,7 +113,20 @@ struct instance { X(BeginCommandBuffer) \ X(ResetCommandBuffer) \ X(ResetCommandPool) \ - X(DestroyCommandPool) + X(DestroyCommandPool) \ + X(CmdDrawIndirect) \ + X(CmdDrawIndexedIndirect) \ + X(GetImageMemoryRequirements) \ + X(BindImageMemory) \ + X(DestroyImageView) \ + X(UpdateDescriptorSets) \ + X(CmdPushDescriptorSet) \ + X(CreateDescriptorUpdateTemplate) \ + X(DestroyDescriptorUpdateTemplate) \ + X(UpdateDescriptorSetWithTemplate) \ + X(CmdPushDescriptorSetWithTemplate) \ + X(CmdBindVertexBuffers) \ + X(CmdBindVertexBuffers2) struct staging_block; @@ -132,6 +149,14 @@ struct device { struct handle_map memories; /* VkDeviceMemory -> struct mapped_memory */ struct handle_map command_buffers; /* VkCommandBuffer -> struct command_buffer */ struct staging_block *free_staging; + struct handle_map templates; /* VkDescriptorUpdateTemplate -> struct template_info (draw.c) */ + /* nullDescriptor stand-ins (draw.c): 0 not made yet, 1 ready, -1 failed. */ + int null_ready; + VkBuffer null_buffer; + VkDeviceMemory null_buffer_memory; + VkImage null_image; + VkDeviceMemory null_image_memory; + VkImageView null_view; struct device *next; }; @@ -243,4 +268,17 @@ struct instance *instance_from_handle(const void *dispatchable); /* What the GPU lacks of the features the layer can make up for. */ uint32_t physical_device_gaps(struct instance *inst, VkPhysicalDevice physical_device); + +/* draw.c */ +VKAPI_ATTR void VKAPI_CALL CmdDrawIndirect(VkCommandBuffer cmd, VkBuffer buffer, VkDeviceSize offset, uint32_t draw_count, uint32_t stride); +VKAPI_ATTR void VKAPI_CALL CmdDrawIndexedIndirect(VkCommandBuffer cmd, VkBuffer buffer, VkDeviceSize offset, uint32_t draw_count, uint32_t stride); +VKAPI_ATTR void VKAPI_CALL UpdateDescriptorSets(VkDevice device, uint32_t write_count, const VkWriteDescriptorSet *writes, uint32_t copy_count, const VkCopyDescriptorSet *copies); +VKAPI_ATTR void VKAPI_CALL CmdPushDescriptorSet(VkCommandBuffer cmd, VkPipelineBindPoint bind_point, VkPipelineLayout layout, uint32_t set, uint32_t write_count, const VkWriteDescriptorSet *writes); +VKAPI_ATTR VkResult VKAPI_CALL CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo *info, const VkAllocationCallbacks *allocator, VkDescriptorUpdateTemplate *tmpl); +VKAPI_ATTR void VKAPI_CALL DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate tmpl, const VkAllocationCallbacks *allocator); +VKAPI_ATTR void VKAPI_CALL UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet set, VkDescriptorUpdateTemplate tmpl, const void *data); +VKAPI_ATTR void VKAPI_CALL CmdPushDescriptorSetWithTemplate(VkCommandBuffer cmd, VkDescriptorUpdateTemplate tmpl, VkPipelineLayout layout, uint32_t set, const void *data); +VKAPI_ATTR void VKAPI_CALL CmdBindVertexBuffers(VkCommandBuffer cmd, uint32_t first, uint32_t count, const VkBuffer *buffers, const VkDeviceSize *offsets); +VKAPI_ATTR void VKAPI_CALL CmdBindVertexBuffers2(VkCommandBuffer cmd, uint32_t first, uint32_t count, const VkBuffer *buffers, const VkDeviceSize *offsets, const VkDeviceSize *sizes, const VkDeviceSize *strides); +void null_resources_destroy(struct device *dev); #endif diff --git a/tools/venus/layer/pipeline.c b/tools/venus/layer/pipeline.c index 3940209a5..a749c21d3 100644 --- a/tools/venus/layer/pipeline.c +++ b/tools/venus/layer/pipeline.c @@ -87,6 +87,8 @@ static void find_stage_code(struct device *dev, const VkPipelineShaderStageCreat struct pipeline_rewrite { VkPipelineRasterizationStateCreateInfo rasterization; + /* VK_EXT_depth_clip_enable's state, taken out of the application's chain for the call. */ + VkBaseOutStructure *clip_prev, *clip_node; VkPipelineViewportStateCreateInfo viewport; VkPipelineShaderStageCreateInfo stages[MAX_STAGES]; VkShaderModuleCreateInfo modules[MAX_STAGES]; @@ -207,6 +209,29 @@ static void rewrite_pipeline(struct device *dev, VkGraphicsPipelineCreateInfo *i rw->rasterization.polygonMode = VK_POLYGON_MODE_FILL; info->pRasterizationState = &rw->rasterization; } + /* No VK_EXT_depth_clip_enable: depth clipping off is the nearest the driver has as depth clamp. */ + if ((dev->emulated & GAP_DEPTH_CLIP) && info->pRasterizationState) { + VkBaseOutStructure *prev = (VkBaseOutStructure *)info->pRasterizationState; + while (prev->pNext && prev->pNext->sType != VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT) + prev = prev->pNext; + VkBaseOutStructure *node = prev->pNext; + if (node) { + bool clip = ((VkPipelineRasterizationDepthClipStateCreateInfoEXT *)node)->depthClipEnable; + if (info->pRasterizationState != &rw->rasterization) { + rw->rasterization = *info->pRasterizationState; + if (prev == (VkBaseOutStructure *)info->pRasterizationState) + prev = (VkBaseOutStructure *)&rw->rasterization; + info->pRasterizationState = &rw->rasterization; + } + if (!clip) + rw->rasterization.depthClampEnable = VK_TRUE; + if (prev != (VkBaseOutStructure *)&rw->rasterization) { + rw->clip_prev = prev; + rw->clip_node = node; + } + prev->pNext = node->pNext; + } + } if ((dev->emulated & GAP_MULTI_VIEWPORT) && info->pViewportState && (info->pViewportState->viewportCount > 1 || info->pViewportState->scissorCount > 1)) { rw->viewport = *info->pViewportState; @@ -222,6 +247,8 @@ static void rewrite_pipeline(struct device *dev, VkGraphicsPipelineCreateInfo *i static void undo_rewrite(struct device *dev, struct pipeline_rewrite *rw) { + if (rw->clip_prev) + rw->clip_prev->pNext = rw->clip_node; for (uint32_t i = 0; i < MAX_STAGES; i++) { if (rw->in_place[i]) { rw->in_place[i]->pCode = rw->in_place_code[i];