493489054f
Remove scissor rect from GrPipeline. Draws can specify "fixed dynamic state" which doesn't use the dynamism at all or can specify dynamic state arrays with an entry per GrMesh. When we state other than scissor rects this will allow the caller to use a mix of truly dynamic and fixed dynamic state. So a caller that only has dynamic scissor rects doesn't need to store its remaining unvarying state in an array. Change-Id: I8fcc07eb600c72a26cc712b185755c2116021a8a Reviewed-on: https://skia-review.googlesource.com/137223 Reviewed-by: Chris Dalton <csmartdalton@google.com> Commit-Queue: Brian Salomon <bsalomon@google.com>
238 lines
9.1 KiB
C++
238 lines
9.1 KiB
C++
/*
|
|
* Copyright 2017 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "SkTypes.h"
|
|
#include "Test.h"
|
|
|
|
#include "GrContext.h"
|
|
#include "GrColor.h"
|
|
#include "GrGeometryProcessor.h"
|
|
#include "GrGpuCommandBuffer.h"
|
|
#include "GrMemoryPool.h"
|
|
#include "GrOpFlushState.h"
|
|
#include "GrRenderTargetContext.h"
|
|
#include "GrRenderTargetContextPriv.h"
|
|
#include "GrResourceProvider.h"
|
|
#include "SkMakeUnique.h"
|
|
#include "glsl/GrGLSLVertexGeoBuilder.h"
|
|
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
|
#include "glsl/GrGLSLGeometryProcessor.h"
|
|
#include "glsl/GrGLSLVarying.h"
|
|
|
|
/**
|
|
* This is a GPU-backend specific test for dynamic pipeline state. It draws boxes using dynamic
|
|
* scissor rectangles then reads back the result to verify a successful test.
|
|
*/
|
|
|
|
using ScissorState = GrPipeline::ScissorState;
|
|
|
|
static constexpr int kScreenSize = 6;
|
|
static constexpr int kNumMeshes = 4;
|
|
static constexpr int kScreenSplitX = kScreenSize/2;
|
|
static constexpr int kScreenSplitY = kScreenSize/2;
|
|
|
|
static const SkIRect kDynamicScissors[kNumMeshes] = {
|
|
SkIRect::MakeLTRB(0, 0, kScreenSplitX, kScreenSplitY),
|
|
SkIRect::MakeLTRB(0, kScreenSplitY, kScreenSplitX, kScreenSize),
|
|
SkIRect::MakeLTRB(kScreenSplitX, 0, kScreenSize, kScreenSplitY),
|
|
SkIRect::MakeLTRB(kScreenSplitX, kScreenSplitY, kScreenSize, kScreenSize),
|
|
};
|
|
|
|
static const GrColor kMeshColors[kNumMeshes] {
|
|
GrColorPackRGBA(255, 0, 0, 255),
|
|
GrColorPackRGBA(0, 255, 0, 255),
|
|
GrColorPackRGBA(0, 0, 255, 255),
|
|
GrColorPackRGBA(0, 0, 0, 255)
|
|
};
|
|
|
|
struct Vertex {
|
|
float fX;
|
|
float fY;
|
|
GrColor fColor;
|
|
};
|
|
|
|
class GrPipelineDynamicStateTestProcessor : public GrGeometryProcessor {
|
|
public:
|
|
GrPipelineDynamicStateTestProcessor()
|
|
: INHERITED(kGrPipelineDynamicStateTestProcessor_ClassID) {
|
|
this->setVertexAttributeCnt(2);
|
|
}
|
|
|
|
const char* name() const override { return "GrPipelineDynamicStateTest Processor"; }
|
|
|
|
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
|
|
|
|
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
|
|
|
|
private:
|
|
const Attribute& onVertexAttribute(int i) const override {
|
|
return IthAttribute(i, kVertex, kColor);
|
|
}
|
|
|
|
static constexpr Attribute kVertex = {"vertex", kHalf2_GrVertexAttribType};
|
|
static constexpr Attribute kColor = {"color", kUByte4_norm_GrVertexAttribType};
|
|
|
|
friend class GLSLPipelineDynamicStateTestProcessor;
|
|
typedef GrGeometryProcessor INHERITED;
|
|
};
|
|
constexpr GrPrimitiveProcessor::Attribute GrPipelineDynamicStateTestProcessor::kVertex;
|
|
constexpr GrPrimitiveProcessor::Attribute GrPipelineDynamicStateTestProcessor::kColor;
|
|
|
|
class GLSLPipelineDynamicStateTestProcessor : public GrGLSLGeometryProcessor {
|
|
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
|
|
FPCoordTransformIter&& transformIter) final {}
|
|
|
|
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
|
|
const GrPipelineDynamicStateTestProcessor& mp =
|
|
args.fGP.cast<GrPipelineDynamicStateTestProcessor>();
|
|
|
|
GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
|
|
varyingHandler->emitAttributes(mp);
|
|
varyingHandler->addPassThroughAttribute(mp.kColor, args.fOutputColor);
|
|
|
|
GrGLSLVertexBuilder* v = args.fVertBuilder;
|
|
v->codeAppendf("float2 vertex = %s;", mp.kVertex.name());
|
|
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
|
|
|
|
GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
|
|
f->codeAppendf("%s = half4(1);", args.fOutputCoverage);
|
|
}
|
|
};
|
|
|
|
GrGLSLPrimitiveProcessor*
|
|
GrPipelineDynamicStateTestProcessor::createGLSLInstance(const GrShaderCaps&) const {
|
|
return new GLSLPipelineDynamicStateTestProcessor;
|
|
}
|
|
|
|
class GrPipelineDynamicStateTestOp : public GrDrawOp {
|
|
public:
|
|
DEFINE_OP_CLASS_ID
|
|
|
|
static std::unique_ptr<GrDrawOp> Make(GrContext* context,
|
|
ScissorState scissorState,
|
|
sk_sp<const GrBuffer> vbuff) {
|
|
GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
|
|
|
|
return pool->allocate<GrPipelineDynamicStateTestOp>(scissorState, std::move(vbuff));
|
|
}
|
|
|
|
private:
|
|
friend class GrOpMemoryPool;
|
|
|
|
GrPipelineDynamicStateTestOp(ScissorState scissorState, sk_sp<const GrBuffer> vbuff)
|
|
: INHERITED(ClassID())
|
|
, fScissorState(scissorState)
|
|
, fVertexBuffer(std::move(vbuff)) {
|
|
this->setBounds(SkRect::MakeIWH(kScreenSize, kScreenSize),
|
|
HasAABloat::kNo, IsZeroArea::kNo);
|
|
}
|
|
|
|
const char* name() const override { return "GrPipelineDynamicStateTestOp"; }
|
|
FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
|
|
RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*,
|
|
GrPixelConfigIsClamped) override {
|
|
return RequiresDstTexture::kNo;
|
|
}
|
|
bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; }
|
|
void onPrepare(GrOpFlushState*) override {}
|
|
void onExecute(GrOpFlushState* state) override {
|
|
GrRenderTargetProxy* proxy = state->drawOpArgs().fProxy;
|
|
GrPipeline pipeline(proxy, fScissorState, SkBlendMode::kSrc);
|
|
SkSTArray<kNumMeshes, GrMesh> meshes;
|
|
for (int i = 0; i < kNumMeshes; ++i) {
|
|
GrMesh& mesh = meshes.emplace_back(GrPrimitiveType::kTriangleStrip);
|
|
mesh.setNonIndexedNonInstanced(4);
|
|
mesh.setVertexData(fVertexBuffer.get(), 4 * i);
|
|
}
|
|
GrPipeline::DynamicStateArrays dynamicState;
|
|
dynamicState.fScissorRects = kDynamicScissors;
|
|
state->rtCommandBuffer()->draw(GrPipelineDynamicStateTestProcessor(), pipeline, nullptr,
|
|
&dynamicState, meshes.begin(), 4,
|
|
SkRect::MakeIWH(kScreenSize, kScreenSize));
|
|
}
|
|
|
|
ScissorState fScissorState;
|
|
const sk_sp<const GrBuffer> fVertexBuffer;
|
|
|
|
typedef GrDrawOp INHERITED;
|
|
};
|
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrPipelineDynamicStateTest, reporter, ctxInfo) {
|
|
GrContext* context = ctxInfo.grContext();
|
|
GrResourceProvider* rp = context->contextPriv().resourceProvider();
|
|
|
|
sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeDeferredRenderTargetContext(
|
|
SkBackingFit::kExact, kScreenSize, kScreenSize,
|
|
kRGBA_8888_GrPixelConfig, nullptr));
|
|
if (!rtc) {
|
|
ERRORF(reporter, "could not create render target context.");
|
|
return;
|
|
}
|
|
|
|
constexpr float d = (float) kScreenSize;
|
|
Vertex vdata[kNumMeshes * 4] = {
|
|
{0, 0, kMeshColors[0]},
|
|
{0, d, kMeshColors[0]},
|
|
{d, 0, kMeshColors[0]},
|
|
{d, d, kMeshColors[0]},
|
|
|
|
{0, 0, kMeshColors[1]},
|
|
{0, d, kMeshColors[1]},
|
|
{d, 0, kMeshColors[1]},
|
|
{d, d, kMeshColors[1]},
|
|
|
|
{0, 0, kMeshColors[2]},
|
|
{0, d, kMeshColors[2]},
|
|
{d, 0, kMeshColors[2]},
|
|
{d, d, kMeshColors[2]},
|
|
|
|
{0, 0, kMeshColors[3]},
|
|
{0, d, kMeshColors[3]},
|
|
{d, 0, kMeshColors[3]},
|
|
{d, d, kMeshColors[3]}
|
|
};
|
|
|
|
sk_sp<const GrBuffer> vbuff(rp->createBuffer(sizeof(vdata), kVertex_GrBufferType,
|
|
kDynamic_GrAccessPattern,
|
|
GrResourceProvider::kNoPendingIO_Flag |
|
|
GrResourceProvider::kRequireGpuMemory_Flag,
|
|
vdata));
|
|
if (!vbuff) {
|
|
ERRORF(reporter, "vbuff is null.");
|
|
return;
|
|
}
|
|
|
|
uint32_t resultPx[kScreenSize * kScreenSize];
|
|
|
|
for (ScissorState scissorState : {ScissorState::kEnabled, ScissorState::kDisabled}) {
|
|
rtc->clear(nullptr, 0xbaaaaaad, GrRenderTargetContext::CanClearFullscreen::kYes);
|
|
rtc->priv().testingOnly_addDrawOp(
|
|
GrPipelineDynamicStateTestOp::Make(context, scissorState, vbuff));
|
|
rtc->readPixels(SkImageInfo::Make(kScreenSize, kScreenSize,
|
|
kRGBA_8888_SkColorType, kPremul_SkAlphaType),
|
|
resultPx, 4 * kScreenSize, 0, 0, 0);
|
|
for (int y = 0; y < kScreenSize; ++y) {
|
|
for (int x = 0; x < kScreenSize; ++x) {
|
|
int expectedColorIdx;
|
|
if (ScissorState::kEnabled == scissorState) {
|
|
expectedColorIdx = (x < kScreenSplitX ? 0 : 2) + (y < kScreenSplitY ? 0 : 1);
|
|
} else {
|
|
expectedColorIdx = kNumMeshes - 1;
|
|
}
|
|
uint32_t expected = kMeshColors[expectedColorIdx];
|
|
uint32_t actual = resultPx[y * kScreenSize + x];
|
|
if (expected != actual) {
|
|
ERRORF(reporter, "[scissor=%s] pixel (%i,%i): got 0x%x expected 0x%x",
|
|
ScissorState::kEnabled == scissorState ? "enabled" : "disabled", x, y,
|
|
actual, expected);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|