OpenGL FSAA tips?


#1

Can any of you resident openGL experts suggest the best way to set up full scene antialiasing within a Juce project? If i try to use glEnable(GL_ARB_MULTISAMPLE) this is undefined so i know there’s more plumbing i have to do somewhere. I’m targeting PC, Mac and iOS, and I know this is a platform specific thing. I’ve been searching old threads, but can’t find a clear explanation of the way one would set this up (at least not one that’s clear to a noob like me).

thanks in advance, n


#2

I have some MSAA enabling code for iOS that i keep meaning to donate to Juce.

I didnt bother with the other platforms because they have enough resolution anyhow, but i had to write this for the ipad mini because its soooo low res that you can otherwise see the pixels really bad!


#3

Hey Hugh, yes please share. Yeah the mini is hard to look at after looking at the retina iPad isn’t it :slight_smile:

For what I’m doing I think I’ve got to get antialiasing working on mac/pc too. If I can do that I can get away with using simple ogl primitives. But right now all i’ve got is polygon smoothing, which is just worthless for what I need to do.

I’m trying to get my head around opengl and it’s quite a bear, especially since all of the books out there use glu/glut shortcuts to explain even basic concepts :roll: – if anyone has suggestions for other places to learn, I’m happy to hear them.


#4

ok, here’s a uDiff. This is the clearest i can make it unfortunately. I can generate HTML differences that look nice but i cant post them :confused:

Jules, the ios.h file is not too bad, you can just take the changes, but the horrible hack i have in OpenGLContext needs attention. basically i just hack in an extra flag for IOS. MSAA/FSAA selection should be made at a more general level, but i wanted to minimise Juce changes here.

the upside, its that MSAA is really worth it on the ipad mini. on the retina pads, it’s better to have a higher frame rate, since you’ve already got 320 dpi.

– hugh

[code]Left base folder: w:\sw\juce
Right base folder: c:\sw\juce
— modules\juce_opengl\opengl\juce_OpenGLContext.h 2013-07-19 11:55:19.000000000 +0100
+++ modules\juce_opengl\opengl\juce_OpenGLContext.h 2013-07-19 12:31:35.000000000 +0100
@@ -125,12 +125,18 @@
/
static OpenGLContext
getCurrentContext();

 /** Asynchronously causes a repaint to be made. */
 void triggerRepaint();
  • // if implemented, call before attach
  • void enableMSAA()
  • {
  •    msaa = true;
    
  • }
  • //==============================================================================
    /** If this context is backed by a frame buffer, this returns its ID number,
    or 0 if the context does not use a framebuffer.
    */
    unsigned int getFrameBufferID() const noexcept;

@@ -240,14 +246,15 @@
NativeContext* nativeContext;
OpenGLRenderer* renderer;
ScopedPointer attachment;
OpenGLPixelFormat pixelFormat;
void* contextToShareWith;
bool renderComponents;

  • bool msaa;

    CachedImage* getCachedImage() const noexcept;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLContext)
    };

#endif // JUCE_OPENGLCONTEXT_H_INCLUDED
— modules\juce_opengl\opengl\juce_OpenGLContext.cpp 2013-07-19 11:55:19.000000000 +0100
+++ modules\juce_opengl\opengl\juce_OpenGLContext.cpp 2013-07-19 12:31:16.000000000 +0100
@@ -24,25 +24,33 @@

class OpenGLContext::CachedImage : public CachedComponentImage,
public Thread
{
public:
CachedImage (OpenGLContext& c, Component& comp,

  •             const OpenGLPixelFormat& pixFormat, void* contextToShare)
    
  •             const OpenGLPixelFormat& pixFormat,
    
  •             void* contextToShare,
    
  •             bool msaa)
       : Thread ("OpenGL Rendering"),
         context (c), component (comp),
         scale (1.0),
        #if JUCE_OPENGL_ES
         shadersAvailable (true),
        #else
         shadersAvailable (false),
        #endif
         hasInitialised (false),
         needsUpdate (1)
    
    {
  •    // XXX HACK, need common constructor
    

+#ifdef JUCE_IOS

  •    nativeContext = new NativeContext (component, pixFormat, contextToShare, msaa);
    

+#else
nativeContext = new NativeContext (component, pixFormat, contextToShare);
+#endif

     if (nativeContext->createdOk())
         context.nativeContext = nativeContext;
     else
         nativeContext = nullptr;
 }

