333 lines
11 KiB
Plaintext
333 lines
11 KiB
Plaintext
|
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
|
|||
|
|
|||
|
|