Optimizing the drawing

Discussion and support for general JUCE issues

Re: Optimizing the drawing

Postby zamrate » Thu Jun 07, 2012 8:13 am

It makes no big difference in speed here. Tried it. BTW, this example uses 60 components. Now imagine having hundreds of components on screen. I don't wanna know how many intersections have to be checked there, how many regions have to be excluded etc..
User avatar
zamrate
JUCE UberWeenie
 
Posts: 1081
Joined: Mon Sep 24, 2007 5:33 pm

Re: Optimizing the drawing

Postby jules » Thu Jun 07, 2012 8:34 am

zamrate, are you actually using an optimised release build??

Your profile looks very suspicious - the top entry is Array<Rectangle>::getReference(), which is a method that the compiler should have optimised down to a couple of machine instructions, *unless* you've got debugging enabled, in which case it would do some assertion checking using isPositiveAndBelow(), which is the second item in your list..
User avatar
jules
Fearless Leader
 
Posts: 17229
Joined: Mon Sep 06, 2004 9:03 am
Location: London, UK

Re: Optimizing the drawing

Postby zamrate » Thu Jun 07, 2012 8:48 am

This was a debug build, yes, BUT: the difference is also there when making the release build on Windows, just tried it.
In fact, on Windows, with an optimized release build, I get 12% CPU usage when using getPeer()::performanAnyPendingRepaints() on each 30 level meters, and above 20% CPU usage when not doing that. So this whole clipping regions stuff really takes time. I had setOpaque() and setPaintingIsUnclipped() both set to true on all my components FWIW.
I suggest doing some performance tests with lots of components on screen, as in a real app with possibly hundreds of components.
User avatar
zamrate
JUCE UberWeenie
 
Posts: 1081
Joined: Mon Sep 24, 2007 5:33 pm

Re: Optimizing the drawing

Postby TheVinn » Thu Jun 07, 2012 2:14 pm

zamrate wrote:I had setOpaque() and setPaintingIsUnclipped() both set to true on all my components FWIW.


Try leaving setPaintingIsUnclipped() to false. Ironically, setting this causes MORE clip rectangles in the RectangleList rather than less! "Unclipped" doesn't mean ignore all clipping, it means don't reduce the clipping region to the intersection of the clip region and the component's bounds.

In a normal paint scenario, paintComponentAndChildren() should reduce your 60 rectangles down to 1 when an individual Component paints (since its bounds only overlaps one dirty rectangle). But when you set the unclipped painting, it never removes the rectangles that don't overlap the Component bounds. Therefore, you end up in paint() with a RectangleList that has 60 rectangles instead of 1 even though they are non-overlapping.

At least, I *think* thats how it works (Jules?)
Open Source: LayerEffects, VFLib, SimpleDJ, DSP Filters, LuaBridge, JUCE, FreeType, TagLib
"This isn't a big project, it shouldn't take long." - Jules
User avatar
TheVinn
JUCE UberWeenie
 
Posts: 2976
Joined: Sat Aug 29, 2009 11:31 am
Location: Marina del Rey, California

Re: Optimizing the drawing

Postby TheVinn » Thu Jun 07, 2012 2:15 pm

You never explained why anti-aliased polygon drawing is taking place either, when it should all be fillRects()
Open Source: LayerEffects, VFLib, SimpleDJ, DSP Filters, LuaBridge, JUCE, FreeType, TagLib
"This isn't a big project, it shouldn't take long." - Jules
User avatar
TheVinn
JUCE UberWeenie
 
Posts: 2976
Joined: Sat Aug 29, 2009 11:31 am
Location: Marina del Rey, California

Re: Optimizing the drawing

Postby jules » Thu Jun 07, 2012 3:11 pm

Try leaving setPaintingIsUnclipped() to false.


Yes, that's worth a try. If you've written a paint method that's *guaranteed* not to draw beyond its own bounds, then it'll save a bunch of rectangle list operations. Read the comments for it though, you have to be a bit careful when you use it.
User avatar
jules
Fearless Leader
 
Posts: 17229
Joined: Mon Sep 06, 2004 9:03 am
Location: London, UK

Re: Optimizing the drawing

Postby TheVinn » Thu Jun 07, 2012 3:18 pm

jules wrote:it'll save a bunch of rectangle list operations.


