blob: 7921b8a542624c2581a1bf8781b856001a86dc68 [file] [log] [blame]
bungeman@google.com16bab872011-05-17 14:24:46 +00001#include "SkEGLContext.h"
2#include "SkTypes.h"
3
4#include <GL/gl.h>
5#include <GL/glext.h>
6#include <GL/glu.h>
7#include <GL/glx.h>
8#include <X11/Xlib.h>
9
10#define SK_GL_GET_PROC(T, F) T F = NULL; \
11 F = (T) glXGetProcAddressARB(reinterpret_cast<const GLubyte*>(#F));
12
13static bool ctxErrorOccurred = false;
14static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
15 ctxErrorOccurred = true;
16 return 0;
17}
18
19SkEGLContext::SkEGLContext() : context(NULL), display(NULL), pixmap(0), glxPixmap(0) {
20}
21
22SkEGLContext::~SkEGLContext() {
23 if (this->display) {
24 glXMakeCurrent(this->display, 0, 0);
25
26 if (this->context)
27 glXDestroyContext(this->display, this->context);
28
29 if (this->glxPixmap)
30 glXDestroyGLXPixmap(this->display, this->glxPixmap);
31
32 if (this->pixmap)
33 XFreePixmap(this->display, this->pixmap);
34
35 XCloseDisplay(this->display);
36 }
37}
38
39bool SkEGLContext::init(const int width, const int height) {
40 Display *display = XOpenDisplay(0);
41 this->display = display;
42
43 if (!display) {
44 SkDebugf("Failed to open X display.\n");
45 return false;
46 }
47
48 // Get a matching FB config
49 static int visual_attribs[] = {
50 GLX_X_RENDERABLE , True,
51 GLX_DRAWABLE_TYPE , GLX_PIXMAP_BIT,
52 GLX_RENDER_TYPE , GLX_RGBA_BIT,
53 GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR,
54 GLX_RED_SIZE , 8,
55 GLX_GREEN_SIZE , 8,
56 GLX_BLUE_SIZE , 8,
57 GLX_ALPHA_SIZE , 8,
58 GLX_DEPTH_SIZE , 24,
59 GLX_STENCIL_SIZE , 8,
60 GLX_DOUBLEBUFFER , True,
61 //GLX_SAMPLE_BUFFERS , 1,
62 //GLX_SAMPLES , 4,
63 None
64 };
65
66 int glx_major, glx_minor;
67
68 // FBConfigs were added in GLX version 1.3.
69 if (!glXQueryVersion( display, &glx_major, &glx_minor) ||
70 ( (glx_major == 1) && (glx_minor < 3) ) || (glx_major < 1))
71 {
72 SkDebugf("Invalid GLX version.");
73 return false;
74 }
75
76 //SkDebugf("Getting matching framebuffer configs.\n");
77 int fbcount;
78 GLXFBConfig *fbc = glXChooseFBConfig(display, DefaultScreen(display),
79 visual_attribs, &fbcount);
80 if (!fbc) {
81 SkDebugf("Failed to retrieve a framebuffer config.\n");
82 return false;
83 }
84 //SkDebugf("Found %d matching FB configs.\n", fbcount);
85
86 // Pick the FB config/visual with the most samples per pixel
87 //SkDebugf("Getting XVisualInfos.\n");
88 int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;
89
90 int i;
91 for (i = 0; i < fbcount; ++i) {
92 XVisualInfo *vi = glXGetVisualFromFBConfig(display, fbc[i]);
93 if (vi) {
94 int samp_buf, samples;
95 glXGetFBConfigAttrib(display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
96 glXGetFBConfigAttrib(display, fbc[i], GLX_SAMPLES, &samples);
97
98 //SkDebugf(" Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
99 // " SAMPLES = %d\n",
100 // i, (unsigned int)vi->visualid, samp_buf, samples);
101
102 if (best_fbc < 0 || (samp_buf && samples > best_num_samp))
103 best_fbc = i, best_num_samp = samples;
104 if (worst_fbc < 0 || !samp_buf || samples < worst_num_samp)
105 worst_fbc = i, worst_num_samp = samples;
106 }
107 XFree(vi);
108 }
109
110 GLXFBConfig bestFbc = fbc[best_fbc];
111
112 // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
113 XFree(fbc);
114
115 // Get a visual
116 XVisualInfo *vi = glXGetVisualFromFBConfig(display, bestFbc);
117 //SkDebugf("Chosen visual ID = 0x%x\n", (unsigned int)vi->visualid);
118
119 Pixmap pixmap = XCreatePixmap(
120 display, RootWindow(display, vi->screen), width, height, vi->depth
121 );
122
123 this->pixmap = pixmap;
124 if (!pixmap) {
125 SkDebugf("Failed to create pixmap.\n");
126 return false;
127 }
128
129 GLXPixmap glxPixmap = glXCreateGLXPixmap(display, vi, pixmap);
130 this->glxPixmap = glxPixmap;
131
132 // Done with the visual info data
133 XFree(vi);
134
135 // Create the context
136 GLXContext ctx = 0;
137
138 // Install an X error handler so the application won't exit if GL 3.0
139 // context allocation fails.
140 //
141 // Note this error handler is global.
142 // All display connections in all threads of a process use the same
143 // error handler, so be sure to guard against other threads issuing
144 // X commands while this code is running.
145 ctxErrorOccurred = false;
146 int (*oldHandler)(Display*, XErrorEvent*) =
147 XSetErrorHandler(&ctxErrorHandler);
148
149 // Get the default screen's GLX extension list
150 const char *glxExts = glXQueryExtensionsString(
151 display, DefaultScreen(display)
152 );
153 // Check for the GLX_ARB_create_context extension string and the function.
154 // If either is not present, use GLX 1.3 context creation method.
155 if (!gluCheckExtension(
156 reinterpret_cast<const GLubyte*>("GLX_ARB_create_context")
157 , reinterpret_cast<const GLubyte*>(glxExts)))
158 {
159 //SkDebugf("GLX_ARB_create_context not found."
160 // " Using old-style GLX context.\n");
161 ctx = glXCreateNewContext(display, bestFbc, GLX_RGBA_TYPE, 0, True);
162
163 } else {
164 //SkDebugf("Creating context.\n");
165
166 SK_GL_GET_PROC(PFNGLXCREATECONTEXTATTRIBSARBPROC, glXCreateContextAttribsARB)
167 int context_attribs[] = {
168 GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
169 GLX_CONTEXT_MINOR_VERSION_ARB, 0,
170 //GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
171 None
172 };
173 ctx = glXCreateContextAttribsARB(
174 display, bestFbc, 0, True, context_attribs
175 );
176
177 // Sync to ensure any errors generated are processed.
178 XSync(display, False);
179 if (!ctxErrorOccurred && ctx) {
180 //SkDebugf( "Created GL 3.0 context.\n" );
181 } else {
182 // Couldn't create GL 3.0 context.
183 // Fall back to old-style 2.x context.
184 // When a context version below 3.0 is requested,
185 // implementations will return the newest context version compatible
186 // with OpenGL versions less than version 3.0.
187
188 // GLX_CONTEXT_MAJOR_VERSION_ARB = 1
189 context_attribs[1] = 1;
190 // GLX_CONTEXT_MINOR_VERSION_ARB = 0
191 context_attribs[3] = 0;
192
193 ctxErrorOccurred = false;
194
195 //SkDebugf("Failed to create GL 3.0 context."
196 // " Using old-style GLX context.\n");
197 ctx = glXCreateContextAttribsARB(
198 display, bestFbc, 0, True, context_attribs
199 );
200 }
201 }
202
203 // Sync to ensure any errors generated are processed.
204 XSync(display, False);
205
206 // Restore the original error handler
207 XSetErrorHandler(oldHandler);
208
209 if (ctxErrorOccurred || !ctx) {
210 SkDebugf("Failed to create an OpenGL context.\n");
211 return false;
212 }
213 this->context = ctx;
214
215 // Verify that context is a direct context
216 if (!glXIsDirect(display, ctx)) {
217 //SkDebugf("Indirect GLX rendering context obtained.\n");
218 } else {
219 //SkDebugf("Direct GLX rendering context obtained.\n");
220 }
221
222 //SkDebugf("Making context current.\n");
223 if (!glXMakeCurrent(display, glxPixmap, ctx)) {
224 SkDebugf("Could not set the context.\n");
225 return false;
226 }
227
228 //Setup the framebuffers
229 const GLubyte* glExts = glGetString(GL_EXTENSIONS);
230 if (!gluCheckExtension(
231 reinterpret_cast<const GLubyte*>("GL_EXT_framebuffer_object")
232 , glExts))
233 {
234 SkDebugf("GL_EXT_framebuffer_object not found.\n");
235 return false;
236 }
237 SK_GL_GET_PROC(PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffersEXT)
238 SK_GL_GET_PROC(PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebufferEXT)
239 SK_GL_GET_PROC(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffersEXT)
240 SK_GL_GET_PROC(PFNGLBINDRENDERBUFFERPROC, glBindRenderbufferEXT)
241 SK_GL_GET_PROC(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorageEXT)
242 SK_GL_GET_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbufferEXT)
243 SK_GL_GET_PROC(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatusEXT)
244
245 GLuint fboID;
246 GLuint cbID;
247 GLuint dsID;
248 glGenFramebuffersEXT(1, &fboID);
249 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID);
250 glGenRenderbuffersEXT(1, &cbID);
251 glBindRenderbufferEXT(GL_RENDERBUFFER, cbID);
252 glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_RGBA, width, height);
253 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cbID);
254 glGenRenderbuffersEXT(1, &dsID);
255 glBindRenderbufferEXT(GL_RENDERBUFFER, dsID);
256 glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
257 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, dsID);
258 glViewport(0, 0, width, height);
259 glClearStencil(0);
260 glClear(GL_STENCIL_BUFFER_BIT);
261
262 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
263 return GL_FRAMEBUFFER_COMPLETE == status;
264}