Make the syphonframegrabber run opengl code properly

visualiser-refactor
James H Ball 2025-05-04 22:29:08 +01:00
rodzic 70898bf497
commit 3d633b110a
2 zmienionych plików z 43 dodań i 49 usunięć

Wyświetl plik

@ -4,12 +4,14 @@
// An invisible component that owns a persistent OpenGL context, always attached to the desktop. // An invisible component that owns a persistent OpenGL context, always attached to the desktop.
class InvisibleOpenGLContextComponent : public juce::Component { class InvisibleOpenGLContextComponent : public juce::Component {
public: public:
InvisibleOpenGLContextComponent() { InvisibleOpenGLContextComponent(juce::OpenGLRenderer* renderer) {
setSize(1, 1); // Minimal size setSize(1, 1); // Minimal size
setBounds(0, 0, 1, 1); // Minimal bounds setBounds(0, 0, 1, 1); // Minimal bounds
setVisible(true); setVisible(true);
setOpaque(false); setOpaque(false);
context.setComponentPaintingEnabled(false); context.setComponentPaintingEnabled(false);
context.setRenderer(renderer);
context.setContinuousRepainting(true);
context.attachTo(*this); context.attachTo(*this);
addToDesktop( addToDesktop(
juce::ComponentPeer::windowIsTemporary | juce::ComponentPeer::windowIsTemporary |

Wyświetl plik

@ -3,64 +3,56 @@
#include "InvisibleOpenGLContextComponent.h" #include "InvisibleOpenGLContextComponent.h"
class SyphonFrameGrabber : private juce::Thread, public juce::Component { class SyphonFrameGrabber : public juce::Component, public juce::OpenGLRenderer {
public: public:
SyphonFrameGrabber(SharedTextureManager& manager, juce::String server, juce::String app, ImageParser& parser, int pollMs = 8) SyphonFrameGrabber(SharedTextureManager& manager, juce::String server, juce::String app, ImageParser& parser)
: juce::Thread("SyphonFrameGrabber"), pollIntervalMs(pollMs), manager(manager), parser(parser) { : manager(manager), parser(parser) {
// Create the invisible OpenGL context component // Create the invisible component that hosts our OpenGL context
glContextComponent = std::make_unique<InvisibleOpenGLContextComponent>(); openGLComponent = std::make_unique<InvisibleOpenGLContextComponent>(this);
// Make sure the context is properly initialized before creating the receiver // Store the server/app info for initializing the receiver
glContextComponent->getContext().makeActive(); serverName = server;
appName = app;
// Create the receiver after the context is active
receiver = manager.addReceiver(server, app);
if (receiver) {
receiver->setUseCPUImage(true); // for pixel access
// Initialize the receiver with the active GL context
receiver->initGL();
}
// Release the context
glContextComponent->getContext().deactivateCurrentContext();
// Start the thread after everything is set up
startThread();
} }
~SyphonFrameGrabber() override { ~SyphonFrameGrabber() override {
juce::CriticalSection::ScopedLockType lock(openGLLock); // The InvisibleOpenGLContextComponent will detach the context in its destructor
openGLComponent = nullptr;
stopThread(500); // Clean up the receiver if needed
if (receiver) { if (receiver) {
manager.removeReceiver(receiver); manager.removeReceiver(receiver);
receiver = nullptr; receiver = nullptr;
} }
glContextComponent.reset();
} }
void run() override { // OpenGLRenderer interface implementation
while (!threadShouldExit()) { void newOpenGLContextCreated() override {
{ // Create the receiver in the GL context creation callback
juce::CriticalSection::ScopedLockType lock(openGLLock); receiver = manager.addReceiver(serverName, appName);
if (receiver) {
receiver->setUseCPUImage(true); // for pixel access
receiver->initGL();
}
}
bool activated = false; void renderOpenGL() override {
if (glContextComponent) { // This is called on the OpenGL thread at the frequency set by the context
activated = glContextComponent->getContext().makeActive(); if (receiver) {
} receiver->renderGL();
if (juce::OpenGLContext::getCurrentContext() != nullptr) {
receiver->renderGL(); if (isActive() && receiver->isConnected) {
} juce::Image image = receiver->getImage();
if (activated && glContextComponent) { parser.updateLiveFrame(image);
juce::OpenGLContext::deactivateCurrentContext();
}
if (isActive() && receiver->isConnected) {
juce::Image image = receiver->getImage();
parser.updateLiveFrame(image);
}
} }
wait(pollIntervalMs); }
}
void openGLContextClosing() override {
// Clean up in the context closing callback
if (receiver) {
manager.removeReceiver(receiver);
receiver = nullptr;
} }
} }
@ -80,12 +72,12 @@ public:
} }
private: private:
int pollIntervalMs;
SharedTextureManager& manager; SharedTextureManager& manager;
SharedTextureReceiver* receiver = nullptr; SharedTextureReceiver* receiver = nullptr;
ImageParser& parser; ImageParser& parser;
std::unique_ptr<InvisibleOpenGLContextComponent> glContextComponent; std::unique_ptr<InvisibleOpenGLContextComponent> openGLComponent;
juce::CriticalSection openGLLock; // To protect OpenGL context operations juce::String serverName;
juce::String appName;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SyphonFrameGrabber) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SyphonFrameGrabber)
}; };