1 //
2 // Copyright 2014 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
7 // Based on Hello_Triangle.c from
8 // Book: OpenGL(R) ES 2.0 Programming Guide
9 // Authors: Aaftab Munshi, Dan Ginsburg, Dave Shreiner
10 // ISBN-10: 0321502795
11 // ISBN-13: 9780321502797
12 // Publisher: Addison-Wesley Professional
13 // URLs: http://safari.informit.com/9780321563835
14 // http://www.opengles-book.com
15
16 #include "SampleApplication.h"
17
18 #include "texture_utils.h"
19 #include "util/shader_utils.h"
20
21 #include <cstring>
22 #include <iostream>
23
24 // This sample demonstrates the differences in rendering efficiency when
25 // drawing with already-created textures whose dimensions have been altered
26 // versus drawing with newly created textures.
27 //
28 // In order to support GL's per-level texture creation semantics over the
29 // D3D API in particular, which requires textures' full mip chains to be
30 // created at texture object creation time, ANGLE maintains copies of the
31 // constituent texture images in system memory until the texture is used in
32 // a draw call, at which time, if the texture passes GL's mip completeness
33 // rules, the D3D texture is created and the contents of the texture are
34 // uploaded. Once the texture is created, redefinition of the dimensions or
35 // format of the texture is costly-- a new D3D texture needs to be created,
36 // and ANGLE may need to read the contents back into system memory.
37 //
38 // Creating an entirely new texture also requires that a new D3D texture be
39 // created, but any overhead associated with tracking the already-present
40 // texture images is eliminated, as it's a novel texture. This sample
41 // demonstrates the contrast in draw call time between these two situations.
42 //
43 // The resizing & creation of a new texture is delayed until several frames
44 // after startup, to eliminate draw time differences caused by caching of
45 // rendering state subsequent to the first frame.
46
47 class TexRedefBenchSample : public SampleApplication
48 {
49 public:
TexRedefBenchSample(int argc,char ** argv)50 TexRedefBenchSample(int argc, char **argv)
51 : SampleApplication("Microbench", argc, argv, ClientType::ES2, 1280, 1280),
52 mPixelsResize(nullptr),
53 mPixelsNewTex(nullptr),
54 mTimeFrame(false),
55 mFrameCount(0)
56 {}
57
defineSquareTexture2D(GLuint texId,GLsizei baseDimension,GLenum format,GLenum type,void * data)58 void defineSquareTexture2D(GLuint texId,
59 GLsizei baseDimension,
60 GLenum format,
61 GLenum type,
62 void *data)
63 {
64 glBindTexture(GL_TEXTURE_2D, texId);
65 GLsizei curDim = baseDimension;
66 GLuint level = 0;
67
68 while (curDim >= 1)
69 {
70 glTexImage2D(GL_TEXTURE_2D, level, format, curDim, curDim, 0, format, type, data);
71 curDim /= 2;
72 level++;
73 }
74 }
75
createPixelData()76 void createPixelData()
77 {
78 mPixelsResize = new GLubyte[512 * 512 * 4];
79 mPixelsNewTex = new GLubyte[512 * 512 * 4];
80 GLubyte *pixPtr0 = mPixelsResize;
81 GLubyte *pixPtr1 = mPixelsNewTex;
82 GLubyte zeroPix[] = {0, 192, 192, 255};
83 GLubyte onePix[] = {192, 0, 0, 255};
84 for (int i = 0; i < 512 * 512; ++i)
85 {
86 memcpy(pixPtr0, zeroPix, 4 * sizeof(GLubyte));
87 memcpy(pixPtr1, onePix, 4 * sizeof(GLubyte));
88 pixPtr0 += 4;
89 pixPtr1 += 4;
90 }
91 }
92
initialize()93 bool initialize() override
94 {
95 constexpr char kVS[] = R"(attribute vec4 a_position;
96 attribute vec2 a_texCoord;
97 varying vec2 v_texCoord;
98 void main()
99 {
100 gl_Position = a_position;
101 v_texCoord = a_texCoord;
102 })";
103
104 constexpr char kFS[] = R"(precision mediump float;
105 varying vec2 v_texCoord;
106 uniform sampler2D s_texture;
107 void main()
108 {
109 gl_FragColor = texture2D(s_texture, v_texCoord);
110 })";
111
112 mProgram = CompileProgram(kVS, kFS);
113 if (!mProgram)
114 {
115 return false;
116 }
117
118 // Get the attribute locations
119 mPositionLoc = glGetAttribLocation(mProgram, "a_position");
120 mTexCoordLoc = glGetAttribLocation(mProgram, "a_texCoord");
121
122 // Get the sampler location
123 mSamplerLoc = glGetUniformLocation(mProgram, "s_texture");
124
125 // Generate texture IDs, and create texture 0
126 glGenTextures(3, mTextureIds);
127
128 createPixelData();
129 defineSquareTexture2D(mTextureIds[0], 256, GL_RGBA, GL_UNSIGNED_BYTE, mPixelsResize);
130
131 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
132
133 return true;
134 }
135
destroy()136 void destroy() override
137 {
138 glDeleteProgram(mProgram);
139
140 delete[] mPixelsResize;
141 delete[] mPixelsNewTex;
142 }
143
draw()144 void draw() override
145 {
146 GLfloat vertices[] = {
147 -0.5f, 0.5f, 0.0f, // Position 0
148 0.0f, 0.0f, // TexCoord 0
149 -0.5f, -0.5f, 0.0f, // Position 1
150 0.0f, 1.0f, // TexCoord 1
151 0.5f, -0.5f, 0.0f, // Position 2
152 1.0f, 1.0f, // TexCoord 2
153 0.5f, 0.5f, 0.0f, // Position 3
154 1.0f, 0.0f // TexCoord 3
155 };
156 GLushort indices[] = {0, 1, 2, 0, 2, 3};
157
158 // Set the viewport
159 glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
160
161 // Clear the color buffer
162 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
163
164 // Use the program object
165 glUseProgram(mProgram);
166
167 // Load the vertex position
168 glVertexAttribPointer(mPositionLoc, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vertices);
169 // Load the texture coordinate
170 glVertexAttribPointer(mTexCoordLoc, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat),
171 vertices + 3);
172
173 glEnableVertexAttribArray(mPositionLoc);
174 glEnableVertexAttribArray(mTexCoordLoc);
175
176 // Bind the texture
177 glActiveTexture(GL_TEXTURE0);
178 glBindTexture(GL_TEXTURE_2D, mTextureIds[0]);
179
180 // Set the texture sampler to texture unit to 0
181 glUniform1i(mSamplerLoc, 0);
182
183 // We delay timing of texture resize/creation until after the first frame, as
184 // caching optimizations will reduce draw time for subsequent frames for reasons
185 // unreleated to texture creation. mTimeFrame is set to true on the fifth frame.
186 if (mTimeFrame)
187 {
188 mOrigTimer.start();
189 }
190
191 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
192
193 if (mTimeFrame)
194 {
195 mOrigTimer.stop();
196 // This timer indicates draw time for an already-created texture resident on the GPU,
197 // which needs no updates. It will be faster than the other draws.
198 std::cout << "Original texture draw: " << mOrigTimer.getElapsedWallClockTime() * 1000
199 << "msec" << std::endl;
200
201 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
202
203 // Now, change the texture dimensions of the original texture
204 mResizeDefineTimer.start();
205 defineSquareTexture2D(mTextureIds[0], 512, GL_RGBA, GL_UNSIGNED_BYTE, mPixelsResize);
206 mResizeDefineTimer.stop();
207
208 mResizeDrawTimer.start();
209 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
210 mResizeDrawTimer.stop();
211 // This timer indicates draw time for a texture which has already been used in a draw,
212 // causing the underlying resource to be allocated, and then resized, requiring resource
213 // reallocation and related overhead.
214 std::cout << "Resized texture definition: "
215 << mResizeDefineTimer.getElapsedWallClockTime() * 1000 << "msec" << std::endl;
216 std::cout << "Resized texture draw: "
217 << mResizeDrawTimer.getElapsedWallClockTime() * 1000 << "msec" << std::endl;
218
219 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
220
221 // Create texure at same dimensions we resized previous texture to
222 mNewTexDefineTimer.start();
223 defineSquareTexture2D(mTextureIds[1], 512, GL_RGBA, GL_UNSIGNED_BYTE, mPixelsNewTex);
224 mNewTexDefineTimer.stop();
225
226 mNewTexDrawTimer.start();
227 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
228 mNewTexDrawTimer.stop();
229 // This timer indicates draw time for a texture newly created this frame. The underlying
230 // resource will need to be created, but because it has not previously been used, there
231 // is no already-resident texture object to manage. This draw is expected to be faster
232 // than the resized texture draw.
233 std::cout << "Newly created texture definition: "
234 << mNewTexDefineTimer.getElapsedWallClockTime() * 1000 << "msec" << std::endl;
235 std::cout << "Newly created texture draw: "
236 << mNewTexDrawTimer.getElapsedWallClockTime() * 1000 << "msec" << std::endl;
237 }
238
239 if (mFrameCount == 5)
240 mTimeFrame = true;
241 else
242 mTimeFrame = false;
243
244 mFrameCount++;
245 }
246
247 private:
248 // Handle to a program object
249 GLuint mProgram;
250
251 // Attribute locations
252 GLint mPositionLoc;
253 GLint mTexCoordLoc;
254
255 // Sampler location
256 GLint mSamplerLoc;
257
258 // Texture handle
259 GLuint mTextureIds[2]; // 0: texture created, then resized
260 // 1: texture newly created with TexImage
261
262 // Texture pixel data
263 GLubyte *mPixelsResize;
264 GLubyte *mPixelsNewTex;
265
266 Timer mOrigTimer;
267 Timer mResizeDrawTimer;
268 Timer mResizeDefineTimer;
269 Timer mNewTexDrawTimer;
270 Timer mNewTexDefineTimer;
271 bool mTimeFrame;
272 unsigned int mFrameCount;
273 };
274
main(int argc,char ** argv)275 int main(int argc, char **argv)
276 {
277 TexRedefBenchSample app(argc, argv);
278 return app.run();
279 }
280