Skip to content

Instantly share code, notes, and snippets.

@oneup
Created July 31, 2009 02:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oneup/159051 to your computer and use it in GitHub Desktop.
Save oneup/159051 to your computer and use it in GitHub Desktop.
NOTE:_
as releasing this i don't even know how it works,
but maybe it helps people wondering about C# Inter Process Communications (IPC) via TCP & System.Runtime.InteropServices.Marshal.Copy
it has too many copies. needs to be more zero-copy-esque
NEEDS
Tao SDL
---
DONE
opengl composition manager
clients schicken updates übers netz (ganzes bild)
TODO next
bpp unabhängig
resize (messages runter zu client schicken)
TODO
bpp unabhängig
resize
messages zu client und rauf
lazy updating (nur opengl calls, wenns was neues gibt)
lazy network traffic (nur geänderte sachen schicken / in textur uploaden)
WIE FUNZT DAS?
WMMain hat zwei threads
opengl zeichnen
warten auf neue clients
jeder client bekommt seinen eigenen thread (ClientThread)
der is state basiert
wartet auf resize messages und image daten (ruf dann window.updateTexture auf)
jeder client hat sein eigenes window
malt sich (und updated texturen) per draw() (wird vom opengl wmmain thread aufgerufen)
WAS IST VERBESSERUNGSWÜRDIG?
texturen uploaden - es wird eine riesieg leere textur neu erstellt beim ersten mal
texturen sind sehr grafik ram verschwenderisch (->power of two)
texturen werden immer komplett geupdated (nicht power of two, aber der ganze sichtbare bereich)
es gibt für einmal updaten ewig viele memory copies (unmanaged to managed in client, network copy client->server, managed to unmanaged in server, unmanaged to opengl)
////
//// OpenGL based server that streams images to screen from multiple clients
//// maybe someone can use this / have fun playing with it
using System;
using System.Text;
using System.Collections;
using System.Threading;
using System.IO;
using System.Net.Sockets;
using System.Drawing;
namespace WM
{
enum ClientThreadState
{
WindowSize,
WindowData
}
class ClientThread
{
private bool stop = false;
public bool running = false;
private TcpClient connection = null;
private Window w = null;
private ClientThreadState state = ClientThreadState.WindowSize;
private Rectangle winrect;
private Thread t = null;
public ClientThread ( TcpClient connection)
{
this.connection = connection;
this.w = w;
Console.WriteLine("Client!!");
t = new Thread ( new ThreadStart ( Run ) );
t.Start ();
}
public void Run ()
{
this.running = true;
Stream outStream = this.connection.GetStream ();
Stream inStream = this.connection.GetStream();
bool loop = true;
while ( loop )
{
try
{
switch(state)
{
case ClientThreadState.WindowSize:
Console.WriteLine("get size");
StreamReader strStream = new StreamReader ( connection.GetStream () );
String size = strStream.ReadLine ();
Console.WriteLine("size: " + size);
string[] sizes = size.Split('|');
winrect = new Rectangle(int.Parse(sizes[0]), int.Parse(sizes[1]), int.Parse(sizes[2]), int.Parse(sizes[3]));
state = ClientThreadState.WindowData;
break;
case ClientThreadState.WindowData:
byte[] blub = new byte[winrect.Width*winrect.Height*3];
connection.ReceiveTimeout = 10;
int count = inStream.Read(blub, 0, winrect.Width*winrect.Height*3);
if(count != 0)
{
if(w == null)
{
w = new Window(winrect, blub);
WMMain.getWM().addWindow(w);
}
else
{
w.updateTexture(blub);
}
}
break;
}
Thread.Sleep(10);
loop = !this.stop;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
loop = false;
}
}
this.connection.Close ();
this.running = false;
WMMain.getWM().removeWindow(w);
}
public void Stop()
{
stop = true;
Console.WriteLine("STOPPING");
// t.Interrupt();
t.Abort();
connection.Close();
this.running = false;
}
}
}
////
//// OpenGL based server that streams images to screen from multiple clients
//// maybe someone can use this / have fun playing with it
using System;
using SdlDotNet;
using Tao.Sdl;
using Tao.OpenGl;
using System.Drawing;
namespace WM
{
public class OpenGlWM
{
private Surface screen;
private const int width = 800;
private const int height = 600;
private const int bpp = 32;
public OpenGlWM()
{
screen = Video.SetVideoModeWindowOpenGL(width, height, true);
reshape();
Gl.glPushAttrib(Gl.GL_COLOR_BUFFER_BIT);
Gl.glPushAttrib(Gl.GL_ENABLE_BIT);
Gl.glDisable(Gl.GL_DEPTH_TEST);
Gl.glDisable(Gl.GL_CULL_FACE);
Gl.glEnable(Gl.GL_TEXTURE_2D);
Gl.glEnable(Gl.GL_BLEND);
Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);
Gl.glTexEnvf(Gl.GL_TEXTURE_ENV, Gl.GL_TEXTURE_ENV_MODE, Gl.GL_MODULATE);
Gl.glColor3f(1,1,1);
}
public void reshape()
{
Gl.glViewport(0,0,width, height);
Gl.glMatrixMode(Gl.GL_PROJECTION);
Gl.glLoadIdentity();
Gl.glOrtho(0, width, height, 0, 0, 1);
Gl.glMatrixMode(Gl.GL_MODELVIEW);
Gl.glLoadIdentity();
}
public void toggleFullscreen()
{
if(screen.FullScreen == true)
screen = Video.SetVideoModeWindowOpenGL(width, height, true);
else
screen = Video.SetVideoModeOpenGL(width, height, bpp);
reshape();
}
}
}
////
//// OpenGL based server that streams images to screen from multiple clients
//// maybe someone can use this / have fun playing with it
using System;
using SdlDotNet;
using Tao.Sdl;
using Tao.OpenGl;
using System.Drawing;
using System.Text;
namespace WM
{
public class Window
{
private static int nextPowerOfTwo(int input)
{
--input;
input |= input >> 16;
input |= input >> 8;
input |= input >> 4;
input |= input >> 2;
input |= input >> 1;
return input + 1;
}
private bool update; //upload new texture
private uint []texture = new uint[1];
private Rectangle winrect; //position in wm (note: rambuffer height/width will be the next power of two)
private float texcoordw;
private float texcoordh;
private IntPtr rambuffer = IntPtr.Zero; //backbuffer in RAM, note that the real window size (w, h) might differ from the rambuffer size (as opengl texture width/height needs to be a power of two for reasonable performance)
private bool newtexture = true;
private int texw, texh;
public Window(Rectangle winrect, byte[] imagedata)
{
//WARNING - this is called in a seperate thread - DO NOT CALL OPENGL functions here.
this.winrect = winrect;
//resize to opengl texture size
texw = nextPowerOfTwo(winrect.Width);
texh = nextPowerOfTwo(winrect.Height);
texcoordw = (float)winrect.Width / texw;
texcoordh = (float)winrect.Height / texh;
Console.WriteLine("Surface restretch - " + winrect.Width + "|" + winrect.Height + " -> " + texw + "|" + texh);
rambuffer = System.Runtime.InteropServices.Marshal.AllocHGlobal(winrect.Width*winrect.Height*3);
//TODO: free rambuffer on destruction
updateTexture(imagedata);
}
public void updateTexture(byte[] newtextdata)
{
System.Runtime.InteropServices.Marshal.Copy(newtextdata, 0, rambuffer, winrect.Width*winrect.Height*3);
update = true;
}
public void draw()
{
if(update)
{
if(newtexture)
{
Gl.glGenTextures(1, texture);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, texture[0]);
IntPtr initialtexture = System.Runtime.InteropServices.Marshal.AllocHGlobal(texw*texh*3);
Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, (int)Gl.GL_RGB8, texw, texh,
0, Gl.GL_BGR_EXT, Gl.GL_UNSIGNED_BYTE, initialtexture);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST); // Linear Filtering
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST); // Linear Filtering
System.Runtime.InteropServices.Marshal.FreeHGlobal(initialtexture);
newtexture = false;
}
else
{
Gl.glBindTexture(Gl.GL_TEXTURE_2D, texture[0]);
}
int i = Timer.TicksElapsed;
Gl.glTexSubImage2D(Gl.GL_TEXTURE_2D, 0,
//x, y
0, 0, winrect.Width, winrect.Height, Gl.GL_BGR_EXT, Gl.GL_UNSIGNED_BYTE, rambuffer);
Console.WriteLine("GLERROR: " + Gl.glGetError());
i = Timer.TicksElapsed - i;
Console.WriteLine("Image from network - upload time: " + i);
Console.WriteLine("Update finished @ " + System.DateTime.Now.Ticks + " aka " + System.DateTime.Now.Millisecond);
update = false;
}
Gl.glEnable(Gl.GL_TEXTURE_2D);
Gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, texture[0]);
Gl.glBegin(Gl.GL_QUADS);
Gl.glTexCoord2f(0,texcoordh);
Gl.glVertex3i(winrect.X+0, winrect.Y+0, 0);
Gl.glTexCoord2f(texcoordw,texcoordh);
Gl.glVertex3i(winrect.X+winrect.Width, winrect.Y+0, 0);
Gl.glTexCoord2f(texcoordw,0);
Gl.glVertex3i(winrect.X+winrect.Width, winrect.Y+winrect.Height, 0);
Gl.glTexCoord2f(0,0);
Gl.glVertex3i(winrect.X+0, winrect.Y+winrect.Height, 0);
Gl.glEnd();
Gl.glDisable(Gl.GL_TEXTURE_2D);
}
public bool hit(int x, int y)
{
if(x >= winrect.X && x <= winrect.X + winrect.Width
&& y >= winrect.Y && y <= winrect.Y + winrect.Width)
return true;
else
return false;
}
public void moveTo(int x, int y)
{
winrect.X = x;
winrect.Y = y;
}
public int getX()
{
return winrect.X;
}
public int getY()
{
return winrect.Y;
}
}
}
////
//// a client i wrote ages ago, sends images via TCP Sockets
//// maybe someone can use this
using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Collections;
using System.Threading;
using System.Drawing;
using System.Runtime.InteropServices;
public class WMClient
{
public static void Main ()
{
int x = 10;
int y = 50;
TcpClient c = new TcpClient ( "localhost", 1337 );
StreamReader inStream = new StreamReader ( c.GetStream () );
Stream outStream = c.GetStream ();
String filename = "bla.bmp";
Bitmap image = new Bitmap(filename);
image.RotateFlip(RotateFlipType.RotateNoneFlipY);
System.Drawing.Imaging.BitmapData [] bitmapdata = new System.Drawing.Imaging.BitmapData[2];
Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
bitmapdata[0] = image.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
Bitmap image2 = new Bitmap("test.bmp");
image2.RotateFlip(RotateFlipType.RotateNoneFlipY);
bitmapdata[1] = image2.LockBits(new Rectangle(0, 0, image2.Width, image2.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
Byte[] bbb = Encoding.ASCII.GetBytes (x+"|"+y+"|"+image.Width+"|"+image.Height+"\n");
outStream.Write ( bbb, 0, bbb.Length );
Console.WriteLine("ready");
string exit = "";
int curen = 0;
long dur = -1;
while(exit != "e"){
//exit = Console.ReadLine();
if(curen == 0)
curen = 1;
else
curen = 0;
Console.WriteLine("Send @ " + System.DateTime.Now.Ticks+ " aka " + System.DateTime.Now.Millisecond);
long i = System.DateTime.Now.Ticks;
try
{
byte[] sendBytes = new byte[image.Width*image.Height*3];
System.Runtime.InteropServices.Marshal.Copy(bitmapdata[curen].Scan0, sendBytes, 0, image.Width*image.Height*3);
outStream.Write ( sendBytes, 0, sendBytes.Length );
}
catch(Exception e)
{
Console.WriteLine(e.ToString());
exit = "e";
}
dur = System.DateTime.Now.Ticks-i;
Console.WriteLine("Send duration: " + dur);
Thread.Sleep(100);
//Console.ReadLine();
}
c.Close ();
}
}
using System;
using SdlDotNet;
using Tao.Sdl;
using Tao.OpenGl;
using System.Drawing;
using System.Collections;
using System.Threading;
using System.IO;
using System.Net.Sockets;
//http://gpwiki.org/index.php/C:Using_SDL_with_OpenGL
/* Next:
* wait for client()
* accept client()
* display client image()
* get client update
*
* jeder client bekommt einen thread (höchstwahrscheinlich)
*
* protocol:
* c connect
* WELCOME c->s 1337?
* READY s->c ALWAYS!
* CLIENT SIZE c->s x,y,w,h,bpp (später alternativ nur w,h und wm sucht platzerl aus)
* FORCE UPDATE s->c UPDATE
* SEND UPDATE c->s UPDATE
* c->s pixeldata
* QUIT s<->c QUIT
*
* */
namespace WM
{
enum WMState
{
NOTHING,
DRAGWINDOW
}
class WMMain
{
private OpenGlWM wm;
private ArrayList windows = new ArrayList();
private WMState state; //todo: wrap in class
private Window dragwindow; //todo: wrap in class with variables etc according to the current state
private int dragxoffset;
private int dragyoffset;
private TcpListener listener;
private static WMMain theOneAndOnlyWM = null;
public static WMMain getWM(){return theOneAndOnlyWM;}
private bool fin = false;
[STAThread]
static void Main(string[] args)
{
theOneAndOnlyWM = new WMMain();
WMMain.getWM().Run();
}
public WMMain()
{
wm = new OpenGlWM();
state = WMState.NOTHING;
Events.Quit += new QuitEventHandler (this.Quit);
Events.KeyboardDown += new KeyboardEventHandler (this.KeyDown);
Events.MouseButtonDown += new MouseButtonEventHandler(this.MouseButton);
Events.MouseButtonUp += new MouseButtonEventHandler(this.MouseButton);
Events.MouseMotion += new MouseMotionEventHandler(this.MouseMotion);
listener = new TcpListener(1337);
listener.Start();
new Thread ( new ThreadStart ( threadNetwork ) ).Start();
Console.WriteLine(WMMain.getWM());
}
private void Quit(Object sender, QuitEventArgs e)
{
fin = true;
listener.Stop();
}
public void Run()
{
int framestart;
float fps;
int delta;
//bool eventhere = false;
while (!fin)
{
framestart = SdlDotNet.Timer.TicksElapsed;
while(Events.Poll())
{
// eventhere = true;
}
//if(eventhere) //only draw when really necessary
{
// Console.WriteLine("draw");
DrawGLScene();
// eventhere = false;
}
delta = SdlDotNet.Timer.TicksElapsed-framestart;
fps = delta == 0 ? 999.0f : 1000/delta;
Console.Write("FPS: " + fps + " Delta: " + delta + "\r");
Thread.Sleep(10);
}
Console.WriteLine("");//preserves FPS string
theOneAndOnlyWM = null;
}
public void threadNetwork()
{
ArrayList threads = new ArrayList ();
while(!fin)
{
try
{
TcpClient c = listener.AcceptTcpClient ();
threads.Add ( new ClientThread ( c ) );
}
catch(SocketException e)
{
Console.WriteLine(e.ToString());
}
catch(Exception e)
{
Console.WriteLine(e.ToString());
}
Thread.Sleep(10);
}
Console.WriteLine("Network Stop");
for ( IEnumerator e = threads.GetEnumerator (); e.MoveNext (); )
{
ClientThread st = (ClientThread)e.Current;
Console.WriteLine("stopping");
st.Stop();
while ( st.running )
Thread.Sleep ( 100 );
Console.WriteLine("STOPPED");
}
Console.WriteLine("ALLE STOPPED");
}
public void addWindow(Window w)
{
windows.Add(w);
}
public void removeWindow(Window w){
windows.Remove(w);
}
private void KeyDown(Object sender, KeyboardEventArgs e)
{
switch(e.Key)
{
case Key.Escape:
Quit(this, null);
break;
case Key.F1:
wm.toggleFullscreen();
break;
}
}
private void MouseButton(Object sender, MouseButtonEventArgs e)
{
if(e.ButtonPressed){
if(e.Button == SdlDotNet.MouseButton.PrimaryButton)
{
for(int i=0;i<windows.Count;i++)
{
Window w = (Window)windows[i];
if(w.hit(e.X, e.Y))
{
setState(WMState.DRAGWINDOW);
dragwindow = w;
dragxoffset = w.getX() - e.X;
dragyoffset = w.getY() - e.Y;
break;
}
}
}
}
else
{
if(state == WMState.DRAGWINDOW)
{
dragwindow = null;
setState(WMState.NOTHING);
}
}
}
private void MouseMotion(Object sender, MouseMotionEventArgs e)
{
if(state == WMState.DRAGWINDOW)
{
dragwindow.moveTo(e.X+dragxoffset, e.Y+dragyoffset);
}
}
public void DrawGLScene()
{
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
for(int i=windows.Count-1;i>=0;i--)
((Window)windows[i]).draw();
Video.GLSwapBuffers();
}
private void setState(WMState newstate)
{
//multi threading sanity check
state = newstate;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment