RESOLVED FIXED 92059
[chromium] Combine color matrix filters and apply them in a single pass.
https://bugs.webkit.org/show_bug.cgi?id=92059
Summary [chromium] Combine color matrix filters and apply them in a single pass.
Antoine Labour
Reported 2012-07-23 20:54:05 PDT
[chromium] Combine color matrix filters and apply them in a single pass.
Attachments
Patch (11.50 KB, patch)
2012-07-23 20:57 PDT, Antoine Labour
no flags
Patch (25.40 KB, patch)
2012-07-24 17:43 PDT, Antoine Labour
no flags
Patch (26.63 KB, patch)
2012-07-24 17:50 PDT, Antoine Labour
no flags
Patch (26.64 KB, patch)
2012-07-24 18:01 PDT, Antoine Labour
no flags
Patch (70.32 KB, patch)
2012-07-25 16:45 PDT, Antoine Labour
no flags
Patch (70.55 KB, patch)
2012-07-25 21:05 PDT, Antoine Labour
no flags
Patch for landing (71.46 KB, patch)
2012-07-26 12:29 PDT, Antoine Labour
no flags
Antoine Labour
Comment 1 2012-07-23 20:57:11 PDT
Antoine Labour
Comment 2 2012-07-23 20:58:05 PDT
See http://code.google.com/p/chromium/issues/detail?id=138645 for a use case for this optimization.
Adrienne Walker
Comment 3 2012-07-23 23:07:48 PDT
Comment on attachment 153949 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=153949&action=review R=me. > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:224 > +class ScopedScratchCanvas { Nice. :)
Stephen White
Comment 4 2012-07-24 06:49:18 PDT
Comment on attachment 153949 [details] Patch As tempting as this optimization is (and I have been tempted), I don't think it's correct for CSS filters. The reason is that colour clamping must occur after each matrix is applied. E.g., if a translation by +255 followed by -255 were applied, it should end up at black, not at the original colour. I'd suggest two possibilities: add a flag so that it is only enabled for your use case, and not CSS filters, or implement a generic colour matrix filter, and convert and concatenate your matrices outside this code. Alternately, I suppose we could try to detect the case where clamping would occur, and avoid the optimization, but that might be tricky.
Stephen White
Comment 5 2012-07-24 06:59:04 PDT
(In reply to comment #4) > I'd suggest two possibilities: add a flag so that it is only enabled for your use case, and not CSS filters, or implement a generic colour matrix filter, and convert and concatenate your matrices outside this code. Alternately, I suppose we could try to detect the case where clamping would occur, and avoid the optimization, but that might be tricky. After thinking about it a bit, I'd prefer the generic colour matrix filter option, since we'll need that for some upcoming work anyway.
Dana Jansens
Comment 6 2012-07-24 09:13:53 PDT
It's tempting to just remove all the color matrix filter operations and keep a single "matrix" operation, but that may be shooting ourselves in the foot if we decide to animate filters inside the compositor someday, as each filter needs to be animated independently rather than interpolating between two matrices. And in this case, the UI would want to use separate filters also, not a single matrix. So I'm a proponent of adding a useStrictClamping flag or somesuch to WebFilterOperations and doing these sorts of optimizations when it is false.
Antoine Labour
Comment 7 2012-07-24 10:01:00 PDT
(In reply to comment #6) > It's tempting to just remove all the color matrix filter operations and keep a single "matrix" operation, but that may be shooting ourselves in the foot if we decide to animate filters inside the compositor someday, as each filter needs to be animated independently rather than interpolating between two matrices. Yeah, that's what happens. > > And in this case, the UI would want to use separate filters also, not a single matrix. So I'm a proponent of adding a useStrictClamping flag or somesuch to WebFilterOperations and doing these sorts of optimizations when it is false. I think I like this option. The flag can be kept off for web stuff.
Stephen White
Comment 8 2012-07-24 10:27:09 PDT
Comment on attachment 153949 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=153949&action=review > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:307 > + if (getColorMatrix(op, matrix)) { > + if (hasMatrix) { > + SkScalar newMatrix[20]; > + multColorMatrix(matrix, combinedMatrix, newMatrix); > + memcpy(combinedMatrix, newMatrix, sizeof(combinedMatrix)); > + } else { > + memcpy(combinedMatrix, matrix, sizeof(combinedMatrix)); > + hasMatrix = true; > + } > + } else { > + if (hasMatrix) { > + ScopedScratchCanvas canvas(gr, desc, &source); > + if (!canvas.isValid()) > + return SkBitmap(); > + applyColorMatrix(canvas.canvas(), source, combinedMatrix); > + hasMatrix = false; > + } This seems to be a lot of special cases (and separate texture allocations) inside what's essentially a pretty simple loop. Could we instead pre-process the list of filters into an optimized list, consisting of matrix and non-matrix operations? Also please add a test in css3/filters to test for the colour clamping I mentioned. You'll need a regular and -hw flavour of the test.
Stephen White
Comment 9 2012-07-24 10:28:59 PDT
(In reply to comment #7) > (In reply to comment #6) > > It's tempting to just remove all the color matrix filter operations and keep a single "matrix" operation, but that may be shooting ourselves in the foot if we decide to animate filters inside the compositor someday, as each filter needs to be animated independently rather than interpolating between two matrices. > > Yeah, that's what happens. > > > > > And in this case, the UI would want to use separate filters also, not a single matrix. So I'm a proponent of adding a useStrictClamping flag or somesuch to WebFilterOperations and doing these sorts of optimizations when it is false. > > I think I like this option. The flag can be kept off for web stuff. Here's another option: create a matrix filter type, but for now only use it inside CCRenderSurfaceFilters.cpp (we can expose it later). Pre-process the list of filters into a list of collapsed matrices and non-matrix operations. Then the processing loop can be simple, as it was before, with only a single backing store allocation.
Antoine Labour
Comment 10 2012-07-24 17:43:11 PDT
Antoine Labour
Comment 11 2012-07-24 17:45:12 PDT
As discussed directly, this new patch does: - automatically figures out if we need clamping, in which case it will apply filters separately. - reuse textures across filter passes (flip-flops) - add layout tests for the clamping case - a few minor simplifications to make the loop body state machine more readable.
WebKit Review Bot
Comment 12 2012-07-24 17:46:46 PDT
Attachment 154196 [details] did not pass style-queue: Failed to run "['Tools/Scripts/check-webkit-style', '--diff-files', u'LayoutTests/css3/filters/effect-brightness..." exit_code: 1 Source/WebCore/ChangeLog:14: Line contains tab character. [whitespace/tab] [5] Total errors found: 1 in 6 files If any of these errors are false positives, please file a bug against check-webkit-style.
Dana Jansens
Comment 13 2012-07-24 17:49:26 PDT
Comment on attachment 154196 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=154196&action=review > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:170 > + for (int j = 0; j < 4; ++j) this should have a { as well as the for loop inside it
Antoine Labour
Comment 14 2012-07-24 17:50:11 PDT
Antoine Labour
Comment 15 2012-07-24 18:01:10 PDT
Antoine Labour
Comment 16 2012-07-24 18:01:46 PDT
Comment on attachment 154196 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=154196&action=review >> Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:170 >> + for (int j = 0; j < 4; ++j) > > this should have a { as well as the for loop inside it Done.
Dana Jansens
Comment 17 2012-07-24 19:13:03 PDT
Comment on attachment 154201 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=154201&action=review Sorry for the horde of nits, also I think I see an extra copy now? > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:164 > +// c = a*b; maybe rename "c" to something like "out" or "result" to make this clear instead of requiring this comment? > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:253 > + FilterBufferState(GrContext* gr, const WebCore::FloatSize& size, unsigned textureId) > + : m_gr(gr) s/gr/grContext/ ? > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:255 > + // Wrap the RenderSurface's texture in a Ganesh platform texture. nit: let's use terminology of "input texture" or something, rather than RenderSurface explicitly, since that will be wrong once we allow filters without a surface in the single-layer case. > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:256 > + GrPlatformTextureDesc platformTexDesc; There's lots of abbreviations in this file which we avoid generally. Sorry to bikeshed on them, but maybe we can clean them up now? eg textureDescription? > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:345 > + SkScalar combinedMatrix[20]; combinedColorMatrix? > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:346 > + bool hasMatrix = false; Can we call this "haveCombinedColorMatrix" or "combinedColorMatrixIsEmpty" or "combinedColorMatrixHasValue" or something? I feel like hasMatrix is a bit more ambiguous about which matrix its meaning. > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:376 > + if (hasMatrix && !applyColorMatrix(&state, combinedMatrix)) > + return SkBitmap(); > + hasMatrix = false; > + > + SkCanvas* canvas = state.canvas(); > + if (!canvas) > return SkBitmap(); > - SkGpuDevice device(gr, destination.get()); > - SkCanvas canvas(&device); > - canvas.clear(0x0); > switch (op.type()) { If we have a combinedColorMatrix and succeed at applying it, should we do a "continue" after marking the combinedColorMatrix as being non-present? Right now, we'll fall into the default: case of the switch statement after applying the color matrix, which will do a copy of the source to the new destination texture (swapped in at the end of the apply).
Stephen White
Comment 18 2012-07-25 08:29:58 PDT
Comment on attachment 154201 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=154201&action=review Thanks very much for implementing this optimization. It's great that we can apply it to web content as well. > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:189 > +bool componentNeedsClamping(SkScalar line[5]) Nit: Could we call this "row" rather than "line"? Matrices usually have rows. > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:316 > + OwnPtr<SkGpuDevice> m_device; > + OwnPtr<SkCanvas> m_canvas; I seem to recall problems with using OwnPtr and Skia stuff in the past, since it has its own ref counting. Consider using an SkAutoTUnref on the device & canvas as well (if you like.. your call). >> Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:346 >> + bool hasMatrix = false; > > Can we call this "haveCombinedColorMatrix" or "combinedColorMatrixIsEmpty" or "combinedColorMatrixHasValue" or something? I feel like hasMatrix is a bit more ambiguous about which matrix its meaning. Perhaps "accumulated" instead of "combined"? "accumulatedColorMatrix" and "haveAccumulatedColorMatrix"? > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:376 > switch (op.type()) { Good catch, Dana (although I think it's only a problem in the clamping case). Maybe we could apply the matrix right away above if it needs clamping, and continue in both the clamping and non-clamping cases: if (matrixNeedsClamping(combinedMatrix)) && !applyColorMatrix(&state, combinedMatrix)) return SkBitmap(); continue; Alternately, we could probably get rid of the identity draw in the default case now since it'll just pick up the previous source, although it would require moving the swap() into the blur and drop-shadow draws so it wouldn't happen in the default case either. > LayoutTests/css3/filters/effect-brightness-clamping-hw.html:13 > +<img style="-webkit-filter: brightness(1) brightness(-1)" src="resources/reference.png"> I'd like to see more coverage here, e.g., brightness followed by blur or drop-shadow.
Dana Jansens
Comment 19 2012-07-25 08:35:06 PDT
(In reply to comment #18) > (From update of attachment 154201 [details]) > View in context: https://bugs.webkit.org/attachment.cgi?id=154201&action=review > > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:376 > > switch (op.type()) { > > Good catch, Dana (although I think it's only a problem in the clamping case). Maybe we could apply the matrix right away above if it needs clamping, and continue in both the clamping and non-clamping cases: > > if (matrixNeedsClamping(combinedMatrix)) && !applyColorMatrix(&state, combinedMatrix)) > return SkBitmap(); > continue; > > Alternately, we could probably get rid of the identity draw in the default case now since it'll just pick up the previous source, although it would require moving the swap() into the blur and drop-shadow draws so it wouldn't happen in the default case either. If we can get rid of the default case, (and add back all the color matrix cases going to the same case-body) that could possibly be nice. That way we would also get compiler errors if a new operation type is added to the enum but not to the switch.
Antoine Labour
Comment 20 2012-07-25 16:45:17 PDT
Antoine Labour
Comment 21 2012-07-25 16:48:19 PDT
Comment on attachment 154201 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=154201&action=review >> Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:164 >> +// c = a*b; > > maybe rename "c" to something like "out" or "result" to make this clear instead of requiring this comment? Done. >> Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:189 >> +bool componentNeedsClamping(SkScalar line[5]) > > Nit: Could we call this "row" rather than "line"? Matrices usually have rows. Done. >> Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:253 >> + : m_gr(gr) > > s/gr/grContext/ ? Done. >> Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:255 >> + // Wrap the RenderSurface's texture in a Ganesh platform texture. > > nit: let's use terminology of "input texture" or something, rather than RenderSurface explicitly, since that will be wrong once we allow filters without a surface in the single-layer case. Done. >> Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:256 >> + GrPlatformTextureDesc platformTexDesc; > > There's lots of abbreviations in this file which we avoid generally. Sorry to bikeshed on them, but maybe we can clean them up now? eg textureDescription? Done. >> Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:316 >> + OwnPtr<SkCanvas> m_canvas; > > I seem to recall problems with using OwnPtr and Skia stuff in the past, since it has its own ref counting. Consider using an SkAutoTUnref on the device & canvas as well (if you like.. your call). Done. I'm not familiar with Skia's flavor of refcounting / smart pointers - my understanding is that a new class starts with a ref of 1 and SkAutoTUnref doesn't add a ref on assignment - so please double-check my usage. >>> Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:346 >>> + bool hasMatrix = false; >> >> Can we call this "haveCombinedColorMatrix" or "combinedColorMatrixIsEmpty" or "combinedColorMatrixHasValue" or something? I feel like hasMatrix is a bit more ambiguous about which matrix its meaning. > > Perhaps "accumulated" instead of "combined"? > "accumulatedColorMatrix" and "haveAccumulatedColorMatrix"? Done. >>>> Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:376 >>>> switch (op.type()) { >>> >>> If we have a combinedColorMatrix and succeed at applying it, should we do a "continue" after marking the combinedColorMatrix as being non-present? Right now, we'll fall into the default: case of the switch statement after applying the color matrix, which will do a copy of the source to the new destination texture (swapped in at the end of the apply). >> >> Good catch, Dana (although I think it's only a problem in the clamping case). Maybe we could apply the matrix right away above if it needs clamping, and continue in both the clamping and non-clamping cases: >> >> if (matrixNeedsClamping(combinedMatrix)) && !applyColorMatrix(&state, combinedMatrix)) >> return SkBitmap(); >> continue; >> >> Alternately, we could probably get rid of the identity draw in the default case now since it'll just pick up the previous source, although it would require moving the swap() into the blur and drop-shadow draws so it wouldn't happen in the default case either. > > If we can get rid of the default case, (and add back all the color matrix cases going to the same case-body) that could possibly be nice. That way we would also get compiler errors if a new operation type is added to the enum but not to the switch. Done. >> LayoutTests/css3/filters/effect-brightness-clamping-hw.html:13 >> +<img style="-webkit-filter: brightness(1) brightness(-1)" src="resources/reference.png"> > > I'd like to see more coverage here, e.g., brightness followed by blur or drop-shadow. Done.
Dana Jansens
Comment 22 2012-07-25 17:00:32 PDT
Comment on attachment 154481 [details] Patch nice, thanks for all the renames!
Antoine Labour
Comment 23 2012-07-25 21:05:20 PDT
Antoine Labour
Comment 24 2012-07-25 21:06:49 PDT
PTAL, I noticed the previous patch was creating a canvas that it never used in one case (when the last filter is a matrix that needs clamping), causing possibly an extra alloc and a clear. New patch addresses that.
Stephen White
Comment 25 2012-07-26 07:07:47 PDT
Comment on attachment 154532 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=154532&action=review Looks good. r=me Note that your new test will likely cause the non-Linux WebKit canary bots to go red (due to pixel differences), so please either land with a suppression in TestExpectations, or rebaseline the tests after landing. > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:198 > + return (maxValue > 255) || (minValue < 0); It just occurred to me.. if the matrix is really large, is there a possibility for float overflow here? I guess it'll just end up at inf or -inf, probably safe for comparison purposes? > Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:388 > + SkCanvas* canvas = state.canvas(); > + if (!canvas) > + return SkBitmap(); I'm going to be annoying and point out that, if we did this optimization as a pre-processing pass, we'd know exactly how many textures we'd need to allocate, and we could do all allocations up front, without the need for all these checks and early-outs. That could be done as a later change, though.
Antoine Labour
Comment 26 2012-07-26 10:12:45 PDT
Comment on attachment 154532 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=154532&action=review >> Source/WebCore/platform/graphics/chromium/cc/CCRenderSurfaceFilters.cpp:198 >> + return (maxValue > 255) || (minValue < 0); > > It just occurred to me.. if the matrix is really large, is there a possibility for float overflow here? I guess it'll just end up at inf or -inf, probably safe for comparison purposes? Correct, float overflow is a lot nicer than unsigned int (let alone int!) for this sort of things. FYI, during my testing I noticed that some matrices (at least the grayscale one depending on the amount value) trigger this because of precision issues. I think I can change the way it's computed to make sure it's stable (will do in a follow up), or possibly add a fudge factor here (I'd rather not).
Antoine Labour
Comment 27 2012-07-26 12:29:34 PDT
Created attachment 154718 [details] Patch for landing
WebKit Review Bot
Comment 28 2012-07-26 13:54:38 PDT
Comment on attachment 154718 [details] Patch for landing Clearing flags on attachment: 154718 Committed r123790: <http://trac.webkit.org/changeset/123790>
WebKit Review Bot
Comment 29 2012-07-26 13:54:43 PDT
All reviewed patches have been landed. Closing bug.
Note You need to log in before you can comment on or make changes to this bug.