JUCE 8 Direct2D + Melatonin Blur issue

I haven’t look into the details yet, but it looks like my blur module’s shadows aren’t happy on JUCE 8 Direct2D. It looks like maybe when compositing from single color to rgba, it’s painting the color at full opacity vs. using the opacity values in the single color image…

I’ll take a look at see if i can get a simpler reproduction…

I’m also seeing these two errors when trying to configure JUCE 8 on GitHub Actions (trying to get the blur tests to run there)

 D:\a\melatonin_blur\melatonin_blur\Builds\_deps\juce-src\modules\juce_gui_basics\native\juce_Windowing_windows.cpp(2493,31):
  error C2065: 'DWMWCP_DONOTROUND': undeclared identifier
  [D:\a\melatonin_blur\melatonin_blur\Builds\_deps\juce-build\tools\extras\Build\juceaide\juceaide.vcxproj]



  D:\a\melatonin_blur\melatonin_blur\Builds\_deps\juce-src\modules\juce_gui_basics\native\juce_Windowing_windows.cpp(2494,42):
  error C2065: 'DWMWA_WINDOW_CORNER_PREFERENCE': undeclared identifier
  [D:\a\melatonin_blur\melatonin_blur\Builds\_deps\juce-build\tools\extras\Build\juceaide\juceaide.vcxproj]

I’ve tried to use the latest Windows 11 sdk available, which is 10.0.22621.0 but it is either not taking or not helping…

OK, here’s something strange. All of my tests are failing locally, it seems like part of the reason is that Image#getPixelAt is always returning transparent black. For example, this fails:

    juce::Image result (juce::Image::ARGB, 9, 9, true);
    juce::Graphics g (result);
    g.fillAll (juce::Colours::white);
    jassert (result.getPixelAt (2, 2).toDisplayString (true) == "FFFFFFFF");

The behavior of the Graphics object for the D2D and OpenGL renderers is that the image is not completely written/flushed intil the Graphics object goes out of scope. The example you provided should work if you add a scope around the Graphics object. If that’s still broken, please let us know.

juce::Image result (juce::Image::ARGB, 9, 9, true);
{
    juce::Graphics g (result);
    g.fillAll (juce::Colours::white);
}
jassert (result.getPixelAt (2, 2).toDisplayString (true) == "FFFFFFFF");

This has come up at least once in the past, so we should probably update the docs to make this a bit clearer.

2 Likes

Thanks Reuben! Glad to hear I’m not losing it :smiley:

It doesn’t look like there’s another way to sync/flush, so I’ll go restructure my tests and see what I run into next!

Documenting sounds useful. Maybe it could be part of the general JUCE 8 release notes / docs too. I tried to poke around to see if i could disable Direct2D (JUCE_DIRECT2D=0) and use the old renderer on JUCE 8, but it seems like that isn’t possible?

1 Like

Hi @sudara

You an set the renderer at runtime:

    void setDirect2D(bool enable) {
        juce::Timer::callAfterDelay(0, [this, enable] {
            jassert(getPeer());
            if (!getPeer()) return;
            getPeer()->setCurrentRenderingEngine(enable ? 1 : 0);
        });
    }

The callAfterDelay is there so you can use it directly in a component constructor, when getPeer() is still false.

1 Like

:exploding_head: amazing!

Back in biz!

It looks like this is also true for writing pixel data with juce::Image::BitmapData.

I encountered one last baby edge case in updating blur to JUCE 8. This is in font rendering – unrelated to D2D (occurs on all platforms).

The following code:

            juce::Image result (juce::Image::ARGB, 9, 9, true);
            juce::Graphics g (result);
            g.fillAll (juce::Colours::white);
            g.setColour (juce::Colours::black);
            g.drawText ("O", 0, 0, 9, 9, juce::Justification::centred, false);

JUCE 7 produces this:

On JUCE 8, the image remains completely white.

I’m pretty sure this is esoterica — sizes of the default font differ slightly between versions and the text size was almost at the limit of not being painted on JUCE 7. For example if I g.setFont(g.getCurrentFont().withHeight(16)); on JUCE 7, I also get a blank image.

Maybe there should be an assertion here vs. just the empty image? But worst case someone, someday will get value from this…

For anyone else in the future running into this issue, the warning proceeding this error gives a clue:

CUSTOMBUILD : warning : Environment variable CMAKE_GENERATOR_PLATFORM will
  be ignored, because CMAKE_GENERATOR is not set.

Basically, I forgot to specify -G Ninja and without a generator it bailed out…

Good to go!

Even though this project really exercises juce::Graphics and friends, upgrading to JUCE 8 was relatively painless, much more so than other frameworks I’ve used in the past.

5 Likes

The JUCE_DIRECT2D flag has been removed.

As @fuo pointed out, you can switch the renderer for a window by calling setCurrentRenderingEngine. I usually set the renderer from the parentHierarchyChanged callback; here’s an example.

Note that this will only set the renderer for the current window. If you are rendering to an Image, you will still be using Direct2D by default unless you explicitly specify a software image.

Matt

2 Likes

This is great, thanks!

@sudara You can also force the software renderer when creating your target-image like this:

auto shadowImage = juce::Image ( juce::Image::SingleChannel, area.getWidth (), area.getHeight (), true, juce::SoftwareImageType () );

Now you don’t need to add all the scopes, and I’m sure that (depending on the size and complexity, of course) this might be faster than:

  1. Create a native image on the GPU
  2. Rendering into it via D2D calls
  3. Flushing the draw-queue
  4. Copying the image back to RAM
4 Likes

@matt, it would be useful to have a convertedToType Image method to convert from one type to another.

This would for example let us use the native renderer to capture a component (as the differences in font rendering are pretty visible between the Direct2D and software renderers, especially for small fonts), and then convert it to software for further manipulations (eg animated fading, etc).

1 Like

Check out ImageType::convert:

https://docs.juce.com/master/classImageType.html#ae9b3581750ee9df9c1f3c45de5915d35

Although you can do animated fading right now without converting the image type.

Matt

2 Likes

Thanks! Matt and I chatted yesterday about exactly this option, as in this case it’s literally CPU blurring (we were discussing maybe geeking out over GPU backed mela blurs too, though!). I might leave the scope in the tests since speed isn’t essential there, but i’ll prob have to switch to software backed for the single channel shadow images. I’m curious on the benchmark difference…