push 73336d9f381967eae40f391d78198b916ed9848d
[wine/hacks.git] / dlls / wined3d / surface.c
blob413d3afaaa9e3a04b6d94edec2aaa935d0054ed1
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-2007 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 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
51 active_texture -= GL_TEXTURE0_ARB;
52 } else {
53 active_texture = 0;
55 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_texture));
56 IWineD3DSurface_BindTexture((IWineD3DSurface *)This);
59 /* This function checks if the primary render target uses the 8bit paletted format. */
60 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
62 if (device->render_targets && device->render_targets[0]) {
63 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
64 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
65 return TRUE;
67 return FALSE;
70 /* This call just downloads data, the caller is responsible for activating the
71 * right context and binding the correct texture. */
72 static void surface_download_data(IWineD3DSurfaceImpl *This) {
73 if (0 == This->glDescription.textureName) {
74 ERR("Surface does not have a texture, but SFLAG_INTEXTURE is set\n");
75 return;
78 /* Only support read back of converted P8 surfaces */
79 if(This->Flags & SFLAG_CONVERTED && (This->resource.format != WINED3DFMT_P8)) {
80 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(This->resource.format));
81 return;
84 ENTER_GL();
86 if (This->resource.format == WINED3DFMT_DXT1 ||
87 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
88 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
89 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
90 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
91 } else {
92 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
93 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
95 if(This->Flags & SFLAG_PBO) {
96 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
97 checkGLcall("glBindBufferARB");
98 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
99 checkGLcall("glGetCompressedTexImageARB()");
100 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
101 checkGLcall("glBindBufferARB");
102 } else {
103 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
104 checkGLcall("glGetCompressedTexImageARB()");
107 LEAVE_GL();
108 } else {
109 void *mem;
110 GLenum format = This->glDescription.glFormat;
111 GLenum type = This->glDescription.glType;
112 int src_pitch = 0;
113 int dst_pitch = 0;
115 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
116 if(This->resource.format == WINED3DFMT_P8) {
117 format = GL_ALPHA;
118 type = GL_UNSIGNED_BYTE;
121 if (This->Flags & SFLAG_NONPOW2) {
122 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
123 src_pitch = This->bytesPerPixel * This->pow2Width;
124 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
125 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
126 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
127 } else {
128 mem = This->resource.allocatedMemory;
131 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
132 format, type, mem);
134 if(This->Flags & SFLAG_PBO) {
135 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
136 checkGLcall("glBindBufferARB");
138 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
139 type, NULL);
140 checkGLcall("glGetTexImage()");
142 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
143 checkGLcall("glBindBufferARB");
144 } else {
145 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
146 type, mem);
147 checkGLcall("glGetTexImage()");
149 LEAVE_GL();
151 if (This->Flags & SFLAG_NONPOW2) {
152 LPBYTE src_data, dst_data;
153 int y;
155 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
156 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
157 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
159 * We're doing this...
161 * instead of boxing the texture :
162 * |<-texture width ->| -->pow2width| /\
163 * |111111111111111111| | |
164 * |222 Texture 222222| boxed empty | texture height
165 * |3333 Data 33333333| | |
166 * |444444444444444444| | \/
167 * ----------------------------------- |
168 * | boxed empty | boxed empty | pow2height
169 * | | | \/
170 * -----------------------------------
173 * we're repacking the data to the expected texture width
175 * |<-texture width ->| -->pow2width| /\
176 * |111111111111111111222222222222222| |
177 * |222333333333333333333444444444444| texture height
178 * |444444 | |
179 * | | \/
180 * | | |
181 * | empty | pow2height
182 * | | \/
183 * -----------------------------------
185 * == is the same as
187 * |<-texture width ->| /\
188 * |111111111111111111|
189 * |222222222222222222|texture height
190 * |333333333333333333|
191 * |444444444444444444| \/
192 * --------------------
194 * this also means that any references to allocatedMemory should work with the data as if were a
195 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
197 * internally the texture is still stored in a boxed format so any references to textureName will
198 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
200 * Performance should not be an issue, because applications normally do not lock the surfaces when
201 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
202 * and doesn't have to be re-read.
204 src_data = mem;
205 dst_data = This->resource.allocatedMemory;
206 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
207 for (y = 1 ; y < This->currentDesc.Height; y++) {
208 /* skip the first row */
209 src_data += src_pitch;
210 dst_data += dst_pitch;
211 memcpy(dst_data, src_data, dst_pitch);
214 HeapFree(GetProcessHeap(), 0, mem);
218 /* Surface has now been downloaded */
219 This->Flags |= SFLAG_INSYSMEM;
222 /* This call just uploads data, the caller is responsible for activating the
223 * right context and binding the correct texture. */
224 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
225 if (This->resource.format == WINED3DFMT_DXT1 ||
226 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
227 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
228 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
229 FIXME("Using DXT1/3/5 without advertized support\n");
230 } else {
231 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
232 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
233 * function uses glCompressedTexImage2D instead of the SubImage call
235 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
236 ENTER_GL();
238 if(This->Flags & SFLAG_PBO) {
239 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
240 checkGLcall("glBindBufferARB");
241 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
243 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
244 width, height, 0 /* border */, This->resource.size, NULL));
245 checkGLcall("glCompressedTexSubImage2D");
247 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
248 checkGLcall("glBindBufferARB");
249 } else {
250 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
251 width, height, 0 /* border */, This->resource.size, data));
252 checkGLcall("glCompressedTexSubImage2D");
254 LEAVE_GL();
256 } else {
257 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
258 ENTER_GL();
260 if(This->Flags & SFLAG_PBO) {
261 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
262 checkGLcall("glBindBufferARB");
263 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
265 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
266 checkGLcall("glTexSubImage2D");
268 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
269 checkGLcall("glBindBufferARB");
271 else {
272 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
273 checkGLcall("glTexSubImage2D");
276 LEAVE_GL();
280 /* This call just allocates the texture, the caller is responsible for
281 * activating the right context and binding the correct texture. */
282 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
283 BOOL enable_client_storage = FALSE;
284 BYTE *mem = NULL;
286 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,
287 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
289 if (This->resource.format == WINED3DFMT_DXT1 ||
290 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
291 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
292 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
293 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
295 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
296 * once, unfortunately
298 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
299 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
300 This->Flags |= SFLAG_CLIENT;
301 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
302 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
303 width, height, 0 /* border */, This->resource.size, mem));
306 return;
309 ENTER_GL();
311 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
312 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
313 /* In some cases we want to disable client storage.
314 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
315 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
316 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
317 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
318 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
320 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
321 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
322 This->Flags &= ~SFLAG_CLIENT;
323 enable_client_storage = TRUE;
324 } else {
325 This->Flags |= SFLAG_CLIENT;
327 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
328 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
330 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
333 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
334 checkGLcall("glTexImage2D");
336 if(enable_client_storage) {
337 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
338 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
340 LEAVE_GL();
342 This->Flags |= SFLAG_ALLOCATED;
345 /* In D3D the depth stencil dimensions have to be greater than or equal to the
346 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
347 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
348 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
349 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
350 renderbuffer_entry_t *entry;
351 GLuint renderbuffer = 0;
352 unsigned int src_width, src_height;
354 src_width = This->pow2Width;
355 src_height = This->pow2Height;
357 /* A depth stencil smaller than the render target is not valid */
358 if (width > src_width || height > src_height) return;
360 /* Remove any renderbuffer set if the sizes match */
361 if (width == src_width && height == src_height) {
362 This->current_renderbuffer = NULL;
363 return;
366 /* Look if we've already got a renderbuffer of the correct dimensions */
367 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
368 if (entry->width == width && entry->height == height) {
369 renderbuffer = entry->id;
370 This->current_renderbuffer = entry;
371 break;
375 if (!renderbuffer) {
376 const GlPixelFormatDesc *glDesc;
377 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
379 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
380 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
381 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
383 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
384 entry->width = width;
385 entry->height = height;
386 entry->id = renderbuffer;
387 list_add_head(&This->renderbuffers, &entry->entry);
389 This->current_renderbuffer = entry;
392 checkGLcall("set_compatible_renderbuffer");
395 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
396 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
397 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
399 TRACE("(%p) : swapchain %p\n", This, swapchain);
401 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
402 TRACE("Returning GL_BACK\n");
403 return GL_BACK;
404 } else if (swapchain_impl->frontBuffer == iface) {
405 TRACE("Returning GL_FRONT\n");
406 return GL_FRONT;
409 FIXME("Higher back buffer, returning GL_BACK\n");
410 return GL_BACK;
413 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
414 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
415 ULONG ref = InterlockedDecrement(&This->resource.ref);
416 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
417 if (ref == 0) {
418 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
419 renderbuffer_entry_t *entry, *entry2;
420 TRACE("(%p) : cleaning up\n", This);
422 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
424 /* Need a context to destroy the texture. Use the currently active render target, but only if
425 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
426 * When destroying the primary rt, Uninit3D will activate a context before doing anything
428 if(device->render_targets && device->render_targets[0]) {
429 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
432 TRACE("Deleting texture %d\n", This->glDescription.textureName);
433 ENTER_GL();
434 glDeleteTextures(1, &This->glDescription.textureName);
435 LEAVE_GL();
438 if(This->Flags & SFLAG_PBO) {
439 /* Delete the PBO */
440 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
443 if(This->Flags & SFLAG_DIBSECTION) {
444 /* Release the DC */
445 SelectObject(This->hDC, This->dib.holdbitmap);
446 DeleteDC(This->hDC);
447 /* Release the DIB section */
448 DeleteObject(This->dib.DIBsection);
449 This->dib.bitmap_data = NULL;
450 This->resource.allocatedMemory = NULL;
452 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
454 HeapFree(GetProcessHeap(), 0, This->palette9);
456 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
457 if(iface == device->ddraw_primary)
458 device->ddraw_primary = NULL;
460 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
461 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
462 HeapFree(GetProcessHeap(), 0, entry);
465 TRACE("(%p) Released\n", This);
466 HeapFree(GetProcessHeap(), 0, This);
469 return ref;
472 /* ****************************************************
473 IWineD3DSurface IWineD3DResource parts follow
474 **************************************************** */
476 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
477 /* TODO: check for locks */
478 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
479 IWineD3DBaseTexture *baseTexture = NULL;
480 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
482 TRACE("(%p)Checking to see if the container is a base texture\n", This);
483 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
484 TRACE("Passing to container\n");
485 IWineD3DBaseTexture_PreLoad(baseTexture);
486 IWineD3DBaseTexture_Release(baseTexture);
487 } else {
488 TRACE("(%p) : About to load surface\n", This);
490 if(!device->isInDraw) {
491 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
494 ENTER_GL();
495 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
496 if (!This->glDescription.level) {
497 if (!This->glDescription.textureName) {
498 glGenTextures(1, &This->glDescription.textureName);
499 checkGLcall("glGenTextures");
500 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
502 glBindTexture(This->glDescription.target, This->glDescription.textureName);
503 checkGLcall("glBindTexture");
504 IWineD3DSurface_LoadTexture(iface, FALSE);
505 /* This is where we should be reducing the amount of GLMemoryUsed */
506 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
507 /* assume this is a coding error not a real error for now */
508 FIXME("Mipmap surface has a glTexture bound to it!\n");
510 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
511 /* Tell opengl to try and keep this texture in video ram (well mostly) */
512 GLclampf tmp;
513 tmp = 0.9f;
514 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
516 LEAVE_GL();
518 return;
521 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
522 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
523 This->resource.allocatedMemory =
524 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
526 ENTER_GL();
527 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
528 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
529 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
530 checkGLcall("glGetBufferSubData");
531 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
532 checkGLcall("glDeleteBuffers");
533 LEAVE_GL();
535 This->pbo = 0;
536 This->Flags &= ~SFLAG_PBO;
539 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
540 IWineD3DBaseTexture *texture = NULL;
541 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
542 renderbuffer_entry_t *entry, *entry2;
543 TRACE("(%p)\n", iface);
545 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
546 /* Default pool resources are supposed to be destroyed before Reset is called.
547 * Implicit resources stay however. So this means we have an implicit render target
548 * or depth stencil. The content may be destroyed, but we still have to tear down
549 * opengl resources, so we cannot leave early.
551 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
552 } else {
553 /* Load the surface into system memory */
554 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
556 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
557 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
558 This->Flags &= ~SFLAG_ALLOCATED;
560 /* Destroy PBOs, but load them into real sysmem before */
561 if(This->Flags & SFLAG_PBO) {
562 surface_remove_pbo(This);
565 /* Destroy fbo render buffers. This is needed for implicit render targets, for
566 * all application-created targets the application has to release the surface
567 * before calling _Reset
569 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
570 ENTER_GL();
571 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
572 LEAVE_GL();
573 list_remove(&entry->entry);
574 HeapFree(GetProcessHeap(), 0, entry);
576 list_init(&This->renderbuffers);
577 This->current_renderbuffer = NULL;
579 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
580 * destroy it
582 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
583 if(!texture) {
584 ENTER_GL();
585 glDeleteTextures(1, &This->glDescription.textureName);
586 This->glDescription.textureName = 0;
587 LEAVE_GL();
588 } else {
589 IWineD3DBaseTexture_Release(texture);
591 return;
594 /* ******************************************************
595 IWineD3DSurface IWineD3DSurface parts follow
596 ****************************************************** */
598 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
599 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
600 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
601 if (This->glDescription.textureName == 0 && textureName != 0) {
602 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
603 IWineD3DSurface_AddDirtyRect(iface, NULL);
605 This->glDescription.textureName = textureName;
606 This->glDescription.target = target;
607 This->Flags &= ~SFLAG_ALLOCATED;
610 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
611 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
612 TRACE("(%p) : returning %p\n", This, &This->glDescription);
613 *glDescription = &This->glDescription;
616 /* TODO: think about moving this down to resource? */
617 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
618 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
619 /* 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 */
620 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
621 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
623 return (CONST void*)(This->resource.allocatedMemory);
626 /* Read the framebuffer back into the surface */
627 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
628 IWineD3DSwapChainImpl *swapchain;
629 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
630 BYTE *mem;
631 GLint fmt;
632 GLint type;
633 BYTE *row, *top, *bottom;
634 int i;
635 BOOL bpp;
636 RECT local_rect;
637 BOOL srcIsUpsideDown;
639 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
640 static BOOL warned = FALSE;
641 if(!warned) {
642 ERR("The application tries to lock the render target, but render target locking is disabled\n");
643 warned = TRUE;
645 return;
648 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
649 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
650 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
651 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
652 * context->last_was_blit set on the unlock.
654 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
655 ENTER_GL();
657 /* Select the correct read buffer, and give some debug output.
658 * There is no need to keep track of the current read buffer or reset it, every part of the code
659 * that reads sets the read buffer as desired.
661 if(!swapchain) {
662 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
663 * Read from the back buffer
665 TRACE("Locking offscreen render target\n");
666 glReadBuffer(myDevice->offscreenBuffer);
667 srcIsUpsideDown = TRUE;
668 } else {
669 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
670 TRACE("Locking %#x buffer\n", buffer);
671 glReadBuffer(buffer);
672 checkGLcall("glReadBuffer");
674 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
675 srcIsUpsideDown = FALSE;
678 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
679 if(!rect) {
680 local_rect.left = 0;
681 local_rect.top = 0;
682 local_rect.right = This->currentDesc.Width;
683 local_rect.bottom = This->currentDesc.Height;
684 } else {
685 local_rect = *rect;
687 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
689 switch(This->resource.format)
691 case WINED3DFMT_P8:
693 if(primary_render_target_is_p8(myDevice)) {
694 /* In case of P8 render targets the index is stored in the alpha component */
695 fmt = GL_ALPHA;
696 type = GL_UNSIGNED_BYTE;
697 mem = dest;
698 bpp = This->bytesPerPixel;
699 } else {
700 /* GL can't return palettized data, so read ARGB pixels into a
701 * separate block of memory and convert them into palettized format
702 * in software. Slow, but if the app means to use palettized render
703 * targets and locks it...
705 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
706 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
707 * for the color channels when palettizing the colors.
709 fmt = GL_RGB;
710 type = GL_UNSIGNED_BYTE;
711 pitch *= 3;
712 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
713 if(!mem) {
714 ERR("Out of memory\n");
715 LEAVE_GL();
716 return;
718 bpp = This->bytesPerPixel * 3;
721 break;
723 default:
724 mem = dest;
725 fmt = This->glDescription.glFormat;
726 type = This->glDescription.glType;
727 bpp = This->bytesPerPixel;
730 if(This->Flags & SFLAG_PBO) {
731 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
732 checkGLcall("glBindBufferARB");
735 glReadPixels(local_rect.left, local_rect.top,
736 local_rect.right - local_rect.left,
737 local_rect.bottom - local_rect.top,
738 fmt, type, mem);
739 vcheckGLcall("glReadPixels");
741 if(This->Flags & SFLAG_PBO) {
742 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
743 checkGLcall("glBindBufferARB");
745 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
746 * to get a pointer to it and perform the flipping in software. This is a lot
747 * faster than calling glReadPixels for each line. In case we want more speed
748 * we should rerender it flipped in a FBO and read the data back from the FBO. */
749 if(!srcIsUpsideDown) {
750 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
751 checkGLcall("glBindBufferARB");
753 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
754 checkGLcall("glMapBufferARB");
758 /* TODO: Merge this with the palettization loop below for P8 targets */
759 if(!srcIsUpsideDown) {
760 UINT len, off;
761 /* glReadPixels returns the image upside down, and there is no way to prevent this.
762 Flip the lines in software */
763 len = (local_rect.right - local_rect.left) * bpp;
764 off = local_rect.left * bpp;
766 row = HeapAlloc(GetProcessHeap(), 0, len);
767 if(!row) {
768 ERR("Out of memory\n");
769 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
770 LEAVE_GL();
771 return;
774 top = mem + pitch * local_rect.top;
775 bottom = mem + pitch * ( local_rect.bottom - local_rect.top - 1);
776 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
777 memcpy(row, top + off, len);
778 memcpy(top + off, bottom + off, len);
779 memcpy(bottom + off, row, len);
780 top += pitch;
781 bottom -= pitch;
783 HeapFree(GetProcessHeap(), 0, row);
785 /* Unmap the temp PBO buffer */
786 if(This->Flags & SFLAG_PBO) {
787 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
788 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
792 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
793 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
794 * the same color but we have no choice.
795 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
797 if((This->resource.format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice)) {
798 PALETTEENTRY *pal;
799 DWORD width = pitch / 3;
800 int x, y, c;
801 if(This->palette) {
802 pal = This->palette->palents;
803 } else {
804 pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
807 for(y = local_rect.top; y < local_rect.bottom; y++) {
808 for(x = local_rect.left; x < local_rect.right; x++) {
809 /* start lines pixels */
810 BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
811 BYTE *green = blue + 1;
812 BYTE *red = green + 1;
814 for(c = 0; c < 256; c++) {
815 if(*red == pal[c].peRed &&
816 *green == pal[c].peGreen &&
817 *blue == pal[c].peBlue)
819 *((BYTE *) dest + y * width + x) = c;
820 break;
825 HeapFree(GetProcessHeap(), 0, mem);
827 LEAVE_GL();
830 /* Read the framebuffer contents into a texture */
831 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This)
833 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
834 IWineD3DSwapChainImpl *swapchain;
835 int bpp;
836 GLenum format, internal, type;
837 CONVERT_TYPES convert;
838 BOOL srcIsUpsideDown;
839 GLint prevRead;
841 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
843 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
844 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
845 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
846 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
847 * context->last_was_blit set on the unlock.
849 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
850 surface_bind_and_dirtify(This);
851 ENTER_GL();
853 glGetIntegerv(GL_READ_BUFFER, &prevRead);
855 /* Select the correct read buffer, and give some debug output.
856 * There is no need to keep track of the current read buffer or reset it, every part of the code
857 * that reads sets the read buffer as desired.
859 if(!swapchain) {
860 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
861 * Read from the back buffer
863 TRACE("Locking offscreen render target\n");
864 glReadBuffer(device->offscreenBuffer);
865 srcIsUpsideDown = TRUE;
866 } else {
867 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
868 TRACE("Locking %#x buffer\n", buffer);
869 glReadBuffer(buffer);
870 checkGLcall("glReadBuffer");
872 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
873 srcIsUpsideDown = FALSE;
876 if(!(This->Flags & SFLAG_ALLOCATED)) {
877 surface_allocate_surface(This, internal, This->pow2Width,
878 This->pow2Height, format, type);
881 clear_unused_channels(This);
883 /* If !SrcIsUpsideDown we should flip the surface.
884 * This can be done using glCopyTexSubImage2D but this
885 * is VERY slow, so don't do that. We should prevent
886 * this code from getting called in such cases or perhaps
887 * we can use FBOs */
889 glCopyTexSubImage2D(This->glDescription.target,
890 This->glDescription.level,
891 0, 0, 0, 0,
892 This->currentDesc.Width,
893 This->currentDesc.Height);
894 checkGLcall("glCopyTexSubImage2D");
896 glReadBuffer(prevRead);
897 vcheckGLcall("glReadBuffer");
899 LEAVE_GL();
900 TRACE("Updated target %d\n", This->glDescription.target);
903 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
904 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
905 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
906 * changed
908 if(!(This->Flags & SFLAG_DYNLOCK)) {
909 This->lockCount++;
910 /* MAXLOCKCOUNT is defined in wined3d_private.h */
911 if(This->lockCount > MAXLOCKCOUNT) {
912 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
913 This->Flags |= SFLAG_DYNLOCK;
917 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
918 * Also don't create a PBO for systemmem surfaces.
920 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
921 GLenum error;
922 ENTER_GL();
924 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
925 error = glGetError();
926 if(This->pbo == 0 || error != GL_NO_ERROR) {
927 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
930 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
932 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
933 checkGLcall("glBindBufferARB");
935 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
936 checkGLcall("glBufferDataARB");
938 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
939 checkGLcall("glBindBufferARB");
941 /* We don't need the system memory anymore and we can't even use it for PBOs */
942 if(!(This->Flags & SFLAG_CLIENT)) {
943 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
944 This->resource.heapMemory = NULL;
946 This->resource.allocatedMemory = NULL;
947 This->Flags |= SFLAG_PBO;
948 LEAVE_GL();
949 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
950 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
951 * or a pbo to map
953 if(!This->resource.heapMemory) {
954 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
956 This->resource.allocatedMemory =
957 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
958 if(This->Flags & SFLAG_INSYSMEM) {
959 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
964 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
965 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
966 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
967 IWineD3DSwapChain *swapchain = NULL;
969 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
971 /* This is also done in the base class, but we have to verify this before loading any data from
972 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
973 * may interfere, and all other bad things may happen
975 if (This->Flags & SFLAG_LOCKED) {
976 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
977 return WINED3DERR_INVALIDCALL;
979 This->Flags |= SFLAG_LOCKED;
981 if (!(This->Flags & SFLAG_LOCKABLE))
983 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
986 if (Flags & WINED3DLOCK_DISCARD) {
987 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
988 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
989 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
990 This->Flags |= SFLAG_INSYSMEM;
991 goto lock_end;
994 if (This->Flags & SFLAG_INSYSMEM) {
995 TRACE("Local copy is up to date, not downloading data\n");
996 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
997 goto lock_end;
1000 /* Now download the surface content from opengl
1001 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1002 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
1004 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1005 if(swapchain || iface == myDevice->render_targets[0]) {
1006 const RECT *pass_rect = pRect;
1008 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1009 * because most caller functions do not need that. So do that here
1011 if(pRect &&
1012 pRect->top == 0 &&
1013 pRect->left == 0 &&
1014 pRect->right == This->currentDesc.Width &&
1015 pRect->bottom == This->currentDesc.Height) {
1016 pass_rect = NULL;
1019 switch(wined3d_settings.rendertargetlock_mode) {
1020 case RTL_TEXDRAW:
1021 case RTL_TEXTEX:
1022 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1023 #if 0
1024 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1025 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1026 * This may be faster on some cards
1028 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1029 #endif
1030 /* drop through */
1032 case RTL_AUTO:
1033 case RTL_READDRAW:
1034 case RTL_READTEX:
1035 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
1036 break;
1038 case RTL_DISABLE:
1039 break;
1041 if(swapchain) IWineD3DSwapChain_Release(swapchain);
1043 } else if(iface == myDevice->stencilBufferTarget) {
1044 /** the depth stencil in openGL has a format of GL_FLOAT
1045 * which should be good for WINED3DFMT_D16_LOCKABLE
1046 * and WINED3DFMT_D16
1047 * it is unclear what format the stencil buffer is in except.
1048 * 'Each index is converted to fixed point...
1049 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1050 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1051 * glReadPixels(This->lockedRect.left,
1052 * This->lockedRect.bottom - j - 1,
1053 * This->lockedRect.right - This->lockedRect.left,
1054 * 1,
1055 * GL_DEPTH_COMPONENT,
1056 * type,
1057 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1059 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1060 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1061 * none of that is the case the problem is not in this function :-)
1062 ********************************************/
1063 FIXME("Depth stencil locking not supported yet\n");
1064 } else {
1065 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1066 TRACE("locking an ordinary surface\n");
1067 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1070 lock_end:
1071 if(This->Flags & SFLAG_PBO) {
1072 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1073 ENTER_GL();
1074 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1075 checkGLcall("glBindBufferARB");
1077 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1078 if(This->resource.allocatedMemory) {
1079 ERR("The surface already has PBO memory allocated!\n");
1082 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1083 checkGLcall("glMapBufferARB");
1085 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1086 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1087 checkGLcall("glBindBufferARB");
1089 LEAVE_GL();
1092 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1093 /* Don't dirtify */
1094 } else {
1095 IWineD3DBaseTexture *pBaseTexture;
1097 * Dirtify on lock
1098 * as seen in msdn docs
1100 IWineD3DSurface_AddDirtyRect(iface, pRect);
1102 /** Dirtify Container if needed */
1103 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
1104 TRACE("Making container dirty\n");
1105 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1106 IWineD3DBaseTexture_Release(pBaseTexture);
1107 } else {
1108 TRACE("Surface is standalone, no need to dirty the container\n");
1112 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1115 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1116 GLint prev_store;
1117 GLint prev_rasterpos[4];
1118 GLint skipBytes = 0;
1119 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1120 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1121 IWineD3DSwapChainImpl *swapchain;
1123 /* Activate the correct context for the render target */
1124 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1125 ENTER_GL();
1127 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
1128 if(!swapchain) {
1129 /* Primary offscreen render target */
1130 TRACE("Offscreen render target\n");
1131 glDrawBuffer(myDevice->offscreenBuffer);
1132 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1133 } else {
1134 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1135 TRACE("Unlocking %#x buffer\n", buffer);
1136 glDrawBuffer(buffer);
1137 checkGLcall("glDrawBuffer");
1139 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1142 glFlush();
1143 vcheckGLcall("glFlush");
1144 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1145 vcheckGLcall("glIntegerv");
1146 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1147 vcheckGLcall("glIntegerv");
1148 glPixelZoom(1.0, -1.0);
1149 vcheckGLcall("glPixelZoom");
1151 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1152 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1153 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1155 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1156 vcheckGLcall("glRasterPos2f");
1158 /* Some drivers(radeon dri, others?) don't like exceptions during
1159 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1160 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1161 * catch to put the dib section in InSync mode, which leads to a crash
1162 * and a blocked x server on my radeon card.
1164 * The following lines read the dib section so it is put in inSync mode
1165 * before glDrawPixels is called and the crash is prevented. There won't
1166 * be any interfering gdi accesses, because UnlockRect is called from
1167 * ReleaseDC, and the app won't use the dc any more afterwards.
1169 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1170 volatile BYTE read;
1171 read = This->resource.allocatedMemory[0];
1174 if(This->Flags & SFLAG_PBO) {
1175 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1176 checkGLcall("glBindBufferARB");
1179 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1180 if(This->Flags & SFLAG_LOCKED) {
1181 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1182 (This->lockedRect.bottom - This->lockedRect.top)-1,
1183 fmt, type,
1184 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1185 checkGLcall("glDrawPixels");
1186 } else {
1187 glDrawPixels(This->currentDesc.Width,
1188 This->currentDesc.Height,
1189 fmt, type, mem);
1190 checkGLcall("glDrawPixels");
1193 if(This->Flags & SFLAG_PBO) {
1194 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1195 checkGLcall("glBindBufferARB");
1198 glPixelZoom(1.0,1.0);
1199 vcheckGLcall("glPixelZoom");
1201 glRasterPos3iv(&prev_rasterpos[0]);
1202 vcheckGLcall("glRasterPos3iv");
1204 /* Reset to previous pack row length */
1205 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1206 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1208 if(!swapchain) {
1209 glDrawBuffer(myDevice->offscreenBuffer);
1210 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1211 } else if(swapchain->backBuffer) {
1212 glDrawBuffer(GL_BACK);
1213 checkGLcall("glDrawBuffer(GL_BACK)");
1214 } else {
1215 glDrawBuffer(GL_FRONT);
1216 checkGLcall("glDrawBuffer(GL_FRONT)");
1218 LEAVE_GL();
1220 return;
1223 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1224 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1225 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1226 IWineD3DSwapChainImpl *swapchain = NULL;
1227 BOOL fullsurface;
1229 if (!(This->Flags & SFLAG_LOCKED)) {
1230 WARN("trying to Unlock an unlocked surf@%p\n", This);
1231 return WINED3DERR_INVALIDCALL;
1234 if (This->Flags & SFLAG_PBO) {
1235 TRACE("Freeing PBO memory\n");
1236 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1237 ENTER_GL();
1238 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1239 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1240 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1241 checkGLcall("glUnmapBufferARB");
1242 LEAVE_GL();
1243 This->resource.allocatedMemory = NULL;
1246 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1248 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1249 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1250 goto unlock_end;
1253 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1254 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1255 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1257 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1258 static BOOL warned = FALSE;
1259 if(!warned) {
1260 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1261 warned = TRUE;
1263 goto unlock_end;
1266 if(This->dirtyRect.left == 0 &&
1267 This->dirtyRect.top == 0 &&
1268 This->dirtyRect.right == This->currentDesc.Width &&
1269 This->dirtyRect.bottom == This->currentDesc.Height) {
1270 fullsurface = TRUE;
1271 } else {
1272 /* TODO: Proper partial rectangle tracking */
1273 fullsurface = FALSE;
1274 This->Flags |= SFLAG_INSYSMEM;
1277 switch(wined3d_settings.rendertargetlock_mode) {
1278 case RTL_READTEX:
1279 case RTL_TEXTEX:
1280 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1281 ENTER_GL();
1282 if (This->glDescription.textureName == 0) {
1283 glGenTextures(1, &This->glDescription.textureName);
1284 checkGLcall("glGenTextures");
1286 glBindTexture(This->glDescription.target, This->glDescription.textureName);
1287 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
1288 LEAVE_GL();
1289 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1290 /* drop through */
1292 case RTL_AUTO:
1293 case RTL_READDRAW:
1294 case RTL_TEXDRAW:
1295 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1296 break;
1299 if(!fullsurface) {
1300 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1301 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1302 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1303 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1304 * not fully up to date because only a subrectangle was read in LockRect.
1306 This->Flags &= ~SFLAG_INSYSMEM;
1307 This->Flags |= SFLAG_INDRAWABLE;
1310 This->dirtyRect.left = This->currentDesc.Width;
1311 This->dirtyRect.top = This->currentDesc.Height;
1312 This->dirtyRect.right = 0;
1313 This->dirtyRect.bottom = 0;
1314 } else if(iface == myDevice->stencilBufferTarget) {
1315 FIXME("Depth Stencil buffer locking is not implemented\n");
1316 } else {
1317 /* The rest should be a normal texture */
1318 IWineD3DBaseTextureImpl *impl;
1319 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1320 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1321 * states need resetting
1323 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1324 if(impl->baseTexture.bindCount) {
1325 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1327 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1331 unlock_end:
1332 This->Flags &= ~SFLAG_LOCKED;
1333 memset(&This->lockedRect, 0, sizeof(RECT));
1334 return WINED3D_OK;
1337 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1338 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1339 WINED3DLOCKED_RECT lock;
1340 HRESULT hr;
1341 RGBQUAD col[256];
1343 TRACE("(%p)->(%p)\n",This,pHDC);
1345 if(This->Flags & SFLAG_USERPTR) {
1346 ERR("Not supported on surfaces with an application-provided surfaces\n");
1347 return WINEDDERR_NODC;
1350 /* Give more detailed info for ddraw */
1351 if (This->Flags & SFLAG_DCINUSE)
1352 return WINEDDERR_DCALREADYCREATED;
1354 /* Can't GetDC if the surface is locked */
1355 if (This->Flags & SFLAG_LOCKED)
1356 return WINED3DERR_INVALIDCALL;
1358 memset(&lock, 0, sizeof(lock)); /* To be sure */
1360 /* Create a DIB section if there isn't a hdc yet */
1361 if(!This->hDC) {
1362 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1363 if(This->Flags & SFLAG_CLIENT) {
1364 IWineD3DSurface_PreLoad(iface);
1367 /* Use the dib section from now on if we are not using a PBO */
1368 if(!(This->Flags & SFLAG_PBO))
1369 This->resource.allocatedMemory = This->dib.bitmap_data;
1372 /* Lock the surface */
1373 hr = IWineD3DSurface_LockRect(iface,
1374 &lock,
1375 NULL,
1378 if(This->Flags & SFLAG_PBO) {
1379 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1380 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1383 if(FAILED(hr)) {
1384 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1385 /* keep the dib section */
1386 return hr;
1389 if(This->resource.format == WINED3DFMT_P8 ||
1390 This->resource.format == WINED3DFMT_A8P8) {
1391 unsigned int n;
1392 if(This->palette) {
1393 PALETTEENTRY ent[256];
1395 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1396 for (n=0; n<256; n++) {
1397 col[n].rgbRed = ent[n].peRed;
1398 col[n].rgbGreen = ent[n].peGreen;
1399 col[n].rgbBlue = ent[n].peBlue;
1400 col[n].rgbReserved = 0;
1402 } else {
1403 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1405 for (n=0; n<256; n++) {
1406 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1407 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1408 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1409 col[n].rgbReserved = 0;
1413 SetDIBColorTable(This->hDC, 0, 256, col);
1416 *pHDC = This->hDC;
1417 TRACE("returning %p\n",*pHDC);
1418 This->Flags |= SFLAG_DCINUSE;
1420 return WINED3D_OK;
1423 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1424 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1426 TRACE("(%p)->(%p)\n",This,hDC);
1428 if (!(This->Flags & SFLAG_DCINUSE))
1429 return WINED3DERR_INVALIDCALL;
1431 if (This->hDC !=hDC) {
1432 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1433 return WINED3DERR_INVALIDCALL;
1436 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1437 /* Copy the contents of the DIB over to the PBO */
1438 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1441 /* we locked first, so unlock now */
1442 IWineD3DSurface_UnlockRect(iface);
1444 This->Flags &= ~SFLAG_DCINUSE;
1446 return WINED3D_OK;
1449 /* ******************************************************
1450 IWineD3DSurface Internal (No mapping to directx api) parts follow
1451 ****************************************************** */
1453 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) {
1454 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1455 const GlPixelFormatDesc *glDesc;
1456 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1457 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1459 /* Default values: From the surface */
1460 *format = glDesc->glFormat;
1461 *type = glDesc->glType;
1462 *convert = NO_CONVERSION;
1463 *target_bpp = This->bytesPerPixel;
1465 if(srgb_mode) {
1466 *internal = glDesc->glGammaInternal;
1467 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1468 *internal = glDesc->rtInternal;
1469 } else {
1470 *internal = glDesc->glInternal;
1473 /* Ok, now look if we have to do any conversion */
1474 switch(This->resource.format) {
1475 case WINED3DFMT_P8:
1476 /* ****************
1477 Paletted Texture
1478 **************** */
1480 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1481 * of the two is available make sure texturing is requested as neither of the two works in
1482 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1483 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1484 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1485 * conflicts with this.
1487 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) && primary_render_target_is_p8(device))) || colorkey_active || !use_texturing ) {
1488 *format = GL_RGBA;
1489 *internal = GL_RGBA;
1490 *type = GL_UNSIGNED_BYTE;
1491 *target_bpp = 4;
1492 if(colorkey_active) {
1493 *convert = CONVERT_PALETTED_CK;
1494 } else {
1495 *convert = CONVERT_PALETTED;
1498 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1499 *format = GL_ALPHA;
1500 *internal = GL_RGBA;
1501 *type = GL_UNSIGNED_BYTE;
1502 *target_bpp = 1;
1505 break;
1507 case WINED3DFMT_R3G3B2:
1508 /* **********************
1509 GL_UNSIGNED_BYTE_3_3_2
1510 ********************** */
1511 if (colorkey_active) {
1512 /* This texture format will never be used.. So do not care about color keying
1513 up until the point in time it will be needed :-) */
1514 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1516 break;
1518 case WINED3DFMT_R5G6B5:
1519 if (colorkey_active) {
1520 *convert = CONVERT_CK_565;
1521 *format = GL_RGBA;
1522 *internal = GL_RGBA;
1523 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1525 break;
1527 case WINED3DFMT_X1R5G5B5:
1528 if (colorkey_active) {
1529 *convert = CONVERT_CK_5551;
1530 *format = GL_BGRA;
1531 *internal = GL_RGBA;
1532 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1534 break;
1536 case WINED3DFMT_R8G8B8:
1537 if (colorkey_active) {
1538 *convert = CONVERT_CK_RGB24;
1539 *format = GL_RGBA;
1540 *internal = GL_RGBA;
1541 *type = GL_UNSIGNED_INT_8_8_8_8;
1542 *target_bpp = 4;
1544 break;
1546 case WINED3DFMT_X8R8G8B8:
1547 if (colorkey_active) {
1548 *convert = CONVERT_RGB32_888;
1549 *format = GL_RGBA;
1550 *internal = GL_RGBA;
1551 *type = GL_UNSIGNED_INT_8_8_8_8;
1553 break;
1555 case WINED3DFMT_V8U8:
1556 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1557 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1558 *format = GL_DUDV_ATI;
1559 *internal = GL_DU8DV8_ATI;
1560 *type = GL_BYTE;
1561 /* No conversion - Just change the gl type */
1562 break;
1564 *convert = CONVERT_V8U8;
1565 *format = GL_BGR;
1566 *internal = GL_RGB8;
1567 *type = GL_UNSIGNED_BYTE;
1568 *target_bpp = 3;
1569 break;
1571 case WINED3DFMT_L6V5U5:
1572 *convert = CONVERT_L6V5U5;
1573 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1574 *target_bpp = 3;
1575 /* Use format and types from table */
1576 } else {
1577 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1578 *target_bpp = 2;
1579 *format = GL_RGB;
1580 *internal = GL_RGB5;
1581 *type = GL_UNSIGNED_SHORT_5_6_5;
1583 break;
1585 case WINED3DFMT_X8L8V8U8:
1586 *convert = CONVERT_X8L8V8U8;
1587 *target_bpp = 4;
1588 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1589 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1590 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1591 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1592 * the needed type and format parameter, so the internal format contains a
1593 * 4th component, which is returned as alpha
1595 } else {
1596 /* Not supported by GL_ATI_envmap_bumpmap */
1597 *format = GL_BGRA;
1598 *internal = GL_RGB8;
1599 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1601 break;
1603 case WINED3DFMT_Q8W8V8U8:
1604 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1605 *convert = CONVERT_Q8W8V8U8;
1606 *format = GL_BGRA;
1607 *internal = GL_RGBA8;
1608 *type = GL_UNSIGNED_BYTE;
1609 *target_bpp = 4;
1610 /* Not supported by GL_ATI_envmap_bumpmap */
1611 break;
1613 case WINED3DFMT_V16U16:
1614 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1615 *convert = CONVERT_V16U16;
1616 *format = GL_BGR;
1617 *internal = GL_RGB16_EXT;
1618 *type = GL_UNSIGNED_SHORT;
1619 *target_bpp = 6;
1620 /* What should I do here about GL_ATI_envmap_bumpmap?
1621 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1623 break;
1625 case WINED3DFMT_A4L4:
1626 /* A4L4 exists as an internal gl format, but for some reason there is not
1627 * format+type combination to load it. Thus convert it to A8L8, then load it
1628 * with A4L4 internal, but A8L8 format+type
1630 *convert = CONVERT_A4L4;
1631 *format = GL_LUMINANCE_ALPHA;
1632 *internal = GL_LUMINANCE4_ALPHA4;
1633 *type = GL_UNSIGNED_BYTE;
1634 *target_bpp = 2;
1635 break;
1637 case WINED3DFMT_R32F:
1638 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1639 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1640 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1641 * 1.0 instead.
1643 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1645 *convert = CONVERT_R32F;
1646 *format = GL_RGB;
1647 *internal = GL_RGB32F_ARB;
1648 *type = GL_FLOAT;
1649 *target_bpp = 12;
1650 break;
1652 case WINED3DFMT_R16F:
1653 /* Similar to R32F */
1654 *convert = CONVERT_R16F;
1655 *format = GL_RGB;
1656 *internal = GL_RGB16F_ARB;
1657 *type = GL_HALF_FLOAT_ARB;
1658 *target_bpp = 6;
1659 break;
1661 case WINED3DFMT_G16R16:
1662 *convert = CONVERT_G16R16;
1663 *format = GL_RGB;
1664 *internal = GL_RGB16_EXT;
1665 *type = GL_UNSIGNED_SHORT;
1666 *target_bpp = 6;
1667 break;
1669 default:
1670 break;
1673 return WINED3D_OK;
1676 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1677 BYTE *source, *dest;
1678 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1680 switch (convert) {
1681 case NO_CONVERSION:
1683 memcpy(dst, src, pitch * height);
1684 break;
1686 case CONVERT_PALETTED:
1687 case CONVERT_PALETTED_CK:
1689 IWineD3DPaletteImpl* pal = This->palette;
1690 BYTE table[256][4];
1691 unsigned int x, y;
1693 if( pal == NULL) {
1694 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1697 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1699 for (y = 0; y < height; y++)
1701 source = src + pitch * y;
1702 dest = dst + outpitch * y;
1703 /* This is an 1 bpp format, using the width here is fine */
1704 for (x = 0; x < width; x++) {
1705 BYTE color = *source++;
1706 *dest++ = table[color][0];
1707 *dest++ = table[color][1];
1708 *dest++ = table[color][2];
1709 *dest++ = table[color][3];
1713 break;
1715 case CONVERT_CK_565:
1717 /* Converting the 565 format in 5551 packed to emulate color-keying.
1719 Note : in all these conversion, it would be best to average the averaging
1720 pixels to get the color of the pixel that will be color-keyed to
1721 prevent 'color bleeding'. This will be done later on if ever it is
1722 too visible.
1724 Note2: Nvidia documents say that their driver does not support alpha + color keying
1725 on the same surface and disables color keying in such a case
1727 unsigned int x, y;
1728 WORD *Source;
1729 WORD *Dest;
1731 TRACE("Color keyed 565\n");
1733 for (y = 0; y < height; y++) {
1734 Source = (WORD *) (src + y * pitch);
1735 Dest = (WORD *) (dst + y * outpitch);
1736 for (x = 0; x < width; x++ ) {
1737 WORD color = *Source++;
1738 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1739 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1740 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1741 *Dest |= 0x0001;
1743 Dest++;
1747 break;
1749 case CONVERT_CK_5551:
1751 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1752 unsigned int x, y;
1753 WORD *Source;
1754 WORD *Dest;
1755 TRACE("Color keyed 5551\n");
1756 for (y = 0; y < height; y++) {
1757 Source = (WORD *) (src + y * pitch);
1758 Dest = (WORD *) (dst + y * outpitch);
1759 for (x = 0; x < width; x++ ) {
1760 WORD color = *Source++;
1761 *Dest = color;
1762 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1763 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1764 *Dest |= (1 << 15);
1766 else {
1767 *Dest &= ~(1 << 15);
1769 Dest++;
1773 break;
1775 case CONVERT_V8U8:
1777 unsigned int x, y;
1778 short *Source;
1779 unsigned char *Dest;
1780 for(y = 0; y < height; y++) {
1781 Source = (short *) (src + y * pitch);
1782 Dest = dst + y * outpitch;
1783 for (x = 0; x < width; x++ ) {
1784 long color = (*Source++);
1785 /* B */ Dest[0] = 0xff;
1786 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1787 /* R */ Dest[2] = (color) + 128; /* U */
1788 Dest += 3;
1791 break;
1794 case CONVERT_V16U16:
1796 unsigned int x, y;
1797 DWORD *Source;
1798 unsigned short *Dest;
1799 for(y = 0; y < height; y++) {
1800 Source = (DWORD *) (src + y * pitch);
1801 Dest = (unsigned short *) (dst + y * outpitch);
1802 for (x = 0; x < width; x++ ) {
1803 DWORD color = (*Source++);
1804 /* B */ Dest[0] = 0xffff;
1805 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1806 /* R */ Dest[2] = (color ) + 32768; /* U */
1807 Dest += 3;
1810 break;
1813 case CONVERT_Q8W8V8U8:
1815 unsigned int x, y;
1816 DWORD *Source;
1817 unsigned char *Dest;
1818 for(y = 0; y < height; y++) {
1819 Source = (DWORD *) (src + y * pitch);
1820 Dest = dst + y * outpitch;
1821 for (x = 0; x < width; x++ ) {
1822 long color = (*Source++);
1823 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1824 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1825 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1826 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1827 Dest += 4;
1830 break;
1833 case CONVERT_L6V5U5:
1835 unsigned int x, y;
1836 WORD *Source;
1837 unsigned char *Dest;
1839 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1840 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1841 * fixed function and shaders without further conversion once the surface is
1842 * loaded
1844 for(y = 0; y < height; y++) {
1845 Source = (WORD *) (src + y * pitch);
1846 Dest = dst + y * outpitch;
1847 for (x = 0; x < width; x++ ) {
1848 short color = (*Source++);
1849 unsigned char l = ((color >> 10) & 0xfc);
1850 char v = ((color >> 5) & 0x3e);
1851 char u = ((color ) & 0x1f);
1853 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1854 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1855 * shift. GL reads a signed value and converts it into an unsigned value.
1857 /* M */ Dest[2] = l << 1;
1859 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1860 * from 5 bit values to 8 bit values.
1862 /* V */ Dest[1] = v << 3;
1863 /* U */ Dest[0] = u << 3;
1864 Dest += 3;
1867 } else {
1868 for(y = 0; y < height; y++) {
1869 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1870 Source = (WORD *) (src + y * pitch);
1871 for (x = 0; x < width; x++ ) {
1872 short color = (*Source++);
1873 unsigned char l = ((color >> 10) & 0xfc);
1874 short v = ((color >> 5) & 0x3e);
1875 short u = ((color ) & 0x1f);
1876 short v_conv = v + 16;
1877 short u_conv = u + 16;
1879 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1880 Dest_s += 1;
1884 break;
1887 case CONVERT_X8L8V8U8:
1889 unsigned int x, y;
1890 DWORD *Source;
1891 unsigned char *Dest;
1893 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1894 /* This implementation works with the fixed function pipeline and shaders
1895 * without further modification after converting the surface.
1897 for(y = 0; y < height; y++) {
1898 Source = (DWORD *) (src + y * pitch);
1899 Dest = dst + y * outpitch;
1900 for (x = 0; x < width; x++ ) {
1901 long color = (*Source++);
1902 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1903 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1904 /* U */ Dest[0] = (color & 0xff); /* U */
1905 /* I */ Dest[3] = 255; /* X */
1906 Dest += 4;
1909 } else {
1910 /* Doesn't work correctly with the fixed function pipeline, but can work in
1911 * shaders if the shader is adjusted. (There's no use for this format in gl's
1912 * standard fixed function pipeline anyway).
1914 for(y = 0; y < height; y++) {
1915 Source = (DWORD *) (src + y * pitch);
1916 Dest = dst + y * outpitch;
1917 for (x = 0; x < width; x++ ) {
1918 long color = (*Source++);
1919 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1920 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1921 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1922 Dest += 4;
1926 break;
1929 case CONVERT_A4L4:
1931 unsigned int x, y;
1932 unsigned char *Source;
1933 unsigned char *Dest;
1934 for(y = 0; y < height; y++) {
1935 Source = src + y * pitch;
1936 Dest = dst + y * outpitch;
1937 for (x = 0; x < width; x++ ) {
1938 unsigned char color = (*Source++);
1939 /* A */ Dest[1] = (color & 0xf0) << 0;
1940 /* L */ Dest[0] = (color & 0x0f) << 4;
1941 Dest += 2;
1944 break;
1947 case CONVERT_R32F:
1949 unsigned int x, y;
1950 float *Source;
1951 float *Dest;
1952 for(y = 0; y < height; y++) {
1953 Source = (float *) (src + y * pitch);
1954 Dest = (float *) (dst + y * outpitch);
1955 for (x = 0; x < width; x++ ) {
1956 float color = (*Source++);
1957 Dest[0] = color;
1958 Dest[1] = 1.0;
1959 Dest[2] = 1.0;
1960 Dest += 3;
1963 break;
1966 case CONVERT_R16F:
1968 unsigned int x, y;
1969 WORD *Source;
1970 WORD *Dest;
1971 WORD one = 0x3c00;
1972 for(y = 0; y < height; y++) {
1973 Source = (WORD *) (src + y * pitch);
1974 Dest = (WORD *) (dst + y * outpitch);
1975 for (x = 0; x < width; x++ ) {
1976 WORD color = (*Source++);
1977 Dest[0] = color;
1978 Dest[1] = one;
1979 Dest[2] = one;
1980 Dest += 3;
1983 break;
1986 case CONVERT_G16R16:
1988 unsigned int x, y;
1989 WORD *Source;
1990 WORD *Dest;
1992 for(y = 0; y < height; y++) {
1993 Source = (WORD *) (src + y * pitch);
1994 Dest = (WORD *) (dst + y * outpitch);
1995 for (x = 0; x < width; x++ ) {
1996 WORD green = (*Source++);
1997 WORD red = (*Source++);
1998 Dest[0] = green;
1999 Dest[1] = red;
2000 Dest[2] = 0xffff;
2001 Dest += 3;
2004 break;
2007 default:
2008 ERR("Unsupported conversation type %d\n", convert);
2010 return WINED3D_OK;
2013 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2014 IWineD3DPaletteImpl* pal = This->palette;
2015 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2016 BOOL index_in_alpha = FALSE;
2017 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2018 int i;
2020 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2021 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2022 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2023 * duplicate entries. Store the color key in the unused alpha component to speed the
2024 * download up and to make conversion unneeded. */
2025 index_in_alpha = primary_render_target_is_p8(device);
2027 if (pal == NULL) {
2028 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2029 if(dxVersion <= 7) {
2030 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2031 return;
2034 /* Still no palette? Use the device's palette */
2035 /* can ddraw and d3d < 8 surfaces use device's palette (d3d >= 8 feature)? */
2036 for (i = 0; i < 256; i++) {
2037 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2038 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2039 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2040 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2041 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2042 capability flag is present (wine does advertise this capability) */
2043 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2045 } else {
2046 TRACE("Using surface palette %p\n", pal);
2047 /* Get the surface's palette */
2048 for (i = 0; i < 256; i++) {
2049 table[i][0] = pal->palents[i].peRed;
2050 table[i][1] = pal->palents[i].peGreen;
2051 table[i][2] = pal->palents[i].peBlue;
2053 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2054 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2055 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2056 of pixels that should be masked away is set to 0. */
2057 if(index_in_alpha) {
2058 table[i][3] = i;
2059 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2060 table[i][3] = 0x00;
2061 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2062 table[i][3] = pal->palents[i].peFlags;
2063 } else {
2064 table[i][3] = 0xFF;
2070 const char *fragment_palette_conversion =
2071 "!!ARBfp1.0\n"
2072 "TEMP index;\n"
2073 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
2074 "TEX index, fragment.texcoord[0], texture[0], 2D;\n" /* The alpha-component contains the palette index */
2075 "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 */
2076 "TEX result.color, index.a, texture[1], 1D;\n" /* use the alpha-component as a index in the palette to get the final color */
2077 "END";
2079 /* This function is used in case of 8bit paletted textures to upload the palette.
2080 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2081 extensions like ATI_fragment_shaders is possible.
2083 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2084 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2085 BYTE table[256][4];
2086 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2088 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2090 /* Try to use the paletted texture extension */
2091 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2093 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2094 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2096 else
2098 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2099 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2100 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2102 /* Create the fragment program if we don't have it */
2103 if(!device->paletteConversionShader)
2105 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2106 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2107 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2108 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2109 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2112 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2113 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2115 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2116 glEnable(GL_TEXTURE_1D);
2117 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2119 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2120 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2121 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2122 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2124 /* Switch back to unit 0 in which the 2D texture will be stored. */
2125 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2127 /* Rebind the texture because it isn't bound anymore */
2128 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2132 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2133 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2135 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2136 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2137 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2139 return FALSE;
2142 if(This->palette9) {
2143 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2144 return FALSE;
2146 } else {
2147 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2149 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2150 return TRUE;
2153 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2154 GLboolean oldwrite[4];
2156 /* Some formats have only some color channels, and the others are 1.0.
2157 * since our rendering renders to all channels, and those pixel formats
2158 * are emulated by using a full texture with the other channels set to 1.0
2159 * manually, clear the unused channels.
2161 * This could be done with hacking colorwriteenable to mask the colors,
2162 * but before drawing the buffer would have to be cleared too, so there's
2163 * no gain in that
2165 switch(This->resource.format) {
2166 case WINED3DFMT_R16F:
2167 case WINED3DFMT_R32F:
2168 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2169 /* Do not activate a context, the correct drawable is active already
2170 * though just the read buffer is set, make sure to have the correct draw
2171 * buffer too
2173 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2174 glDisable(GL_SCISSOR_TEST);
2175 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2176 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2177 glClearColor(0.0, 1.0, 1.0, 1.0);
2178 glClear(GL_COLOR_BUFFER_BIT);
2179 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2180 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2181 checkGLcall("Unused channel clear\n");
2182 break;
2184 default: break;
2188 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2189 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2191 if (!(This->Flags & SFLAG_INTEXTURE)) {
2192 TRACE("Reloading because surface is dirty\n");
2193 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2194 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2195 /* Reload: vice versa OR */
2196 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2197 /* Also reload: Color key is active AND the color key has changed */
2198 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2199 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2200 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2201 TRACE("Reloading because of color keying\n");
2202 /* To perform the color key conversion we need a sysmem copy of
2203 * the surface. Make sure we have it
2206 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2207 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2208 /* TODO: This is not necessarily needed with hw palettized texture support */
2209 This->Flags &= ~SFLAG_INTEXTURE;
2210 } else if(palette9_changed(This)) {
2211 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2212 /* TODO: This is not necessarily needed with hw palettized texture support */
2213 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2215 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2216 This->Flags &= ~SFLAG_INTEXTURE;
2217 } else {
2218 TRACE("surface is already in texture\n");
2219 return WINED3D_OK;
2222 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2223 * These resources are not bound by device size or format restrictions. Because of this,
2224 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2225 * However, these resources can always be created, locked, and copied.
2227 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2229 FIXME("(%p) Operation not supported for scratch textures\n",This);
2230 return WINED3DERR_INVALIDCALL;
2233 This->srgb = srgb_mode;
2234 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2236 #if 0
2238 static unsigned int gen = 0;
2239 char buffer[4096];
2240 ++gen;
2241 if ((gen % 10) == 0) {
2242 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2243 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2246 * debugging crash code
2247 if (gen == 250) {
2248 void** test = NULL;
2249 *test = 0;
2253 #endif
2255 if (!(This->Flags & SFLAG_DONOTFREE)) {
2256 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2257 This->resource.allocatedMemory = NULL;
2258 This->resource.heapMemory = NULL;
2259 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2262 return WINED3D_OK;
2265 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2266 /* TODO: check for locks */
2267 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2268 IWineD3DBaseTexture *baseTexture = NULL;
2269 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2271 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2272 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2273 TRACE("Passing to container\n");
2274 IWineD3DBaseTexture_BindTexture(baseTexture);
2275 IWineD3DBaseTexture_Release(baseTexture);
2276 } else {
2277 TRACE("(%p) : Binding surface\n", This);
2279 if(!device->isInDraw) {
2280 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2282 ENTER_GL();
2283 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2284 LEAVE_GL();
2286 return;
2289 #include <errno.h>
2290 #include <stdio.h>
2291 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2292 FILE* f = NULL;
2293 UINT i, y;
2294 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2295 char *allocatedMemory;
2296 char *textureRow;
2297 IWineD3DSwapChain *swapChain = NULL;
2298 int width, height;
2299 GLuint tmpTexture = 0;
2300 DWORD color;
2301 /*FIXME:
2302 Textures may not be stored in ->allocatedgMemory and a GlTexture
2303 so we should lock the surface before saving a snapshot, or at least check that
2305 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2306 by calling GetTexImage and in compressed form by calling
2307 GetCompressedTexImageARB. Queried compressed images can be saved and
2308 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2309 texture images do not need to be processed by the GL and should
2310 significantly improve texture loading performance relative to uncompressed
2311 images. */
2313 /* Setup the width and height to be the internal texture width and height. */
2314 width = This->pow2Width;
2315 height = This->pow2Height;
2316 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2317 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2319 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2320 /* if were not a real texture then read the back buffer into a real texture */
2321 /* we don't want to interfere with the back buffer so read the data into a temporary
2322 * texture and then save the data out of the temporary texture
2324 GLint prevRead;
2325 ENTER_GL();
2326 TRACE("(%p) Reading render target into texture\n", This);
2327 glEnable(GL_TEXTURE_2D);
2329 glGenTextures(1, &tmpTexture);
2330 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2332 glTexImage2D(GL_TEXTURE_2D,
2334 GL_RGBA,
2335 width,
2336 height,
2337 0/*border*/,
2338 GL_RGBA,
2339 GL_UNSIGNED_INT_8_8_8_8_REV,
2340 NULL);
2342 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2343 vcheckGLcall("glGetIntegerv");
2344 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2345 vcheckGLcall("glReadBuffer");
2346 glCopyTexImage2D(GL_TEXTURE_2D,
2348 GL_RGBA,
2351 width,
2352 height,
2355 checkGLcall("glCopyTexImage2D");
2356 glReadBuffer(prevRead);
2357 LEAVE_GL();
2359 } else { /* bind the real texture, and make sure it up to date */
2360 IWineD3DSurface_PreLoad(iface);
2362 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2363 ENTER_GL();
2364 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2365 glGetTexImage(GL_TEXTURE_2D,
2366 This->glDescription.level,
2367 GL_RGBA,
2368 GL_UNSIGNED_INT_8_8_8_8_REV,
2369 allocatedMemory);
2370 checkGLcall("glTexImage2D");
2371 if (tmpTexture) {
2372 glBindTexture(GL_TEXTURE_2D, 0);
2373 glDeleteTextures(1, &tmpTexture);
2375 LEAVE_GL();
2377 f = fopen(filename, "w+");
2378 if (NULL == f) {
2379 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2380 return WINED3DERR_INVALIDCALL;
2382 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2383 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2384 /* TGA header */
2385 fputc(0,f);
2386 fputc(0,f);
2387 fputc(2,f);
2388 fputc(0,f);
2389 fputc(0,f);
2390 fputc(0,f);
2391 fputc(0,f);
2392 fputc(0,f);
2393 fputc(0,f);
2394 fputc(0,f);
2395 fputc(0,f);
2396 fputc(0,f);
2397 /* short width*/
2398 fwrite(&width,2,1,f);
2399 /* short height */
2400 fwrite(&height,2,1,f);
2401 /* format rgba */
2402 fputc(0x20,f);
2403 fputc(0x28,f);
2404 /* raw data */
2405 /* 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 */
2406 if(swapChain)
2407 textureRow = allocatedMemory + (width * (height - 1) *4);
2408 else
2409 textureRow = allocatedMemory;
2410 for (y = 0 ; y < height; y++) {
2411 for (i = 0; i < width; i++) {
2412 color = *((DWORD*)textureRow);
2413 fputc((color >> 16) & 0xFF, f); /* B */
2414 fputc((color >> 8) & 0xFF, f); /* G */
2415 fputc((color >> 0) & 0xFF, f); /* R */
2416 fputc((color >> 24) & 0xFF, f); /* A */
2417 textureRow += 4;
2419 /* take two rows of the pointer to the texture memory */
2420 if(swapChain)
2421 (textureRow-= width << 3);
2424 TRACE("Closing file\n");
2425 fclose(f);
2427 if(swapChain) {
2428 IWineD3DSwapChain_Release(swapChain);
2430 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2431 return WINED3D_OK;
2435 * Slightly inefficient way to handle multiple dirty rects but it works :)
2437 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2438 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2439 IWineD3DBaseTexture *baseTexture = NULL;
2441 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2442 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2444 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2445 if (NULL != pDirtyRect) {
2446 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2447 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2448 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2449 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2450 } else {
2451 This->dirtyRect.left = 0;
2452 This->dirtyRect.top = 0;
2453 This->dirtyRect.right = This->currentDesc.Width;
2454 This->dirtyRect.bottom = This->currentDesc.Height;
2456 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2457 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2458 /* if the container is a basetexture then mark it dirty. */
2459 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2460 TRACE("Passing to container\n");
2461 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2462 IWineD3DBaseTexture_Release(baseTexture);
2464 return WINED3D_OK;
2467 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2468 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2469 HRESULT hr;
2470 const GlPixelFormatDesc *glDesc;
2471 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2473 TRACE("(%p) : Calling base function first\n", This);
2474 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2475 if(SUCCEEDED(hr)) {
2476 /* Setup some glformat defaults */
2477 This->glDescription.glFormat = glDesc->glFormat;
2478 This->glDescription.glFormatInternal = glDesc->glInternal;
2479 This->glDescription.glType = glDesc->glType;
2481 This->Flags &= ~SFLAG_ALLOCATED;
2482 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2483 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2485 return hr;
2488 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2489 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2491 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2492 WARN("Surface is locked or the HDC is in use\n");
2493 return WINED3DERR_INVALIDCALL;
2496 if(Mem && Mem != This->resource.allocatedMemory) {
2497 void *release = NULL;
2499 /* Do I have to copy the old surface content? */
2500 if(This->Flags & SFLAG_DIBSECTION) {
2501 /* Release the DC. No need to hold the critical section for the update
2502 * Thread because this thread runs only on front buffers, but this method
2503 * fails for render targets in the check above.
2505 SelectObject(This->hDC, This->dib.holdbitmap);
2506 DeleteDC(This->hDC);
2507 /* Release the DIB section */
2508 DeleteObject(This->dib.DIBsection);
2509 This->dib.bitmap_data = NULL;
2510 This->resource.allocatedMemory = NULL;
2511 This->hDC = NULL;
2512 This->Flags &= ~SFLAG_DIBSECTION;
2513 } else if(!(This->Flags & SFLAG_USERPTR)) {
2514 release = This->resource.heapMemory;
2515 This->resource.heapMemory = NULL;
2517 This->resource.allocatedMemory = Mem;
2518 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2520 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2521 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2523 /* For client textures opengl has to be notified */
2524 if(This->Flags & SFLAG_CLIENT) {
2525 This->Flags &= ~SFLAG_ALLOCATED;
2526 IWineD3DSurface_PreLoad(iface);
2527 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2530 /* Now free the old memory if any */
2531 HeapFree(GetProcessHeap(), 0, release);
2532 } else if(This->Flags & SFLAG_USERPTR) {
2533 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2534 This->resource.allocatedMemory = NULL;
2535 /* HeapMemory should be NULL already */
2536 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2537 This->Flags &= ~SFLAG_USERPTR;
2539 if(This->Flags & SFLAG_CLIENT) {
2540 This->Flags &= ~SFLAG_ALLOCATED;
2541 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2542 IWineD3DSurface_PreLoad(iface);
2545 return WINED3D_OK;
2548 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2549 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2550 IWineD3DSwapChainImpl *swapchain = NULL;
2551 HRESULT hr;
2552 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2554 /* Flipping is only supported on RenderTargets */
2555 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2557 if(override) {
2558 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2559 * FIXME("(%p) Target override is not supported by now\n", This);
2560 * Additionally, it isn't really possible to support triple-buffering
2561 * properly on opengl at all
2565 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2566 if(!swapchain) {
2567 ERR("Flipped surface is not on a swapchain\n");
2568 return WINEDDERR_NOTFLIPPABLE;
2571 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2572 * and only d3d8 and d3d9 apps specify the presentation interval
2574 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2575 /* Most common case first to avoid wasting time on all the other cases */
2576 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2577 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2578 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2579 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2580 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2581 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2582 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2583 } else {
2584 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2587 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2588 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2589 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2590 return hr;
2593 /* Does a direct frame buffer -> texture copy. Stretching is done
2594 * with single pixel copy calls
2596 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2597 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2598 float xrel, yrel;
2599 UINT row;
2600 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2603 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2604 ENTER_GL();
2605 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2607 /* TODO: Do we need GL_TEXTURE_2D enabled fpr copyteximage? */
2608 glEnable(This->glDescription.target);
2609 checkGLcall("glEnable(This->glDescription.target)");
2611 /* Bind the target texture */
2612 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2613 checkGLcall("glBindTexture");
2614 if(!swapchain) {
2615 glReadBuffer(myDevice->offscreenBuffer);
2616 } else {
2617 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2618 glReadBuffer(buffer);
2620 checkGLcall("glReadBuffer");
2622 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2623 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2625 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2626 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2628 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2629 ERR("Texture filtering not supported in direct blit\n");
2631 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2632 ERR("Texture filtering not supported in direct blit\n");
2635 if(upsidedown &&
2636 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2637 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2638 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2640 glCopyTexSubImage2D(This->glDescription.target,
2641 This->glDescription.level,
2642 drect->x1, drect->y1, /* xoffset, yoffset */
2643 srect->x1, Src->currentDesc.Height - srect->y2,
2644 drect->x2 - drect->x1, drect->y2 - drect->y1);
2645 } else {
2646 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2647 /* I have to process this row by row to swap the image,
2648 * otherwise it would be upside down, so stretching in y direction
2649 * doesn't cost extra time
2651 * However, stretching in x direction can be avoided if not necessary
2653 for(row = drect->y1; row < drect->y2; row++) {
2654 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2655 /* Well, that stuff works, but it's very slow.
2656 * find a better way instead
2658 UINT col;
2660 for(col = drect->x1; col < drect->x2; col++) {
2661 glCopyTexSubImage2D(This->glDescription.target,
2662 This->glDescription.level,
2663 drect->x1 + col, row, /* xoffset, yoffset */
2664 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2665 1, 1);
2667 } else {
2668 glCopyTexSubImage2D(This->glDescription.target,
2669 This->glDescription.level,
2670 drect->x1, row, /* xoffset, yoffset */
2671 srect->x1, yoffset - (int) (row * yrel),
2672 drect->x2-drect->x1, 1);
2676 vcheckGLcall("glCopyTexSubImage2D");
2678 /* Leave the opengl state valid for blitting */
2679 glDisable(This->glDescription.target);
2680 checkGLcall("glDisable(This->glDescription.target)");
2682 LEAVE_GL();
2685 /* Uses the hardware to stretch and flip the image */
2686 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2687 GLuint src, backup = 0;
2688 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2689 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2690 float left, right, top, bottom; /* Texture coordinates */
2691 UINT fbwidth = Src->currentDesc.Width;
2692 UINT fbheight = Src->currentDesc.Height;
2693 GLenum drawBuffer = GL_BACK;
2694 GLenum texture_target;
2696 TRACE("Using hwstretch blit\n");
2697 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2698 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2699 ENTER_GL();
2701 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2703 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2704 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2706 if(GL_LIMITS(aux_buffers) >= 2) {
2707 /* Got more than one aux buffer? Use the 2nd aux buffer */
2708 drawBuffer = GL_AUX1;
2709 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2710 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2711 drawBuffer = GL_AUX0;
2714 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2715 glGenTextures(1, &backup);
2716 checkGLcall("glGenTextures\n");
2717 glBindTexture(GL_TEXTURE_2D, backup);
2718 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2719 texture_target = GL_TEXTURE_2D;
2720 } else {
2721 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2722 * we are reading from the back buffer, the backup can be used as source texture
2724 if(Src->glDescription.textureName == 0) {
2725 /* Get it a description */
2726 IWineD3DSurface_PreLoad(SrcSurface);
2728 texture_target = Src->glDescription.target;
2729 glBindTexture(texture_target, Src->glDescription.textureName);
2730 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2731 glEnable(texture_target);
2732 checkGLcall("glEnable(texture_target)");
2734 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2735 Src->Flags &= ~SFLAG_INTEXTURE;
2738 glReadBuffer(GL_BACK);
2739 checkGLcall("glReadBuffer(GL_BACK)");
2741 /* TODO: Only back up the part that will be overwritten */
2742 glCopyTexSubImage2D(texture_target, 0,
2743 0, 0 /* read offsets */,
2744 0, 0,
2745 fbwidth,
2746 fbheight);
2748 checkGLcall("glCopyTexSubImage2D");
2750 /* No issue with overriding these - the sampler is dirty due to blit usage */
2751 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2752 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2753 checkGLcall("glTexParameteri");
2754 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2755 minMipLookup[Filter][WINED3DTEXF_NONE]);
2756 checkGLcall("glTexParameteri");
2758 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2759 src = backup ? backup : Src->glDescription.textureName;
2760 } else {
2761 glReadBuffer(GL_FRONT);
2762 checkGLcall("glReadBuffer(GL_FRONT)");
2764 glGenTextures(1, &src);
2765 checkGLcall("glGenTextures(1, &src)");
2766 glBindTexture(GL_TEXTURE_2D, src);
2767 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2769 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2770 * out for power of 2 sizes
2772 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2773 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2774 checkGLcall("glTexImage2D");
2775 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2776 0, 0 /* read offsets */,
2777 0, 0,
2778 fbwidth,
2779 fbheight);
2781 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2782 checkGLcall("glTexParameteri");
2783 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2784 checkGLcall("glTexParameteri");
2786 glReadBuffer(GL_BACK);
2787 checkGLcall("glReadBuffer(GL_BACK)");
2789 if(texture_target != GL_TEXTURE_2D) {
2790 glDisable(texture_target);
2791 glEnable(GL_TEXTURE_2D);
2792 texture_target = GL_TEXTURE_2D;
2795 checkGLcall("glEnd and previous");
2797 left = (float) srect->x1 / (float) Src->pow2Width;
2798 right = (float) srect->x2 / (float) Src->pow2Width;
2800 if(upsidedown) {
2801 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2802 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2803 } else {
2804 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2805 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2808 /* draw the source texture stretched and upside down. The correct surface is bound already */
2809 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
2810 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
2812 glDrawBuffer(drawBuffer);
2813 glReadBuffer(drawBuffer);
2815 glBegin(GL_QUADS);
2816 /* bottom left */
2817 glTexCoord2f(left, bottom);
2818 glVertex2i(0, fbheight);
2820 /* top left */
2821 glTexCoord2f(left, top);
2822 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2824 /* top right */
2825 glTexCoord2f(right, top);
2826 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2828 /* bottom right */
2829 glTexCoord2f(right, bottom);
2830 glVertex2i(drect->x2 - drect->x1, fbheight);
2831 glEnd();
2832 checkGLcall("glEnd and previous");
2834 if(texture_target != This->glDescription.target) {
2835 glDisable(texture_target);
2836 glEnable(This->glDescription.target);
2837 texture_target = This->glDescription.target;
2840 /* Now read the stretched and upside down image into the destination texture */
2841 glBindTexture(texture_target, This->glDescription.textureName);
2842 checkGLcall("glBindTexture");
2843 glCopyTexSubImage2D(texture_target,
2845 drect->x1, drect->y1, /* xoffset, yoffset */
2846 0, 0, /* We blitted the image to the origin */
2847 drect->x2 - drect->x1, drect->y2 - drect->y1);
2848 checkGLcall("glCopyTexSubImage2D");
2850 if(drawBuffer == GL_BACK) {
2851 /* Write the back buffer backup back */
2852 if(backup) {
2853 if(texture_target != GL_TEXTURE_2D) {
2854 glDisable(texture_target);
2855 glEnable(GL_TEXTURE_2D);
2856 texture_target = GL_TEXTURE_2D;
2858 glBindTexture(GL_TEXTURE_2D, backup);
2859 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
2860 } else {
2861 if(texture_target != Src->glDescription.target) {
2862 glDisable(texture_target);
2863 glEnable(Src->glDescription.target);
2864 texture_target = Src->glDescription.target;
2866 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
2867 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2870 glBegin(GL_QUADS);
2871 /* top left */
2872 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2873 glVertex2i(0, 0);
2875 /* bottom left */
2876 glTexCoord2f(0.0, 0.0);
2877 glVertex2i(0, fbheight);
2879 /* bottom right */
2880 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2881 glVertex2i(fbwidth, Src->currentDesc.Height);
2883 /* top right */
2884 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2885 glVertex2i(fbwidth, 0);
2886 glEnd();
2887 } else {
2888 /* Restore the old draw buffer */
2889 glDrawBuffer(GL_BACK);
2891 glDisable(texture_target);
2892 checkGLcall("glDisable(texture_target)");
2894 /* Cleanup */
2895 if(src != Src->glDescription.textureName && src != backup) {
2896 glDeleteTextures(1, &src);
2897 checkGLcall("glDeleteTextures(1, &src)");
2899 if(backup) {
2900 glDeleteTextures(1, &backup);
2901 checkGLcall("glDeleteTextures(1, &backup)");
2904 LEAVE_GL();
2907 /* Not called from the VTable */
2908 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2909 WINED3DRECT rect;
2910 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2911 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2912 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2914 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2916 /* Get the swapchain. One of the surfaces has to be a primary surface */
2917 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2918 WARN("Destination is in sysmem, rejecting gl blt\n");
2919 return WINED3DERR_INVALIDCALL;
2921 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2922 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2923 if(Src) {
2924 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2925 WARN("Src is in sysmem, rejecting gl blt\n");
2926 return WINED3DERR_INVALIDCALL;
2928 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2929 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2932 /* Early sort out of cases where no render target is used */
2933 if(!dstSwapchain && !srcSwapchain &&
2934 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2935 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2936 return WINED3DERR_INVALIDCALL;
2939 /* No destination color keying supported */
2940 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2941 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2942 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2943 return WINED3DERR_INVALIDCALL;
2946 if (DestRect) {
2947 rect.x1 = DestRect->left;
2948 rect.y1 = DestRect->top;
2949 rect.x2 = DestRect->right;
2950 rect.y2 = DestRect->bottom;
2951 } else {
2952 rect.x1 = 0;
2953 rect.y1 = 0;
2954 rect.x2 = This->currentDesc.Width;
2955 rect.y2 = This->currentDesc.Height;
2958 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2959 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2960 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2961 /* Half-life does a Blt from the back buffer to the front buffer,
2962 * Full surface size, no flags... Use present instead
2964 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2967 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2968 while(1)
2970 RECT mySrcRect;
2971 TRACE("Looking if a Present can be done...\n");
2972 /* Source Rectangle must be full surface */
2973 if( SrcRect ) {
2974 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2975 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2976 TRACE("No, Source rectangle doesn't match\n");
2977 break;
2980 mySrcRect.left = 0;
2981 mySrcRect.top = 0;
2982 mySrcRect.right = Src->currentDesc.Width;
2983 mySrcRect.bottom = Src->currentDesc.Height;
2985 /* No stretching may occur */
2986 if(mySrcRect.right != rect.x2 - rect.x1 ||
2987 mySrcRect.bottom != rect.y2 - rect.y1) {
2988 TRACE("No, stretching is done\n");
2989 break;
2992 /* Destination must be full surface or match the clipping rectangle */
2993 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
2995 RECT cliprect;
2996 POINT pos[2];
2997 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
2998 pos[0].x = rect.x1;
2999 pos[0].y = rect.y1;
3000 pos[1].x = rect.x2;
3001 pos[1].y = rect.y2;
3002 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3003 pos, 2);
3005 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3006 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3008 TRACE("No, dest rectangle doesn't match(clipper)\n");
3009 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3010 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3011 break;
3014 else
3016 if(rect.x1 != 0 || rect.y1 != 0 ||
3017 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3018 TRACE("No, dest rectangle doesn't match(surface size)\n");
3019 break;
3023 TRACE("Yes\n");
3025 /* These flags are unimportant for the flag check, remove them */
3026 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3027 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3029 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3030 * take very long, while a flip is fast.
3031 * This applies to Half-Life, which does such Blts every time it finished
3032 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3033 * menu. This is also used by all apps when they do windowed rendering
3035 * The problem is that flipping is not really the same as copying. After a
3036 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3037 * untouched. Therefore it's necessary to override the swap effect
3038 * and to set it back after the flip.
3040 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3041 * testcases.
3044 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3045 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3047 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3048 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3050 dstSwapchain->presentParms.SwapEffect = orig_swap;
3052 return WINED3D_OK;
3054 break;
3057 TRACE("Unsupported blit between buffers on the same swapchain\n");
3058 return WINED3DERR_INVALIDCALL;
3059 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3060 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3061 return WINED3DERR_INVALIDCALL;
3062 } else if(dstSwapchain && srcSwapchain) {
3063 FIXME("Implement hardware blit between two different swapchains\n");
3064 return WINED3DERR_INVALIDCALL;
3065 } else if(dstSwapchain) {
3066 if(SrcSurface == myDevice->render_targets[0]) {
3067 TRACE("Blit from active render target to a swapchain\n");
3068 /* Handled with regular texture -> swapchain blit */
3070 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3071 FIXME("Implement blit from a swapchain to the active render target\n");
3072 return WINED3DERR_INVALIDCALL;
3075 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3076 /* Blit from render target to texture */
3077 WINED3DRECT srect;
3078 BOOL upsideDown, stretchx;
3080 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3081 TRACE("Color keying not supported by frame buffer to texture blit\n");
3082 return WINED3DERR_INVALIDCALL;
3083 /* Destination color key is checked above */
3086 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3087 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3089 if(SrcRect) {
3090 if(SrcRect->top < SrcRect->bottom) {
3091 srect.y1 = SrcRect->top;
3092 srect.y2 = SrcRect->bottom;
3093 upsideDown = FALSE;
3094 } else {
3095 srect.y1 = SrcRect->bottom;
3096 srect.y2 = SrcRect->top;
3097 upsideDown = TRUE;
3099 srect.x1 = SrcRect->left;
3100 srect.x2 = SrcRect->right;
3101 } else {
3102 srect.x1 = 0;
3103 srect.y1 = 0;
3104 srect.x2 = Src->currentDesc.Width;
3105 srect.y2 = Src->currentDesc.Height;
3106 upsideDown = FALSE;
3108 if(rect.x1 > rect.x2) {
3109 UINT tmp = rect.x2;
3110 rect.x2 = rect.x1;
3111 rect.x1 = tmp;
3112 upsideDown = !upsideDown;
3114 if(!srcSwapchain) {
3115 TRACE("Reading from an offscreen target\n");
3116 upsideDown = !upsideDown;
3119 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3120 stretchx = TRUE;
3121 } else {
3122 stretchx = FALSE;
3125 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3126 * flip the image nor scale it.
3128 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3129 * -> If the app wants a image width an unscaled width, copy it line per line
3130 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3131 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3132 * back buffer. This is slower than reading line per line, thus not used for flipping
3133 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3134 * pixel by pixel
3136 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3137 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3138 * backends.
3140 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3141 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3142 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3143 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3144 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3145 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3146 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3147 } else {
3148 TRACE("Using hardware stretching to flip / stretch the texture\n");
3149 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3152 if(!(This->Flags & SFLAG_DONOTFREE)) {
3153 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3154 This->resource.allocatedMemory = NULL;
3155 This->resource.heapMemory = NULL;
3156 } else {
3157 This->Flags &= ~SFLAG_INSYSMEM;
3159 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3160 * path is never entered
3162 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3164 return WINED3D_OK;
3165 } else if(Src) {
3166 /* Blit from offscreen surface to render target */
3167 float glTexCoord[4];
3168 DWORD oldCKeyFlags = Src->CKeyFlags;
3169 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
3170 RECT SourceRectangle;
3171 BOOL paletteOverride = FALSE;
3173 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3175 if(SrcRect) {
3176 SourceRectangle.left = SrcRect->left;
3177 SourceRectangle.right = SrcRect->right;
3178 SourceRectangle.top = SrcRect->top;
3179 SourceRectangle.bottom = SrcRect->bottom;
3180 } else {
3181 SourceRectangle.left = 0;
3182 SourceRectangle.right = Src->currentDesc.Width;
3183 SourceRectangle.top = 0;
3184 SourceRectangle.bottom = Src->currentDesc.Height;
3186 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3187 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3188 TRACE("Using stretch_rect_fbo\n");
3189 /* The source is always a texture, but never the currently active render target, and the texture
3190 * contents are never upside down
3192 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3193 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3194 return WINED3D_OK;
3197 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3198 /* Fall back to software */
3199 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3200 SourceRectangle.left, SourceRectangle.top,
3201 SourceRectangle.right, SourceRectangle.bottom);
3202 return WINED3DERR_INVALIDCALL;
3205 /* Color keying: Check if we have to do a color keyed blt,
3206 * and if not check if a color key is activated.
3208 * Just modify the color keying parameters in the surface and restore them afterwards
3209 * The surface keeps track of the color key last used to load the opengl surface.
3210 * PreLoad will catch the change to the flags and color key and reload if necessary.
3212 if(Flags & WINEDDBLT_KEYSRC) {
3213 /* Use color key from surface */
3214 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3215 /* Use color key from DDBltFx */
3216 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3217 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3218 } else {
3219 /* Do not use color key */
3220 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3223 /* When blitting from an offscreen surface to a rendertarget, the source
3224 * surface is not required to have a palette. Our rendering / conversion
3225 * code further down the road retrieves the palette from the surface, so
3226 * it must have a palette set. */
3227 if((Src->resource.format == WINED3DFMT_P8) && (Src->palette == NULL)) {
3228 paletteOverride = TRUE;
3229 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3230 Src->palette = This->palette;
3233 /* Now load the surface */
3234 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3237 /* Activate the destination context, set it up for blitting */
3238 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3239 ENTER_GL();
3241 glEnable(Src->glDescription.target);
3242 checkGLcall("glEnable(Src->glDescription.target)");
3244 if(!dstSwapchain) {
3245 TRACE("Drawing to offscreen buffer\n");
3246 glDrawBuffer(myDevice->offscreenBuffer);
3247 checkGLcall("glDrawBuffer");
3248 } else {
3249 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3250 TRACE("Drawing to %#x buffer\n", buffer);
3251 glDrawBuffer(buffer);
3252 checkGLcall("glDrawBuffer");
3255 /* Bind the texture */
3256 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3257 checkGLcall("glBindTexture");
3259 /* Filtering for StretchRect */
3260 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3261 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3262 checkGLcall("glTexParameteri");
3263 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3264 minMipLookup[Filter][WINED3DTEXF_NONE]);
3265 checkGLcall("glTexParameteri");
3266 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3267 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3268 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3269 checkGLcall("glTexEnvi");
3271 /* This is for color keying */
3272 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3273 glEnable(GL_ALPHA_TEST);
3274 checkGLcall("glEnable GL_ALPHA_TEST");
3276 /* When the primary render target uses P8, the alpha component contains the palette index.
3277 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3278 * should be masked away have alpha set to 0. */
3279 if(primary_render_target_is_p8(myDevice))
3280 glAlphaFunc(GL_NOTEQUAL, (float)This->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3281 else
3282 glAlphaFunc(GL_NOTEQUAL, 0.0);
3283 checkGLcall("glAlphaFunc\n");
3284 } else {
3285 glDisable(GL_ALPHA_TEST);
3286 checkGLcall("glDisable GL_ALPHA_TEST");
3289 /* Draw a textured quad
3291 glBegin(GL_QUADS);
3293 glColor3d(1.0f, 1.0f, 1.0f);
3294 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3295 glVertex3f(rect.x1,
3296 rect.y1,
3297 0.0);
3299 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3300 glVertex3f(rect.x1, rect.y2, 0.0);
3302 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3303 glVertex3f(rect.x2,
3304 rect.y2,
3305 0.0);
3307 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3308 glVertex3f(rect.x2,
3309 rect.y1,
3310 0.0);
3311 glEnd();
3312 checkGLcall("glEnd");
3314 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3315 glDisable(GL_ALPHA_TEST);
3316 checkGLcall("glDisable(GL_ALPHA_TEST)");
3319 /* Flush in case the drawable is used by multiple GL contexts */
3320 if(dstSwapchain && (dstSwapchain->num_contexts >= 2))
3321 glFlush();
3323 glBindTexture(Src->glDescription.target, 0);
3324 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3325 /* Leave the opengl state valid for blitting */
3326 glDisable(Src->glDescription.target);
3327 checkGLcall("glDisable(Src->glDescription.target)");
3329 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3330 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3332 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3333 glDrawBuffer(GL_BACK);
3334 checkGLcall("glDrawBuffer");
3336 /* Restore the color key parameters */
3337 Src->CKeyFlags = oldCKeyFlags;
3338 This->SrcBltCKey = oldBltCKey;
3340 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3341 if(paletteOverride)
3342 Src->palette = NULL;
3344 LEAVE_GL();
3346 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3347 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3348 * is outdated now
3350 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3351 /* TODO: This should be moved to ModifyLocation() */
3352 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3353 This->Flags |= SFLAG_INTEXTURE;
3356 return WINED3D_OK;
3357 } else {
3358 /* Source-Less Blit to render target */
3359 if (Flags & WINEDDBLT_COLORFILL) {
3360 /* This is easy to handle for the D3D Device... */
3361 DWORD color;
3363 TRACE("Colorfill\n");
3365 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3366 must be true if we are here */
3367 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3368 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3369 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3370 TRACE("Surface is higher back buffer, falling back to software\n");
3371 return WINED3DERR_INVALIDCALL;
3374 /* The color as given in the Blt function is in the format of the frame-buffer...
3375 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3377 if (This->resource.format == WINED3DFMT_P8) {
3378 if (This->palette) {
3379 color = ((0xFF000000) |
3380 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3381 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3382 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3383 } else {
3384 color = 0xFF000000;
3387 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3388 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3389 color = 0xFFFFFFFF;
3390 } else {
3391 color = ((0xFF000000) |
3392 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3393 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3394 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3397 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3398 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3399 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3401 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3402 color = DDBltFx->u5.dwFillColor;
3404 else {
3405 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3406 return WINED3DERR_INVALIDCALL;
3409 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3410 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3411 1, /* Number of rectangles */
3412 &rect, WINED3DCLEAR_TARGET, color,
3413 0.0 /* Z */,
3414 0 /* Stencil */);
3415 return WINED3D_OK;
3419 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3420 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3421 return WINED3DERR_INVALIDCALL;
3424 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3426 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3427 float depth;
3429 if (Flags & WINEDDBLT_DEPTHFILL) {
3430 switch(This->resource.format) {
3431 case WINED3DFMT_D16:
3432 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3433 break;
3434 case WINED3DFMT_D15S1:
3435 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3436 break;
3437 case WINED3DFMT_D24S8:
3438 case WINED3DFMT_D24X8:
3439 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3440 break;
3441 case WINED3DFMT_D32:
3442 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3443 break;
3444 default:
3445 depth = 0.0;
3446 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3449 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3450 DestRect == NULL ? 0 : 1,
3451 (WINED3DRECT *) DestRect,
3452 WINED3DCLEAR_ZBUFFER,
3453 0x00000000,
3454 depth,
3455 0x00000000);
3458 FIXME("(%p): Unsupp depthstencil blit\n", This);
3459 return WINED3DERR_INVALIDCALL;
3462 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3463 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3464 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3465 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3466 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3467 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3469 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3471 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3472 return WINEDDERR_SURFACEBUSY;
3475 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3476 * except depth blits, which seem to work
3478 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3479 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3480 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3481 return WINED3DERR_INVALIDCALL;
3482 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3483 TRACE("Z Blit override handled the blit\n");
3484 return WINED3D_OK;
3488 /* Special cases for RenderTargets */
3489 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3490 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3491 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3494 /* For the rest call the X11 surface implementation.
3495 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3496 * other Blts are rather rare
3498 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3501 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3502 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3503 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3504 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3505 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3507 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3509 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3510 return WINEDDERR_SURFACEBUSY;
3513 if(myDevice->inScene &&
3514 (iface == myDevice->stencilBufferTarget ||
3515 (Source && Source == myDevice->stencilBufferTarget))) {
3516 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3517 return WINED3DERR_INVALIDCALL;
3520 /* Special cases for RenderTargets */
3521 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3522 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3524 RECT SrcRect, DstRect;
3525 DWORD Flags=0;
3527 if(rsrc) {
3528 SrcRect.left = rsrc->left;
3529 SrcRect.top= rsrc->top;
3530 SrcRect.bottom = rsrc->bottom;
3531 SrcRect.right = rsrc->right;
3532 } else {
3533 SrcRect.left = 0;
3534 SrcRect.top = 0;
3535 SrcRect.right = srcImpl->currentDesc.Width;
3536 SrcRect.bottom = srcImpl->currentDesc.Height;
3539 DstRect.left = dstx;
3540 DstRect.top=dsty;
3541 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3542 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3544 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3545 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3546 Flags |= WINEDDBLT_KEYSRC;
3547 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3548 Flags |= WINEDDBLT_KEYDEST;
3549 if(trans & WINEDDBLTFAST_WAIT)
3550 Flags |= WINEDDBLT_WAIT;
3551 if(trans & WINEDDBLTFAST_DONOTWAIT)
3552 Flags |= WINEDDBLT_DONOTWAIT;
3554 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3558 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3561 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3562 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3563 RGBQUAD col[256];
3564 IWineD3DPaletteImpl *pal = This->palette;
3565 unsigned int n;
3566 TRACE("(%p)\n", This);
3568 if(This->resource.format == WINED3DFMT_P8 ||
3569 This->resource.format == WINED3DFMT_A8P8)
3571 if(!(This->Flags & SFLAG_INSYSMEM)) {
3572 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3573 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3575 TRACE("Dirtifying surface\n");
3576 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3579 if(This->Flags & SFLAG_DIBSECTION) {
3580 TRACE("(%p): Updating the hdc's palette\n", This);
3581 for (n=0; n<256; n++) {
3582 if(pal) {
3583 col[n].rgbRed = pal->palents[n].peRed;
3584 col[n].rgbGreen = pal->palents[n].peGreen;
3585 col[n].rgbBlue = pal->palents[n].peBlue;
3586 } else {
3587 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3588 /* Use the default device palette */
3589 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
3590 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
3591 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
3593 col[n].rgbReserved = 0;
3595 SetDIBColorTable(This->hDC, 0, 256, col);
3598 /* Propagate the changes to the drawable when we have a palette. This function is also called
3599 * when the palette is removed.
3600 * TODO: in case of hardware p8 palettes we should only upload the palette. */
3601 if(pal && (This->resource.usage & WINED3DUSAGE_RENDERTARGET))
3602 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3604 return WINED3D_OK;
3607 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3608 /** Check against the maximum texture sizes supported by the video card **/
3609 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3610 unsigned int pow2Width, pow2Height;
3611 const GlPixelFormatDesc *glDesc;
3613 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3614 /* Setup some glformat defaults */
3615 This->glDescription.glFormat = glDesc->glFormat;
3616 This->glDescription.glFormatInternal = glDesc->glInternal;
3617 This->glDescription.glType = glDesc->glType;
3619 This->glDescription.textureName = 0;
3620 This->glDescription.target = GL_TEXTURE_2D;
3622 /* Non-power2 support */
3623 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3624 pow2Width = This->currentDesc.Width;
3625 pow2Height = This->currentDesc.Height;
3626 } else {
3627 /* Find the nearest pow2 match */
3628 pow2Width = pow2Height = 1;
3629 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3630 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3632 This->pow2Width = pow2Width;
3633 This->pow2Height = pow2Height;
3635 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3636 WINED3DFORMAT Format = This->resource.format;
3637 /** TODO: add support for non power two compressed textures **/
3638 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3639 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3640 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3641 This, This->currentDesc.Width, This->currentDesc.Height);
3642 return WINED3DERR_NOTAVAILABLE;
3646 if(pow2Width != This->currentDesc.Width ||
3647 pow2Height != This->currentDesc.Height) {
3648 This->Flags |= SFLAG_NONPOW2;
3651 TRACE("%p\n", This);
3652 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3653 /* one of three options
3654 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)
3655 2: Set the texture to the maximum size (bad idea)
3656 3: WARN and return WINED3DERR_NOTAVAILABLE;
3657 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.
3659 WARN("(%p) Creating an oversized surface\n", This);
3660 This->Flags |= SFLAG_OVERSIZE;
3662 /* This will be initialized on the first blt */
3663 This->glRect.left = 0;
3664 This->glRect.top = 0;
3665 This->glRect.right = 0;
3666 This->glRect.bottom = 0;
3667 } else {
3668 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3669 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3670 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3671 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3673 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE) &&
3674 !((This->resource.format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE) && (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3676 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3677 This->pow2Width = This->currentDesc.Width;
3678 This->pow2Height = This->currentDesc.Height;
3679 This->Flags &= ~SFLAG_NONPOW2;
3682 /* No oversize, gl rect is the full texture size */
3683 This->Flags &= ~SFLAG_OVERSIZE;
3684 This->glRect.left = 0;
3685 This->glRect.top = 0;
3686 This->glRect.right = This->pow2Width;
3687 This->glRect.bottom = This->pow2Height;
3690 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3691 switch(wined3d_settings.offscreen_rendering_mode) {
3692 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3693 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3694 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3698 This->Flags |= SFLAG_INSYSMEM;
3700 return WINED3D_OK;
3703 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3704 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3705 IWineD3DBaseTexture *texture;
3707 TRACE("(%p)->(%s, %s)\n", iface,
3708 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3709 persistent ? "TRUE" : "FALSE");
3711 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3712 IWineD3DSwapChain *swapchain = NULL;
3714 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3715 TRACE("Surface %p is an onscreen surface\n", iface);
3717 IWineD3DSwapChain_Release(swapchain);
3718 } else {
3719 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3720 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3724 if(persistent) {
3725 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
3726 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3727 TRACE("Passing to container\n");
3728 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3729 IWineD3DBaseTexture_Release(texture);
3732 This->Flags &= ~SFLAG_LOCATIONS;
3733 This->Flags |= flag;
3734 } else {
3735 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
3736 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3737 TRACE("Passing to container\n");
3738 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3739 IWineD3DBaseTexture_Release(texture);
3742 This->Flags &= ~flag;
3746 struct coords {
3747 GLfloat x, y, z;
3750 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
3751 struct coords coords[4];
3752 RECT rect;
3753 IWineD3DSwapChain *swapchain = NULL;
3754 IWineD3DBaseTexture *texture = NULL;
3755 HRESULT hr;
3756 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3758 if(rect_in) {
3759 rect = *rect_in;
3760 } else {
3761 rect.left = 0;
3762 rect.top = 0;
3763 rect.right = This->currentDesc.Width;
3764 rect.bottom = This->currentDesc.Height;
3767 ActivateContext(device, device->render_targets[0], CTXUSAGE_BLIT);
3768 ENTER_GL();
3770 if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
3771 glEnable(GL_TEXTURE_RECTANGLE_ARB);
3772 checkGLcall("glEnable(GL_TEXTURE_RECTANGLE_ARB)");
3773 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName);
3774 checkGLcall("GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName)");
3775 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3776 checkGLcall("glTexParameteri");
3777 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3778 checkGLcall("glTexParameteri");
3780 coords[0].x = rect.left;
3781 coords[0].z = 0;
3783 coords[1].x = rect.left;
3784 coords[1].z = 0;
3786 coords[2].x = rect.right;
3787 coords[2].z = 0;
3789 coords[3].x = rect.right;
3790 coords[3].z = 0;
3792 coords[0].y = rect.top;
3793 coords[1].y = rect.bottom;
3794 coords[2].y = rect.bottom;
3795 coords[3].y = rect.top;
3796 } else if(This->glDescription.target == GL_TEXTURE_2D) {
3797 glEnable(GL_TEXTURE_2D);
3798 checkGLcall("glEnable(GL_TEXTURE_2D)");
3799 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
3800 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
3801 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3802 checkGLcall("glTexParameteri");
3803 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3804 checkGLcall("glTexParameteri");
3806 coords[0].x = (float)rect.left / This->pow2Width;
3807 coords[0].z = 0;
3809 coords[1].x = (float)rect.left / This->pow2Width;
3810 coords[1].z = 0;
3812 coords[2].x = (float)rect.right / This->pow2Width;
3813 coords[2].z = 0;
3815 coords[3].x = (float)rect.right / This->pow2Width;
3816 coords[3].z = 0;
3818 coords[0].y = (float)rect.top / This->pow2Height;
3819 coords[1].y = (float)rect.bottom / This->pow2Height;
3820 coords[2].y = (float)rect.bottom / This->pow2Height;
3821 coords[3].y = (float)rect.top / This->pow2Height;
3822 } else {
3823 /* Must be a cube map */
3824 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
3825 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
3826 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
3827 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
3828 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3829 checkGLcall("glTexParameteri");
3830 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3831 checkGLcall("glTexParameteri");
3833 switch(This->glDescription.target) {
3834 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3835 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
3836 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3837 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3838 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
3839 break;
3841 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3842 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3843 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
3844 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
3845 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3846 break;
3848 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3849 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
3850 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3851 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3852 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
3853 break;
3855 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3856 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3857 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3858 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3859 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3860 break;
3862 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3863 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3864 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3865 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
3866 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
3867 break;
3869 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
3870 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
3871 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
3872 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3873 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3875 default:
3876 ERR("Unexpected texture target\n");
3877 LEAVE_GL();
3878 return;
3882 glBegin(GL_QUADS);
3883 glTexCoord3fv(&coords[0].x);
3884 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
3886 glTexCoord3fv(&coords[1].x);
3887 glVertex2i(rect.left, device->render_offscreen ? rect.top : rect.bottom);
3889 glTexCoord3fv(&coords[2].x);
3890 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
3892 glTexCoord3fv(&coords[3].x);
3893 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
3894 glEnd();
3895 checkGLcall("glEnd");
3897 if(This->glDescription.target != GL_TEXTURE_2D) {
3898 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3899 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3900 } else {
3901 glDisable(GL_TEXTURE_2D);
3902 checkGLcall("glDisable(GL_TEXTURE_2D)");
3905 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain);
3906 if(hr == WINED3D_OK && swapchain) {
3907 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
3908 if(((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
3909 glFlush();
3911 IWineD3DSwapChain_Release(swapchain);
3912 } else {
3913 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
3914 * reset properly next draw
3916 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture);
3917 if(hr == WINED3D_OK && texture) {
3918 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
3919 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
3920 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
3921 IWineD3DBaseTexture_Release(texture);
3924 LEAVE_GL();
3927 /*****************************************************************************
3928 * IWineD3DSurface::LoadLocation
3930 * Copies the current surface data from wherever it is to the requested
3931 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
3932 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
3933 * multiple locations, the gl texture is preferred over the drawable, which is
3934 * preferred over system memory. The PBO counts as system memory. If rect is
3935 * not NULL, only the specified rectangle is copied (only supported for
3936 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
3937 * location is marked up to date after the copy.
3939 * Parameters:
3940 * flag: Surface location flag to be updated
3941 * rect: rectangle to be copied
3943 * Returns:
3944 * WINED3D_OK on success
3945 * WINED3DERR_DEVICELOST on an internal error
3947 *****************************************************************************/
3948 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
3949 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3950 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3951 IWineD3DSwapChain *swapchain = NULL;
3952 GLenum format, internal, type;
3953 CONVERT_TYPES convert;
3954 int bpp;
3955 int width, pitch, outpitch;
3956 BYTE *mem;
3958 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3959 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3960 TRACE("Surface %p is an onscreen surface\n", iface);
3962 IWineD3DSwapChain_Release(swapchain);
3963 } else {
3964 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
3965 * Prefer SFLAG_INTEXTURE. */
3966 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
3970 TRACE("(%p)->(%s, %p)\n", iface,
3971 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3972 rect);
3973 if(rect) {
3974 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
3977 if(This->Flags & flag) {
3978 TRACE("Location already up to date\n");
3979 return WINED3D_OK;
3982 if(!(This->Flags & SFLAG_LOCATIONS)) {
3983 ERR("Surface does not have any up to date location\n");
3984 This->Flags |= SFLAG_LOST;
3985 return WINED3DERR_DEVICELOST;
3988 if(flag == SFLAG_INSYSMEM) {
3989 surface_prepare_system_memory(This);
3991 /* Download the surface to system memory */
3992 if(This->Flags & SFLAG_INTEXTURE) {
3993 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
3994 surface_bind_and_dirtify(This);
3996 surface_download_data(This);
3997 } else {
3998 read_from_framebuffer(This, rect,
3999 This->resource.allocatedMemory,
4000 IWineD3DSurface_GetPitch(iface));
4002 } else if(flag == SFLAG_INDRAWABLE) {
4003 if(This->Flags & SFLAG_INTEXTURE) {
4004 surface_blt_to_drawable(This, rect);
4005 } else {
4006 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4008 /* The width is in 'length' not in bytes */
4009 width = This->currentDesc.Width;
4010 pitch = IWineD3DSurface_GetPitch(iface);
4012 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4013 int height = This->currentDesc.Height;
4015 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4016 outpitch = width * bpp;
4017 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4019 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4020 if(!mem) {
4021 ERR("Out of memory %d, %d!\n", outpitch, height);
4022 return WINED3DERR_OUTOFVIDEOMEMORY;
4024 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4026 This->Flags |= SFLAG_CONVERTED;
4027 } else {
4028 This->Flags &= ~SFLAG_CONVERTED;
4029 mem = This->resource.allocatedMemory;
4032 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4034 /* Don't delete PBO memory */
4035 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4036 HeapFree(GetProcessHeap(), 0, mem);
4038 } else /* if(flag == SFLAG_INTEXTURE) */ {
4039 if (This->Flags & SFLAG_INDRAWABLE) {
4040 read_from_framebuffer_texture(This);
4041 } else { /* Upload from system memory */
4042 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4044 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4045 surface_bind_and_dirtify(This);
4046 ENTER_GL();
4048 /* The only place where LoadTexture() might get called when isInDraw=1
4049 * is ActivateContext where lastActiveRenderTarget is preloaded.
4051 if(iface == device->lastActiveRenderTarget && device->isInDraw)
4052 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
4054 /* Otherwise: System memory copy must be most up to date */
4056 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4057 This->Flags |= SFLAG_GLCKEY;
4058 This->glCKey = This->SrcBltCKey;
4060 else This->Flags &= ~SFLAG_GLCKEY;
4062 /* The width is in 'length' not in bytes */
4063 width = This->currentDesc.Width;
4064 pitch = IWineD3DSurface_GetPitch(iface);
4066 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4067 int height = This->currentDesc.Height;
4069 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4070 outpitch = width * bpp;
4071 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4073 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4074 if(!mem) {
4075 ERR("Out of memory %d, %d!\n", outpitch, height);
4076 return WINED3DERR_OUTOFVIDEOMEMORY;
4078 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4080 This->Flags |= SFLAG_CONVERTED;
4081 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
4082 d3dfmt_p8_upload_palette(iface, convert);
4083 This->Flags &= ~SFLAG_CONVERTED;
4084 mem = This->resource.allocatedMemory;
4085 } else {
4086 This->Flags &= ~SFLAG_CONVERTED;
4087 mem = This->resource.allocatedMemory;
4090 /* Make sure the correct pitch is used */
4091 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4093 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4094 TRACE("non power of two support\n");
4095 if(!(This->Flags & SFLAG_ALLOCATED)) {
4096 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4098 if (mem || (This->Flags & SFLAG_PBO)) {
4099 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4101 } else {
4102 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4103 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4105 if(!(This->Flags & SFLAG_ALLOCATED)) {
4106 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4108 if (mem || (This->Flags & SFLAG_PBO)) {
4109 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4113 /* Restore the default pitch */
4114 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4115 LEAVE_GL();
4117 /* Don't delete PBO memory */
4118 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4119 HeapFree(GetProcessHeap(), 0, mem);
4123 if(rect == NULL) {
4124 This->Flags |= flag;
4127 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
4128 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4129 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4130 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4133 return WINED3D_OK;
4136 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
4137 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4138 IWineD3DSwapChain *swapchain = NULL;
4140 /* Update the drawable size method */
4141 if(container) {
4142 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4144 if(swapchain) {
4145 This->get_drawable_size = get_drawable_size_swapchain;
4146 IWineD3DSwapChain_Release(swapchain);
4147 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4148 switch(wined3d_settings.offscreen_rendering_mode) {
4149 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4150 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4151 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4155 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4158 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4159 return SURFACE_OPENGL;
4162 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4164 /* IUnknown */
4165 IWineD3DBaseSurfaceImpl_QueryInterface,
4166 IWineD3DBaseSurfaceImpl_AddRef,
4167 IWineD3DSurfaceImpl_Release,
4168 /* IWineD3DResource */
4169 IWineD3DBaseSurfaceImpl_GetParent,
4170 IWineD3DBaseSurfaceImpl_GetDevice,
4171 IWineD3DBaseSurfaceImpl_SetPrivateData,
4172 IWineD3DBaseSurfaceImpl_GetPrivateData,
4173 IWineD3DBaseSurfaceImpl_FreePrivateData,
4174 IWineD3DBaseSurfaceImpl_SetPriority,
4175 IWineD3DBaseSurfaceImpl_GetPriority,
4176 IWineD3DSurfaceImpl_PreLoad,
4177 IWineD3DSurfaceImpl_UnLoad,
4178 IWineD3DBaseSurfaceImpl_GetType,
4179 /* IWineD3DSurface */
4180 IWineD3DBaseSurfaceImpl_GetContainer,
4181 IWineD3DBaseSurfaceImpl_GetDesc,
4182 IWineD3DSurfaceImpl_LockRect,
4183 IWineD3DSurfaceImpl_UnlockRect,
4184 IWineD3DSurfaceImpl_GetDC,
4185 IWineD3DSurfaceImpl_ReleaseDC,
4186 IWineD3DSurfaceImpl_Flip,
4187 IWineD3DSurfaceImpl_Blt,
4188 IWineD3DBaseSurfaceImpl_GetBltStatus,
4189 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4190 IWineD3DBaseSurfaceImpl_IsLost,
4191 IWineD3DBaseSurfaceImpl_Restore,
4192 IWineD3DSurfaceImpl_BltFast,
4193 IWineD3DBaseSurfaceImpl_GetPalette,
4194 IWineD3DBaseSurfaceImpl_SetPalette,
4195 IWineD3DSurfaceImpl_RealizePalette,
4196 IWineD3DBaseSurfaceImpl_SetColorKey,
4197 IWineD3DBaseSurfaceImpl_GetPitch,
4198 IWineD3DSurfaceImpl_SetMem,
4199 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4200 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4201 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4202 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4203 IWineD3DBaseSurfaceImpl_SetClipper,
4204 IWineD3DBaseSurfaceImpl_GetClipper,
4205 /* Internal use: */
4206 IWineD3DSurfaceImpl_AddDirtyRect,
4207 IWineD3DSurfaceImpl_LoadTexture,
4208 IWineD3DSurfaceImpl_BindTexture,
4209 IWineD3DSurfaceImpl_SaveSnapshot,
4210 IWineD3DSurfaceImpl_SetContainer,
4211 IWineD3DSurfaceImpl_SetGlTextureDesc,
4212 IWineD3DSurfaceImpl_GetGlDesc,
4213 IWineD3DSurfaceImpl_GetData,
4214 IWineD3DSurfaceImpl_SetFormat,
4215 IWineD3DSurfaceImpl_PrivateSetup,
4216 IWineD3DSurfaceImpl_ModifyLocation,
4217 IWineD3DSurfaceImpl_LoadLocation,
4218 IWineD3DSurfaceImpl_GetImplType