[client] egl: make better use of atomics and fix modulus bug

This commit is contained in:
Geoffrey McRae 2020-07-24 17:39:16 +10:00
parent bd42445ea7
commit 06aee158de
2 changed files with 56 additions and 60 deletions

View File

@ -1 +1 @@
B2-rc2-9-g67dec216d2+1 B2-rc2-11-gbd42445ea7+1

View File

@ -30,7 +30,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <SDL2/SDL_egl.h> #include <SDL2/SDL_egl.h>
#define TEXTURE_COUNT 3 /* this must be a multiple of 2 */
#define TEXTURE_COUNT 2
struct Tex struct Tex
{ {
@ -41,19 +42,9 @@ struct Tex
GLsync sync; GLsync sync;
}; };
union TexState struct TexState
{ {
_Atomic(uint32_t) v; _Atomic(uint8_t) w, u, s, d;
struct
{
/*
* w = write
* u = upload
* s = schedule
* d = display
*/
_Atomic(int8_t) w, u, s, d;
};
}; };
struct EGL_Texture struct EGL_Texture
@ -73,7 +64,7 @@ struct EGL_Texture
GLenum dataType; GLenum dataType;
size_t pboBufferSize; size_t pboBufferSize;
union TexState state; struct TexState state;
int textureCount; int textureCount;
struct Tex tex[TEXTURE_COUNT]; struct Tex tex[TEXTURE_COUNT];
}; };
@ -182,7 +173,10 @@ bool egl_texture_setup(EGL_Texture * texture, enum EGL_PixelFormat pixFmt, size_
texture->textureCount = streaming ? TEXTURE_COUNT : 1; texture->textureCount = streaming ? TEXTURE_COUNT : 1;
texture->ready = false; texture->ready = false;
atomic_store_explicit(&texture->state.v, 0, memory_order_relaxed); atomic_store_explicit(&texture->state.w, 0, memory_order_relaxed);
atomic_store_explicit(&texture->state.u, 0, memory_order_relaxed);
atomic_store_explicit(&texture->state.s, 0, memory_order_relaxed);
atomic_store_explicit(&texture->state.d, 0, memory_order_relaxed);
switch(pixFmt) switch(pixFmt)
{ {
@ -322,22 +316,22 @@ bool egl_texture_update(EGL_Texture * texture, const uint8_t * buffer)
{ {
if (texture->streaming) if (texture->streaming)
{ {
union TexState s; const uint8_t sw =
s.v = atomic_load_explicit(&texture->state.v, memory_order_acquire); atomic_load_explicit(&texture->state.w, memory_order_acquire);
const uint8_t next = (s.w + 1) % TEXTURE_COUNT; if (atomic_load_explicit(&texture->state.u, memory_order_acquire) == sw + 1)
if (next == s.u)
{ {
egl_warn_slow(); egl_warn_slow();
return true; return true;
} }
if (!egl_texture_map(texture, s.w)) const uint8_t t = sw % TEXTURE_COUNT;
if (!egl_texture_map(texture, t))
return EGL_TEX_STATUS_ERROR; return EGL_TEX_STATUS_ERROR;
memcpy(texture->tex[s.w].map, buffer, texture->pboBufferSize); memcpy(texture->tex[t].map, buffer, texture->pboBufferSize);
atomic_store_explicit(&texture->state.w, next, memory_order_release); atomic_fetch_add_explicit(&texture->state.w, 1, memory_order_release);
egl_texture_unmap(texture, s.w); egl_texture_unmap(texture, t);
} }
else else
{ {
@ -358,22 +352,22 @@ bool egl_texture_update_from_frame(EGL_Texture * texture, const FrameBuffer * fr
if (!texture->streaming) if (!texture->streaming)
return false; return false;
union TexState s; const uint8_t sw =
s.v = atomic_load_explicit(&texture->state.v, memory_order_acquire); atomic_load_explicit(&texture->state.w, memory_order_acquire);
const uint8_t next = (s.w + 1) % TEXTURE_COUNT; if (atomic_load_explicit(&texture->state.u, memory_order_acquire) == sw + 1)
if (next == s.u)
{ {
egl_warn_slow(); egl_warn_slow();
return true; return true;
} }
if (!egl_texture_map(texture, s.w)) const uint8_t t = sw % TEXTURE_COUNT;
if (!egl_texture_map(texture, t))
return EGL_TEX_STATUS_ERROR; return EGL_TEX_STATUS_ERROR;
framebuffer_read( framebuffer_read(
frame, frame,
texture->tex[s.w].map, texture->tex[t].map,
texture->stride, texture->stride,
texture->height, texture->height,
texture->width, texture->width,
@ -381,8 +375,8 @@ bool egl_texture_update_from_frame(EGL_Texture * texture, const FrameBuffer * fr
texture->stride texture->stride
); );
atomic_store_explicit(&texture->state.w, next, memory_order_release); atomic_fetch_add_explicit(&texture->state.w, 1, memory_order_release);
egl_texture_unmap(texture, s.w); egl_texture_unmap(texture, t);
return true; return true;
} }
@ -392,18 +386,22 @@ enum EGL_TexStatus egl_texture_process(EGL_Texture * texture)
if (!texture->streaming) if (!texture->streaming)
return EGL_TEX_STATUS_OK; return EGL_TEX_STATUS_OK;
union TexState s; const uint8_t su =
s.v = atomic_load_explicit(&texture->state.v, memory_order_acquire); atomic_load_explicit(&texture->state.u, memory_order_acquire);
const uint8_t nextu = (s.u + 1) % TEXTURE_COUNT; const uint8_t nextu = su + 1;
if (s.u == s.w || nextu == s.s || nextu == s.d) if (
su == atomic_load_explicit(&texture->state.w, memory_order_acquire) ||
nextu == atomic_load_explicit(&texture->state.s, memory_order_acquire) ||
nextu == atomic_load_explicit(&texture->state.d, memory_order_acquire))
return texture->ready ? EGL_TEX_STATUS_OK : EGL_TEX_STATUS_NOTREADY; return texture->ready ? EGL_TEX_STATUS_OK : EGL_TEX_STATUS_NOTREADY;
/* update the texture */ /* update the texture */
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->tex[s.u].pbo); const uint8_t t = su % TEXTURE_COUNT;
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->tex[t].pbo);
for(int p = 0; p < texture->planeCount; ++p) for(int p = 0; p < texture->planeCount; ++p)
{ {
glBindTexture(GL_TEXTURE_2D, texture->tex[s.u].t[p]); glBindTexture(GL_TEXTURE_2D, texture->tex[t].t[p]);
glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->planes[p][2]); glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->planes[p][2]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->planes[p][0], texture->planes[p][1], glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->planes[p][0], texture->planes[p][1],
texture->format, texture->dataType, (const void *)texture->offsets[p]); texture->format, texture->dataType, (const void *)texture->offsets[p]);
@ -412,39 +410,39 @@ enum EGL_TexStatus egl_texture_process(EGL_Texture * texture)
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
/* create a fence to prevent usage before the update is complete */ /* create a fence to prevent usage before the update is complete */
texture->tex[s.u].sync = texture->tex[t].sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
/* we must flush to ensure the sync is in the command buffer */ /* we must flush to ensure the sync is in the command buffer */
glFlush(); glFlush();
texture->ready = true; texture->ready = true;
atomic_store_explicit(&texture->state.u, nextu, memory_order_release); atomic_fetch_add_explicit(&texture->state.u, 1, memory_order_release);
return EGL_TEX_STATUS_OK; return EGL_TEX_STATUS_OK;
} }
enum EGL_TexStatus egl_texture_bind(EGL_Texture * texture) enum EGL_TexStatus egl_texture_bind(EGL_Texture * texture)
{ {
union TexState s; uint8_t ss = atomic_load_explicit(&texture->state.s, memory_order_acquire);
s.v = atomic_load_explicit(&texture->state.v, memory_order_acquire); uint8_t sd = atomic_load_explicit(&texture->state.d, memory_order_acquire);
if (texture->streaming) if (texture->streaming)
{ {
if (!texture->ready) if (!texture->ready)
return EGL_TEX_STATUS_NOTREADY; return EGL_TEX_STATUS_NOTREADY;
if (texture->tex[s.s].sync != 0) const uint8_t t = ss % TEXTURE_COUNT;
if (texture->tex[t].sync != 0)
{ {
switch(glClientWaitSync(texture->tex[s.s].sync, 0, 20000000)) // 20ms switch(glClientWaitSync(texture->tex[t].sync, 0, 20000000)) // 20ms
{ {
case GL_ALREADY_SIGNALED: case GL_ALREADY_SIGNALED:
case GL_CONDITION_SATISFIED: case GL_CONDITION_SATISFIED:
glDeleteSync(texture->tex[s.s].sync); glDeleteSync(texture->tex[t].sync);
texture->tex[s.s].sync = 0; texture->tex[t].sync = 0;
s.s = (s.s + 1) % TEXTURE_COUNT; ss = atomic_fetch_add_explicit(&texture->state.s, 1,
atomic_store_explicit(&texture->state.s, s.s, memory_order_release); memory_order_release) + 1;
break; break;
case GL_TIMEOUT_EXPIRED: case GL_TIMEOUT_EXPIRED:
@ -452,25 +450,23 @@ enum EGL_TexStatus egl_texture_bind(EGL_Texture * texture)
case GL_WAIT_FAILED: case GL_WAIT_FAILED:
case GL_INVALID_VALUE: case GL_INVALID_VALUE:
glDeleteSync(texture->tex[s.s].sync); glDeleteSync(texture->tex[t].sync);
texture->tex[s.s].sync = 0; texture->tex[t].sync = 0;
EGL_ERROR("glClientWaitSync failed"); EGL_ERROR("glClientWaitSync failed");
return EGL_TEX_STATUS_ERROR; return EGL_TEX_STATUS_ERROR;
} }
} }
const int8_t nextd = (s.d + 1) % TEXTURE_COUNT; if (ss != sd && ss != sd+1)
if (s.d != s.s && nextd != s.s) sd = atomic_fetch_add_explicit(&texture->state.d, 1,
{ memory_order_release) + 1;
s.d = nextd;
atomic_store_explicit(&texture->state.d, nextd, memory_order_release);
}
} }
const uint8_t t = sd % TEXTURE_COUNT;
for(int i = 0; i < texture->planeCount; ++i) for(int i = 0; i < texture->planeCount; ++i)
{ {
glActiveTexture(GL_TEXTURE0 + i); glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, texture->tex[s.d].t[i]); glBindTexture(GL_TEXTURE_2D, texture->tex[t].t[i]);
glBindSampler(i, texture->samplers[i]); glBindSampler(i, texture->samplers[i]);
} }