blob: 482753565722cc6292a2b2ad2f0b461902dc599e [file] [log] [blame]
/*
* 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 "include/gpu/graphite/Recorder.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/gpu/graphite/BackendTexture.h"
#include "include/gpu/graphite/GraphiteTypes.h"
#include "include/gpu/graphite/ImageProvider.h"
#include "include/gpu/graphite/Recording.h"
#include "src/core/SkConvertPixels.h"
#include "src/core/SkRuntimeEffectDictionary.h"
#include "src/gpu/AtlasTypes.h"
#include "src/gpu/graphite/Caps.h"
#include "src/gpu/graphite/CommandBuffer.h"
#include "src/gpu/graphite/ContextPriv.h"
#include "src/gpu/graphite/CopyTask.h"
#include "src/gpu/graphite/Device.h"
#include "src/gpu/graphite/DrawBufferManager.h"
#include "src/gpu/graphite/GlobalCache.h"
#include "src/gpu/graphite/PipelineData.h"
#include "src/gpu/graphite/PipelineDataCache.h"
#include "src/gpu/graphite/RecorderPriv.h"
#include "src/gpu/graphite/ResourceProvider.h"
#include "src/gpu/graphite/SharedContext.h"
#include "src/gpu/graphite/SynchronizeToCpuTask.h"
#include "src/gpu/graphite/TaskGraph.h"
#include "src/gpu/graphite/Texture.h"
#include "src/gpu/graphite/UploadBufferManager.h"
#include "src/gpu/graphite/UploadTask.h"
#include "src/gpu/graphite/text/AtlasManager.h"
#include "src/image/SkImage_Base.h"
#include "src/text/gpu/StrikeCache.h"
#include "src/text/gpu/TextBlobRedrawCoordinator.h"
namespace skgpu::graphite {
#define ASSERT_SINGLE_OWNER SKGPU_ASSERT_SINGLE_OWNER(this->singleOwner())
#define ASSERT_SINGLE_OWNER_PRIV SKGPU_ASSERT_SINGLE_OWNER(fRecorder->singleOwner())
/*
* The default image provider doesn't perform any conversion so, by default, Graphite won't
* draw any non-Graphite-backed images.
*/
class DefaultImageProvider final : public ImageProvider {
public:
static sk_sp<DefaultImageProvider> Make() {
return sk_ref_sp(new DefaultImageProvider);
}
sk_sp<SkImage> findOrCreate(Recorder* recorder,
const SkImage* image,
SkImage::RequiredImageProperties) override {
SkASSERT(!as_IB(image)->isGraphiteBacked());
return nullptr;
}
private:
DefaultImageProvider() {}
};
/**************************************************************************************************/
RecorderOptions::RecorderOptions() = default;
RecorderOptions::RecorderOptions(const RecorderOptions&) = default;
RecorderOptions::~RecorderOptions() = default;
/**************************************************************************************************/
static int32_t next_id() {
static std::atomic<int32_t> nextID{1};
int32_t id;
do {
id = nextID.fetch_add(1, std::memory_order_relaxed);
} while (id == SK_InvalidGenID);
return id;
}
Recorder::Recorder(sk_sp<SharedContext> sharedContext,
const RecorderOptions& options)
: fSharedContext(std::move(sharedContext))
, fRuntimeEffectDict(std::make_unique<SkRuntimeEffectDictionary>())
, fGraph(new TaskGraph)
, fUniformDataCache(new UniformDataCache)
, fTextureDataCache(new TextureDataCache)
, fRecorderID(next_id())
, fAtlasManager(std::make_unique<AtlasManager>(this))
, fTokenTracker(std::make_unique<TokenTracker>())
, fStrikeCache(std::make_unique<sktext::gpu::StrikeCache>())
, fTextBlobCache(std::make_unique<sktext::gpu::TextBlobRedrawCoordinator>(fRecorderID)) {
fClientImageProvider = options.fImageProvider;
if (!fClientImageProvider) {
fClientImageProvider = DefaultImageProvider::Make();
}
fResourceProvider = fSharedContext->makeResourceProvider(this->singleOwner());
fDrawBufferManager.reset(
new DrawBufferManager(fResourceProvider.get(),
fSharedContext->caps()->requiredUniformBufferAlignment(),
fSharedContext->caps()->requiredStorageBufferAlignment()));
fUploadBufferManager.reset(new UploadBufferManager(fResourceProvider.get()));
SkASSERT(fResourceProvider);
}
Recorder::~Recorder() {
ASSERT_SINGLE_OWNER
for (auto& device : fTrackedDevices) {
device->abandonRecorder();
}
#if GRAPHITE_TEST_UTILS
if (fContext) {
fContext->priv().deregisterRecorder(this);
}
#endif
// TODO: needed?
fStrikeCache->freeAll();
}
BackendApi Recorder::backend() const { return fSharedContext->backend(); }
std::unique_ptr<Recording> Recorder::snap() {
ASSERT_SINGLE_OWNER
for (auto& device : fTrackedDevices) {
device->flushPendingWorkToRecorder();
}
std::unordered_set<sk_sp<TextureProxy>, Recording::ProxyHash> nonVolatileLazyProxies;
std::unordered_set<sk_sp<TextureProxy>, Recording::ProxyHash> volatileLazyProxies;
fTextureDataCache->foreach([&](const TextureDataBlock* block) {
for (int j = 0; j < block->numTextures(); ++j) {
const TextureDataBlock::SampledTexture& tex = block->texture(j);
if (tex.first->isLazy()) {
if (tex.first->isVolatile()) {
volatileLazyProxies.insert(tex.first);
} else {
nonVolatileLazyProxies.insert(tex.first);
}
}
}
});
// TODO: fulfill all promise images in the TextureDataCache here
// TODO: create all the samplers needed in the TextureDataCache here
if (!fGraph->prepareResources(fResourceProvider.get(), fRuntimeEffectDict.get())) {
// Leaving 'fTrackedDevices' alone since they were flushed earlier and could still be
// attached to extant SkSurfaces.
fDrawBufferManager.reset(
new DrawBufferManager(fResourceProvider.get(),
fSharedContext->caps()->requiredUniformBufferAlignment(),
fSharedContext->caps()->requiredStorageBufferAlignment()));
fTextureDataCache = std::make_unique<TextureDataCache>();
// We leave the UniformDataCache alone
fGraph->reset();
fRuntimeEffectDict->reset();
return nullptr;
}
std::unique_ptr<Recording> recording(new Recording(std::move(fGraph),
std::move(nonVolatileLazyProxies),
std::move(volatileLazyProxies)));
fDrawBufferManager->transferToRecording(recording.get());
fUploadBufferManager->transferToRecording(recording.get());
fGraph = std::make_unique<TaskGraph>();
fRuntimeEffectDict->reset();
fTextureDataCache = std::make_unique<TextureDataCache>();
fAtlasManager->evictAtlases();
return recording;
}
void Recorder::registerDevice(Device* device) {
ASSERT_SINGLE_OWNER
fTrackedDevices.push_back(device);
}
void Recorder::deregisterDevice(const Device* device) {
ASSERT_SINGLE_OWNER
for (auto it = fTrackedDevices.begin(); it != fTrackedDevices.end(); it++) {
if (*it == device) {
fTrackedDevices.erase(it);
return;
}
}
}
#if GRAPHITE_TEST_UTILS
bool Recorder::deviceIsRegistered(Device* device) {
ASSERT_SINGLE_OWNER
for (auto& currentDevice : fTrackedDevices) {
if (device == currentDevice) {
return true;
}
}
return false;
}
#endif
BackendTexture Recorder::createBackendTexture(SkISize dimensions, const TextureInfo& info) {
ASSERT_SINGLE_OWNER
if (!info.isValid() || info.backend() != this->backend()) {
return {};
}
return fResourceProvider->createBackendTexture(dimensions, info);
}
bool Recorder::updateBackendTexture(const BackendTexture& backendTex,
const SkPixmap srcData[],
int numLevels) {
ASSERT_SINGLE_OWNER
if (!backendTex.isValid() || backendTex.backend() != this->backend()) {
return false;
}
if (!srcData || numLevels <= 0) {
return false;
}
// If the texture has MIP levels then we require that the full set is overwritten.
int numExpectedLevels = 1;
if (backendTex.info().mipmapped() == Mipmapped::kYes) {
numExpectedLevels = SkMipmap::ComputeLevelCount(backendTex.dimensions().width(),
backendTex.dimensions().height()) + 1;
}
if (numLevels != numExpectedLevels) {
return false;
}
SkColorType ct = srcData[0].colorType();
if (!this->priv().caps()->areColorTypeAndTextureInfoCompatible(ct, backendTex.info())) {
return false;
}
sk_sp<Texture> texture = this->priv().resourceProvider()->createWrappedTexture(backendTex);
if (!texture) {
return false;
}
sk_sp<TextureProxy> proxy(new TextureProxy(std::move(texture)));
std::vector<MipLevel> mipLevels;
mipLevels.resize(numLevels);
for (int i = 0; i < numLevels; ++i) {
SkASSERT(srcData[i].addr());
SkASSERT(srcData[i].colorType() == ct);
mipLevels[i].fPixels = srcData[i].addr();
mipLevels[i].fRowBytes = srcData[i].rowBytes();
}
UploadInstance upload = UploadInstance::Make(this,
std::move(proxy),
ct,
mipLevels,
SkIRect::MakeSize(backendTex.dimensions()));
sk_sp<Task> uploadTask = UploadTask::Make(upload);
this->priv().add(std::move(uploadTask));
return true;
}
void Recorder::deleteBackendTexture(BackendTexture& texture) {
ASSERT_SINGLE_OWNER
if (!texture.isValid() || texture.backend() != this->backend()) {
return;
}
fResourceProvider->deleteBackendTexture(texture);
}
void RecorderPriv::add(sk_sp<Task> task) {
ASSERT_SINGLE_OWNER_PRIV
fRecorder->fGraph->add(std::move(task));
}
void RecorderPriv::flushTrackedDevices() {
ASSERT_SINGLE_OWNER_PRIV
for (Device* device : fRecorder->fTrackedDevices) {
device->flushPendingWorkToRecorder();
}
}
RecorderPriv::PixelTransferResult RecorderPriv::transferPixels(const TextureProxy* proxy,
const SkImageInfo& srcImageInfo,
const SkColorInfo& dstColorInfo,
const SkIRect& srcRect) {
SkASSERT(srcImageInfo.bounds().contains(srcRect));
const Caps* caps = this->caps();
SkColorType supportedColorType =
caps->supportedReadPixelsColorType(srcImageInfo.colorType(),
proxy->textureInfo(),
dstColorInfo.colorType());
if (supportedColorType == kUnknown_SkColorType) {
return {};
}
// Fail if read color type does not have all of dstCT's color channels and those missing color
// channels are in the src.
uint32_t dstChannels = SkColorTypeChannelFlags(dstColorInfo.colorType());
uint32_t legalReadChannels = SkColorTypeChannelFlags(supportedColorType);
uint32_t srcChannels = SkColorTypeChannelFlags(srcImageInfo.colorType());
if ((~legalReadChannels & dstChannels) & srcChannels) {
return {};
}
size_t rowBytes = caps->getAlignedTextureDataRowBytes(
SkColorTypeBytesPerPixel(supportedColorType) * srcRect.width());
size_t size = rowBytes * srcRect.height();
sk_sp<Buffer> buffer = this->resourceProvider()->findOrCreateBuffer(
size,
BufferType::kXferCpuToGpu,
PrioritizeGpuReads::kNo);
if (!buffer) {
return {};
}
// Set up copy task
sk_sp<CopyTextureToBufferTask> copyTask = CopyTextureToBufferTask::Make(sk_ref_sp(proxy),
srcRect,
buffer,
/*bufferOffset=*/0,
rowBytes);
if (!copyTask) {
return {};
}
sk_sp<SynchronizeToCpuTask> syncTask = SynchronizeToCpuTask::Make(buffer);
if (!syncTask) {
return {};
}
this->add(std::move(copyTask));
this->add(std::move(syncTask));
PixelTransferResult result;
result.fTransferBuffer = std::move(buffer);
if (srcImageInfo.colorInfo() != dstColorInfo) {
result.fPixelConverter = [dims = srcRect.size(), dstColorInfo, srcImageInfo](
void* dst, const void* src) {
SkImageInfo srcInfo = SkImageInfo::Make(dims, srcImageInfo.colorInfo());
SkImageInfo dstInfo = SkImageInfo::Make(dims, dstColorInfo);
SkAssertResult(SkConvertPixels(dstInfo, dst, dstInfo.minRowBytes(),
srcInfo, src, srcInfo.minRowBytes()));
};
}
return result;
}
#if GRAPHITE_TEST_UTILS
// used by the Context that created this Recorder to set a back pointer
void RecorderPriv::setContext(Context* context) {
fRecorder->fContext = context;
}
#endif
} // namespace skgpu::graphite