summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJerome Glisse <glisse@freedesktop.org>2008-12-18 18:29:51 +0100
committerCorbin Simpson <MostAwesomeDude@gmail.com>2009-02-01 23:30:19 -0800
commitb3cf2af13a5d053ce3ff0c4f3715123d34bfcc32 (patch)
treed1c76bf9748a3f0877f1b3f51fa7ae5d6d10692d
parent14f79d46a56797924550dfdb3846c8c589637319 (diff)
amd: initial winsys
-rw-r--r--src/gallium/winsys/drm/amd/Makefile28
-rw-r--r--src/gallium/winsys/drm/amd/amd_buffer.c240
-rw-r--r--src/gallium/winsys/drm/amd/amd_buffer.h54
-rw-r--r--src/gallium/winsys/drm/amd/amd_context.c298
-rw-r--r--src/gallium/winsys/drm/amd/amd_context.h68
-rw-r--r--src/gallium/winsys/drm/amd/amd_screen.c286
-rw-r--r--src/gallium/winsys/drm/amd/amd_screen.h42
-rw-r--r--src/gallium/winsys/drm/amd/amd_winsys_softpipe.c77
-rw-r--r--src/gallium/winsys/drm/amd/amd_winsys_softpipe.h37
9 files changed, 1130 insertions, 0 deletions
diff --git a/src/gallium/winsys/drm/amd/Makefile b/src/gallium/winsys/drm/amd/Makefile
new file mode 100644
index 0000000000..a1b5602c05
--- /dev/null
+++ b/src/gallium/winsys/drm/amd/Makefile
@@ -0,0 +1,28 @@
+
+TOP = ../../../../..
+include $(TOP)/configs/current
+
+LIBNAME = amd_dri.so
+
+MINIGLX_SOURCES =
+
+PIPE_DRIVERS = \
+ $(TOP)/src/gallium/drivers/softpipe/libsoftpipe.a
+
+DRIVER_SOURCES = \
+ amd_buffer.c \
+ amd_context.c \
+ amd_screen.c \
+ amd_winsys_softpipe.c
+
+C_SOURCES = \
+ $(COMMON_GALLIUM_SOURCES) \
+ $(DRIVER_SOURCES)
+
+ASM_SOURCES =
+
+include ../Makefile.template
+
+DRI_LIB_DEPS += -ldrm-radeon
+
+symlinks:
diff --git a/src/gallium/winsys/drm/amd/amd_buffer.c b/src/gallium/winsys/drm/amd/amd_buffer.c
new file mode 100644
index 0000000000..3d400243a5
--- /dev/null
+++ b/src/gallium/winsys/drm/amd/amd_buffer.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright © 2008 Jérôme Glisse
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
+ * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors:
+ * Jérôme Glisse <glisse@freedesktop.org>
+ */
+#include <stdio.h>
+#include "dri_util.h"
+#include "state_tracker/st_public.h"
+#include "pipe/p_winsys.h"
+#include "pipe/p_defines.h"
+#include "pipe/p_inlines.h"
+#include "amd_buffer.h"
+#include "amd_screen.h"
+#include "amd_context.h"
+#include "radeon_bo.h"
+#include "radeon_drm.h"
+
+static const char *amd_get_name(struct pipe_winsys *ws)
+{
+ return "AMD/DRI2";
+}
+
+static struct pipe_buffer *amd_buffer_create(struct pipe_winsys *ws,
+ unsigned alignment,
+ unsigned usage,
+ unsigned size)
+{
+ struct amd_pipe_winsys *amd_ws = (struct amd_pipe_winsys *)ws;
+ struct amd_pipe_buffer *amd_buffer;
+ uint32_t domain;
+
+ amd_buffer = calloc(1, sizeof(*amd_buffer));
+ if (amd_buffer == NULL) {
+ return NULL;
+ }
+ amd_buffer->base.refcount = 1;
+ amd_buffer->base.alignment = alignment;
+ amd_buffer->base.usage = usage;
+ amd_buffer->base.size = size;
+
+ domain = 0;
+
+ if (usage & PIPE_BUFFER_USAGE_PIXEL) {
+ domain |= RADEON_GEM_DOMAIN_VRAM;
+ }
+ if (usage & PIPE_BUFFER_USAGE_VERTEX) {
+ domain |= RADEON_GEM_DOMAIN_GTT;
+ }
+
+ if (usage & PIPE_BUFFER_USAGE_INDEX) {
+ domain |= RADEON_GEM_DOMAIN_GTT;
+ }
+ amd_buffer->bo = radeon_bo_open(amd_ws->amd_screen->bom, 0,
+ size, alignment, domain, 0);
+ if (amd_buffer->bo == NULL) {
+ free(amd_buffer);
+ }
+ return &amd_buffer->base;
+}
+
+static struct pipe_buffer *amd_buffer_user_create(struct pipe_winsys *ws,
+ void *ptr,
+ unsigned bytes)
+{
+ struct amd_pipe_buffer *amd_buffer;
+
+ amd_buffer = (struct amd_pipe_buffer*)amd_buffer_create(ws, 0, 0, bytes);
+ if (amd_buffer == NULL) {
+ return NULL;
+ }
+ radeon_bo_map(amd_buffer->bo, 1);
+ memcpy(amd_buffer->bo->ptr, ptr, bytes);
+ radeon_bo_unmap(amd_buffer->bo);
+ return &amd_buffer->base;
+}
+
+static void amd_buffer_del(struct pipe_winsys *ws, struct pipe_buffer *buffer)
+{
+ struct amd_pipe_buffer *amd_buffer = (struct amd_pipe_buffer*)buffer;
+
+ radeon_bo_unref(amd_buffer->bo);
+ free(amd_buffer);
+}
+
+static void *amd_buffer_map(struct pipe_winsys *ws,
+ struct pipe_buffer *buffer,
+ unsigned flags)
+{
+ struct amd_pipe_buffer *amd_buffer = (struct amd_pipe_buffer*)buffer;
+ int write = 0;
+
+ if (flags & PIPE_BUFFER_USAGE_CPU_WRITE) {
+ write = 1;
+ }
+ if (radeon_bo_map(amd_buffer->bo, write))
+ return NULL;
+ return amd_buffer->bo->ptr;
+}
+
+static void amd_buffer_unmap(struct pipe_winsys *ws, struct pipe_buffer *buffer)
+{
+ struct amd_pipe_buffer *amd_buffer = (struct amd_pipe_buffer*)buffer;
+
+ radeon_bo_unmap(amd_buffer->bo);
+}
+
+static void amd_fence_reference(struct pipe_winsys *ws,
+ struct pipe_fence_handle **ptr,
+ struct pipe_fence_handle *pfence)
+{
+}
+
+static int amd_fence_signalled(struct pipe_winsys *ws,
+ struct pipe_fence_handle *pfence,
+ unsigned flag)
+{
+ return 1;
+}
+
+static int amd_fence_finish(struct pipe_winsys *ws,
+ struct pipe_fence_handle *pfence,
+ unsigned flag)
+{
+ return 0;
+}
+
+static void amd_flush_frontbuffer(struct pipe_winsys *pipe_winsys,
+ struct pipe_surface *pipe_surface,
+ void *context_private)
+{
+ /* TODO: call dri2CopyRegion */
+}
+
+struct pipe_winsys *amd_pipe_winsys(struct amd_screen *amd_screen)
+{
+ struct amd_pipe_winsys *amd_ws;
+
+ amd_ws = calloc(1, sizeof(struct amd_pipe_winsys));
+ if (amd_ws == NULL) {
+ return NULL;
+ }
+ amd_ws->amd_screen = amd_screen;
+
+ amd_ws->winsys.flush_frontbuffer = amd_flush_frontbuffer;
+
+ amd_ws->winsys.buffer_create = amd_buffer_create;
+ amd_ws->winsys.buffer_destroy = amd_buffer_del;
+ amd_ws->winsys.user_buffer_create = amd_buffer_user_create;
+ amd_ws->winsys.buffer_map = amd_buffer_map;
+ amd_ws->winsys.buffer_unmap = amd_buffer_unmap;
+
+ amd_ws->winsys.fence_reference = amd_fence_reference;
+ amd_ws->winsys.fence_signalled = amd_fence_signalled;
+ amd_ws->winsys.fence_finish = amd_fence_finish;
+
+ amd_ws->winsys.get_name = amd_get_name;
+
+ return &amd_ws->winsys;
+}
+
+static struct pipe_buffer *amd_buffer_from_handle(struct amd_screen *amd_screen,
+ uint32_t handle)
+{
+ struct amd_pipe_buffer *amd_buffer;
+ struct radeon_bo *bo = NULL;
+
+ bo = radeon_bo_open(amd_screen->bom, handle, 0, 0, 0, 0);
+ if (bo == NULL) {
+ return NULL;
+ }
+ amd_buffer = calloc(1, sizeof(struct amd_pipe_buffer));
+ if (amd_buffer == NULL) {
+ radeon_bo_unref(bo);
+ return NULL;
+ }
+ amd_buffer->base.refcount = 1;
+ amd_buffer->base.usage = PIPE_BUFFER_USAGE_PIXEL;
+ amd_buffer->bo = bo;
+ return &amd_buffer->base;
+}
+
+struct pipe_surface *amd_surface_from_handle(struct amd_context *amd_context,
+ uint32_t handle,
+ enum pipe_format format,
+ int w, int h, int pitch)
+{
+ struct pipe_screen *pipe_screen = amd_context->pipe_screen;
+ struct pipe_winsys *pipe_winsys = amd_context->pipe_winsys;
+ struct pipe_texture tmpl;
+ struct pipe_surface *ps;
+ struct pipe_texture *pt;
+ struct pipe_buffer *pb;
+
+ pb = amd_buffer_from_handle(amd_context->amd_screen, handle);
+ if (pb == NULL) {
+ return NULL;
+ }
+ memset(&tmpl, 0, sizeof(tmpl));
+ tmpl.tex_usage = PIPE_TEXTURE_USAGE_DISPLAY_TARGET;
+ tmpl.target = PIPE_TEXTURE_2D;
+ tmpl.width[0] = w;
+ tmpl.height[0] = h;
+ tmpl.depth[0] = 1;
+ tmpl.format = format;
+ pf_get_block(tmpl.format, &tmpl.block);
+ tmpl.nblocksx[0] = pf_get_nblocksx(&tmpl.block, w);
+ tmpl.nblocksy[0] = pf_get_nblocksy(&tmpl.block, h);
+
+ pt = pipe_screen->texture_blanket(pipe_screen, &tmpl, &pitch, pb);
+ if (pt == NULL) {
+ winsys_buffer_reference(pipe_winsys, &pb, NULL);
+ }
+ ps = pipe_screen->get_tex_surface(pipe_screen, pt, 0, 0, 0,
+ PIPE_BUFFER_USAGE_GPU_WRITE);
+ return ps;
+}
diff --git a/src/gallium/winsys/drm/amd/amd_buffer.h b/src/gallium/winsys/drm/amd/amd_buffer.h
new file mode 100644
index 0000000000..d67967ebea
--- /dev/null
+++ b/src/gallium/winsys/drm/amd/amd_buffer.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2008 Jérôme Glisse
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
+ * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors:
+ * Jérôme Glisse <glisse@freedesktop.org>
+ */
+#ifndef AMD_BUFFER_H
+#define AMD_BUFFER_H
+
+#include "pipe/p_winsys.h"
+#include "amd_screen.h"
+#include "amd_context.h"
+#include "radeon_bo.h"
+
+struct amd_pipe_buffer {
+ struct pipe_buffer base;
+ struct radeon_bo *bo;
+};
+
+struct amd_pipe_winsys {
+ struct pipe_winsys winsys;
+ struct amd_screen *amd_screen;
+};
+
+struct pipe_winsys *amd_pipe_winsys(struct amd_screen *amd_screen);
+struct pipe_surface *amd_surface_from_handle(struct amd_context *amd_context,
+ uint32_t handle,
+ enum pipe_format format,
+ int w, int h, int pitch);
+
+#endif
diff --git a/src/gallium/winsys/drm/amd/amd_context.c b/src/gallium/winsys/drm/amd/amd_context.c
new file mode 100644
index 0000000000..228bd826d3
--- /dev/null
+++ b/src/gallium/winsys/drm/amd/amd_context.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright © 2008 Jérôme Glisse
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
+ * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors:
+ * Jérôme Glisse <glisse@freedesktop.org>
+ */
+#include <stdio.h>
+#include "dri_util.h"
+#include "pipe/p_winsys.h"
+#include "pipe/p_defines.h"
+#include "pipe/p_inlines.h"
+#include "state_tracker/st_public.h"
+#include "state_tracker/st_context.h"
+#include "amd_screen.h"
+#include "amd_context.h"
+#include "amd_buffer.h"
+#include "amd_winsys_softpipe.h"
+
+#define need_GL_ARB_fragment_program
+#define need_GL_ARB_multisample
+#define need_GL_ARB_point_parameters
+#define need_GL_ARB_shader_objects
+#define need_GL_ARB_texture_compression
+#define need_GL_ARB_vertex_buffer_object
+#define need_GL_ARB_vertex_program
+#define need_GL_ARB_vertex_shader
+#define need_GL_EXT_blend_color
+#define need_GL_EXT_blend_equation_separate
+#define need_GL_EXT_blend_func_separate
+#define need_GL_EXT_blend_minmax
+#define need_GL_EXT_cull_vertex
+#define need_GL_EXT_compiled_vertex_array
+#define need_GL_EXT_fog_coord
+#define need_GL_EXT_framebuffer_object
+#define need_GL_EXT_multi_draw_arrays
+#define need_GL_EXT_secondary_color
+#define need_GL_VERSION_2_0
+#define need_GL_VERSION_2_1
+#include "extension_helper.h"
+
+/**
+ * Extension strings exported by the amd driver.
+ */
+const struct dri_extension amd_card_extensions[] = {
+ {"GL_ARB_multitexture", NULL},
+ {"GL_ARB_texture_border_clamp", NULL},
+ {"GL_ARB_texture_rectangle", NULL},
+ {"GL_ARB_pixel_buffer_object", NULL},
+ {"GL_ARB_point_parameters", GL_ARB_point_parameters_functions},
+ {"GL_ARB_shader_objects", GL_ARB_shader_objects_functions},
+ {"GL_ARB_shading_language_100", GL_VERSION_2_0_functions},
+ {"GL_ARB_shading_language_120", GL_VERSION_2_1_functions},
+ {"GL_ARB_vertex_buffer_object", GL_ARB_vertex_buffer_object_functions},
+ {"GL_ARB_vertex_program", GL_ARB_vertex_program_functions},
+ {"GL_ARB_vertex_shader", GL_ARB_vertex_shader_functions},
+ {"GL_EXT_blend_color", GL_EXT_blend_color_functions},
+ {"GL_EXT_blend_equation_separate", GL_EXT_blend_equation_separate_functions},
+ {"GL_EXT_blend_func_separate", GL_EXT_blend_func_separate_functions},
+ {"GL_EXT_blend_minmax", GL_EXT_blend_minmax_functions},
+ {"GL_EXT_blend_subtract", NULL},
+ {"GL_EXT_compiled_vertex_array", GL_EXT_compiled_vertex_array_functions},
+ {"GL_EXT_cull_vertex", GL_EXT_cull_vertex_functions},
+ {"GL_EXT_fog_coord", GL_EXT_fog_coord_functions},
+ {"GL_EXT_framebuffer_object", GL_EXT_framebuffer_object_functions},
+ {"GL_EXT_multi_draw_arrays", GL_EXT_multi_draw_arrays_functions},
+ {"GL_EXT_packed_depth_stencil", NULL},
+ {"GL_EXT_pixel_buffer_object", NULL},
+ {"GL_EXT_secondary_color", GL_EXT_secondary_color_functions},
+ {"GL_EXT_stencil_wrap", NULL},
+ {NULL, NULL}
+};
+
+static void amd_update_renderbuffers(__DRIcontext *dri_context,
+ __DRIdrawable *dri_drawable)
+{
+ struct amd_framebuffer *amd_fb;
+ struct amd_context *amd_context;
+ unsigned attachments[10];
+ __DRIbuffer *buffers;
+ __DRIscreen *screen;
+ int i, count;
+
+ amd_context = dri_context->driverPrivate;
+ screen = dri_drawable->driScreenPriv;
+ amd_fb = dri_drawable->driverPrivate;
+ for (count = 0, i = 0; count < 6; count++) {
+ if (amd_fb->attachments & (1 << count)) {
+ attachments[i++] = count;
+ }
+ }
+
+ buffers = (*screen->dri2.loader->getBuffers)(dri_drawable,
+ &dri_drawable->w,
+ &dri_drawable->h,
+ attachments,
+ i,
+ &count,
+ dri_drawable->loaderPrivate);
+ if (buffers == NULL) {
+ return;
+ }
+
+ /* set one cliprect to cover the whole dri_drawable */
+ dri_drawable->x = 0;
+ dri_drawable->y = 0;
+ dri_drawable->backX = 0;
+ dri_drawable->backY = 0;
+ dri_drawable->numClipRects = 1;
+ dri_drawable->pClipRects[0].x1 = 0;
+ dri_drawable->pClipRects[0].y1 = 0;
+ dri_drawable->pClipRects[0].x2 = dri_drawable->w;
+ dri_drawable->pClipRects[0].y2 = dri_drawable->h;
+ dri_drawable->numBackClipRects = 1;
+ dri_drawable->pBackClipRects[0].x1 = 0;
+ dri_drawable->pBackClipRects[0].y1 = 0;
+ dri_drawable->pBackClipRects[0].x2 = dri_drawable->w;
+ dri_drawable->pBackClipRects[0].y2 = dri_drawable->h;
+
+ for (i = 0; i < count; i++) {
+ struct pipe_surface *ps;
+ enum pipe_format format = 0;
+ int index = 0;
+
+ switch (buffers[i].attachment) {
+ case __DRI_BUFFER_FRONT_LEFT:
+ index = ST_SURFACE_FRONT_LEFT;
+ switch (buffers[i].cpp) {
+ case 4:
+ format = PIPE_FORMAT_A8R8G8B8_UNORM;
+ break;
+ case 2:
+ format = PIPE_FORMAT_R5G6B5_UNORM;
+ break;
+ default:
+ /* FIXME: error */
+ return;
+ }
+ break;
+ case __DRI_BUFFER_BACK_LEFT:
+ index = ST_SURFACE_BACK_LEFT;
+ switch (buffers[i].cpp) {
+ case 4:
+ format = PIPE_FORMAT_A8R8G8B8_UNORM;
+ break;
+ case 2:
+ format = PIPE_FORMAT_R5G6B5_UNORM;
+ break;
+ default:
+ /* FIXME: error */
+ return;
+ }
+ break;
+ case __DRI_BUFFER_STENCIL:
+ case __DRI_BUFFER_DEPTH:
+ index = ST_SURFACE_DEPTH;
+ switch (buffers[i].cpp) {
+ case 4:
+ format = PIPE_FORMAT_Z24S8_UNORM;
+ break;
+ case 2:
+ format = PIPE_FORMAT_Z16_UNORM;
+ break;
+ default:
+ /* FIXME: error */
+ return;
+ }
+ break;
+ case __DRI_BUFFER_ACCUM:
+ default:
+ fprintf(stderr,
+ "unhandled buffer attach event, attacment type %d\n",
+ buffers[i].attachment);
+ return;
+ }
+
+ ps = amd_surface_from_handle(amd_context,
+ buffers[i].name,
+ format,
+ dri_drawable->w,
+ dri_drawable->h,
+ buffers[i].pitch);
+ assert(ps);
+ st_set_framebuffer_surface(amd_fb->st_framebuffer, index, ps);
+ }
+ st_resize_framebuffer(amd_fb->st_framebuffer,
+ dri_drawable->w,
+ dri_drawable->h);
+}
+
+GLboolean amd_context_create(const __GLcontextModes *visual,
+ __DRIcontextPrivate *dri_context,
+ void *shared_context)
+{
+ __DRIscreenPrivate *dri_screen;
+ struct amd_context *amd_context;
+ struct amd_screen *amd_screen;
+ struct pipe_context *pipe;
+ struct st_context *shared_st_context = NULL;
+
+ dri_context->driverPrivate = NULL;
+ amd_context = calloc(1, sizeof(struct amd_context));
+ if (amd_context == NULL) {
+ return GL_FALSE;
+ }
+
+ if (shared_context) {
+ shared_st_context = ((struct amd_context*)shared_context)->st_context;
+ }
+
+ dri_screen = dri_context->driScreenPriv;
+ amd_screen = dri_screen->private;
+ amd_context->dri_screen = dri_screen;
+ amd_context->amd_screen = amd_screen;
+ amd_context->drm_fd = dri_screen->fd;
+
+ amd_context->pipe_winsys = amd_pipe_winsys(amd_screen);
+ if (amd_context->pipe_winsys == NULL) {
+ free(amd_context);
+ return GL_FALSE;
+ }
+
+ pipe = amd_create_softpipe(amd_context);
+ amd_context->st_context = st_create_context(pipe, visual,
+ shared_st_context);
+ driInitExtensions(amd_context->st_context->ctx,
+ amd_card_extensions, GL_TRUE);
+ dri_context->driverPrivate = amd_context;
+ return GL_TRUE;
+}
+
+void amd_context_destroy(__DRIcontextPrivate *dri_context)
+{
+ struct amd_context *amd_context;
+
+ amd_context = dri_context->driverPrivate;
+ st_finish(amd_context->st_context);
+ st_destroy_context(amd_context->st_context);
+ free(amd_context);
+}
+
+GLboolean amd_context_bind(__DRIcontextPrivate *dri_context,
+ __DRIdrawablePrivate *dri_drawable,
+ __DRIdrawablePrivate *dri_readable)
+{
+ struct amd_framebuffer *drawable;
+ struct amd_framebuffer *readable;
+ struct amd_context *amd_context;
+
+ if (dri_context == NULL) {
+ st_make_current(NULL, NULL, NULL);
+ return GL_TRUE;
+ }
+
+ amd_context = dri_context->driverPrivate;
+ drawable = dri_drawable->driverPrivate;
+ readable = dri_readable->driverPrivate;
+ st_make_current(amd_context->st_context,
+ drawable->st_framebuffer,
+ readable->st_framebuffer);
+
+ amd_update_renderbuffers(dri_context, dri_drawable);
+ if (dri_drawable != dri_readable) {
+ amd_update_renderbuffers(dri_context, dri_readable);
+ }
+ return GL_TRUE;
+}
+
+GLboolean amd_context_unbind(__DRIcontextPrivate *dri_context)
+{
+ struct amd_context *amd_context;
+
+ amd_context = dri_context->driverPrivate;
+ st_flush(amd_context->st_context, PIPE_FLUSH_RENDER_CACHE, NULL);
+ return GL_TRUE;
+}
diff --git a/src/gallium/winsys/drm/amd/amd_context.h b/src/gallium/winsys/drm/amd/amd_context.h
new file mode 100644
index 0000000000..54a831f15a
--- /dev/null
+++ b/src/gallium/winsys/drm/amd/amd_context.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright © 2008 Jérôme Glisse
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
+ * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors:
+ * Jérôme Glisse <glisse@freedesktop.org>
+ */
+#ifndef AMD_CONTEXT_H
+#define AMD_CONTEXT_H
+
+#include "dri_util.h"
+#include "state_tracker/st_public.h"
+#include "state_tracker/st_context.h"
+#include "amd_screen.h"
+
+struct amd_framebuffer {
+ struct st_framebuffer *st_framebuffer;
+ unsigned attachments;
+};
+
+struct amd_context {
+ /* st */
+ struct st_context *st_context;
+ /* pipe */
+ struct pipe_screen *pipe_screen;
+ struct pipe_winsys *pipe_winsys;
+ /* DRI */
+ __DRIscreenPrivate *dri_screen;
+ __DRIdrawablePrivate *dri_drawable;
+ __DRIdrawablePrivate *dri_readable;
+ /* DRM */
+ int drm_fd;
+ /* AMD */
+ struct amd_screen *amd_screen;
+};
+
+GLboolean amd_context_create(const __GLcontextModes*,
+ __DRIcontextPrivate*,
+ void*);
+void amd_context_destroy(__DRIcontextPrivate*);
+GLboolean amd_context_bind(__DRIcontextPrivate*,
+ __DRIdrawablePrivate*,
+ __DRIdrawablePrivate*);
+GLboolean amd_context_unbind(__DRIcontextPrivate*);
+
+#endif
diff --git a/src/gallium/winsys/drm/amd/amd_screen.c b/src/gallium/winsys/drm/amd/amd_screen.c
new file mode 100644
index 0000000000..22d8973eb7
--- /dev/null
+++ b/src/gallium/winsys/drm/amd/amd_screen.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright © 2008 Jérôme Glisse
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
+ * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors:
+ * Jérôme Glisse <glisse@freedesktop.org>
+ */
+#include <stdio.h>
+#include "pipe/p_screen.h"
+#include "pipe/p_defines.h"
+#include "pipe/p_inlines.h"
+#include "pipe/p_context.h"
+#include "pipe/p_state.h"
+#include "state_tracker/st_public.h"
+#include "state_tracker/st_context.h"
+#include "utils.h"
+#include "xf86drm.h"
+#include "drm.h"
+#include "dri_util.h"
+#include "amd_screen.h"
+#include "amd_context.h"
+#include "amd_buffer.h"
+#include "radeon_bo.h"
+#include "radeon_bo_gem.h"
+#include "radeon_drm.h"
+
+extern const struct dri_extension amd_card_extensions[];
+
+static const __DRIextension *amd_screen_extensions[] = {
+ &driReadDrawableExtension,
+ &driCopySubBufferExtension.base,
+ &driSwapControlExtension.base,
+ &driFrameTrackingExtension.base,
+ &driMediaStreamCounterExtension.base,
+ NULL
+};
+
+static __DRIconfig **amd_fill_in_modes(unsigned pixel_bits,
+ unsigned depth_bits,
+ GLboolean have_back_buffer)
+{
+ __DRIconfig **configs;
+ unsigned depth_buffer_factor;
+ unsigned back_buffer_factor;
+ unsigned num_modes;
+ GLenum fb_format;
+ GLenum fb_type;
+ uint8_t depth_bits_array[3];
+ uint8_t stencil_bits_array[3];
+ uint8_t msaa_samples_array[1];
+ /* TODO: pageflipping ? */
+ static const GLenum back_buffer_modes[] = {
+ GLX_NONE, GLX_SWAP_UNDEFINED_OML, GLX_SWAP_COPY_OML
+ };
+
+ stencil_bits_array[0] = 0;
+ stencil_bits_array[1] = 0;
+ if (depth_bits == 24) {
+ stencil_bits_array[2] = 8;
+ num_modes = 3;
+ }
+
+ depth_bits_array[0] = 0;
+ depth_bits_array[1] = depth_bits;
+ depth_bits_array[2] = depth_bits;
+ depth_buffer_factor = (depth_bits == 24) ? 3 : 2;
+
+ back_buffer_factor = (have_back_buffer) ? 3 : 1;
+
+ msaa_samples_array[0] = 0;
+
+ if (pixel_bits == 16) {
+ fb_format = GL_RGB;
+ fb_type = GL_UNSIGNED_SHORT_5_6_5;
+ } else {
+ fb_format = GL_BGRA;
+ fb_type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ }
+
+ configs = (__DRIconfig **)driCreateConfigs(fb_format,
+ fb_type,
+ depth_bits_array,
+ stencil_bits_array,
+ depth_buffer_factor,
+ back_buffer_modes,
+ back_buffer_factor,
+ msaa_samples_array,
+ 1);
+ if (configs == NULL) {
+ fprintf(stderr, "[%s:%u] Error creating FBConfig!\n",
+ __FILE__, __LINE__);
+ return NULL;
+ }
+ return configs;
+}
+
+static void amd_screen_destroy(__DRIscreenPrivate *dri_screen)
+{
+ struct amd_screen *amd_screen = (struct amd_screen*)dri_screen->private;
+
+ radeon_bo_manager_gem_dtor(amd_screen->bom);
+ dri_screen = NULL;
+ free(amd_screen);
+}
+
+static const __DRIconfig **amd_screen_init(__DRIscreenPrivate *dri_screen)
+{
+ struct amd_screen *amd_screen;
+
+ /* Calling driInitExtensions here, with a NULL context pointer,
+ * does not actually enable the extensions. It just makes sure
+ * that all the dispatch offsets for all the extensions that
+ * *might* be enables are known. This is needed because the
+ * dispatch offsets need to be known when _mesa_context_create is
+ * called, but we can't enable the extensions until we have a
+ * context pointer.
+ *
+ * Hello chicken. Hello egg. How are you two today?
+ */
+ driInitExtensions(NULL, amd_card_extensions, GL_FALSE);
+
+ amd_screen = calloc(1, sizeof(struct amd_screen));
+ if (amd_screen == NULL) {
+ fprintf(stderr, "\nERROR! Allocating private area failed\n");
+ return NULL;
+ }
+ dri_screen->private = (void*)amd_screen;
+ dri_screen->extensions = amd_screen_extensions;
+ amd_screen->dri_screen = dri_screen;
+
+ amd_screen->bom = radeon_bo_manager_gem_ctor(dri_screen->fd);
+ if (amd_screen->bom == NULL) {
+ amd_screen_destroy(dri_screen);
+ return NULL;
+ }
+
+ return driConcatConfigs(amd_fill_in_modes(16, 16, 1),
+ amd_fill_in_modes(32, 24, 1));
+}
+
+static boolean amd_buffer_create(__DRIscreenPrivate *dri_screen,
+ __DRIdrawablePrivate *dri_drawable,
+ const __GLcontextModes *visual,
+ boolean is_pixmap)
+{
+ if (is_pixmap) {
+ /* TODO: implement ? */
+ return GL_FALSE;
+ } else {
+ enum pipe_format color_format, depth_format, stencil_format;
+ struct amd_framebuffer *amd_fb;
+
+ amd_fb = calloc(1, sizeof(struct amd_framebuffer));
+ if (amd_fb == NULL) {
+ return GL_FALSE;
+ }
+
+ switch (visual->redBits) {
+ case 5:
+ color_format = PIPE_FORMAT_R5G6B5_UNORM;
+ break;
+ default:
+ color_format = PIPE_FORMAT_A8R8G8B8_UNORM;
+ break;
+ }
+
+ switch (visual->depthBits) {
+ case 16:
+ depth_format = PIPE_FORMAT_Z16_UNORM;
+ break;
+ case 24:
+ depth_format = PIPE_FORMAT_S8Z24_UNORM;
+ default:
+ depth_format = PIPE_FORMAT_NONE;
+ break;
+ }
+
+ switch (visual->stencilBits) {
+ case 8:
+ /* force depth format */
+ depth_format = PIPE_FORMAT_S8Z24_UNORM;
+ stencil_format = PIPE_FORMAT_S8Z24_UNORM;
+ break;
+ default:
+ stencil_format = PIPE_FORMAT_NONE;
+ break;
+ }
+
+ amd_fb->st_framebuffer = st_create_framebuffer(visual,
+ color_format,
+ depth_format,
+ stencil_format,
+ dri_drawable->w,
+ dri_drawable->h,
+ (void*)amd_fb);
+ if (amd_fb->st_framebuffer == NULL) {
+ free(amd_fb);
+ return GL_FALSE;
+ }
+ dri_drawable->driverPrivate = (void *) amd_fb;
+
+ amd_fb->attachments = (1 << __DRI_BUFFER_FRONT_LEFT);
+ if (visual->doubleBufferMode) {
+ amd_fb->attachments |= (1 << __DRI_BUFFER_BACK_LEFT);
+ }
+ if (visual->depthBits || visual->stencilBits) {
+ amd_fb->attachments |= (1 << __DRI_BUFFER_DEPTH);
+ }
+
+ return GL_TRUE;
+ }
+}
+
+static void amd_buffer_destroy(__DRIdrawablePrivate * dri_drawable)
+{
+ struct amd_framebuffer *amd_fb;
+
+ amd_fb = dri_drawable->driverPrivate;
+ assert(amd_fb->st_framebuffer);
+ st_unreference_framebuffer(amd_fb->st_framebuffer);
+ free(amd_fb);
+}
+
+static void amd_swap_buffers(__DRIdrawablePrivate *dri_drawable)
+{
+ struct amd_framebuffer *amd_fb;
+ struct pipe_surface *back_surf;
+
+ amd_fb = dri_drawable->driverPrivate;
+ assert(amd_fb);
+ assert(amd_fb->st_framebuffer);
+
+ back_surf = st_get_framebuffer_surface(amd_fb->st_framebuffer,
+ ST_SURFACE_BACK_LEFT);
+ if (back_surf) {
+ st_notify_swapbuffers(amd_fb->st_framebuffer);
+ /* TODO: do we want to do anythings ? */
+ st_notify_swapbuffers_complete(amd_fb->st_framebuffer);
+ }
+}
+
+/**
+ * Called via glXCopySubBufferMESA() to copy a subrect of the back
+ * buffer to the front buffer/screen.
+ */
+static void amd_copy_sub_buffer(__DRIdrawablePrivate *dri_drawable,
+ int x, int y, int w, int h)
+{
+ /* TODO: ... */
+}
+
+const struct __DriverAPIRec driDriverAPI = {
+ .InitScreen = NULL,
+ .DestroyScreen = amd_screen_destroy,
+ .CreateContext = amd_context_create,
+ .DestroyContext = amd_context_destroy,
+ .CreateBuffer = amd_buffer_create,
+ .DestroyBuffer = amd_buffer_destroy,
+ .SwapBuffers = amd_swap_buffers,
+ .MakeCurrent = amd_context_bind,
+ .UnbindContext = amd_context_unbind,
+ .CopySubBuffer = amd_copy_sub_buffer,
+ .InitScreen2 = amd_screen_init,
+};
diff --git a/src/gallium/winsys/drm/amd/amd_screen.h b/src/gallium/winsys/drm/amd/amd_screen.h
new file mode 100644
index 0000000000..7f21fa477c
--- /dev/null
+++ b/src/gallium/winsys/drm/amd/amd_screen.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2008 Jérôme Glisse
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
+ * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors:
+ * Jérôme Glisse <glisse@freedesktop.org>
+ */
+#ifndef AMD_SCREEN_H
+#define AMD_SCREEN_H
+
+#include "pipe/p_winsys.h"
+#include "dri_util.h"
+#include "radeon_bo.h"
+
+struct amd_screen {
+ __DRIscreenPrivate *dri_screen;
+ struct radeon_bo_manager *bom;
+};
+
+#endif
diff --git a/src/gallium/winsys/drm/amd/amd_winsys_softpipe.c b/src/gallium/winsys/drm/amd/amd_winsys_softpipe.c
new file mode 100644
index 0000000000..b9b2a0e8b8
--- /dev/null
+++ b/src/gallium/winsys/drm/amd/amd_winsys_softpipe.c
@@ -0,0 +1,77 @@
+/**************************************************************************
+ *
+ * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ *
+ **************************************************************************/
+/*
+ * Authors: Keith Whitwell <keithw-at-tungstengraphics-dot-com>
+ */
+#include <stdio.h>
+#include "imports.h"
+#include "pipe/p_defines.h"
+#include "pipe/p_format.h"
+#include "softpipe/sp_winsys.h"
+#include "amd_context.h"
+#include "amd_winsys_softpipe.h"
+
+struct amd_softpipe_winsys {
+ struct softpipe_winsys sp_winsys;
+ struct amd_context *amd_context;
+};
+
+/**
+ * Return list of surface formats supported by this driver.
+ */
+static boolean amd_is_format_supported(struct softpipe_winsys *sws, uint format)
+{
+ switch (format) {
+ case PIPE_FORMAT_A8R8G8B8_UNORM:
+ case PIPE_FORMAT_R5G6B5_UNORM:
+ case PIPE_FORMAT_Z24S8_UNORM:
+ return TRUE;
+ default:
+ break;
+ };
+ return FALSE;
+}
+
+struct pipe_context *amd_create_softpipe(struct amd_context *amd_context)
+{
+ struct amd_softpipe_winsys *amd_sp_ws;
+ struct pipe_screen *pipe_screen;
+
+ pipe_screen = softpipe_create_screen(amd_context->pipe_winsys);
+
+ amd_sp_ws = CALLOC_STRUCT(amd_softpipe_winsys);
+ if (amd_sp_ws == NULL) {
+ return NULL;
+ }
+ amd_context->pipe_screen = pipe_screen;
+ amd_sp_ws->amd_context = amd_context;
+ amd_sp_ws->sp_winsys.is_format_supported = amd_is_format_supported;
+ return softpipe_create(pipe_screen,
+ amd_context->pipe_winsys,
+ &amd_sp_ws->sp_winsys);
+}
diff --git a/src/gallium/winsys/drm/amd/amd_winsys_softpipe.h b/src/gallium/winsys/drm/amd/amd_winsys_softpipe.h
new file mode 100644
index 0000000000..e5b68cf8a5
--- /dev/null
+++ b/src/gallium/winsys/drm/amd/amd_winsys_softpipe.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2008 Jérôme Glisse
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
+ * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors:
+ * Jérôme Glisse <glisse@freedesktop.org>
+ */
+#ifndef AMD_WINSYS_SOFTPIPE_H
+#define AMD_WINSYS_SOFTPIPE_H
+
+#include "amd_context.h"
+
+struct pipe_context *amd_create_softpipe(struct amd_context *amd_context);
+
+#endif