texture_upload_perftest.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. // Copyright 2015 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <stddef.h>
  5. #include <stdint.h>
  6. #include <algorithm>
  7. #include <memory>
  8. #include <vector>
  9. #include "base/containers/flat_map.h"
  10. #include "base/logging.h"
  11. #include "base/memory/ref_counted.h"
  12. #include "base/strings/stringprintf.h"
  13. #include "gpu/perftests/measurements.h"
  14. #include "testing/gmock/include/gmock/gmock.h"
  15. #include "testing/gtest/include/gtest/gtest.h"
  16. #include "testing/perf/perf_result_reporter.h"
  17. #include "ui/gfx/geometry/size.h"
  18. #include "ui/gfx/geometry/vector2d_f.h"
  19. #include "ui/gl/gl_bindings.h"
  20. #include "ui/gl/gl_context.h"
  21. #include "ui/gl/gl_enums.h"
  22. #include "ui/gl/gl_surface.h"
  23. #include "ui/gl/gl_utils.h"
  24. #include "ui/gl/gl_version_info.h"
  25. #include "ui/gl/gpu_timing.h"
  26. #include "ui/gl/init/gl_factory.h"
  27. #include "ui/gl/scoped_make_current.h"
  28. namespace gpu {
  29. namespace {
  30. const int kUploadPerfWarmupRuns = 5;
  31. const int kUploadPerfTestRuns = 30;
  32. #define SHADER(Src) #Src
  33. // clang-format off
  34. const char kVertexShader[] =
  35. SHADER(
  36. uniform vec2 translation;
  37. attribute vec2 a_position;
  38. attribute vec2 a_texCoord;
  39. varying vec2 v_texCoord;
  40. void main() {
  41. gl_Position = vec4(
  42. translation.x + a_position.x, translation.y + a_position.y, 0.0, 1.0);
  43. v_texCoord = a_texCoord;
  44. }
  45. );
  46. const char kShaderDefaultFloatPrecision[] =
  47. SHADER(
  48. precision mediump float;
  49. );
  50. const char kFragmentShader[] =
  51. SHADER(
  52. uniform sampler2D a_texture;
  53. varying vec2 v_texCoord;
  54. void main() {
  55. gl_FragColor = texture2D(a_texture, v_texCoord);
  56. }
  57. );
  58. // clang-format on
  59. void CheckNoGlError(const std::string& msg) {
  60. const GLenum error = glGetError();
  61. CHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), error)
  62. << msg << " " << gl::GLEnums::GetStringError(error);
  63. }
  64. // Utility function to compile a shader from a string.
  65. GLuint LoadShader(const GLenum type, const char* const src) {
  66. GLuint shader = 0;
  67. shader = glCreateShader(type);
  68. CHECK_NE(0u, shader);
  69. glShaderSource(shader, 1, &src, nullptr);
  70. glCompileShader(shader);
  71. GLint compiled = 0;
  72. glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
  73. if (compiled == 0) {
  74. GLint len = 0;
  75. glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
  76. if (len > 1) {
  77. std::unique_ptr<char[]> error_log(new char[len]);
  78. glGetShaderInfoLog(shader, len, nullptr, error_log.get());
  79. LOG(ERROR) << "Error compiling shader: " << error_log.get();
  80. }
  81. }
  82. CHECK_NE(0, compiled);
  83. return shader;
  84. }
  85. int GLFormatBytePerPixel(GLenum format) {
  86. DCHECK(format == GL_RGBA || format == GL_LUMINANCE || format == GL_RED_EXT);
  87. return format == GL_RGBA ? 4 : 1;
  88. }
  89. GLenum GLFormatToInternalFormat(GLenum format) {
  90. return format == GL_RED ? GL_R8 : format;
  91. }
  92. GLenum GLFormatToStorageFormat(GLenum format) {
  93. switch (format) {
  94. case GL_RGBA:
  95. return GL_RGBA8;
  96. case GL_LUMINANCE:
  97. return GL_LUMINANCE8;
  98. case GL_RED:
  99. return GL_R8;
  100. default:
  101. NOTREACHED();
  102. }
  103. return 0;
  104. }
  105. void GenerateTextureData(const gfx::Size& size,
  106. int bytes_per_pixel,
  107. const int seed,
  108. std::vector<uint8_t>* const pixels) {
  109. // Row bytes has to be multiple of 4 (GL_PACK_ALIGNMENT defaults to 4).
  110. int stride = ((size.width() * bytes_per_pixel) + 3) & ~0x3;
  111. pixels->resize(size.height() * stride);
  112. for (int y = 0; y < size.height(); ++y) {
  113. for (int x = 0; x < size.width(); ++x) {
  114. for (int channel = 0; channel < bytes_per_pixel; ++channel) {
  115. int index = y * stride + x * bytes_per_pixel;
  116. pixels->at(index) = (index + (seed << 2)) % (0x20 << channel);
  117. }
  118. }
  119. }
  120. }
  121. // Compare a buffer containing pixels in a specified format to GL_RGBA buffer
  122. // where the former buffer have been uploaded as a texture and drawn on the
  123. // RGBA buffer.
  124. bool CompareBufferToRGBABuffer(GLenum format,
  125. const gfx::Size& size,
  126. const std::vector<uint8_t>& pixels,
  127. const std::vector<uint8_t>& rgba) {
  128. int bytes_per_pixel = GLFormatBytePerPixel(format);
  129. int pixels_stride = ((size.width() * bytes_per_pixel) + 3) & ~0x3;
  130. int rgba_stride = size.width() * GLFormatBytePerPixel(GL_RGBA);
  131. for (int y = 0; y < size.height(); ++y) {
  132. for (int x = 0; x < size.width(); ++x) {
  133. int rgba_index = y * rgba_stride + x * GLFormatBytePerPixel(GL_RGBA);
  134. int pixels_index = y * pixels_stride + x * bytes_per_pixel;
  135. uint8_t expected[4] = {0};
  136. switch (format) {
  137. case GL_LUMINANCE: // (L_t, L_t, L_t, 1)
  138. expected[1] = pixels[pixels_index];
  139. expected[2] = pixels[pixels_index];
  140. [[fallthrough]];
  141. case GL_RED: // (R_t, 0, 0, 1)
  142. expected[0] = pixels[pixels_index];
  143. expected[3] = 255;
  144. break;
  145. case GL_RGBA: // (R_t, G_t, B_t, A_t)
  146. memcpy(expected, &pixels[pixels_index], 4);
  147. break;
  148. default:
  149. NOTREACHED();
  150. }
  151. if (memcmp(&rgba[rgba_index], expected, 4)) {
  152. return false;
  153. }
  154. }
  155. }
  156. return true;
  157. }
  158. // PerfTest to check costs of texture upload at different stages
  159. // on different platforms.
  160. class TextureUploadPerfTest : public testing::Test {
  161. public:
  162. TextureUploadPerfTest() : fbo_size_(1024, 1024) {}
  163. // Overridden from testing::Test
  164. void SetUp() override {
  165. // Initialize an offscreen surface and a gl context.
  166. surface_ = gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplay(),
  167. gfx::Size());
  168. gl_context_ =
  169. gl::init::CreateGLContext(nullptr, // share_group
  170. surface_.get(), gl::GLContextAttribs());
  171. ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get());
  172. glGenTextures(1, &color_texture_);
  173. glBindTexture(GL_TEXTURE_2D, color_texture_);
  174. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  175. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  176. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  177. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  178. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo_size_.width(),
  179. fbo_size_.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
  180. glGenFramebuffersEXT(1, &framebuffer_object_);
  181. glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer_object_);
  182. glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  183. GL_TEXTURE_2D, color_texture_, 0);
  184. DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
  185. glCheckFramebufferStatusEXT(GL_FRAMEBUFFER));
  186. glViewport(0, 0, fbo_size_.width(), fbo_size_.height());
  187. gpu_timing_client_ = gl_context_->CreateGPUTimingClient();
  188. if (gpu_timing_client_->IsAvailable()) {
  189. LOG(INFO) << "Gpu timing initialized with timer type: "
  190. << gpu_timing_client_->GetTimerTypeName();
  191. } else {
  192. LOG(WARNING) << "Can't initialize gpu timing";
  193. }
  194. // Prepare a simple program and a vertex buffer that will be
  195. // used to draw a quad on the offscreen surface.
  196. vertex_shader_ = LoadShader(GL_VERTEX_SHADER, kVertexShader);
  197. bool is_gles = gl_context_->GetVersionInfo()->is_es;
  198. fragment_shader_ = LoadShader(
  199. GL_FRAGMENT_SHADER,
  200. base::StringPrintf("%s%s", is_gles ? kShaderDefaultFloatPrecision : "",
  201. kFragmentShader).c_str());
  202. program_object_ = glCreateProgram();
  203. CHECK_NE(0u, program_object_);
  204. glAttachShader(program_object_, vertex_shader_);
  205. glAttachShader(program_object_, fragment_shader_);
  206. glBindAttribLocation(program_object_, 0, "a_position");
  207. glBindAttribLocation(program_object_, 1, "a_texCoord");
  208. glLinkProgram(program_object_);
  209. GLint linked = -1;
  210. glGetProgramiv(program_object_, GL_LINK_STATUS, &linked);
  211. CHECK_NE(0, linked);
  212. glUseProgram(program_object_);
  213. glUniform1i(sampler_location_, 0);
  214. translation_location_ =
  215. glGetUniformLocation(program_object_, "translation");
  216. DCHECK_NE(-1, translation_location_);
  217. glUniform2f(translation_location_, 0.0f, 0.0f);
  218. sampler_location_ = glGetUniformLocation(program_object_, "a_texture");
  219. CHECK_NE(-1, sampler_location_);
  220. glGenBuffersARB(1, &vertex_buffer_);
  221. CHECK_NE(0u, vertex_buffer_);
  222. DCHECK_NE(0u, vertex_buffer_);
  223. glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
  224. glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, 0);
  225. glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4,
  226. reinterpret_cast<void*>(sizeof(GLfloat) * 2));
  227. glEnableVertexAttribArray(0);
  228. glEnableVertexAttribArray(1);
  229. CheckNoGlError("glEnableVertexAttribArray");
  230. has_texture_storage_ =
  231. gl_context_->GetVersionInfo()->is_es3 ||
  232. gl_context_->HasExtension("GL_EXT_texture_storage") ||
  233. gl_context_->HasExtension("GL_ARB_texture_storage");
  234. }
  235. void GenerateVertexBuffer(const gfx::Size& size) {
  236. DCHECK_NE(0u, vertex_buffer_);
  237. glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
  238. // right and top are in clipspace
  239. float right = -1.f + 2.f * size.width() / fbo_size_.width();
  240. float top = -1.f + 2.f * size.height() / fbo_size_.height();
  241. // Four vertexes, one per line. Each vertex has two components per
  242. // position and two per texcoord.
  243. // It represents a quad formed by two triangles if interpreted
  244. // as a tristrip.
  245. // clang-format off
  246. GLfloat data[16] = {
  247. -1.f, -1.f, 0.f, 0.f,
  248. right, -1.f, 1.f, 0.f,
  249. -1.f, top, 0.f, 1.f,
  250. right, top, 1.f, 1.f};
  251. // clang-format on
  252. glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
  253. CheckNoGlError("glBufferData");
  254. }
  255. void TearDown() override {
  256. ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get());
  257. glDeleteProgram(program_object_);
  258. glDeleteShader(vertex_shader_);
  259. glDeleteShader(fragment_shader_);
  260. glDeleteBuffersARB(1, &vertex_buffer_);
  261. glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
  262. glDeleteFramebuffersEXT(1, &framebuffer_object_);
  263. glDeleteTextures(1, &color_texture_);
  264. CheckNoGlError("glDeleteTextures");
  265. gpu_timing_client_ = nullptr;
  266. gl_context_ = nullptr;
  267. surface_ = nullptr;
  268. }
  269. protected:
  270. GLuint CreateGLTexture(const GLenum format,
  271. const gfx::Size& size,
  272. const bool specify_storage) {
  273. GLuint texture_id = 0;
  274. glActiveTexture(GL_TEXTURE0);
  275. glGenTextures(1, &texture_id);
  276. glBindTexture(GL_TEXTURE_2D, texture_id);
  277. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  278. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  279. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  280. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  281. if (specify_storage) {
  282. if (has_texture_storage_) {
  283. glTexStorage2DEXT(GL_TEXTURE_2D, 1, GLFormatToStorageFormat(format),
  284. size.width(), size.height());
  285. CheckNoGlError("glTexStorage2DEXT");
  286. } else {
  287. glTexImage2D(GL_TEXTURE_2D, 0, GLFormatToInternalFormat(format),
  288. size.width(), size.height(), 0, format, GL_UNSIGNED_BYTE,
  289. nullptr);
  290. CheckNoGlError("glTexImage2D");
  291. }
  292. }
  293. return texture_id;
  294. }
  295. void UploadTexture(GLuint texture_id,
  296. const gfx::Size& size,
  297. const std::vector<uint8_t>& pixels,
  298. GLenum format,
  299. const bool subimage) {
  300. if (subimage) {
  301. glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.width(), size.height(),
  302. format, GL_UNSIGNED_BYTE, &pixels[0]);
  303. CheckNoGlError("glTexSubImage2D");
  304. } else {
  305. glTexImage2D(GL_TEXTURE_2D, 0, GLFormatToInternalFormat(format),
  306. size.width(), size.height(), 0, format, GL_UNSIGNED_BYTE,
  307. &pixels[0]);
  308. CheckNoGlError("glTexImage2D");
  309. }
  310. }
  311. // Upload and draw on the offscren surface.
  312. // Return a list of pair. Each pair describe a gl operation and the wall
  313. // time elapsed in milliseconds.
  314. std::vector<Measurement> UploadAndDraw(GLuint texture_id,
  315. const gfx::Size& size,
  316. const std::vector<uint8_t>& pixels,
  317. const GLenum format,
  318. const bool subimage) {
  319. MeasurementTimers tex_timers(gpu_timing_client_.get());
  320. UploadTexture(texture_id, size, pixels, format, subimage);
  321. tex_timers.Record();
  322. MeasurementTimers first_draw_timers(gpu_timing_client_.get());
  323. glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  324. first_draw_timers.Record();
  325. MeasurementTimers draw_timers(gpu_timing_client_.get());
  326. glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  327. draw_timers.Record();
  328. MeasurementTimers finish_timers(gpu_timing_client_.get());
  329. glFinish();
  330. CheckNoGlError("glFinish");
  331. finish_timers.Record();
  332. std::vector<uint8_t> pixels_rendered(size.GetArea() * 4);
  333. glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_BYTE,
  334. &pixels_rendered[0]);
  335. CheckNoGlError("glReadPixels");
  336. EXPECT_TRUE(
  337. CompareBufferToRGBABuffer(format, size, pixels, pixels_rendered))
  338. << "Format is: " << gl::GLEnums::GetStringEnum(format);
  339. std::vector<Measurement> measurements;
  340. bool gpu_timer_errors =
  341. gpu_timing_client_->IsAvailable() &&
  342. gpu_timing_client_->CheckAndResetTimerErrors();
  343. if (!gpu_timer_errors) {
  344. measurements.push_back(tex_timers.GetAsMeasurement(
  345. subimage ? "texsubimage2d" : "teximage2d"));
  346. measurements.push_back(
  347. first_draw_timers.GetAsMeasurement("firstdrawarrays"));
  348. measurements.push_back(draw_timers.GetAsMeasurement("drawarrays"));
  349. measurements.push_back(finish_timers.GetAsMeasurement("finish"));
  350. }
  351. return measurements;
  352. }
  353. void RunUploadAndDrawMultipleTimes(const gfx::Size& size,
  354. const GLenum format,
  355. const bool subimage) {
  356. std::vector<uint8_t> pixels;
  357. base::flat_map<std::string, Measurement> aggregates; // indexed by name
  358. int successful_runs = 0;
  359. GLuint texture_id = CreateGLTexture(format, size, subimage);
  360. for (int i = 0; i < kUploadPerfWarmupRuns + kUploadPerfTestRuns; ++i) {
  361. GenerateTextureData(size, GLFormatBytePerPixel(format), i + 1, &pixels);
  362. auto run = UploadAndDraw(texture_id, size, pixels, format, subimage);
  363. if (i < kUploadPerfWarmupRuns || run.empty()) {
  364. continue;
  365. }
  366. successful_runs++;
  367. for (const Measurement& measurement : run) {
  368. auto& aggregate = aggregates[measurement.metric_basename];
  369. aggregate.metric_basename = measurement.metric_basename;
  370. aggregate.Increment(measurement);
  371. }
  372. }
  373. glDeleteTextures(1, &texture_id);
  374. std::string story_name = base::StringPrintf(
  375. "%d_%s", size.width(), gl::GLEnums::GetStringEnum(format).c_str());
  376. if (subimage) {
  377. story_name += "_sub";
  378. }
  379. if (successful_runs) {
  380. for (const auto& entry : aggregates) {
  381. const auto m = entry.second.Divide(successful_runs);
  382. m.PrintResult(story_name);
  383. }
  384. }
  385. auto reporter = std::make_unique<perf_test::PerfResultReporter>(
  386. "sample_runs", story_name);
  387. reporter->RegisterImportantMetric("", "count");
  388. reporter->AddResult("", static_cast<size_t>(successful_runs));
  389. }
  390. const gfx::Size fbo_size_; // for the fbo
  391. scoped_refptr<gl::GLContext> gl_context_;
  392. scoped_refptr<gl::GLSurface> surface_;
  393. scoped_refptr<gl::GPUTimingClient> gpu_timing_client_;
  394. GLuint color_texture_ = 0;
  395. GLuint framebuffer_object_ = 0;
  396. GLuint vertex_shader_ = 0;
  397. GLuint fragment_shader_ = 0;
  398. GLuint program_object_ = 0;
  399. GLint sampler_location_ = -1;
  400. GLint translation_location_ = -1;
  401. GLuint vertex_buffer_ = 0;
  402. bool has_texture_storage_ = false;
  403. };
  404. // Perf test that generates, uploads and draws a texture on a surface repeatedly
  405. // and prints out aggregated measurements for all the runs.
  406. TEST_F(TextureUploadPerfTest, upload) {
  407. int sizes[] = {21, 128, 256, 512, 1024};
  408. std::vector<GLenum> formats;
  409. formats.push_back(GL_RGBA);
  410. if (!gl_context_->GetVersionInfo()->is_es3) {
  411. // Used by default for ResourceProvider::yuv_resource_format_.
  412. formats.push_back(GL_LUMINANCE);
  413. }
  414. ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get());
  415. const bool has_texture_rg = gl_context_->GetVersionInfo()->is_es3 ||
  416. gl_context_->HasExtension("GL_EXT_texture_rg") ||
  417. gl_context_->HasExtension("GL_ARB_texture_rg");
  418. if (has_texture_rg) {
  419. // Used as ResourceProvider::yuv_resource_format_ if
  420. // {ARB,EXT}_texture_rg are available.
  421. formats.push_back(GL_RED);
  422. }
  423. for (int side : sizes) {
  424. ASSERT_GE(fbo_size_.width(), side);
  425. ASSERT_GE(fbo_size_.height(), side);
  426. gfx::Size size(side, side);
  427. GenerateVertexBuffer(size);
  428. for (GLenum format : formats) {
  429. RunUploadAndDrawMultipleTimes(size, format, true); // use glTexSubImage2D
  430. RunUploadAndDrawMultipleTimes(size, format, false); // use glTexImage2D
  431. }
  432. }
  433. }
  434. // Perf test to check if the driver is doing texture renaming.
  435. // This test creates one GL texture_id and four different images. For
  436. // every image it uploads it using texture_id and it draws multiple
  437. // times. The cpu/wall time and the gpu time for all the uploads and
  438. // draws, but before glFinish, is computed and is printed out at the end as
  439. // "upload_and_draw". If the gpu time is >> than the cpu/wall time we expect the
  440. // driver to do texture renaming: this means that while the gpu is drawing using
  441. // texture_id it didn't block cpu side the texture upload using the same
  442. // texture_id.
  443. TEST_F(TextureUploadPerfTest, renaming) {
  444. gfx::Size texture_size(fbo_size_.width() / 2, fbo_size_.height() / 2);
  445. std::vector<uint8_t> pixels[4];
  446. for (int i = 0; i < 4; ++i) {
  447. GenerateTextureData(texture_size, 4, i + 1, &pixels[i]);
  448. }
  449. ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get());
  450. GenerateVertexBuffer(texture_size);
  451. gfx::Vector2dF positions[] = {gfx::Vector2dF(0.f, 0.f),
  452. gfx::Vector2dF(1.f, 0.f),
  453. gfx::Vector2dF(0.f, 1.f),
  454. gfx::Vector2dF(1.f, 1.f)};
  455. GLuint texture_id = CreateGLTexture(GL_RGBA, texture_size, true);
  456. MeasurementTimers upload_and_draw_timers(gpu_timing_client_.get());
  457. for (int i = 0; i < 4; ++i) {
  458. UploadTexture(texture_id, texture_size, pixels[i % 4], GL_RGBA, true);
  459. DCHECK_NE(-1, translation_location_);
  460. glUniform2f(translation_location_, positions[i % 4].x(),
  461. positions[i % 4].y());
  462. // Draw the same quad multiple times to make sure that the time spent on the
  463. // gpu is more than the cpu time.
  464. for (int draw = 0; draw < 128; ++draw) {
  465. glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  466. }
  467. }
  468. upload_and_draw_timers.Record();
  469. MeasurementTimers finish_timers(gpu_timing_client_.get());
  470. glFinish();
  471. CheckNoGlError("glFinish");
  472. finish_timers.Record();
  473. glDeleteTextures(1, &texture_id);
  474. for (int i = 0; i < 4; ++i) {
  475. std::vector<uint8_t> pixels_rendered(texture_size.GetArea() * 4);
  476. glReadPixels(texture_size.width() * positions[i].x(),
  477. texture_size.height() * positions[i].y(), texture_size.width(),
  478. texture_size.height(), GL_RGBA, GL_UNSIGNED_BYTE,
  479. &pixels_rendered[0]);
  480. CheckNoGlError("glReadPixels");
  481. ASSERT_EQ(pixels[i].size(), pixels_rendered.size());
  482. EXPECT_EQ(pixels[i], pixels_rendered);
  483. }
  484. bool gpu_timer_errors = gpu_timing_client_->IsAvailable() &&
  485. gpu_timing_client_->CheckAndResetTimerErrors();
  486. if (!gpu_timer_errors) {
  487. upload_and_draw_timers.GetAsMeasurement("upload_and_draw")
  488. .PrintResult("renaming");
  489. finish_timers.GetAsMeasurement("finish").PrintResult("renaming");
  490. }
  491. }
  492. } // namespace
  493. } // namespace gpu