diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index 9d90df718f686d4b0327a173e5c1b48ec9563c89..bcca5d791370b32ec7166c72340fa2b76794c8d4 100644
--- a/dlls/wined3d/device.c
+++ b/dlls/wined3d/device.c
@@ -1930,6 +1930,16 @@ static HRESULT WINAPI IWineD3DDeviceImpl_Uninit3D(IWineD3DDevice *iface, D3DCB_D
     ActivateContext(This, This->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
     LEAVE_GL();
 
+    TRACE("Deleting high order patches\n");
+    for(i = 0; i < PATCHMAP_SIZE; i++) {
+        struct list *e1, *e2;
+        struct WineD3DRectPatch *patch;
+        LIST_FOR_EACH_SAFE(e1, e2, &This->patches[i]) {
+            patch = LIST_ENTRY(e1, struct WineD3DRectPatch, entry);
+            IWineD3DDevice_DeletePatch(iface, patch->Handle);
+        }
+    }
+
     /* Delete the pbuffer context if there is any */
     if(This->pbufferContext) DestroyContext(This, This->pbufferContext);
 
@@ -5194,20 +5204,87 @@ static HRESULT  WINAPI  IWineD3DDeviceImpl_UpdateSurface(IWineD3DDevice *iface,
     return WINED3D_OK;
 }
 
-/* Implementation details at http://developer.nvidia.com/attach/6494
-and
-http://oss.sgi.com/projects/ogl-sample/registry/NV/evaluators.txt
-hmm.. no longer supported use
-OpenGL evaluators or  tessellate surfaces within your application.
-*/
-
-/* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/directx/graphics/reference/d3d/interfaces/idirect3ddevice9/DrawRectPatch.asp */
 static HRESULT WINAPI IWineD3DDeviceImpl_DrawRectPatch(IWineD3DDevice *iface, UINT Handle, CONST float* pNumSegs, CONST WINED3DRECTPATCH_INFO* pRectPatchInfo) {
     IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+    struct WineD3DRectPatch *patch;
+    unsigned int i;
+    struct list *e;
+    BOOL found;
     TRACE("(%p) Handle(%d) noSegs(%p) rectpatch(%p)\n", This, Handle, pNumSegs, pRectPatchInfo);
-    FIXME("(%p) : Stub\n", This);
-    return WINED3D_OK;
 
+    if(!(Handle || pRectPatchInfo)) {
+        /* TODO: Write a test for the return value, thus the FIXME */
+        FIXME("Both Handle and pRectPatchInfo are NULL\n");
+        return WINED3DERR_INVALIDCALL;
+    }
+
+    if(Handle) {
+        i = PATCHMAP_HASHFUNC(Handle);
+        found = FALSE;
+        LIST_FOR_EACH(e, &This->patches[i]) {
+            patch = LIST_ENTRY(e, struct WineD3DRectPatch, entry);
+            if(patch->Handle == Handle) {
+                found = TRUE;
+                break;
+            }
+        }
+
+        if(!found) {
+            TRACE("Patch does not exist. Creating a new one\n");
+            patch = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*patch));
+            patch->Handle = Handle;
+            list_add_head(&This->patches[i], &patch->entry);
+        } else {
+            TRACE("Found existing patch %p\n", patch);
+        }
+    } else {
+        /* Since opengl does not load tesselated vertex attributes into numbered vertex
+         * attributes we have to tesselate, read back, and draw. This needs a patch
+         * management structure instance. Create one.
+         *
+         * A possible improvement is to check if a vertex shader is used, and if not directly
+         * draw the patch.
+         */
+        FIXME("Drawing an uncached patch. This is slow\n");
+        patch = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*patch));
+    }
+
+    if(pNumSegs[0] != patch->numSegs[0] || pNumSegs[1] != patch->numSegs[1] ||
+       pNumSegs[2] != patch->numSegs[2] || pNumSegs[3] != patch->numSegs[3] ||
+       (pRectPatchInfo && memcmp(pRectPatchInfo, &patch->RectPatchInfo, sizeof(*pRectPatchInfo)) != 0) ) {
+        HRESULT hr;
+        TRACE("Tesselation density or patch info changed, retesselating\n");
+
+        if(pRectPatchInfo) {
+            memcpy(&patch->RectPatchInfo, pRectPatchInfo, sizeof(*pRectPatchInfo));
+        }
+        patch->numSegs[0] = pNumSegs[0];
+        patch->numSegs[1] = pNumSegs[1];
+        patch->numSegs[2] = pNumSegs[2];
+        patch->numSegs[3] = pNumSegs[3];
+
+        hr = tesselate_rectpatch(This, patch);
+        if(FAILED(hr)) {
+            WARN("Patch tesselation failed\n");
+
+            /* Do not release the handle to store the params of the patch */
+            if(!Handle) {
+                HeapFree(GetProcessHeap(), 0, patch);
+            }
+            return hr;
+        }
+    }
+
+    This->currentPatch = patch;
+    IWineD3DDevice_DrawPrimitiveStrided(iface, WINED3DPT_TRIANGLELIST, patch->numSegs[0] * patch->numSegs[1] * 2, &patch->strided);
+    This->currentPatch = NULL;
+
+    /* Destroy uncached patches */
+    if(!Handle) {
+        HeapFree(GetProcessHeap(), 0, patch->mem);
+        HeapFree(GetProcessHeap(), 0, patch);
+    }
+    return WINED3D_OK;
 }
 
 /* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/directx/graphics/reference/d3d/interfaces/idirect3ddevice9/DrawTriPatch.asp */
