Skip to content

Commit 5d7f087

Browse files
committed
emulator: opengl: 'large' buffer optimization
This patch modifies the guest encoding libraries to avoid un-necessary copies when sending large buffers (e.g. pixels) to the host. Instead, the data is sent directly through a new IOStream method (writeFully()). On my machine, this improves the NenaMark2 benchmark (from 50.8 to 57.1 fps). More importantly, this speeds up the display of non-GL surfaces too, which are sent through the special rcUpdateColorBuffer() function in gralloc_goldfish. This is noticeable in many parts of the UI (e.g. when scrolling through lists). To tag a given parameter, use the new 'isLarge' variable flag in the protocol .attrib file. Implemented for the following encoding functions: rcUpdateColorBuffer glTexSubImage2D glTexImage2Di glBufferData glBufferSubData glCompressedTexImage2D glCompressedTexSubImage2D glTexImage3DOES glTexSubImage3DOES glCompressedTexImage3DOES glCompressedTexSubImage3DOES + Optimize the auto-generated encoder functions to avoid repeated function calls (for size computations). Change-Id: I13a02607b606c40cd05984cd2051b1f3424bc2d0
1 parent e72ed04 commit 5d7f087

File tree

11 files changed

+297
-104
lines changed

11 files changed

+297
-104
lines changed

