#include "context.h" #include "colormac.h" #include "fbobject.h" #include "texfetch.h" #include "texrender.h" #include "renderbuffer.h" /* * Render-to-texture code for GL_EXT_framebuffer_object */ /** * Derived from gl_renderbuffer class */ struct texture_renderbuffer { struct gl_renderbuffer Base; /**< Base class object */ struct gl_texture_image *TexImage; StoreTexelFunc Store; GLint Yoffset; /**< Layer for 1D array textures. */ GLint Zoffset; /**< Layer for 2D array textures, or slice * for 3D textures */ }; /** * Get row of values from the renderbuffer that wraps a texture image. */ static void texture_get_row(GLcontext *ctx, struct gl_renderbuffer *rb, GLuint count, GLint x, GLint y, void *values) { const struct texture_renderbuffer *trb = (const struct texture_renderbuffer *) rb; const GLint z = trb->Zoffset; GLuint i; ASSERT(trb->TexImage->Width == rb->Width); ASSERT(trb->TexImage->Height == rb->Height); y += trb->Yoffset; if (rb->DataType == CHAN_TYPE) { GLchan *rgbaOut = (GLchan *) values; for (i = 0; i < count; i++) { GLfloat rgba[4]; trb->TexImage->FetchTexelf(trb->TexImage, x + i, y, z, rgba); UNCLAMPED_FLOAT_TO_RGBA_CHAN(rgbaOut + 4 * i, rgba); } } else if (rb->DataType == GL_UNSIGNED_SHORT) { GLushort *zValues = (GLushort *) values; for (i = 0; i < count; i++) { GLfloat flt; trb->TexImage->FetchTexelf(trb->TexImage, x + i, y, z, &flt); zValues[i] = (GLushort) (flt * 0xffff); } } else if (rb->DataType == GL_UNSIGNED_INT) { GLuint *zValues = (GLuint *) values; /* const GLdouble scale = (GLdouble) 0xffffffff; */ for (i = 0; i < count; i++) { GLfloat flt; trb->TexImage->FetchTexelf(trb->TexImage, x + i, y, z, &flt); #if 0 /* this should work, but doesn't (overflow due to low precision) */ zValues[i] = (GLuint) (flt * scale); #else /* temporary hack */ zValues[i] = ((GLuint) (flt * 0xffffff)) << 8; #endif } } else if (rb->DataType == GL_UNSIGNED_INT_24_8_EXT) { GLuint *zValues = (GLuint *) values; for (i = 0; i < count; i++) { GLfloat flt; trb->TexImage->FetchTexelf(trb->TexImage, x + i, y, z, &flt); zValues[i] = ((GLuint) (flt * 0xffffff)) << 8; } } else { _mesa_problem(ctx, "invalid rb->DataType in texture_get_row"); } } static void texture_get_values(GLcontext *ctx, struct gl_renderbuffer *rb, GLuint count, const GLint x[], const GLint y[], void *values) { const struct texture_renderbuffer *trb = (const struct texture_renderbuffer *) rb; const GLint z = trb->Zoffset; GLuint i; if (rb->DataType == CHAN_TYPE) { GLchan *rgbaOut = (GLchan *) values; for (i = 0; i < count; i++) { GLfloat rgba[4]; trb->TexImage->FetchTexelf(trb->TexImage, x[i], y[i] + trb->Yoffset, z, rgba); UNCLAMPED_FLOAT_TO_RGBA_CHAN(rgbaOut + 4 * i, rgba); } } else if (rb->DataType == GL_UNSIGNED_SHORT) { GLushort *zValues = (GLushort *) values; for (i = 0; i < count; i++) { GLfloat flt; trb->TexImage->FetchTexelf(trb->TexImage, x[i], y[i] + trb->Yoffset, z, &flt); zValues[i] = (GLushort) (flt * 0xffff); } } else if (rb->DataType == GL_UNSIGNED_INT) { GLuint *zValues = (GLuint *) values; for (i = 0; i < count; i++) { GLfloat flt; trb->TexImage->FetchTexelf(trb->TexImage, x[i], y[i] + trb->Yoffset, z, &flt); #if 0 zValues[i] = (GLuint) (flt * 0xffffffff); #else zValues[i] = ((GLuint) (flt * 0xffffff)) << 8; #endif } } else if (rb->DataType == GL_UNSIGNED_INT_24_8_EXT) { GLuint *zValues = (GLuint *) values; for (i = 0; i < count; i++) { GLfloat flt; trb->TexImage->FetchTexelf(trb->TexImage, x[i], y[i] + trb->Yoffset, z, &flt); zValues[i] = ((GLuint) (flt * 0xffffff)) << 8; } } else { _mesa_problem(ctx, "invalid rb->DataType in texture_get_values"); } } /** * Put row of values into a renderbuffer that wraps a texture image. */ static void texture_put_row(GLcontext *ctx, struct gl_renderbuffer *rb, GLuint count, GLint x, GLint y, const void *values, const GLubyte *mask) { const struct texture_renderbuffer *trb = (const struct texture_renderbuffer *) rb; const GLint z = trb->Zoffset; GLuint i; y += trb->Yoffset; if (rb->DataType == CHAN_TYPE) { const GLchan *rgba = (const GLchan *) values; for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x + i, y, z, rgba); } rgba += 4; } } else if (rb->DataType == GL_UNSIGNED_SHORT) { const GLushort *zValues = (const GLushort *) values; for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x + i, y, z, zValues + i); } } } else if (rb->DataType == GL_UNSIGNED_INT) { const GLuint *zValues = (const GLuint *) values; for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x + i, y, z, zValues + i); } } } else if (rb->DataType == GL_UNSIGNED_INT_24_8_EXT) { const GLuint *zValues = (const GLuint *) values; for (i = 0; i < count; i++) { if (!mask || mask[i]) { GLfloat flt = (GLfloat) ((zValues[i] >> 8) * (1.0 / 0xffffff)); trb->Store(trb->TexImage, x + i, y, z, &flt); } } } else { _mesa_problem(ctx, "invalid rb->DataType in texture_put_row"); } } /** * Put row of RGB values into a renderbuffer that wraps a texture image. */ static void texture_put_row_rgb(GLcontext *ctx, struct gl_renderbuffer *rb, GLuint count, GLint x, GLint y, const void *values, const GLubyte *mask) { const struct texture_renderbuffer *trb = (const struct texture_renderbuffer *) rb; const GLint z = trb->Zoffset; GLuint i; y += trb->Yoffset; if (rb->DataType == CHAN_TYPE) { const GLchan *rgb = (const GLchan *) values; for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x + i, y, z, rgb); } rgb += 3; } } else if (rb->DataType == GL_UNSIGNED_SHORT) { const GLushort *zValues = (const GLushort *) values; for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x + i, y, z, zValues + i); } } } else if (rb->DataType == GL_UNSIGNED_INT) { const GLuint *zValues = (const GLuint *) values; for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x + i, y, z, zValues + i); } } } else if (rb->DataType == GL_UNSIGNED_INT_24_8_EXT) { const GLuint *zValues = (const GLuint *) values; for (i = 0; i < count; i++) { if (!mask || mask[i]) { GLfloat flt = (GLfloat) ((zValues[i] >> 8) * (1.0 / 0xffffff)); trb->Store(trb->TexImage, x + i, y, z, &flt); } } } else { _mesa_problem(ctx, "invalid rb->DataType in texture_put_row"); } } static void texture_put_mono_row(GLcontext *ctx, struct gl_renderbuffer *rb, GLuint count, GLint x, GLint y, const void *value, const GLubyte *mask) { const struct texture_renderbuffer *trb = (const struct texture_renderbuffer *) rb; const GLint z = trb->Zoffset; GLuint i; y += trb->Yoffset; if (rb->DataType == CHAN_TYPE) { const GLchan *rgba = (const GLchan *) value; for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x + i, y, z, rgba); } } } else if (rb->DataType == GL_UNSIGNED_SHORT) { const GLushort zValue = *((const GLushort *) value); for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x + i, y, z, &zValue); } } } else if (rb->DataType == GL_UNSIGNED_INT) { const GLuint zValue = *((const GLuint *) value); for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x + i, y, z, &zValue); } } } else if (rb->DataType == GL_UNSIGNED_INT_24_8_EXT) { const GLuint zValue = *((const GLuint *) value); const GLfloat flt = (GLfloat) ((zValue >> 8) * (1.0 / 0xffffff)); for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x + i, y, z, &flt); } } } else { _mesa_problem(ctx, "invalid rb->DataType in texture_put_mono_row"); } } static void texture_put_values(GLcontext *ctx, struct gl_renderbuffer *rb, GLuint count, const GLint x[], const GLint y[], const void *values, const GLubyte *mask) { const struct texture_renderbuffer *trb = (const struct texture_renderbuffer *) rb; const GLint z = trb->Zoffset; GLuint i; if (rb->DataType == CHAN_TYPE) { const GLchan *rgba = (const GLchan *) values; for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x[i], y[i] + trb->Yoffset, z, rgba); } rgba += 4; } } else if (rb->DataType == GL_UNSIGNED_SHORT) { const GLushort *zValues = (const GLushort *) values; for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x[i], y[i] + trb->Yoffset, z, zValues + i); } } } else if (rb->DataType == GL_UNSIGNED_INT) { const GLuint *zValues = (const GLuint *) values; for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x[i], y[i] + trb->Yoffset, z, zValues + i); } } } else if (rb->DataType == GL_UNSIGNED_INT_24_8_EXT) { const GLuint *zValues = (const GLuint *) values; for (i = 0; i < count; i++) { if (!mask || mask[i]) { GLfloat flt = (GLfloat) ((zValues[i] >> 8) * (1.0 / 0xffffff)); trb->Store(trb->TexImage, x[i], y[i] + trb->Yoffset, z, &flt); } } } else { _mesa_problem(ctx, "invalid rb->DataType in texture_put_values"); } } static void texture_put_mono_values(GLcontext *ctx, struct gl_renderbuffer *rb, GLuint count, const GLint x[], const GLint y[], const void *value, const GLubyte *mask) { const struct texture_renderbuffer *trb = (const struct texture_renderbuffer *) rb; const GLint z = trb->Zoffset; GLuint i; if (rb->DataType == CHAN_TYPE) { const GLchan *rgba = (const GLchan *) value; for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x[i], y[i] + trb->Yoffset, z, rgba); } } } else if (rb->DataType == GL_UNSIGNED_INT) { const GLuint zValue = *((const GLuint *) value); for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x[i], y[i] + trb->Yoffset, z, &zValue); } } } else if (rb->DataType == GL_UNSIGNED_SHORT) { const GLushort zValue = *((const GLushort *) value); for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x[i], y[i] + trb->Yoffset, z, &zValue); } } } else if (rb->DataType == GL_UNSIGNED_INT_24_8_EXT) { const GLuint zValue = *((const GLuint *) value); const GLfloat flt = (GLfloat) ((zValue >> 8) * (1.0 / 0xffffff)); for (i = 0; i < count; i++) { if (!mask || mask[i]) { trb->Store(trb->TexImage, x[i], y[i] + trb->Yoffset, z, &flt); } } } else { _mesa_problem(ctx, "invalid rb->DataType in texture_put_mono_values"); } } static void store_nop(struct gl_texture_image *texImage, GLint col, GLint row, GLint img, const void *texel) { } static void delete_texture_wrapper(struct gl_renderbuffer *rb) { ASSERT(rb->RefCount == 0); _mesa_free(rb); } /** * This function creates a renderbuffer object which wraps a texture image. * The new renderbuffer is plugged into the given attachment point. * This allows rendering into the texture as if it were a renderbuffer. */ static void wrap_texture(GLcontext *ctx, struct gl_renderbuffer_attachment *att) { struct texture_renderbuffer *trb; const GLuint name = 0; ASSERT(att->Type == GL_TEXTURE); ASSERT(att->Renderbuffer == NULL); trb = CALLOC_STRUCT(texture_renderbuffer); if (!trb) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "wrap_texture"); return; } /* init base gl_renderbuffer fields */ _mesa_init_renderbuffer(&trb->Base, name); /* plug in our texture_renderbuffer-specific functions */ trb->Base.Delete = delete_texture_wrapper; trb->Base.AllocStorage = NULL; /* illegal! */ trb->Base.GetRow = texture_get_row; trb->Base.GetValues = texture_get_values; trb->Base.PutRow = texture_put_row; trb->Base.PutRowRGB = texture_put_row_rgb; trb->Base.PutMonoRow = texture_put_mono_row; trb->Base.PutValues = texture_put_values; trb->Base.PutMonoValues = texture_put_mono_values; /* update attachment point */ _mesa_reference_renderbuffer(&att->Renderbuffer, &(trb->Base)); } /** * Update the renderbuffer wrapper for rendering to a texture. * For example, update the width, height of the RB based on the texture size, * update the internal format info, etc. */ static void update_wrapper(GLcontext *ctx, const struct gl_renderbuffer_attachment *att) { struct texture_renderbuffer *trb = (struct texture_renderbuffer *) att->Renderbuffer; gl_format texFormat; (void) ctx; ASSERT(trb); trb->TexImage = att->Texture->Image[att->CubeMapFace][att->TextureLevel]; ASSERT(trb->TexImage); trb->Store = _mesa_get_texel_store_func(trb->TexImage->TexFormat); if (!trb->Store) { /* we'll never draw into some textures (compressed formats) */ trb->Store = store_nop; } if (att->Texture->Target == GL_TEXTURE_1D_ARRAY_EXT) { trb->Yoffset = att->Zoffset; trb->Zoffset = 0; } else { trb->Yoffset = 0; trb->Zoffset = att->Zoffset; } texFormat = trb->TexImage->TexFormat; trb->Base.Width = trb->TexImage->Width; trb->Base.Height = trb->TexImage->Height; trb->Base.InternalFormat = trb->TexImage->InternalFormat; /* XXX may need more special cases here */ if (trb->TexImage->TexFormat == MESA_FORMAT_Z24_S8) { trb->Base.Format = MESA_FORMAT_Z24_S8; trb->Base.DataType = GL_UNSIGNED_INT_24_8_EXT; } else if (trb->TexImage->TexFormat == MESA_FORMAT_Z16) { trb->Base.Format = MESA_FORMAT_Z16; trb->Base.DataType = GL_UNSIGNED_SHORT; } else if (trb->TexImage->TexFormat == MESA_FORMAT_Z32) { trb->Base.Format = MESA_FORMAT_Z32; trb->Base.DataType = GL_UNSIGNED_INT; } else { trb->Base.Format = trb->TexImage->TexFormat; trb->Base.DataType = CHAN_TYPE; } trb->Base.Data = trb->TexImage->Data; trb->Base._BaseFormat = _mesa_base_fbo_format(ctx, trb->Base.InternalFormat); } /** * Called when rendering to a texture image begins, or when changing * the dest mipmap level, cube face, etc. * 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_render_texture(GLcontext *ctx, struct gl_framebuffer *fb, struct gl_renderbuffer_attachment *att) { (void) fb; if (!att->Renderbuffer) { wrap_texture(ctx, att); } update_wrapper(ctx, att); } void _mesa_finish_render_texture(GLcontext *ctx, struct gl_renderbuffer_attachment *att) { /* do nothing */ /* The renderbuffer texture wrapper will get deleted by the * normal mechanism for deleting renderbuffers. */ (void) ctx; (void) att; }