Start implementing basic ffmpeg decoder

This commit is contained in:
mittorn 2024-10-28 06:44:26 +03:00
parent 7c8ee5f384
commit 0d90b67015
3 changed files with 465 additions and 20 deletions

View file

@ -72,12 +72,6 @@ const int WIDTH = 1920; // Size of rendered mandelbrot set.
const int HEIGHT = 1080; // Size of renderered mandelbrot set.
const int WORKGROUP_SIZE = 32; // Workgroup size in compute shader.
/*
The application launches a compute shader that renders the mandelbrot set,
by rendering it into a storage buffer.
The storage buffer is then read from the GPU, and saved as .png.
*/
struct ComputeApplication {
// The pixels of the rendered mandelbrot set are in this format:
struct Pixel {
@ -173,22 +167,9 @@ struct ComputeApplication {
The validation layer will NOT give warnings if you forget these, so be very careful not to forget them.
*/
vkCmdBindPipeline(chain[chidx].commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline.pipeline);
/*
Now we shall start recording commands into the newly allocated command buffer.
*/
vkCmdBindDescriptorSets(chain[chidx].commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline.pipelineLayout, 0, 1, &chain[chidx].descriptorSet, 0, NULL);
/*
Calling vkCmdDispatch basically starts the compute pipeline, and executes the compute shader.
The number of workgroups is specified in the arguments.
If you are already familiar with compute shaders from OpenGL, this should be nothing new to you.
*/
vkCmdDispatch(chain[chidx].commandBuffer, (uint32_t)ceil(WIDTH/2 / float(WORKGROUP_SIZE)), (uint32_t)ceil(HEIGHT/2 / float(WORKGROUP_SIZE)), 1);
VK_CHECK_RESULT(vkEndCommandBuffer(chain[chidx].commandBuffer)); // end recording commands.
/*
We create a fence.
*/
$Sc fenceCreateInfo = FenceInfo();
VK_CHECK_RESULT(vkCreateFence(dev.device, &fenceCreateInfo, NULL, &chain[chidx].fence));

462
vkplayer.cpp Normal file
View file

@ -0,0 +1,462 @@
#include "vulkan_pipeline_utl.h"
#include "vulkan_utl.h"
#include "vulkan_framebuffer_utl.h"
#include "vulkan_texture_utl.h"
#include "vulkan_contructors.h"
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/pixdesc.h>
#include <libavutil/hwcontext.h>
#include <libavutil/opt.h>
#include <libavutil/avassert.h>
#include <libavutil/imgutils.h>
}
#include <pthread.h>
#include <X11/Xlib.h>
#include <vulkan/vulkan_xlib.h>
// use glfw for temporary swapchain window
// todo: GET RID OF THIS, IT SOMEHOW INCLUDES GL!!!
#include <GLFW/glfw3.h>
#define GLFW_EXPOSE_NATIVE_X11
#include <GLFW/glfw3native.h>
// todo: REMOVE THIS. Why "simpliest triangle" demos even use this????
// It does not "HELP" drawing 2D triangle!!! It is not useful in engines using own matrix transform code!
#include <glm/gtc/matrix_transform.hpp>
#define MAX_SWAPCHAIN_IMAGES 32
struct Vertex
{
float position[3];
float color[3];
};
struct DecoderImage
{
VkSamplerYcbcrConversion ycbcr_sampler_conversion;
VkSampler ycbcr_sampler;
VkImage image;
VkDeviceMemory image_memory;
// todo: deduplicate shared parameters
VkDeviceSize memory_offset_plane0, memory_offset_plane1;
VkImageView image_view;
unsigned char *pMappedData;
};
void CreateSoftwareImage(VkInstance inst, VulkanDevice &dev, DecoderImage &image)
{
CallWith(Image2dInfo(VK_IMAGE_USAGE_SAMPLED_BIT, VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, 1920, 1080,
$(flags) = VK_IMAGE_CREATE_DISJOINT_BIT,
$(tiling) = VK_IMAGE_TILING_LINEAR,
$(initialLayout) = VK_IMAGE_LAYOUT_PREINITIALIZED ),
vkCreateImage(dev.device, &ref,NULL, &image.image));
VkImagePlaneMemoryRequirementsInfo plane_info = $M(VkImagePlaneMemoryRequirementsInfo{VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO},
$(planeAspect) = VK_IMAGE_ASPECT_PLANE_0_BIT);
VkImageMemoryRequirementsInfo2 image_info = $M(VkImageMemoryRequirementsInfo2{VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2},
$(image) = image.image);
VkMemoryRequirements2 image_reqs = $M(VkMemoryRequirements2{VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2});
image_info.pNext = &plane_info;
PFN_vkGetImageMemoryRequirements2 pvkGetImageMemoryRequirements2 = (PFN_vkGetImageMemoryRequirements2)vkGetInstanceProcAddr(inst, "vkGetImageMemoryRequirements2KHR");
pvkGetImageMemoryRequirements2(dev.device, &image_info, &image_reqs);
VkDeviceSize image_size = image_reqs.memoryRequirements.size;
uint32_t image_bits = image_reqs.memoryRequirements.memoryTypeBits;
image.memory_offset_plane0 = 0;
image.memory_offset_plane1 = image_size; // todo: should not we align plane offset here
plane_info.planeAspect = VK_IMAGE_ASPECT_PLANE_1_BIT;
pvkGetImageMemoryRequirements2(dev.device, &image_info, &image_reqs);
image_size += image_reqs.memoryRequirements.size;
image_bits = image_bits | image_reqs.memoryRequirements.memoryTypeBits;
VkMemoryAllocateInfo ainfo = AllocateInfo(image_size);
dev.GetMemoryType(image_bits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &ainfo.memoryTypeIndex);
vkAllocateMemory(dev.device, &ainfo, NULL, &image.image_memory);
VkBindImagePlaneMemoryInfo plane0i = {VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO, NULL, VK_IMAGE_ASPECT_PLANE_0_BIT};
VkBindImagePlaneMemoryInfo plane1i = {VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO, NULL, VK_IMAGE_ASPECT_PLANE_1_BIT};
VkBindImageMemoryInfo bindInfo[2] = {{VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO, &plane0i}, {VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO, &plane1i}};
$F(bindInfo[0], $(image) = image.image, $(memory) = image.image_memory, $(memoryOffset) = image.memory_offset_plane0);
$F(bindInfo[1], $(image) = image.image, $(memory) = image.image_memory, $(memoryOffset) = image.memory_offset_plane1);
PFN_vkBindImageMemory2 pvkBindImageMemory2 = (PFN_vkBindImageMemory2)vkGetInstanceProcAddr(inst, "vkBindImageMemory2KHR");
pvkBindImageMemory2(dev.device, 2, bindInfo);
PFN_vkCreateSamplerYcbcrConversion pvkCreateSamplerYcbcrConversion = (PFN_vkCreateSamplerYcbcrConversion)vkGetInstanceProcAddr(inst, "vkCreateSamplerYcbcrConversionKHR");
CallWith($M(VkSamplerYcbcrConversionCreateInfo{VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO},
$(format) = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
$(ycbcrModel) = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY,
$(ycbcrRange) = VK_SAMPLER_YCBCR_RANGE_ITU_FULL,
$(xChromaOffset) = VK_CHROMA_LOCATION_COSITED_EVEN, // zero
$(yChromaOffset) = VK_CHROMA_LOCATION_COSITED_EVEN,
$(chromaFilter) = VK_FILTER_NEAREST), // zero
pvkCreateSamplerYcbcrConversion(dev.device, &ref, NULL, &image.ycbcr_sampler_conversion));
$Sc ycbcr_info = $M(VkSamplerYcbcrConversionInfo{VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO},
$(conversion) = image.ycbcr_sampler_conversion);
CallWith($M(VkSamplerCreateInfo{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, &ycbcr_info},
$(magFilter) = VK_FILTER_NEAREST,
$(minFilter) = VK_FILTER_NEAREST,
$(mipmapMode) = VK_SAMPLER_MIPMAP_MODE_NEAREST, // zero
$(addressModeU) = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
$(addressModeV) = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
$(addressModeW) = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
$(maxLod) = 1.0f,
$(borderColor) = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK),
vkCreateSampler(dev.device, &ref, NULL, &image.ycbcr_sampler);
);
CallWith($M(VkImageViewCreateInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, &ycbcr_info},
$(image) = image.image, $(viewType) = VK_IMAGE_VIEW_TYPE_2D,
$(format) = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
$(subresourceRange) = SubresourceRange()),
vkCreateImageView(dev.device, &ref, NULL, &image.image_view));
vkMapMemory(dev.device, image.image_memory, 0, image_size, 0, (void**)&image.pMappedData);
}
static struct FFmpegDecoder
{
DecoderImage images[4];
// todo: align thread-shared data to unshare with immutable
volatile int decode_index, present_index;
pthread_t thread = 0;
pthread_mutex_t mutex;
pthread_cond_t cond;
// todo: should keep availiable frames list/mask here (atomic uint64_t?)
} gFF;
static int WaitActiveFrame()
{
// todo: where frame selection should be done?
pthread_mutex_lock(&gFF.mutex);
while(gFF.decode_index <= gFF.present_index)
pthread_cond_wait(&gFF.cond, &gFF.mutex);
int ret = ++gFF.present_index;
pthread_mutex_unlock(&gFF.mutex);
return ret;
}
static void *DecoderThread(void*)
{
AVFormatContext *input_ctx = NULL;
AVCodec *decoder = NULL;
AVCodecContext *decoder_ctx = NULL;
AVPacket *packet = NULL;
int video;
avformat_open_input(&input_ctx, "out.265", NULL, NULL);
avformat_find_stream_info(input_ctx, NULL);
video = av_find_best_stream(input_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);
decoder_ctx = avcodec_alloc_context3(decoder);
avcodec_open2(decoder_ctx, decoder, NULL);
packet = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
int idx = 0;
while(true)
{
av_read_frame(input_ctx, packet);
if(packet->stream_index == video)
{
int res = avcodec_send_packet(decoder_ctx, packet);
if(res >= 0)
{
res = avcodec_receive_frame(decoder_ctx, frame);
if(res < 0)
break;
idx++;
DecoderImage &img = gFF.images[idx & 3];
// todo: wait fence?
memcpy(img.pMappedData + img.memory_offset_plane0 , frame->data[0], frame->linesize[0] + frame->height);
memcpy(img.pMappedData + img.memory_offset_plane1 , frame->data[1], frame->linesize[1] + frame->height/2);
pthread_mutex_lock(&gFF.mutex);
gFF.decode_index = idx;
pthread_cond_signal(&gFF.cond);
pthread_mutex_unlock(&gFF.mutex);
printf("frame %d\n", idx);
usleep(20000);
}
else break;
}
av_packet_unref(packet);
}
return NULL;
}
static void SetupFFMpeg(VkInstance inst, VulkanDevice &dev)
{
for(int i = 0; i < 4; i++)
CreateSoftwareImage(inst, dev, gFF.images[i]);
pthread_mutex_init(&gFF.mutex, NULL);
pthread_cond_init(&gFF.cond, NULL);
pthread_create(&gFF.thread, NULL, &DecoderThread, NULL);
}
struct GraphicsApplicationPipeline: BaseVulkanPipeline
{
void Init(VkDevice dev, VkRenderPass renderPass)
{
device = dev;
CreateDescriptorSetLayout(
BasicBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,1,VK_SHADER_STAGE_VERTEX_BIT)
);
CreatePool(1,
BasicPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1)
);
VkShaderModule vp, fp;
CreateGraphicsPipeline(renderPass,
Stages(
ShaderFromFile(vp, "triangle.vert.spv", VK_SHADER_STAGE_VERTEX_BIT),
ShaderFromFile(fp, "triangle.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT)),
VertexBindings(
VertexBinding(0, sizeof(Vertex))
),
VertexAttributes(
VertAttrib(0, 0, offsetof(Vertex,position)),
VertAttrib(1, 0, offsetof(Vertex,color))
),
DynamicStates(
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
),
DepthStencil(true, true)
);
vkDestroyShaderModule(device, vp, NULL);
vkDestroyShaderModule(device, fp, NULL);
}
void UpdateDescriptors(VkDescriptorSet dstSet, const VkDescriptorBufferInfo &buffer)
{
WriteDescriptors(
BufferWrite(dstSet, 0, buffer)
);
}
};
struct GraphicsApplication
{
VulkanContext context;
VulkanDevice dev;
GLFWwindow *window = NULL;
VkSwapchainKHR swapchain;
VkSurfaceKHR surface;
VkImage swapchainImages[MAX_SWAPCHAIN_IMAGES];
unsigned int numSwapchainImages;
VulkanFramebuffer swapchainFbs[MAX_SWAPCHAIN_IMAGES];
VkCommandBuffer commandBuffers[MAX_SWAPCHAIN_IMAGES];
VkFence chainFences[MAX_SWAPCHAIN_IMAGES];
VkSemaphore swapchainRenderSemaphore[MAX_SWAPCHAIN_IMAGES], swapchainPresentSemaphore[MAX_SWAPCHAIN_IMAGES];
struct UBO
{
glm::mat4 transformationMatrix;
};
GraphicsApplicationPipeline graphicsPipeline;
VkDescriptorSet descriptorSet;
VulkanBuffer uboBuf;
VulkanBuffer stagingVert;
VulkanBuffer stagingInd;
//VkFence swapchainFence;
void CreateWindow(const char* windowTitle, int& outWidth, int& outHeight, bool resizable)
{
if (!glfwInit())
return;
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
// render full screen without overlapping taskbar
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
window = glfwCreateWindow(outWidth, outHeight, windowTitle, nullptr, nullptr);
if (!window) {
glfwTerminate();
return;
}
glfwGetWindowSize(window, &outWidth, &outHeight);
glfwSetKeyCallback(window, [](GLFWwindow* window, int key, int, int action, int) {
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
glfwSetWindowShouldClose(window, GLFW_TRUE);
}
});
glfwSetErrorCallback([](int error, const char* description) { printf("GLFW Error (%i): %s\n", error, description); });
}
void DestroyWindow()
{
if(window)
glfwDestroyWindow(window);
glfwTerminate();
}
void CreateSwapchain(uint32_t width, uint32_t height)
{
VK_CHECK_RESULT(CallWith($M(VkXlibSurfaceCreateInfoKHR{VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR},
$(dpy) = glfwGetX11Display(), $(window) = glfwGetX11Window(window)),
vkCreateXlibSurfaceKHR(context.instance, &ref, nullptr, &surface)));
CallWith($M(
VkSwapchainCreateInfoKHR{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR},
$(surface) = surface, $(minImageCount) = 4,
$(imageFormat) = VK_FORMAT_B8G8R8A8_UNORM,
$(imageColorSpace) = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
$(imageExtent) = VkExtent2D{width, height},
$(imageArrayLayers),$(queueFamilyIndexCount),$(clipped),
$(imageUsage) = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
$(pQueueFamilyIndices) = &dev.defaultFamilyIndex,
$(preTransform) = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
$(compositeAlpha) = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
$(presentMode) = VK_PRESENT_MODE_FIFO_KHR),
vkCreateSwapchainKHR(dev.device, &ref, nullptr, &swapchain));
numSwapchainImages = MAX_SWAPCHAIN_IMAGES;
VK_CHECK_RESULT(vkGetSwapchainImagesKHR(dev.device, swapchain, &numSwapchainImages, swapchainImages));
VkSemaphoreCreateInfo semaphoreCreateInfo = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
for(int i = 0; i < numSwapchainImages; i++)
{
swapchainFbs[i].Create(dev.device);
swapchainFbs[i].CreateDepthAttachment(dev, VK_FORMAT_D32_SFLOAT, width, height);
swapchainFbs[i].Init(swapchainImages[i], VK_FORMAT_B8G8R8A8_UNORM, width, height);
VK_CHECK_RESULT(vkCreateSemaphore(dev.device, &semaphoreCreateInfo, nullptr, &swapchainPresentSemaphore[i]));
VK_CHECK_RESULT(vkCreateSemaphore(dev.device, &semaphoreCreateInfo, nullptr, &swapchainRenderSemaphore[i]));
}
}
unsigned int AcquireImage(int sem_index, VkFence fence = NULL)
{
unsigned int index;
vkAcquireNextImageKHR(dev.device, swapchain, UINT64_MAX, swapchainRenderSemaphore[sem_index], fence, &index);
return index;
}
void PresentImage(unsigned int index)
{
CallWith(PresentInfo(&swapchain, &index, &swapchainPresentSemaphore[index]),
vkQueuePresentKHR(dev.defautQueue, &ref));
}
void DestroySwapchain()
{
for(int i = 0; i < numSwapchainImages; i++)
{
swapchainFbs[i].Destroy();
vkDestroySemaphore(dev.device, swapchainPresentSemaphore[i], NULL);
vkDestroySemaphore(dev.device, swapchainRenderSemaphore[i], NULL);
}
vkDestroySwapchainKHR(dev.device, swapchain, NULL);
vkDestroySurfaceKHR(context.instance,surface,NULL);
}
// todo: NUKE THIS!!!
void updateUniformData() {
static unsigned int frameNum;
// Rotate based on time
long long millis = frameNum++;
float angle = (millis % 400) / 400.0f * glm::radians(360.f);
glm::mat4 modelMatrix = glm::identity<glm::mat4>();
modelMatrix = glm::rotate(modelMatrix, angle, glm::vec3(0, 0, 1));
//((UBO*)uboBuf.mapped)->transformationMatrix = modelMatrix;
modelMatrix = glm::translate(modelMatrix, glm::vec3(0.5f / 3.0f, -0.5f / 3.0f, 0.0f));
// Set up view
auto viewMatrix = glm::lookAt(glm::vec3(1, 1, 1), glm::vec3(0, 0, 0), glm::vec3(0, 0, -1));
// Set up projection
auto projMatrix = glm::perspective(glm::radians(70.f), 800.0f / 600.0f, 0.1f, 10.0f);
((UBO*)uboBuf.mapped)->transformationMatrix = projMatrix * viewMatrix * modelMatrix;
}
void waitFence(int chidx)
{
VK_CHECK_RESULT(vkWaitForFences(dev.device, 1, &chainFences[chidx], VK_TRUE, 100000000000));
}
void Run()
{
#ifndef NDEBUG
#define ENABLE_VALIDATION_LAYERS 1
#else
#define ENABLE_VALIDATION_LAYERS 0
#endif
context.Create("streamingengine", "vulkan-playground-server", ENABLE_VALIDATION_LAYERS);
dev.Create(context.FindPhysicalDevice(), VK_QUEUE_GRAPHICS_BIT);
dev.CreateDevice(context);
int width = 800;
int height = 600;
CreateWindow("demo", width, height, false);
CreateSwapchain(width,height);
dev.CreateAndMap(uboBuf, sizeof(UBO));
graphicsPipeline.Init(dev.device, swapchainFbs[0].render_pass);
descriptorSet = graphicsPipeline.AllocateSingleDescriptorSet();
graphicsPipeline.UpdateDescriptors(descriptorSet, uboBuf.descriptor);
updateUniformData();
Vertex vertices[] = {
{ { -0.5f, -0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
{ { -0.5f, 0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f } },
{ { 0.5f, 0.5f, 0.0f }, { 0.0f, 0.0f, 1.0f } }
};
uint32_t indices[] = { 0, 1, 2 };
dev.CreateBuffer(stagingVert, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, sizeof(vertices), vertices);
dev.CreateBuffer(stagingInd, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, sizeof(indices), indices);
for(int i = 0; i < numSwapchainImages; i++)
{
commandBuffers[i] = dev.CreateCommandBuffer();
//VulkanTexture::SetImageLayout(commandBuffers[i], swapchainImages[i], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
//vkCmdClearColorImage(commandBuffers[i], swapchainImages[i],VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, &color, 1, &range );
swapchainFbs[i].BeginRenderPass(commandBuffers[i]);
swapchainFbs[i].SetViewportAndScissor(commandBuffers[i]);
vkCmdBindDescriptorSets(commandBuffers[i],VK_PIPELINE_BIND_POINT_GRAPHICS,graphicsPipeline.pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS,graphicsPipeline.pipeline);
VkDeviceSize offset = 0;
vkCmdBindVertexBuffers(commandBuffers[i],0, 1, &stagingVert.buffer, &offset);
vkCmdBindIndexBuffer(commandBuffers[i], stagingInd.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(commandBuffers[i], 3, 1, 0, 0, 0);
vkCmdEndRenderPass(commandBuffers[i]);
VulkanTexture::SetImageLayout(commandBuffers[i], swapchainImages[i], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
vkEndCommandBuffer(commandBuffers[i]);
VK_CHECK_RESULT(CallWith(FenceInfo(VK_FENCE_CREATE_SIGNALED_BIT),
vkCreateFence(dev.device, &ref, NULL, &chainFences[i])));
}
SetupFFMpeg(context.instance, dev);
uint32_t idx = 0;
int sem_idx = 0;
while(true)
{
updateUniformData();
sem_idx = (sem_idx + 1) % numSwapchainImages;
waitFence(sem_idx);
idx = AcquireImage(sem_idx);
VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
vkResetFences(dev.device,1, &chainFences[sem_idx]);
CallWith(
SubmitInfo(
commandBuffers[idx],$(waitSemaphoreCount), $(signalSemaphoreCount),
$(pWaitSemaphores) = &swapchainRenderSemaphore[sem_idx],
$(pSignalSemaphores) = &swapchainPresentSemaphore[idx],
$(pWaitDstStageMask) = &waitDstStageMask),
vkQueueSubmit(dev.defautQueue, 1, &ref, chainFences[sem_idx]));
PresentImage(idx);
}
vkDeviceWaitIdle(dev.device);
graphicsPipeline.Destroy();
stagingVert.Destroy();
stagingInd.Destroy();
uboBuf.Destroy();
DestroySwapchain();
DestroyWindow();
dev.Destroy();
context.Destroy();
}
};
int main()
{
GraphicsApplication app;
app.Run();
return EXIT_SUCCESS;
}

View file

@ -388,11 +388,13 @@ struct VulkanDevice
deviceExtensions[deviceExtensionsCount++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
VkPhysicalDeviceFeatures enabled_features = {
.samplerAnisotropy = VK_TRUE,
.samplerAnisotropy = VK_TRUE
};
VkPhysicalDeviceSamplerYcbcrConversionFeatures ycbcrFeat = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, .samplerYcbcrConversion = VK_TRUE};
VkDeviceCreateInfo device_info = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = &ycbcrFeat,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &queue_info,
.enabledLayerCount = ctx.enabledLayersCount,