slides, bench

Change-Id: Ie0a8d61c987226f9fdf148cec9f4214bd559444c
Reviewed-on: https://skia-review.googlesource.com/c/175387
Reviewed-by: Mike Klein <mtklein@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ad2c276..e13ae4e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,13 +9,19 @@
 if (DEV_TOOLS)
     find_package(SDL2 REQUIRED)
 
+    add_library               (slides tools/slides.cc)
+    target_link_libraries     (slides PRIVATE eskia)
+
+    add_executable            (bench tools/bench.cc)
+    target_link_libraries     (bench PRIVATE eskia slides)
+
     add_executable            (demo tools/demo.cc)
     target_include_directories(demo SYSTEM PRIVATE ${SDL2_INCLUDE_DIRS})
-    target_link_libraries     (demo PRIVATE eskia ${SDL2_LIBRARIES})
+    target_link_libraries     (demo PRIVATE eskia slides ${SDL2_LIBRARIES})
 
     add_library               (stb_image_write tools/stb_image_write.cc)
     target_include_directories(stb_image_write SYSTEM PUBLIC ext/stb)
 
     add_executable            (test tools/test.cc)
-    target_link_libraries     (test PRIVATE eskia stb_image_write)
+    target_link_libraries     (test PRIVATE eskia slides stb_image_write)
 endif()
