mirror of
https://github.com/elwrcl/intel-hard-lab.git
synced 2026-10-31 07:48:02 +03:00
first commit
This commit is contained in:
54 files changed
+156785
No files matched your search
Symlink
+1
@@ -0,0 +1 @@
|
||||
/nix/store/jr8wwd19z6yvsfxykva6brazipm06r9b-pocl-7.1/etc/OpenCL/vendors/pocl.icd
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,964 @@
|
||||
/* vulkan H.264 encode test for hasvk.
|
||||
* ./hasvk_encode_test # flat-gray 176x144, dumps /tmp/hasvk_enc_out.h264
|
||||
* HASVK_ENC_GRADIENT=1 ./hasvk_encode_test # luma gradient (real residuals)
|
||||
* HASVK_ENC_GRADIENT=1 HASVK_ENC_CHROMA=1 ./... # + chroma gradient
|
||||
* validate the output with ffmpeg:
|
||||
* ffmpeg -i /tmp/hasvk_enc_out.h264 -f rawvideo -pix_fmt yuv420p out.yuv
|
||||
* The stream prepends the driver's own SPS/PPS
|
||||
* (vkGetEncodedVideoSessionParametersKHR) so it decodes standalone.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vk_video/vulkan_video_codec_h264std.h>
|
||||
#include <vk_video/vulkan_video_codec_h264std_encode.h>
|
||||
|
||||
#define CHECK(x) do { \
|
||||
VkResult _r = (x); \
|
||||
if (_r != VK_SUCCESS) { \
|
||||
fprintf(stderr, "FAIL %s:%d: %s -> %d\n", __FILE__, __LINE__, #x, _r); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define WIDTH 176
|
||||
#define HEIGHT 144
|
||||
#define WIDTH_MBS (WIDTH / 16)
|
||||
#define HEIGHT_MBS (HEIGHT / 16)
|
||||
#define LUMA_SZ (WIDTH * HEIGHT)
|
||||
#define CHROMA_SZ ((WIDTH / 2) * (HEIGHT / 2) * 2)
|
||||
#define FRAME_SZ (LUMA_SZ + CHROMA_SZ)
|
||||
|
||||
static int
|
||||
load_frame(const char *path, int idx, int i420, uint8_t *dst)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) { fprintf(stderr, "cannot open '%s'\n", path); return 0; }
|
||||
if (fseek(f, (long)idx * FRAME_SZ, SEEK_SET) != 0) {
|
||||
fprintf(stderr, "'%s': cannot seek to frame %d\n", path, idx);
|
||||
fclose(f); return 0;
|
||||
}
|
||||
uint8_t buf[FRAME_SZ];
|
||||
size_t n = fread(buf, 1, FRAME_SZ, f);
|
||||
fclose(f);
|
||||
if (n != FRAME_SZ) {
|
||||
fprintf(stderr, "'%s': frame %d short read (%zu of %d)\n", path, idx, n, FRAME_SZ);
|
||||
return 0;
|
||||
}
|
||||
memcpy(dst, buf, LUMA_SZ);
|
||||
if (i420) {
|
||||
const uint8_t *cb = buf + LUMA_SZ, *cr = cb + CHROMA_SZ / 2;
|
||||
for (int i = 0; i < CHROMA_SZ / 2; i++) {
|
||||
dst[LUMA_SZ + i * 2 + 0] = cb[i];
|
||||
dst[LUMA_SZ + i * 2 + 1] = cr[i];
|
||||
}
|
||||
} else {
|
||||
memcpy(dst + LUMA_SZ, buf + LUMA_SZ, CHROMA_SZ);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
shift_frame(const uint8_t *src, uint8_t *dst, int dx, int dy)
|
||||
{
|
||||
for (int y = 0; y < HEIGHT; y++) {
|
||||
int sy = y - dy; sy = sy < 0 ? 0 : (sy >= HEIGHT ? HEIGHT - 1 : sy);
|
||||
for (int x = 0; x < WIDTH; x++) {
|
||||
int sx = x - dx; sx = sx < 0 ? 0 : (sx >= WIDTH ? WIDTH - 1 : sx);
|
||||
dst[y * WIDTH + x] = src[sy * WIDTH + sx];
|
||||
}
|
||||
}
|
||||
int cw = WIDTH / 2, ch = HEIGHT / 2, cdx = dx / 2, cdy = dy / 2;
|
||||
for (int y = 0; y < ch; y++) {
|
||||
int sy = y - cdy; sy = sy < 0 ? 0 : (sy >= ch ? ch - 1 : sy);
|
||||
for (int x = 0; x < cw; x++) {
|
||||
int sx = x - cdx; sx = sx < 0 ? 0 : (sx >= cw ? cw - 1 : sx);
|
||||
dst[LUMA_SZ + (y * cw + x) * 2 + 0] = src[LUMA_SZ + (sy * cw + sx) * 2 + 0];
|
||||
dst[LUMA_SZ + (y * cw + x) * 2 + 1] = src[LUMA_SZ + (sy * cw + sx) * 2 + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static PFN_vkGetPhysicalDeviceVideoCapabilitiesKHR pGetPhysicalDeviceVideoCapabilitiesKHR;
|
||||
static PFN_vkGetPhysicalDeviceVideoFormatPropertiesKHR pGetPhysicalDeviceVideoFormatPropertiesKHR;
|
||||
static PFN_vkCreateVideoSessionKHR pCreateVideoSessionKHR;
|
||||
static PFN_vkDestroyVideoSessionKHR pDestroyVideoSessionKHR;
|
||||
static PFN_vkGetVideoSessionMemoryRequirementsKHR pGetVideoSessionMemoryRequirementsKHR;
|
||||
static PFN_vkBindVideoSessionMemoryKHR pBindVideoSessionMemoryKHR;
|
||||
static PFN_vkCreateVideoSessionParametersKHR pCreateVideoSessionParametersKHR;
|
||||
static PFN_vkDestroyVideoSessionParametersKHR pDestroyVideoSessionParametersKHR;
|
||||
static PFN_vkGetEncodedVideoSessionParametersKHR pGetEncodedVideoSessionParametersKHR;
|
||||
static PFN_vkCmdBeginVideoCodingKHR pCmdBeginVideoCodingKHR;
|
||||
static PFN_vkCmdEndVideoCodingKHR pCmdEndVideoCodingKHR;
|
||||
static PFN_vkCmdEncodeVideoKHR pCmdEncodeVideoKHR;
|
||||
|
||||
static uint32_t
|
||||
find_memory_type(VkPhysicalDeviceMemoryProperties *mp, uint32_t type_bits, VkMemoryPropertyFlags want)
|
||||
{
|
||||
for (uint32_t i = 0; i < mp->memoryTypeCount; i++) {
|
||||
if ((type_bits & (1u << i)) &&
|
||||
(mp->memoryTypes[i].propertyFlags & want) == want)
|
||||
return i;
|
||||
}
|
||||
/* fall back: any type in the mask */
|
||||
for (uint32_t i = 0; i < mp->memoryTypeCount; i++) {
|
||||
if (type_bits & (1u << i))
|
||||
return i;
|
||||
}
|
||||
fprintf(stderr, "FAIL: no matching memory type for bits 0x%x\n", type_bits);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static VkDeviceMemory
|
||||
alloc_and_bind_buffer(VkDevice dev, VkPhysicalDeviceMemoryProperties *mp,
|
||||
VkBuffer buf, VkMemoryPropertyFlags want)
|
||||
{
|
||||
VkMemoryRequirements req;
|
||||
vkGetBufferMemoryRequirements(dev, buf, &req);
|
||||
VkMemoryAllocateInfo ai = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = req.size,
|
||||
.memoryTypeIndex = find_memory_type(mp, req.memoryTypeBits, want),
|
||||
};
|
||||
VkDeviceMemory mem;
|
||||
CHECK(vkAllocateMemory(dev, &ai, NULL, &mem));
|
||||
CHECK(vkBindBufferMemory(dev, buf, mem, 0));
|
||||
return mem;
|
||||
}
|
||||
|
||||
static VkDeviceMemory
|
||||
alloc_and_bind_image(VkDevice dev, VkPhysicalDeviceMemoryProperties *mp, VkImage img)
|
||||
{
|
||||
VkMemoryRequirements req;
|
||||
vkGetImageMemoryRequirements(dev, img, &req);
|
||||
VkMemoryAllocateInfo ai = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = req.size,
|
||||
.memoryTypeIndex = find_memory_type(mp, req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
|
||||
};
|
||||
VkDeviceMemory mem;
|
||||
CHECK(vkAllocateMemory(dev, &ai, NULL, &mem));
|
||||
CHECK(vkBindImageMemory(dev, img, mem, 0));
|
||||
return mem;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
/* instance */
|
||||
VkApplicationInfo app = {
|
||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
.apiVersion = VK_API_VERSION_1_3,
|
||||
};
|
||||
VkInstanceCreateInfo ici = {
|
||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
.pApplicationInfo = &app,
|
||||
};
|
||||
VkInstance instance;
|
||||
CHECK(vkCreateInstance(&ici, NULL, &instance));
|
||||
|
||||
uint32_t pdev_count = 0;
|
||||
CHECK(vkEnumeratePhysicalDevices(instance, &pdev_count, NULL));
|
||||
if (pdev_count == 0) { fprintf(stderr, "no physical devices\n"); return 1; }
|
||||
VkPhysicalDevice *pdevs = calloc(pdev_count, sizeof(*pdevs));
|
||||
CHECK(vkEnumeratePhysicalDevices(instance, &pdev_count, pdevs));
|
||||
VkPhysicalDevice pdev = pdevs[0];
|
||||
|
||||
VkPhysicalDeviceProperties pprops;
|
||||
vkGetPhysicalDeviceProperties(pdev, &pprops);
|
||||
printf("Device: %s\n", pprops.deviceName);
|
||||
|
||||
VkPhysicalDeviceMemoryProperties mem_props;
|
||||
vkGetPhysicalDeviceMemoryProperties(pdev, &mem_props);
|
||||
|
||||
/* find encode queue family */
|
||||
uint32_t qf_count = 0;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties2(pdev, &qf_count, NULL);
|
||||
VkQueueFamilyVideoPropertiesKHR *video_props = calloc(qf_count, sizeof(*video_props));
|
||||
VkQueueFamilyProperties2 *qfs = calloc(qf_count, sizeof(*qfs));
|
||||
for (uint32_t i = 0; i < qf_count; i++) {
|
||||
video_props[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_VIDEO_PROPERTIES_KHR;
|
||||
qfs[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2;
|
||||
qfs[i].pNext = &video_props[i];
|
||||
}
|
||||
vkGetPhysicalDeviceQueueFamilyProperties2(pdev, &qf_count, qfs);
|
||||
|
||||
int32_t encode_qf = -1;
|
||||
int32_t transfer_qf = -1;
|
||||
for (uint32_t i = 0; i < qf_count; i++) {
|
||||
if ((qfs[i].queueFamilyProperties.queueFlags & VK_QUEUE_VIDEO_ENCODE_BIT_KHR) &&
|
||||
(video_props[i].videoCodecOperations & VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR)) {
|
||||
encode_qf = (int32_t)i;
|
||||
}
|
||||
if ((qfs[i].queueFamilyProperties.queueFlags & VK_QUEUE_TRANSFER_BIT) && transfer_qf < 0)
|
||||
transfer_qf = (int32_t)i;
|
||||
}
|
||||
if (encode_qf < 0) { fprintf(stderr, "no H.264 encode queue family found\n"); return 1; }
|
||||
if (transfer_qf < 0) { fprintf(stderr, "no transfer-capable queue family found\n"); return 1; }
|
||||
printf("encode queue family = %d, transfer queue family = %d\n", encode_qf, transfer_qf);
|
||||
|
||||
/* ---- Device ---- */
|
||||
const char *dev_exts[] = {
|
||||
"VK_KHR_video_queue",
|
||||
"VK_KHR_video_encode_queue",
|
||||
"VK_KHR_video_encode_h264",
|
||||
"VK_KHR_synchronization2",
|
||||
};
|
||||
float qprio = 1.0f;
|
||||
VkDeviceQueueCreateInfo qcis[2] = {
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
.queueFamilyIndex = (uint32_t)transfer_qf,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &qprio,
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
.queueFamilyIndex = (uint32_t)encode_qf,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &qprio,
|
||||
},
|
||||
};
|
||||
VkDeviceCreateInfo dci = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
.queueCreateInfoCount = (encode_qf == transfer_qf) ? 1 : 2,
|
||||
.pQueueCreateInfos = qcis,
|
||||
.enabledExtensionCount = sizeof(dev_exts) / sizeof(dev_exts[0]),
|
||||
.ppEnabledExtensionNames = dev_exts,
|
||||
};
|
||||
VkDevice dev;
|
||||
CHECK(vkCreateDevice(pdev, &dci, NULL, &dev));
|
||||
|
||||
#define LOAD(name) p##name = (PFN_vk##name)vkGetDeviceProcAddr(dev, "vk" #name)
|
||||
LOAD(GetPhysicalDeviceVideoCapabilitiesKHR);
|
||||
LOAD(GetPhysicalDeviceVideoFormatPropertiesKHR);
|
||||
LOAD(CreateVideoSessionKHR);
|
||||
LOAD(DestroyVideoSessionKHR);
|
||||
LOAD(GetVideoSessionMemoryRequirementsKHR);
|
||||
LOAD(BindVideoSessionMemoryKHR);
|
||||
LOAD(CreateVideoSessionParametersKHR);
|
||||
LOAD(DestroyVideoSessionParametersKHR);
|
||||
LOAD(GetEncodedVideoSessionParametersKHR);
|
||||
LOAD(CmdBeginVideoCodingKHR);
|
||||
LOAD(CmdEndVideoCodingKHR);
|
||||
LOAD(CmdEncodeVideoKHR);
|
||||
pGetPhysicalDeviceVideoCapabilitiesKHR =
|
||||
(PFN_vkGetPhysicalDeviceVideoCapabilitiesKHR)vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceVideoCapabilitiesKHR");
|
||||
pGetPhysicalDeviceVideoFormatPropertiesKHR =
|
||||
(PFN_vkGetPhysicalDeviceVideoFormatPropertiesKHR)vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceVideoFormatPropertiesKHR");
|
||||
#undef LOAD
|
||||
|
||||
VkQueue queue;
|
||||
vkGetDeviceQueue(dev, (uint32_t)encode_qf, 0, &queue);
|
||||
VkQueue transfer_queue;
|
||||
vkGetDeviceQueue(dev, (uint32_t)transfer_qf, 0, &transfer_queue);
|
||||
|
||||
int use_cabac = getenv("HASVK_ENC_CABAC") ? 1 : 0;
|
||||
int use_pframe = getenv("HASVK_ENC_PFRAME") ? 1 : 0;
|
||||
|
||||
/* H.264 encode, 4:2:0, 8-bit */
|
||||
VkVideoEncodeH264ProfileInfoKHR h264_profile = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PROFILE_INFO_KHR,
|
||||
.stdProfileIdc = use_cabac ? STD_VIDEO_H264_PROFILE_IDC_MAIN
|
||||
: STD_VIDEO_H264_PROFILE_IDC_BASELINE,
|
||||
};
|
||||
VkVideoProfileInfoKHR profile = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_INFO_KHR,
|
||||
.pNext = &h264_profile,
|
||||
.videoCodecOperation = VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR,
|
||||
.chromaSubsampling = VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR,
|
||||
.lumaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR,
|
||||
.chromaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR,
|
||||
};
|
||||
VkVideoProfileListInfoKHR profile_list = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR,
|
||||
.profileCount = 1,
|
||||
.pProfiles = &profile,
|
||||
};
|
||||
|
||||
/* capability */
|
||||
VkVideoEncodeH264CapabilitiesKHR h264_caps = { .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_CAPABILITIES_KHR };
|
||||
VkVideoEncodeCapabilitiesKHR enc_caps = { .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_CAPABILITIES_KHR, .pNext = &h264_caps };
|
||||
VkVideoCapabilitiesKHR caps = { .sType = VK_STRUCTURE_TYPE_VIDEO_CAPABILITIES_KHR, .pNext = &enc_caps };
|
||||
CHECK(pGetPhysicalDeviceVideoCapabilitiesKHR(pdev, &profile, &caps));
|
||||
printf("caps: minBitstreamBufferOffsetAlignment=%llu minBitstreamBufferSizeAlignment=%llu maxDpbSlots=%u\n",
|
||||
(unsigned long long)caps.minBitstreamBufferOffsetAlignment,
|
||||
(unsigned long long)caps.minBitstreamBufferSizeAlignment,
|
||||
caps.maxDpbSlots);
|
||||
printf("enc caps: supportedEncodeFeedbackFlags=0x%x rateControlModes=0x%x\n",
|
||||
enc_caps.supportedEncodeFeedbackFlags, enc_caps.rateControlModes);
|
||||
|
||||
/* src/dpb images */
|
||||
VkPhysicalDeviceVideoFormatInfoKHR fmt_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_FORMAT_INFO_KHR,
|
||||
.pNext = &profile_list,
|
||||
.imageUsage = VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR,
|
||||
};
|
||||
uint32_t fmt_count = 0;
|
||||
CHECK(pGetPhysicalDeviceVideoFormatPropertiesKHR(pdev, &fmt_info, &fmt_count, NULL));
|
||||
if (fmt_count == 0) { fprintf(stderr, "no video formats reported\n"); return 1; }
|
||||
VkVideoFormatPropertiesKHR *fmts = calloc(fmt_count, sizeof(*fmts));
|
||||
for (uint32_t i = 0; i < fmt_count; i++)
|
||||
fmts[i].sType = VK_STRUCTURE_TYPE_VIDEO_FORMAT_PROPERTIES_KHR;
|
||||
CHECK(pGetPhysicalDeviceVideoFormatPropertiesKHR(pdev, &fmt_info, &fmt_count, fmts));
|
||||
VkFormat pic_format = fmts[0].format;
|
||||
printf("picture format = %d\n", pic_format);
|
||||
|
||||
/* video sessions */
|
||||
VkExtensionProperties std_hdr = {
|
||||
.extensionName = VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_EXTENSION_NAME,
|
||||
.specVersion = VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_SPEC_VERSION,
|
||||
};
|
||||
VkVideoSessionCreateInfoKHR sess_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_CREATE_INFO_KHR,
|
||||
.queueFamilyIndex = (uint32_t)encode_qf,
|
||||
.pVideoProfile = &profile,
|
||||
.pictureFormat = pic_format,
|
||||
.maxCodedExtent = { WIDTH, HEIGHT },
|
||||
.referencePictureFormat = pic_format,
|
||||
.maxDpbSlots = 1,
|
||||
.maxActiveReferencePictures = 0,
|
||||
.pStdHeaderVersion = &std_hdr,
|
||||
};
|
||||
VkVideoSessionKHR session;
|
||||
CHECK(pCreateVideoSessionKHR(dev, &sess_ci, NULL, &session));
|
||||
|
||||
uint32_t req_count = 0;
|
||||
CHECK(pGetVideoSessionMemoryRequirementsKHR(dev, session, &req_count, NULL));
|
||||
printf("video session memory requirement count = %u\n", req_count);
|
||||
VkVideoSessionMemoryRequirementsKHR *reqs = calloc(req_count, sizeof(*reqs));
|
||||
for (uint32_t i = 0; i < req_count; i++)
|
||||
reqs[i].sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_MEMORY_REQUIREMENTS_KHR;
|
||||
CHECK(pGetVideoSessionMemoryRequirementsKHR(dev, session, &req_count, reqs));
|
||||
|
||||
VkDeviceMemory *sess_mems = calloc(req_count, sizeof(*sess_mems));
|
||||
VkBindVideoSessionMemoryInfoKHR *binds = calloc(req_count, sizeof(*binds));
|
||||
for (uint32_t i = 0; i < req_count; i++) {
|
||||
VkMemoryAllocateInfo ai = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = reqs[i].memoryRequirements.size,
|
||||
.memoryTypeIndex = find_memory_type(&mem_props, reqs[i].memoryRequirements.memoryTypeBits,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
|
||||
};
|
||||
CHECK(vkAllocateMemory(dev, &ai, NULL, &sess_mems[i]));
|
||||
binds[i] = (VkBindVideoSessionMemoryInfoKHR) {
|
||||
.sType = VK_STRUCTURE_TYPE_BIND_VIDEO_SESSION_MEMORY_INFO_KHR,
|
||||
.memoryBindIndex = reqs[i].memoryBindIndex,
|
||||
.memory = sess_mems[i],
|
||||
.memoryOffset = 0,
|
||||
.memorySize = reqs[i].memoryRequirements.size,
|
||||
};
|
||||
printf(" bindIndex=%u size=%llu\n", reqs[i].memoryBindIndex,
|
||||
(unsigned long long)reqs[i].memoryRequirements.size);
|
||||
}
|
||||
CHECK(pBindVideoSessionMemoryKHR(dev, session, req_count, binds));
|
||||
|
||||
/* SPS / PPS */
|
||||
int crop_r = getenv("HASVK_ENC_CROP_R") ? atoi(getenv("HASVK_ENC_CROP_R")) : 0;
|
||||
int crop_b = getenv("HASVK_ENC_CROP_B") ? atoi(getenv("HASVK_ENC_CROP_B")) : 0;
|
||||
crop_r &= ~1; crop_b &= ~1;
|
||||
|
||||
StdVideoH264SequenceParameterSet sps = {
|
||||
.profile_idc = use_cabac ? STD_VIDEO_H264_PROFILE_IDC_MAIN
|
||||
: STD_VIDEO_H264_PROFILE_IDC_BASELINE,
|
||||
.level_idc = getenv("HASVK_ENC_LEVEL0") ? STD_VIDEO_H264_LEVEL_IDC_1_0
|
||||
: STD_VIDEO_H264_LEVEL_IDC_3_0,
|
||||
.chroma_format_idc = STD_VIDEO_H264_CHROMA_FORMAT_IDC_420,
|
||||
.seq_parameter_set_id = 0,
|
||||
.log2_max_frame_num_minus4 = getenv("HASVK_ENC_LOG2MAXFN")
|
||||
? atoi(getenv("HASVK_ENC_LOG2MAXFN")) : 0,
|
||||
.pic_order_cnt_type = getenv("HASVK_ENC_POC2") ? STD_VIDEO_H264_POC_TYPE_2
|
||||
: STD_VIDEO_H264_POC_TYPE_0,
|
||||
.log2_max_pic_order_cnt_lsb_minus4 = 0,
|
||||
.max_num_ref_frames = 0,
|
||||
.pic_width_in_mbs_minus1 = WIDTH_MBS - 1,
|
||||
.pic_height_in_map_units_minus1 = HEIGHT_MBS - 1,
|
||||
.frame_crop_right_offset = (uint32_t)(crop_r / 2),
|
||||
.frame_crop_bottom_offset = (uint32_t)(crop_b / 2),
|
||||
};
|
||||
sps.flags.frame_mbs_only_flag = 1;
|
||||
sps.flags.direct_8x8_inference_flag = 1;
|
||||
sps.flags.frame_cropping_flag = (crop_r || crop_b) ? 1 : 0;
|
||||
|
||||
StdVideoH264PictureParameterSet pps = {
|
||||
.seq_parameter_set_id = 0,
|
||||
.pic_parameter_set_id = 0,
|
||||
.num_ref_idx_l0_default_active_minus1 = 0,
|
||||
.num_ref_idx_l1_default_active_minus1 = 0,
|
||||
.weighted_bipred_idc = STD_VIDEO_H264_WEIGHTED_BIPRED_IDC_DEFAULT,
|
||||
.pic_init_qp_minus26 = 0, /* pic_init_qp = 26 */
|
||||
.pic_init_qs_minus26 = 0,
|
||||
.chroma_qp_index_offset = 0,
|
||||
.second_chroma_qp_index_offset = 0,
|
||||
};
|
||||
pps.flags.deblocking_filter_control_present_flag =
|
||||
getenv("HASVK_ENC_NODEBLOCKCTRL") ? 0 : 1;
|
||||
pps.flags.entropy_coding_mode_flag = use_cabac ? 1 : 0;
|
||||
|
||||
VkVideoEncodeH264SessionParametersAddInfoKHR add_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_ADD_INFO_KHR,
|
||||
.stdSPSCount = 1,
|
||||
.pStdSPSs = &sps,
|
||||
.stdPPSCount = 1,
|
||||
.pStdPPSs = &pps,
|
||||
};
|
||||
VkVideoEncodeH264SessionParametersCreateInfoKHR h264_params_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_CREATE_INFO_KHR,
|
||||
.maxStdSPSCount = 1,
|
||||
.maxStdPPSCount = 1,
|
||||
.pParametersAddInfo = &add_info,
|
||||
};
|
||||
VkVideoSessionParametersCreateInfoKHR params_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR,
|
||||
.pNext = &h264_params_ci,
|
||||
.videoSession = session,
|
||||
};
|
||||
VkVideoSessionParametersKHR params;
|
||||
CHECK(pCreateVideoSessionParametersKHR(dev, ¶ms_ci, NULL, ¶ms));
|
||||
|
||||
static uint8_t hdr_buf[512];
|
||||
size_t hdr_size = 0;
|
||||
{
|
||||
VkVideoEncodeH264SessionParametersGetInfoKHR h264_get = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_GET_INFO_KHR,
|
||||
.writeStdSPS = VK_TRUE,
|
||||
.stdSPSId = 0,
|
||||
.writeStdPPS = VK_TRUE,
|
||||
.stdPPSId = 0,
|
||||
};
|
||||
VkVideoEncodeSessionParametersGetInfoKHR get_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_SESSION_PARAMETERS_GET_INFO_KHR,
|
||||
.pNext = &h264_get,
|
||||
.videoSessionParameters = params,
|
||||
};
|
||||
size_t data_size = 0;
|
||||
VkResult gr = pGetEncodedVideoSessionParametersKHR(dev, &get_info, NULL,
|
||||
&data_size, NULL);
|
||||
printf("GetEncoded SPS+PPS: result=%d size=%zu\n", gr, data_size);
|
||||
if (gr == VK_SUCCESS && data_size > 0 && data_size <= sizeof(hdr_buf)) {
|
||||
gr = pGetEncodedVideoSessionParametersKHR(dev, &get_info, NULL,
|
||||
&data_size, hdr_buf);
|
||||
if (gr == VK_SUCCESS) hdr_size = data_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* source + recon/DPB */
|
||||
VkImageCreateInfo img_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.pNext = &profile_list,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = pic_format,
|
||||
.extent = { WIDTH, HEIGHT, 1 },
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
};
|
||||
VkImage src_image;
|
||||
CHECK(vkCreateImage(dev, &img_ci, NULL, &src_image));
|
||||
VkDeviceMemory src_image_mem = alloc_and_bind_image(dev, &mem_props, src_image);
|
||||
|
||||
VkImageCreateInfo dpb_ci = img_ci;
|
||||
dpb_ci.usage = VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR;
|
||||
VkImage dpb_image;
|
||||
CHECK(vkCreateImage(dev, &dpb_ci, NULL, &dpb_image));
|
||||
VkDeviceMemory dpb_image_mem = alloc_and_bind_image(dev, &mem_props, dpb_image);
|
||||
|
||||
VkImageViewCreateInfo src_view_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = src_image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = pic_format,
|
||||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 },
|
||||
};
|
||||
VkImageView src_view;
|
||||
CHECK(vkCreateImageView(dev, &src_view_ci, NULL, &src_view));
|
||||
|
||||
VkImageViewCreateInfo dpb_view_ci = src_view_ci;
|
||||
dpb_view_ci.image = dpb_image;
|
||||
VkImageView dpb_view;
|
||||
CHECK(vkCreateImageView(dev, &dpb_view_ci, NULL, &dpb_view));
|
||||
|
||||
VkImage dpb_image2 = VK_NULL_HANDLE;
|
||||
VkImageView dpb_view2 = VK_NULL_HANDLE;
|
||||
if (use_pframe) {
|
||||
CHECK(vkCreateImage(dev, &dpb_ci, NULL, &dpb_image2));
|
||||
alloc_and_bind_image(dev, &mem_props, dpb_image2);
|
||||
VkImageViewCreateInfo v = dpb_view_ci;
|
||||
v.image = dpb_image2;
|
||||
CHECK(vkCreateImageView(dev, &v, NULL, &dpb_view2));
|
||||
}
|
||||
|
||||
int shift_px = getenv("HASVK_ENC_SHIFT") ? atoi(getenv("HASVK_ENC_SHIFT")) : 0;
|
||||
int src_step = getenv("HASVK_ENC_SRC_STEP") ? atoi(getenv("HASVK_ENC_SRC_STEP")) : 0;
|
||||
int two_src = use_pframe && (shift_px || src_step);
|
||||
VkImage src_image2 = VK_NULL_HANDLE;
|
||||
VkImageView src_view2 = VK_NULL_HANDLE;
|
||||
if (two_src) {
|
||||
CHECK(vkCreateImage(dev, &img_ci, NULL, &src_image2));
|
||||
alloc_and_bind_image(dev, &mem_props, src_image2);
|
||||
VkImageViewCreateInfo v = src_view_ci;
|
||||
v.image = src_image2;
|
||||
CHECK(vkCreateImageView(dev, &v, NULL, &src_view2));
|
||||
}
|
||||
|
||||
/* fill source image with flat gray via staging buffer */
|
||||
VkDeviceSize luma_size = (VkDeviceSize)WIDTH * HEIGHT;
|
||||
VkDeviceSize chroma_size = (VkDeviceSize)(WIDTH / 2) * (HEIGHT / 2) * 2;
|
||||
VkDeviceSize frame_size = luma_size + chroma_size;
|
||||
VkDeviceSize staging_size = frame_size * (two_src ? 2 : 1);
|
||||
VkBufferCreateInfo staging_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.size = staging_size,
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
};
|
||||
VkBuffer staging_buf;
|
||||
CHECK(vkCreateBuffer(dev, &staging_ci, NULL, &staging_buf));
|
||||
VkDeviceMemory staging_mem = alloc_and_bind_buffer(dev, &mem_props, staging_buf,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||
void *staging_map;
|
||||
CHECK(vkMapMemory(dev, staging_mem, 0, staging_size, 0, &staging_map));
|
||||
const char *src_in = getenv("HASVK_ENC_SRC_IN");
|
||||
int src_i420 = getenv("HASVK_ENC_SRC_I420") ? 1 : 0;
|
||||
int src_frame = getenv("HASVK_ENC_SRC_FRAME") ? atoi(getenv("HASVK_ENC_SRC_FRAME")) : 0;
|
||||
if (src_in) {
|
||||
if (!load_frame(src_in, src_frame, src_i420, (uint8_t *)staging_map))
|
||||
return 1;
|
||||
} else if (getenv("HASVK_ENC_GRADIENT")) {
|
||||
uint8_t *sm = (uint8_t *)staging_map;
|
||||
for (uint32_t yy = 0; yy < HEIGHT; yy++)
|
||||
for (uint32_t xx = 0; xx < WIDTH; xx++)
|
||||
sm[yy * WIDTH + xx] = (uint8_t)((xx * 255) / (WIDTH - 1));
|
||||
if (getenv("HASVK_ENC_CHROMA")) {
|
||||
uint8_t *ch = sm + luma_size;
|
||||
uint32_t cw = WIDTH / 2, chh = HEIGHT / 2;
|
||||
for (uint32_t cy = 0; cy < chh; cy++)
|
||||
for (uint32_t cx = 0; cx < cw; cx++) {
|
||||
ch[(cy * cw + cx) * 2 + 0] = (uint8_t)((cx * 255) / (cw - 1));
|
||||
ch[(cy * cw + cx) * 2 + 1] = (uint8_t)((cy * 255) / (chh - 1));
|
||||
}
|
||||
} else {
|
||||
memset(sm + luma_size, 128, (size_t)chroma_size);
|
||||
}
|
||||
} else {
|
||||
memset(staging_map, 128, (size_t)frame_size);
|
||||
}
|
||||
|
||||
if (two_src) {
|
||||
uint8_t *f0 = (uint8_t *)staging_map, *f1 = f0 + frame_size;
|
||||
if (shift_px) {
|
||||
shift_frame(f0, f1, shift_px, 0);
|
||||
} else if (!src_in) {
|
||||
fprintf(stderr, "HASVK_ENC_SRC_STEP needs HASVK_ENC_SRC_IN\n");
|
||||
return 1;
|
||||
} else if (!load_frame(src_in, src_frame + src_step, src_i420, f1)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
const char *src_out = getenv("HASVK_ENC_SRC_OUT");
|
||||
if (src_out) {
|
||||
const uint8_t *sm = (const uint8_t *)staging_map;
|
||||
const uint8_t *ch = sm + luma_size;
|
||||
uint32_t cw = WIDTH / 2, chh = HEIGHT / 2;
|
||||
FILE *sf = fopen(src_out, "wb");
|
||||
if (sf) {
|
||||
fwrite(sm, 1, (size_t)luma_size, sf);
|
||||
for (uint32_t i = 0; i < cw * chh; i++)
|
||||
fputc(ch[i * 2 + 0], sf);
|
||||
for (uint32_t i = 0; i < cw * chh; i++)
|
||||
fputc(ch[i * 2 + 1], sf);
|
||||
fclose(sf);
|
||||
}
|
||||
}
|
||||
vkUnmapMemory(dev, staging_mem);
|
||||
|
||||
VkDeviceSize dst_align = caps.minBitstreamBufferSizeAlignment ? caps.minBitstreamBufferSizeAlignment : 4096;
|
||||
VkDeviceSize dst_size = ((VkDeviceSize)1 << 20);
|
||||
dst_size = (dst_size + dst_align - 1) & ~(dst_align - 1);
|
||||
VkBufferCreateInfo dst_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = &profile_list,
|
||||
.size = dst_size,
|
||||
.usage = VK_BUFFER_USAGE_VIDEO_ENCODE_DST_BIT_KHR,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
};
|
||||
VkBuffer dst_buf;
|
||||
CHECK(vkCreateBuffer(dev, &dst_ci, NULL, &dst_buf));
|
||||
VkDeviceMemory dst_mem = alloc_and_bind_buffer(dev, &mem_props, dst_buf,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||
void *dst_map;
|
||||
CHECK(vkMapMemory(dev, dst_mem, 0, dst_size, 0, &dst_map));
|
||||
memset(dst_map, 0xCD, (size_t)dst_size);
|
||||
|
||||
VkCommandPoolCreateInfo xfer_pool_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||
.queueFamilyIndex = (uint32_t)transfer_qf,
|
||||
};
|
||||
VkCommandPool xfer_pool;
|
||||
CHECK(vkCreateCommandPool(dev, &xfer_pool_ci, NULL, &xfer_pool));
|
||||
VkCommandBufferAllocateInfo xfer_cb_ai = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = xfer_pool,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount = 1,
|
||||
};
|
||||
VkCommandBuffer xfer_cmd;
|
||||
CHECK(vkAllocateCommandBuffers(dev, &xfer_cb_ai, &xfer_cmd));
|
||||
|
||||
VkCommandBufferBeginInfo xfer_begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
||||
CHECK(vkBeginCommandBuffer(xfer_cmd, &xfer_begin_info));
|
||||
|
||||
VkImageMemoryBarrier to_xfer = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = src_image,
|
||||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 },
|
||||
};
|
||||
VkImageMemoryBarrier to_xfer_both[2] = { to_xfer, to_xfer };
|
||||
to_xfer_both[1].image = src_image2;
|
||||
vkCmdPipelineBarrier(xfer_cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0, 0, NULL, 0, NULL, two_src ? 2 : 1, to_xfer_both);
|
||||
|
||||
VkBufferImageCopy regions[2] = {
|
||||
{ /* luma plane */
|
||||
.bufferOffset = 0,
|
||||
.imageSubresource = { VK_IMAGE_ASPECT_PLANE_0_BIT, 0, 0, 1 },
|
||||
.imageExtent = { WIDTH, HEIGHT, 1 },
|
||||
},
|
||||
{ /* chroma plane (interleaved UV, half res) */
|
||||
.bufferOffset = luma_size,
|
||||
.imageSubresource = { VK_IMAGE_ASPECT_PLANE_1_BIT, 0, 0, 1 },
|
||||
.imageExtent = { WIDTH / 2, HEIGHT / 2, 1 },
|
||||
},
|
||||
};
|
||||
vkCmdCopyBufferToImage(xfer_cmd, staging_buf, src_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 2, regions);
|
||||
if (two_src) {
|
||||
VkBufferImageCopy r2[2] = { regions[0], regions[1] };
|
||||
r2[0].bufferOffset = frame_size;
|
||||
r2[1].bufferOffset = frame_size + luma_size;
|
||||
vkCmdCopyBufferToImage(xfer_cmd, staging_buf, src_image2, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 2, r2);
|
||||
}
|
||||
|
||||
VkImageMemoryBarrier to_encode_src = to_xfer;
|
||||
to_encode_src.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
to_encode_src.dstAccessMask = 0;
|
||||
to_encode_src.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
to_encode_src.newLayout = VK_IMAGE_LAYOUT_VIDEO_ENCODE_SRC_KHR;
|
||||
VkImageMemoryBarrier to_encode_dpb = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.newLayout = VK_IMAGE_LAYOUT_VIDEO_ENCODE_DPB_KHR,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = dpb_image,
|
||||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 },
|
||||
};
|
||||
VkImageMemoryBarrier barriers2[4] = { to_encode_src, to_encode_dpb };
|
||||
uint32_t barrier2_count = 2;
|
||||
if (use_pframe) {
|
||||
VkImageMemoryBarrier to_encode_dpb2 = to_encode_dpb;
|
||||
to_encode_dpb2.image = dpb_image2;
|
||||
barriers2[barrier2_count++] = to_encode_dpb2;
|
||||
}
|
||||
if (two_src) {
|
||||
VkImageMemoryBarrier to_encode_src2 = to_encode_src;
|
||||
to_encode_src2.image = src_image2;
|
||||
barriers2[barrier2_count++] = to_encode_src2;
|
||||
}
|
||||
vkCmdPipelineBarrier(xfer_cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||||
0, 0, NULL, 0, NULL, barrier2_count, barriers2);
|
||||
|
||||
CHECK(vkEndCommandBuffer(xfer_cmd));
|
||||
|
||||
/* HASVK_ENC_SEMSYNC */
|
||||
int use_semsync = getenv("HASVK_ENC_SEMSYNC") ? 1 : 0;
|
||||
VkSemaphore xfer_sem = VK_NULL_HANDLE;
|
||||
if (use_semsync) {
|
||||
VkSemaphoreCreateInfo sci = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
|
||||
CHECK(vkCreateSemaphore(dev, &sci, NULL, &xfer_sem));
|
||||
}
|
||||
|
||||
VkFenceCreateInfo xfer_fence_ci = { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
|
||||
VkFence xfer_fence;
|
||||
CHECK(vkCreateFence(dev, &xfer_fence_ci, NULL, &xfer_fence));
|
||||
VkSubmitInfo xfer_submit = {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &xfer_cmd,
|
||||
.signalSemaphoreCount = use_semsync ? 1u : 0u,
|
||||
.pSignalSemaphores = use_semsync ? &xfer_sem : NULL,
|
||||
};
|
||||
printf("submitting transfer prep...\n");
|
||||
fflush(stdout);
|
||||
CHECK(vkQueueSubmit(transfer_queue, 1, &xfer_submit, xfer_fence));
|
||||
if (!use_semsync) {
|
||||
VkResult xfer_wait = vkWaitForFences(dev, 1, &xfer_fence, VK_TRUE, 10ull * 1000 * 1000 * 1000);
|
||||
printf("transfer wait result = %d\n", xfer_wait);
|
||||
if (xfer_wait != VK_SUCCESS) { fprintf(stderr, "transfer prep failed/hung\n"); return 3; }
|
||||
} else {
|
||||
printf("transfer submitted (semaphore sync, no CPU wait)\n");
|
||||
}
|
||||
|
||||
VkCommandPoolCreateInfo pool_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||
.queueFamilyIndex = (uint32_t)encode_qf,
|
||||
};
|
||||
VkCommandPool pool;
|
||||
CHECK(vkCreateCommandPool(dev, &pool_ci, NULL, &pool));
|
||||
VkCommandBufferAllocateInfo cb_ai = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = pool,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount = 1,
|
||||
};
|
||||
VkCommandBuffer cmd;
|
||||
CHECK(vkAllocateCommandBuffers(dev, &cb_ai, &cmd));
|
||||
|
||||
VkQueryPoolVideoEncodeFeedbackCreateInfoKHR fb_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_VIDEO_ENCODE_FEEDBACK_CREATE_INFO_KHR,
|
||||
.pNext = &profile,
|
||||
.encodeFeedbackFlags =
|
||||
VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BUFFER_OFFSET_BIT_KHR |
|
||||
VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BYTES_WRITTEN_BIT_KHR,
|
||||
};
|
||||
VkQueryPoolCreateInfo qp_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
|
||||
.pNext = &fb_ci,
|
||||
.queryType = VK_QUERY_TYPE_VIDEO_ENCODE_FEEDBACK_KHR,
|
||||
.queryCount = 2,
|
||||
};
|
||||
VkQueryPool qpool;
|
||||
CHECK(vkCreateQueryPool(dev, &qp_ci, NULL, &qpool));
|
||||
|
||||
VkCommandBufferBeginInfo begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
||||
CHECK(vkBeginCommandBuffer(cmd, &begin_info));
|
||||
|
||||
vkCmdResetQueryPool(cmd, qpool, 0, 2);
|
||||
|
||||
if (getenv("HASVK_BISECT_TRULY_EMPTY")) {
|
||||
CHECK(vkEndCommandBuffer(cmd));
|
||||
VkFenceCreateInfo fci = { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
|
||||
VkFence f;
|
||||
CHECK(vkCreateFence(dev, &fci, NULL, &f));
|
||||
VkSubmitInfo si = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .commandBufferCount = 1, .pCommandBuffers = &cmd };
|
||||
printf("submitting TRULY EMPTY (no video calls at all)...\n");
|
||||
fflush(stdout);
|
||||
CHECK(vkQueueSubmit(queue, 1, &si, f));
|
||||
VkResult r = vkWaitForFences(dev, 1, &f, VK_TRUE, 15ull * 1000 * 1000 * 1000);
|
||||
printf("truly-empty wait result = %d\n", r);
|
||||
return r == VK_SUCCESS ? 0 : 2;
|
||||
}
|
||||
|
||||
int qp = getenv("HASVK_ENC_QP") ? atoi(getenv("HASVK_ENC_QP")) : 26;
|
||||
uint64_t bitrate = getenv("HASVK_ENC_BITRATE")
|
||||
? strtoull(getenv("HASVK_ENC_BITRATE"), NULL, 10) : 0;
|
||||
if (qp < 0) qp = 0;
|
||||
if (qp > 51) qp = 51;
|
||||
|
||||
int num_slices = getenv("HASVK_ENC_SLICES") ? atoi(getenv("HASVK_ENC_SLICES")) : 1;
|
||||
if (num_slices < 1) num_slices = 1;
|
||||
if (num_slices > HEIGHT_MBS) num_slices = HEIGHT_MBS;
|
||||
|
||||
VkVideoPictureResourceInfoKHR src_res = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR,
|
||||
.codedExtent = { WIDTH, HEIGHT }, .baseArrayLayer = 0, .imageViewBinding = src_view,
|
||||
};
|
||||
VkVideoPictureResourceInfoKHR src_res2 = src_res;
|
||||
src_res2.imageViewBinding = two_src ? src_view2 : src_view;
|
||||
VkVideoPictureResourceInfoKHR dpb_res0 = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR,
|
||||
.codedExtent = { WIDTH, HEIGHT }, .baseArrayLayer = 0, .imageViewBinding = dpb_view,
|
||||
};
|
||||
VkVideoPictureResourceInfoKHR dpb_res1 = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR,
|
||||
.codedExtent = { WIDTH, HEIGHT }, .baseArrayLayer = 0, .imageViewBinding = dpb_view2,
|
||||
};
|
||||
|
||||
int num_frames = use_pframe ? 2 : 1;
|
||||
VkDeviceSize base_off = getenv("HASVK_ENC_DSTOFF")
|
||||
? (VkDeviceSize)atoi(getenv("HASVK_ENC_DSTOFF")) : 0;
|
||||
VkDeviceSize frame_dst_offset[2] = { base_off, dst_size / 2 };
|
||||
|
||||
for (int frame = 0; frame < num_frames; frame++) {
|
||||
bool is_p_frame = (frame == 1) && !getenv("HASVK_ENC_FORCE_I");
|
||||
|
||||
VkVideoReferenceSlotInfoKHR begin_slots[2];
|
||||
uint32_t begin_slot_count = 0;
|
||||
if (is_p_frame) {
|
||||
begin_slots[begin_slot_count++] = (VkVideoReferenceSlotInfoKHR){
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR,
|
||||
.slotIndex = 0, .pPictureResource = &dpb_res0 };
|
||||
begin_slots[begin_slot_count++] = (VkVideoReferenceSlotInfoKHR){
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR,
|
||||
.slotIndex = 1, .pPictureResource = &dpb_res1 };
|
||||
} else {
|
||||
begin_slots[begin_slot_count++] = (VkVideoReferenceSlotInfoKHR){
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR,
|
||||
.slotIndex = 0, .pPictureResource = &dpb_res0 };
|
||||
}
|
||||
VkVideoEncodeH264RateControlLayerInfoKHR h264_rc_layer = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_RATE_CONTROL_LAYER_INFO_KHR,
|
||||
};
|
||||
VkVideoEncodeRateControlLayerInfoKHR rc_layer = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_RATE_CONTROL_LAYER_INFO_KHR,
|
||||
.pNext = &h264_rc_layer,
|
||||
.averageBitrate = bitrate,
|
||||
.maxBitrate = bitrate,
|
||||
.frameRateNumerator = 30,
|
||||
.frameRateDenominator = 1,
|
||||
};
|
||||
VkVideoEncodeRateControlInfoKHR rc_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_RATE_CONTROL_INFO_KHR,
|
||||
.rateControlMode = getenv("HASVK_ENC_VBR")
|
||||
? VK_VIDEO_ENCODE_RATE_CONTROL_MODE_VBR_BIT_KHR
|
||||
: VK_VIDEO_ENCODE_RATE_CONTROL_MODE_CBR_BIT_KHR,
|
||||
.layerCount = 1,
|
||||
.pLayers = &rc_layer,
|
||||
};
|
||||
VkVideoBeginCodingInfoKHR begin_coding = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_BEGIN_CODING_INFO_KHR,
|
||||
.pNext = bitrate ? &rc_info : NULL,
|
||||
.videoSession = session,
|
||||
.videoSessionParameters = params,
|
||||
.referenceSlotCount = begin_slot_count,
|
||||
.pReferenceSlots = begin_slots,
|
||||
};
|
||||
pCmdBeginVideoCodingKHR(cmd, &begin_coding);
|
||||
|
||||
StdVideoEncodeH264PictureInfo std_pic_info = {
|
||||
.seq_parameter_set_id = 0,
|
||||
.pic_parameter_set_id = 0,
|
||||
.idr_pic_id = 0,
|
||||
.primary_pic_type = is_p_frame ? STD_VIDEO_H264_PICTURE_TYPE_P
|
||||
: STD_VIDEO_H264_PICTURE_TYPE_IDR,
|
||||
.frame_num = (uint16_t)frame,
|
||||
.PicOrderCnt = (int32_t)(frame * 2),
|
||||
};
|
||||
std_pic_info.flags.IdrPicFlag = is_p_frame ? 0 : 1;
|
||||
std_pic_info.flags.is_reference = 1;
|
||||
|
||||
StdVideoEncodeH264SliceHeader std_slice_hdr[HEIGHT_MBS];
|
||||
VkVideoEncodeH264NaluSliceInfoKHR nalu_slice[HEIGHT_MBS];
|
||||
for (int s = 0; s < num_slices; s++) {
|
||||
uint32_t start_row = (uint32_t)s * HEIGHT_MBS / num_slices;
|
||||
std_slice_hdr[s] = (StdVideoEncodeH264SliceHeader){
|
||||
.first_mb_in_slice = start_row * WIDTH_MBS,
|
||||
.slice_type = is_p_frame ? STD_VIDEO_H264_SLICE_TYPE_P
|
||||
: STD_VIDEO_H264_SLICE_TYPE_I,
|
||||
.slice_qp_delta = qp - 26,
|
||||
.cabac_init_idc = STD_VIDEO_H264_CABAC_INIT_IDC_0,
|
||||
/* value 1 ("_ENABLED") == H.264 disable_deblocking_filter_idc==1. */
|
||||
.disable_deblocking_filter_idc = STD_VIDEO_H264_DISABLE_DEBLOCKING_FILTER_IDC_ENABLED,
|
||||
};
|
||||
nalu_slice[s] = (VkVideoEncodeH264NaluSliceInfoKHR){
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_NALU_SLICE_INFO_KHR,
|
||||
.constantQp = qp,
|
||||
.pStdSliceHeader = &std_slice_hdr[s],
|
||||
};
|
||||
}
|
||||
|
||||
VkVideoEncodeH264PictureInfoKHR h264_pic_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PICTURE_INFO_KHR,
|
||||
.naluSliceEntryCount = (uint32_t)num_slices,
|
||||
.pNaluSliceEntries = nalu_slice,
|
||||
.pStdPictureInfo = &std_pic_info,
|
||||
};
|
||||
|
||||
VkVideoReferenceSlotInfoKHR setup_slot = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR,
|
||||
.slotIndex = is_p_frame ? 1 : 0,
|
||||
.pPictureResource = is_p_frame ? &dpb_res1 : &dpb_res0,
|
||||
};
|
||||
VkVideoReferenceSlotInfoKHR ref_slot = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR,
|
||||
.slotIndex = 0,
|
||||
.pPictureResource = &dpb_res0,
|
||||
};
|
||||
|
||||
VkVideoEncodeInfoKHR encode_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_INFO_KHR,
|
||||
.pNext = &h264_pic_info,
|
||||
.dstBuffer = dst_buf,
|
||||
.dstBufferOffset = frame_dst_offset[frame],
|
||||
.dstBufferRange = dst_size / 2,
|
||||
.srcPictureResource = is_p_frame ? src_res2 : src_res,
|
||||
.pSetupReferenceSlot = &setup_slot,
|
||||
.referenceSlotCount = is_p_frame ? 1 : 0,
|
||||
.pReferenceSlots = is_p_frame ? &ref_slot : NULL,
|
||||
};
|
||||
vkCmdBeginQuery(cmd, qpool, frame, 0);
|
||||
pCmdEncodeVideoKHR(cmd, &encode_info);
|
||||
vkCmdEndQuery(cmd, qpool, frame);
|
||||
|
||||
VkVideoEndCodingInfoKHR end_coding = { .sType = VK_STRUCTURE_TYPE_VIDEO_END_CODING_INFO_KHR };
|
||||
pCmdEndVideoCodingKHR(cmd, &end_coding);
|
||||
}
|
||||
|
||||
CHECK(vkEndCommandBuffer(cmd));
|
||||
|
||||
VkFenceCreateInfo fence_ci = { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
|
||||
VkFence fence;
|
||||
CHECK(vkCreateFence(dev, &fence_ci, NULL, &fence));
|
||||
|
||||
VkPipelineStageFlags enc_wait_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
VkSubmitInfo submit = {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &cmd,
|
||||
.waitSemaphoreCount = use_semsync ? 1u : 0u,
|
||||
.pWaitSemaphores = use_semsync ? &xfer_sem : NULL,
|
||||
.pWaitDstStageMask = use_semsync ? &enc_wait_stage : NULL,
|
||||
};
|
||||
printf("submitting...\n");
|
||||
fflush(stdout);
|
||||
CHECK(vkQueueSubmit(queue, 1, &submit, fence));
|
||||
|
||||
VkResult wait_result = vkWaitForFences(dev, 1, &fence, VK_TRUE, 15ull * 1000 * 1000 * 1000);
|
||||
printf("wait result = %d (%s)\n", wait_result,
|
||||
wait_result == VK_SUCCESS ? "SUCCESS" :
|
||||
wait_result == VK_TIMEOUT ? "TIMEOUT" :
|
||||
wait_result == VK_ERROR_DEVICE_LOST ? "DEVICE_LOST" : "OTHER");
|
||||
|
||||
if (wait_result == VK_SUCCESS) {
|
||||
const uint8_t *bytes = (const uint8_t *)dst_map;
|
||||
const char *out_path = getenv("HASVK_ENC_OUT");
|
||||
if (!out_path) out_path = "/tmp/hasvk_enc_out.h264";
|
||||
FILE *of = fopen(out_path, "wb");
|
||||
if (of && hdr_size > 0)
|
||||
fwrite(hdr_buf, 1, hdr_size, of);
|
||||
|
||||
for (int f = 0; f < num_frames; f++) {
|
||||
struct { uint32_t offset; uint32_t bytesWritten; } fb = { 0, 0 };
|
||||
VkResult qr = vkGetQueryPoolResults(dev, qpool, f, 1, sizeof(fb), &fb,
|
||||
sizeof(fb), VK_QUERY_RESULT_WAIT_BIT);
|
||||
printf("frame %d (%s): bytesWritten=%u (query result %d)\n",
|
||||
f, f == 0 ? "I" : "P", fb.bytesWritten, qr);
|
||||
if (of && qr == VK_SUCCESS)
|
||||
fwrite(bytes + frame_dst_offset[f], 1, fb.bytesWritten, of);
|
||||
}
|
||||
if (of) {
|
||||
fclose(of);
|
||||
printf("wrote %zu (SPS/PPS) + %d frame(s) to %s\n",
|
||||
hdr_size, num_frames, out_path);
|
||||
}
|
||||
}
|
||||
|
||||
vkUnmapMemory(dev, dst_mem);
|
||||
printf("done.\n");
|
||||
return wait_result == VK_SUCCESS ? 0 : 2;
|
||||
}
|
||||
Reference in new issue
Block a user