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)276 TexturesParams D3D11Params(bool webglCompat)
277 {
278 TexturesParams params;
279 params.eglParameters = egl_platform::D3D11_NULL();
280 params.webgl = webglCompat;
281 return params;
282 }
283
D3D9Params(bool webglCompat)284 TexturesParams D3D9Params(bool webglCompat)
285 {
286 TexturesParams params;
287 params.eglParameters = egl_platform::D3D9_NULL();
288 params.webgl = webglCompat;
289 return params;
290 }
291
OpenGLOrGLESParams(bool webglCompat)292 TexturesParams OpenGLOrGLESParams(bool webglCompat)
293 {
294 TexturesParams params;
295 params.eglParameters = egl_platform::OPENGL_OR_GLES_NULL();
296 params.webgl = webglCompat;
297 return params;
298 }
299
TEST_P(TexturesBenchmark,Run)300 TEST_P(TexturesBenchmark, Run)
301 {
302 run();
303 }
304
305 ANGLE_INSTANTIATE_TEST(TexturesBenchmark,
306 D3D11Params(false),
307 D3D11Params(true),
308 D3D9Params(true),
309 OpenGLOrGLESParams(false),
310 OpenGLOrGLESParams(true));
311 } // namespace angle
312