diff options
author | 2014-06-16 20:21:06 -0700 | |
---|---|---|
committer | 2014-06-16 20:21:06 -0700 | |
commit | d6043b20b63f895d384b4794205ac914abfafa71 (patch) | |
tree | e80d04d729ffca5264b05f7c914cdc0c89c92d99 /experimental | |
parent | 4aa000df08f184ac3576d03ba50d6367e05d069b (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.cpp | 207 |
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; +} |