Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 1ee21cd

Browse files
rphilliSkia Commit-Bot
authored andcommitted
Create cpu-side blurred RRect mask when recording DDLs (take 2)
Bug: 1108408 Change-Id: Ib2d9a20d67a9afd80fd3c11a32ff7174f4cebb59 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/315298 Reviewed-by: Adlai Holler <[email protected]> Commit-Queue: Robert Phillips <[email protected]>
1 parent 59c60b0 commit 1ee21cd

File tree

4 files changed

+284
-9
lines changed

4 files changed

+284
-9
lines changed

src/core/SkBlurPriv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ bool SkComputeBlurredRRectParams(const SkRRect& srcRRect, const SkRRect& devRRec
3131

3232
int SkCreateIntegralTable(float sixSigma, SkBitmap* table);
3333

34+
void SkFillIn1DGaussianKernel(float* kernel, float gaussianSigma, int radius);
35+
3436
extern void sk_register_blur_maskfilter_createproc();
3537

3638
#endif

src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ void GrGaussianConvolutionFragmentProcessor::Impl::GenKey(const GrProcessor& pro
104104

105105
///////////////////////////////////////////////////////////////////////////////
106106

107-
static void fill_in_1D_gaussian_kernel(float* kernel, float gaussianSigma, int radius) {
107+
void SkFillIn1DGaussianKernel(float* kernel, float gaussianSigma, int radius) {
108108
const float twoSigmaSqrd = 2.0f * gaussianSigma * gaussianSigma;
109109
int width = radius_to_width(radius);
110110
if (SkScalarNearlyZero(twoSigmaSqrd, SK_ScalarNearlyZero)) {
@@ -171,7 +171,7 @@ GrGaussianConvolutionFragmentProcessor::GrGaussianConvolutionFragmentProcessor(
171171
, fDirection(direction) {
172172
this->registerChild(std::move(child), SkSL::SampleUsage::Explicit());
173173
SkASSERT(radius <= kMaxKernelRadius);
174-
fill_in_1D_gaussian_kernel(fKernel, gaussianSigma, fRadius);
174+
SkFillIn1DGaussianKernel(fKernel, gaussianSigma, fRadius);
175175
this->setUsesSampleCoordsDirectly();
176176
}
177177

src/gpu/effects/GrRRectBlurEffect.fp

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ uniform half blurRadius;
3535

3636
@cpp {
3737
#include "include/gpu/GrRecordingContext.h"
38+
#include "src/core/SkAutoMalloc.h"
3839
#include "src/core/SkBlurPriv.h"
3940
#include "src/core/SkGpuBlurUtils.h"
4041
#include "src/core/SkRRectPriv.h"
@@ -109,6 +110,136 @@ uniform half blurRadius;
109110
return rtc2->readSurfaceView();
110111
}
111112

113+
// TODO: merge w/ copy in SkGpuBlurUtils.cpp
114+
static int sigma_radius(float sigma) {
115+
SkASSERT(sigma >= 0);
116+
return static_cast<int>(ceilf(sigma * 3.0f));
117+
}
118+
119+
// Evaluate the vertical blur at the specified 'y' value given the location of the top of the
120+
// rrect.
121+
static uint8_t eval_V(float top, int y,
122+
const uint8_t* integral, int integralSize, float sixSigma) {
123+
if (top < 0) {
124+
return 0; // an empty column
125+
}
126+
127+
float fT = (top - y - 0.5f) * (integralSize/sixSigma);
128+
if (fT < 0) {
129+
return 255;
130+
} else if (fT >= integralSize-1) {
131+
return 0;
132+
}
133+
134+
int lower = (int) fT;
135+
float frac = fT - lower;
136+
137+
SkASSERT(lower+1 < integralSize);
138+
139+
return integral[lower] * (1.0f-frac) + integral[lower+1] * frac;
140+
}
141+
142+
// Apply a gaussian 'kernel' horizontally at the specified 'x', 'y' location.
143+
static uint8_t eval_H(int x, int y, const std::vector<float>& topVec,
144+
const float* kernel, int kernelSize,
145+
const uint8_t* integral, int integralSize, float sixSigma) {
146+
SkASSERT(0 <= x && x < (int) topVec.size());
147+
SkASSERT(kernelSize % 2);
148+
149+
float accum = 0.0f;
150+
151+
int xSampleLoc = x - (kernelSize / 2);
152+
for (int i = 0; i < kernelSize; ++i, ++xSampleLoc) {
153+
if (xSampleLoc < 0 || xSampleLoc >= (int) topVec.size()) {
154+
continue;
155+
}
156+
157+
accum += kernel[i] * eval_V(topVec[xSampleLoc], y, integral, integralSize, sixSigma);
158+
}
159+
160+
return accum + 0.5f;
161+
}
162+
163+
// Create a cpu-side blurred-rrect mask that is close to the version the gpu would've produced.
164+
// The match needs to be close bc the cpu- and gpu-generated version must be interchangeable.
165+
static GrSurfaceProxyView create_mask_on_cpu(GrRecordingContext* context,
166+
const SkRRect& rrectToDraw,
167+
const SkISize& dimensions,
168+
float xformedSigma) {
169+
int radius = sigma_radius(xformedSigma);
170+
int kernelSize = 2*radius + 1;
171+
172+
SkASSERT(kernelSize %2);
173+
SkASSERT(dimensions.width() % 2);
174+
SkASSERT(dimensions.height() % 2);
175+
176+
SkVector radii = rrectToDraw.getSimpleRadii();
177+
SkASSERT(SkScalarNearlyEqual(radii.fX, radii.fY));
178+
179+
const int halfWidthPlus1 = (dimensions.width() / 2) + 1;
180+
const int halfHeightPlus1 = (dimensions.height() / 2) + 1;
181+
182+
std::unique_ptr<float[]> kernel(new float[kernelSize]);
183+
184+
SkFillIn1DGaussianKernel(kernel.get(), xformedSigma, radius);
185+
186+
SkBitmap integral;
187+
if (!SkCreateIntegralTable(6*xformedSigma, &integral)) {
188+
return {};
189+
}
190+
191+
SkBitmap result;
192+
if (!result.tryAllocPixels(SkImageInfo::MakeA8(dimensions.width(), dimensions.height()))) {
193+
return {};
194+
}
195+
196+
std::vector<float> topVec;
197+
topVec.reserve(dimensions.width());
198+
for (int x = 0; x < dimensions.width(); ++x) {
199+
if (x < rrectToDraw.rect().fLeft || x > rrectToDraw.rect().fRight) {
200+
topVec.push_back(-1);
201+
} else {
202+
if (x+0.5f < rrectToDraw.rect().fLeft + radii.fX) { // in the circular section
203+
float xDist = rrectToDraw.rect().fLeft + radii.fX - x - 0.5f;
204+
float h = sqrtf(radii.fX * radii.fX - xDist * xDist);
205+
SkASSERT(0 <= h && h < radii.fY);
206+
topVec.push_back(rrectToDraw.rect().fTop+radii.fX-h + 3*xformedSigma);
207+
} else {
208+
topVec.push_back(rrectToDraw.rect().fTop + 3*xformedSigma);
209+
}
210+
}
211+
}
212+
213+
for (int y = 0; y < halfHeightPlus1; ++y) {
214+
uint8_t* scanline = result.getAddr8(0, y);
215+
216+
for (int x = 0; x < halfWidthPlus1; ++x) {
217+
scanline[x] = eval_H(x, y, topVec,
218+
kernel.get(), kernelSize,
219+
integral.getAddr8(0, 0), integral.width(), 6*xformedSigma);
220+
scanline[dimensions.width()-x-1] = scanline[x];
221+
}
222+
223+
memcpy(result.getAddr8(0, dimensions.height()-y-1), scanline, result.rowBytes());
224+
}
225+
226+
result.setImmutable();
227+
228+
GrProxyProvider* proxyProvider = context->priv().proxyProvider();
229+
230+
sk_sp<GrTextureProxy> proxy = proxyProvider->createProxyFromBitmap(result,
231+
GrMipmapped::kNo,
232+
SkBackingFit::kExact,
233+
SkBudgeted::kYes);
234+
if (!proxy) {
235+
return {};
236+
}
237+
238+
GrSwizzle swizzle = context->priv().caps()->getReadSwizzle(proxy->backendFormat(),
239+
GrColorType::kAlpha_8);
240+
return {std::move(proxy), kBlurredRRectMaskOrigin, swizzle};
241+
}
242+
112243
static std::unique_ptr<GrFragmentProcessor> find_or_create_rrect_blur_mask_fp(
113244
GrRecordingContext* context,
114245
const SkRRect& rrectToDraw,
@@ -132,7 +263,12 @@ uniform half blurRadius;
132263
return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, m);
133264
}
134265

135-
auto mask = create_mask_on_gpu(context, rrectToDraw, dimensions, xformedSigma);
266+
GrSurfaceProxyView mask;
267+
if (proxyProvider->isDDLProvider() == GrDDLProvider::kNo) {
268+
mask = create_mask_on_gpu(context, rrectToDraw, dimensions, xformedSigma);
269+
} else {
270+
mask = create_mask_on_cpu(context, rrectToDraw, dimensions, xformedSigma);
271+
}
136272
if (!mask) {
137273
return nullptr;
138274
}

src/gpu/effects/generated/GrRRectBlurEffect.cpp

Lines changed: 143 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "GrRRectBlurEffect.h"
1212

1313
#include "include/gpu/GrRecordingContext.h"
14+
#include "src/core/SkAutoMalloc.h"
1415
#include "src/core/SkBlurPriv.h"
1516
#include "src/core/SkGpuBlurUtils.h"
1617
#include "src/core/SkRRectPriv.h"
@@ -83,6 +84,137 @@ static GrSurfaceProxyView create_mask_on_gpu(GrRecordingContext* context,
8384
return rtc2->readSurfaceView();
8485
}
8586

87+
// TODO: merge w/ copy in SkGpuBlurUtils.cpp
88+
static int sigma_radius(float sigma) {
89+
SkASSERT(sigma >= 0);
90+
return static_cast<int>(ceilf(sigma * 3.0f));
91+
}
92+
93+
// Evaluate the vertical blur at the specified 'y' value given the location of the top of the
94+
// rrect.
95+
static uint8_t eval_V(float top, int y, const uint8_t* integral, int integralSize, float sixSigma) {
96+
if (top < 0) {
97+
return 0; // an empty column
98+
}
99+
100+
float fT = (top - y - 0.5f) * (integralSize / sixSigma);
101+
if (fT < 0) {
102+
return 255;
103+
} else if (fT >= integralSize - 1) {
104+
return 0;
105+
}
106+
107+
int lower = (int)fT;
108+
float frac = fT - lower;
109+
110+
SkASSERT(lower + 1 < integralSize);
111+
112+
return integral[lower] * (1.0f - frac) + integral[lower + 1] * frac;
113+
}
114+
115+
// Apply a gaussian 'kernel' horizontally at the specified 'x', 'y' location.
116+
static uint8_t eval_H(int x,
117+
int y,
118+
const std::vector<float>& topVec,
119+
const float* kernel,
120+
int kernelSize,
121+
const uint8_t* integral,
122+
int integralSize,
123+
float sixSigma) {
124+
SkASSERT(0 <= x && x < (int)topVec.size());
125+
SkASSERT(kernelSize % 2);
126+
127+
float accum = 0.0f;
128+
129+
int xSampleLoc = x - (kernelSize / 2);
130+
for (int i = 0; i < kernelSize; ++i, ++xSampleLoc) {
131+
if (xSampleLoc < 0 || xSampleLoc >= (int)topVec.size()) {
132+
continue;
133+
}
134+
135+
accum += kernel[i] * eval_V(topVec[xSampleLoc], y, integral, integralSize, sixSigma);
136+
}
137+
138+
return accum + 0.5f;
139+
}
140+
141+
// Create a cpu-side blurred-rrect mask that is close to the version the gpu would've produced.
142+
// The match needs to be close bc the cpu- and gpu-generated version must be interchangeable.
143+
static GrSurfaceProxyView create_mask_on_cpu(GrRecordingContext* context,
144+
const SkRRect& rrectToDraw,
145+
const SkISize& dimensions,
146+
float xformedSigma) {
147+
int radius = sigma_radius(xformedSigma);
148+
int kernelSize = 2 * radius + 1;
149+
150+
SkASSERT(kernelSize % 2);
151+
SkASSERT(dimensions.width() % 2);
152+
SkASSERT(dimensions.height() % 2);
153+
154+
SkVector radii = rrectToDraw.getSimpleRadii();
155+
SkASSERT(SkScalarNearlyEqual(radii.fX, radii.fY));
156+
157+
const int halfWidthPlus1 = (dimensions.width() / 2) + 1;
158+
const int halfHeightPlus1 = (dimensions.height() / 2) + 1;
159+
160+
std::unique_ptr<float[]> kernel(new float[kernelSize]);
161+
162+
SkFillIn1DGaussianKernel(kernel.get(), xformedSigma, radius);
163+
164+
SkBitmap integral;
165+
if (!SkCreateIntegralTable(6 * xformedSigma, &integral)) {
166+
return {};
167+
}
168+
169+
SkBitmap result;
170+
if (!result.tryAllocPixels(SkImageInfo::MakeA8(dimensions.width(), dimensions.height()))) {
171+
return {};
172+
}
173+
174+
std::vector<float> topVec;
175+
topVec.reserve(dimensions.width());
176+
for (int x = 0; x < dimensions.width(); ++x) {
177+
if (x < rrectToDraw.rect().fLeft || x > rrectToDraw.rect().fRight) {
178+
topVec.push_back(-1);
179+
} else {
180+
if (x + 0.5f < rrectToDraw.rect().fLeft + radii.fX) { // in the circular section
181+
float xDist = rrectToDraw.rect().fLeft + radii.fX - x - 0.5f;
182+
float h = sqrtf(radii.fX * radii.fX - xDist * xDist);
183+
SkASSERT(0 <= h && h < radii.fY);
184+
topVec.push_back(rrectToDraw.rect().fTop + radii.fX - h + 3 * xformedSigma);
185+
} else {
186+
topVec.push_back(rrectToDraw.rect().fTop + 3 * xformedSigma);
187+
}
188+
}
189+
}
190+
191+
for (int y = 0; y < halfHeightPlus1; ++y) {
192+
uint8_t* scanline = result.getAddr8(0, y);
193+
194+
for (int x = 0; x < halfWidthPlus1; ++x) {
195+
scanline[x] = eval_H(x, y, topVec, kernel.get(), kernelSize, integral.getAddr8(0, 0),
196+
integral.width(), 6 * xformedSigma);
197+
scanline[dimensions.width() - x - 1] = scanline[x];
198+
}
199+
200+
memcpy(result.getAddr8(0, dimensions.height() - y - 1), scanline, result.rowBytes());
201+
}
202+
203+
result.setImmutable();
204+
205+
GrProxyProvider* proxyProvider = context->priv().proxyProvider();
206+
207+
sk_sp<GrTextureProxy> proxy = proxyProvider->createProxyFromBitmap(
208+
result, GrMipmapped::kNo, SkBackingFit::kExact, SkBudgeted::kYes);
209+
if (!proxy) {
210+
return {};
211+
}
212+
213+
GrSwizzle swizzle =
214+
context->priv().caps()->getReadSwizzle(proxy->backendFormat(), GrColorType::kAlpha_8);
215+
return {std::move(proxy), kBlurredRRectMaskOrigin, swizzle};
216+
}
217+
86218
static std::unique_ptr<GrFragmentProcessor> find_or_create_rrect_blur_mask_fp(
87219
GrRecordingContext* context,
88220
const SkRRect& rrectToDraw,
@@ -106,7 +238,12 @@ static std::unique_ptr<GrFragmentProcessor> find_or_create_rrect_blur_mask_fp(
106238
return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, m);
107239
}
108240

109-
auto mask = create_mask_on_gpu(context, rrectToDraw, dimensions, xformedSigma);
241+
GrSurfaceProxyView mask;
242+
if (proxyProvider->isDDLProvider() == GrDDLProvider::kNo) {
243+
mask = create_mask_on_gpu(context, rrectToDraw, dimensions, xformedSigma);
244+
} else {
245+
mask = create_mask_on_cpu(context, rrectToDraw, dimensions, xformedSigma);
246+
}
110247
if (!mask) {
111248
return nullptr;
112249
}
@@ -200,18 +337,18 @@ half2 texCoord = translatedFragPos / proxyDims;)SkSL",
200337
args.fUniformHandler->getUniformCStr(proxyRectVar),
201338
args.fUniformHandler->getUniformCStr(blurRadiusVar),
202339
args.fUniformHandler->getUniformCStr(cornerRadiusVar));
203-
SkString _sample10076 = this->invokeChild(0, args);
340+
SkString _sample15531 = this->invokeChild(0, args);
204341
fragBuilder->codeAppendf(
205342
R"SkSL(
206343
half4 inputColor = %s;)SkSL",
207-
_sample10076.c_str());
208-
SkString _coords10124("float2(texCoord)");
209-
SkString _sample10124 = this->invokeChild(1, args, _coords10124.c_str());
344+
_sample15531.c_str());
345+
SkString _coords15579("float2(texCoord)");
346+
SkString _sample15579 = this->invokeChild(1, args, _coords15579.c_str());
210347
fragBuilder->codeAppendf(
211348
R"SkSL(
212349
%s = inputColor * %s;
213350
)SkSL",
214-
args.fOutputColor, _sample10124.c_str());
351+
args.fOutputColor, _sample15579.c_str());
215352
}
216353

217354
private:

0 commit comments

Comments
 (0)