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
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");
58 if(This->Flags & SFLAG_CONVERTED) {
59 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(This->resource.format));
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);
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");
82 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
83 checkGLcall("glGetCompressedTexImageARB()");
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);
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");
116 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
117 This->glDescription.glType, mem);
118 checkGLcall("glGetTexImage()");
122 if (This->Flags & SFLAG_NONPOW2) {
123 LPBYTE src_data, dst_data;
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
141 * -----------------------------------
144 * we're repacking the data to the expected texture width
146 * |<-texture width ->| -->pow2width| /\
147 * |111111111111111111222222222222222| |
148 * |222333333333333333333444444444444| texture height
152 * | empty | pow2height
154 * -----------------------------------
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.
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");
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);
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");
221 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
222 width, height, 0 /* border */, This->resource.size, data));
223 checkGLcall("glCompressedTexSubImage2D");
228 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
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");
243 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
244 checkGLcall("glTexSubImage2D");
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;
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));
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;
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)");
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;
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;
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");
375 } else if (swapchain_impl->frontBuffer == iface) {
376 TRACE("Returning GL_FRONT\n");
380 FIXME("Higher back buffer, returning GL_BACK\n");
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);
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);
405 glDeleteTextures(1, &This->glDescription.textureName);
409 if(This->Flags & SFLAG_PBO) {
411 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
414 if(This->Flags & SFLAG_DIBSECTION) {
416 SelectObject(This->hDC, This->dib.holdbitmap);
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);
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);
459 TRACE("(%p) : About to load surface\n", This);
461 if(!device->isInDraw) {
462 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
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) */
485 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
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;
530 BYTE *row, *top, *bottom;
534 BOOL srcIsUpsideDown;
536 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
537 static BOOL warned = FALSE;
539 ERR("The application tries to lock the render target, but render target locking is disabled\n");
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);
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.
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;
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 */
579 local_rect.right = This->currentDesc.Width;
580 local_rect.bottom = This->currentDesc.Height;
584 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
586 switch(This->resource.format)
590 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
591 /* In case of P8 render targets the index is stored in the alpha component */
593 type = GL_UNSIGNED_BYTE;
595 bpp = This->bytesPerPixel;
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.
607 type = GL_UNSIGNED_BYTE;
609 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
611 ERR("Out of memory\n");
615 bpp = This->bytesPerPixel * 3;
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,
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) {
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);
665 ERR("Out of memory\n");
666 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
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);
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)) {
696 DWORD width = pitch / 3;
699 pal = This->palette->palents;
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;
722 HeapFree(GetProcessHeap(), 0, mem);
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
732 if(!(This->Flags & SFLAG_DYNLOCK)) {
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)) {
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;
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,
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 */
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
836 pRect->right == This->currentDesc.Width &&
837 pRect->bottom == This->currentDesc.Height) {
841 switch(wined3d_settings.rendertargetlock_mode) {
844 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
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 */);
857 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
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
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,
877 * GL_DEPTH_COMPONENT,
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");
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 */);
893 if(This->Flags & SFLAG_PBO) {
894 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
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");
914 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
917 IWineD3DBaseTexture *pBaseTexture;
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);
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, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
939 GLint prev_rasterpos[4];
941 BOOL storechanged = FALSE;
942 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
943 IWineD3DDeviceImpl *myDevice = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
944 IWineD3DSwapChainImpl *swapchain;
946 /* Activate the correct context for the render target */
947 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
950 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
952 /* Primary offscreen render target */
953 TRACE("Offscreen render target\n");
954 glDrawBuffer(myDevice->offscreenBuffer);
955 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
957 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
958 TRACE("Unlocking %#x buffer\n", buffer);
959 glDrawBuffer(buffer);
960 checkGLcall("glDrawBuffer");
962 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
966 vcheckGLcall("glFlush");
967 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
968 vcheckGLcall("glIntegerv");
969 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
970 vcheckGLcall("glIntegerv");
971 glPixelZoom(1.0, -1.0);
972 vcheckGLcall("glPixelZoom");
974 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
975 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
976 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
978 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
979 vcheckGLcall("glRasterPos2f");
981 /* Some drivers(radeon dri, others?) don't like exceptions during
982 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
983 * after ReleaseDC. Reading it will cause an exception, which x11drv will
984 * catch to put the dib section in InSync mode, which leads to a crash
985 * and a blocked x server on my radeon card.
987 * The following lines read the dib section so it is put in inSync mode
988 * before glDrawPixels is called and the crash is prevented. There won't
989 * be any interfering gdi accesses, because UnlockRect is called from
990 * ReleaseDC, and the app won't use the dc any more afterwards.
992 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
994 read = This->resource.allocatedMemory[0];
997 if(This->Flags & SFLAG_PBO) {
998 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
999 checkGLcall("glBindBufferARB");
1002 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1003 (This->lockedRect.bottom - This->lockedRect.top)-1,
1005 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1006 checkGLcall("glDrawPixels");
1008 if(This->Flags & SFLAG_PBO) {
1009 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1010 checkGLcall("glBindBufferARB");
1013 glPixelZoom(1.0,1.0);
1014 vcheckGLcall("glPixelZoom");
1016 glRasterPos3iv(&prev_rasterpos[0]);
1017 vcheckGLcall("glRasterPos3iv");
1019 /* Reset to previous pack row length */
1020 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1021 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1023 glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
1024 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
1028 glDrawBuffer(myDevice->offscreenBuffer);
1029 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1030 } else if(swapchain->backBuffer) {
1031 glDrawBuffer(GL_BACK);
1032 checkGLcall("glDrawBuffer(GL_BACK)");
1034 glDrawBuffer(GL_FRONT);
1035 checkGLcall("glDrawBuffer(GL_FRONT)");
1042 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1043 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1044 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1045 IWineD3DSwapChainImpl *swapchain = NULL;
1048 if (!(This->Flags & SFLAG_LOCKED)) {
1049 WARN("trying to Unlock an unlocked surf@%p\n", This);
1050 return WINED3DERR_INVALIDCALL;
1053 if (This->Flags & SFLAG_PBO) {
1054 TRACE("Freeing PBO memory\n");
1055 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1057 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1058 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1059 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1060 checkGLcall("glUnmapBufferARB");
1062 This->resource.allocatedMemory = NULL;
1065 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1067 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1068 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1072 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1073 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1074 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1075 static BOOL warned = FALSE;
1077 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1080 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1084 if(This->dirtyRect.left == 0 &&
1085 This->dirtyRect.top == 0 &&
1086 This->dirtyRect.right == This->currentDesc.Width &&
1087 This->dirtyRect.bottom == This->currentDesc.Height) {
1090 /* TODO: Proper partial rectangle tracking */
1091 fullsurface = FALSE;
1092 This->Flags |= SFLAG_INSYSMEM;
1095 switch(wined3d_settings.rendertargetlock_mode) {
1098 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1100 if (This->glDescription.textureName == 0) {
1101 glGenTextures(1, &This->glDescription.textureName);
1102 checkGLcall("glGenTextures");
1104 glBindTexture(This->glDescription.target, This->glDescription.textureName);
1105 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
1107 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1113 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1118 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1119 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1120 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1121 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1122 * not fully up to date because only a subrectangle was read in LockRect.
1124 This->Flags &= ~SFLAG_INSYSMEM;
1125 This->Flags |= SFLAG_INDRAWABLE;
1128 This->dirtyRect.left = This->currentDesc.Width;
1129 This->dirtyRect.top = This->currentDesc.Height;
1130 This->dirtyRect.right = 0;
1131 This->dirtyRect.bottom = 0;
1132 } else if(iface == myDevice->stencilBufferTarget) {
1133 FIXME("Depth Stencil buffer locking is not implemented\n");
1135 /* The rest should be a normal texture */
1136 IWineD3DBaseTextureImpl *impl;
1137 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1138 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1139 * states need resetting
1141 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1142 if(impl->baseTexture.bindCount) {
1143 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1145 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1150 This->Flags &= ~SFLAG_LOCKED;
1151 memset(&This->lockedRect, 0, sizeof(RECT));
1155 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1156 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1157 WINED3DLOCKED_RECT lock;
1161 TRACE("(%p)->(%p)\n",This,pHDC);
1163 if(This->Flags & SFLAG_USERPTR) {
1164 ERR("Not supported on surfaces with an application-provided surfaces\n");
1165 return WINEDDERR_NODC;
1168 /* Give more detailed info for ddraw */
1169 if (This->Flags & SFLAG_DCINUSE)
1170 return WINEDDERR_DCALREADYCREATED;
1172 /* Can't GetDC if the surface is locked */
1173 if (This->Flags & SFLAG_LOCKED)
1174 return WINED3DERR_INVALIDCALL;
1176 memset(&lock, 0, sizeof(lock)); /* To be sure */
1178 /* Create a DIB section if there isn't a hdc yet */
1180 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1181 if(This->Flags & SFLAG_CLIENT) {
1182 IWineD3DSurface_PreLoad(iface);
1185 /* Use the dib section from now on if we are not using a PBO */
1186 if(!(This->Flags & SFLAG_PBO))
1187 This->resource.allocatedMemory = This->dib.bitmap_data;
1190 /* Lock the surface */
1191 hr = IWineD3DSurface_LockRect(iface,
1196 if(This->Flags & SFLAG_PBO) {
1197 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1198 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1202 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1203 /* keep the dib section */
1207 if(This->resource.format == WINED3DFMT_P8 ||
1208 This->resource.format == WINED3DFMT_A8P8) {
1211 PALETTEENTRY ent[256];
1213 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1214 for (n=0; n<256; n++) {
1215 col[n].rgbRed = ent[n].peRed;
1216 col[n].rgbGreen = ent[n].peGreen;
1217 col[n].rgbBlue = ent[n].peBlue;
1218 col[n].rgbReserved = 0;
1221 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1223 for (n=0; n<256; n++) {
1224 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1225 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1226 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1227 col[n].rgbReserved = 0;
1231 SetDIBColorTable(This->hDC, 0, 256, col);
1235 TRACE("returning %p\n",*pHDC);
1236 This->Flags |= SFLAG_DCINUSE;
1241 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1242 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1244 TRACE("(%p)->(%p)\n",This,hDC);
1246 if (!(This->Flags & SFLAG_DCINUSE))
1247 return WINED3DERR_INVALIDCALL;
1249 if (This->hDC !=hDC) {
1250 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1251 return WINED3DERR_INVALIDCALL;
1254 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1255 /* Copy the contents of the DIB over to the PBO */
1256 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1259 /* we locked first, so unlock now */
1260 IWineD3DSurface_UnlockRect(iface);
1262 This->Flags &= ~SFLAG_DCINUSE;
1267 /* ******************************************************
1268 IWineD3DSurface Internal (No mapping to directx api) parts follow
1269 ****************************************************** */
1271 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) {
1272 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1273 const GlPixelFormatDesc *glDesc;
1274 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1275 BOOL p8_render_target = FALSE;
1276 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1278 /* Default values: From the surface */
1279 *format = glDesc->glFormat;
1280 *type = glDesc->glType;
1281 *convert = NO_CONVERSION;
1282 *target_bpp = This->bytesPerPixel;
1285 *internal = glDesc->glGammaInternal;
1286 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1287 *internal = glDesc->rtInternal;
1289 *internal = glDesc->glInternal;
1292 /* Ok, now look if we have to do any conversion */
1293 switch(This->resource.format) {
1299 if (device->render_targets && device->render_targets[0]) {
1300 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
1301 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
1302 p8_render_target = TRUE;
1305 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1306 * of the two is available make sure texturing is requested as neither of the two works in
1307 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1308 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1309 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1310 * conflicts with this.
1312 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) && p8_render_target)) || colorkey_active || !use_texturing ) {
1314 *internal = GL_RGBA;
1315 *type = GL_UNSIGNED_BYTE;
1317 if(colorkey_active) {
1318 *convert = CONVERT_PALETTED_CK;
1320 *convert = CONVERT_PALETTED;
1323 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1325 *internal = GL_RGBA;
1326 *type = GL_UNSIGNED_BYTE;
1332 case WINED3DFMT_R3G3B2:
1333 /* **********************
1334 GL_UNSIGNED_BYTE_3_3_2
1335 ********************** */
1336 if (colorkey_active) {
1337 /* This texture format will never be used.. So do not care about color keying
1338 up until the point in time it will be needed :-) */
1339 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1343 case WINED3DFMT_R5G6B5:
1344 if (colorkey_active) {
1345 *convert = CONVERT_CK_565;
1347 *internal = GL_RGBA;
1348 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1352 case WINED3DFMT_X1R5G5B5:
1353 if (colorkey_active) {
1354 *convert = CONVERT_CK_5551;
1356 *internal = GL_RGBA;
1357 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1361 case WINED3DFMT_R8G8B8:
1362 if (colorkey_active) {
1363 *convert = CONVERT_CK_RGB24;
1365 *internal = GL_RGBA;
1366 *type = GL_UNSIGNED_INT_8_8_8_8;
1371 case WINED3DFMT_X8R8G8B8:
1372 if (colorkey_active) {
1373 *convert = CONVERT_RGB32_888;
1375 *internal = GL_RGBA;
1376 *type = GL_UNSIGNED_INT_8_8_8_8;
1380 case WINED3DFMT_V8U8:
1381 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1382 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1383 *format = GL_DUDV_ATI;
1384 *internal = GL_DU8DV8_ATI;
1386 /* No conversion - Just change the gl type */
1389 *convert = CONVERT_V8U8;
1391 *internal = GL_RGB8;
1392 *type = GL_UNSIGNED_BYTE;
1396 case WINED3DFMT_L6V5U5:
1397 *convert = CONVERT_L6V5U5;
1398 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1400 /* Use format and types from table */
1402 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1405 *internal = GL_RGB5;
1406 *type = GL_UNSIGNED_SHORT_5_6_5;
1410 case WINED3DFMT_X8L8V8U8:
1411 *convert = CONVERT_X8L8V8U8;
1413 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1414 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1415 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1416 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1417 * the needed type and format parameter, so the internal format contains a
1418 * 4th component, which is returned as alpha
1421 /* Not supported by GL_ATI_envmap_bumpmap */
1423 *internal = GL_RGB8;
1424 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1428 case WINED3DFMT_Q8W8V8U8:
1429 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1430 *convert = CONVERT_Q8W8V8U8;
1432 *internal = GL_RGBA8;
1433 *type = GL_UNSIGNED_BYTE;
1435 /* Not supported by GL_ATI_envmap_bumpmap */
1438 case WINED3DFMT_V16U16:
1439 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1440 *convert = CONVERT_V16U16;
1442 *internal = GL_RGB16_EXT;
1443 *type = GL_UNSIGNED_SHORT;
1445 /* What should I do here about GL_ATI_envmap_bumpmap?
1446 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1450 case WINED3DFMT_A4L4:
1451 /* A4L4 exists as an internal gl format, but for some reason there is not
1452 * format+type combination to load it. Thus convert it to A8L8, then load it
1453 * with A4L4 internal, but A8L8 format+type
1455 *convert = CONVERT_A4L4;
1456 *format = GL_LUMINANCE_ALPHA;
1457 *internal = GL_LUMINANCE4_ALPHA4;
1458 *type = GL_UNSIGNED_BYTE;
1462 case WINED3DFMT_R32F:
1463 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1464 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1465 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1468 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1470 *convert = CONVERT_R32F;
1472 *internal = GL_RGB32F_ARB;
1477 case WINED3DFMT_R16F:
1478 /* Similar to R32F */
1479 *convert = CONVERT_R16F;
1481 *internal = GL_RGB16F_ARB;
1482 *type = GL_HALF_FLOAT_ARB;
1486 case WINED3DFMT_G16R16:
1487 *convert = CONVERT_G16R16;
1489 *internal = GL_RGB16_EXT;
1490 *type = GL_UNSIGNED_SHORT;
1501 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1502 BYTE *source, *dest;
1503 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1508 memcpy(dst, src, pitch * height);
1511 case CONVERT_PALETTED:
1512 case CONVERT_PALETTED_CK:
1514 IWineD3DPaletteImpl* pal = This->palette;
1519 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1522 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1524 for (y = 0; y < height; y++)
1526 source = src + pitch * y;
1527 dest = dst + outpitch * y;
1528 /* This is an 1 bpp format, using the width here is fine */
1529 for (x = 0; x < width; x++) {
1530 BYTE color = *source++;
1531 *dest++ = table[color][0];
1532 *dest++ = table[color][1];
1533 *dest++ = table[color][2];
1534 *dest++ = table[color][3];
1540 case CONVERT_CK_565:
1542 /* Converting the 565 format in 5551 packed to emulate color-keying.
1544 Note : in all these conversion, it would be best to average the averaging
1545 pixels to get the color of the pixel that will be color-keyed to
1546 prevent 'color bleeding'. This will be done later on if ever it is
1549 Note2: Nvidia documents say that their driver does not support alpha + color keying
1550 on the same surface and disables color keying in such a case
1556 TRACE("Color keyed 565\n");
1558 for (y = 0; y < height; y++) {
1559 Source = (WORD *) (src + y * pitch);
1560 Dest = (WORD *) (dst + y * outpitch);
1561 for (x = 0; x < width; x++ ) {
1562 WORD color = *Source++;
1563 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1564 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1565 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1574 case CONVERT_CK_5551:
1576 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1580 TRACE("Color keyed 5551\n");
1581 for (y = 0; y < height; y++) {
1582 Source = (WORD *) (src + y * pitch);
1583 Dest = (WORD *) (dst + y * outpitch);
1584 for (x = 0; x < width; x++ ) {
1585 WORD color = *Source++;
1587 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1588 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1592 *Dest &= ~(1 << 15);
1604 unsigned char *Dest;
1605 for(y = 0; y < height; y++) {
1606 Source = (short *) (src + y * pitch);
1607 Dest = (unsigned char *) (dst + y * outpitch);
1608 for (x = 0; x < width; x++ ) {
1609 long color = (*Source++);
1610 /* B */ Dest[0] = 0xff;
1611 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1612 /* R */ Dest[2] = (color) + 128; /* U */
1619 case CONVERT_V16U16:
1623 unsigned short *Dest;
1624 for(y = 0; y < height; y++) {
1625 Source = (DWORD *) (src + y * pitch);
1626 Dest = (unsigned short *) (dst + y * outpitch);
1627 for (x = 0; x < width; x++ ) {
1628 DWORD color = (*Source++);
1629 /* B */ Dest[0] = 0xffff;
1630 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1631 /* R */ Dest[2] = (color ) + 32768; /* U */
1638 case CONVERT_Q8W8V8U8:
1642 unsigned char *Dest;
1643 for(y = 0; y < height; y++) {
1644 Source = (DWORD *) (src + y * pitch);
1645 Dest = (unsigned char *) (dst + y * outpitch);
1646 for (x = 0; x < width; x++ ) {
1647 long color = (*Source++);
1648 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1649 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1650 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1651 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1658 case CONVERT_L6V5U5:
1662 unsigned char *Dest;
1664 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1665 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1666 * fixed function and shaders without further conversion once the surface is
1669 for(y = 0; y < height; y++) {
1670 Source = (WORD *) (src + y * pitch);
1671 Dest = (unsigned char *) (dst + y * outpitch);
1672 for (x = 0; x < width; x++ ) {
1673 short color = (*Source++);
1674 unsigned char l = ((color >> 10) & 0xfc);
1675 char v = ((color >> 5) & 0x3e);
1676 char u = ((color ) & 0x1f);
1678 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1679 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1680 * shift. GL reads a signed value and converts it into an unsigned value.
1682 /* M */ Dest[2] = l << 1;
1684 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1685 * from 5 bit values to 8 bit values.
1687 /* V */ Dest[1] = v << 3;
1688 /* U */ Dest[0] = u << 3;
1693 for(y = 0; y < height; y++) {
1694 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1695 Source = (WORD *) (src + y * pitch);
1696 for (x = 0; x < width; x++ ) {
1697 short color = (*Source++);
1698 unsigned char l = ((color >> 10) & 0xfc);
1699 short v = ((color >> 5) & 0x3e);
1700 short u = ((color ) & 0x1f);
1701 short v_conv = v + 16;
1702 short u_conv = u + 16;
1704 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1712 case CONVERT_X8L8V8U8:
1716 unsigned char *Dest;
1718 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1719 /* This implementation works with the fixed function pipeline and shaders
1720 * without further modification after converting the surface.
1722 for(y = 0; y < height; y++) {
1723 Source = (DWORD *) (src + y * pitch);
1724 Dest = (unsigned char *) (dst + y * outpitch);
1725 for (x = 0; x < width; x++ ) {
1726 long color = (*Source++);
1727 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1728 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1729 /* U */ Dest[0] = (color & 0xff); /* U */
1730 /* I */ Dest[3] = 255; /* X */
1735 /* Doesn't work correctly with the fixed function pipeline, but can work in
1736 * shaders if the shader is adjusted. (There's no use for this format in gl's
1737 * standard fixed function pipeline anyway).
1739 for(y = 0; y < height; y++) {
1740 Source = (DWORD *) (src + y * pitch);
1741 Dest = (unsigned char *) (dst + y * outpitch);
1742 for (x = 0; x < width; x++ ) {
1743 long color = (*Source++);
1744 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1745 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1746 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1757 unsigned char *Source;
1758 unsigned char *Dest;
1759 for(y = 0; y < height; y++) {
1760 Source = (unsigned char *) (src + y * pitch);
1761 Dest = (unsigned char *) (dst + y * outpitch);
1762 for (x = 0; x < width; x++ ) {
1763 unsigned char color = (*Source++);
1764 /* A */ Dest[1] = (color & 0xf0) << 0;
1765 /* L */ Dest[0] = (color & 0x0f) << 4;
1777 for(y = 0; y < height; y++) {
1778 Source = (float *) (src + y * pitch);
1779 Dest = (float *) (dst + y * outpitch);
1780 for (x = 0; x < width; x++ ) {
1781 float color = (*Source++);
1797 for(y = 0; y < height; y++) {
1798 Source = (WORD *) (src + y * pitch);
1799 Dest = (WORD *) (dst + y * outpitch);
1800 for (x = 0; x < width; x++ ) {
1801 WORD color = (*Source++);
1811 case CONVERT_G16R16:
1817 for(y = 0; y < height; y++) {
1818 Source = (WORD *) (src + y * pitch);
1819 Dest = (WORD *) (dst + y * outpitch);
1820 for (x = 0; x < width; x++ ) {
1821 WORD green = (*Source++);
1822 WORD red = (*Source++);
1833 ERR("Unsupported conversation type %d\n", convert);
1838 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
1839 IWineD3DPaletteImpl* pal = This->palette;
1840 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1841 BOOL index_in_alpha = FALSE;
1844 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1845 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1846 * is slow. Further RGB->P8 conversion is not possible because palettes can have
1847 * duplicate entries. Store the color key in the unused alpha component to speed the
1848 * download up and to make conversion unneeded. */
1849 if (device->render_targets && device->render_targets[0]) {
1850 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
1852 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
1853 index_in_alpha = TRUE;
1857 /* Still no palette? Use the device's palette */
1858 /* Get the surface's palette */
1859 for (i = 0; i < 256; i++) {
1860 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1861 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1862 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1864 if(index_in_alpha) {
1866 } else if (colorkey &&
1867 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1868 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1869 /* We should maybe here put a more 'neutral' color than the standard bright purple
1870 one often used by application to prevent the nice purple borders when bi-linear
1878 TRACE("Using surface palette %p\n", pal);
1879 /* Get the surface's palette */
1880 for (i = 0; i < 256; i++) {
1881 table[i][0] = pal->palents[i].peRed;
1882 table[i][1] = pal->palents[i].peGreen;
1883 table[i][2] = pal->palents[i].peBlue;
1885 if(index_in_alpha) {
1888 else if (colorkey &&
1889 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1890 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1891 /* We should maybe here put a more 'neutral' color than the standard bright purple
1892 one often used by application to prevent the nice purple borders when bi-linear
1895 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
1896 table[i][3] = pal->palents[i].peFlags;
1904 const char *fragment_palette_conversion =
1907 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
1908 "TEX index.x, fragment.texcoord[0], texture[0], 2D;\n" /* store the red-component of the current pixel */
1909 "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 */
1910 "TEX result.color, index, texture[1], 1D;\n" /* use the red-component as a index in the palette to get the final color */
1913 /* This function is used in case of 8bit paletted textures to upload the palette.
1914 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
1915 extensions like ATI_fragment_shaders is possible.
1917 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
1918 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1920 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1922 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1924 /* Try to use the paletted texture extension */
1925 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
1927 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
1928 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
1932 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
1933 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
1934 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
1936 /* Create the fragment program if we don't have it */
1937 if(!device->paletteConversionShader)
1939 glEnable(GL_FRAGMENT_PROGRAM_ARB);
1940 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
1941 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
1942 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
1943 glDisable(GL_FRAGMENT_PROGRAM_ARB);
1946 glEnable(GL_FRAGMENT_PROGRAM_ARB);
1947 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
1949 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
1950 glEnable(GL_TEXTURE_1D);
1951 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
1953 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1954 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
1955 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1956 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
1958 /* Switch back to unit 0 in which the 2D texture will be stored. */
1959 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
1961 /* Rebind the texture because it isn't bound anymore */
1962 glBindTexture(This->glDescription.target, This->glDescription.textureName);
1966 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
1967 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1969 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
1970 /* If a ddraw-style palette is attached assume no d3d9 palette change.
1971 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
1976 if(This->palette9) {
1977 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
1981 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
1983 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
1987 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
1988 GLboolean oldwrite[4];
1990 /* Some formats have only some color channels, and the others are 1.0.
1991 * since our rendering renders to all channels, and those pixel formats
1992 * are emulated by using a full texture with the other channels set to 1.0
1993 * manually, clear the unused channels.
1995 * This could be done with hacking colorwriteenable to mask the colors,
1996 * but before drawing the buffer would have to be cleared too, so there's
1999 switch(This->resource.format) {
2000 case WINED3DFMT_R16F:
2001 case WINED3DFMT_R32F:
2002 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2003 /* Do not activate a context, the correct drawable is active already
2004 * though just the read buffer is set, make sure to have the correct draw
2007 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2008 glDisable(GL_SCISSOR_TEST);
2009 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2010 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2011 glClearColor(0.0, 1.0, 1.0, 1.0);
2012 glClear(GL_COLOR_BUFFER_BIT);
2013 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2014 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2015 checkGLcall("Unused channel clear\n");
2022 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2023 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2025 if (!(This->Flags & SFLAG_INTEXTURE)) {
2026 TRACE("Reloading because surface is dirty\n");
2027 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2028 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2029 /* Reload: vice versa OR */
2030 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2031 /* Also reload: Color key is active AND the color key has changed */
2032 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2033 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2034 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2035 TRACE("Reloading because of color keying\n");
2036 /* To perform the color key conversion we need a sysmem copy of
2037 * the surface. Make sure we have it
2039 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2040 } else if(palette9_changed(This)) {
2041 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2042 /* TODO: This is not necessarily needed with hw palettized texture support */
2043 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2045 TRACE("surface is already in texture\n");
2049 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2050 * These resources are not bound by device size or format restrictions. Because of this,
2051 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2052 * However, these resources can always be created, locked, and copied.
2054 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2056 FIXME("(%p) Operation not supported for scratch textures\n",This);
2057 return WINED3DERR_INVALIDCALL;
2060 This->srgb = srgb_mode;
2061 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2065 static unsigned int gen = 0;
2068 if ((gen % 10) == 0) {
2069 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2070 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2073 * debugging crash code
2082 if (!(This->Flags & SFLAG_DONOTFREE)) {
2083 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2084 This->resource.allocatedMemory = NULL;
2085 This->resource.heapMemory = NULL;
2086 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2092 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2093 /* TODO: check for locks */
2094 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2095 IWineD3DBaseTexture *baseTexture = NULL;
2096 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2098 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2099 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2100 TRACE("Passing to container\n");
2101 IWineD3DBaseTexture_BindTexture(baseTexture);
2102 IWineD3DBaseTexture_Release(baseTexture);
2104 TRACE("(%p) : Binding surface\n", This);
2106 if(!device->isInDraw) {
2107 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2110 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2118 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2121 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2122 char *allocatedMemory;
2124 IWineD3DSwapChain *swapChain = NULL;
2126 GLuint tmpTexture = 0;
2129 Textures may not be stored in ->allocatedgMemory and a GlTexture
2130 so we should lock the surface before saving a snapshot, or at least check that
2132 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2133 by calling GetTexImage and in compressed form by calling
2134 GetCompressedTexImageARB. Queried compressed images can be saved and
2135 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2136 texture images do not need to be processed by the GL and should
2137 significantly improve texture loading performance relative to uncompressed
2140 /* Setup the width and height to be the internal texture width and height. */
2141 width = This->pow2Width;
2142 height = This->pow2Height;
2143 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2144 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2146 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2147 /* if were not a real texture then read the back buffer into a real texture */
2148 /* we don't want to interfere with the back buffer so read the data into a temporary
2149 * texture and then save the data out of the temporary texture
2153 TRACE("(%p) Reading render target into texture\n", This);
2154 glEnable(GL_TEXTURE_2D);
2156 glGenTextures(1, &tmpTexture);
2157 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2159 glTexImage2D(GL_TEXTURE_2D,
2166 GL_UNSIGNED_INT_8_8_8_8_REV,
2169 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2170 vcheckGLcall("glGetIntegerv");
2171 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2172 vcheckGLcall("glReadBuffer");
2173 glCopyTexImage2D(GL_TEXTURE_2D,
2182 checkGLcall("glCopyTexImage2D");
2183 glReadBuffer(prevRead);
2186 } else { /* bind the real texture, and make sure it up to date */
2187 IWineD3DSurface_PreLoad(iface);
2189 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2191 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2192 glGetTexImage(GL_TEXTURE_2D,
2193 This->glDescription.level,
2195 GL_UNSIGNED_INT_8_8_8_8_REV,
2197 checkGLcall("glTexImage2D");
2199 glBindTexture(GL_TEXTURE_2D, 0);
2200 glDeleteTextures(1, &tmpTexture);
2204 f = fopen(filename, "w+");
2206 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2207 return WINED3DERR_INVALIDCALL;
2209 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2210 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2225 fwrite(&width,2,1,f);
2227 fwrite(&height,2,1,f);
2232 /* 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 */
2234 textureRow = allocatedMemory + (width * (height - 1) *4);
2236 textureRow = allocatedMemory;
2237 for (y = 0 ; y < height; y++) {
2238 for (i = 0; i < width; i++) {
2239 color = *((DWORD*)textureRow);
2240 fputc((color >> 16) & 0xFF, f); /* B */
2241 fputc((color >> 8) & 0xFF, f); /* G */
2242 fputc((color >> 0) & 0xFF, f); /* R */
2243 fputc((color >> 24) & 0xFF, f); /* A */
2246 /* take two rows of the pointer to the texture memory */
2248 (textureRow-= width << 3);
2251 TRACE("Closing file\n");
2255 IWineD3DSwapChain_Release(swapChain);
2257 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2262 * Slightly inefficient way to handle multiple dirty rects but it works :)
2264 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2265 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2266 IWineD3DBaseTexture *baseTexture = NULL;
2268 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2269 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2271 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2272 if (NULL != pDirtyRect) {
2273 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2274 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2275 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2276 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2278 This->dirtyRect.left = 0;
2279 This->dirtyRect.top = 0;
2280 This->dirtyRect.right = This->currentDesc.Width;
2281 This->dirtyRect.bottom = This->currentDesc.Height;
2283 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2284 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2285 /* if the container is a basetexture then mark it dirty. */
2286 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2287 TRACE("Passing to container\n");
2288 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2289 IWineD3DBaseTexture_Release(baseTexture);
2294 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2295 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2297 const GlPixelFormatDesc *glDesc;
2298 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2300 TRACE("(%p) : Calling base function first\n", This);
2301 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2303 /* Setup some glformat defaults */
2304 This->glDescription.glFormat = glDesc->glFormat;
2305 This->glDescription.glFormatInternal = glDesc->glInternal;
2306 This->glDescription.glType = glDesc->glType;
2308 This->Flags &= ~SFLAG_ALLOCATED;
2309 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2310 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2315 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2316 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2318 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2319 WARN("Surface is locked or the HDC is in use\n");
2320 return WINED3DERR_INVALIDCALL;
2323 if(Mem && Mem != This->resource.allocatedMemory) {
2324 void *release = NULL;
2326 /* Do I have to copy the old surface content? */
2327 if(This->Flags & SFLAG_DIBSECTION) {
2328 /* Release the DC. No need to hold the critical section for the update
2329 * Thread because this thread runs only on front buffers, but this method
2330 * fails for render targets in the check above.
2332 SelectObject(This->hDC, This->dib.holdbitmap);
2333 DeleteDC(This->hDC);
2334 /* Release the DIB section */
2335 DeleteObject(This->dib.DIBsection);
2336 This->dib.bitmap_data = NULL;
2337 This->resource.allocatedMemory = NULL;
2339 This->Flags &= ~SFLAG_DIBSECTION;
2340 } else if(!(This->Flags & SFLAG_USERPTR)) {
2341 release = This->resource.heapMemory;
2342 This->resource.heapMemory = NULL;
2344 This->resource.allocatedMemory = Mem;
2345 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2347 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2348 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2350 /* For client textures opengl has to be notified */
2351 if(This->Flags & SFLAG_CLIENT) {
2352 This->Flags &= ~SFLAG_ALLOCATED;
2353 IWineD3DSurface_PreLoad(iface);
2354 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2357 /* Now free the old memory if any */
2358 HeapFree(GetProcessHeap(), 0, release);
2359 } else if(This->Flags & SFLAG_USERPTR) {
2360 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2361 This->resource.allocatedMemory = NULL;
2362 /* HeapMemory should be NULL already */
2363 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2364 This->Flags &= ~SFLAG_USERPTR;
2366 if(This->Flags & SFLAG_CLIENT) {
2367 This->Flags &= ~SFLAG_ALLOCATED;
2368 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2369 IWineD3DSurface_PreLoad(iface);
2375 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2376 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2377 IWineD3DSwapChainImpl *swapchain = NULL;
2379 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2381 /* Flipping is only supported on RenderTargets */
2382 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2385 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2386 * FIXME("(%p) Target override is not supported by now\n", This);
2387 * Additionally, it isn't really possible to support triple-buffering
2388 * properly on opengl at all
2392 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2394 ERR("Flipped surface is not on a swapchain\n");
2395 return WINEDDERR_NOTFLIPPABLE;
2398 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2399 * and only d3d8 and d3d9 apps specify the presentation interval
2401 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2402 /* Most common case first to avoid wasting time on all the other cases */
2403 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2404 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2405 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2406 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2407 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2408 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2409 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2411 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2414 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2415 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2416 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2420 /* Does a direct frame buffer -> texture copy. Stretching is done
2421 * with single pixel copy calls
2423 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2424 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2427 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2430 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2432 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2434 /* TODO: Do we need GL_TEXTURE_2D enabled fpr copyteximage? */
2435 glEnable(This->glDescription.target);
2436 checkGLcall("glEnable(This->glDescription.target)");
2438 /* Bind the target texture */
2439 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2440 checkGLcall("glBindTexture");
2442 glReadBuffer(myDevice->offscreenBuffer);
2444 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2445 glReadBuffer(buffer);
2447 checkGLcall("glReadBuffer");
2449 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2450 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2452 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2453 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2455 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2456 ERR("Texture filtering not supported in direct blit\n");
2458 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2459 ERR("Texture filtering not supported in direct blit\n");
2463 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2464 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2465 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2467 glCopyTexSubImage2D(This->glDescription.target,
2468 This->glDescription.level,
2469 drect->x1, drect->y1, /* xoffset, yoffset */
2470 srect->x1, Src->currentDesc.Height - srect->y2,
2471 drect->x2 - drect->x1, drect->y2 - drect->y1);
2473 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2474 /* I have to process this row by row to swap the image,
2475 * otherwise it would be upside down, so stretching in y direction
2476 * doesn't cost extra time
2478 * However, stretching in x direction can be avoided if not necessary
2480 for(row = drect->y1; row < drect->y2; row++) {
2481 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2482 /* Well, that stuff works, but it's very slow.
2483 * find a better way instead
2487 for(col = drect->x1; col < drect->x2; col++) {
2488 glCopyTexSubImage2D(This->glDescription.target,
2489 This->glDescription.level,
2490 drect->x1 + col, row, /* xoffset, yoffset */
2491 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2495 glCopyTexSubImage2D(This->glDescription.target,
2496 This->glDescription.level,
2497 drect->x1, row, /* xoffset, yoffset */
2498 srect->x1, yoffset - (int) (row * yrel),
2499 drect->x2-drect->x1, 1);
2503 vcheckGLcall("glCopyTexSubImage2D");
2505 /* Leave the opengl state valid for blitting */
2506 glDisable(This->glDescription.target);
2507 checkGLcall("glDisable(This->glDescription.target)");
2512 /* Uses the hardware to stretch and flip the image */
2513 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2514 GLuint src, backup = 0;
2515 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2516 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2517 float left, right, top, bottom; /* Texture coordinates */
2518 UINT fbwidth = Src->currentDesc.Width;
2519 UINT fbheight = Src->currentDesc.Height;
2520 GLenum drawBuffer = GL_BACK;
2521 GLenum texture_target;
2523 TRACE("Using hwstretch blit\n");
2524 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2525 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2528 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2530 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2531 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2533 if(GL_LIMITS(aux_buffers) >= 2) {
2534 /* Got more than one aux buffer? Use the 2nd aux buffer */
2535 drawBuffer = GL_AUX1;
2536 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2537 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2538 drawBuffer = GL_AUX0;
2541 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2542 glGenTextures(1, &backup);
2543 checkGLcall("glGenTextures\n");
2544 glBindTexture(GL_TEXTURE_2D, backup);
2545 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2546 texture_target = GL_TEXTURE_2D;
2548 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2549 * we are reading from the back buffer, the backup can be used as source texture
2551 if(Src->glDescription.textureName == 0) {
2552 /* Get it a description */
2553 IWineD3DSurface_PreLoad(SrcSurface);
2555 texture_target = Src->glDescription.target;
2556 glBindTexture(texture_target, Src->glDescription.textureName);
2557 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2558 glEnable(texture_target);
2559 checkGLcall("glEnable(texture_target)");
2561 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2562 Src->Flags &= ~SFLAG_INTEXTURE;
2565 glReadBuffer(GL_BACK);
2566 checkGLcall("glReadBuffer(GL_BACK)");
2568 /* TODO: Only back up the part that will be overwritten */
2569 glCopyTexSubImage2D(texture_target, 0,
2570 0, 0 /* read offsets */,
2575 checkGLcall("glCopyTexSubImage2D");
2577 /* No issue with overriding these - the sampler is dirty due to blit usage */
2578 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2579 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2580 checkGLcall("glTexParameteri");
2581 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2582 minMipLookup[Filter][WINED3DTEXF_NONE]);
2583 checkGLcall("glTexParameteri");
2585 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2586 src = backup ? backup : Src->glDescription.textureName;
2588 glReadBuffer(GL_FRONT);
2589 checkGLcall("glReadBuffer(GL_FRONT)");
2591 glGenTextures(1, &src);
2592 checkGLcall("glGenTextures(1, &src)");
2593 glBindTexture(GL_TEXTURE_2D, src);
2594 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2596 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2597 * out for power of 2 sizes
2599 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2600 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2601 checkGLcall("glTexImage2D");
2602 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2603 0, 0 /* read offsets */,
2608 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2609 checkGLcall("glTexParameteri");
2610 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2611 checkGLcall("glTexParameteri");
2613 glReadBuffer(GL_BACK);
2614 checkGLcall("glReadBuffer(GL_BACK)");
2616 if(texture_target != GL_TEXTURE_2D) {
2617 glDisable(texture_target);
2618 glEnable(GL_TEXTURE_2D);
2619 texture_target = GL_TEXTURE_2D;
2622 checkGLcall("glEnd and previous");
2624 left = (float) srect->x1 / (float) Src->pow2Width;
2625 right = (float) srect->x2 / (float) Src->pow2Width;
2628 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2629 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2631 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2632 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2635 /* draw the source texture stretched and upside down. The correct surface is bound already */
2636 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
2637 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
2639 glDrawBuffer(drawBuffer);
2640 glReadBuffer(drawBuffer);
2644 glTexCoord2f(left, bottom);
2645 glVertex2i(0, fbheight);
2648 glTexCoord2f(left, top);
2649 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2652 glTexCoord2f(right, top);
2653 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2656 glTexCoord2f(right, bottom);
2657 glVertex2i(drect->x2 - drect->x1, fbheight);
2659 checkGLcall("glEnd and previous");
2661 if(texture_target != This->glDescription.target) {
2662 glDisable(texture_target);
2663 glEnable(This->glDescription.target);
2664 texture_target = This->glDescription.target;
2667 /* Now read the stretched and upside down image into the destination texture */
2668 glBindTexture(texture_target, This->glDescription.textureName);
2669 checkGLcall("glBindTexture");
2670 glCopyTexSubImage2D(texture_target,
2672 drect->x1, drect->y1, /* xoffset, yoffset */
2673 0, 0, /* We blitted the image to the origin */
2674 drect->x2 - drect->x1, drect->y2 - drect->y1);
2675 checkGLcall("glCopyTexSubImage2D");
2677 if(drawBuffer == GL_BACK) {
2678 /* Write the back buffer backup back */
2680 if(texture_target != GL_TEXTURE_2D) {
2681 glDisable(texture_target);
2682 glEnable(GL_TEXTURE_2D);
2683 texture_target = GL_TEXTURE_2D;
2685 glBindTexture(GL_TEXTURE_2D, backup);
2686 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
2688 if(texture_target != Src->glDescription.target) {
2689 glDisable(texture_target);
2690 glEnable(Src->glDescription.target);
2691 texture_target = Src->glDescription.target;
2693 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
2694 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2699 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2703 glTexCoord2f(0.0, 0.0);
2704 glVertex2i(0, fbheight);
2707 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2708 glVertex2i(fbwidth, Src->currentDesc.Height);
2711 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2712 glVertex2i(fbwidth, 0);
2715 /* Restore the old draw buffer */
2716 glDrawBuffer(GL_BACK);
2718 glDisable(texture_target);
2719 checkGLcall("glDisable(texture_target)");
2722 if(src != Src->glDescription.textureName && src != backup) {
2723 glDeleteTextures(1, &src);
2724 checkGLcall("glDeleteTextures(1, &src)");
2727 glDeleteTextures(1, &backup);
2728 checkGLcall("glDeleteTextures(1, &backup)");
2734 /* Not called from the VTable */
2735 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2737 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2738 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2739 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2741 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2743 /* Get the swapchain. One of the surfaces has to be a primary surface */
2744 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2745 WARN("Destination is in sysmem, rejecting gl blt\n");
2746 return WINED3DERR_INVALIDCALL;
2748 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2749 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2751 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2752 WARN("Src is in sysmem, rejecting gl blt\n");
2753 return WINED3DERR_INVALIDCALL;
2755 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2756 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2759 /* Early sort out of cases where no render target is used */
2760 if(!dstSwapchain && !srcSwapchain &&
2761 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2762 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2763 return WINED3DERR_INVALIDCALL;
2766 /* No destination color keying supported */
2767 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2768 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2769 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2770 return WINED3DERR_INVALIDCALL;
2774 rect.x1 = DestRect->left;
2775 rect.y1 = DestRect->top;
2776 rect.x2 = DestRect->right;
2777 rect.y2 = DestRect->bottom;
2781 rect.x2 = This->currentDesc.Width;
2782 rect.y2 = This->currentDesc.Height;
2785 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2786 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2787 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2788 /* Half-life does a Blt from the back buffer to the front buffer,
2789 * Full surface size, no flags... Use present instead
2791 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2794 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2798 TRACE("Looking if a Present can be done...\n");
2799 /* Source Rectangle must be full surface */
2801 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2802 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2803 TRACE("No, Source rectangle doesn't match\n");
2809 mySrcRect.right = Src->currentDesc.Width;
2810 mySrcRect.bottom = Src->currentDesc.Height;
2812 /* No stretching may occur */
2813 if(mySrcRect.right != rect.x2 - rect.x1 ||
2814 mySrcRect.bottom != rect.y2 - rect.y1) {
2815 TRACE("No, stretching is done\n");
2819 /* Destination must be full surface or match the clipping rectangle */
2820 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
2824 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
2829 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
2832 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
2833 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
2835 TRACE("No, dest rectangle doesn't match(clipper)\n");
2836 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
2837 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
2843 if(rect.x1 != 0 || rect.y1 != 0 ||
2844 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
2845 TRACE("No, dest rectangle doesn't match(surface size)\n");
2852 /* These flags are unimportant for the flag check, remove them */
2853 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
2854 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2856 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2857 * take very long, while a flip is fast.
2858 * This applies to Half-Life, which does such Blts every time it finished
2859 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2860 * menu. This is also used by all apps when they do windowed rendering
2862 * The problem is that flipping is not really the same as copying. After a
2863 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2864 * untouched. Therefore it's necessary to override the swap effect
2865 * and to set it back after the flip.
2867 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
2871 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2872 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2874 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2875 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
2877 dstSwapchain->presentParms.SwapEffect = orig_swap;
2884 TRACE("Unsupported blit between buffers on the same swapchain\n");
2885 return WINED3DERR_INVALIDCALL;
2886 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
2887 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
2888 return WINED3DERR_INVALIDCALL;
2889 } else if(dstSwapchain && srcSwapchain) {
2890 FIXME("Implement hardware blit between two different swapchains\n");
2891 return WINED3DERR_INVALIDCALL;
2892 } else if(dstSwapchain) {
2893 if(SrcSurface == myDevice->render_targets[0]) {
2894 TRACE("Blit from active render target to a swapchain\n");
2895 /* Handled with regular texture -> swapchain blit */
2897 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2898 FIXME("Implement blit from a swapchain to the active render target\n");
2899 return WINED3DERR_INVALIDCALL;
2902 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
2903 /* Blit from render target to texture */
2905 BOOL upsideDown, stretchx;
2907 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2908 TRACE("Color keying not supported by frame buffer to texture blit\n");
2909 return WINED3DERR_INVALIDCALL;
2910 /* Destination color key is checked above */
2913 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2914 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2917 if(SrcRect->top < SrcRect->bottom) {
2918 srect.y1 = SrcRect->top;
2919 srect.y2 = SrcRect->bottom;
2922 srect.y1 = SrcRect->bottom;
2923 srect.y2 = SrcRect->top;
2926 srect.x1 = SrcRect->left;
2927 srect.x2 = SrcRect->right;
2931 srect.x2 = Src->currentDesc.Width;
2932 srect.y2 = Src->currentDesc.Height;
2935 if(rect.x1 > rect.x2) {
2939 upsideDown = !upsideDown;
2942 TRACE("Reading from an offscreen target\n");
2943 upsideDown = !upsideDown;
2946 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
2952 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2953 * flip the image nor scale it.
2955 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2956 * -> If the app wants a image width an unscaled width, copy it line per line
2957 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
2958 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2959 * back buffer. This is slower than reading line per line, thus not used for flipping
2960 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2963 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
2964 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
2967 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
2968 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
2969 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
2970 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
2971 rect.y2 - rect.y1 > Src->currentDesc.Height) {
2972 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
2973 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2975 TRACE("Using hardware stretching to flip / stretch the texture\n");
2976 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2979 if(!(This->Flags & SFLAG_DONOTFREE)) {
2980 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2981 This->resource.allocatedMemory = NULL;
2982 This->resource.heapMemory = NULL;
2984 This->Flags &= ~SFLAG_INSYSMEM;
2986 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
2987 * path is never entered
2989 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
2993 /* Blit from offscreen surface to render target */
2994 float glTexCoord[4];
2995 DWORD oldCKeyFlags = Src->CKeyFlags;
2996 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
2997 RECT SourceRectangle;
2999 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3002 SourceRectangle.left = SrcRect->left;
3003 SourceRectangle.right = SrcRect->right;
3004 SourceRectangle.top = SrcRect->top;
3005 SourceRectangle.bottom = SrcRect->bottom;
3007 SourceRectangle.left = 0;
3008 SourceRectangle.right = Src->currentDesc.Width;
3009 SourceRectangle.top = 0;
3010 SourceRectangle.bottom = Src->currentDesc.Height;
3012 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3013 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3014 TRACE("Using stretch_rect_fbo\n");
3015 /* The source is always a texture, but never the currently active render target, and the texture
3016 * contents are never upside down
3018 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3019 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3023 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3024 /* Fall back to software */
3025 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3026 SourceRectangle.left, SourceRectangle.top,
3027 SourceRectangle.right, SourceRectangle.bottom);
3028 return WINED3DERR_INVALIDCALL;
3031 /* Color keying: Check if we have to do a color keyed blt,
3032 * and if not check if a color key is activated.
3034 * Just modify the color keying parameters in the surface and restore them afterwards
3035 * The surface keeps track of the color key last used to load the opengl surface.
3036 * PreLoad will catch the change to the flags and color key and reload if necessary.
3038 if(Flags & WINEDDBLT_KEYSRC) {
3039 /* Use color key from surface */
3040 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3041 /* Use color key from DDBltFx */
3042 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3043 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3045 /* Do not use color key */
3046 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3049 /* Now load the surface */
3050 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3053 /* Activate the destination context, set it up for blitting */
3054 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3057 glEnable(Src->glDescription.target);
3058 checkGLcall("glEnable(Src->glDescription.target)");
3061 TRACE("Drawing to offscreen buffer\n");
3062 glDrawBuffer(myDevice->offscreenBuffer);
3063 checkGLcall("glDrawBuffer");
3065 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3066 TRACE("Drawing to %#x buffer\n", buffer);
3067 glDrawBuffer(buffer);
3068 checkGLcall("glDrawBuffer");
3071 /* Bind the texture */
3072 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3073 checkGLcall("glBindTexture");
3075 /* Filtering for StretchRect */
3076 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3077 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3078 checkGLcall("glTexParameteri");
3079 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3080 minMipLookup[Filter][WINED3DTEXF_NONE]);
3081 checkGLcall("glTexParameteri");
3082 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3083 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3084 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3085 checkGLcall("glTexEnvi");
3087 /* This is for color keying */
3088 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3089 glEnable(GL_ALPHA_TEST);
3090 checkGLcall("glEnable GL_ALPHA_TEST");
3091 glAlphaFunc(GL_NOTEQUAL, 0.0);
3092 checkGLcall("glAlphaFunc\n");
3094 glDisable(GL_ALPHA_TEST);
3095 checkGLcall("glDisable GL_ALPHA_TEST");
3098 /* Draw a textured quad
3102 glColor3d(1.0f, 1.0f, 1.0f);
3103 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3108 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3109 glVertex3f(rect.x1, rect.y2, 0.0);
3111 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3116 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3121 checkGLcall("glEnd");
3123 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3124 glDisable(GL_ALPHA_TEST);
3125 checkGLcall("glDisable(GL_ALPHA_TEST)");
3128 /* Flush in case the drawable is used by multiple GL contexts */
3129 if(dstSwapchain && (dstSwapchain->num_contexts >= 2))
3132 glBindTexture(Src->glDescription.target, 0);
3133 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3134 /* Leave the opengl state valid for blitting */
3135 glDisable(Src->glDescription.target);
3136 checkGLcall("glDisable(Src->glDescription.target)");
3138 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3139 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3141 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3142 glDrawBuffer(GL_BACK);
3143 checkGLcall("glDrawBuffer");
3145 /* Restore the color key parameters */
3146 Src->CKeyFlags = oldCKeyFlags;
3147 This->SrcBltCKey = oldBltCKey;
3151 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3152 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3155 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3156 /* TODO: This should be moved to ModifyLocation() */
3157 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3158 This->Flags |= SFLAG_INTEXTURE;
3163 /* Source-Less Blit to render target */
3164 if (Flags & WINEDDBLT_COLORFILL) {
3165 /* This is easy to handle for the D3D Device... */
3168 TRACE("Colorfill\n");
3170 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3171 must be true if we are here */
3172 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3173 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3174 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3175 TRACE("Surface is higher back buffer, falling back to software\n");
3176 return WINED3DERR_INVALIDCALL;
3179 /* The color as given in the Blt function is in the format of the frame-buffer...
3180 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3182 if (This->resource.format == WINED3DFMT_P8) {
3183 if (This->palette) {
3184 color = ((0xFF000000) |
3185 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3186 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3187 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3192 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3193 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3196 color = ((0xFF000000) |
3197 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3198 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3199 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3202 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3203 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3204 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3206 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3207 color = DDBltFx->u5.dwFillColor;
3210 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3211 return WINED3DERR_INVALIDCALL;
3214 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3215 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3216 1, /* Number of rectangles */
3217 &rect, WINED3DCLEAR_TARGET, color,
3224 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3225 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3226 return WINED3DERR_INVALIDCALL;
3229 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3231 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3234 if (Flags & WINEDDBLT_DEPTHFILL) {
3235 switch(This->resource.format) {
3236 case WINED3DFMT_D16:
3237 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3239 case WINED3DFMT_D15S1:
3240 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3242 case WINED3DFMT_D24S8:
3243 case WINED3DFMT_D24X8:
3244 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3246 case WINED3DFMT_D32:
3247 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3251 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3254 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3255 DestRect == NULL ? 0 : 1,
3256 (WINED3DRECT *) DestRect,
3257 WINED3DCLEAR_ZBUFFER,
3263 FIXME("(%p): Unsupp depthstencil blit\n", This);
3264 return WINED3DERR_INVALIDCALL;
3267 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3268 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3269 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3270 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3271 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3272 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3274 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3275 * except depth blits, which seem to work
3277 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3278 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3279 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3280 return WINED3DERR_INVALIDCALL;
3281 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3282 TRACE("Z Blit override handled the blit\n");
3287 /* Special cases for RenderTargets */
3288 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3289 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3290 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3293 /* For the rest call the X11 surface implementation.
3294 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3295 * other Blts are rather rare
3297 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3300 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3301 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3302 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3303 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3304 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3306 if(myDevice->inScene &&
3307 (iface == myDevice->stencilBufferTarget ||
3308 (Source && Source == myDevice->stencilBufferTarget))) {
3309 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3310 return WINED3DERR_INVALIDCALL;
3313 /* Special cases for RenderTargets */
3314 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3315 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3317 RECT SrcRect, DstRect;
3321 SrcRect.left = rsrc->left;
3322 SrcRect.top= rsrc->top;
3323 SrcRect.bottom = rsrc->bottom;
3324 SrcRect.right = rsrc->right;
3328 SrcRect.right = srcImpl->currentDesc.Width;
3329 SrcRect.bottom = srcImpl->currentDesc.Height;
3332 DstRect.left = dstx;
3334 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3335 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3337 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3338 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3339 Flags |= WINEDDBLT_KEYSRC;
3340 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3341 Flags |= WINEDDBLT_KEYDEST;
3342 if(trans & WINEDDBLTFAST_WAIT)
3343 Flags |= WINEDDBLT_WAIT;
3344 if(trans & WINEDDBLTFAST_DONOTWAIT)
3345 Flags |= WINEDDBLT_DONOTWAIT;
3347 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3351 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3354 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3355 /** Check against the maximum texture sizes supported by the video card **/
3356 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3357 unsigned int pow2Width, pow2Height;
3358 const GlPixelFormatDesc *glDesc;
3360 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3361 /* Setup some glformat defaults */
3362 This->glDescription.glFormat = glDesc->glFormat;
3363 This->glDescription.glFormatInternal = glDesc->glInternal;
3364 This->glDescription.glType = glDesc->glType;
3366 This->glDescription.textureName = 0;
3367 This->glDescription.target = GL_TEXTURE_2D;
3369 /* Non-power2 support */
3370 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3371 pow2Width = This->currentDesc.Width;
3372 pow2Height = This->currentDesc.Height;
3374 /* Find the nearest pow2 match */
3375 pow2Width = pow2Height = 1;
3376 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3377 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3379 This->pow2Width = pow2Width;
3380 This->pow2Height = pow2Height;
3382 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3383 WINED3DFORMAT Format = This->resource.format;
3384 /** TODO: add support for non power two compressed textures **/
3385 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3386 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3387 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3388 This, This->currentDesc.Width, This->currentDesc.Height);
3389 return WINED3DERR_NOTAVAILABLE;
3393 if(pow2Width != This->currentDesc.Width ||
3394 pow2Height != This->currentDesc.Height) {
3395 This->Flags |= SFLAG_NONPOW2;
3398 TRACE("%p\n", This);
3399 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3400 /* one of three options
3401 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)
3402 2: Set the texture to the maximum size (bad idea)
3403 3: WARN and return WINED3DERR_NOTAVAILABLE;
3404 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.
3406 WARN("(%p) Creating an oversized surface\n", This);
3407 This->Flags |= SFLAG_OVERSIZE;
3409 /* This will be initialized on the first blt */
3410 This->glRect.left = 0;
3411 This->glRect.top = 0;
3412 This->glRect.right = 0;
3413 This->glRect.bottom = 0;
3415 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one */
3416 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
3417 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3418 This->pow2Width = This->currentDesc.Width;
3419 This->pow2Height = This->currentDesc.Height;
3420 This->Flags &= ~SFLAG_NONPOW2;
3423 /* No oversize, gl rect is the full texture size */
3424 This->Flags &= ~SFLAG_OVERSIZE;
3425 This->glRect.left = 0;
3426 This->glRect.top = 0;
3427 This->glRect.right = This->pow2Width;
3428 This->glRect.bottom = This->pow2Height;
3431 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3432 switch(wined3d_settings.offscreen_rendering_mode) {
3433 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3434 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3435 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3439 This->Flags |= SFLAG_INSYSMEM;
3444 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3445 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3446 IWineD3DBaseTexture *texture;
3448 TRACE("(%p)->(%s, %s)\n", iface,
3449 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3450 persistent ? "TRUE" : "FALSE");
3452 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3453 IWineD3DSwapChain *swapchain = NULL;
3455 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3456 TRACE("Surface %p is an onscreen surface\n", iface);
3458 IWineD3DSwapChain_Release(swapchain);
3460 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3461 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3466 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
3467 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3468 TRACE("Passing to container\n");
3469 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3470 IWineD3DBaseTexture_Release(texture);
3473 This->Flags &= ~SFLAG_LOCATIONS;
3474 This->Flags |= flag;
3476 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
3477 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3478 TRACE("Passing to container\n");
3479 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3480 IWineD3DBaseTexture_Release(texture);
3483 This->Flags &= ~flag;
3491 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
3492 struct coords coords[4];
3494 IWineD3DSwapChain *swapchain = NULL;
3495 IWineD3DBaseTexture *texture = NULL;
3497 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3504 rect.right = This->currentDesc.Width;
3505 rect.bottom = This->currentDesc.Height;
3508 ActivateContext(device, device->render_targets[0], CTXUSAGE_BLIT);
3511 if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
3512 glEnable(GL_TEXTURE_RECTANGLE_ARB);
3513 checkGLcall("glEnable(GL_TEXTURE_RECTANGLE_ARB)");
3514 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName);
3515 checkGLcall("GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName)");
3516 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3517 checkGLcall("glTexParameteri");
3518 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3519 checkGLcall("glTexParameteri");
3521 coords[0].x = rect.left;
3524 coords[1].x = rect.left;
3527 coords[2].x = rect.right;
3530 coords[3].x = rect.right;
3533 coords[0].y = rect.top;
3534 coords[1].y = rect.bottom;
3535 coords[2].y = rect.bottom;
3536 coords[3].y = rect.top;
3537 } else if(This->glDescription.target == GL_TEXTURE_2D) {
3538 glEnable(GL_TEXTURE_2D);
3539 checkGLcall("glEnable(GL_TEXTURE_2D)");
3540 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
3541 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
3542 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3543 checkGLcall("glTexParameteri");
3544 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3545 checkGLcall("glTexParameteri");
3547 coords[0].x = rect.left / This->pow2Width;
3550 coords[1].x = rect.left / This->pow2Width;
3553 coords[2].x = rect.right / This->pow2Width;
3556 coords[3].x = rect.right / This->pow2Width;
3559 coords[0].y = rect.top / This->pow2Height;
3560 coords[1].y = rect.bottom / This->pow2Height;
3561 coords[2].y = rect.bottom / This->pow2Height;
3562 coords[3].y = rect.top / This->pow2Height;
3564 /* Must be a cube map */
3565 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
3566 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
3567 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
3568 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
3569 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3570 checkGLcall("glTexParameteri");
3571 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3572 checkGLcall("glTexParameteri");
3574 switch(This->glDescription.target) {
3575 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3576 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
3577 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3578 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3579 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
3582 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3583 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3584 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
3585 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
3586 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3589 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3590 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
3591 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3592 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3593 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
3596 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3597 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3598 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3599 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3600 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3603 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3604 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3605 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3606 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
3607 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
3610 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
3611 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
3612 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
3613 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3614 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3617 ERR("Unexpected texture target\n");
3624 glTexCoord3iv((GLint *) &coords[0]);
3625 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
3627 glTexCoord3iv((GLint *) &coords[1]);
3628 glVertex2i(0, device->render_offscreen ? rect.top : rect.bottom);
3630 glTexCoord3iv((GLint *) &coords[2]);
3631 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
3633 glTexCoord3iv((GLint *) &coords[3]);
3634 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
3636 checkGLcall("glEnd");
3638 if(This->glDescription.target != GL_TEXTURE_2D) {
3639 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3640 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3642 glDisable(GL_TEXTURE_2D);
3643 checkGLcall("glDisable(GL_TEXTURE_2D)");
3646 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain);
3647 if(hr == WINED3D_OK && swapchain) {
3648 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
3649 if(((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
3652 IWineD3DSwapChain_Release(swapchain);
3654 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
3655 * reset properly next draw
3657 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture);
3658 if(hr == WINED3D_OK && texture) {
3659 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
3660 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
3661 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
3662 IWineD3DBaseTexture_Release(texture);
3668 /*****************************************************************************
3669 * IWineD3DSurface::LoadLocation
3671 * Copies the current surface data from wherever it is to the requested
3672 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
3673 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
3674 * multiple locations, the gl texture is preferred over the drawable, which is
3675 * preferred over system memory. The PBO counts as system memory. If rect is
3676 * not NULL, only the specified rectangle is copied (only supported for
3677 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
3678 * location is marked up to date after the copy.
3681 * flag: Surface location flag to be updated
3682 * rect: rectangle to be copied
3685 * WINED3D_OK on success
3686 * WINED3DERR_DEVICELOST on an internal error
3688 *****************************************************************************/
3689 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
3690 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3691 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3692 IWineD3DSwapChain *swapchain = NULL;
3693 GLenum format, internal, type;
3694 CONVERT_TYPES convert;
3696 int width, pitch, outpitch;
3699 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3700 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3701 TRACE("Surface %p is an onscreen surface\n", iface);
3703 IWineD3DSwapChain_Release(swapchain);
3705 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
3706 * Prefer SFLAG_INTEXTURE. */
3707 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
3711 TRACE("(%p)->(%s, %p)\n", iface,
3712 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3715 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
3718 if(This->Flags & flag) {
3719 TRACE("Location already up to date\n");
3723 if(!(This->Flags & SFLAG_LOCATIONS)) {
3724 ERR("Surface does not have any up to date location\n");
3725 This->Flags |= SFLAG_LOST;
3726 return WINED3DERR_DEVICELOST;
3729 if(flag == SFLAG_INSYSMEM) {
3730 surface_prepare_system_memory(This);
3732 /* Download the surface to system memory */
3733 if(This->Flags & SFLAG_INTEXTURE) {
3734 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
3735 surface_bind_and_dirtify(This);
3737 surface_download_data(This);
3739 read_from_framebuffer(This, rect,
3740 This->resource.allocatedMemory,
3741 IWineD3DSurface_GetPitch(iface));
3743 } else if(flag == SFLAG_INDRAWABLE) {
3744 if(This->Flags & SFLAG_INTEXTURE) {
3745 surface_blt_to_drawable(This, rect);
3747 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
3749 /* The width is in 'length' not in bytes */
3750 width = This->currentDesc.Width;
3751 pitch = IWineD3DSurface_GetPitch(iface);
3753 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
3754 int height = This->currentDesc.Height;
3756 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
3757 outpitch = width * bpp;
3758 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
3760 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
3762 ERR("Out of memory %d, %d!\n", outpitch, height);
3763 return WINED3DERR_OUTOFVIDEOMEMORY;
3765 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
3767 This->Flags |= SFLAG_CONVERTED;
3769 This->Flags &= ~SFLAG_CONVERTED;
3770 mem = This->resource.allocatedMemory;
3773 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
3775 /* Don't delete PBO memory */
3776 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
3777 HeapFree(GetProcessHeap(), 0, mem);
3779 } else /* if(flag == SFLAG_INTEXTURE) */ {
3780 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
3782 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
3783 surface_bind_and_dirtify(This);
3785 if (This->Flags & SFLAG_INDRAWABLE) {
3789 glGetIntegerv(GL_READ_BUFFER, &prevRead);
3790 vcheckGLcall("glGetIntegerv");
3791 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
3792 vcheckGLcall("glReadBuffer");
3794 if(!(This->Flags & SFLAG_ALLOCATED)) {
3795 surface_allocate_surface(This, internal, This->pow2Width,
3796 This->pow2Height, format, type);
3799 clear_unused_channels(This);
3801 glCopyTexSubImage2D(This->glDescription.target,
3802 This->glDescription.level,
3804 This->currentDesc.Width,
3805 This->currentDesc.Height);
3806 checkGLcall("glCopyTexSubImage2D");
3808 glReadBuffer(prevRead);
3809 vcheckGLcall("glReadBuffer");
3813 TRACE("Updated target %d\n", This->glDescription.target);
3815 /* The only place where LoadTexture() might get called when isInDraw=1
3816 * is ActivateContext where lastActiveRenderTarget is preloaded.
3818 if(iface == device->lastActiveRenderTarget && device->isInDraw)
3819 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
3821 /* Otherwise: System memory copy must be most up to date */
3823 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
3824 This->Flags |= SFLAG_GLCKEY;
3825 This->glCKey = This->SrcBltCKey;
3827 else This->Flags &= ~SFLAG_GLCKEY;
3829 /* The width is in 'length' not in bytes */
3830 width = This->currentDesc.Width;
3831 pitch = IWineD3DSurface_GetPitch(iface);
3833 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
3834 int height = This->currentDesc.Height;
3836 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
3837 outpitch = width * bpp;
3838 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
3840 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
3842 ERR("Out of memory %d, %d!\n", outpitch, height);
3843 return WINED3DERR_OUTOFVIDEOMEMORY;
3845 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
3847 This->Flags |= SFLAG_CONVERTED;
3848 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
3849 d3dfmt_p8_upload_palette(iface, convert);
3850 This->Flags &= ~SFLAG_CONVERTED;
3851 mem = This->resource.allocatedMemory;
3853 This->Flags &= ~SFLAG_CONVERTED;
3854 mem = This->resource.allocatedMemory;
3857 /* Make sure the correct pitch is used */
3858 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
3860 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
3861 TRACE("non power of two support\n");
3862 if(!(This->Flags & SFLAG_ALLOCATED)) {
3863 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
3865 if (mem || (This->Flags & SFLAG_PBO)) {
3866 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
3869 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
3870 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
3872 if(!(This->Flags & SFLAG_ALLOCATED)) {
3873 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
3875 if (mem || (This->Flags & SFLAG_PBO)) {
3876 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
3880 /* Restore the default pitch */
3881 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
3883 /* Don't delete PBO memory */
3884 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
3885 HeapFree(GetProcessHeap(), 0, mem);
3890 This->Flags |= flag;
3893 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
3894 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
3895 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3896 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3902 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
3903 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3904 IWineD3DSwapChain *swapchain = NULL;
3906 /* Update the drawable size method */
3908 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
3911 This->get_drawable_size = get_drawable_size_swapchain;
3912 IWineD3DSwapChain_Release(swapchain);
3913 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3914 switch(wined3d_settings.offscreen_rendering_mode) {
3915 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3916 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3917 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3921 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
3924 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3927 IWineD3DBaseSurfaceImpl_QueryInterface,
3928 IWineD3DBaseSurfaceImpl_AddRef,
3929 IWineD3DSurfaceImpl_Release,
3930 /* IWineD3DResource */
3931 IWineD3DBaseSurfaceImpl_GetParent,
3932 IWineD3DBaseSurfaceImpl_GetDevice,
3933 IWineD3DBaseSurfaceImpl_SetPrivateData,
3934 IWineD3DBaseSurfaceImpl_GetPrivateData,
3935 IWineD3DBaseSurfaceImpl_FreePrivateData,
3936 IWineD3DBaseSurfaceImpl_SetPriority,
3937 IWineD3DBaseSurfaceImpl_GetPriority,
3938 IWineD3DSurfaceImpl_PreLoad,
3939 IWineD3DBaseSurfaceImpl_GetType,
3940 /* IWineD3DSurface */
3941 IWineD3DBaseSurfaceImpl_GetContainer,
3942 IWineD3DBaseSurfaceImpl_GetDesc,
3943 IWineD3DSurfaceImpl_LockRect,
3944 IWineD3DSurfaceImpl_UnlockRect,
3945 IWineD3DSurfaceImpl_GetDC,
3946 IWineD3DSurfaceImpl_ReleaseDC,
3947 IWineD3DSurfaceImpl_Flip,
3948 IWineD3DSurfaceImpl_Blt,
3949 IWineD3DBaseSurfaceImpl_GetBltStatus,
3950 IWineD3DBaseSurfaceImpl_GetFlipStatus,
3951 IWineD3DBaseSurfaceImpl_IsLost,
3952 IWineD3DBaseSurfaceImpl_Restore,
3953 IWineD3DSurfaceImpl_BltFast,
3954 IWineD3DBaseSurfaceImpl_GetPalette,
3955 IWineD3DBaseSurfaceImpl_SetPalette,
3956 IWineD3DBaseSurfaceImpl_RealizePalette,
3957 IWineD3DBaseSurfaceImpl_SetColorKey,
3958 IWineD3DBaseSurfaceImpl_GetPitch,
3959 IWineD3DSurfaceImpl_SetMem,
3960 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
3961 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
3962 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
3963 IWineD3DBaseSurfaceImpl_UpdateOverlay,
3964 IWineD3DBaseSurfaceImpl_SetClipper,
3965 IWineD3DBaseSurfaceImpl_GetClipper,
3967 IWineD3DSurfaceImpl_AddDirtyRect,
3968 IWineD3DSurfaceImpl_LoadTexture,
3969 IWineD3DSurfaceImpl_BindTexture,
3970 IWineD3DSurfaceImpl_SaveSnapshot,
3971 IWineD3DSurfaceImpl_SetContainer,
3972 IWineD3DSurfaceImpl_SetGlTextureDesc,
3973 IWineD3DSurfaceImpl_GetGlDesc,
3974 IWineD3DSurfaceImpl_GetData,
3975 IWineD3DSurfaceImpl_SetFormat,
3976 IWineD3DSurfaceImpl_PrivateSetup,
3977 IWineD3DSurfaceImpl_ModifyLocation,
3978 IWineD3DSurfaceImpl_LoadLocation