2026-01-06 09:48:38 +08:00
#include "common/percent.h"
#include "common/printing.h"
#include "common/jsonconfig.h"
#include "common/temps.h"
#include "common/size.h"
#include "common/frequency.h"
2026-05-28 15:58:08 +08:00
#include "common/strutil.h"
2023-03-26 20:42:18 +08:00
#include "detection/host/host.h"
#include "detection/gpu/gpu.h"
#include "modules/gpu/gpu.h"
#include <stdlib.h>
2026-09-22 14:49:43 +08:00
// `hideType` is a filter, and FF_GPU_TYPE_NONE means "hide nothing" -- no detected GPU ever has
// that type. Both the text and the JSON path must apply it, or the two disagree about which GPUs
2026-09-24 10:11:59 +08:00
// exist.
2026-09-22 14:49:43 +08:00
static bool gpuIsHidden ( const FFGPUOptions * options , const FFGPUResult * gpu ) {
return options -> hideType != FF_GPU_TYPE_NONE && gpu -> type == options -> hideType ;
}
static void selectGPUs ( const FFGPUOptions * options , const FFlist * gpus , FFlist * selectedGPUs ) {
FF_LIST_FOR_EACH ( FFGPUResult , gpu , * gpus ) {
if ( ! gpuIsHidden ( options , gpu )) {
* FF_LIST_ADD ( const FFGPUResult * , * selectedGPUs ) = gpu ;
}
}
}
2026-03-29 09:28:49 +08:00
static void printGPUResult ( FFGPUOptions * options , uint8_t index , const FFGPUResult * gpu ) {
2023-06-24 19:49:53 +08:00
const char * type ;
2026-03-29 09:28:49 +08:00
switch ( gpu -> type ) {
case FF_GPU_TYPE_INTEGRATED :
type = "Integrated" ;
break ;
case FF_GPU_TYPE_DISCRETE :
type = "Discrete" ;
break ;
default :
type = "Unknown" ;
break ;
2023-06-24 19:49:53 +08:00
}
2025-02-24 14:36:20 +08:00
FFPercentageTypeFlags percentType = options -> percent . type == 0 ? instance . config . display . percentType : options -> percent . type ;
2026-03-29 09:28:49 +08:00
if ( options -> moduleArgs . outputFormat . length == 0 ) {
2026-08-14 23:33:54 +08:00
ffPrintLogoAndKey ( FF_MODULE_GET_DISPLAY_NAME ( GPU ), index , & options -> moduleArgs , FF_PRINT_TYPE_DEFAULT );
2023-03-26 20:42:18 +08:00
2024-08-15 13:58:02 +08:00
FF_STRBUF_AUTO_DESTROY output = ffStrbufCreate ();
2023-03-26 20:42:18 +08:00
2026-03-29 09:28:49 +08:00
if ( gpu -> vendor . length > 0 && ! ffStrbufStartsWithIgnCase ( & gpu -> name , & gpu -> vendor )) {
2023-03-26 20:42:18 +08:00
ffStrbufAppend ( & output , & gpu -> vendor );
ffStrbufAppendC ( & output , ' ' );
}
ffStrbufAppend ( & output , & gpu -> name );
2026-03-29 09:28:49 +08:00
if ( gpu -> coreCount != FF_GPU_CORE_COUNT_UNSET ) {
2023-03-26 20:42:18 +08:00
ffStrbufAppendF ( & output , " (%d)" , gpu -> coreCount );
2026-03-29 09:28:49 +08:00
}
2023-03-26 20:42:18 +08:00
2026-03-29 09:28:49 +08:00
if ( gpu -> frequency > 0 ) {
2024-07-17 14:52:35 +08:00
ffStrbufAppendS ( & output , " @ " );
2025-07-30 14:30:36 +08:00
ffFreqAppendNum ( gpu -> frequency , & output );
2024-07-17 14:52:35 +08:00
}
2023-12-20 14:45:27 +08:00
2026-03-29 09:28:49 +08:00
if ( gpu -> temperature != FF_GPU_TEMP_UNSET ) {
2023-07-27 15:58:49 +08:00
ffStrbufAppendS ( & output , " - " );
2024-05-08 15:25:59 +08:00
ffTempsAppendNum ( gpu -> temperature , & output , options -> tempConfig , & options -> moduleArgs );
2023-07-27 15:58:49 +08:00
}
2023-03-26 20:42:18 +08:00
2026-03-29 09:28:49 +08:00
if ( gpu -> dedicated . total != FF_GPU_VMEM_SIZE_UNSET && gpu -> dedicated . total != 0 ) {
2023-03-26 20:42:18 +08:00
ffStrbufAppendS ( & output , " (" );
2026-03-29 09:28:49 +08:00
if ( ! ( percentType & FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT )) {
if ( gpu -> dedicated . used != FF_GPU_VMEM_SIZE_UNSET ) {
2025-07-30 14:30:36 +08:00
ffSizeAppendNum ( gpu -> dedicated . used , & output );
2025-02-24 14:36:20 +08:00
ffStrbufAppendS ( & output , " / " );
}
2025-07-30 14:30:36 +08:00
ffSizeAppendNum ( gpu -> dedicated . total , & output );
2023-03-26 20:42:18 +08:00
}
2026-03-29 09:28:49 +08:00
if ( gpu -> dedicated . used != FF_GPU_VMEM_SIZE_UNSET ) {
2025-02-24 14:36:20 +08:00
double percent = ( double ) gpu -> dedicated . used / ( double ) gpu -> dedicated . total * 100.0 ;
2026-03-29 09:28:49 +08:00
if ( percentType & FF_PERCENTAGE_TYPE_NUM_BIT ) {
2025-02-24 14:36:20 +08:00
ffStrbufAppendS ( & output , ", " );
ffPercentAppendNum ( & output , percent , options -> percent , false , & options -> moduleArgs );
}
2026-03-29 09:28:49 +08:00
if ( percentType & FF_PERCENTAGE_TYPE_BAR_BIT ) {
2025-02-24 14:36:20 +08:00
ffStrbufAppendS ( & output , " " );
ffPercentAppendBar ( & output , percent , options -> percent , & options -> moduleArgs );
}
2023-03-26 20:42:18 +08:00
}
ffStrbufAppendC ( & output , ')' );
}
2026-03-29 09:28:49 +08:00
if ( gpu -> type != FF_GPU_TYPE_UNKNOWN ) {
2023-06-24 19:49:53 +08:00
ffStrbufAppendF ( & output , " [%s]" , type );
2026-03-29 09:28:49 +08:00
}
2023-06-24 19:49:53 +08:00
2023-03-26 20:42:18 +08:00
ffStrbufPutTo ( & output , stdout );
2026-03-29 09:28:49 +08:00
} else {
2024-02-26 10:46:13 +08:00
FF_STRBUF_AUTO_DESTROY tempStr = ffStrbufCreate ();
2024-05-08 15:25:59 +08:00
ffTempsAppendNum ( gpu -> temperature , & tempStr , options -> tempConfig , & options -> moduleArgs );
2024-06-28 22:24:02 +08:00
FF_STRBUF_AUTO_DESTROY dTotal = ffStrbufCreate ();
FF_STRBUF_AUTO_DESTROY dUsed = ffStrbufCreate ();
2025-02-24 14:36:20 +08:00
FF_STRBUF_AUTO_DESTROY dPercentNum = ffStrbufCreate ();
FF_STRBUF_AUTO_DESTROY dPercentBar = ffStrbufCreate ();
2026-03-29 09:28:49 +08:00
if ( gpu -> dedicated . total != FF_GPU_VMEM_SIZE_UNSET ) {
ffSizeAppendNum ( gpu -> dedicated . total , & dTotal );
}
if ( gpu -> dedicated . used != FF_GPU_VMEM_SIZE_UNSET ) {
ffSizeAppendNum ( gpu -> dedicated . used , & dUsed );
}
if ( gpu -> dedicated . total != FF_GPU_VMEM_SIZE_UNSET && gpu -> dedicated . used != FF_GPU_VMEM_SIZE_UNSET ) {
2025-02-24 14:36:20 +08:00
double percent = ( double ) gpu -> dedicated . used / ( double ) gpu -> dedicated . total * 100.0 ;
2026-03-29 09:28:49 +08:00
if ( percentType & FF_PERCENTAGE_TYPE_NUM_BIT ) {
2025-02-24 14:36:20 +08:00
ffPercentAppendNum ( & dPercentNum , percent , options -> percent , false , & options -> moduleArgs );
2026-03-29 09:28:49 +08:00
}
if ( percentType & FF_PERCENTAGE_TYPE_BAR_BIT ) {
2025-02-24 14:36:20 +08:00
ffPercentAppendBar ( & dPercentBar , percent , options -> percent , & options -> moduleArgs );
2026-03-29 09:28:49 +08:00
}
2025-02-24 14:36:20 +08:00
}
FF_STRBUF_AUTO_DESTROY sTotal = ffStrbufCreate ();
FF_STRBUF_AUTO_DESTROY sUsed = ffStrbufCreate ();
FF_STRBUF_AUTO_DESTROY sPercentNum = ffStrbufCreate ();
FF_STRBUF_AUTO_DESTROY sPercentBar = ffStrbufCreate ();
2026-03-29 09:28:49 +08:00
if ( gpu -> shared . total != FF_GPU_VMEM_SIZE_UNSET ) {
ffSizeAppendNum ( gpu -> shared . total , & sTotal );
}
if ( gpu -> shared . used != FF_GPU_VMEM_SIZE_UNSET ) {
ffSizeAppendNum ( gpu -> shared . used , & sUsed );
}
if ( gpu -> shared . total != FF_GPU_VMEM_SIZE_UNSET && gpu -> shared . used != FF_GPU_VMEM_SIZE_UNSET ) {
2025-02-24 14:36:20 +08:00
double percent = ( double ) gpu -> shared . used / ( double ) gpu -> shared . total * 100.0 ;
2026-03-29 09:28:49 +08:00
if ( percentType & FF_PERCENTAGE_TYPE_NUM_BIT ) {
2025-02-24 14:36:20 +08:00
ffPercentAppendNum ( & sPercentNum , percent , options -> percent , false , & options -> moduleArgs );
2026-03-29 09:28:49 +08:00
}
if ( percentType & FF_PERCENTAGE_TYPE_BAR_BIT ) {
2025-02-24 14:36:20 +08:00
ffPercentAppendBar ( & sPercentBar , percent , options -> percent , & options -> moduleArgs );
2026-03-29 09:28:49 +08:00
}
2025-02-24 14:36:20 +08:00
}
2024-06-28 22:24:02 +08:00
2024-07-23 09:50:51 +08:00
FF_STRBUF_AUTO_DESTROY frequency = ffStrbufCreate ();
2025-07-30 14:30:36 +08:00
ffFreqAppendNum ( gpu -> frequency , & frequency );
2024-07-23 09:50:51 +08:00
2025-02-24 14:36:20 +08:00
FF_STRBUF_AUTO_DESTROY coreUsageNum = ffStrbufCreate ();
FF_STRBUF_AUTO_DESTROY coreUsageBar = ffStrbufCreate ();
2026-03-29 09:28:49 +08:00
if ( gpu -> coreUsage != FF_GPU_CORE_USAGE_UNSET ) {
if ( percentType & FF_PERCENTAGE_TYPE_NUM_BIT ) {
2025-02-24 14:36:20 +08:00
ffPercentAppendNum ( & coreUsageNum , gpu -> coreUsage , options -> percent , false , & options -> moduleArgs );
2026-03-29 09:28:49 +08:00
}
if ( percentType & FF_PERCENTAGE_TYPE_BAR_BIT ) {
2025-02-24 14:36:20 +08:00
ffPercentAppendBar ( & coreUsageBar , gpu -> coreUsage , options -> percent , & options -> moduleArgs );
2026-03-29 09:28:49 +08:00
}
2025-02-24 14:36:20 +08:00
}
2026-06-14 17:36:54 +08:00
char maxSpeed [ 32 ] = "" , currSpeed [ 32 ] = "" ;
if ( gpu -> pcieSpeed != FF_GPU_PCIE_SPEED_UNSET ) {
snprintf ( maxSpeed , sizeof ( maxSpeed ), "%d x%d" , gpu -> psMax . gen , gpu -> psMax . lanes );
snprintf ( currSpeed , sizeof ( currSpeed ), "%d x%d" , gpu -> psCurr . gen , gpu -> psCurr . lanes );
2026-06-13 15:20:40 +08:00
}
2026-08-14 23:33:54 +08:00
FF_PRINT_FORMAT_CHECKED ( FF_MODULE_GET_DISPLAY_NAME ( GPU ), index , & options -> moduleArgs , FF_PRINT_TYPE_DEFAULT , (( FFformatarg []) {
2026-03-29 09:28:49 +08:00
FF_ARG ( gpu -> vendor , "vendor" ),
FF_ARG ( gpu -> name , "name" ),
FF_ARG ( gpu -> driver , "driver" ),
FF_ARG ( tempStr , "temperature" ),
FF_ARG ( gpu -> coreCount , "core-count" ),
FF_ARG ( type , "type" ),
FF_ARG ( dTotal , "dedicated-total" ),
FF_ARG ( dUsed , "dedicated-used" ),
FF_ARG ( sTotal , "shared-total" ),
FF_ARG ( sUsed , "shared-used" ),
FF_ARG ( gpu -> platformApi , "platform-api" ),
FF_ARG ( frequency , "frequency" ),
FF_ARG ( index , "index" ),
FF_ARG ( dPercentNum , "dedicated-percentage-num" ),
FF_ARG ( dPercentBar , "dedicated-percentage-bar" ),
FF_ARG ( sPercentNum , "shared-percentage-num" ),
FF_ARG ( sPercentBar , "shared-percentage-bar" ),
FF_ARG ( coreUsageNum , "core-usage-num" ),
FF_ARG ( coreUsageBar , "core-usage-bar" ),
FF_ARG ( gpu -> memoryType , "memory-type" ),
2026-06-14 17:36:54 +08:00
FF_ARG ( maxSpeed , "pcie-max-speed" ),
FF_ARG ( currSpeed , "pcie-curr-speed" ),
2026-03-29 09:28:49 +08:00
}));
2023-03-26 20:42:18 +08:00
}
}
2026-03-29 09:28:49 +08:00
bool ffPrintGPU ( FFGPUOptions * options ) {
2026-04-14 10:41:18 +08:00
FF_LIST_AUTO_DESTROY gpus = ffListCreate ();
2023-06-24 10:59:53 +08:00
const char * error = ffDetectGPU ( options , & gpus );
2026-03-29 09:28:49 +08:00
if ( error ) {
2026-08-14 23:33:54 +08:00
ffPrintError ( FF_MODULE_GET_DISPLAY_NAME ( GPU ), 0 , & options -> moduleArgs , FF_PRINT_TYPE_DEFAULT , "%s" , error );
2025-08-18 13:38:30 +08:00
return false ;
2023-06-12 11:30:18 +08:00
}
2023-03-26 20:42:18 +08:00
2026-04-14 10:41:18 +08:00
FF_LIST_AUTO_DESTROY selectedGPUs = ffListCreateA ( sizeof ( const FFGPUResult * ), gpus . length );
2023-03-26 20:42:18 +08:00
2026-09-22 14:49:43 +08:00
selectGPUs ( options , & gpus , & selectedGPUs );
2023-03-26 20:42:18 +08:00
2026-04-14 10:41:18 +08:00
uint32_t i = 0 ;
FF_LIST_FOR_EACH ( const FFGPUResult * , pgpu , selectedGPUs ) {
printGPUResult ( options , selectedGPUs . length == 1 ? 0 : ( uint8_t ) ( i + 1 ), * pgpu );
++ i ;
2026-03-29 09:28:49 +08:00
}
2023-03-26 20:42:18 +08:00
2026-03-29 09:28:49 +08:00
FF_LIST_FOR_EACH ( FFGPUResult , gpu , gpus ) {
2023-06-12 11:30:18 +08:00
ffStrbufDestroy ( & gpu -> vendor );
ffStrbufDestroy ( & gpu -> name );
ffStrbufDestroy ( & gpu -> driver );
2024-05-20 15:32:13 +08:00
ffStrbufDestroy ( & gpu -> platformApi );
2025-03-24 22:25:56 +08:00
ffStrbufDestroy ( & gpu -> memoryType );
2023-06-12 11:30:18 +08:00
}
2025-08-18 13:38:30 +08:00
2026-06-05 22:18:53 +08:00
if ( selectedGPUs . length == 0 ) {
2026-08-14 23:33:54 +08:00
ffPrintError ( FF_MODULE_GET_DISPLAY_NAME ( GPU ), 0 , & options -> moduleArgs , FF_PRINT_TYPE_DEFAULT , ( gpus . length > 0 ? "GPUs found but all hidden by hideType option" : "No GPUs detected" ));
2026-06-05 22:18:53 +08:00
return false ;
}
2025-08-18 13:38:30 +08:00
return true ;
2023-03-26 20:42:18 +08:00
}
2026-03-29 09:28:49 +08:00
void ffParseGPUJsonObject ( FFGPUOptions * options , yyjson_val * module ) {
2025-08-02 18:51:03 +08:00
yyjson_val * key , * val ;
2023-08-17 16:09:41 +08:00
size_t idx , max ;
2026-03-29 09:28:49 +08:00
yyjson_obj_foreach ( module , idx , max , key , val ) {
if ( ffJsonConfigParseModuleArgs ( key , val , & options -> moduleArgs )) {
2023-08-17 16:09:41 +08:00
continue ;
2026-03-29 09:28:49 +08:00
}
2023-03-26 20:42:18 +08:00
2026-03-29 09:28:49 +08:00
if ( ffTempsParseJsonObject ( key , val , & options -> temp , & options -> tempConfig )) {
2023-08-17 16:09:41 +08:00
continue ;
2026-03-29 09:28:49 +08:00
}
2023-03-26 20:42:18 +08:00
2026-03-29 09:28:49 +08:00
if ( unsafe_yyjson_equals_str ( key , "driverSpecific" )) {
2023-12-20 14:59:27 +08:00
options -> driverSpecific = yyjson_get_bool ( val );
2023-10-30 13:46:51 +08:00
continue ;
}
2026-03-29 09:28:49 +08:00
if ( unsafe_yyjson_equals_str ( key , "detectionMethod" )) {
2024-05-20 16:45:23 +08:00
int value ;
2026-06-05 22:18:53 +08:00
const char * error = ffJsonConfigParseEnum ( val , & value , ( FFKeyValuePair []){
2026-03-30 10:27:23 +08:00
{ "auto" , FF_GPU_DETECTION_METHOD_AUTO },
{ "pci" , FF_GPU_DETECTION_METHOD_PCI },
{ "vulkan" , FF_GPU_DETECTION_METHOD_VULKAN },
{ "opencl" , FF_GPU_DETECTION_METHOD_OPENCL },
2026-06-18 19:49:21 +08:00
{ "egl-ext" , FF_GPU_DETECTION_METHOD_EGL_EXT },
2026-03-30 10:27:23 +08:00
{ "opengl" , FF_GPU_DETECTION_METHOD_OPENGL },
2026-03-29 09:28:49 +08:00
{},
});
if ( error ) {
2026-08-14 23:33:54 +08:00
ffPrintError ( FF_MODULE_GET_DISPLAY_NAME ( GPU ), 0 , & options -> moduleArgs , FF_PRINT_TYPE_DEFAULT , "Invalid %s value: %s" , unsafe_yyjson_get_str ( key ), error );
2026-03-29 09:28:49 +08:00
} else {
2024-05-20 16:45:23 +08:00
options -> detectionMethod = ( FFGPUDetectionMethod ) value ;
2026-03-29 09:28:49 +08:00
}
2023-08-17 16:09:41 +08:00
continue ;
}
2023-03-26 20:42:18 +08:00
2026-03-29 09:28:49 +08:00
if ( unsafe_yyjson_equals_str ( key , "hideType" )) {
if ( yyjson_is_null ( val )) {
2025-08-03 10:14:53 +08:00
options -> hideType = FF_GPU_TYPE_NONE ;
2026-03-29 09:28:49 +08:00
} else {
2025-08-03 10:14:53 +08:00
int value ;
2026-06-05 22:18:53 +08:00
const char * error = ffJsonConfigParseEnum ( val , & value , ( FFKeyValuePair []){
2026-03-30 10:27:23 +08:00
{ "none" , FF_GPU_TYPE_NONE },
{ "unknown" , FF_GPU_TYPE_UNKNOWN },
{ "integrated" , FF_GPU_TYPE_INTEGRATED },
{ "discrete" , FF_GPU_TYPE_DISCRETE },
2026-03-29 09:28:49 +08:00
{},
});
if ( error ) {
2026-08-14 23:33:54 +08:00
ffPrintError ( FF_MODULE_GET_DISPLAY_NAME ( GPU ), 0 , & options -> moduleArgs , FF_PRINT_TYPE_DEFAULT , "Invalid %s value: %s" , unsafe_yyjson_get_str ( key ), error );
2026-03-29 09:28:49 +08:00
} else {
2025-08-03 10:14:53 +08:00
options -> hideType = ( FFGPUType ) value ;
2026-03-29 09:28:49 +08:00
}
2025-08-03 10:14:53 +08:00
}
2023-08-17 16:09:41 +08:00
continue ;
2023-03-26 20:42:18 +08:00
}
2023-08-17 16:09:41 +08:00
2026-03-29 09:28:49 +08:00
if ( ffPercentParseJsonObject ( key , val , & options -> percent )) {
2024-01-20 18:40:12 +08:00
continue ;
2026-03-29 09:28:49 +08:00
}
2024-01-20 18:40:12 +08:00
2026-08-14 23:33:54 +08:00
ffPrintError ( FF_MODULE_GET_DISPLAY_NAME ( GPU ), 0 , & options -> moduleArgs , FF_PRINT_TYPE_DEFAULT , "Unknown JSON key %s" , unsafe_yyjson_get_str ( key ));
2023-03-26 20:42:18 +08:00
}
}
2023-09-05 15:49:22 +08:00
2026-10-03 01:32:02 +08:00
[[ gnu :: cold ]]
2026-03-29 09:28:49 +08:00
void ffGenerateGPUJsonConfig ( FFGPUOptions * options , yyjson_mut_doc * doc , yyjson_mut_val * module ) {
2025-08-04 16:46:28 +08:00
ffJsonConfigGenerateModuleArgsConfig ( doc , module , & options -> moduleArgs );
2023-10-24 14:41:29 +08:00
2025-08-04 16:46:28 +08:00
yyjson_mut_obj_add_bool ( doc , module , "driverSpecific" , options -> driverSpecific );
2023-10-24 14:41:29 +08:00
2026-03-29 09:28:49 +08:00
switch ( options -> detectionMethod ) {
2025-08-04 16:46:28 +08:00
case FF_GPU_DETECTION_METHOD_AUTO :
yyjson_mut_obj_add_str ( doc , module , "detectionMethod" , "auto" );
break ;
case FF_GPU_DETECTION_METHOD_PCI :
yyjson_mut_obj_add_str ( doc , module , "detectionMethod" , "pci" );
break ;
case FF_GPU_DETECTION_METHOD_VULKAN :
yyjson_mut_obj_add_str ( doc , module , "detectionMethod" , "vulkan" );
break ;
case FF_GPU_DETECTION_METHOD_OPENCL :
yyjson_mut_obj_add_str ( doc , module , "detectionMethod" , "opencl" );
break ;
2026-06-18 19:49:21 +08:00
case FF_GPU_DETECTION_METHOD_EGL_EXT :
yyjson_mut_obj_add_str ( doc , module , "detectionMethod" , "egl-ext" );
break ;
2025-08-04 16:46:28 +08:00
case FF_GPU_DETECTION_METHOD_OPENGL :
yyjson_mut_obj_add_str ( doc , module , "detectionMethod" , "opengl" );
break ;
2024-05-20 16:45:23 +08:00
}
2023-10-24 14:41:29 +08:00
2025-08-04 16:46:28 +08:00
ffTempsGenerateJsonConfig ( doc , module , options -> temp , options -> tempConfig );
2023-10-24 14:41:29 +08:00
2026-03-29 09:28:49 +08:00
switch ( options -> hideType ) {
2025-08-04 16:46:28 +08:00
case FF_GPU_TYPE_NONE :
yyjson_mut_obj_add_str ( doc , module , "hideType" , "none" );
break ;
case FF_GPU_TYPE_UNKNOWN :
yyjson_mut_obj_add_str ( doc , module , "hideType" , "unknown" );
break ;
case FF_GPU_TYPE_INTEGRATED :
yyjson_mut_obj_add_str ( doc , module , "hideType" , "integrated" );
break ;
case FF_GPU_TYPE_DISCRETE :
yyjson_mut_obj_add_str ( doc , module , "hideType" , "discrete" );
break ;
2023-10-24 14:41:29 +08:00
}
2024-01-20 18:40:12 +08:00
2025-08-04 16:46:28 +08:00
ffPercentGenerateJsonConfig ( doc , module , options -> percent );
2023-10-24 14:41:29 +08:00
}
2026-03-29 09:28:49 +08:00
bool ffGenerateGPUJsonResult ( FFGPUOptions * options , yyjson_mut_doc * doc , yyjson_mut_val * module ) {
2026-04-14 10:41:18 +08:00
FF_LIST_AUTO_DESTROY gpus = ffListCreate ();
2023-09-05 15:49:22 +08:00
const char * error = ffDetectGPU ( options , & gpus );
2026-03-29 09:28:49 +08:00
if ( error ) {
2023-09-05 15:49:22 +08:00
yyjson_mut_obj_add_str ( doc , module , "error" , error );
2025-08-18 13:38:30 +08:00
return false ;
2023-09-05 15:49:22 +08:00
}
2026-09-22 14:49:43 +08:00
FF_LIST_AUTO_DESTROY selectedGPUs = ffListCreateA ( sizeof ( const FFGPUResult * ), gpus . length );
selectGPUs ( options , & gpus , & selectedGPUs );
2023-09-05 15:49:22 +08:00
yyjson_mut_val * arr = yyjson_mut_obj_add_arr ( doc , module , "result" );
2026-09-22 14:49:43 +08:00
FF_LIST_FOR_EACH ( const FFGPUResult * , pgpu , selectedGPUs ) {
const FFGPUResult * gpu = * pgpu ;
2023-09-05 15:49:22 +08:00
yyjson_mut_val * obj = yyjson_mut_arr_add_obj ( doc , arr );
2024-09-19 15:22:09 +08:00
2026-03-29 09:28:49 +08:00
if ( gpu -> index != FF_GPU_INDEX_UNSET ) {
2024-09-19 15:38:43 +08:00
yyjson_mut_obj_add_uint ( doc , obj , "index" , gpu -> index );
2026-03-29 09:28:49 +08:00
} else {
2024-09-19 15:22:09 +08:00
yyjson_mut_obj_add_null ( doc , obj , "index" );
2026-03-29 09:28:49 +08:00
}
2024-09-19 15:22:09 +08:00
2026-03-29 09:28:49 +08:00
if ( gpu -> coreCount != FF_GPU_CORE_COUNT_UNSET ) {
2023-09-05 15:49:22 +08:00
yyjson_mut_obj_add_int ( doc , obj , "coreCount" , gpu -> coreCount );
2026-03-29 09:28:49 +08:00
} else {
2023-09-05 15:49:22 +08:00
yyjson_mut_obj_add_null ( doc , obj , "coreCount" );
2026-03-29 09:28:49 +08:00
}
2023-09-05 15:49:22 +08:00
2026-03-29 09:28:49 +08:00
if ( gpu -> coreUsage != FF_GPU_CORE_USAGE_UNSET ) {
2025-08-14 09:47:14 +08:00
yyjson_mut_obj_add_real ( doc , obj , "coreUsage" , gpu -> coreUsage );
2026-03-29 09:28:49 +08:00
} else {
2025-08-14 09:47:14 +08:00
yyjson_mut_obj_add_null ( doc , obj , "coreUsage" );
2026-03-29 09:28:49 +08:00
}
2024-08-01 13:54:03 +08:00
2023-09-13 14:07:14 +08:00
yyjson_mut_val * memoryObj = yyjson_mut_obj_add_obj ( doc , obj , "memory" );
yyjson_mut_val * dedicatedObj = yyjson_mut_obj_add_obj ( doc , memoryObj , "dedicated" );
2026-03-29 09:28:49 +08:00
if ( gpu -> dedicated . total != FF_GPU_VMEM_SIZE_UNSET ) {
2023-09-05 15:49:22 +08:00
yyjson_mut_obj_add_uint ( doc , dedicatedObj , "total" , gpu -> dedicated . total );
2026-03-29 09:28:49 +08:00
} else {
2023-09-05 15:49:22 +08:00
yyjson_mut_obj_add_null ( doc , dedicatedObj , "total" );
2026-03-29 09:28:49 +08:00
}
2023-09-05 15:49:22 +08:00
2026-03-29 09:28:49 +08:00
if ( gpu -> dedicated . used != FF_GPU_VMEM_SIZE_UNSET ) {
2023-09-05 15:49:22 +08:00
yyjson_mut_obj_add_uint ( doc , dedicatedObj , "used" , gpu -> dedicated . used );
2026-03-29 09:28:49 +08:00
} else {
2023-09-05 15:49:22 +08:00
yyjson_mut_obj_add_null ( doc , dedicatedObj , "used" );
2026-03-29 09:28:49 +08:00
}
2023-09-05 15:49:22 +08:00
2023-09-13 14:07:14 +08:00
yyjson_mut_val * sharedObj = yyjson_mut_obj_add_obj ( doc , memoryObj , "shared" );
2026-03-29 09:28:49 +08:00
if ( gpu -> shared . total != FF_GPU_VMEM_SIZE_UNSET ) {
2023-09-05 15:49:22 +08:00
yyjson_mut_obj_add_uint ( doc , sharedObj , "total" , gpu -> shared . total );
2026-03-29 09:28:49 +08:00
} else {
2023-09-05 15:49:22 +08:00
yyjson_mut_obj_add_null ( doc , sharedObj , "total" );
2026-03-29 09:28:49 +08:00
}
if ( gpu -> shared . used != FF_GPU_VMEM_SIZE_UNSET ) {
2023-09-05 15:49:22 +08:00
yyjson_mut_obj_add_uint ( doc , sharedObj , "used" , gpu -> shared . used );
2026-03-29 09:28:49 +08:00
} else {
2023-09-05 15:49:22 +08:00
yyjson_mut_obj_add_null ( doc , sharedObj , "used" );
2026-03-29 09:28:49 +08:00
}
2023-09-05 15:49:22 +08:00
2026-03-29 09:28:49 +08:00
if ( gpu -> memoryType . length ) {
2025-03-24 22:25:56 +08:00
yyjson_mut_obj_add_strbuf ( doc , memoryObj , "type" , & gpu -> memoryType );
2026-03-29 09:28:49 +08:00
} else {
2025-03-24 22:25:56 +08:00
yyjson_mut_obj_add_null ( doc , memoryObj , "type" );
2026-03-29 09:28:49 +08:00
}
2025-03-24 22:25:56 +08:00
yyjson_mut_obj_add_strbuf ( doc , obj , "driver" , & gpu -> driver );
yyjson_mut_obj_add_strbuf ( doc , obj , "name" , & gpu -> name );
2025-03-24 15:19:34 +08:00
2026-03-29 09:28:49 +08:00
if ( gpu -> temperature != FF_GPU_TEMP_UNSET ) {
2023-10-28 13:12:04 +08:00
yyjson_mut_obj_add_real ( doc , obj , "temperature" , gpu -> temperature );
2026-03-29 09:28:49 +08:00
} else {
2023-10-28 13:12:04 +08:00
yyjson_mut_obj_add_null ( doc , obj , "temperature" );
2026-03-29 09:28:49 +08:00
}
2023-09-05 15:49:22 +08:00
const char * type ;
2026-03-29 09:28:49 +08:00
switch ( gpu -> type ) {
case FF_GPU_TYPE_INTEGRATED :
type = "Integrated" ;
break ;
case FF_GPU_TYPE_DISCRETE :
type = "Discrete" ;
break ;
default :
2026-07-10 18:08:04 +08:00
type = nullptr ;
2026-03-29 09:28:49 +08:00
break ;
2023-09-05 15:49:22 +08:00
}
2026-03-29 09:28:49 +08:00
if ( type ) {
2025-11-01 16:44:36 +08:00
yyjson_mut_obj_add_str ( doc , obj , "type" , type );
2026-03-29 09:28:49 +08:00
} else {
2025-11-01 16:44:36 +08:00
yyjson_mut_obj_add_null ( doc , obj , "type" );
2026-03-29 09:28:49 +08:00
}
2023-09-05 15:49:22 +08:00
yyjson_mut_obj_add_strbuf ( doc , obj , "vendor" , & gpu -> vendor );
2023-12-20 14:45:27 +08:00
2023-12-29 15:12:41 +08:00
yyjson_mut_obj_add_strbuf ( doc , obj , "platformApi" , & gpu -> platformApi );
2026-03-29 09:28:49 +08:00
if ( gpu -> frequency != FF_GPU_FREQUENCY_UNSET ) {
2025-08-14 09:47:14 +08:00
yyjson_mut_obj_add_uint ( doc , obj , "frequency" , gpu -> frequency );
2026-03-29 09:28:49 +08:00
} else {
2025-08-14 09:47:14 +08:00
yyjson_mut_obj_add_null ( doc , obj , "frequency" );
2026-03-29 09:28:49 +08:00
}
2024-06-21 08:57:08 +08:00
yyjson_mut_obj_add_uint ( doc , obj , "deviceId" , gpu -> deviceId );
2026-06-13 15:20:40 +08:00
2026-06-14 17:36:54 +08:00
if ( gpu -> pcieSpeed != FF_GPU_PCIE_SPEED_UNSET ) {
2026-06-13 15:20:40 +08:00
yyjson_mut_val * pcieSpeed = yyjson_mut_obj_add_obj ( doc , obj , "pcieSpeed" );
2026-06-14 17:36:54 +08:00
yyjson_mut_val * maxSpeed = yyjson_mut_obj_add_obj ( doc , pcieSpeed , "max" );
yyjson_mut_obj_add_uint ( doc , maxSpeed , "gen" , gpu -> psMax . gen );
yyjson_mut_obj_add_uint ( doc , maxSpeed , "lanes" , gpu -> psMax . lanes );
yyjson_mut_val * currSpeed = yyjson_mut_obj_add_obj ( doc , pcieSpeed , "current" );
yyjson_mut_obj_add_uint ( doc , currSpeed , "gen" , gpu -> psCurr . gen );
yyjson_mut_obj_add_uint ( doc , currSpeed , "lanes" , gpu -> psCurr . lanes );
2026-06-13 15:20:40 +08:00
} else {
yyjson_mut_obj_add_null ( doc , obj , "pcieSpeed" );
}
2023-09-05 15:49:22 +08:00
}
2026-03-29 09:28:49 +08:00
FF_LIST_FOR_EACH ( FFGPUResult , gpu , gpus ) {
2023-09-05 15:49:22 +08:00
ffStrbufDestroy ( & gpu -> vendor );
ffStrbufDestroy ( & gpu -> name );
ffStrbufDestroy ( & gpu -> driver );
2024-05-20 15:32:13 +08:00
ffStrbufDestroy ( & gpu -> platformApi );
2025-03-24 22:25:56 +08:00
ffStrbufDestroy ( & gpu -> memoryType );
2023-09-05 15:49:22 +08:00
}
2025-08-18 13:38:30 +08:00
return true ;
2023-09-05 15:49:22 +08:00
}
2023-10-07 13:40:40 +08:00
2026-03-29 09:28:49 +08:00
void ffInitGPUOptions ( FFGPUOptions * options ) {
2025-08-04 14:25:14 +08:00
ffOptionInitModuleArg ( & options -> moduleArgs , "" );
options -> driverSpecific = false ;
options -> detectionMethod =
2026-03-29 09:28:49 +08:00
#if defined(__x86_64__) || defined(__i386__)
2025-08-04 14:25:14 +08:00
FF_GPU_DETECTION_METHOD_PCI
2026-03-29 09:28:49 +08:00
#else
2025-08-04 14:25:14 +08:00
FF_GPU_DETECTION_METHOD_AUTO
2026-03-29 09:28:49 +08:00
#endif
;
2025-08-04 14:25:14 +08:00
options -> temp = false ;
options -> hideType = FF_GPU_TYPE_NONE ;
2026-06-05 22:18:53 +08:00
options -> tempConfig = ( FFColorRangeConfig ){ 60 , 80 };
options -> percent = ( FFPercentageModuleConfig ){ 50 , 80 , 0 };
2025-08-04 14:25:14 +08:00
}
2026-03-29 09:28:49 +08:00
void ffDestroyGPUOptions ( FFGPUOptions * options ) {
2025-08-04 14:25:14 +08:00
ffOptionDestroyModuleArg ( & options -> moduleArgs );
}
2025-08-03 22:50:56 +08:00
FFModuleBaseInfo ffGPUModuleInfo = {
2026-08-14 23:33:54 +08:00
. name = "GPU" ,
2026-04-16 18:27:11 +08:00
. description = "Print GPU names, memory sizes, types, etc" ,
2026-08-14 20:38:58 +08:00
. displayName = {
. en = "GPU" ,
2026-08-16 10:51:42 +08:00
. ar = "معالج الرسوميات" ,
2026-08-21 10:19:06 +08:00
. cs = "GPU" ,
2026-08-14 20:38:58 +08:00
. de = "GPU" ,
. es = "GPU" ,
2026-10-03 13:35:56 +03:00
. fi = "Grafiikkasuoritin" ,
2026-08-14 20:38:58 +08:00
. fr = "GPU" ,
2026-08-20 16:25:32 +00:00
. gl = "GPU" ,
2026-08-16 10:51:42 +08:00
. he = "מעבד גרפי" ,
2026-08-21 10:19:06 +08:00
. id = "GPU" ,
2026-08-14 20:38:58 +08:00
. it = "GPU" ,
. ja = "GPU" ,
. ko = "GPU" ,
. pl = "GPU" ,
2026-08-16 10:37:06 +08:00
. pt = "GPU" ,
2026-08-16 17:51:44 +02:00
. ru = "Видеокарта" ,
2026-08-21 10:19:06 +08:00
. tr = "GPU" ,
. uk = "GPU" ,
. vi = "GPU" ,
2026-08-14 20:38:58 +08:00
. zh_CN = "显卡" ,
. zh_TW = "顯卡" ,
},
2025-08-03 18:39:41 +08:00
. initOptions = ( void * ) ffInitGPUOptions ,
. destroyOptions = ( void * ) ffDestroyGPUOptions ,
2024-12-11 10:47:02 +08:00
. parseJsonObject = ( void * ) ffParseGPUJsonObject ,
. printModule = ( void * ) ffPrintGPU ,
. generateJsonResult = ( void * ) ffGenerateGPUJsonResult ,
. generateJsonConfig = ( void * ) ffGenerateGPUJsonConfig ,
2026-06-05 22:18:53 +08:00
. formatArgs = FF_FORMAT_ARG_LIST ((( FFModuleFormatArg []){
2026-03-30 10:27:23 +08:00
{ "GPU vendor" , "vendor" },
{ "GPU name" , "name" },
{ "GPU driver" , "driver" },
{ "GPU temperature" , "temperature" },
{ "GPU core count" , "core-count" },
{ "GPU type" , "type" },
{ "GPU total dedicated memory" , "dedicated-total" },
{ "GPU used dedicated memory" , "dedicated-used" },
{ "GPU total shared memory" , "shared-total" },
{ "GPU used shared memory" , "shared-used" },
{ "The platform API used when detecting the GPU" , "platform-api" },
{ "Current frequency in GHz" , "frequency" },
{ "GPU vendor specific index" , "index" },
{ "Dedicated memory usage percentage num" , "dedicated-percentage-num" },
{ "Dedicated memory usage percentage bar" , "dedicated-percentage-bar" },
{ "Shared memory usage percentage num" , "shared-percentage-num" },
{ "Shared memory usage percentage bar" , "shared-percentage-bar" },
{ "Core usage percentage num" , "core-usage-num" },
{ "Core usage percentage bar" , "core-usage-bar" },
{ "Memory type (Windows only)" , "memory-type" },
2026-06-14 17:36:54 +08:00
{ "PCIe maximum speed in gen and lanes" , "pcie-max-speed" },
{ "PCIe current speed in gen and lanes" , "pcie-curr-speed" },
2024-12-11 10:47:02 +08:00
})),
2026-07-31 10:56:13 +08:00
. defaultOrder = 36 ,
2024-12-11 10:47:02 +08:00
};