wined3d: Add ENTER_GL/LEAVE_GL in surface_bind_and_dirtify.
[wine.git] / dlls / wined3d / surface.c
blobe233ce2ad562340736ab4feb81a51f57967d9b45
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
11 * Copyright 2007 Henri Verbeet
12 * Copyright 2006-2008 Roderick Colenbrander
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
36 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
37 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey);
38 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This);
40 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This) {
41 GLint active_texture;
43 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
44 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
45 * gl states. The current texture unit should always be a valid one.
47 * TODO: Track the current active texture per GL context instead of using glGet
49 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
50 ENTER_GL();
51 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
52 LEAVE_GL();
53 active_texture -= GL_TEXTURE0_ARB;
54 } else {
55 active_texture = 0;
57 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_texture));
58 IWineD3DSurface_BindTexture((IWineD3DSurface *)This);
61 /* This function checks if the primary render target uses the 8bit paletted format. */
62 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
64 if (device->render_targets && device->render_targets[0]) {
65 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
66 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
67 return TRUE;
69 return FALSE;
72 /* This call just downloads data, the caller is responsible for activating the
73 * right context and binding the correct texture. */
74 static void surface_download_data(IWineD3DSurfaceImpl *This) {
75 if (0 == This->glDescription.textureName) {
76 ERR("Surface does not have a texture, but SFLAG_INTEXTURE is set\n");
77 return;
80 /* Only support read back of converted P8 surfaces */
81 if(This->Flags & SFLAG_CONVERTED && (This->resource.format != WINED3DFMT_P8)) {
82 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(This->resource.format));
83 return;
86 ENTER_GL();
88 if (This->resource.format == WINED3DFMT_DXT1 ||
89 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
90 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
91 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
92 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
93 } else {
94 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
95 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
97 if(This->Flags & SFLAG_PBO) {
98 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
99 checkGLcall("glBindBufferARB");
100 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
101 checkGLcall("glGetCompressedTexImageARB()");
102 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
103 checkGLcall("glBindBufferARB");
104 } else {
105 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
106 checkGLcall("glGetCompressedTexImageARB()");
109 LEAVE_GL();
110 } else {
111 void *mem;
112 GLenum format = This->glDescription.glFormat;
113 GLenum type = This->glDescription.glType;
114 int src_pitch = 0;
115 int dst_pitch = 0;
117 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
118 if(This->resource.format == WINED3DFMT_P8) {
119 format = GL_ALPHA;
120 type = GL_UNSIGNED_BYTE;
123 if (This->Flags & SFLAG_NONPOW2) {
124 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
125 src_pitch = This->bytesPerPixel * This->pow2Width;
126 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
127 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
128 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
129 } else {
130 mem = This->resource.allocatedMemory;
133 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
134 format, type, mem);
136 if(This->Flags & SFLAG_PBO) {
137 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
138 checkGLcall("glBindBufferARB");
140 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
141 type, NULL);
142 checkGLcall("glGetTexImage()");
144 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
145 checkGLcall("glBindBufferARB");
146 } else {
147 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
148 type, mem);
149 checkGLcall("glGetTexImage()");
151 LEAVE_GL();
153 if (This->Flags & SFLAG_NONPOW2) {
154 LPBYTE src_data, dst_data;
155 int y;
157 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
158 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
159 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
161 * We're doing this...
163 * instead of boxing the texture :
164 * |<-texture width ->| -->pow2width| /\
165 * |111111111111111111| | |
166 * |222 Texture 222222| boxed empty | texture height
167 * |3333 Data 33333333| | |
168 * |444444444444444444| | \/
169 * ----------------------------------- |
170 * | boxed empty | boxed empty | pow2height
171 * | | | \/
172 * -----------------------------------
175 * we're repacking the data to the expected texture width
177 * |<-texture width ->| -->pow2width| /\
178 * |111111111111111111222222222222222| |
179 * |222333333333333333333444444444444| texture height
180 * |444444 | |
181 * | | \/
182 * | | |
183 * | empty | pow2height
184 * | | \/
185 * -----------------------------------
187 * == is the same as
189 * |<-texture width ->| /\
190 * |111111111111111111|
191 * |222222222222222222|texture height
192 * |333333333333333333|
193 * |444444444444444444| \/
194 * --------------------
196 * this also means that any references to allocatedMemory should work with the data as if were a
197 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
199 * internally the texture is still stored in a boxed format so any references to textureName will
200 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
202 * Performance should not be an issue, because applications normally do not lock the surfaces when
203 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
204 * and doesn't have to be re-read.
206 src_data = mem;
207 dst_data = This->resource.allocatedMemory;
208 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
209 for (y = 1 ; y < This->currentDesc.Height; y++) {
210 /* skip the first row */
211 src_data += src_pitch;
212 dst_data += dst_pitch;
213 memcpy(dst_data, src_data, dst_pitch);
216 HeapFree(GetProcessHeap(), 0, mem);
220 /* Surface has now been downloaded */
221 This->Flags |= SFLAG_INSYSMEM;
224 /* This call just uploads data, the caller is responsible for activating the
225 * right context and binding the correct texture. */
226 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
227 if (This->resource.format == WINED3DFMT_DXT1 ||
228 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
229 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
230 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
231 FIXME("Using DXT1/3/5 without advertized support\n");
232 } else {
233 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
234 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
235 * function uses glCompressedTexImage2D instead of the SubImage call
237 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
238 ENTER_GL();
240 if(This->Flags & SFLAG_PBO) {
241 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
242 checkGLcall("glBindBufferARB");
243 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
245 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
246 width, height, 0 /* border */, This->resource.size, NULL));
247 checkGLcall("glCompressedTexSubImage2D");
249 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
250 checkGLcall("glBindBufferARB");
251 } else {
252 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
253 width, height, 0 /* border */, This->resource.size, data));
254 checkGLcall("glCompressedTexSubImage2D");
256 LEAVE_GL();
258 } else {
259 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
260 ENTER_GL();
262 if(This->Flags & SFLAG_PBO) {
263 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
264 checkGLcall("glBindBufferARB");
265 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
267 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
268 checkGLcall("glTexSubImage2D");
270 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
271 checkGLcall("glBindBufferARB");
273 else {
274 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
275 checkGLcall("glTexSubImage2D");
278 LEAVE_GL();
282 /* This call just allocates the texture, the caller is responsible for
283 * activating the right context and binding the correct texture. */
284 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
285 BOOL enable_client_storage = FALSE;
286 BYTE *mem = NULL;
288 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,
289 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
291 if (This->resource.format == WINED3DFMT_DXT1 ||
292 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
293 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
294 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
295 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
297 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
298 * once, unfortunately
300 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
301 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
302 This->Flags |= SFLAG_CLIENT;
303 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
304 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
305 width, height, 0 /* border */, This->resource.size, mem));
308 return;
311 ENTER_GL();
313 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
314 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
315 /* In some cases we want to disable client storage.
316 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
317 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
318 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
319 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
320 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
322 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
323 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
324 This->Flags &= ~SFLAG_CLIENT;
325 enable_client_storage = TRUE;
326 } else {
327 This->Flags |= SFLAG_CLIENT;
329 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
330 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
332 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
335 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
336 checkGLcall("glTexImage2D");
338 if(enable_client_storage) {
339 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
340 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
342 LEAVE_GL();
344 This->Flags |= SFLAG_ALLOCATED;
347 /* In D3D the depth stencil dimensions have to be greater than or equal to the
348 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
349 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
350 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
351 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
352 renderbuffer_entry_t *entry;
353 GLuint renderbuffer = 0;
354 unsigned int src_width, src_height;
356 src_width = This->pow2Width;
357 src_height = This->pow2Height;
359 /* A depth stencil smaller than the render target is not valid */
360 if (width > src_width || height > src_height) return;
362 /* Remove any renderbuffer set if the sizes match */
363 if (width == src_width && height == src_height) {
364 This->current_renderbuffer = NULL;
365 return;
368 /* Look if we've already got a renderbuffer of the correct dimensions */
369 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
370 if (entry->width == width && entry->height == height) {
371 renderbuffer = entry->id;
372 This->current_renderbuffer = entry;
373 break;
377 if (!renderbuffer) {
378 const GlPixelFormatDesc *glDesc;
379 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
381 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
382 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
383 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
385 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
386 entry->width = width;
387 entry->height = height;
388 entry->id = renderbuffer;
389 list_add_head(&This->renderbuffers, &entry->entry);
391 This->current_renderbuffer = entry;
394 checkGLcall("set_compatible_renderbuffer");
397 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
398 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
399 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
401 TRACE("(%p) : swapchain %p\n", This, swapchain);
403 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
404 TRACE("Returning GL_BACK\n");
405 return GL_BACK;
406 } else if (swapchain_impl->frontBuffer == iface) {
407 TRACE("Returning GL_FRONT\n");
408 return GL_FRONT;
411 FIXME("Higher back buffer, returning GL_BACK\n");
412 return GL_BACK;
415 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
416 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
417 ULONG ref = InterlockedDecrement(&This->resource.ref);
418 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
419 if (ref == 0) {
420 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
421 renderbuffer_entry_t *entry, *entry2;
422 TRACE("(%p) : cleaning up\n", This);
424 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
426 /* Need a context to destroy the texture. Use the currently active render target, but only if
427 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
428 * When destroying the primary rt, Uninit3D will activate a context before doing anything
430 if(device->render_targets && device->render_targets[0]) {
431 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
434 TRACE("Deleting texture %d\n", This->glDescription.textureName);
435 ENTER_GL();
436 glDeleteTextures(1, &This->glDescription.textureName);
437 LEAVE_GL();
440 if(This->Flags & SFLAG_PBO) {
441 /* Delete the PBO */
442 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
445 if(This->Flags & SFLAG_DIBSECTION) {
446 /* Release the DC */
447 SelectObject(This->hDC, This->dib.holdbitmap);
448 DeleteDC(This->hDC);
449 /* Release the DIB section */
450 DeleteObject(This->dib.DIBsection);
451 This->dib.bitmap_data = NULL;
452 This->resource.allocatedMemory = NULL;
454 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
456 HeapFree(GetProcessHeap(), 0, This->palette9);
458 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
459 if(iface == device->ddraw_primary)
460 device->ddraw_primary = NULL;
462 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
463 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
464 HeapFree(GetProcessHeap(), 0, entry);
467 TRACE("(%p) Released\n", This);
468 HeapFree(GetProcessHeap(), 0, This);
471 return ref;
474 /* ****************************************************
475 IWineD3DSurface IWineD3DResource parts follow
476 **************************************************** */
478 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
479 /* TODO: check for locks */
480 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
481 IWineD3DBaseTexture *baseTexture = NULL;
482 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
484 TRACE("(%p)Checking to see if the container is a base texture\n", This);
485 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
486 TRACE("Passing to container\n");
487 IWineD3DBaseTexture_PreLoad(baseTexture);
488 IWineD3DBaseTexture_Release(baseTexture);
489 } else {
490 TRACE("(%p) : About to load surface\n", This);
492 if(!device->isInDraw) {
493 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
496 ENTER_GL();
497 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
498 if (!This->glDescription.level) {
499 if (!This->glDescription.textureName) {
500 glGenTextures(1, &This->glDescription.textureName);
501 checkGLcall("glGenTextures");
502 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
504 glBindTexture(This->glDescription.target, This->glDescription.textureName);
505 checkGLcall("glBindTexture");
506 IWineD3DSurface_LoadTexture(iface, FALSE);
507 /* This is where we should be reducing the amount of GLMemoryUsed */
508 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
509 /* assume this is a coding error not a real error for now */
510 FIXME("Mipmap surface has a glTexture bound to it!\n");
512 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
513 /* Tell opengl to try and keep this texture in video ram (well mostly) */
514 GLclampf tmp;
515 tmp = 0.9f;
516 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
518 LEAVE_GL();
520 return;
523 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
524 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
525 This->resource.allocatedMemory =
526 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
528 ENTER_GL();
529 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
530 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
531 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
532 checkGLcall("glGetBufferSubData");
533 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
534 checkGLcall("glDeleteBuffers");
535 LEAVE_GL();
537 This->pbo = 0;
538 This->Flags &= ~SFLAG_PBO;
541 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
542 IWineD3DBaseTexture *texture = NULL;
543 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
544 renderbuffer_entry_t *entry, *entry2;
545 TRACE("(%p)\n", iface);
547 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
548 /* Default pool resources are supposed to be destroyed before Reset is called.
549 * Implicit resources stay however. So this means we have an implicit render target
550 * or depth stencil. The content may be destroyed, but we still have to tear down
551 * opengl resources, so we cannot leave early.
553 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
554 } else {
555 /* Load the surface into system memory */
556 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
558 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
559 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
560 This->Flags &= ~SFLAG_ALLOCATED;
562 /* Destroy PBOs, but load them into real sysmem before */
563 if(This->Flags & SFLAG_PBO) {
564 surface_remove_pbo(This);
567 /* Destroy fbo render buffers. This is needed for implicit render targets, for
568 * all application-created targets the application has to release the surface
569 * before calling _Reset
571 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
572 ENTER_GL();
573 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
574 LEAVE_GL();
575 list_remove(&entry->entry);
576 HeapFree(GetProcessHeap(), 0, entry);
578 list_init(&This->renderbuffers);
579 This->current_renderbuffer = NULL;
581 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
582 * destroy it
584 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
585 if(!texture) {
586 ENTER_GL();
587 glDeleteTextures(1, &This->glDescription.textureName);
588 This->glDescription.textureName = 0;
589 LEAVE_GL();
590 } else {
591 IWineD3DBaseTexture_Release(texture);
593 return;
596 /* ******************************************************
597 IWineD3DSurface IWineD3DSurface parts follow
598 ****************************************************** */
600 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
601 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
602 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
603 if (This->glDescription.textureName == 0 && textureName != 0) {
604 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
605 IWineD3DSurface_AddDirtyRect(iface, NULL);
607 This->glDescription.textureName = textureName;
608 This->glDescription.target = target;
609 This->Flags &= ~SFLAG_ALLOCATED;
612 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
613 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
614 TRACE("(%p) : returning %p\n", This, &This->glDescription);
615 *glDescription = &This->glDescription;
618 /* TODO: think about moving this down to resource? */
619 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
620 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
621 /* 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 */
622 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
623 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
625 return (CONST void*)(This->resource.allocatedMemory);
628 /* Read the framebuffer back into the surface */
629 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
630 IWineD3DSwapChainImpl *swapchain;
631 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
632 BYTE *mem;
633 GLint fmt;
634 GLint type;
635 BYTE *row, *top, *bottom;
636 int i;
637 BOOL bpp;
638 RECT local_rect;
639 BOOL srcIsUpsideDown;
641 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
642 static BOOL warned = FALSE;
643 if(!warned) {
644 ERR("The application tries to lock the render target, but render target locking is disabled\n");
645 warned = TRUE;
647 return;
650 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
651 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
652 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
653 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
654 * context->last_was_blit set on the unlock.
656 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
657 ENTER_GL();
659 /* Select the correct read buffer, and give some debug output.
660 * There is no need to keep track of the current read buffer or reset it, every part of the code
661 * that reads sets the read buffer as desired.
663 if(!swapchain) {
664 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
665 * Read from the back buffer
667 TRACE("Locking offscreen render target\n");
668 glReadBuffer(myDevice->offscreenBuffer);
669 srcIsUpsideDown = TRUE;
670 } else {
671 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
672 TRACE("Locking %#x buffer\n", buffer);
673 glReadBuffer(buffer);
674 checkGLcall("glReadBuffer");
676 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
677 srcIsUpsideDown = FALSE;
680 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
681 if(!rect) {
682 local_rect.left = 0;
683 local_rect.top = 0;
684 local_rect.right = This->currentDesc.Width;
685 local_rect.bottom = This->currentDesc.Height;
686 } else {
687 local_rect = *rect;
689 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
691 switch(This->resource.format)
693 case WINED3DFMT_P8:
695 if(primary_render_target_is_p8(myDevice)) {
696 /* In case of P8 render targets the index is stored in the alpha component */
697 fmt = GL_ALPHA;
698 type = GL_UNSIGNED_BYTE;
699 mem = dest;
700 bpp = This->bytesPerPixel;
701 } else {
702 /* GL can't return palettized data, so read ARGB pixels into a
703 * separate block of memory and convert them into palettized format
704 * in software. Slow, but if the app means to use palettized render
705 * targets and locks it...
707 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
708 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
709 * for the color channels when palettizing the colors.
711 fmt = GL_RGB;
712 type = GL_UNSIGNED_BYTE;
713 pitch *= 3;
714 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
715 if(!mem) {
716 ERR("Out of memory\n");
717 LEAVE_GL();
718 return;
720 bpp = This->bytesPerPixel * 3;
723 break;
725 default:
726 mem = dest;
727 fmt = This->glDescription.glFormat;
728 type = This->glDescription.glType;
729 bpp = This->bytesPerPixel;
732 if(This->Flags & SFLAG_PBO) {
733 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
734 checkGLcall("glBindBufferARB");
737 glReadPixels(local_rect.left, local_rect.top,
738 local_rect.right - local_rect.left,
739 local_rect.bottom - local_rect.top,
740 fmt, type, mem);
741 vcheckGLcall("glReadPixels");
743 if(This->Flags & SFLAG_PBO) {
744 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
745 checkGLcall("glBindBufferARB");
747 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
748 * to get a pointer to it and perform the flipping in software. This is a lot
749 * faster than calling glReadPixels for each line. In case we want more speed
750 * we should rerender it flipped in a FBO and read the data back from the FBO. */
751 if(!srcIsUpsideDown) {
752 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
753 checkGLcall("glBindBufferARB");
755 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
756 checkGLcall("glMapBufferARB");
760 /* TODO: Merge this with the palettization loop below for P8 targets */
761 if(!srcIsUpsideDown) {
762 UINT len, off;
763 /* glReadPixels returns the image upside down, and there is no way to prevent this.
764 Flip the lines in software */
765 len = (local_rect.right - local_rect.left) * bpp;
766 off = local_rect.left * bpp;
768 row = HeapAlloc(GetProcessHeap(), 0, len);
769 if(!row) {
770 ERR("Out of memory\n");
771 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
772 LEAVE_GL();
773 return;
776 top = mem + pitch * local_rect.top;
777 bottom = mem + pitch * ( local_rect.bottom - local_rect.top - 1);
778 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
779 memcpy(row, top + off, len);
780 memcpy(top + off, bottom + off, len);
781 memcpy(bottom + off, row, len);
782 top += pitch;
783 bottom -= pitch;
785 HeapFree(GetProcessHeap(), 0, row);
787 /* Unmap the temp PBO buffer */
788 if(This->Flags & SFLAG_PBO) {
789 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
790 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
794 LEAVE_GL();
796 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
797 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
798 * the same color but we have no choice.
799 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
801 if((This->resource.format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice)) {
802 PALETTEENTRY *pal = NULL;
803 DWORD width = pitch / 3;
804 int x, y, c;
806 if(This->palette) {
807 pal = This->palette->palents;
808 } else {
809 ERR("Palette is missing, cannot perform inverse palette lookup\n");
810 HeapFree(GetProcessHeap(), 0, mem);
811 return ;
814 for(y = local_rect.top; y < local_rect.bottom; y++) {
815 for(x = local_rect.left; x < local_rect.right; x++) {
816 /* start lines pixels */
817 BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
818 BYTE *green = blue + 1;
819 BYTE *red = green + 1;
821 for(c = 0; c < 256; c++) {
822 if(*red == pal[c].peRed &&
823 *green == pal[c].peGreen &&
824 *blue == pal[c].peBlue)
826 *((BYTE *) dest + y * width + x) = c;
827 break;
832 HeapFree(GetProcessHeap(), 0, mem);
836 /* Read the framebuffer contents into a texture */
837 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This)
839 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
840 IWineD3DSwapChainImpl *swapchain;
841 int bpp;
842 GLenum format, internal, type;
843 CONVERT_TYPES convert;
844 BOOL srcIsUpsideDown;
845 GLint prevRead;
847 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
849 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
850 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
851 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
852 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
853 * context->last_was_blit set on the unlock.
855 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
856 surface_bind_and_dirtify(This);
857 ENTER_GL();
859 glGetIntegerv(GL_READ_BUFFER, &prevRead);
861 /* Select the correct read buffer, and give some debug output.
862 * There is no need to keep track of the current read buffer or reset it, every part of the code
863 * that reads sets the read buffer as desired.
865 if(!swapchain) {
866 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
867 * Read from the back buffer
869 TRACE("Locking offscreen render target\n");
870 glReadBuffer(device->offscreenBuffer);
871 srcIsUpsideDown = TRUE;
872 } else {
873 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
874 TRACE("Locking %#x buffer\n", buffer);
875 glReadBuffer(buffer);
876 checkGLcall("glReadBuffer");
878 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
879 srcIsUpsideDown = FALSE;
882 if(!(This->Flags & SFLAG_ALLOCATED)) {
883 surface_allocate_surface(This, internal, This->pow2Width,
884 This->pow2Height, format, type);
887 clear_unused_channels(This);
889 /* If !SrcIsUpsideDown we should flip the surface.
890 * This can be done using glCopyTexSubImage2D but this
891 * is VERY slow, so don't do that. We should prevent
892 * this code from getting called in such cases or perhaps
893 * we can use FBOs */
895 glCopyTexSubImage2D(This->glDescription.target,
896 This->glDescription.level,
897 0, 0, 0, 0,
898 This->currentDesc.Width,
899 This->currentDesc.Height);
900 checkGLcall("glCopyTexSubImage2D");
902 glReadBuffer(prevRead);
903 vcheckGLcall("glReadBuffer");
905 LEAVE_GL();
906 TRACE("Updated target %d\n", This->glDescription.target);
909 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
910 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
911 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
912 * changed
914 if(!(This->Flags & SFLAG_DYNLOCK)) {
915 This->lockCount++;
916 /* MAXLOCKCOUNT is defined in wined3d_private.h */
917 if(This->lockCount > MAXLOCKCOUNT) {
918 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
919 This->Flags |= SFLAG_DYNLOCK;
923 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
924 * Also don't create a PBO for systemmem surfaces.
926 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
927 GLenum error;
928 ENTER_GL();
930 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
931 error = glGetError();
932 if(This->pbo == 0 || error != GL_NO_ERROR) {
933 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
936 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
938 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
939 checkGLcall("glBindBufferARB");
941 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
942 checkGLcall("glBufferDataARB");
944 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
945 checkGLcall("glBindBufferARB");
947 /* We don't need the system memory anymore and we can't even use it for PBOs */
948 if(!(This->Flags & SFLAG_CLIENT)) {
949 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
950 This->resource.heapMemory = NULL;
952 This->resource.allocatedMemory = NULL;
953 This->Flags |= SFLAG_PBO;
954 LEAVE_GL();
955 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
956 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
957 * or a pbo to map
959 if(!This->resource.heapMemory) {
960 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
962 This->resource.allocatedMemory =
963 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
964 if(This->Flags & SFLAG_INSYSMEM) {
965 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
970 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
971 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
972 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
973 IWineD3DSwapChain *swapchain = NULL;
975 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
977 /* This is also done in the base class, but we have to verify this before loading any data from
978 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
979 * may interfere, and all other bad things may happen
981 if (This->Flags & SFLAG_LOCKED) {
982 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
983 return WINED3DERR_INVALIDCALL;
985 This->Flags |= SFLAG_LOCKED;
987 if (!(This->Flags & SFLAG_LOCKABLE))
989 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
992 if (Flags & WINED3DLOCK_DISCARD) {
993 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
994 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
995 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
996 This->Flags |= SFLAG_INSYSMEM;
997 goto lock_end;
1000 if (This->Flags & SFLAG_INSYSMEM) {
1001 TRACE("Local copy is up to date, not downloading data\n");
1002 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1003 goto lock_end;
1006 /* Now download the surface content from opengl
1007 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1008 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
1010 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1011 if(swapchain || iface == myDevice->render_targets[0]) {
1012 const RECT *pass_rect = pRect;
1014 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1015 * because most caller functions do not need that. So do that here
1017 if(pRect &&
1018 pRect->top == 0 &&
1019 pRect->left == 0 &&
1020 pRect->right == This->currentDesc.Width &&
1021 pRect->bottom == This->currentDesc.Height) {
1022 pass_rect = NULL;
1025 switch(wined3d_settings.rendertargetlock_mode) {
1026 case RTL_TEXDRAW:
1027 case RTL_TEXTEX:
1028 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1029 #if 0
1030 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1031 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1032 * This may be faster on some cards
1034 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1035 #endif
1036 /* drop through */
1038 case RTL_AUTO:
1039 case RTL_READDRAW:
1040 case RTL_READTEX:
1041 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
1042 break;
1044 case RTL_DISABLE:
1045 break;
1047 if(swapchain) IWineD3DSwapChain_Release(swapchain);
1049 } else if(iface == myDevice->stencilBufferTarget) {
1050 /** the depth stencil in openGL has a format of GL_FLOAT
1051 * which should be good for WINED3DFMT_D16_LOCKABLE
1052 * and WINED3DFMT_D16
1053 * it is unclear what format the stencil buffer is in except.
1054 * 'Each index is converted to fixed point...
1055 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1056 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1057 * glReadPixels(This->lockedRect.left,
1058 * This->lockedRect.bottom - j - 1,
1059 * This->lockedRect.right - This->lockedRect.left,
1060 * 1,
1061 * GL_DEPTH_COMPONENT,
1062 * type,
1063 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1065 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1066 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1067 * none of that is the case the problem is not in this function :-)
1068 ********************************************/
1069 FIXME("Depth stencil locking not supported yet\n");
1070 } else {
1071 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1072 TRACE("locking an ordinary surface\n");
1073 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1076 lock_end:
1077 if(This->Flags & SFLAG_PBO) {
1078 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1079 ENTER_GL();
1080 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1081 checkGLcall("glBindBufferARB");
1083 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1084 if(This->resource.allocatedMemory) {
1085 ERR("The surface already has PBO memory allocated!\n");
1088 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1089 checkGLcall("glMapBufferARB");
1091 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1092 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1093 checkGLcall("glBindBufferARB");
1095 LEAVE_GL();
1098 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1099 /* Don't dirtify */
1100 } else {
1101 IWineD3DBaseTexture *pBaseTexture;
1103 * Dirtify on lock
1104 * as seen in msdn docs
1106 IWineD3DSurface_AddDirtyRect(iface, pRect);
1108 /** Dirtify Container if needed */
1109 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
1110 TRACE("Making container dirty\n");
1111 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1112 IWineD3DBaseTexture_Release(pBaseTexture);
1113 } else {
1114 TRACE("Surface is standalone, no need to dirty the container\n");
1118 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1121 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1122 GLint prev_store;
1123 GLint prev_rasterpos[4];
1124 GLint skipBytes = 0;
1125 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1126 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1127 IWineD3DSwapChainImpl *swapchain;
1129 /* Activate the correct context for the render target */
1130 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1131 ENTER_GL();
1133 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
1134 if(!swapchain) {
1135 /* Primary offscreen render target */
1136 TRACE("Offscreen render target\n");
1137 glDrawBuffer(myDevice->offscreenBuffer);
1138 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1139 } else {
1140 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1141 TRACE("Unlocking %#x buffer\n", buffer);
1142 glDrawBuffer(buffer);
1143 checkGLcall("glDrawBuffer");
1145 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1148 glFlush();
1149 vcheckGLcall("glFlush");
1150 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1151 vcheckGLcall("glIntegerv");
1152 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1153 vcheckGLcall("glIntegerv");
1154 glPixelZoom(1.0, -1.0);
1155 vcheckGLcall("glPixelZoom");
1157 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1158 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1159 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1161 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1162 vcheckGLcall("glRasterPos2f");
1164 /* Some drivers(radeon dri, others?) don't like exceptions during
1165 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1166 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1167 * catch to put the dib section in InSync mode, which leads to a crash
1168 * and a blocked x server on my radeon card.
1170 * The following lines read the dib section so it is put in inSync mode
1171 * before glDrawPixels is called and the crash is prevented. There won't
1172 * be any interfering gdi accesses, because UnlockRect is called from
1173 * ReleaseDC, and the app won't use the dc any more afterwards.
1175 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1176 volatile BYTE read;
1177 read = This->resource.allocatedMemory[0];
1180 if(This->Flags & SFLAG_PBO) {
1181 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1182 checkGLcall("glBindBufferARB");
1185 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1186 if(This->Flags & SFLAG_LOCKED) {
1187 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1188 (This->lockedRect.bottom - This->lockedRect.top)-1,
1189 fmt, type,
1190 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1191 checkGLcall("glDrawPixels");
1192 } else {
1193 glDrawPixels(This->currentDesc.Width,
1194 This->currentDesc.Height,
1195 fmt, type, mem);
1196 checkGLcall("glDrawPixels");
1199 if(This->Flags & SFLAG_PBO) {
1200 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1201 checkGLcall("glBindBufferARB");
1204 glPixelZoom(1.0,1.0);
1205 vcheckGLcall("glPixelZoom");
1207 glRasterPos3iv(&prev_rasterpos[0]);
1208 vcheckGLcall("glRasterPos3iv");
1210 /* Reset to previous pack row length */
1211 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1212 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1214 if(!swapchain) {
1215 glDrawBuffer(myDevice->offscreenBuffer);
1216 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1217 } else if(swapchain->backBuffer) {
1218 glDrawBuffer(GL_BACK);
1219 checkGLcall("glDrawBuffer(GL_BACK)");
1220 } else {
1221 glDrawBuffer(GL_FRONT);
1222 checkGLcall("glDrawBuffer(GL_FRONT)");
1224 LEAVE_GL();
1226 return;
1229 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1230 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1231 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1232 IWineD3DSwapChainImpl *swapchain = NULL;
1233 BOOL fullsurface;
1235 if (!(This->Flags & SFLAG_LOCKED)) {
1236 WARN("trying to Unlock an unlocked surf@%p\n", This);
1237 return WINED3DERR_INVALIDCALL;
1240 if (This->Flags & SFLAG_PBO) {
1241 TRACE("Freeing PBO memory\n");
1242 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1243 ENTER_GL();
1244 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1245 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1246 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1247 checkGLcall("glUnmapBufferARB");
1248 LEAVE_GL();
1249 This->resource.allocatedMemory = NULL;
1252 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1254 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1255 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1256 goto unlock_end;
1259 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1260 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1261 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1263 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1264 static BOOL warned = FALSE;
1265 if(!warned) {
1266 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1267 warned = TRUE;
1269 goto unlock_end;
1272 if(This->dirtyRect.left == 0 &&
1273 This->dirtyRect.top == 0 &&
1274 This->dirtyRect.right == This->currentDesc.Width &&
1275 This->dirtyRect.bottom == This->currentDesc.Height) {
1276 fullsurface = TRUE;
1277 } else {
1278 /* TODO: Proper partial rectangle tracking */
1279 fullsurface = FALSE;
1280 This->Flags |= SFLAG_INSYSMEM;
1283 switch(wined3d_settings.rendertargetlock_mode) {
1284 case RTL_READTEX:
1285 case RTL_TEXTEX:
1286 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1287 ENTER_GL();
1288 if (This->glDescription.textureName == 0) {
1289 glGenTextures(1, &This->glDescription.textureName);
1290 checkGLcall("glGenTextures");
1292 glBindTexture(This->glDescription.target, This->glDescription.textureName);
1293 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
1294 LEAVE_GL();
1295 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1296 /* drop through */
1298 case RTL_AUTO:
1299 case RTL_READDRAW:
1300 case RTL_TEXDRAW:
1301 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1302 break;
1305 if(!fullsurface) {
1306 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1307 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1308 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1309 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1310 * not fully up to date because only a subrectangle was read in LockRect.
1312 This->Flags &= ~SFLAG_INSYSMEM;
1313 This->Flags |= SFLAG_INDRAWABLE;
1316 This->dirtyRect.left = This->currentDesc.Width;
1317 This->dirtyRect.top = This->currentDesc.Height;
1318 This->dirtyRect.right = 0;
1319 This->dirtyRect.bottom = 0;
1320 } else if(iface == myDevice->stencilBufferTarget) {
1321 FIXME("Depth Stencil buffer locking is not implemented\n");
1322 } else {
1323 /* The rest should be a normal texture */
1324 IWineD3DBaseTextureImpl *impl;
1325 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1326 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1327 * states need resetting
1329 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1330 if(impl->baseTexture.bindCount) {
1331 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1333 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1337 unlock_end:
1338 This->Flags &= ~SFLAG_LOCKED;
1339 memset(&This->lockedRect, 0, sizeof(RECT));
1340 return WINED3D_OK;
1343 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1344 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1345 WINED3DLOCKED_RECT lock;
1346 HRESULT hr;
1347 RGBQUAD col[256];
1349 TRACE("(%p)->(%p)\n",This,pHDC);
1351 if(This->Flags & SFLAG_USERPTR) {
1352 ERR("Not supported on surfaces with an application-provided surfaces\n");
1353 return WINEDDERR_NODC;
1356 /* Give more detailed info for ddraw */
1357 if (This->Flags & SFLAG_DCINUSE)
1358 return WINEDDERR_DCALREADYCREATED;
1360 /* Can't GetDC if the surface is locked */
1361 if (This->Flags & SFLAG_LOCKED)
1362 return WINED3DERR_INVALIDCALL;
1364 /* According to Direct3D9 docs, only these formats are supported */
1365 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1366 if (This->resource.format != WINED3DFMT_R5G6B5 &&
1367 This->resource.format != WINED3DFMT_X1R5G5B5 &&
1368 This->resource.format != WINED3DFMT_R8G8B8 &&
1369 This->resource.format != WINED3DFMT_X8R8G8B8) return WINED3DERR_INVALIDCALL;
1372 memset(&lock, 0, sizeof(lock)); /* To be sure */
1374 /* Create a DIB section if there isn't a hdc yet */
1375 if(!This->hDC) {
1376 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1377 if(This->Flags & SFLAG_CLIENT) {
1378 IWineD3DSurface_PreLoad(iface);
1381 /* Use the dib section from now on if we are not using a PBO */
1382 if(!(This->Flags & SFLAG_PBO))
1383 This->resource.allocatedMemory = This->dib.bitmap_data;
1386 /* Lock the surface */
1387 hr = IWineD3DSurface_LockRect(iface,
1388 &lock,
1389 NULL,
1392 if(This->Flags & SFLAG_PBO) {
1393 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1394 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1397 if(FAILED(hr)) {
1398 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1399 /* keep the dib section */
1400 return hr;
1403 if(This->resource.format == WINED3DFMT_P8 ||
1404 This->resource.format == WINED3DFMT_A8P8) {
1405 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1406 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1407 unsigned int n;
1408 PALETTEENTRY *pal = NULL;
1410 if(This->palette) {
1411 pal = This->palette->palents;
1412 } else {
1413 IWineD3DSurfaceImpl *dds_primary = (IWineD3DSurfaceImpl *)This->resource.wineD3DDevice->ddraw_primary;
1414 if (dds_primary && dds_primary->palette)
1415 pal = dds_primary->palette->palents;
1418 if (pal) {
1419 for (n=0; n<256; n++) {
1420 col[n].rgbRed = pal[n].peRed;
1421 col[n].rgbGreen = pal[n].peGreen;
1422 col[n].rgbBlue = pal[n].peBlue;
1423 col[n].rgbReserved = 0;
1425 SetDIBColorTable(This->hDC, 0, 256, col);
1429 *pHDC = This->hDC;
1430 TRACE("returning %p\n",*pHDC);
1431 This->Flags |= SFLAG_DCINUSE;
1433 return WINED3D_OK;
1436 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1437 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1439 TRACE("(%p)->(%p)\n",This,hDC);
1441 if (!(This->Flags & SFLAG_DCINUSE))
1442 return WINED3DERR_INVALIDCALL;
1444 if (This->hDC !=hDC) {
1445 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1446 return WINED3DERR_INVALIDCALL;
1449 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1450 /* Copy the contents of the DIB over to the PBO */
1451 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1454 /* we locked first, so unlock now */
1455 IWineD3DSurface_UnlockRect(iface);
1457 This->Flags &= ~SFLAG_DCINUSE;
1459 return WINED3D_OK;
1462 /* ******************************************************
1463 IWineD3DSurface Internal (No mapping to directx api) parts follow
1464 ****************************************************** */
1466 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) {
1467 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1468 const GlPixelFormatDesc *glDesc;
1469 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1470 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1472 /* Default values: From the surface */
1473 *format = glDesc->glFormat;
1474 *type = glDesc->glType;
1475 *convert = NO_CONVERSION;
1476 *target_bpp = This->bytesPerPixel;
1478 if(srgb_mode) {
1479 *internal = glDesc->glGammaInternal;
1480 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1481 *internal = glDesc->rtInternal;
1482 } else {
1483 *internal = glDesc->glInternal;
1486 /* Ok, now look if we have to do any conversion */
1487 switch(This->resource.format) {
1488 case WINED3DFMT_P8:
1489 /* ****************
1490 Paletted Texture
1491 **************** */
1493 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1494 * of the two is available make sure texturing is requested as neither of the two works in
1495 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1496 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1497 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1498 * conflicts with this.
1500 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) && primary_render_target_is_p8(device))) || colorkey_active || !use_texturing ) {
1501 *format = GL_RGBA;
1502 *internal = GL_RGBA;
1503 *type = GL_UNSIGNED_BYTE;
1504 *target_bpp = 4;
1505 if(colorkey_active) {
1506 *convert = CONVERT_PALETTED_CK;
1507 } else {
1508 *convert = CONVERT_PALETTED;
1511 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1512 *format = GL_ALPHA;
1513 *internal = GL_RGBA;
1514 *type = GL_UNSIGNED_BYTE;
1515 *target_bpp = 1;
1518 break;
1520 case WINED3DFMT_R3G3B2:
1521 /* **********************
1522 GL_UNSIGNED_BYTE_3_3_2
1523 ********************** */
1524 if (colorkey_active) {
1525 /* This texture format will never be used.. So do not care about color keying
1526 up until the point in time it will be needed :-) */
1527 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1529 break;
1531 case WINED3DFMT_R5G6B5:
1532 if (colorkey_active) {
1533 *convert = CONVERT_CK_565;
1534 *format = GL_RGBA;
1535 *internal = GL_RGBA;
1536 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1538 break;
1540 case WINED3DFMT_X1R5G5B5:
1541 if (colorkey_active) {
1542 *convert = CONVERT_CK_5551;
1543 *format = GL_BGRA;
1544 *internal = GL_RGBA;
1545 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1547 break;
1549 case WINED3DFMT_R8G8B8:
1550 if (colorkey_active) {
1551 *convert = CONVERT_CK_RGB24;
1552 *format = GL_RGBA;
1553 *internal = GL_RGBA;
1554 *type = GL_UNSIGNED_INT_8_8_8_8;
1555 *target_bpp = 4;
1557 break;
1559 case WINED3DFMT_X8R8G8B8:
1560 if (colorkey_active) {
1561 *convert = CONVERT_RGB32_888;
1562 *format = GL_RGBA;
1563 *internal = GL_RGBA;
1564 *type = GL_UNSIGNED_INT_8_8_8_8;
1566 break;
1568 case WINED3DFMT_V8U8:
1569 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1570 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1571 *format = GL_DUDV_ATI;
1572 *internal = GL_DU8DV8_ATI;
1573 *type = GL_BYTE;
1574 /* No conversion - Just change the gl type */
1575 break;
1577 *convert = CONVERT_V8U8;
1578 *format = GL_BGR;
1579 *internal = GL_RGB8;
1580 *type = GL_UNSIGNED_BYTE;
1581 *target_bpp = 3;
1582 break;
1584 case WINED3DFMT_L6V5U5:
1585 *convert = CONVERT_L6V5U5;
1586 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1587 *target_bpp = 3;
1588 /* Use format and types from table */
1589 } else {
1590 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1591 *target_bpp = 2;
1592 *format = GL_RGB;
1593 *internal = GL_RGB5;
1594 *type = GL_UNSIGNED_SHORT_5_6_5;
1596 break;
1598 case WINED3DFMT_X8L8V8U8:
1599 *convert = CONVERT_X8L8V8U8;
1600 *target_bpp = 4;
1601 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1602 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1603 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1604 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1605 * the needed type and format parameter, so the internal format contains a
1606 * 4th component, which is returned as alpha
1608 } else {
1609 /* Not supported by GL_ATI_envmap_bumpmap */
1610 *format = GL_BGRA;
1611 *internal = GL_RGB8;
1612 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1614 break;
1616 case WINED3DFMT_Q8W8V8U8:
1617 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1618 *convert = CONVERT_Q8W8V8U8;
1619 *format = GL_BGRA;
1620 *internal = GL_RGBA8;
1621 *type = GL_UNSIGNED_BYTE;
1622 *target_bpp = 4;
1623 /* Not supported by GL_ATI_envmap_bumpmap */
1624 break;
1626 case WINED3DFMT_V16U16:
1627 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1628 *convert = CONVERT_V16U16;
1629 *format = GL_BGR;
1630 *internal = GL_RGB16_EXT;
1631 *type = GL_UNSIGNED_SHORT;
1632 *target_bpp = 6;
1633 /* What should I do here about GL_ATI_envmap_bumpmap?
1634 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1636 break;
1638 case WINED3DFMT_A4L4:
1639 /* A4L4 exists as an internal gl format, but for some reason there is not
1640 * format+type combination to load it. Thus convert it to A8L8, then load it
1641 * with A4L4 internal, but A8L8 format+type
1643 *convert = CONVERT_A4L4;
1644 *format = GL_LUMINANCE_ALPHA;
1645 *internal = GL_LUMINANCE4_ALPHA4;
1646 *type = GL_UNSIGNED_BYTE;
1647 *target_bpp = 2;
1648 break;
1650 case WINED3DFMT_R32F:
1651 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1652 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1653 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1654 * 1.0 instead.
1656 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1658 *convert = CONVERT_R32F;
1659 *format = GL_RGB;
1660 *internal = GL_RGB32F_ARB;
1661 *type = GL_FLOAT;
1662 *target_bpp = 12;
1663 break;
1665 case WINED3DFMT_R16F:
1666 /* Similar to R32F */
1667 *convert = CONVERT_R16F;
1668 *format = GL_RGB;
1669 *internal = GL_RGB16F_ARB;
1670 *type = GL_HALF_FLOAT_ARB;
1671 *target_bpp = 6;
1672 break;
1674 case WINED3DFMT_G16R16:
1675 *convert = CONVERT_G16R16;
1676 *format = GL_RGB;
1677 *internal = GL_RGB16_EXT;
1678 *type = GL_UNSIGNED_SHORT;
1679 *target_bpp = 6;
1680 break;
1682 default:
1683 break;
1686 return WINED3D_OK;
1689 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1690 BYTE *source, *dest;
1691 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1693 switch (convert) {
1694 case NO_CONVERSION:
1696 memcpy(dst, src, pitch * height);
1697 break;
1699 case CONVERT_PALETTED:
1700 case CONVERT_PALETTED_CK:
1702 IWineD3DPaletteImpl* pal = This->palette;
1703 BYTE table[256][4];
1704 unsigned int x, y;
1706 if( pal == NULL) {
1707 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1710 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1712 for (y = 0; y < height; y++)
1714 source = src + pitch * y;
1715 dest = dst + outpitch * y;
1716 /* This is an 1 bpp format, using the width here is fine */
1717 for (x = 0; x < width; x++) {
1718 BYTE color = *source++;
1719 *dest++ = table[color][0];
1720 *dest++ = table[color][1];
1721 *dest++ = table[color][2];
1722 *dest++ = table[color][3];
1726 break;
1728 case CONVERT_CK_565:
1730 /* Converting the 565 format in 5551 packed to emulate color-keying.
1732 Note : in all these conversion, it would be best to average the averaging
1733 pixels to get the color of the pixel that will be color-keyed to
1734 prevent 'color bleeding'. This will be done later on if ever it is
1735 too visible.
1737 Note2: Nvidia documents say that their driver does not support alpha + color keying
1738 on the same surface and disables color keying in such a case
1740 unsigned int x, y;
1741 WORD *Source;
1742 WORD *Dest;
1744 TRACE("Color keyed 565\n");
1746 for (y = 0; y < height; y++) {
1747 Source = (WORD *) (src + y * pitch);
1748 Dest = (WORD *) (dst + y * outpitch);
1749 for (x = 0; x < width; x++ ) {
1750 WORD color = *Source++;
1751 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1752 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1753 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1754 *Dest |= 0x0001;
1756 Dest++;
1760 break;
1762 case CONVERT_CK_5551:
1764 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1765 unsigned int x, y;
1766 WORD *Source;
1767 WORD *Dest;
1768 TRACE("Color keyed 5551\n");
1769 for (y = 0; y < height; y++) {
1770 Source = (WORD *) (src + y * pitch);
1771 Dest = (WORD *) (dst + y * outpitch);
1772 for (x = 0; x < width; x++ ) {
1773 WORD color = *Source++;
1774 *Dest = color;
1775 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1776 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1777 *Dest |= (1 << 15);
1779 else {
1780 *Dest &= ~(1 << 15);
1782 Dest++;
1786 break;
1788 case CONVERT_V8U8:
1790 unsigned int x, y;
1791 short *Source;
1792 unsigned char *Dest;
1793 for(y = 0; y < height; y++) {
1794 Source = (short *) (src + y * pitch);
1795 Dest = dst + y * outpitch;
1796 for (x = 0; x < width; x++ ) {
1797 long color = (*Source++);
1798 /* B */ Dest[0] = 0xff;
1799 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1800 /* R */ Dest[2] = (color) + 128; /* U */
1801 Dest += 3;
1804 break;
1807 case CONVERT_V16U16:
1809 unsigned int x, y;
1810 DWORD *Source;
1811 unsigned short *Dest;
1812 for(y = 0; y < height; y++) {
1813 Source = (DWORD *) (src + y * pitch);
1814 Dest = (unsigned short *) (dst + y * outpitch);
1815 for (x = 0; x < width; x++ ) {
1816 DWORD color = (*Source++);
1817 /* B */ Dest[0] = 0xffff;
1818 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1819 /* R */ Dest[2] = (color ) + 32768; /* U */
1820 Dest += 3;
1823 break;
1826 case CONVERT_Q8W8V8U8:
1828 unsigned int x, y;
1829 DWORD *Source;
1830 unsigned char *Dest;
1831 for(y = 0; y < height; y++) {
1832 Source = (DWORD *) (src + y * pitch);
1833 Dest = dst + y * outpitch;
1834 for (x = 0; x < width; x++ ) {
1835 long color = (*Source++);
1836 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1837 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1838 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1839 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1840 Dest += 4;
1843 break;
1846 case CONVERT_L6V5U5:
1848 unsigned int x, y;
1849 WORD *Source;
1850 unsigned char *Dest;
1852 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1853 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1854 * fixed function and shaders without further conversion once the surface is
1855 * loaded
1857 for(y = 0; y < height; y++) {
1858 Source = (WORD *) (src + y * pitch);
1859 Dest = dst + y * outpitch;
1860 for (x = 0; x < width; x++ ) {
1861 short color = (*Source++);
1862 unsigned char l = ((color >> 10) & 0xfc);
1863 char v = ((color >> 5) & 0x3e);
1864 char u = ((color ) & 0x1f);
1866 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1867 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1868 * shift. GL reads a signed value and converts it into an unsigned value.
1870 /* M */ Dest[2] = l << 1;
1872 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1873 * from 5 bit values to 8 bit values.
1875 /* V */ Dest[1] = v << 3;
1876 /* U */ Dest[0] = u << 3;
1877 Dest += 3;
1880 } else {
1881 for(y = 0; y < height; y++) {
1882 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1883 Source = (WORD *) (src + y * pitch);
1884 for (x = 0; x < width; x++ ) {
1885 short color = (*Source++);
1886 unsigned char l = ((color >> 10) & 0xfc);
1887 short v = ((color >> 5) & 0x3e);
1888 short u = ((color ) & 0x1f);
1889 short v_conv = v + 16;
1890 short u_conv = u + 16;
1892 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1893 Dest_s += 1;
1897 break;
1900 case CONVERT_X8L8V8U8:
1902 unsigned int x, y;
1903 DWORD *Source;
1904 unsigned char *Dest;
1906 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1907 /* This implementation works with the fixed function pipeline and shaders
1908 * without further modification after converting the surface.
1910 for(y = 0; y < height; y++) {
1911 Source = (DWORD *) (src + y * pitch);
1912 Dest = dst + y * outpitch;
1913 for (x = 0; x < width; x++ ) {
1914 long color = (*Source++);
1915 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1916 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1917 /* U */ Dest[0] = (color & 0xff); /* U */
1918 /* I */ Dest[3] = 255; /* X */
1919 Dest += 4;
1922 } else {
1923 /* Doesn't work correctly with the fixed function pipeline, but can work in
1924 * shaders if the shader is adjusted. (There's no use for this format in gl's
1925 * standard fixed function pipeline anyway).
1927 for(y = 0; y < height; y++) {
1928 Source = (DWORD *) (src + y * pitch);
1929 Dest = dst + y * outpitch;
1930 for (x = 0; x < width; x++ ) {
1931 long color = (*Source++);
1932 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1933 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1934 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1935 Dest += 4;
1939 break;
1942 case CONVERT_A4L4:
1944 unsigned int x, y;
1945 unsigned char *Source;
1946 unsigned char *Dest;
1947 for(y = 0; y < height; y++) {
1948 Source = src + y * pitch;
1949 Dest = dst + y * outpitch;
1950 for (x = 0; x < width; x++ ) {
1951 unsigned char color = (*Source++);
1952 /* A */ Dest[1] = (color & 0xf0) << 0;
1953 /* L */ Dest[0] = (color & 0x0f) << 4;
1954 Dest += 2;
1957 break;
1960 case CONVERT_R32F:
1962 unsigned int x, y;
1963 float *Source;
1964 float *Dest;
1965 for(y = 0; y < height; y++) {
1966 Source = (float *) (src + y * pitch);
1967 Dest = (float *) (dst + y * outpitch);
1968 for (x = 0; x < width; x++ ) {
1969 float color = (*Source++);
1970 Dest[0] = color;
1971 Dest[1] = 1.0;
1972 Dest[2] = 1.0;
1973 Dest += 3;
1976 break;
1979 case CONVERT_R16F:
1981 unsigned int x, y;
1982 WORD *Source;
1983 WORD *Dest;
1984 WORD one = 0x3c00;
1985 for(y = 0; y < height; y++) {
1986 Source = (WORD *) (src + y * pitch);
1987 Dest = (WORD *) (dst + y * outpitch);
1988 for (x = 0; x < width; x++ ) {
1989 WORD color = (*Source++);
1990 Dest[0] = color;
1991 Dest[1] = one;
1992 Dest[2] = one;
1993 Dest += 3;
1996 break;
1999 case CONVERT_G16R16:
2001 unsigned int x, y;
2002 WORD *Source;
2003 WORD *Dest;
2005 for(y = 0; y < height; y++) {
2006 Source = (WORD *) (src + y * pitch);
2007 Dest = (WORD *) (dst + y * outpitch);
2008 for (x = 0; x < width; x++ ) {
2009 WORD green = (*Source++);
2010 WORD red = (*Source++);
2011 Dest[0] = green;
2012 Dest[1] = red;
2013 Dest[2] = 0xffff;
2014 Dest += 3;
2017 break;
2020 default:
2021 ERR("Unsupported conversation type %d\n", convert);
2023 return WINED3D_OK;
2026 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2027 IWineD3DPaletteImpl* pal = This->palette;
2028 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2029 BOOL index_in_alpha = FALSE;
2030 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2031 int i;
2033 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2034 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2035 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2036 * duplicate entries. Store the color key in the unused alpha component to speed the
2037 * download up and to make conversion unneeded. */
2038 index_in_alpha = primary_render_target_is_p8(device);
2040 if (pal == NULL) {
2041 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2042 if(dxVersion <= 7) {
2043 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2044 return;
2047 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2048 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2049 capability flag is present (wine does advertise this capability) */
2050 for (i = 0; i < 256; i++) {
2051 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2052 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2053 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2054 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2056 } else {
2057 TRACE("Using surface palette %p\n", pal);
2058 /* Get the surface's palette */
2059 for (i = 0; i < 256; i++) {
2060 table[i][0] = pal->palents[i].peRed;
2061 table[i][1] = pal->palents[i].peGreen;
2062 table[i][2] = pal->palents[i].peBlue;
2064 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2065 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2066 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2067 of pixels that should be masked away is set to 0. */
2068 if(index_in_alpha) {
2069 table[i][3] = i;
2070 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2071 table[i][3] = 0x00;
2072 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2073 table[i][3] = pal->palents[i].peFlags;
2074 } else {
2075 table[i][3] = 0xFF;
2081 const char *fragment_palette_conversion =
2082 "!!ARBfp1.0\n"
2083 "TEMP index;\n"
2084 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
2085 "TEX index, fragment.texcoord[0], texture[0], 2D;\n" /* The alpha-component contains the palette index */
2086 "MAD index.a, index.a, constants.x, constants.y;\n" /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2087 "TEX result.color, index.a, texture[1], 1D;\n" /* use the alpha-component as a index in the palette to get the final color */
2088 "END";
2090 /* This function is used in case of 8bit paletted textures to upload the palette.
2091 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2092 extensions like ATI_fragment_shaders is possible.
2094 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2095 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2096 BYTE table[256][4];
2097 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2099 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2101 /* Try to use the paletted texture extension */
2102 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2104 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2105 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2107 else
2109 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2110 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2111 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2113 /* Create the fragment program if we don't have it */
2114 if(!device->paletteConversionShader)
2116 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2117 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2118 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2119 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2120 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2123 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2124 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2126 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2127 glEnable(GL_TEXTURE_1D);
2128 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2130 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2131 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2132 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2133 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2135 /* Switch back to unit 0 in which the 2D texture will be stored. */
2136 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2138 /* Rebind the texture because it isn't bound anymore */
2139 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2143 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2144 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2146 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2147 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2148 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2150 return FALSE;
2153 if(This->palette9) {
2154 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2155 return FALSE;
2157 } else {
2158 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2160 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2161 return TRUE;
2164 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2165 GLboolean oldwrite[4];
2167 /* Some formats have only some color channels, and the others are 1.0.
2168 * since our rendering renders to all channels, and those pixel formats
2169 * are emulated by using a full texture with the other channels set to 1.0
2170 * manually, clear the unused channels.
2172 * This could be done with hacking colorwriteenable to mask the colors,
2173 * but before drawing the buffer would have to be cleared too, so there's
2174 * no gain in that
2176 switch(This->resource.format) {
2177 case WINED3DFMT_R16F:
2178 case WINED3DFMT_R32F:
2179 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2180 /* Do not activate a context, the correct drawable is active already
2181 * though just the read buffer is set, make sure to have the correct draw
2182 * buffer too
2184 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2185 glDisable(GL_SCISSOR_TEST);
2186 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2187 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2188 glClearColor(0.0, 1.0, 1.0, 1.0);
2189 glClear(GL_COLOR_BUFFER_BIT);
2190 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2191 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2192 checkGLcall("Unused channel clear\n");
2193 break;
2195 default: break;
2199 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2200 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2202 if (!(This->Flags & SFLAG_INTEXTURE)) {
2203 TRACE("Reloading because surface is dirty\n");
2204 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2205 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2206 /* Reload: vice versa OR */
2207 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2208 /* Also reload: Color key is active AND the color key has changed */
2209 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2210 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2211 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2212 TRACE("Reloading because of color keying\n");
2213 /* To perform the color key conversion we need a sysmem copy of
2214 * the surface. Make sure we have it
2217 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2218 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2219 /* TODO: This is not necessarily needed with hw palettized texture support */
2220 This->Flags &= ~SFLAG_INTEXTURE;
2221 } else if(palette9_changed(This)) {
2222 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2223 /* TODO: This is not necessarily needed with hw palettized texture support */
2224 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2226 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2227 This->Flags &= ~SFLAG_INTEXTURE;
2228 } else {
2229 TRACE("surface is already in texture\n");
2230 return WINED3D_OK;
2233 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2234 * These resources are not bound by device size or format restrictions. Because of this,
2235 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2236 * However, these resources can always be created, locked, and copied.
2238 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2240 FIXME("(%p) Operation not supported for scratch textures\n",This);
2241 return WINED3DERR_INVALIDCALL;
2244 This->srgb = srgb_mode;
2245 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2247 #if 0
2249 static unsigned int gen = 0;
2250 char buffer[4096];
2251 ++gen;
2252 if ((gen % 10) == 0) {
2253 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2254 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2257 * debugging crash code
2258 if (gen == 250) {
2259 void** test = NULL;
2260 *test = 0;
2264 #endif
2266 if (!(This->Flags & SFLAG_DONOTFREE)) {
2267 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2268 This->resource.allocatedMemory = NULL;
2269 This->resource.heapMemory = NULL;
2270 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2273 return WINED3D_OK;
2276 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2277 /* TODO: check for locks */
2278 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2279 IWineD3DBaseTexture *baseTexture = NULL;
2280 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2282 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2283 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2284 TRACE("Passing to container\n");
2285 IWineD3DBaseTexture_BindTexture(baseTexture);
2286 IWineD3DBaseTexture_Release(baseTexture);
2287 } else {
2288 TRACE("(%p) : Binding surface\n", This);
2290 if(!device->isInDraw) {
2291 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2293 ENTER_GL();
2294 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2295 LEAVE_GL();
2297 return;
2300 #include <errno.h>
2301 #include <stdio.h>
2302 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2303 FILE* f = NULL;
2304 UINT i, y;
2305 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2306 char *allocatedMemory;
2307 char *textureRow;
2308 IWineD3DSwapChain *swapChain = NULL;
2309 int width, height;
2310 GLuint tmpTexture = 0;
2311 DWORD color;
2312 /*FIXME:
2313 Textures may not be stored in ->allocatedgMemory and a GlTexture
2314 so we should lock the surface before saving a snapshot, or at least check that
2316 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2317 by calling GetTexImage and in compressed form by calling
2318 GetCompressedTexImageARB. Queried compressed images can be saved and
2319 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2320 texture images do not need to be processed by the GL and should
2321 significantly improve texture loading performance relative to uncompressed
2322 images. */
2324 /* Setup the width and height to be the internal texture width and height. */
2325 width = This->pow2Width;
2326 height = This->pow2Height;
2327 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2328 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2330 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2331 /* if were not a real texture then read the back buffer into a real texture */
2332 /* we don't want to interfere with the back buffer so read the data into a temporary
2333 * texture and then save the data out of the temporary texture
2335 GLint prevRead;
2336 ENTER_GL();
2337 TRACE("(%p) Reading render target into texture\n", This);
2338 glEnable(GL_TEXTURE_2D);
2340 glGenTextures(1, &tmpTexture);
2341 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2343 glTexImage2D(GL_TEXTURE_2D,
2345 GL_RGBA,
2346 width,
2347 height,
2348 0/*border*/,
2349 GL_RGBA,
2350 GL_UNSIGNED_INT_8_8_8_8_REV,
2351 NULL);
2353 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2354 vcheckGLcall("glGetIntegerv");
2355 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2356 vcheckGLcall("glReadBuffer");
2357 glCopyTexImage2D(GL_TEXTURE_2D,
2359 GL_RGBA,
2362 width,
2363 height,
2366 checkGLcall("glCopyTexImage2D");
2367 glReadBuffer(prevRead);
2368 LEAVE_GL();
2370 } else { /* bind the real texture, and make sure it up to date */
2371 IWineD3DSurface_PreLoad(iface);
2373 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2374 ENTER_GL();
2375 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2376 glGetTexImage(GL_TEXTURE_2D,
2377 This->glDescription.level,
2378 GL_RGBA,
2379 GL_UNSIGNED_INT_8_8_8_8_REV,
2380 allocatedMemory);
2381 checkGLcall("glTexImage2D");
2382 if (tmpTexture) {
2383 glBindTexture(GL_TEXTURE_2D, 0);
2384 glDeleteTextures(1, &tmpTexture);
2386 LEAVE_GL();
2388 f = fopen(filename, "w+");
2389 if (NULL == f) {
2390 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2391 return WINED3DERR_INVALIDCALL;
2393 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2394 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2395 /* TGA header */
2396 fputc(0,f);
2397 fputc(0,f);
2398 fputc(2,f);
2399 fputc(0,f);
2400 fputc(0,f);
2401 fputc(0,f);
2402 fputc(0,f);
2403 fputc(0,f);
2404 fputc(0,f);
2405 fputc(0,f);
2406 fputc(0,f);
2407 fputc(0,f);
2408 /* short width*/
2409 fwrite(&width,2,1,f);
2410 /* short height */
2411 fwrite(&height,2,1,f);
2412 /* format rgba */
2413 fputc(0x20,f);
2414 fputc(0x28,f);
2415 /* raw data */
2416 /* 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 */
2417 if(swapChain)
2418 textureRow = allocatedMemory + (width * (height - 1) *4);
2419 else
2420 textureRow = allocatedMemory;
2421 for (y = 0 ; y < height; y++) {
2422 for (i = 0; i < width; i++) {
2423 color = *((DWORD*)textureRow);
2424 fputc((color >> 16) & 0xFF, f); /* B */
2425 fputc((color >> 8) & 0xFF, f); /* G */
2426 fputc((color >> 0) & 0xFF, f); /* R */
2427 fputc((color >> 24) & 0xFF, f); /* A */
2428 textureRow += 4;
2430 /* take two rows of the pointer to the texture memory */
2431 if(swapChain)
2432 (textureRow-= width << 3);
2435 TRACE("Closing file\n");
2436 fclose(f);
2438 if(swapChain) {
2439 IWineD3DSwapChain_Release(swapChain);
2441 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2442 return WINED3D_OK;
2446 * Slightly inefficient way to handle multiple dirty rects but it works :)
2448 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2449 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2450 IWineD3DBaseTexture *baseTexture = NULL;
2452 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2453 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2455 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2456 if (NULL != pDirtyRect) {
2457 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2458 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2459 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2460 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2461 } else {
2462 This->dirtyRect.left = 0;
2463 This->dirtyRect.top = 0;
2464 This->dirtyRect.right = This->currentDesc.Width;
2465 This->dirtyRect.bottom = This->currentDesc.Height;
2467 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2468 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2469 /* if the container is a basetexture then mark it dirty. */
2470 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2471 TRACE("Passing to container\n");
2472 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2473 IWineD3DBaseTexture_Release(baseTexture);
2475 return WINED3D_OK;
2478 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2479 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2480 HRESULT hr;
2481 const GlPixelFormatDesc *glDesc;
2482 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2484 TRACE("(%p) : Calling base function first\n", This);
2485 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2486 if(SUCCEEDED(hr)) {
2487 /* Setup some glformat defaults */
2488 This->glDescription.glFormat = glDesc->glFormat;
2489 This->glDescription.glFormatInternal = glDesc->glInternal;
2490 This->glDescription.glType = glDesc->glType;
2492 This->Flags &= ~SFLAG_ALLOCATED;
2493 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2494 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2496 return hr;
2499 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2500 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2502 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2503 WARN("Surface is locked or the HDC is in use\n");
2504 return WINED3DERR_INVALIDCALL;
2507 if(Mem && Mem != This->resource.allocatedMemory) {
2508 void *release = NULL;
2510 /* Do I have to copy the old surface content? */
2511 if(This->Flags & SFLAG_DIBSECTION) {
2512 /* Release the DC. No need to hold the critical section for the update
2513 * Thread because this thread runs only on front buffers, but this method
2514 * fails for render targets in the check above.
2516 SelectObject(This->hDC, This->dib.holdbitmap);
2517 DeleteDC(This->hDC);
2518 /* Release the DIB section */
2519 DeleteObject(This->dib.DIBsection);
2520 This->dib.bitmap_data = NULL;
2521 This->resource.allocatedMemory = NULL;
2522 This->hDC = NULL;
2523 This->Flags &= ~SFLAG_DIBSECTION;
2524 } else if(!(This->Flags & SFLAG_USERPTR)) {
2525 release = This->resource.heapMemory;
2526 This->resource.heapMemory = NULL;
2528 This->resource.allocatedMemory = Mem;
2529 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2531 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2532 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2534 /* For client textures opengl has to be notified */
2535 if(This->Flags & SFLAG_CLIENT) {
2536 This->Flags &= ~SFLAG_ALLOCATED;
2537 IWineD3DSurface_PreLoad(iface);
2538 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2541 /* Now free the old memory if any */
2542 HeapFree(GetProcessHeap(), 0, release);
2543 } else if(This->Flags & SFLAG_USERPTR) {
2544 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2545 This->resource.allocatedMemory = NULL;
2546 /* HeapMemory should be NULL already */
2547 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2548 This->Flags &= ~SFLAG_USERPTR;
2550 if(This->Flags & SFLAG_CLIENT) {
2551 This->Flags &= ~SFLAG_ALLOCATED;
2552 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2553 IWineD3DSurface_PreLoad(iface);
2556 return WINED3D_OK;
2559 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2560 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2561 IWineD3DSwapChainImpl *swapchain = NULL;
2562 HRESULT hr;
2563 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2565 /* Flipping is only supported on RenderTargets */
2566 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2568 if(override) {
2569 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2570 * FIXME("(%p) Target override is not supported by now\n", This);
2571 * Additionally, it isn't really possible to support triple-buffering
2572 * properly on opengl at all
2576 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2577 if(!swapchain) {
2578 ERR("Flipped surface is not on a swapchain\n");
2579 return WINEDDERR_NOTFLIPPABLE;
2582 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2583 * and only d3d8 and d3d9 apps specify the presentation interval
2585 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2586 /* Most common case first to avoid wasting time on all the other cases */
2587 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2588 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2589 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2590 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2591 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2592 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2593 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2594 } else {
2595 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2598 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2599 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2600 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2601 return hr;
2604 /* Does a direct frame buffer -> texture copy. Stretching is done
2605 * with single pixel copy calls
2607 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2608 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2609 float xrel, yrel;
2610 UINT row;
2611 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2614 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2615 ENTER_GL();
2616 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2618 /* TODO: Do we need GL_TEXTURE_2D enabled fpr copyteximage? */
2619 glEnable(This->glDescription.target);
2620 checkGLcall("glEnable(This->glDescription.target)");
2622 /* Bind the target texture */
2623 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2624 checkGLcall("glBindTexture");
2625 if(!swapchain) {
2626 glReadBuffer(myDevice->offscreenBuffer);
2627 } else {
2628 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2629 glReadBuffer(buffer);
2631 checkGLcall("glReadBuffer");
2633 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2634 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2636 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2637 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2639 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2640 ERR("Texture filtering not supported in direct blit\n");
2642 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2643 ERR("Texture filtering not supported in direct blit\n");
2646 if(upsidedown &&
2647 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2648 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2649 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2651 glCopyTexSubImage2D(This->glDescription.target,
2652 This->glDescription.level,
2653 drect->x1, drect->y1, /* xoffset, yoffset */
2654 srect->x1, Src->currentDesc.Height - srect->y2,
2655 drect->x2 - drect->x1, drect->y2 - drect->y1);
2656 } else {
2657 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2658 /* I have to process this row by row to swap the image,
2659 * otherwise it would be upside down, so stretching in y direction
2660 * doesn't cost extra time
2662 * However, stretching in x direction can be avoided if not necessary
2664 for(row = drect->y1; row < drect->y2; row++) {
2665 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2666 /* Well, that stuff works, but it's very slow.
2667 * find a better way instead
2669 UINT col;
2671 for(col = drect->x1; col < drect->x2; col++) {
2672 glCopyTexSubImage2D(This->glDescription.target,
2673 This->glDescription.level,
2674 drect->x1 + col, row, /* xoffset, yoffset */
2675 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2676 1, 1);
2678 } else {
2679 glCopyTexSubImage2D(This->glDescription.target,
2680 This->glDescription.level,
2681 drect->x1, row, /* xoffset, yoffset */
2682 srect->x1, yoffset - (int) (row * yrel),
2683 drect->x2-drect->x1, 1);
2687 vcheckGLcall("glCopyTexSubImage2D");
2689 /* Leave the opengl state valid for blitting */
2690 glDisable(This->glDescription.target);
2691 checkGLcall("glDisable(This->glDescription.target)");
2693 LEAVE_GL();
2696 /* Uses the hardware to stretch and flip the image */
2697 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2698 GLuint src, backup = 0;
2699 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2700 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2701 float left, right, top, bottom; /* Texture coordinates */
2702 UINT fbwidth = Src->currentDesc.Width;
2703 UINT fbheight = Src->currentDesc.Height;
2704 GLenum drawBuffer = GL_BACK;
2705 GLenum texture_target;
2707 TRACE("Using hwstretch blit\n");
2708 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2709 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2710 ENTER_GL();
2712 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2714 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2715 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2717 if(GL_LIMITS(aux_buffers) >= 2) {
2718 /* Got more than one aux buffer? Use the 2nd aux buffer */
2719 drawBuffer = GL_AUX1;
2720 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2721 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2722 drawBuffer = GL_AUX0;
2725 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2726 glGenTextures(1, &backup);
2727 checkGLcall("glGenTextures\n");
2728 glBindTexture(GL_TEXTURE_2D, backup);
2729 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2730 texture_target = GL_TEXTURE_2D;
2731 } else {
2732 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2733 * we are reading from the back buffer, the backup can be used as source texture
2735 if(Src->glDescription.textureName == 0) {
2736 /* Get it a description */
2737 IWineD3DSurface_PreLoad(SrcSurface);
2739 texture_target = Src->glDescription.target;
2740 glBindTexture(texture_target, Src->glDescription.textureName);
2741 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2742 glEnable(texture_target);
2743 checkGLcall("glEnable(texture_target)");
2745 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2746 Src->Flags &= ~SFLAG_INTEXTURE;
2749 glReadBuffer(GL_BACK);
2750 checkGLcall("glReadBuffer(GL_BACK)");
2752 /* TODO: Only back up the part that will be overwritten */
2753 glCopyTexSubImage2D(texture_target, 0,
2754 0, 0 /* read offsets */,
2755 0, 0,
2756 fbwidth,
2757 fbheight);
2759 checkGLcall("glCopyTexSubImage2D");
2761 /* No issue with overriding these - the sampler is dirty due to blit usage */
2762 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2763 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2764 checkGLcall("glTexParameteri");
2765 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2766 minMipLookup[Filter][WINED3DTEXF_NONE]);
2767 checkGLcall("glTexParameteri");
2769 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2770 src = backup ? backup : Src->glDescription.textureName;
2771 } else {
2772 glReadBuffer(GL_FRONT);
2773 checkGLcall("glReadBuffer(GL_FRONT)");
2775 glGenTextures(1, &src);
2776 checkGLcall("glGenTextures(1, &src)");
2777 glBindTexture(GL_TEXTURE_2D, src);
2778 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2780 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2781 * out for power of 2 sizes
2783 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2784 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2785 checkGLcall("glTexImage2D");
2786 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2787 0, 0 /* read offsets */,
2788 0, 0,
2789 fbwidth,
2790 fbheight);
2792 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2793 checkGLcall("glTexParameteri");
2794 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2795 checkGLcall("glTexParameteri");
2797 glReadBuffer(GL_BACK);
2798 checkGLcall("glReadBuffer(GL_BACK)");
2800 if(texture_target != GL_TEXTURE_2D) {
2801 glDisable(texture_target);
2802 glEnable(GL_TEXTURE_2D);
2803 texture_target = GL_TEXTURE_2D;
2806 checkGLcall("glEnd and previous");
2808 left = (float) srect->x1 / (float) Src->pow2Width;
2809 right = (float) srect->x2 / (float) Src->pow2Width;
2811 if(upsidedown) {
2812 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2813 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2814 } else {
2815 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2816 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2819 /* draw the source texture stretched and upside down. The correct surface is bound already */
2820 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
2821 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
2823 glDrawBuffer(drawBuffer);
2824 glReadBuffer(drawBuffer);
2826 glBegin(GL_QUADS);
2827 /* bottom left */
2828 glTexCoord2f(left, bottom);
2829 glVertex2i(0, fbheight);
2831 /* top left */
2832 glTexCoord2f(left, top);
2833 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2835 /* top right */
2836 glTexCoord2f(right, top);
2837 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2839 /* bottom right */
2840 glTexCoord2f(right, bottom);
2841 glVertex2i(drect->x2 - drect->x1, fbheight);
2842 glEnd();
2843 checkGLcall("glEnd and previous");
2845 if(texture_target != This->glDescription.target) {
2846 glDisable(texture_target);
2847 glEnable(This->glDescription.target);
2848 texture_target = This->glDescription.target;
2851 /* Now read the stretched and upside down image into the destination texture */
2852 glBindTexture(texture_target, This->glDescription.textureName);
2853 checkGLcall("glBindTexture");
2854 glCopyTexSubImage2D(texture_target,
2856 drect->x1, drect->y1, /* xoffset, yoffset */
2857 0, 0, /* We blitted the image to the origin */
2858 drect->x2 - drect->x1, drect->y2 - drect->y1);
2859 checkGLcall("glCopyTexSubImage2D");
2861 if(drawBuffer == GL_BACK) {
2862 /* Write the back buffer backup back */
2863 if(backup) {
2864 if(texture_target != GL_TEXTURE_2D) {
2865 glDisable(texture_target);
2866 glEnable(GL_TEXTURE_2D);
2867 texture_target = GL_TEXTURE_2D;
2869 glBindTexture(GL_TEXTURE_2D, backup);
2870 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
2871 } else {
2872 if(texture_target != Src->glDescription.target) {
2873 glDisable(texture_target);
2874 glEnable(Src->glDescription.target);
2875 texture_target = Src->glDescription.target;
2877 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
2878 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2881 glBegin(GL_QUADS);
2882 /* top left */
2883 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2884 glVertex2i(0, 0);
2886 /* bottom left */
2887 glTexCoord2f(0.0, 0.0);
2888 glVertex2i(0, fbheight);
2890 /* bottom right */
2891 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2892 glVertex2i(fbwidth, Src->currentDesc.Height);
2894 /* top right */
2895 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2896 glVertex2i(fbwidth, 0);
2897 glEnd();
2898 } else {
2899 /* Restore the old draw buffer */
2900 glDrawBuffer(GL_BACK);
2902 glDisable(texture_target);
2903 checkGLcall("glDisable(texture_target)");
2905 /* Cleanup */
2906 if(src != Src->glDescription.textureName && src != backup) {
2907 glDeleteTextures(1, &src);
2908 checkGLcall("glDeleteTextures(1, &src)");
2910 if(backup) {
2911 glDeleteTextures(1, &backup);
2912 checkGLcall("glDeleteTextures(1, &backup)");
2915 LEAVE_GL();
2918 /* Not called from the VTable */
2919 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2920 WINED3DRECT rect;
2921 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2922 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2923 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2925 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2927 /* Get the swapchain. One of the surfaces has to be a primary surface */
2928 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2929 WARN("Destination is in sysmem, rejecting gl blt\n");
2930 return WINED3DERR_INVALIDCALL;
2932 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2933 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2934 if(Src) {
2935 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2936 WARN("Src is in sysmem, rejecting gl blt\n");
2937 return WINED3DERR_INVALIDCALL;
2939 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2940 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2943 /* Early sort out of cases where no render target is used */
2944 if(!dstSwapchain && !srcSwapchain &&
2945 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2946 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2947 return WINED3DERR_INVALIDCALL;
2950 /* No destination color keying supported */
2951 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2952 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2953 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2954 return WINED3DERR_INVALIDCALL;
2957 if (DestRect) {
2958 rect.x1 = DestRect->left;
2959 rect.y1 = DestRect->top;
2960 rect.x2 = DestRect->right;
2961 rect.y2 = DestRect->bottom;
2962 } else {
2963 rect.x1 = 0;
2964 rect.y1 = 0;
2965 rect.x2 = This->currentDesc.Width;
2966 rect.y2 = This->currentDesc.Height;
2969 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2970 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2971 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2972 /* Half-life does a Blt from the back buffer to the front buffer,
2973 * Full surface size, no flags... Use present instead
2975 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2978 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2979 while(1)
2981 RECT mySrcRect;
2982 TRACE("Looking if a Present can be done...\n");
2983 /* Source Rectangle must be full surface */
2984 if( SrcRect ) {
2985 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2986 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2987 TRACE("No, Source rectangle doesn't match\n");
2988 break;
2991 mySrcRect.left = 0;
2992 mySrcRect.top = 0;
2993 mySrcRect.right = Src->currentDesc.Width;
2994 mySrcRect.bottom = Src->currentDesc.Height;
2996 /* No stretching may occur */
2997 if(mySrcRect.right != rect.x2 - rect.x1 ||
2998 mySrcRect.bottom != rect.y2 - rect.y1) {
2999 TRACE("No, stretching is done\n");
3000 break;
3003 /* Destination must be full surface or match the clipping rectangle */
3004 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3006 RECT cliprect;
3007 POINT pos[2];
3008 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3009 pos[0].x = rect.x1;
3010 pos[0].y = rect.y1;
3011 pos[1].x = rect.x2;
3012 pos[1].y = rect.y2;
3013 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3014 pos, 2);
3016 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3017 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3019 TRACE("No, dest rectangle doesn't match(clipper)\n");
3020 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3021 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3022 break;
3025 else
3027 if(rect.x1 != 0 || rect.y1 != 0 ||
3028 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3029 TRACE("No, dest rectangle doesn't match(surface size)\n");
3030 break;
3034 TRACE("Yes\n");
3036 /* These flags are unimportant for the flag check, remove them */
3037 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3038 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3040 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3041 * take very long, while a flip is fast.
3042 * This applies to Half-Life, which does such Blts every time it finished
3043 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3044 * menu. This is also used by all apps when they do windowed rendering
3046 * The problem is that flipping is not really the same as copying. After a
3047 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3048 * untouched. Therefore it's necessary to override the swap effect
3049 * and to set it back after the flip.
3051 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3052 * testcases.
3055 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3056 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3058 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3059 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3061 dstSwapchain->presentParms.SwapEffect = orig_swap;
3063 return WINED3D_OK;
3065 break;
3068 TRACE("Unsupported blit between buffers on the same swapchain\n");
3069 return WINED3DERR_INVALIDCALL;
3070 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3071 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3072 return WINED3DERR_INVALIDCALL;
3073 } else if(dstSwapchain && srcSwapchain) {
3074 FIXME("Implement hardware blit between two different swapchains\n");
3075 return WINED3DERR_INVALIDCALL;
3076 } else if(dstSwapchain) {
3077 if(SrcSurface == myDevice->render_targets[0]) {
3078 TRACE("Blit from active render target to a swapchain\n");
3079 /* Handled with regular texture -> swapchain blit */
3081 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3082 FIXME("Implement blit from a swapchain to the active render target\n");
3083 return WINED3DERR_INVALIDCALL;
3086 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3087 /* Blit from render target to texture */
3088 WINED3DRECT srect;
3089 BOOL upsideDown, stretchx;
3091 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3092 TRACE("Color keying not supported by frame buffer to texture blit\n");
3093 return WINED3DERR_INVALIDCALL;
3094 /* Destination color key is checked above */
3097 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3098 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3100 if(SrcRect) {
3101 if(SrcRect->top < SrcRect->bottom) {
3102 srect.y1 = SrcRect->top;
3103 srect.y2 = SrcRect->bottom;
3104 upsideDown = FALSE;
3105 } else {
3106 srect.y1 = SrcRect->bottom;
3107 srect.y2 = SrcRect->top;
3108 upsideDown = TRUE;
3110 srect.x1 = SrcRect->left;
3111 srect.x2 = SrcRect->right;
3112 } else {
3113 srect.x1 = 0;
3114 srect.y1 = 0;
3115 srect.x2 = Src->currentDesc.Width;
3116 srect.y2 = Src->currentDesc.Height;
3117 upsideDown = FALSE;
3119 if(rect.x1 > rect.x2) {
3120 UINT tmp = rect.x2;
3121 rect.x2 = rect.x1;
3122 rect.x1 = tmp;
3123 upsideDown = !upsideDown;
3125 if(!srcSwapchain) {
3126 TRACE("Reading from an offscreen target\n");
3127 upsideDown = !upsideDown;
3130 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3131 stretchx = TRUE;
3132 } else {
3133 stretchx = FALSE;
3136 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3137 * flip the image nor scale it.
3139 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3140 * -> If the app wants a image width an unscaled width, copy it line per line
3141 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3142 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3143 * back buffer. This is slower than reading line per line, thus not used for flipping
3144 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3145 * pixel by pixel
3147 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3148 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3149 * backends.
3151 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3152 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3153 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3154 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3155 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3156 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3157 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3158 } else {
3159 TRACE("Using hardware stretching to flip / stretch the texture\n");
3160 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3163 if(!(This->Flags & SFLAG_DONOTFREE)) {
3164 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3165 This->resource.allocatedMemory = NULL;
3166 This->resource.heapMemory = NULL;
3167 } else {
3168 This->Flags &= ~SFLAG_INSYSMEM;
3170 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3171 * path is never entered
3173 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3175 return WINED3D_OK;
3176 } else if(Src) {
3177 /* Blit from offscreen surface to render target */
3178 float glTexCoord[4];
3179 DWORD oldCKeyFlags = Src->CKeyFlags;
3180 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
3181 RECT SourceRectangle;
3182 BOOL paletteOverride = FALSE;
3184 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3186 if(SrcRect) {
3187 SourceRectangle.left = SrcRect->left;
3188 SourceRectangle.right = SrcRect->right;
3189 SourceRectangle.top = SrcRect->top;
3190 SourceRectangle.bottom = SrcRect->bottom;
3191 } else {
3192 SourceRectangle.left = 0;
3193 SourceRectangle.right = Src->currentDesc.Width;
3194 SourceRectangle.top = 0;
3195 SourceRectangle.bottom = Src->currentDesc.Height;
3197 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3198 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3199 TRACE("Using stretch_rect_fbo\n");
3200 /* The source is always a texture, but never the currently active render target, and the texture
3201 * contents are never upside down
3203 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3204 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3205 return WINED3D_OK;
3208 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3209 /* Fall back to software */
3210 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3211 SourceRectangle.left, SourceRectangle.top,
3212 SourceRectangle.right, SourceRectangle.bottom);
3213 return WINED3DERR_INVALIDCALL;
3216 /* Color keying: Check if we have to do a color keyed blt,
3217 * and if not check if a color key is activated.
3219 * Just modify the color keying parameters in the surface and restore them afterwards
3220 * The surface keeps track of the color key last used to load the opengl surface.
3221 * PreLoad will catch the change to the flags and color key and reload if necessary.
3223 if(Flags & WINEDDBLT_KEYSRC) {
3224 /* Use color key from surface */
3225 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3226 /* Use color key from DDBltFx */
3227 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3228 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3229 } else {
3230 /* Do not use color key */
3231 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3234 /* When blitting from an offscreen surface to a rendertarget, the source
3235 * surface is not required to have a palette. Our rendering / conversion
3236 * code further down the road retrieves the palette from the surface, so
3237 * it must have a palette set. */
3238 if((Src->resource.format == WINED3DFMT_P8) && (Src->palette == NULL)) {
3239 paletteOverride = TRUE;
3240 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3241 Src->palette = This->palette;
3244 /* Now load the surface */
3245 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3248 /* Activate the destination context, set it up for blitting */
3249 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3250 ENTER_GL();
3252 glEnable(Src->glDescription.target);
3253 checkGLcall("glEnable(Src->glDescription.target)");
3255 if(!dstSwapchain) {
3256 TRACE("Drawing to offscreen buffer\n");
3257 glDrawBuffer(myDevice->offscreenBuffer);
3258 checkGLcall("glDrawBuffer");
3259 } else {
3260 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3261 TRACE("Drawing to %#x buffer\n", buffer);
3262 glDrawBuffer(buffer);
3263 checkGLcall("glDrawBuffer");
3266 /* Bind the texture */
3267 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3268 checkGLcall("glBindTexture");
3270 /* Filtering for StretchRect */
3271 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3272 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3273 checkGLcall("glTexParameteri");
3274 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3275 minMipLookup[Filter][WINED3DTEXF_NONE]);
3276 checkGLcall("glTexParameteri");
3277 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3278 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3279 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3280 checkGLcall("glTexEnvi");
3282 /* This is for color keying */
3283 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3284 glEnable(GL_ALPHA_TEST);
3285 checkGLcall("glEnable GL_ALPHA_TEST");
3287 /* When the primary render target uses P8, the alpha component contains the palette index.
3288 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3289 * should be masked away have alpha set to 0. */
3290 if(primary_render_target_is_p8(myDevice))
3291 glAlphaFunc(GL_NOTEQUAL, (float)This->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3292 else
3293 glAlphaFunc(GL_NOTEQUAL, 0.0);
3294 checkGLcall("glAlphaFunc\n");
3295 } else {
3296 glDisable(GL_ALPHA_TEST);
3297 checkGLcall("glDisable GL_ALPHA_TEST");
3300 /* Draw a textured quad
3302 glBegin(GL_QUADS);
3304 glColor3d(1.0f, 1.0f, 1.0f);
3305 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3306 glVertex3f(rect.x1,
3307 rect.y1,
3308 0.0);
3310 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3311 glVertex3f(rect.x1, rect.y2, 0.0);
3313 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3314 glVertex3f(rect.x2,
3315 rect.y2,
3316 0.0);
3318 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3319 glVertex3f(rect.x2,
3320 rect.y1,
3321 0.0);
3322 glEnd();
3323 checkGLcall("glEnd");
3325 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3326 glDisable(GL_ALPHA_TEST);
3327 checkGLcall("glDisable(GL_ALPHA_TEST)");
3330 /* Flush in case the drawable is used by multiple GL contexts */
3331 if(dstSwapchain && (dstSwapchain->num_contexts >= 2))
3332 glFlush();
3334 glBindTexture(Src->glDescription.target, 0);
3335 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3336 /* Leave the opengl state valid for blitting */
3337 glDisable(Src->glDescription.target);
3338 checkGLcall("glDisable(Src->glDescription.target)");
3340 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3341 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3343 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3344 glDrawBuffer(GL_BACK);
3345 checkGLcall("glDrawBuffer");
3347 /* Restore the color key parameters */
3348 Src->CKeyFlags = oldCKeyFlags;
3349 This->SrcBltCKey = oldBltCKey;
3351 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3352 if(paletteOverride)
3353 Src->palette = NULL;
3355 LEAVE_GL();
3357 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3358 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3359 * is outdated now
3361 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3362 /* TODO: This should be moved to ModifyLocation() */
3363 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3364 This->Flags |= SFLAG_INTEXTURE;
3367 return WINED3D_OK;
3368 } else {
3369 /* Source-Less Blit to render target */
3370 if (Flags & WINEDDBLT_COLORFILL) {
3371 /* This is easy to handle for the D3D Device... */
3372 DWORD color;
3374 TRACE("Colorfill\n");
3376 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3377 must be true if we are here */
3378 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3379 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3380 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3381 TRACE("Surface is higher back buffer, falling back to software\n");
3382 return WINED3DERR_INVALIDCALL;
3385 /* The color as given in the Blt function is in the format of the frame-buffer...
3386 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3388 if (This->resource.format == WINED3DFMT_P8) {
3389 if (This->palette) {
3390 color = ((0xFF000000) |
3391 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3392 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3393 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3394 } else {
3395 color = 0xFF000000;
3398 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3399 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3400 color = 0xFFFFFFFF;
3401 } else {
3402 color = ((0xFF000000) |
3403 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3404 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3405 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3408 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3409 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3410 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3412 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3413 color = DDBltFx->u5.dwFillColor;
3415 else {
3416 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3417 return WINED3DERR_INVALIDCALL;
3420 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3421 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3422 1, /* Number of rectangles */
3423 &rect, WINED3DCLEAR_TARGET, color,
3424 0.0 /* Z */,
3425 0 /* Stencil */);
3426 return WINED3D_OK;
3430 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3431 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3432 return WINED3DERR_INVALIDCALL;
3435 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3437 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3438 float depth;
3440 if (Flags & WINEDDBLT_DEPTHFILL) {
3441 switch(This->resource.format) {
3442 case WINED3DFMT_D16:
3443 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3444 break;
3445 case WINED3DFMT_D15S1:
3446 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3447 break;
3448 case WINED3DFMT_D24S8:
3449 case WINED3DFMT_D24X8:
3450 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3451 break;
3452 case WINED3DFMT_D32:
3453 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3454 break;
3455 default:
3456 depth = 0.0;
3457 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3460 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3461 DestRect == NULL ? 0 : 1,
3462 (WINED3DRECT *) DestRect,
3463 WINED3DCLEAR_ZBUFFER,
3464 0x00000000,
3465 depth,
3466 0x00000000);
3469 FIXME("(%p): Unsupp depthstencil blit\n", This);
3470 return WINED3DERR_INVALIDCALL;
3473 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3474 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3475 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3476 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3477 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3478 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3480 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3482 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3483 return WINEDDERR_SURFACEBUSY;
3486 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3487 * except depth blits, which seem to work
3489 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3490 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3491 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3492 return WINED3DERR_INVALIDCALL;
3493 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3494 TRACE("Z Blit override handled the blit\n");
3495 return WINED3D_OK;
3499 /* Special cases for RenderTargets */
3500 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3501 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3502 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3505 /* For the rest call the X11 surface implementation.
3506 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3507 * other Blts are rather rare
3509 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3512 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3513 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3514 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3515 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3516 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3518 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3520 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3521 return WINEDDERR_SURFACEBUSY;
3524 if(myDevice->inScene &&
3525 (iface == myDevice->stencilBufferTarget ||
3526 (Source && Source == myDevice->stencilBufferTarget))) {
3527 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3528 return WINED3DERR_INVALIDCALL;
3531 /* Special cases for RenderTargets */
3532 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3533 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3535 RECT SrcRect, DstRect;
3536 DWORD Flags=0;
3538 if(rsrc) {
3539 SrcRect.left = rsrc->left;
3540 SrcRect.top= rsrc->top;
3541 SrcRect.bottom = rsrc->bottom;
3542 SrcRect.right = rsrc->right;
3543 } else {
3544 SrcRect.left = 0;
3545 SrcRect.top = 0;
3546 SrcRect.right = srcImpl->currentDesc.Width;
3547 SrcRect.bottom = srcImpl->currentDesc.Height;
3550 DstRect.left = dstx;
3551 DstRect.top=dsty;
3552 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3553 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3555 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3556 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3557 Flags |= WINEDDBLT_KEYSRC;
3558 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3559 Flags |= WINEDDBLT_KEYDEST;
3560 if(trans & WINEDDBLTFAST_WAIT)
3561 Flags |= WINEDDBLT_WAIT;
3562 if(trans & WINEDDBLTFAST_DONOTWAIT)
3563 Flags |= WINEDDBLT_DONOTWAIT;
3565 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3569 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3572 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3573 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3574 RGBQUAD col[256];
3575 IWineD3DPaletteImpl *pal = This->palette;
3576 unsigned int n;
3577 TRACE("(%p)\n", This);
3579 if (!pal) return WINED3D_OK;
3581 if(This->resource.format == WINED3DFMT_P8 ||
3582 This->resource.format == WINED3DFMT_A8P8)
3584 if(!(This->Flags & SFLAG_INSYSMEM)) {
3585 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3586 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3588 TRACE("Dirtifying surface\n");
3589 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3592 if(This->Flags & SFLAG_DIBSECTION) {
3593 TRACE("(%p): Updating the hdc's palette\n", This);
3594 for (n=0; n<256; n++) {
3595 col[n].rgbRed = pal->palents[n].peRed;
3596 col[n].rgbGreen = pal->palents[n].peGreen;
3597 col[n].rgbBlue = pal->palents[n].peBlue;
3598 col[n].rgbReserved = 0;
3600 SetDIBColorTable(This->hDC, 0, 256, col);
3603 /* Propagate the changes to the drawable when we have a palette.
3604 * TODO: in case of hardware p8 palettes we should only upload the palette. */
3605 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3606 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3608 return WINED3D_OK;
3611 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3612 /** Check against the maximum texture sizes supported by the video card **/
3613 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3614 unsigned int pow2Width, pow2Height;
3615 const GlPixelFormatDesc *glDesc;
3617 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3618 /* Setup some glformat defaults */
3619 This->glDescription.glFormat = glDesc->glFormat;
3620 This->glDescription.glFormatInternal = glDesc->glInternal;
3621 This->glDescription.glType = glDesc->glType;
3623 This->glDescription.textureName = 0;
3624 This->glDescription.target = GL_TEXTURE_2D;
3626 /* Non-power2 support */
3627 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3628 pow2Width = This->currentDesc.Width;
3629 pow2Height = This->currentDesc.Height;
3630 } else {
3631 /* Find the nearest pow2 match */
3632 pow2Width = pow2Height = 1;
3633 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3634 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3636 This->pow2Width = pow2Width;
3637 This->pow2Height = pow2Height;
3639 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3640 WINED3DFORMAT Format = This->resource.format;
3641 /** TODO: add support for non power two compressed textures **/
3642 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3643 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3644 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3645 This, This->currentDesc.Width, This->currentDesc.Height);
3646 return WINED3DERR_NOTAVAILABLE;
3650 if(pow2Width != This->currentDesc.Width ||
3651 pow2Height != This->currentDesc.Height) {
3652 This->Flags |= SFLAG_NONPOW2;
3655 TRACE("%p\n", This);
3656 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3657 /* one of three options
3658 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)
3659 2: Set the texture to the maximum size (bad idea)
3660 3: WARN and return WINED3DERR_NOTAVAILABLE;
3661 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.
3663 WARN("(%p) Creating an oversized surface\n", This);
3664 This->Flags |= SFLAG_OVERSIZE;
3666 /* This will be initialized on the first blt */
3667 This->glRect.left = 0;
3668 This->glRect.top = 0;
3669 This->glRect.right = 0;
3670 This->glRect.bottom = 0;
3671 } else {
3672 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3673 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3674 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3675 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3677 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE) &&
3678 !((This->resource.format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE) && (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3680 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3681 This->pow2Width = This->currentDesc.Width;
3682 This->pow2Height = This->currentDesc.Height;
3683 This->Flags &= ~SFLAG_NONPOW2;
3686 /* No oversize, gl rect is the full texture size */
3687 This->Flags &= ~SFLAG_OVERSIZE;
3688 This->glRect.left = 0;
3689 This->glRect.top = 0;
3690 This->glRect.right = This->pow2Width;
3691 This->glRect.bottom = This->pow2Height;
3694 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3695 switch(wined3d_settings.offscreen_rendering_mode) {
3696 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3697 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3698 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3702 This->Flags |= SFLAG_INSYSMEM;
3704 return WINED3D_OK;
3707 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3708 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3709 IWineD3DBaseTexture *texture;
3711 TRACE("(%p)->(%s, %s)\n", iface,
3712 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3713 persistent ? "TRUE" : "FALSE");
3715 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3716 IWineD3DSwapChain *swapchain = NULL;
3718 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3719 TRACE("Surface %p is an onscreen surface\n", iface);
3721 IWineD3DSwapChain_Release(swapchain);
3722 } else {
3723 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3724 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3728 if(persistent) {
3729 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
3730 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3731 TRACE("Passing to container\n");
3732 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3733 IWineD3DBaseTexture_Release(texture);
3736 This->Flags &= ~SFLAG_LOCATIONS;
3737 This->Flags |= flag;
3738 } else {
3739 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
3740 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3741 TRACE("Passing to container\n");
3742 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3743 IWineD3DBaseTexture_Release(texture);
3746 This->Flags &= ~flag;
3750 struct coords {
3751 GLfloat x, y, z;
3754 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
3755 struct coords coords[4];
3756 RECT rect;
3757 IWineD3DSwapChain *swapchain = NULL;
3758 IWineD3DBaseTexture *texture = NULL;
3759 HRESULT hr;
3760 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3762 if(rect_in) {
3763 rect = *rect_in;
3764 } else {
3765 rect.left = 0;
3766 rect.top = 0;
3767 rect.right = This->currentDesc.Width;
3768 rect.bottom = This->currentDesc.Height;
3771 ActivateContext(device, device->render_targets[0], CTXUSAGE_BLIT);
3772 ENTER_GL();
3774 if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
3775 glEnable(GL_TEXTURE_RECTANGLE_ARB);
3776 checkGLcall("glEnable(GL_TEXTURE_RECTANGLE_ARB)");
3777 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName);
3778 checkGLcall("GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName)");
3779 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3780 checkGLcall("glTexParameteri");
3781 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3782 checkGLcall("glTexParameteri");
3784 coords[0].x = rect.left;
3785 coords[0].z = 0;
3787 coords[1].x = rect.left;
3788 coords[1].z = 0;
3790 coords[2].x = rect.right;
3791 coords[2].z = 0;
3793 coords[3].x = rect.right;
3794 coords[3].z = 0;
3796 coords[0].y = rect.top;
3797 coords[1].y = rect.bottom;
3798 coords[2].y = rect.bottom;
3799 coords[3].y = rect.top;
3800 } else if(This->glDescription.target == GL_TEXTURE_2D) {
3801 glEnable(GL_TEXTURE_2D);
3802 checkGLcall("glEnable(GL_TEXTURE_2D)");
3803 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
3804 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
3805 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3806 checkGLcall("glTexParameteri");
3807 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3808 checkGLcall("glTexParameteri");
3810 coords[0].x = (float)rect.left / This->pow2Width;
3811 coords[0].z = 0;
3813 coords[1].x = (float)rect.left / This->pow2Width;
3814 coords[1].z = 0;
3816 coords[2].x = (float)rect.right / This->pow2Width;
3817 coords[2].z = 0;
3819 coords[3].x = (float)rect.right / This->pow2Width;
3820 coords[3].z = 0;
3822 coords[0].y = (float)rect.top / This->pow2Height;
3823 coords[1].y = (float)rect.bottom / This->pow2Height;
3824 coords[2].y = (float)rect.bottom / This->pow2Height;
3825 coords[3].y = (float)rect.top / This->pow2Height;
3826 } else {
3827 /* Must be a cube map */
3828 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
3829 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
3830 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
3831 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
3832 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3833 checkGLcall("glTexParameteri");
3834 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3835 checkGLcall("glTexParameteri");
3837 switch(This->glDescription.target) {
3838 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3839 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
3840 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3841 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3842 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
3843 break;
3845 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3846 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3847 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
3848 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
3849 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3850 break;
3852 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3853 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
3854 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3855 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3856 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
3857 break;
3859 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3860 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3861 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3862 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3863 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3864 break;
3866 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3867 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3868 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3869 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
3870 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
3871 break;
3873 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
3874 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
3875 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
3876 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3877 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3878 break;
3880 default:
3881 ERR("Unexpected texture target\n");
3882 LEAVE_GL();
3883 return;
3887 glBegin(GL_QUADS);
3888 glTexCoord3fv(&coords[0].x);
3889 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
3891 glTexCoord3fv(&coords[1].x);
3892 glVertex2i(rect.left, device->render_offscreen ? rect.top : rect.bottom);
3894 glTexCoord3fv(&coords[2].x);
3895 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
3897 glTexCoord3fv(&coords[3].x);
3898 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
3899 glEnd();
3900 checkGLcall("glEnd");
3902 if(This->glDescription.target != GL_TEXTURE_2D) {
3903 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3904 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3905 } else {
3906 glDisable(GL_TEXTURE_2D);
3907 checkGLcall("glDisable(GL_TEXTURE_2D)");
3910 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain);
3911 if(hr == WINED3D_OK && swapchain) {
3912 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
3913 if(((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
3914 glFlush();
3916 IWineD3DSwapChain_Release(swapchain);
3917 } else {
3918 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
3919 * reset properly next draw
3921 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture);
3922 if(hr == WINED3D_OK && texture) {
3923 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
3924 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
3925 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
3926 IWineD3DBaseTexture_Release(texture);
3929 LEAVE_GL();
3932 /*****************************************************************************
3933 * IWineD3DSurface::LoadLocation
3935 * Copies the current surface data from wherever it is to the requested
3936 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
3937 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
3938 * multiple locations, the gl texture is preferred over the drawable, which is
3939 * preferred over system memory. The PBO counts as system memory. If rect is
3940 * not NULL, only the specified rectangle is copied (only supported for
3941 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
3942 * location is marked up to date after the copy.
3944 * Parameters:
3945 * flag: Surface location flag to be updated
3946 * rect: rectangle to be copied
3948 * Returns:
3949 * WINED3D_OK on success
3950 * WINED3DERR_DEVICELOST on an internal error
3952 *****************************************************************************/
3953 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
3954 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3955 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3956 IWineD3DSwapChain *swapchain = NULL;
3957 GLenum format, internal, type;
3958 CONVERT_TYPES convert;
3959 int bpp;
3960 int width, pitch, outpitch;
3961 BYTE *mem;
3963 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3964 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3965 TRACE("Surface %p is an onscreen surface\n", iface);
3967 IWineD3DSwapChain_Release(swapchain);
3968 } else {
3969 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
3970 * Prefer SFLAG_INTEXTURE. */
3971 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
3975 TRACE("(%p)->(%s, %p)\n", iface,
3976 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3977 rect);
3978 if(rect) {
3979 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
3982 if(This->Flags & flag) {
3983 TRACE("Location already up to date\n");
3984 return WINED3D_OK;
3987 if(!(This->Flags & SFLAG_LOCATIONS)) {
3988 ERR("Surface does not have any up to date location\n");
3989 This->Flags |= SFLAG_LOST;
3990 return WINED3DERR_DEVICELOST;
3993 if(flag == SFLAG_INSYSMEM) {
3994 surface_prepare_system_memory(This);
3996 /* Download the surface to system memory */
3997 if(This->Flags & SFLAG_INTEXTURE) {
3998 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
3999 surface_bind_and_dirtify(This);
4001 surface_download_data(This);
4002 } else {
4003 read_from_framebuffer(This, rect,
4004 This->resource.allocatedMemory,
4005 IWineD3DSurface_GetPitch(iface));
4007 } else if(flag == SFLAG_INDRAWABLE) {
4008 if(This->Flags & SFLAG_INTEXTURE) {
4009 surface_blt_to_drawable(This, rect);
4010 } else {
4011 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4013 /* The width is in 'length' not in bytes */
4014 width = This->currentDesc.Width;
4015 pitch = IWineD3DSurface_GetPitch(iface);
4017 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4018 int height = This->currentDesc.Height;
4020 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4021 outpitch = width * bpp;
4022 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4024 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4025 if(!mem) {
4026 ERR("Out of memory %d, %d!\n", outpitch, height);
4027 return WINED3DERR_OUTOFVIDEOMEMORY;
4029 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4031 This->Flags |= SFLAG_CONVERTED;
4032 } else {
4033 This->Flags &= ~SFLAG_CONVERTED;
4034 mem = This->resource.allocatedMemory;
4037 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4039 /* Don't delete PBO memory */
4040 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4041 HeapFree(GetProcessHeap(), 0, mem);
4043 } else /* if(flag == SFLAG_INTEXTURE) */ {
4044 if (This->Flags & SFLAG_INDRAWABLE) {
4045 read_from_framebuffer_texture(This);
4046 } else { /* Upload from system memory */
4047 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4049 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4050 surface_bind_and_dirtify(This);
4051 ENTER_GL();
4053 /* The only place where LoadTexture() might get called when isInDraw=1
4054 * is ActivateContext where lastActiveRenderTarget is preloaded.
4056 if(iface == device->lastActiveRenderTarget && device->isInDraw)
4057 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
4059 /* Otherwise: System memory copy must be most up to date */
4061 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4062 This->Flags |= SFLAG_GLCKEY;
4063 This->glCKey = This->SrcBltCKey;
4065 else This->Flags &= ~SFLAG_GLCKEY;
4067 /* The width is in 'length' not in bytes */
4068 width = This->currentDesc.Width;
4069 pitch = IWineD3DSurface_GetPitch(iface);
4071 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4072 int height = This->currentDesc.Height;
4074 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4075 outpitch = width * bpp;
4076 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4078 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4079 if(!mem) {
4080 ERR("Out of memory %d, %d!\n", outpitch, height);
4081 return WINED3DERR_OUTOFVIDEOMEMORY;
4083 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4085 This->Flags |= SFLAG_CONVERTED;
4086 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
4087 d3dfmt_p8_upload_palette(iface, convert);
4088 This->Flags &= ~SFLAG_CONVERTED;
4089 mem = This->resource.allocatedMemory;
4090 } else {
4091 This->Flags &= ~SFLAG_CONVERTED;
4092 mem = This->resource.allocatedMemory;
4095 /* Make sure the correct pitch is used */
4096 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4098 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4099 TRACE("non power of two support\n");
4100 if(!(This->Flags & SFLAG_ALLOCATED)) {
4101 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4103 if (mem || (This->Flags & SFLAG_PBO)) {
4104 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4106 } else {
4107 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4108 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4110 if(!(This->Flags & SFLAG_ALLOCATED)) {
4111 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4113 if (mem || (This->Flags & SFLAG_PBO)) {
4114 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4118 /* Restore the default pitch */
4119 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4120 LEAVE_GL();
4122 /* Don't delete PBO memory */
4123 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4124 HeapFree(GetProcessHeap(), 0, mem);
4128 if(rect == NULL) {
4129 This->Flags |= flag;
4132 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
4133 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4134 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4135 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4138 return WINED3D_OK;
4141 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
4142 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4143 IWineD3DSwapChain *swapchain = NULL;
4145 /* Update the drawable size method */
4146 if(container) {
4147 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4149 if(swapchain) {
4150 This->get_drawable_size = get_drawable_size_swapchain;
4151 IWineD3DSwapChain_Release(swapchain);
4152 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4153 switch(wined3d_settings.offscreen_rendering_mode) {
4154 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4155 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4156 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4160 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4163 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4164 return SURFACE_OPENGL;
4167 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4169 /* IUnknown */
4170 IWineD3DBaseSurfaceImpl_QueryInterface,
4171 IWineD3DBaseSurfaceImpl_AddRef,
4172 IWineD3DSurfaceImpl_Release,
4173 /* IWineD3DResource */
4174 IWineD3DBaseSurfaceImpl_GetParent,
4175 IWineD3DBaseSurfaceImpl_GetDevice,
4176 IWineD3DBaseSurfaceImpl_SetPrivateData,
4177 IWineD3DBaseSurfaceImpl_GetPrivateData,
4178 IWineD3DBaseSurfaceImpl_FreePrivateData,
4179 IWineD3DBaseSurfaceImpl_SetPriority,
4180 IWineD3DBaseSurfaceImpl_GetPriority,
4181 IWineD3DSurfaceImpl_PreLoad,
4182 IWineD3DSurfaceImpl_UnLoad,
4183 IWineD3DBaseSurfaceImpl_GetType,
4184 /* IWineD3DSurface */
4185 IWineD3DBaseSurfaceImpl_GetContainer,
4186 IWineD3DBaseSurfaceImpl_GetDesc,
4187 IWineD3DSurfaceImpl_LockRect,
4188 IWineD3DSurfaceImpl_UnlockRect,
4189 IWineD3DSurfaceImpl_GetDC,
4190 IWineD3DSurfaceImpl_ReleaseDC,
4191 IWineD3DSurfaceImpl_Flip,
4192 IWineD3DSurfaceImpl_Blt,
4193 IWineD3DBaseSurfaceImpl_GetBltStatus,
4194 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4195 IWineD3DBaseSurfaceImpl_IsLost,
4196 IWineD3DBaseSurfaceImpl_Restore,
4197 IWineD3DSurfaceImpl_BltFast,
4198 IWineD3DBaseSurfaceImpl_GetPalette,
4199 IWineD3DBaseSurfaceImpl_SetPalette,
4200 IWineD3DSurfaceImpl_RealizePalette,
4201 IWineD3DBaseSurfaceImpl_SetColorKey,
4202 IWineD3DBaseSurfaceImpl_GetPitch,
4203 IWineD3DSurfaceImpl_SetMem,
4204 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4205 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4206 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4207 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4208 IWineD3DBaseSurfaceImpl_SetClipper,
4209 IWineD3DBaseSurfaceImpl_GetClipper,
4210 /* Internal use: */
4211 IWineD3DSurfaceImpl_AddDirtyRect,
4212 IWineD3DSurfaceImpl_LoadTexture,
4213 IWineD3DSurfaceImpl_BindTexture,
4214 IWineD3DSurfaceImpl_SaveSnapshot,
4215 IWineD3DSurfaceImpl_SetContainer,
4216 IWineD3DSurfaceImpl_SetGlTextureDesc,
4217 IWineD3DSurfaceImpl_GetGlDesc,
4218 IWineD3DSurfaceImpl_GetData,
4219 IWineD3DSurfaceImpl_SetFormat,
4220 IWineD3DSurfaceImpl_PrivateSetup,
4221 IWineD3DSurfaceImpl_ModifyLocation,
4222 IWineD3DSurfaceImpl_LoadLocation,
4223 IWineD3DSurfaceImpl_GetImplType