glthread: add display list support to fix state tracking with display lists

The existing display list code is reused to call display lists from the app
thread. The util_queue_fence_wait call waits for the last call that modifies
display lists (such as glEndList and glDeleteLists), which ensures that
accessing display lists from a non-mesa thread is thread safe because
the wait guarantees that display lists are immutable during the asynchronous
display list execution.

Display lists are executed just like normal display lists except that they
call glthread functions instead of the default GL dispatch. Many calls in
display lists are skipped because glthread only tracks a few states.

Acked-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/8297>
This commit is contained in:
Marek Olšák 2020-12-23 17:19:14 -05:00 committed by Marge Bot
parent 14b47614b4
commit 33ad9e77c5
5 changed files with 321 additions and 9 deletions

View file

@ -1111,23 +1111,25 @@
<type name="DEBUGPROC" size="4" pointer="true"/>
<function name="NewList" deprecated="3.1"
marshal_call_after="ctx->GLThread.ListMode = mode;">
marshal_call_after="_mesa_glthread_NewList(ctx, list, mode);">
<param name="list" type="GLuint"/>
<param name="mode" type="GLenum"/>
<glx sop="101"/>
</function>
<function name="EndList" deprecated="3.1"
marshal_call_after="ctx->GLThread.ListMode = 0;">
marshal_call_after="_mesa_glthread_EndList(ctx);">
<glx sop="102"/>
</function>
<function name="CallList" deprecated="3.1">
<function name="CallList" deprecated="3.1"
marshal_call_after="_mesa_glthread_CallList(ctx, list);">
<param name="list" type="GLuint"/>
<glx rop="1"/>
</function>
<function name="CallLists" deprecated="3.1">
<function name="CallLists" deprecated="3.1"
marshal_call_after="_mesa_glthread_CallLists(ctx, n, type, lists);">
<param name="n" type="GLsizei" counter="true"/>
<param name="type" type="GLenum"/>
<param name="lists" type="const GLvoid *" variable_param="type" count="n"
@ -1135,7 +1137,8 @@
<glx rop="2" large="true"/>
</function>
<function name="DeleteLists" deprecated="3.1">
<function name="DeleteLists" deprecated="3.1"
marshal_call_after="_mesa_glthread_DeleteLists(ctx, range);">
<param name="list" type="GLuint"/>
<param name="range" type="GLsizei"/>
<glx sop="103"/>
@ -1147,7 +1150,8 @@
<glx sop="104"/>
</function>
<function name="ListBase" deprecated="3.1">
<function name="ListBase" deprecated="3.1"
marshal_call_after="_mesa_glthread_ListBase(ctx, base);">
<param name="base" type="GLuint"/>
<glx rop="3"/>
</function>
@ -2376,13 +2380,13 @@
</function>
<function name="Disable" es1="1.0" es2="2.0"
marshal_call_after="if (cap == GL_PRIMITIVE_RESTART || cap == GL_PRIMITIVE_RESTART_FIXED_INDEX) _mesa_glthread_set_prim_restart(ctx, cap, false);">
marshal_call_after="_mesa_glthread_Disable(ctx, cap);">
<param name="cap" type="GLenum"/>
<glx rop="138" handcode="client"/>
</function>
<function name="Enable" es1="1.0" es2="2.0"
marshal_call_after='if (cap == GL_PRIMITIVE_RESTART || cap == GL_PRIMITIVE_RESTART_FIXED_INDEX) { _mesa_glthread_set_prim_restart(ctx, cap, true); } else if (cap == GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) { _mesa_glthread_disable(ctx, "Enable(DEBUG_OUTPUT_SYNCHRONOUS)"); }'>
marshal_call_after='_mesa_glthread_Enable(ctx, cap);'>
<param name="cap" type="GLenum"/>
<glx rop="139" handcode="client"/>
</function>

View file