@@ -493,13 +507,13 @@

 void attach()
 {
     Component& comp = *getComponent();
     CachedImage* const newCachedImage = new CachedImage (context, comp,
                                                          context.pixelFormat,
  •                                                         context.contextToShareWith);
    
  •                                                         context.contextToShareWith, context.msaa);
       comp.setCachedComponentImage (newCachedImage);
       newCachedImage->start(); // (must wait until this is attached before starting its thread)
       newCachedImage->updateViewportSize (true);
    

    }

    void detach()
    @@ -520,12 +534,13 @@

//==============================================================================
OpenGLContext::OpenGLContext()
: nativeContext (nullptr), renderer (nullptr), contextToShareWith (nullptr),
renderComponents (true)
{

  • msaa = false;
    }

OpenGLContext::~OpenGLContext()
{
detach();
}
[/code]

[code]Left base folder: w:\sw\juce
Right base folder: c:\sw\juce
— modules\juce_opengl\native\juce_OpenGL_ios.h 2013-06-27 13:08:11.000000000 +0100
+++ modules\juce_opengl\native\juce_OpenGL_ios.h 2013-07-19 12:30:26.000000000 +0100
@@ -42,17 +42,22 @@

class OpenGLContext::NativeContext
{
public:
NativeContext (Component& component,
const OpenGLPixelFormat& pixelFormat,

  •               void* contextToShareWith)
    
  •               void* contextToShareWith,
    
  •               bool msaa = false)
       : frameBufferHandle (0), colorBufferHandle (0), depthBufferHandle (0),
         lastWidth (0), lastHeight (0), needToRebuildBuffers (false),
    
  •      swapFrames (0), useDepthBuffer (pixelFormat.depthBufferBits > 0)
    
  •      swapFrames (0), useDepthBuffer (pixelFormat.depthBufferBits > 0),
    
  •      useMSAA(msaa)
    
    {
  •    msaaColorHandle = 0;
    
  •    msaaBufferHandle = 0;
    
  •    JUCE_AUTORELEASEPOOL
       {
           ComponentPeer* const peer = component.getPeer();
           jassert (peer != nullptr);
    
           const Rectangle<int> bounds (peer->getComponent().getLocalArea (&component, component.getLocalBounds()));
    

@@ -64,12 +69,13 @@
view.hidden = NO;
view.backgroundColor = [UIColor blackColor];
view.userInteractionEnabled = NO;

         glLayer = (CAEAGLLayer*) [view layer];
         glLayer.contentsScale = Desktop::getInstance().getDisplays().getMainDisplay().scale;
  •    glLayer.opaque = true;
    
           [((UIView*) peer->getNativeHandle()) addSubview: view];
    
           context = [EAGLContext alloc];
    
           const NSUInteger type = kEAGLRenderingAPIOpenGLES2;
    

@@ -111,13 +117,17 @@

 bool makeActive() const noexcept
 {
     if (! [EAGLContext setCurrentContext: context])
         return false;
  •    glBindFramebuffer (GL_FRAMEBUFFER, frameBufferHandle);
    
  •    if (useMSAA)
    
  •        glBindFramebuffer (GL_FRAMEBUFFER, msaaBufferHandle);
    
  •    else
    
  •        glBindFramebuffer (GL_FRAMEBUFFER, frameBufferHandle);
    
  •    return true;
    

    }

    bool isActive() const noexcept
    {
    return [EAGLContext currentContext] == context;
    @@ -127,12 +137,22 @@
    {
    [EAGLContext setCurrentContext: nil];
    }

    void swapBuffers()
    {

  •    if (useMSAA)
    
  •    {
    
  •        glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, frameBufferHandle);
    
  •        glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, msaaBufferHandle);
    
  •        glResolveMultisampleFramebufferAPPLE();
    
  •        const GLenum discards[]  = {GL_COLOR_ATTACHMENT0,GL_DEPTH_ATTACHMENT};
    
  •        glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE,2,discards);
    
  •    }
    
  •    glBindRenderbuffer (GL_RENDERBUFFER, colorBufferHandle);
       [context presentRenderbuffer: GL_RENDERBUFFER];
    
       if (needToRebuildBuffers)
       {
           needToRebuildBuffers = false;
    

@@ -167,48 +187,71 @@

private:
JuceGLView* view;
CAEAGLLayer* glLayer;
EAGLContext* context;
GLuint frameBufferHandle, colorBufferHandle, depthBufferHandle;

  • GLuint msaaBufferHandle;

  • GLuint msaaColorHandle;
    int volatile lastWidth, lastHeight;
    bool volatile needToRebuildBuffers;
    int swapFrames;
    bool useDepthBuffer;

  • bool useMSAA;

    //==============================================================================
    void createGLBuffers()
    {
    glGenFramebuffers (1, &frameBufferHandle);
    glGenRenderbuffers (1, &colorBufferHandle);

  •    glBindFramebuffer (GL_FRAMEBUFFER, frameBufferHandle);
       glBindRenderbuffer (GL_RENDERBUFFER, colorBufferHandle);
    
  •    glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBufferHandle);
    
  •    bool ok = [context renderbufferStorage: GL_RENDERBUFFER fromDrawable: glLayer];
       jassert (ok); (void) ok;
    
  •    if (useDepthBuffer)
    
  •    {
    
  •        GLint width, height;
    
  •        glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
    
  •        glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
    
  •    GLint width, height;
    
  •    glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
    
  •    glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
    
  •    if (useMSAA)
    
  •    {
    
  •        glGenFramebuffers (1, &msaaBufferHandle);
    
  •        glGenRenderbuffers (1, &msaaColorHandle);
    
  •        glBindFramebuffer (GL_FRAMEBUFFER, msaaBufferHandle);
    
  •        glBindRenderbuffer (GL_RENDERBUFFER, msaaColorHandle);
    
  •        glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, 4, GL_RGBA8_OES, width, height);
    
  •        glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaColorHandle);
    
  •    }
    
  •    if (useDepthBuffer)
    
  •    {
           glGenRenderbuffers (1, &depthBufferHandle);
           glBindRenderbuffer (GL_RENDERBUFFER, depthBufferHandle);
    
  •        glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
    
  •    }
    
  •        if (useMSAA)
    
  •        {
    
  •            glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, width, height);
    
  •        }
    
  •        else
    
  •        {
    
  •            glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
    
  •        }
    
  •    glBindRenderbuffer (GL_RENDERBUFFER, colorBufferHandle);
    
  •    glBindFramebuffer (GL_FRAMEBUFFER, frameBufferHandle);
    
  •    glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBufferHandle);
    
  •    if (useDepthBuffer)
           glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBufferHandle);
    
  •    }
    
       jassert (glCheckFramebufferStatus (GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
       JUCE_CHECK_OPENGL_ERROR
    
  • }

    void freeGLBuffers()
    {
    JUCE_CHECK_OPENGL_ERROR
    [context renderbufferStorage: GL_RENDERBUFFER fromDrawable: nil];
    @@ -228,17 +271,32 @@
    if (depthBufferHandle != 0)
    {
    glDeleteRenderbuffers (1, &depthBufferHandle);
    depthBufferHandle = 0;
    }

  •    if (msaaBufferHandle)
    
  •    {
    
  •        glDeleteFramebuffers(1, &msaaBufferHandle);
    
  •        msaaBufferHandle = 0;
    
  •    }
    
  •    if (msaaColorHandle)
    
  •    {
    
  •        glDeleteRenderbuffers(1, &msaaColorHandle);
    
  •        msaaColorHandle = 0;
    
  •    }
    
  •    JUCE_CHECK_OPENGL_ERROR
    

    }

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
    };

