wined3d: Take care for client storage and pbos.
[wine/multimedia.git] / dlls / wined3d / surface.c
blob7b25d18d21672b1de82f8007fe6c364be4a21998
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2007 Stefan Dösinger for CodeWeavers
11 * Copyright 2007 Henri Verbeet
12 * Copyright 2006-2007 Roderick Colenbrander
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
36 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
37 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey);
39 static void surface_download_data(IWineD3DSurfaceImpl *This) {
40 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
42 if (0 == This->glDescription.textureName) {
43 ERR("Surface does not have a texture, but SFLAG_INTEXTURE is set\n");
44 return;
47 if(myDevice->createParms.BehaviorFlags & WINED3DCREATE_MULTITHREADED) {
48 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
51 ENTER_GL();
52 /* Make sure that a proper texture unit is selected, bind the texture
53 * and dirtify the sampler to restore the texture on the next draw
55 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
56 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
57 checkGLcall("glActiveTextureARB");
59 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
60 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
62 if (This->resource.format == WINED3DFMT_DXT1 ||
63 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
64 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
65 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
66 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
67 } else {
68 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
69 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
71 if(This->Flags & SFLAG_PBO) {
72 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
73 checkGLcall("glBindBufferARB");
74 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
75 checkGLcall("glGetCompressedTexImageARB()");
76 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
77 checkGLcall("glBindBufferARB");
78 } else {
79 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
80 checkGLcall("glGetCompressedTexImageARB()");
83 LEAVE_GL();
84 } else {
85 void *mem;
86 int src_pitch = 0;
87 int dst_pitch = 0;
89 if(This->Flags & SFLAG_CONVERTED) {
90 FIXME("Read back converted textures unsupported\n");
91 LEAVE_GL();
92 return;
95 if (This->Flags & SFLAG_NONPOW2) {
96 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
97 src_pitch = This->bytesPerPixel * This->pow2Width;
98 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
99 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
100 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
101 } else {
102 mem = This->resource.allocatedMemory;
105 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
106 This->glDescription.glFormat, This->glDescription.glType, mem);
108 if(This->Flags & SFLAG_PBO) {
109 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
110 checkGLcall("glBindBufferARB");
112 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
113 This->glDescription.glType, NULL);
114 checkGLcall("glGetTexImage()");
116 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
117 checkGLcall("glBindBufferARB");
118 } else {
119 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
120 This->glDescription.glType, mem);
121 checkGLcall("glGetTexImage()");
123 LEAVE_GL();
125 if (This->Flags & SFLAG_NONPOW2) {
126 LPBYTE src_data, dst_data;
127 int y;
129 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
130 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
131 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
133 * We're doing this...
135 * instead of boxing the texture :
136 * |<-texture width ->| -->pow2width| /\
137 * |111111111111111111| | |
138 * |222 Texture 222222| boxed empty | texture height
139 * |3333 Data 33333333| | |
140 * |444444444444444444| | \/
141 * ----------------------------------- |
142 * | boxed empty | boxed empty | pow2height
143 * | | | \/
144 * -----------------------------------
147 * we're repacking the data to the expected texture width
149 * |<-texture width ->| -->pow2width| /\
150 * |111111111111111111222222222222222| |
151 * |222333333333333333333444444444444| texture height
152 * |444444 | |
153 * | | \/
154 * | | |
155 * | empty | pow2height
156 * | | \/
157 * -----------------------------------
159 * == is the same as
161 * |<-texture width ->| /\
162 * |111111111111111111|
163 * |222222222222222222|texture height
164 * |333333333333333333|
165 * |444444444444444444| \/
166 * --------------------
168 * this also means that any references to allocatedMemory should work with the data as if were a
169 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
171 * internally the texture is still stored in a boxed format so any references to textureName will
172 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
174 * Performance should not be an issue, because applications normally do not lock the surfaces when
175 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
176 * and doesn't have to be re-read.
178 src_data = mem;
179 dst_data = This->resource.allocatedMemory;
180 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
181 for (y = 1 ; y < This->currentDesc.Height; y++) {
182 /* skip the first row */
183 src_data += src_pitch;
184 dst_data += dst_pitch;
185 memcpy(dst_data, src_data, dst_pitch);
188 HeapFree(GetProcessHeap(), 0, mem);
192 /* Surface has now been downloaded */
193 This->Flags |= SFLAG_INSYSMEM;
196 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
197 if (This->resource.format == WINED3DFMT_DXT1 ||
198 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
199 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
200 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
201 FIXME("Using DXT1/3/5 without advertized support\n");
202 } else {
203 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
204 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
205 * function uses glCompressedTexImage2D instead of the SubImage call
207 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
208 ENTER_GL();
210 if(This->Flags & SFLAG_PBO) {
211 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
212 checkGLcall("glBindBufferARB");
213 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
215 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
216 width, height, 0 /* border */, This->resource.size, NULL));
217 checkGLcall("glCompressedTexSubImage2D");
219 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
220 checkGLcall("glBindBufferARB");
221 } else {
222 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
223 width, height, 0 /* border */, This->resource.size, data));
224 checkGLcall("glCompressedTexSubImage2D");
226 LEAVE_GL();
228 } else {
229 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
230 ENTER_GL();
232 if(This->Flags & SFLAG_PBO) {
233 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
234 checkGLcall("glBindBufferARB");
235 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
237 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
238 checkGLcall("glTexSubImage2D");
240 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
241 checkGLcall("glBindBufferARB");
243 else {
244 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
245 checkGLcall("glTexSubImage2D");
248 LEAVE_GL();
252 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
253 BOOL enable_client_storage = FALSE;
254 BYTE *mem = NULL;
256 TRACE("(%p) : Creating surface (target %#x) level %d, d3d format %s, internal format %#x, width %d, height %d, gl format %#x, gl type=%#x\n", This,
257 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
259 if (This->resource.format == WINED3DFMT_DXT1 ||
260 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
261 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
262 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
263 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
265 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
266 * once, unfortunately
268 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
269 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
270 This->Flags |= SFLAG_CLIENT;
271 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
272 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
273 width, height, 0 /* border */, This->resource.size, mem));
276 return;
279 ENTER_GL();
281 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
282 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
283 /* In some cases we want to disable client storage.
284 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
285 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
286 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
287 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
288 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
290 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
291 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
292 This->Flags &= ~SFLAG_CLIENT;
293 enable_client_storage = TRUE;
294 } else {
295 This->Flags |= SFLAG_CLIENT;
297 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
298 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
300 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
303 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
304 checkGLcall("glTexImage2D");
306 if(enable_client_storage) {
307 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
308 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
310 LEAVE_GL();
312 This->Flags |= SFLAG_ALLOCATED;
315 /* In D3D the depth stencil dimensions have to be greater than or equal to the
316 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
317 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
318 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
319 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
320 renderbuffer_entry_t *entry;
321 GLuint renderbuffer = 0;
322 unsigned int src_width, src_height;
324 src_width = This->pow2Width;
325 src_height = This->pow2Height;
327 /* A depth stencil smaller than the render target is not valid */
328 if (width > src_width || height > src_height) return;
330 /* Remove any renderbuffer set if the sizes match */
331 if (width == src_width && height == src_height) {
332 This->current_renderbuffer = NULL;
333 return;
336 /* Look if we've already got a renderbuffer of the correct dimensions */
337 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
338 if (entry->width == width && entry->height == height) {
339 renderbuffer = entry->id;
340 This->current_renderbuffer = entry;
341 break;
345 if (!renderbuffer) {
346 const GlPixelFormatDesc *glDesc;
347 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
349 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
350 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
351 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
353 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
354 entry->width = width;
355 entry->height = height;
356 entry->id = renderbuffer;
357 list_add_head(&This->renderbuffers, &entry->entry);
359 This->current_renderbuffer = entry;
362 checkGLcall("set_compatible_renderbuffer");
365 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
366 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
367 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
369 TRACE("(%p) : swapchain %p\n", This, swapchain);
371 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
372 TRACE("Returning GL_BACK\n");
373 return GL_BACK;
374 } else if (swapchain_impl->frontBuffer == iface) {
375 TRACE("Returning GL_FRONT\n");
376 return GL_FRONT;
379 FIXME("Higher back buffer, returning GL_BACK\n");
380 return GL_BACK;
383 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
384 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
385 ULONG ref = InterlockedDecrement(&This->resource.ref);
386 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
387 if (ref == 0) {
388 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
389 renderbuffer_entry_t *entry, *entry2;
390 TRACE("(%p) : cleaning up\n", This);
392 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
394 /* Need a context to destroy the texture. Use the currently active render target, but only if
395 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
396 * When destroying the primary rt, Uninit3D will activate a context before doing anything
398 if(device->render_targets && device->render_targets[0]) {
399 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
402 TRACE("Deleting texture %d\n", This->glDescription.textureName);
403 ENTER_GL();
404 glDeleteTextures(1, &This->glDescription.textureName);
405 LEAVE_GL();
408 if(This->Flags & SFLAG_PBO) {
409 /* Delete the PBO */
410 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
413 if(This->Flags & SFLAG_DIBSECTION) {
414 /* Release the DC */
415 SelectObject(This->hDC, This->dib.holdbitmap);
416 DeleteDC(This->hDC);
417 /* Release the DIB section */
418 DeleteObject(This->dib.DIBsection);
419 This->dib.bitmap_data = NULL;
420 This->resource.allocatedMemory = NULL;
422 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
424 HeapFree(GetProcessHeap(), 0, This->palette9);
426 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
427 if(iface == device->ddraw_primary)
428 device->ddraw_primary = NULL;
430 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
431 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
432 HeapFree(GetProcessHeap(), 0, entry);
435 TRACE("(%p) Released\n", This);
436 HeapFree(GetProcessHeap(), 0, This);
439 return ref;
442 /* ****************************************************
443 IWineD3DSurface IWineD3DResource parts follow
444 **************************************************** */
446 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
447 /* TODO: check for locks */
448 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
449 IWineD3DBaseTexture *baseTexture = NULL;
450 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
452 TRACE("(%p)Checking to see if the container is a base texture\n", This);
453 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
454 TRACE("Passing to container\n");
455 IWineD3DBaseTexture_PreLoad(baseTexture);
456 IWineD3DBaseTexture_Release(baseTexture);
457 } else {
458 TRACE("(%p) : About to load surface\n", This);
460 if(!device->isInDraw) {
461 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
464 ENTER_GL();
465 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
466 if (!This->glDescription.level) {
467 if (!This->glDescription.textureName) {
468 glGenTextures(1, &This->glDescription.textureName);
469 checkGLcall("glGenTextures");
470 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
472 glBindTexture(This->glDescription.target, This->glDescription.textureName);
473 checkGLcall("glBindTexture");
474 IWineD3DSurface_LoadTexture(iface, FALSE);
475 /* This is where we should be reducing the amount of GLMemoryUsed */
476 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
477 /* assume this is a coding error not a real error for now */
478 FIXME("Mipmap surface has a glTexture bound to it!\n");
480 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
481 /* Tell opengl to try and keep this texture in video ram (well mostly) */
482 GLclampf tmp;
483 tmp = 0.9f;
484 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
486 LEAVE_GL();
488 return;
491 /* ******************************************************
492 IWineD3DSurface IWineD3DSurface parts follow
493 ****************************************************** */
495 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
496 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
497 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
498 if (This->glDescription.textureName == 0 && textureName != 0) {
499 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
500 IWineD3DSurface_AddDirtyRect(iface, NULL);
502 This->glDescription.textureName = textureName;
503 This->glDescription.target = target;
504 This->Flags &= ~SFLAG_ALLOCATED;
507 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
508 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
509 TRACE("(%p) : returning %p\n", This, &This->glDescription);
510 *glDescription = &This->glDescription;
513 /* TODO: think about moving this down to resource? */
514 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
515 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
516 /* This should only be called for sysmem textures, it may be a good idea to extend this to all pools at some point in the future */
517 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
518 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
520 return (CONST void*)(This->resource.allocatedMemory);
523 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
524 IWineD3DSwapChainImpl *swapchain;
525 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
526 BYTE *mem;
527 GLint fmt;
528 GLint type;
529 BYTE *row, *top, *bottom;
530 int i;
531 BOOL bpp;
532 RECT local_rect;
533 BOOL srcIsUpsideDown;
535 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
536 static BOOL warned = FALSE;
537 if(!warned) {
538 ERR("The application tries to lock the render target, but render target locking is disabled\n");
539 warned = TRUE;
541 return;
544 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
545 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
546 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
547 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
548 * context->last_was_blit set on the unlock.
550 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
551 ENTER_GL();
553 /* Select the correct read buffer, and give some debug output.
554 * There is no need to keep track of the current read buffer or reset it, every part of the code
555 * that reads sets the read buffer as desired.
557 if(!swapchain) {
558 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
559 * Read from the back buffer
561 TRACE("Locking offscreen render target\n");
562 glReadBuffer(myDevice->offscreenBuffer);
563 srcIsUpsideDown = TRUE;
564 } else {
565 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
566 TRACE("Locking %#x buffer\n", buffer);
567 glReadBuffer(buffer);
568 checkGLcall("glReadBuffer");
570 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
571 srcIsUpsideDown = FALSE;
574 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
575 if(!rect) {
576 local_rect.left = 0;
577 local_rect.top = 0;
578 local_rect.right = This->currentDesc.Width;
579 local_rect.bottom = This->currentDesc.Height;
580 } else {
581 local_rect = *rect;
583 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
585 switch(This->resource.format)
587 case WINED3DFMT_P8:
589 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
590 /* In case of P8 render targets the index is stored in the alpha component */
591 fmt = GL_ALPHA;
592 type = GL_UNSIGNED_BYTE;
593 mem = dest;
594 bpp = This->bytesPerPixel;
595 } else {
596 /* GL can't return palettized data, so read ARGB pixels into a
597 * separate block of memory and convert them into palettized format
598 * in software. Slow, but if the app means to use palettized render
599 * targets and locks it...
601 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
602 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
603 * for the color channels when palettizing the colors.
605 fmt = GL_RGB;
606 type = GL_UNSIGNED_BYTE;
607 pitch *= 3;
608 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
609 if(!mem) {
610 ERR("Out of memory\n");
611 LEAVE_GL();
612 return;
614 bpp = This->bytesPerPixel * 3;
617 break;
619 default:
620 mem = dest;
621 fmt = This->glDescription.glFormat;
622 type = This->glDescription.glType;
623 bpp = This->bytesPerPixel;
626 if(This->Flags & SFLAG_PBO) {
627 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
628 checkGLcall("glBindBufferARB");
631 glReadPixels(local_rect.left, local_rect.top,
632 local_rect.right - local_rect.left,
633 local_rect.bottom - local_rect.top,
634 fmt, type, mem);
635 vcheckGLcall("glReadPixels");
637 if(This->Flags & SFLAG_PBO) {
638 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
639 checkGLcall("glBindBufferARB");
641 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
642 * to get a pointer to it and perform the flipping in software. This is a lot
643 * faster than calling glReadPixels for each line. In case we want more speed
644 * we should rerender it flipped in a FBO and read the data back from the FBO. */
645 if(!srcIsUpsideDown) {
646 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
647 checkGLcall("glBindBufferARB");
649 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
650 checkGLcall("glMapBufferARB");
654 /* TODO: Merge this with the palettization loop below for P8 targets */
655 if(!srcIsUpsideDown) {
656 UINT len, off;
657 /* glReadPixels returns the image upside down, and there is no way to prevent this.
658 Flip the lines in software */
659 len = (local_rect.right - local_rect.left) * bpp;
660 off = local_rect.left * bpp;
662 row = HeapAlloc(GetProcessHeap(), 0, len);
663 if(!row) {
664 ERR("Out of memory\n");
665 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
666 LEAVE_GL();
667 return;
670 top = mem + pitch * local_rect.top;
671 bottom = ((BYTE *) mem) + pitch * ( local_rect.bottom - local_rect.top - 1);
672 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
673 memcpy(row, top + off, len);
674 memcpy(top + off, bottom + off, len);
675 memcpy(bottom + off, row, len);
676 top += pitch;
677 bottom -= pitch;
679 HeapFree(GetProcessHeap(), 0, row);
681 /* Unmap the temp PBO buffer */
682 if(This->Flags & SFLAG_PBO) {
683 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
684 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
688 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
689 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
690 * the same color but we have no choice.
691 * In case of render targets, the index is stored in the alpha component so no conversion is needed.
693 if((This->resource.format == WINED3DFMT_P8) && !(This->resource.usage & WINED3DUSAGE_RENDERTARGET)) {
694 PALETTEENTRY *pal;
695 DWORD width = pitch / 3;
696 int x, y, c;
697 if(This->palette) {
698 pal = This->palette->palents;
699 } else {
700 pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
703 for(y = local_rect.top; y < local_rect.bottom; y++) {
704 for(x = local_rect.left; x < local_rect.right; x++) {
705 /* start lines pixels */
706 BYTE *blue = (BYTE *) ((BYTE *) mem) + y * pitch + x * (sizeof(BYTE) * 3);
707 BYTE *green = blue + 1;
708 BYTE *red = green + 1;
710 for(c = 0; c < 256; c++) {
711 if(*red == pal[c].peRed &&
712 *green == pal[c].peGreen &&
713 *blue == pal[c].peBlue)
715 *((BYTE *) dest + y * width + x) = c;
716 break;
721 HeapFree(GetProcessHeap(), 0, mem);
723 LEAVE_GL();
726 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
727 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
728 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
729 * changed
731 if(!(This->Flags & SFLAG_DYNLOCK)) {
732 This->lockCount++;
733 /* MAXLOCKCOUNT is defined in wined3d_private.h */
734 if(This->lockCount > MAXLOCKCOUNT) {
735 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
736 This->Flags |= SFLAG_DYNLOCK;
740 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
741 * Also don't create a PBO for systemmem surfaces.
743 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
744 GLenum error;
745 ENTER_GL();
747 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
748 error = glGetError();
749 if(This->pbo == 0 || error != GL_NO_ERROR) {
750 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
753 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
755 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
756 checkGLcall("glBindBufferARB");
758 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
759 checkGLcall("glBufferDataARB");
761 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
762 checkGLcall("glBindBufferARB");
764 /* We don't need the system memory anymore and we can't even use it for PBOs */
765 if(!(This->Flags & SFLAG_CLIENT)) {
766 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
767 This->resource.heapMemory = NULL;
769 This->resource.allocatedMemory = NULL;
770 This->Flags |= SFLAG_PBO;
771 LEAVE_GL();
772 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
773 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
774 * or a pbo to map
776 if(!This->resource.heapMemory) {
777 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
779 This->resource.allocatedMemory =
780 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
781 if(This->Flags & SFLAG_INSYSMEM) {
782 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
787 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
788 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
789 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
790 IWineD3DSwapChain *swapchain = NULL;
792 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
794 /* This is also done in the base class, but we have to verify this before loading any data from
795 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
796 * may interfere, and all other bad things may happen
798 if (This->Flags & SFLAG_LOCKED) {
799 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
800 return WINED3DERR_INVALIDCALL;
803 if (Flags & WINED3DLOCK_DISCARD) {
804 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
805 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
806 This->Flags |= SFLAG_INSYSMEM;
809 if (This->Flags & SFLAG_INSYSMEM) {
810 TRACE("Local copy is up to date, not downloading data\n");
811 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
812 goto lock_end;
815 /* Now download the surface content from opengl
816 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
817 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
819 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
820 if(swapchain || iface == myDevice->render_targets[0]) {
821 const RECT *pass_rect = pRect;
823 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
824 * because most caller functions do not need that. So do that here
826 if(pRect &&
827 pRect->top == 0 &&
828 pRect->left == 0 &&
829 pRect->right == This->currentDesc.Width &&
830 pRect->bottom == This->currentDesc.Height) {
831 pass_rect = NULL;
834 switch(wined3d_settings.rendertargetlock_mode) {
835 case RTL_TEXDRAW:
836 case RTL_TEXTEX:
837 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
838 #if 0
839 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
840 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
841 * This may be faster on some cards
843 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
844 #endif
845 /* drop through */
847 case RTL_AUTO:
848 case RTL_READDRAW:
849 case RTL_READTEX:
850 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
851 break;
853 case RTL_DISABLE:
854 break;
856 if(swapchain) IWineD3DSwapChain_Release(swapchain);
858 } else if(iface == myDevice->stencilBufferTarget) {
859 /** the depth stencil in openGL has a format of GL_FLOAT
860 * which should be good for WINED3DFMT_D16_LOCKABLE
861 * and WINED3DFMT_D16
862 * it is unclear what format the stencil buffer is in except.
863 * 'Each index is converted to fixed point...
864 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
865 * mappings in the table GL_PIXEL_MAP_S_TO_S.
866 * glReadPixels(This->lockedRect.left,
867 * This->lockedRect.bottom - j - 1,
868 * This->lockedRect.right - This->lockedRect.left,
869 * 1,
870 * GL_DEPTH_COMPONENT,
871 * type,
872 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
874 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
875 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
876 * none of that is the case the problem is not in this function :-)
877 ********************************************/
878 FIXME("Depth stencil locking not supported yet\n");
879 } else {
880 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
881 TRACE("locking an ordinary surface\n");
882 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
885 lock_end:
886 if(This->Flags & SFLAG_PBO) {
887 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
888 ENTER_GL();
889 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
890 checkGLcall("glBindBufferARB");
892 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
893 if(This->resource.allocatedMemory) {
894 ERR("The surface already has PBO memory allocated!\n");
897 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
898 checkGLcall("glMapBufferARB");
900 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
901 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
902 checkGLcall("glBindBufferARB");
904 LEAVE_GL();
907 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
908 /* Don't dirtify */
909 } else {
910 IWineD3DBaseTexture *pBaseTexture;
912 * Dirtify on lock
913 * as seen in msdn docs
915 IWineD3DSurface_AddDirtyRect(iface, pRect);
917 /** Dirtify Container if needed */
918 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
919 TRACE("Making container dirty\n");
920 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
921 IWineD3DBaseTexture_Release(pBaseTexture);
922 } else {
923 TRACE("Surface is standalone, no need to dirty the container\n");
927 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
930 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This) {
931 GLint prev_store;
932 GLint prev_rasterpos[4];
933 GLint skipBytes = 0;
934 BOOL storechanged = FALSE, memory_allocated = FALSE;
935 GLint fmt, type;
936 BYTE *mem;
937 UINT bpp;
938 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
939 IWineD3DDeviceImpl *myDevice = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
940 IWineD3DSwapChainImpl *swapchain;
942 /* Activate the correct context for the render target */
943 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
944 ENTER_GL();
946 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
947 if(!swapchain) {
948 /* Primary offscreen render target */
949 TRACE("Offscreen render target\n");
950 glDrawBuffer(myDevice->offscreenBuffer);
951 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
952 } else {
953 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
954 TRACE("Unlocking %#x buffer\n", buffer);
955 glDrawBuffer(buffer);
956 checkGLcall("glDrawBuffer");
958 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
961 glDisable(GL_TEXTURE_2D);
962 vcheckGLcall("glDisable(GL_TEXTURE_2D)");
964 glFlush();
965 vcheckGLcall("glFlush");
966 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
967 vcheckGLcall("glIntegerv");
968 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
969 vcheckGLcall("glIntegerv");
970 glPixelZoom(1.0, -1.0);
971 vcheckGLcall("glPixelZoom");
973 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
974 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
975 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
977 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
978 vcheckGLcall("glRasterPos2f");
980 /* Some drivers(radeon dri, others?) don't like exceptions during
981 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
982 * after ReleaseDC. Reading it will cause an exception, which x11drv will
983 * catch to put the dib section in InSync mode, which leads to a crash
984 * and a blocked x server on my radeon card.
986 * The following lines read the dib section so it is put in inSync mode
987 * before glDrawPixels is called and the crash is prevented. There won't
988 * be any interfering gdi accesses, because UnlockRect is called from
989 * ReleaseDC, and the app won't use the dc any more afterwards.
991 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
992 volatile BYTE read;
993 read = This->resource.allocatedMemory[0];
996 switch (This->resource.format) {
997 /* No special care needed */
998 case WINED3DFMT_A4R4G4B4:
999 case WINED3DFMT_R5G6B5:
1000 case WINED3DFMT_A1R5G5B5:
1001 case WINED3DFMT_R8G8B8:
1002 type = This->glDescription.glType;
1003 fmt = This->glDescription.glFormat;
1004 mem = This->resource.allocatedMemory;
1005 bpp = This->bytesPerPixel;
1006 break;
1008 case WINED3DFMT_X4R4G4B4:
1010 int size;
1011 unsigned short *data;
1012 data = (unsigned short *)This->resource.allocatedMemory;
1013 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
1014 while(size > 0) {
1015 *data |= 0xF000;
1016 data++;
1017 size--;
1019 type = This->glDescription.glType;
1020 fmt = This->glDescription.glFormat;
1021 mem = This->resource.allocatedMemory;
1022 bpp = This->bytesPerPixel;
1024 break;
1026 case WINED3DFMT_X1R5G5B5:
1028 int size;
1029 unsigned short *data;
1030 data = (unsigned short *)This->resource.allocatedMemory;
1031 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
1032 while(size > 0) {
1033 *data |= 0x8000;
1034 data++;
1035 size--;
1037 type = This->glDescription.glType;
1038 fmt = This->glDescription.glFormat;
1039 mem = This->resource.allocatedMemory;
1040 bpp = This->bytesPerPixel;
1042 break;
1044 case WINED3DFMT_X8R8G8B8:
1046 /* make sure the X byte is set to alpha on, since it
1047 could be any random value. This fixes the intro movie in Pirates! */
1048 int size;
1049 unsigned int *data;
1050 data = (unsigned int *)This->resource.allocatedMemory;
1051 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
1052 while(size > 0) {
1053 *data |= 0xFF000000;
1054 data++;
1055 size--;
1058 /* Fall through */
1060 case WINED3DFMT_A8R8G8B8:
1062 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1063 vcheckGLcall("glPixelStorei");
1064 storechanged = TRUE;
1065 type = This->glDescription.glType;
1066 fmt = This->glDescription.glFormat;
1067 mem = This->resource.allocatedMemory;
1068 bpp = This->bytesPerPixel;
1070 break;
1072 case WINED3DFMT_A2R10G10B10:
1074 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1075 vcheckGLcall("glPixelStorei");
1076 storechanged = TRUE;
1077 type = This->glDescription.glType;
1078 fmt = This->glDescription.glFormat;
1079 mem = This->resource.allocatedMemory;
1080 bpp = This->bytesPerPixel;
1082 break;
1084 case WINED3DFMT_P8:
1086 int height = This->glRect.bottom - This->glRect.top;
1087 type = GL_UNSIGNED_BYTE;
1088 fmt = GL_RGBA;
1090 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * sizeof(DWORD));
1091 if(!mem) {
1092 ERR("Out of memory\n");
1093 goto cleanup;
1095 memory_allocated = TRUE;
1096 d3dfmt_convert_surface(This->resource.allocatedMemory,
1097 mem,
1098 pitch,
1099 pitch,
1100 height,
1101 pitch * 4,
1102 CONVERT_PALETTED,
1103 This);
1104 bpp = This->bytesPerPixel * 4;
1105 pitch *= 4;
1107 break;
1109 default:
1110 FIXME("Unsupported Format %u in locking func\n", This->resource.format);
1112 /* Give it a try */
1113 type = This->glDescription.glType;
1114 fmt = This->glDescription.glFormat;
1115 mem = This->resource.allocatedMemory;
1116 bpp = This->bytesPerPixel;
1119 if(This->Flags & SFLAG_PBO) {
1120 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1121 checkGLcall("glBindBufferARB");
1124 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1125 (This->lockedRect.bottom - This->lockedRect.top)-1,
1126 fmt, type,
1127 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1128 checkGLcall("glDrawPixels");
1130 cleanup:
1131 if(This->Flags & SFLAG_PBO) {
1132 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1133 checkGLcall("glBindBufferARB");
1136 glPixelZoom(1.0,1.0);
1137 vcheckGLcall("glPixelZoom");
1139 glRasterPos3iv(&prev_rasterpos[0]);
1140 vcheckGLcall("glRasterPos3iv");
1142 /* Reset to previous pack row length */
1143 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1144 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1145 if(storechanged) {
1146 glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
1147 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
1150 /* Blitting environment requires that 2D texturing is enabled. It was turned off before,
1151 * turn it on again
1153 glEnable(GL_TEXTURE_2D);
1154 checkGLcall("glEnable(GL_TEXTURE_2D)");
1156 if(memory_allocated) HeapFree(GetProcessHeap(), 0, mem);
1158 if(!swapchain) {
1159 glDrawBuffer(myDevice->offscreenBuffer);
1160 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1161 } else if(swapchain->backBuffer) {
1162 glDrawBuffer(GL_BACK);
1163 checkGLcall("glDrawBuffer(GL_BACK)");
1164 } else {
1165 glDrawBuffer(GL_FRONT);
1166 checkGLcall("glDrawBuffer(GL_FRONT)");
1168 LEAVE_GL();
1170 return;
1173 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1174 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1175 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1176 IWineD3DSwapChainImpl *swapchain = NULL;
1177 BOOL fullsurface;
1179 if (!(This->Flags & SFLAG_LOCKED)) {
1180 WARN("trying to Unlock an unlocked surf@%p\n", This);
1181 return WINED3DERR_INVALIDCALL;
1184 if (This->Flags & SFLAG_PBO) {
1185 TRACE("Freeing PBO memory\n");
1186 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1187 ENTER_GL();
1188 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1189 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1190 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1191 checkGLcall("glUnmapBufferARB");
1192 LEAVE_GL();
1193 This->resource.allocatedMemory = NULL;
1196 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1198 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1199 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1200 goto unlock_end;
1203 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1204 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1205 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1206 static BOOL warned = FALSE;
1207 if(!warned) {
1208 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1209 warned = TRUE;
1211 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1212 goto unlock_end;
1215 if(This->dirtyRect.left == 0 &&
1216 This->dirtyRect.top == 0 &&
1217 This->dirtyRect.right == This->currentDesc.Width &&
1218 This->dirtyRect.bottom == This->currentDesc.Height) {
1219 fullsurface = TRUE;
1220 } else {
1221 /* TODO: Proper partial rectangle tracking */
1222 fullsurface = FALSE;
1223 This->Flags |= SFLAG_INSYSMEM;
1226 switch(wined3d_settings.rendertargetlock_mode) {
1227 case RTL_READTEX:
1228 case RTL_TEXTEX:
1229 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1230 ENTER_GL();
1231 if (This->glDescription.textureName == 0) {
1232 glGenTextures(1, &This->glDescription.textureName);
1233 checkGLcall("glGenTextures");
1235 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
1236 checkGLcall("glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);");
1237 LEAVE_GL();
1238 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1239 /* drop through */
1241 case RTL_AUTO:
1242 case RTL_READDRAW:
1243 case RTL_TEXDRAW:
1244 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1245 break;
1248 if(!fullsurface) {
1249 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1250 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1251 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1252 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1253 * not fully up to date because only a subrectangle was read in LockRect.
1255 This->Flags &= ~SFLAG_INSYSMEM;
1256 This->Flags |= SFLAG_INDRAWABLE;
1259 This->dirtyRect.left = This->currentDesc.Width;
1260 This->dirtyRect.top = This->currentDesc.Height;
1261 This->dirtyRect.right = 0;
1262 This->dirtyRect.bottom = 0;
1263 } else if(iface == myDevice->stencilBufferTarget) {
1264 FIXME("Depth Stencil buffer locking is not implemented\n");
1265 } else {
1266 /* The rest should be a normal texture */
1267 IWineD3DBaseTextureImpl *impl;
1268 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1269 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1270 * states need resetting
1272 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1273 if(impl->baseTexture.bindCount) {
1274 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1276 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1280 unlock_end:
1281 This->Flags &= ~SFLAG_LOCKED;
1282 memset(&This->lockedRect, 0, sizeof(RECT));
1283 return WINED3D_OK;
1286 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1287 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1288 WINED3DLOCKED_RECT lock;
1289 HRESULT hr;
1290 RGBQUAD col[256];
1292 TRACE("(%p)->(%p)\n",This,pHDC);
1294 if(This->Flags & SFLAG_USERPTR) {
1295 ERR("Not supported on surfaces with an application-provided surfaces\n");
1296 return WINEDDERR_NODC;
1299 /* Give more detailed info for ddraw */
1300 if (This->Flags & SFLAG_DCINUSE)
1301 return WINEDDERR_DCALREADYCREATED;
1303 /* Can't GetDC if the surface is locked */
1304 if (This->Flags & SFLAG_LOCKED)
1305 return WINED3DERR_INVALIDCALL;
1307 memset(&lock, 0, sizeof(lock)); /* To be sure */
1309 /* Create a DIB section if there isn't a hdc yet */
1310 if(!This->hDC) {
1311 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1312 if(This->Flags & SFLAG_CLIENT) {
1313 IWineD3DSurface_PreLoad(iface);
1316 /* Use the dib section from now on if we are not using a PBO */
1317 if(!(This->Flags & SFLAG_PBO))
1318 This->resource.allocatedMemory = This->dib.bitmap_data;
1321 /* Lock the surface */
1322 hr = IWineD3DSurface_LockRect(iface,
1323 &lock,
1324 NULL,
1327 if(This->Flags & SFLAG_PBO) {
1328 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1329 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1332 if(FAILED(hr)) {
1333 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1334 /* keep the dib section */
1335 return hr;
1338 if(This->resource.format == WINED3DFMT_P8 ||
1339 This->resource.format == WINED3DFMT_A8P8) {
1340 unsigned int n;
1341 if(This->palette) {
1342 PALETTEENTRY ent[256];
1344 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1345 for (n=0; n<256; n++) {
1346 col[n].rgbRed = ent[n].peRed;
1347 col[n].rgbGreen = ent[n].peGreen;
1348 col[n].rgbBlue = ent[n].peBlue;
1349 col[n].rgbReserved = 0;
1351 } else {
1352 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1354 for (n=0; n<256; n++) {
1355 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1356 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1357 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1358 col[n].rgbReserved = 0;
1362 SetDIBColorTable(This->hDC, 0, 256, col);
1365 *pHDC = This->hDC;
1366 TRACE("returning %p\n",*pHDC);
1367 This->Flags |= SFLAG_DCINUSE;
1369 return WINED3D_OK;
1372 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1373 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1375 TRACE("(%p)->(%p)\n",This,hDC);
1377 if (!(This->Flags & SFLAG_DCINUSE))
1378 return WINED3DERR_INVALIDCALL;
1380 if (This->hDC !=hDC) {
1381 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1382 return WINED3DERR_INVALIDCALL;
1385 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1386 /* Copy the contents of the DIB over to the PBO */
1387 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1390 /* we locked first, so unlock now */
1391 IWineD3DSurface_UnlockRect(iface);
1393 This->Flags &= ~SFLAG_DCINUSE;
1395 return WINED3D_OK;
1398 /* ******************************************************
1399 IWineD3DSurface Internal (No mapping to directx api) parts follow
1400 ****************************************************** */
1402 HRESULT d3dfmt_get_conv(IWineD3DSurfaceImpl *This, BOOL need_alpha_ck, BOOL use_texturing, GLenum *format, GLenum *internal, GLenum *type, CONVERT_TYPES *convert, int *target_bpp, BOOL srgb_mode) {
1403 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1404 const GlPixelFormatDesc *glDesc;
1405 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1406 BOOL p8_render_target = FALSE;
1407 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1409 /* Default values: From the surface */
1410 *format = glDesc->glFormat;
1411 *internal = srgb_mode?glDesc->glGammaInternal:glDesc->glInternal;
1412 *type = glDesc->glType;
1413 *convert = NO_CONVERSION;
1414 *target_bpp = This->bytesPerPixel;
1416 /* Ok, now look if we have to do any conversion */
1417 switch(This->resource.format) {
1418 case WINED3DFMT_P8:
1419 /* ****************
1420 Paletted Texture
1421 **************** */
1423 if (device->render_targets && device->render_targets[0]) {
1424 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
1425 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
1426 p8_render_target = TRUE;
1429 /* Use conversion when the paletted texture extension is not available, or when it is available make sure it is used
1430 * for texturing as it won't work for calls like glDraw-/glReadPixels and further also use conversion in case of color keying.
1431 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which conflicts with this.
1433 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) && p8_render_target)) || colorkey_active || (!use_texturing && GL_SUPPORT(EXT_PALETTED_TEXTURE)) ) {
1434 *format = GL_RGBA;
1435 *internal = GL_RGBA;
1436 *type = GL_UNSIGNED_BYTE;
1437 *target_bpp = 4;
1438 if(colorkey_active) {
1439 *convert = CONVERT_PALETTED_CK;
1440 } else {
1441 *convert = CONVERT_PALETTED;
1444 else if(GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1445 *format = GL_RED;
1446 *internal = GL_RGBA;
1447 *type = GL_UNSIGNED_BYTE;
1448 *target_bpp = 1;
1451 break;
1453 case WINED3DFMT_R3G3B2:
1454 /* **********************
1455 GL_UNSIGNED_BYTE_3_3_2
1456 ********************** */
1457 if (colorkey_active) {
1458 /* This texture format will never be used.. So do not care about color keying
1459 up until the point in time it will be needed :-) */
1460 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1462 break;
1464 case WINED3DFMT_R5G6B5:
1465 if (colorkey_active) {
1466 *convert = CONVERT_CK_565;
1467 *format = GL_RGBA;
1468 *internal = GL_RGBA;
1469 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1471 break;
1473 case WINED3DFMT_X1R5G5B5:
1474 if (colorkey_active) {
1475 *convert = CONVERT_CK_5551;
1476 *format = GL_BGRA;
1477 *internal = GL_RGBA;
1478 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1480 break;
1482 case WINED3DFMT_R8G8B8:
1483 if (colorkey_active) {
1484 *convert = CONVERT_CK_RGB24;
1485 *format = GL_RGBA;
1486 *internal = GL_RGBA;
1487 *type = GL_UNSIGNED_INT_8_8_8_8;
1488 *target_bpp = 4;
1490 break;
1492 case WINED3DFMT_X8R8G8B8:
1493 if (colorkey_active) {
1494 *convert = CONVERT_RGB32_888;
1495 *format = GL_RGBA;
1496 *internal = GL_RGBA;
1497 *type = GL_UNSIGNED_INT_8_8_8_8;
1499 break;
1501 case WINED3DFMT_V8U8:
1502 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1503 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1504 *format = GL_DUDV_ATI;
1505 *internal = GL_DU8DV8_ATI;
1506 *type = GL_BYTE;
1507 /* No conversion - Just change the gl type */
1508 break;
1510 *convert = CONVERT_V8U8;
1511 *format = GL_BGR;
1512 *internal = GL_RGB8;
1513 *type = GL_UNSIGNED_BYTE;
1514 *target_bpp = 3;
1515 break;
1517 case WINED3DFMT_L6V5U5:
1518 *convert = CONVERT_L6V5U5;
1519 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1520 *target_bpp = 3;
1521 /* Use format and types from table */
1522 } else {
1523 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1524 *target_bpp = 2;
1525 *format = GL_RGB;
1526 *internal = GL_RGB5;
1527 *type = GL_UNSIGNED_SHORT_5_6_5;
1529 break;
1531 case WINED3DFMT_X8L8V8U8:
1532 *convert = CONVERT_X8L8V8U8;
1533 *target_bpp = 4;
1534 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1535 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1536 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1537 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1538 * the needed type and format parameter, so the internal format contains a
1539 * 4th component, which is returned as alpha
1541 } else {
1542 /* Not supported by GL_ATI_envmap_bumpmap */
1543 *format = GL_BGRA;
1544 *internal = GL_RGB8;
1545 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1547 break;
1549 case WINED3DFMT_Q8W8V8U8:
1550 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1551 *convert = CONVERT_Q8W8V8U8;
1552 *format = GL_BGRA;
1553 *internal = GL_RGBA8;
1554 *type = GL_UNSIGNED_BYTE;
1555 *target_bpp = 4;
1556 /* Not supported by GL_ATI_envmap_bumpmap */
1557 break;
1559 case WINED3DFMT_V16U16:
1560 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1561 *convert = CONVERT_V16U16;
1562 *format = GL_BGR;
1563 *internal = GL_RGB16_EXT;
1564 *type = GL_UNSIGNED_SHORT;
1565 *target_bpp = 6;
1566 /* What should I do here about GL_ATI_envmap_bumpmap?
1567 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1569 break;
1571 case WINED3DFMT_A4L4:
1572 /* A4L4 exists as an internal gl format, but for some reason there is not
1573 * format+type combination to load it. Thus convert it to A8L8, then load it
1574 * with A4L4 internal, but A8L8 format+type
1576 *convert = CONVERT_A4L4;
1577 *format = GL_LUMINANCE_ALPHA;
1578 *internal = GL_LUMINANCE4_ALPHA4;
1579 *type = GL_UNSIGNED_BYTE;
1580 *target_bpp = 2;
1581 break;
1583 case WINED3DFMT_R32F:
1584 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1585 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1586 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1587 * 1.0 instead.
1589 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1591 *convert = CONVERT_R32F;
1592 *format = GL_RGB;
1593 *internal = GL_RGB32F_ARB;
1594 *type = GL_FLOAT;
1595 *target_bpp = 12;
1596 break;
1598 case WINED3DFMT_R16F:
1599 /* Similar to R32F */
1600 *convert = CONVERT_R16F;
1601 *format = GL_RGB;
1602 *internal = GL_RGB16F_ARB;
1603 *type = GL_HALF_FLOAT_ARB;
1604 *target_bpp = 6;
1605 break;
1607 default:
1608 break;
1611 return WINED3D_OK;
1614 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1615 BYTE *source, *dest;
1616 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1618 switch (convert) {
1619 case NO_CONVERSION:
1621 memcpy(dst, src, pitch * height);
1622 break;
1624 case CONVERT_PALETTED:
1625 case CONVERT_PALETTED_CK:
1627 IWineD3DPaletteImpl* pal = This->palette;
1628 BYTE table[256][4];
1629 unsigned int x, y;
1631 if( pal == NULL) {
1632 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1635 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1637 for (y = 0; y < height; y++)
1639 source = src + pitch * y;
1640 dest = dst + outpitch * y;
1641 /* This is an 1 bpp format, using the width here is fine */
1642 for (x = 0; x < width; x++) {
1643 BYTE color = *source++;
1644 *dest++ = table[color][0];
1645 *dest++ = table[color][1];
1646 *dest++ = table[color][2];
1647 *dest++ = table[color][3];
1651 break;
1653 case CONVERT_CK_565:
1655 /* Converting the 565 format in 5551 packed to emulate color-keying.
1657 Note : in all these conversion, it would be best to average the averaging
1658 pixels to get the color of the pixel that will be color-keyed to
1659 prevent 'color bleeding'. This will be done later on if ever it is
1660 too visible.
1662 Note2: Nvidia documents say that their driver does not support alpha + color keying
1663 on the same surface and disables color keying in such a case
1665 unsigned int x, y;
1666 WORD *Source;
1667 WORD *Dest;
1669 TRACE("Color keyed 565\n");
1671 for (y = 0; y < height; y++) {
1672 Source = (WORD *) (src + y * pitch);
1673 Dest = (WORD *) (dst + y * outpitch);
1674 for (x = 0; x < width; x++ ) {
1675 WORD color = *Source++;
1676 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1677 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1678 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1679 *Dest |= 0x0001;
1681 Dest++;
1685 break;
1687 case CONVERT_CK_5551:
1689 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1690 unsigned int x, y;
1691 WORD *Source;
1692 WORD *Dest;
1693 TRACE("Color keyed 5551\n");
1694 for (y = 0; y < height; y++) {
1695 Source = (WORD *) (src + y * pitch);
1696 Dest = (WORD *) (dst + y * outpitch);
1697 for (x = 0; x < width; x++ ) {
1698 WORD color = *Source++;
1699 *Dest = color;
1700 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1701 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1702 *Dest |= (1 << 15);
1704 else {
1705 *Dest &= ~(1 << 15);
1707 Dest++;
1711 break;
1713 case CONVERT_V8U8:
1715 unsigned int x, y;
1716 short *Source;
1717 unsigned char *Dest;
1718 for(y = 0; y < height; y++) {
1719 Source = (short *) (src + y * pitch);
1720 Dest = (unsigned char *) (dst + y * outpitch);
1721 for (x = 0; x < width; x++ ) {
1722 long color = (*Source++);
1723 /* B */ Dest[0] = 0xff;
1724 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1725 /* R */ Dest[2] = (color) + 128; /* U */
1726 Dest += 3;
1729 break;
1732 case CONVERT_V16U16:
1734 unsigned int x, y;
1735 DWORD *Source;
1736 unsigned short *Dest;
1737 for(y = 0; y < height; y++) {
1738 Source = (DWORD *) (src + y * pitch);
1739 Dest = (unsigned short *) (dst + y * outpitch);
1740 for (x = 0; x < width; x++ ) {
1741 DWORD color = (*Source++);
1742 /* B */ Dest[0] = 0xffff;
1743 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1744 /* R */ Dest[2] = (color ) + 32768; /* U */
1745 Dest += 3;
1748 break;
1751 case CONVERT_Q8W8V8U8:
1753 unsigned int x, y;
1754 DWORD *Source;
1755 unsigned char *Dest;
1756 for(y = 0; y < height; y++) {
1757 Source = (DWORD *) (src + y * pitch);
1758 Dest = (unsigned char *) (dst + y * outpitch);
1759 for (x = 0; x < width; x++ ) {
1760 long color = (*Source++);
1761 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1762 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1763 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1764 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1765 Dest += 4;
1768 break;
1771 case CONVERT_L6V5U5:
1773 unsigned int x, y;
1774 WORD *Source;
1775 unsigned char *Dest;
1777 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1778 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1779 * fixed function and shaders without further conversion once the surface is
1780 * loaded
1782 for(y = 0; y < height; y++) {
1783 Source = (WORD *) (src + y * pitch);
1784 Dest = (unsigned char *) (dst + y * outpitch);
1785 for (x = 0; x < width; x++ ) {
1786 short color = (*Source++);
1787 unsigned char l = ((color >> 10) & 0xfc);
1788 char v = ((color >> 5) & 0x3e);
1789 char u = ((color ) & 0x1f);
1791 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1792 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1793 * shift. GL reads a signed value and converts it into an unsigned value.
1795 /* M */ Dest[2] = l << 1;
1797 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1798 * from 5 bit values to 8 bit values.
1800 /* V */ Dest[1] = v << 3;
1801 /* U */ Dest[0] = u << 3;
1802 Dest += 3;
1805 } else {
1806 for(y = 0; y < height; y++) {
1807 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1808 Source = (WORD *) (src + y * pitch);
1809 for (x = 0; x < width; x++ ) {
1810 short color = (*Source++);
1811 unsigned char l = ((color >> 10) & 0xfc);
1812 short v = ((color >> 5) & 0x3e);
1813 short u = ((color ) & 0x1f);
1814 short v_conv = v + 16;
1815 short u_conv = u + 16;
1817 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1818 Dest_s += 1;
1822 break;
1825 case CONVERT_X8L8V8U8:
1827 unsigned int x, y;
1828 DWORD *Source;
1829 unsigned char *Dest;
1831 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1832 /* This implementation works with the fixed function pipeline and shaders
1833 * without further modification after converting the surface.
1835 for(y = 0; y < height; y++) {
1836 Source = (DWORD *) (src + y * pitch);
1837 Dest = (unsigned char *) (dst + y * outpitch);
1838 for (x = 0; x < width; x++ ) {
1839 long color = (*Source++);
1840 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1841 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1842 /* U */ Dest[0] = (color & 0xff); /* U */
1843 /* I */ Dest[3] = 255; /* X */
1844 Dest += 4;
1847 } else {
1848 /* Doesn't work correctly with the fixed function pipeline, but can work in
1849 * shaders if the shader is adjusted. (There's no use for this format in gl's
1850 * standard fixed function pipeline anyway).
1852 for(y = 0; y < height; y++) {
1853 Source = (DWORD *) (src + y * pitch);
1854 Dest = (unsigned char *) (dst + y * outpitch);
1855 for (x = 0; x < width; x++ ) {
1856 long color = (*Source++);
1857 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1858 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1859 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1860 Dest += 4;
1864 break;
1867 case CONVERT_A4L4:
1869 unsigned int x, y;
1870 unsigned char *Source;
1871 unsigned char *Dest;
1872 for(y = 0; y < height; y++) {
1873 Source = (unsigned char *) (src + y * pitch);
1874 Dest = (unsigned char *) (dst + y * outpitch);
1875 for (x = 0; x < width; x++ ) {
1876 unsigned char color = (*Source++);
1877 /* A */ Dest[1] = (color & 0xf0) << 0;
1878 /* L */ Dest[0] = (color & 0x0f) << 4;
1879 Dest += 2;
1882 break;
1885 case CONVERT_R32F:
1887 unsigned int x, y;
1888 float *Source;
1889 float *Dest;
1890 for(y = 0; y < height; y++) {
1891 Source = (float *) (src + y * pitch);
1892 Dest = (float *) (dst + y * outpitch);
1893 for (x = 0; x < width; x++ ) {
1894 float color = (*Source++);
1895 Dest[0] = color;
1896 Dest[1] = 1.0;
1897 Dest[2] = 1.0;
1898 Dest += 3;
1901 break;
1904 case CONVERT_R16F:
1906 unsigned int x, y;
1907 WORD *Source;
1908 WORD *Dest;
1909 WORD one = 0x3c00;
1910 for(y = 0; y < height; y++) {
1911 Source = (WORD *) (src + y * pitch);
1912 Dest = (WORD *) (dst + y * outpitch);
1913 for (x = 0; x < width; x++ ) {
1914 WORD color = (*Source++);
1915 Dest[0] = color;
1916 Dest[1] = one;
1917 Dest[2] = one;
1918 Dest += 3;
1921 break;
1923 default:
1924 ERR("Unsupported conversation type %d\n", convert);
1926 return WINED3D_OK;
1929 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
1930 IWineD3DPaletteImpl* pal = This->palette;
1931 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1932 BOOL index_in_alpha = FALSE;
1933 int i;
1935 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1936 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1937 * is slow. Further RGB->P8 conversion is not possible because palettes can have
1938 * duplicate entries. Store the color key in the unused alpha component to speed the
1939 * download up and to make conversion unneeded. */
1940 if (device->render_targets && device->render_targets[0]) {
1941 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
1943 if(render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1944 index_in_alpha = TRUE;
1947 if (pal == NULL) {
1948 /* Still no palette? Use the device's palette */
1949 /* Get the surface's palette */
1950 for (i = 0; i < 256; i++) {
1951 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1952 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1953 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1955 if(index_in_alpha) {
1956 table[i][3] = i;
1957 } else if (colorkey &&
1958 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1959 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1960 /* We should maybe here put a more 'neutral' color than the standard bright purple
1961 one often used by application to prevent the nice purple borders when bi-linear
1962 filtering is on */
1963 table[i][3] = 0x00;
1964 } else {
1965 table[i][3] = 0xFF;
1968 } else {
1969 TRACE("Using surface palette %p\n", pal);
1970 /* Get the surface's palette */
1971 for (i = 0; i < 256; i++) {
1972 table[i][0] = pal->palents[i].peRed;
1973 table[i][1] = pal->palents[i].peGreen;
1974 table[i][2] = pal->palents[i].peBlue;
1976 if(index_in_alpha) {
1977 table[i][3] = i;
1979 else if (colorkey &&
1980 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1981 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1982 /* We should maybe here put a more 'neutral' color than the standard bright purple
1983 one often used by application to prevent the nice purple borders when bi-linear
1984 filtering is on */
1985 table[i][3] = 0x00;
1986 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
1987 table[i][3] = pal->palents[i].peFlags;
1988 } else {
1989 table[i][3] = 0xFF;
1995 const char *fragment_palette_conversion =
1996 "!!ARBfp1.0\n"
1997 "TEMP index;\n"
1998 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
1999 "TEX index.x, fragment.texcoord[0], texture[0], 2D;\n" /* store the red-component of the current pixel */
2000 "MAD index.x, index.x, constants.x, constants.y;\n" /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2001 "TEX result.color, index, texture[1], 1D;\n" /* use the red-component as a index in the palette to get the final color */
2002 "END";
2004 /* This function is used in case of 8bit paletted textures to upload the palette.
2005 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2006 extensions like ATI_fragment_shaders is possible.
2008 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2009 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2010 BYTE table[256][4];
2011 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2013 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2015 /* Try to use the paletted texture extension */
2016 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2018 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2019 GL_EXTCALL(glColorTableEXT(GL_TEXTURE_2D,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2021 else
2023 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2024 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2025 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2027 /* Create the fragment program if we don't have it */
2028 if(!device->paletteConversionShader)
2030 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2031 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2032 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2033 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2034 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2037 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2038 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2040 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2041 glEnable(GL_TEXTURE_1D);
2042 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2044 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2045 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2046 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2047 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2049 /* Switch back to unit 0 in which the 2D texture will be stored. */
2050 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2052 /* Rebind the texture because it isn't bound anymore */
2053 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2057 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2058 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2060 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2061 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2062 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2064 return FALSE;
2067 if(This->palette9) {
2068 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2069 return FALSE;
2071 } else {
2072 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2074 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2075 return TRUE;
2078 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2079 GLboolean oldwrite[4];
2081 /* Some formats have only some color channels, and the others are 1.0.
2082 * since our rendering renders to all channels, and those pixel formats
2083 * are emulated by using a full texture with the other channels set to 1.0
2084 * manually, clear the unused channels.
2086 * This could be done with hacking colorwriteenable to mask the colors,
2087 * but before drawing the buffer would have to be cleared too, so there's
2088 * no gain in that
2090 switch(This->resource.format) {
2091 case WINED3DFMT_R16F:
2092 case WINED3DFMT_R32F:
2093 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2094 /* Do not activate a context, the correct drawable is active already
2095 * though just the read buffer is set, make sure to have the correct draw
2096 * buffer too
2098 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2099 glDisable(GL_SCISSOR_TEST);
2100 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2101 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2102 glClearColor(0.0, 1.0, 1.0, 1.0);
2103 glClear(GL_COLOR_BUFFER_BIT);
2104 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2105 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2106 checkGLcall("Unused channel clear\n");
2107 break;
2109 default: break;
2113 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2114 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2116 if (!(This->Flags & SFLAG_INTEXTURE)) {
2117 TRACE("Reloading because surface is dirty\n");
2118 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2119 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2120 /* Reload: vice versa OR */
2121 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2122 /* Also reload: Color key is active AND the color key has changed */
2123 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2124 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2125 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2126 TRACE("Reloading because of color keying\n");
2127 /* To perform the color key conversion we need a sysmem copy of
2128 * the surface. Make sure we have it
2130 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2131 } else if(palette9_changed(This)) {
2132 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2133 /* TODO: This is not necessarily needed with hw palettized texture support */
2134 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2135 } else {
2136 TRACE("surface is already in texture\n");
2137 return WINED3D_OK;
2140 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2141 * These resources are not bound by device size or format restrictions. Because of this,
2142 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2143 * However, these resources can always be created, locked, and copied.
2145 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2147 FIXME("(%p) Operation not supported for scratch textures\n",This);
2148 return WINED3DERR_INVALIDCALL;
2151 This->srgb = srgb_mode;
2152 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2154 #if 0
2156 static unsigned int gen = 0;
2157 char buffer[4096];
2158 ++gen;
2159 if ((gen % 10) == 0) {
2160 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2161 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2164 * debugging crash code
2165 if (gen == 250) {
2166 void** test = NULL;
2167 *test = 0;
2171 #endif
2173 if (!(This->Flags & SFLAG_DONOTFREE)) {
2174 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2175 This->resource.allocatedMemory = NULL;
2176 This->resource.heapMemory = NULL;
2177 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2180 return WINED3D_OK;
2183 #include <errno.h>
2184 #include <stdio.h>
2185 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2186 FILE* f = NULL;
2187 UINT i, y;
2188 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2189 char *allocatedMemory;
2190 char *textureRow;
2191 IWineD3DSwapChain *swapChain = NULL;
2192 int width, height;
2193 GLuint tmpTexture = 0;
2194 DWORD color;
2195 /*FIXME:
2196 Textures may not be stored in ->allocatedgMemory and a GlTexture
2197 so we should lock the surface before saving a snapshot, or at least check that
2199 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2200 by calling GetTexImage and in compressed form by calling
2201 GetCompressedTexImageARB. Queried compressed images can be saved and
2202 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2203 texture images do not need to be processed by the GL and should
2204 significantly improve texture loading performance relative to uncompressed
2205 images. */
2207 /* Setup the width and height to be the internal texture width and height. */
2208 width = This->pow2Width;
2209 height = This->pow2Height;
2210 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2211 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2213 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2214 /* if were not a real texture then read the back buffer into a real texture */
2215 /* we don't want to interfere with the back buffer so read the data into a temporary
2216 * texture and then save the data out of the temporary texture
2218 GLint prevRead;
2219 ENTER_GL();
2220 TRACE("(%p) Reading render target into texture\n", This);
2221 glEnable(GL_TEXTURE_2D);
2223 glGenTextures(1, &tmpTexture);
2224 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2226 glTexImage2D(GL_TEXTURE_2D,
2228 GL_RGBA,
2229 width,
2230 height,
2231 0/*border*/,
2232 GL_RGBA,
2233 GL_UNSIGNED_INT_8_8_8_8_REV,
2234 NULL);
2236 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2237 vcheckGLcall("glGetIntegerv");
2238 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2239 vcheckGLcall("glReadBuffer");
2240 glCopyTexImage2D(GL_TEXTURE_2D,
2242 GL_RGBA,
2245 width,
2246 height,
2249 checkGLcall("glCopyTexImage2D");
2250 glReadBuffer(prevRead);
2251 LEAVE_GL();
2253 } else { /* bind the real texture, and make sure it up to date */
2254 IWineD3DSurface_PreLoad(iface);
2256 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2257 ENTER_GL();
2258 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2259 glGetTexImage(GL_TEXTURE_2D,
2260 This->glDescription.level,
2261 GL_RGBA,
2262 GL_UNSIGNED_INT_8_8_8_8_REV,
2263 allocatedMemory);
2264 checkGLcall("glTexImage2D");
2265 if (tmpTexture) {
2266 glBindTexture(GL_TEXTURE_2D, 0);
2267 glDeleteTextures(1, &tmpTexture);
2269 LEAVE_GL();
2271 f = fopen(filename, "w+");
2272 if (NULL == f) {
2273 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2274 return WINED3DERR_INVALIDCALL;
2276 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2277 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2278 /* TGA header */
2279 fputc(0,f);
2280 fputc(0,f);
2281 fputc(2,f);
2282 fputc(0,f);
2283 fputc(0,f);
2284 fputc(0,f);
2285 fputc(0,f);
2286 fputc(0,f);
2287 fputc(0,f);
2288 fputc(0,f);
2289 fputc(0,f);
2290 fputc(0,f);
2291 /* short width*/
2292 fwrite(&width,2,1,f);
2293 /* short height */
2294 fwrite(&height,2,1,f);
2295 /* format rgba */
2296 fputc(0x20,f);
2297 fputc(0x28,f);
2298 /* raw data */
2299 /* if the data is upside down if we've fetched it from a back buffer, so it needs flipping again to make it the correct way up */
2300 if(swapChain)
2301 textureRow = allocatedMemory + (width * (height - 1) *4);
2302 else
2303 textureRow = allocatedMemory;
2304 for (y = 0 ; y < height; y++) {
2305 for (i = 0; i < width; i++) {
2306 color = *((DWORD*)textureRow);
2307 fputc((color >> 16) & 0xFF, f); /* B */
2308 fputc((color >> 8) & 0xFF, f); /* G */
2309 fputc((color >> 0) & 0xFF, f); /* R */
2310 fputc((color >> 24) & 0xFF, f); /* A */
2311 textureRow += 4;
2313 /* take two rows of the pointer to the texture memory */
2314 if(swapChain)
2315 (textureRow-= width << 3);
2318 TRACE("Closing file\n");
2319 fclose(f);
2321 if(swapChain) {
2322 IWineD3DSwapChain_Release(swapChain);
2324 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2325 return WINED3D_OK;
2329 * Slightly inefficient way to handle multiple dirty rects but it works :)
2331 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2332 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2333 IWineD3DBaseTexture *baseTexture = NULL;
2335 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2336 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2338 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2339 if (NULL != pDirtyRect) {
2340 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2341 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2342 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2343 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2344 } else {
2345 This->dirtyRect.left = 0;
2346 This->dirtyRect.top = 0;
2347 This->dirtyRect.right = This->currentDesc.Width;
2348 This->dirtyRect.bottom = This->currentDesc.Height;
2350 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2351 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2352 /* if the container is a basetexture then mark it dirty. */
2353 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2354 TRACE("Passing to container\n");
2355 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2356 IWineD3DBaseTexture_Release(baseTexture);
2358 return WINED3D_OK;
2361 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2362 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2363 HRESULT hr;
2364 const GlPixelFormatDesc *glDesc;
2365 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2367 TRACE("(%p) : Calling base function first\n", This);
2368 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2369 if(SUCCEEDED(hr)) {
2370 /* Setup some glformat defaults */
2371 This->glDescription.glFormat = glDesc->glFormat;
2372 This->glDescription.glFormatInternal = glDesc->glInternal;
2373 This->glDescription.glType = glDesc->glType;
2375 This->Flags &= ~SFLAG_ALLOCATED;
2376 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2377 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2379 return hr;
2382 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2383 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2385 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2386 WARN("Surface is locked or the HDC is in use\n");
2387 return WINED3DERR_INVALIDCALL;
2390 if(Mem && Mem != This->resource.allocatedMemory) {
2391 void *release = NULL;
2393 /* Do I have to copy the old surface content? */
2394 if(This->Flags & SFLAG_DIBSECTION) {
2395 /* Release the DC. No need to hold the critical section for the update
2396 * Thread because this thread runs only on front buffers, but this method
2397 * fails for render targets in the check above.
2399 SelectObject(This->hDC, This->dib.holdbitmap);
2400 DeleteDC(This->hDC);
2401 /* Release the DIB section */
2402 DeleteObject(This->dib.DIBsection);
2403 This->dib.bitmap_data = NULL;
2404 This->resource.allocatedMemory = NULL;
2405 This->hDC = NULL;
2406 This->Flags &= ~SFLAG_DIBSECTION;
2407 } else if(!(This->Flags & SFLAG_USERPTR)) {
2408 release = This->resource.heapMemory;
2409 This->resource.heapMemory = NULL;
2411 This->resource.allocatedMemory = Mem;
2412 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2414 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2415 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2417 /* For client textures opengl has to be notified */
2418 if(This->Flags & SFLAG_CLIENT) {
2419 This->Flags &= ~SFLAG_ALLOCATED;
2420 IWineD3DSurface_PreLoad(iface);
2421 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2424 /* Now free the old memory if any */
2425 HeapFree(GetProcessHeap(), 0, release);
2426 } else if(This->Flags & SFLAG_USERPTR) {
2427 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2428 This->resource.allocatedMemory = NULL;
2429 /* HeapMemory should be NULL already */
2430 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2431 This->Flags &= ~SFLAG_USERPTR;
2433 if(This->Flags & SFLAG_CLIENT) {
2434 This->Flags &= ~SFLAG_ALLOCATED;
2435 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2436 IWineD3DSurface_PreLoad(iface);
2439 return WINED3D_OK;
2442 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2443 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2444 IWineD3DSwapChainImpl *swapchain = NULL;
2445 HRESULT hr;
2446 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2448 /* Flipping is only supported on RenderTargets */
2449 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2451 if(override) {
2452 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2453 * FIXME("(%p) Target override is not supported by now\n", This);
2454 * Additionally, it isn't really possible to support triple-buffering
2455 * properly on opengl at all
2459 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2460 if(!swapchain) {
2461 ERR("Flipped surface is not on a swapchain\n");
2462 return WINEDDERR_NOTFLIPPABLE;
2465 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2466 * and only d3d8 and d3d9 apps specify the presentation interval
2468 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2469 /* Most common case first to avoid wasting time on all the other cases */
2470 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2471 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2472 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2473 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2474 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2475 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2476 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2477 } else {
2478 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2481 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2482 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2483 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2484 return hr;
2487 /* Does a direct frame buffer -> texture copy. Stretching is done
2488 * with single pixel copy calls
2490 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2491 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2492 float xrel, yrel;
2493 UINT row;
2494 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2497 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2498 ENTER_GL();
2499 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2501 /* Bind the target texture */
2502 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
2503 checkGLcall("glBindTexture");
2504 if(!swapchain) {
2505 glReadBuffer(myDevice->offscreenBuffer);
2506 } else {
2507 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2508 glReadBuffer(buffer);
2510 checkGLcall("glReadBuffer");
2512 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2513 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2515 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2516 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2518 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2519 ERR("Texture filtering not supported in direct blit\n");
2521 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2522 ERR("Texture filtering not supported in direct blit\n");
2525 if(upsidedown &&
2526 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2527 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2528 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2530 glCopyTexSubImage2D(This->glDescription.target,
2531 This->glDescription.level,
2532 drect->x1, drect->y1, /* xoffset, yoffset */
2533 srect->x1, Src->currentDesc.Height - srect->y2,
2534 drect->x2 - drect->x1, drect->y2 - drect->y1);
2535 } else {
2536 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2537 /* I have to process this row by row to swap the image,
2538 * otherwise it would be upside down, so stretching in y direction
2539 * doesn't cost extra time
2541 * However, stretching in x direction can be avoided if not necessary
2543 for(row = drect->y1; row < drect->y2; row++) {
2544 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2545 /* Well, that stuff works, but it's very slow.
2546 * find a better way instead
2548 UINT col;
2550 for(col = drect->x1; col < drect->x2; col++) {
2551 glCopyTexSubImage2D(This->glDescription.target,
2552 This->glDescription.level,
2553 drect->x1 + col, row, /* xoffset, yoffset */
2554 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2555 1, 1);
2557 } else {
2558 glCopyTexSubImage2D(This->glDescription.target,
2559 This->glDescription.level,
2560 drect->x1, row, /* xoffset, yoffset */
2561 srect->x1, yoffset - (int) (row * yrel),
2562 drect->x2-drect->x1, 1);
2567 vcheckGLcall("glCopyTexSubImage2D");
2568 LEAVE_GL();
2571 /* Uses the hardware to stretch and flip the image */
2572 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2573 GLuint src, backup = 0;
2574 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2575 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2576 float left, right, top, bottom; /* Texture coordinates */
2577 UINT fbwidth = Src->currentDesc.Width;
2578 UINT fbheight = Src->currentDesc.Height;
2579 GLenum drawBuffer = GL_BACK;
2581 TRACE("Using hwstretch blit\n");
2582 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2583 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2584 ENTER_GL();
2585 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2587 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2588 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2590 if(GL_LIMITS(aux_buffers) >= 2) {
2591 /* Got more than one aux buffer? Use the 2nd aux buffer */
2592 drawBuffer = GL_AUX1;
2593 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2594 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2595 drawBuffer = GL_AUX0;
2598 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2599 glGenTextures(1, &backup);
2600 checkGLcall("glGenTextures\n");
2601 glBindTexture(GL_TEXTURE_2D, backup);
2602 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2603 } else {
2604 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2605 * we are reading from the back buffer, the backup can be used as source texture
2607 if(Src->glDescription.textureName == 0) {
2608 /* Get it a description */
2609 IWineD3DSurface_PreLoad(SrcSurface);
2611 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2612 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2614 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2615 Src->Flags &= ~SFLAG_INTEXTURE;
2618 glReadBuffer(GL_BACK);
2619 checkGLcall("glReadBuffer(GL_BACK)");
2621 /* TODO: Only back up the part that will be overwritten */
2622 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2623 0, 0 /* read offsets */,
2624 0, 0,
2625 fbwidth,
2626 fbheight);
2628 checkGLcall("glCopyTexSubImage2D");
2630 /* No issue with overriding these - the sampler is dirty due to blit usage */
2631 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
2632 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2633 checkGLcall("glTexParameteri");
2634 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
2635 minMipLookup[Filter][WINED3DTEXF_NONE]);
2636 checkGLcall("glTexParameteri");
2638 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2639 src = backup ? backup : Src->glDescription.textureName;
2640 } else {
2641 glReadBuffer(GL_FRONT);
2642 checkGLcall("glReadBuffer(GL_FRONT)");
2644 glGenTextures(1, &src);
2645 checkGLcall("glGenTextures(1, &src)");
2646 glBindTexture(GL_TEXTURE_2D, src);
2647 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2649 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2650 * out for power of 2 sizes
2652 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2653 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2654 checkGLcall("glTexImage2D");
2655 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2656 0, 0 /* read offsets */,
2657 0, 0,
2658 fbwidth,
2659 fbheight);
2661 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2662 checkGLcall("glTexParameteri");
2663 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2664 checkGLcall("glTexParameteri");
2666 glReadBuffer(GL_BACK);
2667 checkGLcall("glReadBuffer(GL_BACK)");
2669 checkGLcall("glEnd and previous");
2671 left = (float) srect->x1 / (float) Src->pow2Width;
2672 right = (float) srect->x2 / (float) Src->pow2Width;
2674 if(upsidedown) {
2675 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2676 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2677 } else {
2678 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2679 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2682 /* draw the source texture stretched and upside down. The correct surface is bound already */
2683 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2684 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2686 glDrawBuffer(drawBuffer);
2687 glReadBuffer(drawBuffer);
2689 glBegin(GL_QUADS);
2690 /* bottom left */
2691 glTexCoord2f(left, bottom);
2692 glVertex2i(0, fbheight);
2694 /* top left */
2695 glTexCoord2f(left, top);
2696 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2698 /* top right */
2699 glTexCoord2f(right, top);
2700 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2702 /* bottom right */
2703 glTexCoord2f(right, bottom);
2704 glVertex2i(drect->x2 - drect->x1, fbheight);
2705 glEnd();
2706 checkGLcall("glEnd and previous");
2708 /* Now read the stretched and upside down image into the destination texture */
2709 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2710 checkGLcall("glBindTexture");
2711 glCopyTexSubImage2D(This->glDescription.target,
2713 drect->x1, drect->y1, /* xoffset, yoffset */
2714 0, 0, /* We blitted the image to the origin */
2715 drect->x2 - drect->x1, drect->y2 - drect->y1);
2716 checkGLcall("glCopyTexSubImage2D");
2718 /* Write the back buffer backup back */
2719 glBindTexture(GL_TEXTURE_2D, backup ? backup : Src->glDescription.textureName);
2720 checkGLcall("glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName)");
2722 if(drawBuffer == GL_BACK) {
2723 glBegin(GL_QUADS);
2724 /* top left */
2725 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2726 glVertex2i(0, 0);
2728 /* bottom left */
2729 glTexCoord2f(0.0, 0.0);
2730 glVertex2i(0, fbheight);
2732 /* bottom right */
2733 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2734 glVertex2i(fbwidth, Src->currentDesc.Height);
2736 /* top right */
2737 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2738 glVertex2i(fbwidth, 0);
2739 glEnd();
2740 } else {
2741 /* Restore the old draw buffer */
2742 glDrawBuffer(GL_BACK);
2745 /* Cleanup */
2746 if(src != Src->glDescription.textureName && src != backup) {
2747 glDeleteTextures(1, &src);
2748 checkGLcall("glDeleteTextures(1, &src)");
2750 if(backup) {
2751 glDeleteTextures(1, &backup);
2752 checkGLcall("glDeleteTextures(1, &backup)");
2754 LEAVE_GL();
2757 /* Not called from the VTable */
2758 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2759 WINED3DRECT rect;
2760 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2761 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2762 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2764 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2766 /* Get the swapchain. One of the surfaces has to be a primary surface */
2767 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2768 WARN("Destination is in sysmem, rejecting gl blt\n");
2769 return WINED3DERR_INVALIDCALL;
2771 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2772 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2773 if(Src) {
2774 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2775 WARN("Src is in sysmem, rejecting gl blt\n");
2776 return WINED3DERR_INVALIDCALL;
2778 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2779 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2782 /* Early sort out of cases where no render target is used */
2783 if(!dstSwapchain && !srcSwapchain &&
2784 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2785 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2786 return WINED3DERR_INVALIDCALL;
2789 /* No destination color keying supported */
2790 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2791 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2792 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2793 return WINED3DERR_INVALIDCALL;
2796 if (DestRect) {
2797 rect.x1 = DestRect->left;
2798 rect.y1 = DestRect->top;
2799 rect.x2 = DestRect->right;
2800 rect.y2 = DestRect->bottom;
2801 } else {
2802 rect.x1 = 0;
2803 rect.y1 = 0;
2804 rect.x2 = This->currentDesc.Width;
2805 rect.y2 = This->currentDesc.Height;
2808 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2809 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2810 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2811 /* Half-life does a Blt from the back buffer to the front buffer,
2812 * Full surface size, no flags... Use present instead
2814 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2817 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2818 while(1)
2820 RECT mySrcRect;
2821 TRACE("Looking if a Present can be done...\n");
2822 /* Source Rectangle must be full surface */
2823 if( SrcRect ) {
2824 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2825 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2826 TRACE("No, Source rectangle doesn't match\n");
2827 break;
2830 mySrcRect.left = 0;
2831 mySrcRect.top = 0;
2832 mySrcRect.right = Src->currentDesc.Width;
2833 mySrcRect.bottom = Src->currentDesc.Height;
2835 /* No stretching may occur */
2836 if(mySrcRect.right != rect.x2 - rect.x1 ||
2837 mySrcRect.bottom != rect.y2 - rect.y1) {
2838 TRACE("No, stretching is done\n");
2839 break;
2842 /* Destination must be full surface or match the clipping rectangle */
2843 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
2845 RECT cliprect;
2846 POINT pos[2];
2847 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
2848 pos[0].x = rect.x1;
2849 pos[0].y = rect.y1;
2850 pos[1].x = rect.x2;
2851 pos[1].y = rect.y2;
2852 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
2853 pos, 2);
2855 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
2856 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
2858 TRACE("No, dest rectangle doesn't match(clipper)\n");
2859 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
2860 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
2861 break;
2864 else
2866 if(rect.x1 != 0 || rect.y1 != 0 ||
2867 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
2868 TRACE("No, dest rectangle doesn't match(surface size)\n");
2869 break;
2873 TRACE("Yes\n");
2875 /* These flags are unimportant for the flag check, remove them */
2876 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
2877 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2879 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2880 * take very long, while a flip is fast.
2881 * This applies to Half-Life, which does such Blts every time it finished
2882 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2883 * menu. This is also used by all apps when they do windowed rendering
2885 * The problem is that flipping is not really the same as copying. After a
2886 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2887 * untouched. Therefore it's necessary to override the swap effect
2888 * and to set it back after the flip.
2890 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
2891 * testcases.
2894 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2895 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2897 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2898 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
2900 dstSwapchain->presentParms.SwapEffect = orig_swap;
2902 return WINED3D_OK;
2904 break;
2907 TRACE("Unsupported blit between buffers on the same swapchain\n");
2908 return WINED3DERR_INVALIDCALL;
2909 } else if((dstSwapchain || This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) &&
2910 (srcSwapchain || SrcSurface == myDevice->render_targets[0]) ) {
2911 ERR("Can't perform hardware blit between 2 different swapchains, falling back to software\n");
2912 return WINED3DERR_INVALIDCALL;
2915 if(srcSwapchain || SrcSurface == myDevice->render_targets[0]) {
2916 /* Blit from render target to texture */
2917 WINED3DRECT srect;
2918 BOOL upsideDown, stretchx;
2920 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2921 TRACE("Color keying not supported by frame buffer to texture blit\n");
2922 return WINED3DERR_INVALIDCALL;
2923 /* Destination color key is checked above */
2926 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2927 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2929 if(SrcRect) {
2930 if(SrcRect->top < SrcRect->bottom) {
2931 srect.y1 = SrcRect->top;
2932 srect.y2 = SrcRect->bottom;
2933 upsideDown = FALSE;
2934 } else {
2935 srect.y1 = SrcRect->bottom;
2936 srect.y2 = SrcRect->top;
2937 upsideDown = TRUE;
2939 srect.x1 = SrcRect->left;
2940 srect.x2 = SrcRect->right;
2941 } else {
2942 srect.x1 = 0;
2943 srect.y1 = 0;
2944 srect.x2 = Src->currentDesc.Width;
2945 srect.y2 = Src->currentDesc.Height;
2946 upsideDown = FALSE;
2948 if(rect.x1 > rect.x2) {
2949 UINT tmp = rect.x2;
2950 rect.x2 = rect.x1;
2951 rect.x1 = tmp;
2952 upsideDown = !upsideDown;
2954 if(!srcSwapchain) {
2955 TRACE("Reading from an offscreen target\n");
2956 upsideDown = !upsideDown;
2959 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
2960 stretchx = TRUE;
2961 } else {
2962 stretchx = FALSE;
2965 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2966 * flip the image nor scale it.
2968 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2969 * -> If the app wants a image width an unscaled width, copy it line per line
2970 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
2971 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2972 * back buffer. This is slower than reading line per line, thus not used for flipping
2973 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2974 * pixel by pixel
2976 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
2977 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
2978 * backends.
2980 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
2981 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
2982 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
2983 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
2984 rect.y2 - rect.y1 > Src->currentDesc.Height) {
2985 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
2986 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2987 } else {
2988 TRACE("Using hardware stretching to flip / stretch the texture\n");
2989 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2992 if(!(This->Flags & SFLAG_DONOTFREE)) {
2993 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2994 This->resource.allocatedMemory = NULL;
2995 This->resource.heapMemory = NULL;
2996 } else {
2997 This->Flags &= ~SFLAG_INSYSMEM;
2999 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3000 * path is never entered
3002 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3004 return WINED3D_OK;
3005 } else if(Src) {
3006 /* Blit from offscreen surface to render target */
3007 float glTexCoord[4];
3008 DWORD oldCKeyFlags = Src->CKeyFlags;
3009 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
3010 RECT SourceRectangle;
3012 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3014 if(SrcRect) {
3015 SourceRectangle.left = SrcRect->left;
3016 SourceRectangle.right = SrcRect->right;
3017 SourceRectangle.top = SrcRect->top;
3018 SourceRectangle.bottom = SrcRect->bottom;
3019 } else {
3020 SourceRectangle.left = 0;
3021 SourceRectangle.right = Src->currentDesc.Width;
3022 SourceRectangle.top = 0;
3023 SourceRectangle.bottom = Src->currentDesc.Height;
3026 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3027 /* Fall back to software */
3028 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3029 SourceRectangle.left, SourceRectangle.top,
3030 SourceRectangle.right, SourceRectangle.bottom);
3031 return WINED3DERR_INVALIDCALL;
3034 /* Color keying: Check if we have to do a color keyed blt,
3035 * and if not check if a color key is activated.
3037 * Just modify the color keying parameters in the surface and restore them afterwards
3038 * The surface keeps track of the color key last used to load the opengl surface.
3039 * PreLoad will catch the change to the flags and color key and reload if necessary.
3041 if(Flags & WINEDDBLT_KEYSRC) {
3042 /* Use color key from surface */
3043 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3044 /* Use color key from DDBltFx */
3045 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3046 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3047 } else {
3048 /* Do not use color key */
3049 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3052 /* Now load the surface */
3053 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3056 /* Activate the destination context, set it up for blitting */
3057 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3058 ENTER_GL();
3060 if(!dstSwapchain) {
3061 TRACE("Drawing to offscreen buffer\n");
3062 glDrawBuffer(myDevice->offscreenBuffer);
3063 checkGLcall("glDrawBuffer");
3064 } else {
3065 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3066 TRACE("Drawing to %#x buffer\n", buffer);
3067 glDrawBuffer(buffer);
3068 checkGLcall("glDrawBuffer");
3071 /* Bind the texture */
3072 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
3073 checkGLcall("glBindTexture");
3075 /* Filtering for StretchRect */
3076 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
3077 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3078 checkGLcall("glTexParameteri");
3079 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
3080 minMipLookup[Filter][WINED3DTEXF_NONE]);
3081 checkGLcall("glTexParameteri");
3082 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
3083 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
3084 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3085 checkGLcall("glTexEnvi");
3087 /* This is for color keying */
3088 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3089 glEnable(GL_ALPHA_TEST);
3090 checkGLcall("glEnable GL_ALPHA_TEST");
3091 glAlphaFunc(GL_NOTEQUAL, 0.0);
3092 checkGLcall("glAlphaFunc\n");
3093 } else {
3094 glDisable(GL_ALPHA_TEST);
3095 checkGLcall("glDisable GL_ALPHA_TEST");
3098 /* Draw a textured quad
3100 glBegin(GL_QUADS);
3102 glColor3d(1.0f, 1.0f, 1.0f);
3103 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3104 glVertex3f(rect.x1,
3105 rect.y1,
3106 0.0);
3108 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3109 glVertex3f(rect.x1, rect.y2, 0.0);
3111 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3112 glVertex3f(rect.x2,
3113 rect.y2,
3114 0.0);
3116 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3117 glVertex3f(rect.x2,
3118 rect.y1,
3119 0.0);
3120 glEnd();
3121 checkGLcall("glEnd");
3123 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3124 glDisable(GL_ALPHA_TEST);
3125 checkGLcall("glDisable(GL_ALPHA_TEST)");
3128 /* Unbind the texture */
3129 glBindTexture(GL_TEXTURE_2D, 0);
3130 checkGLcall("glEnable glBindTexture");
3132 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3133 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3135 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3136 glDrawBuffer(GL_BACK);
3137 checkGLcall("glDrawBuffer");
3139 /* Restore the color key parameters */
3140 Src->CKeyFlags = oldCKeyFlags;
3141 This->SrcBltCKey = oldBltCKey;
3143 LEAVE_GL();
3145 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3146 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3147 * is outdated now
3149 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3150 /* TODO: This should be moved to ModifyLocation() */
3151 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3152 This->Flags |= SFLAG_INTEXTURE;
3155 return WINED3D_OK;
3156 } else {
3157 /* Source-Less Blit to render target */
3158 if (Flags & WINEDDBLT_COLORFILL) {
3159 /* This is easy to handle for the D3D Device... */
3160 DWORD color;
3162 TRACE("Colorfill\n");
3164 /* The color as given in the Blt function is in the format of the frame-buffer...
3165 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3167 if (This->resource.format == WINED3DFMT_P8) {
3168 if (This->palette) {
3169 color = ((0xFF000000) |
3170 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3171 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3172 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3173 } else {
3174 color = 0xFF000000;
3177 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3178 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3179 color = 0xFFFFFFFF;
3180 } else {
3181 color = ((0xFF000000) |
3182 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3183 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3184 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3187 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3188 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3189 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3191 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3192 color = DDBltFx->u5.dwFillColor;
3194 else {
3195 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3196 return WINED3DERR_INVALIDCALL;
3199 TRACE("Calling GetSwapChain with mydevice = %p\n", myDevice);
3200 if(dstSwapchain && dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]) {
3201 glDrawBuffer(GL_BACK);
3202 checkGLcall("glDrawBuffer(GL_BACK)");
3203 } else if (dstSwapchain && This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer) {
3204 glDrawBuffer(GL_FRONT);
3205 checkGLcall("glDrawBuffer(GL_FRONT)");
3206 } else if(This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3207 glDrawBuffer(myDevice->offscreenBuffer);
3208 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer3)");
3209 } else {
3210 TRACE("Surface is higher back buffer, falling back to software\n");
3211 return WINED3DERR_INVALIDCALL;
3214 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3216 IWineD3DDevice_Clear( (IWineD3DDevice *) myDevice,
3217 1 /* Number of rectangles */,
3218 &rect,
3219 WINED3DCLEAR_TARGET,
3220 color,
3221 0.0 /* Z */,
3222 0 /* Stencil */);
3224 /* Restore the original draw buffer */
3225 if(!dstSwapchain) {
3226 glDrawBuffer(myDevice->offscreenBuffer);
3227 } else if(dstSwapchain->backBuffer && dstSwapchain->backBuffer[0]) {
3228 glDrawBuffer(GL_BACK);
3230 vcheckGLcall("glDrawBuffer");
3232 return WINED3D_OK;
3236 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3237 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3238 return WINED3DERR_INVALIDCALL;
3241 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3243 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3244 float depth;
3246 if (Flags & WINEDDBLT_DEPTHFILL) {
3247 switch(This->resource.format) {
3248 case WINED3DFMT_D16:
3249 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3250 break;
3251 case WINED3DFMT_D15S1:
3252 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3253 break;
3254 case WINED3DFMT_D24S8:
3255 case WINED3DFMT_D24X8:
3256 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3257 break;
3258 case WINED3DFMT_D32:
3259 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3260 break;
3261 default:
3262 depth = 0.0;
3263 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3266 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3267 DestRect == NULL ? 0 : 1,
3268 (WINED3DRECT *) DestRect,
3269 WINED3DCLEAR_ZBUFFER,
3270 0x00000000,
3271 depth,
3272 0x00000000);
3275 FIXME("(%p): Unsupp depthstencil blit\n", This);
3276 return WINED3DERR_INVALIDCALL;
3279 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3280 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3281 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3282 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3283 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3284 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3286 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3287 * except depth blits, which seem to work
3289 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3290 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3291 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3292 return WINED3DERR_INVALIDCALL;
3293 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3294 TRACE("Z Blit override handled the blit\n");
3295 return WINED3D_OK;
3299 /* Special cases for RenderTargets */
3300 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3301 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3302 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3305 /* For the rest call the X11 surface implementation.
3306 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3307 * other Blts are rather rare
3309 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3312 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3313 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3314 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3315 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3316 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3318 if(myDevice->inScene &&
3319 (iface == myDevice->stencilBufferTarget ||
3320 (Source && Source == myDevice->stencilBufferTarget))) {
3321 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3322 return WINED3DERR_INVALIDCALL;
3325 /* Special cases for RenderTargets */
3326 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3327 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3329 RECT SrcRect, DstRect;
3330 DWORD Flags=0;
3332 if(rsrc) {
3333 SrcRect.left = rsrc->left;
3334 SrcRect.top= rsrc->top;
3335 SrcRect.bottom = rsrc->bottom;
3336 SrcRect.right = rsrc->right;
3337 } else {
3338 SrcRect.left = 0;
3339 SrcRect.top = 0;
3340 SrcRect.right = srcImpl->currentDesc.Width;
3341 SrcRect.bottom = srcImpl->currentDesc.Height;
3344 DstRect.left = dstx;
3345 DstRect.top=dsty;
3346 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3347 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3349 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3350 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3351 Flags |= WINEDDBLT_KEYSRC;
3352 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3353 Flags |= WINEDDBLT_KEYDEST;
3354 if(trans & WINEDDBLTFAST_WAIT)
3355 Flags |= WINEDDBLT_WAIT;
3356 if(trans & WINEDDBLTFAST_DONOTWAIT)
3357 Flags |= WINEDDBLT_DONOTWAIT;
3359 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3363 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3366 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3367 /** Check against the maximum texture sizes supported by the video card **/
3368 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3369 unsigned int pow2Width, pow2Height;
3370 const GlPixelFormatDesc *glDesc;
3372 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3373 /* Setup some glformat defaults */
3374 This->glDescription.glFormat = glDesc->glFormat;
3375 This->glDescription.glFormatInternal = glDesc->glInternal;
3376 This->glDescription.glType = glDesc->glType;
3378 This->glDescription.textureName = 0;
3379 This->glDescription.target = GL_TEXTURE_2D;
3381 /* Non-power2 support */
3382 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3383 pow2Width = This->currentDesc.Width;
3384 pow2Height = This->currentDesc.Height;
3385 } else {
3386 /* Find the nearest pow2 match */
3387 pow2Width = pow2Height = 1;
3388 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3389 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3391 This->pow2Width = pow2Width;
3392 This->pow2Height = pow2Height;
3394 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3395 WINED3DFORMAT Format = This->resource.format;
3396 /** TODO: add support for non power two compressed textures **/
3397 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3398 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3399 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3400 This, This->currentDesc.Width, This->currentDesc.Height);
3401 return WINED3DERR_NOTAVAILABLE;
3405 if(pow2Width != This->currentDesc.Width ||
3406 pow2Height != This->currentDesc.Height) {
3407 This->Flags |= SFLAG_NONPOW2;
3410 TRACE("%p\n", This);
3411 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3412 /* one of three options
3413 1: Do the same as we do with nonpow 2 and scale the texture, (any texture ops would require the texture to be scaled which is potentially slow)
3414 2: Set the texture to the maximum size (bad idea)
3415 3: WARN and return WINED3DERR_NOTAVAILABLE;
3416 4: Create the surface, but allow it to be used only for DirectDraw Blts. Some apps(e.g. Swat 3) create textures with a Height of 16 and a Width > 3000 and blt 16x16 letter areas from them to the render target.
3418 WARN("(%p) Creating an oversized surface\n", This);
3419 This->Flags |= SFLAG_OVERSIZE;
3421 /* This will be initialized on the first blt */
3422 This->glRect.left = 0;
3423 This->glRect.top = 0;
3424 This->glRect.right = 0;
3425 This->glRect.bottom = 0;
3426 } else {
3427 /* No oversize, gl rect is the full texture size */
3428 This->Flags &= ~SFLAG_OVERSIZE;
3429 This->glRect.left = 0;
3430 This->glRect.top = 0;
3431 This->glRect.right = This->pow2Width;
3432 This->glRect.bottom = This->pow2Height;
3435 This->Flags |= SFLAG_INSYSMEM;
3437 return WINED3D_OK;
3440 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3441 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3443 TRACE("(%p)->(%s, %s)\n", iface,
3444 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3445 persistent ? "TRUE" : "FALSE");
3447 /* TODO: For offscreen textures with fbo offscreen rendering the drawable is the same as the texture.*/
3448 if(persistent) {
3449 This->Flags &= ~SFLAG_LOCATIONS;
3450 This->Flags |= flag;
3451 } else {
3452 This->Flags &= ~flag;
3456 struct coords {
3457 int x, y, z;
3460 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
3461 struct coords coords[4];
3462 RECT rect;
3463 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3465 if(rect_in) {
3466 rect = *rect_in;
3467 } else {
3468 rect.left = 0;
3469 rect.top = 0;
3470 rect.right = This->currentDesc.Width;
3471 rect.bottom = This->currentDesc.Height;
3474 ActivateContext(device, device->render_targets[0], CTXUSAGE_BLIT);
3475 ENTER_GL();
3477 if(This->glDescription.target == GL_TEXTURE_2D) {
3478 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
3479 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
3480 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3481 checkGLcall("glTexParameteri");
3482 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3483 checkGLcall("glTexParameteri");
3485 coords[0].x = rect.left / This->pow2Width;
3486 coords[0].z = 0;
3488 coords[1].x = rect.left / This->pow2Width;
3489 coords[1].z = 0;
3491 coords[2].x = rect.right / This->pow2Width;
3492 coords[2].z = 0;
3494 coords[3].x = rect.right / This->pow2Width;
3495 coords[3].z = 0;
3497 coords[0].y = rect.top / This->pow2Height;
3498 coords[1].y = rect.bottom / This->pow2Height;
3499 coords[2].y = rect.bottom / This->pow2Height;
3500 coords[3].y = rect.top / This->pow2Height;
3501 } else {
3502 /* Must be a cube map */
3503 glDisable(GL_TEXTURE_2D);
3504 checkGLcall("glDisable(GL_TEXTURE_2D)");
3505 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
3506 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
3507 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
3508 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
3509 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3510 checkGLcall("glTexParameteri");
3511 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3512 checkGLcall("glTexParameteri");
3514 switch(This->glDescription.target) {
3515 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3516 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
3517 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3518 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3519 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
3520 break;
3522 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3523 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3524 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
3525 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
3526 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3527 break;
3529 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3530 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
3531 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3532 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3533 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
3534 break;
3536 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3537 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3538 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3539 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3540 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3541 break;
3543 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3544 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3545 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3546 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
3547 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
3548 break;
3550 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
3551 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
3552 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
3553 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3554 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3556 default:
3557 ERR("Unexpected texture target\n");
3558 LEAVE_GL();
3559 return;
3563 glBegin(GL_QUADS);
3564 glTexCoord3iv((GLint *) &coords[0]);
3565 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
3567 glTexCoord3iv((GLint *) &coords[1]);
3568 glVertex2i(0, device->render_offscreen ? rect.top : rect.bottom);
3570 glTexCoord3iv((GLint *) &coords[2]);
3571 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
3573 glTexCoord3iv((GLint *) &coords[3]);
3574 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
3575 glEnd();
3576 checkGLcall("glEnd");
3578 if(This->glDescription.target != GL_TEXTURE_2D) {
3579 glEnable(GL_TEXTURE_2D);
3580 checkGLcall("glEnable(GL_TEXTURE_2D)");
3581 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3582 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3584 LEAVE_GL();
3587 /*****************************************************************************
3588 * IWineD3DSurface::LoadLocation
3590 * Copies the current surface data from wherever it is to the requested
3591 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
3592 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
3593 * multiple locations, the gl texture is prefered over the drawable, which is
3594 * prefered over system memory. The PBO counts as system memory. If rect is
3595 * not NULL, only the specified rectangle is copied(only supported for
3596 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
3597 * location is marked up to date after the copy.
3599 * Parameters:
3600 * flag: Surface location flag to be updated
3601 * rect: rectangle to be copied
3603 * Returns:
3604 * WINED3D_OK on success
3605 * WINED3DERR_DEVICELOST on an internal error
3607 *****************************************************************************/
3608 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
3609 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3610 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3611 GLenum format, internal, type;
3612 CONVERT_TYPES convert;
3613 int bpp;
3614 int width, pitch, outpitch;
3615 BYTE *mem;
3617 TRACE("(%p)->(%s, %p)\n", iface,
3618 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3619 rect);
3620 if(rect) {
3621 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
3624 /* TODO: For fbo targets, texture == drawable */
3625 if(This->Flags & flag) {
3626 TRACE("Location already up to date\n");
3627 return WINED3D_OK;
3630 if(!(This->Flags & SFLAG_LOCATIONS)) {
3631 ERR("Surface does not have any up to date location\n");
3632 This->Flags |= SFLAG_LOST;
3633 return WINED3DERR_DEVICELOST;
3636 if(flag == SFLAG_INSYSMEM) {
3637 surface_prepare_system_memory(This);
3639 /* Download the surface to system memory */
3640 if(This->Flags & SFLAG_INTEXTURE) {
3641 surface_download_data(This);
3642 } else {
3643 read_from_framebuffer(This, rect,
3644 This->resource.allocatedMemory,
3645 IWineD3DSurface_GetPitch(iface));
3647 } else if(flag == SFLAG_INDRAWABLE) {
3648 if(This->Flags & SFLAG_INTEXTURE) {
3649 surface_blt_to_drawable(This, rect);
3650 } else {
3651 flush_to_framebuffer_drawpixels(This);
3653 } else /* if(flag == SFLAG_INTEXTURE) */ {
3654 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
3656 if (This->Flags & SFLAG_INDRAWABLE) {
3657 GLint prevRead;
3659 ENTER_GL();
3660 glGetIntegerv(GL_READ_BUFFER, &prevRead);
3661 vcheckGLcall("glGetIntegerv");
3662 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
3663 vcheckGLcall("glReadBuffer");
3665 if(!(This->Flags & SFLAG_ALLOCATED)) {
3666 surface_allocate_surface(This, internal, This->pow2Width,
3667 This->pow2Height, format, type);
3670 clear_unused_channels(This);
3672 glCopyTexSubImage2D(This->glDescription.target,
3673 This->glDescription.level,
3674 0, 0, 0, 0,
3675 This->currentDesc.Width,
3676 This->currentDesc.Height);
3677 checkGLcall("glCopyTexSubImage2D");
3679 glReadBuffer(prevRead);
3680 vcheckGLcall("glReadBuffer");
3682 LEAVE_GL();
3684 TRACE("Updated target %d\n", This->glDescription.target);
3685 } else {
3686 /* The only place where LoadTexture() might get called when isInDraw=1
3687 * is ActivateContext where lastActiveRenderTarget is preloaded.
3689 if(iface == device->lastActiveRenderTarget && device->isInDraw)
3690 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
3692 /* Otherwise: System memory copy must be most up to date */
3694 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
3695 This->Flags |= SFLAG_GLCKEY;
3696 This->glCKey = This->SrcBltCKey;
3698 else This->Flags &= ~SFLAG_GLCKEY;
3700 /* The width is in 'length' not in bytes */
3701 width = This->currentDesc.Width;
3702 pitch = IWineD3DSurface_GetPitch(iface);
3704 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
3705 int height = This->currentDesc.Height;
3707 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
3708 outpitch = width * bpp;
3709 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
3711 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
3712 if(!mem) {
3713 ERR("Out of memory %d, %d!\n", outpitch, height);
3714 return WINED3DERR_OUTOFVIDEOMEMORY;
3716 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
3718 This->Flags |= SFLAG_CONVERTED;
3719 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
3720 d3dfmt_p8_upload_palette(iface, convert);
3721 This->Flags &= ~SFLAG_CONVERTED;
3722 mem = This->resource.allocatedMemory;
3723 } else {
3724 This->Flags &= ~SFLAG_CONVERTED;
3725 mem = This->resource.allocatedMemory;
3728 /* Make sure the correct pitch is used */
3729 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
3731 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
3732 TRACE("non power of two support\n");
3733 if(!(This->Flags & SFLAG_ALLOCATED)) {
3734 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
3736 if (mem || (This->Flags & SFLAG_PBO)) {
3737 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
3739 } else {
3740 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
3741 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
3743 if(!(This->Flags & SFLAG_ALLOCATED)) {
3744 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
3746 if (mem || (This->Flags & SFLAG_PBO)) {
3747 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
3751 /* Restore the default pitch */
3752 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
3754 /* Don't delete PBO memory */
3755 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
3756 HeapFree(GetProcessHeap(), 0, mem);
3760 if(rect == NULL) {
3761 This->Flags |= flag;
3764 return WINED3D_OK;
3767 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3769 /* IUnknown */
3770 IWineD3DBaseSurfaceImpl_QueryInterface,
3771 IWineD3DBaseSurfaceImpl_AddRef,
3772 IWineD3DSurfaceImpl_Release,
3773 /* IWineD3DResource */
3774 IWineD3DBaseSurfaceImpl_GetParent,
3775 IWineD3DBaseSurfaceImpl_GetDevice,
3776 IWineD3DBaseSurfaceImpl_SetPrivateData,
3777 IWineD3DBaseSurfaceImpl_GetPrivateData,
3778 IWineD3DBaseSurfaceImpl_FreePrivateData,
3779 IWineD3DBaseSurfaceImpl_SetPriority,
3780 IWineD3DBaseSurfaceImpl_GetPriority,
3781 IWineD3DSurfaceImpl_PreLoad,
3782 IWineD3DBaseSurfaceImpl_GetType,
3783 /* IWineD3DSurface */
3784 IWineD3DBaseSurfaceImpl_GetContainer,
3785 IWineD3DBaseSurfaceImpl_GetDesc,
3786 IWineD3DSurfaceImpl_LockRect,
3787 IWineD3DSurfaceImpl_UnlockRect,
3788 IWineD3DSurfaceImpl_GetDC,
3789 IWineD3DSurfaceImpl_ReleaseDC,
3790 IWineD3DSurfaceImpl_Flip,
3791 IWineD3DSurfaceImpl_Blt,
3792 IWineD3DBaseSurfaceImpl_GetBltStatus,
3793 IWineD3DBaseSurfaceImpl_GetFlipStatus,
3794 IWineD3DBaseSurfaceImpl_IsLost,
3795 IWineD3DBaseSurfaceImpl_Restore,
3796 IWineD3DSurfaceImpl_BltFast,
3797 IWineD3DBaseSurfaceImpl_GetPalette,
3798 IWineD3DBaseSurfaceImpl_SetPalette,
3799 IWineD3DBaseSurfaceImpl_RealizePalette,
3800 IWineD3DBaseSurfaceImpl_SetColorKey,
3801 IWineD3DBaseSurfaceImpl_GetPitch,
3802 IWineD3DSurfaceImpl_SetMem,
3803 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
3804 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
3805 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
3806 IWineD3DBaseSurfaceImpl_UpdateOverlay,
3807 IWineD3DBaseSurfaceImpl_SetClipper,
3808 IWineD3DBaseSurfaceImpl_GetClipper,
3809 /* Internal use: */
3810 IWineD3DSurfaceImpl_AddDirtyRect,
3811 IWineD3DSurfaceImpl_LoadTexture,
3812 IWineD3DSurfaceImpl_SaveSnapshot,
3813 IWineD3DBaseSurfaceImpl_SetContainer,
3814 IWineD3DSurfaceImpl_SetGlTextureDesc,
3815 IWineD3DSurfaceImpl_GetGlDesc,
3816 IWineD3DSurfaceImpl_GetData,
3817 IWineD3DSurfaceImpl_SetFormat,
3818 IWineD3DSurfaceImpl_PrivateSetup,
3819 IWineD3DSurfaceImpl_ModifyLocation,
3820 IWineD3DSurfaceImpl_LoadLocation