diff options
author | halcanary <halcanary@google.com> | 2015-12-10 07:52:45 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-12-10 07:52:45 -0800 |
commit | decb21e3ae3d296976d8664e49e35971d1b4fadd (patch) | |
tree | ac1f09e066a107e22d46f68775f8f214f53380ea /experimental | |
parent | fd5f6c1b0d352b44ba08a677499c8c27b5aea7c7 (diff) |
experiment/fiddle
Review URL: https://codereview.chromium.org/1349163003
Diffstat (limited to 'experimental')
-rw-r--r-- | experimental/fiddle/.gitignore | 3 | ||||
-rw-r--r-- | experimental/fiddle/draw.cpp | 30 | ||||
-rw-r--r-- | experimental/fiddle/fiddle_main.cpp | 155 | ||||
-rw-r--r-- | experimental/fiddle/fiddle_main.h | 34 | ||||
-rw-r--r-- | experimental/fiddle/fiddle_test | 31 | ||||
-rw-r--r-- | experimental/fiddle/fiddler.go | 159 | ||||
-rwxr-xr-x | experimental/fiddle/parse-fiddle-output | 21 |
7 files changed, 433 insertions, 0 deletions
diff --git a/experimental/fiddle/.gitignore b/experimental/fiddle/.gitignore new file mode 100644 index 0000000000..dd6cc4fbbb --- /dev/null +++ b/experimental/fiddle/.gitignore @@ -0,0 +1,3 @@ +*.gch +*.o +fiddler
\ No newline at end of file diff --git a/experimental/fiddle/draw.cpp b/experimental/fiddle/draw.cpp new file mode 100644 index 0000000000..dfed793701 --- /dev/null +++ b/experimental/fiddle/draw.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// This is an example of the translation unit that needs to be +// assembled by the fiddler program to compile into a fiddle: an +// implementation of the GetDrawOptions() and draw() functions. + +#include "fiddle_main.h" +DrawOptions GetDrawOptions() { + // path *should* be absolute. + static const char path[] = "../../resources/color_wheel.png"; + return DrawOptions(256, 256, true, true, true, true, path); +} +void draw(SkCanvas* canvas) { + canvas->clear(SK_ColorWHITE); + SkMatrix matrix; + matrix.setScale(0.75f, 0.75f); + matrix.preRotate(30.0f); + SkAutoTUnref<SkShader> shader( + image->newShader(SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode, + &matrix)); + SkPaint paint; + paint.setShader(shader); + canvas->drawPaint(paint); +} diff --git a/experimental/fiddle/fiddle_main.cpp b/experimental/fiddle/fiddle_main.cpp new file mode 100644 index 0000000000..eb41b51041 --- /dev/null +++ b/experimental/fiddle/fiddle_main.cpp @@ -0,0 +1,155 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <stdio.h> + +#include <GL/osmesa.h> + +#include "fiddle_main.h" + +// Globals externed in fiddle_main.h +SkBitmap source; +SkImage* image(nullptr); + +static void encode_to_base64(const void* data, size_t size, FILE* out) { + const uint8_t* input = reinterpret_cast<const uint8_t*>(data); + const uint8_t* end = &input[size]; + static const char codes[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz0123456789+/"; + while (input != end) { + uint8_t b = (*input & 0xFC) >> 2; + fputc(codes[b], out); + b = (*input & 0x03) << 4; + ++input; + if (input == end) { + fputc(codes[b], out); + fputs("==", out); + return; + } + b |= (*input & 0xF0) >> 4; + fputc(codes[b], out); + b = (*input & 0x0F) << 2; + ++input; + if (input == end) { + fputc(codes[b], out); + fputc('=', out); + return; + } + b |= (*input & 0xC0) >> 6; + fputc(codes[b], out); + b = *input & 0x3F; + fputc(codes[b], out); + ++input; + } +} + +static void dump_output(SkData* data, const char* name, bool last = true) { + if (data) { + printf("\t\"%s\": \"", name); + encode_to_base64(data->data(), data->size(), stdout); + fputs(last ? "\"\n" : "\",\n", stdout); + } +} + +static SkData* encode_snapshot(SkSurface* surface) { + SkAutoTUnref<SkImage> img(surface->newImageSnapshot()); + return img ? img->encode() : nullptr; +} + +static OSMesaContext create_osmesa_context() { + OSMesaContext osMesaContext = + OSMesaCreateContextExt(OSMESA_BGRA, 0, 0, 0, nullptr); + if (osMesaContext != nullptr) { + static uint32_t buffer[16 * 16]; + OSMesaMakeCurrent(osMesaContext, &buffer, GL_UNSIGNED_BYTE, 16, 16); + } + return osMesaContext; +} + +static GrContext* create_mesa_grcontext() { + SkAutoTUnref<const GrGLInterface> mesa(GrGLCreateMesaInterface()); + intptr_t backend = reinterpret_cast<intptr_t>(mesa.get()); + return backend ? GrContext::Create(kOpenGL_GrBackend, backend) : nullptr; +} + + +int main() { + const DrawOptions options = GetDrawOptions(); + fprintf(stderr, "%s\n", options.source); + if (options.source) { + SkAutoTUnref<SkData> data(SkData::NewFromFileName(options.source)); + if (!data) { + perror(options.source); + return 1; + } else { + image = SkImage::NewFromEncoded(data); + if (!image) { + perror("Unable to decode the source image."); + return 1; + } + SkAssertResult(image->asLegacyBitmap( + &source, SkImage::kRO_LegacyBitmapMode)); + } + } + SkAutoTUnref<SkData> rasterData, gpuData, pdfData, skpData; + if (options.raster) { + SkAutoTUnref<SkSurface> rasterSurface( + SkSurface::NewRaster(SkImageInfo::MakeN32Premul(options.size))); + draw(rasterSurface->getCanvas()); + rasterData.reset(encode_snapshot(rasterSurface)); + } + if (options.gpu) { + OSMesaContext osMesaContext = create_osmesa_context(); + SkAutoTUnref<GrContext> grContext(create_mesa_grcontext()); + if (!grContext) { + fputs("Unable to get Mesa GrContext.\n", stderr); + } else { + SkAutoTUnref<SkSurface> surface( + SkSurface::NewRenderTarget( + grContext, + SkSurface::kNo_Budgeted, + SkImageInfo::MakeN32Premul(options.size))); + if (!surface) { + fputs("Unable to get render surface.\n", stderr); + exit(1); + } + draw(surface->getCanvas()); + gpuData.reset(encode_snapshot(surface)); + } + if (osMesaContext) { + OSMesaDestroyContext(osMesaContext); + } + } + if (options.pdf) { + SkDynamicMemoryWStream pdfStream; + SkAutoTUnref<SkDocument> document(SkDocument::CreatePDF(&pdfStream)); + draw(document->beginPage(options.size.width(), options.size.height())); + document->close(); + pdfData.reset(pdfStream.copyToData()); + } + if (options.skp) { + SkSize size; + size = options.size; + SkPictureRecorder recorder; + draw(recorder.beginRecording(size.width(), size.height())); + SkAutoTUnref<SkPicture> picture(recorder.endRecordingAsPicture()); + SkDynamicMemoryWStream skpStream; + picture->serialize(&skpStream); + skpData.reset(skpStream.copyToData()); + } + + printf("{\n"); + dump_output(rasterData, "Raster", !gpuData && !pdfData && !skpData); + dump_output(gpuData, "Gpu", !pdfData && !skpData); + dump_output(pdfData, "Pdf", !skpData); + dump_output(skpData, "Skp"); + printf("}\n"); + + SkSafeSetNull(image); + return 0; +} diff --git a/experimental/fiddle/fiddle_main.h b/experimental/fiddle/fiddle_main.h new file mode 100644 index 0000000000..ab2aa8b5ab --- /dev/null +++ b/experimental/fiddle/fiddle_main.h @@ -0,0 +1,34 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef fiddle_main_DEFINED +#define fiddle_main_DEFINED + +#include "skia.h" + +extern SkBitmap source; +extern SkImage* image; + +struct DrawOptions { + DrawOptions(int w, int h, bool r, bool g, bool p, bool k, const char* s) + : size(SkISize::Make(w, h)) + , raster(r) + , gpu(g) + , pdf(p) + , skp(k) + , source(s) {} + SkISize size; + bool raster; + bool gpu; + bool pdf; + bool skp; + const char* source; +}; + +extern DrawOptions GetDrawOptions(); +extern void draw(SkCanvas*); + +#endif // fiddle_main_DEFINED diff --git a/experimental/fiddle/fiddle_test b/experimental/fiddle/fiddle_test new file mode 100644 index 0000000000..7955d68614 --- /dev/null +++ b/experimental/fiddle/fiddle_test @@ -0,0 +1,31 @@ +#!/bin/sh +# Copyright 2015 Google Inc. +# +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Script for building Fiddle build bots. + +set -e +set -x + +cd "$(dirname "$0")/../.." +skia_dir="$PWD" +cores=32 + +echo "Bootstrapping CMake" +cmake_dir="${skia_dir}/third_party/externals/cmake" +cd "$cmake_dir" +./bootstrap --parallel=$cores +make -j $cores cmake + +echo "Building fiddle bootstrapped CMake" +cd "${skia_dir}/experimental/fiddle" +export PATH="${cmake_dir}/bin:${PATH}" +go build fiddler.go +./fiddler "$skia_dir" +./fiddler "$skia_dir" draw.cpp > /dev/null + +echo "cleaning up" +cd "$skia_dir" +git clean -fxd cmake experimental/fiddle diff --git a/experimental/fiddle/fiddler.go b/experimental/fiddle/fiddler.go new file mode 100644 index 0000000000..d3056c95d3 --- /dev/null +++ b/experimental/fiddle/fiddler.go @@ -0,0 +1,159 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +package main + +// Example use: +// git clone https://skia.googlesource.com/skia.git +// cd skia +// SKIA=$PWD +// cd experimental/fiddle +// go get github.com/skia-dev/glog +// go get go.skia.org/infra/go/util +// go build fiddler.go +// # compile prerequisites +// ./fiddler "$SKIA" +// # compile and run a fiddle +// ./fiddler "$SKIA" draw.cpp | ./parse-fiddle-output +// # compile and run a different fiddle +// ./fiddler "$SKIA" ANOTHER_FIDDLE.cpp | ./parse-fiddle-output + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path" + "syscall" + + "github.com/skia-dev/glog" + "go.skia.org/infra/go/util" +) + +func setResourceLimits() error { + const maximumTimeInSeconds = 5 + limit := syscall.Rlimit{maximumTimeInSeconds, maximumTimeInSeconds} + if err := syscall.Setrlimit(syscall.RLIMIT_CPU, &limit); err != nil { + return err + } + const maximumMemoryInBytes = 1 << 28 + limit = syscall.Rlimit{maximumMemoryInBytes, maximumMemoryInBytes} + return syscall.Setrlimit(syscall.RLIMIT_AS, &limit) +} + +// execCommand runs command and returns an error if it fails. If there is no +// error, all output is discarded. +func execCommand(input io.Reader, dir string, name string, arg ...string) error { + var buffer bytes.Buffer + cmd := exec.Command(name, arg...) + cmd.Dir = dir + cmd.Stdout = &buffer + cmd.Stderr = &buffer + cmd.Stdin = input + if err := cmd.Run(); err != nil { + return fmt.Errorf("execution failed:\n\n%s\n[%v]", buffer.String(), err) + } + return nil +} + +func compileArgs(skiaSrc string) string { + return "@" + path.Join(skiaSrc, "cmake", "skia_compile_arguments.txt") +} + +func linkArgs(skiaSrc string) string { + return "@" + path.Join(skiaSrc, "cmake", "skia_link_arguments.txt") +} + +// fiddler compiles the input, links against skia, and runs the executable. +// @param skiaSrc: the base directory of the Skia repository +// @param inputReader: C++ fiddle source +// @param output: stdout of executable sent here +// @param tempDir: where to place the compiled executable +func fiddler(skiaSrc string, inputReader io.Reader, output io.Writer, tempDir string) error { + binarypath := path.Join(tempDir, "fiddle") + fiddle_dir := path.Join(skiaSrc, "experimental", "fiddle") + if err := execCommand(inputReader, fiddle_dir, + "c++", + compileArgs(skiaSrc), + "-I", fiddle_dir, + "-o", binarypath, + "-x", "c++", "-", "-x", "none", + "fiddle_main.o", + "-lOSMesa", + linkArgs(skiaSrc), + ); err != nil { + return err + } + var buffer bytes.Buffer + runCmd := exec.Cmd{Path: binarypath, Stdout: output, Stderr: &buffer} + if err := runCmd.Run(); err != nil { + return fmt.Errorf("execution failed:\n\n%s\n[%v]", buffer.String(), err) + } + return nil +} + +// Compile Skia library and fiddle_main.cpp +// @param skiaSrc: the base directory of the Skia repository. +func fiddlerPrerequisites(skiaSrc string) error { + cmakeDir := path.Join(skiaSrc, "cmake") + if err := execCommand(nil, cmakeDir, "cmake", "-G", "Ninja", "."); err != nil { + return err + } + if err := execCommand(nil, cmakeDir, "ninja", "skia"); err != nil { + return err + } + fiddle_dir := path.Join(skiaSrc, "experimental", "fiddle") + if err := execCommand(nil, fiddle_dir, "c++", compileArgs(skiaSrc), + "fiddle_main.h"); err != nil { + return err + } + return execCommand(nil, fiddle_dir, "c++", compileArgs(skiaSrc), + "-c", "-o", "fiddle_main.o", "fiddle_main.cpp") +} + +func main() { + if len(os.Args) < 2 { + glog.Fatalf("usage: %s SKIA_SRC_PATH [PATH_TO_DRAW.CPP]", os.Args[0]) + } + skiaSrc := os.Args[1] + if len(os.Args) < 3 { + // execCommand(nil, skiaSrc, "git", "fetch") + // execCommand(nil, skiaSrc, "git", "checkout", "origin/master") + if err := fiddlerPrerequisites(skiaSrc); err != nil { + glog.Fatal(err) + } + } else { + if err := setResourceLimits(); err != nil { + glog.Fatal(err) + } + tempDir, err := ioutil.TempDir("", "fiddle_") + if err != nil { + glog.Fatal(err) + } + defer func() { + err = os.RemoveAll(tempDir) + if err != nil { + glog.Fatalf("os.RemoveAll(tempDir) failed: %v", err) + } + }() + if os.Args[2] == "-" { + if err := fiddler(skiaSrc, os.Stdin, os.Stdout, tempDir); err != nil { + glog.Fatal(err) + } + } else { + inputFile, err := os.Open(os.Args[2]) + if err != nil { + glog.Fatalf("unable to open \"%s\": %v", os.Args[2], err) + } + util.Close(inputFile) + if err = fiddler(skiaSrc, inputFile, os.Stdout, tempDir); err != nil { + glog.Fatal(err) + } + } + } +} diff --git a/experimental/fiddle/parse-fiddle-output b/experimental/fiddle/parse-fiddle-output new file mode 100755 index 0000000000..f9a2fac6d6 --- /dev/null +++ b/experimental/fiddle/parse-fiddle-output @@ -0,0 +1,21 @@ +#!/bin/sh +# Copyright 2015 Google Inc. +# +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Parse the output of fiddle_main, for use in testing +while IFS= read -r line; do + type=$(echo $line | sed -n 's/[^"]*"\([^"]*\)":.*/\1/p') + if [ "$type" ]; then + case "$type" in + Raster|Gpu) ext='.png';; + Pdf) ext='.pdf';; + Skp) ext='.skp';; + esac + dst="${TMPDIR:-/tmp}/fiddle_${type}${ext}" + echo $line | sed 's/[^"]*"[^"]*": "//; s/"\(,\|\)$//' \ + | base64 -d > "$dst" + echo $dst + fi +done |