From: Brian Paul Date: Sat, 1 Feb 2014 15:58:43 +0000 (-0700) Subject: mesa: move glBlitFramebuffer code into new blit.c file X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=bfcb9bb20451aa7c74d8769ae043fb11503e8453;p=mesa.git mesa: move glBlitFramebuffer code into new blit.c file Just for better organization. v2: update gl_genexec.py too (not api_exec.c) Acked-by: Kenneth Graunke --- diff --git a/src/mapi/glapi/gen/gl_genexec.py b/src/mapi/glapi/gen/gl_genexec.py index b7ac16b061a..6be3f84cf4d 100644 --- a/src/mapi/glapi/gen/gl_genexec.py +++ b/src/mapi/glapi/gen/gl_genexec.py @@ -51,6 +51,7 @@ header = """/** #include "main/atifragshader.h" #include "main/attrib.h" #include "main/blend.h" +#include "main/blit.h" #include "main/bufferobj.h" #include "main/arrayobj.h" #include "main/buffers.h" diff --git a/src/mesa/Makefile.sources b/src/mesa/Makefile.sources index d14823db9e1..6a8a045fb09 100644 --- a/src/mesa/Makefile.sources +++ b/src/mesa/Makefile.sources @@ -18,6 +18,7 @@ MAIN_FILES = \ $(SRCDIR)main/attrib.c \ $(SRCDIR)main/arrayobj.c \ $(SRCDIR)main/blend.c \ + $(SRCDIR)main/blit.c \ $(SRCDIR)main/bufferobj.c \ $(SRCDIR)main/buffers.c \ $(SRCDIR)main/clear.c \ diff --git a/src/mesa/SConscript b/src/mesa/SConscript index 77e2aaa7ee1..4bc4de4f41a 100644 --- a/src/mesa/SConscript +++ b/src/mesa/SConscript @@ -46,6 +46,7 @@ main_sources = [ 'main/attrib.c', 'main/arrayobj.c', 'main/blend.c', + 'main/blit.c', 'main/bufferobj.c', 'main/buffers.c', 'main/clear.c', diff --git a/src/mesa/main/blit.c b/src/mesa/main/blit.c new file mode 100644 index 00000000000..0b70a3da43f --- /dev/null +++ b/src/mesa/main/blit.c @@ -0,0 +1,513 @@ +/* + * 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 + +#include "context.h" +#include "enums.h" +#include "blit.h" +#include "fbobject.h" +#include "glformats.h" +#include "mtypes.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 < Elements(fb->Attachment); i++) { + if (fb->Attachment[i].Renderbuffer == rb) + return &fb->Attachment[i]; + } + return NULL; +} + + +/** + * 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; + + /* The simple case where we know the backing Mesa formats are the same. + */ + if (_mesa_get_srgb_format_linear(readRb->Format) == + _mesa_get_srgb_format_linear(drawRb->Format)) { + return GL_TRUE; + } + + /* The Mesa formats are different, so we must check whether the internal + * formats are compatible. + * + * 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. + * + * 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; + } +} + + +/** + * 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(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter) +{ + const GLbitfield legalMaskBits = (GL_COLOR_BUFFER_BIT | + GL_DEPTH_BUFFER_BIT | + GL_STENCIL_BUFFER_BIT); + const struct gl_framebuffer *readFb, *drawFb; + GET_CURRENT_CONTEXT(ctx); + + FLUSH_VERTICES(ctx, 0); + + 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_lookup_enum_by_nr(filter)); + + if (ctx->NewState) { + _mesa_update_state(ctx); + } + + readFb = ctx->ReadBuffer; + drawFb = ctx->DrawBuffer; + + if (!readFb || !drawFb) { + /* This will normally never happen but someday we may want to + * support MakeCurrent() with no drawables. + */ + return; + } + + /* 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, + "glBlitFramebufferEXT(incomplete draw/read buffers)"); + return; + } + + if (!is_valid_blit_filter(ctx, filter)) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBlitFramebufferEXT(%s)", + _mesa_lookup_enum_by_nr(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, "glBlitFramebufferEXT(%s)", + _mesa_lookup_enum_by_nr(filter)); + return; + } + + if (mask & ~legalMaskBits) { + _mesa_error( ctx, GL_INVALID_VALUE, "glBlitFramebufferEXT(mask)"); + 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, + "glBlitFramebufferEXT(depth/stencil requires GL_NEAREST filter)"); + return; + } + + /* get color read/draw renderbuffers */ + if (mask & GL_COLOR_BUFFER_BIT) { + const GLuint numColorDrawBuffers = ctx->DrawBuffer->_NumColorDrawBuffers; + const struct gl_renderbuffer *colorReadRb = readFb->_ColorReadBuffer; + const struct gl_renderbuffer *colorDrawRb = NULL; + GLuint i; + + /* From the EXT_framebuffer_object spec: + * + * "If a buffer is specified in 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 { + for (i = 0; i < numColorDrawBuffers; i++) { + colorDrawRb = ctx->DrawBuffer->_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, + "glBlitFramebuffer(source and destination color " + "buffer cannot be the same)"); + return; + } + + if (!compatible_color_datatypes(colorReadRb->Format, + colorDrawRb->Format)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBlitFramebufferEXT(color buffer datatypes mismatch)"); + return; + } + /* extra checks for multisample copies... */ + if (readFb->Visual.samples > 0 || drawFb->Visual.samples > 0) { + /* color formats must match */ + if (!compatible_resolve_formats(colorReadRb, colorDrawRb)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBlitFramebufferEXT(bad src/dst multisample pixel formats)"); + return; + } + } + } + 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, + "glBlitFramebufferEXT(integer color type)"); + 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 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 { + int read_z_bits, draw_z_bits; + + if (_mesa_is_gles3(ctx) && (drawRb == readRb)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBlitFramebuffer(source and destination stencil " + "buffer cannot be the same)"); + return; + } + + 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, + "glBlitFramebuffer(stencil attachment format mismatch)"); + return; + } + + 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, "glBlitFramebuffer" + "(stencil attachment depth format mismatch)"); + 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 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 { + int read_s_bit, draw_s_bit; + + if (_mesa_is_gles3(ctx) && (drawRb == readRb)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBlitFramebuffer(source and destination depth " + "buffer cannot be the same)"); + return; + } + + 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, + "glBlitFramebuffer(depth attachment format mismatch)"); + return; + } + + 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, "glBlitFramebuffer" + "(depth attachment stencil bits mismatch)"); + 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, + "glBlitFramebuffer(destination samples must be 0)"); + 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, + "glBlitFramebuffer(bad src/dst multisample region)"); + return; + } + } else { + if (readFb->Visual.samples > 0 && + drawFb->Visual.samples > 0 && + readFb->Visual.samples != drawFb->Visual.samples) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBlitFramebufferEXT(mismatched samples)"); + 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, + "glBlitFramebufferEXT(bad src/dst multisample region sizes)"); + return; + } + } + } + + /* Debug code */ + if (DEBUG_BLIT) { + const struct gl_renderbuffer *colorReadRb = readFb->_ColorReadBuffer; + const struct gl_renderbuffer *colorDrawRb = NULL; + GLuint i = 0; + + printf("glBlitFramebuffer(%d, %d, %d, %d, %d, %d, %d, %d," + " 0x%x, 0x%x)\n", + 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 < ctx->DrawBuffer->_NumColorDrawBuffers; i++) { + colorDrawRb = ctx->DrawBuffer->_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, + srcX0, srcY0, srcX1, srcY1, + dstX0, dstY0, dstX1, dstY1, + mask, filter); +} diff --git a/src/mesa/main/blit.h b/src/mesa/main/blit.h new file mode 100644 index 00000000000..533d6e5d158 --- /dev/null +++ b/src/mesa/main/blit.h @@ -0,0 +1,39 @@ +/* + * Mesa 3-D graphics library + * + * Copyright (C) 1999-2008 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"), + * 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. + */ + + +#ifndef BLIT_H +#define BLIT_H + +#include "compiler.h" +#include "glheader.h" + + +extern void GLAPIENTRY +_mesa_BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter); + + +#endif /* BLIT_H */ diff --git a/src/mesa/main/fbobject.c b/src/mesa/main/fbobject.c index b5b55fc83d1..7a1761f0237 100644 --- a/src/mesa/main/fbobject.c +++ b/src/mesa/main/fbobject.c @@ -50,10 +50,6 @@ #include "texobj.h" -/** Set this to 1 to debug/log glBlitFramebuffer() calls */ -#define DEBUG_BLIT 0 - - /** * Notes: * @@ -3069,475 +3065,6 @@ _mesa_GenerateMipmap(GLenum target) _mesa_unlock_texture(ctx, texObj); } - -static const struct gl_renderbuffer_attachment * -find_attachment(const struct gl_framebuffer *fb, - const struct gl_renderbuffer *rb) -{ - GLuint i; - for (i = 0; i < Elements(fb->Attachment); i++) { - if (fb->Attachment[i].Renderbuffer == rb) - return &fb->Attachment[i]; - } - return NULL; -} - - -/** - * 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; - - /* The simple case where we know the backing Mesa formats are the same. - */ - if (_mesa_get_srgb_format_linear(readRb->Format) == - _mesa_get_srgb_format_linear(drawRb->Format)) { - return GL_TRUE; - } - - /* The Mesa formats are different, so we must check whether the internal - * formats are compatible. - * - * 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. - * - * 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; - } -} - -/** - * 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(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, - GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, - GLbitfield mask, GLenum filter) -{ - const GLbitfield legalMaskBits = (GL_COLOR_BUFFER_BIT | - GL_DEPTH_BUFFER_BIT | - GL_STENCIL_BUFFER_BIT); - const struct gl_framebuffer *readFb, *drawFb; - GET_CURRENT_CONTEXT(ctx); - - FLUSH_VERTICES(ctx, 0); - - 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_lookup_enum_by_nr(filter)); - - if (ctx->NewState) { - _mesa_update_state(ctx); - } - - readFb = ctx->ReadBuffer; - drawFb = ctx->DrawBuffer; - - if (!readFb || !drawFb) { - /* This will normally never happen but someday we may want to - * support MakeCurrent() with no drawables. - */ - return; - } - - /* 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, - "glBlitFramebufferEXT(incomplete draw/read buffers)"); - return; - } - - if (!is_valid_blit_filter(ctx, filter)) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBlitFramebufferEXT(%s)", - _mesa_lookup_enum_by_nr(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, "glBlitFramebufferEXT(%s)", - _mesa_lookup_enum_by_nr(filter)); - return; - } - - if (mask & ~legalMaskBits) { - _mesa_error( ctx, GL_INVALID_VALUE, "glBlitFramebufferEXT(mask)"); - 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, - "glBlitFramebufferEXT(depth/stencil requires GL_NEAREST filter)"); - return; - } - - /* get color read/draw renderbuffers */ - if (mask & GL_COLOR_BUFFER_BIT) { - const GLuint numColorDrawBuffers = ctx->DrawBuffer->_NumColorDrawBuffers; - const struct gl_renderbuffer *colorReadRb = readFb->_ColorReadBuffer; - const struct gl_renderbuffer *colorDrawRb = NULL; - GLuint i; - - /* From the EXT_framebuffer_object spec: - * - * "If a buffer is specified in 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 { - for (i = 0; i < numColorDrawBuffers; i++) { - colorDrawRb = ctx->DrawBuffer->_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, - "glBlitFramebuffer(source and destination color " - "buffer cannot be the same)"); - return; - } - - if (!compatible_color_datatypes(colorReadRb->Format, - colorDrawRb->Format)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(color buffer datatypes mismatch)"); - return; - } - /* extra checks for multisample copies... */ - if (readFb->Visual.samples > 0 || drawFb->Visual.samples > 0) { - /* color formats must match */ - if (!compatible_resolve_formats(colorReadRb, colorDrawRb)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(bad src/dst multisample pixel formats)"); - return; - } - } - } - 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, - "glBlitFramebufferEXT(integer color type)"); - 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 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 { - int read_z_bits, draw_z_bits; - - if (_mesa_is_gles3(ctx) && (drawRb == readRb)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebuffer(source and destination stencil " - "buffer cannot be the same)"); - return; - } - - 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, - "glBlitFramebuffer(stencil attachment format mismatch)"); - return; - } - - 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, "glBlitFramebuffer" - "(stencil attachment depth format mismatch)"); - 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 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 { - int read_s_bit, draw_s_bit; - - if (_mesa_is_gles3(ctx) && (drawRb == readRb)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebuffer(source and destination depth " - "buffer cannot be the same)"); - return; - } - - 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, - "glBlitFramebuffer(depth attachment format mismatch)"); - return; - } - - 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, "glBlitFramebuffer" - "(depth attachment stencil bits mismatch)"); - 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, - "glBlitFramebuffer(destination samples must be 0)"); - 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, - "glBlitFramebuffer(bad src/dst multisample region)"); - return; - } - } else { - if (readFb->Visual.samples > 0 && - drawFb->Visual.samples > 0 && - readFb->Visual.samples != drawFb->Visual.samples) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(mismatched samples)"); - 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, - "glBlitFramebufferEXT(bad src/dst multisample region sizes)"); - return; - } - } - } - - /* Debug code */ - if (DEBUG_BLIT) { - const struct gl_renderbuffer *colorReadRb = readFb->_ColorReadBuffer; - const struct gl_renderbuffer *colorDrawRb = NULL; - GLuint i = 0; - - printf("glBlitFramebuffer(%d, %d, %d, %d, %d, %d, %d, %d," - " 0x%x, 0x%x)\n", - 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 < ctx->DrawBuffer->_NumColorDrawBuffers; i++) { - colorDrawRb = ctx->DrawBuffer->_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, - srcX0, srcY0, srcX1, srcY1, - dstX0, dstY0, dstX1, dstY1, - mask, filter); -} - - static void invalidate_framebuffer_storage(GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, diff --git a/src/mesa/main/fbobject.h b/src/mesa/main/fbobject.h index ab138cfff84..1f5aa34ef74 100644 --- a/src/mesa/main/fbobject.h +++ b/src/mesa/main/fbobject.h @@ -206,11 +206,6 @@ extern void GLAPIENTRY _mesa_GenerateMipmap(GLenum target); -extern void GLAPIENTRY -_mesa_BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, - GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, - GLbitfield mask, GLenum filter); - extern void GLAPIENTRY _mesa_InvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y,