diff options
Diffstat (limited to 'native.c')
-rw-r--r-- | native.c | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/native.c b/native.c new file mode 100644 index 0000000..da1be59 --- /dev/null +++ b/native.c @@ -0,0 +1,442 @@ +/* + * 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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <unistd.h> +#include <i915_drm.h> +#include <radeon_drm.h> +#include <GL/gl.h> /* 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); +} + +EAGLE_EXPORT EGLSurface +eglCreateSurface(EGLDisplay display, EGLConfig config, + uint32_t width, uint32_t height, + uint32_t colorBufferCount, const EGLint *attribList) +{ + EGLSurfaceNative nativeSurface; + int i; + + if (colorBufferCount < 1 || colorBufferCount > 2) + return NULL; + + nativeSurface = malloc(sizeof *nativeSurface); + if (nativeSurface == NULL) + return NULL; + + nativeSurface->current = &nativeSurface->colorBuffers[0]; + nativeSurface->colorBufferCount = colorBufferCount; + for (i = 0; i < colorBufferCount; i++) { + nativeSurface->colorBufferHandles[i] = + intelCreateBuffer(display->fd, + width, + height, + &nativeSurface->colorBuffers[i]); + } + + eglInitSurface(&nativeSurface->base, display, config, width, height); + + return &nativeSurface->base; +} + +EAGLE_EXPORT EGLSurface +eglCreateSurfaceForName(EGLDisplay display, EGLConfig config, + uint32_t name, uint32_t width, + uint32_t height, uint32_t stride, const EGLint *attribList) +{ + EGLSurfaceNative nativeSurface; + struct drm_gem_open open_arg; + int ret; + + nativeSurface = malloc(sizeof *nativeSurface); + if (nativeSurface == NULL) + return NULL; + + nativeSurface->current = &nativeSurface->colorBuffers[0]; + nativeSurface->colorBufferCount = 1; + nativeSurface->colorBuffers[0].attachment = __DRI_BUFFER_FRONT_LEFT; + nativeSurface->colorBuffers[0].name = name; + nativeSurface->colorBuffers[0].pitch = stride; + nativeSurface->colorBuffers[0].cpp = 4; + nativeSurface->colorBuffers[0].flags = 0; + + open_arg.name = name; + ret = ioctl(display->fd, DRM_IOCTL_GEM_OPEN, &open_arg); + if (ret < 0) { + free(nativeSurface); + return NULL; + } + nativeSurface->colorBufferHandles[0] = open_arg.handle; + + eglInitSurface(&nativeSurface->base, display, config, width, height); + + return &nativeSurface->base; +} + +EAGLE_EXPORT EGLBoolean +eglGetColorBuffer(EGLSurface surface, uint32_t index, + uint32_t *name, uint32_t *handle, uint32_t *stride) +{ + EGLSurfaceNative nativeSurface = (EGLSurfaceNative) surface; + + if (index >= nativeSurface->colorBufferCount) + return EGL_FALSE; + + *name = nativeSurface->colorBuffers[index].name; + *handle = nativeSurface->colorBufferHandles[index]; + *stride = nativeSurface->colorBuffers[index].pitch; + + return EGL_TRUE; +} + +EAGLE_EXPORT EGLBoolean +eglBindColorBuffer(EGLDisplay display, EGLSurface surface, uint32_t index) +{ + EGLSurfaceNative nativeSurface = (EGLSurfaceNative) surface; + + if (index >= nativeSurface->colorBufferCount) + return EGL_FALSE; + + display->flush->flushInvalidate(surface->driDrawable); + nativeSurface->current = &nativeSurface->colorBuffers[index]; + + return EGL_TRUE; +} |