/* * Copyright © 2008, 2009 Kristian Høgsberg * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include "eagle.h" #include "eagle-internal.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof ((a)[0])) /* FIXME: Use TLS. */ static EGLint lastError; static EGLContext currentContext; static void eglSetError(EGLint error) { lastError = error; } EAGLE_EXPORT EGLint eglGetError() { return lastError; } static __DRIbuffer * dri2GetBuffers(__DRIdrawable *driDrawable, int *width, int *height, unsigned int *attachments, int count, int *out_count, void *loaderPrivate) { EGLSurface surface = loaderPrivate; EGLDisplay display = surface->display; display->backend->getBuffers(surface, attachments, count); *width = surface->width; *height = surface->height; *out_count = surface->count; return surface->buffers; } const static __DRIdri2LoaderExtension dri2LoaderExtension = { { __DRI_DRI2_LOADER, __DRI_DRI2_LOADER_VERSION }, dri2GetBuffers }; static int getUST(int64_t *ust) { struct timeval tv; if (ust == NULL) return -EFAULT; if (gettimeofday(&tv, NULL) == 0) { ust[0] = (tv.tv_sec * 1000000) + tv.tv_usec; return 0; } else { return -errno; } } const __DRIsystemTimeExtension systemTimeExtension = { { __DRI_SYSTEM_TIME, __DRI_SYSTEM_TIME_VERSION }, getUST, NULL, }; static const __DRIextension *eglLoaderExtensions[] = { &dri2LoaderExtension.base, &systemTimeExtension.base, NULL }; static const char driDriverPath[] = EAGLE_DRIVER_PATH; static int eglLoadDriver(EGLDisplay display, const char *driverName) { char filename[PATH_MAX]; const __DRIextension **extensions; const char *path; int i; /* Don't allow setuid applications to override the driver path. */ if (geteuid() == getuid()) { path = getenv("EAGLE_DRIVER_PATH"); if (path == NULL) path = getenv("LIBGL_DRIVERS_PATH"); } if (path == NULL) path = driDriverPath; snprintf(filename, sizeof filename, "%s/%s_dri.so", path, driverName); display->driver = dlopen(filename, RTLD_NOW | RTLD_LOCAL); if (display->driver == NULL) { fprintf(stderr, "dlopen: %s\n", dlerror()); return -1; } extensions = dlsym(display->driver, __DRI_DRIVER_EXTENSIONS); if (extensions == NULL) { fprintf(stderr, "dlsym: %s\n", dlerror()); dlclose(display->driver); return -1; } for (i = 0; extensions[i]; i++) { if (strcmp(extensions[i]->name, __DRI_CORE) == 0 && extensions[i]->version >= __DRI_CORE_VERSION) { display->core = (__DRIcoreExtension *) extensions[i]; } if (strcmp(extensions[i]->name, __DRI_DRI2) == 0 && extensions[i]->version >= __DRI_DRI2_VERSION) { display->dri2 = (__DRIdri2Extension *) extensions[i]; } } if (display->core == NULL || display->dri2 == NULL) { dlclose(display->driver); return -1; } return 0; } /* Should be eagleCreateDisplay to avoid invading egl namespace?/ */ int eglInitDisplay(EGLDisplay display, const char *path, const char *driver) { const __DRIconfig **configs; const __DRIextension **extensions; int i, context_none = 0; memset(display, 0, sizeof *display); display->initialized = EGL_FALSE; display->next_surface_id = 1; display->fd = open(path, O_RDWR); if (display->fd < 0) { fprintf(stderr, "failed to open drm device %s: %m\n", path); return -1; } if (eglLoadDriver(display, driver) < 0) goto fail; display->driScreen = display->dri2->createNewScreen(0, display->fd, eglLoaderExtensions, &configs, display); if (display->driScreen == NULL) goto fail; extensions = display->core->getExtensions(display->driScreen); if (extensions == NULL) goto fail; for (i = 0; extensions[i]; i++) { if (strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0 && extensions[i]->version >= __DRI_TEX_BUFFER_VERSION) { display->texBuffer = (__DRItexBufferExtension *) extensions[i]; } if (strcmp(extensions[i]->name, __DRI2_FLUSH) == 0 && extensions[i]->version >= 2) { display->flush = (__DRI2flushExtension *) extensions[i]; } if (strcmp(extensions[i]->name, __DRI_CONTEXT_NONE) == 0 && extensions[i]->version >= __DRI_CONTEXT_NONE_VERSION) { context_none = 1; } } if (display->texBuffer == NULL) { fprintf(stderr, "dri driver has no texBuffer extension\n"); goto fail; } if (display->flush == NULL) { fprintf(stderr, "dri driver has no flush extension\n"); goto fail; } if (context_none == 0) { fprintf(stderr, "dri driver has no context none extension\n"); goto fail; } for (i = 0; configs[i]; i++) ; display->numConfigs = i; display->configs = malloc(display->numConfigs * sizeof *display->configs); for (i = 0; configs[i]; i++) { display->configs[i].driConfig = configs[i]; display->configs[i].id = i; } return 0; fail: close(display->fd); return -1; } struct dri_driver_entry { uint32_t vendor_id; uint32_t chip_id; EGLDisplay (*createDisplay)(struct udev_device *device); }; static const struct dri_driver_entry driver_map[] = { /* FIXME: We need to extract this table from the dri drivers * and store it on disk. */ { 0x8086, 0x3577, i915CreateDisplay }, /* PCI_CHIP_I830_M */ { 0x8086, 0x2562, i915CreateDisplay }, /* PCI_CHIP_845_G */ { 0x8086, 0x3582, i915CreateDisplay }, /* PCI_CHIP_I855_GM */ { 0x8086, 0x2572, i915CreateDisplay }, /* PCI_CHIP_I865_G */ { 0x8086, 0x2582, i915CreateDisplay }, /* PCI_CHIP_I915_G */ { 0x8086, 0x258a, i915CreateDisplay }, /* PCI_CHIP_E7221_G */ { 0x8086, 0x2592, i915CreateDisplay }, /* PCI_CHIP_I915_GM */ { 0x8086, 0x2772, i915CreateDisplay }, /* PCI_CHIP_I945_G */ { 0x8086, 0x27a2, i915CreateDisplay }, /* PCI_CHIP_I945_GM */ { 0x8086, 0x27ae, i915CreateDisplay }, /* PCI_CHIP_I945_GME */ { 0x8086, 0x29b2, i915CreateDisplay }, /* PCI_CHIP_Q35_G */ { 0x8086, 0x29c2, i915CreateDisplay }, /* PCI_CHIP_G33_G */ { 0x8086, 0x29d2, i915CreateDisplay }, /* PCI_CHIP_Q33_G */ { 0x8086, 0x29a2, i965CreateDisplay }, /* PCI_CHIP_I965_G */ { 0x8086, 0x2992, i965CreateDisplay }, /* PCI_CHIP_I965_Q */ { 0x8086, 0x2982, i965CreateDisplay }, /* PCI_CHIP_I965_G_1 */ { 0x8086, 0x2972, i965CreateDisplay }, /* PCI_CHIP_I946_GZ */ { 0x8086, 0x2a02, i965CreateDisplay }, /* PCI_CHIP_I965_GM */ { 0x8086, 0x2a12, i965CreateDisplay }, /* PCI_CHIP_I965_GME */ { 0x8086, 0x2a42, i965CreateDisplay }, /* PCI_CHIP_GM45_GM */ { 0x8086, 0x2e02, i965CreateDisplay }, /* PCI_CHIP_IGD_E_G */ { 0x8086, 0x2e12, i965CreateDisplay }, /* PCI_CHIP_Q45_G */ { 0x8086, 0x2e22, i965CreateDisplay }, /* PCI_CHIP_G45_G */ { 0x8086, 0x2e32, i965CreateDisplay }, /* PCI_CHIP_G41_G */ { 0x1002, 0x7249, r300CreateDisplay }, /* X1900 XT */ { 0, } }; EAGLE_EXPORT EGLDisplay eglCreateDisplayNative(struct udev_device *device) { struct udev_device *parent; const char *pci_id; uint32_t vendor_id, chip_id; int i; parent = udev_device_get_parent(device); pci_id = udev_device_get_property_value(parent, "PCI_ID"); if (sscanf(pci_id, "%x:%x", &vendor_id, &chip_id) != 2) return NULL; for (i = 0; i < ARRAY_SIZE(driver_map); i++) { if (driver_map[i].vendor_id == vendor_id && (driver_map[i].chip_id == ~0 || driver_map[i].chip_id == chip_id)) return driver_map[i].createDisplay(device); } return NULL; } EAGLE_EXPORT EGLDisplay eglGetDisplay (EGLNativeDisplayType display_id) { EGLDisplay egl_display; struct udev *udev; struct udev_device *device; udev = udev_new(); device = udev_device_new_from_syspath(udev, "/sys/class/drm/card0"); egl_display = eglGetDisplay(device); udev_device_unref(device); udev_unref(udev); return egl_display; } EAGLE_EXPORT int eglGetDisplayFD(EGLDisplay display) { return display->fd; } EAGLE_EXPORT EGLBoolean eglInitialize(EGLDisplay display, EGLint *major, EGLint *minor) { if (major != NULL) *major = 1; if (minor != NULL) *minor = 1; display->initialized = EGL_TRUE; return EGL_TRUE; } EAGLE_EXPORT EGLBoolean eglTerminate(EGLDisplay display) { return EGL_TRUE; } EAGLE_EXPORT const char * eglQueryString(EGLDisplay display, EGLint name) { if (!display->initialized) { eglSetError(EGL_NOT_INITIALIZED); return NULL; } switch (name) { case EGL_CLIENT_APIS: case EGL_EXTENSIONS: case EGL_VENDOR: case EGL_VERSION: default: eglSetError(EGL_BAD_PARAMETER); return NULL; } } EAGLE_EXPORT EGLBoolean eglGetConfigs(EGLDisplay display, EGLConfig *configs, EGLint configSize, EGLint *numConfigs) { int num, i; if (display->numConfigs > configSize) num = configSize; else num = display->numConfigs; if (numConfigs != NULL) *numConfigs = display->numConfigs; if (configs != NULL) for (i = 0; i < num; i++) configs[i] = &display->configs[i]; return EGL_TRUE; } static struct { int egl_attrib, dri_attrib; } attrib_map[] = { { EGL_BUFFER_SIZE, __DRI_ATTRIB_BUFFER_SIZE }, { EGL_RED_SIZE, __DRI_ATTRIB_RED_SIZE }, { EGL_GREEN_SIZE, __DRI_ATTRIB_GREEN_SIZE }, { EGL_BLUE_SIZE, __DRI_ATTRIB_BLUE_SIZE }, { EGL_LUMINANCE_SIZE, __DRI_ATTRIB_LUMINANCE_SIZE }, { EGL_ALPHA_SIZE, __DRI_ATTRIB_ALPHA_SIZE }, { EGL_ALPHA_MASK_SIZE, __DRI_ATTRIB_ALPHA_MASK_SIZE }, { EGL_BIND_TO_TEXTURE_RGB, __DRI_ATTRIB_BIND_TO_TEXTURE_RGB }, { EGL_BIND_TO_TEXTURE_RGBA, __DRI_ATTRIB_BIND_TO_TEXTURE_RGBA }, { EGL_CONFIG_CAVEAT, __DRI_ATTRIB_CONFIG_CAVEAT }, { EGL_DEPTH_SIZE, __DRI_ATTRIB_DEPTH_SIZE }, { EGL_LEVEL, __DRI_ATTRIB_LEVEL }, { EGL_MAX_PBUFFER_WIDTH, __DRI_ATTRIB_MAX_PBUFFER_WIDTH }, { EGL_MAX_PBUFFER_HEIGHT, __DRI_ATTRIB_MAX_PBUFFER_HEIGHT }, { EGL_MAX_PBUFFER_PIXELS, __DRI_ATTRIB_MAX_PBUFFER_PIXELS }, { EGL_MAX_SWAP_INTERVAL, __DRI_ATTRIB_MAX_SWAP_INTERVAL }, { EGL_MIN_SWAP_INTERVAL, __DRI_ATTRIB_MIN_SWAP_INTERVAL }, { EGL_SAMPLE_BUFFERS, __DRI_ATTRIB_SAMPLE_BUFFERS }, { EGL_SAMPLES, __DRI_ATTRIB_SAMPLES }, { EGL_STENCIL_SIZE, __DRI_ATTRIB_STENCIL_SIZE }, { EGL_TRANSPARENT_TYPE, __DRI_ATTRIB_TRANSPARENT_TYPE }, { EGL_TRANSPARENT_RED_VALUE, __DRI_ATTRIB_TRANSPARENT_RED_VALUE }, { EGL_TRANSPARENT_GREEN_VALUE, __DRI_ATTRIB_TRANSPARENT_GREEN_VALUE }, { EGL_TRANSPARENT_BLUE_VALUE, __DRI_ATTRIB_TRANSPARENT_BLUE_VALUE }, }; static EGLBoolean attributeMatches(EGLDisplay display, EGLConfig config, EGLint attribute, EGLint value) { EGLint config_value; /* FIXME: Handle non-exact matches. */ if (!eglGetConfigAttrib(display, config, attribute, &config_value)) return EGL_FALSE; return value == config_value; } static EGLBoolean configMatches(EGLDisplay display, EGLConfig config, const EGLint *attribList) { int i; for (i = 0; attribList[i] != EGL_NONE; i += 2) if (!attributeMatches(display, config, attribList[i], attribList[i + 1])) return EGL_FALSE; return EGL_TRUE; } EAGLE_EXPORT EGLBoolean eglChooseConfig(EGLDisplay display, const EGLint *attribList, EGLConfig *configs, EGLint configSize, EGLint *numConfigs) { int i, j; for (i = 0, j = 0; i < display->numConfigs; i++) { if (configMatches(display, &display->configs[i], attribList) && j < configSize) configs[j++] = &display->configs[i]; } if (numConfigs != NULL) *numConfigs = j; return j > 0; } EAGLE_EXPORT EGLBoolean eglGetConfigAttrib(EGLDisplay display, EGLConfig config, EGLint attribute, EGLint *value) { unsigned int dri_attrib, dri_value, i; switch (attribute) { case EGL_CONFIG_ID: *value = config->id; return EGL_TRUE; case EGL_SURFACE_TYPE: *value = EGL_WINDOW_BIT | EGL_PIXMAP_BIT | EGL_PBUFFER_BIT; return EGL_TRUE; case EGL_RENDERABLE_TYPE: *value = EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT; return EGL_TRUE; case EGL_NATIVE_RENDERABLE: *value = EGL_TRUE; return EGL_TRUE; case EGL_NATIVE_VISUAL_ID: *value = 1000; /* What's a native visual anyway? */ return EGL_TRUE; case EGL_CONFORMANT: /* FIXME: Look up __DRI_ATTRIB_CONFIG_CAVEAT instead. */ eglSetError(EGL_BAD_ATTRIBUTE); return EGL_FALSE; case EGL_COLOR_BUFFER_TYPE: return EGL_FALSE; } for (i = 0; i < ARRAY_SIZE(attrib_map); i++) if (attrib_map[i].egl_attrib == attribute) { dri_attrib = attrib_map[i].dri_attrib; break; } if (i == ARRAY_SIZE(attrib_map)) return EGL_FALSE; /* FIXME: Signedness of attribs and tokens? */ if (!display->core->getConfigAttrib(config->driConfig, dri_attrib, &dri_value)) { eglSetError(EGL_BAD_ATTRIBUTE); return EGL_FALSE; } switch (attribute) { case EGL_COLOR_BUFFER_TYPE: *value = EGL_RGB_BUFFER; *value = EGL_LUMINANCE_BUFFER; break; case EGL_CONFIG_CAVEAT: if (dri_value & __DRI_ATTRIB_SLOW_BIT) *value = EGL_SLOW_CONFIG; else if (dri_value & __DRI_ATTRIB_NON_CONFORMANT_CONFIG) *value = EGL_NON_CONFORMANT_CONFIG; else *value = EGL_NONE; break; case EGL_TRANSPARENT_TYPE: *value = EGL_NONE; *value = EGL_TRANSPARENT_RGB; break; default: *value = dri_value; break; } return EGL_TRUE; } EAGLE_EXPORT void eglInitSurface(EGLSurface surface, EGLDisplay display, EGLConfig fbconfig, int width, int height) { surface->display = display; surface->id = display->next_surface_id++; surface->width = width; surface->height = height; surface->count = 0; surface->driDrawable = display->dri2->createNewDrawable(display->driScreen, fbconfig->driConfig, surface); } EAGLE_EXPORT EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attribList) { return EGL_NO_SURFACE; } EAGLE_EXPORT EGLSurface eglCreatePbufferSurface(EGLDisplay display, EGLConfig config, const EGLint *attribList) { int i, width, height; for (i = 0; attribList[i]; i += 2) { switch (attribList[i]) { case EGL_WIDTH: width = attribList[i + 1]; break; case EGL_HEIGHT: height = attribList[i + 1]; break; case EGL_LARGEST_PBUFFER: case EGL_TEXTURE_FORMAT: case EGL_TEXTURE_TARGET: case EGL_MIPMAP_TEXTURE: case EGL_COLORSPACE: case EGL_ALPHA_FORMAT: break; } } return EGL_NO_SURFACE; } EAGLE_EXPORT EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attribList) { return EGL_NO_SURFACE; } EAGLE_EXPORT EGLBoolean eglDestroySurface(EGLDisplay display, EGLSurface surface) { display->core->destroyDrawable(surface->driDrawable); return display->backend->destroySurface(display, surface); } EAGLE_EXPORT EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) { return EGL_TRUE; } EAGLE_EXPORT EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value) { return EGL_TRUE; } EAGLE_EXPORT EGLBoolean eglBindTexImage(EGLDisplay display, EGLSurface surface, EGLint buffer) { EGLContext context; context = eglGetCurrentContext(); display->texBuffer->setTexBuffer(context->driContext, buffer, surface->driDrawable); return EGL_TRUE; } EAGLE_EXPORT EGLBoolean eglReleaseTexImage(EGLDisplay display, EGLSurface surface, EGLint buffer) { return EGL_TRUE; } EAGLE_EXPORT EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext shared, const EGLint *attribList) { EGLContext context; __DRIcontext *driShared; const __DRIconfig *driConfig; context = malloc(sizeof *context); if (shared) driShared = shared->driContext; else driShared = NULL; if (config) driConfig = config->driConfig; else driConfig = NULL; context->driContext = display->dri2->createNewContext(display->driScreen, driConfig, driShared, context); if (context->driContext == NULL) { free(context); return EGL_NO_CONTEXT; } return context; } EAGLE_EXPORT void eglDestroyContext(EGLDisplay display, EGLContext context) { if (context == currentContext) currentContext = EGL_NO_CONTEXT; display->core->destroyContext(context->driContext); free(context); } EAGLE_EXPORT EGLBoolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context) { __DRIdrawable *driDraw, *driRead; EGLBoolean status; context->drawSurface = draw; context->readSurface = read; if (draw != NULL) driDraw = draw->driDrawable; else driDraw = NULL; if (read != NULL) driRead = read->driDrawable; else driRead = NULL; status = display->core->bindContext(context->driContext, driDraw, driRead); if (status) currentContext = context; return status; } EAGLE_EXPORT EGLContext eglGetCurrentContext() { return currentContext; } EAGLE_EXPORT EGLSurface eglGetCurrentSurface(EGLint readdraw) { switch (readdraw) { case EGL_READ: return currentContext->readSurface; case EGL_DRAW: return currentContext->drawSurface; default: eglSetError(EGL_BAD_SURFACE); /* FIXME: what's this error */ return EGL_NO_SURFACE; } } EAGLE_EXPORT EGLDisplay eglGetCurrentDisplay(void) { return currentContext->display; } EAGLE_EXPORT EGLBoolean eglQueryContext(EGLDisplay display, EGLContext context, EGLint attribute, EGLint *value) { switch (attribute) { case EGL_CONFIG_ID: *value = context->config->id; return EGL_TRUE; case EGL_CONTEXT_CLIENT_TYPE: *value = EGL_OPENGL_ES_BIT; return EGL_TRUE; case EGL_CONTEXT_CLIENT_VERSION: /* FIXME: What? */ return EGL_FALSE; case EGL_RENDER_BUFFER: if (context->drawSurface == NULL) return EGL_NONE; *value = EGL_SINGLE_BUFFER; /* pixmap */ *value = EGL_BACK_BUFFER; /* pbuffer */ /* window: either, depending on surface EGL_RENDER_BUFFER */ return EGL_TRUE; default: eglSetError(EGL_BAD_ATTRIBUTE); return EGL_FALSE; } } EAGLE_EXPORT EGLBoolean eglBindAPI(EGLenum api) { return EGL_TRUE; } EAGLE_EXPORT EGLenum eglQueryAPI(void) { return EGL_TRUE; } EAGLE_EXPORT EGLBoolean eglWaitClient(void) { return EGL_TRUE; } EAGLE_EXPORT EGLBoolean eglWaitGL(void) { return EGL_TRUE; } EAGLE_EXPORT EGLBoolean eglWaitNative(EGLint engine) { return EGL_TRUE; } EAGLE_EXPORT EGLBoolean eglSwapBuffers(EGLDisplay display, EGLSurface surface) { return EGL_FALSE; /* Sorry... */ } EAGLE_EXPORT EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target) { return EGL_TRUE; } EAGLE_EXPORT EGLBoolean eglSwapInterval(EGLDisplay display, EGLint interval) { __DRIdrawable *drawable; if (display->swapControl) { drawable = currentContext->readSurface->driDrawable; display->swapControl->setSwapInterval(drawable, interval); return EGL_TRUE; } else { return EGL_FALSE; } } EAGLE_EXPORT void (*eglGetProcAddress(const char *procname))(void) { return NULL; }