| /* |
| * Mesa 3-D graphics library |
| * |
| * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. |
| * Copyright (C) 1999-2013 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. |
| */ |
| |
| /* |
| * glBlitFramebuffer functions. |
| */ |
| |
| #include <stdbool.h> |
| #include <stdio.h> |
| |
| #include "context.h" |
| #include "enums.h" |
| #include "blit.h" |
| #include "fbobject.h" |
| #include "framebuffer.h" |
| #include "glformats.h" |
| #include "mtypes.h" |
| #include "macros.h" |
| #include "state.h" |
| |
| |
| /** Set this to 1 to debug/log glBlitFramebuffer() calls */ |
| #define DEBUG_BLIT 0 |
| |
| |
| |
| static const struct gl_renderbuffer_attachment * |
| find_attachment(const struct gl_framebuffer *fb, |
| const struct gl_renderbuffer *rb) |
| { |
| GLuint i; |
| for (i = 0; i < ARRAY_SIZE(fb->Attachment); i++) { |
| if (fb->Attachment[i].Renderbuffer == rb) |
| return &fb->Attachment[i]; |
| } |
| return NULL; |
| } |
| |
| |
| /** |
| * \return true if two regions overlap, false otherwise |
| */ |
| bool |
| _mesa_regions_overlap(int srcX0, int srcY0, |
| int srcX1, int srcY1, |
| int dstX0, int dstY0, |
| int dstX1, int dstY1) |
| { |
| if (MAX2(srcX0, srcX1) <= MIN2(dstX0, dstX1)) |
| return false; /* dst completely right of src */ |
| |
| if (MAX2(dstX0, dstX1) <= MIN2(srcX0, srcX1)) |
| return false; /* dst completely left of src */ |
| |
| if (MAX2(srcY0, srcY1) <= MIN2(dstY0, dstY1)) |
| return false; /* dst completely above src */ |
| |
| if (MAX2(dstY0, dstY1) <= MIN2(srcY0, srcY1)) |
| return false; /* dst completely below src */ |
| |
| return true; /* some overlap */ |
| } |
| |
| |
| /** |
| * Helper function for checking if the datatypes of color buffers are |
| * compatible for glBlitFramebuffer. From the 3.1 spec, page 198: |
| * |
| * "GL_INVALID_OPERATION is generated if mask contains GL_COLOR_BUFFER_BIT |
| * and any of the following conditions hold: |
| * - The read buffer contains fixed-point or floating-point values and any |
| * draw buffer contains neither fixed-point nor floating-point values. |
| * - The read buffer contains unsigned integer values and any draw buffer |
| * does not contain unsigned integer values. |
| * - The read buffer contains signed integer values and any draw buffer |
| * does not contain signed integer values." |
| */ |
| static GLboolean |
| compatible_color_datatypes(mesa_format srcFormat, mesa_format dstFormat) |
| { |
| GLenum srcType = _mesa_get_format_datatype(srcFormat); |
| GLenum dstType = _mesa_get_format_datatype(dstFormat); |
| |
| if (srcType != GL_INT && srcType != GL_UNSIGNED_INT) { |
| assert(srcType == GL_UNSIGNED_NORMALIZED || |
| srcType == GL_SIGNED_NORMALIZED || |
| srcType == GL_FLOAT); |
| /* Boil any of those types down to GL_FLOAT */ |
| srcType = GL_FLOAT; |
| } |
| |
| if (dstType != GL_INT && dstType != GL_UNSIGNED_INT) { |
| assert(dstType == GL_UNSIGNED_NORMALIZED || |
| dstType == GL_SIGNED_NORMALIZED || |
| dstType == GL_FLOAT); |
| /* Boil any of those types down to GL_FLOAT */ |
| dstType = GL_FLOAT; |
| } |
| |
| return srcType == dstType; |
| } |
| |
| |
| static GLboolean |
| compatible_resolve_formats(const struct gl_renderbuffer *readRb, |
| const struct gl_renderbuffer *drawRb) |
| { |
| GLenum readFormat, drawFormat; |
| |
| /* This checks whether the internal formats are compatible rather than the |
| * Mesa format for two reasons: |
| * |
| * • Under some circumstances, the user may request e.g. two GL_RGBA8 |
| * textures and get two entirely different Mesa formats like RGBA8888 and |
| * ARGB8888. Drivers behaving like that should be able to cope with |
| * non-matching formats by themselves, because it's not the user's fault. |
| * |
| * • Picking two different internal formats can end up with the same Mesa |
| * format. For example the driver might be simulating GL_RGB textures |
| * with GL_RGBA internally and in that case both internal formats would |
| * end up with RGBA8888. |
| * |
| * This function is used to generate a GL error according to the spec so in |
| * both cases we want to be looking at the application-level format, which |
| * is InternalFormat. |
| * |
| * Blits between linear and sRGB formats are also allowed. |
| */ |
| readFormat = _mesa_get_nongeneric_internalformat(readRb->InternalFormat); |
| drawFormat = _mesa_get_nongeneric_internalformat(drawRb->InternalFormat); |
| readFormat = _mesa_get_linear_internalformat(readFormat); |
| drawFormat = _mesa_get_linear_internalformat(drawFormat); |
| |
| if (readFormat == drawFormat) { |
| return GL_TRUE; |
| } |
| |
| return GL_FALSE; |
| } |
| |
| |
| static GLboolean |
| is_valid_blit_filter(const struct gl_context *ctx, GLenum filter) |
| { |
| switch (filter) { |
| case GL_NEAREST: |
| case GL_LINEAR: |
| return true; |
| case GL_SCALED_RESOLVE_FASTEST_EXT: |
| case GL_SCALED_RESOLVE_NICEST_EXT: |
| return ctx->Extensions.EXT_framebuffer_multisample_blit_scaled; |
| default: |
| return false; |
| } |
| } |
| |
| |
| static bool |
| validate_color_buffer(struct gl_context *ctx, struct gl_framebuffer *readFb, |
| struct gl_framebuffer *drawFb, GLenum filter, |
| const char *func) |
| { |
| const GLuint numColorDrawBuffers = drawFb->_NumColorDrawBuffers; |
| const struct gl_renderbuffer *colorReadRb = readFb->_ColorReadBuffer; |
| const struct gl_renderbuffer *colorDrawRb = NULL; |
| GLuint i; |
| |
| for (i = 0; i < numColorDrawBuffers; i++) { |
| colorDrawRb = drawFb->_ColorDrawBuffers[i]; |
| if (!colorDrawRb) |
| continue; |
| |
| /* Page 193 (page 205 of the PDF) in section 4.3.2 of the OpenGL |
| * ES 3.0.1 spec says: |
| * |
| * "If the source and destination buffers are identical, an |
| * INVALID_OPERATION error is generated. Different mipmap levels of a |
| * texture, different layers of a three- dimensional texture or |
| * two-dimensional array texture, and different faces of a cube map |
| * texture do not constitute identical buffers." |
| */ |
| if (_mesa_is_gles3(ctx) && (colorDrawRb == colorReadRb)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(source and destination color buffer cannot be the " |
| "same)", func); |
| return false; |
| } |
| |
| if (!compatible_color_datatypes(colorReadRb->Format, |
| colorDrawRb->Format)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(color buffer datatypes mismatch)", func); |
| return false; |
| } |
| |
| /* extra checks for multisample copies... */ |
| if (readFb->Visual.samples > 0 || drawFb->Visual.samples > 0) { |
| /* color formats must match on GLES. This isn't checked on desktop GL |
| * because the GL 4.4 spec was changed to allow it. In the section |
| * entitled “Changes in the released |
| * Specification of July 22, 2013” it says: |
| * |
| * “Relax BlitFramebuffer in section 18.3.1 so that format conversion |
| * can take place during multisample blits, since drivers already |
| * allow this and some apps depend on it.” |
| */ |
| if (_mesa_is_gles(ctx) && |
| !compatible_resolve_formats(colorReadRb, colorDrawRb)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(bad src/dst multisample pixel formats)", func); |
| return false; |
| } |
| } |
| |
| } |
| |
| if (filter != GL_NEAREST) { |
| /* From EXT_framebuffer_multisample_blit_scaled specification: |
| * "Calling BlitFramebuffer will result in an INVALID_OPERATION error if |
| * filter is not NEAREST and read buffer contains integer data." |
| */ |
| GLenum type = _mesa_get_format_datatype(colorReadRb->Format); |
| if (type == GL_INT || type == GL_UNSIGNED_INT) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(integer color type)", func); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| |
| static bool |
| validate_stencil_buffer(struct gl_context *ctx, struct gl_framebuffer *readFb, |
| struct gl_framebuffer *drawFb, const char *func) |
| { |
| struct gl_renderbuffer *readRb = |
| readFb->Attachment[BUFFER_STENCIL].Renderbuffer; |
| struct gl_renderbuffer *drawRb = |
| drawFb->Attachment[BUFFER_STENCIL].Renderbuffer; |
| int read_z_bits, draw_z_bits; |
| |
| if (_mesa_is_gles3(ctx) && (drawRb == readRb)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(source and destination stencil buffer cannot be the " |
| "same)", func); |
| return false; |
| } |
| |
| if (_mesa_get_format_bits(readRb->Format, GL_STENCIL_BITS) != |
| _mesa_get_format_bits(drawRb->Format, GL_STENCIL_BITS)) { |
| /* There is no need to check the stencil datatype here, because |
| * there is only one: GL_UNSIGNED_INT. |
| */ |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(stencil attachment format mismatch)", func); |
| return false; |
| } |
| |
| read_z_bits = _mesa_get_format_bits(readRb->Format, GL_DEPTH_BITS); |
| draw_z_bits = _mesa_get_format_bits(drawRb->Format, GL_DEPTH_BITS); |
| |
| /* If both buffers also have depth data, the depth formats must match |
| * as well. If one doesn't have depth, it's not blitted, so we should |
| * ignore the depth format check. |
| */ |
| if (read_z_bits > 0 && draw_z_bits > 0 && |
| (read_z_bits != draw_z_bits || |
| _mesa_get_format_datatype(readRb->Format) != |
| _mesa_get_format_datatype(drawRb->Format))) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(stencil attachment depth format mismatch)", func); |
| return false; |
| } |
| return true; |
| } |
| |
| |
| static bool |
| validate_depth_buffer(struct gl_context *ctx, struct gl_framebuffer *readFb, |
| struct gl_framebuffer *drawFb, const char *func) |
| { |
| struct gl_renderbuffer *readRb = |
| readFb->Attachment[BUFFER_DEPTH].Renderbuffer; |
| struct gl_renderbuffer *drawRb = |
| drawFb->Attachment[BUFFER_DEPTH].Renderbuffer; |
| int read_s_bit, draw_s_bit; |
| |
| if (_mesa_is_gles3(ctx) && (drawRb == readRb)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(source and destination depth buffer cannot be the same)", |
| func); |
| return false; |
| } |
| |
| if ((_mesa_get_format_bits(readRb->Format, GL_DEPTH_BITS) != |
| _mesa_get_format_bits(drawRb->Format, GL_DEPTH_BITS)) || |
| (_mesa_get_format_datatype(readRb->Format) != |
| _mesa_get_format_datatype(drawRb->Format))) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(depth attachment format mismatch)", func); |
| return false; |
| } |
| |
| read_s_bit = _mesa_get_format_bits(readRb->Format, GL_STENCIL_BITS); |
| draw_s_bit = _mesa_get_format_bits(drawRb->Format, GL_STENCIL_BITS); |
| |
| /* If both buffers also have stencil data, the stencil formats must match as |
| * well. If one doesn't have stencil, it's not blitted, so we should ignore |
| * the stencil format check. |
| */ |
| if (read_s_bit > 0 && draw_s_bit > 0 && read_s_bit != draw_s_bit) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(depth attachment stencil bits mismatch)", func); |
| return false; |
| } |
| return true; |
| } |
| |
| |
| static ALWAYS_INLINE void |
| blit_framebuffer(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, bool no_error, const char *func) |
| { |
| FLUSH_VERTICES(ctx, 0); |
| |
| if (!readFb || !drawFb) { |
| /* This will normally never happen but someday we may want to |
| * support MakeCurrent() with no drawables. |
| */ |
| return; |
| } |
| |
| /* Update completeness status of readFb and drawFb. */ |
| _mesa_update_framebuffer(ctx, readFb, drawFb); |
| |
| /* Make sure drawFb has an initialized bounding box. */ |
| _mesa_update_draw_buffer_bounds(ctx, drawFb); |
| |
| if (!no_error) { |
| const GLbitfield legalMaskBits = (GL_COLOR_BUFFER_BIT | |
| GL_DEPTH_BUFFER_BIT | |
| GL_STENCIL_BUFFER_BIT); |
| |
| /* check for complete framebuffers */ |
| if (drawFb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT || |
| readFb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { |
| _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT, |
| "%s(incomplete draw/read buffers)", func); |
| return; |
| } |
| |
| if (!is_valid_blit_filter(ctx, filter)) { |
| _mesa_error(ctx, GL_INVALID_ENUM, "%s(invalid filter %s)", func, |
| _mesa_enum_to_string(filter)); |
| return; |
| } |
| |
| if ((filter == GL_SCALED_RESOLVE_FASTEST_EXT || |
| filter == GL_SCALED_RESOLVE_NICEST_EXT) && |
| (readFb->Visual.samples == 0 || drawFb->Visual.samples > 0)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, "%s(%s: invalid samples)", func, |
| _mesa_enum_to_string(filter)); |
| return; |
| } |
| |
| if (mask & ~legalMaskBits) { |
| _mesa_error(ctx, GL_INVALID_VALUE, "%s(invalid mask bits set)", func); |
| return; |
| } |
| |
| /* depth/stencil must be blitted with nearest filtering */ |
| if ((mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) |
| && filter != GL_NEAREST) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(depth/stencil requires GL_NEAREST filter)", func); |
| return; |
| } |
| |
| if (_mesa_is_gles3(ctx)) { |
| /* Page 194 (page 206 of the PDF) in section 4.3.2 of the OpenGL ES |
| * 3.0.1 spec says: |
| * |
| * "If SAMPLE_BUFFERS for the draw framebuffer is greater than |
| * zero, an INVALID_OPERATION error is generated." |
| */ |
| if (drawFb->Visual.samples > 0) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(destination samples must be 0)", func); |
| return; |
| } |
| |
| /* Page 194 (page 206 of the PDF) in section 4.3.2 of the OpenGL ES |
| * 3.0.1 spec says: |
| * |
| * "If SAMPLE_BUFFERS for the read framebuffer is greater than |
| * zero, no copy is performed and an INVALID_OPERATION error is |
| * generated if the formats of the read and draw framebuffers are |
| * not identical or if the source and destination rectangles are |
| * not defined with the same (X0, Y0) and (X1, Y1) bounds." |
| * |
| * The format check was made above because desktop OpenGL has the same |
| * requirement. |
| */ |
| if (readFb->Visual.samples > 0 |
| && (srcX0 != dstX0 || srcY0 != dstY0 |
| || srcX1 != dstX1 || srcY1 != dstY1)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(bad src/dst multisample region)", func); |
| return; |
| } |
| } else { |
| if (readFb->Visual.samples > 0 && |
| drawFb->Visual.samples > 0 && |
| readFb->Visual.samples != drawFb->Visual.samples) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(mismatched samples)", func); |
| return; |
| } |
| |
| /* extra checks for multisample copies... */ |
| if ((readFb->Visual.samples > 0 || drawFb->Visual.samples > 0) && |
| (filter == GL_NEAREST || filter == GL_LINEAR)) { |
| /* src and dest region sizes must be the same */ |
| if (abs(srcX1 - srcX0) != abs(dstX1 - dstX0) || |
| abs(srcY1 - srcY0) != abs(dstY1 - dstY0)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(bad src/dst multisample region sizes)", func); |
| return; |
| } |
| } |
| } |
| } |
| |
| /* get color read/draw renderbuffers */ |
| if (mask & GL_COLOR_BUFFER_BIT) { |
| const GLuint numColorDrawBuffers = drawFb->_NumColorDrawBuffers; |
| const struct gl_renderbuffer *colorReadRb = readFb->_ColorReadBuffer; |
| |
| /* From the EXT_framebuffer_object spec: |
| * |
| * "If a buffer is specified in <mask> and does not exist in both |
| * the read and draw framebuffers, the corresponding bit is silently |
| * ignored." |
| */ |
| if (!colorReadRb || numColorDrawBuffers == 0) { |
| mask &= ~GL_COLOR_BUFFER_BIT; |
| } else if (!no_error) { |
| if (!validate_color_buffer(ctx, readFb, drawFb, filter, func)) |
| return; |
| } |
| } |
| |
| if (mask & GL_STENCIL_BUFFER_BIT) { |
| struct gl_renderbuffer *readRb = |
| readFb->Attachment[BUFFER_STENCIL].Renderbuffer; |
| struct gl_renderbuffer *drawRb = |
| drawFb->Attachment[BUFFER_STENCIL].Renderbuffer; |
| |
| /* From the EXT_framebuffer_object spec: |
| * |
| * "If a buffer is specified in <mask> and does not exist in both |
| * the read and draw framebuffers, the corresponding bit is silently |
| * ignored." |
| */ |
| if ((readRb == NULL) || (drawRb == NULL)) { |
| mask &= ~GL_STENCIL_BUFFER_BIT; |
| } else if (!no_error) { |
| if (!validate_stencil_buffer(ctx, readFb, drawFb, func)) |
| return; |
| } |
| } |
| |
| if (mask & GL_DEPTH_BUFFER_BIT) { |
| struct gl_renderbuffer *readRb = |
| readFb->Attachment[BUFFER_DEPTH].Renderbuffer; |
| struct gl_renderbuffer *drawRb = |
| drawFb->Attachment[BUFFER_DEPTH].Renderbuffer; |
| |
| /* From the EXT_framebuffer_object spec: |
| * |
| * "If a buffer is specified in <mask> and does not exist in both |
| * the read and draw framebuffers, the corresponding bit is silently |
| * ignored." |
| */ |
| if ((readRb == NULL) || (drawRb == NULL)) { |
| mask &= ~GL_DEPTH_BUFFER_BIT; |
| } else if (!no_error) { |
| if (!validate_depth_buffer(ctx, readFb, drawFb, func)) |
| return; |
| } |
| } |
| |
| /* Debug code */ |
| if (DEBUG_BLIT) { |
| const struct gl_renderbuffer *colorReadRb = readFb->_ColorReadBuffer; |
| const struct gl_renderbuffer *colorDrawRb = NULL; |
| GLuint i = 0; |
| |
| printf("%s(%d, %d, %d, %d, %d, %d, %d, %d," |
| " 0x%x, 0x%x)\n", func, |
| srcX0, srcY0, srcX1, srcY1, |
| dstX0, dstY0, dstX1, dstY1, |
| mask, filter); |
| |
| if (colorReadRb) { |
| const struct gl_renderbuffer_attachment *att; |
| |
| att = find_attachment(readFb, colorReadRb); |
| printf(" Src FBO %u RB %u (%dx%d) ", |
| readFb->Name, colorReadRb->Name, |
| colorReadRb->Width, colorReadRb->Height); |
| if (att && att->Texture) { |
| printf("Tex %u tgt 0x%x level %u face %u", |
| att->Texture->Name, |
| att->Texture->Target, |
| att->TextureLevel, |
| att->CubeMapFace); |
| } |
| printf("\n"); |
| |
| /* Print all active color render buffers */ |
| for (i = 0; i < drawFb->_NumColorDrawBuffers; i++) { |
| colorDrawRb = drawFb->_ColorDrawBuffers[i]; |
| if (!colorDrawRb) |
| continue; |
| |
| att = find_attachment(drawFb, colorDrawRb); |
| printf(" Dst FBO %u RB %u (%dx%d) ", |
| drawFb->Name, colorDrawRb->Name, |
| colorDrawRb->Width, colorDrawRb->Height); |
| if (att && att->Texture) { |
| printf("Tex %u tgt 0x%x level %u face %u", |
| att->Texture->Name, |
| att->Texture->Target, |
| att->TextureLevel, |
| att->CubeMapFace); |
| } |
| printf("\n"); |
| } |
| } |
| } |
| |
| if (!mask || |
| (srcX1 - srcX0) == 0 || (srcY1 - srcY0) == 0 || |
| (dstX1 - dstX0) == 0 || (dstY1 - dstY0) == 0) { |
| return; |
| } |
| |
| assert(ctx->Driver.BlitFramebuffer); |
| ctx->Driver.BlitFramebuffer(ctx, readFb, drawFb, |
| srcX0, srcY0, srcX1, srcY1, |
| dstX0, dstY0, dstX1, dstY1, |
| mask, filter); |
| } |
| |
| |
| static void |
| blit_framebuffer_err(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, const char *func) |
| { |
| /* We are wrapping the err variant of the always inlined |
| * blit_framebuffer() to avoid inlining it in every caller. |
| */ |
| blit_framebuffer(ctx, readFb, drawFb, srcX0, srcY0, srcX1, srcY1, |
| dstX0, dstY0, dstX1, dstY1, mask, filter, false, func); |
| } |
| |
| |
| /** |
| * Blit rectangular region, optionally from one framebuffer to another. |
| * |
| * Note, if the src buffer is multisampled and the dest is not, this is |
| * when the samples must be resolved to a single color. |
| */ |
| void GLAPIENTRY |
| _mesa_BlitFramebuffer_no_error(GLint srcX0, GLint srcY0, GLint srcX1, |
| GLint srcY1, GLint dstX0, GLint dstY0, |
| GLint dstX1, GLint dstY1, |
| GLbitfield mask, GLenum filter) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| |
| blit_framebuffer(ctx, ctx->ReadBuffer, ctx->DrawBuffer, |
| srcX0, srcY0, srcX1, srcY1, |
| dstX0, dstY0, dstX1, dstY1, |
| mask, filter, true, "glBlitFramebuffer"); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, |
| GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, |
| GLbitfield mask, GLenum filter) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| |
| if (MESA_VERBOSE & VERBOSE_API) |
| _mesa_debug(ctx, |
| "glBlitFramebuffer(%d, %d, %d, %d, " |
| " %d, %d, %d, %d, 0x%x, %s)\n", |
| srcX0, srcY0, srcX1, srcY1, |
| dstX0, dstY0, dstX1, dstY1, |
| mask, _mesa_enum_to_string(filter)); |
| |
| blit_framebuffer_err(ctx, ctx->ReadBuffer, ctx->DrawBuffer, |
| srcX0, srcY0, srcX1, srcY1, |
| dstX0, dstY0, dstX1, dstY1, |
| mask, filter, "glBlitFramebuffer"); |
| } |
| |
| |
| static ALWAYS_INLINE void |
| blit_named_framebuffer(struct gl_context *ctx, |
| GLuint readFramebuffer, GLuint drawFramebuffer, |
| GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, |
| GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, |
| GLbitfield mask, GLenum filter, bool no_error) |
| { |
| struct gl_framebuffer *readFb, *drawFb; |
| |
| /* |
| * According to PDF page 533 of the OpenGL 4.5 core spec (30.10.2014, |
| * Section 18.3 Copying Pixels): |
| * "... if readFramebuffer or drawFramebuffer is zero (for |
| * BlitNamedFramebuffer), then the default read or draw framebuffer is |
| * used as the corresponding source or destination framebuffer, |
| * respectively." |
| */ |
| if (readFramebuffer) { |
| if (no_error) { |
| readFb = _mesa_lookup_framebuffer(ctx, readFramebuffer); |
| } else { |
| readFb = _mesa_lookup_framebuffer_err(ctx, readFramebuffer, |
| "glBlitNamedFramebuffer"); |
| if (!readFb) |
| return; |
| } |
| } else { |
| readFb = ctx->WinSysReadBuffer; |
| } |
| |
| if (drawFramebuffer) { |
| if (no_error) { |
| drawFb = _mesa_lookup_framebuffer(ctx, drawFramebuffer); |
| } else { |
| drawFb = _mesa_lookup_framebuffer_err(ctx, drawFramebuffer, |
| "glBlitNamedFramebuffer"); |
| if (!drawFb) |
| return; |
| } |
| } else { |
| drawFb = ctx->WinSysDrawBuffer; |
| } |
| |
| blit_framebuffer(ctx, readFb, drawFb, |
| srcX0, srcY0, srcX1, srcY1, |
| dstX0, dstY0, dstX1, dstY1, |
| mask, filter, no_error, "glBlitNamedFramebuffer"); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_BlitNamedFramebuffer_no_error(GLuint readFramebuffer, |
| GLuint drawFramebuffer, |
| GLint srcX0, GLint srcY0, |
| GLint srcX1, GLint srcY1, |
| GLint dstX0, GLint dstY0, |
| GLint dstX1, GLint dstY1, |
| GLbitfield mask, GLenum filter) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| |
| blit_named_framebuffer(ctx, readFramebuffer, drawFramebuffer, |
| srcX0, srcY0, srcX1, srcY1, |
| dstX0, dstY0, dstX1, dstY1, |
| mask, filter, true); |
| } |
| |
| |
| void GLAPIENTRY |
| _mesa_BlitNamedFramebuffer(GLuint readFramebuffer, GLuint drawFramebuffer, |
| GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, |
| GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, |
| GLbitfield mask, GLenum filter) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| |
| if (MESA_VERBOSE & VERBOSE_API) |
| _mesa_debug(ctx, |
| "glBlitNamedFramebuffer(%u %u %d, %d, %d, %d, " |
| " %d, %d, %d, %d, 0x%x, %s)\n", |
| readFramebuffer, drawFramebuffer, |
| srcX0, srcY0, srcX1, srcY1, |
| dstX0, dstY0, dstX1, dstY1, |
| mask, _mesa_enum_to_string(filter)); |
| |
| blit_named_framebuffer(ctx, readFramebuffer, drawFramebuffer, |
| srcX0, srcY0, srcX1, srcY1, |
| dstX0, dstY0, dstX1, dstY1, |
| mask, filter, false); |
| } |