[Spice-devel] [RFC] spice with off screen support

Izik Eidus ieidus at redhat.com
Tue Mar 30 16:03:25 PDT 2010


Ok, still need some clean ups and to go the code with meld
(For example i got there goto and stuff like that that i can remove)

But windows 7 look and feel better/faster than ever :-)

So what is missing is really only gdi support (trivial and will send it with gdi tomorrow)

Another things to note are:
There is alot of room for optimizations and improvments
some stuff to note that i want in this patch but decide not
to sending it in the current patch - as this patch is already too
big and complex, so i will send them later after this patch:

* background sending of off-screens commands
  (give higher priority into the primary surface commands)

* much less brutal destruction of surfaces - right now
  I am syncing everything into the client at the
  ~primary surface~ destruction (Only the primary surface
  every other surfaces don`t have this issue)

* Protect surfaces that in the pipe against immidate data changes
  before they are sent into the client (just matter of copy their data,
  trivial)
  (I guess i should have included this in this patch as it matter
   of corectness, so will probably add it tommorow)

* another release ring? I dont sure anymore, started to implmented
  and came into few questions....

* gl stuff - this going to be fun, but first i need to change
  the current gl implemantion in the driver and instead of targeting
  nvidia closed driver to target the latest kms stuff

commit 2d5442c0849ab9c99e7f613f659d406af4bc23fd
Author: Izik Eidus <ieidus at redhat.com>
Date:   Wed Mar 31 01:43:27 2010 +0300

    libspice: add off screens support
    
    Signed-off-by: Izik Eidus <ieidus at redhat.com>

diff --git a/client/canvas.cpp b/client/canvas.cpp
index ba446c9..b72aad5 100644
--- a/client/canvas.cpp
+++ b/client/canvas.cpp
@@ -22,11 +22,12 @@
 
 
 Canvas::Canvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-               GlzDecoderWindow &glz_decoder_window)
+               GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces)
     : _canvas (NULL)
     , _pixmap_cache (pixmap_cache)
     , _palette_cache (palette_cache)
     , _glz_decoder(glz_decoder_window, _glz_handler, _glz_debug)
+    , _csurfaces(csurfaces)
 {
 }
 
@@ -65,6 +66,8 @@ void Canvas::localalize_image(SPICE_ADDRESS* in_bitmap)
     localalize_ptr(in_bitmap);
     image = (SpiceImageDescriptor*)SPICE_GET_ADDRESS(*in_bitmap);
     switch (image->type) {
+    case SPICE_IMAGE_TYPE_SURFACE:
+        break;
     case SPICE_IMAGE_TYPE_BITMAP: {
         SpiceBitmapImage *bitmap = (SpiceBitmapImage *)image;
         localalize_ptr(&bitmap->bitmap.data);
diff --git a/client/canvas.h b/client/canvas.h
index 8e3a8f1..1e251ed 100644
--- a/client/canvas.h
+++ b/client/canvas.h
@@ -36,6 +36,102 @@ enum CanvasType {
     CANVAS_TYPE_GDI,
 };
 
+template <class T, int HASH_SIZE, class Base = EmptyBase>
+class CHash : public Base {
+public:
+    CHash()
+    {
+        memset(_hash, 0, sizeof(_hash));
+    }
+
+    ~CHash()
+    {
+    }
+
+    void add(uint32_t id, T* data)
+    {
+        Item** item = &_hash[key(id)];
+
+        while (*item) {
+            PANIC_ON((*item)->id == id);
+            item = &(*item)->next;
+        }
+        *item = new Item(id, data);
+    }
+
+    bool is_present(uint32_t id)
+    {
+        Item* item = _hash[key(id)];
+
+        for (;;) {
+            if (!item) {
+                return false;
+            }
+
+            if (item->id != id) {
+                item = item->next;
+                continue;
+            }
+
+            return true;
+        }
+    }
+
+    T* get(uint32_t id)
+    {
+        Item* item = _hash[key(id)];
+
+        for (;;) {
+            PANIC_ON(!item);
+
+            if (item->id != id) {
+                item = item->next;
+                continue;
+            }
+
+            return item->data;
+        }
+    }
+
+    void remove(uint32_t id)
+    {
+        Item** item = &_hash[key(id)];
+
+        while (*item) {
+            if ((*item)->id == id) {
+                Item *rm_item = *item;
+                *item = rm_item->next;
+                delete rm_item;
+                return;
+            }
+            item = &(*item)->next;
+        }
+        THROW("id %lu, not found", id);
+    }
+
+private:
+    inline uint32_t key(uint32_t id) {return id % HASH_SIZE;}
+
+private:
+    class Item {
+    public:
+        Item(uint32_t in_id, T* data)
+            : id (in_id)
+            , next (NULL)
+            , data (data) {}
+
+        ~Item()
+        {
+        }
+
+        uint64_t id;
+        Item* next;
+        T* data;
+    };
+
+    Item* _hash[HASH_SIZE];
+};
+
 class PixmapCacheTreat {
 public:
     static inline pixman_image_t *get(pixman_image_t *surf)
@@ -81,6 +177,45 @@ public:
     }
 };
 
+class SpiceImageSurfacesBase;
+
+typedef CHash<SpiceCanvas, 1024, SpiceImageSurfacesBase> CSurfaces;
+
+class SpiceImageSurfacesBase {
+public:
+    SpiceImageSurfaces base;
+
+    static void op_put(SpiceImageSurfaces *c, uint32_t surface_id, SpiceCanvas *surface)
+    {
+        CSurfaces* cache = reinterpret_cast<CSurfaces*>(c);
+        cache->add(surface_id, surface);
+    }
+
+    static SpiceCanvas* op_get(SpiceImageSurfaces *s, uint32_t surface_id)
+    {
+        CSurfaces* cache = reinterpret_cast<CSurfaces*>(s);
+        return cache->get(surface_id);
+    }
+
+    static void op_del(SpiceImageSurfaces *c, uint32_t surface_id)
+    {
+        CSurfaces* cache = reinterpret_cast<CSurfaces*>(c);
+        cache->remove(surface_id);
+    }
+
+    SpiceImageSurfacesBase()
+    {
+        static SpiceImageSurfacesOps cache_ops = {
+            op_get
+        };
+        base.ops = &cache_ops;
+    }
+};
+
+class Canvas;
+
+typedef CHash<Canvas, 1024, SpiceImageSurfacesBase> CCanvases;
+
 class CachedPalette {
 public:
     CachedPalette(SpicePalette* palette)
@@ -241,7 +376,7 @@ public:
 class Canvas {
 public:
     Canvas(PixmapCache& bits_cache, PaletteCache& palette_cache,
-           GlzDecoderWindow &glz_decoder_window);
+           GlzDecoderWindow &glz_decoder_window, CSurfaces& csurfaces);
     virtual ~Canvas();
 
     virtual void copy_pixels(const QRegion& region, RedDrawable* dc,
@@ -274,11 +409,14 @@ public:
 
     virtual CanvasType get_pixmap_type() { return CANVAS_TYPE_INVALID; }
 
+    virtual SpiceCanvas *get_internal_canvas() { return _canvas; }
+
 protected:
     virtual void touched_bbox(const SpiceRect *bbox) {};
 
     PixmapCache& pixmap_cache() { return _pixmap_cache;}
     PaletteCache& palette_cache() { return _palette_cache;}
+    CSurfaces& csurfaces() { return _csurfaces; }
 
     GlzDecoder& glz_decoder() {return _glz_decoder;}
 
@@ -293,6 +431,7 @@ private:
 
 protected:
     SpiceCanvas* _canvas;
+    CSurfaces _surfaces;
 
 private:
     PixmapCache& _pixmap_cache;
@@ -302,6 +441,8 @@ private:
     GlzDecoderCanvasDebug _glz_debug;
     GlzDecoder _glz_decoder;
 
+    CSurfaces& _csurfaces;
+
     unsigned long _base;
     unsigned long _max;
 };
diff --git a/client/display_channel.cpp b/client/display_channel.cpp
index 49e9457..397c724 100644
--- a/client/display_channel.cpp
+++ b/client/display_channel.cpp
@@ -59,7 +59,7 @@ public:
         Application* app = (Application*)events_loop.get_owner();
         _channel.screen()->lock_size();
         _channel.screen()->resize(_width, _height);
-        _channel.create_canvas(app->get_canvas_types(), _width, _height, _depth);
+        _channel.create_canvas(0, app->get_canvas_types(), _width, _height, _depth);
     }
 
 private:
@@ -78,13 +78,56 @@ public:
 
     virtual void do_response(AbstractProcessLoop& events_loop)
     {
-        _channel.destroy_canvas();
+        _channel.destroy_canvas(0);
     }
 
 private:
     DisplayChannel& _channel;
 };
 
+class CreateSurfaceEvent: public SyncEvent {
+public:
+   CreateSurfaceEvent(DisplayChannel& channel, int surface_id, int width, int height, int depth)
+        : _channel (channel)
+        , _surface_id (surface_id)
+        , _width (width)
+        , _height (height)
+        , _depth (depth)
+    {
+    }
+
+    virtual void do_response(AbstractProcessLoop& events_loop)
+    {
+        Application* app = (Application*)events_loop.get_owner();
+        _channel.create_canvas(_surface_id, app->get_canvas_types(), _width, _height, _depth);
+    }
+
+private:
+    DisplayChannel& _channel;
+    int _surface_id;
+    int _width;
+    int _height;
+    int _depth;
+};
+
+class DestroySurfaceEvent: public SyncEvent {
+public:
+    DestroySurfaceEvent(DisplayChannel& channel, int surface_id)
+        : _channel (channel)
+        , _surface_id (surface_id)
+    {
+    }
+
+    virtual void do_response(AbstractProcessLoop& events_loop)
+    {
+        _channel.destroy_canvas(_surface_id);
+    }
+
+private:
+    DisplayChannel& _channel;
+    int _surface_id;
+};
+
 class UnlockScreenEvent: public Event {
 public:
     UnlockScreenEvent(RedScreen* screen)
@@ -637,6 +680,42 @@ void ResetTimer::response(AbstractProcessLoop& events_loop)
     _client.deactivate_interval_timer(this);
 }
 
+void DisplaySurfacesManger::add_surface(int surface_id, SpiceCanvas *surface)
+{
+     surfaces.add(surface_id, surface);
+}
+
+void DisplaySurfacesManger::del_surface(int surface_id)
+{
+    surfaces.remove(surface_id);
+}
+
+void DisplaySurfacesManger::add_canvas(int surface_id, Canvas *canvas)
+{
+    canvases.add(surface_id, canvas);
+}
+
+void DisplaySurfacesManger::del_canvas(int surface_id)
+{
+    canvases.remove(surface_id);
+}
+
+CSurfaces& DisplaySurfacesManger::get_surfaces()
+{ 
+    return surfaces;
+}
+
+bool DisplaySurfacesManger::is_present_canvas(int surface_id)
+{
+
+    return canvases.is_present(surface_id);
+}
+
+Canvas* DisplaySurfacesManger::get_canvas(int surface_id)
+{
+    return canvases.get(surface_id);
+}
+
 class DisplayHandler: public MessageHandlerImp<DisplayChannel, SPICE_MSG_END_DISPLAY> {
 public:
     DisplayHandler(DisplayChannel& channel)
@@ -648,7 +727,6 @@ DisplayChannel::DisplayChannel(RedClient& client, uint32_t id,
     : RedChannel(client, SPICE_CHANNEL_DISPLAY, id, new DisplayHandler(*this),
                  Platform::PRIORITY_LOW)
     , ScreenLayer (SCREEN_LAYER_DISPLAY, true)
-    , _canvas (NULL)
     , _pixmap_cache (pixmap_cache)
     , _glz_window (glz_window)
     , _mark (false)
@@ -710,6 +788,8 @@ DisplayChannel::DisplayChannel(RedClient& client, uint32_t id,
     get_process_loop().add_trigger(_gl_interrupt_recreate);
 #endif
     get_process_loop().add_trigger(_interrupt_update);
+
+    set_draw_handlers();
 }
 
 DisplayChannel::~DisplayChannel()
@@ -718,7 +798,7 @@ DisplayChannel::~DisplayChannel()
         screen()->set_update_interrupt_trigger(NULL);
     }
 
-    destroy_canvas();
+    //destroy_canvas(); fixme destroy all
     destroy_strams();
 }
 
@@ -790,33 +870,40 @@ void DisplayChannel::clear_draw_handlers()
 void DisplayChannel::copy_pixels(const QRegion& dest_region,
                                  const PixmapHeader &dest_pixmap)
 {
-    if (!_canvas.get()) {
+    Canvas *canvas;
+
+    if (!surfaces_mngr.is_present_canvas(0)) {
         return;
     }
 
-    _canvas->copy_pixels(dest_region, NULL, &dest_pixmap);
+    canvas = surfaces_mngr.get_canvas(0);
+    canvas->copy_pixels(dest_region, NULL, &dest_pixmap);
 }
 
 #ifdef USE_OGL
 void DisplayChannel::recreate_ogl_context_interrupt()
 {
-    Canvas* canvas = _canvas.release();
-    if (canvas) {
+    Canvas* canvas;
+
+    if (surfaces_mngr.is_present_canvas(0)) { //fix me to all surfaces
+        canvas = surfaces_mngr.get_canvas(0);
         ((GCanvas *)(canvas))->touch_context();
         ((GCanvas *)canvas)->textures_lost();
         delete canvas;
     }
 
-    if (!create_ogl_canvas(_x_res, _y_res, _depth, 0, _rendertype)) {
+    if (!create_ogl_canvas(0, _x_res, _y_res, _depth, 0, _rendertype)) {
         THROW("create_ogl_canvas failed");
     }
 
-    ((GCanvas *)(_canvas.get()))->touch_context();
+    canvas = surfaces_mngr.get_canvas(0);
+    ((GCanvas *)(canvas))->touch_context();
 }
 
 void DisplayChannel::recreate_ogl_context()
 {
-    if (_canvas.get() && _canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+    if (surfaces_mngr.is_present_canvas(0) && surfaces_mngr.get_canvas(0)->get_pixmap_type() ==
+        CANVAS_TYPE_GL) {
         if (!screen()->need_recreate_context_gl()) {
             _gl_interrupt_recreate.trigger();
         }
@@ -951,21 +1038,25 @@ void DisplayChannel::set_capture_mode(bool on)
 
 void DisplayChannel::update_interrupt()
 {
-    if (!_canvas.get() || !screen()) {
+    Canvas *canvas;
+
+    if (!surfaces_mngr.is_present_canvas(0) || !screen()) {
         return;
     }
 
+    canvas = surfaces_mngr.get_canvas(0);
+
 #ifdef USE_OGL
-    if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
-        ((GCanvas *)(_canvas.get()))->pre_gl_copy();
+    if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+        ((GCanvas *)(canvas))->pre_gl_copy();
     }
 #endif
 
     screen()->update();
 
 #ifdef USE_OGL
-    if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
-        ((GCanvas *)(_canvas.get()))->post_gl_copy();
+    if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+        ((GCanvas *)(canvas))->post_gl_copy();
     }
 #endif
 }
@@ -979,7 +1070,7 @@ void DisplayChannel::pre_migrate()
 void DisplayChannel::post_migrate()
 {
 #ifdef USE_OGL
-    if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+    if (surfaces_mngr.get_canvas(0)->get_pixmap_type() == CANVAS_TYPE_GL) {
         _gl_interrupt_recreate.trigger();
     }
 #endif
@@ -990,11 +1081,15 @@ void DisplayChannel::post_migrate()
 void DisplayChannel::copy_pixels(const QRegion& dest_region,
                                  RedDrawable& dest_dc)
 {
-    if (!_canvas.get()) {
+    Canvas *canvas;
+
+    if (!surfaces_mngr.is_present_canvas(0)) {
         return;
     }
 
-    _canvas->copy_pixels(dest_region, dest_dc);
+    canvas = surfaces_mngr.get_canvas(0);
+
+    canvas->copy_pixels(dest_region, dest_dc);
 }
 
 class ActivateTimerEvent: public Event {
@@ -1099,8 +1194,11 @@ void DisplayChannel::on_connect()
 
 void DisplayChannel::on_disconnect()
 {
-    if (_canvas.get()) {
-        _canvas->clear();
+    if (surfaces_mngr.is_present_canvas(0)) {
+        Canvas *canvas;
+
+        canvas = surfaces_mngr.get_canvas(0);
+        canvas->clear();
     }
 
     if (screen()) {
@@ -1120,12 +1218,14 @@ void DisplayChannel::on_disconnect()
     (*sync_event)->wait();
 }
 
-bool DisplayChannel::create_cairo_canvas(int width, int height, int depth)
+bool DisplayChannel::create_cairo_canvas(int surface_id, int width, int height, int depth)
 {
     try {
-        std::auto_ptr<CCanvas> canvas(new CCanvas(_pixmap_cache, _palette_cache, _glz_window));
+        CCanvas *canvas = new CCanvas(_pixmap_cache, _palette_cache, _glz_window,
+                                      surfaces_mngr.get_surfaces());
         canvas->set_mode(width, height, depth, screen()->get_window());
-        _canvas.reset(canvas.release());
+        surfaces_mngr.add_canvas(surface_id, canvas);
+        surfaces_mngr.add_surface(surface_id, canvas->get_internal_canvas());
         LOG_INFO("display %d: using cairo", get_id());
     } catch (...) {
         return false;
@@ -1134,27 +1234,25 @@ bool DisplayChannel::create_cairo_canvas(int width, int height, int depth)
 }
 
 #ifdef USE_OGL
-bool DisplayChannel::create_ogl_canvas(int width, int height, int depth,
+bool DisplayChannel::create_ogl_canvas(int surface_id, int width, int height, int depth,
                                        bool recreate, RenderType rendertype)
 {
     try {
         RedWindow *win;
-        int ret = 1;
 
-        std::auto_ptr<GCanvas> canvas(new GCanvas(_pixmap_cache,
-                                                  _palette_cache,
-                                                  _glz_window));
+        GCanvas *canvas = new GCanvas(_pixmap_cache,
+                                      _palette_cache,
+                                      _glz_window,
+                                      surfaces_mngr.get_surfaces());
 
         win = screen()->get_window();
-        if (!ret) {
-            return false;
-        }
 
         canvas->set_mode(width, height, depth, win, rendertype);
 
         screen()->untouch_context();
 
-        _canvas.reset(canvas.release());
+        surfaces_mngr.add_canvas(surface_id, canvas);
+        surfaces_mngr.add_surface(surface_id, canvas->get_internal_canvas());
         _rendertype = rendertype;
         LOG_INFO("display %d: using ogl", get_id());
     } catch (...) {
@@ -1166,13 +1264,14 @@ bool DisplayChannel::create_ogl_canvas(int width, int height, int depth,
 #endif
 
 #ifdef WIN32
-bool DisplayChannel::create_gdi_canvas(int width, int height, int depth)
+bool DisplayChannel::create_gdi_canvas(int surface_id, int width, int height, int depth)
 {
     try {
-        std::auto_ptr<GDICanvas> canvas(
-            new GDICanvas(_pixmap_cache, _palette_cache, _glz_window));
+        GDICanvas *canvas = new GDICanvas(_pixmap_cache, _palette_cache, _glz_window,
+                                          surfaces_mngr.get_surfaces());
         canvas->set_mode(width, height, depth);
-        _canvas.reset(canvas.release());
+        surfaces_mngr.add_canvas(surface_id, canvas);
+        surfaces_mngr.add_surface(surface_id, canvas->get_internal_canvas());
         LOG_INFO("display %d: using gdi", get_id());
     } catch (...) {
         return false;
@@ -1182,21 +1281,29 @@ bool DisplayChannel::create_gdi_canvas(int width, int height, int depth)
 
 #endif
 
-void DisplayChannel::destroy_canvas()
+void DisplayChannel::destroy_canvas(int surface_id)
 {
-    Canvas* canvas = _canvas.release();
+    Canvas *canvas;
+
+    if (!surfaces_mngr.is_present_canvas(surface_id)) {
+        return;
+    }
+    
+    canvas = surfaces_mngr.get_canvas(surface_id);
 
-    if (canvas) {
 #ifdef USE_OGL
-        if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
-            ((GCanvas *)(canvas))->touch_context();
-        }
-#endif
-        delete canvas;
+    if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+        ((GCanvas *)(canvas))->touch_context();
     }
+#endif
+
+    surfaces_mngr.del_canvas(surface_id);
+    surfaces_mngr.del_surface(surface_id);
+
+    delete canvas;
 }
 
-void DisplayChannel::create_canvas(const std::vector<int>& canvas_types, int width,
+void DisplayChannel::create_canvas(int surface_id, const std::vector<int>& canvas_types, int width,
                                    int height, int depth)
 {
 #ifdef USE_OGL
@@ -1204,8 +1311,6 @@ void DisplayChannel::create_canvas(const std::vector<int>& canvas_types, int wid
 #endif
     unsigned int i;
 
-    clear_draw_handlers();
-
 #ifdef USE_OGL
     if (screen()->need_recreate_context_gl()) {
         recreate = false;
@@ -1216,23 +1321,23 @@ void DisplayChannel::create_canvas(const std::vector<int>& canvas_types, int wid
 
     for (i = 0; i < canvas_types.size(); i++) {
 
-        if (canvas_types[i] == CANVAS_OPTION_CAIRO && create_cairo_canvas(width, height, depth)) {
+        if (canvas_types[i] == CANVAS_OPTION_CAIRO && create_cairo_canvas(surface_id, width, height, depth)) {
             break;
         }
 #ifdef USE_OGL
-        if (canvas_types[i] == CANVAS_OPTION_OGL_FBO && create_ogl_canvas(width, height, depth,
+        if (canvas_types[i] == CANVAS_OPTION_OGL_FBO && create_ogl_canvas(surface_id, width, height, depth,
                                                                           recreate,
                                                                           RENDER_TYPE_FBO)) {
             break;
         }
-        if (canvas_types[i] == CANVAS_OPTION_OGL_PBUFF && create_ogl_canvas(width, height, depth,
+        if (canvas_types[i] == CANVAS_OPTION_OGL_PBUFF && create_ogl_canvas(surface_id, width, height, depth,
                                                                             recreate,
                                                                             RENDER_TYPE_PBUFF)) {
             break;
         }
 #endif
 #ifdef WIN32
-        if (canvas_types[i] == CANVAS_OPTION_GDI && create_gdi_canvas(width, height, depth)) {
+        if (canvas_types[i] == CANVAS_OPTION_GDI && create_gdi_canvas(surface_id, width, height, depth)) {
             break;
         }
 #endif
@@ -1241,8 +1346,6 @@ void DisplayChannel::create_canvas(const std::vector<int>& canvas_types, int wid
     if (i == canvas_types.size()) {
         THROW("create canvas failed");
     }
-
-    set_draw_handlers();
 }
 
 void DisplayChannel::handle_mark(RedPeer::InMessage *message)
@@ -1262,8 +1365,10 @@ void DisplayChannel::handle_reset(RedPeer::InMessage *message)
 {
     screen()->set_update_interrupt_trigger(NULL);
 
-    if (_canvas.get()) {
-        _canvas->clear();
+    if (surfaces_mngr.is_present_canvas(0)) {
+        Canvas *canvas;
+        canvas = surfaces_mngr.get_canvas(0);
+        canvas->clear();
     }
 
     AutoRef<ResetTimer> reset_timer(new ResetTimer(screen()->ref(), get_client()));
@@ -1345,8 +1450,8 @@ void DisplayChannel::set_clip_rects(const SpiceClip& clip, uint32_t& num_clip_re
 void DisplayChannel::handle_stream_create(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayStreamCreate* stream_create = (SpiceMsgDisplayStreamCreate*)message->data();
+    int surface_id = stream_create->surface_id;
 
-    PANIC_ON(stream_create->surface_id != 0);
     Lock lock(_streams_lock);
     if (_streams.size() <= stream_create->id) {
         _streams.resize(stream_create->id + 1);
@@ -1361,7 +1466,7 @@ void DisplayChannel::handle_stream_create(RedPeer::InMessage* message)
     set_clip_rects(stream_create->clip, num_clip_rects, clip_rects,
                    (unsigned long)message->data(), (uint8_t*)(stream_create + 1),
                    message->data() + message->size());
-    _streams[stream_create->id] = new VideoStream(get_client(), *_canvas.get(),
+    _streams[stream_create->id] = new VideoStream(get_client(), *surfaces_mngr.get_canvas(surface_id),
                                                   *this, stream_create->codec_type,
                                                   !!(stream_create->flags & SPICE_STREAM_FLAGS_TOP_DOWN),
                                                   stream_create->stream_width,
@@ -1447,6 +1552,7 @@ void DisplayChannel::handle_stream_destroy_all(RedPeer::InMessage* message)
 
 void DisplayChannel::create_primary_surface(int width, int height, int depth)
 {
+   Canvas *canvas;
    _mark = false;
     attach_to_screen(get_client().get_application(), get_id());
     clear_area();
@@ -1463,21 +1569,47 @@ void DisplayChannel::create_primary_surface(int width, int height, int depth)
     _y_res = height;
     _depth = depth;
 
+    canvas = surfaces_mngr.get_canvas(0);
+
 #ifdef USE_OGL
-    if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
-        ((GCanvas *)(_canvas.get()))->touch_context();
+    if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+        ((GCanvas *)(canvas))->touch_context();
         screen()->set_update_interrupt_trigger(&_interrupt_update);
         screen()->set_type_gl();
     }
 #endif
 }
 
+void DisplayChannel::create_surface(int surface_id, int width, int height, int depth)
+{
+   Canvas *canvas;
+
+    AutoRef<CreateSurfaceEvent> event(new CreateSurfaceEvent(*this, surface_id, width, height,
+                                                             depth));
+    get_client().push_event(*event);
+    (*event)->wait();
+    if (!(*event)->success()) {
+        THROW("Create surface failed");
+    }
+
+    canvas = surfaces_mngr.get_canvas(surface_id);
+
+#ifdef USE_OGL
+    if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+        ((GCanvas *)(canvas))->touch_context();
+    }
+#endif
+}
+
 void DisplayChannel::destroy_primary_surface()
 {
 #ifdef USE_OGL
     if (screen()) {
-        if (_canvas.get()) {
-            if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+        if (surfaces_mngr.is_present_canvas(0)) {
+            Canvas *canvas;
+
+            canvas = surfaces_mngr.get_canvas(0);
+            if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
                 screen()->unset_type_gl();
                 screen()->untouch_context();
             }
@@ -1493,21 +1625,35 @@ void DisplayChannel::destroy_primary_surface()
     }
 }
 
+void DisplayChannel::destroy_surface(int surface_id)
+{
+    AutoRef<DestroySurfaceEvent> event(new DestroySurfaceEvent(*this, surface_id));
+    get_client().push_event(*event);
+    (*event)->wait();
+    if (!(*event)->success()) {
+        THROW("Destroying surface failed");
+    }
+}
+
 void DisplayChannel::handle_surface_create(RedPeer::InMessage* message)
 {
     SpiceMsgSurfaceCreate* surface_create = (SpiceMsgSurfaceCreate*)message->data();
-    PANIC_ON(surface_create->surface_id != 0);
-    PANIC_ON(surface_create->flags != SPICE_SURFACE_FLAGS_PRIMARY);
-
-    create_primary_surface(surface_create->width, surface_create->height, surface_create->depth);
+    if (surface_create->flags == SPICE_SURFACE_FLAGS_PRIMARY) {
+        create_primary_surface(surface_create->width, surface_create->height, surface_create->depth);
+    } else {
+        create_surface(surface_create->surface_id, surface_create->width, surface_create->height,
+                       surface_create->depth);
+    }
 }
 
 void DisplayChannel::handle_surface_destroy(RedPeer::InMessage* message)
 {
     SpiceMsgSurfaceDestroy* surface_destroy = (SpiceMsgSurfaceDestroy*)message->data();
-    PANIC_ON(surface_destroy->surface_id != 0);
-
-    destroy_primary_surface();
+    if (surface_destroy->surface_id == 0) { //fixme
+        destroy_primary_surface();
+    } else {
+        destroy_surface(surface_destroy->surface_id);
+    }
 }
 
 #define PRE_DRAW
@@ -1515,102 +1661,115 @@ void DisplayChannel::handle_surface_destroy(RedPeer::InMessage* message)
 
 #define DRAW(type) {                                \
     PRE_DRAW;                                       \
-    _canvas->draw_##type(*type, message->size());   \
+    canvas->draw_##type(*type, message->size());    \
     POST_DRAW;                                      \
     invalidate(type->base.box, false);              \
 }
 
 void DisplayChannel::handle_copy_bits(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayCopyBits* copy_bits = (SpiceMsgDisplayCopyBits*)message->data();
-    PANIC_ON(copy_bits->base.surface_id != 0);
     PRE_DRAW;
-    _canvas->copy_bits(*copy_bits, message->size());
+    canvas = surfaces_mngr.get_canvas(copy_bits->base.surface_id);
+    canvas->copy_bits(*copy_bits, message->size());
     POST_DRAW;
     invalidate(copy_bits->base.box, false);
 }
 
 void DisplayChannel::handle_draw_fill(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawFill* fill = (SpiceMsgDisplayDrawFill*)message->data();
-    PANIC_ON(fill->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(fill->base.surface_id);
     DRAW(fill);
 }
 
 void DisplayChannel::handle_draw_opaque(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawOpaque* opaque = (SpiceMsgDisplayDrawOpaque*)message->data();
-    PANIC_ON(opaque->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(opaque->base.surface_id);
     DRAW(opaque);
 }
 
 void DisplayChannel::handle_draw_copy(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawCopy* copy = (SpiceMsgDisplayDrawCopy*)message->data();
-    PANIC_ON(copy->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(copy->base.surface_id);
     DRAW(copy);
 }
 
 void DisplayChannel::handle_draw_blend(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawBlend* blend = (SpiceMsgDisplayDrawBlend*)message->data();
-    PANIC_ON(blend->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(blend->base.surface_id);
     DRAW(blend);
 }
 
 void DisplayChannel::handle_draw_blackness(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawBlackness* blackness = (SpiceMsgDisplayDrawBlackness*)message->data();
-    PANIC_ON(blackness->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(blackness->base.surface_id);
     DRAW(blackness);
 }
 
 void DisplayChannel::handle_draw_whiteness(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawWhiteness* whiteness = (SpiceMsgDisplayDrawWhiteness*)message->data();
-    PANIC_ON(whiteness->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(whiteness->base.surface_id);
     DRAW(whiteness);
 }
 
 void DisplayChannel::handle_draw_invers(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawInvers* invers = (SpiceMsgDisplayDrawInvers*)message->data();
-    PANIC_ON(invers->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(invers->base.surface_id);
     DRAW(invers);
 }
 
 void DisplayChannel::handle_draw_rop3(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawRop3* rop3 = (SpiceMsgDisplayDrawRop3*)message->data();
-    PANIC_ON(rop3->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(rop3->base.surface_id);
     DRAW(rop3);
 }
 
 void DisplayChannel::handle_draw_stroke(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawStroke* stroke = (SpiceMsgDisplayDrawStroke*)message->data();
-    PANIC_ON(stroke->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(stroke->base.surface_id);
     DRAW(stroke);
 }
 
 void DisplayChannel::handle_draw_text(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawText* text = (SpiceMsgDisplayDrawText*)message->data();
-    PANIC_ON(text->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(text->base.surface_id);
     DRAW(text);
 }
 
 void DisplayChannel::handle_draw_transparent(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawTransparent* transparent = (SpiceMsgDisplayDrawTransparent*)message->data();
-    PANIC_ON(transparent->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(transparent->base.surface_id);
     DRAW(transparent);
 }
 
 void DisplayChannel::handle_draw_alpha_blend(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawAlphaBlend* alpha_blend = (SpiceMsgDisplayDrawAlphaBlend*)message->data();
-    PANIC_ON(alpha_blend->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(alpha_blend->base.surface_id);
     DRAW(alpha_blend);
 }
 
diff --git a/client/display_channel.h b/client/display_channel.h
index d603a26..ac97bad 100644
--- a/client/display_channel.h
+++ b/client/display_channel.h
@@ -79,6 +79,21 @@ private:
     DisplayChannel& _channel;
 };
 
+class DisplaySurfacesManger {
+public:
+    void add_surface(int surface_id, SpiceCanvas *surface);
+    void del_surface(int surface_id);
+    void add_canvas(int surface_id, Canvas *canvas);
+    void del_canvas(int surface_id);
+
+    CSurfaces& get_surfaces();
+    bool is_present_canvas(int surface_id);
+    Canvas* get_canvas(int surface_id);
+private:
+    CSurfaces surfaces;
+    CCanvases canvases;
+};
+
 class DisplayChannel: public RedChannel, public ScreenLayer {
 public:
     DisplayChannel(RedClient& client, uint32_t id,
@@ -117,22 +132,24 @@ protected:
 private:
     void set_draw_handlers();
     void clear_draw_handlers();
-    bool create_cairo_canvas(int width, int height, int depth);
+    bool create_cairo_canvas(int surface_id, int width, int height, int depth);
 #ifdef USE_OGL
-    bool create_ogl_canvas(int width, int height, int depth, bool recreate,
+    bool create_ogl_canvas(int surface_id, int width, int height, int depth, bool recreate,
                            RenderType rendertype);
 #endif
 #ifdef WIN32
     bool create_gdi_canvas(int width, int height, int depth);
 #endif
-    void destroy_canvas();
-    void create_canvas(const std::vector<int>& canvas_type, int width, int height,
+    void destroy_canvas(int surface_id);
+    void create_canvas(int surface_id, const std::vector<int>& canvas_type, int width, int height,
                        int depth);
     void destroy_strams();
     void update_cursor();
 
     void create_primary_surface(int width, int height, int depth);
+    void create_surface(int surface_id, int width, int height, int depth);
     void destroy_primary_surface();
+    void destroy_surface(int surface_id);
 
     void handle_mode(RedPeer::InMessage* message);
     void handle_mark(RedPeer::InMessage* message);
@@ -174,7 +191,7 @@ private:
     static void set_clip_rects(const SpiceClip& clip, uint32_t& num_clip_rects, SpiceRect*& clip_rects,
                                unsigned long addr_offset, uint8_t *min, uint8_t *max);
 private:
-    std::auto_ptr<Canvas> _canvas;
+    DisplaySurfacesManger surfaces_mngr;
     PixmapCache& _pixmap_cache;
     PaletteCache _palette_cache;
     GlzDecoderWindow& _glz_window;
@@ -216,6 +233,8 @@ private:
     friend class SetModeEvent;
     friend class CreatePrimarySurfaceEvent;
     friend class DestroyPrimarySurfaceEvent;
+    friend class CreateSurfaceEvent;
+    friend class DestroySurfaceEvent;
     friend class ActivateTimerEvent;
     friend class VideoStream;
     friend class StreamsTrigger;
diff --git a/client/red_cairo_canvas.cpp b/client/red_cairo_canvas.cpp
index 02c105a..71621d1 100644
--- a/client/red_cairo_canvas.cpp
+++ b/client/red_cairo_canvas.cpp
@@ -25,8 +25,8 @@
 #include "red_pixmap_cairo.h"
 
 CCanvas::CCanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-                 GlzDecoderWindow &glz_decoder_window)
-    : Canvas (pixmap_cache, palette_cache, glz_decoder_window)
+                 GlzDecoderWindow &glz_decoder_window, CSurfaces& csurfaces)
+    : Canvas (pixmap_cache, palette_cache, glz_decoder_window, csurfaces)
     , _pixmap (0)
 {
 }
@@ -94,6 +94,7 @@ void CCanvas::set_mode(int width, int height, int depth, RedWindow *win)
     if (!(_canvas = canvas_create(surface, depth,
                                   &pixmap_cache().base,
                                   &palette_cache().base,
+                                  &csurfaces().base,
                                   &glz_decoder()))) {
         THROW("create canvas failed");
     }
diff --git a/client/red_cairo_canvas.h b/client/red_cairo_canvas.h
index 51c6c5a..ce76fef 100644
--- a/client/red_cairo_canvas.h
+++ b/client/red_cairo_canvas.h
@@ -26,7 +26,7 @@ class RedPixmap;
 class CCanvas: public Canvas {
 public:
     CCanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-            GlzDecoderWindow &glz_decoder_window);
+            GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces);
     virtual ~CCanvas();
 
     virtual void set_mode(int x, int y, int bits, RedWindow *win);
diff --git a/client/red_gdi_canvas.cpp b/client/red_gdi_canvas.cpp
index 9f38dfb..76e1542 100644
--- a/client/red_gdi_canvas.cpp
+++ b/client/red_gdi_canvas.cpp
@@ -24,8 +24,8 @@
 #include "red_pixmap_gdi.h"
 
 GDICanvas::GDICanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-                     GlzDecoderWindow &glz_decoder_window)
-    : Canvas (pixmap_cache, palette_cache, glz_decoder_window)
+                     GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces)
+    : Canvas (pixmap_cache, palette_cache, glz_decoder_window, csurfaces)
     , _pixmap (0)
 {
 }
@@ -84,6 +84,7 @@ void GDICanvas::set_mode(int width, int height, int depth)
                                       &_pixmap->get_mutex(),
                                       depth, &pixmap_cache().base,
                                       &palette_cache().base,
+                                      &csurfaces().base,
                                       &glz_decoder()))) {
         THROW("create canvas failed");
     }
diff --git a/client/red_gdi_canvas.h b/client/red_gdi_canvas.h
index 83fc120..b0a2158 100644
--- a/client/red_gdi_canvas.h
+++ b/client/red_gdi_canvas.h
@@ -28,7 +28,7 @@ class RedPixmap;
 class GDICanvas: public Canvas {
 public:
     GDICanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-              GlzDecoderWindow &glz_decoder_window);
+              GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces);
     virtual ~GDICanvas();
 
     virtual void set_mode(int x, int y, int bits);
diff --git a/client/red_gl_canvas.cpp b/client/red_gl_canvas.cpp
index c6bb113..f9c4256 100644
--- a/client/red_gl_canvas.cpp
+++ b/client/red_gl_canvas.cpp
@@ -25,8 +25,8 @@
 #include <GL/glx.h>
 
 GCanvas::GCanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-                 GlzDecoderWindow &glz_decoder_window)
-    : Canvas(pixmap_cache, palette_cache, glz_decoder_window)
+                 GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces)
+    : Canvas(pixmap_cache, palette_cache, glz_decoder_window, csurfaces)
     , _pixmap (0)
     , _textures_lost (false)
 {
@@ -92,6 +92,7 @@ void GCanvas::set_mode(int width, int height, int depth, RedWindow *win,
     if (!(_canvas = gl_canvas_create(width, height, depth,
                                      &pixmap_cache().base,
                                      &palette_cache().base,
+                                     &csurfaces().base,
                                      &glz_decoder()))) {
         THROW("create canvas failed");
     }
diff --git a/client/red_gl_canvas.h b/client/red_gl_canvas.h
index 983f772..4a9f5a9 100644
--- a/client/red_gl_canvas.h
+++ b/client/red_gl_canvas.h
@@ -29,7 +29,7 @@ class RedPixmapGL;
 class GCanvas: public Canvas {
 public:
     GCanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-            GlzDecoderWindow &glz_decoder_window);
+            GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces);
     virtual ~GCanvas();
 
     void set_mode(int width, int height, int depth, RedWindow *win,
diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 770ca4f..17a40a4 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -51,11 +51,18 @@ static pixman_image_t *canvas_get_pixman_brush(CairoCanvas *canvas,
 
         return pixman_image_create_solid_fill(&c);
     }
-    case SPICE_BRUSH_TYPE_PATTERN: {
+    case SPICE_BRUSH_TYPE_PATTERN: { 
+        CairoCanvas *surface_canvas;
         pixman_image_t* surface;
         pixman_transform_t t;
 
-        surface = canvas_get_image(&canvas->base, brush->u.pattern.pat);
+        surface_canvas = (CairoCanvas *)canvas_get_surface(&canvas->base, brush->u.pattern.pat);
+        if (surface_canvas) {
+            surface = surface_canvas->image;
+            surface = pixman_image_ref(surface);
+        } else {
+            surface = canvas_get_image(&canvas->base, brush->u.pattern.pat);
+        }
         pixman_transform_init_translate(&t,
                                         pixman_int_to_fixed(-brush->u.pattern.pos.x),
                                         pixman_int_to_fixed(-brush->u.pattern.pos.y));
@@ -70,6 +77,14 @@ static pixman_image_t *canvas_get_pixman_brush(CairoCanvas *canvas,
     }
 }
 
+static pixman_image_t *get_image(SpiceCanvas *canvas)
+{
+    CairoCanvas *cairo_canvas = (CairoCanvas *)canvas;
+
+    pixman_image_ref(cairo_canvas->image);
+
+    return cairo_canvas->image;
+}
 
 static void copy_region(SpiceCanvas *spice_canvas,
                         pixman_region32_t *dest_region,
@@ -194,16 +209,16 @@ static void fill_solid_rects_rop(SpiceCanvas *spice_canvas,
     }
 }
 
-static void fill_tiled_rects(SpiceCanvas *spice_canvas,
-                             pixman_box32_t *rects,
-                             int n_rects,
-                             pixman_image_t *tile,
-                             int offset_x, int offset_y)
+static void __fill_tiled_rects(SpiceCanvas *spice_canvas,
+                               pixman_box32_t *rects,
+                               int n_rects,
+                               pixman_image_t *tile,
+                               int offset_x, int offset_y)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     int i;
 
-   for (i = 0; i < n_rects; i++) {
+    for (i = 0; i < n_rects; i++) {
         spice_pixman_tile_rect(canvas->image,
                                rects[i].x1, rects[i].y1,
                                rects[i].x2 - rects[i].x1,
@@ -212,17 +227,37 @@ static void fill_tiled_rects(SpiceCanvas *spice_canvas,
     }
 }
 
-static void fill_tiled_rects_rop(SpiceCanvas *spice_canvas,
-                                 pixman_box32_t *rects,
-                                 int n_rects,
-                                 pixman_image_t *tile,
-                                 int offset_x, int offset_y,
-                                 SpiceROP rop)
+static void fill_tiled_rects(SpiceCanvas *spice_canvas,
+                               pixman_box32_t *rects,
+                               int n_rects,
+                               pixman_image_t *tile,
+                               int offset_x, int offset_y)
+{
+    __fill_tiled_rects(spice_canvas, rects, n_rects, tile, offset_x, offset_y);
+}
+
+static void fill_tiled_rects_from_surface(SpiceCanvas *spice_canvas,
+                                          pixman_box32_t *rects,
+                                          int n_rects,
+                                          SpiceCanvas *surface_canvas,
+                                          int offset_x, int offset_y)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __fill_tiled_rects(spice_canvas, rects, n_rects, cairo_surface_canvas->image, offset_x,
+                       offset_y);
+}
+
+static void __fill_tiled_rects_rop(SpiceCanvas *spice_canvas,
+                                   pixman_box32_t *rects,
+                                   int n_rects,
+                                   pixman_image_t *tile,
+                                   int offset_x, int offset_y,
+                                   SpiceROP rop)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     int i;
 
-   for (i = 0; i < n_rects; i++) {
+    for (i = 0; i < n_rects; i++) {
         spice_pixman_tile_rect_rop(canvas->image,
                                    rects[i].x1, rects[i].y1,
                                    rects[i].x2 - rects[i].x1,
@@ -231,11 +266,32 @@ static void fill_tiled_rects_rop(SpiceCanvas *spice_canvas,
                                    rop);
     }
 }
+static void fill_tiled_rects_rop(SpiceCanvas *spice_canvas,
+                                 pixman_box32_t *rects,
+                                 int n_rects,
+                                 pixman_image_t *tile,
+                                 int offset_x, int offset_y,
+                                 SpiceROP rop)
+{
+    __fill_tiled_rects_rop(spice_canvas, rects, n_rects, tile, offset_x, offset_y, rop);
+}
 
-static void blit_image(SpiceCanvas *spice_canvas,
-                       pixman_region32_t *region,
-                       pixman_image_t *src_image,
-                       int offset_x, int offset_y)
+static void fill_tiled_rects_rop_from_surface(SpiceCanvas *spice_canvas,
+                                              pixman_box32_t *rects,
+                                              int n_rects,
+                                              SpiceCanvas *surface_canvas,
+                                              int offset_x, int offset_y,
+                                              SpiceROP rop)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __fill_tiled_rects_rop(spice_canvas, rects, n_rects, cairo_surface_canvas->image, offset_x,
+                           offset_y, rop);
+}
+
+static void __blit_image(SpiceCanvas *spice_canvas,
+                         pixman_region32_t *region,
+                         pixman_image_t *src_image,
+                         int offset_x, int offset_y)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_box32_t *rects;
@@ -262,11 +318,28 @@ static void blit_image(SpiceCanvas *spice_canvas,
     }
 }
 
-static void blit_image_rop(SpiceCanvas *spice_canvas,
-                           pixman_region32_t *region,
-                           pixman_image_t *src_image,
-                           int offset_x, int offset_y,
-                           SpiceROP rop)
+static void blit_image(SpiceCanvas *spice_canvas,
+                       pixman_region32_t *region,
+                       pixman_image_t *src_image,
+                       int offset_x, int offset_y)
+{
+    __blit_image(spice_canvas, region, src_image, offset_x, offset_y);
+}
+
+static void blit_image_from_surface(SpiceCanvas *spice_canvas,
+                                    pixman_region32_t *region,
+                                    SpiceCanvas *surface_canvas,
+                                    int offset_x, int offset_y)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __blit_image(spice_canvas, region, cairo_surface_canvas->image, offset_x, offset_y);
+}
+
+static void __blit_image_rop(SpiceCanvas *spice_canvas,
+                             pixman_region32_t *region,
+                             pixman_image_t *src_image,
+                             int offset_x, int offset_y,
+                             SpiceROP rop)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_box32_t *rects;
@@ -293,14 +366,35 @@ static void blit_image_rop(SpiceCanvas *spice_canvas,
     }
 }
 
-static void scale_image(SpiceCanvas *spice_canvas,
-                        pixman_region32_t *region,
-                        pixman_image_t *src,
-                        int src_x, int src_y,
-                        int src_width, int src_height,
-                        int dest_x, int dest_y,
-                        int dest_width, int dest_height,
-                        int scale_mode)
+static void blit_image_rop(SpiceCanvas *spice_canvas,
+                           pixman_region32_t *region,
+                           pixman_image_t *src_image,
+                           int offset_x, int offset_y,
+                           SpiceROP rop)
+{
+    __blit_image_rop(spice_canvas, region, src_image, offset_x, offset_y, rop);
+}
+
+static void blit_image_rop_from_surface(SpiceCanvas *spice_canvas,
+                                        pixman_region32_t *region,
+                                        SpiceCanvas *surface_canvas,
+                                        int offset_x, int offset_y,
+                                        SpiceROP rop)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __blit_image_rop(spice_canvas, region, cairo_surface_canvas->image, offset_x, offset_y, rop);
+}
+
+
+
+static void __scale_image(SpiceCanvas *spice_canvas,
+                          pixman_region32_t *region,
+                          pixman_image_t *src,
+                          int src_x, int src_y,
+                          int src_width, int src_height,
+                          int dest_x, int dest_y,
+                          int dest_width, int dest_height,
+                          int scale_mode)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_transform_t transform;
@@ -337,14 +431,41 @@ static void scale_image(SpiceCanvas *spice_canvas,
     pixman_image_set_clip_region32(canvas->image, NULL);
 }
 
-static void scale_image_rop(SpiceCanvas *spice_canvas,
-                            pixman_region32_t *region,
-                            pixman_image_t *src,
-                            int src_x, int src_y,
-                            int src_width, int src_height,
-                            int dest_x, int dest_y,
-                            int dest_width, int dest_height,
-                            int scale_mode, SpiceROP rop)
+static void scale_image(SpiceCanvas *spice_canvas,
+                        pixman_region32_t *region,
+                        pixman_image_t *src,
+                        int src_x, int src_y,
+                        int src_width, int src_height,
+                        int dest_x, int dest_y,
+                        int dest_width, int dest_height,
+                        int scale_mode)
+{
+    __scale_image(spice_canvas, region, src, src_x, src_y, src_width, src_height, dest_x, dest_y,
+                  dest_width,dest_height,scale_mode);
+}
+
+static void scale_image_from_surface(SpiceCanvas *spice_canvas,
+                                     pixman_region32_t *region,
+                                     SpiceCanvas *surface_canvas,
+                                     int src_x, int src_y,
+                                     int src_width, int src_height,
+                                     int dest_x, int dest_y,
+                                     int dest_width, int dest_height,
+                                     int scale_mode)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __scale_image(spice_canvas, region, cairo_surface_canvas->image, src_x, src_y, src_width,
+                  src_height, dest_x, dest_y, dest_width,dest_height,scale_mode);
+}
+
+static void __scale_image_rop(SpiceCanvas *spice_canvas,
+                              pixman_region32_t *region,
+                              pixman_image_t *src,
+                              int src_x, int src_y,
+                              int src_width, int src_height,
+                              int dest_x, int dest_y,
+                              int dest_width, int dest_height,
+                              int scale_mode, SpiceROP rop)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_transform_t transform;
@@ -407,13 +528,40 @@ static void scale_image_rop(SpiceCanvas *spice_canvas,
     pixman_image_unref(scaled);
 }
 
-static void blend_image(SpiceCanvas *spice_canvas,
-                        pixman_region32_t *region,
-                        pixman_image_t *src,
-                        int src_x, int src_y,
-                        int dest_x, int dest_y,
-                        int width, int height,
-                        int overall_alpha)
+static void scale_image_rop(SpiceCanvas *spice_canvas,
+                            pixman_region32_t *region,
+                            pixman_image_t *src,
+                            int src_x, int src_y,
+                            int src_width, int src_height,
+                            int dest_x, int dest_y,
+                            int dest_width, int dest_height,
+                            int scale_mode, SpiceROP rop)
+{
+    __scale_image_rop(spice_canvas, region, src, src_x, src_y, src_width, src_height, dest_x,
+                      dest_y, dest_width, dest_height, scale_mode, rop);
+}
+
+static void scale_image_rop_from_surface(SpiceCanvas *spice_canvas,
+                                         pixman_region32_t *region,
+                                         SpiceCanvas *surface_canvas,
+                                         int src_x, int src_y,
+                                         int src_width, int src_height,
+                                         int dest_x, int dest_y,
+                                         int dest_width, int dest_height,
+                                         int scale_mode, SpiceROP rop)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __scale_image_rop(spice_canvas, region, cairo_surface_canvas->image, src_x, src_y, src_width,
+                      src_height, dest_x, dest_y, dest_width, dest_height, scale_mode, rop);
+}
+
+static void __blend_image(SpiceCanvas *spice_canvas,
+                          pixman_region32_t *region,
+                          pixman_image_t *src,
+                          int src_x, int src_y,
+                          int dest_x, int dest_y,
+                          int width, int height,
+                          int overall_alpha)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_image_t *mask;
@@ -444,15 +592,40 @@ static void blend_image(SpiceCanvas *spice_canvas,
     pixman_image_set_clip_region32(canvas->image, NULL);
 }
 
-static void blend_scale_image(SpiceCanvas *spice_canvas,
-                              pixman_region32_t *region,
-                              pixman_image_t *src,
-                              int src_x, int src_y,
-                              int src_width, int src_height,
-                              int dest_x, int dest_y,
-                              int dest_width, int dest_height,
-                              int scale_mode,
-                              int overall_alpha)
+static void blend_image(SpiceCanvas *spice_canvas,
+                        pixman_region32_t *region,
+                        pixman_image_t *src,
+                        int src_x, int src_y,
+                        int dest_x, int dest_y,
+                        int width, int height,
+                        int overall_alpha)
+{
+    __blend_image(spice_canvas, region, src, src_x, src_y, dest_x, dest_y, width, height,
+                  overall_alpha);
+}
+
+static void blend_image_from_surface(SpiceCanvas *spice_canvas,
+                                     pixman_region32_t *region,
+                                     SpiceCanvas *surface_canvas,
+                                     int src_x, int src_y,
+                                     int dest_x, int dest_y,
+                                     int width, int height,
+                                     int overall_alpha)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __blend_image(spice_canvas, region, cairo_surface_canvas->image, src_x, src_y, dest_x, dest_y,
+                  width, height, overall_alpha);
+}
+
+static void __blend_scale_image(SpiceCanvas *spice_canvas,
+                                pixman_region32_t *region,
+                                pixman_image_t *src,
+                                int src_x, int src_y,
+                                int src_width, int src_height,
+                                int dest_x, int dest_y,
+                                int dest_width, int dest_height,
+                                int scale_mode,
+                                int overall_alpha)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_transform_t transform;
@@ -501,11 +674,41 @@ static void blend_scale_image(SpiceCanvas *spice_canvas,
     pixman_image_set_clip_region32(canvas->image, NULL);
 }
 
-static void colorkey_image(SpiceCanvas *spice_canvas,
-                           pixman_region32_t *region,
-                           pixman_image_t *src_image,
-                           int offset_x, int offset_y,
-                           uint32_t transparent_color)
+static void blend_scale_image(SpiceCanvas *spice_canvas,
+                              pixman_region32_t *region,
+                              pixman_image_t *src,
+                              int src_x, int src_y,
+                              int src_width, int src_height,
+                              int dest_x, int dest_y,
+                              int dest_width, int dest_height,
+                              int scale_mode,
+                              int overall_alpha)
+{
+    __blend_scale_image(spice_canvas, region, src, src_x, src_y, src_width, src_height, dest_x,
+                        dest_y, dest_width, dest_height, scale_mode, overall_alpha);
+}
+
+static void blend_scale_image_from_surface(SpiceCanvas *spice_canvas,
+                                           pixman_region32_t *region,
+                                           SpiceCanvas *surface_canvas,
+                                           int src_x, int src_y,
+                                           int src_width, int src_height,
+                                           int dest_x, int dest_y,
+                                           int dest_width, int dest_height,
+                                           int scale_mode,
+                                           int overall_alpha)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __blend_scale_image(spice_canvas, region, cairo_surface_canvas->image, src_x, src_y, src_width,
+                        src_height, dest_x, dest_y, dest_width, dest_height, scale_mode,
+                        overall_alpha);
+}
+
+static void __colorkey_image(SpiceCanvas *spice_canvas,
+                             pixman_region32_t *region,
+                             pixman_image_t *src_image,
+                             int offset_x, int offset_y,
+                             uint32_t transparent_color)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_box32_t *rects;
@@ -533,14 +736,34 @@ static void colorkey_image(SpiceCanvas *spice_canvas,
     }
 }
 
-static void colorkey_scale_image(SpiceCanvas *spice_canvas,
-                                 pixman_region32_t *region,
-                                 pixman_image_t *src,
-                                 int src_x, int src_y,
-                                 int src_width, int src_height,
-                                 int dest_x, int dest_y,
-                                 int dest_width, int dest_height,
-                                 uint32_t transparent_color)
+static void colorkey_image(SpiceCanvas *spice_canvas,
+                           pixman_region32_t *region,
+                           pixman_image_t *src_image,
+                           int offset_x, int offset_y,
+                           uint32_t transparent_color)
+{
+    __colorkey_image(spice_canvas, region, src_image, offset_x, offset_y, transparent_color);
+}
+
+static void colorkey_image_from_surface(SpiceCanvas *spice_canvas,
+                                        pixman_region32_t *region,
+                                        SpiceCanvas *surface_canvas,
+                                        int offset_x, int offset_y,
+                                        uint32_t transparent_color)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __colorkey_image(spice_canvas, region, cairo_surface_canvas->image, offset_x, offset_y,
+                     transparent_color);
+}
+
+static void __colorkey_scale_image(SpiceCanvas *spice_canvas,
+                                   pixman_region32_t *region,
+                                   pixman_image_t *src,
+                                   int src_x, int src_y,
+                                   int src_width, int src_height,
+                                   int dest_x, int dest_y,
+                                   int dest_width, int dest_height,
+                                   uint32_t transparent_color)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_transform_t transform;
@@ -600,6 +823,34 @@ static void colorkey_scale_image(SpiceCanvas *spice_canvas,
     pixman_image_unref(scaled);
 }
 
+static void colorkey_scale_image(SpiceCanvas *spice_canvas,
+                                 pixman_region32_t *region,
+                                 pixman_image_t *src,
+                                 int src_x, int src_y,
+                                 int src_width, int src_height,
+                                 int dest_x, int dest_y,
+                                 int dest_width, int dest_height,
+                                 uint32_t transparent_color)
+{
+    __colorkey_scale_image(spice_canvas, region, src, src_x, src_y, src_width, src_height, dest_x,
+                           dest_y, dest_width, dest_height, transparent_color);
+}
+
+static void colorkey_scale_image_from_surface(SpiceCanvas *spice_canvas,
+                                              pixman_region32_t *region,
+                                              SpiceCanvas *surface_canvas,
+                                              int src_x, int src_y,
+                                              int src_width, int src_height,
+                                              int dest_x, int dest_y,
+                                              int dest_width, int dest_height,
+                                              uint32_t transparent_color)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __colorkey_scale_image(spice_canvas, region, cairo_surface_canvas->image, src_x, src_y,
+                           src_width, src_height, dest_x, dest_y, dest_width, dest_height,
+                           transparent_color);
+}
+
 static void canvas_put_image(SpiceCanvas *spice_canvas,
 #ifdef WIN32
                              HDC dc,
@@ -803,6 +1054,7 @@ SpiceCanvas *canvas_create(pixman_image_t *image, int bits
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                            , SpiceImageCache *bits_cache
 #endif
+                           , SpiceImageSurfaces *surfaces
                            , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , SpiceVirtMapping *virt_mapping
@@ -826,6 +1078,7 @@ SpiceCanvas *canvas_create(pixman_image_t *image, int bits
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                                , bits_cache
 #endif
+                               , surfaces
                                , glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                                , virt_mapping
@@ -858,15 +1111,26 @@ void cairo_canvas_init() //unsafe global function
     cairo_canvas_ops.fill_solid_rects = fill_solid_rects;
     cairo_canvas_ops.fill_solid_rects_rop = fill_solid_rects_rop;
     cairo_canvas_ops.fill_tiled_rects = fill_tiled_rects;
+    cairo_canvas_ops.fill_tiled_rects_from_surface = fill_tiled_rects_from_surface;
     cairo_canvas_ops.fill_tiled_rects_rop = fill_tiled_rects_rop;
+    cairo_canvas_ops.fill_tiled_rects_rop_from_surface = fill_tiled_rects_rop_from_surface;
     cairo_canvas_ops.blit_image = blit_image;
+    cairo_canvas_ops.blit_image_from_surface = blit_image_from_surface;
     cairo_canvas_ops.blit_image_rop = blit_image_rop;
+    cairo_canvas_ops.blit_image_rop_from_surface = blit_image_rop_from_surface;
     cairo_canvas_ops.scale_image = scale_image;
+    cairo_canvas_ops.scale_image_from_surface = scale_image_from_surface;
     cairo_canvas_ops.scale_image_rop = scale_image_rop;
+    cairo_canvas_ops.scale_image_rop_from_surface = scale_image_rop_from_surface;
     cairo_canvas_ops.blend_image = blend_image;
+    cairo_canvas_ops.blend_image_from_surface = blend_image_from_surface;
     cairo_canvas_ops.blend_scale_image = blend_scale_image;
+    cairo_canvas_ops.blend_scale_image_from_surface = blend_scale_image_from_surface;
     cairo_canvas_ops.colorkey_image = colorkey_image;
+    cairo_canvas_ops.colorkey_image_from_surface = colorkey_image_from_surface;
     cairo_canvas_ops.colorkey_scale_image = colorkey_scale_image;
+    cairo_canvas_ops.colorkey_scale_image_from_surface = colorkey_scale_image_from_surface;
     cairo_canvas_ops.copy_region = copy_region;
+    cairo_canvas_ops.get_image = get_image;
     rop3_init();
 }
diff --git a/common/cairo_canvas.h b/common/cairo_canvas.h
index 3f6fbbc..e65b407 100644
--- a/common/cairo_canvas.h
+++ b/common/cairo_canvas.h
@@ -33,6 +33,7 @@ SpiceCanvas *canvas_create(pixman_image_t *image, int bits
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                            , SpiceImageCache *bits_cache
 #endif
+			   , SpiceImageSurfaces *surfaces
                            , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , SpiceVirtMapping *virt_mapping
diff --git a/common/canvas_base.c b/common/canvas_base.c
index e13a005..e15a971 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -190,6 +190,8 @@ typedef struct CanvasBase {
     HDC dc;
 #endif
 
+    SpiceImageSurfaces *surfaces;
+
     LzData lz_data;
     GlzData glz_data;
 
@@ -948,6 +950,34 @@ static void dump_surface(pixman_image_t *surface, int cache)
 
 #endif
 
+static SpiceCanvas *canvas_get_surface_internal(CanvasBase *canvas, SPICE_ADDRESS addr)
+{
+    SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
+    access_test(canvas, descriptor, sizeof(SpiceImageDescriptor));
+
+    if (descriptor->type == SPICE_IMAGE_TYPE_SURFACE) {
+        SpiceSurfaceImage *surface = (SpiceSurfaceImage *)descriptor;
+        access_test(canvas, descriptor, sizeof(SpiceSurfaceImage));
+        return canvas->surfaces->ops->get(canvas->surfaces, surface->surface.surface_id);
+    }
+    return NULL;
+}
+
+static SpiceCanvas *canvas_get_surface_mask_internal(CanvasBase *canvas, SPICE_ADDRESS addr)
+{
+    SpiceImageDescriptor *descriptor;
+ 
+    descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
+    access_test(canvas, descriptor, sizeof(SpiceImageDescriptor));
+
+    if (descriptor->type == SPICE_IMAGE_TYPE_SURFACE) {
+        SpiceSurfaceImage *surface = (SpiceSurfaceImage *)descriptor;
+        access_test(canvas, descriptor, sizeof(SpiceSurfaceImage));
+        return canvas->surfaces->ops->get(canvas->surfaces, surface->surface.surface_id);
+    }
+    return NULL;
+}
+
 #if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
 
 //#define DEBUG_LZ
@@ -1065,6 +1095,16 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
 
 #endif
 
+static SpiceCanvas *canvas_get_surface_mask(CanvasBase *canvas, SPICE_ADDRESS addr)
+{
+    return canvas_get_surface_mask_internal(canvas, addr);
+}
+
+static SpiceCanvas *canvas_get_surface(CanvasBase *canvas, SPICE_ADDRESS addr)
+{
+    return canvas_get_surface_internal(canvas, addr);
+}
+
 static pixman_image_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr)
 {
     return canvas_get_image_internal(canvas, addr, TRUE);
@@ -1318,10 +1358,6 @@ static pixman_image_t *canvas_get_mask(CanvasBase *canvas, SpiceQMask *mask, int
         *needs_invert_out = 0;
     }
 
-    if (!mask->bitmap) {
-        return NULL;
-    }
-
     descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(mask->bitmap);
     access_test(canvas, descriptor, sizeof(SpiceImageDescriptor));
     need_invers = mask->flags & SPICE_MASK_FLAGS_INVERS;
@@ -1807,6 +1843,7 @@ static void canvas_mask_pixman(CanvasBase *canvas,
                                pixman_region32_t *dest_region,
                                SpiceQMask *mask, int x, int y)
 {
+    SpiceCanvas *surface_canvas;
     pixman_image_t *image, *subimage;
     int needs_invert;
     pixman_region32_t mask_region;
@@ -1815,13 +1852,19 @@ static void canvas_mask_pixman(CanvasBase *canvas,
     int mask_width, mask_height, mask_stride;
     pixman_box32_t extents;
 
-    needs_invert = FALSE;
-    image = canvas_get_mask(canvas,
-                            mask,
-                            &needs_invert);
+    if (!mask->bitmap) {
+        return;
+    }
 
-    if (image == NULL) {
-        return; /* no mask */
+    surface_canvas = canvas_get_surface_mask(canvas, mask->bitmap);
+    if (surface_canvas) {
+        needs_invert = mask->flags & SPICE_MASK_FLAGS_INVERS;
+        image = surface_canvas->ops->get_image(surface_canvas);
+    } else {
+        needs_invert = FALSE;
+        image = canvas_get_mask(canvas,
+                                mask,
+                                &needs_invert);
     }
 
     mask_data = pixman_image_get_data(image);
@@ -1921,20 +1964,35 @@ static void draw_brush(SpiceCanvas *canvas,
             canvas->ops->fill_solid_rects_rop(canvas, rects, n_rects, color, rop);
         }
         break;
-    case SPICE_BRUSH_TYPE_PATTERN:
+        case SPICE_BRUSH_TYPE_PATTERN: {
+        SpiceCanvas *surface_canvas;
+
         pattern = &brush->u.pattern;
-        tile = canvas_get_image(canvas_base, pattern->pat);
         offset_x = pattern->pos.x;
         offset_y = pattern->pos.y;
 
-        if (rop == SPICE_ROP_COPY) {
-            canvas->ops->fill_tiled_rects(canvas, rects, n_rects, tile, offset_x, offset_y);
+        surface_canvas = canvas_get_surface(canvas_base, pattern->pat);
+        if (surface_canvas) {
+            if (rop == SPICE_ROP_COPY) {
+                canvas->ops->fill_tiled_rects_from_surface(canvas, rects, n_rects, surface_canvas,
+                                                           offset_x, offset_y);
+            } else {
+                canvas->ops->fill_tiled_rects_rop_from_surface(canvas, rects, n_rects,
+                                                               surface_canvas, offset_x, offset_y,
+                                                               rop);
+            }
         } else {
-            canvas->ops->fill_tiled_rects_rop(canvas, rects, n_rects,
-                                              tile, offset_x, offset_y, rop);
+            tile = canvas_get_image(canvas_base, pattern->pat);
+            if (rop == SPICE_ROP_COPY) {
+                canvas->ops->fill_tiled_rects(canvas, rects, n_rects, tile, offset_x, offset_y);
+            } else {
+                canvas->ops->fill_tiled_rects_rop(canvas, rects, n_rects,
+                                                  tile, offset_x, offset_y, rop);
+            }
+            pixman_image_unref(tile);
         }
-        pixman_image_unref(tile);
         break;
+    }
     case SPICE_BRUSH_TYPE_NONE:
         /* Still need to do *something* here, because rop could be e.g invert dest */
         canvas->ops->fill_solid_rects_rop(canvas, rects, n_rects, 0, rop);
@@ -1991,6 +2049,7 @@ static void canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceCl
 {
     CanvasBase *canvas = (CanvasBase *)spice_canvas;
     pixman_region32_t dest_region;
+    SpiceCanvas *surface_canvas;
     pixman_image_t *src_image;
     SpiceROP rop;
 
@@ -2013,36 +2072,67 @@ static void canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceCl
         return;
     }
 
-    src_image = canvas_get_image(canvas, copy->src_bitmap);
-
-    if (rect_is_same_size(bbox, &copy->src_area)) {
-        if (rop == SPICE_ROP_COPY) {
-            spice_canvas->ops->blit_image(spice_canvas, &dest_region,
-                                          src_image,
-                                          bbox->left - copy->src_area.left,
-                                          bbox->top - copy->src_area.top);
+    surface_canvas = canvas_get_surface(canvas, copy->src_bitmap);
+    if (surface_canvas) {
+        if (rect_is_same_size(bbox, &copy->src_area)) {
+            if (rop == SPICE_ROP_COPY) {
+                spice_canvas->ops->blit_image_from_surface(spice_canvas, &dest_region,
+                                                           surface_canvas,
+                                                           bbox->left - copy->src_area.left,
+                                                           bbox->top - copy->src_area.top);
+            } else {
+                spice_canvas->ops->blit_image_rop_from_surface(spice_canvas, &dest_region,
+                                                               surface_canvas,
+                                                               bbox->left - copy->src_area.left,
+                                                               bbox->top - copy->src_area.top,
+                                                               rop);
+            }
         } else {
-            spice_canvas->ops->blit_image_rop(spice_canvas, &dest_region,
-                                              src_image,
-                                              bbox->left - copy->src_area.left,
-                                              bbox->top - copy->src_area.top,
-                                              rop);
+            if (rop == SPICE_ROP_COPY) {
+                spice_canvas->ops->scale_image_from_surface(spice_canvas, &dest_region,
+                                                            surface_canvas,
+                                                            copy->src_area.left,
+                                                            copy->src_area.top,
+                                                            copy->src_area.right - copy->src_area.left,
+                                                            copy->src_area.bottom - copy->src_area.top,
+                                                            bbox->left,
+                                                            bbox->top,
+                                                            bbox->right - bbox->left,
+                                                            bbox->bottom - bbox->top,
+                                                            copy->scale_mode);
+            } else {
+                spice_canvas->ops->scale_image_rop_from_surface(spice_canvas, &dest_region,
+                                                                surface_canvas,
+                                                                copy->src_area.left,
+                                                                copy->src_area.top,
+                                                                copy->src_area.right - copy->src_area.left,
+                                                                copy->src_area.bottom - copy->src_area.top,
+                                                                bbox->left,
+                                                                bbox->top,
+                                                                bbox->right - bbox->left,
+                                                                bbox->bottom - bbox->top,
+                                                                copy->scale_mode,
+                                                                rop);
+            }
         }
     } else {
-        if (rop == SPICE_ROP_COPY) {
-            spice_canvas->ops->scale_image(spice_canvas, &dest_region,
-                                           src_image,
-                                           copy->src_area.left,
-                                           copy->src_area.top,
-                                           copy->src_area.right - copy->src_area.left,
-                                           copy->src_area.bottom - copy->src_area.top,
-                                           bbox->left,
-                                           bbox->top,
-                                           bbox->right - bbox->left,
-                                           bbox->bottom - bbox->top,
-                                           copy->scale_mode);
+        src_image = canvas_get_image(canvas, copy->src_bitmap);
+        if (rect_is_same_size(bbox, &copy->src_area)) {
+            if (rop == SPICE_ROP_COPY) {
+                spice_canvas->ops->blit_image(spice_canvas, &dest_region,
+                                              src_image,
+                                              bbox->left - copy->src_area.left,
+                                              bbox->top - copy->src_area.top);
+            } else {
+                spice_canvas->ops->blit_image_rop(spice_canvas, &dest_region,
+                                                  src_image,
+                                                  bbox->left - copy->src_area.left,
+                                                  bbox->top - copy->src_area.top,
+                                                  rop);
+            }
         } else {
-            spice_canvas->ops->scale_image_rop(spice_canvas, &dest_region,
+            if (rop == SPICE_ROP_COPY) {
+                spice_canvas->ops->scale_image(spice_canvas, &dest_region,
                                                src_image,
                                                copy->src_area.left,
                                                copy->src_area.top,
@@ -2052,18 +2142,31 @@ static void canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceCl
                                                bbox->top,
                                                bbox->right - bbox->left,
                                                bbox->bottom - bbox->top,
-                                               copy->scale_mode,
-                                               rop);
+                                               copy->scale_mode);
+            } else {
+                spice_canvas->ops->scale_image_rop(spice_canvas, &dest_region,
+                                                   src_image,
+                                                   copy->src_area.left,
+                                                   copy->src_area.top,
+                                                   copy->src_area.right - copy->src_area.left,
+                                                   copy->src_area.bottom - copy->src_area.top,
+                                                   bbox->left,
+                                                   bbox->top,
+                                                   bbox->right - bbox->left,
+                                                   bbox->bottom - bbox->top,
+                                                   copy->scale_mode,
+                                                   rop);
+            }
         }
+        pixman_image_unref(src_image);
     }
-
-    pixman_image_unref(src_image);
     pixman_region32_fini(&dest_region);
 }
 
 static void canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent)
 {
     CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    SpiceCanvas *surface_canvas;
     pixman_image_t *src_image;
     pixman_region32_t dest_region;
 
@@ -2080,29 +2183,50 @@ static void canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bbox,
         return;
     }
 
-    src_image = canvas_get_image(canvas, transparent->src_bitmap);
-
-    if (rect_is_same_size(bbox, &transparent->src_area)) {
-        spice_canvas->ops->colorkey_image(spice_canvas, &dest_region,
-                                          src_image,
-                                          bbox->left - transparent->src_area.left,
-                                          bbox->top - transparent->src_area.top,
-                                          transparent->true_color);
+    surface_canvas = canvas_get_surface(canvas, transparent->src_bitmap);
+    if (surface_canvas) {
+        if (rect_is_same_size(bbox, &transparent->src_area)) {
+            spice_canvas->ops->colorkey_image_from_surface(spice_canvas, &dest_region,
+                                                           surface_canvas,
+                                                           bbox->left - transparent->src_area.left,
+                                                           bbox->top - transparent->src_area.top,
+                                                           transparent->true_color);
+        } else {
+            spice_canvas->ops->colorkey_scale_image_from_surface(spice_canvas, &dest_region,
+                                                                 surface_canvas,
+                                                                 transparent->src_area.left,
+                                                                 transparent->src_area.top,
+                                                                 transparent->src_area.right - transparent->src_area.left,
+                                                                 transparent->src_area.bottom - transparent->src_area.top,
+                                                                 bbox->left,
+                                                                 bbox->top,
+                                                                 bbox->right - bbox->left,
+                                                                 bbox->bottom - bbox->top,
+                                                                 transparent->true_color);
+        }
     } else {
-        spice_canvas->ops->colorkey_scale_image(spice_canvas, &dest_region,
-                                                src_image,
-                                                transparent->src_area.left,
-                                                transparent->src_area.top,
-                                                transparent->src_area.right - transparent->src_area.left,
-                                                transparent->src_area.bottom - transparent->src_area.top,
-                                                bbox->left,
-                                                bbox->top,
-                                                bbox->right - bbox->left,
-                                                bbox->bottom - bbox->top,
-                                                transparent->true_color);
+        src_image = canvas_get_image(canvas, transparent->src_bitmap);
+        if (rect_is_same_size(bbox, &transparent->src_area)) {
+            spice_canvas->ops->colorkey_image(spice_canvas, &dest_region,
+                                              src_image,
+                                              bbox->left - transparent->src_area.left,
+                                              bbox->top - transparent->src_area.top,
+                                              transparent->true_color);
+        } else {
+            spice_canvas->ops->colorkey_scale_image(spice_canvas, &dest_region,
+                                                    src_image,
+                                                    transparent->src_area.left,
+                                                    transparent->src_area.top,
+                                                    transparent->src_area.right - transparent->src_area.left,
+                                                    transparent->src_area.bottom - transparent->src_area.top,
+                                                    bbox->left,
+                                                    bbox->top,
+                                                    bbox->right - bbox->left,
+                                                    bbox->bottom - bbox->top,
+                                                    transparent->true_color);
+        }
+        pixman_image_unref(src_image);
     }
-
-    pixman_image_unref(src_image);
     pixman_region32_fini(&dest_region);
 }
 
@@ -2110,6 +2234,7 @@ static void canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox,
 {
     CanvasBase *canvas = (CanvasBase *)spice_canvas;
     pixman_region32_t dest_region;
+    SpiceCanvas *surface_canvas;
     pixman_image_t *src_image;
 
     pixman_region32_init_rect(&dest_region,
@@ -2126,34 +2251,62 @@ static void canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox,
         return;
     }
 
-    src_image = canvas_get_image(canvas, alpha_blend->src_bitmap);
-
-    if (rect_is_same_size(bbox, &alpha_blend->src_area)) {
-        spice_canvas->ops->blend_image(spice_canvas, &dest_region,
-                                       src_image,
-                                       alpha_blend->src_area.left,
-                                       alpha_blend->src_area.top,
-                                       bbox->left,
-                                       bbox->top,
-                                       bbox->right - bbox->left,
-                                       bbox->bottom - bbox->top,
-                                       alpha_blend->alpha);
-    } else {
-        spice_canvas->ops->blend_scale_image(spice_canvas, &dest_region,
-                                             src_image,
-                                             alpha_blend->src_area.left,
-                                             alpha_blend->src_area.top,
-                                             alpha_blend->src_area.right - alpha_blend->src_area.left,
-                                             alpha_blend->src_area.bottom - alpha_blend->src_area.top,
-                                             bbox->left,
-                                             bbox->top,
-                                             bbox->right - bbox->left,
-                                             bbox->bottom - bbox->top,
-                                             SPICE_IMAGE_SCALE_MODE_NEAREST,
-                                             alpha_blend->alpha);
+    surface_canvas = canvas_get_surface(canvas, alpha_blend->src_bitmap);
+    if (surface_canvas) {
+        if (rect_is_same_size(bbox, &alpha_blend->src_area)) {
+            spice_canvas->ops->blend_image_from_surface(spice_canvas, &dest_region,
+                                                        surface_canvas,
+                                                        alpha_blend->src_area.left,
+                                                        alpha_blend->src_area.top,
+                                                        bbox->left,
+                                                        bbox->top,
+                                                        bbox->right - bbox->left,
+                                                        bbox->bottom - bbox->top,
+                                                        alpha_blend->alpha);
+        } else {
+            spice_canvas->ops->blend_scale_image_from_surface(spice_canvas, &dest_region,
+                                                              surface_canvas,
+                                                              alpha_blend->src_area.left,
+                                                              alpha_blend->src_area.top,
+                                                              alpha_blend->src_area.right - alpha_blend->src_area.left,
+                                                              alpha_blend->src_area.bottom - alpha_blend->src_area.top,
+                                                              bbox->left,
+                                                              bbox->top,
+                                                              bbox->right - bbox->left,
+                                                              bbox->bottom - bbox->top,
+                                                              SPICE_IMAGE_SCALE_MODE_NEAREST,
+                                                              alpha_blend->alpha);
+        }
+     } else {
+        src_image = canvas_get_image(canvas, alpha_blend->src_bitmap);
+        if (rect_is_same_size(bbox, &alpha_blend->src_area)) {
+            spice_canvas->ops->blend_image(spice_canvas, &dest_region,
+                                           src_image,
+                                           alpha_blend->src_area.left,
+                                           alpha_blend->src_area.top,
+                                           bbox->left,
+                                           bbox->top,
+                                           bbox->right - bbox->left,
+                                           bbox->bottom - bbox->top,
+                                           alpha_blend->alpha);
+        } else {
+            spice_canvas->ops->blend_scale_image(spice_canvas, &dest_region,
+                                                 src_image,
+                                                 alpha_blend->src_area.left,
+                                                 alpha_blend->src_area.top,
+                                                 alpha_blend->src_area.right - alpha_blend->src_area.left,
+                                                 alpha_blend->src_area.bottom - alpha_blend->src_area.top,
+                                                 bbox->left,
+                                                 bbox->top,
+                                                 bbox->right - bbox->left,
+                                                 bbox->bottom - bbox->top,
+                                                 SPICE_IMAGE_SCALE_MODE_NEAREST,
+                                                 alpha_blend->alpha);
+        }
+ 
+        pixman_image_unref(src_image);
     }
 
-    pixman_image_unref(src_image);
     pixman_region32_fini(&dest_region);
 }
 
@@ -2214,6 +2367,7 @@ static void canvas_draw_opaque(SpiceCanvas *spice_canvas, SpiceRect *bbox, Spice
 static void canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
 {
     CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    SpiceCanvas *surface_canvas;
     pixman_image_t *src_image;
     pixman_region32_t dest_region;
     SpiceROP rop;
@@ -2237,40 +2391,74 @@ static void canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceC
         return;
     }
 
-    src_image = canvas_get_image(canvas, blend->src_bitmap);
-
-    if (rect_is_same_size(bbox, &blend->src_area)) {
-        if (rop == SPICE_ROP_COPY)
-            spice_canvas->ops->blit_image(spice_canvas, &dest_region,
-                                          src_image,
-                                          bbox->left - blend->src_area.left,
-                                          bbox->top - blend->src_area.top);
-        else
-            spice_canvas->ops->blit_image_rop(spice_canvas, &dest_region,
+    surface_canvas = canvas_get_surface(canvas, blend->src_bitmap);
+    if (surface_canvas) {
+        if (rect_is_same_size(bbox, &blend->src_area)) {
+            if (rop == SPICE_ROP_COPY)
+                spice_canvas->ops->blit_image_from_surface(spice_canvas, &dest_region,
+                                                           surface_canvas,
+                                                           bbox->left - blend->src_area.left,
+                                                           bbox->top - blend->src_area.top);
+            else
+                spice_canvas->ops->blit_image_rop_from_surface(spice_canvas, &dest_region,
+                                                               surface_canvas,
+                                                               bbox->left - blend->src_area.left,
+                                                               bbox->top - blend->src_area.top,
+                                                               rop);
+        } else {
+            double sx, sy;
+    
+            sx = (double)(blend->src_area.right - blend->src_area.left) / (bbox->right - bbox->left);
+            sy = (double)(blend->src_area.bottom - blend->src_area.top) / (bbox->bottom - bbox->top);
+    
+            if (rop == SPICE_ROP_COPY) {
+                spice_canvas->ops->scale_image_from_surface(spice_canvas, &dest_region,
+                                                            surface_canvas,
+                                                            blend->src_area.left,
+                                                            blend->src_area.top,
+                                                            blend->src_area.right - blend->src_area.left,
+                                                            blend->src_area.bottom - blend->src_area.top,
+                                                            bbox->left,
+                                                            bbox->top,
+                                                            bbox->right - bbox->left,
+                                                            bbox->bottom - bbox->top,
+                                                            blend->scale_mode);
+            } else {
+                spice_canvas->ops->scale_image_rop_from_surface(spice_canvas, &dest_region,
+                                                                surface_canvas,
+                                                                blend->src_area.left,
+                                                                blend->src_area.top,
+                                                                blend->src_area.right - blend->src_area.left,
+                                                                blend->src_area.bottom - blend->src_area.top,
+                                                                bbox->left,
+                                                                bbox->top,
+                                                                bbox->right - bbox->left,
+                                                                bbox->bottom - bbox->top,
+                                                                blend->scale_mode, rop);
+            }
+        }
+    } else {
+        src_image = canvas_get_image(canvas, blend->src_bitmap);
+        if (rect_is_same_size(bbox, &blend->src_area)) {
+            if (rop == SPICE_ROP_COPY)
+                spice_canvas->ops->blit_image(spice_canvas, &dest_region,
                                               src_image,
                                               bbox->left - blend->src_area.left,
-                                              bbox->top - blend->src_area.top,
-                                              rop);
-    } else {
-        double sx, sy;
-
-        sx = (double)(blend->src_area.right - blend->src_area.left) / (bbox->right - bbox->left);
-        sy = (double)(blend->src_area.bottom - blend->src_area.top) / (bbox->bottom - bbox->top);
-
-        if (rop == SPICE_ROP_COPY) {
-            spice_canvas->ops->scale_image(spice_canvas, &dest_region,
-                                           src_image,
-                                           blend->src_area.left,
-                                           blend->src_area.top,
-                                           blend->src_area.right - blend->src_area.left,
-                                           blend->src_area.bottom - blend->src_area.top,
-                                           bbox->left,
-                                           bbox->top,
-                                           bbox->right - bbox->left,
-                                           bbox->bottom - bbox->top,
-                                           blend->scale_mode);
+                                              bbox->top - blend->src_area.top);
+            else
+                spice_canvas->ops->blit_image_rop(spice_canvas, &dest_region,
+                                                  src_image,
+                                                  bbox->left - blend->src_area.left,
+                                                  bbox->top - blend->src_area.top,
+                                                  rop);
         } else {
-            spice_canvas->ops->scale_image_rop(spice_canvas, &dest_region,
+            double sx, sy;
+    
+            sx = (double)(blend->src_area.right - blend->src_area.left) / (bbox->right - bbox->left);
+            sy = (double)(blend->src_area.bottom - blend->src_area.top) / (bbox->bottom - bbox->top);
+    
+            if (rop == SPICE_ROP_COPY) {
+                spice_canvas->ops->scale_image(spice_canvas, &dest_region,
                                                src_image,
                                                blend->src_area.left,
                                                blend->src_area.top,
@@ -2280,11 +2468,23 @@ static void canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceC
                                                bbox->top,
                                                bbox->right - bbox->left,
                                                bbox->bottom - bbox->top,
-                                               blend->scale_mode, rop);
+                                               blend->scale_mode);
+            } else {
+                spice_canvas->ops->scale_image_rop(spice_canvas, &dest_region,
+                                                   src_image,
+                                                   blend->src_area.left,
+                                                   blend->src_area.top,
+                                                   blend->src_area.right - blend->src_area.left,
+                                                   blend->src_area.bottom - blend->src_area.top,
+                                                   bbox->left,
+                                                   bbox->top,
+                                                   bbox->right - bbox->left,
+                                                   bbox->bottom - bbox->top,
+                                                   blend->scale_mode, rop);
+            }
         }
+        pixman_image_unref(src_image);
     }
-
-    pixman_image_unref(src_image);
     pixman_region32_fini(&dest_region);
 }
 
@@ -2382,7 +2582,11 @@ typedef struct {
     SpiceROP back_rop;
     int solid;
     uint32_t color;
-    pixman_image_t *tile;
+    int use_surface_canvas;
+    union {
+        SpiceCanvas *surface_canvas;
+        pixman_image_t *tile;
+    };
     int tile_offset_x;
     int tile_offset_y;
 } StrokeGC;
@@ -2505,16 +2709,31 @@ static void stroke_fill_rects(lineGC * pGC,
         }
     } else {
         if (rop == SPICE_ROP_COPY) {
-            canvas->ops->fill_tiled_rects(canvas, area_rects, n_area_rects,
-                                          strokeGC->tile,
-                                          strokeGC->tile_offset_x,
-                                          strokeGC->tile_offset_y);
-        } else {
-            canvas->ops->fill_tiled_rects_rop(canvas, area_rects, n_area_rects,
+            if (strokeGC->use_surface_canvas) {
+                canvas->ops->fill_tiled_rects_from_surface(canvas, area_rects, n_area_rects,
+                                                           strokeGC->surface_canvas,
+                                                           strokeGC->tile_offset_x,
+                                                           strokeGC->tile_offset_y);
+            } else {
+                canvas->ops->fill_tiled_rects(canvas, area_rects, n_area_rects,
                                               strokeGC->tile,
                                               strokeGC->tile_offset_x,
-                                              strokeGC->tile_offset_y,
-                                              rop);
+                                              strokeGC->tile_offset_y);
+            }
+        } else {
+            if (strokeGC->use_surface_canvas) {
+                canvas->ops->fill_tiled_rects_rop_from_surface(canvas, area_rects, n_area_rects,
+                                                               strokeGC->surface_canvas,
+                                                               strokeGC->tile_offset_x,
+                                                               strokeGC->tile_offset_y,
+                                                               rop);
+            } else {
+                canvas->ops->fill_tiled_rects_rop(canvas, area_rects, n_area_rects,
+                                                  strokeGC->tile,
+                                                  strokeGC->tile_offset_x,
+                                                  strokeGC->tile_offset_y,
+                                                  rop);
+            }
         }
     }
 
@@ -2661,6 +2880,7 @@ static void canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox,
                                SpiceClip *clip, SpiceStroke *stroke)
 {
     CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    SpiceCanvas *surface_canvas = NULL;
     StrokeGC gc = { { 0 } };
     lineGCOps ops = {
         stroke_fill_spans,
@@ -2754,8 +2974,16 @@ static void canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox,
         break;
     case SPICE_BRUSH_TYPE_PATTERN:
         gc.solid = FALSE;
-        gc.tile = canvas_get_image(canvas,
-                                   stroke->brush.u.pattern.pat);
+        surface_canvas = canvas_get_surface(canvas,
+                                            stroke->brush.u.pattern.pat);
+        if (surface_canvas) {
+            gc.use_surface_canvas = TRUE;
+            gc.surface_canvas = surface_canvas;
+        } else {
+            gc.use_surface_canvas = FALSE;
+            gc.tile = canvas_get_image(canvas,
+                                       stroke->brush.u.pattern.pat);
+        }
         gc.tile_offset_x = stroke->brush.u.pattern.pos.x;
         gc.tile_offset_y = stroke->brush.u.pattern.pos.y;
         break;
@@ -2817,17 +3045,20 @@ static void canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox,
     }
     stroke_lines_free(&lines);
 
-    if (!gc.solid && gc.tile) {
+    if (!gc.solid && gc.tile && !surface_canvas) {
         pixman_image_unref(gc.tile);
     }
 
     pixman_region32_fini(&gc.dest_region);
 }
 
+
+//need surfaces handling here !!!
 static void canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox,
                              SpiceClip *clip, SpiceRop3 *rop3)
 {
     CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    SpiceCanvas *surface_canvas;
     pixman_region32_t dest_region;
     pixman_image_t *d;
     pixman_image_t *s;
@@ -2848,7 +3079,12 @@ static void canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox,
     heigth = bbox->bottom - bbox->top;
 
     d = canvas_get_image_from_self(spice_canvas, bbox->left, bbox->top, width, heigth);
-    s = canvas_get_image(canvas, rop3->src_bitmap);
+    surface_canvas = canvas_get_surface(canvas, rop3->src_bitmap);
+    if (surface_canvas) {
+        s = surface_canvas->ops->get_image(surface_canvas);
+    } else {
+        s = canvas_get_image(canvas, rop3->src_bitmap);
+    }
 
     if (!rect_is_same_size(bbox, &rop3->src_area)) {
         pixman_image_t *scaled_s = canvas_scale_surface(s, &rop3->src_area, width, heigth,
@@ -2866,7 +3102,15 @@ static void canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox,
         CANVAS_ERROR("bad src bitmap size");
     }
     if (rop3->brush.type == SPICE_BRUSH_TYPE_PATTERN) {
-        pixman_image_t *p = canvas_get_image(canvas, rop3->brush.u.pattern.pat);
+        SpiceCanvas *_surface_canvas;
+        pixman_image_t *p;
+
+        _surface_canvas = canvas_get_surface(canvas, rop3->brush.u.pattern.pat);
+        if (_surface_canvas) {
+            p = _surface_canvas->ops->get_image(_surface_canvas);
+        } else {
+            p = canvas_get_image(canvas, rop3->brush.u.pattern.pat);
+        }
         SpicePoint pat_pos;
 
         pat_pos.x = (bbox->left - rop3->brush.u.pattern.pos.x) % pixman_image_get_width(p);
@@ -2988,6 +3232,7 @@ static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops,
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                             , SpiceImageCache *bits_cache
 #endif
+                            , SpiceImageSurfaces *surfaces
                             , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                             , SpiceVirtMapping *virt_mapping
@@ -3020,6 +3265,7 @@ static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops,
             return 0;
     }
 #endif
+    canvas->surfaces = surfaces;
     canvas->glz_data.decoder = glz_decoder;
 
     if (depth == 16) {
diff --git a/common/canvas_base.h b/common/canvas_base.h
index bed1e1b..0a663bd 100644
--- a/common/canvas_base.h
+++ b/common/canvas_base.h
@@ -28,6 +28,7 @@
 typedef void (*spice_destroy_fn_t)(void *data);
 
 typedef struct _SpiceImageCache SpiceImageCache;
+typedef struct _SpiceImageSurfaces SpiceImageSurfaces;
 typedef struct _SpicePaletteCache SpicePaletteCache;
 typedef struct _SpiceGlzDecoder SpiceGlzDecoder;
 typedef struct _SpiceVirtMapping SpiceVirtMapping;
@@ -46,6 +47,15 @@ struct _SpiceImageCache {
 };
 
 typedef struct {
+ SpiceCanvas *(*get)(SpiceImageSurfaces *surfaces,
+	             uint32_t surface_id);
+} SpiceImageSurfacesOps;
+
+struct _SpiceImageSurfaces {
+ SpiceImageSurfacesOps *ops;
+};
+
+typedef struct {
     void (*put)(SpicePaletteCache *cache,
                 SpicePalette *palette);
     SpicePalette *(*get)(SpicePaletteCache *cache,
@@ -127,21 +137,41 @@ typedef struct {
                              int n_rects,
                              pixman_image_t *tile,
                              int offset_x, int offset_y);
+    void (*fill_tiled_rects_from_surface)(SpiceCanvas *canvas,
+					  pixman_box32_t *rects,
+					  int n_rects,
+					  SpiceCanvas *tile,
+					  int offset_x, int offset_y);
     void (*fill_tiled_rects_rop)(SpiceCanvas *canvas,
                                  pixman_box32_t *rects,
                                  int n_rects,
                                  pixman_image_t *tile,
                                  int offset_x, int offset_y,
                                  SpiceROP rop);
+    void (*fill_tiled_rects_rop_from_surface)(SpiceCanvas *canvas,
+					      pixman_box32_t *rects,
+					      int n_rects,
+					      SpiceCanvas *tile,
+					      int offset_x, int offset_y,
+					      SpiceROP rop);
     void (*blit_image)(SpiceCanvas *canvas,
                        pixman_region32_t *region,
                        pixman_image_t *src_image,
                        int offset_x, int offset_y);
+    void (*blit_image_from_surface)(SpiceCanvas *canvas,
+				    pixman_region32_t *region,
+				    SpiceCanvas *src_image,
+				    int offset_x, int offset_y);
     void (*blit_image_rop)(SpiceCanvas *canvas,
                            pixman_region32_t *region,
                            pixman_image_t *src_image,
                            int offset_x, int offset_y,
                            SpiceROP rop);
+    void (*blit_image_rop_from_surface)(SpiceCanvas *canvas,
+					pixman_region32_t *region,
+					SpiceCanvas *src_image,
+					int offset_x, int offset_y,
+					SpiceROP rop);
     void (*scale_image)(SpiceCanvas *canvas,
                         pixman_region32_t *region,
                         pixman_image_t *src_image,
@@ -150,6 +180,14 @@ typedef struct {
                         int dest_x, int dest_y,
                         int dest_width, int dest_height,
                         int scale_mode);
+    void (*scale_image_from_surface)(SpiceCanvas *canvas,
+				     pixman_region32_t *region,
+				     SpiceCanvas *src_image,
+				     int src_x, int src_y,
+				     int src_width, int src_height,
+				     int dest_x, int dest_y,
+				     int dest_width, int dest_height,
+				     int scale_mode);
     void (*scale_image_rop)(SpiceCanvas *canvas,
                             pixman_region32_t *region,
                             pixman_image_t *src_image,
@@ -158,6 +196,14 @@ typedef struct {
                             int dest_x, int dest_y,
                             int dest_width, int dest_height,
                             int scale_mode, SpiceROP rop);
+    void (*scale_image_rop_from_surface)(SpiceCanvas *canvas,
+					 pixman_region32_t *region,
+					 SpiceCanvas *src_image,
+					 int src_x, int src_y,
+					 int src_width, int src_height,
+					 int dest_x, int dest_y,
+					 int dest_width, int dest_height,
+					 int scale_mode, SpiceROP rop);
     void (*blend_image)(SpiceCanvas *canvas,
                         pixman_region32_t *region,
                         pixman_image_t *src_image,
@@ -165,6 +211,13 @@ typedef struct {
                         int dest_x, int dest_y,
                         int width, int height,
                         int overall_alpha);
+    void (*blend_image_from_surface)(SpiceCanvas *canvas,
+				     pixman_region32_t *region,
+				     SpiceCanvas *src_image,
+				     int src_x, int src_y,
+				     int dest_x, int dest_y,
+				     int width, int height,
+				     int overall_alpha);
     void (*blend_scale_image)(SpiceCanvas *canvas,
                               pixman_region32_t *region,
                               pixman_image_t *src_image,
@@ -174,11 +227,25 @@ typedef struct {
                               int dest_width, int dest_height,
                               int scale_mode,
                               int overall_alpha);
+    void (*blend_scale_image_from_surface)(SpiceCanvas *canvas,
+					   pixman_region32_t *region,
+					   SpiceCanvas *src_image,
+					   int src_x, int src_y,
+					   int src_width, int src_height,
+					   int dest_x, int dest_y,
+					   int dest_width, int dest_height,
+					   int scale_mode,
+					   int overall_alpha);
     void (*colorkey_image)(SpiceCanvas *canvas,
                            pixman_region32_t *region,
                            pixman_image_t *src_image,
                            int offset_x, int offset_y,
                            uint32_t transparent_color);
+    void (*colorkey_image_from_surface)(SpiceCanvas *canvas,
+					pixman_region32_t *region,
+					SpiceCanvas *src_image,
+					int offset_x, int offset_y,
+					uint32_t transparent_color);
     void (*colorkey_scale_image)(SpiceCanvas *canvas,
                                  pixman_region32_t *region,
                                  pixman_image_t *src_image,
@@ -187,9 +254,18 @@ typedef struct {
                                  int dest_x, int dest_y,
                                  int dest_width, int dest_height,
                                  uint32_t transparent_color);
+    void (*colorkey_scale_image_from_surface)(SpiceCanvas *canvas,
+					      pixman_region32_t *region,
+					      SpiceCanvas *src_image,
+					      int src_x, int src_y,
+					      int src_width, int src_height,
+					      int dest_x, int dest_y,
+					      int dest_width, int dest_height,
+					      uint32_t transparent_color);
     void (*copy_region)(SpiceCanvas *canvas,
                         pixman_region32_t *dest_region,
                         int dx, int dy);
+    pixman_image_t *(*get_image)(SpiceCanvas *canvas);
 } SpiceCanvasOps;
 
 void spice_canvas_set_usr_data(SpiceCanvas *canvas, void *data, spice_destroy_fn_t destroy_fn);
diff --git a/common/gdi_canvas.c b/common/gdi_canvas.c
index 5eb31fe..d127180 100644
--- a/common/gdi_canvas.c
+++ b/common/gdi_canvas.c
@@ -1724,6 +1724,7 @@ SpiceCanvas *gdi_canvas_create(int width, int height,
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                              , SpiceImageCache *bits_cache
 #endif
+                            , SpiceImageSurfaces *surfaces
                             , SpiceGlzDecoder *glz_decoder
                             )
 {
@@ -1742,6 +1743,7 @@ SpiceCanvas *gdi_canvas_create(int width, int height,
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                                , bits_cache
 #endif
+                               , surfaces
                                , glz_decoder);
     canvas->dc = dc;
     canvas->lock = lock;
diff --git a/common/gdi_canvas.h b/common/gdi_canvas.h
index 8b8f847..73d6978 100644
--- a/common/gdi_canvas.h
+++ b/common/gdi_canvas.h
@@ -37,6 +37,7 @@ SpiceCanvas *gdi_canvas_create(int width, int height,
                                HDC dc, class Mutex *lock, int bits,
                                SpiceImageCache *bits_cache,
                                SpicePaletteCache *palette_cache,
+			       SpiceImageSurfaces *surfaces,
                                SpiceGlzDecoder *glz_decoder);
 
 void gdi_canvas_init();
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
index f69c920..3fd49f2 100644
--- a/common/gl_canvas.c
+++ b/common/gl_canvas.c
@@ -828,6 +828,7 @@ SpiceCanvas *gl_canvas_create(int width, int height, int depth
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                               , SpiceImageCache *bits_cache
 #endif
+                              , SpiceImageSurfaces *surfaces
                               , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                               , SpiceVirtMapping *virt_mapping
@@ -854,6 +855,7 @@ SpiceCanvas *gl_canvas_create(int width, int height, int depth
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                                , bits_cache
 #endif
+                               , surfaces
                                , glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                                , virt_mapping
diff --git a/common/gl_canvas.h b/common/gl_canvas.h
index 29737ec..6d23629 100644
--- a/common/gl_canvas.h
+++ b/common/gl_canvas.h
@@ -28,6 +28,7 @@ SpiceCanvas *gl_canvas_create(int width, int height, int depth
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                            , SpiceImageCache *bits_cache
 #endif
+			   , SpiceImageSurfaces *surfaces
                            , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                             , SpiceVirtMapping *virt_mapping
diff --git a/server/red_dispatcher.c b/server/red_dispatcher.c
index 17bf24c..88059f6 100644
--- a/server/red_dispatcher.c
+++ b/server/red_dispatcher.c
@@ -522,6 +522,7 @@ RedDispatcher *red_dispatcher_init(QXLInterface *qxl_interface)
     init_data.num_memslots = init_info.num_memslots;
     init_data.num_memslots_groups = init_info.num_memslots_groups;
     init_data.internal_groupslot_id = init_info.internal_groupslot_id;
+    init_data.n_surfaces = init_info.n_surfaces;
 
     num_active_workers = 1;
 
diff --git a/server/red_worker.c b/server/red_worker.c
index a2b441d..ef8652c 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -395,6 +395,7 @@ typedef struct ImageItem {
     int height;
     int stride;
     int top_down;
+    int surface_id;
     uint8_t data[0];
 } ImageItem;
 
@@ -459,6 +460,7 @@ typedef struct  __attribute__ ((__packed__)) RedImage {
         SpiceQUICData quic;
         SpiceLZRGBData lz_rgb;
         SpiceLZPLTData lz_plt;
+        SpiceSurface surface;
     };
 } RedImage;
 
@@ -611,6 +613,8 @@ typedef struct GlzSharedDictionary {
     int migrate_freeze;
 } GlzSharedDictionary;
 
+#define NUM_SURFACES 10000
+
 struct DisplayChannel {
     RedChannel base;
 
@@ -638,7 +642,7 @@ struct DisplayChannel {
     Ring glz_drawables_inst_to_free;               // list of instances to be freed
     pthread_mutex_t glz_drawables_inst_to_free_lock;
 
-    uint8_t surface_client_created;
+    uint8_t surface_client_created[NUM_SURFACES];
 
     struct {
         union {
@@ -796,8 +800,14 @@ typedef enum {
     BITMAP_GRADUAL_FALSE,
 } BitmapGradualType;
 
+typedef struct DependItem {
+    Drawable *drawable;
+    RingItem ring_item;
+} DependItem;
+
 struct Drawable {
     uint8_t refs;
+    RingItem surface_list_link;
     RingItem list_link;
     DrawItem tree_item;
     PipeItem pipe_item;
@@ -819,6 +829,7 @@ struct Drawable {
     BitmapGradualType copy_bitmap_graduality;
     uint32_t group_id;
     uint8_t *self_bitmap;
+    DependItem depend_items[3];
 };
 
 typedef struct _Drawable _Drawable;
@@ -858,7 +869,16 @@ typedef struct DrawContext {
 
 typedef struct RedSurface {
     uint32_t refs;
+    Ring current;
+    Ring current_list;
+    int current_gn;
     DrawContext context;
+
+    Ring depend_on_me;
+
+    //fix me - better handling here
+    QXLReleaseInfo *release_info;
+    uint32_t release_group_id;
 } RedSurface;
 
 #ifdef STREAM_TRACE
@@ -905,10 +925,11 @@ typedef struct RedWorker {
     uint32_t renderers[RED_MAX_RENDERERS];
     uint32_t renderer;
 
-    RedSurface surface;
+    RedSurface surfaces[NUM_SURFACES];
+    uint32_t n_surfaces;
+    SpiceImageSurfaces image_surfaces;
 
     Ring current_list;
-    Ring current;
     uint32_t current_size;
     uint32_t drawable_count;
     uint32_t transparent_count;
@@ -988,14 +1009,14 @@ typedef struct RedWorker {
 pthread_mutex_t avcodec_lock = PTHREAD_MUTEX_INITIALIZER;
 
 static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable);
-static void red_current_flush(RedWorker *worker);
+static void red_current_flush(RedWorker *worker, int surface_id);
 static void display_channel_push(RedWorker *worker);
 #ifdef DRAW_ALL
-#define red_update_area(worker, rect)
+#define red_update_area(worker, rect, surface_id)
 #define red_draw_drawable(worker, item)
 #else
 static void red_draw_drawable(RedWorker *worker, Drawable *item);
-static void red_update_area(RedWorker *worker, const SpiceRect *area);
+static void red_update_area(RedWorker *worker, const SpiceRect *area, int surface_id);
 #endif
 static void red_release_cursor(RedWorker *worker, CursorItem *cursor);
 static inline void release_drawable(RedWorker *worker, Drawable *item);
@@ -1068,6 +1089,31 @@ static void print_compress_stats(DisplayChannel *display_channel)
 
 #endif
 
+static inline int is_primary_surface(RedWorker *worker, uint32_t surface_id)
+{
+    if (surface_id == 0) {
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static inline void __validate_surface(RedWorker *worker, uint32_t surface_id)
+{
+    PANIC_ON(surface_id >= worker->n_surfaces);
+}
+
+static inline void validate_surface(RedWorker *worker, uint32_t surface_id)
+{
+    if (surface_id >= worker->n_surfaces) {
+        red_printf("hrmm %d", surface_id);
+    }
+    if (!worker->surfaces[surface_id].context.canvas) {
+        red_printf("hrmm2 %d", surface_id);
+    }
+    PANIC_ON(surface_id >= worker->n_surfaces);
+    PANIC_ON(!worker->surfaces[surface_id].context.canvas);
+}
+
 static inline int get_memslot_id(RedWorker *worker, unsigned long addr)
 {
     return addr >> worker->memslot_id_shift;
@@ -1343,11 +1389,45 @@ static void red_pipe_add_type(RedChannel* channel, int pipe_item_type)
     red_pipe_add(channel, item);
 }
 
+static inline void red_create_surface_item(RedWorker *worker, int surface_id);
+static void red_add_surface_image(RedWorker *worker, int surface_id);
+
+static inline void red_handle_drawable_surfaces_client_synced(RedWorker *worker, Drawable *drawable)
+{
+    int x;
+    int surface_id;
+
+    for (x = 0; x < 3; ++x) {
+        surface_id = drawable->qxl_drawable->surfaces_dest[x];
+        if (surface_id != -1) {
+            validate_surface(worker, surface_id);
+            if (worker->display_channel->surface_client_created[surface_id] == TRUE) {
+                continue;
+            }
+            red_create_surface_item(worker, surface_id);
+            red_add_surface_image(worker, surface_id);
+        }
+    }
+
+    surface_id = drawable->qxl_drawable->surface_id;
+    validate_surface(worker, surface_id);
+
+    if (worker->display_channel->surface_client_created[surface_id] == TRUE) {
+        return;
+    }
+
+    red_create_surface_item(worker, surface_id);
+    red_add_surface_image(worker, surface_id);
+}
+
 static inline void red_pipe_add_drawable(RedWorker *worker, Drawable *drawable)
 {
     if (!worker->display_channel) {
         return;
     }
+
+    red_handle_drawable_surfaces_client_synced(worker, drawable);
+
     drawable->refs++;
     red_pipe_add(&worker->display_channel->base, &drawable->pipe_item);
 }
@@ -1367,6 +1447,8 @@ static inline void red_pipe_add_drawable_after(RedWorker *worker, Drawable *draw
     red_pipe_add_after(&worker->display_channel->base, &drawable->pipe_item, &pos_after->pipe_item);
 }
 
+static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id);
+
 static inline void red_pipe_remove_drawable(RedWorker *worker, Drawable *drawable)
 {
     if (ring_item_is_linked(&drawable->pipe_item.link)) {
@@ -1530,40 +1612,62 @@ static inline void red_destroy_surface_item(RedWorker *worker, uint32_t surface_
         return;
     }
 
+    worker->display_channel->surface_client_created[surface_id] = FALSE;
+
     destroy = get_surface_destroy_item(surface_id);
 
     red_pipe_add(&worker->display_channel->base, &destroy->pipe_item);
 }
 
-static inline void __red_destroy_surface(RedWorker *worker)
+static inline void __red_destroy_surface(RedWorker *worker, uint32_t surface_id)
 {
-    RedSurface *surface = &worker->surface;
+    RedSurface *surface = &worker->surfaces[surface_id];
 
-    if (!--worker->surface.refs) {
+    if (!--surface->refs) {
 #ifdef STREAM_TRACE
         red_reset_stream_trace(worker);
 #endif
         if (surface->context.canvas) {
             surface->context.canvas->ops->destroy(surface->context.canvas);
+            if (surface->release_info) {
+                QXLReleaseInfoExt release_info_ext;
+
+                release_info_ext.group_id = surface->release_group_id;
+                release_info_ext.info = surface->release_info;
+                worker->qxl->release_resource(worker->qxl, release_info_ext);
+            }
+
             surface->context.canvas = NULL;
-            red_destroy_surface_item(worker, 0);
+            red_destroy_surface_item(worker, surface_id);
         }
+
+        PANIC_ON(!ring_is_empty(&surface->depend_on_me));
     }
 }
 
-static inline void red_destroy_surface(RedWorker *worker)
+static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id)
 {
-    RedSurface *surface = &worker->surface;
+    validate_surface(worker, surface_id);
+    __red_destroy_surface(worker, surface_id);
+}
+
+static inline void set_surface_release_info(RedWorker *worker, uint32_t surface_id,
+                                            QXLReleaseInfo *release_info, uint32_t group_id)
+{
+    RedSurface *surface;
+
+    validate_surface(worker, surface_id);
+    surface = &worker->surfaces[surface_id];
 
-    PANIC_ON(!surface->context.canvas);
-    __red_destroy_surface(worker);
+    surface->release_info = release_info;
+    surface->release_group_id = group_id;
 }
 
 static inline void free_qxl_drawable(RedWorker *worker, QXLDrawable *drawable, uint32_t group_id,
                                      uint8_t *self_bitmap)
 {
     QXLReleaseInfoExt release_info_ext;
-    red_destroy_surface(worker);
+    red_destroy_surface(worker, drawable->surface_id);
 
     if (self_bitmap) {
         free(self_bitmap);
@@ -1573,6 +1677,41 @@ static inline void free_qxl_drawable(RedWorker *worker, QXLDrawable *drawable, u
     worker->qxl->release_resource(worker->qxl, release_info_ext);
 }
 
+static void remove_depended_item(DependItem *item)
+{
+    ASSERT(item->drawable);
+    ASSERT(ring_item_is_linked(&item->ring_item));
+    item->drawable = NULL;
+    ring_remove(&item->ring_item);
+}
+
+static inline void red_dec_surfaces_drawable_dependencies(RedWorker *worker, QXLDrawable *drawable)
+{
+    int x;
+    int surface_id;
+
+    for (x = 0; x < 3; ++x) {
+        surface_id = drawable->surfaces_dest[x];
+        if (surface_id == -1) {
+            continue;
+        }
+        red_destroy_surface(worker, surface_id);
+    }
+}
+
+static void remove_drawable_dependencies(RedWorker *worker, Drawable *drawable)
+{
+    int x;
+    int surface_id;
+
+    for (x = 0; x < 3; ++x) {
+        surface_id = drawable->qxl_drawable->surfaces_dest[x];
+        if (surface_id != -1 && drawable->depend_items[x].drawable) {
+            remove_depended_item(&drawable->depend_items[x]);
+        }
+    }
+}
+
 static inline void release_drawable(RedWorker *worker, Drawable *item)
 {
     if (!--item->refs) {
@@ -1586,6 +1725,9 @@ static inline void release_drawable(RedWorker *worker, Drawable *item)
         ASSERT(!item->tree_item.shadow);
         region_destroy(&item->tree_item.base.rgn);
 
+        remove_drawable_dependencies(worker, item);
+        red_dec_surfaces_drawable_dependencies(worker, item->qxl_drawable);
+
         if (item->red_glz_drawable) {
             item->red_glz_drawable->drawable = NULL;
         } else { // no refernce to the qxl drawable left
@@ -1657,9 +1799,29 @@ static inline void red_add_item_trace(RedWorker *worker, Drawable *item)
 
 #endif
 
+static void surface_flush(RedWorker *worker, int surface_id, SpiceRect *rect)
+{
+    red_update_area(worker, rect, surface_id);
+}
+
+static void red_flush_source_surfaces(RedWorker *worker, Drawable *drawable)
+{
+    int x;
+    int surface_id;
+
+    for (x = 0; x < 3; ++x) {
+        surface_id = drawable->qxl_drawable->surfaces_dest[x];
+        if (surface_id != -1 && drawable->depend_items[x].drawable) {
+            remove_depended_item(&drawable->depend_items[x]);
+            surface_flush(worker, surface_id, &drawable->qxl_drawable->surfaces_rects[x]);
+        }
+    }
+}
+
 static inline void current_remove_drawable(RedWorker *worker, Drawable *item)
 {
     worker->drawable_count--;
+
     if (item->tree_item.effect != QXL_EFFECT_OPAQUE) {
         worker->transparent_count--;
     }
@@ -1673,6 +1835,7 @@ static inline void current_remove_drawable(RedWorker *worker, Drawable *item)
     remove_shadow(worker, &item->tree_item);
     ring_remove(&item->tree_item.base.siblings_link);
     ring_remove(&item->list_link);
+    ring_remove(&item->surface_list_link);
     release_drawable(worker, item);
     worker->current_size--;
 }
@@ -1718,9 +1881,9 @@ static inline void current_remove(RedWorker *worker, TreeItem *item)
     }
 }
 
-static void current_tree_for_each(RedWorker *worker, void (*f)(TreeItem *, void *), void * data)
+static void current_tree_for_each(RedWorker *worker, Ring *ring, void (*f)(TreeItem *, void *),
+                                  void * data)
 {
-    Ring *ring = &worker->current;
     RingItem *ring_item;
     Ring *top_ring;
 
@@ -1756,11 +1919,11 @@ static void current_tree_for_each(RedWorker *worker, void (*f)(TreeItem *, void
     }
 }
 
-static void red_current_clear(RedWorker *worker)
+static void red_current_clear(RedWorker *worker, int surface_id)
 {
     RingItem *ring_item;
 
-    while ((ring_item = ring_get_head(&worker->current))) {
+    while ((ring_item = ring_get_head(&worker->surfaces[surface_id].current))) {
         TreeItem *now = SPICE_CONTAINEROF(ring_item, TreeItem, siblings_link);
         current_remove(worker, now);
     }
@@ -1838,19 +2001,19 @@ void __show_current(TreeItem *item, void *data)
     print_base_item("TREE", item);
 }
 
-static void show_current(RedWorker *worker)
+static void show_current(RedWorker *worker, Ring *ring)
 {
-    if (ring_is_empty(&worker->current)) {
+    if (ring_is_empty(ring)) {
         red_printf("TEST: TREE: EMPTY");
         return;
     }
-    current_tree_for_each(worker, __show_current, NULL);
+    current_tree_for_each(worker, ring, __show_current, NULL);
 }
 
 #else
 #define print_rgn(a, b)
 #define print_draw_private(a, b)
-#define show_current(a)
+#define show_current(a, r)
 #define print_shadow_item(a, b)
 #define print_base_item(a, b)
 #endif
@@ -1870,16 +2033,16 @@ static inline Shadow *__find_shadow(TreeItem *item)
     return ((DrawItem *)item)->shadow;
 }
 
-static inline Ring *ring_of(RedWorker *worker, TreeItem *item)
+static inline Ring *ring_of(RedWorker *worker, Ring *ring, TreeItem *item)
 {
-    return (item->container) ? &item->container->items : &worker->current;
+    return (item->container) ? &item->container->items : ring;
 }
 
 static inline int __contained_by(RedWorker *worker, TreeItem *item, Ring *ring)
 {
     ASSERT(item && ring);
     do {
-        Ring *now = ring_of(worker, item);
+        Ring *now = ring_of(worker, ring, item);
         if (now == ring) {
             return TRUE;
         }
@@ -1888,7 +2051,7 @@ static inline int __contained_by(RedWorker *worker, TreeItem *item, Ring *ring)
     return FALSE;
 }
 
-static inline void __exclude_region(RedWorker *worker, TreeItem *item, QRegion *rgn,
+static inline void __exclude_region(RedWorker *worker, Ring *ring, TreeItem *item, QRegion *rgn,
                                     Ring **top_ring, Drawable *frame_candidate)
 {
     QRegion and_rgn;
@@ -1922,7 +2085,7 @@ static inline void __exclude_region(RedWorker *worker, TreeItem *item, QRegion *
                     region_or(rgn, &and_rgn);
                     // in flat representation of current, shadow is always his owner next
                     if (!__contained_by(worker, (TreeItem*)shadow, *top_ring)) {
-                        *top_ring = ring_of(worker, (TreeItem*)shadow);
+                        *top_ring = ring_of(worker, ring, (TreeItem*)shadow);
                     }
                 }
             } else {
@@ -1942,7 +2105,7 @@ static inline void __exclude_region(RedWorker *worker, TreeItem *item, QRegion *
                 if ((shadow = __find_shadow(item))) {
                     region_or(rgn, &shadow->on_hold);
                     if (!__contained_by(worker, (TreeItem*)shadow, *top_ring)) {
-                        *top_ring = ring_of(worker, (TreeItem*)shadow);
+                        *top_ring = ring_of(worker, ring, (TreeItem*)shadow);
                     }
                 }
             }
@@ -1983,7 +2146,7 @@ static void exclude_region(RedWorker *worker, Ring *ring, RingItem *ring_item, Q
 
         if (region_intersects(rgn, &now->rgn)) {
             print_base_item("EXCLUDE2", now);
-            __exclude_region(worker, now, rgn, &top_ring, frame_candidate);
+            __exclude_region(worker, ring, now, rgn, &top_ring, frame_candidate);
             print_base_item("EXCLUDE3", now);
 
             if (region_is_empty(&now->rgn)) {
@@ -2046,7 +2209,7 @@ static void exclude_region(RedWorker *worker, Ring *ring, RingItem *ring_item, Q
 
         if (region_test(rgn, &now->rgn, REGION_TEST_SHARED)) {
             print_base_item("EXCLUDE2", now);
-            __exclude_region(worker, now, rgn, &top_ring);
+            __exclude_region(worker, ring, now, rgn, &top_ring);
             print_base_item("EXCLUDE3", now);
 
             if (region_is_empty(&now->rgn)) {
@@ -2113,8 +2276,14 @@ static inline int is_opaque_item(TreeItem *item)
 
 static inline void __current_add_drawable(RedWorker *worker, Drawable *drawable, RingItem *pos)
 {
+    RedSurface *surface;
+    uint32_t surface_id = drawable->qxl_drawable->surface_id;
+
+    validate_surface(worker, surface_id);
+    surface = &worker->surfaces[surface_id];
     ring_add_after(&drawable->tree_item.base.siblings_link, pos);
     ring_add(&worker->current_list, &drawable->list_link);
+    ring_add(&surface->current_list, &drawable->surface_list_link);
     drawable->refs++;
 }
 
@@ -3165,14 +3334,13 @@ static void red_reset_stream_trace(RedWorker *worker)
 
 #endif
 
-static inline int red_current_add(RedWorker *worker, Drawable *drawable)
+static inline int red_current_add(RedWorker *worker, Ring *ring, Drawable *drawable)
 {
     DrawItem *item = &drawable->tree_item;
 #ifdef RED_WORKER_STAT
     stat_time_t start_time = stat_now();
 #endif
     RingItem *now;
-    Ring *ring = &worker->current;
     QRegion exclude_rgn;
     RingItem *exclude_base = NULL;
 
@@ -3286,10 +3454,10 @@ static inline int red_current_add(RedWorker *worker, Drawable *drawable)
 
 #else
 
-static inline void __handle_remove_shadow(RedWorker *worker, TreeItem *item)
+static inline void __handle_remove_shadow(RedWorker *worker, TreeItem *item, Ring *ring)
 {
     Shadow *shadow;
-    Ring *ring;
+    Ring *_ring;
 
     while (item->type == TREE_ITEM_TYPE_CONTAINER) {
         if (!(item = (TreeItem *)ring_get_tail(&((Container *)item)->items))) {
@@ -3301,19 +3469,18 @@ static inline void __handle_remove_shadow(RedWorker *worker, TreeItem *item)
         return;
     }
     print_base_item("SHADW", &shadow->base);
-    ring = (shadow->base.container) ? &shadow->base.container->items : &worker->current;
-    exclude_region(worker, ring, ring_next(ring, &shadow->base.siblings_link), &shadow->on_hold);
+    _ring = (shadow->base.container) ? &shadow->base.container->items : ring;
+    exclude_region(worker, _ring, ring_next(_ring, &shadow->base.siblings_link), &shadow->on_hold);
     region_clear(&shadow->on_hold);
 }
 
-static inline int red_current_add(RedWorker *worker, Drawable *drawable)
+static inline int red_current_add(RedWorker *worker, Ring *ring, Drawable *drawable)
 {
     DrawItem *item = &drawable->tree_item;
 #ifdef RED_WORKER_STAT
     stat_time_t start_time = stat_now();
 #endif
     RingItem *now;
-    Ring *ring = &worker->current;
 
     print_base_item("ADD", &item->base);
     ASSERT(!region_is_empty(&item->base.rgn));
@@ -3432,12 +3599,11 @@ static inline Shadow *__new_shadow(RedWorker *worker, Drawable *item, SpicePoint
     return shadow;
 }
 
-static inline int red_current_add_with_shadow(RedWorker *worker, Drawable *item, SpicePoint *delta)
+static inline int red_current_add_with_shadow(RedWorker *worker, Ring *ring, Drawable *item, SpicePoint *delta)
 {
 #ifdef RED_WORKER_STAT
     stat_time_t start_time = stat_now();
 #endif
-    Ring *ring;
 
     Shadow *shadow = __new_shadow(worker, item, delta);
     if (!shadow) {
@@ -3446,7 +3612,6 @@ static inline int red_current_add_with_shadow(RedWorker *worker, Drawable *item,
     }
     print_base_item("ADDSHADOW", &item->tree_item.base);
     worker->current_size++;
-    ring = &worker->current;
     // item and his shadow must initially be placed in the same container.
     // for now putting them on root.
 #ifdef STREAM_TRACE
@@ -3509,7 +3674,7 @@ static inline void red_update_streamable(RedWorker *worker, Drawable *drawable,
 
 #endif
 
-static inline int red_current_add_qxl(RedWorker *worker, Drawable *drawable,
+static inline int red_current_add_qxl(RedWorker *worker, Ring *ring, Drawable *drawable,
                                       QXLDrawable *qxl_drawable)
 {
     int ret;
@@ -3522,12 +3687,12 @@ static inline int red_current_add_qxl(RedWorker *worker, Drawable *drawable,
 #endif
         delta.x = qxl_drawable->u.copy_bits.src_pos.x - qxl_drawable->bbox.left;
         delta.y = qxl_drawable->u.copy_bits.src_pos.y - qxl_drawable->bbox.top;
-        ret = red_current_add_with_shadow(worker, drawable, &delta);
+        ret = red_current_add_with_shadow(worker, ring, drawable, &delta);
     } else {
 #ifdef STREAM_TRACE
         red_update_streamable(worker, drawable, qxl_drawable);
 #endif
-        ret = red_current_add(worker, drawable);
+        ret = red_current_add(worker, ring, drawable);
     }
 #ifdef RED_WORKER_STAT
     if ((++worker->add_count % 100) == 0) {
@@ -3556,14 +3721,18 @@ static inline int red_current_add_qxl(RedWorker *worker, Drawable *drawable,
     return ret;
 }
 
-static void red_get_area(RedWorker *worker, const SpiceRect *area, uint8_t *dest, int dest_stride,
-                         int update)
+static void red_get_area(RedWorker *worker, int surface_id, const SpiceRect *area, uint8_t *dest,
+                         int dest_stride, int update)
 {
-    SpiceCanvas *canvas = worker->surface.context.canvas;
+    SpiceCanvas *canvas;
+    RedSurface *surface;
+
+    surface = &worker->surfaces[surface_id];
     if (update) {
-        red_update_area(worker, area);
+        red_update_area(worker, area, surface_id);
     }
 
+    canvas = surface->context.canvas;
     canvas->ops->read_bits(canvas, dest, dest_stride, area);
 }
 
@@ -3574,11 +3743,18 @@ static inline int red_handle_self_bitmap(RedWorker *worker, Drawable *drawable)
     int32_t height;
     uint8_t *dest;
     int dest_stride;
+    uint32_t surface_id;
+    RedSurface *surface;
 
     if (!drawable->qxl_drawable->self_bitmap) {
         return TRUE;
     }
 
+    surface_id = drawable->qxl_drawable->surface_id;
+    validate_surface(worker, surface_id);
+
+    surface = &worker->surfaces[surface_id];
+
     width = drawable->qxl_drawable->bbox.right - drawable->qxl_drawable->bbox.left;
     height = drawable->qxl_drawable->bbox.bottom - drawable->qxl_drawable->bbox.top;
     dest_stride = width * sizeof(uint32_t);
@@ -3590,7 +3766,7 @@ static inline int red_handle_self_bitmap(RedWorker *worker, Drawable *drawable)
     image->descriptor.flags = 0;
 
     QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_RED, ++worker->bits_unique);
-    image->bitmap.flags = QXL_BITMAP_DIRECT | (worker->surface.context.top_down ?
+    image->bitmap.flags = QXL_BITMAP_DIRECT | (surface->context.top_down ?
                                                QXL_BITMAP_TOP_DOWN : 0);
     image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
     image->bitmap.stride = dest_stride;
@@ -3599,7 +3775,8 @@ static inline int red_handle_self_bitmap(RedWorker *worker, Drawable *drawable)
     image->bitmap.data = (QXLPHYSICAL)dest;
     image->bitmap.palette = 0;
 
-    red_get_area(worker, &drawable->qxl_drawable->self_bitmap_area, dest, dest_stride, TRUE);
+    red_get_area(worker, surface_id,
+                 &drawable->qxl_drawable->self_bitmap_area, dest, dest_stride, TRUE);
 
     drawable->self_bitmap = (uint8_t *)image;
     return TRUE;
@@ -3619,6 +3796,7 @@ static void free_one_drawable(RedWorker *worker, int force_glz_free)
     }
     red_draw_drawable(worker, drawable);
     container = drawable->tree_item.base.container;
+
     current_remove_drawable(worker, drawable);
     container_cleanup(worker, container);
 }
@@ -3639,6 +3817,7 @@ static Drawable *get_drawable(RedWorker *worker, uint8_t effect, QXLDrawable *qx
     drawable->tree_item.base.id = ++worker->last_id;
 #endif
     ring_item_init(&drawable->list_link);
+    ring_item_init(&drawable->surface_list_link);
 #ifdef UPDATE_AREA_BY_TREE
     ring_item_init(&drawable->collect_link);
 #endif
@@ -3653,15 +3832,82 @@ static Drawable *get_drawable(RedWorker *worker, uint8_t effect, QXLDrawable *qx
     return drawable;
 }
 
+static inline int red_handle_depends_on_target_surface(RedWorker *worker, uint32_t surface_id)
+{
+    RedSurface *surface;
+    RingItem *ring_item;
+
+    validate_surface(worker, surface_id);
+    surface = &worker->surfaces[surface_id];
+
+    while ((ring_item = ring_get_tail(&surface->depend_on_me))) {
+        Drawable *drawable;
+        DependItem *depended_item = SPICE_CONTAINEROF(ring_item, DependItem, ring_item);
+        drawable = depended_item->drawable;
+        surface_flush(worker, drawable->qxl_drawable->surface_id, &drawable->qxl_drawable->bbox);
+    }
+
+    return TRUE;
+}
+
+static inline void add_to_surface_dependency(RedWorker *worker, int depend_on_surface_id,
+                                             DependItem *depend_item, Drawable *drawable)
+{
+    RedSurface *surface;
+
+    if (depend_on_surface_id == -1) {
+        depend_item->drawable = NULL;
+        return;
+    }
+
+    validate_surface(worker, depend_on_surface_id);
+    surface = &worker->surfaces[depend_on_surface_id];
+
+    depend_item->drawable = drawable;
+    ring_add(&surface->depend_on_me, &depend_item->ring_item);
+}
+
+static inline int red_handle_surfaces_dependencies(RedWorker *worker, Drawable *drawable)
+{
+    int x;
+
+    for (x = 0; x < 3; ++x) {
+        add_to_surface_dependency(worker, drawable->qxl_drawable->surfaces_dest[x],
+                                  &drawable->depend_items[x], drawable);
+    }
+
+    return TRUE;
+}
+
+static inline void red_inc_surfaces_drawable_dependencies(RedWorker *worker, QXLDrawable *drawable)
+{
+    int x;
+    int surface_id;
+    RedSurface *surface;
+
+    for (x = 0; x < 3; ++x) {
+        surface_id = drawable->surfaces_dest[x];
+        if (surface_id == -1) {
+            continue;
+        }
+        validate_surface(worker, surface_id);
+        surface = &worker->surfaces[surface_id];
+        surface->refs++;
+    }
+}
+
 static inline void red_process_drawable(RedWorker *worker, QXLDrawable *drawable, uint32_t group_id)
 {
+    int surface_id;
     Drawable *item = get_drawable(worker, drawable->effect, drawable, group_id);
 
     ASSERT(item);
-    PANIC_ON(!worker->surface.context.canvas);
-    ASSERT(worker->surface.refs != 0);
 
-    worker->surface.refs++;
+    surface_id = drawable->surface_id;
+    validate_surface(worker, surface_id);
+
+    ASSERT(worker->surfaces[surface_id].refs != 0);
+    worker->surfaces[surface_id].refs++;
 
     region_add(&item->tree_item.base.rgn, &drawable->bbox);
 #ifdef PIPE_DEBUG
@@ -3687,6 +3933,8 @@ static inline void red_process_drawable(RedWorker *worker, QXLDrawable *drawable
 #endif
     }
 
+    red_inc_surfaces_drawable_dependencies(worker, drawable);
+
     if (region_is_empty(&item->tree_item.base.rgn)) {
         release_drawable(worker, item);
         return;
@@ -3697,7 +3945,18 @@ static inline void red_process_drawable(RedWorker *worker, QXLDrawable *drawable
         return;
     }
 
-    if (red_current_add_qxl(worker, item, drawable)) {
+    if (!red_handle_depends_on_target_surface(worker, surface_id)) {
+        release_drawable(worker, item);
+        return;
+    }
+
+    if (!red_handle_surfaces_dependencies(worker, item)) {
+        release_drawable(worker, item);
+        return;
+    }
+
+    if (red_current_add_qxl(worker, &worker->surfaces[surface_id].current, item,
+                            drawable)) {
         worker->drawable_count++;
         if (item->tree_item.effect != QXL_EFFECT_OPAQUE) {
             worker->transparent_count++;
@@ -3710,6 +3969,47 @@ static inline void red_process_drawable(RedWorker *worker, QXLDrawable *drawable
     release_drawable(worker, item);
 }
 
+static inline void red_create_surface(RedWorker *worker, uint32_t surface_id,uint32_t width,
+                                      uint32_t height, int32_t stride, uint8_t depth, void *line_0);
+
+static inline void red_process_surface(RedWorker *worker, QXLSurfaceCmd *surface, uint32_t group_id)
+{
+    int surface_id;
+    RedSurface *red_surface;
+    uint8_t *data;
+
+    surface_id = surface->surface_id;
+    __validate_surface(worker, surface_id);
+
+    red_surface = &worker->surfaces[surface_id];
+
+    switch (surface->type) {
+    case QXL_SURFACE_CMD_CREATE: {
+        unsigned long saved_data = (unsigned long)surface->u.surface_create.data;
+        uint32_t height = surface->u.surface_create.height;
+        int32_t stride = surface->u.surface_create.stride;
+        QXLReleaseInfoExt release_info_ext;
+        
+        data = (uint8_t *)get_virt(worker, saved_data, height * abs(stride), group_id);
+        if (stride < 0) {
+            data -= (int32_t)(stride * (height - 1));
+        }
+        red_create_surface(worker, surface_id, surface->u.surface_create.width,
+                           height, stride, surface->u.surface_create.depth, data);
+        release_info_ext.group_id = group_id;
+        release_info_ext.info = &surface->release_info;
+        worker->qxl->release_resource(worker->qxl, release_info_ext);
+        break;
+    }
+    case QXL_SURFACE_CMD_DESTROY:
+        set_surface_release_info(worker, surface_id, &surface->release_info, group_id);
+        red_destroy_surface(worker, surface_id);
+        break;
+    default:
+            red_error("unknown surface command");
+    };
+}
+
 static void localize_path(RedWorker *worker, QXLPHYSICAL *in_path, uint32_t group_id)
 {
     QXLPath *path;
@@ -3841,6 +4141,26 @@ static LocalImage *alloc_local_image(RedWorker *worker)
     return &worker->local_images[worker->local_images_pos++];
 }
 
+static SpiceCanvas *image_surfaces_get(SpiceImageSurfaces *surfaces,
+                                       uint32_t surface_id)
+{
+    RedWorker *worker;
+
+    worker = SPICE_CONTAINEROF(surfaces, RedWorker, image_surfaces);
+    validate_surface(worker, surface_id);
+
+    return worker->surfaces[surface_id].context.canvas;
+}
+
+static void image_surface_init(RedWorker *worker)
+{
+    static SpiceImageSurfacesOps image_surfaces_ops = {
+        image_surfaces_get,
+    };
+
+    worker->image_surfaces.ops = &image_surfaces_ops;
+}
+
 static ImageCacheItem *image_cache_find(ImageCache *cache, uint64_t id)
 {
     ImageCacheItem *item = cache->hash_table[id % IMAGE_CACHE_HASH_SIZE];
@@ -4061,6 +4381,9 @@ static void localize_bitmap(RedWorker *worker, QXLPHYSICAL *in_bitmap, uint32_t
             local_image->bitmap.palette = (SPICE_ADDRESS)shadow_palette;
         }
         break;
+    case SPICE_IMAGE_TYPE_SURFACE: {
+        break;
+    }
     default:
         red_error("invalid image type");
     }
@@ -4086,6 +4409,7 @@ static void unlocalize_bitmap(QXLPHYSICAL *bitmap)
     case SPICE_IMAGE_TYPE_QUIC:
     case SPICE_IMAGE_TYPE_FROM_CACHE:
         *bitmap = 0;
+    case SPICE_IMAGE_TYPE_SURFACE:
         break;
     default:
         red_error("invalid image type %u", image->descriptor.type);
@@ -4145,8 +4469,16 @@ static void unlocalize_attr(SpiceLineAttr *attr)
 
 static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
 {
+    uint32_t surface_id;
+    RedSurface *surface;
+    SpiceCanvas *canvas; 
     SpiceClip clip = drawable->qxl_drawable->clip;
-    SpiceCanvas *canvas = worker->surface.context.canvas;
+
+    surface_id = drawable->qxl_drawable->surface_id;
+    validate_surface(worker, surface_id);
+
+    surface = &worker->surfaces[surface_id];
+    canvas = surface->context.canvas;
 
     worker->local_images_pos = 0;
     image_cache_eaging(&worker->image_cache);
@@ -4286,24 +4618,30 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
 static void red_draw_drawable(RedWorker *worker, Drawable *drawable)
 {
 #ifdef UPDATE_AREA_BY_TREE
-    SpiceCanvas *canvas = worker->surface.context.canvas;
+    SpiceCanvas *canvas;
+
+    canvas = surface->context.canvas;
     //todo: add need top mask flag
     canvas->ops->group_start(canvas,
                              &drawable->tree_item.base.rgn);
 #endif
+    red_flush_source_surfaces(worker, drawable);
     red_draw_qxl_drawable(worker, drawable);
 #ifdef UPDATE_AREA_BY_TREE
     canvas->ops->group_end(canvas);
 #endif
 }
 
-static void validate_area(RedWorker *worker, const SpiceRect *area)
+static void validate_area(RedWorker *worker, const SpiceRect *area, uint32_t surface_id)
 {
-    if (!worker->surface.context.canvas_draws_on_surface) {
-        SpiceCanvas *canvas = worker->surface.context.canvas;
+    RedSurface *surface;
+
+    surface = &worker->surfaces[surface_id];
+    if (!surface->context.canvas_draws_on_surface) {
+        SpiceCanvas *canvas = surface->context.canvas;
         int h;
-        int stride = worker->surface.context.stride;
-        uint8_t *line_0 = worker->surface.context.line_0;
+        int stride = surface->context.stride;
+        uint8_t *line_0 = surface->context.line_0;
 
         if (!(h = area->bottom - area->top)) {
             return;
@@ -4365,15 +4703,20 @@ static inline void __red_collect_for_update(RedWorker *worker, Ring *ring, RingI
     }
 }
 
-static void red_update_area(RedWorker *worker, const SpiceRect *area)
+static void red_update_area(RedWorker *worker, const SpiceRect *area, int surface_id)
 {
-    Ring *ring = &worker->current;
+    RedSurface *surface;
+    Ring *ring;
     RingItem *ring_item;
     Ring items;
     QRegion rgn;
 
+    validate_surface(worker, surface_id);
+    surface = &worker->surfaces[surface_id];
+    ring = &surface->current;
+
     if (!(ring_item = ring_get_head(ring))) {
-        worker->draw_context.validate_area(worker->draw_context.canvas,
+        worker->draw_context.validate_area(surface->context.canvas,
                                            &worker->dev_info.surface0_area, area);
         return;
     }
@@ -4394,23 +4737,34 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area)
         current_remove_drawable(worker, drawable);
         container_cleanup(worker, container);
     }
-    validate_area(worker, area);
+    validate_area(worker, area, surface_id);
 }
 
 #else
 
-static void red_update_area(RedWorker *worker, const SpiceRect *area)
+static void red_update_area(RedWorker *worker, const SpiceRect *area, int surface_id)
 {
-    Ring *ring = &worker->current_list;
-    RingItem *ring_item = ring;
+    RedSurface *surface;
+    Ring *ring;
+    RingItem *ring_item;
     QRegion rgn;
-    Drawable *last = NULL;
+    Drawable *last;
     Drawable *now;
+    int gn;
+
+    validate_surface(worker, surface_id);
+    surface = &worker->surfaces[surface_id];
+
+start_again:
+    last = NULL;
+    gn = ++surface->current_gn;
+    ring = &surface->current_list;
+    ring_item = ring;
 
     region_init(&rgn);
     region_add(&rgn, area);
     while ((ring_item = ring_next(ring, ring_item))) {
-        now = SPICE_CONTAINEROF(ring_item, Drawable, list_link);
+        now = SPICE_CONTAINEROF(ring_item, Drawable, surface_list_link);
         if (region_intersects(&rgn, &now->tree_item.base.rgn)) {
             last = now;
             break;
@@ -4419,21 +4773,26 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area)
     region_destroy(&rgn);
 
     if (!last) {
-        validate_area(worker, area);
+        validate_area(worker, area, surface_id);
         return;
     }
 
     do {
         Container *container;
 
-        ring_item = ring_get_tail(&worker->current_list);
-        now = SPICE_CONTAINEROF(ring_item, Drawable, list_link);
-        red_draw_drawable(worker, now);
+        ring_item = ring_get_tail(&surface->current_list);
+        now = SPICE_CONTAINEROF(ring_item, Drawable, surface_list_link);
+        now->refs++;
         container = now->tree_item.base.container;
         current_remove_drawable(worker, now);
         container_cleanup(worker, container);
+        red_draw_drawable(worker, now);
+        release_drawable(worker, now);
+        if (gn != surface->current_gn) {
+            goto start_again;
+        }
     } while (now != last);
-    validate_area(worker, area);
+    validate_area(worker, area, surface_id);
 }
 
 #endif
@@ -4626,7 +4985,7 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size)
             QXLUpdateCmd *draw_cmd = (QXLUpdateCmd *)get_virt(worker, ext_cmd.cmd.data,
                                                               sizeof(QXLUpdateCmd),
                                                               ext_cmd.group_id);
-            red_update_area(worker, &draw_cmd->area);
+            red_update_area(worker, &draw_cmd->area, draw_cmd->surface_id);
             worker->qxl->notify_update(worker->qxl, draw_cmd->update_id);
             release_info_ext.group_id = ext_cmd.group_id;
             release_info_ext.info = &draw_cmd->release_info;
@@ -4643,6 +5002,12 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size)
             worker->qxl->release_resource(worker->qxl, release_info_ext);
             break;
         }
+        case QXL_CMD_SURFACE: {
+            QXLSurfaceCmd *surface = (QXLSurfaceCmd *)get_virt(worker, ext_cmd.cmd.data,
+                                                               sizeof(QXLSurfaceCmd), ext_cmd.group_id);
+            red_process_surface(worker, surface, ext_cmd.group_id);
+            break;
+        }
         default:
             red_error("bad command type");
         }
@@ -4678,40 +5043,43 @@ static void red_free_some(RedWorker *worker)
     }
 }
 
-static void red_current_flush(RedWorker *worker)
+static void red_current_flush(RedWorker *worker, int surface_id)
 {
     while (!ring_is_empty(&worker->current_list)) {
         free_one_drawable(worker, FALSE);
     }
-    red_current_clear(worker);
+    red_current_clear(worker, surface_id);
 }
 
-static void red_add_screen_image(RedWorker *worker)
+static void red_add_surface_image(RedWorker *worker, int surface_id)
 {
     ImageItem *item;
     int stride;
     SpiceRect area;
-    SpiceCanvas *canvas = worker->surface.context.canvas;
+    SpiceCanvas *canvas = worker->surfaces[surface_id].context.canvas;
 
-    if (!worker->display_channel || !canvas) {
+    if (!worker->display_channel || !worker->surfaces[surface_id].context.canvas) {
         return;
     }
 
-    stride = worker->surface.context.width << 2;
-    item = (ImageItem *)spice_malloc_n_m(worker->surface.context.height, stride, sizeof(ImageItem));
+    stride = abs(worker->surfaces[surface_id].context.stride);
+
+    item = (ImageItem *)spice_malloc_n_m(worker->surfaces[surface_id].context.height, stride,
+                                         sizeof(ImageItem));
 
     red_pipe_item_init(&item->link, PIPE_ITEM_TYPE_IMAGE);
 
     item->refs = 1;
+    item->surface_id = surface_id;
     item->pos.x = item->pos.y = 0;
-    item->width = worker->surface.context.width;
-    item->height = worker->surface.context.height;
+    item->width = worker->surfaces[surface_id].context.width;
+    item->height = worker->surfaces[surface_id].context.height;
     item->stride = stride;
-    item->top_down = worker->surface.context.top_down;
+    item->top_down = worker->surfaces[surface_id].context.top_down;
 
     area.top = area.left = 0;
-    area.right = worker->surface.context.width;
-    area.bottom = worker->surface.context.height;
+    area.right = worker->surfaces[surface_id].context.width;
+    area.bottom = worker->surfaces[surface_id].context.height;
     canvas->ops->read_bits(canvas, item->data, stride, &area);
     red_pipe_add_image_item(worker, item);
     release_image_item(item);
@@ -6023,6 +6391,23 @@ static void fill_bits(DisplayChannel *display_channel, QXLPHYSICAL *in_bitmap, D
     }
 
     switch (qxl_image->descriptor.type) {
+    case SPICE_IMAGE_TYPE_SURFACE: {
+        int surface_id;
+        RedSurface *surface;
+
+        surface_id = qxl_image->surface_image.surface_id;
+        validate_surface(worker, surface_id);
+
+        surface = &worker->surfaces[surface_id];
+        image->descriptor.type = SPICE_IMAGE_TYPE_SURFACE;
+        image->descriptor.flags = 0;
+        image->descriptor.width = surface->context.width;
+        image->descriptor.height = surface->context.height;
+
+        image->surface.surface_id = surface_id;
+        add_buf(channel, BUF_TYPE_RAW, image, sizeof(SpiceSurfaceImage), 0, 0);
+        break;
+    }
     case SPICE_IMAGE_TYPE_BITMAP:
 #ifdef DUMP_BITMAP
         dump_bitmap(display_channel->base.worker, &qxl_image->bitmap, drawable->group_id);
@@ -6190,7 +6575,7 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_FILL:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_FILL;
         fill_base(display_channel, &display_channel->send_data.u.fill.base, item,
-                  sizeof(SpiceMsgDisplayDrawFill), 0);
+                  sizeof(SpiceMsgDisplayDrawFill), drawable->surface_id);
         display_channel->send_data.u.fill.data = drawable->u.fill;
         fill_brush(display_channel, &display_channel->send_data.u.fill.data.brush, item);
         fill_mask(display_channel, &display_channel->send_data.u.fill.data.mask, item);
@@ -6198,7 +6583,7 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_OPAQUE:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_OPAQUE;
         fill_base(display_channel, &display_channel->send_data.u.opaque.base, item,
-                  sizeof(SpiceMsgDisplayDrawOpaque), 0);
+                  sizeof(SpiceMsgDisplayDrawOpaque), drawable->surface_id);
         display_channel->send_data.u.opaque.data = drawable->u.opaque;
         fill_bits(display_channel, &display_channel->send_data.u.opaque.data.src_bitmap, item);
         fill_brush(display_channel, &display_channel->send_data.u.opaque.data.brush, item);
@@ -6207,7 +6592,7 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_COPY:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_COPY;
         fill_base(display_channel, &display_channel->send_data.u.copy.base, item,
-                  sizeof(SpiceMsgDisplayDrawCopy), 0);
+                  sizeof(SpiceMsgDisplayDrawCopy), drawable->surface_id);
         display_channel->send_data.u.copy.data = drawable->u.copy;
         fill_bits(display_channel, &display_channel->send_data.u.copy.data.src_bitmap, item);
         fill_mask(display_channel, &display_channel->send_data.u.copy.data.mask, item);
@@ -6215,27 +6600,27 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_TRANSPARENT:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_TRANSPARENT;
         fill_base(display_channel, &display_channel->send_data.u.transparent.base, item,
-                  sizeof(SpiceMsgDisplayDrawTransparent), 0);
+                  sizeof(SpiceMsgDisplayDrawTransparent), drawable->surface_id);
         display_channel->send_data.u.transparent.data = drawable->u.transparent;
         fill_bits(display_channel, &display_channel->send_data.u.transparent.data.src_bitmap, item);
         break;
     case QXL_DRAW_ALPHA_BLEND:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND;
         fill_base(display_channel, &display_channel->send_data.u.alpha_blend.base, item,
-                  sizeof(SpiceMsgDisplayDrawAlphaBlend), 0);
+                  sizeof(SpiceMsgDisplayDrawAlphaBlend), drawable->surface_id);
         display_channel->send_data.u.alpha_blend.data = drawable->u.alpha_blend;
         fill_bits(display_channel, &display_channel->send_data.u.alpha_blend.data.src_bitmap, item);
         break;
     case QXL_COPY_BITS:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_COPY_BITS;
         fill_base(display_channel, &display_channel->send_data.u.copy_bits.base, item,
-                  sizeof(SpiceMsgDisplayCopyBits), 0);
+                  sizeof(SpiceMsgDisplayCopyBits), drawable->surface_id);
         display_channel->send_data.u.copy_bits.src_pos = drawable->u.copy_bits.src_pos;
         break;
     case QXL_DRAW_BLEND:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_BLEND;
         fill_base(display_channel, &display_channel->send_data.u.blend.base, item,
-                  sizeof(SpiceMsgDisplayDrawBlend), 0);
+                  sizeof(SpiceMsgDisplayDrawBlend), drawable->surface_id);
         display_channel->send_data.u.blend.data = drawable->u.blend;
         fill_bits(display_channel, &display_channel->send_data.u.blend.data.src_bitmap, item);
         fill_mask(display_channel, &display_channel->send_data.u.blend.data.mask, item);
@@ -6243,28 +6628,28 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_BLACKNESS:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_BLACKNESS;
         fill_base(display_channel, &display_channel->send_data.u.blackness.base, item,
-                  sizeof(SpiceMsgDisplayDrawBlackness), 0);
+                  sizeof(SpiceMsgDisplayDrawBlackness), drawable->surface_id);
         display_channel->send_data.u.blackness.data = drawable->u.blackness;
         fill_mask(display_channel, &display_channel->send_data.u.blackness.data.mask, item);
         break;
     case QXL_DRAW_WHITENESS:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_WHITENESS;
         fill_base(display_channel, &display_channel->send_data.u.whiteness.base, item,
-                  sizeof(SpiceMsgDisplayDrawWhiteness), 0);
+                  sizeof(SpiceMsgDisplayDrawWhiteness), drawable->surface_id);
         display_channel->send_data.u.whiteness.data = drawable->u.whiteness;
         fill_mask(display_channel, &display_channel->send_data.u.whiteness.data.mask, item);
         break;
     case QXL_DRAW_INVERS:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_INVERS;
         fill_base(display_channel, &display_channel->send_data.u.invers.base, item,
-                  sizeof(SpiceMsgDisplayDrawInvers), 0);
+                  sizeof(SpiceMsgDisplayDrawInvers), drawable->surface_id);
         display_channel->send_data.u.invers.data = drawable->u.invers;
         fill_mask(display_channel, &display_channel->send_data.u.invers.data.mask, item);
         break;
     case QXL_DRAW_ROP3:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_ROP3;
         fill_base(display_channel, &display_channel->send_data.u.rop3.base, item,
-                  sizeof(SpiceMsgDisplayDrawRop3), 0);
+                  sizeof(SpiceMsgDisplayDrawRop3), drawable->surface_id);
         display_channel->send_data.u.rop3.data = drawable->u.rop3;
         fill_bits(display_channel, &display_channel->send_data.u.rop3.data.src_bitmap, item);
         fill_brush(display_channel, &display_channel->send_data.u.rop3.data.brush, item);
@@ -6273,7 +6658,7 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_STROKE:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_STROKE;
         fill_base(display_channel, &display_channel->send_data.u.stroke.base, item,
-                  sizeof(SpiceMsgDisplayDrawStroke), 0);
+                  sizeof(SpiceMsgDisplayDrawStroke), drawable->surface_id);
         display_channel->send_data.u.stroke.data = drawable->u.stroke;
         fill_path(display_channel, &display_channel->send_data.u.stroke.data.path, item->group_id);
         fill_attr(display_channel, &display_channel->send_data.u.stroke.data.attr, item->group_id);
@@ -6282,7 +6667,7 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_TEXT:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_TEXT;
         fill_base(display_channel, &display_channel->send_data.u.text.base, item,
-                  sizeof(SpiceMsgDisplayDrawText), 0);
+                  sizeof(SpiceMsgDisplayDrawText), drawable->surface_id);
         display_channel->send_data.u.text.data = drawable->u.text;
         fill_brush(display_channel, &display_channel->send_data.u.text.data.fore_brush, item);
         fill_brush(display_channel, &display_channel->send_data.u.text.data.back_brush, item);
@@ -6476,7 +6861,7 @@ static inline void display_begin_send_massage(DisplayChannel *channel, void *ite
 {
     FreeList *free_list = &channel->send_data.free_list;
 
-    if (free_list->res->count) {
+    if (0 && free_list->res->count) {
         int sync_count = 0;
         int sub_index;
         int i;
@@ -6899,7 +7284,7 @@ static void red_send_image(DisplayChannel *display_channel, ImageItem *item)
     channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_COPY;
 
     add_buf(channel, BUF_TYPE_RAW, &display_channel->send_data.u.copy, sizeof(SpiceMsgDisplayDrawCopy), 0, 0);
-    display_channel->send_data.u.copy.base.surface_id = 0;
+    display_channel->send_data.u.copy.base.surface_id = item->surface_id;
     display_channel->send_data.u.copy.base.box.left = item->pos.x;
     display_channel->send_data.u.copy.base.box.top = item->pos.y;
     display_channel->send_data.u.copy.base.box.right = item->pos.x + bitmap.x;
@@ -7148,8 +7533,6 @@ static void red_send_surface_create(DisplayChannel *display, SpiceMsgSurfaceCrea
     add_buf(channel, BUF_TYPE_RAW, &display->send_data.u.surface_create,
             sizeof(SpiceMsgSurfaceCreate), 0, 0);
 
-    display->surface_client_created = TRUE;
-
     red_begin_send_massage(channel, NULL);
 }
 
@@ -7166,7 +7549,6 @@ static void red_send_surface_destroy(DisplayChannel *display, uint32_t surface_i
     add_buf(channel, BUF_TYPE_RAW, &display->send_data.u.surface_destroy,
             sizeof(SpiceMsgSurfaceDestroy), 0, 0);
 
-    display->surface_client_created = FALSE;
     red_begin_send_massage(channel, NULL);
 }
 
@@ -7392,11 +7774,18 @@ static void __show_tree_call(TreeItem *item, void *data)
 
 void red_show_tree(RedWorker *worker)
 {
+    int x;
+
     ShowTreeData show_tree_data;
     show_tree_data.worker = worker;
     show_tree_data.level = 0;
     show_tree_data.container = NULL;
-    current_tree_for_each(worker, __show_tree_call, &show_tree_data);
+    for (x = 0; x < NUM_SURFACES; ++x) {
+        if (worker->surfaces[x].context.canvas) {
+            current_tree_for_each(worker, &worker->surfaces[x].current, __show_tree_call,
+                                  &show_tree_data);
+        }
+    }
 }
 
 static inline int channel_is_connected(RedChannel *channel)
@@ -7467,7 +7856,8 @@ static SpiceCanvas *create_cairo_context(RedWorker *worker, uint32_t width, uint
     if (surface == NULL) {
         red_error("create cairo surface failed");
     }
-    canvas = canvas_create(surface, depth, &worker->image_cache.base, NULL,
+    canvas = canvas_create(surface, depth, &worker->image_cache.base,
+                           &worker->image_surfaces, NULL,
                            &worker->preload_group_virt_mapping);
     pixman_image_unref (surface);
     return canvas;
@@ -7479,7 +7869,8 @@ static SpiceCanvas *create_ogl_context_common(RedWorker *worker, OGLCtx *ctx, ui
     SpiceCanvas *canvas;
 
     oglctx_make_current(ctx);
-    if (!(canvas = gl_canvas_create(width, height, depth, &worker->image_cache.base, NULL,
+    if (!(canvas = gl_canvas_create(width, height, depth, &worker->image_cache.base,
+                                    &worker->image_surfaces, NULL,
                                     &worker->preload_group_virt_mapping))) {
         return NULL;
     }
@@ -7569,34 +7960,43 @@ static SurfaceCreateItem *get_surface_create_item(uint32_t surface_id, uint32_t
     return create;
 }
 
-static inline void red_create_surface_item(RedWorker *worker, RedSurface *surface)
+static inline void __red_create_surface_item(RedWorker *worker, int surface_id, uint32_t flags)
 {
+    RedSurface *surface;
     SurfaceCreateItem *create;
 
-    if (!surface->context.canvas) {
-        return;
-    }
-
     if (!worker->display_channel) {
         return;
     }
 
-    create = get_surface_create_item(0, surface->context.width, surface->context.height,
-                                     surface->context.depth, SPICE_SURFACE_FLAGS_PRIMARY);
+    surface = &worker->surfaces[surface_id];
+
+    create = get_surface_create_item(surface_id, surface->context.width, surface->context.height,
+                                     surface->context.depth, flags);
+
+    worker->display_channel->surface_client_created[surface_id] = TRUE;
+
     red_pipe_add(&worker->display_channel->base, &create->pipe_item);
 }
 
-static inline void red_create_surface(RedWorker *worker, uint32_t surface_id,uint32_t width,
+static inline void red_create_surface_item(RedWorker *worker, int surface_id)
+{
+    if (is_primary_surface(worker, surface_id)) {
+        __red_create_surface_item(worker, surface_id, SPICE_SURFACE_FLAGS_PRIMARY);
+    } else {
+        __red_create_surface_item(worker, surface_id, 0);
+    } 
+}
+
+static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, uint32_t width,
                                       uint32_t height, int32_t stride, uint8_t depth, void *line_0)
 {
     uint32_t i;
-    RedSurface *surface = &worker->surface;
-
+    RedSurface *surface = &worker->surfaces[surface_id];
     if (stride >= 0) {
         PANIC("Untested path stride >= 0");
     }
     PANIC_ON(surface->context.canvas);
-    ASSERT(surface_id == 0);
 
     surface->context.canvas_draws_on_surface = FALSE;
     surface->context.width = width;
@@ -7604,8 +8004,12 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id,uin
     surface->context.depth = depth;
     surface->context.stride = stride;
     surface->context.line_0 = line_0;
+    memset(line_0 + (int32_t)(stride * (height - 1)), 0, height*abs(stride));
+    surface->release_info = NULL;
+    ring_init(&surface->current);
+    ring_init(&surface->current_list);
+    ring_init(&surface->depend_on_me);
     surface->refs = 1;
-
     if (worker->renderer != RED_RENDERER_INVALID) {
         surface->context.canvas = create_canvas_for_surface(worker, surface, worker->renderer,
                                                             width, height, stride,
@@ -7613,7 +8017,8 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id,uin
         if (!surface->context.canvas) {
             PANIC("drawing canvas creating failed - can`t create same type canvas");
         }