@@ -5220,9 +5297,26 @@ static HRESULT WINAPI IWineD3DDeviceImpl_DrawTriPatch(IWineD3DDevice *iface, UIN
 
 static HRESULT WINAPI IWineD3DDeviceImpl_DeletePatch(IWineD3DDevice *iface, UINT Handle) {
     IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+    int i;
+    struct WineD3DRectPatch *patch;
+    struct list *e;
     TRACE("(%p) Handle(%d)\n", This, Handle);
-    FIXME("(%p) : Stub\n", This);
-    return WINED3D_OK;
+
+    i = PATCHMAP_HASHFUNC(Handle);
+    LIST_FOR_EACH(e, &This->patches[i]) {
+        patch = LIST_ENTRY(e, struct WineD3DRectPatch, entry);
+        if(patch->Handle == Handle) {
+            TRACE("Deleting patch %p\n", patch);
+            list_remove(&patch->entry);
+            HeapFree(GetProcessHeap(), 0, patch->mem);
+            HeapFree(GetProcessHeap(), 0, patch);
+            return WINED3D_OK;
+        }
+    }
+
+    /* TODO: Write a test for the return value */
+    FIXME("Attempt to destroy nonexistant patch\n");
+    return WINED3DERR_INVALIDCALL;
 }
 
 static IWineD3DSwapChain *get_swapchain(IWineD3DSurface *target) {
diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c
index 171d96950d8e4f8525986fc4e9c4be0fd73da1f8..ce2ca7027c1895402a3b0922bf168f873b63b0d4 100644
--- a/dlls/wined3d/directx.c
+++ b/dlls/wined3d/directx.c
@@ -1863,7 +1863,8 @@ static HRESULT WINAPI IWineD3DImpl_GetDeviceCaps(IWineD3D *iface, UINT Adapter,
                                       WINED3DDEVCAPS_TEXTURESYSTEMMEMORY |
                                       WINED3DDEVCAPS_CANRENDERAFTERFLIP  |
                                       WINED3DDEVCAPS_DRAWPRIMITIVES2     |
-                                      WINED3DDEVCAPS_DRAWPRIMITIVES2EX;
+                                      WINED3DDEVCAPS_DRAWPRIMITIVES2EX   |
+                                      WINED3DDEVCAPS_RTPATCHES;
 
     *pCaps->PrimitiveMiscCaps       = WINED3DPMISCCAPS_CULLNONE              |
                                       WINED3DPMISCCAPS_CULLCCW               |
@@ -2382,6 +2383,7 @@ static HRESULT  WINAPI IWineD3DImpl_CreateDevice(IWineD3D *iface, UINT Adapter,
     IWineD3DImpl       *This    = (IWineD3DImpl *)iface;
     HDC hDC;
     HRESULT temp_result;
+    int i;
 
     /* Validate the adapter number */
     if (Adapter >= IWineD3D_GetAdapterCount(iface)) {
@@ -2472,6 +2474,9 @@ static HRESULT  WINAPI IWineD3DImpl_CreateDevice(IWineD3D *iface, UINT Adapter,
     object->ddraw_format = pixelformat_for_depth(GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES));
     ReleaseDC(0, hDC);
 
+    for(i = 0; i < PATCHMAP_SIZE; i++) {
+        list_init(&object->patches[i]);
+    }
     return WINED3D_OK;
 create_device_error:
 
diff --git a/dlls/wined3d/drawprim.c b/dlls/wined3d/drawprim.c
index 10f952ea37fa4d3f72b841fe7321b4cd8a05a36e..1b03528db6a824683f030a088490350f8200b011 100644
--- a/dlls/wined3d/drawprim.c
+++ b/dlls/wined3d/drawprim.c
@@ -1148,3 +1148,387 @@ if(stridedlcl.u.s.type.VBO) { \
     /* Control goes back to the device, stateblock values may change again */
     This->isInDraw = FALSE;
 }
+
+static void normalize_normal(float *n) {
+    float length = n[0] * n[0] + n[1] * n[1] + n[2] * n[2];
+    if(length == 0.0) return;
+    length = sqrt(length);
+    n[0] = n[0] / length;
+    n[1] = n[1] / length;
+    n[2] = n[2] / length;
+}
+
+/* Tesselates a high order rectangular patch into single triangles using gl evaluators
+ *
+ * The problem is that OpenGL does not offer a direct way to return the tesselated primitives,
+ * and they can't be sent off for rendering directly either. Tesselating is slow, so we want
+ * to chache the patches in a vertex buffer. But more importantly, gl can't bind generated
+ * attributes to numbered shader attributes, so we have to store them and rebind them as needed
+ * in drawprim.
+ *
+ * To read back, the opengl feedback mode is used. This creates a proplem because we want
+ * untransformed, unlit vertices, but feedback runs everything through transform and lighting.
+ * Thus disable lighting and set identity matrices to get unmodified colors and positions.
+ * To overcome clipping find the biggest x, y and z values of the vertices in the patch and scale
+ * them to [-1.0;+1.0] and set the viewport up to scale them back.
+ *
+ * Normals are more tricky: Draw white vertices with 3 directional lights, and calculate the
+ * resulting colors back to the normals.
+ *
+ * NOTE: This function activates a context for blitting, modifies matrices & viewport, but
+ * does not restore it because normally a draw follows immediately afterwards. The caller is
+ * responsible of taking care that either the gl states are restored, or the context activated
+ * for drawing to reset the lastWasBlit flag.
+ */
+HRESULT tesselate_rectpatch(IWineD3DDeviceImpl *This,
+                            struct WineD3DRectPatch *patch) {
+    unsigned int i, j, num_quads, out_vertex_size, buffer_size, d3d_out_vertex_size;
+    float max_x = 0.0, max_y = 0.0, max_z = 0.0, neg_z = 0.0;
+    WineDirect3DVertexStridedData strided;
+    BYTE *data;
+    WINED3DRECTPATCH_INFO *info = &patch->RectPatchInfo;
+    DWORD vtxStride;
+    GLenum feedback_type;
+    GLfloat *feedbuffer;
+
+    /* First, locate the position data. This is provided in a vertex buffer in the stateblock.
+     * Beware of vbos
+     */
+    memset(&strided, 0, sizeof(strided));
+    primitiveDeclarationConvertToStridedData((IWineD3DDevice *) This, FALSE, &strided, NULL);
+    if(strided.u.s.position.VBO) {
+        IWineD3DVertexBufferImpl *vb;
+        vb = (IWineD3DVertexBufferImpl *) This->stateBlock->streamSource[strided.u.s.position.streamNo];
+        strided.u.s.position.lpData = (BYTE *) ((unsigned long) strided.u.s.position.lpData +
+                                                (unsigned long) vb->resource.allocatedMemory);
+    }
+    vtxStride = strided.u.s.position.dwStride;
+    data = strided.u.s.position.lpData +
+           vtxStride * info->Stride * info->StartVertexOffsetHeight +
+           vtxStride * info->StartVertexOffsetWidth;
+
+    /* Not entirely sure about what happens with transformed vertices */
+    if(strided.u.s.position_transformed) {
+        FIXME("Transformed position in rectpatch generation\n");
+    }
+    if(vtxStride % sizeof(GLfloat)) {
+        /* glMap2f reads vertex sizes in GLfloats, the d3d stride is in bytes.
+         * I don't see how the stride could not be a multiple of 4, but make sure
+         * to check it
+         */
+        ERR("Vertex stride is not a multiple of sizeof(GLfloat)\n");
+    }
+    if(info->Basis != WINED3DBASIS_BEZIER) {
+        FIXME("Basis is %s, how to handle this?\n", debug_d3dbasis(info->Basis));
+    }
+    if(info->Degree != WINED3DDEGREE_CUBIC) {
+        FIXME("Degree is %s, how to handle this?\n", debug_d3ddegree(info->Degree));
+    }
+
+    /* First, get the boundary cube of the input data */
+    for(j = 0; j < info->Height; j++) {
+        for(i = 0; i < info->Width; i++) {
+            float *v = (float *) (data + vtxStride * i + vtxStride * info->Stride * j);
+            if(fabs(v[0]) > max_x) max_x = fabs(v[0]);
+            if(fabs(v[1]) > max_y) max_y = fabs(v[1]);
+            if(fabs(v[2]) > max_z) max_z = fabs(v[2]);
+            if(v[2] < neg_z) neg_z = v[2];
+        }
+    }
+
+    /* This needs some improvements in the vertex decl code */
+    FIXME("Cannot find data to generate. Only generating position and normals\n");
+    patch->has_normals = TRUE;
+    patch->has_texcoords = FALSE;
+
+    ENTER_GL();
+    /* Simply activate the context for blitting. This disables all the things we don't want and
+     * takes care for dirtifying. Dirtifying is prefered over pushing / popping, since drawing the
+     * patch(as opposed to normal draws) will most likely need different changes anyway
+     */
+    ActivateContext(This, This->lastActiveRenderTarget, CTXUSAGE_BLIT);
+
+    glMatrixMode(GL_PROJECTION);
+    checkGLcall("glMatrixMode(GL_PROJECTION)");
+    glLoadIdentity();
+    checkGLcall("glLoadIndentity()");
+    glScalef(1 / (max_x) , 1 / (max_y), max_z == 0 ? 1 : 1 / ( 2 * max_z));
+    glTranslatef(0, 0, 0.5);
+    checkGLcall("glScalef");
+    glViewport(-max_x, -max_y, 2 * (max_x), 2 * (max_y));
+    checkGLcall("glViewport");
+
+    /* Some states to take care of. If we're in wireframe opengl will produce lines, and confuse
+     * our feedback buffer parser
+     */
+    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+    checkGLcall("glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)");
+    IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_FILLMODE));
+    if(patch->has_normals) {
+        float black[4] = {0, 0, 0, 0};
+        float red[4]   = {1, 0, 0, 0};
+        float green[4] = {0, 1, 0, 0};
+        float blue[4]  = {0, 0, 1, 0};
+        float white[4] = {1, 1, 1, 1};
+        glEnable(GL_LIGHTING);
+        checkGLcall("glEnable(GL_LIGHTING)");
+        glLightModelfv(GL_LIGHT_MODEL_AMBIENT, black);
+        checkGLcall("glLightModel for MODEL_AMBIENT");
+        IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_AMBIENT));
+
+        for(i = 3; i < GL_LIMITS(lights); i++) {
+            glDisable(GL_LIGHT0 + i);
+            checkGLcall("glDisable(GL_LIGHT0 + i)");
+            IWineD3DDeviceImpl_MarkStateDirty(This, STATE_ACTIVELIGHT(i));
+        }
+
+        IWineD3DDeviceImpl_MarkStateDirty(This, STATE_ACTIVELIGHT(0));
+        glLightfv(GL_LIGHT0, GL_DIFFUSE, red);
+        glLightfv(GL_LIGHT0, GL_SPECULAR, black);
+        glLightfv(GL_LIGHT0, GL_AMBIENT, black);
+        glLightfv(GL_LIGHT0, GL_POSITION, red);
+        glEnable(GL_LIGHT0);
+        checkGLcall("Setting up light 1\n");
+        IWineD3DDeviceImpl_MarkStateDirty(This, STATE_ACTIVELIGHT(1));
+        glLightfv(GL_LIGHT1, GL_DIFFUSE, green);
+        glLightfv(GL_LIGHT1, GL_SPECULAR, black);
+        glLightfv(GL_LIGHT1, GL_AMBIENT, black);
+        glLightfv(GL_LIGHT1, GL_POSITION, green);
+        glEnable(GL_LIGHT1);
+        checkGLcall("Setting up light 2\n");
+        IWineD3DDeviceImpl_MarkStateDirty(This, STATE_ACTIVELIGHT(2));
+        glLightfv(GL_LIGHT2, GL_DIFFUSE, blue);
+        glLightfv(GL_LIGHT2, GL_SPECULAR, black);
+        glLightfv(GL_LIGHT2, GL_AMBIENT, black);
+        glLightfv(GL_LIGHT2, GL_POSITION, blue);
+        glEnable(GL_LIGHT2);
+        checkGLcall("Setting up light 3\n");
+
+        IWineD3DDeviceImpl_MarkStateDirty(This, STATE_MATERIAL);
+        IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_COLORVERTEX));
+        glDisable(GL_COLOR_MATERIAL);
+        glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, black);
+        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, black);
+        glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, white);
+        checkGLcall("Setting up materials\n");
+    }
+
+    /* Enable the needed maps.
+     * GL_MAP2_VERTEX_3 is needed for positional data.
+     * GL_AUTO_NORMAL to generate normals from the position. Do not use GL_MAP2_NORMAL.
+     * GL_MAP2_TEXTURE_COORD_4 for texture coords
+     */
+    num_quads = ceilf(patch->numSegs[0]) * ceilf(patch->numSegs[1]);
+    out_vertex_size = 3 /* position */;
+    d3d_out_vertex_size = 3;
+    glEnable(GL_MAP2_VERTEX_3);
+    if(patch->has_normals && patch->has_texcoords) {
+        FIXME("Texcoords not handled yet\n");
+        feedback_type = GL_3D_COLOR_TEXTURE;
+        out_vertex_size += 8;
+        d3d_out_vertex_size += 7;
+        glEnable(GL_AUTO_NORMAL);
+        glEnable(GL_MAP2_TEXTURE_COORD_4);
+    } else if(patch->has_texcoords) {
+        FIXME("Texcoords not handled yet\n");
+        feedback_type = GL_3D_COLOR_TEXTURE;
+        out_vertex_size += 7;
+        d3d_out_vertex_size += 4;
+        glEnable(GL_MAP2_TEXTURE_COORD_4);
+    } else if(patch->has_normals) {
+        feedback_type = GL_3D_COLOR;
+        out_vertex_size += 4;
+        d3d_out_vertex_size += 3;
+        glEnable(GL_AUTO_NORMAL);
+    } else {
+        feedback_type = GL_3D;
+    }
+    checkGLcall("glEnable vertex attrib generation");
+
+    buffer_size = num_quads * out_vertex_size * 2 /* triangle list */ * 3 /* verts per tri */
+                   + 4 * num_quads /* 2 triangle markers per quad + num verts in tri */;
+    feedbuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_size * sizeof(float) * 8);
+
+    glMap2f(GL_MAP2_VERTEX_3,
+            0, 1, vtxStride / sizeof(float), info->Width,
+            0, 1, info->Stride * vtxStride / sizeof(float), info->Height,
+            (float *) data);
+    checkGLcall("glMap2f");
+    if(patch->has_texcoords) {
+        glMap2f(GL_MAP2_TEXTURE_COORD_4,
+                0, 1, vtxStride / sizeof(float), info->Width,
+                0, 1, info->Stride * vtxStride / sizeof(float), info->Height,
+                (float *) data);
+        checkGLcall("glMap2f");
+    }
+    glMapGrid2f(ceilf(patch->numSegs[0]), 0.0, 1.0, ceilf(patch->numSegs[1]), 0.0, 1.0);
+    checkGLcall("glMapGrid2f");
+
+    glFeedbackBuffer(buffer_size * 2, feedback_type, feedbuffer);
+    checkGLcall("glFeedbackBuffer");
+    glRenderMode(GL_FEEDBACK);
+
+    glEvalMesh2(GL_FILL, 0, ceilf(patch->numSegs[0]), 0, ceilf(patch->numSegs[1]));
+    checkGLcall("glEvalMesh2\n");
+
+    i = glRenderMode(GL_RENDER);
+    if(i == -1) {
+        ERR("Feedback failed. Expected %d elements back\n", buffer_size);
+        Sleep(10000);
+        HeapFree(GetProcessHeap(), 0, feedbuffer);
+        return WINED3DERR_DRIVERINTERNALERROR;
+    } else if(i != buffer_size) {
+        ERR("Unexpected amount of elements returned. Expected %d, got %d\n", buffer_size, i);
+        Sleep(10000);
+        HeapFree(GetProcessHeap(), 0, feedbuffer);
+        return WINED3DERR_DRIVERINTERNALERROR;
+    } else {
+        TRACE("Got %d elements as expected\n", i);
+    }
+
+    HeapFree(GetProcessHeap(), 0, patch->mem);
+    patch->mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, num_quads * 6 * d3d_out_vertex_size * sizeof(float) * 8);
+    i = 0;
+    for(j = 0; j < buffer_size; j += (3 /* num verts */ * out_vertex_size + 2 /* tri marker */)) {
+        if(feedbuffer[j] != GL_POLYGON_TOKEN) {
+            ERR("Unexpected token: %f\n", feedbuffer[j]);
+            continue;
+        }
+        if(feedbuffer[j + 1] != 3) {
+            ERR("Unexpected polygon: %f corners\n", feedbuffer[j + 1]);
+            continue;
+        }
+        /* Somehow there are different ideas about back / front facing, so fix up the
+         * vertex order
+         */
+        patch->mem[i + 0] =  feedbuffer[j + out_vertex_size * 2 + 2]; /* x, triangle 2 */
+        patch->mem[i + 1] =  feedbuffer[j + out_vertex_size * 2 + 3]; /* y, triangle 2 */
+        patch->mem[i + 2] = (feedbuffer[j + out_vertex_size * 2 + 4] - 0.5) * 4 * max_z; /* z, triangle 3 */
+        if(patch->has_normals) {
+            patch->mem[i + 3] = feedbuffer[j + out_vertex_size * 2 + 5];
+            patch->mem[i + 4] = feedbuffer[j + out_vertex_size * 2 + 6];
+            patch->mem[i + 5] = feedbuffer[j + out_vertex_size * 2 + 7];
+        }
+        i += d3d_out_vertex_size;
+
+        patch->mem[i + 0] =  feedbuffer[j + out_vertex_size * 1 + 2]; /* x, triangle 2 */
+        patch->mem[i + 1] =  feedbuffer[j + out_vertex_size * 1 + 3]; /* y, triangle 2 */
+        patch->mem[i + 2] = (feedbuffer[j + out_vertex_size * 1 + 4] - 0.5) * 4 * max_z; /* z, triangle 2 */
+        if(patch->has_normals) {
+            patch->mem[i + 3] = feedbuffer[j + out_vertex_size * 1 + 5];
+            patch->mem[i + 4] = feedbuffer[j + out_vertex_size * 1 + 6];
+            patch->mem[i + 5] = feedbuffer[j + out_vertex_size * 1 + 7];
+        }
+        i += d3d_out_vertex_size;
+
+        patch->mem[i + 0] =  feedbuffer[j + out_vertex_size * 0 + 2]; /* x, triangle 1 */
+        patch->mem[i + 1] =  feedbuffer[j + out_vertex_size * 0 + 3]; /* y, triangle 1 */
+        patch->mem[i + 2] = (feedbuffer[j + out_vertex_size * 0 + 4] - 0.5) * 4 * max_z; /* z, triangle 1 */
+        if(patch->has_normals) {
+            patch->mem[i + 3] = feedbuffer[j + out_vertex_size * 0 + 5];
+            patch->mem[i + 4] = feedbuffer[j + out_vertex_size * 0 + 6];
+            patch->mem[i + 5] = feedbuffer[j + out_vertex_size * 0 + 7];
+        }
+        i += d3d_out_vertex_size;
+    }
+
+    if(patch->has_normals) {
+        /* Now do the same with reverse light directions */
+        float x[4] = {-1,  0,  0, 0};
+        float y[4] = { 0, -1,  0, 0};
+        float z[4] = { 0,  0, -1, 0};
+        glLightfv(GL_LIGHT0, GL_POSITION, x);
+        glLightfv(GL_LIGHT1, GL_POSITION, y);
+        glLightfv(GL_LIGHT2, GL_POSITION, z);
+        checkGLcall("Setting up reverse light directions\n");
+
+        glRenderMode(GL_FEEDBACK);
+        checkGLcall("glRenderMode(GL_FEEDBACK)");
+        glEvalMesh2(GL_FILL, 0, ceilf(patch->numSegs[0]), 0, ceilf(patch->numSegs[1]));
+        checkGLcall("glEvalMesh2\n");
+        i = glRenderMode(GL_RENDER);
+        checkGLcall("glRenderMode(GL_RENDER)");
+
+        i = 0;
+        for(j = 0; j < buffer_size; j += (3 /* num verts */ * out_vertex_size + 2 /* tri marker */)) {
+            if(feedbuffer[j] != GL_POLYGON_TOKEN) {
+                ERR("Unexpected token: %f\n", feedbuffer[j]);
+                continue;
+            }
+            if(feedbuffer[j + 1] != 3) {
+                ERR("Unexpected polygon: %f corners\n", feedbuffer[j + 1]);
+                continue;
+            }
+            if(patch->mem[i + 3] == 0.0)
+                patch->mem[i + 3] = -feedbuffer[j + out_vertex_size * 2 + 5];
+            if(patch->mem[i + 4] == 0.0)
+                patch->mem[i + 4] = -feedbuffer[j + out_vertex_size * 2 + 6];
+            if(patch->mem[i + 5] == 0.0)
+                patch->mem[i + 5] = -feedbuffer[j + out_vertex_size * 2 + 7];
+            normalize_normal(patch->mem + i + 3);
+            i += d3d_out_vertex_size;
+
+            if(patch->mem[i + 3] == 0.0)
+                patch->mem[i + 3] = -feedbuffer[j + out_vertex_size * 1 + 5];
+            if(patch->mem[i + 4] == 0.0)
+                patch->mem[i + 4] = -feedbuffer[j + out_vertex_size * 1 + 6];
+            if(patch->mem[i + 5] == 0.0)
+                patch->mem[i + 5] = -feedbuffer[j + out_vertex_size * 1 + 7];
+            normalize_normal(patch->mem + i + 3);
+            i += d3d_out_vertex_size;
+
+            if(patch->mem[i + 3] == 0.0)
+                patch->mem[i + 3] = -feedbuffer[j + out_vertex_size * 0 + 5];
+            if(patch->mem[i + 4] == 0.0)
+                patch->mem[i + 4] = -feedbuffer[j + out_vertex_size * 0 + 6];
+            if(patch->mem[i + 5] == 0.0)
+                patch->mem[i + 5] = -feedbuffer[j + out_vertex_size * 0 + 7];
+            normalize_normal(patch->mem + i + 3);
+            i += d3d_out_vertex_size;
+        }
+    }
+
+    glDisable(GL_MAP2_VERTEX_3);
+    glDisable(GL_AUTO_NORMAL);
+    glDisable(GL_MAP2_NORMAL);
+    glDisable(GL_MAP2_TEXTURE_COORD_4);
+    checkGLcall("glDisable vertex attrib generation");
+    LEAVE_GL();
+
+    HeapFree(GetProcessHeap(), 0, feedbuffer);
+
+    vtxStride = 3 * sizeof(float);
+    if(patch->has_normals) {
+        vtxStride += 3 * sizeof(float);
+    }
+    if(patch->has_texcoords) {
+        vtxStride += 4 * sizeof(float);
+    }
+    memset(&patch->strided, 0, sizeof(&patch->strided));
+    patch->strided.u.s.position.lpData = (BYTE *) patch->mem;
+    patch->strided.u.s.position.dwStride = vtxStride;
+    patch->strided.u.s.position.dwType = WINED3DDECLTYPE_FLOAT3;
+    patch->strided.u.s.position.streamNo = 255;
+
+    if(patch->has_normals) {
+        patch->strided.u.s.normal.lpData = (BYTE *) patch->mem + 3 * sizeof(float) /* pos */;
+        patch->strided.u.s.normal.dwStride = vtxStride;
+        patch->strided.u.s.normal.dwType = WINED3DDECLTYPE_FLOAT3;
+        patch->strided.u.s.normal.streamNo = 255;
+    }
+    if(patch->has_texcoords) {
+        patch->strided.u.s.texCoords[0].lpData = (BYTE *) patch->mem + 3 * sizeof(float) /* pos */;
+        if(patch->has_normals) {
+            patch->strided.u.s.texCoords[0].lpData += 3 * sizeof(float);
+        }
+        patch->strided.u.s.texCoords[0].dwStride = vtxStride;
+        patch->strided.u.s.texCoords[0].dwType = WINED3DDECLTYPE_FLOAT4;
+        /* MAX_STREAMS index points to an unused element in stateblock->streamOffsets which
+         * always remains set to 0. Windows uses stream 255 here, but this is not visible to the
+         * application.
+         */
+        patch->strided.u.s.texCoords[0].streamNo = MAX_STREAMS;
+    }
+
+    return WINED3D_OK;
+}
diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c
index 65bff5cc7b76cf140810ea71293bc093a0f26441..99835dca2e9e66f4e44442e2a71c74a674c92492 100644
--- a/dlls/wined3d/utils.c
+++ b/dlls/wined3d/utils.c
@@ -732,6 +732,25 @@ const char *debug_glerror(GLenum error) {
     }
 }
 
