1 /*
2 * Copyright 2024 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/graphite/GraphiteToolUtils.h"
9
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkImage.h"
12 #include "include/core/SkTiledImageUtils.h"
13 #include "include/gpu/graphite/Image.h"
14 #include "include/gpu/graphite/ImageProvider.h"
15 #include "include/gpu/graphite/Recorder.h"
16 #include "src/core/SkLRUCache.h"
17
18 namespace {
19 // Currently, we give each new Recorder its own ImageProvider. This means we don't have to deal
20 // w/ any threading issues.
21 // TODO: We should probably have this class generate and report some cache stats
22 // TODO: Hook up to listener system?
23 // TODO: add testing of a single ImageProvider passed to multiple recorders
24 class TestingImageProvider : public skgpu::graphite::ImageProvider {
25 public:
TestingImageProvider()26 TestingImageProvider() : fCache(kDefaultNumCachedImages) {}
~TestingImageProvider()27 ~TestingImageProvider() override {}
28
findOrCreate(skgpu::graphite::Recorder * recorder,const SkImage * image,SkImage::RequiredProperties requiredProps)29 sk_sp<SkImage> findOrCreate(skgpu::graphite::Recorder* recorder,
30 const SkImage* image,
31 SkImage::RequiredProperties requiredProps) override {
32 if (!requiredProps.fMipmapped) {
33 // If no mipmaps are required, check to see if we have a mipmapped version anyway -
34 // since it can be used in that case.
35 // TODO: we could get fancy and, if ever a mipmapped key eclipsed a non-mipmapped
36 // key, we could remove the hidden non-mipmapped key/image from the cache.
37 ImageKey mipMappedKey(image, /* mipmapped= */ true);
38 auto result = fCache.find(mipMappedKey);
39 if (result) {
40 return *result;
41 }
42 }
43
44 ImageKey key(image, requiredProps.fMipmapped);
45
46 auto result = fCache.find(key);
47 if (result) {
48 return *result;
49 }
50
51 sk_sp<SkImage> newImage = SkImages::TextureFromImage(recorder, image, requiredProps);
52 if (!newImage) {
53 return nullptr;
54 }
55
56 result = fCache.insert(key, std::move(newImage));
57 SkASSERT(result);
58
59 return *result;
60 }
61
62 private:
63 static constexpr int kDefaultNumCachedImages = 256;
64
65 class ImageKey {
66 public:
ImageKey(const SkImage * image,bool mipmapped)67 ImageKey(const SkImage* image, bool mipmapped) {
68 uint32_t flags = mipmapped ? 0x1 : 0x0;
69 SkTiledImageUtils::GetImageKeyValues(image, &fValues[1]);
70 fValues[kNumValues - 1] = flags;
71 fValues[0] = SkChecksum::Hash32(&fValues[1], (kNumValues - 1) * sizeof(uint32_t));
72 }
73
hash() const74 uint32_t hash() const { return fValues[0]; }
75
operator ==(const ImageKey & other) const76 bool operator==(const ImageKey& other) const {
77 for (int i = 0; i < kNumValues; ++i) {
78 if (fValues[i] != other.fValues[i]) {
79 return false;
80 }
81 }
82
83 return true;
84 }
operator !=(const ImageKey & other) const85 bool operator!=(const ImageKey& other) const { return !(*this == other); }
86
87 private:
88 static const int kNumValues = SkTiledImageUtils::kNumImageKeyValues + 2;
89
90 uint32_t fValues[kNumValues];
91 };
92
93 struct ImageHash {
operator ()__anon2a4697350111::TestingImageProvider::ImageHash94 size_t operator()(const ImageKey& key) const { return key.hash(); }
95 };
96
97 SkLRUCache<ImageKey, sk_sp<SkImage>, ImageHash> fCache;
98 };
99
100 } // anonymous namespace
101
102 namespace ToolUtils {
103
CreateTestingRecorderOptions()104 skgpu::graphite::RecorderOptions CreateTestingRecorderOptions() {
105 skgpu::graphite::RecorderOptions options;
106
107 options.fImageProvider.reset(new TestingImageProvider);
108
109 return options;
110 }
111
112 } // namespace ToolUtils
113