blob: 693f96567d0361cd7eb0cf2e318bdb15dc50a1c7 [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 "src/gpu/graphite/Image_Graphite.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkImage.h"
#include "include/core/SkSurface.h"
#include "include/gpu/graphite/Image.h"
#include "include/gpu/graphite/Recorder.h"
#include "include/gpu/graphite/Surface.h"
#include "src/gpu/SkBackingFit.h"
#include "src/gpu/graphite/Caps.h"
#include "src/gpu/graphite/Device.h"
#include "src/gpu/graphite/Log.h"
#include "src/gpu/graphite/RecorderPriv.h"
#include "src/gpu/graphite/ResourceProvider.h"
#include "src/gpu/graphite/Texture.h"
#include "src/gpu/graphite/TextureUtils.h"
#include "src/gpu/graphite/task/CopyTask.h"
#if defined(GRAPHITE_TEST_UTILS)
#include "include/gpu/graphite/Context.h"
#include "src/gpu/graphite/ContextPriv.h"
#endif
namespace skgpu::graphite {
// Graphite does not cache based on the image's unique ID so always request a new one.
Image::Image(TextureProxyView view,
const SkColorInfo& info)
: Image_Base(SkImageInfo::Make(view.proxy()->dimensions(), info), kNeedNewImageUniqueID)
, fTextureProxyView(std::move(view)) {}
Image::~Image() = default;
sk_sp<Image> Image::WrapDevice(sk_sp<Device> device) {
TextureProxyView proxy = device->readSurfaceView();
if (!proxy) {
return nullptr;
}
// NOTE: If the device was created with an approx backing fit, its SkImageInfo reports the
// logical dimensions, but its proxy has the approximate fit. These larger dimensions are
// propagated to the SkImageInfo of this image view.
sk_sp<Image> image = sk_make_sp<Image>(std::move(proxy),
device->imageInfo().colorInfo());
image->linkDevice(std::move(device));
return image;
}
sk_sp<Image> Image::Copy(Recorder* recorder,
const TextureProxyView& srcView,
const SkColorInfo& srcColorInfo,
const SkIRect& subset,
Budgeted budgeted,
Mipmapped mipmapped,
SkBackingFit backingFit,
std::string_view label) {
SkASSERT(!(mipmapped == Mipmapped::kYes && backingFit == SkBackingFit::kApprox));
if (!srcView) {
return nullptr;
}
SkASSERT(srcView.proxy()->isFullyLazy() ||
SkIRect::MakeSize(srcView.proxy()->dimensions()).contains(subset));
if (!recorder->priv().caps()->supportsReadPixels(srcView.proxy()->textureInfo())) {
if (!recorder->priv().caps()->isTexturable(srcView.proxy()->textureInfo())) {
// The texture is not blittable nor texturable so copying cannot be done.
return nullptr;
}
// Copy-as-draw
sk_sp<Image> srcImage(new Image(srcView, srcColorInfo));
return CopyAsDraw(recorder, srcImage.get(), subset, srcColorInfo,
budgeted, mipmapped, backingFit, std::move(label));
}
skgpu::graphite::TextureInfo textureInfo =
recorder->priv().caps()->getTextureInfoForSampledCopy(srcView.proxy()->textureInfo(),
mipmapped);
sk_sp<TextureProxy> dst = TextureProxy::Make(
recorder->priv().caps(),
recorder->priv().resourceProvider(),
backingFit == SkBackingFit::kApprox ? GetApproxSize(subset.size()) : subset.size(),
textureInfo,
std::move(label),
budgeted);
if (!dst) {
return nullptr;
}
auto copyTask = CopyTextureToTextureTask::Make(srcView.refProxy(), subset, dst, {0, 0});
if (!copyTask) {
return nullptr;
}
recorder->priv().add(std::move(copyTask));
if (mipmapped == Mipmapped::kYes) {
if (!GenerateMipmaps(recorder, dst, srcColorInfo)) {
SKGPU_LOG_W("Image::Copy failed to generate mipmaps");
return nullptr;
}
}
return sk_sp<Image>(new Image({std::move(dst), srcView.swizzle()}, srcColorInfo));
}
size_t Image::textureSize() const {
if (!fTextureProxyView.proxy()) {
return 0;
}
if (!fTextureProxyView.proxy()->texture()) {
return fTextureProxyView.proxy()->uninstantiatedGpuMemorySize();
}
return fTextureProxyView.proxy()->texture()->gpuMemorySize();
}
sk_sp<Image> Image::copyImage(Recorder* recorder,
const SkIRect& subset,
Budgeted budgeted,
Mipmapped mipmapped,
SkBackingFit backingFit,
std::string_view label) const {
this->notifyInUse(recorder, /*drawContext=*/nullptr);
return Image::Copy(recorder, fTextureProxyView, this->imageInfo().colorInfo(),
subset, budgeted, mipmapped, backingFit, std::move(label));
}
sk_sp<SkImage> Image::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
sk_sp<Image> view = sk_make_sp<Image>(fTextureProxyView,
this->imageInfo().colorInfo()
.makeColorSpace(std::move(newCS)));
// The new Image object shares the same texture proxy, so it should also share linked Devices
view->linkDevices(this);
return view;
}
#if defined(GRAPHITE_TEST_UTILS)
bool Image::onReadPixelsGraphite(Recorder* recorder,
const SkPixmap& dst,
int srcX,
int srcY) const {
if (Context* context = recorder->priv().context()) {
// Add all previous commands generated to the command buffer.
// If the client snaps later they'll only get post-read commands in their Recording,
// but since they're doing a readPixels in the middle that shouldn't be unexpected.
std::unique_ptr<Recording> recording = recorder->snap();
if (!recording) {
return false;
}
InsertRecordingInfo info;
info.fRecording = recording.get();
if (!context->insertRecording(info)) {
return false;
}
return context->priv().readPixels(dst,
fTextureProxyView.proxy(),
this->imageInfo(),
srcX,
srcY);
}
return false;
}
#endif
} // namespace skgpu::graphite