|
|
@@ -2,9 +2,9 @@
|
|
|
|
|
|
#include <time.h>
|
|
|
#include <cpuid.h>
|
|
|
-#include <util/c99defs.h>
|
|
|
-#include <util/dstr.h>
|
|
|
+
|
|
|
#include <va/va_drm.h>
|
|
|
+#include <va/va_drmcommon.h>
|
|
|
#include <va/va_str.h>
|
|
|
#include <stdio.h>
|
|
|
#include <stdlib.h>
|
|
|
@@ -14,7 +14,12 @@
|
|
|
#include <dirent.h>
|
|
|
|
|
|
#include <obs.h>
|
|
|
+#include <obs-encoder.h>
|
|
|
#include <obs-nix-platform.h>
|
|
|
+#include <graphics/graphics.h>
|
|
|
+#include <util/c99defs.h>
|
|
|
+#include <util/dstr.h>
|
|
|
+#include <util/bmem.h>
|
|
|
|
|
|
// Set during check_adapters to work-around VPL dispatcher not setting a VADisplay
|
|
|
// for the MSDK runtime.
|
|
|
@@ -27,13 +32,145 @@ struct linux_data {
|
|
|
VADisplay vaDisplay;
|
|
|
};
|
|
|
|
|
|
+#define DEVICE_MGR_TYPE MFX_HANDLE_VA_DISPLAY
|
|
|
+// This ends up at like 72 for 1440p@120 AV1.
|
|
|
+// We may end up hitting this in practice?
|
|
|
+constexpr int32_t MAX_ALLOCABLE_SURFACES = 128;
|
|
|
+
|
|
|
+struct surface_info {
|
|
|
+ VASurfaceID id;
|
|
|
+ int32_t width, height;
|
|
|
+ gs_texture_t *tex_y;
|
|
|
+ gs_texture_t *tex_uv;
|
|
|
+};
|
|
|
+
|
|
|
mfxStatus simple_alloc(mfxHDL pthis, mfxFrameAllocRequest *request,
|
|
|
mfxFrameAllocResponse *response)
|
|
|
{
|
|
|
- UNUSED_PARAMETER(pthis);
|
|
|
- UNUSED_PARAMETER(request);
|
|
|
- UNUSED_PARAMETER(response);
|
|
|
- return MFX_ERR_UNSUPPORTED;
|
|
|
+ if (request->Type & (MFX_MEMTYPE_SYSTEM_MEMORY |
|
|
|
+ MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET))
|
|
|
+ return MFX_ERR_UNSUPPORTED;
|
|
|
+
|
|
|
+ response->mids = (mfxMemId *)nullptr;
|
|
|
+ response->NumFrameActual = 0;
|
|
|
+
|
|
|
+ mfxSession *session = (mfxSession *)pthis;
|
|
|
+ VADisplay display;
|
|
|
+ mfxStatus sts =
|
|
|
+ MFXVideoCORE_GetHandle(*session, DEVICE_MGR_TYPE, &display);
|
|
|
+ MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
|
|
|
+
|
|
|
+ // https://ffmpeg.org/doxygen/5.1/hwcontext__vaapi_8c_source.html#l00109
|
|
|
+ // though earlier comments suggest the driver ignores rt_format so we could choose whatever.
|
|
|
+ unsigned int rt_format;
|
|
|
+ int32_t pix_format;
|
|
|
+ switch (request->Info.FourCC) {
|
|
|
+ case MFX_FOURCC_P010:
|
|
|
+ rt_format = VA_RT_FORMAT_YUV420_10;
|
|
|
+ pix_format = VA_FOURCC_P010;
|
|
|
+ break;
|
|
|
+ case MFX_FOURCC_NV12:
|
|
|
+ default:
|
|
|
+ rt_format = VA_RT_FORMAT_YUV420;
|
|
|
+ pix_format = VA_FOURCC_NV12;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ int num_attrs = 2;
|
|
|
+ VASurfaceAttrib attrs[2] = {
|
|
|
+ {
|
|
|
+ .type = VASurfaceAttribMemoryType,
|
|
|
+ .flags = VA_SURFACE_ATTRIB_SETTABLE,
|
|
|
+ .value =
|
|
|
+ {
|
|
|
+ .type = VAGenericValueTypeInteger,
|
|
|
+ .value =
|
|
|
+ {.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .type = VASurfaceAttribPixelFormat,
|
|
|
+ .flags = VA_SURFACE_ATTRIB_SETTABLE,
|
|
|
+ .value =
|
|
|
+ {
|
|
|
+ .type = VAGenericValueTypeInteger,
|
|
|
+ .value = {.i = (int)pix_format},
|
|
|
+ },
|
|
|
+ }};
|
|
|
+
|
|
|
+ unsigned int num_surfaces = request->NumFrameSuggested;
|
|
|
+ VASurfaceID temp_surfaces[MAX_ALLOCABLE_SURFACES] = {0};
|
|
|
+ assert(num_surfaces < MAX_ALLOCABLE_SURFACES);
|
|
|
+ VAStatus vasts;
|
|
|
+ if ((vasts = vaCreateSurfaces(display, rt_format, request->Info.Width,
|
|
|
+ request->Info.Height, temp_surfaces,
|
|
|
+ num_surfaces, attrs, num_attrs)) !=
|
|
|
+ VA_STATUS_SUCCESS) {
|
|
|
+ blog(LOG_ERROR, "failed to create surfaces: %d", vasts);
|
|
|
+ return MFX_ERR_MEMORY_ALLOC;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Follow the FFmpeg trick and stuff our pointer at the end.
|
|
|
+ mfxMemId *mids =
|
|
|
+ (mfxMemId *)bmalloc(sizeof(mfxMemId) * num_surfaces + 1);
|
|
|
+ struct surface_info *surfaces = (struct surface_info *)bmalloc(
|
|
|
+ sizeof(struct surface_info) * num_surfaces);
|
|
|
+
|
|
|
+ mids[num_surfaces] = surfaces; // stuff it
|
|
|
+ for (uint64_t i = 0; i < num_surfaces; i++) {
|
|
|
+ surfaces[i].id = temp_surfaces[i];
|
|
|
+ surfaces[i].width = request->Info.Width;
|
|
|
+ surfaces[i].height = request->Info.Height;
|
|
|
+ mids[i] = &surfaces[i];
|
|
|
+
|
|
|
+ VADRMPRIMESurfaceDescriptor surfDesc = {0};
|
|
|
+ if (vaExportSurfaceHandle(display, surfaces[i].id,
|
|
|
+ VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
|
|
|
+ VA_EXPORT_SURFACE_READ_WRITE,
|
|
|
+ &surfDesc) != VA_STATUS_SUCCESS)
|
|
|
+ return MFX_ERR_MEMORY_ALLOC;
|
|
|
+
|
|
|
+ obs_enter_graphics();
|
|
|
+ // TODO: P010 format support
|
|
|
+ assert(surfDesc.num_objects == 1);
|
|
|
+ int fds[4] = {0};
|
|
|
+ uint32_t strides[4] = {0};
|
|
|
+ uint32_t offsets[4] = {0};
|
|
|
+ uint64_t modifiers[4] = {0};
|
|
|
+ fds[0] =
|
|
|
+ surfDesc.objects[surfDesc.layers[0].object_index[0]].fd;
|
|
|
+ fds[1] =
|
|
|
+ surfDesc.objects[surfDesc.layers[1].object_index[0]].fd;
|
|
|
+ strides[0] = surfDesc.layers[0].pitch[0];
|
|
|
+ strides[1] = surfDesc.layers[1].pitch[0];
|
|
|
+ offsets[0] = surfDesc.layers[0].offset[0];
|
|
|
+ offsets[1] = surfDesc.layers[1].offset[0];
|
|
|
+ modifiers[0] =
|
|
|
+ surfDesc.objects[surfDesc.layers[0].object_index[0]]
|
|
|
+ .drm_format_modifier;
|
|
|
+ modifiers[1] =
|
|
|
+ surfDesc.objects[surfDesc.layers[1].object_index[0]]
|
|
|
+ .drm_format_modifier;
|
|
|
+
|
|
|
+ surfaces[i].tex_y = gs_texture_create_from_dmabuf(
|
|
|
+ surfDesc.width, surfDesc.height,
|
|
|
+ surfDesc.layers[0].drm_format, GS_R8, 1, fds, strides,
|
|
|
+ offsets, modifiers);
|
|
|
+ surfaces[i].tex_uv = gs_texture_create_from_dmabuf(
|
|
|
+ surfDesc.width / 2, surfDesc.height,
|
|
|
+ surfDesc.layers[1].drm_format, GS_R8G8, 1, fds + 1,
|
|
|
+ strides + 1, offsets + 1, modifiers + 1);
|
|
|
+ obs_leave_graphics();
|
|
|
+
|
|
|
+ close(surfDesc.objects[surfDesc.layers[0].object_index[0]].fd);
|
|
|
+ if (!surfaces[i].tex_y || !surfaces[i].tex_uv) {
|
|
|
+ return MFX_ERR_MEMORY_ALLOC;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ response->mids = (mfxMemId *)mids;
|
|
|
+ response->NumFrameActual = num_surfaces;
|
|
|
+ return MFX_ERR_NONE;
|
|
|
}
|
|
|
|
|
|
mfxStatus simple_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr)
|
|
|
@@ -55,33 +192,78 @@ mfxStatus simple_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr)
|
|
|
mfxStatus simple_gethdl(mfxHDL pthis, mfxMemId mid, mfxHDL *handle)
|
|
|
{
|
|
|
UNUSED_PARAMETER(pthis);
|
|
|
- UNUSED_PARAMETER(mid);
|
|
|
- UNUSED_PARAMETER(handle);
|
|
|
- return MFX_ERR_UNSUPPORTED;
|
|
|
+ if (NULL == handle)
|
|
|
+ return MFX_ERR_INVALID_HANDLE;
|
|
|
+
|
|
|
+ // Seemingly undocumented, but Pair format defined by
|
|
|
+ // oneVPL-intel-gpu-intel-onevpl-23.1.0/_studio/mfx_lib/encode_hw/av1/linux/base/av1ehw_base_va_packer_lin.cpp
|
|
|
+ // https://github.com/intel/vpl-gpu-rt/blob/4170dd9fa1ea319dda81b6189616ecc9b178a321/_studio/shared/src/libmfx_core_vaapi.cpp#L1464
|
|
|
+ mfxHDLPair *pPair = (mfxHDLPair *)handle;
|
|
|
+
|
|
|
+ // first must be a pointer to a VASurfaceID and will be dereferenced by
|
|
|
+ // the driver.
|
|
|
+ pPair->first = &((struct surface_info *)mid)->id;
|
|
|
+ pPair->second = 0;
|
|
|
+
|
|
|
+ return MFX_ERR_NONE;
|
|
|
}
|
|
|
|
|
|
mfxStatus simple_free(mfxHDL pthis, mfxFrameAllocResponse *response)
|
|
|
{
|
|
|
- UNUSED_PARAMETER(pthis);
|
|
|
- UNUSED_PARAMETER(response);
|
|
|
- return MFX_ERR_UNSUPPORTED;
|
|
|
+ if (response->mids == nullptr || response->NumFrameActual == 0)
|
|
|
+ return MFX_ERR_NONE;
|
|
|
+
|
|
|
+ mfxSession *session = (mfxSession *)pthis;
|
|
|
+ VADisplay display;
|
|
|
+ mfxStatus sts =
|
|
|
+ MFXVideoCORE_GetHandle(*session, DEVICE_MGR_TYPE, &display);
|
|
|
+ MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
|
|
|
+
|
|
|
+ struct surface_info *surfs =
|
|
|
+ (struct surface_info *)response->mids[response->NumFrameActual];
|
|
|
+ VASurfaceID temp_surfaces[MAX_ALLOCABLE_SURFACES] = {0};
|
|
|
+ obs_enter_graphics();
|
|
|
+ for (int i = 0; i < response->NumFrameActual; i++) {
|
|
|
+ temp_surfaces[i] = *(VASurfaceID *)response->mids[i];
|
|
|
+ gs_texture_destroy(surfs[i].tex_y);
|
|
|
+ gs_texture_destroy(surfs[i].tex_uv);
|
|
|
+ }
|
|
|
+ obs_leave_graphics();
|
|
|
+
|
|
|
+ bfree(surfs);
|
|
|
+ bfree(response->mids);
|
|
|
+ if (vaDestroySurfaces(display, temp_surfaces,
|
|
|
+ response->NumFrameActual) != VA_STATUS_SUCCESS)
|
|
|
+ return MFX_ERR_MEMORY_ALLOC;
|
|
|
+
|
|
|
+ return MFX_ERR_NONE;
|
|
|
}
|
|
|
|
|
|
-mfxStatus simple_copytex(mfxHDL pthis, mfxMemId mid, mfxU32 tex_handle,
|
|
|
- mfxU64 lock_key, mfxU64 *next_key)
|
|
|
+mfxStatus simple_copytex(mfxHDL pthis, mfxMemId mid, void *tex, mfxU64 lock_key,
|
|
|
+ mfxU64 *next_key)
|
|
|
{
|
|
|
- UNUSED_PARAMETER(pthis);
|
|
|
- UNUSED_PARAMETER(mid);
|
|
|
- UNUSED_PARAMETER(tex_handle);
|
|
|
UNUSED_PARAMETER(lock_key);
|
|
|
UNUSED_PARAMETER(next_key);
|
|
|
- return MFX_ERR_UNSUPPORTED;
|
|
|
-}
|
|
|
|
|
|
-#if 0
|
|
|
-void ClearYUVSurfaceVMem(mfxMemId memId);
|
|
|
-void ClearRGBSurfaceVMem(mfxMemId memId);
|
|
|
-#endif
|
|
|
+ profile_start("copy_tex");
|
|
|
+
|
|
|
+ mfxSession *session = (mfxSession *)pthis;
|
|
|
+ VADisplay display;
|
|
|
+ mfxStatus sts =
|
|
|
+ MFXVideoCORE_GetHandle(*session, DEVICE_MGR_TYPE, &display);
|
|
|
+ MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
|
|
|
+
|
|
|
+ struct encoder_texture *ptex = (struct encoder_texture *)tex;
|
|
|
+ struct surface_info *surf = (struct surface_info *)mid;
|
|
|
+
|
|
|
+ obs_enter_graphics();
|
|
|
+ gs_copy_texture(surf->tex_y, ptex->tex[0]);
|
|
|
+ gs_copy_texture(surf->tex_uv, ptex->tex[1]);
|
|
|
+ obs_leave_graphics();
|
|
|
+
|
|
|
+ profile_end("copy_tex");
|
|
|
+ return MFX_ERR_NONE;
|
|
|
+}
|
|
|
|
|
|
// Initialize Intel VPL Session, device/display and memory manager
|
|
|
mfxStatus Initialize(mfxVersion ver, mfxSession *pSession,
|
|
|
@@ -90,7 +272,6 @@ mfxStatus Initialize(mfxVersion ver, mfxSession *pSession,
|
|
|
void **data)
|
|
|
{
|
|
|
UNUSED_PARAMETER(ver);
|
|
|
- UNUSED_PARAMETER(pmfxAllocator);
|
|
|
UNUSED_PARAMETER(deviceHandle);
|
|
|
UNUSED_PARAMETER(bCreateSharedHandles);
|
|
|
mfxStatus sts = MFX_ERR_NONE;
|
|
|
@@ -117,12 +298,24 @@ mfxStatus Initialize(mfxVersion ver, mfxSession *pSession,
|
|
|
impl);
|
|
|
|
|
|
int fd = -1;
|
|
|
- if (codec == QSV_CODEC_AVC && default_h264_device)
|
|
|
- fd = open(default_h264_device, O_RDWR);
|
|
|
- if (codec == QSV_CODEC_HEVC && default_hevc_device)
|
|
|
- fd = open(default_hevc_device, O_RDWR);
|
|
|
- if (codec == QSV_CODEC_AV1 && default_av1_device)
|
|
|
- fd = open(default_av1_device, O_RDWR);
|
|
|
+ if (pmfxAllocator) {
|
|
|
+ // TODO: This is broken and we need the ovi adapter to be
|
|
|
+ // correct for checks earlier in encoder_create to fallback
|
|
|
+ // properly.
|
|
|
+ char device_path[128];
|
|
|
+ obs_video_info ovi;
|
|
|
+ obs_get_video_info(&ovi);
|
|
|
+ // eglQueryDeviceStringEXT( device, EGL_DRM_DEVICE_FILE_EXT);
|
|
|
+ sprintf(device_path, "/dev/dri/renderD%d", 128 + ovi.adapter);
|
|
|
+ fd = open(device_path, O_RDWR);
|
|
|
+ } else {
|
|
|
+ if (codec == QSV_CODEC_AVC && default_h264_device)
|
|
|
+ fd = open(default_h264_device, O_RDWR);
|
|
|
+ else if (codec == QSV_CODEC_HEVC && default_hevc_device)
|
|
|
+ fd = open(default_hevc_device, O_RDWR);
|
|
|
+ else if (codec == QSV_CODEC_AV1 && default_av1_device)
|
|
|
+ fd = open(default_av1_device, O_RDWR);
|
|
|
+ }
|
|
|
if (fd < 0) {
|
|
|
blog(LOG_ERROR, "Failed to open device '%s'",
|
|
|
default_h264_device);
|
|
|
@@ -152,10 +345,22 @@ mfxStatus Initialize(mfxVersion ver, mfxSession *pSession,
|
|
|
return MFX_ERR_DEVICE_FAILED;
|
|
|
}
|
|
|
|
|
|
- sts = MFXVideoCORE_SetHandle(*pSession, MFX_HANDLE_VA_DISPLAY,
|
|
|
- vaDisplay);
|
|
|
+ sts = MFXVideoCORE_SetHandle(*pSession, DEVICE_MGR_TYPE, vaDisplay);
|
|
|
MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
|
|
|
|
|
|
+ if (pmfxAllocator) {
|
|
|
+ // Allow us to access the session during allocation.
|
|
|
+ pmfxAllocator->pthis = pSession;
|
|
|
+ pmfxAllocator->Alloc = simple_alloc;
|
|
|
+ pmfxAllocator->Free = simple_free;
|
|
|
+ pmfxAllocator->Lock = simple_lock;
|
|
|
+ pmfxAllocator->Unlock = simple_unlock;
|
|
|
+ pmfxAllocator->GetHDL = simple_gethdl;
|
|
|
+
|
|
|
+ sts = MFXVideoCORE_SetFrameAllocator(*pSession, pmfxAllocator);
|
|
|
+ MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
|
|
|
+ }
|
|
|
+
|
|
|
struct linux_data *d =
|
|
|
(struct linux_data *)bmalloc(sizeof(struct linux_data));
|
|
|
d->fd = fd;
|
|
|
@@ -187,7 +392,7 @@ double TimeDiffMsec(mfxTime tfinish, mfxTime tstart)
|
|
|
{
|
|
|
UNUSED_PARAMETER(tfinish);
|
|
|
UNUSED_PARAMETER(tstart);
|
|
|
- //TODO, unused so far it seems
|
|
|
+ //unused so far
|
|
|
return 0.0;
|
|
|
}
|
|
|
|