1// Copyright 2013 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#include "gpu/command_buffer/service/gpu_state_tracer.h"
6
7#include "base/base64.h"
8#include "base/debug/trace_event.h"
9#include "context_state.h"
10#include "ui/gfx/codec/png_codec.h"
11#include "ui/gl/gl_bindings.h"
12
13namespace gpu {
14namespace gles2 {
15namespace {
16
17const int kBytesPerPixel = 4;
18
19class Snapshot : public base::debug::ConvertableToTraceFormat {
20 public:
21  static scoped_refptr<Snapshot> Create(const ContextState* state);
22
23  // Save a screenshot of the currently bound framebuffer.
24  bool SaveScreenshot(const gfx::Size& size);
25
26  // base::debug::ConvertableToTraceFormat implementation.
27  virtual void AppendAsTraceFormat(std::string* out) const OVERRIDE;
28
29 private:
30  explicit Snapshot(const ContextState* state);
31  virtual ~Snapshot() {}
32
33  const ContextState* state_;
34
35  std::vector<unsigned char> screenshot_pixels_;
36  gfx::Size screenshot_size_;
37
38  DISALLOW_COPY_AND_ASSIGN(Snapshot);
39};
40
41}  // namespace
42
43Snapshot::Snapshot(const ContextState* state) : state_(state) {}
44
45scoped_refptr<Snapshot> Snapshot::Create(const ContextState* state) {
46  return scoped_refptr<Snapshot>(new Snapshot(state));
47}
48
49bool Snapshot::SaveScreenshot(const gfx::Size& size) {
50  screenshot_size_ = size;
51  screenshot_pixels_.resize(screenshot_size_.width() *
52                            screenshot_size_.height() * kBytesPerPixel);
53
54  glPixelStorei(GL_PACK_ALIGNMENT, kBytesPerPixel);
55  glReadPixels(0,
56               0,
57               screenshot_size_.width(),
58               screenshot_size_.height(),
59               GL_RGBA,
60               GL_UNSIGNED_BYTE,
61               &screenshot_pixels_[0]);
62  glPixelStorei(GL_PACK_ALIGNMENT, state_->pack_alignment);
63
64  // Flip the screenshot vertically.
65  int bytes_per_row = screenshot_size_.width() * kBytesPerPixel;
66  for (int y = 0; y < screenshot_size_.height() / 2; y++) {
67    for (int x = 0; x < bytes_per_row; x++) {
68      std::swap(screenshot_pixels_[y * bytes_per_row + x],
69                screenshot_pixels_
70                    [(screenshot_size_.height() - y - 1) * bytes_per_row + x]);
71    }
72  }
73  return true;
74}
75
76void Snapshot::AppendAsTraceFormat(std::string* out) const {
77  *out += "{";
78  if (screenshot_pixels_.size()) {
79    std::vector<unsigned char> png_data;
80    int bytes_per_row = screenshot_size_.width() * kBytesPerPixel;
81    bool png_ok = gfx::PNGCodec::Encode(&screenshot_pixels_[0],
82                                        gfx::PNGCodec::FORMAT_RGBA,
83                                        screenshot_size_,
84                                        bytes_per_row,
85                                        false,
86                                        std::vector<gfx::PNGCodec::Comment>(),
87                                        &png_data);
88    DCHECK(png_ok);
89
90    base::StringPiece base64_input(reinterpret_cast<const char*>(&png_data[0]),
91                                   png_data.size());
92    std::string base64_output;
93    Base64Encode(base64_input, &base64_output);
94
95    *out += "\"screenshot\":\"" + base64_output + "\"";
96  }
97  *out += "}";
98}
99
100scoped_ptr<GPUStateTracer> GPUStateTracer::Create(const ContextState* state) {
101  return scoped_ptr<GPUStateTracer>(new GPUStateTracer(state));
102}
103
104GPUStateTracer::GPUStateTracer(const ContextState* state) : state_(state) {
105  TRACE_EVENT_OBJECT_CREATED_WITH_ID(
106      TRACE_DISABLED_BY_DEFAULT("gpu.debug"), "gpu::State", state_);
107}
108
109GPUStateTracer::~GPUStateTracer() {
110  TRACE_EVENT_OBJECT_DELETED_WITH_ID(
111      TRACE_DISABLED_BY_DEFAULT("gpu.debug"), "gpu::State", state_);
112}
113
114void GPUStateTracer::TakeSnapshotWithCurrentFramebuffer(const gfx::Size& size) {
115  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("gpu.debug"),
116               "GPUStateTracer::TakeSnapshotWithCurrentFramebuffer");
117
118  scoped_refptr<Snapshot> snapshot(Snapshot::Create(state_));
119
120  // Only save a screenshot for now.
121  if (!snapshot->SaveScreenshot(size))
122    return;
123
124  TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
125      TRACE_DISABLED_BY_DEFAULT("gpu.debug"),
126      "gpu::State",
127      state_,
128      scoped_refptr<base::debug::ConvertableToTraceFormat>(snapshot));
129}
130
131}  // namespace gles2
132}  // namespace gpu
133