-        red_create_surface_item(worker, surface);
+
+        red_create_surface_item(worker, surface_id);
         return;
     }
 
@@ -7621,9 +8026,9 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id,uin
         surface->context.canvas = create_canvas_for_surface(worker, surface, worker->renderers[i],
                                                             width, height, stride,
                                                             surface->context.depth, line_0);
-        if (surface->context.canvas) {
+        if (surface->context.canvas) { //no need canvas check
             worker->renderer = worker->renderers[i];
-            red_create_surface_item(worker, surface);
+            red_create_surface_item(worker, surface_id);
             return;
         }
     }
@@ -7683,7 +8088,7 @@ static void push_new_primary_surface(RedWorker *worker)
     if ((display_channel = worker->display_channel)) {
         red_pipe_add_type(&display_channel->base, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
         if (!display_channel->base.migrate) {
-            red_create_surface_item(worker, &worker->surface);
+            red_create_surface_item(worker, 0);
         }
         display_channel_push(worker);
     }
@@ -7734,10 +8139,10 @@ static void on_new_display_channel(RedWorker *worker)
         return;
     }
     display_channel->base.messages_window = 0;
-    if (worker->surface.context.canvas) {
-        red_current_flush(worker);
+    if (worker->surfaces[0].context.canvas) {
+        red_current_flush(worker, 0);
         push_new_primary_surface(worker);
-        red_add_screen_image(worker);
+        red_add_surface_image(worker, 0);
         if (channel_is_connected(&display_channel->base)) {
             red_pipe_add_verb(&display_channel->base, SPICE_MSG_DISPLAY_MARK);
             red_disply_start_streams(display_channel);
@@ -8335,7 +8740,7 @@ static void on_new_cursor_channel(RedWorker *worker)
 
     channel->base.messages_window = 0;
     red_pipe_add_type(&channel->base, PIPE_ITEM_TYPE_SET_ACK);
-    if (worker->surface.context.canvas && !channel->base.migrate) {
+    if (worker->surfaces[0].context.canvas && !channel->base.migrate) {
         red_pipe_add_type(&worker->cursor_channel->base, PIPE_ITEM_TYPE_CURSOR_INIT);
     }
 }
@@ -8527,12 +8932,17 @@ static void red_wait_outgoiong_item(RedChannel *channel)
 static inline void handle_dev_update(RedWorker *worker)
 {
     RedWorkeMessage message;
+    const SpiceRect *rect;
+    uint32_t *surface_id;
 
-    ASSERT(worker->surface.context.canvas && worker->running);
+    //ASSERT(worker->surfaces[0].context.canvas && worker->running);
 
     flush_display_commands(worker);
 
-    red_update_area(worker, worker->qxl->get_update_area(worker->qxl));
+    worker->qxl->get_update_area(worker->qxl, &rect, &surface_id);
+    ASSERT(worker->running);
+    validate_surface(worker, *surface_id);
+    red_update_area(worker, rect, *surface_id);
     message = RED_WORKER_MESSAGE_READY;
     write_message(worker->channel, &message);
 }
@@ -8572,12 +8982,14 @@ static inline void handle_dev_del_memslot(RedWorker *worker)
     worker->mem_slots[slot_group_id][slot_id].virt_end_addr = 0;
 }
 
-static inline void destroy_surface_wait(RedWorker *worker)
+static inline void destroy_surface_wait(RedWorker *worker, int surface_id)
 {
-    flush_display_commands(worker);
+    if (worker->surfaces[surface_id].context.canvas) {
+        red_handle_depends_on_target_surface(worker, surface_id);
+    }
     red_flush_surface_pipe(worker);
     red_display_clear_glz_drawables(worker->display_channel);
-    red_current_clear(worker);
+    red_current_clear(worker, surface_id);
     red_wait_outgoiong_item((RedChannel *)worker->display_channel);
     if (worker->display_channel) {
         ASSERT(!worker->display_channel->base.send_data.item);
@@ -8593,8 +9005,9 @@ static inline void handle_dev_destroy_surface_wait(RedWorker *worker)
 
     ASSERT(surface_id == 0);
 
-    if (worker->surface.context.canvas) {
-        destroy_surface_wait(worker);
+    flush_display_commands(worker);
+    if (worker->surfaces[0].context.canvas) {
+        destroy_surface_wait(worker, 0);
     }
 
     message = RED_WORKER_MESSAGE_READY;
@@ -8603,10 +9016,22 @@ static inline void handle_dev_destroy_surface_wait(RedWorker *worker)
 
 static inline void handle_dev_destroy_surfaces(RedWorker *worker)
 {
+    int i;
     RedWorkeMessage message;
 
-    destroy_surface_wait(worker);
-    __red_destroy_surface(worker);
+    flush_display_commands(worker);
+    //to handle better
+    if (worker->surfaces[0].context.canvas) {
+        destroy_surface_wait(worker, 0);
+    }
+    for (i = 0; i < NUM_SURFACES; ++i) {
+        if (worker->surfaces[i].context.canvas) {
+            destroy_surface_wait(worker, i);
+            if (worker->surfaces[i].context.canvas) {
+                __red_destroy_surface(worker, i);
+            }
+        }
+    }
     ASSERT(ring_is_empty(&worker->streams));
 
     red_wait_outgoiong_item((RedChannel *)worker->cursor_channel);
@@ -8626,7 +9051,10 @@ static inline void handle_dev_destroy_surfaces(RedWorker *worker)
         }
     }
 
-    ASSERT(!worker->surface.context.canvas);
+    //to handle better
+    for (i = 0; i < NUM_SURFACES; ++i) {
+        ASSERT(!worker->surfaces[i].context.canvas);
+    }
 
     worker->cursor_visible = TRUE;
     worker->cursor_position.x = worker->cursor_position.y = 0;
@@ -8661,7 +9089,6 @@ static inline void handle_dev_create_primary_surface(RedWorker *worker)
                        line_0);
 
     if (worker->display_channel) {
-        red_create_surface_item(worker, &worker->surface);
         red_pipe_add_verb(&worker->display_channel->base, SPICE_MSG_DISPLAY_MARK);
         display_channel_push(worker);
     }
@@ -8697,12 +9124,12 @@ static inline void handle_dev_destroy_primary_surface(RedWorker *worker)
         ASSERT(!worker->cursor_channel->base.send_data.item);
     }
 
-
-    destroy_surface_wait(worker);
-    red_destroy_surface(worker);
+    flush_display_commands(worker);
+    destroy_surface_wait(worker, 0);
+    red_destroy_surface(worker, 0);
     ASSERT(ring_is_empty(&worker->streams));
 
-    ASSERT(!worker->surface.context.canvas);
+    ASSERT(!worker->surfaces[surface_id].context.canvas);
 
     worker->cursor_visible = TRUE;
     worker->cursor_position.x = worker->cursor_position.y = 0;
@@ -8821,17 +9248,23 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
     case RED_WORKER_MESSAGE_LOAD:
         red_printf("load");
         ASSERT(!worker->running);
-        red_add_screen_image(worker);
+        red_add_surface_image(worker, 0);
         red_cursor_load(worker);
         message = RED_WORKER_MESSAGE_READY;
         write_message(worker->channel, &message);
         break;
-    case RED_WORKER_MESSAGE_STOP: {
+        case RED_WORKER_MESSAGE_STOP: {
+        int x;
+
         red_printf("stop");
         ASSERT(worker->running);
         worker->running = FALSE;
         red_display_clear_glz_drawables(worker->display_channel);
-        red_current_flush(worker);
+        for (x = 0; x < NUM_SURFACES; ++x) {
+            if (worker->surfaces->context.canvas) {
+                red_current_flush(worker, x);
+            }
+        }
         red_cursor_flush(worker);
         red_wait_outgoiong_item((RedChannel *)worker->display_channel);
         red_wait_outgoiong_item((RedChannel *)worker->cursor_channel);
@@ -8973,8 +9406,8 @@ static void red_init(RedWorker *worker, WorkerInitData *init_data)
     worker->image_compression = init_data->image_compression;
     worker->streaming_video = init_data->streaming_video;
     ring_init(&worker->current_list);
-    ring_init(&worker->current);
     image_cache_init(&worker->image_cache);
+    image_surface_init(worker);
     drawables_init(worker);
     cursor_items_init(worker);
     red_init_streams(worker);
@@ -9005,6 +9438,8 @@ static void red_init(RedWorker *worker, WorkerInitData *init_data)
     worker->generation_bits = init_data->memslot_gen_bits;
     worker->mem_slot_bits = init_data->memslot_id_bits;
     worker->internal_groupslot_id = init_data->internal_groupslot_id;
+    PANIC_ON(init_data->n_surfaces > NUM_SURFACES);
+    worker->n_surfaces = init_data->n_surfaces;
     red_create_mem_slots(worker);
 
     worker->preload_group_virt_mapping.ops = &preload_group_virt_mapping_ops;
diff --git a/server/red_worker.h b/server/red_worker.h
index 141caee..54577db 100644
--- a/server/red_worker.h
+++ b/server/red_worker.h
@@ -96,6 +96,7 @@ typedef struct WorkerInitData {
     uint8_t memslot_gen_bits;
     uint8_t memslot_id_bits;
     uint8_t internal_groupslot_id;
+    uint32_t n_surfaces;
 } WorkerInitData;
 
 void *red_worker_main(void *arg);
diff --git a/server/vd_interface.h b/server/vd_interface.h
index 674fae0..6d76b9e 100644
--- a/server/vd_interface.h
+++ b/server/vd_interface.h
@@ -153,6 +153,7 @@ typedef struct QXLDevInitInfo {
     uint8_t memslot_id_bits;
     uint32_t qxl_ram_size;
     uint8_t internal_groupslot_id;
+    uint32_t n_surfaces;
 } QXLDevInitInfo;
 
 struct QXLDevMemSlot {
@@ -178,6 +179,8 @@ struct QXLDevSurfaceCreate {
     uint32_t group_id;
 };
 
+struct SpiceRect;
+
 struct QXLInterface {
     VDInterface base;
 
@@ -196,7 +199,7 @@ struct QXLInterface {
     void (*release_resource)(QXLInterface *qxl, struct QXLReleaseInfoExt release_info);
     int (*get_cursor_command)(QXLInterface *qxl, struct QXLCommandExt *cmd);
     int (*req_cursor_notification)(QXLInterface *qxl);
-    const struct SpiceRect *(*get_update_area)(QXLInterface *qxl);
+    void (*get_update_area)(QXLInterface *qxl, const struct SpiceRect **rect, uint32_t **surface_id);
     void (*notify_update)(QXLInterface *qxl, uint32_t update_id);
     void (*set_save_data)(QXLInterface *qxl, void *data, int size);
     void *(*get_save_data)(QXLInterface *qxl);



commit d850a44051c1d7fe531d27ec525927a365e8a53e
Author: Izik Eidus <ieidus at redhat.com>
Date:   Wed Mar 31 01:45:49 2010 +0300

    qxl device add off screen support
    
    Signed-off-by: Izik Eidus <ieidus at redhat.com>

diff --git a/qemu/hw/qxl.c b/qemu/hw/qxl.c
index 890d521..53b562b 100644
--- a/qemu/hw/qxl.c
+++ b/qemu/hw/qxl.c
@@ -19,7 +19,7 @@
 
 //#define QXL_IO_MEM
 
-#define QXL_VRAM_SIZE 4096
+#define QXL_VRAM_SIZE 1024 * 1024 * 256
 #define QXL_DEFAULT_COMPRESSION_LEVEL 0
 #define QXL_SHARED_VGA_MODE FALSE
 #define QXL_SAVE_VERSION 3
@@ -345,6 +345,7 @@ static void _qxl_get_init_info(PCIQXLDevice *d, QXLDevInitInfo *info)
     info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
     info->internal_groupslot_id = 0;
     info->qxl_ram_size = d->state.rom->num_pages << TARGET_PAGE_BITS;
+    info->n_surfaces = 10000;
 }
 
 static int qxl_get_ring_command(PCIQXLDevice *d, QXLCommandExt *cmd_ext, QXLCommandRing *ring,
@@ -413,9 +414,10 @@ static int _qxl_get_cursor_command(PCIQXLDevice *d, QXLCommandExt *cmd_ext)
     return 1;
 }
 
-static const Rect *_qxl_get_update_area(PCIQXLDevice *d)
+static void _qxl_get_update_area(PCIQXLDevice *d, const Rect **rect, UINT32 **surface_id)
 {
-    return &d->state.ram->update_area;
+    *rect = &d->state.ram->update_area;
+    *surface_id = &d->state.ram->update_surface;
 }
 
 static int _qxl_req_cmd_notification(PCIQXLDevice *d)
@@ -1197,6 +1199,9 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val)
     case QXL_IO_DESTROY_SURFACE_WAIT:
         d->worker->destroy_surface_wait(d->worker, val);
         break;
+    case QXL_IO_DESTROY_ALL_SURFACES:
+        d->worker->destroy_surfaces(d->worker);
+        break;
     default:
         printf("%s: unexpected addr 0x%x val 0x%x\n", __FUNCTION__, addr, val);
     }
@@ -1269,6 +1274,10 @@ static void vram_map(PCIDevice *d, int region_num,
 #else
     cpu_register_physical_memory(addr, size, s->vram_offset | IO_MEM_RAM);
 #endif
+    s->address_ranges[1].virt_start = (unsigned long)s->vram;
+    s->address_ranges[1].virt_end = s->address_ranges[1].virt_start + size;
+    s->address_ranges[1].phys_start = addr;
+    s->address_ranges[1].phys_end = s->address_ranges[1].phys_start + size;
 }
 
 
@@ -1342,6 +1351,8 @@ static uint32_t init_qxl_rom(PCIQXLDevice *d, uint8_t *buf, uint32_t vram_size,
     rom->slots_start = 1;
     rom->slots_end = NUM_MEMSLOTS - 1;
 
+    rom->n_surfaces = 10000;
+
     *max_fb = 0;
     modes->n_modes = sizeof(qxl_modes) / sizeof(QXLMode);
 
@@ -1468,6 +1479,10 @@ static void qxl_vga_update(void)
             }
 
             image = (QXLImage *)(drawable + 1);
+            drawable->surface_id = 0;
+            drawable->surfaces_dest[0] = -1;
+            drawable->surfaces_dest[1] = -1;
+            drawable->surfaces_dest[2] = -1;
             drawable->bbox = *dirty_rect;
             drawable->clip.type = CLIP_TYPE_NONE;
             drawable->clip.data = 0;
@@ -1795,9 +1810,9 @@ int qxl_has_command(QXLDevRef dev_ref)
     return _qxl_has_command((PCIQXLDevice *)dev_ref);
 }
 
-const Rect *qxl_get_update_area(QXLDevRef dev_ref)
+void qxl_get_update_area(QXLDevRef dev_ref, const Rect **rect, UINT32 **surface_id)
 {
-    return _qxl_get_update_area((PCIQXLDevice *)dev_ref);
+    _qxl_get_update_area((PCIQXLDevice *)dev_ref, rect, surface_id);
 }
 
 int qxl_flush_resources(QXLDevRef dev_ref)
@@ -1879,9 +1894,9 @@ static int interface_req_cursor_notification(QXLInterface *qxl)
     return _qxl_req_cursor_notification(((Interface *)qxl)->d);
 }
 
-static const struct Rect *interface_get_update_area(QXLInterface *qxl)
+static void interface_get_update_area(QXLInterface *qxl, const Rect **rect, UINT32 **surface_id)
 {
-    return _qxl_get_update_area(((Interface *)qxl)->d);
+    _qxl_get_update_area(((Interface *)qxl)->d, rect, surface_id);
 }
 
 static void interface_notify_update(QXLInterface *qxl, uint32_t update_id)
@@ -2204,13 +2219,13 @@ void qxl_init(PCIBus *bus, uint8_t *vram, unsigned long vram_offset,
     d->state.vram_offset = vram_offset + rom_size + qxl_ram_size;
     d->state.vram_size = msb_mask(vram_size - (qxl_ram_size + rom_size));
 
-    d->state.address_ranges = (QXLAddressRange *)malloc(sizeof(QXLAddressRange) * (num_ranges + 1));
+    d->state.address_ranges = (QXLAddressRange *)malloc(sizeof(QXLAddressRange) * (num_ranges + 2));
     PANIC_ON(!d->state.address_ranges);
     qxl_reset_device_address_ranges(d);
     for (i = 0; i < num_ranges; ++i) {
-        d->state.address_ranges[i + 1] = address_ranges[i];
+        d->state.address_ranges[i + 2] = address_ranges[i];
     }
-    d->state.num_ranges = num_ranges + 1;
+    d->state.num_ranges = num_ranges + 2;
 
     d->state.group_ids_flip[0] = 0;
     d->state.group_ids_flip[1] = 1;
diff --git a/qemu/hw/qxl_dev.h b/qemu/hw/qxl_dev.h
index cba9e3e..122b8a2 100644
--- a/qemu/hw/qxl_dev.h
+++ b/qemu/hw/qxl_dev.h
@@ -50,6 +50,8 @@ enum {
     QXL_IO_CREATE_PRIMARY,
     QXL_IO_DESTROY_PRIMARY,
     QXL_IO_DESTROY_SURFACE_WAIT,
+    QXL_IO_DESTROY_ALL_SURFACES,
+    QXL_IO_NOTIFY_SURFACES_OOM,
 
     QXL_IO_RANGE_SIZE
 };
@@ -71,6 +73,7 @@ typedef struct ATTR_PACKED QXLRom {
     UINT8 slot_gen_bits;
     UINT8 slot_id_bits;
     UINT8 slot_generation;
+    UINT32 n_surfaces;
 } QXLRom;
 
 typedef struct ATTR_PACKED QXLMode {
@@ -149,6 +152,7 @@ typedef struct ATTR_PACKED QXLRam {
     QXLCursorRing cursor_ring;
     QXLReleaseRing release_ring;
     Rect update_area;
+    UINT32 update_surface;
     QXLMemSlot mem_slot;
     QXLSurfaceCreate create_surface;
     UINT64 flags;
@@ -180,6 +184,7 @@ typedef struct ATTR_PACKED QXLUpdateCmd {
     QXLReleaseInfo release_info;
     Rect area;
     UINT32 update_id;
+    UINT32 surface_id;
 } QXLUpdateCmd;
 
 typedef struct ATTR_PACKED QXLCursor {
@@ -254,6 +259,7 @@ typedef struct ATTR_PACKED QXLCopyBits {
 
 typedef struct ATTR_PACKED QXLDrawable {
     QXLReleaseInfo release_info;
+    uint32_t surface_id;
     UINT8 effect;
     UINT8 type;
     UINT8 self_bitmap;
@@ -261,6 +267,8 @@ typedef struct ATTR_PACKED QXLDrawable {
     Rect bbox;
     Clip clip;
     UINT32 mm_time;
+    INT32 surfaces_dest[3];
+    Rect surfaces_rects[3];
     union {
         Fill fill;
         Opaque opaque;
diff --git a/qemu/hw/qxl_interface.h b/qemu/hw/qxl_interface.h
index 8d37219..1de7e25 100644
--- a/qemu/hw/qxl_interface.h
+++ b/qemu/hw/qxl_interface.h
@@ -95,7 +95,7 @@ int qxl_req_cmd_notification(QXLDevRef dev_ref);
 int qxl_get_cursor_command(QXLDevRef dev_ref, QXLCommandExt *cmd);
 int qxl_req_cursor_notification(QXLDevRef dev_ref);
 int qxl_has_command(QXLDevRef dev_ref);
-const Rect *qxl_get_update_area(QXLDevRef dev_ref);
+void qxl_get_update_area(QXLDevRef dev_ref, const Rect **rect, uint32_t **surface_id);
 int qxl_flush_resources(QXLDevRef dev_ref);
 void qxl_set_save_data(QXLDevRef dev_ref, void *data, int size);
 void *qxl_get_save_data(QXLDevRef dev_ref);

commit 1a2e4c90a21b5115424ad2f1b940c1ee3d3cbbe3
Author: Izik Eidus <ieidus at redhat.com>
Date:   Wed Mar 31 01:47:12 2010 +0300

    qxl driver: add off screen supprot
    
    Signed-off-by: Izik Eidus <ieidus at redhat.com>

diff --git a/display/brush.c b/display/brush.c
index 802c0a9..d69cb76 100644
--- a/display/brush.c
+++ b/display/brush.c
@@ -315,7 +315,8 @@ BOOL APIENTRY DrvRealizeBrush(BRUSHOBJ *brush, SURFOBJ *target, SURFOBJ *pattern
 
 
 static _inline BOOL GetPattern(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *pattern,
-                               InternalBrush *brush)
+                               InternalBrush *brush, INT32 *surface_dest,
+                               SpiceRect *surface_rect)
 {
     HSURF hsurf;
     SURFOBJ *surf_obj;
@@ -342,7 +343,9 @@ static _inline BOOL GetPattern(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *p
     area.right = brush->size.cx;
     area.bottom = brush->size.cy;
 
-    if (!QXLGetBitmap(pdev, drawable, pattern, surf_obj, &area, NULL, &key, TRUE)) {
+    CopyRect(surface_rect, &area);
+
+    if (!QXLGetBitmap(pdev, drawable, pattern, surf_obj, &area, NULL, &key, TRUE, surface_dest)) {
         goto error_2;
     }
 
@@ -361,7 +364,8 @@ error_1:
 
 
 BOOL QXLGetBrush(PDev *pdev, QXLDrawable *drawable, SpiceBrush *qxl_brush,
-                            BRUSHOBJ *brush, POINTL *brush_pos)
+                 BRUSHOBJ *brush, POINTL *brush_pos, INT32 *surface_dest,
+                 SpiceRect *surface_rect)
 {
     DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__));
     ASSERT(pdev, brush);
@@ -377,7 +381,8 @@ BOOL QXLGetBrush(PDev *pdev, QXLDrawable *drawable, SpiceBrush *qxl_brush,
         qxl_brush->type = SPICE_BRUSH_TYPE_PATTERN;
         qxl_brush->u.pattern.pos.x = brush_pos->x;
         qxl_brush->u.pattern.pos.y = brush_pos->y;
-        if (!GetPattern(pdev, drawable, &qxl_brush->u.pattern.pat, brush->pvRbrush)) {
+        if (!GetPattern(pdev, drawable, &qxl_brush->u.pattern.pat, brush->pvRbrush,
+                        surface_dest, surface_rect)) {
             return FALSE;
         }
     } else {
diff --git a/display/dd.c b/display/dd.c
new file mode 100644
index 0000000..a9c0aac
--- /dev/null
+++ b/display/dd.c
@@ -0,0 +1,118 @@
+#include <ddrawi.h>
+#include <ddraw.h>
+#include <dxmini.h>
+#include "os_dep.h"
+#include "devioctl.h"
+#include "ntddvdeo.h"
+#include "ioaccess.h"
+#include "qxldd.h"
+
+static UINT8 get_depth(PDev *pdev)
+{
+    if (pdev->bitmap_format == BMF_32BPP) {
+        return 32;
+    } else {
+        return 16;
+    }
+}
+
+BOOL DrvGetDirectDrawInfo(DHPDEV dhpdev, DD_HALINFO *pHallInfo,
+                          DWORD *pdvNumHeaps, VIDEOMEMORY *pvmList,
+                          DWORD *pdvNumFourCCCodes,
+                          DWORD *pdvFourCC)
+{
+    PDev *pdev;
+    DWORD offset;
+
+    pdev = (PDev *)dhpdev;
+
+    *pdvNumHeaps = 1;
+    *pdvNumFourCCCodes = 0;
+
+    if (!pdev->dd_slot_initialized) {
+        return FALSE;
+    }
+
+    offset = pdev->resolution.cy * pdev->stride;
+
+    if (pvmList) {
+        VIDEOMEMORY *pvmobj = pvmList;
+
+        pvmobj->dwFlags = VIDMEM_ISLINEAR;
+
+        pvmobj->fpStart = (FLATPTR)pdev->fb;
+        pvmobj->fpEnd = pvmobj->fpStart + pdev->fb_size - 1;
+
+        pvmobj->ddsCaps.dwCaps = 0;
+        pvmobj->ddsCapsAlt.dwCaps = 0;
+
+        pdev->pvmList = pvmList;
+    }
+
+    memset(pHallInfo, 0, sizeof(DD_HALINFO));
+
+    pHallInfo->vmiData.pvPrimary =  pdev->fb;
+    pHallInfo->vmiData.fpPrimary = 0;
+
+    pHallInfo->dwSize = sizeof (DD_HALINFO);
+
+    pHallInfo->vmiData.dwFlags = 0;
+    pHallInfo->vmiData.dwDisplayWidth = pdev->resolution.cx;
+    pHallInfo->vmiData.dwDisplayHeight = pdev->resolution.cy;
+    pHallInfo->vmiData.lDisplayPitch = pdev->stride;
+    pHallInfo->vmiData.ddpfDisplay.dwSize = sizeof(DDPIXELFORMAT);
+    pHallInfo->vmiData.ddpfDisplay.dwFlags = DDPF_RGB;
+
+    pHallInfo->vmiData.ddpfDisplay.dwRGBBitCount = get_depth(pdev);
+
+    pHallInfo->vmiData.ddpfDisplay.dwRBitMask = pdev->red_mask;
+    pHallInfo->vmiData.ddpfDisplay.dwGBitMask = pdev->green_mask;
+    pHallInfo->vmiData.ddpfDisplay.dwBBitMask = pdev->blue_mask;
+
+    pHallInfo->vmiData.dwOffscreenAlign = 4;
+    pHallInfo->vmiData.dwOverlayAlign = 4;
+    pHallInfo->vmiData.dwTextureAlign = 4;
+    pHallInfo->vmiData.dwZBufferAlign = 4;
+    pHallInfo->vmiData.dwAlphaAlign = 4;
+
+    pHallInfo->ddCaps.dwSize = sizeof (DDCORECAPS);
+    pHallInfo->ddCaps.dwVidMemTotal = pdev->fb_size;
+    pHallInfo->ddCaps.dwVidMemFree = pdev->fb_size;
+
+    pdev->dd_initialized = TRUE;
+
+    return TRUE;
+}
+
+DWORD CALLBACK QxlCanCreateSurface(PDD_CANCREATESURFACEDATA data)
+{
+    return DDHAL_DRIVER_NOTHANDLED;
+}
+
+DWORD CALLBACK QxlFlip(PDD_FLIPDATA lpFlip)
+{
+    return DDHAL_DRIVER_NOTHANDLED;
+}
+
+BOOL DrvEnableDirectDraw(DHPDEV dhpdev, DD_CALLBACKS *pCallBacks,
+                         DD_SURFACECALLBACKS *pSurfaceCallBacks,
+                         DD_PALETTECALLBACKS *pPaletteCallBacks)
+{
+    memset(pCallBacks, 0, sizeof (DD_CALLBACKS));
+    memset(pSurfaceCallBacks, 0, sizeof (DD_SURFACECALLBACKS));
+    memset(pPaletteCallBacks, 0, sizeof (DD_PALETTECALLBACKS));
+
+    pCallBacks->dwSize = sizeof (DD_CALLBACKS);
+    pCallBacks->CanCreateSurface = QxlCanCreateSurface;
+
+    pSurfaceCallBacks->dwSize = sizeof (DD_SURFACECALLBACKS);
+    pSurfaceCallBacks->Flip = QxlFlip;
+
+    pPaletteCallBacks->dwSize = sizeof (DD_PALETTECALLBACKS);
+
+    return TRUE;
+}
+
+void DrvDisableDirectDraw(DHPDEV dhpdev)
+{
+}
diff --git a/display/dd.h b/display/dd.h
new file mode 100644
index 0000000..ffcd8e1
--- /dev/null
+++ b/display/dd.h
@@ -0,0 +1,15 @@
+#ifndef DD_H
+#define DD_H
+
+BOOL DrvGetDirectDrawInfo(DHPDEV dhpdev, DD_HALINFO *pHallInfo,
+                          DWORD *pdvNumHeaps, VIDEOMEMORY *pvmList,
+                          DWORD *pdvNumFourCCCodes,
+                          DWORD *pdvFourCC);
+
+BOOL DrvEnableDirectDraw(DHPDEV dhpdev, DD_CALLBACKS *pCallBacks,
+                         DD_SURFACECALLBACKS *pSurfaceCallBacks,
+                         DD_PALETTECALLBACKS *pPaletteCallBacks);
+
+void DrvDisableDirectDraw(DHPDEV dhpdev);
+
+#endif
diff --git a/display/driver.c b/display/driver.c
index d3737e3..030917b 100644
--- a/display/driver.c
+++ b/display/driver.c
@@ -35,6 +35,7 @@
 #include "mspace.h"
 #include "res.h"
 #include "surface.h"
+#include "dd.h"
 
 #define DEVICE_NAME L"qxldd"
 
@@ -61,6 +62,12 @@ static DRVFN drv_calls[] = {
     {INDEX_DrvStretchBltROP, (PFN)DrvStretchBltROP},
     {INDEX_DrvTransparentBlt, (PFN)DrvTransparentBlt},
     {INDEX_DrvAlphaBlend, (PFN)DrvAlphaBlend},
+    {INDEX_DrvCreateDeviceBitmap, (PFN)DrvCreateDeviceBitmap},
+    {INDEX_DrvDeleteDeviceBitmap, (PFN)DrvDeleteDeviceBitmap},
+
+    {INDEX_DrvGetDirectDrawInfo, (PFN)DrvGetDirectDrawInfo},
+    {INDEX_DrvEnableDirectDraw, (PFN)DrvEnableDirectDraw},
+    {INDEX_DrvDisableDirectDraw, (PFN)DrvDisableDirectDraw},
 
 #ifdef CALL_TEST
     {INDEX_DrvFillPath, (PFN)DrvFillPath},
@@ -117,7 +124,7 @@ void DebugPrint(PDev *pdev, int level, const char *message, ...)
 {
     va_list ap;
 
-    if (level > (pdev ? (int)*pdev->log_level : DBG_LEVEL)) {
+    if (level > (pdev ? (int)*pdev->log_level : DBG_LEVEL) && level != 211) {
         return;
     }
     va_start(ap, message);
@@ -512,9 +519,14 @@ DHPDEV DrvEnablePDEV(DEVMODEW *dev_mode, PWSTR ignore1, ULONG ignore2, HSURF *ig
         goto err3;
     }
 
+    if (!(pdev->cmd_sem = EngCreateSemaphore())) {
+        DEBUG_PRINT((NULL, 0, "%s: create cmd sem failed\n", __FUNCTION__));
+        goto err4;
+    }
+
     if (!ResInit(pdev)) {
         DEBUG_PRINT((NULL, 0, "%s: init res failed\n", __FUNCTION__));
-        goto err4;
+        goto err5;
     }
 
     RtlCopyMemory(dev_caps, &gdi_info, dev_caps_size);
@@ -523,6 +535,8 @@ DHPDEV DrvEnablePDEV(DEVMODEW *dev_mode, PWSTR ignore1, ULONG ignore2, HSURF *ig
     DEBUG_PRINT((NULL, 1, "%s: 0x%lx\n", __FUNCTION__, pdev));
     return(DHPDEV)pdev;
 
+err5:
+    EngDeleteSemaphore(pdev->cmd_sem);
 err4:
     EngDeleteSemaphore(pdev->print_sem);
 
@@ -543,8 +557,10 @@ VOID DrvDisablePDEV(DHPDEV in_pdev)
     PDev* pdev = (PDev*)in_pdev;
 
     DEBUG_PRINT((NULL, 1, "%s: 0x%lx\n", __FUNCTION__, pdev));
+    EngFreeMem(pdev->surfaces_info);
     ResDestroy(pdev);
     DestroyPalette(pdev);
+    EngDeleteSemaphore(pdev->cmd_sem);
     EngDeleteSemaphore(pdev->malloc_sem);
     EngDeleteSemaphore(pdev->print_sem);
     EngFreeMem(pdev);
@@ -591,6 +607,12 @@ static void DestroyPrimarySurface(PDev *pdev)
     WRITE_PORT_UCHAR(pdev->destroy_primary_port, 0);
 }
 
+static void DestroyAllSurfaces(PDev *pdev)
+{
+    HideMouse(pdev);
+    WRITE_PORT_UCHAR(pdev->destroy_all_surfaces_port, 0);
+}
+
 BOOL SetHardwareMode(PDev *pdev)
 {
     VIDEO_MODE_INFORMATION video_info;
@@ -624,6 +646,49 @@ static VOID UpdateMainSlot(PDev *pdev, MemSlot *slot)
     pdev->va_slot_mask = (~(QXLPHYSICAL)0) >> (pdev->slot_id_bits + pdev->slot_gen_bits);
 }
 
+static void RemoveVRamSlot(PDev *pdev)
+{
+    WRITE_PORT_UCHAR(pdev->memslot_del_port, pdev->dd_mem_slot);
+    pdev->dd_slot_initialized = FALSE;
+}
+
+static BOOLEAN CreateVRamSlot(PDev *pdev)
+{
+    QXLMemSlot *slot;
+    UINT64 high_bits;
+    UINT8 slot_id = pdev->main_mem_slot + 1;
+
+    if (slot_id >= pdev->num_mem_slot) {
+        return FALSE;
+    }
+
+    pdev->va_slot_mask = (~(QXLPHYSICAL)0) >> (pdev->slot_id_bits + pdev->slot_gen_bits);
+
+
+    *pdev->ram_slot_start = pdev->fb_phys;
+    *pdev->ram_slot_end = pdev->fb_phys + pdev->fb_size;
+
+    WRITE_PORT_UCHAR(pdev->memslot_add_port, slot_id);
+
+    pdev->dd_mem_slot = slot_id;
+
+    pdev->mem_slots[slot_id].slot.generation = *pdev->slots_generation;
+    pdev->mem_slots[slot_id].slot.start_phys_addr = pdev->fb_phys;
+    pdev->mem_slots[slot_id].slot.end_phys_addr = pdev->fb_phys + pdev->fb_size;
+    pdev->mem_slots[slot_id].slot.start_virt_addr = (UINT64)pdev->fb;
+    pdev->mem_slots[slot_id].slot.end_virt_addr = (UINT64)pdev->fb + pdev->fb_size;
+
+    high_bits = slot_id << pdev->slot_gen_bits;
+    high_bits |= pdev->mem_slots[slot_id].slot.generation;
+    high_bits <<= (64 - (pdev->slot_gen_bits + pdev->slot_id_bits));
+    pdev->mem_slots[slot_id].high_bits = high_bits;
+
+    pdev->dd_slot_initialized = TRUE;
+
+    return TRUE;
+}
+
+
 BOOL PrepareHardware(PDev *pdev)
 {
     VIDEO_MEMORY video_mem;
@@ -671,6 +736,7 @@ BOOL PrepareHardware(PDev *pdev)
 
     pdev->update_area_port = dev_info.update_area_port;
     pdev->update_area = dev_info.update_area;
+    pdev->update_surface = dev_info.update_surface;
 
     pdev->mm_clock = dev_info.mm_clock;
 
@@ -680,6 +746,14 @@ BOOL PrepareHardware(PDev *pdev)
     pdev->log_buf = dev_info.log_buf;
     pdev->log_level = dev_info.log_level;
 
+    pdev->n_surfaces = dev_info.n_surfaces;
+    if (!(pdev->surfaces_info = (SurfaceInfo *)EngAllocMem(FL_ZERO_MEMORY,
+                                                           sizeof(SurfaceInfo) *
+                                                           pdev->n_surfaces, ALLOC_TAG))) {
+        DEBUG_PRINT((NULL, 0, "%s: surfaces_info alloc failed\n", __FUNCTION__));
+        return FALSE;
+    }
+
     pdev->mem_slots = EngAllocMem(FL_ZERO_MEMORY, sizeof(PMemSlot) * dev_info.num_mem_slot,
                                   ALLOC_TAG);
     if (!pdev->mem_slots) {
@@ -687,6 +761,9 @@ BOOL PrepareHardware(PDev *pdev)
         return FALSE;
     }
 
+    pdev->slots_generation = dev_info.slots_generation;
+    pdev->ram_slot_start = dev_info.ram_slot_start;
+    pdev->ram_slot_end = dev_info.ram_slot_end;
     pdev->slot_id_bits = dev_info.slot_id_bits;
     pdev->slot_gen_bits = dev_info.slot_gen_bits;
     pdev->main_mem_slot = dev_info.main_mem_slot_id;
@@ -706,8 +783,13 @@ BOOL PrepareHardware(PDev *pdev)
                  video_mem_Info.FrameBufferBase, video_mem_Info.FrameBufferLength));
     pdev->fb = (BYTE*)video_mem_Info.FrameBufferBase;
     pdev->fb_size = video_mem_Info.FrameBufferLength;
+    pdev->fb_phys = dev_info.fb_phys;
 
     pdev->destroy_surface_wait_port = dev_info.destroy_surface_wait_port;
+    pdev->destroy_all_surfaces_port = dev_info.destroy_all_surfaces_port;
+    pdev->memslot_add_port = dev_info.memslot_add_port;
+    pdev->memslot_del_port = dev_info.memslot_del_port;
+
     pdev->create_primary_port = dev_info.create_primary_port;
     pdev->destroy_primary_port = dev_info.destroy_primary_port;
 
@@ -718,6 +800,10 @@ BOOL PrepareHardware(PDev *pdev)
 
     pdev->dev_id = dev_info.dev_id;
 
+    pdev->dd_initialized = FALSE;
+
+    CreateVRamSlot(pdev);
+
     DEBUG_PRINT((NULL, 1, "%s: 0x%lx exit: 0x%lx %ul\n", __FUNCTION__, pdev,
                  pdev->fb, pdev->fb_size));
     return TRUE;
@@ -746,7 +832,7 @@ static VOID UnmapFB(PDev *pdev)
     }
 }
 
-VOID EnableQXLSurface(PDev *pdev)
+VOID EnableQXLPrimarySurface(PDev *pdev)
 {
     UINT32 depth;
 
@@ -777,7 +863,6 @@ HSURF DrvEnableSurface(DHPDEV in_pdev)
     DWORD length;
     QXLPHYSICAL phys_mem;
     UINT8 *base_mem;
-    DrawArea drawarea;
 
     DEBUG_PRINT((NULL, 1, "%s: 0x%lx\n", __FUNCTION__, in_pdev));
 
@@ -788,19 +873,12 @@ HSURF DrvEnableSurface(DHPDEV in_pdev)
     InitResources(pdev);
 
     if (!(surf = (HSURF)CreateDeviceBitmap(pdev, pdev->resolution, pdev->bitmap_format, &phys_mem,
-                                           &base_mem, DEVICE_BITMAP_ALLOCATION_TYPE_SURF0))) {
+                                           &base_mem, 0, DEVICE_BITMAP_ALLOCATION_TYPE_SURF0))) {
         DEBUG_PRINT((NULL, 0, "%s: create device surface failed, 0x%lx\n",
                      __FUNCTION__, pdev));
         goto err;
     }
 
-    if (!CreateDrawArea(pdev, &drawarea, base_mem, pdev->resolution.cx, pdev->resolution.cy)) {
-        goto err;
-    }
-
-    pdev->draw_bitmap = drawarea.bitmap;
-    pdev->draw_surf = drawarea.surf_obj;
-
     DEBUG_PRINT((NULL, 1, "%s: EngModifySurface(0x%lx, 0x%lx, 0, MS_NOTSYSTEMMEMORY, "
                  "0x%lx, 0x%lx, %lu, NULL)\n",
                  __FUNCTION__,
@@ -814,7 +892,7 @@ HSURF DrvEnableSurface(DHPDEV in_pdev)
     pdev->surf_phys = phys_mem;
     pdev->surf_base = base_mem;
 
-    EnableQXLSurface(pdev);
+    EnableQXLPrimarySurface(pdev);
 
     DEBUG_PRINT((NULL, 1, "%s: 0x%lx exit\n", __FUNCTION__, pdev));
     return surf;
@@ -825,7 +903,7 @@ err:
     return NULL;
 }
 
-VOID DisableQXLSurface(PDev *pdev)
+VOID DisableQXLPrimarySurface(PDev *pdev)
 {
     DrawArea drawarea;
 
@@ -836,6 +914,11 @@ VOID DisableQXLSurface(PDev *pdev)
     }
 }
 
+VOID DisableQXLAllSurfaces(PDev *pdev)
+{
+    DestroyAllSurfaces(pdev);
+}
+
 VOID DrvDisableSurface(DHPDEV in_pdev)
 {
     PDev *pdev = (PDev*)in_pdev;
@@ -843,12 +926,13 @@ VOID DrvDisableSurface(DHPDEV in_pdev)
 
     DEBUG_PRINT((NULL, 1, "%s: 0x%lx\n", __FUNCTION__, pdev));
 
-    DisableQXLSurface(pdev);
+    DisableQXLPrimarySurface(pdev);
+    //DisableQXLAllSurfaces(pdev);
 
     UnmapFB(pdev);
 
     if (pdev->surf) {
-        DeleteDeviceBitmap(pdev->surf);
+        DeleteDeviceBitmap(pdev, 0, DEVICE_BITMAP_ALLOCATION_TYPE_SURF0);
         pdev->surf = NULL;
     }
 
@@ -860,6 +944,11 @@ VOID DrvDisableSurface(DHPDEV in_pdev)
         pdev->draw_bitmap = NULL;
     }
 
+    if (pdev->surfaces_info) {
+        EngFreeMem(pdev->surfaces_info);
+        pdev->surfaces_info = NULL;
+    }
+
     if (pdev->mem_slots) {
         EngFreeMem(pdev->mem_slots);
         pdev->mem_slots = NULL;
@@ -875,9 +964,11 @@ BOOL DrvAssertMode(DHPDEV in_pdev, BOOL enable)
     DEBUG_PRINT((NULL, 1, "%s: 0x%lx\n", __FUNCTION__, pdev));
     if (enable) {
         InitResources(pdev);
-        EnableQXLSurface(pdev);
+        EnableQXLPrimarySurface(pdev);
+        CreateVRamSlot(pdev);
     } else {
-        DisableQXLSurface(pdev);
+        DisableQXLPrimarySurface(pdev);
+        RemoveVRamSlot(pdev);
     }
     DEBUG_PRINT((NULL, 1, "%s: 0x%lx exit TRUE\n", __FUNCTION__, pdev));
     return TRUE;
@@ -1217,7 +1308,7 @@ BOOL APIENTRY DrvStrokePath(SURFOBJ *surf, PATHOBJ *path, CLIPOBJ *clip, XFORMOB
         }
     }
 
-    if (!(drawable = Drawable(pdev, QXL_DRAW_STROKE, &area, clip))) {
+    if (!(drawable = Drawable(pdev, QXL_DRAW_STROKE, &area, clip, GetSurfaceId(surf)))) {
         return FALSE;
     }
 
@@ -1226,7 +1317,8 @@ BOOL APIENTRY DrvStrokePath(SURFOBJ *surf, PATHOBJ *path, CLIPOBJ *clip, XFORMOB
 
     if (!((fore_rop->flags | back_rop->flags) & ROP3_BRUSH)) {
         drawable->u.stroke.brush.type = SPICE_BRUSH_TYPE_NONE;
-    } else if (!QXLGetBrush(pdev, drawable, &drawable->u.stroke.brush, brush, brush_pos)) {
+    } else if (!QXLGetBrush(pdev, drawable, &drawable->u.stroke.brush, brush, brush_pos,
+                            &drawable->surfaces_dest[0], &drawable->surfaces_rects[0])) {
         goto err;
     }
 
@@ -1266,6 +1358,51 @@ err:
     return FALSE;
 }
 
+HBITMAP APIENTRY DrvCreateDeviceBitmap(DHPDEV dhpdev, SIZEL size, ULONG format)
+{
+    PDev *pdev;
+    UINT8 *base_mem;
+    UINT32 surface_id;
+    QXLPHYSICAL phys_mem;
+    HBITMAP hbitmap;
+
+    pdev = (PDev *)dhpdev;
+
+    if (!pdev->dd_initialized) {
+        return 0;
+    }
+
+    surface_id = GetFreeSurface(pdev);
+    if (!surface_id) {
+        goto out_error;
+    }
+
+    hbitmap = CreateDeviceBitmap(pdev, size, pdev->bitmap_format, &phys_mem, &base_mem, surface_id,
+                                 DEVICE_BITMAP_ALLOCATION_TYPE_VRAM);
+    if (!hbitmap) {
+        goto out_error;
+    }
+
+    return hbitmap;
+
+    // to optimize the failure case
+out_error:
+	return 0; 
+}
+
+VOID APIENTRY DrvDeleteDeviceBitmap(DHSURF dhsurf)
+{
+    UINT32 surface_id;
+    PDev *pdev;
+    SurfaceInfo *surface;
+
+    surface = (SurfaceInfo *)dhsurf;
+    pdev = surface->pdev;
+    surface_id = surface - pdev->surfaces_info;
+
+    DeleteDeviceBitmap(pdev, surface_id, DEVICE_BITMAP_ALLOCATION_TYPE_VRAM);
+}
+
 #ifdef CALL_TEST
 
 void CountCall(PDev *pdev, int counter)
diff --git a/display/qxldd.h b/display/qxldd.h
index 80dd30a..f1623d1 100644
--- a/display/qxldd.h
+++ b/display/qxldd.h
@@ -166,6 +166,8 @@ typedef struct DevRes {
     struct InternalPalette *palette_cache[PALETTE_HASH_SIZE];
     UINT32 num_palettes;
 
+    UINT8 *surfaces_used;
+
 #ifdef DBG
     int num_free_pages;
     int num_outputs;
@@ -187,6 +189,20 @@ typedef struct DevRes {
 #define SSE_MASK 15
 #define SSE_ALIGN 16
 
+ 
+typedef struct DrawArea {
+   HSURF bitmap;
+   SURFOBJ* surf_obj;
+   UINT8 *base_mem;
+} DrawArea;
+
+typedef struct PDev PDev;
+
+typedef struct SurfaceInfo {
+    DrawArea draw_area;
+    PDev *pdev;
+} SurfaceInfo;
+
 typedef struct PDev {
     HANDLE driver;
     HDEV eng;
@@ -197,8 +213,14 @@ typedef struct PDev {
     SIZEL resolution;
     UINT32 max_bitmap_size;
     ULONG bitmap_format;
+
     ULONG fb_size;
     BYTE* fb;
+    UINT64 fb_phys;
+    UINT8 dd_initialized;
+    UINT8 dd_slot_initialized;
+    UINT8 dd_mem_slot;
+
     ULONG stride;
     FLONG red_mask;
     FLONG green_mask;
@@ -229,12 +251,16 @@ typedef struct PDev {
 
     HSEMAPHORE malloc_sem;
     HSEMAPHORE print_sem;
+    HSEMAPHORE cmd_sem;
 
     PMemSlot *mem_slots;
     UINT8 num_mem_slot;
     UINT8 main_mem_slot;
     UINT8 slot_id_bits;
     UINT8 slot_gen_bits;
+    UINT8 *slots_generation;
+    UINT64 *ram_slot_start;
+    UINT64 *ram_slot_end;
     SPICE_ADDRESS va_slot_mask;
 
     UINT32 num_io_pages;
@@ -245,6 +271,7 @@ typedef struct PDev {
 
     UINT32 update_area_port;
     SpiceRect *update_area;
+    UINT32 *update_surface;
 
     UINT32 *mm_clock;
 
@@ -259,6 +286,9 @@ typedef struct PDev {
     UINT32 create_primary_port;
     UINT32 destroy_primary_port;
     UINT32 destroy_surface_wait_port;
+    UINT32 memslot_add_port;
+    UINT32 memslot_del_port;
+    UINT32 destroy_all_surfaces_port;
 
     UINT8* primary_memory_start;
     UINT32 primary_memory_size;
@@ -273,6 +303,11 @@ typedef struct PDev {
     UpdateTrace update_trace_items[NUM_UPDATE_TRACE_ITEMS];
 
     UINT8 FPUSave[16 * 4 + 15];
+
+    UINT32 n_surfaces;
+    SurfaceInfo *surfaces_info;
+
+    VIDEOMEMORY *pvmList;
 } PDev;
 
 
diff --git a/display/res.c b/display/res.c
index 041bf2e..318ea9c 100644
--- a/display/res.c
+++ b/display/res.c
@@ -15,6 +15,9 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#include <ddrawi.h>
+#include <ddraw.h>
+#include <dxmini.h>
 #include "os_dep.h"
 #include "res.h"
 #include "ioaccess.h"
@@ -22,6 +25,8 @@
 #include "mspace.h"
 #include "quic.h"
 #include "murmur_hash2a.h"
+#include "surface.h"
+#include "dd.h"
 
 #if (WINVER < 0x0501)
 #define WAIT_FOR_EVENT(pdev, event, timeout) (pdev)->WaitForEvent(event, timeout)
@@ -67,10 +72,12 @@ static BOOL SetClip(PDev *pdev, CLIPOBJ *clip, QXLDrawable *drawable);
 
 #define PUSH_CMD(pdev) do {                             \
     int notify;                                         \
+    EngAcquireSemaphore(pdev->cmd_sem);                 \
     SPICE_RING_PUSH(pdev->cmd_ring, notify);            \
     if (notify) {                                       \
         WRITE_PORT_UCHAR(pdev->notify_cmd_port, 0);     \
     }                                                   \
+    EngReleaseSemaphore(pdev->cmd_sem);                 \
 } while (0);
 
 #define PUSH_CURSOR_CMD(pdev) do {                      \
@@ -130,6 +137,15 @@ static _inline void DrawableAddRes(PDev *pdev, QXLDrawable *drawable, Resource *
     AddRes(pdev, output, res);
 }
 
+ 
+static _inline void SurfaceAddRes(PDev *pdev, QXLSurfaceCmd *surface, Resource *res)
+{
+    QXLOutput *output;
+
+    output = (QXLOutput *)((UINT8 *)surface - sizeof(QXLOutput));
+    AddRes(pdev, output, res);
+}
+
 static _inline void CursorCmdAddRes(PDev *pdev, QXLCursorCmd *cmd, Resource *res)
 {
     QXLOutput *output;
@@ -311,6 +327,10 @@ void CleanGlobalRes()
                 EngFreeMem(global_res[i].dynamic);
                 global_res[i].dynamic = NULL;
             }
+            if (global_res[i].surfaces_used) {
+                EngFreeMem(global_res[i].surfaces_used);
+                global_res[i].surfaces_used = NULL;
+            }
         }
         EngFreeMem(global_res);
         global_res = NULL;
@@ -347,6 +367,12 @@ static void InitRes(PDev *pdev)
         PANIC(pdev, "Res dynamic allocation failed\n");
     }
 
+    pdev->Res.surfaces_used = EngAllocMem(FL_ZERO_MEMORY, sizeof(UINT8) * pdev->n_surfaces,
+                                          ALLOC_TAG);
+    if (!pdev->Res.surfaces_used) {
+        PANIC(pdev, "Res surfaces_used allocation failed\n");
+    }
+
     pdev->Res.free_outputs = 0;
     InitMspace(&pdev->Res, pdev->io_pages_virt, pdev->num_io_pages * PAGE_SIZE);
     pdev->Res.update_id = *pdev->dev_update_id;
@@ -457,17 +483,21 @@ static QXLDrawable *GetDrawable(PDev *pdev)
     return(QXLDrawable *)output->data;
 }
 
-QXLDrawable *Drawable(PDev *pdev, UINT8 type, RECTL *area, CLIPOBJ *clip)
+QXLDrawable *Drawable(PDev *pdev, UINT8 type, RECTL *area, CLIPOBJ *clip, UINT32 surface_id)
 {
     QXLDrawable *drawable;
 
     ASSERT(pdev, pdev && area);
 
     drawable = GetDrawable(pdev);
+    drawable->surface_id = surface_id;
     drawable->type = type;
     drawable->effect = QXL_EFFECT_BLEND;
     drawable->self_bitmap = 0;
     drawable->mm_time = *pdev->mm_clock;
+    drawable->surfaces_dest[0] = -1;
+    drawable->surfaces_dest[1] = - 1;
+    drawable->surfaces_dest[2] = -1;
     CopyRect(&drawable->bbox, area);
 
     if (!SetClip(pdev, clip, drawable)) {
@@ -489,23 +519,141 @@ void PushDrawable(PDev *pdev, QXLDrawable *drawable)
     PUSH_CMD(pdev);
 }
 
-_inline void GetSurfaceMemory(PDev *pdev, UINT32 x, UINT32 y, UINT32 depth, UINT8 **base_mem,
-                              QXLPHYSICAL *phys_mem, UINT8 allocation_type)
+static QXLSurfaceCmd *GetSurfaceCmd(PDev *pdev)
+{
+    QXLOutput *output;
+
+    output = (QXLOutput *)AllocMem(pdev, sizeof(QXLOutput) + sizeof(QXLSurfaceCmd));
+    output->num_res = 0;
+    ((QXLSurfaceCmd *)output->data)->release_info.id = (UINT64)output;
+    DEBUG_PRINT((pdev, 9, "%s 0x%x\n", __FUNCTION__, output));
+    ONDBG(pdev->Res.num_outputs++); //todo: atomic
+    return(QXLSurfaceCmd *)output->data;
+}
+
+QXLSurfaceCmd *SurfaceCmd(PDev *pdev, UINT8 type, UINT32 surface_id)
+{
+    QXLSurfaceCmd *surface_cmd;
+
+    ASSERT(pdev, pdev && area);
+
+    surface_cmd = GetSurfaceCmd(pdev);
+    surface_cmd->surface_id = surface_id;
+    surface_cmd->type = type;
+    surface_cmd->flags = 0;
+
+    return surface_cmd;
+}
+
+void PushSurfaceCmd(PDev *pdev, QXLSurfaceCmd *surface_cmd)
+{
+    QXLCommand *cmd;
+
+    WaitForCmdRing(pdev);
+    cmd = SPICE_RING_PROD_ITEM(pdev->cmd_ring);
+    cmd->type = QXL_CMD_SURFACE;
+    cmd->data = PA(pdev, surface_cmd, pdev->main_mem_slot);
+    PUSH_CMD(pdev);
+}
+
+
+_inline void GetSurfaceMemory(PDev *pdev, UINT32 x, UINT32 y, UINT32 depth, UINT32 *stride,
+                              UINT8 **base_mem, QXLPHYSICAL *phys_mem, UINT8 allocation_type)
 {
     DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__));
 
-    ASSERT(pdev, allocation_type == DEVICE_BITMAP_ALLOCATION_TYPE_SURF0);
-    ASSERT(pdev, x * y * depth /8 <= pdev->primary_memory_size);
+    switch (allocation_type) {
+    case DEVICE_BITMAP_ALLOCATION_TYPE_SURF0:
+        ASSERT(pdev, x * y * depth /8 <= pdev->primary_memory_size);
+        *base_mem = pdev->primary_memory_start;
+        *phys_mem = PA(pdev, *base_mem, pdev->main_mem_slot);
+        *stride = x * depth / 8;
+        break;
+    case DEVICE_BITMAP_ALLOCATION_TYPE_DEVRAM:
+        *base_mem = AllocMem(pdev, x * y * depth / 8);
+        *phys_mem = PA(pdev, *base_mem, pdev->main_mem_slot);
+        *stride = x * depth / 8;
+        break;
+    case DEVICE_BITMAP_ALLOCATION_TYPE_VRAM: { 
+        SURFACEALIGNMENT surfacealignment;
+
+        memset(&surfacealignment, 0, sizeof(surfacealignment));
+        surfacealignment.Linear.dwStartAlignment = 4;
+        surfacealignment.Linear.dwPitchAlignment = 4;
+        *base_mem = (UINT8 *)HeapVidMemAllocAligned((LPVIDMEM)pdev->pvmList, x * depth / 8, y,
+                                                    &surfacealignment, stride);
+        *phys_mem = PA(pdev, (PVOID)((UINT64)*base_mem), pdev->dd_mem_slot);
+        break;
+    }
+    default:
+        PANIC(pdev, "No allocation type");
+    }
+}
 
-    *base_mem = pdev->primary_memory_start;
-    *phys_mem = PA(pdev, *base_mem, pdev->main_mem_slot);
+void QXLGetSurface(PDev *pdev, QXLPHYSICAL *surface_phys, UINT32 x, UINT32 y, UINT32 depth,
+                   UINT32 *stride, UINT8 **base_mem, UINT8 allocation_type)
+{
+    GetSurfaceMemory(pdev, x, y, depth, stride, base_mem, surface_phys, allocation_type);
 }
 
-BOOL QXLGetSurface(PDev *pdev, QXLPHYSICAL *surface_phys, UINT32 x, UINT32 y, UINT32 depth,
-                   UINT8 **base_mem, UINT8 allocation_type) {
-    ASSERT(pdev, allocation_type == DEVICE_BITMAP_ALLOCATION_TYPE_SURF0);
-    GetSurfaceMemory(pdev, x, y, depth, base_mem, surface_phys, allocation_type);
-    return TRUE;
+void QXLDelSurface(PDev *pdev, UINT8 *base_mem, UINT8 allocation_type)
+{
+    if (allocation_type == DEVICE_BITMAP_ALLOCATION_TYPE_DEVRAM) {
+        FreeMem(pdev, base_mem);
+    }
+}
+
+typedef struct InternalDelSurface {
+    UINT32 surface_id;
+    UINT8 allocation_type;
+} InternalDelSurface;
+
+
+static void FreeDelSurface(PDev *pdev, Resource *res)
+{
+    InternalDelSurface *internal;
+    DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__));
+
+    internal = (InternalDelSurface *)res->res;
+    switch (internal->allocation_type) {
+    case DEVICE_BITMAP_ALLOCATION_TYPE_DEVRAM:
+        FreeMem(pdev, pdev->surfaces_info[internal->surface_id].draw_area.base_mem);
+        break;
+    case DEVICE_BITMAP_ALLOCATION_TYPE_VRAM:
+        VidMemFree(pdev->pvmList->lpHeap,
+                   (FLATPTR)pdev->surfaces_info[internal->surface_id].draw_area.base_mem);
+        break;
+    default:
+        PANIC(pdev, "bad allocation type");
+    }
+    FreeSurface(pdev, internal->surface_id);
+    FreeMem(pdev, res);
+
+    DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__));
+}
+
+#define SURFACEDEL_ALLOC_BASE (sizeof(Resource) + sizeof(InternalDelSurface))
+
+void QXLGetDelSurface(PDev *pdev, QXLSurfaceCmd *surface, UINT32 surface_id, UINT8 allocation_type)
+{
+    Resource *surface_res;
+    InternalDelSurface *internal;
+    size_t alloc_size;
+
+    DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__));
+
+    alloc_size = SURFACEDEL_ALLOC_BASE;
+    surface_res = AllocMem(pdev, alloc_size);
+    
+    surface_res->refs = 1;
+    surface_res->free = FreeDelSurface;
+
+    internal = (InternalDelSurface *)surface_res->res;
+    internal->surface_id = surface_id;
+    internal->allocation_type = allocation_type;
+
+    SurfaceAddRes(pdev, surface, surface_res);
+    RELEASE_RES(pdev, surface_res);
 }
 
 static void FreePath(PDev *pdev, Resource *res)
@@ -1512,6 +1660,15 @@ static _inline void SaveFPU(PDev *pdev)
     }
 }
 
+static void FreeSurfaceImage(PDev *pdev, Resource *res)
+{
+    DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__));
+
+    FreeMem(pdev, res);
+
+    DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__));
+}
+
 #define BITMAP_ALLOC_BASE (sizeof(Resource) + sizeof(InternalImage) + sizeof(QXLDataChunk))
 
 static _inline Resource *GetBitmapImage(PDev *pdev, SURFOBJ *surf, XLATEOBJ *color_trans,
@@ -1789,7 +1946,8 @@ static _inline UINT32 get_image_serial()
 }
 
 BOOL QXLGetBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *image_phys, SURFOBJ *surf,
-                  SpiceRect *area, XLATEOBJ *color_trans, UINT32 *hash_key, BOOL use_cache)
+                  SpiceRect *area, XLATEOBJ *color_trans, UINT32 *hash_key, BOOL use_cache,
+                  INT32 *surface_dest)
 {
     Resource *image_res;
     InternalImage *internal;
@@ -1804,8 +1962,29 @@ BOOL QXLGetBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *image_phys, SU
     ASSERT(pdev, !hash_key || use_cache);
     DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__));
     if (surf->iType != STYPE_BITMAP) {
-        DEBUG_PRINT((pdev, 0, "%s: copy from device doing nothing!!!\n", __FUNCTION__));
-        return FALSE;
+        UINT32 alloc_size;
+
+        DEBUG_PRINT((pdev, 9, "%s: copy from device\n", __FUNCTION__));
+
+        alloc_size = sizeof(Resource) + sizeof(InternalImage);
+        image_res = AllocMem(pdev, alloc_size);
+
+        ONDBG(pdev->num_bits_pages++);
+        image_res->refs = 1;
+        image_res->free = FreeSurfaceImage;
+
+        internal = (InternalImage *)image_res->res;
+
+        internal->image.descriptor.type = SPICE_IMAGE_TYPE_SURFACE;
+        *surface_dest = internal->image.surface_image.surface_id = GetSurfaceId(surf);
+
+        *image_phys = PA(pdev, &internal->image, pdev->main_mem_slot);
+
+        DrawableAddRes(pdev, drawable, image_res);
+
+        RELEASE_RES(pdev, image_res);
+
+        return TRUE;
     }
 
     if (area->left < 0 || area->right > surf->sizlBitmap.cx ||
@@ -1910,7 +2089,7 @@ BOOL QXLGetBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *image_phys, SU
 }
 
 BOOL QXLGetAlphaBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *image_phys,
-                       SURFOBJ *surf, SpiceRect *area)
+                       SURFOBJ *surf, SpiceRect *area, INT32 *surface_dest)
 {
     Resource *image_res;
     InternalImage *internal;
@@ -1922,7 +2101,6 @@ BOOL QXLGetAlphaBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *image_phy
     INT32 height = area->bottom - area->top;
 
     DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__));
