1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #include "Renderer/BsRenderQueue.h" |
4 | #include "RenderAPI/BsSubMesh.h" |
5 | #include "Material/BsShader.h" |
6 | #include "Mesh/BsMesh.h" |
7 | #include "Material/BsMaterial.h" |
8 | #include "Renderer/BsRenderElement.h" |
9 | |
10 | using namespace std::placeholders; |
11 | |
12 | namespace bs { namespace ct |
13 | { |
14 | RenderQueue::RenderQueue(StateReduction mode) |
15 | :mStateReductionMode(mode) |
16 | { |
17 | |
18 | } |
19 | |
20 | void RenderQueue::clear() |
21 | { |
22 | mSortableElements.clear(); |
23 | mSortableElementIdx.clear(); |
24 | mElements.clear(); |
25 | |
26 | mSortedRenderElements.clear(); |
27 | } |
28 | |
29 | void RenderQueue::add(const RenderElement* element, float distFromCamera, UINT32 techniqueIdx) |
30 | { |
31 | SPtr<Material> material = element->material; |
32 | SPtr<Shader> shader = material->getShader(); |
33 | |
34 | UINT32 queuePriority = shader->getQueuePriority(); |
35 | QueueSortType sortType = shader->getQueueSortType(); |
36 | UINT32 shaderId = shader->getId(); |
37 | bool separablePasses = shader->getAllowSeparablePasses(); |
38 | |
39 | switch (sortType) |
40 | { |
41 | case QueueSortType::None: |
42 | distFromCamera = 0; |
43 | break; |
44 | case QueueSortType::BackToFront: |
45 | distFromCamera = -distFromCamera; |
46 | break; |
47 | case QueueSortType::FrontToBack: |
48 | break; |
49 | } |
50 | |
51 | UINT32 numPasses = material->getNumPasses(techniqueIdx); |
52 | if (!separablePasses) |
53 | numPasses = std::min(1U, numPasses); |
54 | |
55 | for (UINT32 i = 0; i < numPasses; i++) |
56 | { |
57 | UINT32 idx = (UINT32)mSortableElementIdx.size(); |
58 | mSortableElementIdx.push_back(idx); |
59 | |
60 | mSortableElements.push_back(SortableElement()); |
61 | SortableElement& sortableElem = mSortableElements.back(); |
62 | |
63 | sortableElem.seqIdx = idx; |
64 | sortableElem.priority = queuePriority; |
65 | sortableElem.shaderId = shaderId; |
66 | sortableElem.techniqueIdx = techniqueIdx; |
67 | sortableElem.passIdx = i; |
68 | sortableElem.distFromCamera = distFromCamera; |
69 | |
70 | mElements.push_back(element); |
71 | } |
72 | } |
73 | |
74 | void RenderQueue::sort() |
75 | { |
76 | std::function<bool(UINT32, UINT32, const Vector<SortableElement>&)> sortMethod; |
77 | |
78 | switch (mStateReductionMode) |
79 | { |
80 | case StateReduction::None: |
81 | sortMethod = &elementSorterNoGroup; |
82 | break; |
83 | case StateReduction::Material: |
84 | sortMethod = &elementSorterPreferGroup; |
85 | break; |
86 | case StateReduction::Distance: |
87 | sortMethod = &elementSorterPreferDistance; |
88 | break; |
89 | } |
90 | |
91 | // Sort only indices since we generate an entirely new data set anyway, it doesn't make sense to move sortable elements |
92 | std::sort(mSortableElementIdx.begin(), mSortableElementIdx.end(), std::bind(sortMethod, _1, _2, mSortableElements)); |
93 | |
94 | UINT32 prevShaderId = (UINT32)-1; |
95 | UINT32 prevTechniqueIdx = (UINT32)-1; |
96 | UINT32 prevPassIdx = (UINT32)-1; |
97 | for (UINT32 i = 0; i < (UINT32)mSortableElementIdx.size(); i++) |
98 | { |
99 | const UINT32 idx = mSortableElementIdx[i]; |
100 | const SortableElement& elem = mSortableElements[idx]; |
101 | const RenderElement* renderElem = mElements[idx]; |
102 | |
103 | const bool separablePasses = renderElem->material->getShader()->getAllowSeparablePasses(); |
104 | |
105 | if (separablePasses) |
106 | { |
107 | mSortedRenderElements.push_back(RenderQueueElement()); |
108 | |
109 | RenderQueueElement& sortedElem = mSortedRenderElements.back(); |
110 | sortedElem.renderElem = renderElem; |
111 | sortedElem.techniqueIdx = elem.techniqueIdx; |
112 | sortedElem.passIdx = elem.passIdx; |
113 | |
114 | if (prevShaderId != elem.shaderId || prevTechniqueIdx != elem.techniqueIdx || prevPassIdx != elem.passIdx) |
115 | { |
116 | sortedElem.applyPass = true; |
117 | prevShaderId = elem.shaderId; |
118 | prevTechniqueIdx = elem.techniqueIdx; |
119 | prevPassIdx = elem.passIdx; |
120 | } |
121 | else |
122 | sortedElem.applyPass = false; |
123 | } |
124 | else |
125 | { |
126 | const UINT32 numPasses = renderElem->material->getNumPasses(elem.techniqueIdx); |
127 | for (UINT32 j = 0; j < numPasses; j++) |
128 | { |
129 | mSortedRenderElements.push_back(RenderQueueElement()); |
130 | |
131 | RenderQueueElement& sortedElem = mSortedRenderElements.back(); |
132 | sortedElem.renderElem = renderElem; |
133 | sortedElem.techniqueIdx = elem.techniqueIdx; |
134 | sortedElem.passIdx = j; |
135 | |
136 | if (prevShaderId != elem.shaderId || prevTechniqueIdx != elem.techniqueIdx || prevPassIdx != j) |
137 | { |
138 | sortedElem.applyPass = true; |
139 | prevShaderId = elem.shaderId; |
140 | prevTechniqueIdx = elem.techniqueIdx; |
141 | prevPassIdx = j; |
142 | } |
143 | else |
144 | sortedElem.applyPass = false; |
145 | } |
146 | } |
147 | } |
148 | } |
149 | |
150 | bool RenderQueue::elementSorterNoGroup(UINT32 aIdx, UINT32 bIdx, const Vector<SortableElement>& lookup) |
151 | { |
152 | const SortableElement& a = lookup[aIdx]; |
153 | const SortableElement& b = lookup[bIdx]; |
154 | |
155 | UINT8 isHigher = (a.priority > b.priority) << 2 | |
156 | (a.distFromCamera < b.distFromCamera) << 1 | |
157 | (a.seqIdx < b.seqIdx); |
158 | |
159 | UINT8 isLower = (a.priority < b.priority) << 2 | |
160 | (a.distFromCamera > b.distFromCamera) << 1 | |
161 | (a.seqIdx > b.seqIdx); |
162 | |
163 | return isHigher > isLower; |
164 | } |
165 | |
166 | bool RenderQueue::elementSorterPreferGroup(UINT32 aIdx, UINT32 bIdx, const Vector<SortableElement>& lookup) |
167 | { |
168 | const SortableElement& a = lookup[aIdx]; |
169 | const SortableElement& b = lookup[bIdx]; |
170 | |
171 | UINT8 isHigher = (a.priority > b.priority) << 5 | |
172 | (a.shaderId < b.shaderId) << 4 | |
173 | (a.techniqueIdx < b.techniqueIdx) << 3 | |
174 | (a.passIdx < b.passIdx) << 2 | |
175 | (a.distFromCamera < b.distFromCamera) << 1 | |
176 | (a.seqIdx < b.seqIdx); |
177 | |
178 | UINT8 isLower = (a.priority < b.priority) << 5 | |
179 | (a.shaderId > b.shaderId) << 4 | |
180 | (a.techniqueIdx > b.techniqueIdx) << 3 | |
181 | (a.passIdx > b.passIdx) << 2 | |
182 | (a.distFromCamera > b.distFromCamera) << 1 | |
183 | (a.seqIdx > b.seqIdx); |
184 | |
185 | return isHigher > isLower; |
186 | } |
187 | |
188 | bool RenderQueue::elementSorterPreferDistance(UINT32 aIdx, UINT32 bIdx, const Vector<SortableElement>& lookup) |
189 | { |
190 | const SortableElement& a = lookup[aIdx]; |
191 | const SortableElement& b = lookup[bIdx]; |
192 | |
193 | UINT8 isHigher = (a.priority > b.priority) << 5 | |
194 | (a.distFromCamera < b.distFromCamera) << 4 | |
195 | (a.shaderId < b.shaderId) << 3 | |
196 | (a.techniqueIdx < b.techniqueIdx) << 2 | |
197 | (a.passIdx < b.passIdx) << 1 | |
198 | (a.seqIdx < b.seqIdx); |
199 | |
200 | UINT8 isLower = (a.priority < b.priority) << 5 | |
201 | (a.distFromCamera > b.distFromCamera) << 4 | |
202 | (a.shaderId > b.shaderId) << 3 | |
203 | (a.techniqueIdx > b.techniqueIdx) << 2 | |
204 | (a.passIdx > b.passIdx) << 1 | |
205 | (a.seqIdx > b.seqIdx); |
206 | |
207 | return isHigher > isLower; |
208 | } |
209 | |
210 | const Vector<RenderQueueElement>& RenderQueue::getSortedElements() const |
211 | { |
212 | return mSortedRenderElements; |
213 | } |
214 | }} |