vulkan: use images

The filter can now run shadertoy filters.
This commit is contained in:
Wim Taymans 2022-05-31 18:00:24 +02:00
parent 24fc972164
commit b02ebec954
5 changed files with 102 additions and 63 deletions

View file

@ -4,14 +4,7 @@
#define WORKGROUP_SIZE 32 #define WORKGROUP_SIZE 32
layout (local_size_x = WORKGROUP_SIZE, local_size_y = WORKGROUP_SIZE, local_size_z = 1 ) in; layout (local_size_x = WORKGROUP_SIZE, local_size_y = WORKGROUP_SIZE, local_size_z = 1 ) in;
struct Pixel{ layout(rgba32f, binding = 0) uniform image2D resultImage;
vec4 value;
};
layout(std140, binding = 0) buffer buf
{
Pixel imageData[];
};
layout( push_constant ) uniform Constants { layout( push_constant ) uniform Constants {
float time; float time;
@ -42,8 +35,7 @@ void main()
mainImage(outColor, coord); mainImage(outColor, coord);
imageData[PushConstant.width * gl_GlobalInvocationID.y + imageStore(resultImage, ivec2(gl_GlobalInvocationID.xy), outColor);
gl_GlobalInvocationID.x].value = outColor;
} }
//#include "plasma-globe.comp" //#include "plasma-globe.comp"

View file