The only operations that it saves are when there are components that overlap on top of the component being drawn. For the use-case in the original post, nothing is saved (since no components are on top). In fact, turning off clipping causes the RectangleList to be EVEN BIGGER! This is because reduceClipRegion() doesn't get called when child.flags.dontClipGraphicsFlag == true:

juce_Component.cpp
Code: Select all
...
                if (child.flags.dontClipGraphicsFlag)
                {
                    child.paintWithinParentContext (g);
                }
                else if (g.reduceClipRegion (child.getBounds()))
                {
...


paintWithinParentContext() is getting a RectangleList with 60 rectangles in it, instead of just one.

Still haven't heard an explanation for why anti-aliased polygons are being drawn.
Open Source: LayerEffects, VFLib, SimpleDJ, DSP Filters, LuaBridge, JUCE, FreeType, TagLib
"This isn't a big project, it shouldn't take long." - Jules
User avatar
TheVinn
JUCE UberWeenie
 
Posts: 2976
Joined: Sat Aug 29, 2009 11:31 am
Location: Marina del Rey, California

Re: Optimizing the drawing

Postby zamrate » Thu Jun 07, 2012 5:53 pm

Still haven't heard an explanation for why anti-aliased polygons are being drawn.


This is the call stack on Windows, using software-renderer. Seems fillRect() fills line by line? Anyway, the class you though that would not be in there, SolidColourTableEdgeRenderer is definitely in there!

DrawSpeedTest.exe!juce::SoftwareRendererClasses::SolidColourEdgeTableRenderer<juce::PixelRGB,0>::handleEdgeTableLineFull(const int x=54, const int width=10) Line 96 C++
DrawSpeedTest.exe!juce::SoftwareRendererClasses::ClipRegion_RectangleList::SubRectangleIterator::iterate<juce::SoftwareRendererClasses::SolidColourEdgeTableRenderer<juce::PixelRGB,0> >(juce::SoftwareRendererClasses::SolidColourEdgeTableRenderer<juce::PixelRGB,0> & r={...}) Line 1562 C++
DrawSpeedTest.exe!juce::SoftwareRendererClasses::ClipRegionBase::renderSolidFill<juce::SoftwareRendererClasses::ClipRegion_RectangleList::SubRectangleIterator,juce::PixelRGB>(juce::SoftwareRendererClasses::ClipRegion_RectangleList::SubRectangleIterator & iter={...}, const juce::Image::BitmapData & destData={...}, const juce::PixelARGB & fillColour={...}, const bool replaceContents=false, juce::PixelRGB * __formal=0x00000000) Line 1165 C++
DrawSpeedTest.exe!juce::SoftwareRendererClasses::ClipRegion_RectangleList::fillRectWithColour(juce::Image::BitmapData & destData={...}, const juce::Rectangle<int> & area={...}, const juce::PixelARGB & colour={...}, bool replaceContents=false) Line 1475 + 0x18 bytes C++
> DrawSpeedTest.exe!juce::LowLevelGraphicsSoftwareRenderer::SavedState::fillRect(const juce::Rectangle<int> & r={...}, const bool replaceContents=false) Line 1870 + 0x8f bytes C++
DrawSpeedTest.exe!juce::LowLevelGraphicsSoftwareRenderer::fillRect(const juce::Rectangle<int> & r={...}, const bool replaceExistingContents=false) Line 2185 C++
DrawSpeedTest.exe!juce::Graphics::fillRect(int x=0, int y=0, int width=10, int height=300) Line 335 + 0x41 bytes C++
DrawSpeedTest.exe!LevelMeter::paint(juce::Graphics & g={...}) Line 29 C++
DrawSpeedTest.exe!juce::Component::paintComponent(juce::Graphics & g={...}) Line 1805 C++[/code]
DrawSpeedTest.exe!juce::Component::paintComponentAndChildren(juce::Graphics & g={...}) Line 1821 C++
DrawSpeedTest.exe!juce::Component::paintEntireComponent(juce::Graphics & g={...}, const bool ignoreAlphaLevel=false) Line 1919 C++
DrawSpeedTest.exe!juce::Component::paintWithinParentContext(juce::Graphics & g={...}) Line 1811 C++
DrawSpeedTest.exe!juce::Component::paintComponentAndChildren(juce::Graphics & g={...}) Line 1854 + 0xc bytes C++
DrawSpeedTest.exe!juce::Component::paintEntireComponent(juce::Graphics & g={...}, const bool ignoreAlphaLevel=false) Line 1919 C++
DrawSpeedTest.exe!juce::Component::paintWithinParentContext(juce::Graphics & g={...}) Line 1811 C++
DrawSpeedTest.exe!juce::Component::paintComponentAndChildren(juce::Graphics & g={...}) Line 1875 C++
DrawSpeedTest.exe!juce::Component::paintEntireComponent(juce::Graphics & g={...}, const bool ignoreAlphaLevel=true) Line 1919 C++
DrawSpeedTest.exe!juce::ComponentPeer::handlePaint(juce::LowLevelGraphicsContext & contextToPaintTo={...}) Line 137 C++
User avatar
zamrate
JUCE UberWeenie
 
Posts: 1081
Joined: Mon Sep 24, 2007 5:33 pm

Re: Optimizing the drawing

Postby TheVinn » Thu Jun 07, 2012 6:02 pm

zamrate wrote:Seems fillRect() fills line by line?


Line by line is how polygons get rasterized. JUCE feels that you need a 4-point polygon fill instead of the special case of solid colour opaque rectangle fill with no transform.

Code: Select all
juce::LowLevelGraphicsSoftwareRenderer::SavedState::fillRect(const juce::Rectangle<int> & r={...}, const bool replaceContents=false)  Line 1870 + 0x8f bytes   C++

juce::LowLevelGraphicsSoftwareRenderer::fillRect(const juce::Rectangle<int> & r={...}, const bool replaceExistingContents=false)  Line 2185   C++


Get a breakpoint in your call to Graphics::fillRect() from Component::paint() and step into SoftwareRendererSavedState::fillRect (const Rectangle<int>& r, const bool replaceContents) (juce_RenderingHelpers.h) and figure out why its going into fillShape() or fillPath() instead of fillRectWithColour():

juce_RenderingHelpers.h
Code: Select all
    void fillRect (const Rectangle<int>& r, const bool replaceContents)
    {
        if (clip != nullptr)
        {
            if (transform.isOnlyTranslated)
            {
                if (fillType.isColour())
                {
                    Image::BitmapData destData (image, Image::BitmapData::readWrite);
                    clip->fillRectWithColour (destData, transform.translated (r), fillType.colour.getPixelARGB(), replaceContents);
                }
                else
                {
                    const Rectangle<int> totalClip (clip->getClipBounds());
                    const Rectangle<int> clipped (totalClip.getIntersection (transform.translated (r)));

                    if (! clipped.isEmpty())
                        fillShape (new ClipRegions::RectangleListRegion (clipped), false);
                }
            }
            else
            {
                Path p;
                p.addRectangle (r);
                fillPath (p, AffineTransform::identity);
            }
        }
    }


It seems that either fillType.isColour() is false for you, or you set a matrix where transform.isOnlyTranslated is false.
Open Source: LayerEffects, VFLib, SimpleDJ, DSP Filters, LuaBridge, JUCE, FreeType, TagLib
"This isn't a big project, it shouldn't take long." - Jules
User avatar
TheVinn
JUCE UberWeenie
 
Posts: 2976
Joined: Sat Aug 29, 2009 11:31 am
Location: Marina del Rey, California

Re: Optimizing the drawing

Postby zamrate » Thu Jun 07, 2012 6:18 pm

Listen, you have the call stack there. I made it especially for you. And you can clearly see that fillRect() calls fillRectWithColour() there?!!
BTW not only paths are filled using the EdgeTableRenderer, everything is, even fonts, AFAIK.
User avatar
zamrate
JUCE UberWeenie
 
Posts: 1081
Joined: Mon Sep 24, 2007 5:33 pm

Re: Optimizing the drawing

Postby TheVinn » Thu Jun 07, 2012 6:47 pm

zamrate wrote:Listen, you have the call stack there. I made it especially for you. And you can clearly see that fillRect() calls fillRectWithColour() there?!!
BTW not only paths are filled using the EdgeTableRenderer, everything is, even fonts, AFAIK.


I see what you mean, you're right. Fonts are paths so it makes sense they would render as anti-aliased polygons. But I'm puzzled as to why its going through the pixel blending code. It used to be the case that when a 100% opaque colour rectangle on integer coordinates with only a translation was drawn, it bypassed the blending and used a scanline renderer that just filled each row with solid colours. Maybe this has something to do with replaceExisting:

Code: Select all
        forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const noexcept
        {
            if (replaceExisting)
                linePixels[x].set (sourceColour);
            else
                linePixels[x].blend (sourceColour, (uint32) alphaLevel);
        }


From your profile it looks like you're going through blend() instead of set(). I would think that juce_GraphicsContext.cpp or some related part would set "replaceExisting" to true when the fill is a solid colour with 100% opacity. Try this, in:

juce_Graphics.cpp
Code: Select all
void Graphics::fillRect (int x, int y, int width, int height) const
{
    // passing in a silly number can cause maths problems in rendering!
    jassert (areCoordsSensibleNumbers (x, y, width, height));

    context->fillRect (Rectangle<int> (x, y, width, height), false);
}


Replace "false" with "true" in the call to fillRect() and post the profile and performance results.
Open Source: LayerEffects, VFLib, SimpleDJ, DSP Filters, LuaBridge, JUCE, FreeType, TagLib
"This isn't a big project, it shouldn't take long." - Jules
User avatar
TheVinn
JUCE UberWeenie
 
Posts: 2976
Joined: Sat Aug 29, 2009 11:31 am
Location: Marina del Rey, California

Re: Optimizing the drawing

Postby zamrate » Thu Jun 07, 2012 6:58 pm

I'm not on Mac right now (to profile), but all I can tell you is that the gfx performance problems I had have nothing to do with the blending per se.
The problem was definitely due to this whole clipping regions stuff and nothing else! As said, calling performAnyPendingRepaints() on the first 30 level meters, then on the next 30 level meters instantly solved the problem (12% cpu usage versus >20%).
User avatar
zamrate
JUCE UberWeenie
 
Posts: 1081
Joined: Mon Sep 24, 2007 5:33 pm

Re: Optimizing the drawing

Postby TheVinn » Thu Jun 07, 2012 7:15 pm

See my comment regarding setPaintingIsUnclipped(). You should be using false (the default) not true, or else it will result in MORE rectangles in your RectangleList rather than less.

Regardless, your first profile shows PixelARGB::getARGB() as the top time consumer and that could be a consequence of replaceExisting == false.
Open Source: LayerEffects, VFLib, SimpleDJ, DSP Filters, LuaBridge, JUCE, FreeType, TagLib
"This isn't a big project, it shouldn't take long." - Jules
User avatar
TheVinn
JUCE UberWeenie
 
Posts: 2976
Joined: Sat Aug 29, 2009 11:31 am
Location: Marina del Rey, California

Re: Optimizing the drawing

Postby zamrate » Thu Jun 07, 2012 7:27 pm

I did try with both setPaintingIsUnclipped(true) or setPaintingIsUnclipped(false) on all level meters. Doesn't change much in the whole. I think I've mentioned that a couple of times already in my previous posts.
As for the speed of the actual painting itself - my renderer is anyway 2x-4x faster than JUCE (I've implemented my own software renderer for fonts, blits, lines and rectangles). So I don't even have to look at these bottlenecks (blending etc..).
User avatar
zamrate
JUCE UberWeenie
 
Posts: 1081
Joined: Mon Sep 24, 2007 5:33 pm

Re: Optimizing the drawing

Postby TheVinn » Fri Jun 08, 2012 6:11 pm

Replace Graphics::fillRect() with Graphics::getInternalContext()->fillRect (..., true);

^^ Disregard that
Last edited by TheVinn on Fri Jun 08, 2012 6:21 pm, edited 1 time in total.
Open Source: LayerEffects, VFLib, SimpleDJ, DSP Filters, LuaBridge, JUCE, FreeType, TagLib
"This isn't a big project, it shouldn't take long." - Jules
User avatar
TheVinn
JUCE UberWeenie
 
Posts: 2976
Joined: Sat Aug 29, 2009 11:31 am
Location: Marina del Rey, California

PreviousNext

Return to General JUCE discussion

Who is online

Users browsing this forum: Bing [Bot], Google [Bot], Google Feedfetcher and 2 guests