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 "util/shader_utils.h"
17
18 namespace angle
19 {
20 constexpr unsigned int kIterationsPerStep = 256;
21
22 struct TexturesParams final : public RenderTestParams
23 {
TexturesParamsangle::TexturesParams24 TexturesParams()
25 {
26 iterationsPerStep = kIterationsPerStep;
27
28 // Common default params
29 majorVersion = 2;
30 minorVersion = 0;
31 windowWidth = 720;
32 windowHeight = 720;
33
34 numTextures = 8;
35 textureRebindFrequency = 5;
36 textureStateUpdateFrequency = 3;
37 textureMipCount = 8;
38
39 webgl = false;
40 }
41
42 std::string story() const override;
43 size_t numTextures;
44 size_t textureRebindFrequency;
45 size_t textureStateUpdateFrequency;
46 size_t textureMipCount;
47
48 bool webgl;
49 };
50
operator <<(std::ostream & os,const TexturesParams & params)51 std::ostream &operator<<(std::ostream &os, const TexturesParams ¶ms)
52 {
53 os << params.backendAndStory().substr(1);
54 return os;
55 }
56
story() const57 std::string TexturesParams::story() const
58 {
59 std::stringstream strstr;
60
61 strstr << RenderTestParams::story();
62 strstr << "_" << numTextures << "_textures";
63 strstr << "_" << textureRebindFrequency << "_rebind";
64 strstr << "_" << textureStateUpdateFrequency << "_state";
65 strstr << "_" << textureMipCount << "_mips";
66
67 if (webgl)
68 {
69 strstr << "_webgl";
70 }
71
72 return strstr.str();
73 }
74
75 class TexturesBenchmark : public ANGLERenderTest,
76 public ::testing::WithParamInterface<TexturesParams>
77 {
78 public:
79 TexturesBenchmark();
80
81 void initializeBenchmark() override;
82 void destroyBenchmark() override;
83 void drawBenchmark() override;
84
85 private:
86 void initShaders();
87 void initTextures();
88
89 std::vector<GLuint> mTextures;
90
91 GLuint mProgram;
92 std::vector<GLuint> mUniformLocations;
93 };
94
TexturesBenchmark()95 TexturesBenchmark::TexturesBenchmark() : ANGLERenderTest("Textures", GetParam()), mProgram(0u)
96 {
97 setWebGLCompatibilityEnabled(GetParam().webgl);
98 setRobustResourceInit(GetParam().webgl);
99 }
100
initializeBenchmark()101 void TexturesBenchmark::initializeBenchmark()
102 {
103 const auto ¶ms = GetParam();
104
105 // Verify the uniform counts are within the limits
106 GLint maxTextureUnits;
107 glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
108 if (params.numTextures > static_cast<size_t>(maxTextureUnits))
109 {
110 FAIL() << "Texture count (" << params.numTextures << ")"
111 << " exceeds maximum texture unit count: " << maxTextureUnits << std::endl;
112 }
113
114 initShaders();
115 initTextures();
116 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
117 glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
118
119 ASSERT_GL_NO_ERROR();
120 }
121
GetUniformLocationName(size_t idx,bool vertexShader)122 std::string GetUniformLocationName(size_t idx, bool vertexShader)
123 {
124 std::stringstream strstr;
125 strstr << (vertexShader ? "vs" : "fs") << "_u_" << idx;
126 return strstr.str();
127 }
128
initShaders()129 void TexturesBenchmark::initShaders()
130 {
131 const auto ¶ms = GetParam();
132
133 std::string vs =
134 "void main()\n"
135 "{\n"
136 " gl_Position = vec4(0, 0, 0, 0);\n"
137 "}\n";
138
139 std::stringstream fstrstr;
140 for (size_t i = 0; i < params.numTextures; i++)
141 {
142 fstrstr << "uniform sampler2D tex" << i << ";";
143 }
144 fstrstr << "void main()\n"
145 "{\n"
146 " gl_FragColor = vec4(0, 0, 0, 0)";
147 for (size_t i = 0; i < params.numTextures; i++)
148 {
149 fstrstr << "+ texture2D(tex" << i << ", vec2(0, 0))";
150 }
151 fstrstr << ";\n"
152 "}\n";
153
154 mProgram = CompileProgram(vs.c_str(), fstrstr.str().c_str());
155 ASSERT_NE(0u, mProgram);
156
157 for (size_t i = 0; i < params.numTextures; ++i)
158 {
159 std::stringstream uniformName;
160 uniformName << "tex" << i;
161
162 GLint location = glGetUniformLocation(mProgram, uniformName.str().c_str());
163 ASSERT_NE(-1, location);
164 mUniformLocations.push_back(location);
165 }
166
167 // Use the program object
168 glUseProgram(mProgram);
169 }
170
initTextures()171 void TexturesBenchmark::initTextures()
172 {
173 const auto ¶ms = GetParam();
174
175 size_t textureSize = static_cast<size_t>(1) << params.textureMipCount;
176 std::vector<GLubyte> textureData(textureSize * textureSize * 4);
177 for (auto &byte : textureData)
178 {
179 byte = rand() % 255u;
180 }
181
182 for (size_t texIndex = 0; texIndex < params.numTextures; texIndex++)
183 {
184 GLuint tex = 0;
185 glGenTextures(1, &tex);
186
187 glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + texIndex));
188 glBindTexture(GL_TEXTURE_2D, tex);
189 for (size_t mip = 0; mip < params.textureMipCount; mip++)
190 {
191 GLsizei levelSize = static_cast<GLsizei>(textureSize >> mip);
192 glTexImage2D(GL_TEXTURE_2D, static_cast<GLint>(mip), GL_RGBA, levelSize, levelSize, 0,
193 GL_RGBA, GL_UNSIGNED_BYTE, textureData.data());
194 }
195 mTextures.push_back(tex);
196
197 glUniform1i(mUniformLocations[texIndex], static_cast<GLint>(texIndex));
198 }
199 }
200
destroyBenchmark()201 void TexturesBenchmark::destroyBenchmark()
202 {
203 glDeleteProgram(mProgram);
204 }
205
drawBenchmark()206 void TexturesBenchmark::drawBenchmark()
207 {
208 const auto ¶ms = GetParam();
209
210 for (size_t it = 0; it < params.iterationsPerStep; ++it)
211 {
212 if (it % params.textureRebindFrequency == 0)
213 {
214 // Swap two textures
215 size_t swapTexture = (it / params.textureRebindFrequency) % (params.numTextures - 1);
216
217 glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + swapTexture));
218 glBindTexture(GL_TEXTURE_2D, mTextures[swapTexture]);
219 glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + swapTexture + 1));
220 glBindTexture(GL_TEXTURE_2D, mTextures[swapTexture + 1]);
221 std::swap(mTextures[swapTexture], mTextures[swapTexture + 1]);
222 }
223
224 if (it % params.textureStateUpdateFrequency == 0)
225 {
226 // Update a texture's state
227 size_t stateUpdateCount = it / params.textureStateUpdateFrequency;
228
229 const size_t numUpdateTextures = 4;
230 ASSERT_LE(numUpdateTextures, params.numTextures);
231
232 size_t firstTexture = stateUpdateCount % (params.numTextures - numUpdateTextures);
233
234 for (size_t updateTextureIdx = 0; updateTextureIdx < numUpdateTextures;
235 updateTextureIdx++)
236 {
237 size_t updateTexture = firstTexture + updateTextureIdx;
238 glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + updateTexture));
239
240 const GLenum minFilters[] = {
241 GL_NEAREST,
242 GL_LINEAR,
243 GL_NEAREST_MIPMAP_NEAREST,
244 GL_LINEAR_MIPMAP_NEAREST,
245 GL_NEAREST_MIPMAP_LINEAR,
246 GL_LINEAR_MIPMAP_LINEAR,
247 };
248 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
249 minFilters[stateUpdateCount % ArraySize(minFilters)]);
250
251 const GLenum magFilters[] = {
252 GL_NEAREST,
253 GL_LINEAR,
254 };
255 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
256 magFilters[stateUpdateCount % ArraySize(magFilters)]);
257
258 const GLenum wrapParameters[] = {
259 GL_CLAMP_TO_EDGE,
260 GL_REPEAT,
261 GL_MIRRORED_REPEAT,
262 };
263 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
264 wrapParameters[stateUpdateCount % ArraySize(wrapParameters)]);
265 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
266 wrapParameters[stateUpdateCount % ArraySize(wrapParameters)]);
267 }
268 }
269
270 glDrawArrays(GL_TRIANGLES, 0, 3);
271 }
272
273 ASSERT_GL_NO_ERROR();
274 }
275
D3D11Params(bool webglCompat,bool frequentUpdate)276 TexturesParams D3D11Params(bool webglCompat, bool frequentUpdate)
277 {
278 TexturesParams params;
279 params.eglParameters = egl_platform::D3D11_NULL();
280 params.webgl = webglCompat;
281 if (frequentUpdate)
282 {
283 params.textureRebindFrequency = 1;
284 params.textureStateUpdateFrequency = 1;
285 }
286 return params;
287 }
288
OpenGLOrGLESParams(bool webglCompat,bool frequentUpdate)289 TexturesParams OpenGLOrGLESParams(bool webglCompat, bool frequentUpdate)
290 {
291 TexturesParams params;
292 params.eglParameters = egl_platform::OPENGL_OR_GLES_NULL();
293 params.webgl = webglCompat;
294 if (frequentUpdate)
295 {
296 params.textureRebindFrequency = 1;
297 params.textureStateUpdateFrequency = 1;
298 }
299 return params;
300 }
301
VulkanParams(bool webglCompat,bool frequentUpdate)302 TexturesParams VulkanParams(bool webglCompat, bool frequentUpdate)
303 {
304 TexturesParams params;
305 params.eglParameters = egl_platform::VULKAN_NULL();
306 params.webgl = webglCompat;
307 if (frequentUpdate)
308 {
309 params.textureRebindFrequency = 1;
310 params.textureStateUpdateFrequency = 1;
311 }
312 return params;
313 }
314
TEST_P(TexturesBenchmark,Run)315 TEST_P(TexturesBenchmark, Run)
316 {
317 run();
318 }
319
320 ANGLE_INSTANTIATE_TEST(TexturesBenchmark,
321 D3D11Params(false, false),
322 D3D11Params(true, false),
323 D3D11Params(false, true),
324 D3D11Params(true, true),
325 OpenGLOrGLESParams(false, false),
326 OpenGLOrGLESParams(true, false),
327 OpenGLOrGLESParams(false, true),
328 OpenGLOrGLESParams(true, true),
329 VulkanParams(false, false),
330 VulkanParams(true, false),
331 VulkanParams(false, true),
332 VulkanParams(true, true));
333 } // namespace angle
334