[D3D] Support for surface locking
Lionel Ulmer
lionel.ulmer at free.fr
Sat May 17 09:56:00 CDT 2003
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/
-------------- next part --------------
--- 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... */
More information about the wine-patches
mailing list