wined3d: Improve detection of device palette change.
[wine.git] / dlls / wined3d / surface.c
blob641c2490ed0c6abf87924d96a5c9511d5a921343
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
11 * Copyright 2007 Henri Verbeet
12 * Copyright 2006-2008 Roderick Colenbrander
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
36 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
37 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey);
38 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This);
40 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This) {
41 GLint active_texture;
43 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
44 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
45 * gl states. The current texture unit should always be a valid one.
47 * TODO: Track the current active texture per GL context instead of using glGet
49 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
50 ENTER_GL();
51 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
52 LEAVE_GL();
53 active_texture -= GL_TEXTURE0_ARB;
54 } else {
55 active_texture = 0;
57 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_texture));
58 IWineD3DSurface_BindTexture((IWineD3DSurface *)This);
61 /* This function checks if the primary render target uses the 8bit paletted format. */
62 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
64 if (device->render_targets && device->render_targets[0]) {
65 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
66 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
67 return TRUE;
69 return FALSE;
72 /* This call just downloads data, the caller is responsible for activating the
73 * right context and binding the correct texture. */
74 static void surface_download_data(IWineD3DSurfaceImpl *This) {
75 if (0 == This->glDescription.textureName) {
76 ERR("Surface does not have a texture, but SFLAG_INTEXTURE is set\n");
77 return;
80 /* Only support read back of converted P8 surfaces */
81 if(This->Flags & SFLAG_CONVERTED && (This->resource.format != WINED3DFMT_P8)) {
82 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(This->resource.format));
83 return;
86 ENTER_GL();
88 if (This->resource.format == WINED3DFMT_DXT1 ||
89 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
90 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
91 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
92 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
93 } else {
94 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
95 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
97 if(This->Flags & SFLAG_PBO) {
98 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
99 checkGLcall("glBindBufferARB");
100 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
101 checkGLcall("glGetCompressedTexImageARB()");
102 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
103 checkGLcall("glBindBufferARB");
104 } else {
105 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
106 checkGLcall("glGetCompressedTexImageARB()");
109 LEAVE_GL();
110 } else {
111 void *mem;
112 GLenum format = This->glDescription.glFormat;
113 GLenum type = This->glDescription.glType;
114 int src_pitch = 0;
115 int dst_pitch = 0;
117 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
118 if(This->resource.format == WINED3DFMT_P8) {
119 format = GL_ALPHA;
120 type = GL_UNSIGNED_BYTE;
123 if (This->Flags & SFLAG_NONPOW2) {
124 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
125 src_pitch = This->bytesPerPixel * This->pow2Width;
126 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
127 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
128 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
129 } else {
130 mem = This->resource.allocatedMemory;
133 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
134 format, type, mem);
136 if(This->Flags & SFLAG_PBO) {
137 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
138 checkGLcall("glBindBufferARB");
140 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
141 type, NULL);
142 checkGLcall("glGetTexImage()");
144 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
145 checkGLcall("glBindBufferARB");
146 } else {
147 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
148 type, mem);
149 checkGLcall("glGetTexImage()");
151 LEAVE_GL();
153 if (This->Flags & SFLAG_NONPOW2) {
154 LPBYTE src_data, dst_data;
155 int y;
157 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
158 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
159 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
161 * We're doing this...
163 * instead of boxing the texture :
164 * |<-texture width ->| -->pow2width| /\
165 * |111111111111111111| | |
166 * |222 Texture 222222| boxed empty | texture height
167 * |3333 Data 33333333| | |
168 * |444444444444444444| | \/
169 * ----------------------------------- |
170 * | boxed empty | boxed empty | pow2height
171 * | | | \/
172 * -----------------------------------
175 * we're repacking the data to the expected texture width
177 * |<-texture width ->| -->pow2width| /\
178 * |111111111111111111222222222222222| |
179 * |222333333333333333333444444444444| texture height
180 * |444444 | |
181 * | | \/
182 * | | |
183 * | empty | pow2height
184 * | | \/
185 * -----------------------------------
187 * == is the same as
189 * |<-texture width ->| /\
190 * |111111111111111111|
191 * |222222222222222222|texture height
192 * |333333333333333333|
193 * |444444444444444444| \/
194 * --------------------
196 * this also means that any references to allocatedMemory should work with the data as if were a
197 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
199 * internally the texture is still stored in a boxed format so any references to textureName will
200 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
202 * Performance should not be an issue, because applications normally do not lock the surfaces when
203 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
204 * and doesn't have to be re-read.
206 src_data = mem;
207 dst_data = This->resource.allocatedMemory;
208 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
209 for (y = 1 ; y < This->currentDesc.Height; y++) {
210 /* skip the first row */
211 src_data += src_pitch;
212 dst_data += dst_pitch;
213 memcpy(dst_data, src_data, dst_pitch);
216 HeapFree(GetProcessHeap(), 0, mem);
220 /* Surface has now been downloaded */
221 This->Flags |= SFLAG_INSYSMEM;
224 /* This call just uploads data, the caller is responsible for activating the
225 * right context and binding the correct texture. */
226 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
227 if (This->resource.format == WINED3DFMT_DXT1 ||
228 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
229 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
230 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
231 FIXME("Using DXT1/3/5 without advertized support\n");
232 } else {
233 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
234 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
235 * function uses glCompressedTexImage2D instead of the SubImage call
237 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
238 ENTER_GL();
240 if(This->Flags & SFLAG_PBO) {
241 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
242 checkGLcall("glBindBufferARB");
243 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
245 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
246 width, height, 0 /* border */, This->resource.size, NULL));
247 checkGLcall("glCompressedTexSubImage2D");
249 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
250 checkGLcall("glBindBufferARB");
251 } else {
252 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
253 width, height, 0 /* border */, This->resource.size, data));
254 checkGLcall("glCompressedTexSubImage2D");
256 LEAVE_GL();
258 } else {
259 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
260 ENTER_GL();
262 if(This->Flags & SFLAG_PBO) {
263 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
264 checkGLcall("glBindBufferARB");
265 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
267 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
268 checkGLcall("glTexSubImage2D");
270 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
271 checkGLcall("glBindBufferARB");
273 else {
274 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
275 checkGLcall("glTexSubImage2D");
278 LEAVE_GL();
282 /* This call just allocates the texture, the caller is responsible for
283 * activating the right context and binding the correct texture. */
284 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
285 BOOL enable_client_storage = FALSE;
286 BYTE *mem = NULL;
288 TRACE("(%p) : Creating surface (target %#x) level %d, d3d format %s, internal format %#x, width %d, height %d, gl format %#x, gl type=%#x\n", This,
289 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
291 if (This->resource.format == WINED3DFMT_DXT1 ||
292 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
293 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
294 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
295 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
297 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
298 * once, unfortunately
300 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
301 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
302 This->Flags |= SFLAG_CLIENT;
303 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
304 ENTER_GL();
305 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
306 width, height, 0 /* border */, This->resource.size, mem));
307 LEAVE_GL();
310 return;
313 ENTER_GL();
315 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
316 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
317 /* In some cases we want to disable client storage.
318 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
319 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
320 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
321 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
322 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
324 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
325 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
326 This->Flags &= ~SFLAG_CLIENT;
327 enable_client_storage = TRUE;
328 } else {
329 This->Flags |= SFLAG_CLIENT;
331 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
332 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
334 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
337 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
338 checkGLcall("glTexImage2D");
340 if(enable_client_storage) {
341 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
342 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
344 LEAVE_GL();
346 This->Flags |= SFLAG_ALLOCATED;
349 /* In D3D the depth stencil dimensions have to be greater than or equal to the
350 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
351 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
352 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
353 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
354 renderbuffer_entry_t *entry;
355 GLuint renderbuffer = 0;
356 unsigned int src_width, src_height;
358 src_width = This->pow2Width;
359 src_height = This->pow2Height;
361 /* A depth stencil smaller than the render target is not valid */
362 if (width > src_width || height > src_height) return;
364 /* Remove any renderbuffer set if the sizes match */
365 if (width == src_width && height == src_height) {
366 This->current_renderbuffer = NULL;
367 return;
370 /* Look if we've already got a renderbuffer of the correct dimensions */
371 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
372 if (entry->width == width && entry->height == height) {
373 renderbuffer = entry->id;
374 This->current_renderbuffer = entry;
375 break;
379 if (!renderbuffer) {
380 const GlPixelFormatDesc *glDesc;
381 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
383 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
384 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
385 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
387 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
388 entry->width = width;
389 entry->height = height;
390 entry->id = renderbuffer;
391 list_add_head(&This->renderbuffers, &entry->entry);
393 This->current_renderbuffer = entry;
396 checkGLcall("set_compatible_renderbuffer");
399 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
400 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
401 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
403 TRACE("(%p) : swapchain %p\n", This, swapchain);
405 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
406 TRACE("Returning GL_BACK\n");
407 return GL_BACK;
408 } else if (swapchain_impl->frontBuffer == iface) {
409 TRACE("Returning GL_FRONT\n");
410 return GL_FRONT;
413 FIXME("Higher back buffer, returning GL_BACK\n");
414 return GL_BACK;
417 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
418 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
419 ULONG ref = InterlockedDecrement(&This->resource.ref);
420 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
421 if (ref == 0) {
422 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
423 renderbuffer_entry_t *entry, *entry2;
424 TRACE("(%p) : cleaning up\n", This);
426 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
428 /* Need a context to destroy the texture. Use the currently active render target, but only if
429 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
430 * When destroying the primary rt, Uninit3D will activate a context before doing anything
432 if(device->render_targets && device->render_targets[0]) {
433 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
436 TRACE("Deleting texture %d\n", This->glDescription.textureName);
437 ENTER_GL();
438 glDeleteTextures(1, &This->glDescription.textureName);
439 LEAVE_GL();
442 if(This->Flags & SFLAG_PBO) {
443 /* Delete the PBO */
444 ENTER_GL();
445 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
446 LEAVE_GL();
449 if(This->Flags & SFLAG_DIBSECTION) {
450 /* Release the DC */
451 SelectObject(This->hDC, This->dib.holdbitmap);
452 DeleteDC(This->hDC);
453 /* Release the DIB section */
454 DeleteObject(This->dib.DIBsection);
455 This->dib.bitmap_data = NULL;
456 This->resource.allocatedMemory = NULL;
458 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
460 HeapFree(GetProcessHeap(), 0, This->palette9);
462 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
463 if(iface == device->ddraw_primary)
464 device->ddraw_primary = NULL;
466 ENTER_GL();
467 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
468 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
469 HeapFree(GetProcessHeap(), 0, entry);
471 LEAVE_GL();
473 TRACE("(%p) Released\n", This);
474 HeapFree(GetProcessHeap(), 0, This);
477 return ref;
480 /* ****************************************************
481 IWineD3DSurface IWineD3DResource parts follow
482 **************************************************** */
484 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
485 /* TODO: check for locks */
486 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
487 IWineD3DBaseTexture *baseTexture = NULL;
488 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
490 TRACE("(%p)Checking to see if the container is a base texture\n", This);
491 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
492 TRACE("Passing to container\n");
493 IWineD3DBaseTexture_PreLoad(baseTexture);
494 IWineD3DBaseTexture_Release(baseTexture);
495 } else {
496 TRACE("(%p) : About to load surface\n", This);
498 if(!device->isInDraw) {
499 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
502 if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8) {
503 if(palette9_changed(This)) {
504 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
505 /* TODO: This is not necessarily needed with hw palettized texture support */
506 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
507 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
508 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
511 ENTER_GL();
512 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
513 if (!This->glDescription.level) {
514 if (!This->glDescription.textureName) {
515 glGenTextures(1, &This->glDescription.textureName);
516 checkGLcall("glGenTextures");
517 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
519 glBindTexture(This->glDescription.target, This->glDescription.textureName);
520 checkGLcall("glBindTexture");
521 LEAVE_GL();
522 IWineD3DSurface_LoadTexture(iface, FALSE);
523 /* This is where we should be reducing the amount of GLMemoryUsed */
524 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
525 /* assume this is a coding error not a real error for now */
526 FIXME("Mipmap surface has a glTexture bound to it!\n");
527 LEAVE_GL();
529 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
530 /* Tell opengl to try and keep this texture in video ram (well mostly) */
531 GLclampf tmp;
532 tmp = 0.9f;
533 ENTER_GL();
534 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
535 LEAVE_GL();
538 return;
541 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
542 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
543 This->resource.allocatedMemory =
544 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
546 ENTER_GL();
547 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
548 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
549 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
550 checkGLcall("glGetBufferSubData");
551 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
552 checkGLcall("glDeleteBuffers");
553 LEAVE_GL();
555 This->pbo = 0;
556 This->Flags &= ~SFLAG_PBO;
559 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
560 IWineD3DBaseTexture *texture = NULL;
561 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
562 renderbuffer_entry_t *entry, *entry2;
563 TRACE("(%p)\n", iface);
565 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
566 /* Default pool resources are supposed to be destroyed before Reset is called.
567 * Implicit resources stay however. So this means we have an implicit render target
568 * or depth stencil. The content may be destroyed, but we still have to tear down
569 * opengl resources, so we cannot leave early.
571 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
572 } else {
573 /* Load the surface into system memory */
574 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
576 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
577 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
578 This->Flags &= ~SFLAG_ALLOCATED;
580 /* Destroy PBOs, but load them into real sysmem before */
581 if(This->Flags & SFLAG_PBO) {
582 surface_remove_pbo(This);
585 /* Destroy fbo render buffers. This is needed for implicit render targets, for
586 * all application-created targets the application has to release the surface
587 * before calling _Reset
589 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
590 ENTER_GL();
591 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
592 LEAVE_GL();
593 list_remove(&entry->entry);
594 HeapFree(GetProcessHeap(), 0, entry);
596 list_init(&This->renderbuffers);
597 This->current_renderbuffer = NULL;
599 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
600 * destroy it
602 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
603 if(!texture) {
604 ENTER_GL();
605 glDeleteTextures(1, &This->glDescription.textureName);
606 This->glDescription.textureName = 0;
607 LEAVE_GL();
608 } else {
609 IWineD3DBaseTexture_Release(texture);
611 return;
614 /* ******************************************************
615 IWineD3DSurface IWineD3DSurface parts follow
616 ****************************************************** */
618 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
619 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
620 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
621 if (This->glDescription.textureName == 0 && textureName != 0) {
622 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
623 IWineD3DSurface_AddDirtyRect(iface, NULL);
625 This->glDescription.textureName = textureName;
626 This->glDescription.target = target;
627 This->Flags &= ~SFLAG_ALLOCATED;
630 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
631 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
632 TRACE("(%p) : returning %p\n", This, &This->glDescription);
633 *glDescription = &This->glDescription;
636 /* TODO: think about moving this down to resource? */
637 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
638 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
639 /* 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 */
640 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
641 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
643 return (CONST void*)(This->resource.allocatedMemory);
646 /* Read the framebuffer back into the surface */
647 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
648 IWineD3DSwapChainImpl *swapchain;
649 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
650 BYTE *mem;
651 GLint fmt;
652 GLint type;
653 BYTE *row, *top, *bottom;
654 int i;
655 BOOL bpp;
656 RECT local_rect;
657 BOOL srcIsUpsideDown;
659 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
660 static BOOL warned = FALSE;
661 if(!warned) {
662 ERR("The application tries to lock the render target, but render target locking is disabled\n");
663 warned = TRUE;
665 return;
668 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
669 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
670 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
671 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
672 * context->last_was_blit set on the unlock.
674 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
675 ENTER_GL();
677 /* Select the correct read buffer, and give some debug output.
678 * There is no need to keep track of the current read buffer or reset it, every part of the code
679 * that reads sets the read buffer as desired.
681 if(!swapchain) {
682 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
683 * Read from the back buffer
685 TRACE("Locking offscreen render target\n");
686 glReadBuffer(myDevice->offscreenBuffer);
687 srcIsUpsideDown = TRUE;
688 } else {
689 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
690 TRACE("Locking %#x buffer\n", buffer);
691 glReadBuffer(buffer);
692 checkGLcall("glReadBuffer");
694 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
695 srcIsUpsideDown = FALSE;
698 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
699 if(!rect) {
700 local_rect.left = 0;
701 local_rect.top = 0;
702 local_rect.right = This->currentDesc.Width;
703 local_rect.bottom = This->currentDesc.Height;
704 } else {
705 local_rect = *rect;
707 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
709 switch(This->resource.format)
711 case WINED3DFMT_P8:
713 if(primary_render_target_is_p8(myDevice)) {
714 /* In case of P8 render targets the index is stored in the alpha component */
715 fmt = GL_ALPHA;
716 type = GL_UNSIGNED_BYTE;
717 mem = dest;
718 bpp = This->bytesPerPixel;
719 } else {
720 /* GL can't return palettized data, so read ARGB pixels into a
721 * separate block of memory and convert them into palettized format
722 * in software. Slow, but if the app means to use palettized render
723 * targets and locks it...
725 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
726 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
727 * for the color channels when palettizing the colors.
729 fmt = GL_RGB;
730 type = GL_UNSIGNED_BYTE;
731 pitch *= 3;
732 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
733 if(!mem) {
734 ERR("Out of memory\n");
735 LEAVE_GL();
736 return;
738 bpp = This->bytesPerPixel * 3;
741 break;
743 default:
744 mem = dest;
745 fmt = This->glDescription.glFormat;
746 type = This->glDescription.glType;
747 bpp = This->bytesPerPixel;
750 if(This->Flags & SFLAG_PBO) {
751 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
752 checkGLcall("glBindBufferARB");
755 glReadPixels(local_rect.left, local_rect.top,
756 local_rect.right - local_rect.left,
757 local_rect.bottom - local_rect.top,
758 fmt, type, mem);
759 vcheckGLcall("glReadPixels");
761 if(This->Flags & SFLAG_PBO) {
762 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
763 checkGLcall("glBindBufferARB");
765 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
766 * to get a pointer to it and perform the flipping in software. This is a lot
767 * faster than calling glReadPixels for each line. In case we want more speed
768 * we should rerender it flipped in a FBO and read the data back from the FBO. */
769 if(!srcIsUpsideDown) {
770 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
771 checkGLcall("glBindBufferARB");
773 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
774 checkGLcall("glMapBufferARB");
778 /* TODO: Merge this with the palettization loop below for P8 targets */
779 if(!srcIsUpsideDown) {
780 UINT len, off;
781 /* glReadPixels returns the image upside down, and there is no way to prevent this.
782 Flip the lines in software */
783 len = (local_rect.right - local_rect.left) * bpp;
784 off = local_rect.left * bpp;
786 row = HeapAlloc(GetProcessHeap(), 0, len);
787 if(!row) {
788 ERR("Out of memory\n");
789 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
790 LEAVE_GL();
791 return;
794 top = mem + pitch * local_rect.top;
795 bottom = mem + pitch * ( local_rect.bottom - local_rect.top - 1);
796 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
797 memcpy(row, top + off, len);
798 memcpy(top + off, bottom + off, len);
799 memcpy(bottom + off, row, len);
800 top += pitch;
801 bottom -= pitch;
803 HeapFree(GetProcessHeap(), 0, row);
805 /* Unmap the temp PBO buffer */
806 if(This->Flags & SFLAG_PBO) {
807 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
808 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
812 LEAVE_GL();
814 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
815 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
816 * the same color but we have no choice.
817 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
819 if((This->resource.format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice)) {
820 PALETTEENTRY *pal = NULL;
821 DWORD width = pitch / 3;
822 int x, y, c;
824 if(This->palette) {
825 pal = This->palette->palents;
826 } else {
827 ERR("Palette is missing, cannot perform inverse palette lookup\n");
828 HeapFree(GetProcessHeap(), 0, mem);
829 return ;
832 for(y = local_rect.top; y < local_rect.bottom; y++) {
833 for(x = local_rect.left; x < local_rect.right; x++) {
834 /* start lines pixels */
835 BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
836 BYTE *green = blue + 1;
837 BYTE *red = green + 1;
839 for(c = 0; c < 256; c++) {
840 if(*red == pal[c].peRed &&
841 *green == pal[c].peGreen &&
842 *blue == pal[c].peBlue)
844 *((BYTE *) dest + y * width + x) = c;
845 break;
850 HeapFree(GetProcessHeap(), 0, mem);
854 /* Read the framebuffer contents into a texture */
855 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This)
857 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
858 IWineD3DSwapChainImpl *swapchain;
859 int bpp;
860 GLenum format, internal, type;
861 CONVERT_TYPES convert;
862 BOOL srcIsUpsideDown;
863 GLint prevRead;
865 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
867 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
868 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
869 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
870 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
871 * context->last_was_blit set on the unlock.
873 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
874 surface_bind_and_dirtify(This);
875 ENTER_GL();
877 glGetIntegerv(GL_READ_BUFFER, &prevRead);
879 /* Select the correct read buffer, and give some debug output.
880 * There is no need to keep track of the current read buffer or reset it, every part of the code
881 * that reads sets the read buffer as desired.
883 if(!swapchain) {
884 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
885 * Read from the back buffer
887 TRACE("Locking offscreen render target\n");
888 glReadBuffer(device->offscreenBuffer);
889 srcIsUpsideDown = TRUE;
890 } else {
891 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
892 TRACE("Locking %#x buffer\n", buffer);
893 glReadBuffer(buffer);
894 checkGLcall("glReadBuffer");
896 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
897 srcIsUpsideDown = FALSE;
900 if(!(This->Flags & SFLAG_ALLOCATED)) {
901 surface_allocate_surface(This, internal, This->pow2Width,
902 This->pow2Height, format, type);
905 clear_unused_channels(This);
907 /* If !SrcIsUpsideDown we should flip the surface.
908 * This can be done using glCopyTexSubImage2D but this
909 * is VERY slow, so don't do that. We should prevent
910 * this code from getting called in such cases or perhaps
911 * we can use FBOs */
913 glCopyTexSubImage2D(This->glDescription.target,
914 This->glDescription.level,
915 0, 0, 0, 0,
916 This->currentDesc.Width,
917 This->currentDesc.Height);
918 checkGLcall("glCopyTexSubImage2D");
920 glReadBuffer(prevRead);
921 vcheckGLcall("glReadBuffer");
923 LEAVE_GL();
924 TRACE("Updated target %d\n", This->glDescription.target);
927 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
928 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
929 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
930 * changed
932 if(!(This->Flags & SFLAG_DYNLOCK)) {
933 This->lockCount++;
934 /* MAXLOCKCOUNT is defined in wined3d_private.h */
935 if(This->lockCount > MAXLOCKCOUNT) {
936 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
937 This->Flags |= SFLAG_DYNLOCK;
941 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
942 * Also don't create a PBO for systemmem surfaces.
944 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
945 GLenum error;
946 ENTER_GL();
948 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
949 error = glGetError();
950 if(This->pbo == 0 || error != GL_NO_ERROR) {
951 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
954 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
956 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
957 checkGLcall("glBindBufferARB");
959 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
960 checkGLcall("glBufferDataARB");
962 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
963 checkGLcall("glBindBufferARB");
965 /* We don't need the system memory anymore and we can't even use it for PBOs */
966 if(!(This->Flags & SFLAG_CLIENT)) {
967 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
968 This->resource.heapMemory = NULL;
970 This->resource.allocatedMemory = NULL;
971 This->Flags |= SFLAG_PBO;
972 LEAVE_GL();
973 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
974 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
975 * or a pbo to map
977 if(!This->resource.heapMemory) {
978 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
980 This->resource.allocatedMemory =
981 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
982 if(This->Flags & SFLAG_INSYSMEM) {
983 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
988 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
989 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
990 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
991 IWineD3DSwapChain *swapchain = NULL;
993 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
995 /* This is also done in the base class, but we have to verify this before loading any data from
996 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
997 * may interfere, and all other bad things may happen
999 if (This->Flags & SFLAG_LOCKED) {
1000 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1001 return WINED3DERR_INVALIDCALL;
1003 This->Flags |= SFLAG_LOCKED;
1005 if (!(This->Flags & SFLAG_LOCKABLE))
1007 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1010 if (Flags & WINED3DLOCK_DISCARD) {
1011 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1012 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1013 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1014 This->Flags |= SFLAG_INSYSMEM;
1015 goto lock_end;
1018 if (This->Flags & SFLAG_INSYSMEM) {
1019 TRACE("Local copy is up to date, not downloading data\n");
1020 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1021 goto lock_end;
1024 /* Now download the surface content from opengl
1025 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1026 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
1028 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1029 if(swapchain || iface == myDevice->render_targets[0]) {
1030 const RECT *pass_rect = pRect;
1032 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1033 * because most caller functions do not need that. So do that here
1035 if(pRect &&
1036 pRect->top == 0 &&
1037 pRect->left == 0 &&
1038 pRect->right == This->currentDesc.Width &&
1039 pRect->bottom == This->currentDesc.Height) {
1040 pass_rect = NULL;
1043 switch(wined3d_settings.rendertargetlock_mode) {
1044 case RTL_TEXDRAW:
1045 case RTL_TEXTEX:
1046 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1047 #if 0
1048 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1049 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1050 * This may be faster on some cards
1052 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1053 #endif
1054 /* drop through */
1056 case RTL_AUTO:
1057 case RTL_READDRAW:
1058 case RTL_READTEX:
1059 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
1060 break;
1062 case RTL_DISABLE:
1063 break;
1065 if(swapchain) IWineD3DSwapChain_Release(swapchain);
1067 } else if(iface == myDevice->stencilBufferTarget) {
1068 /** the depth stencil in openGL has a format of GL_FLOAT
1069 * which should be good for WINED3DFMT_D16_LOCKABLE
1070 * and WINED3DFMT_D16
1071 * it is unclear what format the stencil buffer is in except.
1072 * 'Each index is converted to fixed point...
1073 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1074 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1075 * glReadPixels(This->lockedRect.left,
1076 * This->lockedRect.bottom - j - 1,
1077 * This->lockedRect.right - This->lockedRect.left,
1078 * 1,
1079 * GL_DEPTH_COMPONENT,
1080 * type,
1081 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1083 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1084 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1085 * none of that is the case the problem is not in this function :-)
1086 ********************************************/
1087 FIXME("Depth stencil locking not supported yet\n");
1088 } else {
1089 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1090 TRACE("locking an ordinary surface\n");
1091 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1094 lock_end:
1095 if(This->Flags & SFLAG_PBO) {
1096 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1097 ENTER_GL();
1098 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1099 checkGLcall("glBindBufferARB");
1101 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1102 if(This->resource.allocatedMemory) {
1103 ERR("The surface already has PBO memory allocated!\n");
1106 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1107 checkGLcall("glMapBufferARB");
1109 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1110 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1111 checkGLcall("glBindBufferARB");
1113 LEAVE_GL();
1116 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1117 /* Don't dirtify */
1118 } else {
1119 IWineD3DBaseTexture *pBaseTexture;
1121 * Dirtify on lock
1122 * as seen in msdn docs
1124 IWineD3DSurface_AddDirtyRect(iface, pRect);
1126 /** Dirtify Container if needed */
1127 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
1128 TRACE("Making container dirty\n");
1129 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1130 IWineD3DBaseTexture_Release(pBaseTexture);
1131 } else {
1132 TRACE("Surface is standalone, no need to dirty the container\n");
1136 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1139 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1140 GLint prev_store;
1141 GLint prev_rasterpos[4];
1142 GLint skipBytes = 0;
1143 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1144 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1145 IWineD3DSwapChainImpl *swapchain;
1147 /* Activate the correct context for the render target */
1148 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1149 ENTER_GL();
1151 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
1152 if(!swapchain) {
1153 /* Primary offscreen render target */
1154 TRACE("Offscreen render target\n");
1155 glDrawBuffer(myDevice->offscreenBuffer);
1156 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1157 } else {
1158 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1159 TRACE("Unlocking %#x buffer\n", buffer);
1160 glDrawBuffer(buffer);
1161 checkGLcall("glDrawBuffer");
1163 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1166 glFlush();
1167 vcheckGLcall("glFlush");
1168 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1169 vcheckGLcall("glIntegerv");
1170 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1171 vcheckGLcall("glIntegerv");
1172 glPixelZoom(1.0, -1.0);
1173 vcheckGLcall("glPixelZoom");
1175 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1176 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1177 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1179 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1180 vcheckGLcall("glRasterPos2f");
1182 /* Some drivers(radeon dri, others?) don't like exceptions during
1183 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1184 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1185 * catch to put the dib section in InSync mode, which leads to a crash
1186 * and a blocked x server on my radeon card.
1188 * The following lines read the dib section so it is put in inSync mode
1189 * before glDrawPixels is called and the crash is prevented. There won't
1190 * be any interfering gdi accesses, because UnlockRect is called from
1191 * ReleaseDC, and the app won't use the dc any more afterwards.
1193 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1194 volatile BYTE read;
1195 read = This->resource.allocatedMemory[0];
1198 if(This->Flags & SFLAG_PBO) {
1199 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1200 checkGLcall("glBindBufferARB");
1203 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1204 if(This->Flags & SFLAG_LOCKED) {
1205 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1206 (This->lockedRect.bottom - This->lockedRect.top)-1,
1207 fmt, type,
1208 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1209 checkGLcall("glDrawPixels");
1210 } else {
1211 glDrawPixels(This->currentDesc.Width,
1212 This->currentDesc.Height,
1213 fmt, type, mem);
1214 checkGLcall("glDrawPixels");
1217 if(This->Flags & SFLAG_PBO) {
1218 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1219 checkGLcall("glBindBufferARB");
1222 glPixelZoom(1.0,1.0);
1223 vcheckGLcall("glPixelZoom");
1225 glRasterPos3iv(&prev_rasterpos[0]);
1226 vcheckGLcall("glRasterPos3iv");
1228 /* Reset to previous pack row length */
1229 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1230 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1232 if(!swapchain) {
1233 glDrawBuffer(myDevice->offscreenBuffer);
1234 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1235 } else if(swapchain->backBuffer) {
1236 glDrawBuffer(GL_BACK);
1237 checkGLcall("glDrawBuffer(GL_BACK)");
1238 } else {
1239 glDrawBuffer(GL_FRONT);
1240 checkGLcall("glDrawBuffer(GL_FRONT)");
1242 LEAVE_GL();
1244 return;
1247 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1248 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1249 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1250 IWineD3DSwapChainImpl *swapchain = NULL;
1251 BOOL fullsurface;
1253 if (!(This->Flags & SFLAG_LOCKED)) {
1254 WARN("trying to Unlock an unlocked surf@%p\n", This);
1255 return WINED3DERR_INVALIDCALL;
1258 if (This->Flags & SFLAG_PBO) {
1259 TRACE("Freeing PBO memory\n");
1260 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1261 ENTER_GL();
1262 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1263 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1264 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1265 checkGLcall("glUnmapBufferARB");
1266 LEAVE_GL();
1267 This->resource.allocatedMemory = NULL;
1270 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1272 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1273 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1274 goto unlock_end;
1277 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1278 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1279 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1281 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1282 static BOOL warned = FALSE;
1283 if(!warned) {
1284 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1285 warned = TRUE;
1287 goto unlock_end;
1290 if(This->dirtyRect.left == 0 &&
1291 This->dirtyRect.top == 0 &&
1292 This->dirtyRect.right == This->currentDesc.Width &&
1293 This->dirtyRect.bottom == This->currentDesc.Height) {
1294 fullsurface = TRUE;
1295 } else {
1296 /* TODO: Proper partial rectangle tracking */
1297 fullsurface = FALSE;
1298 This->Flags |= SFLAG_INSYSMEM;
1301 switch(wined3d_settings.rendertargetlock_mode) {
1302 case RTL_READTEX:
1303 case RTL_TEXTEX:
1304 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1305 ENTER_GL();
1306 if (This->glDescription.textureName == 0) {
1307 glGenTextures(1, &This->glDescription.textureName);
1308 checkGLcall("glGenTextures");
1310 glBindTexture(This->glDescription.target, This->glDescription.textureName);
1311 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
1312 LEAVE_GL();
1313 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1314 /* drop through */
1316 case RTL_AUTO:
1317 case RTL_READDRAW:
1318 case RTL_TEXDRAW:
1319 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1320 break;
1323 if(!fullsurface) {
1324 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1325 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1326 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1327 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1328 * not fully up to date because only a subrectangle was read in LockRect.
1330 This->Flags &= ~SFLAG_INSYSMEM;
1331 This->Flags |= SFLAG_INDRAWABLE;
1334 This->dirtyRect.left = This->currentDesc.Width;
1335 This->dirtyRect.top = This->currentDesc.Height;
1336 This->dirtyRect.right = 0;
1337 This->dirtyRect.bottom = 0;
1338 } else if(iface == myDevice->stencilBufferTarget) {
1339 FIXME("Depth Stencil buffer locking is not implemented\n");
1340 } else {
1341 /* The rest should be a normal texture */
1342 IWineD3DBaseTextureImpl *impl;
1343 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1344 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1345 * states need resetting
1347 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1348 if(impl->baseTexture.bindCount) {
1349 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1351 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1355 unlock_end:
1356 This->Flags &= ~SFLAG_LOCKED;
1357 memset(&This->lockedRect, 0, sizeof(RECT));
1358 return WINED3D_OK;
1361 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1362 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1363 WINED3DLOCKED_RECT lock;
1364 HRESULT hr;
1365 RGBQUAD col[256];
1367 TRACE("(%p)->(%p)\n",This,pHDC);
1369 if(This->Flags & SFLAG_USERPTR) {
1370 ERR("Not supported on surfaces with an application-provided surfaces\n");
1371 return WINEDDERR_NODC;
1374 /* Give more detailed info for ddraw */
1375 if (This->Flags & SFLAG_DCINUSE)
1376 return WINEDDERR_DCALREADYCREATED;
1378 /* Can't GetDC if the surface is locked */
1379 if (This->Flags & SFLAG_LOCKED)
1380 return WINED3DERR_INVALIDCALL;
1382 /* According to Direct3D9 docs, only these formats are supported */
1383 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1384 if (This->resource.format != WINED3DFMT_R5G6B5 &&
1385 This->resource.format != WINED3DFMT_X1R5G5B5 &&
1386 This->resource.format != WINED3DFMT_R8G8B8 &&
1387 This->resource.format != WINED3DFMT_X8R8G8B8) return WINED3DERR_INVALIDCALL;
1390 memset(&lock, 0, sizeof(lock)); /* To be sure */
1392 /* Create a DIB section if there isn't a hdc yet */
1393 if(!This->hDC) {
1394 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1395 if(This->Flags & SFLAG_CLIENT) {
1396 IWineD3DSurface_PreLoad(iface);
1399 /* Use the dib section from now on if we are not using a PBO */
1400 if(!(This->Flags & SFLAG_PBO))
1401 This->resource.allocatedMemory = This->dib.bitmap_data;
1404 /* Lock the surface */
1405 hr = IWineD3DSurface_LockRect(iface,
1406 &lock,
1407 NULL,
1410 if(This->Flags & SFLAG_PBO) {
1411 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1412 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1415 if(FAILED(hr)) {
1416 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1417 /* keep the dib section */
1418 return hr;
1421 if(This->resource.format == WINED3DFMT_P8 ||
1422 This->resource.format == WINED3DFMT_A8P8) {
1423 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1424 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1425 unsigned int n;
1426 PALETTEENTRY *pal = NULL;
1428 if(This->palette) {
1429 pal = This->palette->palents;
1430 } else {
1431 IWineD3DSurfaceImpl *dds_primary = (IWineD3DSurfaceImpl *)This->resource.wineD3DDevice->ddraw_primary;
1432 if (dds_primary && dds_primary->palette)
1433 pal = dds_primary->palette->palents;
1436 if (pal) {
1437 for (n=0; n<256; n++) {
1438 col[n].rgbRed = pal[n].peRed;
1439 col[n].rgbGreen = pal[n].peGreen;
1440 col[n].rgbBlue = pal[n].peBlue;
1441 col[n].rgbReserved = 0;
1443 SetDIBColorTable(This->hDC, 0, 256, col);
1447 *pHDC = This->hDC;
1448 TRACE("returning %p\n",*pHDC);
1449 This->Flags |= SFLAG_DCINUSE;
1451 return WINED3D_OK;
1454 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1455 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1457 TRACE("(%p)->(%p)\n",This,hDC);
1459 if (!(This->Flags & SFLAG_DCINUSE))
1460 return WINED3DERR_INVALIDCALL;
1462 if (This->hDC !=hDC) {
1463 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1464 return WINED3DERR_INVALIDCALL;
1467 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1468 /* Copy the contents of the DIB over to the PBO */
1469 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1472 /* we locked first, so unlock now */
1473 IWineD3DSurface_UnlockRect(iface);
1475 This->Flags &= ~SFLAG_DCINUSE;
1477 return WINED3D_OK;
1480 /* ******************************************************
1481 IWineD3DSurface Internal (No mapping to directx api) parts follow
1482 ****************************************************** */
1484 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) {
1485 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1486 const GlPixelFormatDesc *glDesc;
1487 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1488 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1490 /* Default values: From the surface */
1491 *format = glDesc->glFormat;
1492 *type = glDesc->glType;
1493 *convert = NO_CONVERSION;
1494 *target_bpp = This->bytesPerPixel;
1496 if(srgb_mode) {
1497 *internal = glDesc->glGammaInternal;
1498 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1499 *internal = glDesc->rtInternal;
1500 } else {
1501 *internal = glDesc->glInternal;
1504 /* Ok, now look if we have to do any conversion */
1505 switch(This->resource.format) {
1506 case WINED3DFMT_P8:
1507 /* ****************
1508 Paletted Texture
1509 **************** */
1511 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1512 * of the two is available make sure texturing is requested as neither of the two works in
1513 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1514 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1515 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1516 * conflicts with this.
1518 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) && primary_render_target_is_p8(device))) || colorkey_active || !use_texturing ) {
1519 *format = GL_RGBA;
1520 *internal = GL_RGBA;
1521 *type = GL_UNSIGNED_BYTE;
1522 *target_bpp = 4;
1523 if(colorkey_active) {
1524 *convert = CONVERT_PALETTED_CK;
1525 } else {
1526 *convert = CONVERT_PALETTED;
1529 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1530 *format = GL_ALPHA;
1531 *internal = GL_RGBA;
1532 *type = GL_UNSIGNED_BYTE;
1533 *target_bpp = 1;
1536 break;
1538 case WINED3DFMT_R3G3B2:
1539 /* **********************
1540 GL_UNSIGNED_BYTE_3_3_2
1541 ********************** */
1542 if (colorkey_active) {
1543 /* This texture format will never be used.. So do not care about color keying
1544 up until the point in time it will be needed :-) */
1545 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1547 break;
1549 case WINED3DFMT_R5G6B5:
1550 if (colorkey_active) {
1551 *convert = CONVERT_CK_565;
1552 *format = GL_RGBA;
1553 *internal = GL_RGBA;
1554 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1556 break;
1558 case WINED3DFMT_X1R5G5B5:
1559 if (colorkey_active) {
1560 *convert = CONVERT_CK_5551;
1561 *format = GL_BGRA;
1562 *internal = GL_RGBA;
1563 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1565 break;
1567 case WINED3DFMT_R8G8B8:
1568 if (colorkey_active) {
1569 *convert = CONVERT_CK_RGB24;
1570 *format = GL_RGBA;
1571 *internal = GL_RGBA;
1572 *type = GL_UNSIGNED_INT_8_8_8_8;
1573 *target_bpp = 4;
1575 break;
1577 case WINED3DFMT_X8R8G8B8:
1578 if (colorkey_active) {
1579 *convert = CONVERT_RGB32_888;
1580 *format = GL_RGBA;
1581 *internal = GL_RGBA;
1582 *type = GL_UNSIGNED_INT_8_8_8_8;
1584 break;
1586 case WINED3DFMT_V8U8:
1587 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1588 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1589 *format = GL_DUDV_ATI;
1590 *internal = GL_DU8DV8_ATI;
1591 *type = GL_BYTE;
1592 /* No conversion - Just change the gl type */
1593 break;
1595 *convert = CONVERT_V8U8;
1596 *format = GL_BGR;
1597 *internal = GL_RGB8;
1598 *type = GL_UNSIGNED_BYTE;
1599 *target_bpp = 3;
1600 break;
1602 case WINED3DFMT_L6V5U5:
1603 *convert = CONVERT_L6V5U5;
1604 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1605 *target_bpp = 3;
1606 /* Use format and types from table */
1607 } else {
1608 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1609 *target_bpp = 2;
1610 *format = GL_RGB;
1611 *internal = GL_RGB5;
1612 *type = GL_UNSIGNED_SHORT_5_6_5;
1614 break;
1616 case WINED3DFMT_X8L8V8U8:
1617 *convert = CONVERT_X8L8V8U8;
1618 *target_bpp = 4;
1619 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1620 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1621 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1622 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1623 * the needed type and format parameter, so the internal format contains a
1624 * 4th component, which is returned as alpha
1626 } else {
1627 /* Not supported by GL_ATI_envmap_bumpmap */
1628 *format = GL_BGRA;
1629 *internal = GL_RGB8;
1630 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1632 break;
1634 case WINED3DFMT_Q8W8V8U8:
1635 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1636 *convert = CONVERT_Q8W8V8U8;
1637 *format = GL_BGRA;
1638 *internal = GL_RGBA8;
1639 *type = GL_UNSIGNED_BYTE;
1640 *target_bpp = 4;
1641 /* Not supported by GL_ATI_envmap_bumpmap */
1642 break;
1644 case WINED3DFMT_V16U16:
1645 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1646 *convert = CONVERT_V16U16;
1647 *format = GL_BGR;
1648 *internal = GL_RGB16_EXT;
1649 *type = GL_UNSIGNED_SHORT;
1650 *target_bpp = 6;
1651 /* What should I do here about GL_ATI_envmap_bumpmap?
1652 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1654 break;
1656 case WINED3DFMT_A4L4:
1657 /* A4L4 exists as an internal gl format, but for some reason there is not
1658 * format+type combination to load it. Thus convert it to A8L8, then load it
1659 * with A4L4 internal, but A8L8 format+type
1661 *convert = CONVERT_A4L4;
1662 *format = GL_LUMINANCE_ALPHA;
1663 *internal = GL_LUMINANCE4_ALPHA4;
1664 *type = GL_UNSIGNED_BYTE;
1665 *target_bpp = 2;
1666 break;
1668 case WINED3DFMT_R32F:
1669 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1670 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1671 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1672 * 1.0 instead.
1674 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1676 *convert = CONVERT_R32F;
1677 *format = GL_RGB;
1678 *internal = GL_RGB32F_ARB;
1679 *type = GL_FLOAT;
1680 *target_bpp = 12;
1681 break;
1683 case WINED3DFMT_R16F:
1684 /* Similar to R32F */
1685 *convert = CONVERT_R16F;
1686 *format = GL_RGB;
1687 *internal = GL_RGB16F_ARB;
1688 *type = GL_HALF_FLOAT_ARB;
1689 *target_bpp = 6;
1690 break;
1692 case WINED3DFMT_G16R16:
1693 *convert = CONVERT_G16R16;
1694 *format = GL_RGB;
1695 *internal = GL_RGB16_EXT;
1696 *type = GL_UNSIGNED_SHORT;
1697 *target_bpp = 6;
1698 break;
1700 default:
1701 break;
1704 return WINED3D_OK;
1707 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1708 BYTE *source, *dest;
1709 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1711 switch (convert) {
1712 case NO_CONVERSION:
1714 memcpy(dst, src, pitch * height);
1715 break;
1717 case CONVERT_PALETTED:
1718 case CONVERT_PALETTED_CK:
1720 IWineD3DPaletteImpl* pal = This->palette;
1721 BYTE table[256][4];
1722 unsigned int x, y;
1724 if( pal == NULL) {
1725 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1728 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1730 for (y = 0; y < height; y++)
1732 source = src + pitch * y;
1733 dest = dst + outpitch * y;
1734 /* This is an 1 bpp format, using the width here is fine */
1735 for (x = 0; x < width; x++) {
1736 BYTE color = *source++;
1737 *dest++ = table[color][0];
1738 *dest++ = table[color][1];
1739 *dest++ = table[color][2];
1740 *dest++ = table[color][3];
1744 break;
1746 case CONVERT_CK_565:
1748 /* Converting the 565 format in 5551 packed to emulate color-keying.
1750 Note : in all these conversion, it would be best to average the averaging
1751 pixels to get the color of the pixel that will be color-keyed to
1752 prevent 'color bleeding'. This will be done later on if ever it is
1753 too visible.
1755 Note2: Nvidia documents say that their driver does not support alpha + color keying
1756 on the same surface and disables color keying in such a case
1758 unsigned int x, y;
1759 WORD *Source;
1760 WORD *Dest;
1762 TRACE("Color keyed 565\n");
1764 for (y = 0; y < height; y++) {
1765 Source = (WORD *) (src + y * pitch);
1766 Dest = (WORD *) (dst + y * outpitch);
1767 for (x = 0; x < width; x++ ) {
1768 WORD color = *Source++;
1769 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1770 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1771 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1772 *Dest |= 0x0001;
1774 Dest++;
1778 break;
1780 case CONVERT_CK_5551:
1782 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1783 unsigned int x, y;
1784 WORD *Source;
1785 WORD *Dest;
1786 TRACE("Color keyed 5551\n");
1787 for (y = 0; y < height; y++) {
1788 Source = (WORD *) (src + y * pitch);
1789 Dest = (WORD *) (dst + y * outpitch);
1790 for (x = 0; x < width; x++ ) {
1791 WORD color = *Source++;
1792 *Dest = color;
1793 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1794 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1795 *Dest |= (1 << 15);
1797 else {
1798 *Dest &= ~(1 << 15);
1800 Dest++;
1804 break;
1806 case CONVERT_V8U8:
1808 unsigned int x, y;
1809 short *Source;
1810 unsigned char *Dest;
1811 for(y = 0; y < height; y++) {
1812 Source = (short *) (src + y * pitch);
1813 Dest = dst + y * outpitch;
1814 for (x = 0; x < width; x++ ) {
1815 long color = (*Source++);
1816 /* B */ Dest[0] = 0xff;
1817 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1818 /* R */ Dest[2] = (color) + 128; /* U */
1819 Dest += 3;
1822 break;
1825 case CONVERT_V16U16:
1827 unsigned int x, y;
1828 DWORD *Source;
1829 unsigned short *Dest;
1830 for(y = 0; y < height; y++) {
1831 Source = (DWORD *) (src + y * pitch);
1832 Dest = (unsigned short *) (dst + y * outpitch);
1833 for (x = 0; x < width; x++ ) {
1834 DWORD color = (*Source++);
1835 /* B */ Dest[0] = 0xffff;
1836 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1837 /* R */ Dest[2] = (color ) + 32768; /* U */
1838 Dest += 3;
1841 break;
1844 case CONVERT_Q8W8V8U8:
1846 unsigned int x, y;
1847 DWORD *Source;
1848 unsigned char *Dest;
1849 for(y = 0; y < height; y++) {
1850 Source = (DWORD *) (src + y * pitch);
1851 Dest = dst + y * outpitch;
1852 for (x = 0; x < width; x++ ) {
1853 long color = (*Source++);
1854 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1855 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1856 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1857 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1858 Dest += 4;
1861 break;
1864 case CONVERT_L6V5U5:
1866 unsigned int x, y;
1867 WORD *Source;
1868 unsigned char *Dest;
1870 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1871 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1872 * fixed function and shaders without further conversion once the surface is
1873 * loaded
1875 for(y = 0; y < height; y++) {
1876 Source = (WORD *) (src + y * pitch);
1877 Dest = dst + y * outpitch;
1878 for (x = 0; x < width; x++ ) {
1879 short color = (*Source++);
1880 unsigned char l = ((color >> 10) & 0xfc);
1881 char v = ((color >> 5) & 0x3e);
1882 char u = ((color ) & 0x1f);
1884 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1885 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1886 * shift. GL reads a signed value and converts it into an unsigned value.
1888 /* M */ Dest[2] = l << 1;
1890 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1891 * from 5 bit values to 8 bit values.
1893 /* V */ Dest[1] = v << 3;
1894 /* U */ Dest[0] = u << 3;
1895 Dest += 3;
1898 } else {
1899 for(y = 0; y < height; y++) {
1900 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1901 Source = (WORD *) (src + y * pitch);
1902 for (x = 0; x < width; x++ ) {
1903 short color = (*Source++);
1904 unsigned char l = ((color >> 10) & 0xfc);
1905 short v = ((color >> 5) & 0x3e);
1906 short u = ((color ) & 0x1f);
1907 short v_conv = v + 16;
1908 short u_conv = u + 16;
1910 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1911 Dest_s += 1;
1915 break;
1918 case CONVERT_X8L8V8U8:
1920 unsigned int x, y;
1921 DWORD *Source;
1922 unsigned char *Dest;
1924 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1925 /* This implementation works with the fixed function pipeline and shaders
1926 * without further modification after converting the surface.
1928 for(y = 0; y < height; y++) {
1929 Source = (DWORD *) (src + y * pitch);
1930 Dest = dst + y * outpitch;
1931 for (x = 0; x < width; x++ ) {
1932 long color = (*Source++);
1933 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1934 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1935 /* U */ Dest[0] = (color & 0xff); /* U */
1936 /* I */ Dest[3] = 255; /* X */
1937 Dest += 4;
1940 } else {
1941 /* Doesn't work correctly with the fixed function pipeline, but can work in
1942 * shaders if the shader is adjusted. (There's no use for this format in gl's
1943 * standard fixed function pipeline anyway).
1945 for(y = 0; y < height; y++) {
1946 Source = (DWORD *) (src + y * pitch);
1947 Dest = dst + y * outpitch;
1948 for (x = 0; x < width; x++ ) {
1949 long color = (*Source++);
1950 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1951 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1952 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1953 Dest += 4;
1957 break;
1960 case CONVERT_A4L4:
1962 unsigned int x, y;
1963 unsigned char *Source;
1964 unsigned char *Dest;
1965 for(y = 0; y < height; y++) {
1966 Source = src + y * pitch;
1967 Dest = dst + y * outpitch;
1968 for (x = 0; x < width; x++ ) {
1969 unsigned char color = (*Source++);
1970 /* A */ Dest[1] = (color & 0xf0) << 0;
1971 /* L */ Dest[0] = (color & 0x0f) << 4;
1972 Dest += 2;
1975 break;
1978 case CONVERT_R32F:
1980 unsigned int x, y;
1981 float *Source;
1982 float *Dest;
1983 for(y = 0; y < height; y++) {
1984 Source = (float *) (src + y * pitch);
1985 Dest = (float *) (dst + y * outpitch);
1986 for (x = 0; x < width; x++ ) {
1987 float color = (*Source++);
1988 Dest[0] = color;
1989 Dest[1] = 1.0;
1990 Dest[2] = 1.0;
1991 Dest += 3;
1994 break;
1997 case CONVERT_R16F:
1999 unsigned int x, y;
2000 WORD *Source;
2001 WORD *Dest;
2002 WORD one = 0x3c00;
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 color = (*Source++);
2008 Dest[0] = color;
2009 Dest[1] = one;
2010 Dest[2] = one;
2011 Dest += 3;
2014 break;
2017 case CONVERT_G16R16:
2019 unsigned int x, y;
2020 WORD *Source;
2021 WORD *Dest;
2023 for(y = 0; y < height; y++) {
2024 Source = (WORD *) (src + y * pitch);
2025 Dest = (WORD *) (dst + y * outpitch);
2026 for (x = 0; x < width; x++ ) {
2027 WORD green = (*Source++);
2028 WORD red = (*Source++);
2029 Dest[0] = green;
2030 Dest[1] = red;
2031 Dest[2] = 0xffff;
2032 Dest += 3;
2035 break;
2038 default:
2039 ERR("Unsupported conversation type %d\n", convert);
2041 return WINED3D_OK;
2044 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2045 IWineD3DPaletteImpl* pal = This->palette;
2046 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2047 BOOL index_in_alpha = FALSE;
2048 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2049 int i;
2051 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2052 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2053 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2054 * duplicate entries. Store the color key in the unused alpha component to speed the
2055 * download up and to make conversion unneeded. */
2056 index_in_alpha = primary_render_target_is_p8(device);
2058 if (pal == NULL) {
2059 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2060 if(dxVersion <= 7) {
2061 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2062 return;
2065 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2066 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2067 capability flag is present (wine does advertise this capability) */
2068 for (i = 0; i < 256; i++) {
2069 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2070 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2071 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2072 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2074 } else {
2075 TRACE("Using surface palette %p\n", pal);
2076 /* Get the surface's palette */
2077 for (i = 0; i < 256; i++) {
2078 table[i][0] = pal->palents[i].peRed;
2079 table[i][1] = pal->palents[i].peGreen;
2080 table[i][2] = pal->palents[i].peBlue;
2082 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2083 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2084 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2085 of pixels that should be masked away is set to 0. */
2086 if(index_in_alpha) {
2087 table[i][3] = i;
2088 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2089 table[i][3] = 0x00;
2090 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2091 table[i][3] = pal->palents[i].peFlags;
2092 } else {
2093 table[i][3] = 0xFF;
2099 const char *fragment_palette_conversion =
2100 "!!ARBfp1.0\n"
2101 "TEMP index;\n"
2102 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
2103 "TEX index, fragment.texcoord[0], texture[0], 2D;\n" /* The alpha-component contains the palette index */
2104 "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 */
2105 "TEX result.color, index.a, texture[1], 1D;\n" /* use the alpha-component as a index in the palette to get the final color */
2106 "END";
2108 /* This function is used in case of 8bit paletted textures to upload the palette.
2109 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2110 extensions like ATI_fragment_shaders is possible.
2112 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2113 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2114 BYTE table[256][4];
2115 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2117 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2119 /* Try to use the paletted texture extension */
2120 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2122 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2123 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2125 else
2127 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2128 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2129 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2131 /* Create the fragment program if we don't have it */
2132 if(!device->paletteConversionShader)
2134 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2135 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2136 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2137 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2138 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2141 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2142 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2144 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2145 glEnable(GL_TEXTURE_1D);
2146 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2148 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2149 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2150 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2151 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2153 /* Switch back to unit 0 in which the 2D texture will be stored. */
2154 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2156 /* Rebind the texture because it isn't bound anymore */
2157 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2161 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2162 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2164 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2165 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2166 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2168 return FALSE;
2171 if(This->palette9) {
2172 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2173 return FALSE;
2175 } else {
2176 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2178 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2179 return TRUE;
2182 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2183 GLboolean oldwrite[4];
2185 /* Some formats have only some color channels, and the others are 1.0.
2186 * since our rendering renders to all channels, and those pixel formats
2187 * are emulated by using a full texture with the other channels set to 1.0
2188 * manually, clear the unused channels.
2190 * This could be done with hacking colorwriteenable to mask the colors,
2191 * but before drawing the buffer would have to be cleared too, so there's
2192 * no gain in that
2194 switch(This->resource.format) {
2195 case WINED3DFMT_R16F:
2196 case WINED3DFMT_R32F:
2197 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2198 /* Do not activate a context, the correct drawable is active already
2199 * though just the read buffer is set, make sure to have the correct draw
2200 * buffer too
2202 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2203 glDisable(GL_SCISSOR_TEST);
2204 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2205 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2206 glClearColor(0.0, 1.0, 1.0, 1.0);
2207 glClear(GL_COLOR_BUFFER_BIT);
2208 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2209 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2210 checkGLcall("Unused channel clear\n");
2211 break;
2213 default: break;
2217 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2218 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2220 if (!(This->Flags & SFLAG_INTEXTURE)) {
2221 TRACE("Reloading because surface is dirty\n");
2222 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2223 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2224 /* Reload: vice versa OR */
2225 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2226 /* Also reload: Color key is active AND the color key has changed */
2227 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2228 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2229 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2230 TRACE("Reloading because of color keying\n");
2231 /* To perform the color key conversion we need a sysmem copy of
2232 * the surface. Make sure we have it
2235 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2236 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2237 /* TODO: This is not necessarily needed with hw palettized texture support */
2238 This->Flags &= ~SFLAG_INTEXTURE;
2239 } else {
2240 TRACE("surface is already in texture\n");
2241 return WINED3D_OK;
2244 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2245 * These resources are not bound by device size or format restrictions. Because of this,
2246 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2247 * However, these resources can always be created, locked, and copied.
2249 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2251 FIXME("(%p) Operation not supported for scratch textures\n",This);
2252 return WINED3DERR_INVALIDCALL;
2255 This->srgb = srgb_mode;
2256 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2258 #if 0
2260 static unsigned int gen = 0;
2261 char buffer[4096];
2262 ++gen;
2263 if ((gen % 10) == 0) {
2264 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2265 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2268 * debugging crash code
2269 if (gen == 250) {
2270 void** test = NULL;
2271 *test = 0;
2275 #endif
2277 if (!(This->Flags & SFLAG_DONOTFREE)) {
2278 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2279 This->resource.allocatedMemory = NULL;
2280 This->resource.heapMemory = NULL;
2281 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2284 return WINED3D_OK;
2287 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2288 /* TODO: check for locks */
2289 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2290 IWineD3DBaseTexture *baseTexture = NULL;
2291 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2293 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2294 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2295 TRACE("Passing to container\n");
2296 IWineD3DBaseTexture_BindTexture(baseTexture);
2297 IWineD3DBaseTexture_Release(baseTexture);
2298 } else {
2299 TRACE("(%p) : Binding surface\n", This);
2301 if(!device->isInDraw) {
2302 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2304 ENTER_GL();
2305 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2306 LEAVE_GL();
2308 return;
2311 #include <errno.h>
2312 #include <stdio.h>
2313 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2314 FILE* f = NULL;
2315 UINT i, y;
2316 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2317 char *allocatedMemory;
2318 char *textureRow;
2319 IWineD3DSwapChain *swapChain = NULL;
2320 int width, height;
2321 GLuint tmpTexture = 0;
2322 DWORD color;
2323 /*FIXME:
2324 Textures may not be stored in ->allocatedgMemory and a GlTexture
2325 so we should lock the surface before saving a snapshot, or at least check that
2327 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2328 by calling GetTexImage and in compressed form by calling
2329 GetCompressedTexImageARB. Queried compressed images can be saved and
2330 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2331 texture images do not need to be processed by the GL and should
2332 significantly improve texture loading performance relative to uncompressed
2333 images. */
2335 /* Setup the width and height to be the internal texture width and height. */
2336 width = This->pow2Width;
2337 height = This->pow2Height;
2338 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2339 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2341 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2342 /* if were not a real texture then read the back buffer into a real texture */
2343 /* we don't want to interfere with the back buffer so read the data into a temporary
2344 * texture and then save the data out of the temporary texture
2346 GLint prevRead;
2347 ENTER_GL();
2348 TRACE("(%p) Reading render target into texture\n", This);
2349 glEnable(GL_TEXTURE_2D);
2351 glGenTextures(1, &tmpTexture);
2352 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2354 glTexImage2D(GL_TEXTURE_2D,
2356 GL_RGBA,
2357 width,
2358 height,
2359 0/*border*/,
2360 GL_RGBA,
2361 GL_UNSIGNED_INT_8_8_8_8_REV,
2362 NULL);
2364 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2365 vcheckGLcall("glGetIntegerv");
2366 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2367 vcheckGLcall("glReadBuffer");
2368 glCopyTexImage2D(GL_TEXTURE_2D,
2370 GL_RGBA,
2373 width,
2374 height,
2377 checkGLcall("glCopyTexImage2D");
2378 glReadBuffer(prevRead);
2379 LEAVE_GL();
2381 } else { /* bind the real texture, and make sure it up to date */
2382 IWineD3DSurface_PreLoad(iface);
2384 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2385 ENTER_GL();
2386 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2387 glGetTexImage(GL_TEXTURE_2D,
2388 This->glDescription.level,
2389 GL_RGBA,
2390 GL_UNSIGNED_INT_8_8_8_8_REV,
2391 allocatedMemory);
2392 checkGLcall("glTexImage2D");
2393 if (tmpTexture) {
2394 glBindTexture(GL_TEXTURE_2D, 0);
2395 glDeleteTextures(1, &tmpTexture);
2397 LEAVE_GL();
2399 f = fopen(filename, "w+");
2400 if (NULL == f) {
2401 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2402 return WINED3DERR_INVALIDCALL;
2404 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2405 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2406 /* TGA header */
2407 fputc(0,f);
2408 fputc(0,f);
2409 fputc(2,f);
2410 fputc(0,f);
2411 fputc(0,f);
2412 fputc(0,f);
2413 fputc(0,f);
2414 fputc(0,f);
2415 fputc(0,f);
2416 fputc(0,f);
2417 fputc(0,f);
2418 fputc(0,f);
2419 /* short width*/
2420 fwrite(&width,2,1,f);
2421 /* short height */
2422 fwrite(&height,2,1,f);
2423 /* format rgba */
2424 fputc(0x20,f);
2425 fputc(0x28,f);
2426 /* raw data */
2427 /* 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 */
2428 if(swapChain)
2429 textureRow = allocatedMemory + (width * (height - 1) *4);
2430 else
2431 textureRow = allocatedMemory;
2432 for (y = 0 ; y < height; y++) {
2433 for (i = 0; i < width; i++) {
2434 color = *((DWORD*)textureRow);
2435 fputc((color >> 16) & 0xFF, f); /* B */
2436 fputc((color >> 8) & 0xFF, f); /* G */
2437 fputc((color >> 0) & 0xFF, f); /* R */
2438 fputc((color >> 24) & 0xFF, f); /* A */
2439 textureRow += 4;
2441 /* take two rows of the pointer to the texture memory */
2442 if(swapChain)
2443 (textureRow-= width << 3);
2446 TRACE("Closing file\n");
2447 fclose(f);
2449 if(swapChain) {
2450 IWineD3DSwapChain_Release(swapChain);
2452 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2453 return WINED3D_OK;
2457 * Slightly inefficient way to handle multiple dirty rects but it works :)
2459 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2460 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2461 IWineD3DBaseTexture *baseTexture = NULL;
2463 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2464 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2466 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2467 if (NULL != pDirtyRect) {
2468 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2469 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2470 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2471 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2472 } else {
2473 This->dirtyRect.left = 0;
2474 This->dirtyRect.top = 0;
2475 This->dirtyRect.right = This->currentDesc.Width;
2476 This->dirtyRect.bottom = This->currentDesc.Height;
2478 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2479 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2480 /* if the container is a basetexture then mark it dirty. */
2481 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2482 TRACE("Passing to container\n");
2483 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2484 IWineD3DBaseTexture_Release(baseTexture);
2486 return WINED3D_OK;
2489 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2490 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2491 HRESULT hr;
2492 const GlPixelFormatDesc *glDesc;
2493 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2495 TRACE("(%p) : Calling base function first\n", This);
2496 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2497 if(SUCCEEDED(hr)) {
2498 /* Setup some glformat defaults */
2499 This->glDescription.glFormat = glDesc->glFormat;
2500 This->glDescription.glFormatInternal = glDesc->glInternal;
2501 This->glDescription.glType = glDesc->glType;
2503 This->Flags &= ~SFLAG_ALLOCATED;
2504 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2505 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2507 return hr;
2510 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2511 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2513 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2514 WARN("Surface is locked or the HDC is in use\n");
2515 return WINED3DERR_INVALIDCALL;
2518 if(Mem && Mem != This->resource.allocatedMemory) {
2519 void *release = NULL;
2521 /* Do I have to copy the old surface content? */
2522 if(This->Flags & SFLAG_DIBSECTION) {
2523 /* Release the DC. No need to hold the critical section for the update
2524 * Thread because this thread runs only on front buffers, but this method
2525 * fails for render targets in the check above.
2527 SelectObject(This->hDC, This->dib.holdbitmap);
2528 DeleteDC(This->hDC);
2529 /* Release the DIB section */
2530 DeleteObject(This->dib.DIBsection);
2531 This->dib.bitmap_data = NULL;
2532 This->resource.allocatedMemory = NULL;
2533 This->hDC = NULL;
2534 This->Flags &= ~SFLAG_DIBSECTION;
2535 } else if(!(This->Flags & SFLAG_USERPTR)) {
2536 release = This->resource.heapMemory;
2537 This->resource.heapMemory = NULL;
2539 This->resource.allocatedMemory = Mem;
2540 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2542 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2543 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2545 /* For client textures opengl has to be notified */
2546 if(This->Flags & SFLAG_CLIENT) {
2547 This->Flags &= ~SFLAG_ALLOCATED;
2548 IWineD3DSurface_PreLoad(iface);
2549 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2552 /* Now free the old memory if any */
2553 HeapFree(GetProcessHeap(), 0, release);
2554 } else if(This->Flags & SFLAG_USERPTR) {
2555 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2556 This->resource.allocatedMemory = NULL;
2557 /* HeapMemory should be NULL already */
2558 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2559 This->Flags &= ~SFLAG_USERPTR;
2561 if(This->Flags & SFLAG_CLIENT) {
2562 This->Flags &= ~SFLAG_ALLOCATED;
2563 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2564 IWineD3DSurface_PreLoad(iface);
2567 return WINED3D_OK;
2570 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2571 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2572 IWineD3DSwapChainImpl *swapchain = NULL;
2573 HRESULT hr;
2574 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2576 /* Flipping is only supported on RenderTargets */
2577 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2579 if(override) {
2580 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2581 * FIXME("(%p) Target override is not supported by now\n", This);
2582 * Additionally, it isn't really possible to support triple-buffering
2583 * properly on opengl at all
2587 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2588 if(!swapchain) {
2589 ERR("Flipped surface is not on a swapchain\n");
2590 return WINEDDERR_NOTFLIPPABLE;
2593 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2594 * and only d3d8 and d3d9 apps specify the presentation interval
2596 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2597 /* Most common case first to avoid wasting time on all the other cases */
2598 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2599 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2600 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2601 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2602 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2603 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2604 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2605 } else {
2606 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2609 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2610 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2611 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2612 return hr;
2615 /* Does a direct frame buffer -> texture copy. Stretching is done
2616 * with single pixel copy calls
2618 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2619 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2620 float xrel, yrel;
2621 UINT row;
2622 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2625 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2626 ENTER_GL();
2627 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2629 /* TODO: Do we need GL_TEXTURE_2D enabled fpr copyteximage? */
2630 glEnable(This->glDescription.target);
2631 checkGLcall("glEnable(This->glDescription.target)");
2633 /* Bind the target texture */
2634 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2635 checkGLcall("glBindTexture");
2636 if(!swapchain) {
2637 glReadBuffer(myDevice->offscreenBuffer);
2638 } else {
2639 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2640 glReadBuffer(buffer);
2642 checkGLcall("glReadBuffer");
2644 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2645 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2647 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2648 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2650 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2651 ERR("Texture filtering not supported in direct blit\n");
2653 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2654 ERR("Texture filtering not supported in direct blit\n");
2657 if(upsidedown &&
2658 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2659 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2660 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2662 glCopyTexSubImage2D(This->glDescription.target,
2663 This->glDescription.level,
2664 drect->x1, drect->y1, /* xoffset, yoffset */
2665 srect->x1, Src->currentDesc.Height - srect->y2,
2666 drect->x2 - drect->x1, drect->y2 - drect->y1);
2667 } else {
2668 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2669 /* I have to process this row by row to swap the image,
2670 * otherwise it would be upside down, so stretching in y direction
2671 * doesn't cost extra time
2673 * However, stretching in x direction can be avoided if not necessary
2675 for(row = drect->y1; row < drect->y2; row++) {
2676 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2677 /* Well, that stuff works, but it's very slow.
2678 * find a better way instead
2680 UINT col;
2682 for(col = drect->x1; col < drect->x2; col++) {
2683 glCopyTexSubImage2D(This->glDescription.target,
2684 This->glDescription.level,
2685 drect->x1 + col, row, /* xoffset, yoffset */
2686 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2687 1, 1);
2689 } else {
2690 glCopyTexSubImage2D(This->glDescription.target,
2691 This->glDescription.level,
2692 drect->x1, row, /* xoffset, yoffset */
2693 srect->x1, yoffset - (int) (row * yrel),
2694 drect->x2-drect->x1, 1);
2698 vcheckGLcall("glCopyTexSubImage2D");
2700 /* Leave the opengl state valid for blitting */
2701 glDisable(This->glDescription.target);
2702 checkGLcall("glDisable(This->glDescription.target)");
2704 LEAVE_GL();
2707 /* Uses the hardware to stretch and flip the image */
2708 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2709 GLuint src, backup = 0;
2710 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2711 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2712 float left, right, top, bottom; /* Texture coordinates */
2713 UINT fbwidth = Src->currentDesc.Width;
2714 UINT fbheight = Src->currentDesc.Height;
2715 GLenum drawBuffer = GL_BACK;
2716 GLenum texture_target;
2718 TRACE("Using hwstretch blit\n");
2719 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2720 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2721 ENTER_GL();
2723 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2725 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2726 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2728 if(GL_LIMITS(aux_buffers) >= 2) {
2729 /* Got more than one aux buffer? Use the 2nd aux buffer */
2730 drawBuffer = GL_AUX1;
2731 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2732 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2733 drawBuffer = GL_AUX0;
2736 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2737 glGenTextures(1, &backup);
2738 checkGLcall("glGenTextures\n");
2739 glBindTexture(GL_TEXTURE_2D, backup);
2740 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2741 texture_target = GL_TEXTURE_2D;
2742 } else {
2743 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2744 * we are reading from the back buffer, the backup can be used as source texture
2746 if(Src->glDescription.textureName == 0) {
2747 /* Get it a description */
2748 IWineD3DSurface_PreLoad(SrcSurface);
2750 texture_target = Src->glDescription.target;
2751 glBindTexture(texture_target, Src->glDescription.textureName);
2752 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2753 glEnable(texture_target);
2754 checkGLcall("glEnable(texture_target)");
2756 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2757 Src->Flags &= ~SFLAG_INTEXTURE;
2760 glReadBuffer(GL_BACK);
2761 checkGLcall("glReadBuffer(GL_BACK)");
2763 /* TODO: Only back up the part that will be overwritten */
2764 glCopyTexSubImage2D(texture_target, 0,
2765 0, 0 /* read offsets */,
2766 0, 0,
2767 fbwidth,
2768 fbheight);
2770 checkGLcall("glCopyTexSubImage2D");
2772 /* No issue with overriding these - the sampler is dirty due to blit usage */
2773 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2774 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2775 checkGLcall("glTexParameteri");
2776 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2777 minMipLookup[Filter][WINED3DTEXF_NONE]);
2778 checkGLcall("glTexParameteri");
2780 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2781 src = backup ? backup : Src->glDescription.textureName;
2782 } else {
2783 glReadBuffer(GL_FRONT);
2784 checkGLcall("glReadBuffer(GL_FRONT)");
2786 glGenTextures(1, &src);
2787 checkGLcall("glGenTextures(1, &src)");
2788 glBindTexture(GL_TEXTURE_2D, src);
2789 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2791 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2792 * out for power of 2 sizes
2794 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2795 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2796 checkGLcall("glTexImage2D");
2797 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2798 0, 0 /* read offsets */,
2799 0, 0,
2800 fbwidth,
2801 fbheight);
2803 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2804 checkGLcall("glTexParameteri");
2805 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2806 checkGLcall("glTexParameteri");
2808 glReadBuffer(GL_BACK);
2809 checkGLcall("glReadBuffer(GL_BACK)");
2811 if(texture_target != GL_TEXTURE_2D) {
2812 glDisable(texture_target);
2813 glEnable(GL_TEXTURE_2D);
2814 texture_target = GL_TEXTURE_2D;
2817 checkGLcall("glEnd and previous");
2819 left = (float) srect->x1 / (float) Src->pow2Width;
2820 right = (float) srect->x2 / (float) Src->pow2Width;
2822 if(upsidedown) {
2823 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2824 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2825 } else {
2826 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2827 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2830 /* draw the source texture stretched and upside down. The correct surface is bound already */
2831 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
2832 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
2834 glDrawBuffer(drawBuffer);
2835 glReadBuffer(drawBuffer);
2837 glBegin(GL_QUADS);
2838 /* bottom left */
2839 glTexCoord2f(left, bottom);
2840 glVertex2i(0, fbheight);
2842 /* top left */
2843 glTexCoord2f(left, top);
2844 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2846 /* top right */
2847 glTexCoord2f(right, top);
2848 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2850 /* bottom right */
2851 glTexCoord2f(right, bottom);
2852 glVertex2i(drect->x2 - drect->x1, fbheight);
2853 glEnd();
2854 checkGLcall("glEnd and previous");
2856 if(texture_target != This->glDescription.target) {
2857 glDisable(texture_target);
2858 glEnable(This->glDescription.target);
2859 texture_target = This->glDescription.target;
2862 /* Now read the stretched and upside down image into the destination texture */
2863 glBindTexture(texture_target, This->glDescription.textureName);
2864 checkGLcall("glBindTexture");
2865 glCopyTexSubImage2D(texture_target,
2867 drect->x1, drect->y1, /* xoffset, yoffset */
2868 0, 0, /* We blitted the image to the origin */
2869 drect->x2 - drect->x1, drect->y2 - drect->y1);
2870 checkGLcall("glCopyTexSubImage2D");
2872 if(drawBuffer == GL_BACK) {
2873 /* Write the back buffer backup back */
2874 if(backup) {
2875 if(texture_target != GL_TEXTURE_2D) {
2876 glDisable(texture_target);
2877 glEnable(GL_TEXTURE_2D);
2878 texture_target = GL_TEXTURE_2D;
2880 glBindTexture(GL_TEXTURE_2D, backup);
2881 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
2882 } else {
2883 if(texture_target != Src->glDescription.target) {
2884 glDisable(texture_target);
2885 glEnable(Src->glDescription.target);
2886 texture_target = Src->glDescription.target;
2888 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
2889 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2892 glBegin(GL_QUADS);
2893 /* top left */
2894 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2895 glVertex2i(0, 0);
2897 /* bottom left */
2898 glTexCoord2f(0.0, 0.0);
2899 glVertex2i(0, fbheight);
2901 /* bottom right */
2902 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2903 glVertex2i(fbwidth, Src->currentDesc.Height);
2905 /* top right */
2906 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2907 glVertex2i(fbwidth, 0);
2908 glEnd();
2909 } else {
2910 /* Restore the old draw buffer */
2911 glDrawBuffer(GL_BACK);
2913 glDisable(texture_target);
2914 checkGLcall("glDisable(texture_target)");
2916 /* Cleanup */
2917 if(src != Src->glDescription.textureName && src != backup) {
2918 glDeleteTextures(1, &src);
2919 checkGLcall("glDeleteTextures(1, &src)");
2921 if(backup) {
2922 glDeleteTextures(1, &backup);
2923 checkGLcall("glDeleteTextures(1, &backup)");
2926 LEAVE_GL();
2929 /* Not called from the VTable */
2930 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2931 WINED3DRECT rect;
2932 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2933 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2934 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2936 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2938 /* Get the swapchain. One of the surfaces has to be a primary surface */
2939 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2940 WARN("Destination is in sysmem, rejecting gl blt\n");
2941 return WINED3DERR_INVALIDCALL;
2943 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2944 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2945 if(Src) {
2946 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2947 WARN("Src is in sysmem, rejecting gl blt\n");
2948 return WINED3DERR_INVALIDCALL;
2950 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2951 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2954 /* Early sort out of cases where no render target is used */
2955 if(!dstSwapchain && !srcSwapchain &&
2956 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2957 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2958 return WINED3DERR_INVALIDCALL;
2961 /* No destination color keying supported */
2962 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2963 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2964 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2965 return WINED3DERR_INVALIDCALL;
2968 if (DestRect) {
2969 rect.x1 = DestRect->left;
2970 rect.y1 = DestRect->top;
2971 rect.x2 = DestRect->right;
2972 rect.y2 = DestRect->bottom;
2973 } else {
2974 rect.x1 = 0;
2975 rect.y1 = 0;
2976 rect.x2 = This->currentDesc.Width;
2977 rect.y2 = This->currentDesc.Height;
2980 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2981 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2982 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2983 /* Half-life does a Blt from the back buffer to the front buffer,
2984 * Full surface size, no flags... Use present instead
2986 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2989 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2990 while(1)
2992 RECT mySrcRect;
2993 TRACE("Looking if a Present can be done...\n");
2994 /* Source Rectangle must be full surface */
2995 if( SrcRect ) {
2996 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2997 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2998 TRACE("No, Source rectangle doesn't match\n");
2999 break;
3002 mySrcRect.left = 0;
3003 mySrcRect.top = 0;
3004 mySrcRect.right = Src->currentDesc.Width;
3005 mySrcRect.bottom = Src->currentDesc.Height;
3007 /* No stretching may occur */
3008 if(mySrcRect.right != rect.x2 - rect.x1 ||
3009 mySrcRect.bottom != rect.y2 - rect.y1) {
3010 TRACE("No, stretching is done\n");
3011 break;
3014 /* Destination must be full surface or match the clipping rectangle */
3015 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3017 RECT cliprect;
3018 POINT pos[2];
3019 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3020 pos[0].x = rect.x1;
3021 pos[0].y = rect.y1;
3022 pos[1].x = rect.x2;
3023 pos[1].y = rect.y2;
3024 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3025 pos, 2);
3027 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3028 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3030 TRACE("No, dest rectangle doesn't match(clipper)\n");
3031 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3032 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3033 break;
3036 else
3038 if(rect.x1 != 0 || rect.y1 != 0 ||
3039 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3040 TRACE("No, dest rectangle doesn't match(surface size)\n");
3041 break;
3045 TRACE("Yes\n");
3047 /* These flags are unimportant for the flag check, remove them */
3048 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3049 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3051 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3052 * take very long, while a flip is fast.
3053 * This applies to Half-Life, which does such Blts every time it finished
3054 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3055 * menu. This is also used by all apps when they do windowed rendering
3057 * The problem is that flipping is not really the same as copying. After a
3058 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3059 * untouched. Therefore it's necessary to override the swap effect
3060 * and to set it back after the flip.
3062 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3063 * testcases.
3066 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3067 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3069 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3070 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3072 dstSwapchain->presentParms.SwapEffect = orig_swap;
3074 return WINED3D_OK;
3076 break;
3079 TRACE("Unsupported blit between buffers on the same swapchain\n");
3080 return WINED3DERR_INVALIDCALL;
3081 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3082 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3083 return WINED3DERR_INVALIDCALL;
3084 } else if(dstSwapchain && srcSwapchain) {
3085 FIXME("Implement hardware blit between two different swapchains\n");
3086 return WINED3DERR_INVALIDCALL;
3087 } else if(dstSwapchain) {
3088 if(SrcSurface == myDevice->render_targets[0]) {
3089 TRACE("Blit from active render target to a swapchain\n");
3090 /* Handled with regular texture -> swapchain blit */
3092 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3093 FIXME("Implement blit from a swapchain to the active render target\n");
3094 return WINED3DERR_INVALIDCALL;
3097 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3098 /* Blit from render target to texture */
3099 WINED3DRECT srect;
3100 BOOL upsideDown, stretchx;
3102 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3103 TRACE("Color keying not supported by frame buffer to texture blit\n");
3104 return WINED3DERR_INVALIDCALL;
3105 /* Destination color key is checked above */
3108 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3109 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3111 if(SrcRect) {
3112 if(SrcRect->top < SrcRect->bottom) {
3113 srect.y1 = SrcRect->top;
3114 srect.y2 = SrcRect->bottom;
3115 upsideDown = FALSE;
3116 } else {
3117 srect.y1 = SrcRect->bottom;
3118 srect.y2 = SrcRect->top;
3119 upsideDown = TRUE;
3121 srect.x1 = SrcRect->left;
3122 srect.x2 = SrcRect->right;
3123 } else {
3124 srect.x1 = 0;
3125 srect.y1 = 0;
3126 srect.x2 = Src->currentDesc.Width;
3127 srect.y2 = Src->currentDesc.Height;
3128 upsideDown = FALSE;
3130 if(rect.x1 > rect.x2) {
3131 UINT tmp = rect.x2;
3132 rect.x2 = rect.x1;
3133 rect.x1 = tmp;
3134 upsideDown = !upsideDown;
3136 if(!srcSwapchain) {
3137 TRACE("Reading from an offscreen target\n");
3138 upsideDown = !upsideDown;
3141 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3142 stretchx = TRUE;
3143 } else {
3144 stretchx = FALSE;
3147 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3148 * flip the image nor scale it.
3150 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3151 * -> If the app wants a image width an unscaled width, copy it line per line
3152 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3153 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3154 * back buffer. This is slower than reading line per line, thus not used for flipping
3155 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3156 * pixel by pixel
3158 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3159 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3160 * backends.
3162 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3163 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3164 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3165 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3166 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3167 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3168 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3169 } else {
3170 TRACE("Using hardware stretching to flip / stretch the texture\n");
3171 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3174 if(!(This->Flags & SFLAG_DONOTFREE)) {
3175 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3176 This->resource.allocatedMemory = NULL;
3177 This->resource.heapMemory = NULL;
3178 } else {
3179 This->Flags &= ~SFLAG_INSYSMEM;
3181 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3182 * path is never entered
3184 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3186 return WINED3D_OK;
3187 } else if(Src) {
3188 /* Blit from offscreen surface to render target */
3189 float glTexCoord[4];
3190 DWORD oldCKeyFlags = Src->CKeyFlags;
3191 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
3192 RECT SourceRectangle;
3193 BOOL paletteOverride = FALSE;
3195 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3197 if(SrcRect) {
3198 SourceRectangle.left = SrcRect->left;
3199 SourceRectangle.right = SrcRect->right;
3200 SourceRectangle.top = SrcRect->top;
3201 SourceRectangle.bottom = SrcRect->bottom;
3202 } else {
3203 SourceRectangle.left = 0;
3204 SourceRectangle.right = Src->currentDesc.Width;
3205 SourceRectangle.top = 0;
3206 SourceRectangle.bottom = Src->currentDesc.Height;
3208 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3209 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3210 TRACE("Using stretch_rect_fbo\n");
3211 /* The source is always a texture, but never the currently active render target, and the texture
3212 * contents are never upside down
3214 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3215 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3216 return WINED3D_OK;
3219 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3220 /* Fall back to software */
3221 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3222 SourceRectangle.left, SourceRectangle.top,
3223 SourceRectangle.right, SourceRectangle.bottom);
3224 return WINED3DERR_INVALIDCALL;
3227 /* Color keying: Check if we have to do a color keyed blt,
3228 * and if not check if a color key is activated.
3230 * Just modify the color keying parameters in the surface and restore them afterwards
3231 * The surface keeps track of the color key last used to load the opengl surface.
3232 * PreLoad will catch the change to the flags and color key and reload if necessary.
3234 if(Flags & WINEDDBLT_KEYSRC) {
3235 /* Use color key from surface */
3236 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3237 /* Use color key from DDBltFx */
3238 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3239 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3240 } else {
3241 /* Do not use color key */
3242 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3245 /* When blitting from an offscreen surface to a rendertarget, the source
3246 * surface is not required to have a palette. Our rendering / conversion
3247 * code further down the road retrieves the palette from the surface, so
3248 * it must have a palette set. */
3249 if((Src->resource.format == WINED3DFMT_P8) && (Src->palette == NULL)) {
3250 paletteOverride = TRUE;
3251 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3252 Src->palette = This->palette;
3255 /* Now load the surface */
3256 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3259 /* Activate the destination context, set it up for blitting */
3260 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3261 ENTER_GL();
3263 glEnable(Src->glDescription.target);
3264 checkGLcall("glEnable(Src->glDescription.target)");
3266 if(!dstSwapchain) {
3267 TRACE("Drawing to offscreen buffer\n");
3268 glDrawBuffer(myDevice->offscreenBuffer);
3269 checkGLcall("glDrawBuffer");
3270 } else {
3271 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3272 TRACE("Drawing to %#x buffer\n", buffer);
3273 glDrawBuffer(buffer);
3274 checkGLcall("glDrawBuffer");
3277 /* Bind the texture */
3278 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3279 checkGLcall("glBindTexture");
3281 /* Filtering for StretchRect */
3282 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3283 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3284 checkGLcall("glTexParameteri");
3285 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3286 minMipLookup[Filter][WINED3DTEXF_NONE]);
3287 checkGLcall("glTexParameteri");
3288 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3289 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3290 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3291 checkGLcall("glTexEnvi");
3293 /* This is for color keying */
3294 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3295 glEnable(GL_ALPHA_TEST);
3296 checkGLcall("glEnable GL_ALPHA_TEST");
3298 /* When the primary render target uses P8, the alpha component contains the palette index.
3299 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3300 * should be masked away have alpha set to 0. */
3301 if(primary_render_target_is_p8(myDevice))
3302 glAlphaFunc(GL_NOTEQUAL, (float)This->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3303 else
3304 glAlphaFunc(GL_NOTEQUAL, 0.0);
3305 checkGLcall("glAlphaFunc\n");
3306 } else {
3307 glDisable(GL_ALPHA_TEST);
3308 checkGLcall("glDisable GL_ALPHA_TEST");
3311 /* Draw a textured quad
3313 glBegin(GL_QUADS);
3315 glColor3d(1.0f, 1.0f, 1.0f);
3316 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3317 glVertex3f(rect.x1,
3318 rect.y1,
3319 0.0);
3321 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3322 glVertex3f(rect.x1, rect.y2, 0.0);
3324 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3325 glVertex3f(rect.x2,
3326 rect.y2,
3327 0.0);
3329 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3330 glVertex3f(rect.x2,
3331 rect.y1,
3332 0.0);
3333 glEnd();
3334 checkGLcall("glEnd");
3336 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3337 glDisable(GL_ALPHA_TEST);
3338 checkGLcall("glDisable(GL_ALPHA_TEST)");
3341 /* Flush in case the drawable is used by multiple GL contexts */
3342 if(dstSwapchain && (dstSwapchain->num_contexts >= 2))
3343 glFlush();
3345 glBindTexture(Src->glDescription.target, 0);
3346 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3347 /* Leave the opengl state valid for blitting */
3348 glDisable(Src->glDescription.target);
3349 checkGLcall("glDisable(Src->glDescription.target)");
3351 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3352 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3354 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3355 glDrawBuffer(GL_BACK);
3356 checkGLcall("glDrawBuffer");
3358 /* Restore the color key parameters */
3359 Src->CKeyFlags = oldCKeyFlags;
3360 This->SrcBltCKey = oldBltCKey;
3362 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3363 if(paletteOverride)
3364 Src->palette = NULL;
3366 LEAVE_GL();
3368 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3369 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3370 * is outdated now
3372 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3373 /* TODO: This should be moved to ModifyLocation() */
3374 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3375 This->Flags |= SFLAG_INTEXTURE;
3378 return WINED3D_OK;
3379 } else {
3380 /* Source-Less Blit to render target */
3381 if (Flags & WINEDDBLT_COLORFILL) {
3382 /* This is easy to handle for the D3D Device... */
3383 DWORD color;
3385 TRACE("Colorfill\n");
3387 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3388 must be true if we are here */
3389 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3390 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3391 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3392 TRACE("Surface is higher back buffer, falling back to software\n");
3393 return WINED3DERR_INVALIDCALL;
3396 /* The color as given in the Blt function is in the format of the frame-buffer...
3397 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3399 if (This->resource.format == WINED3DFMT_P8) {
3400 DWORD alpha;
3402 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3403 else alpha = 0xFF000000;
3405 if (This->palette) {
3406 color = (alpha |
3407 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3408 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3409 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3410 } else {
3411 color = alpha;
3414 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3415 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3416 color = 0xFFFFFFFF;
3417 } else {
3418 color = ((0xFF000000) |
3419 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3420 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3421 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3424 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3425 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3426 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3428 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3429 color = DDBltFx->u5.dwFillColor;
3431 else {
3432 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3433 return WINED3DERR_INVALIDCALL;
3436 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3437 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3438 1, /* Number of rectangles */
3439 &rect, WINED3DCLEAR_TARGET, color,
3440 0.0 /* Z */,
3441 0 /* Stencil */);
3442 return WINED3D_OK;
3446 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3447 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3448 return WINED3DERR_INVALIDCALL;
3451 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3453 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3454 float depth;
3456 if (Flags & WINEDDBLT_DEPTHFILL) {
3457 switch(This->resource.format) {
3458 case WINED3DFMT_D16:
3459 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3460 break;
3461 case WINED3DFMT_D15S1:
3462 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3463 break;
3464 case WINED3DFMT_D24S8:
3465 case WINED3DFMT_D24X8:
3466 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3467 break;
3468 case WINED3DFMT_D32:
3469 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3470 break;
3471 default:
3472 depth = 0.0;
3473 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3476 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3477 DestRect == NULL ? 0 : 1,
3478 (WINED3DRECT *) DestRect,
3479 WINED3DCLEAR_ZBUFFER,
3480 0x00000000,
3481 depth,
3482 0x00000000);
3485 FIXME("(%p): Unsupp depthstencil blit\n", This);
3486 return WINED3DERR_INVALIDCALL;
3489 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3490 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3491 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3492 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3493 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3494 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3496 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3498 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3499 return WINEDDERR_SURFACEBUSY;
3502 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3503 * except depth blits, which seem to work
3505 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3506 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3507 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3508 return WINED3DERR_INVALIDCALL;
3509 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3510 TRACE("Z Blit override handled the blit\n");
3511 return WINED3D_OK;
3515 /* Special cases for RenderTargets */
3516 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3517 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3518 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3521 /* For the rest call the X11 surface implementation.
3522 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3523 * other Blts are rather rare
3525 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3528 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3529 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3530 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3531 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3532 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3534 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3536 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3537 return WINEDDERR_SURFACEBUSY;
3540 if(myDevice->inScene &&
3541 (iface == myDevice->stencilBufferTarget ||
3542 (Source && Source == myDevice->stencilBufferTarget))) {
3543 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3544 return WINED3DERR_INVALIDCALL;
3547 /* Special cases for RenderTargets */
3548 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3549 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3551 RECT SrcRect, DstRect;
3552 DWORD Flags=0;
3554 if(rsrc) {
3555 SrcRect.left = rsrc->left;
3556 SrcRect.top= rsrc->top;
3557 SrcRect.bottom = rsrc->bottom;
3558 SrcRect.right = rsrc->right;
3559 } else {
3560 SrcRect.left = 0;
3561 SrcRect.top = 0;
3562 SrcRect.right = srcImpl->currentDesc.Width;
3563 SrcRect.bottom = srcImpl->currentDesc.Height;
3566 DstRect.left = dstx;
3567 DstRect.top=dsty;
3568 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3569 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3571 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3572 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3573 Flags |= WINEDDBLT_KEYSRC;
3574 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3575 Flags |= WINEDDBLT_KEYDEST;
3576 if(trans & WINEDDBLTFAST_WAIT)
3577 Flags |= WINEDDBLT_WAIT;
3578 if(trans & WINEDDBLTFAST_DONOTWAIT)
3579 Flags |= WINEDDBLT_DONOTWAIT;
3581 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3585 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3588 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3589 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3590 RGBQUAD col[256];
3591 IWineD3DPaletteImpl *pal = This->palette;
3592 unsigned int n;
3593 TRACE("(%p)\n", This);
3595 if (!pal) return WINED3D_OK;
3597 if(This->resource.format == WINED3DFMT_P8 ||
3598 This->resource.format == WINED3DFMT_A8P8)
3600 if(!(This->Flags & SFLAG_INSYSMEM)) {
3601 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3602 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3604 TRACE("Dirtifying surface\n");
3605 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3608 if(This->Flags & SFLAG_DIBSECTION) {
3609 TRACE("(%p): Updating the hdc's palette\n", This);
3610 for (n=0; n<256; n++) {
3611 col[n].rgbRed = pal->palents[n].peRed;
3612 col[n].rgbGreen = pal->palents[n].peGreen;
3613 col[n].rgbBlue = pal->palents[n].peBlue;
3614 col[n].rgbReserved = 0;
3616 SetDIBColorTable(This->hDC, 0, 256, col);
3619 /* Propagate the changes to the drawable when we have a palette.
3620 * TODO: in case of hardware p8 palettes we should only upload the palette. */
3621 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3622 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3624 return WINED3D_OK;
3627 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3628 /** Check against the maximum texture sizes supported by the video card **/
3629 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3630 unsigned int pow2Width, pow2Height;
3631 const GlPixelFormatDesc *glDesc;
3633 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3634 /* Setup some glformat defaults */
3635 This->glDescription.glFormat = glDesc->glFormat;
3636 This->glDescription.glFormatInternal = glDesc->glInternal;
3637 This->glDescription.glType = glDesc->glType;
3639 This->glDescription.textureName = 0;
3640 This->glDescription.target = GL_TEXTURE_2D;
3642 /* Non-power2 support */
3643 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3644 pow2Width = This->currentDesc.Width;
3645 pow2Height = This->currentDesc.Height;
3646 } else {
3647 /* Find the nearest pow2 match */
3648 pow2Width = pow2Height = 1;
3649 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3650 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3652 This->pow2Width = pow2Width;
3653 This->pow2Height = pow2Height;
3655 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3656 WINED3DFORMAT Format = This->resource.format;
3657 /** TODO: add support for non power two compressed textures **/
3658 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3659 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3660 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3661 This, This->currentDesc.Width, This->currentDesc.Height);
3662 return WINED3DERR_NOTAVAILABLE;
3666 if(pow2Width != This->currentDesc.Width ||
3667 pow2Height != This->currentDesc.Height) {
3668 This->Flags |= SFLAG_NONPOW2;
3671 TRACE("%p\n", This);
3672 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3673 /* one of three options
3674 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)
3675 2: Set the texture to the maximum size (bad idea)
3676 3: WARN and return WINED3DERR_NOTAVAILABLE;
3677 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.
3679 WARN("(%p) Creating an oversized surface\n", This);
3680 This->Flags |= SFLAG_OVERSIZE;
3682 /* This will be initialized on the first blt */
3683 This->glRect.left = 0;
3684 This->glRect.top = 0;
3685 This->glRect.right = 0;
3686 This->glRect.bottom = 0;
3687 } else {
3688 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3689 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3690 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3691 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3693 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE) &&
3694 !((This->resource.format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE) && (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3696 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3697 This->pow2Width = This->currentDesc.Width;
3698 This->pow2Height = This->currentDesc.Height;
3699 This->Flags &= ~SFLAG_NONPOW2;
3702 /* No oversize, gl rect is the full texture size */
3703 This->Flags &= ~SFLAG_OVERSIZE;
3704 This->glRect.left = 0;
3705 This->glRect.top = 0;
3706 This->glRect.right = This->pow2Width;
3707 This->glRect.bottom = This->pow2Height;
3710 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3711 switch(wined3d_settings.offscreen_rendering_mode) {
3712 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3713 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3714 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3718 This->Flags |= SFLAG_INSYSMEM;
3720 return WINED3D_OK;
3723 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3724 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3725 IWineD3DBaseTexture *texture;
3727 TRACE("(%p)->(%s, %s)\n", iface,
3728 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3729 persistent ? "TRUE" : "FALSE");
3731 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3732 IWineD3DSwapChain *swapchain = NULL;
3734 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3735 TRACE("Surface %p is an onscreen surface\n", iface);
3737 IWineD3DSwapChain_Release(swapchain);
3738 } else {
3739 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3740 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3744 if(persistent) {
3745 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
3746 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3747 TRACE("Passing to container\n");
3748 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3749 IWineD3DBaseTexture_Release(texture);
3752 This->Flags &= ~SFLAG_LOCATIONS;
3753 This->Flags |= flag;
3754 } else {
3755 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
3756 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3757 TRACE("Passing to container\n");
3758 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3759 IWineD3DBaseTexture_Release(texture);
3762 This->Flags &= ~flag;
3766 struct coords {
3767 GLfloat x, y, z;
3770 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
3771 struct coords coords[4];
3772 RECT rect;
3773 IWineD3DSwapChain *swapchain = NULL;
3774 IWineD3DBaseTexture *texture = NULL;
3775 HRESULT hr;
3776 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3778 if(rect_in) {
3779 rect = *rect_in;
3780 } else {
3781 rect.left = 0;
3782 rect.top = 0;
3783 rect.right = This->currentDesc.Width;
3784 rect.bottom = This->currentDesc.Height;
3787 ActivateContext(device, device->render_targets[0], CTXUSAGE_BLIT);
3788 ENTER_GL();
3790 if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
3791 glEnable(GL_TEXTURE_RECTANGLE_ARB);
3792 checkGLcall("glEnable(GL_TEXTURE_RECTANGLE_ARB)");
3793 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName);
3794 checkGLcall("GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName)");
3795 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3796 checkGLcall("glTexParameteri");
3797 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3798 checkGLcall("glTexParameteri");
3800 coords[0].x = rect.left;
3801 coords[0].z = 0;
3803 coords[1].x = rect.left;
3804 coords[1].z = 0;
3806 coords[2].x = rect.right;
3807 coords[2].z = 0;
3809 coords[3].x = rect.right;
3810 coords[3].z = 0;
3812 coords[0].y = rect.top;
3813 coords[1].y = rect.bottom;
3814 coords[2].y = rect.bottom;
3815 coords[3].y = rect.top;
3816 } else if(This->glDescription.target == GL_TEXTURE_2D) {
3817 glEnable(GL_TEXTURE_2D);
3818 checkGLcall("glEnable(GL_TEXTURE_2D)");
3819 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
3820 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
3821 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3822 checkGLcall("glTexParameteri");
3823 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3824 checkGLcall("glTexParameteri");
3826 coords[0].x = (float)rect.left / This->pow2Width;
3827 coords[0].z = 0;
3829 coords[1].x = (float)rect.left / This->pow2Width;
3830 coords[1].z = 0;
3832 coords[2].x = (float)rect.right / This->pow2Width;
3833 coords[2].z = 0;
3835 coords[3].x = (float)rect.right / This->pow2Width;
3836 coords[3].z = 0;
3838 coords[0].y = (float)rect.top / This->pow2Height;
3839 coords[1].y = (float)rect.bottom / This->pow2Height;
3840 coords[2].y = (float)rect.bottom / This->pow2Height;
3841 coords[3].y = (float)rect.top / This->pow2Height;
3842 } else {
3843 /* Must be a cube map */
3844 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
3845 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
3846 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
3847 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
3848 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3849 checkGLcall("glTexParameteri");
3850 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3851 checkGLcall("glTexParameteri");
3853 switch(This->glDescription.target) {
3854 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3855 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
3856 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3857 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3858 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
3859 break;
3861 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3862 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3863 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
3864 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
3865 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3866 break;
3868 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3869 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
3870 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3871 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3872 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
3873 break;
3875 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3876 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3877 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3878 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3879 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3880 break;
3882 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3883 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3884 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3885 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
3886 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
3887 break;
3889 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
3890 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
3891 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
3892 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3893 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3894 break;
3896 default:
3897 ERR("Unexpected texture target\n");
3898 LEAVE_GL();
3899 return;
3903 glBegin(GL_QUADS);
3904 glTexCoord3fv(&coords[0].x);
3905 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
3907 glTexCoord3fv(&coords[1].x);
3908 glVertex2i(rect.left, device->render_offscreen ? rect.top : rect.bottom);
3910 glTexCoord3fv(&coords[2].x);
3911 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
3913 glTexCoord3fv(&coords[3].x);
3914 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
3915 glEnd();
3916 checkGLcall("glEnd");
3918 if(This->glDescription.target != GL_TEXTURE_2D) {
3919 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3920 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3921 } else {
3922 glDisable(GL_TEXTURE_2D);
3923 checkGLcall("glDisable(GL_TEXTURE_2D)");
3926 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain);
3927 if(hr == WINED3D_OK && swapchain) {
3928 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
3929 if(((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
3930 glFlush();
3932 IWineD3DSwapChain_Release(swapchain);
3933 } else {
3934 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
3935 * reset properly next draw
3937 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture);
3938 if(hr == WINED3D_OK && texture) {
3939 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
3940 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
3941 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
3942 IWineD3DBaseTexture_Release(texture);
3945 LEAVE_GL();
3948 /*****************************************************************************
3949 * IWineD3DSurface::LoadLocation
3951 * Copies the current surface data from wherever it is to the requested
3952 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
3953 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
3954 * multiple locations, the gl texture is preferred over the drawable, which is
3955 * preferred over system memory. The PBO counts as system memory. If rect is
3956 * not NULL, only the specified rectangle is copied (only supported for
3957 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
3958 * location is marked up to date after the copy.
3960 * Parameters:
3961 * flag: Surface location flag to be updated
3962 * rect: rectangle to be copied
3964 * Returns:
3965 * WINED3D_OK on success
3966 * WINED3DERR_DEVICELOST on an internal error
3968 *****************************************************************************/
3969 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
3970 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3971 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3972 IWineD3DSwapChain *swapchain = NULL;
3973 GLenum format, internal, type;
3974 CONVERT_TYPES convert;
3975 int bpp;
3976 int width, pitch, outpitch;
3977 BYTE *mem;
3979 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3980 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3981 TRACE("Surface %p is an onscreen surface\n", iface);
3983 IWineD3DSwapChain_Release(swapchain);
3984 } else {
3985 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
3986 * Prefer SFLAG_INTEXTURE. */
3987 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
3991 TRACE("(%p)->(%s, %p)\n", iface,
3992 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3993 rect);
3994 if(rect) {
3995 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
3998 if(This->Flags & flag) {
3999 TRACE("Location already up to date\n");
4000 return WINED3D_OK;
4003 if(!(This->Flags & SFLAG_LOCATIONS)) {
4004 ERR("Surface does not have any up to date location\n");
4005 This->Flags |= SFLAG_LOST;
4006 return WINED3DERR_DEVICELOST;
4009 if(flag == SFLAG_INSYSMEM) {
4010 surface_prepare_system_memory(This);
4012 /* Download the surface to system memory */
4013 if(This->Flags & SFLAG_INTEXTURE) {
4014 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4015 surface_bind_and_dirtify(This);
4017 surface_download_data(This);
4018 } else {
4019 read_from_framebuffer(This, rect,
4020 This->resource.allocatedMemory,
4021 IWineD3DSurface_GetPitch(iface));
4023 } else if(flag == SFLAG_INDRAWABLE) {
4024 if(This->Flags & SFLAG_INTEXTURE) {
4025 surface_blt_to_drawable(This, rect);
4026 } else {
4027 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4029 /* The width is in 'length' not in bytes */
4030 width = This->currentDesc.Width;
4031 pitch = IWineD3DSurface_GetPitch(iface);
4033 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4034 int height = This->currentDesc.Height;
4036 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4037 outpitch = width * bpp;
4038 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4040 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4041 if(!mem) {
4042 ERR("Out of memory %d, %d!\n", outpitch, height);
4043 return WINED3DERR_OUTOFVIDEOMEMORY;
4045 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4047 This->Flags |= SFLAG_CONVERTED;
4048 } else {
4049 This->Flags &= ~SFLAG_CONVERTED;
4050 mem = This->resource.allocatedMemory;
4053 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4055 /* Don't delete PBO memory */
4056 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4057 HeapFree(GetProcessHeap(), 0, mem);
4059 } else /* if(flag == SFLAG_INTEXTURE) */ {
4060 if (This->Flags & SFLAG_INDRAWABLE) {
4061 read_from_framebuffer_texture(This);
4062 } else { /* Upload from system memory */
4063 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4065 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4066 surface_bind_and_dirtify(This);
4067 ENTER_GL();
4069 /* The only place where LoadTexture() might get called when isInDraw=1
4070 * is ActivateContext where lastActiveRenderTarget is preloaded.
4072 if(iface == device->lastActiveRenderTarget && device->isInDraw)
4073 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
4075 /* Otherwise: System memory copy must be most up to date */
4077 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4078 This->Flags |= SFLAG_GLCKEY;
4079 This->glCKey = This->SrcBltCKey;
4081 else This->Flags &= ~SFLAG_GLCKEY;
4083 /* The width is in 'length' not in bytes */
4084 width = This->currentDesc.Width;
4085 pitch = IWineD3DSurface_GetPitch(iface);
4087 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4088 int height = This->currentDesc.Height;
4090 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4091 outpitch = width * bpp;
4092 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4094 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4095 if(!mem) {
4096 ERR("Out of memory %d, %d!\n", outpitch, height);
4097 return WINED3DERR_OUTOFVIDEOMEMORY;
4099 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4101 This->Flags |= SFLAG_CONVERTED;
4102 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
4103 d3dfmt_p8_upload_palette(iface, convert);
4104 This->Flags &= ~SFLAG_CONVERTED;
4105 mem = This->resource.allocatedMemory;
4106 } else {
4107 This->Flags &= ~SFLAG_CONVERTED;
4108 mem = This->resource.allocatedMemory;
4111 /* Make sure the correct pitch is used */
4112 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4114 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4115 TRACE("non power of two support\n");
4116 if(!(This->Flags & SFLAG_ALLOCATED)) {
4117 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4119 if (mem || (This->Flags & SFLAG_PBO)) {
4120 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4122 } else {
4123 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4124 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4126 if(!(This->Flags & SFLAG_ALLOCATED)) {
4127 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4129 if (mem || (This->Flags & SFLAG_PBO)) {
4130 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4134 /* Restore the default pitch */
4135 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4136 LEAVE_GL();
4138 /* Don't delete PBO memory */
4139 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4140 HeapFree(GetProcessHeap(), 0, mem);
4144 if(rect == NULL) {
4145 This->Flags |= flag;
4148 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
4149 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4150 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4151 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4154 return WINED3D_OK;
4157 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
4158 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4159 IWineD3DSwapChain *swapchain = NULL;
4161 /* Update the drawable size method */
4162 if(container) {
4163 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4165 if(swapchain) {
4166 This->get_drawable_size = get_drawable_size_swapchain;
4167 IWineD3DSwapChain_Release(swapchain);
4168 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4169 switch(wined3d_settings.offscreen_rendering_mode) {
4170 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4171 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4172 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4176 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4179 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4180 return SURFACE_OPENGL;
4183 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4185 /* IUnknown */
4186 IWineD3DBaseSurfaceImpl_QueryInterface,
4187 IWineD3DBaseSurfaceImpl_AddRef,
4188 IWineD3DSurfaceImpl_Release,
4189 /* IWineD3DResource */
4190 IWineD3DBaseSurfaceImpl_GetParent,
4191 IWineD3DBaseSurfaceImpl_GetDevice,
4192 IWineD3DBaseSurfaceImpl_SetPrivateData,
4193 IWineD3DBaseSurfaceImpl_GetPrivateData,
4194 IWineD3DBaseSurfaceImpl_FreePrivateData,
4195 IWineD3DBaseSurfaceImpl_SetPriority,
4196 IWineD3DBaseSurfaceImpl_GetPriority,
4197 IWineD3DSurfaceImpl_PreLoad,
4198 IWineD3DSurfaceImpl_UnLoad,
4199 IWineD3DBaseSurfaceImpl_GetType,
4200 /* IWineD3DSurface */
4201 IWineD3DBaseSurfaceImpl_GetContainer,
4202 IWineD3DBaseSurfaceImpl_GetDesc,
4203 IWineD3DSurfaceImpl_LockRect,
4204 IWineD3DSurfaceImpl_UnlockRect,
4205 IWineD3DSurfaceImpl_GetDC,
4206 IWineD3DSurfaceImpl_ReleaseDC,
4207 IWineD3DSurfaceImpl_Flip,
4208 IWineD3DSurfaceImpl_Blt,
4209 IWineD3DBaseSurfaceImpl_GetBltStatus,
4210 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4211 IWineD3DBaseSurfaceImpl_IsLost,
4212 IWineD3DBaseSurfaceImpl_Restore,
4213 IWineD3DSurfaceImpl_BltFast,
4214 IWineD3DBaseSurfaceImpl_GetPalette,
4215 IWineD3DBaseSurfaceImpl_SetPalette,
4216 IWineD3DSurfaceImpl_RealizePalette,
4217 IWineD3DBaseSurfaceImpl_SetColorKey,
4218 IWineD3DBaseSurfaceImpl_GetPitch,
4219 IWineD3DSurfaceImpl_SetMem,
4220 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4221 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4222 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4223 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4224 IWineD3DBaseSurfaceImpl_SetClipper,
4225 IWineD3DBaseSurfaceImpl_GetClipper,
4226 /* Internal use: */
4227 IWineD3DSurfaceImpl_AddDirtyRect,
4228 IWineD3DSurfaceImpl_LoadTexture,
4229 IWineD3DSurfaceImpl_BindTexture,
4230 IWineD3DSurfaceImpl_SaveSnapshot,
4231 IWineD3DSurfaceImpl_SetContainer,
4232 IWineD3DSurfaceImpl_SetGlTextureDesc,
4233 IWineD3DSurfaceImpl_GetGlDesc,
4234 IWineD3DSurfaceImpl_GetData,
4235 IWineD3DSurfaceImpl_SetFormat,
4236 IWineD3DSurfaceImpl_PrivateSetup,
4237 IWineD3DSurfaceImpl_ModifyLocation,
4238 IWineD3DSurfaceImpl_LoadLocation,
4239 IWineD3DSurfaceImpl_GetImplType