summaryrefslogtreecommitdiffstats
path: root/gpu/tools/compositor_model_bench/render_model_utils.cc
blob: d2bed4603586a506a0f8c5199b3a54756ad7be30 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Whole-tree processing that's likely to be helpful in multiple render models.

#include "gpu/tools/compositor_model_bench/render_model_utils.h"

#include <cstdlib>
#include <map>
#include <set>
#include <vector>

#include "base/logging.h"

TextureGenerator::TextureGenerator(RenderNode* root)
    : stage_(DiscoveryStage),
      tex_ids_(NULL),
      image_data_(NULL),
      images_generated_(0) {
  DiscoverInputIDs(root);
  GenerateGLTexIDs();
  AssignIDMapping();
  WriteOutNewIDs(root);
  AllocateImageArray();
  BuildTextureImages(root);
}

TextureGenerator::~TextureGenerator() {
  if (tex_ids_.get()) {
    glDeleteTextures(discovered_ids_.size(), tex_ids_.get());
  }
}

void TextureGenerator::BeginVisitRenderNode(RenderNode* node) {
  for (size_t n = 0; n < node->num_tiles(); ++n) {
    Tile* i = node->tile(n);
    HandleTexture(&i->texID,
                  node->tile_width(),
                  node->tile_height(),
                  GL_RGBA);
  }
}

void TextureGenerator::BeginVisitCCNode(CCNode* node) {
  for (size_t n = 0; n < node->num_textures(); ++n) {
    Texture* i = node->texture(n);
    HandleTexture(&i->texID, i->width, i->height, i->format);
  }
  BeginVisitRenderNode(node);
}

void TextureGenerator::DiscoverInputIDs(RenderNode* root) {
  // Pass 1: see which texture ID's have been used.
  stage_ = DiscoveryStage;
  root->Accept(this);
}

void TextureGenerator::GenerateGLTexIDs() {
  int numTextures = discovered_ids_.size();
  tex_ids_.reset(new GLuint[numTextures]);
  glGenTextures(numTextures, tex_ids_.get());
}

void TextureGenerator::AssignIDMapping() {
  // In the original version of this code the assigned ID's were not
  // GL tex ID's, but newly generated consecutive ID's that indexed
  // into an array of GL tex ID's. There's no need for this and now
  // I'm instead generating the GL tex ID's upfront and assigning
  // *those* in the remapping -- this more accurately reflects the
  // behavior in Chromium, and it also takes out some design
  // complexity that came from the extra layer of indirection.
  // HOWEVER -- when I was assigning my own ID's before, I did some
  // clever tricks to make sure the assignation was idempotent.
  // Instead of going to even more clever lengths to preserve that
  // property, I now just assume that the visitor will encounter each
  // node (and consequently each texture) exactly once during a
  // traversal of the tree -- this shouldn't be a hard guarantee
  // to make.
  int j = 0;
  typedef std::set<int>::iterator id_itr;
  for (id_itr i = discovered_ids_.begin();
       i != discovered_ids_.end();
       ++i, ++j) {
    remapped_ids_[*i] = tex_ids_[j];
  }
}

void TextureGenerator::WriteOutNewIDs(RenderNode* root) {
  // Pass 2: write the new texture ID's back into the texture objects.
  stage_ = RemappingStage;
  root->Accept(this);
}

void TextureGenerator::AllocateImageArray() {
  image_data_.reset(new ImagePtr[discovered_ids_.size()]);
  images_generated_ = 0;
}

void TextureGenerator::BuildTextureImages(RenderNode* root) {
  // Pass 3: use the texture metadata to generate images for the
  // textures, and set up the textures for use by OpenGL. This
  // doesn't *have* to be a separate pass (it could be rolled
  // into pass 2) but I think this is more clear and performance
  // shouldn't be bad.
  stage_ = ImageGenerationStage;
  root->Accept(this);
}

void TextureGenerator::HandleTexture(int* texID,
                                     int width,
                                     int height,
                                     GLenum format) {
  if (*texID == -1)
    return;    // -1 means it's not a real texture.
  switch (stage_) {
    case DiscoveryStage:
      discovered_ids_.insert(*texID);
      break;
    case RemappingStage:
      *texID = remapped_ids_[*texID];
      break;
    case ImageGenerationStage:
      // Only handle this one if we haven't already built a
      // texture for its ID.
      if (ids_for_completed_textures_.count(*texID))
        return;
      GenerateImageForTexture(*texID, width, height, format);
      ids_for_completed_textures_.insert(*texID);
      break;
  }
}

void TextureGenerator::GenerateImageForTexture(int texID,
                                               int width,
                                               int height,
                                               GLenum format) {
  int bytes_per_pixel = FormatBytesPerPixel(format);
  DCHECK_LE(bytes_per_pixel, 4);
  int imgID = images_generated_++;
  image_data_[imgID].reset(new uint8[width*height*bytes_per_pixel]);
  // Pick random colors to use for this texture.
  uint8 random_color[4];
  for (int c = 0; c < 4; ++c) {
    random_color[c] = std::rand() % 255;
  }
  // Create the image from those colors.
  for (int x = 0; x < width; ++x) {
    for (int y = 0; y < height; ++y) {
      int pix_addr = (y * width + x) * bytes_per_pixel;
      for (int c = 0; c < bytes_per_pixel; ++c) {
        bool on = ((x/8) + (y/8)) % 2;
        uint8 v = on ? random_color[c] : ~random_color[c];
        (image_data_[imgID])[pix_addr + c] = v;
      }
      if (bytes_per_pixel == 4) {    // Randomize alpha.
        image_data_[imgID][pix_addr + 3] = std::rand() % 255;
      }
    }
  }
  // Set up GL texture.
  glBindTexture(GL_TEXTURE_2D, texID);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glPixelStorei(GL_PACK_ALIGNMENT, 1);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glTexImage2D(GL_TEXTURE_2D,
               0,
               format,
               width, height,
               0,
               format,
               GL_UNSIGNED_BYTE,
               image_data_[imgID].get());
}