Skip to content

Commit 901effb

Browse files
Make COpenGLSync public, allowing for Interoperation with SDKs also using the OpenGL API
1 parent 68277eb commit 901effb

File tree

6 files changed

+156
-140
lines changed

6 files changed

+156
-140
lines changed

include/nbl/video/COpenGLSync.h

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#ifndef _NBL_C_OPENGL_SYNC_H_INCLUDED_
2+
#define _NBL_C_OPENGL_SYNC_H_INCLUDED_
3+
4+
#include "nbl/core/IReferenceCounted.h"
5+
6+
namespace nbl::video
7+
{
8+
9+
class IOpenGL_LogicalDevice;
10+
class IOpenGL_FunctionTable;
11+
12+
class COpenGLSync final : public core::IReferenceCounted
13+
{
14+
protected:
15+
virtual ~COpenGLSync();
16+
17+
public:
18+
enum E_STATUS : uint32_t
19+
{
20+
//! Indicates that an error occurred. Additionally, an OpenGL error will be generated.
21+
ES_FAIL = 0,
22+
//! If it returns GL_TIMEOUT_EXPIRED, then the sync object did not signal within the given timeout period (includes before us calling the func).
23+
ES_TIMEOUT_EXPIRED,
24+
//! Indicates that sync​ was signaled before the timeout expired.
25+
ES_CONDITION_SATISFIED,
26+
//! GPU already completed work before we even asked == THIS IS WHAT WE WANT
27+
ES_ALREADY_SIGNALED
28+
};
29+
30+
// External GLsync Import constructor. If you leave the `_dev` parameter null, then you're in charge of deleting the `_sync` by yourself.
31+
inline COpenGLSync(GLsync _sync, core::smart_refctd_ptr<IOpenGL_LogicalDevice>&& _dev=nullptr)
32+
: device(std::move(_dev)), lockedTable(nullptr), haveNotWaitedOnQueueMask(~0ull), cachedRetval(ES_TIMEOUT_EXPIRED), sync(_sync) {}
33+
34+
//
35+
inline GLsync getOpenGLName() const {return sync;}
36+
37+
// Whether Nabla flushed after placing a fence or not, counting on an implicit flush from waiting on the same context.
38+
// You can still wait on the fence with a different context, but it the context it was placed on needs a flush first.
39+
inline bool initializedWithImplicitFlush() const {return lockedTable;}
40+
41+
// whether `init` has been called and there is a valid `sync` to wait for
42+
bool isInitialized() const
43+
{
44+
// (!sync && (cachedRetval == ES_ALREADY_SIGNALED)) means initialized as signaled
45+
return static_cast<bool>(sync) || cachedRetval == ES_ALREADY_SIGNALED;
46+
}
47+
48+
49+
// for internal Nabla usage
50+
void init(core::smart_refctd_ptr<IOpenGL_LogicalDevice>&& _dev, IOpenGL_FunctionTable* _gl, bool _lockToQueue = false);
51+
uint64_t prewait() const;
52+
E_STATUS waitCPU(IOpenGL_FunctionTable* _gl, uint64_t timeout);
53+
void waitGPU(IOpenGL_FunctionTable* _gl);
54+
55+
// Empty (waiting to be made pending a signal) not yet initialized sync constructor
56+
inline COpenGLSync() : COpenGLSync(nullptr,nullptr) {}
57+
// Already signalled constructor (for IGPUFence)
58+
struct signalled_t {};
59+
constexpr static inline signalled_t signalled = {};
60+
inline COpenGLSync(core::smart_refctd_ptr<IOpenGL_LogicalDevice>&& _dev, IOpenGL_FunctionTable* _gl, signalled_t signalled);
61+
62+
private:
63+
core::smart_refctd_ptr<IOpenGL_LogicalDevice> device;
64+
IOpenGL_FunctionTable* lockedTable;
65+
std::atomic_uint64_t haveNotWaitedOnQueueMask;
66+
E_STATUS cachedRetval;
67+
// maybe atomic instead of volatile would work better?
68+
volatile GLsync sync;
69+
};
70+
71+
}
72+
73+
#endif

src/nbl/video/COpenGLFence.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ class COpenGLFence final : public IGPUFence, public IOpenGLSyncPrimitiveBase
1919
// signaled ctor
2020
COpenGLFence(core::smart_refctd_ptr<IOpenGL_LogicalDevice>&& dev, IOpenGL_FunctionTable* gl) : IGPUFence(core::smart_refctd_ptr<const ILogicalDevice>(dev), ECF_SIGNALED_BIT)
2121
{
22-
auto sync = core::make_smart_refctd_ptr<COpenGLSync>();
23-
sync->initSignaled(dev.get(), gl);
22+
auto sync = core::make_smart_refctd_ptr<COpenGLSync>(std::move(dev),gl,COpenGLSync::signalled);
2423
associateGLSync(std::move(sync));
2524
}
2625
// un-signaled ctor

src/nbl/video/COpenGLSync.cpp

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,89 @@
44

