Line data Source code
1 : /* Copyright (C) 2022 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "ShaderDefines.h"
21 :
22 : #include "graphics/ShaderProgram.h"
23 : #include "lib/hash.h"
24 : #include "maths/Vector4D.h"
25 : #include "ps/ThreadUtil.h"
26 :
27 : #include <algorithm>
28 : #include <sstream>
29 :
30 : namespace std
31 : {
32 : template<>
33 : struct hash<CVector4D>
34 : {
35 3 : std::size_t operator()(const CVector4D& v) const
36 : {
37 3 : size_t hash = 0;
38 3 : hash_combine(hash, v.X);
39 3 : hash_combine(hash, v.Y);
40 3 : hash_combine(hash, v.Z);
41 3 : hash_combine(hash, v.W);
42 3 : return hash;
43 : }
44 : };
45 : }
46 :
47 20 : bool operator==(const CShaderParams<CStrIntern>::SItems& a, const CShaderParams<CStrIntern>::SItems& b)
48 : {
49 20 : return a.items == b.items;
50 : }
51 :
52 1 : bool operator==(const CShaderParams<CVector4D>::SItems& a, const CShaderParams<CVector4D>::SItems& b)
53 : {
54 1 : return a.items == b.items;
55 : }
56 :
57 : template<typename value_t>
58 24 : bool CShaderParams<value_t>::SItems::NameLess(const Item& a, const Item& b)
59 : {
60 24 : return a.first < b.first;
61 : }
62 :
63 : template<typename value_t>
64 40 : typename CShaderParams<value_t>::SItems* CShaderParams<value_t>::GetInterned(const SItems& items)
65 : {
66 40 : ENSURE(Threading::IsMainThread()); // s_InternedItems is not thread-safe
67 :
68 40 : typename InternedItems_t::iterator it = s_InternedItems.find(items);
69 40 : if (it != s_InternedItems.end())
70 21 : return it->second.get();
71 :
72 : // Sanity test: the items list is meant to be sorted by name.
73 : // This is a reasonable place to verify that, since this will be called once per distinct SItems.
74 19 : ENSURE(std::is_sorted(items.items.begin(), items.items.end(), SItems::NameLess));
75 :
76 38 : std::shared_ptr<SItems> ptr = std::make_shared<SItems>(items);
77 19 : s_InternedItems.insert(std::make_pair(items, ptr));
78 19 : return ptr.get();
79 : }
80 :
81 : template<typename value_t>
82 44 : CShaderParams<value_t>::CShaderParams()
83 : {
84 44 : *this = s_Empty;
85 44 : }
86 :
87 : template<typename value_t>
88 2 : CShaderParams<value_t>::CShaderParams(SItems* items) : m_Items(items)
89 : {
90 2 : }
91 :
92 : template<typename value_t>
93 2 : CShaderParams<value_t> CShaderParams<value_t>::CreateEmpty()
94 : {
95 4 : SItems items;
96 2 : items.RecalcHash();
97 4 : return CShaderParams(GetInterned(items));
98 : }
99 :
100 : template<typename value_t>
101 36 : void CShaderParams<value_t>::Set(CStrIntern name, const value_t& value)
102 : {
103 72 : SItems items = *m_Items;
104 :
105 36 : typename SItems::Item addedItem = std::make_pair(name, value);
106 :
107 : // Add the new item in a way that preserves the sortedness and uniqueness of item names
108 44 : for (typename std::vector<typename SItems::Item>::iterator it = items.items.begin(); ; ++it)
109 : {
110 52 : if (it == items.items.end() || addedItem.first < it->first)
111 : {
112 30 : items.items.insert(it, addedItem);
113 30 : break;
114 : }
115 14 : else if (addedItem.first == it->first)
116 : {
117 6 : it->second = addedItem.second;
118 6 : break;
119 : }
120 : }
121 :
122 36 : items.RecalcHash();
123 36 : m_Items = GetInterned(items);
124 36 : }
125 :
126 : template<typename value_t>
127 2 : void CShaderParams<value_t>::SetMany(const CShaderParams& params)
128 : {
129 4 : SItems items;
130 : // set_union merges the two sorted lists into a new sorted list;
131 : // if two items are equivalent (i.e. equal names, possibly different values)
132 : // then the one from the first list is kept
133 10 : std::set_union(
134 4 : params.m_Items->items.begin(), params.m_Items->items.end(),
135 4 : m_Items->items.begin(), m_Items->items.end(),
136 : std::inserter(items.items, items.items.begin()),
137 : SItems::NameLess);
138 2 : items.RecalcHash();
139 2 : m_Items = GetInterned(items);
140 2 : }
141 :
142 : template<typename value_t>
143 0 : std::map<CStrIntern, value_t> CShaderParams<value_t>::GetMap() const
144 : {
145 0 : std::map<CStrIntern, value_t> ret;
146 0 : for (size_t i = 0; i < m_Items->items.size(); ++i)
147 0 : ret[m_Items->items[i].first] = m_Items->items[i].second;
148 0 : return ret;
149 : }
150 :
151 : template<typename value_t>
152 8 : size_t CShaderParams<value_t>::GetHash() const
153 : {
154 8 : return m_Items->hash;
155 : }
156 :
157 : template<typename value_t>
158 40 : void CShaderParams<value_t>::SItems::RecalcHash()
159 : {
160 40 : size_t h = 0;
161 108 : for (size_t i = 0; i < items.size(); ++i)
162 : {
163 68 : hash_combine(h, items[i].first);
164 68 : hash_combine(h, items[i].second);
165 : }
166 40 : hash = h;
167 40 : }
168 :
169 :
170 33 : void CShaderDefines::Add(CStrIntern name, CStrIntern value)
171 : {
172 33 : Set(name, value);
173 33 : }
174 :
175 7 : int CShaderDefines::GetInt(const char* name) const
176 : {
177 7 : CStrIntern nameIntern(name);
178 7 : for (size_t i = 0; i < m_Items->items.size(); ++i)
179 : {
180 6 : if (m_Items->items[i].first == nameIntern)
181 : {
182 : int ret;
183 12 : std::stringstream str(m_Items->items[i].second.c_str());
184 6 : str >> ret;
185 6 : return ret;
186 : }
187 : }
188 1 : return 0;
189 : }
190 :
191 :
192 3 : void CShaderUniforms::Add(const char* name, const CVector4D& value)
193 : {
194 3 : Set(CStrIntern(name), value);
195 3 : }
196 :
197 2 : CVector4D CShaderUniforms::GetVector(const char* name) const
198 : {
199 2 : CStrIntern nameIntern(name);
200 2 : for (size_t i = 0; i < m_Items->items.size(); ++i)
201 : {
202 1 : if (m_Items->items[i].first == nameIntern)
203 : {
204 1 : return m_Items->items[i].second;
205 : }
206 : }
207 1 : return CVector4D();
208 : }
209 :
210 0 : void CShaderUniforms::BindUniforms(
211 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
212 : Renderer::Backend::IShaderProgram* shader) const
213 : {
214 0 : for (const SItems::Item& item : m_Items->items)
215 : {
216 0 : const CVector4D& v = item.second;
217 0 : deviceCommandContext->SetUniform(
218 0 : shader->GetBindingSlot(item.first), v.AsFloatArray());
219 : }
220 0 : }
221 :
222 0 : void CShaderRenderQueries::Add(const char* name)
223 : {
224 0 : if (name == CStr("sim_time"))
225 : {
226 0 : m_Items.emplace_back(RQUERY_TIME, CStrIntern(name));
227 : }
228 0 : else if (name == CStr("water_tex"))
229 : {
230 0 : m_Items.emplace_back(RQUERY_WATER_TEX, CStrIntern(name));
231 : }
232 0 : else if (name == CStr("sky_cube"))
233 : {
234 0 : m_Items.emplace_back(RQUERY_SKY_CUBE, CStrIntern(name));
235 : }
236 0 : }
237 :
238 : // Explicit instantiations:
239 :
240 1 : template<> CShaderParams<CStrIntern>::InternedItems_t CShaderParams<CStrIntern>::s_InternedItems = CShaderParams<CStrIntern>::InternedItems_t();
241 1 : template<> CShaderParams<CVector4D>::InternedItems_t CShaderParams<CVector4D>::s_InternedItems = CShaderParams<CVector4D>::InternedItems_t();
242 :
243 1 : template<> CShaderParams<CStrIntern> CShaderParams<CStrIntern>::s_Empty = CShaderParams<CStrIntern>::CreateEmpty();
244 1 : template<> CShaderParams<CVector4D> CShaderParams<CVector4D>::s_Empty = CShaderParams<CVector4D>::CreateEmpty();
245 :
246 : template class CShaderParams<CStrIntern>;
247 3 : template class CShaderParams<CVector4D>;
|