Improvements to GPU dither.

1) Makes the range of the offset dependent on the config.

2) Uses an ordered dither on GPUs that support integers in shaders.

3) Enables dithering for all paints with dither flag when the color type of the dst is 4444

4) Dithers r,g,b and clamps to 0,a rather than dithering all four channels (same as CPU backend).

Bug: skia:
Change-Id: Ie22c3adc38c6d1dbbcd97e4b7d16fc843e392c2e
Reviewed-on: https://skia-review.googlesource.com/23485
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Brian Salomon 2017-07-18 12:22:58 -04:00 committed by Skia Commit-Bot
parent 086a427b0c
commit 1806e33e6a
5 changed files with 113 additions and 22 deletions

View File

@ -75,8 +75,8 @@ bool SkPaintPriv::ShouldDither(const SkPaint& p, SkColorType dstCT) {
return false;
}
// We always dither 565 when requested.
if (dstCT == SkColorType::kRGB_565_SkColorType) {
// We always dither 565 or 4444 when requested.
if (dstCT == kRGB_565_SkColorType || dstCT == kARGB_4444_SkColorType) {
return true;
}

View File

@ -535,7 +535,10 @@ static inline bool skpaint_to_grpaint_impl(GrContext* context,
GrPixelConfigToColorType(rtc->config(), &ct);
if (SkPaintPriv::ShouldDither(skPaint, ct) && grPaint->numColorFragmentProcessors() > 0
&& !rtc->isGammaCorrect()) {
grPaint->addColorFragmentProcessor(GrDitherEffect::Make());
auto ditherFP = GrDitherEffect::Make(rtc->config());
if (ditherFP) {
grPaint->addColorFragmentProcessor(std::move(ditherFP));
}
}
#endif
return true;

View File

@ -23,7 +23,7 @@ public:
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
const GrDitherEffect& _outer = args.fFp.cast<GrDitherEffect>();
(void) _outer;
fragBuilder->codeAppendf("float r = fract(sin(dot(sk_FragCoord.xy, vec2(12.989800000000001, 78.233000000000004))) * 43758.545299999998) - 0.5;\n%s = clamp(0.0039215686274509803 * vec4(r) + %s, 0.0, 1.0);\n", args.fOutputColor, args.fInputColor ? args.fInputColor : "vec4(1)");
fragBuilder->codeAppendf("float value;\nfloat range;\n@switch (%d) {\n case 0:\n range = 0.0039215686274509803;\n break;\n case 1:\n range = 0.015873015873015872;\n break;\n default:\n range = 0.0083333333333333332;\n break;\n}\n@if (sk_Caps.integerSupport) {\n int x = int(sk_FragCoord.x);\n int y = int(sk_FragCoord.y);\n uint m = uint((((((y & 1) << 5 | (x & 1) << 4) | (y & 2) << 2) | (x & 2) << 1) | (y & 4) >> 1) | (x & 4) >> 2);\n value = float(m) / 64.0 - 0.4921875;\n} else {\n value = fract(sin(dot(sk_FragCoord.xy, vec2(12.989800000000001, 78.233000000000004))) * 43758.545299999998) - 0.5;\n}\n%s = vec4(clamp(%s.xyz + value * range, 0.0, %s.w), %s.w);\n", _outer.rangeType(), args.fOutputColor, args.fInputColor ? args.fInputColor : "vec4(1)", args.fInputColor ? args.fInputColor : "vec4(1)", args.fInputColor ? args.fInputColor : "vec4(1)");
}
private:
void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& _proc) override {
@ -33,17 +33,20 @@ GrGLSLFragmentProcessor* GrDitherEffect::onCreateGLSLInstance() const {
return new GrGLSLDitherEffect();
}
void GrDitherEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const {
b->add32(fRangeType);
}
bool GrDitherEffect::onIsEqual(const GrFragmentProcessor& other) const {
const GrDitherEffect& that = other.cast<GrDitherEffect>();
(void) that;
if (fRangeType != that.fRangeType) return false;
return true;
}
GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDitherEffect);
#if GR_TEST_UTILS
sk_sp<GrFragmentProcessor> GrDitherEffect::TestCreate(GrProcessorTestData* testData) {
return GrDitherEffect::Make();
float range = testData->fRandom->nextRangeF(0.001f, 0.05f);
return sk_sp<GrFragmentProcessor>(new GrDitherEffect(range));
}
#endif
#endif

View File

