/* * 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 /* dri_interface.h uses some GL integer types... */ #include "eagle-internal.h" #define INTEL_STRIDE_ALIGNMENT 64 struct EagleBackendNative { struct EagleBackend base; int (*createBuffer)(int fd, GLint width, GLint height, __DRIbuffer *buffer); }; typedef struct EGLSurfaceNative *EGLSurfaceNative; struct EGLSurfaceNative { struct EGLSurface base; __DRIbuffer *current; __DRIbuffer colorBuffers[2]; int colorBufferCount; uint32_t colorBufferHandles[2]; uint32_t handles[10]; }; static inline uint32_t align_to(uint32_t value, uint32_t align) { return (value + align - 1) & ~(align - 1); } static void nativeGetBuffers(EGLSurface surface, unsigned int *attachments, int count) { EGLSurfaceNative nativeSurface = (EGLSurfaceNative) surface; struct EagleBackendNative *backend = (struct EagleBackendNative *) surface->display->backend; __DRIbuffer *buffer; int i; buffer = &surface->buffers[0]; buffer->pitch = nativeSurface->current->pitch; buffer->name = nativeSurface->current->name; buffer->cpp = nativeSurface->current->cpp; if (count == surface->count) return; for (i = 0; i < count; i++) { if (attachments[i] == __DRI_BUFFER_DEPTH) surface->depth = i; buffer = &surface->buffers[i]; buffer->attachment = attachments[i]; if (buffer->attachment == __DRI_BUFFER_FRONT_LEFT) { buffer->pitch = nativeSurface->current->pitch; buffer->name = nativeSurface->current->name; buffer->cpp = nativeSurface->current->cpp; continue; } if (attachments[i] == __DRI_BUFFER_STENCIL) { buffer->name = surface->buffers[surface->depth].name; nativeSurface->handles[i] = nativeSurface->handles[surface->depth]; continue; } nativeSurface->handles[i] = backend->createBuffer(surface->display->fd, surface->width, surface->height, buffer); } surface->count = count; } static EGLBoolean nativeDestroySurface(EGLDisplay display, EGLSurface surface) { EGLSurfaceNative nativeSurface = (EGLSurfaceNative) surface; struct drm_gem_close close; int i; for (i = 0; i < nativeSurface->colorBufferCount; i++) { close.handle = nativeSurface->colorBufferHandles[i]; if (ioctl(surface->display->fd, DRM_IOCTL_GEM_CLOSE, &close) < 0) fprintf(stderr, "close of bo %d failed\n", close.handle); } for (i = 0; i < surface->count; i++) { close.handle = nativeSurface->handles[i]; if (ioctl(surface->display->fd, DRM_IOCTL_GEM_CLOSE, &close) < 0) fprintf(stderr, "close of bo %d failed\n", close.handle); } free(surface); return EGL_TRUE; } static EGLDisplay nativeCreateDisplay(struct udev_device *device, const char *driver, const struct EagleBackend *backend) { EGLDisplay display; const char *path; display = malloc(sizeof *display); if (display == NULL) return NULL; path = udev_device_get_devnode(device); if (eglInitDisplay(display, path, driver) < 0) { free(display); return NULL; } display->backend = backend; return display; } /* Look at xf86-video-intel/src/common.h for the full horror of device * identification. */ enum { GEN_1 = 0x10, GEN_2 = 0x20, GEN_3 = 0x40, GEN_31 = 0x41, GEN_4 = 0x80, GEN_MAJOR_MASK = 0xf0, GEN_MINOR_MASK = 0x0f, }; #define IS_I965(x) ((x) & GEN_4) #define IS_I915(x) ((x) & GEN_3) #define IS_I9xx(x) ((x) & (GEN_3 | GEN_4)) #define IS_I8xx(x) ((x) & (GEN_1 | GEN_2)) #define HAS_128_BYTE_Y_TILING(x) (((x) & (GEN_3 | GEN_4 | GEN_MINOR_MASK)) > GEN_3) static uint32_t tiling_stride (int dev, int tiling_mode, uint32_t pitch) { uint32_t tile_width; if (tiling_mode == I915_TILING_NONE) return pitch; if (tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING (dev)) tile_width = 128; else tile_width = 512; /* 965+ just needs multiples of tile width */ if (IS_I965 (dev)) return align_to (pitch, tile_width); /* Pre-965 needs power of two tile widths */ while (tile_width < pitch) tile_width <<= 1; return tile_width; } static uint32_t tiling_size (int dev, uint32_t tiling, uint32_t size) { uint32_t fence; if (tiling == I915_TILING_NONE) return size; /* The 965 can have fences at any page boundary. */ if (IS_I965 (dev)) return align_to (size, 4096); /* Align the size to a power of two greater than the smallest fence. */ if (IS_I9xx (dev)) fence = 1024 * 1024; /* 1 MiB */ else fence = 512 * 1024; /* 512 KiB */ while (fence < size) fence <<= 1; return fence; } static int intelCreateBuffer(int fd, GLint width, GLint height, __DRIbuffer *buffer) { struct drm_i915_gem_create create; struct drm_gem_flink flink; uint32_t size; int tiling; int dev = GEN_4; /* XXX query using I915_GETPARAM + PARAM_CHIPSET_ID */ tiling = I915_TILING_X; buffer->pitch = align_to(width * 4, INTEL_STRIDE_ALIGNMENT); if (tiling != I915_TILING_NONE) { buffer->pitch = tiling_stride (dev, tiling, buffer->pitch); size = buffer->pitch * height; size = tiling_size (dev, tiling, size); } else { size = buffer->pitch * height; } create.size = size; if (ioctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create)) { fprintf(stderr, "failed to create buffer\n"); return -1; } if (tiling != I915_TILING_NONE) { struct drm_i915_gem_set_tiling set_tiling; memset (&set_tiling, 0, sizeof (set_tiling)); set_tiling.handle = create.handle; set_tiling.tiling_mode = tiling; set_tiling.stride = buffer->pitch; if (ioctl (fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling)) { fprintf(stderr, "failed to enable tiling\n"); } } create.size = size; if (ioctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create)) { fprintf(stderr, "failed to create buffer\n"); return -1; } flink.handle = create.handle; if (ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink) < 0) { fprintf(stderr, "failed to create buffer\n"); return -1; } buffer->name = flink.name; buffer->cpp = 4; return create.handle; } static const struct EagleBackendNative intelBackend = { { nativeGetBuffers, nativeDestroySurface, }, intelCreateBuffer }; EGLDisplay i915CreateDisplay(struct udev_device *device) { return nativeCreateDisplay(device, "i915", &intelBackend.base); } EGLDisplay i965CreateDisplay(struct udev_device *device) { return nativeCreateDisplay(device, "i965", &intelBackend.base); } #define RADEON_ALIGNMENT 64 static int radeonCreateBuffer(int fd, GLint width, GLint height, __DRIbuffer *buffer) { #ifdef DRM_IOCTL_RADEON_GEM_CREATE struct drm_radeon_gem_create create; struct drm_gem_flink flink; buffer->pitch = align_to(width * 4, RADEON_ALIGNMENT); buffer->cpp = 4; create.size = buffer->pitch * height; create.alignment = 4096; create.initial_domain = RADEON_GEM_DOMAIN_VRAM; create.flags = RADEON_GEM_NO_BACKING_STORE; if (ioctl(fd, DRM_IOCTL_RADEON_GEM_CREATE, &create)) { fprintf(stderr, "failed to create buffer\n"); return -1; } flink.handle = create.handle; if (ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink) < 0) { fprintf(stderr, "failed to create buffer\n"); return -1; } buffer->name = flink.name; return create.handle; #else return -1; #endif } static const struct EagleBackendNative radeonBackend = { { nativeGetBuffers, nativeDestroySurface, }, radeonCreateBuffer }; EGLDisplay r300CreateDisplay(struct udev_device *device) { return nativeCreateDisplay(device, "r300", &radeonBackend.base); }