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 Stefan Dösinger for CodeWeavers
11 * Copyright 2007 Henri Verbeet
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "wine/port.h"
30 #include "wined3d_private.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface
);
33 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
35 HRESULT
d3dfmt_convert_surface(BYTE
*src
, BYTE
*dst
, UINT pitch
, UINT width
, UINT height
, UINT outpitch
, CONVERT_TYPES convert
, IWineD3DSurfaceImpl
*surf
);
37 static void surface_download_data(IWineD3DSurfaceImpl
*This
) {
38 if (!This
->resource
.allocatedMemory
) This
->resource
.allocatedMemory
= HeapAlloc(GetProcessHeap(), 0, This
->resource
.size
+ 4);
39 if (This
->resource
.format
== WINED3DFMT_DXT1
||
40 This
->resource
.format
== WINED3DFMT_DXT2
|| This
->resource
.format
== WINED3DFMT_DXT3
||
41 This
->resource
.format
== WINED3DFMT_DXT4
|| This
->resource
.format
== WINED3DFMT_DXT5
) {
42 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC
)) { /* We can assume this as the texture would not have been created otherwise */
43 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This
);
45 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This
, This
->glDescription
.level
,
46 This
->glDescription
.glFormat
, This
->glDescription
.glType
, This
->resource
.allocatedMemory
);
48 GL_EXTCALL(glGetCompressedTexImageARB(This
->glDescription
.target
, This
->glDescription
.level
, This
->resource
.allocatedMemory
));
49 checkGLcall("glGetCompressedTexImageARB()");
56 if(This
->Flags
& SFLAG_CONVERTED
) {
57 FIXME("Read back converted textures unsupported\n");
61 if (This
->Flags
& SFLAG_NONPOW2
) {
62 unsigned char alignment
= This
->resource
.wineD3DDevice
->surface_alignment
;
63 src_pitch
= This
->bytesPerPixel
* This
->pow2Width
;
64 dst_pitch
= IWineD3DSurface_GetPitch((IWineD3DSurface
*) This
);
65 src_pitch
= (src_pitch
+ alignment
- 1) & ~(alignment
- 1);
66 mem
= HeapAlloc(GetProcessHeap(), 0, src_pitch
* This
->pow2Height
);
68 mem
= This
->resource
.allocatedMemory
;
71 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This
, This
->glDescription
.level
,
72 This
->glDescription
.glFormat
, This
->glDescription
.glType
, mem
);
74 glGetTexImage(This
->glDescription
.target
, This
->glDescription
.level
, This
->glDescription
.glFormat
,
75 This
->glDescription
.glType
, mem
);
76 checkGLcall("glGetTexImage()");
78 if (This
->Flags
& SFLAG_NONPOW2
) {
79 LPBYTE src_data
, dst_data
;
82 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
83 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
84 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
88 * instead of boxing the texture :
89 * |<-texture width ->| -->pow2width| /\
90 * |111111111111111111| | |
91 * |222 Texture 222222| boxed empty | texture height
92 * |3333 Data 33333333| | |
93 * |444444444444444444| | \/
94 * ----------------------------------- |
95 * | boxed empty | boxed empty | pow2height
97 * -----------------------------------
100 * we're repacking the data to the expected texture width
102 * |<-texture width ->| -->pow2width| /\
103 * |111111111111111111222222222222222| |
104 * |222333333333333333333444444444444| texture height
108 * | empty | pow2height
110 * -----------------------------------
114 * |<-texture width ->| /\
115 * |111111111111111111|
116 * |222222222222222222|texture height
117 * |333333333333333333|
118 * |444444444444444444| \/
119 * --------------------
121 * this also means that any references to allocatedMemory should work with the data as if were a
122 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
124 * internally the texture is still stored in a boxed format so any references to textureName will
125 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
127 * Performance should not be an issue, because applications normally do not lock the surfaces when
128 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
129 * and doesn't have to be re-read.
132 dst_data
= This
->resource
.allocatedMemory
;
133 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This
, src_pitch
, dst_pitch
);
134 for (y
= 1 ; y
< This
->currentDesc
.Height
; y
++) {
135 /* skip the first row */
136 src_data
+= src_pitch
;
137 dst_data
+= dst_pitch
;
138 memcpy(dst_data
, src_data
, dst_pitch
);
141 HeapFree(GetProcessHeap(), 0, mem
);
144 /* Surface has now been downloaded */
145 This
->Flags
|= SFLAG_INSYSMEM
;
148 static void surface_upload_data(IWineD3DSurfaceImpl
*This
, GLenum internal
, GLsizei width
, GLsizei height
, GLenum format
, GLenum type
, const GLvoid
*data
) {
149 if (This
->resource
.format
== WINED3DFMT_DXT1
||
150 This
->resource
.format
== WINED3DFMT_DXT2
|| This
->resource
.format
== WINED3DFMT_DXT3
||
151 This
->resource
.format
== WINED3DFMT_DXT4
|| This
->resource
.format
== WINED3DFMT_DXT5
) {
152 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC
)) {
153 FIXME("Using DXT1/3/5 without advertized support\n");
155 if(GL_SUPPORT(APPLE_CLIENT_STORAGE
)) {
156 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
157 This
->Flags
|= SFLAG_CLIENT
;
160 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This
, width
, height
, data
);
162 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
163 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
164 * function uses glCompressedTexImage2D instead of the SubImage call
166 GL_EXTCALL(glCompressedTexImage2DARB(This
->glDescription
.target
, This
->glDescription
.level
, internal
,
167 width
, height
, 0 /* border */, This
->resource
.size
, data
));
168 checkGLcall("glCompressedTexSubImage2D");
172 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This
, width
, height
, data
);
174 glTexSubImage2D(This
->glDescription
.target
, This
->glDescription
.level
, 0, 0, width
, height
, format
, type
, data
);
175 checkGLcall("glTexSubImage2D");
180 static void surface_allocate_surface(IWineD3DSurfaceImpl
*This
, GLenum internal
, GLsizei width
, GLsizei height
, GLenum format
, GLenum type
) {
181 BOOL enable_client_storage
= FALSE
;
183 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
,
184 This
->glDescription
.target
, This
->glDescription
.level
, debug_d3dformat(This
->resource
.format
), internal
, width
, height
, format
, type
);
186 if (This
->resource
.format
== WINED3DFMT_DXT1
||
187 This
->resource
.format
== WINED3DFMT_DXT2
|| This
->resource
.format
== WINED3DFMT_DXT3
||
188 This
->resource
.format
== WINED3DFMT_DXT4
|| This
->resource
.format
== WINED3DFMT_DXT5
) {
189 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
190 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
196 if(GL_SUPPORT(APPLE_CLIENT_STORAGE
)) {
197 if(This
->Flags
& (SFLAG_NONPOW2
| SFLAG_DIBSECTION
| SFLAG_OVERSIZE
| SFLAG_CONVERTED
) || This
->resource
.allocatedMemory
== NULL
) {
198 /* In some cases we want to disable client storage.
199 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
200 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
201 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
202 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
203 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
205 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE
, GL_FALSE
);
206 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
207 This
->Flags
&= SFLAG_CLIENT
;
208 enable_client_storage
= TRUE
;
210 This
->Flags
|= SFLAG_CLIENT
;
211 /* Below point opengl to our allocated texture memory */
214 glTexImage2D(This
->glDescription
.target
, This
->glDescription
.level
, internal
, width
, height
, 0, format
, type
,
215 This
->Flags
& SFLAG_CLIENT
? This
->resource
.allocatedMemory
: NULL
);
216 checkGLcall("glTexImage2D");
218 if(enable_client_storage
) {
219 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE
, GL_TRUE
);
220 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
224 This
->Flags
|= SFLAG_ALLOCATED
;
227 /* In D3D the depth stencil dimensions have to be greater than or equal to the
228 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
229 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
230 void surface_set_compatible_renderbuffer(IWineD3DSurface
*iface
, unsigned int width
, unsigned int height
) {
231 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
232 renderbuffer_entry_t
*entry
;
233 GLuint renderbuffer
= 0;
234 unsigned int src_width
, src_height
;
236 src_width
= This
->pow2Width
;
237 src_height
= This
->pow2Height
;
239 /* A depth stencil smaller than the render target is not valid */
240 if (width
> src_width
|| height
> src_height
) return;
242 /* Remove any renderbuffer set if the sizes match */
243 if (width
== src_width
&& height
== src_height
) {
244 This
->current_renderbuffer
= NULL
;
248 /* Look if we've already got a renderbuffer of the correct dimensions */
249 LIST_FOR_EACH_ENTRY(entry
, &This
->renderbuffers
, renderbuffer_entry_t
, entry
) {
250 if (entry
->width
== width
&& entry
->height
== height
) {
251 renderbuffer
= entry
->id
;
252 This
->current_renderbuffer
= entry
;
258 const GlPixelFormatDesc
*glDesc
;
259 getFormatDescEntry(This
->resource
.format
, &GLINFO_LOCATION
, &glDesc
);
261 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer
));
262 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT
, renderbuffer
));
263 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT
, glDesc
->glFormat
, width
, height
));
265 entry
= HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t
));
266 entry
->width
= width
;
267 entry
->height
= height
;
268 entry
->id
= renderbuffer
;
269 list_add_head(&This
->renderbuffers
, &entry
->entry
);
271 This
->current_renderbuffer
= entry
;
274 checkGLcall("set_compatible_renderbuffer");
277 GLenum
surface_get_gl_buffer(IWineD3DSurface
*iface
, IWineD3DSwapChain
*swapchain
) {
278 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
279 IWineD3DSwapChainImpl
*swapchain_impl
= (IWineD3DSwapChainImpl
*)swapchain
;
281 TRACE("(%p) : swapchain %p\n", This
, swapchain
);
283 if (swapchain_impl
->backBuffer
&& swapchain_impl
->backBuffer
[0] == iface
) {
284 TRACE("Returning GL_BACK\n");
286 } else if (swapchain_impl
->frontBuffer
== iface
) {
287 TRACE("Returning GL_FRONT\n");
291 FIXME("Higher back buffer, returning GL_BACK\n");
295 /* *******************************************
296 IWineD3DSurface IUnknown parts follow
297 ******************************************* */
298 HRESULT WINAPI
IWineD3DSurfaceImpl_QueryInterface(IWineD3DSurface
*iface
, REFIID riid
, LPVOID
*ppobj
)
300 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
301 /* Warn ,but be nice about things */
302 TRACE("(%p)->(%s,%p)\n", This
,debugstr_guid(riid
),ppobj
);
304 if (IsEqualGUID(riid
, &IID_IUnknown
)
305 || IsEqualGUID(riid
, &IID_IWineD3DBase
)
306 || IsEqualGUID(riid
, &IID_IWineD3DResource
)
307 || IsEqualGUID(riid
, &IID_IWineD3DSurface
)) {
308 IUnknown_AddRef((IUnknown
*)iface
);
313 return E_NOINTERFACE
;
316 ULONG WINAPI
IWineD3DSurfaceImpl_AddRef(IWineD3DSurface
*iface
) {
317 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
318 ULONG ref
= InterlockedIncrement(&This
->resource
.ref
);
319 TRACE("(%p) : AddRef increasing from %d\n", This
,ref
- 1);
323 ULONG WINAPI
IWineD3DSurfaceImpl_Release(IWineD3DSurface
*iface
) {
324 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
325 ULONG ref
= InterlockedDecrement(&This
->resource
.ref
);
326 TRACE("(%p) : Releasing from %d\n", This
, ref
+ 1);
328 IWineD3DDeviceImpl
*device
= (IWineD3DDeviceImpl
*) This
->resource
.wineD3DDevice
;
329 renderbuffer_entry_t
*entry
, *entry2
;
330 TRACE("(%p) : cleaning up\n", This
);
332 if(iface
== device
->lastActiveRenderTarget
) {
333 IWineD3DSwapChainImpl
*swapchain
= device
->swapchains
? (IWineD3DSwapChainImpl
*) device
->swapchains
[0] : NULL
;
335 TRACE("Last active render target destroyed\n");
336 /* Find a replacement surface for the currently active back buffer. The context manager does not do NULL
337 * checks, so switch to a valid target as long as the currently set surface is still valid. Use the
338 * surface of the implicit swpchain. If that is the same as the destroyed surface the device is destroyed
339 * and the lastActiveRenderTarget member shouldn't matter
342 if(swapchain
->backBuffer
&& swapchain
->backBuffer
[0] != iface
) {
343 TRACE("Activating primary back buffer\n");
344 ActivateContext(device
, swapchain
->backBuffer
[0], CTXUSAGE_RESOURCELOAD
);
345 } else if(!swapchain
->backBuffer
&& swapchain
->frontBuffer
!= iface
) {
346 /* Single buffering environment */
347 TRACE("Activating primary front buffer\n");
348 ActivateContext(device
, swapchain
->frontBuffer
, CTXUSAGE_RESOURCELOAD
);
350 TRACE("Device is being destroyed, setting lastActiveRenderTarget = 0xdeadbabe\n");
351 /* Implicit render target destroyed, that means the device is being destroyed
352 * whatever we set here, it shouldn't matter
354 device
->lastActiveRenderTarget
= (IWineD3DSurface
*) 0xdeadbabe;
357 /* May happen during ddraw uninitialization */
358 TRACE("Render target set, but swapchain does not exist!\n");
359 device
->lastActiveRenderTarget
= (IWineD3DSurface
*) 0xdeadcafe;
363 if (This
->glDescription
.textureName
!= 0) { /* release the openGL texture.. */
365 /* Need a context to destroy the texture. Use the currently active render target, but only if
366 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
367 * When destroying the primary rt, Uninit3D will activate a context before doing anything
369 if(device
->render_targets
[0]) {
370 ActivateContext(device
, device
->lastActiveRenderTarget
, CTXUSAGE_RESOURCELOAD
);
373 TRACE("Deleting texture %d\n", This
->glDescription
.textureName
);
375 glDeleteTextures(1, &This
->glDescription
.textureName
);
379 if(This
->Flags
& SFLAG_DIBSECTION
) {
381 SelectObject(This
->hDC
, This
->dib
.holdbitmap
);
383 /* Release the DIB section */
384 DeleteObject(This
->dib
.DIBsection
);
385 This
->dib
.bitmap_data
= NULL
;
386 This
->resource
.allocatedMemory
= NULL
;
388 if(This
->Flags
& SFLAG_USERPTR
) IWineD3DSurface_SetMem(iface
, NULL
);
390 HeapFree(GetProcessHeap(), 0, This
->palette9
);
392 IWineD3DResourceImpl_CleanUp((IWineD3DResource
*)iface
);
393 if(iface
== device
->ddraw_primary
)
394 device
->ddraw_primary
= NULL
;
396 LIST_FOR_EACH_ENTRY_SAFE(entry
, entry2
, &This
->renderbuffers
, renderbuffer_entry_t
, entry
) {
397 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry
->id
));
398 HeapFree(GetProcessHeap(), 0, entry
);
401 TRACE("(%p) Released\n", This
);
402 HeapFree(GetProcessHeap(), 0, This
);
408 /* ****************************************************
409 IWineD3DSurface IWineD3DResource parts follow
410 **************************************************** */
411 HRESULT WINAPI
IWineD3DSurfaceImpl_GetDevice(IWineD3DSurface
*iface
, IWineD3DDevice
** ppDevice
) {
412 return IWineD3DResourceImpl_GetDevice((IWineD3DResource
*)iface
, ppDevice
);
415 HRESULT WINAPI
IWineD3DSurfaceImpl_SetPrivateData(IWineD3DSurface
*iface
, REFGUID refguid
, CONST
void* pData
, DWORD SizeOfData
, DWORD Flags
) {
416 return IWineD3DResourceImpl_SetPrivateData((IWineD3DResource
*)iface
, refguid
, pData
, SizeOfData
, Flags
);
419 HRESULT WINAPI
IWineD3DSurfaceImpl_GetPrivateData(IWineD3DSurface
*iface
, REFGUID refguid
, void* pData
, DWORD
* pSizeOfData
) {
420 return IWineD3DResourceImpl_GetPrivateData((IWineD3DResource
*)iface
, refguid
, pData
, pSizeOfData
);
423 HRESULT WINAPI
IWineD3DSurfaceImpl_FreePrivateData(IWineD3DSurface
*iface
, REFGUID refguid
) {
424 return IWineD3DResourceImpl_FreePrivateData((IWineD3DResource
*)iface
, refguid
);
427 DWORD WINAPI
IWineD3DSurfaceImpl_SetPriority(IWineD3DSurface
*iface
, DWORD PriorityNew
) {
428 return IWineD3DResourceImpl_SetPriority((IWineD3DResource
*)iface
, PriorityNew
);
431 DWORD WINAPI
IWineD3DSurfaceImpl_GetPriority(IWineD3DSurface
*iface
) {
432 return IWineD3DResourceImpl_GetPriority((IWineD3DResource
*)iface
);
435 void WINAPI
IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface
*iface
) {
436 /* TODO: check for locks */
437 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
438 IWineD3DBaseTexture
*baseTexture
= NULL
;
439 IWineD3DDeviceImpl
*device
= This
->resource
.wineD3DDevice
;
441 TRACE("(%p)Checking to see if the container is a base texture\n", This
);
442 if (IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DBaseTexture
, (void **)&baseTexture
) == WINED3D_OK
) {
443 TRACE("Passing to container\n");
444 IWineD3DBaseTexture_PreLoad(baseTexture
);
445 IWineD3DBaseTexture_Release(baseTexture
);
447 TRACE("(%p) : About to load surface\n", This
);
449 if(!device
->isInDraw
) {
450 ActivateContext(device
, device
->lastActiveRenderTarget
, CTXUSAGE_RESOURCELOAD
);
454 glEnable(This
->glDescription
.target
);/* make sure texture support is enabled in this context */
455 if (!This
->glDescription
.level
) {
456 if (!This
->glDescription
.textureName
) {
457 glGenTextures(1, &This
->glDescription
.textureName
);
458 checkGLcall("glGenTextures");
459 TRACE("Surface %p given name %d\n", This
, This
->glDescription
.textureName
);
461 glBindTexture(This
->glDescription
.target
, This
->glDescription
.textureName
);
462 checkGLcall("glBindTexture");
463 IWineD3DSurface_LoadTexture(iface
, FALSE
);
464 /* This is where we should be reducing the amount of GLMemoryUsed */
465 } else if (This
->glDescription
.textureName
) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
466 /* assume this is a coding error not a real error for now */
467 FIXME("Mipmap surface has a glTexture bound to it!\n");
469 if (This
->resource
.pool
== WINED3DPOOL_DEFAULT
) {
470 /* Tell opengl to try and keep this texture in video ram (well mostly) */
473 glPrioritizeTextures(1, &This
->glDescription
.textureName
, &tmp
);
480 WINED3DRESOURCETYPE WINAPI
IWineD3DSurfaceImpl_GetType(IWineD3DSurface
*iface
) {
481 TRACE("(%p) : calling resourceimpl_GetType\n", iface
);
482 return IWineD3DResourceImpl_GetType((IWineD3DResource
*)iface
);
485 HRESULT WINAPI
IWineD3DSurfaceImpl_GetParent(IWineD3DSurface
*iface
, IUnknown
**pParent
) {
486 TRACE("(%p) : calling resourceimpl_GetParent\n", iface
);
487 return IWineD3DResourceImpl_GetParent((IWineD3DResource
*)iface
, pParent
);
490 /* ******************************************************
491 IWineD3DSurface IWineD3DSurface parts follow
492 ****************************************************** */
494 HRESULT WINAPI
IWineD3DSurfaceImpl_GetContainer(IWineD3DSurface
* iface
, REFIID riid
, void** ppContainer
) {
495 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
496 IWineD3DBase
*container
= 0;
498 TRACE("(This %p, riid %s, ppContainer %p)\n", This
, debugstr_guid(riid
), ppContainer
);
501 ERR("Called without a valid ppContainer.\n");
505 * If the surface is created using CreateImageSurface/CreateOffscreenPlainSurface, CreateRenderTarget,
506 * or CreateDepthStencilSurface, the surface is considered stand alone. In this case,
507 * GetContainer will return the Direct3D device used to create the surface.
509 if (This
->container
) {
510 container
= This
->container
;
512 container
= (IWineD3DBase
*)This
->resource
.wineD3DDevice
;
515 TRACE("Relaying to QueryInterface\n");
516 return IUnknown_QueryInterface(container
, riid
, ppContainer
);
519 HRESULT WINAPI
IWineD3DSurfaceImpl_GetDesc(IWineD3DSurface
*iface
, WINED3DSURFACE_DESC
*pDesc
) {
520 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
522 TRACE("(%p) : copying into %p\n", This
, pDesc
);
523 if(pDesc
->Format
!= NULL
) *(pDesc
->Format
) = This
->resource
.format
;
524 if(pDesc
->Type
!= NULL
) *(pDesc
->Type
) = This
->resource
.resourceType
;
525 if(pDesc
->Usage
!= NULL
) *(pDesc
->Usage
) = This
->resource
.usage
;
526 if(pDesc
->Pool
!= NULL
) *(pDesc
->Pool
) = This
->resource
.pool
;
527 if(pDesc
->Size
!= NULL
) *(pDesc
->Size
) = This
->resource
.size
; /* dx8 only */
528 if(pDesc
->MultiSampleType
!= NULL
) *(pDesc
->MultiSampleType
) = This
->currentDesc
.MultiSampleType
;
529 if(pDesc
->MultiSampleQuality
!= NULL
) *(pDesc
->MultiSampleQuality
) = This
->currentDesc
.MultiSampleQuality
;
530 if(pDesc
->Width
!= NULL
) *(pDesc
->Width
) = This
->currentDesc
.Width
;
531 if(pDesc
->Height
!= NULL
) *(pDesc
->Height
) = This
->currentDesc
.Height
;
535 void WINAPI
IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface
*iface
, UINT textureName
, int target
) {
536 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
537 TRACE("(%p) : setting textureName %u, target %i\n", This
, textureName
, target
);
538 if (This
->glDescription
.textureName
== 0 && textureName
!= 0) {
539 This
->Flags
&= ~SFLAG_INTEXTURE
;
540 IWineD3DSurface_AddDirtyRect(iface
, NULL
);
542 This
->glDescription
.textureName
= textureName
;
543 This
->glDescription
.target
= target
;
544 This
->Flags
&= ~SFLAG_ALLOCATED
;
547 void WINAPI
IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface
*iface
, glDescriptor
**glDescription
) {
548 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
549 TRACE("(%p) : returning %p\n", This
, &This
->glDescription
);
550 *glDescription
= &This
->glDescription
;
553 /* TODO: think about moving this down to resource? */
554 const void *WINAPI
IWineD3DSurfaceImpl_GetData(IWineD3DSurface
*iface
) {
555 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
556 /* 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 */
557 if (This
->resource
.pool
!= WINED3DPOOL_SYSTEMMEM
) {
558 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface
);
560 return (CONST
void*)(This
->resource
.allocatedMemory
);
563 static void read_from_framebuffer(IWineD3DSurfaceImpl
*This
, CONST RECT
*rect
, void *dest
, UINT pitch
, BOOL srcUpsideDown
) {
567 BYTE
*row
, *top
, *bottom
;
571 switch(This
->resource
.format
)
575 /* GL can't return palettized data, so read ARGB pixels into a
576 * separate block of memory and convert them into palettized format
577 * in software. Slow, but if the app means to use palettized render
578 * targets and locks it...
580 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
581 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
582 * for the color channels when palettizing the colors.
585 type
= GL_UNSIGNED_BYTE
;
587 mem
= HeapAlloc(GetProcessHeap(), 0, This
->resource
.size
* 3);
589 ERR("Out of memory\n");
592 bpp
= This
->bytesPerPixel
* 3;
598 fmt
= This
->glDescription
.glFormat
;
599 type
= This
->glDescription
.glType
;
600 bpp
= This
->bytesPerPixel
;
603 glReadPixels(rect
->left
, rect
->top
,
604 rect
->right
- rect
->left
,
605 rect
->bottom
- rect
->top
,
607 vcheckGLcall("glReadPixels");
609 /* TODO: Merge this with the palettization loop below for P8 targets */
613 /* glReadPixels returns the image upside down, and there is no way to prevent this.
614 Flip the lines in software */
615 len
= (rect
->right
- rect
->left
) * bpp
;
616 off
= rect
->left
* bpp
;
618 row
= HeapAlloc(GetProcessHeap(), 0, len
);
620 ERR("Out of memory\n");
621 if(This
->resource
.format
== WINED3DFMT_P8
) HeapFree(GetProcessHeap(), 0, mem
);
625 top
= mem
+ pitch
* rect
->top
;
626 bottom
= ((BYTE
*) mem
) + pitch
* ( rect
->bottom
- rect
->top
- 1);
627 for(i
= 0; i
< (rect
->bottom
- rect
->top
) / 2; i
++) {
628 memcpy(row
, top
+ off
, len
);
629 memcpy(top
+ off
, bottom
+ off
, len
);
630 memcpy(bottom
+ off
, row
, len
);
634 HeapFree(GetProcessHeap(), 0, row
);
637 if(This
->resource
.format
== WINED3DFMT_P8
) {
639 DWORD width
= pitch
/ 3;
642 pal
= This
->palette
->palents
;
644 pal
= This
->resource
.wineD3DDevice
->palettes
[This
->resource
.wineD3DDevice
->currentPalette
];
647 for(y
= rect
->top
; y
< rect
->bottom
; y
++) {
648 for(x
= rect
->left
; x
< rect
->right
; x
++) {
649 /* start lines pixels */
650 BYTE
*blue
= (BYTE
*) ((BYTE
*) mem
) + y
* pitch
+ x
* (sizeof(BYTE
) * 3);
651 BYTE
*green
= blue
+ 1;
652 BYTE
*red
= green
+ 1;
654 for(c
= 0; c
< 256; c
++) {
655 if(*red
== pal
[c
].peRed
&&
656 *green
== pal
[c
].peGreen
&&
657 *blue
== pal
[c
].peBlue
)
659 *((BYTE
*) dest
+ y
* width
+ x
) = c
;
665 HeapFree(GetProcessHeap(), 0, mem
);
669 static HRESULT WINAPI
IWineD3DSurfaceImpl_LockRect(IWineD3DSurface
*iface
, WINED3DLOCKED_RECT
* pLockedRect
, CONST RECT
* pRect
, DWORD Flags
) {
670 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
671 IWineD3DDeviceImpl
*myDevice
= This
->resource
.wineD3DDevice
;
672 IWineD3DSwapChainImpl
*swapchain
= NULL
;
674 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This
, pRect
, Flags
, pLockedRect
, This
->resource
.allocatedMemory
);
676 if (This
->Flags
& SFLAG_LOCKED
) {
677 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
678 return WINED3DERR_INVALIDCALL
;
680 if (!(This
->Flags
& SFLAG_LOCKABLE
)) {
681 /* Note: UpdateTextures calls CopyRects which calls this routine to populate the
682 texture regions, and since the destination is an unlockable region we need
684 TRACE("Warning: trying to lock unlockable surf@%p\n", This
);
685 /*return WINED3DERR_INVALIDCALL; */
688 pLockedRect
->Pitch
= IWineD3DSurface_GetPitch(iface
);
690 /* Mark the surface locked */
691 This
->Flags
|= SFLAG_LOCKED
;
693 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy */
694 if(!This
->resource
.allocatedMemory
) {
695 This
->resource
.allocatedMemory
= HeapAlloc(GetProcessHeap() ,0 , This
->resource
.size
+ 4);
696 This
->Flags
&= ~SFLAG_INSYSMEM
; /* This is the marker that surface data has to be downloaded */
699 /* Calculate the correct start address to report */
701 pLockedRect
->pBits
= This
->resource
.allocatedMemory
;
702 This
->lockedRect
.left
= 0;
703 This
->lockedRect
.top
= 0;
704 This
->lockedRect
.right
= This
->currentDesc
.Width
;
705 This
->lockedRect
.bottom
= This
->currentDesc
.Height
;
706 TRACE("Locked Rect (%p) = l %d, t %d, r %d, b %d\n", &This
->lockedRect
, This
->lockedRect
.left
, This
->lockedRect
.top
, This
->lockedRect
.right
, This
->lockedRect
.bottom
);
708 TRACE("Lock Rect (%p) = l %d, t %d, r %d, b %d\n", pRect
, pRect
->left
, pRect
->top
, pRect
->right
, pRect
->bottom
);
710 /* DXTn textures are based on compressed blocks of 4x4 pixels, each
711 * 16 bytes large (8 bytes in case of DXT1). Because of that Pitch has
712 * slightly different meaning compared to regular textures. For DXTn
713 * textures Pitch is the size of a row of blocks, 4 high and "width"
714 * long. The x offset is calculated differently as well, since moving 4
715 * pixels to the right actually moves an entire 4x4 block to right, ie
716 * 16 bytes (8 in case of DXT1). */
717 if (This
->resource
.format
== WINED3DFMT_DXT1
) {
718 pLockedRect
->pBits
= This
->resource
.allocatedMemory
+ (pLockedRect
->Pitch
* pRect
->top
/ 4) + (pRect
->left
* 2);
719 } else if (This
->resource
.format
== WINED3DFMT_DXT2
|| This
->resource
.format
== WINED3DFMT_DXT3
720 || This
->resource
.format
== WINED3DFMT_DXT4
|| This
->resource
.format
== WINED3DFMT_DXT5
) {
721 pLockedRect
->pBits
= This
->resource
.allocatedMemory
+ (pLockedRect
->Pitch
* pRect
->top
/ 4) + (pRect
->left
* 4);
723 pLockedRect
->pBits
= This
->resource
.allocatedMemory
+ (pLockedRect
->Pitch
* pRect
->top
) + (pRect
->left
* This
->bytesPerPixel
);
725 This
->lockedRect
.left
= pRect
->left
;
726 This
->lockedRect
.top
= pRect
->top
;
727 This
->lockedRect
.right
= pRect
->right
;
728 This
->lockedRect
.bottom
= pRect
->bottom
;
731 if (This
->Flags
& SFLAG_NONPOW2
) {
732 TRACE("Locking non-power 2 texture\n");
735 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
736 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
739 if(!(This
->Flags
& SFLAG_DYNLOCK
)) {
741 /* MAXLOCKCOUNT is defined in wined3d_private.h */
742 if(This
->lockCount
> MAXLOCKCOUNT
) {
743 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
744 This
->Flags
|= SFLAG_DYNLOCK
;
748 if (Flags
& WINED3DLOCK_DISCARD
) {
749 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
750 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
751 This
->Flags
|= SFLAG_INSYSMEM
;
754 if (This
->Flags
& SFLAG_INSYSMEM
) {
755 TRACE("Local copy is up to date, not downloading data\n");
759 /* Now download the surface content from opengl
760 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
761 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
763 IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DSwapChain
, (void **)&swapchain
);
764 if(swapchain
|| iface
== myDevice
->render_targets
[0]) {
765 BOOL srcIsUpsideDown
;
767 if(wined3d_settings
.rendertargetlock_mode
== RTL_DISABLE
) {
768 static BOOL warned
= FALSE
;
770 ERR("The application tries to lock the render target, but render target locking is disabled\n");
773 if(swapchain
) IWineD3DSwapChain_Release((IWineD3DSwapChain
*) swapchain
);
777 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
778 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
779 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
780 * context->last_was_blit set on the unlock.
782 ActivateContext(myDevice
, iface
, CTXUSAGE_BLIT
);
785 /* Select the correct read buffer, and give some debug output.
786 * There is no need to keep track of the current read buffer or reset it, every part of the code
787 * that reads sets the read buffer as desired.
790 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
791 * Read from the back buffer
793 TRACE("Locking offscreen render target\n");
794 glReadBuffer(myDevice
->offscreenBuffer
);
795 srcIsUpsideDown
= TRUE
;
797 GLenum buffer
= surface_get_gl_buffer(iface
, (IWineD3DSwapChain
*)swapchain
);
798 TRACE("Locking %#x buffer\n", buffer
);
799 glReadBuffer(buffer
);
800 checkGLcall("glReadBuffer");
802 IWineD3DSwapChain_Release((IWineD3DSwapChain
*) swapchain
);
803 srcIsUpsideDown
= FALSE
;
806 switch(wined3d_settings
.rendertargetlock_mode
) {
810 read_from_framebuffer(This
, &This
->lockedRect
, This
->resource
.allocatedMemory
, pLockedRect
->Pitch
, srcIsUpsideDown
);
815 read_from_framebuffer(This
, &This
->lockedRect
, This
->resource
.allocatedMemory
, pLockedRect
->Pitch
, srcIsUpsideDown
);
816 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
821 /* Mark the local copy up to date if a full download was done */
822 if(This
->lockedRect
.left
== 0 &&
823 This
->lockedRect
.top
== 0 &&
824 This
->lockedRect
.right
== This
->currentDesc
.Width
&&
825 This
->lockedRect
.bottom
== This
->currentDesc
.Height
) {
826 This
->Flags
|= SFLAG_INSYSMEM
;
828 } else if(iface
== myDevice
->stencilBufferTarget
) {
829 /** the depth stencil in openGL has a format of GL_FLOAT
830 * which should be good for WINED3DFMT_D16_LOCKABLE
832 * it is unclear what format the stencil buffer is in except.
833 * 'Each index is converted to fixed point...
834 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
835 * mappings in the table GL_PIXEL_MAP_S_TO_S.
836 * glReadPixels(This->lockedRect.left,
837 * This->lockedRect.bottom - j - 1,
838 * This->lockedRect.right - This->lockedRect.left,
840 * GL_DEPTH_COMPONENT,
842 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
844 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
845 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
846 * none of that is the case the problem is not in this function :-)
847 ********************************************/
848 FIXME("Depth stencil locking not supported yet\n");
850 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
851 TRACE("locking an ordinary surface\n");
853 if (0 != This
->glDescription
.textureName
) {
854 /* Now I have to copy thing bits back */
856 if(myDevice
->createParms
.BehaviorFlags
& WINED3DCREATE_MULTITHREADED
) {
857 ActivateContext(myDevice
, myDevice
->lastActiveRenderTarget
, CTXUSAGE_RESOURCELOAD
);
861 /* Make sure that a proper texture unit is selected, bind the texture and dirtify the sampler to restore the texture on the next draw */
862 if (GL_SUPPORT(ARB_MULTITEXTURE
)) {
863 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB
));
864 checkGLcall("glActiveTextureARB");
866 IWineD3DDeviceImpl_MarkStateDirty(This
->resource
.wineD3DDevice
, STATE_SAMPLER(0));
867 IWineD3DSurface_PreLoad(iface
);
869 surface_download_data(This
);
875 if (Flags
& (WINED3DLOCK_NO_DIRTY_UPDATE
| WINED3DLOCK_READONLY
)) {
878 IWineD3DBaseTexture
*pBaseTexture
;
881 * as seen in msdn docs
883 IWineD3DSurface_AddDirtyRect(iface
, &This
->lockedRect
);
885 /** Dirtify Container if needed */
886 if (WINED3D_OK
== IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DBaseTexture
, (void **)&pBaseTexture
) && pBaseTexture
!= NULL
) {
887 TRACE("Making container dirty\n");
888 IWineD3DBaseTexture_SetDirty(pBaseTexture
, TRUE
);
889 IWineD3DBaseTexture_Release(pBaseTexture
);
891 TRACE("Surface is standalone, no need to dirty the container\n");
895 TRACE("returning memory@%p, pitch(%d) dirtyfied(%d)\n", pLockedRect
->pBits
, pLockedRect
->Pitch
,
896 This
->Flags
& (SFLAG_INDRAWABLE
| SFLAG_INTEXTURE
) ? 0 : 1);
900 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl
*This
) {
902 GLint prev_rasterpos
[4];
904 BOOL storechanged
= FALSE
, memory_allocated
= FALSE
;
908 UINT pitch
= IWineD3DSurface_GetPitch((IWineD3DSurface
*) This
); /* target is argb, 4 byte */
910 glDisable(GL_TEXTURE_2D
);
911 vcheckGLcall("glDisable(GL_TEXTURE_2D)");
914 vcheckGLcall("glFlush");
915 glGetIntegerv(GL_PACK_SWAP_BYTES
, &prev_store
);
916 vcheckGLcall("glIntegerv");
917 glGetIntegerv(GL_CURRENT_RASTER_POSITION
, &prev_rasterpos
[0]);
918 vcheckGLcall("glIntegerv");
919 glPixelZoom(1.0, -1.0);
920 vcheckGLcall("glPixelZoom");
922 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
923 glGetIntegerv(GL_UNPACK_ROW_LENGTH
, &skipBytes
);
924 glPixelStorei(GL_UNPACK_ROW_LENGTH
, This
->currentDesc
.Width
);
926 glRasterPos3i(This
->lockedRect
.left
, This
->lockedRect
.top
, 1);
927 vcheckGLcall("glRasterPos2f");
929 /* Some drivers(radeon dri, others?) don't like exceptions during
930 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
931 * after ReleaseDC. Reading it will cause an exception, which x11drv will
932 * catch to put the dib section in InSync mode, which leads to a crash
933 * and a blocked x server on my radeon card.
935 * The following lines read the dib section so it is put in inSync mode
936 * before glDrawPixels is called and the crash is prevented. There won't
937 * be any interfering gdi accesses, because UnlockRect is called from
938 * ReleaseDC, and the app won't use the dc any more afterwards.
940 if(This
->Flags
& SFLAG_DIBSECTION
) {
942 read
= This
->resource
.allocatedMemory
[0];
945 switch (This
->resource
.format
) {
946 /* No special care needed */
947 case WINED3DFMT_A4R4G4B4
:
948 case WINED3DFMT_R5G6B5
:
949 case WINED3DFMT_A1R5G5B5
:
950 case WINED3DFMT_R8G8B8
:
951 type
= This
->glDescription
.glType
;
952 fmt
= This
->glDescription
.glFormat
;
953 mem
= This
->resource
.allocatedMemory
;
954 bpp
= This
->bytesPerPixel
;
957 case WINED3DFMT_X4R4G4B4
:
960 unsigned short *data
;
961 data
= (unsigned short *)This
->resource
.allocatedMemory
;
962 size
= (This
->lockedRect
.bottom
- This
->lockedRect
.top
) * (This
->lockedRect
.right
- This
->lockedRect
.left
);
968 type
= This
->glDescription
.glType
;
969 fmt
= This
->glDescription
.glFormat
;
970 mem
= This
->resource
.allocatedMemory
;
971 bpp
= This
->bytesPerPixel
;
975 case WINED3DFMT_X1R5G5B5
:
978 unsigned short *data
;
979 data
= (unsigned short *)This
->resource
.allocatedMemory
;
980 size
= (This
->lockedRect
.bottom
- This
->lockedRect
.top
) * (This
->lockedRect
.right
- This
->lockedRect
.left
);
986 type
= This
->glDescription
.glType
;
987 fmt
= This
->glDescription
.glFormat
;
988 mem
= This
->resource
.allocatedMemory
;
989 bpp
= This
->bytesPerPixel
;
993 case WINED3DFMT_X8R8G8B8
:
995 /* make sure the X byte is set to alpha on, since it
996 could be any random value. This fixes the intro movie in Pirates! */
999 data
= (unsigned int *)This
->resource
.allocatedMemory
;
1000 size
= (This
->lockedRect
.bottom
- This
->lockedRect
.top
) * (This
->lockedRect
.right
- This
->lockedRect
.left
);
1002 *data
|= 0xFF000000;
1009 case WINED3DFMT_A8R8G8B8
:
1011 glPixelStorei(GL_PACK_SWAP_BYTES
, TRUE
);
1012 vcheckGLcall("glPixelStorei");
1013 storechanged
= TRUE
;
1014 type
= This
->glDescription
.glType
;
1015 fmt
= This
->glDescription
.glFormat
;
1016 mem
= This
->resource
.allocatedMemory
;
1017 bpp
= This
->bytesPerPixel
;
1021 case WINED3DFMT_A2R10G10B10
:
1023 glPixelStorei(GL_PACK_SWAP_BYTES
, TRUE
);
1024 vcheckGLcall("glPixelStorei");
1025 storechanged
= TRUE
;
1026 type
= This
->glDescription
.glType
;
1027 fmt
= This
->glDescription
.glFormat
;
1028 mem
= This
->resource
.allocatedMemory
;
1029 bpp
= This
->bytesPerPixel
;
1035 int height
= This
->glRect
.bottom
- This
->glRect
.top
;
1036 type
= GL_UNSIGNED_BYTE
;
1039 mem
= HeapAlloc(GetProcessHeap(), 0, This
->resource
.size
* sizeof(DWORD
));
1041 ERR("Out of memory\n");
1044 memory_allocated
= TRUE
;
1045 d3dfmt_convert_surface(This
->resource
.allocatedMemory
,
1053 bpp
= This
->bytesPerPixel
* 4;
1059 FIXME("Unsupported Format %u in locking func\n", This
->resource
.format
);
1062 type
= This
->glDescription
.glType
;
1063 fmt
= This
->glDescription
.glFormat
;
1064 mem
= This
->resource
.allocatedMemory
;
1065 bpp
= This
->bytesPerPixel
;
1068 glDrawPixels(This
->lockedRect
.right
- This
->lockedRect
.left
,
1069 (This
->lockedRect
.bottom
- This
->lockedRect
.top
)-1,
1071 mem
+ bpp
* This
->lockedRect
.left
+ pitch
* This
->lockedRect
.top
);
1072 checkGLcall("glDrawPixels");
1073 glPixelZoom(1.0,1.0);
1074 vcheckGLcall("glPixelZoom");
1076 glRasterPos3iv(&prev_rasterpos
[0]);
1077 vcheckGLcall("glRasterPos3iv");
1079 /* Reset to previous pack row length */
1080 glPixelStorei(GL_UNPACK_ROW_LENGTH
, skipBytes
);
1081 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1083 glPixelStorei(GL_PACK_SWAP_BYTES
, prev_store
);
1084 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
1087 /* Blitting environment requires that 2D texturing is enabled. It was turned off before,
1090 glEnable(GL_TEXTURE_2D
);
1091 checkGLcall("glEnable(GL_TEXTURE_2D)");
1093 if(memory_allocated
) HeapFree(GetProcessHeap(), 0, mem
);
1097 static void flush_to_framebuffer_texture(IWineD3DSurfaceImpl
*This
) {
1098 float glTexCoord
[4];
1100 glTexCoord
[0] = (float) This
->lockedRect
.left
/ (float) This
->pow2Width
; /* left */
1101 glTexCoord
[1] = (float) This
->lockedRect
.right
/ (float) This
->pow2Width
; /* right */
1102 glTexCoord
[2] = (float) This
->lockedRect
.top
/ (float) This
->pow2Height
; /* top */
1103 glTexCoord
[3] = (float) This
->lockedRect
.bottom
/ (float) This
->pow2Height
; /* bottom */
1105 IWineD3DSurface_PreLoad((IWineD3DSurface
*) This
);
1109 glBindTexture(GL_TEXTURE_2D
, This
->glDescription
.textureName
);
1110 checkGLcall("glEnable glBindTexture");
1112 /* No filtering for blts */
1113 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
1114 checkGLcall("glTexParameteri");
1115 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
1116 checkGLcall("glTexParameteri");
1118 /* Start drawing a quad */
1121 glColor3d(1.0f
, 1.0f
, 1.0f
);
1122 glTexCoord2f(glTexCoord
[0], glTexCoord
[2]);
1123 glVertex3f(This
->lockedRect
.left
, This
->lockedRect
.top
, 0.0);
1125 glTexCoord2f(glTexCoord
[0], glTexCoord
[3]);
1126 glVertex3f(This
->lockedRect
.left
, This
->lockedRect
.bottom
, 0.0);
1128 glTexCoord2f(glTexCoord
[1], glTexCoord
[3]);
1129 glVertex3d(This
->lockedRect
.right
, This
->lockedRect
.bottom
, 0.0);
1131 glTexCoord2f(glTexCoord
[1], glTexCoord
[2]);
1132 glVertex3f(This
->lockedRect
.right
, This
->lockedRect
.top
, 0.0);
1135 checkGLcall("glEnd");
1137 /* Unbind the texture */
1138 glBindTexture(GL_TEXTURE_2D
, 0);
1139 checkGLcall("glEnable glBindTexture");
1144 static HRESULT WINAPI
IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface
*iface
) {
1145 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
1146 IWineD3DDeviceImpl
*myDevice
= This
->resource
.wineD3DDevice
;
1147 IWineD3DSwapChainImpl
*swapchain
= NULL
;
1149 if (!(This
->Flags
& SFLAG_LOCKED
)) {
1150 WARN("trying to Unlock an unlocked surf@%p\n", This
);
1151 return WINED3DERR_INVALIDCALL
;
1154 TRACE("(%p) : dirtyfied(%d)\n", This
, This
->Flags
& (SFLAG_INDRAWABLE
| SFLAG_INTEXTURE
) ? 0 : 1);
1156 if (This
->Flags
& (SFLAG_INDRAWABLE
| SFLAG_INTEXTURE
)) {
1157 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This
);
1161 IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DSwapChain
, (void **)&swapchain
);
1162 if(swapchain
|| (myDevice
->render_targets
&& iface
== myDevice
->render_targets
[0])) {
1163 if(wined3d_settings
.rendertargetlock_mode
== RTL_DISABLE
) {
1164 static BOOL warned
= FALSE
;
1166 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1169 if(swapchain
) IWineD3DSwapChain_Release((IWineD3DSwapChain
*) swapchain
);
1173 /* Activate the correct context for the render target */
1174 ActivateContext(myDevice
, iface
, CTXUSAGE_BLIT
);
1178 /* Primary offscreen render target */
1179 TRACE("Offscreen render target\n");
1180 glDrawBuffer(myDevice
->offscreenBuffer
);
1181 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1183 GLenum buffer
= surface_get_gl_buffer(iface
, (IWineD3DSwapChain
*)swapchain
);
1184 TRACE("Unlocking %#x buffer\n", buffer
);
1185 glDrawBuffer(buffer
);
1186 checkGLcall("glDrawBuffer");
1188 IWineD3DSwapChain_Release((IWineD3DSwapChain
*)swapchain
);
1191 switch(wined3d_settings
.rendertargetlock_mode
) {
1195 flush_to_framebuffer_drawpixels(This
);
1200 flush_to_framebuffer_texture(This
);
1204 glDrawBuffer(myDevice
->offscreenBuffer
);
1205 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1206 } else if(swapchain
->backBuffer
) {
1207 glDrawBuffer(GL_BACK
);
1208 checkGLcall("glDrawBuffer(GL_BACK)");
1210 glDrawBuffer(GL_FRONT
);
1211 checkGLcall("glDrawBuffer(GL_FRONT)");
1215 This
->dirtyRect
.left
= This
->currentDesc
.Width
;
1216 This
->dirtyRect
.top
= This
->currentDesc
.Height
;
1217 This
->dirtyRect
.right
= 0;
1218 This
->dirtyRect
.bottom
= 0;
1219 This
->Flags
|= SFLAG_INDRAWABLE
;
1220 } else if(iface
== myDevice
->stencilBufferTarget
) {
1221 FIXME("Depth Stencil buffer locking is not implemented\n");
1223 /* The rest should be a normal texture */
1224 IWineD3DBaseTextureImpl
*impl
;
1225 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1226 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1227 * states need resetting
1229 if(IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DBaseTexture
, (void **)&impl
) == WINED3D_OK
) {
1230 if(impl
->baseTexture
.bindCount
) {
1231 IWineD3DDeviceImpl_MarkStateDirty(myDevice
, STATE_SAMPLER(impl
->baseTexture
.sampler
));
1233 IWineD3DBaseTexture_Release((IWineD3DBaseTexture
*) impl
);
1238 This
->Flags
&= ~SFLAG_LOCKED
;
1239 memset(&This
->lockedRect
, 0, sizeof(RECT
));
1243 HRESULT WINAPI
IWineD3DSurfaceImpl_GetDC(IWineD3DSurface
*iface
, HDC
*pHDC
) {
1244 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
1245 WINED3DLOCKED_RECT lock
;
1252 const StaticPixelFormatDesc
*formatEntry
= getFormatDescEntry(This
->resource
.format
, NULL
, NULL
);
1254 TRACE("(%p)->(%p)\n",This
,pHDC
);
1256 if(This
->Flags
& SFLAG_USERPTR
) {
1257 ERR("Not supported on surfaces with an application-provided surfaces\n");
1258 return WINEDDERR_NODC
;
1261 /* Give more detailed info for ddraw */
1262 if (This
->Flags
& SFLAG_DCINUSE
)
1263 return WINEDDERR_DCALREADYCREATED
;
1265 /* Can't GetDC if the surface is locked */
1266 if (This
->Flags
& SFLAG_LOCKED
)
1267 return WINED3DERR_INVALIDCALL
;
1269 memset(&lock
, 0, sizeof(lock
)); /* To be sure */
1271 /* Create a DIB section if there isn't a hdc yet */
1274 SYSTEM_INFO sysInfo
;
1275 void *oldmem
= This
->resource
.allocatedMemory
;
1277 switch (This
->bytesPerPixel
) {
1280 /* Allocate extra space to store the RGB bit masks. */
1281 b_info
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(BITMAPINFOHEADER
) + 3 * sizeof(DWORD
));
1285 b_info
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(BITMAPINFOHEADER
));
1289 /* Allocate extra space for a palette. */
1290 b_info
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
1291 sizeof(BITMAPINFOHEADER
)
1293 * (1 << (This
->bytesPerPixel
* 8)));
1298 return E_OUTOFMEMORY
;
1300 /* Some apps access the surface in via DWORDs, and do not take the necessary care at the end of the
1301 * surface. So we need at least extra 4 bytes at the end of the surface. Check against the page size,
1302 * if the last page used for the surface has at least 4 spare bytes we're safe, otherwise
1303 * add an extra line to the dib section
1305 GetSystemInfo(&sysInfo
);
1306 if( ((This
->resource
.size
+ 3) % sysInfo
.dwPageSize
) < 4) {
1308 TRACE("Adding an extra line to the dib section\n");
1311 b_info
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
1312 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
1313 b_info
->bmiHeader
.biWidth
= IWineD3DSurface_GetPitch(iface
) / This
->bytesPerPixel
;
1314 b_info
->bmiHeader
.biHeight
= -This
->currentDesc
.Height
-extraline
;
1315 b_info
->bmiHeader
.biSizeImage
= ( This
->currentDesc
.Height
+ extraline
) * IWineD3DSurface_GetPitch(iface
);
1316 b_info
->bmiHeader
.biPlanes
= 1;
1317 b_info
->bmiHeader
.biBitCount
= This
->bytesPerPixel
* 8;
1319 b_info
->bmiHeader
.biXPelsPerMeter
= 0;
1320 b_info
->bmiHeader
.biYPelsPerMeter
= 0;
1321 b_info
->bmiHeader
.biClrUsed
= 0;
1322 b_info
->bmiHeader
.biClrImportant
= 0;
1324 /* Get the bit masks */
1325 masks
= (DWORD
*) &(b_info
->bmiColors
);
1326 switch (This
->resource
.format
) {
1327 case WINED3DFMT_R8G8B8
:
1328 usage
= DIB_RGB_COLORS
;
1329 b_info
->bmiHeader
.biCompression
= BI_RGB
;
1332 case WINED3DFMT_X1R5G5B5
:
1333 case WINED3DFMT_A1R5G5B5
:
1334 case WINED3DFMT_A4R4G4B4
:
1335 case WINED3DFMT_X4R4G4B4
:
1336 case WINED3DFMT_R3G3B2
:
1337 case WINED3DFMT_A8R3G3B2
:
1338 case WINED3DFMT_A2B10G10R10
:
1339 case WINED3DFMT_A8B8G8R8
:
1340 case WINED3DFMT_X8B8G8R8
:
1341 case WINED3DFMT_A2R10G10B10
:
1342 case WINED3DFMT_R5G6B5
:
1343 case WINED3DFMT_A16B16G16R16
:
1345 b_info
->bmiHeader
.biCompression
= BI_BITFIELDS
;
1346 masks
[0] = formatEntry
->redMask
;
1347 masks
[1] = formatEntry
->greenMask
;
1348 masks
[2] = formatEntry
->blueMask
;
1352 /* Don't know palette */
1353 b_info
->bmiHeader
.biCompression
= BI_RGB
;
1360 HeapFree(GetProcessHeap(), 0, b_info
);
1361 return HRESULT_FROM_WIN32(GetLastError());
1364 TRACE("Creating a DIB section with size %dx%dx%d, size=%d\n", b_info
->bmiHeader
.biWidth
, b_info
->bmiHeader
.biHeight
, b_info
->bmiHeader
.biBitCount
, b_info
->bmiHeader
.biSizeImage
);
1365 This
->dib
.DIBsection
= CreateDIBSection(ddc
, b_info
, usage
, &This
->dib
.bitmap_data
, 0 /* Handle */, 0 /* Offset */);
1368 if (!This
->dib
.DIBsection
) {
1369 ERR("CreateDIBSection failed!\n");
1370 HeapFree(GetProcessHeap(), 0, b_info
);
1371 return HRESULT_FROM_WIN32(GetLastError());
1374 TRACE("DIBSection at : %p\n", This
->dib
.bitmap_data
);
1376 /* copy the existing surface to the dib section */
1377 if(This
->resource
.allocatedMemory
) {
1378 memcpy(This
->dib
.bitmap_data
, This
->resource
.allocatedMemory
, b_info
->bmiHeader
.biSizeImage
);
1379 /* We won't need that any more */
1381 /* This is to make LockRect read the gl Texture although memory is allocated */
1382 This
->Flags
&= ~SFLAG_INSYSMEM
;
1385 HeapFree(GetProcessHeap(), 0, b_info
);
1387 /* Use the dib section from now on */
1388 This
->resource
.allocatedMemory
= This
->dib
.bitmap_data
;
1390 /* Now allocate a HDC */
1391 This
->hDC
= CreateCompatibleDC(0);
1392 This
->dib
.holdbitmap
= SelectObject(This
->hDC
, This
->dib
.DIBsection
);
1393 TRACE("using wined3d palette %p\n", This
->palette
);
1394 SelectPalette(This
->hDC
,
1395 This
->palette
? This
->palette
->hpal
: 0,
1398 This
->Flags
|= SFLAG_DIBSECTION
;
1400 if(This
->Flags
& SFLAG_CLIENT
) {
1401 IWineD3DSurface_PreLoad(iface
);
1403 HeapFree(GetProcessHeap(), 0, oldmem
);
1406 /* Lock the surface */
1407 hr
= IWineD3DSurface_LockRect(iface
,
1412 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr
);
1413 /* keep the dib section */
1417 if(This
->resource
.format
== WINED3DFMT_P8
||
1418 This
->resource
.format
== WINED3DFMT_A8P8
) {
1421 PALETTEENTRY ent
[256];
1423 GetPaletteEntries(This
->palette
->hpal
, 0, 256, ent
);
1424 for (n
=0; n
<256; n
++) {
1425 col
[n
].rgbRed
= ent
[n
].peRed
;
1426 col
[n
].rgbGreen
= ent
[n
].peGreen
;
1427 col
[n
].rgbBlue
= ent
[n
].peBlue
;
1428 col
[n
].rgbReserved
= 0;
1431 IWineD3DDeviceImpl
*device
= This
->resource
.wineD3DDevice
;
1433 for (n
=0; n
<256; n
++) {
1434 col
[n
].rgbRed
= device
->palettes
[device
->currentPalette
][n
].peRed
;
1435 col
[n
].rgbGreen
= device
->palettes
[device
->currentPalette
][n
].peGreen
;
1436 col
[n
].rgbBlue
= device
->palettes
[device
->currentPalette
][n
].peBlue
;
1437 col
[n
].rgbReserved
= 0;
1441 SetDIBColorTable(This
->hDC
, 0, 256, col
);
1445 TRACE("returning %p\n",*pHDC
);
1446 This
->Flags
|= SFLAG_DCINUSE
;
1451 HRESULT WINAPI
IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface
*iface
, HDC hDC
) {
1452 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
1454 TRACE("(%p)->(%p)\n",This
,hDC
);
1456 if (!(This
->Flags
& SFLAG_DCINUSE
))
1457 return WINED3DERR_INVALIDCALL
;
1459 if (This
->hDC
!=hDC
) {
1460 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC
, This
->hDC
);
1461 return WINED3DERR_INVALIDCALL
;
1464 /* we locked first, so unlock now */
1465 IWineD3DSurface_UnlockRect(iface
);
1467 This
->Flags
&= ~SFLAG_DCINUSE
;
1472 /* ******************************************************
1473 IWineD3DSurface Internal (No mapping to directx api) parts follow
1474 ****************************************************** */
1476 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
) {
1477 BOOL colorkey_active
= need_alpha_ck
&& (This
->CKeyFlags
& WINEDDSD_CKSRCBLT
);
1478 const GlPixelFormatDesc
*glDesc
;
1479 getFormatDescEntry(This
->resource
.format
, &GLINFO_LOCATION
, &glDesc
);
1481 /* Default values: From the surface */
1482 *format
= glDesc
->glFormat
;
1483 *internal
= srgb_mode
?glDesc
->glGammaInternal
:glDesc
->glInternal
;
1484 *type
= glDesc
->glType
;
1485 *convert
= NO_CONVERSION
;
1486 *target_bpp
= This
->bytesPerPixel
;
1488 /* Ok, now look if we have to do any conversion */
1489 switch(This
->resource
.format
) {
1494 /* Use conversion when the paletted texture extension is not available, or when it is available make sure it is used
1495 * for texturing as it won't work for calls like glDraw-/glReadPixels and further also use conversion in case of color keying.
1497 if(!GL_SUPPORT(EXT_PALETTED_TEXTURE
) || colorkey_active
|| (!use_texturing
&& GL_SUPPORT(EXT_PALETTED_TEXTURE
)) ) {
1499 *internal
= GL_RGBA
;
1500 *type
= GL_UNSIGNED_BYTE
;
1502 if(colorkey_active
) {
1503 *convert
= CONVERT_PALETTED_CK
;
1505 *convert
= CONVERT_PALETTED
;
1511 case WINED3DFMT_R3G3B2
:
1512 /* **********************
1513 GL_UNSIGNED_BYTE_3_3_2
1514 ********************** */
1515 if (colorkey_active
) {
1516 /* This texture format will never be used.. So do not care about color keying
1517 up until the point in time it will be needed :-) */
1518 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1522 case WINED3DFMT_R5G6B5
:
1523 if (colorkey_active
) {
1524 *convert
= CONVERT_CK_565
;
1526 *internal
= GL_RGBA
;
1527 *type
= GL_UNSIGNED_SHORT_5_5_5_1
;
1531 case WINED3DFMT_X1R5G5B5
:
1532 if (colorkey_active
) {
1533 *convert
= CONVERT_CK_5551
;
1535 *internal
= GL_RGBA
;
1536 *type
= GL_UNSIGNED_SHORT_1_5_5_5_REV
;
1540 case WINED3DFMT_R8G8B8
:
1541 if (colorkey_active
) {
1542 *convert
= CONVERT_CK_RGB24
;
1544 *internal
= GL_RGBA
;
1545 *type
= GL_UNSIGNED_INT_8_8_8_8
;
1550 case WINED3DFMT_X8R8G8B8
:
1551 if (colorkey_active
) {
1552 *convert
= CONVERT_RGB32_888
;
1554 *internal
= GL_RGBA
;
1555 *type
= GL_UNSIGNED_INT_8_8_8_8
;
1559 case WINED3DFMT_V8U8
:
1560 if(GL_SUPPORT(NV_TEXTURE_SHADER3
)) break;
1561 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP
)) {
1562 *format
= GL_DUDV_ATI
;
1563 *internal
= GL_DU8DV8_ATI
;
1565 /* No conversion - Just change the gl type */
1568 *convert
= CONVERT_V8U8
;
1570 *internal
= GL_RGB8
;
1571 *type
= GL_UNSIGNED_BYTE
;
1575 case WINED3DFMT_X8L8V8U8
:
1576 if(GL_SUPPORT(NV_TEXTURE_SHADER3
)) break;
1577 *convert
= CONVERT_X8L8V8U8
;
1579 *internal
= GL_RGBA8
;
1580 *type
= GL_UNSIGNED_BYTE
;
1582 /* Not supported by GL_ATI_envmap_bumpmap */
1585 case WINED3DFMT_Q8W8V8U8
:
1586 if(GL_SUPPORT(NV_TEXTURE_SHADER3
)) break;
1587 *convert
= CONVERT_Q8W8V8U8
;
1589 *internal
= GL_RGBA8
;
1590 *type
= GL_UNSIGNED_BYTE
;
1592 /* Not supported by GL_ATI_envmap_bumpmap */
1595 case WINED3DFMT_V16U16
:
1596 if(GL_SUPPORT(NV_TEXTURE_SHADER3
)) break;
1597 *convert
= CONVERT_V16U16
;
1599 *internal
= GL_RGB16
;
1602 /* What should I do here about GL_ATI_envmap_bumpmap?
1603 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1607 case WINED3DFMT_A4L4
:
1608 /* A4L4 exists as an internal gl format, but for some reason there is not
1609 * format+type combination to load it. Thus convert it to A8L8, then load it
1610 * with A4L4 internal, but A8L8 format+type
1612 *convert
= CONVERT_A4L4
;
1613 *format
= GL_LUMINANCE_ALPHA
;
1614 *internal
= GL_LUMINANCE4_ALPHA4
;
1615 *type
= GL_UNSIGNED_BYTE
;
1619 case WINED3DFMT_R32F
:
1620 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1621 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1622 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1625 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1627 *convert
= CONVERT_R32F
;
1629 *internal
= GL_RGB32F_ARB
;
1634 case WINED3DFMT_R16F
:
1635 /* Simmilar to R32F */
1636 *convert
= CONVERT_R16F
;
1638 *internal
= GL_RGB16F_ARB
;
1639 *type
= GL_HALF_FLOAT_ARB
;
1650 HRESULT
d3dfmt_convert_surface(BYTE
*src
, BYTE
*dst
, UINT pitch
, UINT width
, UINT height
, UINT outpitch
, CONVERT_TYPES convert
, IWineD3DSurfaceImpl
*surf
) {
1651 BYTE
*source
, *dest
;
1652 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src
, dst
, pitch
, height
, outpitch
, convert
, surf
);
1657 memcpy(dst
, src
, pitch
* height
);
1660 case CONVERT_PALETTED
:
1661 case CONVERT_PALETTED_CK
:
1663 IWineD3DPaletteImpl
* pal
= surf
->palette
;
1669 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1673 /* Still no palette? Use the device's palette */
1674 /* Get the surface's palette */
1675 for (i
= 0; i
< 256; i
++) {
1676 IWineD3DDeviceImpl
*device
= surf
->resource
.wineD3DDevice
;
1678 table
[i
][0] = device
->palettes
[device
->currentPalette
][i
].peRed
;
1679 table
[i
][1] = device
->palettes
[device
->currentPalette
][i
].peGreen
;
1680 table
[i
][2] = device
->palettes
[device
->currentPalette
][i
].peBlue
;
1681 if ((convert
== CONVERT_PALETTED_CK
) &&
1682 (i
>= surf
->SrcBltCKey
.dwColorSpaceLowValue
) &&
1683 (i
<= surf
->SrcBltCKey
.dwColorSpaceHighValue
)) {
1684 /* We should maybe here put a more 'neutral' color than the standard bright purple
1685 one often used by application to prevent the nice purple borders when bi-linear
1693 TRACE("Using surface palette %p\n", pal
);
1694 /* Get the surface's palette */
1695 for (i
= 0; i
< 256; i
++) {
1696 table
[i
][0] = pal
->palents
[i
].peRed
;
1697 table
[i
][1] = pal
->palents
[i
].peGreen
;
1698 table
[i
][2] = pal
->palents
[i
].peBlue
;
1699 if ((convert
== CONVERT_PALETTED_CK
) &&
1700 (i
>= surf
->SrcBltCKey
.dwColorSpaceLowValue
) &&
1701 (i
<= surf
->SrcBltCKey
.dwColorSpaceHighValue
)) {
1702 /* We should maybe here put a more 'neutral' color than the standard bright purple
1703 one often used by application to prevent the nice purple borders when bi-linear
1706 } else if(pal
->Flags
& WINEDDPCAPS_ALPHA
) {
1707 table
[i
][3] = pal
->palents
[i
].peFlags
;
1714 for (y
= 0; y
< height
; y
++)
1716 source
= src
+ pitch
* y
;
1717 dest
= dst
+ outpitch
* y
;
1718 /* This is an 1 bpp format, using the width here is fine */
1719 for (x
= 0; x
< width
; x
++) {
1720 BYTE color
= *source
++;
1721 *dest
++ = table
[color
][0];
1722 *dest
++ = table
[color
][1];
1723 *dest
++ = table
[color
][2];
1724 *dest
++ = table
[color
][3];
1730 case CONVERT_CK_565
:
1732 /* Converting the 565 format in 5551 packed to emulate color-keying.
1734 Note : in all these conversion, it would be best to average the averaging
1735 pixels to get the color of the pixel that will be color-keyed to
1736 prevent 'color bleeding'. This will be done later on if ever it is
1739 Note2: Nvidia documents say that their driver does not support alpha + color keying
1740 on the same surface and disables color keying in such a case
1746 TRACE("Color keyed 565\n");
1748 for (y
= 0; y
< height
; y
++) {
1749 Source
= (WORD
*) (src
+ y
* pitch
);
1750 Dest
= (WORD
*) (dst
+ y
* outpitch
);
1751 for (x
= 0; x
< width
; x
++ ) {
1752 WORD color
= *Source
++;
1753 *Dest
= ((color
& 0xFFC0) | ((color
& 0x1F) << 1));
1754 if ((color
< surf
->SrcBltCKey
.dwColorSpaceLowValue
) ||
1755 (color
> surf
->SrcBltCKey
.dwColorSpaceHighValue
)) {
1764 case CONVERT_CK_5551
:
1766 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1770 TRACE("Color keyed 5551\n");
1771 for (y
= 0; y
< height
; y
++) {
1772 Source
= (WORD
*) (src
+ y
* pitch
);
1773 Dest
= (WORD
*) (dst
+ y
* outpitch
);
1774 for (x
= 0; x
< width
; x
++ ) {
1775 WORD color
= *Source
++;
1777 if ((color
< surf
->SrcBltCKey
.dwColorSpaceLowValue
) ||
1778 (color
> surf
->SrcBltCKey
.dwColorSpaceHighValue
)) {
1782 *Dest
&= ~(1 << 15);
1794 unsigned char *Dest
;
1795 for(y
= 0; y
< height
; y
++) {
1796 Source
= (short *) (src
+ y
* pitch
);
1797 Dest
= (unsigned char *) (dst
+ y
* outpitch
);
1798 for (x
= 0; x
< width
; x
++ ) {
1799 long color
= (*Source
++);
1800 /* B */ Dest
[0] = 0xff;
1801 /* G */ Dest
[1] = (color
>> 8) + 128; /* V */
1802 /* R */ Dest
[2] = (color
) + 128; /* U */
1809 case CONVERT_Q8W8V8U8
:
1813 unsigned char *Dest
;
1814 for(y
= 0; y
< height
; y
++) {
1815 Source
= (DWORD
*) (src
+ y
* pitch
);
1816 Dest
= (unsigned char *) (dst
+ y
* outpitch
);
1817 for (x
= 0; x
< width
; x
++ ) {
1818 long color
= (*Source
++);
1819 /* B */ Dest
[0] = ((color
>> 16) & 0xff) + 128; /* W */
1820 /* G */ Dest
[1] = ((color
>> 8 ) & 0xff) + 128; /* V */
1821 /* R */ Dest
[2] = (color
& 0xff) + 128; /* U */
1822 /* A */ Dest
[3] = ((color
>> 24) & 0xff) + 128; /* Q */
1832 unsigned char *Source
;
1833 unsigned char *Dest
;
1834 for(y
= 0; y
< height
; y
++) {
1835 Source
= (unsigned char *) (src
+ y
* pitch
);
1836 Dest
= (unsigned char *) (dst
+ y
* outpitch
);
1837 for (x
= 0; x
< width
; x
++ ) {
1838 unsigned char color
= (*Source
++);
1839 /* A */ Dest
[1] = (color
& 0xf0) << 0;
1840 /* L */ Dest
[0] = (color
& 0x0f) << 4;
1852 for(y
= 0; y
< height
; y
++) {
1853 Source
= (float *) (src
+ y
* pitch
);
1854 Dest
= (float *) (dst
+ y
* outpitch
);
1855 for (x
= 0; x
< width
; x
++ ) {
1856 float color
= (*Source
++);
1872 for(y
= 0; y
< height
; y
++) {
1873 Source
= (WORD
*) (src
+ y
* pitch
);
1874 Dest
= (WORD
*) (dst
+ y
* outpitch
);
1875 for (x
= 0; x
< width
; x
++ ) {
1876 WORD color
= (*Source
++);
1886 ERR("Unsupported conversation type %d\n", convert
);
1891 /* This function is used in case of 8bit paletted textures to upload the palette.
1892 For now it only supports GL_EXT_paletted_texture extension but support for other
1893 extensions like ARB_fragment_program and ATI_fragment_shaders will be added as well.
1895 static void d3dfmt_p8_upload_palette(IWineD3DSurface
*iface
, CONVERT_TYPES convert
) {
1896 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
1897 IWineD3DPaletteImpl
* pal
= This
->palette
;
1902 /* Still no palette? Use the device's palette */
1903 /* Get the surface's palette */
1904 for (i
= 0; i
< 256; i
++) {
1905 IWineD3DDeviceImpl
*device
= This
->resource
.wineD3DDevice
;
1907 table
[i
][0] = device
->palettes
[device
->currentPalette
][i
].peRed
;
1908 table
[i
][1] = device
->palettes
[device
->currentPalette
][i
].peGreen
;
1909 table
[i
][2] = device
->palettes
[device
->currentPalette
][i
].peBlue
;
1910 if ((convert
== CONVERT_PALETTED_CK
) &&
1911 (i
>= This
->SrcBltCKey
.dwColorSpaceLowValue
) &&
1912 (i
<= This
->SrcBltCKey
.dwColorSpaceHighValue
)) {
1913 /* We should maybe here put a more 'neutral' color than the standard bright purple
1914 one often used by application to prevent the nice purple borders when bi-linear
1922 TRACE("Using surface palette %p\n", pal
);
1923 /* Get the surface's palette */
1924 for (i
= 0; i
< 256; i
++) {
1925 table
[i
][0] = pal
->palents
[i
].peRed
;
1926 table
[i
][1] = pal
->palents
[i
].peGreen
;
1927 table
[i
][2] = pal
->palents
[i
].peBlue
;
1928 if ((convert
== CONVERT_PALETTED_CK
) &&
1929 (i
>= This
->SrcBltCKey
.dwColorSpaceLowValue
) &&
1930 (i
<= This
->SrcBltCKey
.dwColorSpaceHighValue
)) {
1931 /* We should maybe here put a more 'neutral' color than the standard bright purple
1932 one often used by application to prevent the nice purple borders when bi-linear
1935 } else if(pal
->Flags
& WINEDDPCAPS_ALPHA
) {
1936 table
[i
][3] = pal
->palents
[i
].peFlags
;
1942 GL_EXTCALL(glColorTableEXT(GL_TEXTURE_2D
,GL_RGBA
,256,GL_RGBA
,GL_UNSIGNED_BYTE
, table
));
1945 static BOOL
palette9_changed(IWineD3DSurfaceImpl
*This
) {
1946 IWineD3DDeviceImpl
*device
= This
->resource
.wineD3DDevice
;
1948 if(This
->palette
|| (This
->resource
.format
!= WINED3DFMT_P8
&& This
->resource
.format
!= WINED3DFMT_A8P8
)) {
1949 /* If a ddraw-style palette is attached assume no d3d9 palette change.
1950 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
1955 if(This
->palette9
) {
1956 if(memcmp(This
->palette9
, &device
->palettes
[device
->currentPalette
], sizeof(PALETTEENTRY
) * 256) == 0) {
1960 This
->palette9
= HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY
) * 256);
1962 memcpy(This
->palette9
, &device
->palettes
[device
->currentPalette
], sizeof(PALETTEENTRY
) * 256);
1966 static inline void clear_unused_channels(IWineD3DSurfaceImpl
*This
) {
1967 GLboolean oldwrite
[4];
1969 /* Some formats have only some color channels, and the others are 1.0.
1970 * since our rendering renders to all channels, and those pixel formats
1971 * are emulated by using a full texture with the other channels set to 1.0
1972 * manually, clear the unused channels.
1974 * This could be done with hacking colorwriteenable to mask the colors,
1975 * but before drawing the buffer would have to be cleared too, so there's
1978 switch(This
->resource
.format
) {
1979 case WINED3DFMT_R16F
:
1980 case WINED3DFMT_R32F
:
1981 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
1982 /* Do not activate a context, the correct drawable is active already
1983 * though just the read buffer is set, make sure to have the correct draw
1986 glDrawBuffer(This
->resource
.wineD3DDevice
->offscreenBuffer
);
1987 glDisable(GL_SCISSOR_TEST
);
1988 glGetBooleanv(GL_COLOR_WRITEMASK
, oldwrite
);
1989 glColorMask(GL_FALSE
, GL_TRUE
, GL_TRUE
, GL_TRUE
);
1990 glClearColor(0.0, 1.0, 1.0, 1.0);
1991 glClear(GL_COLOR_BUFFER_BIT
);
1992 glColorMask(oldwrite
[0], oldwrite
[1], oldwrite
[2], oldwrite
[3]);
1993 if(!This
->resource
.wineD3DDevice
->render_offscreen
) glDrawBuffer(GL_BACK
);
1994 checkGLcall("Unused channel clear\n");
2001 static HRESULT WINAPI
IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface
*iface
, BOOL srgb_mode
) {
2002 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
2003 IWineD3DDeviceImpl
*device
= This
->resource
.wineD3DDevice
;
2004 GLenum format
, internal
, type
;
2005 CONVERT_TYPES convert
;
2007 int width
, pitch
, outpitch
;
2010 if (!(This
->Flags
& SFLAG_INTEXTURE
)) {
2011 TRACE("Reloading because surface is dirty\n");
2012 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2013 ((This
->Flags
& SFLAG_GLCKEY
) && (!(This
->CKeyFlags
& WINEDDSD_CKSRCBLT
))) ||
2014 /* Reload: vice versa OR */
2015 ((!(This
->Flags
& SFLAG_GLCKEY
)) && (This
->CKeyFlags
& WINEDDSD_CKSRCBLT
)) ||
2016 /* Also reload: Color key is active AND the color key has changed */
2017 ((This
->CKeyFlags
& WINEDDSD_CKSRCBLT
) && (
2018 (This
->glCKey
.dwColorSpaceLowValue
!= This
->SrcBltCKey
.dwColorSpaceLowValue
) ||
2019 (This
->glCKey
.dwColorSpaceHighValue
!= This
->SrcBltCKey
.dwColorSpaceHighValue
)))) {
2020 TRACE("Reloading because of color keying\n");
2021 } else if(palette9_changed(This
)) {
2022 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2024 TRACE("surface is already in texture\n");
2028 This
->Flags
|= SFLAG_INTEXTURE
;
2030 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2031 * These resources are not bound by device size or format restrictions. Because of this,
2032 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2033 * However, these resources can always be created, locked, and copied.
2035 if (This
->resource
.pool
== WINED3DPOOL_SCRATCH
)
2037 FIXME("(%p) Operation not supported for scratch textures\n",This
);
2038 return WINED3DERR_INVALIDCALL
;
2041 d3dfmt_get_conv(This
, TRUE
/* We need color keying */, TRUE
/* We will use textures */, &format
, &internal
, &type
, &convert
, &bpp
, srgb_mode
);
2043 if (This
->Flags
& SFLAG_INDRAWABLE
) {
2044 if (This
->resource
.format
== WINED3DFMT_P8
|| This
->resource
.format
== WINED3DFMT_A8P8
||
2045 This
->resource
.format
== WINED3DFMT_DXT1
|| This
->resource
.format
== WINED3DFMT_DXT2
||
2046 This
->resource
.format
== WINED3DFMT_DXT3
|| This
->resource
.format
== WINED3DFMT_DXT4
||
2047 This
->resource
.format
== WINED3DFMT_DXT5
)
2048 FIXME("Format %d not supported\n", This
->resource
.format
);
2053 glGetIntegerv(GL_READ_BUFFER
, &prevRead
);
2054 vcheckGLcall("glGetIntegerv");
2055 glReadBuffer(This
->resource
.wineD3DDevice
->offscreenBuffer
);
2056 vcheckGLcall("glReadBuffer");
2058 if(!(This
->Flags
& SFLAG_ALLOCATED
)) {
2059 surface_allocate_surface(This
, internal
, This
->pow2Width
,
2060 This
->pow2Height
, format
, type
);
2063 clear_unused_channels(This
);
2065 glCopyTexSubImage2D(This
->glDescription
.target
,
2066 This
->glDescription
.level
,
2068 This
->currentDesc
.Width
,
2069 This
->currentDesc
.Height
);
2070 checkGLcall("glCopyTexSubImage2D");
2072 glReadBuffer(prevRead
);
2073 vcheckGLcall("glReadBuffer");
2077 TRACE("Updated target %d\n", This
->glDescription
.target
);
2081 /* The only place where LoadTexture() might get called when isInDraw=1
2082 * is ActivateContext where lastActiveRenderTarget is preloaded.
2084 if(iface
== device
->lastActiveRenderTarget
&& device
->isInDraw
)
2085 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
2087 /* Otherwise: System memory copy must be most up to date */
2089 if(This
->CKeyFlags
& WINEDDSD_CKSRCBLT
) {
2090 This
->Flags
|= SFLAG_GLCKEY
;
2091 This
->glCKey
= This
->SrcBltCKey
;
2093 else This
->Flags
&= ~SFLAG_GLCKEY
;
2095 /* The width is in 'length' not in bytes */
2096 width
= This
->currentDesc
.Width
;
2097 pitch
= IWineD3DSurface_GetPitch(iface
);
2099 if((convert
!= NO_CONVERSION
) && This
->resource
.allocatedMemory
) {
2100 int height
= This
->currentDesc
.Height
;
2102 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
2103 outpitch
= width
* bpp
;
2104 outpitch
= (outpitch
+ device
->surface_alignment
- 1) & ~(device
->surface_alignment
- 1);
2106 mem
= HeapAlloc(GetProcessHeap(), 0, outpitch
* height
);
2108 ERR("Out of memory %d, %d!\n", outpitch
, height
);
2109 return WINED3DERR_OUTOFVIDEOMEMORY
;
2111 d3dfmt_convert_surface(This
->resource
.allocatedMemory
, mem
, pitch
, width
, height
, outpitch
, convert
, This
);
2113 This
->Flags
|= SFLAG_CONVERTED
;
2114 } else if (This
->resource
.format
== WINED3DFMT_P8
&& GL_SUPPORT(EXT_PALETTED_TEXTURE
)) {
2115 d3dfmt_p8_upload_palette(iface
, convert
);
2116 This
->Flags
&= ~SFLAG_CONVERTED
;
2117 mem
= This
->resource
.allocatedMemory
;
2119 This
->Flags
&= ~SFLAG_CONVERTED
;
2120 mem
= This
->resource
.allocatedMemory
;
2123 /* Make sure the correct pitch is used */
2124 glPixelStorei(GL_UNPACK_ROW_LENGTH
, width
);
2126 if ((This
->Flags
& SFLAG_NONPOW2
) && !(This
->Flags
& SFLAG_OVERSIZE
)) {
2127 TRACE("non power of two support\n");
2128 if(!(This
->Flags
& SFLAG_ALLOCATED
)) {
2129 surface_allocate_surface(This
, internal
, This
->pow2Width
, This
->pow2Height
, format
, type
);
2132 surface_upload_data(This
, internal
, This
->currentDesc
.Width
, This
->currentDesc
.Height
, format
, type
, mem
);
2135 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
2136 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
2138 if(!(This
->Flags
& SFLAG_ALLOCATED
)) {
2139 surface_allocate_surface(This
, internal
, This
->glRect
.right
- This
->glRect
.left
, This
->glRect
.bottom
- This
->glRect
.top
, format
, type
);
2142 surface_upload_data(This
, internal
, This
->glRect
.right
- This
->glRect
.left
, This
->glRect
.bottom
- This
->glRect
.top
, format
, type
, mem
);
2146 /* Restore the default pitch */
2147 glPixelStorei(GL_UNPACK_ROW_LENGTH
, 0);
2149 if (mem
!= This
->resource
.allocatedMemory
)
2150 HeapFree(GetProcessHeap(), 0, mem
);
2154 static unsigned int gen
= 0;
2157 if ((gen
% 10) == 0) {
2158 snprintf(buffer
, sizeof(buffer
), "/tmp/surface%p_type%u_level%u_%u.ppm", This
, This
->glDescription
.target
, This
->glDescription
.level
, gen
);
2159 IWineD3DSurfaceImpl_SaveSnapshot(iface
, buffer
);
2162 * debugging crash code
2171 if (!(This
->Flags
& SFLAG_DONOTFREE
)) {
2172 HeapFree(GetProcessHeap(), 0, This
->resource
.allocatedMemory
);
2173 This
->resource
.allocatedMemory
= NULL
;
2174 This
->Flags
&= ~SFLAG_INSYSMEM
;
2182 HRESULT WINAPI
IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface
*iface
, const char* filename
) {
2185 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
2186 char *allocatedMemory
;
2188 IWineD3DSwapChain
*swapChain
= NULL
;
2190 GLuint tmpTexture
= 0;
2193 Textures may not be stored in ->allocatedgMemory and a GlTexture
2194 so we should lock the surface before saving a snapshot, or at least check that
2196 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2197 by calling GetTexImage and in compressed form by calling
2198 GetCompressedTexImageARB. Queried compressed images can be saved and
2199 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2200 texture images do not need to be processed by the GL and should
2201 significantly improve texture loading performance relative to uncompressed
2204 /* Setup the width and height to be the internal texture width and height. */
2205 width
= This
->pow2Width
;
2206 height
= This
->pow2Height
;
2207 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2208 IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DSwapChain
, (void **)&swapChain
);
2210 if (This
->Flags
& SFLAG_INDRAWABLE
&& !(This
->Flags
& SFLAG_INTEXTURE
)) {
2211 /* if were not a real texture then read the back buffer into a real texture */
2212 /* we don't want to interfere with the back buffer so read the data into a temporary
2213 * texture and then save the data out of the temporary texture
2217 TRACE("(%p) Reading render target into texture\n", This
);
2218 glEnable(GL_TEXTURE_2D
);
2220 glGenTextures(1, &tmpTexture
);
2221 glBindTexture(GL_TEXTURE_2D
, tmpTexture
);
2223 glTexImage2D(GL_TEXTURE_2D
,
2230 GL_UNSIGNED_INT_8_8_8_8_REV
,
2233 glGetIntegerv(GL_READ_BUFFER
, &prevRead
);
2234 vcheckGLcall("glGetIntegerv");
2235 glReadBuffer(swapChain
? GL_BACK
: This
->resource
.wineD3DDevice
->offscreenBuffer
);
2236 vcheckGLcall("glReadBuffer");
2237 glCopyTexImage2D(GL_TEXTURE_2D
,
2246 checkGLcall("glCopyTexImage2D");
2247 glReadBuffer(prevRead
);
2250 } else { /* bind the real texture, and make sure it up to date */
2251 IWineD3DSurface_PreLoad(iface
);
2253 allocatedMemory
= HeapAlloc(GetProcessHeap(), 0, width
* height
* 4);
2255 FIXME("Saving texture level %d width %d height %d\n", This
->glDescription
.level
, width
, height
);
2256 glGetTexImage(GL_TEXTURE_2D
,
2257 This
->glDescription
.level
,
2259 GL_UNSIGNED_INT_8_8_8_8_REV
,
2261 checkGLcall("glTexImage2D");
2263 glBindTexture(GL_TEXTURE_2D
, 0);
2264 glDeleteTextures(1, &tmpTexture
);
2268 f
= fopen(filename
, "w+");
2270 ERR("opening of %s failed with: %s\n", filename
, strerror(errno
));
2271 return WINED3DERR_INVALIDCALL
;
2273 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2274 TRACE("(%p) opened %s with format %s\n", This
, filename
, debug_d3dformat(This
->resource
.format
));
2289 fwrite(&width
,2,1,f
);
2291 fwrite(&height
,2,1,f
);
2296 /* 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 */
2298 textureRow
= allocatedMemory
+ (width
* (height
- 1) *4);
2300 textureRow
= allocatedMemory
;
2301 for (y
= 0 ; y
< height
; y
++) {
2302 for (i
= 0; i
< width
; i
++) {
2303 color
= *((DWORD
*)textureRow
);
2304 fputc((color
>> 16) & 0xFF, f
); /* B */
2305 fputc((color
>> 8) & 0xFF, f
); /* G */
2306 fputc((color
>> 0) & 0xFF, f
); /* R */
2307 fputc((color
>> 24) & 0xFF, f
); /* A */
2310 /* take two rows of the pointer to the texture memory */
2312 (textureRow
-= width
<< 3);
2315 TRACE("Closing file\n");
2319 IWineD3DSwapChain_Release(swapChain
);
2321 HeapFree(GetProcessHeap(), 0, allocatedMemory
);
2326 * Slightly inefficient way to handle multiple dirty rects but it works :)
2328 extern HRESULT WINAPI
IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface
*iface
, CONST RECT
* pDirtyRect
) {
2329 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
2330 IWineD3DBaseTexture
*baseTexture
= NULL
;
2331 if (!(This
->Flags
& SFLAG_INSYSMEM
) && (This
->Flags
& SFLAG_INTEXTURE
))
2332 surface_download_data(This
);
2334 This
->Flags
&= ~(SFLAG_INTEXTURE
| SFLAG_INDRAWABLE
);
2335 if (NULL
!= pDirtyRect
) {
2336 This
->dirtyRect
.left
= min(This
->dirtyRect
.left
, pDirtyRect
->left
);
2337 This
->dirtyRect
.top
= min(This
->dirtyRect
.top
, pDirtyRect
->top
);
2338 This
->dirtyRect
.right
= max(This
->dirtyRect
.right
, pDirtyRect
->right
);
2339 This
->dirtyRect
.bottom
= max(This
->dirtyRect
.bottom
, pDirtyRect
->bottom
);
2341 This
->dirtyRect
.left
= 0;
2342 This
->dirtyRect
.top
= 0;
2343 This
->dirtyRect
.right
= This
->currentDesc
.Width
;
2344 This
->dirtyRect
.bottom
= This
->currentDesc
.Height
;
2346 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This
, This
->dirtyRect
.left
,
2347 This
->dirtyRect
.top
, This
->dirtyRect
.right
, This
->dirtyRect
.bottom
);
2348 /* if the container is a basetexture then mark it dirty. */
2349 if (IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DBaseTexture
, (void **)&baseTexture
) == WINED3D_OK
) {
2350 TRACE("Passing to container\n");
2351 IWineD3DBaseTexture_SetDirty(baseTexture
, TRUE
);
2352 IWineD3DBaseTexture_Release(baseTexture
);
2357 HRESULT WINAPI
IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface
*iface
, IWineD3DBase
*container
) {
2358 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
2360 TRACE("This %p, container %p\n", This
, container
);
2362 /* We can't keep a reference to the container, since the container already keeps a reference to us. */
2364 TRACE("Setting container to %p from %p\n", container
, This
->container
);
2365 This
->container
= container
;
2370 HRESULT WINAPI
IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface
*iface
, WINED3DFORMAT format
) {
2371 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
2372 const GlPixelFormatDesc
*glDesc
;
2373 const StaticPixelFormatDesc
*formatEntry
= getFormatDescEntry(format
, &GLINFO_LOCATION
, &glDesc
);
2375 if (This
->resource
.format
!= WINED3DFMT_UNKNOWN
) {
2376 FIXME("(%p) : The format of the surface must be WINED3DFORMAT_UNKNOWN\n", This
);
2377 return WINED3DERR_INVALIDCALL
;
2380 TRACE("(%p) : Setting texture format to (%d,%s)\n", This
, format
, debug_d3dformat(format
));
2381 if (format
== WINED3DFMT_UNKNOWN
) {
2382 This
->resource
.size
= 0;
2383 } else if (format
== WINED3DFMT_DXT1
) {
2384 /* DXT1 is half byte per pixel */
2385 This
->resource
.size
= ((max(This
->pow2Width
, 4) * formatEntry
->bpp
) * max(This
->pow2Height
, 4)) >> 1;
2387 } else if (format
== WINED3DFMT_DXT2
|| format
== WINED3DFMT_DXT3
||
2388 format
== WINED3DFMT_DXT4
|| format
== WINED3DFMT_DXT5
) {
2389 This
->resource
.size
= ((max(This
->pow2Width
, 4) * formatEntry
->bpp
) * max(This
->pow2Height
, 4));
2391 unsigned char alignment
= This
->resource
.wineD3DDevice
->surface_alignment
;
2392 This
->resource
.size
= ((This
->pow2Width
* formatEntry
->bpp
) + alignment
- 1) & ~(alignment
- 1);
2393 This
->resource
.size
*= This
->pow2Height
;
2397 /* Setup some glformat defaults */
2398 This
->glDescription
.glFormat
= glDesc
->glFormat
;
2399 This
->glDescription
.glFormatInternal
= glDesc
->glInternal
;
2400 This
->glDescription
.glType
= glDesc
->glType
;
2402 if (format
!= WINED3DFMT_UNKNOWN
) {
2403 This
->bytesPerPixel
= formatEntry
->bpp
;
2405 This
->bytesPerPixel
= 0;
2408 This
->Flags
|= (WINED3DFMT_D16_LOCKABLE
== format
) ? SFLAG_LOCKABLE
: 0;
2409 This
->Flags
&= ~SFLAG_ALLOCATED
;
2411 This
->resource
.format
= format
;
2413 TRACE("(%p) : Size %d, bytesPerPixel %d, glFormat %d, glFotmatInternal %d, glType %d\n", This
, This
->resource
.size
, This
->bytesPerPixel
, This
->glDescription
.glFormat
, This
->glDescription
.glFormatInternal
, This
->glDescription
.glType
);
2418 HRESULT WINAPI
IWineD3DSurfaceImpl_SetMem(IWineD3DSurface
*iface
, void *Mem
) {
2419 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
2421 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer */
2422 if(This
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
) {
2423 ERR("Not supported on render targets\n");
2424 return WINED3DERR_INVALIDCALL
;
2427 if(This
->Flags
& (SFLAG_LOCKED
| SFLAG_DCINUSE
)) {
2428 WARN("Surface is locked or the HDC is in use\n");
2429 return WINED3DERR_INVALIDCALL
;
2432 if(Mem
&& Mem
!= This
->resource
.allocatedMemory
) {
2433 void *release
= NULL
;
2435 /* Do I have to copy the old surface content? */
2436 if(This
->Flags
& SFLAG_DIBSECTION
) {
2437 /* Release the DC. No need to hold the critical section for the update
2438 * Thread because this thread runs only on front buffers, but this method
2439 * fails for render targets in the check above.
2441 SelectObject(This
->hDC
, This
->dib
.holdbitmap
);
2442 DeleteDC(This
->hDC
);
2443 /* Release the DIB section */
2444 DeleteObject(This
->dib
.DIBsection
);
2445 This
->dib
.bitmap_data
= NULL
;
2446 This
->resource
.allocatedMemory
= NULL
;
2448 This
->Flags
&= ~SFLAG_DIBSECTION
;
2449 } else if(!(This
->Flags
& SFLAG_USERPTR
)) {
2450 release
= This
->resource
.allocatedMemory
;
2452 This
->resource
.allocatedMemory
= Mem
;
2453 This
->Flags
|= SFLAG_USERPTR
| SFLAG_INSYSMEM
;
2455 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2456 This
->Flags
&= ~(SFLAG_INDRAWABLE
| SFLAG_INTEXTURE
);
2458 /* For client textures opengl has to be notified */
2459 if(This
->Flags
& SFLAG_CLIENT
) {
2460 This
->Flags
&= ~SFLAG_ALLOCATED
;
2461 IWineD3DSurface_PreLoad(iface
);
2462 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2465 /* Now free the old memory if any */
2466 HeapFree(GetProcessHeap(), 0, release
);
2467 } else if(This
->Flags
& SFLAG_USERPTR
) {
2468 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2469 This
->resource
.allocatedMemory
= NULL
;
2470 This
->Flags
&= ~SFLAG_USERPTR
;
2472 if(This
->Flags
& SFLAG_CLIENT
) {
2473 This
->Flags
&= ~SFLAG_ALLOCATED
;
2474 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2475 IWineD3DSurface_PreLoad(iface
);
2481 static HRESULT WINAPI
IWineD3DSurfaceImpl_Flip(IWineD3DSurface
*iface
, IWineD3DSurface
*override
, DWORD Flags
) {
2482 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
2483 IWineD3DSwapChainImpl
*swapchain
= NULL
;
2485 TRACE("(%p)->(%p,%x)\n", This
, override
, Flags
);
2487 /* Flipping is only supported on RenderTargets */
2488 if( !(This
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
) ) return WINEDDERR_NOTFLIPPABLE
;
2491 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2492 * FIXME("(%p) Target override is not supported by now\n", This);
2493 * Additionally, it isn't really possible to support triple-buffering
2494 * properly on opengl at all
2498 IWineD3DSurface_GetContainer(iface
, &IID_IWineD3DSwapChain
, (void **) &swapchain
);
2500 ERR("Flipped surface is not on a swapchain\n");
2501 return WINEDDERR_NOTFLIPPABLE
;
2504 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2505 * and only d3d8 and d3d9 apps specify the presentation interval
2507 if((Flags
& (WINEDDFLIP_NOVSYNC
| WINEDDFLIP_INTERVAL2
| WINEDDFLIP_INTERVAL3
| WINEDDFLIP_INTERVAL4
)) == 0) {
2508 /* Most common case first to avoid wasting time on all the other cases */
2509 swapchain
->presentParms
.PresentationInterval
= WINED3DPRESENT_INTERVAL_ONE
;
2510 } else if(Flags
& WINEDDFLIP_NOVSYNC
) {
2511 swapchain
->presentParms
.PresentationInterval
= WINED3DPRESENT_INTERVAL_IMMEDIATE
;
2512 } else if(Flags
& WINEDDFLIP_INTERVAL2
) {
2513 swapchain
->presentParms
.PresentationInterval
= WINED3DPRESENT_INTERVAL_TWO
;
2514 } else if(Flags
& WINEDDFLIP_INTERVAL3
) {
2515 swapchain
->presentParms
.PresentationInterval
= WINED3DPRESENT_INTERVAL_THREE
;
2517 swapchain
->presentParms
.PresentationInterval
= WINED3DPRESENT_INTERVAL_FOUR
;
2520 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2521 hr
= IWineD3DSwapChain_Present((IWineD3DSwapChain
*) swapchain
, NULL
, NULL
, 0, NULL
, 0);
2522 IWineD3DSwapChain_Release((IWineD3DSwapChain
*) swapchain
);
2526 /* Does a direct frame buffer -> texture copy. Stretching is done
2527 * with single pixel copy calls
2529 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl
*This
, IWineD3DSurface
*SrcSurface
, IWineD3DSwapChainImpl
*swapchain
, WINED3DRECT
*srect
, WINED3DRECT
*drect
, BOOL upsidedown
, WINED3DTEXTUREFILTERTYPE Filter
) {
2530 IWineD3DDeviceImpl
*myDevice
= This
->resource
.wineD3DDevice
;
2533 IWineD3DSurfaceImpl
*Src
= (IWineD3DSurfaceImpl
*) SrcSurface
;
2536 ActivateContext(myDevice
, SrcSurface
, CTXUSAGE_BLIT
);
2538 IWineD3DSurface_PreLoad((IWineD3DSurface
*) This
);
2540 /* Bind the target texture */
2541 glBindTexture(GL_TEXTURE_2D
, This
->glDescription
.textureName
);
2542 checkGLcall("glBindTexture");
2544 glReadBuffer(myDevice
->offscreenBuffer
);
2546 GLenum buffer
= surface_get_gl_buffer(SrcSurface
, (IWineD3DSwapChain
*)swapchain
);
2547 glReadBuffer(buffer
);
2549 checkGLcall("glReadBuffer");
2551 xrel
= (float) (srect
->x2
- srect
->x1
) / (float) (drect
->x2
- drect
->x1
);
2552 yrel
= (float) (srect
->y2
- srect
->y1
) / (float) (drect
->y2
- drect
->y1
);
2554 if( (xrel
- 1.0 < -eps
) || (xrel
- 1.0 > eps
)) {
2555 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2557 if(Filter
!= WINED3DTEXF_NONE
&& Filter
!= WINED3DTEXF_POINT
) {
2558 ERR("Texture filtering not supported in direct blit\n");
2560 } else if((Filter
!= WINED3DTEXF_NONE
&& Filter
!= WINED3DTEXF_POINT
) && ((yrel
- 1.0 < -eps
) || (yrel
- 1.0 > eps
))) {
2561 ERR("Texture filtering not supported in direct blit\n");
2565 !((xrel
- 1.0 < -eps
) || (xrel
- 1.0 > eps
)) &&
2566 !((yrel
- 1.0 < -eps
) || (yrel
- 1.0 > eps
))) {
2567 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2569 glCopyTexSubImage2D(This
->glDescription
.target
,
2570 This
->glDescription
.level
,
2571 drect
->x1
, drect
->y1
, /* xoffset, yoffset */
2572 srect
->x1
, Src
->currentDesc
.Height
- srect
->y2
,
2573 drect
->x2
- drect
->x1
, drect
->y2
- drect
->y1
);
2575 UINT yoffset
= Src
->currentDesc
.Height
- srect
->y1
+ drect
->y1
- 1;
2576 /* I have to process this row by row to swap the image,
2577 * otherwise it would be upside down, so stretching in y direction
2578 * doesn't cost extra time
2580 * However, stretching in x direction can be avoided if not necessary
2582 for(row
= drect
->y1
; row
< drect
->y2
; row
++) {
2583 if( (xrel
- 1.0 < -eps
) || (xrel
- 1.0 > eps
)) {
2584 /* Well, that stuff works, but it's very slow.
2585 * find a better way instead
2589 for(col
= drect
->x1
; col
< drect
->x2
; col
++) {
2590 glCopyTexSubImage2D(This
->glDescription
.target
,
2591 This
->glDescription
.level
,
2592 drect
->x1
+ col
, row
, /* xoffset, yoffset */
2593 srect
->x1
+ col
* xrel
, yoffset
- (int) (row
* yrel
),
2597 glCopyTexSubImage2D(This
->glDescription
.target
,
2598 This
->glDescription
.level
,
2599 drect
->x1
, row
, /* xoffset, yoffset */
2600 srect
->x1
, yoffset
- (int) (row
* yrel
),
2601 drect
->x2
-drect
->x1
, 1);
2606 vcheckGLcall("glCopyTexSubImage2D");
2610 /* Uses the hardware to stretch and flip the image */
2611 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl
*This
, IWineD3DSurface
*SrcSurface
, IWineD3DSwapChainImpl
*swapchain
, WINED3DRECT
*srect
, WINED3DRECT
*drect
, BOOL upsidedown
, WINED3DTEXTUREFILTERTYPE Filter
) {
2612 GLuint src
, backup
= 0;
2613 IWineD3DDeviceImpl
*myDevice
= This
->resource
.wineD3DDevice
;
2614 IWineD3DSurfaceImpl
*Src
= (IWineD3DSurfaceImpl
*) SrcSurface
;
2615 float left
, right
, top
, bottom
; /* Texture coordinates */
2616 UINT fbwidth
= Src
->currentDesc
.Width
;
2617 UINT fbheight
= Src
->currentDesc
.Height
;
2618 GLenum drawBuffer
= GL_BACK
;
2620 TRACE("Using hwstretch blit\n");
2621 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2622 ActivateContext(myDevice
, SrcSurface
, CTXUSAGE_BLIT
);
2624 IWineD3DSurface_PreLoad((IWineD3DSurface
*) This
);
2626 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2627 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2629 if(GL_LIMITS(aux_buffers
) >= 2) {
2630 /* Got more than one aux buffer? Use the 2nd aux buffer */
2631 drawBuffer
= GL_AUX1
;
2632 } else if((swapchain
|| myDevice
->offscreenBuffer
== GL_BACK
) && GL_LIMITS(aux_buffers
) >= 1) {
2633 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2634 drawBuffer
= GL_AUX0
;
2637 if(!swapchain
&& wined3d_settings
.offscreen_rendering_mode
== ORM_FBO
) {
2638 glGenTextures(1, &backup
);
2639 checkGLcall("glGenTextures\n");
2640 glBindTexture(GL_TEXTURE_2D
, backup
);
2641 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2643 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2644 * we are reading from the back buffer, the backup can be used as source texture
2646 if(Src
->glDescription
.textureName
== 0) {
2647 /* Get it a description */
2648 IWineD3DSurface_PreLoad(SrcSurface
);
2650 glBindTexture(GL_TEXTURE_2D
, Src
->glDescription
.textureName
);
2651 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2653 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2654 Src
->Flags
&= ~SFLAG_INTEXTURE
;
2657 glReadBuffer(GL_BACK
);
2658 checkGLcall("glReadBuffer(GL_BACK)");
2660 /* TODO: Only back up the part that will be overwritten */
2661 glCopyTexSubImage2D(GL_TEXTURE_2D
, 0,
2662 0, 0 /* read offsets */,
2667 checkGLcall("glCopyTexSubImage2D");
2669 /* No issue with overriding these - the sampler is dirty due to blit usage */
2670 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
,
2671 stateLookup
[WINELOOKUP_MAGFILTER
][Filter
- minLookup
[WINELOOKUP_MAGFILTER
]]);
2672 checkGLcall("glTexParameteri");
2673 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
,
2674 minMipLookup
[Filter
][WINED3DTEXF_NONE
]);
2675 checkGLcall("glTexParameteri");
2677 if(!swapchain
|| (IWineD3DSurface
*) Src
== swapchain
->backBuffer
[0]) {
2678 src
= backup
? backup
: Src
->glDescription
.textureName
;
2680 glReadBuffer(GL_FRONT
);
2681 checkGLcall("glReadBuffer(GL_FRONT)");
2683 glGenTextures(1, &src
);
2684 checkGLcall("glGenTextures(1, &src)");
2685 glBindTexture(GL_TEXTURE_2D
, src
);
2686 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2688 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2689 * out for power of 2 sizes
2691 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, Src
->pow2Width
, Src
->pow2Height
, 0,
2692 GL_RGBA
, GL_UNSIGNED_BYTE
, NULL
);
2693 checkGLcall("glTexImage2D");
2694 glCopyTexSubImage2D(GL_TEXTURE_2D
, 0,
2695 0, 0 /* read offsets */,
2700 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
2701 checkGLcall("glTexParameteri");
2702 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
2703 checkGLcall("glTexParameteri");
2705 glReadBuffer(GL_BACK
);
2706 checkGLcall("glReadBuffer(GL_BACK)");
2708 checkGLcall("glEnd and previous");
2710 left
= (float) srect
->x1
/ (float) Src
->pow2Width
;
2711 right
= (float) srect
->x2
/ (float) Src
->pow2Width
;
2714 top
= (float) (Src
->currentDesc
.Height
- srect
->y1
) / (float) Src
->pow2Height
;
2715 bottom
= (float) (Src
->currentDesc
.Height
- srect
->y2
) / (float) Src
->pow2Height
;
2717 top
= (float) (Src
->currentDesc
.Height
- srect
->y2
) / (float) Src
->pow2Height
;
2718 bottom
= (float) (Src
->currentDesc
.Height
- srect
->y1
) / (float) Src
->pow2Height
;
2721 /* draw the source texture stretched and upside down. The correct surface is bound already */
2722 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP
);
2723 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP
);
2725 glDrawBuffer(drawBuffer
);
2726 glReadBuffer(drawBuffer
);
2730 glTexCoord2f(left
, bottom
);
2731 glVertex2i(0, fbheight
);
2734 glTexCoord2f(left
, top
);
2735 glVertex2i(0, fbheight
- drect
->y2
- drect
->y1
);
2738 glTexCoord2f(right
, top
);
2739 glVertex2i(drect
->x2
- drect
->x1
, fbheight
- drect
->y2
- drect
->y1
);
2742 glTexCoord2f(right
, bottom
);
2743 glVertex2i(drect
->x2
- drect
->x1
, fbheight
);
2745 checkGLcall("glEnd and previous");
2747 /* Now read the stretched and upside down image into the destination texture */
2748 glBindTexture(This
->glDescription
.target
, This
->glDescription
.textureName
);
2749 checkGLcall("glBindTexture");
2750 glCopyTexSubImage2D(This
->glDescription
.target
,
2752 drect
->x1
, drect
->y1
, /* xoffset, yoffset */
2753 0, 0, /* We blitted the image to the origin */
2754 drect
->x2
- drect
->x1
, drect
->y2
- drect
->y1
);
2755 checkGLcall("glCopyTexSubImage2D");
2757 /* Write the back buffer backup back */
2758 glBindTexture(GL_TEXTURE_2D
, backup
? backup
: Src
->glDescription
.textureName
);
2759 checkGLcall("glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName)");
2761 if(drawBuffer
== GL_BACK
) {
2764 glTexCoord2f(0.0, (float) fbheight
/ (float) Src
->pow2Height
);
2768 glTexCoord2f(0.0, 0.0);
2769 glVertex2i(0, fbheight
);
2772 glTexCoord2f((float) fbwidth
/ (float) Src
->pow2Width
, 0.0);
2773 glVertex2i(fbwidth
, Src
->currentDesc
.Height
);
2776 glTexCoord2f((float) fbwidth
/ (float) Src
->pow2Width
, (float) fbheight
/ (float) Src
->pow2Height
);
2777 glVertex2i(fbwidth
, 0);
2780 /* Restore the old draw buffer */
2781 glDrawBuffer(GL_BACK
);
2785 if(src
!= Src
->glDescription
.textureName
&& src
!= backup
) {
2786 glDeleteTextures(1, &src
);
2787 checkGLcall("glDeleteTextures(1, &src)");
2790 glDeleteTextures(1, &backup
);
2791 checkGLcall("glDeleteTextures(1, &backup)");
2796 /* Not called from the VTable */
2797 static HRESULT
IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl
*This
, RECT
*DestRect
, IWineD3DSurface
*SrcSurface
, RECT
*SrcRect
, DWORD Flags
, WINEDDBLTFX
*DDBltFx
, WINED3DTEXTUREFILTERTYPE Filter
) {
2799 IWineD3DDeviceImpl
*myDevice
= This
->resource
.wineD3DDevice
;
2800 IWineD3DSwapChainImpl
*srcSwapchain
= NULL
, *dstSwapchain
= NULL
;
2801 IWineD3DSurfaceImpl
*Src
= (IWineD3DSurfaceImpl
*) SrcSurface
;
2803 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This
, DestRect
, SrcSurface
, SrcRect
, Flags
, DDBltFx
);
2805 /* Get the swapchain. One of the surfaces has to be a primary surface */
2806 if(This
->resource
.pool
== WINED3DPOOL_SYSTEMMEM
) {
2807 WARN("Destination is in sysmem, rejecting gl blt\n");
2808 return WINED3DERR_INVALIDCALL
;
2810 IWineD3DSurface_GetContainer( (IWineD3DSurface
*) This
, &IID_IWineD3DSwapChain
, (void **)&dstSwapchain
);
2811 if(dstSwapchain
) IWineD3DSwapChain_Release((IWineD3DSwapChain
*) dstSwapchain
);
2813 if(Src
->resource
.pool
== WINED3DPOOL_SYSTEMMEM
) {
2814 WARN("Src is in sysmem, rejecting gl blt\n");
2815 return WINED3DERR_INVALIDCALL
;
2817 IWineD3DSurface_GetContainer( (IWineD3DSurface
*) Src
, &IID_IWineD3DSwapChain
, (void **)&srcSwapchain
);
2818 if(srcSwapchain
) IWineD3DSwapChain_Release((IWineD3DSwapChain
*) srcSwapchain
);
2821 /* Early sort out of cases where no render target is used */
2822 if(!dstSwapchain
&& !srcSwapchain
&&
2823 SrcSurface
!= myDevice
->render_targets
[0] && This
!= (IWineD3DSurfaceImpl
*) myDevice
->render_targets
[0]) {
2824 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src
, This
);
2825 return WINED3DERR_INVALIDCALL
;
2828 /* No destination color keying supported */
2829 if(Flags
& (WINEDDBLT_KEYDEST
| WINEDDBLT_KEYDESTOVERRIDE
)) {
2830 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2831 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2832 return WINED3DERR_INVALIDCALL
;
2836 rect
.x1
= DestRect
->left
;
2837 rect
.y1
= DestRect
->top
;
2838 rect
.x2
= DestRect
->right
;
2839 rect
.y2
= DestRect
->bottom
;
2843 rect
.x2
= This
->currentDesc
.Width
;
2844 rect
.y2
= This
->currentDesc
.Height
;
2847 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2848 if(dstSwapchain
&& dstSwapchain
== srcSwapchain
&& dstSwapchain
->backBuffer
&&
2849 ((IWineD3DSurface
*) This
== dstSwapchain
->frontBuffer
) && SrcSurface
== dstSwapchain
->backBuffer
[0]) {
2850 /* Half-life does a Blt from the back buffer to the front buffer,
2851 * Full surface size, no flags... Use present instead
2853 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2856 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2860 TRACE("Looking if a Present can be done...\n");
2861 /* Source Rectangle must be full surface */
2863 if(SrcRect
->left
!= 0 || SrcRect
->top
!= 0 ||
2864 SrcRect
->right
!= Src
->currentDesc
.Width
|| SrcRect
->bottom
!= Src
->currentDesc
.Height
) {
2865 TRACE("No, Source rectangle doesn't match\n");
2871 mySrcRect
.right
= Src
->currentDesc
.Width
;
2872 mySrcRect
.bottom
= Src
->currentDesc
.Height
;
2874 /* No stretching may occur */
2875 if(mySrcRect
.right
!= rect
.x2
- rect
.x1
||
2876 mySrcRect
.bottom
!= rect
.y2
- rect
.y1
) {
2877 TRACE("No, stretching is done\n");
2881 /* Destination must be full surface or match the clipping rectangle */
2882 if(This
->clipper
&& ((IWineD3DClipperImpl
*) This
->clipper
)->hWnd
)
2886 GetClientRect(((IWineD3DClipperImpl
*) This
->clipper
)->hWnd
, &cliprect
);
2891 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl
*) This
->clipper
)->hWnd
,
2894 if(pos
[0].x
!= cliprect
.left
|| pos
[0].y
!= cliprect
.top
||
2895 pos
[1].x
!= cliprect
.right
|| pos
[1].y
!= cliprect
.bottom
)
2897 TRACE("No, dest rectangle doesn't match(clipper)\n");
2898 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect
.left
, cliprect
.top
, cliprect
.right
, cliprect
.bottom
);
2899 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect
.x1
, rect
.y1
, rect
.x2
, rect
.y2
);
2905 if(rect
.x1
!= 0 || rect
.y1
!= 0 ||
2906 rect
.x2
!= This
->currentDesc
.Width
|| rect
.y2
!= This
->currentDesc
.Height
) {
2907 TRACE("No, dest rectangle doesn't match(surface size)\n");
2914 /* These flags are unimportant for the flag check, remove them */
2915 if((Flags
& ~(WINEDDBLT_DONOTWAIT
| WINEDDBLT_WAIT
)) == 0) {
2916 WINED3DSWAPEFFECT orig_swap
= dstSwapchain
->presentParms
.SwapEffect
;
2918 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2919 * take very long, while a flip is fast.
2920 * This applies to Half-Life, which does such Blts every time it finished
2921 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2922 * menu. This is also used by all apps when they do windowed rendering
2924 * The problem is that flipping is not really the same as copying. After a
2925 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2926 * untouched. Therefore it's necessary to override the swap effect
2927 * and to set it back after the flip.
2929 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
2933 dstSwapchain
->presentParms
.SwapEffect
= WINED3DSWAPEFFECT_COPY
;
2934 dstSwapchain
->presentParms
.PresentationInterval
= WINED3DPRESENT_INTERVAL_IMMEDIATE
;
2936 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2937 IWineD3DSwapChain_Present((IWineD3DSwapChain
*) dstSwapchain
, NULL
, NULL
, 0, NULL
, 0);
2939 dstSwapchain
->presentParms
.SwapEffect
= orig_swap
;
2946 TRACE("Unsupported blit between buffers on the same swapchain\n");
2947 return WINED3DERR_INVALIDCALL
;
2948 } else if((dstSwapchain
|| This
== (IWineD3DSurfaceImpl
*) myDevice
->render_targets
[0]) &&
2949 (srcSwapchain
|| SrcSurface
== myDevice
->render_targets
[0]) ) {
2950 ERR("Can't perform hardware blit between 2 different swapchains, falling back to software\n");
2951 return WINED3DERR_INVALIDCALL
;
2954 if(srcSwapchain
|| SrcSurface
== myDevice
->render_targets
[0]) {
2955 /* Blit from render target to texture */
2957 BOOL upsideDown
, stretchx
;
2959 if(Flags
& (WINEDDBLT_KEYSRC
| WINEDDBLT_KEYSRCOVERRIDE
)) {
2960 TRACE("Color keying not supported by frame buffer to texture blit\n");
2961 return WINED3DERR_INVALIDCALL
;
2962 /* Destination color key is checked above */
2965 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2966 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2969 if(SrcRect
->top
< SrcRect
->bottom
) {
2970 srect
.y1
= SrcRect
->top
;
2971 srect
.y2
= SrcRect
->bottom
;
2974 srect
.y1
= SrcRect
->bottom
;
2975 srect
.y2
= SrcRect
->top
;
2978 srect
.x1
= SrcRect
->left
;
2979 srect
.x2
= SrcRect
->right
;
2983 srect
.x2
= Src
->currentDesc
.Width
;
2984 srect
.y2
= Src
->currentDesc
.Height
;
2987 if(rect
.x1
> rect
.x2
) {
2991 upsideDown
= !upsideDown
;
2994 TRACE("Reading from an offscreen target\n");
2995 upsideDown
= !upsideDown
;
2998 if(rect
.x2
- rect
.x1
!= srect
.x2
- srect
.x1
) {
3004 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3005 * flip the image nor scale it.
3007 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3008 * -> If the app wants a image width an unscaled width, copy it line per line
3009 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3010 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3011 * back buffer. This is slower than reading line per line, thus not used for flipping
3012 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3015 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3016 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3019 if (wined3d_settings
.offscreen_rendering_mode
== ORM_FBO
&& GL_SUPPORT(EXT_FRAMEBUFFER_BLIT
)) {
3020 stretch_rect_fbo((IWineD3DDevice
*)myDevice
, SrcSurface
, &srect
,
3021 (IWineD3DSurface
*)This
, &rect
, Filter
, upsideDown
);
3022 } else if((!stretchx
) || rect
.x2
- rect
.x1
> Src
->currentDesc
.Width
||
3023 rect
.y2
- rect
.y1
> Src
->currentDesc
.Height
) {
3024 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3025 fb_copy_to_texture_direct(This
, SrcSurface
, srcSwapchain
, &srect
, &rect
, upsideDown
, Filter
);
3027 TRACE("Using hardware stretching to flip / stretch the texture\n");
3028 fb_copy_to_texture_hwstretch(This
, SrcSurface
, srcSwapchain
, &srect
, &rect
, upsideDown
, Filter
);
3031 if(!(This
->Flags
& SFLAG_DONOTFREE
)) {
3032 HeapFree(GetProcessHeap(), 0, This
->resource
.allocatedMemory
);
3033 This
->resource
.allocatedMemory
= NULL
;
3035 This
->Flags
&= ~SFLAG_INSYSMEM
;
3037 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3038 * path is never entered
3040 This
->Flags
|= SFLAG_INTEXTURE
;
3044 /* Blit from offscreen surface to render target */
3045 float glTexCoord
[4];
3046 DWORD oldCKeyFlags
= Src
->CKeyFlags
;
3047 WINEDDCOLORKEY oldBltCKey
= This
->SrcBltCKey
;
3048 RECT SourceRectangle
;
3050 TRACE("Blt from surface %p to rendertarget %p\n", Src
, This
);
3053 SourceRectangle
.left
= SrcRect
->left
;
3054 SourceRectangle
.right
= SrcRect
->right
;
3055 SourceRectangle
.top
= SrcRect
->top
;
3056 SourceRectangle
.bottom
= SrcRect
->bottom
;
3058 SourceRectangle
.left
= 0;
3059 SourceRectangle
.right
= Src
->currentDesc
.Width
;
3060 SourceRectangle
.top
= 0;
3061 SourceRectangle
.bottom
= Src
->currentDesc
.Height
;
3064 if(!CalculateTexRect(Src
, &SourceRectangle
, glTexCoord
)) {
3065 /* Fall back to software */
3066 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src
,
3067 SourceRectangle
.left
, SourceRectangle
.top
,
3068 SourceRectangle
.right
, SourceRectangle
.bottom
);
3069 return WINED3DERR_INVALIDCALL
;
3072 /* Color keying: Check if we have to do a color keyed blt,
3073 * and if not check if a color key is activated.
3075 * Just modify the color keying parameters in the surface and restore them afterwards
3076 * The surface keeps track of the color key last used to load the opengl surface.
3077 * PreLoad will catch the change to the flags and color key and reload if necessary.
3079 if(Flags
& WINEDDBLT_KEYSRC
) {
3080 /* Use color key from surface */
3081 } else if(Flags
& WINEDDBLT_KEYSRCOVERRIDE
) {
3082 /* Use color key from DDBltFx */
3083 Src
->CKeyFlags
|= WINEDDSD_CKSRCBLT
;
3084 This
->SrcBltCKey
= DDBltFx
->ddckSrcColorkey
;
3086 /* Do not use color key */
3087 Src
->CKeyFlags
&= ~WINEDDSD_CKSRCBLT
;
3090 /* Now load the surface */
3091 IWineD3DSurface_PreLoad((IWineD3DSurface
*) Src
);
3094 /* Activate the destination context, set it up for blitting */
3095 ActivateContext(myDevice
, (IWineD3DSurface
*) This
, CTXUSAGE_BLIT
);
3099 TRACE("Drawing to offscreen buffer\n");
3100 glDrawBuffer(myDevice
->offscreenBuffer
);
3101 checkGLcall("glDrawBuffer");
3103 GLenum buffer
= surface_get_gl_buffer((IWineD3DSurface
*)This
, (IWineD3DSwapChain
*)dstSwapchain
);
3104 TRACE("Drawing to %#x buffer\n", buffer
);
3105 glDrawBuffer(buffer
);
3106 checkGLcall("glDrawBuffer");
3109 /* Bind the texture */
3110 glBindTexture(GL_TEXTURE_2D
, Src
->glDescription
.textureName
);
3111 checkGLcall("glBindTexture");
3113 /* Filtering for StretchRect */
3114 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
,
3115 stateLookup
[WINELOOKUP_MAGFILTER
][Filter
- minLookup
[WINELOOKUP_MAGFILTER
]]);
3116 checkGLcall("glTexParameteri");
3117 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
,
3118 minMipLookup
[Filter
][WINED3DTEXF_NONE
]);
3119 checkGLcall("glTexParameteri");
3120 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP
);
3121 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP
);
3122 glTexEnvi(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_REPLACE
);
3123 checkGLcall("glTexEnvi");
3125 /* This is for color keying */
3126 if(Flags
& (WINEDDBLT_KEYSRC
| WINEDDBLT_KEYSRCOVERRIDE
)) {
3127 glEnable(GL_ALPHA_TEST
);
3128 checkGLcall("glEnable GL_ALPHA_TEST");
3129 glAlphaFunc(GL_NOTEQUAL
, 0.0);
3130 checkGLcall("glAlphaFunc\n");
3132 glDisable(GL_ALPHA_TEST
);
3133 checkGLcall("glDisable GL_ALPHA_TEST");
3136 /* Draw a textured quad
3140 glColor3d(1.0f
, 1.0f
, 1.0f
);
3141 glTexCoord2f(glTexCoord
[0], glTexCoord
[2]);
3146 glTexCoord2f(glTexCoord
[0], glTexCoord
[3]);
3147 glVertex3f(rect
.x1
, rect
.y2
, 0.0);
3149 glTexCoord2f(glTexCoord
[1], glTexCoord
[3]);
3154 glTexCoord2f(glTexCoord
[1], glTexCoord
[2]);
3159 checkGLcall("glEnd");
3161 if(Flags
& (WINEDDBLT_KEYSRC
| WINEDDBLT_KEYSRCOVERRIDE
)) {
3162 glDisable(GL_ALPHA_TEST
);
3163 checkGLcall("glDisable(GL_ALPHA_TEST)");
3166 /* Unbind the texture */
3167 glBindTexture(GL_TEXTURE_2D
, 0);
3168 checkGLcall("glEnable glBindTexture");
3170 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3171 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3173 if(dstSwapchain
&& This
== (IWineD3DSurfaceImpl
*) dstSwapchain
->frontBuffer
&& dstSwapchain
->backBuffer
) {
3174 glDrawBuffer(GL_BACK
);
3175 checkGLcall("glDrawBuffer");
3177 /* Restore the color key parameters */
3178 Src
->CKeyFlags
= oldCKeyFlags
;
3179 This
->SrcBltCKey
= oldBltCKey
;
3183 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3184 This
->Flags
&= ~SFLAG_INSYSMEM
;
3185 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3188 if(dstSwapchain
|| wined3d_settings
.offscreen_rendering_mode
!= ORM_FBO
) {
3189 This
->Flags
|= SFLAG_INDRAWABLE
;
3190 This
->Flags
&= ~SFLAG_INTEXTURE
;
3192 This
->Flags
|= SFLAG_INTEXTURE
;
3197 /* Source-Less Blit to render target */
3198 if (Flags
& WINEDDBLT_COLORFILL
) {
3199 /* This is easy to handle for the D3D Device... */
3202 TRACE("Colorfill\n");
3204 /* The color as given in the Blt function is in the format of the frame-buffer...
3205 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3207 if (This
->resource
.format
== WINED3DFMT_P8
) {
3208 if (This
->palette
) {
3209 color
= ((0xFF000000) |
3210 (This
->palette
->palents
[DDBltFx
->u5
.dwFillColor
].peRed
<< 16) |
3211 (This
->palette
->palents
[DDBltFx
->u5
.dwFillColor
].peGreen
<< 8) |
3212 (This
->palette
->palents
[DDBltFx
->u5
.dwFillColor
].peBlue
));
3217 else if (This
->resource
.format
== WINED3DFMT_R5G6B5
) {
3218 if (DDBltFx
->u5
.dwFillColor
== 0xFFFF) {
3221 color
= ((0xFF000000) |
3222 ((DDBltFx
->u5
.dwFillColor
& 0xF800) << 8) |
3223 ((DDBltFx
->u5
.dwFillColor
& 0x07E0) << 5) |
3224 ((DDBltFx
->u5
.dwFillColor
& 0x001F) << 3));
3227 else if ((This
->resource
.format
== WINED3DFMT_R8G8B8
) ||
3228 (This
->resource
.format
== WINED3DFMT_X8R8G8B8
) ) {
3229 color
= 0xFF000000 | DDBltFx
->u5
.dwFillColor
;
3231 else if (This
->resource
.format
== WINED3DFMT_A8R8G8B8
) {
3232 color
= DDBltFx
->u5
.dwFillColor
;
3235 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3236 return WINED3DERR_INVALIDCALL
;
3239 TRACE("Calling GetSwapChain with mydevice = %p\n", myDevice
);
3240 if(dstSwapchain
&& dstSwapchain
->backBuffer
&& This
== (IWineD3DSurfaceImpl
*) dstSwapchain
->backBuffer
[0]) {
3241 glDrawBuffer(GL_BACK
);
3242 checkGLcall("glDrawBuffer(GL_BACK)");
3243 } else if (dstSwapchain
&& This
== (IWineD3DSurfaceImpl
*) dstSwapchain
->frontBuffer
) {
3244 glDrawBuffer(GL_FRONT
);
3245 checkGLcall("glDrawBuffer(GL_FRONT)");
3246 } else if(This
== (IWineD3DSurfaceImpl
*) myDevice
->render_targets
[0]) {
3247 glDrawBuffer(myDevice
->offscreenBuffer
);
3248 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer3)");
3250 TRACE("Surface is higher back buffer, falling back to software\n");
3251 return WINED3DERR_INVALIDCALL
;
3254 TRACE("(%p) executing Render Target override, color = %x\n", This
, color
);
3256 IWineD3DDevice_Clear( (IWineD3DDevice
*) myDevice
,
3257 1 /* Number of rectangles */,
3259 WINED3DCLEAR_TARGET
,
3264 /* Restore the original draw buffer */
3266 glDrawBuffer(myDevice
->offscreenBuffer
);
3267 } else if(dstSwapchain
->backBuffer
&& dstSwapchain
->backBuffer
[0]) {
3268 glDrawBuffer(GL_BACK
);
3270 vcheckGLcall("glDrawBuffer");
3276 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3277 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3278 return WINED3DERR_INVALIDCALL
;
3281 static HRESULT WINAPI
IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl
*This
, RECT
*DestRect
, IWineD3DSurface
*SrcSurface
, RECT
*SrcRect
, DWORD Flags
, WINEDDBLTFX
*DDBltFx
)
3283 IWineD3DDeviceImpl
*myDevice
= This
->resource
.wineD3DDevice
;
3285 if (Flags
& WINEDDBLT_DEPTHFILL
)
3286 return IWineD3DDevice_Clear((IWineD3DDevice
*) myDevice
,
3287 DestRect
== NULL
? 0 : 1,
3288 (WINED3DRECT
*) DestRect
,
3289 WINED3DCLEAR_ZBUFFER
,
3291 (float) DDBltFx
->u5
.dwFillDepth
/ (float) MAXDWORD
,
3294 FIXME("(%p): Unsupp depthstencil blit\n", This
);
3295 return WINED3DERR_INVALIDCALL
;
3298 static HRESULT WINAPI
IWineD3DSurfaceImpl_Blt(IWineD3DSurface
*iface
, RECT
*DestRect
, IWineD3DSurface
*SrcSurface
, RECT
*SrcRect
, DWORD Flags
, WINEDDBLTFX
*DDBltFx
, WINED3DTEXTUREFILTERTYPE Filter
) {
3299 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
3300 IWineD3DSurfaceImpl
*Src
= (IWineD3DSurfaceImpl
*) SrcSurface
;
3301 IWineD3DDeviceImpl
*myDevice
= This
->resource
.wineD3DDevice
;
3302 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This
, DestRect
, SrcSurface
, SrcRect
, Flags
, DDBltFx
);
3303 TRACE("(%p): Usage is %s\n", This
, debug_d3dusage(This
->resource
.usage
));
3305 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3306 * except depth blits, which seem to work
3308 if(iface
== myDevice
->stencilBufferTarget
|| (SrcSurface
&& SrcSurface
== myDevice
->stencilBufferTarget
)) {
3309 if(myDevice
->inScene
&& !(Flags
& WINEDDBLT_DEPTHFILL
)) {
3310 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3311 return WINED3DERR_INVALIDCALL
;
3312 } else if(IWineD3DSurfaceImpl_BltZ(This
, DestRect
, SrcSurface
, SrcRect
, Flags
, DDBltFx
) == WINED3D_OK
) {
3313 TRACE("Z Blit override handled the blit\n");
3318 /* Special cases for RenderTargets */
3319 if( (This
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
) ||
3320 ( Src
&& (Src
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
) )) {
3321 if(IWineD3DSurfaceImpl_BltOverride(This
, DestRect
, SrcSurface
, SrcRect
, Flags
, DDBltFx
, Filter
) == WINED3D_OK
) return WINED3D_OK
;
3324 /* For the rest call the X11 surface implementation.
3325 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3326 * other Blts are rather rare
3328 return IWineGDISurfaceImpl_Blt(iface
, DestRect
, SrcSurface
, SrcRect
, Flags
, DDBltFx
, Filter
);
3331 HRESULT WINAPI
IWineD3DSurfaceImpl_GetBltStatus(IWineD3DSurface
*iface
, DWORD Flags
) {
3332 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*)iface
;
3333 TRACE("(%p)->(%x)\n", This
, Flags
);
3337 case WINEDDGBS_CANBLT
:
3338 case WINEDDGBS_ISBLTDONE
:
3342 return WINED3DERR_INVALIDCALL
;
3346 HRESULT WINAPI
IWineD3DSurfaceImpl_GetFlipStatus(IWineD3DSurface
*iface
, DWORD Flags
) {
3347 /* XXX: DDERR_INVALIDSURFACETYPE */
3349 TRACE("(%p)->(%08x)\n",iface
,Flags
);
3351 case WINEDDGFS_CANFLIP
:
3352 case WINEDDGFS_ISFLIPDONE
:
3356 return WINED3DERR_INVALIDCALL
;
3360 HRESULT WINAPI
IWineD3DSurfaceImpl_IsLost(IWineD3DSurface
*iface
) {
3361 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3362 TRACE("(%p)\n", This
);
3364 /* D3D8 and 9 loose full devices, ddraw only surfaces */
3365 return This
->Flags
& SFLAG_LOST
? WINED3DERR_DEVICELOST
: WINED3D_OK
;
3368 HRESULT WINAPI
IWineD3DSurfaceImpl_Restore(IWineD3DSurface
*iface
) {
3369 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3370 TRACE("(%p)\n", This
);
3372 /* So far we don't lose anything :) */
3373 This
->Flags
&= ~SFLAG_LOST
;
3377 HRESULT WINAPI
IWineD3DSurfaceImpl_BltFast(IWineD3DSurface
*iface
, DWORD dstx
, DWORD dsty
, IWineD3DSurface
*Source
, RECT
*rsrc
, DWORD trans
) {
3378 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3379 IWineD3DSurfaceImpl
*srcImpl
= (IWineD3DSurfaceImpl
*) Source
;
3380 IWineD3DDeviceImpl
*myDevice
= This
->resource
.wineD3DDevice
;
3381 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface
, dstx
, dsty
, Source
, rsrc
, trans
);
3383 if(myDevice
->inScene
&&
3384 (iface
== myDevice
->stencilBufferTarget
||
3385 (Source
&& Source
== myDevice
->stencilBufferTarget
))) {
3386 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3387 return WINED3DERR_INVALIDCALL
;
3390 /* Special cases for RenderTargets */
3391 if( (This
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
) ||
3392 ( srcImpl
&& (srcImpl
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
) )) {
3394 RECT SrcRect
, DstRect
;
3398 SrcRect
.left
= rsrc
->left
;
3399 SrcRect
.top
= rsrc
->top
;
3400 SrcRect
.bottom
= rsrc
->bottom
;
3401 SrcRect
.right
= rsrc
->right
;
3405 SrcRect
.right
= srcImpl
->currentDesc
.Width
;
3406 SrcRect
.bottom
= srcImpl
->currentDesc
.Height
;
3409 DstRect
.left
= dstx
;
3411 DstRect
.right
= dstx
+ SrcRect
.right
- SrcRect
.left
;
3412 DstRect
.bottom
= dsty
+ SrcRect
.bottom
- SrcRect
.top
;
3414 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3415 if(trans
& WINEDDBLTFAST_SRCCOLORKEY
)
3416 Flags
|= WINEDDBLT_KEYSRC
;
3417 if(trans
& WINEDDBLTFAST_DESTCOLORKEY
)
3418 Flags
|= WINEDDBLT_KEYDEST
;
3419 if(trans
& WINEDDBLTFAST_WAIT
)
3420 Flags
|= WINEDDBLT_WAIT
;
3421 if(trans
& WINEDDBLTFAST_DONOTWAIT
)
3422 Flags
|= WINEDDBLT_DONOTWAIT
;
3424 if(IWineD3DSurfaceImpl_BltOverride(This
, &DstRect
, Source
, &SrcRect
, Flags
, NULL
, WINED3DTEXF_POINT
) == WINED3D_OK
) return WINED3D_OK
;
3428 return IWineGDISurfaceImpl_BltFast(iface
, dstx
, dsty
, Source
, rsrc
, trans
);
3431 HRESULT WINAPI
IWineD3DSurfaceImpl_GetPalette(IWineD3DSurface
*iface
, IWineD3DPalette
**Pal
) {
3432 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3433 TRACE("(%p)->(%p)\n", This
, Pal
);
3435 *Pal
= (IWineD3DPalette
*) This
->palette
;
3439 HRESULT WINAPI
IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface
*iface
) {
3440 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3442 IWineD3DPaletteImpl
*pal
= This
->palette
;
3444 TRACE("(%p)\n", This
);
3446 if(This
->resource
.format
== WINED3DFMT_P8
||
3447 This
->resource
.format
== WINED3DFMT_A8P8
)
3449 if(!This
->Flags
& SFLAG_INSYSMEM
) {
3450 FIXME("Palette changed with surface that does not have an up to date system memory copy\n");
3452 TRACE("Dirtifying surface\n");
3453 This
->Flags
&= ~(SFLAG_INTEXTURE
| SFLAG_INDRAWABLE
);
3456 if(This
->Flags
& SFLAG_DIBSECTION
) {
3457 TRACE("(%p): Updating the hdc's palette\n", This
);
3458 for (n
=0; n
<256; n
++) {
3460 col
[n
].rgbRed
= pal
->palents
[n
].peRed
;
3461 col
[n
].rgbGreen
= pal
->palents
[n
].peGreen
;
3462 col
[n
].rgbBlue
= pal
->palents
[n
].peBlue
;
3464 IWineD3DDeviceImpl
*device
= This
->resource
.wineD3DDevice
;
3465 /* Use the default device palette */
3466 col
[n
].rgbRed
= device
->palettes
[device
->currentPalette
][n
].peRed
;
3467 col
[n
].rgbGreen
= device
->palettes
[device
->currentPalette
][n
].peGreen
;
3468 col
[n
].rgbBlue
= device
->palettes
[device
->currentPalette
][n
].peBlue
;
3470 col
[n
].rgbReserved
= 0;
3472 SetDIBColorTable(This
->hDC
, 0, 256, col
);
3478 HRESULT WINAPI
IWineD3DSurfaceImpl_SetPalette(IWineD3DSurface
*iface
, IWineD3DPalette
*Pal
) {
3479 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3480 IWineD3DPaletteImpl
*PalImpl
= (IWineD3DPaletteImpl
*) Pal
;
3481 TRACE("(%p)->(%p)\n", This
, Pal
);
3483 if(This
->palette
!= NULL
)
3484 if(This
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
)
3485 This
->palette
->Flags
&= ~WINEDDPCAPS_PRIMARYSURFACE
;
3487 if(PalImpl
!= NULL
) {
3488 if(This
->resource
.usage
& WINED3DUSAGE_RENDERTARGET
) {
3489 /* Set the device's main palette if the palette
3490 * wasn't a primary palette before
3492 if(!(PalImpl
->Flags
& WINEDDPCAPS_PRIMARYSURFACE
)) {
3493 IWineD3DDeviceImpl
*device
= This
->resource
.wineD3DDevice
;
3496 for(i
=0; i
< 256; i
++) {
3497 device
->palettes
[device
->currentPalette
][i
] = PalImpl
->palents
[i
];
3501 (PalImpl
)->Flags
|= WINEDDPCAPS_PRIMARYSURFACE
;
3504 This
->palette
= PalImpl
;
3506 return IWineD3DSurface_RealizePalette(iface
);
3509 HRESULT WINAPI
IWineD3DSurfaceImpl_SetColorKey(IWineD3DSurface
*iface
, DWORD Flags
, WINEDDCOLORKEY
*CKey
) {
3510 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3511 TRACE("(%p)->(%08x,%p)\n", This
, Flags
, CKey
);
3513 if ((Flags
& WINEDDCKEY_COLORSPACE
) != 0) {
3514 FIXME(" colorkey value not supported (%08x) !\n", Flags
);
3515 return WINED3DERR_INVALIDCALL
;
3518 /* Dirtify the surface, but only if a key was changed */
3520 switch (Flags
& ~WINEDDCKEY_COLORSPACE
) {
3521 case WINEDDCKEY_DESTBLT
:
3522 This
->DestBltCKey
= *CKey
;
3523 This
->CKeyFlags
|= WINEDDSD_CKDESTBLT
;
3526 case WINEDDCKEY_DESTOVERLAY
:
3527 This
->DestOverlayCKey
= *CKey
;
3528 This
->CKeyFlags
|= WINEDDSD_CKDESTOVERLAY
;
3531 case WINEDDCKEY_SRCOVERLAY
:
3532 This
->SrcOverlayCKey
= *CKey
;
3533 This
->CKeyFlags
|= WINEDDSD_CKSRCOVERLAY
;
3536 case WINEDDCKEY_SRCBLT
:
3537 This
->SrcBltCKey
= *CKey
;
3538 This
->CKeyFlags
|= WINEDDSD_CKSRCBLT
;
3543 switch (Flags
& ~WINEDDCKEY_COLORSPACE
) {
3544 case WINEDDCKEY_DESTBLT
:
3545 This
->CKeyFlags
&= ~WINEDDSD_CKDESTBLT
;
3548 case WINEDDCKEY_DESTOVERLAY
:
3549 This
->CKeyFlags
&= ~WINEDDSD_CKDESTOVERLAY
;
3552 case WINEDDCKEY_SRCOVERLAY
:
3553 This
->CKeyFlags
&= ~WINEDDSD_CKSRCOVERLAY
;
3556 case WINEDDCKEY_SRCBLT
:
3557 This
->CKeyFlags
&= ~WINEDDSD_CKSRCBLT
;
3565 static HRESULT WINAPI
IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface
*iface
) {
3566 /** Check against the maximum texture sizes supported by the video card **/
3567 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3568 unsigned int pow2Width
, pow2Height
;
3569 const GlPixelFormatDesc
*glDesc
;
3571 getFormatDescEntry(This
->resource
.format
, &GLINFO_LOCATION
, &glDesc
);
3572 /* Setup some glformat defaults */
3573 This
->glDescription
.glFormat
= glDesc
->glFormat
;
3574 This
->glDescription
.glFormatInternal
= glDesc
->glInternal
;
3575 This
->glDescription
.glType
= glDesc
->glType
;
3577 This
->glDescription
.textureName
= 0;
3578 This
->glDescription
.target
= GL_TEXTURE_2D
;
3580 /* Non-power2 support */
3581 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO
)) {
3582 pow2Width
= This
->currentDesc
.Width
;
3583 pow2Height
= This
->currentDesc
.Height
;
3585 /* Find the nearest pow2 match */
3586 pow2Width
= pow2Height
= 1;
3587 while (pow2Width
< This
->currentDesc
.Width
) pow2Width
<<= 1;
3588 while (pow2Height
< This
->currentDesc
.Height
) pow2Height
<<= 1;
3590 This
->pow2Width
= pow2Width
;
3591 This
->pow2Height
= pow2Height
;
3593 if (pow2Width
> This
->currentDesc
.Width
|| pow2Height
> This
->currentDesc
.Height
) {
3594 WINED3DFORMAT Format
= This
->resource
.format
;
3595 /** TODO: add support for non power two compressed textures **/
3596 if (Format
== WINED3DFMT_DXT1
|| Format
== WINED3DFMT_DXT2
|| Format
== WINED3DFMT_DXT3
3597 || Format
== WINED3DFMT_DXT4
|| Format
== WINED3DFMT_DXT5
) {
3598 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3599 This
, This
->currentDesc
.Width
, This
->currentDesc
.Height
);
3600 return WINED3DERR_NOTAVAILABLE
;
3604 if(pow2Width
!= This
->currentDesc
.Width
||
3605 pow2Height
!= This
->currentDesc
.Height
) {
3606 This
->Flags
|= SFLAG_NONPOW2
;
3609 TRACE("%p\n", This
);
3610 if ((This
->pow2Width
> GL_LIMITS(texture_size
) || This
->pow2Height
> GL_LIMITS(texture_size
)) && !(This
->resource
.usage
& (WINED3DUSAGE_RENDERTARGET
| WINED3DUSAGE_DEPTHSTENCIL
))) {
3611 /* one of three options
3612 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)
3613 2: Set the texture to the maximum size (bad idea)
3614 3: WARN and return WINED3DERR_NOTAVAILABLE;
3615 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.
3617 WARN("(%p) Creating an oversized surface\n", This
);
3618 This
->Flags
|= SFLAG_OVERSIZE
;
3620 /* This will be initialized on the first blt */
3621 This
->glRect
.left
= 0;
3622 This
->glRect
.top
= 0;
3623 This
->glRect
.right
= 0;
3624 This
->glRect
.bottom
= 0;
3626 /* No oversize, gl rect is the full texture size */
3627 This
->Flags
&= ~SFLAG_OVERSIZE
;
3628 This
->glRect
.left
= 0;
3629 This
->glRect
.top
= 0;
3630 This
->glRect
.right
= This
->pow2Width
;
3631 This
->glRect
.bottom
= This
->pow2Height
;
3634 if(This
->resource
.allocatedMemory
== NULL
) {
3635 /* Make sure memory exists from the start, and it is initialized properly. D3D initializes surfaces,
3636 * gl does not, so we need to upload zeroes to init the gl texture.
3638 This
->resource
.allocatedMemory
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, This
->resource
.size
+ 4);
3640 This
->Flags
|= SFLAG_INSYSMEM
;
3645 DWORD WINAPI
IWineD3DSurfaceImpl_GetPitch(IWineD3DSurface
*iface
) {
3646 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3648 TRACE("(%p)\n", This
);
3650 /* DXTn formats don't have exact pitches as they are to the new row of blocks,
3651 where each block is 4x4 pixels, 8 bytes (dxt1) and 16 bytes (dxt2/3/4/5)
3652 ie pitch = (width/4) * bytes per block */
3653 if (This
->resource
.format
== WINED3DFMT_DXT1
) /* DXT1 is 8 bytes per block */
3654 ret
= ((This
->currentDesc
.Width
+ 3) >> 2) << 3;
3655 else if (This
->resource
.format
== WINED3DFMT_DXT2
|| This
->resource
.format
== WINED3DFMT_DXT3
||
3656 This
->resource
.format
== WINED3DFMT_DXT4
|| This
->resource
.format
== WINED3DFMT_DXT5
) /* DXT2/3/4/5 is 16 bytes per block */
3657 ret
= ((This
->currentDesc
.Width
+ 3) >> 2) << 4;
3659 unsigned char alignment
= This
->resource
.wineD3DDevice
->surface_alignment
;
3660 ret
= This
->bytesPerPixel
* This
->currentDesc
.Width
; /* Bytes / row */
3661 ret
= (ret
+ alignment
- 1) & ~(alignment
- 1);
3663 TRACE("(%p) Returning %d\n", This
, ret
);
3667 HRESULT WINAPI
IWineD3DSurfaceImpl_SetOverlayPosition(IWineD3DSurface
*iface
, LONG X
, LONG Y
) {
3668 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3670 FIXME("(%p)->(%d,%d) Stub!\n", This
, X
, Y
);
3672 if(!(This
->resource
.usage
& WINED3DUSAGE_OVERLAY
))
3674 TRACE("(%p): Not an overlay surface\n", This
);
3675 return WINEDDERR_NOTAOVERLAYSURFACE
;
3681 HRESULT WINAPI
IWineD3DSurfaceImpl_GetOverlayPosition(IWineD3DSurface
*iface
, LONG
*X
, LONG
*Y
) {
3682 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3684 FIXME("(%p)->(%p,%p) Stub!\n", This
, X
, Y
);
3686 if(!(This
->resource
.usage
& WINED3DUSAGE_OVERLAY
))
3688 TRACE("(%p): Not an overlay surface\n", This
);
3689 return WINEDDERR_NOTAOVERLAYSURFACE
;
3695 HRESULT WINAPI
IWineD3DSurfaceImpl_UpdateOverlayZOrder(IWineD3DSurface
*iface
, DWORD Flags
, IWineD3DSurface
*Ref
) {
3696 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3697 IWineD3DSurfaceImpl
*RefImpl
= (IWineD3DSurfaceImpl
*) Ref
;
3699 FIXME("(%p)->(%08x,%p) Stub!\n", This
, Flags
, RefImpl
);
3701 if(!(This
->resource
.usage
& WINED3DUSAGE_OVERLAY
))
3703 TRACE("(%p): Not an overlay surface\n", This
);
3704 return WINEDDERR_NOTAOVERLAYSURFACE
;
3710 HRESULT WINAPI
IWineD3DSurfaceImpl_UpdateOverlay(IWineD3DSurface
*iface
, RECT
*SrcRect
, IWineD3DSurface
*DstSurface
, RECT
*DstRect
, DWORD Flags
, WINEDDOVERLAYFX
*FX
) {
3711 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3712 IWineD3DSurfaceImpl
*Dst
= (IWineD3DSurfaceImpl
*) DstSurface
;
3713 FIXME("(%p)->(%p, %p, %p, %08x, %p)\n", This
, SrcRect
, Dst
, DstRect
, Flags
, FX
);
3715 if(!(This
->resource
.usage
& WINED3DUSAGE_OVERLAY
))
3717 TRACE("(%p): Not an overlay surface\n", This
);
3718 return WINEDDERR_NOTAOVERLAYSURFACE
;
3724 HRESULT WINAPI
IWineD3DSurfaceImpl_SetClipper(IWineD3DSurface
*iface
, IWineD3DClipper
*clipper
)
3726 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3727 TRACE("(%p)->(%p)\n", This
, clipper
);
3729 This
->clipper
= clipper
;
3733 HRESULT WINAPI
IWineD3DSurfaceImpl_GetClipper(IWineD3DSurface
*iface
, IWineD3DClipper
**clipper
)
3735 IWineD3DSurfaceImpl
*This
= (IWineD3DSurfaceImpl
*) iface
;
3736 TRACE("(%p)->(%p)\n", This
, clipper
);
3738 *clipper
= This
->clipper
;
3740 IWineD3DClipper_AddRef(*clipper
);
3745 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl
=
3748 IWineD3DSurfaceImpl_QueryInterface
,
3749 IWineD3DSurfaceImpl_AddRef
,
3750 IWineD3DSurfaceImpl_Release
,
3751 /* IWineD3DResource */
3752 IWineD3DSurfaceImpl_GetParent
,
3753 IWineD3DSurfaceImpl_GetDevice
,
3754 IWineD3DSurfaceImpl_SetPrivateData
,
3755 IWineD3DSurfaceImpl_GetPrivateData
,
3756 IWineD3DSurfaceImpl_FreePrivateData
,
3757 IWineD3DSurfaceImpl_SetPriority
,
3758 IWineD3DSurfaceImpl_GetPriority
,
3759 IWineD3DSurfaceImpl_PreLoad
,
3760 IWineD3DSurfaceImpl_GetType
,
3761 /* IWineD3DSurface */
3762 IWineD3DSurfaceImpl_GetContainer
,
3763 IWineD3DSurfaceImpl_GetDesc
,
3764 IWineD3DSurfaceImpl_LockRect
,
3765 IWineD3DSurfaceImpl_UnlockRect
,
3766 IWineD3DSurfaceImpl_GetDC
,
3767 IWineD3DSurfaceImpl_ReleaseDC
,
3768 IWineD3DSurfaceImpl_Flip
,
3769 IWineD3DSurfaceImpl_Blt
,
3770 IWineD3DSurfaceImpl_GetBltStatus
,
3771 IWineD3DSurfaceImpl_GetFlipStatus
,
3772 IWineD3DSurfaceImpl_IsLost
,
3773 IWineD3DSurfaceImpl_Restore
,
3774 IWineD3DSurfaceImpl_BltFast
,
3775 IWineD3DSurfaceImpl_GetPalette
,
3776 IWineD3DSurfaceImpl_SetPalette
,
3777 IWineD3DSurfaceImpl_RealizePalette
,
3778 IWineD3DSurfaceImpl_SetColorKey
,
3779 IWineD3DSurfaceImpl_GetPitch
,
3780 IWineD3DSurfaceImpl_SetMem
,
3781 IWineD3DSurfaceImpl_SetOverlayPosition
,
3782 IWineD3DSurfaceImpl_GetOverlayPosition
,
3783 IWineD3DSurfaceImpl_UpdateOverlayZOrder
,
3784 IWineD3DSurfaceImpl_UpdateOverlay
,
3785 IWineD3DSurfaceImpl_SetClipper
,
3786 IWineD3DSurfaceImpl_GetClipper
,
3788 IWineD3DSurfaceImpl_AddDirtyRect
,
3789 IWineD3DSurfaceImpl_LoadTexture
,
3790 IWineD3DSurfaceImpl_SaveSnapshot
,
3791 IWineD3DSurfaceImpl_SetContainer
,
3792 IWineD3DSurfaceImpl_SetGlTextureDesc
,
3793 IWineD3DSurfaceImpl_GetGlDesc
,
3794 IWineD3DSurfaceImpl_GetData
,
3795 IWineD3DSurfaceImpl_SetFormat
,
3796 IWineD3DSurfaceImpl_PrivateSetup