-    ASSERT(pdev, surf->iBitmapFormat == BMF_32BPP && surf->iType == STYPE_BITMAP);
     ASSERT(pdev, area->left >= 0 && area->right <= surf->sizlBitmap.cx &&
            area->top >= 0 && area->bottom <= surf->sizlBitmap.cy);
 
@@ -1932,6 +2110,32 @@ BOOL QXLGetAlphaBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *image_phy
                  surf->sizlBitmap.cx, surf->sizlBitmap.cy, surf->hsurf,
                  surf->iBitmapFormat));
 
+    if (surf->iType != STYPE_BITMAP) {
+        UINT32 alloc_size;
+
+        DEBUG_PRINT((pdev, 9, "%s: copy from device\n", __FUNCTION__));
+
+        alloc_size = sizeof(Resource) + sizeof(InternalImage);
+        image_res = AllocMem(pdev, alloc_size);
+
+        ONDBG(pdev->num_bits_pages++);
+        image_res->refs = 1;
+        image_res->free = FreeSurfaceImage;
+
+        internal = (InternalImage *)image_res->res;
+
+        internal->image.descriptor.type = SPICE_IMAGE_TYPE_SURFACE;
+        *surface_dest = internal->image.surface_image.surface_id = GetSurfaceId(surf);
+
+        *image_phys = PA(pdev, &internal->image, pdev->main_mem_slot);
+        DrawableAddRes(pdev, drawable, image_res);
+        RELEASE_RES(pdev, image_res);
+
+        return TRUE;
+    }
+
+    ASSERT(pdev, surf->iBitmapFormat == BMF_32BPP && surf->iType == STYPE_BITMAP);
+
     //todo: use GetChachImage
 
     // NOTE: Same BMF_DONTCACHE issue as in QXLGetBitmap
