/* The following three are meta instructions */
OPCODE_ERROR, /* raise compiled-in error */
OPCODE_CONTINUE,
+ OPCODE_NOP, /* No-op (used for 8-byte alignment */
OPCODE_END_OF_LIST,
OPCODE_EXT_0
} OpCode;
* Allocate space for a display list instruction (opcode + payload space).
* \param opcode the instruction opcode (OPCODE_* value)
* \param bytes instruction payload size (not counting opcode)
- * \return pointer to allocated memory (the opcode space)
+ * \param align8 does the payload need to be 8-byte aligned?
+ * This is only relevant in 64-bit environments.
+ * \return pointer to allocated memory (the payload will be at pointer+1)
*/
static Node *
-dlist_alloc(struct gl_context *ctx, OpCode opcode, GLuint bytes)
+dlist_alloc(struct gl_context *ctx, OpCode opcode, GLuint bytes, bool align8)
{
const GLuint numNodes = 1 + (bytes + sizeof(Node) - 1) / sizeof(Node);
const GLuint contNodes = 1 + POINTER_DWORDS; /* size of continue info */
+ GLuint nopNode;
Node *n;
if (opcode < OPCODE_EXT_0) {
}
}
- if (ctx->ListState.CurrentPos + numNodes + contNodes > BLOCK_SIZE) {
+ if (sizeof(void *) > sizeof(Node) && align8
+ && ctx->ListState.CurrentPos % 2 == 0) {
+ /* The opcode would get placed at node[0] and the payload would start
+ * at node[1]. But the payload needs to be at an even offset (8-byte
+ * multiple).
+ */
+ nopNode = 1;
+ }
+ else {
+ nopNode = 0;
+ }
+
+ if (ctx->ListState.CurrentPos + nopNode + 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;
_mesa_error(ctx, GL_OUT_OF_MEMORY, "Building display list");
return NULL;
}
+
+ /* a fresh block should be 8-byte aligned on 64-bit systems */
+ assert(((GLintptr) newblock) % sizeof(void *) == 0);
+
save_pointer(&n[1], newblock);
ctx->ListState.CurrentBlock = newblock;
ctx->ListState.CurrentPos = 0;
+
+ /* Display list nodes are always 4 bytes. If we need 8-byte alignment
+ * we have to insert a NOP so that the payload of the real opcode lands
+ * on an even location:
+ * node[0] = OPCODE_NOP
+ * node[1] = OPCODE_x;
+ * node[2] = start of payload
+ */
+ nopNode = sizeof(void *) > sizeof(Node) && align8;
}
n = ctx->ListState.CurrentBlock + ctx->ListState.CurrentPos;
- ctx->ListState.CurrentPos += numNodes;
+ if (nopNode) {
+ assert(ctx->ListState.CurrentPos % 2 == 0); /* even value */
+ n[0].opcode = OPCODE_NOP;
+ n++;
+ /* The "real" opcode will now be at an odd location and the payload
+ * will be at an even location.
+ */
+ }
+ ctx->ListState.CurrentPos += nopNode + numNodes;
n[0].opcode = opcode;
void *
_mesa_dlist_alloc(struct gl_context *ctx, GLuint opcode, GLuint bytes)
{
- Node *n = dlist_alloc(ctx, (OpCode) opcode, bytes);
+ Node *n = dlist_alloc(ctx, (OpCode) opcode, bytes, false);
+ if (n)
+ return n + 1; /* return pointer to payload area, after opcode */
+ else
+ return NULL;
+}
+
+
+/**
+ * Same as _mesa_dlist_alloc(), but return a pointer which is 8-byte
+ * aligned in 64-bit environments, 4-byte aligned otherwise.
+ */
+void *
+_mesa_dlist_alloc_aligned(struct gl_context *ctx, GLuint opcode, GLuint bytes)
+{
+ Node *n = dlist_alloc(ctx, (OpCode) opcode, bytes, true);
if (n)
return n + 1; /* return pointer to payload area, after opcode */
else
static inline Node *
alloc_instruction(struct gl_context *ctx, OpCode opcode, GLuint nparams)
{
- return dlist_alloc(ctx, opcode, nparams * sizeof(Node));
+ return dlist_alloc(ctx, opcode, nparams * sizeof(Node), false);
}
case OPCODE_CONTINUE:
n = (Node *) get_pointer(&n[1]);
break;
+ case OPCODE_NOP:
+ /* no-op */
+ break;
case OPCODE_END_OF_LIST:
done = GL_TRUE;
break;
fprintf(f, "DISPLAY-LIST-CONTINUE\n");
n = (Node *) get_pointer(&n[1]);
break;
+ case OPCODE_NOP:
+ fprintf(f, "NOP\n");
+ break;
case OPCODE_END_OF_LIST:
fprintf(f, "END-LIST %u\n", list);
done = GL_TRUE;
ctx->List.ListBase = 0;
save_vtxfmt_init(&ctx->ListState.ListVtxfmt);
+
+ InstSize[OPCODE_NOP] = 1;
}