aboutsummaryrefslogtreecommitdiff
path: root/mesalib/src/mesa/state_tracker
diff options
context:
space:
mode:
authormarha <marha@users.sourceforge.net>2015-05-26 19:43:34 +0200
committermarha <marha@users.sourceforge.net>2015-05-26 19:43:34 +0200
commit912e881bec8b16f2331225960645c3bdf5a8ba2d (patch)
tree92afd568d4b3eaab1faf26d8d95d8c6df0173079 /mesalib/src/mesa/state_tracker
parent2e00f3764228cfc91180bbe9375a8d85e0e65a5b (diff)
parent843964ee791452b197e41dacb0146f5b456ffaa5 (diff)
downloadvcxsrv-912e881bec8b16f2331225960645c3bdf5a8ba2d.tar.gz
vcxsrv-912e881bec8b16f2331225960645c3bdf5a8ba2d.tar.bz2
vcxsrv-912e881bec8b16f2331225960645c3bdf5a8ba2d.zip
Merge remote-tracking branch 'origin/released'
Conflicts: mesalib/src/mapi/glapi/glapi_priv.h mesalib/src/mesa/drivers/dri/swrast/swrast.c xorg-server/dix/dispatch.c xorg-server/os/utils.c xorg-server/record/record.c
Diffstat (limited to 'mesalib/src/mesa/state_tracker')
-rw-r--r--mesalib/src/mesa/state_tracker/st_atom_array.c172
-rw-r--r--mesalib/src/mesa/state_tracker/st_atom_framebuffer.c5
-rw-r--r--mesalib/src/mesa/state_tracker/st_cb_fbo.c2
-rw-r--r--mesalib/src/mesa/state_tracker/st_cb_flush.c35
-rw-r--r--mesalib/src/mesa/state_tracker/st_cb_flush.h3
-rw-r--r--mesalib/src/mesa/state_tracker/st_cb_perfmon.c425
-rw-r--r--mesalib/src/mesa/state_tracker/st_cb_perfmon.h64
-rw-r--r--mesalib/src/mesa/state_tracker/st_context.c16
-rw-r--r--mesalib/src/mesa/state_tracker/st_context.h3
-rw-r--r--mesalib/src/mesa/state_tracker/st_extensions.c10
-rw-r--r--mesalib/src/mesa/state_tracker/st_format.c2
-rw-r--r--mesalib/src/mesa/state_tracker/st_glsl_to_tgsi.cpp30
-rw-r--r--mesalib/src/mesa/state_tracker/st_manager.c4
-rw-r--r--mesalib/src/mesa/state_tracker/st_program.c5
-rw-r--r--mesalib/src/mesa/state_tracker/st_program.h1
15 files changed, 722 insertions, 55 deletions
diff --git a/mesalib/src/mesa/state_tracker/st_atom_array.c b/mesalib/src/mesa/state_tracker/st_atom_array.c
index d4fb8b862..56b8019a3 100644
--- a/mesalib/src/mesa/state_tracker/st_atom_array.c
+++ b/mesalib/src/mesa/state_tracker/st_atom_array.c
@@ -44,7 +44,6 @@
#include "cso_cache/cso_context.h"
#include "util/u_math.h"
-
#include "main/bufferobj.h"
#include "main/glformats.h"
@@ -311,6 +310,18 @@ st_pipe_vertex_format(GLenum type, GLuint size, GLenum format,
return PIPE_FORMAT_NONE; /* silence compiler warning */
}
+static const struct gl_client_array *
+get_client_array(const struct st_vertex_program *vp,
+ const struct gl_client_array **arrays,
+ int attr)
+{
+ const GLuint mesaAttr = vp->index_to_input[attr];
+ /* st_program uses 0xffffffff to denote a double placeholder attribute */
+ if (mesaAttr == ST_DOUBLE_ATTRIB_PLACEHOLDER)
+ return NULL;
+ return arrays[mesaAttr];
+}
+
/**
* Examine the active arrays to determine if we have interleaved
* vertex arrays all living in one VBO, or all living in user space.
@@ -327,11 +338,16 @@ is_interleaved_arrays(const struct st_vertex_program *vp,
GLboolean userSpaceBuffer = GL_FALSE;
for (attr = 0; attr < vpv->num_inputs; attr++) {
- const GLuint mesaAttr = vp->index_to_input[attr];
- const struct gl_client_array *array = arrays[mesaAttr];
- const struct gl_buffer_object *bufObj = array->BufferObj;
- const GLsizei stride = array->StrideB; /* in bytes */
+ const struct gl_client_array *array;
+ const struct gl_buffer_object *bufObj;
+ GLsizei stride;
+
+ array = get_client_array(vp, arrays, attr);
+ if (!array)
+ continue;
+ stride = array->StrideB; /* in bytes */
+ bufObj = array->BufferObj;
if (attr == 0) {
/* save info about the first array */
firstStride = stride;
@@ -358,6 +374,55 @@ is_interleaved_arrays(const struct st_vertex_program *vp,
return GL_TRUE;
}
+static void init_velement(struct pipe_vertex_element *velement,
+ int src_offset, int format,
+ int instance_divisor, int vbo_index)
+{
+ velement->src_offset = src_offset;
+ velement->src_format = format;
+ velement->instance_divisor = instance_divisor;
+ velement->vertex_buffer_index = vbo_index;
+ assert(velement->src_format);
+}
+
+static void init_velement_lowered(struct st_context *st,
+ struct pipe_vertex_element *velements,
+ int src_offset, int format,
+ int instance_divisor, int vbo_index,
+ int nr_components, GLboolean doubles,
+ GLuint *attr_idx)
+{
+ int idx = *attr_idx;
+ if (doubles) {
+ int lower_format;
+
+ if (nr_components == 1)
+ lower_format = PIPE_FORMAT_R32G32_UINT;
+ else if (nr_components >= 2)
+ lower_format = PIPE_FORMAT_R32G32B32A32_UINT;
+
+ init_velement(&velements[idx], src_offset,
+ lower_format, instance_divisor, vbo_index);
+ idx++;
+
+ if (nr_components > 2) {
+ if (nr_components == 3)
+ lower_format = PIPE_FORMAT_R32G32_UINT;
+ else if (nr_components >= 4)
+ lower_format = PIPE_FORMAT_R32G32B32A32_UINT;
+
+ init_velement(&velements[idx], src_offset + 4 * sizeof(float),
+ lower_format, instance_divisor, vbo_index);
+ idx++;
+ }
+ } else {
+ init_velement(&velements[idx], src_offset,
+ format, instance_divisor, vbo_index);
+ idx++;
+ }
+ *attr_idx = idx;
+}
+
/**
* Set up for drawing interleaved arrays that all live in one VBO
* or all live in user space.
@@ -365,13 +430,15 @@ is_interleaved_arrays(const struct st_vertex_program *vp,
* \param velements returns vertex element info
*/
static boolean
-setup_interleaved_attribs(const struct st_vertex_program *vp,
+setup_interleaved_attribs(struct st_context *st,
+ const struct st_vertex_program *vp,
const struct st_vp_variant *vpv,
const struct gl_client_array **arrays,
struct pipe_vertex_buffer *vbuffer,
- struct pipe_vertex_element velements[])
+ struct pipe_vertex_element velements[],
+ unsigned *num_velements)
{
- GLuint attr;
+ GLuint attr, attr_idx;
const GLubyte *low_addr = NULL;
GLboolean usingVBO; /* all arrays in a VBO? */
struct gl_buffer_object *bufobj;
@@ -381,8 +448,10 @@ setup_interleaved_attribs(const struct st_vertex_program *vp,
* Init bufobj and stride.
*/
if (vpv->num_inputs) {
- const GLuint mesaAttr0 = vp->index_to_input[0];
- const struct gl_client_array *array = arrays[mesaAttr0];
+ const struct gl_client_array *array;
+
+ array = get_client_array(vp, arrays, 0);
+ assert(array);
/* Since we're doing interleaved arrays, we know there'll be at most
* one buffer object and the stride will be the same for all arrays.
@@ -394,7 +463,11 @@ setup_interleaved_attribs(const struct st_vertex_program *vp,
low_addr = arrays[vp->index_to_input[0]]->Ptr;
for (attr = 1; attr < vpv->num_inputs; attr++) {
- const GLubyte *start = arrays[vp->index_to_input[attr]]->Ptr;
+ const GLubyte *start;
+ array = get_client_array(vp, arrays, attr);
+ if (!array)
+ continue;
+ start = array->Ptr;
low_addr = MIN2(low_addr, start);
}
}
@@ -408,25 +481,33 @@ setup_interleaved_attribs(const struct st_vertex_program *vp,
/* are the arrays in user space? */
usingVBO = _mesa_is_bufferobj(bufobj);
+ attr_idx = 0;
for (attr = 0; attr < vpv->num_inputs; attr++) {
- const GLuint mesaAttr = vp->index_to_input[attr];
- const struct gl_client_array *array = arrays[mesaAttr];
- unsigned src_offset = (unsigned) (array->Ptr - low_addr);
+ const struct gl_client_array *array;
+ unsigned src_offset;
+ unsigned src_format;
+
+ array = get_client_array(vp, arrays, attr);
+ if (!array)
+ continue;
+ src_offset = (unsigned) (array->Ptr - low_addr);
assert(array->_ElementSize ==
_mesa_bytes_per_vertex_attrib(array->Size, array->Type));
- velements[attr].src_offset = src_offset;
- velements[attr].instance_divisor = array->InstanceDivisor;
- velements[attr].vertex_buffer_index = 0;
- velements[attr].src_format = st_pipe_vertex_format(array->Type,
- array->Size,
- array->Format,
- array->Normalized,
- array->Integer);
- assert(velements[attr].src_format);
+ src_format = st_pipe_vertex_format(array->Type,
+ array->Size,
+ array->Format,
+ array->Normalized,
+ array->Integer);
+
+ init_velement_lowered(st, velements, src_offset, src_format,
+ array->InstanceDivisor, 0,
+ array->Size, array->Doubles, &attr_idx);
}
+ *num_velements = attr_idx;
+
/*
* Return the vbuffer info and setup user-space attrib info, if needed.
*/
@@ -472,17 +553,25 @@ setup_non_interleaved_attribs(struct st_context *st,
const struct st_vp_variant *vpv,
const struct gl_client_array **arrays,
struct pipe_vertex_buffer vbuffer[],
- struct pipe_vertex_element velements[])
+ struct pipe_vertex_element velements[],
+ unsigned *num_velements)
{
struct gl_context *ctx = st->ctx;
- GLuint attr;
+ GLuint attr, attr_idx = 0;
for (attr = 0; attr < vpv->num_inputs; attr++) {
const GLuint mesaAttr = vp->index_to_input[attr];
- const struct gl_client_array *array = arrays[mesaAttr];
- struct gl_buffer_object *bufobj = array->BufferObj;
- GLsizei stride = array->StrideB;
+ const struct gl_client_array *array;
+ struct gl_buffer_object *bufobj;
+ GLsizei stride;
+ unsigned src_format;
+ array = get_client_array(vp, arrays, attr);
+ if (!array)
+ continue;
+
+ stride = array->StrideB;
+ bufobj = array->BufferObj;
assert(array->_ElementSize ==
_mesa_bytes_per_vertex_attrib(array->Size, array->Type));
@@ -524,16 +613,19 @@ setup_non_interleaved_attribs(struct st_context *st,
/* common-case setup */
vbuffer[attr].stride = stride; /* in bytes */
- velements[attr].src_offset = 0;
- velements[attr].instance_divisor = array->InstanceDivisor;
- velements[attr].vertex_buffer_index = attr;
- velements[attr].src_format = st_pipe_vertex_format(array->Type,
- array->Size,
- array->Format,
- array->Normalized,
- array->Integer);
- assert(velements[attr].src_format);
+ src_format = st_pipe_vertex_format(array->Type,
+ array->Size,
+ array->Format,
+ array->Normalized,
+ array->Integer);
+
+ init_velement_lowered(st, velements, 0, src_format,
+ array->InstanceDivisor, attr,
+ array->Size, array->Doubles, &attr_idx);
+
}
+
+ *num_velements = attr_idx;
return TRUE;
}
@@ -563,25 +655,23 @@ static void update_array(struct st_context *st)
* Setup the vbuffer[] and velements[] arrays.
*/
if (is_interleaved_arrays(vp, vpv, arrays)) {
- if (!setup_interleaved_attribs(vp, vpv, arrays, vbuffer, velements)) {
+ if (!setup_interleaved_attribs(st, vp, vpv, arrays, vbuffer, velements, &num_velements)) {
st->vertex_array_out_of_memory = TRUE;
return;
}
num_vbuffers = 1;
- num_velements = vpv->num_inputs;
if (num_velements == 0)
num_vbuffers = 0;
}
else {
if (!setup_non_interleaved_attribs(st, vp, vpv, arrays, vbuffer,
- velements)) {
+ velements, &num_velements)) {
st->vertex_array_out_of_memory = TRUE;
return;
}
num_vbuffers = vpv->num_inputs;
- num_velements = vpv->num_inputs;
}
cso_set_vertex_buffers(st->cso_context, 0, num_vbuffers, vbuffer);
diff --git a/mesalib/src/mesa/state_tracker/st_atom_framebuffer.c b/mesalib/src/mesa/state_tracker/st_atom_framebuffer.c
index b195c55b3..ae883a253 100644
--- a/mesalib/src/mesa/state_tracker/st_atom_framebuffer.c
+++ b/mesalib/src/mesa/state_tracker/st_atom_framebuffer.c
@@ -134,7 +134,10 @@ update_framebuffer_state( struct st_context *st )
else {
strb = st_renderbuffer(fb->Attachment[BUFFER_STENCIL].Renderbuffer);
if (strb) {
- assert(strb->surface);
+ if (strb->is_rtt) {
+ /* rendering to a GL texture, may have to update surface */
+ st_update_renderbuffer_surface(st, strb);
+ }
pipe_surface_reference(&framebuffer->zsbuf, strb->surface);
update_framebuffer_size(framebuffer, strb->surface);
}
diff --git a/mesalib/src/mesa/state_tracker/st_cb_fbo.c b/mesalib/src/mesa/state_tracker/st_cb_fbo.c
index 296ea1e0d..0399eef72 100644
--- a/mesalib/src/mesa/state_tracker/st_cb_fbo.c
+++ b/mesalib/src/mesa/state_tracker/st_cb_fbo.c
@@ -842,7 +842,7 @@ void st_init_fbo_functions(struct dd_function_table *functions)
functions->NewFramebuffer = st_new_framebuffer;
functions->NewRenderbuffer = st_new_renderbuffer;
functions->BindFramebuffer = st_bind_framebuffer;
- functions->FramebufferRenderbuffer = _mesa_framebuffer_renderbuffer;
+ functions->FramebufferRenderbuffer = _mesa_FramebufferRenderbuffer_sw;
functions->RenderTexture = st_render_texture;
functions->FinishRenderTexture = st_finish_render_texture;
functions->ValidateFramebuffer = st_validate_framebuffer;
diff --git a/mesalib/src/mesa/state_tracker/st_cb_flush.c b/mesalib/src/mesa/state_tracker/st_cb_flush.c
index ca51eeee3..82affd2de 100644
--- a/mesalib/src/mesa/state_tracker/st_cb_flush.c
+++ b/mesalib/src/mesa/state_tracker/st_cb_flush.c
@@ -141,11 +141,44 @@ static void st_glFinish(struct gl_context *ctx)
}
-void st_init_flush_functions(struct dd_function_table *functions)
+/**
+ * Query information about GPU resets observed by this context
+ *
+ * Called via \c dd_function_table::GetGraphicsResetStatus.
+ */
+static GLenum
+st_get_graphics_reset_status(struct gl_context *ctx)
+{
+ struct st_context *st = st_context(ctx);
+ enum pipe_reset_status status;
+
+ status = st->pipe->get_device_reset_status(st->pipe);
+
+ switch (status) {
+ case PIPE_NO_RESET:
+ return GL_NO_ERROR;
+ case PIPE_GUILTY_CONTEXT_RESET:
+ return GL_GUILTY_CONTEXT_RESET_ARB;
+ case PIPE_INNOCENT_CONTEXT_RESET:
+ return GL_INNOCENT_CONTEXT_RESET_ARB;
+ case PIPE_UNKNOWN_CONTEXT_RESET:
+ return GL_UNKNOWN_CONTEXT_RESET_ARB;
+ default:
+ assert(0);
+ return GL_NO_ERROR;
+ }
+}
+
+
+void st_init_flush_functions(struct pipe_screen *screen,
+ struct dd_function_table *functions)
{
functions->Flush = st_glFlush;
functions->Finish = st_glFinish;
+ if (screen->get_param(screen, PIPE_CAP_DEVICE_RESET_STATUS_QUERY))
+ functions->GetGraphicsResetStatus = st_get_graphics_reset_status;
+
/* Windows opengl32.dll calls glFinish prior to every swapbuffers.
* This is unnecessary and degrades performance. Luckily we have some
* scope to work around this, as the externally-visible behaviour of
diff --git a/mesalib/src/mesa/state_tracker/st_cb_flush.h b/mesalib/src/mesa/state_tracker/st_cb_flush.h
index 84ffc63ae..f92dcd56b 100644
--- a/mesalib/src/mesa/state_tracker/st_cb_flush.h
+++ b/mesalib/src/mesa/state_tracker/st_cb_flush.h
@@ -37,7 +37,8 @@ struct pipe_fence_handle;
struct st_context;
extern void
-st_init_flush_functions(struct dd_function_table *functions);
+st_init_flush_functions(struct pipe_screen *screen,
+ struct dd_function_table *functions);
extern void
st_flush(struct st_context *st,
diff --git a/mesalib/src/mesa/state_tracker/st_cb_perfmon.c b/mesalib/src/mesa/state_tracker/st_cb_perfmon.c
new file mode 100644
index 000000000..1bb5be397
--- /dev/null
+++ b/mesalib/src/mesa/state_tracker/st_cb_perfmon.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2013 Christoph Bumiller
+ * Copyright (C) 2015 Samuel Pitoiset
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * Performance monitoring counters interface to gallium.
+ */
+
+#include "st_debug.h"
+#include "st_context.h"
+#include "st_cb_bitmap.h"
+#include "st_cb_perfmon.h"
+
+#include "util/bitset.h"
+
+#include "pipe/p_context.h"
+#include "pipe/p_screen.h"
+#include "util/u_memory.h"
+
+/**
+ * Return a PIPE_QUERY_x type >= PIPE_QUERY_DRIVER_SPECIFIC, or -1 if
+ * the driver-specific query doesn't exist.
+ */
+static int
+find_query_type(struct pipe_screen *screen, const char *name)
+{
+ int num_queries;
+ int type = -1;
+ int i;
+
+ num_queries = screen->get_driver_query_info(screen, 0, NULL);
+ if (!num_queries)
+ return type;
+
+ for (i = 0; i < num_queries; i++) {
+ struct pipe_driver_query_info info;
+
+ if (!screen->get_driver_query_info(screen, i, &info))
+ continue;
+
+ if (!strncmp(info.name, name, strlen(name))) {
+ type = info.query_type;
+ break;
+ }
+ }
+ return type;
+}
+
+/**
+ * Return TRUE if the underlying driver expose GPU counters.
+ */
+static bool
+has_gpu_counters(struct pipe_screen *screen)
+{
+ int num_groups, gid;
+
+ num_groups = screen->get_driver_query_group_info(screen, 0, NULL);
+ for (gid = 0; gid < num_groups; gid++) {
+ struct pipe_driver_query_group_info group_info;
+
+ if (!screen->get_driver_query_group_info(screen, gid, &group_info))
+ continue;
+
+ if (group_info.type == PIPE_DRIVER_QUERY_GROUP_TYPE_GPU)
+ return true;
+ }
+ return false;
+}
+
+static bool
+init_perf_monitor(struct gl_context *ctx, struct gl_perf_monitor_object *m)
+{
+ struct st_perf_monitor_object *stm = st_perf_monitor_object(m);
+ struct pipe_screen *screen = st_context(ctx)->pipe->screen;
+ struct pipe_context *pipe = st_context(ctx)->pipe;
+ int gid, cid;
+
+ st_flush_bitmap_cache(st_context(ctx));
+
+ /* Create a query for each active counter. */
+ for (gid = 0; gid < ctx->PerfMonitor.NumGroups; gid++) {
+ const struct gl_perf_monitor_group *g = &ctx->PerfMonitor.Groups[gid];
+
+ if (m->ActiveGroups[gid] > g->MaxActiveCounters) {
+ /* Maximum number of counters reached. Cannot start the session. */
+ if (ST_DEBUG & DEBUG_MESA) {
+ debug_printf("Maximum number of counters reached. "
+ "Cannot start the session!\n");
+ }
+ return false;
+ }
+
+ for (cid = 0; cid < g->NumCounters; cid++) {
+ const struct gl_perf_monitor_counter *c = &g->Counters[cid];
+ struct st_perf_counter_object *cntr;
+ int query_type;
+
+ if (!BITSET_TEST(m->ActiveCounters[gid], cid))
+ continue;
+
+ query_type = find_query_type(screen, c->Name);
+ assert(query_type != -1);
+
+ cntr = CALLOC_STRUCT(st_perf_counter_object);
+ if (!cntr)
+ return false;
+
+ cntr->query = pipe->create_query(pipe, query_type, 0);
+ cntr->id = cid;
+ cntr->group_id = gid;
+
+ list_addtail(&cntr->list, &stm->active_counters);
+ }
+ }
+ return true;
+}
+
+static void
+reset_perf_monitor(struct st_perf_monitor_object *stm,
+ struct pipe_context *pipe)
+{
+ struct st_perf_counter_object *cntr, *tmp;
+
+ LIST_FOR_EACH_ENTRY_SAFE(cntr, tmp, &stm->active_counters, list) {
+ if (cntr->query)
+ pipe->destroy_query(pipe, cntr->query);
+ list_del(&cntr->list);
+ free(cntr);
+ }
+}
+
+static struct gl_perf_monitor_object *
+st_NewPerfMonitor(struct gl_context *ctx)
+{
+ struct st_perf_monitor_object *stq = ST_CALLOC_STRUCT(st_perf_monitor_object);
+ if (stq) {
+ list_inithead(&stq->active_counters);
+ return &stq->base;
+ }
+ return NULL;
+}
+
+static void
+st_DeletePerfMonitor(struct gl_context *ctx, struct gl_perf_monitor_object *m)
+{
+ struct st_perf_monitor_object *stm = st_perf_monitor_object(m);
+ struct pipe_context *pipe = st_context(ctx)->pipe;
+
+ reset_perf_monitor(stm, pipe);
+ FREE(stm);
+}
+
+static GLboolean
+st_BeginPerfMonitor(struct gl_context *ctx, struct gl_perf_monitor_object *m)
+{
+ struct st_perf_monitor_object *stm = st_perf_monitor_object(m);
+ struct pipe_context *pipe = st_context(ctx)->pipe;
+ struct st_perf_counter_object *cntr;
+
+ if (LIST_IS_EMPTY(&stm->active_counters)) {
+ /* Create a query for each active counter before starting
+ * a new monitoring session. */
+ if (!init_perf_monitor(ctx, m))
+ goto fail;
+ }
+
+ /* Start the query for each active counter. */
+ LIST_FOR_EACH_ENTRY(cntr, &stm->active_counters, list) {
+ if (!pipe->begin_query(pipe, cntr->query))
+ goto fail;
+ }
+ return true;
+
+fail:
+ /* Failed to start the monitoring session. */
+ reset_perf_monitor(stm, pipe);
+ return false;
+}
+
+static void
+st_EndPerfMonitor(struct gl_context *ctx, struct gl_perf_monitor_object *m)
+{
+ struct st_perf_monitor_object *stm = st_perf_monitor_object(m);
+ struct pipe_context *pipe = st_context(ctx)->pipe;
+ struct st_perf_counter_object *cntr;
+
+ /* Stop the query for each active counter. */
+ LIST_FOR_EACH_ENTRY(cntr, &stm->active_counters, list)
+ pipe->end_query(pipe, cntr->query);
+}
+
+static void
+st_ResetPerfMonitor(struct gl_context *ctx, struct gl_perf_monitor_object *m)
+{
+ struct st_perf_monitor_object *stm = st_perf_monitor_object(m);
+ struct pipe_context *pipe = st_context(ctx)->pipe;
+
+ if (!m->Ended)
+ st_EndPerfMonitor(ctx, m);
+
+ reset_perf_monitor(stm, pipe);
+
+ if (m->Active)
+ st_BeginPerfMonitor(ctx, m);
+}
+
+static GLboolean
+st_IsPerfMonitorResultAvailable(struct gl_context *ctx,
+ struct gl_perf_monitor_object *m)
+{
+ struct st_perf_monitor_object *stm = st_perf_monitor_object(m);
+ struct pipe_context *pipe = st_context(ctx)->pipe;
+ struct st_perf_counter_object *cntr;
+
+ if (LIST_IS_EMPTY(&stm->active_counters))
+ return false;
+
+ /* The result of a monitoring session is only available if the query of
+ * each active counter is idle. */
+ LIST_FOR_EACH_ENTRY(cntr, &stm->active_counters, list) {
+ union pipe_query_result result;
+ if (!pipe->get_query_result(pipe, cntr->query, FALSE, &result)) {
+ /* The query is busy. */
+ return false;
+ }
+ }
+ return true;
+}
+
+static void
+st_GetPerfMonitorResult(struct gl_context *ctx,
+ struct gl_perf_monitor_object *m,
+ GLsizei dataSize,
+ GLuint *data,
+ GLint *bytesWritten)
+{
+ struct st_perf_monitor_object *stm = st_perf_monitor_object(m);
+ struct pipe_context *pipe = st_context(ctx)->pipe;
+ struct st_perf_counter_object *cntr;
+
+ /* Copy data to the supplied array (data).
+ *
+ * The output data format is: <group ID, counter ID, value> for each
+ * active counter. The API allows counters to appear in any order.
+ */
+ GLsizei offset = 0;
+
+ /* Read query results for each active counter. */
+ LIST_FOR_EACH_ENTRY(cntr, &stm->active_counters, list) {
+ union pipe_query_result result = { 0 };
+ int gid, cid;
+ GLenum type;
+
+ cid = cntr->id;
+ gid = cntr->group_id;
+ type = ctx->PerfMonitor.Groups[gid].Counters[cid].Type;
+
+ if (!pipe->get_query_result(pipe, cntr->query, TRUE, &result))
+ continue;
+
+ data[offset++] = gid;
+ data[offset++] = cid;
+ switch (type) {
+ case GL_UNSIGNED_INT64_AMD:
+ *(uint64_t *)&data[offset] = result.u64;
+ offset += sizeof(uint64_t) / sizeof(GLuint);
+ break;
+ case GL_UNSIGNED_INT:
+ *(uint32_t *)&data[offset] = result.u32;
+ offset += sizeof(uint32_t) / sizeof(GLuint);
+ break;
+ case GL_FLOAT:
+ case GL_PERCENTAGE_AMD:
+ *(GLfloat *)&data[offset] = result.f;
+ offset += sizeof(GLfloat) / sizeof(GLuint);
+ break;
+ }
+ }
+
+ if (bytesWritten)
+ *bytesWritten = offset * sizeof(GLuint);
+}
+
+
+bool
+st_init_perfmon(struct st_context *st)
+{
+ struct gl_perf_monitor_state *perfmon = &st->ctx->PerfMonitor;
+ struct pipe_screen *screen = st->pipe->screen;
+ struct gl_perf_monitor_group *groups = NULL;
+ int num_counters, num_groups;
+ int gid, cid;
+
+ if (!screen->get_driver_query_info || !screen->get_driver_query_group_info)
+ return false;
+
+ if (!has_gpu_counters(screen)) {
+ /* According to the spec, GL_AMD_performance_monitor must only
+ * expose GPU counters. */
+ return false;
+ }
+
+ /* Get the number of available queries. */
+ num_counters = screen->get_driver_query_info(screen, 0, NULL);
+ if (!num_counters)
+ return false;
+
+ /* Get the number of available groups. */
+ num_groups = screen->get_driver_query_group_info(screen, 0, NULL);
+ if (num_groups)
+ groups = CALLOC(num_groups, sizeof(*groups));
+ if (!groups)
+ return false;
+
+ for (gid = 0; gid < num_groups; gid++) {
+ struct gl_perf_monitor_group *g = &groups[perfmon->NumGroups];
+ struct pipe_driver_query_group_info group_info;
+ struct gl_perf_monitor_counter *counters = NULL;
+
+ if (!screen->get_driver_query_group_info(screen, gid, &group_info))
+ continue;
+
+ if (group_info.type != PIPE_DRIVER_QUERY_GROUP_TYPE_GPU)
+ continue;
+
+ g->Name = group_info.name;
+ g->MaxActiveCounters = group_info.max_active_queries;
+ g->NumCounters = 0;
+ g->Counters = NULL;
+
+ if (group_info.num_queries)
+ counters = CALLOC(group_info.num_queries, sizeof(*counters));
+ if (!counters)
+ goto fail;
+
+ for (cid = 0; cid < num_counters; cid++) {
+ struct gl_perf_monitor_counter *c = &counters[g->NumCounters];
+ struct pipe_driver_query_info info;
+
+ if (!screen->get_driver_query_info(screen, cid, &info))
+ continue;
+ if (info.group_id != gid)
+ continue;
+
+ c->Name = info.name;
+ switch (info.type) {
+ case PIPE_DRIVER_QUERY_TYPE_UINT64:
+ c->Minimum.u64 = 0;
+ c->Maximum.u64 = info.max_value.u64 ? info.max_value.u64 : -1;
+ c->Type = GL_UNSIGNED_INT64_AMD;
+ break;
+ case PIPE_DRIVER_QUERY_TYPE_UINT:
+ c->Minimum.u32 = 0;
+ c->Maximum.u32 = info.max_value.u32 ? info.max_value.u32 : -1;
+ c->Type = GL_UNSIGNED_INT;
+ break;
+ case PIPE_DRIVER_QUERY_TYPE_FLOAT:
+ c->Minimum.f = 0.0;
+ c->Maximum.f = info.max_value.f ? info.max_value.f : -1;
+ c->Type = GL_FLOAT;
+ break;
+ case PIPE_DRIVER_QUERY_TYPE_PERCENTAGE:
+ c->Minimum.f = 0.0f;
+ c->Maximum.f = 100.0f;
+ c->Type = GL_PERCENTAGE_AMD;
+ break;
+ default:
+ unreachable("Invalid driver query type!");
+ }
+ g->NumCounters++;
+ }
+ g->Counters = counters;
+ perfmon->NumGroups++;
+ }
+ perfmon->Groups = groups;
+
+ return true;
+
+fail:
+ for (gid = 0; gid < num_groups; gid++)
+ FREE((void *)groups[gid].Counters);
+ FREE(groups);
+ return false;
+}
+
+void
+st_destroy_perfmon(struct st_context *st)
+{
+ struct gl_perf_monitor_state *perfmon = &st->ctx->PerfMonitor;
+ int gid;
+
+ for (gid = 0; gid < perfmon->NumGroups; gid++)
+ FREE((void *)perfmon->Groups[gid].Counters);
+ FREE((void *)perfmon->Groups);
+}
+
+void st_init_perfmon_functions(struct dd_function_table *functions)
+{
+ functions->NewPerfMonitor = st_NewPerfMonitor;
+ functions->DeletePerfMonitor = st_DeletePerfMonitor;
+ functions->BeginPerfMonitor = st_BeginPerfMonitor;
+ functions->EndPerfMonitor = st_EndPerfMonitor;
+ functions->ResetPerfMonitor = st_ResetPerfMonitor;
+ functions->IsPerfMonitorResultAvailable = st_IsPerfMonitorResultAvailable;
+ functions->GetPerfMonitorResult = st_GetPerfMonitorResult;
+}
diff --git a/mesalib/src/mesa/state_tracker/st_cb_perfmon.h b/mesalib/src/mesa/state_tracker/st_cb_perfmon.h
new file mode 100644
index 000000000..13d3627de
--- /dev/null
+++ b/mesalib/src/mesa/state_tracker/st_cb_perfmon.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2013 Christoph Bumiller
+ * Copyright (C) 2015 Samuel Pitoiset
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef ST_CB_PERFMON_H
+#define ST_CB_PERFMON_H
+
+#include "util/list.h"
+
+/**
+ * Subclass of gl_perf_monitor_object
+ */
+struct st_perf_monitor_object
+{
+ struct gl_perf_monitor_object base;
+ struct list_head active_counters;
+};
+
+struct st_perf_counter_object
+{
+ struct list_head list;
+ struct pipe_query *query;
+ int id;
+ int group_id;
+};
+
+/**
+ * Cast wrapper
+ */
+static INLINE struct st_perf_monitor_object *
+st_perf_monitor_object(struct gl_perf_monitor_object *q)
+{
+ return (struct st_perf_monitor_object *)q;
+}
+
+bool
+st_init_perfmon(struct st_context *st);
+
+void
+st_destroy_perfmon(struct st_context *st);
+
+extern void
+st_init_perfmon_functions(struct dd_function_table *functions);
+
+#endif
diff --git a/mesalib/src/mesa/state_tracker/st_context.c b/mesalib/src/mesa/state_tracker/st_context.c
index 5fe132ac2..69e0f929d 100644
--- a/mesalib/src/mesa/state_tracker/st_context.c
+++ b/mesalib/src/mesa/state_tracker/st_context.c
@@ -51,6 +51,7 @@
#include "st_cb_fbo.h"
#include "st_cb_feedback.h"
#include "st_cb_msaa.h"
+#include "st_cb_perfmon.h"
#include "st_cb_program.h"
#include "st_cb_queryobj.h"
#include "st_cb_readpixels.h"
@@ -116,6 +117,7 @@ st_destroy_context_priv(struct st_context *st)
st_destroy_bitmap(st);
st_destroy_drawpix(st);
st_destroy_drawtex(st);
+ st_destroy_perfmon(st);
for (shader = 0; shader < ARRAY_SIZE(st->state.sampler_views); shader++) {
for (i = 0; i < ARRAY_SIZE(st->state.sampler_views[0]); i++) {
@@ -250,6 +252,12 @@ st_create_context_priv( struct gl_context *ctx, struct pipe_context *pipe,
st_init_extensions(st->pipe->screen, &ctx->Const,
&ctx->Extensions, &st->options, ctx->Mesa_DXTn);
+ if (st_init_perfmon(st)) {
+ /* GL_AMD_performance_monitor is only enabled when the underlying
+ * driver expose GPU hardware performance counters. */
+ ctx->Extensions.AMD_performance_monitor = GL_TRUE;
+ }
+
/* Enable shader-based fallbacks for ARB_color_buffer_float if needed. */
if (screen->get_param(screen, PIPE_CAP_VERTEX_COLOR_UNCLAMPED)) {
if (!screen->get_param(screen, PIPE_CAP_VERTEX_COLOR_CLAMPED)) {
@@ -313,7 +321,7 @@ struct st_context *st_create_context(gl_api api, struct pipe_context *pipe,
struct st_context *st;
memset(&funcs, 0, sizeof(funcs));
- st_init_driver_functions(&funcs);
+ st_init_driver_functions(pipe->screen, &funcs);
ctx = _mesa_create_context(api, visual, shareCtx, &funcs);
if (!ctx) {
@@ -393,7 +401,8 @@ void st_destroy_context( struct st_context *st )
}
-void st_init_driver_functions(struct dd_function_table *functions)
+void st_init_driver_functions(struct pipe_screen *screen,
+ struct dd_function_table *functions)
{
_mesa_init_shader_object_functions(functions);
_mesa_init_sampler_object_functions(functions);
@@ -414,13 +423,14 @@ void st_init_driver_functions(struct dd_function_table *functions)
st_init_fbo_functions(functions);
st_init_feedback_functions(functions);
st_init_msaa_functions(functions);
+ st_init_perfmon_functions(functions);
st_init_program_functions(functions);
st_init_query_functions(functions);
st_init_cond_render_functions(functions);
st_init_readpixels_functions(functions);
st_init_texture_functions(functions);
st_init_texture_barrier_functions(functions);
- st_init_flush_functions(functions);
+ st_init_flush_functions(screen, functions);
st_init_string_functions(functions);
st_init_viewport_functions(functions);
diff --git a/mesalib/src/mesa/state_tracker/st_context.h b/mesalib/src/mesa/state_tracker/st_context.h
index 8a9504bb7..dac5a4b90 100644
--- a/mesalib/src/mesa/state_tracker/st_context.h
+++ b/mesalib/src/mesa/state_tracker/st_context.h
@@ -237,7 +237,8 @@ struct st_framebuffer
};
-extern void st_init_driver_functions(struct dd_function_table *functions);
+extern void st_init_driver_functions(struct pipe_screen *screen,
+ struct dd_function_table *functions);
void st_invalidate_state(struct gl_context * ctx, GLuint new_state);
diff --git a/mesalib/src/mesa/state_tracker/st_extensions.c b/mesalib/src/mesa/state_tracker/st_extensions.c
index 82e4a3009..23a45883d 100644
--- a/mesalib/src/mesa/state_tracker/st_extensions.c
+++ b/mesalib/src/mesa/state_tracker/st_extensions.c
@@ -650,6 +650,12 @@ void st_init_extensions(struct pipe_screen *screen,
ARRAY_SIZE(vertex_mapping), PIPE_BUFFER,
PIPE_BIND_VERTEX_BUFFER);
+ /* ARB_direct_state_access requires OpenGL 2.0. Assume that all drivers
+ * that support NPOT textures are able to support GL 2.0.
+ */
+ if (extensions->ARB_texture_non_power_of_two)
+ extensions->ARB_direct_state_access = GL_TRUE;
+
if (extensions->ARB_stencil_texturing)
extensions->ARB_texture_stencil8 = GL_TRUE;
@@ -909,6 +915,8 @@ void st_init_extensions(struct pipe_screen *screen,
if (screen->get_shader_param(screen, PIPE_SHADER_VERTEX,
PIPE_SHADER_CAP_DOUBLES) &&
screen->get_shader_param(screen, PIPE_SHADER_FRAGMENT,
- PIPE_SHADER_CAP_DOUBLES))
+ PIPE_SHADER_CAP_DOUBLES)) {
extensions->ARB_gpu_shader_fp64 = GL_TRUE;
+ extensions->ARB_vertex_attrib_64bit = GL_TRUE;
+ }
}
diff --git a/mesalib/src/mesa/state_tracker/st_format.c b/mesalib/src/mesa/state_tracker/st_format.c
index 181465dd8..db7b5b71d 100644
--- a/mesalib/src/mesa/state_tracker/st_format.c
+++ b/mesalib/src/mesa/state_tracker/st_format.c
@@ -991,7 +991,7 @@ static const struct format_mapping format_map[] = {
{
{ GL_RGB10, 0 },
{ PIPE_FORMAT_B10G10R10X2_UNORM, PIPE_FORMAT_B10G10R10A2_UNORM,
- DEFAULT_RGB_FORMATS }
+ PIPE_FORMAT_R10G10B10A2_UNORM, DEFAULT_RGB_FORMATS }
},
{
{ GL_RGB10_A2, 0 },
diff --git a/mesalib/src/mesa/state_tracker/st_glsl_to_tgsi.cpp b/mesalib/src/mesa/state_tracker/st_glsl_to_tgsi.cpp
index 93671ba9c..f0f2a77d0 100644
--- a/mesalib/src/mesa/state_tracker/st_glsl_to_tgsi.cpp
+++ b/mesalib/src/mesa/state_tracker/st_glsl_to_tgsi.cpp
@@ -88,6 +88,7 @@ public:
this->reladdr = NULL;
this->reladdr2 = NULL;
this->has_index2 = false;
+ this->double_reg2 = false;
}
st_src_reg(gl_register_file file, int index, int type)
@@ -101,6 +102,7 @@ public:
this->reladdr = NULL;
this->reladdr2 = NULL;
this->has_index2 = false;
+ this->double_reg2 = false;
}
st_src_reg(gl_register_file file, int index, int type, int index2D)
@@ -114,6 +116,7 @@ public:
this->reladdr = NULL;
this->reladdr2 = NULL;
this->has_index2 = false;
+ this->double_reg2 = false;
}
st_src_reg()
@@ -127,6 +130,7 @@ public:
this->reladdr = NULL;
this->reladdr2 = NULL;
this->has_index2 = false;
+ this->double_reg2 = false;
}
explicit st_src_reg(st_dst_reg reg);
@@ -141,6 +145,11 @@ public:
st_src_reg *reladdr;
st_src_reg *reladdr2;
bool has_index2;
+ /*
+ * Is this the second half of a double register pair?
+ * currently used for input mapping only.
+ */
+ bool double_reg2;
};
class st_dst_reg {
@@ -197,6 +206,7 @@ st_src_reg::st_src_reg(st_dst_reg reg)
this->index2D = 0;
this->reladdr2 = NULL;
this->has_index2 = false;
+ this->double_reg2 = false;
}
st_dst_reg::st_dst_reg(st_src_reg reg)
@@ -677,8 +687,10 @@ glsl_to_tgsi_visitor::emit(ir_instruction *ir, unsigned op,
if (dinst->src[j].type == GLSL_TYPE_DOUBLE) {
dinst->src[j].index = initial_src_idx[j];
- if (swz > 1)
+ if (swz > 1) {
+ dinst->src[j].double_reg2 = true;
dinst->src[j].index++;
+ }
if (swz & 1)
dinst->src[j].swizzle = MAKE_SWIZZLE4(SWIZZLE_Z, SWIZZLE_W, SWIZZLE_Z, SWIZZLE_W);
@@ -1941,7 +1953,7 @@ glsl_to_tgsi_visitor::visit(ir_expression *ir)
break;
case ir_unop_i2b:
if (native_integers)
- emit(ir, TGSI_OPCODE_INEG, result_dst, op[0]);
+ emit(ir, TGSI_OPCODE_USNE, result_dst, op[0], st_src_reg_for_int(0));
else
emit(ir, TGSI_OPCODE_SNE, result_dst, op[0], st_src_reg_for_float(0.0));
break;
@@ -2611,6 +2623,7 @@ glsl_to_tgsi_visitor::visit(ir_assignment *ir)
assert(!ir->lhs->type->is_scalar() && !ir->lhs->type->is_vector());
l.writemask = WRITEMASK_XYZW;
} else if (ir->lhs->type->is_scalar() &&
+ !ir->lhs->type->is_double() &&
ir->lhs->variable_referenced()->data.mode == ir_var_shader_out) {
/* FINISHME: This hack makes writing to gl_FragDepth, which lives in the
* FINISHME: W component of fragment shader output zero, work correctly.
@@ -3704,6 +3717,7 @@ glsl_to_tgsi_visitor::copy_propagate(void)
} else {
if (first->src[0].file != copy_chan->src[0].file ||
first->src[0].index != copy_chan->src[0].index ||
+ first->src[0].double_reg2 != copy_chan->src[0].double_reg2 ||
first->src[0].index2D != copy_chan->src[0].index2D) {
good = false;
break;
@@ -3719,6 +3733,7 @@ glsl_to_tgsi_visitor::copy_propagate(void)
inst->src[r].index = first->src[0].index;
inst->src[r].index2D = first->src[0].index2D;
inst->src[r].has_index2 = first->src[0].has_index2;
+ inst->src[r].double_reg2 = first->src[0].double_reg2;
int swizzle = 0;
for (int i = 0; i < 4; i++) {
@@ -4551,6 +4566,9 @@ dst_register(struct st_translate *t,
static struct ureg_src
src_register(struct st_translate *t, const st_src_reg *reg)
{
+ int index = reg->index;
+ int double_reg2 = reg->double_reg2 ? 1 : 0;
+
switch(reg->file) {
case PROGRAM_UNDEFINED:
return ureg_imm4f(t->ureg, 0, 0, 0, 0);
@@ -4576,8 +4594,12 @@ src_register(struct st_translate *t, const st_src_reg *reg)
return t->immediates[reg->index];
case PROGRAM_INPUT:
- assert(t->inputMapping[reg->index] < ARRAY_SIZE(t->inputs));
- return t->inputs[t->inputMapping[reg->index]];
+ /* GLSL inputs are 64-bit containers, so we have to
+ * map back to the original index and add the offset after
+ * mapping. */
+ index -= double_reg2;
+ assert(t->inputMapping[index] < ARRAY_SIZE(t->inputs));
+ return t->inputs[t->inputMapping[index] + double_reg2];
case PROGRAM_OUTPUT:
assert(t->outputMapping[reg->index] < ARRAY_SIZE(t->outputs));
diff --git a/mesalib/src/mesa/state_tracker/st_manager.c b/mesalib/src/mesa/state_tracker/st_manager.c
index 840f76a13..0376954f7 100644
--- a/mesalib/src/mesa/state_tracker/st_manager.c
+++ b/mesalib/src/mesa/state_tracker/st_manager.c
@@ -680,6 +680,10 @@ st_api_create_context(struct st_api *stapi, struct st_manager *smapi,
if (attribs->flags & ST_CONTEXT_FLAG_FORWARD_COMPATIBLE)
st->ctx->Const.ContextFlags |= GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT;
+ if (attribs->flags & ST_CONTEXT_FLAG_ROBUST_ACCESS)
+ st->ctx->Const.ContextFlags |= GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB;
+ if (attribs->flags & ST_CONTEXT_FLAG_RESET_NOTIFICATION_ENABLED)
+ st->ctx->Const.ResetStrategy = GL_LOSE_CONTEXT_ON_RESET_ARB;
/* need to perform version check */
if (attribs->major > 1 || attribs->minor > 0) {
diff --git a/mesalib/src/mesa/state_tracker/st_program.c b/mesalib/src/mesa/state_tracker/st_program.c
index d93b3c7bc..a9110d3c6 100644
--- a/mesalib/src/mesa/state_tracker/st_program.c
+++ b/mesalib/src/mesa/state_tracker/st_program.c
@@ -194,6 +194,11 @@ st_prepare_vertex_program(struct gl_context *ctx,
stvp->input_to_index[attr] = stvp->num_inputs;
stvp->index_to_input[stvp->num_inputs] = attr;
stvp->num_inputs++;
+ if ((stvp->Base.Base.DoubleInputsRead & BITFIELD64_BIT(attr)) != 0) {
+ /* add placeholder for second part of a double attribute */
+ stvp->index_to_input[stvp->num_inputs] = ST_DOUBLE_ATTRIB_PLACEHOLDER;
+ stvp->num_inputs++;
+ }
}
}
/* bit of a hack, presetup potentially unused edgeflag input */
diff --git a/mesalib/src/mesa/state_tracker/st_program.h b/mesalib/src/mesa/state_tracker/st_program.h
index b2c86faec..a2c56062d 100644
--- a/mesalib/src/mesa/state_tracker/st_program.h
+++ b/mesalib/src/mesa/state_tracker/st_program.h
@@ -45,6 +45,7 @@
extern "C" {
#endif
+#define ST_DOUBLE_ATTRIB_PLACEHOLDER 0xffffffff
/** Fragment program variant key */
struct st_fp_variant_key