• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "tools/GpuToolUtils.h"
9 
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkImage.h"
12 
13 #if defined(SK_GANESH)
14 #include "include/gpu/GrDirectContext.h"
15 #include "include/gpu/GrRecordingContext.h"
16 #include "include/gpu/ganesh/SkImageGanesh.h"
17 #include "src/gpu/ganesh/GrCaps.h"
18 #include "src/gpu/ganesh/GrDirectContextPriv.h"
19 #endif
20 
21 #if defined(SK_GRAPHITE)
22 #include "include/core/SkTiledImageUtils.h"
23 #include "include/gpu/graphite/Image.h"
24 #include "include/gpu/graphite/ImageProvider.h"
25 #include "include/gpu/graphite/Recorder.h"
26 #include "src/core/SkLRUCache.h"
27 #endif
28 
29 
30 namespace ToolUtils {
31 
MakeTextureImage(SkCanvas * canvas,sk_sp<SkImage> orig)32 sk_sp<SkImage> MakeTextureImage(SkCanvas* canvas, sk_sp<SkImage> orig) {
33     if (!orig) {
34         return nullptr;
35     }
36 
37 #if defined(SK_GANESH)
38     if (canvas->recordingContext() && canvas->recordingContext()->asDirectContext()) {
39         GrDirectContext* dContext = canvas->recordingContext()->asDirectContext();
40         const GrCaps* caps = dContext->priv().caps();
41 
42         if (orig->width() >= caps->maxTextureSize() || orig->height() >= caps->maxTextureSize()) {
43             // Ganesh is able to tile large SkImage draws. Always forcing SkImages to be uploaded
44             // prevents this feature from being tested by our tools. For now, leave excessively
45             // large SkImages as bitmaps.
46             return orig;
47         }
48 
49         return SkImages::TextureFromImage(dContext, orig);
50     }
51 #endif
52 #if defined(SK_GRAPHITE)
53     if (canvas->recorder()) {
54         return SkImages::TextureFromImage(canvas->recorder(), orig, {false});
55     }
56 #endif
57     return orig;
58 }
59 
60 #if defined(SK_GRAPHITE)
61 
62 // Currently, we give each new Recorder its own ImageProvider. This means we don't have to deal
63 // w/ any threading issues.
64 // TODO: We should probably have this class generate and report some cache stats
65 // TODO: Hook up to listener system?
66 // TODO: add testing of a single ImageProvider passed to multiple recorders
67 class TestingImageProvider : public skgpu::graphite::ImageProvider {
68 public:
TestingImageProvider()69     TestingImageProvider() : fCache(kDefaultNumCachedImages) {}
~TestingImageProvider()70     ~TestingImageProvider() override {}
71 
findOrCreate(skgpu::graphite::Recorder * recorder,const SkImage * image,SkImage::RequiredProperties requiredProps)72     sk_sp<SkImage> findOrCreate(skgpu::graphite::Recorder* recorder,
73                                 const SkImage* image,
74                                 SkImage::RequiredProperties requiredProps) override {
75         if (!requiredProps.fMipmapped) {
76             // If no mipmaps are required, check to see if we have a mipmapped version anyway -
77             // since it can be used in that case.
78             // TODO: we could get fancy and, if ever a mipmapped key eclipsed a non-mipmapped
79             // key, we could remove the hidden non-mipmapped key/image from the cache.
80             ImageKey mipMappedKey(image, /* mipmapped= */ true);
81             auto result = fCache.find(mipMappedKey);
82             if (result) {
83                 return *result;
84             }
85         }
86 
87         ImageKey key(image, requiredProps.fMipmapped);
88 
89         auto result = fCache.find(key);
90         if (result) {
91             return *result;
92         }
93 
94         sk_sp<SkImage> newImage = SkImages::TextureFromImage(recorder, image, requiredProps);
95         if (!newImage) {
96             return nullptr;
97         }
98 
99         result = fCache.insert(key, std::move(newImage));
100         SkASSERT(result);
101 
102         return *result;
103     }
104 
105 private:
106     static constexpr int kDefaultNumCachedImages = 256;
107 
108     class ImageKey {
109     public:
ImageKey(const SkImage * image,bool mipmapped)110         ImageKey(const SkImage* image, bool mipmapped) {
111             uint32_t flags = mipmapped ? 0x1 : 0x0;
112             SkTiledImageUtils::GetImageKeyValues(image, &fValues[1]);
113             fValues[kNumValues-1] = flags;
114             fValues[0] = SkChecksum::Hash32(&fValues[1], (kNumValues-1) * sizeof(uint32_t));
115         }
116 
hash() const117         uint32_t hash() const { return fValues[0]; }
118 
operator ==(const ImageKey & other) const119         bool operator==(const ImageKey& other) const {
120             for (int i = 0; i < kNumValues; ++i) {
121                 if (fValues[i] != other.fValues[i]) {
122                     return false;
123                 }
124             }
125 
126             return true;
127         }
operator !=(const ImageKey & other) const128         bool operator!=(const ImageKey& other) const { return !(*this == other); }
129 
130     private:
131         static const int kNumValues = SkTiledImageUtils::kNumImageKeyValues + 2;
132 
133         uint32_t fValues[kNumValues];
134     };
135 
136     struct ImageHash {
operator ()ToolUtils::TestingImageProvider::ImageHash137         size_t operator()(const ImageKey& key) const { return key.hash(); }
138     };
139 
140     SkLRUCache<ImageKey, sk_sp<SkImage>, ImageHash> fCache;
141 };
142 
CreateTestingRecorderOptions()143 skgpu::graphite::RecorderOptions CreateTestingRecorderOptions() {
144     skgpu::graphite::RecorderOptions options;
145 
146     options.fImageProvider.reset(new TestingImageProvider);
147 
148     return options;
149 }
150 
151 #endif // SK_GRAPHITE
152 
153 }  // namespace ToolUtils
154