Remove sample locations usage from GL backend

Bug: skia:8921

Change-Id: If9e65a78f9e1fddb5c5ad5afa5612294935122e5
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/386837
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Robert Phillips 2021-03-22 11:26:45 -04:00 committed by Skia Commit-Bot
parent 43e3dc994e
commit 22970175f7
26 changed files with 21 additions and 864 deletions

View File

@ -1,397 +0,0 @@
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gm/gm.h"
#include "include/core/SkBlendMode.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkTypes.h"
#include "include/gpu/GrRecordingContext.h"
#include "include/gpu/GrTypes.h"
#include "include/private/GrTypesPriv.h"
#include "include/private/SkColorData.h"
#include "src/gpu/GrBuffer.h"
#include "src/gpu/GrCaps.h"
#include "src/gpu/GrColorSpaceXform.h"
#include "src/gpu/GrDirectContextPriv.h"
#include "src/gpu/GrGeometryProcessor.h"
#include "src/gpu/GrMemoryPool.h"
#include "src/gpu/GrOpFlushState.h"
#include "src/gpu/GrOpsRenderPass.h"
#include "src/gpu/GrPaint.h"
#include "src/gpu/GrPipeline.h"
#include "src/gpu/GrPrimitiveProcessor.h"
#include "src/gpu/GrProcessor.h"
#include "src/gpu/GrProcessorSet.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/GrSamplerState.h"
#include "src/gpu/GrShaderCaps.h"
#include "src/gpu/GrShaderVar.h"
#include "src/gpu/GrSurfaceDrawContext.h"
#include "src/gpu/GrSurfaceProxy.h"
#include "src/gpu/GrTextureProxy.h"
#include "src/gpu/GrUserStencilSettings.h"
#include "src/gpu/effects/GrPorterDuffXferProcessor.h"
#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
#include "src/gpu/glsl/GrGLSLPrimitiveProcessor.h"
#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
#include "src/gpu/glsl/GrGLSLVarying.h"
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
#include "src/gpu/ops/GrDrawOp.h"
#include "src/gpu/ops/GrOp.h"
#include "tools/gpu/ProxyUtils.h"
#include <memory>
#include <utility>
class GrAppliedClip;
class GrGLSLProgramDataManager;
namespace skiagm {
enum class GradType : bool {
kHW,
kSW
};
/**
* This test ensures that the shaderBuilder's sample offsets and sample mask are correlated with
* actual HW sample locations. It does so by drawing pseudo-random subpixel boxes, and only turning
* off the samples whose locations fall inside the boxes.
*/
class SampleLocationsGM : public GpuGM {
public:
SampleLocationsGM(GradType gradType, GrSurfaceOrigin origin)
: fGradType(gradType)
, fOrigin(origin) {}
private:
SkString onShortName() override {
return SkStringPrintf("samplelocations%s%s",
(GradType::kHW == fGradType) ? "_hwgrad" : "_swgrad",
(kTopLeft_GrSurfaceOrigin == fOrigin) ? "_topleft" : "_botleft");
}
SkISize onISize() override { return SkISize::Make(200, 200); }
DrawResult onDraw(GrRecordingContext*, GrSurfaceDrawContext*,
SkCanvas*, SkString* errorMsg) override;
const GradType fGradType;
const GrSurfaceOrigin fOrigin;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// SkSL code.
class SampleLocationsTestProcessor : public GrGeometryProcessor {
public:
static GrGeometryProcessor* Make(SkArenaAlloc* arena, GradType gradType) {
return arena->make([&](void* ptr) {
return new (ptr) SampleLocationsTestProcessor(gradType);
});
}
const char* name() const override { return "SampleLocationsTestProcessor"; }
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
b->add32((uint32_t)fGradType);
}
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
private:
SampleLocationsTestProcessor(GradType gradType)
: GrGeometryProcessor(kSampleLocationsTestProcessor_ClassID)
, fGradType(gradType) {
this->setWillUseCustomFeature(CustomFeatures::kSampleLocations);
}
const GradType fGradType;
class Impl;
using INHERITED = GrGeometryProcessor;
};
class SampleLocationsTestProcessor::Impl : public GrGLSLGeometryProcessor {
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
const auto& proc = args.fGP.cast<SampleLocationsTestProcessor>();
auto* v = args.fVertBuilder;
auto* f = args.fFragBuilder;
GrGLSLVarying coord(kFloat2_GrSLType);
GrGLSLVarying grad(kFloat2_GrSLType);
args.fVaryingHandler->addVarying("coord", &coord);
if (GradType::kSW == proc.fGradType) {
args.fVaryingHandler->addVarying("grad", &grad);
}
// Pixel grid.
v->codeAppendf("int x = sk_InstanceID %% 200;");
v->codeAppendf("int y = sk_InstanceID / 200;");
// Create pseudo-random rectangles inside a 16x16 subpixel grid. This works out nicely
// because there are 17 positions on the grid (including both edges), and 17 is a great
// prime number for generating pseudo-random numbers.
v->codeAppendf("int ileft = (sk_InstanceID*929) %% 17;");
v->codeAppendf("int iright = ileft + 1 + ((sk_InstanceID*1637) %% (17 - ileft));");
v->codeAppendf("int itop = (sk_InstanceID*313) %% 17;");
v->codeAppendf("int ibot = itop + 1 + ((sk_InstanceID*1901) %% (17 - itop));");
// Outset (or inset) the rectangle, for the very likely scenario that samples fall on exact
// 16ths of a pixel. GL_SUBPIXEL_BITS is allowed to be as low as 4, so try not to let the
// outset value to get too small.
v->codeAppendf("float outset = 1/32.0;");
v->codeAppendf("outset = (0 == (x + y) %% 2) ? -outset : +outset;");
v->codeAppendf("float l = float(ileft)/16.0 - outset;");
v->codeAppendf("float r = float(iright)/16.0 + outset;");
v->codeAppendf("float t = float(itop)/16.0 - outset;");
v->codeAppendf("float b = float(ibot)/16.0 + outset;");
v->codeAppendf("float2 vertexpos;");
v->codeAppendf("vertexpos.x = float(x) + ((0 == (sk_VertexID %% 2)) ? l : r);");
v->codeAppendf("vertexpos.y = float(y) + ((0 == (sk_VertexID / 2)) ? t : b);");
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
v->codeAppendf("%s.x = (0 == (sk_VertexID %% 2)) ? -1 : +1;", coord.vsOut());
v->codeAppendf("%s.y = (0 == (sk_VertexID / 2)) ? -1 : +1;", coord.vsOut());
if (GradType::kSW == proc.fGradType) {
v->codeAppendf("%s = 2/float2(r - l, b - t);", grad.vsOut());
}
// Fragment shader: Output RED.
f->codeAppendf("const half4 %s = half4(1,0,0,1);", args.fOutputColor);
f->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
// Now turn off all the samples inside our sub-rectangle. As long as the shaderBuilder's
// sample offsets and sample mask are correlated with actual HW sample locations, no red
// will bleed through.
f->codeAppendf("for (int i = 0; i < %i; ++i) {",
f->getProgramBuilder()->effectiveSampleCnt());
if (GradType::kHW == proc.fGradType) {
f->codeAppendf("float2x2 grad = float2x2(dFdx(%s), dFdy(%s));",
coord.fsIn(), coord.fsIn());
} else {
f->codeAppendf("float2x2 grad = float2x2(%s.x, 0, 0, %s.y);", grad.fsIn(), grad.fsIn());
}
f->codeAppendf( "float2 samplecoord = %s[i] * grad + %s;",
f->sampleOffsets(), coord.fsIn());
f->codeAppendf( "if (all(lessThanEqual(abs(samplecoord), float2(1)))) {");
f->maskOffMultisampleCoverage(
"~(1 << i)", GrGLSLFPFragmentBuilder::ScopeFlags::kInsideLoop);
f->codeAppendf( "}");
f->codeAppendf("}");
}
void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override {}
};
GrGLSLPrimitiveProcessor* SampleLocationsTestProcessor::createGLSLInstance(
const GrShaderCaps&) const {
return new Impl();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Draw Op.
static constexpr GrUserStencilSettings gStencilWrite(
GrUserStencilSettings::StaticInit<
0x0001,
GrUserStencilTest::kAlways,
0xffff,
GrUserStencilOp::kReplace,
GrUserStencilOp::kKeep,
0xffff>()
);
class SampleLocationsTestOp : public GrDrawOp {
public:
DEFINE_OP_CLASS_ID
static GrOp::Owner Make(
GrRecordingContext* ctx, const SkMatrix& viewMatrix, GradType gradType) {
return GrOp::Make<SampleLocationsTestOp>(ctx, gradType);
}
private:
SampleLocationsTestOp(GradType gradType) : GrDrawOp(ClassID()), fGradType(gradType) {
this->setBounds(SkRect::MakeIWH(200, 200), HasAABloat::kNo, IsHairline::kNo);
}
const char* name() const override { return "SampleLocationsTestOp"; }
FixedFunctionFlags fixedFunctionFlags() const override {
return FixedFunctionFlags::kUsesHWAA | FixedFunctionFlags::kUsesStencil;
}
GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
bool hasMixedSampledCoverage, GrClampType) override {
return GrProcessorSet::EmptySetAnalysis();
}
GrProgramInfo* createProgramInfo(const GrCaps* caps,
SkArenaAlloc* arena,
const GrSurfaceProxyView& writeView,
GrAppliedClip&& appliedClip,
const GrXferProcessor::DstProxyView& dstProxyView,
GrXferBarrierFlags renderPassXferBarriers,
GrLoadOp colorLoadOp) const {
GrGeometryProcessor* geomProc = SampleLocationsTestProcessor::Make(arena, fGradType);
GrPipeline::InputFlags flags = GrPipeline::InputFlags::kHWAntialias;
return sk_gpu_test::CreateProgramInfo(caps, arena, writeView,
std::move(appliedClip), dstProxyView,
geomProc, SkBlendMode::kSrcOver,
GrPrimitiveType::kTriangleStrip,
renderPassXferBarriers, colorLoadOp,
flags, &gStencilWrite);
}
GrProgramInfo* createProgramInfo(GrOpFlushState* flushState) const {
return this->createProgramInfo(&flushState->caps(),
flushState->allocator(),
flushState->writeView(),
flushState->detachAppliedClip(),
flushState->dstProxyView(),
flushState->renderPassBarriers(),
flushState->colorLoadOp());
}
void onPrePrepare(GrRecordingContext* context,
const GrSurfaceProxyView& writeView,
GrAppliedClip* clip,
const GrXferProcessor::DstProxyView& dstProxyView,
GrXferBarrierFlags renderPassXferBarriers,
GrLoadOp colorLoadOp) final {
// We're going to create the GrProgramInfo (and the GrPipeline and geometry processor
// it relies on) in the DDL-record-time arena.
SkArenaAlloc* arena = context->priv().recordTimeAllocator();
// This is equivalent to a GrOpFlushState::detachAppliedClip
GrAppliedClip appliedClip = clip ? std::move(*clip) : GrAppliedClip::Disabled();
fProgramInfo = this->createProgramInfo(context->priv().caps(), arena, writeView,
std::move(appliedClip), dstProxyView,
renderPassXferBarriers, colorLoadOp);
context->priv().recordProgramInfo(fProgramInfo);
}
void onPrepare(GrOpFlushState*) final {}
void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) final {
if (!fProgramInfo) {
fProgramInfo = this->createProgramInfo(flushState);
}
flushState->bindPipelineAndScissorClip(*fProgramInfo, SkRect::MakeIWH(200, 200));
flushState->bindBuffers(nullptr, nullptr, nullptr);
flushState->drawInstanced(200*200, 0, 4, 0);
}
const GradType fGradType;
// The program info (and both the GrPipeline and GrPrimitiveProcessor it relies on), when
// allocated, are allocated in either the ddl-record-time or flush-time arena. It is the
// arena's job to free up their memory so we just have a bare programInfo pointer here. We
// don't even store the GrPipeline and GrPrimitiveProcessor pointers here bc they are
// guaranteed to have the same lifetime as the program info.
GrProgramInfo* fProgramInfo = nullptr;
friend class ::GrOp; // for ctor
using INHERITED = GrDrawOp;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Test.
DrawResult SampleLocationsGM::onDraw(GrRecordingContext* ctx, GrSurfaceDrawContext* rtc,
SkCanvas* canvas, SkString* errorMsg) {
if (!ctx->priv().caps()->sampleLocationsSupport()) {
*errorMsg = "Requires support for sample locations.";
return DrawResult::kSkip;
}
if (!ctx->priv().caps()->shaderCaps()->sampleMaskSupport()) {
*errorMsg = "Requires support for sample mask.";
return DrawResult::kSkip;
}
if (!ctx->priv().caps()->drawInstancedSupport()) {
*errorMsg = "Requires support for instanced rendering.";
return DrawResult::kSkip;
}
if (rtc->numSamples() <= 1 && !ctx->priv().caps()->mixedSamplesSupport()) {
*errorMsg = "MSAA and mixed samples only.";
return DrawResult::kSkip;
}
auto offscreenRTC = GrSurfaceDrawContext::Make(
ctx, rtc->colorInfo().colorType(), nullptr, SkBackingFit::kExact, {200, 200},
rtc->numSamples(), GrMipmapped::kNo, GrProtected::kNo, fOrigin);
if (!offscreenRTC) {
*errorMsg = "Failed to create offscreen render target.";
return DrawResult::kFail;
}
if (offscreenRTC->numSamples() <= 1 &&
!offscreenRTC->asRenderTargetProxy()->canUseMixedSamples(*ctx->priv().caps())) {
*errorMsg = "MSAA and mixed samples only.";
return DrawResult::kSkip;
}
static constexpr GrUserStencilSettings kStencilCover(
GrUserStencilSettings::StaticInit<
0x0000,
GrUserStencilTest::kNotEqual,
0xffff,
GrUserStencilOp::kZero,
GrUserStencilOp::kKeep,
0xffff>()
);
offscreenRTC->clear(SkPMColor4f{0, 1, 0, 1});
// Stencil.
offscreenRTC->addDrawOp(SampleLocationsTestOp::Make(ctx, canvas->getTotalMatrix(), fGradType));
// Cover.
GrPaint coverPaint;
coverPaint.setColor4f({1,0,0,1});
coverPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrcOver));
rtc->stencilRect(nullptr, &kStencilCover, std::move(coverPaint), GrAA::kNo, SkMatrix::I(),
SkRect::MakeWH(200, 200));
// Copy offscreen texture to canvas.
rtc->drawTexture(nullptr,
offscreenRTC->readSurfaceView(),
offscreenRTC->colorInfo().alphaType(),
GrSamplerState::Filter::kNearest,
GrSamplerState::MipmapMode::kNone,
SkBlendMode::kSrc,
SK_PMColor4fWHITE,
{0, 0, 200, 200},
{0, 0, 200, 200},
GrAA::kNo,
GrQuadAAFlags::kNone,
SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint,
SkMatrix::I(),
nullptr);
return skiagm::DrawResult::kOk;
}
DEF_GM( return new SampleLocationsGM(GradType::kHW, kTopLeft_GrSurfaceOrigin); )
DEF_GM( return new SampleLocationsGM(GradType::kHW, kBottomLeft_GrSurfaceOrigin); )
DEF_GM( return new SampleLocationsGM(GradType::kSW, kTopLeft_GrSurfaceOrigin); )
DEF_GM( return new SampleLocationsGM(GradType::kSW, kBottomLeft_GrSurfaceOrigin); )
} // namespace skiagm