tools/emulator/opengl/host/include/libOpenglRender/IOStream.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class IOStream {
3434
virtual int commitBuffer(size_t size) = 0;
3535
virtual const unsigned char *readFully( void *buf, size_t len) = 0;
3636
virtual const unsigned char *read( void *buf, size_t *inout_len) = 0;
37+
virtual int writeFully(const void* buf, size_t len) = 0;
3738

3839
virtual ~IOStream() {
3940

@@ -82,6 +83,7 @@ class IOStream {
8283
return readFully(buf, len);
8384
}
8485

86+
8587
private:
8688
unsigned char *m_buf;
8789
size_t m_bufsize;

tools/emulator/opengl/host/tools/emugen/ApiGen.cpp

Lines changed: 231 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@
2121
#include <errno.h>
2222
#include <sys/types.h>
2323

24+
/* Define this to 1 to enable support for the 'isLarge' variable flag
25+
* that instructs the encoder to send large data buffers by a direct
26+
* write through the pipe (i.e. without copying it into a temporary
27+
* buffer. This has definite performance benefits when using a QEMU Pipe.
28+
*
29+
* Set to 0 otherwise.
30+
*/
31+
#define WITH_LARGE_SUPPORT 1
32+
2433
EntryPoint * ApiGen::findEntryByName(const std::string & name)
2534
{
2635
EntryPoint * entry = NULL;
@@ -338,6 +347,99 @@ int ApiGen::genEncoderHeader(const std::string &filename)
338347
return 0;
339348
}
340349

350+
// Format the byte length expression for a given variable into a user-provided buffer
351+
// If the variable type is not a pointer, this is simply its size as a decimal constant
352+
// If the variable is a pointer, this will be an expression provided by the .attrib file
353+
// through the 'len' attribute.
354+
//
355+
// Returns 1 if the variable is a pointer, 0 otherwise
356+
//
357+
static int getVarEncodingSizeExpression(Var& var, EntryPoint* e, char* buff, size_t bufflen)
358+
{
359+
int ret = 0;
360+
if (!var.isPointer()) {
361+
snprintf(buff, bufflen, "%u", (unsigned int) var.type()->bytes());
362+
} else {
363+
ret = 1;
364+
const char* lenExpr = var.lenExpression().c_str();
365+
const char* varname = var.name().c_str();
366+
if (e != NULL && lenExpr[0] == '\0') {
367+
fprintf(stderr, "%s: data len is undefined for '%s'\n",
368+
e->name().c_str(), varname);
369+
}
370+
if (var.nullAllowed()) {
371+
snprintf(buff, bufflen, "((%s != NULL) ? %s : 0)", varname, lenExpr);
372+
} else {
373+
snprintf(buff, bufflen, "%s", lenExpr);
374+
}
375+
}
376+
return ret;
377+
}
378+
379+
static int writeVarEncodingSize(Var& var, FILE* fp)
380+
{
381+
int ret = 0;
382+
if (!var.isPointer()) {
383+
fprintf(fp, "%u", (unsigned int) var.type()->bytes());
384+
} else {
385+
ret = 1;
386+
fprintf(fp, "__size_%s", var.name().c_str());
387+
}
388+
return ret;
389+
}
390+
391+
392+
393+
static void writeVarEncodingExpression(Var& var, FILE* fp)
394+
{
395+
const char* varname = var.name().c_str();
396+
397+
if (var.isPointer()) {
398+
// encode a pointer header
399+
fprintf(fp, "\t*(unsigned int *)(ptr) = __size_%s; ptr += 4;\n", varname);
400+
401+
Var::PointerDir dir = var.pointerDir();
402+
if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) {
403+
if (var.nullAllowed()) {
404+
fprintf(fp, "\tif (%s != NULL) ", varname);
405+
} else {
406+
fprintf(fp, "\t");
407+
}
408+
409+
if (var.packExpression().size() != 0) {
410+
fprintf(fp, "%s;", var.packExpression().c_str());
411+
} else {
412+
fprintf(fp, "memcpy(ptr, %s, __size_%s);",
413+
varname, varname);
414+
}
415+
416+
fprintf(fp, "ptr += __size_%s;\n", varname);
417+
}
418+
} else {
419+
// encode a non pointer variable
420+
if (!var.isVoid()) {
421+
fprintf(fp, "\t*(%s *) (ptr) = %s; ptr += %u;\n",
422+
var.type()->name().c_str(), varname,
423+
(uint) var.type()->bytes());
424+
}
425+
}
426+
}
427+
428+
#if WITH_LARGE_SUPPORT
429+
static void writeVarLargeEncodingExpression(Var& var, FILE* fp)
430+
{
431+
const char* varname = var.name().c_str();
432+
433+
fprintf(fp, "\tstream->writeFully(&__size_%s,4);\n", varname);
434+
if (var.nullAllowed()) {
435+
fprintf(fp, "\tif (%s != NULL) ", varname);
436+
} else {
437+
fprintf(fp, "\t");
438+
}
439+
fprintf(fp, "stream->writeFully(%s, __size_%s);\n", varname, varname);
440+
}
441+
#endif /* WITH_LARGE_SUPPORT */
442+
341443
int ApiGen::genEncoderImpl(const std::string &filename)
342444
{
343445
FILE *fp = fopen(filename.c_str(), "wt");
@@ -368,109 +470,163 @@ int ApiGen::genEncoderImpl(const std::string &filename)
368470
fprintf(fp, "{\n");
369471

370472
// fprintf(fp, "\n\tDBG(\">>>> %s\\n\");\n", e->name().c_str());
371-
fprintf(fp, "\n\t%s *ctx = (%s *)self;\n\n",
473+
fprintf(fp, "\n\t%s *ctx = (%s *)self;\n",
372474
classname.c_str(),
373475
classname.c_str());
476+
fprintf(fp, "\tIOStream *stream = ctx->m_stream;\n\n");
477+
VarsArray & evars = e->vars();
478+
size_t maxvars = evars.size();
479+
size_t j;
374480

375-
// size calculation ;
376-
fprintf(fp, "\t size_t packetSize = ");
481+
char buff[256];
377482

378-
VarsArray & evars = e->vars();
379-
size_t nvars = evars.size();
380-
size_t npointers = 0;
381-
for (size_t j = 0; j < nvars; j++) {
382-
fprintf(fp, "%s ", j == 0 ? "" : " +");
383-
if (evars[j].isPointer()) {
384-
npointers++;
483+
// Define the __size_XXX variables that contain the size of data
484+
// associated with pointers.
485+
for (j = 0; j < maxvars; j++) {
486+
Var& var = evars[j];
385487

386-
if (evars[j].lenExpression() == "") {
387-
fprintf(stderr, "%s: data len is undefined for '%s'\n",
388-
e->name().c_str(), evars[j].name().c_str());
389-
}
488+
if (!var.isPointer())
489+
continue;
490+
491+
const char* varname = var.name().c_str();
492+
fprintf(fp, "\tconst unsigned int __size_%s = ", varname);
493+
494+
getVarEncodingSizeExpression(var, e, buff, sizeof(buff));
495+
fprintf(fp, "%s;\n", buff);
496+
}
497+
498+
#if WITH_LARGE_SUPPORT
499+
// We need to take care of 'isLarge' variable in a special way
500+
// Anything before an isLarge variable can be packed into a single
501+
// buffer, which is then commited. Each isLarge variable is a pointer
502+
// to data that can be written to directly through the pipe, which
503+
// will be instant when using a QEMU pipe
504+
505+
size_t nvars = 0;
506+
size_t npointers = 0;
507+
508+
// First, compute the total size, 8 bytes for the opcode + payload size
509+
fprintf(fp, "\t unsigned char *ptr;\n");
510+
fprintf(fp, "\t const size_t packetSize = 8");
511+
512+
for (j = 0; j < maxvars; j++) {
513+
fprintf(fp, " + ");
514+
npointers += writeVarEncodingSize(evars[j], fp);
515+
}
516+
if (npointers > 0) {
517+
fprintf(fp, " + %u*4", npointers);
518+
}
519+
fprintf(fp, ";\n");
520+
521+
// We need to divide the packet into fragments. Each fragment contains
522+
// either copied arguments to a temporary buffer, or direct writes for
523+
// large variables.
524+
//
525+
// The first fragment must also contain the opcode+payload_size
526+
//
527+
nvars = 0;
528+
while (nvars < maxvars || maxvars == 0) {
529+
530+
// Skip over non-large fields
531+
for (j = nvars; j < maxvars; j++) {
532+
if (evars[j].isLarge())
533+
break;
534+
}
535+
536+
// Write a fragment if needed.
537+
if (nvars == 0 || j > nvars) {
538+
const char* plus = "";
539+
540+
if (nvars == 0 && j == maxvars) {
541+
// Simple shortcut for the common case where we don't have large variables;
542+
fprintf(fp, "\tptr = stream->alloc(packetSize);\n");
390543

391-
if (evars[j].nullAllowed()) {
392-
fprintf(fp, "(%s != NULL ? %s : 0)",
393-
evars[j].name().c_str(),
394-
evars[j].lenExpression().c_str());
395544
} else {
396-
if (evars[j].pointerDir() == Var::POINTER_IN ||
397-
evars[j].pointerDir() == Var::POINTER_INOUT) {
398-
fprintf(fp, "%s", evars[j].lenExpression().c_str());
399-
} else {
400-
fprintf(fp, "0");
545+
// allocate buffer from the stream until the first large variable
546+
fprintf(fp, "\tptr = stream->alloc(");
547+
plus = "";
548+
549+
if (nvars == 0) {
550+
fprintf(fp,"8"); plus = " + ";
551+
}
552+
if (j > nvars) {
553+
npointers = 0;
554+
for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
555+
fprintf(fp, "%s", plus); plus = " + ";
556+
npointers += writeVarEncodingSize(evars[j], fp);
557+
}
558+
if (npointers > 0) {
559+
fprintf(fp, "%s%u*4", plus, npointers); plus = " + ";
560+
}
401561
}
562+
fprintf(fp,");\n");
563+
}
564+
565+
// encode packet header if needed.
566+
if (nvars == 0) {
567+
fprintf(fp, "\t*(unsigned int *)(ptr) = OP_%s; ptr += 4;\n", e->name().c_str());
568+
fprintf(fp, "\t*(unsigned int *)(ptr) = (unsigned int) packetSize; ptr += 4;\n");
569+
}
570+
571+
if (maxvars == 0)
572+
break;
573+
574+
// encode non-large fields in this fragment
575+
for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
576+
writeVarEncodingExpression(evars[j],fp);
577+
}
578+
579+
// Ensure the fragment is commited if it is followed by a large variable
580+
if (j < maxvars) {
581+
fprintf(fp, "\tstream->flush();\n");
402582
}
403-
} else {
404-
fprintf(fp, "%u", (unsigned int) evars[j].type()->bytes());
405583
}
584+
585+
// If we have one or more large variables, write them directly.
586+
// As size + data
587+
for ( ; j < maxvars && evars[j].isLarge(); j++) {
588+
writeVarLargeEncodingExpression(evars[j], fp);
589+
}
590+
591+
nvars = j;
406592
}
407-
fprintf(fp, " %s 8 + %u * 4;\n", nvars != 0 ? "+" : "", (unsigned int) npointers);
593+
594+
#else /* !WITH_LARGE_SUPPORT */
595+
size_t nvars = evars.size();
596+
size_t npointers = 0;
597+
fprintf(fp, "\t const size_t packetSize = 8");
598+
for (size_t j = 0; j < nvars; j++) {
599+
npointers += getVarEncodingSizeExpression(evars[j],e,buff,sizeof(buff));
600+
fprintf(fp, " + %s", buff);
601+
}
602+
fprintf(fp, " + %u * 4;\n", (unsigned int) npointers);
408603

409604
// allocate buffer from the stream;
410-
fprintf(fp, "\t unsigned char *ptr = ctx->m_stream->alloc(packetSize);\n\n");
605+
fprintf(fp, "\t unsigned char *ptr = stream->alloc(packetSize);\n\n");
411606

412607
// encode into the stream;
413608
fprintf(fp, "\t*(unsigned int *)(ptr) = OP_%s; ptr += 4;\n", e->name().c_str());
414609
fprintf(fp, "\t*(unsigned int *)(ptr) = (unsigned int) packetSize; ptr += 4;\n\n");
415610

416611
// out variables
417612
for (size_t j = 0; j < nvars; j++) {
418-
if (evars[j].isPointer()) {
419-
// encode a pointer header
420-
if (evars[j].nullAllowed()) {
421-
fprintf(fp, "\t*(unsigned int *)(ptr) = (%s != NULL) ? %s : 0; ptr += 4; \n",
422-
evars[j].name().c_str(), evars[j].lenExpression().c_str());
423-
} else {
424-
fprintf(fp, "\t*(unsigned int *)(ptr) = %s; ptr += 4; \n",
425-
evars[j].lenExpression().c_str());
426-
}
427-
428-
Var::PointerDir dir = evars[j].pointerDir();
429-
if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) {
430-
if (evars[j].nullAllowed()) {
431-
fprintf(fp, "\tif (%s != NULL) ", evars[j].name().c_str());
432-
} else {
433-
fprintf(fp, "\t");
434-
}
435-
436-
if (evars[j].packExpression().size() != 0) {
437-
fprintf(fp, "%s;", evars[j].packExpression().c_str());
438-
} else {
439-
fprintf(fp, "memcpy(ptr, %s, %s);",
440-
evars[j].name().c_str(),
441-
evars[j].lenExpression().c_str());
442-
}
443-
444-
if (evars[j].nullAllowed()) {
445-
fprintf(fp, "ptr += %s == NULL ? 0 : %s; \n", evars[j].name().c_str(), evars[j].lenExpression().c_str());
446-
} else {
447-
fprintf(fp, "ptr += %s;\n", evars[j].lenExpression().c_str());
448-
}
449-
}
450-
} else {
451-
// encode a non pointer variable
452-
if (!evars[j].isVoid()) {
453-
fprintf(fp, "\t*(%s *) (ptr) = %s; ptr += %u;\n",
454-
evars[j].type()->name().c_str(), evars[j].name().c_str(),
455-
(uint) evars[j].type()->bytes());
456-
}
457-
}
613+
writeVarEncodingExpression(evars[j], fp);
458614
}
615+
#endif /* !WITH_LARGE_SUPPORT */
616+
459617
// in variables;
460618
for (size_t j = 0; j < nvars; j++) {
461619
if (evars[j].isPointer()) {
462620
Var::PointerDir dir = evars[j].pointerDir();
463621
if (dir == Var::POINTER_INOUT || dir == Var::POINTER_OUT) {
622+
const char* varname = evars[j].name().c_str();
464623
if (evars[j].nullAllowed()) {
465-
fprintf(fp, "\tif (%s != NULL) ctx->m_stream->readback(%s, %s);\n",
466-
evars[j].name().c_str(),
467-
evars[j].name().c_str(),
468-
evars[j].lenExpression().c_str());
624+
fprintf(fp, "\tif (%s != NULL) ",varname);
469625
} else {
470-
fprintf(fp, "\tctx->m_stream->readback(%s, %s);\n",
471-
evars[j].name().c_str(),
472-
evars[j].lenExpression().c_str());
626+
fprintf(fp, "\t");
473627
}
628+
fprintf(fp, "stream->readback(%s, __size_%s);\n",
629+
varname, varname);
474630
}
475631
}
476632
}
@@ -482,7 +638,7 @@ int ApiGen::genEncoderImpl(const std::string &filename)
482638
fprintf(fp, "\t return NULL;\n");
483639
} else if (e->retval().type()->name() != "void") {
484640
fprintf(fp, "\n\t%s retval;\n", e->retval().type()->name().c_str());
485-
fprintf(fp, "\tctx->m_stream->readback(&retval, %u);\n",(uint) e->retval().type()->bytes());
641+
fprintf(fp, "\tstream->readback(&retval, %u);\n",(uint) e->retval().type()->bytes());
486642
fprintf(fp, "\treturn retval;\n");
487643
}
488644
fprintf(fp, "}\n\n");

0 commit comments

Comments
 (0)