| // Copyright 2013 The Chromium Authors | |
| // 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 <string_view> | |
| #include <vector> | |
| #include "base/base64.h" | |
| #include "base/memory/ptr_util.h" | |
| #include "base/memory/raw_ptr.h" | |
| #include "base/trace_event/trace_event.h" | |
| #include "context_state.h" | |
| #include "third_party/perfetto/include/perfetto/tracing/track_event_args.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(const Snapshot&) = delete; | |
| Snapshot& operator=(const Snapshot&) = delete; | |
| ~Snapshot() override = default; | |
| // 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); | |
| raw_ptr<const ContextState> state_; | |
| std::vector<unsigned char> screenshot_pixels_; | |
| gfx::Size screenshot_size_; | |
| }; | |
| } // 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::optional<std::vector<uint8_t>> png_data = gfx::PNGCodec::Encode( | |
| screenshot_pixels_.data(), gfx::PNGCodec::FORMAT_RGBA, screenshot_size_, | |
| /*row_byte_width=*/screenshot_size_.width() * kBytesPerPixel, | |
| /*discard_transparency=*/false, std::vector<gfx::PNGCodec::Comment>()); | |
| DCHECK(png_data); | |
| std::string base64_output = | |
| base::Base64Encode(png_data.value_or(std::vector<uint8_t>())); | |
| *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_INSTANT(TRACE_DISABLED_BY_DEFAULT("gpu.debug"), | |
| "gpu::State:created", | |
| perfetto::Flow::FromPointer(state_, "GPUStateTracer")); | |
| } | |
| GPUStateTracer::~GPUStateTracer() { | |
| TRACE_EVENT_INSTANT( | |
| TRACE_DISABLED_BY_DEFAULT("gpu.debug"), "gpu::State:deleted", | |
| perfetto::TerminatingFlow::FromPointer(state_, "GPUStateTracer")); | |
| } | |
| 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_INSTANT(TRACE_DISABLED_BY_DEFAULT("gpu.debug"), | |
| "gpu::State:screenshot", | |
| perfetto::Flow::FromPointer(state_, "GPUStateTracer"), | |
| "snapshot", std::move(snapshot)); | |
| } | |
| } // namespace gles2 | |
| } // namespace gpu |