x86: AVX2 nal_escape
[x262.git] / common / opencl.c
1 /*****************************************************************************
2  * opencl.c: OpenCL initialization and kernel compilation
3  *****************************************************************************
4  * Copyright (C) 2012-2013 x264 project
5  *
6  * Authors: Steve Borho <sborho@multicorewareinc.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
21  *
22  * This program is also available under a commercial proprietary license.
23  * For more information, contact us at licensing@x264.com.
24  *****************************************************************************/
25
26 #include "common.h"
27 #if _WIN32
28 #include <windows.h>
29 #else
30 #include <dlfcn.h> //dlopen, dlsym, dlclose
31 #endif
32
33 /* define from recent cl_ext.h, copied here in case headers are old */
34 #define CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD        0x4042
35
36 /* Requires full include path in case of out-of-tree builds */
37 #include "common/oclobj.h"
38
39 static int x264_detect_switchable_graphics();
40
41 /* Try to load the cached compiled program binary, verify the device context is
42  * still valid before reuse */
43 static cl_program x264_opencl_cache_load( x264_t *h, char *devname, char *devvendor, char *driverversion )
44 {
45     cl_program program = NULL;
46     cl_int status;
47
48     /* try to load cached program binary */
49     FILE *fp = fopen( h->param.psz_clbin_file, "rb" );
50     if( !fp )
51         return NULL;
52
53     fseek( fp, 0L, SEEK_END );
54     size_t size = ftell( fp );
55     rewind( fp );
56     uint8_t *binary;
57     CHECKED_MALLOC( binary, size );
58
59     fread( binary, 1, size, fp );
60     const uint8_t *ptr = (const uint8_t*)binary;
61
62 #define CHECK_STRING( STR )\
63     do {\
64         size_t len = strlen( STR );\
65         if( size <= len || strncmp( (char*)ptr, STR, len ) )\
66             goto fail;\
67         else {\
68             size -= (len+1); ptr += (len+1);\
69         }\
70     } while( 0 )
71
72     CHECK_STRING( devname );
73     CHECK_STRING( devvendor );
74     CHECK_STRING( driverversion );
75     CHECK_STRING( x264_opencl_source_hash );
76 #undef CHECK_STRING
77
78     program = clCreateProgramWithBinary( h->opencl.context, 1, &h->opencl.device, &size, &ptr, NULL, &status );
79     if( status != CL_SUCCESS )
80         program = NULL;
81
82 fail:
83     fclose( fp );
84     x264_free( binary );
85     return program;
86 }
87
88 /* Save the compiled program binary to a file for later reuse.  Device context
89  * is also saved in the cache file so we do not reuse stale binaries */
90 static void x264_opencl_cache_save( x264_t *h, cl_program program, char *devname, char *devvendor, char *driverversion )
91 {
92     FILE *fp = fopen( h->param.psz_clbin_file, "wb" );
93     if( !fp )
94     {
95         x264_log( h, X264_LOG_INFO, "OpenCL: unable to open clbin file for write");
96         return;
97     }
98
99     size_t size;
100     cl_int status = clGetProgramInfo( program, CL_PROGRAM_BINARY_SIZES, sizeof(size_t), &size, NULL );
101     if( status == CL_SUCCESS )
102     {
103         uint8_t *binary;
104         CHECKED_MALLOC( binary, size );
105         status = clGetProgramInfo( program, CL_PROGRAM_BINARIES, sizeof(uint8_t *), &binary, NULL );
106         if( status == CL_SUCCESS )
107         {
108             fputs( devname, fp );
109             fputc( '\n', fp );
110             fputs( devvendor, fp );
111             fputc( '\n', fp );
112             fputs( driverversion, fp );
113             fputc( '\n', fp );
114             fputs( x264_opencl_source_hash, fp );
115             fputc( '\n', fp );
116             fwrite( binary, 1, size, fp );
117         }
118         else
119             x264_log( h, X264_LOG_INFO, "OpenCL: Unable to query program binary, no cache file generated");
120         x264_free( binary );
121     }
122     else
123         x264_log( h, X264_LOG_INFO, "OpenCL: Unable to query program binary size, no cache file generated");
124     fclose( fp );
125
126 fail:
127     return;
128 }
129
130 /* The OpenCL source under common/opencl will be merged into common/oclobj.h by
131  * the Makefile. It defines a x264_opencl_source byte array which we will pass
132  * to clCreateProgramWithSource().  We also attempt to use a cache file for the
133  * compiled binary, stored in the current working folder. */
134 static cl_program x264_opencl_compile( x264_t *h )
135 {
136     cl_program program;
137     cl_int status;
138
139     char devname[64];
140     char devvendor[64];
141     char driverversion[64];
142     status  = clGetDeviceInfo( h->opencl.device, CL_DEVICE_NAME,    sizeof(devname), devname, NULL );
143     status |= clGetDeviceInfo( h->opencl.device, CL_DEVICE_VENDOR,  sizeof(devvendor), devvendor, NULL );
144     status |= clGetDeviceInfo( h->opencl.device, CL_DRIVER_VERSION, sizeof(driverversion), driverversion, NULL );
145     if( status != CL_SUCCESS )
146         return NULL;
147
148     // Most AMD GPUs have vector registers
149     int vectorize = !strcmp( devvendor, "Advanced Micro Devices, Inc." );
150     h->opencl.b_device_AMD_SI = 0;
151
152     if( vectorize )
153     {
154         /* Disable OpenCL on Intel/AMD switchable graphics devices */
155         if( x264_detect_switchable_graphics() )
156         {
157             x264_log( h, X264_LOG_INFO, "OpenCL acceleration disabled, switchable graphics detected\n" );
158             return NULL;
159         }
160
161         /* Detect AMD SouthernIsland or newer device (single-width registers) */
162         cl_uint simdwidth = 4;
163         status = clGetDeviceInfo( h->opencl.device, CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD, sizeof(cl_uint), &simdwidth, NULL );
164         if( status == CL_SUCCESS && simdwidth == 1 )
165         {
166             vectorize = 0;
167             h->opencl.b_device_AMD_SI = 1;
168         }
169     }
170
171     x264_log( h, X264_LOG_INFO, "OpenCL acceleration enabled with %s %s %s\n", devvendor, devname, h->opencl.b_device_AMD_SI ? "(SI)" : "" );
172
173     program = x264_opencl_cache_load( h, devname, devvendor, driverversion );
174     if( !program )
175     {
176         /* clCreateProgramWithSource() requires a pointer variable, you cannot just use &x264_opencl_source */
177         x264_log( h, X264_LOG_INFO, "Compiling OpenCL kernels...\n" );
178         const char *strptr = (const char*)x264_opencl_source;
179         size_t size = sizeof(x264_opencl_source);
180         program = clCreateProgramWithSource( h->opencl.context, 1, &strptr, &size, &status );
181         if( status != CL_SUCCESS || !program )
182         {
183             x264_log( h, X264_LOG_WARNING, "OpenCL: unable to create program\n" );
184             return NULL;
185         }
186     }
187
188     /* Build the program binary for the OpenCL device */
189     const char *buildopts = vectorize ? "-DVECTORIZE=1" : "";
190     status = clBuildProgram( program, 1, &h->opencl.device, buildopts, NULL, NULL );
191     if( status == CL_SUCCESS )
192     {
193         x264_opencl_cache_save( h, program, devname, devvendor, driverversion );
194         return program;
195     }
196
197     /* Compile failure, should not happen with production code. */
198
199     size_t build_log_len = 0;
200
201     status = clGetProgramBuildInfo( program, h->opencl.device, CL_PROGRAM_BUILD_LOG, build_log_len, NULL, &build_log_len );
202     if( status != CL_SUCCESS )
203     {
204         x264_log( h, X264_LOG_WARNING, "OpenCL: Compilation failed, unable to query build log\n" );
205         return NULL;
206     }
207
208     char *build_log;
209     CHECKED_MALLOC( build_log, build_log_len );
210     if( !build_log )
211     {
212         x264_log( h, X264_LOG_WARNING, "OpenCL: Compilation failed, unable to alloc build log\n" );
213         return NULL;
214     }
215
216     status = clGetProgramBuildInfo( program, h->opencl.device, CL_PROGRAM_BUILD_LOG, build_log_len, build_log, NULL );
217     if( status != CL_SUCCESS )
218     {
219         x264_log( h, X264_LOG_WARNING, "OpenCL: Compilation failed, unable to get build log\n" );
220         x264_free( build_log );
221         return NULL;
222     }
223
224     FILE *lg = fopen( "x264_kernel_build_log.txt", "w" );
225     if( lg )
226     {
227         fwrite( build_log, 1, build_log_len, lg );
228         fclose( lg );
229         x264_log( h, X264_LOG_WARNING, "OpenCL: kernel build errors written to x264_kernel_build_log.txt\n" );
230     }
231
232     x264_free( build_log );
233 fail:
234     return NULL;
235 }
236
237 static void x264_opencl_free_lookahead( x264_t *h )
238 {
239 #define RELEASE( a, f ) if( a ) f( a );
240     RELEASE( h->opencl.intra_kernel, clReleaseKernel )
241     RELEASE( h->opencl.rowsum_intra_kernel, clReleaseKernel )
242     RELEASE( h->opencl.downscale_kernel1, clReleaseKernel )
243     RELEASE( h->opencl.downscale_kernel2, clReleaseKernel )
244     RELEASE( h->opencl.downscale_hpel_kernel, clReleaseKernel )
245     RELEASE( h->opencl.weightp_hpel_kernel, clReleaseKernel )
246     RELEASE( h->opencl.weightp_scaled_images_kernel, clReleaseKernel )
247     RELEASE( h->opencl.memset_kernel, clReleaseKernel )
248     RELEASE( h->opencl.hme_kernel, clReleaseKernel )
249     RELEASE( h->opencl.subpel_refine_kernel, clReleaseKernel )
250     RELEASE( h->opencl.mode_select_kernel, clReleaseKernel )
251     RELEASE( h->opencl.rowsum_inter_kernel, clReleaseKernel )
252     RELEASE( h->opencl.lookahead_program, clReleaseProgram )
253     RELEASE( h->opencl.row_satds[0], clReleaseMemObject )
254     RELEASE( h->opencl.row_satds[1], clReleaseMemObject )
255     RELEASE( h->opencl.frame_stats[0], clReleaseMemObject )
256     RELEASE( h->opencl.frame_stats[1], clReleaseMemObject )
257     RELEASE( h->opencl.mv_buffers[0], clReleaseMemObject )
258     RELEASE( h->opencl.mv_buffers[1], clReleaseMemObject )
259     RELEASE( h->opencl.mvp_buffer, clReleaseMemObject )
260     RELEASE( h->opencl.luma_16x16_image[0], clReleaseMemObject )
261     RELEASE( h->opencl.luma_16x16_image[1], clReleaseMemObject )
262     RELEASE( h->opencl.lowres_mv_costs, clReleaseMemObject )
263     RELEASE( h->opencl.lowres_costs[0], clReleaseMemObject )
264     RELEASE( h->opencl.lowres_costs[1], clReleaseMemObject )
265     RELEASE( h->opencl.page_locked_buffer, clReleaseMemObject )
266     RELEASE( h->opencl.weighted_luma_hpel, clReleaseMemObject )
267     for( int i = 0; i < NUM_IMAGE_SCALES; i++ )
268         RELEASE( h->opencl.weighted_scaled_images[i], clReleaseMemObject )
269 #undef RELEASE
270 }
271
272 int x264_opencl_init_lookahead( x264_t *h )
273 {
274     if( !h->param.rc.i_lookahead )
275         return -1;
276
277     static const char const *kernelnames[] = {
278         "mb_intra_cost_satd_8x8",
279         "sum_intra_cost",
280         "downscale_hpel",
281         "downscale1",
282         "downscale2",
283         "memset_int16",
284         "weightp_scaled_images",
285         "weightp_hpel",
286         "hierarchical_motion",
287         "subpel_refine",
288         "mode_selection",
289         "sum_inter_cost"
290     };
291     cl_kernel *kernels[] = {
292         &h->opencl.intra_kernel,
293         &h->opencl.rowsum_intra_kernel,
294         &h->opencl.downscale_hpel_kernel,
295         &h->opencl.downscale_kernel1,
296         &h->opencl.downscale_kernel2,
297         &h->opencl.memset_kernel,
298         &h->opencl.weightp_scaled_images_kernel,
299         &h->opencl.weightp_hpel_kernel,
300         &h->opencl.hme_kernel,
301         &h->opencl.subpel_refine_kernel,
302         &h->opencl.mode_select_kernel,
303         &h->opencl.rowsum_inter_kernel
304     };
305     cl_int status;
306
307     h->opencl.lookahead_program = x264_opencl_compile( h );
308     if( !h->opencl.lookahead_program )
309     {
310         x264_opencl_free_lookahead( h );
311         return -1;
312     }
313
314     for( int i = 0; i < ARRAY_SIZE(kernelnames); i++ )
315     {
316         *kernels[i] = clCreateKernel( h->opencl.lookahead_program, kernelnames[i], &status );
317         if( status != CL_SUCCESS )
318         {
319             x264_log( h, X264_LOG_WARNING, "OpenCL: Unable to compile kernel '%s' (%d)\n", kernelnames[i], status );
320             x264_opencl_free_lookahead( h );
321             return -1;
322         }
323     }
324
325     h->opencl.page_locked_buffer = clCreateBuffer( h->opencl.context, CL_MEM_WRITE_ONLY|CL_MEM_ALLOC_HOST_PTR, PAGE_LOCKED_BUF_SIZE, NULL, &status );
326     if( status != CL_SUCCESS )
327     {
328         x264_log( h, X264_LOG_WARNING, "OpenCL: Unable to allocate page-locked buffer, error '%d'\n", status );
329         x264_opencl_free_lookahead( h );
330         return -1;
331     }
332     h->opencl.page_locked_ptr = clEnqueueMapBuffer( h->opencl.queue, h->opencl.page_locked_buffer, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE,
333                                                     0, PAGE_LOCKED_BUF_SIZE, 0, NULL, NULL, &status );
334     if( status != CL_SUCCESS )
335     {
336         x264_log( h, X264_LOG_WARNING, "OpenCL: Unable to map page-locked buffer, error '%d'\n", status );
337         x264_opencl_free_lookahead( h );
338         return -1;
339     }
340
341     return 0;
342 }
343
344 static void x264_opencl_error_notify( const char *errinfo, const void *private_info, size_t cb, void *user_data )
345 {
346     /* Any error notification can be assumed to be fatal to the OpenCL context.
347      * We need to stop using it immediately to prevent further damage. */
348     x264_t *h = (x264_t*)user_data;
349     h->param.b_opencl = 0;
350     h->opencl.b_fatal_error = 1;
351     x264_log( h, X264_LOG_ERROR, "OpenCL: %s\n", errinfo );
352     x264_log( h, X264_LOG_ERROR, "OpenCL: fatal error, aborting encode\n" );
353 }
354
355 int x264_opencl_init( x264_t *h )
356 {
357     cl_int status;
358     cl_uint numPlatforms;
359     int ret = -1;
360
361     status = clGetPlatformIDs( 0, NULL, &numPlatforms );
362     if( status != CL_SUCCESS || numPlatforms == 0 )
363     {
364         x264_log( h, X264_LOG_WARNING, "OpenCL: Unable to query installed platforms\n");
365         return -1;
366     }
367
368     cl_platform_id *platforms = (cl_platform_id*)x264_malloc( numPlatforms * sizeof(cl_platform_id) );
369     status = clGetPlatformIDs( numPlatforms, platforms, NULL );
370     if( status != CL_SUCCESS )
371     {
372         x264_log( h, X264_LOG_WARNING, "OpenCL: Unable to query installed platforms\n");
373         x264_free( platforms );
374         return -1;
375     }
376
377     /* Select the first OpenCL platform with a GPU device that supports our
378      * required image (texture) formats */
379     for( cl_uint i = 0; i < numPlatforms; ++i )
380     {
381         cl_uint gpu_count = 0;
382         status = clGetDeviceIDs( platforms[i], CL_DEVICE_TYPE_GPU, 0, NULL, &gpu_count );
383         if( status != CL_SUCCESS || !gpu_count )
384             continue;
385
386         cl_device_id *devices = x264_malloc( sizeof(cl_device_id) * gpu_count );
387         if( !devices )
388             continue;
389
390         status = clGetDeviceIDs( platforms[i], CL_DEVICE_TYPE_GPU, gpu_count, devices, NULL );
391         if( status != CL_SUCCESS )
392         {
393             x264_free( devices );
394             continue;
395         }
396
397         /* Find a GPU device that supports our image formats */
398         for( cl_uint gpu = 0; gpu < gpu_count; gpu++ )
399         {
400             h->opencl.device = devices[gpu];
401
402             /* if the user has specified an exact device ID, skip all other
403              * GPUs.  If this device matches, allow it to continue through the
404              * checks for supported images, etc.  */
405             if( h->param.opencl_device_id && devices[gpu] != (cl_device_id) h->param.opencl_device_id )
406                 continue;
407
408             cl_bool image_support;
409             clGetDeviceInfo( h->opencl.device, CL_DEVICE_IMAGE_SUPPORT, sizeof(cl_bool), &image_support, NULL );
410             if( !image_support )
411                 continue;
412
413             cl_context context = clCreateContext( NULL, 1, &h->opencl.device, (void*)x264_opencl_error_notify, (void*)h, &status );
414             if( status != CL_SUCCESS )
415                 continue;
416
417             cl_uint imagecount = 0;
418             clGetSupportedImageFormats( context, CL_MEM_READ_WRITE, CL_MEM_OBJECT_IMAGE2D, 0, NULL, &imagecount );
419             if( !imagecount )
420             {
421                 clReleaseContext( context );
422                 continue;
423             }
424
425             cl_image_format *imageType = x264_malloc( sizeof(cl_image_format) * imagecount );
426             if( !imageType )
427             {
428                 clReleaseContext( context );
429                 continue;
430             }
431
432             clGetSupportedImageFormats( context, CL_MEM_READ_WRITE, CL_MEM_OBJECT_IMAGE2D, imagecount, imageType, NULL );
433
434             int b_has_r = 0;
435             int b_has_rgba = 0;
436             for( cl_uint j = 0; j < imagecount; j++ )
437             {
438                 if( imageType[j].image_channel_order == CL_R &&
439                     imageType[j].image_channel_data_type == CL_UNSIGNED_INT32 )
440                     b_has_r = 1;
441                 else if( imageType[j].image_channel_order == CL_RGBA &&
442                          imageType[j].image_channel_data_type == CL_UNSIGNED_INT8 )
443                     b_has_rgba = 1;
444             }
445             x264_free( imageType );
446             if( !b_has_r || !b_has_rgba )
447             {
448                 char devname[64];
449                 status = clGetDeviceInfo( h->opencl.device, CL_DEVICE_NAME, sizeof(devname), devname, NULL );
450                 if( status == CL_SUCCESS )
451                 {
452                     /* emit warning if we are discarding the user's explicit choice */
453                     int level = h->param.opencl_device_id ? X264_LOG_WARNING : X264_LOG_DEBUG;
454                     x264_log( h, level, "OpenCL: %s does not support required image formats\n", devname);
455                 }
456                 clReleaseContext( context );
457                 continue;
458             }
459
460             /* user selection of GPU device, skip N first matches */
461             if( h->param.i_opencl_device )
462             {
463                 h->param.i_opencl_device--;
464                 clReleaseContext( context );
465                 continue;
466             }
467
468             h->opencl.queue = clCreateCommandQueue( context, h->opencl.device, 0, &status );
469             if( status != CL_SUCCESS )
470             {
471                 clReleaseContext( context );
472                 continue;
473             }
474
475             h->opencl.context = context;
476
477             ret = 0;
478             break;
479         }
480
481         x264_free( devices );
482
483         if( !ret )
484             break;
485     }
486
487     x264_free( platforms );
488
489     if( !h->param.psz_clbin_file )
490         h->param.psz_clbin_file = "x264_lookahead.clbin";
491
492     if( ret )
493         x264_log( h, X264_LOG_WARNING, "OpenCL: Unable to find a compatible device\n");
494     else
495         ret = x264_opencl_init_lookahead( h );
496
497     return ret;
498 }
499
500 void x264_opencl_frame_delete( x264_frame_t *frame )
501 {
502 #define RELEASEBUF(mem) if( mem ) clReleaseMemObject( mem );
503     for( int j = 0; j < NUM_IMAGE_SCALES; j++ )
504         RELEASEBUF( frame->opencl.scaled_image2Ds[j] );
505     RELEASEBUF( frame->opencl.luma_hpel );
506     RELEASEBUF( frame->opencl.inv_qscale_factor );
507     RELEASEBUF( frame->opencl.intra_cost );
508     RELEASEBUF( frame->opencl.lowres_mvs0 );
509     RELEASEBUF( frame->opencl.lowres_mvs1 );
510     RELEASEBUF( frame->opencl.lowres_mv_costs0 );
511     RELEASEBUF( frame->opencl.lowres_mv_costs1 );
512 #undef RELEASEBUF
513 }
514
515 void x264_opencl_free( x264_t *h )
516 {
517     if( h->opencl.queue )
518         clFinish(h->opencl.queue );
519
520     x264_opencl_free_lookahead( h );
521
522     if( h->opencl.queue )
523         clReleaseCommandQueue( h->opencl.queue );
524     if( h->opencl.context )
525         clReleaseContext( h->opencl.context );
526 }
527
528 /* OpenCL misbehaves on hybrid laptops with Intel iGPU and AMD dGPU, so
529  * we consult AMD's ADL interface to detect this situation and disable
530  * OpenCL on these machines (Linux and Windows) */
531 #ifndef _WIN32
532 #define __stdcall
533 #define HINSTANCE void *
534 #endif
535 typedef void* ( __stdcall *ADL_MAIN_MALLOC_CALLBACK )( int );
536 typedef int ( *ADL_MAIN_CONTROL_CREATE )(ADL_MAIN_MALLOC_CALLBACK, int );
537 typedef int ( *ADL_ADAPTER_NUMBEROFADAPTERS_GET ) ( int* );
538 typedef int ( *ADL_POWERXPRESS_SCHEME_GET ) ( int, int *, int *, int * );
539 typedef int ( *ADL_MAIN_CONTROL_DESTROY )();
540 #define ADL_OK 0
541 #define ADL_PX_SCHEME_DYNAMIC 2
542
543 void* __stdcall adl_malloc_wrapper( int iSize ) { return x264_malloc( iSize ); }
544
545 static int x264_detect_switchable_graphics()
546 {
547     ADL_MAIN_CONTROL_CREATE          ADL_Main_Control_Create;
548     ADL_ADAPTER_NUMBEROFADAPTERS_GET ADL_Adapter_NumberOfAdapters_Get;
549     ADL_POWERXPRESS_SCHEME_GET       ADL_PowerXpress_Scheme_Get;
550     ADL_MAIN_CONTROL_DESTROY         ADL_Main_Control_Destroy;
551     HINSTANCE hDLL;
552     int ret = 0;
553
554 #if _WIN32
555     hDLL = LoadLibrary( "atiadlxx.dll" );
556     if( !hDLL )
557         hDLL = LoadLibrary( "atiadlxy.dll" );
558 #else
559     hDLL = dlopen( "libatiadlxx.so", RTLD_LAZY|RTLD_GLOBAL );
560 #define GetProcAddress dlsym
561 #endif
562     if( !hDLL )
563         return ret;
564
565     ADL_Main_Control_Create = (ADL_MAIN_CONTROL_CREATE) GetProcAddress(hDLL, "ADL_Main_Control_Create");
566     ADL_Main_Control_Destroy = (ADL_MAIN_CONTROL_DESTROY) GetProcAddress(hDLL, "ADL_Main_Control_Destroy");
567     ADL_Adapter_NumberOfAdapters_Get = (ADL_ADAPTER_NUMBEROFADAPTERS_GET) GetProcAddress(hDLL, "ADL_Adapter_NumberOfAdapters_Get");
568     ADL_PowerXpress_Scheme_Get = (ADL_POWERXPRESS_SCHEME_GET) GetProcAddress(hDLL, "ADL_PowerXpress_Scheme_Get");
569     if( !ADL_Main_Control_Destroy || !ADL_Main_Control_Destroy || !ADL_Adapter_NumberOfAdapters_Get ||
570         !ADL_PowerXpress_Scheme_Get )
571         goto bail;
572
573     if( ADL_OK != ADL_Main_Control_Create( adl_malloc_wrapper, 1) )
574         goto bail;
575
576     int numAdapters = 0;
577     if( ADL_OK != ADL_Adapter_NumberOfAdapters_Get( &numAdapters ) )
578     {
579         ADL_Main_Control_Destroy();
580         goto bail;
581     }
582
583     for( int i = 0; i < numAdapters; i++ )
584     {
585         int PXSchemeRange, PXSchemeCurrentState, PXSchemeDefaultState;
586         if( ADL_OK != ADL_PowerXpress_Scheme_Get( i, &PXSchemeRange, &PXSchemeCurrentState, &PXSchemeDefaultState) )
587             break;
588
589         if( PXSchemeRange >= ADL_PX_SCHEME_DYNAMIC )
590         {
591             ret = 1;
592             break;
593         }
594     }
595
596     ADL_Main_Control_Destroy();
597
598 bail:
599 #if _WIN32
600     FreeLibrary( hDLL );
601 #else
602     dlclose( hDLL );
603 #endif
604
605     return ret;
606 }