aboutsummaryrefslogtreecommitdiffhomepage
path: root/experimental
diff options
context:
space:
mode:
authorGravatar mtklein <mtklein@chromium.org>2014-06-16 20:21:06 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2014-06-16 20:21:06 -0700
commitd6043b20b63f895d384b4794205ac914abfafa71 (patch)
treee80d04d729ffca5264b05f7c914cdc0c89c92d99 /experimental
parent4aa000df08f184ac3576d03ba50d6367e05d069b (diff)
Add nanomsg to third_party, with some demos.
This isn't something I want to make part of Skia, but just a substrate to build cross-process demos on top of. If I client were to use Skia cross-process, they'd drop their own IPC system in here. If you're not familiar, nanomsg (nanomsg.org) is the next-gen zeromq (zeromq.org), from the same author, righting all his design wrongs from zeromq. It's a lot like the lower half of mojo, dealing with making the connections and getting messages reliably from A to B. Think, better sockets, and it spans nicely across in-process (with zero-copy), inter-process, and TCP. BUG=skia: R=bsalomon@google.com, mtklein@google.com Author: mtklein@chromium.org Review URL: https://codereview.chromium.org/294873004
Diffstat (limited to 'experimental')
-rw-r--r--experimental/nanomsg/picture_demo.cpp207
1 files changed, 207 insertions, 0 deletions
diff --git a/experimental/nanomsg/picture_demo.cpp b/experimental/nanomsg/picture_demo.cpp
new file mode 100644
index 0000000000..5a3ebdc816
--- /dev/null
+++ b/experimental/nanomsg/picture_demo.cpp
@@ -0,0 +1,207 @@
+#include "nanomsg/src/nn.h"
+#include "nanomsg/src/pipeline.h"
+#include "nanomsg/src/reqrep.h"
+
+#include "SkCanvas.h"
+#include "SkCommandLineFlags.h"
+#include "SkData.h"
+#include "SkForceLinking.h"
+#include "SkGraphics.h"
+#include "SkImageEncoder.h"
+#include "SkOSFile.h"
+#include "SkPicture.h"
+#include "SkRandom.h"
+#include "SkStream.h"
+
+__SK_FORCE_IMAGE_DECODER_LINKING;
+
+// To keep things simple, PictureHeader is fixed-size POD.
+struct PictureHeader {
+ SkMatrix matrix;
+ SkRect clip;
+ SkXfermode::Mode xfermode;
+ pid_t pid;
+ uint8_t alpha;
+
+ PictureHeader()
+ : matrix(SkMatrix::I())
+ , clip(SkRect::MakeLargest())
+ , xfermode(SkXfermode::kSrcOver_Mode)
+ , pid(getpid())
+ , alpha(0xFF) {}
+};
+
+// A little adaptor: nn_iovec wants a non-const pointer for no obvious reason.
+static struct nn_iovec create_iov(const void* ptr, size_t size) {
+ struct nn_iovec iov = { const_cast<void*>(ptr), size };
+ return iov;
+}
+
+static void send_picture(int socket, const PictureHeader& header, const SkData& skp) {
+ // Vectored IO lets us send header and skp contiguously without first
+ // copying them to a contiguous buffer.
+ struct nn_iovec iov[] = {
+ create_iov(&header, sizeof(header)),
+ create_iov(skp.data(), skp.size()),
+ };
+
+ struct nn_msghdr msg;
+ sk_bzero(&msg, sizeof(msg));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = SK_ARRAY_COUNT(iov);
+
+ nn_sendmsg(socket, &msg, 0/*flags*/);
+}
+
+static SkPicture* recv_picture(int socket, PictureHeader* header) {
+ static const size_t hSize = sizeof(*header); // It's easy to slip up and use sizeof(header).
+
+ void* msg;
+ int size = nn_recv(socket, &msg, NN_MSG, 0/*flags*/);
+ SkDebugf("%d bytes", size);
+
+ // msg is first a fixed-size header, then an .skp.
+ memcpy(header, msg, hSize);
+ SkMemoryStream stream((uint8_t*)msg + hSize, size - hSize);
+ SkPicture* pic = SkPicture::CreateFromStream(&stream);
+
+ SkDebugf(" from proccess %d:", header->pid);
+
+ nn_freemsg(msg);
+ return pic;
+}
+
+static void client(const char* skpPath, const char* dataEndpoint) {
+ // Read the .skp.
+ SkAutoTUnref<const SkData> skp(SkData::NewFromFileName(skpPath));
+ if (!skp) {
+ SkDebugf("Couldn't read %s\n", skpPath);
+ exit(1);
+ }
+ SkMemoryStream stream(skp->data(), skp->size());
+ SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromStream(&stream));
+
+ PictureHeader header;
+ SkRandom rand(picture->width() * picture->height());
+ SkScalar r = rand.nextRangeScalar(0, picture->width()),
+ b = rand.nextRangeScalar(0, picture->height()),
+ l = rand.nextRangeScalar(0, r),
+ t = rand.nextRangeScalar(0, b);
+ header.clip.setLTRB(l,t,r,b);
+ header.matrix.setTranslate(-l, -t);
+ header.matrix.postRotate(rand.nextRangeScalar(-25, 25));
+ header.alpha = 0x7F;
+
+ //Clients use NN_REQ (request) type sockets.
+ int socket = nn_socket(AF_SP, NN_REQ);
+
+ // Clients connect a socket to an endpoint.
+ nn_connect(socket, dataEndpoint);
+
+ // Send the picture and its header.
+ SkDebugf("Sending %s (%d bytes)...", skpPath, skp->size());
+ send_picture(socket, header, *skp);
+
+ // Wait for ack.
+ uint8_t ack;
+ nn_recv(socket, &ack, sizeof(ack), 0/*flags*/);
+ SkDebugf(" ok.\n");
+}
+
+// Wait until socketA or socketB has something to tell us, and return which one.
+static int poll_in(int socketA, int socketB) {
+ struct nn_pollfd polls[] = {
+ { socketA, NN_POLLIN, 0 },
+ { socketB, NN_POLLIN, 0 },
+ };
+
+ nn_poll(polls, SK_ARRAY_COUNT(polls), -1/*no timeout*/);
+
+ if (polls[0].revents & NN_POLLIN) { return socketA; }
+ if (polls[1].revents & NN_POLLIN) { return socketB; }
+
+ SkFAIL("unreachable");
+ return 0;
+}
+
+static void server(const char* dataEndpoint, const char* controlEndpoint, SkCanvas* canvas) {
+ // NN_REP sockets receive a request then make a reply. NN_PULL sockets just receive a request.
+ int data = nn_socket(AF_SP, NN_REP);
+ int control = nn_socket(AF_SP, NN_PULL);
+
+ // Servers bind a socket to an endpoint.
+ nn_bind(data, dataEndpoint);
+ nn_bind(control, controlEndpoint);
+
+ while (true) {
+ int ready = poll_in(data, control);
+
+ // If we got any message on the control socket, we can stop.
+ if (ready == control) {
+ break;
+ }
+
+ // We should have an .skp waiting for us on data socket.
+ PictureHeader header;
+ SkAutoTUnref<SkPicture> picture(recv_picture(data, &header));
+
+ SkPaint paint;
+ paint.setAlpha(header.alpha);
+ paint.setXfermodeMode(header.xfermode);
+
+ canvas->saveLayer(NULL, &paint);
+ canvas->concat(header.matrix);
+ canvas->clipRect(header.clip);
+ picture->draw(canvas);
+ canvas->restore();
+ SkDebugf(" drew");
+
+ // Send back an ack.
+ uint8_t ack = 42;
+ nn_send(data, &ack, sizeof(ack), 0/*flags*/);
+ SkDebugf(" and acked.\n");
+ }
+}
+
+static void stop(const char* controlEndpoint) {
+ // An NN_PUSH socket can send messages but not receive them.
+ int control = nn_socket(AF_SP, NN_PUSH);
+ nn_connect(control, controlEndpoint);
+
+ // Sending anything (including this 0-byte message) will tell server() to stop.
+ nn_send(control, NULL, 0, 0/*flags*/);
+}
+
+DEFINE_string2(skp, r, "", ".skp to send (as client)");
+DEFINE_string2(png, w, "", ".png to write (as server)");
+DEFINE_bool(stop, false, "If true, tell server to stop and write its canvas out as a .png.");
+DEFINE_string(data, "ipc://nanomsg-picture-data", "Endpoint for sending pictures.");
+DEFINE_string(control, "ipc://nanomsg-picture-control", "Endpoint for control channel.");
+
+int main(int argc, char** argv) {
+ SkAutoGraphics ag;
+ SkCommandLineFlags::Parse(argc, argv);
+
+ if (FLAGS_stop) {
+ stop(FLAGS_control[0]);
+ }
+
+ if (!FLAGS_skp.isEmpty()) {
+ client(FLAGS_skp[0], FLAGS_data[0]);
+ }
+
+ if (!FLAGS_png.isEmpty()) {
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(1000, 1000);
+ SkCanvas canvas(bitmap);
+ canvas.clear(0xFFFFFFFF);
+
+ server(FLAGS_data[0], FLAGS_control[0], &canvas);
+ canvas.flush();
+
+ SkImageEncoder::EncodeFile(FLAGS_png[0], bitmap, SkImageEncoder::kPNG_Type, 100);
+ SkDebugf("Wrote %s.\n", FLAGS_png[0]);
+ }
+
+ return 0;
+}