#include "src/gpu/graphite/ComputePathAtlas.h"
#include "include/gpu/graphite/Recorder.h"
#include "src/core/SkTraceEvent.h"
#include "src/gpu/graphite/AtlasProvider.h"
#include "src/gpu/graphite/Caps.h"
#include "src/gpu/graphite/Log.h"
#include "src/gpu/graphite/RasterPathUtils.h"
#include "src/gpu/graphite/RecorderPriv.h"
#include "src/gpu/graphite/RendererProvider.h"
#include "src/gpu/graphite/TextureProxy.h"
#include "src/gpu/graphite/TextureUtils.h"
#include "src/gpu/graphite/geom/Transform_graphite.h"
#ifdef SK_ENABLE_VELLO_SHADERS
#include "src/gpu/graphite/compute/DispatchGroup.h"
#endif
namespace skgpu::graphite {
namespace {
constexpr uint16_t kComputeAtlasDim = …;
constexpr size_t kBboxAreaThreshold = …;
constexpr float kCoordinateThreshold = …;
}
ComputePathAtlas::ComputePathAtlas(Recorder* recorder)
: … { … }
bool ComputePathAtlas::initializeTextureIfNeeded() { … }
bool ComputePathAtlas::isSuitableForAtlasing(const Rect& transformedShapeBounds,
const Rect& clipBounds) const { … }
const TextureProxy* ComputePathAtlas::addRect(skvx::half2 maskSize,
SkIPoint16* outPos) { … }
void ComputePathAtlas::reset() { … }
#ifdef SK_ENABLE_VELLO_SHADERS
class VelloComputePathAtlas final : public ComputePathAtlas {
public:
explicit VelloComputePathAtlas(Recorder* recorder)
: ComputePathAtlas(recorder)
, fCachedAtlasMgr(fWidth, fHeight, recorder->priv().caps()) {}
bool recordDispatches(Recorder*, ComputeTask::DispatchGroupList*) const override;
private:
const TextureProxy* onAddShape(const Shape&,
const Transform&,
const SkStrokeRec&,
skvx::half2 maskSize,
skvx::half2* outPos) override;
void onReset() override {
fCachedAtlasMgr.onReset();
fUncachedScene.reset();
fUncachedOccupiedArea = { 0, 0 };
}
class VelloAtlasMgr : public PathAtlas::DrawAtlasMgr {
public:
VelloAtlasMgr(size_t width, size_t height, const Caps* caps)
: PathAtlas::DrawAtlasMgr(width, height, width, height,
DrawAtlas::UseStorageTextures::kYes,
"VelloPathAtlas", caps) {}
bool recordDispatches(Recorder* recorder, ComputeTask::DispatchGroupList* dispatches) const;
void onReset() {
fDrawAtlas->markUsedPlotsAsFull();
for (int i = 0; i < PlotLocator::kMaxMultitexturePages; ++i) {
fScenes[i].reset();
fOccupiedAreas[i] = {0, 0};
}
}
protected:
bool onAddToAtlas(const Shape&,
const Transform& transform,
const SkStrokeRec&,
SkIRect shapeBounds,
const AtlasLocator&) override;
private:
VelloScene fScenes[PlotLocator::kMaxMultitexturePages];
SkISize fOccupiedAreas[PlotLocator::kMaxMultitexturePages] = {
{0, 0}, {0, 0}, {0, 0}, {0, 0}
};
};
VelloAtlasMgr fCachedAtlasMgr;
VelloScene fUncachedScene;
SkISize fUncachedOccupiedArea = { 0, 0 };
};
static VelloAaConfig get_vello_aa_config(Recorder* recorder) {
VelloAaConfig config = VelloAaConfig::kAnalyticArea;
#if defined(GPU_TEST_UTILS)
PathRendererStrategy strategy = recorder->priv().caps()->requestedPathRendererStrategy();
if (strategy == PathRendererStrategy::kComputeMSAA16) {
config = VelloAaConfig::kMSAA16;
} else if (strategy == PathRendererStrategy::kComputeMSAA8) {
config = VelloAaConfig::kMSAA8;
}
#endif
return config;
}
static std::unique_ptr<DispatchGroup> render_vello_scene(Recorder* recorder,
sk_sp<TextureProxy> texture,
const VelloScene& scene,
SkISize occupiedArea,
VelloAaConfig config) {
return recorder->priv().rendererProvider()->velloRenderer()->renderScene(
{(uint32_t)occupiedArea.width(),
(uint32_t)occupiedArea.height(),
SkColors::kBlack,
config},
scene,
std::move(texture),
recorder);
}
static void add_shape_to_scene(const Shape& shape,
const Transform& transform,
const SkStrokeRec& style,
Rect atlasBounds,
VelloScene* scene,
SkISize* occupiedArea) {
occupiedArea->fWidth = std::max(occupiedArea->fWidth,
(int)atlasBounds.right() + PathAtlas::kEntryPadding);
occupiedArea->fHeight = std::max(occupiedArea->fHeight,
(int)atlasBounds.bot() + PathAtlas::kEntryPadding);
SkPath clipRect = SkPath::Rect(atlasBounds.asSkRect());
scene->pushClipLayer(clipRect, Transform::Identity());
Transform atlasTransform = transform.postTranslate(atlasBounds.x(), atlasBounds.y());
SkPath devicePath = shape.asPath();
SkStrokeRec::Style styleType = style.getStyle();
if (styleType == SkStrokeRec::kStroke_Style ||
styleType == SkStrokeRec::kHairline_Style ||
styleType == SkStrokeRec::kStrokeAndFill_Style) {
float width = style.getWidth();
float deviceWidth = width * atlasTransform.maxScaleFactor();
if (style.isHairlineStyle() || deviceWidth <= 1.0) {
SkColor4f color = SkColors::kRed;
color.fR *= style.isHairlineStyle() ? 1.0 : deviceWidth;
float transformedWidth = 1.0f / atlasTransform.maxScaleFactor();
SkStrokeRec adjustedStyle(style);
adjustedStyle.setStrokeStyle(transformedWidth);
scene->solidStroke(devicePath, color, adjustedStyle, atlasTransform);
} else {
scene->solidStroke(devicePath, SkColors::kRed, style, atlasTransform);
}
}
if (styleType == SkStrokeRec::kFill_Style || styleType == SkStrokeRec::kStrokeAndFill_Style) {
scene->solidFill(devicePath, SkColors::kRed, shape.fillType(), atlasTransform);
}
scene->popClipLayer();
}
bool VelloComputePathAtlas::recordDispatches(Recorder* recorder,
ComputeTask::DispatchGroupList* dispatches) const {
bool addedDispatches = fCachedAtlasMgr.recordDispatches(recorder, dispatches);
if (this->texture() && !fUncachedOccupiedArea.isEmpty()) {
SkASSERT(recorder && recorder == fRecorder);
VelloAaConfig config = get_vello_aa_config(recorder);
std::unique_ptr<DispatchGroup> dispatchGroup =
render_vello_scene(recorder,
sk_ref_sp(this->texture()),
fUncachedScene,
fUncachedOccupiedArea,
config);
if (dispatchGroup) {
TRACE_EVENT_INSTANT1("skia.gpu", TRACE_FUNC, TRACE_EVENT_SCOPE_THREAD,
"# dispatches", dispatchGroup->dispatches().size());
dispatches->emplace_back(std::move(dispatchGroup));
return true;
} else {
SKGPU_LOG_E("VelloComputePathAtlas:: Failed to create dispatch group.");
}
}
return addedDispatches;
}
const TextureProxy* VelloComputePathAtlas::onAddShape(
const Shape& shape,
const Transform& transform,
const SkStrokeRec& style,
skvx::half2 maskSize,
skvx::half2* outPos) {
skgpu::UniqueKey maskKey;
bool hasKey = shape.hasKey();
if (hasKey) {
const TextureProxy* proxy = fCachedAtlasMgr.findOrCreateEntry(fRecorder,
shape,
transform,
style,
maskSize,
outPos);
if (proxy) {
return proxy;
}
}
SkIPoint16 iPos;
const TextureProxy* texProxy = this->addRect(maskSize, &iPos);
if (!texProxy) {
return nullptr;
}
*outPos = skvx::half2(iPos.x(), iPos.y());
if (!all(maskSize)) {
return texProxy;
}
SkASSERT(transform.type() != Transform::Type::kPerspective);
Rect atlasBounds = Rect::XYWH(skvx::float2(iPos.x(), iPos.y()), skvx::cast<float>(maskSize));
add_shape_to_scene(shape, transform, style, atlasBounds,
&fUncachedScene, &fUncachedOccupiedArea);
return texProxy;
}
bool VelloComputePathAtlas::VelloAtlasMgr::onAddToAtlas(const Shape& shape,
const Transform& transform,
const SkStrokeRec& style,
SkIRect shapeBounds,
const AtlasLocator& locator) {
uint32_t index = locator.pageIndex();
const TextureProxy* texProxy = fDrawAtlas->getProxies()[index].get();
if (!texProxy) {
return false;
}
SkASSERT(transform.type() != Transform::Type::kPerspective);
SkIPoint iPos = locator.topLeft();
Rect atlasBounds = Rect::XYWH(skvx::float2(iPos.x() + kEntryPadding, iPos.y() + kEntryPadding),
skvx::float2(shapeBounds.width(), shapeBounds.height()));
add_shape_to_scene(shape, transform, style, atlasBounds,
&fScenes[index], &fOccupiedAreas[index]);
return true;
}
bool VelloComputePathAtlas::VelloAtlasMgr::recordDispatches(
Recorder* recorder, ComputeTask::DispatchGroupList* dispatches) const {
SkASSERT(recorder);
VelloAaConfig config = get_vello_aa_config(recorder);
bool addedDispatches = false;
for (int i = 0; i < 4; ++i) {
if (!fOccupiedAreas[i].isEmpty()) {
std::unique_ptr<DispatchGroup> dispatchGroup =
render_vello_scene(recorder,
fDrawAtlas->getProxies()[i],
fScenes[i],
fOccupiedAreas[i],
config);
if (dispatchGroup) {
TRACE_EVENT_INSTANT1("skia.gpu", TRACE_FUNC, TRACE_EVENT_SCOPE_THREAD,
"# dispatches", dispatchGroup->dispatches().size());
dispatches->emplace_back(std::move(dispatchGroup));
addedDispatches = true;
} else {
SKGPU_LOG_E("VelloComputePathAtlas:: Failed to create dispatch group.");
}
}
}
return addedDispatches;
}
#endif
std::unique_ptr<ComputePathAtlas> ComputePathAtlas::CreateDefault(Recorder* recorder) { … }
}