441 lines
19 KiB
C++
441 lines
19 KiB
C++
/*
|
|
* Copyright 2021 Google LLC
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#ifndef skgpu_graphite_Context_DEFINED
|
|
#define skgpu_graphite_Context_DEFINED
|
|
|
|
#include "include/core/SkImage.h"
|
|
#include "include/core/SkRefCnt.h"
|
|
#include "include/core/SkSize.h"
|
|
#include "include/core/SkSpan.h"
|
|
#include "include/core/SkTypes.h"
|
|
#include "include/gpu/graphite/GraphiteTypes.h"
|
|
#include "include/gpu/graphite/Recorder.h" // IWYU pragma: keep
|
|
#include "include/private/base/SingleOwner.h"
|
|
#include "include/private/base/SkThreadAnnotations.h"
|
|
|
|
#if defined(GPU_TEST_UTILS)
|
|
#include "include/private/base/SkMutex.h"
|
|
#endif
|
|
|
|
#include <chrono>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
class SkColorInfo;
|
|
class SkSurface;
|
|
class SkCapture;
|
|
enum SkYUVColorSpace : int;
|
|
class SkColorSpace;
|
|
class SkTraceMemoryDump;
|
|
struct SkIRect;
|
|
struct SkImageInfo;
|
|
|
|
namespace skcpu {
|
|
class ContextImpl;
|
|
class Recorder;
|
|
} // namespace skcpu
|
|
|
|
namespace skgpu {
|
|
enum class BackendApi : unsigned int;
|
|
enum class GpuStatsFlags : uint32_t;
|
|
}
|
|
|
|
namespace skgpu::graphite {
|
|
|
|
class BackendTexture;
|
|
class Buffer;
|
|
class ClientMappedBufferManager;
|
|
class ContextPriv;
|
|
struct ContextOptions;
|
|
class PersistentPipelineStorage;
|
|
class PrecompileContext;
|
|
class QueueManager;
|
|
class ResourceProvider;
|
|
class SharedContext;
|
|
class TextureProxy;
|
|
class TextureProxyView;
|
|
|
|
class SK_API Context final {
|
|
public:
|
|
Context(const Context&) = delete;
|
|
Context(Context&&) = delete;
|
|
Context& operator=(const Context&) = delete;
|
|
Context& operator=(Context&&) = delete;
|
|
|
|
~Context();
|
|
|
|
BackendApi backend() const;
|
|
|
|
std::unique_ptr<Recorder> makeRecorder(const RecorderOptions& = {});
|
|
std::unique_ptr<skcpu::Recorder> makeCPURecorder();
|
|
|
|
/** Creates a helper object that can be moved to a different thread and used
|
|
* for precompilation.
|
|
*/
|
|
std::unique_ptr<PrecompileContext> makePrecompileContext();
|
|
|
|
InsertStatus insertRecording(const InsertRecordingInfo&);
|
|
bool submit(SubmitInfo submitInfo = {});
|
|
|
|
/** Returns true if there is work that was submitted to the GPU that has not finished. */
|
|
bool hasUnfinishedGpuWork() const;
|
|
|
|
/** Returns true if there is pending GPU work that needs to be submitted. */
|
|
bool hasPendingGPUWork() const;
|
|
|
|
/** Makes image pixel data available to caller, possibly asynchronously. It can also rescale
|
|
the image pixels.
|
|
|
|
Data is read from the source sub-rectangle, is optionally converted to a linear gamma, is
|
|
rescaled to the size indicated by 'dstImageInfo', is then converted to the color space,
|
|
color type, and alpha type of 'dstImageInfo'. A 'srcRect' that is not contained by the
|
|
bounds of the image causes failure.
|
|
|
|
When the pixel data is ready the caller's ReadPixelsCallback is called with a
|
|
AsyncReadResult containing pixel data in the requested color type, alpha type, and color
|
|
space. The AsyncReadResult will have count() == 1. Upon failure the callback is called with
|
|
nullptr for AsyncReadResult. The callback can be triggered, for example, with a call to
|
|
Context::submit(SyncToCpu::kYes).
|
|
|
|
The data is valid for the lifetime of AsyncReadResult with the exception that the data is
|
|
immediately invalidated if the Graphite context is abandoned or destroyed.
|
|
|
|
@param src Graphite-backed image or surface to read the data from.
|
|
@param dstImageInfo info of the requested pixels
|
|
@param srcRect subrectangle of image to read
|
|
@param rescaleGamma controls whether rescaling is done in the image's gamma or whether
|
|
the source data is transformed to a linear gamma before rescaling.
|
|
@param rescaleMode controls the technique (and cost) of the rescaling
|
|
@param callback function to call with result of the read
|
|
@param context passed to callback
|
|
*/
|
|
void asyncRescaleAndReadPixels(const SkImage* src,
|
|
const SkImageInfo& dstImageInfo,
|
|
const SkIRect& srcRect,
|
|
SkImage::RescaleGamma rescaleGamma,
|
|
SkImage::RescaleMode rescaleMode,
|
|
SkImage::ReadPixelsCallback callback,
|
|
SkImage::ReadPixelsContext context);
|
|
void asyncRescaleAndReadPixels(const SkSurface* src,
|
|
const SkImageInfo& dstImageInfo,
|
|
const SkIRect& srcRect,
|
|
SkImage::RescaleGamma rescaleGamma,
|
|
SkImage::RescaleMode rescaleMode,
|
|
SkImage::ReadPixelsCallback callback,
|
|
SkImage::ReadPixelsContext context);
|
|
|
|
/**
|
|
Similar to asyncRescaleAndReadPixels but performs an additional conversion to YUV. The
|
|
RGB->YUV conversion is controlled by 'yuvColorSpace'. The YUV data is returned as three
|
|
planes ordered y, u, v. The u and v planes are half the width and height of the resized
|
|
rectangle. The y, u, and v values are single bytes. Currently this fails if 'dstSize'
|
|
width and height are not even. A 'srcRect' that is not contained by the bounds of the
|
|
surface causes failure.
|
|
|
|
When the pixel data is ready the caller's ReadPixelsCallback is called with a
|
|
AsyncReadResult containing the planar data. The AsyncReadResult will have count() == 3.
|
|
Upon failure the callback is called with nullptr for AsyncReadResult. The callback can
|
|
be triggered, for example, with a call to Context::submit(SyncToCpu::kYes).
|
|
|
|
The data is valid for the lifetime of AsyncReadResult with the exception that the data
|
|
is immediately invalidated if the context is abandoned or destroyed.
|
|
|
|
@param src Graphite-backed image or surface to read the data from.
|
|
@param yuvColorSpace The transformation from RGB to YUV. Applied to the resized image
|
|
after it is converted to dstColorSpace.
|
|
@param dstColorSpace The color space to convert the resized image to, after rescaling.
|
|
@param srcRect The portion of the surface to rescale and convert to YUV planes.
|
|
@param dstSize The size to rescale srcRect to
|
|
@param rescaleGamma controls whether rescaling is done in the surface's gamma or whether
|
|
the source data is transformed to a linear gamma before rescaling.
|
|
@param rescaleMode controls the sampling technique of the rescaling
|
|
@param callback function to call with the planar read result
|
|
@param context passed to callback
|
|
*/
|
|
void asyncRescaleAndReadPixelsYUV420(const SkImage* src,
|
|
SkYUVColorSpace yuvColorSpace,
|
|
sk_sp<SkColorSpace> dstColorSpace,
|
|
const SkIRect& srcRect,
|
|
const SkISize& dstSize,
|
|
SkImage::RescaleGamma rescaleGamma,
|
|
SkImage::RescaleMode rescaleMode,
|
|
SkImage::ReadPixelsCallback callback,
|
|
SkImage::ReadPixelsContext context);
|
|
void asyncRescaleAndReadPixelsYUV420(const SkSurface* src,
|
|
SkYUVColorSpace yuvColorSpace,
|
|
sk_sp<SkColorSpace> dstColorSpace,
|
|
const SkIRect& srcRect,
|
|
const SkISize& dstSize,
|
|
SkImage::RescaleGamma rescaleGamma,
|
|
SkImage::RescaleMode rescaleMode,
|
|
SkImage::ReadPixelsCallback callback,
|
|
SkImage::ReadPixelsContext context);
|
|
|
|
/**
|
|
* Identical to asyncRescaleAndReadPixelsYUV420 but a fourth plane is returned in the
|
|
* AsyncReadResult passed to 'callback'. The fourth plane contains the alpha chanel at the
|
|
* same full resolution as the Y plane.
|
|
*/
|
|
void asyncRescaleAndReadPixelsYUVA420(const SkImage* src,
|
|
SkYUVColorSpace yuvColorSpace,
|
|
sk_sp<SkColorSpace> dstColorSpace,
|
|
const SkIRect& srcRect,
|
|
const SkISize& dstSize,
|
|
SkImage::RescaleGamma rescaleGamma,
|
|
SkImage::RescaleMode rescaleMode,
|
|
SkImage::ReadPixelsCallback callback,
|
|
SkImage::ReadPixelsContext context);
|
|
void asyncRescaleAndReadPixelsYUVA420(const SkSurface* src,
|
|
SkYUVColorSpace yuvColorSpace,
|
|
sk_sp<SkColorSpace> dstColorSpace,
|
|
const SkIRect& srcRect,
|
|
const SkISize& dstSize,
|
|
SkImage::RescaleGamma rescaleGamma,
|
|
SkImage::RescaleMode rescaleMode,
|
|
SkImage::ReadPixelsCallback callback,
|
|
SkImage::ReadPixelsContext context);
|
|
|
|
/**
|
|
* Checks whether any asynchronous work is complete and if so calls related callbacks.
|
|
*/
|
|
void checkAsyncWorkCompletion();
|
|
|
|
/**
|
|
* Called to delete the passed in BackendTexture. This should only be called if the
|
|
* BackendTexture was created by calling Recorder::createBackendTexture on a Recorder created
|
|
* from this Context. If the BackendTexture is not valid or does not match the BackendApi of the
|
|
* Context then nothing happens.
|
|
*
|
|
* Otherwise this will delete/release the backend object that is wrapped in the BackendTexture.
|
|
* The BackendTexture will be reset to an invalid state and should not be used again.
|
|
*/
|
|
void deleteBackendTexture(const BackendTexture&);
|
|
|
|
/**
|
|
* Frees GPU resources created and held by the Context. Can be called to reduce GPU memory
|
|
* pressure. Any resources that are still in use (e.g. being used by work submitted to the GPU)
|
|
* will not be deleted by this call. If the caller wants to make sure all resources are freed,
|
|
* then they should first make sure to submit and wait on any outstanding work.
|
|
*/
|
|
void freeGpuResources();
|
|
|
|
/**
|
|
* Purge GPU resources on the Context that haven't been used in the past 'msNotUsed'
|
|
* milliseconds or are otherwise marked for deletion, regardless of whether the context is under
|
|
* budget. Optionally provide a `microsMaxPurgingDur` after which Skia should stop purging
|
|
* resources.
|
|
*/
|
|
void performDeferredCleanup(
|
|
std::chrono::milliseconds msNotUsed,
|
|
std::optional<std::chrono::microseconds> microsMaxPurgingDur = std::nullopt);
|
|
|
|
/**
|
|
* Returns the number of bytes of the Context's gpu memory cache budget that are currently in
|
|
* use.
|
|
*/
|
|
size_t currentBudgetedBytes() const;
|
|
|
|
/**
|
|
* Returns the number of bytes of the Context's resource cache that are currently purgeable.
|
|
*/
|
|
size_t currentPurgeableBytes() const;
|
|
|
|
/**
|
|
* Returns the size of Context's gpu memory cache budget in bytes.
|
|
*/
|
|
size_t maxBudgetedBytes() const;
|
|
|
|
/**
|
|
* Sets the size of Context's gpu memory cache budget in bytes. If the new budget is lower than
|
|
* the current budget, the cache will try to free resources to get under the new budget.
|
|
*/
|
|
void setMaxBudgetedBytes(size_t bytes);
|
|
|
|
/**
|
|
* Enumerates all cached GPU resources owned by the Context and dumps their memory to
|
|
* traceMemoryDump.
|
|
*/
|
|
void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const;
|
|
|
|
/**
|
|
* Returns true if the backend-specific context has gotten into an unrecoverarble, lost state
|
|
* (e.g. if we've gotten a VK_ERROR_DEVICE_LOST in the Vulkan backend).
|
|
*/
|
|
bool isDeviceLost() const;
|
|
|
|
/**
|
|
* Returns the maximum texture dimension supported by the underlying backend.
|
|
*/
|
|
int maxTextureSize() const;
|
|
|
|
/*
|
|
* Does this context support protected content?
|
|
*/
|
|
bool supportsProtectedContent() const;
|
|
|
|
/*
|
|
* Gets the types of GPU stats supported by this Context.
|
|
*/
|
|
GpuStatsFlags supportedGpuStats() const;
|
|
|
|
/**
|
|
* If supported by the backend, stores the current pipeline cache data into the
|
|
* PersistentPipelineStorage-derived object passed into Graphite via
|
|
* ContextOptions::fPersistentPipelineStorage. The amount stored is limited to 'maxSize'.
|
|
*
|
|
* Skia attempts to only call store() on the PersistentPipelineStorage object when the data
|
|
* is likely to be different from what was last sync'ed.
|
|
*/
|
|
void syncPipelineData(size_t maxSize = SIZE_MAX);
|
|
|
|
/*
|
|
* TODO (b/412351769): Do not use startCapture() or endCapture() as the feature is still under
|
|
* development.
|
|
*
|
|
* Starts the SkCapture. Must have set ContextOptions::fEnableCapture to start.
|
|
*/
|
|
void startCapture();
|
|
|
|
/*
|
|
* Ends the SkCapture and returns the collected draws and surface creation.
|
|
*/
|
|
sk_sp<SkCapture> endCapture();
|
|
|
|
// Provides access to functions that aren't part of the public API.
|
|
ContextPriv priv();
|
|
const ContextPriv priv() const; // NOLINT(readability-const-return-type)
|
|
|
|
class ContextID {
|
|
public:
|
|
static Context::ContextID Next();
|
|
|
|
ContextID() : fID(SK_InvalidUniqueID) {}
|
|
|
|
bool operator==(const ContextID& that) const { return fID == that.fID; }
|
|
bool operator!=(const ContextID& that) const { return !(*this == that); }
|
|
|
|
void makeInvalid() { fID = SK_InvalidUniqueID; }
|
|
bool isValid() const { return fID != SK_InvalidUniqueID; }
|
|
|
|
private:
|
|
constexpr ContextID(uint32_t id) : fID(id) {}
|
|
uint32_t fID;
|
|
};
|
|
|
|
ContextID contextID() const { return fContextID; }
|
|
|
|
protected:
|
|
Context(sk_sp<SharedContext>, std::unique_ptr<QueueManager>, const ContextOptions&);
|
|
|
|
private:
|
|
friend class ContextPriv;
|
|
friend class ContextCtorAccessor;
|
|
|
|
struct PixelTransferResult {
|
|
PixelTransferResult();
|
|
PixelTransferResult(const PixelTransferResult&);
|
|
PixelTransferResult(PixelTransferResult&&);
|
|
PixelTransferResult& operator=(const PixelTransferResult&);
|
|
~PixelTransferResult();
|
|
|
|
using ConversionFn = void(void* dst, const void* mappedBuffer);
|
|
// If null then the transfer could not be performed. Otherwise this buffer will contain
|
|
// the pixel data when the transfer is complete.
|
|
sk_sp<Buffer> fTransferBuffer;
|
|
// Size of the read.
|
|
SkISize fSize;
|
|
// RowBytes for transfer buffer data
|
|
size_t fRowBytes;
|
|
// If this is null then the transfer buffer will contain the data in the requested
|
|
// color type. Otherwise, when the transfer is done this must be called to convert
|
|
// from the transfer buffer's color type to the requested color type.
|
|
std::function<ConversionFn> fPixelConverter;
|
|
};
|
|
|
|
SingleOwner* singleOwner() const { return &fSingleOwner; }
|
|
|
|
// Must be called in Make() to handle one-time GPU setup operations that can possibly fail and
|
|
// require Context::Make() to return a nullptr.
|
|
bool finishInitialization();
|
|
|
|
void checkForFinishedWork(SyncToCpu);
|
|
|
|
std::unique_ptr<Recorder> makeInternalRecorder() const;
|
|
|
|
template <typename SrcPixels> struct AsyncParams;
|
|
|
|
template <typename ReadFn, typename... ExtraArgs>
|
|
void asyncRescaleAndReadImpl(ReadFn Context::* asyncRead,
|
|
SkImage::RescaleGamma rescaleGamma,
|
|
SkImage::RescaleMode rescaleMode,
|
|
const AsyncParams<SkImage>&,
|
|
ExtraArgs...);
|
|
|
|
// Recorder is optional and will be used if drawing operations are required. If no Recorder is
|
|
// provided but drawing operations are needed, a new Recorder will be created automatically.
|
|
void asyncReadPixels(std::unique_ptr<Recorder>, const AsyncParams<SkImage>&);
|
|
void asyncReadPixelsYUV420(std::unique_ptr<Recorder>,
|
|
const AsyncParams<SkImage>&,
|
|
SkYUVColorSpace);
|
|
|
|
// Like asyncReadPixels() except it performs no fallbacks, and requires that the texture be
|
|
// readable. However, the texture does not need to be sampleable.
|
|
void asyncReadTexture(std::unique_ptr<Recorder>,
|
|
const AsyncParams<TextureProxyView>&,
|
|
const SkColorInfo& srcColorInfo);
|
|
|
|
// Inserts a texture to buffer transfer task, used by asyncReadPixels methods. If the
|
|
// Recorder is non-null, tasks will be added to the Recorder's list; otherwise the transfer
|
|
// tasks will be added to the queue manager directly.
|
|
PixelTransferResult transferPixels(Recorder*,
|
|
const TextureProxyView& srcView,
|
|
const SkColorInfo& srcColorInfo,
|
|
const SkColorInfo& dstColorInfo,
|
|
const SkIRect& srcRect);
|
|
|
|
// If the recorder is non-null, it will be snapped and inserted with the assumption that the
|
|
// copy tasks (and possibly preparatory draw tasks) have already been added to the Recording.
|
|
void finalizeAsyncReadPixels(std::unique_ptr<Recorder>,
|
|
SkSpan<PixelTransferResult>,
|
|
SkImage::ReadPixelsCallback callback,
|
|
SkImage::ReadPixelsContext callbackContext);
|
|
|
|
sk_sp<SharedContext> fSharedContext;
|
|
std::unique_ptr<ResourceProvider> fResourceProvider;
|
|
std::unique_ptr<ClientMappedBufferManager> fMappedBufferManager;
|
|
std::unique_ptr<QueueManager> fQueueManager;
|
|
std::unique_ptr<const skcpu::ContextImpl> fCPUContext;
|
|
|
|
PersistentPipelineStorage* fPersistentPipelineStorage;
|
|
|
|
// In debug builds we guard against improper thread handling. This guard is passed to the
|
|
// ResourceCache for the Context.
|
|
mutable SingleOwner fSingleOwner;
|
|
|
|
#if defined(GPU_TEST_UTILS)
|
|
void deregisterRecorder(const Recorder*) SK_EXCLUDES(fTestingLock);
|
|
|
|
// In test builds a Recorder may track the Context that was used to create it.
|
|
bool fStoreContextRefInRecorder = false;
|
|
// If this tracking is on, to allow the client to safely delete this Context or its Recorders
|
|
// in any order we must also track the Recorders created here.
|
|
SkMutex fTestingLock;
|
|
std::vector<Recorder*> fTrackedRecorders SK_GUARDED_BY(fTestingLock);
|
|
#endif
|
|
|
|
// Needed for MessageBox handling
|
|
const ContextID fContextID;
|
|
};
|
|
|
|
} // namespace skgpu::graphite
|
|
|
|
#endif // skgpu_graphite_Context_DEFINED
|