imm32: Correct a typo accidently using data instead of root_context.
[wine/gsoc_dplay.git] / dlls / wined3d / surface.c
blob8ed1513fec17502526ea92099fc4e18d87d4c0c1
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 LEAVE_GL();
794 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
795 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
796 * the same color but we have no choice.
797 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
799 if((This->resource.format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice)) {
800 PALETTEENTRY *pal = NULL;
801 DWORD width = pitch / 3;
802 int x, y, c;
804 if(This->palette) {
805 pal = This->palette->palents;
806 } else {
807 ERR("Palette is missing, cannot perform inverse palette lookup\n");
808 HeapFree(GetProcessHeap(), 0, mem);
809 return ;
812 for(y = local_rect.top; y < local_rect.bottom; y++) {
813 for(x = local_rect.left; x < local_rect.right; x++) {
814 /* start lines pixels */
815 BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
816 BYTE *green = blue + 1;
817 BYTE *red = green + 1;
819 for(c = 0; c < 256; c++) {
820 if(*red == pal[c].peRed &&
821 *green == pal[c].peGreen &&
822 *blue == pal[c].peBlue)
824 *((BYTE *) dest + y * width + x) = c;
825 break;
830 HeapFree(GetProcessHeap(), 0, mem);
834 /* Read the framebuffer contents into a texture */
835 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This)
837 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
838 IWineD3DSwapChainImpl *swapchain;
839 int bpp;
840 GLenum format, internal, type;
841 CONVERT_TYPES convert;
842 BOOL srcIsUpsideDown;
843 GLint prevRead;
845 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
847 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
848 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
849 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
850 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
851 * context->last_was_blit set on the unlock.
853 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
854 surface_bind_and_dirtify(This);
855 ENTER_GL();
857 glGetIntegerv(GL_READ_BUFFER, &prevRead);
859 /* Select the correct read buffer, and give some debug output.
860 * There is no need to keep track of the current read buffer or reset it, every part of the code
861 * that reads sets the read buffer as desired.
863 if(!swapchain) {
864 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
865 * Read from the back buffer
867 TRACE("Locking offscreen render target\n");
868 glReadBuffer(device->offscreenBuffer);
869 srcIsUpsideDown = TRUE;
870 } else {
871 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
872 TRACE("Locking %#x buffer\n", buffer);
873 glReadBuffer(buffer);
874 checkGLcall("glReadBuffer");
876 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
877 srcIsUpsideDown = FALSE;
880 if(!(This->Flags & SFLAG_ALLOCATED)) {
881 surface_allocate_surface(This, internal, This->pow2Width,
882 This->pow2Height, format, type);
885 clear_unused_channels(This);
887 /* If !SrcIsUpsideDown we should flip the surface.
888 * This can be done using glCopyTexSubImage2D but this
889 * is VERY slow, so don't do that. We should prevent
890 * this code from getting called in such cases or perhaps
891 * we can use FBOs */
893 glCopyTexSubImage2D(This->glDescription.target,
894 This->glDescription.level,
895 0, 0, 0, 0,
896 This->currentDesc.Width,
897 This->currentDesc.Height);
898 checkGLcall("glCopyTexSubImage2D");
900 glReadBuffer(prevRead);
901 vcheckGLcall("glReadBuffer");
903 LEAVE_GL();
904 TRACE("Updated target %d\n", This->glDescription.target);
907 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
908 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
909 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
910 * changed
912 if(!(This->Flags & SFLAG_DYNLOCK)) {
913 This->lockCount++;
914 /* MAXLOCKCOUNT is defined in wined3d_private.h */
915 if(This->lockCount > MAXLOCKCOUNT) {
916 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
917 This->Flags |= SFLAG_DYNLOCK;
921 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
922 * Also don't create a PBO for systemmem surfaces.
924 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
925 GLenum error;
926 ENTER_GL();
928 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
929 error = glGetError();
930 if(This->pbo == 0 || error != GL_NO_ERROR) {
931 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
934 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
936 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
937 checkGLcall("glBindBufferARB");
939 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
940 checkGLcall("glBufferDataARB");
942 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
943 checkGLcall("glBindBufferARB");
945 /* We don't need the system memory anymore and we can't even use it for PBOs */
946 if(!(This->Flags & SFLAG_CLIENT)) {
947 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
948 This->resource.heapMemory = NULL;
950 This->resource.allocatedMemory = NULL;
951 This->Flags |= SFLAG_PBO;
952 LEAVE_GL();
953 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
954 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
955 * or a pbo to map
957 if(!This->resource.heapMemory) {
958 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
960 This->resource.allocatedMemory =
961 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
962 if(This->Flags & SFLAG_INSYSMEM) {
963 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
968 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
969 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
970 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
971 IWineD3DSwapChain *swapchain = NULL;
973 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
975 /* This is also done in the base class, but we have to verify this before loading any data from
976 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
977 * may interfere, and all other bad things may happen
979 if (This->Flags & SFLAG_LOCKED) {
980 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
981 return WINED3DERR_INVALIDCALL;
983 This->Flags |= SFLAG_LOCKED;
985 if (!(This->Flags & SFLAG_LOCKABLE))
987 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
990 if (Flags & WINED3DLOCK_DISCARD) {
991 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
992 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
993 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
994 This->Flags |= SFLAG_INSYSMEM;
995 goto lock_end;
998 if (This->Flags & SFLAG_INSYSMEM) {
999 TRACE("Local copy is up to date, not downloading data\n");
1000 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1001 goto lock_end;
1004 /* Now download the surface content from opengl
1005 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1006 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
1008 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1009 if(swapchain || iface == myDevice->render_targets[0]) {
1010 const RECT *pass_rect = pRect;
1012 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1013 * because most caller functions do not need that. So do that here
1015 if(pRect &&
1016 pRect->top == 0 &&
1017 pRect->left == 0 &&
1018 pRect->right == This->currentDesc.Width &&
1019 pRect->bottom == This->currentDesc.Height) {
1020 pass_rect = NULL;
1023 switch(wined3d_settings.rendertargetlock_mode) {
1024 case RTL_TEXDRAW:
1025 case RTL_TEXTEX:
1026 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1027 #if 0
1028 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1029 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1030 * This may be faster on some cards
1032 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1033 #endif
1034 /* drop through */
1036 case RTL_AUTO:
1037 case RTL_READDRAW:
1038 case RTL_READTEX:
1039 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
1040 break;
1042 case RTL_DISABLE:
1043 break;
1045 if(swapchain) IWineD3DSwapChain_Release(swapchain);
1047 } else if(iface == myDevice->stencilBufferTarget) {
1048 /** the depth stencil in openGL has a format of GL_FLOAT
1049 * which should be good for WINED3DFMT_D16_LOCKABLE
1050 * and WINED3DFMT_D16
1051 * it is unclear what format the stencil buffer is in except.
1052 * 'Each index is converted to fixed point...
1053 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1054 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1055 * glReadPixels(This->lockedRect.left,
1056 * This->lockedRect.bottom - j - 1,
1057 * This->lockedRect.right - This->lockedRect.left,
1058 * 1,
1059 * GL_DEPTH_COMPONENT,
1060 * type,
1061 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1063 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1064 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1065 * none of that is the case the problem is not in this function :-)
1066 ********************************************/
1067 FIXME("Depth stencil locking not supported yet\n");
1068 } else {
1069 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1070 TRACE("locking an ordinary surface\n");
1071 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1074 lock_end:
1075 if(This->Flags & SFLAG_PBO) {
1076 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1077 ENTER_GL();
1078 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1079 checkGLcall("glBindBufferARB");
1081 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1082 if(This->resource.allocatedMemory) {
1083 ERR("The surface already has PBO memory allocated!\n");
1086 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1087 checkGLcall("glMapBufferARB");
1089 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1090 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1091 checkGLcall("glBindBufferARB");
1093 LEAVE_GL();
1096 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1097 /* Don't dirtify */
1098 } else {
1099 IWineD3DBaseTexture *pBaseTexture;
1101 * Dirtify on lock
1102 * as seen in msdn docs
1104 IWineD3DSurface_AddDirtyRect(iface, pRect);
1106 /** Dirtify Container if needed */
1107 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
1108 TRACE("Making container dirty\n");
1109 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1110 IWineD3DBaseTexture_Release(pBaseTexture);
1111 } else {
1112 TRACE("Surface is standalone, no need to dirty the container\n");
1116 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1119 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1120 GLint prev_store;
1121 GLint prev_rasterpos[4];
1122 GLint skipBytes = 0;
1123 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1124 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1125 IWineD3DSwapChainImpl *swapchain;
1127 /* Activate the correct context for the render target */
1128 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1129 ENTER_GL();
1131 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
1132 if(!swapchain) {
1133 /* Primary offscreen render target */
1134 TRACE("Offscreen render target\n");
1135 glDrawBuffer(myDevice->offscreenBuffer);
1136 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1137 } else {
1138 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1139 TRACE("Unlocking %#x buffer\n", buffer);
1140 glDrawBuffer(buffer);
1141 checkGLcall("glDrawBuffer");
1143 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1146 glFlush();
1147 vcheckGLcall("glFlush");
1148 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1149 vcheckGLcall("glIntegerv");
1150 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1151 vcheckGLcall("glIntegerv");
1152 glPixelZoom(1.0, -1.0);
1153 vcheckGLcall("glPixelZoom");
1155 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1156 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1157 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1159 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1160 vcheckGLcall("glRasterPos2f");
1162 /* Some drivers(radeon dri, others?) don't like exceptions during
1163 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1164 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1165 * catch to put the dib section in InSync mode, which leads to a crash
1166 * and a blocked x server on my radeon card.
1168 * The following lines read the dib section so it is put in inSync mode
1169 * before glDrawPixels is called and the crash is prevented. There won't
1170 * be any interfering gdi accesses, because UnlockRect is called from
1171 * ReleaseDC, and the app won't use the dc any more afterwards.
1173 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1174 volatile BYTE read;
1175 read = This->resource.allocatedMemory[0];
1178 if(This->Flags & SFLAG_PBO) {
1179 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1180 checkGLcall("glBindBufferARB");
1183 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1184 if(This->Flags & SFLAG_LOCKED) {
1185 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1186 (This->lockedRect.bottom - This->lockedRect.top)-1,
1187 fmt, type,
1188 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1189 checkGLcall("glDrawPixels");
1190 } else {
1191 glDrawPixels(This->currentDesc.Width,
1192 This->currentDesc.Height,
1193 fmt, type, mem);
1194 checkGLcall("glDrawPixels");
1197 if(This->Flags & SFLAG_PBO) {
1198 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1199 checkGLcall("glBindBufferARB");
1202 glPixelZoom(1.0,1.0);
1203 vcheckGLcall("glPixelZoom");
1205 glRasterPos3iv(&prev_rasterpos[0]);
1206 vcheckGLcall("glRasterPos3iv");
1208 /* Reset to previous pack row length */
1209 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1210 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1212 if(!swapchain) {
1213 glDrawBuffer(myDevice->offscreenBuffer);
1214 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1215 } else if(swapchain->backBuffer) {
1216 glDrawBuffer(GL_BACK);
1217 checkGLcall("glDrawBuffer(GL_BACK)");
1218 } else {
1219 glDrawBuffer(GL_FRONT);
1220 checkGLcall("glDrawBuffer(GL_FRONT)");
1222 LEAVE_GL();
1224 return;
1227 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1228 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1229 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1230 IWineD3DSwapChainImpl *swapchain = NULL;
1231 BOOL fullsurface;
1233 if (!(This->Flags & SFLAG_LOCKED)) {
1234 WARN("trying to Unlock an unlocked surf@%p\n", This);
1235 return WINED3DERR_INVALIDCALL;
1238 if (This->Flags & SFLAG_PBO) {
1239 TRACE("Freeing PBO memory\n");
1240 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1241 ENTER_GL();
1242 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1243 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1244 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1245 checkGLcall("glUnmapBufferARB");
1246 LEAVE_GL();
1247 This->resource.allocatedMemory = NULL;
1250 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1252 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1253 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1254 goto unlock_end;
1257 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1258 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1259 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1261 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1262 static BOOL warned = FALSE;
1263 if(!warned) {
1264 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1265 warned = TRUE;
1267 goto unlock_end;
1270 if(This->dirtyRect.left == 0 &&
1271 This->dirtyRect.top == 0 &&
1272 This->dirtyRect.right == This->currentDesc.Width &&
1273 This->dirtyRect.bottom == This->currentDesc.Height) {
1274 fullsurface = TRUE;
1275 } else {
1276 /* TODO: Proper partial rectangle tracking */
1277 fullsurface = FALSE;
1278 This->Flags |= SFLAG_INSYSMEM;
1281 switch(wined3d_settings.rendertargetlock_mode) {
1282 case RTL_READTEX:
1283 case RTL_TEXTEX:
1284 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1285 ENTER_GL();
1286 if (This->glDescription.textureName == 0) {
1287 glGenTextures(1, &This->glDescription.textureName);
1288 checkGLcall("glGenTextures");
1290 glBindTexture(This->glDescription.target, This->glDescription.textureName);
1291 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
1292 LEAVE_GL();
1293 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1294 /* drop through */
1296 case RTL_AUTO:
1297 case RTL_READDRAW:
1298 case RTL_TEXDRAW:
1299 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1300 break;
1303 if(!fullsurface) {
1304 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1305 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1306 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1307 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1308 * not fully up to date because only a subrectangle was read in LockRect.
1310 This->Flags &= ~SFLAG_INSYSMEM;
1311 This->Flags |= SFLAG_INDRAWABLE;
1314 This->dirtyRect.left = This->currentDesc.Width;
1315 This->dirtyRect.top = This->currentDesc.Height;
1316 This->dirtyRect.right = 0;
1317 This->dirtyRect.bottom = 0;
1318 } else if(iface == myDevice->stencilBufferTarget) {
1319 FIXME("Depth Stencil buffer locking is not implemented\n");
1320 } else {
1321 /* The rest should be a normal texture */
1322 IWineD3DBaseTextureImpl *impl;
1323 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1324 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1325 * states need resetting
1327 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1328 if(impl->baseTexture.bindCount) {
1329 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1331 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1335 unlock_end:
1336 This->Flags &= ~SFLAG_LOCKED;
1337 memset(&This->lockedRect, 0, sizeof(RECT));
1338 return WINED3D_OK;
1341 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1342 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1343 WINED3DLOCKED_RECT lock;
1344 HRESULT hr;
1345 RGBQUAD col[256];
1347 TRACE("(%p)->(%p)\n",This,pHDC);
1349 if(This->Flags & SFLAG_USERPTR) {
1350 ERR("Not supported on surfaces with an application-provided surfaces\n");
1351 return WINEDDERR_NODC;
1354 /* Give more detailed info for ddraw */
1355 if (This->Flags & SFLAG_DCINUSE)
1356 return WINEDDERR_DCALREADYCREATED;
1358 /* Can't GetDC if the surface is locked */
1359 if (This->Flags & SFLAG_LOCKED)
1360 return WINED3DERR_INVALIDCALL;
1362 /* According to Direct3D9 docs, only these formats are supported */
1363 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1364 if (This->resource.format != WINED3DFMT_R5G6B5 &&
1365 This->resource.format != WINED3DFMT_X1R5G5B5 &&
1366 This->resource.format != WINED3DFMT_R8G8B8 &&
1367 This->resource.format != WINED3DFMT_X8R8G8B8) return WINED3DERR_INVALIDCALL;
1370 memset(&lock, 0, sizeof(lock)); /* To be sure */
1372 /* Create a DIB section if there isn't a hdc yet */
1373 if(!This->hDC) {
1374 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1375 if(This->Flags & SFLAG_CLIENT) {
1376 IWineD3DSurface_PreLoad(iface);
1379 /* Use the dib section from now on if we are not using a PBO */
1380 if(!(This->Flags & SFLAG_PBO))
1381 This->resource.allocatedMemory = This->dib.bitmap_data;
1384 /* Lock the surface */
1385 hr = IWineD3DSurface_LockRect(iface,
1386 &lock,
1387 NULL,
1390 if(This->Flags & SFLAG_PBO) {
1391 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1392 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1395 if(FAILED(hr)) {
1396 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1397 /* keep the dib section */
1398 return hr;
1401 if(This->resource.format == WINED3DFMT_P8 ||
1402 This->resource.format == WINED3DFMT_A8P8) {
1403 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1404 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1405 unsigned int n;
1406 PALETTEENTRY *pal = NULL;
1408 if(This->palette) {
1409 pal = This->palette->palents;
1410 } else {
1411 IWineD3DSurfaceImpl *dds_primary = (IWineD3DSurfaceImpl *)This->resource.wineD3DDevice->ddraw_primary;
1412 if (dds_primary && dds_primary->palette)
1413 pal = dds_primary->palette->palents;
1416 if (pal) {
1417 for (n=0; n<256; n++) {
1418 col[n].rgbRed = pal[n].peRed;
1419 col[n].rgbGreen = pal[n].peGreen;
1420 col[n].rgbBlue = pal[n].peBlue;
1421 col[n].rgbReserved = 0;
1423 SetDIBColorTable(This->hDC, 0, 256, col);
1427 *pHDC = This->hDC;
1428 TRACE("returning %p\n",*pHDC);
1429 This->Flags |= SFLAG_DCINUSE;
1431 return WINED3D_OK;
1434 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1435 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1437 TRACE("(%p)->(%p)\n",This,hDC);
1439 if (!(This->Flags & SFLAG_DCINUSE))
1440 return WINED3DERR_INVALIDCALL;
1442 if (This->hDC !=hDC) {
1443 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1444 return WINED3DERR_INVALIDCALL;
1447 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1448 /* Copy the contents of the DIB over to the PBO */
1449 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1452 /* we locked first, so unlock now */
1453 IWineD3DSurface_UnlockRect(iface);
1455 This->Flags &= ~SFLAG_DCINUSE;
1457 return WINED3D_OK;
1460 /* ******************************************************
1461 IWineD3DSurface Internal (No mapping to directx api) parts follow
1462 ****************************************************** */
1464 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) {
1465 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1466 const GlPixelFormatDesc *glDesc;
1467 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1468 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1470 /* Default values: From the surface */
1471 *format = glDesc->glFormat;
1472 *type = glDesc->glType;
1473 *convert = NO_CONVERSION;
1474 *target_bpp = This->bytesPerPixel;
1476 if(srgb_mode) {
1477 *internal = glDesc->glGammaInternal;
1478 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1479 *internal = glDesc->rtInternal;
1480 } else {
1481 *internal = glDesc->glInternal;
1484 /* Ok, now look if we have to do any conversion */
1485 switch(This->resource.format) {
1486 case WINED3DFMT_P8:
1487 /* ****************
1488 Paletted Texture
1489 **************** */
1491 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1492 * of the two is available make sure texturing is requested as neither of the two works in
1493 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1494 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1495 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1496 * conflicts with this.
1498 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) && primary_render_target_is_p8(device))) || colorkey_active || !use_texturing ) {
1499 *format = GL_RGBA;
1500 *internal = GL_RGBA;
1501 *type = GL_UNSIGNED_BYTE;
1502 *target_bpp = 4;
1503 if(colorkey_active) {
1504 *convert = CONVERT_PALETTED_CK;
1505 } else {
1506 *convert = CONVERT_PALETTED;
1509 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1510 *format = GL_ALPHA;
1511 *internal = GL_RGBA;
1512 *type = GL_UNSIGNED_BYTE;
1513 *target_bpp = 1;
1516 break;
1518 case WINED3DFMT_R3G3B2:
1519 /* **********************
1520 GL_UNSIGNED_BYTE_3_3_2
1521 ********************** */
1522 if (colorkey_active) {
1523 /* This texture format will never be used.. So do not care about color keying
1524 up until the point in time it will be needed :-) */
1525 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1527 break;
1529 case WINED3DFMT_R5G6B5:
1530 if (colorkey_active) {
1531 *convert = CONVERT_CK_565;
1532 *format = GL_RGBA;
1533 *internal = GL_RGBA;
1534 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1536 break;
1538 case WINED3DFMT_X1R5G5B5:
1539 if (colorkey_active) {
1540 *convert = CONVERT_CK_5551;
1541 *format = GL_BGRA;
1542 *internal = GL_RGBA;
1543 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1545 break;
1547 case WINED3DFMT_R8G8B8:
1548 if (colorkey_active) {
1549 *convert = CONVERT_CK_RGB24;
1550 *format = GL_RGBA;
1551 *internal = GL_RGBA;
1552 *type = GL_UNSIGNED_INT_8_8_8_8;
1553 *target_bpp = 4;
1555 break;
1557 case WINED3DFMT_X8R8G8B8:
1558 if (colorkey_active) {
1559 *convert = CONVERT_RGB32_888;
1560 *format = GL_RGBA;
1561 *internal = GL_RGBA;
1562 *type = GL_UNSIGNED_INT_8_8_8_8;
1564 break;
1566 case WINED3DFMT_V8U8:
1567 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1568 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1569 *format = GL_DUDV_ATI;
1570 *internal = GL_DU8DV8_ATI;
1571 *type = GL_BYTE;
1572 /* No conversion - Just change the gl type */
1573 break;
1575 *convert = CONVERT_V8U8;
1576 *format = GL_BGR;
1577 *internal = GL_RGB8;
1578 *type = GL_UNSIGNED_BYTE;
1579 *target_bpp = 3;
1580 break;
1582 case WINED3DFMT_L6V5U5:
1583 *convert = CONVERT_L6V5U5;
1584 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1585 *target_bpp = 3;
1586 /* Use format and types from table */
1587 } else {
1588 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1589 *target_bpp = 2;
1590 *format = GL_RGB;
1591 *internal = GL_RGB5;
1592 *type = GL_UNSIGNED_SHORT_5_6_5;
1594 break;
1596 case WINED3DFMT_X8L8V8U8:
1597 *convert = CONVERT_X8L8V8U8;
1598 *target_bpp = 4;
1599 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1600 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1601 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1602 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1603 * the needed type and format parameter, so the internal format contains a
1604 * 4th component, which is returned as alpha
1606 } else {
1607 /* Not supported by GL_ATI_envmap_bumpmap */
1608 *format = GL_BGRA;
1609 *internal = GL_RGB8;
1610 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1612 break;
1614 case WINED3DFMT_Q8W8V8U8:
1615 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1616 *convert = CONVERT_Q8W8V8U8;
1617 *format = GL_BGRA;
1618 *internal = GL_RGBA8;
1619 *type = GL_UNSIGNED_BYTE;
1620 *target_bpp = 4;
1621 /* Not supported by GL_ATI_envmap_bumpmap */
1622 break;
1624 case WINED3DFMT_V16U16:
1625 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1626 *convert = CONVERT_V16U16;
1627 *format = GL_BGR;
1628 *internal = GL_RGB16_EXT;
1629 *type = GL_UNSIGNED_SHORT;
1630 *target_bpp = 6;
1631 /* What should I do here about GL_ATI_envmap_bumpmap?
1632 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1634 break;
1636 case WINED3DFMT_A4L4:
1637 /* A4L4 exists as an internal gl format, but for some reason there is not
1638 * format+type combination to load it. Thus convert it to A8L8, then load it
1639 * with A4L4 internal, but A8L8 format+type
1641 *convert = CONVERT_A4L4;
1642 *format = GL_LUMINANCE_ALPHA;
1643 *internal = GL_LUMINANCE4_ALPHA4;
1644 *type = GL_UNSIGNED_BYTE;
1645 *target_bpp = 2;
1646 break;
1648 case WINED3DFMT_R32F:
1649 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1650 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1651 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1652 * 1.0 instead.
1654 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1656 *convert = CONVERT_R32F;
1657 *format = GL_RGB;
1658 *internal = GL_RGB32F_ARB;
1659 *type = GL_FLOAT;
1660 *target_bpp = 12;
1661 break;
1663 case WINED3DFMT_R16F:
1664 /* Similar to R32F */
1665 *convert = CONVERT_R16F;
1666 *format = GL_RGB;
1667 *internal = GL_RGB16F_ARB;
1668 *type = GL_HALF_FLOAT_ARB;
1669 *target_bpp = 6;
1670 break;
1672 case WINED3DFMT_G16R16:
1673 *convert = CONVERT_G16R16;
1674 *format = GL_RGB;
1675 *internal = GL_RGB16_EXT;
1676 *type = GL_UNSIGNED_SHORT;
1677 *target_bpp = 6;
1678 break;
1680 default:
1681 break;
1684 return WINED3D_OK;
1687 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1688 BYTE *source, *dest;
1689 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1691 switch (convert) {
1692 case NO_CONVERSION:
1694 memcpy(dst, src, pitch * height);
1695 break;
1697 case CONVERT_PALETTED:
1698 case CONVERT_PALETTED_CK:
1700 IWineD3DPaletteImpl* pal = This->palette;
1701 BYTE table[256][4];
1702 unsigned int x, y;
1704 if( pal == NULL) {
1705 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1708 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1710 for (y = 0; y < height; y++)
1712 source = src + pitch * y;
1713 dest = dst + outpitch * y;
1714 /* This is an 1 bpp format, using the width here is fine */
1715 for (x = 0; x < width; x++) {
1716 BYTE color = *source++;
1717 *dest++ = table[color][0];
1718 *dest++ = table[color][1];
1719 *dest++ = table[color][2];
1720 *dest++ = table[color][3];
1724 break;
1726 case CONVERT_CK_565:
1728 /* Converting the 565 format in 5551 packed to emulate color-keying.
1730 Note : in all these conversion, it would be best to average the averaging
1731 pixels to get the color of the pixel that will be color-keyed to
1732 prevent 'color bleeding'. This will be done later on if ever it is
1733 too visible.
1735 Note2: Nvidia documents say that their driver does not support alpha + color keying
1736 on the same surface and disables color keying in such a case
1738 unsigned int x, y;
1739 WORD *Source;
1740 WORD *Dest;
1742 TRACE("Color keyed 565\n");
1744 for (y = 0; y < height; y++) {
1745 Source = (WORD *) (src + y * pitch);
1746 Dest = (WORD *) (dst + y * outpitch);
1747 for (x = 0; x < width; x++ ) {
1748 WORD color = *Source++;
1749 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1750 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1751 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1752 *Dest |= 0x0001;
1754 Dest++;
1758 break;
1760 case CONVERT_CK_5551:
1762 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1763 unsigned int x, y;
1764 WORD *Source;
1765 WORD *Dest;
1766 TRACE("Color keyed 5551\n");
1767 for (y = 0; y < height; y++) {
1768 Source = (WORD *) (src + y * pitch);
1769 Dest = (WORD *) (dst + y * outpitch);
1770 for (x = 0; x < width; x++ ) {
1771 WORD color = *Source++;
1772 *Dest = color;
1773 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1774 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1775 *Dest |= (1 << 15);
1777 else {
1778 *Dest &= ~(1 << 15);
1780 Dest++;
1784 break;
1786 case CONVERT_V8U8:
1788 unsigned int x, y;
1789 short *Source;
1790 unsigned char *Dest;
1791 for(y = 0; y < height; y++) {
1792 Source = (short *) (src + y * pitch);
1793 Dest = dst + y * outpitch;
1794 for (x = 0; x < width; x++ ) {
1795 long color = (*Source++);
1796 /* B */ Dest[0] = 0xff;
1797 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1798 /* R */ Dest[2] = (color) + 128; /* U */
1799 Dest += 3;
1802 break;
1805 case CONVERT_V16U16:
1807 unsigned int x, y;
1808 DWORD *Source;
1809 unsigned short *Dest;
1810 for(y = 0; y < height; y++) {
1811 Source = (DWORD *) (src + y * pitch);
1812 Dest = (unsigned short *) (dst + y * outpitch);
1813 for (x = 0; x < width; x++ ) {
1814 DWORD color = (*Source++);
1815 /* B */ Dest[0] = 0xffff;
1816 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1817 /* R */ Dest[2] = (color ) + 32768; /* U */
1818 Dest += 3;
1821 break;
1824 case CONVERT_Q8W8V8U8:
1826 unsigned int x, y;
1827 DWORD *Source;
1828 unsigned char *Dest;
1829 for(y = 0; y < height; y++) {
1830 Source = (DWORD *) (src + y * pitch);
1831 Dest = dst + y * outpitch;
1832 for (x = 0; x < width; x++ ) {
1833 long color = (*Source++);
1834 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1835 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1836 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1837 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1838 Dest += 4;
1841 break;
1844 case CONVERT_L6V5U5:
1846 unsigned int x, y;
1847 WORD *Source;
1848 unsigned char *Dest;
1850 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1851 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1852 * fixed function and shaders without further conversion once the surface is
1853 * loaded
1855 for(y = 0; y < height; y++) {
1856 Source = (WORD *) (src + y * pitch);
1857 Dest = dst + y * outpitch;
1858 for (x = 0; x < width; x++ ) {
1859 short color = (*Source++);
1860 unsigned char l = ((color >> 10) & 0xfc);
1861 char v = ((color >> 5) & 0x3e);
1862 char u = ((color ) & 0x1f);
1864 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1865 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1866 * shift. GL reads a signed value and converts it into an unsigned value.
1868 /* M */ Dest[2] = l << 1;
1870 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1871 * from 5 bit values to 8 bit values.
1873 /* V */ Dest[1] = v << 3;
1874 /* U */ Dest[0] = u << 3;
1875 Dest += 3;
1878 } else {
1879 for(y = 0; y < height; y++) {
1880 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1881 Source = (WORD *) (src + y * pitch);
1882 for (x = 0; x < width; x++ ) {
1883 short color = (*Source++);
1884 unsigned char l = ((color >> 10) & 0xfc);
1885 short v = ((color >> 5) & 0x3e);
1886 short u = ((color ) & 0x1f);
1887 short v_conv = v + 16;
1888 short u_conv = u + 16;
1890 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1891 Dest_s += 1;
1895 break;
1898 case CONVERT_X8L8V8U8:
1900 unsigned int x, y;
1901 DWORD *Source;
1902 unsigned char *Dest;
1904 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1905 /* This implementation works with the fixed function pipeline and shaders
1906 * without further modification after converting the surface.
1908 for(y = 0; y < height; y++) {
1909 Source = (DWORD *) (src + y * pitch);
1910 Dest = dst + y * outpitch;
1911 for (x = 0; x < width; x++ ) {
1912 long color = (*Source++);
1913 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1914 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1915 /* U */ Dest[0] = (color & 0xff); /* U */
1916 /* I */ Dest[3] = 255; /* X */
1917 Dest += 4;
1920 } else {
1921 /* Doesn't work correctly with the fixed function pipeline, but can work in
1922 * shaders if the shader is adjusted. (There's no use for this format in gl's
1923 * standard fixed function pipeline anyway).
1925 for(y = 0; y < height; y++) {
1926 Source = (DWORD *) (src + y * pitch);
1927 Dest = dst + y * outpitch;
1928 for (x = 0; x < width; x++ ) {
1929 long color = (*Source++);
1930 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1931 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1932 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1933 Dest += 4;
1937 break;
1940 case CONVERT_A4L4:
1942 unsigned int x, y;
1943 unsigned char *Source;
1944 unsigned char *Dest;
1945 for(y = 0; y < height; y++) {
1946 Source = src + y * pitch;
1947 Dest = dst + y * outpitch;
1948 for (x = 0; x < width; x++ ) {
1949 unsigned char color = (*Source++);
1950 /* A */ Dest[1] = (color & 0xf0) << 0;
1951 /* L */ Dest[0] = (color & 0x0f) << 4;
1952 Dest += 2;
1955 break;
1958 case CONVERT_R32F:
1960 unsigned int x, y;
1961 float *Source;
1962 float *Dest;
1963 for(y = 0; y < height; y++) {
1964 Source = (float *) (src + y * pitch);
1965 Dest = (float *) (dst + y * outpitch);
1966 for (x = 0; x < width; x++ ) {
1967 float color = (*Source++);
1968 Dest[0] = color;
1969 Dest[1] = 1.0;
1970 Dest[2] = 1.0;
1971 Dest += 3;
1974 break;
1977 case CONVERT_R16F:
1979 unsigned int x, y;
1980 WORD *Source;
1981 WORD *Dest;
1982 WORD one = 0x3c00;
1983 for(y = 0; y < height; y++) {
1984 Source = (WORD *) (src + y * pitch);
1985 Dest = (WORD *) (dst + y * outpitch);
1986 for (x = 0; x < width; x++ ) {
1987 WORD color = (*Source++);
1988 Dest[0] = color;
1989 Dest[1] = one;
1990 Dest[2] = one;
1991 Dest += 3;
1994 break;
1997 case CONVERT_G16R16:
1999 unsigned int x, y;
2000 WORD *Source;
2001 WORD *Dest;
2003 for(y = 0; y < height; y++) {
2004 Source = (WORD *) (src + y * pitch);
2005 Dest = (WORD *) (dst + y * outpitch);
2006 for (x = 0; x < width; x++ ) {
2007 WORD green = (*Source++);
2008 WORD red = (*Source++);
2009 Dest[0] = green;
2010 Dest[1] = red;
2011 Dest[2] = 0xffff;
2012 Dest += 3;
2015 break;
2018 default:
2019 ERR("Unsupported conversation type %d\n", convert);
2021 return WINED3D_OK;
2024 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2025 IWineD3DPaletteImpl* pal = This->palette;
2026 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2027 BOOL index_in_alpha = FALSE;
2028 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2029 int i;
2031 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2032 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2033 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2034 * duplicate entries. Store the color key in the unused alpha component to speed the
2035 * download up and to make conversion unneeded. */
2036 index_in_alpha = primary_render_target_is_p8(device);
2038 if (pal == NULL) {
2039 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2040 if(dxVersion <= 7) {
2041 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2042 return;
2045 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2046 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2047 capability flag is present (wine does advertise this capability) */
2048 for (i = 0; i < 256; i++) {
2049 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2050 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2051 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2052 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2054 } else {
2055 TRACE("Using surface palette %p\n", pal);
2056 /* Get the surface's palette */
2057 for (i = 0; i < 256; i++) {
2058 table[i][0] = pal->palents[i].peRed;
2059 table[i][1] = pal->palents[i].peGreen;
2060 table[i][2] = pal->palents[i].peBlue;
2062 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2063 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2064 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2065 of pixels that should be masked away is set to 0. */
2066 if(index_in_alpha) {
2067 table[i][3] = i;
2068 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2069 table[i][3] = 0x00;
2070 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2071 table[i][3] = pal->palents[i].peFlags;
2072 } else {
2073 table[i][3] = 0xFF;
2079 const char *fragment_palette_conversion =
2080 "!!ARBfp1.0\n"
2081 "TEMP index;\n"
2082 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
2083 "TEX index, fragment.texcoord[0], texture[0], 2D;\n" /* The alpha-component contains the palette index */
2084 "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 */
2085 "TEX result.color, index.a, texture[1], 1D;\n" /* use the alpha-component as a index in the palette to get the final color */
2086 "END";
2088 /* This function is used in case of 8bit paletted textures to upload the palette.
2089 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2090 extensions like ATI_fragment_shaders is possible.
2092 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2093 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2094 BYTE table[256][4];
2095 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2097 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2099 /* Try to use the paletted texture extension */
2100 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2102 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2103 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2105 else
2107 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2108 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2109 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2111 /* Create the fragment program if we don't have it */
2112 if(!device->paletteConversionShader)
2114 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2115 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2116 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2117 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2118 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2121 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2122 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2124 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2125 glEnable(GL_TEXTURE_1D);
2126 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2128 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2129 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2130 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2131 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2133 /* Switch back to unit 0 in which the 2D texture will be stored. */
2134 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2136 /* Rebind the texture because it isn't bound anymore */
2137 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2141 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2142 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2144 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2145 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2146 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2148 return FALSE;
2151 if(This->palette9) {
2152 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2153 return FALSE;
2155 } else {
2156 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2158 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2159 return TRUE;
2162 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2163 GLboolean oldwrite[4];
2165 /* Some formats have only some color channels, and the others are 1.0.
2166 * since our rendering renders to all channels, and those pixel formats
2167 * are emulated by using a full texture with the other channels set to 1.0
2168 * manually, clear the unused channels.
2170 * This could be done with hacking colorwriteenable to mask the colors,
2171 * but before drawing the buffer would have to be cleared too, so there's
2172 * no gain in that
2174 switch(This->resource.format) {
2175 case WINED3DFMT_R16F:
2176 case WINED3DFMT_R32F:
2177 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2178 /* Do not activate a context, the correct drawable is active already
2179 * though just the read buffer is set, make sure to have the correct draw
2180 * buffer too
2182 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2183 glDisable(GL_SCISSOR_TEST);
2184 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2185 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2186 glClearColor(0.0, 1.0, 1.0, 1.0);
2187 glClear(GL_COLOR_BUFFER_BIT);
2188 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2189 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2190 checkGLcall("Unused channel clear\n");
2191 break;
2193 default: break;
2197 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2198 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2200 if (!(This->Flags & SFLAG_INTEXTURE)) {
2201 TRACE("Reloading because surface is dirty\n");
2202 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2203 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2204 /* Reload: vice versa OR */
2205 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2206 /* Also reload: Color key is active AND the color key has changed */
2207 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2208 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2209 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2210 TRACE("Reloading because of color keying\n");
2211 /* To perform the color key conversion we need a sysmem copy of
2212 * the surface. Make sure we have it
2215 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2216 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2217 /* TODO: This is not necessarily needed with hw palettized texture support */
2218 This->Flags &= ~SFLAG_INTEXTURE;
2219 } else if(palette9_changed(This)) {
2220 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2221 /* TODO: This is not necessarily needed with hw palettized texture support */
2222 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2224 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2225 This->Flags &= ~SFLAG_INTEXTURE;
2226 } else {
2227 TRACE("surface is already in texture\n");
2228 return WINED3D_OK;
2231 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2232 * These resources are not bound by device size or format restrictions. Because of this,
2233 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2234 * However, these resources can always be created, locked, and copied.
2236 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2238 FIXME("(%p) Operation not supported for scratch textures\n",This);
2239 return WINED3DERR_INVALIDCALL;
2242 This->srgb = srgb_mode;
2243 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2245 #if 0
2247 static unsigned int gen = 0;
2248 char buffer[4096];
2249 ++gen;
2250 if ((gen % 10) == 0) {
2251 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2252 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2255 * debugging crash code
2256 if (gen == 250) {
2257 void** test = NULL;
2258 *test = 0;
2262 #endif
2264 if (!(This->Flags & SFLAG_DONOTFREE)) {
2265 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2266 This->resource.allocatedMemory = NULL;
2267 This->resource.heapMemory = NULL;
2268 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2271 return WINED3D_OK;
2274 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2275 /* TODO: check for locks */
2276 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2277 IWineD3DBaseTexture *baseTexture = NULL;
2278 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2280 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2281 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2282 TRACE("Passing to container\n");
2283 IWineD3DBaseTexture_BindTexture(baseTexture);
2284 IWineD3DBaseTexture_Release(baseTexture);
2285 } else {
2286 TRACE("(%p) : Binding surface\n", This);
2288 if(!device->isInDraw) {
2289 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2291 ENTER_GL();
2292 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2293 LEAVE_GL();
2295 return;
2298 #include <errno.h>
2299 #include <stdio.h>
2300 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2301 FILE* f = NULL;
2302 UINT i, y;
2303 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2304 char *allocatedMemory;
2305 char *textureRow;
2306 IWineD3DSwapChain *swapChain = NULL;
2307 int width, height;
2308 GLuint tmpTexture = 0;
2309 DWORD color;
2310 /*FIXME:
2311 Textures may not be stored in ->allocatedgMemory and a GlTexture
2312 so we should lock the surface before saving a snapshot, or at least check that
2314 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2315 by calling GetTexImage and in compressed form by calling
2316 GetCompressedTexImageARB. Queried compressed images can be saved and
2317 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2318 texture images do not need to be processed by the GL and should
2319 significantly improve texture loading performance relative to uncompressed
2320 images. */
2322 /* Setup the width and height to be the internal texture width and height. */
2323 width = This->pow2Width;
2324 height = This->pow2Height;
2325 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2326 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2328 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2329 /* if were not a real texture then read the back buffer into a real texture */
2330 /* we don't want to interfere with the back buffer so read the data into a temporary
2331 * texture and then save the data out of the temporary texture
2333 GLint prevRead;
2334 ENTER_GL();
2335 TRACE("(%p) Reading render target into texture\n", This);
2336 glEnable(GL_TEXTURE_2D);
2338 glGenTextures(1, &tmpTexture);
2339 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2341 glTexImage2D(GL_TEXTURE_2D,
2343 GL_RGBA,
2344 width,
2345 height,
2346 0/*border*/,
2347 GL_RGBA,
2348 GL_UNSIGNED_INT_8_8_8_8_REV,
2349 NULL);
2351 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2352 vcheckGLcall("glGetIntegerv");
2353 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2354 vcheckGLcall("glReadBuffer");
2355 glCopyTexImage2D(GL_TEXTURE_2D,
2357 GL_RGBA,
2360 width,
2361 height,
2364 checkGLcall("glCopyTexImage2D");
2365 glReadBuffer(prevRead);
2366 LEAVE_GL();
2368 } else { /* bind the real texture, and make sure it up to date */
2369 IWineD3DSurface_PreLoad(iface);
2371 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2372 ENTER_GL();
2373 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2374 glGetTexImage(GL_TEXTURE_2D,
2375 This->glDescription.level,
2376 GL_RGBA,
2377 GL_UNSIGNED_INT_8_8_8_8_REV,
2378 allocatedMemory);
2379 checkGLcall("glTexImage2D");
2380 if (tmpTexture) {
2381 glBindTexture(GL_TEXTURE_2D, 0);
2382 glDeleteTextures(1, &tmpTexture);
2384 LEAVE_GL();
2386 f = fopen(filename, "w+");
2387 if (NULL == f) {
2388 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2389 return WINED3DERR_INVALIDCALL;
2391 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2392 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2393 /* TGA header */
2394 fputc(0,f);
2395 fputc(0,f);
2396 fputc(2,f);
2397 fputc(0,f);
2398 fputc(0,f);
2399 fputc(0,f);
2400 fputc(0,f);
2401 fputc(0,f);
2402 fputc(0,f);
2403 fputc(0,f);
2404 fputc(0,f);
2405 fputc(0,f);
2406 /* short width*/
2407 fwrite(&width,2,1,f);
2408 /* short height */
2409 fwrite(&height,2,1,f);
2410 /* format rgba */
2411 fputc(0x20,f);
2412 fputc(0x28,f);
2413 /* raw data */
2414 /* 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 */
2415 if(swapChain)
2416 textureRow = allocatedMemory + (width * (height - 1) *4);
2417 else
2418 textureRow = allocatedMemory;
2419 for (y = 0 ; y < height; y++) {
2420 for (i = 0; i < width; i++) {
2421 color = *((DWORD*)textureRow);
2422 fputc((color >> 16) & 0xFF, f); /* B */
2423 fputc((color >> 8) & 0xFF, f); /* G */
2424 fputc((color >> 0) & 0xFF, f); /* R */
2425 fputc((color >> 24) & 0xFF, f); /* A */
2426 textureRow += 4;
2428 /* take two rows of the pointer to the texture memory */
2429 if(swapChain)
2430 (textureRow-= width << 3);
2433 TRACE("Closing file\n");
2434 fclose(f);
2436 if(swapChain) {
2437 IWineD3DSwapChain_Release(swapChain);
2439 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2440 return WINED3D_OK;
2444 * Slightly inefficient way to handle multiple dirty rects but it works :)
2446 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2447 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2448 IWineD3DBaseTexture *baseTexture = NULL;
2450 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2451 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2453 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2454 if (NULL != pDirtyRect) {
2455 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2456 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2457 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2458 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2459 } else {
2460 This->dirtyRect.left = 0;
2461 This->dirtyRect.top = 0;
2462 This->dirtyRect.right = This->currentDesc.Width;
2463 This->dirtyRect.bottom = This->currentDesc.Height;
2465 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2466 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2467 /* if the container is a basetexture then mark it dirty. */
2468 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2469 TRACE("Passing to container\n");
2470 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2471 IWineD3DBaseTexture_Release(baseTexture);
2473 return WINED3D_OK;
2476 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2477 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2478 HRESULT hr;
2479 const GlPixelFormatDesc *glDesc;
2480 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2482 TRACE("(%p) : Calling base function first\n", This);
2483 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2484 if(SUCCEEDED(hr)) {
2485 /* Setup some glformat defaults */
2486 This->glDescription.glFormat = glDesc->glFormat;
2487 This->glDescription.glFormatInternal = glDesc->glInternal;
2488 This->glDescription.glType = glDesc->glType;
2490 This->Flags &= ~SFLAG_ALLOCATED;
2491 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2492 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2494 return hr;
2497 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2498 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2500 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2501 WARN("Surface is locked or the HDC is in use\n");
2502 return WINED3DERR_INVALIDCALL;
2505 if(Mem && Mem != This->resource.allocatedMemory) {
2506 void *release = NULL;
2508 /* Do I have to copy the old surface content? */
2509 if(This->Flags & SFLAG_DIBSECTION) {
2510 /* Release the DC. No need to hold the critical section for the update
2511 * Thread because this thread runs only on front buffers, but this method
2512 * fails for render targets in the check above.
2514 SelectObject(This->hDC, This->dib.holdbitmap);
2515 DeleteDC(This->hDC);
2516 /* Release the DIB section */
2517 DeleteObject(This->dib.DIBsection);
2518 This->dib.bitmap_data = NULL;
2519 This->resource.allocatedMemory = NULL;
2520 This->hDC = NULL;
2521 This->Flags &= ~SFLAG_DIBSECTION;
2522 } else if(!(This->Flags & SFLAG_USERPTR)) {
2523 release = This->resource.heapMemory;
2524 This->resource.heapMemory = NULL;
2526 This->resource.allocatedMemory = Mem;
2527 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2529 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2530 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2532 /* For client textures opengl has to be notified */
2533 if(This->Flags & SFLAG_CLIENT) {
2534 This->Flags &= ~SFLAG_ALLOCATED;
2535 IWineD3DSurface_PreLoad(iface);
2536 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2539 /* Now free the old memory if any */
2540 HeapFree(GetProcessHeap(), 0, release);
2541 } else if(This->Flags & SFLAG_USERPTR) {
2542 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2543 This->resource.allocatedMemory = NULL;
2544 /* HeapMemory should be NULL already */
2545 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2546 This->Flags &= ~SFLAG_USERPTR;
2548 if(This->Flags & SFLAG_CLIENT) {
2549 This->Flags &= ~SFLAG_ALLOCATED;
2550 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2551 IWineD3DSurface_PreLoad(iface);
2554 return WINED3D_OK;
2557 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2558 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2559 IWineD3DSwapChainImpl *swapchain = NULL;
2560 HRESULT hr;
2561 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2563 /* Flipping is only supported on RenderTargets */
2564 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2566 if(override) {
2567 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2568 * FIXME("(%p) Target override is not supported by now\n", This);
2569 * Additionally, it isn't really possible to support triple-buffering
2570 * properly on opengl at all
2574 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2575 if(!swapchain) {
2576 ERR("Flipped surface is not on a swapchain\n");
2577 return WINEDDERR_NOTFLIPPABLE;
2580 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2581 * and only d3d8 and d3d9 apps specify the presentation interval
2583 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2584 /* Most common case first to avoid wasting time on all the other cases */
2585 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2586 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2587 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2588 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2589 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2590 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2591 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2592 } else {
2593 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2596 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2597 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2598 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2599 return hr;
2602 /* Does a direct frame buffer -> texture copy. Stretching is done
2603 * with single pixel copy calls
2605 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2606 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2607 float xrel, yrel;
2608 UINT row;
2609 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2612 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2613 ENTER_GL();
2614 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2616 /* TODO: Do we need GL_TEXTURE_2D enabled fpr copyteximage? */
2617 glEnable(This->glDescription.target);
2618 checkGLcall("glEnable(This->glDescription.target)");
2620 /* Bind the target texture */
2621 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2622 checkGLcall("glBindTexture");
2623 if(!swapchain) {
2624 glReadBuffer(myDevice->offscreenBuffer);
2625 } else {
2626 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2627 glReadBuffer(buffer);
2629 checkGLcall("glReadBuffer");
2631 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2632 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2634 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2635 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2637 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2638 ERR("Texture filtering not supported in direct blit\n");
2640 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2641 ERR("Texture filtering not supported in direct blit\n");
2644 if(upsidedown &&
2645 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2646 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2647 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2649 glCopyTexSubImage2D(This->glDescription.target,
2650 This->glDescription.level,
2651 drect->x1, drect->y1, /* xoffset, yoffset */
2652 srect->x1, Src->currentDesc.Height - srect->y2,
2653 drect->x2 - drect->x1, drect->y2 - drect->y1);
2654 } else {
2655 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2656 /* I have to process this row by row to swap the image,
2657 * otherwise it would be upside down, so stretching in y direction
2658 * doesn't cost extra time
2660 * However, stretching in x direction can be avoided if not necessary
2662 for(row = drect->y1; row < drect->y2; row++) {
2663 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2664 /* Well, that stuff works, but it's very slow.
2665 * find a better way instead
2667 UINT col;
2669 for(col = drect->x1; col < drect->x2; col++) {
2670 glCopyTexSubImage2D(This->glDescription.target,
2671 This->glDescription.level,
2672 drect->x1 + col, row, /* xoffset, yoffset */
2673 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2674 1, 1);
2676 } else {
2677 glCopyTexSubImage2D(This->glDescription.target,
2678 This->glDescription.level,
2679 drect->x1, row, /* xoffset, yoffset */
2680 srect->x1, yoffset - (int) (row * yrel),
2681 drect->x2-drect->x1, 1);
2685 vcheckGLcall("glCopyTexSubImage2D");
2687 /* Leave the opengl state valid for blitting */
2688 glDisable(This->glDescription.target);
2689 checkGLcall("glDisable(This->glDescription.target)");
2691 LEAVE_GL();
2694 /* Uses the hardware to stretch and flip the image */
2695 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2696 GLuint src, backup = 0;
2697 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2698 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2699 float left, right, top, bottom; /* Texture coordinates */
2700 UINT fbwidth = Src->currentDesc.Width;
2701 UINT fbheight = Src->currentDesc.Height;
2702 GLenum drawBuffer = GL_BACK;
2703 GLenum texture_target;
2705 TRACE("Using hwstretch blit\n");
2706 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2707 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2708 ENTER_GL();
2710 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2712 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2713 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2715 if(GL_LIMITS(aux_buffers) >= 2) {
2716 /* Got more than one aux buffer? Use the 2nd aux buffer */
2717 drawBuffer = GL_AUX1;
2718 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2719 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2720 drawBuffer = GL_AUX0;
2723 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2724 glGenTextures(1, &backup);
2725 checkGLcall("glGenTextures\n");
2726 glBindTexture(GL_TEXTURE_2D, backup);
2727 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2728 texture_target = GL_TEXTURE_2D;
2729 } else {
2730 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2731 * we are reading from the back buffer, the backup can be used as source texture
2733 if(Src->glDescription.textureName == 0) {
2734 /* Get it a description */
2735 IWineD3DSurface_PreLoad(SrcSurface);
2737 texture_target = Src->glDescription.target;
2738 glBindTexture(texture_target, Src->glDescription.textureName);
2739 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2740 glEnable(texture_target);
2741 checkGLcall("glEnable(texture_target)");
2743 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2744 Src->Flags &= ~SFLAG_INTEXTURE;
2747 glReadBuffer(GL_BACK);
2748 checkGLcall("glReadBuffer(GL_BACK)");
2750 /* TODO: Only back up the part that will be overwritten */
2751 glCopyTexSubImage2D(texture_target, 0,
2752 0, 0 /* read offsets */,
2753 0, 0,
2754 fbwidth,
2755 fbheight);
2757 checkGLcall("glCopyTexSubImage2D");
2759 /* No issue with overriding these - the sampler is dirty due to blit usage */
2760 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2761 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2762 checkGLcall("glTexParameteri");
2763 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2764 minMipLookup[Filter][WINED3DTEXF_NONE]);
2765 checkGLcall("glTexParameteri");
2767 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2768 src = backup ? backup : Src->glDescription.textureName;
2769 } else {
2770 glReadBuffer(GL_FRONT);
2771 checkGLcall("glReadBuffer(GL_FRONT)");
2773 glGenTextures(1, &src);
2774 checkGLcall("glGenTextures(1, &src)");
2775 glBindTexture(GL_TEXTURE_2D, src);
2776 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2778 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2779 * out for power of 2 sizes
2781 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2782 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2783 checkGLcall("glTexImage2D");
2784 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2785 0, 0 /* read offsets */,
2786 0, 0,
2787 fbwidth,
2788 fbheight);
2790 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2791 checkGLcall("glTexParameteri");
2792 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2793 checkGLcall("glTexParameteri");
2795 glReadBuffer(GL_BACK);
2796 checkGLcall("glReadBuffer(GL_BACK)");
2798 if(texture_target != GL_TEXTURE_2D) {
2799 glDisable(texture_target);
2800 glEnable(GL_TEXTURE_2D);
2801 texture_target = GL_TEXTURE_2D;
2804 checkGLcall("glEnd and previous");
2806 left = (float) srect->x1 / (float) Src->pow2Width;
2807 right = (float) srect->x2 / (float) Src->pow2Width;
2809 if(upsidedown) {
2810 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2811 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2812 } else {
2813 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2814 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2817 /* draw the source texture stretched and upside down. The correct surface is bound already */
2818 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
2819 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
2821 glDrawBuffer(drawBuffer);
2822 glReadBuffer(drawBuffer);
2824 glBegin(GL_QUADS);
2825 /* bottom left */
2826 glTexCoord2f(left, bottom);
2827 glVertex2i(0, fbheight);
2829 /* top left */
2830 glTexCoord2f(left, top);
2831 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2833 /* top right */
2834 glTexCoord2f(right, top);
2835 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2837 /* bottom right */
2838 glTexCoord2f(right, bottom);
2839 glVertex2i(drect->x2 - drect->x1, fbheight);
2840 glEnd();
2841 checkGLcall("glEnd and previous");
2843 if(texture_target != This->glDescription.target) {
2844 glDisable(texture_target);
2845 glEnable(This->glDescription.target);
2846 texture_target = This->glDescription.target;
2849 /* Now read the stretched and upside down image into the destination texture */
2850 glBindTexture(texture_target, This->glDescription.textureName);
2851 checkGLcall("glBindTexture");
2852 glCopyTexSubImage2D(texture_target,
2854 drect->x1, drect->y1, /* xoffset, yoffset */
2855 0, 0, /* We blitted the image to the origin */
2856 drect->x2 - drect->x1, drect->y2 - drect->y1);
2857 checkGLcall("glCopyTexSubImage2D");
2859 if(drawBuffer == GL_BACK) {
2860 /* Write the back buffer backup back */
2861 if(backup) {
2862 if(texture_target != GL_TEXTURE_2D) {
2863 glDisable(texture_target);
2864 glEnable(GL_TEXTURE_2D);
2865 texture_target = GL_TEXTURE_2D;
2867 glBindTexture(GL_TEXTURE_2D, backup);
2868 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
2869 } else {
2870 if(texture_target != Src->glDescription.target) {
2871 glDisable(texture_target);
2872 glEnable(Src->glDescription.target);
2873 texture_target = Src->glDescription.target;
2875 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
2876 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2879 glBegin(GL_QUADS);
2880 /* top left */
2881 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2882 glVertex2i(0, 0);
2884 /* bottom left */
2885 glTexCoord2f(0.0, 0.0);
2886 glVertex2i(0, fbheight);
2888 /* bottom right */
2889 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2890 glVertex2i(fbwidth, Src->currentDesc.Height);
2892 /* top right */
2893 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2894 glVertex2i(fbwidth, 0);
2895 glEnd();
2896 } else {
2897 /* Restore the old draw buffer */
2898 glDrawBuffer(GL_BACK);
2900 glDisable(texture_target);
2901 checkGLcall("glDisable(texture_target)");
2903 /* Cleanup */
2904 if(src != Src->glDescription.textureName && src != backup) {
2905 glDeleteTextures(1, &src);
2906 checkGLcall("glDeleteTextures(1, &src)");
2908 if(backup) {
2909 glDeleteTextures(1, &backup);
2910 checkGLcall("glDeleteTextures(1, &backup)");
2913 LEAVE_GL();
2916 /* Not called from the VTable */
2917 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2918 WINED3DRECT rect;
2919 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2920 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2921 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2923 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2925 /* Get the swapchain. One of the surfaces has to be a primary surface */
2926 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2927 WARN("Destination is in sysmem, rejecting gl blt\n");
2928 return WINED3DERR_INVALIDCALL;
2930 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2931 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2932 if(Src) {
2933 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2934 WARN("Src is in sysmem, rejecting gl blt\n");
2935 return WINED3DERR_INVALIDCALL;
2937 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2938 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2941 /* Early sort out of cases where no render target is used */
2942 if(!dstSwapchain && !srcSwapchain &&
2943 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2944 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2945 return WINED3DERR_INVALIDCALL;
2948 /* No destination color keying supported */
2949 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2950 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2951 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2952 return WINED3DERR_INVALIDCALL;
2955 if (DestRect) {
2956 rect.x1 = DestRect->left;
2957 rect.y1 = DestRect->top;
2958 rect.x2 = DestRect->right;
2959 rect.y2 = DestRect->bottom;
2960 } else {
2961 rect.x1 = 0;
2962 rect.y1 = 0;
2963 rect.x2 = This->currentDesc.Width;
2964 rect.y2 = This->currentDesc.Height;
2967 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2968 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2969 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2970 /* Half-life does a Blt from the back buffer to the front buffer,
2971 * Full surface size, no flags... Use present instead
2973 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2976 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2977 while(1)
2979 RECT mySrcRect;
2980 TRACE("Looking if a Present can be done...\n");
2981 /* Source Rectangle must be full surface */
2982 if( SrcRect ) {
2983 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2984 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2985 TRACE("No, Source rectangle doesn't match\n");
2986 break;
2989 mySrcRect.left = 0;
2990 mySrcRect.top = 0;
2991 mySrcRect.right = Src->currentDesc.Width;
2992 mySrcRect.bottom = Src->currentDesc.Height;
2994 /* No stretching may occur */
2995 if(mySrcRect.right != rect.x2 - rect.x1 ||
2996 mySrcRect.bottom != rect.y2 - rect.y1) {
2997 TRACE("No, stretching is done\n");
2998 break;
3001 /* Destination must be full surface or match the clipping rectangle */
3002 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3004 RECT cliprect;
3005 POINT pos[2];
3006 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3007 pos[0].x = rect.x1;
3008 pos[0].y = rect.y1;
3009 pos[1].x = rect.x2;
3010 pos[1].y = rect.y2;
3011 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3012 pos, 2);
3014 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3015 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3017 TRACE("No, dest rectangle doesn't match(clipper)\n");
3018 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3019 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3020 break;
3023 else
3025 if(rect.x1 != 0 || rect.y1 != 0 ||
3026 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3027 TRACE("No, dest rectangle doesn't match(surface size)\n");
3028 break;
3032 TRACE("Yes\n");
3034 /* These flags are unimportant for the flag check, remove them */
3035 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3036 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3038 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3039 * take very long, while a flip is fast.
3040 * This applies to Half-Life, which does such Blts every time it finished
3041 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3042 * menu. This is also used by all apps when they do windowed rendering
3044 * The problem is that flipping is not really the same as copying. After a
3045 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3046 * untouched. Therefore it's necessary to override the swap effect
3047 * and to set it back after the flip.
3049 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3050 * testcases.
3053 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3054 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3056 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3057 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3059 dstSwapchain->presentParms.SwapEffect = orig_swap;
3061 return WINED3D_OK;
3063 break;
3066 TRACE("Unsupported blit between buffers on the same swapchain\n");
3067 return WINED3DERR_INVALIDCALL;
3068 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3069 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3070 return WINED3DERR_INVALIDCALL;
3071 } else if(dstSwapchain && srcSwapchain) {
3072 FIXME("Implement hardware blit between two different swapchains\n");
3073 return WINED3DERR_INVALIDCALL;
3074 } else if(dstSwapchain) {
3075 if(SrcSurface == myDevice->render_targets[0]) {
3076 TRACE("Blit from active render target to a swapchain\n");
3077 /* Handled with regular texture -> swapchain blit */
3079 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3080 FIXME("Implement blit from a swapchain to the active render target\n");
3081 return WINED3DERR_INVALIDCALL;
3084 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3085 /* Blit from render target to texture */
3086 WINED3DRECT srect;
3087 BOOL upsideDown, stretchx;
3089 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3090 TRACE("Color keying not supported by frame buffer to texture blit\n");
3091 return WINED3DERR_INVALIDCALL;
3092 /* Destination color key is checked above */
3095 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3096 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3098 if(SrcRect) {
3099 if(SrcRect->top < SrcRect->bottom) {
3100 srect.y1 = SrcRect->top;
3101 srect.y2 = SrcRect->bottom;
3102 upsideDown = FALSE;
3103 } else {
3104 srect.y1 = SrcRect->bottom;
3105 srect.y2 = SrcRect->top;
3106 upsideDown = TRUE;
3108 srect.x1 = SrcRect->left;
3109 srect.x2 = SrcRect->right;
3110 } else {
3111 srect.x1 = 0;
3112 srect.y1 = 0;
3113 srect.x2 = Src->currentDesc.Width;
3114 srect.y2 = Src->currentDesc.Height;
3115 upsideDown = FALSE;
3117 if(rect.x1 > rect.x2) {
3118 UINT tmp = rect.x2;
3119 rect.x2 = rect.x1;
3120 rect.x1 = tmp;
3121 upsideDown = !upsideDown;
3123 if(!srcSwapchain) {
3124 TRACE("Reading from an offscreen target\n");
3125 upsideDown = !upsideDown;
3128 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3129 stretchx = TRUE;
3130 } else {
3131 stretchx = FALSE;
3134 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3135 * flip the image nor scale it.
3137 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3138 * -> If the app wants a image width an unscaled width, copy it line per line
3139 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3140 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3141 * back buffer. This is slower than reading line per line, thus not used for flipping
3142 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3143 * pixel by pixel
3145 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3146 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3147 * backends.
3149 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3150 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3151 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3152 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3153 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3154 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3155 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3156 } else {
3157 TRACE("Using hardware stretching to flip / stretch the texture\n");
3158 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3161 if(!(This->Flags & SFLAG_DONOTFREE)) {
3162 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3163 This->resource.allocatedMemory = NULL;
3164 This->resource.heapMemory = NULL;
3165 } else {
3166 This->Flags &= ~SFLAG_INSYSMEM;
3168 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3169 * path is never entered
3171 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3173 return WINED3D_OK;
3174 } else if(Src) {
3175 /* Blit from offscreen surface to render target */
3176 float glTexCoord[4];
3177 DWORD oldCKeyFlags = Src->CKeyFlags;
3178 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
3179 RECT SourceRectangle;
3180 BOOL paletteOverride = FALSE;
3182 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3184 if(SrcRect) {
3185 SourceRectangle.left = SrcRect->left;
3186 SourceRectangle.right = SrcRect->right;
3187 SourceRectangle.top = SrcRect->top;
3188 SourceRectangle.bottom = SrcRect->bottom;
3189 } else {
3190 SourceRectangle.left = 0;
3191 SourceRectangle.right = Src->currentDesc.Width;
3192 SourceRectangle.top = 0;
3193 SourceRectangle.bottom = Src->currentDesc.Height;
3195 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3196 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3197 TRACE("Using stretch_rect_fbo\n");
3198 /* The source is always a texture, but never the currently active render target, and the texture
3199 * contents are never upside down
3201 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3202 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3203 return WINED3D_OK;
3206 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3207 /* Fall back to software */
3208 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3209 SourceRectangle.left, SourceRectangle.top,
3210 SourceRectangle.right, SourceRectangle.bottom);
3211 return WINED3DERR_INVALIDCALL;
3214 /* Color keying: Check if we have to do a color keyed blt,
3215 * and if not check if a color key is activated.
3217 * Just modify the color keying parameters in the surface and restore them afterwards
3218 * The surface keeps track of the color key last used to load the opengl surface.
3219 * PreLoad will catch the change to the flags and color key and reload if necessary.
3221 if(Flags & WINEDDBLT_KEYSRC) {
3222 /* Use color key from surface */
3223 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3224 /* Use color key from DDBltFx */
3225 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3226 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3227 } else {
3228 /* Do not use color key */
3229 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3232 /* When blitting from an offscreen surface to a rendertarget, the source
3233 * surface is not required to have a palette. Our rendering / conversion
3234 * code further down the road retrieves the palette from the surface, so
3235 * it must have a palette set. */
3236 if((Src->resource.format == WINED3DFMT_P8) && (Src->palette == NULL)) {
3237 paletteOverride = TRUE;
3238 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3239 Src->palette = This->palette;
3242 /* Now load the surface */
3243 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3246 /* Activate the destination context, set it up for blitting */
3247 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3248 ENTER_GL();
3250 glEnable(Src->glDescription.target);
3251 checkGLcall("glEnable(Src->glDescription.target)");
3253 if(!dstSwapchain) {
3254 TRACE("Drawing to offscreen buffer\n");
3255 glDrawBuffer(myDevice->offscreenBuffer);
3256 checkGLcall("glDrawBuffer");
3257 } else {
3258 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3259 TRACE("Drawing to %#x buffer\n", buffer);
3260 glDrawBuffer(buffer);
3261 checkGLcall("glDrawBuffer");
3264 /* Bind the texture */
3265 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3266 checkGLcall("glBindTexture");
3268 /* Filtering for StretchRect */
3269 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3270 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3271 checkGLcall("glTexParameteri");
3272 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3273 minMipLookup[Filter][WINED3DTEXF_NONE]);
3274 checkGLcall("glTexParameteri");
3275 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3276 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3277 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3278 checkGLcall("glTexEnvi");
3280 /* This is for color keying */
3281 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3282 glEnable(GL_ALPHA_TEST);
3283 checkGLcall("glEnable GL_ALPHA_TEST");
3285 /* When the primary render target uses P8, the alpha component contains the palette index.
3286 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3287 * should be masked away have alpha set to 0. */
3288 if(primary_render_target_is_p8(myDevice))
3289 glAlphaFunc(GL_NOTEQUAL, (float)This->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3290 else
3291 glAlphaFunc(GL_NOTEQUAL, 0.0);
3292 checkGLcall("glAlphaFunc\n");
3293 } else {
3294 glDisable(GL_ALPHA_TEST);
3295 checkGLcall("glDisable GL_ALPHA_TEST");
3298 /* Draw a textured quad
3300 glBegin(GL_QUADS);
3302 glColor3d(1.0f, 1.0f, 1.0f);
3303 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3304 glVertex3f(rect.x1,
3305 rect.y1,
3306 0.0);
3308 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3309 glVertex3f(rect.x1, rect.y2, 0.0);
3311 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3312 glVertex3f(rect.x2,
3313 rect.y2,
3314 0.0);
3316 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3317 glVertex3f(rect.x2,
3318 rect.y1,
3319 0.0);
3320 glEnd();
3321 checkGLcall("glEnd");
3323 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3324 glDisable(GL_ALPHA_TEST);
3325 checkGLcall("glDisable(GL_ALPHA_TEST)");
3328 /* Flush in case the drawable is used by multiple GL contexts */
3329 if(dstSwapchain && (dstSwapchain->num_contexts >= 2))
3330 glFlush();
3332 glBindTexture(Src->glDescription.target, 0);
3333 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3334 /* Leave the opengl state valid for blitting */
3335 glDisable(Src->glDescription.target);
3336 checkGLcall("glDisable(Src->glDescription.target)");
3338 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3339 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3341 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3342 glDrawBuffer(GL_BACK);
3343 checkGLcall("glDrawBuffer");
3345 /* Restore the color key parameters */
3346 Src->CKeyFlags = oldCKeyFlags;
3347 This->SrcBltCKey = oldBltCKey;
3349 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3350 if(paletteOverride)
3351 Src->palette = NULL;
3353 LEAVE_GL();
3355 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3356 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3357 * is outdated now
3359 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3360 /* TODO: This should be moved to ModifyLocation() */
3361 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3362 This->Flags |= SFLAG_INTEXTURE;
3365 return WINED3D_OK;
3366 } else {
3367 /* Source-Less Blit to render target */
3368 if (Flags & WINEDDBLT_COLORFILL) {
3369 /* This is easy to handle for the D3D Device... */
3370 DWORD color;
3372 TRACE("Colorfill\n");
3374 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3375 must be true if we are here */
3376 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3377 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3378 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3379 TRACE("Surface is higher back buffer, falling back to software\n");
3380 return WINED3DERR_INVALIDCALL;
3383 /* The color as given in the Blt function is in the format of the frame-buffer...
3384 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3386 if (This->resource.format == WINED3DFMT_P8) {
3387 if (This->palette) {
3388 color = ((0xFF000000) |
3389 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3390 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3391 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3392 } else {
3393 color = 0xFF000000;
3396 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3397 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3398 color = 0xFFFFFFFF;
3399 } else {
3400 color = ((0xFF000000) |
3401 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3402 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3403 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3406 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3407 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3408 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3410 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3411 color = DDBltFx->u5.dwFillColor;
3413 else {
3414 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3415 return WINED3DERR_INVALIDCALL;
3418 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3419 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3420 1, /* Number of rectangles */
3421 &rect, WINED3DCLEAR_TARGET, color,
3422 0.0 /* Z */,
3423 0 /* Stencil */);
3424 return WINED3D_OK;
3428 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3429 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3430 return WINED3DERR_INVALIDCALL;
3433 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3435 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3436 float depth;
3438 if (Flags & WINEDDBLT_DEPTHFILL) {
3439 switch(This->resource.format) {
3440 case WINED3DFMT_D16:
3441 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3442 break;
3443 case WINED3DFMT_D15S1:
3444 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3445 break;
3446 case WINED3DFMT_D24S8:
3447 case WINED3DFMT_D24X8:
3448 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3449 break;
3450 case WINED3DFMT_D32:
3451 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3452 break;
3453 default:
3454 depth = 0.0;
3455 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3458 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3459 DestRect == NULL ? 0 : 1,
3460 (WINED3DRECT *) DestRect,
3461 WINED3DCLEAR_ZBUFFER,
3462 0x00000000,
3463 depth,
3464 0x00000000);
3467 FIXME("(%p): Unsupp depthstencil blit\n", This);
3468 return WINED3DERR_INVALIDCALL;
3471 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3472 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3473 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3474 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3475 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3476 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3478 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3480 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3481 return WINEDDERR_SURFACEBUSY;
3484 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3485 * except depth blits, which seem to work
3487 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3488 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3489 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3490 return WINED3DERR_INVALIDCALL;
3491 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3492 TRACE("Z Blit override handled the blit\n");
3493 return WINED3D_OK;
3497 /* Special cases for RenderTargets */
3498 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3499 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3500 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3503 /* For the rest call the X11 surface implementation.
3504 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3505 * other Blts are rather rare
3507 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3510 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3511 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3512 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3513 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3514 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3516 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3518 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3519 return WINEDDERR_SURFACEBUSY;
3522 if(myDevice->inScene &&
3523 (iface == myDevice->stencilBufferTarget ||
3524 (Source && Source == myDevice->stencilBufferTarget))) {
3525 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3526 return WINED3DERR_INVALIDCALL;
3529 /* Special cases for RenderTargets */
3530 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3531 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3533 RECT SrcRect, DstRect;
3534 DWORD Flags=0;
3536 if(rsrc) {
3537 SrcRect.left = rsrc->left;
3538 SrcRect.top= rsrc->top;
3539 SrcRect.bottom = rsrc->bottom;
3540 SrcRect.right = rsrc->right;
3541 } else {
3542 SrcRect.left = 0;
3543 SrcRect.top = 0;
3544 SrcRect.right = srcImpl->currentDesc.Width;
3545 SrcRect.bottom = srcImpl->currentDesc.Height;
3548 DstRect.left = dstx;
3549 DstRect.top=dsty;
3550 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3551 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3553 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3554 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3555 Flags |= WINEDDBLT_KEYSRC;
3556 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3557 Flags |= WINEDDBLT_KEYDEST;
3558 if(trans & WINEDDBLTFAST_WAIT)
3559 Flags |= WINEDDBLT_WAIT;
3560 if(trans & WINEDDBLTFAST_DONOTWAIT)
3561 Flags |= WINEDDBLT_DONOTWAIT;
3563 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3567 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3570 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3571 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3572 RGBQUAD col[256];
3573 IWineD3DPaletteImpl *pal = This->palette;
3574 unsigned int n;
3575 TRACE("(%p)\n", This);
3577 if (!pal) return WINED3D_OK;
3579 if(This->resource.format == WINED3DFMT_P8 ||
3580 This->resource.format == WINED3DFMT_A8P8)
3582 if(!(This->Flags & SFLAG_INSYSMEM)) {
3583 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3584 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3586 TRACE("Dirtifying surface\n");
3587 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3590 if(This->Flags & SFLAG_DIBSECTION) {
3591 TRACE("(%p): Updating the hdc's palette\n", This);
3592 for (n=0; n<256; n++) {
3593 col[n].rgbRed = pal->palents[n].peRed;
3594 col[n].rgbGreen = pal->palents[n].peGreen;
3595 col[n].rgbBlue = pal->palents[n].peBlue;
3596 col[n].rgbReserved = 0;
3598 SetDIBColorTable(This->hDC, 0, 256, col);
3601 /* Propagate the changes to the drawable when we have a palette.
3602 * TODO: in case of hardware p8 palettes we should only upload the palette. */
3603 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3604 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3606 return WINED3D_OK;
3609 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3610 /** Check against the maximum texture sizes supported by the video card **/
3611 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3612 unsigned int pow2Width, pow2Height;
3613 const GlPixelFormatDesc *glDesc;
3615 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3616 /* Setup some glformat defaults */
3617 This->glDescription.glFormat = glDesc->glFormat;
3618 This->glDescription.glFormatInternal = glDesc->glInternal;
3619 This->glDescription.glType = glDesc->glType;
3621 This->glDescription.textureName = 0;
3622 This->glDescription.target = GL_TEXTURE_2D;
3624 /* Non-power2 support */
3625 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3626 pow2Width = This->currentDesc.Width;
3627 pow2Height = This->currentDesc.Height;
3628 } else {
3629 /* Find the nearest pow2 match */
3630 pow2Width = pow2Height = 1;
3631 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3632 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3634 This->pow2Width = pow2Width;
3635 This->pow2Height = pow2Height;
3637 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3638 WINED3DFORMAT Format = This->resource.format;
3639 /** TODO: add support for non power two compressed textures **/
3640 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3641 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3642 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3643 This, This->currentDesc.Width, This->currentDesc.Height);
3644 return WINED3DERR_NOTAVAILABLE;
3648 if(pow2Width != This->currentDesc.Width ||
3649 pow2Height != This->currentDesc.Height) {
3650 This->Flags |= SFLAG_NONPOW2;
3653 TRACE("%p\n", This);
3654 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3655 /* one of three options
3656 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)
3657 2: Set the texture to the maximum size (bad idea)
3658 3: WARN and return WINED3DERR_NOTAVAILABLE;
3659 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.
3661 WARN("(%p) Creating an oversized surface\n", This);
3662 This->Flags |= SFLAG_OVERSIZE;
3664 /* This will be initialized on the first blt */
3665 This->glRect.left = 0;
3666 This->glRect.top = 0;
3667 This->glRect.right = 0;
3668 This->glRect.bottom = 0;
3669 } else {
3670 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3671 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3672 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3673 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3675 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE) &&
3676 !((This->resource.format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE) && (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3678 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3679 This->pow2Width = This->currentDesc.Width;
3680 This->pow2Height = This->currentDesc.Height;
3681 This->Flags &= ~SFLAG_NONPOW2;
3684 /* No oversize, gl rect is the full texture size */
3685 This->Flags &= ~SFLAG_OVERSIZE;
3686 This->glRect.left = 0;
3687 This->glRect.top = 0;
3688 This->glRect.right = This->pow2Width;
3689 This->glRect.bottom = This->pow2Height;
3692 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3693 switch(wined3d_settings.offscreen_rendering_mode) {
3694 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3695 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3696 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3700 This->Flags |= SFLAG_INSYSMEM;
3702 return WINED3D_OK;
3705 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3706 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3707 IWineD3DBaseTexture *texture;
3709 TRACE("(%p)->(%s, %s)\n", iface,
3710 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3711 persistent ? "TRUE" : "FALSE");
3713 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3714 IWineD3DSwapChain *swapchain = NULL;
3716 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3717 TRACE("Surface %p is an onscreen surface\n", iface);
3719 IWineD3DSwapChain_Release(swapchain);
3720 } else {
3721 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3722 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3726 if(persistent) {
3727 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
3728 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3729 TRACE("Passing to container\n");
3730 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3731 IWineD3DBaseTexture_Release(texture);
3734 This->Flags &= ~SFLAG_LOCATIONS;
3735 This->Flags |= flag;
3736 } else {
3737 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
3738 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3739 TRACE("Passing to container\n");
3740 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3741 IWineD3DBaseTexture_Release(texture);
3744 This->Flags &= ~flag;
3748 struct coords {
3749 GLfloat x, y, z;
3752 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
3753 struct coords coords[4];
3754 RECT rect;
3755 IWineD3DSwapChain *swapchain = NULL;
3756 IWineD3DBaseTexture *texture = NULL;
3757 HRESULT hr;
3758 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3760 if(rect_in) {
3761 rect = *rect_in;
3762 } else {
3763 rect.left = 0;
3764 rect.top = 0;
3765 rect.right = This->currentDesc.Width;
3766 rect.bottom = This->currentDesc.Height;
3769 ActivateContext(device, device->render_targets[0], CTXUSAGE_BLIT);
3770 ENTER_GL();
3772 if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
3773 glEnable(GL_TEXTURE_RECTANGLE_ARB);
3774 checkGLcall("glEnable(GL_TEXTURE_RECTANGLE_ARB)");
3775 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName);
3776 checkGLcall("GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName)");
3777 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3778 checkGLcall("glTexParameteri");
3779 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3780 checkGLcall("glTexParameteri");
3782 coords[0].x = rect.left;
3783 coords[0].z = 0;
3785 coords[1].x = rect.left;
3786 coords[1].z = 0;
3788 coords[2].x = rect.right;
3789 coords[2].z = 0;
3791 coords[3].x = rect.right;
3792 coords[3].z = 0;
3794 coords[0].y = rect.top;
3795 coords[1].y = rect.bottom;
3796 coords[2].y = rect.bottom;
3797 coords[3].y = rect.top;
3798 } else if(This->glDescription.target == GL_TEXTURE_2D) {
3799 glEnable(GL_TEXTURE_2D);
3800 checkGLcall("glEnable(GL_TEXTURE_2D)");
3801 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
3802 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
3803 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3804 checkGLcall("glTexParameteri");
3805 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3806 checkGLcall("glTexParameteri");
3808 coords[0].x = (float)rect.left / This->pow2Width;
3809 coords[0].z = 0;
3811 coords[1].x = (float)rect.left / This->pow2Width;
3812 coords[1].z = 0;
3814 coords[2].x = (float)rect.right / This->pow2Width;
3815 coords[2].z = 0;
3817 coords[3].x = (float)rect.right / This->pow2Width;
3818 coords[3].z = 0;
3820 coords[0].y = (float)rect.top / This->pow2Height;
3821 coords[1].y = (float)rect.bottom / This->pow2Height;
3822 coords[2].y = (float)rect.bottom / This->pow2Height;
3823 coords[3].y = (float)rect.top / This->pow2Height;
3824 } else {
3825 /* Must be a cube map */
3826 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
3827 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
3828 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
3829 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
3830 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3831 checkGLcall("glTexParameteri");
3832 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3833 checkGLcall("glTexParameteri");
3835 switch(This->glDescription.target) {
3836 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3837 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
3838 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3839 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3840 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
3841 break;
3843 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3844 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3845 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
3846 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
3847 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3848 break;
3850 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3851 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
3852 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3853 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3854 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
3855 break;
3857 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3858 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3859 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3860 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3861 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3862 break;
3864 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3865 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3866 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3867 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
3868 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
3869 break;
3871 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
3872 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
3873 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
3874 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3875 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3876 break;
3878 default:
3879 ERR("Unexpected texture target\n");
3880 LEAVE_GL();
3881 return;
3885 glBegin(GL_QUADS);
3886 glTexCoord3fv(&coords[0].x);
3887 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
3889 glTexCoord3fv(&coords[1].x);
3890 glVertex2i(rect.left, device->render_offscreen ? rect.top : rect.bottom);
3892 glTexCoord3fv(&coords[2].x);
3893 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
3895 glTexCoord3fv(&coords[3].x);
3896 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
3897 glEnd();
3898 checkGLcall("glEnd");
3900 if(This->glDescription.target != GL_TEXTURE_2D) {
3901 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3902 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3903 } else {
3904 glDisable(GL_TEXTURE_2D);
3905 checkGLcall("glDisable(GL_TEXTURE_2D)");
3908 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain);
3909 if(hr == WINED3D_OK && swapchain) {
3910 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
3911 if(((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
3912 glFlush();
3914 IWineD3DSwapChain_Release(swapchain);
3915 } else {
3916 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
3917 * reset properly next draw
3919 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture);
3920 if(hr == WINED3D_OK && texture) {
3921 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
3922 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
3923 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
3924 IWineD3DBaseTexture_Release(texture);
3927 LEAVE_GL();
3930 /*****************************************************************************
3931 * IWineD3DSurface::LoadLocation
3933 * Copies the current surface data from wherever it is to the requested
3934 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
3935 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
3936 * multiple locations, the gl texture is preferred over the drawable, which is
3937 * preferred over system memory. The PBO counts as system memory. If rect is
3938 * not NULL, only the specified rectangle is copied (only supported for
3939 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
3940 * location is marked up to date after the copy.
3942 * Parameters:
3943 * flag: Surface location flag to be updated
3944 * rect: rectangle to be copied
3946 * Returns:
3947 * WINED3D_OK on success
3948 * WINED3DERR_DEVICELOST on an internal error
3950 *****************************************************************************/
3951 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
3952 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3953 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3954 IWineD3DSwapChain *swapchain = NULL;
3955 GLenum format, internal, type;
3956 CONVERT_TYPES convert;
3957 int bpp;
3958 int width, pitch, outpitch;
3959 BYTE *mem;
3961 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3962 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3963 TRACE("Surface %p is an onscreen surface\n", iface);
3965 IWineD3DSwapChain_Release(swapchain);
3966 } else {
3967 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
3968 * Prefer SFLAG_INTEXTURE. */
3969 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
3973 TRACE("(%p)->(%s, %p)\n", iface,
3974 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3975 rect);
3976 if(rect) {
3977 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
3980 if(This->Flags & flag) {
3981 TRACE("Location already up to date\n");
3982 return WINED3D_OK;
3985 if(!(This->Flags & SFLAG_LOCATIONS)) {
3986 ERR("Surface does not have any up to date location\n");
3987 This->Flags |= SFLAG_LOST;
3988 return WINED3DERR_DEVICELOST;
3991 if(flag == SFLAG_INSYSMEM) {
3992 surface_prepare_system_memory(This);
3994 /* Download the surface to system memory */
3995 if(This->Flags & SFLAG_INTEXTURE) {
3996 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
3997 surface_bind_and_dirtify(This);
3999 surface_download_data(This);
4000 } else {
4001 read_from_framebuffer(This, rect,
4002 This->resource.allocatedMemory,
4003 IWineD3DSurface_GetPitch(iface));
4005 } else if(flag == SFLAG_INDRAWABLE) {
4006 if(This->Flags & SFLAG_INTEXTURE) {
4007 surface_blt_to_drawable(This, rect);
4008 } else {
4009 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4011 /* The width is in 'length' not in bytes */
4012 width = This->currentDesc.Width;
4013 pitch = IWineD3DSurface_GetPitch(iface);
4015 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4016 int height = This->currentDesc.Height;
4018 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4019 outpitch = width * bpp;
4020 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4022 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4023 if(!mem) {
4024 ERR("Out of memory %d, %d!\n", outpitch, height);
4025 return WINED3DERR_OUTOFVIDEOMEMORY;
4027 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4029 This->Flags |= SFLAG_CONVERTED;
4030 } else {
4031 This->Flags &= ~SFLAG_CONVERTED;
4032 mem = This->resource.allocatedMemory;
4035 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4037 /* Don't delete PBO memory */
4038 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4039 HeapFree(GetProcessHeap(), 0, mem);
4041 } else /* if(flag == SFLAG_INTEXTURE) */ {
4042 if (This->Flags & SFLAG_INDRAWABLE) {
4043 read_from_framebuffer_texture(This);
4044 } else { /* Upload from system memory */
4045 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4047 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4048 surface_bind_and_dirtify(This);
4049 ENTER_GL();
4051 /* The only place where LoadTexture() might get called when isInDraw=1
4052 * is ActivateContext where lastActiveRenderTarget is preloaded.
4054 if(iface == device->lastActiveRenderTarget && device->isInDraw)
4055 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
4057 /* Otherwise: System memory copy must be most up to date */
4059 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4060 This->Flags |= SFLAG_GLCKEY;
4061 This->glCKey = This->SrcBltCKey;
4063 else This->Flags &= ~SFLAG_GLCKEY;
4065 /* The width is in 'length' not in bytes */
4066 width = This->currentDesc.Width;
4067 pitch = IWineD3DSurface_GetPitch(iface);
4069 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4070 int height = This->currentDesc.Height;
4072 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4073 outpitch = width * bpp;
4074 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4076 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4077 if(!mem) {
4078 ERR("Out of memory %d, %d!\n", outpitch, height);
4079 return WINED3DERR_OUTOFVIDEOMEMORY;
4081 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4083 This->Flags |= SFLAG_CONVERTED;
4084 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
4085 d3dfmt_p8_upload_palette(iface, convert);
4086 This->Flags &= ~SFLAG_CONVERTED;
4087 mem = This->resource.allocatedMemory;
4088 } else {
4089 This->Flags &= ~SFLAG_CONVERTED;
4090 mem = This->resource.allocatedMemory;
4093 /* Make sure the correct pitch is used */
4094 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4096 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4097 TRACE("non power of two support\n");
4098 if(!(This->Flags & SFLAG_ALLOCATED)) {
4099 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4101 if (mem || (This->Flags & SFLAG_PBO)) {
4102 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4104 } else {
4105 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4106 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4108 if(!(This->Flags & SFLAG_ALLOCATED)) {
4109 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4111 if (mem || (This->Flags & SFLAG_PBO)) {
4112 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4116 /* Restore the default pitch */
4117 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4118 LEAVE_GL();
4120 /* Don't delete PBO memory */
4121 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4122 HeapFree(GetProcessHeap(), 0, mem);
4126 if(rect == NULL) {
4127 This->Flags |= flag;
4130 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
4131 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4132 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4133 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4136 return WINED3D_OK;
4139 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
4140 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4141 IWineD3DSwapChain *swapchain = NULL;
4143 /* Update the drawable size method */
4144 if(container) {
4145 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4147 if(swapchain) {
4148 This->get_drawable_size = get_drawable_size_swapchain;
4149 IWineD3DSwapChain_Release(swapchain);
4150 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4151 switch(wined3d_settings.offscreen_rendering_mode) {
4152 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4153 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4154 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4158 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4161 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4162 return SURFACE_OPENGL;
4165 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4167 /* IUnknown */
4168 IWineD3DBaseSurfaceImpl_QueryInterface,
4169 IWineD3DBaseSurfaceImpl_AddRef,
4170 IWineD3DSurfaceImpl_Release,
4171 /* IWineD3DResource */
4172 IWineD3DBaseSurfaceImpl_GetParent,
4173 IWineD3DBaseSurfaceImpl_GetDevice,
4174 IWineD3DBaseSurfaceImpl_SetPrivateData,
4175 IWineD3DBaseSurfaceImpl_GetPrivateData,
4176 IWineD3DBaseSurfaceImpl_FreePrivateData,
4177 IWineD3DBaseSurfaceImpl_SetPriority,
4178 IWineD3DBaseSurfaceImpl_GetPriority,
4179 IWineD3DSurfaceImpl_PreLoad,
4180 IWineD3DSurfaceImpl_UnLoad,
4181 IWineD3DBaseSurfaceImpl_GetType,
4182 /* IWineD3DSurface */
4183 IWineD3DBaseSurfaceImpl_GetContainer,
4184 IWineD3DBaseSurfaceImpl_GetDesc,
4185 IWineD3DSurfaceImpl_LockRect,
4186 IWineD3DSurfaceImpl_UnlockRect,
4187 IWineD3DSurfaceImpl_GetDC,
4188 IWineD3DSurfaceImpl_ReleaseDC,
4189 IWineD3DSurfaceImpl_Flip,
4190 IWineD3DSurfaceImpl_Blt,
4191 IWineD3DBaseSurfaceImpl_GetBltStatus,
4192 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4193 IWineD3DBaseSurfaceImpl_IsLost,
4194 IWineD3DBaseSurfaceImpl_Restore,
4195 IWineD3DSurfaceImpl_BltFast,
4196 IWineD3DBaseSurfaceImpl_GetPalette,
4197 IWineD3DBaseSurfaceImpl_SetPalette,
4198 IWineD3DSurfaceImpl_RealizePalette,
4199 IWineD3DBaseSurfaceImpl_SetColorKey,
4200 IWineD3DBaseSurfaceImpl_GetPitch,
4201 IWineD3DSurfaceImpl_SetMem,
4202 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4203 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4204 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4205 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4206 IWineD3DBaseSurfaceImpl_SetClipper,
4207 IWineD3DBaseSurfaceImpl_GetClipper,
4208 /* Internal use: */
4209 IWineD3DSurfaceImpl_AddDirtyRect,
4210 IWineD3DSurfaceImpl_LoadTexture,
4211 IWineD3DSurfaceImpl_BindTexture,
4212 IWineD3DSurfaceImpl_SaveSnapshot,
4213 IWineD3DSurfaceImpl_SetContainer,
4214 IWineD3DSurfaceImpl_SetGlTextureDesc,
4215 IWineD3DSurfaceImpl_GetGlDesc,
4216 IWineD3DSurfaceImpl_GetData,
4217 IWineD3DSurfaceImpl_SetFormat,
4218 IWineD3DSurfaceImpl_PrivateSetup,
4219 IWineD3DSurfaceImpl_ModifyLocation,
4220 IWineD3DSurfaceImpl_LoadLocation,
4221 IWineD3DSurfaceImpl_GetImplType