push 1251f02bf3949998f05156a60f8708b2ec391462
[wine/hacks.git] / dlls / wined3d / surface.c
blobac4224ed469cfc4e967c2dae0d0e1a7c0db8a7a7
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 /* Need a context to destroy the texture. Use the currently active render target, but only if
427 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
428 * When destroying the primary rt, Uninit3D will activate a context before doing anything
430 if(device->render_targets && device->render_targets[0]) {
431 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
434 ENTER_GL();
435 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
436 TRACE("Deleting texture %d\n", This->glDescription.textureName);
437 glDeleteTextures(1, &This->glDescription.textureName);
440 if(This->Flags & SFLAG_PBO) {
441 /* Delete the PBO */
442 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
445 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
446 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
447 HeapFree(GetProcessHeap(), 0, entry);
449 LEAVE_GL();
451 if(This->Flags & SFLAG_DIBSECTION) {
452 /* Release the DC */
453 SelectObject(This->hDC, This->dib.holdbitmap);
454 DeleteDC(This->hDC);
455 /* Release the DIB section */
456 DeleteObject(This->dib.DIBsection);
457 This->dib.bitmap_data = NULL;
458 This->resource.allocatedMemory = NULL;
460 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
462 HeapFree(GetProcessHeap(), 0, This->palette9);
464 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
465 if(iface == device->ddraw_primary)
466 device->ddraw_primary = NULL;
468 TRACE("(%p) Released\n", This);
469 HeapFree(GetProcessHeap(), 0, This);
472 return ref;
475 /* ****************************************************
476 IWineD3DSurface IWineD3DResource parts follow
477 **************************************************** */
479 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
480 /* TODO: check for locks */
481 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
482 IWineD3DBaseTexture *baseTexture = NULL;
483 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
485 TRACE("(%p)Checking to see if the container is a base texture\n", This);
486 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
487 TRACE("Passing to container\n");
488 IWineD3DBaseTexture_PreLoad(baseTexture);
489 IWineD3DBaseTexture_Release(baseTexture);
490 } else {
491 TRACE("(%p) : About to load surface\n", This);
493 if(!device->isInDraw) {
494 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
497 if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8) {
498 if(palette9_changed(This)) {
499 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
500 /* TODO: This is not necessarily needed with hw palettized texture support */
501 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
502 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
503 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
506 ENTER_GL();
507 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
508 if (!This->glDescription.level) {
509 if (!This->glDescription.textureName) {
510 glGenTextures(1, &This->glDescription.textureName);
511 checkGLcall("glGenTextures");
512 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
514 glBindTexture(This->glDescription.target, This->glDescription.textureName);
515 checkGLcall("glBindTexture");
516 LEAVE_GL();
517 IWineD3DSurface_LoadTexture(iface, FALSE);
518 /* This is where we should be reducing the amount of GLMemoryUsed */
519 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
520 /* assume this is a coding error not a real error for now */
521 FIXME("Mipmap surface has a glTexture bound to it!\n");
522 LEAVE_GL();
524 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
525 /* Tell opengl to try and keep this texture in video ram (well mostly) */
526 GLclampf tmp;
527 tmp = 0.9f;
528 ENTER_GL();
529 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
530 LEAVE_GL();
533 return;
536 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
537 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
538 This->resource.allocatedMemory =
539 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
541 ENTER_GL();
542 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
543 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
544 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
545 checkGLcall("glGetBufferSubData");
546 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
547 checkGLcall("glDeleteBuffers");
548 LEAVE_GL();
550 This->pbo = 0;
551 This->Flags &= ~SFLAG_PBO;
554 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
555 IWineD3DBaseTexture *texture = NULL;
556 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
557 renderbuffer_entry_t *entry, *entry2;
558 TRACE("(%p)\n", iface);
560 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
561 /* Default pool resources are supposed to be destroyed before Reset is called.
562 * Implicit resources stay however. So this means we have an implicit render target
563 * or depth stencil. The content may be destroyed, but we still have to tear down
564 * opengl resources, so we cannot leave early.
566 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
567 } else {
568 /* Load the surface into system memory */
569 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
571 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
572 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
573 This->Flags &= ~SFLAG_ALLOCATED;
575 /* Destroy PBOs, but load them into real sysmem before */
576 if(This->Flags & SFLAG_PBO) {
577 surface_remove_pbo(This);
580 /* Destroy fbo render buffers. This is needed for implicit render targets, for
581 * all application-created targets the application has to release the surface
582 * before calling _Reset
584 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
585 ENTER_GL();
586 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
587 LEAVE_GL();
588 list_remove(&entry->entry);
589 HeapFree(GetProcessHeap(), 0, entry);
591 list_init(&This->renderbuffers);
592 This->current_renderbuffer = NULL;
594 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
595 * destroy it
597 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
598 if(!texture) {
599 ENTER_GL();
600 glDeleteTextures(1, &This->glDescription.textureName);
601 This->glDescription.textureName = 0;
602 LEAVE_GL();
603 } else {
604 IWineD3DBaseTexture_Release(texture);
606 return;
609 /* ******************************************************
610 IWineD3DSurface IWineD3DSurface parts follow
611 ****************************************************** */
613 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
614 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
615 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
616 if (This->glDescription.textureName == 0 && textureName != 0) {
617 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
618 IWineD3DSurface_AddDirtyRect(iface, NULL);
620 This->glDescription.textureName = textureName;
621 This->glDescription.target = target;
622 This->Flags &= ~SFLAG_ALLOCATED;
625 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
626 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
627 TRACE("(%p) : returning %p\n", This, &This->glDescription);
628 *glDescription = &This->glDescription;
631 /* TODO: think about moving this down to resource? */
632 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
633 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
634 /* 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 */
635 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
636 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
638 return (CONST void*)(This->resource.allocatedMemory);
641 /* Read the framebuffer back into the surface */
642 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
643 IWineD3DSwapChainImpl *swapchain;
644 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
645 BYTE *mem;
646 GLint fmt;
647 GLint type;
648 BYTE *row, *top, *bottom;
649 int i;
650 BOOL bpp;
651 RECT local_rect;
652 BOOL srcIsUpsideDown;
654 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
655 static BOOL warned = FALSE;
656 if(!warned) {
657 ERR("The application tries to lock the render target, but render target locking is disabled\n");
658 warned = TRUE;
660 return;
663 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
664 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
665 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
666 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
667 * context->last_was_blit set on the unlock.
669 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
670 ENTER_GL();
672 /* Select the correct read buffer, and give some debug output.
673 * There is no need to keep track of the current read buffer or reset it, every part of the code
674 * that reads sets the read buffer as desired.
676 if(!swapchain) {
677 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
678 * Read from the back buffer
680 TRACE("Locking offscreen render target\n");
681 glReadBuffer(myDevice->offscreenBuffer);
682 srcIsUpsideDown = TRUE;
683 } else {
684 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
685 TRACE("Locking %#x buffer\n", buffer);
686 glReadBuffer(buffer);
687 checkGLcall("glReadBuffer");
689 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
690 srcIsUpsideDown = FALSE;
693 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
694 if(!rect) {
695 local_rect.left = 0;
696 local_rect.top = 0;
697 local_rect.right = This->currentDesc.Width;
698 local_rect.bottom = This->currentDesc.Height;
699 } else {
700 local_rect = *rect;
702 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
704 switch(This->resource.format)
706 case WINED3DFMT_P8:
708 if(primary_render_target_is_p8(myDevice)) {
709 /* In case of P8 render targets the index is stored in the alpha component */
710 fmt = GL_ALPHA;
711 type = GL_UNSIGNED_BYTE;
712 mem = dest;
713 bpp = This->bytesPerPixel;
714 } else {
715 /* GL can't return palettized data, so read ARGB pixels into a
716 * separate block of memory and convert them into palettized format
717 * in software. Slow, but if the app means to use palettized render
718 * targets and locks it...
720 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
721 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
722 * for the color channels when palettizing the colors.
724 fmt = GL_RGB;
725 type = GL_UNSIGNED_BYTE;
726 pitch *= 3;
727 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
728 if(!mem) {
729 ERR("Out of memory\n");
730 LEAVE_GL();
731 return;
733 bpp = This->bytesPerPixel * 3;
736 break;
738 default:
739 mem = dest;
740 fmt = This->glDescription.glFormat;
741 type = This->glDescription.glType;
742 bpp = This->bytesPerPixel;
745 if(This->Flags & SFLAG_PBO) {
746 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
747 checkGLcall("glBindBufferARB");
750 glReadPixels(local_rect.left, local_rect.top,
751 local_rect.right - local_rect.left,
752 local_rect.bottom - local_rect.top,
753 fmt, type, mem);
754 vcheckGLcall("glReadPixels");
756 if(This->Flags & SFLAG_PBO) {
757 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
758 checkGLcall("glBindBufferARB");
760 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
761 * to get a pointer to it and perform the flipping in software. This is a lot
762 * faster than calling glReadPixels for each line. In case we want more speed
763 * we should rerender it flipped in a FBO and read the data back from the FBO. */
764 if(!srcIsUpsideDown) {
765 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
766 checkGLcall("glBindBufferARB");
768 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
769 checkGLcall("glMapBufferARB");
773 /* TODO: Merge this with the palettization loop below for P8 targets */
774 if(!srcIsUpsideDown) {
775 UINT len, off;
776 /* glReadPixels returns the image upside down, and there is no way to prevent this.
777 Flip the lines in software */
778 len = (local_rect.right - local_rect.left) * bpp;
779 off = local_rect.left * bpp;
781 row = HeapAlloc(GetProcessHeap(), 0, len);
782 if(!row) {
783 ERR("Out of memory\n");
784 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
785 LEAVE_GL();
786 return;
789 top = mem + pitch * local_rect.top;
790 bottom = mem + pitch * ( local_rect.bottom - local_rect.top - 1);
791 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
792 memcpy(row, top + off, len);
793 memcpy(top + off, bottom + off, len);
794 memcpy(bottom + off, row, len);
795 top += pitch;
796 bottom -= pitch;
798 HeapFree(GetProcessHeap(), 0, row);
800 /* Unmap the temp PBO buffer */
801 if(This->Flags & SFLAG_PBO) {
802 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
803 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
807 LEAVE_GL();
809 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
810 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
811 * the same color but we have no choice.
812 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
814 if((This->resource.format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice)) {
815 PALETTEENTRY *pal = NULL;
816 DWORD width = pitch / 3;
817 int x, y, c;
819 if(This->palette) {
820 pal = This->palette->palents;
821 } else {
822 ERR("Palette is missing, cannot perform inverse palette lookup\n");
823 HeapFree(GetProcessHeap(), 0, mem);
824 return ;
827 for(y = local_rect.top; y < local_rect.bottom; y++) {
828 for(x = local_rect.left; x < local_rect.right; x++) {
829 /* start lines pixels */
830 BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
831 BYTE *green = blue + 1;
832 BYTE *red = green + 1;
834 for(c = 0; c < 256; c++) {
835 if(*red == pal[c].peRed &&
836 *green == pal[c].peGreen &&
837 *blue == pal[c].peBlue)
839 *((BYTE *) dest + y * width + x) = c;
840 break;
845 HeapFree(GetProcessHeap(), 0, mem);
849 /* Read the framebuffer contents into a texture */
850 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This)
852 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
853 IWineD3DSwapChainImpl *swapchain;
854 int bpp;
855 GLenum format, internal, type;
856 CONVERT_TYPES convert;
857 BOOL srcIsUpsideDown;
858 GLint prevRead;
860 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
862 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
863 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
864 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
865 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
866 * context->last_was_blit set on the unlock.
868 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
869 surface_bind_and_dirtify(This);
870 ENTER_GL();
872 glGetIntegerv(GL_READ_BUFFER, &prevRead);
874 /* Select the correct read buffer, and give some debug output.
875 * There is no need to keep track of the current read buffer or reset it, every part of the code
876 * that reads sets the read buffer as desired.
878 if(!swapchain) {
879 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
880 * Read from the back buffer
882 TRACE("Locking offscreen render target\n");
883 glReadBuffer(device->offscreenBuffer);
884 srcIsUpsideDown = TRUE;
885 } else {
886 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
887 TRACE("Locking %#x buffer\n", buffer);
888 glReadBuffer(buffer);
889 checkGLcall("glReadBuffer");
891 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
892 srcIsUpsideDown = FALSE;
895 if(!(This->Flags & SFLAG_ALLOCATED)) {
896 surface_allocate_surface(This, internal, This->pow2Width,
897 This->pow2Height, format, type);
900 clear_unused_channels(This);
902 /* If !SrcIsUpsideDown we should flip the surface.
903 * This can be done using glCopyTexSubImage2D but this
904 * is VERY slow, so don't do that. We should prevent
905 * this code from getting called in such cases or perhaps
906 * we can use FBOs */
908 glCopyTexSubImage2D(This->glDescription.target,
909 This->glDescription.level,
910 0, 0, 0, 0,
911 This->currentDesc.Width,
912 This->currentDesc.Height);
913 checkGLcall("glCopyTexSubImage2D");
915 glReadBuffer(prevRead);
916 vcheckGLcall("glReadBuffer");
918 LEAVE_GL();
919 TRACE("Updated target %d\n", This->glDescription.target);
922 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
923 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
924 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
925 * changed
927 if(!(This->Flags & SFLAG_DYNLOCK)) {
928 This->lockCount++;
929 /* MAXLOCKCOUNT is defined in wined3d_private.h */
930 if(This->lockCount > MAXLOCKCOUNT) {
931 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
932 This->Flags |= SFLAG_DYNLOCK;
936 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
937 * Also don't create a PBO for systemmem surfaces.
939 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
940 GLenum error;
941 ENTER_GL();
943 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
944 error = glGetError();
945 if(This->pbo == 0 || error != GL_NO_ERROR) {
946 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
949 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
951 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
952 checkGLcall("glBindBufferARB");
954 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
955 checkGLcall("glBufferDataARB");
957 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
958 checkGLcall("glBindBufferARB");
960 /* We don't need the system memory anymore and we can't even use it for PBOs */
961 if(!(This->Flags & SFLAG_CLIENT)) {
962 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
963 This->resource.heapMemory = NULL;
965 This->resource.allocatedMemory = NULL;
966 This->Flags |= SFLAG_PBO;
967 LEAVE_GL();
968 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
969 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
970 * or a pbo to map
972 if(!This->resource.heapMemory) {
973 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
975 This->resource.allocatedMemory =
976 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
977 if(This->Flags & SFLAG_INSYSMEM) {
978 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
983 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
984 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
985 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
986 IWineD3DSwapChain *swapchain = NULL;
988 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
990 /* This is also done in the base class, but we have to verify this before loading any data from
991 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
992 * may interfere, and all other bad things may happen
994 if (This->Flags & SFLAG_LOCKED) {
995 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
996 return WINED3DERR_INVALIDCALL;
998 This->Flags |= SFLAG_LOCKED;
1000 if (!(This->Flags & SFLAG_LOCKABLE))
1002 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1005 if (Flags & WINED3DLOCK_DISCARD) {
1006 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1007 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1008 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1009 This->Flags |= SFLAG_INSYSMEM;
1010 goto lock_end;
1013 if (This->Flags & SFLAG_INSYSMEM) {
1014 TRACE("Local copy is up to date, not downloading data\n");
1015 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1016 goto lock_end;
1019 /* Now download the surface content from opengl
1020 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1021 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
1023 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1024 if(swapchain || iface == myDevice->render_targets[0]) {
1025 const RECT *pass_rect = pRect;
1027 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1028 * because most caller functions do not need that. So do that here
1030 if(pRect &&
1031 pRect->top == 0 &&
1032 pRect->left == 0 &&
1033 pRect->right == This->currentDesc.Width &&
1034 pRect->bottom == This->currentDesc.Height) {
1035 pass_rect = NULL;
1038 switch(wined3d_settings.rendertargetlock_mode) {
1039 case RTL_TEXDRAW:
1040 case RTL_TEXTEX:
1041 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1042 #if 0
1043 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1044 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1045 * This may be faster on some cards
1047 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1048 #endif
1049 /* drop through */
1051 case RTL_AUTO:
1052 case RTL_READDRAW:
1053 case RTL_READTEX:
1054 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
1055 break;
1057 case RTL_DISABLE:
1058 break;
1060 if(swapchain) IWineD3DSwapChain_Release(swapchain);
1062 } else if(iface == myDevice->stencilBufferTarget) {
1063 /** the depth stencil in openGL has a format of GL_FLOAT
1064 * which should be good for WINED3DFMT_D16_LOCKABLE
1065 * and WINED3DFMT_D16
1066 * it is unclear what format the stencil buffer is in except.
1067 * 'Each index is converted to fixed point...
1068 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1069 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1070 * glReadPixels(This->lockedRect.left,
1071 * This->lockedRect.bottom - j - 1,
1072 * This->lockedRect.right - This->lockedRect.left,
1073 * 1,
1074 * GL_DEPTH_COMPONENT,
1075 * type,
1076 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1078 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1079 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1080 * none of that is the case the problem is not in this function :-)
1081 ********************************************/
1082 FIXME("Depth stencil locking not supported yet\n");
1083 } else {
1084 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1085 TRACE("locking an ordinary surface\n");
1086 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1089 lock_end:
1090 if(This->Flags & SFLAG_PBO) {
1091 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1092 ENTER_GL();
1093 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1094 checkGLcall("glBindBufferARB");
1096 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1097 if(This->resource.allocatedMemory) {
1098 ERR("The surface already has PBO memory allocated!\n");
1101 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1102 checkGLcall("glMapBufferARB");
1104 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1105 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1106 checkGLcall("glBindBufferARB");
1108 LEAVE_GL();
1111 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1112 /* Don't dirtify */
1113 } else {
1114 IWineD3DBaseTexture *pBaseTexture;
1116 * Dirtify on lock
1117 * as seen in msdn docs
1119 IWineD3DSurface_AddDirtyRect(iface, pRect);
1121 /** Dirtify Container if needed */
1122 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
1123 TRACE("Making container dirty\n");
1124 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1125 IWineD3DBaseTexture_Release(pBaseTexture);
1126 } else {
1127 TRACE("Surface is standalone, no need to dirty the container\n");
1131 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1134 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1135 GLint prev_store;
1136 GLint prev_rasterpos[4];
1137 GLint skipBytes = 0;
1138 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1139 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1140 IWineD3DSwapChainImpl *swapchain;
1142 /* Activate the correct context for the render target */
1143 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1144 ENTER_GL();
1146 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
1147 if(!swapchain) {
1148 /* Primary offscreen render target */
1149 TRACE("Offscreen render target\n");
1150 glDrawBuffer(myDevice->offscreenBuffer);
1151 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1152 } else {
1153 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1154 TRACE("Unlocking %#x buffer\n", buffer);
1155 glDrawBuffer(buffer);
1156 checkGLcall("glDrawBuffer");
1158 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1161 glFlush();
1162 vcheckGLcall("glFlush");
1163 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1164 vcheckGLcall("glIntegerv");
1165 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1166 vcheckGLcall("glIntegerv");
1167 glPixelZoom(1.0, -1.0);
1168 vcheckGLcall("glPixelZoom");
1170 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1171 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1172 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1174 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1175 vcheckGLcall("glRasterPos2f");
1177 /* Some drivers(radeon dri, others?) don't like exceptions during
1178 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1179 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1180 * catch to put the dib section in InSync mode, which leads to a crash
1181 * and a blocked x server on my radeon card.
1183 * The following lines read the dib section so it is put in inSync mode
1184 * before glDrawPixels is called and the crash is prevented. There won't
1185 * be any interfering gdi accesses, because UnlockRect is called from
1186 * ReleaseDC, and the app won't use the dc any more afterwards.
1188 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1189 volatile BYTE read;
1190 read = This->resource.allocatedMemory[0];
1193 if(This->Flags & SFLAG_PBO) {
1194 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1195 checkGLcall("glBindBufferARB");
1198 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1199 if(This->Flags & SFLAG_LOCKED) {
1200 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1201 (This->lockedRect.bottom - This->lockedRect.top)-1,
1202 fmt, type,
1203 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1204 checkGLcall("glDrawPixels");
1205 } else {
1206 glDrawPixels(This->currentDesc.Width,
1207 This->currentDesc.Height,
1208 fmt, type, mem);
1209 checkGLcall("glDrawPixels");
1212 if(This->Flags & SFLAG_PBO) {
1213 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1214 checkGLcall("glBindBufferARB");
1217 glPixelZoom(1.0,1.0);
1218 vcheckGLcall("glPixelZoom");
1220 glRasterPos3iv(&prev_rasterpos[0]);
1221 vcheckGLcall("glRasterPos3iv");
1223 /* Reset to previous pack row length */
1224 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1225 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1227 if(!swapchain) {
1228 glDrawBuffer(myDevice->offscreenBuffer);
1229 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1230 } else if(swapchain->backBuffer) {
1231 glDrawBuffer(GL_BACK);
1232 checkGLcall("glDrawBuffer(GL_BACK)");
1233 } else {
1234 glDrawBuffer(GL_FRONT);
1235 checkGLcall("glDrawBuffer(GL_FRONT)");
1237 LEAVE_GL();
1239 return;
1242 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1243 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1244 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1245 IWineD3DSwapChainImpl *swapchain = NULL;
1246 BOOL fullsurface;
1248 if (!(This->Flags & SFLAG_LOCKED)) {
1249 WARN("trying to Unlock an unlocked surf@%p\n", This);
1250 return WINED3DERR_INVALIDCALL;
1253 if (This->Flags & SFLAG_PBO) {
1254 TRACE("Freeing PBO memory\n");
1255 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1256 ENTER_GL();
1257 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1258 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1259 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1260 checkGLcall("glUnmapBufferARB");
1261 LEAVE_GL();
1262 This->resource.allocatedMemory = NULL;
1265 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1267 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1268 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1269 goto unlock_end;
1272 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1273 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1274 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1276 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1277 static BOOL warned = FALSE;
1278 if(!warned) {
1279 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1280 warned = TRUE;
1282 goto unlock_end;
1285 if(This->dirtyRect.left == 0 &&
1286 This->dirtyRect.top == 0 &&
1287 This->dirtyRect.right == This->currentDesc.Width &&
1288 This->dirtyRect.bottom == This->currentDesc.Height) {
1289 fullsurface = TRUE;
1290 } else {
1291 /* TODO: Proper partial rectangle tracking */
1292 fullsurface = FALSE;
1293 This->Flags |= SFLAG_INSYSMEM;
1296 switch(wined3d_settings.rendertargetlock_mode) {
1297 case RTL_READTEX:
1298 case RTL_TEXTEX:
1299 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1300 ENTER_GL();
1301 if (This->glDescription.textureName == 0) {
1302 glGenTextures(1, &This->glDescription.textureName);
1303 checkGLcall("glGenTextures");
1305 glBindTexture(This->glDescription.target, This->glDescription.textureName);
1306 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
1307 LEAVE_GL();
1308 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1309 /* drop through */
1311 case RTL_AUTO:
1312 case RTL_READDRAW:
1313 case RTL_TEXDRAW:
1314 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1315 break;
1318 if(!fullsurface) {
1319 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1320 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1321 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1322 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1323 * not fully up to date because only a subrectangle was read in LockRect.
1325 This->Flags &= ~SFLAG_INSYSMEM;
1326 This->Flags |= SFLAG_INDRAWABLE;
1329 This->dirtyRect.left = This->currentDesc.Width;
1330 This->dirtyRect.top = This->currentDesc.Height;
1331 This->dirtyRect.right = 0;
1332 This->dirtyRect.bottom = 0;
1333 } else if(iface == myDevice->stencilBufferTarget) {
1334 FIXME("Depth Stencil buffer locking is not implemented\n");
1335 } else {
1336 /* The rest should be a normal texture */
1337 IWineD3DBaseTextureImpl *impl;
1338 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1339 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1340 * states need resetting
1342 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1343 if(impl->baseTexture.bindCount) {
1344 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1346 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1350 unlock_end:
1351 This->Flags &= ~SFLAG_LOCKED;
1352 memset(&This->lockedRect, 0, sizeof(RECT));
1353 return WINED3D_OK;
1356 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1357 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1358 WINED3DLOCKED_RECT lock;
1359 HRESULT hr;
1360 RGBQUAD col[256];
1362 TRACE("(%p)->(%p)\n",This,pHDC);
1364 if(This->Flags & SFLAG_USERPTR) {
1365 ERR("Not supported on surfaces with an application-provided surfaces\n");
1366 return WINEDDERR_NODC;
1369 /* Give more detailed info for ddraw */
1370 if (This->Flags & SFLAG_DCINUSE)
1371 return WINEDDERR_DCALREADYCREATED;
1373 /* Can't GetDC if the surface is locked */
1374 if (This->Flags & SFLAG_LOCKED)
1375 return WINED3DERR_INVALIDCALL;
1377 /* According to Direct3D9 docs, only these formats are supported */
1378 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1379 if (This->resource.format != WINED3DFMT_R5G6B5 &&
1380 This->resource.format != WINED3DFMT_X1R5G5B5 &&
1381 This->resource.format != WINED3DFMT_R8G8B8 &&
1382 This->resource.format != WINED3DFMT_X8R8G8B8) return WINED3DERR_INVALIDCALL;
1385 memset(&lock, 0, sizeof(lock)); /* To be sure */
1387 /* Create a DIB section if there isn't a hdc yet */
1388 if(!This->hDC) {
1389 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1390 if(This->Flags & SFLAG_CLIENT) {
1391 IWineD3DSurface_PreLoad(iface);
1394 /* Use the dib section from now on if we are not using a PBO */
1395 if(!(This->Flags & SFLAG_PBO))
1396 This->resource.allocatedMemory = This->dib.bitmap_data;
1399 /* Lock the surface */
1400 hr = IWineD3DSurface_LockRect(iface,
1401 &lock,
1402 NULL,
1405 if(This->Flags & SFLAG_PBO) {
1406 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1407 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1410 if(FAILED(hr)) {
1411 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1412 /* keep the dib section */
1413 return hr;
1416 if(This->resource.format == WINED3DFMT_P8 ||
1417 This->resource.format == WINED3DFMT_A8P8) {
1418 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1419 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1420 unsigned int n;
1421 PALETTEENTRY *pal = NULL;
1423 if(This->palette) {
1424 pal = This->palette->palents;
1425 } else {
1426 IWineD3DSurfaceImpl *dds_primary = (IWineD3DSurfaceImpl *)This->resource.wineD3DDevice->ddraw_primary;
1427 if (dds_primary && dds_primary->palette)
1428 pal = dds_primary->palette->palents;
1431 if (pal) {
1432 for (n=0; n<256; n++) {
1433 col[n].rgbRed = pal[n].peRed;
1434 col[n].rgbGreen = pal[n].peGreen;
1435 col[n].rgbBlue = pal[n].peBlue;
1436 col[n].rgbReserved = 0;
1438 SetDIBColorTable(This->hDC, 0, 256, col);
1442 *pHDC = This->hDC;
1443 TRACE("returning %p\n",*pHDC);
1444 This->Flags |= SFLAG_DCINUSE;
1446 return WINED3D_OK;
1449 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1450 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1452 TRACE("(%p)->(%p)\n",This,hDC);
1454 if (!(This->Flags & SFLAG_DCINUSE))
1455 return WINED3DERR_INVALIDCALL;
1457 if (This->hDC !=hDC) {
1458 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1459 return WINED3DERR_INVALIDCALL;
1462 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1463 /* Copy the contents of the DIB over to the PBO */
1464 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1467 /* we locked first, so unlock now */
1468 IWineD3DSurface_UnlockRect(iface);
1470 This->Flags &= ~SFLAG_DCINUSE;
1472 return WINED3D_OK;
1475 /* ******************************************************
1476 IWineD3DSurface Internal (No mapping to directx api) parts follow
1477 ****************************************************** */
1479 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) {
1480 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1481 const GlPixelFormatDesc *glDesc;
1482 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1483 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1485 /* Default values: From the surface */
1486 *format = glDesc->glFormat;
1487 *type = glDesc->glType;
1488 *convert = NO_CONVERSION;
1489 *target_bpp = This->bytesPerPixel;
1491 if(srgb_mode) {
1492 *internal = glDesc->glGammaInternal;
1493 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1494 *internal = glDesc->rtInternal;
1495 } else {
1496 *internal = glDesc->glInternal;
1499 /* Ok, now look if we have to do any conversion */
1500 switch(This->resource.format) {
1501 case WINED3DFMT_P8:
1502 /* ****************
1503 Paletted Texture
1504 **************** */
1506 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1507 * of the two is available make sure texturing is requested as neither of the two works in
1508 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1509 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1510 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1511 * conflicts with this.
1513 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) && primary_render_target_is_p8(device))) || colorkey_active || !use_texturing ) {
1514 *format = GL_RGBA;
1515 *internal = GL_RGBA;
1516 *type = GL_UNSIGNED_BYTE;
1517 *target_bpp = 4;
1518 if(colorkey_active) {
1519 *convert = CONVERT_PALETTED_CK;
1520 } else {
1521 *convert = CONVERT_PALETTED;
1524 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1525 *format = GL_ALPHA;
1526 *internal = GL_RGBA;
1527 *type = GL_UNSIGNED_BYTE;
1528 *target_bpp = 1;
1531 break;
1533 case WINED3DFMT_R3G3B2:
1534 /* **********************
1535 GL_UNSIGNED_BYTE_3_3_2
1536 ********************** */
1537 if (colorkey_active) {
1538 /* This texture format will never be used.. So do not care about color keying
1539 up until the point in time it will be needed :-) */
1540 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1542 break;
1544 case WINED3DFMT_R5G6B5:
1545 if (colorkey_active) {
1546 *convert = CONVERT_CK_565;
1547 *format = GL_RGBA;
1548 *internal = GL_RGBA;
1549 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1551 break;
1553 case WINED3DFMT_X1R5G5B5:
1554 if (colorkey_active) {
1555 *convert = CONVERT_CK_5551;
1556 *format = GL_BGRA;
1557 *internal = GL_RGBA;
1558 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1560 break;
1562 case WINED3DFMT_R8G8B8:
1563 if (colorkey_active) {
1564 *convert = CONVERT_CK_RGB24;
1565 *format = GL_RGBA;
1566 *internal = GL_RGBA;
1567 *type = GL_UNSIGNED_INT_8_8_8_8;
1568 *target_bpp = 4;
1570 break;
1572 case WINED3DFMT_X8R8G8B8:
1573 if (colorkey_active) {
1574 *convert = CONVERT_RGB32_888;
1575 *format = GL_RGBA;
1576 *internal = GL_RGBA;
1577 *type = GL_UNSIGNED_INT_8_8_8_8;
1579 break;
1581 case WINED3DFMT_V8U8:
1582 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1583 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1584 *format = GL_DUDV_ATI;
1585 *internal = GL_DU8DV8_ATI;
1586 *type = GL_BYTE;
1587 /* No conversion - Just change the gl type */
1588 break;
1590 *convert = CONVERT_V8U8;
1591 *format = GL_BGR;
1592 *internal = GL_RGB8;
1593 *type = GL_UNSIGNED_BYTE;
1594 *target_bpp = 3;
1595 break;
1597 case WINED3DFMT_L6V5U5:
1598 *convert = CONVERT_L6V5U5;
1599 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1600 *target_bpp = 3;
1601 /* Use format and types from table */
1602 } else {
1603 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1604 *target_bpp = 2;
1605 *format = GL_RGB;
1606 *internal = GL_RGB5;
1607 *type = GL_UNSIGNED_SHORT_5_6_5;
1609 break;
1611 case WINED3DFMT_X8L8V8U8:
1612 *convert = CONVERT_X8L8V8U8;
1613 *target_bpp = 4;
1614 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1615 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1616 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1617 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1618 * the needed type and format parameter, so the internal format contains a
1619 * 4th component, which is returned as alpha
1621 } else {
1622 /* Not supported by GL_ATI_envmap_bumpmap */
1623 *format = GL_BGRA;
1624 *internal = GL_RGB8;
1625 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1627 break;
1629 case WINED3DFMT_Q8W8V8U8:
1630 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1631 *convert = CONVERT_Q8W8V8U8;
1632 *format = GL_BGRA;
1633 *internal = GL_RGBA8;
1634 *type = GL_UNSIGNED_BYTE;
1635 *target_bpp = 4;
1636 /* Not supported by GL_ATI_envmap_bumpmap */
1637 break;
1639 case WINED3DFMT_V16U16:
1640 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1641 *convert = CONVERT_V16U16;
1642 *format = GL_BGR;
1643 *internal = GL_RGB16_EXT;
1644 *type = GL_UNSIGNED_SHORT;
1645 *target_bpp = 6;
1646 /* What should I do here about GL_ATI_envmap_bumpmap?
1647 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1649 break;
1651 case WINED3DFMT_A4L4:
1652 /* A4L4 exists as an internal gl format, but for some reason there is not
1653 * format+type combination to load it. Thus convert it to A8L8, then load it
1654 * with A4L4 internal, but A8L8 format+type
1656 *convert = CONVERT_A4L4;
1657 *format = GL_LUMINANCE_ALPHA;
1658 *internal = GL_LUMINANCE4_ALPHA4;
1659 *type = GL_UNSIGNED_BYTE;
1660 *target_bpp = 2;
1661 break;
1663 case WINED3DFMT_R32F:
1664 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1665 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1666 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1667 * 1.0 instead.
1669 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1671 *convert = CONVERT_R32F;
1672 *format = GL_RGB;
1673 *internal = GL_RGB32F_ARB;
1674 *type = GL_FLOAT;
1675 *target_bpp = 12;
1676 break;
1678 case WINED3DFMT_R16F:
1679 /* Similar to R32F */
1680 *convert = CONVERT_R16F;
1681 *format = GL_RGB;
1682 *internal = GL_RGB16F_ARB;
1683 *type = GL_HALF_FLOAT_ARB;
1684 *target_bpp = 6;
1685 break;
1687 case WINED3DFMT_G16R16:
1688 *convert = CONVERT_G16R16;
1689 *format = GL_RGB;
1690 *internal = GL_RGB16_EXT;
1691 *type = GL_UNSIGNED_SHORT;
1692 *target_bpp = 6;
1693 break;
1695 default:
1696 break;
1699 return WINED3D_OK;
1702 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1703 BYTE *source, *dest;
1704 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1706 switch (convert) {
1707 case NO_CONVERSION:
1709 memcpy(dst, src, pitch * height);
1710 break;
1712 case CONVERT_PALETTED:
1713 case CONVERT_PALETTED_CK:
1715 IWineD3DPaletteImpl* pal = This->palette;
1716 BYTE table[256][4];
1717 unsigned int x, y;
1719 if( pal == NULL) {
1720 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1723 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1725 for (y = 0; y < height; y++)
1727 source = src + pitch * y;
1728 dest = dst + outpitch * y;
1729 /* This is an 1 bpp format, using the width here is fine */
1730 for (x = 0; x < width; x++) {
1731 BYTE color = *source++;
1732 *dest++ = table[color][0];
1733 *dest++ = table[color][1];
1734 *dest++ = table[color][2];
1735 *dest++ = table[color][3];
1739 break;
1741 case CONVERT_CK_565:
1743 /* Converting the 565 format in 5551 packed to emulate color-keying.
1745 Note : in all these conversion, it would be best to average the averaging
1746 pixels to get the color of the pixel that will be color-keyed to
1747 prevent 'color bleeding'. This will be done later on if ever it is
1748 too visible.
1750 Note2: Nvidia documents say that their driver does not support alpha + color keying
1751 on the same surface and disables color keying in such a case
1753 unsigned int x, y;
1754 WORD *Source;
1755 WORD *Dest;
1757 TRACE("Color keyed 565\n");
1759 for (y = 0; y < height; y++) {
1760 Source = (WORD *) (src + y * pitch);
1761 Dest = (WORD *) (dst + y * outpitch);
1762 for (x = 0; x < width; x++ ) {
1763 WORD color = *Source++;
1764 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1765 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1766 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1767 *Dest |= 0x0001;
1769 Dest++;
1773 break;
1775 case CONVERT_CK_5551:
1777 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1778 unsigned int x, y;
1779 WORD *Source;
1780 WORD *Dest;
1781 TRACE("Color keyed 5551\n");
1782 for (y = 0; y < height; y++) {
1783 Source = (WORD *) (src + y * pitch);
1784 Dest = (WORD *) (dst + y * outpitch);
1785 for (x = 0; x < width; x++ ) {
1786 WORD color = *Source++;
1787 *Dest = color;
1788 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1789 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1790 *Dest |= (1 << 15);
1792 else {
1793 *Dest &= ~(1 << 15);
1795 Dest++;
1799 break;
1801 case CONVERT_V8U8:
1803 unsigned int x, y;
1804 short *Source;
1805 unsigned char *Dest;
1806 for(y = 0; y < height; y++) {
1807 Source = (short *) (src + y * pitch);
1808 Dest = dst + y * outpitch;
1809 for (x = 0; x < width; x++ ) {
1810 long color = (*Source++);
1811 /* B */ Dest[0] = 0xff;
1812 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1813 /* R */ Dest[2] = (color) + 128; /* U */
1814 Dest += 3;
1817 break;
1820 case CONVERT_V16U16:
1822 unsigned int x, y;
1823 DWORD *Source;
1824 unsigned short *Dest;
1825 for(y = 0; y < height; y++) {
1826 Source = (DWORD *) (src + y * pitch);
1827 Dest = (unsigned short *) (dst + y * outpitch);
1828 for (x = 0; x < width; x++ ) {
1829 DWORD color = (*Source++);
1830 /* B */ Dest[0] = 0xffff;
1831 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1832 /* R */ Dest[2] = (color ) + 32768; /* U */
1833 Dest += 3;
1836 break;
1839 case CONVERT_Q8W8V8U8:
1841 unsigned int x, y;
1842 DWORD *Source;
1843 unsigned char *Dest;
1844 for(y = 0; y < height; y++) {
1845 Source = (DWORD *) (src + y * pitch);
1846 Dest = dst + y * outpitch;
1847 for (x = 0; x < width; x++ ) {
1848 long color = (*Source++);
1849 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1850 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1851 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1852 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1853 Dest += 4;
1856 break;
1859 case CONVERT_L6V5U5:
1861 unsigned int x, y;
1862 WORD *Source;
1863 unsigned char *Dest;
1865 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1866 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1867 * fixed function and shaders without further conversion once the surface is
1868 * loaded
1870 for(y = 0; y < height; y++) {
1871 Source = (WORD *) (src + y * pitch);
1872 Dest = dst + y * outpitch;
1873 for (x = 0; x < width; x++ ) {
1874 short color = (*Source++);
1875 unsigned char l = ((color >> 10) & 0xfc);
1876 char v = ((color >> 5) & 0x3e);
1877 char u = ((color ) & 0x1f);
1879 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1880 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1881 * shift. GL reads a signed value and converts it into an unsigned value.
1883 /* M */ Dest[2] = l << 1;
1885 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1886 * from 5 bit values to 8 bit values.
1888 /* V */ Dest[1] = v << 3;
1889 /* U */ Dest[0] = u << 3;
1890 Dest += 3;
1893 } else {
1894 for(y = 0; y < height; y++) {
1895 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1896 Source = (WORD *) (src + y * pitch);
1897 for (x = 0; x < width; x++ ) {
1898 short color = (*Source++);
1899 unsigned char l = ((color >> 10) & 0xfc);
1900 short v = ((color >> 5) & 0x3e);
1901 short u = ((color ) & 0x1f);
1902 short v_conv = v + 16;
1903 short u_conv = u + 16;
1905 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1906 Dest_s += 1;
1910 break;
1913 case CONVERT_X8L8V8U8:
1915 unsigned int x, y;
1916 DWORD *Source;
1917 unsigned char *Dest;
1919 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1920 /* This implementation works with the fixed function pipeline and shaders
1921 * without further modification after converting the surface.
1923 for(y = 0; y < height; y++) {
1924 Source = (DWORD *) (src + y * pitch);
1925 Dest = dst + y * outpitch;
1926 for (x = 0; x < width; x++ ) {
1927 long color = (*Source++);
1928 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1929 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1930 /* U */ Dest[0] = (color & 0xff); /* U */
1931 /* I */ Dest[3] = 255; /* X */
1932 Dest += 4;
1935 } else {
1936 /* Doesn't work correctly with the fixed function pipeline, but can work in
1937 * shaders if the shader is adjusted. (There's no use for this format in gl's
1938 * standard fixed function pipeline anyway).
1940 for(y = 0; y < height; y++) {
1941 Source = (DWORD *) (src + y * pitch);
1942 Dest = dst + y * outpitch;
1943 for (x = 0; x < width; x++ ) {
1944 long color = (*Source++);
1945 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1946 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1947 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1948 Dest += 4;
1952 break;
1955 case CONVERT_A4L4:
1957 unsigned int x, y;
1958 unsigned char *Source;
1959 unsigned char *Dest;
1960 for(y = 0; y < height; y++) {
1961 Source = src + y * pitch;
1962 Dest = dst + y * outpitch;
1963 for (x = 0; x < width; x++ ) {
1964 unsigned char color = (*Source++);
1965 /* A */ Dest[1] = (color & 0xf0) << 0;
1966 /* L */ Dest[0] = (color & 0x0f) << 4;
1967 Dest += 2;
1970 break;
1973 case CONVERT_R32F:
1975 unsigned int x, y;
1976 float *Source;
1977 float *Dest;
1978 for(y = 0; y < height; y++) {
1979 Source = (float *) (src + y * pitch);
1980 Dest = (float *) (dst + y * outpitch);
1981 for (x = 0; x < width; x++ ) {
1982 float color = (*Source++);
1983 Dest[0] = color;
1984 Dest[1] = 1.0;
1985 Dest[2] = 1.0;
1986 Dest += 3;
1989 break;
1992 case CONVERT_R16F:
1994 unsigned int x, y;
1995 WORD *Source;
1996 WORD *Dest;
1997 WORD one = 0x3c00;
1998 for(y = 0; y < height; y++) {
1999 Source = (WORD *) (src + y * pitch);
2000 Dest = (WORD *) (dst + y * outpitch);
2001 for (x = 0; x < width; x++ ) {
2002 WORD color = (*Source++);
2003 Dest[0] = color;
2004 Dest[1] = one;
2005 Dest[2] = one;
2006 Dest += 3;
2009 break;
2012 case CONVERT_G16R16:
2014 unsigned int x, y;
2015 WORD *Source;
2016 WORD *Dest;
2018 for(y = 0; y < height; y++) {
2019 Source = (WORD *) (src + y * pitch);
2020 Dest = (WORD *) (dst + y * outpitch);
2021 for (x = 0; x < width; x++ ) {
2022 WORD green = (*Source++);
2023 WORD red = (*Source++);
2024 Dest[0] = green;
2025 Dest[1] = red;
2026 Dest[2] = 0xffff;
2027 Dest += 3;
2030 break;
2033 default:
2034 ERR("Unsupported conversation type %d\n", convert);
2036 return WINED3D_OK;
2039 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2040 IWineD3DPaletteImpl* pal = This->palette;
2041 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2042 BOOL index_in_alpha = FALSE;
2043 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2044 int i;
2046 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2047 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2048 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2049 * duplicate entries. Store the color key in the unused alpha component to speed the
2050 * download up and to make conversion unneeded. */
2051 index_in_alpha = primary_render_target_is_p8(device);
2053 if (pal == NULL) {
2054 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2055 if(dxVersion <= 7) {
2056 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2057 return;
2060 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2061 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2062 capability flag is present (wine does advertise this capability) */
2063 for (i = 0; i < 256; i++) {
2064 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2065 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2066 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2067 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2069 } else {
2070 TRACE("Using surface palette %p\n", pal);
2071 /* Get the surface's palette */
2072 for (i = 0; i < 256; i++) {
2073 table[i][0] = pal->palents[i].peRed;
2074 table[i][1] = pal->palents[i].peGreen;
2075 table[i][2] = pal->palents[i].peBlue;
2077 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2078 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2079 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2080 of pixels that should be masked away is set to 0. */
2081 if(index_in_alpha) {
2082 table[i][3] = i;
2083 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2084 table[i][3] = 0x00;
2085 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2086 table[i][3] = pal->palents[i].peFlags;
2087 } else {
2088 table[i][3] = 0xFF;
2094 const char *fragment_palette_conversion =
2095 "!!ARBfp1.0\n"
2096 "TEMP index;\n"
2097 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
2098 "TEX index, fragment.texcoord[0], texture[0], 2D;\n" /* The alpha-component contains the palette index */
2099 "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 */
2100 "TEX result.color, index.a, texture[1], 1D;\n" /* use the alpha-component as a index in the palette to get the final color */
2101 "END";
2103 /* This function is used in case of 8bit paletted textures to upload the palette.
2104 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2105 extensions like ATI_fragment_shaders is possible.
2107 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2108 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2109 BYTE table[256][4];
2110 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2112 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2114 /* Try to use the paletted texture extension */
2115 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2117 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2118 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2120 else
2122 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2123 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2124 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2126 /* Create the fragment program if we don't have it */
2127 if(!device->paletteConversionShader)
2129 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2130 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2131 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2132 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2133 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2136 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2137 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2139 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2140 glEnable(GL_TEXTURE_1D);
2141 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2143 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2144 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2145 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2146 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2148 /* Switch back to unit 0 in which the 2D texture will be stored. */
2149 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2151 /* Rebind the texture because it isn't bound anymore */
2152 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2156 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2157 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2159 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2160 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2161 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2163 return FALSE;
2166 if(This->palette9) {
2167 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2168 return FALSE;
2170 } else {
2171 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2173 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2174 return TRUE;
2177 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2178 GLboolean oldwrite[4];
2180 /* Some formats have only some color channels, and the others are 1.0.
2181 * since our rendering renders to all channels, and those pixel formats
2182 * are emulated by using a full texture with the other channels set to 1.0
2183 * manually, clear the unused channels.
2185 * This could be done with hacking colorwriteenable to mask the colors,
2186 * but before drawing the buffer would have to be cleared too, so there's
2187 * no gain in that
2189 switch(This->resource.format) {
2190 case WINED3DFMT_R16F:
2191 case WINED3DFMT_R32F:
2192 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2193 /* Do not activate a context, the correct drawable is active already
2194 * though just the read buffer is set, make sure to have the correct draw
2195 * buffer too
2197 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2198 glDisable(GL_SCISSOR_TEST);
2199 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2200 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2201 glClearColor(0.0, 1.0, 1.0, 1.0);
2202 glClear(GL_COLOR_BUFFER_BIT);
2203 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2204 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2205 checkGLcall("Unused channel clear\n");
2206 break;
2208 default: break;
2212 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2213 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2215 if (!(This->Flags & SFLAG_INTEXTURE)) {
2216 TRACE("Reloading because surface is dirty\n");
2217 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2218 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2219 /* Reload: vice versa OR */
2220 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2221 /* Also reload: Color key is active AND the color key has changed */
2222 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2223 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2224 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2225 TRACE("Reloading because of color keying\n");
2226 /* To perform the color key conversion we need a sysmem copy of
2227 * the surface. Make sure we have it
2230 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2231 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2232 /* TODO: This is not necessarily needed with hw palettized texture support */
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 DWORD alpha;
3397 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3398 else alpha = 0xFF000000;
3400 if (This->palette) {
3401 color = (alpha |
3402 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3403 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3404 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3405 } else {
3406 color = alpha;
3409 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3410 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3411 color = 0xFFFFFFFF;
3412 } else {
3413 color = ((0xFF000000) |
3414 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3415 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3416 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3419 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3420 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3421 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3423 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3424 color = DDBltFx->u5.dwFillColor;
3426 else {
3427 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3428 return WINED3DERR_INVALIDCALL;
3431 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3432 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3433 1, /* Number of rectangles */
3434 &rect, WINED3DCLEAR_TARGET, color,
3435 0.0 /* Z */,
3436 0 /* Stencil */);
3437 return WINED3D_OK;
3441 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3442 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3443 return WINED3DERR_INVALIDCALL;
3446 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3448 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3449 float depth;
3451 if (Flags & WINEDDBLT_DEPTHFILL) {
3452 switch(This->resource.format) {
3453 case WINED3DFMT_D16:
3454 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3455 break;
3456 case WINED3DFMT_D15S1:
3457 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3458 break;
3459 case WINED3DFMT_D24S8:
3460 case WINED3DFMT_D24X8:
3461 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3462 break;
3463 case WINED3DFMT_D32:
3464 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3465 break;
3466 default:
3467 depth = 0.0;
3468 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3471 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3472 DestRect == NULL ? 0 : 1,
3473 (WINED3DRECT *) DestRect,
3474 WINED3DCLEAR_ZBUFFER,
3475 0x00000000,
3476 depth,
3477 0x00000000);
3480 FIXME("(%p): Unsupp depthstencil blit\n", This);
3481 return WINED3DERR_INVALIDCALL;
3484 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3485 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3486 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3487 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3488 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3489 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3491 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3493 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3494 return WINEDDERR_SURFACEBUSY;
3497 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3498 * except depth blits, which seem to work
3500 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3501 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3502 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3503 return WINED3DERR_INVALIDCALL;
3504 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3505 TRACE("Z Blit override handled the blit\n");
3506 return WINED3D_OK;
3510 /* Special cases for RenderTargets */
3511 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3512 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3513 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3516 /* For the rest call the X11 surface implementation.
3517 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3518 * other Blts are rather rare
3520 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3523 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3524 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3525 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3526 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3527 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3529 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3531 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3532 return WINEDDERR_SURFACEBUSY;
3535 if(myDevice->inScene &&
3536 (iface == myDevice->stencilBufferTarget ||
3537 (Source && Source == myDevice->stencilBufferTarget))) {
3538 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3539 return WINED3DERR_INVALIDCALL;
3542 /* Special cases for RenderTargets */
3543 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3544 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3546 RECT SrcRect, DstRect;
3547 DWORD Flags=0;
3549 if(rsrc) {
3550 SrcRect.left = rsrc->left;
3551 SrcRect.top= rsrc->top;
3552 SrcRect.bottom = rsrc->bottom;
3553 SrcRect.right = rsrc->right;
3554 } else {
3555 SrcRect.left = 0;
3556 SrcRect.top = 0;
3557 SrcRect.right = srcImpl->currentDesc.Width;
3558 SrcRect.bottom = srcImpl->currentDesc.Height;
3561 DstRect.left = dstx;
3562 DstRect.top=dsty;
3563 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3564 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3566 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3567 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3568 Flags |= WINEDDBLT_KEYSRC;
3569 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3570 Flags |= WINEDDBLT_KEYDEST;
3571 if(trans & WINEDDBLTFAST_WAIT)
3572 Flags |= WINEDDBLT_WAIT;
3573 if(trans & WINEDDBLTFAST_DONOTWAIT)
3574 Flags |= WINEDDBLT_DONOTWAIT;
3576 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3580 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3583 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3584 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3585 RGBQUAD col[256];
3586 IWineD3DPaletteImpl *pal = This->palette;
3587 unsigned int n;
3588 TRACE("(%p)\n", This);
3590 if (!pal) return WINED3D_OK;
3592 if(This->resource.format == WINED3DFMT_P8 ||
3593 This->resource.format == WINED3DFMT_A8P8)
3595 if(!(This->Flags & SFLAG_INSYSMEM)) {
3596 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3597 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3599 TRACE("Dirtifying surface\n");
3600 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3603 if(This->Flags & SFLAG_DIBSECTION) {
3604 TRACE("(%p): Updating the hdc's palette\n", This);
3605 for (n=0; n<256; n++) {
3606 col[n].rgbRed = pal->palents[n].peRed;
3607 col[n].rgbGreen = pal->palents[n].peGreen;
3608 col[n].rgbBlue = pal->palents[n].peBlue;
3609 col[n].rgbReserved = 0;
3611 SetDIBColorTable(This->hDC, 0, 256, col);
3614 /* Propagate the changes to the drawable when we have a palette.
3615 * TODO: in case of hardware p8 palettes we should only upload the palette. */
3616 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3617 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3619 return WINED3D_OK;
3622 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3623 /** Check against the maximum texture sizes supported by the video card **/
3624 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3625 unsigned int pow2Width, pow2Height;
3626 const GlPixelFormatDesc *glDesc;
3628 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3629 /* Setup some glformat defaults */
3630 This->glDescription.glFormat = glDesc->glFormat;
3631 This->glDescription.glFormatInternal = glDesc->glInternal;
3632 This->glDescription.glType = glDesc->glType;
3634 This->glDescription.textureName = 0;
3635 This->glDescription.target = GL_TEXTURE_2D;
3637 /* Non-power2 support */
3638 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3639 pow2Width = This->currentDesc.Width;
3640 pow2Height = This->currentDesc.Height;
3641 } else {
3642 /* Find the nearest pow2 match */
3643 pow2Width = pow2Height = 1;
3644 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3645 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3647 This->pow2Width = pow2Width;
3648 This->pow2Height = pow2Height;
3650 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3651 WINED3DFORMAT Format = This->resource.format;
3652 /** TODO: add support for non power two compressed textures **/
3653 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3654 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3655 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3656 This, This->currentDesc.Width, This->currentDesc.Height);
3657 return WINED3DERR_NOTAVAILABLE;
3661 if(pow2Width != This->currentDesc.Width ||
3662 pow2Height != This->currentDesc.Height) {
3663 This->Flags |= SFLAG_NONPOW2;
3666 TRACE("%p\n", This);
3667 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3668 /* one of three options
3669 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)
3670 2: Set the texture to the maximum size (bad idea)
3671 3: WARN and return WINED3DERR_NOTAVAILABLE;
3672 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.
3674 WARN("(%p) Creating an oversized surface\n", This);
3675 This->Flags |= SFLAG_OVERSIZE;
3677 /* This will be initialized on the first blt */
3678 This->glRect.left = 0;
3679 This->glRect.top = 0;
3680 This->glRect.right = 0;
3681 This->glRect.bottom = 0;
3682 } else {
3683 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3684 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3685 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3686 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3688 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE) &&
3689 !((This->resource.format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE) && (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3691 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3692 This->pow2Width = This->currentDesc.Width;
3693 This->pow2Height = This->currentDesc.Height;
3694 This->Flags &= ~SFLAG_NONPOW2;
3697 /* No oversize, gl rect is the full texture size */
3698 This->Flags &= ~SFLAG_OVERSIZE;
3699 This->glRect.left = 0;
3700 This->glRect.top = 0;
3701 This->glRect.right = This->pow2Width;
3702 This->glRect.bottom = This->pow2Height;
3705 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3706 switch(wined3d_settings.offscreen_rendering_mode) {
3707 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3708 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3709 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3713 This->Flags |= SFLAG_INSYSMEM;
3715 return WINED3D_OK;
3718 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3719 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3720 IWineD3DBaseTexture *texture;
3722 TRACE("(%p)->(%s, %s)\n", iface,
3723 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3724 persistent ? "TRUE" : "FALSE");
3726 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3727 IWineD3DSwapChain *swapchain = NULL;
3729 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3730 TRACE("Surface %p is an onscreen surface\n", iface);
3732 IWineD3DSwapChain_Release(swapchain);
3733 } else {
3734 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3735 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3739 if(persistent) {
3740 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
3741 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3742 TRACE("Passing to container\n");
3743 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3744 IWineD3DBaseTexture_Release(texture);
3747 This->Flags &= ~SFLAG_LOCATIONS;
3748 This->Flags |= flag;
3749 } else {
3750 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
3751 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3752 TRACE("Passing to container\n");
3753 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3754 IWineD3DBaseTexture_Release(texture);
3757 This->Flags &= ~flag;
3761 struct coords {
3762 GLfloat x, y, z;
3765 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
3766 struct coords coords[4];
3767 RECT rect;
3768 IWineD3DSwapChain *swapchain = NULL;
3769 IWineD3DBaseTexture *texture = NULL;
3770 HRESULT hr;
3771 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3773 if(rect_in) {
3774 rect = *rect_in;
3775 } else {
3776 rect.left = 0;
3777 rect.top = 0;
3778 rect.right = This->currentDesc.Width;
3779 rect.bottom = This->currentDesc.Height;
3782 ActivateContext(device, device->render_targets[0], CTXUSAGE_BLIT);
3783 ENTER_GL();
3785 if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
3786 glEnable(GL_TEXTURE_RECTANGLE_ARB);
3787 checkGLcall("glEnable(GL_TEXTURE_RECTANGLE_ARB)");
3788 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName);
3789 checkGLcall("GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName)");
3790 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3791 checkGLcall("glTexParameteri");
3792 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3793 checkGLcall("glTexParameteri");
3795 coords[0].x = rect.left;
3796 coords[0].z = 0;
3798 coords[1].x = rect.left;
3799 coords[1].z = 0;
3801 coords[2].x = rect.right;
3802 coords[2].z = 0;
3804 coords[3].x = rect.right;
3805 coords[3].z = 0;
3807 coords[0].y = rect.top;
3808 coords[1].y = rect.bottom;
3809 coords[2].y = rect.bottom;
3810 coords[3].y = rect.top;
3811 } else if(This->glDescription.target == GL_TEXTURE_2D) {
3812 glEnable(GL_TEXTURE_2D);
3813 checkGLcall("glEnable(GL_TEXTURE_2D)");
3814 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
3815 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
3816 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3817 checkGLcall("glTexParameteri");
3818 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3819 checkGLcall("glTexParameteri");
3821 coords[0].x = (float)rect.left / This->pow2Width;
3822 coords[0].z = 0;
3824 coords[1].x = (float)rect.left / This->pow2Width;
3825 coords[1].z = 0;
3827 coords[2].x = (float)rect.right / This->pow2Width;
3828 coords[2].z = 0;
3830 coords[3].x = (float)rect.right / This->pow2Width;
3831 coords[3].z = 0;
3833 coords[0].y = (float)rect.top / This->pow2Height;
3834 coords[1].y = (float)rect.bottom / This->pow2Height;
3835 coords[2].y = (float)rect.bottom / This->pow2Height;
3836 coords[3].y = (float)rect.top / This->pow2Height;
3837 } else {
3838 /* Must be a cube map */
3839 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
3840 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
3841 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
3842 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
3843 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3844 checkGLcall("glTexParameteri");
3845 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3846 checkGLcall("glTexParameteri");
3848 switch(This->glDescription.target) {
3849 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3850 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
3851 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3852 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3853 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
3854 break;
3856 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3857 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3858 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
3859 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
3860 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3861 break;
3863 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3864 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
3865 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3866 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3867 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
3868 break;
3870 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3871 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3872 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3873 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3874 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3875 break;
3877 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3878 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3879 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3880 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
3881 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
3882 break;
3884 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
3885 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
3886 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
3887 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3888 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3889 break;
3891 default:
3892 ERR("Unexpected texture target\n");
3893 LEAVE_GL();
3894 return;
3898 glBegin(GL_QUADS);
3899 glTexCoord3fv(&coords[0].x);
3900 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
3902 glTexCoord3fv(&coords[1].x);
3903 glVertex2i(rect.left, device->render_offscreen ? rect.top : rect.bottom);
3905 glTexCoord3fv(&coords[2].x);
3906 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
3908 glTexCoord3fv(&coords[3].x);
3909 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
3910 glEnd();
3911 checkGLcall("glEnd");
3913 if(This->glDescription.target != GL_TEXTURE_2D) {
3914 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3915 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3916 } else {
3917 glDisable(GL_TEXTURE_2D);
3918 checkGLcall("glDisable(GL_TEXTURE_2D)");
3921 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain);
3922 if(hr == WINED3D_OK && swapchain) {
3923 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
3924 if(((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
3925 glFlush();
3927 IWineD3DSwapChain_Release(swapchain);
3928 } else {
3929 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
3930 * reset properly next draw
3932 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture);
3933 if(hr == WINED3D_OK && texture) {
3934 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
3935 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
3936 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
3937 IWineD3DBaseTexture_Release(texture);
3940 LEAVE_GL();
3943 /*****************************************************************************
3944 * IWineD3DSurface::LoadLocation
3946 * Copies the current surface data from wherever it is to the requested
3947 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
3948 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
3949 * multiple locations, the gl texture is preferred over the drawable, which is
3950 * preferred over system memory. The PBO counts as system memory. If rect is
3951 * not NULL, only the specified rectangle is copied (only supported for
3952 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
3953 * location is marked up to date after the copy.
3955 * Parameters:
3956 * flag: Surface location flag to be updated
3957 * rect: rectangle to be copied
3959 * Returns:
3960 * WINED3D_OK on success
3961 * WINED3DERR_DEVICELOST on an internal error
3963 *****************************************************************************/
3964 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
3965 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3966 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3967 IWineD3DSwapChain *swapchain = NULL;
3968 GLenum format, internal, type;
3969 CONVERT_TYPES convert;
3970 int bpp;
3971 int width, pitch, outpitch;
3972 BYTE *mem;
3974 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3975 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3976 TRACE("Surface %p is an onscreen surface\n", iface);
3978 IWineD3DSwapChain_Release(swapchain);
3979 } else {
3980 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
3981 * Prefer SFLAG_INTEXTURE. */
3982 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
3986 TRACE("(%p)->(%s, %p)\n", iface,
3987 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3988 rect);
3989 if(rect) {
3990 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
3993 if(This->Flags & flag) {
3994 TRACE("Location already up to date\n");
3995 return WINED3D_OK;
3998 if(!(This->Flags & SFLAG_LOCATIONS)) {
3999 ERR("Surface does not have any up to date location\n");
4000 This->Flags |= SFLAG_LOST;
4001 return WINED3DERR_DEVICELOST;
4004 if(flag == SFLAG_INSYSMEM) {
4005 surface_prepare_system_memory(This);
4007 /* Download the surface to system memory */
4008 if(This->Flags & SFLAG_INTEXTURE) {
4009 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4010 surface_bind_and_dirtify(This);
4012 surface_download_data(This);
4013 } else {
4014 read_from_framebuffer(This, rect,
4015 This->resource.allocatedMemory,
4016 IWineD3DSurface_GetPitch(iface));
4018 } else if(flag == SFLAG_INDRAWABLE) {
4019 if(This->Flags & SFLAG_INTEXTURE) {
4020 surface_blt_to_drawable(This, rect);
4021 } else {
4022 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4024 /* The width is in 'length' not in bytes */
4025 width = This->currentDesc.Width;
4026 pitch = IWineD3DSurface_GetPitch(iface);
4028 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4029 int height = This->currentDesc.Height;
4031 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4032 outpitch = width * bpp;
4033 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4035 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4036 if(!mem) {
4037 ERR("Out of memory %d, %d!\n", outpitch, height);
4038 return WINED3DERR_OUTOFVIDEOMEMORY;
4040 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4042 This->Flags |= SFLAG_CONVERTED;
4043 } else {
4044 This->Flags &= ~SFLAG_CONVERTED;
4045 mem = This->resource.allocatedMemory;
4048 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4050 /* Don't delete PBO memory */
4051 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4052 HeapFree(GetProcessHeap(), 0, mem);
4054 } else /* if(flag == SFLAG_INTEXTURE) */ {
4055 if (This->Flags & SFLAG_INDRAWABLE) {
4056 read_from_framebuffer_texture(This);
4057 } else { /* Upload from system memory */
4058 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4060 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4061 surface_bind_and_dirtify(This);
4062 ENTER_GL();
4064 /* The only place where LoadTexture() might get called when isInDraw=1
4065 * is ActivateContext where lastActiveRenderTarget is preloaded.
4067 if(iface == device->lastActiveRenderTarget && device->isInDraw)
4068 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
4070 /* Otherwise: System memory copy must be most up to date */
4072 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4073 This->Flags |= SFLAG_GLCKEY;
4074 This->glCKey = This->SrcBltCKey;
4076 else This->Flags &= ~SFLAG_GLCKEY;
4078 /* The width is in 'length' not in bytes */
4079 width = This->currentDesc.Width;
4080 pitch = IWineD3DSurface_GetPitch(iface);
4082 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4083 int height = This->currentDesc.Height;
4085 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4086 outpitch = width * bpp;
4087 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4089 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4090 if(!mem) {
4091 ERR("Out of memory %d, %d!\n", outpitch, height);
4092 return WINED3DERR_OUTOFVIDEOMEMORY;
4094 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4096 This->Flags |= SFLAG_CONVERTED;
4097 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
4098 d3dfmt_p8_upload_palette(iface, convert);
4099 This->Flags &= ~SFLAG_CONVERTED;
4100 mem = This->resource.allocatedMemory;
4101 } else {
4102 This->Flags &= ~SFLAG_CONVERTED;
4103 mem = This->resource.allocatedMemory;
4106 /* Make sure the correct pitch is used */
4107 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4109 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4110 TRACE("non power of two support\n");
4111 if(!(This->Flags & SFLAG_ALLOCATED)) {
4112 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4114 if (mem || (This->Flags & SFLAG_PBO)) {
4115 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4117 } else {
4118 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4119 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4121 if(!(This->Flags & SFLAG_ALLOCATED)) {
4122 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4124 if (mem || (This->Flags & SFLAG_PBO)) {
4125 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4129 /* Restore the default pitch */
4130 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4131 LEAVE_GL();
4133 /* Don't delete PBO memory */
4134 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4135 HeapFree(GetProcessHeap(), 0, mem);
4139 if(rect == NULL) {
4140 This->Flags |= flag;
4143 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
4144 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4145 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4146 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4149 return WINED3D_OK;
4152 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
4153 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4154 IWineD3DSwapChain *swapchain = NULL;
4156 /* Update the drawable size method */
4157 if(container) {
4158 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4160 if(swapchain) {
4161 This->get_drawable_size = get_drawable_size_swapchain;
4162 IWineD3DSwapChain_Release(swapchain);
4163 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4164 switch(wined3d_settings.offscreen_rendering_mode) {
4165 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4166 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4167 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4171 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4174 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4175 return SURFACE_OPENGL;
4178 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4180 /* IUnknown */
4181 IWineD3DBaseSurfaceImpl_QueryInterface,
4182 IWineD3DBaseSurfaceImpl_AddRef,
4183 IWineD3DSurfaceImpl_Release,
4184 /* IWineD3DResource */
4185 IWineD3DBaseSurfaceImpl_GetParent,
4186 IWineD3DBaseSurfaceImpl_GetDevice,
4187 IWineD3DBaseSurfaceImpl_SetPrivateData,
4188 IWineD3DBaseSurfaceImpl_GetPrivateData,
4189 IWineD3DBaseSurfaceImpl_FreePrivateData,
4190 IWineD3DBaseSurfaceImpl_SetPriority,
4191 IWineD3DBaseSurfaceImpl_GetPriority,
4192 IWineD3DSurfaceImpl_PreLoad,
4193 IWineD3DSurfaceImpl_UnLoad,
4194 IWineD3DBaseSurfaceImpl_GetType,
4195 /* IWineD3DSurface */
4196 IWineD3DBaseSurfaceImpl_GetContainer,
4197 IWineD3DBaseSurfaceImpl_GetDesc,
4198 IWineD3DSurfaceImpl_LockRect,
4199 IWineD3DSurfaceImpl_UnlockRect,
4200 IWineD3DSurfaceImpl_GetDC,
4201 IWineD3DSurfaceImpl_ReleaseDC,
4202 IWineD3DSurfaceImpl_Flip,
4203 IWineD3DSurfaceImpl_Blt,
4204 IWineD3DBaseSurfaceImpl_GetBltStatus,
4205 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4206 IWineD3DBaseSurfaceImpl_IsLost,
4207 IWineD3DBaseSurfaceImpl_Restore,
4208 IWineD3DSurfaceImpl_BltFast,
4209 IWineD3DBaseSurfaceImpl_GetPalette,
4210 IWineD3DBaseSurfaceImpl_SetPalette,
4211 IWineD3DSurfaceImpl_RealizePalette,
4212 IWineD3DBaseSurfaceImpl_SetColorKey,
4213 IWineD3DBaseSurfaceImpl_GetPitch,
4214 IWineD3DSurfaceImpl_SetMem,
4215 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4216 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4217 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4218 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4219 IWineD3DBaseSurfaceImpl_SetClipper,
4220 IWineD3DBaseSurfaceImpl_GetClipper,
4221 /* Internal use: */
4222 IWineD3DSurfaceImpl_AddDirtyRect,
4223 IWineD3DSurfaceImpl_LoadTexture,
4224 IWineD3DSurfaceImpl_BindTexture,
4225 IWineD3DSurfaceImpl_SaveSnapshot,
4226 IWineD3DSurfaceImpl_SetContainer,
4227 IWineD3DSurfaceImpl_SetGlTextureDesc,
4228 IWineD3DSurfaceImpl_GetGlDesc,
4229 IWineD3DSurfaceImpl_GetData,
4230 IWineD3DSurfaceImpl_SetFormat,
4231 IWineD3DSurfaceImpl_PrivateSetup,
4232 IWineD3DSurfaceImpl_ModifyLocation,
4233 IWineD3DSurfaceImpl_LoadLocation,
4234 IWineD3DSurfaceImpl_GetImplType