@@ -2024,7 +2228,7 @@ BOOL QXLGetBitsFromCache(PDev *pdev, QXLDrawable *drawable, UINT32 hash_key, QXL
 }
 
 BOOL QXLGetMask(PDev *pdev, QXLDrawable *drawable, SpiceQMask *qxl_mask, SURFOBJ *mask, POINTL *pos,
-                BOOL invers, LONG width, LONG height)
+                BOOL invers, LONG width, LONG height, INT32 *surface_dest)
 {
     SpiceRect area;
 
@@ -2046,7 +2250,8 @@ BOOL QXLGetMask(PDev *pdev, QXLDrawable *drawable, SpiceQMask *qxl_mask, SURFOBJ
     area.top = pos->y;
     area.bottom = area.top + height;
 
-    if (QXLGetBitmap(pdev, drawable, &qxl_mask->bitmap, mask, &area, NULL, NULL, TRUE)) {
+    if (QXLGetBitmap(pdev, drawable, &qxl_mask->bitmap, mask, &area, NULL, NULL, TRUE,
+                     surface_dest)) {
         qxl_mask->pos.x = area.left;
         qxl_mask->pos.y = area.top;
         return TRUE;
@@ -2082,7 +2287,7 @@ UINT8 *QXLGetBuf(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *buf_phys, UINT3
 }
 
 #ifdef UPDATE_CMD
-void UpdateArea(PDev *pdev, RECTL *area)
+void UpdateArea(PDev *pdev, RECTL *area, UINT32 surface_id)
 {
     QXLCommand *cmd;
     QXLOutput *output;
@@ -2098,6 +2303,7 @@ void UpdateArea(PDev *pdev, RECTL *area)
 
     CopyRect(&updat_cmd->area, area);
     updat_cmd->update_id = ++pdev->Res.update_id;
+    updat_cmd->surface_id = surface_id;
 
     WaitForCmdRing(pdev);
     cmd = SPICE_RING_PROD_ITEM(pdev->cmd_ring);
@@ -2131,10 +2337,11 @@ void UpdateArea(PDev *pdev, RECTL *area)
 
 #else
 
-void UpdateArea(PDev *pdev, RECTL *area)
+void UpdateArea(PDev *pdev, RECTL *area, UINT32 surface_id)
 {
     DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__));
     CopyRect(pdev->update_area, area);
+    *pdev->update_surface = surface_id;
     WRITE_PORT_UCHAR(pdev->update_area_port, 0);
 }
 
diff --git a/display/res.h b/display/res.h
index 5af8a26..90223ae 100644
--- a/display/res.h
+++ b/display/res.h
@@ -22,26 +22,32 @@
 
 UINT64 ReleaseOutput(PDev *pdev, UINT64 output_id);
 
-QXLDrawable *Drawable(PDev *pdev, UINT8 type, RECTL *area, CLIPOBJ *clip);
+QXLDrawable *Drawable(PDev *pdev, UINT8 type, RECTL *area, CLIPOBJ *clip, UINT32 surface_id);
 void PushDrawable(PDev *pdev, QXLDrawable *drawable);
+QXLSurfaceCmd *SurfaceCmd(PDev *pdev, UINT8 type, UINT32 surface_id);
+void PushSurfaceCmd(PDev *pdev, QXLSurfaceCmd *surface_cmd);
 
-BOOL QXLGetSurface(PDev *pdev, QXLPHYSICAL *surface_phys, UINT32 x, UINT32 y, UINT32 depth,
-                    UINT8 **base_mem, UINT8 allocation_type);
+void QXLGetSurface(PDev *pdev, QXLPHYSICAL *surface_phys, UINT32 x, UINT32 y, UINT32 depth,
+                   UINT32 *stride, UINT8 **base_mem, UINT8 allocation_type);
+void QXLGetDelSurface(PDev *pdev, QXLSurfaceCmd *surface, UINT32 surface_id, UINT8 allocation_type);
+void QXLDelSurface(PDev *pdev, UINT8 *base_mem, UINT8 allocation_type);
 BOOL QXLGetPath(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *path_phys, PATHOBJ *path);
 BOOL QXLGetMask(PDev *pdev, QXLDrawable *drawable, SpiceQMask *qxl_mask, SURFOBJ *mask, POINTL *pos,
-                BOOL invers, LONG width, LONG height);
+                BOOL invers, LONG width, LONG height, INT32 *surface_dest);
 BOOL QXLGetBrush(PDev *pdev, QXLDrawable *drawable, SpiceBrush *qxl_brush,
-                            BRUSHOBJ *brush, POINTL *brush_pos);
+                            BRUSHOBJ *brush, POINTL *brush_pos, INT32 *surface_dest,
+                            SpiceRect *surface_rect);
 BOOL QXLGetBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *image_phys, SURFOBJ *surf,
-                  SpiceRect *area, XLATEOBJ *color_trans, UINT32 *hash_key, BOOL use_cache);
+                  SpiceRect *area, XLATEOBJ *color_trans, UINT32 *hash_key, BOOL use_cache,
+                  INT32 *surface_dest);
 BOOL QXLGetBitsFromCache(PDev *pdev, QXLDrawable *drawable, UINT32 hash_key, QXLPHYSICAL *image_phys);
 BOOL QXLGetAlphaBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *image_phys, SURFOBJ *surf,