+const char *debug_d3dbasis(WINED3DBASISTYPE basis) {
+    switch(basis) {
+        case WINED3DBASIS_BEZIER:       return "WINED3DBASIS_BEZIER";
+        case WINED3DBASIS_BSPLINE:      return "WINED3DBASIS_BSPLINE";
+        case WINED3DBASIS_INTERPOLATE:  return "WINED3DBASIS_INTERPOLATE";
+        default:                        return "unrecognized";
+    }
+}
+
+const char *debug_d3ddegree(WINED3DDEGREETYPE degree) {
+    switch(degree) {
+        case WINED3DDEGREE_LINEAR:      return "WINED3DDEGREE_LINEAR";
+        case WINED3DDEGREE_QUADRATIC:   return "WINED3DDEGREE_QUADRATIC";
+        case WINED3DDEGREE_CUBIC:       return "WINED3DDEGREE_CUBIC";
+        case WINED3DDEGREE_QUINTIC:     return "WINED3DDEGREE_QUINTIC";
+        default:                        return "unrecognized";
+    }
+}
+
 /*****************************************************************************
  * Useful functions mapping GL <-> D3D values
  */
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 558773ccf983f807307c541ea845403374efb7f8..86cad0b1d00c5d79e0be9c5a89e4a60e19b3a564 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -582,6 +582,22 @@ struct WineD3DAdapter
 
 extern BOOL InitAdapters(void);
 