@ -63,6 +63,7 @@
#include "varray.h"
#include "arbprogram.h"
#include "transformfeedback.h"
#include "glthread_marshal.h"
#include "math/m_matrix.h"
@ -14990,6 +14991,87 @@ print_list(struct gl_context *ctx, GLuint list, const char *fname)
}
void
_mesa_glthread_execute_list(struct gl_context *ctx, GLuint list)
{
struct gl_display_list *dlist;
if (list == 0 ||
ctx->GLThread.ListCallDepth == MAX_LIST_NESTING ||
!_mesa_get_list(ctx, list, &dlist))
return;
ctx->GLThread.ListCallDepth++;
Node *n = dlist->Head;
while (1) {
const OpCode opcode = n[0].opcode;
if (is_ext_opcode(opcode)) {
n += ctx->ListExt->Opcode[n[0].opcode - OPCODE_EXT_0].Size;
} else {
switch (opcode) {
case OPCODE_CALL_LIST:
/* Generated by glCallList(), don't add ListBase */
if (ctx->GLThread.ListCallDepth < MAX_LIST_NESTING)
_mesa_glthread_execute_list(ctx, n[1].ui);
break;
case OPCODE_CALL_LISTS:
if (ctx->GLThread.ListCallDepth < MAX_LIST_NESTING)
_mesa_glthread_CallLists(ctx, n[1].i, n[2].e, get_pointer(&n[3]));
break;
case OPCODE_DISABLE:
_mesa_glthread_Disable(ctx, n[1].e);
break;
case OPCODE_ENABLE:
_mesa_glthread_Enable(ctx, n[1].e);
break;
case OPCODE_LIST_BASE:
_mesa_glthread_ListBase(ctx, n[1].ui);
break;
case OPCODE_MATRIX_MODE:
_mesa_glthread_MatrixMode(ctx, n[1].e);
break;
case OPCODE_POP_ATTRIB:
_mesa_glthread_PopAttrib(ctx);
break;
case OPCODE_POP_MATRIX:
_mesa_glthread_PopMatrix(ctx);
break;
case OPCODE_PUSH_ATTRIB:
_mesa_glthread_PushAttrib(ctx, n[1].bf);
break;
case OPCODE_PUSH_MATRIX:
_mesa_glthread_PushMatrix(ctx);
break;
case OPCODE_ACTIVE_TEXTURE: /* GL_ARB_multitexture */
_mesa_glthread_ActiveTexture(ctx, n[1].e);
break;
case OPCODE_MATRIX_PUSH:
_mesa_glthread_MatrixPushEXT(ctx, n[1].e);
break;
case OPCODE_MATRIX_POP:
_mesa_glthread_MatrixPopEXT(ctx, n[1].e);
break;
case OPCODE_CONTINUE:
n = (Node *)get_pointer(&n[1]);
continue;
case OPCODE_END_OF_LIST:
ctx->GLThread.ListCallDepth--;
return;
default:
/* ignore */
break;
}
/* increment n to point to next compiled command */
assert(InstSize[opcode] > 0);
n += InstSize[opcode];
}
}
}
/**
* Clients may call this function to help debug display list problems.

View file

@ -76,7 +76,7 @@ glthread_unmarshal_batch(void *job, int thread_index)
unsigned batch_index = batch - ctx->GLThread.batches;
/* Atomically set this to -1 if it's equal to batch_index. */
p_atomic_cmpxchg(&ctx->GLThread.LastProgramChangeBatch, batch_index, -1);
p_atomic_cmpxchg(&ctx->GLThread.LastDListChangeBatchIndex, batch_index, -1);
}
static void
@ -139,6 +139,8 @@ _mesa_glthread_init(struct gl_context *ctx)
ctx->CurrentClientDispatch = ctx->MarshalExec;
glthread->LastDListChangeBatchIndex = -1;
/* Execute the thread initialization function in the thread. */
struct util_queue_fence fence;
util_queue_fence_init(&fence);

View file

