summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMicael Dias <kam1kaz3@gmail.com>2011-08-04 14:59:06 +0100
committerAlex Deucher <alexander.deucher@amd.com>2011-08-25 12:14:00 -0400
commiteac31ea1367d1ab1a100513fbbee24a831a8d4e0 (patch)
treec28fe3ef04e6e2fbd5ff0ee85b1217f94975d6cd
parentb9eb4d8a59699e233255113acafae220c3d8fe3c (diff)
Implement HW accelerated GL_SELECThw_gl_select
-rw-r--r--src/mesa/SConscript1
-rw-r--r--src/mesa/main/fbobject.c16
-rw-r--r--src/mesa/main/fbobject.h6
-rw-r--r--src/mesa/sources.mak1
-rw-r--r--src/mesa/state_tracker/st_cb_feedback.c21
-rw-r--r--src/mesa/state_tracker/st_context.h10
-rw-r--r--src/mesa/state_tracker/st_draw.h17
-rw-r--r--src/mesa/state_tracker/st_draw_select_emul.c490
8 files changed, 549 insertions, 13 deletions
diff --git a/src/mesa/SConscript b/src/mesa/SConscript
index b0c3334fa48..d37457bdbd8 100644
--- a/src/mesa/SConscript
+++ b/src/mesa/SConscript
@@ -262,6 +262,7 @@ statetracker_sources = [
'state_tracker/st_debug.c',
'state_tracker/st_draw.c',
'state_tracker/st_draw_feedback.c',
+ 'state_tracker/st_draw_select_emul.c',
'state_tracker/st_extensions.c',
'state_tracker/st_format.c',
'state_tracker/st_glsl_to_tgsi.cpp',
diff --git a/src/mesa/main/fbobject.c b/src/mesa/main/fbobject.c
index 0b48fc7eab0..68dfa08f04f 100644
--- a/src/mesa/main/fbobject.c
+++ b/src/mesa/main/fbobject.c
@@ -1560,8 +1560,8 @@ _mesa_IsFramebufferEXT(GLuint framebuffer)
* (render to texture). Call ctx->Driver.RenderTexture() for such
* attachments.
*/
-static void
-check_begin_texture_render(struct gl_context *ctx, struct gl_framebuffer *fb)
+void
+_mesa_check_begin_texture_render(struct gl_context *ctx, struct gl_framebuffer *fb)
{
GLuint i;
ASSERT(ctx->Driver.RenderTexture);
@@ -1583,8 +1583,8 @@ check_begin_texture_render(struct gl_context *ctx, struct gl_framebuffer *fb)
* 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)
+void
+_mesa_check_end_texture_render(struct gl_context *ctx, struct gl_framebuffer *fb)
{
if (is_winsys_fbo(fb))
return; /* can't render to texture with winsys framebuffers */
@@ -1713,7 +1713,7 @@ _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
FLUSH_VERTICES(ctx, _NEW_BUFFERS);
/* check if old readbuffer was render-to-texture */
- check_end_texture_render(ctx, oldReadFb);
+ _mesa_check_end_texture_render(ctx, oldReadFb);
_mesa_reference_framebuffer(&ctx->ReadBuffer, newReadFb);
}
@@ -1723,13 +1723,13 @@ _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
/* check if old read/draw buffers were render-to-texture */
if (!bindReadBuf)
- check_end_texture_render(ctx, oldReadFb);
+ _mesa_check_end_texture_render(ctx, oldReadFb);
if (oldDrawFb != oldReadFb)
- check_end_texture_render(ctx, oldDrawFb);
+ _mesa_check_end_texture_render(ctx, oldDrawFb);
/* check if newly bound framebuffer has any texture attachments */
- check_begin_texture_render(ctx, newDrawFb);
+ _mesa_check_begin_texture_render(ctx, newDrawFb);
_mesa_reference_framebuffer(&ctx->DrawBuffer, newDrawFb);
}
diff --git a/src/mesa/main/fbobject.h b/src/mesa/main/fbobject.h
index 7eb20050209..ca6251c5d7c 100644
--- a/src/mesa/main/fbobject.h
+++ b/src/mesa/main/fbobject.h
@@ -101,6 +101,12 @@ _mesa_is_legal_color_format(const struct gl_context *ctx, GLenum baseFormat);
extern GLenum
_mesa_base_fbo_format(struct gl_context *ctx, GLenum internalFormat);
+extern void
+_mesa_check_begin_texture_render(struct gl_context *ctx, struct gl_framebuffer *fb);
+
+extern void
+_mesa_check_end_texture_render(struct gl_context *ctx, struct gl_framebuffer *fb);
+
extern GLboolean GLAPIENTRY
_mesa_IsRenderbufferEXT(GLuint renderbuffer);
diff --git a/src/mesa/sources.mak b/src/mesa/sources.mak
index 5e77e0f5919..6c158c61f7c 100644
--- a/src/mesa/sources.mak
+++ b/src/mesa/sources.mak
@@ -229,6 +229,7 @@ STATETRACKER_SOURCES = \
state_tracker/st_debug.c \
state_tracker/st_draw.c \
state_tracker/st_draw_feedback.c \
+ state_tracker/st_draw_select_emul.c \
state_tracker/st_extensions.c \
state_tracker/st_format.c \
state_tracker/st_gen_mipmap.c \
diff --git a/src/mesa/state_tracker/st_cb_feedback.c b/src/mesa/state_tracker/st_cb_feedback.c
index 9b85a39bedd..c96c97c7f87 100644
--- a/src/mesa/state_tracker/st_cb_feedback.c
+++ b/src/mesa/state_tracker/st_cb_feedback.c
@@ -276,17 +276,28 @@ st_RenderMode(struct gl_context *ctx, GLenum newMode )
{
struct st_context *st = st_context(ctx);
struct draw_context *draw = st->draw;
+ bool hw_acc_path = _mesa_getenv("MESA_HW_SELECT") && !st->select_emul.hw_unsupported;
if (newMode == GL_RENDER) {
/* restore normal VBO draw function */
vbo_set_draw_func(ctx, st_draw_vbo);
}
else if (newMode == GL_SELECT) {
- if (!st->selection_stage)
- st->selection_stage = draw_glselect_stage(ctx, draw);
- draw_set_rasterize_stage(draw, st->selection_stage);
- /* Plug in new vbo draw function */
- vbo_set_draw_func(ctx, st_feedback_draw_vbo);
+ if (hw_acc_path) {
+ if (st_select_emul_begin(ctx)) {
+ vbo_set_draw_func(ctx, st_select_draw_func);
+ }
+ else {
+ hw_acc_path = false;
+ }
+ }
+ if (!hw_acc_path) {
+ if (!st->selection_stage)
+ st->selection_stage = draw_glselect_stage(ctx, draw);
+ draw_set_rasterize_stage(draw, st->selection_stage);
+ /* Plug in new vbo draw function */
+ vbo_set_draw_func(ctx, st_feedback_draw_vbo);
+ }
}
else {
if (!st->feedback_stage)
diff --git a/src/mesa/state_tracker/st_context.h b/src/mesa/state_tracker/st_context.h
index 0a322022149..8a8e11a2d40 100644
--- a/src/mesa/state_tracker/st_context.h
+++ b/src/mesa/state_tracker/st_context.h
@@ -207,6 +207,16 @@ struct st_context
int32_t draw_stamp;
int32_t read_stamp;
+
+ /* data related to hw accelerated GL_SELECT */
+ struct gl_selection_emul
+ {
+ GLboolean hw_unsupported;
+ struct gl_framebuffer *fbo;
+ GLuint rb_depth_name;
+ GLuint rb_color_name;
+ void *fs;
+ } select_emul;
};
diff --git a/src/mesa/state_tracker/st_draw.h b/src/mesa/state_tracker/st_draw.h
index a7b50ce977a..d27e321e92f 100644
--- a/src/mesa/state_tracker/st_draw.h
+++ b/src/mesa/state_tracker/st_draw.h
@@ -87,5 +87,22 @@ pointer_to_offset(const void *ptr)
return (unsigned) (((unsigned long) ptr) & 0xffffffffUL);
}
+/* Functions used by the hw accelerated GL_SELECT emulator
+ */
+extern bool
+st_select_emul_begin(struct gl_context *ctx);
+
+extern void
+st_select_emul_end(struct gl_context *ctx);
+
+extern void
+st_select_draw_func(struct gl_context *ctx,
+ const struct gl_client_array **arrays,
+ const struct _mesa_prim *prims,
+ GLuint nr_prims,
+ const struct _mesa_index_buffer *ib,
+ GLboolean index_bounds_valid,
+ GLuint min_index,
+ GLuint max_index);
#endif
diff --git a/src/mesa/state_tracker/st_draw_select_emul.c b/src/mesa/state_tracker/st_draw_select_emul.c
new file mode 100644
index 00000000000..d6bb2449841
--- /dev/null
+++ b/src/mesa/state_tracker/st_draw_select_emul.c
@@ -0,0 +1,490 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Micael Dias <kam1kaz3 (at) gmail (dot) com>,
+ * Pierre-Eric Pelloux-Prayer
+ * 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, sub license, 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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.
+ *
+ **************************************************************************/
+
+/**
+ * This file contains code that attempts to accelerate GL_SELECT rendering.
+ * It was created with the intent of solving the following bug:
+ * https://bugs.freedesktop.org/show_bug.cgi?id=34495
+ *
+ * This code is a rewritten version of the patch "GL_SELECT hw support v5"
+ * posted by user Pierre-Eric Pelloux-Prayer in the previously mentioned bug.
+ *
+ * In order to test this code, the env. variable MESA_HW_SELECT should be
+ * exported.
+ *
+ * How this algorithm works:
+ * - When in GL_SELECT mode, draw calls are redirected to st_select_draw_func()
+ * which configures states in a way that makes the rendered primitives draw
+ * to a custom FBO that stores depth values (both min and max values) and
+ * emits these values through _mesa_update_hitflag().
+ */
+
+#include "main/imports.h"
+#include "main/image.h"
+#include "main/macros.h"
+#include "main/mfeatures.h"
+#include "main/hash.h"
+
+#include "main/context.h"
+#include "main/enable.h"
+#include "main/fbobject.h"
+#include "main/depth.h"
+#include "main/state.h"
+#include "main/scissor.h"
+#include "main/viewport.h"
+#include "main/framebuffer.h"
+#include "main/feedback.h"
+
+#include "vbo/vbo.h"
+
+#include "st_context.h"
+#include "st_atom.h"
+#include "st_cb_bufferobjects.h"
+#include "st_draw.h"
+#include "st_program.h"
+#include "st_texture.h"
+#include "st_cb_readpixels.h"
+
+#include "pipe/p_context.h"
+#include "pipe/p_defines.h"
+#include "pipe/p_state.h"
+#include "pipe/p_format.h"
+#include "pipe/p_shader_tokens.h"
+#include "util/u_inlines.h"
+#include "util/u_simple_shaders.h"
+#include "cso_cache/cso_context.h"
+#include "tgsi/tgsi_ureg.h"
+
+#include "draw/draw_private.h"
+#include "draw/draw_context.h"
+
+/**
+ * Custom fragment shader that does nothing but ouput a constant color
+ */
+static void*
+st_select_emul_create_fs(struct gl_context *ctx)
+{
+ struct st_context *st = st_context(ctx);
+ struct pipe_context *pipe = st->pipe;
+ struct ureg_program *ureg;
+ struct ureg_dst out;
+ struct ureg_src imm;
+
+ ureg = ureg_create( TGSI_PROCESSOR_FRAGMENT );
+ if (ureg == NULL)
+ return NULL;
+
+ out = ureg_DECL_output( ureg,
+ TGSI_SEMANTIC_COLOR,
+ 0 );
+
+ imm = ureg_imm4f( ureg, 0.0f, 0.0f, 0.0f, 1.0f );
+
+ ureg_MOV( ureg, out, imm );
+ ureg_END( ureg );
+
+ return ureg_create_shader_and_destroy( ureg, pipe );
+}
+
+/**
+ * Clears the depth values of our FBO by setting the
+ * 1st pixel to 1.0f and the 2nd to 0.0f
+ */
+static void
+st_select_emul_clear_fbo(struct gl_context *ctx)
+{
+ struct st_context *st = st_context(ctx);
+ struct gl_scissor_attrib saved_scissor;
+ struct gl_framebuffer *saved_fbo = NULL;
+ GLfloat clearDepth;
+
+ /* save states */
+ saved_scissor = ctx->Scissor;
+ if (ctx->DrawBuffer) {
+ _mesa_reference_framebuffer(&saved_fbo, ctx->DrawBuffer);
+ }
+ clearDepth = ctx->Depth.Clear;
+
+ /* hack needed because Clear does nothing if render mode != GL_RENDER */
+ ctx->RenderMode = GL_RENDER;
+
+ /* disable old draw fbo */
+ if (saved_fbo) {
+ /* XXX: needs flushing? */
+ _mesa_check_end_texture_render(ctx, saved_fbo);
+ }
+
+ /* use our fbo */
+ _mesa_check_begin_texture_render(ctx, st->select_emul.fbo);
+ _mesa_reference_framebuffer(&ctx->DrawBuffer, st->select_emul.fbo);
+
+ /* update fbo */
+ _mesa_update_framebuffer(ctx);
+
+ /* make sure context is up to date */
+ _mesa_update_state(ctx);
+
+ /* enable scissor */
+ ctx->Scissor.Enabled = GL_TRUE;
+
+ /* clear min-Z */
+ ctx->Scissor.X = 0;
+ ctx->Scissor.Y = 0;
+ ctx->Scissor.Width = 1;
+ ctx->Scissor.Height = 1;
+ ctx->Depth.Clear = 1.0f;
+ if (ctx->Driver.ClearDepth)
+ (*ctx->Driver.ClearDepth)(ctx, ctx->Depth.Clear);
+ ctx->NewState |= _NEW_SCISSOR | _NEW_DEPTH | _NEW_BUFFERS;
+ _mesa_update_state(ctx);
+ ctx->Driver.Clear(ctx, BUFFER_BIT_DEPTH);
+
+ /* clear max-Z */
+ ctx->Scissor.X = 1;
+ ctx->Scissor.Y = 0;
+ ctx->Scissor.Width = 1;
+ ctx->Scissor.Height = 1;
+ ctx->Depth.Clear = 0.0f;
+ if (ctx->Driver.ClearDepth)
+ (*ctx->Driver.ClearDepth)(ctx, ctx->Depth.Clear);
+ ctx->NewState |= _NEW_SCISSOR | _NEW_DEPTH;
+ _mesa_update_state(ctx);
+ ctx->Driver.Clear(ctx, BUFFER_BIT_DEPTH);
+
+ /* restore states */
+ _mesa_check_end_texture_render(ctx, st->select_emul.fbo);
+ if (saved_fbo) {
+ _mesa_check_begin_texture_render(ctx, saved_fbo);
+ _mesa_reference_framebuffer(&ctx->DrawBuffer, saved_fbo);
+ _mesa_reference_framebuffer(&saved_fbo, NULL);
+ }
+ else
+ _mesa_reference_framebuffer(&ctx->DrawBuffer, NULL);
+
+ ctx->RenderMode = GL_SELECT;
+ ctx->Depth.Clear = clearDepth;
+ if (ctx->Driver.ClearDepth)
+ (*ctx->Driver.ClearDepth)(ctx, ctx->Depth.Clear);
+ ctx->Scissor = saved_scissor;
+ ctx->NewState |= _NEW_SCISSOR | _NEW_DEPTH | _NEW_BUFFERS;
+ _mesa_update_state(ctx);
+}
+
+/**
+ * Clean up data
+ */
+static void
+st_select_emul_destroy_fbo(struct gl_context *ctx)
+{
+ struct st_context *st = st_context(ctx);
+ if (st->select_emul.rb_color_name) {
+ _mesa_DeleteRenderbuffersEXT(1, &st->select_emul.rb_color_name);
+ st->select_emul.rb_color_name = 0;
+ }
+ if (st->select_emul.rb_depth_name) {
+ _mesa_DeleteRenderbuffersEXT(1, &st->select_emul.rb_depth_name);
+ st->select_emul.rb_depth_name = 0;
+ }
+ if (st->select_emul.fbo) {
+ _mesa_DeleteFramebuffersEXT(1, &st->select_emul.fbo->Name);
+ _mesa_reference_framebuffer(&st->select_emul.fbo, NULL);
+ }
+ if (st->select_emul.fs) {
+ cso_delete_fragment_shader(st->cso_context, st->select_emul.fs);
+ st->select_emul.fs = NULL;
+ }
+}
+
+/**
+ * Create and setup FBO needed for our operations
+ */
+static bool
+st_select_emul_create_fbo(struct gl_context *ctx)
+{
+ GLuint fboName;
+ struct st_context *st = st_context(ctx);
+
+ /* make sure we don't leak memory */
+ st_select_emul_destroy_fbo(ctx);
+
+ /* create buffer names */
+ _mesa_GenFramebuffersEXT(1, &fboName);
+ _mesa_GenRenderbuffersEXT(1, &st->select_emul.rb_depth_name);
+ _mesa_GenRenderbuffersEXT(1, &st->select_emul.rb_color_name);
+
+ /* allocate buffers' memory */
+ _mesa_BindFramebufferEXT(GL_FRAMEBUFFER, fboName);
+ _mesa_BindRenderbufferEXT(GL_RENDERBUFFER, st->select_emul.rb_depth_name);
+ _mesa_RenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, 2, 1);
+ _mesa_BindRenderbufferEXT(GL_RENDERBUFFER, st->select_emul.rb_color_name);
+ _mesa_RenderbufferStorageEXT(GL_RENDERBUFFER, GL_RGBA, 2, 1);
+ _mesa_BindRenderbufferEXT(GL_RENDERBUFFER, 0);
+
+ /* setup fbo */
+ _mesa_FramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER,
+ st->select_emul.rb_depth_name);
+ _mesa_FramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER,
+ st->select_emul.rb_color_name);
+
+ if (_mesa_CheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ st_select_emul_destroy_fbo(ctx);
+ return false;
+ }
+
+ _mesa_BindFramebufferEXT(GL_FRAMEBUFFER, 0);
+
+ /* get fbo pointer */
+ st->select_emul.fbo = _mesa_lookup_framebuffer(ctx, fboName);
+
+ /* create fragment shader */
+ st->select_emul.fs = st_select_emul_create_fs(ctx);
+
+ /* check fbo pointer validity */
+ if (!st->select_emul.fbo) {
+ st_select_emul_destroy_fbo(ctx);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Called when entering GL_SELECT mode.
+ * This function initializes our private data and clears
+ * depth so that the next draw call will have it ready.
+ */
+bool
+st_select_emul_begin(struct gl_context *ctx)
+{
+ struct st_context *st = st_context(ctx);
+
+ /* perform an hw caps check once */
+ static bool hw_checked = false;
+ if (!hw_checked) {
+ hw_checked = true;
+ /* XXX: is this a proper way to check for hw support? */
+ if (!ctx->Driver.ReadPixels ||
+ !ctx->Driver.DrawPixels ||
+ !ctx->Driver.NewFramebuffer ||
+ !ctx->Driver.NewRenderbuffer) {
+ st->select_emul.hw_unsupported = true;
+ return false;
+ }
+ }
+
+ /* initialize fbo if not yet initialized */
+ if (!st->select_emul.fbo) {
+ if (!st_select_emul_create_fbo(ctx)) {
+ return false;
+ }
+ }
+
+ /* clear FBO */
+ st_select_emul_clear_fbo(ctx);
+
+ return true;
+}
+
+void
+st_select_emul_end(struct gl_context *ctx)
+{
+ st_select_emul_destroy_fbo(ctx);
+}
+
+/**
+ * Reads into our FBO and emit z-buffer results
+ * called by st_select_draw_func()
+ */
+static void
+st_select_emul_update_hits(struct gl_context *ctx)
+{
+ struct st_context *st = st_context(ctx);
+ struct gl_framebuffer *saved_fbo = NULL;
+ float zData[2];
+
+ /* set state */
+ if (ctx->ReadBuffer) {
+ _mesa_reference_framebuffer(&saved_fbo, ctx->ReadBuffer);
+ _mesa_check_end_texture_render(ctx, saved_fbo);
+ }
+ _mesa_check_begin_texture_render(ctx, st->select_emul.fbo);
+ _mesa_reference_framebuffer(&ctx->ReadBuffer, st->select_emul.fbo);
+
+ /* update fbo */
+ _mesa_update_framebuffer( ctx );
+
+ /* update state */
+ ctx->NewState |= _NEW_BUFFERS;
+ _mesa_update_state( ctx );
+
+ /* read pixels */
+ ctx->Driver.ReadPixels( ctx, 0, 0, 2, 1,
+ GL_DEPTH_COMPONENT, GL_FLOAT,
+ &ctx->Pack, (void*)zData );
+
+ /* emit z-buffer results */
+ if (zData[0] != 1.0f)
+ _mesa_update_hitflag( ctx, zData[0] );
+ if (zData[1] != 0.0f)
+ _mesa_update_hitflag( ctx, zData[1] );
+
+ /* clear FBO */
+ st_select_emul_clear_fbo(ctx);
+
+ /* restore state */
+ _mesa_check_end_texture_render(ctx, st->select_emul.fbo);
+ if (saved_fbo) {
+ _mesa_check_begin_texture_render(ctx, saved_fbo);
+ _mesa_reference_framebuffer(&ctx->ReadBuffer, saved_fbo);
+ _mesa_reference_framebuffer(&saved_fbo, NULL);
+ }
+ else
+ _mesa_reference_framebuffer(&ctx->ReadBuffer, NULL);
+
+ /* update state */
+ ctx->NewState |= _NEW_BUFFERS;
+ _mesa_update_state( ctx );
+}
+
+/**
+ * Plug in draw function to draw in GL_SELECT mode.
+ * This function does the following steps:
+ * 1) render into the 1st pixel of our FBO with depth state
+ * configured so that we get the lowest Z values
+ * 2) render into the 2nd pixel of our FBO with depth state
+ * configured so that we get the highest Z values
+ * 3) calls st_select_emul_update_hits() to emit results
+ */
+void
+st_select_draw_func(struct gl_context *ctx,
+ const struct gl_client_array **arrays,
+ const struct _mesa_prim *prims,
+ GLuint nr_prims,
+ const struct _mesa_index_buffer *ib,
+ GLboolean index_bounds_valid,
+ GLuint min_index,
+ GLuint max_index)
+{
+ GLboolean saved_scissorEnabled;
+ struct gl_framebuffer *saved_fbo = NULL;
+ struct st_context *st = st_context(ctx);
+ struct pipe_depth_stencil_alpha_state state_depth;
+ struct pipe_viewport_state state_viewport;
+
+ /* save states */
+ if (ctx->DrawBuffer)
+ _mesa_reference_framebuffer(&saved_fbo, ctx->DrawBuffer);
+
+ saved_scissorEnabled = ctx->Scissor.Enabled;
+ cso_save_fragment_shader(st->cso_context);
+ cso_save_depth_stencil_alpha(st->cso_context);
+ cso_save_viewport(st->cso_context);
+
+ /* prepare stencil state */
+ memset(&state_depth, 0, sizeof(state_depth));
+ state_depth.depth.enabled = 1;
+ state_depth.depth.writemask = 1;
+
+ /* prepare viewport state */
+ memset(&state_viewport, 0, sizeof(state_viewport));
+ state_viewport.scale[0] = 0.5f * 2.0f * 0.5;
+ state_viewport.scale[1] = 0.5f * 1.0f;
+ state_viewport.scale[2] = 1.0f;
+ state_viewport.scale[3] = 1.0f;
+ state_viewport.translate[1] = 0.5f * 1.0f;
+ state_viewport.translate[2] = 0.0f;
+ state_viewport.translate[3] = 0.0f;
+
+ /* use our custom fragment shader */
+ cso_set_fragment_shader_handle(st->cso_context, st->select_emul.fs);
+
+ /* disable scissor */
+ ctx->Scissor.Enabled = GL_FALSE;
+
+ /* disable old draw fbo */
+ if (saved_fbo)
+ _mesa_check_end_texture_render(ctx, saved_fbo);
+
+ /* use our fbo */
+ _mesa_check_begin_texture_render(ctx, st->select_emul.fbo);
+ _mesa_reference_framebuffer(&ctx->DrawBuffer, st->select_emul.fbo);
+
+ /* update fbo */
+ _mesa_update_framebuffer( ctx );
+
+ /* update context */
+ _mesa_update_state(ctx);
+
+ /* render min-z */
+ {
+ /* specific states */
+ state_depth.depth.func = PIPE_FUNC_LESS;
+ state_viewport.translate[0] = 0.5f * 1.0f + 0.0f;
+
+ cso_set_depth_stencil_alpha(st->cso_context, &state_depth);
+ cso_set_viewport(st->cso_context, &state_viewport);
+
+ /* draw */
+ st_draw_vbo(ctx, arrays, prims, nr_prims, ib, index_bounds_valid, min_index, max_index);
+ }
+
+ /* render max-z */
+ {
+ state_depth.depth.func = PIPE_FUNC_GREATER;
+ state_viewport.translate[0] = 0.5f * 1.0f + 1.0f;
+
+ cso_set_depth_stencil_alpha(st->cso_context, &state_depth);
+ cso_set_viewport(st->cso_context, &state_viewport);
+
+ /* draw */
+ st_draw_vbo(ctx, arrays, prims, nr_prims, ib, index_bounds_valid, min_index, max_index);
+ }
+
+ /* XXX: needs flushing? */
+
+ /* restore states */
+ cso_restore_fragment_shader(st->cso_context);
+ cso_restore_depth_stencil_alpha(st->cso_context);
+ cso_restore_viewport(st->cso_context);
+ ctx->Scissor.Enabled = saved_scissorEnabled;
+ _mesa_check_end_texture_render(ctx, st->select_emul.fbo);
+ if (saved_fbo) {
+ _mesa_check_begin_texture_render(ctx, saved_fbo);
+ _mesa_reference_framebuffer(&ctx->DrawBuffer, saved_fbo);
+ _mesa_reference_framebuffer(&saved_fbo, NULL);
+ }
+ else
+ _mesa_reference_framebuffer(&ctx->DrawBuffer, NULL);
+ _mesa_update_state(ctx);
+
+ /* update hits */
+ st_select_emul_update_hits(ctx);
+}
+