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
|
||
|
||
|