52cb1d059d
At present, only Xfer processors allow reading back from the destination image since they are in charge of blending. However, we'd like to expose the destination color to fragment processors and Runtime Effects in the future. To make this possible, the DstProxyView will need to be accessible outside of Xfer processors. This CL migrates DstProxyView to be a top-level Ganesh class and fixes up the references to it throughout Skia. It's interesting to note that several call sites were already using typedefs to hide the class nesting anyway. Change-Id: I93a294aa097f9319a968503c4f2f7e4f388ff033 Bug: skia:12066 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/414899 Auto-Submit: John Stiles <johnstiles@google.com> Reviewed-by: Greg Daniel <egdaniel@google.com> Commit-Queue: John Stiles <johnstiles@google.com>
214 lines
8.6 KiB
C++
214 lines
8.6 KiB
C++
/*
|
|
* Copyright 2016 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
// This is a GPU-backend specific test. It relies on static intializers to work
|
|
|
|
#include <memory>
|
|
|
|
#include "include/core/SkTypes.h"
|
|
#include "tests/Test.h"
|
|
|
|
#include "include/core/SkString.h"
|
|
#include "include/gpu/GrDirectContext.h"
|
|
#include "src/core/SkPointPriv.h"
|
|
#include "src/gpu/GrDirectContextPriv.h"
|
|
#include "src/gpu/GrGeometryProcessor.h"
|
|
#include "src/gpu/GrGpu.h"
|
|
#include "src/gpu/GrMemoryPool.h"
|
|
#include "src/gpu/GrOpFlushState.h"
|
|
#include "src/gpu/GrProgramInfo.h"
|
|
#include "src/gpu/GrSurfaceDrawContext.h"
|
|
#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
|
|
#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
|
|
#include "src/gpu/glsl/GrGLSLVarying.h"
|
|
#include "src/gpu/ops/GrMeshDrawOp.h"
|
|
#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
|
|
|
|
namespace {
|
|
class Op : public GrMeshDrawOp {
|
|
public:
|
|
DEFINE_OP_CLASS_ID
|
|
|
|
const char* name() const override { return "Dummy Op"; }
|
|
|
|
static GrOp::Owner Make(GrRecordingContext* rContext, int numAttribs) {
|
|
return GrOp::Make<Op>(rContext, numAttribs);
|
|
}
|
|
|
|
FixedFunctionFlags fixedFunctionFlags() const override {
|
|
return FixedFunctionFlags::kNone;
|
|
}
|
|
|
|
GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override {
|
|
return GrProcessorSet::EmptySetAnalysis();
|
|
}
|
|
|
|
private:
|
|
friend class ::GrOp;
|
|
|
|
Op(int numAttribs) : INHERITED(ClassID()), fNumAttribs(numAttribs) {
|
|
this->setBounds(SkRect::MakeWH(1.f, 1.f), HasAABloat::kNo, IsHairline::kNo);
|
|
}
|
|
|
|
GrProgramInfo* programInfo() override { return fProgramInfo; }
|
|
|
|
void onCreateProgramInfo(const GrCaps* caps,
|
|
SkArenaAlloc* arena,
|
|
const GrSurfaceProxyView& writeView,
|
|
GrAppliedClip&& appliedClip,
|
|
const GrDstProxyView& dstProxyView,
|
|
GrXferBarrierFlags renderPassXferBarriers,
|
|
GrLoadOp colorLoadOp) override {
|
|
class GP : public GrGeometryProcessor {
|
|
public:
|
|
static GrGeometryProcessor* Make(SkArenaAlloc* arena, int numAttribs) {
|
|
return arena->make([&](void* ptr) {
|
|
return new (ptr) GP(numAttribs);
|
|
});
|
|
}
|
|
|
|
const char* name() const override { return "Dummy GP"; }
|
|
|
|
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override {
|
|
class GLSLGP : public GrGLSLGeometryProcessor {
|
|
public:
|
|
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
|
|
const GP& gp = args.fGeomProc.cast<GP>();
|
|
args.fVaryingHandler->emitAttributes(gp);
|
|
WriteOutputPosition(args.fVertBuilder, gpArgs, gp.fAttributes[0].name());
|
|
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
|
|
fragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputColor);
|
|
fragBuilder->codeAppendf("const half4 %s = half4(1);",
|
|
args.fOutputCoverage);
|
|
}
|
|
void setData(const GrGLSLProgramDataManager&,
|
|
const GrShaderCaps&,
|
|
const GrGeometryProcessor&) override {}
|
|
};
|
|
return new GLSLGP();
|
|
}
|
|
void getGLSLProcessorKey(const GrShaderCaps&,
|
|
GrProcessorKeyBuilder* builder) const override {
|
|
builder->add32(fNumAttribs);
|
|
}
|
|
|
|
private:
|
|
GP(int numAttribs) : INHERITED(kGP_ClassID), fNumAttribs(numAttribs) {
|
|
SkASSERT(numAttribs > 1);
|
|
fAttribNames = std::make_unique<SkString[]>(numAttribs);
|
|
fAttributes = std::make_unique<Attribute[]>(numAttribs);
|
|
for (auto i = 0; i < numAttribs; ++i) {
|
|
fAttribNames[i].printf("attr%d", i);
|
|
// This gives us more of a mix of attribute types, and allows the
|
|
// component count to fit within the limits for iOS Metal.
|
|
if (i & 0x1) {
|
|
fAttributes[i] = {fAttribNames[i].c_str(), kFloat_GrVertexAttribType,
|
|
kFloat_GrSLType};
|
|
} else {
|
|
fAttributes[i] = {fAttribNames[i].c_str(), kFloat2_GrVertexAttribType,
|
|
kFloat2_GrSLType};
|
|
}
|
|
}
|
|
this->setVertexAttributes(fAttributes.get(), numAttribs);
|
|
}
|
|
|
|
int fNumAttribs;
|
|
std::unique_ptr<SkString[]> fAttribNames;
|
|
std::unique_ptr<Attribute[]> fAttributes;
|
|
|
|
using INHERITED = GrGeometryProcessor;
|
|
};
|
|
|
|
GrGeometryProcessor* gp = GP::Make(arena, fNumAttribs);
|
|
|
|
fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps,
|
|
arena,
|
|
writeView,
|
|
std::move(appliedClip),
|
|
dstProxyView,
|
|
gp,
|
|
GrProcessorSet::MakeEmptySet(),
|
|
GrPrimitiveType::kTriangles,
|
|
renderPassXferBarriers,
|
|
colorLoadOp,
|
|
GrPipeline::InputFlags::kNone);
|
|
}
|
|
|
|
void onPrepareDraws(Target* target) override {
|
|
if (!fProgramInfo) {
|
|
this->createProgramInfo(target);
|
|
}
|
|
|
|
size_t vertexStride = fProgramInfo->geomProc().vertexStride();
|
|
QuadHelper helper(target, vertexStride, 1);
|
|
SkPoint* vertices = reinterpret_cast<SkPoint*>(helper.vertices());
|
|
SkPointPriv::SetRectTriStrip(vertices, 0.f, 0.f, 1.f, 1.f, vertexStride);
|
|
fMesh = helper.mesh();
|
|
}
|
|
|
|
void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
|
|
if (!fProgramInfo || !fMesh) {
|
|
return;
|
|
}
|
|
|
|
flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
|
|
flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
|
|
flushState->drawMesh(*fMesh);
|
|
}
|
|
|
|
int fNumAttribs;
|
|
GrSimpleMesh* fMesh = nullptr;
|
|
GrProgramInfo* fProgramInfo = nullptr;
|
|
|
|
using INHERITED = GrMeshDrawOp;
|
|
};
|
|
} // namespace
|
|
|
|
DEF_GPUTEST_FOR_ALL_CONTEXTS(VertexAttributeCount, reporter, ctxInfo) {
|
|
auto context = ctxInfo.directContext();
|
|
#if GR_GPU_STATS
|
|
GrGpu* gpu = context->priv().getGpu();
|
|
#endif
|
|
|
|
auto surfaceDrawContext = GrSurfaceDrawContext::Make(context, GrColorType::kRGBA_8888, nullptr,
|
|
SkBackingFit::kApprox, {1, 1},
|
|
SkSurfaceProps());
|
|
if (!surfaceDrawContext) {
|
|
ERRORF(reporter, "Could not create render target context.");
|
|
return;
|
|
}
|
|
int attribCnt = context->priv().caps()->maxVertexAttributes();
|
|
if (!attribCnt) {
|
|
ERRORF(reporter, "No attributes allowed?!");
|
|
return;
|
|
}
|
|
context->flushAndSubmit();
|
|
context->priv().resetGpuStats();
|
|
#if GR_GPU_STATS
|
|
REPORTER_ASSERT(reporter, gpu->stats()->numDraws() == 0);
|
|
REPORTER_ASSERT(reporter, gpu->stats()->numFailedDraws() == 0);
|
|
#endif
|
|
// Adding discard to appease vulkan validation warning about loading uninitialized data on draw
|
|
surfaceDrawContext->discard();
|
|
|
|
GrPaint grPaint;
|
|
// This one should succeed.
|
|
surfaceDrawContext->addDrawOp(Op::Make(context, attribCnt));
|
|
context->flushAndSubmit();
|
|
#if GR_GPU_STATS
|
|
REPORTER_ASSERT(reporter, gpu->stats()->numDraws() == 1);
|
|
REPORTER_ASSERT(reporter, gpu->stats()->numFailedDraws() == 0);
|
|
#endif
|
|
context->priv().resetGpuStats();
|
|
surfaceDrawContext->addDrawOp(Op::Make(context, attribCnt + 1));
|
|
context->flushAndSubmit();
|
|
#if GR_GPU_STATS
|
|
REPORTER_ASSERT(reporter, gpu->stats()->numDraws() == 0);
|
|
REPORTER_ASSERT(reporter, gpu->stats()->numFailedDraws() == 1);
|
|
#endif
|
|
}
|