2017-06-06 18:27:16 +00:00
|
|
|
/*
|
|
|
|
* 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"
|
2017-06-15 18:01:04 +00:00
|
|
|
#include "GrGpuCommandBuffer.h"
|
2018-06-12 14:11:12 +00:00
|
|
|
#include "GrMemoryPool.h"
|
2017-06-06 18:27:16 +00:00
|
|
|
#include "GrOpFlushState.h"
|
|
|
|
#include "GrRenderTargetContext.h"
|
|
|
|
#include "GrRenderTargetContextPriv.h"
|
|
|
|
#include "GrResourceProvider.h"
|
|
|
|
#include "SkMakeUnique.h"
|
2017-10-24 16:59:03 +00:00
|
|
|
#include "glsl/GrGLSLVertexGeoBuilder.h"
|
2017-06-06 18:27:16 +00:00
|
|
|
#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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static constexpr int kScreenSize = 6;
|
|
|
|
static constexpr int kNumMeshes = 4;
|
|
|
|
static constexpr int kScreenSplitX = kScreenSize/2;
|
|
|
|
static constexpr int kScreenSplitY = kScreenSize/2;
|
|
|
|
|
2018-06-26 13:12:38 +00:00
|
|
|
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),
|
2017-06-06 18:27:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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()
|
2018-06-19 18:33:47 +00:00
|
|
|
: INHERITED(kGrPipelineDynamicStateTestProcessor_ClassID) {
|
Change how GPs configure attributes
Adds setVertexAttributes and setInstanceAttributes. These take a pointer
to the first attribute, and a count. The count is the total number of
possible attributes, though some may not be initialized. The base class
computes the number of initialized attributes, pre-computes the strides,
and only allows subsequent access to the initialized attributes.
The attributes need to be allocated contiguously. Some GPs place them in
an array, though most just place them as consecutive members, and pass
a pointer to the first one.
Indexed access would be possible, but now it makes more sense to iterate
over all attributes, so enable that, and use range-based for everywhere.
Completely remove the per-attribute offset helper (again - possible, but
not real helpful), and make the stride always available. In many ops,
just use the GP's computed stride, rather than re-computing it.
Bug: skia:
Change-Id: Ie4cccb7969a98ee5a10b373e714fbd702e875b3e
Reviewed-on: https://skia-review.googlesource.com/c/169241
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2018-11-12 20:34:00 +00:00
|
|
|
this->setVertexAttributes(kAttributes, SK_ARRAY_COUNT(kAttributes));
|
2018-06-19 18:33:47 +00:00
|
|
|
}
|
2017-06-06 18:27:16 +00:00
|
|
|
|
|
|
|
const char* name() const override { return "GrPipelineDynamicStateTest Processor"; }
|
|
|
|
|
|
|
|
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
|
|
|
|
|
|
|
|
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
|
|
|
|
|
Change how GPs configure attributes
Adds setVertexAttributes and setInstanceAttributes. These take a pointer
to the first attribute, and a count. The count is the total number of
possible attributes, though some may not be initialized. The base class
computes the number of initialized attributes, pre-computes the strides,
and only allows subsequent access to the initialized attributes.
The attributes need to be allocated contiguously. Some GPs place them in
an array, though most just place them as consecutive members, and pass
a pointer to the first one.
Indexed access would be possible, but now it makes more sense to iterate
over all attributes, so enable that, and use range-based for everywhere.
Completely remove the per-attribute offset helper (again - possible, but
not real helpful), and make the stride always available. In many ops,
just use the GP's computed stride, rather than re-computing it.
Bug: skia:
Change-Id: Ie4cccb7969a98ee5a10b373e714fbd702e875b3e
Reviewed-on: https://skia-review.googlesource.com/c/169241
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2018-11-12 20:34:00 +00:00
|
|
|
const Attribute& inVertex() const { return kAttributes[0]; }
|
|
|
|
const Attribute& inColor() const { return kAttributes[1]; }
|
2018-06-19 18:33:47 +00:00
|
|
|
|
Change how GPs configure attributes
Adds setVertexAttributes and setInstanceAttributes. These take a pointer
to the first attribute, and a count. The count is the total number of
possible attributes, though some may not be initialized. The base class
computes the number of initialized attributes, pre-computes the strides,
and only allows subsequent access to the initialized attributes.
The attributes need to be allocated contiguously. Some GPs place them in
an array, though most just place them as consecutive members, and pass
a pointer to the first one.
Indexed access would be possible, but now it makes more sense to iterate
over all attributes, so enable that, and use range-based for everywhere.
Completely remove the per-attribute offset helper (again - possible, but
not real helpful), and make the stride always available. In many ops,
just use the GP's computed stride, rather than re-computing it.
Bug: skia:
Change-Id: Ie4cccb7969a98ee5a10b373e714fbd702e875b3e
Reviewed-on: https://skia-review.googlesource.com/c/169241
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2018-11-12 20:34:00 +00:00
|
|
|
private:
|
|
|
|
static constexpr Attribute kAttributes[] = {
|
|
|
|
{"vertex", kFloat2_GrVertexAttribType, kHalf2_GrSLType},
|
|
|
|
{"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType},
|
|
|
|
};
|
2017-06-06 18:27:16 +00:00
|
|
|
|
|
|
|
friend class GLSLPipelineDynamicStateTestProcessor;
|
|
|
|
typedef GrGeometryProcessor INHERITED;
|
|
|
|
};
|
Change how GPs configure attributes
Adds setVertexAttributes and setInstanceAttributes. These take a pointer
to the first attribute, and a count. The count is the total number of
possible attributes, though some may not be initialized. The base class
computes the number of initialized attributes, pre-computes the strides,
and only allows subsequent access to the initialized attributes.
The attributes need to be allocated contiguously. Some GPs place them in
an array, though most just place them as consecutive members, and pass
a pointer to the first one.
Indexed access would be possible, but now it makes more sense to iterate
over all attributes, so enable that, and use range-based for everywhere.
Completely remove the per-attribute offset helper (again - possible, but
not real helpful), and make the stride always available. In many ops,
just use the GP's computed stride, rather than re-computing it.
Bug: skia:
Change-Id: Ie4cccb7969a98ee5a10b373e714fbd702e875b3e
Reviewed-on: https://skia-review.googlesource.com/c/169241
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2018-11-12 20:34:00 +00:00
|
|
|
constexpr GrPrimitiveProcessor::Attribute GrPipelineDynamicStateTestProcessor::kAttributes[];
|
2017-06-06 18:27:16 +00:00
|
|
|
|
|
|
|
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);
|
Change how GPs configure attributes
Adds setVertexAttributes and setInstanceAttributes. These take a pointer
to the first attribute, and a count. The count is the total number of
possible attributes, though some may not be initialized. The base class
computes the number of initialized attributes, pre-computes the strides,
and only allows subsequent access to the initialized attributes.
The attributes need to be allocated contiguously. Some GPs place them in
an array, though most just place them as consecutive members, and pass
a pointer to the first one.
Indexed access would be possible, but now it makes more sense to iterate
over all attributes, so enable that, and use range-based for everywhere.
Completely remove the per-attribute offset helper (again - possible, but
not real helpful), and make the stride always available. In many ops,
just use the GP's computed stride, rather than re-computing it.
Bug: skia:
Change-Id: Ie4cccb7969a98ee5a10b373e714fbd702e875b3e
Reviewed-on: https://skia-review.googlesource.com/c/169241
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2018-11-12 20:34:00 +00:00
|
|
|
varyingHandler->addPassThroughAttribute(mp.inColor(), args.fOutputColor);
|
2017-06-06 18:27:16 +00:00
|
|
|
|
|
|
|
GrGLSLVertexBuilder* v = args.fVertBuilder;
|
Change how GPs configure attributes
Adds setVertexAttributes and setInstanceAttributes. These take a pointer
to the first attribute, and a count. The count is the total number of
possible attributes, though some may not be initialized. The base class
computes the number of initialized attributes, pre-computes the strides,
and only allows subsequent access to the initialized attributes.
The attributes need to be allocated contiguously. Some GPs place them in
an array, though most just place them as consecutive members, and pass
a pointer to the first one.
Indexed access would be possible, but now it makes more sense to iterate
over all attributes, so enable that, and use range-based for everywhere.
Completely remove the per-attribute offset helper (again - possible, but
not real helpful), and make the stride always available. In many ops,
just use the GP's computed stride, rather than re-computing it.
Bug: skia:
Change-Id: Ie4cccb7969a98ee5a10b373e714fbd702e875b3e
Reviewed-on: https://skia-review.googlesource.com/c/169241
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2018-11-12 20:34:00 +00:00
|
|
|
v->codeAppendf("float2 vertex = %s;", mp.inVertex().name());
|
2017-09-20 15:24:15 +00:00
|
|
|
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
|
2017-06-06 18:27:16 +00:00
|
|
|
|
2018-02-14 20:38:14 +00:00
|
|
|
GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
|
2017-09-18 18:10:39 +00:00
|
|
|
f->codeAppendf("%s = half4(1);", args.fOutputCoverage);
|
2017-06-06 18:27:16 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
GrGLSLPrimitiveProcessor*
|
|
|
|
GrPipelineDynamicStateTestProcessor::createGLSLInstance(const GrShaderCaps&) const {
|
|
|
|
return new GLSLPipelineDynamicStateTestProcessor;
|
|
|
|
}
|
|
|
|
|
|
|
|
class GrPipelineDynamicStateTestOp : public GrDrawOp {
|
|
|
|
public:
|
|
|
|
DEFINE_OP_CLASS_ID
|
|
|
|
|
2018-06-07 15:05:56 +00:00
|
|
|
static std::unique_ptr<GrDrawOp> Make(GrContext* context,
|
2018-08-15 06:53:25 +00:00
|
|
|
GrScissorTest scissorTest,
|
2018-06-07 15:05:56 +00:00
|
|
|
sk_sp<const GrBuffer> vbuff) {
|
2019-02-04 18:26:26 +00:00
|
|
|
GrOpMemoryPool* pool = context->priv().opMemoryPool();
|
2018-06-19 17:09:54 +00:00
|
|
|
|
2018-08-15 06:53:25 +00:00
|
|
|
return pool->allocate<GrPipelineDynamicStateTestOp>(scissorTest, std::move(vbuff));
|
2018-06-07 15:05:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2018-06-12 14:11:12 +00:00
|
|
|
friend class GrOpMemoryPool;
|
|
|
|
|
2018-08-15 06:53:25 +00:00
|
|
|
GrPipelineDynamicStateTestOp(GrScissorTest scissorTest, sk_sp<const GrBuffer> vbuff)
|
2018-07-02 14:08:49 +00:00
|
|
|
: INHERITED(ClassID())
|
2018-08-15 06:53:25 +00:00
|
|
|
, fScissorTest(scissorTest)
|
2018-07-02 14:08:49 +00:00
|
|
|
, fVertexBuffer(std::move(vbuff)) {
|
2017-06-06 18:27:16 +00:00
|
|
|
this->setBounds(SkRect::MakeIWH(kScreenSize, kScreenSize),
|
|
|
|
HasAABloat::kNo, IsZeroArea::kNo);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* name() const override { return "GrPipelineDynamicStateTestOp"; }
|
|
|
|
FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
|
2019-01-15 18:53:00 +00:00
|
|
|
GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*) override {
|
|
|
|
return GrProcessorSet::EmptySetAnalysis();
|
2017-06-16 14:04:34 +00:00
|
|
|
}
|
2017-06-06 18:27:16 +00:00
|
|
|
void onPrepare(GrOpFlushState*) override {}
|
2018-11-14 18:56:37 +00:00
|
|
|
void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override {
|
2019-01-31 19:13:59 +00:00
|
|
|
GrPipeline pipeline(fScissorTest, SkBlendMode::kSrc);
|
2017-06-06 18:27:16 +00:00
|
|
|
SkSTArray<kNumMeshes, GrMesh> meshes;
|
|
|
|
for (int i = 0; i < kNumMeshes; ++i) {
|
2017-06-13 16:55:06 +00:00
|
|
|
GrMesh& mesh = meshes.emplace_back(GrPrimitiveType::kTriangleStrip);
|
2017-06-06 18:27:16 +00:00
|
|
|
mesh.setNonIndexedNonInstanced(4);
|
2019-01-29 19:38:50 +00:00
|
|
|
mesh.setVertexData(fVertexBuffer, 4 * i);
|
2017-06-06 18:27:16 +00:00
|
|
|
}
|
2018-06-26 13:12:38 +00:00
|
|
|
GrPipeline::DynamicStateArrays dynamicState;
|
|
|
|
dynamicState.fScissorRects = kDynamicScissors;
|
|
|
|
state->rtCommandBuffer()->draw(GrPipelineDynamicStateTestProcessor(), pipeline, nullptr,
|
|
|
|
&dynamicState, meshes.begin(), 4,
|
2017-08-24 19:59:33 +00:00
|
|
|
SkRect::MakeIWH(kScreenSize, kScreenSize));
|
2017-06-06 18:27:16 +00:00
|
|
|
}
|
|
|
|
|
2018-08-15 06:53:25 +00:00
|
|
|
GrScissorTest fScissorTest;
|
2017-06-06 18:27:16 +00:00
|
|
|
const sk_sp<const GrBuffer> fVertexBuffer;
|
|
|
|
|
|
|
|
typedef GrDrawOp INHERITED;
|
|
|
|
};
|
|
|
|
|
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrPipelineDynamicStateTest, reporter, ctxInfo) {
|
2018-06-07 15:05:56 +00:00
|
|
|
GrContext* context = ctxInfo.grContext();
|
2019-02-04 18:26:26 +00:00
|
|
|
GrResourceProvider* rp = context->priv().resourceProvider();
|
2017-06-06 18:27:16 +00:00
|
|
|
|
2018-11-16 20:43:41 +00:00
|
|
|
const GrBackendFormat format =
|
2019-02-04 18:26:26 +00:00
|
|
|
context->priv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType);
|
2018-11-16 20:43:41 +00:00
|
|
|
|
2019-02-04 18:26:26 +00:00
|
|
|
sk_sp<GrRenderTargetContext> rtc(context->priv().makeDeferredRenderTargetContext(
|
2018-11-16 20:43:41 +00:00
|
|
|
format, SkBackingFit::kExact, kScreenSize,
|
|
|
|
kScreenSize, kRGBA_8888_GrPixelConfig, nullptr));
|
2017-06-06 18:27:16 +00:00
|
|
|
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,
|
2018-09-27 15:28:03 +00:00
|
|
|
GrResourceProvider::Flags::kRequireGpuMemory,
|
2017-06-06 18:27:16 +00:00
|
|
|
vdata));
|
|
|
|
if (!vbuff) {
|
|
|
|
ERRORF(reporter, "vbuff is null.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t resultPx[kScreenSize * kScreenSize];
|
|
|
|
|
2018-08-15 06:53:25 +00:00
|
|
|
for (GrScissorTest scissorTest : {GrScissorTest::kEnabled, GrScissorTest::kDisabled}) {
|
2018-11-05 20:06:26 +00:00
|
|
|
rtc->clear(nullptr, SkPMColor4f::FromBytes_RGBA(0xbaaaaaad),
|
|
|
|
GrRenderTargetContext::CanClearFullscreen::kYes);
|
2017-06-06 18:27:16 +00:00
|
|
|
rtc->priv().testingOnly_addDrawOp(
|
2018-08-15 06:53:25 +00:00
|
|
|
GrPipelineDynamicStateTestOp::Make(context, scissorTest, vbuff));
|
2017-06-06 18:27:16 +00:00
|
|
|
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;
|
2018-08-15 06:53:25 +00:00
|
|
|
if (GrScissorTest::kEnabled == scissorTest) {
|
2017-06-06 18:27:16 +00:00
|
|
|
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",
|
2018-08-15 06:53:25 +00:00
|
|
|
GrScissorTest::kEnabled == scissorTest ? "enabled" : "disabled", x, y,
|
2017-06-06 18:27:16 +00:00
|
|
|
actual, expected);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|