bdc0bad2e2
Renderer::StencilAndFill() chooses between two instances based on fill type (more to come when we add stencil pass). The inverse fill uses different stencil settings and different geometry (hence why it must be a distinct renderer, since stencil is part of the pipeline). Also updates the command buffer asserts and types to support float3 attributes and has the fill bounds render step pre-transform vertices. This matches the intended plan of device-space control points to avoid matrix transform uniforms when no other coords are needed. Makes DepthStencilSettings constexpr so they can be declared constants. Cq-Include-Trybots: luci.skia.skia.primary:Test-Mac11-Clang-MacMini9.1-GPU-AppleM1-arm64-Release-All-Graphite,Test-Mac11-Clang-MacMini9.1-GPU-AppleM1-arm64-Debug-All-ASAN_Graphite,Build-Mac-Clang-arm64-Release-iOS_Graphite,Build-Mac-Clang-arm64-Release-Graphite,Build-Mac-Clang-arm64-Debug-iOS_Graphite,Build-Mac-Clang-arm64-Debug-Graphite_NoGpu,Build-Mac-Clang-arm64-Debug-Graphite,Build-Mac-Clang-arm64-Debug-ASAN_Graphite Bug: skia:12703 Change-Id: I5be4151f533e4cc5c560baf96c59193162b48dab Reviewed-on: https://skia-review.googlesource.com/c/skia/+/484559 Reviewed-by: Greg Daniel <egdaniel@google.com> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
368 lines
14 KiB
C++
368 lines
14 KiB
C++
/*
|
|
* Copyright 2021 Google LLC
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "tests/Test.h"
|
|
|
|
#include "experimental/graphite/include/Context.h"
|
|
#include "experimental/graphite/src/ContextPriv.h"
|
|
|
|
#include "experimental/graphite/include/mtl/MtlTypes.h"
|
|
#include "experimental/graphite/src/Buffer.h"
|
|
#include "experimental/graphite/src/Caps.h"
|
|
#include "experimental/graphite/src/CommandBuffer.h"
|
|
#include "experimental/graphite/src/ContextUtils.h"
|
|
#include "experimental/graphite/src/DrawBufferManager.h"
|
|
#include "experimental/graphite/src/DrawWriter.h"
|
|
#include "experimental/graphite/src/Gpu.h"
|
|
#include "experimental/graphite/src/GraphicsPipeline.h"
|
|
#include "experimental/graphite/src/Renderer.h"
|
|
#include "experimental/graphite/src/ResourceProvider.h"
|
|
#include "experimental/graphite/src/Texture.h"
|
|
#include "experimental/graphite/src/TextureProxy.h"
|
|
#include "experimental/graphite/src/UniformManager.h"
|
|
#include "experimental/graphite/src/geom/Shape.h"
|
|
#include "experimental/graphite/src/geom/Transform_graphite.h"
|
|
|
|
#if GRAPHITE_TEST_UTILS
|
|
// set to 1 if you want to do GPU capture of the commandBuffer
|
|
#define CAPTURE_COMMANDBUFFER 0
|
|
#endif
|
|
|
|
using namespace skgpu;
|
|
|
|
namespace {
|
|
|
|
const DepthStencilSettings kTestDepthStencilSettings = {
|
|
// stencil
|
|
{},
|
|
{},
|
|
0,
|
|
true,
|
|
// depth
|
|
CompareOp::kAlways,
|
|
true,
|
|
false,
|
|
};
|
|
|
|
class UniformRectDraw final : public RenderStep {
|
|
public:
|
|
~UniformRectDraw() override {}
|
|
|
|
static const RenderStep* Singleton() {
|
|
static const UniformRectDraw kSingleton;
|
|
return &kSingleton;
|
|
}
|
|
|
|
const char* name() const override { return "uniform-rect"; }
|
|
|
|
const char* vertexSkSL() const override {
|
|
return "float2 tmpPosition = float2(float(sk_VertexID >> 1), float(sk_VertexID & 1));\n"
|
|
"float4 devPosition = float4(tmpPosition * scale + translate, 0.0, 1.0);\n";
|
|
}
|
|
|
|
void writeVertices(DrawWriter* writer,
|
|
const SkIRect&,
|
|
const Transform&,
|
|
const Shape&) const override {
|
|
// The shape is upload via uniforms, so this just needs to record 4 data-less vertices
|
|
writer->draw({}, {}, 4);
|
|
}
|
|
|
|
sk_sp<UniformData> writeUniforms(Layout layout,
|
|
const SkIRect&,
|
|
const Transform&,
|
|
const Shape& shape) const override {
|
|
SkASSERT(shape.isRect());
|
|
// TODO: A << API for uniforms would be nice, particularly if it could take pre-computed
|
|
// offsets for each uniform.
|
|
auto uniforms = UniformData::Make(this->numUniforms(), this->uniforms().data(),
|
|
sizeof(float) * 4);
|
|
float2 scale = shape.rect().size();
|
|
float2 translate = shape.rect().topLeft();
|
|
memcpy(uniforms->data(), &scale, sizeof(float2));
|
|
memcpy(uniforms->data() + sizeof(float2), &translate, sizeof(float2));
|
|
return uniforms;
|
|
}
|
|
|
|
private:
|
|
UniformRectDraw() : RenderStep(Flags::kPerformsShading,
|
|
/*uniforms=*/{{"scale", SLType::kFloat2},
|
|
{"translate", SLType::kFloat2}},
|
|
PrimitiveType::kTriangleStrip,
|
|
kTestDepthStencilSettings,
|
|
/*vertexAttrs=*/{},
|
|
/*instanceAttrs=*/{}) {}
|
|
};
|
|
|
|
class TriangleRectDraw final : public RenderStep {
|
|
public:
|
|
~TriangleRectDraw() override {}
|
|
|
|
static const RenderStep* Singleton() {
|
|
static const TriangleRectDraw kSingleton;
|
|
return &kSingleton;
|
|
}
|
|
|
|
const char* name() const override { return "triangle-rect"; }
|
|
|
|
const char* vertexSkSL() const override {
|
|
return "float4 devPosition = float4(position * scale + translate, 0.0, 1.0);\n";
|
|
}
|
|
|
|
void writeVertices(DrawWriter* writer,
|
|
const SkIRect&,
|
|
const Transform&,
|
|
const Shape& shape) const override {
|
|
DrawBufferManager* bufferMgr = writer->bufferManager();
|
|
auto [vertexWriter, vertices] = bufferMgr->getVertexWriter(4 * this->vertexStride());
|
|
vertexWriter << 0.5f * (shape.rect().left() + 1.f) << 0.5f * (shape.rect().top() + 1.f)
|
|
<< 0.5f * (shape.rect().left() + 1.f) << 0.5f * (shape.rect().bot() + 1.f)
|
|
<< 0.5f * (shape.rect().right() + 1.f) << 0.5f * (shape.rect().top() + 1.f)
|
|
<< 0.5f * (shape.rect().right() + 1.f) << 0.5f * (shape.rect().bot() + 1.f);
|
|
|
|
// TODO: Would be nice to re-use this
|
|
auto [indexWriter, indices] = bufferMgr->getIndexWriter(6 * sizeof(uint16_t));
|
|
indexWriter << 0 << 1 << 2
|
|
<< 2 << 1 << 3;
|
|
|
|
writer->draw(vertices, indices, 6);
|
|
}
|
|
|
|
sk_sp<UniformData> writeUniforms(Layout layout,
|
|
const SkIRect&,
|
|
const Transform&,
|
|
const Shape&) const override {
|
|
auto uniforms = UniformData::Make(this->numUniforms(), this->uniforms().data(),
|
|
sizeof(float) * 4);
|
|
float data[4] = {2.f, 2.f, -1.f, -1.f};
|
|
memcpy(uniforms->data(), data, 4 * sizeof(float));
|
|
return uniforms;
|
|
}
|
|
|
|
private:
|
|
TriangleRectDraw()
|
|
: RenderStep(Flags::kPerformsShading,
|
|
/*uniforms=*/{{"scale", SLType::kFloat2},
|
|
{"translate", SLType::kFloat2}},
|
|
PrimitiveType::kTriangles,
|
|
kTestDepthStencilSettings,
|
|
/*vertexAttrs=*/{{"position", VertexAttribType::kFloat2, SLType::kFloat2}},
|
|
/*instanceAttrs=*/{}) {}
|
|
};
|
|
|
|
class InstanceRectDraw final : public RenderStep {
|
|
public:
|
|
~InstanceRectDraw() override {}
|
|
|
|
static const RenderStep* Singleton() {
|
|
static const InstanceRectDraw kSingleton;
|
|
return &kSingleton;
|
|
}
|
|
|
|
const char* name() const override { return "instance-rect"; }
|
|
|
|
const char* vertexSkSL() const override {
|
|
return "float2 tmpPosition = float2(float(sk_VertexID >> 1), float(sk_VertexID & 1));\n"
|
|
"float4 devPosition = float4(tmpPosition * dims + position, 0.0, 1.0);\n";
|
|
}
|
|
|
|
void writeVertices(DrawWriter* writer,
|
|
const SkIRect&,
|
|
const Transform&,
|
|
const Shape& shape) const override {
|
|
SkASSERT(shape.isRect());
|
|
|
|
DrawBufferManager* bufferMgr = writer->bufferManager();
|
|
|
|
// TODO: To truly test draw merging, this index buffer needs to remembered across
|
|
// writeVertices calls
|
|
auto [indexWriter, indices] = bufferMgr->getIndexWriter(6 * sizeof(uint16_t));
|
|
indexWriter << 0 << 1 << 2
|
|
<< 2 << 1 << 3;
|
|
|
|
writer->setInstanceTemplate({}, indices, 6);
|
|
auto instanceWriter = writer->appendInstances(1);
|
|
instanceWriter << shape.rect().topLeft() << shape.rect().size();
|
|
}
|
|
|
|
sk_sp<UniformData> writeUniforms(Layout,
|
|
const SkIRect&,
|
|
const Transform&,
|
|
const Shape&) const override {
|
|
return nullptr;
|
|
}
|
|
|
|
private:
|
|
InstanceRectDraw()
|
|
: RenderStep(Flags::kPerformsShading,
|
|
/*uniforms=*/{},
|
|
PrimitiveType::kTriangles,
|
|
kTestDepthStencilSettings,
|
|
/*vertexAttrs=*/{},
|
|
/*instanceAttrs=*/ {
|
|
{ "position", VertexAttribType::kFloat2, SLType::kFloat2 },
|
|
{ "dims", VertexAttribType::kFloat2, SLType::kFloat2 }
|
|
}) {}
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
/*
|
|
* This is to test the various pieces of the CommandBuffer interface.
|
|
*/
|
|
DEF_GRAPHITE_TEST_FOR_CONTEXTS(CommandBufferTest, reporter, context) {
|
|
constexpr int kTextureWidth = 1024;
|
|
constexpr int kTextureHeight = 768;
|
|
|
|
auto gpu = context->priv().gpu();
|
|
REPORTER_ASSERT(reporter, gpu);
|
|
|
|
#if GRAPHITE_TEST_UTILS && CAPTURE_COMMANDBUFFER
|
|
gpu->testingOnly_startCapture();
|
|
#endif
|
|
auto commandBuffer = gpu->resourceProvider()->createCommandBuffer();
|
|
|
|
SkISize textureSize = { kTextureWidth, kTextureHeight };
|
|
#ifdef SK_METAL
|
|
skgpu::mtl::TextureInfo mtlTextureInfo = {
|
|
1,
|
|
1,
|
|
70, // MTLPixelFormatRGBA8Unorm
|
|
0x0005, // MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead
|
|
2, // MTLStorageModePrivate
|
|
false, // framebufferOnly
|
|
};
|
|
TextureInfo textureInfo(mtlTextureInfo);
|
|
#else
|
|
TextureInfo textureInfo;
|
|
#endif
|
|
|
|
auto target = sk_sp<TextureProxy>(new TextureProxy(textureSize, textureInfo));
|
|
REPORTER_ASSERT(reporter, target);
|
|
|
|
RenderPassDesc renderPassDesc = {};
|
|
renderPassDesc.fColorAttachment.fTextureInfo = target->textureInfo();
|
|
renderPassDesc.fColorAttachment.fLoadOp = LoadOp::kClear;
|
|
renderPassDesc.fColorAttachment.fStoreOp = StoreOp::kStore;
|
|
renderPassDesc.fClearColor = { 1, 0, 0, 1 }; // red
|
|
|
|
target->instantiate(gpu->resourceProvider());
|
|
DrawBufferManager bufferMgr(gpu->resourceProvider(), 4);
|
|
|
|
TextureInfo depthStencilInfo =
|
|
gpu->caps()->getDefaultDepthStencilTextureInfo(DepthStencilFlags::kDepthStencil,
|
|
1,
|
|
Protected::kNo);
|
|
renderPassDesc.fDepthStencilAttachment.fTextureInfo = depthStencilInfo;
|
|
renderPassDesc.fDepthStencilAttachment.fLoadOp = LoadOp::kDiscard;
|
|
renderPassDesc.fDepthStencilAttachment.fStoreOp = StoreOp::kDiscard;
|
|
sk_sp<Texture> depthStencilTexture =
|
|
gpu->resourceProvider()->findOrCreateTexture(textureSize, depthStencilInfo);
|
|
|
|
commandBuffer->beginRenderPass(renderPassDesc, target->refTexture(), nullptr,
|
|
depthStencilTexture);
|
|
|
|
commandBuffer->setViewport(0.f, 0.f, kTextureWidth, kTextureHeight);
|
|
|
|
DrawWriter drawWriter(commandBuffer->asDrawDispatcher(), &bufferMgr);
|
|
|
|
struct RectAndColor {
|
|
SkRect fRect;
|
|
SkColor4f fColor;
|
|
};
|
|
|
|
auto draw = [&](const RenderStep* step, std::vector<RectAndColor> draws) {
|
|
Combination shader{ShaderCombo::ShaderType::kSolidColor};
|
|
GraphicsPipelineDesc pipelineDesc;
|
|
pipelineDesc.setProgram(step, shader);
|
|
drawWriter.newPipelineState(step->primitiveType(),
|
|
step->vertexStride(),
|
|
step->instanceStride());
|
|
auto pipeline = gpu->resourceProvider()->findOrCreateGraphicsPipeline(pipelineDesc);
|
|
commandBuffer->bindGraphicsPipeline(std::move(pipeline));
|
|
|
|
// All of the test RenderSteps ignore the transform, so just use the identity
|
|
static const Transform kIdentity{SkM44()};
|
|
// No set scissor, so use entire render target dimensions
|
|
static const SkIRect kBounds = SkIRect::MakeWH(kTextureWidth, kTextureHeight);
|
|
|
|
for (auto d : draws) {
|
|
drawWriter.newDynamicState();
|
|
Shape shape(d.fRect);
|
|
|
|
auto renderStepUniforms =
|
|
step->writeUniforms(Layout::kMetal, kBounds, kIdentity, shape);
|
|
if (renderStepUniforms) {
|
|
auto [writer, bindInfo] =
|
|
bufferMgr.getUniformWriter(renderStepUniforms->dataSize());
|
|
writer.write(renderStepUniforms->data(), renderStepUniforms->dataSize());
|
|
commandBuffer->bindUniformBuffer(UniformSlot::kRenderStep,
|
|
sk_ref_sp(bindInfo.fBuffer),
|
|
bindInfo.fOffset);
|
|
}
|
|
|
|
// TODO: Rely on uniform writer and GetUniforms(kSolidColor).
|
|
auto [writer, bindInfo] = bufferMgr.getUniformWriter(sizeof(SkColor4f));
|
|
writer.write(&d.fColor, sizeof(SkColor4f));
|
|
commandBuffer->bindUniformBuffer(UniformSlot::kPaint,
|
|
sk_ref_sp(bindInfo.fBuffer),
|
|
bindInfo.fOffset);
|
|
|
|
step->writeVertices(&drawWriter, kBounds, kIdentity, shape);
|
|
}
|
|
};
|
|
|
|
SkRect fullRect = SkRect::MakeIWH(kTextureWidth, kTextureHeight);
|
|
// Draw blue rectangle over entire rendertarget (which was red)
|
|
draw(UniformRectDraw::Singleton(), {{fullRect, SkColors::kBlue}});
|
|
|
|
// Draw inset yellow rectangle using uniforms
|
|
draw(UniformRectDraw::Singleton(),
|
|
{{fullRect.makeInset(kTextureWidth/20.f, kTextureHeight/20.f), SkColors::kYellow}});
|
|
|
|
// Draw inset magenta rectangle with triangles in vertex buffer
|
|
draw(TriangleRectDraw::Singleton(),
|
|
{{fullRect.makeInset(kTextureWidth/4.f, kTextureHeight/4.f), SkColors::kMagenta}});
|
|
|
|
// Draw green and cyan rects using instance buffer
|
|
draw(InstanceRectDraw::Singleton(),
|
|
{ {{kTextureWidth/3.f, kTextureHeight/3.f,
|
|
kTextureWidth/2.f, kTextureHeight/2.f}, SkColors::kGreen},
|
|
{{kTextureWidth/2.f, kTextureHeight/2.f,
|
|
5.f*kTextureWidth/8.f, 5.f*kTextureHeight/8.f}, SkColors::kCyan} });
|
|
|
|
drawWriter.flush();
|
|
bufferMgr.transferToCommandBuffer(commandBuffer.get());
|
|
commandBuffer->endRenderPass();
|
|
|
|
// Do readback
|
|
|
|
// TODO: add 4-byte transfer buffer alignment for Mac to Caps
|
|
// add bpp to Caps
|
|
size_t rowBytes = 4*kTextureWidth;
|
|
size_t bufferSize = rowBytes*kTextureHeight;
|
|
sk_sp<Buffer> copyBuffer = gpu->resourceProvider()->findOrCreateBuffer(
|
|
bufferSize, BufferType::kXferGpuToCpu, PrioritizeGpuReads::kNo);
|
|
REPORTER_ASSERT(reporter, copyBuffer);
|
|
SkIRect srcRect = { 0, 0, kTextureWidth, kTextureHeight };
|
|
commandBuffer->copyTextureToBuffer(target->refTexture(), srcRect, copyBuffer, 0, rowBytes);
|
|
|
|
bool result = gpu->submit(commandBuffer);
|
|
REPORTER_ASSERT(reporter, result);
|
|
|
|
gpu->checkForFinishedWork(skgpu::SyncToCpu::kYes);
|
|
uint32_t* pixels = (uint32_t*)(copyBuffer->map());
|
|
REPORTER_ASSERT(reporter, pixels[0] == 0xffff0000);
|
|
REPORTER_ASSERT(reporter, pixels[51 + 38*kTextureWidth] == 0xff00ffff);
|
|
REPORTER_ASSERT(reporter, pixels[256 + 192*kTextureWidth] == 0xffff00ff);
|
|
copyBuffer->unmap();
|
|
|
|
#if GRAPHITE_TEST_UTILS && CAPTURE_COMMANDBUFFER
|
|
gpu->testingOnly_endCapture();
|
|
#endif
|
|
}
|