Skip to content

Instantly share code, notes, and snippets.

@dbarnes
Last active July 23, 2017 11:28
Show Gist options
  • Save dbarnes/94ded353e16a579ba3da52d2c6261173 to your computer and use it in GitHub Desktop.
Save dbarnes/94ded353e16a579ba3da52d2c6261173 to your computer and use it in GitHub Desktop.
Multi-GPU OpenGL on OSX with GLFW3.3
// Post here:
// Multi-GPU OpenGL on OSX with GLFW3.3
// https://medium.com/@danbarnes333/multi-gpu-opengl-on-osx-with-glfw3-3-6b0dab55467d
// Note: I’ve left in a lot of information gathering about the OpenGL Renderers that I thought
// might be helpful to those looking and can be deleted if you want more optimal code.
// For example if you dont know the virtual screen no. up front you could cycle through the
// renderers to find the desired virtual screen, checking they are:
// i) - accelerated (i.e. GPU)
// ii) - offline (renderers not connected to a display, i.e. second GPU on Mac Pro)
#include <iostream>
#include <iomanip>
#include <glog/logging.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <OpenGL/OpenGL.h>
DEFINE_int32(glfw_context_version_major, 3, "GLFW major context version");
DEFINE_int32(glfw_context_version_minor, 2, "GLFW minor context version");
DEFINE_int32(virtual_screen, -1,
"Use a specific virtual screeen which is currently believed to be "
"a stable identifier. -1 == default. On Mac Pro: 0 == Secondary "
"GPU. 1 == Primary GPU. 2 == Software.");
// Struct to hold renderer info
struct GLRendererInfo {
GLint rendererID; // RendererID number
GLint accelerated; // Whether Hardware accelerated
GLint online; // Whether renderer (/GPU) is onilne. Second GPU on Mac Pro is offline
GLint virtualScreen; // Virtual screen number
GLint videoMemoryMB;
GLint textureMemoryMB;
const GLubyte *vendor;
};
GLFWwindow *InitialiseOpenGLContext(const unsigned int &width,
const unsigned int &height,
const std::string &name) {
// Initialise GLFW context
glfwSetErrorCallback(glfw::GLFWErrorCallback);
LOG_IF(FATAL, !glfwInit()) << "GLFW failed to initialise";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,
FLAGS_glfw_context_version_major);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,
FLAGS_glfw_context_version_minor);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_SAMPLES, 0);
glfwWindowHint(GLFW_VISIBLE, GL_FALSE);
glfwWindowHint(GLFW_REFRESH_RATE, GLFW_DONT_CARE);
// This is so all OpenGL Renderers are visible (GPUs / Software)
glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, GL_TRUE);
// Initialise GLFW window
GLFWwindow *window = glfwCreateWindow(width, height, name.c_str(), NULL, NULL);
LOG_IF(FATAL, !window) << "GLFW failed to create window";
glfwMakeContextCurrent(window);
// Initialise GLEW
glewExperimental = GL_TRUE;
LOG_IF(FATAL, glewInit() != GLEW_OK) << "GLEW failed to initialise";
glGetError();
// Code to find and swap the OpenGL context to the desired virtual screen.
// We use -1 to use GLFW default
if (FLAGS_virtual_screen < 0) {
LOG(INFO) << "OpenGL Renderer: Using GLFW default.";
} else {
LOG(INFO) << "OpenGL Renderer: Using selected virtual screen: " << FLAGS_virtual_screen;
// Grab the GLFW context and pixel format for future calls
CGLContextObj contextObject = CGLGetCurrentContext();
CGLPixelFormatObj pixel_format = CGLGetPixelFormat(contextObject);
// Number of renderers
CGLRendererInfoObj rend;
GLint nRenderers = 0;
CGLQueryRendererInfo(0xffffffff, &rend, &nRenderers);
// Number of virtual screens
GLint nVirtualScreens = 0;
CGLDescribePixelFormat(pixel_format, 0, kCGLPFAVirtualScreenCount, &nVirtualScreens);
// Get renderer information
std::vector<GLRendererInfo> Renderers(nRenderers);
for (GLint i = 0; i < nRenderers; ++i) {
CGLDescribeRenderer(rend, i, kCGLRPOnline, &(Renderers[i].online));
CGLDescribeRenderer(rend, i, kCGLRPAcceleratedCompute, &(Renderers[i].accelerated));
CGLDescribeRenderer(rend, i, kCGLRPRendererID, &(Renderers[i].rendererID));
CGLDescribeRenderer(rend, i, kCGLRPVideoMemoryMegabytes, &(Renderers[i].videoMemoryMB));
CGLDescribeRenderer(rend, i, kCGLRPTextureMemoryMegabytes, &(Renderers[i].textureMemoryMB));
}
// Get corresponding virtual screen
for (GLint i = 0; i != nVirtualScreens; ++i) {
CGLSetVirtualScreen(contextObject, i);
GLint r;
CGLGetParameter(contextObject, kCGLCPCurrentRendererID, &r);
for (GLint j = 0; j < nRenderers; ++j) {
if (Renderers[j].rendererID == r) {
Renderers[j].virtualScreen = i;
Renderers[j].vendor = glGetString(GL_VENDOR);
}
}
}
// Print out information of renderers
VLOG(1) << "No. renderers: " << nRenderers
<< " No. virtual screens: " << nVirtualScreens << "\n";
for (GLint i = 0; i < nRenderers; ++i) {
VLOG(1) << "Renderer: " << i
<< " Virtual Screen: " << Renderers[i].virtualScreen
<< " Renderer ID: " << Renderers[i].rendererID
<< " Vendor: " << Renderers[i].vendor
<< " Accelerated: " << Renderers[i].accelerated
<< " Online: " << Renderers[i].online
<< " Video Memory MB: " << Renderers[i].videoMemoryMB
<< " Texture Memory MB: " << Renderers[i].textureMemoryMB << "\n";
}
// Set the context to our desired virtual screen (and therefore OpenGL renderer)
bool found = false;
for (GLint i = 0; i < nRenderers; ++i) {
if (Renderers[i].virtualScreen == FLAGS_virtual_screen) {
CGLSetVirtualScreen(contextObject, Renderers[i].virtualScreen);
found = true;
}
}
LOG_IF(FATAL, !found) << "Cound not find requested virtual screen: "
<< FLAGS_virtual_screen;
}
// set default color value to zero
glClearColor(0, 0, 0, 0);
// clear initialisation errors
(void)glGetError();
// return pointer to glfw window
return window;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment