mesa: Allow setting GL_TEXTURE_MAX_LEVEL to 0 with GL_TEXTURE_RECTANGLE.
[mesa.git] / src / mesa / main / dlist.c
index 786ab319cb199c1a2025ee9a48960deda04813b5..d431fd2216f1d063e98573a1d7f616234c11bdee 100644 (file)
@@ -470,6 +470,10 @@ typedef enum
  * Each instruction in the display list is stored as a sequence of
  * contiguous nodes in memory.
  * Each node is the union of a variety of data types.
+ *
+ * Note, all of these members should be 4 bytes in size or less for the
+ * sake of compact display lists.  We store 8-byte pointers in a pair of
+ * these nodes using the save/get_pointer() functions below.
  */
 union gl_dlist_node
 {
@@ -484,7 +488,6 @@ union gl_dlist_node
    GLenum e;
    GLfloat f;
    GLsizei si;
-   void *next;                  /* If prev node's opcode==OPCODE_CONTINUE */
 };
 
 
@@ -515,9 +518,7 @@ save_pointer(union gl_dlist_node *dest, void *src)
    unsigned i;
 
    STATIC_ASSERT(POINTER_DWORDS == 1 || POINTER_DWORDS == 2);
-   /* XXX enable this when work is done:
    STATIC_ASSERT(sizeof(union gl_dlist_node) == 4);
-   */
 
    p.ptr = src;
 
@@ -554,9 +555,9 @@ union uint64_pair
 
 
 /**
- * How many nodes to allocate at a time.
- *
- * \note Reduced now that we hold vertices etc. elsewhere.
+ * How many nodes to allocate at a time.  Note that bulk vertex data
+ * from glBegin/glVertex/glEnd primitives will typically wind up in
+ * a VBO, and not directly in the display list itself.
  */
 #define BLOCK_SIZE 256
 
@@ -572,14 +573,9 @@ static GLuint InstSize[OPCODE_END_OF_LIST + 1];
 void mesa_print_display_list(GLuint list);
 
 
-/**********************************************************************/
-/*****                           Private                          *****/
-/**********************************************************************/
-
-
 /**
- * Make an empty display list.  This is used by glGenLists() to
- * reserve display list IDs.
+ * Allocate a gl_display_list object with an initial block of storage.
+ * \param count  how many display list nodes/tokes to allocate
  */
 static struct gl_display_list *
 make_list(GLuint name, GLuint count)
@@ -771,10 +767,11 @@ _mesa_delete_list(struct gl_context *ctx, struct gl_display_list *dlist)
             break;
          case OPCODE_PIXEL_MAP:
             free(get_pointer(&n[3]));
+            n += InstSize[n[0].opcode];
             break;
 
          case OPCODE_CONTINUE:
-            n = (Node *) n[1].next;
+            n = (Node *) get_pointer(&n[1]);
             free(block);
             block = n;
             break;
@@ -873,12 +870,6 @@ translate_id(GLsizei n, GLenum type, const GLvoid * list)
 }
 
 
-
-
-/**********************************************************************/
-/*****                        Public                              *****/
-/**********************************************************************/
-
 /**
  * Wrapper for _mesa_unpack_image/bitmap() that handles pixel buffer objects.
  * If width < 0 or height < 0 or format or type are invalid we'll just
@@ -923,7 +914,8 @@ unpack_image(struct gl_context *ctx, GLuint dimensions,
 
       map = (GLubyte *)
          ctx->Driver.MapBufferRange(ctx, 0, unpack->BufferObj->Size,
-                                   GL_MAP_READ_BIT, unpack->BufferObj);
+                                   GL_MAP_READ_BIT, unpack->BufferObj,
+                                    MAP_INTERNAL);
       if (!map) {
          /* unable to map src buffer! */
          _mesa_error(ctx, GL_INVALID_OPERATION, "unable to map PBO");
@@ -937,7 +929,7 @@ unpack_image(struct gl_context *ctx, GLuint dimensions,
          image = _mesa_unpack_image(dimensions, width, height, depth,
                                     format, type, src, unpack);
 
-      ctx->Driver.UnmapBuffer(ctx, unpack->BufferObj);
+      ctx->Driver.UnmapBuffer(ctx, unpack->BufferObj, MAP_INTERNAL);
 
       if (!image) {
          _mesa_error(ctx, GL_OUT_OF_MEMORY, "display list construction");
@@ -972,6 +964,7 @@ static Node *
 dlist_alloc(struct gl_context *ctx, OpCode opcode, GLuint bytes)
 {
    const GLuint numNodes = 1 + (bytes + sizeof(Node) - 1) / sizeof(Node);
+   const GLuint contNodes = 1 + POINTER_DWORDS;  /* size of continue info */
    Node *n;
 
    if (opcode < (GLuint) OPCODE_EXT_0) {
@@ -985,7 +978,7 @@ dlist_alloc(struct gl_context *ctx, OpCode opcode, GLuint bytes)
       }
    }
 
-   if (ctx->ListState.CurrentPos + numNodes + 2 > BLOCK_SIZE) {
+   if (ctx->ListState.CurrentPos + numNodes + contNodes > BLOCK_SIZE) {
       /* This block is full.  Allocate a new block and chain to it */
       Node *newblock;
       n = ctx->ListState.CurrentBlock + ctx->ListState.CurrentPos;
@@ -995,7 +988,7 @@ dlist_alloc(struct gl_context *ctx, OpCode opcode, GLuint bytes)
          _mesa_error(ctx, GL_OUT_OF_MEMORY, "Building display list");
          return NULL;
       }
-      n[1].next = (Node *) newblock;
+      save_pointer(&n[1], newblock);
       ctx->ListState.CurrentBlock = newblock;
       ctx->ListState.CurrentPos = 0;
    }
@@ -1076,6 +1069,37 @@ alloc_instruction(struct gl_context *ctx, OpCode opcode, GLuint nparams)
 }
 
 
+/**
+ * Called by EndList to try to reduce memory used for the list.
+ */
+static void
+trim_list(struct gl_context *ctx)
+{
+   /* If the list we're ending only has one allocated block of nodes/tokens
+    * and its size isn't a full block size, realloc the block to use less
+    * memory.  This is important for apps that create many small display
+    * lists and apps that use glXUseXFont (many lists each containing one
+    * glBitmap call).
+    * Note: we currently only trim display lists that allocated one block
+    * of tokens.  That hits the short list case which is what we're mainly
+    * concerned with.  Trimming longer lists would involve traversing the
+    * linked list of blocks.
+    */
+   struct gl_dlist_state *list = &ctx->ListState;
+
+   if ((list->CurrentList->Head == list->CurrentBlock) &&
+       (list->CurrentPos < BLOCK_SIZE)) {
+      /* There's only one block and it's not full, so realloc */
+      GLuint newSize = list->CurrentPos * sizeof(Node);
+      list->CurrentList->Head =
+      list->CurrentBlock = realloc(list->CurrentBlock, newSize);
+      if (!list->CurrentBlock) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glEndList");
+      }
+   }
+}
+
+
 
 /*
  * Display List compilation functions
@@ -8062,7 +8086,7 @@ execute_list(struct gl_context *ctx, GLuint list)
             break;
 
          case OPCODE_CONTINUE:
-            n = (Node *) n[1].next;
+            n = (Node *) get_pointer(&n[1]);
             break;
          case OPCODE_END_OF_LIST:
             done = GL_TRUE;
@@ -8153,7 +8177,7 @@ _mesa_GenLists(GLsizei range)
    /*
     * Make this an atomic operation
     */
-   _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
+   mtx_lock(&ctx->Shared->Mutex);
 
    base = _mesa_HashFindFreeKeyBlock(ctx->Shared->DisplayList, range);
    if (base) {
@@ -8165,7 +8189,7 @@ _mesa_GenLists(GLsizei range)
       }
    }
 
-   _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
+   mtx_unlock(&ctx->Shared->Mutex);
 
    return base;
 }
@@ -8251,6 +8275,8 @@ _mesa_EndList(void)
 
    (void) alloc_instruction(ctx, OPCODE_END_OF_LIST, 0);
 
+   trim_list(ctx);
+
    /* Destroy old list, if any */
    destroy_list(ctx, ctx->ListState.CurrentList->Name);
 
@@ -8264,6 +8290,8 @@ _mesa_EndList(void)
       mesa_print_display_list(ctx->ListState.CurrentList->Name);
 
    ctx->ListState.CurrentList = NULL;
+   ctx->ListState.CurrentBlock = NULL;
+   ctx->ListState.CurrentPos = 0;
    ctx->ExecuteFlag = GL_TRUE;
    ctx->CompileFlag = GL_FALSE;
 
@@ -9066,7 +9094,7 @@ print_list(struct gl_context *ctx, GLuint list)
             break;
          case OPCODE_CONTINUE:
             printf("DISPLAY-LIST-CONTINUE\n");
-            n = (Node *) n[1].next;
+            n = (Node *) get_pointer(&n[1]);
             break;
          case OPCODE_END_OF_LIST:
             printf("END-LIST %u\n", list);