I still haven’t been able to repro the problem, but I do have a hypothesis:
Prior to Direct2D: Use WM_PAINT and vblank callbacks to drive painting, we would paint the non-OpenGL part of the window on every vblank.
After that commit, we only repaint the non-OpenGL part of the window after it receives a WM_PAINT message. WM_PAINT is sent at a lower priority than other types of message, so if the main thread is busy processing other messages, then the paint will be delayed.
I suspect that this change is delaying the repaint of the non-OpenGL window content until after the message thread is done processing messages related to resizing the GL context, which makes it look like the JUCE drawing is lagging behind the resize of the GL pane.
If that is the case, I’m not sure how best to address the issue, since this throttling behaviour is the main reason we made the switch to using WM_PAINT to trigger painting! If your paint routine is very expensive, it’s important that the system is able to process other events so that the app remains (somewhat) responsive.
Are there other approaches you could try to achieve the functionality you’re looking for? One idea would be to attach the OpenGL context to the top-level window (so that it contains the resizer bar). This way, the same GL context will be used to render the left-hand panel and the right-hand panel. You can then use glViewport() to adjust the portion of the window where the GL content will display.
I’ve adjusted your example code to show this approach. Please could you check whether this still causes any glitches?
class ColorPane final : public juce::Component
{
public:
explicit ColorPane(juce::Colour backgroundColour)
: bgColour(backgroundColour)
{
setOpaque(true);
}
void paint(juce::Graphics& g) override
{
g.fillAll(bgColour);
}
juce::MouseCursor getMouseCursor() override
{
return juce::MouseCursor::NormalCursor;
}
private:
const juce::Colour bgColour;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ColorPane)
};
class OGLContextPane final : public juce::Component,
public juce::OpenGLRenderer
{
public:
explicit OGLContextPane (juce::OpenGLContext& x)
: ctx (x)
{
setOpaque(true);
}
void paint(juce::Graphics&) override {}
void newOpenGLContextCreated() override
{
using namespace juce::gl;
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
const char* vertexShaderSource =
"#version 150\n"
"in vec2 position;\n"
"void main() {\n"
" gl_Position = vec4(position, 0.0, 1.0);\n"
"}\n";
const char* fragmentShaderSource =
"#version 150\n"
"out vec4 fragColour;\n"
"void main() {\n"
" fragColour = vec4(1.0, 0.0, 0.0, 1.0);\n"
"}\n";
program = std::make_unique<juce::OpenGLShaderProgram>(ctx);
if(!program->addVertexShader(vertexShaderSource) ||
!program->addFragmentShader(fragmentShaderSource) ||
!program->link())
{
DBG("Shader link failed: " << program->getLastError());
jassertfalse;
return;
}
position = std::make_unique<juce::OpenGLShaderProgram::Attribute>(*program, "position");
const float vertices[] = {
-0.7f, -0.7f,
0.7f, -0.7f,
0.7f, 0.7f,
-0.7f, -0.7f,
0.7f, 0.7f,
-0.7f, 0.7f
};
using namespace juce::gl;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
}
void renderOpenGL() override
{
using namespace juce::gl;
GLint oldViewport[4]{};
glGetIntegerv (GL_VIEWPORT, oldViewport);
const juce::ScopeGuard scope { [&] { glViewport (oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); } };
const auto peerScale = std::invoke ([&]
{
if (auto* p = getPeer())
return p->getPlatformScaleFactor();
return 1.0;
});
const auto logicalArea = getTopLevelComponent()->getLocalArea (this, getLocalBounds());
const auto physicalArea = logicalArea * getApproximateScaleFactorForComponent (getTopLevelComponent()) * peerScale;
glViewport (physicalArea.getX(), physicalArea.getY(), physicalArea.getWidth(), physicalArea.getHeight());
if(!juce::OpenGLHelpers::isContextActive() || program == nullptr || position == nullptr)
return;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
program->use();
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glEnableVertexAttribArray(position->attributeID);
glVertexAttribPointer(position->attributeID, 2, GL_FLOAT, GL_FALSE,
2 * sizeof(float), nullptr);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(position->attributeID);
}
void openGLContextClosing() override
{
using namespace juce::gl;
if(vbo != 0)
{
glDeleteBuffers(1, &vbo);
vbo = 0;
}
position.reset();
program.reset();
}
void resized() override {}
private:
juce::OpenGLContext& ctx;
std::unique_ptr<juce::OpenGLShaderProgram> program;
std::unique_ptr<juce::OpenGLShaderProgram::Attribute> position;
GLuint vbo = 0;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(OGLContextPane)
};
class MainComponent : public juce::Component
{
public:
MainComponent()
{
leftPane = std::make_unique<ColorPane>(juce::Colours::seagreen);
rightPane = std::make_unique<OGLContextPane> (ctx);
addAndMakeVisible (*leftPane);
addAndMakeVisible (*rightPane);
layout.setItemLayout (0, 50, 1000, -0.6);
layout.setItemLayout (1, 6, 6, 6);
layout.setItemLayout (2, 50, 1000, -0.4);
divider = std::make_unique<juce::StretchableLayoutResizerBar> (&layout, 1, true);
addAndMakeVisible (divider.get());
setSize (800, 500);
ctx.setOpenGLVersionRequired(juce::OpenGLContext::openGL4_3);
ctx.setRenderer(rightPane.get());
ctx.attachTo(*this);
ctx.setContinuousRepainting(true);
}
~MainComponent() override
{
ctx.detach();
}
void resized() override
{
juce::Component* items[] = { leftPane.get(), divider.get(), rightPane.get() };
layout.layOutComponents (items, 3, 0, 0, getWidth(), getHeight(), false, true);
}
private:
juce::OpenGLContext ctx;
std::unique_ptr<ColorPane> leftPane;
std::unique_ptr<OGLContextPane> rightPane;
std::unique_ptr<juce::StretchableLayoutResizerBar> divider;
juce::StretchableLayoutManager layout;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};