-                       SpiceRect *area);
+                       SpiceRect *area, INT32 *surface_dest);
 BOOL CheckIfCacheImage(PDev *pdev, SURFOBJ *surf, XLATEOBJ *color_trans);
 UINT8 *QXLGetBuf(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *buf_phys, UINT32 size);
 BOOL QXLGetStr(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *str_phys, FONTOBJ *font, STROBJ *str);
 
-void UpdateArea(PDev *pdev, RECTL *area);
+void UpdateArea(PDev *pdev, RECTL *area, UINT32 surface_id);
 
 QXLCursorCmd *CursorCmd(PDev *pdev);
 void PushCursorCmd(PDev *pdev, QXLCursorCmd *cursor_cmd);
diff --git a/display/rop.c b/display/rop.c
index 83efdd3..362eef4 100644
--- a/display/rop.c
+++ b/display/rop.c
@@ -20,6 +20,7 @@
 #include "utils.h"
 #include "res.h"
 #include "rop.h"
+#include "surface.h"
 
 
 enum ROP3type {
@@ -382,21 +383,28 @@ ROP3Info rops3[] = {
 };
 
 
-static BOOL DoFill(PDev *pdev, RECTL *area, CLIPOBJ *clip, BRUSHOBJ *brush, POINTL *brush_pos,
-                   ROP3Info *rop_info, SURFOBJ *mask, POINTL *mask_pos, BOOL invers_mask)
+static BOOL DoFill(PDev *pdev, UINT32 surface_id, RECTL *area, CLIPOBJ *clip, BRUSHOBJ *brush,
+                   POINTL *brush_pos, ROP3Info *rop_info, SURFOBJ *mask, POINTL *mask_pos,
+                   BOOL invers_mask)
 {
     QXLDrawable *drawable;
+    UINT32 width;
+    UINT32 height;
 
     DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__));
     ASSERT(pdev, pdev && area && brush);
 
