#include "main/context.h" #include "main/colormac.h" #include "main/fbobject.h" #include "main/macros.h" #include "main/teximage.h" #include "main/renderbuffer.h" #include "swrast/swrast.h" #include "swrast/s_context.h" #include "swrast/s_texfetch.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 swrast_texture_image *TexImage; StoreTexelFunc Store; FetchTexelFunc Fetch; GLint Yoffset; /**< Layer for 1D array textures. */ GLint Zoffset; /**< Layer for 2D array textures, or slice * for 3D textures */ }; /** cast wrapper */ static inline struct texture_renderbuffer * texture_renderbuffer(struct gl_renderbuffer *rb) { return (struct texture_renderbuffer *) rb; } static void store_nop(struct swrast_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); 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(struct gl_context *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! */ /* 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(struct gl_context *ctx, struct gl_renderbuffer_attachment *att) { struct texture_renderbuffer *trb = (struct texture_renderbuffer *) att->Renderbuffer; (void) ctx; ASSERT(trb); trb->TexImage = swrast_texture_image(_mesa_get_attachment_teximage(att)); ASSERT(trb->TexImage); trb->Store = _mesa_get_texel_store_func(trb->TexImage->Base.TexFormat); if (!trb->Store) { /* we'll never draw into some textures (compressed formats) */ trb->Store = store_nop; } if (!trb->TexImage->FetchTexel) { _mesa_update_fetch_functions(trb->TexImage->Base.TexObject); } trb->Fetch = trb->TexImage->FetchTexel; assert(trb->Fetch); if (att->Texture->Target == GL_TEXTURE_1D_ARRAY_EXT) { trb->Yoffset = att->Zoffset; trb->Zoffset = 0; } else { trb->Yoffset = 0; trb->Zoffset = att->Zoffset; } trb->Base.Width = trb->TexImage->Base.Width; trb->Base.Height = trb->TexImage->Base.Height; trb->Base.InternalFormat = trb->TexImage->Base.InternalFormat; trb->Base.Format = trb->TexImage->Base.TexFormat; /* Set the gl_renderbuffer::Data field so that mapping the buffer * in renderbuffer.c succeeds. */ if (att->Texture->Target == GL_TEXTURE_3D || att->Texture->Target == GL_TEXTURE_2D_ARRAY_EXT) { trb->Base.Data = trb->TexImage->Buffer + trb->TexImage->ImageOffsets[trb->Zoffset] * _mesa_get_format_bytes(trb->TexImage->Base.TexFormat); } else { trb->Base.Data = trb->TexImage->Buffer; } /* XXX may need more special cases here */ switch (trb->TexImage->Base.TexFormat) { case MESA_FORMAT_Z24_S8: trb->Base.DataType = GL_UNSIGNED_INT_24_8_EXT; trb->Base._BaseFormat = GL_DEPTH_STENCIL; break; case MESA_FORMAT_S8_Z24: trb->Base.DataType = GL_UNSIGNED_INT_8_24_REV_MESA; trb->Base._BaseFormat = GL_DEPTH_STENCIL; break; case MESA_FORMAT_Z24_X8: trb->Base.DataType = GL_UNSIGNED_INT_24_8_EXT; trb->Base._BaseFormat = GL_DEPTH_COMPONENT; break; case MESA_FORMAT_X8_Z24: trb->Base.DataType = GL_UNSIGNED_INT_8_24_REV_MESA; trb->Base._BaseFormat = GL_DEPTH_COMPONENT; break; case MESA_FORMAT_Z16: trb->Base.DataType = GL_UNSIGNED_SHORT; trb->Base._BaseFormat = GL_DEPTH_COMPONENT; break; case MESA_FORMAT_Z32: trb->Base.DataType = GL_UNSIGNED_INT; trb->Base._BaseFormat = GL_DEPTH_COMPONENT; break; /* SRGB formats pre EXT_framebuffer_sRGB don't do sRGB translations on FBO readback */ case MESA_FORMAT_SRGB8: trb->Fetch = _mesa_get_texel_fetch_func(MESA_FORMAT_RGB888, _mesa_get_texture_dimensions(att->Texture->Target)); trb->Base.DataType = CHAN_TYPE; trb->Base._BaseFormat = GL_RGBA; break; case MESA_FORMAT_SRGBA8: trb->Fetch = _mesa_get_texel_fetch_func(MESA_FORMAT_RGBA8888, _mesa_get_texture_dimensions(att->Texture->Target)); trb->Base.DataType = CHAN_TYPE; trb->Base._BaseFormat = GL_RGBA; break; case MESA_FORMAT_SARGB8: trb->Fetch = _mesa_get_texel_fetch_func(MESA_FORMAT_ARGB8888, _mesa_get_texture_dimensions(att->Texture->Target)); trb->Base.DataType = CHAN_TYPE; trb->Base._BaseFormat = GL_RGBA; break; default: trb->Base.DataType = CHAN_TYPE; trb->Base._BaseFormat = GL_RGBA; } } /** * 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 _swrast_render_texture(struct gl_context *ctx, struct gl_framebuffer *fb, struct gl_renderbuffer_attachment *att) { (void) fb; if (!att->Renderbuffer) { wrap_texture(ctx, att); } update_wrapper(ctx, att); } void _swrast_finish_render_texture(struct gl_context *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; }