blob: e54f21be6009c7a1051127df9aeb68f1843c7a61 [file] [log] [blame]
/**************************************************************************
*
* Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
* 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 implements the st_draw_vbo() function which is called from
* Mesa's VBO module. All point/line/triangle rendering is done through
* this function whether the user called glBegin/End, glDrawArrays,
* glDrawElements, glEvalMesh, or glCalList, etc.
*
* We basically convert the VBO's vertex attribute/array information into
* Gallium vertex state, bind the vertex buffer objects and call
* pipe->draw_elements(), pipe->draw_range_elements() or pipe->draw_arrays().
*
* Authors:
* Keith Whitwell <keith@tungstengraphics.com>
*/
#include "main/imports.h"
#include "main/image.h"
#include "main/macros.h"
#include "shader/prog_uniform.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 "pipe/p_context.h"
#include "pipe/p_defines.h"
#include "pipe/p_inlines.h"
static GLuint double_types[4] = {
PIPE_FORMAT_R64_FLOAT,
PIPE_FORMAT_R64G64_FLOAT,
PIPE_FORMAT_R64G64B64_FLOAT,
PIPE_FORMAT_R64G64B64A64_FLOAT
};
static GLuint float_types[4] = {
PIPE_FORMAT_R32_FLOAT,
PIPE_FORMAT_R32G32_FLOAT,
PIPE_FORMAT_R32G32B32_FLOAT,
PIPE_FORMAT_R32G32B32A32_FLOAT
};
static GLuint uint_types_norm[4] = {
PIPE_FORMAT_R32_UNORM,
PIPE_FORMAT_R32G32_UNORM,
PIPE_FORMAT_R32G32B32_UNORM,
PIPE_FORMAT_R32G32B32A32_UNORM
};
static GLuint uint_types_scale[4] = {
PIPE_FORMAT_R32_USCALED,
PIPE_FORMAT_R32G32_USCALED,
PIPE_FORMAT_R32G32B32_USCALED,
PIPE_FORMAT_R32G32B32A32_USCALED
};
static GLuint int_types_norm[4] = {
PIPE_FORMAT_R32_SNORM,
PIPE_FORMAT_R32G32_SNORM,
PIPE_FORMAT_R32G32B32_SNORM,
PIPE_FORMAT_R32G32B32A32_SNORM
};
static GLuint int_types_scale[4] = {
PIPE_FORMAT_R32_SSCALED,
PIPE_FORMAT_R32G32_SSCALED,
PIPE_FORMAT_R32G32B32_SSCALED,
PIPE_FORMAT_R32G32B32A32_SSCALED
};
static GLuint ushort_types_norm[4] = {
PIPE_FORMAT_R16_UNORM,
PIPE_FORMAT_R16G16_UNORM,
PIPE_FORMAT_R16G16B16_UNORM,
PIPE_FORMAT_R16G16B16A16_UNORM
};
static GLuint ushort_types_scale[4] = {
PIPE_FORMAT_R16_USCALED,
PIPE_FORMAT_R16G16_USCALED,
PIPE_FORMAT_R16G16B16_USCALED,
PIPE_FORMAT_R16G16B16A16_USCALED
};
static GLuint short_types_norm[4] = {
PIPE_FORMAT_R16_SNORM,
PIPE_FORMAT_R16G16_SNORM,
PIPE_FORMAT_R16G16B16_SNORM,
PIPE_FORMAT_R16G16B16A16_SNORM
};
static GLuint short_types_scale[4] = {
PIPE_FORMAT_R16_SSCALED,
PIPE_FORMAT_R16G16_SSCALED,
PIPE_FORMAT_R16G16B16_SSCALED,
PIPE_FORMAT_R16G16B16A16_SSCALED
};
static GLuint ubyte_types_norm[4] = {
PIPE_FORMAT_R8_UNORM,
PIPE_FORMAT_R8G8_UNORM,
PIPE_FORMAT_R8G8B8_UNORM,
PIPE_FORMAT_R8G8B8A8_UNORM
};
static GLuint ubyte_types_scale[4] = {
PIPE_FORMAT_R8_USCALED,
PIPE_FORMAT_R8G8_USCALED,
PIPE_FORMAT_R8G8B8_USCALED,
PIPE_FORMAT_R8G8B8A8_USCALED
};
static GLuint byte_types_norm[4] = {
PIPE_FORMAT_R8_SNORM,
PIPE_FORMAT_R8G8_SNORM,
PIPE_FORMAT_R8G8B8_SNORM,
PIPE_FORMAT_R8G8B8A8_SNORM
};
static GLuint byte_types_scale[4] = {
PIPE_FORMAT_R8_SSCALED,
PIPE_FORMAT_R8G8_SSCALED,
PIPE_FORMAT_R8G8B8_SSCALED,
PIPE_FORMAT_R8G8B8A8_SSCALED
};
static GLuint fixed_types[4] = {
PIPE_FORMAT_R32_FIXED,
PIPE_FORMAT_R32G32_FIXED,
PIPE_FORMAT_R32G32B32_FIXED,
PIPE_FORMAT_R32G32B32A32_FIXED
};
/**
* Return a PIPE_FORMAT_x for the given GL datatype and size.
*/
GLuint
st_pipe_vertex_format(GLenum type, GLuint size, GLenum format,
GLboolean normalized)
{
assert((type >= GL_BYTE && type <= GL_DOUBLE) ||
type == GL_FIXED);
assert(size >= 1);
assert(size <= 4);
assert(format == GL_RGBA || format == GL_BGRA);
if (format == GL_BGRA) {
/* this is an odd-ball case */
assert(type == GL_UNSIGNED_BYTE);
assert(normalized);
return PIPE_FORMAT_B8G8R8A8_UNORM;
}
if (normalized) {
switch (type) {
case GL_DOUBLE: return double_types[size-1];
case GL_FLOAT: return float_types[size-1];
case GL_INT: return int_types_norm[size-1];
case GL_SHORT: return short_types_norm[size-1];
case GL_BYTE: return byte_types_norm[size-1];
case GL_UNSIGNED_INT: return uint_types_norm[size-1];
case GL_UNSIGNED_SHORT: return ushort_types_norm[size-1];
case GL_UNSIGNED_BYTE: return ubyte_types_norm[size-1];
case GL_FIXED: return fixed_types[size-1];
default: assert(0); return 0;
}
}
else {
switch (type) {
case GL_DOUBLE: return double_types[size-1];
case GL_FLOAT: return float_types[size-1];
case GL_INT: return int_types_scale[size-1];
case GL_SHORT: return short_types_scale[size-1];
case GL_BYTE: return byte_types_scale[size-1];
case GL_UNSIGNED_INT: return uint_types_scale[size-1];
case GL_UNSIGNED_SHORT: return ushort_types_scale[size-1];
case GL_UNSIGNED_BYTE: return ubyte_types_scale[size-1];
case GL_FIXED: return fixed_types[size-1];
default: assert(0); return 0;
}
}
return 0; /* silence compiler warning */
}
/**
* Examine the active arrays to determine if we have interleaved
* vertex arrays all living in one VBO, or all living in user space.
* \param userSpace returns whether the arrays are in user space.
*/
static GLboolean
is_interleaved_arrays(const struct st_vertex_program *vp,
const struct st_vp_varient *vpv,
const struct gl_client_array **arrays,
GLboolean *userSpace)
{
GLuint attr;
const struct gl_buffer_object *firstBufObj = NULL;
GLint firstStride = -1;
GLuint num_client_arrays = 0;
const GLubyte *client_addr = NULL;
for (attr = 0; attr < vpv->num_inputs; attr++) {
const GLuint mesaAttr = vp->index_to_input[attr];
const struct gl_buffer_object *bufObj = arrays[mesaAttr]->BufferObj;
const GLsizei stride = arrays[mesaAttr]->StrideB; /* in bytes */
if (firstStride < 0) {
firstStride = stride;
}
else if (firstStride != stride) {
return GL_FALSE;
}
if (!bufObj || !bufObj->Name) {
num_client_arrays++;
/* Try to detect if the client-space arrays are
* "close" to each other.
*/
if (!client_addr) {
client_addr = arrays[mesaAttr]->Ptr;
}
else if (abs(arrays[mesaAttr]->Ptr - client_addr) > firstStride) {
/* arrays start too far apart */
return GL_FALSE;
}
}
else if (!firstBufObj) {
firstBufObj = bufObj;
}
else if (bufObj != firstBufObj) {
return GL_FALSE;
}
}
*userSpace = (num_client_arrays == vpv->num_inputs);
/* printf("user space: %d (%d %d)\n", (int) *userSpace,num_client_arrays,vp->num_inputs); */
return GL_TRUE;
}
/**
* Compute the memory range occupied by the arrays.
*/
static void
get_arrays_bounds(const struct st_vertex_program *vp,
const struct st_vp_varient *vpv,
const struct gl_client_array **arrays,
GLuint max_index,
const GLubyte **low, const GLubyte **high)
{
const GLubyte *low_addr = NULL;
const GLubyte *high_addr = NULL;
GLuint attr;
for (attr = 0; attr < vpv->num_inputs; attr++) {
const GLuint mesaAttr = vp->index_to_input[attr];
const GLint stride = arrays[mesaAttr]->StrideB;
const GLubyte *start = arrays[mesaAttr]->Ptr;
const unsigned sz = (arrays[mesaAttr]->Size *
_mesa_sizeof_type(arrays[mesaAttr]->Type));
const GLubyte *end = start + (max_index * stride) + sz;
if (attr == 0) {
low_addr = start;
high_addr = end;
}
else {
low_addr = MIN2(low_addr, start);
high_addr = MAX2(high_addr, end);
}
}
*low = low_addr;
*high = high_addr;
}
/**
* Set up for drawing interleaved arrays that all live in one VBO
* or all live in user space.
* \param vbuffer returns vertex buffer info
* \param velements returns vertex element info
*/
static void
setup_interleaved_attribs(GLcontext *ctx,
const struct st_vertex_program *vp,
const struct st_vp_varient *vpv,
const struct gl_client_array **arrays,
GLuint max_index,
GLboolean userSpace,
struct pipe_vertex_buffer *vbuffer,
struct pipe_vertex_element velements[])
{
struct pipe_context *pipe = ctx->st->pipe;
GLuint attr;
const GLubyte *offset0 = NULL;
for (attr = 0; attr < vpv->num_inputs; attr++) {
const GLuint mesaAttr = vp->index_to_input[attr];
struct gl_buffer_object *bufobj = arrays[mesaAttr]->BufferObj;
struct st_buffer_object *stobj = st_buffer_object(bufobj);
GLsizei stride = arrays[mesaAttr]->StrideB;
/*printf("stobj %u = %p\n", attr, (void*)stobj);*/
if (attr == 0) {
const GLubyte *low, *high;
get_arrays_bounds(vp, vpv, arrays, max_index, &low, &high);
/*printf("buffer range: %p %p %d\n", low, high, high-low);*/
offset0 = low;
if (userSpace) {
vbuffer->buffer =
pipe_user_buffer_create(pipe->screen, (void *) low, high - low);
vbuffer->buffer_offset = 0;
}
else {
vbuffer->buffer = NULL;
pipe_buffer_reference(&vbuffer->buffer, stobj->buffer);
vbuffer->buffer_offset = pointer_to_offset(low);
}
vbuffer->stride = stride; /* in bytes */
vbuffer->max_index = max_index;
}
velements[attr].src_offset =
(unsigned) (arrays[mesaAttr]->Ptr - offset0);
velements[attr].vertex_buffer_index = 0;
velements[attr].nr_components = arrays[mesaAttr]->Size;
velements[attr].src_format =
st_pipe_vertex_format(arrays[mesaAttr]->Type,
arrays[mesaAttr]->Size,
arrays[mesaAttr]->Format,
arrays[mesaAttr]->Normalized);
assert(velements[attr].src_format);
}
}
/**
* Set up a separate pipe_vertex_buffer and pipe_vertex_element for each
* vertex attribute.
* \param vbuffer returns vertex buffer info
* \param velements returns vertex element info
*/
static void
setup_non_interleaved_attribs(GLcontext *ctx,
const struct st_vertex_program *vp,
const struct st_vp_varient *vpv,
const struct gl_client_array **arrays,
GLuint max_index,
GLboolean *userSpace,
struct pipe_vertex_buffer vbuffer[],
struct pipe_vertex_element velements[])
{
struct pipe_context *pipe = ctx->st->pipe;
GLuint attr;
for (attr = 0; attr < vpv->num_inputs; attr++) {
const GLuint mesaAttr = vp->index_to_input[attr];
struct gl_buffer_object *bufobj = arrays[mesaAttr]->BufferObj;
GLsizei stride = arrays[mesaAttr]->StrideB;
*userSpace = GL_FALSE;
if (bufobj && bufobj->Name) {
/* Attribute data is in a VBO.
* Recall that for VBOs, the gl_client_array->Ptr field is
* really an offset from the start of the VBO, not a pointer.
*/
struct st_buffer_object *stobj = st_buffer_object(bufobj);
assert(stobj->buffer);
/*printf("stobj %u = %p\n", attr, (void*) stobj);*/
vbuffer[attr].buffer = NULL;
pipe_buffer_reference(&vbuffer[attr].buffer, stobj->buffer);
vbuffer[attr].buffer_offset = pointer_to_offset(arrays[mesaAttr]->Ptr);
velements[attr].src_offset = 0;
}
else {
/* attribute data is in user-space memory, not a VBO */
uint bytes;
/*printf("user-space array %d stride %d\n", attr, stride);*/
*userSpace = GL_TRUE;
/* wrap user data */
if (arrays[mesaAttr]->Ptr) {
/* user's vertex array */
if (arrays[mesaAttr]->StrideB) {
bytes = arrays[mesaAttr]->StrideB * (max_index + 1);
}
else {
bytes = arrays[mesaAttr]->Size
* _mesa_sizeof_type(arrays[mesaAttr]->Type);
}
vbuffer[attr].buffer = pipe_user_buffer_create(pipe->screen,
(void *) arrays[mesaAttr]->Ptr, bytes);
}
else {
/* no array, use ctx->Current.Attrib[] value */
bytes = sizeof(ctx->Current.Attrib[0]);
vbuffer[attr].buffer = pipe_user_buffer_create(pipe->screen,
(void *) ctx->Current.Attrib[mesaAttr], bytes);
stride = 0;
}
vbuffer[attr].buffer_offset = 0;
velements[attr].src_offset = 0;
}
assert(velements[attr].src_offset <= 2048); /* 11-bit field */
/* common-case setup */
vbuffer[attr].stride = stride; /* in bytes */
vbuffer[attr].max_index = max_index;
velements[attr].vertex_buffer_index = attr;
velements[attr].nr_components = arrays[mesaAttr]->Size;
velements[attr].src_format
= st_pipe_vertex_format(arrays[mesaAttr]->Type,
arrays[mesaAttr]->Size,
arrays[mesaAttr]->Format,
arrays[mesaAttr]->Normalized);
assert(velements[attr].src_format);
}
}
/**
* Prior to drawing, check that any uniforms referenced by the
* current shader have been set. If a uniform has not been set,
* issue a warning.
*/
static void
check_uniforms(GLcontext *ctx)
{
const struct gl_shader_program *shProg = ctx->Shader.CurrentProgram;
if (shProg && shProg->LinkStatus) {
GLuint i;
for (i = 0; i < shProg->Uniforms->NumUniforms; i++) {
const struct gl_uniform *u = &shProg->Uniforms->Uniforms[i];
if (!u->Initialized) {
_mesa_warning(ctx,
"Using shader with uninitialized uniform: %s",
u->Name);
}
}
}
}
static unsigned translate_prim( GLcontext *ctx,
unsigned prim )
{
/* Avoid quadstrips if it's easy to do so:
*/
if (prim == GL_QUAD_STRIP &&
ctx->Light.ShadeModel != GL_FLAT &&
ctx->Polygon.FrontMode == GL_FILL &&
ctx->Polygon.BackMode == GL_FILL)
prim = GL_TRIANGLE_STRIP;
return prim;
}
/**
* This function gets plugged into the VBO module and is called when
* we have something to render.
* Basically, translate the information into the format expected by gallium.
*/
void
st_draw_vbo(GLcontext *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)
{
struct pipe_context *pipe = ctx->st->pipe;
const struct st_vertex_program *vp;
const struct st_vp_varient *vpv;
const struct pipe_shader_state *vs;
struct pipe_vertex_buffer vbuffer[PIPE_MAX_SHADER_INPUTS];
GLuint attr;
struct pipe_vertex_element velements[PIPE_MAX_ATTRIBS];
unsigned num_vbuffers, num_velements;
GLboolean userSpace = GL_FALSE;
GLboolean vertDataEdgeFlags;
/* Gallium probably doesn't want this in some cases. */
if (!index_bounds_valid)
if (!vbo_all_varyings_in_vbos(arrays))
vbo_get_minmax_index(ctx, prims, ib, &min_index, &max_index);
/* sanity check for pointer arithmetic below */
assert(sizeof(arrays[0]->Ptr[0]) == 1);
vertDataEdgeFlags = arrays[VERT_ATTRIB_EDGEFLAG]->BufferObj &&
arrays[VERT_ATTRIB_EDGEFLAG]->BufferObj->Name;
if (vertDataEdgeFlags != ctx->st->vertdata_edgeflags) {
ctx->st->vertdata_edgeflags = vertDataEdgeFlags;
ctx->st->dirty.st |= ST_NEW_EDGEFLAGS_DATA;
}
st_validate_state(ctx->st);
/* must get these after state validation! */
vp = ctx->st->vp;
vpv = ctx->st->vp_varient;
vs = &vpv->state;
#if 0
if (MESA_VERBOSE & VERBOSE_GLSL) {
check_uniforms(ctx);
}
#else
(void) check_uniforms;
#endif
/*
* Setup the vbuffer[] and velements[] arrays.
*/
if (is_interleaved_arrays(vp, vpv, arrays, &userSpace)) {
/*printf("Draw interleaved\n");*/
setup_interleaved_attribs(ctx, vp, vpv, arrays, max_index, userSpace,
vbuffer, velements);
num_vbuffers = 1;
num_velements = vpv->num_inputs;
if (num_velements == 0)
num_vbuffers = 0;
}
else {
/*printf("Draw non-interleaved\n");*/
setup_non_interleaved_attribs(ctx, vp, vpv, arrays, max_index,
&userSpace, vbuffer, velements);
num_vbuffers = vpv->num_inputs;
num_velements = vpv->num_inputs;
}
#if 0
{
GLuint i;
for (i = 0; i < num_vbuffers; i++) {
printf("buffers[%d].stride = %u\n", i, vbuffer[i].stride);
printf("buffers[%d].max_index = %u\n", i, vbuffer[i].max_index);
printf("buffers[%d].buffer_offset = %u\n", i, vbuffer[i].buffer_offset);
printf("buffers[%d].buffer = %p\n", i, (void*) vbuffer[i].buffer);
}
for (i = 0; i < num_velements; i++) {
printf("vlements[%d].vbuffer_index = %u\n", i, velements[i].vertex_buffer_index);
printf("vlements[%d].src_offset = %u\n", i, velements[i].src_offset);
printf("vlements[%d].nr_comps = %u\n", i, velements[i].nr_components);
printf("vlements[%d].format = %s\n", i, pf_name(velements[i].src_format));
}
}
#endif
pipe->set_vertex_buffers(pipe, num_vbuffers, vbuffer);
pipe->set_vertex_elements(pipe, num_velements, velements);
if (num_vbuffers == 0 || num_velements == 0)
return;
/* do actual drawing */
if (ib) {
/* indexed primitive */
struct gl_buffer_object *bufobj = ib->obj;
struct pipe_buffer *indexBuf = NULL;
unsigned indexSize, indexOffset, i;
unsigned prim;
switch (ib->type) {
case GL_UNSIGNED_INT:
indexSize = 4;
break;
case GL_UNSIGNED_SHORT:
indexSize = 2;
break;
case GL_UNSIGNED_BYTE:
indexSize = 1;
break;
default:
assert(0);
return;
}
/* get/create the index buffer object */
if (bufobj && bufobj->Name) {
/* elements/indexes are in a real VBO */
struct st_buffer_object *stobj = st_buffer_object(bufobj);
pipe_buffer_reference(&indexBuf, stobj->buffer);
indexOffset = pointer_to_offset(ib->ptr) / indexSize;
}
else {
/* element/indicies are in user space memory */
indexBuf = pipe_user_buffer_create(pipe->screen, (void *) ib->ptr,
ib->count * indexSize);
indexOffset = 0;
}
/* draw */
if (nr_prims == 1 && pipe->draw_range_elements != NULL) {
i = 0;
/* XXX: exercise temporary path to pass min/max directly
* through to driver & draw module. These interfaces still
* need a bit of work...
*/
prim = translate_prim( ctx, prims[i].mode );
pipe->draw_range_elements(pipe, indexBuf, indexSize,
min_index,
max_index,
prim,
prims[i].start + indexOffset, prims[i].count);
}
else {
for (i = 0; i < nr_prims; i++) {
prim = translate_prim( ctx, prims[i].mode );
pipe->draw_elements(pipe, indexBuf, indexSize,
prim,
prims[i].start + indexOffset, prims[i].count);
}
}
pipe_buffer_reference(&indexBuf, NULL);
}
else {
/* non-indexed */
GLuint i;
GLuint prim;
for (i = 0; i < nr_prims; i++) {
prim = translate_prim( ctx, prims[i].mode );
pipe->draw_arrays(pipe, prim, prims[i].start, prims[i].count);
}
}
/* unreference buffers (frees wrapped user-space buffer objects) */
for (attr = 0; attr < num_vbuffers; attr++) {
pipe_buffer_reference(&vbuffer[attr].buffer, NULL);
assert(!vbuffer[attr].buffer);
}
if (userSpace)
{
pipe->set_vertex_buffers(pipe, 0, NULL);
}
}
void st_init_draw( struct st_context *st )
{
GLcontext *ctx = st->ctx;
vbo_set_draw_func(ctx, st_draw_vbo);
}
void st_destroy_draw( struct st_context *st )
{
}