Hi all, With this patch, I hereby declare System Shock 2 working perfectly (modulo the movies :-/ ). It will work only in 16 bit mode for now (as I need to add the 32bpp support in the Locking code). Changelog: - support for device surface locking -- Lionel Ulmer - http://www.bbrox.org/
--- dlls/ddraw_CVS/d3d_private.h Fri May 16 09:51:47 2003 +++ dlls/ddraw/d3d_private.h Sat May 17 15:29:45 2003 @@ -244,6 +244,7 @@ void (*matrices_updated)(IDirect3DDeviceImpl *This, DWORD matrices); void (*set_matrices)(IDirect3DDeviceImpl *This, DWORD matrices, D3DMATRIX *world_mat, D3DMATRIX *view_mat, D3DMATRIX *proj_mat); + void (*flush_to_framebuffer)(IDirect3DDeviceImpl *This, LPCRECT pRect); STATEBLOCK state_block; --- dlls/ddraw_CVS/d3ddevice/mesa.c Fri May 16 18:31:39 2003 +++ dlls/ddraw/d3ddevice/mesa.c Sat May 17 16:46:52 2003 @@ -105,11 +105,19 @@ } -static BOOL opengl_flip( LPVOID display, LPVOID drawable) +static BOOL opengl_flip( LPVOID dev, LPVOID drawable) { - TRACE("(%p, %ld)\n",(Display*)display,(Drawable)drawable); + IDirect3DDeviceImpl *d3d_dev = (IDirect3DDeviceImpl *) dev; + IDirect3DDeviceGLImpl *gl_d3d_dev = (IDirect3DDeviceGLImpl *) dev; + + TRACE("(%p, %ld)\n", gl_d3d_dev->display,(Drawable)drawable); ENTER_GL(); - glXSwapBuffers((Display*)display,(Drawable)drawable); + if (gl_d3d_dev->state == SURFACE_MEMORY) { + d3d_dev->flush_to_framebuffer(d3d_dev, NULL); + } + gl_d3d_dev->state = SURFACE_GL; + gl_d3d_dev->front_state = SURFACE_GL; + glXSwapBuffers(gl_d3d_dev->display, (Drawable)drawable); LEAVE_GL(); return TRUE; } @@ -331,12 +339,38 @@ TRACE("(%p/%p)->() decrementing from %lu.\n", This, iface, This->ref); if (!--(This->ref)) { int i; + IDirectDrawSurfaceImpl *surface = This->surface, *surf; + /* Release texture associated with the device */ for (i = 0; i < MAX_TEXTURES; i++) if (This->current_texture[i] != NULL) IDirectDrawSurface7_Release(ICOM_INTERFACE(This->current_texture[i], IDirectDrawSurface7)); - /* TODO: remove the 'callbacks' for Flip and Lock/Unlock */ + /* Look for the front buffer and override its surface's Flip method (if in double buffering) */ + for (surf = surface; surf != NULL; surf = surf->surface_owner) { + if ((surf->surface_desc.ddsCaps.dwCaps&(DDSCAPS_FLIP|DDSCAPS_FRONTBUFFER)) == (DDSCAPS_FLIP|DDSCAPS_FRONTBUFFER)) { + surf->aux_ctx = NULL; + surf->aux_data = NULL; + surf->aux_flip = NULL; + break; + } + } + for (surf = surface; surf != NULL; surf = surf->surface_owner) { + IDirectDrawSurfaceImpl *surf2; + for (surf2 = surf; surf2->prev_attached != NULL; surf2 = surf2->prev_attached) ; + for (; surf2 != NULL; surf2 = surf2->next_attached) { + if (((surf2->surface_desc.ddsCaps.dwCaps & (DDSCAPS_3DDEVICE)) == (DDSCAPS_3DDEVICE)) && + ((surf2->surface_desc.ddsCaps.dwCaps & (DDSCAPS_ZBUFFER)) != (DDSCAPS_ZBUFFER))) { + /* Override the Lock / Unlock function for all these surfaces */ + surf2->lock_update = surf2->lock_update_prev; + surf2->unlock_update = surf2->unlock_update_prev;; + /* And install also the blt / bltfast overrides */ + surf2->aux_blt = NULL; + surf2->aux_bltfast = NULL; + } + surf2->d3ddevice = NULL; + } + } /* And warn the D3D object that this device is no longer active... */ This->d3d->removed_device(This->d3d, This); @@ -348,6 +382,8 @@ DeleteCriticalSection(&(This->crit)); ENTER_GL(); + if (glThis->unlock_tex) + glDeleteTextures(1, &(glThis->unlock_tex)); glXDestroyContext(glThis->display, glThis->gl_context); LEAVE_GL(); HeapFree(GetProcessHeap(), 0, This->clipping_planes); @@ -1060,6 +1096,14 @@ IDirect3DDeviceGLImpl* glThis = (IDirect3DDeviceGLImpl*) This; int num_active_stages = 0; + ENTER_GL(); + if (glThis->state == SURFACE_MEMORY) { + This->flush_to_framebuffer(This, NULL); + } + LEAVE_GL(); + + glThis->state = SURFACE_GL; + /* Compute the number of active texture stages */ while (This->current_texture[num_active_stages] != NULL) num_active_stages++; @@ -2514,39 +2558,59 @@ */ static void d3ddevice_lock_update(IDirectDrawSurfaceImpl* This, LPCRECT pRect, DWORD dwFlags) { - /* Try to acquire the device critical section */ - EnterCriticalSection(&(This->d3ddevice->crit)); + IDirect3DDeviceImpl *d3d_dev = This->d3ddevice; + IDirect3DDeviceGLImpl* gl_d3d_dev = (IDirect3DDeviceGLImpl*) d3d_dev; + BOOLEAN is_front; + + if ((This->surface_desc.ddsCaps.dwCaps & (DDSCAPS_FRONTBUFFER|DDSCAPS_PRIMARYSURFACE)) != 0) { + is_front = TRUE; + } else if ((This->surface_desc.ddsCaps.dwCaps & (DDSCAPS_BACKBUFFER)) == (DDSCAPS_BACKBUFFER)) { + is_front = FALSE; + } else { + ERR("Wrong surface type for locking !\n"); + return; + } - /* Then check if we need to do anything */ - if ((This->lastlocktype & DDLOCK_WRITEONLY) == 0) { + /* Try to acquire the device critical section */ + EnterCriticalSection(&(d3d_dev->crit)); + + if (((is_front == TRUE) && (gl_d3d_dev->front_state != SURFACE_MEMORY)) || + ((is_front == FALSE) && (gl_d3d_dev->state != SURFACE_MEMORY))) { + /* If the surface is already in memory, no need to do anything here... */ GLenum buffer_type; GLenum prev_read; RECT loc_rect; + int y; + char *dst; - WARN(" application does a lock on a 3D surface - expect slow downs.\n"); + TRACE(" copying frame buffer to main memory.\n"); + + /* Note that here we cannot do 'optmizations' about the WriteOnly flag... Indeed, a game + may only write to the device... But when we will blit it back to the screen, we need + also to blit correctly the parts the application did not overwrite... */ ENTER_GL(); - + glGetIntegerv(GL_READ_BUFFER, &prev_read); glFlush(); - if ((This->surface_desc.ddsCaps.dwCaps & (DDSCAPS_FRONTBUFFER|DDSCAPS_PRIMARYSURFACE)) != 0) { + if (is_front == TRUE) /* Application wants to lock the front buffer */ glReadBuffer(GL_FRONT); - } else if ((This->surface_desc.ddsCaps.dwCaps & (DDSCAPS_BACKBUFFER)) == (DDSCAPS_BACKBUFFER)) { + else /* Application wants to lock the back buffer */ glReadBuffer(GL_BACK); - } else { - WARN(" do not support 3D surface locking for this surface type - trying to use default buffer.\n"); - } if (This->surface_desc.u4.ddpfPixelFormat.u1.dwRGBBitCount == 16) { buffer_type = GL_UNSIGNED_SHORT_5_6_5; } else { - WARN(" unsupported pixel format.\n"); + ERR(" unsupported pixel format at device locking.\n"); LEAVE_GL(); return; } + + /* Just a hack while waiting for proper rectangle support */ + pRect = NULL; if (pRect == NULL) { loc_rect.top = 0; loc_rect.left = 0; @@ -2555,62 +2619,161 @@ } else { loc_rect = *pRect; } -#if 0 - glReadPixels(loc_rect.left, loc_rect.top, loc_rect.right, loc_rect.bottom, - GL_RGB, buffer_type, ((char *)This->surface_desc.lpSurface - + loc_rect.top * This->surface_desc.u1.lPitch - + loc_rect.left * GET_BPP(This->surface_desc))); -#endif + + dst = ((char *)This->surface_desc.lpSurface) + + (loc_rect.top * This->surface_desc.u1.lPitch) + (loc_rect.left * GET_BPP(This->surface_desc)); + for (y = (This->surface_desc.dwHeight - loc_rect.top - 1); + y >= ((int) This->surface_desc.dwHeight - (int) loc_rect.bottom); + y--) { + glReadPixels(loc_rect.left, y, + loc_rect.right - loc_rect.left, 1, + GL_RGB, buffer_type, dst); + dst += This->surface_desc.u1.lPitch; + } + glReadBuffer(prev_read); + + if (is_front) + gl_d3d_dev->front_state = SURFACE_MEMORY; + else + gl_d3d_dev->state = SURFACE_MEMORY; + LEAVE_GL(); } } -static void d3ddevice_unlock_update(IDirectDrawSurfaceImpl* This, LPCRECT pRect) -{ - /* First, check if we need to do anything */ - if ((This->lastlocktype & DDLOCK_READONLY) == 0) { - GLenum buffer_type; - GLenum prev_draw; +#define UNLOCK_TEX_SIZE 256 - WARN(" application does an unlock on a 3D surface - expect slow downs.\n"); +static void d3ddevice_flush_to_frame_buffer(IDirect3DDeviceImpl *d3d_dev, LPCRECT pRect) { + GLenum buffer_type; + RECT loc_rect; + IDirectDrawSurfaceImpl *surf = d3d_dev->surface; + IDirect3DDeviceGLImpl* gl_d3d_dev = (IDirect3DDeviceGLImpl*) d3d_dev; + GLint depth_test, alpha_test, cull_face, lighting, min_tex, max_tex, tex_env; + GLuint initial_texture; + GLint tex_state; + int x, y; - ENTER_GL(); + loc_rect.top = 0; + loc_rect.left = 0; + loc_rect.bottom = surf->surface_desc.dwHeight; + loc_rect.right = surf->surface_desc.dwWidth; - glGetIntegerv(GL_DRAW_BUFFER, &prev_draw); + TRACE(" flushing memory back to the frame-buffer.\n"); + + glGetIntegerv(GL_DEPTH_TEST, &depth_test); + glGetIntegerv(GL_ALPHA_TEST, &alpha_test); + glGetIntegerv(GL_CULL_FACE, &cull_face); + glGetIntegerv(GL_LIGHTING, &lighting); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &initial_texture); + glGetIntegerv(GL_TEXTURE_2D, &tex_state); + glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &max_tex); + glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &min_tex); + glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &tex_env); + /* TODO: scissor test if ever we use it ! */ + + if (surf->surface_desc.u4.ddpfPixelFormat.u1.dwRGBBitCount == 16) { + buffer_type = GL_UNSIGNED_SHORT_5_6_5; + } else { + WARN(" unsupported pixel format.\n"); + return; + } - if ((This->surface_desc.ddsCaps.dwCaps & (DDSCAPS_FRONTBUFFER|DDSCAPS_PRIMARYSURFACE)) != 0) { - /* Application wants to lock the front buffer */ - glDrawBuffer(GL_FRONT); - } else if ((This->surface_desc.ddsCaps.dwCaps & (DDSCAPS_BACKBUFFER)) == (DDSCAPS_BACKBUFFER)) { - /* Application wants to lock the back buffer */ - glDrawBuffer(GL_BACK); - } else { - WARN(" do not support 3D surface unlocking for this surface type - trying to use default buffer.\n"); + gl_d3d_dev->transform_state = GL_TRANSFORM_ORTHO; + d3ddevice_set_ortho(d3d_dev); + + glDisable(GL_DEPTH_TEST); + glEnable(GL_TEXTURE_2D); + glEnable(GL_SCISSOR_TEST); + glScissor(loc_rect.left, surf->surface_desc.dwHeight - loc_rect.bottom, + loc_rect.right - loc_rect.left, loc_rect.bottom - loc_rect.top); + + if (gl_d3d_dev->unlock_tex == 0) { + glGenTextures(1, &gl_d3d_dev->unlock_tex); + glBindTexture(GL_TEXTURE_2D, gl_d3d_dev->unlock_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, + UNLOCK_TEX_SIZE, UNLOCK_TEX_SIZE, 0, + GL_RGB, buffer_type, NULL); + } else { + glBindTexture(GL_TEXTURE_2D, gl_d3d_dev->unlock_tex); + } + glPixelStorei(GL_UNPACK_ROW_LENGTH, surf->surface_desc.dwWidth); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glDisable(GL_LIGHTING); + glDisable(GL_CULL_FACE); + glDisable(GL_ALPHA_TEST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + for (x = loc_rect.left; x < loc_rect.right; x += UNLOCK_TEX_SIZE) { + for (y = loc_rect.top; y < loc_rect.bottom; y += UNLOCK_TEX_SIZE) { + /* First, upload the texture... */ + int w = (x + UNLOCK_TEX_SIZE > surf->surface_desc.dwWidth) ? (surf->surface_desc.dwWidth - x) : UNLOCK_TEX_SIZE; + int h = (y + UNLOCK_TEX_SIZE > surf->surface_desc.dwHeight) ? (surf->surface_desc.dwHeight - y) : UNLOCK_TEX_SIZE; + glTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, 0, + w, h, + GL_RGB, + buffer_type, + ((char *) surf->surface_desc.lpSurface) + (x * GET_BPP(surf->surface_desc)) + (y * surf->surface_desc.u1.lPitch)); + glBegin(GL_QUADS); + glColor3ub(0xFF, 0xFF, 0xFF); + glTexCoord2f(0.0, 0.0); + glVertex3d(x, y, 0.5); + glTexCoord2f(1.0, 0.0); + glVertex3d(x + UNLOCK_TEX_SIZE, y, 0.5); + glTexCoord2f(1.0, 1.0); + glVertex3d(x + UNLOCK_TEX_SIZE, y + UNLOCK_TEX_SIZE, 0.5); + glTexCoord2f(0.0, 1.0); + glVertex3d(x, y + UNLOCK_TEX_SIZE, 0.5); + glEnd(); } + } + + + /* And restore all the various states modified by this code */ + if (depth_test != 0) glEnable(GL_DEPTH_TEST); + if (lighting != 0) glEnable(GL_LIGHTING); + if (alpha_test != 0) glEnable(GL_ALPHA_TEST); + if (cull_face != 0) glEnable(GL_CULL_FACE); + glBindTexture(GL_TEXTURE_2D, initial_texture); + if (tex_state == 0) glDisable(GL_TEXTURE_2D); + glDisable(GL_SCISSOR_TEST); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, max_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_tex); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, tex_env); +} - if (This->surface_desc.u4.ddpfPixelFormat.u1.dwRGBBitCount == 16) { - buffer_type = GL_UNSIGNED_SHORT_5_6_5; - } else { - WARN(" unsupported pixel format.\n"); - LEAVE_GL(); - - /* And 'frees' the device critical section */ - LeaveCriticalSection(&(This->d3ddevice->crit)); - return; - } - glRasterPos2f(0.0, 0.0); -#if 0 - glDrawPixels(This->surface_desc.dwWidth, This->surface_desc.dwHeight, - GL_RGB, buffer_type, This->surface_desc.lpSurface); -#endif +static void d3ddevice_unlock_update(IDirectDrawSurfaceImpl* This, LPCRECT pRect) +{ + BOOLEAN is_front; + IDirect3DDeviceImpl *d3d_dev = This->d3ddevice; + + if ((This->surface_desc.ddsCaps.dwCaps & (DDSCAPS_FRONTBUFFER|DDSCAPS_PRIMARYSURFACE)) != 0) { + is_front = TRUE; + } else if ((This->surface_desc.ddsCaps.dwCaps & (DDSCAPS_BACKBUFFER)) == (DDSCAPS_BACKBUFFER)) { + is_front = FALSE; + } else { + ERR("Wrong surface type for locking !\n"); + return; + } + /* First, check if we need to do anything. For the backbuffer, flushing is done at the next 3D activity. */ + if (((This->lastlocktype & DDLOCK_READONLY) == 0) && + (is_front == TRUE)) { + GLenum prev_draw; + ENTER_GL(); + glGetIntegerv(GL_DRAW_BUFFER, &prev_draw); + glDrawBuffer(GL_FRONT); + d3d_dev->flush_to_framebuffer(d3d_dev, pRect); glDrawBuffer(prev_draw); - LEAVE_GL(); - } + } /* And 'frees' the device critical section */ - LeaveCriticalSection(&(This->d3ddevice->crit)); + LeaveCriticalSection(&(d3d_dev->crit)); } static void @@ -2655,7 +2818,8 @@ object->clear = d3ddevice_clear; object->set_matrices = d3ddevice_set_matrices; object->matrices_updated = d3ddevice_matrices_updated; - + object->flush_to_framebuffer = d3ddevice_flush_to_frame_buffer; + InitializeCriticalSection(&(object->crit)); TRACE(" creating OpenGL device for surface = %p, d3d = %p\n", surface, d3d); @@ -2692,7 +2856,7 @@ /* Look for the front buffer and override its surface's Flip method (if in double buffering) */ for (surf = surface; surf != NULL; surf = surf->surface_owner) { if ((surf->surface_desc.ddsCaps.dwCaps&(DDSCAPS_FLIP|DDSCAPS_FRONTBUFFER)) == (DDSCAPS_FLIP|DDSCAPS_FRONTBUFFER)) { - surf->aux_ctx = (LPVOID) gl_object->display; + surf->aux_ctx = (LPVOID) object; surf->aux_data = (LPVOID) gl_object->drawable; surf->aux_flip = opengl_flip; buffer = GL_BACK; @@ -2713,7 +2877,9 @@ if (((surf2->surface_desc.ddsCaps.dwCaps & (DDSCAPS_3DDEVICE)) == (DDSCAPS_3DDEVICE)) && ((surf2->surface_desc.ddsCaps.dwCaps & (DDSCAPS_ZBUFFER)) != (DDSCAPS_ZBUFFER))) { /* Override the Lock / Unlock function for all these surfaces */ + surf2->lock_update_prev = surf2->lock_update; surf2->lock_update = d3ddevice_lock_update; + surf2->unlock_update_prev = surf2->unlock_update; surf2->unlock_update = d3ddevice_unlock_update; /* And install also the blt / bltfast overrides */ surf2->aux_blt = d3ddevice_blt; @@ -2769,6 +2935,8 @@ /* glDisable(GL_DEPTH_TEST); Need here to check for the presence of a ZBuffer and to reenable it when the ZBuffer is attached */ LEAVE_GL(); + gl_object->state = SURFACE_GL; + /* fill_device_capabilities(d3d->ddraw); */ ICOM_INIT_INTERFACE(object, IDirect3DDevice, VTABLE_IDirect3DDevice); --- dlls/ddraw_CVS/d3dexecutebuffer.c Fri May 16 09:51:47 2003 +++ dlls/ddraw/d3dexecutebuffer.c Sat May 17 16:34:08 2003 @@ -206,7 +206,12 @@ _dump_executedata(&(This->data)); ENTER_GL(); - + + if (((IDirect3DDeviceGLImpl *) lpDevice)->state == SURFACE_MEMORY) { + lpDevice->flush_to_framebuffer(lpDevice, NULL); + } + ((IDirect3DDeviceGLImpl *) lpDevice)->state = SURFACE_GL; + while (1) { LPD3DINSTRUCTION current = (LPD3DINSTRUCTION) instr; BYTE size; --- dlls/ddraw_CVS/ddraw_private.h Sun Jan 5 23:58:59 2003 +++ dlls/ddraw/ddraw_private.h Sat May 17 16:45:11 2003 @@ -303,6 +303,8 @@ IDirectDrawSurfaceImpl *mip_main; int mipmap_level; LPVOID tex_private; + void (*lock_update_prev)(IDirectDrawSurfaceImpl* This, LPCRECT pRect, DWORD dwFlags); + void (*unlock_update_prev)(IDirectDrawSurfaceImpl* This, LPCRECT pRect); }; /***************************************************************************** --- dlls/ddraw_CVS/mesa_private.h Fri May 16 09:51:47 2003 +++ dlls/ddraw/mesa_private.h Sat May 17 16:32:01 2003 @@ -95,6 +95,11 @@ GL_TRANSFORM_VERTEXBUFFER } GL_TRANSFORM_STATE; +typedef enum { + SURFACE_GL, + SURFACE_MEMORY +} SURFACE_STATE; + typedef struct IDirect3DDeviceGLImpl { struct IDirect3DDeviceImpl parent; @@ -111,6 +116,9 @@ Display *display; Drawable drawable; + + GLuint unlock_tex; + SURFACE_STATE state, front_state; } IDirect3DDeviceGLImpl; /* This is for the OpenGL additions... */