242 lines
10 KiB
C++
242 lines
10 KiB
C++
/*
|
|
* Copyright 2022 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 "include/core/SkBitmap.h"
|
|
#include "include/core/SkCanvas.h"
|
|
#include "include/core/SkImage.h"
|
|
#include "include/core/SkSurface.h"
|
|
#include "include/gpu/graphite/Context.h"
|
|
#include "include/gpu/graphite/Recorder.h"
|
|
#include "include/gpu/graphite/Recording.h"
|
|
#include "src/core/SkCanvasPriv.h"
|
|
#include "src/gpu/graphite/Device.h"
|
|
#include "src/gpu/graphite/RecorderPriv.h"
|
|
#include "src/gpu/graphite/Resource.h"
|
|
#include "src/gpu/graphite/ResourceCache.h"
|
|
#include "src/gpu/graphite/ResourceProvider.h"
|
|
#include "src/gpu/graphite/SharedContext.h"
|
|
#include "src/gpu/graphite/Texture.h"
|
|
#include "src/gpu/graphite/TextureProxyView.h"
|
|
#include "src/image/SkImage_Base.h"
|
|
|
|
namespace skgpu::graphite {
|
|
|
|
class TestResource : public Resource {
|
|
public:
|
|
static sk_sp<TestResource> Make(const SharedContext* sharedContext,
|
|
Ownership owned,
|
|
skgpu::Budgeted budgeted,
|
|
Shareable shareable) {
|
|
auto resource = sk_sp<TestResource>(new TestResource(sharedContext, owned, budgeted));
|
|
if (!resource) {
|
|
return nullptr;
|
|
}
|
|
|
|
GraphiteResourceKey key;
|
|
CreateKey(&key, shareable);
|
|
|
|
resource->setKey(key);
|
|
return resource;
|
|
}
|
|
|
|
static void CreateKey(GraphiteResourceKey* key, Shareable shareable) {
|
|
// Internally we assert that we don't make the same key twice where the only difference is
|
|
// shareable vs non-shareable. That allows us to now have Shareable be part of the Key's
|
|
// key. So here we make two different resource types so the keys will be different.
|
|
static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
|
|
static const ResourceType kShareableType = GraphiteResourceKey::GenerateResourceType();
|
|
ResourceType type = shareable == Shareable::kNo ? kType : kShareableType;
|
|
GraphiteResourceKey::Builder(key, type, 0, shareable);
|
|
}
|
|
|
|
private:
|
|
TestResource(const SharedContext* sharedContext, Ownership owned, skgpu::Budgeted budgeted)
|
|
: Resource(sharedContext, owned, budgeted) {}
|
|
|
|
void freeGpuData() override {}
|
|
};
|
|
|
|
static sk_sp<SkData> create_image_data(const SkImageInfo& info) {
|
|
const size_t rowBytes = info.minRowBytes();
|
|
sk_sp<SkData> data(SkData::MakeUninitialized(rowBytes * info.height()));
|
|
{
|
|
SkBitmap bm;
|
|
bm.installPixels(info, data->writable_data(), rowBytes);
|
|
SkCanvas canvas(bm);
|
|
canvas.clear(SK_ColorRED);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(GraphiteBudgetedResourcesTest, reporter, context) {
|
|
std::unique_ptr<Recorder> recorder = context->makeRecorder();
|
|
ResourceProvider* resourceProvider = recorder->priv().resourceProvider();
|
|
ResourceCache* resourceCache = resourceProvider->resourceCache();
|
|
const SharedContext* sharedContext = resourceProvider->sharedContext();
|
|
|
|
REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 0);
|
|
REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 0);
|
|
|
|
// Test making a non budgeted, non shareable resource.
|
|
auto resource = TestResource::Make(
|
|
sharedContext, Ownership::kOwned, skgpu::Budgeted::kNo, Shareable::kNo);
|
|
if (!resource) {
|
|
ERRORF(reporter, "Failed to make TestResource");
|
|
return;
|
|
}
|
|
Resource* resourcePtr = resource.get();
|
|
|
|
REPORTER_ASSERT(reporter, resource->budgeted() == skgpu::Budgeted::kNo);
|
|
resourceCache->insertResource(resourcePtr);
|
|
REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 1);
|
|
// Resource is not shareable and we have a ref on it. Thus it shouldn't ben findable in the
|
|
// cache.
|
|
REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 0);
|
|
|
|
// When we reset our TestResource it should go back into the cache since it can be used as a
|
|
// scratch texture (since it is not shareable). At that point the budget should be changed to
|
|
// skgpu::Budgeted::kYes.
|
|
resource.reset();
|
|
resourceCache->forceProcessReturnedResources();
|
|
REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 1);
|
|
REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 1);
|
|
// Even though we reset our ref on the resource we still have the ptr to it and should be the
|
|
// resource in the cache. So in general this is dangerous it should be safe for this test to
|
|
// directly access the texture.
|
|
REPORTER_ASSERT(reporter, resourcePtr->budgeted() == skgpu::Budgeted::kYes);
|
|
|
|
GraphiteResourceKey key;
|
|
TestResource::CreateKey(&key, Shareable::kNo);
|
|
Resource* resourcePtr2 = resourceCache->findAndRefResource(key, skgpu::Budgeted::kNo);
|
|
REPORTER_ASSERT(reporter, resourcePtr == resourcePtr2);
|
|
REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 1);
|
|
REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 0);
|
|
REPORTER_ASSERT(reporter, resourcePtr2->budgeted() == skgpu::Budgeted::kNo);
|
|
resourcePtr2->unref();
|
|
resourceCache->forceProcessReturnedResources();
|
|
|
|
// Test making a non budgeted, non shareable resource.
|
|
resource = TestResource::Make(
|
|
sharedContext, Ownership::kOwned, skgpu::Budgeted::kYes, Shareable::kYes);
|
|
if (!resource) {
|
|
ERRORF(reporter, "Failed to make TestResource");
|
|
return;
|
|
}
|
|
resourcePtr = resource.get();
|
|
REPORTER_ASSERT(reporter, resource->budgeted() == skgpu::Budgeted::kYes);
|
|
resourceCache->insertResource(resourcePtr);
|
|
REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 2);
|
|
REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 2);
|
|
|
|
resource.reset();
|
|
resourceCache->forceProcessReturnedResources();
|
|
REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 2);
|
|
REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 2);
|
|
REPORTER_ASSERT(reporter, resourcePtr->budgeted() == skgpu::Budgeted::kYes);
|
|
|
|
TestResource::CreateKey(&key, Shareable::kYes);
|
|
resourcePtr2 = resourceCache->findAndRefResource(key, skgpu::Budgeted::kYes);
|
|
REPORTER_ASSERT(reporter, resourcePtr == resourcePtr2);
|
|
REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 2);
|
|
REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 2);
|
|
REPORTER_ASSERT(reporter, resourcePtr2->budgeted() == skgpu::Budgeted::kYes);
|
|
resourcePtr2->unref();
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Test that SkImage's and SkSurface's underlying Resource's follow the expected budgeted
|
|
// system.
|
|
auto info = SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
|
|
|
|
// First test SkImages. Since we can't directly create a Graphite SkImage we first have to make
|
|
// a raster SkImage than convert that to a Graphite SkImage via makeTextureImage.
|
|
sk_sp<SkData> data(create_image_data(info));
|
|
sk_sp<SkImage> image = SkImage::MakeRasterData(info, std::move(data), info.minRowBytes());
|
|
REPORTER_ASSERT(reporter, image);
|
|
|
|
sk_sp<SkImage> imageGpu = image->makeTextureImage(recorder.get());
|
|
REPORTER_ASSERT(reporter, imageGpu);
|
|
|
|
TextureProxy* imageProxy = nullptr;
|
|
{
|
|
// We don't want the view holding a ref to the Proxy or else we can't send things back to
|
|
// the cache.
|
|
auto [view, colorType] = as_IB(imageGpu.get())->asView(recorder.get(), Mipmapped::kNo);
|
|
REPORTER_ASSERT(reporter, view);
|
|
imageProxy = view.proxy();
|
|
}
|
|
// Make sure the proxy is instantiated
|
|
if (!imageProxy->instantiate(resourceProvider)) {
|
|
ERRORF(reporter, "Failed to instantiate Proxy");
|
|
return;
|
|
}
|
|
const Resource* imageResourcePtr = imageProxy->texture();
|
|
REPORTER_ASSERT(reporter, imageResourcePtr);
|
|
// There is an extra resource for the buffer that is uploading the data to the texture
|
|
REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 4);
|
|
REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 2);
|
|
REPORTER_ASSERT(reporter, imageResourcePtr->budgeted() == skgpu::Budgeted::kNo);
|
|
|
|
// Submit all upload work so we can drop refs to the image and get it returned to the cache.
|
|
std::unique_ptr<Recording> recording = recorder->snap();
|
|
if (!recording) {
|
|
ERRORF(reporter, "Failed to make recording");
|
|
return;
|
|
}
|
|
InsertRecordingInfo insertInfo;
|
|
insertInfo.fRecording = recording.get();
|
|
context->insertRecording(insertInfo);
|
|
context->submit(SyncToCpu::kYes);
|
|
recording.reset();
|
|
imageGpu.reset();
|
|
resourceCache->forceProcessReturnedResources();
|
|
|
|
REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 4);
|
|
REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 4);
|
|
REPORTER_ASSERT(reporter, imageResourcePtr->budgeted() == skgpu::Budgeted::kYes);
|
|
|
|
// Now try an SkSurface. This is simpler since we can directly create Graphite SkSurface's.
|
|
sk_sp<SkSurface> surface = SkSurface::MakeGraphite(recorder.get(), info);
|
|
if (!surface) {
|
|
ERRORF(reporter, "Failed to make surface");
|
|
return;
|
|
}
|
|
|
|
TextureProxy* surfaceProxy = SkCanvasPriv::TopDeviceGraphiteTargetProxy(surface->getCanvas());
|
|
if (!surfaceProxy) {
|
|
ERRORF(reporter, "Failed to get surface proxy");
|
|
return;
|
|
}
|
|
|
|
// Make sure the proxy is instantiated
|
|
if (!surfaceProxy->instantiate(resourceProvider)) {
|
|
ERRORF(reporter, "Failed to instantiate surface proxy");
|
|
return;
|
|
}
|
|
const Resource* surfaceResourcePtr = surfaceProxy->texture();
|
|
|
|
REPORTER_ASSERT(reporter, resourceCache->getResourceCount() == 5);
|
|
REPORTER_ASSERT(reporter, resourceCache->numFindableResources() == 4);
|
|
REPORTER_ASSERT(reporter, surfaceResourcePtr->budgeted() == skgpu::Budgeted::kNo);
|
|
|
|
// The creation of the surface may have added an initial clear to it. Thus if we just reset the
|
|
// surface it will flush the clean on the device and we don't be dropping all our refs to the
|
|
// surface. So we force all the work to happen first.
|
|
recording = recorder->snap();
|
|
insertInfo.fRecording = recording.get();
|
|
context->insertRecording(insertInfo);
|
|
context->submit(SyncToCpu::kYes);
|
|
recording.reset();
|
|
|
|
surface.reset();
|
|
resourceCache->forceProcessReturnedResources();
|
|
REPORTER_ASSERT(reporter, surfaceResourcePtr->budgeted() == skgpu::Budgeted::kYes);
|
|
}
|
|
|
|
} // namespace skgpu::graphite
|