Introduction:
-------------
The emugen tool is a tool to generate a wire protocol implementation
based on provided API. The tool generates c++ encoder code that takes
API calls and encodes them into the wire and decoder code that decodes
the wire stream and calls server matching API.
The emugen tool includes additional functionality that enables to
generate an wrapper library. The wrapper library provides entry points
for the specified API, where each entry routes the call via a dispatch
table. The dispatch table may be initialized as required by a specific application.
The following paragraphs includes the following:
* Wire Protocol Description
* Input files description & format
* Generated code description.
Note: In this document, the caller is referred to as Encoder or Client
and the callee is referred to as the Decoder or Server. These terms
are used interchangeably by the context.
Wire Protocol packet structure:
-------------------------------
A general Encoder->Decoder packet is structured as following:
struct Packet {
unsigned int opcode;
unsigned int packet_len;
… parameter 1
… parameter 2
};
A general Decoder->Encoder reply is expected to be received in the
context of the ‘call’ that triggered it, thus it includes the reply
data only, with no context headers. In precise term terms, a reply
packet will look like:
struct {
...// reply data
};
consider the following function call:
int foo(int p1, short s1)
will be encoded into :
{
101, // foo opcode
14, // sizeof(opcode) + sizeof(packet_len) + sizeof(int) + sizeof(short)
p1, // 4 bytes
s1 // 2 bytes
}
Since ‘foo’ returns value, the caller is expected to read back the return packet from the server->client stream. The return value in this example is in thus the return packet is:
{
int retval;
}
Pointer decoding:
----------------
The wire protocol also allows exchanging of pointer data
(arrays). Pointers are defined with directions:
in : Data is sent from the caller to the calle
out: Data is sent from the callee to the caller
in_out: data is sent from the caller and return in place.
‘in’ and ‘in_out’ encoded with their len:
{
unsinged int pointer_data_len;
unsigned char data[pointer_data_len];… // pointer data
}
‘out’ pointers are encoded by data length only:
{
unsigned int pointer_data_len;
}
‘out’ and ‘in_out’ pointer’s data is returned in the return
packet. For example, consider the following call:
int foo(int n, int *ptr); // assume that ‘data’ is in_out pointer which contains ‘n’ ints
The caller packet will have the following form:
{
101, // foo opcode
xx, sizeof(opcode) + sizeof(datalen) + sizeof(int) + sizeof(unsigned int) + n * sizeof(int);
n, // the n parameter
n * sizeof(int), // size of the data in ptr
… // n* sizeof(int) bytes
}
The return packet is;
{
…. // n * sizeof(int) bytes of data return in ptr
retval // sizeof(int) - the return value of the function;
}
Endianess
---------
The Wire protocol is designed to impose minimum overhead on the client
side. Thus, the data endianness that is sent across the wire is
determined by the ‘client’ side. It is up to the server side to
determine the client endianess and marshal the packets as required.
Emugen input files - protocol specification
-------------------------------------------
The protocol generated by emugen consists of two input files:
1. basename.in - A sepcification of the protocol RPC procedures. This
part of the specification is expected to be generated automatically
from c/c++ header files or similar.
‘basename’ is the basename for the protocol and will be used to prefix
the files that are generated for this protocol. A line in the .in
file has the following format:
[prefix](retvalType, FuncName, [param name],...)
where
retvalType - The function return value type
FuncName - function name
mandatory parameter type
[param name] - optional parameter name
Examples:
GL_ENTRY(void, glVertex1f, float v)
XXX(int *, foo, int n, float, short)
XXX(void, glFlush, void)
Note: Empty lines in the file are ignored. A line starts with # is a comment
2. basename.attrib - Attributes information of the API.
This file includes additional flags, pointers datalen information and
global attributes of the protocol. For uptodate format of the file,
please refer to the specification file in the project source
tree. The format of the .attrib file is described below.
3. basename.types - Types information
This files describes the types that are described by the API. A type
is defined as follows:
where:
is the name of the type as described in the API
0, 8, 16, 32 sizes are accepted
a string to format the value of the type, as
acceted by printf(3)
true or false string species whether the type should be
treated as a pointer.
example:
GLint 32 %d false
GLint* 32 %p true
GLptr 32 %p true
Encoder generated code files
----------------------------
In order to generate the encoder files, one should run the ‘emugen’
tool as follows:
emugen -i -E
where:
containes the api specification files (basename.in + basename.attrib)
- a directory name to generate the encoder output files
basename - The basename for the api.
Assuming the basename is ‘api’, The following files are generated:
api_opcodes.h - defines the protocol opcodes. The first opcode value
is 0, unless defined otherwise in the .attrib file
api_entry.cpp - defines entry points for the functions that are
defined by the protocol. this File also includes a function call
‘setContextAccessor(void *(*f)()). This function should be used to
provide a callback function that is used by the functions to access
the encoder context. For example, such callback could fetch the
context from a Thread Local Storage (TLS) location.
api_client_proc.h - type defintions for the protocol procedures.
api_client_context.h - defines the client side dispatch table data
structure that stores the encoding functions. This data structure also
includes ‘accessors’ methods such that library user can override
default entries for special case handling.
api_client_context.cpp - defines an initialization function for
dispatch table
api_enc.h - This header file defines the encoder data strcuture. The
encoder data structure inherits its functionality from the
‘client_context’ class above and adds encoding and streaming
functionality.
api_enc.cpp - Encoder implementation.
Decoder generated files
-----------------------
In order to generate the decoder files, one should run the ‘emugen’
tool as follows:
emugen -i -D basename
where:
containes the api specification files (basename.in + basename.attrib)
- a directory name to generate the decoder output files
basename - The basename for the api.
With resepct to the example above, Emugen will generate the following
files:
api_opcodes.h - Protocol opcodes
api_server_proc.h - type definitions for the server side procedures
api_server_context.h - dispatch table the decoder functions
api_server_context.cpp - dispatch table initialization function
api_dec.h - Decoder header file
api_dec.cpp - Decoder implementation. In addtion, this file includes
an intiailization function that uses a user provided callback to
initialize the API server implementation. An example for such
initialization is loading a set of functions from a shared library
module.
Wrapper generated files
-----------------------
In order to generate a wrapper library files, one should run the
'emugen' tool as follows:
emugen -i -W basename
where:
containes the api specification files (basename.in + basename.attrib)
- a directory name to generate the wrapper output files
basename - The basename for the api.
With resepct to the example above, Emugen will generate the following
files:
api_wrapper_proc.h - type definitions for the wrapper procedures
api_wrapper_context.h - dispatch table the wrapper functions
api_wrapper_context.cpp - dispatch table initialization function
api_wrapper_entry.cpp - entry points for the API
.attrib file format description:
-------------------------------
The .attrib file is an input file to emugen and is used to provide
additional information that is required for the code generation.
The file format is as follows:
a line that starts with # is ignored (comment)
a empty line just whitespace of (" " "\t" "\n") is ignored.
The file is divided into 'sections', each describes a specific API
function call. A section starts with the name of the function in
column 0.
A section that starts with the reserved word 'GLOBAL' provides global
attributes.
below are few sections examples:
GLOBAL
encoder_headers string.h kuku.h
glVertex3fv
len data (size)
glTexImage2D
len pixels (pixels == NULL? 0 : (format_pixel_size(internalformat) * width * height * type_size(type)))
Global section flags description:
base_opcode
set the base opcode value for this api
format: base_opcode 100
encoder_headers
a list of headers that will be included in the encoder header file
format: encoder_headers "kuku.h"
client_context_headers
a list of headers that will be included in the client context header file
format: client_context_headers "kuku.h"
decoder_headers
a list of headers that will be included in the decoder header file
format: decoder_headers "kuku.h"
server_context_headers
a list of headers that will be included in the server context header file
format: server_context_headers "kuku.h"
Entry point flags description:
len
desciption : provide an expression to calcualte an expression data len
format: len
custom_pack
description: provide an expression to pack data into the stream.
format: custom_pack
The stream is represented by a (unsigned char *)ptr. The expression may also refer
to other function parameters. In addition, the expression may refer to 'void *self' which
is the encoding context as provided by the caller.
dir
description : set a pointer direction (in - for data that goes
to the codec, out from data that returns from the codec.
format: dir <[in | out | inout]>
var_flag
description : set variable flags
format: var_flag < nullAllowed | isLarge | ... >
nullAllowed -> for pointer variables, indicates that NULL is a valid value
isLarge -> for pointer variables, indicates that the data should be sent without an intermediate copy
flag
description: set entry point flag;
format: flag < unsupported | ... >
supported flags are:
unsupported - The encoder side implementation is pointed to "unsuppored reporting function".
custom_decoder - The decoder is expected to be provided with
custom implementation. The call to the
deocder function includes a pointer to the
context
not_api - the function is not native gl api