|  | // Copyright 2013 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. | 
|  |  | 
|  | #include "gpu/command_buffer/service/gpu_state_tracer.h" | 
|  |  | 
|  | #include "base/base64.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/trace_event/trace_event.h" | 
|  | #include "context_state.h" | 
|  | #include "ui/gfx/codec/png_codec.h" | 
|  | #include "ui/gl/gl_bindings.h" | 
|  |  | 
|  | namespace gpu { | 
|  | namespace gles2 { | 
|  | namespace { | 
|  |  | 
|  | const int kBytesPerPixel = 4; | 
|  |  | 
|  | class Snapshot : public base::trace_event::ConvertableToTraceFormat { | 
|  | public: | 
|  | static std::unique_ptr<Snapshot> Create(const ContextState* state); | 
|  |  | 
|  | ~Snapshot() override {} | 
|  |  | 
|  | // Save a screenshot of the currently bound framebuffer. | 
|  | bool SaveScreenshot(const gfx::Size& size); | 
|  |  | 
|  | // base::trace_event::ConvertableToTraceFormat implementation. | 
|  | void AppendAsTraceFormat(std::string* out) const override; | 
|  |  | 
|  | private: | 
|  | explicit Snapshot(const ContextState* state); | 
|  |  | 
|  | const ContextState* state_; | 
|  |  | 
|  | std::vector<unsigned char> screenshot_pixels_; | 
|  | gfx::Size screenshot_size_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(Snapshot); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | Snapshot::Snapshot(const ContextState* state) : state_(state) {} | 
|  |  | 
|  | std::unique_ptr<Snapshot> Snapshot::Create(const ContextState* state) { | 
|  | return base::WrapUnique(new Snapshot(state)); | 
|  | } | 
|  |  | 
|  | bool Snapshot::SaveScreenshot(const gfx::Size& size) { | 
|  | screenshot_size_ = size; | 
|  | screenshot_pixels_.resize(screenshot_size_.width() * | 
|  | screenshot_size_.height() * kBytesPerPixel); | 
|  |  | 
|  | glPixelStorei(GL_PACK_ALIGNMENT, kBytesPerPixel); | 
|  | glReadPixels(0, | 
|  | 0, | 
|  | screenshot_size_.width(), | 
|  | screenshot_size_.height(), | 
|  | GL_RGBA, | 
|  | GL_UNSIGNED_BYTE, | 
|  | &screenshot_pixels_[0]); | 
|  | glPixelStorei(GL_PACK_ALIGNMENT, state_->pack_alignment); | 
|  |  | 
|  | // Flip the screenshot vertically. | 
|  | int bytes_per_row = screenshot_size_.width() * kBytesPerPixel; | 
|  | for (int y = 0; y < screenshot_size_.height() / 2; y++) { | 
|  | for (int x = 0; x < bytes_per_row; x++) { | 
|  | std::swap(screenshot_pixels_[y * bytes_per_row + x], | 
|  | screenshot_pixels_ | 
|  | [(screenshot_size_.height() - y - 1) * bytes_per_row + x]); | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void Snapshot::AppendAsTraceFormat(std::string* out) const { | 
|  | *out += "{"; | 
|  | if (screenshot_pixels_.size()) { | 
|  | std::vector<unsigned char> png_data; | 
|  | int bytes_per_row = screenshot_size_.width() * kBytesPerPixel; | 
|  | bool png_ok = gfx::PNGCodec::Encode(&screenshot_pixels_[0], | 
|  | gfx::PNGCodec::FORMAT_RGBA, | 
|  | screenshot_size_, | 
|  | bytes_per_row, | 
|  | false, | 
|  | std::vector<gfx::PNGCodec::Comment>(), | 
|  | &png_data); | 
|  | DCHECK(png_ok); | 
|  |  | 
|  | base::StringPiece base64_input(reinterpret_cast<const char*>(&png_data[0]), | 
|  | png_data.size()); | 
|  | std::string base64_output; | 
|  | Base64Encode(base64_input, &base64_output); | 
|  |  | 
|  | *out += "\"screenshot\":\"" + base64_output + "\""; | 
|  | } | 
|  | *out += "}"; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<GPUStateTracer> GPUStateTracer::Create( | 
|  | const ContextState* state) { | 
|  | return std::unique_ptr<GPUStateTracer>(new GPUStateTracer(state)); | 
|  | } | 
|  |  | 
|  | GPUStateTracer::GPUStateTracer(const ContextState* state) : state_(state) { | 
|  | TRACE_EVENT_OBJECT_CREATED_WITH_ID( | 
|  | TRACE_DISABLED_BY_DEFAULT("gpu.debug"), "gpu::State", state_); | 
|  | } | 
|  |  | 
|  | GPUStateTracer::~GPUStateTracer() { | 
|  | TRACE_EVENT_OBJECT_DELETED_WITH_ID( | 
|  | TRACE_DISABLED_BY_DEFAULT("gpu.debug"), "gpu::State", state_); | 
|  | } | 
|  |  | 
|  | void GPUStateTracer::TakeSnapshotWithCurrentFramebuffer(const gfx::Size& size) { | 
|  | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("gpu.debug"), | 
|  | "GPUStateTracer::TakeSnapshotWithCurrentFramebuffer"); | 
|  |  | 
|  | std::unique_ptr<Snapshot> snapshot(Snapshot::Create(state_)); | 
|  |  | 
|  | // Only save a screenshot for now. | 
|  | if (!snapshot->SaveScreenshot(size)) | 
|  | return; | 
|  |  | 
|  | TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(TRACE_DISABLED_BY_DEFAULT("gpu.debug"), | 
|  | "gpu::State", state_, | 
|  | std::move(snapshot)); | 
|  | } | 
|  |  | 
|  | }  // namespace gles2 | 
|  | }  // namespace gpu |