summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Paul <[email protected]>2006-03-20 18:51:57 +0000
committerBrian Paul <[email protected]>2006-03-20 18:51:57 +0000
commit519b23b21f9cd6945fd17cdb26e7a6f531cdeec0 (patch)
treecad1402052d00a0e4140454baa07746336019a75
parent4991888fa0ea8e31e3cd2a0d87bb7e205ad1dccd (diff)
Lots of changes/fixes for rendering to framebuffer objects.
- When deleting texture objects, unbind from FBOs if necessary. - Changed driver hooks for starting/ending render to texture. - Now properly handle case where gl[Copy]TexImage() is called after glFramebufferTexture[123]D(). That didn't work before.
-rw-r--r--src/mesa/main/dd.h8
-rw-r--r--src/mesa/main/fbobject.c44
-rw-r--r--src/mesa/main/fbobject.h1
-rw-r--r--src/mesa/main/teximage.c65
-rw-r--r--src/mesa/main/texobj.c133
-rw-r--r--src/mesa/main/texrender.c49
-rw-r--r--src/mesa/main/texrender.h5
7 files changed, 211 insertions, 94 deletions
diff --git a/src/mesa/main/dd.h b/src/mesa/main/dd.h
index 6af304fe26a..4b2764979d8 100644
--- a/src/mesa/main/dd.h
+++ b/src/mesa/main/dd.h
@@ -808,12 +808,10 @@ struct dd_function_table {
GLenum attachment,
struct gl_renderbuffer *rb);
void (*RenderbufferTexture)(GLcontext *ctx,
- struct gl_renderbuffer_attachment *att,
- struct gl_texture_object *texObj,
- GLenum texTarget, GLuint level, GLuint zoffset);
+ struct gl_framebuffer *fb,
+ struct gl_renderbuffer_attachment *att);
void (*FinishRenderTexture)(GLcontext *ctx,
- struct gl_texture_object *texObj,
- GLuint face, GLuint level);
+ struct gl_renderbuffer_attachment *att);
/*@}*/
#endif
#if FEATURE_EXT_framebuffer_blit
diff --git a/src/mesa/main/fbobject.c b/src/mesa/main/fbobject.c
index a83ed56069d..53c4d27e81a 100644
--- a/src/mesa/main/fbobject.c
+++ b/src/mesa/main/fbobject.c
@@ -155,9 +155,7 @@ _mesa_remove_attachment(GLcontext *ctx, struct gl_renderbuffer_attachment *att)
else {
/* tell driver that we're done rendering to this texture. */
if (ctx->Driver.FinishRenderTexture) {
- ctx->Driver.FinishRenderTexture(ctx, att->Texture,
- att->CubeMapFace,
- att->TextureLevel);
+ ctx->Driver.FinishRenderTexture(ctx, att);
}
}
att->Texture = NULL;
@@ -182,6 +180,7 @@ _mesa_remove_attachment(GLcontext *ctx, struct gl_renderbuffer_attachment *att)
*/
void
_mesa_set_texture_attachment(GLcontext *ctx,
+ struct gl_framebuffer *fb,
struct gl_renderbuffer_attachment *att,
struct gl_texture_object *texObj,
GLenum texTarget, GLuint level, GLuint zoffset)
@@ -208,6 +207,10 @@ _mesa_set_texture_attachment(GLcontext *ctx,
}
att->Zoffset = zoffset;
att->Complete = GL_FALSE;
+
+ if (att->Texture->Image[att->CubeMapFace][att->TextureLevel]) {
+ ctx->Driver.RenderbufferTexture(ctx, fb, att);
+ }
}
@@ -903,8 +906,7 @@ check_texture_render(GLcontext *ctx, struct gl_framebuffer *fb)
struct gl_renderbuffer_attachment *att = fb->Attachment + i;
struct gl_texture_object *texObj = att->Texture;
if (texObj) {
- ctx->Driver.FinishRenderTexture(ctx, texObj, att->CubeMapFace,
- att->TextureLevel);
+ ctx->Driver.FinishRenderTexture(ctx, att);
}
}
}
@@ -1178,12 +1180,15 @@ error_check_framebuffer_texture(GLcontext *ctx, GLuint dims,
}
+/**
+ * XXX The code in _mesa_FramebufferTexture1/2/3DEXT could be probably
+ * be combined into one function.
+ */
void GLAPIENTRY
_mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
GLenum textarget, GLuint texture, GLint level)
{
struct gl_renderbuffer_attachment *att;
- struct gl_texture_object *texObj;
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END(ctx);
@@ -1205,7 +1210,7 @@ _mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
FLUSH_VERTICES(ctx, _NEW_BUFFERS);
if (texture) {
- texObj = (struct gl_texture_object *)
+ struct gl_texture_object *texObj = (struct gl_texture_object *)
_mesa_HashLookup(ctx->Shared->TexObjects, texture);
if (!texObj) {
_mesa_error(ctx, GL_INVALID_VALUE,
@@ -1217,12 +1222,12 @@ _mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
"glFramebufferTexture1DEXT(texture target)");
return;
}
+ _mesa_set_texture_attachment(ctx, ctx->DrawBuffer, att,
+ texObj, textarget, level, 0);
}
else {
- /* remove texture attachment */
- texObj = NULL;
+ _mesa_remove_attachment(ctx, att);
}
- ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, level, 0);
}
@@ -1231,7 +1236,6 @@ _mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
GLenum textarget, GLuint texture, GLint level)
{
struct gl_renderbuffer_attachment *att;
- struct gl_texture_object *texObj;
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END(ctx);
@@ -1254,7 +1258,7 @@ _mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
FLUSH_VERTICES(ctx, _NEW_BUFFERS);
if (texture) {
- texObj = (struct gl_texture_object *)
+ struct gl_texture_object *texObj = (struct gl_texture_object *)
_mesa_HashLookup(ctx->Shared->TexObjects, texture);
if (!texObj) {
_mesa_error(ctx, GL_INVALID_VALUE,
@@ -1270,12 +1274,12 @@ _mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
"glFramebufferTexture2DEXT(texture target)");
return;
}
+ _mesa_set_texture_attachment(ctx, ctx->DrawBuffer, att,
+ texObj, textarget, level, 0);
}
else {
- /* remove texture attachment */
- texObj = NULL;
+ _mesa_remove_attachment(ctx, att);
}
- ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, level, 0);
}
@@ -1285,7 +1289,6 @@ _mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
GLint level, GLint zoffset)
{
struct gl_renderbuffer_attachment *att;
- struct gl_texture_object *texObj;
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END(ctx);
@@ -1307,7 +1310,7 @@ _mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
if (texture) {
const GLint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1);
- texObj = (struct gl_texture_object *)
+ struct gl_texture_object *texObj = (struct gl_texture_object *)
_mesa_HashLookup(ctx->Shared->TexObjects, texture);
if (!texObj) {
_mesa_error(ctx, GL_INVALID_VALUE,
@@ -1324,13 +1327,12 @@ _mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
"glFramebufferTexture3DEXT(zoffset)");
return;
}
+ _mesa_set_texture_attachment(ctx, ctx->DrawBuffer, att,
+ texObj, textarget, level,zoffset);
}
else {
- /* remove texture attachment */
- texObj = NULL;
+ _mesa_remove_attachment(ctx, att);
}
- ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget,
- level, zoffset);
}
diff --git a/src/mesa/main/fbobject.h b/src/mesa/main/fbobject.h
index 3171aa7b1b9..c16f628a092 100644
--- a/src/mesa/main/fbobject.h
+++ b/src/mesa/main/fbobject.h
@@ -38,6 +38,7 @@ _mesa_remove_attachment(GLcontext *ctx,
extern void
_mesa_set_texture_attachment(GLcontext *ctx,
+ struct gl_framebuffer *fb,
struct gl_renderbuffer_attachment *att,
struct gl_texture_object *texObj,
GLenum texTarget, GLuint level, GLuint zoffset);
diff --git a/src/mesa/main/teximage.c b/src/mesa/main/teximage.c
index edfe82adcf7..15a12ee4dcc 100644
--- a/src/mesa/main/teximage.c
+++ b/src/mesa/main/teximage.c
@@ -551,6 +551,18 @@ is_compressed_format(GLcontext *ctx, GLenum internalFormat)
}
+static GLuint
+texture_face(GLenum target)
+{
+ if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB &&
+ target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB)
+ return (GLuint) target - (GLuint) GL_TEXTURE_CUBE_MAP_POSITIVE_X;
+ else
+ return 0;
+}
+
+
+
/**
* Store a gl_texture_image pointer in a gl_texture_object structure
* according to the target and level parameters.
@@ -2135,6 +2147,36 @@ _mesa_GetTexImage( GLenum target, GLint level, GLenum format,
+/**
+ * Check if the given texture image is bound to any framebuffer objects
+ * and update/invalidate them.
+ * XXX We're only checking the currently bound framebuffer object for now.
+ * In the future, perhaps struct gl_texture_image should have a pointer (or
+ * list of pointers (yikes)) to the gl_framebuffer(s) which it's bound to.
+ */
+static void
+update_fbo_texture(GLcontext *ctx, struct gl_texture_object *texObj,
+ GLuint face, GLuint level)
+{
+ if (ctx->DrawBuffer->Name) {
+ GLuint i;
+ for (i = 0; i < BUFFER_COUNT; i++) {
+ struct gl_renderbuffer_attachment *att =
+ ctx->DrawBuffer->Attachment + i;
+ if (att->Type == GL_TEXTURE &&
+ att->Texture == texObj &&
+ att->TextureLevel == level &&
+ att->CubeMapFace == face) {
+ ASSERT(att->Texture->Image[att->CubeMapFace][att->TextureLevel]);
+ /* Tell driver about the new renderbuffer texture */
+ ctx->Driver.RenderbufferTexture(ctx, ctx->DrawBuffer, att);
+ }
+ }
+ }
+}
+
+
+
/*
* Called from the API. Note that width includes the border.
*/
@@ -2156,6 +2198,7 @@ _mesa_TexImage1D( GLenum target, GLint level, GLint internalFormat,
struct gl_texture_unit *texUnit;
struct gl_texture_object *texObj;
struct gl_texture_image *texImage;
+ const GLuint face = texture_face(target);
if (texture_error_check(ctx, target, level, internalFormat,
format, type, 1, postConvWidth, 1, 1, border)) {
@@ -2191,6 +2234,8 @@ _mesa_TexImage1D( GLenum target, GLint level, GLint internalFormat,
ASSERT(texImage->TexFormat);
+ update_fbo_texture(ctx, texObj, face, level);
+
/* state update */
texObj->Complete = GL_FALSE;
ctx->NewState |= _NEW_TEXTURE;
@@ -2247,6 +2292,7 @@ _mesa_TexImage2D( GLenum target, GLint level, GLint internalFormat,
struct gl_texture_unit *texUnit;
struct gl_texture_object *texObj;
struct gl_texture_image *texImage;
+ const GLuint face = texture_face(target);
if (texture_error_check(ctx, target, level, internalFormat,
format, type, 2, postConvWidth, postConvHeight,
@@ -2280,14 +2326,10 @@ _mesa_TexImage2D( GLenum target, GLint level, GLint internalFormat,
width, height, border, format, type, pixels,
&ctx->Unpack, texObj, texImage);
- /*
- * XXX if this texture image is currently bound to a user-created
- * framebuffer object, we have to invalidate that framebuffer's
- * completeness state.
- */
-
ASSERT(texImage->TexFormat);
+ update_fbo_texture(ctx, texObj, face, level);
+
/* state update */
texObj->Complete = GL_FALSE;
ctx->NewState |= _NEW_TEXTURE;
@@ -2337,10 +2379,11 @@ _mesa_TexImage3D( GLenum target, GLint level, GLint internalFormat,
ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
if (target == GL_TEXTURE_3D) {
+ /* non-proxy target */
struct gl_texture_unit *texUnit;
struct gl_texture_object *texObj;
struct gl_texture_image *texImage;
- /* non-proxy target */
+ const GLuint face = texture_face(target);
if (texture_error_check(ctx, target, level, (GLint) internalFormat,
format, type, 3, width, height, depth, border)) {
@@ -2375,6 +2418,8 @@ _mesa_TexImage3D( GLenum target, GLint level, GLint internalFormat,
ASSERT(texImage->TexFormat);
+ update_fbo_texture(ctx, texObj, face, level);
+
/* state update */
texObj->Complete = GL_FALSE;
ctx->NewState |= _NEW_TEXTURE;
@@ -2565,6 +2610,7 @@ _mesa_CopyTexImage1D( GLenum target, GLint level,
struct gl_texture_object *texObj;
struct gl_texture_image *texImage;
GLsizei postConvWidth = width;
+ const GLuint face = texture_face(target);
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
@@ -2602,6 +2648,8 @@ _mesa_CopyTexImage1D( GLenum target, GLint level,
ASSERT(texImage->TexFormat);
+ update_fbo_texture(ctx, texObj, face, level);
+
/* state update */
texObj->Complete = GL_FALSE;
ctx->NewState |= _NEW_TEXTURE;
@@ -2618,6 +2666,7 @@ _mesa_CopyTexImage2D( GLenum target, GLint level, GLenum internalFormat,
struct gl_texture_object *texObj;
struct gl_texture_image *texImage;
GLsizei postConvWidth = width, postConvHeight = height;
+ const GLuint face = texture_face(target);
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
@@ -2656,6 +2705,8 @@ _mesa_CopyTexImage2D( GLenum target, GLint level, GLenum internalFormat,
ASSERT(texImage->TexFormat);
+ update_fbo_texture(ctx, texObj, face, level);
+
/* state update */
texObj->Complete = GL_FALSE;
ctx->NewState |= _NEW_TEXTURE;
diff --git a/src/mesa/main/texobj.c b/src/mesa/main/texobj.c
index 0f320fea6a5..59a1d9375ff 100644
--- a/src/mesa/main/texobj.c
+++ b/src/mesa/main/texobj.c
@@ -5,9 +5,9 @@
/*
* Mesa 3-D graphics library
- * Version: 6.3
+ * Version: 6.5
*
- * Copyright (C) 1999-2005 Brian Paul All Rights Reserved.
+ * Copyright (C) 1999-2006 Brian Paul 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"),
@@ -32,6 +32,7 @@
#include "colortab.h"
#include "context.h"
#include "enums.h"
+#include "fbobject.h"
#include "hash.h"
#include "imports.h"
#include "macros.h"
@@ -40,6 +41,7 @@
#include "texobj.h"
#include "mtypes.h"
+
#ifdef __VMS
#define _mesa_sprintf sprintf
#endif
@@ -579,6 +581,82 @@ _mesa_GenTextures( GLsizei n, GLuint *textures )
/**
+ * Check if the given texture object is bound to the current draw or
+ * read framebuffer. If so, Unbind it.
+ */
+static void
+unbind_texobj_from_fbo(GLcontext *ctx, struct gl_texture_object *texObj)
+{
+ const GLuint n = (ctx->DrawBuffer == ctx->ReadBuffer) ? 1 : 2;
+ GLuint i;
+
+ for (i = 0; i < n; i++) {
+ struct gl_framebuffer *fb = (i == 0) ? ctx->DrawBuffer : ctx->ReadBuffer;
+ if (fb->Name) {
+ GLuint j;
+ for (j = 0; j < BUFFER_COUNT; j++) {
+ if (fb->Attachment[j].Type == GL_TEXTURE &&
+ fb->Attachment[j].Texture == texObj) {
+ _mesa_remove_attachment(ctx, fb->Attachment + j);
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Check if the given texture object is bound to any texture image units and
+ * unbind it if so.
+ * XXX all RefCount accesses should be protected by a mutex.
+ */
+static void
+unbind_texobj_from_texunits(GLcontext *ctx, struct gl_texture_object *texObj)
+{
+ GLuint u;
+
+ for (u = 0; u < MAX_TEXTURE_IMAGE_UNITS; u++) {
+ struct gl_texture_unit *unit = &ctx->Texture.Unit[u];
+ if (texObj == unit->Current1D) {
+ unit->Current1D = ctx->Shared->Default1D;
+ ctx->Shared->Default1D->RefCount++;
+ texObj->RefCount--;
+ if (texObj == unit->_Current)
+ unit->_Current = unit->Current1D;
+ }
+ else if (texObj == unit->Current2D) {
+ unit->Current2D = ctx->Shared->Default2D;
+ ctx->Shared->Default2D->RefCount++;
+ texObj->RefCount--;
+ if (texObj == unit->_Current)
+ unit->_Current = unit->Current2D;
+ }
+ else if (texObj == unit->Current3D) {
+ unit->Current3D = ctx->Shared->Default3D;
+ ctx->Shared->Default3D->RefCount++;
+ texObj->RefCount--;
+ if (texObj == unit->_Current)
+ unit->_Current = unit->Current3D;
+ }
+ else if (texObj == unit->CurrentCubeMap) {
+ unit->CurrentCubeMap = ctx->Shared->DefaultCubeMap;
+ ctx->Shared->DefaultCubeMap->RefCount++;
+ texObj->RefCount--;
+ if (texObj == unit->_Current)
+ unit->_Current = unit->CurrentCubeMap;
+ }
+ else if (texObj == unit->CurrentRect) {
+ unit->CurrentRect = ctx->Shared->DefaultRect;
+ ctx->Shared->DefaultRect->RefCount++;
+ texObj->RefCount--;
+ if (texObj == unit->_Current)
+ unit->_Current = unit->CurrentRect;
+ }
+ }
+}
+
+
+/**
* Delete named textures.
*
* \param n number of textures to be deleted.
@@ -607,49 +685,18 @@ _mesa_DeleteTextures( GLsizei n, const GLuint *textures)
struct gl_texture_object *delObj = (struct gl_texture_object *)
_mesa_HashLookup(ctx->Shared->TexObjects, textures[i]);
if (delObj) {
- /* First check if this texture is currently bound.
+
+ /* Check if texture is bound to any framebuffer objects.
+ * If so, unbind.
+ * See section 4.4.2.3 of GL_EXT_framebuffer_object.
+ */
+ unbind_texobj_from_fbo(ctx, delObj);
+
+ /* Check if this texture is currently bound to any texture units.
* If so, unbind it and decrement the reference count.
- * XXX all RefCount accesses should be protected by a mutex.
*/
- GLuint u;
- for (u = 0; u < MAX_TEXTURE_IMAGE_UNITS; u++) {
- struct gl_texture_unit *unit = &ctx->Texture.Unit[u];
- if (delObj == unit->Current1D) {
- unit->Current1D = ctx->Shared->Default1D;
- ctx->Shared->Default1D->RefCount++;
- delObj->RefCount--;
- if (delObj == unit->_Current)
- unit->_Current = unit->Current1D;
- }
- else if (delObj == unit->Current2D) {
- unit->Current2D = ctx->Shared->Default2D;
- ctx->Shared->Default2D->RefCount++;
- delObj->RefCount--;
- if (delObj == unit->_Current)
- unit->_Current = unit->Current2D;
- }
- else if (delObj == unit->Current3D) {
- unit->Current3D = ctx->Shared->Default3D;
- ctx->Shared->Default3D->RefCount++;
- delObj->RefCount--;
- if (delObj == unit->_Current)
- unit->_Current = unit->Current3D;
- }
- else if (delObj == unit->CurrentCubeMap) {
- unit->CurrentCubeMap = ctx->Shared->DefaultCubeMap;
- ctx->Shared->DefaultCubeMap->RefCount++;
- delObj->RefCount--;
- if (delObj == unit->_Current)
- unit->_Current = unit->CurrentCubeMap;
- }
- else if (delObj == unit->CurrentRect) {
- unit->CurrentRect = ctx->Shared->DefaultRect;
- ctx->Shared->DefaultRect->RefCount++;
- delObj->RefCount--;
- if (delObj == unit->_Current)
- unit->_Current = unit->CurrentRect;
- }
- }
+ unbind_texobj_from_texunits(ctx, delObj);
+
ctx->NewState |= _NEW_TEXTURE;
/* The texture _name_ is now free for re-use.
diff --git a/src/mesa/main/texrender.c b/src/mesa/main/texrender.c
index a4efe640382..dca93a1c04a 100644
--- a/src/mesa/main/texrender.c
+++ b/src/mesa/main/texrender.c
@@ -200,25 +200,44 @@ wrap_texture(GLcontext *ctx, struct gl_renderbuffer_attachment *att)
/**
- * Software fallback for ctx->Driver.RenderbufferTexture.
- * This is called via the glRenderbufferTexture1D/2D/3D() functions.
- * If we're unbinding a texture, texObj will be NULL.
- * The framebuffer of interest is ctx->DrawBuffer.
+ * Called when rendering to a texture image begins.
+ * This is a fallback routine for software render-to-texture.
+ *
+ * Called via the glRenderbufferTexture1D/2D/3D() functions
+ * and elsewhere (such as glTexImage2D).
+ *
+ * The image we're rendering into is
+ * att->Texture->Image[att->CubeMapFace][att->TextureLevel];
+ * It'll never be NULL.
+ *
+ * \param fb the framebuffer object the texture is being bound to
+ * \param att the fb attachment point of the texture
+ *
* \sa _mesa_framebuffer_renderbuffer
*/
void
_mesa_renderbuffer_texture(GLcontext *ctx,
- struct gl_renderbuffer_attachment *att,
- struct gl_texture_object *texObj,
- GLenum texTarget, GLuint level, GLuint zoffset)
+ struct gl_framebuffer *fb,
+ struct gl_renderbuffer_attachment *att)
{
- if (texObj) {
- _mesa_set_texture_attachment(ctx, att, texObj,
- texTarget, level, zoffset);
- if (!att->Renderbuffer)
- wrap_texture(ctx, att);
- }
- else {
- _mesa_remove_attachment(ctx, att);
+ struct gl_texture_image *newImage
+ = att->Texture->Image[att->CubeMapFace][att->TextureLevel];
+ struct texture_renderbuffer *trb
+ = (struct texture_renderbuffer *) att->Renderbuffer;
+ struct gl_texture_image *oldImage = trb ? trb->TexImage : NULL;
+
+ (void) fb;
+
+ ASSERT(newImage);
+
+ if (oldImage != newImage) {
+ if (trb) {
+ /* get rid of old wrapper */
+ /* XXX also if Zoffset changes? */
+ trb->Base.Delete(&trb->Base);
+ }
+ wrap_texture(ctx, att);
}
}
+
+
diff --git a/src/mesa/main/texrender.h b/src/mesa/main/texrender.h
index 6d8bc964141..1e11e505130 100644
--- a/src/mesa/main/texrender.h
+++ b/src/mesa/main/texrender.h
@@ -4,9 +4,8 @@
extern void
_mesa_renderbuffer_texture(GLcontext *ctx,
- struct gl_renderbuffer_attachment *att,
- struct gl_texture_object *texObj,
- GLenum texTarget, GLuint level, GLuint zoffset);
+ struct gl_framebuffer *fb,
+ struct gl_renderbuffer_attachment *att);
#endif /* TEXRENDER_H */