+/*****************************************************************************
+ * High order patch management
+ */
+struct WineD3DRectPatch
+{
+    UINT                            Handle;
+    float                          *mem;
+    WineDirect3DVertexStridedData   strided;
+    WINED3DRECTPATCH_INFO           RectPatchInfo;
+    float                           numSegs[4];
+    char                            has_normals, has_texcoords;
+    struct list                     entry;
+};
+
+HRESULT tesselate_rectpatch(IWineD3DDeviceImpl *This, struct WineD3DRectPatch *patch);
+
 /*****************************************************************************
  * IWineD3D implementation structure
  */
@@ -741,6 +757,12 @@ struct IWineD3DDeviceImpl
     UINT                    numContexts;
     WineD3DContext          *pbufferContext;             /* The context that has a pbuffer as drawable */
     DWORD                   pbufferWidth, pbufferHeight; /* Size of the buffer drawable */
+
+    /* High level patch management */
+#define PATCHMAP_SIZE 43
+#define PATCHMAP_HASHFUNC(x) ((x) % PATCHMAP_SIZE) /* Primitive and simple function */
+    struct list             patches[PATCHMAP_SIZE];
+    struct WineD3DRectPatch *currentPatch;
 };
 
 extern const IWineD3DDeviceVtbl IWineD3DDevice_Vtbl;
