ddraw: Add missing pitch in callback from EnumDisplayModes().
[wine.git] / dlls / wined3d / surface.c
blobceeb1855f46d919777718032888a0ba472c49eae
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_bind_and_dirtify(IWineD3DSurfaceImpl *This) {
40 /* Make sure that a proper texture unit is selected, bind the texture
41 * and dirtify the sampler to restore the texture on the next draw. */
42 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
43 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
44 checkGLcall("glActiveTextureARB");
46 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
47 IWineD3DSurface_BindTexture((IWineD3DSurface *)This);
50 /* This call just downloads data, the caller is responsible for activating the
51 * right context and binding the correct texture. */
52 static void surface_download_data(IWineD3DSurfaceImpl *This) {
53 if (0 == This->glDescription.textureName) {
54 ERR("Surface does not have a texture, but SFLAG_INTEXTURE is set\n");
55 return;
58 if(This->Flags & SFLAG_CONVERTED) {
59 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(This->resource.format));
60 return;
63 ENTER_GL();
65 if (This->resource.format == WINED3DFMT_DXT1 ||
66 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
67 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
68 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
69 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
70 } else {
71 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
72 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
74 if(This->Flags & SFLAG_PBO) {
75 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
76 checkGLcall("glBindBufferARB");
77 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
78 checkGLcall("glGetCompressedTexImageARB()");
79 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
80 checkGLcall("glBindBufferARB");
81 } else {
82 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
83 checkGLcall("glGetCompressedTexImageARB()");
86 LEAVE_GL();
87 } else {
88 void *mem;
89 int src_pitch = 0;
90 int dst_pitch = 0;
92 if (This->Flags & SFLAG_NONPOW2) {
93 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
94 src_pitch = This->bytesPerPixel * This->pow2Width;
95 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
96 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
97 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
98 } else {
99 mem = This->resource.allocatedMemory;
102 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
103 This->glDescription.glFormat, This->glDescription.glType, mem);
105 if(This->Flags & SFLAG_PBO) {
106 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
107 checkGLcall("glBindBufferARB");
109 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
110 This->glDescription.glType, NULL);
111 checkGLcall("glGetTexImage()");
113 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
114 checkGLcall("glBindBufferARB");
115 } else {
116 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
117 This->glDescription.glType, mem);
118 checkGLcall("glGetTexImage()");
120 LEAVE_GL();
122 if (This->Flags & SFLAG_NONPOW2) {
123 LPBYTE src_data, dst_data;
124 int y;
126 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
127 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
128 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
130 * We're doing this...
132 * instead of boxing the texture :
133 * |<-texture width ->| -->pow2width| /\
134 * |111111111111111111| | |
135 * |222 Texture 222222| boxed empty | texture height
136 * |3333 Data 33333333| | |
137 * |444444444444444444| | \/
138 * ----------------------------------- |
139 * | boxed empty | boxed empty | pow2height
140 * | | | \/
141 * -----------------------------------
144 * we're repacking the data to the expected texture width
146 * |<-texture width ->| -->pow2width| /\
147 * |111111111111111111222222222222222| |
148 * |222333333333333333333444444444444| texture height
149 * |444444 | |
150 * | | \/
151 * | | |
152 * | empty | pow2height
153 * | | \/
154 * -----------------------------------
156 * == is the same as
158 * |<-texture width ->| /\
159 * |111111111111111111|
160 * |222222222222222222|texture height
161 * |333333333333333333|
162 * |444444444444444444| \/
163 * --------------------
165 * this also means that any references to allocatedMemory should work with the data as if were a
166 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
168 * internally the texture is still stored in a boxed format so any references to textureName will
169 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
171 * Performance should not be an issue, because applications normally do not lock the surfaces when
172 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
173 * and doesn't have to be re-read.
175 src_data = mem;
176 dst_data = This->resource.allocatedMemory;
177 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
178 for (y = 1 ; y < This->currentDesc.Height; y++) {
179 /* skip the first row */
180 src_data += src_pitch;
181 dst_data += dst_pitch;
182 memcpy(dst_data, src_data, dst_pitch);
185 HeapFree(GetProcessHeap(), 0, mem);
189 /* Surface has now been downloaded */
190 This->Flags |= SFLAG_INSYSMEM;
193 /* This call just uploads data, the caller is responsible for activating the
194 * right context and binding the correct texture. */
195 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
196 if (This->resource.format == WINED3DFMT_DXT1 ||
197 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
198 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
199 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
200 FIXME("Using DXT1/3/5 without advertized support\n");
201 } else {
202 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
203 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
204 * function uses glCompressedTexImage2D instead of the SubImage call
206 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
207 ENTER_GL();
209 if(This->Flags & SFLAG_PBO) {
210 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
211 checkGLcall("glBindBufferARB");
212 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
214 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
215 width, height, 0 /* border */, This->resource.size, NULL));
216 checkGLcall("glCompressedTexSubImage2D");
218 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
219 checkGLcall("glBindBufferARB");
220 } else {
221 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
222 width, height, 0 /* border */, This->resource.size, data));
223 checkGLcall("glCompressedTexSubImage2D");
225 LEAVE_GL();
227 } else {
228 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
229 ENTER_GL();
231 if(This->Flags & SFLAG_PBO) {
232 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
233 checkGLcall("glBindBufferARB");
234 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
236 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
237 checkGLcall("glTexSubImage2D");
239 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
240 checkGLcall("glBindBufferARB");
242 else {
243 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
244 checkGLcall("glTexSubImage2D");
247 LEAVE_GL();
251 /* This call just allocates the texture, the caller is responsible for
252 * activating the right context and binding the correct texture. */
253 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
254 BOOL enable_client_storage = FALSE;
255 BYTE *mem = NULL;
257 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,
258 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
260 if (This->resource.format == WINED3DFMT_DXT1 ||
261 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
262 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
263 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
264 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
266 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
267 * once, unfortunately
269 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
270 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
271 This->Flags |= SFLAG_CLIENT;
272 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
273 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
274 width, height, 0 /* border */, This->resource.size, mem));
277 return;
280 ENTER_GL();
282 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
283 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
284 /* In some cases we want to disable client storage.
285 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
286 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
287 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
288 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
289 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
291 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
292 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
293 This->Flags &= ~SFLAG_CLIENT;
294 enable_client_storage = TRUE;
295 } else {
296 This->Flags |= SFLAG_CLIENT;
298 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
299 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
301 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
304 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
305 checkGLcall("glTexImage2D");
307 if(enable_client_storage) {
308 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
309 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
311 LEAVE_GL();
313 This->Flags |= SFLAG_ALLOCATED;
316 /* In D3D the depth stencil dimensions have to be greater than or equal to the
317 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
318 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
319 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
320 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
321 renderbuffer_entry_t *entry;
322 GLuint renderbuffer = 0;
323 unsigned int src_width, src_height;
325 src_width = This->pow2Width;
326 src_height = This->pow2Height;
328 /* A depth stencil smaller than the render target is not valid */
329 if (width > src_width || height > src_height) return;
331 /* Remove any renderbuffer set if the sizes match */
332 if (width == src_width && height == src_height) {
333 This->current_renderbuffer = NULL;
334 return;
337 /* Look if we've already got a renderbuffer of the correct dimensions */
338 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
339 if (entry->width == width && entry->height == height) {
340 renderbuffer = entry->id;
341 This->current_renderbuffer = entry;
342 break;
346 if (!renderbuffer) {
347 const GlPixelFormatDesc *glDesc;
348 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
350 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
351 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
352 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
354 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
355 entry->width = width;
356 entry->height = height;
357 entry->id = renderbuffer;
358 list_add_head(&This->renderbuffers, &entry->entry);
360 This->current_renderbuffer = entry;
363 checkGLcall("set_compatible_renderbuffer");
366 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
367 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
368 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
370 TRACE("(%p) : swapchain %p\n", This, swapchain);
372 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
373 TRACE("Returning GL_BACK\n");
374 return GL_BACK;
375 } else if (swapchain_impl->frontBuffer == iface) {
376 TRACE("Returning GL_FRONT\n");
377 return GL_FRONT;
380 FIXME("Higher back buffer, returning GL_BACK\n");
381 return GL_BACK;
384 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
385 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
386 ULONG ref = InterlockedDecrement(&This->resource.ref);
387 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
388 if (ref == 0) {
389 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
390 renderbuffer_entry_t *entry, *entry2;
391 TRACE("(%p) : cleaning up\n", This);
393 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
395 /* Need a context to destroy the texture. Use the currently active render target, but only if
396 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
397 * When destroying the primary rt, Uninit3D will activate a context before doing anything
399 if(device->render_targets && device->render_targets[0]) {
400 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
403 TRACE("Deleting texture %d\n", This->glDescription.textureName);
404 ENTER_GL();
405 glDeleteTextures(1, &This->glDescription.textureName);
406 LEAVE_GL();
409 if(This->Flags & SFLAG_PBO) {
410 /* Delete the PBO */
411 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
414 if(This->Flags & SFLAG_DIBSECTION) {
415 /* Release the DC */
416 SelectObject(This->hDC, This->dib.holdbitmap);
417 DeleteDC(This->hDC);
418 /* Release the DIB section */
419 DeleteObject(This->dib.DIBsection);
420 This->dib.bitmap_data = NULL;
421 This->resource.allocatedMemory = NULL;
423 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
425 HeapFree(GetProcessHeap(), 0, This->palette9);
427 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
428 if(iface == device->ddraw_primary)
429 device->ddraw_primary = NULL;
431 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
432 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
433 HeapFree(GetProcessHeap(), 0, entry);
436 TRACE("(%p) Released\n", This);
437 HeapFree(GetProcessHeap(), 0, This);
440 return ref;
443 /* ****************************************************
444 IWineD3DSurface IWineD3DResource parts follow
445 **************************************************** */
447 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
448 /* TODO: check for locks */
449 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
450 IWineD3DBaseTexture *baseTexture = NULL;
451 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
453 TRACE("(%p)Checking to see if the container is a base texture\n", This);
454 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
455 TRACE("Passing to container\n");
456 IWineD3DBaseTexture_PreLoad(baseTexture);
457 IWineD3DBaseTexture_Release(baseTexture);
458 } else {
459 TRACE("(%p) : About to load surface\n", This);
461 if(!device->isInDraw) {
462 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
465 ENTER_GL();
466 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
467 if (!This->glDescription.level) {
468 if (!This->glDescription.textureName) {
469 glGenTextures(1, &This->glDescription.textureName);
470 checkGLcall("glGenTextures");
471 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
473 glBindTexture(This->glDescription.target, This->glDescription.textureName);
474 checkGLcall("glBindTexture");
475 IWineD3DSurface_LoadTexture(iface, FALSE);
476 /* This is where we should be reducing the amount of GLMemoryUsed */
477 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
478 /* assume this is a coding error not a real error for now */
479 FIXME("Mipmap surface has a glTexture bound to it!\n");
481 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
482 /* Tell opengl to try and keep this texture in video ram (well mostly) */
483 GLclampf tmp;
484 tmp = 0.9f;
485 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
487 LEAVE_GL();
489 return;
492 /* ******************************************************
493 IWineD3DSurface IWineD3DSurface parts follow
494 ****************************************************** */
496 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
497 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
498 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
499 if (This->glDescription.textureName == 0 && textureName != 0) {
500 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
501 IWineD3DSurface_AddDirtyRect(iface, NULL);
503 This->glDescription.textureName = textureName;
504 This->glDescription.target = target;
505 This->Flags &= ~SFLAG_ALLOCATED;
508 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
509 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
510 TRACE("(%p) : returning %p\n", This, &This->glDescription);
511 *glDescription = &This->glDescription;
514 /* TODO: think about moving this down to resource? */
515 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
516 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
517 /* 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 */
518 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
519 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
521 return (CONST void*)(This->resource.allocatedMemory);
524 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
525 IWineD3DSwapChainImpl *swapchain;
526 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
527 BYTE *mem;
528 GLint fmt;
529 GLint type;
530 BYTE *row, *top, *bottom;
531 int i;
532 BOOL bpp;
533 RECT local_rect;
534 BOOL srcIsUpsideDown;
536 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
537 static BOOL warned = FALSE;
538 if(!warned) {
539 ERR("The application tries to lock the render target, but render target locking is disabled\n");
540 warned = TRUE;
542 return;
545 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
546 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
547 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
548 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
549 * context->last_was_blit set on the unlock.
551 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
552 ENTER_GL();
554 /* Select the correct read buffer, and give some debug output.
555 * There is no need to keep track of the current read buffer or reset it, every part of the code
556 * that reads sets the read buffer as desired.
558 if(!swapchain) {
559 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
560 * Read from the back buffer
562 TRACE("Locking offscreen render target\n");
563 glReadBuffer(myDevice->offscreenBuffer);
564 srcIsUpsideDown = TRUE;
565 } else {
566 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
567 TRACE("Locking %#x buffer\n", buffer);
568 glReadBuffer(buffer);
569 checkGLcall("glReadBuffer");
571 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
572 srcIsUpsideDown = FALSE;
575 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
576 if(!rect) {
577 local_rect.left = 0;
578 local_rect.top = 0;
579 local_rect.right = This->currentDesc.Width;
580 local_rect.bottom = This->currentDesc.Height;
581 } else {
582 local_rect = *rect;
584 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
586 switch(This->resource.format)
588 case WINED3DFMT_P8:
590 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
591 /* In case of P8 render targets the index is stored in the alpha component */
592 fmt = GL_ALPHA;
593 type = GL_UNSIGNED_BYTE;
594 mem = dest;
595 bpp = This->bytesPerPixel;
596 } else {
597 /* GL can't return palettized data, so read ARGB pixels into a
598 * separate block of memory and convert them into palettized format
599 * in software. Slow, but if the app means to use palettized render
600 * targets and locks it...
602 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
603 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
604 * for the color channels when palettizing the colors.
606 fmt = GL_RGB;
607 type = GL_UNSIGNED_BYTE;
608 pitch *= 3;
609 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
610 if(!mem) {
611 ERR("Out of memory\n");
612 LEAVE_GL();
613 return;
615 bpp = This->bytesPerPixel * 3;
618 break;
620 default:
621 mem = dest;
622 fmt = This->glDescription.glFormat;
623 type = This->glDescription.glType;
624 bpp = This->bytesPerPixel;
627 if(This->Flags & SFLAG_PBO) {
628 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
629 checkGLcall("glBindBufferARB");
632 glReadPixels(local_rect.left, local_rect.top,
633 local_rect.right - local_rect.left,
634 local_rect.bottom - local_rect.top,
635 fmt, type, mem);
636 vcheckGLcall("glReadPixels");
638 if(This->Flags & SFLAG_PBO) {
639 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
640 checkGLcall("glBindBufferARB");
642 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
643 * to get a pointer to it and perform the flipping in software. This is a lot
644 * faster than calling glReadPixels for each line. In case we want more speed
645 * we should rerender it flipped in a FBO and read the data back from the FBO. */
646 if(!srcIsUpsideDown) {
647 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
648 checkGLcall("glBindBufferARB");
650 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
651 checkGLcall("glMapBufferARB");
655 /* TODO: Merge this with the palettization loop below for P8 targets */
656 if(!srcIsUpsideDown) {
657 UINT len, off;
658 /* glReadPixels returns the image upside down, and there is no way to prevent this.
659 Flip the lines in software */
660 len = (local_rect.right - local_rect.left) * bpp;
661 off = local_rect.left * bpp;
663 row = HeapAlloc(GetProcessHeap(), 0, len);
664 if(!row) {
665 ERR("Out of memory\n");
666 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
667 LEAVE_GL();
668 return;
671 top = mem + pitch * local_rect.top;
672 bottom = ((BYTE *) mem) + pitch * ( local_rect.bottom - local_rect.top - 1);
673 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
674 memcpy(row, top + off, len);
675 memcpy(top + off, bottom + off, len);
676 memcpy(bottom + off, row, len);
677 top += pitch;
678 bottom -= pitch;
680 HeapFree(GetProcessHeap(), 0, row);
682 /* Unmap the temp PBO buffer */
683 if(This->Flags & SFLAG_PBO) {
684 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
685 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
689 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
690 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
691 * the same color but we have no choice.
692 * In case of render targets, the index is stored in the alpha component so no conversion is needed.
694 if((This->resource.format == WINED3DFMT_P8) && !(This->resource.usage & WINED3DUSAGE_RENDERTARGET)) {
695 PALETTEENTRY *pal;
696 DWORD width = pitch / 3;
697 int x, y, c;
698 if(This->palette) {
699 pal = This->palette->palents;
700 } else {
701 pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
704 for(y = local_rect.top; y < local_rect.bottom; y++) {
705 for(x = local_rect.left; x < local_rect.right; x++) {
706 /* start lines pixels */
707 BYTE *blue = (BYTE *) ((BYTE *) mem) + y * pitch + x * (sizeof(BYTE) * 3);
708 BYTE *green = blue + 1;
709 BYTE *red = green + 1;
711 for(c = 0; c < 256; c++) {
712 if(*red == pal[c].peRed &&
713 *green == pal[c].peGreen &&
714 *blue == pal[c].peBlue)
716 *((BYTE *) dest + y * width + x) = c;
717 break;
722 HeapFree(GetProcessHeap(), 0, mem);
724 LEAVE_GL();
727 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
728 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
729 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
730 * changed
732 if(!(This->Flags & SFLAG_DYNLOCK)) {
733 This->lockCount++;
734 /* MAXLOCKCOUNT is defined in wined3d_private.h */
735 if(This->lockCount > MAXLOCKCOUNT) {
736 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
737 This->Flags |= SFLAG_DYNLOCK;
741 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
742 * Also don't create a PBO for systemmem surfaces.
744 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
745 GLenum error;
746 ENTER_GL();
748 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
749 error = glGetError();
750 if(This->pbo == 0 || error != GL_NO_ERROR) {
751 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
754 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
756 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
757 checkGLcall("glBindBufferARB");
759 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
760 checkGLcall("glBufferDataARB");
762 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
763 checkGLcall("glBindBufferARB");
765 /* We don't need the system memory anymore and we can't even use it for PBOs */
766 if(!(This->Flags & SFLAG_CLIENT)) {
767 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
768 This->resource.heapMemory = NULL;
770 This->resource.allocatedMemory = NULL;
771 This->Flags |= SFLAG_PBO;
772 LEAVE_GL();
773 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
774 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
775 * or a pbo to map
777 if(!This->resource.heapMemory) {
778 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
780 This->resource.allocatedMemory =
781 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
782 if(This->Flags & SFLAG_INSYSMEM) {
783 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
788 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
789 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
790 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
791 IWineD3DSwapChain *swapchain = NULL;
793 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
795 /* This is also done in the base class, but we have to verify this before loading any data from
796 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
797 * may interfere, and all other bad things may happen
799 if (This->Flags & SFLAG_LOCKED) {
800 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
801 return WINED3DERR_INVALIDCALL;
803 This->Flags |= SFLAG_LOCKED;
805 if (!(This->Flags & SFLAG_LOCKABLE))
807 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
810 if (Flags & WINED3DLOCK_DISCARD) {
811 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
812 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
813 This->Flags |= SFLAG_INSYSMEM;
816 if (This->Flags & SFLAG_INSYSMEM) {
817 TRACE("Local copy is up to date, not downloading data\n");
818 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
819 goto lock_end;
822 /* Now download the surface content from opengl
823 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
824 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
826 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
827 if(swapchain || iface == myDevice->render_targets[0]) {
828 const RECT *pass_rect = pRect;
830 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
831 * because most caller functions do not need that. So do that here
833 if(pRect &&
834 pRect->top == 0 &&
835 pRect->left == 0 &&
836 pRect->right == This->currentDesc.Width &&
837 pRect->bottom == This->currentDesc.Height) {
838 pass_rect = NULL;
841 switch(wined3d_settings.rendertargetlock_mode) {
842 case RTL_TEXDRAW:
843 case RTL_TEXTEX:
844 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
845 #if 0
846 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
847 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
848 * This may be faster on some cards
850 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
851 #endif
852 /* drop through */
854 case RTL_AUTO:
855 case RTL_READDRAW:
856 case RTL_READTEX:
857 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
858 break;
860 case RTL_DISABLE:
861 break;
863 if(swapchain) IWineD3DSwapChain_Release(swapchain);
865 } else if(iface == myDevice->stencilBufferTarget) {
866 /** the depth stencil in openGL has a format of GL_FLOAT
867 * which should be good for WINED3DFMT_D16_LOCKABLE
868 * and WINED3DFMT_D16
869 * it is unclear what format the stencil buffer is in except.
870 * 'Each index is converted to fixed point...
871 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
872 * mappings in the table GL_PIXEL_MAP_S_TO_S.
873 * glReadPixels(This->lockedRect.left,
874 * This->lockedRect.bottom - j - 1,
875 * This->lockedRect.right - This->lockedRect.left,
876 * 1,
877 * GL_DEPTH_COMPONENT,
878 * type,
879 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
881 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
882 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
883 * none of that is the case the problem is not in this function :-)
884 ********************************************/
885 FIXME("Depth stencil locking not supported yet\n");
886 } else {
887 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
888 TRACE("locking an ordinary surface\n");
889 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
892 lock_end:
893 if(This->Flags & SFLAG_PBO) {
894 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
895 ENTER_GL();
896 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
897 checkGLcall("glBindBufferARB");
899 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
900 if(This->resource.allocatedMemory) {
901 ERR("The surface already has PBO memory allocated!\n");
904 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
905 checkGLcall("glMapBufferARB");
907 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
908 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
909 checkGLcall("glBindBufferARB");
911 LEAVE_GL();
914 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
915 /* Don't dirtify */
916 } else {
917 IWineD3DBaseTexture *pBaseTexture;
919 * Dirtify on lock
920 * as seen in msdn docs
922 IWineD3DSurface_AddDirtyRect(iface, pRect);
924 /** Dirtify Container if needed */
925 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
926 TRACE("Making container dirty\n");
927 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
928 IWineD3DBaseTexture_Release(pBaseTexture);
929 } else {
930 TRACE("Surface is standalone, no need to dirty the container\n");
934 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
937 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This) {
938 GLint prev_store;
939 GLint prev_rasterpos[4];
940 GLint skipBytes = 0;
941 BOOL storechanged = FALSE, memory_allocated = FALSE;
942 GLint fmt, type;
943 BYTE *mem;
944 UINT bpp;
945 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
946 IWineD3DDeviceImpl *myDevice = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
947 IWineD3DSwapChainImpl *swapchain;
949 /* Activate the correct context for the render target */
950 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
951 ENTER_GL();
953 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
954 if(!swapchain) {
955 /* Primary offscreen render target */
956 TRACE("Offscreen render target\n");
957 glDrawBuffer(myDevice->offscreenBuffer);
958 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
959 } else {
960 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
961 TRACE("Unlocking %#x buffer\n", buffer);
962 glDrawBuffer(buffer);
963 checkGLcall("glDrawBuffer");
965 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
968 glFlush();
969 vcheckGLcall("glFlush");
970 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
971 vcheckGLcall("glIntegerv");
972 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
973 vcheckGLcall("glIntegerv");
974 glPixelZoom(1.0, -1.0);
975 vcheckGLcall("glPixelZoom");
977 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
978 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
979 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
981 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
982 vcheckGLcall("glRasterPos2f");
984 /* Some drivers(radeon dri, others?) don't like exceptions during
985 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
986 * after ReleaseDC. Reading it will cause an exception, which x11drv will
987 * catch to put the dib section in InSync mode, which leads to a crash
988 * and a blocked x server on my radeon card.
990 * The following lines read the dib section so it is put in inSync mode
991 * before glDrawPixels is called and the crash is prevented. There won't
992 * be any interfering gdi accesses, because UnlockRect is called from
993 * ReleaseDC, and the app won't use the dc any more afterwards.
995 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
996 volatile BYTE read;
997 read = This->resource.allocatedMemory[0];
1000 switch (This->resource.format) {
1001 /* No special care needed */
1002 case WINED3DFMT_A4R4G4B4:
1003 case WINED3DFMT_R5G6B5:
1004 case WINED3DFMT_A1R5G5B5:
1005 case WINED3DFMT_R8G8B8:
1006 case WINED3DFMT_X4R4G4B4:
1007 case WINED3DFMT_X1R5G5B5:
1008 type = This->glDescription.glType;
1009 fmt = This->glDescription.glFormat;
1010 mem = This->resource.allocatedMemory;
1011 bpp = This->bytesPerPixel;
1012 break;
1014 /* In the past times we used to set the X channel of X8R8G8B8 and the above formats to
1015 * 1.0 because it happened to fix the intro movie in Pirates. However, this seems wrong.
1016 * If the game uses an X8R8G8B8 back buffer the GL alpha channel should not make any differences,
1017 * and the bug must be somewhere else. If we really have to set the alpha channel to 1.0 in
1018 * this case do it by clearing it after the draw instead of fixing it up in the CPU. Blending
1019 * is disabled via CTXUSAGE_BLIT context setup, so in the glDrawPixels call it does not
1020 * have any effects
1022 case WINED3DFMT_X8R8G8B8:
1023 case WINED3DFMT_A8R8G8B8:
1025 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1026 vcheckGLcall("glPixelStorei");
1027 storechanged = TRUE;
1028 type = This->glDescription.glType;
1029 fmt = This->glDescription.glFormat;
1030 mem = This->resource.allocatedMemory;
1031 bpp = This->bytesPerPixel;
1033 break;
1035 case WINED3DFMT_A2R10G10B10:
1037 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1038 vcheckGLcall("glPixelStorei");
1039 storechanged = TRUE;
1040 type = This->glDescription.glType;
1041 fmt = This->glDescription.glFormat;
1042 mem = This->resource.allocatedMemory;
1043 bpp = This->bytesPerPixel;
1045 break;
1047 case WINED3DFMT_P8:
1049 int height = This->glRect.bottom - This->glRect.top;
1050 type = GL_UNSIGNED_BYTE;
1051 fmt = GL_RGBA;
1053 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * sizeof(DWORD));
1054 if(!mem) {
1055 ERR("Out of memory\n");
1056 goto cleanup;
1058 memory_allocated = TRUE;
1059 d3dfmt_convert_surface(This->resource.allocatedMemory,
1060 mem,
1061 pitch,
1062 pitch,
1063 height,
1064 pitch * 4,
1065 CONVERT_PALETTED,
1066 This);
1067 bpp = This->bytesPerPixel * 4;
1068 pitch *= 4;
1070 break;
1072 default:
1073 FIXME("Unsupported Format %u in locking func\n", This->resource.format);
1075 /* Give it a try */
1076 type = This->glDescription.glType;
1077 fmt = This->glDescription.glFormat;
1078 mem = This->resource.allocatedMemory;
1079 bpp = This->bytesPerPixel;
1082 if(This->Flags & SFLAG_PBO) {
1083 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1084 checkGLcall("glBindBufferARB");
1087 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1088 (This->lockedRect.bottom - This->lockedRect.top)-1,
1089 fmt, type,
1090 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1091 checkGLcall("glDrawPixels");
1093 cleanup:
1094 if(This->Flags & SFLAG_PBO) {
1095 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1096 checkGLcall("glBindBufferARB");
1099 glPixelZoom(1.0,1.0);
1100 vcheckGLcall("glPixelZoom");
1102 glRasterPos3iv(&prev_rasterpos[0]);
1103 vcheckGLcall("glRasterPos3iv");
1105 /* Reset to previous pack row length */
1106 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1107 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1108 if(storechanged) {
1109 glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
1110 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
1113 if(memory_allocated) HeapFree(GetProcessHeap(), 0, mem);
1115 if(!swapchain) {
1116 glDrawBuffer(myDevice->offscreenBuffer);
1117 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1118 } else if(swapchain->backBuffer) {
1119 glDrawBuffer(GL_BACK);
1120 checkGLcall("glDrawBuffer(GL_BACK)");
1121 } else {
1122 glDrawBuffer(GL_FRONT);
1123 checkGLcall("glDrawBuffer(GL_FRONT)");
1125 LEAVE_GL();
1127 return;
1130 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1131 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1132 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1133 IWineD3DSwapChainImpl *swapchain = NULL;
1134 BOOL fullsurface;
1136 if (!(This->Flags & SFLAG_LOCKED)) {
1137 WARN("trying to Unlock an unlocked surf@%p\n", This);
1138 return WINED3DERR_INVALIDCALL;
1141 if (This->Flags & SFLAG_PBO) {
1142 TRACE("Freeing PBO memory\n");
1143 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1144 ENTER_GL();
1145 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1146 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1147 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1148 checkGLcall("glUnmapBufferARB");
1149 LEAVE_GL();
1150 This->resource.allocatedMemory = NULL;
1153 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1155 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1156 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1157 goto unlock_end;
1160 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1161 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1162 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1163 static BOOL warned = FALSE;
1164 if(!warned) {
1165 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1166 warned = TRUE;
1168 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1169 goto unlock_end;
1172 if(This->dirtyRect.left == 0 &&
1173 This->dirtyRect.top == 0 &&
1174 This->dirtyRect.right == This->currentDesc.Width &&
1175 This->dirtyRect.bottom == This->currentDesc.Height) {
1176 fullsurface = TRUE;
1177 } else {
1178 /* TODO: Proper partial rectangle tracking */
1179 fullsurface = FALSE;
1180 This->Flags |= SFLAG_INSYSMEM;
1183 switch(wined3d_settings.rendertargetlock_mode) {
1184 case RTL_READTEX:
1185 case RTL_TEXTEX:
1186 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1187 ENTER_GL();
1188 if (This->glDescription.textureName == 0) {
1189 glGenTextures(1, &This->glDescription.textureName);
1190 checkGLcall("glGenTextures");
1192 glBindTexture(This->glDescription.target, This->glDescription.textureName);
1193 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
1194 LEAVE_GL();
1195 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1196 /* drop through */
1198 case RTL_AUTO:
1199 case RTL_READDRAW:
1200 case RTL_TEXDRAW:
1201 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1202 break;
1205 if(!fullsurface) {
1206 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1207 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1208 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1209 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1210 * not fully up to date because only a subrectangle was read in LockRect.
1212 This->Flags &= ~SFLAG_INSYSMEM;
1213 This->Flags |= SFLAG_INDRAWABLE;
1216 This->dirtyRect.left = This->currentDesc.Width;
1217 This->dirtyRect.top = This->currentDesc.Height;
1218 This->dirtyRect.right = 0;
1219 This->dirtyRect.bottom = 0;
1220 } else if(iface == myDevice->stencilBufferTarget) {
1221 FIXME("Depth Stencil buffer locking is not implemented\n");
1222 } else {
1223 /* The rest should be a normal texture */
1224 IWineD3DBaseTextureImpl *impl;
1225 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1226 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1227 * states need resetting
1229 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1230 if(impl->baseTexture.bindCount) {
1231 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1233 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1237 unlock_end:
1238 This->Flags &= ~SFLAG_LOCKED;
1239 memset(&This->lockedRect, 0, sizeof(RECT));
1240 return WINED3D_OK;
1243 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1244 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1245 WINED3DLOCKED_RECT lock;
1246 HRESULT hr;
1247 RGBQUAD col[256];
1249 TRACE("(%p)->(%p)\n",This,pHDC);
1251 if(This->Flags & SFLAG_USERPTR) {
1252 ERR("Not supported on surfaces with an application-provided surfaces\n");
1253 return WINEDDERR_NODC;
1256 /* Give more detailed info for ddraw */
1257 if (This->Flags & SFLAG_DCINUSE)
1258 return WINEDDERR_DCALREADYCREATED;
1260 /* Can't GetDC if the surface is locked */
1261 if (This->Flags & SFLAG_LOCKED)
1262 return WINED3DERR_INVALIDCALL;
1264 memset(&lock, 0, sizeof(lock)); /* To be sure */
1266 /* Create a DIB section if there isn't a hdc yet */
1267 if(!This->hDC) {
1268 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1269 if(This->Flags & SFLAG_CLIENT) {
1270 IWineD3DSurface_PreLoad(iface);
1273 /* Use the dib section from now on if we are not using a PBO */
1274 if(!(This->Flags & SFLAG_PBO))
1275 This->resource.allocatedMemory = This->dib.bitmap_data;
1278 /* Lock the surface */
1279 hr = IWineD3DSurface_LockRect(iface,
1280 &lock,
1281 NULL,
1284 if(This->Flags & SFLAG_PBO) {
1285 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1286 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1289 if(FAILED(hr)) {
1290 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1291 /* keep the dib section */
1292 return hr;
1295 if(This->resource.format == WINED3DFMT_P8 ||
1296 This->resource.format == WINED3DFMT_A8P8) {
1297 unsigned int n;
1298 if(This->palette) {
1299 PALETTEENTRY ent[256];
1301 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1302 for (n=0; n<256; n++) {
1303 col[n].rgbRed = ent[n].peRed;
1304 col[n].rgbGreen = ent[n].peGreen;
1305 col[n].rgbBlue = ent[n].peBlue;
1306 col[n].rgbReserved = 0;
1308 } else {
1309 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1311 for (n=0; n<256; n++) {
1312 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1313 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1314 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1315 col[n].rgbReserved = 0;
1319 SetDIBColorTable(This->hDC, 0, 256, col);
1322 *pHDC = This->hDC;
1323 TRACE("returning %p\n",*pHDC);
1324 This->Flags |= SFLAG_DCINUSE;
1326 return WINED3D_OK;
1329 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1330 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1332 TRACE("(%p)->(%p)\n",This,hDC);
1334 if (!(This->Flags & SFLAG_DCINUSE))
1335 return WINED3DERR_INVALIDCALL;
1337 if (This->hDC !=hDC) {
1338 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1339 return WINED3DERR_INVALIDCALL;
1342 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1343 /* Copy the contents of the DIB over to the PBO */
1344 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1347 /* we locked first, so unlock now */
1348 IWineD3DSurface_UnlockRect(iface);
1350 This->Flags &= ~SFLAG_DCINUSE;
1352 return WINED3D_OK;
1355 /* ******************************************************
1356 IWineD3DSurface Internal (No mapping to directx api) parts follow
1357 ****************************************************** */
1359 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) {
1360 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1361 const GlPixelFormatDesc *glDesc;
1362 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1363 BOOL p8_render_target = FALSE;
1364 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1366 /* Default values: From the surface */
1367 *format = glDesc->glFormat;
1368 *type = glDesc->glType;
1369 *convert = NO_CONVERSION;
1370 *target_bpp = This->bytesPerPixel;
1372 if(srgb_mode) {
1373 *internal = glDesc->glGammaInternal;
1374 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1375 *internal = glDesc->rtInternal;
1376 } else {
1377 *internal = glDesc->glInternal;
1380 /* Ok, now look if we have to do any conversion */
1381 switch(This->resource.format) {
1382 case WINED3DFMT_P8:
1383 /* ****************
1384 Paletted Texture
1385 **************** */
1387 if (device->render_targets && device->render_targets[0]) {
1388 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
1389 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
1390 p8_render_target = TRUE;
1393 /* Use conversion when the paletted texture extension is not available, or when it is available make sure it is used
1394 * for texturing as it won't work for calls like glDraw-/glReadPixels and further also use conversion in case of color keying.
1395 * 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.
1397 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) && p8_render_target)) || colorkey_active || (!use_texturing && GL_SUPPORT(EXT_PALETTED_TEXTURE)) ) {
1398 *format = GL_RGBA;
1399 *internal = GL_RGBA;
1400 *type = GL_UNSIGNED_BYTE;
1401 *target_bpp = 4;
1402 if(colorkey_active) {
1403 *convert = CONVERT_PALETTED_CK;
1404 } else {
1405 *convert = CONVERT_PALETTED;
1408 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1409 *format = GL_RED;
1410 *internal = GL_RGBA;
1411 *type = GL_UNSIGNED_BYTE;
1412 *target_bpp = 1;
1415 break;
1417 case WINED3DFMT_R3G3B2:
1418 /* **********************
1419 GL_UNSIGNED_BYTE_3_3_2
1420 ********************** */
1421 if (colorkey_active) {
1422 /* This texture format will never be used.. So do not care about color keying
1423 up until the point in time it will be needed :-) */
1424 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1426 break;
1428 case WINED3DFMT_R5G6B5:
1429 if (colorkey_active) {
1430 *convert = CONVERT_CK_565;
1431 *format = GL_RGBA;
1432 *internal = GL_RGBA;
1433 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1435 break;
1437 case WINED3DFMT_X1R5G5B5:
1438 if (colorkey_active) {
1439 *convert = CONVERT_CK_5551;
1440 *format = GL_BGRA;
1441 *internal = GL_RGBA;
1442 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1444 break;
1446 case WINED3DFMT_R8G8B8:
1447 if (colorkey_active) {
1448 *convert = CONVERT_CK_RGB24;
1449 *format = GL_RGBA;
1450 *internal = GL_RGBA;
1451 *type = GL_UNSIGNED_INT_8_8_8_8;
1452 *target_bpp = 4;
1454 break;
1456 case WINED3DFMT_X8R8G8B8:
1457 if (colorkey_active) {
1458 *convert = CONVERT_RGB32_888;
1459 *format = GL_RGBA;
1460 *internal = GL_RGBA;
1461 *type = GL_UNSIGNED_INT_8_8_8_8;
1463 break;
1465 case WINED3DFMT_V8U8:
1466 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1467 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1468 *format = GL_DUDV_ATI;
1469 *internal = GL_DU8DV8_ATI;
1470 *type = GL_BYTE;
1471 /* No conversion - Just change the gl type */
1472 break;
1474 *convert = CONVERT_V8U8;
1475 *format = GL_BGR;
1476 *internal = GL_RGB8;
1477 *type = GL_UNSIGNED_BYTE;
1478 *target_bpp = 3;
1479 break;
1481 case WINED3DFMT_L6V5U5:
1482 *convert = CONVERT_L6V5U5;
1483 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1484 *target_bpp = 3;
1485 /* Use format and types from table */
1486 } else {
1487 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1488 *target_bpp = 2;
1489 *format = GL_RGB;
1490 *internal = GL_RGB5;
1491 *type = GL_UNSIGNED_SHORT_5_6_5;
1493 break;
1495 case WINED3DFMT_X8L8V8U8:
1496 *convert = CONVERT_X8L8V8U8;
1497 *target_bpp = 4;
1498 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1499 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1500 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1501 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1502 * the needed type and format parameter, so the internal format contains a
1503 * 4th component, which is returned as alpha
1505 } else {
1506 /* Not supported by GL_ATI_envmap_bumpmap */
1507 *format = GL_BGRA;
1508 *internal = GL_RGB8;
1509 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1511 break;
1513 case WINED3DFMT_Q8W8V8U8:
1514 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1515 *convert = CONVERT_Q8W8V8U8;
1516 *format = GL_BGRA;
1517 *internal = GL_RGBA8;
1518 *type = GL_UNSIGNED_BYTE;
1519 *target_bpp = 4;
1520 /* Not supported by GL_ATI_envmap_bumpmap */
1521 break;
1523 case WINED3DFMT_V16U16:
1524 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1525 *convert = CONVERT_V16U16;
1526 *format = GL_BGR;
1527 *internal = GL_RGB16_EXT;
1528 *type = GL_UNSIGNED_SHORT;
1529 *target_bpp = 6;
1530 /* What should I do here about GL_ATI_envmap_bumpmap?
1531 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1533 break;
1535 case WINED3DFMT_A4L4:
1536 /* A4L4 exists as an internal gl format, but for some reason there is not
1537 * format+type combination to load it. Thus convert it to A8L8, then load it
1538 * with A4L4 internal, but A8L8 format+type
1540 *convert = CONVERT_A4L4;
1541 *format = GL_LUMINANCE_ALPHA;
1542 *internal = GL_LUMINANCE4_ALPHA4;
1543 *type = GL_UNSIGNED_BYTE;
1544 *target_bpp = 2;
1545 break;
1547 case WINED3DFMT_R32F:
1548 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1549 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1550 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1551 * 1.0 instead.
1553 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1555 *convert = CONVERT_R32F;
1556 *format = GL_RGB;
1557 *internal = GL_RGB32F_ARB;
1558 *type = GL_FLOAT;
1559 *target_bpp = 12;
1560 break;
1562 case WINED3DFMT_R16F:
1563 /* Similar to R32F */
1564 *convert = CONVERT_R16F;
1565 *format = GL_RGB;
1566 *internal = GL_RGB16F_ARB;
1567 *type = GL_HALF_FLOAT_ARB;
1568 *target_bpp = 6;
1569 break;
1571 case WINED3DFMT_G16R16:
1572 *convert = CONVERT_G16R16;
1573 *format = GL_RGB;
1574 *internal = GL_RGB16_EXT;
1575 *type = GL_UNSIGNED_SHORT;
1576 *target_bpp = 6;
1577 break;
1579 default:
1580 break;
1583 return WINED3D_OK;
1586 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1587 BYTE *source, *dest;
1588 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1590 switch (convert) {
1591 case NO_CONVERSION:
1593 memcpy(dst, src, pitch * height);
1594 break;
1596 case CONVERT_PALETTED:
1597 case CONVERT_PALETTED_CK:
1599 IWineD3DPaletteImpl* pal = This->palette;
1600 BYTE table[256][4];
1601 unsigned int x, y;
1603 if( pal == NULL) {
1604 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1607 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1609 for (y = 0; y < height; y++)
1611 source = src + pitch * y;
1612 dest = dst + outpitch * y;
1613 /* This is an 1 bpp format, using the width here is fine */
1614 for (x = 0; x < width; x++) {
1615 BYTE color = *source++;
1616 *dest++ = table[color][0];
1617 *dest++ = table[color][1];
1618 *dest++ = table[color][2];
1619 *dest++ = table[color][3];
1623 break;
1625 case CONVERT_CK_565:
1627 /* Converting the 565 format in 5551 packed to emulate color-keying.
1629 Note : in all these conversion, it would be best to average the averaging
1630 pixels to get the color of the pixel that will be color-keyed to
1631 prevent 'color bleeding'. This will be done later on if ever it is
1632 too visible.
1634 Note2: Nvidia documents say that their driver does not support alpha + color keying
1635 on the same surface and disables color keying in such a case
1637 unsigned int x, y;
1638 WORD *Source;
1639 WORD *Dest;
1641 TRACE("Color keyed 565\n");
1643 for (y = 0; y < height; y++) {
1644 Source = (WORD *) (src + y * pitch);
1645 Dest = (WORD *) (dst + y * outpitch);
1646 for (x = 0; x < width; x++ ) {
1647 WORD color = *Source++;
1648 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1649 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1650 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1651 *Dest |= 0x0001;
1653 Dest++;
1657 break;
1659 case CONVERT_CK_5551:
1661 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1662 unsigned int x, y;
1663 WORD *Source;
1664 WORD *Dest;
1665 TRACE("Color keyed 5551\n");
1666 for (y = 0; y < height; y++) {
1667 Source = (WORD *) (src + y * pitch);
1668 Dest = (WORD *) (dst + y * outpitch);
1669 for (x = 0; x < width; x++ ) {
1670 WORD color = *Source++;
1671 *Dest = color;
1672 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1673 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1674 *Dest |= (1 << 15);
1676 else {
1677 *Dest &= ~(1 << 15);
1679 Dest++;
1683 break;
1685 case CONVERT_V8U8:
1687 unsigned int x, y;
1688 short *Source;
1689 unsigned char *Dest;
1690 for(y = 0; y < height; y++) {
1691 Source = (short *) (src + y * pitch);
1692 Dest = (unsigned char *) (dst + y * outpitch);
1693 for (x = 0; x < width; x++ ) {
1694 long color = (*Source++);
1695 /* B */ Dest[0] = 0xff;
1696 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1697 /* R */ Dest[2] = (color) + 128; /* U */
1698 Dest += 3;
1701 break;
1704 case CONVERT_V16U16:
1706 unsigned int x, y;
1707 DWORD *Source;
1708 unsigned short *Dest;
1709 for(y = 0; y < height; y++) {
1710 Source = (DWORD *) (src + y * pitch);
1711 Dest = (unsigned short *) (dst + y * outpitch);
1712 for (x = 0; x < width; x++ ) {
1713 DWORD color = (*Source++);
1714 /* B */ Dest[0] = 0xffff;
1715 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1716 /* R */ Dest[2] = (color ) + 32768; /* U */
1717 Dest += 3;
1720 break;
1723 case CONVERT_Q8W8V8U8:
1725 unsigned int x, y;
1726 DWORD *Source;
1727 unsigned char *Dest;
1728 for(y = 0; y < height; y++) {
1729 Source = (DWORD *) (src + y * pitch);
1730 Dest = (unsigned char *) (dst + y * outpitch);
1731 for (x = 0; x < width; x++ ) {
1732 long color = (*Source++);
1733 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1734 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1735 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1736 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1737 Dest += 4;
1740 break;
1743 case CONVERT_L6V5U5:
1745 unsigned int x, y;
1746 WORD *Source;
1747 unsigned char *Dest;
1749 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1750 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1751 * fixed function and shaders without further conversion once the surface is
1752 * loaded
1754 for(y = 0; y < height; y++) {
1755 Source = (WORD *) (src + y * pitch);
1756 Dest = (unsigned char *) (dst + y * outpitch);
1757 for (x = 0; x < width; x++ ) {
1758 short color = (*Source++);
1759 unsigned char l = ((color >> 10) & 0xfc);
1760 char v = ((color >> 5) & 0x3e);
1761 char u = ((color ) & 0x1f);
1763 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1764 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1765 * shift. GL reads a signed value and converts it into an unsigned value.
1767 /* M */ Dest[2] = l << 1;
1769 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1770 * from 5 bit values to 8 bit values.
1772 /* V */ Dest[1] = v << 3;
1773 /* U */ Dest[0] = u << 3;
1774 Dest += 3;
1777 } else {
1778 for(y = 0; y < height; y++) {
1779 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1780 Source = (WORD *) (src + y * pitch);
1781 for (x = 0; x < width; x++ ) {
1782 short color = (*Source++);
1783 unsigned char l = ((color >> 10) & 0xfc);
1784 short v = ((color >> 5) & 0x3e);
1785 short u = ((color ) & 0x1f);
1786 short v_conv = v + 16;
1787 short u_conv = u + 16;
1789 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1790 Dest_s += 1;
1794 break;
1797 case CONVERT_X8L8V8U8:
1799 unsigned int x, y;
1800 DWORD *Source;
1801 unsigned char *Dest;
1803 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1804 /* This implementation works with the fixed function pipeline and shaders
1805 * without further modification after converting the surface.
1807 for(y = 0; y < height; y++) {
1808 Source = (DWORD *) (src + y * pitch);
1809 Dest = (unsigned char *) (dst + y * outpitch);
1810 for (x = 0; x < width; x++ ) {
1811 long color = (*Source++);
1812 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1813 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1814 /* U */ Dest[0] = (color & 0xff); /* U */
1815 /* I */ Dest[3] = 255; /* X */
1816 Dest += 4;
1819 } else {
1820 /* Doesn't work correctly with the fixed function pipeline, but can work in
1821 * shaders if the shader is adjusted. (There's no use for this format in gl's
1822 * standard fixed function pipeline anyway).
1824 for(y = 0; y < height; y++) {
1825 Source = (DWORD *) (src + y * pitch);
1826 Dest = (unsigned char *) (dst + y * outpitch);
1827 for (x = 0; x < width; x++ ) {
1828 long color = (*Source++);
1829 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1830 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1831 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1832 Dest += 4;
1836 break;
1839 case CONVERT_A4L4:
1841 unsigned int x, y;
1842 unsigned char *Source;
1843 unsigned char *Dest;
1844 for(y = 0; y < height; y++) {
1845 Source = (unsigned char *) (src + y * pitch);
1846 Dest = (unsigned char *) (dst + y * outpitch);
1847 for (x = 0; x < width; x++ ) {
1848 unsigned char color = (*Source++);
1849 /* A */ Dest[1] = (color & 0xf0) << 0;
1850 /* L */ Dest[0] = (color & 0x0f) << 4;
1851 Dest += 2;
1854 break;
1857 case CONVERT_R32F:
1859 unsigned int x, y;
1860 float *Source;
1861 float *Dest;
1862 for(y = 0; y < height; y++) {
1863 Source = (float *) (src + y * pitch);
1864 Dest = (float *) (dst + y * outpitch);
1865 for (x = 0; x < width; x++ ) {
1866 float color = (*Source++);
1867 Dest[0] = color;
1868 Dest[1] = 1.0;
1869 Dest[2] = 1.0;
1870 Dest += 3;
1873 break;
1876 case CONVERT_R16F:
1878 unsigned int x, y;
1879 WORD *Source;
1880 WORD *Dest;
1881 WORD one = 0x3c00;
1882 for(y = 0; y < height; y++) {
1883 Source = (WORD *) (src + y * pitch);
1884 Dest = (WORD *) (dst + y * outpitch);
1885 for (x = 0; x < width; x++ ) {
1886 WORD color = (*Source++);
1887 Dest[0] = color;
1888 Dest[1] = one;
1889 Dest[2] = one;
1890 Dest += 3;
1893 break;
1896 case CONVERT_G16R16:
1898 unsigned int x, y;
1899 WORD *Source;
1900 WORD *Dest;
1902 for(y = 0; y < height; y++) {
1903 Source = (WORD *) (src + y * pitch);
1904 Dest = (WORD *) (dst + y * outpitch);
1905 for (x = 0; x < width; x++ ) {
1906 WORD green = (*Source++);
1907 WORD red = (*Source++);
1908 Dest[0] = green;
1909 Dest[1] = red;
1910 Dest[2] = 0xffff;
1911 Dest += 3;
1914 break;
1917 default:
1918 ERR("Unsupported conversation type %d\n", convert);
1920 return WINED3D_OK;
1923 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
1924 IWineD3DPaletteImpl* pal = This->palette;
1925 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1926 BOOL index_in_alpha = FALSE;
1927 int i;
1929 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1930 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1931 * is slow. Further RGB->P8 conversion is not possible because palettes can have
1932 * duplicate entries. Store the color key in the unused alpha component to speed the
1933 * download up and to make conversion unneeded. */
1934 if (device->render_targets && device->render_targets[0]) {
1935 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
1937 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
1938 index_in_alpha = TRUE;
1941 if (pal == NULL) {
1942 /* Still no palette? Use the device's palette */
1943 /* Get the surface's palette */
1944 for (i = 0; i < 256; i++) {
1945 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1946 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1947 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1949 if(index_in_alpha) {
1950 table[i][3] = i;
1951 } else if (colorkey &&
1952 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1953 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1954 /* We should maybe here put a more 'neutral' color than the standard bright purple
1955 one often used by application to prevent the nice purple borders when bi-linear
1956 filtering is on */
1957 table[i][3] = 0x00;
1958 } else {
1959 table[i][3] = 0xFF;
1962 } else {
1963 TRACE("Using surface palette %p\n", pal);
1964 /* Get the surface's palette */
1965 for (i = 0; i < 256; i++) {
1966 table[i][0] = pal->palents[i].peRed;
1967 table[i][1] = pal->palents[i].peGreen;
1968 table[i][2] = pal->palents[i].peBlue;
1970 if(index_in_alpha) {
1971 table[i][3] = i;
1973 else if (colorkey &&
1974 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1975 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1976 /* We should maybe here put a more 'neutral' color than the standard bright purple
1977 one often used by application to prevent the nice purple borders when bi-linear
1978 filtering is on */
1979 table[i][3] = 0x00;
1980 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
1981 table[i][3] = pal->palents[i].peFlags;
1982 } else {
1983 table[i][3] = 0xFF;
1989 const char *fragment_palette_conversion =
1990 "!!ARBfp1.0\n"
1991 "TEMP index;\n"
1992 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
1993 "TEX index.x, fragment.texcoord[0], texture[0], 2D;\n" /* store the red-component of the current pixel */
1994 "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 */
1995 "TEX result.color, index, texture[1], 1D;\n" /* use the red-component as a index in the palette to get the final color */
1996 "END";
1998 /* This function is used in case of 8bit paletted textures to upload the palette.
1999 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2000 extensions like ATI_fragment_shaders is possible.
2002 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2003 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2004 BYTE table[256][4];
2005 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2007 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2009 /* Try to use the paletted texture extension */
2010 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2012 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2013 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2015 else
2017 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2018 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2019 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2021 /* Create the fragment program if we don't have it */
2022 if(!device->paletteConversionShader)
2024 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2025 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2026 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2027 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2028 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2031 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2032 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2034 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2035 glEnable(GL_TEXTURE_1D);
2036 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2038 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2039 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2040 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2041 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2043 /* Switch back to unit 0 in which the 2D texture will be stored. */
2044 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2046 /* Rebind the texture because it isn't bound anymore */
2047 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2051 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2052 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2054 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2055 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2056 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2058 return FALSE;
2061 if(This->palette9) {
2062 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2063 return FALSE;
2065 } else {
2066 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2068 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2069 return TRUE;
2072 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2073 GLboolean oldwrite[4];
2075 /* Some formats have only some color channels, and the others are 1.0.
2076 * since our rendering renders to all channels, and those pixel formats
2077 * are emulated by using a full texture with the other channels set to 1.0
2078 * manually, clear the unused channels.
2080 * This could be done with hacking colorwriteenable to mask the colors,
2081 * but before drawing the buffer would have to be cleared too, so there's
2082 * no gain in that
2084 switch(This->resource.format) {
2085 case WINED3DFMT_R16F:
2086 case WINED3DFMT_R32F:
2087 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2088 /* Do not activate a context, the correct drawable is active already
2089 * though just the read buffer is set, make sure to have the correct draw
2090 * buffer too
2092 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2093 glDisable(GL_SCISSOR_TEST);
2094 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2095 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2096 glClearColor(0.0, 1.0, 1.0, 1.0);
2097 glClear(GL_COLOR_BUFFER_BIT);
2098 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2099 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2100 checkGLcall("Unused channel clear\n");
2101 break;
2103 default: break;
2107 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2108 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2110 if (!(This->Flags & SFLAG_INTEXTURE)) {
2111 TRACE("Reloading because surface is dirty\n");
2112 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2113 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2114 /* Reload: vice versa OR */
2115 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2116 /* Also reload: Color key is active AND the color key has changed */
2117 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2118 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2119 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2120 TRACE("Reloading because of color keying\n");
2121 /* To perform the color key conversion we need a sysmem copy of
2122 * the surface. Make sure we have it
2124 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2125 } else if(palette9_changed(This)) {
2126 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2127 /* TODO: This is not necessarily needed with hw palettized texture support */
2128 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2129 } else {
2130 TRACE("surface is already in texture\n");
2131 return WINED3D_OK;
2134 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2135 * These resources are not bound by device size or format restrictions. Because of this,
2136 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2137 * However, these resources can always be created, locked, and copied.
2139 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2141 FIXME("(%p) Operation not supported for scratch textures\n",This);
2142 return WINED3DERR_INVALIDCALL;
2145 This->srgb = srgb_mode;
2146 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2148 #if 0
2150 static unsigned int gen = 0;
2151 char buffer[4096];
2152 ++gen;
2153 if ((gen % 10) == 0) {
2154 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2155 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2158 * debugging crash code
2159 if (gen == 250) {
2160 void** test = NULL;
2161 *test = 0;
2165 #endif
2167 if (!(This->Flags & SFLAG_DONOTFREE)) {
2168 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2169 This->resource.allocatedMemory = NULL;
2170 This->resource.heapMemory = NULL;
2171 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2174 return WINED3D_OK;
2177 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2178 /* TODO: check for locks */
2179 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2180 IWineD3DBaseTexture *baseTexture = NULL;
2181 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2183 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2184 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2185 TRACE("Passing to container\n");
2186 IWineD3DBaseTexture_BindTexture(baseTexture);
2187 IWineD3DBaseTexture_Release(baseTexture);
2188 } else {
2189 TRACE("(%p) : Binding surface\n", This);
2191 if(!device->isInDraw) {
2192 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2194 ENTER_GL();
2195 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2196 LEAVE_GL();
2198 return;
2201 #include <errno.h>
2202 #include <stdio.h>
2203 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2204 FILE* f = NULL;
2205 UINT i, y;
2206 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2207 char *allocatedMemory;
2208 char *textureRow;
2209 IWineD3DSwapChain *swapChain = NULL;
2210 int width, height;
2211 GLuint tmpTexture = 0;
2212 DWORD color;
2213 /*FIXME:
2214 Textures may not be stored in ->allocatedgMemory and a GlTexture
2215 so we should lock the surface before saving a snapshot, or at least check that
2217 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2218 by calling GetTexImage and in compressed form by calling
2219 GetCompressedTexImageARB. Queried compressed images can be saved and
2220 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2221 texture images do not need to be processed by the GL and should
2222 significantly improve texture loading performance relative to uncompressed
2223 images. */
2225 /* Setup the width and height to be the internal texture width and height. */
2226 width = This->pow2Width;
2227 height = This->pow2Height;
2228 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2229 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2231 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2232 /* if were not a real texture then read the back buffer into a real texture */
2233 /* we don't want to interfere with the back buffer so read the data into a temporary
2234 * texture and then save the data out of the temporary texture
2236 GLint prevRead;
2237 ENTER_GL();
2238 TRACE("(%p) Reading render target into texture\n", This);
2239 glEnable(GL_TEXTURE_2D);
2241 glGenTextures(1, &tmpTexture);
2242 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2244 glTexImage2D(GL_TEXTURE_2D,
2246 GL_RGBA,
2247 width,
2248 height,
2249 0/*border*/,
2250 GL_RGBA,
2251 GL_UNSIGNED_INT_8_8_8_8_REV,
2252 NULL);
2254 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2255 vcheckGLcall("glGetIntegerv");
2256 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2257 vcheckGLcall("glReadBuffer");
2258 glCopyTexImage2D(GL_TEXTURE_2D,
2260 GL_RGBA,
2263 width,
2264 height,
2267 checkGLcall("glCopyTexImage2D");
2268 glReadBuffer(prevRead);
2269 LEAVE_GL();
2271 } else { /* bind the real texture, and make sure it up to date */
2272 IWineD3DSurface_PreLoad(iface);
2274 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2275 ENTER_GL();
2276 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2277 glGetTexImage(GL_TEXTURE_2D,
2278 This->glDescription.level,
2279 GL_RGBA,
2280 GL_UNSIGNED_INT_8_8_8_8_REV,
2281 allocatedMemory);
2282 checkGLcall("glTexImage2D");
2283 if (tmpTexture) {
2284 glBindTexture(GL_TEXTURE_2D, 0);
2285 glDeleteTextures(1, &tmpTexture);
2287 LEAVE_GL();
2289 f = fopen(filename, "w+");
2290 if (NULL == f) {
2291 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2292 return WINED3DERR_INVALIDCALL;
2294 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2295 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2296 /* TGA header */
2297 fputc(0,f);
2298 fputc(0,f);
2299 fputc(2,f);
2300 fputc(0,f);
2301 fputc(0,f);
2302 fputc(0,f);
2303 fputc(0,f);
2304 fputc(0,f);
2305 fputc(0,f);
2306 fputc(0,f);
2307 fputc(0,f);
2308 fputc(0,f);
2309 /* short width*/
2310 fwrite(&width,2,1,f);
2311 /* short height */
2312 fwrite(&height,2,1,f);
2313 /* format rgba */
2314 fputc(0x20,f);
2315 fputc(0x28,f);
2316 /* raw data */
2317 /* 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 */
2318 if(swapChain)
2319 textureRow = allocatedMemory + (width * (height - 1) *4);
2320 else
2321 textureRow = allocatedMemory;
2322 for (y = 0 ; y < height; y++) {
2323 for (i = 0; i < width; i++) {
2324 color = *((DWORD*)textureRow);
2325 fputc((color >> 16) & 0xFF, f); /* B */
2326 fputc((color >> 8) & 0xFF, f); /* G */
2327 fputc((color >> 0) & 0xFF, f); /* R */
2328 fputc((color >> 24) & 0xFF, f); /* A */
2329 textureRow += 4;
2331 /* take two rows of the pointer to the texture memory */
2332 if(swapChain)
2333 (textureRow-= width << 3);
2336 TRACE("Closing file\n");
2337 fclose(f);
2339 if(swapChain) {
2340 IWineD3DSwapChain_Release(swapChain);
2342 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2343 return WINED3D_OK;
2347 * Slightly inefficient way to handle multiple dirty rects but it works :)
2349 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2350 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2351 IWineD3DBaseTexture *baseTexture = NULL;
2353 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2354 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2356 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2357 if (NULL != pDirtyRect) {
2358 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2359 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2360 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2361 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2362 } else {
2363 This->dirtyRect.left = 0;
2364 This->dirtyRect.top = 0;
2365 This->dirtyRect.right = This->currentDesc.Width;
2366 This->dirtyRect.bottom = This->currentDesc.Height;
2368 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2369 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2370 /* if the container is a basetexture then mark it dirty. */
2371 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2372 TRACE("Passing to container\n");
2373 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2374 IWineD3DBaseTexture_Release(baseTexture);
2376 return WINED3D_OK;
2379 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2380 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2381 HRESULT hr;
2382 const GlPixelFormatDesc *glDesc;
2383 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2385 TRACE("(%p) : Calling base function first\n", This);
2386 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2387 if(SUCCEEDED(hr)) {
2388 /* Setup some glformat defaults */
2389 This->glDescription.glFormat = glDesc->glFormat;
2390 This->glDescription.glFormatInternal = glDesc->glInternal;
2391 This->glDescription.glType = glDesc->glType;
2393 This->Flags &= ~SFLAG_ALLOCATED;
2394 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2395 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2397 return hr;
2400 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2401 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2403 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2404 WARN("Surface is locked or the HDC is in use\n");
2405 return WINED3DERR_INVALIDCALL;
2408 if(Mem && Mem != This->resource.allocatedMemory) {
2409 void *release = NULL;
2411 /* Do I have to copy the old surface content? */
2412 if(This->Flags & SFLAG_DIBSECTION) {
2413 /* Release the DC. No need to hold the critical section for the update
2414 * Thread because this thread runs only on front buffers, but this method
2415 * fails for render targets in the check above.
2417 SelectObject(This->hDC, This->dib.holdbitmap);
2418 DeleteDC(This->hDC);
2419 /* Release the DIB section */
2420 DeleteObject(This->dib.DIBsection);
2421 This->dib.bitmap_data = NULL;
2422 This->resource.allocatedMemory = NULL;
2423 This->hDC = NULL;
2424 This->Flags &= ~SFLAG_DIBSECTION;
2425 } else if(!(This->Flags & SFLAG_USERPTR)) {
2426 release = This->resource.heapMemory;
2427 This->resource.heapMemory = NULL;
2429 This->resource.allocatedMemory = Mem;
2430 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2432 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2433 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2435 /* For client textures opengl has to be notified */
2436 if(This->Flags & SFLAG_CLIENT) {
2437 This->Flags &= ~SFLAG_ALLOCATED;
2438 IWineD3DSurface_PreLoad(iface);
2439 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2442 /* Now free the old memory if any */
2443 HeapFree(GetProcessHeap(), 0, release);
2444 } else if(This->Flags & SFLAG_USERPTR) {
2445 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2446 This->resource.allocatedMemory = NULL;
2447 /* HeapMemory should be NULL already */
2448 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2449 This->Flags &= ~SFLAG_USERPTR;
2451 if(This->Flags & SFLAG_CLIENT) {
2452 This->Flags &= ~SFLAG_ALLOCATED;
2453 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2454 IWineD3DSurface_PreLoad(iface);
2457 return WINED3D_OK;
2460 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2461 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2462 IWineD3DSwapChainImpl *swapchain = NULL;
2463 HRESULT hr;
2464 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2466 /* Flipping is only supported on RenderTargets */
2467 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2469 if(override) {
2470 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2471 * FIXME("(%p) Target override is not supported by now\n", This);
2472 * Additionally, it isn't really possible to support triple-buffering
2473 * properly on opengl at all
2477 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2478 if(!swapchain) {
2479 ERR("Flipped surface is not on a swapchain\n");
2480 return WINEDDERR_NOTFLIPPABLE;
2483 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2484 * and only d3d8 and d3d9 apps specify the presentation interval
2486 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2487 /* Most common case first to avoid wasting time on all the other cases */
2488 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2489 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2490 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2491 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2492 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2493 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2494 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2495 } else {
2496 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2499 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2500 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2501 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2502 return hr;
2505 /* Does a direct frame buffer -> texture copy. Stretching is done
2506 * with single pixel copy calls
2508 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2509 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2510 float xrel, yrel;
2511 UINT row;
2512 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2515 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2516 ENTER_GL();
2517 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2519 /* TODO: Do we need GL_TEXTURE_2D enabled fpr copyteximage? */
2520 glEnable(This->glDescription.target);
2521 checkGLcall("glEnable(This->glDescription.target)");
2523 /* Bind the target texture */
2524 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2525 checkGLcall("glBindTexture");
2526 if(!swapchain) {
2527 glReadBuffer(myDevice->offscreenBuffer);
2528 } else {
2529 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2530 glReadBuffer(buffer);
2532 checkGLcall("glReadBuffer");
2534 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2535 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2537 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2538 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2540 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2541 ERR("Texture filtering not supported in direct blit\n");
2543 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2544 ERR("Texture filtering not supported in direct blit\n");
2547 if(upsidedown &&
2548 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2549 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2550 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2552 glCopyTexSubImage2D(This->glDescription.target,
2553 This->glDescription.level,
2554 drect->x1, drect->y1, /* xoffset, yoffset */
2555 srect->x1, Src->currentDesc.Height - srect->y2,
2556 drect->x2 - drect->x1, drect->y2 - drect->y1);
2557 } else {
2558 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2559 /* I have to process this row by row to swap the image,
2560 * otherwise it would be upside down, so stretching in y direction
2561 * doesn't cost extra time
2563 * However, stretching in x direction can be avoided if not necessary
2565 for(row = drect->y1; row < drect->y2; row++) {
2566 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2567 /* Well, that stuff works, but it's very slow.
2568 * find a better way instead
2570 UINT col;
2572 for(col = drect->x1; col < drect->x2; col++) {
2573 glCopyTexSubImage2D(This->glDescription.target,
2574 This->glDescription.level,
2575 drect->x1 + col, row, /* xoffset, yoffset */
2576 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2577 1, 1);
2579 } else {
2580 glCopyTexSubImage2D(This->glDescription.target,
2581 This->glDescription.level,
2582 drect->x1, row, /* xoffset, yoffset */
2583 srect->x1, yoffset - (int) (row * yrel),
2584 drect->x2-drect->x1, 1);
2588 vcheckGLcall("glCopyTexSubImage2D");
2590 /* Leave the opengl state valid for blitting */
2591 glDisable(This->glDescription.target);
2592 checkGLcall("glDisable(This->glDescription.target)");
2594 LEAVE_GL();
2597 /* Uses the hardware to stretch and flip the image */
2598 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2599 GLuint src, backup = 0;
2600 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2601 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2602 float left, right, top, bottom; /* Texture coordinates */
2603 UINT fbwidth = Src->currentDesc.Width;
2604 UINT fbheight = Src->currentDesc.Height;
2605 GLenum drawBuffer = GL_BACK;
2606 GLenum texture_target;
2608 TRACE("Using hwstretch blit\n");
2609 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2610 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2611 ENTER_GL();
2613 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2615 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2616 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2618 if(GL_LIMITS(aux_buffers) >= 2) {
2619 /* Got more than one aux buffer? Use the 2nd aux buffer */
2620 drawBuffer = GL_AUX1;
2621 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2622 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2623 drawBuffer = GL_AUX0;
2626 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2627 glGenTextures(1, &backup);
2628 checkGLcall("glGenTextures\n");
2629 glBindTexture(GL_TEXTURE_2D, backup);
2630 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2631 texture_target = GL_TEXTURE_2D;
2632 } else {
2633 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2634 * we are reading from the back buffer, the backup can be used as source texture
2636 if(Src->glDescription.textureName == 0) {
2637 /* Get it a description */
2638 IWineD3DSurface_PreLoad(SrcSurface);
2640 texture_target = Src->glDescription.target;
2641 glBindTexture(texture_target, Src->glDescription.textureName);
2642 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2643 glEnable(texture_target);
2644 checkGLcall("glEnable(texture_target)");
2646 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2647 Src->Flags &= ~SFLAG_INTEXTURE;
2650 glReadBuffer(GL_BACK);
2651 checkGLcall("glReadBuffer(GL_BACK)");
2653 /* TODO: Only back up the part that will be overwritten */
2654 glCopyTexSubImage2D(texture_target, 0,
2655 0, 0 /* read offsets */,
2656 0, 0,
2657 fbwidth,
2658 fbheight);
2660 checkGLcall("glCopyTexSubImage2D");
2662 /* No issue with overriding these - the sampler is dirty due to blit usage */
2663 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2664 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2665 checkGLcall("glTexParameteri");
2666 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2667 minMipLookup[Filter][WINED3DTEXF_NONE]);
2668 checkGLcall("glTexParameteri");
2670 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2671 src = backup ? backup : Src->glDescription.textureName;
2672 } else {
2673 glReadBuffer(GL_FRONT);
2674 checkGLcall("glReadBuffer(GL_FRONT)");
2676 glGenTextures(1, &src);
2677 checkGLcall("glGenTextures(1, &src)");
2678 glBindTexture(GL_TEXTURE_2D, src);
2679 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2681 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2682 * out for power of 2 sizes
2684 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2685 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2686 checkGLcall("glTexImage2D");
2687 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2688 0, 0 /* read offsets */,
2689 0, 0,
2690 fbwidth,
2691 fbheight);
2693 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2694 checkGLcall("glTexParameteri");
2695 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2696 checkGLcall("glTexParameteri");
2698 glReadBuffer(GL_BACK);
2699 checkGLcall("glReadBuffer(GL_BACK)");
2701 if(texture_target != GL_TEXTURE_2D) {
2702 glDisable(texture_target);
2703 glEnable(GL_TEXTURE_2D);
2704 texture_target = GL_TEXTURE_2D;
2707 checkGLcall("glEnd and previous");
2709 left = (float) srect->x1 / (float) Src->pow2Width;
2710 right = (float) srect->x2 / (float) Src->pow2Width;
2712 if(upsidedown) {
2713 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2714 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2715 } else {
2716 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2717 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2720 /* draw the source texture stretched and upside down. The correct surface is bound already */
2721 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
2722 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
2724 glDrawBuffer(drawBuffer);
2725 glReadBuffer(drawBuffer);
2727 glBegin(GL_QUADS);
2728 /* bottom left */
2729 glTexCoord2f(left, bottom);
2730 glVertex2i(0, fbheight);
2732 /* top left */
2733 glTexCoord2f(left, top);
2734 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2736 /* top right */
2737 glTexCoord2f(right, top);
2738 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2740 /* bottom right */
2741 glTexCoord2f(right, bottom);
2742 glVertex2i(drect->x2 - drect->x1, fbheight);
2743 glEnd();
2744 checkGLcall("glEnd and previous");
2746 if(texture_target != This->glDescription.target) {
2747 glDisable(texture_target);
2748 glEnable(This->glDescription.target);
2749 texture_target = This->glDescription.target;
2752 /* Now read the stretched and upside down image into the destination texture */
2753 glBindTexture(texture_target, This->glDescription.textureName);
2754 checkGLcall("glBindTexture");
2755 glCopyTexSubImage2D(texture_target,
2757 drect->x1, drect->y1, /* xoffset, yoffset */
2758 0, 0, /* We blitted the image to the origin */
2759 drect->x2 - drect->x1, drect->y2 - drect->y1);
2760 checkGLcall("glCopyTexSubImage2D");
2762 if(drawBuffer == GL_BACK) {
2763 /* Write the back buffer backup back */
2764 if(backup) {
2765 if(texture_target != GL_TEXTURE_2D) {
2766 glDisable(texture_target);
2767 glEnable(GL_TEXTURE_2D);
2768 texture_target = GL_TEXTURE_2D;
2770 glBindTexture(GL_TEXTURE_2D, backup);
2771 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
2772 } else {
2773 if(texture_target != Src->glDescription.target) {
2774 glDisable(texture_target);
2775 glEnable(Src->glDescription.target);
2776 texture_target = Src->glDescription.target;
2778 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
2779 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2782 glBegin(GL_QUADS);
2783 /* top left */
2784 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2785 glVertex2i(0, 0);
2787 /* bottom left */
2788 glTexCoord2f(0.0, 0.0);
2789 glVertex2i(0, fbheight);
2791 /* bottom right */
2792 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2793 glVertex2i(fbwidth, Src->currentDesc.Height);
2795 /* top right */
2796 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2797 glVertex2i(fbwidth, 0);
2798 glEnd();
2799 } else {
2800 /* Restore the old draw buffer */
2801 glDrawBuffer(GL_BACK);
2803 glDisable(texture_target);
2804 checkGLcall("glDisable(texture_target)");
2806 /* Cleanup */
2807 if(src != Src->glDescription.textureName && src != backup) {
2808 glDeleteTextures(1, &src);
2809 checkGLcall("glDeleteTextures(1, &src)");
2811 if(backup) {
2812 glDeleteTextures(1, &backup);
2813 checkGLcall("glDeleteTextures(1, &backup)");
2816 LEAVE_GL();
2819 /* Not called from the VTable */
2820 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2821 WINED3DRECT rect;
2822 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2823 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2824 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2826 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2828 /* Get the swapchain. One of the surfaces has to be a primary surface */
2829 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2830 WARN("Destination is in sysmem, rejecting gl blt\n");
2831 return WINED3DERR_INVALIDCALL;
2833 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2834 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2835 if(Src) {
2836 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2837 WARN("Src is in sysmem, rejecting gl blt\n");
2838 return WINED3DERR_INVALIDCALL;
2840 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2841 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2844 /* Early sort out of cases where no render target is used */
2845 if(!dstSwapchain && !srcSwapchain &&
2846 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2847 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2848 return WINED3DERR_INVALIDCALL;
2851 /* No destination color keying supported */
2852 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2853 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2854 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2855 return WINED3DERR_INVALIDCALL;
2858 if (DestRect) {
2859 rect.x1 = DestRect->left;
2860 rect.y1 = DestRect->top;
2861 rect.x2 = DestRect->right;
2862 rect.y2 = DestRect->bottom;
2863 } else {
2864 rect.x1 = 0;
2865 rect.y1 = 0;
2866 rect.x2 = This->currentDesc.Width;
2867 rect.y2 = This->currentDesc.Height;
2870 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2871 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2872 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2873 /* Half-life does a Blt from the back buffer to the front buffer,
2874 * Full surface size, no flags... Use present instead
2876 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2879 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2880 while(1)
2882 RECT mySrcRect;
2883 TRACE("Looking if a Present can be done...\n");
2884 /* Source Rectangle must be full surface */
2885 if( SrcRect ) {
2886 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2887 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2888 TRACE("No, Source rectangle doesn't match\n");
2889 break;
2892 mySrcRect.left = 0;
2893 mySrcRect.top = 0;
2894 mySrcRect.right = Src->currentDesc.Width;
2895 mySrcRect.bottom = Src->currentDesc.Height;
2897 /* No stretching may occur */
2898 if(mySrcRect.right != rect.x2 - rect.x1 ||
2899 mySrcRect.bottom != rect.y2 - rect.y1) {
2900 TRACE("No, stretching is done\n");
2901 break;
2904 /* Destination must be full surface or match the clipping rectangle */
2905 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
2907 RECT cliprect;
2908 POINT pos[2];
2909 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
2910 pos[0].x = rect.x1;
2911 pos[0].y = rect.y1;
2912 pos[1].x = rect.x2;
2913 pos[1].y = rect.y2;
2914 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
2915 pos, 2);
2917 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
2918 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
2920 TRACE("No, dest rectangle doesn't match(clipper)\n");
2921 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
2922 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
2923 break;
2926 else
2928 if(rect.x1 != 0 || rect.y1 != 0 ||
2929 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
2930 TRACE("No, dest rectangle doesn't match(surface size)\n");
2931 break;
2935 TRACE("Yes\n");
2937 /* These flags are unimportant for the flag check, remove them */
2938 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
2939 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2941 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2942 * take very long, while a flip is fast.
2943 * This applies to Half-Life, which does such Blts every time it finished
2944 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2945 * menu. This is also used by all apps when they do windowed rendering
2947 * The problem is that flipping is not really the same as copying. After a
2948 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2949 * untouched. Therefore it's necessary to override the swap effect
2950 * and to set it back after the flip.
2952 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
2953 * testcases.
2956 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2957 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2959 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2960 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
2962 dstSwapchain->presentParms.SwapEffect = orig_swap;
2964 return WINED3D_OK;
2966 break;
2969 TRACE("Unsupported blit between buffers on the same swapchain\n");
2970 return WINED3DERR_INVALIDCALL;
2971 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
2972 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
2973 return WINED3DERR_INVALIDCALL;
2974 } else if(dstSwapchain && srcSwapchain) {
2975 FIXME("Implement hardware blit between two different swapchains\n");
2976 return WINED3DERR_INVALIDCALL;
2977 } else if(dstSwapchain) {
2978 if(SrcSurface == myDevice->render_targets[0]) {
2979 TRACE("Blit from active render target to a swapchain\n");
2980 /* Handled with regular texture -> swapchain blit */
2982 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2983 FIXME("Implement blit from a swapchain to the active render target\n");
2984 return WINED3DERR_INVALIDCALL;
2987 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
2988 /* Blit from render target to texture */
2989 WINED3DRECT srect;
2990 BOOL upsideDown, stretchx;
2992 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2993 TRACE("Color keying not supported by frame buffer to texture blit\n");
2994 return WINED3DERR_INVALIDCALL;
2995 /* Destination color key is checked above */
2998 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2999 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3001 if(SrcRect) {
3002 if(SrcRect->top < SrcRect->bottom) {
3003 srect.y1 = SrcRect->top;
3004 srect.y2 = SrcRect->bottom;
3005 upsideDown = FALSE;
3006 } else {
3007 srect.y1 = SrcRect->bottom;
3008 srect.y2 = SrcRect->top;
3009 upsideDown = TRUE;
3011 srect.x1 = SrcRect->left;
3012 srect.x2 = SrcRect->right;
3013 } else {
3014 srect.x1 = 0;
3015 srect.y1 = 0;
3016 srect.x2 = Src->currentDesc.Width;
3017 srect.y2 = Src->currentDesc.Height;
3018 upsideDown = FALSE;
3020 if(rect.x1 > rect.x2) {
3021 UINT tmp = rect.x2;
3022 rect.x2 = rect.x1;
3023 rect.x1 = tmp;
3024 upsideDown = !upsideDown;
3026 if(!srcSwapchain) {
3027 TRACE("Reading from an offscreen target\n");
3028 upsideDown = !upsideDown;
3031 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3032 stretchx = TRUE;
3033 } else {
3034 stretchx = FALSE;
3037 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3038 * flip the image nor scale it.
3040 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3041 * -> If the app wants a image width an unscaled width, copy it line per line
3042 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3043 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3044 * back buffer. This is slower than reading line per line, thus not used for flipping
3045 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3046 * pixel by pixel
3048 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3049 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3050 * backends.
3052 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3053 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3054 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3055 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3056 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3057 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3058 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3059 } else {
3060 TRACE("Using hardware stretching to flip / stretch the texture\n");
3061 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3064 if(!(This->Flags & SFLAG_DONOTFREE)) {
3065 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3066 This->resource.allocatedMemory = NULL;
3067 This->resource.heapMemory = NULL;
3068 } else {
3069 This->Flags &= ~SFLAG_INSYSMEM;
3071 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3072 * path is never entered
3074 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3076 return WINED3D_OK;
3077 } else if(Src) {
3078 /* Blit from offscreen surface to render target */
3079 float glTexCoord[4];
3080 DWORD oldCKeyFlags = Src->CKeyFlags;
3081 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
3082 RECT SourceRectangle;
3084 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3086 if(SrcRect) {
3087 SourceRectangle.left = SrcRect->left;
3088 SourceRectangle.right = SrcRect->right;
3089 SourceRectangle.top = SrcRect->top;
3090 SourceRectangle.bottom = SrcRect->bottom;
3091 } else {
3092 SourceRectangle.left = 0;
3093 SourceRectangle.right = Src->currentDesc.Width;
3094 SourceRectangle.top = 0;
3095 SourceRectangle.bottom = Src->currentDesc.Height;
3097 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3098 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3099 TRACE("Using stretch_rect_fbo\n");
3100 /* The source is always a texture, but never the currently active render target, and the texture
3101 * contents are never upside down
3103 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3104 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3105 return WINED3D_OK;
3108 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3109 /* Fall back to software */
3110 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3111 SourceRectangle.left, SourceRectangle.top,
3112 SourceRectangle.right, SourceRectangle.bottom);
3113 return WINED3DERR_INVALIDCALL;
3116 /* Color keying: Check if we have to do a color keyed blt,
3117 * and if not check if a color key is activated.
3119 * Just modify the color keying parameters in the surface and restore them afterwards
3120 * The surface keeps track of the color key last used to load the opengl surface.
3121 * PreLoad will catch the change to the flags and color key and reload if necessary.
3123 if(Flags & WINEDDBLT_KEYSRC) {
3124 /* Use color key from surface */
3125 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3126 /* Use color key from DDBltFx */
3127 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3128 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3129 } else {
3130 /* Do not use color key */
3131 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3134 /* Now load the surface */
3135 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3138 /* Activate the destination context, set it up for blitting */
3139 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3140 ENTER_GL();
3142 glEnable(Src->glDescription.target);
3143 checkGLcall("glEnable(Src->glDescription.target)");
3145 if(!dstSwapchain) {
3146 TRACE("Drawing to offscreen buffer\n");
3147 glDrawBuffer(myDevice->offscreenBuffer);
3148 checkGLcall("glDrawBuffer");
3149 } else {
3150 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3151 TRACE("Drawing to %#x buffer\n", buffer);
3152 glDrawBuffer(buffer);
3153 checkGLcall("glDrawBuffer");
3156 /* Bind the texture */
3157 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3158 checkGLcall("glBindTexture");
3160 /* Filtering for StretchRect */
3161 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3162 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3163 checkGLcall("glTexParameteri");
3164 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3165 minMipLookup[Filter][WINED3DTEXF_NONE]);
3166 checkGLcall("glTexParameteri");
3167 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3168 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3169 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3170 checkGLcall("glTexEnvi");
3172 /* This is for color keying */
3173 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3174 glEnable(GL_ALPHA_TEST);
3175 checkGLcall("glEnable GL_ALPHA_TEST");
3176 glAlphaFunc(GL_NOTEQUAL, 0.0);
3177 checkGLcall("glAlphaFunc\n");
3178 } else {
3179 glDisable(GL_ALPHA_TEST);
3180 checkGLcall("glDisable GL_ALPHA_TEST");
3183 /* Draw a textured quad
3185 glBegin(GL_QUADS);
3187 glColor3d(1.0f, 1.0f, 1.0f);
3188 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3189 glVertex3f(rect.x1,
3190 rect.y1,
3191 0.0);
3193 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3194 glVertex3f(rect.x1, rect.y2, 0.0);
3196 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3197 glVertex3f(rect.x2,
3198 rect.y2,
3199 0.0);
3201 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3202 glVertex3f(rect.x2,
3203 rect.y1,
3204 0.0);
3205 glEnd();
3206 checkGLcall("glEnd");
3208 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3209 glDisable(GL_ALPHA_TEST);
3210 checkGLcall("glDisable(GL_ALPHA_TEST)");
3213 /* Flush in case the drawable is used by multiple GL contexts */
3214 if(dstSwapchain && (dstSwapchain->num_contexts >= 2))
3215 glFlush();
3217 glBindTexture(Src->glDescription.target, 0);
3218 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3219 /* Leave the opengl state valid for blitting */
3220 glDisable(Src->glDescription.target);
3221 checkGLcall("glDisable(Src->glDescription.target)");
3223 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3224 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3226 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3227 glDrawBuffer(GL_BACK);
3228 checkGLcall("glDrawBuffer");
3230 /* Restore the color key parameters */
3231 Src->CKeyFlags = oldCKeyFlags;
3232 This->SrcBltCKey = oldBltCKey;
3234 LEAVE_GL();
3236 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3237 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3238 * is outdated now
3240 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3241 /* TODO: This should be moved to ModifyLocation() */
3242 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3243 This->Flags |= SFLAG_INTEXTURE;
3246 return WINED3D_OK;
3247 } else {
3248 /* Source-Less Blit to render target */
3249 if (Flags & WINEDDBLT_COLORFILL) {
3250 /* This is easy to handle for the D3D Device... */
3251 DWORD color;
3253 TRACE("Colorfill\n");
3255 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3256 must be true if we are here */
3257 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3258 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3259 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3260 TRACE("Surface is higher back buffer, falling back to software\n");
3261 return WINED3DERR_INVALIDCALL;
3264 /* The color as given in the Blt function is in the format of the frame-buffer...
3265 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3267 if (This->resource.format == WINED3DFMT_P8) {
3268 if (This->palette) {
3269 color = ((0xFF000000) |
3270 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3271 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3272 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3273 } else {
3274 color = 0xFF000000;
3277 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3278 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3279 color = 0xFFFFFFFF;
3280 } else {
3281 color = ((0xFF000000) |
3282 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3283 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3284 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3287 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3288 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3289 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3291 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3292 color = DDBltFx->u5.dwFillColor;
3294 else {
3295 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3296 return WINED3DERR_INVALIDCALL;
3299 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3300 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3301 1, /* Number of rectangles */
3302 &rect, WINED3DCLEAR_TARGET, color,
3303 0.0 /* Z */,
3304 0 /* Stencil */);
3305 return WINED3D_OK;
3309 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3310 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3311 return WINED3DERR_INVALIDCALL;
3314 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3316 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3317 float depth;
3319 if (Flags & WINEDDBLT_DEPTHFILL) {
3320 switch(This->resource.format) {
3321 case WINED3DFMT_D16:
3322 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3323 break;
3324 case WINED3DFMT_D15S1:
3325 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3326 break;
3327 case WINED3DFMT_D24S8:
3328 case WINED3DFMT_D24X8:
3329 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3330 break;
3331 case WINED3DFMT_D32:
3332 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3333 break;
3334 default:
3335 depth = 0.0;
3336 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3339 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3340 DestRect == NULL ? 0 : 1,
3341 (WINED3DRECT *) DestRect,
3342 WINED3DCLEAR_ZBUFFER,
3343 0x00000000,
3344 depth,
3345 0x00000000);
3348 FIXME("(%p): Unsupp depthstencil blit\n", This);
3349 return WINED3DERR_INVALIDCALL;
3352 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3353 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3354 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3355 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3356 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3357 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3359 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3360 * except depth blits, which seem to work
3362 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3363 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3364 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3365 return WINED3DERR_INVALIDCALL;
3366 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3367 TRACE("Z Blit override handled the blit\n");
3368 return WINED3D_OK;
3372 /* Special cases for RenderTargets */
3373 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3374 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3375 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3378 /* For the rest call the X11 surface implementation.
3379 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3380 * other Blts are rather rare
3382 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3385 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3386 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3387 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3388 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3389 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3391 if(myDevice->inScene &&
3392 (iface == myDevice->stencilBufferTarget ||
3393 (Source && Source == myDevice->stencilBufferTarget))) {
3394 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3395 return WINED3DERR_INVALIDCALL;
3398 /* Special cases for RenderTargets */
3399 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3400 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3402 RECT SrcRect, DstRect;
3403 DWORD Flags=0;
3405 if(rsrc) {
3406 SrcRect.left = rsrc->left;
3407 SrcRect.top= rsrc->top;
3408 SrcRect.bottom = rsrc->bottom;
3409 SrcRect.right = rsrc->right;
3410 } else {
3411 SrcRect.left = 0;
3412 SrcRect.top = 0;
3413 SrcRect.right = srcImpl->currentDesc.Width;
3414 SrcRect.bottom = srcImpl->currentDesc.Height;
3417 DstRect.left = dstx;
3418 DstRect.top=dsty;
3419 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3420 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3422 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3423 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3424 Flags |= WINEDDBLT_KEYSRC;
3425 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3426 Flags |= WINEDDBLT_KEYDEST;
3427 if(trans & WINEDDBLTFAST_WAIT)
3428 Flags |= WINEDDBLT_WAIT;
3429 if(trans & WINEDDBLTFAST_DONOTWAIT)
3430 Flags |= WINEDDBLT_DONOTWAIT;
3432 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3436 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3439 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3440 /** Check against the maximum texture sizes supported by the video card **/
3441 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3442 unsigned int pow2Width, pow2Height;
3443 const GlPixelFormatDesc *glDesc;
3445 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3446 /* Setup some glformat defaults */
3447 This->glDescription.glFormat = glDesc->glFormat;
3448 This->glDescription.glFormatInternal = glDesc->glInternal;
3449 This->glDescription.glType = glDesc->glType;
3451 This->glDescription.textureName = 0;
3452 This->glDescription.target = GL_TEXTURE_2D;
3454 /* Non-power2 support */
3455 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3456 pow2Width = This->currentDesc.Width;
3457 pow2Height = This->currentDesc.Height;
3458 } else {
3459 /* Find the nearest pow2 match */
3460 pow2Width = pow2Height = 1;
3461 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3462 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3464 This->pow2Width = pow2Width;
3465 This->pow2Height = pow2Height;
3467 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3468 WINED3DFORMAT Format = This->resource.format;
3469 /** TODO: add support for non power two compressed textures **/
3470 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3471 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3472 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3473 This, This->currentDesc.Width, This->currentDesc.Height);
3474 return WINED3DERR_NOTAVAILABLE;
3478 if(pow2Width != This->currentDesc.Width ||
3479 pow2Height != This->currentDesc.Height) {
3480 This->Flags |= SFLAG_NONPOW2;
3483 TRACE("%p\n", This);
3484 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3485 /* one of three options
3486 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)
3487 2: Set the texture to the maximum size (bad idea)
3488 3: WARN and return WINED3DERR_NOTAVAILABLE;
3489 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.
3491 WARN("(%p) Creating an oversized surface\n", This);
3492 This->Flags |= SFLAG_OVERSIZE;
3494 /* This will be initialized on the first blt */
3495 This->glRect.left = 0;
3496 This->glRect.top = 0;
3497 This->glRect.right = 0;
3498 This->glRect.bottom = 0;
3499 } else {
3500 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one */
3501 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
3502 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3503 This->pow2Width = This->currentDesc.Width;
3504 This->pow2Height = This->currentDesc.Height;
3505 This->Flags &= ~SFLAG_NONPOW2;
3508 /* No oversize, gl rect is the full texture size */
3509 This->Flags &= ~SFLAG_OVERSIZE;
3510 This->glRect.left = 0;
3511 This->glRect.top = 0;
3512 This->glRect.right = This->pow2Width;
3513 This->glRect.bottom = This->pow2Height;
3516 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3517 switch(wined3d_settings.offscreen_rendering_mode) {
3518 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3519 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3520 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3524 This->Flags |= SFLAG_INSYSMEM;
3526 return WINED3D_OK;
3529 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3530 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3531 IWineD3DBaseTexture *texture;
3533 TRACE("(%p)->(%s, %s)\n", iface,
3534 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3535 persistent ? "TRUE" : "FALSE");
3537 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3538 IWineD3DSwapChain *swapchain = NULL;
3540 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3541 TRACE("Surface %p is an onscreen surface\n", iface);
3543 IWineD3DSwapChain_Release(swapchain);
3544 } else {
3545 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3546 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3550 if(persistent) {
3551 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
3552 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3553 TRACE("Passing to container\n");
3554 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3555 IWineD3DBaseTexture_Release(texture);
3558 This->Flags &= ~SFLAG_LOCATIONS;
3559 This->Flags |= flag;
3560 } else {
3561 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
3562 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3563 TRACE("Passing to container\n");
3564 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3565 IWineD3DBaseTexture_Release(texture);
3568 This->Flags &= ~flag;
3572 struct coords {
3573 int x, y, z;
3576 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
3577 struct coords coords[4];
3578 RECT rect;
3579 IWineD3DSwapChain *swapchain = NULL;
3580 IWineD3DBaseTexture *texture = NULL;
3581 HRESULT hr;
3582 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3584 if(rect_in) {
3585 rect = *rect_in;
3586 } else {
3587 rect.left = 0;
3588 rect.top = 0;
3589 rect.right = This->currentDesc.Width;
3590 rect.bottom = This->currentDesc.Height;
3593 ActivateContext(device, device->render_targets[0], CTXUSAGE_BLIT);
3594 ENTER_GL();
3596 if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
3597 glEnable(GL_TEXTURE_RECTANGLE_ARB);
3598 checkGLcall("glEnable(GL_TEXTURE_RECTANGLE_ARB)");
3599 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName);
3600 checkGLcall("GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName)");
3601 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3602 checkGLcall("glTexParameteri");
3603 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3604 checkGLcall("glTexParameteri");
3606 coords[0].x = rect.left;
3607 coords[0].z = 0;
3609 coords[1].x = rect.left;
3610 coords[1].z = 0;
3612 coords[2].x = rect.right;
3613 coords[2].z = 0;
3615 coords[3].x = rect.right;
3616 coords[3].z = 0;
3618 coords[0].y = rect.top;
3619 coords[1].y = rect.bottom;
3620 coords[2].y = rect.bottom;
3621 coords[3].y = rect.top;
3622 } else if(This->glDescription.target == GL_TEXTURE_2D) {
3623 glEnable(GL_TEXTURE_2D);
3624 checkGLcall("glEnable(GL_TEXTURE_2D)");
3625 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
3626 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
3627 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3628 checkGLcall("glTexParameteri");
3629 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3630 checkGLcall("glTexParameteri");
3632 coords[0].x = rect.left / This->pow2Width;
3633 coords[0].z = 0;
3635 coords[1].x = rect.left / This->pow2Width;
3636 coords[1].z = 0;
3638 coords[2].x = rect.right / This->pow2Width;
3639 coords[2].z = 0;
3641 coords[3].x = rect.right / This->pow2Width;
3642 coords[3].z = 0;
3644 coords[0].y = rect.top / This->pow2Height;
3645 coords[1].y = rect.bottom / This->pow2Height;
3646 coords[2].y = rect.bottom / This->pow2Height;
3647 coords[3].y = rect.top / This->pow2Height;
3648 } else {
3649 /* Must be a cube map */
3650 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
3651 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
3652 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
3653 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
3654 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3655 checkGLcall("glTexParameteri");
3656 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3657 checkGLcall("glTexParameteri");
3659 switch(This->glDescription.target) {
3660 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3661 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
3662 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3663 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3664 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
3665 break;
3667 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3668 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3669 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
3670 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
3671 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3672 break;
3674 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3675 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
3676 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3677 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3678 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
3679 break;
3681 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3682 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3683 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3684 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3685 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3686 break;
3688 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3689 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3690 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3691 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
3692 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
3693 break;
3695 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
3696 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
3697 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
3698 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3699 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3701 default:
3702 ERR("Unexpected texture target\n");
3703 LEAVE_GL();
3704 return;
3708 glBegin(GL_QUADS);
3709 glTexCoord3iv((GLint *) &coords[0]);
3710 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
3712 glTexCoord3iv((GLint *) &coords[1]);
3713 glVertex2i(0, device->render_offscreen ? rect.top : rect.bottom);
3715 glTexCoord3iv((GLint *) &coords[2]);
3716 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
3718 glTexCoord3iv((GLint *) &coords[3]);
3719 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
3720 glEnd();
3721 checkGLcall("glEnd");
3723 if(This->glDescription.target != GL_TEXTURE_2D) {
3724 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3725 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3726 } else {
3727 glDisable(GL_TEXTURE_2D);
3728 checkGLcall("glDisable(GL_TEXTURE_2D)");
3731 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain);
3732 if(hr == WINED3D_OK && swapchain) {
3733 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
3734 if(((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
3735 glFlush();
3737 IWineD3DSwapChain_Release(swapchain);
3738 } else {
3739 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
3740 * reset properly next draw
3742 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture);
3743 if(hr == WINED3D_OK && texture) {
3744 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
3745 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
3746 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
3747 IWineD3DBaseTexture_Release(texture);
3750 LEAVE_GL();
3753 /*****************************************************************************
3754 * IWineD3DSurface::LoadLocation
3756 * Copies the current surface data from wherever it is to the requested
3757 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
3758 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
3759 * multiple locations, the gl texture is preferred over the drawable, which is
3760 * preferred over system memory. The PBO counts as system memory. If rect is
3761 * not NULL, only the specified rectangle is copied (only supported for
3762 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
3763 * location is marked up to date after the copy.
3765 * Parameters:
3766 * flag: Surface location flag to be updated
3767 * rect: rectangle to be copied
3769 * Returns:
3770 * WINED3D_OK on success
3771 * WINED3DERR_DEVICELOST on an internal error
3773 *****************************************************************************/
3774 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
3775 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3776 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3777 IWineD3DSwapChain *swapchain = NULL;
3778 GLenum format, internal, type;
3779 CONVERT_TYPES convert;
3780 int bpp;
3781 int width, pitch, outpitch;
3782 BYTE *mem;
3784 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3785 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3786 TRACE("Surface %p is an onscreen surface\n", iface);
3788 IWineD3DSwapChain_Release(swapchain);
3789 } else {
3790 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
3791 * Prefer SFLAG_INTEXTURE. */
3792 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
3796 TRACE("(%p)->(%s, %p)\n", iface,
3797 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3798 rect);
3799 if(rect) {
3800 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
3803 if(This->Flags & flag) {
3804 TRACE("Location already up to date\n");
3805 return WINED3D_OK;
3808 if(!(This->Flags & SFLAG_LOCATIONS)) {
3809 ERR("Surface does not have any up to date location\n");
3810 This->Flags |= SFLAG_LOST;
3811 return WINED3DERR_DEVICELOST;
3814 if(flag == SFLAG_INSYSMEM) {
3815 surface_prepare_system_memory(This);
3817 /* Download the surface to system memory */
3818 if(This->Flags & SFLAG_INTEXTURE) {
3819 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
3820 surface_bind_and_dirtify(This);
3822 surface_download_data(This);
3823 } else {
3824 read_from_framebuffer(This, rect,
3825 This->resource.allocatedMemory,
3826 IWineD3DSurface_GetPitch(iface));
3828 } else if(flag == SFLAG_INDRAWABLE) {
3829 if(This->Flags & SFLAG_INTEXTURE) {
3830 surface_blt_to_drawable(This, rect);
3831 } else {
3832 flush_to_framebuffer_drawpixels(This);
3834 } else /* if(flag == SFLAG_INTEXTURE) */ {
3835 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
3837 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
3838 surface_bind_and_dirtify(This);
3840 if (This->Flags & SFLAG_INDRAWABLE) {
3841 GLint prevRead;
3843 ENTER_GL();
3844 glGetIntegerv(GL_READ_BUFFER, &prevRead);
3845 vcheckGLcall("glGetIntegerv");
3846 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
3847 vcheckGLcall("glReadBuffer");
3849 if(!(This->Flags & SFLAG_ALLOCATED)) {
3850 surface_allocate_surface(This, internal, This->pow2Width,
3851 This->pow2Height, format, type);
3854 clear_unused_channels(This);
3856 glCopyTexSubImage2D(This->glDescription.target,
3857 This->glDescription.level,
3858 0, 0, 0, 0,
3859 This->currentDesc.Width,
3860 This->currentDesc.Height);
3861 checkGLcall("glCopyTexSubImage2D");
3863 glReadBuffer(prevRead);
3864 vcheckGLcall("glReadBuffer");
3866 LEAVE_GL();
3868 TRACE("Updated target %d\n", This->glDescription.target);
3869 } else {
3870 /* The only place where LoadTexture() might get called when isInDraw=1
3871 * is ActivateContext where lastActiveRenderTarget is preloaded.
3873 if(iface == device->lastActiveRenderTarget && device->isInDraw)
3874 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
3876 /* Otherwise: System memory copy must be most up to date */
3878 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
3879 This->Flags |= SFLAG_GLCKEY;
3880 This->glCKey = This->SrcBltCKey;
3882 else This->Flags &= ~SFLAG_GLCKEY;
3884 /* The width is in 'length' not in bytes */
3885 width = This->currentDesc.Width;
3886 pitch = IWineD3DSurface_GetPitch(iface);
3888 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
3889 int height = This->currentDesc.Height;
3891 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
3892 outpitch = width * bpp;
3893 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
3895 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
3896 if(!mem) {
3897 ERR("Out of memory %d, %d!\n", outpitch, height);
3898 return WINED3DERR_OUTOFVIDEOMEMORY;
3900 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
3902 This->Flags |= SFLAG_CONVERTED;
3903 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
3904 d3dfmt_p8_upload_palette(iface, convert);
3905 This->Flags &= ~SFLAG_CONVERTED;
3906 mem = This->resource.allocatedMemory;
3907 } else {
3908 This->Flags &= ~SFLAG_CONVERTED;
3909 mem = This->resource.allocatedMemory;
3912 /* Make sure the correct pitch is used */
3913 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
3915 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
3916 TRACE("non power of two support\n");
3917 if(!(This->Flags & SFLAG_ALLOCATED)) {
3918 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
3920 if (mem || (This->Flags & SFLAG_PBO)) {
3921 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
3923 } else {
3924 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
3925 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
3927 if(!(This->Flags & SFLAG_ALLOCATED)) {
3928 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
3930 if (mem || (This->Flags & SFLAG_PBO)) {
3931 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
3935 /* Restore the default pitch */
3936 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
3938 /* Don't delete PBO memory */
3939 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
3940 HeapFree(GetProcessHeap(), 0, mem);
3944 if(rect == NULL) {
3945 This->Flags |= flag;
3948 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
3949 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
3950 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3951 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3954 return WINED3D_OK;
3957 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
3958 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3959 IWineD3DSwapChain *swapchain = NULL;
3961 /* Update the drawable size method */
3962 if(container) {
3963 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
3965 if(swapchain) {
3966 This->get_drawable_size = get_drawable_size_swapchain;
3967 IWineD3DSwapChain_Release(swapchain);
3968 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3969 switch(wined3d_settings.offscreen_rendering_mode) {
3970 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3971 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3972 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3976 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
3979 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3981 /* IUnknown */
3982 IWineD3DBaseSurfaceImpl_QueryInterface,
3983 IWineD3DBaseSurfaceImpl_AddRef,
3984 IWineD3DSurfaceImpl_Release,
3985 /* IWineD3DResource */
3986 IWineD3DBaseSurfaceImpl_GetParent,
3987 IWineD3DBaseSurfaceImpl_GetDevice,
3988 IWineD3DBaseSurfaceImpl_SetPrivateData,
3989 IWineD3DBaseSurfaceImpl_GetPrivateData,
3990 IWineD3DBaseSurfaceImpl_FreePrivateData,
3991 IWineD3DBaseSurfaceImpl_SetPriority,
3992 IWineD3DBaseSurfaceImpl_GetPriority,
3993 IWineD3DSurfaceImpl_PreLoad,
3994 IWineD3DBaseSurfaceImpl_GetType,
3995 /* IWineD3DSurface */
3996 IWineD3DBaseSurfaceImpl_GetContainer,
3997 IWineD3DBaseSurfaceImpl_GetDesc,
3998 IWineD3DSurfaceImpl_LockRect,
3999 IWineD3DSurfaceImpl_UnlockRect,
4000 IWineD3DSurfaceImpl_GetDC,
4001 IWineD3DSurfaceImpl_ReleaseDC,
4002 IWineD3DSurfaceImpl_Flip,
4003 IWineD3DSurfaceImpl_Blt,
4004 IWineD3DBaseSurfaceImpl_GetBltStatus,
4005 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4006 IWineD3DBaseSurfaceImpl_IsLost,
4007 IWineD3DBaseSurfaceImpl_Restore,
4008 IWineD3DSurfaceImpl_BltFast,
4009 IWineD3DBaseSurfaceImpl_GetPalette,
4010 IWineD3DBaseSurfaceImpl_SetPalette,
4011 IWineD3DBaseSurfaceImpl_RealizePalette,
4012 IWineD3DBaseSurfaceImpl_SetColorKey,
4013 IWineD3DBaseSurfaceImpl_GetPitch,
4014 IWineD3DSurfaceImpl_SetMem,
4015 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4016 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4017 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4018 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4019 IWineD3DBaseSurfaceImpl_SetClipper,
4020 IWineD3DBaseSurfaceImpl_GetClipper,
4021 /* Internal use: */
4022 IWineD3DSurfaceImpl_AddDirtyRect,
4023 IWineD3DSurfaceImpl_LoadTexture,
4024 IWineD3DSurfaceImpl_BindTexture,
4025 IWineD3DSurfaceImpl_SaveSnapshot,
4026 IWineD3DSurfaceImpl_SetContainer,
4027 IWineD3DSurfaceImpl_SetGlTextureDesc,
4028 IWineD3DSurfaceImpl_GetGlDesc,
4029 IWineD3DSurfaceImpl_GetData,
4030 IWineD3DSurfaceImpl_SetFormat,
4031 IWineD3DSurfaceImpl_PrivateSetup,
4032 IWineD3DSurfaceImpl_ModifyLocation,
4033 IWineD3DSurfaceImpl_LoadLocation