summaryrefslogtreecommitdiffstats
path: root/spa/plugins/vulkan/vulkan-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'spa/plugins/vulkan/vulkan-utils.c')
-rw-r--r--spa/plugins/vulkan/vulkan-utils.c758
1 files changed, 758 insertions, 0 deletions
diff --git a/spa/plugins/vulkan/vulkan-utils.c b/spa/plugins/vulkan/vulkan-utils.c
new file mode 100644
index 0000000..ae3337b
--- /dev/null
+++ b/spa/plugins/vulkan/vulkan-utils.c
@@ -0,0 +1,758 @@
+#include <vulkan/vulkan.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+#if !defined(__FreeBSD__) && !defined(__MidnightBSD__)
+#include <alloca.h>
+#endif
+#include <errno.h>
+#include <stdio.h>
+#include <assert.h>
+#include <math.h>
+#include <time.h>
+
+#include <spa/utils/result.h>
+#include <spa/utils/string.h>
+#include <spa/support/log.h>
+#include <spa/debug/mem.h>
+
+#include "vulkan-utils.h"
+
+//#define ENABLE_VALIDATION
+
+#define VULKAN_INSTANCE_FUNCTION(name) \
+ PFN_##name name = (PFN_##name)vkGetInstanceProcAddr(s->instance, #name)
+
+static int vkresult_to_errno(VkResult result)
+{
+ switch (result) {
+ case VK_SUCCESS:
+ case VK_EVENT_SET:
+ case VK_EVENT_RESET:
+ return 0;
+ case VK_NOT_READY:
+ case VK_INCOMPLETE:
+ case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
+ return EBUSY;
+ case VK_TIMEOUT:
+ return ETIMEDOUT;
+ case VK_ERROR_OUT_OF_HOST_MEMORY:
+ case VK_ERROR_OUT_OF_DEVICE_MEMORY:
+ case VK_ERROR_MEMORY_MAP_FAILED:
+ case VK_ERROR_OUT_OF_POOL_MEMORY:
+ case VK_ERROR_FRAGMENTED_POOL:
+#ifdef VK_ERROR_FRAGMENTATION_EXT
+ case VK_ERROR_FRAGMENTATION_EXT:
+#endif
+ return ENOMEM;
+ case VK_ERROR_INITIALIZATION_FAILED:
+ return EIO;
+ case VK_ERROR_DEVICE_LOST:
+ case VK_ERROR_SURFACE_LOST_KHR:
+#ifdef VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT
+ case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
+#endif
+ return ENODEV;
+ case VK_ERROR_LAYER_NOT_PRESENT:
+ case VK_ERROR_EXTENSION_NOT_PRESENT:
+ case VK_ERROR_FEATURE_NOT_PRESENT:
+ return ENOENT;
+ case VK_ERROR_INCOMPATIBLE_DRIVER:
+ case VK_ERROR_FORMAT_NOT_SUPPORTED:
+ case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
+ return ENOTSUP;
+ case VK_ERROR_TOO_MANY_OBJECTS:
+ return ENFILE;
+ case VK_SUBOPTIMAL_KHR:
+ case VK_ERROR_OUT_OF_DATE_KHR:
+ return EIO;
+ case VK_ERROR_INVALID_EXTERNAL_HANDLE:
+ case VK_ERROR_INVALID_SHADER_NV:
+#ifdef VK_ERROR_VALIDATION_FAILED_EXT
+ case VK_ERROR_VALIDATION_FAILED_EXT:
+#endif
+#ifdef VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT
+ case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
+#endif
+#ifdef VK_ERROR_INVALID_DEVICE_ADDRESS_EXT
+ case VK_ERROR_INVALID_DEVICE_ADDRESS_EXT:
+#endif
+ return EINVAL;
+#ifdef VK_ERROR_NOT_PERMITTED_EXT
+ case VK_ERROR_NOT_PERMITTED_EXT:
+ return EPERM;
+#endif
+ default:
+ return EIO;
+ }
+}
+
+#define VK_CHECK_RESULT(f) \
+{ \
+ VkResult _result = (f); \
+ int _r = -vkresult_to_errno(_result); \
+ if (_result != VK_SUCCESS) { \
+ spa_log_error(s->log, "error: %d (%d %s)", _result, _r, spa_strerror(_r)); \
+ return _r; \
+ } \
+}
+#define CHECK(f) \
+{ \
+ int _res = (f); \
+ if (_res < 0) \
+ return _res; \
+}
+
+static int createInstance(struct vulkan_state *s)
+{
+ static const VkApplicationInfo applicationInfo = {
+ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
+ .pApplicationName = "PipeWire",
+ .applicationVersion = 0,
+ .pEngineName = "PipeWire Vulkan Engine",
+ .engineVersion = 0,
+ .apiVersion = VK_API_VERSION_1_1
+ };
+ static const char * const extensions[] = {
+ VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME
+ };
+ static const char * const checkLayers[] = {
+#ifdef ENABLE_VALIDATION
+ "VK_LAYER_KHRONOS_validation",
+#endif
+ NULL
+ };
+ uint32_t i, j, layerCount, n_layers = 0;
+ const char *layers[1];
+ vkEnumerateInstanceLayerProperties(&layerCount, NULL);
+
+ VkLayerProperties availableLayers[layerCount];
+ vkEnumerateInstanceLayerProperties(&layerCount, availableLayers);
+
+ for (i = 0; i < layerCount; i++) {
+ for (j = 0; j < SPA_N_ELEMENTS(checkLayers); j++) {
+ if (spa_streq(availableLayers[i].layerName, checkLayers[j]))
+ layers[n_layers++] = checkLayers[j];
+ }
+ }
+
+ const VkInstanceCreateInfo createInfo = {
+ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+ .pApplicationInfo = &applicationInfo,
+ .enabledExtensionCount = 1,
+ .ppEnabledExtensionNames = extensions,
+ .enabledLayerCount = n_layers,
+ .ppEnabledLayerNames = layers,
+ };
+
+ VK_CHECK_RESULT(vkCreateInstance(&createInfo, NULL, &s->instance));
+
+ return 0;
+}
+
+static uint32_t getComputeQueueFamilyIndex(struct vulkan_state *s)
+{
+ uint32_t i, queueFamilyCount;
+ VkQueueFamilyProperties *queueFamilies;
+
+ vkGetPhysicalDeviceQueueFamilyProperties(s->physicalDevice, &queueFamilyCount, NULL);
+
+ queueFamilies = alloca(queueFamilyCount * sizeof(VkQueueFamilyProperties));
+ vkGetPhysicalDeviceQueueFamilyProperties(s->physicalDevice, &queueFamilyCount, queueFamilies);
+
+ for (i = 0; i < queueFamilyCount; i++) {
+ VkQueueFamilyProperties props = queueFamilies[i];
+
+ if (props.queueCount > 0 && (props.queueFlags & VK_QUEUE_COMPUTE_BIT))
+ break;
+ }
+ if (i == queueFamilyCount)
+ return -ENODEV;
+
+ return i;
+}
+
+static int findPhysicalDevice(struct vulkan_state *s)
+{
+ uint32_t deviceCount;
+ VkPhysicalDevice *devices;
+
+ vkEnumeratePhysicalDevices(s->instance, &deviceCount, NULL);
+ if (deviceCount == 0)
+ return -ENODEV;
+
+ devices = alloca(deviceCount * sizeof(VkPhysicalDevice));
+ vkEnumeratePhysicalDevices(s->instance, &deviceCount, devices);
+
+ s->physicalDevice = devices[0];
+
+ s->queueFamilyIndex = getComputeQueueFamilyIndex(s);
+
+ return 0;
+}
+
+static int createDevice(struct vulkan_state *s)
+{
+
+ const VkDeviceQueueCreateInfo queueCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+ .queueFamilyIndex = s->queueFamilyIndex,
+ .queueCount = 1,
+ .pQueuePriorities = (const float[]) { 1.0f }
+ };
+ static const char * const extensions[] = {
+ VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
+ VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME
+ };
+ const VkDeviceCreateInfo deviceCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+ .queueCreateInfoCount = 1,
+ .pQueueCreateInfos = &queueCreateInfo,
+ .enabledExtensionCount = 2,
+ .ppEnabledExtensionNames = extensions,
+ };
+
+ VK_CHECK_RESULT(vkCreateDevice(s->physicalDevice, &deviceCreateInfo, NULL, &s->device));
+
+ vkGetDeviceQueue(s->device, s->queueFamilyIndex, 0, &s->queue);
+
+ static const VkFenceCreateInfo fenceCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+ .flags = 0,
+ };
+ VK_CHECK_RESULT(vkCreateFence(s->device, &fenceCreateInfo, NULL, &s->fence));
+
+ return 0;
+}
+
+static uint32_t findMemoryType(struct vulkan_state *s,
+ uint32_t memoryTypeBits, VkMemoryPropertyFlags properties)
+{
+ uint32_t i;
+ VkPhysicalDeviceMemoryProperties memoryProperties;
+
+ vkGetPhysicalDeviceMemoryProperties(s->physicalDevice, &memoryProperties);
+
+ for (i = 0; i < memoryProperties.memoryTypeCount; i++) {
+ if ((memoryTypeBits & (1 << i)) &&
+ ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties))
+ return i;
+ }
+ return -1;
+}
+
+static int createDescriptors(struct vulkan_state *s)
+{
+ uint32_t i;
+
+ VkDescriptorPoolSize descriptorPoolSizes[2] = {
+ {
+ .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+ .descriptorCount = 1,
+ },
+ {
+ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .descriptorCount = s->n_streams - 1,
+ },
+ };
+ const VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+ .maxSets = s->n_streams,
+ .poolSizeCount = s->n_streams > 1 ? 2 : 1,
+ .pPoolSizes = descriptorPoolSizes,
+ };
+
+ VK_CHECK_RESULT(vkCreateDescriptorPool(s->device,
+ &descriptorPoolCreateInfo, NULL,
+ &s->descriptorPool));
+
+ VkDescriptorSetLayoutBinding descriptorSetLayoutBinding[s->n_streams];
+ descriptorSetLayoutBinding[0] = (VkDescriptorSetLayoutBinding) {
+ .binding = 0,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT
+ };
+ for (i = 1; i < s->n_streams; i++) {
+ descriptorSetLayoutBinding[i] = (VkDescriptorSetLayoutBinding) {
+ .binding = i,
+ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT
+ };
+ };
+ const VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
+ .bindingCount = s->n_streams,
+ .pBindings = descriptorSetLayoutBinding
+ };
+ VK_CHECK_RESULT(vkCreateDescriptorSetLayout(s->device,
+ &descriptorSetLayoutCreateInfo, NULL,
+ &s->descriptorSetLayout));
+
+ const VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+ .descriptorPool = s->descriptorPool,
+ .descriptorSetCount = 1,
+ .pSetLayouts = &s->descriptorSetLayout
+ };
+
+ VK_CHECK_RESULT(vkAllocateDescriptorSets(s->device,
+ &descriptorSetAllocateInfo,
+ &s->descriptorSet));
+
+ const VkSamplerCreateInfo samplerInfo = {
+ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+ .magFilter = VK_FILTER_LINEAR,
+ .minFilter = VK_FILTER_LINEAR,
+ .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
+ .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
+ .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
+ .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
+ .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
+ .unnormalizedCoordinates = VK_FALSE,
+ .compareEnable = VK_FALSE,
+ .compareOp = VK_COMPARE_OP_ALWAYS,
+ .mipLodBias = 0.0f,
+ .minLod = 0,
+ .maxLod = 5,
+ };
+ VK_CHECK_RESULT(vkCreateSampler(s->device, &samplerInfo, NULL, &s->sampler));
+
+ return 0;
+}
+
+static int updateDescriptors(struct vulkan_state *s)
+{
+ uint32_t i;
+ VkDescriptorImageInfo descriptorImageInfo[s->n_streams];
+ VkWriteDescriptorSet writeDescriptorSet[s->n_streams];
+
+ for (i = 0; i < s->n_streams; i++) {
+ struct vulkan_stream *p = &s->streams[i];
+
+ if (p->current_buffer_id == p->pending_buffer_id ||
+ p->pending_buffer_id == SPA_ID_INVALID)
+ continue;
+
+ p->current_buffer_id = p->pending_buffer_id;
+ p->busy_buffer_id = p->current_buffer_id;
+ p->pending_buffer_id = SPA_ID_INVALID;
+
+ descriptorImageInfo[i] = (VkDescriptorImageInfo) {
+ .sampler = s->sampler,
+ .imageView = p->buffers[p->current_buffer_id].view,
+ .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
+ };
+ writeDescriptorSet[i] = (VkWriteDescriptorSet) {
+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ .dstSet = s->descriptorSet,
+ .dstBinding = i,
+ .descriptorCount = 1,
+ .descriptorType = i == 0 ?
+ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE :
+ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .pImageInfo = &descriptorImageInfo[i],
+ };
+ }
+ vkUpdateDescriptorSets(s->device, s->n_streams,
+ writeDescriptorSet, 0, NULL);
+
+ return 0;
+}
+
+static VkShaderModule createShaderModule(struct vulkan_state *s, const char* shaderFile)
+{
+ VkShaderModule shaderModule = VK_NULL_HANDLE;
+ VkResult result;
+ void *data;
+ int fd;
+ struct stat stat;
+
+ if ((fd = open(shaderFile, 0, O_RDONLY)) == -1) {
+ spa_log_error(s->log, "can't open %s: %m", shaderFile);
+ return VK_NULL_HANDLE;
+ }
+ if (fstat(fd, &stat) < 0) {
+ spa_log_error(s->log, "can't stat %s: %m", shaderFile);
+ close(fd);
+ return VK_NULL_HANDLE;
+ }
+
+ data = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+
+ const VkShaderModuleCreateInfo shaderModuleCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
+ .codeSize = stat.st_size,
+ .pCode = data,
+ };
+ result = vkCreateShaderModule(s->device,
+ &shaderModuleCreateInfo, 0, &shaderModule);
+
+ munmap(data, stat.st_size);
+ close(fd);
+
+ if (result != VK_SUCCESS) {
+ spa_log_error(s->log, "can't create shader %s: %m", shaderFile);
+ return VK_NULL_HANDLE;
+ }
+ return shaderModule;
+}
+
+static int createComputePipeline(struct vulkan_state *s, const char *shader_file)
+{
+ static const VkPushConstantRange range = {
+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
+ .offset = 0,
+ .size = sizeof(struct push_constants)
+ };
+
+ const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+ .setLayoutCount = 1,
+ .pSetLayouts = &s->descriptorSetLayout,
+ .pushConstantRangeCount = 1,
+ .pPushConstantRanges = &range,
+ };
+ VK_CHECK_RESULT(vkCreatePipelineLayout(s->device,
+ &pipelineLayoutCreateInfo, NULL,
+ &s->pipelineLayout));
+
+ s->computeShaderModule = createShaderModule(s, shader_file);
+ if (s->computeShaderModule == VK_NULL_HANDLE)
+ return -ENOENT;
+
+ const VkPipelineShaderStageCreateInfo shaderStageCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ .stage = VK_SHADER_STAGE_COMPUTE_BIT,
+ .module = s->computeShaderModule,
+ .pName = "main",
+ };
+ const VkComputePipelineCreateInfo pipelineCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
+ .stage = shaderStageCreateInfo,
+ .layout = s->pipelineLayout,
+ };
+ VK_CHECK_RESULT(vkCreateComputePipelines(s->device, VK_NULL_HANDLE,
+ 1, &pipelineCreateInfo, NULL,
+ &s->pipeline));
+ return 0;
+}
+
+static int createCommandBuffer(struct vulkan_state *s)
+{
+ const VkCommandPoolCreateInfo commandPoolCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+ .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
+ .queueFamilyIndex = s->queueFamilyIndex,
+ };
+ VK_CHECK_RESULT(vkCreateCommandPool(s->device,
+ &commandPoolCreateInfo, NULL,
+ &s->commandPool));
+
+ const VkCommandBufferAllocateInfo commandBufferAllocateInfo = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+ .commandPool = s->commandPool,
+ .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ .commandBufferCount = 1,
+ };
+ VK_CHECK_RESULT(vkAllocateCommandBuffers(s->device,
+ &commandBufferAllocateInfo,
+ &s->commandBuffer));
+
+ return 0;
+}
+
+static int runCommandBuffer(struct vulkan_state *s)
+{
+ static const VkCommandBufferBeginInfo beginInfo = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
+ };
+ VK_CHECK_RESULT(vkBeginCommandBuffer(s->commandBuffer, &beginInfo));
+
+ VkImageMemoryBarrier barrier[s->n_streams];
+ uint32_t i;
+
+ for (i = 0; i < s->n_streams; i++) {
+ struct vulkan_stream *p = &s->streams[i];
+
+ barrier[i]= (VkImageMemoryBarrier) {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .subresourceRange.levelCount = 1,
+ .subresourceRange.layerCount = 1,
+ .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .newLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .srcAccessMask = 0,
+ .dstAccessMask = 0,
+ .image = p->buffers[p->current_buffer_id].image,
+ };
+ }
+
+ vkCmdPipelineBarrier(s->commandBuffer,
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
+ 0, 0, NULL, 0, NULL,
+ s->n_streams, barrier);
+
+ vkCmdBindPipeline(s->commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, s->pipeline);
+ vkCmdPushConstants (s->commandBuffer,
+ s->pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT,
+ 0, sizeof(struct push_constants), (const void *) &s->constants);
+ vkCmdBindDescriptorSets(s->commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE,
+ s->pipelineLayout, 0, 1, &s->descriptorSet, 0, NULL);
+
+ vkCmdDispatch(s->commandBuffer,
+ (uint32_t)ceil(s->constants.width / (float)WORKGROUP_SIZE),
+ (uint32_t)ceil(s->constants.height / (float)WORKGROUP_SIZE), 1);
+
+ VK_CHECK_RESULT(vkEndCommandBuffer(s->commandBuffer));
+
+ VK_CHECK_RESULT(vkResetFences(s->device, 1, &s->fence));
+
+ const VkSubmitInfo submitInfo = {
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .commandBufferCount = 1,
+ .pCommandBuffers = &s->commandBuffer,
+ };
+ VK_CHECK_RESULT(vkQueueSubmit(s->queue, 1, &submitInfo, s->fence));
+ s->started = true;
+
+ return 0;
+}
+
+static void clear_buffers(struct vulkan_state *s, struct vulkan_stream *p)
+{
+ uint32_t i;
+
+ for (i = 0; i < p->n_buffers; i++) {
+ if (p->buffers[i].fd != -1)
+ close(p->buffers[i].fd);
+ vkFreeMemory(s->device, p->buffers[i].memory, NULL);
+ vkDestroyImage(s->device, p->buffers[i].image, NULL);
+ vkDestroyImageView(s->device, p->buffers[i].view, NULL);
+ }
+ p->n_buffers = 0;
+}
+
+static void clear_streams(struct vulkan_state *s)
+{
+ uint32_t i;
+ for (i = 0; i < s->n_streams; i++) {
+ struct vulkan_stream *p = &s->streams[i];
+ clear_buffers(s, p);
+ }
+}
+
+int spa_vulkan_use_buffers(struct vulkan_state *s, struct vulkan_stream *p, uint32_t flags,
+ uint32_t n_buffers, struct spa_buffer **buffers)
+{
+ uint32_t i;
+ VULKAN_INSTANCE_FUNCTION(vkGetMemoryFdKHR);
+
+ clear_buffers(s, p);
+
+ for (i = 0; i < n_buffers; i++) {
+ VkExternalMemoryImageCreateInfo extInfo;
+ VkImageCreateInfo imageCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ .imageType = VK_IMAGE_TYPE_2D,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .extent.width = s->constants.width,
+ .extent.height = s->constants.height,
+ .extent.depth = 1,
+ .mipLevels = 1,
+ .arrayLayers = 1,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .tiling = VK_IMAGE_TILING_LINEAR,
+ .usage = p->direction == SPA_DIRECTION_OUTPUT ?
+ VK_IMAGE_USAGE_STORAGE_BIT:
+ VK_IMAGE_USAGE_SAMPLED_BIT,
+ .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ };
+
+ if (!(flags & SPA_NODE_BUFFERS_FLAG_ALLOC)) {
+ extInfo = (VkExternalMemoryImageCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
+ .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
+ };
+ imageCreateInfo.pNext = &extInfo;
+ }
+
+ VK_CHECK_RESULT(vkCreateImage(s->device,
+ &imageCreateInfo, NULL, &p->buffers[i].image));
+
+ VkMemoryRequirements memoryRequirements;
+ vkGetImageMemoryRequirements(s->device,
+ p->buffers[i].image, &memoryRequirements);
+
+ VkMemoryAllocateInfo allocateInfo = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ .allocationSize = memoryRequirements.size,
+ .memoryTypeIndex = findMemoryType(s,
+ memoryRequirements.memoryTypeBits,
+ VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT),
+ };
+
+ if (flags & SPA_NODE_BUFFERS_FLAG_ALLOC) {
+ VK_CHECK_RESULT(vkAllocateMemory(s->device,
+ &allocateInfo, NULL, &p->buffers[i].memory));
+
+ const VkMemoryGetFdInfoKHR getFdInfo = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
+ .memory = p->buffers[i].memory,
+ .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT
+ };
+ int fd;
+
+ VK_CHECK_RESULT(vkGetMemoryFdKHR(s->device, &getFdInfo, &fd));
+
+ spa_log_info(s->log, "export DMABUF %zd", memoryRequirements.size);
+
+// buffers[i]->datas[0].type = SPA_DATA_DmaBuf;
+ buffers[i]->datas[0].type = SPA_DATA_MemFd;
+ buffers[i]->datas[0].fd = fd;
+ buffers[i]->datas[0].flags = SPA_DATA_FLAG_READABLE;
+ buffers[i]->datas[0].mapoffset = 0;
+ buffers[i]->datas[0].maxsize = memoryRequirements.size;
+ p->buffers[i].fd = fd;
+ } else {
+ VkImportMemoryFdInfoKHR importInfo = {
+ .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
+ .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
+ .fd = fcntl(buffers[i]->datas[0].fd, F_DUPFD_CLOEXEC, 0),
+ };
+ allocateInfo.pNext = &importInfo;
+ p->buffers[i].fd = -1;
+ spa_log_info(s->log, "import DMABUF");
+
+ VK_CHECK_RESULT(vkAllocateMemory(s->device,
+ &allocateInfo, NULL, &p->buffers[i].memory));
+ }
+ VK_CHECK_RESULT(vkBindImageMemory(s->device,
+ p->buffers[i].image, p->buffers[i].memory, 0));
+
+ VkImageViewCreateInfo viewInfo = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .image = p->buffers[i].image,
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .components.r = VK_COMPONENT_SWIZZLE_R,
+ .components.g = VK_COMPONENT_SWIZZLE_G,
+ .components.b = VK_COMPONENT_SWIZZLE_B,
+ .components.a = VK_COMPONENT_SWIZZLE_A,
+ .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .subresourceRange.levelCount = 1,
+ .subresourceRange.layerCount = 1,
+ };
+
+ VK_CHECK_RESULT(vkCreateImageView(s->device,
+ &viewInfo, NULL, &p->buffers[i].view));
+ }
+ p->n_buffers = n_buffers;
+
+ return 0;
+}
+
+int spa_vulkan_init_stream(struct vulkan_state *s, struct vulkan_stream *stream,
+ enum spa_direction direction, struct spa_dict *props)
+{
+ spa_zero(*stream);
+ stream->direction = direction;
+ stream->current_buffer_id = SPA_ID_INVALID;
+ stream->busy_buffer_id = SPA_ID_INVALID;
+ stream->ready_buffer_id = SPA_ID_INVALID;
+ return 0;
+}
+
+int spa_vulkan_prepare(struct vulkan_state *s)
+{
+ if (!s->prepared) {
+ CHECK(createInstance(s));
+ CHECK(findPhysicalDevice(s));
+ CHECK(createDevice(s));
+ CHECK(createDescriptors(s));
+ CHECK(createComputePipeline(s, s->shaderName));
+ CHECK(createCommandBuffer(s));
+ s->prepared = true;
+ }
+ return 0;
+}
+
+int spa_vulkan_unprepare(struct vulkan_state *s)
+{
+ if (s->prepared) {
+ vkDestroyShaderModule(s->device, s->computeShaderModule, NULL);
+ vkDestroySampler(s->device, s->sampler, NULL);
+ vkDestroyDescriptorPool(s->device, s->descriptorPool, NULL);
+ vkDestroyDescriptorSetLayout(s->device, s->descriptorSetLayout, NULL);
+ vkDestroyPipelineLayout(s->device, s->pipelineLayout, NULL);
+ vkDestroyPipeline(s->device, s->pipeline, NULL);
+ vkDestroyCommandPool(s->device, s->commandPool, NULL);
+ vkDestroyFence(s->device, s->fence, NULL);
+ vkDestroyDevice(s->device, NULL);
+ vkDestroyInstance(s->instance, NULL);
+ s->prepared = false;
+ }
+ return 0;
+}
+
+int spa_vulkan_start(struct vulkan_state *s)
+{
+ uint32_t i;
+
+ for (i = 0; i < s->n_streams; i++) {
+ struct vulkan_stream *p = &s->streams[i];
+ p->current_buffer_id = SPA_ID_INVALID;
+ p->busy_buffer_id = SPA_ID_INVALID;
+ p->ready_buffer_id = SPA_ID_INVALID;
+ }
+ return 0;
+}
+
+int spa_vulkan_stop(struct vulkan_state *s)
+{
+ VK_CHECK_RESULT(vkDeviceWaitIdle(s->device));
+ clear_streams(s);
+ s->started = false;
+ return 0;
+}
+
+int spa_vulkan_ready(struct vulkan_state *s)
+{
+ uint32_t i;
+ VkResult result;
+
+ if (!s->started)
+ return 0;
+
+ result = vkGetFenceStatus(s->device, s->fence);
+ if (result == VK_NOT_READY)
+ return -EBUSY;
+ VK_CHECK_RESULT(result);
+
+ s->started = false;
+
+ for (i = 0; i < s->n_streams; i++) {
+ struct vulkan_stream *p = &s->streams[i];
+ p->ready_buffer_id = p->busy_buffer_id;
+ p->busy_buffer_id = SPA_ID_INVALID;
+ }
+ return 0;
+}
+
+int spa_vulkan_process(struct vulkan_state *s)
+{
+ CHECK(updateDescriptors(s));
+ CHECK(runCommandBuffer(s));
+ VK_CHECK_RESULT(vkDeviceWaitIdle(s->device));
+
+ return 0;
+}