-    if (!(drawable = Drawable(pdev, QXL_DRAW_FILL, area, clip))) {
+    if (!(drawable = Drawable(pdev, QXL_DRAW_FILL, area, clip, surface_id))) {
         return FALSE;
     }
 
-    if (!QXLGetBrush(pdev, drawable, &drawable->u.fill.brush, brush, brush_pos) ||
+    width = area->right - area->left;
+    height = area->bottom - area->top;
+
+    if (!QXLGetBrush(pdev, drawable, &drawable->u.fill.brush, brush, brush_pos,
+                     &drawable->surfaces_dest[0], &drawable->surfaces_rects[0]) ||
         !QXLGetMask(pdev, drawable, &drawable->u.fill.mask, mask, mask_pos, invers_mask,
-                    area->right - area->left, area->bottom - area->top)) {
+                     width, height, &drawable->surfaces_dest[1])) {
         ReleaseOutput(pdev, drawable->release_info.id);
         return FALSE;
     }
@@ -405,28 +413,37 @@ static BOOL DoFill(PDev *pdev, RECTL *area, CLIPOBJ *clip, BRUSHOBJ *brush, POIN
 
     drawable->effect = mask ? QXL_EFFECT_BLEND : rop_info->effect;
 
+    if (mask_pos) {
+        CopyRectPoint(&drawable->surfaces_rects[1], mask_pos, width, height);
+    }
+
     PushDrawable(pdev, drawable);
     return TRUE;
 }
 
 static BOOL GetBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *bitmap_phys, SURFOBJ *surf,
-                      SpiceRect *area, XLATEOBJ *color_trans, BOOL use_cache)
+                      SpiceRect *area, XLATEOBJ *color_trans, BOOL use_cache, INT32 *surface_dest)
 {
     DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__));
     if (surf->iType != STYPE_BITMAP) {
+        UINT32 surface_id;
+
         ASSERT(pdev, (PDev *)surf->dhpdev == pdev);
-        DEBUG_PRINT((pdev, 9, "%s copy from self\n", __FUNCTION__));
-        *bitmap_phys = 0;
-        drawable->self_bitmap = TRUE;
-        drawable->self_bitmap_area = *area;
-        area->right = area->right - area->left;
-        area->left = 0;
-        area->bottom = area->bottom - area->top;
-        area->top = 0;
-        return TRUE;
+        surface_id =  GetSurfaceId(surf);
+        if (surface_id == drawable->surface_id) {
+            DEBUG_PRINT((pdev, 9, "%s copy from self\n", __FUNCTION__));
+            *bitmap_phys = 0;
+            drawable->self_bitmap = TRUE;
+            drawable->self_bitmap_area = *area;
+            area->right = area->right - area->left;
+            area->left = 0;
+            area->bottom = area->bottom - area->top;
+            area->top = 0;
+            return TRUE;
+        }
     }
     return QXLGetBitmap(pdev, drawable, &drawable->u.opaque.src_bitmap, surf,
-                        area, color_trans, NULL, use_cache);
+                        area, color_trans, NULL, use_cache, surface_dest);
 }
 
 static _inline UINT8 GdiScaleModeToQxl(ULONG scale_mode)