View File

@ -326,7 +326,6 @@ gm_sources = [
"$_gm/runtimeshader.cpp",
"$_gm/sample_matrix_constant.cpp",
"$_gm/sample_matrix_variable.cpp",
"$_gm/samplelocations.cpp",
"$_gm/samplerstress.cpp",
"$_gm/savelayer.cpp",
"$_gm/scaledemoji.cpp",

View File

@ -201,8 +201,6 @@ skia_gpu_sources = [
"$_src/gpu/GrSPIRVVaryingHandler.h",
"$_src/gpu/GrSWMaskHelper.cpp",
"$_src/gpu/GrSWMaskHelper.h",
"$_src/gpu/GrSamplePatternDictionary.cpp",
"$_src/gpu/GrSamplePatternDictionary.h",
"$_src/gpu/GrSamplerState.h",
"$_src/gpu/GrScissorState.h",
"$_src/gpu/GrSemaphore.h",

View File

@ -231,7 +231,6 @@ tests_sources = [
"$_tests/SRGBTest.cpp",
"$_tests/SVGDeviceTest.cpp",
"$_tests/SafeMathTest.cpp",
"$_tests/SamplePatternDictionaryTest.cpp",
"$_tests/SamplingTest.cpp",
"$_tests/ScalarTest.cpp",
"$_tests/ScaleToSidesTest.cpp",

View File

@ -48,11 +48,6 @@ private:
return;
}
if (programInfo->requestedFeatures() & GrProcessor::CustomFeatures::kSampleLocations) {
// Sample locations require a live renderTarget to compute the key
return;
}
GrProgramDesc desc = caps->makeDesc(nullptr, *programInfo);
if (!desc.isValid()) {
return;

View File

@ -577,17 +577,6 @@ void GrGpu::didWriteToSurface(GrSurface* surface, GrSurfaceOrigin origin, const
}
}
int GrGpu::findOrAssignSamplePatternKey(GrRenderTarget* renderTarget) {
SkASSERT(this->caps()->sampleLocationsSupport());
SkASSERT(renderTarget->numSamples() > 1 ||
(renderTarget->getStencilAttachment() &&
renderTarget->getStencilAttachment()->numSamples() > 1));
SkSTArray<16, SkPoint> sampleLocations;
this->querySampleLocations(renderTarget, &sampleLocations);
return fSamplePatternDictionary.findOrAssignSamplePatternKey(sampleLocations);
}
void GrGpu::executeFlushInfo(SkSpan<GrSurfaceProxy*> proxies,
SkSurface::BackendSurfaceAccess access,
const GrFlushInfo& info,

View File

@ -18,7 +18,6 @@
#include "src/gpu/GrCaps.h"
#include "src/gpu/GrOpsRenderPass.h"
#include "src/gpu/GrPixmap.h"
#include "src/gpu/GrSamplePatternDictionary.h"
#include "src/gpu/GrSwizzle.h"
#include "src/gpu/GrTextureProducer.h"
#include "src/gpu/GrXferProcessor.h"
@ -339,19 +338,6 @@ public:
bool copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
const SkIPoint& dstPoint);
// Queries the per-pixel HW sample locations for the given render target, and then finds or
// assigns a key that uniquely identifies the sample pattern. The actual sample locations can be
// retrieved with retrieveSampleLocations().
int findOrAssignSamplePatternKey(GrRenderTarget*);
// Retrieves the per-pixel HW sample locations for the given sample pattern key, and, as a
// by-product, the actual number of samples in use. (This may differ from the number of samples
// requested by the render target.) Sample locations are returned as 0..1 offsets relative to
// the top-left corner of the pixel.
const SkTArray<SkPoint>& retrieveSampleLocations(int samplePatternKey) const {
return fSamplePatternDictionary.retrieveSampleLocations(samplePatternKey);
}
// Returns a GrOpsRenderPass which GrOpsTasks send draw commands to instead of directly
// to the Gpu object. The 'bounds' rect is the content rect of the renderTarget.
// If a 'stencil' is provided it will be the one bound to 'renderTarget'. If one is not
@ -848,7 +834,6 @@ private:
uint32_t fResetBits;
// The context owns us, not vice-versa, so this ptr is not ref'ed by Gpu.
GrDirectContext* fContext;
GrSamplePatternDictionary fSamplePatternDictionary;
struct SubmittedProc {
SubmittedProc(GrGpuSubmittedProc proc, GrGpuSubmittedContext context)

View File

@ -102,12 +102,6 @@ void GrOpsRenderPass::bindPipeline(const GrProgramInfo& programInfo, const SkRec
}
#ifdef SK_DEBUG
GrProcessor::CustomFeatures processorFeatures = programInfo.requestedFeatures();
if (GrProcessor::CustomFeatures::kSampleLocations & processorFeatures) {
// Verify we always have the same sample pattern key, regardless of graphics state.
SkASSERT(this->gpu()->findOrAssignSamplePatternKey(fRenderTarget) ==
fRenderTarget->getSamplePatternKey());
}
fScissorStatus = (programInfo.pipeline().isScissorTestEnabled()) ?
DynamicStateStatus::kUninitialized : DynamicStateStatus::kDisabled;
bool hasTextures = (programInfo.primProc().numTextureSamplers() > 0);

View File

@ -119,7 +119,6 @@ public:
kPremulFragmentProcessor_ClassID,
kQuadEdgeEffect_ClassID,
kQuadPerEdgeAAGeometryProcessor_ClassID,
kSampleLocationsTestProcessor_ClassID,
kSeriesFragmentProcessor_ClassID,
kShaderPDXferProcessor_ClassID,
kStencilResolveProcessor_ClassID,
@ -168,7 +167,6 @@ public:
*/
enum class CustomFeatures {
kNone = 0,
kSampleLocations = 1 << 0,
};
GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(CustomFeatures);

View File

@ -150,11 +150,6 @@ static void gen_key(GrProcessorKeyBuilder* b,
gen_xp_key(pipeline.getXferProcessor(), caps, pipeline, b);
if (programInfo.requestedFeatures() & GrProcessor::CustomFeatures::kSampleLocations) {
SkASSERT(pipeline.isHWAntialiasState());
b->add32(renderTarget->getSamplePatternKey(), "samplePattern");
}
b->addBits(16, pipeline.writeSwizzle().asKey(), "writeSwizzle");
// If we knew the shader won't depend on origin, we could skip this (and use the same program
// for both origins). Instrumenting all fragment processors would be difficult and error prone.

View File

@ -12,7 +12,6 @@
#include "src/gpu/GrAttachment.h"
#include "src/gpu/GrBackendUtils.h"
#include "src/gpu/GrGpu.h"
#include "src/gpu/GrSamplePatternDictionary.h"
#include "src/gpu/GrStencilSettings.h"
#include "src/gpu/GrSurfaceDrawContext.h"
@ -23,8 +22,8 @@ GrRenderTarget::GrRenderTarget(GrGpu* gpu,
GrAttachment* stencil)
: INHERITED(gpu, dimensions, isProtected)
, fStencilAttachment(stencil)
, fSampleCnt(sampleCount)
, fSamplePatternKey(GrSamplePatternDictionary::kInvalidSamplePatternKey) {}
, fSampleCnt(sampleCount) {
}
GrRenderTarget::~GrRenderTarget() = default;
@ -42,13 +41,7 @@ void GrRenderTarget::onAbandon() {
void GrRenderTarget::attachStencilAttachment(sk_sp<GrAttachment> stencil) {
#ifdef SK_DEBUG
if (fSampleCnt == 1) {
// TODO: We don't expect a mixed sampled render target to ever change its stencil buffer
// right now. But if it does swap in a stencil buffer with a different number of samples,
// and if we have a valid fSamplePatternKey, we will need to invalidate fSamplePatternKey
// here and add tests to make sure we it properly.
SkASSERT(GrSamplePatternDictionary::kInvalidSamplePatternKey == fSamplePatternKey);
} else {
if (fSampleCnt > 1) {
// Render targets with >1 color sample should never use mixed samples. (This would lead to
// different sample patterns, depending on stencil state.)
SkASSERT(!stencil || stencil->numSamples() == fSampleCnt);
@ -72,27 +65,3 @@ int GrRenderTarget::numStencilBits() const {
return GrBackendFormatStencilBits(this->getStencilAttachment()->backendFormat());
}
int GrRenderTarget::getSamplePatternKey() {
#ifdef SK_DEBUG
if (fSampleCnt <= 1) {
// If the color buffer is not multisampled, the sample pattern better come from the stencil
// buffer (mixed samples).
SkASSERT(fStencilAttachment && fStencilAttachment->numSamples() > 1);
} else {
// The color sample count and stencil count cannot both be unequal and both greater than
// one. If this were the case, there would be more than one sample pattern associated with
// the render target.
SkASSERT(!fStencilAttachment || fStencilAttachment->numSamples() == fSampleCnt);
}
#endif
if (GrSamplePatternDictionary::kInvalidSamplePatternKey == fSamplePatternKey) {
fSamplePatternKey = this->getGpu()->findOrAssignSamplePatternKey(this);
}
SkASSERT(fSamplePatternKey != GrSamplePatternDictionary::kInvalidSamplePatternKey);
return fSamplePatternKey;
}
const SkTArray<SkPoint>& GrRenderTarget::getSampleLocations() {
int samplePatternKey = this->getSamplePatternKey();
return this->getGpu()->retrieveSampleLocations(samplePatternKey);
}

View File

@ -79,7 +79,6 @@ private:
sk_sp<GrAttachment> fStencilAttachment;
int fSampleCnt;
int fSamplePatternKey;
using INHERITED = GrSurface;
};

View File

@ -1,42 +0,0 @@
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/GrSamplePatternDictionary.h"
bool GrSamplePatternDictionary::LessThan::operator()(
const SkTArray<SkPoint>& a, const SkTArray<SkPoint>& b) const {
if (a.count() != b.count()) {
return a.count() < b.count();
}
for (int i = 0; i < a.count(); ++i) {
// This doesn't have geometric meaning. We just need to define an ordering for std::map.
if (a[i].x() != b[i].x()) {
return a[i].x() < b[i].x();
}
if (a[i].y() != b[i].y()) {
return a[i].y() < b[i].y();
}
}
return false; // Both sample patterns are equal, therefore, "a < b" is false.
}
int GrSamplePatternDictionary::findOrAssignSamplePatternKey(
const SkTArray<SkPoint>& sampleLocations) {
if (std::numeric_limits<int>::max() == fSampleLocationsArray.count()) {
return 0;
}
const auto& insertResult = fSamplePatternKeyMap.insert(
{sampleLocations, fSampleLocationsArray.count()});
if (insertResult.second) {
// This means the "insert" call did not find the pattern in the key map already, and
// therefore an actual insertion took place. (We don't expect to see many unique sample
// patterns.)
const SkTArray<SkPoint>& sampleLocations = insertResult.first->first;
fSampleLocationsArray.push_back(&sampleLocations);
}
return insertResult.first->second; // Return the new sample pattern key.
}

View File

@ -1,39 +0,0 @@
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrSamplePatternDictionary_DEFINED
#define GrSamplePatternDictionary_DEFINED
#include "include/core/SkPoint.h"
#include "include/private/SkTArray.h"
#include <map>
/**
* A bidirectional dictionary mapping between sample patterns (i.e., a list of sample locations) and
* unique keys. Since we expect that most render targets will draw from the same small pool of
* sample patterns, we favor sample pattern keys over actual arrays of points.
*/
class GrSamplePatternDictionary {
public:
static constexpr int kInvalidSamplePatternKey = -1;
int findOrAssignSamplePatternKey(const SkTArray<SkPoint>& sampleLocations);
const SkTArray<SkPoint>& retrieveSampleLocations(int samplePatternKey) const {
return *fSampleLocationsArray[samplePatternKey];
}
private:
struct LessThan {
bool operator()(const SkTArray<SkPoint>&, const SkTArray<SkPoint>&) const;
};
std::map<SkTArray<SkPoint>, int, LessThan> fSamplePatternKeyMap;
SkTArray<const SkTArray<SkPoint>*> fSampleLocationsArray;
};
#endif

View File

@ -43,7 +43,6 @@ GrShaderCaps::GrShaderCaps(const GrContextOptions& options) {
fRemovePowWithConstantExponent = false;
fMustWriteToFragColor = false;
fNoDefaultPrecisionForExternalSamplers = false;
fCanOnlyUseSampleMaskWithStencil = false;
fFlatInterpolationSupport = false;
fPreferFlatInterpolation = false;
fNoPerspectiveInterpolationSupport = false;
@ -124,7 +123,6 @@ void GrShaderCaps::dumpJSON(SkJSONWriter* writer) const {
writer->appendBool("Must write to sk_FragColor [workaround]", fMustWriteToFragColor);
writer->appendBool("Don't add default precision statement for samplerExternalOES",
fNoDefaultPrecisionForExternalSamplers);
writer->appendBool("Can only use sample mask with stencil", fCanOnlyUseSampleMaskWithStencil);
writer->appendBool("Flat interpolation support", fFlatInterpolationSupport);
writer->appendBool("Prefer flat interpolation", fPreferFlatInterpolation);
writer->appendBool("No perspective interpolation support", fNoPerspectiveInterpolationSupport);

View File

@ -165,11 +165,6 @@ public:
return fNoDefaultPrecisionForExternalSamplers;
}
// The sample mask round rect op draws nothing on several Adreno and Radeon bots. Other ops that
// use sample mask while rendering to stencil seem to work fine.
// http://skbug.com/8921
bool canOnlyUseSampleMaskWithStencil() const { return fCanOnlyUseSampleMaskWithStencil; }
// ANGLE disallows do loops altogether, and we're seeing crashes on Tegra3 with do loops in at
// least some cases.
bool canUseDoLoops() const { return fCanUseDoLoops; }
@ -306,7 +301,6 @@ private:
bool fRemovePowWithConstantExponent : 1;
bool fMustWriteToFragColor : 1;
bool fNoDefaultPrecisionForExternalSamplers : 1;
bool fCanOnlyUseSampleMaskWithStencil : 1;
bool fColorSpaceMathNeedsFloat : 1;
bool fCanUseDoLoops : 1;
bool fCanUseFastMath : 1;

View File

@ -4067,13 +4067,6 @@ void GrGLCaps::applyDriverCorrectnessWorkarounds(const GrGLContextInfo& ctxInfo,
fTiledRenderingSupport = false;
}
if (kQualcomm_GrGLVendor == ctxInfo.vendor() || kATI_GrGLVendor == ctxInfo.vendor()) {
// The sample mask round rect op draws nothing on several Adreno and Radeon bots. Other ops
// that use sample mask while rendering to stencil seem to work fine.
// http://skbug.com/8921
shaderCaps->fCanOnlyUseSampleMaskWithStencil = true;
}
if (ctxInfo.angleBackend() == GrGLANGLEBackend::kD3D9) {
formatWorkarounds->fDisallowBGRA8ReadPixels = true;
}
@ -4116,7 +4109,6 @@ void GrGLCaps::onApplyOptionsOverrides(const GrContextOptions& options) {
SkASSERT(!fDetachStencilFromMSAABuffersBeforeReadPixels);
SkASSERT(!fDontSetBaseOrMaxLevelForExternalTextures);
SkASSERT(!fNeverDisableColorWrites);
SkASSERT(!fShaderCaps->fCanOnlyUseSampleMaskWithStencil);
}
if (options.fShaderCacheStrategy < GrContextOptions::ShaderCacheStrategy::kBackendBinary) {
fProgramBinarySupport = false;

View File

@ -3673,8 +3673,6 @@ void GrGLGpu::deleteBackendTexture(const GrBackendTexture& tex) {
}
bool GrGLGpu::compile(const GrProgramDesc& desc, const GrProgramInfo& programInfo) {
SkASSERT(!(GrProcessor::CustomFeatures::kSampleLocations & programInfo.requestedFeatures()));
GrThreadSafePipelineBuilder::Stats::ProgramCacheResult stat;
sk_sp<GrGLProgram> tmp = fProgramCache->findOrCreateProgram(this->getContext(),

View File

@ -360,20 +360,10 @@ private:
sk_sp<GrGLProgram> findOrCreateProgram(GrDirectContext*,
GrRenderTarget*,
const GrProgramInfo&);
sk_sp<GrGLProgram> findOrCreateProgram(GrDirectContext* dContext,
const GrProgramDesc& desc,
const GrProgramInfo& programInfo,
Stats::ProgramCacheResult* stat) {
sk_sp<GrGLProgram> tmp = this->findOrCreateProgram(dContext, nullptr, desc,
programInfo, stat);
if (!tmp) {
fStats.incNumPreCompilationFailures();
} else {
fStats.incNumPreProgramCacheResult(*stat);
}
return tmp;
}
sk_sp<GrGLProgram> findOrCreateProgram(GrDirectContext*,
const GrProgramDesc&,
const GrProgramInfo&,
Stats::ProgramCacheResult*);
bool precompileShader(GrDirectContext*, const SkData& key, const SkData& data);
private:

View File

@ -71,6 +71,20 @@ sk_sp<GrGLProgram> GrGLGpu::ProgramCache::findOrCreateProgram(GrDirectContext* d
return tmp;
}
sk_sp<GrGLProgram> GrGLGpu::ProgramCache::findOrCreateProgram(GrDirectContext* dContext,
const GrProgramDesc& desc,
const GrProgramInfo& programInfo,
Stats::ProgramCacheResult* stat) {
sk_sp<GrGLProgram> tmp = this->findOrCreateProgram(dContext, nullptr, desc, programInfo, stat);
if (!tmp) {
fStats.incNumPreCompilationFailures();
} else {
fStats.incNumPreProgramCacheResult(*stat);
}
return tmp;
}
sk_sp<GrGLProgram> GrGLGpu::ProgramCache::findOrCreateProgram(GrDirectContext* dContext,
GrRenderTarget* renderTarget,
const GrProgramDesc& desc,

View File

@ -28,78 +28,6 @@ GrGLSLFragmentShaderBuilder::GrGLSLFragmentShaderBuilder(GrGLSLProgramBuilder* p
fSubstageIndices.push_back(0);
}
const char* GrGLSLFragmentShaderBuilder::sampleOffsets() {
SkASSERT(CustomFeatures::kSampleLocations & fProgramBuilder->processorFeatures());
SkDEBUGCODE(fUsedProcessorFeaturesThisStage_DebugOnly |= CustomFeatures::kSampleLocations);
SkDEBUGCODE(fUsedProcessorFeaturesAllStages_DebugOnly |= CustomFeatures::kSampleLocations);
return "_sampleOffsets";
}
void GrGLSLFragmentShaderBuilder::maskOffMultisampleCoverage(
const char* mask, ScopeFlags scopeFlags) {
const GrShaderCaps& shaderCaps = *fProgramBuilder->shaderCaps();
if (!shaderCaps.sampleMaskSupport()) {
SkDEBUGFAIL("Attempted to mask sample coverage without support.");
return;
}
if (const char* extension = shaderCaps.sampleVariablesExtensionString()) {
this->addFeature(1 << kSampleVariables_GLSLPrivateFeature, extension);
}
if (!fHasModifiedSampleMask) {
fHasModifiedSampleMask = true;
if (ScopeFlags::kTopLevel != scopeFlags) {
this->codePrependf("sk_SampleMask[0] = ~0;");
}
if (!(ScopeFlags::kInsideLoop & scopeFlags)) {
this->codeAppendf("sk_SampleMask[0] = (%s);", mask);
return;
}
}
this->codeAppendf("sk_SampleMask[0] &= (%s);", mask);
}
void GrGLSLFragmentShaderBuilder::applyFnToMultisampleMask(
const char* fn, const char* grad, ScopeFlags scopeFlags) {
SkASSERT(CustomFeatures::kSampleLocations & fProgramBuilder->processorFeatures());
SkDEBUGCODE(fUsedProcessorFeaturesThisStage_DebugOnly |= CustomFeatures::kSampleLocations);
SkDEBUGCODE(fUsedProcessorFeaturesAllStages_DebugOnly |= CustomFeatures::kSampleLocations);
int sampleCnt = fProgramBuilder->effectiveSampleCnt();
SkASSERT(sampleCnt > 1);
this->codeAppendf("{");
if (!grad) {
SkASSERT(fProgramBuilder->shaderCaps()->shaderDerivativeSupport());
// In order to use HW derivatives, our neighbors within the same primitive must also be
// executing the same code. A per-pixel branch makes this pre-condition impossible to
// fulfill.
SkASSERT(!(ScopeFlags::kInsidePerPixelBranch & scopeFlags));
this->codeAppendf("float2 grad = float2(dFdx(%s), dFdy(%s));", fn, fn);
this->codeAppendf("float fnwidth = fwidth(%s);", fn);
grad = "grad";
} else {
this->codeAppendf("float fnwidth = abs(%s.x) + abs(%s.y);", grad, grad);
}
this->codeAppendf("int mask = 0;");
this->codeAppendf("if (%s*2 < fnwidth) {", fn); // Are ANY samples inside the implicit fn?
this->codeAppendf( "if (%s*-2 >= fnwidth) {", fn); // Are ALL samples inside the implicit?
this->codeAppendf( "mask = ~0;");
this->codeAppendf( "} else for (int i = 0; i < %i; ++i) {", sampleCnt);
this->codeAppendf( "float fnsample = dot(%s, _sampleOffsets[i]) + %s;", grad, fn);
this->codeAppendf( "if (fnsample < 0) {");
this->codeAppendf( "mask |= (1 << i);");
this->codeAppendf( "}");
this->codeAppendf( "}");
this->codeAppendf("}");
this->maskOffMultisampleCoverage("mask", scopeFlags);
this->codeAppendf("}");
}
SkString GrGLSLFPFragmentBuilder::writeProcessorFunction(GrGLSLFragmentProcessor* fp,
GrGLSLFragmentProcessor::EmitArgs& args) {
this->onBeforeChildProcEmitCode();
@ -240,20 +168,6 @@ GrSurfaceOrigin GrGLSLFragmentShaderBuilder::getSurfaceOrigin() const {
void GrGLSLFragmentShaderBuilder::onFinalize() {
SkASSERT(fProgramBuilder->processorFeatures() == fUsedProcessorFeaturesAllStages_DebugOnly);
if (CustomFeatures::kSampleLocations & fProgramBuilder->processorFeatures()) {
const SkTArray<SkPoint>& sampleLocations = fProgramBuilder->getSampleLocations();
this->definitions().appendf("const float2 _sampleOffsets[%i] = float2[%i](",
sampleLocations.count(), sampleLocations.count());
for (int i = 0; i < sampleLocations.count(); ++i) {
SkPoint offset = sampleLocations[i] - SkPoint::Make(.5f, .5f);
if (kBottomLeft_GrSurfaceOrigin == this->getSurfaceOrigin()) {
offset.fY = -offset.fY;
}
this->definitions().appendf("float2(%f, %f)", offset.x(), offset.y());
this->definitions().append((i + 1 != sampleLocations.count()) ? ", " : ");");
}
}
fProgramBuilder->varyingHandler()->getFragDecls(&this->inputs(), &this->outputs());
}

View File

@ -27,13 +27,6 @@ public:
(void) fDummyPadding;
}
/**
* Returns the variable name that holds the array of sample offsets from pixel center to each
* sample location. Before this is called, a processor must have advertised that it will use
* CustomFeatures::kSampleLocations.
*/
virtual const char* sampleOffsets() = 0;
enum class ScopeFlags {
// Every fragment will always execute this code, and will do it exactly once.
kTopLevel = 0,
@ -45,31 +38,6 @@ public:
kInsideLoop = (1 << 2)
};
/**
* Subtracts multisample coverage by AND-ing the sample mask with the provided "mask".
* Sample N corresponds to bit "1 << N".
*
* If the given scope is "kTopLevel" and the sample mask has not yet been modified, this method
* assigns the sample mask in place rather than pre-initializing it to ~0 then AND-ing it.
*
* Requires MSAA and GLSL support for sample variables.
*/
virtual void maskOffMultisampleCoverage(const char* mask, ScopeFlags) = 0;
/**
* Turns off coverage at each sample where the implicit function fn > 0.
*
* The provided "fn" value represents the implicit function at pixel center. We then approximate
* the implicit at each sample by riding the gradient, "grad", linearly from pixel center to
* each sample location.
*
* If "grad" is null, we approximate the gradient using HW derivatives.
*
* Requires MSAA and GLSL support for sample variables. Also requires HW derivatives if not
* providing a gradient.
*/
virtual void applyFnToMultisampleMask(const char* fn, const char* grad, ScopeFlags) = 0;
SkString writeProcessorFunction(GrGLSLFragmentProcessor*, GrGLSLFragmentProcessor::EmitArgs&);
virtual void forceHighPrecision() = 0;
@ -127,9 +95,6 @@ public:
GrGLSLFragmentShaderBuilder(GrGLSLProgramBuilder* program);
// GrGLSLFPFragmentBuilder interface.
const char* sampleOffsets() override;
void maskOffMultisampleCoverage(const char* mask, ScopeFlags) override;
void applyFnToMultisampleMask(const char* fn, const char* grad, ScopeFlags) override;
void forceHighPrecision() override { fForceHighPrecision = true; }
// GrGLSLXPFragmentBuilder interface.

View File

@ -51,16 +51,6 @@ public:
bool hasPointSize() const { return fProgramInfo.primitiveType() == GrPrimitiveType::kPoints; }
virtual SkSL::Compiler* shaderCompiler() const = 0;
// TODO: stop passing in the renderTarget for just the sampleLocations
int effectiveSampleCnt() {
SkASSERT(GrProcessor::CustomFeatures::kSampleLocations & fProgramInfo.requestedFeatures());
return fRenderTarget->getSampleLocations().count();
}
const SkTArray<SkPoint>& getSampleLocations() {
SkASSERT(GrProcessor::CustomFeatures::kSampleLocations & fProgramInfo.requestedFeatures());
return fRenderTarget->getSampleLocations();
}
const GrProgramDesc& desc() const { return fDesc; }
void appendUniformDecls(GrShaderFlags visibility, SkString*) const;

View File

@ -598,12 +598,6 @@ void GrVkCaps::applyDriverCorrectnessWorkarounds(const VkPhysicalDevicePropertie
if (kImagination_VkVendor == properties.vendorID) {
fShaderCaps->fAtan2ImplementedAsAtanYOverX = true;
}
if (kQualcomm_VkVendor == properties.vendorID) {
// The sample mask round rect op draws nothing on Adreno for the srcmode gm.
// http://skbug.com/8921
fShaderCaps->fCanOnlyUseSampleMaskWithStencil = true;
}
}
void GrVkCaps::initGrCaps(const GrVkInterface* vkInterface,

View File

@ -1924,8 +1924,6 @@ void GrVkGpu::deleteBackendTexture(const GrBackendTexture& tex) {
}
bool GrVkGpu::compile(const GrProgramDesc& desc, const GrProgramInfo& programInfo) {
SkASSERT(!(GrProcessor::CustomFeatures::kSampleLocations & programInfo.requestedFeatures()));
GrVkRenderPass::AttachmentsDescriptor attachmentsDescriptor;
GrVkRenderPass::AttachmentFlags attachmentFlags;
GrVkRenderTarget::ReconstructAttachmentsDescriptor(this->vkCaps(), programInfo,

View File

@ -1,132 +0,0 @@
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkPoint.h"
#include "include/core/SkTypes.h"
#include "include/utils/SkRandom.h"
#include "tests/Test.h"
#include <vector>
#if SK_SUPPORT_GPU
#include "src/gpu/GrSamplePatternDictionary.h"
static SkTArray<SkPoint> make_sample_pattern(const std::vector<SkPoint>& sampleLocations) {
return SkTArray<SkPoint>(sampleLocations.data(), sampleLocations.size());
}
static SkTArray<SkPoint> make_random_sample_pattern(SkRandom* rand) {
SkTArray<SkPoint> pattern;
int count = rand->nextULessThan(20) + 1;
pattern.reset(count);
for (int i = 0; i < count; ++i) {
pattern[i] = SkPoint::Make(rand->nextF(), rand->nextF());
}
return pattern;
}
// This test ensures that the sample pattern dictionary caches and retrieves patterns correctly.
DEF_TEST(SamplePatternDictionary, reporter) {
SkTArray<SkTArray<SkPoint>> testPatterns;
testPatterns.push_back() = make_sample_pattern({ // Intel on mac, msaa8, offscreen.
{0.562500, 0.312500},
{0.437500, 0.687500},
{0.812500, 0.562500},
{0.312500, 0.187500},
{0.187500, 0.812500},
{0.062500, 0.437500},
{0.687500, 0.937500},
{0.937500, 0.062500}
});
testPatterns.push_back() = make_sample_pattern({ // Intel on mac, msaa8, on-screen.
{0.562500, 0.687500},
{0.437500, 0.312500},
{0.812500, 0.437500},
{0.312500, 0.812500},
{0.187500, 0.187500},
{0.062500, 0.562500},
{0.687500, 0.062500},
{0.937500, 0.937500}
});
testPatterns.push_back() = make_sample_pattern({ // NVIDIA, msaa16.
{0.062500, 0.000000},
{0.250000, 0.125000},
{0.187500, 0.375000},
{0.437500, 0.312500},
{0.500000, 0.062500},
{0.687500, 0.187500},
{0.750000, 0.437500},
{0.937500, 0.250000},
{0.000000, 0.500000},
{0.312500, 0.625000},
{0.125000, 0.750000},
{0.375000, 0.875000},
{0.562500, 0.562500},
{0.812500, 0.687500},
{0.625000, 0.812500},
{0.875000, 0.937500}
});
testPatterns.push_back() = make_sample_pattern({ // NVIDIA, mixed samples, 16:1.
{0.250000, 0.125000},
{0.625000, 0.812500},
{0.500000, 0.062500},
{0.812500, 0.687500},
{0.187500, 0.375000},
{0.875000, 0.937500},
{0.125000, 0.750000},
{0.750000, 0.437500},
{0.937500, 0.250000},
{0.312500, 0.625000},
{0.437500, 0.312500},
{0.000000, 0.500000},
{0.375000, 0.875000},
{0.687500, 0.187500},
{0.062500, 0.000000},
{0.562500, 0.562500}
});
SkRandom rand;
for (int i = 0; i < 23; ++i) {
testPatterns.push_back(make_random_sample_pattern(&rand));
}
// Duplicate the initial 4 patterns, with slight differences.
testPatterns.push_back(testPatterns[0]);
testPatterns.back().back().fX += 0.001f;
testPatterns.push_back(testPatterns[1]);
testPatterns.back().back().fY -= 0.002f;
testPatterns.push_back(testPatterns[2]);
testPatterns.back().push_back(SkPoint::Make(.5f, .5f));
testPatterns.push_back(testPatterns[3]);
testPatterns.back().pop_back();
for (int i = 0; i < 13; ++i) {
testPatterns.push_back(make_random_sample_pattern(&rand));
}
GrSamplePatternDictionary dict;
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < testPatterns.count(); ++j) {
for (int k = 0; k < 3; ++k) {
const SkTArray<SkPoint>& pattern = testPatterns[testPatterns.count() - j - 1];
REPORTER_ASSERT(reporter, j == dict.findOrAssignSamplePatternKey(pattern));
}
}
}
for (int j = 0; j < testPatterns.count(); ++j) {
const SkTArray<SkPoint>& pattern = testPatterns[testPatterns.count() - j - 1];
REPORTER_ASSERT(reporter, dict.retrieveSampleLocations(j) == pattern);
}
}
#endif