1// Copyright (c) 2011 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
5// Whole-tree processing that's likely to be helpful in multiple render models.
6
7#include "gpu/tools/compositor_model_bench/render_model_utils.h"
8
9#include <cstdlib>
10#include <map>
11#include <set>
12#include <vector>
13
14#include "base/logging.h"
15
16TextureGenerator::TextureGenerator(RenderNode* root)
17    : stage_(DiscoveryStage),
18      images_generated_(0) {
19  DiscoverInputIDs(root);
20  GenerateGLTexIDs();
21  AssignIDMapping();
22  WriteOutNewIDs(root);
23  AllocateImageArray();
24  BuildTextureImages(root);
25}
26
27TextureGenerator::~TextureGenerator() {
28  if (tex_ids_.get()) {
29    glDeleteTextures(discovered_ids_.size(), tex_ids_.get());
30  }
31}
32
33void TextureGenerator::BeginVisitRenderNode(RenderNode* node) {
34  for (size_t n = 0; n < node->num_tiles(); ++n) {
35    Tile* i = node->tile(n);
36    HandleTexture(&i->texID,
37                  node->tile_width(),
38                  node->tile_height(),
39                  GL_RGBA);
40  }
41}
42
43void TextureGenerator::BeginVisitCCNode(CCNode* node) {
44  for (size_t n = 0; n < node->num_textures(); ++n) {
45    Texture* i = node->texture(n);
46    HandleTexture(&i->texID, i->width, i->height, i->format);
47  }
48  BeginVisitRenderNode(node);
49}
50
51void TextureGenerator::DiscoverInputIDs(RenderNode* root) {
52  // Pass 1: see which texture ID's have been used.
53  stage_ = DiscoveryStage;
54  root->Accept(this);
55}
56
57void TextureGenerator::GenerateGLTexIDs() {
58  int numTextures = discovered_ids_.size();
59  tex_ids_.reset(new GLuint[numTextures]);
60  glGenTextures(numTextures, tex_ids_.get());
61}
62
63void TextureGenerator::AssignIDMapping() {
64  // In the original version of this code the assigned ID's were not
65  // GL tex ID's, but newly generated consecutive ID's that indexed
66  // into an array of GL tex ID's. There's no need for this and now
67  // I'm instead generating the GL tex ID's upfront and assigning
68  // *those* in the remapping -- this more accurately reflects the
69  // behavior in Chromium, and it also takes out some design
70  // complexity that came from the extra layer of indirection.
71  // HOWEVER -- when I was assigning my own ID's before, I did some
72  // clever tricks to make sure the assignation was idempotent.
73  // Instead of going to even more clever lengths to preserve that
74  // property, I now just assume that the visitor will encounter each
75  // node (and consequently each texture) exactly once during a
76  // traversal of the tree -- this shouldn't be a hard guarantee
77  // to make.
78  int j = 0;
79  typedef std::set<int>::iterator id_itr;
80  for (id_itr i = discovered_ids_.begin();
81       i != discovered_ids_.end();
82       ++i, ++j) {
83    remapped_ids_[*i] = tex_ids_[j];
84  }
85}
86
87void TextureGenerator::WriteOutNewIDs(RenderNode* root) {
88  // Pass 2: write the new texture ID's back into the texture objects.
89  stage_ = RemappingStage;
90  root->Accept(this);
91}
92
93void TextureGenerator::AllocateImageArray() {
94  image_data_.reset(new ImagePtr[discovered_ids_.size()]);
95  images_generated_ = 0;
96}
97
98void TextureGenerator::BuildTextureImages(RenderNode* root) {
99  // Pass 3: use the texture metadata to generate images for the
100  // textures, and set up the textures for use by OpenGL. This
101  // doesn't *have* to be a separate pass (it could be rolled
102  // into pass 2) but I think this is more clear and performance
103  // shouldn't be bad.
104  stage_ = ImageGenerationStage;
105  root->Accept(this);
106}
107
108void TextureGenerator::HandleTexture(int* texID,
109                                     int width,
110                                     int height,
111                                     GLenum format) {
112  if (*texID == -1)
113    return;    // -1 means it's not a real texture.
114  switch (stage_) {
115    case DiscoveryStage:
116      discovered_ids_.insert(*texID);
117      break;
118    case RemappingStage:
119      *texID = remapped_ids_[*texID];
120      break;
121    case ImageGenerationStage:
122      // Only handle this one if we haven't already built a
123      // texture for its ID.
124      if (ids_for_completed_textures_.count(*texID))
125        return;
126      GenerateImageForTexture(*texID, width, height, format);
127      ids_for_completed_textures_.insert(*texID);
128      break;
129  }
130}
131
132void TextureGenerator::GenerateImageForTexture(int texID,
133                                               int width,
134                                               int height,
135                                               GLenum format) {
136  int bytes_per_pixel = FormatBytesPerPixel(format);
137  DCHECK_LE(bytes_per_pixel, 4);
138  int imgID = images_generated_++;
139  image_data_[imgID].reset(new uint8[width*height*bytes_per_pixel]);
140  // Pick random colors to use for this texture.
141  uint8 random_color[4];
142  for (int c = 0; c < 4; ++c) {
143    random_color[c] = std::rand() % 255;
144  }
145  // Create the image from those colors.
146  for (int x = 0; x < width; ++x) {
147    for (int y = 0; y < height; ++y) {
148      int pix_addr = (y * width + x) * bytes_per_pixel;
149      for (int c = 0; c < bytes_per_pixel; ++c) {
150        bool on = ((x/8) + (y/8)) % 2;
151        uint8 v = on ? random_color[c] : ~random_color[c];
152        (image_data_[imgID])[pix_addr + c] = v;
153      }
154      if (bytes_per_pixel == 4) {    // Randomize alpha.
155        image_data_[imgID][pix_addr + 3] = std::rand() % 255;
156      }
157    }
158  }
159  // Set up GL texture.
160  glBindTexture(GL_TEXTURE_2D, texID);
161  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
162  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
163  glPixelStorei(GL_PACK_ALIGNMENT, 1);
164  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
165  glTexImage2D(GL_TEXTURE_2D,
166               0,
167               format,
168               width, height,
169               0,
170               format,
171               GL_UNSIGNED_BYTE,
172               image_data_[imgID].get());
173}
174
175