@@ -435,31 +452,44 @@ static _inline UINT8 GdiScaleModeToQxl(ULONG scale_mode)
                                       SPICE_IMAGE_SCALE_MODE_NEAREST;
 }
 
-static BOOL DoOpaque(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *src, RECTL *src_rect,
-                     XLATEOBJ *color_trans, BRUSHOBJ *brush, POINTL *brush_pos,
+static BOOL DoOpaque(PDev *pdev, UINT32 surface_id, RECTL *area, CLIPOBJ *clip, SURFOBJ *src,
+                     RECTL *src_rect, XLATEOBJ *color_trans, BRUSHOBJ *brush, POINTL *brush_pos,
                      UINT16 rop_decriptor, SURFOBJ *mask, POINTL *mask_pos, BOOL invers_mask,
                      ULONG scale_mode)
 {
     QXLDrawable *drawable;
+    UINT32 width;
+    UINT32 height;
 
     DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__));
     ASSERT(pdev, pdev && area && brush && src_rect && src);
 
-    if (!(drawable = Drawable(pdev, QXL_DRAW_OPAQUE, area, clip))) {
+    if (!(drawable = Drawable(pdev, QXL_DRAW_OPAQUE, area, clip, surface_id))) {
         return FALSE;
     }
 
     drawable->u.opaque.scale_mode = GdiScaleModeToQxl(scale_mode);
     CopyRect(&drawable->u.opaque.src_area, src_rect);
-    if (!QXLGetBrush(pdev, drawable, &drawable->u.opaque.brush, brush, brush_pos) ||
+
+    width = area->right - area->left;
+    height = area->bottom - area->top;
+
+    if (!QXLGetBrush(pdev, drawable, &drawable->u.opaque.brush, brush, brush_pos,
+                     &drawable->surfaces_dest[0], &drawable->surfaces_rects[0]) ||
         !QXLGetMask(pdev, drawable, &drawable->u.opaque.mask, mask, mask_pos, invers_mask,
-                    area->right - area->left, area->bottom - area->top) ||
+                    width, height, &drawable->surfaces_dest[1]) ||
         !GetBitmap(pdev, drawable, &drawable->u.opaque.src_bitmap, src,
-                   &drawable->u.opaque.src_area, color_trans, TRUE)) {
+                   &drawable->u.opaque.src_area, color_trans, TRUE,
+                   &drawable->surfaces_dest[2])) {
         ReleaseOutput(pdev, drawable->release_info.id);
         return FALSE;
     }
 
+    if (mask_pos) {
+        CopyRectPoint(&drawable->surfaces_rects[1], mask_pos, width, height);
+    }
+    CopyRect(&drawable->surfaces_rects[2], src_rect);
+
     drawable->u.opaque.rop_decriptor = rop_decriptor;
     drawable->effect = mask ? QXL_EFFECT_BLEND : QXL_EFFECT_OPAQUE;
     PushDrawable(pdev, drawable);
@@ -574,19 +604,24 @@ static BOOL TestSplitClips(PDev *pdev, RECTL *src_rect, CLIPOBJ *clip, SURFOBJ *
     return FALSE;
 }
 
-static _inline BOOL DoPartialCopy(PDev *pdev, SURFOBJ *src, RECTL *src_rect, RECTL *area_rect,
-                                  RECTL *clip_rect, XLATEOBJ *color_trans, ULONG scale_mode,
-                                  UINT16 rop_decriptor)
+static _inline BOOL DoPartialCopy(PDev *pdev, UINT32 surface_id, SURFOBJ *src, RECTL *src_rect,
+                                  RECTL *area_rect, RECTL *clip_rect, XLATEOBJ *color_trans,
+                                  ULONG scale_mode, UINT16 rop_decriptor)
 {
     QXLDrawable *drawable;
     RECTL clip_area;
+    UINT32 width;
+    UINT32 height;
 
     SectRect(area_rect, clip_rect, &clip_area);
     if (IsEmptyRect(&clip_area)) {
         return TRUE;
     }
 
-    if (!(drawable = Drawable(pdev, QXL_DRAW_COPY, &clip_area, NULL))) {
+    width = clip_area.right - clip_area.left;
+    height = clip_area.bottom - clip_area.top;
+
+    if (!(drawable = Drawable(pdev, QXL_DRAW_COPY, &clip_area, NULL, surface_id))) {
         return FALSE;
     }
 
@@ -603,24 +638,30 @@ static _inline BOOL DoPartialCopy(PDev *pdev, SURFOBJ *src, RECTL *src_rect, REC
                                       clip_area.left;
 
     if(!GetBitmap(pdev, drawable, &drawable->u.copy.src_bitmap, src, &drawable->u.copy.src_area,
-                  color_trans, FALSE)) {
+                  color_trans, FALSE, &drawable->surfaces_dest[0])) {
         ReleaseOutput(pdev, drawable->release_info.id);
         return FALSE;
     }
+    CopyRect(&drawable->surfaces_rects[0], src_rect);
     PushDrawable(pdev, drawable);
     return TRUE;
 }
 
-static BOOL DoCopy(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *src, RECTL *src_rect,
-                   XLATEOBJ *color_trans, UINT16 rop_decriptor, SURFOBJ *mask, POINTL *mask_pos,
-                   BOOL invers_mask, ULONG scale_mode)
+static BOOL DoCopy(PDev *pdev, UINT32 surface_id, RECTL *area, CLIPOBJ *clip, SURFOBJ *src,
+                   RECTL *src_rect, XLATEOBJ *color_trans, UINT16 rop_decriptor, SURFOBJ *mask,
+                   POINTL *mask_pos, BOOL invers_mask, ULONG scale_mode)
 {
     QXLDrawable *drawable;
     BOOL use_cache;
+    UINT32 width;
+    UINT32 height;
 
     ASSERT(pdev, pdev && area && src_rect && src);
     DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__));
 
+    width = area->right - area->left;
+    height = area->bottom - area->top;
+
     if (mask) {
         use_cache = TRUE;
     } else {
@@ -629,10 +670,9 @@ static BOOL DoCopy(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *src, RECTL *
 
     if (use_cache && TestSplitClips(pdev, src_rect, clip, mask) &&
         !CheckIfCacheImage(pdev, src, color_trans)) {
-
         if (clip->iDComplexity == DC_RECT) {
-            if (!DoPartialCopy(pdev, src, src_rect, area, &clip->rclBounds, color_trans, scale_mode,
-                               rop_decriptor)) {
+            if (!DoPartialCopy(pdev, surface_id, src, src_rect, area, &clip->rclBounds, color_trans,
+                               scale_mode, rop_decriptor)) {
                 return FALSE;
             }
         } else {
@@ -649,8 +689,8 @@ static BOOL DoCopy(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *src, RECTL *
                 } buf;
                 more = CLIPOBJ_bEnum(clip, sizeof(buf), (ULONG *)&buf);
                 for(now = buf.rects, end = now + buf.count; now < end; now++) {
-                    if (!DoPartialCopy(pdev, src, src_rect, area, now, color_trans, scale_mode,
-                                       rop_decriptor)) {
+                    if (!DoPartialCopy(pdev, surface_id, src, src_rect, area, now, color_trans,
+                                       scale_mode, rop_decriptor)) {
                         return FALSE;
                     }
                 }
@@ -659,7 +699,7 @@ static BOOL DoCopy(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *src, RECTL *
         return TRUE;
     }
 
-    if (!(drawable = Drawable(pdev, QXL_DRAW_COPY, area, clip))) {
+    if (!(drawable = Drawable(pdev, QXL_DRAW_COPY, area, clip, surface_id))) {
         return FALSE;
     }
 
@@ -672,22 +712,29 @@ static BOOL DoCopy(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *src, RECTL *
     drawable->u.copy.scale_mode = GdiScaleModeToQxl(scale_mode);
     CopyRect(&drawable->u.copy.src_area, src_rect);
     if (!QXLGetMask(pdev, drawable, &drawable->u.copy.mask, mask, mask_pos, invers_mask,
-                    area->right - area->left, area->bottom - area->top) ||
+                    width, height, &drawable->surfaces_dest[0]) ||
         !GetBitmap(pdev, drawable, &drawable->u.copy.src_bitmap, src, &drawable->u.copy.src_area,
-                   color_trans, use_cache)) {
+                   color_trans, use_cache, &drawable->surfaces_dest[1])) {
         ReleaseOutput(pdev, drawable->release_info.id);
         return FALSE;
     }
 
+    if (mask_pos) {
+        CopyRectPoint(&drawable->surfaces_rects[0], mask_pos, width, height);
+    }
+    CopyRect(&drawable->surfaces_rects[1], src_rect);
+
     drawable->u.copy.rop_decriptor = rop_decriptor;
     PushDrawable(pdev, drawable);
     DEBUG_PRINT((pdev, 7, "%s: done\n", __FUNCTION__));
     return TRUE;
 }
 
-static BOOL DoCopyBits(PDev *pdev, CLIPOBJ *clip, RECTL *area, POINTL *src_pos)
+static BOOL DoCopyBits(PDev *pdev, UINT32 surface_id, CLIPOBJ *clip, RECTL *area, POINTL *src_pos)
 {
     QXLDrawable *drawable;
+    UINT32 width;
+    UINT32 height; 
 
     ASSERT(pdev, pdev && area && src_pos);
     DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__));
@@ -697,144 +744,199 @@ static BOOL DoCopyBits(PDev *pdev, CLIPOBJ *clip, RECTL *area, POINTL *src_pos)
         return TRUE;
     }
 
-    if (!(drawable = Drawable(pdev, QXL_COPY_BITS, area, clip))) {
+    width = area->right - area->left;
+    height = area->bottom - area->top;
+
+    if (!(drawable = Drawable(pdev, QXL_COPY_BITS, area, clip, surface_id))) {
         return FALSE;
     }
+
+    drawable->surfaces_dest[0] = surface_id;
+    CopyRectPoint(&drawable->surfaces_rects[0], src_pos, width, height);
+
     CopyPoint(&drawable->u.copy_bits.src_pos, src_pos);
     drawable->effect = QXL_EFFECT_OPAQUE;
     PushDrawable(pdev, drawable);
     return TRUE;
 }
 
-static BOOL DoBlend(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *src, RECTL *src_rect,
-                    XLATEOBJ *color_trans, ROP3Info *rop_info, SURFOBJ *mask, POINTL *mask_pos,
-                    BOOL invers_mask, ULONG scale_mode)
+static BOOL DoBlend(PDev *pdev, UINT32 surface_id, RECTL *area, CLIPOBJ *clip, SURFOBJ *src,
+                    RECTL *src_rect, XLATEOBJ *color_trans, ROP3Info *rop_info, SURFOBJ *mask,
+                    POINTL *mask_pos, BOOL invers_mask, ULONG scale_mode)
 {
     QXLDrawable *drawable;
+    UINT32 width;
+    UINT32 height; 
 
     DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__));
     ASSERT(pdev, pdev && area && src_rect && src);
 
-    if (!(drawable = Drawable(pdev, QXL_DRAW_BLEND, area, clip))) {
+    if (!(drawable = Drawable(pdev, QXL_DRAW_BLEND, area, clip, surface_id))) {
         return FALSE;
     }
 
+    width = area->right - area->left;
+    height = area->bottom - area->top;
+
     drawable->u.blend.scale_mode = GdiScaleModeToQxl(scale_mode);
     CopyRect(&drawable->u.blend.src_area, src_rect);
     if (!QXLGetMask(pdev, drawable, &drawable->u.blend.mask, mask, mask_pos, invers_mask,
-                    area->right - area->left, area->bottom - area->top) ||
+                    width, height, &drawable->surfaces_dest[0]) ||
         !GetBitmap(pdev, drawable, &drawable->u.blend.src_bitmap, src, &drawable->u.blend.src_area,
-                   color_trans, TRUE)) {
+                   color_trans, TRUE, &drawable->surfaces_dest[1])) {
         ReleaseOutput(pdev, drawable->release_info.id);
         return FALSE;
     }
 
+    if (mask_pos) {
+        CopyRectPoint(&drawable->surfaces_rects[0], mask_pos, width, height);
+    }
+    CopyRect(&drawable->surfaces_rects[1], src_rect);
+
     drawable->u.blend.rop_decriptor = rop_info->method_data;
     drawable->effect = mask ? QXL_EFFECT_BLEND : rop_info->effect;
     PushDrawable(pdev, drawable);
     return TRUE;
 }
 
-static BOOL DoBlackness(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *mask, POINTL *mask_pos,
-                        BOOL invers_mask)
+static BOOL DoBlackness(PDev *pdev, UINT32 surface_id, RECTL *area, CLIPOBJ *clip, SURFOBJ *mask,
+                        POINTL *mask_pos, BOOL invers_mask)
 {
     QXLDrawable *drawable;
+    UINT32 width;
+    UINT32 height;
 
     DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__));
     ASSERT(pdev, pdev && area);
 
-    if (!(drawable = Drawable(pdev, QXL_DRAW_BLACKNESS, area, clip))) {
+    if (!(drawable = Drawable(pdev, QXL_DRAW_BLACKNESS, area, clip, surface_id))) {
         return FALSE;
     }
 
+    width = area->right - area->left;
+    height = area->bottom - area->top;
+
     if (!QXLGetMask(pdev, drawable, &drawable->u.blackness.mask, mask, mask_pos, invers_mask,
-                    area->right - area->left, area->bottom - area->top)) {
+                    width, height, &drawable->surfaces_dest[0])) {
         ReleaseOutput(pdev, drawable->release_info.id);
         return FALSE;
     }
 
+ if (mask_pos) {
+        CopyRectPoint(&drawable->surfaces_rects[0], mask_pos, width, height);
+ }
+
     drawable->effect = mask ? QXL_EFFECT_BLEND : QXL_EFFECT_OPAQUE;
     PushDrawable(pdev, drawable);
     return TRUE;
 }
 
-static BOOL DoWhiteness(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *mask, POINTL *mask_pos,
-                        BOOL invers_mask)
+static BOOL DoWhiteness(PDev *pdev, UINT32 surface_id, RECTL *area, CLIPOBJ *clip, SURFOBJ *mask, 
+                        POINTL *mask_pos, BOOL invers_mask)
 {
     QXLDrawable *drawable;
+    UINT32 width;
+    UINT32 height;
 
     DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__));
     ASSERT(pdev, pdev && area);
 
-    if (!(drawable = Drawable(pdev, QXL_DRAW_WHITENESS, area, clip))) {
+    if (!(drawable = Drawable(pdev, QXL_DRAW_WHITENESS, area, clip, surface_id))) {
         return FALSE;
     }
 
+    width = area->right - area->left;
+    height = area->bottom - area->top;
+
     if (!QXLGetMask(pdev, drawable, &drawable->u.whiteness.mask, mask, mask_pos, invers_mask,
-                    area->right - area->left, area->bottom - area->top)) {
+                    width, height, &drawable->surfaces_dest[0])) {
         ReleaseOutput(pdev, drawable->release_info.id);
         return FALSE;
     }
 
+    if (mask_pos) {
+        CopyRectPoint(&drawable->surfaces_rects[0], mask_pos, width, height);
+    }
+
     drawable->effect = mask ? QXL_EFFECT_BLEND : QXL_EFFECT_OPAQUE;
     PushDrawable(pdev, drawable);
     return TRUE;
 }
 
-static BOOL DoInvers(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *mask, POINTL *mask_pos,
-                     BOOL invers_mask)
+static BOOL DoInvers(PDev *pdev, UINT32 surface_id, RECTL *area, CLIPOBJ *clip, SURFOBJ *mask, 
+                     POINTL *mask_pos, BOOL invers_mask)
 {
     QXLDrawable *drawable;
+    UINT32 width;
+    UINT32 height;
 
     DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__));
     ASSERT(pdev, pdev && area);
 
-    if (!(drawable = Drawable(pdev, QXL_DRAW_INVERS, area, clip))) {
+    if (!(drawable = Drawable(pdev, QXL_DRAW_INVERS, area, clip, surface_id))) {
         return FALSE;
     }
 
+    width = area->right - area->left;
+    height = area->bottom - area->top;
+
     if (!QXLGetMask(pdev, drawable, &drawable->u.invers.mask, mask, mask_pos, invers_mask,
-                    area->right - area->left, area->bottom - area->top)) {
+                    width, height, &drawable->surfaces_dest[0])) {
         ReleaseOutput(pdev, drawable->release_info.id);
         return FALSE;
     }
 
+    if (mask_pos) {
+        CopyRectPoint(&drawable->surfaces_rects[0], mask_pos, width, height);
+    }
+
     drawable->effect = mask ? QXL_EFFECT_BLEND : QXL_EFFECT_REVERT_ON_DUP;
     PushDrawable(pdev, drawable);
     return TRUE;
 }
 
-static BOOL DoROP3(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *src, RECTL *src_rect,
-                   XLATEOBJ *color_trans, BRUSHOBJ *brush, POINTL *brush_pos, UINT8 rop3,
-                   SURFOBJ *mask, POINTL *mask_pos, BOOL invers_mask, ULONG scale_mode)
+static BOOL DoROP3(PDev *pdev, UINT32 surface_id, RECTL *area, CLIPOBJ *clip, SURFOBJ *src,
+                   RECTL *src_rect, XLATEOBJ *color_trans, BRUSHOBJ *brush, POINTL *brush_pos,
+                   UINT8 rop3, SURFOBJ *mask, POINTL *mask_pos, BOOL invers_mask, ULONG scale_mode)
 {
     QXLDrawable *drawable;
+    UINT32 width;
+    UINT32 height;
 
     DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__));
     ASSERT(pdev, pdev && area && brush && src_rect && src);
 
-    if (!(drawable = Drawable(pdev, QXL_DRAW_ROP3, area, clip))) {
+    if (!(drawable = Drawable(pdev, QXL_DRAW_ROP3, area, clip, surface_id))) {
         return FALSE;
     }
 
+    width = area->right - area->left;
+    height = area->bottom - area->top;
+
     drawable->u.rop3.scale_mode = GdiScaleModeToQxl(scale_mode);
     CopyRect(&drawable->u.rop3.src_area, src_rect);
-    if (!QXLGetBrush(pdev, drawable, &drawable->u.rop3.brush, brush, brush_pos) ||
+    if (!QXLGetBrush(pdev, drawable, &drawable->u.rop3.brush, brush, brush_pos,
+                     &drawable->surfaces_dest[0], &drawable->surfaces_rects[0]) ||
         !QXLGetMask(pdev, drawable, &drawable->u.rop3.mask, mask, mask_pos, invers_mask,
-                    area->right - area->left, area->bottom - area->top) ||
+                    width, height, &drawable->surfaces_dest[1]) ||
         !GetBitmap(pdev, drawable, &drawable->u.rop3.src_bitmap, src, &drawable->u.rop3.src_area,
-                   color_trans, TRUE)) {
+                   color_trans, TRUE, &drawable->surfaces_dest[2])) {
         ReleaseOutput(pdev, drawable->release_info.id);
         return FALSE;
     }
 
+    if (mask_pos) {
+        CopyRectPoint(&drawable->surfaces_rects[1], mask_pos, width, height);
+    }
+    CopyRect(&drawable->surfaces_rects[2], src_rect);
+
     drawable->u.rop3.rop3 = rop3;
     drawable->effect = mask ? QXL_EFFECT_BLEND : QXL_EFFECT_BLEND; //for now
     PushDrawable(pdev, drawable);
     return TRUE;
 }
 
