/* * Mesa 3-D graphics library * * Copyright (C) 2009 VMware, Inc. 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, sublicense, * 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 above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * 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 NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS 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. */ #include "main/glheader.h" #include "main/mtypes.h" #include "main/arbprogram.h" #include "main/arrayobj.h" #include "main/blend.h" #include "main/depth.h" #include "main/enable.h" #include "main/enums.h" #include "main/fbobject.h" #include "main/image.h" #include "main/macros.h" #include "main/matrix.h" #include "main/readpix.h" #include "main/scissor.h" #include "main/shaderapi.h" #include "main/texobj.h" #include "main/texenv.h" #include "main/teximage.h" #include "main/texparam.h" #include "main/uniforms.h" #include "main/varray.h" #include "main/viewport.h" #include "swrast/swrast.h" #include "drivers/common/meta.h" static struct gl_texture_object * texture_object_from_renderbuffer(struct gl_context *, struct gl_renderbuffer *); static struct gl_sampler_object * setup_sampler(struct gl_context *, struct gl_texture_object *, GLenum target, GLenum filter, GLuint srcLevel); /** Return offset in bytes of the field within a vertex struct */ #define OFFSET(FIELD) ((void *) offsetof(struct vertex, FIELD)) static void setup_glsl_blit_framebuffer(struct gl_context *ctx, struct blit_state *blit, const struct gl_framebuffer *drawFb, struct gl_renderbuffer *src_rb, GLenum target, bool do_depth) { const unsigned texcoord_size = 2 + (src_rb->Depth > 1 ? 1 : 0); /* target = GL_TEXTURE_RECTANGLE is not supported in GLES 3.0 */ assert(_mesa_is_desktop_gl(ctx) || target == GL_TEXTURE_2D); _mesa_meta_setup_vertex_objects(ctx, &blit->VAO, &blit->buf_obj, true, 2, texcoord_size, 0); _mesa_meta_setup_blit_shader(ctx, target, do_depth, do_depth ? &blit->shaders_with_depth : &blit->shaders_without_depth); } /** * Try to do a color or depth glBlitFramebuffer using texturing. * * We can do this when the src renderbuffer is actually a texture, or when the * driver exposes BindRenderbufferTexImage(). */ static bool blitframebuffer_texture(struct gl_context *ctx, const struct gl_framebuffer *readFb, const struct gl_framebuffer *drawFb, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLenum filter, GLint flipX, GLint flipY, GLboolean glsl_version, GLboolean do_depth) { int att_index = do_depth ? BUFFER_DEPTH : readFb->_ColorReadBufferIndex; const struct gl_renderbuffer_attachment *readAtt = &readFb->Attachment[att_index]; struct blit_state *blit = &ctx->Meta->Blit; struct fb_tex_blit_state fb_tex_blit; const GLint dstX = MIN2(dstX0, dstX1); const GLint dstY = MIN2(dstY0, dstY1); const GLint dstW = abs(dstX1 - dstX0); const GLint dstH = abs(dstY1 - dstY0); const int srcW = abs(srcX1 - srcX0); const int srcH = abs(srcY1 - srcY0); struct gl_texture_object *texObj; GLuint srcLevel; GLenum target; struct gl_renderbuffer *rb = readAtt->Renderbuffer; struct temp_texture *meta_temp_texture; assert(rb->NumSamples == 0); _mesa_meta_fb_tex_blit_begin(ctx, &fb_tex_blit); if (readAtt->Texture && (readAtt->Texture->Target == GL_TEXTURE_2D || readAtt->Texture->Target == GL_TEXTURE_RECTANGLE)) { /* If there's a texture attached of a type we can handle, then just use * it directly. */ srcLevel = readAtt->TextureLevel; texObj = readAtt->Texture; } else if (!readAtt->Texture && ctx->Driver.BindRenderbufferTexImage) { texObj = texture_object_from_renderbuffer(ctx, rb); if (texObj == NULL) return false; fb_tex_blit.temp_tex_obj = texObj; srcLevel = 0; if (_mesa_is_winsys_fbo(readFb)) { GLint temp = srcY0; srcY0 = rb->Height - srcY1; srcY1 = rb->Height - temp; flipY = -flipY; } } else { GLenum tex_base_format; /* Fall back to doing a CopyTexSubImage to get the destination * renderbuffer into a texture. */ if (ctx->Meta->Blit.no_ctsi_fallback) return false; if (do_depth) { meta_temp_texture = _mesa_meta_get_temp_depth_texture(ctx); tex_base_format = GL_DEPTH_COMPONENT; } else { meta_temp_texture = _mesa_meta_get_temp_texture(ctx); tex_base_format = _mesa_base_tex_format(ctx, rb->InternalFormat); } srcLevel = 0; texObj = meta_temp_texture->tex_obj; if (texObj == NULL) { return false; } _mesa_meta_setup_copypix_texture(ctx, meta_temp_texture, srcX0, srcY0, srcW, srcH, tex_base_format, filter); assert(texObj->Target == meta_temp_texture->Target); srcX0 = 0; srcY0 = 0; srcX1 = srcW; srcY1 = srcH; } target = texObj->Target; fb_tex_blit.tex_obj = texObj; fb_tex_blit.baseLevelSave = texObj->Attrib.BaseLevel; fb_tex_blit.maxLevelSave = texObj->Attrib.MaxLevel; fb_tex_blit.stencilSamplingSave = texObj->StencilSampling; if (glsl_version) { setup_glsl_blit_framebuffer(ctx, blit, drawFb, rb, target, do_depth); } else { _mesa_meta_setup_ff_tnl_for_blit(ctx, &ctx->Meta->Blit.VAO, &ctx->Meta->Blit.buf_obj, 2); } /* printf("Blit from texture!\n"); printf(" srcAtt %p dstAtt %p\n", readAtt, drawAtt); printf(" srcTex %p dstText %p\n", texObj, drawAtt->Texture); */ fb_tex_blit.samp_obj = setup_sampler(ctx, texObj, target, filter, srcLevel); if (ctx->Extensions.EXT_texture_sRGB_decode) { /* The GL 4.4 spec, section 18.3.1 ("Blitting Pixel Rectangles") says: * * "When values are taken from the read buffer, if FRAMEBUFFER_SRGB * is enabled and the value of FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING * for the framebuffer attachment corresponding to the read buffer * is SRGB (see section 9.2.3), the red, green, and blue components * are converted from the non-linear sRGB color space according to * equation 3.24. * * When values are written to the draw buffers, blit operations * bypass most of the fragment pipeline. The only fragment * operations which affect a blit are the pixel ownership test, * the scissor test, and sRGB conversion (see section 17.3.9)." * * ES 3.0 contains nearly the exact same text, but omits the part * about GL_FRAMEBUFFER_SRGB as that doesn't exist in ES. Mesa * defaults it to on for ES contexts, so we can safely check it. */ const bool decode = ctx->Color.sRGBEnabled && _mesa_is_format_srgb(rb->Format); _mesa_set_sampler_srgb_decode(ctx, fb_tex_blit.samp_obj, decode ? GL_DECODE_EXT : GL_SKIP_DECODE_EXT); } if (!glsl_version) { _mesa_TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); _mesa_set_enable(ctx, target, GL_TRUE); } /* Prepare vertex data (the VBO was previously created and bound) */ { struct vertex verts[4]; GLfloat s0, t0, s1, t1; if (target == GL_TEXTURE_2D) { const struct gl_texture_image *texImage = _mesa_select_tex_image(texObj, target, srcLevel); s0 = srcX0 / (float) texImage->Width; s1 = srcX1 / (float) texImage->Width; t0 = srcY0 / (float) texImage->Height; t1 = srcY1 / (float) texImage->Height; } else { assert(target == GL_TEXTURE_RECTANGLE_ARB); s0 = (float) srcX0; s1 = (float) srcX1; t0 = (float) srcY0; t1 = (float) srcY1; } /* Silence valgrind warnings about reading uninitialized stack. */ memset(verts, 0, sizeof(verts)); /* setup vertex positions */ verts[0].x = -1.0F * flipX; verts[0].y = -1.0F * flipY; verts[1].x = 1.0F * flipX; verts[1].y = -1.0F * flipY; verts[2].x = 1.0F * flipX; verts[2].y = 1.0F * flipY; verts[3].x = -1.0F * flipX; verts[3].y = 1.0F * flipY; verts[0].tex[0] = s0; verts[0].tex[1] = t0; verts[0].tex[2] = readAtt->Zoffset; verts[1].tex[0] = s1; verts[1].tex[1] = t0; verts[1].tex[2] = readAtt->Zoffset; verts[2].tex[0] = s1; verts[2].tex[1] = t1; verts[2].tex[2] = readAtt->Zoffset; verts[3].tex[0] = s0; verts[3].tex[1] = t1; verts[3].tex[2] = readAtt->Zoffset; _mesa_buffer_sub_data(ctx, blit->buf_obj, 0, sizeof(verts), verts); } /* setup viewport */ _mesa_set_viewport(ctx, 0, dstX, dstY, dstW, dstH); _mesa_ColorMask(!do_depth, !do_depth, !do_depth, !do_depth); _mesa_set_enable(ctx, GL_DEPTH_TEST, do_depth); _mesa_DepthMask(do_depth); _mesa_DepthFunc(GL_ALWAYS); _mesa_DrawArrays(GL_TRIANGLE_FAN, 0, 4); _mesa_meta_fb_tex_blit_end(ctx, target, &fb_tex_blit); return true; } void _mesa_meta_fb_tex_blit_begin(struct gl_context *ctx, struct fb_tex_blit_state *blit) { /* None of the existing callers preinitialize fb_tex_blit_state to zeros, * and both use stack variables. If samp_obj_save is not NULL, * _mesa_reference_sampler_object will try to dereference it. Leaving * random garbage in samp_obj_save can only lead to crashes. * * Since the state isn't persistent across calls, we won't catch ref * counting problems. */ blit->samp_obj_save = NULL; _mesa_reference_sampler_object(ctx, &blit->samp_obj_save, ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler); blit->temp_tex_obj = NULL; } void _mesa_meta_fb_tex_blit_end(struct gl_context *ctx, GLenum target, struct fb_tex_blit_state *blit) { struct gl_texture_object *const texObj = _mesa_get_current_tex_object(ctx, target); /* Either there is no temporary texture or the temporary texture is bound. */ assert(blit->temp_tex_obj == NULL || blit->temp_tex_obj == texObj); /* Restore texture object state, the texture binding will be restored by * _mesa_meta_end(). If the texture is the temporary texture that is about * to be destroyed, don't bother restoring its state. */ if (blit->temp_tex_obj == NULL) { /* If the target restricts values for base level or max level, we assume * that the original values were valid. */ if (blit->baseLevelSave != texObj->Attrib.BaseLevel) _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_BASE_LEVEL, &blit->baseLevelSave, false); if (blit->maxLevelSave != texObj->Attrib.MaxLevel) _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_MAX_LEVEL, &blit->maxLevelSave, false); /* If ARB_stencil_texturing is not supported, the mode won't have changed. */ if (texObj->StencilSampling != blit->stencilSamplingSave) { /* GLint so the compiler won't complain about type signedness mismatch * in the call to _mesa_texture_parameteriv below. */ const GLint param = blit->stencilSamplingSave ? GL_STENCIL_INDEX : GL_DEPTH_COMPONENT; _mesa_texture_parameteriv(ctx, texObj, GL_DEPTH_STENCIL_TEXTURE_MODE, ¶m, false); } } _mesa_bind_sampler(ctx, ctx->Texture.CurrentUnit, blit->samp_obj_save); _mesa_reference_sampler_object(ctx, &blit->samp_obj_save, NULL); _mesa_reference_sampler_object(ctx, &blit->samp_obj, NULL); _mesa_delete_nameless_texture(ctx, blit->temp_tex_obj); } static struct gl_texture_object * texture_object_from_renderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb) { struct gl_texture_image *texImage; struct gl_texture_object *texObj; const GLenum target = GL_TEXTURE_2D; texObj = ctx->Driver.NewTextureObject(ctx, 0xDEADBEEF, target); texImage = _mesa_get_tex_image(ctx, texObj, target, 0); if (!ctx->Driver.BindRenderbufferTexImage(ctx, rb, texImage)) { _mesa_delete_nameless_texture(ctx, texObj); return NULL; } if (ctx->Driver.FinishRenderTexture && !rb->NeedsFinishRenderTexture) { rb->NeedsFinishRenderTexture = true; ctx->Driver.FinishRenderTexture(ctx, rb); } return texObj; } static struct gl_sampler_object * setup_sampler(struct gl_context *ctx, struct gl_texture_object *texObj, GLenum target, GLenum filter, GLuint srcLevel) { struct gl_sampler_object *samp_obj = ctx->Driver.NewSamplerObject(ctx, 0xDEADBEEF); if (samp_obj == NULL) return NULL; _mesa_bind_sampler(ctx, ctx->Texture.CurrentUnit, samp_obj); _mesa_set_sampler_filters(ctx, samp_obj, filter, filter); _mesa_set_sampler_wrap(ctx, samp_obj, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, samp_obj->Attrib.WrapR); /* Prepare src texture state */ _mesa_bind_texture(ctx, target, texObj); if (target != GL_TEXTURE_RECTANGLE_ARB) { _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_BASE_LEVEL, (GLint *) &srcLevel, false); _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_MAX_LEVEL, (GLint *) &srcLevel, false); } return samp_obj; } /** * Meta implementation of ctx->Driver.BlitFramebuffer() in terms * of texture mapping and polygon rendering. */ GLbitfield _mesa_meta_BlitFramebuffer(struct gl_context *ctx, const struct gl_framebuffer *readFb, const struct gl_framebuffer *drawFb, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { const GLint dstW = abs(dstX1 - dstX0); const GLint dstH = abs(dstY1 - dstY0); const GLint dstFlipX = (dstX1 - dstX0) / dstW; const GLint dstFlipY = (dstY1 - dstY0) / dstH; struct { GLint srcX0, srcY0, srcX1, srcY1; GLint dstX0, dstY0, dstX1, dstY1; } clip = { srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1 }; const GLboolean use_glsl_version = ctx->Extensions.ARB_vertex_shader && ctx->Extensions.ARB_fragment_shader; /* Multisample blit is not supported. */ if (readFb->Visual.samples > 0) return mask; /* Clip a copy of the blit coordinates. If these differ from the input * coordinates, then we'll set the scissor. */ if (!_mesa_clip_blit(ctx, readFb, drawFb, &clip.srcX0, &clip.srcY0, &clip.srcX1, &clip.srcY1, &clip.dstX0, &clip.dstY0, &clip.dstX1, &clip.dstY1)) { /* clipped/scissored everything away */ return 0; } /* Only scissor and FRAMEBUFFER_SRGB affect blit. Leave sRGB alone, but * save restore scissor as we'll set a custom scissor if necessary. */ _mesa_meta_begin(ctx, MESA_META_ALL & ~(MESA_META_DRAW_BUFFERS | MESA_META_FRAMEBUFFER_SRGB)); /* Dithering shouldn't be performed for glBlitFramebuffer */ _mesa_set_enable(ctx, GL_DITHER, GL_FALSE); /* If the clipping earlier changed the destination rect at all, then * enable the scissor to clip to it. */ if (clip.dstX0 != dstX0 || clip.dstY0 != dstY0 || clip.dstX1 != dstX1 || clip.dstY1 != dstY1) { _mesa_set_enable(ctx, GL_SCISSOR_TEST, GL_TRUE); _mesa_Scissor(MIN2(clip.dstX0, clip.dstX1), MIN2(clip.dstY0, clip.dstY1), abs(clip.dstX0 - clip.dstX1), abs(clip.dstY0 - clip.dstY1)); } /* Try faster, direct texture approach first */ if (mask & GL_COLOR_BUFFER_BIT) { if (blitframebuffer_texture(ctx, readFb, drawFb, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, filter, dstFlipX, dstFlipY, use_glsl_version, false)) { mask &= ~GL_COLOR_BUFFER_BIT; } } if (mask & GL_DEPTH_BUFFER_BIT && use_glsl_version) { if (blitframebuffer_texture(ctx, readFb, drawFb, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, filter, dstFlipX, dstFlipY, use_glsl_version, true)) { mask &= ~GL_DEPTH_BUFFER_BIT; } } if (mask & GL_STENCIL_BUFFER_BIT) { /* XXX can't easily do stencil */ } _mesa_meta_end(ctx); return mask; } void _mesa_meta_glsl_blit_cleanup(struct gl_context *ctx, struct blit_state *blit) { if (blit->VAO) { _mesa_DeleteVertexArrays(1, &blit->VAO); blit->VAO = 0; _mesa_reference_buffer_object(ctx, &blit->buf_obj, NULL); } _mesa_meta_blit_shader_table_cleanup(ctx, &blit->shaders_with_depth); _mesa_meta_blit_shader_table_cleanup(ctx, &blit->shaders_without_depth); if (blit->depthTex.tex_obj != NULL) { _mesa_delete_nameless_texture(ctx, blit->depthTex.tex_obj); blit->depthTex.tex_obj = NULL; } } void _mesa_meta_and_swrast_BlitFramebuffer(struct gl_context *ctx, struct gl_framebuffer *readFb, struct gl_framebuffer *drawFb, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { mask = _mesa_meta_BlitFramebuffer(ctx, readFb, drawFb, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); if (mask == 0x0) return; _swrast_BlitFramebuffer(ctx, readFb, drawFb, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); }