Chromium Code Reviews| Index: chrome/gpu/gpu_info_collector_linux.cc |
| =================================================================== |
| --- chrome/gpu/gpu_info_collector_linux.cc (revision 69274) |
| +++ chrome/gpu/gpu_info_collector_linux.cc (working copy) |
| @@ -4,18 +4,253 @@ |
| #include "chrome/gpu/gpu_info_collector.h" |
| +#include <dlfcn.h> |
| +#include <vector> |
| + |
| +#include "app/gfx/gl/gl_bindings.h" |
| +#include "app/gfx/gl/gl_context.h" |
| +#include "app/gfx/gl/gl_implementation.h" |
| +#include "base/logging.h" |
| +#include "base/scoped_ptr.h" |
| +#include "base/string_util.h" |
| + |
| +namespace { |
| + |
| +// PciDevice and PciAccess are defined to access libpci functions. Their |
| +// members match the corresponding structures defined by libpci in size up to |
| +// fields we may access. For those members we don't use, their names are |
| +// defined as "fieldX", etc., or, left out if they are declared after the |
| +// members we care about in libpci. |
| + |
| +struct PciDevice { |
| + PciDevice* next; |
| + |
| + uint16 field0; |
| + uint8 field1; |
| + uint8 field2; |
| + uint8 field3; |
| + int field4; |
| + |
| + uint16 vendor_id; |
| + uint16 device_id; |
| + uint16 device_class; |
| +}; |
| + |
| +struct PciAccess { |
| + unsigned int field0; |
| + int field1; |
| + int field2; |
| + char* field3; |
| + int field4; |
| + int field5; |
| + unsigned int field6; |
| + int field7; |
| + |
| + void (*function0)(); |
| + void (*function1)(); |
| + void (*function2)(); |
| + |
| + PciDevice* device_list; |
| +}; |
| + |
| +// Define function types. |
| +typedef PciAccess* (*FT_pci_alloc)(); |
| +typedef void (*FT_pci_init)(PciAccess*); |
| +typedef void (*FT_pci_cleanup)(PciAccess*); |
| +typedef void (*FT_pci_scan_bus)(PciAccess*); |
| +typedef void (*FT_pci_scan_bus)(PciAccess*); |
| +typedef int (*FT_pci_fill_info)(PciDevice*, int); |
| +typedef char* (*FT_pci_lookup_name)(PciAccess*, char*, int, int, ...); |
| + |
| +// This includes dynamically linked library handle and functions pointers from |
| +// libpci. |
| +struct PciInterface { |
| + void* lib_handle; |
| + |
| + FT_pci_alloc pci_alloc; |
| + FT_pci_init pci_init; |
| + FT_pci_cleanup pci_cleanup; |
| + FT_pci_scan_bus pci_scan_bus; |
| + FT_pci_fill_info pci_fill_info; |
| + FT_pci_lookup_name pci_lookup_name; |
| +}; |
| + |
| +// This dynamically opens libpci and get function pointers we need. Return |
| +// NULL if library fails to open or any functions can not be located. |
| +// Returned interface (if not NULL) should be deleted in FinalizeLibPci. |
| +PciInterface* InitializeLibPci(const char* lib_name) { |
| + void* handle = dlopen(lib_name, RTLD_LAZY); |
| + if (handle == NULL) { |
| + LOG(ERROR) << "Fail to dlopen libpci"; |
| + return NULL; |
| + } |
| + PciInterface* interface = new struct PciInterface; |
| + interface->lib_handle = handle; |
| + interface->pci_alloc = reinterpret_cast<FT_pci_alloc>( |
| + dlsym(handle, "pci_alloc")); |
| + interface->pci_init = reinterpret_cast<FT_pci_init>( |
| + dlsym(handle, "pci_init")); |
| + interface->pci_cleanup = reinterpret_cast<FT_pci_cleanup>( |
| + dlsym(handle, "pci_cleanup")); |
| + interface->pci_scan_bus = reinterpret_cast<FT_pci_scan_bus>( |
| + dlsym(handle, "pci_scan_bus")); |
| + interface->pci_fill_info = reinterpret_cast<FT_pci_fill_info>( |
| + dlsym(handle, "pci_fill_info")); |
| + interface->pci_lookup_name = reinterpret_cast<FT_pci_lookup_name>( |
| + dlsym(handle, "pci_lookup_name")); |
| + if (interface->pci_alloc == NULL || |
| + interface->pci_init == NULL || |
| + interface->pci_cleanup == NULL || |
| + interface->pci_scan_bus == NULL || |
| + interface->pci_fill_info == NULL || |
| + interface->pci_lookup_name == NULL) { |
| + LOG(ERROR) << "Missing required function(s) from libpci"; |
| + dlclose(handle); |
| + delete interface; |
| + return NULL; |
| + } |
| + return interface; |
| +} |
| + |
| +// This close the dynamically opened libpci and delete the interface. |
| +void FinalizeLibPci(PciInterface*& interface) { |
| + DCHECK(interface != NULL && interface->lib_handle != NULL); |
| + dlclose(interface->lib_handle); |
| + delete interface; |
| + interface = NULL; |
| +} |
| + |
| +// This creates an offscreen GL context for gl queries. Returned GLContext |
| +// should be deleted in FinalizeGLContext. |
| +gfx::GLContext* InitializeGLContext() { |
| + if (!gfx::GLContext::InitializeOneOff()) { |
| + LOG(ERROR) << "gfx::GLContext::InitializeOneOff() failed"; |
| + return NULL; |
| + } |
| + gfx::GLContext* context = gfx::GLContext::CreateOffscreenGLContext(NULL); |
| + if (context == NULL) { |
| + LOG(ERROR) << "gfx::GLContext::CreateOffscreenGLContext(NULL) failed"; |
| + return NULL; |
| + } |
| + if (!context->MakeCurrent()) { |
| + LOG(ERROR) << "gfx::GLContext::MakeCurrent() failed"; |
| + context->Destroy(); |
| + delete context; |
| + return NULL; |
| + } |
| + return context; |
| +} |
| + |
| +// This destroy and delete the GL context. |
| +void FinalizeGLContext(gfx::GLContext*& context) { |
| + DCHECK(context != NULL); |
| + context->Destroy(); |
| + delete context; |
| + context = NULL; |
| +} |
| + |
| +} // namespace anonymous |
| + |
| namespace gpu_info_collector { |
| bool CollectGraphicsInfo(GPUInfo* gpu_info) { |
| - // TODO(rlp): complete this function |
| - // TODO(apatrick): this illustrates how can_lose_context will be implemented |
| - // on this platform in the future. |
| - // bool can_lose_context = |
| - // gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2; |
| + gfx::GLContext* context = InitializeGLContext(); |
| + if (context == NULL) |
| + return false; |
| - gpu_info->SetProgress(GPUInfo::kComplete); |
| + // TODO(zmo): collect driver version, pixel shader version, vertex shader |
| + // version, and gl version. |
| + std::wstring driver_version = L""; |
| + uint32 pixel_shader_version = 0; |
| + uint32 vertex_shader_version = 0; |
| + uint32 gl_version = 0; |
| + bool can_lose_context = |
| + (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2); |
| - return true; |
| + // TODO(zmo): be more flexible about library name. |
| + PciInterface* interface = InitializeLibPci("libpci.so.3"); |
| + if (interface == NULL) { |
| + FinalizeGLContext(context); |
| + return false; |
| + } |
| + |
| + PciAccess* access = (interface->pci_alloc)(); |
| + DCHECK(access != NULL); |
| + (interface->pci_init)(access); |
| + (interface->pci_scan_bus)(access); |
| + std::vector<PciDevice*> gpu_list; |
| + PciDevice* gpu_active = NULL; |
| + for (PciDevice* device = access->device_list; |
| + device != NULL; device = device->next) { |
| + (interface->pci_fill_info)(device, 33); // Fill the IDs and class fields. |
| + if (device->device_class == 0x0300) { // Device class is DISPLAY_VGA. |
| + gpu_list.push_back(device); |
| + } |
| + } |
| + if (gpu_list.size() == 1) { |
| + gpu_active = gpu_list[0]; |
| + } else { |
| + // If more than one graphics card are identified, find the one that matches |
| + // gl VENDOR and RENDERER info. |
| + std::string gl_vendor_string = |
| + reinterpret_cast<const char*>(glGetString(GL_VENDOR)); |
| + std::string gl_renderer_string = |
| + reinterpret_cast<const char*>(glGetString(GL_RENDERER)); |
| + const int buffer_size = 255; |
| + scoped_array<char> buffer(new char[buffer_size]); |
| + std::vector<PciDevice*> candidates; |
| + for (size_t i = 0; i < gpu_list.size(); ++i) { |
| + PciDevice* gpu = gpu_list[i]; |
| + if ((interface->pci_lookup_name)(access, |
|
Ken Russell (switch to Gerrit)
2010/12/20 19:52:54
Add a comment indicating that the current implemen
|
| + buffer.get(), |
| + buffer_size, |
| + 1, |
| + gpu->vendor_id) != buffer.get()) |
| + continue; |
| + std::string vendor_string = buffer.get(); |
| + const bool kCaseSensitive = false; |
| + if (!StartsWithASCII(gl_vendor_string, vendor_string, kCaseSensitive)) |
| + continue; |
| + if ((interface->pci_lookup_name)(access, |
| + buffer.get(), |
| + buffer_size, |
| + 2, |
| + gpu->vendor_id, |
| + gpu->device_id) != buffer.get()) |
| + continue; |
| + std::string device_string = buffer.get(); |
| + size_t begin = device_string.find_first_of('['); |
| + size_t end = device_string.find_last_of(']'); |
| + if (begin != std::string::npos && end != std::string::npos && |
| + begin < end) { |
| + device_string = device_string.substr(begin + 1, end - begin - 1); |
| + } |
| + if (StartsWithASCII(gl_renderer_string, device_string, kCaseSensitive)) { |
| + gpu_active = gpu; |
| + break; |
| + } |
| + // If a device's vendor matches gl VENDOR string, we want to consider the |
| + // possibility that libpci may not return the exact same name as gl |
| + // RENDERER string. |
| + candidates.push_back(gpu); |
| + } |
| + if (gpu_active == NULL && candidates.size() == 1) |
| + gpu_active = candidates[0]; |
| + } |
| + if (gpu_active != NULL) { |
| + gpu_info->SetGraphicsInfo(gpu_active->vendor_id, |
| + gpu_active->device_id, |
| + driver_version, |
| + pixel_shader_version, |
| + vertex_shader_version, |
| + gl_version, |
| + can_lose_context); |
| + gpu_info->SetProgress(GPUInfo::kComplete); |
| + } |
| + (interface->pci_cleanup)(access); |
| + FinalizeLibPci(interface); |
| + FinalizeGLContext(context); |
| + return (gpu_active != NULL); |
| } |
| } // namespace gpu_info_collector |