@ -155,6 +155,8 @@ struct glthread_state
/** Display lists. */
GLenum ListMode; /**< Zero if not inside display list, else list mode. */
unsigned ListBase;
unsigned ListCallDepth;
/** For L3 cache pinning. */
unsigned pin_thread_counter;
@ -210,6 +212,12 @@ struct glthread_state
*/
int LastProgramChangeBatch;
/**
* The batch index of the last occurence of glEndList or
* glDeleteLists or -1 if there is no such enqueued call.
*/
int LastDListChangeBatchIndex;
/** Basic matrix state tracking. */
int ActiveTexture;
GLenum MatrixMode;
@ -234,6 +242,7 @@ void _mesa_glthread_upload(struct gl_context *ctx, const void *data,
void _mesa_glthread_reset_vao(struct glthread_vao *vao);
void _mesa_error_glthread_safe(struct gl_context *ctx, GLenum error,
bool glthread, const char *format, ...);
void _mesa_glthread_execute_list(struct gl_context *ctx, GLuint list);
void _mesa_glthread_BindBuffer(struct gl_context *ctx, GLenum target,
GLuint buffer);

View file

@ -423,9 +423,36 @@ _mesa_get_matrix_index(struct gl_context *ctx, GLenum mode)
return M_DUMMY;
}
static inline void
_mesa_glthread_Enable(struct gl_context *ctx, GLenum cap)
{
if (ctx->GLThread.ListMode == GL_COMPILE)
return;
if (cap == GL_PRIMITIVE_RESTART ||
cap == GL_PRIMITIVE_RESTART_FIXED_INDEX)
_mesa_glthread_set_prim_restart(ctx, cap, true);
else if (cap == GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB)
_mesa_glthread_disable(ctx, "Enable(DEBUG_OUTPUT_SYNCHRONOUS)");
}
static inline void
_mesa_glthread_Disable(struct gl_context *ctx, GLenum cap)
{
if (ctx->GLThread.ListMode == GL_COMPILE)
return;
if (cap == GL_PRIMITIVE_RESTART ||
cap == GL_PRIMITIVE_RESTART_FIXED_INDEX)
_mesa_glthread_set_prim_restart(ctx, cap, false);
}
static inline void
_mesa_glthread_PushAttrib(struct gl_context *ctx, GLbitfield mask)
{
if (ctx->GLThread.ListMode == GL_COMPILE)
return;
struct glthread_attrib_node *attr =
&ctx->GLThread.AttribStack[ctx->GLThread.AttribStackDepth++];
@ -441,6 +468,9 @@ _mesa_glthread_PushAttrib(struct gl_context *ctx, GLbitfield mask)
static inline void
_mesa_glthread_PopAttrib(struct gl_context *ctx)
{
if (ctx->GLThread.ListMode == GL_COMPILE)
return;
struct glthread_attrib_node *attr =
&ctx->GLThread.AttribStack[--ctx->GLThread.AttribStackDepth];
unsigned mask = attr->Mask;
@ -457,18 +487,27 @@ _mesa_glthread_PopAttrib(struct gl_context *ctx)
static inline void
_mesa_glthread_MatrixPushEXT(struct gl_context *ctx, GLenum matrixMode)
{
if (ctx->GLThread.ListMode == GL_COMPILE)
return;
ctx->GLThread.MatrixStackDepth[_mesa_get_matrix_index(ctx, matrixMode)]++;
}
static inline void
_mesa_glthread_MatrixPopEXT(struct gl_context *ctx, GLenum matrixMode)
{
if (ctx->GLThread.ListMode == GL_COMPILE)
return;
ctx->GLThread.MatrixStackDepth[_mesa_get_matrix_index(ctx, matrixMode)]--;
}
static inline void
_mesa_glthread_ActiveTexture(struct gl_context *ctx, GLenum texture)
{
if (ctx->GLThread.ListMode == GL_COMPILE)
return;
ctx->GLThread.ActiveTexture = texture - GL_TEXTURE0;
if (ctx->GLThread.MatrixMode == GL_TEXTURE)
ctx->GLThread.MatrixIndex = _mesa_get_matrix_index(ctx, texture);
@ -477,20 +516,196 @@ _mesa_glthread_ActiveTexture(struct gl_context *ctx, GLenum texture)
static inline void
_mesa_glthread_PushMatrix(struct gl_context *ctx)
{
if (ctx->GLThread.ListMode == GL_COMPILE)
return;
ctx->GLThread.MatrixStackDepth[ctx->GLThread.MatrixIndex]++;
}
static inline void
_mesa_glthread_PopMatrix(struct gl_context *ctx)
{
if (ctx->GLThread.ListMode == GL_COMPILE)
return;
ctx->GLThread.MatrixStackDepth[ctx->GLThread.MatrixIndex]--;
}
static inline void
_mesa_glthread_MatrixMode(struct gl_context *ctx, GLenum mode)
{
if (ctx->GLThread.ListMode == GL_COMPILE)
return;
ctx->GLThread.MatrixIndex = _mesa_get_matrix_index(ctx, mode);
ctx->GLThread.MatrixMode = mode;
}
static inline void
_mesa_glthread_ListBase(struct gl_context *ctx, GLuint base)
{
if (ctx->GLThread.ListMode == GL_COMPILE)
return;
ctx->GLThread.ListBase = base;
}
static inline void
_mesa_glthread_CallList(struct gl_context *ctx, GLuint list)
{
if (ctx->GLThread.ListMode == GL_COMPILE)
return;
/* Wait for all glEndList and glDeleteLists calls to finish to ensure that
* all display lists are up to date and the driver thread is not
* modifiying them. We will be executing them in the application thread.
*/
int batch = p_atomic_read(&ctx->GLThread.LastDListChangeBatchIndex);
if (batch != -1) {
util_queue_fence_wait(&ctx->GLThread.batches[batch].fence);
p_atomic_set(&ctx->GLThread.LastDListChangeBatchIndex, -1);
}
/* Clear GL_COMPILE_AND_EXECUTE if needed. We only execute here. */
unsigned saved_mode = ctx->GLThread.ListMode;
ctx->GLThread.ListMode = 0;
_mesa_glthread_execute_list(ctx, list);
ctx->GLThread.ListMode = saved_mode;
}
static inline void
_mesa_glthread_CallLists(struct gl_context *ctx, GLsizei n, GLenum type,
const GLvoid *lists)
{
if (ctx->GLThread.ListMode == GL_COMPILE)
return;
if (n <= 0 || !lists)
return;
/* Wait for all glEndList and glDeleteLists calls to finish to ensure that
* all display lists are up to date and the driver thread is not
* modifiying them. We will be executing them in the application thread.
*/
int batch = p_atomic_read(&ctx->GLThread.LastDListChangeBatchIndex);
if (batch != -1) {
util_queue_fence_wait(&ctx->GLThread.batches[batch].fence);
p_atomic_set(&ctx->GLThread.LastDListChangeBatchIndex, -1);
}
/* Clear GL_COMPILE_AND_EXECUTE if needed. We only execute here. */
unsigned saved_mode = ctx->GLThread.ListMode;
ctx->GLThread.ListMode = 0;
unsigned base = ctx->GLThread.ListBase;
GLbyte *bptr;
GLubyte *ubptr;
GLshort *sptr;
GLushort *usptr;
GLint *iptr;
GLuint *uiptr;
GLfloat *fptr;
switch (type) {
case GL_BYTE:
bptr = (GLbyte *) lists;
for (unsigned i = 0; i < n; i++)
_mesa_glthread_CallList(ctx, base + bptr[i]);
break;
case GL_UNSIGNED_BYTE:
ubptr = (GLubyte *) lists;
for (unsigned i = 0; i < n; i++)
_mesa_glthread_CallList(ctx, base + ubptr[i]);
break;
case GL_SHORT:
sptr = (GLshort *) lists;
for (unsigned i = 0; i < n; i++)
_mesa_glthread_CallList(ctx, base + sptr[i]);
break;
case GL_UNSIGNED_SHORT:
usptr = (GLushort *) lists;
for (unsigned i = 0; i < n; i++)
_mesa_glthread_CallList(ctx, base + usptr[i]);
break;
case GL_INT:
iptr = (GLint *) lists;
for (unsigned i = 0; i < n; i++)
_mesa_glthread_CallList(ctx, base + iptr[i]);
break;
case GL_UNSIGNED_INT:
uiptr = (GLuint *) lists;
for (unsigned i = 0; i < n; i++)
_mesa_glthread_CallList(ctx, base + uiptr[i]);
break;
case GL_FLOAT:
fptr = (GLfloat *) lists;
for (unsigned i = 0; i < n; i++)
_mesa_glthread_CallList(ctx, base + fptr[i]);
break;
case GL_2_BYTES:
ubptr = (GLubyte *) lists;
for (unsigned i = 0; i < n; i++) {
_mesa_glthread_CallList(ctx, base +
(GLint)ubptr[2 * i] * 256 +
(GLint)ubptr[2 * i + 1]);
}
break;
case GL_3_BYTES:
ubptr = (GLubyte *) lists;
for (unsigned i = 0; i < n; i++) {
_mesa_glthread_CallList(ctx, base +
(GLint)ubptr[3 * i] * 65536 +
(GLint)ubptr[3 * i + 1] * 256 +
(GLint)ubptr[3 * i + 2]);
}
break;
case GL_4_BYTES:
ubptr = (GLubyte *) lists;
for (unsigned i = 0; i < n; i++) {
_mesa_glthread_CallList(ctx, base +
(GLint)ubptr[4 * i] * 16777216 +
(GLint)ubptr[4 * i + 1] * 65536 +
(GLint)ubptr[4 * i + 2] * 256 +
(GLint)ubptr[4 * i + 3]);
}
break;
}
ctx->GLThread.ListMode = saved_mode;
}
static inline void
_mesa_glthread_NewList(struct gl_context *ctx, GLuint list, GLuint mode)
{
if (!ctx->GLThread.ListMode)
ctx->GLThread.ListMode = mode;
}
static inline void
_mesa_glthread_EndList(struct gl_context *ctx)
{
if (!ctx->GLThread.ListMode)
return;
ctx->GLThread.ListMode = 0;
/* Track the last display list change. */
p_atomic_set(&ctx->GLThread.LastDListChangeBatchIndex, ctx->GLThread.next);
_mesa_glthread_flush_batch(ctx);
}
static inline void
_mesa_glthread_DeleteLists(struct gl_context *ctx, GLsizei range)
{
if (range < 0)
return;
/* Track the last display list change. */
p_atomic_set(&ctx->GLThread.LastDListChangeBatchIndex, ctx->GLThread.next);
_mesa_glthread_flush_batch(ctx);
}
#endif /* MARSHAL_H */