blob: 9c836a00bfea41bb11a7148a7f5ef7bf94ca106d [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 Craikb565df12015-10-05 13:00:52 -0700127 Matrix4 transform;
128 Rect clipRect;
129 int clipSideFlags = 0;
130 Rect clippedBounds;
131};
132
133/**
134 * Self-contained op wrapper, containing all resolved state required to draw the op.
135 *
136 * Stashed pointers within all point to longer lived objects, with no ownership implied.
137 */
138class BakedOpState {
139public:
140 static BakedOpState* tryConstruct(LinearAllocator& allocator,
141 const Snapshot& snapshot, const RecordedOp& recordedOp) {
Chris Craik386aa032015-12-07 17:08:25 -0800142 BakedOpState* bakedState = new (allocator) BakedOpState(snapshot, recordedOp, false);
143 if (bakedState->computedState.clippedBounds.isEmpty()) {
Chris Craikb565df12015-10-05 13:00:52 -0700144 // bounds are empty, so op is rejected
Chris Craik386aa032015-12-07 17:08:25 -0800145 allocator.rewindIfLastAlloc(bakedState);
Chris Craikb565df12015-10-05 13:00:52 -0700146 return nullptr;
147 }
Chris Craik386aa032015-12-07 17:08:25 -0800148 return bakedState;
149 }
150
151 enum class StrokeBehavior {
152 // stroking is forced, regardless of style on paint
153 Forced,
154 // stroking is defined by style on paint
155 StyleDefined,
156 };
157
158 static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator,
159 const Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) {
160 bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined)
161 ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
162 : true;
163
164 BakedOpState* bakedState = new (allocator) BakedOpState(
165 snapshot, recordedOp, expandForStroke);
166 if (bakedState->computedState.clippedBounds.isEmpty()) {
167 // bounds are empty, so op is rejected
168 allocator.rewindIfLastAlloc(bakedState);
169 return nullptr;
170 }
171 return bakedState;
Chris Craikb565df12015-10-05 13:00:52 -0700172 }
173
Chris Craikd3daa312015-11-06 10:59:56 -0800174 static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator,
175 const Snapshot& snapshot, const ShadowOp* shadowOpPtr) {
176 if (snapshot.getRenderTargetClip().isEmpty()) return nullptr;
177
178 // clip isn't empty, so construct the op
179 return new (allocator) BakedOpState(snapshot, shadowOpPtr);
180 }
181
Chris Craikb565df12015-10-05 13:00:52 -0700182 static void* operator new(size_t size, LinearAllocator& allocator) {
183 return allocator.alloc(size);
184 }
185
186 // computed state:
187 const ResolvedRenderState computedState;
188
189 // simple state (straight pointer/value storage):
190 const float alpha;
191 const RoundRectClipState* roundRectClipState;
192 const ProjectionPathMask* projectionPathMask;
193 const RecordedOp* op;
194
195private:
Chris Craik386aa032015-12-07 17:08:25 -0800196 BakedOpState(const Snapshot& snapshot, const RecordedOp& recordedOp, bool expandForStroke)
197 : computedState(snapshot, recordedOp, expandForStroke)
Chris Craikb565df12015-10-05 13:00:52 -0700198 , alpha(snapshot.alpha)
199 , roundRectClipState(snapshot.roundRectClipState)
200 , projectionPathMask(snapshot.projectionPathMask)
201 , op(&recordedOp) {}
Chris Craikd3daa312015-11-06 10:59:56 -0800202
203 BakedOpState(const Snapshot& snapshot, const ShadowOp* shadowOpPtr)
204 : computedState(snapshot)
205 , alpha(snapshot.alpha)
206 , roundRectClipState(snapshot.roundRectClipState)
207 , projectionPathMask(snapshot.projectionPathMask)
208 , op(shadowOpPtr) {}
Chris Craikb565df12015-10-05 13:00:52 -0700209};
210
211}; // namespace uirenderer
212}; // namespace android
213
214#endif // ANDROID_HWUI_BAKED_OP_STATE_H