@ -1,17 +1,74 @@
void main() {
// Generate a random number based on the fragment position. For this
// random number generator, we use the "GLSL rand" function
// that seems to be floating around on the internet. It works under
// the assumption that sin(<big number>) oscillates with high frequency
// and sampling it will generate "randomness". Since we're using this
// for rendering and not cryptography it should be OK.
// This controls the range of values added to color channels
layout(key) in int rangeType;
// For each channel c, add the random offset to the pixel to either bump
// it up or let it remain constant during quantization.
float r = fract(sin(dot(sk_FragCoord.xy, vec2(12.9898, 78.233))) * 43758.5453) - .5;
sk_OutColor = clamp(1 / 255.0 * vec4(r) + sk_InColor, 0, 1);
@make {
static sk_sp<GrFragmentProcessor> Make(GrPixelConfig dstConfig) {
int rangeType;
switch (dstConfig) {
case kGray_8_GrPixelConfig:
case kRGBA_8888_GrPixelConfig:
case kBGRA_8888_GrPixelConfig:
case kSRGBA_8888_GrPixelConfig:
case kSBGRA_8888_GrPixelConfig:
rangeType = 0;
break;
case kRGB_565_GrPixelConfig:
rangeType = 1;
break;
case kRGBA_4444_GrPixelConfig:
rangeType = 2;
break;
case kUnknown_GrPixelConfig:
case kAlpha_half_GrPixelConfig:
case kRGBA_8888_sint_GrPixelConfig:
case kRGBA_float_GrPixelConfig:
case kRG_float_GrPixelConfig:
case kRGBA_half_GrPixelConfig:
case kAlpha_8_GrPixelConfig:
return nullptr;
}
return sk_sp<GrFragmentProcessor>(new GrDitherEffect(rangeType));
}
}
void main() {
float value;
float range;
@switch (rangeType) {
case 0:
range = 1.0 / 255.0;
break;
case 1:
range = 1.0 / 63.0;
break;
default:
// Experimentally this looks better than the expected value of 1/15.
range = 0.125 / 15.0;
break;
}
@if (sk_Caps.integerSupport) {
// This ordered-dither code is lifted from the cpu backend.
int x = int(sk_FragCoord.x);
int y = int(sk_FragCoord.y);
uint m = (y & 1) << 5 | (x & 1) << 4 |
(y & 2) << 2 | (x & 2) << 1 |
(y & 4) >> 1 | (x & 4) >> 2;
value = float(m) * 1.0 / 64.0 - 63.0 / 128.0;
} else {
// Generate a random number based on the fragment position. For this
// random number generator, we use the "GLSL rand" function
// that seems to be floating around on the internet. It works under
// the assumption that sin(<big number>) oscillates with high frequency
// and sampling it will generate "randomness". Since we're using this
// for rendering and not cryptography it should be OK.
value = fract(sin(dot(sk_FragCoord.xy, vec2(12.9898, 78.233))) * 43758.5453) - .5;
}
// For each color channel, add the random offset to the channel value and then clamp
// between 0 and alpha to keep the color premultiplied.
sk_OutColor = vec4(clamp(sk_InColor.rgb + value * range, 0, sk_InColor.a), sk_InColor.a);
}
@test(testData) {
return GrDitherEffect::Make();
}
float range = testData->fRandom->nextRangeF(0.001f, 0.05f);
return sk_sp<GrFragmentProcessor>(new GrDitherEffect(range));
}

View File

@ -18,19 +18,47 @@
#include "effects/GrProxyMove.h"
class GrDitherEffect : public GrFragmentProcessor {
public:
static sk_sp<GrFragmentProcessor> Make() {
return sk_sp<GrFragmentProcessor>(new GrDitherEffect());
int rangeType() const { return fRangeType; }
static sk_sp<GrFragmentProcessor> Make(GrPixelConfig dstConfig) {
int rangeType;
switch (dstConfig) {
case kGray_8_GrPixelConfig:
case kRGBA_8888_GrPixelConfig:
case kBGRA_8888_GrPixelConfig:
case kSRGBA_8888_GrPixelConfig:
case kSBGRA_8888_GrPixelConfig:
rangeType = 0;
break;
case kRGB_565_GrPixelConfig:
rangeType = 1;
break;
case kRGBA_4444_GrPixelConfig:
rangeType = 2;
break;
case kUnknown_GrPixelConfig:
case kAlpha_half_GrPixelConfig:
case kRGBA_8888_sint_GrPixelConfig:
case kRGBA_float_GrPixelConfig:
case kRG_float_GrPixelConfig:
case kRGBA_half_GrPixelConfig:
case kAlpha_8_GrPixelConfig:
return nullptr;
}
return sk_sp<GrFragmentProcessor>(new GrDitherEffect(rangeType));
}
const char* name() const override { return "DitherEffect"; }
private:
GrDitherEffect()
: INHERITED(kNone_OptimizationFlags) {
GrDitherEffect(int rangeType)
: INHERITED(kNone_OptimizationFlags)
, fRangeType(rangeType) {
this->initClassID<GrDitherEffect>();
}
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
void onGetGLSLProcessorKey(const GrShaderCaps&,GrProcessorKeyBuilder*) const override;
bool onIsEqual(const GrFragmentProcessor&) const override;
GR_DECLARE_FRAGMENT_PROCESSOR_TEST
int fRangeType;
typedef GrFragmentProcessor INHERITED;
};
#endif