diff --git a/api/draw.h b/api/draw.h
index 826bd64..b40d3ae 100644
--- a/api/draw.h
+++ b/api/draw.h
@@ -30,11 +30,14 @@
     ShadeFn single_color;
 
     struct Frame {
-        void* dst;
-        int   width,
-              height,
-              bpp,
-              stride;
+        void* const dst;
+        const int   width,
+                    height,
+                    bpp,
+                    stride;
+
+        BlendFn* const src;
+        BlendFn* const srcover;
 
         // Draws may not be flushed to dst until the frame ends.
         ~Frame();
diff --git a/tools/bench.cc b/tools/bench.cc
new file mode 100644
index 0000000..9b76885
--- /dev/null
+++ b/tools/bench.cc
@@ -0,0 +1,43 @@
+#include "../api/draw.h"
+#include "../api/implicit_cast.h"
+#include "slides.h"
+#include <chrono>
+#include <cstdio>
+#include <cstdlib>
+#include <memory>
+
+using namespace eskia;
+
+int main(int argc, char** argv) {
+    int w = argc > 1 ? atoi(argv[1]) : 400,
+        h = argc > 2 ? atoi(argv[2]) : 400;
+
+    auto buf = std::make_unique<RGBA_8888[]>(implicit_cast<size_t>(w*h));
+
+    Frame frame{buf.get(),
+                w,h, sizeof(RGBA_8888), w,
+                src_RGBA_8888, srcover_RGBA_8888};
+
+    for (auto it = slides; *it; it++) {
+        auto slide = *it;
+
+        Paint paint;
+        paint.color = RGBA_8888{255,255,255,255};
+        frame.draw({0,0,w,h}, paint, single_color, frame.src);
+
+        printf("%s", slide(nullptr));
+
+        std::chrono::duration<double, std::micro> elapsed;
+        int n = 0;
+        const auto start = std::chrono::steady_clock::now();
+        do {
+            slide(&frame);
+            elapsed = std::chrono::steady_clock::now() - start;
+            n++;
+        } while (elapsed < std::chrono::milliseconds(100));
+
+        printf("\t%gµs\n", elapsed.count() / n);
+    }
+
+    return 0;
+}
diff --git a/tools/demo.cc b/tools/demo.cc
index e2f9cd9..7ba6bf8 100644
--- a/tools/demo.cc
+++ b/tools/demo.cc
@@ -1,6 +1,7 @@
 #include "../api/implicit_cast.h"
 #include "../api/pixel_formats.h"
 #include "../api/draw.h"
+#include "slides.h"
 #include <SDL.h>
 #include <chrono>
 #include <cstdio>
@@ -72,6 +73,11 @@
     bool done = false;
     auto last_title_update = std::chrono::steady_clock::now();
 
+    auto slide_iterator = slides;
+    auto slide = *slide_iterator;
+
+    Frame frame{implicit_cast<void*>(buf.get()), w,h, sizeof(Pixel), w, src,srcover};
+
     while (!done) {
         SDL_Event event;
         while (SDL_PollEvent(&event)) {
@@ -81,40 +87,37 @@
                 case SDL_QUIT:
                     done = true;
                     break;
+
+                case SDL_KEYUP:
+                    switch (event.key.keysym.sym) {
+                        case SDLK_RIGHT:
+                            if ( slide_iterator[+1] ) {
+                                slide = *(++slide_iterator);
+                            }
+                            break;
+                        case SDLK_LEFT:
+                            if ( slide_iterator > slides ) {
+                                slide = *(--slide_iterator);
+                            }
+                            break;
+                    }
             }
         }
 
         const auto start = std::chrono::steady_clock::now();
         {
-            Frame frame{implicit_cast<void*>(buf.get()), w,h, sizeof(Pixel), w};
             Paint paint;
-
-            paint.color = rgba_8888(1.0f,1.0f,1.0f,1.0f);
-            frame.draw({0,0,w,h}, paint, single_color, src);
-
-            for (int n = 1000; n --> 0; ) {
-                // Get some in-bounds coordinates.
-                float l = rand() % w,
-                      t = rand() % h,
-                      r = rand() % w,
-                      b = rand() % h;
-
-                // Push them out a bit to make sure we clip.
-                l = l*1.50f - w*0.25f;
-                t = t*1.50f - h*0.25f;
-                r = r*1.50f - w*0.25f;
-                b = b*1.50f - h*0.25f;
-
-                int color = rand();
-                memcpy(&paint.color, &color, 4);
-                frame.draw(Rect{l,t,r,b}, paint, single_color, srcover);
-            }
+            paint.color = RGBA_8888{255,255,255,255};
+            frame.draw({0,0,w,h}, paint, single_color, frame.src);
         }
-
-        // Take a time point marking the end of our drawing work.
-        // The rest is format converting, uploading, etc.
+        const auto clear = std::chrono::steady_clock::now();
+        {
+            slide(&frame);
+        }
         const auto draw = std::chrono::steady_clock::now();
 
+        // The rest is format converting, uploading, etc.
+
         // Push that up to our texture.
         SDL_UpdateTexture(tex.get(), nullptr, buf.get(), implicit_cast<int>(sizeof(Pixel))*w);
 
@@ -131,12 +134,14 @@
         if (last_title_update < push - std::chrono::milliseconds(100)) {
             last_title_update = push;
 
-            std::chrono::duration<double, std::milli> draw_ms = draw - start,
-                                                      push_ms = push - draw;
+            std::chrono::duration<double, std::milli> clear_ms = clear - start,
+                                                      draw_ms  = draw - clear,
+                                                      push_ms  = push - draw;
             char title[128];
-            sprintf(title, "%.2g + %.2g = %.2g ms per frame",
-                    draw_ms.count(),  push_ms.count(),
-                    draw_ms.count() + push_ms.count());
+            sprintf(title, "%s %.2g + %.2g + %.2g = %.2g ms per frame",
+                    slide(nullptr),
+                    clear_ms.count(),  draw_ms.count(),  push_ms.count(),
+                    clear_ms.count() + draw_ms.count() + push_ms.count());
             SDL_SetWindowTitle(window.get(), title);
         }
     }