@ -91,12 +91,7 @@ struct impl {
struct spa_hook_list hooks; struct spa_hook_list hooks;
struct spa_callbacks callbacks; struct spa_callbacks callbacks;
bool async;
struct spa_source timer_source;
struct itimerspec timerspec;
bool started; bool started;
uint64_t frame_count;
struct vulkan_state state; struct vulkan_state state;
struct port port[2]; struct port port[2];
@ -177,7 +172,7 @@ static inline void reuse_buffer(struct impl *this, struct port *port, uint32_t i
struct buffer *b = &port->buffers[id]; struct buffer *b = &port->buffers[id];
if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) { if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) {
spa_log_info(this->log, NAME " %p: reuse buffer %d", this, id); spa_log_debug(this->log, NAME " %p: reuse buffer %d", this, id);
SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT); SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
spa_list_append(&port->empty, &b->link); spa_list_append(&port->empty, &b->link);
@ -196,7 +191,6 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
if (this->started) if (this->started)
return 0; return 0;
this->frame_count = 0;
this->started = true; this->started = true;
spa_vulkan_start(&this->state); spa_vulkan_start(&this->state);
break; break;
@ -608,21 +602,31 @@ static int impl_node_process(void *object)
} }
if (spa_list_is_empty(&outport->empty)) { if (spa_list_is_empty(&outport->empty)) {
spa_log_error(this->log, NAME " %p: out of buffers", this); spa_log_debug(this->log, NAME " %p: out of buffers", this);
return -EPIPE; return -EPIPE;
} }
b = &inport->buffers[inio->buffer_id]; b = &inport->buffers[inio->buffer_id];
this->state.streams[0].pending_buffer_id = b->id; this->state.streams[inport->stream_id].pending_buffer_id = b->id;
inio->status = SPA_STATUS_NEED_DATA;
b = spa_list_first(&outport->empty, struct buffer, link); b = spa_list_first(&outport->empty, struct buffer, link);
spa_list_remove(&b->link); spa_list_remove(&b->link);
this->state.streams[1].pending_buffer_id = b->id; SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT);
this->state.streams[outport->stream_id].pending_buffer_id = b->id;
this->state.constants.time += 0.025;
this->state.constants.frame++;
spa_log_debug(this->log, "filter into %d", b->id);
spa_vulkan_process(&this->state); spa_vulkan_process(&this->state);
b->outbuf->datas[0].chunk->offset = 0;
b->outbuf->datas[0].chunk->size = b->outbuf->datas[0].maxsize;
b->outbuf->datas[0].chunk->stride = this->position->video.stride;
outio->buffer_id = b->id; outio->buffer_id = b->id;
outio->status = SPA_STATUS_HAVE_DATA; outio->status = SPA_STATUS_HAVE_DATA;
inio->status = SPA_STATUS_NEED_DATA;
return SPA_STATUS_NEED_DATA | SPA_STATUS_HAVE_DATA; return SPA_STATUS_NEED_DATA | SPA_STATUS_HAVE_DATA;
} }
@ -694,6 +698,7 @@ impl_init(const struct spa_handle_factory *factory,
this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
this->state.log = this->log; this->state.log = this->log;
this->state.shaderName = "spa/plugins/vulkan/shaders/filter.spv";
spa_hook_list_init(&this->hooks); spa_hook_list_init(&this->hooks);
@ -715,7 +720,7 @@ impl_init(const struct spa_handle_factory *factory,
this->info.n_params = 2; this->info.n_params = 2;
port = &this->port[0]; port = &this->port[0];
port->stream_id = 0; port->stream_id = 1;
port->direction = SPA_DIRECTION_INPUT; port->direction = SPA_DIRECTION_INPUT;
port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
SPA_PORT_CHANGE_MASK_PARAMS | SPA_PORT_CHANGE_MASK_PARAMS |
@ -729,13 +734,13 @@ impl_init(const struct spa_handle_factory *factory,
port->params[4] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); port->params[4] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
port->info.params = port->params; port->info.params = port->params;
port->info.n_params = 5; port->info.n_params = 5;
spa_vulkan_init_stream(&this->state, &this->state.streams[0], spa_vulkan_init_stream(&this->state, &this->state.streams[port->stream_id],
SPA_DIRECTION_INPUT, NULL); SPA_DIRECTION_INPUT, NULL);
spa_list_init(&port->empty); spa_list_init(&port->empty);
spa_list_init(&port->ready); spa_list_init(&port->ready);
port = &this->port[1]; port = &this->port[1];
port->stream_id = 1; port->stream_id = 0;
port->direction = SPA_DIRECTION_OUTPUT; port->direction = SPA_DIRECTION_OUTPUT;
port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
SPA_PORT_CHANGE_MASK_PARAMS | SPA_PORT_CHANGE_MASK_PARAMS |
@ -751,7 +756,7 @@ impl_init(const struct spa_handle_factory *factory,
port->info.n_params = 5; port->info.n_params = 5;
spa_list_init(&port->empty); spa_list_init(&port->empty);
spa_list_init(&port->ready); spa_list_init(&port->ready);
spa_vulkan_init_stream(&this->state, &this->state.streams[1], spa_vulkan_init_stream(&this->state, &this->state.streams[port->stream_id],
SPA_DIRECTION_OUTPUT, NULL); SPA_DIRECTION_OUTPUT, NULL);
this->state.n_streams = 2; this->state.n_streams = 2;

View file

@ -959,6 +959,7 @@ impl_init(const struct spa_handle_factory *factory,
this->state.log = this->log; this->state.log = this->log;
spa_vulkan_init_stream(&this->state, &this->state.streams[0], spa_vulkan_init_stream(&this->state, &this->state.streams[0],
SPA_DIRECTION_OUTPUT, NULL); SPA_DIRECTION_OUTPUT, NULL);
this->state.shaderName = "spa/plugins/vulkan/shaders/main.spv";
this->state.n_streams = 1; this->state.n_streams = 1;
return 0; return 0;

View file

@ -16,6 +16,7 @@
#include <time.h> #include <time.h>
#include <spa/utils/result.h> #include <spa/utils/result.h>
#include <spa/utils/string.h>
#include <spa/support/log.h> #include <spa/support/log.h>
#include <spa/debug/mem.h> #include <spa/debug/mem.h>
@ -91,10 +92,10 @@ static int vkresult_to_errno(VkResult result)
#define VK_CHECK_RESULT(f) \ #define VK_CHECK_RESULT(f) \
{ \ { \
VkResult _result = (f); \ VkResult _result = (f); \
int _res = -vkresult_to_errno(_result); \ int _r = -vkresult_to_errno(_result); \
if (_result != VK_SUCCESS) { \ if (_result != VK_SUCCESS) { \
spa_log_error(s->log, "error: %d (%s)", _result, spa_strerror(_res)); \ spa_log_error(s->log, "error: %d (%d %s)", _result, _r, spa_strerror(_r)); \
return _res; \ return _r; \
} \ } \
} }
#define CHECK(f) \ #define CHECK(f) \
@ -117,24 +118,32 @@ static int createInstance(struct vulkan_state *s)
static const char * const extensions[] = { static const char * const extensions[] = {
VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME
}; };
static const char * const layers[] = { static const char * const checkLayers[] = {
#ifdef ENABLE_VALIDATION
"VK_LAYER_KHRONOS_validation", "VK_LAYER_KHRONOS_validation",
#endif
NULL
}; };
uint32_t i, layerCount; uint32_t i, j, layerCount, n_layers = 0;
const char *layers[1];
vkEnumerateInstanceLayerProperties(&layerCount, NULL); vkEnumerateInstanceLayerProperties(&layerCount, NULL);
VkLayerProperties availableLayers[layerCount]; VkLayerProperties availableLayers[layerCount];
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers); vkEnumerateInstanceLayerProperties(&layerCount, availableLayers);
for (i = 0; i < layerCount; i++) for (i = 0; i < layerCount; i++) {
spa_log_info(s->log, "%s", availableLayers[i].layerName); 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 = { const VkInstanceCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &applicationInfo, .pApplicationInfo = &applicationInfo,
.enabledExtensionCount = 1, .enabledExtensionCount = 1,
.ppEnabledExtensionNames = extensions, .ppEnabledExtensionNames = extensions,
.enabledLayerCount = 1, .enabledLayerCount = n_layers,
.ppEnabledLayerNames = layers, .ppEnabledLayerNames = layers,
}; };
@ -239,7 +248,7 @@ static int createDescriptors(struct vulkan_state *s)
uint32_t i; uint32_t i;
VkDescriptorPoolSize descriptorPoolSize = { VkDescriptorPoolSize descriptorPoolSize = {
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.descriptorCount = s->n_streams, .descriptorCount = s->n_streams,
}; };
const VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = { const VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = {
@ -257,7 +266,7 @@ static int createDescriptors(struct vulkan_state *s)
for (i = 0; i < s->n_streams; i++) { for (i = 0; i < s->n_streams; i++) {
descriptorSetLayoutBinding[i] = (VkDescriptorSetLayoutBinding) { descriptorSetLayoutBinding[i] = (VkDescriptorSetLayoutBinding) {
.binding = i, .binding = i,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.descriptorCount = 1, .descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT
}; };
@ -281,13 +290,16 @@ static int createDescriptors(struct vulkan_state *s)
VK_CHECK_RESULT(vkAllocateDescriptorSets(s->device, VK_CHECK_RESULT(vkAllocateDescriptorSets(s->device,
&descriptorSetAllocateInfo, &descriptorSetAllocateInfo,
&s->descriptorSet)); &s->descriptorSet));
return 0; return 0;
} }
static int updateDescriptors(struct vulkan_state *s) static int updateDescriptors(struct vulkan_state *s)
{ {
uint32_t i; uint32_t i;
VkDescriptorBufferInfo descriptorBufferInfo[s->n_streams]; VkDescriptorImageInfo descriptorImageInfo[s->n_streams];
VkWriteDescriptorSet writeDescriptorSet[s->n_streams]; VkWriteDescriptorSet writeDescriptorSet[s->n_streams];
for (i = 0; i < s->n_streams; i++) { for (i = 0; i < s->n_streams; i++) {
@ -301,18 +313,17 @@ static int updateDescriptors(struct vulkan_state *s)
p->busy_buffer_id = p->current_buffer_id; p->busy_buffer_id = p->current_buffer_id;
p->pending_buffer_id = SPA_ID_INVALID; p->pending_buffer_id = SPA_ID_INVALID;
descriptorBufferInfo[i] = (VkDescriptorBufferInfo) { descriptorImageInfo[i] = (VkDescriptorImageInfo) {
.buffer = p->buffers[p->current_buffer_id].buffer, .imageView = p->buffers[p->current_buffer_id].view,
.offset = 0, .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
.range = p->bufferSize,
}; };
writeDescriptorSet[i] = (VkWriteDescriptorSet) { writeDescriptorSet[i] = (VkWriteDescriptorSet) {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = s->descriptorSet, .dstSet = s->descriptorSet,
.dstBinding = i, .dstBinding = i,
.descriptorCount = 1, .descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.pBufferInfo = &descriptorBufferInfo[i], .pImageInfo = &descriptorImageInfo[i],
}; };
} }
vkUpdateDescriptorSets(s->device, s->n_streams, vkUpdateDescriptorSets(s->device, s->n_streams,
@ -465,7 +476,8 @@ static void clear_buffers(struct vulkan_state *s, struct vulkan_stream *p)
if (p->buffers[i].fd != -1) if (p->buffers[i].fd != -1)
close(p->buffers[i].fd); close(p->buffers[i].fd);
vkFreeMemory(s->device, p->buffers[i].memory, NULL); vkFreeMemory(s->device, p->buffers[i].memory, NULL);
vkDestroyBuffer(s->device, p->buffers[i].buffer, NULL); vkDestroyImage(s->device, p->buffers[i].image, NULL);
vkDestroyImageView(s->device, p->buffers[i].view, NULL);
} }
p->n_buffers = 0; p->n_buffers = 0;
} }
@ -487,30 +499,37 @@ int spa_vulkan_use_buffers(struct vulkan_state *s, struct vulkan_stream *p, uint
clear_buffers(s, p); clear_buffers(s, p);
p->bufferSize = s->constants.width * s->constants.height * sizeof(struct pixel);
for (i = 0; i < n_buffers; i++) { for (i = 0; i < n_buffers; i++) {
VkExternalMemoryBufferCreateInfo extInfo; VkExternalMemoryImageCreateInfo extInfo;
VkBufferCreateInfo bufferCreateInfo = { VkImageCreateInfo imageCreateInfo = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.size = p->bufferSize, .imageType = VK_IMAGE_TYPE_2D,
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, .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 = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
}; };
if (!(flags & SPA_NODE_BUFFERS_FLAG_ALLOC)) { if (!(flags & SPA_NODE_BUFFERS_FLAG_ALLOC)) {
extInfo = (VkExternalMemoryBufferCreateInfo) { extInfo = (VkExternalMemoryImageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
}; };
bufferCreateInfo.pNext = &extInfo; imageCreateInfo.pNext = &extInfo;
} }
VK_CHECK_RESULT(vkCreateBuffer(s->device, VK_CHECK_RESULT(vkCreateImage(s->device,
&bufferCreateInfo, NULL, &p->buffers[i].buffer)); &imageCreateInfo, NULL, &p->buffers[i].image));
VkMemoryRequirements memoryRequirements; VkMemoryRequirements memoryRequirements;
vkGetBufferMemoryRequirements(s->device, vkGetImageMemoryRequirements(s->device,
p->buffers[i].buffer, &memoryRequirements); p->buffers[i].image, &memoryRequirements);
VkMemoryAllocateInfo allocateInfo = { VkMemoryAllocateInfo allocateInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
@ -534,12 +553,14 @@ int spa_vulkan_use_buffers(struct vulkan_state *s, struct vulkan_stream *p, uint
VK_CHECK_RESULT(vkGetMemoryFdKHR(s->device, &getFdInfo, &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_DmaBuf;
buffers[i]->datas[0].type = SPA_DATA_MemFd; buffers[i]->datas[0].type = SPA_DATA_MemFd;
buffers[i]->datas[0].fd = fd; buffers[i]->datas[0].fd = fd;
buffers[i]->datas[0].flags = SPA_DATA_FLAG_READABLE; buffers[i]->datas[0].flags = SPA_DATA_FLAG_READABLE;
buffers[i]->datas[0].mapoffset = 0; buffers[i]->datas[0].mapoffset = 0;
buffers[i]->datas[0].maxsize = p->bufferSize; buffers[i]->datas[0].maxsize = memoryRequirements.size;
p->buffers[i].fd = fd; p->buffers[i].fd = fd;
} else { } else {
VkImportMemoryFdInfoKHR importInfo = { VkImportMemoryFdInfoKHR importInfo = {
@ -549,12 +570,30 @@ int spa_vulkan_use_buffers(struct vulkan_state *s, struct vulkan_stream *p, uint
}; };
allocateInfo.pNext = &importInfo; allocateInfo.pNext = &importInfo;
p->buffers[i].fd = -1; p->buffers[i].fd = -1;
spa_log_info(s->log, "import DMABUF");
VK_CHECK_RESULT(vkAllocateMemory(s->device, VK_CHECK_RESULT(vkAllocateMemory(s->device,
&allocateInfo, NULL, &p->buffers[i].memory)); &allocateInfo, NULL, &p->buffers[i].memory));
} }
VK_CHECK_RESULT(vkBindBufferMemory(s->device, VK_CHECK_RESULT(vkBindImageMemory(s->device,
p->buffers[i].buffer, p->buffers[i].memory, 0)); 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; p->n_buffers = n_buffers;
@ -579,7 +618,7 @@ int spa_vulkan_prepare(struct vulkan_state *s)
CHECK(findPhysicalDevice(s)); CHECK(findPhysicalDevice(s));
CHECK(createDevice(s)); CHECK(createDevice(s));
CHECK(createDescriptors(s)); CHECK(createDescriptors(s));
CHECK(createComputePipeline(s, "spa/plugins/vulkan/shaders/main.spv")); CHECK(createComputePipeline(s, s->shaderName));
CHECK(createCommandBuffer(s)); CHECK(createCommandBuffer(s));
s->prepared = true; s->prepared = true;
} }
@ -651,6 +690,7 @@ int spa_vulkan_process(struct vulkan_state *s)
{ {
CHECK(updateDescriptors(s)); CHECK(updateDescriptors(s));
CHECK(runCommandBuffer(s)); CHECK(runCommandBuffer(s));
VK_CHECK_RESULT(vkDeviceWaitIdle(s->device));
return 0; return 0;
} }

View file

@ -20,7 +20,8 @@ struct push_constants {
struct vulkan_buffer { struct vulkan_buffer {
int fd; int fd;
VkBuffer buffer; VkImage image;
VkImageView view;
VkDeviceMemory memory; VkDeviceMemory memory;
}; };
@ -32,7 +33,6 @@ struct vulkan_stream {
uint32_t busy_buffer_id; uint32_t busy_buffer_id;
uint32_t ready_buffer_id; uint32_t ready_buffer_id;
uint32_t bufferSize;
struct vulkan_buffer buffers[MAX_BUFFERS]; struct vulkan_buffer buffers[MAX_BUFFERS];
uint32_t n_buffers; uint32_t n_buffers;
}; };
@ -49,6 +49,7 @@ struct vulkan_state {
VkPipeline pipeline; VkPipeline pipeline;
VkPipelineLayout pipelineLayout; VkPipelineLayout pipelineLayout;
const char *shaderName;
VkShaderModule computeShaderModule; VkShaderModule computeShaderModule;
VkCommandPool commandPool; VkCommandPool commandPool;