2017-05-26 21:17:19 +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.
|
|
|
|
*/
|
|
|
|
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "tests/Test.h"
|
2017-05-26 21:17:19 +00:00
|
|
|
|
2022-04-05 16:56:01 +00:00
|
|
|
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "include/core/SkBitmap.h"
|
2022-04-05 16:56:01 +00:00
|
|
|
#include "include/core/SkColorSpace.h"
|
2020-07-06 14:56:46 +00:00
|
|
|
#include "include/gpu/GrDirectContext.h"
|
2021-12-20 17:37:56 +00:00
|
|
|
#include "src/gpu/KeyBuilder.h"
|
2021-12-21 23:01:47 +00:00
|
|
|
#include "src/gpu/ResourceKey.h"
|
2022-04-07 15:20:24 +00:00
|
|
|
#include "src/gpu/ganesh/GrCaps.h"
|
|
|
|
#include "src/gpu/ganesh/GrDirectContextPriv.h"
|
|
|
|
#include "src/gpu/ganesh/GrGeometryProcessor.h"
|
|
|
|
#include "src/gpu/ganesh/GrImageInfo.h"
|
|
|
|
#include "src/gpu/ganesh/GrMemoryPool.h"
|
|
|
|
#include "src/gpu/ganesh/GrOpFlushState.h"
|
|
|
|
#include "src/gpu/ganesh/GrOpsRenderPass.h"
|
|
|
|
#include "src/gpu/ganesh/GrProgramInfo.h"
|
|
|
|
#include "src/gpu/ganesh/GrResourceProvider.h"
|
|
|
|
#include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
|
|
|
|
#include "src/gpu/ganesh/glsl/GrGLSLVarying.h"
|
|
|
|
#include "src/gpu/ganesh/glsl/GrGLSLVertexGeoBuilder.h"
|
|
|
|
#include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelper.h"
|
|
|
|
#include "src/gpu/ganesh/v1/SurfaceDrawContext_v1.h"
|
2017-05-26 21:17:19 +00:00
|
|
|
|
2022-04-05 16:56:01 +00:00
|
|
|
#include <array>
|
|
|
|
#include <memory>
|
|
|
|
#include <vector>
|
|
|
|
|
2020-04-03 17:01:30 +00:00
|
|
|
#if 0
|
|
|
|
#include "tools/ToolUtils.h"
|
|
|
|
#define WRITE_PNG_CONTEXT_TYPE kANGLE_D3D11_ES3_ContextType
|
|
|
|
#endif
|
|
|
|
|
2021-12-21 23:01:47 +00:00
|
|
|
SKGPU_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
|
2017-05-26 21:17:19 +00:00
|
|
|
|
|
|
|
static constexpr int kBoxSize = 2;
|
|
|
|
static constexpr int kBoxCountY = 8;
|
|
|
|
static constexpr int kBoxCountX = 8;
|
|
|
|
static constexpr int kBoxCount = kBoxCountY * kBoxCountX;
|
|
|
|
|
|
|
|
static constexpr int kImageWidth = kBoxCountY * kBoxSize;
|
|
|
|
static constexpr int kImageHeight = kBoxCountX * kBoxSize;
|
|
|
|
|
|
|
|
static constexpr int kIndexPatternRepeatCount = 3;
|
|
|
|
constexpr uint16_t kIndexPattern[6] = {0, 1, 2, 1, 2, 3};
|
|
|
|
|
|
|
|
|
|
|
|
class DrawMeshHelper {
|
|
|
|
public:
|
|
|
|
DrawMeshHelper(GrOpFlushState* state) : fState(state) {}
|
|
|
|
|
|
|
|
sk_sp<const GrBuffer> getIndexBuffer();
|
|
|
|
|
2020-03-12 18:13:29 +00:00
|
|
|
sk_sp<const GrBuffer> makeIndexBuffer(const uint16_t[], int count);
|
|
|
|
|
2017-05-26 21:17:19 +00:00
|
|
|
template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const SkTArray<T>& data) {
|
|
|
|
return this->makeVertexBuffer(data.begin(), data.count());
|
|
|
|
}
|
2017-05-31 18:51:23 +00:00
|
|
|
template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const std::vector<T>& data) {
|
|
|
|
return this->makeVertexBuffer(data.data(), data.size());
|
|
|
|
}
|
2017-05-26 21:17:19 +00:00
|
|
|
template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const T* data, int count);
|
|
|
|
|
2021-06-17 18:55:07 +00:00
|
|
|
GrMeshDrawTarget* target() { return fState; }
|
2020-04-07 18:31:59 +00:00
|
|
|
|
2019-09-05 17:23:23 +00:00
|
|
|
sk_sp<const GrBuffer> fIndexBuffer;
|
2020-03-12 18:13:29 +00:00
|
|
|
sk_sp<const GrBuffer> fIndexBuffer2;
|
2019-09-05 17:23:23 +00:00
|
|
|
sk_sp<const GrBuffer> fInstBuffer;
|
2020-03-12 18:13:29 +00:00
|
|
|
sk_sp<const GrBuffer> fVertBuffer;
|
|
|
|
sk_sp<const GrBuffer> fVertBuffer2;
|
2020-04-07 18:31:59 +00:00
|
|
|
sk_sp<const GrBuffer> fDrawIndirectBuffer;
|
|
|
|
size_t fDrawIndirectBufferOffset;
|
2019-09-05 17:23:23 +00:00
|
|
|
|
2020-03-12 18:13:29 +00:00
|
|
|
GrOpsRenderPass* bindPipeline(GrPrimitiveType, bool isInstanced, bool hasVertexBuffer);
|
2017-05-26 21:17:19 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
GrOpFlushState* fState;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Box {
|
|
|
|
float fX, fY;
|
|
|
|
GrColor fColor;
|
|
|
|
};
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/**
|
2020-03-16 15:25:50 +00:00
|
|
|
* This is a GPU-backend specific test. It tries to test all possible usecases of
|
|
|
|
* GrOpsRenderPass::draw*. The test works by drawing checkerboards of colored boxes, reading back
|
|
|
|
* the pixels, and comparing with expected results. The boxes are drawn on integer boundaries and
|
|
|
|
* the (opaque) colors are chosen from the set (r,g,b) = (0,255)^3, so the GPU renderings ought to
|
|
|
|
* produce exact matches.
|
2017-05-26 21:17:19 +00:00
|
|
|
*/
|
|
|
|
|
2020-08-11 16:02:22 +00:00
|
|
|
static void run_test(GrDirectContext*, const char* testName, skiatest::Reporter*,
|
2021-07-28 19:13:20 +00:00
|
|
|
const std::unique_ptr<skgpu::v1::SurfaceDrawContext>&, const SkBitmap& gold,
|
2019-09-05 17:23:23 +00:00
|
|
|
std::function<void(DrawMeshHelper*)> prepareFn,
|
|
|
|
std::function<void(DrawMeshHelper*)> executeFn);
|
2017-05-26 21:17:19 +00:00
|
|
|
|
2020-04-03 17:01:30 +00:00
|
|
|
#ifdef WRITE_PNG_CONTEXT_TYPE
|
|
|
|
static bool IsContextTypeForOutputPNGs(skiatest::GrContextFactoryContextType type) {
|
|
|
|
return type == skiatest::GrContextFactoryContextType::WRITE_PNG_CONTEXT_TYPE;
|
|
|
|
}
|
|
|
|
DEF_GPUTEST_FOR_CONTEXTS(GrMeshTest, IsContextTypeForOutputPNGs, reporter, ctxInfo, nullptr) {
|
|
|
|
#else
|
2017-05-26 21:17:19 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrMeshTest, reporter, ctxInfo) {
|
2020-04-03 17:01:30 +00:00
|
|
|
#endif
|
2020-07-20 19:00:36 +00:00
|
|
|
auto dContext = ctxInfo.directContext();
|
2017-05-26 21:17:19 +00:00
|
|
|
|
2021-07-28 19:13:20 +00:00
|
|
|
auto sdc = skgpu::v1::SurfaceDrawContext::Make(
|
2020-07-20 19:00:36 +00:00
|
|
|
dContext, GrColorType::kRGBA_8888, nullptr, SkBackingFit::kExact,
|
2021-04-19 23:27:09 +00:00
|
|
|
{kImageWidth, kImageHeight}, SkSurfaceProps());
|
2021-07-28 19:13:20 +00:00
|
|
|
if (!sdc) {
|
2017-05-26 21:17:19 +00:00
|
|
|
ERRORF(reporter, "could not create render target context.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkTArray<Box> boxes;
|
|
|
|
SkTArray<std::array<Box, 4>> vertexData;
|
|
|
|
SkBitmap gold;
|
|
|
|
|
|
|
|
// ---- setup ----------
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setBlendMode(SkBlendMode::kSrc);
|
|
|
|
gold.allocN32Pixels(kImageWidth, kImageHeight);
|
|
|
|
|
|
|
|
SkCanvas goldCanvas(gold);
|
|
|
|
|
|
|
|
for (int y = 0; y < kBoxCountY; ++y) {
|
|
|
|
for (int x = 0; x < kBoxCountX; ++x) {
|
|
|
|
int c = y + x;
|
|
|
|
int rgb[3] = {-(c & 1) & 0xff, -((c >> 1) & 1) & 0xff, -((c >> 2) & 1) & 0xff};
|
|
|
|
|
|
|
|
const Box box = boxes.push_back() = {
|
2018-11-30 20:33:19 +00:00
|
|
|
float(x * kBoxSize),
|
|
|
|
float(y * kBoxSize),
|
|
|
|
GrColorPackRGBA(rgb[0], rgb[1], rgb[2], 255)
|
2017-05-26 21:17:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
std::array<Box, 4>& boxVertices = vertexData.push_back();
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
|
|
boxVertices[i] = {
|
2018-11-30 20:33:19 +00:00
|
|
|
box.fX + (i / 2) * kBoxSize,
|
|
|
|
box.fY + (i % 2) * kBoxSize,
|
|
|
|
box.fColor
|
2017-05-26 21:17:19 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
paint.setARGB(255, rgb[0], rgb[1], rgb[2]);
|
|
|
|
goldCanvas.drawRect(SkRect::MakeXYWH(box.fX, box.fY, kBoxSize, kBoxSize), paint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---- tests ----------
|
|
|
|
|
2018-11-30 20:33:19 +00:00
|
|
|
#define VALIDATE(buff) \
|
|
|
|
do { \
|
|
|
|
if (!buff) { \
|
|
|
|
ERRORF(reporter, #buff " is null."); \
|
|
|
|
return; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
2017-05-26 21:17:19 +00:00
|
|
|
|
2021-07-28 19:13:20 +00:00
|
|
|
run_test(dContext, "draw", reporter, sdc, gold,
|
2018-06-07 15:05:56 +00:00
|
|
|
[&](DrawMeshHelper* helper) {
|
|
|
|
SkTArray<Box> expandedVertexData;
|
|
|
|
for (int i = 0; i < kBoxCount; ++i) {
|
|
|
|
for (int j = 0; j < 6; ++j) {
|
|
|
|
expandedVertexData.push_back(vertexData[i][kIndexPattern[j]]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw boxes one line at a time to exercise base vertex.
|
2019-09-05 17:23:23 +00:00
|
|
|
helper->fVertBuffer = helper->makeVertexBuffer(expandedVertexData);
|
|
|
|
VALIDATE(helper->fVertBuffer);
|
|
|
|
},
|
|
|
|
[&](DrawMeshHelper* helper) {
|
2018-06-07 15:05:56 +00:00
|
|
|
for (int y = 0; y < kBoxCountY; ++y) {
|
2020-03-12 18:13:29 +00:00
|
|
|
auto pass = helper->bindPipeline(GrPrimitiveType::kTriangles, false, true);
|
2020-07-20 15:37:38 +00:00
|
|
|
pass->bindBuffers(nullptr, nullptr, helper->fVertBuffer);
|
2020-03-12 18:13:29 +00:00
|
|
|
pass->draw(kBoxCountX * 6, y * kBoxCountX * 6);
|
2018-06-07 15:05:56 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-07-28 19:13:20 +00:00
|
|
|
run_test(dContext, "drawIndexed", reporter, sdc, gold,
|
2019-09-05 17:23:23 +00:00
|
|
|
[&](DrawMeshHelper* helper) {
|
|
|
|
helper->fIndexBuffer = helper->getIndexBuffer();
|
|
|
|
VALIDATE(helper->fIndexBuffer);
|
|
|
|
helper->fVertBuffer = helper->makeVertexBuffer(vertexData);
|
|
|
|
VALIDATE(helper->fVertBuffer);
|
|
|
|
},
|
|
|
|
[&](DrawMeshHelper* helper) {
|
|
|
|
int baseRepetition = 0;
|
|
|
|
int i = 0;
|
|
|
|
// Start at various repetitions within the patterned index buffer to exercise base
|
|
|
|
// index.
|
|
|
|
while (i < kBoxCount) {
|
2019-12-18 15:43:10 +00:00
|
|
|
static_assert(kIndexPatternRepeatCount >= 3);
|
2020-02-07 15:36:46 +00:00
|
|
|
int repetitionCount = std::min(3 - baseRepetition, kBoxCount - i);
|
2019-09-05 17:23:23 +00:00
|
|
|
|
2020-03-12 18:13:29 +00:00
|
|
|
auto pass = helper->bindPipeline(GrPrimitiveType::kTriangles, false, true);
|
2020-07-20 15:37:38 +00:00
|
|
|
pass->bindBuffers(helper->fIndexBuffer, nullptr, helper->fVertBuffer);
|
2020-03-12 18:13:29 +00:00
|
|
|
pass->drawIndexed(repetitionCount * 6, baseRepetition * 6, baseRepetition * 4,
|
|
|
|
(baseRepetition + repetitionCount) * 4 - 1,
|
|
|
|
(i - baseRepetition) * 4);
|
2019-09-05 17:23:23 +00:00
|
|
|
|
|
|
|
baseRepetition = (baseRepetition + 1) % 3;
|
|
|
|
i += repetitionCount;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-07-28 19:13:20 +00:00
|
|
|
run_test(dContext, "drawIndexPattern", reporter, sdc, gold,
|
2019-09-05 17:23:23 +00:00
|
|
|
[&](DrawMeshHelper* helper) {
|
|
|
|
helper->fIndexBuffer = helper->getIndexBuffer();
|
|
|
|
VALIDATE(helper->fIndexBuffer);
|
|
|
|
helper->fVertBuffer = helper->makeVertexBuffer(vertexData);
|
|
|
|
VALIDATE(helper->fVertBuffer);
|
|
|
|
},
|
|
|
|
[&](DrawMeshHelper* helper) {
|
2020-03-12 18:13:29 +00:00
|
|
|
// Draw boxes one line at a time to exercise base vertex. drawIndexPattern does
|
2019-09-05 17:23:23 +00:00
|
|
|
// not support a base index.
|
|
|
|
for (int y = 0; y < kBoxCountY; ++y) {
|
2020-03-12 18:13:29 +00:00
|
|
|
auto pass = helper->bindPipeline(GrPrimitiveType::kTriangles, false, true);
|
2020-07-20 15:37:38 +00:00
|
|
|
pass->bindBuffers(helper->fIndexBuffer, nullptr, helper->fVertBuffer);
|
2020-03-12 18:13:29 +00:00
|
|
|
pass->drawIndexPattern(6, kBoxCountX, kIndexPatternRepeatCount, 4,
|
|
|
|
y * kBoxCountX * 4);
|
|
|
|
|
2019-09-05 17:23:23 +00:00
|
|
|
}
|
|
|
|
});
|
2017-05-31 18:51:23 +00:00
|
|
|
|
|
|
|
for (bool indexed : {false, true}) {
|
2020-07-20 19:00:36 +00:00
|
|
|
if (!dContext->priv().caps()->drawInstancedSupport()) {
|
2017-05-31 18:51:23 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-07-20 19:00:36 +00:00
|
|
|
run_test(dContext, indexed ? "drawIndexedInstanced" : "drawInstanced",
|
2021-07-28 19:13:20 +00:00
|
|
|
reporter, sdc, gold,
|
2019-09-05 17:23:23 +00:00
|
|
|
[&](DrawMeshHelper* helper) {
|
|
|
|
helper->fIndexBuffer = indexed ? helper->getIndexBuffer() : nullptr;
|
2020-03-12 18:13:29 +00:00
|
|
|
SkTArray<uint16_t> baseIndexData;
|
|
|
|
baseIndexData.push_back(kBoxCountX/2 * 6); // for testing base index.
|
2020-04-07 18:31:59 +00:00
|
|
|
for (int i = 0; i < 6; ++i) {
|
|
|
|
baseIndexData.push_back(kIndexPattern[i]);
|
2020-03-12 18:13:29 +00:00
|
|
|
}
|
|
|
|
helper->fIndexBuffer2 = helper->makeIndexBuffer(baseIndexData.begin(),
|
|
|
|
baseIndexData.count());
|
2019-09-05 17:23:23 +00:00
|
|
|
helper->fInstBuffer = helper->makeVertexBuffer(boxes);
|
|
|
|
VALIDATE(helper->fInstBuffer);
|
|
|
|
helper->fVertBuffer =
|
|
|
|
helper->makeVertexBuffer(std::vector<float>{0,0, 0,1, 1,0, 1,1});
|
|
|
|
VALIDATE(helper->fVertBuffer);
|
|
|
|
helper->fVertBuffer2 = helper->makeVertexBuffer( // for testing base vertex.
|
|
|
|
std::vector<float>{-1,-1, -1,-1, 0,0, 0,1, 1,0, 1,1});
|
|
|
|
VALIDATE(helper->fVertBuffer2);
|
|
|
|
},
|
|
|
|
[&](DrawMeshHelper* helper) {
|
|
|
|
// Draw boxes one line at a time to exercise base instance, base vertex, and
|
2020-03-12 18:13:29 +00:00
|
|
|
// null vertex buffer.
|
2019-09-05 17:23:23 +00:00
|
|
|
for (int y = 0; y < kBoxCountY; ++y) {
|
2020-07-20 15:37:38 +00:00
|
|
|
sk_sp<const GrBuffer> vertexBuffer;
|
2020-03-12 18:13:29 +00:00
|
|
|
int baseVertex = 0;
|
2019-09-05 17:23:23 +00:00
|
|
|
switch (y % 3) {
|
|
|
|
case 0:
|
2022-06-07 21:03:38 +00:00
|
|
|
if (dContext->priv().caps()->shaderCaps()->fVertexIDSupport) {
|
2019-09-05 17:23:23 +00:00
|
|
|
break;
|
|
|
|
}
|
2020-06-11 21:55:07 +00:00
|
|
|
[[fallthrough]];
|
2019-09-05 17:23:23 +00:00
|
|
|
case 1:
|
2020-07-20 15:37:38 +00:00
|
|
|
vertexBuffer = helper->fVertBuffer;
|
2019-09-05 17:23:23 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
2020-07-20 15:37:38 +00:00
|
|
|
vertexBuffer = helper->fVertBuffer2;
|
2020-03-12 18:13:29 +00:00
|
|
|
baseVertex = 2;
|
2019-09-05 17:23:23 +00:00
|
|
|
break;
|
|
|
|
}
|
2020-03-12 18:13:29 +00:00
|
|
|
|
|
|
|
GrPrimitiveType primitiveType = indexed ? GrPrimitiveType::kTriangles
|
|
|
|
: GrPrimitiveType::kTriangleStrip;
|
|
|
|
auto pass = helper->bindPipeline(primitiveType, true,
|
|
|
|
SkToBool(vertexBuffer));
|
|
|
|
if (indexed) {
|
2020-07-20 15:37:38 +00:00
|
|
|
sk_sp<const GrBuffer> indexBuffer = (y % 2) ?
|
|
|
|
helper->fIndexBuffer2 : helper->fIndexBuffer;
|
2020-03-12 18:13:29 +00:00
|
|
|
VALIDATE(indexBuffer);
|
|
|
|
int baseIndex = (y % 2);
|
2020-07-20 15:37:38 +00:00
|
|
|
pass->bindBuffers(std::move(indexBuffer), helper->fInstBuffer,
|
|
|
|
std::move(vertexBuffer));
|
2020-04-07 18:31:59 +00:00
|
|
|
pass->drawIndexedInstanced(6, baseIndex, kBoxCountX, y * kBoxCountX,
|
2020-03-12 18:13:29 +00:00
|
|
|
baseVertex);
|
|
|
|
} else {
|
2020-07-20 15:37:38 +00:00
|
|
|
pass->bindBuffers(nullptr, helper->fInstBuffer,
|
|
|
|
std::move(vertexBuffer));
|
2020-03-12 18:13:29 +00:00
|
|
|
pass->drawInstanced(kBoxCountX, y * kBoxCountY, 4, baseVertex);
|
|
|
|
}
|
2019-09-05 17:23:23 +00:00
|
|
|
}
|
|
|
|
});
|
2017-05-31 18:51:23 +00:00
|
|
|
}
|
2020-04-07 18:31:59 +00:00
|
|
|
|
|
|
|
for (bool indexed : {false, true}) {
|
2020-07-20 19:00:36 +00:00
|
|
|
if (!dContext->priv().caps()->drawInstancedSupport()) {
|
2020-04-07 18:31:59 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-07-20 19:00:36 +00:00
|
|
|
run_test(dContext, (indexed) ? "drawIndexedIndirect" : "drawIndirect",
|
2021-07-28 19:13:20 +00:00
|
|
|
reporter, sdc, gold,
|
2020-04-07 18:31:59 +00:00
|
|
|
[&](DrawMeshHelper* helper) {
|
|
|
|
SkTArray<uint16_t> baseIndexData;
|
|
|
|
baseIndexData.push_back(kBoxCountX/2 * 6); // for testing base index.
|
|
|
|
for (int j = 0; j < kBoxCountY; ++j) {
|
|
|
|
for (int i = 0; i < 6; ++i) {
|
|
|
|
baseIndexData.push_back(kIndexPattern[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
helper->fIndexBuffer2 = helper->makeIndexBuffer(baseIndexData.begin(),
|
|
|
|
baseIndexData.count());
|
|
|
|
VALIDATE(helper->fIndexBuffer2);
|
|
|
|
helper->fInstBuffer = helper->makeVertexBuffer(boxes);
|
|
|
|
VALIDATE(helper->fInstBuffer);
|
|
|
|
helper->fVertBuffer = helper->makeVertexBuffer(std::vector<float>{
|
|
|
|
-1,-1, 0,0, 0,1, 1,0, 1,1, -1,-1, 0,0, 1,0, 0,1, 1,1});
|
|
|
|
VALIDATE(helper->fVertBuffer);
|
|
|
|
|
2021-02-08 03:56:36 +00:00
|
|
|
GrDrawIndirectWriter indirectWriter;
|
|
|
|
GrDrawIndexedIndirectWriter indexedIndirectWriter;
|
2020-04-07 18:31:59 +00:00
|
|
|
if (indexed) {
|
|
|
|
// Make helper->fDrawIndirectBufferOffset nonzero.
|
2021-06-16 19:28:00 +00:00
|
|
|
sk_sp<const GrBuffer> ignoredBuff;
|
|
|
|
size_t ignoredOffset;
|
2020-05-13 17:02:10 +00:00
|
|
|
// Make a superfluous call to makeDrawIndirectSpace in order to test
|
|
|
|
// "offsetInBytes!=0" for the actual call to makeDrawIndexedIndirectSpace.
|
2021-06-16 19:28:00 +00:00
|
|
|
helper->target()->makeDrawIndirectSpace(29, &ignoredBuff, &ignoredOffset);
|
2021-02-08 03:56:36 +00:00
|
|
|
indexedIndirectWriter = helper->target()->makeDrawIndexedIndirectSpace(
|
2020-04-07 18:31:59 +00:00
|
|
|
kBoxCountY, &helper->fDrawIndirectBuffer,
|
|
|
|
&helper->fDrawIndirectBufferOffset);
|
|
|
|
} else {
|
|
|
|
// Make helper->fDrawIndirectBufferOffset nonzero.
|
2021-06-16 19:28:00 +00:00
|
|
|
sk_sp<const GrBuffer> ignoredBuff;
|
|
|
|
size_t ignoredOffset;
|
2020-05-13 17:02:10 +00:00
|
|
|
// Make a superfluous call to makeDrawIndexedIndirectSpace in order to test
|
|
|
|
// "offsetInBytes!=0" for the actual call to makeDrawIndirectSpace.
|
2021-06-16 19:28:00 +00:00
|
|
|
helper->target()->makeDrawIndexedIndirectSpace(7, &ignoredBuff,
|
|
|
|
&ignoredOffset);
|
2021-02-08 03:56:36 +00:00
|
|
|
indirectWriter = helper->target()->makeDrawIndirectSpace(
|
2020-04-07 18:31:59 +00:00
|
|
|
kBoxCountY, &helper->fDrawIndirectBuffer,
|
|
|
|
&helper->fDrawIndirectBufferOffset);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw boxes one line at a time to exercise multiple draws.
|
|
|
|
for (int y = 0; y < kBoxCountY; ++y) {
|
|
|
|
int baseVertex = (y % 2) ? 1 : 6;
|
|
|
|
if (indexed) {
|
|
|
|
int baseIndex = 1 + y * 6;
|
2021-02-08 03:56:36 +00:00
|
|
|
indexedIndirectWriter.writeIndexed(6, baseIndex, kBoxCountX,
|
2021-02-10 19:55:48 +00:00
|
|
|
y * kBoxCountX, baseVertex);
|
2020-04-07 18:31:59 +00:00
|
|
|
} else {
|
2021-02-10 19:55:48 +00:00
|
|
|
indirectWriter.write(kBoxCountX, y * kBoxCountX, 4, baseVertex);
|
2020-04-07 18:31:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[&](DrawMeshHelper* helper) {
|
|
|
|
GrOpsRenderPass* pass;
|
|
|
|
if (indexed) {
|
|
|
|
pass = helper->bindPipeline(GrPrimitiveType::kTriangles, true, true);
|
2020-07-20 15:37:38 +00:00
|
|
|
pass->bindBuffers(helper->fIndexBuffer2, helper->fInstBuffer,
|
|
|
|
helper->fVertBuffer);
|
2020-04-07 18:31:59 +00:00
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
|
|
int start = kBoxCountY * i / 3;
|
|
|
|
int end = kBoxCountY * (i + 1) / 3;
|
|
|
|
size_t offset = helper->fDrawIndirectBufferOffset + start *
|
|
|
|
sizeof(GrDrawIndexedIndirectCommand);
|
|
|
|
pass->drawIndexedIndirect(helper->fDrawIndirectBuffer.get(), offset,
|
|
|
|
end - start);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pass = helper->bindPipeline(GrPrimitiveType::kTriangleStrip, true, true);
|
2020-07-20 15:37:38 +00:00
|
|
|
pass->bindBuffers(nullptr, helper->fInstBuffer, helper->fVertBuffer);
|
2020-04-07 18:31:59 +00:00
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
|
|
int start = kBoxCountY * i / 2;
|
|
|
|
int end = kBoxCountY * (i + 1) / 2;
|
|
|
|
size_t offset = helper->fDrawIndirectBufferOffset + start *
|
|
|
|
sizeof(GrDrawIndirectCommand);
|
|
|
|
pass->drawIndirect(helper->fDrawIndirectBuffer.get(), offset,
|
|
|
|
end - start);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2017-05-26 21:17:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2021-08-11 13:59:56 +00:00
|
|
|
namespace {
|
|
|
|
class MeshTestOp : public GrDrawOp {
|
2017-05-26 21:17:19 +00:00
|
|
|
public:
|
|
|
|
DEFINE_OP_CLASS_ID
|
|
|
|
|
2020-10-07 20:46:15 +00:00
|
|
|
static GrOp::Owner Make(GrRecordingContext* rContext,
|
|
|
|
std::function<void(DrawMeshHelper*)> prepareFn,
|
|
|
|
std::function<void(DrawMeshHelper*)> executeFn) {
|
2021-08-11 13:59:56 +00:00
|
|
|
return GrOp::Make<MeshTestOp>(rContext, prepareFn, executeFn);
|
2018-06-07 15:05:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2021-08-11 13:59:56 +00:00
|
|
|
friend class GrOp; // for ctor
|
|
|
|
|
|
|
|
MeshTestOp(std::function<void(DrawMeshHelper*)> prepareFn,
|
|
|
|
std::function<void(DrawMeshHelper*)> executeFn)
|
|
|
|
: INHERITED(ClassID()), fPrepareFn(prepareFn), fExecuteFn(executeFn) {
|
|
|
|
this->setBounds(
|
|
|
|
SkRect::MakeIWH(kImageWidth, kImageHeight), HasAABloat::kNo, IsHairline::kNo);
|
2017-05-26 21:17:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const char* name() const override { return "GrMeshTestOp"; }
|
|
|
|
FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
|
2021-04-22 18:57:28 +00:00
|
|
|
GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override {
|
2019-01-15 18:53:00 +00:00
|
|
|
return GrProcessorSet::EmptySetAnalysis();
|
2017-06-16 14:04:34 +00:00
|
|
|
}
|
2020-03-18 17:23:45 +00:00
|
|
|
|
|
|
|
void onPrePrepare(GrRecordingContext*,
|
2020-11-19 18:41:26 +00:00
|
|
|
const GrSurfaceProxyView& writeView,
|
2020-03-18 17:23:45 +00:00
|
|
|
GrAppliedClip*,
|
2021-06-02 15:58:05 +00:00
|
|
|
const GrDstProxyView&,
|
2020-11-20 15:22:43 +00:00
|
|
|
GrXferBarrierFlags renderPassXferBarriers,
|
|
|
|
GrLoadOp colorLoadOp) override {}
|
2019-09-05 17:23:23 +00:00
|
|
|
void onPrepare(GrOpFlushState* state) override {
|
2020-08-03 17:21:46 +00:00
|
|
|
fHelper = std::make_unique<DrawMeshHelper>(state);
|
2019-09-05 17:23:23 +00:00
|
|
|
fPrepareFn(fHelper.get());
|
|
|
|
}
|
2018-11-14 18:56:37 +00:00
|
|
|
void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override {
|
2019-09-05 17:23:23 +00:00
|
|
|
fExecuteFn(fHelper.get());
|
2017-05-26 21:17:19 +00:00
|
|
|
}
|
|
|
|
|
2019-09-05 17:23:23 +00:00
|
|
|
std::unique_ptr<DrawMeshHelper> fHelper;
|
|
|
|
std::function<void(DrawMeshHelper*)> fPrepareFn;
|
|
|
|
std::function<void(DrawMeshHelper*)> fExecuteFn;
|
2017-05-26 21:17:19 +00:00
|
|
|
|
2020-09-03 02:42:33 +00:00
|
|
|
using INHERITED = GrDrawOp;
|
2017-05-26 21:17:19 +00:00
|
|
|
};
|
|
|
|
|
2021-08-11 13:59:56 +00:00
|
|
|
class MeshTestProcessor : public GrGeometryProcessor {
|
2017-05-26 21:17:19 +00:00
|
|
|
public:
|
2019-11-20 21:08:10 +00:00
|
|
|
static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool instanced, bool hasVertexBuffer) {
|
2020-12-14 21:59:09 +00:00
|
|
|
return arena->make([&](void* ptr) {
|
2021-08-11 13:59:56 +00:00
|
|
|
return new (ptr) MeshTestProcessor(instanced, hasVertexBuffer);
|
2020-12-14 21:59:09 +00:00
|
|
|
});
|
2019-11-20 21:08:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const char* name() const override { return "GrMeshTestProcessor"; }
|
|
|
|
|
2021-12-20 17:37:56 +00:00
|
|
|
void addToKey(const GrShaderCaps&, skgpu::KeyBuilder* b) const final {
|
2019-11-20 21:08:10 +00:00
|
|
|
b->add32(fInstanceLocation.isInitialized());
|
|
|
|
b->add32(fVertexPosition.isInitialized());
|
|
|
|
}
|
|
|
|
|
2021-08-09 19:56:24 +00:00
|
|
|
std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
|
2019-11-20 21:08:10 +00:00
|
|
|
|
|
|
|
private:
|
2021-08-11 13:59:56 +00:00
|
|
|
class Impl;
|
2019-11-20 21:08:10 +00:00
|
|
|
|
2021-08-11 13:59:56 +00:00
|
|
|
const Attribute& inColor() const {
|
|
|
|
return fVertexColor.isInitialized() ? fVertexColor : fInstanceColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
MeshTestProcessor(bool instanced, bool hasVertexBuffer)
|
2018-06-19 18:33:47 +00:00
|
|
|
: INHERITED(kGrMeshTestProcessor_ClassID) {
|
2017-05-31 18:51:23 +00:00
|
|
|
if (instanced) {
|
2022-01-21 19:59:22 +00:00
|
|
|
fInstanceLocation = {"location", kFloat2_GrVertexAttribType, SkSLType::kHalf2};
|
|
|
|
fInstanceColor = {"color", kUByte4_norm_GrVertexAttribType, SkSLType::kHalf4};
|
2021-12-16 21:14:56 +00:00
|
|
|
this->setInstanceAttributesWithImplicitOffsets(&fInstanceLocation, 2);
|
2017-05-31 18:51:23 +00:00
|
|
|
if (hasVertexBuffer) {
|
2022-01-21 19:59:22 +00:00
|
|
|
fVertexPosition = {"vertex", kFloat2_GrVertexAttribType, SkSLType::kHalf2};
|
2021-12-16 21:14:56 +00:00
|
|
|
this->setVertexAttributesWithImplicitOffsets(&fVertexPosition, 1);
|
2017-05-31 18:51:23 +00:00
|
|
|
}
|
|
|
|
} else {
|
2022-01-21 19:59:22 +00:00
|
|
|
fVertexPosition = {"vertex", kFloat2_GrVertexAttribType, SkSLType::kHalf2};
|
|
|
|
fVertexColor = {"color", kUByte4_norm_GrVertexAttribType, SkSLType::kHalf4};
|
2021-12-16 21:14:56 +00:00
|
|
|
this->setVertexAttributesWithImplicitOffsets(&fVertexPosition, 2);
|
2017-05-31 18:51:23 +00:00
|
|
|
}
|
2017-05-26 21:17:19 +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
|
|
|
Attribute fVertexPosition;
|
|
|
|
Attribute fVertexColor;
|
2018-06-19 18:33:47 +00:00
|
|
|
|
|
|
|
Attribute fInstanceLocation;
|
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
|
|
|
Attribute fInstanceColor;
|
2017-05-26 21:17:19 +00:00
|
|
|
|
2020-09-03 02:42:33 +00:00
|
|
|
using INHERITED = GrGeometryProcessor;
|
2017-05-26 21:17:19 +00:00
|
|
|
};
|
2021-08-11 13:59:56 +00:00
|
|
|
} // anonymous namespace
|
2017-05-26 21:17:19 +00:00
|
|
|
|
2021-08-11 13:59:56 +00:00
|
|
|
std::unique_ptr<GrGeometryProcessor::ProgramImpl> MeshTestProcessor::makeProgramImpl(
|
|
|
|
const GrShaderCaps&) const {
|
|
|
|
class Impl : public ProgramImpl {
|
|
|
|
public:
|
|
|
|
void setData(const GrGLSLProgramDataManager&,
|
|
|
|
const GrShaderCaps&,
|
|
|
|
const GrGeometryProcessor&) final {}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
|
|
|
|
const MeshTestProcessor& mp = args.fGeomProc.cast<MeshTestProcessor>();
|
|
|
|
GrGLSLVertexBuilder* v = args.fVertBuilder;
|
|
|
|
GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
|
|
|
|
|
|
|
|
GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
|
|
|
|
varyingHandler->emitAttributes(mp);
|
|
|
|
f->codeAppendf("half4 %s;", args.fOutputColor);
|
2021-08-11 17:01:06 +00:00
|
|
|
varyingHandler->addPassThroughAttribute(mp.inColor().asShaderVar(), args.fOutputColor);
|
2021-08-11 13:59:56 +00:00
|
|
|
|
|
|
|
if (!mp.fInstanceLocation.isInitialized()) {
|
|
|
|
v->codeAppendf("float2 vertex = %s;", mp.fVertexPosition.name());
|
2017-05-31 18:51:23 +00:00
|
|
|
} else {
|
2021-08-11 13:59:56 +00:00
|
|
|
if (mp.fVertexPosition.isInitialized()) {
|
|
|
|
v->codeAppendf("float2 offset = %s;", mp.fVertexPosition.name());
|
|
|
|
} else {
|
|
|
|
v->codeAppend("float2 offset = float2(sk_VertexID / 2, sk_VertexID % 2);");
|
|
|
|
}
|
|
|
|
v->codeAppendf("float2 vertex = %s + offset * %i;", mp.fInstanceLocation.name(),
|
|
|
|
kBoxSize);
|
2017-05-31 18:51:23 +00:00
|
|
|
}
|
2022-01-21 19:59:22 +00:00
|
|
|
gpArgs->fPositionVar.set(SkSLType::kFloat2, "vertex");
|
2017-05-26 21:17:19 +00:00
|
|
|
|
2021-08-11 13:59:56 +00:00
|
|
|
f->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
|
|
|
|
}
|
|
|
|
};
|
2017-05-26 21:17:19 +00:00
|
|
|
|
2021-08-11 13:59:56 +00:00
|
|
|
return std::make_unique<Impl>();
|
2017-05-26 21:17:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2020-03-12 18:13:29 +00:00
|
|
|
sk_sp<const GrBuffer> DrawMeshHelper::makeIndexBuffer(const uint16_t indices[], int count) {
|
2022-05-26 23:33:20 +00:00
|
|
|
return fState->resourceProvider()->createBuffer(indices,
|
|
|
|
count*sizeof(uint16_t),
|
|
|
|
GrGpuBufferType::kIndex,
|
|
|
|
kDynamic_GrAccessPattern);
|
2020-03-12 18:13:29 +00:00
|
|
|
}
|
|
|
|
|
2017-05-26 21:17:19 +00:00
|
|
|
template<typename T>
|
|
|
|
sk_sp<const GrBuffer> DrawMeshHelper::makeVertexBuffer(const T* data, int count) {
|
2022-05-26 23:33:20 +00:00
|
|
|
return fState->resourceProvider()->createBuffer(data,
|
|
|
|
count*sizeof(T),
|
|
|
|
GrGpuBufferType::kVertex,
|
|
|
|
kDynamic_GrAccessPattern);
|
2017-05-26 21:17:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sk_sp<const GrBuffer> DrawMeshHelper::getIndexBuffer() {
|
2021-12-21 23:01:47 +00:00
|
|
|
SKGPU_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
|
2017-10-16 17:01:07 +00:00
|
|
|
return fState->resourceProvider()->findOrCreatePatternedIndexBuffer(
|
|
|
|
kIndexPattern, 6, kIndexPatternRepeatCount, 4, gIndexBufferKey);
|
2017-05-26 21:17:19 +00:00
|
|
|
}
|
|
|
|
|
2020-03-12 18:13:29 +00:00
|
|
|
GrOpsRenderPass* DrawMeshHelper::bindPipeline(GrPrimitiveType primitiveType, bool isInstanced,
|
|
|
|
bool hasVertexBuffer) {
|
2019-12-05 21:40:31 +00:00
|
|
|
GrProcessorSet processorSet(SkBlendMode::kSrc);
|
|
|
|
|
|
|
|
// TODO: add a GrProcessorSet testing helper to make this easier
|
|
|
|
SkPMColor4f overrideColor;
|
|
|
|
processorSet.finalize(GrProcessorAnalysisColor(),
|
|
|
|
GrProcessorAnalysisCoverage::kNone,
|
|
|
|
fState->appliedClip(),
|
|
|
|
nullptr,
|
|
|
|
fState->caps(),
|
|
|
|
GrClampType::kAuto,
|
|
|
|
&overrideColor);
|
|
|
|
|
|
|
|
auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(fState,
|
|
|
|
std::move(processorSet),
|
|
|
|
GrPipeline::InputFlags::kNone);
|
2019-11-20 21:08:10 +00:00
|
|
|
|
2021-08-11 13:59:56 +00:00
|
|
|
GrGeometryProcessor* mtp = MeshTestProcessor::Make(fState->allocator(), isInstanced,
|
|
|
|
hasVertexBuffer);
|
2019-10-08 16:32:56 +00:00
|
|
|
|
2021-08-26 16:05:11 +00:00
|
|
|
GrProgramInfo programInfo(fState->caps(), fState->writeView(), fState->usesMSAASurface(),
|
2022-05-04 14:31:37 +00:00
|
|
|
pipeline, &GrUserStencilSettings::kUnused, mtp, primitiveType,
|
2021-08-26 16:05:11 +00:00
|
|
|
fState->renderPassBarriers(), fState->colorLoadOp());
|
2019-10-08 16:32:56 +00:00
|
|
|
|
2020-02-19 23:42:06 +00:00
|
|
|
fState->opsRenderPass()->bindPipeline(programInfo, SkRect::MakeIWH(kImageWidth, kImageHeight));
|
2020-03-12 18:13:29 +00:00
|
|
|
return fState->opsRenderPass();
|
2017-05-26 21:17:19 +00:00
|
|
|
}
|
|
|
|
|
2021-07-28 19:13:20 +00:00
|
|
|
static void run_test(GrDirectContext* dContext,
|
|
|
|
const char* testName,
|
2020-07-20 19:00:36 +00:00
|
|
|
skiatest::Reporter* reporter,
|
2021-07-28 19:13:20 +00:00
|
|
|
const std::unique_ptr<skgpu::v1::SurfaceDrawContext>& sdc,
|
|
|
|
const SkBitmap& gold,
|
2019-09-05 17:23:23 +00:00
|
|
|
std::function<void(DrawMeshHelper*)> prepareFn,
|
|
|
|
std::function<void(DrawMeshHelper*)> executeFn) {
|
2020-12-24 01:36:44 +00:00
|
|
|
const int w = gold.width(), h = gold.height();
|
2017-05-26 21:17:19 +00:00
|
|
|
const uint32_t* goldPx = reinterpret_cast<const uint32_t*>(gold.getPixels());
|
2021-07-28 19:13:20 +00:00
|
|
|
if (h != sdc->height() || w != sdc->width()) {
|
2017-05-26 21:17:19 +00:00
|
|
|
ERRORF(reporter, "[%s] expectation and rtc not compatible (?).", testName);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (sizeof(uint32_t) * kImageWidth != gold.rowBytes()) {
|
2020-05-12 14:41:04 +00:00
|
|
|
ERRORF(reporter, "[%s] unexpected row bytes in gold image", testName);
|
2017-05-26 21:17:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-26 18:29:30 +00:00
|
|
|
GrPixmap resultPM = GrPixmap::Allocate(gold.info());
|
2021-07-28 19:13:20 +00:00
|
|
|
sdc->clear(SkPMColor4f::FromBytes_RGBA(0xbaaaaaad));
|
2021-08-11 13:59:56 +00:00
|
|
|
sdc->addDrawOp(MeshTestOp::Make(dContext, prepareFn, executeFn));
|
2020-04-03 17:01:30 +00:00
|
|
|
|
2021-07-28 19:13:20 +00:00
|
|
|
sdc->readPixels(dContext, resultPM, {0, 0});
|
2020-04-03 17:01:30 +00:00
|
|
|
|
|
|
|
#ifdef WRITE_PNG_CONTEXT_TYPE
|
|
|
|
#define STRINGIFY(X) #X
|
|
|
|
#define TOSTRING(X) STRINGIFY(X)
|
|
|
|
SkString filename;
|
|
|
|
filename.printf("GrMeshTest_%s_%s.png", TOSTRING(WRITE_PNG_CONTEXT_TYPE), testName);
|
|
|
|
SkDebugf("writing %s...\n", filename.c_str());
|
2020-12-24 01:36:44 +00:00
|
|
|
ToolUtils::EncodeImageToFile(filename.c_str(), resultPM, SkEncodedImageFormat::kPNG, 100);
|
2020-04-03 17:01:30 +00:00
|
|
|
#endif
|
|
|
|
|
2017-05-26 21:17:19 +00:00
|
|
|
for (int y = 0; y < h; ++y) {
|
|
|
|
for (int x = 0; x < w; ++x) {
|
|
|
|
uint32_t expected = goldPx[y * kImageWidth + x];
|
2020-12-24 01:36:44 +00:00
|
|
|
uint32_t actual = static_cast<uint32_t*>(resultPM.addr())[y * kImageWidth + x];
|
2017-05-26 21:17:19 +00:00
|
|
|
if (expected != actual) {
|
|
|
|
ERRORF(reporter, "[%s] pixel (%i,%i): got 0x%x expected 0x%x",
|
|
|
|
testName, x, y, actual, expected);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|