From 0d89378021b9e8913b521164d1bc436d9bc2630f Mon Sep 17 00:00:00 2001 From: dec05eba Date: Mon, 4 Mar 2024 20:30:43 +0100 Subject: Use opengl capture for nvfbc (prepare for vulkan capture without cuda) --- TODO | 10 +- include/capture/capture.h | 2 +- include/capture/nvfbc.h | 6 +- include/color_conversion.h | 5 +- include/egl.h | 45 ++++++++- src/capture/capture.c | 4 +- src/capture/kms.c | 4 +- src/capture/kms_cuda.c | 2 +- src/capture/nvfbc.c | 123 ++++++++++++++++++------- src/capture/xcomposite_cuda.c | 4 +- src/capture/xcomposite_vaapi.c | 4 +- src/color_conversion.c | 18 ++++ src/egl.c | 205 ++++++++++++++++++++++++++++++++++++----- src/main.cpp | 79 ++++++++-------- 14 files changed, 397 insertions(+), 114 deletions(-) diff --git a/TODO b/TODO index 2971a9e..8c0f567 100644 --- a/TODO +++ b/TODO @@ -114,4 +114,12 @@ Color range doesn't seem to work on nvidia. This might be because the output col Setup hardware video context so we can query constraints and capabilities for better default and better error messages. -Use CAP_SYS_NICE in flatpak too on the main gpu screen recorder binary. It makes recording smoother, especially with constant framerate. \ No newline at end of file +Use CAP_SYS_NICE in flatpak too on the main gpu screen recorder binary. It makes recording smoother, especially with constant framerate. + +Show error when using compressed kms plane which isn't supported. Also do that in the gui. + +Use video_codec_context->width/height instead of frame->width/height in capture. + +Modify ffmpeg to accept opengl texture for nvenc encoding. Removes extra buffers and copies. + +When vulkan encode is added, mention minimum nvidia driver required. (550.54.14?). diff --git a/include/capture/capture.h b/include/capture/capture.h index 2ca57cc..43b5de5 100644 --- a/include/capture/capture.h +++ b/include/capture/capture.h @@ -55,7 +55,7 @@ void gsr_capture_end(gsr_capture *cap, AVFrame *frame); void gsr_capture_destroy(gsr_capture *cap, AVCodecContext *video_codec_context); bool gsr_capture_base_setup_vaapi_textures(gsr_capture_base *self, AVFrame *frame, gsr_egl *egl, VADisplay va_dpy, VADRMPRIMESurfaceDescriptor *prime, gsr_color_range color_range); -bool gsr_capture_base_setup_cuda_textures(gsr_capture_base *base, AVFrame *frame, gsr_cuda_context *cuda_context, gsr_egl *egl, gsr_color_range color_range, bool hdr); +bool gsr_capture_base_setup_cuda_textures(gsr_capture_base *base, AVFrame *frame, gsr_cuda_context *cuda_context, gsr_egl *egl, gsr_color_range color_range, gsr_source_color source_color, bool hdr); void gsr_capture_base_stop(gsr_capture_base *self, gsr_egl *egl); #endif /* GSR_CAPTURE_CAPTURE_H */ diff --git a/include/capture/nvfbc.h b/include/capture/nvfbc.h index 431777b..da486f2 100644 --- a/include/capture/nvfbc.h +++ b/include/capture/nvfbc.h @@ -4,16 +4,16 @@ #include "capture.h" #include "../vec2.h" -typedef struct _XDisplay Display; - typedef struct { - Display *dpy; + gsr_egl *egl; const char *display_to_capture; /* if this is "screen", then the entire x11 screen is captured (all displays). A copy is made of this */ int fps; vec2i pos; vec2i size; bool direct_capture; bool overclock; + bool hdr; + gsr_color_range color_range; } gsr_capture_nvfbc_params; gsr_capture* gsr_capture_nvfbc_create(const gsr_capture_nvfbc_params *params); diff --git a/include/color_conversion.h b/include/color_conversion.h index 222be4b..1848715 100644 --- a/include/color_conversion.h +++ b/include/color_conversion.h @@ -11,11 +11,12 @@ typedef enum { } gsr_color_range; typedef enum { - GSR_SOURCE_COLOR_RGB + GSR_SOURCE_COLOR_RGB, + GSR_SOURCE_COLOR_BGR } gsr_source_color; typedef enum { - GSR_DESTINATION_COLOR_BGR, + GSR_DESTINATION_COLOR_BGR, // TODO: remove GSR_DESTINATION_COLOR_NV12, /* YUV420, BT709, 8-bit */ GSR_DESTINATION_COLOR_P010 /* YUV420, BT2020, 10-bit */ } gsr_destination_color; diff --git a/include/egl.h b/include/egl.h index 6f0c11b..a0a44d0 100644 --- a/include/egl.h +++ b/include/egl.h @@ -8,6 +8,7 @@ #include #include #include "vec2.h" +#include "defs.h" #ifdef _WIN64 typedef signed long long int khronos_intptr_t; @@ -33,6 +34,10 @@ typedef void* EGLImage; typedef void* EGLImageKHR; typedef void *GLeglImageOES; typedef void (*__eglMustCastToProperFunctionPointerType)(void); +typedef struct __GLXFBConfigRec *GLXFBConfig; +typedef struct __GLXcontextRec *GLXContext; +typedef XID GLXDrawable; +typedef void(*__GLXextFuncPtr)(void); #define EGL_SUCCESS 0x3000 #define EGL_BUFFER_SIZE 0x3020 @@ -71,6 +76,10 @@ typedef void (*__eglMustCastToProperFunctionPointerType)(void); #define GL_TEXTURE_2D 0x0DE1 #define GL_TEXTURE_EXTERNAL_OES 0x8D65 #define GL_RED 0x1903 +#define GL_GREEN 0x1904 +#define GL_BLUE 0x1905 +#define GL_ALPHA 0x1906 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 #define GL_RG 0x8227 #define GL_RGB 0x1907 #define GL_RGBA 0x1908 @@ -98,6 +107,7 @@ typedef void (*__eglMustCastToProperFunctionPointerType)(void); #define GL_BLEND 0x0BE2 #define GL_SRC_ALPHA 0x0302 #define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DEBUG_OUTPUT 0x92E0 #define GL_VENDOR 0x1F00 #define GL_RENDERER 0x1F01 @@ -112,6 +122,11 @@ typedef void (*__eglMustCastToProperFunctionPointerType)(void); typedef unsigned int (*FUNC_eglExportDMABUFImageQueryMESA)(EGLDisplay dpy, EGLImageKHR image, int *fourcc, int *num_planes, uint64_t *modifiers); typedef unsigned int (*FUNC_eglExportDMABUFImageMESA)(EGLDisplay dpy, EGLImageKHR image, int *fds, int32_t *strides, int32_t *offsets); typedef void (*FUNC_glEGLImageTargetTexture2DOES)(unsigned int target, GLeglImageOES image); +typedef GLXContext (*FUNC_glXCreateContextAttribsARB)(Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); +typedef void (*FUNC_glXSwapIntervalEXT)(Display * dpy, GLXDrawable drawable, int interval); +typedef int (*FUNC_glXSwapIntervalMESA)(unsigned int interval); +typedef int (*FUNC_glXSwapIntervalSGI)(int interval); +typedef void (*GLDEBUGPROC)(unsigned int source, unsigned int type, unsigned int id, unsigned int severity, int length, const char *message, const void *userParam); #define GSR_MAX_OUTPUTS 32 @@ -139,15 +154,28 @@ typedef struct { int num_outputs; } gsr_wayland; +typedef enum { + GSR_GL_CONTEXT_TYPE_EGL, + GSR_GL_CONTEXT_TYPE_GLX +} gsr_gl_context_type; + typedef struct gsr_egl gsr_egl; struct gsr_egl { void *egl_library; + void *glx_library; void *gl_library; + gsr_gl_context_type context_type; + EGLDisplay egl_display; EGLSurface egl_surface; EGLContext egl_context; + void *glx_context; + void *glx_fb_config; + + gsr_gpu_info gpu_info; + gsr_x11 x11; gsr_wayland wayland; char card_path[128]; @@ -173,6 +201,19 @@ struct gsr_egl { FUNC_eglExportDMABUFImageMESA eglExportDMABUFImageMESA; FUNC_glEGLImageTargetTexture2DOES glEGLImageTargetTexture2DOES; + __GLXextFuncPtr (*glXGetProcAddress)(const unsigned char *procName); + GLXFBConfig* (*glXChooseFBConfig)(Display *dpy, int screen, const int *attribList, int *nitems); + Bool (*glXMakeContextCurrent)(Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); + // TODO: Remove + GLXContext (*glXCreateNewContext)(Display *dpy, GLXFBConfig config, int renderType, GLXContext shareList, Bool direct); + void (*glXSwapBuffers)(Display *dpy, GLXDrawable drawable); + FUNC_glXCreateContextAttribsARB glXCreateContextAttribsARB; + + /* Optional */ + FUNC_glXSwapIntervalEXT glXSwapIntervalEXT; + FUNC_glXSwapIntervalMESA glXSwapIntervalMESA; + FUNC_glXSwapIntervalSGI glXSwapIntervalSGI; + unsigned int (*glGetError)(void); const unsigned char* (*glGetString)(unsigned int name); void (*glFlush)(void); @@ -183,6 +224,7 @@ struct gsr_egl { void (*glDeleteTextures)(int n, const unsigned int *texture); void (*glBindTexture)(unsigned int target, unsigned int texture); void (*glTexParameteri)(unsigned int target, unsigned int pname, int param); + void (*glTexParameteriv)(unsigned int target, unsigned int pname, const int *params); void (*glGetTexLevelParameteriv)(unsigned int target, int level, unsigned int pname, int *params); void (*glTexImage2D)(unsigned int target, int level, int internalFormat, int width, int height, int border, unsigned int format, unsigned int type, const void *pixels); void (*glCopyImageSubData)(unsigned int srcName, unsigned int srcTarget, int srcLevel, int srcX, int srcY, int srcZ, unsigned int dstName, unsigned int dstTarget, int dstLevel, int dstX, int dstY, int dstZ, int srcWidth, int srcHeight, int srcDepth); @@ -224,9 +266,10 @@ struct gsr_egl { int (*glGetUniformLocation)(unsigned int program, const char *name); void (*glUniform1f)(int location, float v0); void (*glUniform2f)(int location, float v0, float v1); + void (*glDebugMessageCallback)(GLDEBUGPROC callback, const void *userParam); }; -bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland); +bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland, bool is_monitor_capture); void gsr_egl_unload(gsr_egl *self); void gsr_egl_update(gsr_egl *self); diff --git a/src/capture/capture.c b/src/capture/capture.c index f775d01..0b26e87 100644 --- a/src/capture/capture.c +++ b/src/capture/capture.c @@ -220,7 +220,7 @@ static bool cuda_register_opengl_texture(gsr_cuda *cuda, CUgraphicsResource *cud return true; } -bool gsr_capture_base_setup_cuda_textures(gsr_capture_base *base, AVFrame *frame, gsr_cuda_context *cuda_context, gsr_egl *egl, gsr_color_range color_range, bool hdr) { +bool gsr_capture_base_setup_cuda_textures(gsr_capture_base *base, AVFrame *frame, gsr_cuda_context *cuda_context, gsr_egl *egl, gsr_color_range color_range, gsr_source_color source_color, bool hdr) { // TODO: const int res = av_hwframe_get_buffer(base->video_codec_context->hw_frames_ctx, frame, 0); if(res < 0) { @@ -264,7 +264,7 @@ bool gsr_capture_base_setup_cuda_textures(gsr_capture_base *base, AVFrame *frame gsr_color_conversion_params color_conversion_params = {0}; color_conversion_params.color_range = color_range; color_conversion_params.egl = egl; - color_conversion_params.source_color = GSR_SOURCE_COLOR_RGB; + color_conversion_params.source_color = source_color; if(!hdr) color_conversion_params.destination_color = GSR_DESTINATION_COLOR_NV12; else diff --git a/src/capture/kms.c b/src/capture/kms.c index d5c6918..32e5d3e 100644 --- a/src/capture/kms.c +++ b/src/capture/kms.c @@ -188,8 +188,8 @@ static vec2i swap_vec2i(vec2i value) { } bool gsr_capture_kms_capture(gsr_capture_kms *self, gsr_capture_base *base, AVFrame *frame, gsr_egl *egl, bool hdr, bool screen_plane_use_modifiers, bool cursor_texture_is_external) { - egl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - egl->glClear(GL_COLOR_BUFFER_BIT); + //egl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + egl->glClear(0); gsr_capture_kms_cleanup_kms_fds(self); diff --git a/src/capture/kms_cuda.c b/src/capture/kms_cuda.c index 11c3d79..8583d56 100644 --- a/src/capture/kms_cuda.c +++ b/src/capture/kms_cuda.c @@ -102,7 +102,7 @@ static int gsr_capture_kms_cuda_start(gsr_capture *cap, AVCodecContext *video_co .cuda_graphics_resources = cap_kms->cuda_graphics_resources, .mapped_arrays = cap_kms->mapped_arrays }; - if(!gsr_capture_base_setup_cuda_textures(&cap_kms->base, frame, &cuda_context, cap_kms->params.egl, cap_kms->params.color_range, cap_kms->params.hdr)) { + if(!gsr_capture_base_setup_cuda_textures(&cap_kms->base, frame, &cuda_context, cap_kms->params.egl, cap_kms->params.color_range, GSR_SOURCE_COLOR_RGB, cap_kms->params.hdr)) { gsr_capture_kms_cuda_stop(cap, video_codec_context); return -1; } diff --git a/src/capture/nvfbc.c b/src/capture/nvfbc.c index 1401cca..0b7ce60 100644 --- a/src/capture/nvfbc.c +++ b/src/capture/nvfbc.c @@ -1,6 +1,7 @@ #include "../../include/capture/nvfbc.h" #include "../../external/NvFBC.h" #include "../../include/cuda.h" +#include "../../include/egl.h" #include #include #include @@ -14,6 +15,7 @@ #include typedef struct { + gsr_capture_base base; gsr_capture_nvfbc_params params; void *library; @@ -24,6 +26,10 @@ typedef struct { bool capture_session_created; gsr_cuda cuda; + CUgraphicsResource cuda_graphics_resources[2]; + CUarray mapped_arrays[2]; + CUstream cuda_stream; // TODO: asdasdsa + NVFBC_TOGL_SETUP_PARAMS setup_params; } gsr_capture_nvfbc; #if defined(_WIN64) || defined(__LP64__) @@ -155,7 +161,7 @@ static bool ffmpeg_create_cuda_contexts(gsr_capture_nvfbc *cap_nvfbc, AVCodecCon AVHWFramesContext *hw_frame_context = (AVHWFramesContext*)frame_context->data; hw_frame_context->width = video_codec_context->width; hw_frame_context->height = video_codec_context->height; - hw_frame_context->sw_format = AV_PIX_FMT_BGR0; + hw_frame_context->sw_format = AV_PIX_FMT_NV12; hw_frame_context->format = video_codec_context->pix_fmt; hw_frame_context->device_ref = device_ctx; hw_frame_context->device_ctx = (AVHWDeviceContext*)device_ctx->data; @@ -168,14 +174,37 @@ static bool ffmpeg_create_cuda_contexts(gsr_capture_nvfbc *cap_nvfbc, AVCodecCon return false; } + cap_nvfbc->cuda_stream = cuda_device_context->stream; video_codec_context->hw_device_ctx = av_buffer_ref(device_ctx); video_codec_context->hw_frames_ctx = av_buffer_ref(frame_context); return true; } +/* TODO: check for glx swap control extension string (GLX_EXT_swap_control, etc) */ +static void set_vertical_sync_enabled(gsr_egl *egl, int enabled) { + int result = 0; + + if(egl->glXSwapIntervalEXT) { + egl->glXSwapIntervalEXT(egl->x11.dpy, egl->x11.window, enabled ? 1 : 0); + } else if(egl->glXSwapIntervalMESA) { + result = egl->glXSwapIntervalMESA(enabled ? 1 : 0); + } else if(egl->glXSwapIntervalSGI) { + result = egl->glXSwapIntervalSGI(enabled ? 1 : 0); + } else { + static int warned = 0; + if (!warned) { + warned = 1; + fprintf(stderr, "gsr warning: setting vertical sync not supported\n"); + } + } + + if(result != 0) + fprintf(stderr, "gsr warning: setting vertical sync failed\n"); +} + static int gsr_capture_nvfbc_start(gsr_capture *cap, AVCodecContext *video_codec_context, AVFrame *frame) { gsr_capture_nvfbc *cap_nvfbc = cap->priv; - if(!gsr_cuda_load(&cap_nvfbc->cuda, cap_nvfbc->params.dpy, cap_nvfbc->params.overclock)) + if(!gsr_cuda_load(&cap_nvfbc->cuda, cap_nvfbc->params.egl->x11.dpy, cap_nvfbc->params.overclock)) return -1; if(!gsr_capture_nvfbc_load_library(cap)) { @@ -224,6 +253,9 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, AVCodecContext *video_codec NVFBC_CREATE_HANDLE_PARAMS create_params; memset(&create_params, 0, sizeof(create_params)); create_params.dwVersion = NVFBC_CREATE_HANDLE_PARAMS_VER; + create_params.bExternallyManagedContext = NVFBC_TRUE; + create_params.glxCtx = cap_nvfbc->params.egl->glx_context; + create_params.glxFBConfig = cap_nvfbc->params.egl->glx_fb_config; status = cap_nvfbc->nv_fbc_function_list.nvFBCCreateHandle(&cap_nvfbc->nv_fbc_handle, &create_params); if(status != NVFBC_SUCCESS) { @@ -255,8 +287,8 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, AVCodecContext *video_codec goto error_cleanup; } - uint32_t tracking_width = XWidthOfScreen(DefaultScreenOfDisplay(cap_nvfbc->params.dpy)); - uint32_t tracking_height = XHeightOfScreen(DefaultScreenOfDisplay(cap_nvfbc->params.dpy)); + uint32_t tracking_width = XWidthOfScreen(DefaultScreenOfDisplay(cap_nvfbc->params.egl->x11.dpy)); + uint32_t tracking_height = XHeightOfScreen(DefaultScreenOfDisplay(cap_nvfbc->params.egl->x11.dpy)); tracking_type = strcmp(cap_nvfbc->params.display_to_capture, "screen") == 0 ? NVFBC_TRACKING_SCREEN : NVFBC_TRACKING_OUTPUT; if(tracking_type == NVFBC_TRACKING_OUTPUT) { if(!status_params.bXRandRAvailable) { @@ -279,7 +311,7 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, AVCodecContext *video_codec NVFBC_CREATE_CAPTURE_SESSION_PARAMS create_capture_params; memset(&create_capture_params, 0, sizeof(create_capture_params)); create_capture_params.dwVersion = NVFBC_CREATE_CAPTURE_SESSION_PARAMS_VER; - create_capture_params.eCaptureType = NVFBC_CAPTURE_SHARED_CUDA; + create_capture_params.eCaptureType = NVFBC_CAPTURE_TO_GL; create_capture_params.bWithCursor = (!direct_capture || supports_direct_cursor) ? NVFBC_TRUE : NVFBC_FALSE; if(capture_region) create_capture_params.captureBox = (NVFBC_BOX){ x, y, width, height }; @@ -298,17 +330,17 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, AVCodecContext *video_codec } cap_nvfbc->capture_session_created = true; - NVFBC_TOCUDA_SETUP_PARAMS setup_params; - memset(&setup_params, 0, sizeof(setup_params)); - setup_params.dwVersion = NVFBC_TOCUDA_SETUP_PARAMS_VER; - setup_params.eBufferFormat = NVFBC_BUFFER_FORMAT_BGRA; + memset(&cap_nvfbc->setup_params, 0, sizeof(cap_nvfbc->setup_params)); + cap_nvfbc->setup_params.dwVersion = NVFBC_TOGL_SETUP_PARAMS_VER; + cap_nvfbc->setup_params.eBufferFormat = NVFBC_BUFFER_FORMAT_BGRA; - status = cap_nvfbc->nv_fbc_function_list.nvFBCToCudaSetUp(cap_nvfbc->nv_fbc_handle, &setup_params); + status = cap_nvfbc->nv_fbc_function_list.nvFBCToGLSetUp(cap_nvfbc->nv_fbc_handle, &cap_nvfbc->setup_params); if(status != NVFBC_SUCCESS) { fprintf(stderr, "gsr error: gsr_capture_nvfbc_start failed: %s\n", cap_nvfbc->nv_fbc_function_list.nvFBCGetLastErrorStr(cap_nvfbc->nv_fbc_handle)); goto error_cleanup; } + cap_nvfbc->base.video_codec_context = video_codec_context; if(capture_region) { video_codec_context->width = width & ~1; video_codec_context->height = height & ~1; @@ -320,12 +352,17 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, AVCodecContext *video_codec if(!ffmpeg_create_cuda_contexts(cap_nvfbc, video_codec_context)) goto error_cleanup; - // TODO: Remove - const int res = av_hwframe_get_buffer(video_codec_context->hw_frames_ctx, frame, 0); - if(res < 0) { - fprintf(stderr, "gsr error: gsr_capture_nvfbc_start: av_hwframe_get_buffer failed: %d\n", res); + gsr_cuda_context cuda_context = { + .cuda = &cap_nvfbc->cuda, + .cuda_graphics_resources = cap_nvfbc->cuda_graphics_resources, + .mapped_arrays = cap_nvfbc->mapped_arrays + }; + // TODO: Remove this, it creates shit we dont need + if(!gsr_capture_base_setup_cuda_textures(&cap_nvfbc->base, frame, &cuda_context, cap_nvfbc->params.egl, cap_nvfbc->params.color_range, GSR_SOURCE_COLOR_BGR, cap_nvfbc->params.hdr)) { goto error_cleanup; } + /* Disable vsync */ + set_vertical_sync_enabled(cap_nvfbc->params.egl, 0); return 0; @@ -351,6 +388,7 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, AVCodecContext *video_codec if(video_codec_context->hw_frames_ctx) av_buffer_unref(&video_codec_context->hw_frames_ctx); + gsr_capture_base_stop(&cap_nvfbc->base, cap_nvfbc->params.egl); gsr_cuda_unload(&cap_nvfbc->cuda); return -1; } @@ -380,39 +418,57 @@ static void gsr_capture_nvfbc_destroy_session(gsr_capture *cap) { static int gsr_capture_nvfbc_capture(gsr_capture *cap, AVFrame *frame) { gsr_capture_nvfbc *cap_nvfbc = cap->priv; - CUdeviceptr cu_device_ptr = 0; - NVFBC_FRAME_GRAB_INFO frame_info; memset(&frame_info, 0, sizeof(frame_info)); - NVFBC_TOCUDA_GRAB_FRAME_PARAMS grab_params; + NVFBC_TOGL_GRAB_FRAME_PARAMS grab_params; memset(&grab_params, 0, sizeof(grab_params)); - grab_params.dwVersion = NVFBC_TOCUDA_GRAB_FRAME_PARAMS_VER; - grab_params.dwFlags = NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT | NVFBC_TOCUDA_GRAB_FLAGS_FORCE_REFRESH;/* | NVFBC_TOCUDA_GRAB_FLAGS_FORCE_REFRESH;*/ + grab_params.dwVersion = NVFBC_TOGL_GRAB_FRAME_PARAMS_VER; + grab_params.dwFlags = NVFBC_TOGL_GRAB_FLAGS_NOWAIT | NVFBC_TOGL_GRAB_FLAGS_FORCE_REFRESH; // TODO: Remove NVFBC_TOGL_GRAB_FLAGS_FORCE_REFRESH grab_params.pFrameGrabInfo = &frame_info; - grab_params.pCUDADeviceBuffer = &cu_device_ptr; grab_params.dwTimeoutMs = 0; - NVFBCSTATUS status = cap_nvfbc->nv_fbc_function_list.nvFBCToCudaGrabFrame(cap_nvfbc->nv_fbc_handle, &grab_params); + NVFBCSTATUS status = cap_nvfbc->nv_fbc_function_list.nvFBCToGLGrabFrame(cap_nvfbc->nv_fbc_handle, &grab_params); if(status != NVFBC_SUCCESS) { fprintf(stderr, "gsr error: gsr_capture_nvfbc_capture failed: %s\n", cap_nvfbc->nv_fbc_function_list.nvFBCGetLastErrorStr(cap_nvfbc->nv_fbc_handle)); return -1; } - /* - *byte_size = frame_info.dwByteSize; + //cap_nvfbc->params.egl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + cap_nvfbc->params.egl->glClear(0); + + gsr_color_conversion_draw(&cap_nvfbc->base.color_conversion, cap_nvfbc->setup_params.dwTextures[grab_params.dwTextureIndex], + (vec2i){0, 0}, (vec2i){cap_nvfbc->base.video_codec_context->width, cap_nvfbc->base.video_codec_context->height}, + (vec2i){0, 0}, (vec2i){cap_nvfbc->base.video_codec_context->width, cap_nvfbc->base.video_codec_context->height}, + 0.0f, false); + + cap_nvfbc->params.egl->glXSwapBuffers(cap_nvfbc->params.egl->x11.dpy, cap_nvfbc->params.egl->x11.window); + + // TODO: HDR is broken + const int div[2] = {1, 2}; // divide UV texture size by 2 because chroma is half size + for(int i = 0; i < 2; ++i) { + CUDA_MEMCPY2D memcpy_struct; + memcpy_struct.srcXInBytes = 0; + memcpy_struct.srcY = 0; + memcpy_struct.srcMemoryType = CU_MEMORYTYPE_ARRAY; + + memcpy_struct.dstXInBytes = 0; + memcpy_struct.dstY = 0; + memcpy_struct.dstMemoryType = CU_MEMORYTYPE_DEVICE; + + memcpy_struct.srcArray = cap_nvfbc->mapped_arrays[i]; + memcpy_struct.srcPitch = cap_nvfbc->base.video_codec_context->width / div[i]; + memcpy_struct.dstDevice = (CUdeviceptr)frame->data[i]; + memcpy_struct.dstPitch = frame->linesize[i]; + memcpy_struct.WidthInBytes = cap_nvfbc->base.video_codec_context->width * (cap_nvfbc->params.hdr ? 2 : 1); + memcpy_struct.Height = cap_nvfbc->base.video_codec_context->height / div[i]; + // TODO: Remove this copy if possible + cap_nvfbc->cuda.cuMemcpy2DAsync_v2(&memcpy_struct, cap_nvfbc->cuda_stream); + } - TODO: Check bIsNewFrame - TODO: Check dwWidth and dwHeight and update size in video output in ffmpeg. This can happen when xrandr is used to change monitor resolution - */ + // TODO: needed? + cap_nvfbc->cuda.cuStreamSynchronize(cap_nvfbc->cuda_stream); - frame->data[0] = (uint8_t*)cu_device_ptr; - //frame->data[1] = (uint8_t*)cu_device_ptr; - //frame->data[2] = (uint8_t*)cu_device_ptr; - frame->linesize[0] = frame->width * 4; - // TODO: Use these when outputting yuv444 by changing nvfbc color to YUV444P and sw_format to YUV444P - //frame->linesize[1] = frame->width * 1; - //frame->linesize[2] = frame->width * 1; return 0; } @@ -424,6 +480,7 @@ static void gsr_capture_nvfbc_destroy(gsr_capture *cap, AVCodecContext *video_co if(video_codec_context->hw_frames_ctx) av_buffer_unref(&video_codec_context->hw_frames_ctx); if(cap_nvfbc) { + gsr_capture_base_stop(&cap_nvfbc->base, cap_nvfbc->params.egl); gsr_cuda_unload(&cap_nvfbc->cuda); dlclose(cap_nvfbc->library); free((void*)cap_nvfbc->params.display_to_capture); diff --git a/src/capture/xcomposite_cuda.c b/src/capture/xcomposite_cuda.c index 99c262d..1194edb 100644 --- a/src/capture/xcomposite_cuda.c +++ b/src/capture/xcomposite_cuda.c @@ -397,8 +397,8 @@ static bool gsr_capture_xcomposite_cuda_should_stop(gsr_capture *cap, bool *err) static int gsr_capture_xcomposite_cuda_capture(gsr_capture *cap, AVFrame *frame) { gsr_capture_xcomposite_cuda *cap_xcomp = cap->priv; - cap_xcomp->params.egl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - cap_xcomp->params.egl->glClear(GL_COLOR_BUFFER_BIT); + //cap_xcomp->params.egl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + cap_xcomp->params.egl->glClear(0); vec2i source_pos = { 0, 0 }; vec2i source_size = cap_xcomp->texture_size; diff --git a/src/capture/xcomposite_vaapi.c b/src/capture/xcomposite_vaapi.c index 941467a..134c8bb 100644 --- a/src/capture/xcomposite_vaapi.c +++ b/src/capture/xcomposite_vaapi.c @@ -195,8 +195,8 @@ static int gsr_capture_xcomposite_vaapi_start(gsr_capture *cap, AVCodecContext * static void gsr_capture_xcomposite_vaapi_tick(gsr_capture *cap, AVCodecContext *video_codec_context) { gsr_capture_xcomposite_vaapi *cap_xcomp = cap->priv; - cap_xcomp->params.egl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - cap_xcomp->params.egl->glClear(GL_COLOR_BUFFER_BIT); + //cap_xcomp->params.egl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + cap_xcomp->params.egl->glClear(0); bool init_new_window = false; while(XPending(cap_xcomp->params.egl->x11.dpy)) { diff --git a/src/color_conversion.c b/src/color_conversion.c index 6a3972f..dad58ff 100644 --- a/src/color_conversion.c +++ b/src/color_conversion.c @@ -370,6 +370,20 @@ void gsr_color_conversion_deinit(gsr_color_conversion *self) { self->params.egl = NULL; } +static void gsr_color_conversion_swizzle_texture_source(gsr_color_conversion *self) { + if(self->params.source_color == GSR_SOURCE_COLOR_BGR) { + const int swizzle_mask[] = { GL_BLUE, GL_GREEN, GL_RED, 1 }; + self->params.egl->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle_mask); + } +} + +static void gsr_color_conversion_swizzle_reset(gsr_color_conversion *self) { + if(self->params.source_color == GSR_SOURCE_COLOR_BGR) { + const int swizzle_mask[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA }; + self->params.egl->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle_mask); + } +} + /* |source_pos| is in pixel coordinates and |source_size| */ void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_id, vec2i source_pos, vec2i source_size, vec2i texture_pos, vec2i texture_size, float rotation, bool external_texture) { // TODO: Remove this crap @@ -432,6 +446,8 @@ void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_ -1.0f + 0.0f + size_norm.x, -1.0f + 0.0f + size_norm.y, texture_pos_norm.x + texture_size_norm.x, texture_pos_norm.y + texture_size_norm.y }; + gsr_color_conversion_swizzle_texture_source(self); + self->params.egl->glBindVertexArray(self->vertex_array_object_id); self->params.egl->glViewport(0, 0, dest_texture_size.x, dest_texture_size.y); @@ -469,6 +485,8 @@ void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_ gsr_shader_use_none(&self->shaders[0]); self->params.egl->glBindTexture(texture_target, 0); self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, 0); + + gsr_color_conversion_swizzle_reset(self); } void gsr_color_conversion_clear(gsr_color_conversion *self) { diff --git a/src/egl.c b/src/egl.c index 7971dbd..884a7a6 100644 --- a/src/egl.c +++ b/src/egl.c @@ -1,5 +1,6 @@ #include "../include/egl.h" #include "../include/library_loader.h" +#include "../include/utils.h" #include #include #include @@ -11,7 +12,9 @@ #include #include -// Move this shit to a separate wayland file, and have a separate file for x11. +// TODO: rename gsr_egl to something else since this includes both egl and eglx and in the future maybe vulkan too + +// TODO: Move this shit to a separate wayland file, and have a separate file for x11. static void output_handle_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t phys_width, int32_t phys_height, @@ -131,6 +134,52 @@ static void reset_cap_nice(void) { cap_free(caps); } +#define GLX_DRAWABLE_TYPE 0x8010 +#define GLX_RENDER_TYPE 0x8011 +#define GLX_RGBA_BIT 0x00000001 +#define GLX_WINDOW_BIT 0x00000001 +#define GLX_PIXMAP_BIT 0x00000002 +#define GLX_BIND_TO_TEXTURE_RGBA_EXT 0x20D1 +#define GLX_BIND_TO_TEXTURE_TARGETS_EXT 0x20D3 +#define GLX_TEXTURE_2D_BIT_EXT 0x00000002 +#define GLX_DOUBLEBUFFER 5 +#define GLX_RED_SIZE 8 +#define GLX_GREEN_SIZE 9 +#define GLX_BLUE_SIZE 10 +#define GLX_ALPHA_SIZE 11 +#define GLX_DEPTH_SIZE 12 +#define GLX_RGBA_TYPE 0x8014 + +#define GLX_CONTEXT_PRIORITY_LEVEL_EXT 0x3100 +#define GLX_CONTEXT_PRIORITY_HIGH_EXT 0x3101 +#define GLX_CONTEXT_PRIORITY_MEDIUM_EXT 0x3102 +#define GLX_CONTEXT_PRIORITY_LOW_EXT 0x3103 + +static GLXFBConfig glx_fb_config_choose(gsr_egl *self) { + const int glx_visual_attribs[] = { + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + // TODO: + //GLX_BIND_TO_TEXTURE_RGBA_EXT, 1, + //GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, + GLX_DOUBLEBUFFER, True, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 0, + GLX_DEPTH_SIZE, 0, + None, None + }; + + // TODO: Cleanup + int c = 0; + GLXFBConfig *fb_configs = self->glXChooseFBConfig(self->x11.dpy, DefaultScreen(self->x11.dpy), glx_visual_attribs, &c); + if(c == 0 || !fb_configs) + return NULL; + + return fb_configs[0]; +} + // TODO: Create egl context without surface (in other words, x11/wayland agnostic, doesn't require x11/wayland dependency) static bool gsr_egl_create_window(gsr_egl *self, bool wayland) { EGLConfig ecfg; @@ -139,13 +188,13 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) { const int32_t attr[] = { EGL_BUFFER_SIZE, 24, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, - EGL_NONE + EGL_NONE, EGL_NONE }; const int32_t ctxattr[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG, /* requires cap_sys_nice, ignored otherwise */ - EGL_NONE + EGL_NONE, EGL_NONE }; if(wayland) { @@ -215,7 +264,7 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) { } if(!self->eglMakeCurrent(self->egl_display, self->egl_surface, self->egl_surface, self->egl_context)) { - fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to make context current\n"); + fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to make egl context current\n"); goto fail; } @@ -228,6 +277,50 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) { return false; } +static bool gsr_egl_switch_to_glx_context(gsr_egl *self) { + // TODO: Cleanup + + if(self->egl_context) { + self->eglMakeCurrent(self->egl_display, NULL, NULL, NULL); + self->eglDestroyContext(self->egl_display, self->egl_context); + self->egl_context = NULL; + } + + if(self->egl_surface) { + self->eglDestroySurface(self->egl_display, self->egl_surface); + self->egl_surface = NULL; + } + + if(self->egl_display) { + self->eglTerminate(self->egl_display); + self->egl_display = NULL; + } + + self->glx_fb_config = glx_fb_config_choose(self); + if(!self->glx_fb_config) { + fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to find a suitable fb config\n"); + goto fail; + } + + // TODO: + //self->glx_context = self->glXCreateContextAttribsARB(self->x11.dpy, self->glx_fb_config, NULL, True, context_attrib_list); + self->glx_context = self->glXCreateNewContext(self->x11.dpy, self->glx_fb_config, GLX_RGBA_TYPE, NULL, True); + if(!self->glx_context) { + fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to create glx context\n"); + goto fail; + } + + if(!self->glXMakeContextCurrent(self->x11.dpy, self->x11.window, self->x11.window, self->glx_context)) { + fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to make glx context current\n"); + goto fail; + } + + return true; + + fail: + return false; +} + static bool gsr_egl_load_egl(gsr_egl *self, void *library) { dlsym_assign required_dlsym[] = { { (void**)&self->eglGetError, "eglGetError" }, @@ -271,6 +364,35 @@ static bool gsr_egl_proc_load_egl(gsr_egl *self) { return true; } +static bool gsr_egl_load_glx(gsr_egl *self, void *library) { + dlsym_assign required_dlsym[] = { + { (void**)&self->glXGetProcAddress, "glXGetProcAddress" }, + { (void**)&self->glXChooseFBConfig, "glXChooseFBConfig" }, + { (void**)&self->glXMakeContextCurrent, "glXMakeContextCurrent" }, + { (void**)&self->glXCreateNewContext, "glXCreateNewContext" }, + { (void**)&self->glXSwapBuffers, "glXSwapBuffers" }, + + { NULL, NULL } + }; + + if(!dlsym_load_list(library, required_dlsym)) { + fprintf(stderr, "gsr error: gsr_egl_load failed: missing required symbols in libGLX.so.0\n"); + return false; + } + + self->glXCreateContextAttribsARB = (FUNC_glXCreateContextAttribsARB)self->glXGetProcAddress((const unsigned char*)"glXCreateContextAttribsARB"); + if(!self->glXCreateContextAttribsARB) { + fprintf(stderr, "gsr error: gsr_egl_load_glx failed: could not find glXCreateContextAttribsARB\n"); + return false; + } + + self->glXSwapIntervalEXT = (FUNC_glXSwapIntervalEXT)self->glXGetProcAddress((const unsigned char*)"glXSwapIntervalEXT"); + self->glXSwapIntervalMESA = (FUNC_glXSwapIntervalMESA)self->glXGetProcAddress((const unsigned char*)"glXSwapIntervalMESA"); + self->glXSwapIntervalSGI = (FUNC_glXSwapIntervalSGI)self->glXGetProcAddress((const unsigned char*)"glXSwapIntervalSGI"); + + return true; +} + static bool gsr_egl_load_gl(gsr_egl *self, void *library) { dlsym_assign required_dlsym[] = { { (void**)&self->glGetError, "glGetError" }, @@ -283,6 +405,7 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) { { (void**)&self->glDeleteTextures, "glDeleteTextures" }, { (void**)&self->glBindTexture, "glBindTexture" }, { (void**)&self->glTexParameteri, "glTexParameteri" }, + { (void**)&self->glTexParameteriv, "glTexParameteriv" }, { (void**)&self->glGetTexLevelParameteriv, "glGetTexLevelParameteriv" }, { (void**)&self->glTexImage2D, "glTexImage2D" }, { (void**)&self->glCopyImageSubData, "glCopyImageSubData" }, @@ -324,6 +447,7 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) { { (void**)&self->glGetUniformLocation, "glGetUniformLocation" }, { (void**)&self->glUniform1f, "glUniform1f" }, { (void**)&self->glUniform2f, "glUniform2f" }, + { (void**)&self->glDebugMessageCallback, "glDebugMessageCallback" }, { NULL, NULL } }; @@ -336,30 +460,56 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) { return true; } -bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland) { +// #define GL_DEBUG_TYPE_ERROR 0x824C +// static void debug_callback( unsigned int source, +// unsigned int type, +// unsigned int id, +// unsigned int severity, +// int length, +// const char* message, +// const void* userParam ) +// { +// (void)source; +// (void)id; +// (void)length; +// (void)userParam; +// fprintf( stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", +// ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ), +// type, severity, message ); +// } + +bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland, bool is_monitor_capture) { + (void)is_monitor_capture; memset(self, 0, sizeof(gsr_egl)); self->x11.dpy = dpy; - - void *egl_lib = NULL; - void *gl_lib = NULL; + self->context_type = GSR_GL_CONTEXT_TYPE_EGL; dlerror(); /* clear */ - egl_lib = dlopen("libEGL.so.1", RTLD_LAZY); - if(!egl_lib) { + self->egl_library = dlopen("libEGL.so.1", RTLD_LAZY); + if(!self->egl_library) { fprintf(stderr, "gsr error: gsr_egl_load: failed to load libEGL.so.1, error: %s\n", dlerror()); goto fail; } - gl_lib = dlopen("libGL.so.1", RTLD_LAZY); - if(!egl_lib) { + self->glx_library = dlopen("libGLX.so.0", RTLD_LAZY); + if(!self->glx_library) { + fprintf(stderr, "gsr error: gsr_egl_load: failed to load libGLX.so.0, error: %s\n", dlerror()); + goto fail; + } + + self->gl_library = dlopen("libGL.so.1", RTLD_LAZY); + if(!self->egl_library) { fprintf(stderr, "gsr error: gsr_egl_load: failed to load libGL.so.1, error: %s\n", dlerror()); goto fail; } - if(!gsr_egl_load_egl(self, egl_lib)) + if(!gsr_egl_load_egl(self, self->egl_library)) goto fail; - if(!gsr_egl_load_gl(self, gl_lib)) + if(!gsr_egl_load_glx(self, self->glx_library)) + goto fail; + + if(!gsr_egl_load_gl(self, self->gl_library)) goto fail; if(!gsr_egl_proc_load_egl(self)) @@ -368,24 +518,32 @@ bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland) { if(!gsr_egl_create_window(self, wayland)) goto fail; + if(!gl_get_gpu_info(self, &self->gpu_info)) + goto fail; + + /* Nvfbc requires glx */ + if(!wayland && is_monitor_capture && self->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA) { + self->context_type = GSR_GL_CONTEXT_TYPE_GLX; + if(!gsr_egl_switch_to_glx_context(self)) + goto fail; + } + self->glEnable(GL_BLEND); self->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - self->egl_library = egl_lib; - self->gl_library = gl_lib; + //self->glEnable(GL_DEBUG_OUTPUT); + //self->glDebugMessageCallback(debug_callback, NULL); + return true; fail: - if(egl_lib) - dlclose(egl_lib); - if(gl_lib) - dlclose(gl_lib); - memset(self, 0, sizeof(gsr_egl)); + gsr_egl_unload(self); return false; } void gsr_egl_unload(gsr_egl *self) { if(self->egl_context) { + self->eglMakeCurrent(self->egl_display, NULL, NULL, NULL); self->eglDestroyContext(self->egl_display, self->egl_context); self->egl_context = NULL; } @@ -448,6 +606,11 @@ void gsr_egl_unload(gsr_egl *self) { self->egl_library = NULL; } + if(self->glx_library) { + dlclose(self->glx_library); + self->glx_library = NULL; + } + if(self->gl_library) { dlclose(self->gl_library); self->gl_library = NULL; diff --git a/src/main.cpp b/src/main.cpp index 31d69f4..6f66a7c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -808,13 +808,10 @@ static void usage_full() { usage_header(); fprintf(stderr, "\n"); fprintf(stderr, "OPTIONS:\n"); - fprintf(stderr, " -w Window id to record, a display (monitor name), \"screen\", \"screen-direct\", \"screen-direct-force\" or \"focused\".\n"); - fprintf(stderr, " If this is \"screen\", \"screen-direct\" or \"screen-direct-force\" then all displays are recorded.\n"); - fprintf(stderr, " If this is \"focused\" then the currently focused window is recorded. When recording the focused window then the -s option has to be used as well.\n"); - fprintf(stderr, " \"screen-direct\"/\"screen-direct-force\" skips one texture copy for fullscreen applications so it may lead to better performance and it works with VRR monitors\n"); - fprintf(stderr, " when recording fullscreen application but may break some applications, such as mpv in fullscreen mode or might cause games to freeze/crash because of nvidia driver issues.\n"); - fprintf(stderr, " Direct mode doesn't capture cursor either.\n"); - fprintf(stderr, " \"screen-direct-force\" is not recommended unless you use a VRR monitor and you are aware that using this option can cause games to freeze/crash or other issues.\n"); + fprintf(stderr, " -w Window id to record, a display (monitor name), \"screen\", \"screen-direct-force\" or \"focused\".\n"); + fprintf(stderr, " If this is \"screen\" or \"screen-direct-force\" then all monitors are recorded.\n"); + fprintf(stderr, " \"screen-direct-force\" is not recommended unless you use a VRR monitor on Nvidia X11 and you are aware that using this option can cause games to freeze/crash or other issues because of Nvidia driver issues.\n"); + fprintf(stderr, " \"screen-direct-force\" option is only available on Nvidia X11. VRR works without this option on other systems.\n"); fprintf(stderr, "\n"); fprintf(stderr, " -c Container format for output file, for example mp4, or flv. Only required if no output file is specified or if recording in replay buffer mode.\n"); fprintf(stderr, " If an output file is specified and -c is not used then the container format is determined from the output filename extension.\n"); @@ -1372,22 +1369,18 @@ static void list_supported_video_codecs() { wayland = is_xwayland(dpy); gsr_egl egl; - if(!gsr_egl_load(&egl, dpy, wayland)) { + if(!gsr_egl_load(&egl, dpy, wayland, false)) { fprintf(stderr, "gsr error: failed to load opengl\n"); _exit(1); } - gsr_gpu_info gpu_inf; - if(!gl_get_gpu_info(&egl, &gpu_inf)) - _exit(2); - gsr_egl_unload(&egl); if(dpy) XCloseDisplay(dpy); char card_path[128]; card_path[0] = '\0'; - if(wayland || gpu_inf.vendor != GSR_GPU_VENDOR_NVIDIA) { + if(wayland || egl.gpu_info.vendor != GSR_GPU_VENDOR_NVIDIA) { // TODO: Allow specifying another card, and in other places if(!gsr_get_valid_card_path(card_path)) { fprintf(stderr, "Error: no /dev/dri/cardX device found\n"); @@ -1398,11 +1391,11 @@ static void list_supported_video_codecs() { av_log_set_level(AV_LOG_FATAL); // TODO: Output hdr - if(find_h264_encoder(gpu_inf.vendor, card_path)) + if(find_h264_encoder(egl.gpu_info.vendor, card_path)) puts("h264"); - if(find_h265_encoder(gpu_inf.vendor, card_path)) + if(find_h265_encoder(egl.gpu_info.vendor, card_path)) puts("hevc"); - if(find_av1_encoder(gpu_inf.vendor, card_path)) + if(find_av1_encoder(egl.gpu_info.vendor, card_path)) puts("av1"); fflush(stdout); @@ -1437,7 +1430,7 @@ static gsr_capture* create_capture_impl(const char *window_str, const char *scre follow_focused = true; } else if(contains_non_hex_number(window_str)) { - if(wayland || gpu_inf.vendor != GSR_GPU_VENDOR_NVIDIA) { + if(wayland || egl.gpu_info.vendor != GSR_GPU_VENDOR_NVIDIA) { if(strcmp(window_str, "screen") == 0) { FirstOutputCallback first_output; first_output.output_name = NULL; @@ -1473,7 +1466,7 @@ static gsr_capture* create_capture_impl(const char *window_str, const char *scre } } - if(gpu_inf.vendor == GSR_GPU_VENDOR_NVIDIA) { + if(egl.gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA) { if(wayland) { gsr_capture_kms_cuda_params kms_params; kms_params.egl = &egl; @@ -1500,14 +1493,15 @@ static gsr_capture* create_capture_impl(const char *window_str, const char *scre } gsr_capture_nvfbc_params nvfbc_params; - nvfbc_params.dpy = egl.x11.dpy; + nvfbc_params.egl = &egl; nvfbc_params.display_to_capture = capture_target; nvfbc_params.fps = fps; nvfbc_params.pos = { 0, 0 }; nvfbc_params.size = { 0, 0 }; nvfbc_params.direct_capture = direct_capture; nvfbc_params.overclock = overclock; - gsr_egl_unload(&egl); + nvfbc_params.hdr = video_codec_is_hdr(video_codec); + nvfbc_params.color_range = color_range; capture = gsr_capture_nvfbc_create(&nvfbc_params); if(!capture) _exit(1); @@ -1538,7 +1532,7 @@ static gsr_capture* create_capture_impl(const char *window_str, const char *scre } if(!capture) { - switch(gpu_inf.vendor) { + switch(egl.gpu_info.vendor) { case GSR_GPU_VENDOR_AMD: case GSR_GPU_VENDOR_INTEL: { gsr_capture_xcomposite_vaapi_params xcomposite_params; @@ -1836,6 +1830,8 @@ int main(int argc, char **argv) { replay_buffer_size_secs += 3; // Add a few seconds to account of lost packets because of non-keyframe packets skipped } + const char *window_str = strdup(args["-w"].value()); + bool wayland = false; Display *dpy = XOpenDisplay(nullptr); if (!dpy) { @@ -1849,32 +1845,30 @@ int main(int argc, char **argv) { if(!wayland) wayland = is_xwayland(dpy); + const bool is_monitor_capture = strcmp(window_str, "focused") != 0 && contains_non_hex_number(window_str); gsr_egl egl; - if(!gsr_egl_load(&egl, dpy, wayland)) { + if(!gsr_egl_load(&egl, dpy, wayland, is_monitor_capture)) { fprintf(stderr, "gsr error: failed to load opengl\n"); _exit(1); } - gsr_gpu_info gpu_inf; bool very_old_gpu = false; - if(!gl_get_gpu_info(&egl, &gpu_inf)) - _exit(2); - if(gpu_inf.vendor == GSR_GPU_VENDOR_NVIDIA && gpu_inf.gpu_version != 0 && gpu_inf.gpu_version < 900) { + if(egl.gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA && egl.gpu_info.gpu_version != 0 && egl.gpu_info.gpu_version < 900) { fprintf(stderr, "Info: your gpu appears to be very old (older than maxwell architecture). Switching to lower preset\n"); very_old_gpu = true; } - if(gpu_inf.vendor != GSR_GPU_VENDOR_NVIDIA && overclock) { + if(egl.gpu_info.vendor != GSR_GPU_VENDOR_NVIDIA && overclock) { fprintf(stderr, "Info: overclock option has no effect on amd/intel, ignoring option\n"); } - if(gpu_inf.vendor == GSR_GPU_VENDOR_NVIDIA && overclock && wayland) { + if(egl.gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA && overclock && wayland) { fprintf(stderr, "Info: overclocking is not possible on nvidia on wayland, ignoring option\n"); } egl.card_path[0] = '\0'; - if(wayland || gpu_inf.vendor != GSR_GPU_VENDOR_NVIDIA) { + if(wayland || egl.gpu_info.vendor != GSR_GPU_VENDOR_NVIDIA) { // TODO: Allow specifying another card, and in other places if(!gsr_get_valid_card_path(egl.card_path)) { fprintf(stderr, "Error: no /dev/dri/cardX device found\n"); @@ -1914,7 +1908,6 @@ int main(int argc, char **argv) { } const char *screen_region = args["-s"].value(); - const char *window_str = strdup(args["-w"].value()); if(screen_region && strcmp(window_str, "focused") != 0) { fprintf(stderr, "Error: option -s is only available when using -w focused\n"); @@ -1979,7 +1972,7 @@ int main(int argc, char **argv) { file_extension = file_extension.substr(0, comma_index); } - if(gpu_inf.vendor != GSR_GPU_VENDOR_NVIDIA && file_extension == "mkv" && strcmp(video_codec_to_use, "h264") == 0) { + if(egl.gpu_info.vendor != GSR_GPU_VENDOR_NVIDIA && file_extension == "mkv" && strcmp(video_codec_to_use, "h264") == 0) { video_codec_to_use = "hevc"; video_codec = VideoCodec::HEVC; fprintf(stderr, "Warning: video codec was forcefully set to hevc because mkv container is used and mesa (AMD and Intel driver) does not support h264 in mkv files\n"); @@ -2017,8 +2010,8 @@ int main(int argc, char **argv) { const bool video_codec_auto = strcmp(video_codec_to_use, "auto") == 0; if(video_codec_auto) { - if(gpu_inf.vendor == GSR_GPU_VENDOR_INTEL) { - const AVCodec *h264_codec = find_h264_encoder(gpu_inf.vendor, egl.card_path); + if(egl.gpu_info.vendor == GSR_GPU_VENDOR_INTEL) { + const AVCodec *h264_codec = find_h264_encoder(egl.gpu_info.vendor, egl.card_path); if(!h264_codec) { fprintf(stderr, "Info: using hevc encoder because a codec was not specified and your gpu does not support h264\n"); video_codec_to_use = "hevc"; @@ -2029,7 +2022,7 @@ int main(int argc, char **argv) { video_codec = VideoCodec::H264; } } else { - const AVCodec *h265_codec = find_h265_encoder(gpu_inf.vendor, egl.card_path); + const AVCodec *h265_codec = find_h265_encoder(egl.gpu_info.vendor, egl.card_path); if(h265_codec && fps > 60) { fprintf(stderr, "Warning: recording at higher fps than 60 with hevc might result in recording at a very low fps. If this happens, switch to h264 or av1\n"); @@ -2061,15 +2054,15 @@ int main(int argc, char **argv) { const AVCodec *video_codec_f = nullptr; switch(video_codec) { case VideoCodec::H264: - video_codec_f = find_h264_encoder(gpu_inf.vendor, egl.card_path); + video_codec_f = find_h264_encoder(egl.gpu_info.vendor, egl.card_path); break; case VideoCodec::HEVC: case VideoCodec::HEVC_HDR: - video_codec_f = find_h265_encoder(gpu_inf.vendor, egl.card_path); + video_codec_f = find_h265_encoder(egl.gpu_info.vendor, egl.card_path); break; case VideoCodec::AV1: case VideoCodec::AV1_HDR: - video_codec_f = find_av1_encoder(gpu_inf.vendor, egl.card_path); + video_codec_f = find_av1_encoder(egl.gpu_info.vendor, egl.card_path); break; } @@ -2079,7 +2072,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Warning: selected video codec h264 is not supported, trying hevc instead\n"); video_codec_to_use = "hevc"; video_codec = VideoCodec::HEVC; - video_codec_f = find_h265_encoder(gpu_inf.vendor, egl.card_path); + video_codec_f = find_h265_encoder(egl.gpu_info.vendor, egl.card_path); break; } case VideoCodec::HEVC: @@ -2087,7 +2080,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Warning: selected video codec hevc is not supported, trying h264 instead\n"); video_codec_to_use = "h264"; video_codec = VideoCodec::H264; - video_codec_f = find_h264_encoder(gpu_inf.vendor, egl.card_path); + video_codec_f = find_h264_encoder(egl.gpu_info.vendor, egl.card_path); break; } case VideoCodec::AV1: @@ -2095,7 +2088,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Warning: selected video codec av1 is not supported, trying h264 instead\n"); video_codec_to_use = "h264"; video_codec = VideoCodec::H264; - video_codec_f = find_h264_encoder(gpu_inf.vendor, egl.card_path); + video_codec_f = find_h264_encoder(egl.gpu_info.vendor, egl.card_path); break; } } @@ -2132,7 +2125,7 @@ int main(int argc, char **argv) { _exit(2); } - gsr_capture *capture = create_capture_impl(window_str, screen_region, wayland, gpu_inf, egl, fps, overclock, video_codec, color_range); + gsr_capture *capture = create_capture_impl(window_str, screen_region, wayland, egl.gpu_info, egl, fps, overclock, video_codec, color_range); const bool is_livestream = is_livestream_path(filename); // (Some?) livestreaming services require at least one audio track to work. @@ -2159,7 +2152,7 @@ int main(int argc, char **argv) { std::vector audio_tracks; const bool hdr = video_codec_is_hdr(video_codec); - AVCodecContext *video_codec_context = create_video_codec_context(gpu_inf.vendor == GSR_GPU_VENDOR_NVIDIA ? AV_PIX_FMT_CUDA : AV_PIX_FMT_VAAPI, quality, fps, video_codec_f, is_livestream, gpu_inf.vendor, framerate_mode, hdr, color_range); + AVCodecContext *video_codec_context = create_video_codec_context(egl.gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA ? AV_PIX_FMT_CUDA : AV_PIX_FMT_VAAPI, quality, fps, video_codec_f, is_livestream, egl.gpu_info.vendor, framerate_mode, hdr, color_range); if(replay_buffer_size_secs == -1) video_stream = create_stream(av_format_context, video_codec_context); @@ -2183,7 +2176,7 @@ int main(int argc, char **argv) { _exit(capture_result); } - open_video(video_codec_context, quality, very_old_gpu, gpu_inf.vendor, pixel_format, hdr); + open_video(video_codec_context, quality, very_old_gpu, egl.gpu_info.vendor, pixel_format, hdr); if(video_stream) avcodec_parameters_from_context(video_stream->codecpar, video_codec_context); -- cgit v1.2.3