Bug 45812 - Filter builder should be able to follow the filter object dependencies
Summary: Filter builder should be able to follow the filter object dependencies
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: SVG (show other bugs)
Version: 528+ (Nightly build)
Hardware: PC OS X 10.5
: P2 Enhancement
Assignee: Nobody
URL:
Keywords:
Depends on:
Blocks: 68469 26389
  Show dependency treegraph
 
Reported: 2010-09-15 01:36 PDT by Zoltan Herczeg
Modified: 2014-05-12 05:54 PDT (History)
2 users (show)

See Also:


Attachments
Draft patch (16.67 KB, patch)
2010-09-15 01:36 PDT, Zoltan Herczeg
zimmermann: review-
Details | Formatted Diff | Diff
Draft patch 2 (15.51 KB, patch)
2010-09-15 06:47 PDT, Zoltan Herczeg
no flags Details | Formatted Diff | Diff
Draft patch 3 (15.52 KB, patch)
2010-09-22 02:51 PDT, Zoltan Herczeg
no flags Details | Formatted Diff | Diff
Draft patch 4 (5.60 KB, patch)
2010-09-22 04:50 PDT, Zoltan Herczeg
krit: review-
Details | Formatted Diff | Diff
patch (7.81 KB, patch)
2010-09-24 06:59 PDT, Zoltan Herczeg
krit: review-
Details | Formatted Diff | Diff
Patch 2 (7.86 KB, patch)
2010-09-27 03:42 PDT, Zoltan Herczeg
krit: review+
krit: commit-queue-
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Zoltan Herczeg 2010-09-15 01:36:12 PDT
This might be a complex patch, so the name of the bug can be changed later.
Comment 1 Zoltan Herczeg 2010-09-15 01:36:39 PDT
Created attachment 67655 [details]
Draft patch
Comment 2 Nikolas Zimmermann 2010-09-15 03:38:51 PDT
Comment on attachment 67655 [details]
Draft patch

View in context: https://bugs.webkit.org/attachment.cgi?id=67655&action=prettypatch

> WebCore/rendering/RenderSVGResourceFilterPrimitive.h:39
> +    RenderSVGResourceFilterPrimitive(SVGFilterPrimitiveStandardAttributes* filterPrimitiveElement);
You should omit the argument name here, it's redundant.

> WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:77
> +        return effect;
I'd prefer a return 0 here.

> WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:85
> +        if (m_lastEffectReference1.get() != effect)
No need for the .get() here.

> WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:91
> +    if (m_lastEffectReference1.get() == effect || m_lastEffectReference2.get() == effect)
Ditto. RefPtr defines operator*.

> WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:95
> +        if (m_lastEffectFurtherReferences[i].get() == effect)
Ditto.

> WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:105
> +    ASSERT(m_effectReferences.find(effect) == m_effectReferences.end());
ASSERT(!m_effectReferences.contains(effect)): ?

> WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:108
> +    if (m_lastEffectReference1.get())
You can omit the .get()

> WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:110
> +    if (m_lastEffectReference2.get())
Ditto.

> WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:112
> +    if (m_lastEffectFurtherReferences.size()) {
Use !isEmpty()

> WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:128
> +    ASSERT(!m_lastEffectFurtherReferences.size());
ASSERT(m_last..isEmpty())

> WebCore/svg/graphics/filters/SVGFilterBuilder.h:46
>          FilterEffect* getEffectById(const AtomicString& id) const;
The id parameter is redundant.

> WebCore/svg/graphics/filters/SVGFilterBuilder.h:48
> +        FilterEffect* getAndRecordEffectById(const AtomicString& id);
Ditto.

> WebCore/svg/graphics/filters/SVGFilterBuilder.h:55
> +            ASSERT(m_effectReferences.find(effect) != m_effectReferences.end());
ASSERT(m_effectReferences.contains(...)

> WebCore/svg/graphics/filters/SVGFilterBuilder.h:67
> +            ASSERT(m_effectReferences.find(effect) != m_effectReferences.end());
ASSERT(m_effectReferences.contains(...)

> WebCore/svg/graphics/filters/SVGFilterBuilder.h:85
> +        RefPtr<FilterEffect> m_lastEffectReference1;
It's not immediately clear to me, why those have to be refcounted at all.

I'll leave the actual review to Dirk, just wanted to share some notes.
Comment 3 Zoltan Herczeg 2010-09-15 06:47:27 PDT
Created attachment 67671 [details]
Draft patch 2

Based on discussion on #webkit
Comment 4 Dirk Schulze 2010-09-16 01:31:29 PDT
Comment on attachment 67671 [details]
Draft patch 2

View in context: https://bugs.webkit.org/attachment.cgi?id=67671&action=prettypatch

> WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:102
> +void SVGFilterBuilder::appendEffectToEffectReferences(RefPtr<FilterEffect> effect)
> +{
> +    // The effect must be a newly created filter effect.
> +    ASSERT(!m_effectReferences.contains(effect));
> +    m_effectReferences.add(effect, EffectReferences());
> +
> +    int size = m_recordedEffects.size();
> +    for (int i = 0; i < size; ++i) {
> +        // Since targetEffect is a newly created object, it cannot already be added to the list.
> +        ASSERT(m_effectReferences.contains(m_recordedEffects[i]));
> +        m_effectReferences.find(m_recordedEffects[i])->second.append(effect);
> +    }
> +    m_recordedEffects.clear();
> +}
> +

I won't review the code like Niko right now. I'm more thinking about the process itself.

At the moment you record every request of filter effects in m_recordedEffects. Create the new effect and add the new created effect to the requester list of the requested effects afterwards (compilcated phrasing, I know ;-)).

I wounder if it won't be easier and more understandable, if we skip the recording of the requested effects and ask the new created effect directly for its requested effects.
This may cause us to let effects store their input effects in a Vector. Not sure if this is a noticeable performance or memory waste. But it could help to make the layout of primitives much easier. When you look at the current FilterEffect code, we have a lot of functions to determine the subregion, just because filter effects have different counts of input effects. We could simplify the code a lot if we allways use a vector for input effects. This would also mean that we can get rid of a lot of code in the FE* objects. IIRC Firefox is going the same way.

What do you think?
Comment 5 Dirk Schulze 2010-09-18 12:24:39 PDT
I added a patch for the input effect Vector to bug 45612.
Comment 6 Zoltan Herczeg 2010-09-22 02:51:31 PDT
Created attachment 68354 [details]
Draft patch 3

Updated patch
Comment 7 Zoltan Herczeg 2010-09-22 04:50:45 PDT
Created attachment 68357 [details]
Draft patch 4
Comment 8 Dirk Schulze 2010-09-22 05:31:27 PDT
Comment on attachment 68357 [details]
Draft patch 4

View in context: https://bugs.webkit.org/attachment.cgi?id=68357&action=review

Please add a ChangeLog with detailed descriptions next time. The patch looks great and I'm looking forward to see the next patch.

> WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:89
> +void SVGFilterBuilder::appendEffectToEffectReferences(RefPtr<FilterEffect> effect)
> +{
> +    // The effect must be a newly created filter effect.
> +    ASSERT(!m_effectReferences.contains(effect));
> +    m_effectReferences.add(effect, FilterEffectVector());
> +
> +    FilterEffectVector& inputEffects = effect->inputEffects();
> +    HashSet<RefPtr<FilterEffect> > seenEffects;
> +    int size = inputEffects.size();
> +
> +    for (int i = 0; i < size; ++i) {
> +        // Since targetEffect is a newly created object, it cannot already be added to the list.
> +        ASSERT(m_effectReferences.contains(inputEffects[i]));
> +        if (seenEffects.contains(inputEffects[i]))
> +          continue;
> +        m_effectReferences.find(inputEffects[i])->second.append(effect);
> +        seenEffects.add(inputEffects[i]);
> +    }
> +}

I'm not sure why you want to have a FilterEffectVector instead of a Set? At the moment you create a set for every call of  appendEffectToEffectReferences. Wouldn't it be more useful to put a set into m_effectReferences:
HashMap<RefPtr<FilterEffect>, Set<RefPtr<FilterEffect> > > ? So you just need check the set, if the effect is already in there: contains(effect), and it if not. Niko already asked it, do we need the RefPtr's? Can't we just store the pointer?

> WebCore/svg/graphics/filters/SVGFilterBuilder.h:51
> +        inline FilterEffectVector& getReferences(FilterEffect* effect)
> +        {
> +            // Only allowed for effects belongs to this builder.
> +            ASSERT(m_effectReferences.contains(effect));
> +            return m_effectReferences.find(effect)->second;
> +        }

Who is using that? Also this one liner don't need to be in an extra function.

> WebCore/svg/graphics/filters/SVGFilterBuilder.h:63
> +        inline void addBuiltinEffects()
> +        {
> +            HashMap<AtomicString, RefPtr<FilterEffect> >::iterator end = m_builtinEffects.end();
> +            for (HashMap<AtomicString, RefPtr<FilterEffect> >::iterator iterator = m_builtinEffects.begin(); iterator != end; ++iterator)
> +                 m_effectReferences.add(iterator->second, FilterEffectVector());
> +        }

You should call this during initialization. Not sure if we get a leak if we leaf it in on clearEffects and call the DTor of FilterBuilder.
Comment 9 Zoltan Herczeg 2010-09-22 07:06:13 PDT
> > WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:89
> > +void SVGFilterBuilder::appendEffectToEffectReferences(RefPtr<FilterEffect> effect)
> > +{
> > +    // The effect must be a newly created filter effect.
> > +    ASSERT(!m_effectReferences.contains(effect));
> > +    m_effectReferences.add(effect, FilterEffectVector());
> > +
> > +    FilterEffectVector& inputEffects = effect->inputEffects();
> > +    HashSet<RefPtr<FilterEffect> > seenEffects;
> > +    int size = inputEffects.size();
> > +
> > +    for (int i = 0; i < size; ++i) {
> > +        // Since targetEffect is a newly created object, it cannot already be added to the list.
> > +        ASSERT(m_effectReferences.contains(inputEffects[i]));
> > +        if (seenEffects.contains(inputEffects[i]))
> > +          continue;
> > +        m_effectReferences.find(inputEffects[i])->second.append(effect);
> > +        seenEffects.add(inputEffects[i]);
> > +    }
> > +}
> 
> I'm not sure why you want to have a FilterEffectVector instead of a Set? At the moment you create a set for every call of  appendEffectToEffectReferences. Wouldn't it be more useful to put a set into m_effectReferences:
> HashMap<RefPtr<FilterEffect>, Set<RefPtr<FilterEffect> > > ? So you just need check the set, if the effect is already in there: contains(effect), and it if not. Niko already asked it, do we need the RefPtr's? Can't we just store the pointer?

If we would use a 'set', we would also create a new 'set' for each effect:
+    m_effectReferences.add(effect, FilterEffectVector());
would be
+    m_effectReferences.add(effect, set());

Using a vector here is a speed optimization, since iterating through a vector (needed by the 'invalidation' process) is much cheaper than iterating through a set. Moreover, we only append items during initialization, but invalidation will use the iteration many times.

> > WebCore/svg/graphics/filters/SVGFilterBuilder.h:51
> > +        inline FilterEffectVector& getReferences(FilterEffect* effect)
> > +        {
> > +            // Only allowed for effects belongs to this builder.
> > +            ASSERT(m_effectReferences.contains(effect));
> > +            return m_effectReferences.find(effect)->second;
> > +        }
> 
> Who is using that? Also this one liner don't need to be in an extra function.

Nobody at the moment, but it will be needed in the future, I am sure.

> > WebCore/svg/graphics/filters/SVGFilterBuilder.h:63
> > +        inline void addBuiltinEffects()
> > +        {
> > +            HashMap<AtomicString, RefPtr<FilterEffect> >::iterator end = m_builtinEffects.end();
> > +            for (HashMap<AtomicString, RefPtr<FilterEffect> >::iterator iterator = m_builtinEffects.begin(); iterator != end; ++iterator)
> > +                 m_effectReferences.add(iterator->second, FilterEffectVector());
> > +        }
> 
> You should call this during initialization. Not sure if we get a leak if we leaf it in on clearEffects and call the DTor of FilterBuilder.

It is called from the constructor: 

SVGFilterBuilder::SVGFilterBuilder()
{
    m_builtinEffects.add(SourceGraphic::effectName(), SourceGraphic::create());
    m_builtinEffects.add(SourceAlpha::effectName(), SourceAlpha::create());
    addBuiltinEffects();
}

What do you mean by 'initialization'?

I am agree with the other things.
Comment 10 Dirk Schulze 2010-09-23 00:31:16 PDT
(In reply to comment #9)
> > > WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:89
> > > +void SVGFilterBuilder::appendEffectToEffectReferences(RefPtr<FilterEffect> effect)
> > > +{
> > > +    // The effect must be a newly created filter effect.
> > > +    ASSERT(!m_effectReferences.contains(effect));
> > > +    m_effectReferences.add(effect, FilterEffectVector());
> > > +
> > > +    FilterEffectVector& inputEffects = effect->inputEffects();
> > > +    HashSet<RefPtr<FilterEffect> > seenEffects;
> > > +    int size = inputEffects.size();
> > > +
> > > +    for (int i = 0; i < size; ++i) {
> > > +        // Since targetEffect is a newly created object, it cannot already be added to the list.
> > > +        ASSERT(m_effectReferences.contains(inputEffects[i]));
> > > +        if (seenEffects.contains(inputEffects[i]))
> > > +          continue;
> > > +        m_effectReferences.find(inputEffects[i])->second.append(effect);
> > > +        seenEffects.add(inputEffects[i]);
> > > +    }
> > > +}
> > 
> > I'm not sure why you want to have a FilterEffectVector instead of a Set? At the moment you create a set for every call of  appendEffectToEffectReferences. Wouldn't it be more useful to put a set into m_effectReferences:
> > HashMap<RefPtr<FilterEffect>, Set<RefPtr<FilterEffect> > > ? So you just need check the set, if the effect is already in there: contains(effect), and it if not. Niko already asked it, do we need the RefPtr's? Can't we just store the pointer?
> 
> If we would use a 'set', we would also create a new 'set' for each effect:
> +    m_effectReferences.add(effect, FilterEffectVector());
> would be
> +    m_effectReferences.add(effect, set());
Your're doing it anyway two lines later. And it doesn't matter if you check it with you temporary set or with the set of EffectReferences. Or do I get something wrong? Later you want to update the ReferenceList and it makes no sense to write your own iterator for the vector, if you could just call contains(..)

> 
> Using a vector here is a speed optimization, since iterating through a vector (needed by the 'invalidation' process) is much cheaper than iterating through a set. Moreover, we only append items during initialization, but invalidation will use the iteration many times.
> 
> > > WebCore/svg/graphics/filters/SVGFilterBuilder.h:51
> > > +        inline FilterEffectVector& getReferences(FilterEffect* effect)
> > > +        {
> > > +            // Only allowed for effects belongs to this builder.
> > > +            ASSERT(m_effectReferences.contains(effect));
> > > +            return m_effectReferences.find(effect)->second;
> > > +        }
> > 
> > Who is using that? Also this one liner don't need to be in an extra function.
> 
> Nobody at the moment, but it will be needed in the future, I am sure.
Please add it at this time when we need it.

> 
> > > WebCore/svg/graphics/filters/SVGFilterBuilder.h:63
> > > +        inline void addBuiltinEffects()
> > > +        {
> > > +            HashMap<AtomicString, RefPtr<FilterEffect> >::iterator end = m_builtinEffects.end();
> > > +            for (HashMap<AtomicString, RefPtr<FilterEffect> >::iterator iterator = m_builtinEffects.begin(); iterator != end; ++iterator)
> > > +                 m_effectReferences.add(iterator->second, FilterEffectVector());
> > > +        }
> > 
> > You should call this during initialization. Not sure if we get a leak if we leaf it in on clearEffects and call the DTor of FilterBuilder.
> 
> It is called from the constructor: 
> 
> SVGFilterBuilder::SVGFilterBuilder()
> {
>     m_builtinEffects.add(SourceGraphic::effectName(), SourceGraphic::create());
>     m_builtinEffects.add(SourceAlpha::effectName(), SourceAlpha::create());
>     addBuiltinEffects();
> }
> 
> What do you mean by 'initialization'?
Exactly that :-) But we call clearFilterBuilder in the destructor. And clearFilterBuilder calls your function that adds the pseudo effects. That needs to change somehow. Not sure why we call clear on RenderSVGResourceFilter right after creating the Builder. Maybe that should be removed as well.

> 
> I am agree with the other things.
Comment 11 Zoltan Herczeg 2010-09-24 06:59:11 PDT
Created attachment 68673 [details]
patch
Comment 12 Dirk Schulze 2010-09-24 08:37:09 PDT
Comment on attachment 68673 [details]
patch

View in context: https://bugs.webkit.org/attachment.cgi?id=68673&action=review

Otherwise looks great!

> WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:82
> +    m_effectReferences.add(effect, FilterEffectSet());
> +
> +    FilterEffectVector& inputEffects = effect->inputEffects();
> +    int size = inputEffects.size();
> +
> +    // It is not possible to add the same value to a set twice.
> +    for (int i = 0; i < size; ++i)
> +        getEffectReferences(effect.get()).add(inputEffects[i].get());

Please use numberOfInputEffects and inputEffect(unsigned) instead of the EffectVector. Store the pointer effect.get() in a own variable.

> WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:90
> +    addBuiltinEffects();

Please remove this from clearEffects().
Comment 13 Zoltan Herczeg 2010-09-27 03:42:13 PDT
Created attachment 68896 [details]
Patch 2
Comment 14 Dirk Schulze 2010-09-27 04:17:01 PDT
Comment on attachment 68896 [details]
Patch 2

View in context: https://bugs.webkit.org/attachment.cgi?id=68896&action=review

Great ptach! Please change the two issues above before landing. r=me.

> WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:78
> +    int numberOfInputEffects = effect->inputEffects().size();

should be unsigned

> WebCore/svg/graphics/filters/SVGFilterBuilder.cpp:81
> +    for (int i = 0; i < numberOfInputEffects; ++i)

ditto.
Comment 15 Zoltan Herczeg 2010-09-29 06:46:26 PDT
Landed in http://trac.webkit.org/changeset/68385
Closing bug