diff --git a/tools/slides.cc b/tools/slides.cc
new file mode 100644
index 0000000..6d6bf54
--- /dev/null
+++ b/tools/slides.cc
@@ -0,0 +1,52 @@
+#include "../api/pixel_formats.h"
+#include "slides.h"
+#include <cstdlib>
+#include <cstring>
+
+using namespace eskia;
+
+#define SLIDE(name)                         \
+    static void name##_core(Frame* frame);  \
+    static const char* name(Frame* frame) { \
+        if (frame) { name##_core(frame); }  \
+        return #name;                       \
+    }                                       \
+    static void name##_core(Frame* frame)
+
+SLIDE(two_rects) {
+    Paint paint;
+
+    paint.color = rgba_8888(0.50f, 0.75f, 0.25f, 0.5f);
+    frame->draw(Rect{100,100,200,200}, paint, single_color, frame->srcover);
+
+    paint.color = rgba_8888(0.75f, 0.25f, 0.50f, 0.25f);
+    frame->draw(Rect{150,150,250,300}, paint, single_color, frame->srcover);
+}
+
+SLIDE(rand_rects) {
+    Paint paint;
+
+    for (int n = 1000; n --> 0; ) {
+        // Get some in-bounds coordinates.
+        float l = rand() % frame->width,
+              t = rand() % frame->height,
+              r = rand() % frame->width,
+              b = rand() % frame->height;
+
+        // Push them out a bit to make sure we clip.
+        l = l*1.50f - frame->width*0.25f;
+        t = t*1.50f - frame->height*0.25f;
+        r = r*1.50f - frame->width*0.25f;
+        b = b*1.50f - frame->height*0.25f;
+
+        int color = rand();
+        memcpy(&paint.color, &color, 4);
+        frame->draw(Rect{l,t,r,b}, paint, single_color, frame->srcover);
+    }
+}
+
+Slide* slides[] = {
+    two_rects,
+    rand_rects,
+    nullptr,
+};
diff --git a/tools/slides.h b/tools/slides.h
new file mode 100644
index 0000000..ca482cf
--- /dev/null
+++ b/tools/slides.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "../api/draw.h"
+
+using Slide = const char*(eskia::Frame*);
+
+extern Slide* slides[];
diff --git a/tools/stb_image_write.cc b/tools/stb_image_write.cc
index f25fa1b..ddfeedd 100644
--- a/tools/stb_image_write.cc
+++ b/tools/stb_image_write.cc
@@ -1,2 +1,13 @@
-#define STB_IMAGE_WRITE_IMPLEMENTATION
-#include <stb_image_write.h>
+// Integer santizer is a little too aggressive for stb_image_write.
+//   - there is intentional unsigned overflow in zlib hash functions;
+//   - PNG filtering intentionally byte-wraps by truncating ints to uint8_t.
+#if defined(__clang__)
+    #pragma clang attribute push(__attribute__((no_sanitize("integer"))), apply_to=function)
+#endif
+
+    #define STB_IMAGE_WRITE_IMPLEMENTATION
+    #include <stb_image_write.h>
+
+#if defined(__clang__)
+    #pragma clang attribute pop
+#endif
diff --git a/tools/test.cc b/tools/test.cc
index 1d97e04..02d2aa2 100644
--- a/tools/test.cc
+++ b/tools/test.cc
@@ -1,5 +1,6 @@
 #include "../api/draw.h"
 #include "../api/pixel_formats.h"
+#include "slides.h"
 #include <algorithm>
 #include <memory>
 #include <stb_image_write.h>
@@ -7,31 +8,31 @@
 using namespace eskia;
 
 int main(int, char**) {
-    // Avoid using PNG filtering, which wraps byte-wise and trips up -fsanitize=integer.
-    stbi_write_force_png_filter = 0;
-
     const int w = 400,
               h = 400;
     auto buf = std::make_unique<RGBA_8888[]>(w*h);
 
-    {
-        Frame frame{buf.get(), w,h, sizeof(RGBA_8888), w};
+    Frame frame{buf.get(),
+                w,h, sizeof(RGBA_8888), w,
+                src_RGBA_8888, srcover_RGBA_8888};
+
+    for (auto it = slides; *it; it++) {
+        auto slide = *it;
 
         Paint paint;
-        paint.color = rgba_8888(1.0f, 1.0f, 1.0f, 1.0f);
-        frame.draw(Rect{0,0,w,h}, paint, single_color, src_RGBA_8888);
+        paint.color = RGBA_8888{255,255,255,255};
+        frame.draw({0,0,w,h}, paint, single_color, frame.src);
 
-        paint.color = rgba_8888(0.50f, 0.75f, 0.25f, 0.5f);
-        frame.draw(Rect{100,100,200,200}, paint, single_color, srcover_RGBA_8888);
+        const char* name = slide(&frame);
 
-        paint.color = rgba_8888(0.75f, 0.25f, 0.50f, 0.25f);
-        frame.draw(Rect{150,150,250,300}, paint, single_color, srcover_RGBA_8888);
+        std::transform(buf.get(), buf.get() + w*h, buf.get(),
+                       unpremultiply);
+
+        char path[128];
+        sprintf(path, "%s.png", name);
+
+        stbi_write_png(path, w,h, 4/*RGBA*/, buf.get(), sizeof(RGBA_8888)*w);
     }
 
-    std::transform(buf.get(), buf.get() + w*h, buf.get(),
-                   unpremultiply);
-
-    stbi_write_png("test.png", w,h, 4/*RGBA*/, buf.get(), sizeof(RGBA_8888)*w);
-
     return 0;
 }