From 53bf2508fe3d5bd7889ccf4817fcd2bcc531d6ae Mon Sep 17 00:00:00 2001 From: marha Date: Mon, 7 Feb 2011 07:14:23 +0000 Subject: mesa git update 7 Feb 2011 --- mesalib/configure.ac | 2 +- mesalib/src/glsl/Makefile | 1 + mesalib/src/glsl/SConscript | 1 + mesalib/src/glsl/glsl_parser_extras.cpp | 1 + mesalib/src/glsl/ir_optimization.h | 145 +- mesalib/src/glsl/linker.cpp | 14 + mesalib/src/glsl/opt_constant_propagation.cpp | 7 +- mesalib/src/glsl/opt_constant_variable.cpp | 7 +- mesalib/src/glsl/opt_copy_propagation.cpp | 9 +- mesalib/src/glsl/opt_copy_propagation_elements.cpp | 458 ++ mesalib/src/glsl/opt_dead_code_local.cpp | 9 +- mesalib/src/mesa/main/enable.c | 3007 ++++++------ mesalib/src/mesa/main/fbobject.c | 4974 ++++++++++---------- mesalib/src/mesa/main/framebuffer.c | 2170 ++++----- mesalib/src/mesa/main/mtypes.h | 3 + mesalib/src/mesa/main/varray.c | 2414 +++++----- 16 files changed, 6842 insertions(+), 6380 deletions(-) create mode 100644 mesalib/src/glsl/opt_copy_propagation_elements.cpp (limited to 'mesalib') diff --git a/mesalib/configure.ac b/mesalib/configure.ac index 46938f4b4..9c6ce3f21 100644 --- a/mesalib/configure.ac +++ b/mesalib/configure.ac @@ -818,7 +818,7 @@ AC_SUBST([GLAPI_LIB_DEPS]) AC_SUBST([HAVE_XF86VIDMODE]) PKG_CHECK_MODULES([LIBDRM_RADEON], - [libdrm_radeon libdrm >= $LIBDRM_RADEON_REQUIRED], + [libdrm_radeon >= $LIBDRM_RADEON_REQUIRED], HAVE_LIBDRM_RADEON=yes, HAVE_LIBDRM_RADEON=no) diff --git a/mesalib/src/glsl/Makefile b/mesalib/src/glsl/Makefile index 4f307422c..ec11c8a7b 100644 --- a/mesalib/src/glsl/Makefile +++ b/mesalib/src/glsl/Makefile @@ -69,6 +69,7 @@ CXX_SOURCES = \ opt_constant_propagation.cpp \ opt_constant_variable.cpp \ opt_copy_propagation.cpp \ + opt_copy_propagation_elements.cpp \ opt_dead_code.cpp \ opt_dead_code_local.cpp \ opt_dead_functions.cpp \ diff --git a/mesalib/src/glsl/SConscript b/mesalib/src/glsl/SConscript index 2cc7b0eda..f55d7de74 100644 --- a/mesalib/src/glsl/SConscript +++ b/mesalib/src/glsl/SConscript @@ -64,6 +64,7 @@ sources = [ 'opt_constant_propagation.cpp', 'opt_constant_variable.cpp', 'opt_copy_propagation.cpp', + 'opt_copy_propagation_elements.cpp', 'opt_dead_code.cpp', 'opt_dead_code_local.cpp', 'opt_dead_functions.cpp', diff --git a/mesalib/src/glsl/glsl_parser_extras.cpp b/mesalib/src/glsl/glsl_parser_extras.cpp index 1fa209c03..c2bb59b9c 100644 --- a/mesalib/src/glsl/glsl_parser_extras.cpp +++ b/mesalib/src/glsl/glsl_parser_extras.cpp @@ -764,6 +764,7 @@ do_common_optimization(exec_list *ir, bool linked, unsigned max_unroll_iteration progress = do_if_simplification(ir) || progress; progress = do_discard_simplification(ir) || progress; progress = do_copy_propagation(ir) || progress; + progress = do_copy_propagation_elements(ir) || progress; if (linked) progress = do_dead_code(ir) || progress; else diff --git a/mesalib/src/glsl/ir_optimization.h b/mesalib/src/glsl/ir_optimization.h index 609092e76..dd265673c 100644 --- a/mesalib/src/glsl/ir_optimization.h +++ b/mesalib/src/glsl/ir_optimization.h @@ -1,72 +1,73 @@ -/* - * Copyright © 2010 Intel Corporation - * - * 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 (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - - -/** - * \file ir_optimization.h - * - * Prototypes for optimization passes to be called by the compiler and drivers. - */ - -/* Operations for lower_instructions() */ -#define SUB_TO_ADD_NEG 0x01 -#define DIV_TO_MUL_RCP 0x02 -#define EXP_TO_EXP2 0x04 -#define POW_TO_EXP2 0x08 -#define LOG_TO_LOG2 0x10 -#define MOD_TO_FRACT 0x20 - -bool do_common_optimization(exec_list *ir, bool linked, unsigned max_unroll_iterations); - -bool do_algebraic(exec_list *instructions); -bool do_constant_folding(exec_list *instructions); -bool do_constant_variable(exec_list *instructions); -bool do_constant_variable_unlinked(exec_list *instructions); -bool do_copy_propagation(exec_list *instructions); -bool do_constant_propagation(exec_list *instructions); -bool do_dead_code(exec_list *instructions); -bool do_dead_code_local(exec_list *instructions); -bool do_dead_code_unlinked(exec_list *instructions); -bool do_dead_functions(exec_list *instructions); -bool do_function_inlining(exec_list *instructions); -bool do_lower_jumps(exec_list *instructions, bool pull_out_jumps = true, bool lower_sub_return = true, bool lower_main_return = false, bool lower_continue = false, bool lower_break = false); -bool do_lower_texture_projection(exec_list *instructions); -bool do_if_simplification(exec_list *instructions); -bool do_discard_simplification(exec_list *instructions); -bool lower_if_to_cond_assign(exec_list *instructions, unsigned max_depth = 0); -bool do_mat_op_to_vec(exec_list *instructions); -bool do_mod_to_fract(exec_list *instructions); -bool do_noop_swizzle(exec_list *instructions); -bool do_structure_splitting(exec_list *instructions); -bool do_sub_to_add_neg(exec_list *instructions); -bool do_swizzle_swizzle(exec_list *instructions); -bool do_tree_grafting(exec_list *instructions); -bool do_vec_index_to_cond_assign(exec_list *instructions); -bool do_vec_index_to_swizzle(exec_list *instructions); -bool lower_discard(exec_list *instructions); -bool lower_instructions(exec_list *instructions, unsigned what_to_lower); -bool lower_noise(exec_list *instructions); -bool lower_variable_index_to_cond_assign(exec_list *instructions, - bool lower_input, bool lower_output, bool lower_temp, bool lower_uniform); -bool lower_quadop_vector(exec_list *instructions, bool dont_lower_swz); -bool optimize_redundant_jumps(exec_list *instructions); +/* + * Copyright © 2010 Intel Corporation + * + * 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 (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + + +/** + * \file ir_optimization.h + * + * Prototypes for optimization passes to be called by the compiler and drivers. + */ + +/* Operations for lower_instructions() */ +#define SUB_TO_ADD_NEG 0x01 +#define DIV_TO_MUL_RCP 0x02 +#define EXP_TO_EXP2 0x04 +#define POW_TO_EXP2 0x08 +#define LOG_TO_LOG2 0x10 +#define MOD_TO_FRACT 0x20 + +bool do_common_optimization(exec_list *ir, bool linked, unsigned max_unroll_iterations); + +bool do_algebraic(exec_list *instructions); +bool do_constant_folding(exec_list *instructions); +bool do_constant_variable(exec_list *instructions); +bool do_constant_variable_unlinked(exec_list *instructions); +bool do_copy_propagation(exec_list *instructions); +bool do_copy_propagation_elements(exec_list *instructions); +bool do_constant_propagation(exec_list *instructions); +bool do_dead_code(exec_list *instructions); +bool do_dead_code_local(exec_list *instructions); +bool do_dead_code_unlinked(exec_list *instructions); +bool do_dead_functions(exec_list *instructions); +bool do_function_inlining(exec_list *instructions); +bool do_lower_jumps(exec_list *instructions, bool pull_out_jumps = true, bool lower_sub_return = true, bool lower_main_return = false, bool lower_continue = false, bool lower_break = false); +bool do_lower_texture_projection(exec_list *instructions); +bool do_if_simplification(exec_list *instructions); +bool do_discard_simplification(exec_list *instructions); +bool lower_if_to_cond_assign(exec_list *instructions, unsigned max_depth = 0); +bool do_mat_op_to_vec(exec_list *instructions); +bool do_mod_to_fract(exec_list *instructions); +bool do_noop_swizzle(exec_list *instructions); +bool do_structure_splitting(exec_list *instructions); +bool do_sub_to_add_neg(exec_list *instructions); +bool do_swizzle_swizzle(exec_list *instructions); +bool do_tree_grafting(exec_list *instructions); +bool do_vec_index_to_cond_assign(exec_list *instructions); +bool do_vec_index_to_swizzle(exec_list *instructions); +bool lower_discard(exec_list *instructions); +bool lower_instructions(exec_list *instructions, unsigned what_to_lower); +bool lower_noise(exec_list *instructions); +bool lower_variable_index_to_cond_assign(exec_list *instructions, + bool lower_input, bool lower_output, bool lower_temp, bool lower_uniform); +bool lower_quadop_vector(exec_list *instructions, bool dont_lower_swz); +bool optimize_redundant_jumps(exec_list *instructions); diff --git a/mesalib/src/glsl/linker.cpp b/mesalib/src/glsl/linker.cpp index c7fb62437..46cd1950c 100644 --- a/mesalib/src/glsl/linker.cpp +++ b/mesalib/src/glsl/linker.cpp @@ -1682,6 +1682,20 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) demote_shader_inputs_and_outputs(sh, ir_var_in); } + /* OpenGL ES requires that a vertex shader and a fragment shader both be + * present in a linked program. By checking for use of shading language + * version 1.00, we also catch the GL_ARB_ES2_compatibility case. + */ + if (ctx->API == API_OPENGLES2 || prog->Version == 100) { + if (prog->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) { + linker_error_printf(prog, "program lacks a vertex shader\n"); + prog->LinkStatus = false; + } else if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL) { + linker_error_printf(prog, "program lacks a fragment shader\n"); + prog->LinkStatus = false; + } + } + /* FINISHME: Assign fragment shader output locations. */ done: diff --git a/mesalib/src/glsl/opt_constant_propagation.cpp b/mesalib/src/glsl/opt_constant_propagation.cpp index 1fff71753..814f6e61f 100644 --- a/mesalib/src/glsl/opt_constant_propagation.cpp +++ b/mesalib/src/glsl/opt_constant_propagation.cpp @@ -400,11 +400,8 @@ ir_constant_propagation_visitor::add_constant(ir_assignment *ir) { acp_entry *entry; - if (ir->condition) { - ir_constant *condition = ir->condition->as_constant(); - if (!condition || !condition->value.b[0]) - return; - } + if (ir->condition) + return; if (!ir->write_mask) return; diff --git a/mesalib/src/glsl/opt_constant_variable.cpp b/mesalib/src/glsl/opt_constant_variable.cpp index 1f51da6e4..9dca0ff8e 100644 --- a/mesalib/src/glsl/opt_constant_variable.cpp +++ b/mesalib/src/glsl/opt_constant_variable.cpp @@ -107,11 +107,8 @@ ir_constant_variable_visitor::visit_enter(ir_assignment *ir) /* OK, now find if we actually have all the right conditions for * this to be a constant value assigned to the var. */ - if (ir->condition) { - constval = ir->condition->constant_expression_value(); - if (!constval || !constval->value.b[0]) - return visit_continue; - } + if (ir->condition) + return visit_continue; ir_variable *var = ir->whole_variable_written(); if (!var) diff --git a/mesalib/src/glsl/opt_copy_propagation.cpp b/mesalib/src/glsl/opt_copy_propagation.cpp index 4ab23bf9b..efa3afda3 100644 --- a/mesalib/src/glsl/opt_copy_propagation.cpp +++ b/mesalib/src/glsl/opt_copy_propagation.cpp @@ -191,7 +191,7 @@ ir_copy_propagation_visitor::visit_enter(ir_call *ir) sig_param_iter.next(); } - /* Since we're unlinked, we don't (necssarily) know the side effects of + /* Since we're unlinked, we don't (necessarily) know the side effects of * this call. So kill all copies. */ acp->make_empty(); @@ -309,11 +309,8 @@ ir_copy_propagation_visitor::add_copy(ir_assignment *ir) { acp_entry *entry; - if (ir->condition) { - ir_constant *condition = ir->condition->as_constant(); - if (!condition || !condition->value.b[0]) - return; - } + if (ir->condition) + return; ir_variable *lhs_var = ir->whole_variable_written(); ir_variable *rhs_var = ir->rhs->whole_variable_referenced(); diff --git a/mesalib/src/glsl/opt_copy_propagation_elements.cpp b/mesalib/src/glsl/opt_copy_propagation_elements.cpp new file mode 100644 index 000000000..8541d9a8e --- /dev/null +++ b/mesalib/src/glsl/opt_copy_propagation_elements.cpp @@ -0,0 +1,458 @@ +/* + * Copyright © 2010 Intel Corporation + * + * 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 (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * \file opt_copy_propagation_elements.cpp + * + * Replaces usage of recently-copied components of variables with the + * previous copy of the variable. + * + * This pass can be compared with opt_copy_propagation, which operands + * on arbitrary whole-variable copies. However, in order to handle + * the copy propagation of swizzled variables or writemasked writes, + * we want to track things on a channel-wise basis. I found that + * trying to mix the swizzled/writemasked support here with the + * whole-variable stuff in opt_copy_propagation.cpp just made a mess, + * so this is separate despite the ACP handling being somewhat + * similar. + * + * This should reduce the number of MOV instructions in the generated + * programs unless copy propagation is also done on the LIR, and may + * help anyway by triggering other optimizations that live in the HIR. + */ + +#include "ir.h" +#include "ir_rvalue_visitor.h" +#include "ir_basic_block.h" +#include "ir_optimization.h" +#include "glsl_types.h" + +static bool debug = false; + +class acp_entry : public exec_node +{ +public: + acp_entry(ir_variable *lhs, ir_variable *rhs, int write_mask, int swizzle[4]) + { + this->lhs = lhs; + this->rhs = rhs; + this->write_mask = write_mask; + memcpy(this->swizzle, swizzle, sizeof(this->swizzle)); + } + + acp_entry(acp_entry *a) + { + this->lhs = a->lhs; + this->rhs = a->rhs; + this->write_mask = a->write_mask; + memcpy(this->swizzle, a->swizzle, sizeof(this->swizzle)); + } + + ir_variable *lhs; + ir_variable *rhs; + unsigned int write_mask; + int swizzle[4]; +}; + + +class kill_entry : public exec_node +{ +public: + kill_entry(ir_variable *var, int write_mask) + { + this->var = var; + this->write_mask = write_mask; + } + + ir_variable *var; + unsigned int write_mask; +}; + +class ir_copy_propagation_elements_visitor : public ir_rvalue_visitor { +public: + ir_copy_propagation_elements_visitor() + { + this->progress = false; + this->mem_ctx = ralloc_context(NULL); + this->shader_mem_ctx = NULL; + this->acp = new(mem_ctx) exec_list; + this->kills = new(mem_ctx) exec_list; + } + ~ir_copy_propagation_elements_visitor() + { + ralloc_free(mem_ctx); + } + + virtual ir_visitor_status visit_enter(class ir_loop *); + virtual ir_visitor_status visit_enter(class ir_function_signature *); + virtual ir_visitor_status visit_leave(class ir_assignment *); + virtual ir_visitor_status visit_enter(class ir_call *); + virtual ir_visitor_status visit_enter(class ir_if *); + + void handle_rvalue(ir_rvalue **rvalue); + + void add_copy(ir_assignment *ir); + void kill(kill_entry *k); + void handle_if_block(exec_list *instructions); + + /** List of acp_entry: The available copies to propagate */ + exec_list *acp; + /** + * List of kill_entry: The variables whose values were killed in this + * block. + */ + exec_list *kills; + + bool progress; + + bool killed_all; + + /* Context for our local data structures. */ + void *mem_ctx; + /* Context for allocating new shader nodes. */ + void *shader_mem_ctx; +}; + +ir_visitor_status +ir_copy_propagation_elements_visitor::visit_enter(ir_function_signature *ir) +{ + /* Treat entry into a function signature as a completely separate + * block. Any instructions at global scope will be shuffled into + * main() at link time, so they're irrelevant to us. + */ + exec_list *orig_acp = this->acp; + exec_list *orig_kills = this->kills; + bool orig_killed_all = this->killed_all; + + this->acp = new(mem_ctx) exec_list; + this->kills = new(mem_ctx) exec_list; + this->killed_all = false; + + visit_list_elements(this, &ir->body); + + this->kills = orig_kills; + this->acp = orig_acp; + this->killed_all = orig_killed_all; + + return visit_continue_with_parent; +} + +ir_visitor_status +ir_copy_propagation_elements_visitor::visit_leave(ir_assignment *ir) +{ + ir_dereference_variable *lhs = ir->lhs->as_dereference_variable(); + + if (lhs && (lhs->type->is_scalar() || lhs->type->is_vector())) { + kill_entry *k = new(mem_ctx) kill_entry(lhs->var, ir->write_mask); + kill(k); + } + + add_copy(ir); + + return visit_continue; +} + +/** + * Replaces dereferences of ACP RHS variables with ACP LHS variables. + * + * This is where the actual copy propagation occurs. Note that the + * rewriting of ir_dereference means that the ir_dereference instance + * must not be shared by multiple IR operations! + */ +void +ir_copy_propagation_elements_visitor::handle_rvalue(ir_rvalue **ir) +{ + int swizzle_chan[4]; + ir_dereference_variable *deref_var; + ir_variable *source[4] = {NULL, NULL, NULL, NULL}; + int source_chan[4]; + int chans; + + if (!*ir) + return; + + ir_swizzle *swizzle = (*ir)->as_swizzle(); + if (swizzle) { + deref_var = swizzle->val->as_dereference_variable(); + if (!deref_var) + return; + + swizzle_chan[0] = swizzle->mask.x; + swizzle_chan[1] = swizzle->mask.y; + swizzle_chan[2] = swizzle->mask.z; + swizzle_chan[3] = swizzle->mask.w; + chans = swizzle->type->vector_elements; + } else { + deref_var = (*ir)->as_dereference_variable(); + if (!deref_var) + return; + + swizzle_chan[0] = 0; + swizzle_chan[1] = 1; + swizzle_chan[2] = 2; + swizzle_chan[3] = 3; + chans = deref_var->type->vector_elements; + } + + if (this->in_assignee) + return; + + ir_variable *var = deref_var->var; + + /* Try to find ACP entries covering swizzle_chan[], hoping they're + * the same source variable. + */ + foreach_iter(exec_list_iterator, iter, *this->acp) { + acp_entry *entry = (acp_entry *)iter.get(); + + if (var == entry->lhs) { + for (int c = 0; c < chans; c++) { + if (entry->write_mask & (1 << swizzle_chan[c])) { + source[c] = entry->rhs; + source_chan[c] = entry->swizzle[swizzle_chan[c]]; + } + } + } + } + + /* Make sure all channels are copying from the same source variable. */ + if (!source[0]) + return; + for (int c = 1; c < chans; c++) { + if (source[c] != source[0]) + return; + } + + if (!shader_mem_ctx) + shader_mem_ctx = ralloc_parent(deref_var); + + if (debug) { + printf("Copy propagation from:\n"); + (*ir)->print(); + } + + deref_var = new(shader_mem_ctx) ir_dereference_variable(source[0]); + *ir = new(shader_mem_ctx) ir_swizzle(deref_var, + source_chan[0], + source_chan[1], + source_chan[2], + source_chan[3], + chans); + + if (debug) { + printf("to:\n"); + (*ir)->print(); + printf("\n"); + } +} + + +ir_visitor_status +ir_copy_propagation_elements_visitor::visit_enter(ir_call *ir) +{ + /* Do copy propagation on call parameters, but skip any out params */ + exec_list_iterator sig_param_iter = ir->get_callee()->parameters.iterator(); + foreach_iter(exec_list_iterator, iter, ir->actual_parameters) { + ir_variable *sig_param = (ir_variable *)sig_param_iter.get(); + ir_instruction *ir = (ir_instruction *)iter.get(); + if (sig_param->mode != ir_var_out && sig_param->mode != ir_var_inout) { + ir->accept(this); + } + sig_param_iter.next(); + } + + /* Since we're unlinked, we don't (necessarily) know the side effects of + * this call. So kill all copies. + */ + acp->make_empty(); + this->killed_all = true; + + return visit_continue_with_parent; +} + +void +ir_copy_propagation_elements_visitor::handle_if_block(exec_list *instructions) +{ + exec_list *orig_acp = this->acp; + exec_list *orig_kills = this->kills; + bool orig_killed_all = this->killed_all; + + this->acp = new(mem_ctx) exec_list; + this->kills = new(mem_ctx) exec_list; + this->killed_all = false; + + /* Populate the initial acp with a copy of the original */ + foreach_iter(exec_list_iterator, iter, *orig_acp) { + acp_entry *a = (acp_entry *)iter.get(); + this->acp->push_tail(new(this->mem_ctx) acp_entry(a)); + } + + visit_list_elements(this, instructions); + + if (this->killed_all) { + orig_acp->make_empty(); + } + + exec_list *new_kills = this->kills; + this->kills = orig_kills; + this->acp = orig_acp; + this->killed_all = this->killed_all || orig_killed_all; + + /* Move the new kills into the parent block's list, removing them + * from the parent's ACP list in the process. + */ + foreach_list_safe(node, new_kills) { + kill_entry *k = (kill_entry *)node; + kill(k); + } +} + +ir_visitor_status +ir_copy_propagation_elements_visitor::visit_enter(ir_if *ir) +{ + ir->condition->accept(this); + + handle_if_block(&ir->then_instructions); + handle_if_block(&ir->else_instructions); + + /* handle_if_block() already descended into the children. */ + return visit_continue_with_parent; +} + +ir_visitor_status +ir_copy_propagation_elements_visitor::visit_enter(ir_loop *ir) +{ + exec_list *orig_acp = this->acp; + exec_list *orig_kills = this->kills; + bool orig_killed_all = this->killed_all; + + /* FINISHME: For now, the initial acp for loops is totally empty. + * We could go through once, then go through again with the acp + * cloned minus the killed entries after the first run through. + */ + this->acp = new(mem_ctx) exec_list; + this->kills = new(mem_ctx) exec_list; + this->killed_all = false; + + visit_list_elements(this, &ir->body_instructions); + + if (this->killed_all) { + orig_acp->make_empty(); + } + + exec_list *new_kills = this->kills; + this->kills = orig_kills; + this->acp = orig_acp; + this->killed_all = this->killed_all || orig_killed_all; + + foreach_list_safe(node, new_kills) { + kill_entry *k = (kill_entry *)node; + kill(k); + } + + /* already descended into the children. */ + return visit_continue_with_parent; +} + +/* Remove any entries currently in the ACP for this kill. */ +void +ir_copy_propagation_elements_visitor::kill(kill_entry *k) +{ + foreach_list_safe(node, acp) { + acp_entry *entry = (acp_entry *)node; + + if (entry->lhs == k->var) { + entry->write_mask = entry->write_mask & ~k->write_mask; + if (entry->write_mask == 0) + entry->remove(); + } + if (entry->rhs == k->var) { + entry->remove(); + } + } + + /* If we were on a list, remove ourselves before inserting */ + if (k->next) + k->remove(); + + this->kills->push_tail(k); +} + +/** + * Adds directly-copied channels between vector variables to the available + * copy propagation list. + */ +void +ir_copy_propagation_elements_visitor::add_copy(ir_assignment *ir) +{ + acp_entry *entry; + int orig_swizzle[4] = {0, 1, 2, 3}; + int swizzle[4]; + + if (ir->condition) + return; + + ir_dereference_variable *lhs = ir->lhs->as_dereference_variable(); + if (!lhs || !(lhs->type->is_scalar() || lhs->type->is_vector())) + return; + + ir_dereference_variable *rhs = ir->rhs->as_dereference_variable(); + if (!rhs) { + ir_swizzle *swiz = ir->rhs->as_swizzle(); + if (!swiz) + return; + + rhs = swiz->val->as_dereference_variable(); + if (!rhs) + return; + + orig_swizzle[0] = swiz->mask.x; + orig_swizzle[1] = swiz->mask.y; + orig_swizzle[2] = swiz->mask.z; + orig_swizzle[3] = swiz->mask.w; + } + + /* Move the swizzle channels out to the positions they match in the + * destination. We don't want to have to rewrite the swizzle[] + * array every time we clear a bit of the write_mask. + */ + int j = 0; + for (int i = 0; i < 4; i++) { + if (ir->write_mask & (1 << i)) + swizzle[i] = orig_swizzle[j++]; + } + + entry = new(this->mem_ctx) acp_entry(lhs->var, rhs->var, ir->write_mask, + swizzle); + this->acp->push_tail(entry); +} + +bool +do_copy_propagation_elements(exec_list *instructions) +{ + ir_copy_propagation_elements_visitor v; + + visit_list_elements(&v, instructions); + + return v.progress; +} diff --git a/mesalib/src/glsl/opt_dead_code_local.cpp b/mesalib/src/glsl/opt_dead_code_local.cpp index 88dcdc251..f67b46337 100644 --- a/mesalib/src/glsl/opt_dead_code_local.cpp +++ b/mesalib/src/glsl/opt_dead_code_local.cpp @@ -131,15 +131,8 @@ process_assignment(void *ctx, ir_assignment *ir, exec_list *assignments) var = ir->lhs->variable_referenced(); assert(var); - bool always_assign = true; - if (ir->condition) { - ir_constant *condition = ir->condition->as_constant(); - if (!condition || !condition->value.b[0]) - always_assign = false; - } - /* Now, check if we did a whole-variable assignment. */ - if (always_assign && (ir->whole_variable_written() != NULL)) { + if (!ir->condition && (ir->whole_variable_written() != NULL)) { /* We did a whole-variable assignment. So, any instruction in * the assignment list with the same LHS is dead. */ diff --git a/mesalib/src/mesa/main/enable.c b/mesalib/src/mesa/main/enable.c index 08e396bc1..d34c6ff90 100644 --- a/mesalib/src/mesa/main/enable.c +++ b/mesalib/src/mesa/main/enable.c @@ -1,1503 +1,1504 @@ -/** - * \file enable.c - * Enable/disable/query GL capabilities. - */ - -/* - * Mesa 3-D graphics library - * Version: 7.0.3 - * - * 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 "enable.h" -#include "light.h" -#include "simple_list.h" -#include "mfeatures.h" -#include "mtypes.h" -#include "enums.h" -#include "api_arrayelt.h" -#include "texstate.h" - - - -#define CHECK_EXTENSION(EXTNAME, CAP) \ - if (!ctx->Extensions.EXTNAME) { \ - goto invalid_enum_error; \ - } - - -/** - * Helper to enable/disable client-side state. - */ -static void -client_state(struct gl_context *ctx, GLenum cap, GLboolean state) -{ - struct gl_array_object *arrayObj = ctx->Array.ArrayObj; - GLuint flag; - GLboolean *var; - - switch (cap) { - case GL_VERTEX_ARRAY: - var = &arrayObj->Vertex.Enabled; - flag = _NEW_ARRAY_VERTEX; - break; - case GL_NORMAL_ARRAY: - var = &arrayObj->Normal.Enabled; - flag = _NEW_ARRAY_NORMAL; - break; - case GL_COLOR_ARRAY: - var = &arrayObj->Color.Enabled; - flag = _NEW_ARRAY_COLOR0; - break; - case GL_INDEX_ARRAY: - var = &arrayObj->Index.Enabled; - flag = _NEW_ARRAY_INDEX; - break; - case GL_TEXTURE_COORD_ARRAY: - var = &arrayObj->TexCoord[ctx->Array.ActiveTexture].Enabled; - flag = _NEW_ARRAY_TEXCOORD(ctx->Array.ActiveTexture); - break; - case GL_EDGE_FLAG_ARRAY: - var = &arrayObj->EdgeFlag.Enabled; - flag = _NEW_ARRAY_EDGEFLAG; - break; - case GL_FOG_COORDINATE_ARRAY_EXT: - var = &arrayObj->FogCoord.Enabled; - flag = _NEW_ARRAY_FOGCOORD; - break; - case GL_SECONDARY_COLOR_ARRAY_EXT: - var = &arrayObj->SecondaryColor.Enabled; - flag = _NEW_ARRAY_COLOR1; - break; - -#if FEATURE_point_size_array - case GL_POINT_SIZE_ARRAY_OES: - var = &arrayObj->PointSize.Enabled; - flag = _NEW_ARRAY_POINT_SIZE; - break; -#endif - -#if FEATURE_NV_vertex_program - case GL_VERTEX_ATTRIB_ARRAY0_NV: - case GL_VERTEX_ATTRIB_ARRAY1_NV: - case GL_VERTEX_ATTRIB_ARRAY2_NV: - case GL_VERTEX_ATTRIB_ARRAY3_NV: - case GL_VERTEX_ATTRIB_ARRAY4_NV: - case GL_VERTEX_ATTRIB_ARRAY5_NV: - case GL_VERTEX_ATTRIB_ARRAY6_NV: - case GL_VERTEX_ATTRIB_ARRAY7_NV: - case GL_VERTEX_ATTRIB_ARRAY8_NV: - case GL_VERTEX_ATTRIB_ARRAY9_NV: - case GL_VERTEX_ATTRIB_ARRAY10_NV: - case GL_VERTEX_ATTRIB_ARRAY11_NV: - case GL_VERTEX_ATTRIB_ARRAY12_NV: - case GL_VERTEX_ATTRIB_ARRAY13_NV: - case GL_VERTEX_ATTRIB_ARRAY14_NV: - case GL_VERTEX_ATTRIB_ARRAY15_NV: - CHECK_EXTENSION(NV_vertex_program, cap); - { - GLint n = (GLint) cap - GL_VERTEX_ATTRIB_ARRAY0_NV; - ASSERT(n < Elements(ctx->Array.ArrayObj->VertexAttrib)); - var = &arrayObj->VertexAttrib[n].Enabled; - flag = _NEW_ARRAY_ATTRIB(n); - } - break; -#endif /* FEATURE_NV_vertex_program */ - - /* GL_NV_primitive_restart */ - case GL_PRIMITIVE_RESTART_NV: - if (!ctx->Extensions.NV_primitive_restart) { - goto invalid_enum_error; - } - var = &ctx->Array.PrimitiveRestart; - flag = 0; - break; - - default: - goto invalid_enum_error; - } - - if (*var == state) - return; - - FLUSH_VERTICES(ctx, _NEW_ARRAY); - ctx->Array.NewState |= flag; - - _ae_invalidate_state(ctx, _NEW_ARRAY); - - *var = state; - - if (state) - ctx->Array.ArrayObj->_Enabled |= flag; - else - ctx->Array.ArrayObj->_Enabled &= ~flag; - - if (ctx->Driver.Enable) { - ctx->Driver.Enable( ctx, cap, state ); - } - - return; - -invalid_enum_error: - _mesa_error(ctx, GL_INVALID_ENUM, "gl%sClientState(0x%x)", - state ? "Enable" : "Disable", cap); -} - - -/** - * Enable GL capability. - * \param cap state to enable/disable. - * - * Get's the current context, assures that we're outside glBegin()/glEnd() and - * calls client_state(). - */ -void GLAPIENTRY -_mesa_EnableClientState( GLenum cap ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - client_state( ctx, cap, GL_TRUE ); -} - - -/** - * Disable GL capability. - * \param cap state to enable/disable. - * - * Get's the current context, assures that we're outside glBegin()/glEnd() and - * calls client_state(). - */ -void GLAPIENTRY -_mesa_DisableClientState( GLenum cap ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - client_state( ctx, cap, GL_FALSE ); -} - - -#undef CHECK_EXTENSION -#define CHECK_EXTENSION(EXTNAME, CAP) \ - if (!ctx->Extensions.EXTNAME) { \ - goto invalid_enum_error; \ - } - -#define CHECK_EXTENSION2(EXT1, EXT2, CAP) \ - if (!ctx->Extensions.EXT1 && !ctx->Extensions.EXT2) { \ - goto invalid_enum_error; \ - } - - - -/** - * Return pointer to current texture unit for setting/getting coordinate - * state. - * Note that we'll set GL_INVALID_OPERATION if the active texture unit is - * higher than the number of supported coordinate units. And we'll return NULL. - */ -static struct gl_texture_unit * -get_texcoord_unit(struct gl_context *ctx) -{ - if (ctx->Texture.CurrentUnit >= ctx->Const.MaxTextureCoordUnits) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glEnable/Disable(texcoord unit)"); - return NULL; - } - else { - return &ctx->Texture.Unit[ctx->Texture.CurrentUnit]; - } -} - - -/** - * Helper function to enable or disable a texture target. - * \param bit one of the TEXTURE_x_BIT values - * \return GL_TRUE if state is changing or GL_FALSE if no change - */ -static GLboolean -enable_texture(struct gl_context *ctx, GLboolean state, GLbitfield texBit) -{ - struct gl_texture_unit *texUnit = _mesa_get_current_tex_unit(ctx); - const GLbitfield newenabled = state - ? (texUnit->Enabled | texBit) : (texUnit->Enabled & ~texBit); - - if (texUnit->Enabled == newenabled) - return GL_FALSE; - - FLUSH_VERTICES(ctx, _NEW_TEXTURE); - texUnit->Enabled = newenabled; - return GL_TRUE; -} - - -/** - * Helper function to enable or disable state. - * - * \param ctx GL context. - * \param cap the state to enable/disable - * \param state whether to enable or disable the specified capability. - * - * Updates the current context and flushes the vertices as needed. For - * capabilities associated with extensions it verifies that those extensions - * are effectivly present before updating. Notifies the driver via - * dd_function_table::Enable. - */ -void -_mesa_set_enable(struct gl_context *ctx, GLenum cap, GLboolean state) -{ - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, "%s %s (newstate is %x)\n", - state ? "glEnable" : "glDisable", - _mesa_lookup_enum_by_nr(cap), - ctx->NewState); - - switch (cap) { - case GL_ALPHA_TEST: - if (ctx->Color.AlphaEnabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_COLOR); - ctx->Color.AlphaEnabled = state; - break; - case GL_AUTO_NORMAL: - if (ctx->Eval.AutoNormal == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.AutoNormal = state; - break; - case GL_BLEND: - { - GLbitfield newEnabled = state * ((1 << ctx->Const.MaxDrawBuffers) - 1); - if (newEnabled != ctx->Color.BlendEnabled) { - FLUSH_VERTICES(ctx, _NEW_COLOR); - ctx->Color.BlendEnabled = newEnabled; - } - } - break; -#if FEATURE_userclip - case GL_CLIP_PLANE0: - case GL_CLIP_PLANE1: - case GL_CLIP_PLANE2: - case GL_CLIP_PLANE3: - case GL_CLIP_PLANE4: - case GL_CLIP_PLANE5: - { - const GLuint p = cap - GL_CLIP_PLANE0; - - if ((ctx->Transform.ClipPlanesEnabled & (1 << p)) == ((GLuint) state << p)) - return; - - FLUSH_VERTICES(ctx, _NEW_TRANSFORM); - - if (state) { - ctx->Transform.ClipPlanesEnabled |= (1 << p); - - if (_math_matrix_is_dirty(ctx->ProjectionMatrixStack.Top)) - _math_matrix_analyse( ctx->ProjectionMatrixStack.Top ); - - /* This derived state also calculated in clip.c and - * from _mesa_update_state() on changes to EyeUserPlane - * and ctx->ProjectionMatrix respectively. - */ - _mesa_transform_vector( ctx->Transform._ClipUserPlane[p], - ctx->Transform.EyeUserPlane[p], - ctx->ProjectionMatrixStack.Top->inv ); - } - else { - ctx->Transform.ClipPlanesEnabled &= ~(1 << p); - } - } - break; -#endif - case GL_COLOR_MATERIAL: - if (ctx->Light.ColorMaterialEnabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_LIGHT); - FLUSH_CURRENT(ctx, 0); - ctx->Light.ColorMaterialEnabled = state; - if (state) { - _mesa_update_color_material( ctx, - ctx->Current.Attrib[VERT_ATTRIB_COLOR0] ); - } - break; - case GL_CULL_FACE: - if (ctx->Polygon.CullFlag == state) - return; - FLUSH_VERTICES(ctx, _NEW_POLYGON); - ctx->Polygon.CullFlag = state; - break; - case GL_DEPTH_TEST: - if (ctx->Depth.Test == state) - return; - FLUSH_VERTICES(ctx, _NEW_DEPTH); - ctx->Depth.Test = state; - break; - case GL_DITHER: - if (ctx->NoDither) { - state = GL_FALSE; /* MESA_NO_DITHER env var */ - } - if (ctx->Color.DitherFlag == state) - return; - FLUSH_VERTICES(ctx, _NEW_COLOR); - ctx->Color.DitherFlag = state; - break; - case GL_FOG: - if (ctx->Fog.Enabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_FOG); - ctx->Fog.Enabled = state; - break; - case GL_LIGHT0: - case GL_LIGHT1: - case GL_LIGHT2: - case GL_LIGHT3: - case GL_LIGHT4: - case GL_LIGHT5: - case GL_LIGHT6: - case GL_LIGHT7: - if (ctx->Light.Light[cap-GL_LIGHT0].Enabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_LIGHT); - ctx->Light.Light[cap-GL_LIGHT0].Enabled = state; - if (state) { - insert_at_tail(&ctx->Light.EnabledList, - &ctx->Light.Light[cap-GL_LIGHT0]); - } - else { - remove_from_list(&ctx->Light.Light[cap-GL_LIGHT0]); - } - break; - case GL_LIGHTING: - if (ctx->Light.Enabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_LIGHT); - ctx->Light.Enabled = state; - if (ctx->Light.Enabled && ctx->Light.Model.TwoSide) - ctx->_TriangleCaps |= DD_TRI_LIGHT_TWOSIDE; - else - ctx->_TriangleCaps &= ~DD_TRI_LIGHT_TWOSIDE; - break; - case GL_LINE_SMOOTH: - if (ctx->Line.SmoothFlag == state) - return; - FLUSH_VERTICES(ctx, _NEW_LINE); - ctx->Line.SmoothFlag = state; - ctx->_TriangleCaps ^= DD_LINE_SMOOTH; - break; - case GL_LINE_STIPPLE: - if (ctx->Line.StippleFlag == state) - return; - FLUSH_VERTICES(ctx, _NEW_LINE); - ctx->Line.StippleFlag = state; - ctx->_TriangleCaps ^= DD_LINE_STIPPLE; - break; - case GL_INDEX_LOGIC_OP: - if (ctx->Color.IndexLogicOpEnabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_COLOR); - ctx->Color.IndexLogicOpEnabled = state; - break; - case GL_COLOR_LOGIC_OP: - if (ctx->Color.ColorLogicOpEnabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_COLOR); - ctx->Color.ColorLogicOpEnabled = state; - break; - case GL_MAP1_COLOR_4: - if (ctx->Eval.Map1Color4 == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map1Color4 = state; - break; - case GL_MAP1_INDEX: - if (ctx->Eval.Map1Index == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map1Index = state; - break; - case GL_MAP1_NORMAL: - if (ctx->Eval.Map1Normal == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map1Normal = state; - break; - case GL_MAP1_TEXTURE_COORD_1: - if (ctx->Eval.Map1TextureCoord1 == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map1TextureCoord1 = state; - break; - case GL_MAP1_TEXTURE_COORD_2: - if (ctx->Eval.Map1TextureCoord2 == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map1TextureCoord2 = state; - break; - case GL_MAP1_TEXTURE_COORD_3: - if (ctx->Eval.Map1TextureCoord3 == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map1TextureCoord3 = state; - break; - case GL_MAP1_TEXTURE_COORD_4: - if (ctx->Eval.Map1TextureCoord4 == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map1TextureCoord4 = state; - break; - case GL_MAP1_VERTEX_3: - if (ctx->Eval.Map1Vertex3 == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map1Vertex3 = state; - break; - case GL_MAP1_VERTEX_4: - if (ctx->Eval.Map1Vertex4 == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map1Vertex4 = state; - break; - case GL_MAP2_COLOR_4: - if (ctx->Eval.Map2Color4 == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map2Color4 = state; - break; - case GL_MAP2_INDEX: - if (ctx->Eval.Map2Index == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map2Index = state; - break; - case GL_MAP2_NORMAL: - if (ctx->Eval.Map2Normal == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map2Normal = state; - break; - case GL_MAP2_TEXTURE_COORD_1: - if (ctx->Eval.Map2TextureCoord1 == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map2TextureCoord1 = state; - break; - case GL_MAP2_TEXTURE_COORD_2: - if (ctx->Eval.Map2TextureCoord2 == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map2TextureCoord2 = state; - break; - case GL_MAP2_TEXTURE_COORD_3: - if (ctx->Eval.Map2TextureCoord3 == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map2TextureCoord3 = state; - break; - case GL_MAP2_TEXTURE_COORD_4: - if (ctx->Eval.Map2TextureCoord4 == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map2TextureCoord4 = state; - break; - case GL_MAP2_VERTEX_3: - if (ctx->Eval.Map2Vertex3 == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map2Vertex3 = state; - break; - case GL_MAP2_VERTEX_4: - if (ctx->Eval.Map2Vertex4 == state) - return; - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map2Vertex4 = state; - break; - case GL_NORMALIZE: - if (ctx->Transform.Normalize == state) - return; - FLUSH_VERTICES(ctx, _NEW_TRANSFORM); - ctx->Transform.Normalize = state; - break; - case GL_POINT_SMOOTH: - if (ctx->Point.SmoothFlag == state) - return; - FLUSH_VERTICES(ctx, _NEW_POINT); - ctx->Point.SmoothFlag = state; - ctx->_TriangleCaps ^= DD_POINT_SMOOTH; - break; - case GL_POLYGON_SMOOTH: - if (ctx->Polygon.SmoothFlag == state) - return; - FLUSH_VERTICES(ctx, _NEW_POLYGON); - ctx->Polygon.SmoothFlag = state; - ctx->_TriangleCaps ^= DD_TRI_SMOOTH; - break; - case GL_POLYGON_STIPPLE: - if (ctx->Polygon.StippleFlag == state) - return; - FLUSH_VERTICES(ctx, _NEW_POLYGON); - ctx->Polygon.StippleFlag = state; - ctx->_TriangleCaps ^= DD_TRI_STIPPLE; - break; - case GL_POLYGON_OFFSET_POINT: - if (ctx->Polygon.OffsetPoint == state) - return; - FLUSH_VERTICES(ctx, _NEW_POLYGON); - ctx->Polygon.OffsetPoint = state; - break; - case GL_POLYGON_OFFSET_LINE: - if (ctx->Polygon.OffsetLine == state) - return; - FLUSH_VERTICES(ctx, _NEW_POLYGON); - ctx->Polygon.OffsetLine = state; - break; - case GL_POLYGON_OFFSET_FILL: - /*case GL_POLYGON_OFFSET_EXT:*/ - if (ctx->Polygon.OffsetFill == state) - return; - FLUSH_VERTICES(ctx, _NEW_POLYGON); - ctx->Polygon.OffsetFill = state; - break; - case GL_RESCALE_NORMAL_EXT: - if (ctx->Transform.RescaleNormals == state) - return; - FLUSH_VERTICES(ctx, _NEW_TRANSFORM); - ctx->Transform.RescaleNormals = state; - break; - case GL_SCISSOR_TEST: - if (ctx->Scissor.Enabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_SCISSOR); - ctx->Scissor.Enabled = state; - break; - case GL_SHARED_TEXTURE_PALETTE_EXT: - if (ctx->Texture.SharedPalette == state) - return; - FLUSH_VERTICES(ctx, _NEW_TEXTURE); - ctx->Texture.SharedPalette = state; - break; - case GL_STENCIL_TEST: - if (ctx->Stencil.Enabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.Enabled = state; - break; - case GL_TEXTURE_1D: - if (!enable_texture(ctx, state, TEXTURE_1D_BIT)) { - return; - } - break; - case GL_TEXTURE_2D: - if (!enable_texture(ctx, state, TEXTURE_2D_BIT)) { - return; - } - break; - case GL_TEXTURE_3D: - if (!enable_texture(ctx, state, TEXTURE_3D_BIT)) { - return; - } - break; - case GL_TEXTURE_GEN_Q: - { - struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); - if (texUnit) { - GLuint newenabled = texUnit->TexGenEnabled & ~Q_BIT; - if (state) - newenabled |= Q_BIT; - if (texUnit->TexGenEnabled == newenabled) - return; - FLUSH_VERTICES(ctx, _NEW_TEXTURE); - texUnit->TexGenEnabled = newenabled; - } - } - break; - case GL_TEXTURE_GEN_R: - { - struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); - if (texUnit) { - GLuint newenabled = texUnit->TexGenEnabled & ~R_BIT; - if (state) - newenabled |= R_BIT; - if (texUnit->TexGenEnabled == newenabled) - return; - FLUSH_VERTICES(ctx, _NEW_TEXTURE); - texUnit->TexGenEnabled = newenabled; - } - } - break; - case GL_TEXTURE_GEN_S: - { - struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); - if (texUnit) { - GLuint newenabled = texUnit->TexGenEnabled & ~S_BIT; - if (state) - newenabled |= S_BIT; - if (texUnit->TexGenEnabled == newenabled) - return; - FLUSH_VERTICES(ctx, _NEW_TEXTURE); - texUnit->TexGenEnabled = newenabled; - } - } - break; - case GL_TEXTURE_GEN_T: - { - struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); - if (texUnit) { - GLuint newenabled = texUnit->TexGenEnabled & ~T_BIT; - if (state) - newenabled |= T_BIT; - if (texUnit->TexGenEnabled == newenabled) - return; - FLUSH_VERTICES(ctx, _NEW_TEXTURE); - texUnit->TexGenEnabled = newenabled; - } - } - break; - -#if FEATURE_ES1 - case GL_TEXTURE_GEN_STR_OES: - /* disable S, T, and R at the same time */ - { - struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); - if (texUnit) { - GLuint newenabled = - texUnit->TexGenEnabled & ~STR_BITS; - if (state) - newenabled |= STR_BITS; - if (texUnit->TexGenEnabled == newenabled) - return; - FLUSH_VERTICES(ctx, _NEW_TEXTURE); - texUnit->TexGenEnabled = newenabled; - } - } - break; -#endif - - /* - * CLIENT STATE!!! - */ - case GL_VERTEX_ARRAY: - case GL_NORMAL_ARRAY: - case GL_COLOR_ARRAY: - case GL_INDEX_ARRAY: - case GL_TEXTURE_COORD_ARRAY: - case GL_EDGE_FLAG_ARRAY: - case GL_FOG_COORDINATE_ARRAY_EXT: - case GL_SECONDARY_COLOR_ARRAY_EXT: - case GL_POINT_SIZE_ARRAY_OES: - client_state( ctx, cap, state ); - return; - - /* GL_SGI_texture_color_table */ - case GL_TEXTURE_COLOR_TABLE_SGI: - CHECK_EXTENSION(SGI_texture_color_table, cap); - if (ctx->Texture.Unit[ctx->Texture.CurrentUnit].ColorTableEnabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_TEXTURE); - ctx->Texture.Unit[ctx->Texture.CurrentUnit].ColorTableEnabled = state; - break; - - /* GL_ARB_texture_cube_map */ - case GL_TEXTURE_CUBE_MAP_ARB: - CHECK_EXTENSION(ARB_texture_cube_map, cap); - if (!enable_texture(ctx, state, TEXTURE_CUBE_BIT)) { - return; - } - break; - - /* GL_EXT_secondary_color */ - case GL_COLOR_SUM_EXT: - CHECK_EXTENSION2(EXT_secondary_color, ARB_vertex_program, cap); - if (ctx->Fog.ColorSumEnabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_FOG); - ctx->Fog.ColorSumEnabled = state; - break; - - /* GL_ARB_multisample */ - case GL_MULTISAMPLE_ARB: - if (ctx->Multisample.Enabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_MULTISAMPLE); - ctx->Multisample.Enabled = state; - break; - case GL_SAMPLE_ALPHA_TO_COVERAGE_ARB: - if (ctx->Multisample.SampleAlphaToCoverage == state) - return; - FLUSH_VERTICES(ctx, _NEW_MULTISAMPLE); - ctx->Multisample.SampleAlphaToCoverage = state; - break; - case GL_SAMPLE_ALPHA_TO_ONE_ARB: - if (ctx->Multisample.SampleAlphaToOne == state) - return; - FLUSH_VERTICES(ctx, _NEW_MULTISAMPLE); - ctx->Multisample.SampleAlphaToOne = state; - break; - case GL_SAMPLE_COVERAGE_ARB: - if (ctx->Multisample.SampleCoverage == state) - return; - FLUSH_VERTICES(ctx, _NEW_MULTISAMPLE); - ctx->Multisample.SampleCoverage = state; - break; - case GL_SAMPLE_COVERAGE_INVERT_ARB: - if (ctx->Multisample.SampleCoverageInvert == state) - return; - FLUSH_VERTICES(ctx, _NEW_MULTISAMPLE); - ctx->Multisample.SampleCoverageInvert = state; - break; - - /* GL_IBM_rasterpos_clip */ - case GL_RASTER_POSITION_UNCLIPPED_IBM: - CHECK_EXTENSION(IBM_rasterpos_clip, cap); - if (ctx->Transform.RasterPositionUnclipped == state) - return; - FLUSH_VERTICES(ctx, _NEW_TRANSFORM); - ctx->Transform.RasterPositionUnclipped = state; - break; - - /* GL_NV_point_sprite */ - case GL_POINT_SPRITE_NV: - CHECK_EXTENSION2(NV_point_sprite, ARB_point_sprite, cap); - if (ctx->Point.PointSprite == state) - return; - FLUSH_VERTICES(ctx, _NEW_POINT); - ctx->Point.PointSprite = state; - break; - -#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program - case GL_VERTEX_PROGRAM_ARB: - CHECK_EXTENSION2(ARB_vertex_program, NV_vertex_program, cap); - if (ctx->VertexProgram.Enabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_PROGRAM); - ctx->VertexProgram.Enabled = state; - break; - case GL_VERTEX_PROGRAM_POINT_SIZE_ARB: - CHECK_EXTENSION2(ARB_vertex_program, NV_vertex_program, cap); - if (ctx->VertexProgram.PointSizeEnabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_PROGRAM); - ctx->VertexProgram.PointSizeEnabled = state; - break; - case GL_VERTEX_PROGRAM_TWO_SIDE_ARB: - CHECK_EXTENSION2(ARB_vertex_program, NV_vertex_program, cap); - if (ctx->VertexProgram.TwoSideEnabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_PROGRAM); - ctx->VertexProgram.TwoSideEnabled = state; - break; -#endif -#if FEATURE_NV_vertex_program - case GL_MAP1_VERTEX_ATTRIB0_4_NV: - case GL_MAP1_VERTEX_ATTRIB1_4_NV: - case GL_MAP1_VERTEX_ATTRIB2_4_NV: - case GL_MAP1_VERTEX_ATTRIB3_4_NV: - case GL_MAP1_VERTEX_ATTRIB4_4_NV: - case GL_MAP1_VERTEX_ATTRIB5_4_NV: - case GL_MAP1_VERTEX_ATTRIB6_4_NV: - case GL_MAP1_VERTEX_ATTRIB7_4_NV: - case GL_MAP1_VERTEX_ATTRIB8_4_NV: - case GL_MAP1_VERTEX_ATTRIB9_4_NV: - case GL_MAP1_VERTEX_ATTRIB10_4_NV: - case GL_MAP1_VERTEX_ATTRIB11_4_NV: - case GL_MAP1_VERTEX_ATTRIB12_4_NV: - case GL_MAP1_VERTEX_ATTRIB13_4_NV: - case GL_MAP1_VERTEX_ATTRIB14_4_NV: - case GL_MAP1_VERTEX_ATTRIB15_4_NV: - CHECK_EXTENSION(NV_vertex_program, cap); - { - const GLuint map = (GLuint) (cap - GL_MAP1_VERTEX_ATTRIB0_4_NV); - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map1Attrib[map] = state; - } - break; - case GL_MAP2_VERTEX_ATTRIB0_4_NV: - case GL_MAP2_VERTEX_ATTRIB1_4_NV: - case GL_MAP2_VERTEX_ATTRIB2_4_NV: - case GL_MAP2_VERTEX_ATTRIB3_4_NV: - case GL_MAP2_VERTEX_ATTRIB4_4_NV: - case GL_MAP2_VERTEX_ATTRIB5_4_NV: - case GL_MAP2_VERTEX_ATTRIB6_4_NV: - case GL_MAP2_VERTEX_ATTRIB7_4_NV: - case GL_MAP2_VERTEX_ATTRIB8_4_NV: - case GL_MAP2_VERTEX_ATTRIB9_4_NV: - case GL_MAP2_VERTEX_ATTRIB10_4_NV: - case GL_MAP2_VERTEX_ATTRIB11_4_NV: - case GL_MAP2_VERTEX_ATTRIB12_4_NV: - case GL_MAP2_VERTEX_ATTRIB13_4_NV: - case GL_MAP2_VERTEX_ATTRIB14_4_NV: - case GL_MAP2_VERTEX_ATTRIB15_4_NV: - CHECK_EXTENSION(NV_vertex_program, cap); - { - const GLuint map = (GLuint) (cap - GL_MAP2_VERTEX_ATTRIB0_4_NV); - FLUSH_VERTICES(ctx, _NEW_EVAL); - ctx->Eval.Map2Attrib[map] = state; - } - break; -#endif /* FEATURE_NV_vertex_program */ - -#if FEATURE_NV_fragment_program - case GL_FRAGMENT_PROGRAM_NV: - CHECK_EXTENSION(NV_fragment_program, cap); - if (ctx->FragmentProgram.Enabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_PROGRAM); - ctx->FragmentProgram.Enabled = state; - break; -#endif /* FEATURE_NV_fragment_program */ - - /* GL_NV_texture_rectangle */ - case GL_TEXTURE_RECTANGLE_NV: - CHECK_EXTENSION(NV_texture_rectangle, cap); - if (!enable_texture(ctx, state, TEXTURE_RECT_BIT)) { - return; - } - break; - - /* GL_EXT_stencil_two_side */ - case GL_STENCIL_TEST_TWO_SIDE_EXT: - CHECK_EXTENSION(EXT_stencil_two_side, cap); - if (ctx->Stencil.TestTwoSide == state) - return; - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.TestTwoSide = state; - if (state) { - ctx->Stencil._BackFace = 2; - ctx->_TriangleCaps |= DD_TRI_TWOSTENCIL; - } else { - ctx->Stencil._BackFace = 1; - ctx->_TriangleCaps &= ~DD_TRI_TWOSTENCIL; - } - break; - -#if FEATURE_ARB_fragment_program - case GL_FRAGMENT_PROGRAM_ARB: - CHECK_EXTENSION(ARB_fragment_program, cap); - if (ctx->FragmentProgram.Enabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_PROGRAM); - ctx->FragmentProgram.Enabled = state; - break; -#endif /* FEATURE_ARB_fragment_program */ - - /* GL_EXT_depth_bounds_test */ - case GL_DEPTH_BOUNDS_TEST_EXT: - CHECK_EXTENSION(EXT_depth_bounds_test, cap); - if (ctx->Depth.BoundsTest == state) - return; - FLUSH_VERTICES(ctx, _NEW_DEPTH); - ctx->Depth.BoundsTest = state; - break; - - case GL_DEPTH_CLAMP: - if (ctx->Transform.DepthClamp == state) - return; - CHECK_EXTENSION(ARB_depth_clamp, cap); - FLUSH_VERTICES(ctx, _NEW_TRANSFORM); - ctx->Transform.DepthClamp = state; - break; - -#if FEATURE_ATI_fragment_shader - case GL_FRAGMENT_SHADER_ATI: - CHECK_EXTENSION(ATI_fragment_shader, cap); - if (ctx->ATIFragmentShader.Enabled == state) - return; - FLUSH_VERTICES(ctx, _NEW_PROGRAM); - ctx->ATIFragmentShader.Enabled = state; - break; -#endif - - /* GL_MESA_texture_array */ - case GL_TEXTURE_1D_ARRAY_EXT: - CHECK_EXTENSION(MESA_texture_array, cap); - if (!enable_texture(ctx, state, TEXTURE_1D_ARRAY_BIT)) { - return; - } - break; - - case GL_TEXTURE_2D_ARRAY_EXT: - CHECK_EXTENSION(MESA_texture_array, cap); - if (!enable_texture(ctx, state, TEXTURE_2D_ARRAY_BIT)) { - return; - } - break; - - case GL_TEXTURE_CUBE_MAP_SEAMLESS: - CHECK_EXTENSION(ARB_seamless_cube_map, cap); - ctx->Texture.CubeMapSeamless = state; - break; - -#if FEATURE_EXT_transform_feedback - case GL_RASTERIZER_DISCARD: - CHECK_EXTENSION(EXT_transform_feedback, cap); - if (ctx->TransformFeedback.RasterDiscard != state) { - ctx->TransformFeedback.RasterDiscard = state; - FLUSH_VERTICES(ctx, _NEW_TRANSFORM); - } - break; -#endif - - /* GL 3.1 primitive restart. Note: this enum is different from - * GL_PRIMITIVE_RESTART_NV (which is client state). - */ - case GL_PRIMITIVE_RESTART: - if (ctx->VersionMajor * 10 + ctx->VersionMinor < 31) { - goto invalid_enum_error; - } - if (ctx->Array.PrimitiveRestart != state) { - FLUSH_VERTICES(ctx, _NEW_TRANSFORM); - ctx->Array.PrimitiveRestart = state; - } - break; - - /* GL3.0 - GL_framebuffer_sRGB */ - case GL_FRAMEBUFFER_SRGB_EXT: - CHECK_EXTENSION(EXT_framebuffer_sRGB, cap); - ctx->Color.sRGBEnabled = state; - break; - - default: - goto invalid_enum_error; - } - - if (ctx->Driver.Enable) { - ctx->Driver.Enable( ctx, cap, state ); - } - - return; - -invalid_enum_error: - _mesa_error(ctx, GL_INVALID_ENUM, "gl%s(0x%x)", - state ? "Enable" : "Disable", cap); -} - - -/** - * Enable GL capability. Called by glEnable() - * \param cap state to enable. - */ -void GLAPIENTRY -_mesa_Enable( GLenum cap ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - _mesa_set_enable( ctx, cap, GL_TRUE ); -} - - -/** - * Disable GL capability. Called by glDisable() - * \param cap state to disable. - */ -void GLAPIENTRY -_mesa_Disable( GLenum cap ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - _mesa_set_enable( ctx, cap, GL_FALSE ); -} - - - -/** - * Enable/disable an indexed state var. - */ -void -_mesa_set_enablei(struct gl_context *ctx, GLenum cap, GLuint index, GLboolean state) -{ - ASSERT(state == 0 || state == 1); - switch (cap) { - case GL_BLEND: - if (!ctx->Extensions.EXT_draw_buffers2) { - goto invalid_enum_error; - } - if (index >= ctx->Const.MaxDrawBuffers) { - _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%u)", - state ? "glEnableIndexed" : "glDisableIndexed", index); - return; - } - if (((ctx->Color.BlendEnabled >> index) & 1) != state) { - FLUSH_VERTICES(ctx, _NEW_COLOR); - if (state) - ctx->Color.BlendEnabled |= (1 << index); - else - ctx->Color.BlendEnabled &= ~(1 << index); - } - break; - default: - goto invalid_enum_error; - } - return; - -invalid_enum_error: - _mesa_error(ctx, GL_INVALID_ENUM, "%s(cap=%s)", - state ? "glEnablei" : "glDisablei", - _mesa_lookup_enum_by_nr(cap)); -} - - -void GLAPIENTRY -_mesa_DisableIndexed( GLenum cap, GLuint index ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - _mesa_set_enablei(ctx, cap, index, GL_FALSE); -} - - -void GLAPIENTRY -_mesa_EnableIndexed( GLenum cap, GLuint index ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - _mesa_set_enablei(ctx, cap, index, GL_TRUE); -} - - -GLboolean GLAPIENTRY -_mesa_IsEnabledIndexed( GLenum cap, GLuint index ) -{ - GET_CURRENT_CONTEXT(ctx); - switch (cap) { - case GL_BLEND: - if (index >= ctx->Const.MaxDrawBuffers) { - _mesa_error(ctx, GL_INVALID_VALUE, "glIsEnabledIndexed(index=%u)", - index); - return GL_FALSE; - } - return (ctx->Color.BlendEnabled >> index) & 1; - default: - _mesa_error(ctx, GL_INVALID_ENUM, "glIsEnabledIndexed(cap=%s)", - _mesa_lookup_enum_by_nr(cap)); - return GL_FALSE; - } -} - - - - -#undef CHECK_EXTENSION -#define CHECK_EXTENSION(EXTNAME) \ - if (!ctx->Extensions.EXTNAME) { \ - goto invalid_enum_error; \ - } - -#undef CHECK_EXTENSION2 -#define CHECK_EXTENSION2(EXT1, EXT2) \ - if (!ctx->Extensions.EXT1 && !ctx->Extensions.EXT2) { \ - goto invalid_enum_error; \ - } - - -/** - * Helper function to determine whether a texture target is enabled. - */ -static GLboolean -is_texture_enabled(struct gl_context *ctx, GLbitfield bit) -{ - const struct gl_texture_unit *const texUnit = - &ctx->Texture.Unit[ctx->Texture.CurrentUnit]; - return (texUnit->Enabled & bit) ? GL_TRUE : GL_FALSE; -} - - -/** - * Return simple enable/disable state. - * - * \param cap state variable to query. - * - * Returns the state of the specified capability from the current GL context. - * For the capabilities associated with extensions verifies that those - * extensions are effectively present before reporting. - */ -GLboolean GLAPIENTRY -_mesa_IsEnabled( GLenum cap ) -{ - GET_CURRENT_CONTEXT(ctx); - switch (cap) { - case GL_ALPHA_TEST: - return ctx->Color.AlphaEnabled; - case GL_AUTO_NORMAL: - return ctx->Eval.AutoNormal; - case GL_BLEND: - return ctx->Color.BlendEnabled & 1; /* return state for buffer[0] */ - case GL_CLIP_PLANE0: - case GL_CLIP_PLANE1: - case GL_CLIP_PLANE2: - case GL_CLIP_PLANE3: - case GL_CLIP_PLANE4: - case GL_CLIP_PLANE5: - return (ctx->Transform.ClipPlanesEnabled >> (cap - GL_CLIP_PLANE0)) & 1; - case GL_COLOR_MATERIAL: - return ctx->Light.ColorMaterialEnabled; - case GL_CULL_FACE: - return ctx->Polygon.CullFlag; - case GL_DEPTH_TEST: - return ctx->Depth.Test; - case GL_DITHER: - return ctx->Color.DitherFlag; - case GL_FOG: - return ctx->Fog.Enabled; - case GL_LIGHTING: - return ctx->Light.Enabled; - case GL_LIGHT0: - case GL_LIGHT1: - case GL_LIGHT2: - case GL_LIGHT3: - case GL_LIGHT4: - case GL_LIGHT5: - case GL_LIGHT6: - case GL_LIGHT7: - return ctx->Light.Light[cap-GL_LIGHT0].Enabled; - case GL_LINE_SMOOTH: - return ctx->Line.SmoothFlag; - case GL_LINE_STIPPLE: - return ctx->Line.StippleFlag; - case GL_INDEX_LOGIC_OP: - return ctx->Color.IndexLogicOpEnabled; - case GL_COLOR_LOGIC_OP: - return ctx->Color.ColorLogicOpEnabled; - case GL_MAP1_COLOR_4: - return ctx->Eval.Map1Color4; - case GL_MAP1_INDEX: - return ctx->Eval.Map1Index; - case GL_MAP1_NORMAL: - return ctx->Eval.Map1Normal; - case GL_MAP1_TEXTURE_COORD_1: - return ctx->Eval.Map1TextureCoord1; - case GL_MAP1_TEXTURE_COORD_2: - return ctx->Eval.Map1TextureCoord2; - case GL_MAP1_TEXTURE_COORD_3: - return ctx->Eval.Map1TextureCoord3; - case GL_MAP1_TEXTURE_COORD_4: - return ctx->Eval.Map1TextureCoord4; - case GL_MAP1_VERTEX_3: - return ctx->Eval.Map1Vertex3; - case GL_MAP1_VERTEX_4: - return ctx->Eval.Map1Vertex4; - case GL_MAP2_COLOR_4: - return ctx->Eval.Map2Color4; - case GL_MAP2_INDEX: - return ctx->Eval.Map2Index; - case GL_MAP2_NORMAL: - return ctx->Eval.Map2Normal; - case GL_MAP2_TEXTURE_COORD_1: - return ctx->Eval.Map2TextureCoord1; - case GL_MAP2_TEXTURE_COORD_2: - return ctx->Eval.Map2TextureCoord2; - case GL_MAP2_TEXTURE_COORD_3: - return ctx->Eval.Map2TextureCoord3; - case GL_MAP2_TEXTURE_COORD_4: - return ctx->Eval.Map2TextureCoord4; - case GL_MAP2_VERTEX_3: - return ctx->Eval.Map2Vertex3; - case GL_MAP2_VERTEX_4: - return ctx->Eval.Map2Vertex4; - case GL_NORMALIZE: - return ctx->Transform.Normalize; - case GL_POINT_SMOOTH: - return ctx->Point.SmoothFlag; - case GL_POLYGON_SMOOTH: - return ctx->Polygon.SmoothFlag; - case GL_POLYGON_STIPPLE: - return ctx->Polygon.StippleFlag; - case GL_POLYGON_OFFSET_POINT: - return ctx->Polygon.OffsetPoint; - case GL_POLYGON_OFFSET_LINE: - return ctx->Polygon.OffsetLine; - case GL_POLYGON_OFFSET_FILL: - /*case GL_POLYGON_OFFSET_EXT:*/ - return ctx->Polygon.OffsetFill; - case GL_RESCALE_NORMAL_EXT: - return ctx->Transform.RescaleNormals; - case GL_SCISSOR_TEST: - return ctx->Scissor.Enabled; - case GL_SHARED_TEXTURE_PALETTE_EXT: - return ctx->Texture.SharedPalette; - case GL_STENCIL_TEST: - return ctx->Stencil.Enabled; - case GL_TEXTURE_1D: - return is_texture_enabled(ctx, TEXTURE_1D_BIT); - case GL_TEXTURE_2D: - return is_texture_enabled(ctx, TEXTURE_2D_BIT); - case GL_TEXTURE_3D: - return is_texture_enabled(ctx, TEXTURE_3D_BIT); - case GL_TEXTURE_GEN_Q: - { - const struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); - if (texUnit) { - return (texUnit->TexGenEnabled & Q_BIT) ? GL_TRUE : GL_FALSE; - } - } - return GL_FALSE; - case GL_TEXTURE_GEN_R: - { - const struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); - if (texUnit) { - return (texUnit->TexGenEnabled & R_BIT) ? GL_TRUE : GL_FALSE; - } - } - return GL_FALSE; - case GL_TEXTURE_GEN_S: - { - const struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); - if (texUnit) { - return (texUnit->TexGenEnabled & S_BIT) ? GL_TRUE : GL_FALSE; - } - } - return GL_FALSE; - case GL_TEXTURE_GEN_T: - { - const struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); - if (texUnit) { - return (texUnit->TexGenEnabled & T_BIT) ? GL_TRUE : GL_FALSE; - } - } - return GL_FALSE; -#if FEATURE_ES1 - case GL_TEXTURE_GEN_STR_OES: - { - const struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); - if (texUnit) { - return (texUnit->TexGenEnabled & STR_BITS) == STR_BITS ? GL_TRUE : GL_FALSE; - } - } -#endif - - /* - * CLIENT STATE!!! - */ - case GL_VERTEX_ARRAY: - return (ctx->Array.ArrayObj->Vertex.Enabled != 0); - case GL_NORMAL_ARRAY: - return (ctx->Array.ArrayObj->Normal.Enabled != 0); - case GL_COLOR_ARRAY: - return (ctx->Array.ArrayObj->Color.Enabled != 0); - case GL_INDEX_ARRAY: - return (ctx->Array.ArrayObj->Index.Enabled != 0); - case GL_TEXTURE_COORD_ARRAY: - return (ctx->Array.ArrayObj->TexCoord[ctx->Array.ActiveTexture].Enabled != 0); - case GL_EDGE_FLAG_ARRAY: - return (ctx->Array.ArrayObj->EdgeFlag.Enabled != 0); - case GL_FOG_COORDINATE_ARRAY_EXT: - CHECK_EXTENSION(EXT_fog_coord); - return (ctx->Array.ArrayObj->FogCoord.Enabled != 0); - case GL_SECONDARY_COLOR_ARRAY_EXT: - CHECK_EXTENSION(EXT_secondary_color); - return (ctx->Array.ArrayObj->SecondaryColor.Enabled != 0); -#if FEATURE_point_size_array - case GL_POINT_SIZE_ARRAY_OES: - return (ctx->Array.ArrayObj->PointSize.Enabled != 0); -#endif - - /* GL_SGI_texture_color_table */ - case GL_TEXTURE_COLOR_TABLE_SGI: - CHECK_EXTENSION(SGI_texture_color_table); - return ctx->Texture.Unit[ctx->Texture.CurrentUnit].ColorTableEnabled; - - /* GL_ARB_texture_cube_map */ - case GL_TEXTURE_CUBE_MAP_ARB: - CHECK_EXTENSION(ARB_texture_cube_map); - return is_texture_enabled(ctx, TEXTURE_CUBE_BIT); - - /* GL_EXT_secondary_color */ - case GL_COLOR_SUM_EXT: - CHECK_EXTENSION2(EXT_secondary_color, ARB_vertex_program); - return ctx->Fog.ColorSumEnabled; - - /* GL_ARB_multisample */ - case GL_MULTISAMPLE_ARB: - return ctx->Multisample.Enabled; - case GL_SAMPLE_ALPHA_TO_COVERAGE_ARB: - return ctx->Multisample.SampleAlphaToCoverage; - case GL_SAMPLE_ALPHA_TO_ONE_ARB: - return ctx->Multisample.SampleAlphaToOne; - case GL_SAMPLE_COVERAGE_ARB: - return ctx->Multisample.SampleCoverage; - case GL_SAMPLE_COVERAGE_INVERT_ARB: - return ctx->Multisample.SampleCoverageInvert; - - /* GL_IBM_rasterpos_clip */ - case GL_RASTER_POSITION_UNCLIPPED_IBM: - CHECK_EXTENSION(IBM_rasterpos_clip); - return ctx->Transform.RasterPositionUnclipped; - - /* GL_NV_point_sprite */ - case GL_POINT_SPRITE_NV: - CHECK_EXTENSION2(NV_point_sprite, ARB_point_sprite) - return ctx->Point.PointSprite; - -#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program - case GL_VERTEX_PROGRAM_ARB: - CHECK_EXTENSION2(ARB_vertex_program, NV_vertex_program); - return ctx->VertexProgram.Enabled; - case GL_VERTEX_PROGRAM_POINT_SIZE_ARB: - CHECK_EXTENSION2(ARB_vertex_program, NV_vertex_program); - return ctx->VertexProgram.PointSizeEnabled; - case GL_VERTEX_PROGRAM_TWO_SIDE_ARB: - CHECK_EXTENSION2(ARB_vertex_program, NV_vertex_program); - return ctx->VertexProgram.TwoSideEnabled; -#endif -#if FEATURE_NV_vertex_program - case GL_VERTEX_ATTRIB_ARRAY0_NV: - case GL_VERTEX_ATTRIB_ARRAY1_NV: - case GL_VERTEX_ATTRIB_ARRAY2_NV: - case GL_VERTEX_ATTRIB_ARRAY3_NV: - case GL_VERTEX_ATTRIB_ARRAY4_NV: - case GL_VERTEX_ATTRIB_ARRAY5_NV: - case GL_VERTEX_ATTRIB_ARRAY6_NV: - case GL_VERTEX_ATTRIB_ARRAY7_NV: - case GL_VERTEX_ATTRIB_ARRAY8_NV: - case GL_VERTEX_ATTRIB_ARRAY9_NV: - case GL_VERTEX_ATTRIB_ARRAY10_NV: - case GL_VERTEX_ATTRIB_ARRAY11_NV: - case GL_VERTEX_ATTRIB_ARRAY12_NV: - case GL_VERTEX_ATTRIB_ARRAY13_NV: - case GL_VERTEX_ATTRIB_ARRAY14_NV: - case GL_VERTEX_ATTRIB_ARRAY15_NV: - CHECK_EXTENSION(NV_vertex_program); - { - GLint n = (GLint) cap - GL_VERTEX_ATTRIB_ARRAY0_NV; - ASSERT(n < Elements(ctx->Array.ArrayObj->VertexAttrib)); - return (ctx->Array.ArrayObj->VertexAttrib[n].Enabled != 0); - } - case GL_MAP1_VERTEX_ATTRIB0_4_NV: - case GL_MAP1_VERTEX_ATTRIB1_4_NV: - case GL_MAP1_VERTEX_ATTRIB2_4_NV: - case GL_MAP1_VERTEX_ATTRIB3_4_NV: - case GL_MAP1_VERTEX_ATTRIB4_4_NV: - case GL_MAP1_VERTEX_ATTRIB5_4_NV: - case GL_MAP1_VERTEX_ATTRIB6_4_NV: - case GL_MAP1_VERTEX_ATTRIB7_4_NV: - case GL_MAP1_VERTEX_ATTRIB8_4_NV: - case GL_MAP1_VERTEX_ATTRIB9_4_NV: - case GL_MAP1_VERTEX_ATTRIB10_4_NV: - case GL_MAP1_VERTEX_ATTRIB11_4_NV: - case GL_MAP1_VERTEX_ATTRIB12_4_NV: - case GL_MAP1_VERTEX_ATTRIB13_4_NV: - case GL_MAP1_VERTEX_ATTRIB14_4_NV: - case GL_MAP1_VERTEX_ATTRIB15_4_NV: - CHECK_EXTENSION(NV_vertex_program); - { - const GLuint map = (GLuint) (cap - GL_MAP1_VERTEX_ATTRIB0_4_NV); - return ctx->Eval.Map1Attrib[map]; - } - case GL_MAP2_VERTEX_ATTRIB0_4_NV: - case GL_MAP2_VERTEX_ATTRIB1_4_NV: - case GL_MAP2_VERTEX_ATTRIB2_4_NV: - case GL_MAP2_VERTEX_ATTRIB3_4_NV: - case GL_MAP2_VERTEX_ATTRIB4_4_NV: - case GL_MAP2_VERTEX_ATTRIB5_4_NV: - case GL_MAP2_VERTEX_ATTRIB6_4_NV: - case GL_MAP2_VERTEX_ATTRIB7_4_NV: - case GL_MAP2_VERTEX_ATTRIB8_4_NV: - case GL_MAP2_VERTEX_ATTRIB9_4_NV: - case GL_MAP2_VERTEX_ATTRIB10_4_NV: - case GL_MAP2_VERTEX_ATTRIB11_4_NV: - case GL_MAP2_VERTEX_ATTRIB12_4_NV: - case GL_MAP2_VERTEX_ATTRIB13_4_NV: - case GL_MAP2_VERTEX_ATTRIB14_4_NV: - case GL_MAP2_VERTEX_ATTRIB15_4_NV: - CHECK_EXTENSION(NV_vertex_program); - { - const GLuint map = (GLuint) (cap - GL_MAP2_VERTEX_ATTRIB0_4_NV); - return ctx->Eval.Map2Attrib[map]; - } -#endif /* FEATURE_NV_vertex_program */ - -#if FEATURE_NV_fragment_program - case GL_FRAGMENT_PROGRAM_NV: - CHECK_EXTENSION(NV_fragment_program); - return ctx->FragmentProgram.Enabled; -#endif /* FEATURE_NV_fragment_program */ - - /* GL_NV_texture_rectangle */ - case GL_TEXTURE_RECTANGLE_NV: - CHECK_EXTENSION(NV_texture_rectangle); - return is_texture_enabled(ctx, TEXTURE_RECT_BIT); - - /* GL_EXT_stencil_two_side */ - case GL_STENCIL_TEST_TWO_SIDE_EXT: - CHECK_EXTENSION(EXT_stencil_two_side); - return ctx->Stencil.TestTwoSide; - -#if FEATURE_ARB_fragment_program - case GL_FRAGMENT_PROGRAM_ARB: - return ctx->FragmentProgram.Enabled; -#endif /* FEATURE_ARB_fragment_program */ - - /* GL_EXT_depth_bounds_test */ - case GL_DEPTH_BOUNDS_TEST_EXT: - CHECK_EXTENSION(EXT_depth_bounds_test); - return ctx->Depth.BoundsTest; - - /* GL_ARB_depth_clamp */ - case GL_DEPTH_CLAMP: - CHECK_EXTENSION(ARB_depth_clamp); - return ctx->Transform.DepthClamp; - -#if FEATURE_ATI_fragment_shader - case GL_FRAGMENT_SHADER_ATI: - CHECK_EXTENSION(ATI_fragment_shader); - return ctx->ATIFragmentShader.Enabled; -#endif /* FEATURE_ATI_fragment_shader */ - - case GL_TEXTURE_CUBE_MAP_SEAMLESS: - CHECK_EXTENSION(ARB_seamless_cube_map); - return ctx->Texture.CubeMapSeamless; - -#if FEATURE_EXT_transform_feedback - case GL_RASTERIZER_DISCARD: - CHECK_EXTENSION(EXT_transform_feedback); - return ctx->TransformFeedback.RasterDiscard; -#endif - - /* GL_NV_primitive_restart */ - case GL_PRIMITIVE_RESTART_NV: - if (!ctx->Extensions.NV_primitive_restart) { - goto invalid_enum_error; - } - return ctx->Array.PrimitiveRestart; - - /* GL 3.1 primitive restart */ - case GL_PRIMITIVE_RESTART: - if (ctx->VersionMajor * 10 + ctx->VersionMinor < 31) { - goto invalid_enum_error; - } - return ctx->Array.PrimitiveRestart; - - /* GL3.0 - GL_framebuffer_sRGB */ - case GL_FRAMEBUFFER_SRGB_EXT: - CHECK_EXTENSION(EXT_framebuffer_sRGB); - return ctx->Color.sRGBEnabled; - - default: - goto invalid_enum_error; - } - - return GL_FALSE; - -invalid_enum_error: - _mesa_error(ctx, GL_INVALID_ENUM, "glIsEnabled(0x%x)", (int) cap); - return GL_FALSE; -} +/** + * \file enable.c + * Enable/disable/query GL capabilities. + */ + +/* + * Mesa 3-D graphics library + * Version: 7.0.3 + * + * 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 "enable.h" +#include "light.h" +#include "simple_list.h" +#include "mfeatures.h" +#include "mtypes.h" +#include "enums.h" +#include "api_arrayelt.h" +#include "texstate.h" + + + +#define CHECK_EXTENSION(EXTNAME, CAP) \ + if (!ctx->Extensions.EXTNAME) { \ + goto invalid_enum_error; \ + } + + +/** + * Helper to enable/disable client-side state. + */ +static void +client_state(struct gl_context *ctx, GLenum cap, GLboolean state) +{ + struct gl_array_object *arrayObj = ctx->Array.ArrayObj; + GLuint flag; + GLboolean *var; + + switch (cap) { + case GL_VERTEX_ARRAY: + var = &arrayObj->Vertex.Enabled; + flag = _NEW_ARRAY_VERTEX; + break; + case GL_NORMAL_ARRAY: + var = &arrayObj->Normal.Enabled; + flag = _NEW_ARRAY_NORMAL; + break; + case GL_COLOR_ARRAY: + var = &arrayObj->Color.Enabled; + flag = _NEW_ARRAY_COLOR0; + break; + case GL_INDEX_ARRAY: + var = &arrayObj->Index.Enabled; + flag = _NEW_ARRAY_INDEX; + break; + case GL_TEXTURE_COORD_ARRAY: + var = &arrayObj->TexCoord[ctx->Array.ActiveTexture].Enabled; + flag = _NEW_ARRAY_TEXCOORD(ctx->Array.ActiveTexture); + break; + case GL_EDGE_FLAG_ARRAY: + var = &arrayObj->EdgeFlag.Enabled; + flag = _NEW_ARRAY_EDGEFLAG; + break; + case GL_FOG_COORDINATE_ARRAY_EXT: + var = &arrayObj->FogCoord.Enabled; + flag = _NEW_ARRAY_FOGCOORD; + break; + case GL_SECONDARY_COLOR_ARRAY_EXT: + var = &arrayObj->SecondaryColor.Enabled; + flag = _NEW_ARRAY_COLOR1; + break; + +#if FEATURE_point_size_array + case GL_POINT_SIZE_ARRAY_OES: + var = &arrayObj->PointSize.Enabled; + flag = _NEW_ARRAY_POINT_SIZE; + break; +#endif + +#if FEATURE_NV_vertex_program + case GL_VERTEX_ATTRIB_ARRAY0_NV: + case GL_VERTEX_ATTRIB_ARRAY1_NV: + case GL_VERTEX_ATTRIB_ARRAY2_NV: + case GL_VERTEX_ATTRIB_ARRAY3_NV: + case GL_VERTEX_ATTRIB_ARRAY4_NV: + case GL_VERTEX_ATTRIB_ARRAY5_NV: + case GL_VERTEX_ATTRIB_ARRAY6_NV: + case GL_VERTEX_ATTRIB_ARRAY7_NV: + case GL_VERTEX_ATTRIB_ARRAY8_NV: + case GL_VERTEX_ATTRIB_ARRAY9_NV: + case GL_VERTEX_ATTRIB_ARRAY10_NV: + case GL_VERTEX_ATTRIB_ARRAY11_NV: + case GL_VERTEX_ATTRIB_ARRAY12_NV: + case GL_VERTEX_ATTRIB_ARRAY13_NV: + case GL_VERTEX_ATTRIB_ARRAY14_NV: + case GL_VERTEX_ATTRIB_ARRAY15_NV: + CHECK_EXTENSION(NV_vertex_program, cap); + { + GLint n = (GLint) cap - GL_VERTEX_ATTRIB_ARRAY0_NV; + ASSERT(n < Elements(ctx->Array.ArrayObj->VertexAttrib)); + var = &arrayObj->VertexAttrib[n].Enabled; + flag = _NEW_ARRAY_ATTRIB(n); + } + break; +#endif /* FEATURE_NV_vertex_program */ + + /* GL_NV_primitive_restart */ + case GL_PRIMITIVE_RESTART_NV: + if (!ctx->Extensions.NV_primitive_restart) { + goto invalid_enum_error; + } + var = &ctx->Array.PrimitiveRestart; + flag = 0; + break; + + default: + goto invalid_enum_error; + } + + if (*var == state) + return; + + FLUSH_VERTICES(ctx, _NEW_ARRAY); + ctx->Array.NewState |= flag; + + _ae_invalidate_state(ctx, _NEW_ARRAY); + + *var = state; + + if (state) + ctx->Array.ArrayObj->_Enabled |= flag; + else + ctx->Array.ArrayObj->_Enabled &= ~flag; + + if (ctx->Driver.Enable) { + ctx->Driver.Enable( ctx, cap, state ); + } + + return; + +invalid_enum_error: + _mesa_error(ctx, GL_INVALID_ENUM, "gl%sClientState(0x%x)", + state ? "Enable" : "Disable", cap); +} + + +/** + * Enable GL capability. + * \param cap state to enable/disable. + * + * Get's the current context, assures that we're outside glBegin()/glEnd() and + * calls client_state(). + */ +void GLAPIENTRY +_mesa_EnableClientState( GLenum cap ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + client_state( ctx, cap, GL_TRUE ); +} + + +/** + * Disable GL capability. + * \param cap state to enable/disable. + * + * Get's the current context, assures that we're outside glBegin()/glEnd() and + * calls client_state(). + */ +void GLAPIENTRY +_mesa_DisableClientState( GLenum cap ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + client_state( ctx, cap, GL_FALSE ); +} + + +#undef CHECK_EXTENSION +#define CHECK_EXTENSION(EXTNAME, CAP) \ + if (!ctx->Extensions.EXTNAME) { \ + goto invalid_enum_error; \ + } + +#define CHECK_EXTENSION2(EXT1, EXT2, CAP) \ + if (!ctx->Extensions.EXT1 && !ctx->Extensions.EXT2) { \ + goto invalid_enum_error; \ + } + + + +/** + * Return pointer to current texture unit for setting/getting coordinate + * state. + * Note that we'll set GL_INVALID_OPERATION if the active texture unit is + * higher than the number of supported coordinate units. And we'll return NULL. + */ +static struct gl_texture_unit * +get_texcoord_unit(struct gl_context *ctx) +{ + if (ctx->Texture.CurrentUnit >= ctx->Const.MaxTextureCoordUnits) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glEnable/Disable(texcoord unit)"); + return NULL; + } + else { + return &ctx->Texture.Unit[ctx->Texture.CurrentUnit]; + } +} + + +/** + * Helper function to enable or disable a texture target. + * \param bit one of the TEXTURE_x_BIT values + * \return GL_TRUE if state is changing or GL_FALSE if no change + */ +static GLboolean +enable_texture(struct gl_context *ctx, GLboolean state, GLbitfield texBit) +{ + struct gl_texture_unit *texUnit = _mesa_get_current_tex_unit(ctx); + const GLbitfield newenabled = state + ? (texUnit->Enabled | texBit) : (texUnit->Enabled & ~texBit); + + if (texUnit->Enabled == newenabled) + return GL_FALSE; + + FLUSH_VERTICES(ctx, _NEW_TEXTURE); + texUnit->Enabled = newenabled; + return GL_TRUE; +} + + +/** + * Helper function to enable or disable state. + * + * \param ctx GL context. + * \param cap the state to enable/disable + * \param state whether to enable or disable the specified capability. + * + * Updates the current context and flushes the vertices as needed. For + * capabilities associated with extensions it verifies that those extensions + * are effectivly present before updating. Notifies the driver via + * dd_function_table::Enable. + */ +void +_mesa_set_enable(struct gl_context *ctx, GLenum cap, GLboolean state) +{ + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "%s %s (newstate is %x)\n", + state ? "glEnable" : "glDisable", + _mesa_lookup_enum_by_nr(cap), + ctx->NewState); + + switch (cap) { + case GL_ALPHA_TEST: + if (ctx->Color.AlphaEnabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_COLOR); + ctx->Color.AlphaEnabled = state; + break; + case GL_AUTO_NORMAL: + if (ctx->Eval.AutoNormal == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.AutoNormal = state; + break; + case GL_BLEND: + { + GLbitfield newEnabled = state * ((1 << ctx->Const.MaxDrawBuffers) - 1); + if (newEnabled != ctx->Color.BlendEnabled) { + FLUSH_VERTICES(ctx, _NEW_COLOR); + ctx->Color.BlendEnabled = newEnabled; + } + } + break; +#if FEATURE_userclip + case GL_CLIP_PLANE0: + case GL_CLIP_PLANE1: + case GL_CLIP_PLANE2: + case GL_CLIP_PLANE3: + case GL_CLIP_PLANE4: + case GL_CLIP_PLANE5: + { + const GLuint p = cap - GL_CLIP_PLANE0; + + if ((ctx->Transform.ClipPlanesEnabled & (1 << p)) == ((GLuint) state << p)) + return; + + FLUSH_VERTICES(ctx, _NEW_TRANSFORM); + + if (state) { + ctx->Transform.ClipPlanesEnabled |= (1 << p); + + if (_math_matrix_is_dirty(ctx->ProjectionMatrixStack.Top)) + _math_matrix_analyse( ctx->ProjectionMatrixStack.Top ); + + /* This derived state also calculated in clip.c and + * from _mesa_update_state() on changes to EyeUserPlane + * and ctx->ProjectionMatrix respectively. + */ + _mesa_transform_vector( ctx->Transform._ClipUserPlane[p], + ctx->Transform.EyeUserPlane[p], + ctx->ProjectionMatrixStack.Top->inv ); + } + else { + ctx->Transform.ClipPlanesEnabled &= ~(1 << p); + } + } + break; +#endif + case GL_COLOR_MATERIAL: + if (ctx->Light.ColorMaterialEnabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_LIGHT); + FLUSH_CURRENT(ctx, 0); + ctx->Light.ColorMaterialEnabled = state; + if (state) { + _mesa_update_color_material( ctx, + ctx->Current.Attrib[VERT_ATTRIB_COLOR0] ); + } + break; + case GL_CULL_FACE: + if (ctx->Polygon.CullFlag == state) + return; + FLUSH_VERTICES(ctx, _NEW_POLYGON); + ctx->Polygon.CullFlag = state; + break; + case GL_DEPTH_TEST: + if (ctx->Depth.Test == state) + return; + FLUSH_VERTICES(ctx, _NEW_DEPTH); + ctx->Depth.Test = state; + break; + case GL_DITHER: + if (ctx->NoDither) { + state = GL_FALSE; /* MESA_NO_DITHER env var */ + } + if (ctx->Color.DitherFlag == state) + return; + FLUSH_VERTICES(ctx, _NEW_COLOR); + ctx->Color.DitherFlag = state; + break; + case GL_FOG: + if (ctx->Fog.Enabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_FOG); + ctx->Fog.Enabled = state; + break; + case GL_LIGHT0: + case GL_LIGHT1: + case GL_LIGHT2: + case GL_LIGHT3: + case GL_LIGHT4: + case GL_LIGHT5: + case GL_LIGHT6: + case GL_LIGHT7: + if (ctx->Light.Light[cap-GL_LIGHT0].Enabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_LIGHT); + ctx->Light.Light[cap-GL_LIGHT0].Enabled = state; + if (state) { + insert_at_tail(&ctx->Light.EnabledList, + &ctx->Light.Light[cap-GL_LIGHT0]); + } + else { + remove_from_list(&ctx->Light.Light[cap-GL_LIGHT0]); + } + break; + case GL_LIGHTING: + if (ctx->Light.Enabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_LIGHT); + ctx->Light.Enabled = state; + if (ctx->Light.Enabled && ctx->Light.Model.TwoSide) + ctx->_TriangleCaps |= DD_TRI_LIGHT_TWOSIDE; + else + ctx->_TriangleCaps &= ~DD_TRI_LIGHT_TWOSIDE; + break; + case GL_LINE_SMOOTH: + if (ctx->Line.SmoothFlag == state) + return; + FLUSH_VERTICES(ctx, _NEW_LINE); + ctx->Line.SmoothFlag = state; + ctx->_TriangleCaps ^= DD_LINE_SMOOTH; + break; + case GL_LINE_STIPPLE: + if (ctx->Line.StippleFlag == state) + return; + FLUSH_VERTICES(ctx, _NEW_LINE); + ctx->Line.StippleFlag = state; + ctx->_TriangleCaps ^= DD_LINE_STIPPLE; + break; + case GL_INDEX_LOGIC_OP: + if (ctx->Color.IndexLogicOpEnabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_COLOR); + ctx->Color.IndexLogicOpEnabled = state; + break; + case GL_COLOR_LOGIC_OP: + if (ctx->Color.ColorLogicOpEnabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_COLOR); + ctx->Color.ColorLogicOpEnabled = state; + break; + case GL_MAP1_COLOR_4: + if (ctx->Eval.Map1Color4 == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map1Color4 = state; + break; + case GL_MAP1_INDEX: + if (ctx->Eval.Map1Index == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map1Index = state; + break; + case GL_MAP1_NORMAL: + if (ctx->Eval.Map1Normal == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map1Normal = state; + break; + case GL_MAP1_TEXTURE_COORD_1: + if (ctx->Eval.Map1TextureCoord1 == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map1TextureCoord1 = state; + break; + case GL_MAP1_TEXTURE_COORD_2: + if (ctx->Eval.Map1TextureCoord2 == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map1TextureCoord2 = state; + break; + case GL_MAP1_TEXTURE_COORD_3: + if (ctx->Eval.Map1TextureCoord3 == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map1TextureCoord3 = state; + break; + case GL_MAP1_TEXTURE_COORD_4: + if (ctx->Eval.Map1TextureCoord4 == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map1TextureCoord4 = state; + break; + case GL_MAP1_VERTEX_3: + if (ctx->Eval.Map1Vertex3 == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map1Vertex3 = state; + break; + case GL_MAP1_VERTEX_4: + if (ctx->Eval.Map1Vertex4 == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map1Vertex4 = state; + break; + case GL_MAP2_COLOR_4: + if (ctx->Eval.Map2Color4 == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map2Color4 = state; + break; + case GL_MAP2_INDEX: + if (ctx->Eval.Map2Index == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map2Index = state; + break; + case GL_MAP2_NORMAL: + if (ctx->Eval.Map2Normal == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map2Normal = state; + break; + case GL_MAP2_TEXTURE_COORD_1: + if (ctx->Eval.Map2TextureCoord1 == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map2TextureCoord1 = state; + break; + case GL_MAP2_TEXTURE_COORD_2: + if (ctx->Eval.Map2TextureCoord2 == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map2TextureCoord2 = state; + break; + case GL_MAP2_TEXTURE_COORD_3: + if (ctx->Eval.Map2TextureCoord3 == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map2TextureCoord3 = state; + break; + case GL_MAP2_TEXTURE_COORD_4: + if (ctx->Eval.Map2TextureCoord4 == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map2TextureCoord4 = state; + break; + case GL_MAP2_VERTEX_3: + if (ctx->Eval.Map2Vertex3 == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map2Vertex3 = state; + break; + case GL_MAP2_VERTEX_4: + if (ctx->Eval.Map2Vertex4 == state) + return; + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map2Vertex4 = state; + break; + case GL_NORMALIZE: + if (ctx->Transform.Normalize == state) + return; + FLUSH_VERTICES(ctx, _NEW_TRANSFORM); + ctx->Transform.Normalize = state; + break; + case GL_POINT_SMOOTH: + if (ctx->Point.SmoothFlag == state) + return; + FLUSH_VERTICES(ctx, _NEW_POINT); + ctx->Point.SmoothFlag = state; + ctx->_TriangleCaps ^= DD_POINT_SMOOTH; + break; + case GL_POLYGON_SMOOTH: + if (ctx->Polygon.SmoothFlag == state) + return; + FLUSH_VERTICES(ctx, _NEW_POLYGON); + ctx->Polygon.SmoothFlag = state; + ctx->_TriangleCaps ^= DD_TRI_SMOOTH; + break; + case GL_POLYGON_STIPPLE: + if (ctx->Polygon.StippleFlag == state) + return; + FLUSH_VERTICES(ctx, _NEW_POLYGON); + ctx->Polygon.StippleFlag = state; + ctx->_TriangleCaps ^= DD_TRI_STIPPLE; + break; + case GL_POLYGON_OFFSET_POINT: + if (ctx->Polygon.OffsetPoint == state) + return; + FLUSH_VERTICES(ctx, _NEW_POLYGON); + ctx->Polygon.OffsetPoint = state; + break; + case GL_POLYGON_OFFSET_LINE: + if (ctx->Polygon.OffsetLine == state) + return; + FLUSH_VERTICES(ctx, _NEW_POLYGON); + ctx->Polygon.OffsetLine = state; + break; + case GL_POLYGON_OFFSET_FILL: + /*case GL_POLYGON_OFFSET_EXT:*/ + if (ctx->Polygon.OffsetFill == state) + return; + FLUSH_VERTICES(ctx, _NEW_POLYGON); + ctx->Polygon.OffsetFill = state; + break; + case GL_RESCALE_NORMAL_EXT: + if (ctx->Transform.RescaleNormals == state) + return; + FLUSH_VERTICES(ctx, _NEW_TRANSFORM); + ctx->Transform.RescaleNormals = state; + break; + case GL_SCISSOR_TEST: + if (ctx->Scissor.Enabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_SCISSOR); + ctx->Scissor.Enabled = state; + break; + case GL_SHARED_TEXTURE_PALETTE_EXT: + if (ctx->Texture.SharedPalette == state) + return; + FLUSH_VERTICES(ctx, _NEW_TEXTURE); + ctx->Texture.SharedPalette = state; + break; + case GL_STENCIL_TEST: + if (ctx->Stencil.Enabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.Enabled = state; + break; + case GL_TEXTURE_1D: + if (!enable_texture(ctx, state, TEXTURE_1D_BIT)) { + return; + } + break; + case GL_TEXTURE_2D: + if (!enable_texture(ctx, state, TEXTURE_2D_BIT)) { + return; + } + break; + case GL_TEXTURE_3D: + if (!enable_texture(ctx, state, TEXTURE_3D_BIT)) { + return; + } + break; + case GL_TEXTURE_GEN_Q: + { + struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); + if (texUnit) { + GLuint newenabled = texUnit->TexGenEnabled & ~Q_BIT; + if (state) + newenabled |= Q_BIT; + if (texUnit->TexGenEnabled == newenabled) + return; + FLUSH_VERTICES(ctx, _NEW_TEXTURE); + texUnit->TexGenEnabled = newenabled; + } + } + break; + case GL_TEXTURE_GEN_R: + { + struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); + if (texUnit) { + GLuint newenabled = texUnit->TexGenEnabled & ~R_BIT; + if (state) + newenabled |= R_BIT; + if (texUnit->TexGenEnabled == newenabled) + return; + FLUSH_VERTICES(ctx, _NEW_TEXTURE); + texUnit->TexGenEnabled = newenabled; + } + } + break; + case GL_TEXTURE_GEN_S: + { + struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); + if (texUnit) { + GLuint newenabled = texUnit->TexGenEnabled & ~S_BIT; + if (state) + newenabled |= S_BIT; + if (texUnit->TexGenEnabled == newenabled) + return; + FLUSH_VERTICES(ctx, _NEW_TEXTURE); + texUnit->TexGenEnabled = newenabled; + } + } + break; + case GL_TEXTURE_GEN_T: + { + struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); + if (texUnit) { + GLuint newenabled = texUnit->TexGenEnabled & ~T_BIT; + if (state) + newenabled |= T_BIT; + if (texUnit->TexGenEnabled == newenabled) + return; + FLUSH_VERTICES(ctx, _NEW_TEXTURE); + texUnit->TexGenEnabled = newenabled; + } + } + break; + +#if FEATURE_ES1 + case GL_TEXTURE_GEN_STR_OES: + /* disable S, T, and R at the same time */ + { + struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); + if (texUnit) { + GLuint newenabled = + texUnit->TexGenEnabled & ~STR_BITS; + if (state) + newenabled |= STR_BITS; + if (texUnit->TexGenEnabled == newenabled) + return; + FLUSH_VERTICES(ctx, _NEW_TEXTURE); + texUnit->TexGenEnabled = newenabled; + } + } + break; +#endif + + /* + * CLIENT STATE!!! + */ + case GL_VERTEX_ARRAY: + case GL_NORMAL_ARRAY: + case GL_COLOR_ARRAY: + case GL_INDEX_ARRAY: + case GL_TEXTURE_COORD_ARRAY: + case GL_EDGE_FLAG_ARRAY: + case GL_FOG_COORDINATE_ARRAY_EXT: + case GL_SECONDARY_COLOR_ARRAY_EXT: + case GL_POINT_SIZE_ARRAY_OES: + client_state( ctx, cap, state ); + return; + + /* GL_SGI_texture_color_table */ + case GL_TEXTURE_COLOR_TABLE_SGI: + CHECK_EXTENSION(SGI_texture_color_table, cap); + if (ctx->Texture.Unit[ctx->Texture.CurrentUnit].ColorTableEnabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_TEXTURE); + ctx->Texture.Unit[ctx->Texture.CurrentUnit].ColorTableEnabled = state; + break; + + /* GL_ARB_texture_cube_map */ + case GL_TEXTURE_CUBE_MAP_ARB: + CHECK_EXTENSION(ARB_texture_cube_map, cap); + if (!enable_texture(ctx, state, TEXTURE_CUBE_BIT)) { + return; + } + break; + + /* GL_EXT_secondary_color */ + case GL_COLOR_SUM_EXT: + CHECK_EXTENSION2(EXT_secondary_color, ARB_vertex_program, cap); + if (ctx->Fog.ColorSumEnabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_FOG); + ctx->Fog.ColorSumEnabled = state; + break; + + /* GL_ARB_multisample */ + case GL_MULTISAMPLE_ARB: + if (ctx->Multisample.Enabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_MULTISAMPLE); + ctx->Multisample.Enabled = state; + break; + case GL_SAMPLE_ALPHA_TO_COVERAGE_ARB: + if (ctx->Multisample.SampleAlphaToCoverage == state) + return; + FLUSH_VERTICES(ctx, _NEW_MULTISAMPLE); + ctx->Multisample.SampleAlphaToCoverage = state; + break; + case GL_SAMPLE_ALPHA_TO_ONE_ARB: + if (ctx->Multisample.SampleAlphaToOne == state) + return; + FLUSH_VERTICES(ctx, _NEW_MULTISAMPLE); + ctx->Multisample.SampleAlphaToOne = state; + break; + case GL_SAMPLE_COVERAGE_ARB: + if (ctx->Multisample.SampleCoverage == state) + return; + FLUSH_VERTICES(ctx, _NEW_MULTISAMPLE); + ctx->Multisample.SampleCoverage = state; + break; + case GL_SAMPLE_COVERAGE_INVERT_ARB: + if (ctx->Multisample.SampleCoverageInvert == state) + return; + FLUSH_VERTICES(ctx, _NEW_MULTISAMPLE); + ctx->Multisample.SampleCoverageInvert = state; + break; + + /* GL_IBM_rasterpos_clip */ + case GL_RASTER_POSITION_UNCLIPPED_IBM: + CHECK_EXTENSION(IBM_rasterpos_clip, cap); + if (ctx->Transform.RasterPositionUnclipped == state) + return; + FLUSH_VERTICES(ctx, _NEW_TRANSFORM); + ctx->Transform.RasterPositionUnclipped = state; + break; + + /* GL_NV_point_sprite */ + case GL_POINT_SPRITE_NV: + CHECK_EXTENSION2(NV_point_sprite, ARB_point_sprite, cap); + if (ctx->Point.PointSprite == state) + return; + FLUSH_VERTICES(ctx, _NEW_POINT); + ctx->Point.PointSprite = state; + break; + +#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program + case GL_VERTEX_PROGRAM_ARB: + CHECK_EXTENSION2(ARB_vertex_program, NV_vertex_program, cap); + if (ctx->VertexProgram.Enabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_PROGRAM); + ctx->VertexProgram.Enabled = state; + break; + case GL_VERTEX_PROGRAM_POINT_SIZE_ARB: + CHECK_EXTENSION2(ARB_vertex_program, NV_vertex_program, cap); + if (ctx->VertexProgram.PointSizeEnabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_PROGRAM); + ctx->VertexProgram.PointSizeEnabled = state; + break; + case GL_VERTEX_PROGRAM_TWO_SIDE_ARB: + CHECK_EXTENSION2(ARB_vertex_program, NV_vertex_program, cap); + if (ctx->VertexProgram.TwoSideEnabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_PROGRAM); + ctx->VertexProgram.TwoSideEnabled = state; + break; +#endif +#if FEATURE_NV_vertex_program + case GL_MAP1_VERTEX_ATTRIB0_4_NV: + case GL_MAP1_VERTEX_ATTRIB1_4_NV: + case GL_MAP1_VERTEX_ATTRIB2_4_NV: + case GL_MAP1_VERTEX_ATTRIB3_4_NV: + case GL_MAP1_VERTEX_ATTRIB4_4_NV: + case GL_MAP1_VERTEX_ATTRIB5_4_NV: + case GL_MAP1_VERTEX_ATTRIB6_4_NV: + case GL_MAP1_VERTEX_ATTRIB7_4_NV: + case GL_MAP1_VERTEX_ATTRIB8_4_NV: + case GL_MAP1_VERTEX_ATTRIB9_4_NV: + case GL_MAP1_VERTEX_ATTRIB10_4_NV: + case GL_MAP1_VERTEX_ATTRIB11_4_NV: + case GL_MAP1_VERTEX_ATTRIB12_4_NV: + case GL_MAP1_VERTEX_ATTRIB13_4_NV: + case GL_MAP1_VERTEX_ATTRIB14_4_NV: + case GL_MAP1_VERTEX_ATTRIB15_4_NV: + CHECK_EXTENSION(NV_vertex_program, cap); + { + const GLuint map = (GLuint) (cap - GL_MAP1_VERTEX_ATTRIB0_4_NV); + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map1Attrib[map] = state; + } + break; + case GL_MAP2_VERTEX_ATTRIB0_4_NV: + case GL_MAP2_VERTEX_ATTRIB1_4_NV: + case GL_MAP2_VERTEX_ATTRIB2_4_NV: + case GL_MAP2_VERTEX_ATTRIB3_4_NV: + case GL_MAP2_VERTEX_ATTRIB4_4_NV: + case GL_MAP2_VERTEX_ATTRIB5_4_NV: + case GL_MAP2_VERTEX_ATTRIB6_4_NV: + case GL_MAP2_VERTEX_ATTRIB7_4_NV: + case GL_MAP2_VERTEX_ATTRIB8_4_NV: + case GL_MAP2_VERTEX_ATTRIB9_4_NV: + case GL_MAP2_VERTEX_ATTRIB10_4_NV: + case GL_MAP2_VERTEX_ATTRIB11_4_NV: + case GL_MAP2_VERTEX_ATTRIB12_4_NV: + case GL_MAP2_VERTEX_ATTRIB13_4_NV: + case GL_MAP2_VERTEX_ATTRIB14_4_NV: + case GL_MAP2_VERTEX_ATTRIB15_4_NV: + CHECK_EXTENSION(NV_vertex_program, cap); + { + const GLuint map = (GLuint) (cap - GL_MAP2_VERTEX_ATTRIB0_4_NV); + FLUSH_VERTICES(ctx, _NEW_EVAL); + ctx->Eval.Map2Attrib[map] = state; + } + break; +#endif /* FEATURE_NV_vertex_program */ + +#if FEATURE_NV_fragment_program + case GL_FRAGMENT_PROGRAM_NV: + CHECK_EXTENSION(NV_fragment_program, cap); + if (ctx->FragmentProgram.Enabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_PROGRAM); + ctx->FragmentProgram.Enabled = state; + break; +#endif /* FEATURE_NV_fragment_program */ + + /* GL_NV_texture_rectangle */ + case GL_TEXTURE_RECTANGLE_NV: + CHECK_EXTENSION(NV_texture_rectangle, cap); + if (!enable_texture(ctx, state, TEXTURE_RECT_BIT)) { + return; + } + break; + + /* GL_EXT_stencil_two_side */ + case GL_STENCIL_TEST_TWO_SIDE_EXT: + CHECK_EXTENSION(EXT_stencil_two_side, cap); + if (ctx->Stencil.TestTwoSide == state) + return; + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.TestTwoSide = state; + if (state) { + ctx->Stencil._BackFace = 2; + ctx->_TriangleCaps |= DD_TRI_TWOSTENCIL; + } else { + ctx->Stencil._BackFace = 1; + ctx->_TriangleCaps &= ~DD_TRI_TWOSTENCIL; + } + break; + +#if FEATURE_ARB_fragment_program + case GL_FRAGMENT_PROGRAM_ARB: + CHECK_EXTENSION(ARB_fragment_program, cap); + if (ctx->FragmentProgram.Enabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_PROGRAM); + ctx->FragmentProgram.Enabled = state; + break; +#endif /* FEATURE_ARB_fragment_program */ + + /* GL_EXT_depth_bounds_test */ + case GL_DEPTH_BOUNDS_TEST_EXT: + CHECK_EXTENSION(EXT_depth_bounds_test, cap); + if (ctx->Depth.BoundsTest == state) + return; + FLUSH_VERTICES(ctx, _NEW_DEPTH); + ctx->Depth.BoundsTest = state; + break; + + case GL_DEPTH_CLAMP: + if (ctx->Transform.DepthClamp == state) + return; + CHECK_EXTENSION(ARB_depth_clamp, cap); + FLUSH_VERTICES(ctx, _NEW_TRANSFORM); + ctx->Transform.DepthClamp = state; + break; + +#if FEATURE_ATI_fragment_shader + case GL_FRAGMENT_SHADER_ATI: + CHECK_EXTENSION(ATI_fragment_shader, cap); + if (ctx->ATIFragmentShader.Enabled == state) + return; + FLUSH_VERTICES(ctx, _NEW_PROGRAM); + ctx->ATIFragmentShader.Enabled = state; + break; +#endif + + /* GL_MESA_texture_array */ + case GL_TEXTURE_1D_ARRAY_EXT: + CHECK_EXTENSION(MESA_texture_array, cap); + if (!enable_texture(ctx, state, TEXTURE_1D_ARRAY_BIT)) { + return; + } + break; + + case GL_TEXTURE_2D_ARRAY_EXT: + CHECK_EXTENSION(MESA_texture_array, cap); + if (!enable_texture(ctx, state, TEXTURE_2D_ARRAY_BIT)) { + return; + } + break; + + case GL_TEXTURE_CUBE_MAP_SEAMLESS: + CHECK_EXTENSION(ARB_seamless_cube_map, cap); + ctx->Texture.CubeMapSeamless = state; + break; + +#if FEATURE_EXT_transform_feedback + case GL_RASTERIZER_DISCARD: + CHECK_EXTENSION(EXT_transform_feedback, cap); + if (ctx->TransformFeedback.RasterDiscard != state) { + ctx->TransformFeedback.RasterDiscard = state; + FLUSH_VERTICES(ctx, _NEW_TRANSFORM); + } + break; +#endif + + /* GL 3.1 primitive restart. Note: this enum is different from + * GL_PRIMITIVE_RESTART_NV (which is client state). + */ + case GL_PRIMITIVE_RESTART: + if (ctx->VersionMajor * 10 + ctx->VersionMinor < 31) { + goto invalid_enum_error; + } + if (ctx->Array.PrimitiveRestart != state) { + FLUSH_VERTICES(ctx, _NEW_TRANSFORM); + ctx->Array.PrimitiveRestart = state; + } + break; + + /* GL3.0 - GL_framebuffer_sRGB */ + case GL_FRAMEBUFFER_SRGB_EXT: + CHECK_EXTENSION(EXT_framebuffer_sRGB, cap); + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + ctx->Color.sRGBEnabled = state; + break; + + default: + goto invalid_enum_error; + } + + if (ctx->Driver.Enable) { + ctx->Driver.Enable( ctx, cap, state ); + } + + return; + +invalid_enum_error: + _mesa_error(ctx, GL_INVALID_ENUM, "gl%s(0x%x)", + state ? "Enable" : "Disable", cap); +} + + +/** + * Enable GL capability. Called by glEnable() + * \param cap state to enable. + */ +void GLAPIENTRY +_mesa_Enable( GLenum cap ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + _mesa_set_enable( ctx, cap, GL_TRUE ); +} + + +/** + * Disable GL capability. Called by glDisable() + * \param cap state to disable. + */ +void GLAPIENTRY +_mesa_Disable( GLenum cap ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + _mesa_set_enable( ctx, cap, GL_FALSE ); +} + + + +/** + * Enable/disable an indexed state var. + */ +void +_mesa_set_enablei(struct gl_context *ctx, GLenum cap, GLuint index, GLboolean state) +{ + ASSERT(state == 0 || state == 1); + switch (cap) { + case GL_BLEND: + if (!ctx->Extensions.EXT_draw_buffers2) { + goto invalid_enum_error; + } + if (index >= ctx->Const.MaxDrawBuffers) { + _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%u)", + state ? "glEnableIndexed" : "glDisableIndexed", index); + return; + } + if (((ctx->Color.BlendEnabled >> index) & 1) != state) { + FLUSH_VERTICES(ctx, _NEW_COLOR); + if (state) + ctx->Color.BlendEnabled |= (1 << index); + else + ctx->Color.BlendEnabled &= ~(1 << index); + } + break; + default: + goto invalid_enum_error; + } + return; + +invalid_enum_error: + _mesa_error(ctx, GL_INVALID_ENUM, "%s(cap=%s)", + state ? "glEnablei" : "glDisablei", + _mesa_lookup_enum_by_nr(cap)); +} + + +void GLAPIENTRY +_mesa_DisableIndexed( GLenum cap, GLuint index ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + _mesa_set_enablei(ctx, cap, index, GL_FALSE); +} + + +void GLAPIENTRY +_mesa_EnableIndexed( GLenum cap, GLuint index ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + _mesa_set_enablei(ctx, cap, index, GL_TRUE); +} + + +GLboolean GLAPIENTRY +_mesa_IsEnabledIndexed( GLenum cap, GLuint index ) +{ + GET_CURRENT_CONTEXT(ctx); + switch (cap) { + case GL_BLEND: + if (index >= ctx->Const.MaxDrawBuffers) { + _mesa_error(ctx, GL_INVALID_VALUE, "glIsEnabledIndexed(index=%u)", + index); + return GL_FALSE; + } + return (ctx->Color.BlendEnabled >> index) & 1; + default: + _mesa_error(ctx, GL_INVALID_ENUM, "glIsEnabledIndexed(cap=%s)", + _mesa_lookup_enum_by_nr(cap)); + return GL_FALSE; + } +} + + + + +#undef CHECK_EXTENSION +#define CHECK_EXTENSION(EXTNAME) \ + if (!ctx->Extensions.EXTNAME) { \ + goto invalid_enum_error; \ + } + +#undef CHECK_EXTENSION2 +#define CHECK_EXTENSION2(EXT1, EXT2) \ + if (!ctx->Extensions.EXT1 && !ctx->Extensions.EXT2) { \ + goto invalid_enum_error; \ + } + + +/** + * Helper function to determine whether a texture target is enabled. + */ +static GLboolean +is_texture_enabled(struct gl_context *ctx, GLbitfield bit) +{ + const struct gl_texture_unit *const texUnit = + &ctx->Texture.Unit[ctx->Texture.CurrentUnit]; + return (texUnit->Enabled & bit) ? GL_TRUE : GL_FALSE; +} + + +/** + * Return simple enable/disable state. + * + * \param cap state variable to query. + * + * Returns the state of the specified capability from the current GL context. + * For the capabilities associated with extensions verifies that those + * extensions are effectively present before reporting. + */ +GLboolean GLAPIENTRY +_mesa_IsEnabled( GLenum cap ) +{ + GET_CURRENT_CONTEXT(ctx); + switch (cap) { + case GL_ALPHA_TEST: + return ctx->Color.AlphaEnabled; + case GL_AUTO_NORMAL: + return ctx->Eval.AutoNormal; + case GL_BLEND: + return ctx->Color.BlendEnabled & 1; /* return state for buffer[0] */ + case GL_CLIP_PLANE0: + case GL_CLIP_PLANE1: + case GL_CLIP_PLANE2: + case GL_CLIP_PLANE3: + case GL_CLIP_PLANE4: + case GL_CLIP_PLANE5: + return (ctx->Transform.ClipPlanesEnabled >> (cap - GL_CLIP_PLANE0)) & 1; + case GL_COLOR_MATERIAL: + return ctx->Light.ColorMaterialEnabled; + case GL_CULL_FACE: + return ctx->Polygon.CullFlag; + case GL_DEPTH_TEST: + return ctx->Depth.Test; + case GL_DITHER: + return ctx->Color.DitherFlag; + case GL_FOG: + return ctx->Fog.Enabled; + case GL_LIGHTING: + return ctx->Light.Enabled; + case GL_LIGHT0: + case GL_LIGHT1: + case GL_LIGHT2: + case GL_LIGHT3: + case GL_LIGHT4: + case GL_LIGHT5: + case GL_LIGHT6: + case GL_LIGHT7: + return ctx->Light.Light[cap-GL_LIGHT0].Enabled; + case GL_LINE_SMOOTH: + return ctx->Line.SmoothFlag; + case GL_LINE_STIPPLE: + return ctx->Line.StippleFlag; + case GL_INDEX_LOGIC_OP: + return ctx->Color.IndexLogicOpEnabled; + case GL_COLOR_LOGIC_OP: + return ctx->Color.ColorLogicOpEnabled; + case GL_MAP1_COLOR_4: + return ctx->Eval.Map1Color4; + case GL_MAP1_INDEX: + return ctx->Eval.Map1Index; + case GL_MAP1_NORMAL: + return ctx->Eval.Map1Normal; + case GL_MAP1_TEXTURE_COORD_1: + return ctx->Eval.Map1TextureCoord1; + case GL_MAP1_TEXTURE_COORD_2: + return ctx->Eval.Map1TextureCoord2; + case GL_MAP1_TEXTURE_COORD_3: + return ctx->Eval.Map1TextureCoord3; + case GL_MAP1_TEXTURE_COORD_4: + return ctx->Eval.Map1TextureCoord4; + case GL_MAP1_VERTEX_3: + return ctx->Eval.Map1Vertex3; + case GL_MAP1_VERTEX_4: + return ctx->Eval.Map1Vertex4; + case GL_MAP2_COLOR_4: + return ctx->Eval.Map2Color4; + case GL_MAP2_INDEX: + return ctx->Eval.Map2Index; + case GL_MAP2_NORMAL: + return ctx->Eval.Map2Normal; + case GL_MAP2_TEXTURE_COORD_1: + return ctx->Eval.Map2TextureCoord1; + case GL_MAP2_TEXTURE_COORD_2: + return ctx->Eval.Map2TextureCoord2; + case GL_MAP2_TEXTURE_COORD_3: + return ctx->Eval.Map2TextureCoord3; + case GL_MAP2_TEXTURE_COORD_4: + return ctx->Eval.Map2TextureCoord4; + case GL_MAP2_VERTEX_3: + return ctx->Eval.Map2Vertex3; + case GL_MAP2_VERTEX_4: + return ctx->Eval.Map2Vertex4; + case GL_NORMALIZE: + return ctx->Transform.Normalize; + case GL_POINT_SMOOTH: + return ctx->Point.SmoothFlag; + case GL_POLYGON_SMOOTH: + return ctx->Polygon.SmoothFlag; + case GL_POLYGON_STIPPLE: + return ctx->Polygon.StippleFlag; + case GL_POLYGON_OFFSET_POINT: + return ctx->Polygon.OffsetPoint; + case GL_POLYGON_OFFSET_LINE: + return ctx->Polygon.OffsetLine; + case GL_POLYGON_OFFSET_FILL: + /*case GL_POLYGON_OFFSET_EXT:*/ + return ctx->Polygon.OffsetFill; + case GL_RESCALE_NORMAL_EXT: + return ctx->Transform.RescaleNormals; + case GL_SCISSOR_TEST: + return ctx->Scissor.Enabled; + case GL_SHARED_TEXTURE_PALETTE_EXT: + return ctx->Texture.SharedPalette; + case GL_STENCIL_TEST: + return ctx->Stencil.Enabled; + case GL_TEXTURE_1D: + return is_texture_enabled(ctx, TEXTURE_1D_BIT); + case GL_TEXTURE_2D: + return is_texture_enabled(ctx, TEXTURE_2D_BIT); + case GL_TEXTURE_3D: + return is_texture_enabled(ctx, TEXTURE_3D_BIT); + case GL_TEXTURE_GEN_Q: + { + const struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); + if (texUnit) { + return (texUnit->TexGenEnabled & Q_BIT) ? GL_TRUE : GL_FALSE; + } + } + return GL_FALSE; + case GL_TEXTURE_GEN_R: + { + const struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); + if (texUnit) { + return (texUnit->TexGenEnabled & R_BIT) ? GL_TRUE : GL_FALSE; + } + } + return GL_FALSE; + case GL_TEXTURE_GEN_S: + { + const struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); + if (texUnit) { + return (texUnit->TexGenEnabled & S_BIT) ? GL_TRUE : GL_FALSE; + } + } + return GL_FALSE; + case GL_TEXTURE_GEN_T: + { + const struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); + if (texUnit) { + return (texUnit->TexGenEnabled & T_BIT) ? GL_TRUE : GL_FALSE; + } + } + return GL_FALSE; +#if FEATURE_ES1 + case GL_TEXTURE_GEN_STR_OES: + { + const struct gl_texture_unit *texUnit = get_texcoord_unit(ctx); + if (texUnit) { + return (texUnit->TexGenEnabled & STR_BITS) == STR_BITS ? GL_TRUE : GL_FALSE; + } + } +#endif + + /* + * CLIENT STATE!!! + */ + case GL_VERTEX_ARRAY: + return (ctx->Array.ArrayObj->Vertex.Enabled != 0); + case GL_NORMAL_ARRAY: + return (ctx->Array.ArrayObj->Normal.Enabled != 0); + case GL_COLOR_ARRAY: + return (ctx->Array.ArrayObj->Color.Enabled != 0); + case GL_INDEX_ARRAY: + return (ctx->Array.ArrayObj->Index.Enabled != 0); + case GL_TEXTURE_COORD_ARRAY: + return (ctx->Array.ArrayObj->TexCoord[ctx->Array.ActiveTexture].Enabled != 0); + case GL_EDGE_FLAG_ARRAY: + return (ctx->Array.ArrayObj->EdgeFlag.Enabled != 0); + case GL_FOG_COORDINATE_ARRAY_EXT: + CHECK_EXTENSION(EXT_fog_coord); + return (ctx->Array.ArrayObj->FogCoord.Enabled != 0); + case GL_SECONDARY_COLOR_ARRAY_EXT: + CHECK_EXTENSION(EXT_secondary_color); + return (ctx->Array.ArrayObj->SecondaryColor.Enabled != 0); +#if FEATURE_point_size_array + case GL_POINT_SIZE_ARRAY_OES: + return (ctx->Array.ArrayObj->PointSize.Enabled != 0); +#endif + + /* GL_SGI_texture_color_table */ + case GL_TEXTURE_COLOR_TABLE_SGI: + CHECK_EXTENSION(SGI_texture_color_table); + return ctx->Texture.Unit[ctx->Texture.CurrentUnit].ColorTableEnabled; + + /* GL_ARB_texture_cube_map */ + case GL_TEXTURE_CUBE_MAP_ARB: + CHECK_EXTENSION(ARB_texture_cube_map); + return is_texture_enabled(ctx, TEXTURE_CUBE_BIT); + + /* GL_EXT_secondary_color */ + case GL_COLOR_SUM_EXT: + CHECK_EXTENSION2(EXT_secondary_color, ARB_vertex_program); + return ctx->Fog.ColorSumEnabled; + + /* GL_ARB_multisample */ + case GL_MULTISAMPLE_ARB: + return ctx->Multisample.Enabled; + case GL_SAMPLE_ALPHA_TO_COVERAGE_ARB: + return ctx->Multisample.SampleAlphaToCoverage; + case GL_SAMPLE_ALPHA_TO_ONE_ARB: + return ctx->Multisample.SampleAlphaToOne; + case GL_SAMPLE_COVERAGE_ARB: + return ctx->Multisample.SampleCoverage; + case GL_SAMPLE_COVERAGE_INVERT_ARB: + return ctx->Multisample.SampleCoverageInvert; + + /* GL_IBM_rasterpos_clip */ + case GL_RASTER_POSITION_UNCLIPPED_IBM: + CHECK_EXTENSION(IBM_rasterpos_clip); + return ctx->Transform.RasterPositionUnclipped; + + /* GL_NV_point_sprite */ + case GL_POINT_SPRITE_NV: + CHECK_EXTENSION2(NV_point_sprite, ARB_point_sprite) + return ctx->Point.PointSprite; + +#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program + case GL_VERTEX_PROGRAM_ARB: + CHECK_EXTENSION2(ARB_vertex_program, NV_vertex_program); + return ctx->VertexProgram.Enabled; + case GL_VERTEX_PROGRAM_POINT_SIZE_ARB: + CHECK_EXTENSION2(ARB_vertex_program, NV_vertex_program); + return ctx->VertexProgram.PointSizeEnabled; + case GL_VERTEX_PROGRAM_TWO_SIDE_ARB: + CHECK_EXTENSION2(ARB_vertex_program, NV_vertex_program); + return ctx->VertexProgram.TwoSideEnabled; +#endif +#if FEATURE_NV_vertex_program + case GL_VERTEX_ATTRIB_ARRAY0_NV: + case GL_VERTEX_ATTRIB_ARRAY1_NV: + case GL_VERTEX_ATTRIB_ARRAY2_NV: + case GL_VERTEX_ATTRIB_ARRAY3_NV: + case GL_VERTEX_ATTRIB_ARRAY4_NV: + case GL_VERTEX_ATTRIB_ARRAY5_NV: + case GL_VERTEX_ATTRIB_ARRAY6_NV: + case GL_VERTEX_ATTRIB_ARRAY7_NV: + case GL_VERTEX_ATTRIB_ARRAY8_NV: + case GL_VERTEX_ATTRIB_ARRAY9_NV: + case GL_VERTEX_ATTRIB_ARRAY10_NV: + case GL_VERTEX_ATTRIB_ARRAY11_NV: + case GL_VERTEX_ATTRIB_ARRAY12_NV: + case GL_VERTEX_ATTRIB_ARRAY13_NV: + case GL_VERTEX_ATTRIB_ARRAY14_NV: + case GL_VERTEX_ATTRIB_ARRAY15_NV: + CHECK_EXTENSION(NV_vertex_program); + { + GLint n = (GLint) cap - GL_VERTEX_ATTRIB_ARRAY0_NV; + ASSERT(n < Elements(ctx->Array.ArrayObj->VertexAttrib)); + return (ctx->Array.ArrayObj->VertexAttrib[n].Enabled != 0); + } + case GL_MAP1_VERTEX_ATTRIB0_4_NV: + case GL_MAP1_VERTEX_ATTRIB1_4_NV: + case GL_MAP1_VERTEX_ATTRIB2_4_NV: + case GL_MAP1_VERTEX_ATTRIB3_4_NV: + case GL_MAP1_VERTEX_ATTRIB4_4_NV: + case GL_MAP1_VERTEX_ATTRIB5_4_NV: + case GL_MAP1_VERTEX_ATTRIB6_4_NV: + case GL_MAP1_VERTEX_ATTRIB7_4_NV: + case GL_MAP1_VERTEX_ATTRIB8_4_NV: + case GL_MAP1_VERTEX_ATTRIB9_4_NV: + case GL_MAP1_VERTEX_ATTRIB10_4_NV: + case GL_MAP1_VERTEX_ATTRIB11_4_NV: + case GL_MAP1_VERTEX_ATTRIB12_4_NV: + case GL_MAP1_VERTEX_ATTRIB13_4_NV: + case GL_MAP1_VERTEX_ATTRIB14_4_NV: + case GL_MAP1_VERTEX_ATTRIB15_4_NV: + CHECK_EXTENSION(NV_vertex_program); + { + const GLuint map = (GLuint) (cap - GL_MAP1_VERTEX_ATTRIB0_4_NV); + return ctx->Eval.Map1Attrib[map]; + } + case GL_MAP2_VERTEX_ATTRIB0_4_NV: + case GL_MAP2_VERTEX_ATTRIB1_4_NV: + case GL_MAP2_VERTEX_ATTRIB2_4_NV: + case GL_MAP2_VERTEX_ATTRIB3_4_NV: + case GL_MAP2_VERTEX_ATTRIB4_4_NV: + case GL_MAP2_VERTEX_ATTRIB5_4_NV: + case GL_MAP2_VERTEX_ATTRIB6_4_NV: + case GL_MAP2_VERTEX_ATTRIB7_4_NV: + case GL_MAP2_VERTEX_ATTRIB8_4_NV: + case GL_MAP2_VERTEX_ATTRIB9_4_NV: + case GL_MAP2_VERTEX_ATTRIB10_4_NV: + case GL_MAP2_VERTEX_ATTRIB11_4_NV: + case GL_MAP2_VERTEX_ATTRIB12_4_NV: + case GL_MAP2_VERTEX_ATTRIB13_4_NV: + case GL_MAP2_VERTEX_ATTRIB14_4_NV: + case GL_MAP2_VERTEX_ATTRIB15_4_NV: + CHECK_EXTENSION(NV_vertex_program); + { + const GLuint map = (GLuint) (cap - GL_MAP2_VERTEX_ATTRIB0_4_NV); + return ctx->Eval.Map2Attrib[map]; + } +#endif /* FEATURE_NV_vertex_program */ + +#if FEATURE_NV_fragment_program + case GL_FRAGMENT_PROGRAM_NV: + CHECK_EXTENSION(NV_fragment_program); + return ctx->FragmentProgram.Enabled; +#endif /* FEATURE_NV_fragment_program */ + + /* GL_NV_texture_rectangle */ + case GL_TEXTURE_RECTANGLE_NV: + CHECK_EXTENSION(NV_texture_rectangle); + return is_texture_enabled(ctx, TEXTURE_RECT_BIT); + + /* GL_EXT_stencil_two_side */ + case GL_STENCIL_TEST_TWO_SIDE_EXT: + CHECK_EXTENSION(EXT_stencil_two_side); + return ctx->Stencil.TestTwoSide; + +#if FEATURE_ARB_fragment_program + case GL_FRAGMENT_PROGRAM_ARB: + return ctx->FragmentProgram.Enabled; +#endif /* FEATURE_ARB_fragment_program */ + + /* GL_EXT_depth_bounds_test */ + case GL_DEPTH_BOUNDS_TEST_EXT: + CHECK_EXTENSION(EXT_depth_bounds_test); + return ctx->Depth.BoundsTest; + + /* GL_ARB_depth_clamp */ + case GL_DEPTH_CLAMP: + CHECK_EXTENSION(ARB_depth_clamp); + return ctx->Transform.DepthClamp; + +#if FEATURE_ATI_fragment_shader + case GL_FRAGMENT_SHADER_ATI: + CHECK_EXTENSION(ATI_fragment_shader); + return ctx->ATIFragmentShader.Enabled; +#endif /* FEATURE_ATI_fragment_shader */ + + case GL_TEXTURE_CUBE_MAP_SEAMLESS: + CHECK_EXTENSION(ARB_seamless_cube_map); + return ctx->Texture.CubeMapSeamless; + +#if FEATURE_EXT_transform_feedback + case GL_RASTERIZER_DISCARD: + CHECK_EXTENSION(EXT_transform_feedback); + return ctx->TransformFeedback.RasterDiscard; +#endif + + /* GL_NV_primitive_restart */ + case GL_PRIMITIVE_RESTART_NV: + if (!ctx->Extensions.NV_primitive_restart) { + goto invalid_enum_error; + } + return ctx->Array.PrimitiveRestart; + + /* GL 3.1 primitive restart */ + case GL_PRIMITIVE_RESTART: + if (ctx->VersionMajor * 10 + ctx->VersionMinor < 31) { + goto invalid_enum_error; + } + return ctx->Array.PrimitiveRestart; + + /* GL3.0 - GL_framebuffer_sRGB */ + case GL_FRAMEBUFFER_SRGB_EXT: + CHECK_EXTENSION(EXT_framebuffer_sRGB); + return ctx->Color.sRGBEnabled; + + default: + goto invalid_enum_error; + } + + return GL_FALSE; + +invalid_enum_error: + _mesa_error(ctx, GL_INVALID_ENUM, "glIsEnabled(0x%x)", (int) cap); + return GL_FALSE; +} diff --git a/mesalib/src/mesa/main/fbobject.c b/mesalib/src/mesa/main/fbobject.c index f20689b82..97cbd3c61 100644 --- a/mesalib/src/mesa/main/fbobject.c +++ b/mesalib/src/mesa/main/fbobject.c @@ -1,2487 +1,2487 @@ -/* - * Mesa 3-D graphics library - * Version: 7.1 - * - * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. - * Copyright (C) 1999-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. - */ - - -/* - * GL_EXT/ARB_framebuffer_object extensions - * - * Authors: - * Brian Paul - */ - - -#include "buffers.h" -#include "context.h" -#include "enums.h" -#include "fbobject.h" -#include "formats.h" -#include "framebuffer.h" -#include "hash.h" -#include "macros.h" -#include "mfeatures.h" -#include "mtypes.h" -#include "renderbuffer.h" -#include "state.h" -#include "teximage.h" -#include "texobj.h" - - -/** Set this to 1 to help debug FBO incompleteness problems */ -#define DEBUG_FBO 0 - -/** Set this to 1 to debug/log glBlitFramebuffer() calls */ -#define DEBUG_BLIT 0 - - -/** - * Notes: - * - * None of the GL_EXT_framebuffer_object functions are compiled into - * display lists. - */ - - - -/* - * When glGenRender/FramebuffersEXT() is called we insert pointers to - * these placeholder objects into the hash table. - * Later, when the object ID is first bound, we replace the placeholder - * with the real frame/renderbuffer. - */ -static struct gl_framebuffer DummyFramebuffer; -static struct gl_renderbuffer DummyRenderbuffer; - -/* We bind this framebuffer when applications pass a NULL - * drawable/surface in make current. */ -static struct gl_framebuffer IncompleteFramebuffer; - - -#define IS_CUBE_FACE(TARGET) \ - ((TARGET) >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && \ - (TARGET) <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) - - -static void -delete_dummy_renderbuffer(struct gl_renderbuffer *rb) -{ - /* no op */ -} - -static void -delete_dummy_framebuffer(struct gl_framebuffer *fb) -{ - /* no op */ -} - - -void -_mesa_init_fbobjects(struct gl_context *ctx) -{ - _glthread_INIT_MUTEX(DummyFramebuffer.Mutex); - _glthread_INIT_MUTEX(DummyRenderbuffer.Mutex); - _glthread_INIT_MUTEX(IncompleteFramebuffer.Mutex); - DummyFramebuffer.Delete = delete_dummy_framebuffer; - DummyRenderbuffer.Delete = delete_dummy_renderbuffer; - IncompleteFramebuffer.Delete = delete_dummy_framebuffer; -} - -struct gl_framebuffer * -_mesa_get_incomplete_framebuffer(void) -{ - return &IncompleteFramebuffer; -} - -/** - * Helper routine for getting a gl_renderbuffer. - */ -struct gl_renderbuffer * -_mesa_lookup_renderbuffer(struct gl_context *ctx, GLuint id) -{ - struct gl_renderbuffer *rb; - - if (id == 0) - return NULL; - - rb = (struct gl_renderbuffer *) - _mesa_HashLookup(ctx->Shared->RenderBuffers, id); - return rb; -} - - -/** - * Helper routine for getting a gl_framebuffer. - */ -struct gl_framebuffer * -_mesa_lookup_framebuffer(struct gl_context *ctx, GLuint id) -{ - struct gl_framebuffer *fb; - - if (id == 0) - return NULL; - - fb = (struct gl_framebuffer *) - _mesa_HashLookup(ctx->Shared->FrameBuffers, id); - return fb; -} - - -/** - * Mark the given framebuffer as invalid. This will force the - * test for framebuffer completeness to be done before the framebuffer - * is used. - */ -static void -invalidate_framebuffer(struct gl_framebuffer *fb) -{ - fb->_Status = 0; /* "indeterminate" */ -} - - -/** - * Given a GL_*_ATTACHMENTn token, return a pointer to the corresponding - * gl_renderbuffer_attachment object. - * This function is only used for user-created FB objects, not the - * default / window-system FB object. - * If \p attachment is GL_DEPTH_STENCIL_ATTACHMENT, return a pointer to - * the depth buffer attachment point. - */ -struct gl_renderbuffer_attachment * -_mesa_get_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, - GLenum attachment) -{ - GLuint i; - - assert(fb->Name > 0); - - switch (attachment) { - case GL_COLOR_ATTACHMENT0_EXT: - case GL_COLOR_ATTACHMENT1_EXT: - case GL_COLOR_ATTACHMENT2_EXT: - case GL_COLOR_ATTACHMENT3_EXT: - case GL_COLOR_ATTACHMENT4_EXT: - case GL_COLOR_ATTACHMENT5_EXT: - case GL_COLOR_ATTACHMENT6_EXT: - case GL_COLOR_ATTACHMENT7_EXT: - case GL_COLOR_ATTACHMENT8_EXT: - case GL_COLOR_ATTACHMENT9_EXT: - case GL_COLOR_ATTACHMENT10_EXT: - case GL_COLOR_ATTACHMENT11_EXT: - case GL_COLOR_ATTACHMENT12_EXT: - case GL_COLOR_ATTACHMENT13_EXT: - case GL_COLOR_ATTACHMENT14_EXT: - case GL_COLOR_ATTACHMENT15_EXT: - i = attachment - GL_COLOR_ATTACHMENT0_EXT; - if (i >= ctx->Const.MaxColorAttachments) { - return NULL; - } - return &fb->Attachment[BUFFER_COLOR0 + i]; - case GL_DEPTH_STENCIL_ATTACHMENT: - /* fall-through */ - case GL_DEPTH_BUFFER: - /* fall-through / new in GL 3.0 */ - case GL_DEPTH_ATTACHMENT_EXT: - return &fb->Attachment[BUFFER_DEPTH]; - case GL_STENCIL_BUFFER: - /* fall-through / new in GL 3.0 */ - case GL_STENCIL_ATTACHMENT_EXT: - return &fb->Attachment[BUFFER_STENCIL]; - default: - return NULL; - } -} - - -/** - * As above, but only used for getting attachments of the default / - * window-system framebuffer (not user-created framebuffer objects). - */ -static struct gl_renderbuffer_attachment * -_mesa_get_fb0_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, - GLenum attachment) -{ - assert(fb->Name == 0); - - switch (attachment) { - case GL_FRONT_LEFT: - return &fb->Attachment[BUFFER_FRONT_LEFT]; - case GL_FRONT_RIGHT: - return &fb->Attachment[BUFFER_FRONT_RIGHT]; - case GL_BACK_LEFT: - return &fb->Attachment[BUFFER_BACK_LEFT]; - case GL_BACK_RIGHT: - return &fb->Attachment[BUFFER_BACK_RIGHT]; - case GL_AUX0: - if (fb->Visual.numAuxBuffers == 1) { - return &fb->Attachment[BUFFER_AUX0]; - } - return NULL; - case GL_DEPTH_BUFFER: - /* fall-through / new in GL 3.0 */ - case GL_DEPTH_ATTACHMENT_EXT: - return &fb->Attachment[BUFFER_DEPTH]; - case GL_STENCIL_BUFFER: - /* fall-through / new in GL 3.0 */ - case GL_STENCIL_ATTACHMENT_EXT: - return &fb->Attachment[BUFFER_STENCIL]; - default: - return NULL; - } -} - - - -/** - * Remove any texture or renderbuffer attached to the given attachment - * point. Update reference counts, etc. - */ -void -_mesa_remove_attachment(struct gl_context *ctx, - struct gl_renderbuffer_attachment *att) -{ - if (att->Type == GL_TEXTURE) { - ASSERT(att->Texture); - if (ctx->Driver.FinishRenderTexture) { - /* tell driver that we're done rendering to this texture. */ - ctx->Driver.FinishRenderTexture(ctx, att); - } - _mesa_reference_texobj(&att->Texture, NULL); /* unbind */ - ASSERT(!att->Texture); - } - if (att->Type == GL_TEXTURE || att->Type == GL_RENDERBUFFER_EXT) { - ASSERT(!att->Texture); - _mesa_reference_renderbuffer(&att->Renderbuffer, NULL); /* unbind */ - ASSERT(!att->Renderbuffer); - } - att->Type = GL_NONE; - att->Complete = GL_TRUE; -} - - -/** - * Bind a texture object to an attachment point. - * The previous binding, if any, will be removed first. - */ -void -_mesa_set_texture_attachment(struct gl_context *ctx, - struct gl_framebuffer *fb, - struct gl_renderbuffer_attachment *att, - struct gl_texture_object *texObj, - GLenum texTarget, GLuint level, GLuint zoffset) -{ - if (att->Texture == texObj) { - /* re-attaching same texture */ - ASSERT(att->Type == GL_TEXTURE); - if (ctx->Driver.FinishRenderTexture) - ctx->Driver.FinishRenderTexture(ctx, att); - } - else { - /* new attachment */ - if (ctx->Driver.FinishRenderTexture && att->Texture) - ctx->Driver.FinishRenderTexture(ctx, att); - _mesa_remove_attachment(ctx, att); - att->Type = GL_TEXTURE; - assert(!att->Texture); - _mesa_reference_texobj(&att->Texture, texObj); - } - - /* always update these fields */ - att->TextureLevel = level; - att->CubeMapFace = _mesa_tex_target_to_face(texTarget); - att->Zoffset = zoffset; - att->Complete = GL_FALSE; - - if (att->Texture->Image[att->CubeMapFace][att->TextureLevel]) { - ctx->Driver.RenderTexture(ctx, fb, att); - } - - invalidate_framebuffer(fb); -} - - -/** - * Bind a renderbuffer to an attachment point. - * The previous binding, if any, will be removed first. - */ -void -_mesa_set_renderbuffer_attachment(struct gl_context *ctx, - struct gl_renderbuffer_attachment *att, - struct gl_renderbuffer *rb) -{ - /* XXX check if re-doing same attachment, exit early */ - _mesa_remove_attachment(ctx, att); - att->Type = GL_RENDERBUFFER_EXT; - att->Texture = NULL; /* just to be safe */ - att->Complete = GL_FALSE; - _mesa_reference_renderbuffer(&att->Renderbuffer, rb); -} - - -/** - * Fallback for ctx->Driver.FramebufferRenderbuffer() - * Attach a renderbuffer object to a framebuffer object. - */ -void -_mesa_framebuffer_renderbuffer(struct gl_context *ctx, - struct gl_framebuffer *fb, - GLenum attachment, struct gl_renderbuffer *rb) -{ - struct gl_renderbuffer_attachment *att; - - _glthread_LOCK_MUTEX(fb->Mutex); - - att = _mesa_get_attachment(ctx, fb, attachment); - ASSERT(att); - if (rb) { - _mesa_set_renderbuffer_attachment(ctx, att, rb); - if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { - /* do stencil attachment here (depth already done above) */ - att = _mesa_get_attachment(ctx, fb, GL_STENCIL_ATTACHMENT_EXT); - assert(att); - _mesa_set_renderbuffer_attachment(ctx, att, rb); - } - } - else { - _mesa_remove_attachment(ctx, att); - } - - invalidate_framebuffer(fb); - - _glthread_UNLOCK_MUTEX(fb->Mutex); -} - - -/** - * Fallback for ctx->Driver.ValidateFramebuffer() - * Check if the renderbuffer's formats are supported by the software - * renderer. - * Drivers should probably override this. - */ -void -_mesa_validate_framebuffer(struct gl_context *ctx, struct gl_framebuffer *fb) -{ - gl_buffer_index buf; - for (buf = 0; buf < BUFFER_COUNT; buf++) { - const struct gl_renderbuffer *rb = fb->Attachment[buf].Renderbuffer; - if (rb) { - switch (rb->_BaseFormat) { - case GL_ALPHA: - case GL_LUMINANCE_ALPHA: - case GL_LUMINANCE: - case GL_INTENSITY: - case GL_RED: - case GL_RG: - fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED; - return; - default: - /* render buffer format is supported by software rendering */ - ; - } - } - } -} - - -/** - * For debug only. - */ -static void -att_incomplete(const char *msg) -{ -#if DEBUG_FBO - _mesa_debug(NULL, "attachment incomplete: %s\n", msg); -#else - (void) msg; -#endif -} - - -/** - * For debug only. - */ -static void -fbo_incomplete(const char *msg, int index) -{ -#if DEBUG_FBO - _mesa_debug(NULL, "FBO Incomplete: %s [%d]\n", msg, index); -#else - (void) msg; - (void) index; -#endif -} - - -/** - * Is the given base format a legal format for a color renderbuffer? - */ -GLboolean -_mesa_is_legal_color_format(const struct gl_context *ctx, GLenum baseFormat) -{ - switch (baseFormat) { - case GL_RGB: - case GL_RGBA: - return GL_TRUE; - case GL_LUMINANCE: - case GL_LUMINANCE_ALPHA: - case GL_INTENSITY: - case GL_ALPHA: - return ctx->Extensions.ARB_framebuffer_object; - case GL_RED: - case GL_RG: - return ctx->Extensions.ARB_texture_rg; - default: - return GL_FALSE; - } -} - - -/** - * Is the given base format a legal format for a depth/stencil renderbuffer? - */ -static GLboolean -is_legal_depth_format(const struct gl_context *ctx, GLenum baseFormat) -{ - switch (baseFormat) { - case GL_DEPTH_COMPONENT: - case GL_DEPTH_STENCIL_EXT: - return GL_TRUE; - default: - return GL_FALSE; - } -} - - -/** - * Test if an attachment point is complete and update its Complete field. - * \param format if GL_COLOR, this is a color attachment point, - * if GL_DEPTH, this is a depth component attachment point, - * if GL_STENCIL, this is a stencil component attachment point. - */ -static void -test_attachment_completeness(const struct gl_context *ctx, GLenum format, - struct gl_renderbuffer_attachment *att) -{ - assert(format == GL_COLOR || format == GL_DEPTH || format == GL_STENCIL); - - /* assume complete */ - att->Complete = GL_TRUE; - - /* Look for reasons why the attachment might be incomplete */ - if (att->Type == GL_TEXTURE) { - const struct gl_texture_object *texObj = att->Texture; - struct gl_texture_image *texImage; - GLenum baseFormat; - - if (!texObj) { - att_incomplete("no texobj"); - att->Complete = GL_FALSE; - return; - } - - texImage = texObj->Image[att->CubeMapFace][att->TextureLevel]; - if (!texImage) { - att_incomplete("no teximage"); - att->Complete = GL_FALSE; - return; - } - if (texImage->Width < 1 || texImage->Height < 1) { - att_incomplete("teximage width/height=0"); - printf("texobj = %u\n", texObj->Name); - printf("level = %d\n", att->TextureLevel); - att->Complete = GL_FALSE; - return; - } - if (texObj->Target == GL_TEXTURE_3D && att->Zoffset >= texImage->Depth) { - att_incomplete("bad z offset"); - att->Complete = GL_FALSE; - return; - } - - baseFormat = _mesa_get_format_base_format(texImage->TexFormat); - - if (format == GL_COLOR) { - if (!_mesa_is_legal_color_format(ctx, baseFormat)) { - att_incomplete("bad format"); - att->Complete = GL_FALSE; - return; - } - if (_mesa_is_format_compressed(texImage->TexFormat)) { - att_incomplete("compressed internalformat"); - att->Complete = GL_FALSE; - return; - } - } - else if (format == GL_DEPTH) { - if (baseFormat == GL_DEPTH_COMPONENT) { - /* OK */ - } - else if (ctx->Extensions.EXT_packed_depth_stencil && - ctx->Extensions.ARB_depth_texture && - baseFormat == GL_DEPTH_STENCIL_EXT) { - /* OK */ - } - else { - att->Complete = GL_FALSE; - att_incomplete("bad depth format"); - return; - } - } - else { - ASSERT(format == GL_STENCIL); - if (ctx->Extensions.EXT_packed_depth_stencil && - ctx->Extensions.ARB_depth_texture && - baseFormat == GL_DEPTH_STENCIL_EXT) { - /* OK */ - } - else { - /* no such thing as stencil-only textures */ - att_incomplete("illegal stencil texture"); - att->Complete = GL_FALSE; - return; - } - } - } - else if (att->Type == GL_RENDERBUFFER_EXT) { - const GLenum baseFormat = - _mesa_get_format_base_format(att->Renderbuffer->Format); - - ASSERT(att->Renderbuffer); - if (!att->Renderbuffer->InternalFormat || - att->Renderbuffer->Width < 1 || - att->Renderbuffer->Height < 1) { - att_incomplete("0x0 renderbuffer"); - att->Complete = GL_FALSE; - return; - } - if (format == GL_COLOR) { - if (!_mesa_is_legal_color_format(ctx, baseFormat)) { - att_incomplete("bad renderbuffer color format"); - att->Complete = GL_FALSE; - return; - } - } - else if (format == GL_DEPTH) { - if (baseFormat == GL_DEPTH_COMPONENT) { - /* OK */ - } - else if (ctx->Extensions.EXT_packed_depth_stencil && - baseFormat == GL_DEPTH_STENCIL_EXT) { - /* OK */ - } - else { - att_incomplete("bad renderbuffer depth format"); - att->Complete = GL_FALSE; - return; - } - } - else { - assert(format == GL_STENCIL); - if (baseFormat == GL_STENCIL_INDEX) { - /* OK */ - } - else if (ctx->Extensions.EXT_packed_depth_stencil && - baseFormat == GL_DEPTH_STENCIL_EXT) { - /* OK */ - } - else { - att->Complete = GL_FALSE; - att_incomplete("bad renderbuffer stencil format"); - return; - } - } - } - else { - ASSERT(att->Type == GL_NONE); - /* complete */ - return; - } -} - - -/** - * Test if the given framebuffer object is complete and update its - * Status field with the results. - * Calls the ctx->Driver.ValidateFramebuffer() function to allow the - * driver to make hardware-specific validation/completeness checks. - * Also update the framebuffer's Width and Height fields if the - * framebuffer is complete. - */ -void -_mesa_test_framebuffer_completeness(struct gl_context *ctx, - struct gl_framebuffer *fb) -{ - GLuint numImages; - GLenum intFormat = GL_NONE; /* color buffers' internal format */ - GLuint minWidth = ~0, minHeight = ~0, maxWidth = 0, maxHeight = 0; - GLint numSamples = -1; - GLint i; - GLuint j; - - assert(fb->Name != 0); - - numImages = 0; - fb->Width = 0; - fb->Height = 0; - - /* Start at -2 to more easily loop over all attachment points. - * -2: depth buffer - * -1: stencil buffer - * >=0: color buffer - */ - for (i = -2; i < (GLint) ctx->Const.MaxColorAttachments; i++) { - struct gl_renderbuffer_attachment *att; - GLenum f; - gl_format mesaFormat; - - /* - * XXX for ARB_fbo, only check color buffers that are named by - * GL_READ_BUFFER and GL_DRAW_BUFFERi. - */ - - /* check for attachment completeness - */ - if (i == -2) { - att = &fb->Attachment[BUFFER_DEPTH]; - test_attachment_completeness(ctx, GL_DEPTH, att); - if (!att->Complete) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT; - fbo_incomplete("depth attachment incomplete", -1); - return; - } - } - else if (i == -1) { - att = &fb->Attachment[BUFFER_STENCIL]; - test_attachment_completeness(ctx, GL_STENCIL, att); - if (!att->Complete) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT; - fbo_incomplete("stencil attachment incomplete", -1); - return; - } - } - else { - att = &fb->Attachment[BUFFER_COLOR0 + i]; - test_attachment_completeness(ctx, GL_COLOR, att); - if (!att->Complete) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT; - fbo_incomplete("color attachment incomplete", i); - return; - } - } - - /* get width, height, format of the renderbuffer/texture - */ - if (att->Type == GL_TEXTURE) { - const struct gl_texture_image *texImg - = att->Texture->Image[att->CubeMapFace][att->TextureLevel]; - minWidth = MIN2(minWidth, texImg->Width); - maxWidth = MAX2(maxWidth, texImg->Width); - minHeight = MIN2(minHeight, texImg->Height); - maxHeight = MAX2(maxHeight, texImg->Height); - f = texImg->_BaseFormat; - mesaFormat = texImg->TexFormat; - numImages++; - if (!_mesa_is_legal_color_format(ctx, f) && - !is_legal_depth_format(ctx, f)) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT; - fbo_incomplete("texture attachment incomplete", -1); - return; - } - } - else if (att->Type == GL_RENDERBUFFER_EXT) { - minWidth = MIN2(minWidth, att->Renderbuffer->Width); - maxWidth = MAX2(minWidth, att->Renderbuffer->Width); - minHeight = MIN2(minHeight, att->Renderbuffer->Height); - maxHeight = MAX2(minHeight, att->Renderbuffer->Height); - f = att->Renderbuffer->InternalFormat; - mesaFormat = att->Renderbuffer->Format; - numImages++; - } - else { - assert(att->Type == GL_NONE); - continue; - } - - if (numSamples < 0) { - /* first buffer */ - numSamples = att->Renderbuffer->NumSamples; - } - - /* check if integer color */ - fb->_IntegerColor = _mesa_is_format_integer_color(mesaFormat); - - /* Error-check width, height, format, samples - */ - if (numImages == 1) { - /* save format, num samples */ - if (i >= 0) { - intFormat = f; - } - } - else { - if (!ctx->Extensions.ARB_framebuffer_object) { - /* check that width, height, format are same */ - if (minWidth != maxWidth || minHeight != maxHeight) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT; - fbo_incomplete("width or height mismatch", -1); - return; - } - /* check that all color buffer have same format */ - if (intFormat != GL_NONE && f != intFormat) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT; - fbo_incomplete("format mismatch", -1); - return; - } - } - if (att->Renderbuffer && - att->Renderbuffer->NumSamples != numSamples) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; - fbo_incomplete("inconsistant number of samples", i); - return; - } - - } - } - -#if FEATURE_GL - if (ctx->API == API_OPENGL) { - /* Check that all DrawBuffers are present */ - for (j = 0; j < ctx->Const.MaxDrawBuffers; j++) { - if (fb->ColorDrawBuffer[j] != GL_NONE) { - const struct gl_renderbuffer_attachment *att - = _mesa_get_attachment(ctx, fb, fb->ColorDrawBuffer[j]); - assert(att); - if (att->Type == GL_NONE) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT; - fbo_incomplete("missing drawbuffer", j); - return; - } - } - } - - /* Check that the ReadBuffer is present */ - if (fb->ColorReadBuffer != GL_NONE) { - const struct gl_renderbuffer_attachment *att - = _mesa_get_attachment(ctx, fb, fb->ColorReadBuffer); - assert(att); - if (att->Type == GL_NONE) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT; - fbo_incomplete("missing readbuffer", -1); - return; - } - } - } -#else - (void) j; -#endif - - if (numImages == 0) { - fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT; - fbo_incomplete("no attachments", -1); - return; - } - - /* Provisionally set status = COMPLETE ... */ - fb->_Status = GL_FRAMEBUFFER_COMPLETE_EXT; - - /* ... but the driver may say the FB is incomplete. - * Drivers will most likely set the status to GL_FRAMEBUFFER_UNSUPPORTED - * if anything. - */ - if (ctx->Driver.ValidateFramebuffer) { - ctx->Driver.ValidateFramebuffer(ctx, fb); - if (fb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { - fbo_incomplete("driver marked FBO as incomplete", -1); - } - } - - if (fb->_Status == GL_FRAMEBUFFER_COMPLETE_EXT) { - /* - * Note that if ARB_framebuffer_object is supported and the attached - * renderbuffers/textures are different sizes, the framebuffer - * width/height will be set to the smallest width/height. - */ - fb->Width = minWidth; - fb->Height = minHeight; - - /* finally, update the visual info for the framebuffer */ - _mesa_update_framebuffer_visual(ctx, fb); - } -} - - -GLboolean GLAPIENTRY -_mesa_IsRenderbufferEXT(GLuint renderbuffer) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); - if (renderbuffer) { - struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, renderbuffer); - if (rb != NULL && rb != &DummyRenderbuffer) - return GL_TRUE; - } - return GL_FALSE; -} - - -void GLAPIENTRY -_mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer) -{ - struct gl_renderbuffer *newRb; - GET_CURRENT_CONTEXT(ctx); - - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (target != GL_RENDERBUFFER_EXT) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBindRenderbufferEXT(target)"); - return; - } - - /* No need to flush here since the render buffer binding has no - * effect on rendering state. - */ - - if (renderbuffer) { - newRb = _mesa_lookup_renderbuffer(ctx, renderbuffer); - if (newRb == &DummyRenderbuffer) { - /* ID was reserved, but no real renderbuffer object made yet */ - newRb = NULL; - } - else if (!newRb && ctx->Extensions.ARB_framebuffer_object) { - /* All RB IDs must be Gen'd */ - _mesa_error(ctx, GL_INVALID_OPERATION, "glBindRenderbuffer(buffer)"); - return; - } - - if (!newRb) { - /* create new renderbuffer object */ - newRb = ctx->Driver.NewRenderbuffer(ctx, renderbuffer); - if (!newRb) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindRenderbufferEXT"); - return; - } - ASSERT(newRb->AllocStorage); - _mesa_HashInsert(ctx->Shared->RenderBuffers, renderbuffer, newRb); - newRb->RefCount = 1; /* referenced by hash table */ - } - } - else { - newRb = NULL; - } - - ASSERT(newRb != &DummyRenderbuffer); - - _mesa_reference_renderbuffer(&ctx->CurrentRenderbuffer, newRb); -} - - -/** - * If the given renderbuffer is anywhere attached to the framebuffer, detach - * the renderbuffer. - * This is used when a renderbuffer object is deleted. - * The spec calls for unbinding. - */ -static void -detach_renderbuffer(struct gl_context *ctx, - struct gl_framebuffer *fb, - struct gl_renderbuffer *rb) -{ - GLuint i; - for (i = 0; i < BUFFER_COUNT; i++) { - if (fb->Attachment[i].Renderbuffer == rb) { - _mesa_remove_attachment(ctx, &fb->Attachment[i]); - } - } - invalidate_framebuffer(fb); -} - - -void GLAPIENTRY -_mesa_DeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers) -{ - GLint i; - GET_CURRENT_CONTEXT(ctx); - - ASSERT_OUTSIDE_BEGIN_END(ctx); - FLUSH_VERTICES(ctx, _NEW_BUFFERS); - - for (i = 0; i < n; i++) { - if (renderbuffers[i] > 0) { - struct gl_renderbuffer *rb; - rb = _mesa_lookup_renderbuffer(ctx, renderbuffers[i]); - if (rb) { - /* check if deleting currently bound renderbuffer object */ - if (rb == ctx->CurrentRenderbuffer) { - /* bind default */ - ASSERT(rb->RefCount >= 2); - _mesa_BindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); - } - - if (ctx->DrawBuffer->Name) { - detach_renderbuffer(ctx, ctx->DrawBuffer, rb); - } - if (ctx->ReadBuffer->Name && ctx->ReadBuffer != ctx->DrawBuffer) { - detach_renderbuffer(ctx, ctx->ReadBuffer, rb); - } - - /* Remove from hash table immediately, to free the ID. - * But the object will not be freed until it's no longer - * referenced anywhere else. - */ - _mesa_HashRemove(ctx->Shared->RenderBuffers, renderbuffers[i]); - - if (rb != &DummyRenderbuffer) { - /* no longer referenced by hash table */ - _mesa_reference_renderbuffer(&rb, NULL); - } - } - } - } -} - - -void GLAPIENTRY -_mesa_GenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers) -{ - GET_CURRENT_CONTEXT(ctx); - GLuint first; - GLint i; - - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (n < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, "glGenRenderbuffersEXT(n)"); - return; - } - - if (!renderbuffers) - return; - - first = _mesa_HashFindFreeKeyBlock(ctx->Shared->RenderBuffers, n); - - for (i = 0; i < n; i++) { - GLuint name = first + i; - renderbuffers[i] = name; - /* insert dummy placeholder into hash table */ - _glthread_LOCK_MUTEX(ctx->Shared->Mutex); - _mesa_HashInsert(ctx->Shared->RenderBuffers, name, &DummyRenderbuffer); - _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); - } -} - - -/** - * Given an internal format token for a render buffer, return the - * corresponding base format (one of GL_RGB, GL_RGBA, GL_STENCIL_INDEX, - * GL_DEPTH_COMPONENT, GL_DEPTH_STENCIL_EXT, GL_ALPHA, GL_LUMINANCE, - * GL_LUMINANCE_ALPHA, GL_INTENSITY, etc). - * - * This is similar to _mesa_base_tex_format() but the set of valid - * internal formats is different. - * - * Note that even if a format is determined to be legal here, validation - * of the FBO may fail if the format is not supported by the driver/GPU. - * - * \param internalFormat as passed to glRenderbufferStorage() - * \return the base internal format, or 0 if internalFormat is illegal - */ -GLenum -_mesa_base_fbo_format(struct gl_context *ctx, GLenum internalFormat) -{ - /* - * Notes: some formats such as alpha, luminance, etc. were added - * with GL_ARB_framebuffer_object. - */ - switch (internalFormat) { - case GL_ALPHA: - case GL_ALPHA4: - case GL_ALPHA8: - case GL_ALPHA12: - case GL_ALPHA16: - return ctx->Extensions.ARB_framebuffer_object ? GL_ALPHA : 0; - case GL_LUMINANCE: - case GL_LUMINANCE4: - case GL_LUMINANCE8: - case GL_LUMINANCE12: - case GL_LUMINANCE16: - return ctx->Extensions.ARB_framebuffer_object ? GL_LUMINANCE : 0; - case GL_LUMINANCE_ALPHA: - case GL_LUMINANCE4_ALPHA4: - case GL_LUMINANCE6_ALPHA2: - case GL_LUMINANCE8_ALPHA8: - case GL_LUMINANCE12_ALPHA4: - case GL_LUMINANCE12_ALPHA12: - case GL_LUMINANCE16_ALPHA16: - return ctx->Extensions.ARB_framebuffer_object ? GL_LUMINANCE_ALPHA : 0; - case GL_INTENSITY: - case GL_INTENSITY4: - case GL_INTENSITY8: - case GL_INTENSITY12: - case GL_INTENSITY16: - return ctx->Extensions.ARB_framebuffer_object ? GL_INTENSITY : 0; - case GL_RGB: - case GL_R3_G3_B2: - case GL_RGB4: - case GL_RGB5: - case GL_RGB8: - case GL_RGB10: - case GL_RGB12: - case GL_RGB16: - case GL_SRGB8_EXT: - return GL_RGB; - case GL_RGBA: - case GL_RGBA2: - case GL_RGBA4: - case GL_RGB5_A1: - case GL_RGBA8: - case GL_RGB10_A2: - case GL_RGBA12: - case GL_RGBA16: - case GL_RGBA16_SNORM: - case GL_SRGB8_ALPHA8_EXT: - return GL_RGBA; - case GL_STENCIL_INDEX: - case GL_STENCIL_INDEX1_EXT: - case GL_STENCIL_INDEX4_EXT: - case GL_STENCIL_INDEX8_EXT: - case GL_STENCIL_INDEX16_EXT: - return GL_STENCIL_INDEX; - case GL_DEPTH_COMPONENT: - case GL_DEPTH_COMPONENT16: - case GL_DEPTH_COMPONENT24: - case GL_DEPTH_COMPONENT32: - return GL_DEPTH_COMPONENT; - case GL_DEPTH_STENCIL_EXT: - case GL_DEPTH24_STENCIL8_EXT: - if (ctx->Extensions.EXT_packed_depth_stencil) - return GL_DEPTH_STENCIL_EXT; - else - return 0; - case GL_RED: - case GL_R8: - case GL_R16: - return ctx->Extensions.ARB_texture_rg ? GL_RED : 0; - case GL_RG: - case GL_RG8: - case GL_RG16: - return ctx->Extensions.ARB_texture_rg ? GL_RG : 0; - /* XXX add floating point and integer formats eventually */ - default: - return 0; - } -} - - -/** sentinal value, see below */ -#define NO_SAMPLES 1000 - - -/** - * Helper function used by _mesa_RenderbufferStorageEXT() and - * _mesa_RenderbufferStorageMultisample(). - * samples will be NO_SAMPLES if called by _mesa_RenderbufferStorageEXT(). - */ -static void -renderbuffer_storage(GLenum target, GLenum internalFormat, - GLsizei width, GLsizei height, GLsizei samples) -{ - const char *func = samples == NO_SAMPLES ? - "glRenderbufferStorage" : "RenderbufferStorageMultisample"; - struct gl_renderbuffer *rb; - GLenum baseFormat; - GET_CURRENT_CONTEXT(ctx); - - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (target != GL_RENDERBUFFER_EXT) { - _mesa_error(ctx, GL_INVALID_ENUM, "%s(target)", func); - return; - } - - baseFormat = _mesa_base_fbo_format(ctx, internalFormat); - if (baseFormat == 0) { - _mesa_error(ctx, GL_INVALID_ENUM, "%s(internalFormat)", func); - return; - } - - if (width < 1 || width > (GLsizei) ctx->Const.MaxRenderbufferSize) { - _mesa_error(ctx, GL_INVALID_VALUE, "%s(width)", func); - return; - } - - if (height < 1 || height > (GLsizei) ctx->Const.MaxRenderbufferSize) { - _mesa_error(ctx, GL_INVALID_VALUE, "%s(height)", func); - return; - } - - if (samples == NO_SAMPLES) { - /* NumSamples == 0 indicates non-multisampling */ - samples = 0; - } - else if (samples > (GLsizei) ctx->Const.MaxSamples) { - /* note: driver may choose to use more samples than what's requested */ - _mesa_error(ctx, GL_INVALID_VALUE, "%s(samples)", func); - return; - } - - rb = ctx->CurrentRenderbuffer; - if (!rb) { - _mesa_error(ctx, GL_INVALID_OPERATION, "%s", func); - return; - } - - FLUSH_VERTICES(ctx, _NEW_BUFFERS); - - if (rb->InternalFormat == internalFormat && - rb->Width == (GLuint) width && - rb->Height == (GLuint) height) { - /* no change in allocation needed */ - return; - } - - /* These MUST get set by the AllocStorage func */ - rb->Format = MESA_FORMAT_NONE; - rb->NumSamples = samples; - - /* Now allocate the storage */ - ASSERT(rb->AllocStorage); - if (rb->AllocStorage(ctx, rb, internalFormat, width, height)) { - /* No error - check/set fields now */ - assert(rb->Format != MESA_FORMAT_NONE); - assert(rb->Width == (GLuint) width); - assert(rb->Height == (GLuint) height); - rb->InternalFormat = internalFormat; - rb->_BaseFormat = baseFormat; - assert(rb->_BaseFormat != 0); - } - else { - /* Probably ran out of memory - clear the fields */ - rb->Width = 0; - rb->Height = 0; - rb->Format = MESA_FORMAT_NONE; - rb->InternalFormat = GL_NONE; - rb->_BaseFormat = GL_NONE; - rb->NumSamples = 0; - } - - /* - test_framebuffer_completeness(ctx, fb); - */ - /* XXX if this renderbuffer is attached anywhere, invalidate attachment - * points??? - */ -} - - -#if FEATURE_OES_EGL_image -void GLAPIENTRY -_mesa_EGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image) -{ - struct gl_renderbuffer *rb; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (!ctx->Extensions.OES_EGL_image) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glEGLImageTargetRenderbufferStorageOES(unsupported)"); - return; - } - - if (target != GL_RENDERBUFFER) { - _mesa_error(ctx, GL_INVALID_ENUM, - "EGLImageTargetRenderbufferStorageOES"); - return; - } - - rb = ctx->CurrentRenderbuffer; - if (!rb) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "EGLImageTargetRenderbufferStorageOES"); - return; - } - - FLUSH_VERTICES(ctx, _NEW_BUFFERS); - - ctx->Driver.EGLImageTargetRenderbufferStorage(ctx, rb, image); -} -#endif - - -/** - * Helper function for _mesa_GetRenderbufferParameterivEXT() and - * _mesa_GetFramebufferAttachmentParameterivEXT() - * We have to be careful to respect the base format. For example, if a - * renderbuffer/texture was created with internalFormat=GL_RGB but the - * driver actually chose a GL_RGBA format, when the user queries ALPHA_SIZE - * we need to return zero. - */ -static GLint -get_component_bits(GLenum pname, GLenum baseFormat, gl_format format) -{ - switch (pname) { - case GL_RENDERBUFFER_RED_SIZE_EXT: - case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: - if (baseFormat == GL_RGB || baseFormat == GL_RGBA || - baseFormat == GL_RG || baseFormat == GL_RED) - return _mesa_get_format_bits(format, pname); - else - return 0; - case GL_RENDERBUFFER_GREEN_SIZE_EXT: - case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: - if (baseFormat == GL_RGB || baseFormat == GL_RGBA || baseFormat == GL_RG) - return _mesa_get_format_bits(format, pname); - else - return 0; - case GL_RENDERBUFFER_BLUE_SIZE_EXT: - case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: - if (baseFormat == GL_RGB || baseFormat == GL_RGBA) - return _mesa_get_format_bits(format, pname); - else - return 0; - case GL_RENDERBUFFER_ALPHA_SIZE_EXT: - case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: - if (baseFormat == GL_RGBA || baseFormat == GL_ALPHA || - baseFormat == GL_LUMINANCE_ALPHA) - return _mesa_get_format_bits(format, pname); - else - return 0; - case GL_RENDERBUFFER_DEPTH_SIZE_EXT: - case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: - if (baseFormat == GL_DEPTH_COMPONENT || baseFormat == GL_DEPTH_STENCIL) - return _mesa_get_format_bits(format, pname); - else - return 0; - case GL_RENDERBUFFER_STENCIL_SIZE_EXT: - case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: - if (baseFormat == GL_STENCIL_INDEX || baseFormat == GL_DEPTH_STENCIL) - return _mesa_get_format_bits(format, pname); - else - return 0; - default: - return 0; - } -} - - - -void GLAPIENTRY -_mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat, - GLsizei width, GLsizei height) -{ - /* GL_ARB_fbo says calling this function is equivalent to calling - * glRenderbufferStorageMultisample() with samples=0. We pass in - * a token value here just for error reporting purposes. - */ - renderbuffer_storage(target, internalFormat, width, height, NO_SAMPLES); -} - - -void GLAPIENTRY -_mesa_RenderbufferStorageMultisample(GLenum target, GLsizei samples, - GLenum internalFormat, - GLsizei width, GLsizei height) -{ - renderbuffer_storage(target, internalFormat, width, height, samples); -} - - -/** - * OpenGL ES version of glRenderBufferStorage. - */ -void GLAPIENTRY -_es_RenderbufferStorageEXT(GLenum target, GLenum internalFormat, - GLsizei width, GLsizei height) -{ - switch (internalFormat) { - case GL_RGB565: - /* XXX this confuses GL_RENDERBUFFER_INTERNAL_FORMAT_OES */ - /* choose a closest format */ - internalFormat = GL_RGB5; - break; - default: - break; - } - - renderbuffer_storage(target, internalFormat, width, height, 0); -} - - -void GLAPIENTRY -_mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params) -{ - struct gl_renderbuffer *rb; - GET_CURRENT_CONTEXT(ctx); - - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (target != GL_RENDERBUFFER_EXT) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetRenderbufferParameterivEXT(target)"); - return; - } - - rb = ctx->CurrentRenderbuffer; - if (!rb) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetRenderbufferParameterivEXT"); - return; - } - - /* No need to flush here since we're just quering state which is - * not effected by rendering. - */ - - switch (pname) { - case GL_RENDERBUFFER_WIDTH_EXT: - *params = rb->Width; - return; - case GL_RENDERBUFFER_HEIGHT_EXT: - *params = rb->Height; - return; - case GL_RENDERBUFFER_INTERNAL_FORMAT_EXT: - *params = rb->InternalFormat; - return; - case GL_RENDERBUFFER_RED_SIZE_EXT: - case GL_RENDERBUFFER_GREEN_SIZE_EXT: - case GL_RENDERBUFFER_BLUE_SIZE_EXT: - case GL_RENDERBUFFER_ALPHA_SIZE_EXT: - case GL_RENDERBUFFER_DEPTH_SIZE_EXT: - case GL_RENDERBUFFER_STENCIL_SIZE_EXT: - *params = get_component_bits(pname, rb->_BaseFormat, rb->Format); - break; - case GL_RENDERBUFFER_SAMPLES: - if (ctx->Extensions.ARB_framebuffer_object) { - *params = rb->NumSamples; - break; - } - /* fallthrough */ - default: - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetRenderbufferParameterivEXT(target)"); - return; - } -} - - -GLboolean GLAPIENTRY -_mesa_IsFramebufferEXT(GLuint framebuffer) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); - if (framebuffer) { - struct gl_framebuffer *rb = _mesa_lookup_framebuffer(ctx, framebuffer); - if (rb != NULL && rb != &DummyFramebuffer) - return GL_TRUE; - } - return GL_FALSE; -} - - -/** - * Check if any of the attachments of the given framebuffer are textures - * (render to texture). Call ctx->Driver.RenderTexture() for such - * attachments. - */ -static void -check_begin_texture_render(struct gl_context *ctx, struct gl_framebuffer *fb) -{ - GLuint i; - ASSERT(ctx->Driver.RenderTexture); - - if (fb->Name == 0) - return; /* can't render to texture with winsys framebuffers */ - - for (i = 0; i < BUFFER_COUNT; i++) { - struct gl_renderbuffer_attachment *att = fb->Attachment + i; - struct gl_texture_object *texObj = att->Texture; - if (texObj - && texObj->Image[att->CubeMapFace][att->TextureLevel]) { - ctx->Driver.RenderTexture(ctx, fb, att); - } - } -} - - -/** - * Examine all the framebuffer's attachments to see if any are textures. - * If so, call ctx->Driver.FinishRenderTexture() for each texture to - * notify the device driver that the texture image may have changed. - */ -static void -check_end_texture_render(struct gl_context *ctx, struct gl_framebuffer *fb) -{ - if (fb->Name == 0) - return; /* can't render to texture with winsys framebuffers */ - - if (ctx->Driver.FinishRenderTexture) { - GLuint i; - for (i = 0; i < BUFFER_COUNT; i++) { - struct gl_renderbuffer_attachment *att = fb->Attachment + i; - if (att->Texture && att->Renderbuffer) { - ctx->Driver.FinishRenderTexture(ctx, att); - } - } - } -} - - -void GLAPIENTRY -_mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer) -{ - struct gl_framebuffer *newDrawFb, *newReadFb; - struct gl_framebuffer *oldDrawFb, *oldReadFb; - GLboolean bindReadBuf, bindDrawBuf; - GET_CURRENT_CONTEXT(ctx); - -#ifdef DEBUG - if (ctx->Extensions.ARB_framebuffer_object) { - ASSERT(ctx->Extensions.EXT_framebuffer_object); - ASSERT(ctx->Extensions.EXT_framebuffer_blit); - } -#endif - - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (!ctx->Extensions.EXT_framebuffer_object) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBindFramebufferEXT(unsupported)"); - return; - } - - switch (target) { -#if FEATURE_EXT_framebuffer_blit - case GL_DRAW_FRAMEBUFFER_EXT: - if (!ctx->Extensions.EXT_framebuffer_blit) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)"); - return; - } - bindDrawBuf = GL_TRUE; - bindReadBuf = GL_FALSE; - break; - case GL_READ_FRAMEBUFFER_EXT: - if (!ctx->Extensions.EXT_framebuffer_blit) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)"); - return; - } - bindDrawBuf = GL_FALSE; - bindReadBuf = GL_TRUE; - break; -#endif - case GL_FRAMEBUFFER_EXT: - bindDrawBuf = GL_TRUE; - bindReadBuf = GL_TRUE; - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)"); - return; - } - - if (framebuffer) { - /* Binding a user-created framebuffer object */ - newDrawFb = _mesa_lookup_framebuffer(ctx, framebuffer); - if (newDrawFb == &DummyFramebuffer) { - /* ID was reserved, but no real framebuffer object made yet */ - newDrawFb = NULL; - } - else if (!newDrawFb && ctx->Extensions.ARB_framebuffer_object) { - /* All FBO IDs must be Gen'd */ - _mesa_error(ctx, GL_INVALID_OPERATION, "glBindFramebuffer(buffer)"); - return; - } - - if (!newDrawFb) { - /* create new framebuffer object */ - newDrawFb = ctx->Driver.NewFramebuffer(ctx, framebuffer); - if (!newDrawFb) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindFramebufferEXT"); - return; - } - _mesa_HashInsert(ctx->Shared->FrameBuffers, framebuffer, newDrawFb); - } - newReadFb = newDrawFb; - } - else { - /* Binding the window system framebuffer (which was originally set - * with MakeCurrent). - */ - newDrawFb = ctx->WinSysDrawBuffer; - newReadFb = ctx->WinSysReadBuffer; - } - - ASSERT(newDrawFb); - ASSERT(newDrawFb != &DummyFramebuffer); - - /* save pointers to current/old framebuffers */ - oldDrawFb = ctx->DrawBuffer; - oldReadFb = ctx->ReadBuffer; - - /* check if really changing bindings */ - if (oldDrawFb == newDrawFb) - bindDrawBuf = GL_FALSE; - if (oldReadFb == newReadFb) - bindReadBuf = GL_FALSE; - - /* - * OK, now bind the new Draw/Read framebuffers, if they're changing. - * - * We also check if we're beginning and/or ending render-to-texture. - * When a framebuffer with texture attachments is unbound, call - * ctx->Driver.FinishRenderTexture(). - * When a framebuffer with texture attachments is bound, call - * ctx->Driver.RenderTexture(). - * - * Note that if the ReadBuffer has texture attachments we don't consider - * that a render-to-texture case. - */ - if (bindReadBuf) { - FLUSH_VERTICES(ctx, _NEW_BUFFERS); - - /* check if old readbuffer was render-to-texture */ - check_end_texture_render(ctx, oldReadFb); - - _mesa_reference_framebuffer(&ctx->ReadBuffer, newReadFb); - } - - if (bindDrawBuf) { - FLUSH_VERTICES(ctx, _NEW_BUFFERS); - - /* check if old read/draw buffers were render-to-texture */ - if (!bindReadBuf) - check_end_texture_render(ctx, oldReadFb); - - if (oldDrawFb != oldReadFb) - check_end_texture_render(ctx, oldDrawFb); - - /* check if newly bound framebuffer has any texture attachments */ - check_begin_texture_render(ctx, newDrawFb); - - _mesa_reference_framebuffer(&ctx->DrawBuffer, newDrawFb); - } - - if ((bindDrawBuf || bindReadBuf) && ctx->Driver.BindFramebuffer) { - ctx->Driver.BindFramebuffer(ctx, target, newDrawFb, newReadFb); - } -} - - -void GLAPIENTRY -_mesa_DeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers) -{ - GLint i; - GET_CURRENT_CONTEXT(ctx); - - ASSERT_OUTSIDE_BEGIN_END(ctx); - FLUSH_VERTICES(ctx, _NEW_BUFFERS); - - for (i = 0; i < n; i++) { - if (framebuffers[i] > 0) { - struct gl_framebuffer *fb; - fb = _mesa_lookup_framebuffer(ctx, framebuffers[i]); - if (fb) { - ASSERT(fb == &DummyFramebuffer || fb->Name == framebuffers[i]); - - /* check if deleting currently bound framebuffer object */ - if (ctx->Extensions.EXT_framebuffer_blit) { - /* separate draw/read binding points */ - if (fb == ctx->DrawBuffer) { - /* bind default */ - ASSERT(fb->RefCount >= 2); - _mesa_BindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); - } - if (fb == ctx->ReadBuffer) { - /* bind default */ - ASSERT(fb->RefCount >= 2); - _mesa_BindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); - } - } - else { - /* only one binding point for read/draw buffers */ - if (fb == ctx->DrawBuffer || fb == ctx->ReadBuffer) { - /* bind default */ - ASSERT(fb->RefCount >= 2); - _mesa_BindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - } - } - - /* remove from hash table immediately, to free the ID */ - _mesa_HashRemove(ctx->Shared->FrameBuffers, framebuffers[i]); - - if (fb != &DummyFramebuffer) { - /* But the object will not be freed until it's no longer - * bound in any context. - */ - _mesa_reference_framebuffer(&fb, NULL); - } - } - } - } -} - - -void GLAPIENTRY -_mesa_GenFramebuffersEXT(GLsizei n, GLuint *framebuffers) -{ - GET_CURRENT_CONTEXT(ctx); - GLuint first; - GLint i; - - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (n < 0) { - _mesa_error(ctx, GL_INVALID_VALUE, "glGenFramebuffersEXT(n)"); - return; - } - - if (!framebuffers) - return; - - first = _mesa_HashFindFreeKeyBlock(ctx->Shared->FrameBuffers, n); - - for (i = 0; i < n; i++) { - GLuint name = first + i; - framebuffers[i] = name; - /* insert dummy placeholder into hash table */ - _glthread_LOCK_MUTEX(ctx->Shared->Mutex); - _mesa_HashInsert(ctx->Shared->FrameBuffers, name, &DummyFramebuffer); - _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); - } -} - - - -GLenum GLAPIENTRY -_mesa_CheckFramebufferStatusEXT(GLenum target) -{ - struct gl_framebuffer *buffer; - GET_CURRENT_CONTEXT(ctx); - - ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0); - - switch (target) { -#if FEATURE_EXT_framebuffer_blit - case GL_DRAW_FRAMEBUFFER_EXT: - if (!ctx->Extensions.EXT_framebuffer_blit) { - _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)"); - return 0; - } - buffer = ctx->DrawBuffer; - break; - case GL_READ_FRAMEBUFFER_EXT: - if (!ctx->Extensions.EXT_framebuffer_blit) { - _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)"); - return 0; - } - buffer = ctx->ReadBuffer; - break; -#endif - case GL_FRAMEBUFFER_EXT: - buffer = ctx->DrawBuffer; - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)"); - return 0; /* formerly GL_FRAMEBUFFER_STATUS_ERROR_EXT */ - } - - if (buffer->Name == 0) { - /* The window system / default framebuffer is always complete */ - return GL_FRAMEBUFFER_COMPLETE_EXT; - } - - /* No need to flush here */ - - if (buffer->_Status != GL_FRAMEBUFFER_COMPLETE) { - _mesa_test_framebuffer_completeness(ctx, buffer); - } - - return buffer->_Status; -} - - - -/** - * Common code called by glFramebufferTexture1D/2D/3DEXT(). - */ -static void -framebuffer_texture(struct gl_context *ctx, const char *caller, GLenum target, - GLenum attachment, GLenum textarget, GLuint texture, - GLint level, GLint zoffset) -{ - struct gl_renderbuffer_attachment *att; - struct gl_texture_object *texObj = NULL; - struct gl_framebuffer *fb; - GLboolean error = GL_FALSE; - - ASSERT_OUTSIDE_BEGIN_END(ctx); - - switch (target) { - case GL_READ_FRAMEBUFFER_EXT: - error = !ctx->Extensions.EXT_framebuffer_blit; - fb = ctx->ReadBuffer; - break; - case GL_DRAW_FRAMEBUFFER_EXT: - error = !ctx->Extensions.EXT_framebuffer_blit; - /* fall-through */ - case GL_FRAMEBUFFER_EXT: - fb = ctx->DrawBuffer; - break; - default: - error = GL_TRUE; - } - - if (error) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture%sEXT(target=0x%x)", caller, target); - return; - } - - ASSERT(fb); - - /* check framebuffer binding */ - if (fb->Name == 0) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture%sEXT", caller); - return; - } - - - /* The textarget, level, and zoffset parameters are only validated if - * texture is non-zero. - */ - if (texture) { - GLboolean err = GL_TRUE; - - texObj = _mesa_lookup_texture(ctx, texture); - if (texObj != NULL) { - if (textarget == 0) { - /* XXX what's the purpose of this? */ - err = (texObj->Target != GL_TEXTURE_3D) && - (texObj->Target != GL_TEXTURE_1D_ARRAY_EXT) && - (texObj->Target != GL_TEXTURE_2D_ARRAY_EXT); - } - else { - err = (texObj->Target == GL_TEXTURE_CUBE_MAP) - ? !IS_CUBE_FACE(textarget) - : (texObj->Target != textarget); - } - } - else { - /* can't render to a non-existant texture */ - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture%sEXT(non existant texture)", - caller); - return; - } - - if (err) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture%sEXT(texture target mismatch)", - caller); - return; - } - - if (texObj->Target == GL_TEXTURE_3D) { - const GLint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1); - if (zoffset < 0 || zoffset >= maxSize) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferTexture%sEXT(zoffset)", caller); - return; - } - } - else if ((texObj->Target == GL_TEXTURE_1D_ARRAY_EXT) || - (texObj->Target == GL_TEXTURE_2D_ARRAY_EXT)) { - if (zoffset < 0 || zoffset >= ctx->Const.MaxArrayTextureLayers) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferTexture%sEXT(layer)", caller); - return; - } - } - - if ((level < 0) || - (level >= _mesa_max_texture_levels(ctx, texObj->Target))) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferTexture%sEXT(level)", caller); - return; - } - } - - att = _mesa_get_attachment(ctx, fb, attachment); - if (att == NULL) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture%sEXT(attachment)", caller); - return; - } - - FLUSH_VERTICES(ctx, _NEW_BUFFERS); - - _glthread_LOCK_MUTEX(fb->Mutex); - if (texObj) { - _mesa_set_texture_attachment(ctx, fb, att, texObj, textarget, - level, zoffset); - /* Set the render-to-texture flag. We'll check this flag in - * glTexImage() and friends to determine if we need to revalidate - * any FBOs that might be rendering into this texture. - * This flag never gets cleared since it's non-trivial to determine - * when all FBOs might be done rendering to this texture. That's OK - * though since it's uncommon to render to a texture then repeatedly - * call glTexImage() to change images in the texture. - */ - texObj->_RenderToTexture = GL_TRUE; - } - else { - _mesa_remove_attachment(ctx, att); - } - - invalidate_framebuffer(fb); - - _glthread_UNLOCK_MUTEX(fb->Mutex); -} - - - -void GLAPIENTRY -_mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment, - GLenum textarget, GLuint texture, GLint level) -{ - GET_CURRENT_CONTEXT(ctx); - - if ((texture != 0) && (textarget != GL_TEXTURE_1D)) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture1DEXT(textarget)"); - return; - } - - framebuffer_texture(ctx, "1D", target, attachment, textarget, texture, - level, 0); -} - - -void GLAPIENTRY -_mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment, - GLenum textarget, GLuint texture, GLint level) -{ - GET_CURRENT_CONTEXT(ctx); - - if ((texture != 0) && - (textarget != GL_TEXTURE_2D) && - (textarget != GL_TEXTURE_RECTANGLE_ARB) && - (!IS_CUBE_FACE(textarget))) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTexture2DEXT(textarget=0x%x)", textarget); - return; - } - - framebuffer_texture(ctx, "2D", target, attachment, textarget, texture, - level, 0); -} - - -void GLAPIENTRY -_mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment, - GLenum textarget, GLuint texture, - GLint level, GLint zoffset) -{ - GET_CURRENT_CONTEXT(ctx); - - if ((texture != 0) && (textarget != GL_TEXTURE_3D)) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferTexture3DEXT(textarget)"); - return; - } - - framebuffer_texture(ctx, "3D", target, attachment, textarget, texture, - level, zoffset); -} - - -void GLAPIENTRY -_mesa_FramebufferTextureLayerEXT(GLenum target, GLenum attachment, - GLuint texture, GLint level, GLint layer) -{ - GET_CURRENT_CONTEXT(ctx); - - framebuffer_texture(ctx, "Layer", target, attachment, 0, texture, - level, layer); -} - - -void GLAPIENTRY -_mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment, - GLenum renderbufferTarget, - GLuint renderbuffer) -{ - struct gl_renderbuffer_attachment *att; - struct gl_framebuffer *fb; - struct gl_renderbuffer *rb; - GET_CURRENT_CONTEXT(ctx); - - ASSERT_OUTSIDE_BEGIN_END(ctx); - - switch (target) { -#if FEATURE_EXT_framebuffer_blit - case GL_DRAW_FRAMEBUFFER_EXT: - if (!ctx->Extensions.EXT_framebuffer_blit) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferRenderbufferEXT(target)"); - return; - } - fb = ctx->DrawBuffer; - break; - case GL_READ_FRAMEBUFFER_EXT: - if (!ctx->Extensions.EXT_framebuffer_blit) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferRenderbufferEXT(target)"); - return; - } - fb = ctx->ReadBuffer; - break; -#endif - case GL_FRAMEBUFFER_EXT: - fb = ctx->DrawBuffer; - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferRenderbufferEXT(target)"); - return; - } - - if (renderbufferTarget != GL_RENDERBUFFER_EXT) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferRenderbufferEXT(renderbufferTarget)"); - return; - } - - if (fb->Name == 0) { - /* Can't attach new renderbuffers to a window system framebuffer */ - _mesa_error(ctx, GL_INVALID_OPERATION, "glFramebufferRenderbufferEXT"); - return; - } - - att = _mesa_get_attachment(ctx, fb, attachment); - if (att == NULL) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glFramebufferRenderbufferEXT(invalid attachment %s)", - _mesa_lookup_enum_by_nr(attachment)); - return; - } - - if (renderbuffer) { - rb = _mesa_lookup_renderbuffer(ctx, renderbuffer); - if (!rb) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferRenderbufferEXT(non-existant" - " renderbuffer %u)", renderbuffer); - return; - } - else if (rb == &DummyRenderbuffer) { - /* This is what NVIDIA does */ - _mesa_error(ctx, GL_INVALID_VALUE, - "glFramebufferRenderbufferEXT(renderbuffer %u)", - renderbuffer); - return; - } - } - else { - /* remove renderbuffer attachment */ - rb = NULL; - } - - if (attachment == GL_DEPTH_STENCIL_ATTACHMENT && - rb && rb->Format != MESA_FORMAT_NONE) { - /* make sure the renderbuffer is a depth/stencil format */ - const GLenum baseFormat = _mesa_get_format_base_format(rb->Format); - if (baseFormat != GL_DEPTH_STENCIL) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferRenderbufferEXT(renderbuffer" - " is not DEPTH_STENCIL format)"); - return; - } - } - - - FLUSH_VERTICES(ctx, _NEW_BUFFERS); - - assert(ctx->Driver.FramebufferRenderbuffer); - ctx->Driver.FramebufferRenderbuffer(ctx, fb, attachment, rb); - - /* Some subsequent GL commands may depend on the framebuffer's visual - * after the binding is updated. Update visual info now. - */ - _mesa_update_framebuffer_visual(ctx, fb); -} - - -void GLAPIENTRY -_mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, - GLenum pname, GLint *params) -{ - const struct gl_renderbuffer_attachment *att; - struct gl_framebuffer *buffer; - GET_CURRENT_CONTEXT(ctx); - - ASSERT_OUTSIDE_BEGIN_END(ctx); - - switch (target) { -#if FEATURE_EXT_framebuffer_blit - case GL_DRAW_FRAMEBUFFER_EXT: - if (!ctx->Extensions.EXT_framebuffer_blit) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(target)"); - return; - } - buffer = ctx->DrawBuffer; - break; - case GL_READ_FRAMEBUFFER_EXT: - if (!ctx->Extensions.EXT_framebuffer_blit) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(target)"); - return; - } - buffer = ctx->ReadBuffer; - break; -#endif - case GL_FRAMEBUFFER_EXT: - buffer = ctx->DrawBuffer; - break; - default: - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(target)"); - return; - } - - if (buffer->Name == 0) { - /* the default / window-system FBO */ - att = _mesa_get_fb0_attachment(ctx, buffer, attachment); - } - else { - /* user-created framebuffer FBO */ - att = _mesa_get_attachment(ctx, buffer, attachment); - } - - if (att == NULL) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(attachment)"); - return; - } - - if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { - /* the depth and stencil attachments must point to the same buffer */ - const struct gl_renderbuffer_attachment *depthAtt, *stencilAtt; - depthAtt = _mesa_get_attachment(ctx, buffer, GL_DEPTH_ATTACHMENT); - stencilAtt = _mesa_get_attachment(ctx, buffer, GL_STENCIL_ATTACHMENT); - if (depthAtt->Renderbuffer != stencilAtt->Renderbuffer) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetFramebufferAttachmentParameterivEXT(DEPTH/STENCIL" - " attachments differ)"); - return; - } - } - - /* No need to flush here */ - - switch (pname) { - case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: - *params = buffer->Name == 0 ? GL_FRAMEBUFFER_DEFAULT : att->Type; - return; - case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT: - if (att->Type == GL_RENDERBUFFER_EXT) { - *params = att->Renderbuffer->Name; - } - else if (att->Type == GL_TEXTURE) { - *params = att->Texture->Name; - } - else { - assert(att->Type == GL_NONE); - *params = 0; - } - return; - case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT: - if (att->Type == GL_TEXTURE) { - *params = att->TextureLevel; - } - else { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(pname)"); - } - return; - case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT: - if (att->Type == GL_TEXTURE) { - if (att->Texture && att->Texture->Target == GL_TEXTURE_CUBE_MAP) { - *params = GL_TEXTURE_CUBE_MAP_POSITIVE_X + att->CubeMapFace; - } - else { - *params = 0; - } - } - else { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(pname)"); - } - return; - case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT: - if (att->Type == GL_TEXTURE) { - if (att->Texture && att->Texture->Target == GL_TEXTURE_3D) { - *params = att->Zoffset; - } - else { - *params = 0; - } - } - else { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(pname)"); - } - return; - case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: - if (!ctx->Extensions.ARB_framebuffer_object) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(pname)"); - } - else { - if (ctx->Extensions.EXT_framebuffer_sRGB) { - *params = _mesa_get_format_color_encoding(att->Renderbuffer->Format); - } - else { - /* According to ARB_framebuffer_sRGB, we should return LINEAR - * if the sRGB conversion is unsupported. */ - *params = GL_LINEAR; - } - } - return; - case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: - if (!ctx->Extensions.ARB_framebuffer_object) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(pname)"); - return; - } - else { - gl_format format = att->Renderbuffer->Format; - if (format == MESA_FORMAT_CI8 || format == MESA_FORMAT_S8) { - /* special cases */ - *params = GL_INDEX; - } - else { - *params = _mesa_get_format_datatype(format); - } - } - return; - case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: - case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: - case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: - case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: - case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: - case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: - if (!ctx->Extensions.ARB_framebuffer_object) { - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(pname)"); - } - else if (att->Texture) { - const struct gl_texture_image *texImage = - _mesa_select_tex_image(ctx, att->Texture, att->Texture->Target, - att->TextureLevel); - if (texImage) { - *params = get_component_bits(pname, texImage->_BaseFormat, - texImage->TexFormat); - } - else { - *params = 0; - } - } - else if (att->Renderbuffer) { - *params = get_component_bits(pname, att->Renderbuffer->_BaseFormat, - att->Renderbuffer->Format); - } - else { - *params = 0; - } - return; - default: - _mesa_error(ctx, GL_INVALID_ENUM, - "glGetFramebufferAttachmentParameterivEXT(pname)"); - return; - } -} - - -void GLAPIENTRY -_mesa_GenerateMipmapEXT(GLenum target) -{ - struct gl_texture_object *texObj; - GET_CURRENT_CONTEXT(ctx); - - ASSERT_OUTSIDE_BEGIN_END(ctx); - FLUSH_VERTICES(ctx, _NEW_BUFFERS); - - switch (target) { - case GL_TEXTURE_1D: - case GL_TEXTURE_2D: - case GL_TEXTURE_3D: - case GL_TEXTURE_CUBE_MAP: - /* OK, legal value */ - break; - default: - /* XXX need to implement GL_TEXTURE_1D_ARRAY and GL_TEXTURE_2D_ARRAY */ - _mesa_error(ctx, GL_INVALID_ENUM, "glGenerateMipmapEXT(target)"); - return; - } - - texObj = _mesa_get_current_tex_object(ctx, target); - - if (texObj->BaseLevel >= texObj->MaxLevel) { - /* nothing to do */ - return; - } - - if (texObj->Target == GL_TEXTURE_CUBE_MAP && - !_mesa_cube_complete(texObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGenerateMipmap(incomplete cube map)"); - return; - } - - _mesa_lock_texture(ctx, texObj); - if (target == GL_TEXTURE_CUBE_MAP) { - GLuint face; - for (face = 0; face < 6; face++) - ctx->Driver.GenerateMipmap(ctx, - GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + face, - texObj); - } - else { - ctx->Driver.GenerateMipmap(ctx, target, texObj); - } - _mesa_unlock_texture(ctx, texObj); -} - - -#if FEATURE_EXT_framebuffer_blit - -static const struct gl_renderbuffer_attachment * -find_attachment(const struct gl_framebuffer *fb, - const struct gl_renderbuffer *rb) -{ - GLuint i; - for (i = 0; i < Elements(fb->Attachment); i++) { - if (fb->Attachment[i].Renderbuffer == rb) - return &fb->Attachment[i]; - } - return NULL; -} - - - -/** - * Blit rectangular region, optionally from one framebuffer to another. - * - * Note, if the src buffer is multisampled and the dest is not, this is - * when the samples must be resolved to a single color. - */ -void GLAPIENTRY -_mesa_BlitFramebufferEXT(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, - GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, - GLbitfield mask, GLenum filter) -{ - const GLbitfield legalMaskBits = (GL_COLOR_BUFFER_BIT | - GL_DEPTH_BUFFER_BIT | - GL_STENCIL_BUFFER_BIT); - const struct gl_framebuffer *readFb, *drawFb; - const struct gl_renderbuffer *colorReadRb, *colorDrawRb; - GET_CURRENT_CONTEXT(ctx); - - ASSERT_OUTSIDE_BEGIN_END(ctx); - FLUSH_VERTICES(ctx, _NEW_BUFFERS); - - if (ctx->NewState) { - _mesa_update_state(ctx); - } - - readFb = ctx->ReadBuffer; - drawFb = ctx->DrawBuffer; - - if (!readFb || !drawFb) { - /* This will normally never happen but someday we may want to - * support MakeCurrent() with no drawables. - */ - return; - } - - /* check for complete framebuffers */ - if (drawFb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT || - readFb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { - _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT, - "glBlitFramebufferEXT(incomplete draw/read buffers)"); - return; - } - - if (filter != GL_NEAREST && filter != GL_LINEAR) { - _mesa_error(ctx, GL_INVALID_ENUM, "glBlitFramebufferEXT(filter)"); - return; - } - - if (mask & ~legalMaskBits) { - _mesa_error( ctx, GL_INVALID_VALUE, "glBlitFramebufferEXT(mask)"); - return; - } - - /* depth/stencil must be blitted with nearest filtering */ - if ((mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) - && filter != GL_NEAREST) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(depth/stencil requires GL_NEAREST filter"); - return; - } - - /* get color read/draw renderbuffers */ - if (mask & GL_COLOR_BUFFER_BIT) { - colorReadRb = readFb->_ColorReadBuffer; - colorDrawRb = drawFb->_ColorDrawBuffers[0]; - } - else { - colorReadRb = colorDrawRb = NULL; - } - - if (mask & GL_STENCIL_BUFFER_BIT) { - struct gl_renderbuffer *readRb = readFb->_StencilBuffer; - struct gl_renderbuffer *drawRb = drawFb->_StencilBuffer; - if (!readRb || - !drawRb || - _mesa_get_format_bits(readRb->Format, GL_STENCIL_BITS) != - _mesa_get_format_bits(drawRb->Format, GL_STENCIL_BITS)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(stencil buffer size mismatch"); - return; - } - } - - if (mask & GL_DEPTH_BUFFER_BIT) { - struct gl_renderbuffer *readRb = readFb->_DepthBuffer; - struct gl_renderbuffer *drawRb = drawFb->_DepthBuffer; - if (!readRb || - !drawRb || - _mesa_get_format_bits(readRb->Format, GL_DEPTH_BITS) != - _mesa_get_format_bits(drawRb->Format, GL_DEPTH_BITS)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(depth buffer size mismatch"); - return; - } - } - - if (readFb->Visual.samples > 0 && - drawFb->Visual.samples > 0 && - readFb->Visual.samples != drawFb->Visual.samples) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(mismatched samples"); - return; - } - - /* extra checks for multisample copies... */ - if (readFb->Visual.samples > 0 || drawFb->Visual.samples > 0) { - /* src and dest region sizes must be the same */ - if (srcX1 - srcX0 != dstX1 - dstX0 || - srcY1 - srcY0 != dstY1 - dstY0) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(bad src/dst multisample region sizes"); - return; - } - - /* color formats must match */ - if (colorReadRb && - colorDrawRb && - colorReadRb->Format != colorDrawRb->Format) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBlitFramebufferEXT(bad src/dst multisample pixel formats"); - return; - } - } - - if (!ctx->Extensions.EXT_framebuffer_blit) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glBlitFramebufferEXT"); - return; - } - - /* Debug code */ - if (DEBUG_BLIT) { - printf("glBlitFramebuffer(%d, %d, %d, %d, %d, %d, %d, %d," - " 0x%x, 0x%x)\n", - srcX0, srcY0, srcX1, srcY1, - dstX0, dstY0, dstX1, dstY1, - mask, filter); - if (colorReadRb) { - const struct gl_renderbuffer_attachment *att; - - att = find_attachment(readFb, colorReadRb); - printf(" Src FBO %u RB %u (%dx%d) ", - readFb->Name, colorReadRb->Name, - colorReadRb->Width, colorReadRb->Height); - if (att && att->Texture) { - printf("Tex %u tgt 0x%x level %u face %u", - att->Texture->Name, - att->Texture->Target, - att->TextureLevel, - att->CubeMapFace); - } - printf("\n"); - - att = find_attachment(drawFb, colorDrawRb); - printf(" Dst FBO %u RB %u (%dx%d) ", - drawFb->Name, colorDrawRb->Name, - colorDrawRb->Width, colorDrawRb->Height); - if (att && att->Texture) { - printf("Tex %u tgt 0x%x level %u face %u", - att->Texture->Name, - att->Texture->Target, - att->TextureLevel, - att->CubeMapFace); - } - printf("\n"); - } - } - - ASSERT(ctx->Driver.BlitFramebuffer); - ctx->Driver.BlitFramebuffer(ctx, - srcX0, srcY0, srcX1, srcY1, - dstX0, dstY0, dstX1, dstY1, - mask, filter); -} -#endif /* FEATURE_EXT_framebuffer_blit */ - -#if FEATURE_ARB_geometry_shader4 -void GLAPIENTRY -_mesa_FramebufferTextureARB(GLenum target, GLenum attachment, - GLuint texture, GLint level) -{ - GET_CURRENT_CONTEXT(ctx); - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTextureARB " - "not implemented!"); -} - -void GLAPIENTRY -_mesa_FramebufferTextureFaceARB(GLenum target, GLenum attachment, - GLuint texture, GLint level, GLenum face) -{ - GET_CURRENT_CONTEXT(ctx); - _mesa_error(ctx, GL_INVALID_OPERATION, - "glFramebufferTextureFaceARB " - "not implemented!"); -} -#endif /* FEATURE_ARB_geometry_shader4 */ +/* + * Mesa 3-D graphics library + * Version: 7.1 + * + * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. + * Copyright (C) 1999-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. + */ + + +/* + * GL_EXT/ARB_framebuffer_object extensions + * + * Authors: + * Brian Paul + */ + + +#include "buffers.h" +#include "context.h" +#include "enums.h" +#include "fbobject.h" +#include "formats.h" +#include "framebuffer.h" +#include "hash.h" +#include "macros.h" +#include "mfeatures.h" +#include "mtypes.h" +#include "renderbuffer.h" +#include "state.h" +#include "teximage.h" +#include "texobj.h" + + +/** Set this to 1 to help debug FBO incompleteness problems */ +#define DEBUG_FBO 0 + +/** Set this to 1 to debug/log glBlitFramebuffer() calls */ +#define DEBUG_BLIT 0 + + +/** + * Notes: + * + * None of the GL_EXT_framebuffer_object functions are compiled into + * display lists. + */ + + + +/* + * When glGenRender/FramebuffersEXT() is called we insert pointers to + * these placeholder objects into the hash table. + * Later, when the object ID is first bound, we replace the placeholder + * with the real frame/renderbuffer. + */ +static struct gl_framebuffer DummyFramebuffer; +static struct gl_renderbuffer DummyRenderbuffer; + +/* We bind this framebuffer when applications pass a NULL + * drawable/surface in make current. */ +static struct gl_framebuffer IncompleteFramebuffer; + + +#define IS_CUBE_FACE(TARGET) \ + ((TARGET) >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && \ + (TARGET) <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) + + +static void +delete_dummy_renderbuffer(struct gl_renderbuffer *rb) +{ + /* no op */ +} + +static void +delete_dummy_framebuffer(struct gl_framebuffer *fb) +{ + /* no op */ +} + + +void +_mesa_init_fbobjects(struct gl_context *ctx) +{ + _glthread_INIT_MUTEX(DummyFramebuffer.Mutex); + _glthread_INIT_MUTEX(DummyRenderbuffer.Mutex); + _glthread_INIT_MUTEX(IncompleteFramebuffer.Mutex); + DummyFramebuffer.Delete = delete_dummy_framebuffer; + DummyRenderbuffer.Delete = delete_dummy_renderbuffer; + IncompleteFramebuffer.Delete = delete_dummy_framebuffer; +} + +struct gl_framebuffer * +_mesa_get_incomplete_framebuffer(void) +{ + return &IncompleteFramebuffer; +} + +/** + * Helper routine for getting a gl_renderbuffer. + */ +struct gl_renderbuffer * +_mesa_lookup_renderbuffer(struct gl_context *ctx, GLuint id) +{ + struct gl_renderbuffer *rb; + + if (id == 0) + return NULL; + + rb = (struct gl_renderbuffer *) + _mesa_HashLookup(ctx->Shared->RenderBuffers, id); + return rb; +} + + +/** + * Helper routine for getting a gl_framebuffer. + */ +struct gl_framebuffer * +_mesa_lookup_framebuffer(struct gl_context *ctx, GLuint id) +{ + struct gl_framebuffer *fb; + + if (id == 0) + return NULL; + + fb = (struct gl_framebuffer *) + _mesa_HashLookup(ctx->Shared->FrameBuffers, id); + return fb; +} + + +/** + * Mark the given framebuffer as invalid. This will force the + * test for framebuffer completeness to be done before the framebuffer + * is used. + */ +static void +invalidate_framebuffer(struct gl_framebuffer *fb) +{ + fb->_Status = 0; /* "indeterminate" */ +} + + +/** + * Given a GL_*_ATTACHMENTn token, return a pointer to the corresponding + * gl_renderbuffer_attachment object. + * This function is only used for user-created FB objects, not the + * default / window-system FB object. + * If \p attachment is GL_DEPTH_STENCIL_ATTACHMENT, return a pointer to + * the depth buffer attachment point. + */ +struct gl_renderbuffer_attachment * +_mesa_get_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, + GLenum attachment) +{ + GLuint i; + + assert(fb->Name > 0); + + switch (attachment) { + case GL_COLOR_ATTACHMENT0_EXT: + case GL_COLOR_ATTACHMENT1_EXT: + case GL_COLOR_ATTACHMENT2_EXT: + case GL_COLOR_ATTACHMENT3_EXT: + case GL_COLOR_ATTACHMENT4_EXT: + case GL_COLOR_ATTACHMENT5_EXT: + case GL_COLOR_ATTACHMENT6_EXT: + case GL_COLOR_ATTACHMENT7_EXT: + case GL_COLOR_ATTACHMENT8_EXT: + case GL_COLOR_ATTACHMENT9_EXT: + case GL_COLOR_ATTACHMENT10_EXT: + case GL_COLOR_ATTACHMENT11_EXT: + case GL_COLOR_ATTACHMENT12_EXT: + case GL_COLOR_ATTACHMENT13_EXT: + case GL_COLOR_ATTACHMENT14_EXT: + case GL_COLOR_ATTACHMENT15_EXT: + i = attachment - GL_COLOR_ATTACHMENT0_EXT; + if (i >= ctx->Const.MaxColorAttachments) { + return NULL; + } + return &fb->Attachment[BUFFER_COLOR0 + i]; + case GL_DEPTH_STENCIL_ATTACHMENT: + /* fall-through */ + case GL_DEPTH_BUFFER: + /* fall-through / new in GL 3.0 */ + case GL_DEPTH_ATTACHMENT_EXT: + return &fb->Attachment[BUFFER_DEPTH]; + case GL_STENCIL_BUFFER: + /* fall-through / new in GL 3.0 */ + case GL_STENCIL_ATTACHMENT_EXT: + return &fb->Attachment[BUFFER_STENCIL]; + default: + return NULL; + } +} + + +/** + * As above, but only used for getting attachments of the default / + * window-system framebuffer (not user-created framebuffer objects). + */ +static struct gl_renderbuffer_attachment * +_mesa_get_fb0_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, + GLenum attachment) +{ + assert(fb->Name == 0); + + switch (attachment) { + case GL_FRONT_LEFT: + return &fb->Attachment[BUFFER_FRONT_LEFT]; + case GL_FRONT_RIGHT: + return &fb->Attachment[BUFFER_FRONT_RIGHT]; + case GL_BACK_LEFT: + return &fb->Attachment[BUFFER_BACK_LEFT]; + case GL_BACK_RIGHT: + return &fb->Attachment[BUFFER_BACK_RIGHT]; + case GL_AUX0: + if (fb->Visual.numAuxBuffers == 1) { + return &fb->Attachment[BUFFER_AUX0]; + } + return NULL; + case GL_DEPTH_BUFFER: + /* fall-through / new in GL 3.0 */ + case GL_DEPTH_ATTACHMENT_EXT: + return &fb->Attachment[BUFFER_DEPTH]; + case GL_STENCIL_BUFFER: + /* fall-through / new in GL 3.0 */ + case GL_STENCIL_ATTACHMENT_EXT: + return &fb->Attachment[BUFFER_STENCIL]; + default: + return NULL; + } +} + + + +/** + * Remove any texture or renderbuffer attached to the given attachment + * point. Update reference counts, etc. + */ +void +_mesa_remove_attachment(struct gl_context *ctx, + struct gl_renderbuffer_attachment *att) +{ + if (att->Type == GL_TEXTURE) { + ASSERT(att->Texture); + if (ctx->Driver.FinishRenderTexture) { + /* tell driver that we're done rendering to this texture. */ + ctx->Driver.FinishRenderTexture(ctx, att); + } + _mesa_reference_texobj(&att->Texture, NULL); /* unbind */ + ASSERT(!att->Texture); + } + if (att->Type == GL_TEXTURE || att->Type == GL_RENDERBUFFER_EXT) { + ASSERT(!att->Texture); + _mesa_reference_renderbuffer(&att->Renderbuffer, NULL); /* unbind */ + ASSERT(!att->Renderbuffer); + } + att->Type = GL_NONE; + att->Complete = GL_TRUE; +} + + +/** + * Bind a texture object to an attachment point. + * The previous binding, if any, will be removed first. + */ +void +_mesa_set_texture_attachment(struct gl_context *ctx, + struct gl_framebuffer *fb, + struct gl_renderbuffer_attachment *att, + struct gl_texture_object *texObj, + GLenum texTarget, GLuint level, GLuint zoffset) +{ + if (att->Texture == texObj) { + /* re-attaching same texture */ + ASSERT(att->Type == GL_TEXTURE); + if (ctx->Driver.FinishRenderTexture) + ctx->Driver.FinishRenderTexture(ctx, att); + } + else { + /* new attachment */ + if (ctx->Driver.FinishRenderTexture && att->Texture) + ctx->Driver.FinishRenderTexture(ctx, att); + _mesa_remove_attachment(ctx, att); + att->Type = GL_TEXTURE; + assert(!att->Texture); + _mesa_reference_texobj(&att->Texture, texObj); + } + + /* always update these fields */ + att->TextureLevel = level; + att->CubeMapFace = _mesa_tex_target_to_face(texTarget); + att->Zoffset = zoffset; + att->Complete = GL_FALSE; + + if (att->Texture->Image[att->CubeMapFace][att->TextureLevel]) { + ctx->Driver.RenderTexture(ctx, fb, att); + } + + invalidate_framebuffer(fb); +} + + +/** + * Bind a renderbuffer to an attachment point. + * The previous binding, if any, will be removed first. + */ +void +_mesa_set_renderbuffer_attachment(struct gl_context *ctx, + struct gl_renderbuffer_attachment *att, + struct gl_renderbuffer *rb) +{ + /* XXX check if re-doing same attachment, exit early */ + _mesa_remove_attachment(ctx, att); + att->Type = GL_RENDERBUFFER_EXT; + att->Texture = NULL; /* just to be safe */ + att->Complete = GL_FALSE; + _mesa_reference_renderbuffer(&att->Renderbuffer, rb); +} + + +/** + * Fallback for ctx->Driver.FramebufferRenderbuffer() + * Attach a renderbuffer object to a framebuffer object. + */ +void +_mesa_framebuffer_renderbuffer(struct gl_context *ctx, + struct gl_framebuffer *fb, + GLenum attachment, struct gl_renderbuffer *rb) +{ + struct gl_renderbuffer_attachment *att; + + _glthread_LOCK_MUTEX(fb->Mutex); + + att = _mesa_get_attachment(ctx, fb, attachment); + ASSERT(att); + if (rb) { + _mesa_set_renderbuffer_attachment(ctx, att, rb); + if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { + /* do stencil attachment here (depth already done above) */ + att = _mesa_get_attachment(ctx, fb, GL_STENCIL_ATTACHMENT_EXT); + assert(att); + _mesa_set_renderbuffer_attachment(ctx, att, rb); + } + } + else { + _mesa_remove_attachment(ctx, att); + } + + invalidate_framebuffer(fb); + + _glthread_UNLOCK_MUTEX(fb->Mutex); +} + + +/** + * Fallback for ctx->Driver.ValidateFramebuffer() + * Check if the renderbuffer's formats are supported by the software + * renderer. + * Drivers should probably override this. + */ +void +_mesa_validate_framebuffer(struct gl_context *ctx, struct gl_framebuffer *fb) +{ + gl_buffer_index buf; + for (buf = 0; buf < BUFFER_COUNT; buf++) { + const struct gl_renderbuffer *rb = fb->Attachment[buf].Renderbuffer; + if (rb) { + switch (rb->_BaseFormat) { + case GL_ALPHA: + case GL_LUMINANCE_ALPHA: + case GL_LUMINANCE: + case GL_INTENSITY: + case GL_RED: + case GL_RG: + fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED; + return; + default: + /* render buffer format is supported by software rendering */ + ; + } + } + } +} + + +/** + * For debug only. + */ +static void +att_incomplete(const char *msg) +{ +#if DEBUG_FBO + _mesa_debug(NULL, "attachment incomplete: %s\n", msg); +#else + (void) msg; +#endif +} + + +/** + * For debug only. + */ +static void +fbo_incomplete(const char *msg, int index) +{ +#if DEBUG_FBO + _mesa_debug(NULL, "FBO Incomplete: %s [%d]\n", msg, index); +#else + (void) msg; + (void) index; +#endif +} + + +/** + * Is the given base format a legal format for a color renderbuffer? + */ +GLboolean +_mesa_is_legal_color_format(const struct gl_context *ctx, GLenum baseFormat) +{ + switch (baseFormat) { + case GL_RGB: + case GL_RGBA: + return GL_TRUE; + case GL_LUMINANCE: + case GL_LUMINANCE_ALPHA: + case GL_INTENSITY: + case GL_ALPHA: + return ctx->Extensions.ARB_framebuffer_object; + case GL_RED: + case GL_RG: + return ctx->Extensions.ARB_texture_rg; + default: + return GL_FALSE; + } +} + + +/** + * Is the given base format a legal format for a depth/stencil renderbuffer? + */ +static GLboolean +is_legal_depth_format(const struct gl_context *ctx, GLenum baseFormat) +{ + switch (baseFormat) { + case GL_DEPTH_COMPONENT: + case GL_DEPTH_STENCIL_EXT: + return GL_TRUE; + default: + return GL_FALSE; + } +} + + +/** + * Test if an attachment point is complete and update its Complete field. + * \param format if GL_COLOR, this is a color attachment point, + * if GL_DEPTH, this is a depth component attachment point, + * if GL_STENCIL, this is a stencil component attachment point. + */ +static void +test_attachment_completeness(const struct gl_context *ctx, GLenum format, + struct gl_renderbuffer_attachment *att) +{ + assert(format == GL_COLOR || format == GL_DEPTH || format == GL_STENCIL); + + /* assume complete */ + att->Complete = GL_TRUE; + + /* Look for reasons why the attachment might be incomplete */ + if (att->Type == GL_TEXTURE) { + const struct gl_texture_object *texObj = att->Texture; + struct gl_texture_image *texImage; + GLenum baseFormat; + + if (!texObj) { + att_incomplete("no texobj"); + att->Complete = GL_FALSE; + return; + } + + texImage = texObj->Image[att->CubeMapFace][att->TextureLevel]; + if (!texImage) { + att_incomplete("no teximage"); + att->Complete = GL_FALSE; + return; + } + if (texImage->Width < 1 || texImage->Height < 1) { + att_incomplete("teximage width/height=0"); + printf("texobj = %u\n", texObj->Name); + printf("level = %d\n", att->TextureLevel); + att->Complete = GL_FALSE; + return; + } + if (texObj->Target == GL_TEXTURE_3D && att->Zoffset >= texImage->Depth) { + att_incomplete("bad z offset"); + att->Complete = GL_FALSE; + return; + } + + baseFormat = _mesa_get_format_base_format(texImage->TexFormat); + + if (format == GL_COLOR) { + if (!_mesa_is_legal_color_format(ctx, baseFormat)) { + att_incomplete("bad format"); + att->Complete = GL_FALSE; + return; + } + if (_mesa_is_format_compressed(texImage->TexFormat)) { + att_incomplete("compressed internalformat"); + att->Complete = GL_FALSE; + return; + } + } + else if (format == GL_DEPTH) { + if (baseFormat == GL_DEPTH_COMPONENT) { + /* OK */ + } + else if (ctx->Extensions.EXT_packed_depth_stencil && + ctx->Extensions.ARB_depth_texture && + baseFormat == GL_DEPTH_STENCIL_EXT) { + /* OK */ + } + else { + att->Complete = GL_FALSE; + att_incomplete("bad depth format"); + return; + } + } + else { + ASSERT(format == GL_STENCIL); + if (ctx->Extensions.EXT_packed_depth_stencil && + ctx->Extensions.ARB_depth_texture && + baseFormat == GL_DEPTH_STENCIL_EXT) { + /* OK */ + } + else { + /* no such thing as stencil-only textures */ + att_incomplete("illegal stencil texture"); + att->Complete = GL_FALSE; + return; + } + } + } + else if (att->Type == GL_RENDERBUFFER_EXT) { + const GLenum baseFormat = + _mesa_get_format_base_format(att->Renderbuffer->Format); + + ASSERT(att->Renderbuffer); + if (!att->Renderbuffer->InternalFormat || + att->Renderbuffer->Width < 1 || + att->Renderbuffer->Height < 1) { + att_incomplete("0x0 renderbuffer"); + att->Complete = GL_FALSE; + return; + } + if (format == GL_COLOR) { + if (!_mesa_is_legal_color_format(ctx, baseFormat)) { + att_incomplete("bad renderbuffer color format"); + att->Complete = GL_FALSE; + return; + } + } + else if (format == GL_DEPTH) { + if (baseFormat == GL_DEPTH_COMPONENT) { + /* OK */ + } + else if (ctx->Extensions.EXT_packed_depth_stencil && + baseFormat == GL_DEPTH_STENCIL_EXT) { + /* OK */ + } + else { + att_incomplete("bad renderbuffer depth format"); + att->Complete = GL_FALSE; + return; + } + } + else { + assert(format == GL_STENCIL); + if (baseFormat == GL_STENCIL_INDEX) { + /* OK */ + } + else if (ctx->Extensions.EXT_packed_depth_stencil && + baseFormat == GL_DEPTH_STENCIL_EXT) { + /* OK */ + } + else { + att->Complete = GL_FALSE; + att_incomplete("bad renderbuffer stencil format"); + return; + } + } + } + else { + ASSERT(att->Type == GL_NONE); + /* complete */ + return; + } +} + + +/** + * Test if the given framebuffer object is complete and update its + * Status field with the results. + * Calls the ctx->Driver.ValidateFramebuffer() function to allow the + * driver to make hardware-specific validation/completeness checks. + * Also update the framebuffer's Width and Height fields if the + * framebuffer is complete. + */ +void +_mesa_test_framebuffer_completeness(struct gl_context *ctx, + struct gl_framebuffer *fb) +{ + GLuint numImages; + GLenum intFormat = GL_NONE; /* color buffers' internal format */ + GLuint minWidth = ~0, minHeight = ~0, maxWidth = 0, maxHeight = 0; + GLint numSamples = -1; + GLint i; + GLuint j; + + assert(fb->Name != 0); + + numImages = 0; + fb->Width = 0; + fb->Height = 0; + + /* Start at -2 to more easily loop over all attachment points. + * -2: depth buffer + * -1: stencil buffer + * >=0: color buffer + */ + for (i = -2; i < (GLint) ctx->Const.MaxColorAttachments; i++) { + struct gl_renderbuffer_attachment *att; + GLenum f; + gl_format mesaFormat; + + /* + * XXX for ARB_fbo, only check color buffers that are named by + * GL_READ_BUFFER and GL_DRAW_BUFFERi. + */ + + /* check for attachment completeness + */ + if (i == -2) { + att = &fb->Attachment[BUFFER_DEPTH]; + test_attachment_completeness(ctx, GL_DEPTH, att); + if (!att->Complete) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT; + fbo_incomplete("depth attachment incomplete", -1); + return; + } + } + else if (i == -1) { + att = &fb->Attachment[BUFFER_STENCIL]; + test_attachment_completeness(ctx, GL_STENCIL, att); + if (!att->Complete) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT; + fbo_incomplete("stencil attachment incomplete", -1); + return; + } + } + else { + att = &fb->Attachment[BUFFER_COLOR0 + i]; + test_attachment_completeness(ctx, GL_COLOR, att); + if (!att->Complete) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT; + fbo_incomplete("color attachment incomplete", i); + return; + } + } + + /* get width, height, format of the renderbuffer/texture + */ + if (att->Type == GL_TEXTURE) { + const struct gl_texture_image *texImg + = att->Texture->Image[att->CubeMapFace][att->TextureLevel]; + minWidth = MIN2(minWidth, texImg->Width); + maxWidth = MAX2(maxWidth, texImg->Width); + minHeight = MIN2(minHeight, texImg->Height); + maxHeight = MAX2(maxHeight, texImg->Height); + f = texImg->_BaseFormat; + mesaFormat = texImg->TexFormat; + numImages++; + if (!_mesa_is_legal_color_format(ctx, f) && + !is_legal_depth_format(ctx, f)) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT; + fbo_incomplete("texture attachment incomplete", -1); + return; + } + } + else if (att->Type == GL_RENDERBUFFER_EXT) { + minWidth = MIN2(minWidth, att->Renderbuffer->Width); + maxWidth = MAX2(minWidth, att->Renderbuffer->Width); + minHeight = MIN2(minHeight, att->Renderbuffer->Height); + maxHeight = MAX2(minHeight, att->Renderbuffer->Height); + f = att->Renderbuffer->InternalFormat; + mesaFormat = att->Renderbuffer->Format; + numImages++; + } + else { + assert(att->Type == GL_NONE); + continue; + } + + if (numSamples < 0) { + /* first buffer */ + numSamples = att->Renderbuffer->NumSamples; + } + + /* check if integer color */ + fb->_IntegerColor = _mesa_is_format_integer_color(mesaFormat); + + /* Error-check width, height, format, samples + */ + if (numImages == 1) { + /* save format, num samples */ + if (i >= 0) { + intFormat = f; + } + } + else { + if (!ctx->Extensions.ARB_framebuffer_object) { + /* check that width, height, format are same */ + if (minWidth != maxWidth || minHeight != maxHeight) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT; + fbo_incomplete("width or height mismatch", -1); + return; + } + /* check that all color buffer have same format */ + if (intFormat != GL_NONE && f != intFormat) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT; + fbo_incomplete("format mismatch", -1); + return; + } + } + if (att->Renderbuffer && + att->Renderbuffer->NumSamples != numSamples) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; + fbo_incomplete("inconsistant number of samples", i); + return; + } + + } + } + +#if FEATURE_GL + if (ctx->API == API_OPENGL) { + /* Check that all DrawBuffers are present */ + for (j = 0; j < ctx->Const.MaxDrawBuffers; j++) { + if (fb->ColorDrawBuffer[j] != GL_NONE) { + const struct gl_renderbuffer_attachment *att + = _mesa_get_attachment(ctx, fb, fb->ColorDrawBuffer[j]); + assert(att); + if (att->Type == GL_NONE) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT; + fbo_incomplete("missing drawbuffer", j); + return; + } + } + } + + /* Check that the ReadBuffer is present */ + if (fb->ColorReadBuffer != GL_NONE) { + const struct gl_renderbuffer_attachment *att + = _mesa_get_attachment(ctx, fb, fb->ColorReadBuffer); + assert(att); + if (att->Type == GL_NONE) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT; + fbo_incomplete("missing readbuffer", -1); + return; + } + } + } +#else + (void) j; +#endif + + if (numImages == 0) { + fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT; + fbo_incomplete("no attachments", -1); + return; + } + + /* Provisionally set status = COMPLETE ... */ + fb->_Status = GL_FRAMEBUFFER_COMPLETE_EXT; + + /* ... but the driver may say the FB is incomplete. + * Drivers will most likely set the status to GL_FRAMEBUFFER_UNSUPPORTED + * if anything. + */ + if (ctx->Driver.ValidateFramebuffer) { + ctx->Driver.ValidateFramebuffer(ctx, fb); + if (fb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { + fbo_incomplete("driver marked FBO as incomplete", -1); + } + } + + if (fb->_Status == GL_FRAMEBUFFER_COMPLETE_EXT) { + /* + * Note that if ARB_framebuffer_object is supported and the attached + * renderbuffers/textures are different sizes, the framebuffer + * width/height will be set to the smallest width/height. + */ + fb->Width = minWidth; + fb->Height = minHeight; + + /* finally, update the visual info for the framebuffer */ + _mesa_update_framebuffer_visual(ctx, fb); + } +} + + +GLboolean GLAPIENTRY +_mesa_IsRenderbufferEXT(GLuint renderbuffer) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); + if (renderbuffer) { + struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, renderbuffer); + if (rb != NULL && rb != &DummyRenderbuffer) + return GL_TRUE; + } + return GL_FALSE; +} + + +void GLAPIENTRY +_mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer) +{ + struct gl_renderbuffer *newRb; + GET_CURRENT_CONTEXT(ctx); + + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (target != GL_RENDERBUFFER_EXT) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBindRenderbufferEXT(target)"); + return; + } + + /* No need to flush here since the render buffer binding has no + * effect on rendering state. + */ + + if (renderbuffer) { + newRb = _mesa_lookup_renderbuffer(ctx, renderbuffer); + if (newRb == &DummyRenderbuffer) { + /* ID was reserved, but no real renderbuffer object made yet */ + newRb = NULL; + } + else if (!newRb && ctx->Extensions.ARB_framebuffer_object) { + /* All RB IDs must be Gen'd */ + _mesa_error(ctx, GL_INVALID_OPERATION, "glBindRenderbuffer(buffer)"); + return; + } + + if (!newRb) { + /* create new renderbuffer object */ + newRb = ctx->Driver.NewRenderbuffer(ctx, renderbuffer); + if (!newRb) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindRenderbufferEXT"); + return; + } + ASSERT(newRb->AllocStorage); + _mesa_HashInsert(ctx->Shared->RenderBuffers, renderbuffer, newRb); + newRb->RefCount = 1; /* referenced by hash table */ + } + } + else { + newRb = NULL; + } + + ASSERT(newRb != &DummyRenderbuffer); + + _mesa_reference_renderbuffer(&ctx->CurrentRenderbuffer, newRb); +} + + +/** + * If the given renderbuffer is anywhere attached to the framebuffer, detach + * the renderbuffer. + * This is used when a renderbuffer object is deleted. + * The spec calls for unbinding. + */ +static void +detach_renderbuffer(struct gl_context *ctx, + struct gl_framebuffer *fb, + struct gl_renderbuffer *rb) +{ + GLuint i; + for (i = 0; i < BUFFER_COUNT; i++) { + if (fb->Attachment[i].Renderbuffer == rb) { + _mesa_remove_attachment(ctx, &fb->Attachment[i]); + } + } + invalidate_framebuffer(fb); +} + + +void GLAPIENTRY +_mesa_DeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers) +{ + GLint i; + GET_CURRENT_CONTEXT(ctx); + + ASSERT_OUTSIDE_BEGIN_END(ctx); + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + for (i = 0; i < n; i++) { + if (renderbuffers[i] > 0) { + struct gl_renderbuffer *rb; + rb = _mesa_lookup_renderbuffer(ctx, renderbuffers[i]); + if (rb) { + /* check if deleting currently bound renderbuffer object */ + if (rb == ctx->CurrentRenderbuffer) { + /* bind default */ + ASSERT(rb->RefCount >= 2); + _mesa_BindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + } + + if (ctx->DrawBuffer->Name) { + detach_renderbuffer(ctx, ctx->DrawBuffer, rb); + } + if (ctx->ReadBuffer->Name && ctx->ReadBuffer != ctx->DrawBuffer) { + detach_renderbuffer(ctx, ctx->ReadBuffer, rb); + } + + /* Remove from hash table immediately, to free the ID. + * But the object will not be freed until it's no longer + * referenced anywhere else. + */ + _mesa_HashRemove(ctx->Shared->RenderBuffers, renderbuffers[i]); + + if (rb != &DummyRenderbuffer) { + /* no longer referenced by hash table */ + _mesa_reference_renderbuffer(&rb, NULL); + } + } + } + } +} + + +void GLAPIENTRY +_mesa_GenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers) +{ + GET_CURRENT_CONTEXT(ctx); + GLuint first; + GLint i; + + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (n < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "glGenRenderbuffersEXT(n)"); + return; + } + + if (!renderbuffers) + return; + + first = _mesa_HashFindFreeKeyBlock(ctx->Shared->RenderBuffers, n); + + for (i = 0; i < n; i++) { + GLuint name = first + i; + renderbuffers[i] = name; + /* insert dummy placeholder into hash table */ + _glthread_LOCK_MUTEX(ctx->Shared->Mutex); + _mesa_HashInsert(ctx->Shared->RenderBuffers, name, &DummyRenderbuffer); + _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); + } +} + + +/** + * Given an internal format token for a render buffer, return the + * corresponding base format (one of GL_RGB, GL_RGBA, GL_STENCIL_INDEX, + * GL_DEPTH_COMPONENT, GL_DEPTH_STENCIL_EXT, GL_ALPHA, GL_LUMINANCE, + * GL_LUMINANCE_ALPHA, GL_INTENSITY, etc). + * + * This is similar to _mesa_base_tex_format() but the set of valid + * internal formats is different. + * + * Note that even if a format is determined to be legal here, validation + * of the FBO may fail if the format is not supported by the driver/GPU. + * + * \param internalFormat as passed to glRenderbufferStorage() + * \return the base internal format, or 0 if internalFormat is illegal + */ +GLenum +_mesa_base_fbo_format(struct gl_context *ctx, GLenum internalFormat) +{ + /* + * Notes: some formats such as alpha, luminance, etc. were added + * with GL_ARB_framebuffer_object. + */ + switch (internalFormat) { + case GL_ALPHA: + case GL_ALPHA4: + case GL_ALPHA8: + case GL_ALPHA12: + case GL_ALPHA16: + return ctx->Extensions.ARB_framebuffer_object ? GL_ALPHA : 0; + case GL_LUMINANCE: + case GL_LUMINANCE4: + case GL_LUMINANCE8: + case GL_LUMINANCE12: + case GL_LUMINANCE16: + return ctx->Extensions.ARB_framebuffer_object ? GL_LUMINANCE : 0; + case GL_LUMINANCE_ALPHA: + case GL_LUMINANCE4_ALPHA4: + case GL_LUMINANCE6_ALPHA2: + case GL_LUMINANCE8_ALPHA8: + case GL_LUMINANCE12_ALPHA4: + case GL_LUMINANCE12_ALPHA12: + case GL_LUMINANCE16_ALPHA16: + return ctx->Extensions.ARB_framebuffer_object ? GL_LUMINANCE_ALPHA : 0; + case GL_INTENSITY: + case GL_INTENSITY4: + case GL_INTENSITY8: + case GL_INTENSITY12: + case GL_INTENSITY16: + return ctx->Extensions.ARB_framebuffer_object ? GL_INTENSITY : 0; + case GL_RGB: + case GL_R3_G3_B2: + case GL_RGB4: + case GL_RGB5: + case GL_RGB8: + case GL_RGB10: + case GL_RGB12: + case GL_RGB16: + case GL_SRGB8_EXT: + return GL_RGB; + case GL_RGBA: + case GL_RGBA2: + case GL_RGBA4: + case GL_RGB5_A1: + case GL_RGBA8: + case GL_RGB10_A2: + case GL_RGBA12: + case GL_RGBA16: + case GL_RGBA16_SNORM: + case GL_SRGB8_ALPHA8_EXT: + return GL_RGBA; + case GL_STENCIL_INDEX: + case GL_STENCIL_INDEX1_EXT: + case GL_STENCIL_INDEX4_EXT: + case GL_STENCIL_INDEX8_EXT: + case GL_STENCIL_INDEX16_EXT: + return GL_STENCIL_INDEX; + case GL_DEPTH_COMPONENT: + case GL_DEPTH_COMPONENT16: + case GL_DEPTH_COMPONENT24: + case GL_DEPTH_COMPONENT32: + return GL_DEPTH_COMPONENT; + case GL_DEPTH_STENCIL_EXT: + case GL_DEPTH24_STENCIL8_EXT: + if (ctx->Extensions.EXT_packed_depth_stencil) + return GL_DEPTH_STENCIL_EXT; + else + return 0; + case GL_RED: + case GL_R8: + case GL_R16: + return ctx->Extensions.ARB_texture_rg ? GL_RED : 0; + case GL_RG: + case GL_RG8: + case GL_RG16: + return ctx->Extensions.ARB_texture_rg ? GL_RG : 0; + /* XXX add floating point and integer formats eventually */ + default: + return 0; + } +} + + +/** sentinal value, see below */ +#define NO_SAMPLES 1000 + + +/** + * Helper function used by _mesa_RenderbufferStorageEXT() and + * _mesa_RenderbufferStorageMultisample(). + * samples will be NO_SAMPLES if called by _mesa_RenderbufferStorageEXT(). + */ +static void +renderbuffer_storage(GLenum target, GLenum internalFormat, + GLsizei width, GLsizei height, GLsizei samples) +{ + const char *func = samples == NO_SAMPLES ? + "glRenderbufferStorage" : "RenderbufferStorageMultisample"; + struct gl_renderbuffer *rb; + GLenum baseFormat; + GET_CURRENT_CONTEXT(ctx); + + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (target != GL_RENDERBUFFER_EXT) { + _mesa_error(ctx, GL_INVALID_ENUM, "%s(target)", func); + return; + } + + baseFormat = _mesa_base_fbo_format(ctx, internalFormat); + if (baseFormat == 0) { + _mesa_error(ctx, GL_INVALID_ENUM, "%s(internalFormat)", func); + return; + } + + if (width < 1 || width > (GLsizei) ctx->Const.MaxRenderbufferSize) { + _mesa_error(ctx, GL_INVALID_VALUE, "%s(width)", func); + return; + } + + if (height < 1 || height > (GLsizei) ctx->Const.MaxRenderbufferSize) { + _mesa_error(ctx, GL_INVALID_VALUE, "%s(height)", func); + return; + } + + if (samples == NO_SAMPLES) { + /* NumSamples == 0 indicates non-multisampling */ + samples = 0; + } + else if (samples > (GLsizei) ctx->Const.MaxSamples) { + /* note: driver may choose to use more samples than what's requested */ + _mesa_error(ctx, GL_INVALID_VALUE, "%s(samples)", func); + return; + } + + rb = ctx->CurrentRenderbuffer; + if (!rb) { + _mesa_error(ctx, GL_INVALID_OPERATION, "%s", func); + return; + } + + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + if (rb->InternalFormat == internalFormat && + rb->Width == (GLuint) width && + rb->Height == (GLuint) height) { + /* no change in allocation needed */ + return; + } + + /* These MUST get set by the AllocStorage func */ + rb->Format = MESA_FORMAT_NONE; + rb->NumSamples = samples; + + /* Now allocate the storage */ + ASSERT(rb->AllocStorage); + if (rb->AllocStorage(ctx, rb, internalFormat, width, height)) { + /* No error - check/set fields now */ + assert(rb->Format != MESA_FORMAT_NONE); + assert(rb->Width == (GLuint) width); + assert(rb->Height == (GLuint) height); + rb->InternalFormat = internalFormat; + rb->_BaseFormat = baseFormat; + assert(rb->_BaseFormat != 0); + } + else { + /* Probably ran out of memory - clear the fields */ + rb->Width = 0; + rb->Height = 0; + rb->Format = MESA_FORMAT_NONE; + rb->InternalFormat = GL_NONE; + rb->_BaseFormat = GL_NONE; + rb->NumSamples = 0; + } + + /* + test_framebuffer_completeness(ctx, fb); + */ + /* XXX if this renderbuffer is attached anywhere, invalidate attachment + * points??? + */ +} + + +#if FEATURE_OES_EGL_image +void GLAPIENTRY +_mesa_EGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image) +{ + struct gl_renderbuffer *rb; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (!ctx->Extensions.OES_EGL_image) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glEGLImageTargetRenderbufferStorageOES(unsupported)"); + return; + } + + if (target != GL_RENDERBUFFER) { + _mesa_error(ctx, GL_INVALID_ENUM, + "EGLImageTargetRenderbufferStorageOES"); + return; + } + + rb = ctx->CurrentRenderbuffer; + if (!rb) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "EGLImageTargetRenderbufferStorageOES"); + return; + } + + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + ctx->Driver.EGLImageTargetRenderbufferStorage(ctx, rb, image); +} +#endif + + +/** + * Helper function for _mesa_GetRenderbufferParameterivEXT() and + * _mesa_GetFramebufferAttachmentParameterivEXT() + * We have to be careful to respect the base format. For example, if a + * renderbuffer/texture was created with internalFormat=GL_RGB but the + * driver actually chose a GL_RGBA format, when the user queries ALPHA_SIZE + * we need to return zero. + */ +static GLint +get_component_bits(GLenum pname, GLenum baseFormat, gl_format format) +{ + switch (pname) { + case GL_RENDERBUFFER_RED_SIZE_EXT: + case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: + if (baseFormat == GL_RGB || baseFormat == GL_RGBA || + baseFormat == GL_RG || baseFormat == GL_RED) + return _mesa_get_format_bits(format, pname); + else + return 0; + case GL_RENDERBUFFER_GREEN_SIZE_EXT: + case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: + if (baseFormat == GL_RGB || baseFormat == GL_RGBA || baseFormat == GL_RG) + return _mesa_get_format_bits(format, pname); + else + return 0; + case GL_RENDERBUFFER_BLUE_SIZE_EXT: + case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: + if (baseFormat == GL_RGB || baseFormat == GL_RGBA) + return _mesa_get_format_bits(format, pname); + else + return 0; + case GL_RENDERBUFFER_ALPHA_SIZE_EXT: + case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: + if (baseFormat == GL_RGBA || baseFormat == GL_ALPHA || + baseFormat == GL_LUMINANCE_ALPHA) + return _mesa_get_format_bits(format, pname); + else + return 0; + case GL_RENDERBUFFER_DEPTH_SIZE_EXT: + case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: + if (baseFormat == GL_DEPTH_COMPONENT || baseFormat == GL_DEPTH_STENCIL) + return _mesa_get_format_bits(format, pname); + else + return 0; + case GL_RENDERBUFFER_STENCIL_SIZE_EXT: + case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: + if (baseFormat == GL_STENCIL_INDEX || baseFormat == GL_DEPTH_STENCIL) + return _mesa_get_format_bits(format, pname); + else + return 0; + default: + return 0; + } +} + + + +void GLAPIENTRY +_mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat, + GLsizei width, GLsizei height) +{ + /* GL_ARB_fbo says calling this function is equivalent to calling + * glRenderbufferStorageMultisample() with samples=0. We pass in + * a token value here just for error reporting purposes. + */ + renderbuffer_storage(target, internalFormat, width, height, NO_SAMPLES); +} + + +void GLAPIENTRY +_mesa_RenderbufferStorageMultisample(GLenum target, GLsizei samples, + GLenum internalFormat, + GLsizei width, GLsizei height) +{ + renderbuffer_storage(target, internalFormat, width, height, samples); +} + + +/** + * OpenGL ES version of glRenderBufferStorage. + */ +void GLAPIENTRY +_es_RenderbufferStorageEXT(GLenum target, GLenum internalFormat, + GLsizei width, GLsizei height) +{ + switch (internalFormat) { + case GL_RGB565: + /* XXX this confuses GL_RENDERBUFFER_INTERNAL_FORMAT_OES */ + /* choose a closest format */ + internalFormat = GL_RGB5; + break; + default: + break; + } + + renderbuffer_storage(target, internalFormat, width, height, 0); +} + + +void GLAPIENTRY +_mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params) +{ + struct gl_renderbuffer *rb; + GET_CURRENT_CONTEXT(ctx); + + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (target != GL_RENDERBUFFER_EXT) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetRenderbufferParameterivEXT(target)"); + return; + } + + rb = ctx->CurrentRenderbuffer; + if (!rb) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glGetRenderbufferParameterivEXT"); + return; + } + + /* No need to flush here since we're just quering state which is + * not effected by rendering. + */ + + switch (pname) { + case GL_RENDERBUFFER_WIDTH_EXT: + *params = rb->Width; + return; + case GL_RENDERBUFFER_HEIGHT_EXT: + *params = rb->Height; + return; + case GL_RENDERBUFFER_INTERNAL_FORMAT_EXT: + *params = rb->InternalFormat; + return; + case GL_RENDERBUFFER_RED_SIZE_EXT: + case GL_RENDERBUFFER_GREEN_SIZE_EXT: + case GL_RENDERBUFFER_BLUE_SIZE_EXT: + case GL_RENDERBUFFER_ALPHA_SIZE_EXT: + case GL_RENDERBUFFER_DEPTH_SIZE_EXT: + case GL_RENDERBUFFER_STENCIL_SIZE_EXT: + *params = get_component_bits(pname, rb->_BaseFormat, rb->Format); + break; + case GL_RENDERBUFFER_SAMPLES: + if (ctx->Extensions.ARB_framebuffer_object) { + *params = rb->NumSamples; + break; + } + /* fallthrough */ + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetRenderbufferParameterivEXT(target)"); + return; + } +} + + +GLboolean GLAPIENTRY +_mesa_IsFramebufferEXT(GLuint framebuffer) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); + if (framebuffer) { + struct gl_framebuffer *rb = _mesa_lookup_framebuffer(ctx, framebuffer); + if (rb != NULL && rb != &DummyFramebuffer) + return GL_TRUE; + } + return GL_FALSE; +} + + +/** + * Check if any of the attachments of the given framebuffer are textures + * (render to texture). Call ctx->Driver.RenderTexture() for such + * attachments. + */ +static void +check_begin_texture_render(struct gl_context *ctx, struct gl_framebuffer *fb) +{ + GLuint i; + ASSERT(ctx->Driver.RenderTexture); + + if (fb->Name == 0) + return; /* can't render to texture with winsys framebuffers */ + + for (i = 0; i < BUFFER_COUNT; i++) { + struct gl_renderbuffer_attachment *att = fb->Attachment + i; + struct gl_texture_object *texObj = att->Texture; + if (texObj + && texObj->Image[att->CubeMapFace][att->TextureLevel]) { + ctx->Driver.RenderTexture(ctx, fb, att); + } + } +} + + +/** + * Examine all the framebuffer's attachments to see if any are textures. + * If so, call ctx->Driver.FinishRenderTexture() for each texture to + * notify the device driver that the texture image may have changed. + */ +static void +check_end_texture_render(struct gl_context *ctx, struct gl_framebuffer *fb) +{ + if (fb->Name == 0) + return; /* can't render to texture with winsys framebuffers */ + + if (ctx->Driver.FinishRenderTexture) { + GLuint i; + for (i = 0; i < BUFFER_COUNT; i++) { + struct gl_renderbuffer_attachment *att = fb->Attachment + i; + if (att->Texture && att->Renderbuffer) { + ctx->Driver.FinishRenderTexture(ctx, att); + } + } + } +} + + +void GLAPIENTRY +_mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer) +{ + struct gl_framebuffer *newDrawFb, *newReadFb; + struct gl_framebuffer *oldDrawFb, *oldReadFb; + GLboolean bindReadBuf, bindDrawBuf; + GET_CURRENT_CONTEXT(ctx); + +#ifdef DEBUG + if (ctx->Extensions.ARB_framebuffer_object) { + ASSERT(ctx->Extensions.EXT_framebuffer_object); + ASSERT(ctx->Extensions.EXT_framebuffer_blit); + } +#endif + + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (!ctx->Extensions.EXT_framebuffer_object) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBindFramebufferEXT(unsupported)"); + return; + } + + switch (target) { +#if FEATURE_EXT_framebuffer_blit + case GL_DRAW_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)"); + return; + } + bindDrawBuf = GL_TRUE; + bindReadBuf = GL_FALSE; + break; + case GL_READ_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)"); + return; + } + bindDrawBuf = GL_FALSE; + bindReadBuf = GL_TRUE; + break; +#endif + case GL_FRAMEBUFFER_EXT: + bindDrawBuf = GL_TRUE; + bindReadBuf = GL_TRUE; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)"); + return; + } + + if (framebuffer) { + /* Binding a user-created framebuffer object */ + newDrawFb = _mesa_lookup_framebuffer(ctx, framebuffer); + if (newDrawFb == &DummyFramebuffer) { + /* ID was reserved, but no real framebuffer object made yet */ + newDrawFb = NULL; + } + else if (!newDrawFb && ctx->Extensions.ARB_framebuffer_object) { + /* All FBO IDs must be Gen'd */ + _mesa_error(ctx, GL_INVALID_OPERATION, "glBindFramebuffer(buffer)"); + return; + } + + if (!newDrawFb) { + /* create new framebuffer object */ + newDrawFb = ctx->Driver.NewFramebuffer(ctx, framebuffer); + if (!newDrawFb) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindFramebufferEXT"); + return; + } + _mesa_HashInsert(ctx->Shared->FrameBuffers, framebuffer, newDrawFb); + } + newReadFb = newDrawFb; + } + else { + /* Binding the window system framebuffer (which was originally set + * with MakeCurrent). + */ + newDrawFb = ctx->WinSysDrawBuffer; + newReadFb = ctx->WinSysReadBuffer; + } + + ASSERT(newDrawFb); + ASSERT(newDrawFb != &DummyFramebuffer); + + /* save pointers to current/old framebuffers */ + oldDrawFb = ctx->DrawBuffer; + oldReadFb = ctx->ReadBuffer; + + /* check if really changing bindings */ + if (oldDrawFb == newDrawFb) + bindDrawBuf = GL_FALSE; + if (oldReadFb == newReadFb) + bindReadBuf = GL_FALSE; + + /* + * OK, now bind the new Draw/Read framebuffers, if they're changing. + * + * We also check if we're beginning and/or ending render-to-texture. + * When a framebuffer with texture attachments is unbound, call + * ctx->Driver.FinishRenderTexture(). + * When a framebuffer with texture attachments is bound, call + * ctx->Driver.RenderTexture(). + * + * Note that if the ReadBuffer has texture attachments we don't consider + * that a render-to-texture case. + */ + if (bindReadBuf) { + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + /* check if old readbuffer was render-to-texture */ + check_end_texture_render(ctx, oldReadFb); + + _mesa_reference_framebuffer(&ctx->ReadBuffer, newReadFb); + } + + if (bindDrawBuf) { + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + /* check if old read/draw buffers were render-to-texture */ + if (!bindReadBuf) + check_end_texture_render(ctx, oldReadFb); + + if (oldDrawFb != oldReadFb) + check_end_texture_render(ctx, oldDrawFb); + + /* check if newly bound framebuffer has any texture attachments */ + check_begin_texture_render(ctx, newDrawFb); + + _mesa_reference_framebuffer(&ctx->DrawBuffer, newDrawFb); + } + + if ((bindDrawBuf || bindReadBuf) && ctx->Driver.BindFramebuffer) { + ctx->Driver.BindFramebuffer(ctx, target, newDrawFb, newReadFb); + } +} + + +void GLAPIENTRY +_mesa_DeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers) +{ + GLint i; + GET_CURRENT_CONTEXT(ctx); + + ASSERT_OUTSIDE_BEGIN_END(ctx); + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + for (i = 0; i < n; i++) { + if (framebuffers[i] > 0) { + struct gl_framebuffer *fb; + fb = _mesa_lookup_framebuffer(ctx, framebuffers[i]); + if (fb) { + ASSERT(fb == &DummyFramebuffer || fb->Name == framebuffers[i]); + + /* check if deleting currently bound framebuffer object */ + if (ctx->Extensions.EXT_framebuffer_blit) { + /* separate draw/read binding points */ + if (fb == ctx->DrawBuffer) { + /* bind default */ + ASSERT(fb->RefCount >= 2); + _mesa_BindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); + } + if (fb == ctx->ReadBuffer) { + /* bind default */ + ASSERT(fb->RefCount >= 2); + _mesa_BindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); + } + } + else { + /* only one binding point for read/draw buffers */ + if (fb == ctx->DrawBuffer || fb == ctx->ReadBuffer) { + /* bind default */ + ASSERT(fb->RefCount >= 2); + _mesa_BindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + } + } + + /* remove from hash table immediately, to free the ID */ + _mesa_HashRemove(ctx->Shared->FrameBuffers, framebuffers[i]); + + if (fb != &DummyFramebuffer) { + /* But the object will not be freed until it's no longer + * bound in any context. + */ + _mesa_reference_framebuffer(&fb, NULL); + } + } + } + } +} + + +void GLAPIENTRY +_mesa_GenFramebuffersEXT(GLsizei n, GLuint *framebuffers) +{ + GET_CURRENT_CONTEXT(ctx); + GLuint first; + GLint i; + + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (n < 0) { + _mesa_error(ctx, GL_INVALID_VALUE, "glGenFramebuffersEXT(n)"); + return; + } + + if (!framebuffers) + return; + + first = _mesa_HashFindFreeKeyBlock(ctx->Shared->FrameBuffers, n); + + for (i = 0; i < n; i++) { + GLuint name = first + i; + framebuffers[i] = name; + /* insert dummy placeholder into hash table */ + _glthread_LOCK_MUTEX(ctx->Shared->Mutex); + _mesa_HashInsert(ctx->Shared->FrameBuffers, name, &DummyFramebuffer); + _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); + } +} + + + +GLenum GLAPIENTRY +_mesa_CheckFramebufferStatusEXT(GLenum target) +{ + struct gl_framebuffer *buffer; + GET_CURRENT_CONTEXT(ctx); + + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0); + + switch (target) { +#if FEATURE_EXT_framebuffer_blit + case GL_DRAW_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)"); + return 0; + } + buffer = ctx->DrawBuffer; + break; + case GL_READ_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)"); + return 0; + } + buffer = ctx->ReadBuffer; + break; +#endif + case GL_FRAMEBUFFER_EXT: + buffer = ctx->DrawBuffer; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)"); + return 0; /* formerly GL_FRAMEBUFFER_STATUS_ERROR_EXT */ + } + + if (buffer->Name == 0) { + /* The window system / default framebuffer is always complete */ + return GL_FRAMEBUFFER_COMPLETE_EXT; + } + + /* No need to flush here */ + + if (buffer->_Status != GL_FRAMEBUFFER_COMPLETE) { + _mesa_test_framebuffer_completeness(ctx, buffer); + } + + return buffer->_Status; +} + + + +/** + * Common code called by glFramebufferTexture1D/2D/3DEXT(). + */ +static void +framebuffer_texture(struct gl_context *ctx, const char *caller, GLenum target, + GLenum attachment, GLenum textarget, GLuint texture, + GLint level, GLint zoffset) +{ + struct gl_renderbuffer_attachment *att; + struct gl_texture_object *texObj = NULL; + struct gl_framebuffer *fb; + GLboolean error = GL_FALSE; + + ASSERT_OUTSIDE_BEGIN_END(ctx); + + switch (target) { + case GL_READ_FRAMEBUFFER_EXT: + error = !ctx->Extensions.EXT_framebuffer_blit; + fb = ctx->ReadBuffer; + break; + case GL_DRAW_FRAMEBUFFER_EXT: + error = !ctx->Extensions.EXT_framebuffer_blit; + /* fall-through */ + case GL_FRAMEBUFFER_EXT: + fb = ctx->DrawBuffer; + break; + default: + error = GL_TRUE; + } + + if (error) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferTexture%sEXT(target=0x%x)", caller, target); + return; + } + + ASSERT(fb); + + /* check framebuffer binding */ + if (fb->Name == 0) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glFramebufferTexture%sEXT", caller); + return; + } + + + /* The textarget, level, and zoffset parameters are only validated if + * texture is non-zero. + */ + if (texture) { + GLboolean err = GL_TRUE; + + texObj = _mesa_lookup_texture(ctx, texture); + if (texObj != NULL) { + if (textarget == 0) { + /* XXX what's the purpose of this? */ + err = (texObj->Target != GL_TEXTURE_3D) && + (texObj->Target != GL_TEXTURE_1D_ARRAY_EXT) && + (texObj->Target != GL_TEXTURE_2D_ARRAY_EXT); + } + else { + err = (texObj->Target == GL_TEXTURE_CUBE_MAP) + ? !IS_CUBE_FACE(textarget) + : (texObj->Target != textarget); + } + } + else { + /* can't render to a non-existant texture */ + _mesa_error(ctx, GL_INVALID_OPERATION, + "glFramebufferTexture%sEXT(non existant texture)", + caller); + return; + } + + if (err) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glFramebufferTexture%sEXT(texture target mismatch)", + caller); + return; + } + + if (texObj->Target == GL_TEXTURE_3D) { + const GLint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1); + if (zoffset < 0 || zoffset >= maxSize) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glFramebufferTexture%sEXT(zoffset)", caller); + return; + } + } + else if ((texObj->Target == GL_TEXTURE_1D_ARRAY_EXT) || + (texObj->Target == GL_TEXTURE_2D_ARRAY_EXT)) { + if (zoffset < 0 || zoffset >= ctx->Const.MaxArrayTextureLayers) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glFramebufferTexture%sEXT(layer)", caller); + return; + } + } + + if ((level < 0) || + (level >= _mesa_max_texture_levels(ctx, texObj->Target))) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glFramebufferTexture%sEXT(level)", caller); + return; + } + } + + att = _mesa_get_attachment(ctx, fb, attachment); + if (att == NULL) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferTexture%sEXT(attachment)", caller); + return; + } + + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + _glthread_LOCK_MUTEX(fb->Mutex); + if (texObj) { + _mesa_set_texture_attachment(ctx, fb, att, texObj, textarget, + level, zoffset); + /* Set the render-to-texture flag. We'll check this flag in + * glTexImage() and friends to determine if we need to revalidate + * any FBOs that might be rendering into this texture. + * This flag never gets cleared since it's non-trivial to determine + * when all FBOs might be done rendering to this texture. That's OK + * though since it's uncommon to render to a texture then repeatedly + * call glTexImage() to change images in the texture. + */ + texObj->_RenderToTexture = GL_TRUE; + } + else { + _mesa_remove_attachment(ctx, att); + } + + invalidate_framebuffer(fb); + + _glthread_UNLOCK_MUTEX(fb->Mutex); +} + + + +void GLAPIENTRY +_mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, GLint level) +{ + GET_CURRENT_CONTEXT(ctx); + + if ((texture != 0) && (textarget != GL_TEXTURE_1D)) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferTexture1DEXT(textarget)"); + return; + } + + framebuffer_texture(ctx, "1D", target, attachment, textarget, texture, + level, 0); +} + + +void GLAPIENTRY +_mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, GLint level) +{ + GET_CURRENT_CONTEXT(ctx); + + if ((texture != 0) && + (textarget != GL_TEXTURE_2D) && + (textarget != GL_TEXTURE_RECTANGLE_ARB) && + (!IS_CUBE_FACE(textarget))) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glFramebufferTexture2DEXT(textarget=0x%x)", textarget); + return; + } + + framebuffer_texture(ctx, "2D", target, attachment, textarget, texture, + level, 0); +} + + +void GLAPIENTRY +_mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, + GLint level, GLint zoffset) +{ + GET_CURRENT_CONTEXT(ctx); + + if ((texture != 0) && (textarget != GL_TEXTURE_3D)) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferTexture3DEXT(textarget)"); + return; + } + + framebuffer_texture(ctx, "3D", target, attachment, textarget, texture, + level, zoffset); +} + + +void GLAPIENTRY +_mesa_FramebufferTextureLayerEXT(GLenum target, GLenum attachment, + GLuint texture, GLint level, GLint layer) +{ + GET_CURRENT_CONTEXT(ctx); + + framebuffer_texture(ctx, "Layer", target, attachment, 0, texture, + level, layer); +} + + +void GLAPIENTRY +_mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment, + GLenum renderbufferTarget, + GLuint renderbuffer) +{ + struct gl_renderbuffer_attachment *att; + struct gl_framebuffer *fb; + struct gl_renderbuffer *rb; + GET_CURRENT_CONTEXT(ctx); + + ASSERT_OUTSIDE_BEGIN_END(ctx); + + switch (target) { +#if FEATURE_EXT_framebuffer_blit + case GL_DRAW_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferRenderbufferEXT(target)"); + return; + } + fb = ctx->DrawBuffer; + break; + case GL_READ_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferRenderbufferEXT(target)"); + return; + } + fb = ctx->ReadBuffer; + break; +#endif + case GL_FRAMEBUFFER_EXT: + fb = ctx->DrawBuffer; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferRenderbufferEXT(target)"); + return; + } + + if (renderbufferTarget != GL_RENDERBUFFER_EXT) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferRenderbufferEXT(renderbufferTarget)"); + return; + } + + if (fb->Name == 0) { + /* Can't attach new renderbuffers to a window system framebuffer */ + _mesa_error(ctx, GL_INVALID_OPERATION, "glFramebufferRenderbufferEXT"); + return; + } + + att = _mesa_get_attachment(ctx, fb, attachment); + if (att == NULL) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glFramebufferRenderbufferEXT(invalid attachment %s)", + _mesa_lookup_enum_by_nr(attachment)); + return; + } + + if (renderbuffer) { + rb = _mesa_lookup_renderbuffer(ctx, renderbuffer); + if (!rb) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glFramebufferRenderbufferEXT(non-existant" + " renderbuffer %u)", renderbuffer); + return; + } + else if (rb == &DummyRenderbuffer) { + /* This is what NVIDIA does */ + _mesa_error(ctx, GL_INVALID_VALUE, + "glFramebufferRenderbufferEXT(renderbuffer %u)", + renderbuffer); + return; + } + } + else { + /* remove renderbuffer attachment */ + rb = NULL; + } + + if (attachment == GL_DEPTH_STENCIL_ATTACHMENT && + rb && rb->Format != MESA_FORMAT_NONE) { + /* make sure the renderbuffer is a depth/stencil format */ + const GLenum baseFormat = _mesa_get_format_base_format(rb->Format); + if (baseFormat != GL_DEPTH_STENCIL) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glFramebufferRenderbufferEXT(renderbuffer" + " is not DEPTH_STENCIL format)"); + return; + } + } + + + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + assert(ctx->Driver.FramebufferRenderbuffer); + ctx->Driver.FramebufferRenderbuffer(ctx, fb, attachment, rb); + + /* Some subsequent GL commands may depend on the framebuffer's visual + * after the binding is updated. Update visual info now. + */ + _mesa_update_framebuffer_visual(ctx, fb); +} + + +void GLAPIENTRY +_mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, + GLenum pname, GLint *params) +{ + const struct gl_renderbuffer_attachment *att; + struct gl_framebuffer *buffer; + GET_CURRENT_CONTEXT(ctx); + + ASSERT_OUTSIDE_BEGIN_END(ctx); + + switch (target) { +#if FEATURE_EXT_framebuffer_blit + case GL_DRAW_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameterivEXT(target)"); + return; + } + buffer = ctx->DrawBuffer; + break; + case GL_READ_FRAMEBUFFER_EXT: + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameterivEXT(target)"); + return; + } + buffer = ctx->ReadBuffer; + break; +#endif + case GL_FRAMEBUFFER_EXT: + buffer = ctx->DrawBuffer; + break; + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameterivEXT(target)"); + return; + } + + if (buffer->Name == 0) { + /* the default / window-system FBO */ + att = _mesa_get_fb0_attachment(ctx, buffer, attachment); + } + else { + /* user-created framebuffer FBO */ + att = _mesa_get_attachment(ctx, buffer, attachment); + } + + if (att == NULL) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameterivEXT(attachment)"); + return; + } + + if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { + /* the depth and stencil attachments must point to the same buffer */ + const struct gl_renderbuffer_attachment *depthAtt, *stencilAtt; + depthAtt = _mesa_get_attachment(ctx, buffer, GL_DEPTH_ATTACHMENT); + stencilAtt = _mesa_get_attachment(ctx, buffer, GL_STENCIL_ATTACHMENT); + if (depthAtt->Renderbuffer != stencilAtt->Renderbuffer) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glGetFramebufferAttachmentParameterivEXT(DEPTH/STENCIL" + " attachments differ)"); + return; + } + } + + /* No need to flush here */ + + switch (pname) { + case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: + *params = buffer->Name == 0 ? GL_FRAMEBUFFER_DEFAULT : att->Type; + return; + case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT: + if (att->Type == GL_RENDERBUFFER_EXT) { + *params = att->Renderbuffer->Name; + } + else if (att->Type == GL_TEXTURE) { + *params = att->Texture->Name; + } + else { + assert(att->Type == GL_NONE); + *params = 0; + } + return; + case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT: + if (att->Type == GL_TEXTURE) { + *params = att->TextureLevel; + } + else { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameterivEXT(pname)"); + } + return; + case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT: + if (att->Type == GL_TEXTURE) { + if (att->Texture && att->Texture->Target == GL_TEXTURE_CUBE_MAP) { + *params = GL_TEXTURE_CUBE_MAP_POSITIVE_X + att->CubeMapFace; + } + else { + *params = 0; + } + } + else { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameterivEXT(pname)"); + } + return; + case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT: + if (att->Type == GL_TEXTURE) { + if (att->Texture && att->Texture->Target == GL_TEXTURE_3D) { + *params = att->Zoffset; + } + else { + *params = 0; + } + } + else { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameterivEXT(pname)"); + } + return; + case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: + if (!ctx->Extensions.ARB_framebuffer_object) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameterivEXT(pname)"); + } + else { + if (ctx->Extensions.EXT_framebuffer_sRGB && ctx->Const.sRGBCapable) { + *params = _mesa_get_format_color_encoding(att->Renderbuffer->Format); + } + else { + /* According to ARB_framebuffer_sRGB, we should return LINEAR + * if the sRGB conversion is unsupported. */ + *params = GL_LINEAR; + } + } + return; + case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: + if (!ctx->Extensions.ARB_framebuffer_object) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameterivEXT(pname)"); + return; + } + else { + gl_format format = att->Renderbuffer->Format; + if (format == MESA_FORMAT_CI8 || format == MESA_FORMAT_S8) { + /* special cases */ + *params = GL_INDEX; + } + else { + *params = _mesa_get_format_datatype(format); + } + } + return; + case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: + case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: + case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: + case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: + case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: + case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: + if (!ctx->Extensions.ARB_framebuffer_object) { + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameterivEXT(pname)"); + } + else if (att->Texture) { + const struct gl_texture_image *texImage = + _mesa_select_tex_image(ctx, att->Texture, att->Texture->Target, + att->TextureLevel); + if (texImage) { + *params = get_component_bits(pname, texImage->_BaseFormat, + texImage->TexFormat); + } + else { + *params = 0; + } + } + else if (att->Renderbuffer) { + *params = get_component_bits(pname, att->Renderbuffer->_BaseFormat, + att->Renderbuffer->Format); + } + else { + *params = 0; + } + return; + default: + _mesa_error(ctx, GL_INVALID_ENUM, + "glGetFramebufferAttachmentParameterivEXT(pname)"); + return; + } +} + + +void GLAPIENTRY +_mesa_GenerateMipmapEXT(GLenum target) +{ + struct gl_texture_object *texObj; + GET_CURRENT_CONTEXT(ctx); + + ASSERT_OUTSIDE_BEGIN_END(ctx); + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + switch (target) { + case GL_TEXTURE_1D: + case GL_TEXTURE_2D: + case GL_TEXTURE_3D: + case GL_TEXTURE_CUBE_MAP: + /* OK, legal value */ + break; + default: + /* XXX need to implement GL_TEXTURE_1D_ARRAY and GL_TEXTURE_2D_ARRAY */ + _mesa_error(ctx, GL_INVALID_ENUM, "glGenerateMipmapEXT(target)"); + return; + } + + texObj = _mesa_get_current_tex_object(ctx, target); + + if (texObj->BaseLevel >= texObj->MaxLevel) { + /* nothing to do */ + return; + } + + if (texObj->Target == GL_TEXTURE_CUBE_MAP && + !_mesa_cube_complete(texObj)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glGenerateMipmap(incomplete cube map)"); + return; + } + + _mesa_lock_texture(ctx, texObj); + if (target == GL_TEXTURE_CUBE_MAP) { + GLuint face; + for (face = 0; face < 6; face++) + ctx->Driver.GenerateMipmap(ctx, + GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + face, + texObj); + } + else { + ctx->Driver.GenerateMipmap(ctx, target, texObj); + } + _mesa_unlock_texture(ctx, texObj); +} + + +#if FEATURE_EXT_framebuffer_blit + +static const struct gl_renderbuffer_attachment * +find_attachment(const struct gl_framebuffer *fb, + const struct gl_renderbuffer *rb) +{ + GLuint i; + for (i = 0; i < Elements(fb->Attachment); i++) { + if (fb->Attachment[i].Renderbuffer == rb) + return &fb->Attachment[i]; + } + return NULL; +} + + + +/** + * Blit rectangular region, optionally from one framebuffer to another. + * + * Note, if the src buffer is multisampled and the dest is not, this is + * when the samples must be resolved to a single color. + */ +void GLAPIENTRY +_mesa_BlitFramebufferEXT(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter) +{ + const GLbitfield legalMaskBits = (GL_COLOR_BUFFER_BIT | + GL_DEPTH_BUFFER_BIT | + GL_STENCIL_BUFFER_BIT); + const struct gl_framebuffer *readFb, *drawFb; + const struct gl_renderbuffer *colorReadRb, *colorDrawRb; + GET_CURRENT_CONTEXT(ctx); + + ASSERT_OUTSIDE_BEGIN_END(ctx); + FLUSH_VERTICES(ctx, _NEW_BUFFERS); + + if (ctx->NewState) { + _mesa_update_state(ctx); + } + + readFb = ctx->ReadBuffer; + drawFb = ctx->DrawBuffer; + + if (!readFb || !drawFb) { + /* This will normally never happen but someday we may want to + * support MakeCurrent() with no drawables. + */ + return; + } + + /* check for complete framebuffers */ + if (drawFb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT || + readFb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { + _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT, + "glBlitFramebufferEXT(incomplete draw/read buffers)"); + return; + } + + if (filter != GL_NEAREST && filter != GL_LINEAR) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBlitFramebufferEXT(filter)"); + return; + } + + if (mask & ~legalMaskBits) { + _mesa_error( ctx, GL_INVALID_VALUE, "glBlitFramebufferEXT(mask)"); + return; + } + + /* depth/stencil must be blitted with nearest filtering */ + if ((mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) + && filter != GL_NEAREST) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBlitFramebufferEXT(depth/stencil requires GL_NEAREST filter"); + return; + } + + /* get color read/draw renderbuffers */ + if (mask & GL_COLOR_BUFFER_BIT) { + colorReadRb = readFb->_ColorReadBuffer; + colorDrawRb = drawFb->_ColorDrawBuffers[0]; + } + else { + colorReadRb = colorDrawRb = NULL; + } + + if (mask & GL_STENCIL_BUFFER_BIT) { + struct gl_renderbuffer *readRb = readFb->_StencilBuffer; + struct gl_renderbuffer *drawRb = drawFb->_StencilBuffer; + if (!readRb || + !drawRb || + _mesa_get_format_bits(readRb->Format, GL_STENCIL_BITS) != + _mesa_get_format_bits(drawRb->Format, GL_STENCIL_BITS)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBlitFramebufferEXT(stencil buffer size mismatch"); + return; + } + } + + if (mask & GL_DEPTH_BUFFER_BIT) { + struct gl_renderbuffer *readRb = readFb->_DepthBuffer; + struct gl_renderbuffer *drawRb = drawFb->_DepthBuffer; + if (!readRb || + !drawRb || + _mesa_get_format_bits(readRb->Format, GL_DEPTH_BITS) != + _mesa_get_format_bits(drawRb->Format, GL_DEPTH_BITS)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBlitFramebufferEXT(depth buffer size mismatch"); + return; + } + } + + if (readFb->Visual.samples > 0 && + drawFb->Visual.samples > 0 && + readFb->Visual.samples != drawFb->Visual.samples) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBlitFramebufferEXT(mismatched samples"); + return; + } + + /* extra checks for multisample copies... */ + if (readFb->Visual.samples > 0 || drawFb->Visual.samples > 0) { + /* src and dest region sizes must be the same */ + if (srcX1 - srcX0 != dstX1 - dstX0 || + srcY1 - srcY0 != dstY1 - dstY0) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBlitFramebufferEXT(bad src/dst multisample region sizes"); + return; + } + + /* color formats must match */ + if (colorReadRb && + colorDrawRb && + colorReadRb->Format != colorDrawRb->Format) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBlitFramebufferEXT(bad src/dst multisample pixel formats"); + return; + } + } + + if (!ctx->Extensions.EXT_framebuffer_blit) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glBlitFramebufferEXT"); + return; + } + + /* Debug code */ + if (DEBUG_BLIT) { + printf("glBlitFramebuffer(%d, %d, %d, %d, %d, %d, %d, %d," + " 0x%x, 0x%x)\n", + srcX0, srcY0, srcX1, srcY1, + dstX0, dstY0, dstX1, dstY1, + mask, filter); + if (colorReadRb) { + const struct gl_renderbuffer_attachment *att; + + att = find_attachment(readFb, colorReadRb); + printf(" Src FBO %u RB %u (%dx%d) ", + readFb->Name, colorReadRb->Name, + colorReadRb->Width, colorReadRb->Height); + if (att && att->Texture) { + printf("Tex %u tgt 0x%x level %u face %u", + att->Texture->Name, + att->Texture->Target, + att->TextureLevel, + att->CubeMapFace); + } + printf("\n"); + + att = find_attachment(drawFb, colorDrawRb); + printf(" Dst FBO %u RB %u (%dx%d) ", + drawFb->Name, colorDrawRb->Name, + colorDrawRb->Width, colorDrawRb->Height); + if (att && att->Texture) { + printf("Tex %u tgt 0x%x level %u face %u", + att->Texture->Name, + att->Texture->Target, + att->TextureLevel, + att->CubeMapFace); + } + printf("\n"); + } + } + + ASSERT(ctx->Driver.BlitFramebuffer); + ctx->Driver.BlitFramebuffer(ctx, + srcX0, srcY0, srcX1, srcY1, + dstX0, dstY0, dstX1, dstY1, + mask, filter); +} +#endif /* FEATURE_EXT_framebuffer_blit */ + +#if FEATURE_ARB_geometry_shader4 +void GLAPIENTRY +_mesa_FramebufferTextureARB(GLenum target, GLenum attachment, + GLuint texture, GLint level) +{ + GET_CURRENT_CONTEXT(ctx); + _mesa_error(ctx, GL_INVALID_OPERATION, + "glFramebufferTextureARB " + "not implemented!"); +} + +void GLAPIENTRY +_mesa_FramebufferTextureFaceARB(GLenum target, GLenum attachment, + GLuint texture, GLint level, GLenum face) +{ + GET_CURRENT_CONTEXT(ctx); + _mesa_error(ctx, GL_INVALID_OPERATION, + "glFramebufferTextureFaceARB " + "not implemented!"); +} +#endif /* FEATURE_ARB_geometry_shader4 */ diff --git a/mesalib/src/mesa/main/framebuffer.c b/mesalib/src/mesa/main/framebuffer.c index 5833e5d58..948b3b7b5 100644 --- a/mesalib/src/mesa/main/framebuffer.c +++ b/mesalib/src/mesa/main/framebuffer.c @@ -1,1084 +1,1086 @@ -/* - * Mesa 3-D graphics library - * Version: 7.2 - * - * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * 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. - */ - - -/** - * Functions for allocating/managing framebuffers and renderbuffers. - * Also, routines for reading/writing renderbuffer data as ubytes, - * ushorts, uints, etc. - */ - - -#include "glheader.h" -#include "imports.h" -#include "buffers.h" -#include "context.h" -#include "depthstencil.h" -#include "enums.h" -#include "formats.h" -#include "macros.h" -#include "mtypes.h" -#include "fbobject.h" -#include "framebuffer.h" -#include "renderbuffer.h" -#include "texobj.h" - - - -/** - * Compute/set the _DepthMax field for the given framebuffer. - * This value depends on the Z buffer resolution. - */ -static void -compute_depth_max(struct gl_framebuffer *fb) -{ - if (fb->Visual.depthBits == 0) { - /* Special case. Even if we don't have a depth buffer we need - * good values for DepthMax for Z vertex transformation purposes - * and for per-fragment fog computation. - */ - fb->_DepthMax = (1 << 16) - 1; - } - else if (fb->Visual.depthBits < 32) { - fb->_DepthMax = (1 << fb->Visual.depthBits) - 1; - } - else { - /* Special case since shift values greater than or equal to the - * number of bits in the left hand expression's type are undefined. - */ - fb->_DepthMax = 0xffffffff; - } - fb->_DepthMaxF = (GLfloat) fb->_DepthMax; - - /* Minimum resolvable depth value, for polygon offset */ - fb->_MRD = (GLfloat)1.0 / fb->_DepthMaxF; -} - -/** - * Create and initialize a gl_framebuffer object. - * This is intended for creating _window_system_ framebuffers, not generic - * framebuffer objects ala GL_EXT_framebuffer_object. - * - * \sa _mesa_new_framebuffer - */ -struct gl_framebuffer * -_mesa_create_framebuffer(const struct gl_config *visual) -{ - struct gl_framebuffer *fb = CALLOC_STRUCT(gl_framebuffer); - assert(visual); - if (fb) { - _mesa_initialize_window_framebuffer(fb, visual); - } - return fb; -} - - -/** - * Allocate a new gl_framebuffer object. - * This is the default function for ctx->Driver.NewFramebuffer(). - * This is for allocating user-created framebuffers, not window-system - * framebuffers! - * \sa _mesa_create_framebuffer - */ -struct gl_framebuffer * -_mesa_new_framebuffer(struct gl_context *ctx, GLuint name) -{ - struct gl_framebuffer *fb; - (void) ctx; - assert(name != 0); - fb = CALLOC_STRUCT(gl_framebuffer); - if (fb) { - _mesa_initialize_user_framebuffer(fb, name); - } - return fb; -} - - -/** - * Initialize a gl_framebuffer object. Typically used to initialize - * window system-created framebuffers, not user-created framebuffers. - * \sa _mesa_initialize_user_framebuffer - */ -void -_mesa_initialize_window_framebuffer(struct gl_framebuffer *fb, - const struct gl_config *visual) -{ - assert(fb); - assert(visual); - - memset(fb, 0, sizeof(struct gl_framebuffer)); - - _glthread_INIT_MUTEX(fb->Mutex); - - fb->RefCount = 1; - - /* save the visual */ - fb->Visual = *visual; - - /* Init read/draw renderbuffer state */ - if (visual->doubleBufferMode) { - fb->_NumColorDrawBuffers = 1; - fb->ColorDrawBuffer[0] = GL_BACK; - fb->_ColorDrawBufferIndexes[0] = BUFFER_BACK_LEFT; - fb->ColorReadBuffer = GL_BACK; - fb->_ColorReadBufferIndex = BUFFER_BACK_LEFT; - } - else { - fb->_NumColorDrawBuffers = 1; - fb->ColorDrawBuffer[0] = GL_FRONT; - fb->_ColorDrawBufferIndexes[0] = BUFFER_FRONT_LEFT; - fb->ColorReadBuffer = GL_FRONT; - fb->_ColorReadBufferIndex = BUFFER_FRONT_LEFT; - } - - fb->Delete = _mesa_destroy_framebuffer; - fb->_Status = GL_FRAMEBUFFER_COMPLETE_EXT; - - compute_depth_max(fb); -} - - -/** - * Initialize a user-created gl_framebuffer object. - * \sa _mesa_initialize_window_framebuffer - */ -void -_mesa_initialize_user_framebuffer(struct gl_framebuffer *fb, GLuint name) -{ - assert(fb); - assert(name); - - memset(fb, 0, sizeof(struct gl_framebuffer)); - - fb->Name = name; - fb->RefCount = 1; - fb->_NumColorDrawBuffers = 1; - fb->ColorDrawBuffer[0] = GL_COLOR_ATTACHMENT0_EXT; - fb->_ColorDrawBufferIndexes[0] = BUFFER_COLOR0; - fb->ColorReadBuffer = GL_COLOR_ATTACHMENT0_EXT; - fb->_ColorReadBufferIndex = BUFFER_COLOR0; - fb->Delete = _mesa_destroy_framebuffer; - _glthread_INIT_MUTEX(fb->Mutex); -} - - -/** - * Deallocate buffer and everything attached to it. - * Typically called via the gl_framebuffer->Delete() method. - */ -void -_mesa_destroy_framebuffer(struct gl_framebuffer *fb) -{ - if (fb) { - _mesa_free_framebuffer_data(fb); - free(fb); - } -} - - -/** - * Free all the data hanging off the given gl_framebuffer, but don't free - * the gl_framebuffer object itself. - */ -void -_mesa_free_framebuffer_data(struct gl_framebuffer *fb) -{ - GLuint i; - - assert(fb); - assert(fb->RefCount == 0); - - _glthread_DESTROY_MUTEX(fb->Mutex); - - for (i = 0; i < BUFFER_COUNT; i++) { - struct gl_renderbuffer_attachment *att = &fb->Attachment[i]; - if (att->Renderbuffer) { - _mesa_reference_renderbuffer(&att->Renderbuffer, NULL); - } - if (att->Texture) { - _mesa_reference_texobj(&att->Texture, NULL); - } - ASSERT(!att->Renderbuffer); - ASSERT(!att->Texture); - att->Type = GL_NONE; - } - - /* unbind _Depth/_StencilBuffer to decr ref counts */ - _mesa_reference_renderbuffer(&fb->_DepthBuffer, NULL); - _mesa_reference_renderbuffer(&fb->_StencilBuffer, NULL); -} - - -/** - * Set *ptr to point to fb, with refcounting and locking. - */ -void -_mesa_reference_framebuffer(struct gl_framebuffer **ptr, - struct gl_framebuffer *fb) -{ - assert(ptr); - if (*ptr == fb) { - /* no change */ - return; - } - - if (*ptr) { - /* unreference old renderbuffer */ - GLboolean deleteFlag = GL_FALSE; - struct gl_framebuffer *oldFb = *ptr; - - _glthread_LOCK_MUTEX(oldFb->Mutex); - ASSERT(oldFb->RefCount > 0); - oldFb->RefCount--; - deleteFlag = (oldFb->RefCount == 0); - _glthread_UNLOCK_MUTEX(oldFb->Mutex); - - if (deleteFlag) - oldFb->Delete(oldFb); - - *ptr = NULL; - } - assert(!*ptr); - - if (fb) { - _glthread_LOCK_MUTEX(fb->Mutex); - fb->RefCount++; - _glthread_UNLOCK_MUTEX(fb->Mutex); - *ptr = fb; - } -} - - -/** - * Resize the given framebuffer's renderbuffers to the new width and height. - * This should only be used for window-system framebuffers, not - * user-created renderbuffers (i.e. made with GL_EXT_framebuffer_object). - * This will typically be called via ctx->Driver.ResizeBuffers() or directly - * from a device driver. - * - * \note it's possible for ctx to be null since a window can be resized - * without a currently bound rendering context. - */ -void -_mesa_resize_framebuffer(struct gl_context *ctx, struct gl_framebuffer *fb, - GLuint width, GLuint height) -{ - GLuint i; - - /* XXX I think we could check if the size is not changing - * and return early. - */ - - /* For window system framebuffers, Name is zero */ - assert(fb->Name == 0); - - for (i = 0; i < BUFFER_COUNT; i++) { - struct gl_renderbuffer_attachment *att = &fb->Attachment[i]; - if (att->Type == GL_RENDERBUFFER_EXT && att->Renderbuffer) { - struct gl_renderbuffer *rb = att->Renderbuffer; - /* only resize if size is changing */ - if (rb->Width != width || rb->Height != height) { - if (rb->AllocStorage(ctx, rb, rb->InternalFormat, width, height)) { - ASSERT(rb->Width == width); - ASSERT(rb->Height == height); - } - else { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "Resizing framebuffer"); - /* no return */ - } - } - } - } - - if (fb->_DepthBuffer) { - struct gl_renderbuffer *rb = fb->_DepthBuffer; - if (rb->Width != width || rb->Height != height) { - if (!rb->AllocStorage(ctx, rb, rb->InternalFormat, width, height)) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "Resizing framebuffer"); - } - } - } - - if (fb->_StencilBuffer) { - struct gl_renderbuffer *rb = fb->_StencilBuffer; - if (rb->Width != width || rb->Height != height) { - if (!rb->AllocStorage(ctx, rb, rb->InternalFormat, width, height)) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "Resizing framebuffer"); - } - } - } - - fb->Width = width; - fb->Height = height; - - if (ctx) { - /* update scissor / window bounds */ - _mesa_update_draw_buffer_bounds(ctx); - /* Signal new buffer state so that swrast will update its clipping - * info (the CLIP_BIT flag). - */ - ctx->NewState |= _NEW_BUFFERS; - } -} - - - -/** - * XXX THIS IS OBSOLETE - drivers should take care of detecting window - * size changes and act accordingly, likely calling _mesa_resize_framebuffer(). - * - * GL_MESA_resize_buffers extension. - * - * When this function is called, we'll ask the window system how large - * the current window is. If it's a new size, we'll call the driver's - * ResizeBuffers function. The driver will then resize its color buffers - * as needed, and maybe call the swrast's routine for reallocating - * swrast-managed depth/stencil/accum/etc buffers. - * \note This function should only be called through the GL API, not - * from device drivers (as was done in the past). - */ -void -_mesa_resizebuffers( struct gl_context *ctx ) -{ - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH( ctx ); - - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, "glResizeBuffersMESA\n"); - - if (!ctx->Driver.GetBufferSize) { - return; - } - - if (ctx->WinSysDrawBuffer) { - GLuint newWidth, newHeight; - struct gl_framebuffer *buffer = ctx->WinSysDrawBuffer; - - assert(buffer->Name == 0); - - /* ask device driver for size of output buffer */ - ctx->Driver.GetBufferSize( buffer, &newWidth, &newHeight ); - - /* see if size of device driver's color buffer (window) has changed */ - if (buffer->Width != newWidth || buffer->Height != newHeight) { - if (ctx->Driver.ResizeBuffers) - ctx->Driver.ResizeBuffers(ctx, buffer, newWidth, newHeight ); - } - } - - if (ctx->WinSysReadBuffer - && ctx->WinSysReadBuffer != ctx->WinSysDrawBuffer) { - GLuint newWidth, newHeight; - struct gl_framebuffer *buffer = ctx->WinSysReadBuffer; - - assert(buffer->Name == 0); - - /* ask device driver for size of read buffer */ - ctx->Driver.GetBufferSize( buffer, &newWidth, &newHeight ); - - /* see if size of device driver's color buffer (window) has changed */ - if (buffer->Width != newWidth || buffer->Height != newHeight) { - if (ctx->Driver.ResizeBuffers) - ctx->Driver.ResizeBuffers(ctx, buffer, newWidth, newHeight ); - } - } - - ctx->NewState |= _NEW_BUFFERS; /* to update scissor / window bounds */ -} - - -/* - * XXX THIS IS OBSOLETE - */ -void GLAPIENTRY -_mesa_ResizeBuffersMESA( void ) -{ - GET_CURRENT_CONTEXT(ctx); - - if (ctx->Extensions.MESA_resize_buffers) - _mesa_resizebuffers( ctx ); -} - - - -/** - * Examine all the framebuffer's renderbuffers to update the Width/Height - * fields of the framebuffer. If we have renderbuffers with different - * sizes, set the framebuffer's width and height to the min size. - * Note: this is only intended for user-created framebuffers, not - * window-system framebuffes. - */ -static void -update_framebuffer_size(struct gl_context *ctx, struct gl_framebuffer *fb) -{ - GLuint minWidth = ~0, minHeight = ~0; - GLuint i; - - /* user-created framebuffers only */ - assert(fb->Name); - - for (i = 0; i < BUFFER_COUNT; i++) { - struct gl_renderbuffer_attachment *att = &fb->Attachment[i]; - const struct gl_renderbuffer *rb = att->Renderbuffer; - if (rb) { - minWidth = MIN2(minWidth, rb->Width); - minHeight = MIN2(minHeight, rb->Height); - } - } - - if (minWidth != ~0) { - fb->Width = minWidth; - fb->Height = minHeight; - } - else { - fb->Width = 0; - fb->Height = 0; - } -} - - -/** - * Update the context's current drawing buffer's Xmin, Xmax, Ymin, Ymax fields. - * These values are computed from the buffer's width and height and - * the scissor box, if it's enabled. - * \param ctx the GL context. - */ -void -_mesa_update_draw_buffer_bounds(struct gl_context *ctx) -{ - struct gl_framebuffer *buffer = ctx->DrawBuffer; - - if (!buffer) - return; - - if (buffer->Name) { - /* user-created framebuffer size depends on the renderbuffers */ - update_framebuffer_size(ctx, buffer); - } - - buffer->_Xmin = 0; - buffer->_Ymin = 0; - buffer->_Xmax = buffer->Width; - buffer->_Ymax = buffer->Height; - - if (ctx->Scissor.Enabled) { - if (ctx->Scissor.X > buffer->_Xmin) { - buffer->_Xmin = ctx->Scissor.X; - } - if (ctx->Scissor.Y > buffer->_Ymin) { - buffer->_Ymin = ctx->Scissor.Y; - } - if (ctx->Scissor.X + ctx->Scissor.Width < buffer->_Xmax) { - buffer->_Xmax = ctx->Scissor.X + ctx->Scissor.Width; - } - if (ctx->Scissor.Y + ctx->Scissor.Height < buffer->_Ymax) { - buffer->_Ymax = ctx->Scissor.Y + ctx->Scissor.Height; - } - /* finally, check for empty region */ - if (buffer->_Xmin > buffer->_Xmax) { - buffer->_Xmin = buffer->_Xmax; - } - if (buffer->_Ymin > buffer->_Ymax) { - buffer->_Ymin = buffer->_Ymax; - } - } - - ASSERT(buffer->_Xmin <= buffer->_Xmax); - ASSERT(buffer->_Ymin <= buffer->_Ymax); -} - - -/** - * The glGet queries of the framebuffer red/green/blue size, stencil size, - * etc. are satisfied by the fields of ctx->DrawBuffer->Visual. These can - * change depending on the renderbuffer bindings. This function updates - * the given framebuffer's Visual from the current renderbuffer bindings. - * - * This may apply to user-created framebuffers or window system framebuffers. - * - * Also note: ctx->DrawBuffer->Visual.depthBits might not equal - * ctx->DrawBuffer->Attachment[BUFFER_DEPTH].Renderbuffer.DepthBits. - * The former one is used to convert floating point depth values into - * integer Z values. - */ -void -_mesa_update_framebuffer_visual(struct gl_context *ctx, - struct gl_framebuffer *fb) -{ - GLuint i; - - memset(&fb->Visual, 0, sizeof(fb->Visual)); - fb->Visual.rgbMode = GL_TRUE; /* assume this */ - -#if 0 /* this _might_ be needed */ - if (fb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { - /* leave visual fields zero'd */ - return; - } -#endif - - /* find first RGB renderbuffer */ - for (i = 0; i < BUFFER_COUNT; i++) { - if (fb->Attachment[i].Renderbuffer) { - const struct gl_renderbuffer *rb = fb->Attachment[i].Renderbuffer; - const GLenum baseFormat = _mesa_get_format_base_format(rb->Format); - const gl_format fmt = rb->Format; - - if (_mesa_is_legal_color_format(ctx, baseFormat)) { - fb->Visual.redBits = _mesa_get_format_bits(fmt, GL_RED_BITS); - fb->Visual.greenBits = _mesa_get_format_bits(fmt, GL_GREEN_BITS); - fb->Visual.blueBits = _mesa_get_format_bits(fmt, GL_BLUE_BITS); - fb->Visual.alphaBits = _mesa_get_format_bits(fmt, GL_ALPHA_BITS); - fb->Visual.rgbBits = fb->Visual.redBits - + fb->Visual.greenBits + fb->Visual.blueBits; - fb->Visual.floatMode = GL_FALSE; - fb->Visual.samples = rb->NumSamples; - break; - } - } - } - - if (fb->Attachment[BUFFER_DEPTH].Renderbuffer) { - const struct gl_renderbuffer *rb = - fb->Attachment[BUFFER_DEPTH].Renderbuffer; - const gl_format fmt = rb->Format; - fb->Visual.haveDepthBuffer = GL_TRUE; - fb->Visual.depthBits = _mesa_get_format_bits(fmt, GL_DEPTH_BITS); - } - - if (fb->Attachment[BUFFER_STENCIL].Renderbuffer) { - const struct gl_renderbuffer *rb = - fb->Attachment[BUFFER_STENCIL].Renderbuffer; - const gl_format fmt = rb->Format; - fb->Visual.haveStencilBuffer = GL_TRUE; - fb->Visual.stencilBits = _mesa_get_format_bits(fmt, GL_STENCIL_BITS); - } - - if (fb->Attachment[BUFFER_ACCUM].Renderbuffer) { - const struct gl_renderbuffer *rb = - fb->Attachment[BUFFER_ACCUM].Renderbuffer; - const gl_format fmt = rb->Format; - fb->Visual.haveAccumBuffer = GL_TRUE; - fb->Visual.accumRedBits = _mesa_get_format_bits(fmt, GL_RED_BITS); - fb->Visual.accumGreenBits = _mesa_get_format_bits(fmt, GL_GREEN_BITS); - fb->Visual.accumBlueBits = _mesa_get_format_bits(fmt, GL_BLUE_BITS); - fb->Visual.accumAlphaBits = _mesa_get_format_bits(fmt, GL_ALPHA_BITS); - } - - compute_depth_max(fb); -} - - -/** - * Update the framebuffer's _DepthBuffer field using the renderbuffer - * found at the given attachment index. - * - * If that attachment points to a combined GL_DEPTH_STENCIL renderbuffer, - * create and install a depth wrapper/adaptor. - * - * \param fb the framebuffer whose _DepthBuffer field to update - * \param attIndex indicates the renderbuffer to possibly wrap - */ -void -_mesa_update_depth_buffer(struct gl_context *ctx, - struct gl_framebuffer *fb, - GLuint attIndex) -{ - struct gl_renderbuffer *depthRb; - - /* only one possiblity for now */ - ASSERT(attIndex == BUFFER_DEPTH); - - depthRb = fb->Attachment[attIndex].Renderbuffer; - - if (depthRb && _mesa_is_format_packed_depth_stencil(depthRb->Format)) { - /* The attached depth buffer is a GL_DEPTH_STENCIL renderbuffer */ - if (!fb->_DepthBuffer - || fb->_DepthBuffer->Wrapped != depthRb - || _mesa_get_format_base_format(fb->_DepthBuffer->Format) != GL_DEPTH_COMPONENT) { - /* need to update wrapper */ - struct gl_renderbuffer *wrapper - = _mesa_new_z24_renderbuffer_wrapper(ctx, depthRb); - _mesa_reference_renderbuffer(&fb->_DepthBuffer, wrapper); - ASSERT(fb->_DepthBuffer->Wrapped == depthRb); - } - } - else { - /* depthRb may be null */ - _mesa_reference_renderbuffer(&fb->_DepthBuffer, depthRb); - } -} - - -/** - * Update the framebuffer's _StencilBuffer field using the renderbuffer - * found at the given attachment index. - * - * If that attachment points to a combined GL_DEPTH_STENCIL renderbuffer, - * create and install a stencil wrapper/adaptor. - * - * \param fb the framebuffer whose _StencilBuffer field to update - * \param attIndex indicates the renderbuffer to possibly wrap - */ -void -_mesa_update_stencil_buffer(struct gl_context *ctx, - struct gl_framebuffer *fb, - GLuint attIndex) -{ - struct gl_renderbuffer *stencilRb; - - ASSERT(attIndex == BUFFER_DEPTH || - attIndex == BUFFER_STENCIL); - - stencilRb = fb->Attachment[attIndex].Renderbuffer; - - if (stencilRb && _mesa_is_format_packed_depth_stencil(stencilRb->Format)) { - /* The attached stencil buffer is a GL_DEPTH_STENCIL renderbuffer */ - if (!fb->_StencilBuffer - || fb->_StencilBuffer->Wrapped != stencilRb - || _mesa_get_format_base_format(fb->_StencilBuffer->Format) != GL_STENCIL_INDEX) { - /* need to update wrapper */ - struct gl_renderbuffer *wrapper - = _mesa_new_s8_renderbuffer_wrapper(ctx, stencilRb); - _mesa_reference_renderbuffer(&fb->_StencilBuffer, wrapper); - ASSERT(fb->_StencilBuffer->Wrapped == stencilRb); - } - } - else { - /* stencilRb may be null */ - _mesa_reference_renderbuffer(&fb->_StencilBuffer, stencilRb); - } -} - - -/* - * Example DrawBuffers scenarios: - * - * 1. glDrawBuffer(GL_FRONT_AND_BACK), fixed-func or shader writes to - * "gl_FragColor" or program writes to the "result.color" register: - * - * fragment color output renderbuffer - * --------------------- --------------- - * color[0] Front, Back - * - * - * 2. glDrawBuffers(3, [GL_FRONT, GL_AUX0, GL_AUX1]), shader writes to - * gl_FragData[i] or program writes to result.color[i] registers: - * - * fragment color output renderbuffer - * --------------------- --------------- - * color[0] Front - * color[1] Aux0 - * color[3] Aux1 - * - * - * 3. glDrawBuffers(3, [GL_FRONT, GL_AUX0, GL_AUX1]) and shader writes to - * gl_FragColor, or fixed function: - * - * fragment color output renderbuffer - * --------------------- --------------- - * color[0] Front, Aux0, Aux1 - * - * - * In either case, the list of renderbuffers is stored in the - * framebuffer->_ColorDrawBuffers[] array and - * framebuffer->_NumColorDrawBuffers indicates the number of buffers. - * The renderer (like swrast) has to look at the current fragment shader - * to see if it writes to gl_FragColor vs. gl_FragData[i] to determine - * how to map color outputs to renderbuffers. - * - * Note that these two calls are equivalent (for fixed function fragment - * shading anyway): - * a) glDrawBuffer(GL_FRONT_AND_BACK); (assuming non-stereo framebuffer) - * b) glDrawBuffers(2, [GL_FRONT_LEFT, GL_BACK_LEFT]); - */ - - - - -/** - * Update the (derived) list of color drawing renderbuffer pointers. - * Later, when we're rendering we'll loop from 0 to _NumColorDrawBuffers - * writing colors. - */ -static void -update_color_draw_buffers(struct gl_context *ctx, struct gl_framebuffer *fb) -{ - GLuint output; - - /* set 0th buffer to NULL now in case _NumColorDrawBuffers is zero */ - fb->_ColorDrawBuffers[0] = NULL; - - for (output = 0; output < fb->_NumColorDrawBuffers; output++) { - GLint buf = fb->_ColorDrawBufferIndexes[output]; - if (buf >= 0) { - fb->_ColorDrawBuffers[output] = fb->Attachment[buf].Renderbuffer; - } - else { - fb->_ColorDrawBuffers[output] = NULL; - } - } -} - - -/** - * Update the (derived) color read renderbuffer pointer. - * Unlike the DrawBuffer, we can only read from one (or zero) color buffers. - */ -static void -update_color_read_buffer(struct gl_context *ctx, struct gl_framebuffer *fb) -{ - (void) ctx; - if (fb->_ColorReadBufferIndex == -1 || - fb->DeletePending || - fb->Width == 0 || - fb->Height == 0) { - fb->_ColorReadBuffer = NULL; /* legal! */ - } - else { - ASSERT(fb->_ColorReadBufferIndex >= 0); - ASSERT(fb->_ColorReadBufferIndex < BUFFER_COUNT); - fb->_ColorReadBuffer - = fb->Attachment[fb->_ColorReadBufferIndex].Renderbuffer; - } -} - - -/** - * Update a gl_framebuffer's derived state. - * - * Specifically, update these framebuffer fields: - * _ColorDrawBuffers - * _NumColorDrawBuffers - * _ColorReadBuffer - * _DepthBuffer - * _StencilBuffer - * - * If the framebuffer is user-created, make sure it's complete. - * - * The following functions (at least) can effect framebuffer state: - * glReadBuffer, glDrawBuffer, glDrawBuffersARB, glFramebufferRenderbufferEXT, - * glRenderbufferStorageEXT. - */ -static void -update_framebuffer(struct gl_context *ctx, struct gl_framebuffer *fb) -{ - if (fb->Name == 0) { - /* This is a window-system framebuffer */ - /* Need to update the FB's GL_DRAW_BUFFER state to match the - * context state (GL_READ_BUFFER too). - */ - if (fb->ColorDrawBuffer[0] != ctx->Color.DrawBuffer[0]) { - _mesa_drawbuffers(ctx, ctx->Const.MaxDrawBuffers, - ctx->Color.DrawBuffer, NULL); - } - if (fb->ColorReadBuffer != ctx->Pixel.ReadBuffer) { - - } - } - else { - /* This is a user-created framebuffer. - * Completeness only matters for user-created framebuffers. - */ - if (fb->_Status != GL_FRAMEBUFFER_COMPLETE) { - _mesa_test_framebuffer_completeness(ctx, fb); - } - } - - /* Strictly speaking, we don't need to update the draw-state - * if this FB is bound as ctx->ReadBuffer (and conversely, the - * read-state if this FB is bound as ctx->DrawBuffer), but no - * harm. - */ - update_color_draw_buffers(ctx, fb); - update_color_read_buffer(ctx, fb); - _mesa_update_depth_buffer(ctx, fb, BUFFER_DEPTH); - _mesa_update_stencil_buffer(ctx, fb, BUFFER_STENCIL); - - compute_depth_max(fb); -} - - -/** - * Update state related to the current draw/read framebuffers. - */ -void -_mesa_update_framebuffer(struct gl_context *ctx) -{ - struct gl_framebuffer *drawFb; - struct gl_framebuffer *readFb; - - assert(ctx); - drawFb = ctx->DrawBuffer; - readFb = ctx->ReadBuffer; - - update_framebuffer(ctx, drawFb); - if (readFb != drawFb) - update_framebuffer(ctx, readFb); -} - - -/** - * Check if the renderbuffer for a read operation (glReadPixels, glCopyPixels, - * glCopyTex[Sub]Image, etc) exists. - * \param format a basic image format such as GL_RGB, GL_RGBA, GL_ALPHA, - * GL_DEPTH_COMPONENT, etc. or GL_COLOR, GL_DEPTH, GL_STENCIL. - * \return GL_TRUE if buffer exists, GL_FALSE otherwise - */ -GLboolean -_mesa_source_buffer_exists(struct gl_context *ctx, GLenum format) -{ - const struct gl_renderbuffer_attachment *att = ctx->ReadBuffer->Attachment; - - /* If we don't know the framebuffer status, update it now */ - if (ctx->ReadBuffer->_Status == 0) { - _mesa_test_framebuffer_completeness(ctx, ctx->ReadBuffer); - } - - if (ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { - return GL_FALSE; - } - - switch (format) { - case GL_COLOR: - case GL_RED: - case GL_GREEN: - case GL_BLUE: - case GL_ALPHA: - case GL_LUMINANCE: - case GL_LUMINANCE_ALPHA: - case GL_INTENSITY: - case GL_RG: - case GL_RGB: - case GL_BGR: - case GL_RGBA: - case GL_BGRA: - case GL_ABGR_EXT: - case GL_COLOR_INDEX: - case GL_RED_INTEGER_EXT: - case GL_GREEN_INTEGER_EXT: - case GL_BLUE_INTEGER_EXT: - case GL_ALPHA_INTEGER_EXT: - case GL_RGB_INTEGER_EXT: - case GL_RGBA_INTEGER_EXT: - case GL_BGR_INTEGER_EXT: - case GL_BGRA_INTEGER_EXT: - case GL_LUMINANCE_INTEGER_EXT: - case GL_LUMINANCE_ALPHA_INTEGER_EXT: - if (ctx->ReadBuffer->_ColorReadBuffer == NULL) { - return GL_FALSE; - } - ASSERT(_mesa_get_format_bits(ctx->ReadBuffer->_ColorReadBuffer->Format, GL_RED_BITS) > 0 || - _mesa_get_format_bits(ctx->ReadBuffer->_ColorReadBuffer->Format, GL_ALPHA_BITS) > 0 || - _mesa_get_format_bits(ctx->ReadBuffer->_ColorReadBuffer->Format, GL_INDEX_BITS) > 0); - break; - case GL_DEPTH: - case GL_DEPTH_COMPONENT: - if (!att[BUFFER_DEPTH].Renderbuffer) { - return GL_FALSE; - } - /*ASSERT(att[BUFFER_DEPTH].Renderbuffer->DepthBits > 0);*/ - break; - case GL_STENCIL: - case GL_STENCIL_INDEX: - if (!att[BUFFER_STENCIL].Renderbuffer) { - return GL_FALSE; - } - /*ASSERT(att[BUFFER_STENCIL].Renderbuffer->StencilBits > 0);*/ - break; - case GL_DEPTH_STENCIL_EXT: - if (!att[BUFFER_DEPTH].Renderbuffer || - !att[BUFFER_STENCIL].Renderbuffer) { - return GL_FALSE; - } - /* - ASSERT(att[BUFFER_DEPTH].Renderbuffer->DepthBits > 0); - ASSERT(att[BUFFER_STENCIL].Renderbuffer->StencilBits > 0); - */ - break; - default: - _mesa_problem(ctx, - "Unexpected format 0x%x in _mesa_source_buffer_exists", - format); - return GL_FALSE; - } - - /* OK */ - return GL_TRUE; -} - - -/** - * As above, but for drawing operations. - * XXX could do some code merging w/ above function. - */ -GLboolean -_mesa_dest_buffer_exists(struct gl_context *ctx, GLenum format) -{ - const struct gl_renderbuffer_attachment *att = ctx->DrawBuffer->Attachment; - - /* If we don't know the framebuffer status, update it now */ - if (ctx->DrawBuffer->_Status == 0) { - _mesa_test_framebuffer_completeness(ctx, ctx->DrawBuffer); - } - - if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { - return GL_FALSE; - } - - switch (format) { - case GL_COLOR: - case GL_RED: - case GL_GREEN: - case GL_BLUE: - case GL_ALPHA: - case GL_LUMINANCE: - case GL_LUMINANCE_ALPHA: - case GL_INTENSITY: - case GL_RGB: - case GL_BGR: - case GL_RGBA: - case GL_BGRA: - case GL_ABGR_EXT: - case GL_COLOR_INDEX: - case GL_RED_INTEGER_EXT: - case GL_GREEN_INTEGER_EXT: - case GL_BLUE_INTEGER_EXT: - case GL_ALPHA_INTEGER_EXT: - case GL_RGB_INTEGER_EXT: - case GL_RGBA_INTEGER_EXT: - case GL_BGR_INTEGER_EXT: - case GL_BGRA_INTEGER_EXT: - case GL_LUMINANCE_INTEGER_EXT: - case GL_LUMINANCE_ALPHA_INTEGER_EXT: - /* Nothing special since GL_DRAW_BUFFER could be GL_NONE. */ - /* Could assert that colorbuffer has RedBits > 0 */ - break; - case GL_DEPTH: - case GL_DEPTH_COMPONENT: - if (!att[BUFFER_DEPTH].Renderbuffer) { - return GL_FALSE; - } - /*ASSERT(att[BUFFER_DEPTH].Renderbuffer->DepthBits > 0);*/ - break; - case GL_STENCIL: - case GL_STENCIL_INDEX: - if (!att[BUFFER_STENCIL].Renderbuffer) { - return GL_FALSE; - } - /*ASSERT(att[BUFFER_STENCIL].Renderbuffer->StencilBits > 0);*/ - break; - case GL_DEPTH_STENCIL_EXT: - if (!att[BUFFER_DEPTH].Renderbuffer || - !att[BUFFER_STENCIL].Renderbuffer) { - return GL_FALSE; - } - /* - ASSERT(att[BUFFER_DEPTH].Renderbuffer->DepthBits > 0); - ASSERT(att[BUFFER_STENCIL].Renderbuffer->StencilBits > 0); - */ - break; - default: - _mesa_problem(ctx, - "Unexpected format 0x%x in _mesa_dest_buffer_exists", - format); - return GL_FALSE; - } - - /* OK */ - return GL_TRUE; -} - - -/** - * Used to answer the GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES query. - */ -GLenum -_mesa_get_color_read_format(struct gl_context *ctx) -{ - switch (ctx->ReadBuffer->_ColorReadBuffer->Format) { - case MESA_FORMAT_ARGB8888: - return GL_BGRA; - case MESA_FORMAT_RGB565: - return GL_BGR; - default: - return GL_RGBA; - } -} - - -/** - * Used to answer the GL_IMPLEMENTATION_COLOR_READ_TYPE_OES query. - */ -GLenum -_mesa_get_color_read_type(struct gl_context *ctx) -{ - switch (ctx->ReadBuffer->_ColorReadBuffer->Format) { - case MESA_FORMAT_ARGB8888: - return GL_UNSIGNED_BYTE; - case MESA_FORMAT_RGB565: - return GL_UNSIGNED_SHORT_5_6_5_REV; - default: - return GL_UNSIGNED_BYTE; - } -} - - -/** - * Print framebuffer info to stderr, for debugging. - */ -void -_mesa_print_framebuffer(const struct gl_framebuffer *fb) -{ - GLuint i; - - fprintf(stderr, "Mesa Framebuffer %u at %p\n", fb->Name, (void *) fb); - fprintf(stderr, " Size: %u x %u Status: %s\n", fb->Width, fb->Height, - _mesa_lookup_enum_by_nr(fb->_Status)); - fprintf(stderr, " Attachments:\n"); - - for (i = 0; i < BUFFER_COUNT; i++) { - const struct gl_renderbuffer_attachment *att = &fb->Attachment[i]; - if (att->Type == GL_TEXTURE) { - const struct gl_texture_image *texImage; - fprintf(stderr, - " %2d: Texture %u, level %u, face %u, slice %u, complete %d\n", - i, att->Texture->Name, att->TextureLevel, att->CubeMapFace, - att->Zoffset, att->Complete); - texImage = att->Texture->Image[att->CubeMapFace][att->TextureLevel]; - fprintf(stderr, " Size: %u x %u x %u Format %s\n", - texImage->Width, texImage->Height, texImage->Depth, - _mesa_get_format_name(texImage->TexFormat)); - } - else if (att->Type == GL_RENDERBUFFER) { - fprintf(stderr, " %2d: Renderbuffer %u, complete %d\n", - i, att->Renderbuffer->Name, att->Complete); - fprintf(stderr, " Size: %u x %u Format %s\n", - att->Renderbuffer->Width, att->Renderbuffer->Height, - _mesa_get_format_name(att->Renderbuffer->Format)); - } - else { - fprintf(stderr, " %2d: none\n", i); - } - } -} +/* + * Mesa 3-D graphics library + * Version: 7.2 + * + * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * 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. + */ + + +/** + * Functions for allocating/managing framebuffers and renderbuffers. + * Also, routines for reading/writing renderbuffer data as ubytes, + * ushorts, uints, etc. + */ + + +#include "glheader.h" +#include "imports.h" +#include "buffers.h" +#include "context.h" +#include "depthstencil.h" +#include "enums.h" +#include "formats.h" +#include "macros.h" +#include "mtypes.h" +#include "fbobject.h" +#include "framebuffer.h" +#include "renderbuffer.h" +#include "texobj.h" + + + +/** + * Compute/set the _DepthMax field for the given framebuffer. + * This value depends on the Z buffer resolution. + */ +static void +compute_depth_max(struct gl_framebuffer *fb) +{ + if (fb->Visual.depthBits == 0) { + /* Special case. Even if we don't have a depth buffer we need + * good values for DepthMax for Z vertex transformation purposes + * and for per-fragment fog computation. + */ + fb->_DepthMax = (1 << 16) - 1; + } + else if (fb->Visual.depthBits < 32) { + fb->_DepthMax = (1 << fb->Visual.depthBits) - 1; + } + else { + /* Special case since shift values greater than or equal to the + * number of bits in the left hand expression's type are undefined. + */ + fb->_DepthMax = 0xffffffff; + } + fb->_DepthMaxF = (GLfloat) fb->_DepthMax; + + /* Minimum resolvable depth value, for polygon offset */ + fb->_MRD = (GLfloat)1.0 / fb->_DepthMaxF; +} + +/** + * Create and initialize a gl_framebuffer object. + * This is intended for creating _window_system_ framebuffers, not generic + * framebuffer objects ala GL_EXT_framebuffer_object. + * + * \sa _mesa_new_framebuffer + */ +struct gl_framebuffer * +_mesa_create_framebuffer(const struct gl_config *visual) +{ + struct gl_framebuffer *fb = CALLOC_STRUCT(gl_framebuffer); + assert(visual); + if (fb) { + _mesa_initialize_window_framebuffer(fb, visual); + } + return fb; +} + + +/** + * Allocate a new gl_framebuffer object. + * This is the default function for ctx->Driver.NewFramebuffer(). + * This is for allocating user-created framebuffers, not window-system + * framebuffers! + * \sa _mesa_create_framebuffer + */ +struct gl_framebuffer * +_mesa_new_framebuffer(struct gl_context *ctx, GLuint name) +{ + struct gl_framebuffer *fb; + (void) ctx; + assert(name != 0); + fb = CALLOC_STRUCT(gl_framebuffer); + if (fb) { + _mesa_initialize_user_framebuffer(fb, name); + } + return fb; +} + + +/** + * Initialize a gl_framebuffer object. Typically used to initialize + * window system-created framebuffers, not user-created framebuffers. + * \sa _mesa_initialize_user_framebuffer + */ +void +_mesa_initialize_window_framebuffer(struct gl_framebuffer *fb, + const struct gl_config *visual) +{ + assert(fb); + assert(visual); + + memset(fb, 0, sizeof(struct gl_framebuffer)); + + _glthread_INIT_MUTEX(fb->Mutex); + + fb->RefCount = 1; + + /* save the visual */ + fb->Visual = *visual; + + /* Init read/draw renderbuffer state */ + if (visual->doubleBufferMode) { + fb->_NumColorDrawBuffers = 1; + fb->ColorDrawBuffer[0] = GL_BACK; + fb->_ColorDrawBufferIndexes[0] = BUFFER_BACK_LEFT; + fb->ColorReadBuffer = GL_BACK; + fb->_ColorReadBufferIndex = BUFFER_BACK_LEFT; + } + else { + fb->_NumColorDrawBuffers = 1; + fb->ColorDrawBuffer[0] = GL_FRONT; + fb->_ColorDrawBufferIndexes[0] = BUFFER_FRONT_LEFT; + fb->ColorReadBuffer = GL_FRONT; + fb->_ColorReadBufferIndex = BUFFER_FRONT_LEFT; + } + + fb->Delete = _mesa_destroy_framebuffer; + fb->_Status = GL_FRAMEBUFFER_COMPLETE_EXT; + + compute_depth_max(fb); +} + + +/** + * Initialize a user-created gl_framebuffer object. + * \sa _mesa_initialize_window_framebuffer + */ +void +_mesa_initialize_user_framebuffer(struct gl_framebuffer *fb, GLuint name) +{ + assert(fb); + assert(name); + + memset(fb, 0, sizeof(struct gl_framebuffer)); + + fb->Name = name; + fb->RefCount = 1; + fb->_NumColorDrawBuffers = 1; + fb->ColorDrawBuffer[0] = GL_COLOR_ATTACHMENT0_EXT; + fb->_ColorDrawBufferIndexes[0] = BUFFER_COLOR0; + fb->ColorReadBuffer = GL_COLOR_ATTACHMENT0_EXT; + fb->_ColorReadBufferIndex = BUFFER_COLOR0; + fb->Delete = _mesa_destroy_framebuffer; + _glthread_INIT_MUTEX(fb->Mutex); +} + + +/** + * Deallocate buffer and everything attached to it. + * Typically called via the gl_framebuffer->Delete() method. + */ +void +_mesa_destroy_framebuffer(struct gl_framebuffer *fb) +{ + if (fb) { + _mesa_free_framebuffer_data(fb); + free(fb); + } +} + + +/** + * Free all the data hanging off the given gl_framebuffer, but don't free + * the gl_framebuffer object itself. + */ +void +_mesa_free_framebuffer_data(struct gl_framebuffer *fb) +{ + GLuint i; + + assert(fb); + assert(fb->RefCount == 0); + + _glthread_DESTROY_MUTEX(fb->Mutex); + + for (i = 0; i < BUFFER_COUNT; i++) { + struct gl_renderbuffer_attachment *att = &fb->Attachment[i]; + if (att->Renderbuffer) { + _mesa_reference_renderbuffer(&att->Renderbuffer, NULL); + } + if (att->Texture) { + _mesa_reference_texobj(&att->Texture, NULL); + } + ASSERT(!att->Renderbuffer); + ASSERT(!att->Texture); + att->Type = GL_NONE; + } + + /* unbind _Depth/_StencilBuffer to decr ref counts */ + _mesa_reference_renderbuffer(&fb->_DepthBuffer, NULL); + _mesa_reference_renderbuffer(&fb->_StencilBuffer, NULL); +} + + +/** + * Set *ptr to point to fb, with refcounting and locking. + */ +void +_mesa_reference_framebuffer(struct gl_framebuffer **ptr, + struct gl_framebuffer *fb) +{ + assert(ptr); + if (*ptr == fb) { + /* no change */ + return; + } + + if (*ptr) { + /* unreference old renderbuffer */ + GLboolean deleteFlag = GL_FALSE; + struct gl_framebuffer *oldFb = *ptr; + + _glthread_LOCK_MUTEX(oldFb->Mutex); + ASSERT(oldFb->RefCount > 0); + oldFb->RefCount--; + deleteFlag = (oldFb->RefCount == 0); + _glthread_UNLOCK_MUTEX(oldFb->Mutex); + + if (deleteFlag) + oldFb->Delete(oldFb); + + *ptr = NULL; + } + assert(!*ptr); + + if (fb) { + _glthread_LOCK_MUTEX(fb->Mutex); + fb->RefCount++; + _glthread_UNLOCK_MUTEX(fb->Mutex); + *ptr = fb; + } +} + + +/** + * Resize the given framebuffer's renderbuffers to the new width and height. + * This should only be used for window-system framebuffers, not + * user-created renderbuffers (i.e. made with GL_EXT_framebuffer_object). + * This will typically be called via ctx->Driver.ResizeBuffers() or directly + * from a device driver. + * + * \note it's possible for ctx to be null since a window can be resized + * without a currently bound rendering context. + */ +void +_mesa_resize_framebuffer(struct gl_context *ctx, struct gl_framebuffer *fb, + GLuint width, GLuint height) +{ + GLuint i; + + /* XXX I think we could check if the size is not changing + * and return early. + */ + + /* For window system framebuffers, Name is zero */ + assert(fb->Name == 0); + + for (i = 0; i < BUFFER_COUNT; i++) { + struct gl_renderbuffer_attachment *att = &fb->Attachment[i]; + if (att->Type == GL_RENDERBUFFER_EXT && att->Renderbuffer) { + struct gl_renderbuffer *rb = att->Renderbuffer; + /* only resize if size is changing */ + if (rb->Width != width || rb->Height != height) { + if (rb->AllocStorage(ctx, rb, rb->InternalFormat, width, height)) { + ASSERT(rb->Width == width); + ASSERT(rb->Height == height); + } + else { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "Resizing framebuffer"); + /* no return */ + } + } + } + } + + if (fb->_DepthBuffer) { + struct gl_renderbuffer *rb = fb->_DepthBuffer; + if (rb->Width != width || rb->Height != height) { + if (!rb->AllocStorage(ctx, rb, rb->InternalFormat, width, height)) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "Resizing framebuffer"); + } + } + } + + if (fb->_StencilBuffer) { + struct gl_renderbuffer *rb = fb->_StencilBuffer; + if (rb->Width != width || rb->Height != height) { + if (!rb->AllocStorage(ctx, rb, rb->InternalFormat, width, height)) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "Resizing framebuffer"); + } + } + } + + fb->Width = width; + fb->Height = height; + + if (ctx) { + /* update scissor / window bounds */ + _mesa_update_draw_buffer_bounds(ctx); + /* Signal new buffer state so that swrast will update its clipping + * info (the CLIP_BIT flag). + */ + ctx->NewState |= _NEW_BUFFERS; + } +} + + + +/** + * XXX THIS IS OBSOLETE - drivers should take care of detecting window + * size changes and act accordingly, likely calling _mesa_resize_framebuffer(). + * + * GL_MESA_resize_buffers extension. + * + * When this function is called, we'll ask the window system how large + * the current window is. If it's a new size, we'll call the driver's + * ResizeBuffers function. The driver will then resize its color buffers + * as needed, and maybe call the swrast's routine for reallocating + * swrast-managed depth/stencil/accum/etc buffers. + * \note This function should only be called through the GL API, not + * from device drivers (as was done in the past). + */ +void +_mesa_resizebuffers( struct gl_context *ctx ) +{ + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH( ctx ); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glResizeBuffersMESA\n"); + + if (!ctx->Driver.GetBufferSize) { + return; + } + + if (ctx->WinSysDrawBuffer) { + GLuint newWidth, newHeight; + struct gl_framebuffer *buffer = ctx->WinSysDrawBuffer; + + assert(buffer->Name == 0); + + /* ask device driver for size of output buffer */ + ctx->Driver.GetBufferSize( buffer, &newWidth, &newHeight ); + + /* see if size of device driver's color buffer (window) has changed */ + if (buffer->Width != newWidth || buffer->Height != newHeight) { + if (ctx->Driver.ResizeBuffers) + ctx->Driver.ResizeBuffers(ctx, buffer, newWidth, newHeight ); + } + } + + if (ctx->WinSysReadBuffer + && ctx->WinSysReadBuffer != ctx->WinSysDrawBuffer) { + GLuint newWidth, newHeight; + struct gl_framebuffer *buffer = ctx->WinSysReadBuffer; + + assert(buffer->Name == 0); + + /* ask device driver for size of read buffer */ + ctx->Driver.GetBufferSize( buffer, &newWidth, &newHeight ); + + /* see if size of device driver's color buffer (window) has changed */ + if (buffer->Width != newWidth || buffer->Height != newHeight) { + if (ctx->Driver.ResizeBuffers) + ctx->Driver.ResizeBuffers(ctx, buffer, newWidth, newHeight ); + } + } + + ctx->NewState |= _NEW_BUFFERS; /* to update scissor / window bounds */ +} + + +/* + * XXX THIS IS OBSOLETE + */ +void GLAPIENTRY +_mesa_ResizeBuffersMESA( void ) +{ + GET_CURRENT_CONTEXT(ctx); + + if (ctx->Extensions.MESA_resize_buffers) + _mesa_resizebuffers( ctx ); +} + + + +/** + * Examine all the framebuffer's renderbuffers to update the Width/Height + * fields of the framebuffer. If we have renderbuffers with different + * sizes, set the framebuffer's width and height to the min size. + * Note: this is only intended for user-created framebuffers, not + * window-system framebuffes. + */ +static void +update_framebuffer_size(struct gl_context *ctx, struct gl_framebuffer *fb) +{ + GLuint minWidth = ~0, minHeight = ~0; + GLuint i; + + /* user-created framebuffers only */ + assert(fb->Name); + + for (i = 0; i < BUFFER_COUNT; i++) { + struct gl_renderbuffer_attachment *att = &fb->Attachment[i]; + const struct gl_renderbuffer *rb = att->Renderbuffer; + if (rb) { + minWidth = MIN2(minWidth, rb->Width); + minHeight = MIN2(minHeight, rb->Height); + } + } + + if (minWidth != ~0) { + fb->Width = minWidth; + fb->Height = minHeight; + } + else { + fb->Width = 0; + fb->Height = 0; + } +} + + +/** + * Update the context's current drawing buffer's Xmin, Xmax, Ymin, Ymax fields. + * These values are computed from the buffer's width and height and + * the scissor box, if it's enabled. + * \param ctx the GL context. + */ +void +_mesa_update_draw_buffer_bounds(struct gl_context *ctx) +{ + struct gl_framebuffer *buffer = ctx->DrawBuffer; + + if (!buffer) + return; + + if (buffer->Name) { + /* user-created framebuffer size depends on the renderbuffers */ + update_framebuffer_size(ctx, buffer); + } + + buffer->_Xmin = 0; + buffer->_Ymin = 0; + buffer->_Xmax = buffer->Width; + buffer->_Ymax = buffer->Height; + + if (ctx->Scissor.Enabled) { + if (ctx->Scissor.X > buffer->_Xmin) { + buffer->_Xmin = ctx->Scissor.X; + } + if (ctx->Scissor.Y > buffer->_Ymin) { + buffer->_Ymin = ctx->Scissor.Y; + } + if (ctx->Scissor.X + ctx->Scissor.Width < buffer->_Xmax) { + buffer->_Xmax = ctx->Scissor.X + ctx->Scissor.Width; + } + if (ctx->Scissor.Y + ctx->Scissor.Height < buffer->_Ymax) { + buffer->_Ymax = ctx->Scissor.Y + ctx->Scissor.Height; + } + /* finally, check for empty region */ + if (buffer->_Xmin > buffer->_Xmax) { + buffer->_Xmin = buffer->_Xmax; + } + if (buffer->_Ymin > buffer->_Ymax) { + buffer->_Ymin = buffer->_Ymax; + } + } + + ASSERT(buffer->_Xmin <= buffer->_Xmax); + ASSERT(buffer->_Ymin <= buffer->_Ymax); +} + + +/** + * The glGet queries of the framebuffer red/green/blue size, stencil size, + * etc. are satisfied by the fields of ctx->DrawBuffer->Visual. These can + * change depending on the renderbuffer bindings. This function updates + * the given framebuffer's Visual from the current renderbuffer bindings. + * + * This may apply to user-created framebuffers or window system framebuffers. + * + * Also note: ctx->DrawBuffer->Visual.depthBits might not equal + * ctx->DrawBuffer->Attachment[BUFFER_DEPTH].Renderbuffer.DepthBits. + * The former one is used to convert floating point depth values into + * integer Z values. + */ +void +_mesa_update_framebuffer_visual(struct gl_context *ctx, + struct gl_framebuffer *fb) +{ + GLuint i; + + memset(&fb->Visual, 0, sizeof(fb->Visual)); + fb->Visual.rgbMode = GL_TRUE; /* assume this */ + +#if 0 /* this _might_ be needed */ + if (fb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { + /* leave visual fields zero'd */ + return; + } +#endif + + /* find first RGB renderbuffer */ + for (i = 0; i < BUFFER_COUNT; i++) { + if (fb->Attachment[i].Renderbuffer) { + const struct gl_renderbuffer *rb = fb->Attachment[i].Renderbuffer; + const GLenum baseFormat = _mesa_get_format_base_format(rb->Format); + const gl_format fmt = rb->Format; + + if (_mesa_is_legal_color_format(ctx, baseFormat)) { + fb->Visual.redBits = _mesa_get_format_bits(fmt, GL_RED_BITS); + fb->Visual.greenBits = _mesa_get_format_bits(fmt, GL_GREEN_BITS); + fb->Visual.blueBits = _mesa_get_format_bits(fmt, GL_BLUE_BITS); + fb->Visual.alphaBits = _mesa_get_format_bits(fmt, GL_ALPHA_BITS); + fb->Visual.rgbBits = fb->Visual.redBits + + fb->Visual.greenBits + fb->Visual.blueBits; + fb->Visual.floatMode = GL_FALSE; + fb->Visual.samples = rb->NumSamples; + if (_mesa_get_format_color_encoding(fmt) == GL_SRGB) + fb->Visual.sRGBCapable = ctx->Const.sRGBCapable; + break; + } + } + } + + if (fb->Attachment[BUFFER_DEPTH].Renderbuffer) { + const struct gl_renderbuffer *rb = + fb->Attachment[BUFFER_DEPTH].Renderbuffer; + const gl_format fmt = rb->Format; + fb->Visual.haveDepthBuffer = GL_TRUE; + fb->Visual.depthBits = _mesa_get_format_bits(fmt, GL_DEPTH_BITS); + } + + if (fb->Attachment[BUFFER_STENCIL].Renderbuffer) { + const struct gl_renderbuffer *rb = + fb->Attachment[BUFFER_STENCIL].Renderbuffer; + const gl_format fmt = rb->Format; + fb->Visual.haveStencilBuffer = GL_TRUE; + fb->Visual.stencilBits = _mesa_get_format_bits(fmt, GL_STENCIL_BITS); + } + + if (fb->Attachment[BUFFER_ACCUM].Renderbuffer) { + const struct gl_renderbuffer *rb = + fb->Attachment[BUFFER_ACCUM].Renderbuffer; + const gl_format fmt = rb->Format; + fb->Visual.haveAccumBuffer = GL_TRUE; + fb->Visual.accumRedBits = _mesa_get_format_bits(fmt, GL_RED_BITS); + fb->Visual.accumGreenBits = _mesa_get_format_bits(fmt, GL_GREEN_BITS); + fb->Visual.accumBlueBits = _mesa_get_format_bits(fmt, GL_BLUE_BITS); + fb->Visual.accumAlphaBits = _mesa_get_format_bits(fmt, GL_ALPHA_BITS); + } + + compute_depth_max(fb); +} + + +/** + * Update the framebuffer's _DepthBuffer field using the renderbuffer + * found at the given attachment index. + * + * If that attachment points to a combined GL_DEPTH_STENCIL renderbuffer, + * create and install a depth wrapper/adaptor. + * + * \param fb the framebuffer whose _DepthBuffer field to update + * \param attIndex indicates the renderbuffer to possibly wrap + */ +void +_mesa_update_depth_buffer(struct gl_context *ctx, + struct gl_framebuffer *fb, + GLuint attIndex) +{ + struct gl_renderbuffer *depthRb; + + /* only one possiblity for now */ + ASSERT(attIndex == BUFFER_DEPTH); + + depthRb = fb->Attachment[attIndex].Renderbuffer; + + if (depthRb && _mesa_is_format_packed_depth_stencil(depthRb->Format)) { + /* The attached depth buffer is a GL_DEPTH_STENCIL renderbuffer */ + if (!fb->_DepthBuffer + || fb->_DepthBuffer->Wrapped != depthRb + || _mesa_get_format_base_format(fb->_DepthBuffer->Format) != GL_DEPTH_COMPONENT) { + /* need to update wrapper */ + struct gl_renderbuffer *wrapper + = _mesa_new_z24_renderbuffer_wrapper(ctx, depthRb); + _mesa_reference_renderbuffer(&fb->_DepthBuffer, wrapper); + ASSERT(fb->_DepthBuffer->Wrapped == depthRb); + } + } + else { + /* depthRb may be null */ + _mesa_reference_renderbuffer(&fb->_DepthBuffer, depthRb); + } +} + + +/** + * Update the framebuffer's _StencilBuffer field using the renderbuffer + * found at the given attachment index. + * + * If that attachment points to a combined GL_DEPTH_STENCIL renderbuffer, + * create and install a stencil wrapper/adaptor. + * + * \param fb the framebuffer whose _StencilBuffer field to update + * \param attIndex indicates the renderbuffer to possibly wrap + */ +void +_mesa_update_stencil_buffer(struct gl_context *ctx, + struct gl_framebuffer *fb, + GLuint attIndex) +{ + struct gl_renderbuffer *stencilRb; + + ASSERT(attIndex == BUFFER_DEPTH || + attIndex == BUFFER_STENCIL); + + stencilRb = fb->Attachment[attIndex].Renderbuffer; + + if (stencilRb && _mesa_is_format_packed_depth_stencil(stencilRb->Format)) { + /* The attached stencil buffer is a GL_DEPTH_STENCIL renderbuffer */ + if (!fb->_StencilBuffer + || fb->_StencilBuffer->Wrapped != stencilRb + || _mesa_get_format_base_format(fb->_StencilBuffer->Format) != GL_STENCIL_INDEX) { + /* need to update wrapper */ + struct gl_renderbuffer *wrapper + = _mesa_new_s8_renderbuffer_wrapper(ctx, stencilRb); + _mesa_reference_renderbuffer(&fb->_StencilBuffer, wrapper); + ASSERT(fb->_StencilBuffer->Wrapped == stencilRb); + } + } + else { + /* stencilRb may be null */ + _mesa_reference_renderbuffer(&fb->_StencilBuffer, stencilRb); + } +} + + +/* + * Example DrawBuffers scenarios: + * + * 1. glDrawBuffer(GL_FRONT_AND_BACK), fixed-func or shader writes to + * "gl_FragColor" or program writes to the "result.color" register: + * + * fragment color output renderbuffer + * --------------------- --------------- + * color[0] Front, Back + * + * + * 2. glDrawBuffers(3, [GL_FRONT, GL_AUX0, GL_AUX1]), shader writes to + * gl_FragData[i] or program writes to result.color[i] registers: + * + * fragment color output renderbuffer + * --------------------- --------------- + * color[0] Front + * color[1] Aux0 + * color[3] Aux1 + * + * + * 3. glDrawBuffers(3, [GL_FRONT, GL_AUX0, GL_AUX1]) and shader writes to + * gl_FragColor, or fixed function: + * + * fragment color output renderbuffer + * --------------------- --------------- + * color[0] Front, Aux0, Aux1 + * + * + * In either case, the list of renderbuffers is stored in the + * framebuffer->_ColorDrawBuffers[] array and + * framebuffer->_NumColorDrawBuffers indicates the number of buffers. + * The renderer (like swrast) has to look at the current fragment shader + * to see if it writes to gl_FragColor vs. gl_FragData[i] to determine + * how to map color outputs to renderbuffers. + * + * Note that these two calls are equivalent (for fixed function fragment + * shading anyway): + * a) glDrawBuffer(GL_FRONT_AND_BACK); (assuming non-stereo framebuffer) + * b) glDrawBuffers(2, [GL_FRONT_LEFT, GL_BACK_LEFT]); + */ + + + + +/** + * Update the (derived) list of color drawing renderbuffer pointers. + * Later, when we're rendering we'll loop from 0 to _NumColorDrawBuffers + * writing colors. + */ +static void +update_color_draw_buffers(struct gl_context *ctx, struct gl_framebuffer *fb) +{ + GLuint output; + + /* set 0th buffer to NULL now in case _NumColorDrawBuffers is zero */ + fb->_ColorDrawBuffers[0] = NULL; + + for (output = 0; output < fb->_NumColorDrawBuffers; output++) { + GLint buf = fb->_ColorDrawBufferIndexes[output]; + if (buf >= 0) { + fb->_ColorDrawBuffers[output] = fb->Attachment[buf].Renderbuffer; + } + else { + fb->_ColorDrawBuffers[output] = NULL; + } + } +} + + +/** + * Update the (derived) color read renderbuffer pointer. + * Unlike the DrawBuffer, we can only read from one (or zero) color buffers. + */ +static void +update_color_read_buffer(struct gl_context *ctx, struct gl_framebuffer *fb) +{ + (void) ctx; + if (fb->_ColorReadBufferIndex == -1 || + fb->DeletePending || + fb->Width == 0 || + fb->Height == 0) { + fb->_ColorReadBuffer = NULL; /* legal! */ + } + else { + ASSERT(fb->_ColorReadBufferIndex >= 0); + ASSERT(fb->_ColorReadBufferIndex < BUFFER_COUNT); + fb->_ColorReadBuffer + = fb->Attachment[fb->_ColorReadBufferIndex].Renderbuffer; + } +} + + +/** + * Update a gl_framebuffer's derived state. + * + * Specifically, update these framebuffer fields: + * _ColorDrawBuffers + * _NumColorDrawBuffers + * _ColorReadBuffer + * _DepthBuffer + * _StencilBuffer + * + * If the framebuffer is user-created, make sure it's complete. + * + * The following functions (at least) can effect framebuffer state: + * glReadBuffer, glDrawBuffer, glDrawBuffersARB, glFramebufferRenderbufferEXT, + * glRenderbufferStorageEXT. + */ +static void +update_framebuffer(struct gl_context *ctx, struct gl_framebuffer *fb) +{ + if (fb->Name == 0) { + /* This is a window-system framebuffer */ + /* Need to update the FB's GL_DRAW_BUFFER state to match the + * context state (GL_READ_BUFFER too). + */ + if (fb->ColorDrawBuffer[0] != ctx->Color.DrawBuffer[0]) { + _mesa_drawbuffers(ctx, ctx->Const.MaxDrawBuffers, + ctx->Color.DrawBuffer, NULL); + } + if (fb->ColorReadBuffer != ctx->Pixel.ReadBuffer) { + + } + } + else { + /* This is a user-created framebuffer. + * Completeness only matters for user-created framebuffers. + */ + if (fb->_Status != GL_FRAMEBUFFER_COMPLETE) { + _mesa_test_framebuffer_completeness(ctx, fb); + } + } + + /* Strictly speaking, we don't need to update the draw-state + * if this FB is bound as ctx->ReadBuffer (and conversely, the + * read-state if this FB is bound as ctx->DrawBuffer), but no + * harm. + */ + update_color_draw_buffers(ctx, fb); + update_color_read_buffer(ctx, fb); + _mesa_update_depth_buffer(ctx, fb, BUFFER_DEPTH); + _mesa_update_stencil_buffer(ctx, fb, BUFFER_STENCIL); + + compute_depth_max(fb); +} + + +/** + * Update state related to the current draw/read framebuffers. + */ +void +_mesa_update_framebuffer(struct gl_context *ctx) +{ + struct gl_framebuffer *drawFb; + struct gl_framebuffer *readFb; + + assert(ctx); + drawFb = ctx->DrawBuffer; + readFb = ctx->ReadBuffer; + + update_framebuffer(ctx, drawFb); + if (readFb != drawFb) + update_framebuffer(ctx, readFb); +} + + +/** + * Check if the renderbuffer for a read operation (glReadPixels, glCopyPixels, + * glCopyTex[Sub]Image, etc) exists. + * \param format a basic image format such as GL_RGB, GL_RGBA, GL_ALPHA, + * GL_DEPTH_COMPONENT, etc. or GL_COLOR, GL_DEPTH, GL_STENCIL. + * \return GL_TRUE if buffer exists, GL_FALSE otherwise + */ +GLboolean +_mesa_source_buffer_exists(struct gl_context *ctx, GLenum format) +{ + const struct gl_renderbuffer_attachment *att = ctx->ReadBuffer->Attachment; + + /* If we don't know the framebuffer status, update it now */ + if (ctx->ReadBuffer->_Status == 0) { + _mesa_test_framebuffer_completeness(ctx, ctx->ReadBuffer); + } + + if (ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { + return GL_FALSE; + } + + switch (format) { + case GL_COLOR: + case GL_RED: + case GL_GREEN: + case GL_BLUE: + case GL_ALPHA: + case GL_LUMINANCE: + case GL_LUMINANCE_ALPHA: + case GL_INTENSITY: + case GL_RG: + case GL_RGB: + case GL_BGR: + case GL_RGBA: + case GL_BGRA: + case GL_ABGR_EXT: + case GL_COLOR_INDEX: + case GL_RED_INTEGER_EXT: + case GL_GREEN_INTEGER_EXT: + case GL_BLUE_INTEGER_EXT: + case GL_ALPHA_INTEGER_EXT: + case GL_RGB_INTEGER_EXT: + case GL_RGBA_INTEGER_EXT: + case GL_BGR_INTEGER_EXT: + case GL_BGRA_INTEGER_EXT: + case GL_LUMINANCE_INTEGER_EXT: + case GL_LUMINANCE_ALPHA_INTEGER_EXT: + if (ctx->ReadBuffer->_ColorReadBuffer == NULL) { + return GL_FALSE; + } + ASSERT(_mesa_get_format_bits(ctx->ReadBuffer->_ColorReadBuffer->Format, GL_RED_BITS) > 0 || + _mesa_get_format_bits(ctx->ReadBuffer->_ColorReadBuffer->Format, GL_ALPHA_BITS) > 0 || + _mesa_get_format_bits(ctx->ReadBuffer->_ColorReadBuffer->Format, GL_INDEX_BITS) > 0); + break; + case GL_DEPTH: + case GL_DEPTH_COMPONENT: + if (!att[BUFFER_DEPTH].Renderbuffer) { + return GL_FALSE; + } + /*ASSERT(att[BUFFER_DEPTH].Renderbuffer->DepthBits > 0);*/ + break; + case GL_STENCIL: + case GL_STENCIL_INDEX: + if (!att[BUFFER_STENCIL].Renderbuffer) { + return GL_FALSE; + } + /*ASSERT(att[BUFFER_STENCIL].Renderbuffer->StencilBits > 0);*/ + break; + case GL_DEPTH_STENCIL_EXT: + if (!att[BUFFER_DEPTH].Renderbuffer || + !att[BUFFER_STENCIL].Renderbuffer) { + return GL_FALSE; + } + /* + ASSERT(att[BUFFER_DEPTH].Renderbuffer->DepthBits > 0); + ASSERT(att[BUFFER_STENCIL].Renderbuffer->StencilBits > 0); + */ + break; + default: + _mesa_problem(ctx, + "Unexpected format 0x%x in _mesa_source_buffer_exists", + format); + return GL_FALSE; + } + + /* OK */ + return GL_TRUE; +} + + +/** + * As above, but for drawing operations. + * XXX could do some code merging w/ above function. + */ +GLboolean +_mesa_dest_buffer_exists(struct gl_context *ctx, GLenum format) +{ + const struct gl_renderbuffer_attachment *att = ctx->DrawBuffer->Attachment; + + /* If we don't know the framebuffer status, update it now */ + if (ctx->DrawBuffer->_Status == 0) { + _mesa_test_framebuffer_completeness(ctx, ctx->DrawBuffer); + } + + if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { + return GL_FALSE; + } + + switch (format) { + case GL_COLOR: + case GL_RED: + case GL_GREEN: + case GL_BLUE: + case GL_ALPHA: + case GL_LUMINANCE: + case GL_LUMINANCE_ALPHA: + case GL_INTENSITY: + case GL_RGB: + case GL_BGR: + case GL_RGBA: + case GL_BGRA: + case GL_ABGR_EXT: + case GL_COLOR_INDEX: + case GL_RED_INTEGER_EXT: + case GL_GREEN_INTEGER_EXT: + case GL_BLUE_INTEGER_EXT: + case GL_ALPHA_INTEGER_EXT: + case GL_RGB_INTEGER_EXT: + case GL_RGBA_INTEGER_EXT: + case GL_BGR_INTEGER_EXT: + case GL_BGRA_INTEGER_EXT: + case GL_LUMINANCE_INTEGER_EXT: + case GL_LUMINANCE_ALPHA_INTEGER_EXT: + /* Nothing special since GL_DRAW_BUFFER could be GL_NONE. */ + /* Could assert that colorbuffer has RedBits > 0 */ + break; + case GL_DEPTH: + case GL_DEPTH_COMPONENT: + if (!att[BUFFER_DEPTH].Renderbuffer) { + return GL_FALSE; + } + /*ASSERT(att[BUFFER_DEPTH].Renderbuffer->DepthBits > 0);*/ + break; + case GL_STENCIL: + case GL_STENCIL_INDEX: + if (!att[BUFFER_STENCIL].Renderbuffer) { + return GL_FALSE; + } + /*ASSERT(att[BUFFER_STENCIL].Renderbuffer->StencilBits > 0);*/ + break; + case GL_DEPTH_STENCIL_EXT: + if (!att[BUFFER_DEPTH].Renderbuffer || + !att[BUFFER_STENCIL].Renderbuffer) { + return GL_FALSE; + } + /* + ASSERT(att[BUFFER_DEPTH].Renderbuffer->DepthBits > 0); + ASSERT(att[BUFFER_STENCIL].Renderbuffer->StencilBits > 0); + */ + break; + default: + _mesa_problem(ctx, + "Unexpected format 0x%x in _mesa_dest_buffer_exists", + format); + return GL_FALSE; + } + + /* OK */ + return GL_TRUE; +} + + +/** + * Used to answer the GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES query. + */ +GLenum +_mesa_get_color_read_format(struct gl_context *ctx) +{ + switch (ctx->ReadBuffer->_ColorReadBuffer->Format) { + case MESA_FORMAT_ARGB8888: + return GL_BGRA; + case MESA_FORMAT_RGB565: + return GL_BGR; + default: + return GL_RGBA; + } +} + + +/** + * Used to answer the GL_IMPLEMENTATION_COLOR_READ_TYPE_OES query. + */ +GLenum +_mesa_get_color_read_type(struct gl_context *ctx) +{ + switch (ctx->ReadBuffer->_ColorReadBuffer->Format) { + case MESA_FORMAT_ARGB8888: + return GL_UNSIGNED_BYTE; + case MESA_FORMAT_RGB565: + return GL_UNSIGNED_SHORT_5_6_5_REV; + default: + return GL_UNSIGNED_BYTE; + } +} + + +/** + * Print framebuffer info to stderr, for debugging. + */ +void +_mesa_print_framebuffer(const struct gl_framebuffer *fb) +{ + GLuint i; + + fprintf(stderr, "Mesa Framebuffer %u at %p\n", fb->Name, (void *) fb); + fprintf(stderr, " Size: %u x %u Status: %s\n", fb->Width, fb->Height, + _mesa_lookup_enum_by_nr(fb->_Status)); + fprintf(stderr, " Attachments:\n"); + + for (i = 0; i < BUFFER_COUNT; i++) { + const struct gl_renderbuffer_attachment *att = &fb->Attachment[i]; + if (att->Type == GL_TEXTURE) { + const struct gl_texture_image *texImage; + fprintf(stderr, + " %2d: Texture %u, level %u, face %u, slice %u, complete %d\n", + i, att->Texture->Name, att->TextureLevel, att->CubeMapFace, + att->Zoffset, att->Complete); + texImage = att->Texture->Image[att->CubeMapFace][att->TextureLevel]; + fprintf(stderr, " Size: %u x %u x %u Format %s\n", + texImage->Width, texImage->Height, texImage->Depth, + _mesa_get_format_name(texImage->TexFormat)); + } + else if (att->Type == GL_RENDERBUFFER) { + fprintf(stderr, " %2d: Renderbuffer %u, complete %d\n", + i, att->Renderbuffer->Name, att->Complete); + fprintf(stderr, " Size: %u x %u Format %s\n", + att->Renderbuffer->Width, att->Renderbuffer->Height, + _mesa_get_format_name(att->Renderbuffer->Format)); + } + else { + fprintf(stderr, " %2d: none\n", i); + } + } +} diff --git a/mesalib/src/mesa/main/mtypes.h b/mesalib/src/mesa/main/mtypes.h index 37f39ceef..4e7621239 100644 --- a/mesalib/src/mesa/main/mtypes.h +++ b/mesalib/src/mesa/main/mtypes.h @@ -2710,6 +2710,9 @@ struct gl_constants /** GL_EXT_gpu_shader4 */ GLint MinProgramTexelOffset, MaxProgramTexelOffset; + + /* GL_EXT_framebuffer_sRGB */ + GLboolean sRGBCapable; /* can enable sRGB blend/update on FBOs */ }; diff --git a/mesalib/src/mesa/main/varray.c b/mesalib/src/mesa/main/varray.c index 39855cbb0..cfed4b506 100644 --- a/mesalib/src/mesa/main/varray.c +++ b/mesalib/src/mesa/main/varray.c @@ -1,1209 +1,1205 @@ -/* - * 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. - */ - - -#include "glheader.h" -#include "imports.h" -#include "bufferobj.h" -#include "context.h" -#include "enable.h" -#include "enums.h" -#include "hash.h" -#include "image.h" -#include "macros.h" -#include "mfeatures.h" -#include "mtypes.h" -#include "varray.h" -#include "arrayobj.h" -#include "main/dispatch.h" - - -/** Used to do error checking for GL_EXT_vertex_array_bgra */ -#define BGRA_OR_4 5 - - -/** Used to indicate which GL datatypes are accepted by each of the - * glVertex/Color/Attrib/EtcPointer() functions. - */ -#define BOOL_BIT 0x1 -#define BYTE_BIT 0x2 -#define UNSIGNED_BYTE_BIT 0x4 -#define SHORT_BIT 0x8 -#define UNSIGNED_SHORT_BIT 0x10 -#define INT_BIT 0x20 -#define UNSIGNED_INT_BIT 0x40 -#define HALF_BIT 0x80 -#define FLOAT_BIT 0x100 -#define DOUBLE_BIT 0x200 -#define FIXED_BIT 0x400 - - - -/** Convert GL datatype enum into a _BIT value seen above */ -static GLbitfield -type_to_bit(const struct gl_context *ctx, GLenum type) -{ - switch (type) { - case GL_BOOL: - return BOOL_BIT; - case GL_BYTE: - return BYTE_BIT; - case GL_UNSIGNED_BYTE: - return UNSIGNED_BYTE_BIT; - case GL_SHORT: - return SHORT_BIT; - case GL_UNSIGNED_SHORT: - return UNSIGNED_SHORT_BIT; - case GL_INT: - return INT_BIT; - case GL_UNSIGNED_INT: - return UNSIGNED_INT_BIT; - case GL_HALF_FLOAT: - if (ctx->Extensions.ARB_half_float_vertex) - return HALF_BIT; - else - return 0x0; - case GL_FLOAT: - return FLOAT_BIT; - case GL_DOUBLE: - return DOUBLE_BIT; - case GL_FIXED: - return FIXED_BIT; - default: - return 0; - } -} - - -/** - * Do error checking and update state for glVertex/Color/TexCoord/...Pointer - * functions. - * - * \param func name of calling function used for error reporting - * \param array the array to update - * \param dirtyBit which bit to set in ctx->Array.NewState for this array - * \param legalTypes bitmask of *_BIT above indicating legal datatypes - * \param sizeMin min allowable size value - * \param sizeMax max allowable size value (may also be BGRA_OR_4) - * \param size components per element (1, 2, 3 or 4) - * \param type datatype of each component (GL_FLOAT, GL_INT, etc) - * \param stride stride between elements, in elements - * \param normalized are integer types converted to floats in [-1, 1]? - * \param integer integer-valued values (will not be normalized to [-1,1]) - * \param ptr the address (or offset inside VBO) of the array data - */ -static void -update_array(struct gl_context *ctx, - const char *func, - struct gl_client_array *array, - GLbitfield dirtyBit, GLbitfield legalTypesMask, - GLint sizeMin, GLint sizeMax, - GLint size, GLenum type, GLsizei stride, - GLboolean normalized, GLboolean integer, - const GLvoid *ptr) -{ - GLbitfield typeBit; - GLsizei elementSize; - GLenum format = GL_RGBA; - - if (ctx->API != API_OPENGLES && ctx->API != API_OPENGLES2) { - /* fixed point arrays / data is only allowed with OpenGL ES 1.x/2.0 */ - legalTypesMask &= ~FIXED_BIT; - } - - typeBit = type_to_bit(ctx, type); - if (typeBit == 0x0 || (typeBit & legalTypesMask) == 0x0) { - _mesa_error(ctx, GL_INVALID_ENUM, "%s(type = %s)", - func, _mesa_lookup_enum_by_nr(type)); - return; - } - - /* Do size parameter checking. - * If sizeMax = BGRA_OR_4 it means that size = GL_BGRA is legal and - * must be handled specially. - */ - if (ctx->Extensions.EXT_vertex_array_bgra && - sizeMax == BGRA_OR_4 && - size == GL_BGRA) { - if (type != GL_UNSIGNED_BYTE) { - _mesa_error(ctx, GL_INVALID_VALUE, "%s(GL_BGRA/GLubyte)", func); - return; - } - format = GL_BGRA; - size = 4; - } - else if (size < sizeMin || size > sizeMax || size > 4) { - _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d)", func, size); - return; - } - - ASSERT(size <= 4); - - if (stride < 0) { - _mesa_error( ctx, GL_INVALID_VALUE, "%s(stride=%d)", func, stride ); - return; - } - - if (ctx->Array.ArrayObj->VBOonly && - ctx->Array.ArrayBufferObj->Name == 0) { - /* GL_ARB_vertex_array_object requires that all arrays reside in VBOs. - * Generate GL_INVALID_OPERATION if that's not true. - */ - _mesa_error(ctx, GL_INVALID_OPERATION, "%s(non-VBO array)", func); - return; - } - - elementSize = _mesa_sizeof_type(type) * size; - - array->Size = size; - array->Type = type; - array->Format = format; - array->Stride = stride; - array->StrideB = stride ? stride : elementSize; - array->Normalized = normalized; - array->Ptr = (const GLubyte *) ptr; - array->_ElementSize = elementSize; - - _mesa_reference_buffer_object(ctx, &array->BufferObj, - ctx->Array.ArrayBufferObj); - - ctx->NewState |= _NEW_ARRAY; - ctx->Array.NewState |= dirtyBit; -} - - -void GLAPIENTRY -_mesa_VertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) -{ - GLbitfield legalTypes = (SHORT_BIT | INT_BIT | FLOAT_BIT | - DOUBLE_BIT | HALF_BIT | FIXED_BIT); - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - if (ctx->API == API_OPENGLES) - legalTypes |= BYTE_BIT; - - update_array(ctx, "glVertexPointer", - &ctx->Array.ArrayObj->Vertex, _NEW_ARRAY_VERTEX, - legalTypes, 2, 4, - size, type, stride, GL_FALSE, GL_FALSE, ptr); -} - - -void GLAPIENTRY -_mesa_NormalPointer(GLenum type, GLsizei stride, const GLvoid *ptr ) -{ - const GLbitfield legalTypes = (BYTE_BIT | SHORT_BIT | INT_BIT | - HALF_BIT | FLOAT_BIT | DOUBLE_BIT | - FIXED_BIT); - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - update_array(ctx, "glNormalPointer", - &ctx->Array.ArrayObj->Normal, _NEW_ARRAY_NORMAL, - legalTypes, 3, 3, - 3, type, stride, GL_TRUE, GL_FALSE, ptr); -} - - -void GLAPIENTRY -_mesa_ColorPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) -{ - const GLbitfield legalTypes = (BYTE_BIT | UNSIGNED_BYTE_BIT | - SHORT_BIT | UNSIGNED_SHORT_BIT | - INT_BIT | UNSIGNED_INT_BIT | - HALF_BIT | FLOAT_BIT | DOUBLE_BIT | - FIXED_BIT); - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - update_array(ctx, "glColorPointer", - &ctx->Array.ArrayObj->Color, _NEW_ARRAY_COLOR0, - legalTypes, 3, BGRA_OR_4, - size, type, stride, GL_TRUE, GL_FALSE, ptr); -} - - -void GLAPIENTRY -_mesa_FogCoordPointerEXT(GLenum type, GLsizei stride, const GLvoid *ptr) -{ - const GLbitfield legalTypes = (HALF_BIT | FLOAT_BIT | DOUBLE_BIT); - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - update_array(ctx, "glFogCoordPointer", - &ctx->Array.ArrayObj->FogCoord, _NEW_ARRAY_FOGCOORD, - legalTypes, 1, 1, - 1, type, stride, GL_FALSE, GL_FALSE, ptr); -} - - -void GLAPIENTRY -_mesa_IndexPointer(GLenum type, GLsizei stride, const GLvoid *ptr) -{ - const GLbitfield legalTypes = (UNSIGNED_BYTE_BIT | SHORT_BIT | INT_BIT | - FLOAT_BIT | DOUBLE_BIT); - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - update_array(ctx, "glIndexPointer", - &ctx->Array.ArrayObj->Index, _NEW_ARRAY_INDEX, - legalTypes, 1, 1, - 1, type, stride, GL_FALSE, GL_FALSE, ptr); -} - - -void GLAPIENTRY -_mesa_SecondaryColorPointerEXT(GLint size, GLenum type, - GLsizei stride, const GLvoid *ptr) -{ - const GLbitfield legalTypes = (BYTE_BIT | UNSIGNED_BYTE_BIT | - SHORT_BIT | UNSIGNED_SHORT_BIT | - INT_BIT | UNSIGNED_INT_BIT | - HALF_BIT | FLOAT_BIT | DOUBLE_BIT); - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - update_array(ctx, "glSecondaryColorPointer", - &ctx->Array.ArrayObj->SecondaryColor, _NEW_ARRAY_COLOR1, - legalTypes, 3, BGRA_OR_4, - size, type, stride, GL_TRUE, GL_FALSE, ptr); -} - - -void GLAPIENTRY -_mesa_TexCoordPointer(GLint size, GLenum type, GLsizei stride, - const GLvoid *ptr) -{ - GLbitfield legalTypes = (SHORT_BIT | INT_BIT | - HALF_BIT | FLOAT_BIT | DOUBLE_BIT | - FIXED_BIT); - GET_CURRENT_CONTEXT(ctx); - const GLuint unit = ctx->Array.ActiveTexture; - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - if (ctx->API == API_OPENGLES) - legalTypes |= BYTE_BIT; - - ASSERT(unit < Elements(ctx->Array.ArrayObj->TexCoord)); - - update_array(ctx, "glTexCoordPointer", - &ctx->Array.ArrayObj->TexCoord[unit], - _NEW_ARRAY_TEXCOORD(unit), - legalTypes, 1, 4, - size, type, stride, GL_FALSE, GL_FALSE, - ptr); -} - - -void GLAPIENTRY -_mesa_EdgeFlagPointer(GLsizei stride, const GLvoid *ptr) -{ - const GLbitfield legalTypes = UNSIGNED_BYTE_BIT; - /* see table 2.4 edits in GL_EXT_gpu_shader4 spec: */ - const GLboolean integer = GL_TRUE; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - update_array(ctx, "glEdgeFlagPointer", - &ctx->Array.ArrayObj->EdgeFlag, _NEW_ARRAY_EDGEFLAG, - legalTypes, 1, 1, - 1, GL_UNSIGNED_BYTE, stride, GL_FALSE, integer, ptr); -} - - -void GLAPIENTRY -_mesa_PointSizePointer(GLenum type, GLsizei stride, const GLvoid *ptr) -{ - const GLbitfield legalTypes = (FLOAT_BIT | FIXED_BIT); - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - if (ctx->API != API_OPENGLES) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glPointSizePointer(ES 1.x only)"); - return; - } - - update_array(ctx, "glPointSizePointer", - &ctx->Array.ArrayObj->PointSize, _NEW_ARRAY_POINT_SIZE, - legalTypes, 1, 1, - 1, type, stride, GL_FALSE, GL_FALSE, ptr); -} - - -#if FEATURE_NV_vertex_program -/** - * Set a vertex attribute array. - * Note that these arrays DO alias the conventional GL vertex arrays - * (position, normal, color, fog, texcoord, etc). - * The generic attribute slots at #16 and above are not touched. - */ -void GLAPIENTRY -_mesa_VertexAttribPointerNV(GLuint index, GLint size, GLenum type, - GLsizei stride, const GLvoid *ptr) -{ - const GLbitfield legalTypes = (UNSIGNED_BYTE_BIT | SHORT_BIT | - FLOAT_BIT | DOUBLE_BIT); - GLboolean normalized = GL_FALSE; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (index >= MAX_NV_VERTEX_PROGRAM_INPUTS) { - _mesa_error(ctx, GL_INVALID_VALUE, "glVertexAttribPointerNV(index)"); - return; - } - - if (type == GL_UNSIGNED_BYTE && size != 4) { - _mesa_error(ctx, GL_INVALID_VALUE, "glVertexAttribPointerNV(size!=4)"); - return; - } - - update_array(ctx, "glVertexAttribPointerNV", - &ctx->Array.ArrayObj->VertexAttrib[index], - _NEW_ARRAY_ATTRIB(index), - legalTypes, 1, BGRA_OR_4, - size, type, stride, normalized, GL_FALSE, ptr); -} -#endif - - -#if FEATURE_ARB_vertex_program -/** - * Set a generic vertex attribute array. - * Note that these arrays DO NOT alias the conventional GL vertex arrays - * (position, normal, color, fog, texcoord, etc). - */ -void GLAPIENTRY -_mesa_VertexAttribPointerARB(GLuint index, GLint size, GLenum type, - GLboolean normalized, - GLsizei stride, const GLvoid *ptr) -{ - const GLbitfield legalTypes = (BYTE_BIT | UNSIGNED_BYTE_BIT | - SHORT_BIT | UNSIGNED_SHORT_BIT | - INT_BIT | UNSIGNED_INT_BIT | - HALF_BIT | FLOAT_BIT | DOUBLE_BIT | - FIXED_BIT); - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (index >= ctx->Const.VertexProgram.MaxAttribs) { - _mesa_error(ctx, GL_INVALID_VALUE, "glVertexAttribPointerARB(index)"); - return; - } - - update_array(ctx, "glVertexAttribPointer", - &ctx->Array.ArrayObj->VertexAttrib[index], - _NEW_ARRAY_ATTRIB(index), - legalTypes, 1, BGRA_OR_4, - size, type, stride, normalized, GL_FALSE, ptr); -} -#endif - - -/** - * GL_EXT_gpu_shader4 / GL 3.0. - * Set an integer-valued vertex attribute array. - * Note that these arrays DO NOT alias the conventional GL vertex arrays - * (position, normal, color, fog, texcoord, etc). - */ -void GLAPIENTRY -_mesa_VertexAttribIPointer(GLuint index, GLint size, GLenum type, - GLsizei stride, const GLvoid *ptr) -{ - const GLbitfield legalTypes = (BYTE_BIT | UNSIGNED_BYTE_BIT | - SHORT_BIT | UNSIGNED_SHORT_BIT | - INT_BIT | UNSIGNED_INT_BIT); - const GLboolean normalized = GL_FALSE; - const GLboolean integer = GL_TRUE; - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (index >= ctx->Const.VertexProgram.MaxAttribs) { - _mesa_error(ctx, GL_INVALID_VALUE, "glVertexAttribIPointer(index)"); - return; - } - - update_array(ctx, "glVertexAttribIPointer", - &ctx->Array.ArrayObj->VertexAttrib[index], - _NEW_ARRAY_ATTRIB(index), - legalTypes, 1, 4, - size, type, stride, normalized, integer, ptr); -} - - - -void GLAPIENTRY -_mesa_EnableVertexAttribArrayARB(GLuint index) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (index >= ctx->Const.VertexProgram.MaxAttribs) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glEnableVertexAttribArrayARB(index)"); - return; - } - - ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib)); - - FLUSH_VERTICES(ctx, _NEW_ARRAY); - ctx->Array.ArrayObj->VertexAttrib[index].Enabled = GL_TRUE; - ctx->Array.ArrayObj->_Enabled |= _NEW_ARRAY_ATTRIB(index); - ctx->Array.NewState |= _NEW_ARRAY_ATTRIB(index); -} - - -void GLAPIENTRY -_mesa_DisableVertexAttribArrayARB(GLuint index) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (index >= ctx->Const.VertexProgram.MaxAttribs) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glEnableVertexAttribArrayARB(index)"); - return; - } - - ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib)); - - FLUSH_VERTICES(ctx, _NEW_ARRAY); - ctx->Array.ArrayObj->VertexAttrib[index].Enabled = GL_FALSE; - ctx->Array.ArrayObj->_Enabled &= ~_NEW_ARRAY_ATTRIB(index); - ctx->Array.NewState |= _NEW_ARRAY_ATTRIB(index); -} - - -/** - * Return info for a vertex attribute array (no alias with legacy - * vertex attributes (pos, normal, color, etc)). This function does - * not handle the 4-element GL_CURRENT_VERTEX_ATTRIB_ARB query. - */ -static GLuint -get_vertex_array_attrib(struct gl_context *ctx, GLuint index, GLenum pname, - const char *caller) -{ - const struct gl_client_array *array; - - if (index >= MAX_VERTEX_GENERIC_ATTRIBS) { - _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%u)", caller, index); - return 0; - } - - ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib)); - - array = &ctx->Array.ArrayObj->VertexAttrib[index]; - - switch (pname) { - case GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB: - return array->Enabled; - case GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB: - return array->Size; - case GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB: - return array->Stride; - case GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB: - return array->Type; - case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB: - return array->Normalized; - case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB: - return array->BufferObj->Name; - case GL_VERTEX_ATTRIB_ARRAY_INTEGER: - if (ctx->Extensions.EXT_gpu_shader4) { - return array->Integer; - } - goto error; - case GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB: - if (ctx->Extensions.ARB_instanced_arrays) { - return array->InstanceDivisor; - } - goto error; - default: - ; /* fall-through */ - } - -error: - _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname=0x%x)", caller, pname); - return 0; -} - - -void GLAPIENTRY -_mesa_GetVertexAttribfvARB(GLuint index, GLenum pname, GLfloat *params) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) { - if (index == 0) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetVertexAttribfv(index==0)"); - } - else { - const GLfloat *v = ctx->Current.Attrib[VERT_ATTRIB_GENERIC0 + index]; - FLUSH_CURRENT(ctx, 0); - COPY_4V(params, v); - } - } - else { - params[0] = (GLfloat) get_vertex_array_attrib(ctx, index, pname, - "glGetVertexAttribfv"); - } -} - - -void GLAPIENTRY -_mesa_GetVertexAttribdvARB(GLuint index, GLenum pname, GLdouble *params) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) { - if (index == 0) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetVertexAttribdv(index==0)"); - } - else { - const GLfloat *v = ctx->Current.Attrib[VERT_ATTRIB_GENERIC0 + index]; - FLUSH_CURRENT(ctx, 0); - params[0] = (GLdouble) v[0]; - params[1] = (GLdouble) v[1]; - params[2] = (GLdouble) v[2]; - params[3] = (GLdouble) v[3]; - } - } - else { - params[0] = (GLdouble) get_vertex_array_attrib(ctx, index, pname, - "glGetVertexAttribdv"); - } -} - - -void GLAPIENTRY -_mesa_GetVertexAttribivARB(GLuint index, GLenum pname, GLint *params) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) { - if (index == 0) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetVertexAttribiv(index==0)"); - } - else { - const GLfloat *v = ctx->Current.Attrib[VERT_ATTRIB_GENERIC0 + index]; - FLUSH_CURRENT(ctx, 0); - /* XXX should floats in[0,1] be scaled to full int range? */ - params[0] = (GLint) v[0]; - params[1] = (GLint) v[1]; - params[2] = (GLint) v[2]; - params[3] = (GLint) v[3]; - } - } - else { - params[0] = (GLint) get_vertex_array_attrib(ctx, index, pname, - "glGetVertexAttribiv"); - } -} - - -/** GL 3.0 */ -void GLAPIENTRY -_mesa_GetVertexAttribIiv(GLuint index, GLenum pname, GLint *params) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) { - if (index == 0) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetVertexAttribIiv(index==0)"); - } - else { - const GLfloat *v = ctx->Current.Attrib[VERT_ATTRIB_GENERIC0 + index]; - FLUSH_CURRENT(ctx, 0); - /* XXX we don't have true integer-valued vertex attribs yet */ - params[0] = (GLint) v[0]; - params[1] = (GLint) v[1]; - params[2] = (GLint) v[2]; - params[3] = (GLint) v[3]; - } - } - else { - params[0] = (GLint) get_vertex_array_attrib(ctx, index, pname, - "glGetVertexAttribIiv"); - } -} - - -/** GL 3.0 */ -void GLAPIENTRY -_mesa_GetVertexAttribIuiv(GLuint index, GLenum pname, GLuint *params) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) { - if (index == 0) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glGetVertexAttribIuiv(index==0)"); - } - else { - const GLfloat *v = ctx->Current.Attrib[VERT_ATTRIB_GENERIC0 + index]; - FLUSH_CURRENT(ctx, 0); - /* XXX we don't have true integer-valued vertex attribs yet */ - params[0] = (GLuint) v[0]; - params[1] = (GLuint) v[1]; - params[2] = (GLuint) v[2]; - params[3] = (GLuint) v[3]; - } - } - else { - params[0] = get_vertex_array_attrib(ctx, index, pname, - "glGetVertexAttribIuiv"); - } -} - - -void GLAPIENTRY -_mesa_GetVertexAttribPointervARB(GLuint index, GLenum pname, GLvoid **pointer) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END(ctx); - - if (index >= ctx->Const.VertexProgram.MaxAttribs) { - _mesa_error(ctx, GL_INVALID_VALUE, "glGetVertexAttribPointerARB(index)"); - return; - } - - if (pname != GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB) { - _mesa_error(ctx, GL_INVALID_ENUM, "glGetVertexAttribPointerARB(pname)"); - return; - } - - ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib)); - - *pointer = (GLvoid *) ctx->Array.ArrayObj->VertexAttrib[index].Ptr; -} - - -void GLAPIENTRY -_mesa_VertexPointerEXT(GLint size, GLenum type, GLsizei stride, - GLsizei count, const GLvoid *ptr) -{ - (void) count; - _mesa_VertexPointer(size, type, stride, ptr); -} - - -void GLAPIENTRY -_mesa_NormalPointerEXT(GLenum type, GLsizei stride, GLsizei count, - const GLvoid *ptr) -{ - (void) count; - _mesa_NormalPointer(type, stride, ptr); -} - - -void GLAPIENTRY -_mesa_ColorPointerEXT(GLint size, GLenum type, GLsizei stride, GLsizei count, - const GLvoid *ptr) -{ - (void) count; - _mesa_ColorPointer(size, type, stride, ptr); -} - - -void GLAPIENTRY -_mesa_IndexPointerEXT(GLenum type, GLsizei stride, GLsizei count, - const GLvoid *ptr) -{ - (void) count; - _mesa_IndexPointer(type, stride, ptr); -} - - -void GLAPIENTRY -_mesa_TexCoordPointerEXT(GLint size, GLenum type, GLsizei stride, - GLsizei count, const GLvoid *ptr) -{ - (void) count; - _mesa_TexCoordPointer(size, type, stride, ptr); -} - - -void GLAPIENTRY -_mesa_EdgeFlagPointerEXT(GLsizei stride, GLsizei count, const GLboolean *ptr) -{ - (void) count; - _mesa_EdgeFlagPointer(stride, ptr); -} - - -void GLAPIENTRY -_mesa_InterleavedArrays(GLenum format, GLsizei stride, const GLvoid *pointer) -{ - GET_CURRENT_CONTEXT(ctx); - GLboolean tflag, cflag, nflag; /* enable/disable flags */ - GLint tcomps, ccomps, vcomps; /* components per texcoord, color, vertex */ - GLenum ctype = 0; /* color type */ - GLint coffset = 0, noffset = 0, voffset;/* color, normal, vertex offsets */ - const GLint toffset = 0; /* always zero */ - GLint defstride; /* default stride */ - GLint c, f; - - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - f = sizeof(GLfloat); - c = f * ((4 * sizeof(GLubyte) + (f - 1)) / f); - - if (stride < 0) { - _mesa_error( ctx, GL_INVALID_VALUE, "glInterleavedArrays(stride)" ); - return; - } - - switch (format) { - case GL_V2F: - tflag = GL_FALSE; cflag = GL_FALSE; nflag = GL_FALSE; - tcomps = 0; ccomps = 0; vcomps = 2; - voffset = 0; - defstride = 2*f; - break; - case GL_V3F: - tflag = GL_FALSE; cflag = GL_FALSE; nflag = GL_FALSE; - tcomps = 0; ccomps = 0; vcomps = 3; - voffset = 0; - defstride = 3*f; - break; - case GL_C4UB_V2F: - tflag = GL_FALSE; cflag = GL_TRUE; nflag = GL_FALSE; - tcomps = 0; ccomps = 4; vcomps = 2; - ctype = GL_UNSIGNED_BYTE; - coffset = 0; - voffset = c; - defstride = c + 2*f; - break; - case GL_C4UB_V3F: - tflag = GL_FALSE; cflag = GL_TRUE; nflag = GL_FALSE; - tcomps = 0; ccomps = 4; vcomps = 3; - ctype = GL_UNSIGNED_BYTE; - coffset = 0; - voffset = c; - defstride = c + 3*f; - break; - case GL_C3F_V3F: - tflag = GL_FALSE; cflag = GL_TRUE; nflag = GL_FALSE; - tcomps = 0; ccomps = 3; vcomps = 3; - ctype = GL_FLOAT; - coffset = 0; - voffset = 3*f; - defstride = 6*f; - break; - case GL_N3F_V3F: - tflag = GL_FALSE; cflag = GL_FALSE; nflag = GL_TRUE; - tcomps = 0; ccomps = 0; vcomps = 3; - noffset = 0; - voffset = 3*f; - defstride = 6*f; - break; - case GL_C4F_N3F_V3F: - tflag = GL_FALSE; cflag = GL_TRUE; nflag = GL_TRUE; - tcomps = 0; ccomps = 4; vcomps = 3; - ctype = GL_FLOAT; - coffset = 0; - noffset = 4*f; - voffset = 7*f; - defstride = 10*f; - break; - case GL_T2F_V3F: - tflag = GL_TRUE; cflag = GL_FALSE; nflag = GL_FALSE; - tcomps = 2; ccomps = 0; vcomps = 3; - voffset = 2*f; - defstride = 5*f; - break; - case GL_T4F_V4F: - tflag = GL_TRUE; cflag = GL_FALSE; nflag = GL_FALSE; - tcomps = 4; ccomps = 0; vcomps = 4; - voffset = 4*f; - defstride = 8*f; - break; - case GL_T2F_C4UB_V3F: - tflag = GL_TRUE; cflag = GL_TRUE; nflag = GL_FALSE; - tcomps = 2; ccomps = 4; vcomps = 3; - ctype = GL_UNSIGNED_BYTE; - coffset = 2*f; - voffset = c+2*f; - defstride = c+5*f; - break; - case GL_T2F_C3F_V3F: - tflag = GL_TRUE; cflag = GL_TRUE; nflag = GL_FALSE; - tcomps = 2; ccomps = 3; vcomps = 3; - ctype = GL_FLOAT; - coffset = 2*f; - voffset = 5*f; - defstride = 8*f; - break; - case GL_T2F_N3F_V3F: - tflag = GL_TRUE; cflag = GL_FALSE; nflag = GL_TRUE; - tcomps = 2; ccomps = 0; vcomps = 3; - noffset = 2*f; - voffset = 5*f; - defstride = 8*f; - break; - case GL_T2F_C4F_N3F_V3F: - tflag = GL_TRUE; cflag = GL_TRUE; nflag = GL_TRUE; - tcomps = 2; ccomps = 4; vcomps = 3; - ctype = GL_FLOAT; - coffset = 2*f; - noffset = 6*f; - voffset = 9*f; - defstride = 12*f; - break; - case GL_T4F_C4F_N3F_V4F: - tflag = GL_TRUE; cflag = GL_TRUE; nflag = GL_TRUE; - tcomps = 4; ccomps = 4; vcomps = 4; - ctype = GL_FLOAT; - coffset = 4*f; - noffset = 8*f; - voffset = 11*f; - defstride = 15*f; - break; - default: - _mesa_error( ctx, GL_INVALID_ENUM, "glInterleavedArrays(format)" ); - return; - } - - if (stride==0) { - stride = defstride; - } - - _mesa_DisableClientState( GL_EDGE_FLAG_ARRAY ); - _mesa_DisableClientState( GL_INDEX_ARRAY ); - /* XXX also disable secondary color and generic arrays? */ - - /* Texcoords */ - if (tflag) { - _mesa_EnableClientState( GL_TEXTURE_COORD_ARRAY ); - _mesa_TexCoordPointer( tcomps, GL_FLOAT, stride, - (GLubyte *) pointer + toffset ); - } - else { - _mesa_DisableClientState( GL_TEXTURE_COORD_ARRAY ); - } - - /* Color */ - if (cflag) { - _mesa_EnableClientState( GL_COLOR_ARRAY ); - _mesa_ColorPointer( ccomps, ctype, stride, - (GLubyte *) pointer + coffset ); - } - else { - _mesa_DisableClientState( GL_COLOR_ARRAY ); - } - - - /* Normals */ - if (nflag) { - _mesa_EnableClientState( GL_NORMAL_ARRAY ); - _mesa_NormalPointer( GL_FLOAT, stride, (GLubyte *) pointer + noffset ); - } - else { - _mesa_DisableClientState( GL_NORMAL_ARRAY ); - } - - /* Vertices */ - _mesa_EnableClientState( GL_VERTEX_ARRAY ); - _mesa_VertexPointer( vcomps, GL_FLOAT, stride, - (GLubyte *) pointer + voffset ); -} - - -void GLAPIENTRY -_mesa_LockArraysEXT(GLint first, GLsizei count) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, "glLockArrays %d %d\n", first, count); - - if (first < 0) { - _mesa_error( ctx, GL_INVALID_VALUE, "glLockArraysEXT(first)" ); - return; - } - if (count <= 0) { - _mesa_error( ctx, GL_INVALID_VALUE, "glLockArraysEXT(count)" ); - return; - } - if (ctx->Array.LockCount != 0) { - _mesa_error( ctx, GL_INVALID_OPERATION, "glLockArraysEXT(reentry)" ); - return; - } - - ctx->Array.LockFirst = first; - ctx->Array.LockCount = count; - - ctx->NewState |= _NEW_ARRAY; - ctx->Array.NewState |= _NEW_ARRAY_ALL; -} - - -void GLAPIENTRY -_mesa_UnlockArraysEXT( void ) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - if (MESA_VERBOSE & VERBOSE_API) - _mesa_debug(ctx, "glUnlockArrays\n"); - - if (ctx->Array.LockCount == 0) { - _mesa_error( ctx, GL_INVALID_OPERATION, "glUnlockArraysEXT(reexit)" ); - return; - } - - ctx->Array.LockFirst = 0; - ctx->Array.LockCount = 0; - ctx->NewState |= _NEW_ARRAY; - ctx->Array.NewState |= _NEW_ARRAY_ALL; -} - - -/* GL_EXT_multi_draw_arrays */ -void GLAPIENTRY -_mesa_MultiDrawArraysEXT( GLenum mode, const GLint *first, - const GLsizei *count, GLsizei primcount ) -{ - GET_CURRENT_CONTEXT(ctx); - GLint i; - - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - for (i = 0; i < primcount; i++) { - if (count[i] > 0) { - CALL_DrawArrays(ctx->Exec, (mode, first[i], count[i])); - } - } -} - - -/* GL_IBM_multimode_draw_arrays */ -void GLAPIENTRY -_mesa_MultiModeDrawArraysIBM( const GLenum * mode, const GLint * first, - const GLsizei * count, - GLsizei primcount, GLint modestride ) -{ - GET_CURRENT_CONTEXT(ctx); - GLint i; - - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - for ( i = 0 ; i < primcount ; i++ ) { - if ( count[i] > 0 ) { - GLenum m = *((GLenum *) ((GLubyte *) mode + i * modestride)); - CALL_DrawArrays(ctx->Exec, ( m, first[i], count[i] )); - } - } -} - - -/* GL_IBM_multimode_draw_arrays */ -void GLAPIENTRY -_mesa_MultiModeDrawElementsIBM( const GLenum * mode, const GLsizei * count, - GLenum type, const GLvoid * const * indices, - GLsizei primcount, GLint modestride ) -{ - GET_CURRENT_CONTEXT(ctx); - GLint i; - - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - /* XXX not sure about ARB_vertex_buffer_object handling here */ - - for ( i = 0 ; i < primcount ; i++ ) { - if ( count[i] > 0 ) { - GLenum m = *((GLenum *) ((GLubyte *) mode + i * modestride)); - CALL_DrawElements(ctx->Exec, ( m, count[i], type, indices[i] )); - } - } -} - - -/** - * GL_NV_primitive_restart and GL 3.1 - */ -void GLAPIENTRY -_mesa_PrimitiveRestartIndex(GLuint index) -{ - GET_CURRENT_CONTEXT(ctx); - - if (!ctx->Extensions.NV_primitive_restart && - ctx->VersionMajor * 10 + ctx->VersionMinor < 31) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glPrimitiveRestartIndexNV()"); - return; - } - - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - FLUSH_VERTICES(ctx, _NEW_TRANSFORM); - - ctx->Array.RestartIndex = index; -} - - -/** - * See GL_ARB_instanced_arrays. - * Note that the instance divisor only applies to generic arrays, not - * the legacy vertex arrays. - */ -void GLAPIENTRY -_mesa_VertexAttribDivisor(GLuint index, GLuint divisor) -{ - GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - if (!ctx->Extensions.ARB_instanced_arrays) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glVertexAttribDivisor()"); - return; - } - - if (index >= ctx->Const.VertexProgram.MaxAttribs) { - _mesa_error(ctx, GL_INVALID_ENUM, "glVertexAttribDivisor(index = %u)", - index); - return; - } - - ctx->Array.ArrayObj->VertexAttrib[index].InstanceDivisor = divisor; -} - - - -/** - * Copy one client vertex array to another. - */ -void -_mesa_copy_client_array(struct gl_context *ctx, - struct gl_client_array *dst, - struct gl_client_array *src) -{ - dst->Size = src->Size; - dst->Type = src->Type; - dst->Format = src->Format; - dst->Stride = src->Stride; - dst->StrideB = src->StrideB; - dst->Ptr = src->Ptr; - dst->Enabled = src->Enabled; - dst->Normalized = src->Normalized; - dst->Integer = src->Integer; - dst->InstanceDivisor = src->InstanceDivisor; - dst->_ElementSize = src->_ElementSize; - _mesa_reference_buffer_object(ctx, &dst->BufferObj, src->BufferObj); - dst->_MaxElement = src->_MaxElement; -} - - - -/** - * Print vertex array's fields. - */ -static void -print_array(const char *name, GLint index, const struct gl_client_array *array) -{ - if (index >= 0) - printf(" %s[%d]: ", name, index); - else - printf(" %s: ", name); - printf("Ptr=%p, Type=0x%x, Size=%d, ElemSize=%u, Stride=%d, Buffer=%u(Size %lu), MaxElem=%u\n", - array->Ptr, array->Type, array->Size, - array->_ElementSize, array->StrideB, - array->BufferObj->Name, (unsigned long) array->BufferObj->Size, - array->_MaxElement); -} - - -/** - * Print current vertex object/array info. For debug. - */ -void -_mesa_print_arrays(struct gl_context *ctx) -{ - struct gl_array_object *arrayObj = ctx->Array.ArrayObj; - GLuint i; - - _mesa_update_array_object_max_element(ctx, arrayObj); - - printf("Array Object %u\n", arrayObj->Name); - if (arrayObj->Vertex.Enabled) - print_array("Vertex", -1, &arrayObj->Vertex); - if (arrayObj->Normal.Enabled) - print_array("Normal", -1, &arrayObj->Normal); - if (arrayObj->Color.Enabled) - print_array("Color", -1, &arrayObj->Color); - for (i = 0; i < Elements(arrayObj->TexCoord); i++) - if (arrayObj->TexCoord[i].Enabled) - print_array("TexCoord", i, &arrayObj->TexCoord[i]); - for (i = 0; i < Elements(arrayObj->VertexAttrib); i++) - if (arrayObj->VertexAttrib[i].Enabled) - print_array("Attrib", i, &arrayObj->VertexAttrib[i]); - printf(" _MaxElement = %u\n", arrayObj->_MaxElement); -} - - -/** - * Initialize vertex array state for given context. - */ -void -_mesa_init_varray(struct gl_context *ctx) -{ - ctx->Array.DefaultArrayObj = _mesa_new_array_object(ctx, 0); - _mesa_reference_array_object(ctx, &ctx->Array.ArrayObj, - ctx->Array.DefaultArrayObj); - ctx->Array.ActiveTexture = 0; /* GL_ARB_multitexture */ - - ctx->Array.Objects = _mesa_NewHashTable(); -} - - -/** - * Callback for deleting an array object. Called by _mesa_HashDeleteAll(). - */ -static void -delete_arrayobj_cb(GLuint id, void *data, void *userData) -{ - struct gl_array_object *arrayObj = (struct gl_array_object *) data; - struct gl_context *ctx = (struct gl_context *) userData; - _mesa_delete_array_object(ctx, arrayObj); -} - - -/** - * Free vertex array state for given context. - */ -void -_mesa_free_varray_data(struct gl_context *ctx) -{ - _mesa_HashDeleteAll(ctx->Array.Objects, delete_arrayobj_cb, ctx); - _mesa_DeleteHashTable(ctx->Array.Objects); -} +/* + * 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. + */ + + +#include "glheader.h" +#include "imports.h" +#include "bufferobj.h" +#include "context.h" +#include "enable.h" +#include "enums.h" +#include "hash.h" +#include "image.h" +#include "macros.h" +#include "mfeatures.h" +#include "mtypes.h" +#include "varray.h" +#include "arrayobj.h" +#include "main/dispatch.h" + + +/** Used to do error checking for GL_EXT_vertex_array_bgra */ +#define BGRA_OR_4 5 + + +/** Used to indicate which GL datatypes are accepted by each of the + * glVertex/Color/Attrib/EtcPointer() functions. + */ +#define BOOL_BIT 0x1 +#define BYTE_BIT 0x2 +#define UNSIGNED_BYTE_BIT 0x4 +#define SHORT_BIT 0x8 +#define UNSIGNED_SHORT_BIT 0x10 +#define INT_BIT 0x20 +#define UNSIGNED_INT_BIT 0x40 +#define HALF_BIT 0x80 +#define FLOAT_BIT 0x100 +#define DOUBLE_BIT 0x200 +#define FIXED_BIT 0x400 + + + +/** Convert GL datatype enum into a _BIT value seen above */ +static GLbitfield +type_to_bit(const struct gl_context *ctx, GLenum type) +{ + switch (type) { + case GL_BOOL: + return BOOL_BIT; + case GL_BYTE: + return BYTE_BIT; + case GL_UNSIGNED_BYTE: + return UNSIGNED_BYTE_BIT; + case GL_SHORT: + return SHORT_BIT; + case GL_UNSIGNED_SHORT: + return UNSIGNED_SHORT_BIT; + case GL_INT: + return INT_BIT; + case GL_UNSIGNED_INT: + return UNSIGNED_INT_BIT; + case GL_HALF_FLOAT: + if (ctx->Extensions.ARB_half_float_vertex) + return HALF_BIT; + else + return 0x0; + case GL_FLOAT: + return FLOAT_BIT; + case GL_DOUBLE: + return DOUBLE_BIT; + case GL_FIXED: + return FIXED_BIT; + default: + return 0; + } +} + + +/** + * Do error checking and update state for glVertex/Color/TexCoord/...Pointer + * functions. + * + * \param func name of calling function used for error reporting + * \param array the array to update + * \param dirtyBit which bit to set in ctx->Array.NewState for this array + * \param legalTypes bitmask of *_BIT above indicating legal datatypes + * \param sizeMin min allowable size value + * \param sizeMax max allowable size value (may also be BGRA_OR_4) + * \param size components per element (1, 2, 3 or 4) + * \param type datatype of each component (GL_FLOAT, GL_INT, etc) + * \param stride stride between elements, in elements + * \param normalized are integer types converted to floats in [-1, 1]? + * \param integer integer-valued values (will not be normalized to [-1,1]) + * \param ptr the address (or offset inside VBO) of the array data + */ +static void +update_array(struct gl_context *ctx, + const char *func, + struct gl_client_array *array, + GLbitfield dirtyBit, GLbitfield legalTypesMask, + GLint sizeMin, GLint sizeMax, + GLint size, GLenum type, GLsizei stride, + GLboolean normalized, GLboolean integer, + const GLvoid *ptr) +{ + GLbitfield typeBit; + GLsizei elementSize; + GLenum format = GL_RGBA; + + if (ctx->API != API_OPENGLES && ctx->API != API_OPENGLES2) { + /* fixed point arrays / data is only allowed with OpenGL ES 1.x/2.0 */ + legalTypesMask &= ~FIXED_BIT; + } + + typeBit = type_to_bit(ctx, type); + if (typeBit == 0x0 || (typeBit & legalTypesMask) == 0x0) { + _mesa_error(ctx, GL_INVALID_ENUM, "%s(type = %s)", + func, _mesa_lookup_enum_by_nr(type)); + return; + } + + /* Do size parameter checking. + * If sizeMax = BGRA_OR_4 it means that size = GL_BGRA is legal and + * must be handled specially. + */ + if (ctx->Extensions.EXT_vertex_array_bgra && + sizeMax == BGRA_OR_4 && + size == GL_BGRA) { + if (type != GL_UNSIGNED_BYTE) { + _mesa_error(ctx, GL_INVALID_VALUE, "%s(GL_BGRA/GLubyte)", func); + return; + } + format = GL_BGRA; + size = 4; + } + else if (size < sizeMin || size > sizeMax || size > 4) { + _mesa_error(ctx, GL_INVALID_VALUE, "%s(size=%d)", func, size); + return; + } + + ASSERT(size <= 4); + + if (stride < 0) { + _mesa_error( ctx, GL_INVALID_VALUE, "%s(stride=%d)", func, stride ); + return; + } + + if (ctx->Array.ArrayObj->VBOonly && + ctx->Array.ArrayBufferObj->Name == 0) { + /* GL_ARB_vertex_array_object requires that all arrays reside in VBOs. + * Generate GL_INVALID_OPERATION if that's not true. + */ + _mesa_error(ctx, GL_INVALID_OPERATION, "%s(non-VBO array)", func); + return; + } + + elementSize = _mesa_sizeof_type(type) * size; + + array->Size = size; + array->Type = type; + array->Format = format; + array->Stride = stride; + array->StrideB = stride ? stride : elementSize; + array->Normalized = normalized; + array->Ptr = (const GLubyte *) ptr; + array->_ElementSize = elementSize; + + _mesa_reference_buffer_object(ctx, &array->BufferObj, + ctx->Array.ArrayBufferObj); + + ctx->NewState |= _NEW_ARRAY; + ctx->Array.NewState |= dirtyBit; +} + + +void GLAPIENTRY +_mesa_VertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) +{ + GLbitfield legalTypes = (SHORT_BIT | INT_BIT | FLOAT_BIT | + DOUBLE_BIT | HALF_BIT | FIXED_BIT); + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + if (ctx->API == API_OPENGLES) + legalTypes |= BYTE_BIT; + + update_array(ctx, "glVertexPointer", + &ctx->Array.ArrayObj->Vertex, _NEW_ARRAY_VERTEX, + legalTypes, 2, 4, + size, type, stride, GL_FALSE, GL_FALSE, ptr); +} + + +void GLAPIENTRY +_mesa_NormalPointer(GLenum type, GLsizei stride, const GLvoid *ptr ) +{ + const GLbitfield legalTypes = (BYTE_BIT | SHORT_BIT | INT_BIT | + HALF_BIT | FLOAT_BIT | DOUBLE_BIT | + FIXED_BIT); + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + update_array(ctx, "glNormalPointer", + &ctx->Array.ArrayObj->Normal, _NEW_ARRAY_NORMAL, + legalTypes, 3, 3, + 3, type, stride, GL_TRUE, GL_FALSE, ptr); +} + + +void GLAPIENTRY +_mesa_ColorPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) +{ + const GLbitfield legalTypes = (BYTE_BIT | UNSIGNED_BYTE_BIT | + SHORT_BIT | UNSIGNED_SHORT_BIT | + INT_BIT | UNSIGNED_INT_BIT | + HALF_BIT | FLOAT_BIT | DOUBLE_BIT | + FIXED_BIT); + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + update_array(ctx, "glColorPointer", + &ctx->Array.ArrayObj->Color, _NEW_ARRAY_COLOR0, + legalTypes, 3, BGRA_OR_4, + size, type, stride, GL_TRUE, GL_FALSE, ptr); +} + + +void GLAPIENTRY +_mesa_FogCoordPointerEXT(GLenum type, GLsizei stride, const GLvoid *ptr) +{ + const GLbitfield legalTypes = (HALF_BIT | FLOAT_BIT | DOUBLE_BIT); + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + update_array(ctx, "glFogCoordPointer", + &ctx->Array.ArrayObj->FogCoord, _NEW_ARRAY_FOGCOORD, + legalTypes, 1, 1, + 1, type, stride, GL_FALSE, GL_FALSE, ptr); +} + + +void GLAPIENTRY +_mesa_IndexPointer(GLenum type, GLsizei stride, const GLvoid *ptr) +{ + const GLbitfield legalTypes = (UNSIGNED_BYTE_BIT | SHORT_BIT | INT_BIT | + FLOAT_BIT | DOUBLE_BIT); + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + update_array(ctx, "glIndexPointer", + &ctx->Array.ArrayObj->Index, _NEW_ARRAY_INDEX, + legalTypes, 1, 1, + 1, type, stride, GL_FALSE, GL_FALSE, ptr); +} + + +void GLAPIENTRY +_mesa_SecondaryColorPointerEXT(GLint size, GLenum type, + GLsizei stride, const GLvoid *ptr) +{ + const GLbitfield legalTypes = (BYTE_BIT | UNSIGNED_BYTE_BIT | + SHORT_BIT | UNSIGNED_SHORT_BIT | + INT_BIT | UNSIGNED_INT_BIT | + HALF_BIT | FLOAT_BIT | DOUBLE_BIT); + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + update_array(ctx, "glSecondaryColorPointer", + &ctx->Array.ArrayObj->SecondaryColor, _NEW_ARRAY_COLOR1, + legalTypes, 3, BGRA_OR_4, + size, type, stride, GL_TRUE, GL_FALSE, ptr); +} + + +void GLAPIENTRY +_mesa_TexCoordPointer(GLint size, GLenum type, GLsizei stride, + const GLvoid *ptr) +{ + GLbitfield legalTypes = (SHORT_BIT | INT_BIT | + HALF_BIT | FLOAT_BIT | DOUBLE_BIT | + FIXED_BIT); + GET_CURRENT_CONTEXT(ctx); + const GLuint unit = ctx->Array.ActiveTexture; + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + if (ctx->API == API_OPENGLES) + legalTypes |= BYTE_BIT; + + ASSERT(unit < Elements(ctx->Array.ArrayObj->TexCoord)); + + update_array(ctx, "glTexCoordPointer", + &ctx->Array.ArrayObj->TexCoord[unit], + _NEW_ARRAY_TEXCOORD(unit), + legalTypes, 1, 4, + size, type, stride, GL_FALSE, GL_FALSE, + ptr); +} + + +void GLAPIENTRY +_mesa_EdgeFlagPointer(GLsizei stride, const GLvoid *ptr) +{ + const GLbitfield legalTypes = UNSIGNED_BYTE_BIT; + /* see table 2.4 edits in GL_EXT_gpu_shader4 spec: */ + const GLboolean integer = GL_TRUE; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + update_array(ctx, "glEdgeFlagPointer", + &ctx->Array.ArrayObj->EdgeFlag, _NEW_ARRAY_EDGEFLAG, + legalTypes, 1, 1, + 1, GL_UNSIGNED_BYTE, stride, GL_FALSE, integer, ptr); +} + + +void GLAPIENTRY +_mesa_PointSizePointer(GLenum type, GLsizei stride, const GLvoid *ptr) +{ + const GLbitfield legalTypes = (FLOAT_BIT | FIXED_BIT); + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + if (ctx->API != API_OPENGLES) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glPointSizePointer(ES 1.x only)"); + return; + } + + update_array(ctx, "glPointSizePointer", + &ctx->Array.ArrayObj->PointSize, _NEW_ARRAY_POINT_SIZE, + legalTypes, 1, 1, + 1, type, stride, GL_FALSE, GL_FALSE, ptr); +} + + +#if FEATURE_NV_vertex_program +/** + * Set a vertex attribute array. + * Note that these arrays DO alias the conventional GL vertex arrays + * (position, normal, color, fog, texcoord, etc). + * The generic attribute slots at #16 and above are not touched. + */ +void GLAPIENTRY +_mesa_VertexAttribPointerNV(GLuint index, GLint size, GLenum type, + GLsizei stride, const GLvoid *ptr) +{ + const GLbitfield legalTypes = (UNSIGNED_BYTE_BIT | SHORT_BIT | + FLOAT_BIT | DOUBLE_BIT); + GLboolean normalized = GL_FALSE; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (index >= MAX_NV_VERTEX_PROGRAM_INPUTS) { + _mesa_error(ctx, GL_INVALID_VALUE, "glVertexAttribPointerNV(index)"); + return; + } + + if (type == GL_UNSIGNED_BYTE && size != 4) { + _mesa_error(ctx, GL_INVALID_VALUE, "glVertexAttribPointerNV(size!=4)"); + return; + } + + update_array(ctx, "glVertexAttribPointerNV", + &ctx->Array.ArrayObj->VertexAttrib[index], + _NEW_ARRAY_ATTRIB(index), + legalTypes, 1, BGRA_OR_4, + size, type, stride, normalized, GL_FALSE, ptr); +} +#endif + + +#if FEATURE_ARB_vertex_program +/** + * Set a generic vertex attribute array. + * Note that these arrays DO NOT alias the conventional GL vertex arrays + * (position, normal, color, fog, texcoord, etc). + */ +void GLAPIENTRY +_mesa_VertexAttribPointerARB(GLuint index, GLint size, GLenum type, + GLboolean normalized, + GLsizei stride, const GLvoid *ptr) +{ + const GLbitfield legalTypes = (BYTE_BIT | UNSIGNED_BYTE_BIT | + SHORT_BIT | UNSIGNED_SHORT_BIT | + INT_BIT | UNSIGNED_INT_BIT | + HALF_BIT | FLOAT_BIT | DOUBLE_BIT | + FIXED_BIT); + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (index >= ctx->Const.VertexProgram.MaxAttribs) { + _mesa_error(ctx, GL_INVALID_VALUE, "glVertexAttribPointerARB(index)"); + return; + } + + update_array(ctx, "glVertexAttribPointer", + &ctx->Array.ArrayObj->VertexAttrib[index], + _NEW_ARRAY_ATTRIB(index), + legalTypes, 1, BGRA_OR_4, + size, type, stride, normalized, GL_FALSE, ptr); +} +#endif + + +/** + * GL_EXT_gpu_shader4 / GL 3.0. + * Set an integer-valued vertex attribute array. + * Note that these arrays DO NOT alias the conventional GL vertex arrays + * (position, normal, color, fog, texcoord, etc). + */ +void GLAPIENTRY +_mesa_VertexAttribIPointer(GLuint index, GLint size, GLenum type, + GLsizei stride, const GLvoid *ptr) +{ + const GLbitfield legalTypes = (BYTE_BIT | UNSIGNED_BYTE_BIT | + SHORT_BIT | UNSIGNED_SHORT_BIT | + INT_BIT | UNSIGNED_INT_BIT); + const GLboolean normalized = GL_FALSE; + const GLboolean integer = GL_TRUE; + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (index >= ctx->Const.VertexProgram.MaxAttribs) { + _mesa_error(ctx, GL_INVALID_VALUE, "glVertexAttribIPointer(index)"); + return; + } + + update_array(ctx, "glVertexAttribIPointer", + &ctx->Array.ArrayObj->VertexAttrib[index], + _NEW_ARRAY_ATTRIB(index), + legalTypes, 1, 4, + size, type, stride, normalized, integer, ptr); +} + + + +void GLAPIENTRY +_mesa_EnableVertexAttribArrayARB(GLuint index) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (index >= ctx->Const.VertexProgram.MaxAttribs) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glEnableVertexAttribArrayARB(index)"); + return; + } + + ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib)); + + FLUSH_VERTICES(ctx, _NEW_ARRAY); + ctx->Array.ArrayObj->VertexAttrib[index].Enabled = GL_TRUE; + ctx->Array.ArrayObj->_Enabled |= _NEW_ARRAY_ATTRIB(index); + ctx->Array.NewState |= _NEW_ARRAY_ATTRIB(index); +} + + +void GLAPIENTRY +_mesa_DisableVertexAttribArrayARB(GLuint index) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (index >= ctx->Const.VertexProgram.MaxAttribs) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glEnableVertexAttribArrayARB(index)"); + return; + } + + ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib)); + + FLUSH_VERTICES(ctx, _NEW_ARRAY); + ctx->Array.ArrayObj->VertexAttrib[index].Enabled = GL_FALSE; + ctx->Array.ArrayObj->_Enabled &= ~_NEW_ARRAY_ATTRIB(index); + ctx->Array.NewState |= _NEW_ARRAY_ATTRIB(index); +} + + +/** + * Return info for a vertex attribute array (no alias with legacy + * vertex attributes (pos, normal, color, etc)). This function does + * not handle the 4-element GL_CURRENT_VERTEX_ATTRIB_ARB query. + */ +static GLuint +get_vertex_array_attrib(struct gl_context *ctx, GLuint index, GLenum pname, + const char *caller) +{ + const struct gl_client_array *array; + + if (index >= ctx->Const.VertexProgram.MaxAttribs) { + _mesa_error(ctx, GL_INVALID_VALUE, "%s(index=%u)", caller, index); + return 0; + } + + ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib)); + + array = &ctx->Array.ArrayObj->VertexAttrib[index]; + + switch (pname) { + case GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB: + return array->Enabled; + case GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB: + return array->Size; + case GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB: + return array->Stride; + case GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB: + return array->Type; + case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB: + return array->Normalized; + case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB: + return array->BufferObj->Name; + case GL_VERTEX_ATTRIB_ARRAY_INTEGER: + if (ctx->Extensions.EXT_gpu_shader4) { + return array->Integer; + } + goto error; + case GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB: + if (ctx->Extensions.ARB_instanced_arrays) { + return array->InstanceDivisor; + } + goto error; + default: + ; /* fall-through */ + } + +error: + _mesa_error(ctx, GL_INVALID_ENUM, "%s(pname=0x%x)", caller, pname); + return 0; +} + + +static const GLfloat * +get_current_attrib(struct gl_context *ctx, GLuint index, const char *function) +{ + if (index == 0) { + if (ctx->API != API_OPENGLES2) { + _mesa_error(ctx, GL_INVALID_OPERATION, "%s(index==0)", function); + return NULL; + } + } + else if (index >= ctx->Const.VertexProgram.MaxAttribs) { + _mesa_error(ctx, GL_INVALID_VALUE, + "%s(index>=GL_MAX_VERTEX_ATTRIBS)", function); + return NULL; + } + + FLUSH_CURRENT(ctx, 0); + return ctx->Current.Attrib[VERT_ATTRIB_GENERIC0 + index]; +} + +void GLAPIENTRY +_mesa_GetVertexAttribfvARB(GLuint index, GLenum pname, GLfloat *params) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) { + const GLfloat *v = get_current_attrib(ctx, index, "glGetVertexAttribfv"); + if (v != NULL) { + COPY_4V(params, v); + } + } + else { + params[0] = (GLfloat) get_vertex_array_attrib(ctx, index, pname, + "glGetVertexAttribfv"); + } +} + + +void GLAPIENTRY +_mesa_GetVertexAttribdvARB(GLuint index, GLenum pname, GLdouble *params) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) { + const GLfloat *v = get_current_attrib(ctx, index, "glGetVertexAttribdv"); + if (v != NULL) { + params[0] = (GLdouble) v[0]; + params[1] = (GLdouble) v[1]; + params[2] = (GLdouble) v[2]; + params[3] = (GLdouble) v[3]; + } + } + else { + params[0] = (GLdouble) get_vertex_array_attrib(ctx, index, pname, + "glGetVertexAttribdv"); + } +} + + +void GLAPIENTRY +_mesa_GetVertexAttribivARB(GLuint index, GLenum pname, GLint *params) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) { + const GLfloat *v = get_current_attrib(ctx, index, "glGetVertexAttribiv"); + if (v != NULL) { + /* XXX should floats in[0,1] be scaled to full int range? */ + params[0] = (GLint) v[0]; + params[1] = (GLint) v[1]; + params[2] = (GLint) v[2]; + params[3] = (GLint) v[3]; + } + } + else { + params[0] = (GLint) get_vertex_array_attrib(ctx, index, pname, + "glGetVertexAttribiv"); + } +} + + +/** GL 3.0 */ +void GLAPIENTRY +_mesa_GetVertexAttribIiv(GLuint index, GLenum pname, GLint *params) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) { + const GLfloat *v = + get_current_attrib(ctx, index, "glGetVertexAttribIiv"); + if (v != NULL) { + /* XXX we don't have true integer-valued vertex attribs yet */ + params[0] = (GLint) v[0]; + params[1] = (GLint) v[1]; + params[2] = (GLint) v[2]; + params[3] = (GLint) v[3]; + } + } + else { + params[0] = (GLint) get_vertex_array_attrib(ctx, index, pname, + "glGetVertexAttribIiv"); + } +} + + +/** GL 3.0 */ +void GLAPIENTRY +_mesa_GetVertexAttribIuiv(GLuint index, GLenum pname, GLuint *params) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (pname == GL_CURRENT_VERTEX_ATTRIB_ARB) { + const GLfloat *v = + get_current_attrib(ctx, index, "glGetVertexAttribIuiv"); + if (v != NULL) { + /* XXX we don't have true integer-valued vertex attribs yet */ + params[0] = (GLuint) v[0]; + params[1] = (GLuint) v[1]; + params[2] = (GLuint) v[2]; + params[3] = (GLuint) v[3]; + } + } + else { + params[0] = get_vertex_array_attrib(ctx, index, pname, + "glGetVertexAttribIuiv"); + } +} + + +void GLAPIENTRY +_mesa_GetVertexAttribPointervARB(GLuint index, GLenum pname, GLvoid **pointer) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END(ctx); + + if (index >= ctx->Const.VertexProgram.MaxAttribs) { + _mesa_error(ctx, GL_INVALID_VALUE, "glGetVertexAttribPointerARB(index)"); + return; + } + + if (pname != GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB) { + _mesa_error(ctx, GL_INVALID_ENUM, "glGetVertexAttribPointerARB(pname)"); + return; + } + + ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib)); + + *pointer = (GLvoid *) ctx->Array.ArrayObj->VertexAttrib[index].Ptr; +} + + +void GLAPIENTRY +_mesa_VertexPointerEXT(GLint size, GLenum type, GLsizei stride, + GLsizei count, const GLvoid *ptr) +{ + (void) count; + _mesa_VertexPointer(size, type, stride, ptr); +} + + +void GLAPIENTRY +_mesa_NormalPointerEXT(GLenum type, GLsizei stride, GLsizei count, + const GLvoid *ptr) +{ + (void) count; + _mesa_NormalPointer(type, stride, ptr); +} + + +void GLAPIENTRY +_mesa_ColorPointerEXT(GLint size, GLenum type, GLsizei stride, GLsizei count, + const GLvoid *ptr) +{ + (void) count; + _mesa_ColorPointer(size, type, stride, ptr); +} + + +void GLAPIENTRY +_mesa_IndexPointerEXT(GLenum type, GLsizei stride, GLsizei count, + const GLvoid *ptr) +{ + (void) count; + _mesa_IndexPointer(type, stride, ptr); +} + + +void GLAPIENTRY +_mesa_TexCoordPointerEXT(GLint size, GLenum type, GLsizei stride, + GLsizei count, const GLvoid *ptr) +{ + (void) count; + _mesa_TexCoordPointer(size, type, stride, ptr); +} + + +void GLAPIENTRY +_mesa_EdgeFlagPointerEXT(GLsizei stride, GLsizei count, const GLboolean *ptr) +{ + (void) count; + _mesa_EdgeFlagPointer(stride, ptr); +} + + +void GLAPIENTRY +_mesa_InterleavedArrays(GLenum format, GLsizei stride, const GLvoid *pointer) +{ + GET_CURRENT_CONTEXT(ctx); + GLboolean tflag, cflag, nflag; /* enable/disable flags */ + GLint tcomps, ccomps, vcomps; /* components per texcoord, color, vertex */ + GLenum ctype = 0; /* color type */ + GLint coffset = 0, noffset = 0, voffset;/* color, normal, vertex offsets */ + const GLint toffset = 0; /* always zero */ + GLint defstride; /* default stride */ + GLint c, f; + + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + f = sizeof(GLfloat); + c = f * ((4 * sizeof(GLubyte) + (f - 1)) / f); + + if (stride < 0) { + _mesa_error( ctx, GL_INVALID_VALUE, "glInterleavedArrays(stride)" ); + return; + } + + switch (format) { + case GL_V2F: + tflag = GL_FALSE; cflag = GL_FALSE; nflag = GL_FALSE; + tcomps = 0; ccomps = 0; vcomps = 2; + voffset = 0; + defstride = 2*f; + break; + case GL_V3F: + tflag = GL_FALSE; cflag = GL_FALSE; nflag = GL_FALSE; + tcomps = 0; ccomps = 0; vcomps = 3; + voffset = 0; + defstride = 3*f; + break; + case GL_C4UB_V2F: + tflag = GL_FALSE; cflag = GL_TRUE; nflag = GL_FALSE; + tcomps = 0; ccomps = 4; vcomps = 2; + ctype = GL_UNSIGNED_BYTE; + coffset = 0; + voffset = c; + defstride = c + 2*f; + break; + case GL_C4UB_V3F: + tflag = GL_FALSE; cflag = GL_TRUE; nflag = GL_FALSE; + tcomps = 0; ccomps = 4; vcomps = 3; + ctype = GL_UNSIGNED_BYTE; + coffset = 0; + voffset = c; + defstride = c + 3*f; + break; + case GL_C3F_V3F: + tflag = GL_FALSE; cflag = GL_TRUE; nflag = GL_FALSE; + tcomps = 0; ccomps = 3; vcomps = 3; + ctype = GL_FLOAT; + coffset = 0; + voffset = 3*f; + defstride = 6*f; + break; + case GL_N3F_V3F: + tflag = GL_FALSE; cflag = GL_FALSE; nflag = GL_TRUE; + tcomps = 0; ccomps = 0; vcomps = 3; + noffset = 0; + voffset = 3*f; + defstride = 6*f; + break; + case GL_C4F_N3F_V3F: + tflag = GL_FALSE; cflag = GL_TRUE; nflag = GL_TRUE; + tcomps = 0; ccomps = 4; vcomps = 3; + ctype = GL_FLOAT; + coffset = 0; + noffset = 4*f; + voffset = 7*f; + defstride = 10*f; + break; + case GL_T2F_V3F: + tflag = GL_TRUE; cflag = GL_FALSE; nflag = GL_FALSE; + tcomps = 2; ccomps = 0; vcomps = 3; + voffset = 2*f; + defstride = 5*f; + break; + case GL_T4F_V4F: + tflag = GL_TRUE; cflag = GL_FALSE; nflag = GL_FALSE; + tcomps = 4; ccomps = 0; vcomps = 4; + voffset = 4*f; + defstride = 8*f; + break; + case GL_T2F_C4UB_V3F: + tflag = GL_TRUE; cflag = GL_TRUE; nflag = GL_FALSE; + tcomps = 2; ccomps = 4; vcomps = 3; + ctype = GL_UNSIGNED_BYTE; + coffset = 2*f; + voffset = c+2*f; + defstride = c+5*f; + break; + case GL_T2F_C3F_V3F: + tflag = GL_TRUE; cflag = GL_TRUE; nflag = GL_FALSE; + tcomps = 2; ccomps = 3; vcomps = 3; + ctype = GL_FLOAT; + coffset = 2*f; + voffset = 5*f; + defstride = 8*f; + break; + case GL_T2F_N3F_V3F: + tflag = GL_TRUE; cflag = GL_FALSE; nflag = GL_TRUE; + tcomps = 2; ccomps = 0; vcomps = 3; + noffset = 2*f; + voffset = 5*f; + defstride = 8*f; + break; + case GL_T2F_C4F_N3F_V3F: + tflag = GL_TRUE; cflag = GL_TRUE; nflag = GL_TRUE; + tcomps = 2; ccomps = 4; vcomps = 3; + ctype = GL_FLOAT; + coffset = 2*f; + noffset = 6*f; + voffset = 9*f; + defstride = 12*f; + break; + case GL_T4F_C4F_N3F_V4F: + tflag = GL_TRUE; cflag = GL_TRUE; nflag = GL_TRUE; + tcomps = 4; ccomps = 4; vcomps = 4; + ctype = GL_FLOAT; + coffset = 4*f; + noffset = 8*f; + voffset = 11*f; + defstride = 15*f; + break; + default: + _mesa_error( ctx, GL_INVALID_ENUM, "glInterleavedArrays(format)" ); + return; + } + + if (stride==0) { + stride = defstride; + } + + _mesa_DisableClientState( GL_EDGE_FLAG_ARRAY ); + _mesa_DisableClientState( GL_INDEX_ARRAY ); + /* XXX also disable secondary color and generic arrays? */ + + /* Texcoords */ + if (tflag) { + _mesa_EnableClientState( GL_TEXTURE_COORD_ARRAY ); + _mesa_TexCoordPointer( tcomps, GL_FLOAT, stride, + (GLubyte *) pointer + toffset ); + } + else { + _mesa_DisableClientState( GL_TEXTURE_COORD_ARRAY ); + } + + /* Color */ + if (cflag) { + _mesa_EnableClientState( GL_COLOR_ARRAY ); + _mesa_ColorPointer( ccomps, ctype, stride, + (GLubyte *) pointer + coffset ); + } + else { + _mesa_DisableClientState( GL_COLOR_ARRAY ); + } + + + /* Normals */ + if (nflag) { + _mesa_EnableClientState( GL_NORMAL_ARRAY ); + _mesa_NormalPointer( GL_FLOAT, stride, (GLubyte *) pointer + noffset ); + } + else { + _mesa_DisableClientState( GL_NORMAL_ARRAY ); + } + + /* Vertices */ + _mesa_EnableClientState( GL_VERTEX_ARRAY ); + _mesa_VertexPointer( vcomps, GL_FLOAT, stride, + (GLubyte *) pointer + voffset ); +} + + +void GLAPIENTRY +_mesa_LockArraysEXT(GLint first, GLsizei count) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glLockArrays %d %d\n", first, count); + + if (first < 0) { + _mesa_error( ctx, GL_INVALID_VALUE, "glLockArraysEXT(first)" ); + return; + } + if (count <= 0) { + _mesa_error( ctx, GL_INVALID_VALUE, "glLockArraysEXT(count)" ); + return; + } + if (ctx->Array.LockCount != 0) { + _mesa_error( ctx, GL_INVALID_OPERATION, "glLockArraysEXT(reentry)" ); + return; + } + + ctx->Array.LockFirst = first; + ctx->Array.LockCount = count; + + ctx->NewState |= _NEW_ARRAY; + ctx->Array.NewState |= _NEW_ARRAY_ALL; +} + + +void GLAPIENTRY +_mesa_UnlockArraysEXT( void ) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glUnlockArrays\n"); + + if (ctx->Array.LockCount == 0) { + _mesa_error( ctx, GL_INVALID_OPERATION, "glUnlockArraysEXT(reexit)" ); + return; + } + + ctx->Array.LockFirst = 0; + ctx->Array.LockCount = 0; + ctx->NewState |= _NEW_ARRAY; + ctx->Array.NewState |= _NEW_ARRAY_ALL; +} + + +/* GL_EXT_multi_draw_arrays */ +void GLAPIENTRY +_mesa_MultiDrawArraysEXT( GLenum mode, const GLint *first, + const GLsizei *count, GLsizei primcount ) +{ + GET_CURRENT_CONTEXT(ctx); + GLint i; + + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + for (i = 0; i < primcount; i++) { + if (count[i] > 0) { + CALL_DrawArrays(ctx->Exec, (mode, first[i], count[i])); + } + } +} + + +/* GL_IBM_multimode_draw_arrays */ +void GLAPIENTRY +_mesa_MultiModeDrawArraysIBM( const GLenum * mode, const GLint * first, + const GLsizei * count, + GLsizei primcount, GLint modestride ) +{ + GET_CURRENT_CONTEXT(ctx); + GLint i; + + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + for ( i = 0 ; i < primcount ; i++ ) { + if ( count[i] > 0 ) { + GLenum m = *((GLenum *) ((GLubyte *) mode + i * modestride)); + CALL_DrawArrays(ctx->Exec, ( m, first[i], count[i] )); + } + } +} + + +/* GL_IBM_multimode_draw_arrays */ +void GLAPIENTRY +_mesa_MultiModeDrawElementsIBM( const GLenum * mode, const GLsizei * count, + GLenum type, const GLvoid * const * indices, + GLsizei primcount, GLint modestride ) +{ + GET_CURRENT_CONTEXT(ctx); + GLint i; + + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + /* XXX not sure about ARB_vertex_buffer_object handling here */ + + for ( i = 0 ; i < primcount ; i++ ) { + if ( count[i] > 0 ) { + GLenum m = *((GLenum *) ((GLubyte *) mode + i * modestride)); + CALL_DrawElements(ctx->Exec, ( m, count[i], type, indices[i] )); + } + } +} + + +/** + * GL_NV_primitive_restart and GL 3.1 + */ +void GLAPIENTRY +_mesa_PrimitiveRestartIndex(GLuint index) +{ + GET_CURRENT_CONTEXT(ctx); + + if (!ctx->Extensions.NV_primitive_restart && + ctx->VersionMajor * 10 + ctx->VersionMinor < 31) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glPrimitiveRestartIndexNV()"); + return; + } + + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + FLUSH_VERTICES(ctx, _NEW_TRANSFORM); + + ctx->Array.RestartIndex = index; +} + + +/** + * See GL_ARB_instanced_arrays. + * Note that the instance divisor only applies to generic arrays, not + * the legacy vertex arrays. + */ +void GLAPIENTRY +_mesa_VertexAttribDivisor(GLuint index, GLuint divisor) +{ + GET_CURRENT_CONTEXT(ctx); + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + if (!ctx->Extensions.ARB_instanced_arrays) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glVertexAttribDivisor()"); + return; + } + + if (index >= ctx->Const.VertexProgram.MaxAttribs) { + _mesa_error(ctx, GL_INVALID_ENUM, "glVertexAttribDivisor(index = %u)", + index); + return; + } + + ctx->Array.ArrayObj->VertexAttrib[index].InstanceDivisor = divisor; +} + + + +/** + * Copy one client vertex array to another. + */ +void +_mesa_copy_client_array(struct gl_context *ctx, + struct gl_client_array *dst, + struct gl_client_array *src) +{ + dst->Size = src->Size; + dst->Type = src->Type; + dst->Format = src->Format; + dst->Stride = src->Stride; + dst->StrideB = src->StrideB; + dst->Ptr = src->Ptr; + dst->Enabled = src->Enabled; + dst->Normalized = src->Normalized; + dst->Integer = src->Integer; + dst->InstanceDivisor = src->InstanceDivisor; + dst->_ElementSize = src->_ElementSize; + _mesa_reference_buffer_object(ctx, &dst->BufferObj, src->BufferObj); + dst->_MaxElement = src->_MaxElement; +} + + + +/** + * Print vertex array's fields. + */ +static void +print_array(const char *name, GLint index, const struct gl_client_array *array) +{ + if (index >= 0) + printf(" %s[%d]: ", name, index); + else + printf(" %s: ", name); + printf("Ptr=%p, Type=0x%x, Size=%d, ElemSize=%u, Stride=%d, Buffer=%u(Size %lu), MaxElem=%u\n", + array->Ptr, array->Type, array->Size, + array->_ElementSize, array->StrideB, + array->BufferObj->Name, (unsigned long) array->BufferObj->Size, + array->_MaxElement); +} + + +/** + * Print current vertex object/array info. For debug. + */ +void +_mesa_print_arrays(struct gl_context *ctx) +{ + struct gl_array_object *arrayObj = ctx->Array.ArrayObj; + GLuint i; + + _mesa_update_array_object_max_element(ctx, arrayObj); + + printf("Array Object %u\n", arrayObj->Name); + if (arrayObj->Vertex.Enabled) + print_array("Vertex", -1, &arrayObj->Vertex); + if (arrayObj->Normal.Enabled) + print_array("Normal", -1, &arrayObj->Normal); + if (arrayObj->Color.Enabled) + print_array("Color", -1, &arrayObj->Color); + for (i = 0; i < Elements(arrayObj->TexCoord); i++) + if (arrayObj->TexCoord[i].Enabled) + print_array("TexCoord", i, &arrayObj->TexCoord[i]); + for (i = 0; i < Elements(arrayObj->VertexAttrib); i++) + if (arrayObj->VertexAttrib[i].Enabled) + print_array("Attrib", i, &arrayObj->VertexAttrib[i]); + printf(" _MaxElement = %u\n", arrayObj->_MaxElement); +} + + +/** + * Initialize vertex array state for given context. + */ +void +_mesa_init_varray(struct gl_context *ctx) +{ + ctx->Array.DefaultArrayObj = _mesa_new_array_object(ctx, 0); + _mesa_reference_array_object(ctx, &ctx->Array.ArrayObj, + ctx->Array.DefaultArrayObj); + ctx->Array.ActiveTexture = 0; /* GL_ARB_multitexture */ + + ctx->Array.Objects = _mesa_NewHashTable(); +} + + +/** + * Callback for deleting an array object. Called by _mesa_HashDeleteAll(). + */ +static void +delete_arrayobj_cb(GLuint id, void *data, void *userData) +{ + struct gl_array_object *arrayObj = (struct gl_array_object *) data; + struct gl_context *ctx = (struct gl_context *) userData; + _mesa_delete_array_object(ctx, arrayObj); +} + + +/** + * Free vertex array state for given context. + */ +void +_mesa_free_varray_data(struct gl_context *ctx) +{ + _mesa_HashDeleteAll(ctx->Array.Objects, delete_arrayobj_cb, ctx); + _mesa_DeleteHashTable(ctx->Array.Objects); +} -- cgit v1.2.3