blob: 5e1be76b25b8a37ba324a2ce723f26f997b09491 [file] [log] [blame]
/*
* Copyright 2023 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/graphite/RasterPathAtlas.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkColorSpace.h"
#include "include/gpu/graphite/Recorder.h"
#include "src/gpu/graphite/AtlasProvider.h"
#include "src/gpu/graphite/DrawContext.h"
#include "src/gpu/graphite/Log.h"
#include "src/gpu/graphite/ProxyCache.h"
#include "src/gpu/graphite/RasterPathUtils.h"
#include "src/gpu/graphite/RecorderPriv.h"
namespace skgpu::graphite {
static constexpr uint32_t kDefaultAtlasDim = 2048;
static constexpr uint32_t kSmallPathPlotWidth = 512;
static constexpr uint32_t kSmallPathPlotHeight = 256;
static constexpr uint32_t kUncachedAtlasDim = 2048;
RasterPathAtlas::RasterPathAtlas(Recorder* recorder)
: PathAtlas(recorder, kDefaultAtlasDim, kDefaultAtlasDim)
, fCachedAtlasMgr(fWidth, fHeight,
/*plotWidth=*/fWidth/2, /*plotHeight=*/fHeight/2,
recorder->priv().caps())
, fSmallPathAtlasMgr(std::max(fWidth, kSmallPathPlotWidth),
std::max(fHeight, kSmallPathPlotHeight),
kSmallPathPlotWidth, kSmallPathPlotHeight,
recorder->priv().caps())
, fUncachedAtlasMgr(kUncachedAtlasDim, kUncachedAtlasDim,
/*plotWidth=*/kUncachedAtlasDim/2, /*plotHeight=*/kUncachedAtlasDim/2,
recorder->priv().caps()) {
SkASSERT(recorder);
}
void RasterPathAtlas::recordUploads(DrawContext* dc) {
fCachedAtlasMgr.recordUploads(dc, fRecorder);
fSmallPathAtlasMgr.recordUploads(dc, fRecorder);
fUncachedAtlasMgr.recordUploads(dc, fRecorder);
}
static bool draw_path_to_pixmap(const Shape& shape,
const Transform& localToDevice,
const SkStrokeRec& strokeRec,
SkIRect shapeBounds,
SkIVector transformedMaskOffset,
SkISize pixmapSize,
SkAutoPixmapStorage* dst) {
RasterMaskHelper helper(dst);
if (!helper.init(pixmapSize, transformedMaskOffset)) {
return false;
}
helper.drawShape(shape, localToDevice, strokeRec, shapeBounds);
return true;
}
sk_sp<TextureProxy> RasterPathAtlas::onAddShape(const Shape& shape,
const Transform& localToDevice,
const SkStrokeRec& strokeRec,
skvx::half2 maskOrigin,
skvx::half2 maskSize,
SkIVector transformedMaskOffset,
skvx::half2* outPos) {
sk_sp<TextureProxy> proxy;
if (!shape.isVolatilePath()) {
constexpr int kMaxSmallPathSize = 162;
// Try to locate or add to cached DrawAtlas
if (maskSize.x() <= kMaxSmallPathSize && maskSize.y() <= kMaxSmallPathSize) {
proxy = fSmallPathAtlasMgr.findOrCreateEntry(fRecorder,
shape,
localToDevice,
strokeRec,
maskOrigin,
maskSize,
transformedMaskOffset,
outPos);
}
if (!proxy) {
proxy = fCachedAtlasMgr.findOrCreateEntry(fRecorder,
shape,
localToDevice,
strokeRec,
maskOrigin,
maskSize,
transformedMaskOffset,
outPos);
}
}
// Try to add to uncached DrawAtlas
if (!proxy) {
AtlasLocator loc;
proxy = fUncachedAtlasMgr.addToAtlas(fRecorder,
shape,
localToDevice,
strokeRec,
maskSize,
transformedMaskOffset,
outPos,
&loc);
}
if (proxy) {
return proxy;
}
// Failed to add to atlases, try to add to ProxyCache
skgpu::UniqueKey maskKey = GeneratePathMaskKey(shape, localToDevice, strokeRec,
maskOrigin, maskSize);
struct PathDrawContext {
const Shape& fShape;
const Transform& fLocalToDevice;
const SkStrokeRec& fStrokeRec;
SkIRect fShapeBounds;
SkIVector fTransformedMaskOffset;
} context = { shape, localToDevice, strokeRec,
SkIRect::MakeSize({maskSize.x(), maskSize.y()}).makeOffset(kEntryPadding,
kEntryPadding),
transformedMaskOffset };
sk_sp<TextureProxy> cachedProxy = fRecorder->priv().proxyCache()->findOrCreateCachedProxy(
fRecorder, maskKey, &context,
[](const void* ctx) {
const PathDrawContext* pdc = static_cast<const PathDrawContext*>(ctx);
SkAutoPixmapStorage dst;
SkISize pixmapSize = pdc->fShapeBounds.size();
pixmapSize.fWidth += 2*kEntryPadding;
pixmapSize.fHeight += 2*kEntryPadding;
draw_path_to_pixmap(pdc->fShape, pdc->fLocalToDevice, pdc->fStrokeRec,
pdc->fShapeBounds, pdc->fTransformedMaskOffset,
pixmapSize, &dst);
SkBitmap bm;
// The bitmap needs to take ownership of the pixels, so we detach them from the
// SkAutoPixmapStorage and pass them to SkBitmap::installPixels().
SkImageInfo ii = dst.info();
size_t rowBytes = dst.rowBytes();
SkAssertResult(bm.installPixels(ii, dst.detachPixels(), rowBytes,
[](void* addr, void* context) { sk_free(addr); },
nullptr));
bm.setImmutable();
return bm;
});
*outPos = { kEntryPadding, kEntryPadding };
return cachedProxy;
}
/////////////////////////////////////////////////////////////////////////////////////////
bool RasterPathAtlas::RasterAtlasMgr::onAddToAtlas(const Shape& shape,
const Transform& localToDevice,
const SkStrokeRec& strokeRec,
SkIRect shapeBounds,
SkIVector transformedMaskOffset,
const AtlasLocator& locator) {
// Rasterize path to backing pixmap.
// This pixmap will be the size of the Plot that contains the given rect, not the entire atlas,
// and hence the position we render at will be relative to that Plot.
// The value of outPos is relative to the entire texture, to be used for texture coords.
SkAutoPixmapStorage dst;
SkIPoint renderPos = fDrawAtlas->prepForRender(locator, &dst);
// Offset to plot location and draw
shapeBounds.offset(renderPos.x()+kEntryPadding, renderPos.y()+kEntryPadding);
return draw_path_to_pixmap(shape, localToDevice, strokeRec, shapeBounds,
transformedMaskOffset, fDrawAtlas->plotSize(), &dst);
}
} // namespace skgpu::graphite