xref: /aosp_15_r20/external/skia/src/gpu/ganesh/effects/GrTextureEffect.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2017 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #ifndef GrTextureEffect_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define GrTextureEffect_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSamplingOptions.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrCaps.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrFragmentProcessor.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrProcessorUnitTest.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrSamplerState.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrSurfaceProxyView.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrTextureProxy.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/glsl/GrGLSLShaderBuilder.h"
22*c8dee2aaSAndroid Build Coastguard Worker 
23*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
24*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
25*c8dee2aaSAndroid Build Coastguard Worker 
26*c8dee2aaSAndroid Build Coastguard Worker class GrGLSLProgramDataManager;
27*c8dee2aaSAndroid Build Coastguard Worker class GrTexture;
28*c8dee2aaSAndroid Build Coastguard Worker enum SkAlphaType : int;
29*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu { class KeyBuilder; }
30*c8dee2aaSAndroid Build Coastguard Worker struct GrShaderCaps;
31*c8dee2aaSAndroid Build Coastguard Worker 
32*c8dee2aaSAndroid Build Coastguard Worker class GrTextureEffect : public GrFragmentProcessor {
33*c8dee2aaSAndroid Build Coastguard Worker public:
34*c8dee2aaSAndroid Build Coastguard Worker     inline static constexpr float kDefaultBorder[4] = {0};
35*c8dee2aaSAndroid Build Coastguard Worker 
36*c8dee2aaSAndroid Build Coastguard Worker     // An extra inset amount to apply to subset clamp boundaries to ensure clamped coordinates will
37*c8dee2aaSAndroid Build Coastguard Worker     // access texel (i-1) to i instead of i to (i+1), even if the weights should mean the clamped
38*c8dee2aaSAndroid Build Coastguard Worker     // color is still equal to texel i's value.
39*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_USE_SAFE_INSET_FOR_TEXTURE_SAMPLING)
40*c8dee2aaSAndroid Build Coastguard Worker     inline static constexpr float kInsetEpsilon = 0.0001f;
41*c8dee2aaSAndroid Build Coastguard Worker #else
42*c8dee2aaSAndroid Build Coastguard Worker     // A value of 0 means that coordinates for linear filtering will be clamped to exactly 1/2px
43*c8dee2aaSAndroid Build Coastguard Worker     // inset from the defined data boundary (for approx-fit textures that don't fill to the HW
44*c8dee2aaSAndroid Build Coastguard Worker     // boundaries). For bottom/right edges this can then access the row or column outside of the
45*c8dee2aaSAndroid Build Coastguard Worker     // defined data. The filtering weight should be 0, which should be safe for non-floating-point
46*c8dee2aaSAndroid Build Coastguard Worker     // color types, and not have any impact on the clamped sample. In practice, devices with lower
47*c8dee2aaSAndroid Build Coastguard Worker     // precision texture coordinates may not actually evaluate with a weight of 0, and it is
48*c8dee2aaSAndroid Build Coastguard Worker     // caught by Swiftshader+MSAN.
49*c8dee2aaSAndroid Build Coastguard Worker     inline static constexpr float kInsetEpsilon = 0.f;
50*c8dee2aaSAndroid Build Coastguard Worker #endif
51*c8dee2aaSAndroid Build Coastguard Worker 
52*c8dee2aaSAndroid Build Coastguard Worker     inline static constexpr float kLinearInset = 0.5f + kInsetEpsilon;
53*c8dee2aaSAndroid Build Coastguard Worker 
54*c8dee2aaSAndroid Build Coastguard Worker     /** Make from a filter. The sampler will be configured with clamp mode. */
55*c8dee2aaSAndroid Build Coastguard Worker     static std::unique_ptr<GrFragmentProcessor> Make(
56*c8dee2aaSAndroid Build Coastguard Worker             GrSurfaceProxyView,
57*c8dee2aaSAndroid Build Coastguard Worker             SkAlphaType,
58*c8dee2aaSAndroid Build Coastguard Worker             const SkMatrix& = SkMatrix::I(),
59*c8dee2aaSAndroid Build Coastguard Worker             GrSamplerState::Filter = GrSamplerState::Filter::kNearest,
60*c8dee2aaSAndroid Build Coastguard Worker             GrSamplerState::MipmapMode mipmapMode = GrSamplerState::MipmapMode::kNone);
61*c8dee2aaSAndroid Build Coastguard Worker 
62*c8dee2aaSAndroid Build Coastguard Worker     /**
63*c8dee2aaSAndroid Build Coastguard Worker      * Make from a full GrSamplerState. Caps are required to determine support for kClampToBorder.
64*c8dee2aaSAndroid Build Coastguard Worker      * This will be emulated in the shader if there is no hardware support.
65*c8dee2aaSAndroid Build Coastguard Worker      */
66*c8dee2aaSAndroid Build Coastguard Worker     static std::unique_ptr<GrFragmentProcessor> Make(GrSurfaceProxyView, SkAlphaType,
67*c8dee2aaSAndroid Build Coastguard Worker                                                      const SkMatrix&, GrSamplerState,
68*c8dee2aaSAndroid Build Coastguard Worker                                                      const GrCaps& caps,
69*c8dee2aaSAndroid Build Coastguard Worker                                                      const float border[4] = kDefaultBorder);
70*c8dee2aaSAndroid Build Coastguard Worker 
71*c8dee2aaSAndroid Build Coastguard Worker     /**
72*c8dee2aaSAndroid Build Coastguard Worker      * Makes a texture effect that samples a subset of a texture. The wrap modes of the
73*c8dee2aaSAndroid Build Coastguard Worker      * GrSampleState are applied to the subset in the shader rather than using HW samplers.
74*c8dee2aaSAndroid Build Coastguard Worker      * The 'subset' parameter specifies the texels in the base level. The shader code will
75*c8dee2aaSAndroid Build Coastguard Worker      * avoid allowing linear filtering to read outside the texel window. However, if MIP
76*c8dee2aaSAndroid Build Coastguard Worker      * filtering is used and a shader invocation reads from a level other than the base
77*c8dee2aaSAndroid Build Coastguard Worker      * then it may read texel values that were computed from in part from base level texels
78*c8dee2aaSAndroid Build Coastguard Worker      * outside the window. More specifically, we treat the MIP map case exactly like the
79*c8dee2aaSAndroid Build Coastguard Worker      * linear case in terms of how the final texture coords are computed. If
80*c8dee2aaSAndroid Build Coastguard Worker      * alwaysUseShaderTileMode is true then MakeSubset won't attempt to use HW wrap modes if the
81*c8dee2aaSAndroid Build Coastguard Worker      * subset contains the entire texture.
82*c8dee2aaSAndroid Build Coastguard Worker      */
83*c8dee2aaSAndroid Build Coastguard Worker     static std::unique_ptr<GrFragmentProcessor> MakeSubset(GrSurfaceProxyView,
84*c8dee2aaSAndroid Build Coastguard Worker                                                            SkAlphaType,
85*c8dee2aaSAndroid Build Coastguard Worker                                                            const SkMatrix&,
86*c8dee2aaSAndroid Build Coastguard Worker                                                            GrSamplerState,
87*c8dee2aaSAndroid Build Coastguard Worker                                                            const SkRect& subset,
88*c8dee2aaSAndroid Build Coastguard Worker                                                            const GrCaps& caps,
89*c8dee2aaSAndroid Build Coastguard Worker                                                            const float border[4] = kDefaultBorder,
90*c8dee2aaSAndroid Build Coastguard Worker                                                            bool alwaysUseShaderTileMode = false);
91*c8dee2aaSAndroid Build Coastguard Worker 
92*c8dee2aaSAndroid Build Coastguard Worker     /**
93*c8dee2aaSAndroid Build Coastguard Worker      * The same as above but also takes a 'domain' that specifies any known limit on the post-
94*c8dee2aaSAndroid Build Coastguard Worker      * matrix texture coords that will be used to sample the texture. Specifying this requires
95*c8dee2aaSAndroid Build Coastguard Worker      * knowledge of how this effect will be nested into a paint, the local coords used with the
96*c8dee2aaSAndroid Build Coastguard Worker      * draw, etc. It is only used to attempt to optimize away the shader subset calculations.
97*c8dee2aaSAndroid Build Coastguard Worker      */
98*c8dee2aaSAndroid Build Coastguard Worker     static std::unique_ptr<GrFragmentProcessor> MakeSubset(GrSurfaceProxyView,
99*c8dee2aaSAndroid Build Coastguard Worker                                                            SkAlphaType,
100*c8dee2aaSAndroid Build Coastguard Worker                                                            const SkMatrix&,
101*c8dee2aaSAndroid Build Coastguard Worker                                                            GrSamplerState,
102*c8dee2aaSAndroid Build Coastguard Worker                                                            const SkRect& subset,
103*c8dee2aaSAndroid Build Coastguard Worker                                                            const SkRect& domain,
104*c8dee2aaSAndroid Build Coastguard Worker                                                            const GrCaps& caps,
105*c8dee2aaSAndroid Build Coastguard Worker                                                            const float border[4] = kDefaultBorder);
106*c8dee2aaSAndroid Build Coastguard Worker 
107*c8dee2aaSAndroid Build Coastguard Worker     /**
108*c8dee2aaSAndroid Build Coastguard Worker      * Like MakeSubset() but always uses kLinear filtering. MakeSubset() uses the subset rect
109*c8dee2aaSAndroid Build Coastguard Worker      * dimensions to determine the period of the wrap mode (for repeat and mirror). Once it computes
110*c8dee2aaSAndroid Build Coastguard Worker      * the wrapped texture coordinate inside subset rect it further clamps it to a 0.5 inset rect of
111*c8dee2aaSAndroid Build Coastguard Worker      * subset. When subset is an integer rectangle this clamping avoids the hw linear filtering from
112*c8dee2aaSAndroid Build Coastguard Worker      * reading texels just outside the subset rect. This factory allows a custom inset clamping
113*c8dee2aaSAndroid Build Coastguard Worker      * distance rather than 0.5, allowing those neighboring texels to influence the linear filtering
114*c8dee2aaSAndroid Build Coastguard Worker      * sample result. If there is a known restriction on the post-matrix texture coords it can be
115*c8dee2aaSAndroid Build Coastguard Worker      * specified using domain.
116*c8dee2aaSAndroid Build Coastguard Worker      */
117*c8dee2aaSAndroid Build Coastguard Worker     static std::unique_ptr<GrFragmentProcessor> MakeCustomLinearFilterInset(
118*c8dee2aaSAndroid Build Coastguard Worker             GrSurfaceProxyView,
119*c8dee2aaSAndroid Build Coastguard Worker             SkAlphaType,
120*c8dee2aaSAndroid Build Coastguard Worker             const SkMatrix&,
121*c8dee2aaSAndroid Build Coastguard Worker             GrSamplerState::WrapMode wx,
122*c8dee2aaSAndroid Build Coastguard Worker             GrSamplerState::WrapMode wy,
123*c8dee2aaSAndroid Build Coastguard Worker             const SkRect& subset,
124*c8dee2aaSAndroid Build Coastguard Worker             const SkRect* domain,
125*c8dee2aaSAndroid Build Coastguard Worker             SkVector inset,
126*c8dee2aaSAndroid Build Coastguard Worker             const GrCaps& caps,
127*c8dee2aaSAndroid Build Coastguard Worker             const float border[4] = kDefaultBorder);
128*c8dee2aaSAndroid Build Coastguard Worker 
129*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<GrFragmentProcessor> clone() const override;
130*c8dee2aaSAndroid Build Coastguard Worker 
name()131*c8dee2aaSAndroid Build Coastguard Worker     const char* name() const override { return "TextureEffect"; }
132*c8dee2aaSAndroid Build Coastguard Worker 
samplerState()133*c8dee2aaSAndroid Build Coastguard Worker     GrSamplerState samplerState() const { return fSamplerState; }
134*c8dee2aaSAndroid Build Coastguard Worker 
texture()135*c8dee2aaSAndroid Build Coastguard Worker     GrTexture* texture() const { return fView.asTextureProxy()->peekTexture(); }
136*c8dee2aaSAndroid Build Coastguard Worker 
view()137*c8dee2aaSAndroid Build Coastguard Worker     const GrSurfaceProxyView& view() const { return fView; }
138*c8dee2aaSAndroid Build Coastguard Worker 
139*c8dee2aaSAndroid Build Coastguard Worker     // Gets a matrix that is concat'ed by wrapping GrMatrixEffect that handles y-flip and coord
140*c8dee2aaSAndroid Build Coastguard Worker     // normalization if required. This matrix is not always known when we make the GrTextureEffect
141*c8dee2aaSAndroid Build Coastguard Worker     // because of fully-lazy proxies. Hence, this method  exists to allow this concat to happen
142*c8dee2aaSAndroid Build Coastguard Worker     // after proxy instantiation with coordination from GrMatrixEffect.
143*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix coordAdjustmentMatrix() const;
144*c8dee2aaSAndroid Build Coastguard Worker 
145*c8dee2aaSAndroid Build Coastguard Worker     class Impl : public ProgramImpl {
146*c8dee2aaSAndroid Build Coastguard Worker     public:
147*c8dee2aaSAndroid Build Coastguard Worker         void emitCode(EmitArgs&) override;
148*c8dee2aaSAndroid Build Coastguard Worker 
setSamplerHandle(GrGLSLShaderBuilder::SamplerHandle handle)149*c8dee2aaSAndroid Build Coastguard Worker         void setSamplerHandle(GrGLSLShaderBuilder::SamplerHandle handle) {
150*c8dee2aaSAndroid Build Coastguard Worker             fSamplerHandle = handle;
151*c8dee2aaSAndroid Build Coastguard Worker         }
152*c8dee2aaSAndroid Build Coastguard Worker 
153*c8dee2aaSAndroid Build Coastguard Worker     private:
154*c8dee2aaSAndroid Build Coastguard Worker         void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
155*c8dee2aaSAndroid Build Coastguard Worker 
156*c8dee2aaSAndroid Build Coastguard Worker         UniformHandle fSubsetUni;
157*c8dee2aaSAndroid Build Coastguard Worker         UniformHandle fClampUni;
158*c8dee2aaSAndroid Build Coastguard Worker         UniformHandle fIDimsUni;
159*c8dee2aaSAndroid Build Coastguard Worker         UniformHandle fBorderUni;
160*c8dee2aaSAndroid Build Coastguard Worker         GrGLSLShaderBuilder::SamplerHandle fSamplerHandle;
161*c8dee2aaSAndroid Build Coastguard Worker     };
162*c8dee2aaSAndroid Build Coastguard Worker 
163*c8dee2aaSAndroid Build Coastguard Worker private:
164*c8dee2aaSAndroid Build Coastguard Worker     struct Sampling;
165*c8dee2aaSAndroid Build Coastguard Worker 
166*c8dee2aaSAndroid Build Coastguard Worker     /**
167*c8dee2aaSAndroid Build Coastguard Worker      * Possible implementation of wrap mode in shader code. Some modes are specialized by
168*c8dee2aaSAndroid Build Coastguard Worker      * filter.
169*c8dee2aaSAndroid Build Coastguard Worker      */
170*c8dee2aaSAndroid Build Coastguard Worker     enum class ShaderMode : uint16_t {
171*c8dee2aaSAndroid Build Coastguard Worker         kNone,                   // Using HW mode
172*c8dee2aaSAndroid Build Coastguard Worker         kClamp,                  // Shader based clamp, no filter specialization
173*c8dee2aaSAndroid Build Coastguard Worker         kRepeat_Nearest_None,    // Simple repeat for nearest sampling, no mipmapping
174*c8dee2aaSAndroid Build Coastguard Worker         kRepeat_Linear_None,     // Filter the subset boundary for kRepeat mode, no mip mapping
175*c8dee2aaSAndroid Build Coastguard Worker         kRepeat_Linear_Mipmap,   // Logic for linear filtering and LOD selection with kRepeat mode.
176*c8dee2aaSAndroid Build Coastguard Worker         kRepeat_Nearest_Mipmap,  // Logic for nearest filtering and LOD selection with kRepeat mode.
177*c8dee2aaSAndroid Build Coastguard Worker         kMirrorRepeat,           // Mirror repeat (doesn't depend on filter))
178*c8dee2aaSAndroid Build Coastguard Worker         kClampToBorder_Nearest,  // Logic for hard transition to border color when not filtering.
179*c8dee2aaSAndroid Build Coastguard Worker         kClampToBorder_Filter,   // Logic for fading to border color when filtering.
180*c8dee2aaSAndroid Build Coastguard Worker     };
181*c8dee2aaSAndroid Build Coastguard Worker     static ShaderMode GetShaderMode(GrSamplerState::WrapMode,
182*c8dee2aaSAndroid Build Coastguard Worker                                     GrSamplerState::Filter,
183*c8dee2aaSAndroid Build Coastguard Worker                                     GrSamplerState::MipmapMode);
184*c8dee2aaSAndroid Build Coastguard Worker     static bool ShaderModeIsClampToBorder(ShaderMode);
185*c8dee2aaSAndroid Build Coastguard Worker     // To keep things a little simpler, when we have filtering logic in the shader we
186*c8dee2aaSAndroid Build Coastguard Worker     // operate on unnormalized texture coordinates. We will add a uniform that stores
187*c8dee2aaSAndroid Build Coastguard Worker     // {1/w, 1/h} in a float2 and normalizes after the mode is handled if the texture
188*c8dee2aaSAndroid Build Coastguard Worker     // is not rectangle.
189*c8dee2aaSAndroid Build Coastguard Worker     static bool ShaderModeRequiresUnormCoord(ShaderMode);
190*c8dee2aaSAndroid Build Coastguard Worker 
191*c8dee2aaSAndroid Build Coastguard Worker     GrSurfaceProxyView fView;
192*c8dee2aaSAndroid Build Coastguard Worker     GrSamplerState fSamplerState;
193*c8dee2aaSAndroid Build Coastguard Worker     float fBorder[4];
194*c8dee2aaSAndroid Build Coastguard Worker     SkRect fSubset;
195*c8dee2aaSAndroid Build Coastguard Worker     SkRect fClamp;
196*c8dee2aaSAndroid Build Coastguard Worker     ShaderMode fShaderModes[2];
197*c8dee2aaSAndroid Build Coastguard Worker 
198*c8dee2aaSAndroid Build Coastguard Worker     inline GrTextureEffect(GrSurfaceProxyView, SkAlphaType, const Sampling&);
199*c8dee2aaSAndroid Build Coastguard Worker 
200*c8dee2aaSAndroid Build Coastguard Worker     explicit GrTextureEffect(const GrTextureEffect& src);
201*c8dee2aaSAndroid Build Coastguard Worker 
202*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override;
203*c8dee2aaSAndroid Build Coastguard Worker 
204*c8dee2aaSAndroid Build Coastguard Worker     void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override;
205*c8dee2aaSAndroid Build Coastguard Worker 
206*c8dee2aaSAndroid Build Coastguard Worker     bool onIsEqual(const GrFragmentProcessor&) const override;
207*c8dee2aaSAndroid Build Coastguard Worker 
208*c8dee2aaSAndroid Build Coastguard Worker     bool matrixEffectShouldNormalize() const;
209*c8dee2aaSAndroid Build Coastguard Worker 
hasClampToBorderShaderMode()210*c8dee2aaSAndroid Build Coastguard Worker     bool hasClampToBorderShaderMode() const {
211*c8dee2aaSAndroid Build Coastguard Worker         return ShaderModeIsClampToBorder(fShaderModes[0]) ||
212*c8dee2aaSAndroid Build Coastguard Worker                ShaderModeIsClampToBorder(fShaderModes[1]);
213*c8dee2aaSAndroid Build Coastguard Worker     }
214*c8dee2aaSAndroid Build Coastguard Worker 
215*c8dee2aaSAndroid Build Coastguard Worker     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
216*c8dee2aaSAndroid Build Coastguard Worker 
217*c8dee2aaSAndroid Build Coastguard Worker     using INHERITED = GrFragmentProcessor;
218*c8dee2aaSAndroid Build Coastguard Worker };
219*c8dee2aaSAndroid Build Coastguard Worker #endif
220