1 | // [Blend2D] |
2 | // 2D Vector Graphics Powered by a JIT Compiler. |
3 | // |
4 | // [License] |
5 | // Zlib - See LICENSE.md file in the package. |
6 | |
7 | #ifndef BLEND2D_BLPIPE_P_H |
8 | #define BLEND2D_BLPIPE_P_H |
9 | |
10 | #include "./blapi-internal_p.h" |
11 | #include "./blpipedefs_p.h" |
12 | #include "./blsimd_p.h" |
13 | |
14 | //! \cond INTERNAL |
15 | //! \addtogroup blend2d_internal |
16 | //! \{ |
17 | |
18 | // ============================================================================ |
19 | // [Forward Declarations] |
20 | // ============================================================================ |
21 | |
22 | struct BLPipeRuntime; |
23 | struct BLPipeProvider; |
24 | struct BLPipeLookupCache; |
25 | |
26 | // ============================================================================ |
27 | // [Constants] |
28 | // ============================================================================ |
29 | |
30 | enum BLPipeRuntimeType : uint32_t { |
31 | //! Fixed runtime that doesn't use JIT (can be either reference or optimized). |
32 | BL_PIPE_RUNTIME_TYPE_FIXED = 0, |
33 | //! Runtime that uses PipeGen - JIT optimized pipelines. |
34 | BL_PIPE_RUNTIME_TYPE_PIPEGEN = 1, |
35 | |
36 | //! Count of pipeline runtime types. |
37 | BL_PIPE_RUNTIME_TYPE_COUNT |
38 | }; |
39 | |
40 | enum BLPipeRuntimeFlags : uint32_t { |
41 | BL_PIPE_RUNTIME_FLAG_ISOLATED = 0x00000001 |
42 | }; |
43 | |
44 | // ============================================================================ |
45 | // [BLPipeRuntime] |
46 | // ============================================================================ |
47 | |
48 | //! This is a base class used by either `BLPipeGenRuntime` (for dynamic piplines) |
49 | //! or `BLFixedPipeRuntime` for static pipelines. The purpose of this class is |
50 | //! to create interface that is used by the rendering context so it doesn't |
51 | //! have to know which kind of pipelines it uses. |
52 | struct BLPipeRuntime { |
53 | //! Type of the runtime, see `BLPipeRuntimeType`. |
54 | uint8_t _runtimeType; |
55 | //! Reserved for future use. |
56 | uint8_t _reserved; |
57 | //! Size of this runtime in bytes. |
58 | uint16_t _runtimeSize; |
59 | //! Runtime flags, see `BLPipeRuntimeFlags`. |
60 | uint32_t _runtimeFlags; |
61 | |
62 | //! Runtime destructor. |
63 | void (BL_CDECL* _destroy)(BLPipeRuntime* self) BL_NOEXCEPT; |
64 | |
65 | //! Functions exposed by the runtime that are copied to `BLPipeProvider` to |
66 | //! make them local in the rendering context. It seems hacky, but this removes |
67 | //! one extra indirection that would be needed if they were virtual. |
68 | struct Funcs { |
69 | BLPipeFillFunc (BL_CDECL* get)(BLPipeRuntime* self, uint32_t signature, BLPipeLookupCache* cache) BL_NOEXCEPT; |
70 | BLPipeFillFunc (BL_CDECL* test)(BLPipeRuntime* self, uint32_t signature, BLPipeLookupCache* cache) BL_NOEXCEPT; |
71 | } _funcs; |
72 | |
73 | BL_INLINE uint32_t runtimeType() const noexcept { return _runtimeType; } |
74 | BL_INLINE uint32_t runtimeFlags() const noexcept { return _runtimeFlags; } |
75 | BL_INLINE uint32_t runtimeSize() const noexcept { return _runtimeSize; } |
76 | |
77 | BL_INLINE void destroy() noexcept { _destroy(this); } |
78 | }; |
79 | |
80 | // ============================================================================ |
81 | // [BLPipeProvider] |
82 | // ============================================================================ |
83 | |
84 | struct BLPipeProvider { |
85 | BLPipeRuntime* _runtime; |
86 | BLPipeRuntime::Funcs _funcs; |
87 | |
88 | BL_INLINE BLPipeProvider() noexcept |
89 | : _runtime(nullptr), |
90 | _funcs {} {} |
91 | |
92 | BL_INLINE bool isInitialized() const noexcept { |
93 | return _runtime != nullptr; |
94 | } |
95 | |
96 | BL_INLINE void init(BLPipeRuntime* runtime) noexcept { |
97 | _runtime = runtime; |
98 | _funcs = runtime->_funcs; |
99 | } |
100 | |
101 | BL_INLINE void reset() noexcept { |
102 | memset(this, 0, sizeof(*this)); |
103 | } |
104 | |
105 | BL_INLINE BLPipeRuntime* runtime() const noexcept { return _runtime; } |
106 | BL_INLINE BLPipeFillFunc get(uint32_t signature, BLPipeLookupCache* cache = nullptr) const noexcept { return _funcs.get(_runtime, signature, cache); } |
107 | BL_INLINE BLPipeFillFunc test(uint32_t signature, BLPipeLookupCache* cache = nullptr) const noexcept { return _funcs.test(_runtime, signature, cache); } |
108 | }; |
109 | |
110 | // ============================================================================ |
111 | // [BLPipeLookupCache] |
112 | // ============================================================================ |
113 | |
114 | //! Pipe lookup cache is a local cache used by the rendering engine to store |
115 | //! `N` recently used pipelines so it doesn't have to use `BLPipeProvider` that |
116 | //! may would call `BLPipeRuntime` to query (or compile) the required pipeline. |
117 | struct BLPipeLookupCache { |
118 | //! Number of cached pipelines, must be multiply of 4. |
119 | enum : uint32_t { N = 8 }; |
120 | |
121 | //! Array of signatures for the lookup, uninitialized signatures should be zero. |
122 | uint32_t _signs[N]; |
123 | //! Array of functions matching signatures from `_signs` array. There is one |
124 | //! extra function at the end that must always be `nullptr` and is returned |
125 | //! when a signature isn't matched. |
126 | void* _funcs[N + 1]; |
127 | //! Index where a next signature will be written (incremental, wraps to zero). |
128 | size_t _currentIndex; |
129 | |
130 | BL_INLINE void reset() { memset(this, 0, sizeof(*this)); } |
131 | |
132 | BL_INLINE void* _lookup(uint32_t signature) const noexcept { |
133 | #if defined(BL_TARGET_OPT_SSE2) && 0 |
134 | using namespace SIMD; |
135 | static_assert(N == 8, "This code is written for N == 8" ); |
136 | |
137 | I128 vSign = vseti128u32(signature); |
138 | I128 v0123 = vcmpeqi32(vloadi128u(_signs + 0), vSign); |
139 | I128 v4567 = vcmpeqi32(vloadi128u(_signs + 4), vSign); |
140 | |
141 | uint32_t m0123 = uint32_t(_mm_movemask_ps(vcast<F128>(v0123))); |
142 | uint32_t m4567 = uint32_t(_mm_movemask_ps(vcast<F128>(v4567)) << 4); |
143 | |
144 | uint32_t i = blBitCtz(m0123 + m4567 + (1u << N)); |
145 | return _funcs[i]; |
146 | #else |
147 | size_t i; |
148 | for (i = 0; i < N; i++) |
149 | if (_signs[i] == signature) |
150 | break; |
151 | return _funcs[i]; |
152 | #endif |
153 | } |
154 | |
155 | BL_INLINE void _store(uint32_t signature, void* func) noexcept { |
156 | _signs[_currentIndex] = signature; |
157 | _funcs[_currentIndex] = func; |
158 | |
159 | if (++_currentIndex >= N) |
160 | _currentIndex = 0; |
161 | } |
162 | |
163 | template<typename Func> |
164 | BL_INLINE Func lookup(uint32_t signature) const noexcept { return (Func)_lookup(signature); } |
165 | |
166 | template<typename Func> |
167 | BL_INLINE void store(uint32_t signature, Func func) noexcept { _store(signature, (void*)func); } |
168 | }; |
169 | |
170 | //! \} |
171 | //! \endcond |
172 | |
173 | #endif // BLEND2D_BLPIPE_P_H |
174 | |