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
10using namespace std::placeholders;
11
12namespace 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}}