Commit da6832a5 authored by Daniel Nicoara's avatar Daniel Nicoara Committed by Commit Bot

ozone: headless: Dump pixels to file

Add support for file output to the SwiftShader backend for the headless
platform.

Bug: None
Test: Manually
Change-Id: I5d1f9fce3cd493ceba8c9f3a882bd3fc0586931d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1842678Reviewed-by: default avatarRobert Kroeger <rjkroege@chromium.org>
Commit-Queue: Daniel Nicoara <dnicoara@chromium.org>
Cr-Commit-Position: refs/heads/master@{#703884}
parent f360fe0a
......@@ -34,12 +34,34 @@ namespace {
const base::FilePath::CharType kDevNull[] = FILE_PATH_LITERAL("/dev/null");
base::FilePath GetPathForWidget(const base::FilePath& base_path,
gfx::AcceleratedWidget widget) {
if (base_path.empty() || base_path == base::FilePath(kDevNull))
return base_path;
// Disambiguate multiple window output files with the window id.
#if defined(OS_WIN)
std::string path =
base::NumberToString(reinterpret_cast<int>(widget)) + ".png";
std::wstring wpath(path.begin(), path.end());
return base_path.Append(wpath);
#else
return base_path.Append(base::NumberToString(widget) + ".png");
#endif
}
void WriteDataToFile(const base::FilePath& location, const SkBitmap& bitmap) {
DCHECK(!location.empty());
std::vector<unsigned char> png_data;
gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, true, &png_data);
base::WriteFile(location, reinterpret_cast<const char*>(png_data.data()),
png_data.size());
if (base::WriteFile(location, reinterpret_cast<const char*>(png_data.data()),
png_data.size()) < 0) {
static bool logged_once = false;
LOG_IF(ERROR, !logged_once)
<< "Failed to write frame to file. "
"If running with the GPU process try --no-sandbox.";
logged_once = true;
}
}
// TODO(altimin): Find a proper way to capture rendering output.
......@@ -78,6 +100,40 @@ class FileSurface : public SurfaceOzoneCanvas {
sk_sp<SkSurface> surface_;
};
class FileGLSurface : public GLSurfaceEglReadback {
public:
explicit FileGLSurface(const base::FilePath& location)
: location_(location) {}
private:
~FileGLSurface() override = default;
// GLSurfaceEglReadback:
bool HandlePixels(uint8_t* pixels) override {
if (location_.empty())
return true;
const gfx::Size size = GetSize();
SkImageInfo info = SkImageInfo::MakeN32Premul(size.width(), size.height());
SkPixmap pixmap(info, pixels, info.minRowBytes());
SkBitmap bitmap;
bitmap.allocPixels(info);
if (!bitmap.writePixels(pixmap))
return false;
base::PostTask(FROM_HERE,
{base::ThreadPool(), base::MayBlock(),
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&WriteDataToFile, location_, bitmap));
return true;
}
base::FilePath location_;
DISALLOW_COPY_AND_ASSIGN(FileGLSurface);
};
class TestPixmap : public gfx::NativePixmap {
public:
explicit TestPixmap(gfx::BufferFormat format) : format_(format) {}
......@@ -117,15 +173,14 @@ class TestPixmap : public gfx::NativePixmap {
class GLOzoneEGLHeadless : public GLOzoneEGL {
public:
GLOzoneEGLHeadless() = default;
GLOzoneEGLHeadless(const base::FilePath& base_path) : base_path_(base_path) {}
~GLOzoneEGLHeadless() override = default;
// GLOzone:
scoped_refptr<gl::GLSurface> CreateViewGLSurface(
gfx::AcceleratedWidget window) override {
// TODO(kylechar): Extend GLSurface implementation to write to PNG file.
return gl::InitializeGLSurface(
base::MakeRefCounted<GLSurfaceEglReadback>());
return gl::InitializeGLSurface(base::MakeRefCounted<FileGLSurface>(
GetPathForWidget(base_path_, window)));
}
scoped_refptr<gl::GLSurface> CreateOffscreenGLSurface(
......@@ -143,6 +198,8 @@ class GLOzoneEGLHeadless : public GLOzoneEGL {
}
private:
base::FilePath base_path_;
DISALLOW_COPY_AND_ASSIGN(GLOzoneEGLHeadless);
};
......@@ -150,28 +207,13 @@ class GLOzoneEGLHeadless : public GLOzoneEGL {
HeadlessSurfaceFactory::HeadlessSurfaceFactory(base::FilePath base_path)
: base_path_(base_path),
swiftshader_implementation_(std::make_unique<GLOzoneEGLHeadless>()) {
swiftshader_implementation_(
std::make_unique<GLOzoneEGLHeadless>(base_path)) {
CheckBasePath();
}
HeadlessSurfaceFactory::~HeadlessSurfaceFactory() = default;
base::FilePath HeadlessSurfaceFactory::GetPathForWidget(
gfx::AcceleratedWidget widget) {
if (base_path_.empty() || base_path_ == base::FilePath(kDevNull))
return base_path_;
// Disambiguate multiple window output files with the window id.
#if defined(OS_WIN)
std::string path =
base::NumberToString(reinterpret_cast<int>(widget)) + ".png";
std::wstring wpath(path.begin(), path.end());
return base_path_.Append(wpath);
#else
return base_path_.Append(base::NumberToString(widget) + ".png");
#endif
}
std::vector<gl::GLImplementation>
HeadlessSurfaceFactory::GetAllowedGLImplementations() {
return std::vector<gl::GLImplementation>{gl::kGLImplementationSwiftShaderGL};
......@@ -192,7 +234,7 @@ GLOzone* HeadlessSurfaceFactory::GetGLOzone(
std::unique_ptr<SurfaceOzoneCanvas>
HeadlessSurfaceFactory::CreateCanvasForWidget(gfx::AcceleratedWidget widget,
base::TaskRunner* task_runner) {
return std::make_unique<FileSurface>(GetPathForWidget(widget));
return std::make_unique<FileSurface>(GetPathForWidget(base_path_, widget));
}
scoped_refptr<gfx::NativePixmap> HeadlessSurfaceFactory::CreateNativePixmap(
......
......@@ -20,8 +20,6 @@ class HeadlessSurfaceFactory : public SurfaceFactoryOzone {
explicit HeadlessSurfaceFactory(base::FilePath base_path);
~HeadlessSurfaceFactory() override;
base::FilePath GetPathForWidget(gfx::AcceleratedWidget widget);
// SurfaceFactoryOzone:
std::vector<gl::GLImplementation> GetAllowedGLImplementations() override;
GLOzone* GetGLOzone(gl::GLImplementation implementation) override;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment