wined3d: Add ENTER_GL/LEAVE_GL in IWineD3DSurfaceImpl_Release.
[wine.git] / dlls / wined3d / surface.c
blob28ffdcbc7eeda7f1ac39615b3766ae31b98bbb6d
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 ENTER_GL();
305 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
306 width, height, 0 /* border */, This->resource.size, mem));
307 LEAVE_GL();
310 return;
313 ENTER_GL();
315 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
316 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
317 /* In some cases we want to disable client storage.
318 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
319 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
320 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
321 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
322 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
324 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
325 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
326 This->Flags &= ~SFLAG_CLIENT;
327 enable_client_storage = TRUE;
328 } else {
329 This->Flags |= SFLAG_CLIENT;
331 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
332 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
334 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
337 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
338 checkGLcall("glTexImage2D");
340 if(enable_client_storage) {
341 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
342 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
344 LEAVE_GL();
346 This->Flags |= SFLAG_ALLOCATED;
349 /* In D3D the depth stencil dimensions have to be greater than or equal to the
350 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
351 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
352 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
353 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
354 renderbuffer_entry_t *entry;
355 GLuint renderbuffer = 0;
356 unsigned int src_width, src_height;
358 src_width = This->pow2Width;
359 src_height = This->pow2Height;
361 /* A depth stencil smaller than the render target is not valid */
362 if (width > src_width || height > src_height) return;
364 /* Remove any renderbuffer set if the sizes match */
365 if (width == src_width && height == src_height) {
366 This->current_renderbuffer = NULL;
367 return;
370 /* Look if we've already got a renderbuffer of the correct dimensions */
371 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
372 if (entry->width == width && entry->height == height) {
373 renderbuffer = entry->id;
374 This->current_renderbuffer = entry;
375 break;
379 if (!renderbuffer) {
380 const GlPixelFormatDesc *glDesc;
381 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
383 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
384 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
385 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
387 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
388 entry->width = width;
389 entry->height = height;
390 entry->id = renderbuffer;
391 list_add_head(&This->renderbuffers, &entry->entry);
393 This->current_renderbuffer = entry;
396 checkGLcall("set_compatible_renderbuffer");
399 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
400 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
401 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
403 TRACE("(%p) : swapchain %p\n", This, swapchain);
405 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
406 TRACE("Returning GL_BACK\n");
407 return GL_BACK;
408 } else if (swapchain_impl->frontBuffer == iface) {
409 TRACE("Returning GL_FRONT\n");
410 return GL_FRONT;
413 FIXME("Higher back buffer, returning GL_BACK\n");
414 return GL_BACK;
417 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
418 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
419 ULONG ref = InterlockedDecrement(&This->resource.ref);
420 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
421 if (ref == 0) {
422 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
423 renderbuffer_entry_t *entry, *entry2;
424 TRACE("(%p) : cleaning up\n", This);
426 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
428 /* Need a context to destroy the texture. Use the currently active render target, but only if
429 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
430 * When destroying the primary rt, Uninit3D will activate a context before doing anything
432 if(device->render_targets && device->render_targets[0]) {
433 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
436 TRACE("Deleting texture %d\n", This->glDescription.textureName);
437 ENTER_GL();
438 glDeleteTextures(1, &This->glDescription.textureName);
439 LEAVE_GL();
442 if(This->Flags & SFLAG_PBO) {
443 /* Delete the PBO */
444 ENTER_GL();
445 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
446 LEAVE_GL();
449 if(This->Flags & SFLAG_DIBSECTION) {
450 /* Release the DC */
451 SelectObject(This->hDC, This->dib.holdbitmap);
452 DeleteDC(This->hDC);
453 /* Release the DIB section */
454 DeleteObject(This->dib.DIBsection);
455 This->dib.bitmap_data = NULL;
456 This->resource.allocatedMemory = NULL;
458 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
460 HeapFree(GetProcessHeap(), 0, This->palette9);
462 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
463 if(iface == device->ddraw_primary)
464 device->ddraw_primary = NULL;
466 ENTER_GL();
467 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
468 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
469 HeapFree(GetProcessHeap(), 0, entry);
471 LEAVE_GL();
473 TRACE("(%p) Released\n", This);
474 HeapFree(GetProcessHeap(), 0, This);
477 return ref;
480 /* ****************************************************
481 IWineD3DSurface IWineD3DResource parts follow
482 **************************************************** */
484 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
485 /* TODO: check for locks */
486 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
487 IWineD3DBaseTexture *baseTexture = NULL;
488 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
490 TRACE("(%p)Checking to see if the container is a base texture\n", This);
491 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
492 TRACE("Passing to container\n");
493 IWineD3DBaseTexture_PreLoad(baseTexture);
494 IWineD3DBaseTexture_Release(baseTexture);
495 } else {
496 TRACE("(%p) : About to load surface\n", This);
498 if(!device->isInDraw) {
499 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
502 ENTER_GL();
503 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
504 if (!This->glDescription.level) {
505 if (!This->glDescription.textureName) {
506 glGenTextures(1, &This->glDescription.textureName);
507 checkGLcall("glGenTextures");
508 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
510 glBindTexture(This->glDescription.target, This->glDescription.textureName);
511 checkGLcall("glBindTexture");
512 IWineD3DSurface_LoadTexture(iface, FALSE);
513 /* This is where we should be reducing the amount of GLMemoryUsed */
514 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
515 /* assume this is a coding error not a real error for now */
516 FIXME("Mipmap surface has a glTexture bound to it!\n");
518 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
519 /* Tell opengl to try and keep this texture in video ram (well mostly) */
520 GLclampf tmp;
521 tmp = 0.9f;
522 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
524 LEAVE_GL();
526 return;
529 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
530 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
531 This->resource.allocatedMemory =
532 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
534 ENTER_GL();
535 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
536 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
537 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
538 checkGLcall("glGetBufferSubData");
539 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
540 checkGLcall("glDeleteBuffers");
541 LEAVE_GL();
543 This->pbo = 0;
544 This->Flags &= ~SFLAG_PBO;
547 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
548 IWineD3DBaseTexture *texture = NULL;
549 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
550 renderbuffer_entry_t *entry, *entry2;
551 TRACE("(%p)\n", iface);
553 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
554 /* Default pool resources are supposed to be destroyed before Reset is called.
555 * Implicit resources stay however. So this means we have an implicit render target
556 * or depth stencil. The content may be destroyed, but we still have to tear down
557 * opengl resources, so we cannot leave early.
559 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
560 } else {
561 /* Load the surface into system memory */
562 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
564 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
565 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
566 This->Flags &= ~SFLAG_ALLOCATED;
568 /* Destroy PBOs, but load them into real sysmem before */
569 if(This->Flags & SFLAG_PBO) {
570 surface_remove_pbo(This);
573 /* Destroy fbo render buffers. This is needed for implicit render targets, for
574 * all application-created targets the application has to release the surface
575 * before calling _Reset
577 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
578 ENTER_GL();
579 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
580 LEAVE_GL();
581 list_remove(&entry->entry);
582 HeapFree(GetProcessHeap(), 0, entry);
584 list_init(&This->renderbuffers);
585 This->current_renderbuffer = NULL;
587 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
588 * destroy it
590 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
591 if(!texture) {
592 ENTER_GL();
593 glDeleteTextures(1, &This->glDescription.textureName);
594 This->glDescription.textureName = 0;
595 LEAVE_GL();
596 } else {
597 IWineD3DBaseTexture_Release(texture);
599 return;
602 /* ******************************************************
603 IWineD3DSurface IWineD3DSurface parts follow
604 ****************************************************** */
606 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
607 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
608 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
609 if (This->glDescription.textureName == 0 && textureName != 0) {
610 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
611 IWineD3DSurface_AddDirtyRect(iface, NULL);
613 This->glDescription.textureName = textureName;
614 This->glDescription.target = target;
615 This->Flags &= ~SFLAG_ALLOCATED;
618 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
619 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
620 TRACE("(%p) : returning %p\n", This, &This->glDescription);
621 *glDescription = &This->glDescription;
624 /* TODO: think about moving this down to resource? */
625 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
626 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
627 /* 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 */
628 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
629 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
631 return (CONST void*)(This->resource.allocatedMemory);
634 /* Read the framebuffer back into the surface */
635 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
636 IWineD3DSwapChainImpl *swapchain;
637 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
638 BYTE *mem;
639 GLint fmt;
640 GLint type;
641 BYTE *row, *top, *bottom;
642 int i;
643 BOOL bpp;
644 RECT local_rect;
645 BOOL srcIsUpsideDown;
647 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
648 static BOOL warned = FALSE;
649 if(!warned) {
650 ERR("The application tries to lock the render target, but render target locking is disabled\n");
651 warned = TRUE;
653 return;
656 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
657 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
658 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
659 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
660 * context->last_was_blit set on the unlock.
662 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
663 ENTER_GL();
665 /* Select the correct read buffer, and give some debug output.
666 * There is no need to keep track of the current read buffer or reset it, every part of the code
667 * that reads sets the read buffer as desired.
669 if(!swapchain) {
670 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
671 * Read from the back buffer
673 TRACE("Locking offscreen render target\n");
674 glReadBuffer(myDevice->offscreenBuffer);
675 srcIsUpsideDown = TRUE;
676 } else {
677 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
678 TRACE("Locking %#x buffer\n", buffer);
679 glReadBuffer(buffer);
680 checkGLcall("glReadBuffer");
682 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
683 srcIsUpsideDown = FALSE;
686 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
687 if(!rect) {
688 local_rect.left = 0;
689 local_rect.top = 0;
690 local_rect.right = This->currentDesc.Width;
691 local_rect.bottom = This->currentDesc.Height;
692 } else {
693 local_rect = *rect;
695 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
697 switch(This->resource.format)
699 case WINED3DFMT_P8:
701 if(primary_render_target_is_p8(myDevice)) {
702 /* In case of P8 render targets the index is stored in the alpha component */
703 fmt = GL_ALPHA;
704 type = GL_UNSIGNED_BYTE;
705 mem = dest;
706 bpp = This->bytesPerPixel;
707 } else {
708 /* GL can't return palettized data, so read ARGB pixels into a
709 * separate block of memory and convert them into palettized format
710 * in software. Slow, but if the app means to use palettized render
711 * targets and locks it...
713 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
714 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
715 * for the color channels when palettizing the colors.
717 fmt = GL_RGB;
718 type = GL_UNSIGNED_BYTE;
719 pitch *= 3;
720 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
721 if(!mem) {
722 ERR("Out of memory\n");
723 LEAVE_GL();
724 return;
726 bpp = This->bytesPerPixel * 3;
729 break;
731 default:
732 mem = dest;
733 fmt = This->glDescription.glFormat;
734 type = This->glDescription.glType;
735 bpp = This->bytesPerPixel;
738 if(This->Flags & SFLAG_PBO) {
739 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
740 checkGLcall("glBindBufferARB");
743 glReadPixels(local_rect.left, local_rect.top,
744 local_rect.right - local_rect.left,
745 local_rect.bottom - local_rect.top,
746 fmt, type, mem);
747 vcheckGLcall("glReadPixels");
749 if(This->Flags & SFLAG_PBO) {
750 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
751 checkGLcall("glBindBufferARB");
753 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
754 * to get a pointer to it and perform the flipping in software. This is a lot
755 * faster than calling glReadPixels for each line. In case we want more speed
756 * we should rerender it flipped in a FBO and read the data back from the FBO. */
757 if(!srcIsUpsideDown) {
758 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
759 checkGLcall("glBindBufferARB");
761 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
762 checkGLcall("glMapBufferARB");
766 /* TODO: Merge this with the palettization loop below for P8 targets */
767 if(!srcIsUpsideDown) {
768 UINT len, off;
769 /* glReadPixels returns the image upside down, and there is no way to prevent this.
770 Flip the lines in software */
771 len = (local_rect.right - local_rect.left) * bpp;
772 off = local_rect.left * bpp;
774 row = HeapAlloc(GetProcessHeap(), 0, len);
775 if(!row) {
776 ERR("Out of memory\n");
777 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
778 LEAVE_GL();
779 return;
782 top = mem + pitch * local_rect.top;
783 bottom = mem + pitch * ( local_rect.bottom - local_rect.top - 1);
784 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
785 memcpy(row, top + off, len);
786 memcpy(top + off, bottom + off, len);
787 memcpy(bottom + off, row, len);
788 top += pitch;
789 bottom -= pitch;
791 HeapFree(GetProcessHeap(), 0, row);
793 /* Unmap the temp PBO buffer */
794 if(This->Flags & SFLAG_PBO) {
795 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
796 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
800 LEAVE_GL();
802 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
803 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
804 * the same color but we have no choice.
805 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
807 if((This->resource.format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice)) {
808 PALETTEENTRY *pal = NULL;
809 DWORD width = pitch / 3;
810 int x, y, c;
812 if(This->palette) {
813 pal = This->palette->palents;
814 } else {
815 ERR("Palette is missing, cannot perform inverse palette lookup\n");
816 HeapFree(GetProcessHeap(), 0, mem);
817 return ;
820 for(y = local_rect.top; y < local_rect.bottom; y++) {
821 for(x = local_rect.left; x < local_rect.right; x++) {
822 /* start lines pixels */
823 BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
824 BYTE *green = blue + 1;
825 BYTE *red = green + 1;
827 for(c = 0; c < 256; c++) {
828 if(*red == pal[c].peRed &&
829 *green == pal[c].peGreen &&
830 *blue == pal[c].peBlue)
832 *((BYTE *) dest + y * width + x) = c;
833 break;
838 HeapFree(GetProcessHeap(), 0, mem);
842 /* Read the framebuffer contents into a texture */
843 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This)
845 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
846 IWineD3DSwapChainImpl *swapchain;
847 int bpp;
848 GLenum format, internal, type;
849 CONVERT_TYPES convert;
850 BOOL srcIsUpsideDown;
851 GLint prevRead;
853 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
855 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
856 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
857 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
858 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
859 * context->last_was_blit set on the unlock.
861 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
862 surface_bind_and_dirtify(This);
863 ENTER_GL();
865 glGetIntegerv(GL_READ_BUFFER, &prevRead);
867 /* Select the correct read buffer, and give some debug output.
868 * There is no need to keep track of the current read buffer or reset it, every part of the code
869 * that reads sets the read buffer as desired.
871 if(!swapchain) {
872 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
873 * Read from the back buffer
875 TRACE("Locking offscreen render target\n");
876 glReadBuffer(device->offscreenBuffer);
877 srcIsUpsideDown = TRUE;
878 } else {
879 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
880 TRACE("Locking %#x buffer\n", buffer);
881 glReadBuffer(buffer);
882 checkGLcall("glReadBuffer");
884 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
885 srcIsUpsideDown = FALSE;
888 if(!(This->Flags & SFLAG_ALLOCATED)) {
889 surface_allocate_surface(This, internal, This->pow2Width,
890 This->pow2Height, format, type);
893 clear_unused_channels(This);
895 /* If !SrcIsUpsideDown we should flip the surface.
896 * This can be done using glCopyTexSubImage2D but this
897 * is VERY slow, so don't do that. We should prevent
898 * this code from getting called in such cases or perhaps
899 * we can use FBOs */
901 glCopyTexSubImage2D(This->glDescription.target,
902 This->glDescription.level,
903 0, 0, 0, 0,
904 This->currentDesc.Width,
905 This->currentDesc.Height);
906 checkGLcall("glCopyTexSubImage2D");
908 glReadBuffer(prevRead);
909 vcheckGLcall("glReadBuffer");
911 LEAVE_GL();
912 TRACE("Updated target %d\n", This->glDescription.target);
915 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
916 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
917 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
918 * changed
920 if(!(This->Flags & SFLAG_DYNLOCK)) {
921 This->lockCount++;
922 /* MAXLOCKCOUNT is defined in wined3d_private.h */
923 if(This->lockCount > MAXLOCKCOUNT) {
924 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
925 This->Flags |= SFLAG_DYNLOCK;
929 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
930 * Also don't create a PBO for systemmem surfaces.
932 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
933 GLenum error;
934 ENTER_GL();
936 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
937 error = glGetError();
938 if(This->pbo == 0 || error != GL_NO_ERROR) {
939 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
942 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
944 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
945 checkGLcall("glBindBufferARB");
947 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
948 checkGLcall("glBufferDataARB");
950 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
951 checkGLcall("glBindBufferARB");
953 /* We don't need the system memory anymore and we can't even use it for PBOs */
954 if(!(This->Flags & SFLAG_CLIENT)) {
955 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
956 This->resource.heapMemory = NULL;
958 This->resource.allocatedMemory = NULL;
959 This->Flags |= SFLAG_PBO;
960 LEAVE_GL();
961 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
962 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
963 * or a pbo to map
965 if(!This->resource.heapMemory) {
966 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
968 This->resource.allocatedMemory =
969 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
970 if(This->Flags & SFLAG_INSYSMEM) {
971 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
976 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
977 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
978 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
979 IWineD3DSwapChain *swapchain = NULL;
981 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
983 /* This is also done in the base class, but we have to verify this before loading any data from
984 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
985 * may interfere, and all other bad things may happen
987 if (This->Flags & SFLAG_LOCKED) {
988 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
989 return WINED3DERR_INVALIDCALL;
991 This->Flags |= SFLAG_LOCKED;
993 if (!(This->Flags & SFLAG_LOCKABLE))
995 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
998 if (Flags & WINED3DLOCK_DISCARD) {
999 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1000 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1001 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1002 This->Flags |= SFLAG_INSYSMEM;
1003 goto lock_end;
1006 if (This->Flags & SFLAG_INSYSMEM) {
1007 TRACE("Local copy is up to date, not downloading data\n");
1008 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1009 goto lock_end;
1012 /* Now download the surface content from opengl
1013 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1014 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
1016 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1017 if(swapchain || iface == myDevice->render_targets[0]) {
1018 const RECT *pass_rect = pRect;
1020 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1021 * because most caller functions do not need that. So do that here
1023 if(pRect &&
1024 pRect->top == 0 &&
1025 pRect->left == 0 &&
1026 pRect->right == This->currentDesc.Width &&
1027 pRect->bottom == This->currentDesc.Height) {
1028 pass_rect = NULL;
1031 switch(wined3d_settings.rendertargetlock_mode) {
1032 case RTL_TEXDRAW:
1033 case RTL_TEXTEX:
1034 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1035 #if 0
1036 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1037 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1038 * This may be faster on some cards
1040 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1041 #endif
1042 /* drop through */
1044 case RTL_AUTO:
1045 case RTL_READDRAW:
1046 case RTL_READTEX:
1047 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
1048 break;
1050 case RTL_DISABLE:
1051 break;
1053 if(swapchain) IWineD3DSwapChain_Release(swapchain);
1055 } else if(iface == myDevice->stencilBufferTarget) {
1056 /** the depth stencil in openGL has a format of GL_FLOAT
1057 * which should be good for WINED3DFMT_D16_LOCKABLE
1058 * and WINED3DFMT_D16
1059 * it is unclear what format the stencil buffer is in except.
1060 * 'Each index is converted to fixed point...
1061 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1062 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1063 * glReadPixels(This->lockedRect.left,
1064 * This->lockedRect.bottom - j - 1,
1065 * This->lockedRect.right - This->lockedRect.left,
1066 * 1,
1067 * GL_DEPTH_COMPONENT,
1068 * type,
1069 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1071 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1072 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1073 * none of that is the case the problem is not in this function :-)
1074 ********************************************/
1075 FIXME("Depth stencil locking not supported yet\n");
1076 } else {
1077 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1078 TRACE("locking an ordinary surface\n");
1079 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1082 lock_end:
1083 if(This->Flags & SFLAG_PBO) {
1084 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1085 ENTER_GL();
1086 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1087 checkGLcall("glBindBufferARB");
1089 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1090 if(This->resource.allocatedMemory) {
1091 ERR("The surface already has PBO memory allocated!\n");
1094 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1095 checkGLcall("glMapBufferARB");
1097 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1098 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1099 checkGLcall("glBindBufferARB");
1101 LEAVE_GL();
1104 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1105 /* Don't dirtify */
1106 } else {
1107 IWineD3DBaseTexture *pBaseTexture;
1109 * Dirtify on lock
1110 * as seen in msdn docs
1112 IWineD3DSurface_AddDirtyRect(iface, pRect);
1114 /** Dirtify Container if needed */
1115 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
1116 TRACE("Making container dirty\n");
1117 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1118 IWineD3DBaseTexture_Release(pBaseTexture);
1119 } else {
1120 TRACE("Surface is standalone, no need to dirty the container\n");
1124 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1127 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1128 GLint prev_store;
1129 GLint prev_rasterpos[4];
1130 GLint skipBytes = 0;
1131 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1132 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1133 IWineD3DSwapChainImpl *swapchain;
1135 /* Activate the correct context for the render target */
1136 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1137 ENTER_GL();
1139 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
1140 if(!swapchain) {
1141 /* Primary offscreen render target */
1142 TRACE("Offscreen render target\n");
1143 glDrawBuffer(myDevice->offscreenBuffer);
1144 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1145 } else {
1146 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1147 TRACE("Unlocking %#x buffer\n", buffer);
1148 glDrawBuffer(buffer);
1149 checkGLcall("glDrawBuffer");
1151 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1154 glFlush();
1155 vcheckGLcall("glFlush");
1156 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1157 vcheckGLcall("glIntegerv");
1158 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1159 vcheckGLcall("glIntegerv");
1160 glPixelZoom(1.0, -1.0);
1161 vcheckGLcall("glPixelZoom");
1163 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1164 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1165 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1167 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1168 vcheckGLcall("glRasterPos2f");
1170 /* Some drivers(radeon dri, others?) don't like exceptions during
1171 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1172 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1173 * catch to put the dib section in InSync mode, which leads to a crash
1174 * and a blocked x server on my radeon card.
1176 * The following lines read the dib section so it is put in inSync mode
1177 * before glDrawPixels is called and the crash is prevented. There won't
1178 * be any interfering gdi accesses, because UnlockRect is called from
1179 * ReleaseDC, and the app won't use the dc any more afterwards.
1181 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1182 volatile BYTE read;
1183 read = This->resource.allocatedMemory[0];
1186 if(This->Flags & SFLAG_PBO) {
1187 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1188 checkGLcall("glBindBufferARB");
1191 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1192 if(This->Flags & SFLAG_LOCKED) {
1193 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1194 (This->lockedRect.bottom - This->lockedRect.top)-1,
1195 fmt, type,
1196 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1197 checkGLcall("glDrawPixels");
1198 } else {
1199 glDrawPixels(This->currentDesc.Width,
1200 This->currentDesc.Height,
1201 fmt, type, mem);
1202 checkGLcall("glDrawPixels");
1205 if(This->Flags & SFLAG_PBO) {
1206 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1207 checkGLcall("glBindBufferARB");
1210 glPixelZoom(1.0,1.0);
1211 vcheckGLcall("glPixelZoom");
1213 glRasterPos3iv(&prev_rasterpos[0]);
1214 vcheckGLcall("glRasterPos3iv");
1216 /* Reset to previous pack row length */
1217 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1218 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1220 if(!swapchain) {
1221 glDrawBuffer(myDevice->offscreenBuffer);
1222 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1223 } else if(swapchain->backBuffer) {
1224 glDrawBuffer(GL_BACK);
1225 checkGLcall("glDrawBuffer(GL_BACK)");
1226 } else {
1227 glDrawBuffer(GL_FRONT);
1228 checkGLcall("glDrawBuffer(GL_FRONT)");
1230 LEAVE_GL();
1232 return;
1235 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1236 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1237 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1238 IWineD3DSwapChainImpl *swapchain = NULL;
1239 BOOL fullsurface;
1241 if (!(This->Flags & SFLAG_LOCKED)) {
1242 WARN("trying to Unlock an unlocked surf@%p\n", This);
1243 return WINED3DERR_INVALIDCALL;
1246 if (This->Flags & SFLAG_PBO) {
1247 TRACE("Freeing PBO memory\n");
1248 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1249 ENTER_GL();
1250 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1251 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1252 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1253 checkGLcall("glUnmapBufferARB");
1254 LEAVE_GL();
1255 This->resource.allocatedMemory = NULL;
1258 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1260 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1261 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1262 goto unlock_end;
1265 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1266 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1267 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1269 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1270 static BOOL warned = FALSE;
1271 if(!warned) {
1272 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1273 warned = TRUE;
1275 goto unlock_end;
1278 if(This->dirtyRect.left == 0 &&
1279 This->dirtyRect.top == 0 &&
1280 This->dirtyRect.right == This->currentDesc.Width &&
1281 This->dirtyRect.bottom == This->currentDesc.Height) {
1282 fullsurface = TRUE;
1283 } else {
1284 /* TODO: Proper partial rectangle tracking */
1285 fullsurface = FALSE;
1286 This->Flags |= SFLAG_INSYSMEM;
1289 switch(wined3d_settings.rendertargetlock_mode) {
1290 case RTL_READTEX:
1291 case RTL_TEXTEX:
1292 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1293 ENTER_GL();
1294 if (This->glDescription.textureName == 0) {
1295 glGenTextures(1, &This->glDescription.textureName);
1296 checkGLcall("glGenTextures");
1298 glBindTexture(This->glDescription.target, This->glDescription.textureName);
1299 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
1300 LEAVE_GL();
1301 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1302 /* drop through */
1304 case RTL_AUTO:
1305 case RTL_READDRAW:
1306 case RTL_TEXDRAW:
1307 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1308 break;
1311 if(!fullsurface) {
1312 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1313 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1314 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1315 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1316 * not fully up to date because only a subrectangle was read in LockRect.
1318 This->Flags &= ~SFLAG_INSYSMEM;
1319 This->Flags |= SFLAG_INDRAWABLE;
1322 This->dirtyRect.left = This->currentDesc.Width;
1323 This->dirtyRect.top = This->currentDesc.Height;
1324 This->dirtyRect.right = 0;
1325 This->dirtyRect.bottom = 0;
1326 } else if(iface == myDevice->stencilBufferTarget) {
1327 FIXME("Depth Stencil buffer locking is not implemented\n");
1328 } else {
1329 /* The rest should be a normal texture */
1330 IWineD3DBaseTextureImpl *impl;
1331 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1332 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1333 * states need resetting
1335 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1336 if(impl->baseTexture.bindCount) {
1337 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1339 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1343 unlock_end:
1344 This->Flags &= ~SFLAG_LOCKED;
1345 memset(&This->lockedRect, 0, sizeof(RECT));
1346 return WINED3D_OK;
1349 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1350 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1351 WINED3DLOCKED_RECT lock;
1352 HRESULT hr;
1353 RGBQUAD col[256];
1355 TRACE("(%p)->(%p)\n",This,pHDC);
1357 if(This->Flags & SFLAG_USERPTR) {
1358 ERR("Not supported on surfaces with an application-provided surfaces\n");
1359 return WINEDDERR_NODC;
1362 /* Give more detailed info for ddraw */
1363 if (This->Flags & SFLAG_DCINUSE)
1364 return WINEDDERR_DCALREADYCREATED;
1366 /* Can't GetDC if the surface is locked */
1367 if (This->Flags & SFLAG_LOCKED)
1368 return WINED3DERR_INVALIDCALL;
1370 /* According to Direct3D9 docs, only these formats are supported */
1371 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1372 if (This->resource.format != WINED3DFMT_R5G6B5 &&
1373 This->resource.format != WINED3DFMT_X1R5G5B5 &&
1374 This->resource.format != WINED3DFMT_R8G8B8 &&
1375 This->resource.format != WINED3DFMT_X8R8G8B8) return WINED3DERR_INVALIDCALL;
1378 memset(&lock, 0, sizeof(lock)); /* To be sure */
1380 /* Create a DIB section if there isn't a hdc yet */
1381 if(!This->hDC) {
1382 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1383 if(This->Flags & SFLAG_CLIENT) {
1384 IWineD3DSurface_PreLoad(iface);
1387 /* Use the dib section from now on if we are not using a PBO */
1388 if(!(This->Flags & SFLAG_PBO))
1389 This->resource.allocatedMemory = This->dib.bitmap_data;
1392 /* Lock the surface */
1393 hr = IWineD3DSurface_LockRect(iface,
1394 &lock,
1395 NULL,
1398 if(This->Flags & SFLAG_PBO) {
1399 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1400 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1403 if(FAILED(hr)) {
1404 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1405 /* keep the dib section */
1406 return hr;
1409 if(This->resource.format == WINED3DFMT_P8 ||
1410 This->resource.format == WINED3DFMT_A8P8) {
1411 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1412 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1413 unsigned int n;
1414 PALETTEENTRY *pal = NULL;
1416 if(This->palette) {
1417 pal = This->palette->palents;
1418 } else {
1419 IWineD3DSurfaceImpl *dds_primary = (IWineD3DSurfaceImpl *)This->resource.wineD3DDevice->ddraw_primary;
1420 if (dds_primary && dds_primary->palette)
1421 pal = dds_primary->palette->palents;
1424 if (pal) {
1425 for (n=0; n<256; n++) {
1426 col[n].rgbRed = pal[n].peRed;
1427 col[n].rgbGreen = pal[n].peGreen;
1428 col[n].rgbBlue = pal[n].peBlue;
1429 col[n].rgbReserved = 0;
1431 SetDIBColorTable(This->hDC, 0, 256, col);
1435 *pHDC = This->hDC;
1436 TRACE("returning %p\n",*pHDC);
1437 This->Flags |= SFLAG_DCINUSE;
1439 return WINED3D_OK;
1442 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1443 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1445 TRACE("(%p)->(%p)\n",This,hDC);
1447 if (!(This->Flags & SFLAG_DCINUSE))
1448 return WINED3DERR_INVALIDCALL;
1450 if (This->hDC !=hDC) {
1451 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1452 return WINED3DERR_INVALIDCALL;
1455 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1456 /* Copy the contents of the DIB over to the PBO */
1457 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1460 /* we locked first, so unlock now */
1461 IWineD3DSurface_UnlockRect(iface);
1463 This->Flags &= ~SFLAG_DCINUSE;
1465 return WINED3D_OK;
1468 /* ******************************************************
1469 IWineD3DSurface Internal (No mapping to directx api) parts follow
1470 ****************************************************** */
1472 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) {
1473 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1474 const GlPixelFormatDesc *glDesc;
1475 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1476 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1478 /* Default values: From the surface */
1479 *format = glDesc->glFormat;
1480 *type = glDesc->glType;
1481 *convert = NO_CONVERSION;
1482 *target_bpp = This->bytesPerPixel;
1484 if(srgb_mode) {
1485 *internal = glDesc->glGammaInternal;
1486 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1487 *internal = glDesc->rtInternal;
1488 } else {
1489 *internal = glDesc->glInternal;
1492 /* Ok, now look if we have to do any conversion */
1493 switch(This->resource.format) {
1494 case WINED3DFMT_P8:
1495 /* ****************
1496 Paletted Texture
1497 **************** */
1499 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1500 * of the two is available make sure texturing is requested as neither of the two works in
1501 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1502 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1503 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1504 * conflicts with this.
1506 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) && primary_render_target_is_p8(device))) || colorkey_active || !use_texturing ) {
1507 *format = GL_RGBA;
1508 *internal = GL_RGBA;
1509 *type = GL_UNSIGNED_BYTE;
1510 *target_bpp = 4;
1511 if(colorkey_active) {
1512 *convert = CONVERT_PALETTED_CK;
1513 } else {
1514 *convert = CONVERT_PALETTED;
1517 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1518 *format = GL_ALPHA;
1519 *internal = GL_RGBA;
1520 *type = GL_UNSIGNED_BYTE;
1521 *target_bpp = 1;
1524 break;
1526 case WINED3DFMT_R3G3B2:
1527 /* **********************
1528 GL_UNSIGNED_BYTE_3_3_2
1529 ********************** */
1530 if (colorkey_active) {
1531 /* This texture format will never be used.. So do not care about color keying
1532 up until the point in time it will be needed :-) */
1533 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1535 break;
1537 case WINED3DFMT_R5G6B5:
1538 if (colorkey_active) {
1539 *convert = CONVERT_CK_565;
1540 *format = GL_RGBA;
1541 *internal = GL_RGBA;
1542 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1544 break;
1546 case WINED3DFMT_X1R5G5B5:
1547 if (colorkey_active) {
1548 *convert = CONVERT_CK_5551;
1549 *format = GL_BGRA;
1550 *internal = GL_RGBA;
1551 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1553 break;
1555 case WINED3DFMT_R8G8B8:
1556 if (colorkey_active) {
1557 *convert = CONVERT_CK_RGB24;
1558 *format = GL_RGBA;
1559 *internal = GL_RGBA;
1560 *type = GL_UNSIGNED_INT_8_8_8_8;
1561 *target_bpp = 4;
1563 break;
1565 case WINED3DFMT_X8R8G8B8:
1566 if (colorkey_active) {
1567 *convert = CONVERT_RGB32_888;
1568 *format = GL_RGBA;
1569 *internal = GL_RGBA;
1570 *type = GL_UNSIGNED_INT_8_8_8_8;
1572 break;
1574 case WINED3DFMT_V8U8:
1575 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1576 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1577 *format = GL_DUDV_ATI;
1578 *internal = GL_DU8DV8_ATI;
1579 *type = GL_BYTE;
1580 /* No conversion - Just change the gl type */
1581 break;
1583 *convert = CONVERT_V8U8;
1584 *format = GL_BGR;
1585 *internal = GL_RGB8;
1586 *type = GL_UNSIGNED_BYTE;
1587 *target_bpp = 3;
1588 break;
1590 case WINED3DFMT_L6V5U5:
1591 *convert = CONVERT_L6V5U5;
1592 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1593 *target_bpp = 3;
1594 /* Use format and types from table */
1595 } else {
1596 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1597 *target_bpp = 2;
1598 *format = GL_RGB;
1599 *internal = GL_RGB5;
1600 *type = GL_UNSIGNED_SHORT_5_6_5;
1602 break;
1604 case WINED3DFMT_X8L8V8U8:
1605 *convert = CONVERT_X8L8V8U8;
1606 *target_bpp = 4;
1607 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1608 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1609 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1610 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1611 * the needed type and format parameter, so the internal format contains a
1612 * 4th component, which is returned as alpha
1614 } else {
1615 /* Not supported by GL_ATI_envmap_bumpmap */
1616 *format = GL_BGRA;
1617 *internal = GL_RGB8;
1618 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1620 break;
1622 case WINED3DFMT_Q8W8V8U8:
1623 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1624 *convert = CONVERT_Q8W8V8U8;
1625 *format = GL_BGRA;
1626 *internal = GL_RGBA8;
1627 *type = GL_UNSIGNED_BYTE;
1628 *target_bpp = 4;
1629 /* Not supported by GL_ATI_envmap_bumpmap */
1630 break;
1632 case WINED3DFMT_V16U16:
1633 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1634 *convert = CONVERT_V16U16;
1635 *format = GL_BGR;
1636 *internal = GL_RGB16_EXT;
1637 *type = GL_UNSIGNED_SHORT;
1638 *target_bpp = 6;
1639 /* What should I do here about GL_ATI_envmap_bumpmap?
1640 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1642 break;
1644 case WINED3DFMT_A4L4:
1645 /* A4L4 exists as an internal gl format, but for some reason there is not
1646 * format+type combination to load it. Thus convert it to A8L8, then load it
1647 * with A4L4 internal, but A8L8 format+type
1649 *convert = CONVERT_A4L4;
1650 *format = GL_LUMINANCE_ALPHA;
1651 *internal = GL_LUMINANCE4_ALPHA4;
1652 *type = GL_UNSIGNED_BYTE;
1653 *target_bpp = 2;
1654 break;
1656 case WINED3DFMT_R32F:
1657 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1658 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1659 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1660 * 1.0 instead.
1662 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1664 *convert = CONVERT_R32F;
1665 *format = GL_RGB;
1666 *internal = GL_RGB32F_ARB;
1667 *type = GL_FLOAT;
1668 *target_bpp = 12;
1669 break;
1671 case WINED3DFMT_R16F:
1672 /* Similar to R32F */
1673 *convert = CONVERT_R16F;
1674 *format = GL_RGB;
1675 *internal = GL_RGB16F_ARB;
1676 *type = GL_HALF_FLOAT_ARB;
1677 *target_bpp = 6;
1678 break;
1680 case WINED3DFMT_G16R16:
1681 *convert = CONVERT_G16R16;
1682 *format = GL_RGB;
1683 *internal = GL_RGB16_EXT;
1684 *type = GL_UNSIGNED_SHORT;
1685 *target_bpp = 6;
1686 break;
1688 default:
1689 break;
1692 return WINED3D_OK;
1695 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1696 BYTE *source, *dest;
1697 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1699 switch (convert) {
1700 case NO_CONVERSION:
1702 memcpy(dst, src, pitch * height);
1703 break;
1705 case CONVERT_PALETTED:
1706 case CONVERT_PALETTED_CK:
1708 IWineD3DPaletteImpl* pal = This->palette;
1709 BYTE table[256][4];
1710 unsigned int x, y;
1712 if( pal == NULL) {
1713 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1716 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1718 for (y = 0; y < height; y++)
1720 source = src + pitch * y;
1721 dest = dst + outpitch * y;
1722 /* This is an 1 bpp format, using the width here is fine */
1723 for (x = 0; x < width; x++) {
1724 BYTE color = *source++;
1725 *dest++ = table[color][0];
1726 *dest++ = table[color][1];
1727 *dest++ = table[color][2];
1728 *dest++ = table[color][3];
1732 break;
1734 case CONVERT_CK_565:
1736 /* Converting the 565 format in 5551 packed to emulate color-keying.
1738 Note : in all these conversion, it would be best to average the averaging
1739 pixels to get the color of the pixel that will be color-keyed to
1740 prevent 'color bleeding'. This will be done later on if ever it is
1741 too visible.
1743 Note2: Nvidia documents say that their driver does not support alpha + color keying
1744 on the same surface and disables color keying in such a case
1746 unsigned int x, y;
1747 WORD *Source;
1748 WORD *Dest;
1750 TRACE("Color keyed 565\n");
1752 for (y = 0; y < height; y++) {
1753 Source = (WORD *) (src + y * pitch);
1754 Dest = (WORD *) (dst + y * outpitch);
1755 for (x = 0; x < width; x++ ) {
1756 WORD color = *Source++;
1757 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1758 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1759 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1760 *Dest |= 0x0001;
1762 Dest++;
1766 break;
1768 case CONVERT_CK_5551:
1770 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1771 unsigned int x, y;
1772 WORD *Source;
1773 WORD *Dest;
1774 TRACE("Color keyed 5551\n");
1775 for (y = 0; y < height; y++) {
1776 Source = (WORD *) (src + y * pitch);
1777 Dest = (WORD *) (dst + y * outpitch);
1778 for (x = 0; x < width; x++ ) {
1779 WORD color = *Source++;
1780 *Dest = color;
1781 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1782 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1783 *Dest |= (1 << 15);
1785 else {
1786 *Dest &= ~(1 << 15);
1788 Dest++;
1792 break;
1794 case CONVERT_V8U8:
1796 unsigned int x, y;
1797 short *Source;
1798 unsigned char *Dest;
1799 for(y = 0; y < height; y++) {
1800 Source = (short *) (src + y * pitch);
1801 Dest = dst + y * outpitch;
1802 for (x = 0; x < width; x++ ) {
1803 long color = (*Source++);
1804 /* B */ Dest[0] = 0xff;
1805 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1806 /* R */ Dest[2] = (color) + 128; /* U */
1807 Dest += 3;
1810 break;
1813 case CONVERT_V16U16:
1815 unsigned int x, y;
1816 DWORD *Source;
1817 unsigned short *Dest;
1818 for(y = 0; y < height; y++) {
1819 Source = (DWORD *) (src + y * pitch);
1820 Dest = (unsigned short *) (dst + y * outpitch);
1821 for (x = 0; x < width; x++ ) {
1822 DWORD color = (*Source++);
1823 /* B */ Dest[0] = 0xffff;
1824 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1825 /* R */ Dest[2] = (color ) + 32768; /* U */
1826 Dest += 3;
1829 break;
1832 case CONVERT_Q8W8V8U8:
1834 unsigned int x, y;
1835 DWORD *Source;
1836 unsigned char *Dest;
1837 for(y = 0; y < height; y++) {
1838 Source = (DWORD *) (src + y * pitch);
1839 Dest = dst + y * outpitch;
1840 for (x = 0; x < width; x++ ) {
1841 long color = (*Source++);
1842 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1843 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1844 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1845 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1846 Dest += 4;
1849 break;
1852 case CONVERT_L6V5U5:
1854 unsigned int x, y;
1855 WORD *Source;
1856 unsigned char *Dest;
1858 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1859 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1860 * fixed function and shaders without further conversion once the surface is
1861 * loaded
1863 for(y = 0; y < height; y++) {
1864 Source = (WORD *) (src + y * pitch);
1865 Dest = dst + y * outpitch;
1866 for (x = 0; x < width; x++ ) {
1867 short color = (*Source++);
1868 unsigned char l = ((color >> 10) & 0xfc);
1869 char v = ((color >> 5) & 0x3e);
1870 char u = ((color ) & 0x1f);
1872 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1873 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1874 * shift. GL reads a signed value and converts it into an unsigned value.
1876 /* M */ Dest[2] = l << 1;
1878 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1879 * from 5 bit values to 8 bit values.
1881 /* V */ Dest[1] = v << 3;
1882 /* U */ Dest[0] = u << 3;
1883 Dest += 3;
1886 } else {
1887 for(y = 0; y < height; y++) {
1888 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1889 Source = (WORD *) (src + y * pitch);
1890 for (x = 0; x < width; x++ ) {
1891 short color = (*Source++);
1892 unsigned char l = ((color >> 10) & 0xfc);
1893 short v = ((color >> 5) & 0x3e);
1894 short u = ((color ) & 0x1f);
1895 short v_conv = v + 16;
1896 short u_conv = u + 16;
1898 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1899 Dest_s += 1;
1903 break;
1906 case CONVERT_X8L8V8U8:
1908 unsigned int x, y;
1909 DWORD *Source;
1910 unsigned char *Dest;
1912 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1913 /* This implementation works with the fixed function pipeline and shaders
1914 * without further modification after converting the surface.
1916 for(y = 0; y < height; y++) {
1917 Source = (DWORD *) (src + y * pitch);
1918 Dest = dst + y * outpitch;
1919 for (x = 0; x < width; x++ ) {
1920 long color = (*Source++);
1921 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1922 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1923 /* U */ Dest[0] = (color & 0xff); /* U */
1924 /* I */ Dest[3] = 255; /* X */
1925 Dest += 4;
1928 } else {
1929 /* Doesn't work correctly with the fixed function pipeline, but can work in
1930 * shaders if the shader is adjusted. (There's no use for this format in gl's
1931 * standard fixed function pipeline anyway).
1933 for(y = 0; y < height; y++) {
1934 Source = (DWORD *) (src + y * pitch);
1935 Dest = dst + y * outpitch;
1936 for (x = 0; x < width; x++ ) {
1937 long color = (*Source++);
1938 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1939 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1940 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1941 Dest += 4;
1945 break;
1948 case CONVERT_A4L4:
1950 unsigned int x, y;
1951 unsigned char *Source;
1952 unsigned char *Dest;
1953 for(y = 0; y < height; y++) {
1954 Source = src + y * pitch;
1955 Dest = dst + y * outpitch;
1956 for (x = 0; x < width; x++ ) {
1957 unsigned char color = (*Source++);
1958 /* A */ Dest[1] = (color & 0xf0) << 0;
1959 /* L */ Dest[0] = (color & 0x0f) << 4;
1960 Dest += 2;
1963 break;
1966 case CONVERT_R32F:
1968 unsigned int x, y;
1969 float *Source;
1970 float *Dest;
1971 for(y = 0; y < height; y++) {
1972 Source = (float *) (src + y * pitch);
1973 Dest = (float *) (dst + y * outpitch);
1974 for (x = 0; x < width; x++ ) {
1975 float color = (*Source++);
1976 Dest[0] = color;
1977 Dest[1] = 1.0;
1978 Dest[2] = 1.0;
1979 Dest += 3;
1982 break;
1985 case CONVERT_R16F:
1987 unsigned int x, y;
1988 WORD *Source;
1989 WORD *Dest;
1990 WORD one = 0x3c00;
1991 for(y = 0; y < height; y++) {
1992 Source = (WORD *) (src + y * pitch);
1993 Dest = (WORD *) (dst + y * outpitch);
1994 for (x = 0; x < width; x++ ) {
1995 WORD color = (*Source++);
1996 Dest[0] = color;
1997 Dest[1] = one;
1998 Dest[2] = one;
1999 Dest += 3;
2002 break;
2005 case CONVERT_G16R16:
2007 unsigned int x, y;
2008 WORD *Source;
2009 WORD *Dest;
2011 for(y = 0; y < height; y++) {
2012 Source = (WORD *) (src + y * pitch);
2013 Dest = (WORD *) (dst + y * outpitch);
2014 for (x = 0; x < width; x++ ) {
2015 WORD green = (*Source++);
2016 WORD red = (*Source++);
2017 Dest[0] = green;
2018 Dest[1] = red;
2019 Dest[2] = 0xffff;
2020 Dest += 3;
2023 break;
2026 default:
2027 ERR("Unsupported conversation type %d\n", convert);
2029 return WINED3D_OK;
2032 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2033 IWineD3DPaletteImpl* pal = This->palette;
2034 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2035 BOOL index_in_alpha = FALSE;
2036 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2037 int i;
2039 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2040 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2041 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2042 * duplicate entries. Store the color key in the unused alpha component to speed the
2043 * download up and to make conversion unneeded. */
2044 index_in_alpha = primary_render_target_is_p8(device);
2046 if (pal == NULL) {
2047 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2048 if(dxVersion <= 7) {
2049 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2050 return;
2053 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2054 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2055 capability flag is present (wine does advertise this capability) */
2056 for (i = 0; i < 256; i++) {
2057 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2058 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2059 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2060 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2062 } else {
2063 TRACE("Using surface palette %p\n", pal);
2064 /* Get the surface's palette */
2065 for (i = 0; i < 256; i++) {
2066 table[i][0] = pal->palents[i].peRed;
2067 table[i][1] = pal->palents[i].peGreen;
2068 table[i][2] = pal->palents[i].peBlue;
2070 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2071 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2072 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2073 of pixels that should be masked away is set to 0. */
2074 if(index_in_alpha) {
2075 table[i][3] = i;
2076 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2077 table[i][3] = 0x00;
2078 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2079 table[i][3] = pal->palents[i].peFlags;
2080 } else {
2081 table[i][3] = 0xFF;
2087 const char *fragment_palette_conversion =
2088 "!!ARBfp1.0\n"
2089 "TEMP index;\n"
2090 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
2091 "TEX index, fragment.texcoord[0], texture[0], 2D;\n" /* The alpha-component contains the palette index */
2092 "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 */
2093 "TEX result.color, index.a, texture[1], 1D;\n" /* use the alpha-component as a index in the palette to get the final color */
2094 "END";
2096 /* This function is used in case of 8bit paletted textures to upload the palette.
2097 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2098 extensions like ATI_fragment_shaders is possible.
2100 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2101 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2102 BYTE table[256][4];
2103 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2105 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2107 /* Try to use the paletted texture extension */
2108 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2110 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2111 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2113 else
2115 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2116 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2117 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2119 /* Create the fragment program if we don't have it */
2120 if(!device->paletteConversionShader)
2122 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2123 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2124 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2125 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2126 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2129 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2130 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2132 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2133 glEnable(GL_TEXTURE_1D);
2134 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2136 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2137 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2138 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2139 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2141 /* Switch back to unit 0 in which the 2D texture will be stored. */
2142 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2144 /* Rebind the texture because it isn't bound anymore */
2145 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2149 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2150 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2152 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2153 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2154 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2156 return FALSE;
2159 if(This->palette9) {
2160 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2161 return FALSE;
2163 } else {
2164 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2166 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2167 return TRUE;
2170 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2171 GLboolean oldwrite[4];
2173 /* Some formats have only some color channels, and the others are 1.0.
2174 * since our rendering renders to all channels, and those pixel formats
2175 * are emulated by using a full texture with the other channels set to 1.0
2176 * manually, clear the unused channels.
2178 * This could be done with hacking colorwriteenable to mask the colors,
2179 * but before drawing the buffer would have to be cleared too, so there's
2180 * no gain in that
2182 switch(This->resource.format) {
2183 case WINED3DFMT_R16F:
2184 case WINED3DFMT_R32F:
2185 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2186 /* Do not activate a context, the correct drawable is active already
2187 * though just the read buffer is set, make sure to have the correct draw
2188 * buffer too
2190 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2191 glDisable(GL_SCISSOR_TEST);
2192 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2193 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2194 glClearColor(0.0, 1.0, 1.0, 1.0);
2195 glClear(GL_COLOR_BUFFER_BIT);
2196 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2197 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2198 checkGLcall("Unused channel clear\n");
2199 break;
2201 default: break;
2205 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2206 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2208 if (!(This->Flags & SFLAG_INTEXTURE)) {
2209 TRACE("Reloading because surface is dirty\n");
2210 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2211 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2212 /* Reload: vice versa OR */
2213 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2214 /* Also reload: Color key is active AND the color key has changed */
2215 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2216 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2217 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2218 TRACE("Reloading because of color keying\n");
2219 /* To perform the color key conversion we need a sysmem copy of
2220 * the surface. Make sure we have it
2223 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2224 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2225 /* TODO: This is not necessarily needed with hw palettized texture support */
2226 This->Flags &= ~SFLAG_INTEXTURE;
2227 } else if(palette9_changed(This)) {
2228 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2229 /* TODO: This is not necessarily needed with hw palettized texture support */
2230 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2232 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2233 This->Flags &= ~SFLAG_INTEXTURE;
2234 } else {
2235 TRACE("surface is already in texture\n");
2236 return WINED3D_OK;
2239 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2240 * These resources are not bound by device size or format restrictions. Because of this,
2241 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2242 * However, these resources can always be created, locked, and copied.
2244 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2246 FIXME("(%p) Operation not supported for scratch textures\n",This);
2247 return WINED3DERR_INVALIDCALL;
2250 This->srgb = srgb_mode;
2251 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2253 #if 0
2255 static unsigned int gen = 0;
2256 char buffer[4096];
2257 ++gen;
2258 if ((gen % 10) == 0) {
2259 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2260 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2263 * debugging crash code
2264 if (gen == 250) {
2265 void** test = NULL;
2266 *test = 0;
2270 #endif
2272 if (!(This->Flags & SFLAG_DONOTFREE)) {
2273 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2274 This->resource.allocatedMemory = NULL;
2275 This->resource.heapMemory = NULL;
2276 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2279 return WINED3D_OK;
2282 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2283 /* TODO: check for locks */
2284 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2285 IWineD3DBaseTexture *baseTexture = NULL;
2286 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2288 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2289 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2290 TRACE("Passing to container\n");
2291 IWineD3DBaseTexture_BindTexture(baseTexture);
2292 IWineD3DBaseTexture_Release(baseTexture);
2293 } else {
2294 TRACE("(%p) : Binding surface\n", This);
2296 if(!device->isInDraw) {
2297 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2299 ENTER_GL();
2300 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2301 LEAVE_GL();
2303 return;
2306 #include <errno.h>
2307 #include <stdio.h>
2308 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2309 FILE* f = NULL;
2310 UINT i, y;
2311 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2312 char *allocatedMemory;
2313 char *textureRow;
2314 IWineD3DSwapChain *swapChain = NULL;
2315 int width, height;
2316 GLuint tmpTexture = 0;
2317 DWORD color;
2318 /*FIXME:
2319 Textures may not be stored in ->allocatedgMemory and a GlTexture
2320 so we should lock the surface before saving a snapshot, or at least check that
2322 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2323 by calling GetTexImage and in compressed form by calling
2324 GetCompressedTexImageARB. Queried compressed images can be saved and
2325 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2326 texture images do not need to be processed by the GL and should
2327 significantly improve texture loading performance relative to uncompressed
2328 images. */
2330 /* Setup the width and height to be the internal texture width and height. */
2331 width = This->pow2Width;
2332 height = This->pow2Height;
2333 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2334 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2336 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2337 /* if were not a real texture then read the back buffer into a real texture */
2338 /* we don't want to interfere with the back buffer so read the data into a temporary
2339 * texture and then save the data out of the temporary texture
2341 GLint prevRead;
2342 ENTER_GL();
2343 TRACE("(%p) Reading render target into texture\n", This);
2344 glEnable(GL_TEXTURE_2D);
2346 glGenTextures(1, &tmpTexture);
2347 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2349 glTexImage2D(GL_TEXTURE_2D,
2351 GL_RGBA,
2352 width,
2353 height,
2354 0/*border*/,
2355 GL_RGBA,
2356 GL_UNSIGNED_INT_8_8_8_8_REV,
2357 NULL);
2359 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2360 vcheckGLcall("glGetIntegerv");
2361 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2362 vcheckGLcall("glReadBuffer");
2363 glCopyTexImage2D(GL_TEXTURE_2D,
2365 GL_RGBA,
2368 width,
2369 height,
2372 checkGLcall("glCopyTexImage2D");
2373 glReadBuffer(prevRead);
2374 LEAVE_GL();
2376 } else { /* bind the real texture, and make sure it up to date */
2377 IWineD3DSurface_PreLoad(iface);
2379 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2380 ENTER_GL();
2381 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2382 glGetTexImage(GL_TEXTURE_2D,
2383 This->glDescription.level,
2384 GL_RGBA,
2385 GL_UNSIGNED_INT_8_8_8_8_REV,
2386 allocatedMemory);
2387 checkGLcall("glTexImage2D");
2388 if (tmpTexture) {
2389 glBindTexture(GL_TEXTURE_2D, 0);
2390 glDeleteTextures(1, &tmpTexture);
2392 LEAVE_GL();
2394 f = fopen(filename, "w+");
2395 if (NULL == f) {
2396 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2397 return WINED3DERR_INVALIDCALL;
2399 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2400 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2401 /* TGA header */
2402 fputc(0,f);
2403 fputc(0,f);
2404 fputc(2,f);
2405 fputc(0,f);
2406 fputc(0,f);
2407 fputc(0,f);
2408 fputc(0,f);
2409 fputc(0,f);
2410 fputc(0,f);
2411 fputc(0,f);
2412 fputc(0,f);
2413 fputc(0,f);
2414 /* short width*/
2415 fwrite(&width,2,1,f);
2416 /* short height */
2417 fwrite(&height,2,1,f);
2418 /* format rgba */
2419 fputc(0x20,f);
2420 fputc(0x28,f);
2421 /* raw data */
2422 /* 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 */
2423 if(swapChain)
2424 textureRow = allocatedMemory + (width * (height - 1) *4);
2425 else
2426 textureRow = allocatedMemory;
2427 for (y = 0 ; y < height; y++) {
2428 for (i = 0; i < width; i++) {
2429 color = *((DWORD*)textureRow);
2430 fputc((color >> 16) & 0xFF, f); /* B */
2431 fputc((color >> 8) & 0xFF, f); /* G */
2432 fputc((color >> 0) & 0xFF, f); /* R */
2433 fputc((color >> 24) & 0xFF, f); /* A */
2434 textureRow += 4;
2436 /* take two rows of the pointer to the texture memory */
2437 if(swapChain)
2438 (textureRow-= width << 3);
2441 TRACE("Closing file\n");
2442 fclose(f);
2444 if(swapChain) {
2445 IWineD3DSwapChain_Release(swapChain);
2447 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2448 return WINED3D_OK;
2452 * Slightly inefficient way to handle multiple dirty rects but it works :)
2454 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2455 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2456 IWineD3DBaseTexture *baseTexture = NULL;
2458 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2459 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2461 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2462 if (NULL != pDirtyRect) {
2463 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2464 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2465 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2466 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2467 } else {
2468 This->dirtyRect.left = 0;
2469 This->dirtyRect.top = 0;
2470 This->dirtyRect.right = This->currentDesc.Width;
2471 This->dirtyRect.bottom = This->currentDesc.Height;
2473 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2474 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2475 /* if the container is a basetexture then mark it dirty. */
2476 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2477 TRACE("Passing to container\n");
2478 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2479 IWineD3DBaseTexture_Release(baseTexture);
2481 return WINED3D_OK;
2484 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2485 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2486 HRESULT hr;
2487 const GlPixelFormatDesc *glDesc;
2488 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2490 TRACE("(%p) : Calling base function first\n", This);
2491 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2492 if(SUCCEEDED(hr)) {
2493 /* Setup some glformat defaults */
2494 This->glDescription.glFormat = glDesc->glFormat;
2495 This->glDescription.glFormatInternal = glDesc->glInternal;
2496 This->glDescription.glType = glDesc->glType;
2498 This->Flags &= ~SFLAG_ALLOCATED;
2499 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2500 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2502 return hr;
2505 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2506 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2508 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2509 WARN("Surface is locked or the HDC is in use\n");
2510 return WINED3DERR_INVALIDCALL;
2513 if(Mem && Mem != This->resource.allocatedMemory) {
2514 void *release = NULL;
2516 /* Do I have to copy the old surface content? */
2517 if(This->Flags & SFLAG_DIBSECTION) {
2518 /* Release the DC. No need to hold the critical section for the update
2519 * Thread because this thread runs only on front buffers, but this method
2520 * fails for render targets in the check above.
2522 SelectObject(This->hDC, This->dib.holdbitmap);
2523 DeleteDC(This->hDC);
2524 /* Release the DIB section */
2525 DeleteObject(This->dib.DIBsection);
2526 This->dib.bitmap_data = NULL;
2527 This->resource.allocatedMemory = NULL;
2528 This->hDC = NULL;
2529 This->Flags &= ~SFLAG_DIBSECTION;
2530 } else if(!(This->Flags & SFLAG_USERPTR)) {
2531 release = This->resource.heapMemory;
2532 This->resource.heapMemory = NULL;
2534 This->resource.allocatedMemory = Mem;
2535 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2537 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2538 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2540 /* For client textures opengl has to be notified */
2541 if(This->Flags & SFLAG_CLIENT) {
2542 This->Flags &= ~SFLAG_ALLOCATED;
2543 IWineD3DSurface_PreLoad(iface);
2544 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2547 /* Now free the old memory if any */
2548 HeapFree(GetProcessHeap(), 0, release);
2549 } else if(This->Flags & SFLAG_USERPTR) {
2550 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2551 This->resource.allocatedMemory = NULL;
2552 /* HeapMemory should be NULL already */
2553 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2554 This->Flags &= ~SFLAG_USERPTR;
2556 if(This->Flags & SFLAG_CLIENT) {
2557 This->Flags &= ~SFLAG_ALLOCATED;
2558 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2559 IWineD3DSurface_PreLoad(iface);
2562 return WINED3D_OK;
2565 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2566 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2567 IWineD3DSwapChainImpl *swapchain = NULL;
2568 HRESULT hr;
2569 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2571 /* Flipping is only supported on RenderTargets */
2572 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2574 if(override) {
2575 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2576 * FIXME("(%p) Target override is not supported by now\n", This);
2577 * Additionally, it isn't really possible to support triple-buffering
2578 * properly on opengl at all
2582 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2583 if(!swapchain) {
2584 ERR("Flipped surface is not on a swapchain\n");
2585 return WINEDDERR_NOTFLIPPABLE;
2588 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2589 * and only d3d8 and d3d9 apps specify the presentation interval
2591 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2592 /* Most common case first to avoid wasting time on all the other cases */
2593 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2594 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2595 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2596 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2597 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2598 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2599 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2600 } else {
2601 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2604 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2605 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2606 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2607 return hr;
2610 /* Does a direct frame buffer -> texture copy. Stretching is done
2611 * with single pixel copy calls
2613 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2614 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2615 float xrel, yrel;
2616 UINT row;
2617 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2620 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2621 ENTER_GL();
2622 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2624 /* TODO: Do we need GL_TEXTURE_2D enabled fpr copyteximage? */
2625 glEnable(This->glDescription.target);
2626 checkGLcall("glEnable(This->glDescription.target)");
2628 /* Bind the target texture */
2629 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2630 checkGLcall("glBindTexture");
2631 if(!swapchain) {
2632 glReadBuffer(myDevice->offscreenBuffer);
2633 } else {
2634 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2635 glReadBuffer(buffer);
2637 checkGLcall("glReadBuffer");
2639 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2640 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2642 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2643 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2645 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2646 ERR("Texture filtering not supported in direct blit\n");
2648 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2649 ERR("Texture filtering not supported in direct blit\n");
2652 if(upsidedown &&
2653 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2654 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2655 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2657 glCopyTexSubImage2D(This->glDescription.target,
2658 This->glDescription.level,
2659 drect->x1, drect->y1, /* xoffset, yoffset */
2660 srect->x1, Src->currentDesc.Height - srect->y2,
2661 drect->x2 - drect->x1, drect->y2 - drect->y1);
2662 } else {
2663 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2664 /* I have to process this row by row to swap the image,
2665 * otherwise it would be upside down, so stretching in y direction
2666 * doesn't cost extra time
2668 * However, stretching in x direction can be avoided if not necessary
2670 for(row = drect->y1; row < drect->y2; row++) {
2671 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2672 /* Well, that stuff works, but it's very slow.
2673 * find a better way instead
2675 UINT col;
2677 for(col = drect->x1; col < drect->x2; col++) {
2678 glCopyTexSubImage2D(This->glDescription.target,
2679 This->glDescription.level,
2680 drect->x1 + col, row, /* xoffset, yoffset */
2681 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2682 1, 1);
2684 } else {
2685 glCopyTexSubImage2D(This->glDescription.target,
2686 This->glDescription.level,
2687 drect->x1, row, /* xoffset, yoffset */
2688 srect->x1, yoffset - (int) (row * yrel),
2689 drect->x2-drect->x1, 1);
2693 vcheckGLcall("glCopyTexSubImage2D");
2695 /* Leave the opengl state valid for blitting */
2696 glDisable(This->glDescription.target);
2697 checkGLcall("glDisable(This->glDescription.target)");
2699 LEAVE_GL();
2702 /* Uses the hardware to stretch and flip the image */
2703 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2704 GLuint src, backup = 0;
2705 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2706 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2707 float left, right, top, bottom; /* Texture coordinates */
2708 UINT fbwidth = Src->currentDesc.Width;
2709 UINT fbheight = Src->currentDesc.Height;
2710 GLenum drawBuffer = GL_BACK;
2711 GLenum texture_target;
2713 TRACE("Using hwstretch blit\n");
2714 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2715 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2716 ENTER_GL();
2718 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2720 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2721 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2723 if(GL_LIMITS(aux_buffers) >= 2) {
2724 /* Got more than one aux buffer? Use the 2nd aux buffer */
2725 drawBuffer = GL_AUX1;
2726 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2727 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2728 drawBuffer = GL_AUX0;
2731 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2732 glGenTextures(1, &backup);
2733 checkGLcall("glGenTextures\n");
2734 glBindTexture(GL_TEXTURE_2D, backup);
2735 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2736 texture_target = GL_TEXTURE_2D;
2737 } else {
2738 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2739 * we are reading from the back buffer, the backup can be used as source texture
2741 if(Src->glDescription.textureName == 0) {
2742 /* Get it a description */
2743 IWineD3DSurface_PreLoad(SrcSurface);
2745 texture_target = Src->glDescription.target;
2746 glBindTexture(texture_target, Src->glDescription.textureName);
2747 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2748 glEnable(texture_target);
2749 checkGLcall("glEnable(texture_target)");
2751 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2752 Src->Flags &= ~SFLAG_INTEXTURE;
2755 glReadBuffer(GL_BACK);
2756 checkGLcall("glReadBuffer(GL_BACK)");
2758 /* TODO: Only back up the part that will be overwritten */
2759 glCopyTexSubImage2D(texture_target, 0,
2760 0, 0 /* read offsets */,
2761 0, 0,
2762 fbwidth,
2763 fbheight);
2765 checkGLcall("glCopyTexSubImage2D");
2767 /* No issue with overriding these - the sampler is dirty due to blit usage */
2768 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2769 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2770 checkGLcall("glTexParameteri");
2771 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2772 minMipLookup[Filter][WINED3DTEXF_NONE]);
2773 checkGLcall("glTexParameteri");
2775 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2776 src = backup ? backup : Src->glDescription.textureName;
2777 } else {
2778 glReadBuffer(GL_FRONT);
2779 checkGLcall("glReadBuffer(GL_FRONT)");
2781 glGenTextures(1, &src);
2782 checkGLcall("glGenTextures(1, &src)");
2783 glBindTexture(GL_TEXTURE_2D, src);
2784 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2786 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2787 * out for power of 2 sizes
2789 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2790 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2791 checkGLcall("glTexImage2D");
2792 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2793 0, 0 /* read offsets */,
2794 0, 0,
2795 fbwidth,
2796 fbheight);
2798 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2799 checkGLcall("glTexParameteri");
2800 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2801 checkGLcall("glTexParameteri");
2803 glReadBuffer(GL_BACK);
2804 checkGLcall("glReadBuffer(GL_BACK)");
2806 if(texture_target != GL_TEXTURE_2D) {
2807 glDisable(texture_target);
2808 glEnable(GL_TEXTURE_2D);
2809 texture_target = GL_TEXTURE_2D;
2812 checkGLcall("glEnd and previous");
2814 left = (float) srect->x1 / (float) Src->pow2Width;
2815 right = (float) srect->x2 / (float) Src->pow2Width;
2817 if(upsidedown) {
2818 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2819 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2820 } else {
2821 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2822 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2825 /* draw the source texture stretched and upside down. The correct surface is bound already */
2826 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
2827 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
2829 glDrawBuffer(drawBuffer);
2830 glReadBuffer(drawBuffer);
2832 glBegin(GL_QUADS);
2833 /* bottom left */
2834 glTexCoord2f(left, bottom);
2835 glVertex2i(0, fbheight);
2837 /* top left */
2838 glTexCoord2f(left, top);
2839 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2841 /* top right */
2842 glTexCoord2f(right, top);
2843 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2845 /* bottom right */
2846 glTexCoord2f(right, bottom);
2847 glVertex2i(drect->x2 - drect->x1, fbheight);
2848 glEnd();
2849 checkGLcall("glEnd and previous");
2851 if(texture_target != This->glDescription.target) {
2852 glDisable(texture_target);
2853 glEnable(This->glDescription.target);
2854 texture_target = This->glDescription.target;
2857 /* Now read the stretched and upside down image into the destination texture */
2858 glBindTexture(texture_target, This->glDescription.textureName);
2859 checkGLcall("glBindTexture");
2860 glCopyTexSubImage2D(texture_target,
2862 drect->x1, drect->y1, /* xoffset, yoffset */
2863 0, 0, /* We blitted the image to the origin */
2864 drect->x2 - drect->x1, drect->y2 - drect->y1);
2865 checkGLcall("glCopyTexSubImage2D");
2867 if(drawBuffer == GL_BACK) {
2868 /* Write the back buffer backup back */
2869 if(backup) {
2870 if(texture_target != GL_TEXTURE_2D) {
2871 glDisable(texture_target);
2872 glEnable(GL_TEXTURE_2D);
2873 texture_target = GL_TEXTURE_2D;
2875 glBindTexture(GL_TEXTURE_2D, backup);
2876 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
2877 } else {
2878 if(texture_target != Src->glDescription.target) {
2879 glDisable(texture_target);
2880 glEnable(Src->glDescription.target);
2881 texture_target = Src->glDescription.target;
2883 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
2884 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2887 glBegin(GL_QUADS);
2888 /* top left */
2889 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2890 glVertex2i(0, 0);
2892 /* bottom left */
2893 glTexCoord2f(0.0, 0.0);
2894 glVertex2i(0, fbheight);
2896 /* bottom right */
2897 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2898 glVertex2i(fbwidth, Src->currentDesc.Height);
2900 /* top right */
2901 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2902 glVertex2i(fbwidth, 0);
2903 glEnd();
2904 } else {
2905 /* Restore the old draw buffer */
2906 glDrawBuffer(GL_BACK);
2908 glDisable(texture_target);
2909 checkGLcall("glDisable(texture_target)");
2911 /* Cleanup */
2912 if(src != Src->glDescription.textureName && src != backup) {
2913 glDeleteTextures(1, &src);
2914 checkGLcall("glDeleteTextures(1, &src)");
2916 if(backup) {
2917 glDeleteTextures(1, &backup);
2918 checkGLcall("glDeleteTextures(1, &backup)");
2921 LEAVE_GL();
2924 /* Not called from the VTable */
2925 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2926 WINED3DRECT rect;
2927 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2928 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2929 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2931 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2933 /* Get the swapchain. One of the surfaces has to be a primary surface */
2934 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2935 WARN("Destination is in sysmem, rejecting gl blt\n");
2936 return WINED3DERR_INVALIDCALL;
2938 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2939 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2940 if(Src) {
2941 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2942 WARN("Src is in sysmem, rejecting gl blt\n");
2943 return WINED3DERR_INVALIDCALL;
2945 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2946 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2949 /* Early sort out of cases where no render target is used */
2950 if(!dstSwapchain && !srcSwapchain &&
2951 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2952 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2953 return WINED3DERR_INVALIDCALL;
2956 /* No destination color keying supported */
2957 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2958 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2959 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2960 return WINED3DERR_INVALIDCALL;
2963 if (DestRect) {
2964 rect.x1 = DestRect->left;
2965 rect.y1 = DestRect->top;
2966 rect.x2 = DestRect->right;
2967 rect.y2 = DestRect->bottom;
2968 } else {
2969 rect.x1 = 0;
2970 rect.y1 = 0;
2971 rect.x2 = This->currentDesc.Width;
2972 rect.y2 = This->currentDesc.Height;
2975 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2976 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2977 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2978 /* Half-life does a Blt from the back buffer to the front buffer,
2979 * Full surface size, no flags... Use present instead
2981 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2984 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2985 while(1)
2987 RECT mySrcRect;
2988 TRACE("Looking if a Present can be done...\n");
2989 /* Source Rectangle must be full surface */
2990 if( SrcRect ) {
2991 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2992 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2993 TRACE("No, Source rectangle doesn't match\n");
2994 break;
2997 mySrcRect.left = 0;
2998 mySrcRect.top = 0;
2999 mySrcRect.right = Src->currentDesc.Width;
3000 mySrcRect.bottom = Src->currentDesc.Height;
3002 /* No stretching may occur */
3003 if(mySrcRect.right != rect.x2 - rect.x1 ||
3004 mySrcRect.bottom != rect.y2 - rect.y1) {
3005 TRACE("No, stretching is done\n");
3006 break;
3009 /* Destination must be full surface or match the clipping rectangle */
3010 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3012 RECT cliprect;
3013 POINT pos[2];
3014 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3015 pos[0].x = rect.x1;
3016 pos[0].y = rect.y1;
3017 pos[1].x = rect.x2;
3018 pos[1].y = rect.y2;
3019 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3020 pos, 2);
3022 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3023 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3025 TRACE("No, dest rectangle doesn't match(clipper)\n");
3026 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3027 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3028 break;
3031 else
3033 if(rect.x1 != 0 || rect.y1 != 0 ||
3034 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3035 TRACE("No, dest rectangle doesn't match(surface size)\n");
3036 break;
3040 TRACE("Yes\n");
3042 /* These flags are unimportant for the flag check, remove them */
3043 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3044 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3046 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3047 * take very long, while a flip is fast.
3048 * This applies to Half-Life, which does such Blts every time it finished
3049 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3050 * menu. This is also used by all apps when they do windowed rendering
3052 * The problem is that flipping is not really the same as copying. After a
3053 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3054 * untouched. Therefore it's necessary to override the swap effect
3055 * and to set it back after the flip.
3057 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3058 * testcases.
3061 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3062 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3064 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3065 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3067 dstSwapchain->presentParms.SwapEffect = orig_swap;
3069 return WINED3D_OK;
3071 break;
3074 TRACE("Unsupported blit between buffers on the same swapchain\n");
3075 return WINED3DERR_INVALIDCALL;
3076 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3077 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3078 return WINED3DERR_INVALIDCALL;
3079 } else if(dstSwapchain && srcSwapchain) {
3080 FIXME("Implement hardware blit between two different swapchains\n");
3081 return WINED3DERR_INVALIDCALL;
3082 } else if(dstSwapchain) {
3083 if(SrcSurface == myDevice->render_targets[0]) {
3084 TRACE("Blit from active render target to a swapchain\n");
3085 /* Handled with regular texture -> swapchain blit */
3087 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3088 FIXME("Implement blit from a swapchain to the active render target\n");
3089 return WINED3DERR_INVALIDCALL;
3092 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3093 /* Blit from render target to texture */
3094 WINED3DRECT srect;
3095 BOOL upsideDown, stretchx;
3097 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3098 TRACE("Color keying not supported by frame buffer to texture blit\n");
3099 return WINED3DERR_INVALIDCALL;
3100 /* Destination color key is checked above */
3103 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3104 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3106 if(SrcRect) {
3107 if(SrcRect->top < SrcRect->bottom) {
3108 srect.y1 = SrcRect->top;
3109 srect.y2 = SrcRect->bottom;
3110 upsideDown = FALSE;
3111 } else {
3112 srect.y1 = SrcRect->bottom;
3113 srect.y2 = SrcRect->top;
3114 upsideDown = TRUE;
3116 srect.x1 = SrcRect->left;
3117 srect.x2 = SrcRect->right;
3118 } else {
3119 srect.x1 = 0;
3120 srect.y1 = 0;
3121 srect.x2 = Src->currentDesc.Width;
3122 srect.y2 = Src->currentDesc.Height;
3123 upsideDown = FALSE;
3125 if(rect.x1 > rect.x2) {
3126 UINT tmp = rect.x2;
3127 rect.x2 = rect.x1;
3128 rect.x1 = tmp;
3129 upsideDown = !upsideDown;
3131 if(!srcSwapchain) {
3132 TRACE("Reading from an offscreen target\n");
3133 upsideDown = !upsideDown;
3136 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3137 stretchx = TRUE;
3138 } else {
3139 stretchx = FALSE;
3142 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3143 * flip the image nor scale it.
3145 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3146 * -> If the app wants a image width an unscaled width, copy it line per line
3147 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3148 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3149 * back buffer. This is slower than reading line per line, thus not used for flipping
3150 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3151 * pixel by pixel
3153 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3154 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3155 * backends.
3157 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3158 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3159 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3160 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3161 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3162 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3163 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3164 } else {
3165 TRACE("Using hardware stretching to flip / stretch the texture\n");
3166 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3169 if(!(This->Flags & SFLAG_DONOTFREE)) {
3170 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3171 This->resource.allocatedMemory = NULL;
3172 This->resource.heapMemory = NULL;
3173 } else {
3174 This->Flags &= ~SFLAG_INSYSMEM;
3176 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3177 * path is never entered
3179 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3181 return WINED3D_OK;
3182 } else if(Src) {
3183 /* Blit from offscreen surface to render target */
3184 float glTexCoord[4];
3185 DWORD oldCKeyFlags = Src->CKeyFlags;
3186 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
3187 RECT SourceRectangle;
3188 BOOL paletteOverride = FALSE;
3190 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3192 if(SrcRect) {
3193 SourceRectangle.left = SrcRect->left;
3194 SourceRectangle.right = SrcRect->right;
3195 SourceRectangle.top = SrcRect->top;
3196 SourceRectangle.bottom = SrcRect->bottom;
3197 } else {
3198 SourceRectangle.left = 0;
3199 SourceRectangle.right = Src->currentDesc.Width;
3200 SourceRectangle.top = 0;
3201 SourceRectangle.bottom = Src->currentDesc.Height;
3203 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3204 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3205 TRACE("Using stretch_rect_fbo\n");
3206 /* The source is always a texture, but never the currently active render target, and the texture
3207 * contents are never upside down
3209 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3210 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3211 return WINED3D_OK;
3214 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3215 /* Fall back to software */
3216 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3217 SourceRectangle.left, SourceRectangle.top,
3218 SourceRectangle.right, SourceRectangle.bottom);
3219 return WINED3DERR_INVALIDCALL;
3222 /* Color keying: Check if we have to do a color keyed blt,
3223 * and if not check if a color key is activated.
3225 * Just modify the color keying parameters in the surface and restore them afterwards
3226 * The surface keeps track of the color key last used to load the opengl surface.
3227 * PreLoad will catch the change to the flags and color key and reload if necessary.
3229 if(Flags & WINEDDBLT_KEYSRC) {
3230 /* Use color key from surface */
3231 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3232 /* Use color key from DDBltFx */
3233 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3234 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3235 } else {
3236 /* Do not use color key */
3237 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3240 /* When blitting from an offscreen surface to a rendertarget, the source
3241 * surface is not required to have a palette. Our rendering / conversion
3242 * code further down the road retrieves the palette from the surface, so
3243 * it must have a palette set. */
3244 if((Src->resource.format == WINED3DFMT_P8) && (Src->palette == NULL)) {
3245 paletteOverride = TRUE;
3246 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3247 Src->palette = This->palette;
3250 /* Now load the surface */
3251 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3254 /* Activate the destination context, set it up for blitting */
3255 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3256 ENTER_GL();
3258 glEnable(Src->glDescription.target);
3259 checkGLcall("glEnable(Src->glDescription.target)");
3261 if(!dstSwapchain) {
3262 TRACE("Drawing to offscreen buffer\n");
3263 glDrawBuffer(myDevice->offscreenBuffer);
3264 checkGLcall("glDrawBuffer");
3265 } else {
3266 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3267 TRACE("Drawing to %#x buffer\n", buffer);
3268 glDrawBuffer(buffer);
3269 checkGLcall("glDrawBuffer");
3272 /* Bind the texture */
3273 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3274 checkGLcall("glBindTexture");
3276 /* Filtering for StretchRect */
3277 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3278 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3279 checkGLcall("glTexParameteri");
3280 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3281 minMipLookup[Filter][WINED3DTEXF_NONE]);
3282 checkGLcall("glTexParameteri");
3283 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3284 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3285 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3286 checkGLcall("glTexEnvi");
3288 /* This is for color keying */
3289 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3290 glEnable(GL_ALPHA_TEST);
3291 checkGLcall("glEnable GL_ALPHA_TEST");
3293 /* When the primary render target uses P8, the alpha component contains the palette index.
3294 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3295 * should be masked away have alpha set to 0. */
3296 if(primary_render_target_is_p8(myDevice))
3297 glAlphaFunc(GL_NOTEQUAL, (float)This->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3298 else
3299 glAlphaFunc(GL_NOTEQUAL, 0.0);
3300 checkGLcall("glAlphaFunc\n");
3301 } else {
3302 glDisable(GL_ALPHA_TEST);
3303 checkGLcall("glDisable GL_ALPHA_TEST");
3306 /* Draw a textured quad
3308 glBegin(GL_QUADS);
3310 glColor3d(1.0f, 1.0f, 1.0f);
3311 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3312 glVertex3f(rect.x1,
3313 rect.y1,
3314 0.0);
3316 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3317 glVertex3f(rect.x1, rect.y2, 0.0);
3319 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3320 glVertex3f(rect.x2,
3321 rect.y2,
3322 0.0);
3324 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3325 glVertex3f(rect.x2,
3326 rect.y1,
3327 0.0);
3328 glEnd();
3329 checkGLcall("glEnd");
3331 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3332 glDisable(GL_ALPHA_TEST);
3333 checkGLcall("glDisable(GL_ALPHA_TEST)");
3336 /* Flush in case the drawable is used by multiple GL contexts */
3337 if(dstSwapchain && (dstSwapchain->num_contexts >= 2))
3338 glFlush();
3340 glBindTexture(Src->glDescription.target, 0);
3341 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3342 /* Leave the opengl state valid for blitting */
3343 glDisable(Src->glDescription.target);
3344 checkGLcall("glDisable(Src->glDescription.target)");
3346 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3347 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3349 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3350 glDrawBuffer(GL_BACK);
3351 checkGLcall("glDrawBuffer");
3353 /* Restore the color key parameters */
3354 Src->CKeyFlags = oldCKeyFlags;
3355 This->SrcBltCKey = oldBltCKey;
3357 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3358 if(paletteOverride)
3359 Src->palette = NULL;
3361 LEAVE_GL();
3363 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3364 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3365 * is outdated now
3367 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3368 /* TODO: This should be moved to ModifyLocation() */
3369 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3370 This->Flags |= SFLAG_INTEXTURE;
3373 return WINED3D_OK;
3374 } else {
3375 /* Source-Less Blit to render target */
3376 if (Flags & WINEDDBLT_COLORFILL) {
3377 /* This is easy to handle for the D3D Device... */
3378 DWORD color;
3380 TRACE("Colorfill\n");
3382 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3383 must be true if we are here */
3384 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3385 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3386 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3387 TRACE("Surface is higher back buffer, falling back to software\n");
3388 return WINED3DERR_INVALIDCALL;
3391 /* The color as given in the Blt function is in the format of the frame-buffer...
3392 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3394 if (This->resource.format == WINED3DFMT_P8) {
3395 if (This->palette) {
3396 color = ((0xFF000000) |
3397 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3398 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3399 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3400 } else {
3401 color = 0xFF000000;
3404 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3405 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3406 color = 0xFFFFFFFF;
3407 } else {
3408 color = ((0xFF000000) |
3409 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3410 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3411 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3414 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3415 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3416 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3418 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3419 color = DDBltFx->u5.dwFillColor;
3421 else {
3422 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3423 return WINED3DERR_INVALIDCALL;
3426 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3427 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3428 1, /* Number of rectangles */
3429 &rect, WINED3DCLEAR_TARGET, color,
3430 0.0 /* Z */,
3431 0 /* Stencil */);
3432 return WINED3D_OK;
3436 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3437 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3438 return WINED3DERR_INVALIDCALL;
3441 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3443 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3444 float depth;
3446 if (Flags & WINEDDBLT_DEPTHFILL) {
3447 switch(This->resource.format) {
3448 case WINED3DFMT_D16:
3449 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3450 break;
3451 case WINED3DFMT_D15S1:
3452 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3453 break;
3454 case WINED3DFMT_D24S8:
3455 case WINED3DFMT_D24X8:
3456 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3457 break;
3458 case WINED3DFMT_D32:
3459 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3460 break;
3461 default:
3462 depth = 0.0;
3463 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3466 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3467 DestRect == NULL ? 0 : 1,
3468 (WINED3DRECT *) DestRect,
3469 WINED3DCLEAR_ZBUFFER,
3470 0x00000000,
3471 depth,
3472 0x00000000);
3475 FIXME("(%p): Unsupp depthstencil blit\n", This);
3476 return WINED3DERR_INVALIDCALL;
3479 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3480 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3481 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3482 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3483 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3484 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3486 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3488 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3489 return WINEDDERR_SURFACEBUSY;
3492 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3493 * except depth blits, which seem to work
3495 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3496 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3497 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3498 return WINED3DERR_INVALIDCALL;
3499 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3500 TRACE("Z Blit override handled the blit\n");
3501 return WINED3D_OK;
3505 /* Special cases for RenderTargets */
3506 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3507 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3508 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3511 /* For the rest call the X11 surface implementation.
3512 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3513 * other Blts are rather rare
3515 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3518 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3519 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3520 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3521 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3522 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3524 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3526 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3527 return WINEDDERR_SURFACEBUSY;
3530 if(myDevice->inScene &&
3531 (iface == myDevice->stencilBufferTarget ||
3532 (Source && Source == myDevice->stencilBufferTarget))) {
3533 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3534 return WINED3DERR_INVALIDCALL;
3537 /* Special cases for RenderTargets */
3538 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3539 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3541 RECT SrcRect, DstRect;
3542 DWORD Flags=0;
3544 if(rsrc) {
3545 SrcRect.left = rsrc->left;
3546 SrcRect.top= rsrc->top;
3547 SrcRect.bottom = rsrc->bottom;
3548 SrcRect.right = rsrc->right;
3549 } else {
3550 SrcRect.left = 0;
3551 SrcRect.top = 0;
3552 SrcRect.right = srcImpl->currentDesc.Width;
3553 SrcRect.bottom = srcImpl->currentDesc.Height;
3556 DstRect.left = dstx;
3557 DstRect.top=dsty;
3558 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3559 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3561 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3562 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3563 Flags |= WINEDDBLT_KEYSRC;
3564 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3565 Flags |= WINEDDBLT_KEYDEST;
3566 if(trans & WINEDDBLTFAST_WAIT)
3567 Flags |= WINEDDBLT_WAIT;
3568 if(trans & WINEDDBLTFAST_DONOTWAIT)
3569 Flags |= WINEDDBLT_DONOTWAIT;
3571 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3575 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3578 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3579 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3580 RGBQUAD col[256];
3581 IWineD3DPaletteImpl *pal = This->palette;
3582 unsigned int n;
3583 TRACE("(%p)\n", This);
3585 if (!pal) return WINED3D_OK;
3587 if(This->resource.format == WINED3DFMT_P8 ||
3588 This->resource.format == WINED3DFMT_A8P8)
3590 if(!(This->Flags & SFLAG_INSYSMEM)) {
3591 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3592 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3594 TRACE("Dirtifying surface\n");
3595 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3598 if(This->Flags & SFLAG_DIBSECTION) {
3599 TRACE("(%p): Updating the hdc's palette\n", This);
3600 for (n=0; n<256; n++) {
3601 col[n].rgbRed = pal->palents[n].peRed;
3602 col[n].rgbGreen = pal->palents[n].peGreen;
3603 col[n].rgbBlue = pal->palents[n].peBlue;
3604 col[n].rgbReserved = 0;
3606 SetDIBColorTable(This->hDC, 0, 256, col);
3609 /* Propagate the changes to the drawable when we have a palette.
3610 * TODO: in case of hardware p8 palettes we should only upload the palette. */
3611 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3612 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3614 return WINED3D_OK;
3617 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3618 /** Check against the maximum texture sizes supported by the video card **/
3619 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3620 unsigned int pow2Width, pow2Height;
3621 const GlPixelFormatDesc *glDesc;
3623 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3624 /* Setup some glformat defaults */
3625 This->glDescription.glFormat = glDesc->glFormat;
3626 This->glDescription.glFormatInternal = glDesc->glInternal;
3627 This->glDescription.glType = glDesc->glType;
3629 This->glDescription.textureName = 0;
3630 This->glDescription.target = GL_TEXTURE_2D;
3632 /* Non-power2 support */
3633 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3634 pow2Width = This->currentDesc.Width;
3635 pow2Height = This->currentDesc.Height;
3636 } else {
3637 /* Find the nearest pow2 match */
3638 pow2Width = pow2Height = 1;
3639 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3640 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3642 This->pow2Width = pow2Width;
3643 This->pow2Height = pow2Height;
3645 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3646 WINED3DFORMAT Format = This->resource.format;
3647 /** TODO: add support for non power two compressed textures **/
3648 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3649 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3650 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3651 This, This->currentDesc.Width, This->currentDesc.Height);
3652 return WINED3DERR_NOTAVAILABLE;
3656 if(pow2Width != This->currentDesc.Width ||
3657 pow2Height != This->currentDesc.Height) {
3658 This->Flags |= SFLAG_NONPOW2;
3661 TRACE("%p\n", This);
3662 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3663 /* one of three options
3664 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)
3665 2: Set the texture to the maximum size (bad idea)
3666 3: WARN and return WINED3DERR_NOTAVAILABLE;
3667 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.
3669 WARN("(%p) Creating an oversized surface\n", This);
3670 This->Flags |= SFLAG_OVERSIZE;
3672 /* This will be initialized on the first blt */
3673 This->glRect.left = 0;
3674 This->glRect.top = 0;
3675 This->glRect.right = 0;
3676 This->glRect.bottom = 0;
3677 } else {
3678 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3679 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3680 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3681 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3683 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE) &&
3684 !((This->resource.format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE) && (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3686 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3687 This->pow2Width = This->currentDesc.Width;
3688 This->pow2Height = This->currentDesc.Height;
3689 This->Flags &= ~SFLAG_NONPOW2;
3692 /* No oversize, gl rect is the full texture size */
3693 This->Flags &= ~SFLAG_OVERSIZE;
3694 This->glRect.left = 0;
3695 This->glRect.top = 0;
3696 This->glRect.right = This->pow2Width;
3697 This->glRect.bottom = This->pow2Height;
3700 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3701 switch(wined3d_settings.offscreen_rendering_mode) {
3702 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3703 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3704 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3708 This->Flags |= SFLAG_INSYSMEM;
3710 return WINED3D_OK;
3713 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3714 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3715 IWineD3DBaseTexture *texture;
3717 TRACE("(%p)->(%s, %s)\n", iface,
3718 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3719 persistent ? "TRUE" : "FALSE");
3721 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3722 IWineD3DSwapChain *swapchain = NULL;
3724 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3725 TRACE("Surface %p is an onscreen surface\n", iface);
3727 IWineD3DSwapChain_Release(swapchain);
3728 } else {
3729 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3730 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3734 if(persistent) {
3735 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
3736 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3737 TRACE("Passing to container\n");
3738 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3739 IWineD3DBaseTexture_Release(texture);
3742 This->Flags &= ~SFLAG_LOCATIONS;
3743 This->Flags |= flag;
3744 } else {
3745 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
3746 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3747 TRACE("Passing to container\n");
3748 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3749 IWineD3DBaseTexture_Release(texture);
3752 This->Flags &= ~flag;
3756 struct coords {
3757 GLfloat x, y, z;
3760 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
3761 struct coords coords[4];
3762 RECT rect;
3763 IWineD3DSwapChain *swapchain = NULL;
3764 IWineD3DBaseTexture *texture = NULL;
3765 HRESULT hr;
3766 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3768 if(rect_in) {
3769 rect = *rect_in;
3770 } else {
3771 rect.left = 0;
3772 rect.top = 0;
3773 rect.right = This->currentDesc.Width;
3774 rect.bottom = This->currentDesc.Height;
3777 ActivateContext(device, device->render_targets[0], CTXUSAGE_BLIT);
3778 ENTER_GL();
3780 if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
3781 glEnable(GL_TEXTURE_RECTANGLE_ARB);
3782 checkGLcall("glEnable(GL_TEXTURE_RECTANGLE_ARB)");
3783 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName);
3784 checkGLcall("GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName)");
3785 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3786 checkGLcall("glTexParameteri");
3787 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3788 checkGLcall("glTexParameteri");
3790 coords[0].x = rect.left;
3791 coords[0].z = 0;
3793 coords[1].x = rect.left;
3794 coords[1].z = 0;
3796 coords[2].x = rect.right;
3797 coords[2].z = 0;
3799 coords[3].x = rect.right;
3800 coords[3].z = 0;
3802 coords[0].y = rect.top;
3803 coords[1].y = rect.bottom;
3804 coords[2].y = rect.bottom;
3805 coords[3].y = rect.top;
3806 } else if(This->glDescription.target == GL_TEXTURE_2D) {
3807 glEnable(GL_TEXTURE_2D);
3808 checkGLcall("glEnable(GL_TEXTURE_2D)");
3809 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
3810 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
3811 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3812 checkGLcall("glTexParameteri");
3813 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3814 checkGLcall("glTexParameteri");
3816 coords[0].x = (float)rect.left / This->pow2Width;
3817 coords[0].z = 0;
3819 coords[1].x = (float)rect.left / This->pow2Width;
3820 coords[1].z = 0;
3822 coords[2].x = (float)rect.right / This->pow2Width;
3823 coords[2].z = 0;
3825 coords[3].x = (float)rect.right / This->pow2Width;
3826 coords[3].z = 0;
3828 coords[0].y = (float)rect.top / This->pow2Height;
3829 coords[1].y = (float)rect.bottom / This->pow2Height;
3830 coords[2].y = (float)rect.bottom / This->pow2Height;
3831 coords[3].y = (float)rect.top / This->pow2Height;
3832 } else {
3833 /* Must be a cube map */
3834 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
3835 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
3836 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
3837 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
3838 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3839 checkGLcall("glTexParameteri");
3840 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3841 checkGLcall("glTexParameteri");
3843 switch(This->glDescription.target) {
3844 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3845 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
3846 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3847 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3848 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
3849 break;
3851 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3852 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3853 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
3854 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
3855 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3856 break;
3858 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3859 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
3860 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3861 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3862 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
3863 break;
3865 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3866 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3867 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3868 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3869 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3870 break;
3872 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3873 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3874 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3875 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
3876 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
3877 break;
3879 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
3880 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
3881 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
3882 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3883 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3884 break;
3886 default:
3887 ERR("Unexpected texture target\n");
3888 LEAVE_GL();
3889 return;
3893 glBegin(GL_QUADS);
3894 glTexCoord3fv(&coords[0].x);
3895 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
3897 glTexCoord3fv(&coords[1].x);
3898 glVertex2i(rect.left, device->render_offscreen ? rect.top : rect.bottom);
3900 glTexCoord3fv(&coords[2].x);
3901 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
3903 glTexCoord3fv(&coords[3].x);
3904 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
3905 glEnd();
3906 checkGLcall("glEnd");
3908 if(This->glDescription.target != GL_TEXTURE_2D) {
3909 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3910 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3911 } else {
3912 glDisable(GL_TEXTURE_2D);
3913 checkGLcall("glDisable(GL_TEXTURE_2D)");
3916 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain);
3917 if(hr == WINED3D_OK && swapchain) {
3918 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
3919 if(((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
3920 glFlush();
3922 IWineD3DSwapChain_Release(swapchain);
3923 } else {
3924 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
3925 * reset properly next draw
3927 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture);
3928 if(hr == WINED3D_OK && texture) {
3929 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
3930 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
3931 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
3932 IWineD3DBaseTexture_Release(texture);
3935 LEAVE_GL();
3938 /*****************************************************************************
3939 * IWineD3DSurface::LoadLocation
3941 * Copies the current surface data from wherever it is to the requested
3942 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
3943 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
3944 * multiple locations, the gl texture is preferred over the drawable, which is
3945 * preferred over system memory. The PBO counts as system memory. If rect is
3946 * not NULL, only the specified rectangle is copied (only supported for
3947 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
3948 * location is marked up to date after the copy.
3950 * Parameters:
3951 * flag: Surface location flag to be updated
3952 * rect: rectangle to be copied
3954 * Returns:
3955 * WINED3D_OK on success
3956 * WINED3DERR_DEVICELOST on an internal error
3958 *****************************************************************************/
3959 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
3960 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3961 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3962 IWineD3DSwapChain *swapchain = NULL;
3963 GLenum format, internal, type;
3964 CONVERT_TYPES convert;
3965 int bpp;
3966 int width, pitch, outpitch;
3967 BYTE *mem;
3969 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3970 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3971 TRACE("Surface %p is an onscreen surface\n", iface);
3973 IWineD3DSwapChain_Release(swapchain);
3974 } else {
3975 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
3976 * Prefer SFLAG_INTEXTURE. */
3977 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
3981 TRACE("(%p)->(%s, %p)\n", iface,
3982 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3983 rect);
3984 if(rect) {
3985 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
3988 if(This->Flags & flag) {
3989 TRACE("Location already up to date\n");
3990 return WINED3D_OK;
3993 if(!(This->Flags & SFLAG_LOCATIONS)) {
3994 ERR("Surface does not have any up to date location\n");
3995 This->Flags |= SFLAG_LOST;
3996 return WINED3DERR_DEVICELOST;
3999 if(flag == SFLAG_INSYSMEM) {
4000 surface_prepare_system_memory(This);
4002 /* Download the surface to system memory */
4003 if(This->Flags & SFLAG_INTEXTURE) {
4004 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4005 surface_bind_and_dirtify(This);
4007 surface_download_data(This);
4008 } else {
4009 read_from_framebuffer(This, rect,
4010 This->resource.allocatedMemory,
4011 IWineD3DSurface_GetPitch(iface));
4013 } else if(flag == SFLAG_INDRAWABLE) {
4014 if(This->Flags & SFLAG_INTEXTURE) {
4015 surface_blt_to_drawable(This, rect);
4016 } else {
4017 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4019 /* The width is in 'length' not in bytes */
4020 width = This->currentDesc.Width;
4021 pitch = IWineD3DSurface_GetPitch(iface);
4023 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4024 int height = This->currentDesc.Height;
4026 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4027 outpitch = width * bpp;
4028 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4030 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4031 if(!mem) {
4032 ERR("Out of memory %d, %d!\n", outpitch, height);
4033 return WINED3DERR_OUTOFVIDEOMEMORY;
4035 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4037 This->Flags |= SFLAG_CONVERTED;
4038 } else {
4039 This->Flags &= ~SFLAG_CONVERTED;
4040 mem = This->resource.allocatedMemory;
4043 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4045 /* Don't delete PBO memory */
4046 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4047 HeapFree(GetProcessHeap(), 0, mem);
4049 } else /* if(flag == SFLAG_INTEXTURE) */ {
4050 if (This->Flags & SFLAG_INDRAWABLE) {
4051 read_from_framebuffer_texture(This);
4052 } else { /* Upload from system memory */
4053 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4055 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4056 surface_bind_and_dirtify(This);
4057 ENTER_GL();
4059 /* The only place where LoadTexture() might get called when isInDraw=1
4060 * is ActivateContext where lastActiveRenderTarget is preloaded.
4062 if(iface == device->lastActiveRenderTarget && device->isInDraw)
4063 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
4065 /* Otherwise: System memory copy must be most up to date */
4067 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4068 This->Flags |= SFLAG_GLCKEY;
4069 This->glCKey = This->SrcBltCKey;
4071 else This->Flags &= ~SFLAG_GLCKEY;
4073 /* The width is in 'length' not in bytes */
4074 width = This->currentDesc.Width;
4075 pitch = IWineD3DSurface_GetPitch(iface);
4077 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4078 int height = This->currentDesc.Height;
4080 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4081 outpitch = width * bpp;
4082 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4084 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4085 if(!mem) {
4086 ERR("Out of memory %d, %d!\n", outpitch, height);
4087 return WINED3DERR_OUTOFVIDEOMEMORY;
4089 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4091 This->Flags |= SFLAG_CONVERTED;
4092 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
4093 d3dfmt_p8_upload_palette(iface, convert);
4094 This->Flags &= ~SFLAG_CONVERTED;
4095 mem = This->resource.allocatedMemory;
4096 } else {
4097 This->Flags &= ~SFLAG_CONVERTED;
4098 mem = This->resource.allocatedMemory;
4101 /* Make sure the correct pitch is used */
4102 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4104 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4105 TRACE("non power of two support\n");
4106 if(!(This->Flags & SFLAG_ALLOCATED)) {
4107 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4109 if (mem || (This->Flags & SFLAG_PBO)) {
4110 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4112 } else {
4113 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4114 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4116 if(!(This->Flags & SFLAG_ALLOCATED)) {
4117 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4119 if (mem || (This->Flags & SFLAG_PBO)) {
4120 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4124 /* Restore the default pitch */
4125 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4126 LEAVE_GL();
4128 /* Don't delete PBO memory */
4129 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4130 HeapFree(GetProcessHeap(), 0, mem);
4134 if(rect == NULL) {
4135 This->Flags |= flag;
4138 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
4139 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4140 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4141 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4144 return WINED3D_OK;
4147 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
4148 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4149 IWineD3DSwapChain *swapchain = NULL;
4151 /* Update the drawable size method */
4152 if(container) {
4153 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4155 if(swapchain) {
4156 This->get_drawable_size = get_drawable_size_swapchain;
4157 IWineD3DSwapChain_Release(swapchain);
4158 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4159 switch(wined3d_settings.offscreen_rendering_mode) {
4160 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4161 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4162 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4166 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4169 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4170 return SURFACE_OPENGL;
4173 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4175 /* IUnknown */
4176 IWineD3DBaseSurfaceImpl_QueryInterface,
4177 IWineD3DBaseSurfaceImpl_AddRef,
4178 IWineD3DSurfaceImpl_Release,
4179 /* IWineD3DResource */
4180 IWineD3DBaseSurfaceImpl_GetParent,
4181 IWineD3DBaseSurfaceImpl_GetDevice,
4182 IWineD3DBaseSurfaceImpl_SetPrivateData,
4183 IWineD3DBaseSurfaceImpl_GetPrivateData,
4184 IWineD3DBaseSurfaceImpl_FreePrivateData,
4185 IWineD3DBaseSurfaceImpl_SetPriority,
4186 IWineD3DBaseSurfaceImpl_GetPriority,
4187 IWineD3DSurfaceImpl_PreLoad,
4188 IWineD3DSurfaceImpl_UnLoad,
4189 IWineD3DBaseSurfaceImpl_GetType,
4190 /* IWineD3DSurface */
4191 IWineD3DBaseSurfaceImpl_GetContainer,
4192 IWineD3DBaseSurfaceImpl_GetDesc,
4193 IWineD3DSurfaceImpl_LockRect,
4194 IWineD3DSurfaceImpl_UnlockRect,
4195 IWineD3DSurfaceImpl_GetDC,
4196 IWineD3DSurfaceImpl_ReleaseDC,
4197 IWineD3DSurfaceImpl_Flip,
4198 IWineD3DSurfaceImpl_Blt,
4199 IWineD3DBaseSurfaceImpl_GetBltStatus,
4200 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4201 IWineD3DBaseSurfaceImpl_IsLost,
4202 IWineD3DBaseSurfaceImpl_Restore,
4203 IWineD3DSurfaceImpl_BltFast,
4204 IWineD3DBaseSurfaceImpl_GetPalette,
4205 IWineD3DBaseSurfaceImpl_SetPalette,
4206 IWineD3DSurfaceImpl_RealizePalette,
4207 IWineD3DBaseSurfaceImpl_SetColorKey,
4208 IWineD3DBaseSurfaceImpl_GetPitch,
4209 IWineD3DSurfaceImpl_SetMem,
4210 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4211 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4212 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4213 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4214 IWineD3DBaseSurfaceImpl_SetClipper,
4215 IWineD3DBaseSurfaceImpl_GetClipper,
4216 /* Internal use: */
4217 IWineD3DSurfaceImpl_AddDirtyRect,
4218 IWineD3DSurfaceImpl_LoadTexture,
4219 IWineD3DSurfaceImpl_BindTexture,
4220 IWineD3DSurfaceImpl_SaveSnapshot,
4221 IWineD3DSurfaceImpl_SetContainer,
4222 IWineD3DSurfaceImpl_SetGlTextureDesc,
4223 IWineD3DSurfaceImpl_GetGlDesc,
4224 IWineD3DSurfaceImpl_GetData,
4225 IWineD3DSurfaceImpl_SetFormat,
4226 IWineD3DSurfaceImpl_PrivateSetup,
4227 IWineD3DSurfaceImpl_ModifyLocation,
4228 IWineD3DSurfaceImpl_LoadLocation,
4229 IWineD3DSurfaceImpl_GetImplType