@@ -1271,10 +1293,10 @@ struct IWineD3DStateBlockImpl
     /* Stream Source */
     BOOL                      streamIsUP;
     UINT                      streamStride[MAX_STREAMS];
-    UINT                      streamOffset[MAX_STREAMS];
+    UINT                      streamOffset[MAX_STREAMS + 1 /* tesselated pseudo-stream */ ];
     IWineD3DVertexBuffer     *streamSource[MAX_STREAMS];
-    UINT                      streamFreq[MAX_STREAMS];
-    UINT                      streamFlags[MAX_STREAMS];     /*0 | WINED3DSTREAMSOURCE_INSTANCEDATA | WINED3DSTREAMSOURCE_INDEXEDDATA  */
+    UINT                      streamFreq[MAX_STREAMS + 1];
+    UINT                      streamFlags[MAX_STREAMS + 1];     /*0 | WINED3DSTREAMSOURCE_INSTANCEDATA | WINED3DSTREAMSOURCE_INDEXEDDATA  */
 
     /* Indices */
     IWineD3DIndexBuffer*      pIndexData;
@@ -1436,6 +1458,8 @@ const char* debug_d3dtstype(WINED3DTRANSFORMSTATETYPE tstype);
 const char* debug_d3dpool(WINED3DPOOL pool);
 const char *debug_fbostatus(GLenum status);
 const char *debug_glerror(GLenum error);
+const char *debug_d3dbasis(WINED3DBASISTYPE basis);
+const char *debug_d3ddegree(WINED3DDEGREETYPE order);
 
 /* Routines for GL <-> D3D values */
 GLenum StencilOp(DWORD op);