vulkan-playground/vkplayer.cpp
2024-11-11 11:18:28 +03:00

881 lines
38 KiB
C++

#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>
#define MAX_SWAPCHAIN_IMAGES 8
// workaround for ffmpeg stable api nonsence with incompatible pointers between versions
struct PointerWrap
{
void *ptr;
template <typename T>
PointerWrap(T p):ptr((void*)p){}
template <typename T>
operator T() const
{
return (T)ptr;
}
};
struct Vertex
{
float position[3];
float color[3];
};
struct DecoderImage
{
VkImage image;
VkDeviceMemory image_memory;
// todo: deduplicate shared parameters
VkDeviceSize memory_offset_plane0, memory_offset_plane1;
VkDeviceSize stride0, stride1;
VkImageView image_view;
unsigned char *pMappedData;
};
void CreateSoftwareImage(VkInstance inst, VulkanDevice &dev, DecoderImage &image, const VkSamplerYcbcrConversionInfo &ycbcr_info, VkFormat format, unsigned int width, unsigned int height)
{
CallWith(Image2dInfo(VK_IMAGE_USAGE_SAMPLED_BIT, format, width, height,
$(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);
CallWith($M(VkImageViewCreateInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, &ycbcr_info},
$(image) = image.image, $(viewType) = VK_IMAGE_VIEW_TYPE_2D,
$(format) = format,
$(subresourceRange) = SubresourceRange()),
vkCreateImageView(dev.device, &ref, NULL, &image.image_view));
vkMapMemory(dev.device, image.image_memory, 0, image_size, 0, (void**)&image.pMappedData);
VkCommandBuffer cbuf = dev.CreateCommandBuffer();
VulkanTexture::SetImageLayout(cbuf, image.image, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_GENERAL, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
dev.FlushCommandBuffer(cbuf,dev.defaultQueue);
VkImageSubresource plane = {VK_IMAGE_ASPECT_PLANE_0_BIT};
VkSubresourceLayout layout = {};
vkGetImageSubresourceLayout(dev.device, image.image, &plane, &layout);
image.stride0 = layout.rowPitch;
plane.aspectMask = VK_IMAGE_ASPECT_PLANE_1_BIT;
vkGetImageSubresourceLayout(dev.device, image.image, &plane, &layout);
image.stride1 = layout.rowPitch;
}
#define MAX_DECODER_FRAMES 4
static struct FFmpegDecoder
{
DecoderImage images[MAX_DECODER_FRAMES];
VkSamplerYcbcrConversion ycbcr_sampler_conversion;
VkSampler ycbcr_sampler;
// 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;
}
template <bool p010>
static enum AVPixelFormat my_get_format(struct AVCodecContext *s, const enum AVPixelFormat * fmt)
{
return p010?AV_PIX_FMT_YUV420P10LE:AV_PIX_FMT_YUV420P;
}
template<typename pixel>
void ConvertFrame(AVFrame *frame, const DecoderImage &img)
{
constexpr uint8_t pad = sizeof(pixel) > 1?6:0;
for(int i = 0; i < frame->height; i++)
{
pixel *data = (pixel*)(img.pMappedData + img.stride0 * i);
pixel *src = (pixel*)(frame->data[0] + frame->linesize[0]*i);
for(int j = 0; j < frame->linesize[0]/sizeof(pixel); j++)
data[j] = src[j] << pad;
}
// ffmpeg cannot output 2 planes
for(int i = 0; i < frame->height / 2; i++)
{
pixel *data = (pixel*)(img.pMappedData + img.memory_offset_plane1 + img.stride1*i);
pixel *src1 = (pixel*)(frame->data[1] + frame->linesize[1]*i);
pixel *src2 = (pixel*)(frame->data[2] + frame->linesize[2]*i);
for(int j = 0; j < frame->linesize[1]/sizeof(pixel); j++)
{
data[j*2] = src1[j] << pad;
data[j*2+1] = src2[j] << pad;
}
}
}
template <bool p010>
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, PointerWrap(&decoder), 0);
decoder_ctx = avcodec_alloc_context3(decoder);
decoder_ctx->get_format = my_get_format<p010>;
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++;
if constexpr(p010)
ConvertFrame<uint16_t>(frame,gFF.images[idx & 3]);
else
ConvertFrame<uint8_t>(frame,gFF.images[idx & 3]);
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, unsigned int width, unsigned int height, bool p010)
{
PFN_vkCreateSamplerYcbcrConversion pvkCreateSamplerYcbcrConversion = (PFN_vkCreateSamplerYcbcrConversion)vkGetInstanceProcAddr(inst, "vkCreateSamplerYcbcrConversionKHR");
VkFormat format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
if (p010)
{
VkFormatProperties format_properties;
vkGetPhysicalDeviceFormatProperties(dev.physicalDevice, VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, &format_properties);
// intel does not support 10 bit, causing validation errors (but works!), but 16 bit layout is very similar;
// todo: option to ignore this check
if (format_properties.linearTilingFeatures & VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT)
format = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16;
else
format = VK_FORMAT_G16_B16R16_2PLANE_420_UNORM;
}
CallWith($M(VkSamplerYcbcrConversionCreateInfo{VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO},
$(format) = format,
$(ycbcrModel) = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY,
$(ycbcrRange) = VK_SAMPLER_YCBCR_RANGE_ITU_FULL,
$(xChromaOffset) = VK_CHROMA_LOCATION_MIDPOINT, // zero
$(yChromaOffset) = VK_CHROMA_LOCATION_MIDPOINT,
$(chromaFilter) = VK_FILTER_LINEAR), // zero
pvkCreateSamplerYcbcrConversion(dev.device, &ref, NULL, &gFF.ycbcr_sampler_conversion));
$Sc ycbcr_info = $M(VkSamplerYcbcrConversionInfo{VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO},
$(conversion) = gFF.ycbcr_sampler_conversion);
CallWith($M(VkSamplerCreateInfo{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, &ycbcr_info},
$(magFilter) = VK_FILTER_LINEAR,
$(minFilter) = VK_FILTER_LINEAR,
$(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, &gFF.ycbcr_sampler);
);
for(int i = 0; i < MAX_DECODER_FRAMES; i++)
CreateSoftwareImage(inst, dev, gFF.images[i], ycbcr_info, format, width, height);
pthread_mutex_init(&gFF.mutex, NULL);
pthread_cond_init(&gFF.cond, NULL);
pthread_create(&gFF.thread, NULL, p010?&DecoderThread<true>:&DecoderThread<false>, NULL);
}
#define USE_SAMPLER 1
// decoder outputs frames mapped to random VkImage from decoder pool
// reconstruction source frames match decoder frame index (but not image index)
#define RECONSTRUCTION_SOURCE_FRAMES 4
struct ReconsructionSource
{
VkImage image;
VkDeviceMemory image_memory;
VkImageView image_view;
VkSampler image_sampler;
unsigned char *pMappedData;
VkCommandBuffer copyCmdBuf;
};
static struct
{
ReconsructionSource sources[RECONSTRUCTION_SOURCE_FRAMES];
unsigned int frame_idx;
FILE *reconstructionStream;
VulkanBuffer stagingBuffer;
VulkanDevice *device;
// todo: separate thread
// todo: separate queue family if needed
// todo: staging buffer when linear not supported
} gReconstruction;
void SetupReconstructionImages(VulkanDevice &dev, VkFormat format, unsigned int width, unsigned int height)
{
dev.CreateBuffer(gReconstruction.stagingBuffer, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, ((1024 + 3) >> 2) * ((height + 3) >> 2)*8);
gReconstruction.stagingBuffer.Map();
for(int i = 0; i < RECONSTRUCTION_SOURCE_FRAMES; i++)
{
ReconsructionSource &image = gReconstruction.sources[i];
CallWith(Image2dInfo((USE_SAMPLER?VK_IMAGE_USAGE_SAMPLED_BIT:VK_IMAGE_USAGE_STORAGE_BIT) |VK_IMAGE_USAGE_TRANSFER_DST_BIT , format, width, height,
$(tiling) = VK_IMAGE_TILING_OPTIMAL,
$(initialLayout) = VK_IMAGE_LAYOUT_UNDEFINED ),
vkCreateImage(dev.device, &ref,NULL, &image.image));
VkMemoryRequirements mem_reqs;
vkGetImageMemoryRequirements(dev.device, image.image, &mem_reqs);
VkMemoryAllocateInfo info = AllocateInfo(mem_reqs.size);
dev.GetMemoryType(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, &info.memoryTypeIndex);
vkAllocateMemory(dev.device, &info, NULL, &image.image_memory);
vkMapMemory(dev.device, image.image_memory, 0, mem_reqs.size, 0, (void**)&image.pMappedData);
vkBindImageMemory(dev.device, image.image, image.image_memory, 0);
CallWith($M(VkImageViewCreateInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO},
$(image) = image.image, $(viewType) = VK_IMAGE_VIEW_TYPE_2D,
$(format) = format,
$(subresourceRange) = SubresourceRange()),
vkCreateImageView(dev.device, &ref, NULL, &image.image_view));
#if USE_SAMPLER
CallWith($M(VkSamplerCreateInfo{VK_STRUCTURE_TYPE_SAMPLER_CREATE_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.image_sampler);
);
#endif
VkCommandBuffer cbuf = dev.CreateCommandBuffer();
VulkanTexture::SetImageLayout(cbuf, image.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
dev.FlushCommandBuffer(cbuf,dev.defaultQueue);
image.copyCmdBuf = dev.CreateCommandBuffer();
VulkanTexture::SetImageLayout(image.copyCmdBuf, image.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
VkBufferImageCopy cp = VkBufferImageCopy {
.bufferOffset = 0,
.bufferRowLength = 1024,
.imageSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageExtent = {
.width = width,
.height = height,
.depth = 1,
},
};
vkCmdCopyBufferToImage(image.copyCmdBuf, gReconstruction.stagingBuffer.buffer, image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &cp);
VulkanTexture::SetImageLayout(image.copyCmdBuf, image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
vkEndCommandBuffer(image.copyCmdBuf);
}
gReconstruction.reconstructionStream = fopen("reconstruction.bin", "rb");
gReconstruction.device = &dev;
}
//#include <lz4.h>
#include "xpack/libxpack.h"
static xpack_decompressor *dec;
int ReadReconstructionFrame()
{
if(!dec)
dec = xpack_alloc_decompressor();
uint32_t length = 0;
ReconsructionSource &image = gReconstruction.sources[++gReconstruction.frame_idx & 3];
fread(&length, sizeof(int), 1, gReconstruction.reconstructionStream);
#if 1
#if 1
static char buffer[((1024 + 3) >> 2) * ((540 + 3) >> 2)*8+16];
fread(buffer, 1, length, gReconstruction.reconstructionStream);
//LZ4_decompress_safe(buffer,(char*)gReconstruction.stagingBuffer.mapped, length, ((1024 + 3) >> 2) * ((540 + 3) >> 2)*8);
size_t length2;
xpack_decompress(dec, buffer, length, gReconstruction.stagingBuffer.mapped, ((1024 + 3) >> 2) * ((540 + 3) >> 2)*8+16, &length2);
#else
fread(gReconstruction.stagingBuffer.mapped, 1, length, gReconstruction.reconstructionStream);
#endif
CallWith(SubmitInfo(
image.copyCmdBuf),
vkQueueSubmit(gReconstruction.device->defaultQueue, 1, &ref, NULL));
#else
fread(image.pMappedData, 1, length, gReconstruction.reconstructionStream);
#endif
return gReconstruction.frame_idx;
}
// if separate reconstruction disabled, each decoder/reconstruction source combo generates command buffer for each swapchain image
// separate reconstruction:
// reconstruction target frame index always match reconstruction source frames
#define RECONSTRUCTION_TARGET_FRAMES 4
// each reconstruction target builds command buffer for each decoder image
// foveation pipeline builds command buffer for each swapchain image/reprojection targer pair
// reconstruction
// sample one decoder pixel and one reconstruction pixel
// map decoder pixels to 0-511 (or 0-2) and reconstruction to -256-255 (-1-1) (divide by two?)
// graphics
// depending on even/noteven coordinates, add or substract pixels and write to output
struct GraphicsApplicationPipeline: BaseVulkanPipeline
{
void Init(VkDevice dev, VkRenderPass renderPass, VkSampler *pImmutableSamplers, int count)
{
device = dev;
CreateDescriptorSetLayout(
//BasicBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,1,VK_SHADER_STAGE_VERTEX_BIT)
BasicBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,1,VK_SHADER_STAGE_FRAGMENT_BIT, pImmutableSamplers)
);
CreatePool(count,
//BasicPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1)
BasicPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, count)
);
VkShaderModule vp, fp;
CreateGraphicsPipeline(renderPass,
Stages(
ShaderFromFile(vp, "quad.vert.spv", VK_SHADER_STAGE_VERTEX_BIT),
ShaderFromFile(fp, "quad.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT)),
VertexBindings(),
VertexAttributes(),
DynamicStates(
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
),
DepthStencil(true, true),
AssemblyTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN)
//RasterMode(VK_FRONT_FACE_CLOCKWISE)
);
vkDestroyShaderModule(device, vp, NULL);
vkDestroyShaderModule(device, fp, NULL);
}
void UpdateDescriptors(VkDescriptorSet dstSet, VkImageView imageView, VkSampler sampler)
{
WriteDescriptors(
//BufferWrite(dstSet, 0, buffer)
ImageWrite(dstSet, 0, ImageDescriptor(imageView, VK_IMAGE_LAYOUT_GENERAL, sampler), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
);
}
};
// compute
// write 4 output pixels for each source pair
struct ComputeApplicationPipeline: BaseVulkanPipeline
{
void Init(VkDevice dev, VkRenderPass renderPass, VkSampler *pImmutableSamplers, int count1, int count2, int count3, unsigned int width, unsigned int height, unsigned int fwidth, unsigned int fheight)
{
device = dev;
CreateDescriptorSetLayout(
BasicBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,1,VK_SHADER_STAGE_COMPUTE_BIT, pImmutableSamplers),
#if USE_SAMPLER
BasicBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,1,VK_SHADER_STAGE_COMPUTE_BIT),
#else
BasicBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,1,VK_SHADER_STAGE_COMPUTE_BIT),
#endif
BasicBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,1,VK_SHADER_STAGE_COMPUTE_BIT)
);
CreatePool((count1 + count3) * count2,
BasicPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, count2 * count3*3),
#if USE_SAMPLER
// BasicPoolSize(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, count3 * count2 * count1),
BasicPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, count3 * count2 * count1)
#else
BasicPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, count3 * count2 * count1*2)
#endif
);
// todo: wrapper for specialization arrays...
float specData[2] = {(float)width,(float)height};
VkSpecializationMapEntry specs[2] = {{0, 0, sizeof(float)},1,sizeof(float),sizeof(float)};
VkSpecializationInfo sinfo = {2, specs, sizeof(specData), specData };
VkShaderModule shader;
// todo: combined reconstruction/foveation shader? (maybe ineffective for compute pipeline)
CreateComputePipeline(ShaderFromFile(shader, "reconstruction.comp.spv", VK_SHADER_STAGE_COMPUTE_BIT, &sinfo));
// todo: should not we destroy shader internally?
vkDestroyShaderModule(device, shader, NULL);
}
void UpdateDescriptors(VkDescriptorSet dstSet, VkImageView imageView, VkSampler sampler, VkImageView imageView1, VkImageView imageView2, VkSampler sampler2)
{
WriteDescriptors(
ImageWrite(dstSet, 0, ImageDescriptor(imageView, VK_IMAGE_LAYOUT_GENERAL, sampler), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER),
ImageWrite(dstSet, 1, ImageDescriptor(imageView1, VK_IMAGE_LAYOUT_GENERAL, sampler2), USE_SAMPLER?VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:VK_DESCRIPTOR_TYPE_STORAGE_IMAGE),
ImageWrite(dstSet, 2, ImageDescriptor(imageView2, VK_IMAGE_LAYOUT_GENERAL)));
}
};
// todo: how should be compute defoveation done (input/output resolution?)
struct GraphicsApplication
{
VulkanContext context;
VulkanDevice dev;
GLFWwindow *window = NULL;
// swapchain stuff
VkSwapchainKHR swapchain;
VkSurfaceKHR surface;
VkImage swapchainImages[MAX_SWAPCHAIN_IMAGES];
unsigned int numSwapchainImages;
VulkanFramebuffer swapchainFbs[MAX_SWAPCHAIN_IMAGES];
VkFence chainFences[MAX_SWAPCHAIN_IMAGES];
VkSemaphore swapchainRenderSemaphore[MAX_SWAPCHAIN_IMAGES], swapchainPresentSemaphore[MAX_SWAPCHAIN_IMAGES];
// direct reconstruction to swapchain
VkCommandBuffer commandBuffers[MAX_SWAPCHAIN_IMAGES * MAX_DECODER_FRAMES];
// reconstruction to temporary framebuffer
VulkanFramebuffer reconstructionFbs[RECONSTRUCTION_TARGET_FRAMES];
VkImage reconstructionImages[RECONSTRUCTION_TARGET_FRAMES];
VkDeviceMemory reconstructionImagesMem[RECONSTRUCTION_TARGET_FRAMES];
VkCommandBuffer reconstructionCommandBuffers[MAX_DECODER_FRAMES * RECONSTRUCTION_TARGET_FRAMES];
VkCommandBuffer presentCommandBuffers[MAX_SWAPCHAIN_IMAGES * RECONSTRUCTION_TARGET_FRAMES];
VkSemaphore reconstructionSemaphore[RECONSTRUCTION_TARGET_FRAMES];
struct UBO
{
};
GraphicsApplicationPipeline graphicsPipeline;
ComputeApplicationPipeline computePipeline;
VkDescriptorSet descriptorSet;
VkDescriptorSet swapchainDescriptorSets[MAX_SWAPCHAIN_IMAGES*MAX_DECODER_FRAMES];
VkDescriptorSet reconstructionDescriptorSets[RECONSTRUCTION_TARGET_FRAMES*MAX_DECODER_FRAMES];
VkDescriptorSet decodeDescriptorSet[MAX_DECODER_FRAMES];
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, bool graphics)
{
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_TRANSFER_DST_BIT | (graphics?VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT:VK_IMAGE_USAGE_STORAGE_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);
if(graphics)
{
swapchainFbs[i].CreateDepthAttachment(dev, VK_FORMAT_D32_SFLOAT, width, height);
swapchainFbs[i].Init(swapchainImages[i], VK_FORMAT_B8G8R8A8_UNORM, width, height);
}
else
{
CallWith($M(VkImageViewCreateInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO},
$(image) = swapchainImages[i], $(viewType) = VK_IMAGE_VIEW_TYPE_2D,
$(format) = VK_FORMAT_B8G8R8A8_UNORM,
$(subresourceRange) = SubresourceRange()),
vkCreateImageView(dev.device, &ref, NULL, &swapchainFbs[i].color_view));
}
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.defaultQueue, &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);
}
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
bool compute = true;
bool separateReconstruction = true;
context.Create("streamingengine", "vulkan-playground-server", ENABLE_VALIDATION_LAYERS);
dev.Create(context.FindPhysicalDevice(), compute?VK_QUEUE_COMPUTE_BIT : VK_QUEUE_GRAPHICS_BIT);
dev.CreateDevice(context);
int width = 1920;
int height = 1080;
// todo: reconstruction frames source resolution may be lower (another way doing foveated rendering)
// this might be useful, because foveated stream high-frequency part is much detailed
// on edges and may increase bandwidth to much
int rwidth_out = 1920; // fov
int rheight_out = 1080; // fov
int rwidth_in = 1920/2;
int rheight_in = 1080/2;
SetupReconstructionImages(dev, VK_FORMAT_BC1_RGB_UNORM_BLOCK, rwidth_in, rheight_in);
CreateWindow("demo", width, height, false);
CreateSwapchain(width,height, !compute);
printf("%d swapchain images\n", numSwapchainImages);
dev.CreateAndMap(uboBuf, sizeof(UBO));
SetupFFMpeg(context.instance, dev, rwidth_in, rheight_in, true);
for(int i = 0; i < RECONSTRUCTION_TARGET_FRAMES; i++)
{
VkSemaphoreCreateInfo semaphoreCreateInfo = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
VK_CHECK_RESULT(vkCreateSemaphore(dev.device, &semaphoreCreateInfo, nullptr, &reconstructionSemaphore[i]));
CallWith(Image2dInfo(VK_IMAGE_USAGE_STORAGE_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
VK_FORMAT_B8G8R8A8_UNORM, rwidth_out, rheight_out),
vkCreateImage(dev.device, &ref,NULL, &reconstructionImages[i]));
VkMemoryRequirements mem_reqs;
vkGetImageMemoryRequirements(dev.device, reconstructionImages[i], &mem_reqs);
VkMemoryAllocateInfo mem_alloc = AllocateInfo(mem_reqs.size);
if (!dev.GetMemoryType(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&mem_alloc.memoryTypeIndex))
printf("Could not find memory type.\n");
VK_CHECK_RESULT(vkAllocateMemory(dev.device, &mem_alloc, NULL, &reconstructionImagesMem[i]));
VK_CHECK_RESULT(vkBindImageMemory(dev.device, reconstructionImages[i], reconstructionImagesMem[i], 0));
}
if(compute)
{
computePipeline.Init(dev.device, swapchainFbs[0].render_pass, &gFF.ycbcr_sampler,
numSwapchainImages, MAX_DECODER_FRAMES, RECONSTRUCTION_TARGET_FRAMES,
rwidth_in, rheight_in, width, height);
for(int i = 0; i < numSwapchainImages; i++)
{
for(int j = 0; j < MAX_DECODER_FRAMES; j++)
{
int ci = MAX_DECODER_FRAMES * i + j;
swapchainDescriptorSets[ci] = computePipeline.AllocateSingleDescriptorSet();
// todo: reconstruction sources are incorrect here!
computePipeline.UpdateDescriptors(swapchainDescriptorSets[ci], gFF.images[j].image_view, gFF.ycbcr_sampler, gReconstruction.sources[i].image_view, swapchainFbs[i].color_view, gReconstruction.sources[i].image_sampler);
}
}
for(int i = 0; i < RECONSTRUCTION_TARGET_FRAMES; i++)
{
CallWith($M(VkImageViewCreateInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO},
$(image) = reconstructionImages[i], $(viewType) = VK_IMAGE_VIEW_TYPE_2D,
$(format) = VK_FORMAT_B8G8R8A8_UNORM,
$(subresourceRange) = SubresourceRange()),
vkCreateImageView(dev.device, &ref, NULL, &reconstructionFbs[i].color_view));
for(int j = 0; j < MAX_DECODER_FRAMES; j++)
{
int ci = MAX_DECODER_FRAMES * i + j;
reconstructionDescriptorSets[ci] = computePipeline.AllocateSingleDescriptorSet();
computePipeline.UpdateDescriptors(reconstructionDescriptorSets[ci], gFF.images[j].image_view, gFF.ycbcr_sampler, gReconstruction.sources[i].image_view, reconstructionFbs[i].color_view, gReconstruction.sources[i].image_sampler);
}
}
}
else
{
graphicsPipeline.Init(dev.device, swapchainFbs[0].render_pass, &gFF.ycbcr_sampler, MAX_DECODER_FRAMES);
for(int j = 0; j < MAX_DECODER_FRAMES; j++)
{
decodeDescriptorSet[j] = graphicsPipeline.AllocateSingleDescriptorSet();
graphicsPipeline.UpdateDescriptors(decodeDescriptorSet[j], gFF.images[j].image_view, gFF.ycbcr_sampler);
}
for(int i = 0; i < RECONSTRUCTION_TARGET_FRAMES; i++)
{
reconstructionFbs[i].Create(dev.device);
reconstructionFbs[i].CreateDepthAttachment(dev,VK_FORMAT_D32_SFLOAT, rwidth_out, rheight_out);
reconstructionFbs[i].Init(reconstructionImages[i],VK_FORMAT_B8G8R8A8_UNORM, rwidth_out, rheight_out);
}
}
for(int i = 0; i < numSwapchainImages; i++)
{
for(int j = 0; j < MAX_DECODER_FRAMES; j++)
{
int ci = MAX_DECODER_FRAMES * i + j;
commandBuffers[ci] = 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 );
if(!compute)
{
swapchainFbs[i].BeginRenderPass(commandBuffers[ci]);
swapchainFbs[i].SetViewportAndScissor(commandBuffers[ci]);
vkCmdBindDescriptorSets(commandBuffers[ci],VK_PIPELINE_BIND_POINT_GRAPHICS,graphicsPipeline.pipelineLayout, 0, 1, &decodeDescriptorSet[j], 0, nullptr);
vkCmdBindPipeline(commandBuffers[ci], 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);
vkCmdDraw(commandBuffers[ci], 4, 1, 0, 0);
vkCmdEndRenderPass(commandBuffers[ci]);
VulkanTexture::SetImageLayout(commandBuffers[ci], swapchainImages[i], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
}
else
{
const int WIDTH = rwidth_in;
const int HEIGHT = rheight_in;
const int WORKGROUP_SIZE = 32; // Workgroup size in compute shader.
VulkanTexture::SetImageLayout(commandBuffers[ci], swapchainImages[i], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
vkCmdBindPipeline(commandBuffers[ci], VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline.pipeline);
vkCmdBindDescriptorSets(commandBuffers[ci], VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline.pipelineLayout, 0, 1, &swapchainDescriptorSets[ci], 0, NULL);
vkCmdDispatch(commandBuffers[ci], (uint32_t)ceil(WIDTH / float(WORKGROUP_SIZE)), (uint32_t)ceil(HEIGHT / float(WORKGROUP_SIZE)), 1);
VulkanTexture::SetImageLayout(commandBuffers[ci], swapchainImages[i], VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
}
vkEndCommandBuffer(commandBuffers[ci]);
}
VK_CHECK_RESULT(CallWith(FenceInfo(VK_FENCE_CREATE_SIGNALED_BIT),
vkCreateFence(dev.device, &ref, NULL, &chainFences[i])));
}
for(int i = 0; i < numSwapchainImages; i++)
{
for(int j = 0; j < RECONSTRUCTION_TARGET_FRAMES; j++)
{
int ci = RECONSTRUCTION_TARGET_FRAMES*i+j;
presentCommandBuffers[ci] = dev.CreateCommandBuffer();
//VulkanTexture::SetImageLayout(presentCommandBuffers[ci], reconstructionImages[j], VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
VulkanTexture::SetImageLayout(presentCommandBuffers[ci], swapchainImages[i], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
VkImageCopy regions = $M(VkImageCopy{}, $(srcSubresource) = SubresourceLayers(), $(dstSubresource) = SubresourceLayers(), $(extent) = VkExtent3D{(unsigned int)rwidth_out, (unsigned int)rheight_out, 1});
vkCmdCopyImage(presentCommandBuffers[ci], reconstructionImages[j],VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, swapchainImages[i], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &regions);
VulkanTexture::SetImageLayout(presentCommandBuffers[ci], swapchainImages[i], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
vkEndCommandBuffer(presentCommandBuffers[ci]);
}
}
for(int i = 0; i < RECONSTRUCTION_TARGET_FRAMES; i++)
{
for(int j = 0; j < MAX_DECODER_FRAMES; j++)
{
int ci = RECONSTRUCTION_TARGET_FRAMES*i+j;
reconstructionCommandBuffers[ci] = dev.CreateCommandBuffer();
if(!compute)
{
VulkanTexture::SetImageLayout(reconstructionCommandBuffers[ci], reconstructionImages[i], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
reconstructionFbs[i].BeginRenderPass(reconstructionCommandBuffers[ci]);
reconstructionFbs[i].SetViewportAndScissor(reconstructionCommandBuffers[ci]);
vkCmdBindDescriptorSets(reconstructionCommandBuffers[ci],VK_PIPELINE_BIND_POINT_GRAPHICS,graphicsPipeline.pipelineLayout, 0, 1, &decodeDescriptorSet[j], 0, nullptr);
vkCmdBindPipeline(reconstructionCommandBuffers[ci], 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);
vkCmdDraw(reconstructionCommandBuffers[ci], 4, 1, 0, 0);
vkCmdEndRenderPass(reconstructionCommandBuffers[ci]);
VulkanTexture::SetImageLayout(reconstructionCommandBuffers[ci], reconstructionImages[i], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
}
else
{
const int WIDTH = rwidth_in;
const int HEIGHT = rheight_in;
const int WORKGROUP_SIZE = 32; // Workgroup size in compute shader.
//VulkanTexture::SetImageLayout(reconstructionCommandBuffers[ci], swapchainImages[i], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
VulkanTexture::SetImageLayout(reconstructionCommandBuffers[ci], reconstructionImages[i], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
vkCmdBindPipeline(reconstructionCommandBuffers[ci], VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline.pipeline);
vkCmdBindDescriptorSets(reconstructionCommandBuffers[ci], VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline.pipelineLayout, 0, 1, &reconstructionDescriptorSets[ci], 0, NULL);
vkCmdDispatch(reconstructionCommandBuffers[ci], (uint32_t)ceil(WIDTH / float(WORKGROUP_SIZE)), (uint32_t)ceil(HEIGHT / float(WORKGROUP_SIZE)), 1);
VulkanTexture::SetImageLayout(reconstructionCommandBuffers[ci], reconstructionImages[i], VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
}
vkEndCommandBuffer(reconstructionCommandBuffers[ci]);
}
}
uint32_t idx = 0;
int sem_idx = 0;
int ridx = 0;
while(true)
{
sem_idx = (sem_idx + 1) % numSwapchainImages;
waitFence(sem_idx);
idx = AcquireImage(sem_idx);
int decoder_idx = WaitActiveFrame() & 3;
VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
vkResetFences(dev.device,1, &chainFences[sem_idx]);
if(separateReconstruction)
{
ridx = ReadReconstructionFrame() & 3;
CallWith(
SubmitInfo(
reconstructionCommandBuffers[ridx * MAX_DECODER_FRAMES + decoder_idx],$(waitSemaphoreCount), $(signalSemaphoreCount),
$(pWaitSemaphores) = &swapchainRenderSemaphore[sem_idx],
$(pSignalSemaphores) = &reconstructionSemaphore[ridx],
$(pWaitDstStageMask) = &waitDstStageMask),
vkQueueSubmit(dev.defaultQueue, 1, &ref, NULL));
CallWith(
SubmitInfo(
presentCommandBuffers[idx * RECONSTRUCTION_TARGET_FRAMES + ridx],$(waitSemaphoreCount), $(signalSemaphoreCount),
$(pWaitSemaphores) = &reconstructionSemaphore[ridx],
$(pSignalSemaphores) = &swapchainPresentSemaphore[idx],
$(pWaitDstStageMask) = &waitDstStageMask),
vkQueueSubmit(dev.defaultQueue, 1, &ref, chainFences[sem_idx]));
//ridx = (ridx + 1) & 3;
}
else
{
CallWith(
SubmitInfo(
commandBuffers[idx*MAX_DECODER_FRAMES + decoder_idx],$(waitSemaphoreCount), $(signalSemaphoreCount),
$(pWaitSemaphores) = &swapchainRenderSemaphore[sem_idx],
$(pSignalSemaphores) = &swapchainPresentSemaphore[idx],
$(pWaitDstStageMask) = &waitDstStageMask),
vkQueueSubmit(dev.defaultQueue, 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;
}