blob: b12c0c97035247fd55cbdb9a05d83bc76ddea33c [file] [log] [blame]
Chris Craikb565df12015-10-05 13:00:52 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef ANDROID_HWUI_BAKED_OP_STATE_H
18#define ANDROID_HWUI_BAKED_OP_STATE_H
19
20#include "Matrix.h"
21#include "RecordedOp.h"
22#include "Rect.h"
23#include "Snapshot.h"
24
25namespace android {
26namespace uirenderer {
27
28namespace OpClipSideFlags {
29 enum {
30 None = 0x0,
31 Left = 0x1,
32 Top = 0x2,
33 Right = 0x4,
34 Bottom = 0x8,
35 Full = 0xF,
36 // ConservativeFull = 0x1F needed?
37 };
38}
39
40/**
Chris Craik15c3f192015-12-03 12:16:56 -080041 * Holds a list of BakedOpStates of ops that can be drawn together
42 */
43struct MergedBakedOpList {
44 const BakedOpState*const* states;
45 size_t count;
46 int clipSideFlags;
47 Rect clip;
48};
49
50/**
Chris Craikb565df12015-10-05 13:00:52 -070051 * Holds the resolved clip, transform, and bounds of a recordedOp, when replayed with a snapshot
52 */
53class ResolvedRenderState {
54public:
55 // TODO: remove the mapRects/matrix multiply when snapshot & recorded transforms are translates
Chris Craik386aa032015-12-07 17:08:25 -080056 ResolvedRenderState(const Snapshot& snapshot, const RecordedOp& recordedOp, bool expandForStroke) {
Chris Craikb565df12015-10-05 13:00:52 -070057 /* TODO: benchmark a fast path for translate-only matrices, such as:
58 if (CC_LIKELY(snapshot.transform->getType() == Matrix4::kTypeTranslate
59 && recordedOp.localMatrix.getType() == Matrix4::kTypeTranslate)) {
60 float translateX = snapshot.transform->getTranslateX() + recordedOp.localMatrix.getTranslateX();
61 float translateY = snapshot.transform->getTranslateY() + recordedOp.localMatrix.getTranslateY();
62 transform.loadTranslate(translateX, translateY, 0);
63
64 // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
65 clipRect = recordedOp.localClipRect;
66 clipRect.translate(translateX, translateY);
67 clipRect.doIntersect(snapshot.getClipRect());
68 clipRect.snapToPixelBoundaries();
69
70 // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
71 clippedBounds = recordedOp.unmappedBounds;
72 clippedBounds.translate(translateX, translateY);
73 } ... */
74
75 // resolvedMatrix = parentMatrix * localMatrix
76 transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix);
77
78 // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
79 clipRect = recordedOp.localClipRect;
80 snapshot.transform->mapRect(clipRect);
Chris Craik6fe991e52015-10-20 09:39:42 -070081 clipRect.doIntersect(snapshot.getRenderTargetClip());
Chris Craikb565df12015-10-05 13:00:52 -070082 clipRect.snapToPixelBoundaries();
83
84 // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
85 clippedBounds = recordedOp.unmappedBounds;
Chris Craik386aa032015-12-07 17:08:25 -080086 if (CC_UNLIKELY(expandForStroke)) {
87 // account for non-hairline stroke
88 clippedBounds.outset(recordedOp.paint->getStrokeWidth() * 0.5f);
89 }
Chris Craikb565df12015-10-05 13:00:52 -070090 transform.mapRect(clippedBounds);
Chris Craik386aa032015-12-07 17:08:25 -080091 if (CC_UNLIKELY(expandForStroke
92 && (!transform.isPureTranslate() || recordedOp.paint->getStrokeWidth() < 1.0f))) {
93 // account for hairline stroke when stroke may be < 1 scaled pixel
94 // Non translate || strokeWidth < 1 is conservative, but will cover all cases
95 clippedBounds.outset(0.5f);
96 }
Chris Craikb565df12015-10-05 13:00:52 -070097
98 if (clipRect.left > clippedBounds.left) clipSideFlags |= OpClipSideFlags::Left;
99 if (clipRect.top > clippedBounds.top) clipSideFlags |= OpClipSideFlags::Top;
100 if (clipRect.right < clippedBounds.right) clipSideFlags |= OpClipSideFlags::Right;
101 if (clipRect.bottom < clippedBounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom;
102 clippedBounds.doIntersect(clipRect);
103
104 /**
105 * TODO: once we support complex clips, we may want to reject to avoid that work where
106 * possible. Should we:
107 * 1 - quickreject based on clippedBounds, quick early (duplicating logic in resolvedOp)
108 * 2 - merge stuff into tryConstruct factory method, so it can handle quickRejection
109 * and early return null in one place.
110 */
111 }
Chris Craikd3daa312015-11-06 10:59:56 -0800112
113 /**
114 * Constructor for unbounded ops without transform/clip (namely shadows)
115 *
116 * Since the op doesn't have known bounds, we conservatively set the mapped bounds
117 * to the current clipRect, and clipSideFlags to Full.
118 */
119 ResolvedRenderState(const Snapshot& snapshot) {
120 transform = *snapshot.transform;
121 clipRect = snapshot.getRenderTargetClip();
122 clippedBounds = clipRect;
123 transform.mapRect(clippedBounds);
124 clipSideFlags = OpClipSideFlags::Full;
125 }
126
Chris Craikd7448e62015-12-15 10:34:36 -0800127 Rect computeLocalSpaceClip() const {
128 Matrix4 inverse;
129 inverse.loadInverse(transform);
130
131 Rect outClip(clipRect);
132 inverse.mapRect(outClip);
133 return outClip;
134 }
135
Chris Craikb565df12015-10-05 13:00:52 -0700136 Matrix4 transform;
137 Rect clipRect;
138 int clipSideFlags = 0;
139 Rect clippedBounds;
140};
141
142/**
143 * Self-contained op wrapper, containing all resolved state required to draw the op.
144 *
145 * Stashed pointers within all point to longer lived objects, with no ownership implied.
146 */
147class BakedOpState {
148public:
149 static BakedOpState* tryConstruct(LinearAllocator& allocator,
150 const Snapshot& snapshot, const RecordedOp& recordedOp) {
Chris Craik386aa032015-12-07 17:08:25 -0800151 BakedOpState* bakedState = new (allocator) BakedOpState(snapshot, recordedOp, false);
152 if (bakedState->computedState.clippedBounds.isEmpty()) {
Chris Craikb565df12015-10-05 13:00:52 -0700153 // bounds are empty, so op is rejected
Chris Craik386aa032015-12-07 17:08:25 -0800154 allocator.rewindIfLastAlloc(bakedState);
Chris Craikb565df12015-10-05 13:00:52 -0700155 return nullptr;
156 }
Chris Craik386aa032015-12-07 17:08:25 -0800157 return bakedState;
158 }
159
160 enum class StrokeBehavior {
161 // stroking is forced, regardless of style on paint
162 Forced,
163 // stroking is defined by style on paint
164 StyleDefined,
165 };
166
167 static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator,
168 const Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) {
169 bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined)
170 ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
171 : true;
172
173 BakedOpState* bakedState = new (allocator) BakedOpState(
174 snapshot, recordedOp, expandForStroke);
175 if (bakedState->computedState.clippedBounds.isEmpty()) {
176 // bounds are empty, so op is rejected
177 allocator.rewindIfLastAlloc(bakedState);
178 return nullptr;
179 }
180 return bakedState;
Chris Craikb565df12015-10-05 13:00:52 -0700181 }
182
Chris Craikd3daa312015-11-06 10:59:56 -0800183 static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator,
184 const Snapshot& snapshot, const ShadowOp* shadowOpPtr) {
185 if (snapshot.getRenderTargetClip().isEmpty()) return nullptr;
186
187 // clip isn't empty, so construct the op
188 return new (allocator) BakedOpState(snapshot, shadowOpPtr);
189 }
190
Chris Craikb565df12015-10-05 13:00:52 -0700191 static void* operator new(size_t size, LinearAllocator& allocator) {
192 return allocator.alloc(size);
193 }
194
195 // computed state:
196 const ResolvedRenderState computedState;
197
198 // simple state (straight pointer/value storage):
199 const float alpha;
200 const RoundRectClipState* roundRectClipState;
201 const ProjectionPathMask* projectionPathMask;
202 const RecordedOp* op;
203
204private:
Chris Craik386aa032015-12-07 17:08:25 -0800205 BakedOpState(const Snapshot& snapshot, const RecordedOp& recordedOp, bool expandForStroke)
206 : computedState(snapshot, recordedOp, expandForStroke)
Chris Craikb565df12015-10-05 13:00:52 -0700207 , alpha(snapshot.alpha)
208 , roundRectClipState(snapshot.roundRectClipState)
209 , projectionPathMask(snapshot.projectionPathMask)
210 , op(&recordedOp) {}
Chris Craikd3daa312015-11-06 10:59:56 -0800211
212 BakedOpState(const Snapshot& snapshot, const ShadowOp* shadowOpPtr)
213 : computedState(snapshot)
214 , alpha(snapshot.alpha)
215 , roundRectClipState(snapshot.roundRectClipState)
216 , projectionPathMask(snapshot.projectionPathMask)
217 , op(shadowOpPtr) {}
Chris Craikb565df12015-10-05 13:00:52 -0700218};
219
220}; // namespace uirenderer
221}; // namespace android
222
223#endif // ANDROID_HWUI_BAKED_OP_STATE_H