1 //
2 // Copyright 2016 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // TexturesPerf:
7 // Performance test for setting texture state.
8 //
9
10 #include "ANGLEPerfTest.h"
11
12 #include <iostream>
13 #include <random>
14 #include <sstream>
15
16 #include "common/debug.h"
17 #include "util/shader_utils.h"
18
19 namespace angle
20 {
21 constexpr unsigned int kIterationsPerStep = 256;
22
23 enum class Frequency
24 {
25 Always,
26 Sometimes,
27 Never
28 };
29
GetFrequencyValue(Frequency frequency,size_t sometimesValue)30 size_t GetFrequencyValue(Frequency frequency, size_t sometimesValue)
31 {
32 switch (frequency)
33 {
34 case Frequency::Always:
35 return 1;
36 case Frequency::Never:
37 return std::numeric_limits<size_t>::max();
38 case Frequency::Sometimes:
39 return sometimesValue;
40 default:
41 UNREACHABLE();
42 return 0;
43 }
44 }
45
FrequencyToString(Frequency frequency)46 std::string FrequencyToString(Frequency frequency)
47 {
48 switch (frequency)
49 {
50 case Frequency::Always:
51 return "always";
52 case Frequency::Sometimes:
53 return "sometimes";
54 case Frequency::Never:
55 return "never";
56 default:
57 UNREACHABLE();
58 return "";
59 }
60 }
61
62 constexpr size_t kRebindSometimesFrequency = 5;
63 constexpr size_t kStateUpdateSometimesFrequency = 3;
64
65 struct TexturesParams final : public RenderTestParams
66 {
TexturesParamsangle::TexturesParams67 TexturesParams()
68 {
69 iterationsPerStep = kIterationsPerStep;
70
71 // Common default params
72 majorVersion = 2;
73 minorVersion = 0;
74 windowWidth = 720;
75 windowHeight = 720;
76
77 numTextures = 8;
78 textureRebindFrequency = Frequency::Sometimes;
79 textureStateUpdateFrequency = Frequency::Sometimes;
80 textureMipCount = 8;
81
82 webgl = false;
83 }
84
85 std::string story() const override;
86 size_t numTextures;
87 Frequency textureRebindFrequency;
88 Frequency textureStateUpdateFrequency;
89 size_t textureMipCount;
90
91 bool webgl;
92 };
93
operator <<(std::ostream & os,const TexturesParams & params)94 std::ostream &operator<<(std::ostream &os, const TexturesParams ¶ms)
95 {
96 os << params.backendAndStory().substr(1);
97 return os;
98 }
99
story() const100 std::string TexturesParams::story() const
101 {
102 std::stringstream strstr;
103
104 strstr << RenderTestParams::story();
105 strstr << "_" << FrequencyToString(textureRebindFrequency) << "_rebind";
106 strstr << "_" << FrequencyToString(textureStateUpdateFrequency) << "_update";
107
108 if (webgl)
109 {
110 strstr << "_webgl";
111 }
112
113 return strstr.str();
114 }
115
116 class TexturesBenchmark : public ANGLERenderTest,
117 public ::testing::WithParamInterface<TexturesParams>
118 {
119 public:
120 TexturesBenchmark();
121
122 void initializeBenchmark() override;
123 void destroyBenchmark() override;
124 void drawBenchmark() override;
125
126 private:
127 void initShaders();
128 void initTextures();
129
130 std::vector<GLuint> mTextures;
131
132 GLuint mProgram;
133 std::vector<GLuint> mUniformLocations;
134 };
135
TexturesBenchmark()136 TexturesBenchmark::TexturesBenchmark() : ANGLERenderTest("Textures", GetParam()), mProgram(0u)
137 {
138 setWebGLCompatibilityEnabled(GetParam().webgl);
139 setRobustResourceInit(GetParam().webgl);
140 }
141
initializeBenchmark()142 void TexturesBenchmark::initializeBenchmark()
143 {
144 const auto ¶ms = GetParam();
145
146 // Verify the uniform counts are within the limits
147 GLint maxTextureUnits;
148 glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
149 if (params.numTextures > static_cast<size_t>(maxTextureUnits))
150 {
151 FAIL() << "Texture count (" << params.numTextures << ")"
152 << " exceeds maximum texture unit count: " << maxTextureUnits << std::endl;
153 }
154
155 initShaders();
156 initTextures();
157 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
158 glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
159
160 ASSERT_GL_NO_ERROR();
161 }
162
GetUniformLocationName(size_t idx,bool vertexShader)163 std::string GetUniformLocationName(size_t idx, bool vertexShader)
164 {
165 std::stringstream strstr;
166 strstr << (vertexShader ? "vs" : "fs") << "_u_" << idx;
167 return strstr.str();
168 }
169
initShaders()170 void TexturesBenchmark::initShaders()
171 {
172 const auto ¶ms = GetParam();
173
174 std::string vs =
175 "void main()\n"
176 "{\n"
177 " gl_Position = vec4(0, 0, 0, 0);\n"
178 "}\n";
179
180 std::stringstream fstrstr;
181 for (size_t i = 0; i < params.numTextures; i++)
182 {
183 fstrstr << "uniform sampler2D tex" << i << ";";
184 }
185 fstrstr << "void main()\n"
186 "{\n"
187 " gl_FragColor = vec4(0, 0, 0, 0)";
188 for (size_t i = 0; i < params.numTextures; i++)
189 {
190 fstrstr << "+ texture2D(tex" << i << ", vec2(0, 0))";
191 }
192 fstrstr << ";\n"
193 "}\n";
194
195 mProgram = CompileProgram(vs.c_str(), fstrstr.str().c_str());
196 ASSERT_NE(0u, mProgram);
197
198 for (size_t i = 0; i < params.numTextures; ++i)
199 {
200 std::stringstream uniformName;
201 uniformName << "tex" << i;
202
203 GLint location = glGetUniformLocation(mProgram, uniformName.str().c_str());
204 ASSERT_NE(-1, location);
205 mUniformLocations.push_back(location);
206 }
207
208 // Use the program object
209 glUseProgram(mProgram);
210 }
211
initTextures()212 void TexturesBenchmark::initTextures()
213 {
214 const auto ¶ms = GetParam();
215
216 size_t textureSize = static_cast<size_t>(1) << params.textureMipCount;
217 std::vector<GLubyte> textureData(textureSize * textureSize * 4);
218 for (auto &byte : textureData)
219 {
220 byte = rand() % 255u;
221 }
222
223 for (size_t texIndex = 0; texIndex < params.numTextures; texIndex++)
224 {
225 GLuint tex = 0;
226 glGenTextures(1, &tex);
227
228 glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + texIndex));
229 glBindTexture(GL_TEXTURE_2D, tex);
230 for (size_t mip = 0; mip < params.textureMipCount; mip++)
231 {
232 GLsizei levelSize = static_cast<GLsizei>(textureSize >> mip);
233 glTexImage2D(GL_TEXTURE_2D, static_cast<GLint>(mip), GL_RGBA, levelSize, levelSize, 0,
234 GL_RGBA, GL_UNSIGNED_BYTE, textureData.data());
235 }
236 mTextures.push_back(tex);
237
238 glUniform1i(mUniformLocations[texIndex], static_cast<GLint>(texIndex));
239 }
240 }
241
destroyBenchmark()242 void TexturesBenchmark::destroyBenchmark()
243 {
244 glDeleteProgram(mProgram);
245 }
246
drawBenchmark()247 void TexturesBenchmark::drawBenchmark()
248 {
249 const auto ¶ms = GetParam();
250
251 size_t textureRebindPeriod =
252 GetFrequencyValue(params.textureRebindFrequency, kRebindSometimesFrequency);
253 size_t textureStateUpdatePeriod =
254 GetFrequencyValue(params.textureStateUpdateFrequency, kStateUpdateSometimesFrequency);
255
256 for (size_t it = 0; it < params.iterationsPerStep; ++it)
257 {
258 if (it % textureRebindPeriod == 0)
259 {
260 // Swap two textures
261 size_t swapTexture = (it / textureRebindPeriod) % (params.numTextures - 1);
262
263 glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + swapTexture));
264 glBindTexture(GL_TEXTURE_2D, mTextures[swapTexture]);
265 glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + swapTexture + 1));
266 glBindTexture(GL_TEXTURE_2D, mTextures[swapTexture + 1]);
267 std::swap(mTextures[swapTexture], mTextures[swapTexture + 1]);
268 }
269
270 if (it % textureStateUpdatePeriod == 0)
271 {
272 // Update a texture's state
273 size_t stateUpdateCount = it / textureStateUpdatePeriod;
274
275 const size_t numUpdateTextures = 4;
276 ASSERT_LE(numUpdateTextures, params.numTextures);
277
278 size_t firstTexture = stateUpdateCount % (params.numTextures - numUpdateTextures);
279
280 for (size_t updateTextureIdx = 0; updateTextureIdx < numUpdateTextures;
281 updateTextureIdx++)
282 {
283 size_t updateTexture = firstTexture + updateTextureIdx;
284 glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + updateTexture));
285
286 const GLenum minFilters[] = {
287 GL_NEAREST,
288 GL_LINEAR,
289 GL_NEAREST_MIPMAP_NEAREST,
290 GL_LINEAR_MIPMAP_NEAREST,
291 GL_NEAREST_MIPMAP_LINEAR,
292 GL_LINEAR_MIPMAP_LINEAR,
293 };
294 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
295 minFilters[stateUpdateCount % ArraySize(minFilters)]);
296
297 const GLenum magFilters[] = {
298 GL_NEAREST,
299 GL_LINEAR,
300 };
301 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
302 magFilters[stateUpdateCount % ArraySize(magFilters)]);
303
304 const GLenum wrapParameters[] = {
305 GL_CLAMP_TO_EDGE,
306 GL_REPEAT,
307 GL_MIRRORED_REPEAT,
308 };
309 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
310 wrapParameters[stateUpdateCount % ArraySize(wrapParameters)]);
311 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
312 wrapParameters[stateUpdateCount % ArraySize(wrapParameters)]);
313 }
314 }
315
316 glDrawArrays(GL_TRIANGLES, 0, 3);
317 }
318
319 ASSERT_GL_NO_ERROR();
320 }
321
ApplyFrequencies(const TexturesParams & paramsIn,Frequency rebindFrequency,Frequency stateUpdateFrequency)322 TexturesParams ApplyFrequencies(const TexturesParams ¶msIn,
323 Frequency rebindFrequency,
324 Frequency stateUpdateFrequency)
325 {
326 TexturesParams paramsOut = paramsIn;
327 paramsOut.textureRebindFrequency = rebindFrequency;
328 paramsOut.textureStateUpdateFrequency = stateUpdateFrequency;
329 return paramsOut;
330 }
331
D3D11Params(bool webglCompat,Frequency rebindFrequency,Frequency stateUpdateFrequency)332 TexturesParams D3D11Params(bool webglCompat,
333 Frequency rebindFrequency,
334 Frequency stateUpdateFrequency)
335 {
336 TexturesParams params;
337 params.eglParameters = egl_platform::D3D11_NULL();
338 params.webgl = webglCompat;
339 return ApplyFrequencies(params, rebindFrequency, stateUpdateFrequency);
340 }
341
MetalParams(bool webglCompat,Frequency rebindFrequency,Frequency stateUpdateFrequency)342 TexturesParams MetalParams(bool webglCompat,
343 Frequency rebindFrequency,
344 Frequency stateUpdateFrequency)
345 {
346 TexturesParams params;
347 params.eglParameters = egl_platform::METAL();
348 params.webgl = webglCompat;
349 return ApplyFrequencies(params, rebindFrequency, stateUpdateFrequency);
350 }
351
OpenGLOrGLESParams(bool webglCompat,Frequency rebindFrequency,Frequency stateUpdateFrequency)352 TexturesParams OpenGLOrGLESParams(bool webglCompat,
353 Frequency rebindFrequency,
354 Frequency stateUpdateFrequency)
355 {
356 TexturesParams params;
357 params.eglParameters = egl_platform::OPENGL_OR_GLES_NULL();
358 params.webgl = webglCompat;
359 return ApplyFrequencies(params, rebindFrequency, stateUpdateFrequency);
360 }
361
VulkanParams(bool webglCompat,Frequency rebindFrequency,Frequency stateUpdateFrequency)362 TexturesParams VulkanParams(bool webglCompat,
363 Frequency rebindFrequency,
364 Frequency stateUpdateFrequency)
365 {
366 TexturesParams params;
367 params.eglParameters = egl_platform::VULKAN_NULL();
368 params.webgl = webglCompat;
369 return ApplyFrequencies(params, rebindFrequency, stateUpdateFrequency);
370 }
371
TEST_P(TexturesBenchmark,Run)372 TEST_P(TexturesBenchmark, Run)
373 {
374 run();
375 }
376
377 ANGLE_INSTANTIATE_TEST(TexturesBenchmark,
378 D3D11Params(false, Frequency::Sometimes, Frequency::Sometimes),
379 D3D11Params(true, Frequency::Sometimes, Frequency::Sometimes),
380 D3D11Params(false, Frequency::Always, Frequency::Always),
381 D3D11Params(true, Frequency::Always, Frequency::Always),
382 MetalParams(false, Frequency::Sometimes, Frequency::Sometimes),
383 MetalParams(true, Frequency::Sometimes, Frequency::Sometimes),
384 MetalParams(false, Frequency::Always, Frequency::Always),
385 MetalParams(true, Frequency::Always, Frequency::Always),
386 OpenGLOrGLESParams(false, Frequency::Sometimes, Frequency::Sometimes),
387 OpenGLOrGLESParams(true, Frequency::Sometimes, Frequency::Sometimes),
388 OpenGLOrGLESParams(false, Frequency::Always, Frequency::Always),
389 OpenGLOrGLESParams(true, Frequency::Always, Frequency::Always),
390 VulkanParams(false, Frequency::Sometimes, Frequency::Sometimes),
391 VulkanParams(true, Frequency::Sometimes, Frequency::Sometimes),
392 VulkanParams(false, Frequency::Always, Frequency::Always),
393 VulkanParams(true, Frequency::Always, Frequency::Always),
394 VulkanParams(false, Frequency::Always, Frequency::Never));
395 } // namespace angle
396