1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

OpenGL: Carry out all GL rendering on a single thread

This commit is contained in:
reuk 2022-09-13 14:32:44 +01:00
parent ae3bfdb1d2
commit 2ae87f95f1
No known key found for this signature in database
GPG key ID: FCB43929F012EE5C
8 changed files with 415 additions and 337 deletions

View file

@ -124,7 +124,7 @@ DECLARE_JNI_CLASS_WITH_BYTECODE (JuceOpenGLViewSurface, "com/rmsl/juce/JuceOpenG
#undef JNI_CLASS_MEMBERS
//==============================================================================
class OpenGLContext::NativeContext : private SurfaceHolderCallback
class OpenGLContext::NativeContext : private SurfaceHolderCallback
{
public:
NativeContext (Component& comp,
@ -132,8 +132,7 @@ public:
void* /*contextToShareWith*/,
bool useMultisamplingIn,
OpenGLVersion)
: component (comp),
surface (EGL_NO_SURFACE), context (EGL_NO_CONTEXT)
: component (comp)
{
auto env = getEnv();
@ -175,88 +174,45 @@ public:
}
//==============================================================================
bool initialiseOnRenderThread (OpenGLContext& aContext)
InitResult initialiseOnRenderThread (OpenGLContext& ctx)
{
jassert (hasInitialised);
// The "real" initialisation happens when the surface is created. Here, we'll
// just return true if the initialisation happened successfully, or false if
// it hasn't happened yet, or was unsuccessful.
const std::lock_guard lock { nativeHandleMutex };
// has the context already attached?
jassert (surface == EGL_NO_SURFACE && context == EGL_NO_CONTEXT);
if (! hasInitialised)
return InitResult::fatal;
auto env = getEnv();
if (context.get() == EGL_NO_CONTEXT && surface.get() == EGL_NO_SURFACE)
return InitResult::retry;
ANativeWindow* window = nullptr;
LocalRef<jobject> holder (env->CallObjectMethod (surfaceView.get(), JuceOpenGLViewSurface.getHolder));
if (holder != nullptr)
{
LocalRef<jobject> jSurface (env->CallObjectMethod (holder.get(), AndroidSurfaceHolder.getSurface));
if (jSurface != nullptr)
{
window = ANativeWindow_fromSurface(env, jSurface.get());
// if we didn't succeed the first time, wait 25ms and try again
if (window == nullptr)
{
Thread::sleep (200);
window = ANativeWindow_fromSurface (env, jSurface.get());
}
}
}
if (window == nullptr)
{
// failed to get a pointer to the native window after second try so bail out
jassertfalse;
return false;
}
// create the surface
surface = eglCreateWindowSurface (display, config, window, nullptr);
jassert (surface != EGL_NO_SURFACE);
ANativeWindow_release (window);
// create the OpenGL context
EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
context = eglCreateContext (display, config, EGL_NO_CONTEXT, contextAttribs);
jassert (context != EGL_NO_CONTEXT);
juceContext = &aContext;
return true;
juceContext = &ctx;
return InitResult::success;
}
void shutdownOnRenderThread()
{
jassert (hasInitialised);
// is there a context available to detach?
jassert (surface != EGL_NO_SURFACE && context != EGL_NO_CONTEXT);
eglDestroyContext (display, context);
context = EGL_NO_CONTEXT;
eglDestroySurface (display, surface);
surface = EGL_NO_SURFACE;
const std::lock_guard lock { nativeHandleMutex };
juceContext = nullptr;
}
//==============================================================================
bool makeActive() const noexcept
{
if (! hasInitialised)
return false;
const std::lock_guard lock { nativeHandleMutex };
if (surface == EGL_NO_SURFACE || context == EGL_NO_CONTEXT)
return false;
if (! eglMakeCurrent (display, surface, surface, context))
return false;
return true;
return hasInitialised
&& surface.get() != EGL_NO_SURFACE
&& context.get() != EGL_NO_CONTEXT
&& eglMakeCurrent (display, surface.get(), surface.get(), context.get());
}
bool isActive() const noexcept { return eglGetCurrentContext() == context; }
bool isActive() const noexcept
{
const std::lock_guard lock { nativeHandleMutex };
return eglGetCurrentContext() == context.get();
}
static void deactivateCurrentContext()
{
@ -264,7 +220,7 @@ public:
}
//==============================================================================
void swapBuffers() const noexcept { eglSwapBuffers (display, surface); }
void swapBuffers() const noexcept { eglSwapBuffers (display, surface.get()); }
bool setSwapInterval (const int) { return false; }
int getSwapInterval() const { return 0; }
@ -295,8 +251,8 @@ public:
ignoreUnused (holder, format, width, height);
}
void surfaceCreated (LocalRef<jobject> holder) override;
void surfaceDestroyed (LocalRef<jobject> holder) override;
void surfaceCreated (LocalRef<jobject>) override;
void surfaceDestroyed (LocalRef<jobject>) override;
//==============================================================================
struct Locker
@ -338,6 +294,8 @@ private:
void dispatchDraw (jobject /*canvas*/)
{
const std::lock_guard lock { nativeHandleMutex };
if (juceContext != nullptr)
juceContext->triggerRepaint();
}
@ -393,6 +351,34 @@ private:
return false;
}
struct NativeWindowReleaser
{
void operator() (ANativeWindow* ptr) const { if (ptr != nullptr) ANativeWindow_release (ptr); }
};
std::unique_ptr<ANativeWindow, NativeWindowReleaser> getNativeWindow() const
{
auto* env = getEnv();
const LocalRef<jobject> holder (env->CallObjectMethod (surfaceView.get(), JuceOpenGLViewSurface.getHolder));
if (holder == nullptr)
return nullptr;
const LocalRef<jobject> jSurface (env->CallObjectMethod (holder.get(), AndroidSurfaceHolder.getSurface));
if (jSurface == nullptr)
return nullptr;
constexpr auto numAttempts = 2;
for (auto i = 0; i < numAttempts; Thread::sleep (200), ++i)
if (auto* ptr = ANativeWindow_fromSurface (env, jSurface.get()))
return std::unique_ptr<ANativeWindow, NativeWindowReleaser> { ptr };
return nullptr;
}
//==============================================================================
CriticalSection mutex;
bool hasInitialised = false;
@ -400,9 +386,20 @@ private:
GlobalRef surfaceView;
Rectangle<int> lastBounds;
struct SurfaceDestructor
{
void operator() (EGLSurface x) const { if (x != EGL_NO_SURFACE) eglDestroySurface (display, x); }
};
struct ContextDestructor
{
void operator() (EGLContext x) const { if (x != EGL_NO_CONTEXT) eglDestroyContext (display, x); }
};
mutable std::mutex nativeHandleMutex;
OpenGLContext* juceContext = nullptr;
EGLSurface surface;
EGLContext context;
std::unique_ptr<std::remove_pointer_t<EGLSurface>, SurfaceDestructor> surface { EGL_NO_SURFACE };
std::unique_ptr<std::remove_pointer_t<EGLContext>, ContextDestructor> context { EGL_NO_CONTEXT };
GlobalRef surfaceHolderCallback;
@ -412,6 +409,9 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
};
EGLDisplay OpenGLContext::NativeContext::display = EGL_NO_DISPLAY;
EGLDisplay OpenGLContext::NativeContext::config;
//==============================================================================
void AndroidGLCallbacks::attachedToWindow (JNIEnv*, jobject /*this*/, jlong host)
{