|
|
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 type> [param name],...)
|
|
|
where
|
|
|
retvalType - The function return value type
|
|
|
FuncName - function name
|
|
|
<param type> 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:
|
|
|
<type name> <size in bits> <print format string> <is a pointer? true|false>
|
|
|
where:
|
|
|
<type name> is the name of the type as described in the API
|
|
|
<size in bits> 0, 8, 16, 32 sizes are accepted
|
|
|
<print format string> a string to format the value of the type, as
|
|
|
acceted by printf(3)
|
|
|
<is pointer?> 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 <input directory> -E <encoder files output directory> <basename>
|
|
|
where:
|
|
|
<input directory> containes the api specification files (basename.in + basename.attrib)
|
|
|
<encoder directory> - 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 <input directory> -D <decoder files output directory> basename
|
|
|
where:
|
|
|
<input directory> containes the api specification files (basename.in + basename.attrib)
|
|
|
<decoder directory> - 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 <input directory> -W <wrapper files output directory> basename
|
|
|
where:
|
|
|
<input directory> containes the api specification files (basename.in + basename.attrib)
|
|
|
<wrapper directory> - 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 <stdio.h> "kuku.h"
|
|
|
|
|
|
client_context_headers
|
|
|
a list of headers that will be included in the client context header file
|
|
|
format: client_context_headers <stdio.h> "kuku.h"
|
|
|
|
|
|
decoder_headers
|
|
|
a list of headers that will be included in the decoder header file
|
|
|
format: decoder_headers <stdio.h> "kuku.h"
|
|
|
|
|
|
server_context_headers
|
|
|
a list of headers that will be included in the server context header file
|
|
|
format: server_context_headers <stdio.h> "kuku.h"
|
|
|
|
|
|
|
|
|
Entry point flags description:
|
|
|
|
|
|
len
|
|
|
desciption : provide an expression to calcualte an expression data len
|
|
|
format: len <var name> <c expression that calcluates the data len>
|
|
|
|
|
|
custom_pack
|
|
|
description: provide an expression to pack data into the stream.
|
|
|
format: custom_pack <var name> <c++ expression that pack data from var into the stream>
|
|
|
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 <varname> <[in | out | inout]>
|
|
|
|
|
|
var_flag
|
|
|
description : set variable flags
|
|
|
format: var_flag <varname> < 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
|
|
|
|
|
|
|