From 51a59b7f7f9b134791d3b09673063e4c45ea9eee Mon Sep 17 00:00:00 2001 From: marha Date: Wed, 9 Feb 2011 07:47:31 +0000 Subject: libX11 mesa git update 9 Feb 2011 --- mesalib/src/mesa/main/blend.c | 1636 +++++++------- mesalib/src/mesa/main/bufferobj.c | 4303 +++++++++++++++++++------------------ mesalib/src/mesa/main/context.c | 61 +- mesalib/src/mesa/main/context.h | 639 +++--- mesalib/src/mesa/main/depth.c | 342 +-- mesalib/src/mesa/main/hint.c | 293 +-- mesalib/src/mesa/main/lines.c | 228 +- mesalib/src/mesa/main/mtypes.h | 17 +- mesalib/src/mesa/main/queryobj.c | 1208 ++++++----- mesalib/src/mesa/main/shaderapi.c | 11 + mesalib/src/mesa/main/stencil.c | 1204 ++++++----- 11 files changed, 4993 insertions(+), 4949 deletions(-) (limited to 'mesalib/src/mesa/main') diff --git a/mesalib/src/mesa/main/blend.c b/mesalib/src/mesa/main/blend.c index 30466cca3..c74a16803 100644 --- a/mesalib/src/mesa/main/blend.c +++ b/mesalib/src/mesa/main/blend.c @@ -1,814 +1,822 @@ -/** - * \file blend.c - * Blending operations. - */ - -/* - * Mesa 3-D graphics library - * Version: 6.5.1 - * - * Copyright (C) 1999-2006 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 - * BRIAN PAUL 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 "glheader.h" -#include "blend.h" -#include "context.h" -#include "enums.h" -#include "macros.h" -#include "mtypes.h" - - - -/** - * Check if given blend source factor is legal. - * \return GL_TRUE if legal, GL_FALSE otherwise. - */ -static GLboolean -legal_src_factor(const struct gl_context *ctx, GLenum factor) -{ - switch (factor) { - case GL_SRC_COLOR: - case GL_ONE_MINUS_SRC_COLOR: - return ctx->Extensions.NV_blend_square; - case GL_ZERO: - case GL_ONE: - case GL_DST_COLOR: - case GL_ONE_MINUS_DST_COLOR: - case GL_SRC_ALPHA: - case GL_ONE_MINUS_SRC_ALPHA: - case GL_DST_ALPHA: - case GL_ONE_MINUS_DST_ALPHA: - case GL_SRC_ALPHA_SATURATE: - case GL_CONSTANT_COLOR: - case GL_ONE_MINUS_CONSTANT_COLOR: - case GL_CONSTANT_ALPHA: - case GL_ONE_MINUS_CONSTANT_ALPHA: - return GL_TRUE; - default: - return GL_FALSE; - } -} - - -/** - * Check if given blend destination factor is legal. - * \return GL_TRUE if legal, GL_FALSE otherwise. - */ -static GLboolean -legal_dst_factor(const struct gl_context *ctx, GLenum factor) -{ - switch (factor) { - case GL_DST_COLOR: - case GL_ONE_MINUS_DST_COLOR: - return ctx->Extensions.NV_blend_square; - case GL_ZERO: - case GL_ONE: - case GL_SRC_COLOR: - case GL_ONE_MINUS_SRC_COLOR: - case GL_SRC_ALPHA: - case GL_ONE_MINUS_SRC_ALPHA: - case GL_DST_ALPHA: - case GL_ONE_MINUS_DST_ALPHA: - case GL_CONSTANT_COLOR: - case GL_ONE_MINUS_CONSTANT_COLOR: - case GL_CONSTANT_ALPHA: - case GL_ONE_MINUS_CONSTANT_ALPHA: - return GL_TRUE; - default: - return GL_FALSE; - } -} - - -/** - * Check if src/dest RGB/A blend factors are legal. If not generate - * a GL error. - * \return GL_TRUE if factors are legal, GL_FALSE otherwise. - */ -static GLboolean -validate_blend_factors(struct gl_context *ctx, const char *func, - GLenum sfactorRGB, GLenum dfactorRGB, - GLenum sfactorA, GLenum dfactorA) -{ - if (!legal_src_factor(ctx, sfactorRGB)) { - _mesa_error(ctx, GL_INVALID_ENUM, - "%s(sfactorRGB = %s)", func, - _mesa_lookup_enum_by_nr(sfactorRGB)); - return GL_FALSE; - } - - if (!legal_dst_factor(ctx, dfactorRGB)) { - _mesa_error(ctx, GL_INVALID_ENUM, - "%s(dfactorRGB = %s)", func, - _mesa_lookup_enum_by_nr(dfactorRGB)); - return GL_FALSE; - } - - if (sfactorA != sfactorRGB && !legal_src_factor(ctx, sfactorA)) { - _mesa_error(ctx, GL_INVALID_ENUM, - "%s(sfactorA = %s)", func, - _mesa_lookup_enum_by_nr(sfactorA)); - return GL_FALSE; - } - - if (dfactorA != dfactorRGB && !legal_dst_factor(ctx, dfactorA)) { - _mesa_error(ctx, GL_INVALID_ENUM, - "%s(dfactorA = %s)", func, - _mesa_lookup_enum_by_nr(dfactorA)); - return GL_FALSE; - } - - return GL_TRUE; -} - - -/** - * Specify the blending operation. - * - * \param sfactor source factor operator. - * \param dfactor destination factor operator. - * - * \sa glBlendFunc, glBlendFuncSeparateEXT - */ -void GLAPIENTRY -_mesa_BlendFunc( GLenum sfactor, GLenum dfactor ) -{ - _mesa_BlendFuncSeparateEXT(sfactor, dfactor, sfactor, dfactor); -} - - -/** - * Set the separate blend source/dest factors for all draw buffers. - * - * \param sfactorRGB RGB source factor operator. - * \param dfactorRGB RGB destination factor operator. - * \param sfactorA alpha source factor operator. - * \param dfactorA alpha destination factor operator. - */ -void GLAPIENTRY -_mesa_BlendFuncSeparateEXT( GLenum sfactorRGB, GLenum dfactorRGB, - GLenum sfactorA, GLenum dfactorA ) -{ - GLuint buf, numBuffers; - GLboolean changed; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, "glBlendFuncSeparate %s %s %s %s\n", - _mesa_lookup_enum_by_nr(sfactorRGB), - _mesa_lookup_enum_by_nr(dfactorRGB), - _mesa_lookup_enum_by_nr(sfactorA), - _mesa_lookup_enum_by_nr(dfactorA)); - - if (!validate_blend_factors(ctx, "glBlendFuncSeparate", - sfactorRGB, dfactorRGB, - sfactorA, dfactorA)) { - return; - } - - numBuffers = ctx->Extensions.ARB_draw_buffers_blend - ? ctx->Const.MaxDrawBuffers : 1; - - changed = GL_FALSE; - for (buf = 0; buf < numBuffers; buf++) { - if (ctx->Color.Blend[buf].SrcRGB != sfactorRGB || - ctx->Color.Blend[buf].DstRGB != dfactorRGB || - ctx->Color.Blend[buf].SrcA != sfactorA || - ctx->Color.Blend[buf].DstA != dfactorA) { - changed = GL_TRUE; - break; - } - } - if (!changed) - return; - - FLUSH_VERTICES(ctx, _NEW_COLOR); - - for (buf = 0; buf < numBuffers; buf++) { - ctx->Color.Blend[buf].SrcRGB = sfactorRGB; - ctx->Color.Blend[buf].DstRGB = dfactorRGB; - ctx->Color.Blend[buf].SrcA = sfactorA; - ctx->Color.Blend[buf].DstA = dfactorA; - } - ctx->Color._BlendFuncPerBuffer = GL_FALSE; - - if (ctx->Driver.BlendFuncSeparate) { - ctx->Driver.BlendFuncSeparate(ctx, sfactorRGB, dfactorRGB, - sfactorA, dfactorA); - } -} - - -#if _HAVE_FULL_GL - - -/** - * Set blend source/dest factors for one color buffer/target. - */ -void GLAPIENTRY -_mesa_BlendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) -{ - _mesa_BlendFuncSeparatei(buf, sfactor, dfactor, sfactor, dfactor); -} - - -/** - * Set separate blend source/dest factors for one color buffer/target. - */ -void GLAPIENTRY -_mesa_BlendFuncSeparatei(GLuint buf, GLenum sfactorRGB, GLenum dfactorRGB, - GLenum sfactorA, GLenum dfactorA) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (!ctx->Extensions.ARB_draw_buffers_blend) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glBlendFunc[Separate]i()"); - return; - } - - if (buf >= ctx->Const.MaxDrawBuffers) { - _mesa_error(ctx, GL_INVALID_VALUE, "glBlendFuncSeparatei(buffer=%u)", - buf); - return; - } - - if (!validate_blend_factors(ctx, "glBlendFuncSeparatei", - sfactorRGB, dfactorRGB, - sfactorA, dfactorA)) { - return; - } - - if (ctx->Color.Blend[buf].SrcRGB == sfactorRGB && - ctx->Color.Blend[buf].DstRGB == dfactorRGB && - ctx->Color.Blend[buf].SrcA == sfactorA && - ctx->Color.Blend[buf].DstA == dfactorA) - return; /* no change */ - - FLUSH_VERTICES(ctx, _NEW_COLOR); - - ctx->Color.Blend[buf].SrcRGB = sfactorRGB; - ctx->Color.Blend[buf].DstRGB = dfactorRGB; - ctx->Color.Blend[buf].SrcA = sfactorA; - ctx->Color.Blend[buf].DstA = dfactorA; - ctx->Color._BlendFuncPerBuffer = GL_TRUE; - - if (ctx->Driver.BlendFuncSeparatei) { - ctx->Driver.BlendFuncSeparatei(ctx, buf, sfactorRGB, dfactorRGB, - sfactorA, dfactorA); - } -} - - -/** - * Check if given blend equation is legal. - * \return GL_TRUE if legal, GL_FALSE otherwise. - */ -static GLboolean -legal_blend_equation(const struct gl_context *ctx, - GLenum mode, GLboolean is_separate) -{ - switch (mode) { - case GL_FUNC_ADD: - return GL_TRUE; - case GL_MIN: - case GL_MAX: - return ctx->Extensions.EXT_blend_minmax; - case GL_LOGIC_OP: - /* glBlendEquationSeparate cannot take GL_LOGIC_OP as a parameter. - */ - return ctx->Extensions.EXT_blend_logic_op && !is_separate; - case GL_FUNC_SUBTRACT: - case GL_FUNC_REVERSE_SUBTRACT: - return ctx->Extensions.EXT_blend_subtract; - default: - return GL_FALSE; - } -} - - -/* This is really an extension function! */ -void GLAPIENTRY -_mesa_BlendEquation( GLenum mode ) -{ - GLuint buf, numBuffers; - GLboolean changed; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, "glBlendEquation %s\n", - _mesa_lookup_enum_by_nr(mode)); - - if (!legal_blend_equation(ctx, mode, GL_FALSE)) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBlendEquation"); - return; - } - - numBuffers = ctx->Extensions.ARB_draw_buffers_blend - ? ctx->Const.MaxDrawBuffers : 1; - - changed = GL_FALSE; - for (buf = 0; buf < numBuffers; buf++) { - if (ctx->Color.Blend[buf].EquationRGB != mode || - ctx->Color.Blend[buf].EquationA != mode) { - changed = GL_TRUE; - break; - } - } - if (!changed) - return; - - FLUSH_VERTICES(ctx, _NEW_COLOR); - for (buf = 0; buf < numBuffers; buf++) { - ctx->Color.Blend[buf].EquationRGB = mode; - ctx->Color.Blend[buf].EquationA = mode; - } - ctx->Color._BlendEquationPerBuffer = GL_FALSE; - - if (ctx->Driver.BlendEquationSeparate) - (*ctx->Driver.BlendEquationSeparate)( ctx, mode, mode ); -} - - -/** - * Set blend equation for one color buffer/target. - */ -void GLAPIENTRY -_mesa_BlendEquationi(GLuint buf, GLenum mode) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, "glBlendEquationi(%u, %s)\n", - buf, _mesa_lookup_enum_by_nr(mode)); - - if (buf >= ctx->Const.MaxDrawBuffers) { - _mesa_error(ctx, GL_INVALID_VALUE, "glBlendFuncSeparatei(buffer=%u)", - buf); - return; - } - - if (!legal_blend_equation(ctx, mode, GL_FALSE)) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBlendEquationi"); - return; - } - - if (ctx->Color.Blend[buf].EquationRGB == mode && - ctx->Color.Blend[buf].EquationA == mode) - return; /* no change */ - - FLUSH_VERTICES(ctx, _NEW_COLOR); - ctx->Color.Blend[buf].EquationRGB = mode; - ctx->Color.Blend[buf].EquationA = mode; - ctx->Color._BlendEquationPerBuffer = GL_TRUE; - - if (ctx->Driver.BlendEquationSeparatei) - ctx->Driver.BlendEquationSeparatei(ctx, buf, mode, mode); -} - - -void GLAPIENTRY -_mesa_BlendEquationSeparateEXT( GLenum modeRGB, GLenum modeA ) -{ - GLuint buf, numBuffers; - GLboolean changed; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, "glBlendEquationSeparateEXT %s %s\n", - _mesa_lookup_enum_by_nr(modeRGB), - _mesa_lookup_enum_by_nr(modeA)); - - if ( (modeRGB != modeA) && !ctx->Extensions.EXT_blend_equation_separate ) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlendEquationSeparateEXT not supported by driver"); - return; - } - - if (!legal_blend_equation(ctx, modeRGB, GL_TRUE)) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBlendEquationSeparateEXT(modeRGB)"); - return; - } - - if (!legal_blend_equation(ctx, modeA, GL_TRUE)) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBlendEquationSeparateEXT(modeA)"); - return; - } - - numBuffers = ctx->Extensions.ARB_draw_buffers_blend - ? ctx->Const.MaxDrawBuffers : 1; - - changed = GL_FALSE; - for (buf = 0; buf < numBuffers; buf++) { - if (ctx->Color.Blend[buf].EquationRGB != modeRGB || - ctx->Color.Blend[buf].EquationA != modeA) { - changed = GL_TRUE; - break; - } - } - if (!changed) - return; - - FLUSH_VERTICES(ctx, _NEW_COLOR); - for (buf = 0; buf < numBuffers; buf++) { - ctx->Color.Blend[buf].EquationRGB = modeRGB; - ctx->Color.Blend[buf].EquationA = modeA; - } - ctx->Color._BlendEquationPerBuffer = GL_FALSE; - - if (ctx->Driver.BlendEquationSeparate) - ctx->Driver.BlendEquationSeparate(ctx, modeRGB, modeA); -} - - -/** - * Set separate blend equations for one color buffer/target. - */ -void -_mesa_BlendEquationSeparatei(GLuint buf, GLenum modeRGB, GLenum modeA) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, "glBlendEquationSeparatei %u, %s %s\n", buf, - _mesa_lookup_enum_by_nr(modeRGB), - _mesa_lookup_enum_by_nr(modeA)); - - if (buf >= ctx->Const.MaxDrawBuffers) { - _mesa_error(ctx, GL_INVALID_VALUE, "glBlendEquationSeparatei(buffer=%u)", - buf); - return; - } - - if (!legal_blend_equation(ctx, modeRGB, GL_TRUE)) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBlendEquationSeparatei(modeRGB)"); - return; - } - - if (!legal_blend_equation(ctx, modeA, GL_TRUE)) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBlendEquationSeparatei(modeA)"); - return; - } - - if (ctx->Color.Blend[buf].EquationRGB == modeRGB && - ctx->Color.Blend[buf].EquationA == modeA) - return; /* no change */ - - FLUSH_VERTICES(ctx, _NEW_COLOR); - ctx->Color.Blend[buf].EquationRGB = modeRGB; - ctx->Color.Blend[buf].EquationA = modeA; - ctx->Color._BlendEquationPerBuffer = GL_TRUE; - - if (ctx->Driver.BlendEquationSeparatei) - ctx->Driver.BlendEquationSeparatei(ctx, buf, modeRGB, modeA); -} - - - -#endif /* _HAVE_FULL_GL */ - - -/** - * Set the blending color. - * - * \param red red color component. - * \param green green color component. - * \param blue blue color component. - * \param alpha alpha color component. - * - * \sa glBlendColor(). - * - * Clamps the parameters and updates gl_colorbuffer_attrib::BlendColor. On a - * change, flushes the vertices and notifies the driver via - * dd_function_table::BlendColor callback. - */ -void GLAPIENTRY -_mesa_BlendColor( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha ) -{ - GLfloat tmp[4]; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - tmp[0] = CLAMP( red, 0.0F, 1.0F ); - tmp[1] = CLAMP( green, 0.0F, 1.0F ); - tmp[2] = CLAMP( blue, 0.0F, 1.0F ); - tmp[3] = CLAMP( alpha, 0.0F, 1.0F ); - - if (TEST_EQ_4V(tmp, ctx->Color.BlendColor)) - return; - - FLUSH_VERTICES(ctx, _NEW_COLOR); - COPY_4FV( ctx->Color.BlendColor, tmp ); - - if (ctx->Driver.BlendColor) - (*ctx->Driver.BlendColor)(ctx, tmp); -} - - -/** - * Specify the alpha test function. - * - * \param func alpha comparison function. - * \param ref reference value. - * - * Verifies the parameters and updates gl_colorbuffer_attrib. - * On a change, flushes the vertices and notifies the driver via - * dd_function_table::AlphaFunc callback. - */ -void GLAPIENTRY -_mesa_AlphaFunc( GLenum func, GLclampf ref ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - switch (func) { - case GL_NEVER: - case GL_LESS: - case GL_EQUAL: - case GL_LEQUAL: - case GL_GREATER: - case GL_NOTEQUAL: - case GL_GEQUAL: - case GL_ALWAYS: - ref = CLAMP(ref, 0.0F, 1.0F); - - if (ctx->Color.AlphaFunc == func && ctx->Color.AlphaRef == ref) - return; /* no change */ - - FLUSH_VERTICES(ctx, _NEW_COLOR); - ctx->Color.AlphaFunc = func; - ctx->Color.AlphaRef = ref; - - if (ctx->Driver.AlphaFunc) - ctx->Driver.AlphaFunc(ctx, func, ref); - return; - - default: - _mesa_error( ctx, GL_INVALID_ENUM, "glAlphaFunc(func)" ); - return; - } -} - - -/** - * Specify a logic pixel operation for color index rendering. - * - * \param opcode operation. - * - * Verifies that \p opcode is a valid enum and updates -gl_colorbuffer_attrib::LogicOp. - * On a change, flushes the vertices and notifies the driver via the - * dd_function_table::LogicOpcode callback. - */ -void GLAPIENTRY -_mesa_LogicOp( GLenum opcode ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - switch (opcode) { - case GL_CLEAR: - case GL_SET: - case GL_COPY: - case GL_COPY_INVERTED: - case GL_NOOP: - case GL_INVERT: - case GL_AND: - case GL_NAND: - case GL_OR: - case GL_NOR: - case GL_XOR: - case GL_EQUIV: - case GL_AND_REVERSE: - case GL_AND_INVERTED: - case GL_OR_REVERSE: - case GL_OR_INVERTED: - break; - default: - _mesa_error( ctx, GL_INVALID_ENUM, "glLogicOp" ); - return; - } - - if (ctx->Color.LogicOp == opcode) - return; - - FLUSH_VERTICES(ctx, _NEW_COLOR); - ctx->Color.LogicOp = opcode; - - if (ctx->Driver.LogicOpcode) - ctx->Driver.LogicOpcode( ctx, opcode ); -} - -#if _HAVE_FULL_GL -void GLAPIENTRY -_mesa_IndexMask( GLuint mask ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (ctx->Color.IndexMask == mask) - return; - - FLUSH_VERTICES(ctx, _NEW_COLOR); - ctx->Color.IndexMask = mask; -} -#endif - - -/** - * Enable or disable writing of frame buffer color components. - * - * \param red whether to mask writing of the red color component. - * \param green whether to mask writing of the green color component. - * \param blue whether to mask writing of the blue color component. - * \param alpha whether to mask writing of the alpha color component. - * - * \sa glColorMask(). - * - * Sets the appropriate value of gl_colorbuffer_attrib::ColorMask. On a - * change, flushes the vertices and notifies the driver via the - * dd_function_table::ColorMask callback. - */ -void GLAPIENTRY -_mesa_ColorMask( GLboolean red, GLboolean green, - GLboolean blue, GLboolean alpha ) -{ - GET_CURRENT_CONTEXT(ctx); - GLubyte tmp[4]; - GLuint i; - GLboolean flushed; - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, "glColorMask %d %d %d %d\n", red, green, blue, alpha); - - /* Shouldn't have any information about channel depth in core mesa - * -- should probably store these as the native booleans: - */ - tmp[RCOMP] = red ? 0xff : 0x0; - tmp[GCOMP] = green ? 0xff : 0x0; - tmp[BCOMP] = blue ? 0xff : 0x0; - tmp[ACOMP] = alpha ? 0xff : 0x0; - - flushed = GL_FALSE; - for (i = 0; i < ctx->Const.MaxDrawBuffers; i++) { - if (!TEST_EQ_4V(tmp, ctx->Color.ColorMask[i])) { - if (!flushed) { - FLUSH_VERTICES(ctx, _NEW_COLOR); - } - flushed = GL_TRUE; - COPY_4UBV(ctx->Color.ColorMask[i], tmp); - } - } - - if (ctx->Driver.ColorMask) - ctx->Driver.ColorMask( ctx, red, green, blue, alpha ); -} - - -/** - * For GL_EXT_draw_buffers2 and GL3 - */ -void GLAPIENTRY -_mesa_ColorMaskIndexed( GLuint buf, GLboolean red, GLboolean green, - GLboolean blue, GLboolean alpha ) -{ - GLubyte tmp[4]; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, "glColorMaskIndexed %u %d %d %d %d\n", - buf, red, green, blue, alpha); - - if (buf >= ctx->Const.MaxDrawBuffers) { - _mesa_error(ctx, GL_INVALID_VALUE, "glColorMaskIndexed(buf=%u)", buf); - return; - } - - /* Shouldn't have any information about channel depth in core mesa - * -- should probably store these as the native booleans: - */ - tmp[RCOMP] = red ? 0xff : 0x0; - tmp[GCOMP] = green ? 0xff : 0x0; - tmp[BCOMP] = blue ? 0xff : 0x0; - tmp[ACOMP] = alpha ? 0xff : 0x0; - - if (TEST_EQ_4V(tmp, ctx->Color.ColorMask[buf])) - return; - - FLUSH_VERTICES(ctx, _NEW_COLOR); - COPY_4UBV(ctx->Color.ColorMask[buf], tmp); - - if (ctx->Driver.ColorMaskIndexed) - ctx->Driver.ColorMaskIndexed(ctx, buf, red, green, blue, alpha); -} - - -extern void GLAPIENTRY -_mesa_ClampColorARB(GLenum target, GLenum clamp) -{ - GET_CURRENT_CONTEXT(ctx); - - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (clamp != GL_TRUE && clamp != GL_FALSE && clamp != GL_FIXED_ONLY_ARB) { - _mesa_error(ctx, GL_INVALID_ENUM, "glClampColorARB(clamp)"); - return; - } - - switch (target) { - case GL_CLAMP_VERTEX_COLOR_ARB: - ctx->Light.ClampVertexColor = clamp; - break; - case GL_CLAMP_FRAGMENT_COLOR_ARB: - ctx->Color.ClampFragmentColor = clamp; - break; - case GL_CLAMP_READ_COLOR_ARB: - ctx->Color.ClampReadColor = clamp; - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, "glClampColorARB(target)"); - return; - } -} - - - - -/**********************************************************************/ -/** \name Initialization */ -/*@{*/ - -/** - * Initialization of the context's Color attribute group. - * - * \param ctx GL context. - * - * Initializes the related fields in the context color attribute group, - * __struct gl_contextRec::Color. - */ -void _mesa_init_color( struct gl_context * ctx ) -{ - GLuint i; - - /* Color buffer group */ - ctx->Color.IndexMask = ~0u; - memset(ctx->Color.ColorMask, 0xff, sizeof(ctx->Color.ColorMask)); - ctx->Color.ClearIndex = 0; - ASSIGN_4V( ctx->Color.ClearColor, 0, 0, 0, 0 ); - ctx->Color.AlphaEnabled = GL_FALSE; - ctx->Color.AlphaFunc = GL_ALWAYS; - ctx->Color.AlphaRef = 0; - ctx->Color.BlendEnabled = 0x0; - for (i = 0; i < Elements(ctx->Color.Blend); i++) { - ctx->Color.Blend[i].SrcRGB = GL_ONE; - ctx->Color.Blend[i].DstRGB = GL_ZERO; - ctx->Color.Blend[i].SrcA = GL_ONE; - ctx->Color.Blend[i].DstA = GL_ZERO; - ctx->Color.Blend[i].EquationRGB = GL_FUNC_ADD; - ctx->Color.Blend[i].EquationA = GL_FUNC_ADD; - } - ASSIGN_4V( ctx->Color.BlendColor, 0.0, 0.0, 0.0, 0.0 ); - ctx->Color.IndexLogicOpEnabled = GL_FALSE; - ctx->Color.ColorLogicOpEnabled = GL_FALSE; - ctx->Color._LogicOpEnabled = GL_FALSE; - ctx->Color.LogicOp = GL_COPY; - ctx->Color.DitherFlag = GL_TRUE; - - if (ctx->Visual.doubleBufferMode) { - ctx->Color.DrawBuffer[0] = GL_BACK; - } - else { - ctx->Color.DrawBuffer[0] = GL_FRONT; - } - - ctx->Color.ClampFragmentColor = GL_FIXED_ONLY_ARB; - ctx->Color.ClampReadColor = GL_FIXED_ONLY_ARB; -} - -/*@}*/ +/** + * \file blend.c + * Blending operations. + */ + +/* + * Mesa 3-D graphics library + * Version: 6.5.1 + * + * Copyright (C) 1999-2006 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 + * BRIAN PAUL 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 "glheader.h" +#include "blend.h" +#include "context.h" +#include "enums.h" +#include "macros.h" +#include "mtypes.h" + + + +/** + * Check if given blend source factor is legal. + * \return GL_TRUE if legal, GL_FALSE otherwise. + */ +static GLboolean +legal_src_factor(const struct gl_context *ctx, GLenum factor) +{ + switch (factor) { + case GL_SRC_COLOR: + case GL_ONE_MINUS_SRC_COLOR: + return ctx->Extensions.NV_blend_square; + case GL_ZERO: + case GL_ONE: + case GL_DST_COLOR: + case GL_ONE_MINUS_DST_COLOR: + case GL_SRC_ALPHA: + case GL_ONE_MINUS_SRC_ALPHA: + case GL_DST_ALPHA: + case GL_ONE_MINUS_DST_ALPHA: + case GL_SRC_ALPHA_SATURATE: + case GL_CONSTANT_COLOR: + case GL_ONE_MINUS_CONSTANT_COLOR: + case GL_CONSTANT_ALPHA: + case GL_ONE_MINUS_CONSTANT_ALPHA: + return GL_TRUE; + default: + return GL_FALSE; + } +} + + +/** + * Check if given blend destination factor is legal. + * \return GL_TRUE if legal, GL_FALSE otherwise. + */ +static GLboolean +legal_dst_factor(const struct gl_context *ctx, GLenum factor) +{ + switch (factor) { + case GL_DST_COLOR: + case GL_ONE_MINUS_DST_COLOR: + return ctx->Extensions.NV_blend_square; + case GL_ZERO: + case GL_ONE: + case GL_SRC_COLOR: + case GL_ONE_MINUS_SRC_COLOR: + case GL_SRC_ALPHA: + case GL_ONE_MINUS_SRC_ALPHA: + case GL_DST_ALPHA: + case GL_ONE_MINUS_DST_ALPHA: + case GL_CONSTANT_COLOR: + case GL_ONE_MINUS_CONSTANT_COLOR: + case GL_CONSTANT_ALPHA: + case GL_ONE_MINUS_CONSTANT_ALPHA: + return GL_TRUE; + default: + return GL_FALSE; + } +} + + +/** + * Check if src/dest RGB/A blend factors are legal. If not generate + * a GL error. + * \return GL_TRUE if factors are legal, GL_FALSE otherwise. + */ +static GLboolean +validate_blend_factors(struct gl_context *ctx, const char *func, + GLenum sfactorRGB, GLenum dfactorRGB, + GLenum sfactorA, GLenum dfactorA) +{ + if (!legal_src_factor(ctx, sfactorRGB)) { + _mesa_error(ctx, GL_INVALID_ENUM, + "%s(sfactorRGB = %s)", func, + _mesa_lookup_enum_by_nr(sfactorRGB)); + return GL_FALSE; + } + + if (!legal_dst_factor(ctx, dfactorRGB)) { + _mesa_error(ctx, GL_INVALID_ENUM, + "%s(dfactorRGB = %s)", func, + _mesa_lookup_enum_by_nr(dfactorRGB)); + return GL_FALSE; + } + + if (sfactorA != sfactorRGB && !legal_src_factor(ctx, sfactorA)) { + _mesa_error(ctx, GL_INVALID_ENUM, + "%s(sfactorA = %s)", func, + _mesa_lookup_enum_by_nr(sfactorA)); + return GL_FALSE; + } + + if (dfactorA != dfactorRGB && !legal_dst_factor(ctx, dfactorA)) { + _mesa_error(ctx, GL_INVALID_ENUM, + "%s(dfactorA = %s)", func, + _mesa_lookup_enum_by_nr(dfactorA)); + return GL_FALSE; + } + + return GL_TRUE; +} + + +/** + * Specify the blending operation. + * + * \param sfactor source factor operator. + * \param dfactor destination factor operator. + * + * \sa glBlendFunc, glBlendFuncSeparateEXT + */ +void GLAPIENTRY +_mesa_BlendFunc( GLenum sfactor, GLenum dfactor ) +{ + _mesa_BlendFuncSeparateEXT(sfactor, dfactor, sfactor, dfactor); +} + + +/** + * Set the separate blend source/dest factors for all draw buffers. + * + * \param sfactorRGB RGB source factor operator. + * \param dfactorRGB RGB destination factor operator. + * \param sfactorA alpha source factor operator. + * \param dfactorA alpha destination factor operator. + */ +void GLAPIENTRY +_mesa_BlendFuncSeparateEXT( GLenum sfactorRGB, GLenum dfactorRGB, + GLenum sfactorA, GLenum dfactorA ) +{ + GLuint buf, numBuffers; + GLboolean changed; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glBlendFuncSeparate %s %s %s %s\n", + _mesa_lookup_enum_by_nr(sfactorRGB), + _mesa_lookup_enum_by_nr(dfactorRGB), + _mesa_lookup_enum_by_nr(sfactorA), + _mesa_lookup_enum_by_nr(dfactorA)); + + if (!validate_blend_factors(ctx, "glBlendFuncSeparate", + sfactorRGB, dfactorRGB, + sfactorA, dfactorA)) { + return; + } + + numBuffers = ctx->Extensions.ARB_draw_buffers_blend + ? ctx->Const.MaxDrawBuffers : 1; + + changed = GL_FALSE; + for (buf = 0; buf < numBuffers; buf++) { + if (ctx->Color.Blend[buf].SrcRGB != sfactorRGB || + ctx->Color.Blend[buf].DstRGB != dfactorRGB || + ctx->Color.Blend[buf].SrcA != sfactorA || + ctx->Color.Blend[buf].DstA != dfactorA) { + changed = GL_TRUE; + break; + } + } + if (!changed) + return; + + FLUSH_VERTICES(ctx, _NEW_COLOR); + + for (buf = 0; buf < numBuffers; buf++) { + ctx->Color.Blend[buf].SrcRGB = sfactorRGB; + ctx->Color.Blend[buf].DstRGB = dfactorRGB; + ctx->Color.Blend[buf].SrcA = sfactorA; + ctx->Color.Blend[buf].DstA = dfactorA; + } + ctx->Color._BlendFuncPerBuffer = GL_FALSE; + + if (ctx->Driver.BlendFuncSeparate) { + ctx->Driver.BlendFuncSeparate(ctx, sfactorRGB, dfactorRGB, + sfactorA, dfactorA); + } +} + + +#if _HAVE_FULL_GL + + +/** + * Set blend source/dest factors for one color buffer/target. + */ +void GLAPIENTRY +_mesa_BlendFunci(GLuint buf, GLenum sfactor, GLenum dfactor) +{ + _mesa_BlendFuncSeparatei(buf, sfactor, dfactor, sfactor, dfactor); +} + + +/** + * Set separate blend source/dest factors for one color buffer/target. + */ +void GLAPIENTRY +_mesa_BlendFuncSeparatei(GLuint buf, GLenum sfactorRGB, GLenum dfactorRGB, + GLenum sfactorA, GLenum dfactorA) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (!ctx->Extensions.ARB_draw_buffers_blend) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glBlendFunc[Separate]i()"); + return; + } + + if (buf >= ctx->Const.MaxDrawBuffers) { + _mesa_error(ctx, GL_INVALID_VALUE, "glBlendFuncSeparatei(buffer=%u)", + buf); + return; + } + + if (!validate_blend_factors(ctx, "glBlendFuncSeparatei", + sfactorRGB, dfactorRGB, + sfactorA, dfactorA)) { + return; + } + + if (ctx->Color.Blend[buf].SrcRGB == sfactorRGB && + ctx->Color.Blend[buf].DstRGB == dfactorRGB && + ctx->Color.Blend[buf].SrcA == sfactorA && + ctx->Color.Blend[buf].DstA == dfactorA) + return; /* no change */ + + FLUSH_VERTICES(ctx, _NEW_COLOR); + + ctx->Color.Blend[buf].SrcRGB = sfactorRGB; + ctx->Color.Blend[buf].DstRGB = dfactorRGB; + ctx->Color.Blend[buf].SrcA = sfactorA; + ctx->Color.Blend[buf].DstA = dfactorA; + ctx->Color._BlendFuncPerBuffer = GL_TRUE; + + if (ctx->Driver.BlendFuncSeparatei) { + ctx->Driver.BlendFuncSeparatei(ctx, buf, sfactorRGB, dfactorRGB, + sfactorA, dfactorA); + } +} + + +/** + * Check if given blend equation is legal. + * \return GL_TRUE if legal, GL_FALSE otherwise. + */ +static GLboolean +legal_blend_equation(const struct gl_context *ctx, + GLenum mode, GLboolean is_separate) +{ + switch (mode) { + case GL_FUNC_ADD: + return GL_TRUE; + case GL_MIN: + case GL_MAX: + return ctx->Extensions.EXT_blend_minmax; + case GL_LOGIC_OP: + /* glBlendEquationSeparate cannot take GL_LOGIC_OP as a parameter. + */ + return ctx->Extensions.EXT_blend_logic_op && !is_separate; + case GL_FUNC_SUBTRACT: + case GL_FUNC_REVERSE_SUBTRACT: + return ctx->Extensions.EXT_blend_subtract; + default: + return GL_FALSE; + } +} + + +/* This is really an extension function! */ +void GLAPIENTRY +_mesa_BlendEquation( GLenum mode ) +{ + GLuint buf, numBuffers; + GLboolean changed; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glBlendEquation(%s)\n", + _mesa_lookup_enum_by_nr(mode)); + + if (!legal_blend_equation(ctx, mode, GL_FALSE)) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBlendEquation"); + return; + } + + numBuffers = ctx->Extensions.ARB_draw_buffers_blend + ? ctx->Const.MaxDrawBuffers : 1; + + changed = GL_FALSE; + for (buf = 0; buf < numBuffers; buf++) { + if (ctx->Color.Blend[buf].EquationRGB != mode || + ctx->Color.Blend[buf].EquationA != mode) { + changed = GL_TRUE; + break; + } + } + if (!changed) + return; + + FLUSH_VERTICES(ctx, _NEW_COLOR); + for (buf = 0; buf < numBuffers; buf++) { + ctx->Color.Blend[buf].EquationRGB = mode; + ctx->Color.Blend[buf].EquationA = mode; + } + ctx->Color._BlendEquationPerBuffer = GL_FALSE; + + if (ctx->Driver.BlendEquationSeparate) + (*ctx->Driver.BlendEquationSeparate)( ctx, mode, mode ); +} + + +/** + * Set blend equation for one color buffer/target. + */ +void GLAPIENTRY +_mesa_BlendEquationi(GLuint buf, GLenum mode) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glBlendEquationi(%u, %s)\n", + buf, _mesa_lookup_enum_by_nr(mode)); + + if (buf >= ctx->Const.MaxDrawBuffers) { + _mesa_error(ctx, GL_INVALID_VALUE, "glBlendFuncSeparatei(buffer=%u)", + buf); + return; + } + + if (!legal_blend_equation(ctx, mode, GL_FALSE)) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBlendEquationi"); + return; + } + + if (ctx->Color.Blend[buf].EquationRGB == mode && + ctx->Color.Blend[buf].EquationA == mode) + return; /* no change */ + + FLUSH_VERTICES(ctx, _NEW_COLOR); + ctx->Color.Blend[buf].EquationRGB = mode; + ctx->Color.Blend[buf].EquationA = mode; + ctx->Color._BlendEquationPerBuffer = GL_TRUE; + + if (ctx->Driver.BlendEquationSeparatei) + ctx->Driver.BlendEquationSeparatei(ctx, buf, mode, mode); +} + + +void GLAPIENTRY +_mesa_BlendEquationSeparateEXT( GLenum modeRGB, GLenum modeA ) +{ + GLuint buf, numBuffers; + GLboolean changed; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glBlendEquationSeparateEXT(%s %s)\n", + _mesa_lookup_enum_by_nr(modeRGB), + _mesa_lookup_enum_by_nr(modeA)); + + if ( (modeRGB != modeA) && !ctx->Extensions.EXT_blend_equation_separate ) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBlendEquationSeparateEXT not supported by driver"); + return; + } + + if (!legal_blend_equation(ctx, modeRGB, GL_TRUE)) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBlendEquationSeparateEXT(modeRGB)"); + return; + } + + if (!legal_blend_equation(ctx, modeA, GL_TRUE)) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBlendEquationSeparateEXT(modeA)"); + return; + } + + numBuffers = ctx->Extensions.ARB_draw_buffers_blend + ? ctx->Const.MaxDrawBuffers : 1; + + changed = GL_FALSE; + for (buf = 0; buf < numBuffers; buf++) { + if (ctx->Color.Blend[buf].EquationRGB != modeRGB || + ctx->Color.Blend[buf].EquationA != modeA) { + changed = GL_TRUE; + break; + } + } + if (!changed) + return; + + FLUSH_VERTICES(ctx, _NEW_COLOR); + for (buf = 0; buf < numBuffers; buf++) { + ctx->Color.Blend[buf].EquationRGB = modeRGB; + ctx->Color.Blend[buf].EquationA = modeA; + } + ctx->Color._BlendEquationPerBuffer = GL_FALSE; + + if (ctx->Driver.BlendEquationSeparate) + ctx->Driver.BlendEquationSeparate(ctx, modeRGB, modeA); +} + + +/** + * Set separate blend equations for one color buffer/target. + */ +void +_mesa_BlendEquationSeparatei(GLuint buf, GLenum modeRGB, GLenum modeA) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glBlendEquationSeparatei(%u, %s %s)\n", buf, + _mesa_lookup_enum_by_nr(modeRGB), + _mesa_lookup_enum_by_nr(modeA)); + + if (buf >= ctx->Const.MaxDrawBuffers) { + _mesa_error(ctx, GL_INVALID_VALUE, "glBlendEquationSeparatei(buffer=%u)", + buf); + return; + } + + if (!legal_blend_equation(ctx, modeRGB, GL_TRUE)) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBlendEquationSeparatei(modeRGB)"); + return; + } + + if (!legal_blend_equation(ctx, modeA, GL_TRUE)) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBlendEquationSeparatei(modeA)"); + return; + } + + if (ctx->Color.Blend[buf].EquationRGB == modeRGB && + ctx->Color.Blend[buf].EquationA == modeA) + return; /* no change */ + + FLUSH_VERTICES(ctx, _NEW_COLOR); + ctx->Color.Blend[buf].EquationRGB = modeRGB; + ctx->Color.Blend[buf].EquationA = modeA; + ctx->Color._BlendEquationPerBuffer = GL_TRUE; + + if (ctx->Driver.BlendEquationSeparatei) + ctx->Driver.BlendEquationSeparatei(ctx, buf, modeRGB, modeA); +} + + + +#endif /* _HAVE_FULL_GL */ + + +/** + * Set the blending color. + * + * \param red red color component. + * \param green green color component. + * \param blue blue color component. + * \param alpha alpha color component. + * + * \sa glBlendColor(). + * + * Clamps the parameters and updates gl_colorbuffer_attrib::BlendColor. On a + * change, flushes the vertices and notifies the driver via + * dd_function_table::BlendColor callback. + */ +void GLAPIENTRY +_mesa_BlendColor( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha ) +{ + GLfloat tmp[4]; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + tmp[0] = CLAMP( red, 0.0F, 1.0F ); + tmp[1] = CLAMP( green, 0.0F, 1.0F ); + tmp[2] = CLAMP( blue, 0.0F, 1.0F ); + tmp[3] = CLAMP( alpha, 0.0F, 1.0F ); + + if (TEST_EQ_4V(tmp, ctx->Color.BlendColor)) + return; + + FLUSH_VERTICES(ctx, _NEW_COLOR); + COPY_4FV( ctx->Color.BlendColor, tmp ); + + if (ctx->Driver.BlendColor) + (*ctx->Driver.BlendColor)(ctx, tmp); +} + + +/** + * Specify the alpha test function. + * + * \param func alpha comparison function. + * \param ref reference value. + * + * Verifies the parameters and updates gl_colorbuffer_attrib. + * On a change, flushes the vertices and notifies the driver via + * dd_function_table::AlphaFunc callback. + */ +void GLAPIENTRY +_mesa_AlphaFunc( GLenum func, GLclampf ref ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glAlphaFunc(%s, %f)\n", + _mesa_lookup_enum_by_nr(func), ref); + + switch (func) { + case GL_NEVER: + case GL_LESS: + case GL_EQUAL: + case GL_LEQUAL: + case GL_GREATER: + case GL_NOTEQUAL: + case GL_GEQUAL: + case GL_ALWAYS: + ref = CLAMP(ref, 0.0F, 1.0F); + + if (ctx->Color.AlphaFunc == func && ctx->Color.AlphaRef == ref) + return; /* no change */ + + FLUSH_VERTICES(ctx, _NEW_COLOR); + ctx->Color.AlphaFunc = func; + ctx->Color.AlphaRef = ref; + + if (ctx->Driver.AlphaFunc) + ctx->Driver.AlphaFunc(ctx, func, ref); + return; + + default: + _mesa_error( ctx, GL_INVALID_ENUM, "glAlphaFunc(func)" ); + return; + } +} + + +/** + * Specify a logic pixel operation for color index rendering. + * + * \param opcode operation. + * + * Verifies that \p opcode is a valid enum and updates +gl_colorbuffer_attrib::LogicOp. + * On a change, flushes the vertices and notifies the driver via the + * dd_function_table::LogicOpcode callback. + */ +void GLAPIENTRY +_mesa_LogicOp( GLenum opcode ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glLogicOp(%s)\n", _mesa_lookup_enum_by_nr(opcode)); + + switch (opcode) { + case GL_CLEAR: + case GL_SET: + case GL_COPY: + case GL_COPY_INVERTED: + case GL_NOOP: + case GL_INVERT: + case GL_AND: + case GL_NAND: + case GL_OR: + case GL_NOR: + case GL_XOR: + case GL_EQUIV: + case GL_AND_REVERSE: + case GL_AND_INVERTED: + case GL_OR_REVERSE: + case GL_OR_INVERTED: + break; + default: + _mesa_error( ctx, GL_INVALID_ENUM, "glLogicOp" ); + return; + } + + if (ctx->Color.LogicOp == opcode) + return; + + FLUSH_VERTICES(ctx, _NEW_COLOR); + ctx->Color.LogicOp = opcode; + + if (ctx->Driver.LogicOpcode) + ctx->Driver.LogicOpcode( ctx, opcode ); +} + +#if _HAVE_FULL_GL +void GLAPIENTRY +_mesa_IndexMask( GLuint mask ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (ctx->Color.IndexMask == mask) + return; + + FLUSH_VERTICES(ctx, _NEW_COLOR); + ctx->Color.IndexMask = mask; +} +#endif + + +/** + * Enable or disable writing of frame buffer color components. + * + * \param red whether to mask writing of the red color component. + * \param green whether to mask writing of the green color component. + * \param blue whether to mask writing of the blue color component. + * \param alpha whether to mask writing of the alpha color component. + * + * \sa glColorMask(). + * + * Sets the appropriate value of gl_colorbuffer_attrib::ColorMask. On a + * change, flushes the vertices and notifies the driver via the + * dd_function_table::ColorMask callback. + */ +void GLAPIENTRY +_mesa_ColorMask( GLboolean red, GLboolean green, + GLboolean blue, GLboolean alpha ) +{ + GET_CURRENT_CONTEXT(ctx); + GLubyte tmp[4]; + GLuint i; + GLboolean flushed; + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glColorMask(%d, %d, %d, %d)\n", + red, green, blue, alpha); + + /* Shouldn't have any information about channel depth in core mesa + * -- should probably store these as the native booleans: + */ + tmp[RCOMP] = red ? 0xff : 0x0; + tmp[GCOMP] = green ? 0xff : 0x0; + tmp[BCOMP] = blue ? 0xff : 0x0; + tmp[ACOMP] = alpha ? 0xff : 0x0; + + flushed = GL_FALSE; + for (i = 0; i < ctx->Const.MaxDrawBuffers; i++) { + if (!TEST_EQ_4V(tmp, ctx->Color.ColorMask[i])) { + if (!flushed) { + FLUSH_VERTICES(ctx, _NEW_COLOR); + } + flushed = GL_TRUE; + COPY_4UBV(ctx->Color.ColorMask[i], tmp); + } + } + + if (ctx->Driver.ColorMask) + ctx->Driver.ColorMask( ctx, red, green, blue, alpha ); +} + + +/** + * For GL_EXT_draw_buffers2 and GL3 + */ +void GLAPIENTRY +_mesa_ColorMaskIndexed( GLuint buf, GLboolean red, GLboolean green, + GLboolean blue, GLboolean alpha ) +{ + GLubyte tmp[4]; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glColorMaskIndexed %u %d %d %d %d\n", + buf, red, green, blue, alpha); + + if (buf >= ctx->Const.MaxDrawBuffers) { + _mesa_error(ctx, GL_INVALID_VALUE, "glColorMaskIndexed(buf=%u)", buf); + return; + } + + /* Shouldn't have any information about channel depth in core mesa + * -- should probably store these as the native booleans: + */ + tmp[RCOMP] = red ? 0xff : 0x0; + tmp[GCOMP] = green ? 0xff : 0x0; + tmp[BCOMP] = blue ? 0xff : 0x0; + tmp[ACOMP] = alpha ? 0xff : 0x0; + + if (TEST_EQ_4V(tmp, ctx->Color.ColorMask[buf])) + return; + + FLUSH_VERTICES(ctx, _NEW_COLOR); + COPY_4UBV(ctx->Color.ColorMask[buf], tmp); + + if (ctx->Driver.ColorMaskIndexed) + ctx->Driver.ColorMaskIndexed(ctx, buf, red, green, blue, alpha); +} + + +extern void GLAPIENTRY +_mesa_ClampColorARB(GLenum target, GLenum clamp) +{ + GET_CURRENT_CONTEXT(ctx); + + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (clamp != GL_TRUE && clamp != GL_FALSE && clamp != GL_FIXED_ONLY_ARB) { + _mesa_error(ctx, GL_INVALID_ENUM, "glClampColorARB(clamp)"); + return; + } + + switch (target) { + case GL_CLAMP_VERTEX_COLOR_ARB: + ctx->Light.ClampVertexColor = clamp; + break; + case GL_CLAMP_FRAGMENT_COLOR_ARB: + ctx->Color.ClampFragmentColor = clamp; + break; + case GL_CLAMP_READ_COLOR_ARB: + ctx->Color.ClampReadColor = clamp; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, "glClampColorARB(target)"); + return; + } +} + + + + +/**********************************************************************/ +/** \name Initialization */ +/*@{*/ + +/** + * Initialization of the context's Color attribute group. + * + * \param ctx GL context. + * + * Initializes the related fields in the context color attribute group, + * __struct gl_contextRec::Color. + */ +void _mesa_init_color( struct gl_context * ctx ) +{ + GLuint i; + + /* Color buffer group */ + ctx->Color.IndexMask = ~0u; + memset(ctx->Color.ColorMask, 0xff, sizeof(ctx->Color.ColorMask)); + ctx->Color.ClearIndex = 0; + ASSIGN_4V( ctx->Color.ClearColor, 0, 0, 0, 0 ); + ctx->Color.AlphaEnabled = GL_FALSE; + ctx->Color.AlphaFunc = GL_ALWAYS; + ctx->Color.AlphaRef = 0; + ctx->Color.BlendEnabled = 0x0; + for (i = 0; i < Elements(ctx->Color.Blend); i++) { + ctx->Color.Blend[i].SrcRGB = GL_ONE; + ctx->Color.Blend[i].DstRGB = GL_ZERO; + ctx->Color.Blend[i].SrcA = GL_ONE; + ctx->Color.Blend[i].DstA = GL_ZERO; + ctx->Color.Blend[i].EquationRGB = GL_FUNC_ADD; + ctx->Color.Blend[i].EquationA = GL_FUNC_ADD; + } + ASSIGN_4V( ctx->Color.BlendColor, 0.0, 0.0, 0.0, 0.0 ); + ctx->Color.IndexLogicOpEnabled = GL_FALSE; + ctx->Color.ColorLogicOpEnabled = GL_FALSE; + ctx->Color._LogicOpEnabled = GL_FALSE; + ctx->Color.LogicOp = GL_COPY; + ctx->Color.DitherFlag = GL_TRUE; + + if (ctx->Visual.doubleBufferMode) { + ctx->Color.DrawBuffer[0] = GL_BACK; + } + else { + ctx->Color.DrawBuffer[0] = GL_FRONT; + } + + ctx->Color.ClampFragmentColor = GL_FIXED_ONLY_ARB; + ctx->Color.ClampReadColor = GL_FIXED_ONLY_ARB; +} + +/*@}*/ diff --git a/mesalib/src/mesa/main/bufferobj.c b/mesalib/src/mesa/main/bufferobj.c index 31475529e..75afae0ad 100644 --- a/mesalib/src/mesa/main/bufferobj.c +++ b/mesalib/src/mesa/main/bufferobj.c @@ -1,2145 +1,2158 @@ -/* - * Mesa 3-D graphics library - * Version: 7.6 - * - * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. - * 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 - * BRIAN PAUL 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 bufferobj.c - * \brief Functions for the GL_ARB_vertex/pixel_buffer_object extensions. - * \author Brian Paul, Ian Romanick - */ - - -#include "glheader.h" -#include "enums.h" -#include "hash.h" -#include "imports.h" -#include "image.h" -#include "context.h" -#include "bufferobj.h" -#include "fbobject.h" -#include "mfeatures.h" -#include "mtypes.h" -#include "texobj.h" - - -/* Debug flags */ -/*#define VBO_DEBUG*/ -/*#define BOUNDS_CHECK*/ - - -#if FEATURE_OES_mapbuffer -#define DEFAULT_ACCESS GL_MAP_WRITE_BIT -#else -#define DEFAULT_ACCESS (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT) -#endif - - -/** - * Used as a placeholder for buffer objects between glGenBuffers() and - * glBindBuffer() so that glIsBuffer() can work correctly. - */ -static struct gl_buffer_object DummyBufferObject; - - -/** - * Return pointer to address of a buffer object target. - * \param ctx the GL context - * \param target the buffer object target to be retrieved. - * \return pointer to pointer to the buffer object bound to \c target in the - * specified context or \c NULL if \c target is invalid. - */ -static INLINE struct gl_buffer_object ** -get_buffer_target(struct gl_context *ctx, GLenum target) -{ - switch (target) { - case GL_ARRAY_BUFFER_ARB: - return &ctx->Array.ArrayBufferObj; - case GL_ELEMENT_ARRAY_BUFFER_ARB: - return &ctx->Array.ElementArrayBufferObj; - case GL_PIXEL_PACK_BUFFER_EXT: - return &ctx->Pack.BufferObj; - case GL_PIXEL_UNPACK_BUFFER_EXT: - return &ctx->Unpack.BufferObj; - case GL_COPY_READ_BUFFER: - return &ctx->CopyReadBuffer; - case GL_COPY_WRITE_BUFFER: - return &ctx->CopyWriteBuffer; -#if FEATURE_EXT_transform_feedback - case GL_TRANSFORM_FEEDBACK_BUFFER: - if (ctx->Extensions.EXT_transform_feedback) { - return &ctx->TransformFeedback.CurrentBuffer; - } - break; -#endif - default: - return NULL; - } - return NULL; -} - - -/** - * Get the buffer object bound to the specified target in a GL context. - * \param ctx the GL context - * \param target the buffer object target to be retrieved. - * \return pointer to the buffer object bound to \c target in the - * specified context or \c NULL if \c target is invalid. - */ -static INLINE struct gl_buffer_object * -get_buffer(struct gl_context *ctx, GLenum target) -{ - struct gl_buffer_object **bufObj = get_buffer_target(ctx, target); - if (bufObj) - return *bufObj; - return NULL; -} - - -/** - * Convert a GLbitfield describing the mapped buffer access flags - * into one of GL_READ_WRITE, GL_READ_ONLY, or GL_WRITE_ONLY. - */ -static GLenum -simplified_access_mode(GLbitfield access) -{ - const GLbitfield rwFlags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT; - if ((access & rwFlags) == rwFlags) - return GL_READ_WRITE; - if ((access & GL_MAP_READ_BIT) == GL_MAP_READ_BIT) - return GL_READ_ONLY; - if ((access & GL_MAP_WRITE_BIT) == GL_MAP_WRITE_BIT) - return GL_WRITE_ONLY; - return GL_READ_WRITE; /* this should never happen, but no big deal */ -} - - -/** - * Tests the subdata range parameters and sets the GL error code for - * \c glBufferSubDataARB and \c glGetBufferSubDataARB. - * - * \param ctx GL context. - * \param target Buffer object target on which to operate. - * \param offset Offset of the first byte of the subdata range. - * \param size Size, in bytes, of the subdata range. - * \param caller Name of calling function for recording errors. - * \return A pointer to the buffer object bound to \c target in the - * specified context or \c NULL if any of the parameter or state - * conditions for \c glBufferSubDataARB or \c glGetBufferSubDataARB - * are invalid. - * - * \sa glBufferSubDataARB, glGetBufferSubDataARB - */ -static struct gl_buffer_object * -buffer_object_subdata_range_good( struct gl_context * ctx, GLenum target, - GLintptrARB offset, GLsizeiptrARB size, - const char *caller ) -{ - struct gl_buffer_object *bufObj; - - if (size < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, "%s(size < 0)", caller); - return NULL; - } - - if (offset < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset < 0)", caller); - return NULL; - } - - bufObj = get_buffer(ctx, target); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_ENUM, "%s(target)", caller); - return NULL; - } - if (!_mesa_is_bufferobj(bufObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller); - return NULL; - } - if (offset + size > bufObj->Size) { - _mesa_error(ctx, GL_INVALID_VALUE, - "%s(size + offset > buffer size)", caller); - return NULL; - } - if (_mesa_bufferobj_mapped(bufObj)) { - /* Buffer is currently mapped */ - _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller); - return NULL; - } - - return bufObj; -} - - -/** - * Allocate and initialize a new buffer object. - * - * Default callback for the \c dd_function_table::NewBufferObject() hook. - */ -static struct gl_buffer_object * -_mesa_new_buffer_object( struct gl_context *ctx, GLuint name, GLenum target ) -{ - struct gl_buffer_object *obj; - - (void) ctx; - - obj = MALLOC_STRUCT(gl_buffer_object); - _mesa_initialize_buffer_object(obj, name, target); - return obj; -} - - -/** - * Delete a buffer object. - * - * Default callback for the \c dd_function_table::DeleteBuffer() hook. - */ -static void -_mesa_delete_buffer_object( struct gl_context *ctx, struct gl_buffer_object *bufObj ) -{ - (void) ctx; - - if (bufObj->Data) - free(bufObj->Data); - - /* assign strange values here to help w/ debugging */ - bufObj->RefCount = -1000; - bufObj->Name = ~0; - - _glthread_DESTROY_MUTEX(bufObj->Mutex); - free(bufObj); -} - - - -/** - * Set ptr to bufObj w/ reference counting. - */ -void -_mesa_reference_buffer_object(struct gl_context *ctx, - struct gl_buffer_object **ptr, - struct gl_buffer_object *bufObj) -{ - if (*ptr == bufObj) - return; - - if (*ptr) { - /* Unreference the old buffer */ - GLboolean deleteFlag = GL_FALSE; - struct gl_buffer_object *oldObj = *ptr; - - _glthread_LOCK_MUTEX(oldObj->Mutex); - ASSERT(oldObj->RefCount > 0); - oldObj->RefCount--; -#if 0 - printf("BufferObj %p %d DECR to %d\n", - (void *) oldObj, oldObj->Name, oldObj->RefCount); -#endif - deleteFlag = (oldObj->RefCount == 0); - _glthread_UNLOCK_MUTEX(oldObj->Mutex); - - if (deleteFlag) { - - /* some sanity checking: don't delete a buffer still in use */ -#if 0 - /* unfortunately, these tests are invalid during context tear-down */ - ASSERT(ctx->Array.ArrayBufferObj != bufObj); - ASSERT(ctx->Array.ElementArrayBufferObj != bufObj); - ASSERT(ctx->Array.ArrayObj->Vertex.BufferObj != bufObj); -#endif - - ASSERT(ctx->Driver.DeleteBuffer); - ctx->Driver.DeleteBuffer(ctx, oldObj); - } - - *ptr = NULL; - } - ASSERT(!*ptr); - - if (bufObj) { - /* reference new buffer */ - _glthread_LOCK_MUTEX(bufObj->Mutex); - if (bufObj->RefCount == 0) { - /* this buffer's being deleted (look just above) */ - /* Not sure this can every really happen. Warn if it does. */ - _mesa_problem(NULL, "referencing deleted buffer object"); - *ptr = NULL; - } - else { - bufObj->RefCount++; -#if 0 - printf("BufferObj %p %d INCR to %d\n", - (void *) bufObj, bufObj->Name, bufObj->RefCount); -#endif - *ptr = bufObj; - } - _glthread_UNLOCK_MUTEX(bufObj->Mutex); - } -} - - -/** - * Initialize a buffer object to default values. - */ -void -_mesa_initialize_buffer_object( struct gl_buffer_object *obj, - GLuint name, GLenum target ) -{ - (void) target; - - memset(obj, 0, sizeof(struct gl_buffer_object)); - _glthread_INIT_MUTEX(obj->Mutex); - obj->RefCount = 1; - obj->Name = name; - obj->Usage = GL_STATIC_DRAW_ARB; - obj->AccessFlags = DEFAULT_ACCESS; -} - - -/** - * Allocate space for and store data in a buffer object. Any data that was - * previously stored in the buffer object is lost. If \c data is \c NULL, - * memory will be allocated, but no copy will occur. - * - * This is the default callback for \c dd_function_table::BufferData() - * Note that all GL error checking will have been done already. - * - * \param ctx GL context. - * \param target Buffer object target on which to operate. - * \param size Size, in bytes, of the new data store. - * \param data Pointer to the data to store in the buffer object. This - * pointer may be \c NULL. - * \param usage Hints about how the data will be used. - * \param bufObj Object to be used. - * - * \return GL_TRUE for success, GL_FALSE for failure - * \sa glBufferDataARB, dd_function_table::BufferData. - */ -static GLboolean -_mesa_buffer_data( struct gl_context *ctx, GLenum target, GLsizeiptrARB size, - const GLvoid * data, GLenum usage, - struct gl_buffer_object * bufObj ) -{ - void * new_data; - - (void) ctx; (void) target; - - new_data = _mesa_realloc( bufObj->Data, bufObj->Size, size ); - if (new_data) { - bufObj->Data = (GLubyte *) new_data; - bufObj->Size = size; - bufObj->Usage = usage; - - if (data) { - memcpy( bufObj->Data, data, size ); - } - - return GL_TRUE; - } - else { - return GL_FALSE; - } -} - - -/** - * Replace data in a subrange of buffer object. If the data range - * specified by \c size + \c offset extends beyond the end of the buffer or - * if \c data is \c NULL, no copy is performed. - * - * This is the default callback for \c dd_function_table::BufferSubData() - * Note that all GL error checking will have been done already. - * - * \param ctx GL context. - * \param target Buffer object target on which to operate. - * \param offset Offset of the first byte to be modified. - * \param size Size, in bytes, of the data range. - * \param data Pointer to the data to store in the buffer object. - * \param bufObj Object to be used. - * - * \sa glBufferSubDataARB, dd_function_table::BufferSubData. - */ -static void -_mesa_buffer_subdata( struct gl_context *ctx, GLenum target, GLintptrARB offset, - GLsizeiptrARB size, const GLvoid * data, - struct gl_buffer_object * bufObj ) -{ - (void) ctx; (void) target; - - /* this should have been caught in _mesa_BufferSubData() */ - ASSERT(size + offset <= bufObj->Size); - - if (bufObj->Data) { - memcpy( (GLubyte *) bufObj->Data + offset, data, size ); - } -} - - -/** - * Retrieve data from a subrange of buffer object. If the data range - * specified by \c size + \c offset extends beyond the end of the buffer or - * if \c data is \c NULL, no copy is performed. - * - * This is the default callback for \c dd_function_table::GetBufferSubData() - * Note that all GL error checking will have been done already. - * - * \param ctx GL context. - * \param target Buffer object target on which to operate. - * \param offset Offset of the first byte to be fetched. - * \param size Size, in bytes, of the data range. - * \param data Destination for data - * \param bufObj Object to be used. - * - * \sa glBufferGetSubDataARB, dd_function_table::GetBufferSubData. - */ -static void -_mesa_buffer_get_subdata( struct gl_context *ctx, GLenum target, GLintptrARB offset, - GLsizeiptrARB size, GLvoid * data, - struct gl_buffer_object * bufObj ) -{ - (void) ctx; (void) target; - - if (bufObj->Data && ((GLsizeiptrARB) (size + offset) <= bufObj->Size)) { - memcpy( data, (GLubyte *) bufObj->Data + offset, size ); - } -} - - -/** - * Default callback for \c dd_function_tabel::MapBuffer(). - * - * The function parameters will have been already tested for errors. - * - * \param ctx GL context. - * \param target Buffer object target on which to operate. - * \param access Information about how the buffer will be accessed. - * \param bufObj Object to be mapped. - * \return A pointer to the object's internal data store that can be accessed - * by the processor - * - * \sa glMapBufferARB, dd_function_table::MapBuffer - */ -static void * -_mesa_buffer_map( struct gl_context *ctx, GLenum target, GLenum access, - struct gl_buffer_object *bufObj ) -{ - (void) ctx; - (void) target; - (void) access; - /* Just return a direct pointer to the data */ - if (_mesa_bufferobj_mapped(bufObj)) { - /* already mapped! */ - return NULL; - } - bufObj->Pointer = bufObj->Data; - bufObj->Length = bufObj->Size; - bufObj->Offset = 0; - return bufObj->Pointer; -} - - -/** - * Default fallback for \c dd_function_table::MapBufferRange(). - * Called via glMapBufferRange(). - */ -static void * -_mesa_buffer_map_range( struct gl_context *ctx, GLenum target, GLintptr offset, - GLsizeiptr length, GLbitfield access, - struct gl_buffer_object *bufObj ) -{ - (void) ctx; - (void) target; - assert(!_mesa_bufferobj_mapped(bufObj)); - /* Just return a direct pointer to the data */ - bufObj->Pointer = bufObj->Data + offset; - bufObj->Length = length; - bufObj->Offset = offset; - bufObj->AccessFlags = access; - return bufObj->Pointer; -} - - -/** - * Default fallback for \c dd_function_table::FlushMappedBufferRange(). - * Called via glFlushMappedBufferRange(). - */ -static void -_mesa_buffer_flush_mapped_range( struct gl_context *ctx, GLenum target, - GLintptr offset, GLsizeiptr length, - struct gl_buffer_object *obj ) -{ - (void) ctx; - (void) target; - (void) offset; - (void) length; - (void) obj; - /* no-op */ -} - - -/** - * Default callback for \c dd_function_table::MapBuffer(). - * - * The input parameters will have been already tested for errors. - * - * \sa glUnmapBufferARB, dd_function_table::UnmapBuffer - */ -static GLboolean -_mesa_buffer_unmap( struct gl_context *ctx, GLenum target, - struct gl_buffer_object *bufObj ) -{ - (void) ctx; - (void) target; - /* XXX we might assert here that bufObj->Pointer is non-null */ - bufObj->Pointer = NULL; - bufObj->Length = 0; - bufObj->Offset = 0; - bufObj->AccessFlags = 0x0; - return GL_TRUE; -} - - -/** - * Default fallback for \c dd_function_table::CopyBufferSubData(). - * Called via glCopyBuffserSubData(). - */ -static void -_mesa_copy_buffer_subdata(struct gl_context *ctx, - struct gl_buffer_object *src, - struct gl_buffer_object *dst, - GLintptr readOffset, GLintptr writeOffset, - GLsizeiptr size) -{ - GLubyte *srcPtr, *dstPtr; - - /* buffer should not already be mapped */ - assert(!_mesa_bufferobj_mapped(src)); - assert(!_mesa_bufferobj_mapped(dst)); - - srcPtr = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_COPY_READ_BUFFER, - GL_READ_ONLY, src); - dstPtr = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_COPY_WRITE_BUFFER, - GL_WRITE_ONLY, dst); - - if (srcPtr && dstPtr) - memcpy(dstPtr + writeOffset, srcPtr + readOffset, size); - - ctx->Driver.UnmapBuffer(ctx, GL_COPY_READ_BUFFER, src); - ctx->Driver.UnmapBuffer(ctx, GL_COPY_WRITE_BUFFER, dst); -} - - - -/** - * Initialize the state associated with buffer objects - */ -void -_mesa_init_buffer_objects( struct gl_context *ctx ) -{ - memset(&DummyBufferObject, 0, sizeof(DummyBufferObject)); - DummyBufferObject.RefCount = 1000*1000*1000; /* never delete */ - - _mesa_reference_buffer_object(ctx, &ctx->Array.ArrayBufferObj, - ctx->Shared->NullBufferObj); - _mesa_reference_buffer_object(ctx, &ctx->Array.ElementArrayBufferObj, - ctx->Shared->NullBufferObj); - - _mesa_reference_buffer_object(ctx, &ctx->CopyReadBuffer, - ctx->Shared->NullBufferObj); - _mesa_reference_buffer_object(ctx, &ctx->CopyWriteBuffer, - ctx->Shared->NullBufferObj); -} - - -void -_mesa_free_buffer_objects( struct gl_context *ctx ) -{ - _mesa_reference_buffer_object(ctx, &ctx->Array.ArrayBufferObj, NULL); - _mesa_reference_buffer_object(ctx, &ctx->Array.ElementArrayBufferObj, NULL); - - _mesa_reference_buffer_object(ctx, &ctx->CopyReadBuffer, NULL); - _mesa_reference_buffer_object(ctx, &ctx->CopyWriteBuffer, NULL); -} - - -/** - * Bind the specified target to buffer for the specified context. - * Called by glBindBuffer() and other functions. - */ -static void -bind_buffer_object(struct gl_context *ctx, GLenum target, GLuint buffer) -{ - struct gl_buffer_object *oldBufObj; - struct gl_buffer_object *newBufObj = NULL; - struct gl_buffer_object **bindTarget = NULL; - - bindTarget = get_buffer_target(ctx, target); - if (!bindTarget) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferARB(target 0x%x)", target); - return; - } - - /* Get pointer to old buffer object (to be unbound) */ - oldBufObj = *bindTarget; - if (oldBufObj && oldBufObj->Name == buffer) - return; /* rebinding the same buffer object- no change */ - - /* - * Get pointer to new buffer object (newBufObj) - */ - if (buffer == 0) { - /* The spec says there's not a buffer object named 0, but we use - * one internally because it simplifies things. - */ - newBufObj = ctx->Shared->NullBufferObj; - } - else { - /* non-default buffer object */ - newBufObj = _mesa_lookup_bufferobj(ctx, buffer); - if (!newBufObj || newBufObj == &DummyBufferObject) { - /* If this is a new buffer object id, or one which was generated but - * never used before, allocate a buffer object now. - */ - ASSERT(ctx->Driver.NewBufferObject); - newBufObj = ctx->Driver.NewBufferObject(ctx, buffer, target); - if (!newBufObj) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindBufferARB"); - return; - } - _mesa_HashInsert(ctx->Shared->BufferObjects, buffer, newBufObj); - } - } - - /* bind new buffer */ - _mesa_reference_buffer_object(ctx, bindTarget, newBufObj); - - /* Pass BindBuffer call to device driver */ - if (ctx->Driver.BindBuffer) - ctx->Driver.BindBuffer( ctx, target, newBufObj ); -} - - -/** - * Update the default buffer objects in the given context to reference those - * specified in the shared state and release those referencing the old - * shared state. - */ -void -_mesa_update_default_objects_buffer_objects(struct gl_context *ctx) -{ - /* Bind the NullBufferObj to remove references to those - * in the shared context hash table. - */ - bind_buffer_object( ctx, GL_ARRAY_BUFFER_ARB, 0); - bind_buffer_object( ctx, GL_ELEMENT_ARRAY_BUFFER_ARB, 0); - bind_buffer_object( ctx, GL_PIXEL_PACK_BUFFER_ARB, 0); - bind_buffer_object( ctx, GL_PIXEL_UNPACK_BUFFER_ARB, 0); -} - - -/** - * When we're about to read pixel data out of a PBO (via glDrawPixels, - * glTexImage, etc) or write data into a PBO (via glReadPixels, - * glGetTexImage, etc) we call this function to check that we're not - * going to read out of bounds. - * - * XXX This would also be a convenient time to check that the PBO isn't - * currently mapped. Whoever calls this function should check for that. - * Remember, we can't use a PBO when it's mapped! - * - * If we're not using a PBO, this is a no-op. - * - * \param width width of image to read/write - * \param height height of image to read/write - * \param depth depth of image to read/write - * \param format format of image to read/write - * \param type datatype of image to read/write - * \param ptr the user-provided pointer/offset - * \return GL_TRUE if the PBO access is OK, GL_FALSE if the access would - * go out of bounds. - */ -GLboolean -_mesa_validate_pbo_access(GLuint dimensions, - const struct gl_pixelstore_attrib *pack, - GLsizei width, GLsizei height, GLsizei depth, - GLenum format, GLenum type, const GLvoid *ptr) -{ - GLvoid *start, *end; - const GLubyte *sizeAddr; /* buffer size, cast to a pointer */ - - if (!_mesa_is_bufferobj(pack->BufferObj)) - return GL_TRUE; /* no PBO, OK */ - - if (pack->BufferObj->Size == 0) - /* no buffer! */ - return GL_FALSE; - - /* get address of first pixel we'll read */ - start = _mesa_image_address(dimensions, pack, ptr, width, height, - format, type, 0, 0, 0); - - /* get address just past the last pixel we'll read */ - end = _mesa_image_address(dimensions, pack, ptr, width, height, - format, type, depth-1, height-1, width); - - - sizeAddr = ((const GLubyte *) 0) + pack->BufferObj->Size; - - if ((const GLubyte *) start > sizeAddr) { - /* This will catch negative values / wrap-around */ - return GL_FALSE; - } - if ((const GLubyte *) end > sizeAddr) { - /* Image read goes beyond end of buffer */ - return GL_FALSE; - } - - /* OK! */ - return GL_TRUE; -} - - -/** - * For commands that read from a PBO (glDrawPixels, glTexImage, - * glPolygonStipple, etc), if we're reading from a PBO, map it read-only - * and return the pointer into the PBO. If we're not reading from a - * PBO, return \p src as-is. - * If non-null return, must call _mesa_unmap_pbo_source() when done. - * - * \return NULL if error, else pointer to start of data - */ -const GLvoid * -_mesa_map_pbo_source(struct gl_context *ctx, - const struct gl_pixelstore_attrib *unpack, - const GLvoid *src) -{ - const GLubyte *buf; - - if (_mesa_is_bufferobj(unpack->BufferObj)) { - /* unpack from PBO */ - buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT, - GL_READ_ONLY_ARB, - unpack->BufferObj); - if (!buf) - return NULL; - - buf = ADD_POINTERS(buf, src); - } - else { - /* unpack from normal memory */ - buf = src; - } - - return buf; -} - - -/** - * Combine PBO-read validation and mapping. - * If any GL errors are detected, they'll be recorded and NULL returned. - * \sa _mesa_validate_pbo_access - * \sa _mesa_map_pbo_source - * A call to this function should have a matching call to - * _mesa_unmap_pbo_source(). - */ -const GLvoid * -_mesa_map_validate_pbo_source(struct gl_context *ctx, - GLuint dimensions, - const struct gl_pixelstore_attrib *unpack, - GLsizei width, GLsizei height, GLsizei depth, - GLenum format, GLenum type, const GLvoid *ptr, - const char *where) -{ - ASSERT(dimensions == 1 || dimensions == 2 || dimensions == 3); - - if (!_mesa_is_bufferobj(unpack->BufferObj)) { - /* non-PBO access: no validation to be done */ - return ptr; - } - - if (!_mesa_validate_pbo_access(dimensions, unpack, - width, height, depth, format, type, ptr)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "%s(out of bounds PBO access)", where); - return NULL; - } - - if (_mesa_bufferobj_mapped(unpack->BufferObj)) { - /* buffer is already mapped - that's an error */ - _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where); - return NULL; - } - - ptr = _mesa_map_pbo_source(ctx, unpack, ptr); - return ptr; -} - - -/** - * Counterpart to _mesa_map_pbo_source() - */ -void -_mesa_unmap_pbo_source(struct gl_context *ctx, - const struct gl_pixelstore_attrib *unpack) -{ - ASSERT(unpack != &ctx->Pack); /* catch pack/unpack mismatch */ - if (_mesa_is_bufferobj(unpack->BufferObj)) { - ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT, - unpack->BufferObj); - } -} - - -/** - * For commands that write to a PBO (glReadPixels, glGetColorTable, etc), - * if we're writing to a PBO, map it write-only and return the pointer - * into the PBO. If we're not writing to a PBO, return \p dst as-is. - * If non-null return, must call _mesa_unmap_pbo_dest() when done. - * - * \return NULL if error, else pointer to start of data - */ -void * -_mesa_map_pbo_dest(struct gl_context *ctx, - const struct gl_pixelstore_attrib *pack, - GLvoid *dest) -{ - void *buf; - - if (_mesa_is_bufferobj(pack->BufferObj)) { - /* pack into PBO */ - buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT, - GL_WRITE_ONLY_ARB, - pack->BufferObj); - if (!buf) - return NULL; - - buf = ADD_POINTERS(buf, dest); - } - else { - /* pack to normal memory */ - buf = dest; - } - - return buf; -} - - -/** - * Combine PBO-write validation and mapping. - * If any GL errors are detected, they'll be recorded and NULL returned. - * \sa _mesa_validate_pbo_access - * \sa _mesa_map_pbo_dest - * A call to this function should have a matching call to - * _mesa_unmap_pbo_dest(). - */ -GLvoid * -_mesa_map_validate_pbo_dest(struct gl_context *ctx, - GLuint dimensions, - const struct gl_pixelstore_attrib *unpack, - GLsizei width, GLsizei height, GLsizei depth, - GLenum format, GLenum type, GLvoid *ptr, - const char *where) -{ - ASSERT(dimensions == 1 || dimensions == 2 || dimensions == 3); - - if (!_mesa_is_bufferobj(unpack->BufferObj)) { - /* non-PBO access: no validation to be done */ - return ptr; - } - - if (!_mesa_validate_pbo_access(dimensions, unpack, - width, height, depth, format, type, ptr)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "%s(out of bounds PBO access)", where); - return NULL; - } - - if (_mesa_bufferobj_mapped(unpack->BufferObj)) { - /* buffer is already mapped - that's an error */ - _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where); - return NULL; - } - - ptr = _mesa_map_pbo_dest(ctx, unpack, ptr); - return ptr; -} - - -/** - * Counterpart to _mesa_map_pbo_dest() - */ -void -_mesa_unmap_pbo_dest(struct gl_context *ctx, - const struct gl_pixelstore_attrib *pack) -{ - ASSERT(pack != &ctx->Unpack); /* catch pack/unpack mismatch */ - if (_mesa_is_bufferobj(pack->BufferObj)) { - ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT, pack->BufferObj); - } -} - - - -/** - * Return the gl_buffer_object for the given ID. - * Always return NULL for ID 0. - */ -struct gl_buffer_object * -_mesa_lookup_bufferobj(struct gl_context *ctx, GLuint buffer) -{ - if (buffer == 0) - return NULL; - else - return (struct gl_buffer_object *) - _mesa_HashLookup(ctx->Shared->BufferObjects, buffer); -} - - -/** - * If *ptr points to obj, set ptr = the Null/default buffer object. - * This is a helper for buffer object deletion. - * The GL spec says that deleting a buffer object causes it to get - * unbound from all arrays in the current context. - */ -static void -unbind(struct gl_context *ctx, - struct gl_buffer_object **ptr, - struct gl_buffer_object *obj) -{ - if (*ptr == obj) { - _mesa_reference_buffer_object(ctx, ptr, ctx->Shared->NullBufferObj); - } -} - - -/** - * Plug default/fallback buffer object functions into the device - * driver hooks. - */ -void -_mesa_init_buffer_object_functions(struct dd_function_table *driver) -{ - /* GL_ARB_vertex/pixel_buffer_object */ - driver->NewBufferObject = _mesa_new_buffer_object; - driver->DeleteBuffer = _mesa_delete_buffer_object; - driver->BindBuffer = NULL; - driver->BufferData = _mesa_buffer_data; - driver->BufferSubData = _mesa_buffer_subdata; - driver->GetBufferSubData = _mesa_buffer_get_subdata; - driver->MapBuffer = _mesa_buffer_map; - driver->UnmapBuffer = _mesa_buffer_unmap; - - /* GL_ARB_map_buffer_range */ - driver->MapBufferRange = _mesa_buffer_map_range; - driver->FlushMappedBufferRange = _mesa_buffer_flush_mapped_range; - - /* GL_ARB_copy_buffer */ - driver->CopyBufferSubData = _mesa_copy_buffer_subdata; -} - - - -/**********************************************************************/ -/* API Functions */ -/**********************************************************************/ - -void GLAPIENTRY -_mesa_BindBufferARB(GLenum target, GLuint buffer) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - bind_buffer_object(ctx, target, buffer); -} - - -/** - * Delete a set of buffer objects. - * - * \param n Number of buffer objects to delete. - * \param ids Array of \c n buffer object IDs. - */ -void GLAPIENTRY -_mesa_DeleteBuffersARB(GLsizei n, const GLuint *ids) -{ - GET_CURRENT_CONTEXT(ctx); - GLsizei i; - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (n < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteBuffersARB(n)"); - return; - } - - _glthread_LOCK_MUTEX(ctx->Shared->Mutex); - - for (i = 0; i < n; i++) { - struct gl_buffer_object *bufObj = _mesa_lookup_bufferobj(ctx, ids[i]); - if (bufObj) { - struct gl_array_object *arrayObj = ctx->Array.ArrayObj; - GLuint j; - - ASSERT(bufObj->Name == ids[i] || bufObj == &DummyBufferObject); - - if (_mesa_bufferobj_mapped(bufObj)) { - /* if mapped, unmap it now */ - ctx->Driver.UnmapBuffer(ctx, 0, bufObj); - bufObj->AccessFlags = DEFAULT_ACCESS; - bufObj->Pointer = NULL; - } - - /* unbind any vertex pointers bound to this buffer */ - unbind(ctx, &arrayObj->Vertex.BufferObj, bufObj); - unbind(ctx, &arrayObj->Weight.BufferObj, bufObj); - unbind(ctx, &arrayObj->Normal.BufferObj, bufObj); - unbind(ctx, &arrayObj->Color.BufferObj, bufObj); - unbind(ctx, &arrayObj->SecondaryColor.BufferObj, bufObj); - unbind(ctx, &arrayObj->FogCoord.BufferObj, bufObj); - unbind(ctx, &arrayObj->Index.BufferObj, bufObj); - unbind(ctx, &arrayObj->EdgeFlag.BufferObj, bufObj); - for (j = 0; j < Elements(arrayObj->TexCoord); j++) { - unbind(ctx, &arrayObj->TexCoord[j].BufferObj, bufObj); - } - for (j = 0; j < Elements(arrayObj->VertexAttrib); j++) { - unbind(ctx, &arrayObj->VertexAttrib[j].BufferObj, bufObj); - } - - if (ctx->Array.ArrayBufferObj == bufObj) { - _mesa_BindBufferARB( GL_ARRAY_BUFFER_ARB, 0 ); - } - if (ctx->Array.ElementArrayBufferObj == bufObj) { - _mesa_BindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 ); - } - - /* unbind any pixel pack/unpack pointers bound to this buffer */ - if (ctx->Pack.BufferObj == bufObj) { - _mesa_BindBufferARB( GL_PIXEL_PACK_BUFFER_EXT, 0 ); - } - if (ctx->Unpack.BufferObj == bufObj) { - _mesa_BindBufferARB( GL_PIXEL_UNPACK_BUFFER_EXT, 0 ); - } - - /* The ID is immediately freed for re-use */ - _mesa_HashRemove(ctx->Shared->BufferObjects, ids[i]); - _mesa_reference_buffer_object(ctx, &bufObj, NULL); - } - } - - _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); -} - - -/** - * Generate a set of unique buffer object IDs and store them in \c buffer. - * - * \param n Number of IDs to generate. - * \param buffer Array of \c n locations to store the IDs. - */ -void GLAPIENTRY -_mesa_GenBuffersARB(GLsizei n, GLuint *buffer) -{ - GET_CURRENT_CONTEXT(ctx); - GLuint first; - GLint i; - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (n < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, "glGenBuffersARB"); - return; - } - - if (!buffer) { - return; - } - - /* - * This must be atomic (generation and allocation of buffer object IDs) - */ - _glthread_LOCK_MUTEX(ctx->Shared->Mutex); - - first = _mesa_HashFindFreeKeyBlock(ctx->Shared->BufferObjects, n); - - /* Insert the ID and pointer to dummy buffer object into hash table */ - for (i = 0; i < n; i++) { - _mesa_HashInsert(ctx->Shared->BufferObjects, first + i, - &DummyBufferObject); - buffer[i] = first + i; - } - - _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); -} - - -/** - * Determine if ID is the name of a buffer object. - * - * \param id ID of the potential buffer object. - * \return \c GL_TRUE if \c id is the name of a buffer object, - * \c GL_FALSE otherwise. - */ -GLboolean GLAPIENTRY -_mesa_IsBufferARB(GLuint id) -{ - struct gl_buffer_object *bufObj; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); - - _glthread_LOCK_MUTEX(ctx->Shared->Mutex); - bufObj = _mesa_lookup_bufferobj(ctx, id); - _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); - - return bufObj && bufObj != &DummyBufferObject; -} - - -void GLAPIENTRY -_mesa_BufferDataARB(GLenum target, GLsizeiptrARB size, - const GLvoid * data, GLenum usage) -{ - GET_CURRENT_CONTEXT(ctx); - struct gl_buffer_object *bufObj; - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (size < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, "glBufferDataARB(size < 0)"); - return; - } - - switch (usage) { - case GL_STREAM_DRAW_ARB: - case GL_STREAM_READ_ARB: - case GL_STREAM_COPY_ARB: - case GL_STATIC_DRAW_ARB: - case GL_STATIC_READ_ARB: - case GL_STATIC_COPY_ARB: - case GL_DYNAMIC_DRAW_ARB: - case GL_DYNAMIC_READ_ARB: - case GL_DYNAMIC_COPY_ARB: - /* OK */ - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, "glBufferDataARB(usage)"); - return; - } - - bufObj = get_buffer(ctx, target); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBufferDataARB(target)" ); - return; - } - if (!_mesa_is_bufferobj(bufObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glBufferDataARB(buffer 0)" ); - return; - } - - if (_mesa_bufferobj_mapped(bufObj)) { - /* Unmap the existing buffer. We'll replace it now. Not an error. */ - ctx->Driver.UnmapBuffer(ctx, target, bufObj); - bufObj->AccessFlags = DEFAULT_ACCESS; - ASSERT(bufObj->Pointer == NULL); - } - - FLUSH_VERTICES(ctx, _NEW_BUFFER_OBJECT); - - bufObj->Written = GL_TRUE; - -#ifdef VBO_DEBUG - printf("glBufferDataARB(%u, sz %ld, from %p, usage 0x%x)\n", - bufObj->Name, size, data, usage); -#endif - -#ifdef BOUNDS_CHECK - size += 100; -#endif - - ASSERT(ctx->Driver.BufferData); - if (!ctx->Driver.BufferData( ctx, target, size, data, usage, bufObj )) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBufferDataARB()"); - } -} - - -void GLAPIENTRY -_mesa_BufferSubDataARB(GLenum target, GLintptrARB offset, - GLsizeiptrARB size, const GLvoid * data) -{ - GET_CURRENT_CONTEXT(ctx); - struct gl_buffer_object *bufObj; - ASSERT_OUTSIDE_BEGIN_END(ctx); - - bufObj = buffer_object_subdata_range_good( ctx, target, offset, size, - "glBufferSubDataARB" ); - if (!bufObj) { - /* error already recorded */ - return; - } - - if (size == 0) - return; - - bufObj->Written = GL_TRUE; - - ASSERT(ctx->Driver.BufferSubData); - ctx->Driver.BufferSubData( ctx, target, offset, size, data, bufObj ); -} - - -void GLAPIENTRY -_mesa_GetBufferSubDataARB(GLenum target, GLintptrARB offset, - GLsizeiptrARB size, void * data) -{ - GET_CURRENT_CONTEXT(ctx); - struct gl_buffer_object *bufObj; - ASSERT_OUTSIDE_BEGIN_END(ctx); - - bufObj = buffer_object_subdata_range_good( ctx, target, offset, size, - "glGetBufferSubDataARB" ); - if (!bufObj) { - /* error already recorded */ - return; - } - - ASSERT(ctx->Driver.GetBufferSubData); - ctx->Driver.GetBufferSubData( ctx, target, offset, size, data, bufObj ); -} - - -void * GLAPIENTRY -_mesa_MapBufferARB(GLenum target, GLenum access) -{ - GET_CURRENT_CONTEXT(ctx); - struct gl_buffer_object * bufObj; - GLbitfield accessFlags; - void *map; - - ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, NULL); - - switch (access) { - case GL_READ_ONLY_ARB: - accessFlags = GL_MAP_READ_BIT; - break; - case GL_WRITE_ONLY_ARB: - accessFlags = GL_MAP_WRITE_BIT; - break; - case GL_READ_WRITE_ARB: - accessFlags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT; - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, "glMapBufferARB(access)"); - return NULL; - } - - bufObj = get_buffer(ctx, target); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_ENUM, "glMapBufferARB(target)" ); - return NULL; - } - if (!_mesa_is_bufferobj(bufObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glMapBufferARB(buffer 0)" ); - return NULL; - } - if (_mesa_bufferobj_mapped(bufObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glMapBufferARB(already mapped)"); - return NULL; - } - - ASSERT(ctx->Driver.MapBuffer); - map = ctx->Driver.MapBuffer( ctx, target, access, bufObj ); - if (!map) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "glMapBufferARB(map failed)"); - return NULL; - } - else { - /* The driver callback should have set these fields. - * This is important because other modules (like VBO) might call - * the driver function directly. - */ - ASSERT(bufObj->Pointer == map); - ASSERT(bufObj->Length == bufObj->Size); - ASSERT(bufObj->Offset == 0); - bufObj->AccessFlags = accessFlags; - } - - if (access == GL_WRITE_ONLY_ARB || access == GL_READ_WRITE_ARB) - bufObj->Written = GL_TRUE; - -#ifdef VBO_DEBUG - printf("glMapBufferARB(%u, sz %ld, access 0x%x)\n", - bufObj->Name, bufObj->Size, access); - if (access == GL_WRITE_ONLY_ARB) { - GLuint i; - GLubyte *b = (GLubyte *) bufObj->Pointer; - for (i = 0; i < bufObj->Size; i++) - b[i] = i & 0xff; - } -#endif - -#ifdef BOUNDS_CHECK - { - GLubyte *buf = (GLubyte *) bufObj->Pointer; - GLuint i; - /* buffer is 100 bytes larger than requested, fill with magic value */ - for (i = 0; i < 100; i++) { - buf[bufObj->Size - i - 1] = 123; - } - } -#endif - - return bufObj->Pointer; -} - - -GLboolean GLAPIENTRY -_mesa_UnmapBufferARB(GLenum target) -{ - GET_CURRENT_CONTEXT(ctx); - struct gl_buffer_object *bufObj; - GLboolean status = GL_TRUE; - ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); - - bufObj = get_buffer(ctx, target); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_ENUM, "glUnmapBufferARB(target)" ); - return GL_FALSE; - } - if (!_mesa_is_bufferobj(bufObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glUnmapBufferARB" ); - return GL_FALSE; - } - if (!_mesa_bufferobj_mapped(bufObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glUnmapBufferARB"); - return GL_FALSE; - } - -#ifdef BOUNDS_CHECK - if (bufObj->Access != GL_READ_ONLY_ARB) { - GLubyte *buf = (GLubyte *) bufObj->Pointer; - GLuint i; - /* check that last 100 bytes are still = magic value */ - for (i = 0; i < 100; i++) { - GLuint pos = bufObj->Size - i - 1; - if (buf[pos] != 123) { - _mesa_warning(ctx, "Out of bounds buffer object write detected" - " at position %d (value = %u)\n", - pos, buf[pos]); - } - } - } -#endif - -#ifdef VBO_DEBUG - if (bufObj->AccessFlags & GL_MAP_WRITE_BIT) { - GLuint i, unchanged = 0; - GLubyte *b = (GLubyte *) bufObj->Pointer; - GLint pos = -1; - /* check which bytes changed */ - for (i = 0; i < bufObj->Size - 1; i++) { - if (b[i] == (i & 0xff) && b[i+1] == ((i+1) & 0xff)) { - unchanged++; - if (pos == -1) - pos = i; - } - } - if (unchanged) { - printf("glUnmapBufferARB(%u): %u of %ld unchanged, starting at %d\n", - bufObj->Name, unchanged, bufObj->Size, pos); - } - } -#endif - - status = ctx->Driver.UnmapBuffer( ctx, target, bufObj ); - bufObj->AccessFlags = DEFAULT_ACCESS; - ASSERT(bufObj->Pointer == NULL); - ASSERT(bufObj->Offset == 0); - ASSERT(bufObj->Length == 0); - - return status; -} - - -void GLAPIENTRY -_mesa_GetBufferParameterivARB(GLenum target, GLenum pname, GLint *params) -{ - GET_CURRENT_CONTEXT(ctx); - struct gl_buffer_object *bufObj; - ASSERT_OUTSIDE_BEGIN_END(ctx); - - bufObj = get_buffer(ctx, target); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameterivARB(target)" ); - return; - } - if (!_mesa_is_bufferobj(bufObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glGetBufferParameterivARB" ); - return; - } - - switch (pname) { - case GL_BUFFER_SIZE_ARB: - *params = (GLint) bufObj->Size; - return; - case GL_BUFFER_USAGE_ARB: - *params = bufObj->Usage; - return; - case GL_BUFFER_ACCESS_ARB: - *params = simplified_access_mode(bufObj->AccessFlags); - return; - case GL_BUFFER_MAPPED_ARB: - *params = _mesa_bufferobj_mapped(bufObj); - return; - case GL_BUFFER_ACCESS_FLAGS: - if (ctx->VersionMajor < 3) - goto invalid_pname; - *params = bufObj->AccessFlags; - return; - case GL_BUFFER_MAP_OFFSET: - if (ctx->VersionMajor < 3) - goto invalid_pname; - *params = (GLint) bufObj->Offset; - return; - case GL_BUFFER_MAP_LENGTH: - if (ctx->VersionMajor < 3) - goto invalid_pname; - *params = (GLint) bufObj->Length; - return; - default: - ; /* fall-through */ - } - -invalid_pname: - _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameterivARB(pname=%s)", - _mesa_lookup_enum_by_nr(pname)); -} - - -/** - * New in GL 3.2 - * This is pretty much a duplicate of GetBufferParameteriv() but the - * GL_BUFFER_SIZE_ARB attribute will be 64-bits on a 64-bit system. - */ -void GLAPIENTRY -_mesa_GetBufferParameteri64v(GLenum target, GLenum pname, GLint64 *params) -{ - GET_CURRENT_CONTEXT(ctx); - struct gl_buffer_object *bufObj; - ASSERT_OUTSIDE_BEGIN_END(ctx); - - bufObj = get_buffer(ctx, target); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameteri64v(target)" ); - return; - } - if (!_mesa_is_bufferobj(bufObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glGetBufferParameteri64v" ); - return; - } - - switch (pname) { - case GL_BUFFER_SIZE_ARB: - *params = bufObj->Size; - return; - case GL_BUFFER_USAGE_ARB: - *params = bufObj->Usage; - return; - case GL_BUFFER_ACCESS_ARB: - *params = simplified_access_mode(bufObj->AccessFlags); - return; - case GL_BUFFER_ACCESS_FLAGS: - if (ctx->VersionMajor < 3) - goto invalid_pname; - *params = bufObj->AccessFlags; - return; - case GL_BUFFER_MAPPED_ARB: - *params = _mesa_bufferobj_mapped(bufObj); - return; - case GL_BUFFER_MAP_OFFSET: - if (ctx->VersionMajor < 3) - goto invalid_pname; - *params = bufObj->Offset; - return; - case GL_BUFFER_MAP_LENGTH: - if (ctx->VersionMajor < 3) - goto invalid_pname; - *params = bufObj->Length; - return; - default: - ; /* fall-through */ - } - -invalid_pname: - _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameteri64v(pname=%s)", - _mesa_lookup_enum_by_nr(pname)); -} - - -void GLAPIENTRY -_mesa_GetBufferPointervARB(GLenum target, GLenum pname, GLvoid **params) -{ - GET_CURRENT_CONTEXT(ctx); - struct gl_buffer_object * bufObj; - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (pname != GL_BUFFER_MAP_POINTER_ARB) { - _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferPointervARB(pname)"); - return; - } - - bufObj = get_buffer(ctx, target); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferPointervARB(target)" ); - return; - } - if (!_mesa_is_bufferobj(bufObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glGetBufferPointervARB" ); - return; - } - - *params = bufObj->Pointer; -} - - -void GLAPIENTRY -_mesa_CopyBufferSubData(GLenum readTarget, GLenum writeTarget, - GLintptr readOffset, GLintptr writeOffset, - GLsizeiptr size) -{ - GET_CURRENT_CONTEXT(ctx); - struct gl_buffer_object *src, *dst; - ASSERT_OUTSIDE_BEGIN_END(ctx); - - src = get_buffer(ctx, readTarget); - if (!src || !_mesa_is_bufferobj(src)) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glCopyBuffserSubData(readTarget = 0x%x)", readTarget); - return; - } - - dst = get_buffer(ctx, writeTarget); - if (!dst || !_mesa_is_bufferobj(dst)) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glCopyBuffserSubData(writeTarget = 0x%x)", writeTarget); - return; - } - - if (_mesa_bufferobj_mapped(src)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glCopyBuffserSubData(readBuffer is mapped)"); - return; - } - - if (_mesa_bufferobj_mapped(dst)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glCopyBuffserSubData(writeBuffer is mapped)"); - return; - } - - if (readOffset < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyBuffserSubData(readOffset = %d)", (int) readOffset); - return; - } - - if (writeOffset < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyBuffserSubData(writeOffset = %d)", (int) writeOffset); - return; - } - - if (readOffset + size > src->Size) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyBuffserSubData(readOffset + size = %d)", - (int) (readOffset + size)); - return; - } - - if (writeOffset + size > dst->Size) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyBuffserSubData(writeOffset + size = %d)", - (int) (writeOffset + size)); - return; - } - - if (src == dst) { - if (readOffset + size <= writeOffset) { - /* OK */ - } - else if (writeOffset + size <= readOffset) { - /* OK */ - } - else { - /* overlapping src/dst is illegal */ - _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyBuffserSubData(overlapping src/dst)"); - return; - } - } - - ctx->Driver.CopyBufferSubData(ctx, src, dst, readOffset, writeOffset, size); -} - - -/** - * See GL_ARB_map_buffer_range spec - */ -void * GLAPIENTRY -_mesa_MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, - GLbitfield access) -{ - GET_CURRENT_CONTEXT(ctx); - struct gl_buffer_object *bufObj; - void *map; - - ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, NULL); - - if (!ctx->Extensions.ARB_map_buffer_range) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glMapBufferRange(extension not supported)"); - return NULL; - } - - if (offset < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glMapBufferRange(offset = %ld)", (long)offset); - return NULL; - } - - if (length < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glMapBufferRange(length = %ld)", (long)length); - return NULL; - } - - if ((access & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) == 0) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glMapBufferRange(access indicates neither read or write)"); - return NULL; - } - - if (access & GL_MAP_READ_BIT) { - if ((access & GL_MAP_INVALIDATE_RANGE_BIT) || - (access & GL_MAP_INVALIDATE_BUFFER_BIT) || - (access & GL_MAP_UNSYNCHRONIZED_BIT)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glMapBufferRange(invalid access flags)"); - return NULL; - } - } - - if ((access & GL_MAP_FLUSH_EXPLICIT_BIT) && - ((access & GL_MAP_WRITE_BIT) == 0)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glMapBufferRange(invalid access flags)"); - return NULL; - } - - bufObj = get_buffer(ctx, target); - if (!bufObj || !_mesa_is_bufferobj(bufObj)) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glMapBufferRange(target = 0x%x)", target); - return NULL; - } - - if (offset + length > bufObj->Size) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glMapBufferRange(offset + length > size)"); - return NULL; - } - - if (_mesa_bufferobj_mapped(bufObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glMapBufferRange(buffer already mapped)"); - return NULL; - } - - ASSERT(ctx->Driver.MapBufferRange); - map = ctx->Driver.MapBufferRange(ctx, target, offset, length, - access, bufObj); - if (!map) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "glMapBufferARB(map failed)"); - } - else { - /* The driver callback should have set all these fields. - * This is important because other modules (like VBO) might call - * the driver function directly. - */ - ASSERT(bufObj->Pointer == map); - ASSERT(bufObj->Length == length); - ASSERT(bufObj->Offset == offset); - ASSERT(bufObj->AccessFlags == access); - } - - return map; -} - - -/** - * See GL_ARB_map_buffer_range spec - */ -void GLAPIENTRY -_mesa_FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length) -{ - GET_CURRENT_CONTEXT(ctx); - struct gl_buffer_object *bufObj; - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (!ctx->Extensions.ARB_map_buffer_range) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glMapBufferRange(extension not supported)"); - return; - } - - if (offset < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glMapBufferRange(offset = %ld)", (long)offset); - return; - } - - if (length < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glMapBufferRange(length = %ld)", (long)length); - return; - } - - bufObj = get_buffer(ctx, target); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glMapBufferRange(target = 0x%x)", target); - return; - } - - if (!_mesa_is_bufferobj(bufObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glMapBufferRange(current buffer is 0)"); - return; - } - - if (!_mesa_bufferobj_mapped(bufObj)) { - /* buffer is not mapped */ - _mesa_error(ctx, GL_INVALID_OPERATION, - "glMapBufferRange(buffer is not mapped)"); - return; - } - - if ((bufObj->AccessFlags & GL_MAP_FLUSH_EXPLICIT_BIT) == 0) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glMapBufferRange(GL_MAP_FLUSH_EXPLICIT_BIT not set)"); - return; - } - - if (offset + length > bufObj->Length) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glMapBufferRange(offset %ld + length %ld > mapped length %ld)", - (long)offset, (long)length, (long)bufObj->Length); - return; - } - - ASSERT(bufObj->AccessFlags & GL_MAP_WRITE_BIT); - - if (ctx->Driver.FlushMappedBufferRange) - ctx->Driver.FlushMappedBufferRange(ctx, target, offset, length, bufObj); -} - - -#if FEATURE_APPLE_object_purgeable -static GLenum -_mesa_BufferObjectPurgeable(struct gl_context *ctx, GLuint name, GLenum option) -{ - struct gl_buffer_object *bufObj; - GLenum retval; - - bufObj = _mesa_lookup_bufferobj(ctx, name); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glObjectPurgeable(name = 0x%x)", name); - return 0; - } - if (!_mesa_is_bufferobj(bufObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glObjectPurgeable(buffer 0)" ); - return 0; - } - - if (bufObj->Purgeable) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glObjectPurgeable(name = 0x%x) is already purgeable", name); - return GL_VOLATILE_APPLE; - } - - bufObj->Purgeable = GL_TRUE; - - retval = GL_VOLATILE_APPLE; - if (ctx->Driver.BufferObjectPurgeable) - retval = ctx->Driver.BufferObjectPurgeable(ctx, bufObj, option); - - return retval; -} - - -static GLenum -_mesa_RenderObjectPurgeable(struct gl_context *ctx, GLuint name, GLenum option) -{ - struct gl_renderbuffer *bufObj; - GLenum retval; - - bufObj = _mesa_lookup_renderbuffer(ctx, name); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glObjectUnpurgeable(name = 0x%x)", name); - return 0; - } - - if (bufObj->Purgeable) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glObjectPurgeable(name = 0x%x) is already purgeable", name); - return GL_VOLATILE_APPLE; - } - - bufObj->Purgeable = GL_TRUE; - - retval = GL_VOLATILE_APPLE; - if (ctx->Driver.RenderObjectPurgeable) - retval = ctx->Driver.RenderObjectPurgeable(ctx, bufObj, option); - - return retval; -} - - -static GLenum -_mesa_TextureObjectPurgeable(struct gl_context *ctx, GLuint name, GLenum option) -{ - struct gl_texture_object *bufObj; - GLenum retval; - - bufObj = _mesa_lookup_texture(ctx, name); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glObjectPurgeable(name = 0x%x)", name); - return 0; - } - - if (bufObj->Purgeable) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glObjectPurgeable(name = 0x%x) is already purgeable", name); - return GL_VOLATILE_APPLE; - } - - bufObj->Purgeable = GL_TRUE; - - retval = GL_VOLATILE_APPLE; - if (ctx->Driver.TextureObjectPurgeable) - retval = ctx->Driver.TextureObjectPurgeable(ctx, bufObj, option); - - return retval; -} - - -GLenum GLAPIENTRY -_mesa_ObjectPurgeableAPPLE(GLenum objectType, GLuint name, GLenum option) -{ - GLenum retval; - - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0); - - if (name == 0) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glObjectPurgeable(name = 0x%x)", name); - return 0; - } - - switch (option) { - case GL_VOLATILE_APPLE: - case GL_RELEASED_APPLE: - /* legal */ - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, - "glObjectPurgeable(name = 0x%x) invalid option: %d", - name, option); - return 0; - } - - switch (objectType) { - case GL_TEXTURE: - retval = _mesa_TextureObjectPurgeable (ctx, name, option); - break; - case GL_RENDERBUFFER_EXT: - retval = _mesa_RenderObjectPurgeable (ctx, name, option); - break; - case GL_BUFFER_OBJECT_APPLE: - retval = _mesa_BufferObjectPurgeable (ctx, name, option); - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, - "glObjectPurgeable(name = 0x%x) invalid type: %d", - name, objectType); - return 0; - } - - /* In strict conformance to the spec, we must only return VOLATILE when - * when passed the VOLATILE option. Madness. - * - * XXX First fix the spec, then fix me. - */ - return option == GL_VOLATILE_APPLE ? GL_VOLATILE_APPLE : retval; -} - - -static GLenum -_mesa_BufferObjectUnpurgeable(struct gl_context *ctx, GLuint name, GLenum option) -{ - struct gl_buffer_object *bufObj; - GLenum retval; - - bufObj = _mesa_lookup_bufferobj(ctx, name); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glObjectUnpurgeable(name = 0x%x)", name); - return 0; - } - - if (! bufObj->Purgeable) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glObjectUnpurgeable(name = 0x%x) object is " - " already \"unpurged\"", name); - return 0; - } - - bufObj->Purgeable = GL_FALSE; - - retval = option; - if (ctx->Driver.BufferObjectUnpurgeable) - retval = ctx->Driver.BufferObjectUnpurgeable(ctx, bufObj, option); - - return retval; -} - - -static GLenum -_mesa_RenderObjectUnpurgeable(struct gl_context *ctx, GLuint name, GLenum option) -{ - struct gl_renderbuffer *bufObj; - GLenum retval; - - bufObj = _mesa_lookup_renderbuffer(ctx, name); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glObjectUnpurgeable(name = 0x%x)", name); - return 0; - } - - if (! bufObj->Purgeable) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glObjectUnpurgeable(name = 0x%x) object is " - " already \"unpurged\"", name); - return 0; - } - - bufObj->Purgeable = GL_FALSE; - - retval = option; - if (ctx->Driver.RenderObjectUnpurgeable) - retval = ctx->Driver.RenderObjectUnpurgeable(ctx, bufObj, option); - - return retval; -} - - -static GLenum -_mesa_TextureObjectUnpurgeable(struct gl_context *ctx, GLuint name, GLenum option) -{ - struct gl_texture_object *bufObj; - GLenum retval; - - bufObj = _mesa_lookup_texture(ctx, name); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glObjectUnpurgeable(name = 0x%x)", name); - return 0; - } - - if (! bufObj->Purgeable) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glObjectUnpurgeable(name = 0x%x) object is" - " already \"unpurged\"", name); - return 0; - } - - bufObj->Purgeable = GL_FALSE; - - retval = option; - if (ctx->Driver.TextureObjectUnpurgeable) - retval = ctx->Driver.TextureObjectUnpurgeable(ctx, bufObj, option); - - return retval; -} - - -GLenum GLAPIENTRY -_mesa_ObjectUnpurgeableAPPLE(GLenum objectType, GLuint name, GLenum option) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0); - - if (name == 0) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glObjectUnpurgeable(name = 0x%x)", name); - return 0; - } - - switch (option) { - case GL_RETAINED_APPLE: - case GL_UNDEFINED_APPLE: - /* legal */ - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, - "glObjectUnpurgeable(name = 0x%x) invalid option: %d", - name, option); - return 0; - } - - switch (objectType) { - case GL_BUFFER_OBJECT_APPLE: - return _mesa_BufferObjectUnpurgeable(ctx, name, option); - case GL_TEXTURE: - return _mesa_TextureObjectUnpurgeable(ctx, name, option); - case GL_RENDERBUFFER_EXT: - return _mesa_RenderObjectUnpurgeable(ctx, name, option); - default: - _mesa_error(ctx, GL_INVALID_ENUM, - "glObjectUnpurgeable(name = 0x%x) invalid type: %d", - name, objectType); - return 0; - } -} - - -static void -_mesa_GetBufferObjectParameterivAPPLE(struct gl_context *ctx, GLuint name, - GLenum pname, GLint* params) -{ - struct gl_buffer_object *bufObj; - - bufObj = _mesa_lookup_bufferobj(ctx, name); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glGetObjectParameteriv(name = 0x%x) invalid object", name); - return; - } - - switch (pname) { - case GL_PURGEABLE_APPLE: - *params = bufObj->Purgeable; - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetObjectParameteriv(name = 0x%x) invalid enum: %d", - name, pname); - break; - } -} - - -static void -_mesa_GetRenderObjectParameterivAPPLE(struct gl_context *ctx, GLuint name, - GLenum pname, GLint* params) -{ - struct gl_renderbuffer *bufObj; - - bufObj = _mesa_lookup_renderbuffer(ctx, name); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glObjectUnpurgeable(name = 0x%x)", name); - return; - } - - switch (pname) { - case GL_PURGEABLE_APPLE: - *params = bufObj->Purgeable; - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetObjectParameteriv(name = 0x%x) invalid enum: %d", - name, pname); - break; - } -} - - -static void -_mesa_GetTextureObjectParameterivAPPLE(struct gl_context *ctx, GLuint name, - GLenum pname, GLint* params) -{ - struct gl_texture_object *bufObj; - - bufObj = _mesa_lookup_texture(ctx, name); - if (!bufObj) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glObjectUnpurgeable(name = 0x%x)", name); - return; - } - - switch (pname) { - case GL_PURGEABLE_APPLE: - *params = bufObj->Purgeable; - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetObjectParameteriv(name = 0x%x) invalid enum: %d", - name, pname); - break; - } -} - - -void GLAPIENTRY -_mesa_GetObjectParameterivAPPLE(GLenum objectType, GLuint name, GLenum pname, - GLint* params) -{ - GET_CURRENT_CONTEXT(ctx); - - if (name == 0) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glGetObjectParameteriv(name = 0x%x)", name); - return; - } - - switch (objectType) { - case GL_TEXTURE: - _mesa_GetTextureObjectParameterivAPPLE (ctx, name, pname, params); - break; - case GL_BUFFER_OBJECT_APPLE: - _mesa_GetBufferObjectParameterivAPPLE (ctx, name, pname, params); - break; - case GL_RENDERBUFFER_EXT: - _mesa_GetRenderObjectParameterivAPPLE (ctx, name, pname, params); - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetObjectParameteriv(name = 0x%x) invalid type: %d", - name, objectType); - } -} - -#endif /* FEATURE_APPLE_object_purgeable */ +/* + * Mesa 3-D graphics library + * Version: 7.6 + * + * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. + * 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 + * BRIAN PAUL 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 bufferobj.c + * \brief Functions for the GL_ARB_vertex/pixel_buffer_object extensions. + * \author Brian Paul, Ian Romanick + */ + + +#include "glheader.h" +#include "enums.h" +#include "hash.h" +#include "imports.h" +#include "image.h" +#include "context.h" +#include "bufferobj.h" +#include "fbobject.h" +#include "mfeatures.h" +#include "mtypes.h" +#include "texobj.h" + + +/* Debug flags */ +/*#define VBO_DEBUG*/ +/*#define BOUNDS_CHECK*/ + + +#if FEATURE_OES_mapbuffer +#define DEFAULT_ACCESS GL_MAP_WRITE_BIT +#else +#define DEFAULT_ACCESS (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT) +#endif + + +/** + * Used as a placeholder for buffer objects between glGenBuffers() and + * glBindBuffer() so that glIsBuffer() can work correctly. + */ +static struct gl_buffer_object DummyBufferObject; + + +/** + * Return pointer to address of a buffer object target. + * \param ctx the GL context + * \param target the buffer object target to be retrieved. + * \return pointer to pointer to the buffer object bound to \c target in the + * specified context or \c NULL if \c target is invalid. + */ +static INLINE struct gl_buffer_object ** +get_buffer_target(struct gl_context *ctx, GLenum target) +{ + switch (target) { + case GL_ARRAY_BUFFER_ARB: + return &ctx->Array.ArrayBufferObj; + case GL_ELEMENT_ARRAY_BUFFER_ARB: + return &ctx->Array.ElementArrayBufferObj; + case GL_PIXEL_PACK_BUFFER_EXT: + return &ctx->Pack.BufferObj; + case GL_PIXEL_UNPACK_BUFFER_EXT: + return &ctx->Unpack.BufferObj; + case GL_COPY_READ_BUFFER: + return &ctx->CopyReadBuffer; + case GL_COPY_WRITE_BUFFER: + return &ctx->CopyWriteBuffer; +#if FEATURE_EXT_transform_feedback + case GL_TRANSFORM_FEEDBACK_BUFFER: + if (ctx->Extensions.EXT_transform_feedback) { + return &ctx->TransformFeedback.CurrentBuffer; + } + break; +#endif + default: + return NULL; + } + return NULL; +} + + +/** + * Get the buffer object bound to the specified target in a GL context. + * \param ctx the GL context + * \param target the buffer object target to be retrieved. + * \return pointer to the buffer object bound to \c target in the + * specified context or \c NULL if \c target is invalid. + */ +static INLINE struct gl_buffer_object * +get_buffer(struct gl_context *ctx, GLenum target) +{ + struct gl_buffer_object **bufObj = get_buffer_target(ctx, target); + if (bufObj) + return *bufObj; + return NULL; +} + + +/** + * Convert a GLbitfield describing the mapped buffer access flags + * into one of GL_READ_WRITE, GL_READ_ONLY, or GL_WRITE_ONLY. + */ +static GLenum +simplified_access_mode(GLbitfield access) +{ + const GLbitfield rwFlags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT; + if ((access & rwFlags) == rwFlags) + return GL_READ_WRITE; + if ((access & GL_MAP_READ_BIT) == GL_MAP_READ_BIT) + return GL_READ_ONLY; + if ((access & GL_MAP_WRITE_BIT) == GL_MAP_WRITE_BIT) + return GL_WRITE_ONLY; + return GL_READ_WRITE; /* this should never happen, but no big deal */ +} + + +/** + * Tests the subdata range parameters and sets the GL error code for + * \c glBufferSubDataARB and \c glGetBufferSubDataARB. + * + * \param ctx GL context. + * \param target Buffer object target on which to operate. + * \param offset Offset of the first byte of the subdata range. + * \param size Size, in bytes, of the subdata range. + * \param caller Name of calling function for recording errors. + * \return A pointer to the buffer object bound to \c target in the + * specified context or \c NULL if any of the parameter or state + * conditions for \c glBufferSubDataARB or \c glGetBufferSubDataARB + * are invalid. + * + * \sa glBufferSubDataARB, glGetBufferSubDataARB + */ +static struct gl_buffer_object * +buffer_object_subdata_range_good( struct gl_context * ctx, GLenum target, + GLintptrARB offset, GLsizeiptrARB size, + const char *caller ) +{ + struct gl_buffer_object *bufObj; + + if (size < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "%s(size < 0)", caller); + return NULL; + } + + if (offset < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "%s(offset < 0)", caller); + return NULL; + } + + bufObj = get_buffer(ctx, target); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_ENUM, "%s(target)", caller); + return NULL; + } + if (!_mesa_is_bufferobj(bufObj)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller); + return NULL; + } + if (offset + size > bufObj->Size) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(size + offset > buffer size)", caller); + return NULL; + } + if (_mesa_bufferobj_mapped(bufObj)) { + /* Buffer is currently mapped */ + _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller); + return NULL; + } + + return bufObj; +} + + +/** + * Allocate and initialize a new buffer object. + * + * Default callback for the \c dd_function_table::NewBufferObject() hook. + */ +static struct gl_buffer_object * +_mesa_new_buffer_object( struct gl_context *ctx, GLuint name, GLenum target ) +{ + struct gl_buffer_object *obj; + + (void) ctx; + + obj = MALLOC_STRUCT(gl_buffer_object); + _mesa_initialize_buffer_object(obj, name, target); + return obj; +} + + +/** + * Delete a buffer object. + * + * Default callback for the \c dd_function_table::DeleteBuffer() hook. + */ +static void +_mesa_delete_buffer_object( struct gl_context *ctx, struct gl_buffer_object *bufObj ) +{ + (void) ctx; + + if (bufObj->Data) + free(bufObj->Data); + + /* assign strange values here to help w/ debugging */ + bufObj->RefCount = -1000; + bufObj->Name = ~0; + + _glthread_DESTROY_MUTEX(bufObj->Mutex); + free(bufObj); +} + + + +/** + * Set ptr to bufObj w/ reference counting. + */ +void +_mesa_reference_buffer_object(struct gl_context *ctx, + struct gl_buffer_object **ptr, + struct gl_buffer_object *bufObj) +{ + if (*ptr == bufObj) + return; + + if (*ptr) { + /* Unreference the old buffer */ + GLboolean deleteFlag = GL_FALSE; + struct gl_buffer_object *oldObj = *ptr; + + _glthread_LOCK_MUTEX(oldObj->Mutex); + ASSERT(oldObj->RefCount > 0); + oldObj->RefCount--; +#if 0 + printf("BufferObj %p %d DECR to %d\n", + (void *) oldObj, oldObj->Name, oldObj->RefCount); +#endif + deleteFlag = (oldObj->RefCount == 0); + _glthread_UNLOCK_MUTEX(oldObj->Mutex); + + if (deleteFlag) { + + /* some sanity checking: don't delete a buffer still in use */ +#if 0 + /* unfortunately, these tests are invalid during context tear-down */ + ASSERT(ctx->Array.ArrayBufferObj != bufObj); + ASSERT(ctx->Array.ElementArrayBufferObj != bufObj); + ASSERT(ctx->Array.ArrayObj->Vertex.BufferObj != bufObj); +#endif + + ASSERT(ctx->Driver.DeleteBuffer); + ctx->Driver.DeleteBuffer(ctx, oldObj); + } + + *ptr = NULL; + } + ASSERT(!*ptr); + + if (bufObj) { + /* reference new buffer */ + _glthread_LOCK_MUTEX(bufObj->Mutex); + if (bufObj->RefCount == 0) { + /* this buffer's being deleted (look just above) */ + /* Not sure this can every really happen. Warn if it does. */ + _mesa_problem(NULL, "referencing deleted buffer object"); + *ptr = NULL; + } + else { + bufObj->RefCount++; +#if 0 + printf("BufferObj %p %d INCR to %d\n", + (void *) bufObj, bufObj->Name, bufObj->RefCount); +#endif + *ptr = bufObj; + } + _glthread_UNLOCK_MUTEX(bufObj->Mutex); + } +} + + +/** + * Initialize a buffer object to default values. + */ +void +_mesa_initialize_buffer_object( struct gl_buffer_object *obj, + GLuint name, GLenum target ) +{ + (void) target; + + memset(obj, 0, sizeof(struct gl_buffer_object)); + _glthread_INIT_MUTEX(obj->Mutex); + obj->RefCount = 1; + obj->Name = name; + obj->Usage = GL_STATIC_DRAW_ARB; + obj->AccessFlags = DEFAULT_ACCESS; +} + + +/** + * Allocate space for and store data in a buffer object. Any data that was + * previously stored in the buffer object is lost. If \c data is \c NULL, + * memory will be allocated, but no copy will occur. + * + * This is the default callback for \c dd_function_table::BufferData() + * Note that all GL error checking will have been done already. + * + * \param ctx GL context. + * \param target Buffer object target on which to operate. + * \param size Size, in bytes, of the new data store. + * \param data Pointer to the data to store in the buffer object. This + * pointer may be \c NULL. + * \param usage Hints about how the data will be used. + * \param bufObj Object to be used. + * + * \return GL_TRUE for success, GL_FALSE for failure + * \sa glBufferDataARB, dd_function_table::BufferData. + */ +static GLboolean +_mesa_buffer_data( struct gl_context *ctx, GLenum target, GLsizeiptrARB size, + const GLvoid * data, GLenum usage, + struct gl_buffer_object * bufObj ) +{ + void * new_data; + + (void) ctx; (void) target; + + new_data = _mesa_realloc( bufObj->Data, bufObj->Size, size ); + if (new_data) { + bufObj->Data = (GLubyte *) new_data; + bufObj->Size = size; + bufObj->Usage = usage; + + if (data) { + memcpy( bufObj->Data, data, size ); + } + + return GL_TRUE; + } + else { + return GL_FALSE; + } +} + + +/** + * Replace data in a subrange of buffer object. If the data range + * specified by \c size + \c offset extends beyond the end of the buffer or + * if \c data is \c NULL, no copy is performed. + * + * This is the default callback for \c dd_function_table::BufferSubData() + * Note that all GL error checking will have been done already. + * + * \param ctx GL context. + * \param target Buffer object target on which to operate. + * \param offset Offset of the first byte to be modified. + * \param size Size, in bytes, of the data range. + * \param data Pointer to the data to store in the buffer object. + * \param bufObj Object to be used. + * + * \sa glBufferSubDataARB, dd_function_table::BufferSubData. + */ +static void +_mesa_buffer_subdata( struct gl_context *ctx, GLenum target, GLintptrARB offset, + GLsizeiptrARB size, const GLvoid * data, + struct gl_buffer_object * bufObj ) +{ + (void) ctx; (void) target; + + /* this should have been caught in _mesa_BufferSubData() */ + ASSERT(size + offset <= bufObj->Size); + + if (bufObj->Data) { + memcpy( (GLubyte *) bufObj->Data + offset, data, size ); + } +} + + +/** + * Retrieve data from a subrange of buffer object. If the data range + * specified by \c size + \c offset extends beyond the end of the buffer or + * if \c data is \c NULL, no copy is performed. + * + * This is the default callback for \c dd_function_table::GetBufferSubData() + * Note that all GL error checking will have been done already. + * + * \param ctx GL context. + * \param target Buffer object target on which to operate. + * \param offset Offset of the first byte to be fetched. + * \param size Size, in bytes, of the data range. + * \param data Destination for data + * \param bufObj Object to be used. + * + * \sa glBufferGetSubDataARB, dd_function_table::GetBufferSubData. + */ +static void +_mesa_buffer_get_subdata( struct gl_context *ctx, GLenum target, GLintptrARB offset, + GLsizeiptrARB size, GLvoid * data, + struct gl_buffer_object * bufObj ) +{ + (void) ctx; (void) target; + + if (bufObj->Data && ((GLsizeiptrARB) (size + offset) <= bufObj->Size)) { + memcpy( data, (GLubyte *) bufObj->Data + offset, size ); + } +} + + +/** + * Default callback for \c dd_function_tabel::MapBuffer(). + * + * The function parameters will have been already tested for errors. + * + * \param ctx GL context. + * \param target Buffer object target on which to operate. + * \param access Information about how the buffer will be accessed. + * \param bufObj Object to be mapped. + * \return A pointer to the object's internal data store that can be accessed + * by the processor + * + * \sa glMapBufferARB, dd_function_table::MapBuffer + */ +static void * +_mesa_buffer_map( struct gl_context *ctx, GLenum target, GLenum access, + struct gl_buffer_object *bufObj ) +{ + (void) ctx; + (void) target; + (void) access; + /* Just return a direct pointer to the data */ + if (_mesa_bufferobj_mapped(bufObj)) { + /* already mapped! */ + return NULL; + } + bufObj->Pointer = bufObj->Data; + bufObj->Length = bufObj->Size; + bufObj->Offset = 0; + return bufObj->Pointer; +} + + +/** + * Default fallback for \c dd_function_table::MapBufferRange(). + * Called via glMapBufferRange(). + */ +static void * +_mesa_buffer_map_range( struct gl_context *ctx, GLenum target, GLintptr offset, + GLsizeiptr length, GLbitfield access, + struct gl_buffer_object *bufObj ) +{ + (void) ctx; + (void) target; + assert(!_mesa_bufferobj_mapped(bufObj)); + /* Just return a direct pointer to the data */ + bufObj->Pointer = bufObj->Data + offset; + bufObj->Length = length; + bufObj->Offset = offset; + bufObj->AccessFlags = access; + return bufObj->Pointer; +} + + +/** + * Default fallback for \c dd_function_table::FlushMappedBufferRange(). + * Called via glFlushMappedBufferRange(). + */ +static void +_mesa_buffer_flush_mapped_range( struct gl_context *ctx, GLenum target, + GLintptr offset, GLsizeiptr length, + struct gl_buffer_object *obj ) +{ + (void) ctx; + (void) target; + (void) offset; + (void) length; + (void) obj; + /* no-op */ +} + + +/** + * Default callback for \c dd_function_table::MapBuffer(). + * + * The input parameters will have been already tested for errors. + * + * \sa glUnmapBufferARB, dd_function_table::UnmapBuffer + */ +static GLboolean +_mesa_buffer_unmap( struct gl_context *ctx, GLenum target, + struct gl_buffer_object *bufObj ) +{ + (void) ctx; + (void) target; + /* XXX we might assert here that bufObj->Pointer is non-null */ + bufObj->Pointer = NULL; + bufObj->Length = 0; + bufObj->Offset = 0; + bufObj->AccessFlags = 0x0; + return GL_TRUE; +} + + +/** + * Default fallback for \c dd_function_table::CopyBufferSubData(). + * Called via glCopyBuffserSubData(). + */ +static void +_mesa_copy_buffer_subdata(struct gl_context *ctx, + struct gl_buffer_object *src, + struct gl_buffer_object *dst, + GLintptr readOffset, GLintptr writeOffset, + GLsizeiptr size) +{ + GLubyte *srcPtr, *dstPtr; + + /* buffer should not already be mapped */ + assert(!_mesa_bufferobj_mapped(src)); + assert(!_mesa_bufferobj_mapped(dst)); + + srcPtr = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_COPY_READ_BUFFER, + GL_READ_ONLY, src); + dstPtr = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_COPY_WRITE_BUFFER, + GL_WRITE_ONLY, dst); + + if (srcPtr && dstPtr) + memcpy(dstPtr + writeOffset, srcPtr + readOffset, size); + + ctx->Driver.UnmapBuffer(ctx, GL_COPY_READ_BUFFER, src); + ctx->Driver.UnmapBuffer(ctx, GL_COPY_WRITE_BUFFER, dst); +} + + + +/** + * Initialize the state associated with buffer objects + */ +void +_mesa_init_buffer_objects( struct gl_context *ctx ) +{ + memset(&DummyBufferObject, 0, sizeof(DummyBufferObject)); + DummyBufferObject.RefCount = 1000*1000*1000; /* never delete */ + + _mesa_reference_buffer_object(ctx, &ctx->Array.ArrayBufferObj, + ctx->Shared->NullBufferObj); + _mesa_reference_buffer_object(ctx, &ctx->Array.ElementArrayBufferObj, + ctx->Shared->NullBufferObj); + + _mesa_reference_buffer_object(ctx, &ctx->CopyReadBuffer, + ctx->Shared->NullBufferObj); + _mesa_reference_buffer_object(ctx, &ctx->CopyWriteBuffer, + ctx->Shared->NullBufferObj); +} + + +void +_mesa_free_buffer_objects( struct gl_context *ctx ) +{ + _mesa_reference_buffer_object(ctx, &ctx->Array.ArrayBufferObj, NULL); + _mesa_reference_buffer_object(ctx, &ctx->Array.ElementArrayBufferObj, NULL); + + _mesa_reference_buffer_object(ctx, &ctx->CopyReadBuffer, NULL); + _mesa_reference_buffer_object(ctx, &ctx->CopyWriteBuffer, NULL); +} + + +/** + * Bind the specified target to buffer for the specified context. + * Called by glBindBuffer() and other functions. + */ +static void +bind_buffer_object(struct gl_context *ctx, GLenum target, GLuint buffer) +{ + struct gl_buffer_object *oldBufObj; + struct gl_buffer_object *newBufObj = NULL; + struct gl_buffer_object **bindTarget = NULL; + + bindTarget = get_buffer_target(ctx, target); + if (!bindTarget) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBindBufferARB(target 0x%x)", target); + return; + } + + /* Get pointer to old buffer object (to be unbound) */ + oldBufObj = *bindTarget; + if (oldBufObj && oldBufObj->Name == buffer) + return; /* rebinding the same buffer object- no change */ + + /* + * Get pointer to new buffer object (newBufObj) + */ + if (buffer == 0) { + /* The spec says there's not a buffer object named 0, but we use + * one internally because it simplifies things. + */ + newBufObj = ctx->Shared->NullBufferObj; + } + else { + /* non-default buffer object */ + newBufObj = _mesa_lookup_bufferobj(ctx, buffer); + if (!newBufObj || newBufObj == &DummyBufferObject) { + /* If this is a new buffer object id, or one which was generated but + * never used before, allocate a buffer object now. + */ + ASSERT(ctx->Driver.NewBufferObject); + newBufObj = ctx->Driver.NewBufferObject(ctx, buffer, target); + if (!newBufObj) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindBufferARB"); + return; + } + _mesa_HashInsert(ctx->Shared->BufferObjects, buffer, newBufObj); + } + } + + /* bind new buffer */ + _mesa_reference_buffer_object(ctx, bindTarget, newBufObj); + + /* Pass BindBuffer call to device driver */ + if (ctx->Driver.BindBuffer) + ctx->Driver.BindBuffer( ctx, target, newBufObj ); +} + + +/** + * Update the default buffer objects in the given context to reference those + * specified in the shared state and release those referencing the old + * shared state. + */ +void +_mesa_update_default_objects_buffer_objects(struct gl_context *ctx) +{ + /* Bind the NullBufferObj to remove references to those + * in the shared context hash table. + */ + bind_buffer_object( ctx, GL_ARRAY_BUFFER_ARB, 0); + bind_buffer_object( ctx, GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + bind_buffer_object( ctx, GL_PIXEL_PACK_BUFFER_ARB, 0); + bind_buffer_object( ctx, GL_PIXEL_UNPACK_BUFFER_ARB, 0); +} + + +/** + * When we're about to read pixel data out of a PBO (via glDrawPixels, + * glTexImage, etc) or write data into a PBO (via glReadPixels, + * glGetTexImage, etc) we call this function to check that we're not + * going to read out of bounds. + * + * XXX This would also be a convenient time to check that the PBO isn't + * currently mapped. Whoever calls this function should check for that. + * Remember, we can't use a PBO when it's mapped! + * + * If we're not using a PBO, this is a no-op. + * + * \param width width of image to read/write + * \param height height of image to read/write + * \param depth depth of image to read/write + * \param format format of image to read/write + * \param type datatype of image to read/write + * \param ptr the user-provided pointer/offset + * \return GL_TRUE if the PBO access is OK, GL_FALSE if the access would + * go out of bounds. + */ +GLboolean +_mesa_validate_pbo_access(GLuint dimensions, + const struct gl_pixelstore_attrib *pack, + GLsizei width, GLsizei height, GLsizei depth, + GLenum format, GLenum type, const GLvoid *ptr) +{ + GLvoid *start, *end; + const GLubyte *sizeAddr; /* buffer size, cast to a pointer */ + + if (!_mesa_is_bufferobj(pack->BufferObj)) + return GL_TRUE; /* no PBO, OK */ + + if (pack->BufferObj->Size == 0) + /* no buffer! */ + return GL_FALSE; + + /* get address of first pixel we'll read */ + start = _mesa_image_address(dimensions, pack, ptr, width, height, + format, type, 0, 0, 0); + + /* get address just past the last pixel we'll read */ + end = _mesa_image_address(dimensions, pack, ptr, width, height, + format, type, depth-1, height-1, width); + + + sizeAddr = ((const GLubyte *) 0) + pack->BufferObj->Size; + + if ((const GLubyte *) start > sizeAddr) { + /* This will catch negative values / wrap-around */ + return GL_FALSE; + } + if ((const GLubyte *) end > sizeAddr) { + /* Image read goes beyond end of buffer */ + return GL_FALSE; + } + + /* OK! */ + return GL_TRUE; +} + + +/** + * For commands that read from a PBO (glDrawPixels, glTexImage, + * glPolygonStipple, etc), if we're reading from a PBO, map it read-only + * and return the pointer into the PBO. If we're not reading from a + * PBO, return \p src as-is. + * If non-null return, must call _mesa_unmap_pbo_source() when done. + * + * \return NULL if error, else pointer to start of data + */ +const GLvoid * +_mesa_map_pbo_source(struct gl_context *ctx, + const struct gl_pixelstore_attrib *unpack, + const GLvoid *src) +{ + const GLubyte *buf; + + if (_mesa_is_bufferobj(unpack->BufferObj)) { + /* unpack from PBO */ + buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT, + GL_READ_ONLY_ARB, + unpack->BufferObj); + if (!buf) + return NULL; + + buf = ADD_POINTERS(buf, src); + } + else { + /* unpack from normal memory */ + buf = src; + } + + return buf; +} + + +/** + * Combine PBO-read validation and mapping. + * If any GL errors are detected, they'll be recorded and NULL returned. + * \sa _mesa_validate_pbo_access + * \sa _mesa_map_pbo_source + * A call to this function should have a matching call to + * _mesa_unmap_pbo_source(). + */ +const GLvoid * +_mesa_map_validate_pbo_source(struct gl_context *ctx, + GLuint dimensions, + const struct gl_pixelstore_attrib *unpack, + GLsizei width, GLsizei height, GLsizei depth, + GLenum format, GLenum type, const GLvoid *ptr, + const char *where) +{ + ASSERT(dimensions == 1 || dimensions == 2 || dimensions == 3); + + if (!_mesa_is_bufferobj(unpack->BufferObj)) { + /* non-PBO access: no validation to be done */ + return ptr; + } + + if (!_mesa_validate_pbo_access(dimensions, unpack, + width, height, depth, format, type, ptr)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(out of bounds PBO access)", where); + return NULL; + } + + if (_mesa_bufferobj_mapped(unpack->BufferObj)) { + /* buffer is already mapped - that's an error */ + _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where); + return NULL; + } + + ptr = _mesa_map_pbo_source(ctx, unpack, ptr); + return ptr; +} + + +/** + * Counterpart to _mesa_map_pbo_source() + */ +void +_mesa_unmap_pbo_source(struct gl_context *ctx, + const struct gl_pixelstore_attrib *unpack) +{ + ASSERT(unpack != &ctx->Pack); /* catch pack/unpack mismatch */ + if (_mesa_is_bufferobj(unpack->BufferObj)) { + ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT, + unpack->BufferObj); + } +} + + +/** + * For commands that write to a PBO (glReadPixels, glGetColorTable, etc), + * if we're writing to a PBO, map it write-only and return the pointer + * into the PBO. If we're not writing to a PBO, return \p dst as-is. + * If non-null return, must call _mesa_unmap_pbo_dest() when done. + * + * \return NULL if error, else pointer to start of data + */ +void * +_mesa_map_pbo_dest(struct gl_context *ctx, + const struct gl_pixelstore_attrib *pack, + GLvoid *dest) +{ + void *buf; + + if (_mesa_is_bufferobj(pack->BufferObj)) { + /* pack into PBO */ + buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT, + GL_WRITE_ONLY_ARB, + pack->BufferObj); + if (!buf) + return NULL; + + buf = ADD_POINTERS(buf, dest); + } + else { + /* pack to normal memory */ + buf = dest; + } + + return buf; +} + + +/** + * Combine PBO-write validation and mapping. + * If any GL errors are detected, they'll be recorded and NULL returned. + * \sa _mesa_validate_pbo_access + * \sa _mesa_map_pbo_dest + * A call to this function should have a matching call to + * _mesa_unmap_pbo_dest(). + */ +GLvoid * +_mesa_map_validate_pbo_dest(struct gl_context *ctx, + GLuint dimensions, + const struct gl_pixelstore_attrib *unpack, + GLsizei width, GLsizei height, GLsizei depth, + GLenum format, GLenum type, GLvoid *ptr, + const char *where) +{ + ASSERT(dimensions == 1 || dimensions == 2 || dimensions == 3); + + if (!_mesa_is_bufferobj(unpack->BufferObj)) { + /* non-PBO access: no validation to be done */ + return ptr; + } + + if (!_mesa_validate_pbo_access(dimensions, unpack, + width, height, depth, format, type, ptr)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "%s(out of bounds PBO access)", where); + return NULL; + } + + if (_mesa_bufferobj_mapped(unpack->BufferObj)) { + /* buffer is already mapped - that's an error */ + _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where); + return NULL; + } + + ptr = _mesa_map_pbo_dest(ctx, unpack, ptr); + return ptr; +} + + +/** + * Counterpart to _mesa_map_pbo_dest() + */ +void +_mesa_unmap_pbo_dest(struct gl_context *ctx, + const struct gl_pixelstore_attrib *pack) +{ + ASSERT(pack != &ctx->Unpack); /* catch pack/unpack mismatch */ + if (_mesa_is_bufferobj(pack->BufferObj)) { + ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT, pack->BufferObj); + } +} + + + +/** + * Return the gl_buffer_object for the given ID. + * Always return NULL for ID 0. + */ +struct gl_buffer_object * +_mesa_lookup_bufferobj(struct gl_context *ctx, GLuint buffer) +{ + if (buffer == 0) + return NULL; + else + return (struct gl_buffer_object *) + _mesa_HashLookup(ctx->Shared->BufferObjects, buffer); +} + + +/** + * If *ptr points to obj, set ptr = the Null/default buffer object. + * This is a helper for buffer object deletion. + * The GL spec says that deleting a buffer object causes it to get + * unbound from all arrays in the current context. + */ +static void +unbind(struct gl_context *ctx, + struct gl_buffer_object **ptr, + struct gl_buffer_object *obj) +{ + if (*ptr == obj) { + _mesa_reference_buffer_object(ctx, ptr, ctx->Shared->NullBufferObj); + } +} + + +/** + * Plug default/fallback buffer object functions into the device + * driver hooks. + */ +void +_mesa_init_buffer_object_functions(struct dd_function_table *driver) +{ + /* GL_ARB_vertex/pixel_buffer_object */ + driver->NewBufferObject = _mesa_new_buffer_object; + driver->DeleteBuffer = _mesa_delete_buffer_object; + driver->BindBuffer = NULL; + driver->BufferData = _mesa_buffer_data; + driver->BufferSubData = _mesa_buffer_subdata; + driver->GetBufferSubData = _mesa_buffer_get_subdata; + driver->MapBuffer = _mesa_buffer_map; + driver->UnmapBuffer = _mesa_buffer_unmap; + + /* GL_ARB_map_buffer_range */ + driver->MapBufferRange = _mesa_buffer_map_range; + driver->FlushMappedBufferRange = _mesa_buffer_flush_mapped_range; + + /* GL_ARB_copy_buffer */ + driver->CopyBufferSubData = _mesa_copy_buffer_subdata; +} + + + +/**********************************************************************/ +/* API Functions */ +/**********************************************************************/ + +void GLAPIENTRY +_mesa_BindBufferARB(GLenum target, GLuint buffer) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glBindBuffer(%s, %u)\n", + _mesa_lookup_enum_by_nr(target), buffer); + + bind_buffer_object(ctx, target, buffer); +} + + +/** + * Delete a set of buffer objects. + * + * \param n Number of buffer objects to delete. + * \param ids Array of \c n buffer object IDs. + */ +void GLAPIENTRY +_mesa_DeleteBuffersARB(GLsizei n, const GLuint *ids) +{ + GET_CURRENT_CONTEXT(ctx); + GLsizei i; + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (n < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteBuffersARB(n)"); + return; + } + + _glthread_LOCK_MUTEX(ctx->Shared->Mutex); + + for (i = 0; i < n; i++) { + struct gl_buffer_object *bufObj = _mesa_lookup_bufferobj(ctx, ids[i]); + if (bufObj) { + struct gl_array_object *arrayObj = ctx->Array.ArrayObj; + GLuint j; + + ASSERT(bufObj->Name == ids[i] || bufObj == &DummyBufferObject); + + if (_mesa_bufferobj_mapped(bufObj)) { + /* if mapped, unmap it now */ + ctx->Driver.UnmapBuffer(ctx, 0, bufObj); + bufObj->AccessFlags = DEFAULT_ACCESS; + bufObj->Pointer = NULL; + } + + /* unbind any vertex pointers bound to this buffer */ + unbind(ctx, &arrayObj->Vertex.BufferObj, bufObj); + unbind(ctx, &arrayObj->Weight.BufferObj, bufObj); + unbind(ctx, &arrayObj->Normal.BufferObj, bufObj); + unbind(ctx, &arrayObj->Color.BufferObj, bufObj); + unbind(ctx, &arrayObj->SecondaryColor.BufferObj, bufObj); + unbind(ctx, &arrayObj->FogCoord.BufferObj, bufObj); + unbind(ctx, &arrayObj->Index.BufferObj, bufObj); + unbind(ctx, &arrayObj->EdgeFlag.BufferObj, bufObj); + for (j = 0; j < Elements(arrayObj->TexCoord); j++) { + unbind(ctx, &arrayObj->TexCoord[j].BufferObj, bufObj); + } + for (j = 0; j < Elements(arrayObj->VertexAttrib); j++) { + unbind(ctx, &arrayObj->VertexAttrib[j].BufferObj, bufObj); + } + + if (ctx->Array.ArrayBufferObj == bufObj) { + _mesa_BindBufferARB( GL_ARRAY_BUFFER_ARB, 0 ); + } + if (ctx->Array.ElementArrayBufferObj == bufObj) { + _mesa_BindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 ); + } + + /* unbind any pixel pack/unpack pointers bound to this buffer */ + if (ctx->Pack.BufferObj == bufObj) { + _mesa_BindBufferARB( GL_PIXEL_PACK_BUFFER_EXT, 0 ); + } + if (ctx->Unpack.BufferObj == bufObj) { + _mesa_BindBufferARB( GL_PIXEL_UNPACK_BUFFER_EXT, 0 ); + } + + /* The ID is immediately freed for re-use */ + _mesa_HashRemove(ctx->Shared->BufferObjects, ids[i]); + _mesa_reference_buffer_object(ctx, &bufObj, NULL); + } + } + + _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); +} + + +/** + * Generate a set of unique buffer object IDs and store them in \c buffer. + * + * \param n Number of IDs to generate. + * \param buffer Array of \c n locations to store the IDs. + */ +void GLAPIENTRY +_mesa_GenBuffersARB(GLsizei n, GLuint *buffer) +{ + GET_CURRENT_CONTEXT(ctx); + GLuint first; + GLint i; + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glGenBuffers(%d)\n", n); + + if (n < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "glGenBuffersARB"); + return; + } + + if (!buffer) { + return; + } + + /* + * This must be atomic (generation and allocation of buffer object IDs) + */ + _glthread_LOCK_MUTEX(ctx->Shared->Mutex); + + first = _mesa_HashFindFreeKeyBlock(ctx->Shared->BufferObjects, n); + + /* Insert the ID and pointer to dummy buffer object into hash table */ + for (i = 0; i < n; i++) { + _mesa_HashInsert(ctx->Shared->BufferObjects, first + i, + &DummyBufferObject); + buffer[i] = first + i; + } + + _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); +} + + +/** + * Determine if ID is the name of a buffer object. + * + * \param id ID of the potential buffer object. + * \return \c GL_TRUE if \c id is the name of a buffer object, + * \c GL_FALSE otherwise. + */ +GLboolean GLAPIENTRY +_mesa_IsBufferARB(GLuint id) +{ + struct gl_buffer_object *bufObj; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); + + _glthread_LOCK_MUTEX(ctx->Shared->Mutex); + bufObj = _mesa_lookup_bufferobj(ctx, id); + _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); + + return bufObj && bufObj != &DummyBufferObject; +} + + +void GLAPIENTRY +_mesa_BufferDataARB(GLenum target, GLsizeiptrARB size, + const GLvoid * data, GLenum usage) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_buffer_object *bufObj; + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glBufferData(%s, %ld, %p, %s)\n", + _mesa_lookup_enum_by_nr(target), + (long int) size, data, + _mesa_lookup_enum_by_nr(usage)); + + if (size < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "glBufferDataARB(size < 0)"); + return; + } + + switch (usage) { + case GL_STREAM_DRAW_ARB: + case GL_STREAM_READ_ARB: + case GL_STREAM_COPY_ARB: + case GL_STATIC_DRAW_ARB: + case GL_STATIC_READ_ARB: + case GL_STATIC_COPY_ARB: + case GL_DYNAMIC_DRAW_ARB: + case GL_DYNAMIC_READ_ARB: + case GL_DYNAMIC_COPY_ARB: + /* OK */ + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, "glBufferDataARB(usage)"); + return; + } + + bufObj = get_buffer(ctx, target); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBufferDataARB(target)" ); + return; + } + if (!_mesa_is_bufferobj(bufObj)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glBufferDataARB(buffer 0)" ); + return; + } + + if (_mesa_bufferobj_mapped(bufObj)) { + /* Unmap the existing buffer. We'll replace it now. Not an error. */ + ctx->Driver.UnmapBuffer(ctx, target, bufObj); + bufObj->AccessFlags = DEFAULT_ACCESS; + ASSERT(bufObj->Pointer == NULL); + } + + FLUSH_VERTICES(ctx, _NEW_BUFFER_OBJECT); + + bufObj->Written = GL_TRUE; + +#ifdef VBO_DEBUG + printf("glBufferDataARB(%u, sz %ld, from %p, usage 0x%x)\n", + bufObj->Name, size, data, usage); +#endif + +#ifdef BOUNDS_CHECK + size += 100; +#endif + + ASSERT(ctx->Driver.BufferData); + if (!ctx->Driver.BufferData( ctx, target, size, data, usage, bufObj )) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBufferDataARB()"); + } +} + + +void GLAPIENTRY +_mesa_BufferSubDataARB(GLenum target, GLintptrARB offset, + GLsizeiptrARB size, const GLvoid * data) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_buffer_object *bufObj; + ASSERT_OUTSIDE_BEGIN_END(ctx); + + bufObj = buffer_object_subdata_range_good( ctx, target, offset, size, + "glBufferSubDataARB" ); + if (!bufObj) { + /* error already recorded */ + return; + } + + if (size == 0) + return; + + bufObj->Written = GL_TRUE; + + ASSERT(ctx->Driver.BufferSubData); + ctx->Driver.BufferSubData( ctx, target, offset, size, data, bufObj ); +} + + +void GLAPIENTRY +_mesa_GetBufferSubDataARB(GLenum target, GLintptrARB offset, + GLsizeiptrARB size, void * data) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_buffer_object *bufObj; + ASSERT_OUTSIDE_BEGIN_END(ctx); + + bufObj = buffer_object_subdata_range_good( ctx, target, offset, size, + "glGetBufferSubDataARB" ); + if (!bufObj) { + /* error already recorded */ + return; + } + + ASSERT(ctx->Driver.GetBufferSubData); + ctx->Driver.GetBufferSubData( ctx, target, offset, size, data, bufObj ); +} + + +void * GLAPIENTRY +_mesa_MapBufferARB(GLenum target, GLenum access) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_buffer_object * bufObj; + GLbitfield accessFlags; + void *map; + + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, NULL); + + switch (access) { + case GL_READ_ONLY_ARB: + accessFlags = GL_MAP_READ_BIT; + break; + case GL_WRITE_ONLY_ARB: + accessFlags = GL_MAP_WRITE_BIT; + break; + case GL_READ_WRITE_ARB: + accessFlags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, "glMapBufferARB(access)"); + return NULL; + } + + bufObj = get_buffer(ctx, target); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_ENUM, "glMapBufferARB(target)" ); + return NULL; + } + if (!_mesa_is_bufferobj(bufObj)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glMapBufferARB(buffer 0)" ); + return NULL; + } + if (_mesa_bufferobj_mapped(bufObj)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glMapBufferARB(already mapped)"); + return NULL; + } + + ASSERT(ctx->Driver.MapBuffer); + map = ctx->Driver.MapBuffer( ctx, target, access, bufObj ); + if (!map) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glMapBufferARB(map failed)"); + return NULL; + } + else { + /* The driver callback should have set these fields. + * This is important because other modules (like VBO) might call + * the driver function directly. + */ + ASSERT(bufObj->Pointer == map); + ASSERT(bufObj->Length == bufObj->Size); + ASSERT(bufObj->Offset == 0); + bufObj->AccessFlags = accessFlags; + } + + if (access == GL_WRITE_ONLY_ARB || access == GL_READ_WRITE_ARB) + bufObj->Written = GL_TRUE; + +#ifdef VBO_DEBUG + printf("glMapBufferARB(%u, sz %ld, access 0x%x)\n", + bufObj->Name, bufObj->Size, access); + if (access == GL_WRITE_ONLY_ARB) { + GLuint i; + GLubyte *b = (GLubyte *) bufObj->Pointer; + for (i = 0; i < bufObj->Size; i++) + b[i] = i & 0xff; + } +#endif + +#ifdef BOUNDS_CHECK + { + GLubyte *buf = (GLubyte *) bufObj->Pointer; + GLuint i; + /* buffer is 100 bytes larger than requested, fill with magic value */ + for (i = 0; i < 100; i++) { + buf[bufObj->Size - i - 1] = 123; + } + } +#endif + + return bufObj->Pointer; +} + + +GLboolean GLAPIENTRY +_mesa_UnmapBufferARB(GLenum target) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_buffer_object *bufObj; + GLboolean status = GL_TRUE; + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); + + bufObj = get_buffer(ctx, target); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_ENUM, "glUnmapBufferARB(target)" ); + return GL_FALSE; + } + if (!_mesa_is_bufferobj(bufObj)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glUnmapBufferARB" ); + return GL_FALSE; + } + if (!_mesa_bufferobj_mapped(bufObj)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glUnmapBufferARB"); + return GL_FALSE; + } + +#ifdef BOUNDS_CHECK + if (bufObj->Access != GL_READ_ONLY_ARB) { + GLubyte *buf = (GLubyte *) bufObj->Pointer; + GLuint i; + /* check that last 100 bytes are still = magic value */ + for (i = 0; i < 100; i++) { + GLuint pos = bufObj->Size - i - 1; + if (buf[pos] != 123) { + _mesa_warning(ctx, "Out of bounds buffer object write detected" + " at position %d (value = %u)\n", + pos, buf[pos]); + } + } + } +#endif + +#ifdef VBO_DEBUG + if (bufObj->AccessFlags & GL_MAP_WRITE_BIT) { + GLuint i, unchanged = 0; + GLubyte *b = (GLubyte *) bufObj->Pointer; + GLint pos = -1; + /* check which bytes changed */ + for (i = 0; i < bufObj->Size - 1; i++) { + if (b[i] == (i & 0xff) && b[i+1] == ((i+1) & 0xff)) { + unchanged++; + if (pos == -1) + pos = i; + } + } + if (unchanged) { + printf("glUnmapBufferARB(%u): %u of %ld unchanged, starting at %d\n", + bufObj->Name, unchanged, bufObj->Size, pos); + } + } +#endif + + status = ctx->Driver.UnmapBuffer( ctx, target, bufObj ); + bufObj->AccessFlags = DEFAULT_ACCESS; + ASSERT(bufObj->Pointer == NULL); + ASSERT(bufObj->Offset == 0); + ASSERT(bufObj->Length == 0); + + return status; +} + + +void GLAPIENTRY +_mesa_GetBufferParameterivARB(GLenum target, GLenum pname, GLint *params) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_buffer_object *bufObj; + ASSERT_OUTSIDE_BEGIN_END(ctx); + + bufObj = get_buffer(ctx, target); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameterivARB(target)" ); + return; + } + if (!_mesa_is_bufferobj(bufObj)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glGetBufferParameterivARB" ); + return; + } + + switch (pname) { + case GL_BUFFER_SIZE_ARB: + *params = (GLint) bufObj->Size; + return; + case GL_BUFFER_USAGE_ARB: + *params = bufObj->Usage; + return; + case GL_BUFFER_ACCESS_ARB: + *params = simplified_access_mode(bufObj->AccessFlags); + return; + case GL_BUFFER_MAPPED_ARB: + *params = _mesa_bufferobj_mapped(bufObj); + return; + case GL_BUFFER_ACCESS_FLAGS: + if (ctx->VersionMajor < 3) + goto invalid_pname; + *params = bufObj->AccessFlags; + return; + case GL_BUFFER_MAP_OFFSET: + if (ctx->VersionMajor < 3) + goto invalid_pname; + *params = (GLint) bufObj->Offset; + return; + case GL_BUFFER_MAP_LENGTH: + if (ctx->VersionMajor < 3) + goto invalid_pname; + *params = (GLint) bufObj->Length; + return; + default: + ; /* fall-through */ + } + +invalid_pname: + _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameterivARB(pname=%s)", + _mesa_lookup_enum_by_nr(pname)); +} + + +/** + * New in GL 3.2 + * This is pretty much a duplicate of GetBufferParameteriv() but the + * GL_BUFFER_SIZE_ARB attribute will be 64-bits on a 64-bit system. + */ +void GLAPIENTRY +_mesa_GetBufferParameteri64v(GLenum target, GLenum pname, GLint64 *params) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_buffer_object *bufObj; + ASSERT_OUTSIDE_BEGIN_END(ctx); + + bufObj = get_buffer(ctx, target); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameteri64v(target)" ); + return; + } + if (!_mesa_is_bufferobj(bufObj)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glGetBufferParameteri64v" ); + return; + } + + switch (pname) { + case GL_BUFFER_SIZE_ARB: + *params = bufObj->Size; + return; + case GL_BUFFER_USAGE_ARB: + *params = bufObj->Usage; + return; + case GL_BUFFER_ACCESS_ARB: + *params = simplified_access_mode(bufObj->AccessFlags); + return; + case GL_BUFFER_ACCESS_FLAGS: + if (ctx->VersionMajor < 3) + goto invalid_pname; + *params = bufObj->AccessFlags; + return; + case GL_BUFFER_MAPPED_ARB: + *params = _mesa_bufferobj_mapped(bufObj); + return; + case GL_BUFFER_MAP_OFFSET: + if (ctx->VersionMajor < 3) + goto invalid_pname; + *params = bufObj->Offset; + return; + case GL_BUFFER_MAP_LENGTH: + if (ctx->VersionMajor < 3) + goto invalid_pname; + *params = bufObj->Length; + return; + default: + ; /* fall-through */ + } + +invalid_pname: + _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferParameteri64v(pname=%s)", + _mesa_lookup_enum_by_nr(pname)); +} + + +void GLAPIENTRY +_mesa_GetBufferPointervARB(GLenum target, GLenum pname, GLvoid **params) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_buffer_object * bufObj; + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (pname != GL_BUFFER_MAP_POINTER_ARB) { + _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferPointervARB(pname)"); + return; + } + + bufObj = get_buffer(ctx, target); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_ENUM, "glGetBufferPointervARB(target)" ); + return; + } + if (!_mesa_is_bufferobj(bufObj)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glGetBufferPointervARB" ); + return; + } + + *params = bufObj->Pointer; +} + + +void GLAPIENTRY +_mesa_CopyBufferSubData(GLenum readTarget, GLenum writeTarget, + GLintptr readOffset, GLintptr writeOffset, + GLsizeiptr size) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_buffer_object *src, *dst; + ASSERT_OUTSIDE_BEGIN_END(ctx); + + src = get_buffer(ctx, readTarget); + if (!src || !_mesa_is_bufferobj(src)) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glCopyBuffserSubData(readTarget = 0x%x)", readTarget); + return; + } + + dst = get_buffer(ctx, writeTarget); + if (!dst || !_mesa_is_bufferobj(dst)) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glCopyBuffserSubData(writeTarget = 0x%x)", writeTarget); + return; + } + + if (_mesa_bufferobj_mapped(src)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glCopyBuffserSubData(readBuffer is mapped)"); + return; + } + + if (_mesa_bufferobj_mapped(dst)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glCopyBuffserSubData(writeBuffer is mapped)"); + return; + } + + if (readOffset < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glCopyBuffserSubData(readOffset = %d)", (int) readOffset); + return; + } + + if (writeOffset < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glCopyBuffserSubData(writeOffset = %d)", (int) writeOffset); + return; + } + + if (readOffset + size > src->Size) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glCopyBuffserSubData(readOffset + size = %d)", + (int) (readOffset + size)); + return; + } + + if (writeOffset + size > dst->Size) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glCopyBuffserSubData(writeOffset + size = %d)", + (int) (writeOffset + size)); + return; + } + + if (src == dst) { + if (readOffset + size <= writeOffset) { + /* OK */ + } + else if (writeOffset + size <= readOffset) { + /* OK */ + } + else { + /* overlapping src/dst is illegal */ + _mesa_error(ctx, GL_INVALID_VALUE, + "glCopyBuffserSubData(overlapping src/dst)"); + return; + } + } + + ctx->Driver.CopyBufferSubData(ctx, src, dst, readOffset, writeOffset, size); +} + + +/** + * See GL_ARB_map_buffer_range spec + */ +void * GLAPIENTRY +_mesa_MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, + GLbitfield access) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_buffer_object *bufObj; + void *map; + + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, NULL); + + if (!ctx->Extensions.ARB_map_buffer_range) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glMapBufferRange(extension not supported)"); + return NULL; + } + + if (offset < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glMapBufferRange(offset = %ld)", (long)offset); + return NULL; + } + + if (length < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glMapBufferRange(length = %ld)", (long)length); + return NULL; + } + + if ((access & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) == 0) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glMapBufferRange(access indicates neither read or write)"); + return NULL; + } + + if (access & GL_MAP_READ_BIT) { + if ((access & GL_MAP_INVALIDATE_RANGE_BIT) || + (access & GL_MAP_INVALIDATE_BUFFER_BIT) || + (access & GL_MAP_UNSYNCHRONIZED_BIT)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glMapBufferRange(invalid access flags)"); + return NULL; + } + } + + if ((access & GL_MAP_FLUSH_EXPLICIT_BIT) && + ((access & GL_MAP_WRITE_BIT) == 0)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glMapBufferRange(invalid access flags)"); + return NULL; + } + + bufObj = get_buffer(ctx, target); + if (!bufObj || !_mesa_is_bufferobj(bufObj)) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glMapBufferRange(target = 0x%x)", target); + return NULL; + } + + if (offset + length > bufObj->Size) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glMapBufferRange(offset + length > size)"); + return NULL; + } + + if (_mesa_bufferobj_mapped(bufObj)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glMapBufferRange(buffer already mapped)"); + return NULL; + } + + ASSERT(ctx->Driver.MapBufferRange); + map = ctx->Driver.MapBufferRange(ctx, target, offset, length, + access, bufObj); + if (!map) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glMapBufferARB(map failed)"); + } + else { + /* The driver callback should have set all these fields. + * This is important because other modules (like VBO) might call + * the driver function directly. + */ + ASSERT(bufObj->Pointer == map); + ASSERT(bufObj->Length == length); + ASSERT(bufObj->Offset == offset); + ASSERT(bufObj->AccessFlags == access); + } + + return map; +} + + +/** + * See GL_ARB_map_buffer_range spec + */ +void GLAPIENTRY +_mesa_FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_buffer_object *bufObj; + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (!ctx->Extensions.ARB_map_buffer_range) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glMapBufferRange(extension not supported)"); + return; + } + + if (offset < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glMapBufferRange(offset = %ld)", (long)offset); + return; + } + + if (length < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glMapBufferRange(length = %ld)", (long)length); + return; + } + + bufObj = get_buffer(ctx, target); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glMapBufferRange(target = 0x%x)", target); + return; + } + + if (!_mesa_is_bufferobj(bufObj)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glMapBufferRange(current buffer is 0)"); + return; + } + + if (!_mesa_bufferobj_mapped(bufObj)) { + /* buffer is not mapped */ + _mesa_error(ctx, GL_INVALID_OPERATION, + "glMapBufferRange(buffer is not mapped)"); + return; + } + + if ((bufObj->AccessFlags & GL_MAP_FLUSH_EXPLICIT_BIT) == 0) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glMapBufferRange(GL_MAP_FLUSH_EXPLICIT_BIT not set)"); + return; + } + + if (offset + length > bufObj->Length) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glMapBufferRange(offset %ld + length %ld > mapped length %ld)", + (long)offset, (long)length, (long)bufObj->Length); + return; + } + + ASSERT(bufObj->AccessFlags & GL_MAP_WRITE_BIT); + + if (ctx->Driver.FlushMappedBufferRange) + ctx->Driver.FlushMappedBufferRange(ctx, target, offset, length, bufObj); +} + + +#if FEATURE_APPLE_object_purgeable +static GLenum +_mesa_BufferObjectPurgeable(struct gl_context *ctx, GLuint name, GLenum option) +{ + struct gl_buffer_object *bufObj; + GLenum retval; + + bufObj = _mesa_lookup_bufferobj(ctx, name); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glObjectPurgeable(name = 0x%x)", name); + return 0; + } + if (!_mesa_is_bufferobj(bufObj)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glObjectPurgeable(buffer 0)" ); + return 0; + } + + if (bufObj->Purgeable) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glObjectPurgeable(name = 0x%x) is already purgeable", name); + return GL_VOLATILE_APPLE; + } + + bufObj->Purgeable = GL_TRUE; + + retval = GL_VOLATILE_APPLE; + if (ctx->Driver.BufferObjectPurgeable) + retval = ctx->Driver.BufferObjectPurgeable(ctx, bufObj, option); + + return retval; +} + + +static GLenum +_mesa_RenderObjectPurgeable(struct gl_context *ctx, GLuint name, GLenum option) +{ + struct gl_renderbuffer *bufObj; + GLenum retval; + + bufObj = _mesa_lookup_renderbuffer(ctx, name); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glObjectUnpurgeable(name = 0x%x)", name); + return 0; + } + + if (bufObj->Purgeable) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glObjectPurgeable(name = 0x%x) is already purgeable", name); + return GL_VOLATILE_APPLE; + } + + bufObj->Purgeable = GL_TRUE; + + retval = GL_VOLATILE_APPLE; + if (ctx->Driver.RenderObjectPurgeable) + retval = ctx->Driver.RenderObjectPurgeable(ctx, bufObj, option); + + return retval; +} + + +static GLenum +_mesa_TextureObjectPurgeable(struct gl_context *ctx, GLuint name, GLenum option) +{ + struct gl_texture_object *bufObj; + GLenum retval; + + bufObj = _mesa_lookup_texture(ctx, name); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glObjectPurgeable(name = 0x%x)", name); + return 0; + } + + if (bufObj->Purgeable) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glObjectPurgeable(name = 0x%x) is already purgeable", name); + return GL_VOLATILE_APPLE; + } + + bufObj->Purgeable = GL_TRUE; + + retval = GL_VOLATILE_APPLE; + if (ctx->Driver.TextureObjectPurgeable) + retval = ctx->Driver.TextureObjectPurgeable(ctx, bufObj, option); + + return retval; +} + + +GLenum GLAPIENTRY +_mesa_ObjectPurgeableAPPLE(GLenum objectType, GLuint name, GLenum option) +{ + GLenum retval; + + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0); + + if (name == 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glObjectPurgeable(name = 0x%x)", name); + return 0; + } + + switch (option) { + case GL_VOLATILE_APPLE: + case GL_RELEASED_APPLE: + /* legal */ + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "glObjectPurgeable(name = 0x%x) invalid option: %d", + name, option); + return 0; + } + + switch (objectType) { + case GL_TEXTURE: + retval = _mesa_TextureObjectPurgeable (ctx, name, option); + break; + case GL_RENDERBUFFER_EXT: + retval = _mesa_RenderObjectPurgeable (ctx, name, option); + break; + case GL_BUFFER_OBJECT_APPLE: + retval = _mesa_BufferObjectPurgeable (ctx, name, option); + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "glObjectPurgeable(name = 0x%x) invalid type: %d", + name, objectType); + return 0; + } + + /* In strict conformance to the spec, we must only return VOLATILE when + * when passed the VOLATILE option. Madness. + * + * XXX First fix the spec, then fix me. + */ + return option == GL_VOLATILE_APPLE ? GL_VOLATILE_APPLE : retval; +} + + +static GLenum +_mesa_BufferObjectUnpurgeable(struct gl_context *ctx, GLuint name, GLenum option) +{ + struct gl_buffer_object *bufObj; + GLenum retval; + + bufObj = _mesa_lookup_bufferobj(ctx, name); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glObjectUnpurgeable(name = 0x%x)", name); + return 0; + } + + if (! bufObj->Purgeable) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glObjectUnpurgeable(name = 0x%x) object is " + " already \"unpurged\"", name); + return 0; + } + + bufObj->Purgeable = GL_FALSE; + + retval = option; + if (ctx->Driver.BufferObjectUnpurgeable) + retval = ctx->Driver.BufferObjectUnpurgeable(ctx, bufObj, option); + + return retval; +} + + +static GLenum +_mesa_RenderObjectUnpurgeable(struct gl_context *ctx, GLuint name, GLenum option) +{ + struct gl_renderbuffer *bufObj; + GLenum retval; + + bufObj = _mesa_lookup_renderbuffer(ctx, name); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glObjectUnpurgeable(name = 0x%x)", name); + return 0; + } + + if (! bufObj->Purgeable) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glObjectUnpurgeable(name = 0x%x) object is " + " already \"unpurged\"", name); + return 0; + } + + bufObj->Purgeable = GL_FALSE; + + retval = option; + if (ctx->Driver.RenderObjectUnpurgeable) + retval = ctx->Driver.RenderObjectUnpurgeable(ctx, bufObj, option); + + return retval; +} + + +static GLenum +_mesa_TextureObjectUnpurgeable(struct gl_context *ctx, GLuint name, GLenum option) +{ + struct gl_texture_object *bufObj; + GLenum retval; + + bufObj = _mesa_lookup_texture(ctx, name); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glObjectUnpurgeable(name = 0x%x)", name); + return 0; + } + + if (! bufObj->Purgeable) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glObjectUnpurgeable(name = 0x%x) object is" + " already \"unpurged\"", name); + return 0; + } + + bufObj->Purgeable = GL_FALSE; + + retval = option; + if (ctx->Driver.TextureObjectUnpurgeable) + retval = ctx->Driver.TextureObjectUnpurgeable(ctx, bufObj, option); + + return retval; +} + + +GLenum GLAPIENTRY +_mesa_ObjectUnpurgeableAPPLE(GLenum objectType, GLuint name, GLenum option) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0); + + if (name == 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glObjectUnpurgeable(name = 0x%x)", name); + return 0; + } + + switch (option) { + case GL_RETAINED_APPLE: + case GL_UNDEFINED_APPLE: + /* legal */ + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "glObjectUnpurgeable(name = 0x%x) invalid option: %d", + name, option); + return 0; + } + + switch (objectType) { + case GL_BUFFER_OBJECT_APPLE: + return _mesa_BufferObjectUnpurgeable(ctx, name, option); + case GL_TEXTURE: + return _mesa_TextureObjectUnpurgeable(ctx, name, option); + case GL_RENDERBUFFER_EXT: + return _mesa_RenderObjectUnpurgeable(ctx, name, option); + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "glObjectUnpurgeable(name = 0x%x) invalid type: %d", + name, objectType); + return 0; + } +} + + +static void +_mesa_GetBufferObjectParameterivAPPLE(struct gl_context *ctx, GLuint name, + GLenum pname, GLint* params) +{ + struct gl_buffer_object *bufObj; + + bufObj = _mesa_lookup_bufferobj(ctx, name); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glGetObjectParameteriv(name = 0x%x) invalid object", name); + return; + } + + switch (pname) { + case GL_PURGEABLE_APPLE: + *params = bufObj->Purgeable; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetObjectParameteriv(name = 0x%x) invalid enum: %d", + name, pname); + break; + } +} + + +static void +_mesa_GetRenderObjectParameterivAPPLE(struct gl_context *ctx, GLuint name, + GLenum pname, GLint* params) +{ + struct gl_renderbuffer *bufObj; + + bufObj = _mesa_lookup_renderbuffer(ctx, name); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glObjectUnpurgeable(name = 0x%x)", name); + return; + } + + switch (pname) { + case GL_PURGEABLE_APPLE: + *params = bufObj->Purgeable; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetObjectParameteriv(name = 0x%x) invalid enum: %d", + name, pname); + break; + } +} + + +static void +_mesa_GetTextureObjectParameterivAPPLE(struct gl_context *ctx, GLuint name, + GLenum pname, GLint* params) +{ + struct gl_texture_object *bufObj; + + bufObj = _mesa_lookup_texture(ctx, name); + if (!bufObj) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glObjectUnpurgeable(name = 0x%x)", name); + return; + } + + switch (pname) { + case GL_PURGEABLE_APPLE: + *params = bufObj->Purgeable; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetObjectParameteriv(name = 0x%x) invalid enum: %d", + name, pname); + break; + } +} + + +void GLAPIENTRY +_mesa_GetObjectParameterivAPPLE(GLenum objectType, GLuint name, GLenum pname, + GLint* params) +{ + GET_CURRENT_CONTEXT(ctx); + + if (name == 0) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glGetObjectParameteriv(name = 0x%x)", name); + return; + } + + switch (objectType) { + case GL_TEXTURE: + _mesa_GetTextureObjectParameterivAPPLE (ctx, name, pname, params); + break; + case GL_BUFFER_OBJECT_APPLE: + _mesa_GetBufferObjectParameterivAPPLE (ctx, name, pname, params); + break; + case GL_RENDERBUFFER_EXT: + _mesa_GetRenderObjectParameterivAPPLE (ctx, name, pname, params); + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetObjectParameteriv(name = 0x%x) invalid type: %d", + name, objectType); + } +} + +#endif /* FEATURE_APPLE_object_purgeable */ diff --git a/mesalib/src/mesa/main/context.c b/mesalib/src/mesa/main/context.c index e017939a4..a94231455 100644 --- a/mesalib/src/mesa/main/context.c +++ b/mesalib/src/mesa/main/context.c @@ -886,12 +886,12 @@ _mesa_alloc_dispatch_table(int size) * \param driverContext pointer to driver-specific context data */ GLboolean -_mesa_initialize_context_for_api(struct gl_context *ctx, - gl_api api, - const struct gl_config *visual, - struct gl_context *share_list, - const struct dd_function_table *driverFunctions, - void *driverContext) +_mesa_initialize_context(struct gl_context *ctx, + gl_api api, + const struct gl_config *visual, + struct gl_context *share_list, + const struct dd_function_table *driverFunctions, + void *driverContext) { struct gl_shared_state *shared; int i; @@ -1028,25 +1028,6 @@ _mesa_initialize_context_for_api(struct gl_context *ctx, } -/** - * Initialize an OpenGL context. - */ -GLboolean -_mesa_initialize_context(struct gl_context *ctx, - const struct gl_config *visual, - struct gl_context *share_list, - const struct dd_function_table *driverFunctions, - void *driverContext) -{ - return _mesa_initialize_context_for_api(ctx, - API_OPENGL, - visual, - share_list, - driverFunctions, - driverContext); -} - - /** * Allocate and initialize a struct gl_context structure. * Note that the driver needs to pass in its dd_function_table here since @@ -1063,11 +1044,11 @@ _mesa_initialize_context(struct gl_context *ctx, * \return pointer to a new __struct gl_contextRec or NULL if error. */ struct gl_context * -_mesa_create_context_for_api(gl_api api, - const struct gl_config *visual, - struct gl_context *share_list, - const struct dd_function_table *driverFunctions, - void *driverContext) +_mesa_create_context(gl_api api, + const struct gl_config *visual, + struct gl_context *share_list, + const struct dd_function_table *driverFunctions, + void *driverContext) { struct gl_context *ctx; @@ -1078,8 +1059,8 @@ _mesa_create_context_for_api(gl_api api, if (!ctx) return NULL; - if (_mesa_initialize_context_for_api(ctx, api, visual, share_list, - driverFunctions, driverContext)) { + if (_mesa_initialize_context(ctx, api, visual, share_list, + driverFunctions, driverContext)) { return ctx; } else { @@ -1089,22 +1070,6 @@ _mesa_create_context_for_api(gl_api api, } -/** - * Create an OpenGL context. - */ -struct gl_context * -_mesa_create_context(const struct gl_config *visual, - struct gl_context *share_list, - const struct dd_function_table *driverFunctions, - void *driverContext) -{ - return _mesa_create_context_for_api(API_OPENGL, visual, - share_list, - driverFunctions, - driverContext); -} - - /** * Free the data associated with the given context. * diff --git a/mesalib/src/mesa/main/context.h b/mesalib/src/mesa/main/context.h index bcadaea4f..4e391dde4 100644 --- a/mesalib/src/mesa/main/context.h +++ b/mesalib/src/mesa/main/context.h @@ -1,326 +1,313 @@ -/* - * Mesa 3-D graphics library - * Version: 6.5.1 - * - * Copyright (C) 1999-2006 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 - * BRIAN PAUL 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 context.h - * Mesa context and visual-related functions. - * - * There are three large Mesa data types/classes which are meant to be - * used by device drivers: - * - struct gl_context: this contains the Mesa rendering state - * - struct gl_config: this describes the color buffer (RGB vs. ci), whether or not - * there's a depth buffer, stencil buffer, etc. - * - struct gl_framebuffer: contains pointers to the depth buffer, stencil buffer, - * accum buffer and alpha buffers. - * - * These types should be encapsulated by corresponding device driver - * data types. See xmesa.h and xmesaP.h for an example. - * - * In OOP terms, struct gl_context, struct gl_config, and struct gl_framebuffer are base classes - * which the device driver must derive from. - * - * The following functions create and destroy these data types. - */ - - -#ifndef CONTEXT_H -#define CONTEXT_H - - -#include "imports.h" -#include "mtypes.h" - - -struct _glapi_table; - - -/** \name Visual-related functions */ -/*@{*/ - -extern struct gl_config * -_mesa_create_visual( GLboolean dbFlag, - GLboolean stereoFlag, - GLint redBits, - GLint greenBits, - GLint blueBits, - GLint alphaBits, - GLint depthBits, - GLint stencilBits, - GLint accumRedBits, - GLint accumGreenBits, - GLint accumBlueBits, - GLint accumAlphaBits, - GLint numSamples ); - -extern GLboolean -_mesa_initialize_visual( struct gl_config *v, - GLboolean dbFlag, - GLboolean stereoFlag, - GLint redBits, - GLint greenBits, - GLint blueBits, - GLint alphaBits, - GLint depthBits, - GLint stencilBits, - GLint accumRedBits, - GLint accumGreenBits, - GLint accumBlueBits, - GLint accumAlphaBits, - GLint numSamples ); - -extern void -_mesa_destroy_visual( struct gl_config *vis ); - -/*@}*/ - - -/** \name Context-related functions */ -/*@{*/ - -extern struct gl_context * -_mesa_create_context( const struct gl_config *visual, - struct gl_context *share_list, - const struct dd_function_table *driverFunctions, - void *driverContext ); - -extern GLboolean -_mesa_initialize_context( struct gl_context *ctx, - const struct gl_config *visual, - struct gl_context *share_list, - const struct dd_function_table *driverFunctions, - void *driverContext ); - -extern struct gl_context * -_mesa_create_context_for_api(gl_api api, - const struct gl_config *visual, - struct gl_context *share_list, - const struct dd_function_table *driverFunctions, - void *driverContext); - -extern GLboolean -_mesa_initialize_context_for_api(struct gl_context *ctx, - gl_api api, - const struct gl_config *visual, - struct gl_context *share_list, - const struct dd_function_table *driverFunctions, - void *driverContext); - -extern void -_mesa_free_context_data( struct gl_context *ctx ); - -extern void -_mesa_destroy_context( struct gl_context *ctx ); - - -extern void -_mesa_copy_context(const struct gl_context *src, struct gl_context *dst, GLuint mask); - - -extern void -_mesa_check_init_viewport(struct gl_context *ctx, GLuint width, GLuint height); - -extern GLboolean -_mesa_make_current( struct gl_context *ctx, struct gl_framebuffer *drawBuffer, - struct gl_framebuffer *readBuffer ); - -extern GLboolean -_mesa_share_state(struct gl_context *ctx, struct gl_context *ctxToShare); - -extern struct gl_context * -_mesa_get_current_context(void); - -/*@}*/ - -extern void -_mesa_init_get_hash(struct gl_context *ctx); - -extern void -_mesa_notifySwapBuffers(struct gl_context *gc); - - -extern struct _glapi_table * -_mesa_get_dispatch(struct gl_context *ctx); - - -void -_mesa_set_mvp_with_dp4( struct gl_context *ctx, - GLboolean flag ); - - -extern GLboolean -_mesa_valid_to_render(struct gl_context *ctx, const char *where); - - - -/** \name Miscellaneous */ -/*@{*/ - -extern void -_mesa_record_error( struct gl_context *ctx, GLenum error ); - - -extern void -_mesa_finish(struct gl_context *ctx); - -extern void -_mesa_flush(struct gl_context *ctx); - - -extern void GLAPIENTRY -_mesa_Finish( void ); - -extern void GLAPIENTRY -_mesa_Flush( void ); - -/*@}*/ - - -/** - * \name Macros for flushing buffered rendering commands before state changes, - * checking if inside glBegin/glEnd, etc. - */ -/*@{*/ - -/** - * Flush vertices. - * - * \param ctx GL context. - * \param newstate new state. - * - * Checks if dd_function_table::NeedFlush is marked to flush stored vertices, - * and calls dd_function_table::FlushVertices if so. Marks - * __struct gl_contextRec::NewState with \p newstate. - */ -#define FLUSH_VERTICES(ctx, newstate) \ -do { \ - if (MESA_VERBOSE & VERBOSE_STATE) \ - _mesa_debug(ctx, "FLUSH_VERTICES in %s\n", MESA_FUNCTION);\ - if (ctx->Driver.NeedFlush & FLUSH_STORED_VERTICES) \ - ctx->Driver.FlushVertices(ctx, FLUSH_STORED_VERTICES); \ - ctx->NewState |= newstate; \ -} while (0) - -/** - * Flush current state. - * - * \param ctx GL context. - * \param newstate new state. - * - * Checks if dd_function_table::NeedFlush is marked to flush current state, - * and calls dd_function_table::FlushVertices if so. Marks - * __struct gl_contextRec::NewState with \p newstate. - */ -#define FLUSH_CURRENT(ctx, newstate) \ -do { \ - if (MESA_VERBOSE & VERBOSE_STATE) \ - _mesa_debug(ctx, "FLUSH_CURRENT in %s\n", MESA_FUNCTION); \ - if (ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT) \ - ctx->Driver.FlushVertices(ctx, FLUSH_UPDATE_CURRENT); \ - ctx->NewState |= newstate; \ -} while (0) - -/** - * Macro to assert that the API call was made outside the - * glBegin()/glEnd() pair, with return value. - * - * \param ctx GL context. - * \param retval value to return value in case the assertion fails. - */ -#define ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, retval) \ -do { \ - if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { \ - _mesa_error(ctx, GL_INVALID_OPERATION, "Inside glBegin/glEnd"); \ - return retval; \ - } \ -} while (0) - -/** - * Macro to assert that the API call was made outside the - * glBegin()/glEnd() pair. - * - * \param ctx GL context. - */ -#define ASSERT_OUTSIDE_BEGIN_END(ctx) \ -do { \ - if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { \ - _mesa_error(ctx, GL_INVALID_OPERATION, "Inside glBegin/glEnd"); \ - return; \ - } \ -} while (0) - -/** - * Macro to assert that the API call was made outside the - * glBegin()/glEnd() pair and flush the vertices. - * - * \param ctx GL context. - */ -#define ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx) \ -do { \ - ASSERT_OUTSIDE_BEGIN_END(ctx); \ - FLUSH_VERTICES(ctx, 0); \ -} while (0) - -/** - * Macro to assert that the API call was made outside the - * glBegin()/glEnd() pair and flush the vertices, with return value. - * - * \param ctx GL context. - * \param retval value to return value in case the assertion fails. - */ -#define ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, retval) \ -do { \ - ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, retval); \ - FLUSH_VERTICES(ctx, 0); \ -} while (0) - -/*@}*/ - - - -/** - * Is the secondary color needed? - */ -#define NEED_SECONDARY_COLOR(CTX) \ - (((CTX)->Light.Enabled && \ - (CTX)->Light.Model.ColorControl == GL_SEPARATE_SPECULAR_COLOR) \ - || (CTX)->Fog.ColorSumEnabled \ - || ((CTX)->VertexProgram._Current && \ - ((CTX)->VertexProgram._Current != (CTX)->VertexProgram._TnlProgram) && \ - ((CTX)->VertexProgram._Current->Base.InputsRead & VERT_BIT_COLOR1)) \ - || ((CTX)->FragmentProgram._Current && \ - ((CTX)->FragmentProgram._Current != (CTX)->FragmentProgram._TexEnvProgram) && \ - ((CTX)->FragmentProgram._Current->Base.InputsRead & FRAG_BIT_COL1)) \ - ) - - -/** - * Is RGBA LogicOp enabled? - */ -#define RGBA_LOGICOP_ENABLED(CTX) \ - ((CTX)->Color.ColorLogicOpEnabled || \ - ((CTX)->Color.BlendEnabled && (CTX)->Color.Blend[0].EquationRGB == GL_LOGIC_OP)) - - -#endif /* CONTEXT_H */ +/* + * Mesa 3-D graphics library + * Version: 6.5.1 + * + * Copyright (C) 1999-2006 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 + * BRIAN PAUL 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 context.h + * Mesa context and visual-related functions. + * + * There are three large Mesa data types/classes which are meant to be + * used by device drivers: + * - struct gl_context: this contains the Mesa rendering state + * - struct gl_config: this describes the color buffer (RGB vs. ci), whether or not + * there's a depth buffer, stencil buffer, etc. + * - struct gl_framebuffer: contains pointers to the depth buffer, stencil buffer, + * accum buffer and alpha buffers. + * + * These types should be encapsulated by corresponding device driver + * data types. See xmesa.h and xmesaP.h for an example. + * + * In OOP terms, struct gl_context, struct gl_config, and struct gl_framebuffer are base classes + * which the device driver must derive from. + * + * The following functions create and destroy these data types. + */ + + +#ifndef CONTEXT_H +#define CONTEXT_H + + +#include "imports.h" +#include "mtypes.h" + + +struct _glapi_table; + + +/** \name Visual-related functions */ +/*@{*/ + +extern struct gl_config * +_mesa_create_visual( GLboolean dbFlag, + GLboolean stereoFlag, + GLint redBits, + GLint greenBits, + GLint blueBits, + GLint alphaBits, + GLint depthBits, + GLint stencilBits, + GLint accumRedBits, + GLint accumGreenBits, + GLint accumBlueBits, + GLint accumAlphaBits, + GLint numSamples ); + +extern GLboolean +_mesa_initialize_visual( struct gl_config *v, + GLboolean dbFlag, + GLboolean stereoFlag, + GLint redBits, + GLint greenBits, + GLint blueBits, + GLint alphaBits, + GLint depthBits, + GLint stencilBits, + GLint accumRedBits, + GLint accumGreenBits, + GLint accumBlueBits, + GLint accumAlphaBits, + GLint numSamples ); + +extern void +_mesa_destroy_visual( struct gl_config *vis ); + +/*@}*/ + + +/** \name Context-related functions */ +/*@{*/ + +extern GLboolean +_mesa_initialize_context( struct gl_context *ctx, + gl_api api, + const struct gl_config *visual, + struct gl_context *share_list, + const struct dd_function_table *driverFunctions, + void *driverContext ); + +extern struct gl_context * +_mesa_create_context(gl_api api, + const struct gl_config *visual, + struct gl_context *share_list, + const struct dd_function_table *driverFunctions, + void *driverContext); + +extern void +_mesa_free_context_data( struct gl_context *ctx ); + +extern void +_mesa_destroy_context( struct gl_context *ctx ); + + +extern void +_mesa_copy_context(const struct gl_context *src, struct gl_context *dst, GLuint mask); + + +extern void +_mesa_check_init_viewport(struct gl_context *ctx, GLuint width, GLuint height); + +extern GLboolean +_mesa_make_current( struct gl_context *ctx, struct gl_framebuffer *drawBuffer, + struct gl_framebuffer *readBuffer ); + +extern GLboolean +_mesa_share_state(struct gl_context *ctx, struct gl_context *ctxToShare); + +extern struct gl_context * +_mesa_get_current_context(void); + +/*@}*/ + +extern void +_mesa_init_get_hash(struct gl_context *ctx); + +extern void +_mesa_notifySwapBuffers(struct gl_context *gc); + + +extern struct _glapi_table * +_mesa_get_dispatch(struct gl_context *ctx); + + +void +_mesa_set_mvp_with_dp4( struct gl_context *ctx, + GLboolean flag ); + + +extern GLboolean +_mesa_valid_to_render(struct gl_context *ctx, const char *where); + + + +/** \name Miscellaneous */ +/*@{*/ + +extern void +_mesa_record_error( struct gl_context *ctx, GLenum error ); + + +extern void +_mesa_finish(struct gl_context *ctx); + +extern void +_mesa_flush(struct gl_context *ctx); + + +extern void GLAPIENTRY +_mesa_Finish( void ); + +extern void GLAPIENTRY +_mesa_Flush( void ); + +/*@}*/ + + +/** + * \name Macros for flushing buffered rendering commands before state changes, + * checking if inside glBegin/glEnd, etc. + */ +/*@{*/ + +/** + * Flush vertices. + * + * \param ctx GL context. + * \param newstate new state. + * + * Checks if dd_function_table::NeedFlush is marked to flush stored vertices, + * and calls dd_function_table::FlushVertices if so. Marks + * __struct gl_contextRec::NewState with \p newstate. + */ +#define FLUSH_VERTICES(ctx, newstate) \ +do { \ + if (MESA_VERBOSE & VERBOSE_STATE) \ + _mesa_debug(ctx, "FLUSH_VERTICES in %s\n", MESA_FUNCTION);\ + if (ctx->Driver.NeedFlush & FLUSH_STORED_VERTICES) \ + ctx->Driver.FlushVertices(ctx, FLUSH_STORED_VERTICES); \ + ctx->NewState |= newstate; \ +} while (0) + +/** + * Flush current state. + * + * \param ctx GL context. + * \param newstate new state. + * + * Checks if dd_function_table::NeedFlush is marked to flush current state, + * and calls dd_function_table::FlushVertices if so. Marks + * __struct gl_contextRec::NewState with \p newstate. + */ +#define FLUSH_CURRENT(ctx, newstate) \ +do { \ + if (MESA_VERBOSE & VERBOSE_STATE) \ + _mesa_debug(ctx, "FLUSH_CURRENT in %s\n", MESA_FUNCTION); \ + if (ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT) \ + ctx->Driver.FlushVertices(ctx, FLUSH_UPDATE_CURRENT); \ + ctx->NewState |= newstate; \ +} while (0) + +/** + * Macro to assert that the API call was made outside the + * glBegin()/glEnd() pair, with return value. + * + * \param ctx GL context. + * \param retval value to return value in case the assertion fails. + */ +#define ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, retval) \ +do { \ + if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { \ + _mesa_error(ctx, GL_INVALID_OPERATION, "Inside glBegin/glEnd"); \ + return retval; \ + } \ +} while (0) + +/** + * Macro to assert that the API call was made outside the + * glBegin()/glEnd() pair. + * + * \param ctx GL context. + */ +#define ASSERT_OUTSIDE_BEGIN_END(ctx) \ +do { \ + if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { \ + _mesa_error(ctx, GL_INVALID_OPERATION, "Inside glBegin/glEnd"); \ + return; \ + } \ +} while (0) + +/** + * Macro to assert that the API call was made outside the + * glBegin()/glEnd() pair and flush the vertices. + * + * \param ctx GL context. + */ +#define ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx) \ +do { \ + ASSERT_OUTSIDE_BEGIN_END(ctx); \ + FLUSH_VERTICES(ctx, 0); \ +} while (0) + +/** + * Macro to assert that the API call was made outside the + * glBegin()/glEnd() pair and flush the vertices, with return value. + * + * \param ctx GL context. + * \param retval value to return value in case the assertion fails. + */ +#define ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, retval) \ +do { \ + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, retval); \ + FLUSH_VERTICES(ctx, 0); \ +} while (0) + +/*@}*/ + + + +/** + * Is the secondary color needed? + */ +#define NEED_SECONDARY_COLOR(CTX) \ + (((CTX)->Light.Enabled && \ + (CTX)->Light.Model.ColorControl == GL_SEPARATE_SPECULAR_COLOR) \ + || (CTX)->Fog.ColorSumEnabled \ + || ((CTX)->VertexProgram._Current && \ + ((CTX)->VertexProgram._Current != (CTX)->VertexProgram._TnlProgram) && \ + ((CTX)->VertexProgram._Current->Base.InputsRead & VERT_BIT_COLOR1)) \ + || ((CTX)->FragmentProgram._Current && \ + ((CTX)->FragmentProgram._Current != (CTX)->FragmentProgram._TexEnvProgram) && \ + ((CTX)->FragmentProgram._Current->Base.InputsRead & FRAG_BIT_COL1)) \ + ) + + +/** + * Is RGBA LogicOp enabled? + */ +#define RGBA_LOGICOP_ENABLED(CTX) \ + ((CTX)->Color.ColorLogicOpEnabled || \ + ((CTX)->Color.BlendEnabled && (CTX)->Color.Blend[0].EquationRGB == GL_LOGIC_OP)) + + +#endif /* CONTEXT_H */ diff --git a/mesalib/src/mesa/main/depth.c b/mesalib/src/mesa/main/depth.c index fe1380e71..52c69a6bc 100644 --- a/mesalib/src/mesa/main/depth.c +++ b/mesalib/src/mesa/main/depth.c @@ -1,168 +1,174 @@ -/* - * Mesa 3-D graphics library - * Version: 7.1 - * - * 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 - * BRIAN PAUL 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 "glheader.h" -#include "imports.h" -#include "context.h" -#include "depth.h" -#include "enums.h" -#include "macros.h" -#include "mtypes.h" - - -/**********************************************************************/ -/***** API Functions *****/ -/**********************************************************************/ - - - -void GLAPIENTRY -_mesa_ClearDepth( GLclampd depth ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - depth = CLAMP( depth, 0.0, 1.0 ); - - if (ctx->Depth.Clear == depth) - return; - - FLUSH_VERTICES(ctx, _NEW_DEPTH); - ctx->Depth.Clear = depth; - if (ctx->Driver.ClearDepth) - (*ctx->Driver.ClearDepth)( ctx, ctx->Depth.Clear ); -} - - -void GLAPIENTRY -_mesa_ClearDepthf( GLclampf depth ) -{ - _mesa_ClearDepth(depth); -} - - -void GLAPIENTRY -_mesa_DepthFunc( GLenum func ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, "glDepthFunc %s\n", _mesa_lookup_enum_by_nr(func)); - - switch (func) { - case GL_LESS: /* (default) pass if incoming z < stored z */ - case GL_GEQUAL: - case GL_LEQUAL: - case GL_GREATER: - case GL_NOTEQUAL: - case GL_EQUAL: - case GL_ALWAYS: - case GL_NEVER: - break; - default: - _mesa_error( ctx, GL_INVALID_ENUM, "glDepth.Func" ); - return; - } - - if (ctx->Depth.Func == func) - return; - - FLUSH_VERTICES(ctx, _NEW_DEPTH); - ctx->Depth.Func = func; - - if (ctx->Driver.DepthFunc) - ctx->Driver.DepthFunc( ctx, func ); -} - - - -void GLAPIENTRY -_mesa_DepthMask( GLboolean flag ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, "glDepthMask %d\n", flag); - - /* - * GL_TRUE indicates depth buffer writing is enabled (default) - * GL_FALSE indicates depth buffer writing is disabled - */ - if (ctx->Depth.Mask == flag) - return; - - FLUSH_VERTICES(ctx, _NEW_DEPTH); - ctx->Depth.Mask = flag; - - if (ctx->Driver.DepthMask) - ctx->Driver.DepthMask( ctx, flag ); -} - - - -/** - * Specified by the GL_EXT_depth_bounds_test extension. - */ -void GLAPIENTRY -_mesa_DepthBoundsEXT( GLclampd zmin, GLclampd zmax ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (zmin > zmax) { - _mesa_error(ctx, GL_INVALID_VALUE, "glDepthBoundsEXT(zmin > zmax)"); - return; - } - - zmin = CLAMP(zmin, 0.0, 1.0); - zmax = CLAMP(zmax, 0.0, 1.0); - - if (ctx->Depth.BoundsMin == zmin && ctx->Depth.BoundsMax == zmax) - return; - - FLUSH_VERTICES(ctx, _NEW_DEPTH); - ctx->Depth.BoundsMin = (GLfloat) zmin; - ctx->Depth.BoundsMax = (GLfloat) zmax; -} - - -/**********************************************************************/ -/***** Initialization *****/ -/**********************************************************************/ - - -/** - * Initialize the depth buffer attribute group in the given context. - */ -void -_mesa_init_depth(struct gl_context *ctx) -{ - ctx->Depth.Test = GL_FALSE; - ctx->Depth.Clear = 1.0; - ctx->Depth.Func = GL_LESS; - ctx->Depth.Mask = GL_TRUE; -} +/* + * Mesa 3-D graphics library + * Version: 7.1 + * + * 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 + * BRIAN PAUL 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 "glheader.h" +#include "imports.h" +#include "context.h" +#include "depth.h" +#include "enums.h" +#include "macros.h" +#include "mtypes.h" + + +/**********************************************************************/ +/***** API Functions *****/ +/**********************************************************************/ + + + +void GLAPIENTRY +_mesa_ClearDepth( GLclampd depth ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glClearDepth(%f)\n", depth); + + depth = CLAMP( depth, 0.0, 1.0 ); + + if (ctx->Depth.Clear == depth) + return; + + FLUSH_VERTICES(ctx, _NEW_DEPTH); + ctx->Depth.Clear = depth; + if (ctx->Driver.ClearDepth) + (*ctx->Driver.ClearDepth)( ctx, ctx->Depth.Clear ); +} + + +void GLAPIENTRY +_mesa_ClearDepthf( GLclampf depth ) +{ + _mesa_ClearDepth(depth); +} + + +void GLAPIENTRY +_mesa_DepthFunc( GLenum func ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glDepthFunc %s\n", _mesa_lookup_enum_by_nr(func)); + + switch (func) { + case GL_LESS: /* (default) pass if incoming z < stored z */ + case GL_GEQUAL: + case GL_LEQUAL: + case GL_GREATER: + case GL_NOTEQUAL: + case GL_EQUAL: + case GL_ALWAYS: + case GL_NEVER: + break; + default: + _mesa_error( ctx, GL_INVALID_ENUM, "glDepth.Func" ); + return; + } + + if (ctx->Depth.Func == func) + return; + + FLUSH_VERTICES(ctx, _NEW_DEPTH); + ctx->Depth.Func = func; + + if (ctx->Driver.DepthFunc) + ctx->Driver.DepthFunc( ctx, func ); +} + + + +void GLAPIENTRY +_mesa_DepthMask( GLboolean flag ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glDepthMask %d\n", flag); + + /* + * GL_TRUE indicates depth buffer writing is enabled (default) + * GL_FALSE indicates depth buffer writing is disabled + */ + if (ctx->Depth.Mask == flag) + return; + + FLUSH_VERTICES(ctx, _NEW_DEPTH); + ctx->Depth.Mask = flag; + + if (ctx->Driver.DepthMask) + ctx->Driver.DepthMask( ctx, flag ); +} + + + +/** + * Specified by the GL_EXT_depth_bounds_test extension. + */ +void GLAPIENTRY +_mesa_DepthBoundsEXT( GLclampd zmin, GLclampd zmax ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glDepthBounds(%f, %f)\n", zmin, zmax); + + if (zmin > zmax) { + _mesa_error(ctx, GL_INVALID_VALUE, "glDepthBoundsEXT(zmin > zmax)"); + return; + } + + zmin = CLAMP(zmin, 0.0, 1.0); + zmax = CLAMP(zmax, 0.0, 1.0); + + if (ctx->Depth.BoundsMin == zmin && ctx->Depth.BoundsMax == zmax) + return; + + FLUSH_VERTICES(ctx, _NEW_DEPTH); + ctx->Depth.BoundsMin = (GLfloat) zmin; + ctx->Depth.BoundsMax = (GLfloat) zmax; +} + + +/**********************************************************************/ +/***** Initialization *****/ +/**********************************************************************/ + + +/** + * Initialize the depth buffer attribute group in the given context. + */ +void +_mesa_init_depth(struct gl_context *ctx) +{ + ctx->Depth.Test = GL_FALSE; + ctx->Depth.Clear = 1.0; + ctx->Depth.Func = GL_LESS; + ctx->Depth.Mask = GL_TRUE; +} diff --git a/mesalib/src/mesa/main/hint.c b/mesalib/src/mesa/main/hint.c index 96f5f4d6b..ff8d88fff 100644 --- a/mesalib/src/mesa/main/hint.c +++ b/mesalib/src/mesa/main/hint.c @@ -1,146 +1,147 @@ - -/* - * Mesa 3-D graphics library - * Version: 4.1 - * - * Copyright (C) 1999-2002 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 - * BRIAN PAUL 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 "glheader.h" -#include "enums.h" -#include "context.h" -#include "hint.h" -#include "imports.h" -#include "mtypes.h" - - - -void GLAPIENTRY -_mesa_Hint( GLenum target, GLenum mode ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, "glHint %s %d\n", - _mesa_lookup_enum_by_nr(target), mode); - - if (mode != GL_NICEST && mode != GL_FASTEST && mode != GL_DONT_CARE) { - _mesa_error(ctx, GL_INVALID_ENUM, "glHint(mode)"); - return; - } - - switch (target) { - case GL_FOG_HINT: - if (ctx->Hint.Fog == mode) - return; - FLUSH_VERTICES(ctx, _NEW_HINT); - ctx->Hint.Fog = mode; - break; - case GL_LINE_SMOOTH_HINT: - if (ctx->Hint.LineSmooth == mode) - return; - FLUSH_VERTICES(ctx, _NEW_HINT); - ctx->Hint.LineSmooth = mode; - break; - case GL_PERSPECTIVE_CORRECTION_HINT: - if (ctx->Hint.PerspectiveCorrection == mode) - return; - FLUSH_VERTICES(ctx, _NEW_HINT); - ctx->Hint.PerspectiveCorrection = mode; - break; - case GL_POINT_SMOOTH_HINT: - if (ctx->Hint.PointSmooth == mode) - return; - FLUSH_VERTICES(ctx, _NEW_HINT); - ctx->Hint.PointSmooth = mode; - break; - case GL_POLYGON_SMOOTH_HINT: - if (ctx->Hint.PolygonSmooth == mode) - return; - FLUSH_VERTICES(ctx, _NEW_HINT); - ctx->Hint.PolygonSmooth = mode; - break; - - /* GL_EXT_clip_volume_hint */ - case GL_CLIP_VOLUME_CLIPPING_HINT_EXT: - if (ctx->Hint.ClipVolumeClipping == mode) - return; - FLUSH_VERTICES(ctx, _NEW_HINT); - ctx->Hint.ClipVolumeClipping = mode; - break; - - /* GL_ARB_texture_compression */ - case GL_TEXTURE_COMPRESSION_HINT_ARB: - if (ctx->Hint.TextureCompression == mode) - return; - FLUSH_VERTICES(ctx, _NEW_HINT); - ctx->Hint.TextureCompression = mode; - break; - - /* GL_SGIS_generate_mipmap */ - case GL_GENERATE_MIPMAP_HINT_SGIS: - if (ctx->Hint.GenerateMipmap == mode) - return; - FLUSH_VERTICES(ctx, _NEW_HINT); - ctx->Hint.GenerateMipmap = mode; - break; - - /* GL_ARB_fragment_shader */ - case GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB: - if (!ctx->Extensions.ARB_fragment_shader) { - _mesa_error(ctx, GL_INVALID_ENUM, "glHint(target)"); - return; - } - if (ctx->Hint.FragmentShaderDerivative == mode) - return; - FLUSH_VERTICES(ctx, _NEW_HINT); - ctx->Hint.FragmentShaderDerivative = mode; - break; - - default: - _mesa_error(ctx, GL_INVALID_ENUM, "glHint(target)"); - return; - } - - if (ctx->Driver.Hint) { - (*ctx->Driver.Hint)( ctx, target, mode ); - } -} - - -/**********************************************************************/ -/***** Initialization *****/ -/**********************************************************************/ - -void _mesa_init_hint( struct gl_context * ctx ) -{ - /* Hint group */ - ctx->Hint.PerspectiveCorrection = GL_DONT_CARE; - ctx->Hint.PointSmooth = GL_DONT_CARE; - ctx->Hint.LineSmooth = GL_DONT_CARE; - ctx->Hint.PolygonSmooth = GL_DONT_CARE; - ctx->Hint.Fog = GL_DONT_CARE; - ctx->Hint.ClipVolumeClipping = GL_DONT_CARE; - ctx->Hint.TextureCompression = GL_DONT_CARE; - ctx->Hint.GenerateMipmap = GL_DONT_CARE; - ctx->Hint.FragmentShaderDerivative = GL_DONT_CARE; -} + +/* + * Mesa 3-D graphics library + * Version: 4.1 + * + * Copyright (C) 1999-2002 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 + * BRIAN PAUL 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 "glheader.h" +#include "enums.h" +#include "context.h" +#include "hint.h" +#include "imports.h" +#include "mtypes.h" + + + +void GLAPIENTRY +_mesa_Hint( GLenum target, GLenum mode ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glHint %s %s\n", + _mesa_lookup_enum_by_nr(target), + _mesa_lookup_enum_by_nr(mode)); + + if (mode != GL_NICEST && mode != GL_FASTEST && mode != GL_DONT_CARE) { + _mesa_error(ctx, GL_INVALID_ENUM, "glHint(mode)"); + return; + } + + switch (target) { + case GL_FOG_HINT: + if (ctx->Hint.Fog == mode) + return; + FLUSH_VERTICES(ctx, _NEW_HINT); + ctx->Hint.Fog = mode; + break; + case GL_LINE_SMOOTH_HINT: + if (ctx->Hint.LineSmooth == mode) + return; + FLUSH_VERTICES(ctx, _NEW_HINT); + ctx->Hint.LineSmooth = mode; + break; + case GL_PERSPECTIVE_CORRECTION_HINT: + if (ctx->Hint.PerspectiveCorrection == mode) + return; + FLUSH_VERTICES(ctx, _NEW_HINT); + ctx->Hint.PerspectiveCorrection = mode; + break; + case GL_POINT_SMOOTH_HINT: + if (ctx->Hint.PointSmooth == mode) + return; + FLUSH_VERTICES(ctx, _NEW_HINT); + ctx->Hint.PointSmooth = mode; + break; + case GL_POLYGON_SMOOTH_HINT: + if (ctx->Hint.PolygonSmooth == mode) + return; + FLUSH_VERTICES(ctx, _NEW_HINT); + ctx->Hint.PolygonSmooth = mode; + break; + + /* GL_EXT_clip_volume_hint */ + case GL_CLIP_VOLUME_CLIPPING_HINT_EXT: + if (ctx->Hint.ClipVolumeClipping == mode) + return; + FLUSH_VERTICES(ctx, _NEW_HINT); + ctx->Hint.ClipVolumeClipping = mode; + break; + + /* GL_ARB_texture_compression */ + case GL_TEXTURE_COMPRESSION_HINT_ARB: + if (ctx->Hint.TextureCompression == mode) + return; + FLUSH_VERTICES(ctx, _NEW_HINT); + ctx->Hint.TextureCompression = mode; + break; + + /* GL_SGIS_generate_mipmap */ + case GL_GENERATE_MIPMAP_HINT_SGIS: + if (ctx->Hint.GenerateMipmap == mode) + return; + FLUSH_VERTICES(ctx, _NEW_HINT); + ctx->Hint.GenerateMipmap = mode; + break; + + /* GL_ARB_fragment_shader */ + case GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB: + if (!ctx->Extensions.ARB_fragment_shader) { + _mesa_error(ctx, GL_INVALID_ENUM, "glHint(target)"); + return; + } + if (ctx->Hint.FragmentShaderDerivative == mode) + return; + FLUSH_VERTICES(ctx, _NEW_HINT); + ctx->Hint.FragmentShaderDerivative = mode; + break; + + default: + _mesa_error(ctx, GL_INVALID_ENUM, "glHint(target)"); + return; + } + + if (ctx->Driver.Hint) { + (*ctx->Driver.Hint)( ctx, target, mode ); + } +} + + +/**********************************************************************/ +/***** Initialization *****/ +/**********************************************************************/ + +void _mesa_init_hint( struct gl_context * ctx ) +{ + /* Hint group */ + ctx->Hint.PerspectiveCorrection = GL_DONT_CARE; + ctx->Hint.PointSmooth = GL_DONT_CARE; + ctx->Hint.LineSmooth = GL_DONT_CARE; + ctx->Hint.PolygonSmooth = GL_DONT_CARE; + ctx->Hint.Fog = GL_DONT_CARE; + ctx->Hint.ClipVolumeClipping = GL_DONT_CARE; + ctx->Hint.TextureCompression = GL_DONT_CARE; + ctx->Hint.GenerateMipmap = GL_DONT_CARE; + ctx->Hint.FragmentShaderDerivative = GL_DONT_CARE; +} diff --git a/mesalib/src/mesa/main/lines.c b/mesalib/src/mesa/main/lines.c index 79385cd75..79bf5679d 100644 --- a/mesalib/src/mesa/main/lines.c +++ b/mesalib/src/mesa/main/lines.c @@ -1,111 +1,117 @@ -/* - * Mesa 3-D graphics library - * Version: 6.5.3 - * - * Copyright (C) 1999-2006 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 - * BRIAN PAUL 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 "glheader.h" -#include "context.h" -#include "lines.h" -#include "macros.h" -#include "mtypes.h" - - -/** - * Set the line width. - * - * \param width line width in pixels. - * - * \sa glLineWidth(). - */ -void GLAPIENTRY -_mesa_LineWidth( GLfloat width ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (width<=0.0) { - _mesa_error( ctx, GL_INVALID_VALUE, "glLineWidth" ); - return; - } - - if (ctx->Line.Width == width) - return; - - FLUSH_VERTICES(ctx, _NEW_LINE); - ctx->Line.Width = width; - - if (ctx->Driver.LineWidth) - ctx->Driver.LineWidth(ctx, width); -} - - -/** - * Set the line stipple pattern. - * - * \param factor pattern scale factor. - * \param pattern bit pattern. - * - * \sa glLineStipple(). - * - * Updates gl_line_attrib::StippleFactor and gl_line_attrib::StipplePattern. On - * change flushes the vertices and notifies the driver via - * the dd_function_table::LineStipple callback. - */ -void GLAPIENTRY -_mesa_LineStipple( GLint factor, GLushort pattern ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - factor = CLAMP( factor, 1, 256 ); - - if (ctx->Line.StippleFactor == factor && - ctx->Line.StipplePattern == pattern) - return; - - FLUSH_VERTICES(ctx, _NEW_LINE); - ctx->Line.StippleFactor = factor; - ctx->Line.StipplePattern = pattern; - - if (ctx->Driver.LineStipple) - ctx->Driver.LineStipple( ctx, factor, pattern ); -} - - -/** - * Initialize the context line state. - * - * \param ctx GL context. - * - * Initializes __struct gl_contextRec::Line and line related constants in - * __struct gl_contextRec::Const. - */ -void GLAPIENTRY -_mesa_init_line( struct gl_context * ctx ) -{ - ctx->Line.SmoothFlag = GL_FALSE; - ctx->Line.StippleFlag = GL_FALSE; - ctx->Line.Width = 1.0; - ctx->Line.StipplePattern = 0xffff; - ctx->Line.StippleFactor = 1; -} +/* + * Mesa 3-D graphics library + * Version: 6.5.3 + * + * Copyright (C) 1999-2006 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 + * BRIAN PAUL 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 "glheader.h" +#include "context.h" +#include "lines.h" +#include "macros.h" +#include "mtypes.h" + + +/** + * Set the line width. + * + * \param width line width in pixels. + * + * \sa glLineWidth(). + */ +void GLAPIENTRY +_mesa_LineWidth( GLfloat width ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glLineWidth %f\n", width); + + if (width<=0.0) { + _mesa_error( ctx, GL_INVALID_VALUE, "glLineWidth" ); + return; + } + + if (ctx->Line.Width == width) + return; + + FLUSH_VERTICES(ctx, _NEW_LINE); + ctx->Line.Width = width; + + if (ctx->Driver.LineWidth) + ctx->Driver.LineWidth(ctx, width); +} + + +/** + * Set the line stipple pattern. + * + * \param factor pattern scale factor. + * \param pattern bit pattern. + * + * \sa glLineStipple(). + * + * Updates gl_line_attrib::StippleFactor and gl_line_attrib::StipplePattern. On + * change flushes the vertices and notifies the driver via + * the dd_function_table::LineStipple callback. + */ +void GLAPIENTRY +_mesa_LineStipple( GLint factor, GLushort pattern ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glLineStipple %d %u\n", factor, pattern); + + factor = CLAMP( factor, 1, 256 ); + + if (ctx->Line.StippleFactor == factor && + ctx->Line.StipplePattern == pattern) + return; + + FLUSH_VERTICES(ctx, _NEW_LINE); + ctx->Line.StippleFactor = factor; + ctx->Line.StipplePattern = pattern; + + if (ctx->Driver.LineStipple) + ctx->Driver.LineStipple( ctx, factor, pattern ); +} + + +/** + * Initialize the context line state. + * + * \param ctx GL context. + * + * Initializes __struct gl_contextRec::Line and line related constants in + * __struct gl_contextRec::Const. + */ +void GLAPIENTRY +_mesa_init_line( struct gl_context * ctx ) +{ + ctx->Line.SmoothFlag = GL_FALSE; + ctx->Line.StippleFlag = GL_FALSE; + ctx->Line.Width = 1.0; + ctx->Line.StipplePattern = 0xffff; + ctx->Line.StippleFactor = 1; +} diff --git a/mesalib/src/mesa/main/mtypes.h b/mesalib/src/mesa/main/mtypes.h index 4e7621239..6f0aac6ee 100644 --- a/mesalib/src/mesa/main/mtypes.h +++ b/mesalib/src/mesa/main/mtypes.h @@ -84,23 +84,8 @@ /*@{*/ typedef GLuint64 GLbitfield64; -#define BITFIELD64_ONE 1ULL -#define BITFIELD64_ALLONES ~0ULL - /** Set a single bit */ -#define BITFIELD64_BIT(b) (BITFIELD64_ONE << (b)) - -/** Set a mask of the least significant \c b bits */ -#define BITFIELD64_MASK(b) (((b) >= 64) ? BITFIELD64_ALLONES : \ - (BITFIELD64_BIT(b) - 1)) - -/** - * Set all bits from l (low bit) to h (high bit), inclusive. - * - * \note \C BITFIELD_64_RANGE(0, 63) return 64 set bits. - */ -#define BITFIELD64_RANGE(l, h) (BITFIELD64_MASK((h) + 1) & ~BITFIELD64_MASK(l)) -/*@}*/ +#define BITFIELD64_BIT(b) (1ULL << (b)) /** diff --git a/mesalib/src/mesa/main/queryobj.c b/mesalib/src/mesa/main/queryobj.c index 1c45c38b9..fa35c6ce5 100644 --- a/mesalib/src/mesa/main/queryobj.c +++ b/mesalib/src/mesa/main/queryobj.c @@ -1,585 +1,623 @@ -/* - * Mesa 3-D graphics library - * Version: 7.1 - * - * 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 - * BRIAN PAUL 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 "glheader.h" -#include "context.h" -#include "hash.h" -#include "imports.h" -#include "queryobj.h" -#include "mfeatures.h" -#include "mtypes.h" -#include "main/dispatch.h" - - -#if FEATURE_queryobj - - -/** - * Allocate a new query object. This is a fallback routine called via - * ctx->Driver.NewQueryObject(). - * \param ctx - rendering context - * \param id - the new object's ID - * \return pointer to new query_object object or NULL if out of memory. - */ -static struct gl_query_object * -_mesa_new_query_object(struct gl_context *ctx, GLuint id) -{ - struct gl_query_object *q = MALLOC_STRUCT(gl_query_object); - (void) ctx; - if (q) { - q->Id = id; - q->Result = 0; - q->Active = GL_FALSE; - q->Ready = GL_TRUE; /* correct, see spec */ - } - return q; -} - - -/** - * Begin a query. Software driver fallback. - * Called via ctx->Driver.BeginQuery(). - */ -static void -_mesa_begin_query(struct gl_context *ctx, struct gl_query_object *q) -{ - /* no-op */ -} - - -/** - * End a query. Software driver fallback. - * Called via ctx->Driver.EndQuery(). - */ -static void -_mesa_end_query(struct gl_context *ctx, struct gl_query_object *q) -{ - q->Ready = GL_TRUE; -} - - -/** - * Wait for query to complete. Software driver fallback. - * Called via ctx->Driver.WaitQuery(). - */ -static void -_mesa_wait_query(struct gl_context *ctx, struct gl_query_object *q) -{ - /* For software drivers, _mesa_end_query() should have completed the query. - * For real hardware, implement a proper WaitQuery() driver function, - * which may require issuing a flush. - */ - assert(q->Ready); -} - - -/** - * Check if a query results are ready. Software driver fallback. - * Called via ctx->Driver.CheckQuery(). - */ -static void -_mesa_check_query(struct gl_context *ctx, struct gl_query_object *q) -{ - /* No-op for sw rendering. - * HW drivers may need to flush at this time. - */ -} - - -/** - * Delete a query object. Called via ctx->Driver.DeleteQuery(). - * Not removed from hash table here. - */ -static void -_mesa_delete_query(struct gl_context *ctx, struct gl_query_object *q) -{ - free(q); -} - - -void -_mesa_init_query_object_functions(struct dd_function_table *driver) -{ - driver->NewQueryObject = _mesa_new_query_object; - driver->DeleteQuery = _mesa_delete_query; - driver->BeginQuery = _mesa_begin_query; - driver->EndQuery = _mesa_end_query; - driver->WaitQuery = _mesa_wait_query; - driver->CheckQuery = _mesa_check_query; -} - - -/** - * Return pointer to the query object binding point for the given target. - * \return NULL if invalid target, else the address of binding point - */ -static struct gl_query_object ** -get_query_binding_point(struct gl_context *ctx, GLenum target) -{ - switch (target) { - case GL_SAMPLES_PASSED_ARB: - if (ctx->Extensions.ARB_occlusion_query) - return &ctx->Query.CurrentOcclusionObject; - else - return NULL; - case GL_ANY_SAMPLES_PASSED: - if (ctx->Extensions.ARB_occlusion_query2) - return &ctx->Query.CurrentOcclusionObject; - else - return NULL; - case GL_TIME_ELAPSED_EXT: - if (ctx->Extensions.EXT_timer_query) - return &ctx->Query.CurrentTimerObject; - else - return NULL; -#if FEATURE_EXT_transform_feedback - case GL_PRIMITIVES_GENERATED: - if (ctx->Extensions.EXT_transform_feedback) - return &ctx->Query.PrimitivesGenerated; - else - return NULL; - case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: - if (ctx->Extensions.EXT_transform_feedback) - return &ctx->Query.PrimitivesWritten; - else - return NULL; -#endif - default: - return NULL; - } -} - - -void GLAPIENTRY -_mesa_GenQueriesARB(GLsizei n, GLuint *ids) -{ - GLuint first; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (n < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)"); - return; - } - - /* No query objects can be active at this time! */ - if (ctx->Query.CurrentOcclusionObject || - ctx->Query.CurrentTimerObject) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glGenQueriesARB"); - return; - } - - first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n); - if (first) { - GLsizei i; - for (i = 0; i < n; i++) { - struct gl_query_object *q - = ctx->Driver.NewQueryObject(ctx, first + i); - if (!q) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB"); - return; - } - ids[i] = first + i; - _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q); - } - } -} - - -void GLAPIENTRY -_mesa_DeleteQueriesARB(GLsizei n, const GLuint *ids) -{ - GLint i; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (n < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)"); - return; - } - - /* No query objects can be active at this time! */ - if (ctx->Query.CurrentOcclusionObject || - ctx->Query.CurrentTimerObject) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glDeleteQueriesARB"); - return; - } - - for (i = 0; i < n; i++) { - if (ids[i] > 0) { - struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]); - if (q) { - ASSERT(!q->Active); /* should be caught earlier */ - _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]); - ctx->Driver.DeleteQuery(ctx, q); - } - } - } -} - - -GLboolean GLAPIENTRY -_mesa_IsQueryARB(GLuint id) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); - - if (id && _mesa_lookup_query_object(ctx, id)) - return GL_TRUE; - else - return GL_FALSE; -} - - -static void GLAPIENTRY -_mesa_BeginQueryARB(GLenum target, GLuint id) -{ - struct gl_query_object *q, **bindpt; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - FLUSH_VERTICES(ctx, _NEW_DEPTH); - - bindpt = get_query_binding_point(ctx, target); - if (!bindpt) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)"); - return; - } - - if (id == 0) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB(id==0)"); - return; - } - - q = _mesa_lookup_query_object(ctx, id); - if (!q) { - /* create new object */ - q = ctx->Driver.NewQueryObject(ctx, id); - if (!q) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQueryARB"); - return; - } - _mesa_HashInsert(ctx->Query.QueryObjects, id, q); - } - else { - /* pre-existing object */ - if (q->Active) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBeginQueryARB(query already active)"); - return; - } - } - - q->Target = target; - q->Active = GL_TRUE; - q->Result = 0; - q->Ready = GL_FALSE; - - /* XXX should probably refcount query objects */ - *bindpt = q; - - ctx->Driver.BeginQuery(ctx, q); -} - - -static void GLAPIENTRY -_mesa_EndQueryARB(GLenum target) -{ - struct gl_query_object *q, **bindpt; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - FLUSH_VERTICES(ctx, _NEW_DEPTH); - - bindpt = get_query_binding_point(ctx, target); - if (!bindpt) { - _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)"); - return; - } - - /* XXX should probably refcount query objects */ - q = *bindpt; - *bindpt = NULL; - - if (!q || !q->Active) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glEndQueryARB(no matching glBeginQueryARB)"); - return; - } - - q->Active = GL_FALSE; - ctx->Driver.EndQuery(ctx, q); -} - - -void GLAPIENTRY -_mesa_GetQueryivARB(GLenum target, GLenum pname, GLint *params) -{ - struct gl_query_object *q, **bindpt; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - bindpt = get_query_binding_point(ctx, target); - if (!bindpt) { - _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)"); - return; - } - - q = *bindpt; - - switch (pname) { - case GL_QUERY_COUNTER_BITS_ARB: - *params = 8 * sizeof(q->Result); - break; - case GL_CURRENT_QUERY_ARB: - *params = q ? q->Id : 0; - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(pname)"); - return; - } -} - - -void GLAPIENTRY -_mesa_GetQueryObjectivARB(GLuint id, GLenum pname, GLint *params) -{ - struct gl_query_object *q = NULL; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (id) - q = _mesa_lookup_query_object(ctx, id); - - if (!q || q->Active) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetQueryObjectivARB(id=%d is invalid or active)", id); - return; - } - - switch (pname) { - case GL_QUERY_RESULT_ARB: - if (!q->Ready) - ctx->Driver.WaitQuery(ctx, q); - /* if result is too large for returned type, clamp to max value */ - if (q->Target == GL_ANY_SAMPLES_PASSED) { - if (q->Result) - *params = GL_TRUE; - else - *params = GL_FALSE; - } else { - if (q->Result > 0x7fffffff) { - *params = 0x7fffffff; - } - else { - *params = (GLint)q->Result; - } - } - break; - case GL_QUERY_RESULT_AVAILABLE_ARB: - if (!q->Ready) - ctx->Driver.CheckQuery( ctx, q ); - *params = q->Ready; - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)"); - return; - } -} - - -void GLAPIENTRY -_mesa_GetQueryObjectuivARB(GLuint id, GLenum pname, GLuint *params) -{ - struct gl_query_object *q = NULL; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (id) - q = _mesa_lookup_query_object(ctx, id); - - if (!q || q->Active) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetQueryObjectuivARB(id=%d is invalid or active)", id); - return; - } - - switch (pname) { - case GL_QUERY_RESULT_ARB: - if (!q->Ready) - ctx->Driver.WaitQuery(ctx, q); - /* if result is too large for returned type, clamp to max value */ - if (q->Target == GL_ANY_SAMPLES_PASSED) { - if (q->Result) - *params = GL_TRUE; - else - *params = GL_FALSE; - } else { - if (q->Result > 0xffffffff) { - *params = 0xffffffff; - } - else { - *params = (GLuint)q->Result; - } - } - break; - case GL_QUERY_RESULT_AVAILABLE_ARB: - if (!q->Ready) - ctx->Driver.CheckQuery( ctx, q ); - *params = q->Ready; - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)"); - return; - } -} - - -/** - * New with GL_EXT_timer_query - */ -static void GLAPIENTRY -_mesa_GetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64EXT *params) -{ - struct gl_query_object *q = NULL; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (id) - q = _mesa_lookup_query_object(ctx, id); - - if (!q || q->Active) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetQueryObjectui64vARB(id=%d is invalid or active)", id); - return; - } - - switch (pname) { - case GL_QUERY_RESULT_ARB: - if (!q->Ready) - ctx->Driver.WaitQuery(ctx, q); - *params = q->Result; - break; - case GL_QUERY_RESULT_AVAILABLE_ARB: - if (!q->Ready) - ctx->Driver.CheckQuery( ctx, q ); - *params = q->Ready; - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjecti64vARB(pname)"); - return; - } -} - - -/** - * New with GL_EXT_timer_query - */ -static void GLAPIENTRY -_mesa_GetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64EXT *params) -{ - struct gl_query_object *q = NULL; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (id) - q = _mesa_lookup_query_object(ctx, id); - - if (!q || q->Active) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id); - return; - } - - switch (pname) { - case GL_QUERY_RESULT_ARB: - if (!q->Ready) - ctx->Driver.WaitQuery(ctx, q); - *params = q->Result; - break; - case GL_QUERY_RESULT_AVAILABLE_ARB: - if (!q->Ready) - ctx->Driver.CheckQuery( ctx, q ); - *params = q->Ready; - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectui64vARB(pname)"); - return; - } -} - - -void -_mesa_init_queryobj_dispatch(struct _glapi_table *disp) -{ - SET_GenQueriesARB(disp, _mesa_GenQueriesARB); - SET_DeleteQueriesARB(disp, _mesa_DeleteQueriesARB); - SET_IsQueryARB(disp, _mesa_IsQueryARB); - SET_BeginQueryARB(disp, _mesa_BeginQueryARB); - SET_EndQueryARB(disp, _mesa_EndQueryARB); - SET_GetQueryivARB(disp, _mesa_GetQueryivARB); - SET_GetQueryObjectivARB(disp, _mesa_GetQueryObjectivARB); - SET_GetQueryObjectuivARB(disp, _mesa_GetQueryObjectuivARB); - - SET_GetQueryObjecti64vEXT(disp, _mesa_GetQueryObjecti64vEXT); - SET_GetQueryObjectui64vEXT(disp, _mesa_GetQueryObjectui64vEXT); -} - - -#endif /* FEATURE_queryobj */ - - -/** - * Allocate/init the context state related to query objects. - */ -void -_mesa_init_queryobj(struct gl_context *ctx) -{ - ctx->Query.QueryObjects = _mesa_NewHashTable(); - ctx->Query.CurrentOcclusionObject = NULL; -} - - -/** - * Callback for deleting a query object. Called by _mesa_HashDeleteAll(). - */ -static void -delete_queryobj_cb(GLuint id, void *data, void *userData) -{ - struct gl_query_object *q= (struct gl_query_object *) data; - struct gl_context *ctx = (struct gl_context *)userData; - ctx->Driver.DeleteQuery(ctx, q); -} - - -/** - * Free the context state related to query objects. - */ -void -_mesa_free_queryobj_data(struct gl_context *ctx) -{ - _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx); - _mesa_DeleteHashTable(ctx->Query.QueryObjects); -} +/* + * Mesa 3-D graphics library + * Version: 7.1 + * + * 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 + * BRIAN PAUL 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 "glheader.h" +#include "context.h" +#include "enums.h" +#include "hash.h" +#include "imports.h" +#include "queryobj.h" +#include "mfeatures.h" +#include "mtypes.h" +#include "main/dispatch.h" + + +#if FEATURE_queryobj + + +/** + * Allocate a new query object. This is a fallback routine called via + * ctx->Driver.NewQueryObject(). + * \param ctx - rendering context + * \param id - the new object's ID + * \return pointer to new query_object object or NULL if out of memory. + */ +static struct gl_query_object * +_mesa_new_query_object(struct gl_context *ctx, GLuint id) +{ + struct gl_query_object *q = MALLOC_STRUCT(gl_query_object); + (void) ctx; + if (q) { + q->Id = id; + q->Result = 0; + q->Active = GL_FALSE; + q->Ready = GL_TRUE; /* correct, see spec */ + } + return q; +} + + +/** + * Begin a query. Software driver fallback. + * Called via ctx->Driver.BeginQuery(). + */ +static void +_mesa_begin_query(struct gl_context *ctx, struct gl_query_object *q) +{ + /* no-op */ +} + + +/** + * End a query. Software driver fallback. + * Called via ctx->Driver.EndQuery(). + */ +static void +_mesa_end_query(struct gl_context *ctx, struct gl_query_object *q) +{ + q->Ready = GL_TRUE; +} + + +/** + * Wait for query to complete. Software driver fallback. + * Called via ctx->Driver.WaitQuery(). + */ +static void +_mesa_wait_query(struct gl_context *ctx, struct gl_query_object *q) +{ + /* For software drivers, _mesa_end_query() should have completed the query. + * For real hardware, implement a proper WaitQuery() driver function, + * which may require issuing a flush. + */ + assert(q->Ready); +} + + +/** + * Check if a query results are ready. Software driver fallback. + * Called via ctx->Driver.CheckQuery(). + */ +static void +_mesa_check_query(struct gl_context *ctx, struct gl_query_object *q) +{ + /* No-op for sw rendering. + * HW drivers may need to flush at this time. + */ +} + + +/** + * Delete a query object. Called via ctx->Driver.DeleteQuery(). + * Not removed from hash table here. + */ +static void +_mesa_delete_query(struct gl_context *ctx, struct gl_query_object *q) +{ + free(q); +} + + +void +_mesa_init_query_object_functions(struct dd_function_table *driver) +{ + driver->NewQueryObject = _mesa_new_query_object; + driver->DeleteQuery = _mesa_delete_query; + driver->BeginQuery = _mesa_begin_query; + driver->EndQuery = _mesa_end_query; + driver->WaitQuery = _mesa_wait_query; + driver->CheckQuery = _mesa_check_query; +} + + +/** + * Return pointer to the query object binding point for the given target. + * \return NULL if invalid target, else the address of binding point + */ +static struct gl_query_object ** +get_query_binding_point(struct gl_context *ctx, GLenum target) +{ + switch (target) { + case GL_SAMPLES_PASSED_ARB: + if (ctx->Extensions.ARB_occlusion_query) + return &ctx->Query.CurrentOcclusionObject; + else + return NULL; + case GL_ANY_SAMPLES_PASSED: + if (ctx->Extensions.ARB_occlusion_query2) + return &ctx->Query.CurrentOcclusionObject; + else + return NULL; + case GL_TIME_ELAPSED_EXT: + if (ctx->Extensions.EXT_timer_query) + return &ctx->Query.CurrentTimerObject; + else + return NULL; +#if FEATURE_EXT_transform_feedback + case GL_PRIMITIVES_GENERATED: + if (ctx->Extensions.EXT_transform_feedback) + return &ctx->Query.PrimitivesGenerated; + else + return NULL; + case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: + if (ctx->Extensions.EXT_transform_feedback) + return &ctx->Query.PrimitivesWritten; + else + return NULL; +#endif + default: + return NULL; + } +} + + +void GLAPIENTRY +_mesa_GenQueriesARB(GLsizei n, GLuint *ids) +{ + GLuint first; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glGenQueries(%d)\n", n); + + if (n < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "glGenQueriesARB(n < 0)"); + return; + } + + /* No query objects can be active at this time! */ + if (ctx->Query.CurrentOcclusionObject || + ctx->Query.CurrentTimerObject) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glGenQueriesARB"); + return; + } + + first = _mesa_HashFindFreeKeyBlock(ctx->Query.QueryObjects, n); + if (first) { + GLsizei i; + for (i = 0; i < n; i++) { + struct gl_query_object *q + = ctx->Driver.NewQueryObject(ctx, first + i); + if (!q) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenQueriesARB"); + return; + } + ids[i] = first + i; + _mesa_HashInsert(ctx->Query.QueryObjects, first + i, q); + } + } +} + + +void GLAPIENTRY +_mesa_DeleteQueriesARB(GLsizei n, const GLuint *ids) +{ + GLint i; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glDeleeteQueries(%d)\n", n); + + if (n < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteQueriesARB(n < 0)"); + return; + } + + /* No query objects can be active at this time! */ + if (ctx->Query.CurrentOcclusionObject || + ctx->Query.CurrentTimerObject) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glDeleteQueriesARB"); + return; + } + + for (i = 0; i < n; i++) { + if (ids[i] > 0) { + struct gl_query_object *q = _mesa_lookup_query_object(ctx, ids[i]); + if (q) { + ASSERT(!q->Active); /* should be caught earlier */ + _mesa_HashRemove(ctx->Query.QueryObjects, ids[i]); + ctx->Driver.DeleteQuery(ctx, q); + } + } + } +} + + +GLboolean GLAPIENTRY +_mesa_IsQueryARB(GLuint id) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glIsQuery(%u)\n", id); + + if (id && _mesa_lookup_query_object(ctx, id)) + return GL_TRUE; + else + return GL_FALSE; +} + + +static void GLAPIENTRY +_mesa_BeginQueryARB(GLenum target, GLuint id) +{ + struct gl_query_object *q, **bindpt; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glBeginQuery(%s, %u)\n", + _mesa_lookup_enum_by_nr(target), id); + + FLUSH_VERTICES(ctx, _NEW_DEPTH); + + bindpt = get_query_binding_point(ctx, target); + if (!bindpt) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBeginQueryARB(target)"); + return; + } + + if (id == 0) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glBeginQueryARB(id==0)"); + return; + } + + q = _mesa_lookup_query_object(ctx, id); + if (!q) { + /* create new object */ + q = ctx->Driver.NewQueryObject(ctx, id); + if (!q) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBeginQueryARB"); + return; + } + _mesa_HashInsert(ctx->Query.QueryObjects, id, q); + } + else { + /* pre-existing object */ + if (q->Active) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBeginQueryARB(query already active)"); + return; + } + } + + q->Target = target; + q->Active = GL_TRUE; + q->Result = 0; + q->Ready = GL_FALSE; + + /* XXX should probably refcount query objects */ + *bindpt = q; + + ctx->Driver.BeginQuery(ctx, q); +} + + +static void GLAPIENTRY +_mesa_EndQueryARB(GLenum target) +{ + struct gl_query_object *q, **bindpt; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glEndQuery(%s)\n", _mesa_lookup_enum_by_nr(target)); + + FLUSH_VERTICES(ctx, _NEW_DEPTH); + + bindpt = get_query_binding_point(ctx, target); + if (!bindpt) { + _mesa_error(ctx, GL_INVALID_ENUM, "glEndQueryARB(target)"); + return; + } + + /* XXX should probably refcount query objects */ + q = *bindpt; + *bindpt = NULL; + + if (!q || !q->Active) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glEndQueryARB(no matching glBeginQueryARB)"); + return; + } + + q->Active = GL_FALSE; + ctx->Driver.EndQuery(ctx, q); +} + + +void GLAPIENTRY +_mesa_GetQueryivARB(GLenum target, GLenum pname, GLint *params) +{ + struct gl_query_object *q, **bindpt; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glGetQueryiv(%s, %s)\n", + _mesa_lookup_enum_by_nr(target), + _mesa_lookup_enum_by_nr(pname)); + + bindpt = get_query_binding_point(ctx, target); + if (!bindpt) { + _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryARB(target)"); + return; + } + + q = *bindpt; + + switch (pname) { + case GL_QUERY_COUNTER_BITS_ARB: + *params = 8 * sizeof(q->Result); + break; + case GL_CURRENT_QUERY_ARB: + *params = q ? q->Id : 0; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryivARB(pname)"); + return; + } +} + + +void GLAPIENTRY +_mesa_GetQueryObjectivARB(GLuint id, GLenum pname, GLint *params) +{ + struct gl_query_object *q = NULL; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glGetQueryObjectiv(%u, %s)\n", id, + _mesa_lookup_enum_by_nr(pname)); + + if (id) + q = _mesa_lookup_query_object(ctx, id); + + if (!q || q->Active) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glGetQueryObjectivARB(id=%d is invalid or active)", id); + return; + } + + switch (pname) { + case GL_QUERY_RESULT_ARB: + if (!q->Ready) + ctx->Driver.WaitQuery(ctx, q); + /* if result is too large for returned type, clamp to max value */ + if (q->Target == GL_ANY_SAMPLES_PASSED) { + if (q->Result) + *params = GL_TRUE; + else + *params = GL_FALSE; + } else { + if (q->Result > 0x7fffffff) { + *params = 0x7fffffff; + } + else { + *params = (GLint)q->Result; + } + } + break; + case GL_QUERY_RESULT_AVAILABLE_ARB: + if (!q->Ready) + ctx->Driver.CheckQuery( ctx, q ); + *params = q->Ready; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectivARB(pname)"); + return; + } +} + + +void GLAPIENTRY +_mesa_GetQueryObjectuivARB(GLuint id, GLenum pname, GLuint *params) +{ + struct gl_query_object *q = NULL; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glGetQueryObjectuiv(%u, %s)\n", id, + _mesa_lookup_enum_by_nr(pname)); + + if (id) + q = _mesa_lookup_query_object(ctx, id); + + if (!q || q->Active) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glGetQueryObjectuivARB(id=%d is invalid or active)", id); + return; + } + + switch (pname) { + case GL_QUERY_RESULT_ARB: + if (!q->Ready) + ctx->Driver.WaitQuery(ctx, q); + /* if result is too large for returned type, clamp to max value */ + if (q->Target == GL_ANY_SAMPLES_PASSED) { + if (q->Result) + *params = GL_TRUE; + else + *params = GL_FALSE; + } else { + if (q->Result > 0xffffffff) { + *params = 0xffffffff; + } + else { + *params = (GLuint)q->Result; + } + } + break; + case GL_QUERY_RESULT_AVAILABLE_ARB: + if (!q->Ready) + ctx->Driver.CheckQuery( ctx, q ); + *params = q->Ready; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectuivARB(pname)"); + return; + } +} + + +/** + * New with GL_EXT_timer_query + */ +static void GLAPIENTRY +_mesa_GetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64EXT *params) +{ + struct gl_query_object *q = NULL; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glGetQueryObjecti64v(%u, %s)\n", id, + _mesa_lookup_enum_by_nr(pname)); + + if (id) + q = _mesa_lookup_query_object(ctx, id); + + if (!q || q->Active) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glGetQueryObjectui64vARB(id=%d is invalid or active)", id); + return; + } + + switch (pname) { + case GL_QUERY_RESULT_ARB: + if (!q->Ready) + ctx->Driver.WaitQuery(ctx, q); + *params = q->Result; + break; + case GL_QUERY_RESULT_AVAILABLE_ARB: + if (!q->Ready) + ctx->Driver.CheckQuery( ctx, q ); + *params = q->Ready; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjecti64vARB(pname)"); + return; + } +} + + +/** + * New with GL_EXT_timer_query + */ +static void GLAPIENTRY +_mesa_GetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64EXT *params) +{ + struct gl_query_object *q = NULL; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glGetQueryObjectui64v(%u, %s)\n", id, + _mesa_lookup_enum_by_nr(pname)); + + if (id) + q = _mesa_lookup_query_object(ctx, id); + + if (!q || q->Active) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glGetQueryObjectuui64vARB(id=%d is invalid or active)", id); + return; + } + + switch (pname) { + case GL_QUERY_RESULT_ARB: + if (!q->Ready) + ctx->Driver.WaitQuery(ctx, q); + *params = q->Result; + break; + case GL_QUERY_RESULT_AVAILABLE_ARB: + if (!q->Ready) + ctx->Driver.CheckQuery( ctx, q ); + *params = q->Ready; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, "glGetQueryObjectui64vARB(pname)"); + return; + } +} + + +void +_mesa_init_queryobj_dispatch(struct _glapi_table *disp) +{ + SET_GenQueriesARB(disp, _mesa_GenQueriesARB); + SET_DeleteQueriesARB(disp, _mesa_DeleteQueriesARB); + SET_IsQueryARB(disp, _mesa_IsQueryARB); + SET_BeginQueryARB(disp, _mesa_BeginQueryARB); + SET_EndQueryARB(disp, _mesa_EndQueryARB); + SET_GetQueryivARB(disp, _mesa_GetQueryivARB); + SET_GetQueryObjectivARB(disp, _mesa_GetQueryObjectivARB); + SET_GetQueryObjectuivARB(disp, _mesa_GetQueryObjectuivARB); + + SET_GetQueryObjecti64vEXT(disp, _mesa_GetQueryObjecti64vEXT); + SET_GetQueryObjectui64vEXT(disp, _mesa_GetQueryObjectui64vEXT); +} + + +#endif /* FEATURE_queryobj */ + + +/** + * Allocate/init the context state related to query objects. + */ +void +_mesa_init_queryobj(struct gl_context *ctx) +{ + ctx->Query.QueryObjects = _mesa_NewHashTable(); + ctx->Query.CurrentOcclusionObject = NULL; +} + + +/** + * Callback for deleting a query object. Called by _mesa_HashDeleteAll(). + */ +static void +delete_queryobj_cb(GLuint id, void *data, void *userData) +{ + struct gl_query_object *q= (struct gl_query_object *) data; + struct gl_context *ctx = (struct gl_context *)userData; + ctx->Driver.DeleteQuery(ctx, q); +} + + +/** + * Free the context state related to query objects. + */ +void +_mesa_free_queryobj_data(struct gl_context *ctx) +{ + _mesa_HashDeleteAll(ctx->Query.QueryObjects, delete_queryobj_cb, ctx); + _mesa_DeleteHashTable(ctx->Query.QueryObjects); +} diff --git a/mesalib/src/mesa/main/shaderapi.c b/mesalib/src/mesa/main/shaderapi.c index f2b8aa449..11b0f884f 100644 --- a/mesalib/src/mesa/main/shaderapi.c +++ b/mesalib/src/mesa/main/shaderapi.c @@ -1184,6 +1184,8 @@ void GLAPIENTRY _mesa_CompileShaderARB(GLhandleARB shaderObj) { GET_CURRENT_CONTEXT(ctx); + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glCompileShader %u\n", shaderObj); compile_shader(ctx, shaderObj); } @@ -1192,6 +1194,8 @@ GLuint GLAPIENTRY _mesa_CreateShader(GLenum type) { GET_CURRENT_CONTEXT(ctx); + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glCreateShader %s\n", _mesa_lookup_enum_by_nr(type)); return create_shader(ctx, type); } @@ -1208,6 +1212,8 @@ GLuint GLAPIENTRY _mesa_CreateProgram(void) { GET_CURRENT_CONTEXT(ctx); + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glCreateProgram\n"); return create_shader_program(ctx); } @@ -1223,6 +1229,11 @@ _mesa_CreateProgramObjectARB(void) void GLAPIENTRY _mesa_DeleteObjectARB(GLhandleARB obj) { + if (MESA_VERBOSE & VERBOSE_API) { + GET_CURRENT_CONTEXT(ctx); + _mesa_debug(ctx, "glDeleteObjectARB(%u)\n", obj); + } + if (obj) { GET_CURRENT_CONTEXT(ctx); if (is_program(ctx, obj)) { diff --git a/mesalib/src/mesa/main/stencil.c b/mesalib/src/mesa/main/stencil.c index c63b14d27..d898bf1d7 100644 --- a/mesalib/src/mesa/main/stencil.c +++ b/mesalib/src/mesa/main/stencil.c @@ -1,590 +1,614 @@ -/* - * Mesa 3-D graphics library - * Version: 7.1 - * - * 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 - * BRIAN PAUL 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 stencil.c - * Stencil operations. - * - * Note: There's some conflict between GL_EXT_stencil_two_side and - * OpenGL 2.0's two-sided stencil feature. - * - * With GL_EXT_stencil_two_side, calling glStencilOp/Func/Mask() only the - * front OR back face state (as set by glActiveStencilFaceEXT) is set. - * - * But with OpenGL 2.0, calling glStencilOp/Func/Mask() sets BOTH the - * front AND back state. - * - * Also, note that GL_ATI_separate_stencil is different as well: - * glStencilFuncSeparateATI(GLenum frontfunc, GLenum backfunc, ...) vs. - * glStencilFuncSeparate(GLenum face, GLenum func, ...). - * - * This problem is solved by keeping three sets of stencil state: - * state[0] = GL_FRONT state. - * state[1] = OpenGL 2.0 / GL_ATI_separate_stencil GL_BACK state. - * state[2] = GL_EXT_stencil_two_side GL_BACK state. - */ - - -#include "glheader.h" -#include "imports.h" -#include "context.h" -#include "macros.h" -#include "stencil.h" -#include "mtypes.h" - - -static GLboolean -validate_stencil_op(struct gl_context *ctx, GLenum op) -{ - switch (op) { - case GL_KEEP: - case GL_ZERO: - case GL_REPLACE: - case GL_INCR: - case GL_DECR: - case GL_INVERT: - return GL_TRUE; - case GL_INCR_WRAP_EXT: - case GL_DECR_WRAP_EXT: - if (ctx->Extensions.EXT_stencil_wrap) { - return GL_TRUE; - } - /* FALL-THROUGH */ - default: - return GL_FALSE; - } -} - - -static GLboolean -validate_stencil_func(struct gl_context *ctx, GLenum func) -{ - switch (func) { - case GL_NEVER: - case GL_LESS: - case GL_LEQUAL: - case GL_GREATER: - case GL_GEQUAL: - case GL_EQUAL: - case GL_NOTEQUAL: - case GL_ALWAYS: - return GL_TRUE; - default: - return GL_FALSE; - } -} - - -/** - * Set the clear value for the stencil buffer. - * - * \param s clear value. - * - * \sa glClearStencil(). - * - * Updates gl_stencil_attrib::Clear. On change - * flushes the vertices and notifies the driver via - * the dd_function_table::ClearStencil callback. - */ -void GLAPIENTRY -_mesa_ClearStencil( GLint s ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (ctx->Stencil.Clear == (GLuint) s) - return; - - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.Clear = (GLuint) s; - - if (ctx->Driver.ClearStencil) { - ctx->Driver.ClearStencil( ctx, s ); - } -} - - -/** - * Set the function and reference value for stencil testing. - * - * \param frontfunc front test function. - * \param backfunc back test function. - * \param ref front and back reference value. - * \param mask front and back bitmask. - * - * \sa glStencilFunc(). - * - * Verifies the parameters and updates the respective values in - * __struct gl_contextRec::Stencil. On change flushes the vertices and notifies the - * driver via the dd_function_table::StencilFunc callback. - */ -void GLAPIENTRY -_mesa_StencilFuncSeparateATI( GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask ) -{ - GET_CURRENT_CONTEXT(ctx); - const GLint stencilMax = (1 << ctx->DrawBuffer->Visual.stencilBits) - 1; - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (!validate_stencil_func(ctx, frontfunc)) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glStencilFuncSeparateATI(frontfunc)"); - return; - } - if (!validate_stencil_func(ctx, backfunc)) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glStencilFuncSeparateATI(backfunc)"); - return; - } - - ref = CLAMP( ref, 0, stencilMax ); - - /* set both front and back state */ - if (ctx->Stencil.Function[0] == frontfunc && - ctx->Stencil.Function[1] == backfunc && - ctx->Stencil.ValueMask[0] == mask && - ctx->Stencil.ValueMask[1] == mask && - ctx->Stencil.Ref[0] == ref && - ctx->Stencil.Ref[1] == ref) - return; - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.Function[0] = frontfunc; - ctx->Stencil.Function[1] = backfunc; - ctx->Stencil.Ref[0] = ctx->Stencil.Ref[1] = ref; - ctx->Stencil.ValueMask[0] = ctx->Stencil.ValueMask[1] = mask; - if (ctx->Driver.StencilFuncSeparate) { - ctx->Driver.StencilFuncSeparate(ctx, GL_FRONT, - frontfunc, ref, mask); - ctx->Driver.StencilFuncSeparate(ctx, GL_BACK, - backfunc, ref, mask); - } -} - - -/** - * Set the function and reference value for stencil testing. - * - * \param func test function. - * \param ref reference value. - * \param mask bitmask. - * - * \sa glStencilFunc(). - * - * Verifies the parameters and updates the respective values in - * __struct gl_contextRec::Stencil. On change flushes the vertices and notifies the - * driver via the dd_function_table::StencilFunc callback. - */ -void GLAPIENTRY -_mesa_StencilFunc( GLenum func, GLint ref, GLuint mask ) -{ - GET_CURRENT_CONTEXT(ctx); - const GLint stencilMax = (1 << ctx->DrawBuffer->Visual.stencilBits) - 1; - const GLint face = ctx->Stencil.ActiveFace; - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (!validate_stencil_func(ctx, func)) { - _mesa_error(ctx, GL_INVALID_ENUM, "glStencilFunc(func)"); - return; - } - - ref = CLAMP( ref, 0, stencilMax ); - - if (face != 0) { - if (ctx->Stencil.Function[face] == func && - ctx->Stencil.ValueMask[face] == mask && - ctx->Stencil.Ref[face] == ref) - return; - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.Function[face] = func; - ctx->Stencil.Ref[face] = ref; - ctx->Stencil.ValueMask[face] = mask; - - /* Only propagate the change to the driver if EXT_stencil_two_side - * is enabled. - */ - if (ctx->Driver.StencilFuncSeparate && ctx->Stencil.TestTwoSide) { - ctx->Driver.StencilFuncSeparate(ctx, GL_BACK, func, ref, mask); - } - } - else { - /* set both front and back state */ - if (ctx->Stencil.Function[0] == func && - ctx->Stencil.Function[1] == func && - ctx->Stencil.ValueMask[0] == mask && - ctx->Stencil.ValueMask[1] == mask && - ctx->Stencil.Ref[0] == ref && - ctx->Stencil.Ref[1] == ref) - return; - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.Function[0] = ctx->Stencil.Function[1] = func; - ctx->Stencil.Ref[0] = ctx->Stencil.Ref[1] = ref; - ctx->Stencil.ValueMask[0] = ctx->Stencil.ValueMask[1] = mask; - if (ctx->Driver.StencilFuncSeparate) { - ctx->Driver.StencilFuncSeparate(ctx, - ((ctx->Stencil.TestTwoSide) - ? GL_FRONT : GL_FRONT_AND_BACK), - func, ref, mask); - } - } -} - - -/** - * Set the stencil writing mask. - * - * \param mask bit-mask to enable/disable writing of individual bits in the - * stencil planes. - * - * \sa glStencilMask(). - * - * Updates gl_stencil_attrib::WriteMask. On change flushes the vertices and - * notifies the driver via the dd_function_table::StencilMask callback. - */ -void GLAPIENTRY -_mesa_StencilMask( GLuint mask ) -{ - GET_CURRENT_CONTEXT(ctx); - const GLint face = ctx->Stencil.ActiveFace; - - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (face != 0) { - /* Only modify the EXT_stencil_two_side back-face state. - */ - if (ctx->Stencil.WriteMask[face] == mask) - return; - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.WriteMask[face] = mask; - - /* Only propagate the change to the driver if EXT_stencil_two_side - * is enabled. - */ - if (ctx->Driver.StencilMaskSeparate && ctx->Stencil.TestTwoSide) { - ctx->Driver.StencilMaskSeparate(ctx, GL_BACK, mask); - } - } - else { - /* set both front and back state */ - if (ctx->Stencil.WriteMask[0] == mask && - ctx->Stencil.WriteMask[1] == mask) - return; - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.WriteMask[0] = ctx->Stencil.WriteMask[1] = mask; - if (ctx->Driver.StencilMaskSeparate) { - ctx->Driver.StencilMaskSeparate(ctx, - ((ctx->Stencil.TestTwoSide) - ? GL_FRONT : GL_FRONT_AND_BACK), - mask); - } - } -} - - -/** - * Set the stencil test actions. - * - * \param fail action to take when stencil test fails. - * \param zfail action to take when stencil test passes, but depth test fails. - * \param zpass action to take when stencil test passes and the depth test - * passes (or depth testing is not enabled). - * - * \sa glStencilOp(). - * - * Verifies the parameters and updates the respective fields in - * __struct gl_contextRec::Stencil. On change flushes the vertices and notifies the - * driver via the dd_function_table::StencilOp callback. - */ -void GLAPIENTRY -_mesa_StencilOp(GLenum fail, GLenum zfail, GLenum zpass) -{ - GET_CURRENT_CONTEXT(ctx); - const GLint face = ctx->Stencil.ActiveFace; - - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (!validate_stencil_op(ctx, fail)) { - _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOp(sfail)"); - return; - } - if (!validate_stencil_op(ctx, zfail)) { - _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOp(zfail)"); - return; - } - if (!validate_stencil_op(ctx, zpass)) { - _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOp(zpass)"); - return; - } - - if (face != 0) { - /* only set active face state */ - if (ctx->Stencil.ZFailFunc[face] == zfail && - ctx->Stencil.ZPassFunc[face] == zpass && - ctx->Stencil.FailFunc[face] == fail) - return; - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.ZFailFunc[face] = zfail; - ctx->Stencil.ZPassFunc[face] = zpass; - ctx->Stencil.FailFunc[face] = fail; - - /* Only propagate the change to the driver if EXT_stencil_two_side - * is enabled. - */ - if (ctx->Driver.StencilOpSeparate && ctx->Stencil.TestTwoSide) { - ctx->Driver.StencilOpSeparate(ctx, GL_BACK, fail, zfail, zpass); - } - } - else { - /* set both front and back state */ - if (ctx->Stencil.ZFailFunc[0] == zfail && - ctx->Stencil.ZFailFunc[1] == zfail && - ctx->Stencil.ZPassFunc[0] == zpass && - ctx->Stencil.ZPassFunc[1] == zpass && - ctx->Stencil.FailFunc[0] == fail && - ctx->Stencil.FailFunc[1] == fail) - return; - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.ZFailFunc[0] = ctx->Stencil.ZFailFunc[1] = zfail; - ctx->Stencil.ZPassFunc[0] = ctx->Stencil.ZPassFunc[1] = zpass; - ctx->Stencil.FailFunc[0] = ctx->Stencil.FailFunc[1] = fail; - if (ctx->Driver.StencilOpSeparate) { - ctx->Driver.StencilOpSeparate(ctx, - ((ctx->Stencil.TestTwoSide) - ? GL_FRONT : GL_FRONT_AND_BACK), - fail, zfail, zpass); - } - } -} - - - -#if _HAVE_FULL_GL -/* GL_EXT_stencil_two_side */ -void GLAPIENTRY -_mesa_ActiveStencilFaceEXT(GLenum face) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (!ctx->Extensions.EXT_stencil_two_side) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glActiveStencilFaceEXT"); - return; - } - - if (face == GL_FRONT || face == GL_BACK) { - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.ActiveFace = (face == GL_FRONT) ? 0 : 2; - } - else { - _mesa_error(ctx, GL_INVALID_ENUM, "glActiveStencilFaceEXT(face)"); - } -} -#endif - - - -/** - * OpenGL 2.0 function. - * \todo Make StencilOp() call this function. And eventually remove the - * ctx->Driver.StencilOp function and use ctx->Driver.StencilOpSeparate - * instead. - */ -void GLAPIENTRY -_mesa_StencilOpSeparate(GLenum face, GLenum sfail, GLenum zfail, GLenum zpass) -{ - GLboolean set = GL_FALSE; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (!validate_stencil_op(ctx, sfail)) { - _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOpSeparate(sfail)"); - return; - } - if (!validate_stencil_op(ctx, zfail)) { - _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOpSeparate(zfail)"); - return; - } - if (!validate_stencil_op(ctx, zpass)) { - _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOpSeparate(zpass)"); - return; - } - if (face != GL_FRONT && face != GL_BACK && face != GL_FRONT_AND_BACK) { - _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOpSeparate(face)"); - return; - } - - if (face != GL_BACK) { - /* set front */ - if (ctx->Stencil.ZFailFunc[0] != zfail || - ctx->Stencil.ZPassFunc[0] != zpass || - ctx->Stencil.FailFunc[0] != sfail){ - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.ZFailFunc[0] = zfail; - ctx->Stencil.ZPassFunc[0] = zpass; - ctx->Stencil.FailFunc[0] = sfail; - set = GL_TRUE; - } - } - if (face != GL_FRONT) { - /* set back */ - if (ctx->Stencil.ZFailFunc[1] != zfail || - ctx->Stencil.ZPassFunc[1] != zpass || - ctx->Stencil.FailFunc[1] != sfail) { - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.ZFailFunc[1] = zfail; - ctx->Stencil.ZPassFunc[1] = zpass; - ctx->Stencil.FailFunc[1] = sfail; - set = GL_TRUE; - } - } - if (set && ctx->Driver.StencilOpSeparate) { - ctx->Driver.StencilOpSeparate(ctx, face, sfail, zfail, zpass); - } -} - - -/* OpenGL 2.0 */ -void GLAPIENTRY -_mesa_StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) -{ - GET_CURRENT_CONTEXT(ctx); - const GLint stencilMax = (1 << ctx->DrawBuffer->Visual.stencilBits) - 1; - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (face != GL_FRONT && face != GL_BACK && face != GL_FRONT_AND_BACK) { - _mesa_error(ctx, GL_INVALID_ENUM, "glStencilFuncSeparate(face)"); - return; - } - if (!validate_stencil_func(ctx, func)) { - _mesa_error(ctx, GL_INVALID_ENUM, "glStencilFuncSeparate(func)"); - return; - } - - ref = CLAMP(ref, 0, stencilMax); - - FLUSH_VERTICES(ctx, _NEW_STENCIL); - - if (face != GL_BACK) { - /* set front */ - ctx->Stencil.Function[0] = func; - ctx->Stencil.Ref[0] = ref; - ctx->Stencil.ValueMask[0] = mask; - } - if (face != GL_FRONT) { - /* set back */ - ctx->Stencil.Function[1] = func; - ctx->Stencil.Ref[1] = ref; - ctx->Stencil.ValueMask[1] = mask; - } - if (ctx->Driver.StencilFuncSeparate) { - ctx->Driver.StencilFuncSeparate(ctx, face, func, ref, mask); - } -} - - -/* OpenGL 2.0 */ -void GLAPIENTRY -_mesa_StencilMaskSeparate(GLenum face, GLuint mask) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (face != GL_FRONT && face != GL_BACK && face != GL_FRONT_AND_BACK) { - _mesa_error(ctx, GL_INVALID_ENUM, "glStencilaMaskSeparate(face)"); - return; - } - - FLUSH_VERTICES(ctx, _NEW_STENCIL); - - if (face != GL_BACK) { - ctx->Stencil.WriteMask[0] = mask; - } - if (face != GL_FRONT) { - ctx->Stencil.WriteMask[1] = mask; - } - if (ctx->Driver.StencilMaskSeparate) { - ctx->Driver.StencilMaskSeparate(ctx, face, mask); - } -} - - -/** - * Update derived stencil state. - */ -void -_mesa_update_stencil(struct gl_context *ctx) -{ - const GLint face = ctx->Stencil._BackFace; - - ctx->Stencil._Enabled = (ctx->Stencil.Enabled && - ctx->DrawBuffer->Visual.stencilBits > 0); - - ctx->Stencil._TestTwoSide = - ctx->Stencil._Enabled && - (ctx->Stencil.Function[0] != ctx->Stencil.Function[face] || - ctx->Stencil.FailFunc[0] != ctx->Stencil.FailFunc[face] || - ctx->Stencil.ZPassFunc[0] != ctx->Stencil.ZPassFunc[face] || - ctx->Stencil.ZFailFunc[0] != ctx->Stencil.ZFailFunc[face] || - ctx->Stencil.Ref[0] != ctx->Stencil.Ref[face] || - ctx->Stencil.ValueMask[0] != ctx->Stencil.ValueMask[face] || - ctx->Stencil.WriteMask[0] != ctx->Stencil.WriteMask[face]); -} - - -/** - * Initialize the context stipple state. - * - * \param ctx GL context. - * - * Initializes __struct gl_contextRec::Stencil attribute group. - */ -void -_mesa_init_stencil(struct gl_context *ctx) -{ - ctx->Stencil.Enabled = GL_FALSE; - ctx->Stencil.TestTwoSide = GL_FALSE; - ctx->Stencil.ActiveFace = 0; /* 0 = GL_FRONT, 2 = GL_BACK */ - ctx->Stencil.Function[0] = GL_ALWAYS; - ctx->Stencil.Function[1] = GL_ALWAYS; - ctx->Stencil.Function[2] = GL_ALWAYS; - ctx->Stencil.FailFunc[0] = GL_KEEP; - ctx->Stencil.FailFunc[1] = GL_KEEP; - ctx->Stencil.FailFunc[2] = GL_KEEP; - ctx->Stencil.ZPassFunc[0] = GL_KEEP; - ctx->Stencil.ZPassFunc[1] = GL_KEEP; - ctx->Stencil.ZPassFunc[2] = GL_KEEP; - ctx->Stencil.ZFailFunc[0] = GL_KEEP; - ctx->Stencil.ZFailFunc[1] = GL_KEEP; - ctx->Stencil.ZFailFunc[2] = GL_KEEP; - ctx->Stencil.Ref[0] = 0; - ctx->Stencil.Ref[1] = 0; - ctx->Stencil.Ref[2] = 0; - ctx->Stencil.ValueMask[0] = ~0U; - ctx->Stencil.ValueMask[1] = ~0U; - ctx->Stencil.ValueMask[2] = ~0U; - ctx->Stencil.WriteMask[0] = ~0U; - ctx->Stencil.WriteMask[1] = ~0U; - ctx->Stencil.WriteMask[2] = ~0U; - ctx->Stencil.Clear = 0; - ctx->Stencil._BackFace = 1; -} +/* + * Mesa 3-D graphics library + * Version: 7.1 + * + * 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 + * BRIAN PAUL 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 stencil.c + * Stencil operations. + * + * Note: There's some conflict between GL_EXT_stencil_two_side and + * OpenGL 2.0's two-sided stencil feature. + * + * With GL_EXT_stencil_two_side, calling glStencilOp/Func/Mask() only the + * front OR back face state (as set by glActiveStencilFaceEXT) is set. + * + * But with OpenGL 2.0, calling glStencilOp/Func/Mask() sets BOTH the + * front AND back state. + * + * Also, note that GL_ATI_separate_stencil is different as well: + * glStencilFuncSeparateATI(GLenum frontfunc, GLenum backfunc, ...) vs. + * glStencilFuncSeparate(GLenum face, GLenum func, ...). + * + * This problem is solved by keeping three sets of stencil state: + * state[0] = GL_FRONT state. + * state[1] = OpenGL 2.0 / GL_ATI_separate_stencil GL_BACK state. + * state[2] = GL_EXT_stencil_two_side GL_BACK state. + */ + + +#include "glheader.h" +#include "imports.h" +#include "context.h" +#include "macros.h" +#include "stencil.h" +#include "mtypes.h" + + +static GLboolean +validate_stencil_op(struct gl_context *ctx, GLenum op) +{ + switch (op) { + case GL_KEEP: + case GL_ZERO: + case GL_REPLACE: + case GL_INCR: + case GL_DECR: + case GL_INVERT: + return GL_TRUE; + case GL_INCR_WRAP_EXT: + case GL_DECR_WRAP_EXT: + if (ctx->Extensions.EXT_stencil_wrap) { + return GL_TRUE; + } + /* FALL-THROUGH */ + default: + return GL_FALSE; + } +} + + +static GLboolean +validate_stencil_func(struct gl_context *ctx, GLenum func) +{ + switch (func) { + case GL_NEVER: + case GL_LESS: + case GL_LEQUAL: + case GL_GREATER: + case GL_GEQUAL: + case GL_EQUAL: + case GL_NOTEQUAL: + case GL_ALWAYS: + return GL_TRUE; + default: + return GL_FALSE; + } +} + + +/** + * Set the clear value for the stencil buffer. + * + * \param s clear value. + * + * \sa glClearStencil(). + * + * Updates gl_stencil_attrib::Clear. On change + * flushes the vertices and notifies the driver via + * the dd_function_table::ClearStencil callback. + */ +void GLAPIENTRY +_mesa_ClearStencil( GLint s ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (ctx->Stencil.Clear == (GLuint) s) + return; + + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.Clear = (GLuint) s; + + if (ctx->Driver.ClearStencil) { + ctx->Driver.ClearStencil( ctx, s ); + } +} + + +/** + * Set the function and reference value for stencil testing. + * + * \param frontfunc front test function. + * \param backfunc back test function. + * \param ref front and back reference value. + * \param mask front and back bitmask. + * + * \sa glStencilFunc(). + * + * Verifies the parameters and updates the respective values in + * __struct gl_contextRec::Stencil. On change flushes the vertices and notifies the + * driver via the dd_function_table::StencilFunc callback. + */ +void GLAPIENTRY +_mesa_StencilFuncSeparateATI( GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask ) +{ + GET_CURRENT_CONTEXT(ctx); + const GLint stencilMax = (1 << ctx->DrawBuffer->Visual.stencilBits) - 1; + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glStencilFuncSeparateATI()\n"); + + if (!validate_stencil_func(ctx, frontfunc)) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glStencilFuncSeparateATI(frontfunc)"); + return; + } + if (!validate_stencil_func(ctx, backfunc)) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glStencilFuncSeparateATI(backfunc)"); + return; + } + + ref = CLAMP( ref, 0, stencilMax ); + + /* set both front and back state */ + if (ctx->Stencil.Function[0] == frontfunc && + ctx->Stencil.Function[1] == backfunc && + ctx->Stencil.ValueMask[0] == mask && + ctx->Stencil.ValueMask[1] == mask && + ctx->Stencil.Ref[0] == ref && + ctx->Stencil.Ref[1] == ref) + return; + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.Function[0] = frontfunc; + ctx->Stencil.Function[1] = backfunc; + ctx->Stencil.Ref[0] = ctx->Stencil.Ref[1] = ref; + ctx->Stencil.ValueMask[0] = ctx->Stencil.ValueMask[1] = mask; + if (ctx->Driver.StencilFuncSeparate) { + ctx->Driver.StencilFuncSeparate(ctx, GL_FRONT, + frontfunc, ref, mask); + ctx->Driver.StencilFuncSeparate(ctx, GL_BACK, + backfunc, ref, mask); + } +} + + +/** + * Set the function and reference value for stencil testing. + * + * \param func test function. + * \param ref reference value. + * \param mask bitmask. + * + * \sa glStencilFunc(). + * + * Verifies the parameters and updates the respective values in + * __struct gl_contextRec::Stencil. On change flushes the vertices and notifies the + * driver via the dd_function_table::StencilFunc callback. + */ +void GLAPIENTRY +_mesa_StencilFunc( GLenum func, GLint ref, GLuint mask ) +{ + GET_CURRENT_CONTEXT(ctx); + const GLint stencilMax = (1 << ctx->DrawBuffer->Visual.stencilBits) - 1; + const GLint face = ctx->Stencil.ActiveFace; + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glStencilFunc()\n"); + + if (!validate_stencil_func(ctx, func)) { + _mesa_error(ctx, GL_INVALID_ENUM, "glStencilFunc(func)"); + return; + } + + ref = CLAMP( ref, 0, stencilMax ); + + if (face != 0) { + if (ctx->Stencil.Function[face] == func && + ctx->Stencil.ValueMask[face] == mask && + ctx->Stencil.Ref[face] == ref) + return; + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.Function[face] = func; + ctx->Stencil.Ref[face] = ref; + ctx->Stencil.ValueMask[face] = mask; + + /* Only propagate the change to the driver if EXT_stencil_two_side + * is enabled. + */ + if (ctx->Driver.StencilFuncSeparate && ctx->Stencil.TestTwoSide) { + ctx->Driver.StencilFuncSeparate(ctx, GL_BACK, func, ref, mask); + } + } + else { + /* set both front and back state */ + if (ctx->Stencil.Function[0] == func && + ctx->Stencil.Function[1] == func && + ctx->Stencil.ValueMask[0] == mask && + ctx->Stencil.ValueMask[1] == mask && + ctx->Stencil.Ref[0] == ref && + ctx->Stencil.Ref[1] == ref) + return; + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.Function[0] = ctx->Stencil.Function[1] = func; + ctx->Stencil.Ref[0] = ctx->Stencil.Ref[1] = ref; + ctx->Stencil.ValueMask[0] = ctx->Stencil.ValueMask[1] = mask; + if (ctx->Driver.StencilFuncSeparate) { + ctx->Driver.StencilFuncSeparate(ctx, + ((ctx->Stencil.TestTwoSide) + ? GL_FRONT : GL_FRONT_AND_BACK), + func, ref, mask); + } + } +} + + +/** + * Set the stencil writing mask. + * + * \param mask bit-mask to enable/disable writing of individual bits in the + * stencil planes. + * + * \sa glStencilMask(). + * + * Updates gl_stencil_attrib::WriteMask. On change flushes the vertices and + * notifies the driver via the dd_function_table::StencilMask callback. + */ +void GLAPIENTRY +_mesa_StencilMask( GLuint mask ) +{ + GET_CURRENT_CONTEXT(ctx); + const GLint face = ctx->Stencil.ActiveFace; + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glStencilMask()\n"); + + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (face != 0) { + /* Only modify the EXT_stencil_two_side back-face state. + */ + if (ctx->Stencil.WriteMask[face] == mask) + return; + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.WriteMask[face] = mask; + + /* Only propagate the change to the driver if EXT_stencil_two_side + * is enabled. + */ + if (ctx->Driver.StencilMaskSeparate && ctx->Stencil.TestTwoSide) { + ctx->Driver.StencilMaskSeparate(ctx, GL_BACK, mask); + } + } + else { + /* set both front and back state */ + if (ctx->Stencil.WriteMask[0] == mask && + ctx->Stencil.WriteMask[1] == mask) + return; + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.WriteMask[0] = ctx->Stencil.WriteMask[1] = mask; + if (ctx->Driver.StencilMaskSeparate) { + ctx->Driver.StencilMaskSeparate(ctx, + ((ctx->Stencil.TestTwoSide) + ? GL_FRONT : GL_FRONT_AND_BACK), + mask); + } + } +} + + +/** + * Set the stencil test actions. + * + * \param fail action to take when stencil test fails. + * \param zfail action to take when stencil test passes, but depth test fails. + * \param zpass action to take when stencil test passes and the depth test + * passes (or depth testing is not enabled). + * + * \sa glStencilOp(). + * + * Verifies the parameters and updates the respective fields in + * __struct gl_contextRec::Stencil. On change flushes the vertices and notifies the + * driver via the dd_function_table::StencilOp callback. + */ +void GLAPIENTRY +_mesa_StencilOp(GLenum fail, GLenum zfail, GLenum zpass) +{ + GET_CURRENT_CONTEXT(ctx); + const GLint face = ctx->Stencil.ActiveFace; + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glStencilOp()\n"); + + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (!validate_stencil_op(ctx, fail)) { + _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOp(sfail)"); + return; + } + if (!validate_stencil_op(ctx, zfail)) { + _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOp(zfail)"); + return; + } + if (!validate_stencil_op(ctx, zpass)) { + _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOp(zpass)"); + return; + } + + if (face != 0) { + /* only set active face state */ + if (ctx->Stencil.ZFailFunc[face] == zfail && + ctx->Stencil.ZPassFunc[face] == zpass && + ctx->Stencil.FailFunc[face] == fail) + return; + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.ZFailFunc[face] = zfail; + ctx->Stencil.ZPassFunc[face] = zpass; + ctx->Stencil.FailFunc[face] = fail; + + /* Only propagate the change to the driver if EXT_stencil_two_side + * is enabled. + */ + if (ctx->Driver.StencilOpSeparate && ctx->Stencil.TestTwoSide) { + ctx->Driver.StencilOpSeparate(ctx, GL_BACK, fail, zfail, zpass); + } + } + else { + /* set both front and back state */ + if (ctx->Stencil.ZFailFunc[0] == zfail && + ctx->Stencil.ZFailFunc[1] == zfail && + ctx->Stencil.ZPassFunc[0] == zpass && + ctx->Stencil.ZPassFunc[1] == zpass && + ctx->Stencil.FailFunc[0] == fail && + ctx->Stencil.FailFunc[1] == fail) + return; + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.ZFailFunc[0] = ctx->Stencil.ZFailFunc[1] = zfail; + ctx->Stencil.ZPassFunc[0] = ctx->Stencil.ZPassFunc[1] = zpass; + ctx->Stencil.FailFunc[0] = ctx->Stencil.FailFunc[1] = fail; + if (ctx->Driver.StencilOpSeparate) { + ctx->Driver.StencilOpSeparate(ctx, + ((ctx->Stencil.TestTwoSide) + ? GL_FRONT : GL_FRONT_AND_BACK), + fail, zfail, zpass); + } + } +} + + + +#if _HAVE_FULL_GL +/* GL_EXT_stencil_two_side */ +void GLAPIENTRY +_mesa_ActiveStencilFaceEXT(GLenum face) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glActiveStencilFaceEXT()\n"); + + if (!ctx->Extensions.EXT_stencil_two_side) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glActiveStencilFaceEXT"); + return; + } + + if (face == GL_FRONT || face == GL_BACK) { + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.ActiveFace = (face == GL_FRONT) ? 0 : 2; + } + else { + _mesa_error(ctx, GL_INVALID_ENUM, "glActiveStencilFaceEXT(face)"); + } +} +#endif + + + +/** + * OpenGL 2.0 function. + * \todo Make StencilOp() call this function. And eventually remove the + * ctx->Driver.StencilOp function and use ctx->Driver.StencilOpSeparate + * instead. + */ +void GLAPIENTRY +_mesa_StencilOpSeparate(GLenum face, GLenum sfail, GLenum zfail, GLenum zpass) +{ + GLboolean set = GL_FALSE; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glStencilOpSeparate()\n"); + + if (!validate_stencil_op(ctx, sfail)) { + _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOpSeparate(sfail)"); + return; + } + if (!validate_stencil_op(ctx, zfail)) { + _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOpSeparate(zfail)"); + return; + } + if (!validate_stencil_op(ctx, zpass)) { + _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOpSeparate(zpass)"); + return; + } + if (face != GL_FRONT && face != GL_BACK && face != GL_FRONT_AND_BACK) { + _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOpSeparate(face)"); + return; + } + + if (face != GL_BACK) { + /* set front */ + if (ctx->Stencil.ZFailFunc[0] != zfail || + ctx->Stencil.ZPassFunc[0] != zpass || + ctx->Stencil.FailFunc[0] != sfail){ + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.ZFailFunc[0] = zfail; + ctx->Stencil.ZPassFunc[0] = zpass; + ctx->Stencil.FailFunc[0] = sfail; + set = GL_TRUE; + } + } + if (face != GL_FRONT) { + /* set back */ + if (ctx->Stencil.ZFailFunc[1] != zfail || + ctx->Stencil.ZPassFunc[1] != zpass || + ctx->Stencil.FailFunc[1] != sfail) { + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.ZFailFunc[1] = zfail; + ctx->Stencil.ZPassFunc[1] = zpass; + ctx->Stencil.FailFunc[1] = sfail; + set = GL_TRUE; + } + } + if (set && ctx->Driver.StencilOpSeparate) { + ctx->Driver.StencilOpSeparate(ctx, face, sfail, zfail, zpass); + } +} + + +/* OpenGL 2.0 */ +void GLAPIENTRY +_mesa_StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) +{ + GET_CURRENT_CONTEXT(ctx); + const GLint stencilMax = (1 << ctx->DrawBuffer->Visual.stencilBits) - 1; + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glStencilFuncSeparate()\n"); + + if (face != GL_FRONT && face != GL_BACK && face != GL_FRONT_AND_BACK) { + _mesa_error(ctx, GL_INVALID_ENUM, "glStencilFuncSeparate(face)"); + return; + } + if (!validate_stencil_func(ctx, func)) { + _mesa_error(ctx, GL_INVALID_ENUM, "glStencilFuncSeparate(func)"); + return; + } + + ref = CLAMP(ref, 0, stencilMax); + + FLUSH_VERTICES(ctx, _NEW_STENCIL); + + if (face != GL_BACK) { + /* set front */ + ctx->Stencil.Function[0] = func; + ctx->Stencil.Ref[0] = ref; + ctx->Stencil.ValueMask[0] = mask; + } + if (face != GL_FRONT) { + /* set back */ + ctx->Stencil.Function[1] = func; + ctx->Stencil.Ref[1] = ref; + ctx->Stencil.ValueMask[1] = mask; + } + if (ctx->Driver.StencilFuncSeparate) { + ctx->Driver.StencilFuncSeparate(ctx, face, func, ref, mask); + } +} + + +/* OpenGL 2.0 */ +void GLAPIENTRY +_mesa_StencilMaskSeparate(GLenum face, GLuint mask) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glStencilMaskSeparate()\n"); + + if (face != GL_FRONT && face != GL_BACK && face != GL_FRONT_AND_BACK) { + _mesa_error(ctx, GL_INVALID_ENUM, "glStencilaMaskSeparate(face)"); + return; + } + + FLUSH_VERTICES(ctx, _NEW_STENCIL); + + if (face != GL_BACK) { + ctx->Stencil.WriteMask[0] = mask; + } + if (face != GL_FRONT) { + ctx->Stencil.WriteMask[1] = mask; + } + if (ctx->Driver.StencilMaskSeparate) { + ctx->Driver.StencilMaskSeparate(ctx, face, mask); + } +} + + +/** + * Update derived stencil state. + */ +void +_mesa_update_stencil(struct gl_context *ctx) +{ + const GLint face = ctx->Stencil._BackFace; + + ctx->Stencil._Enabled = (ctx->Stencil.Enabled && + ctx->DrawBuffer->Visual.stencilBits > 0); + + ctx->Stencil._TestTwoSide = + ctx->Stencil._Enabled && + (ctx->Stencil.Function[0] != ctx->Stencil.Function[face] || + ctx->Stencil.FailFunc[0] != ctx->Stencil.FailFunc[face] || + ctx->Stencil.ZPassFunc[0] != ctx->Stencil.ZPassFunc[face] || + ctx->Stencil.ZFailFunc[0] != ctx->Stencil.ZFailFunc[face] || + ctx->Stencil.Ref[0] != ctx->Stencil.Ref[face] || + ctx->Stencil.ValueMask[0] != ctx->Stencil.ValueMask[face] || + ctx->Stencil.WriteMask[0] != ctx->Stencil.WriteMask[face]); +} + + +/** + * Initialize the context stipple state. + * + * \param ctx GL context. + * + * Initializes __struct gl_contextRec::Stencil attribute group. + */ +void +_mesa_init_stencil(struct gl_context *ctx) +{ + ctx->Stencil.Enabled = GL_FALSE; + ctx->Stencil.TestTwoSide = GL_FALSE; + ctx->Stencil.ActiveFace = 0; /* 0 = GL_FRONT, 2 = GL_BACK */ + ctx->Stencil.Function[0] = GL_ALWAYS; + ctx->Stencil.Function[1] = GL_ALWAYS; + ctx->Stencil.Function[2] = GL_ALWAYS; + ctx->Stencil.FailFunc[0] = GL_KEEP; + ctx->Stencil.FailFunc[1] = GL_KEEP; + ctx->Stencil.FailFunc[2] = GL_KEEP; + ctx->Stencil.ZPassFunc[0] = GL_KEEP; + ctx->Stencil.ZPassFunc[1] = GL_KEEP; + ctx->Stencil.ZPassFunc[2] = GL_KEEP; + ctx->Stencil.ZFailFunc[0] = GL_KEEP; + ctx->Stencil.ZFailFunc[1] = GL_KEEP; + ctx->Stencil.ZFailFunc[2] = GL_KEEP; + ctx->Stencil.Ref[0] = 0; + ctx->Stencil.Ref[1] = 0; + ctx->Stencil.Ref[2] = 0; + ctx->Stencil.ValueMask[0] = ~0U; + ctx->Stencil.ValueMask[1] = ~0U; + ctx->Stencil.ValueMask[2] = ~0U; + ctx->Stencil.WriteMask[0] = ~0U; + ctx->Stencil.WriteMask[1] = ~0U; + ctx->Stencil.WriteMask[2] = ~0U; + ctx->Stencil.Clear = 0; + ctx->Stencil._BackFace = 1; +} -- cgit v1.2.3