/* * Mesa 3-D graphics library * * Copyright (C) 1999-2007 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. */ /** * \file clear.c * glClearColor, glClearIndex, glClear() functions. */ #include "glheader.h" #include "clear.h" #include "context.h" #include "enums.h" #include "fbobject.h" #include "get.h" #include "macros.h" #include "mtypes.h" #include "state.h" void GLAPIENTRY _mesa_ClearIndex( GLfloat c ) { GET_CURRENT_CONTEXT(ctx); ctx->Color.ClearIndex = (GLuint) c; } /** * Specify the clear values for the color buffers. * * \param red red color component. * \param green green color component. * \param blue blue color component. * \param alpha alpha component. * * \sa glClearColor(). */ void GLAPIENTRY _mesa_ClearColor( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha ) { GET_CURRENT_CONTEXT(ctx); ctx->Color.ClearColor.f[0] = red; ctx->Color.ClearColor.f[1] = green; ctx->Color.ClearColor.f[2] = blue; ctx->Color.ClearColor.f[3] = alpha; } /** * GL_EXT_texture_integer */ void GLAPIENTRY _mesa_ClearColorIiEXT(GLint r, GLint g, GLint b, GLint a) { GET_CURRENT_CONTEXT(ctx); ctx->Color.ClearColor.i[0] = r; ctx->Color.ClearColor.i[1] = g; ctx->Color.ClearColor.i[2] = b; ctx->Color.ClearColor.i[3] = a; } /** * GL_EXT_texture_integer */ void GLAPIENTRY _mesa_ClearColorIuiEXT(GLuint r, GLuint g, GLuint b, GLuint a) { GET_CURRENT_CONTEXT(ctx); ctx->Color.ClearColor.ui[0] = r; ctx->Color.ClearColor.ui[1] = g; ctx->Color.ClearColor.ui[2] = b; ctx->Color.ClearColor.ui[3] = a; } /** * Returns true if color writes are enabled for the given color attachment. * * Beyond checking ColorMask, this uses _mesa_format_has_color_component to * ignore components that don't actually exist in the format (such as X in * XRGB). */ static bool color_buffer_writes_enabled(const struct gl_context *ctx, unsigned idx) { struct gl_renderbuffer *rb = ctx->DrawBuffer->_ColorDrawBuffers[idx]; GLuint c; if (rb) { for (c = 0; c < 4; c++) { if (ctx->Color.ColorMask[idx][c] && _mesa_format_has_color_component(rb->Format, c)) { return true; } } } return false; } /** * Clear buffers. * * \param mask bit-mask indicating the buffers to be cleared. * * Flushes the vertices and verifies the parameter. * If __struct gl_contextRec::NewState is set then calls _mesa_update_state() * to update gl_frame_buffer::_Xmin, etc. If the rasterization mode is set to * GL_RENDER then requests the driver to clear the buffers, via the * dd_function_table::Clear callback. */ static ALWAYS_INLINE void clear(struct gl_context *ctx, GLbitfield mask, bool no_error) { FLUSH_VERTICES(ctx, 0); FLUSH_CURRENT(ctx, 0); if (!no_error) { if (mask & ~(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_ACCUM_BUFFER_BIT)) { _mesa_error( ctx, GL_INVALID_VALUE, "glClear(0x%x)", mask); return; } /* Accumulation buffers were removed in core contexts, and they never * existed in OpenGL ES. */ if ((mask & GL_ACCUM_BUFFER_BIT) != 0 && (ctx->API == API_OPENGL_CORE || _mesa_is_gles(ctx))) { _mesa_error( ctx, GL_INVALID_VALUE, "glClear(GL_ACCUM_BUFFER_BIT)"); return; } } if (ctx->NewState) { _mesa_update_state( ctx ); /* update _Xmin, etc */ } if (!no_error && ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT, "glClear(incomplete framebuffer)"); return; } if (ctx->RasterDiscard) return; if (ctx->RenderMode == GL_RENDER) { GLbitfield bufferMask; /* don't clear depth buffer if depth writing disabled */ if (!ctx->Depth.Mask) mask &= ~GL_DEPTH_BUFFER_BIT; /* Build the bitmask to send to device driver's Clear function. * Note that the GL_COLOR_BUFFER_BIT flag will expand to 0, 1, 2 or 4 * of the BUFFER_BIT_FRONT/BACK_LEFT/RIGHT flags, or one of the * BUFFER_BIT_COLORn flags. */ bufferMask = 0; if (mask & GL_COLOR_BUFFER_BIT) { GLuint i; for (i = 0; i < ctx->DrawBuffer->_NumColorDrawBuffers; i++) { GLint buf = ctx->DrawBuffer->_ColorDrawBufferIndexes[i]; if (buf >= 0 && color_buffer_writes_enabled(ctx, i)) { bufferMask |= 1 << buf; } } } if ((mask & GL_DEPTH_BUFFER_BIT) && ctx->DrawBuffer->Visual.haveDepthBuffer) { bufferMask |= BUFFER_BIT_DEPTH; } if ((mask & GL_STENCIL_BUFFER_BIT) && ctx->DrawBuffer->Visual.haveStencilBuffer) { bufferMask |= BUFFER_BIT_STENCIL; } if ((mask & GL_ACCUM_BUFFER_BIT) && ctx->DrawBuffer->Visual.haveAccumBuffer) { bufferMask |= BUFFER_BIT_ACCUM; } assert(ctx->Driver.Clear); ctx->Driver.Clear(ctx, bufferMask); } } void GLAPIENTRY _mesa_Clear_no_error(GLbitfield mask) { GET_CURRENT_CONTEXT(ctx); clear(ctx, mask, true); } void GLAPIENTRY _mesa_Clear(GLbitfield mask) { GET_CURRENT_CONTEXT(ctx); if (MESA_VERBOSE & VERBOSE_API) _mesa_debug(ctx, "glClear 0x%x\n", mask); clear(ctx, mask, false); } /** Returned by make_color_buffer_mask() for errors */ #define INVALID_MASK ~0x0U /** * Convert the glClearBuffer 'drawbuffer' parameter into a bitmask of * BUFFER_BIT_x values. * Return INVALID_MASK if the drawbuffer value is invalid. */ static GLbitfield make_color_buffer_mask(struct gl_context *ctx, GLint drawbuffer) { const struct gl_renderbuffer_attachment *att = ctx->DrawBuffer->Attachment; GLbitfield mask = 0x0; /* From the GL 4.0 specification: * If buffer is COLOR, a particular draw buffer DRAW_BUFFERi is * specified by passing i as the parameter drawbuffer, and value * points to a four-element vector specifying the R, G, B, and A * color to clear that draw buffer to. If the draw buffer is one * of FRONT, BACK, LEFT, RIGHT, or FRONT_AND_BACK, identifying * multiple buffers, each selected buffer is cleared to the same * value. * * Note that "drawbuffer" and "draw buffer" have different meaning. * "drawbuffer" specifies DRAW_BUFFERi, while "draw buffer" is what's * assigned to DRAW_BUFFERi. It could be COLOR_ATTACHMENT0, FRONT, BACK, * etc. */ if (drawbuffer < 0 || drawbuffer >= (GLint)ctx->Const.MaxDrawBuffers) { return INVALID_MASK; } switch (ctx->DrawBuffer->ColorDrawBuffer[drawbuffer]) { case GL_FRONT: if (att[BUFFER_FRONT_LEFT].Renderbuffer) mask |= BUFFER_BIT_FRONT_LEFT; if (att[BUFFER_FRONT_RIGHT].Renderbuffer) mask |= BUFFER_BIT_FRONT_RIGHT; break; case GL_BACK: /* For GLES contexts with a single buffered configuration, we actually * only have a front renderbuffer, so any clear calls to GL_BACK should * affect that buffer. See draw_buffer_enum_to_bitmask for details. */ if (_mesa_is_gles(ctx)) if (!ctx->DrawBuffer->Visual.doubleBufferMode) if (att[BUFFER_FRONT_LEFT].Renderbuffer) mask |= BUFFER_BIT_FRONT_LEFT; if (att[BUFFER_BACK_LEFT].Renderbuffer) mask |= BUFFER_BIT_BACK_LEFT; if (att[BUFFER_BACK_RIGHT].Renderbuffer) mask |= BUFFER_BIT_BACK_RIGHT; break; case GL_LEFT: if (att[BUFFER_FRONT_LEFT].Renderbuffer) mask |= BUFFER_BIT_FRONT_LEFT; if (att[BUFFER_BACK_LEFT].Renderbuffer) mask |= BUFFER_BIT_BACK_LEFT; break; case GL_RIGHT: if (att[BUFFER_FRONT_RIGHT].Renderbuffer) mask |= BUFFER_BIT_FRONT_RIGHT; if (att[BUFFER_BACK_RIGHT].Renderbuffer) mask |= BUFFER_BIT_BACK_RIGHT; break; case GL_FRONT_AND_BACK: if (att[BUFFER_FRONT_LEFT].Renderbuffer) mask |= BUFFER_BIT_FRONT_LEFT; if (att[BUFFER_BACK_LEFT].Renderbuffer) mask |= BUFFER_BIT_BACK_LEFT; if (att[BUFFER_FRONT_RIGHT].Renderbuffer) mask |= BUFFER_BIT_FRONT_RIGHT; if (att[BUFFER_BACK_RIGHT].Renderbuffer) mask |= BUFFER_BIT_BACK_RIGHT; break; default: { GLint buf = ctx->DrawBuffer->_ColorDrawBufferIndexes[drawbuffer]; if (buf >= 0 && att[buf].Renderbuffer) { mask |= 1 << buf; } } } return mask; } /** * New in GL 3.0 * Clear signed integer color buffer or stencil buffer (not depth). */ void GLAPIENTRY _mesa_ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint *value) { GET_CURRENT_CONTEXT(ctx); FLUSH_VERTICES(ctx, 0); FLUSH_CURRENT(ctx, 0); if (ctx->NewState) { _mesa_update_state( ctx ); } switch (buffer) { case GL_STENCIL: /* Page 264 (page 280 of the PDF) of the OpenGL 3.0 spec says: * * "ClearBuffer generates an INVALID VALUE error if buffer is * COLOR and drawbuffer is less than zero, or greater than the * value of MAX DRAW BUFFERS minus one; or if buffer is DEPTH, * STENCIL, or DEPTH STENCIL and drawbuffer is not zero." */ if (drawbuffer != 0) { _mesa_error(ctx, GL_INVALID_VALUE, "glClearBufferiv(drawbuffer=%d)", drawbuffer); return; } else if (ctx->DrawBuffer->Attachment[BUFFER_STENCIL].Renderbuffer && !ctx->RasterDiscard) { /* Save current stencil clear value, set to 'value', do the * stencil clear and restore the clear value. * XXX in the future we may have a new ctx->Driver.ClearBuffer() * hook instead. */ const GLuint clearSave = ctx->Stencil.Clear; ctx->Stencil.Clear = *value; ctx->Driver.Clear(ctx, BUFFER_BIT_STENCIL); ctx->Stencil.Clear = clearSave; } break; case GL_COLOR: { const GLbitfield mask = make_color_buffer_mask(ctx, drawbuffer); if (mask == INVALID_MASK) { _mesa_error(ctx, GL_INVALID_VALUE, "glClearBufferiv(drawbuffer=%d)", drawbuffer); return; } else if (mask && !ctx->RasterDiscard) { union gl_color_union clearSave; /* save color */ clearSave = ctx->Color.ClearColor; /* set color */ COPY_4V(ctx->Color.ClearColor.i, value); /* clear buffer(s) */ ctx->Driver.Clear(ctx, mask); /* restore color */ ctx->Color.ClearColor = clearSave; } } break; default: /* Page 498 of the PDF, section '17.4.3.1 Clearing Individual Buffers' * of the OpenGL 4.5 spec states: * * "An INVALID_ENUM error is generated by ClearBufferiv and * ClearNamedFramebufferiv if buffer is not COLOR or STENCIL." */ _mesa_error(ctx, GL_INVALID_ENUM, "glClearBufferiv(buffer=%s)", _mesa_enum_to_string(buffer)); return; } } /** * The ClearBuffer framework is so complicated and so riddled with the * assumption that the framebuffer is bound that, for now, we will just fake * direct state access clearing for the user. */ void GLAPIENTRY _mesa_ClearNamedFramebufferiv(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value) { GLint oldfb; _mesa_GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldfb); _mesa_BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer); _mesa_ClearBufferiv(buffer, drawbuffer, value); _mesa_BindFramebuffer(GL_DRAW_FRAMEBUFFER, (GLuint) oldfb); } /** * New in GL 3.0 * Clear unsigned integer color buffer (not depth, not stencil). */ static ALWAYS_INLINE void clear_bufferuiv(struct gl_context *ctx, GLenum buffer, GLint drawbuffer, const GLuint *value, bool no_error) { FLUSH_VERTICES(ctx, 0); FLUSH_CURRENT(ctx, 0); if (ctx->NewState) { _mesa_update_state( ctx ); } switch (buffer) { case GL_COLOR: { const GLbitfield mask = make_color_buffer_mask(ctx, drawbuffer); if (!no_error && mask == INVALID_MASK) { _mesa_error(ctx, GL_INVALID_VALUE, "glClearBufferuiv(drawbuffer=%d)", drawbuffer); return; } else if (mask && !ctx->RasterDiscard) { union gl_color_union clearSave; /* save color */ clearSave = ctx->Color.ClearColor; /* set color */ COPY_4V(ctx->Color.ClearColor.ui, value); /* clear buffer(s) */ ctx->Driver.Clear(ctx, mask); /* restore color */ ctx->Color.ClearColor = clearSave; } } break; default: if (!no_error) { /* Page 498 of the PDF, section '17.4.3.1 Clearing Individual Buffers' * of the OpenGL 4.5 spec states: * * "An INVALID_ENUM error is generated by ClearBufferuiv and * ClearNamedFramebufferuiv if buffer is not COLOR." */ _mesa_error(ctx, GL_INVALID_ENUM, "glClearBufferuiv(buffer=%s)", _mesa_enum_to_string(buffer)); } return; } } void GLAPIENTRY _mesa_ClearBufferuiv_no_error(GLenum buffer, GLint drawbuffer, const GLuint *value) { GET_CURRENT_CONTEXT(ctx); clear_bufferuiv(ctx, buffer, drawbuffer, value, true); } void GLAPIENTRY _mesa_ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint *value) { GET_CURRENT_CONTEXT(ctx); clear_bufferuiv(ctx, buffer, drawbuffer, value, false); } /** * The ClearBuffer framework is so complicated and so riddled with the * assumption that the framebuffer is bound that, for now, we will just fake * direct state access clearing for the user. */ void GLAPIENTRY _mesa_ClearNamedFramebufferuiv(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value) { GLint oldfb; _mesa_GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldfb); _mesa_BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer); _mesa_ClearBufferuiv(buffer, drawbuffer, value); _mesa_BindFramebuffer(GL_DRAW_FRAMEBUFFER, (GLuint) oldfb); } /** * New in GL 3.0 * Clear fixed-pt or float color buffer or depth buffer (not stencil). */ static ALWAYS_INLINE void clear_bufferfv(struct gl_context *ctx, GLenum buffer, GLint drawbuffer, const GLfloat *value, bool no_error) { FLUSH_VERTICES(ctx, 0); FLUSH_CURRENT(ctx, 0); if (ctx->NewState) { _mesa_update_state( ctx ); } switch (buffer) { case GL_DEPTH: /* Page 264 (page 280 of the PDF) of the OpenGL 3.0 spec says: * * "ClearBuffer generates an INVALID VALUE error if buffer is * COLOR and drawbuffer is less than zero, or greater than the * value of MAX DRAW BUFFERS minus one; or if buffer is DEPTH, * STENCIL, or DEPTH STENCIL and drawbuffer is not zero." */ if (!no_error && drawbuffer != 0) { _mesa_error(ctx, GL_INVALID_VALUE, "glClearBufferfv(drawbuffer=%d)", drawbuffer); return; } else if (ctx->DrawBuffer->Attachment[BUFFER_DEPTH].Renderbuffer && !ctx->RasterDiscard) { /* Save current depth clear value, set to 'value', do the * depth clear and restore the clear value. * XXX in the future we may have a new ctx->Driver.ClearBuffer() * hook instead. */ const GLclampd clearSave = ctx->Depth.Clear; ctx->Depth.Clear = *value; ctx->Driver.Clear(ctx, BUFFER_BIT_DEPTH); ctx->Depth.Clear = clearSave; } /* clear depth buffer to value */ break; case GL_COLOR: { const GLbitfield mask = make_color_buffer_mask(ctx, drawbuffer); if (!no_error && mask == INVALID_MASK) { _mesa_error(ctx, GL_INVALID_VALUE, "glClearBufferfv(drawbuffer=%d)", drawbuffer); return; } else if (mask && !ctx->RasterDiscard) { union gl_color_union clearSave; /* save color */ clearSave = ctx->Color.ClearColor; /* set color */ COPY_4V(ctx->Color.ClearColor.f, value); /* clear buffer(s) */ ctx->Driver.Clear(ctx, mask); /* restore color */ ctx->Color.ClearColor = clearSave; } } break; default: if (!no_error) { /* Page 498 of the PDF, section '17.4.3.1 Clearing Individual Buffers' * of the OpenGL 4.5 spec states: * * "An INVALID_ENUM error is generated by ClearBufferfv and * ClearNamedFramebufferfv if buffer is not COLOR or DEPTH." */ _mesa_error(ctx, GL_INVALID_ENUM, "glClearBufferfv(buffer=%s)", _mesa_enum_to_string(buffer)); } return; } } void GLAPIENTRY _mesa_ClearBufferfv_no_error(GLenum buffer, GLint drawbuffer, const GLfloat *value) { GET_CURRENT_CONTEXT(ctx); clear_bufferfv(ctx, buffer, drawbuffer, value, true); } void GLAPIENTRY _mesa_ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat *value) { GET_CURRENT_CONTEXT(ctx); clear_bufferfv(ctx, buffer, drawbuffer, value, false); } /** * The ClearBuffer framework is so complicated and so riddled with the * assumption that the framebuffer is bound that, for now, we will just fake * direct state access clearing for the user. */ void GLAPIENTRY _mesa_ClearNamedFramebufferfv(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value) { GLint oldfb; _mesa_GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldfb); _mesa_BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer); _mesa_ClearBufferfv(buffer, drawbuffer, value); _mesa_BindFramebuffer(GL_DRAW_FRAMEBUFFER, (GLuint) oldfb); } /** * New in GL 3.0 * Clear depth/stencil buffer only. */ static ALWAYS_INLINE void clear_bufferfi(struct gl_context *ctx, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil, bool no_error) { GLbitfield mask = 0; FLUSH_VERTICES(ctx, 0); FLUSH_CURRENT(ctx, 0); if (!no_error) { if (buffer != GL_DEPTH_STENCIL) { _mesa_error(ctx, GL_INVALID_ENUM, "glClearBufferfi(buffer=%s)", _mesa_enum_to_string(buffer)); return; } /* Page 264 (page 280 of the PDF) of the OpenGL 3.0 spec says: * * "ClearBuffer generates an INVALID VALUE error if buffer is * COLOR and drawbuffer is less than zero, or greater than the * value of MAX DRAW BUFFERS minus one; or if buffer is DEPTH, * STENCIL, or DEPTH STENCIL and drawbuffer is not zero." */ if (drawbuffer != 0) { _mesa_error(ctx, GL_INVALID_VALUE, "glClearBufferfi(drawbuffer=%d)", drawbuffer); return; } } if (ctx->RasterDiscard) return; if (ctx->NewState) { _mesa_update_state( ctx ); } if (ctx->DrawBuffer->Attachment[BUFFER_DEPTH].Renderbuffer) mask |= BUFFER_BIT_DEPTH; if (ctx->DrawBuffer->Attachment[BUFFER_STENCIL].Renderbuffer) mask |= BUFFER_BIT_STENCIL; if (mask) { /* save current clear values */ const GLclampd clearDepthSave = ctx->Depth.Clear; const GLuint clearStencilSave = ctx->Stencil.Clear; /* set new clear values */ ctx->Depth.Clear = depth; ctx->Stencil.Clear = stencil; /* clear buffers */ ctx->Driver.Clear(ctx, mask); /* restore */ ctx->Depth.Clear = clearDepthSave; ctx->Stencil.Clear = clearStencilSave; } } void GLAPIENTRY _mesa_ClearBufferfi_no_error(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { GET_CURRENT_CONTEXT(ctx); clear_bufferfi(ctx, buffer, drawbuffer, depth, stencil, true); } void GLAPIENTRY _mesa_ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { GET_CURRENT_CONTEXT(ctx); clear_bufferfi(ctx, buffer, drawbuffer, depth, stencil, false); } /** * The ClearBuffer framework is so complicated and so riddled with the * assumption that the framebuffer is bound that, for now, we will just fake * direct state access clearing for the user. */ void GLAPIENTRY _mesa_ClearNamedFramebufferfi(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { GLint oldfb; _mesa_GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldfb); _mesa_BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer); _mesa_ClearBufferfi(buffer, drawbuffer, depth, stencil); _mesa_BindFramebuffer(GL_DRAW_FRAMEBUFFER, (GLuint) oldfb); }