55
namespace nbl::video
66
{
7+
8+
COpenGLSync::COpenGLSync(core::smart_refctd_ptr<IOpenGL_LogicalDevice>&& _dev, IOpenGL_FunctionTable* _gl, signalled_t signalled)
9+
: device(_dev), lockedTable(nullptr), haveNotWaitedOnQueueMask(~(0x1ull<<_gl->getGUID())), cachedRetval(ES_ALREADY_SIGNALED), sync(nullptr)
10+
{
11+
}
712

813
COpenGLSync::~COpenGLSync()
914
{
10-
if (sync)
15+
if (device && sync)
1116
device->destroySync(sync);
1217
}
1318

19+
void COpenGLSync::init(core::smart_refctd_ptr<IOpenGL_LogicalDevice>&& _dev, IOpenGL_FunctionTable* _gl, bool _lockToQueue)
20+
{
21+
device = _dev;
22+
sync = _gl->glSync.pglFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
23+
if (_lockToQueue)
24+
lockedTable = _gl;
25+
else
26+
_gl->glGeneral.pglFlush();
27+
haveNotWaitedOnQueueMask ^= (1ull << _gl->getGUID());
28+
}
29+
30+
uint64_t COpenGLSync::prewait() const
31+
{
32+
if (sync)
33+
return 0ull;
34+
35+
using clock_t = std::chrono::high_resolution_clock;
36+
auto start = clock_t::now();
37+
while (!sync)
38+
{
39+
std::this_thread::yield();
40+
}
41+
42+
return std::chrono::duration_cast<std::chrono::nanoseconds>(clock_t::now() - start).count();
43+
}
44+
45+
COpenGLSync::E_STATUS COpenGLSync::waitCPU(IOpenGL_FunctionTable* _gl, uint64_t timeout)
46+
{
47+
assert(!lockedTable || lockedTable==_gl);
48+
if (cachedRetval != ES_TIMEOUT_EXPIRED)
49+
return cachedRetval;
50+
51+
const uint64_t spintime = prewait();
52+
if (spintime > timeout)
53+
return (cachedRetval = ES_TIMEOUT_EXPIRED);
54+
timeout -= spintime;
55+
56+
GLenum status = _gl->glSync.pglClientWaitSync(sync, lockedTable?GL_SYNC_FLUSH_COMMANDS_BIT:0, timeout); // GL_SYNC_FLUSH_COMMANDS_BIT to flags?
57+
switch (status)
58+
{
59+
case GL_ALREADY_SIGNALED:
60+
return (cachedRetval = ES_ALREADY_SIGNALED);
61+
break;
62+
case GL_TIMEOUT_EXPIRED:
63+
return (cachedRetval = ES_TIMEOUT_EXPIRED);
64+
break;
65+
case GL_CONDITION_SATISFIED:;
66+
return (cachedRetval = ES_CONDITION_SATISFIED);
67+
break;
68+
default:
69+
break;
70+
}
71+
return (cachedRetval = ES_FAIL);
72+
}
73+
74+
void COpenGLSync::waitGPU(IOpenGL_FunctionTable* _gl)
75+
{
76+
if (!lockedTable) // OpenGL device does not need to wait on itself within the same context
77+
{
78+
const uint64_t queueMask = 1ull << _gl->getGUID();
79+
if (haveNotWaitedOnQueueMask.load() & queueMask)
80+
{
81+
prewait();
82+
_gl->glSync.pglWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
83+
haveNotWaitedOnQueueMask ^= queueMask;
84+
}
85+
}
86+
else
87+
{
88+
assert(lockedTable==_gl);
89+
}
90+
}
91+
1492
}

src/nbl/video/COpenGLSync.h

Lines changed: 0 additions & 134 deletions
This file was deleted.

src/nbl/video/COpenGL_Queue.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ class COpenGL_Queue final : public IGPUQueue
263263

264264
if (submit.syncToInit)
265265
{
266-
submit.syncToInit->init(m_device, &gl);
266+
submit.syncToInit->init(core::smart_refctd_ptr<IOpenGL_LogicalDevice>(m_device), &gl);
267267
}
268268
else // need to flush, otherwise OpenGL goes gaslighting the user with wrong error messages
269269
gl.glGeneral.pglFlush();

src/nbl/video/COpenGL_Swapchain.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ class COpenGL_Swapchain final : public ISwapchain
258258
for (uint32_t i = 0u; i < fboCount; ++i)
259259
{
260260
syncs[i] = core::make_smart_refctd_ptr<COpenGLSync>();
261-
syncs[i]->init(m_device, &gl, false);
261+
syncs[i]->init(core::smart_refctd_ptr<IOpenGL_LogicalDevice>(m_device), &gl, false);
262262
}
263263

264264
gl.glGeneral.pglFinish();
@@ -283,7 +283,7 @@ class COpenGL_Swapchain final : public ISwapchain
283283

284284
gl.extGlBlitNamedFramebuffer(fbos[imgix], 0, 0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
285285
syncs[imgix] = core::make_smart_refctd_ptr<COpenGLSync>();
286-
syncs[imgix]->init(m_device, &gl, false);
286+
syncs[imgix]->init(core::smart_refctd_ptr<IOpenGL_LogicalDevice>(m_device), &gl, false);
287287
// swap buffers performs an implicit flush before swapping
288288
// https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglSwapBuffers.xhtml
289289
egl->call.peglSwapBuffers(egl->display, glctx.surface);

0 commit comments

Comments
 (0)