-static SURFOBJ *Copy16bppArea(PDev *pdev, RECTL *area)
+static SURFOBJ *Copy16bppArea(PDev *pdev, SURFOBJ *src, RECTL *area)
 {
     SIZEL  size;
     HSURF bitmap;
@@ -843,6 +945,9 @@ static SURFOBJ *Copy16bppArea(PDev *pdev, RECTL *area)
     UINT8 *dest_end_line;
     LONG src_stride;
     UINT8 *src_line;
+    SurfaceInfo *surface;
+
+    surface = (SurfaceInfo *)src->dhsurf;
 
     size.cx = area->right - area->left;
     size.cy = area->bottom - area->top;
@@ -864,8 +969,9 @@ static SURFOBJ *Copy16bppArea(PDev *pdev, RECTL *area)
 
     dest_line = surf_obj->pvScan0;
     dest_end_line = dest_line + surf_obj->lDelta * surf_obj->sizlBitmap.cy;
-    src_stride = pdev->draw_surf->lDelta;
-    src_line = (UINT8 *)pdev->draw_surf->pvScan0 + area->top * src_stride + (area->left << 2);
+    src_stride = surface->draw_area.surf_obj->lDelta;
+    src_line = (UINT8 *)surface->draw_area.surf_obj->pvScan0 + area->top * src_stride +
+                        (area->left << 2);
 
     for (; dest_line != dest_end_line; dest_line += surf_obj->lDelta, src_line += src_stride) {
         UINT16 *dest = (UINT16 *)dest_line;
@@ -885,13 +991,16 @@ error:
 
 }
 
-static BOOL BitBltFromDev(PDev *pdev, SURFOBJ *dest, SURFOBJ *mask, CLIPOBJ *clip,
+static BOOL BitBltFromDev(PDev *pdev, SURFOBJ *src, SURFOBJ *dest, SURFOBJ *mask, CLIPOBJ *clip,
                    XLATEOBJ *color_trans, RECTL *dest_rect, POINTL src_pos,
                    POINTL *mask_pos, BRUSHOBJ *brush, POINTL *brush_pos, ROP4 rop4)
 {
     RECTL area;
     SURFOBJ* surf_obj;
     BOOL ret;
+    UINT32 surface_id;
+
+    surface_id = GetSurfaceId(src);
 
     DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__));
 
@@ -900,17 +1009,20 @@ static BOOL BitBltFromDev(PDev *pdev, SURFOBJ *dest, SURFOBJ *mask, CLIPOBJ *cli
     area.left = MAX(0, src_pos.x);
     area.right = MIN(src_pos.x + dest_rect->right - dest_rect->left, pdev->resolution.cx);
 
-    UpdateArea(pdev, &area);
+    UpdateArea(pdev, &area, surface_id);
 
     if (pdev->bitmap_format == BMF_16BPP) {
-        surf_obj = Copy16bppArea(pdev, &area);
+        surf_obj = Copy16bppArea(pdev, src, &area);
         if (!surf_obj) {
             return FALSE;
         }
         src_pos.y = src_pos.y - area.top;
         src_pos.x = src_pos.x - area.left;
     } else {
-        surf_obj = pdev->draw_surf;
+        SurfaceInfo *surface;
+
+        surface = (SurfaceInfo *)src->dhsurf;
+        surf_obj = surface->draw_area.surf_obj;
     }
 
     if (rop4 == 0xcccc) {
@@ -930,9 +1042,10 @@ static BOOL BitBltFromDev(PDev *pdev, SURFOBJ *dest, SURFOBJ *mask, CLIPOBJ *cli
     return ret;
 }
 
-BOOL _inline __DrvBitBlt(PDev *pdev, RECTL *dest_rect, CLIPOBJ *clip, SURFOBJ *src, RECTL *src_rect,
-                         XLATEOBJ *color_trans, BRUSHOBJ *brush, POINTL *brush_pos, ULONG rop3,
-                         SURFOBJ *mask, POINTL *mask_pos, BOOL invers_mask, ULONG scale_mode)
+BOOL _inline __DrvBitBlt(PDev *pdev, UINT32 surface_id, RECTL *dest_rect, CLIPOBJ *clip,
+                        SURFOBJ  *src, RECTL *src_rect, XLATEOBJ *color_trans, BRUSHOBJ *brush, 
+                        POINTL *brush_pos, ULONG rop3, SURFOBJ *mask, POINTL *mask_pos,
+                        BOOL invers_mask, ULONG scale_mode)
 {
     ROP3Info *rop_info = &rops3[rop3];
 
@@ -940,26 +1053,27 @@ BOOL _inline __DrvBitBlt(PDev *pdev, RECTL *dest_rect, CLIPOBJ *clip, SURFOBJ *s
 
     switch (rop_info->method_type) {
     case ROP3_TYPE_FILL:
-        return DoFill(pdev, dest_rect, clip, brush, brush_pos, rop_info, mask, mask_pos,
+        return DoFill(pdev, surface_id, dest_rect, clip, brush, brush_pos, rop_info, mask, mask_pos,
                       invers_mask);
     case ROP3_TYPE_OPAQUE:
-        return DoOpaque(pdev, dest_rect, clip, src, src_rect, color_trans, brush, brush_pos,
-                            rop_info->method_data, mask, mask_pos, invers_mask, scale_mode);
+        return DoOpaque(pdev, surface_id, dest_rect, clip, src, src_rect, color_trans, brush,
+                        brush_pos, rop_info->method_data, mask, mask_pos, invers_mask, scale_mode);
     case ROP3_TYPE_COPY:
-        return DoCopy(pdev, dest_rect, clip, src, src_rect, color_trans, rop_info->method_data,
-                      mask, mask_pos, invers_mask, scale_mode);
+        return DoCopy(pdev, surface_id, dest_rect, clip, src, src_rect, color_trans,
+                      rop_info->method_data, mask, mask_pos, invers_mask, scale_mode);
     case ROP3_TYPE_BLEND:
-        return DoBlend(pdev, dest_rect, clip, src, src_rect, color_trans, rop_info, mask, mask_pos,
-                       invers_mask, scale_mode);
+        return DoBlend(pdev, surface_id, dest_rect, clip, src, src_rect, color_trans, rop_info,
+                       mask, mask_pos, invers_mask, scale_mode);
     case ROP3_TYPE_BLACKNESS:
-        return DoBlackness(pdev, dest_rect, clip, mask, mask_pos, invers_mask);
+        return DoBlackness(pdev, surface_id, dest_rect, clip, mask, mask_pos, invers_mask);
     case ROP3_TYPE_WHITENESS:
-        return DoWhiteness(pdev, dest_rect, clip, mask, mask_pos, invers_mask);
+        return DoWhiteness(pdev, surface_id, dest_rect, clip, mask, mask_pos, invers_mask);
     case ROP3_TYPE_INVERS:
-        return DoInvers(pdev, dest_rect, clip, mask, mask_pos, invers_mask);
+        return DoInvers(pdev, surface_id, dest_rect, clip, mask, mask_pos, invers_mask);
     case ROP3_TYPE_ROP3:
-        return DoROP3(pdev, dest_rect, clip, src, src_rect, color_trans, brush, brush_pos,
-                      (UINT8)rop_info->method_data, mask, mask_pos, invers_mask, scale_mode);
+        return DoROP3(pdev, surface_id, dest_rect, clip, src, src_rect, color_trans, brush,
+                      brush_pos, (UINT8)rop_info->method_data, mask, mask_pos, invers_mask,
+                      scale_mode);
     case ROP3_TYPE_NOP:
         return TRUE;
     default:
@@ -1055,14 +1169,20 @@ static QXLRESULT BitBltCommon(PDev *pdev, SURFOBJ *dest, SURFOBJ *src, SURFOBJ *
     SURFOBJ *brush_mask = NULL;
 #endif
     QXLRESULT res;
+    UINT32 surface_id;
+
+    ASSERT(pdev, dest->iType != STYPE_BITMAP);
+
+    surface_id = GetSurfaceId(dest);
 
     if (!PrepareBrush(brush)) {
         return QXL_FAILED;
     }
 
     if ((rop3 = rop4 & 0xff) == (second_rop3 = ((rop4 >> 8) & 0xff))) {
-        return __DrvBitBlt(pdev, dest_rect, clip, src, src_rect, color_trans, brush, brush_pos,
-                           rop3, NULL, NULL, FALSE, scale_mode) ? QXL_SUCCESS : QXL_FAILED;
+        return __DrvBitBlt(pdev, surface_id, dest_rect, clip, src, src_rect, color_trans, brush,
+                           brush_pos, rop3, NULL, NULL, FALSE, scale_mode) ? QXL_SUCCESS :
+                           QXL_FAILED;
     }
 
     if (!mask) {
@@ -1080,15 +1200,17 @@ static QXLRESULT BitBltCommon(PDev *pdev, SURFOBJ *dest, SURFOBJ *src, SURFOBJ *
     }
     DEBUG_PRINT((pdev, 5, "%s: mask, rop4 is 0x%x\n", __FUNCTION__, rop4));
     ASSERT(pdev, mask_pos);
-    res = (__DrvBitBlt(pdev, dest_rect, clip, src, src_rect, color_trans, brush, brush_pos, rop3,
-                      mask, mask_pos, FALSE, scale_mode) &&
-          __DrvBitBlt(pdev, dest_rect, clip, src, src_rect, color_trans, brush, brush_pos,
-                      second_rop3, mask, mask_pos, TRUE, scale_mode)) ? QXL_SUCCESS : QXL_FAILED;
+    res = (__DrvBitBlt(pdev, surface_id, dest_rect, clip, src, src_rect, color_trans, brush,
+                       brush_pos, rop3, mask, mask_pos, FALSE, scale_mode) &&
+          __DrvBitBlt(pdev, surface_id, dest_rect, clip, src, src_rect, color_trans, brush,
+                      brush_pos, second_rop3, mask, mask_pos, TRUE, scale_mode)) ? QXL_SUCCESS :
+                      QXL_FAILED;
 #ifdef SUPPORT_BRUSH_AS_MASK
     if (brush_mask) {
         //free brush_mask;
     }
 #endif
+
     return res;
 }
 
@@ -1151,8 +1273,6 @@ static QXLRESULT _BitBlt(PDev *pdev, SURFOBJ *dest, SURFOBJ *src, SURFOBJ *mask,
     }
 #endif
 
-    ASSERT(pdev, dest->iType == STYPE_BITMAP || dest->hsurf == pdev->surf);
-    ASSERT(pdev, !src || src->iType == STYPE_BITMAP || src->hsurf == pdev->surf);
     ASSERT(pdev, dest_rect && dest_rect->left < dest_rect->right &&
            dest_rect->top < dest_rect->bottom);
 
@@ -1169,12 +1289,14 @@ static QXLRESULT _BitBlt(PDev *pdev, SURFOBJ *dest, SURFOBJ *src, SURFOBJ *mask,
         local_pos.y = src_pos->y + (area.top - dest_rect->top);
 
         if (dest->iType == STYPE_BITMAP) {
-            return BitBltFromDev(pdev, dest, mask, clip, color_trans, &area, local_pos, mask_pos,
-                                 brush, brush_pos, rop4) ? QXL_SUCCESS : QXL_FAILED;
+            return BitBltFromDev(pdev, src, dest, mask, clip, color_trans, &area, local_pos,
+                                 mask_pos, brush, brush_pos, rop4) ? QXL_SUCCESS : QXL_FAILED;
         }
 
-        if (src->iType != STYPE_BITMAP && rop4 == 0xcccc) { //SRCCOPY no mask
-            return DoCopyBits(pdev, clip, &area, &local_pos) ? QXL_SUCCESS : QXL_FAILED;
+        if (src->iType != STYPE_BITMAP
+            && GetSurfaceId(src) == GetSurfaceId(dest) && rop4 == 0xcccc) { //SRCCOPY no mask
+            return DoCopyBits(pdev, GetSurfaceId(src), clip, &area, &local_pos) ?
+                              QXL_SUCCESS : QXL_FAILED;
         }
 
         src_rect.left = local_pos.x;
@@ -1185,6 +1307,7 @@ static QXLRESULT _BitBlt(PDev *pdev, SURFOBJ *dest, SURFOBJ *src, SURFOBJ *mask,
     } else {
         src_rect_ptr = NULL;
     }
+
     return BitBltCommon(pdev, dest, src, mask, clip, color_trans, &area, src_rect_ptr,
                         mask_pos, brush, brush_pos, rop4, COLORONCOLOR, NULL);
 }
@@ -1197,14 +1320,13 @@ BOOL APIENTRY DrvBitBlt(SURFOBJ *dest, SURFOBJ *src, SURFOBJ *mask, CLIPOBJ *cli
     QXLRESULT res;
 
     if (dest->iType == STYPE_BITMAP) {
-        ASSERT(NULL, src && src->iType != STYPE_BITMAP && src->dhpdev && src_pos);
         pdev = (PDev *)src->dhpdev;
     } else {
-        ASSERT(NULL, dest->dhpdev);
         pdev = (PDev *)dest->dhpdev;
-        CountCall(pdev, CALL_COUNTER_BIT_BLT);
     }
 
+    CountCall(pdev, CALL_COUNTER_BIT_BLT);
+
     DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__));
     if ((res = _BitBlt(pdev, dest, src, mask, clip, color_trans, dest_rect, src_pos, mask_pos,
                        brush, brush_pos, rop4))) {
@@ -1216,6 +1338,7 @@ BOOL APIENTRY DrvBitBlt(SURFOBJ *dest, SURFOBJ *src, SURFOBJ *mask, CLIPOBJ *cli
         return FALSE;
 
     }
+
     DEBUG_PRINT((pdev, 4, "%s: done\n", __FUNCTION__));
     return TRUE;
 }
@@ -1226,14 +1349,13 @@ BOOL APIENTRY DrvCopyBits(SURFOBJ *dest, SURFOBJ *src, CLIPOBJ *clip,
     PDev *pdev;
 
     if (dest->iType == STYPE_BITMAP) {
-        ASSERT(NULL, src && src->iType != STYPE_BITMAP && src->dhpdev && src_pos);
         pdev = (PDev *)src->dhpdev;
     } else {
-        ASSERT(NULL, dest->dhpdev);
         pdev = (PDev *)dest->dhpdev;
-        CountCall(pdev, CALL_COUNTER_BIT_BLT);
     }
 
+    CountCall(pdev, CALL_COUNTER_BIT_BLT);
+
     DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__));
 
     return _BitBlt(pdev, dest, src, NULL, clip, color_trans, dest_rect, src_pos, NULL, NULL,
@@ -1389,13 +1511,12 @@ BOOL APIENTRY DrvStretchBltROP(SURFOBJ *dest, SURFOBJ *src, SURFOBJ *mask, CLIPO
     PDev *pdev;
     QXLRESULT res;
 
-    if (!src || src->iType != STYPE_BITMAP) {
-        ASSERT(NULL, src->dhpdev);
+    if (src && src->iType != STYPE_BITMAP) {
         pdev = (PDev *)src->dhpdev;
-        goto punt;
+    } else {
+        pdev = (PDev *)dest->dhpdev;
     }
 
-    ASSERT(NULL, dest && dest->iType != STYPE_BITMAP && dest->dhpdev);
     pdev = (PDev *)dest->dhpdev;
     DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__));
     CountCall(pdev, CALL_COUNTER_STRETCH_BLT_ROP);
@@ -1425,11 +1546,10 @@ BOOL APIENTRY DrvStretchBlt(SURFOBJ *dest, SURFOBJ *src, SURFOBJ *mask, CLIPOBJ
 
     ASSERT(NULL, src);
     if (src->iType != STYPE_BITMAP) {
-        ASSERT(NULL, src->dhpdev);
         pdev = (PDev *)src->dhpdev;
-        goto punt;
+    } else {
+        pdev = (PDev *)dest->dhpdev;
     }
-    ASSERT(NULL, dest && dest->iType != STYPE_BITMAP && dest->dhpdev);
     pdev = (PDev *)dest->dhpdev;
 
     DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__));
@@ -1522,11 +1642,11 @@ BOOL APIENTRY DrvAlphaBlend(SURFOBJ *dest, SURFOBJ *src, CLIPOBJ *clip, XLATEOBJ
 
     ASSERT(NULL, src && dest);
     if (src->iType != STYPE_BITMAP) {
-        ASSERT(NULL, src->dhpdev);
         pdev = (PDev *)src->dhpdev;
-        goto punt;
+    } else {
+        pdev = (PDev *)dest->dhpdev;
     }
-    ASSERT(NULL, dest->iType != STYPE_BITMAP && dest->dhpdev);
+
     pdev = (PDev *)dest->dhpdev;
     DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__));
 
@@ -1562,7 +1682,7 @@ BOOL APIENTRY DrvAlphaBlend(SURFOBJ *dest, SURFOBJ *src, CLIPOBJ *clip, XLATEOBJ
         return TRUE;
     }
 
-    if (!(drawable = Drawable(pdev, QXL_DRAW_ALPHA_BLEND, &area, clip))) {
+    if (!(drawable = Drawable(pdev, QXL_DRAW_ALPHA_BLEND, &area, clip, GetSurfaceId(dest)))) {
         DEBUG_PRINT((pdev, 0, "%s: Drawable failed\n", __FUNCTION__));
         return FALSE;
     }
@@ -1571,18 +1691,21 @@ BOOL APIENTRY DrvAlphaBlend(SURFOBJ *dest, SURFOBJ *src, CLIPOBJ *clip, XLATEOBJ
         src_rect = &local_src;
     }
 
+    CopyRect(&drawable->surfaces_rects[0], src_rect);
     CopyRect(&drawable->u.alpha_blend.src_area, src_rect);
     if (bland->BlendFunction.AlphaFormat == AC_SRC_ALPHA) {
         ASSERT(pdev, src->iBitmapFormat == BMF_32BPP);
         if (!QXLGetAlphaBitmap(pdev, drawable, &drawable->u.alpha_blend.src_bitmap, src,
-                               &drawable->u.alpha_blend.src_area)) {
+                               &drawable->u.alpha_blend.src_area,
+                               &drawable->surfaces_dest[0])) {
             DEBUG_PRINT((pdev, 0, "%s: QXLGetAlphaBitmap failed\n", __FUNCTION__));
             ReleaseOutput(pdev, drawable->release_info.id);
             return FALSE;
         }
     } else {
         if (!QXLGetBitmap(pdev, drawable, &drawable->u.alpha_blend.src_bitmap, src,
-                        &drawable->u.alpha_blend.src_area, color_trans, NULL, TRUE)) {
+                        &drawable->u.alpha_blend.src_area, color_trans, NULL, TRUE,
+                        &drawable->surfaces_dest[0])) {
             DEBUG_PRINT((pdev, 0, "%s: QXLGetBitmap failed\n", __FUNCTION__));
             ReleaseOutput(pdev, drawable->release_info.id);
             return FALSE;
@@ -1613,11 +1736,11 @@ BOOL APIENTRY DrvTransparentBlt(SURFOBJ *dest, SURFOBJ *src, CLIPOBJ *clip, XLAT
     if (src->iType != STYPE_BITMAP) {
         ASSERT(NULL, src->dhpdev);
         pdev = (PDev *)src->dhpdev;
-        goto punt;
+    } else {
+        ASSERT(NULL, dest->dhpdev);
+        pdev = (PDev *)dest->dhpdev;
     }
 
-    ASSERT(NULL, dest->iType != STYPE_BITMAP && dest->dhpdev);
-    pdev = (PDev *)dest->dhpdev;
     DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__));
 
     ASSERT(pdev, src_rect && src_rect->left < src_rect->right &&
@@ -1643,7 +1766,7 @@ BOOL APIENTRY DrvTransparentBlt(SURFOBJ *dest, SURFOBJ *src, CLIPOBJ *clip, XLAT
         return TRUE;
     }
 
-    if (!(drawable = Drawable(pdev, QXL_DRAW_TRANSPARENT, &area, clip))) {
+    if (!(drawable = Drawable(pdev, QXL_DRAW_TRANSPARENT, &area, clip, GetSurfaceId(dest)))) {
         DEBUG_PRINT((pdev, 0, "%s: Drawable failed\n", __FUNCTION__));
         return FALSE;
     }
@@ -1653,8 +1776,10 @@ BOOL APIENTRY DrvTransparentBlt(SURFOBJ *dest, SURFOBJ *src, CLIPOBJ *clip, XLAT
     }
 
     CopyRect(&drawable->u.transparent.src_area, src_rect);
+    CopyRect(&drawable->surfaces_rects[0], src_rect);
     if (!QXLGetBitmap(pdev, drawable, &drawable->u.transparent.src_bitmap, src,
-                      &drawable->u.transparent.src_area, color_trans, NULL, TRUE)) {
+                      &drawable->u.transparent.src_area, color_trans, NULL, TRUE,
+                      &drawable->surfaces_dest[0])) {
         DEBUG_PRINT((pdev, 0, "%s: QXLGetBitmap failed\n", __FUNCTION__));
         ReleaseOutput(pdev, drawable->release_info.id);
         return FALSE;
diff --git a/display/sources b/display/sources
index 6c1d5c7..617f42c 100644
--- a/display/sources
+++ b/display/sources
@@ -30,5 +30,6 @@ SOURCES=driver.c        \
         mspace.c        \
         quic.c          \
         surface.c       \
+        dd.c            \
         driver.rc
 
diff --git a/display/surface.c b/display/surface.c
index 36ae44c..f15255a 100644
--- a/display/surface.c
+++ b/display/surface.c
@@ -36,14 +36,18 @@
 #include "res.h"
 #include "surface.h"
 
-BOOL CreateDrawArea(PDev *pdev, DrawArea *drawarea, UINT8 *base_mem, UINT32 cx, UINT32 cy)
+BOOL CreateDrawArea(PDev *pdev, UINT8 *base_mem, UINT32 cx, UINT32 cy, UINT32 stride,
+                    UINT32 surface_id)
 {
     SIZEL  size;
+    DrawArea *drawarea;
 
     size.cx = cx;
     size.cy = cy;
 
-    if (!(drawarea->bitmap = (HSURF)EngCreateBitmap(size, size.cx << 2, BMF_32BPP, 0, base_mem))) {
+    drawarea = &pdev->surfaces_info[surface_id].draw_area;
+
+    if (!(drawarea->bitmap = (HSURF)EngCreateBitmap(size, stride, BMF_32BPP, 0, base_mem))) {
         DEBUG_PRINT((pdev, 0, "%s: EngCreateBitmap failed\n", __FUNCTION__));
         return FALSE;
     }
@@ -58,6 +62,8 @@ BOOL CreateDrawArea(PDev *pdev, DrawArea *drawarea, UINT8 *base_mem, UINT32 cx,
         goto error;
     }
 
+    drawarea->base_mem = base_mem;
+
     return TRUE;
 error:
     EngDeleteSurface(drawarea->bitmap);
@@ -66,15 +72,19 @@ error:
 
 VOID FreeDrawArea(DrawArea *drawarea)
 {
-    EngUnlockSurface(drawarea->surf_obj);
-    EngDeleteSurface(drawarea->bitmap);
+    if (drawarea->surf_obj) {
+        EngUnlockSurface(drawarea->surf_obj);
+        EngDeleteSurface(drawarea->bitmap);
+        drawarea->surf_obj = NULL;
+    }
 }
 
 HBITMAP CreateDeviceBitmap(PDev *pdev, SIZEL size, ULONG format, QXLPHYSICAL *phys_mem,
-                           UINT8 **base_mem, UINT8 allocation_type)
+                           UINT8 **base_mem, UINT32 surface_id, UINT8 allocation_type)
 {
     UINT8 depth;
     HBITMAP surf;
+    UINT32 stride;
 
     switch (format) {
         case BMF_8BPP:
@@ -93,7 +103,7 @@ HBITMAP CreateDeviceBitmap(PDev *pdev, SIZEL size, ULONG format, QXLPHYSICAL *ph
             return 0;
     };
 
-    if (!(surf = EngCreateDeviceBitmap((DHSURF)pdev, size, format))) {
+    if (!(surf = EngCreateDeviceBitmap((DHSURF)&pdev->surfaces_info[surface_id], size, format))) {
         DEBUG_PRINT((NULL, 0, "%s: create device surface failed, 0x%lx\n",
                      __FUNCTION__, pdev));
         goto out_error1;
@@ -104,26 +114,61 @@ HBITMAP CreateDeviceBitmap(PDev *pdev, SIZEL size, ULONG format, QXLPHYSICAL *ph
                              HOOK_STRETCHBLTROP | HOOK_TRANSPARENTBLT | HOOK_ALPHABLEND
 #ifdef CALL_TEST
                              | HOOK_PLGBLT | HOOK_FILLPATH | HOOK_STROKEANDFILLPATH | HOOK_LINETO |
-                             HOOK_GRADIENTFILL
+                             HOOK_GRADIENTFILL 
 #endif
                              )) {
         DEBUG_PRINT((pdev, 0, "%s: EngAssociateSurface failed\n", __FUNCTION__));
         goto out_error2;
     }
 
-    if (!QXLGetSurface(pdev, phys_mem, size.cx, size.cy, 32, base_mem, allocation_type)) {
+    pdev->surfaces_info[surface_id].pdev = pdev;
+
+    QXLGetSurface(pdev, phys_mem, size.cx, size.cy, depth, &stride, base_mem, allocation_type);
+    if (!*base_mem) {
         goto out_error2;
     }
 
+    if (!CreateDrawArea(pdev, *base_mem, size.cx, size.cy, stride, surface_id)) {
+        goto out_error3;
+    }
+
+    if (allocation_type != DEVICE_BITMAP_ALLOCATION_TYPE_SURF0) {
+        QXLSurfaceCmd *surface;
+
+        surface = SurfaceCmd(pdev, QXL_SURFACE_CMD_CREATE, surface_id);
+        surface->u.surface_create.depth = depth;
+        surface->u.surface_create.width = size.cx;
+        surface->u.surface_create.height = size.cy;
+        surface->u.surface_create.stride = -(INT32)stride;
+        surface->u.surface_create.data = *phys_mem;
+        PushSurfaceCmd(pdev, surface);
+    }
+
     return surf;
 
+out_error3:
+    QXLDelSurface(pdev, *base_mem, allocation_type);
 out_error2:
+    FreeSurface(pdev, surface_id);
     EngDeleteSurface((HSURF)surf);
 out_error1:
     return 0;
 }
 
-VOID DeleteDeviceBitmap(HSURF surf)
+VOID DeleteDeviceBitmap(PDev *pdev, UINT32 surface_id, UINT8 allocation_type)
 {
-    EngDeleteSurface(surf);
+    DrawArea *drawarea;
+
+    drawarea = &pdev->surfaces_info[surface_id].draw_area;
+
+    FreeDrawArea(drawarea);
+
+    if (allocation_type != DEVICE_BITMAP_ALLOCATION_TYPE_SURF0 &&
+        pdev->Res.surfaces_used[surface_id]) {
+        QXLSurfaceCmd *surface;
+
+        surface = SurfaceCmd(pdev, QXL_SURFACE_CMD_DESTROY, surface_id);
+        QXLGetDelSurface(pdev, surface, surface_id, allocation_type);
+        PushSurfaceCmd(pdev, surface);
+    }
 }
diff --git a/display/surface.h b/display/surface.h
index a384020..e0ecc57 100644
--- a/display/surface.h
+++ b/display/surface.h
@@ -1,20 +1,54 @@
 #ifndef SURFACE_H
 #define SURFACE_H
 
+#include "qxldd.h"
+
+static _inline UINT32 GetSurfaceId(SURFOBJ *surf)
+{
+    PDev *pdev;
+    SurfaceInfo *surface;
+    UINT32 surface_id;
+
+    pdev = (PDev *)surf->dhpdev;
+
+    surface = (SurfaceInfo *)surf->dhsurf;
+    surface_id = surface - pdev->surfaces_info;
+    return surface_id;
+}
+
+static _inline void FreeSurface(PDev *pdev, UINT32 surface_id)
+{
+   pdev->Res.surfaces_used[surface_id] = 0;
+}
+
+
+static UINT32 GetFreeSurface(PDev *pdev)
+{
+    UINT32 x;
+
+    //not effective, fix me
+    for (x = 1; x < pdev->n_surfaces; ++x) {
+        if (!pdev->Res.surfaces_used[x]) {
+            pdev->Res.surfaces_used[x] = 1;
+            return x;
+        }
+    }
+
+    return 0;
+}
+
 enum {
     DEVICE_BITMAP_ALLOCATION_TYPE_SURF0,
+    DEVICE_BITMAP_ALLOCATION_TYPE_DEVRAM,
+    DEVICE_BITMAP_ALLOCATION_TYPE_VRAM,
 };
 
-typedef struct DrawArea {
-   HSURF bitmap;
-   SURFOBJ* surf_obj;
-} DrawArea;
-
-BOOL CreateDrawArea(PDev *pdev, DrawArea *drawarea, UINT8 *base_mem, UINT32 cx, UINT32 cy);
+BOOL CreateDrawArea(PDev *pdev, UINT8 *base_mem, UINT32 cx, UINT32 cy, UINT32 stride,
+                    UINT32 surface_id);
 VOID FreeDrawArea(DrawArea *drawarea);
 
 HBITMAP CreateDeviceBitmap(PDev *pdev, SIZEL size, ULONG format, QXLPHYSICAL *phys_mem,
-                           UINT8 **base_mem, UINT8 allocation_type);
-VOID DeleteDeviceBitmap(HSURF surf);
+                           UINT8 **base_mem, UINT32 surface_id, UINT8 allocation_type);
+VOID DeleteDeviceBitmap(PDev *pdev, UINT32 surface_id, UINT8 allocation_type);
 
 #endif
diff --git a/display/text.c b/display/text.c
index c661544..9d04472 100644
--- a/display/text.c
+++ b/display/text.c
@@ -20,6 +20,7 @@
 #include "utils.h"
 #include "res.h"
 #include "rop.h"
+#include "surface.h"
 
 BOOL APIENTRY DrvTextOut(SURFOBJ *surf, STROBJ *str, FONTOBJ *font, CLIPOBJ *clip,
                          RECTL *ignored, RECTL *opaque_rect,
@@ -31,12 +32,15 @@ BOOL APIENTRY DrvTextOut(SURFOBJ *surf, STROBJ *str, FONTOBJ *font, CLIPOBJ *cli
     ROP3Info *back_rop;
     PDev* pdev;
     RECTL area;
+    UINT32 surface_id;
 
     if (!(pdev = (PDev *)surf->dhpdev)) {
         DEBUG_PRINT((NULL, 0, "%s: err no pdev\n", __FUNCTION__));
         return FALSE;
     }
 
+    surface_id = GetSurfaceId(surf);
+
     CountCall(pdev, CALL_COUNTER_TEXT_OUT);
 
     DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__));
@@ -62,13 +66,14 @@ BOOL APIENTRY DrvTextOut(SURFOBJ *surf, STROBJ *str, FONTOBJ *font, CLIPOBJ *cli
         }
     }
 
-    if (!(drawable = Drawable(pdev, QXL_DRAW_TEXT, &area, clip))) {
+    if (!(drawable = Drawable(pdev, QXL_DRAW_TEXT, &area, clip, surface_id))) {
         return FALSE;
     }
 
     if (opaque_rect) {
         ASSERT(pdev, back_brash && brushs_origin);
-        if (!QXLGetBrush(pdev, drawable, &drawable->u.text.back_brush, back_brash, brushs_origin)) {
+        if (!QXLGetBrush(pdev, drawable, &drawable->u.text.back_brush, back_brash, brushs_origin,
+                         &drawable->surfaces_dest[0], &drawable->surfaces_rects[0])) {
             goto error;
         }
         CopyRect(&drawable->u.text.back_area, &area);
@@ -87,7 +92,8 @@ BOOL APIENTRY DrvTextOut(SURFOBJ *surf, STROBJ *str, FONTOBJ *font, CLIPOBJ *cli
     if (!((fore_rop->flags | back_rop->flags) & ROP3_BRUSH)) {
         drawable->u.stroke.brush.type = SPICE_BRUSH_TYPE_NONE;
     } else if (!QXLGetBrush(pdev, drawable, &drawable->u.text.fore_brush, fore_brush,
-                            brushs_origin)) {
+                            brushs_origin, &drawable->surfaces_dest[1],
+                            &drawable->surfaces_rects[1])) {
         DEBUG_PRINT((pdev, 0, "%s: get brush failed\n", __FUNCTION__));
         goto error;
     }
diff --git a/display/utils.h b/display/utils.h
index 74f3476..124aff7 100644
--- a/display/utils.h
+++ b/display/utils.h
@@ -46,6 +46,12 @@ static _inline LONG RectSize(RECTL *rect)
     return (rect->right - rect->left) * (rect->bottom - rect->top);
 }
 
+#define CopyRectPoint(dest, src, width, height) \
+    (dest)->left = (src)->x; \
+    (dest)->right = (src)->x + width; \
+    (dest)->top = (src)->y; \
+    (dest)->bottom = (src)->y + height; 
+
 #define SameRect(r1, r2) ((r1)->left == (r2)->left && (r1)->right == (r2)->right && \
                           (r1)->top == (r2)->top && (r1)->bottom == (r2)->bottom)
 
diff --git a/include/qxl_driver.h b/include/qxl_driver.h
index 12f5aac..566648c 100644
--- a/include/qxl_driver.h
+++ b/include/qxl_driver.h
@@ -66,6 +66,7 @@ typedef struct QXLDriverInfo {
 
     UINT32 update_area_port;
     SpiceRect *update_area;
+    UINT32 *update_surface;
 
     UINT32 *mm_clock;
 
@@ -79,15 +80,25 @@ typedef struct QXLDriverInfo {
     UINT8 main_mem_slot_id;
     UINT8 slot_id_bits;
     UINT8 slot_gen_bits;
+    UINT8 *slots_generation;
+    UINT64 *ram_slot_start;
+    UINT64 *ram_slot_end;
     MemSlot main_mem_slot;
 
     UINT32 destroy_surface_wait_port;
     UINT32 create_primary_port;
     UINT32 destroy_primary_port;
+    UINT32 memslot_add_port;
+    UINT32 memslot_del_port;
+    UINT32 destroy_all_surfaces_port;
 
     UINT32 dev_id;
 
     QXLSurfaceCreate *primary_surface_create;
+
+    UINT32 n_surfaces;
+
+    UINT64 fb_phys;
 } QXLDriverInfo;
 
 
diff --git a/miniport/qxl.c b/miniport/qxl.c
index fe5449c..9c7e1bc 100644
--- a/miniport/qxl.c
+++ b/miniport/qxl.c
@@ -946,6 +946,7 @@ BOOLEAN StartIO(PVOID dev_extension, PVIDEO_REQUEST_PACKET packet)
             driver_info->notify_cmd_port = dev_ext->io_port + QXL_IO_NOTIFY_CMD;
             driver_info->notify_cursor_port = dev_ext->io_port + QXL_IO_NOTIFY_CURSOR;
             driver_info->notify_oom_port = dev_ext->io_port + QXL_IO_NOTIFY_OOM;
+
             driver_info->log_port = dev_ext->io_port + QXL_IO_LOG;
             driver_info->log_buf = dev_ext->ram_header->log_buf;
 
@@ -957,6 +958,7 @@ BOOLEAN StartIO(PVOID dev_extension, PVIDEO_REQUEST_PACKET packet)
             driver_info->log_level = &dev_ext->rom->log_level;
             driver_info->update_area_port = dev_ext->io_port + QXL_IO_UPDATE_AREA;
             driver_info->update_area = &dev_ext->ram_header->update_area;
+            driver_info->update_surface = &dev_ext->ram_header->update_surface;
 
             driver_info->num_pages = dev_ext->rom->num_pages;
             driver_info->io_pages_virt = dev_ext->ram_start + driver_info->surface0_area_size;
@@ -967,17 +969,27 @@ BOOLEAN StartIO(PVOID dev_extension, PVIDEO_REQUEST_PACKET packet)
             driver_info->num_mem_slot = dev_ext->rom->slots_end;
             driver_info->slot_gen_bits = dev_ext->rom->slot_gen_bits;
             driver_info->slot_id_bits = dev_ext->rom->slot_id_bits;
+	    driver_info->slots_generation = &dev_ext->rom->slot_generation;
+	    driver_info->ram_slot_start = &dev_ext->ram_header->mem_slot.mem_start;
+	    driver_info->ram_slot_end = &dev_ext->ram_header->mem_slot.mem_end;
             driver_info->main_mem_slot = dev_ext->mem_slots[driver_info->main_mem_slot_id];
 
 #if (WINVER < 0x0501)
             driver_info->WaitForEvent = QXLWaitForEvent;
 #endif
             driver_info->destroy_surface_wait_port = dev_ext->io_port + QXL_IO_DESTROY_SURFACE_WAIT;
+            driver_info->destroy_all_surfaces_port = dev_ext->io_port + QXL_IO_DESTROY_ALL_SURFACES;
             driver_info->create_primary_port = dev_ext->io_port + QXL_IO_CREATE_PRIMARY;
             driver_info->destroy_primary_port = dev_ext->io_port + QXL_IO_DESTROY_PRIMARY;
+            driver_info->memslot_add_port = dev_ext->io_port + QXL_IO_MEMSLOT_ADD;
+            driver_info->memslot_del_port = dev_ext->io_port + QXL_IO_MEMSLOT_DEL;
 
             driver_info->primary_surface_create = &dev_ext->ram_header->create_surface;
 
+            driver_info->n_surfaces = dev_ext->rom->n_surfaces;
+
+	    driver_info->fb_phys = dev_ext->vram_physical.QuadPart;
+
             driver_info->dev_id = dev_ext->rom->id;
         }
         break;


commit b53df9ae9e1384a412702df9a371fba6cd3cd29e
Author: Izik Eidus <ieidus at redhat.com>
Date:   Wed Mar 31 01:46:38 2010 +0300

    spice-protocol off screens supports
    
    Signed-off-by: Izik Eidus <ieidus at redhat.com>

diff --git a/spice/draw.h b/spice/draw.h
index 254e0b0..075324b 100644
--- a/spice/draw.h
+++ b/spice/draw.h
@@ -155,6 +155,7 @@ enum {
     SPICE_IMAGE_TYPE_LZ_RGB,
     SPICE_IMAGE_TYPE_GLZ_RGB,
     SPICE_IMAGE_TYPE_FROM_CACHE,
+    SPICE_IMAGE_TYPE_SURFACE,
 };
 
 enum {
@@ -203,6 +204,15 @@ typedef struct SPICE_ATTR_PACKED SpiceBitmapImage {
     SpiceBitmap bitmap;
 } SpiceBitmapImage;
 
+typedef struct SPICE_ATTR_PACKED SpiceSurface {
+    uint32_t surface_id;
+} SpiceSurface;
+
+typedef struct SPICE_ATTR_PACKED SpiceSurfaceImage {
+    SpiceImageDescriptor descriptor; //?
+    SpiceSurface surface;
+} SpiceSurfaceImage;
+
 typedef struct SPICE_ATTR_PACKED SpiceQUICData {
     uint32_t data_size;
     uint8_t data[0];
diff --git a/spice/qxl_dev.h b/spice/qxl_dev.h
index 353018d..af9f30a 100644
--- a/spice/qxl_dev.h
+++ b/spice/qxl_dev.h
@@ -70,6 +70,7 @@ enum {
     QXL_IO_CREATE_PRIMARY,
     QXL_IO_DESTROY_PRIMARY,
     QXL_IO_DESTROY_SURFACE_WAIT,
+    QXL_IO_DESTROY_ALL_SURFACES,
 
     QXL_IO_RANGE_SIZE
 };
@@ -91,6 +92,7 @@ typedef struct SPICE_ATTR_PACKED QXLRom {
     uint8_t slot_gen_bits;
     uint8_t slot_id_bits;
     uint8_t slot_generation;
+    uint32_t n_surfaces;
 } QXLRom;
 
 typedef struct SPICE_ATTR_PACKED QXLMode {
@@ -118,6 +120,7 @@ enum QXLCmdType {
     QXL_CMD_UPDATE,
     QXL_CMD_CURSOR,
     QXL_CMD_MESSAGE,
+    QXL_CMD_SURFACE,
 };
 
 typedef struct SPICE_ATTR_PACKED QXLCommand {
@@ -169,6 +172,7 @@ typedef struct SPICE_ATTR_PACKED QXLRam {
     QXLCursorRing cursor_ring;
     QXLReleaseRing release_ring;
     SpiceRect update_area;
+    uint32_t update_surface;
     QXLMemSlot mem_slot;
     QXLSurfaceCreate create_surface;
     uint64_t flags;
@@ -200,6 +204,7 @@ typedef struct SPICE_ATTR_PACKED QXLUpdateCmd {
     QXLReleaseInfo release_info;
     SpiceRect area;
     uint32_t update_id;
+    uint32_t surface_id;
 } QXLUpdateCmd;
 
 typedef struct SPICE_ATTR_PACKED QXLCursor {
@@ -274,6 +279,7 @@ typedef struct SPICE_ATTR_PACKED QXLCopyBits {
 
 typedef struct SPICE_ATTR_PACKED QXLDrawable {
     QXLReleaseInfo release_info;
+    uint32_t surface_id;
     uint8_t effect;
     uint8_t type;
     uint8_t self_bitmap;
@@ -281,6 +287,8 @@ typedef struct SPICE_ATTR_PACKED QXLDrawable {
     SpiceRect bbox;
     SpiceClip clip;
     uint32_t mm_time;
+    int32_t surfaces_dest[3];
+    SpiceRect surfaces_rects[3];
     union {
         SpiceFill fill;
         SpiceOpaque opaque;
@@ -298,6 +306,29 @@ typedef struct SPICE_ATTR_PACKED QXLDrawable {
     } u;
 } QXLDrawable;
 
+enum QXLSurfaceCmdType {
+    QXL_SURFACE_CMD_CREATE,
+    QXL_SURFACE_CMD_DESTROY,
+};
+
+typedef struct SPICE_ATTR_PACKED QXLSurface {
+    uint8_t depth;
+    uint32_t width;
+    uint32_t height;
+    int32_t stride;
+    QXLPHYSICAL data;
+} QXLSurface;
+
+typedef struct SPICE_ATTR_PACKED QXLSurfaceCmd {
+    QXLReleaseInfo release_info;
+    uint32_t surface_id;
+    uint8_t type;
+    uint32_t flags;
+    union {
+        QXLSurface surface_create;
+    } u;
+} QXLSurfaceCmd;
+
 typedef struct SPICE_ATTR_PACKED QXLClipRects {
     uint32_t num_rects;
     QXLDataChunk chunk;
@@ -349,6 +380,7 @@ typedef struct SPICE_ATTR_PACKED QXLImage {
     union { // variable length
         SpiceBitmap bitmap;
         SpiceQUICData quic;
+        SpiceSurface surface_image;
     };
 } QXLImage;
 


More information about the Spice-devel mailing list