//==============================================================================
bool OpenGLHelpers::isContextActive()
{
return [EAGLContext currentContext] != nil;
}
+
+#undef MSAA
[/code]


#5

Cool - I’ve implemented this now - let me know if it works!


#6

You need the ‘Blue Book’ - the OpenGL SuperBible. In the latest version, they do use a fair amount of provided code, as the new VBO stuff makes getting your first app done so tricky, but I believe it still has great info on creating contexts for each platform.

Side note - how would I tell which iOS platforms need MSAA and which don’t? Is it just enable for everything that doesn’t have retina?

Bruce


#7

Thanks Bruce, I’ll put that on my shopping list!


#8

Does anyone know if MSAA works on windows? My hardware allows for MSAA, I have apps running with it enabled outside of juce, however, when I :

 

context.setMultiSamplingEnabled(true);

it does not look like there is any MSAA.

 

Also, where do you tell juce how many MSAA samples you want? is the default 16x? 8x? 4x? 

 

Thanks!


#9

In the context of JUCE, I believe MSAA is not supported.

But various games on Windows support MSAA, and have for a while (e.g.: Skyrim)...


#10

I got it working on Windows. You need to specify the multisampling level using OpenGLPixelFormat, setting the multisamplingLevel, and calling OpenGLContext::setPixelFormat. Note that all of this has to happen before the OpenGLContext is attached to anything.