wined3d: Support index in alpha in BltOverride colorfill codepath.
[wine/hacks.git] / dlls / wined3d / surface.c
blob91bc419eb88ec75c28e2d171c346155426d06877
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
11 * Copyright 2007 Henri Verbeet
12 * Copyright 2006-2008 Roderick Colenbrander
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
36 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
37 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey);
38 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This);
40 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This) {
41 GLint active_texture;
43 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
44 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
45 * gl states. The current texture unit should always be a valid one.
47 * TODO: Track the current active texture per GL context instead of using glGet
49 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
50 ENTER_GL();
51 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
52 LEAVE_GL();
53 active_texture -= GL_TEXTURE0_ARB;
54 } else {
55 active_texture = 0;
57 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_texture));
58 IWineD3DSurface_BindTexture((IWineD3DSurface *)This);
61 /* This function checks if the primary render target uses the 8bit paletted format. */
62 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
64 if (device->render_targets && device->render_targets[0]) {
65 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
66 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
67 return TRUE;
69 return FALSE;
72 /* This call just downloads data, the caller is responsible for activating the
73 * right context and binding the correct texture. */
74 static void surface_download_data(IWineD3DSurfaceImpl *This) {
75 if (0 == This->glDescription.textureName) {
76 ERR("Surface does not have a texture, but SFLAG_INTEXTURE is set\n");
77 return;
80 /* Only support read back of converted P8 surfaces */
81 if(This->Flags & SFLAG_CONVERTED && (This->resource.format != WINED3DFMT_P8)) {
82 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(This->resource.format));
83 return;
86 ENTER_GL();
88 if (This->resource.format == WINED3DFMT_DXT1 ||
89 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
90 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
91 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
92 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
93 } else {
94 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
95 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
97 if(This->Flags & SFLAG_PBO) {
98 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
99 checkGLcall("glBindBufferARB");
100 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
101 checkGLcall("glGetCompressedTexImageARB()");
102 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
103 checkGLcall("glBindBufferARB");
104 } else {
105 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
106 checkGLcall("glGetCompressedTexImageARB()");
109 LEAVE_GL();
110 } else {
111 void *mem;
112 GLenum format = This->glDescription.glFormat;
113 GLenum type = This->glDescription.glType;
114 int src_pitch = 0;
115 int dst_pitch = 0;
117 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
118 if(This->resource.format == WINED3DFMT_P8) {
119 format = GL_ALPHA;
120 type = GL_UNSIGNED_BYTE;
123 if (This->Flags & SFLAG_NONPOW2) {
124 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
125 src_pitch = This->bytesPerPixel * This->pow2Width;
126 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
127 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
128 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
129 } else {
130 mem = This->resource.allocatedMemory;
133 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
134 format, type, mem);
136 if(This->Flags & SFLAG_PBO) {
137 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
138 checkGLcall("glBindBufferARB");
140 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
141 type, NULL);
142 checkGLcall("glGetTexImage()");
144 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
145 checkGLcall("glBindBufferARB");
146 } else {
147 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
148 type, mem);
149 checkGLcall("glGetTexImage()");
151 LEAVE_GL();
153 if (This->Flags & SFLAG_NONPOW2) {
154 LPBYTE src_data, dst_data;
155 int y;
157 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
158 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
159 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
161 * We're doing this...
163 * instead of boxing the texture :
164 * |<-texture width ->| -->pow2width| /\
165 * |111111111111111111| | |
166 * |222 Texture 222222| boxed empty | texture height
167 * |3333 Data 33333333| | |
168 * |444444444444444444| | \/
169 * ----------------------------------- |
170 * | boxed empty | boxed empty | pow2height
171 * | | | \/
172 * -----------------------------------
175 * we're repacking the data to the expected texture width
177 * |<-texture width ->| -->pow2width| /\
178 * |111111111111111111222222222222222| |
179 * |222333333333333333333444444444444| texture height
180 * |444444 | |
181 * | | \/
182 * | | |
183 * | empty | pow2height
184 * | | \/
185 * -----------------------------------
187 * == is the same as
189 * |<-texture width ->| /\
190 * |111111111111111111|
191 * |222222222222222222|texture height
192 * |333333333333333333|
193 * |444444444444444444| \/
194 * --------------------
196 * this also means that any references to allocatedMemory should work with the data as if were a
197 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
199 * internally the texture is still stored in a boxed format so any references to textureName will
200 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
202 * Performance should not be an issue, because applications normally do not lock the surfaces when
203 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
204 * and doesn't have to be re-read.
206 src_data = mem;
207 dst_data = This->resource.allocatedMemory;
208 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
209 for (y = 1 ; y < This->currentDesc.Height; y++) {
210 /* skip the first row */
211 src_data += src_pitch;
212 dst_data += dst_pitch;
213 memcpy(dst_data, src_data, dst_pitch);
216 HeapFree(GetProcessHeap(), 0, mem);
220 /* Surface has now been downloaded */
221 This->Flags |= SFLAG_INSYSMEM;
224 /* This call just uploads data, the caller is responsible for activating the
225 * right context and binding the correct texture. */
226 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
227 if (This->resource.format == WINED3DFMT_DXT1 ||
228 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
229 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
230 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
231 FIXME("Using DXT1/3/5 without advertized support\n");
232 } else {
233 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
234 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
235 * function uses glCompressedTexImage2D instead of the SubImage call
237 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
238 ENTER_GL();
240 if(This->Flags & SFLAG_PBO) {
241 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
242 checkGLcall("glBindBufferARB");
243 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
245 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
246 width, height, 0 /* border */, This->resource.size, NULL));
247 checkGLcall("glCompressedTexSubImage2D");
249 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
250 checkGLcall("glBindBufferARB");
251 } else {
252 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
253 width, height, 0 /* border */, This->resource.size, data));
254 checkGLcall("glCompressedTexSubImage2D");
256 LEAVE_GL();
258 } else {
259 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
260 ENTER_GL();
262 if(This->Flags & SFLAG_PBO) {
263 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
264 checkGLcall("glBindBufferARB");
265 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
267 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
268 checkGLcall("glTexSubImage2D");
270 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
271 checkGLcall("glBindBufferARB");
273 else {
274 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
275 checkGLcall("glTexSubImage2D");
278 LEAVE_GL();
282 /* This call just allocates the texture, the caller is responsible for
283 * activating the right context and binding the correct texture. */
284 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
285 BOOL enable_client_storage = FALSE;
286 BYTE *mem = NULL;
288 TRACE("(%p) : Creating surface (target %#x) level %d, d3d format %s, internal format %#x, width %d, height %d, gl format %#x, gl type=%#x\n", This,
289 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
291 if (This->resource.format == WINED3DFMT_DXT1 ||
292 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
293 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
294 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
295 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
297 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
298 * once, unfortunately
300 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
301 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
302 This->Flags |= SFLAG_CLIENT;
303 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
304 ENTER_GL();
305 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
306 width, height, 0 /* border */, This->resource.size, mem));
307 LEAVE_GL();
310 return;
313 ENTER_GL();
315 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
316 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
317 /* In some cases we want to disable client storage.
318 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
319 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
320 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
321 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
322 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
324 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
325 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
326 This->Flags &= ~SFLAG_CLIENT;
327 enable_client_storage = TRUE;
328 } else {
329 This->Flags |= SFLAG_CLIENT;
331 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
332 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
334 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
337 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
338 checkGLcall("glTexImage2D");
340 if(enable_client_storage) {
341 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
342 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
344 LEAVE_GL();
346 This->Flags |= SFLAG_ALLOCATED;
349 /* In D3D the depth stencil dimensions have to be greater than or equal to the
350 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
351 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
352 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
353 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
354 renderbuffer_entry_t *entry;
355 GLuint renderbuffer = 0;
356 unsigned int src_width, src_height;
358 src_width = This->pow2Width;
359 src_height = This->pow2Height;
361 /* A depth stencil smaller than the render target is not valid */
362 if (width > src_width || height > src_height) return;
364 /* Remove any renderbuffer set if the sizes match */
365 if (width == src_width && height == src_height) {
366 This->current_renderbuffer = NULL;
367 return;
370 /* Look if we've already got a renderbuffer of the correct dimensions */
371 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
372 if (entry->width == width && entry->height == height) {
373 renderbuffer = entry->id;
374 This->current_renderbuffer = entry;
375 break;
379 if (!renderbuffer) {
380 const GlPixelFormatDesc *glDesc;
381 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
383 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
384 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
385 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
387 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
388 entry->width = width;
389 entry->height = height;
390 entry->id = renderbuffer;
391 list_add_head(&This->renderbuffers, &entry->entry);
393 This->current_renderbuffer = entry;
396 checkGLcall("set_compatible_renderbuffer");
399 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
400 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
401 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
403 TRACE("(%p) : swapchain %p\n", This, swapchain);
405 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
406 TRACE("Returning GL_BACK\n");
407 return GL_BACK;
408 } else if (swapchain_impl->frontBuffer == iface) {
409 TRACE("Returning GL_FRONT\n");
410 return GL_FRONT;
413 FIXME("Higher back buffer, returning GL_BACK\n");
414 return GL_BACK;
417 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
418 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
419 ULONG ref = InterlockedDecrement(&This->resource.ref);
420 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
421 if (ref == 0) {
422 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
423 renderbuffer_entry_t *entry, *entry2;
424 TRACE("(%p) : cleaning up\n", This);
426 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
428 /* Need a context to destroy the texture. Use the currently active render target, but only if
429 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
430 * When destroying the primary rt, Uninit3D will activate a context before doing anything
432 if(device->render_targets && device->render_targets[0]) {
433 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
436 TRACE("Deleting texture %d\n", This->glDescription.textureName);
437 ENTER_GL();
438 glDeleteTextures(1, &This->glDescription.textureName);
439 LEAVE_GL();
442 if(This->Flags & SFLAG_PBO) {
443 /* Delete the PBO */
444 ENTER_GL();
445 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
446 LEAVE_GL();
449 if(This->Flags & SFLAG_DIBSECTION) {
450 /* Release the DC */
451 SelectObject(This->hDC, This->dib.holdbitmap);
452 DeleteDC(This->hDC);
453 /* Release the DIB section */
454 DeleteObject(This->dib.DIBsection);
455 This->dib.bitmap_data = NULL;
456 This->resource.allocatedMemory = NULL;
458 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
460 HeapFree(GetProcessHeap(), 0, This->palette9);
462 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
463 if(iface == device->ddraw_primary)
464 device->ddraw_primary = NULL;
466 ENTER_GL();
467 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
468 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
469 HeapFree(GetProcessHeap(), 0, entry);
471 LEAVE_GL();
473 TRACE("(%p) Released\n", This);
474 HeapFree(GetProcessHeap(), 0, This);
477 return ref;
480 /* ****************************************************
481 IWineD3DSurface IWineD3DResource parts follow
482 **************************************************** */
484 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
485 /* TODO: check for locks */
486 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
487 IWineD3DBaseTexture *baseTexture = NULL;
488 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
490 TRACE("(%p)Checking to see if the container is a base texture\n", This);
491 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
492 TRACE("Passing to container\n");
493 IWineD3DBaseTexture_PreLoad(baseTexture);
494 IWineD3DBaseTexture_Release(baseTexture);
495 } else {
496 TRACE("(%p) : About to load surface\n", This);
498 if(!device->isInDraw) {
499 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
502 ENTER_GL();
503 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
504 if (!This->glDescription.level) {
505 if (!This->glDescription.textureName) {
506 glGenTextures(1, &This->glDescription.textureName);
507 checkGLcall("glGenTextures");
508 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
510 glBindTexture(This->glDescription.target, This->glDescription.textureName);
511 checkGLcall("glBindTexture");
512 LEAVE_GL();
513 IWineD3DSurface_LoadTexture(iface, FALSE);
514 /* This is where we should be reducing the amount of GLMemoryUsed */
515 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
516 /* assume this is a coding error not a real error for now */
517 FIXME("Mipmap surface has a glTexture bound to it!\n");
518 LEAVE_GL();
520 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
521 /* Tell opengl to try and keep this texture in video ram (well mostly) */
522 GLclampf tmp;
523 tmp = 0.9f;
524 ENTER_GL();
525 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
526 LEAVE_GL();
529 return;
532 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
533 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
534 This->resource.allocatedMemory =
535 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
537 ENTER_GL();
538 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
539 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
540 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
541 checkGLcall("glGetBufferSubData");
542 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
543 checkGLcall("glDeleteBuffers");
544 LEAVE_GL();
546 This->pbo = 0;
547 This->Flags &= ~SFLAG_PBO;
550 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
551 IWineD3DBaseTexture *texture = NULL;
552 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
553 renderbuffer_entry_t *entry, *entry2;
554 TRACE("(%p)\n", iface);
556 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
557 /* Default pool resources are supposed to be destroyed before Reset is called.
558 * Implicit resources stay however. So this means we have an implicit render target
559 * or depth stencil. The content may be destroyed, but we still have to tear down
560 * opengl resources, so we cannot leave early.
562 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
563 } else {
564 /* Load the surface into system memory */
565 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
567 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
568 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
569 This->Flags &= ~SFLAG_ALLOCATED;
571 /* Destroy PBOs, but load them into real sysmem before */
572 if(This->Flags & SFLAG_PBO) {
573 surface_remove_pbo(This);
576 /* Destroy fbo render buffers. This is needed for implicit render targets, for
577 * all application-created targets the application has to release the surface
578 * before calling _Reset
580 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
581 ENTER_GL();
582 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
583 LEAVE_GL();
584 list_remove(&entry->entry);
585 HeapFree(GetProcessHeap(), 0, entry);
587 list_init(&This->renderbuffers);
588 This->current_renderbuffer = NULL;
590 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
591 * destroy it
593 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
594 if(!texture) {
595 ENTER_GL();
596 glDeleteTextures(1, &This->glDescription.textureName);
597 This->glDescription.textureName = 0;
598 LEAVE_GL();
599 } else {
600 IWineD3DBaseTexture_Release(texture);
602 return;
605 /* ******************************************************
606 IWineD3DSurface IWineD3DSurface parts follow
607 ****************************************************** */
609 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
610 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
611 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
612 if (This->glDescription.textureName == 0 && textureName != 0) {
613 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
614 IWineD3DSurface_AddDirtyRect(iface, NULL);
616 This->glDescription.textureName = textureName;
617 This->glDescription.target = target;
618 This->Flags &= ~SFLAG_ALLOCATED;
621 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
622 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
623 TRACE("(%p) : returning %p\n", This, &This->glDescription);
624 *glDescription = &This->glDescription;
627 /* TODO: think about moving this down to resource? */
628 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
629 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
630 /* 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 */
631 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
632 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
634 return (CONST void*)(This->resource.allocatedMemory);
637 /* Read the framebuffer back into the surface */
638 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
639 IWineD3DSwapChainImpl *swapchain;
640 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
641 BYTE *mem;
642 GLint fmt;
643 GLint type;
644 BYTE *row, *top, *bottom;
645 int i;
646 BOOL bpp;
647 RECT local_rect;
648 BOOL srcIsUpsideDown;
650 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
651 static BOOL warned = FALSE;
652 if(!warned) {
653 ERR("The application tries to lock the render target, but render target locking is disabled\n");
654 warned = TRUE;
656 return;
659 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
660 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
661 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
662 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
663 * context->last_was_blit set on the unlock.
665 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
666 ENTER_GL();
668 /* Select the correct read buffer, and give some debug output.
669 * There is no need to keep track of the current read buffer or reset it, every part of the code
670 * that reads sets the read buffer as desired.
672 if(!swapchain) {
673 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
674 * Read from the back buffer
676 TRACE("Locking offscreen render target\n");
677 glReadBuffer(myDevice->offscreenBuffer);
678 srcIsUpsideDown = TRUE;
679 } else {
680 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
681 TRACE("Locking %#x buffer\n", buffer);
682 glReadBuffer(buffer);
683 checkGLcall("glReadBuffer");
685 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
686 srcIsUpsideDown = FALSE;
689 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
690 if(!rect) {
691 local_rect.left = 0;
692 local_rect.top = 0;
693 local_rect.right = This->currentDesc.Width;
694 local_rect.bottom = This->currentDesc.Height;
695 } else {
696 local_rect = *rect;
698 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
700 switch(This->resource.format)
702 case WINED3DFMT_P8:
704 if(primary_render_target_is_p8(myDevice)) {
705 /* In case of P8 render targets the index is stored in the alpha component */
706 fmt = GL_ALPHA;
707 type = GL_UNSIGNED_BYTE;
708 mem = dest;
709 bpp = This->bytesPerPixel;
710 } else {
711 /* GL can't return palettized data, so read ARGB pixels into a
712 * separate block of memory and convert them into palettized format
713 * in software. Slow, but if the app means to use palettized render
714 * targets and locks it...
716 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
717 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
718 * for the color channels when palettizing the colors.
720 fmt = GL_RGB;
721 type = GL_UNSIGNED_BYTE;
722 pitch *= 3;
723 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
724 if(!mem) {
725 ERR("Out of memory\n");
726 LEAVE_GL();
727 return;
729 bpp = This->bytesPerPixel * 3;
732 break;
734 default:
735 mem = dest;
736 fmt = This->glDescription.glFormat;
737 type = This->glDescription.glType;
738 bpp = This->bytesPerPixel;
741 if(This->Flags & SFLAG_PBO) {
742 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
743 checkGLcall("glBindBufferARB");
746 glReadPixels(local_rect.left, local_rect.top,
747 local_rect.right - local_rect.left,
748 local_rect.bottom - local_rect.top,
749 fmt, type, mem);
750 vcheckGLcall("glReadPixels");
752 if(This->Flags & SFLAG_PBO) {
753 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
754 checkGLcall("glBindBufferARB");
756 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
757 * to get a pointer to it and perform the flipping in software. This is a lot
758 * faster than calling glReadPixels for each line. In case we want more speed
759 * we should rerender it flipped in a FBO and read the data back from the FBO. */
760 if(!srcIsUpsideDown) {
761 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
762 checkGLcall("glBindBufferARB");
764 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
765 checkGLcall("glMapBufferARB");
769 /* TODO: Merge this with the palettization loop below for P8 targets */
770 if(!srcIsUpsideDown) {
771 UINT len, off;
772 /* glReadPixels returns the image upside down, and there is no way to prevent this.
773 Flip the lines in software */
774 len = (local_rect.right - local_rect.left) * bpp;
775 off = local_rect.left * bpp;
777 row = HeapAlloc(GetProcessHeap(), 0, len);
778 if(!row) {
779 ERR("Out of memory\n");
780 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
781 LEAVE_GL();
782 return;
785 top = mem + pitch * local_rect.top;
786 bottom = mem + pitch * ( local_rect.bottom - local_rect.top - 1);
787 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
788 memcpy(row, top + off, len);
789 memcpy(top + off, bottom + off, len);
790 memcpy(bottom + off, row, len);
791 top += pitch;
792 bottom -= pitch;
794 HeapFree(GetProcessHeap(), 0, row);
796 /* Unmap the temp PBO buffer */
797 if(This->Flags & SFLAG_PBO) {
798 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
799 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
803 LEAVE_GL();
805 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
806 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
807 * the same color but we have no choice.
808 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
810 if((This->resource.format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice)) {
811 PALETTEENTRY *pal = NULL;
812 DWORD width = pitch / 3;
813 int x, y, c;
815 if(This->palette) {
816 pal = This->palette->palents;
817 } else {
818 ERR("Palette is missing, cannot perform inverse palette lookup\n");
819 HeapFree(GetProcessHeap(), 0, mem);
820 return ;
823 for(y = local_rect.top; y < local_rect.bottom; y++) {
824 for(x = local_rect.left; x < local_rect.right; x++) {
825 /* start lines pixels */
826 BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
827 BYTE *green = blue + 1;
828 BYTE *red = green + 1;
830 for(c = 0; c < 256; c++) {
831 if(*red == pal[c].peRed &&
832 *green == pal[c].peGreen &&
833 *blue == pal[c].peBlue)
835 *((BYTE *) dest + y * width + x) = c;
836 break;
841 HeapFree(GetProcessHeap(), 0, mem);
845 /* Read the framebuffer contents into a texture */
846 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This)
848 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
849 IWineD3DSwapChainImpl *swapchain;
850 int bpp;
851 GLenum format, internal, type;
852 CONVERT_TYPES convert;
853 BOOL srcIsUpsideDown;
854 GLint prevRead;
856 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
858 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
859 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
860 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
861 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
862 * context->last_was_blit set on the unlock.
864 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
865 surface_bind_and_dirtify(This);
866 ENTER_GL();
868 glGetIntegerv(GL_READ_BUFFER, &prevRead);
870 /* Select the correct read buffer, and give some debug output.
871 * There is no need to keep track of the current read buffer or reset it, every part of the code
872 * that reads sets the read buffer as desired.
874 if(!swapchain) {
875 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
876 * Read from the back buffer
878 TRACE("Locking offscreen render target\n");
879 glReadBuffer(device->offscreenBuffer);
880 srcIsUpsideDown = TRUE;
881 } else {
882 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
883 TRACE("Locking %#x buffer\n", buffer);
884 glReadBuffer(buffer);
885 checkGLcall("glReadBuffer");
887 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
888 srcIsUpsideDown = FALSE;
891 if(!(This->Flags & SFLAG_ALLOCATED)) {
892 surface_allocate_surface(This, internal, This->pow2Width,
893 This->pow2Height, format, type);
896 clear_unused_channels(This);
898 /* If !SrcIsUpsideDown we should flip the surface.
899 * This can be done using glCopyTexSubImage2D but this
900 * is VERY slow, so don't do that. We should prevent
901 * this code from getting called in such cases or perhaps
902 * we can use FBOs */
904 glCopyTexSubImage2D(This->glDescription.target,
905 This->glDescription.level,
906 0, 0, 0, 0,
907 This->currentDesc.Width,
908 This->currentDesc.Height);
909 checkGLcall("glCopyTexSubImage2D");
911 glReadBuffer(prevRead);
912 vcheckGLcall("glReadBuffer");
914 LEAVE_GL();
915 TRACE("Updated target %d\n", This->glDescription.target);
918 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
919 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
920 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
921 * changed
923 if(!(This->Flags & SFLAG_DYNLOCK)) {
924 This->lockCount++;
925 /* MAXLOCKCOUNT is defined in wined3d_private.h */
926 if(This->lockCount > MAXLOCKCOUNT) {
927 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
928 This->Flags |= SFLAG_DYNLOCK;
932 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
933 * Also don't create a PBO for systemmem surfaces.
935 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
936 GLenum error;
937 ENTER_GL();
939 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
940 error = glGetError();
941 if(This->pbo == 0 || error != GL_NO_ERROR) {
942 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
945 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
947 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
948 checkGLcall("glBindBufferARB");
950 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
951 checkGLcall("glBufferDataARB");
953 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
954 checkGLcall("glBindBufferARB");
956 /* We don't need the system memory anymore and we can't even use it for PBOs */
957 if(!(This->Flags & SFLAG_CLIENT)) {
958 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
959 This->resource.heapMemory = NULL;
961 This->resource.allocatedMemory = NULL;
962 This->Flags |= SFLAG_PBO;
963 LEAVE_GL();
964 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
965 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
966 * or a pbo to map
968 if(!This->resource.heapMemory) {
969 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
971 This->resource.allocatedMemory =
972 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
973 if(This->Flags & SFLAG_INSYSMEM) {
974 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
979 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
980 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
981 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
982 IWineD3DSwapChain *swapchain = NULL;
984 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
986 /* This is also done in the base class, but we have to verify this before loading any data from
987 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
988 * may interfere, and all other bad things may happen
990 if (This->Flags & SFLAG_LOCKED) {
991 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
992 return WINED3DERR_INVALIDCALL;
994 This->Flags |= SFLAG_LOCKED;
996 if (!(This->Flags & SFLAG_LOCKABLE))
998 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1001 if (Flags & WINED3DLOCK_DISCARD) {
1002 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1003 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1004 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1005 This->Flags |= SFLAG_INSYSMEM;
1006 goto lock_end;
1009 if (This->Flags & SFLAG_INSYSMEM) {
1010 TRACE("Local copy is up to date, not downloading data\n");
1011 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1012 goto lock_end;
1015 /* Now download the surface content from opengl
1016 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1017 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
1019 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1020 if(swapchain || iface == myDevice->render_targets[0]) {
1021 const RECT *pass_rect = pRect;
1023 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1024 * because most caller functions do not need that. So do that here
1026 if(pRect &&
1027 pRect->top == 0 &&
1028 pRect->left == 0 &&
1029 pRect->right == This->currentDesc.Width &&
1030 pRect->bottom == This->currentDesc.Height) {
1031 pass_rect = NULL;
1034 switch(wined3d_settings.rendertargetlock_mode) {
1035 case RTL_TEXDRAW:
1036 case RTL_TEXTEX:
1037 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1038 #if 0
1039 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1040 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1041 * This may be faster on some cards
1043 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1044 #endif
1045 /* drop through */
1047 case RTL_AUTO:
1048 case RTL_READDRAW:
1049 case RTL_READTEX:
1050 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
1051 break;
1053 case RTL_DISABLE:
1054 break;
1056 if(swapchain) IWineD3DSwapChain_Release(swapchain);
1058 } else if(iface == myDevice->stencilBufferTarget) {
1059 /** the depth stencil in openGL has a format of GL_FLOAT
1060 * which should be good for WINED3DFMT_D16_LOCKABLE
1061 * and WINED3DFMT_D16
1062 * it is unclear what format the stencil buffer is in except.
1063 * 'Each index is converted to fixed point...
1064 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1065 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1066 * glReadPixels(This->lockedRect.left,
1067 * This->lockedRect.bottom - j - 1,
1068 * This->lockedRect.right - This->lockedRect.left,
1069 * 1,
1070 * GL_DEPTH_COMPONENT,
1071 * type,
1072 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1074 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1075 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1076 * none of that is the case the problem is not in this function :-)
1077 ********************************************/
1078 FIXME("Depth stencil locking not supported yet\n");
1079 } else {
1080 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1081 TRACE("locking an ordinary surface\n");
1082 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1085 lock_end:
1086 if(This->Flags & SFLAG_PBO) {
1087 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1088 ENTER_GL();
1089 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1090 checkGLcall("glBindBufferARB");
1092 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1093 if(This->resource.allocatedMemory) {
1094 ERR("The surface already has PBO memory allocated!\n");
1097 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1098 checkGLcall("glMapBufferARB");
1100 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1101 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1102 checkGLcall("glBindBufferARB");
1104 LEAVE_GL();
1107 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1108 /* Don't dirtify */
1109 } else {
1110 IWineD3DBaseTexture *pBaseTexture;
1112 * Dirtify on lock
1113 * as seen in msdn docs
1115 IWineD3DSurface_AddDirtyRect(iface, pRect);
1117 /** Dirtify Container if needed */
1118 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
1119 TRACE("Making container dirty\n");
1120 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1121 IWineD3DBaseTexture_Release(pBaseTexture);
1122 } else {
1123 TRACE("Surface is standalone, no need to dirty the container\n");
1127 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1130 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1131 GLint prev_store;
1132 GLint prev_rasterpos[4];
1133 GLint skipBytes = 0;
1134 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1135 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1136 IWineD3DSwapChainImpl *swapchain;
1138 /* Activate the correct context for the render target */
1139 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1140 ENTER_GL();
1142 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
1143 if(!swapchain) {
1144 /* Primary offscreen render target */
1145 TRACE("Offscreen render target\n");
1146 glDrawBuffer(myDevice->offscreenBuffer);
1147 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1148 } else {
1149 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1150 TRACE("Unlocking %#x buffer\n", buffer);
1151 glDrawBuffer(buffer);
1152 checkGLcall("glDrawBuffer");
1154 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1157 glFlush();
1158 vcheckGLcall("glFlush");
1159 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1160 vcheckGLcall("glIntegerv");
1161 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1162 vcheckGLcall("glIntegerv");
1163 glPixelZoom(1.0, -1.0);
1164 vcheckGLcall("glPixelZoom");
1166 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1167 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1168 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1170 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1171 vcheckGLcall("glRasterPos2f");
1173 /* Some drivers(radeon dri, others?) don't like exceptions during
1174 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1175 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1176 * catch to put the dib section in InSync mode, which leads to a crash
1177 * and a blocked x server on my radeon card.
1179 * The following lines read the dib section so it is put in inSync mode
1180 * before glDrawPixels is called and the crash is prevented. There won't
1181 * be any interfering gdi accesses, because UnlockRect is called from
1182 * ReleaseDC, and the app won't use the dc any more afterwards.
1184 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1185 volatile BYTE read;
1186 read = This->resource.allocatedMemory[0];
1189 if(This->Flags & SFLAG_PBO) {
1190 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1191 checkGLcall("glBindBufferARB");
1194 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1195 if(This->Flags & SFLAG_LOCKED) {
1196 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1197 (This->lockedRect.bottom - This->lockedRect.top)-1,
1198 fmt, type,
1199 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1200 checkGLcall("glDrawPixels");
1201 } else {
1202 glDrawPixels(This->currentDesc.Width,
1203 This->currentDesc.Height,
1204 fmt, type, mem);
1205 checkGLcall("glDrawPixels");
1208 if(This->Flags & SFLAG_PBO) {
1209 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1210 checkGLcall("glBindBufferARB");
1213 glPixelZoom(1.0,1.0);
1214 vcheckGLcall("glPixelZoom");
1216 glRasterPos3iv(&prev_rasterpos[0]);
1217 vcheckGLcall("glRasterPos3iv");
1219 /* Reset to previous pack row length */
1220 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1221 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1223 if(!swapchain) {
1224 glDrawBuffer(myDevice->offscreenBuffer);
1225 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1226 } else if(swapchain->backBuffer) {
1227 glDrawBuffer(GL_BACK);
1228 checkGLcall("glDrawBuffer(GL_BACK)");
1229 } else {
1230 glDrawBuffer(GL_FRONT);
1231 checkGLcall("glDrawBuffer(GL_FRONT)");
1233 LEAVE_GL();
1235 return;
1238 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1239 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1240 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1241 IWineD3DSwapChainImpl *swapchain = NULL;
1242 BOOL fullsurface;
1244 if (!(This->Flags & SFLAG_LOCKED)) {
1245 WARN("trying to Unlock an unlocked surf@%p\n", This);
1246 return WINED3DERR_INVALIDCALL;
1249 if (This->Flags & SFLAG_PBO) {
1250 TRACE("Freeing PBO memory\n");
1251 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1252 ENTER_GL();
1253 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1254 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1255 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1256 checkGLcall("glUnmapBufferARB");
1257 LEAVE_GL();
1258 This->resource.allocatedMemory = NULL;
1261 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1263 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1264 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1265 goto unlock_end;
1268 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1269 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1270 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1272 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1273 static BOOL warned = FALSE;
1274 if(!warned) {
1275 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1276 warned = TRUE;
1278 goto unlock_end;
1281 if(This->dirtyRect.left == 0 &&
1282 This->dirtyRect.top == 0 &&
1283 This->dirtyRect.right == This->currentDesc.Width &&
1284 This->dirtyRect.bottom == This->currentDesc.Height) {
1285 fullsurface = TRUE;
1286 } else {
1287 /* TODO: Proper partial rectangle tracking */
1288 fullsurface = FALSE;
1289 This->Flags |= SFLAG_INSYSMEM;
1292 switch(wined3d_settings.rendertargetlock_mode) {
1293 case RTL_READTEX:
1294 case RTL_TEXTEX:
1295 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1296 ENTER_GL();
1297 if (This->glDescription.textureName == 0) {
1298 glGenTextures(1, &This->glDescription.textureName);
1299 checkGLcall("glGenTextures");
1301 glBindTexture(This->glDescription.target, This->glDescription.textureName);
1302 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
1303 LEAVE_GL();
1304 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1305 /* drop through */
1307 case RTL_AUTO:
1308 case RTL_READDRAW:
1309 case RTL_TEXDRAW:
1310 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1311 break;
1314 if(!fullsurface) {
1315 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1316 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1317 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1318 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1319 * not fully up to date because only a subrectangle was read in LockRect.
1321 This->Flags &= ~SFLAG_INSYSMEM;
1322 This->Flags |= SFLAG_INDRAWABLE;
1325 This->dirtyRect.left = This->currentDesc.Width;
1326 This->dirtyRect.top = This->currentDesc.Height;
1327 This->dirtyRect.right = 0;
1328 This->dirtyRect.bottom = 0;
1329 } else if(iface == myDevice->stencilBufferTarget) {
1330 FIXME("Depth Stencil buffer locking is not implemented\n");
1331 } else {
1332 /* The rest should be a normal texture */
1333 IWineD3DBaseTextureImpl *impl;
1334 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1335 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1336 * states need resetting
1338 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1339 if(impl->baseTexture.bindCount) {
1340 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1342 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1346 unlock_end:
1347 This->Flags &= ~SFLAG_LOCKED;
1348 memset(&This->lockedRect, 0, sizeof(RECT));
1349 return WINED3D_OK;
1352 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1353 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1354 WINED3DLOCKED_RECT lock;
1355 HRESULT hr;
1356 RGBQUAD col[256];
1358 TRACE("(%p)->(%p)\n",This,pHDC);
1360 if(This->Flags & SFLAG_USERPTR) {
1361 ERR("Not supported on surfaces with an application-provided surfaces\n");
1362 return WINEDDERR_NODC;
1365 /* Give more detailed info for ddraw */
1366 if (This->Flags & SFLAG_DCINUSE)
1367 return WINEDDERR_DCALREADYCREATED;
1369 /* Can't GetDC if the surface is locked */
1370 if (This->Flags & SFLAG_LOCKED)
1371 return WINED3DERR_INVALIDCALL;
1373 /* According to Direct3D9 docs, only these formats are supported */
1374 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1375 if (This->resource.format != WINED3DFMT_R5G6B5 &&
1376 This->resource.format != WINED3DFMT_X1R5G5B5 &&
1377 This->resource.format != WINED3DFMT_R8G8B8 &&
1378 This->resource.format != WINED3DFMT_X8R8G8B8) return WINED3DERR_INVALIDCALL;
1381 memset(&lock, 0, sizeof(lock)); /* To be sure */
1383 /* Create a DIB section if there isn't a hdc yet */
1384 if(!This->hDC) {
1385 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1386 if(This->Flags & SFLAG_CLIENT) {
1387 IWineD3DSurface_PreLoad(iface);
1390 /* Use the dib section from now on if we are not using a PBO */
1391 if(!(This->Flags & SFLAG_PBO))
1392 This->resource.allocatedMemory = This->dib.bitmap_data;
1395 /* Lock the surface */
1396 hr = IWineD3DSurface_LockRect(iface,
1397 &lock,
1398 NULL,
1401 if(This->Flags & SFLAG_PBO) {
1402 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1403 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1406 if(FAILED(hr)) {
1407 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1408 /* keep the dib section */
1409 return hr;
1412 if(This->resource.format == WINED3DFMT_P8 ||
1413 This->resource.format == WINED3DFMT_A8P8) {
1414 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1415 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1416 unsigned int n;
1417 PALETTEENTRY *pal = NULL;
1419 if(This->palette) {
1420 pal = This->palette->palents;
1421 } else {
1422 IWineD3DSurfaceImpl *dds_primary = (IWineD3DSurfaceImpl *)This->resource.wineD3DDevice->ddraw_primary;
1423 if (dds_primary && dds_primary->palette)
1424 pal = dds_primary->palette->palents;
1427 if (pal) {
1428 for (n=0; n<256; n++) {
1429 col[n].rgbRed = pal[n].peRed;
1430 col[n].rgbGreen = pal[n].peGreen;
1431 col[n].rgbBlue = pal[n].peBlue;
1432 col[n].rgbReserved = 0;
1434 SetDIBColorTable(This->hDC, 0, 256, col);
1438 *pHDC = This->hDC;
1439 TRACE("returning %p\n",*pHDC);
1440 This->Flags |= SFLAG_DCINUSE;
1442 return WINED3D_OK;
1445 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1446 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1448 TRACE("(%p)->(%p)\n",This,hDC);
1450 if (!(This->Flags & SFLAG_DCINUSE))
1451 return WINED3DERR_INVALIDCALL;
1453 if (This->hDC !=hDC) {
1454 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1455 return WINED3DERR_INVALIDCALL;
1458 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1459 /* Copy the contents of the DIB over to the PBO */
1460 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1463 /* we locked first, so unlock now */
1464 IWineD3DSurface_UnlockRect(iface);
1466 This->Flags &= ~SFLAG_DCINUSE;
1468 return WINED3D_OK;
1471 /* ******************************************************
1472 IWineD3DSurface Internal (No mapping to directx api) parts follow
1473 ****************************************************** */
1475 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) {
1476 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1477 const GlPixelFormatDesc *glDesc;
1478 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1479 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1481 /* Default values: From the surface */
1482 *format = glDesc->glFormat;
1483 *type = glDesc->glType;
1484 *convert = NO_CONVERSION;
1485 *target_bpp = This->bytesPerPixel;
1487 if(srgb_mode) {
1488 *internal = glDesc->glGammaInternal;
1489 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1490 *internal = glDesc->rtInternal;
1491 } else {
1492 *internal = glDesc->glInternal;
1495 /* Ok, now look if we have to do any conversion */
1496 switch(This->resource.format) {
1497 case WINED3DFMT_P8:
1498 /* ****************
1499 Paletted Texture
1500 **************** */
1502 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1503 * of the two is available make sure texturing is requested as neither of the two works in
1504 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1505 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1506 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1507 * conflicts with this.
1509 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) && primary_render_target_is_p8(device))) || colorkey_active || !use_texturing ) {
1510 *format = GL_RGBA;
1511 *internal = GL_RGBA;
1512 *type = GL_UNSIGNED_BYTE;
1513 *target_bpp = 4;
1514 if(colorkey_active) {
1515 *convert = CONVERT_PALETTED_CK;
1516 } else {
1517 *convert = CONVERT_PALETTED;
1520 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1521 *format = GL_ALPHA;
1522 *internal = GL_RGBA;
1523 *type = GL_UNSIGNED_BYTE;
1524 *target_bpp = 1;
1527 break;
1529 case WINED3DFMT_R3G3B2:
1530 /* **********************
1531 GL_UNSIGNED_BYTE_3_3_2
1532 ********************** */
1533 if (colorkey_active) {
1534 /* This texture format will never be used.. So do not care about color keying
1535 up until the point in time it will be needed :-) */
1536 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1538 break;
1540 case WINED3DFMT_R5G6B5:
1541 if (colorkey_active) {
1542 *convert = CONVERT_CK_565;
1543 *format = GL_RGBA;
1544 *internal = GL_RGBA;
1545 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1547 break;
1549 case WINED3DFMT_X1R5G5B5:
1550 if (colorkey_active) {
1551 *convert = CONVERT_CK_5551;
1552 *format = GL_BGRA;
1553 *internal = GL_RGBA;
1554 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1556 break;
1558 case WINED3DFMT_R8G8B8:
1559 if (colorkey_active) {
1560 *convert = CONVERT_CK_RGB24;
1561 *format = GL_RGBA;
1562 *internal = GL_RGBA;
1563 *type = GL_UNSIGNED_INT_8_8_8_8;
1564 *target_bpp = 4;
1566 break;
1568 case WINED3DFMT_X8R8G8B8:
1569 if (colorkey_active) {
1570 *convert = CONVERT_RGB32_888;
1571 *format = GL_RGBA;
1572 *internal = GL_RGBA;
1573 *type = GL_UNSIGNED_INT_8_8_8_8;
1575 break;
1577 case WINED3DFMT_V8U8:
1578 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1579 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1580 *format = GL_DUDV_ATI;
1581 *internal = GL_DU8DV8_ATI;
1582 *type = GL_BYTE;
1583 /* No conversion - Just change the gl type */
1584 break;
1586 *convert = CONVERT_V8U8;
1587 *format = GL_BGR;
1588 *internal = GL_RGB8;
1589 *type = GL_UNSIGNED_BYTE;
1590 *target_bpp = 3;
1591 break;
1593 case WINED3DFMT_L6V5U5:
1594 *convert = CONVERT_L6V5U5;
1595 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1596 *target_bpp = 3;
1597 /* Use format and types from table */
1598 } else {
1599 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1600 *target_bpp = 2;
1601 *format = GL_RGB;
1602 *internal = GL_RGB5;
1603 *type = GL_UNSIGNED_SHORT_5_6_5;
1605 break;
1607 case WINED3DFMT_X8L8V8U8:
1608 *convert = CONVERT_X8L8V8U8;
1609 *target_bpp = 4;
1610 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1611 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1612 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1613 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1614 * the needed type and format parameter, so the internal format contains a
1615 * 4th component, which is returned as alpha
1617 } else {
1618 /* Not supported by GL_ATI_envmap_bumpmap */
1619 *format = GL_BGRA;
1620 *internal = GL_RGB8;
1621 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1623 break;
1625 case WINED3DFMT_Q8W8V8U8:
1626 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1627 *convert = CONVERT_Q8W8V8U8;
1628 *format = GL_BGRA;
1629 *internal = GL_RGBA8;
1630 *type = GL_UNSIGNED_BYTE;
1631 *target_bpp = 4;
1632 /* Not supported by GL_ATI_envmap_bumpmap */
1633 break;
1635 case WINED3DFMT_V16U16:
1636 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1637 *convert = CONVERT_V16U16;
1638 *format = GL_BGR;
1639 *internal = GL_RGB16_EXT;
1640 *type = GL_UNSIGNED_SHORT;
1641 *target_bpp = 6;
1642 /* What should I do here about GL_ATI_envmap_bumpmap?
1643 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1645 break;
1647 case WINED3DFMT_A4L4:
1648 /* A4L4 exists as an internal gl format, but for some reason there is not
1649 * format+type combination to load it. Thus convert it to A8L8, then load it
1650 * with A4L4 internal, but A8L8 format+type
1652 *convert = CONVERT_A4L4;
1653 *format = GL_LUMINANCE_ALPHA;
1654 *internal = GL_LUMINANCE4_ALPHA4;
1655 *type = GL_UNSIGNED_BYTE;
1656 *target_bpp = 2;
1657 break;
1659 case WINED3DFMT_R32F:
1660 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1661 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1662 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1663 * 1.0 instead.
1665 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1667 *convert = CONVERT_R32F;
1668 *format = GL_RGB;
1669 *internal = GL_RGB32F_ARB;
1670 *type = GL_FLOAT;
1671 *target_bpp = 12;
1672 break;
1674 case WINED3DFMT_R16F:
1675 /* Similar to R32F */
1676 *convert = CONVERT_R16F;
1677 *format = GL_RGB;
1678 *internal = GL_RGB16F_ARB;
1679 *type = GL_HALF_FLOAT_ARB;
1680 *target_bpp = 6;
1681 break;
1683 case WINED3DFMT_G16R16:
1684 *convert = CONVERT_G16R16;
1685 *format = GL_RGB;
1686 *internal = GL_RGB16_EXT;
1687 *type = GL_UNSIGNED_SHORT;
1688 *target_bpp = 6;
1689 break;
1691 default:
1692 break;
1695 return WINED3D_OK;
1698 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1699 BYTE *source, *dest;
1700 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1702 switch (convert) {
1703 case NO_CONVERSION:
1705 memcpy(dst, src, pitch * height);
1706 break;
1708 case CONVERT_PALETTED:
1709 case CONVERT_PALETTED_CK:
1711 IWineD3DPaletteImpl* pal = This->palette;
1712 BYTE table[256][4];
1713 unsigned int x, y;
1715 if( pal == NULL) {
1716 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1719 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1721 for (y = 0; y < height; y++)
1723 source = src + pitch * y;
1724 dest = dst + outpitch * y;
1725 /* This is an 1 bpp format, using the width here is fine */
1726 for (x = 0; x < width; x++) {
1727 BYTE color = *source++;
1728 *dest++ = table[color][0];
1729 *dest++ = table[color][1];
1730 *dest++ = table[color][2];
1731 *dest++ = table[color][3];
1735 break;
1737 case CONVERT_CK_565:
1739 /* Converting the 565 format in 5551 packed to emulate color-keying.
1741 Note : in all these conversion, it would be best to average the averaging
1742 pixels to get the color of the pixel that will be color-keyed to
1743 prevent 'color bleeding'. This will be done later on if ever it is
1744 too visible.
1746 Note2: Nvidia documents say that their driver does not support alpha + color keying
1747 on the same surface and disables color keying in such a case
1749 unsigned int x, y;
1750 WORD *Source;
1751 WORD *Dest;
1753 TRACE("Color keyed 565\n");
1755 for (y = 0; y < height; y++) {
1756 Source = (WORD *) (src + y * pitch);
1757 Dest = (WORD *) (dst + y * outpitch);
1758 for (x = 0; x < width; x++ ) {
1759 WORD color = *Source++;
1760 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1761 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1762 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1763 *Dest |= 0x0001;
1765 Dest++;
1769 break;
1771 case CONVERT_CK_5551:
1773 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1774 unsigned int x, y;
1775 WORD *Source;
1776 WORD *Dest;
1777 TRACE("Color keyed 5551\n");
1778 for (y = 0; y < height; y++) {
1779 Source = (WORD *) (src + y * pitch);
1780 Dest = (WORD *) (dst + y * outpitch);
1781 for (x = 0; x < width; x++ ) {
1782 WORD color = *Source++;
1783 *Dest = color;
1784 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1785 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1786 *Dest |= (1 << 15);
1788 else {
1789 *Dest &= ~(1 << 15);
1791 Dest++;
1795 break;
1797 case CONVERT_V8U8:
1799 unsigned int x, y;
1800 short *Source;
1801 unsigned char *Dest;
1802 for(y = 0; y < height; y++) {
1803 Source = (short *) (src + y * pitch);
1804 Dest = dst + y * outpitch;
1805 for (x = 0; x < width; x++ ) {
1806 long color = (*Source++);
1807 /* B */ Dest[0] = 0xff;
1808 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1809 /* R */ Dest[2] = (color) + 128; /* U */
1810 Dest += 3;
1813 break;
1816 case CONVERT_V16U16:
1818 unsigned int x, y;
1819 DWORD *Source;
1820 unsigned short *Dest;
1821 for(y = 0; y < height; y++) {
1822 Source = (DWORD *) (src + y * pitch);
1823 Dest = (unsigned short *) (dst + y * outpitch);
1824 for (x = 0; x < width; x++ ) {
1825 DWORD color = (*Source++);
1826 /* B */ Dest[0] = 0xffff;
1827 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1828 /* R */ Dest[2] = (color ) + 32768; /* U */
1829 Dest += 3;
1832 break;
1835 case CONVERT_Q8W8V8U8:
1837 unsigned int x, y;
1838 DWORD *Source;
1839 unsigned char *Dest;
1840 for(y = 0; y < height; y++) {
1841 Source = (DWORD *) (src + y * pitch);
1842 Dest = dst + y * outpitch;
1843 for (x = 0; x < width; x++ ) {
1844 long color = (*Source++);
1845 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1846 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1847 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1848 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1849 Dest += 4;
1852 break;
1855 case CONVERT_L6V5U5:
1857 unsigned int x, y;
1858 WORD *Source;
1859 unsigned char *Dest;
1861 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1862 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1863 * fixed function and shaders without further conversion once the surface is
1864 * loaded
1866 for(y = 0; y < height; y++) {
1867 Source = (WORD *) (src + y * pitch);
1868 Dest = dst + y * outpitch;
1869 for (x = 0; x < width; x++ ) {
1870 short color = (*Source++);
1871 unsigned char l = ((color >> 10) & 0xfc);
1872 char v = ((color >> 5) & 0x3e);
1873 char u = ((color ) & 0x1f);
1875 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1876 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1877 * shift. GL reads a signed value and converts it into an unsigned value.
1879 /* M */ Dest[2] = l << 1;
1881 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1882 * from 5 bit values to 8 bit values.
1884 /* V */ Dest[1] = v << 3;
1885 /* U */ Dest[0] = u << 3;
1886 Dest += 3;
1889 } else {
1890 for(y = 0; y < height; y++) {
1891 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1892 Source = (WORD *) (src + y * pitch);
1893 for (x = 0; x < width; x++ ) {
1894 short color = (*Source++);
1895 unsigned char l = ((color >> 10) & 0xfc);
1896 short v = ((color >> 5) & 0x3e);
1897 short u = ((color ) & 0x1f);
1898 short v_conv = v + 16;
1899 short u_conv = u + 16;
1901 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1902 Dest_s += 1;
1906 break;
1909 case CONVERT_X8L8V8U8:
1911 unsigned int x, y;
1912 DWORD *Source;
1913 unsigned char *Dest;
1915 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1916 /* This implementation works with the fixed function pipeline and shaders
1917 * without further modification after converting the surface.
1919 for(y = 0; y < height; y++) {
1920 Source = (DWORD *) (src + y * pitch);
1921 Dest = dst + y * outpitch;
1922 for (x = 0; x < width; x++ ) {
1923 long color = (*Source++);
1924 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1925 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1926 /* U */ Dest[0] = (color & 0xff); /* U */
1927 /* I */ Dest[3] = 255; /* X */
1928 Dest += 4;
1931 } else {
1932 /* Doesn't work correctly with the fixed function pipeline, but can work in
1933 * shaders if the shader is adjusted. (There's no use for this format in gl's
1934 * standard fixed function pipeline anyway).
1936 for(y = 0; y < height; y++) {
1937 Source = (DWORD *) (src + y * pitch);
1938 Dest = dst + y * outpitch;
1939 for (x = 0; x < width; x++ ) {
1940 long color = (*Source++);
1941 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1942 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1943 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1944 Dest += 4;
1948 break;
1951 case CONVERT_A4L4:
1953 unsigned int x, y;
1954 unsigned char *Source;
1955 unsigned char *Dest;
1956 for(y = 0; y < height; y++) {
1957 Source = src + y * pitch;
1958 Dest = dst + y * outpitch;
1959 for (x = 0; x < width; x++ ) {
1960 unsigned char color = (*Source++);
1961 /* A */ Dest[1] = (color & 0xf0) << 0;
1962 /* L */ Dest[0] = (color & 0x0f) << 4;
1963 Dest += 2;
1966 break;
1969 case CONVERT_R32F:
1971 unsigned int x, y;
1972 float *Source;
1973 float *Dest;
1974 for(y = 0; y < height; y++) {
1975 Source = (float *) (src + y * pitch);
1976 Dest = (float *) (dst + y * outpitch);
1977 for (x = 0; x < width; x++ ) {
1978 float color = (*Source++);
1979 Dest[0] = color;
1980 Dest[1] = 1.0;
1981 Dest[2] = 1.0;
1982 Dest += 3;
1985 break;
1988 case CONVERT_R16F:
1990 unsigned int x, y;
1991 WORD *Source;
1992 WORD *Dest;
1993 WORD one = 0x3c00;
1994 for(y = 0; y < height; y++) {
1995 Source = (WORD *) (src + y * pitch);
1996 Dest = (WORD *) (dst + y * outpitch);
1997 for (x = 0; x < width; x++ ) {
1998 WORD color = (*Source++);
1999 Dest[0] = color;
2000 Dest[1] = one;
2001 Dest[2] = one;
2002 Dest += 3;
2005 break;
2008 case CONVERT_G16R16:
2010 unsigned int x, y;
2011 WORD *Source;
2012 WORD *Dest;
2014 for(y = 0; y < height; y++) {
2015 Source = (WORD *) (src + y * pitch);
2016 Dest = (WORD *) (dst + y * outpitch);
2017 for (x = 0; x < width; x++ ) {
2018 WORD green = (*Source++);
2019 WORD red = (*Source++);
2020 Dest[0] = green;
2021 Dest[1] = red;
2022 Dest[2] = 0xffff;
2023 Dest += 3;
2026 break;
2029 default:
2030 ERR("Unsupported conversation type %d\n", convert);
2032 return WINED3D_OK;
2035 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2036 IWineD3DPaletteImpl* pal = This->palette;
2037 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2038 BOOL index_in_alpha = FALSE;
2039 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2040 int i;
2042 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2043 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2044 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2045 * duplicate entries. Store the color key in the unused alpha component to speed the
2046 * download up and to make conversion unneeded. */
2047 index_in_alpha = primary_render_target_is_p8(device);
2049 if (pal == NULL) {
2050 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2051 if(dxVersion <= 7) {
2052 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2053 return;
2056 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2057 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2058 capability flag is present (wine does advertise this capability) */
2059 for (i = 0; i < 256; i++) {
2060 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2061 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2062 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2063 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2065 } else {
2066 TRACE("Using surface palette %p\n", pal);
2067 /* Get the surface's palette */
2068 for (i = 0; i < 256; i++) {
2069 table[i][0] = pal->palents[i].peRed;
2070 table[i][1] = pal->palents[i].peGreen;
2071 table[i][2] = pal->palents[i].peBlue;
2073 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2074 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2075 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2076 of pixels that should be masked away is set to 0. */
2077 if(index_in_alpha) {
2078 table[i][3] = i;
2079 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2080 table[i][3] = 0x00;
2081 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2082 table[i][3] = pal->palents[i].peFlags;
2083 } else {
2084 table[i][3] = 0xFF;
2090 const char *fragment_palette_conversion =
2091 "!!ARBfp1.0\n"
2092 "TEMP index;\n"
2093 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
2094 "TEX index, fragment.texcoord[0], texture[0], 2D;\n" /* The alpha-component contains the palette index */
2095 "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 */
2096 "TEX result.color, index.a, texture[1], 1D;\n" /* use the alpha-component as a index in the palette to get the final color */
2097 "END";
2099 /* This function is used in case of 8bit paletted textures to upload the palette.
2100 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2101 extensions like ATI_fragment_shaders is possible.
2103 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2104 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2105 BYTE table[256][4];
2106 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2108 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2110 /* Try to use the paletted texture extension */
2111 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2113 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2114 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2116 else
2118 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2119 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2120 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2122 /* Create the fragment program if we don't have it */
2123 if(!device->paletteConversionShader)
2125 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2126 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2127 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2128 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2129 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2132 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2133 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2135 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2136 glEnable(GL_TEXTURE_1D);
2137 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2139 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2140 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2141 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2142 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2144 /* Switch back to unit 0 in which the 2D texture will be stored. */
2145 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2147 /* Rebind the texture because it isn't bound anymore */
2148 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2152 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2153 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2155 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2156 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2157 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2159 return FALSE;
2162 if(This->palette9) {
2163 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2164 return FALSE;
2166 } else {
2167 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2169 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2170 return TRUE;
2173 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2174 GLboolean oldwrite[4];
2176 /* Some formats have only some color channels, and the others are 1.0.
2177 * since our rendering renders to all channels, and those pixel formats
2178 * are emulated by using a full texture with the other channels set to 1.0
2179 * manually, clear the unused channels.
2181 * This could be done with hacking colorwriteenable to mask the colors,
2182 * but before drawing the buffer would have to be cleared too, so there's
2183 * no gain in that
2185 switch(This->resource.format) {
2186 case WINED3DFMT_R16F:
2187 case WINED3DFMT_R32F:
2188 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2189 /* Do not activate a context, the correct drawable is active already
2190 * though just the read buffer is set, make sure to have the correct draw
2191 * buffer too
2193 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2194 glDisable(GL_SCISSOR_TEST);
2195 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2196 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2197 glClearColor(0.0, 1.0, 1.0, 1.0);
2198 glClear(GL_COLOR_BUFFER_BIT);
2199 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2200 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2201 checkGLcall("Unused channel clear\n");
2202 break;
2204 default: break;
2208 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2209 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2211 if (!(This->Flags & SFLAG_INTEXTURE)) {
2212 TRACE("Reloading because surface is dirty\n");
2213 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2214 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2215 /* Reload: vice versa OR */
2216 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2217 /* Also reload: Color key is active AND the color key has changed */
2218 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2219 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2220 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2221 TRACE("Reloading because of color keying\n");
2222 /* To perform the color key conversion we need a sysmem copy of
2223 * the surface. Make sure we have it
2226 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2227 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2228 /* TODO: This is not necessarily needed with hw palettized texture support */
2229 This->Flags &= ~SFLAG_INTEXTURE;
2230 } else if(palette9_changed(This)) {
2231 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2232 /* TODO: This is not necessarily needed with hw palettized texture support */
2233 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2235 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2236 This->Flags &= ~SFLAG_INTEXTURE;
2237 } else {
2238 TRACE("surface is already in texture\n");
2239 return WINED3D_OK;
2242 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2243 * These resources are not bound by device size or format restrictions. Because of this,
2244 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2245 * However, these resources can always be created, locked, and copied.
2247 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2249 FIXME("(%p) Operation not supported for scratch textures\n",This);
2250 return WINED3DERR_INVALIDCALL;
2253 This->srgb = srgb_mode;
2254 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2256 #if 0
2258 static unsigned int gen = 0;
2259 char buffer[4096];
2260 ++gen;
2261 if ((gen % 10) == 0) {
2262 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2263 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2266 * debugging crash code
2267 if (gen == 250) {
2268 void** test = NULL;
2269 *test = 0;
2273 #endif
2275 if (!(This->Flags & SFLAG_DONOTFREE)) {
2276 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2277 This->resource.allocatedMemory = NULL;
2278 This->resource.heapMemory = NULL;
2279 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2282 return WINED3D_OK;
2285 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2286 /* TODO: check for locks */
2287 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2288 IWineD3DBaseTexture *baseTexture = NULL;
2289 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2291 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2292 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2293 TRACE("Passing to container\n");
2294 IWineD3DBaseTexture_BindTexture(baseTexture);
2295 IWineD3DBaseTexture_Release(baseTexture);
2296 } else {
2297 TRACE("(%p) : Binding surface\n", This);
2299 if(!device->isInDraw) {
2300 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2302 ENTER_GL();
2303 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2304 LEAVE_GL();
2306 return;
2309 #include <errno.h>
2310 #include <stdio.h>
2311 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2312 FILE* f = NULL;
2313 UINT i, y;
2314 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2315 char *allocatedMemory;
2316 char *textureRow;
2317 IWineD3DSwapChain *swapChain = NULL;
2318 int width, height;
2319 GLuint tmpTexture = 0;
2320 DWORD color;
2321 /*FIXME:
2322 Textures may not be stored in ->allocatedgMemory and a GlTexture
2323 so we should lock the surface before saving a snapshot, or at least check that
2325 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2326 by calling GetTexImage and in compressed form by calling
2327 GetCompressedTexImageARB. Queried compressed images can be saved and
2328 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2329 texture images do not need to be processed by the GL and should
2330 significantly improve texture loading performance relative to uncompressed
2331 images. */
2333 /* Setup the width and height to be the internal texture width and height. */
2334 width = This->pow2Width;
2335 height = This->pow2Height;
2336 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2337 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2339 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2340 /* if were not a real texture then read the back buffer into a real texture */
2341 /* we don't want to interfere with the back buffer so read the data into a temporary
2342 * texture and then save the data out of the temporary texture
2344 GLint prevRead;
2345 ENTER_GL();
2346 TRACE("(%p) Reading render target into texture\n", This);
2347 glEnable(GL_TEXTURE_2D);
2349 glGenTextures(1, &tmpTexture);
2350 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2352 glTexImage2D(GL_TEXTURE_2D,
2354 GL_RGBA,
2355 width,
2356 height,
2357 0/*border*/,
2358 GL_RGBA,
2359 GL_UNSIGNED_INT_8_8_8_8_REV,
2360 NULL);
2362 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2363 vcheckGLcall("glGetIntegerv");
2364 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2365 vcheckGLcall("glReadBuffer");
2366 glCopyTexImage2D(GL_TEXTURE_2D,
2368 GL_RGBA,
2371 width,
2372 height,
2375 checkGLcall("glCopyTexImage2D");
2376 glReadBuffer(prevRead);
2377 LEAVE_GL();
2379 } else { /* bind the real texture, and make sure it up to date */
2380 IWineD3DSurface_PreLoad(iface);
2382 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2383 ENTER_GL();
2384 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2385 glGetTexImage(GL_TEXTURE_2D,
2386 This->glDescription.level,
2387 GL_RGBA,
2388 GL_UNSIGNED_INT_8_8_8_8_REV,
2389 allocatedMemory);
2390 checkGLcall("glTexImage2D");
2391 if (tmpTexture) {
2392 glBindTexture(GL_TEXTURE_2D, 0);
2393 glDeleteTextures(1, &tmpTexture);
2395 LEAVE_GL();
2397 f = fopen(filename, "w+");
2398 if (NULL == f) {
2399 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2400 return WINED3DERR_INVALIDCALL;
2402 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2403 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2404 /* TGA header */
2405 fputc(0,f);
2406 fputc(0,f);
2407 fputc(2,f);
2408 fputc(0,f);
2409 fputc(0,f);
2410 fputc(0,f);
2411 fputc(0,f);
2412 fputc(0,f);
2413 fputc(0,f);
2414 fputc(0,f);
2415 fputc(0,f);
2416 fputc(0,f);
2417 /* short width*/
2418 fwrite(&width,2,1,f);
2419 /* short height */
2420 fwrite(&height,2,1,f);
2421 /* format rgba */
2422 fputc(0x20,f);
2423 fputc(0x28,f);
2424 /* raw data */
2425 /* 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 */
2426 if(swapChain)
2427 textureRow = allocatedMemory + (width * (height - 1) *4);
2428 else
2429 textureRow = allocatedMemory;
2430 for (y = 0 ; y < height; y++) {
2431 for (i = 0; i < width; i++) {
2432 color = *((DWORD*)textureRow);
2433 fputc((color >> 16) & 0xFF, f); /* B */
2434 fputc((color >> 8) & 0xFF, f); /* G */
2435 fputc((color >> 0) & 0xFF, f); /* R */
2436 fputc((color >> 24) & 0xFF, f); /* A */
2437 textureRow += 4;
2439 /* take two rows of the pointer to the texture memory */
2440 if(swapChain)
2441 (textureRow-= width << 3);
2444 TRACE("Closing file\n");
2445 fclose(f);
2447 if(swapChain) {
2448 IWineD3DSwapChain_Release(swapChain);
2450 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2451 return WINED3D_OK;
2455 * Slightly inefficient way to handle multiple dirty rects but it works :)
2457 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2458 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2459 IWineD3DBaseTexture *baseTexture = NULL;
2461 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2462 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2464 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2465 if (NULL != pDirtyRect) {
2466 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2467 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2468 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2469 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2470 } else {
2471 This->dirtyRect.left = 0;
2472 This->dirtyRect.top = 0;
2473 This->dirtyRect.right = This->currentDesc.Width;
2474 This->dirtyRect.bottom = This->currentDesc.Height;
2476 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2477 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2478 /* if the container is a basetexture then mark it dirty. */
2479 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2480 TRACE("Passing to container\n");
2481 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2482 IWineD3DBaseTexture_Release(baseTexture);
2484 return WINED3D_OK;
2487 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2488 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2489 HRESULT hr;
2490 const GlPixelFormatDesc *glDesc;
2491 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2493 TRACE("(%p) : Calling base function first\n", This);
2494 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2495 if(SUCCEEDED(hr)) {
2496 /* Setup some glformat defaults */
2497 This->glDescription.glFormat = glDesc->glFormat;
2498 This->glDescription.glFormatInternal = glDesc->glInternal;
2499 This->glDescription.glType = glDesc->glType;
2501 This->Flags &= ~SFLAG_ALLOCATED;
2502 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2503 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2505 return hr;
2508 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2509 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2511 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2512 WARN("Surface is locked or the HDC is in use\n");
2513 return WINED3DERR_INVALIDCALL;
2516 if(Mem && Mem != This->resource.allocatedMemory) {
2517 void *release = NULL;
2519 /* Do I have to copy the old surface content? */
2520 if(This->Flags & SFLAG_DIBSECTION) {
2521 /* Release the DC. No need to hold the critical section for the update
2522 * Thread because this thread runs only on front buffers, but this method
2523 * fails for render targets in the check above.
2525 SelectObject(This->hDC, This->dib.holdbitmap);
2526 DeleteDC(This->hDC);
2527 /* Release the DIB section */
2528 DeleteObject(This->dib.DIBsection);
2529 This->dib.bitmap_data = NULL;
2530 This->resource.allocatedMemory = NULL;
2531 This->hDC = NULL;
2532 This->Flags &= ~SFLAG_DIBSECTION;
2533 } else if(!(This->Flags & SFLAG_USERPTR)) {
2534 release = This->resource.heapMemory;
2535 This->resource.heapMemory = NULL;
2537 This->resource.allocatedMemory = Mem;
2538 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2540 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2541 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2543 /* For client textures opengl has to be notified */
2544 if(This->Flags & SFLAG_CLIENT) {
2545 This->Flags &= ~SFLAG_ALLOCATED;
2546 IWineD3DSurface_PreLoad(iface);
2547 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2550 /* Now free the old memory if any */
2551 HeapFree(GetProcessHeap(), 0, release);
2552 } else if(This->Flags & SFLAG_USERPTR) {
2553 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2554 This->resource.allocatedMemory = NULL;
2555 /* HeapMemory should be NULL already */
2556 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2557 This->Flags &= ~SFLAG_USERPTR;
2559 if(This->Flags & SFLAG_CLIENT) {
2560 This->Flags &= ~SFLAG_ALLOCATED;
2561 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2562 IWineD3DSurface_PreLoad(iface);
2565 return WINED3D_OK;
2568 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2569 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2570 IWineD3DSwapChainImpl *swapchain = NULL;
2571 HRESULT hr;
2572 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2574 /* Flipping is only supported on RenderTargets */
2575 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2577 if(override) {
2578 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2579 * FIXME("(%p) Target override is not supported by now\n", This);
2580 * Additionally, it isn't really possible to support triple-buffering
2581 * properly on opengl at all
2585 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2586 if(!swapchain) {
2587 ERR("Flipped surface is not on a swapchain\n");
2588 return WINEDDERR_NOTFLIPPABLE;
2591 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2592 * and only d3d8 and d3d9 apps specify the presentation interval
2594 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2595 /* Most common case first to avoid wasting time on all the other cases */
2596 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2597 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2598 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2599 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2600 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2601 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2602 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2603 } else {
2604 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2607 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2608 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2609 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2610 return hr;
2613 /* Does a direct frame buffer -> texture copy. Stretching is done
2614 * with single pixel copy calls
2616 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2617 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2618 float xrel, yrel;
2619 UINT row;
2620 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2623 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2624 ENTER_GL();
2625 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2627 /* TODO: Do we need GL_TEXTURE_2D enabled fpr copyteximage? */
2628 glEnable(This->glDescription.target);
2629 checkGLcall("glEnable(This->glDescription.target)");
2631 /* Bind the target texture */
2632 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2633 checkGLcall("glBindTexture");
2634 if(!swapchain) {
2635 glReadBuffer(myDevice->offscreenBuffer);
2636 } else {
2637 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2638 glReadBuffer(buffer);
2640 checkGLcall("glReadBuffer");
2642 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2643 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2645 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2646 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2648 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2649 ERR("Texture filtering not supported in direct blit\n");
2651 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2652 ERR("Texture filtering not supported in direct blit\n");
2655 if(upsidedown &&
2656 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2657 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2658 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2660 glCopyTexSubImage2D(This->glDescription.target,
2661 This->glDescription.level,
2662 drect->x1, drect->y1, /* xoffset, yoffset */
2663 srect->x1, Src->currentDesc.Height - srect->y2,
2664 drect->x2 - drect->x1, drect->y2 - drect->y1);
2665 } else {
2666 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2667 /* I have to process this row by row to swap the image,
2668 * otherwise it would be upside down, so stretching in y direction
2669 * doesn't cost extra time
2671 * However, stretching in x direction can be avoided if not necessary
2673 for(row = drect->y1; row < drect->y2; row++) {
2674 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2675 /* Well, that stuff works, but it's very slow.
2676 * find a better way instead
2678 UINT col;
2680 for(col = drect->x1; col < drect->x2; col++) {
2681 glCopyTexSubImage2D(This->glDescription.target,
2682 This->glDescription.level,
2683 drect->x1 + col, row, /* xoffset, yoffset */
2684 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2685 1, 1);
2687 } else {
2688 glCopyTexSubImage2D(This->glDescription.target,
2689 This->glDescription.level,
2690 drect->x1, row, /* xoffset, yoffset */
2691 srect->x1, yoffset - (int) (row * yrel),
2692 drect->x2-drect->x1, 1);
2696 vcheckGLcall("glCopyTexSubImage2D");
2698 /* Leave the opengl state valid for blitting */
2699 glDisable(This->glDescription.target);
2700 checkGLcall("glDisable(This->glDescription.target)");
2702 LEAVE_GL();
2705 /* Uses the hardware to stretch and flip the image */
2706 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2707 GLuint src, backup = 0;
2708 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2709 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2710 float left, right, top, bottom; /* Texture coordinates */
2711 UINT fbwidth = Src->currentDesc.Width;
2712 UINT fbheight = Src->currentDesc.Height;
2713 GLenum drawBuffer = GL_BACK;
2714 GLenum texture_target;
2716 TRACE("Using hwstretch blit\n");
2717 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2718 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2719 ENTER_GL();
2721 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2723 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2724 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2726 if(GL_LIMITS(aux_buffers) >= 2) {
2727 /* Got more than one aux buffer? Use the 2nd aux buffer */
2728 drawBuffer = GL_AUX1;
2729 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2730 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2731 drawBuffer = GL_AUX0;
2734 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2735 glGenTextures(1, &backup);
2736 checkGLcall("glGenTextures\n");
2737 glBindTexture(GL_TEXTURE_2D, backup);
2738 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2739 texture_target = GL_TEXTURE_2D;
2740 } else {
2741 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2742 * we are reading from the back buffer, the backup can be used as source texture
2744 if(Src->glDescription.textureName == 0) {
2745 /* Get it a description */
2746 IWineD3DSurface_PreLoad(SrcSurface);
2748 texture_target = Src->glDescription.target;
2749 glBindTexture(texture_target, Src->glDescription.textureName);
2750 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2751 glEnable(texture_target);
2752 checkGLcall("glEnable(texture_target)");
2754 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2755 Src->Flags &= ~SFLAG_INTEXTURE;
2758 glReadBuffer(GL_BACK);
2759 checkGLcall("glReadBuffer(GL_BACK)");
2761 /* TODO: Only back up the part that will be overwritten */
2762 glCopyTexSubImage2D(texture_target, 0,
2763 0, 0 /* read offsets */,
2764 0, 0,
2765 fbwidth,
2766 fbheight);
2768 checkGLcall("glCopyTexSubImage2D");
2770 /* No issue with overriding these - the sampler is dirty due to blit usage */
2771 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2772 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2773 checkGLcall("glTexParameteri");
2774 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2775 minMipLookup[Filter][WINED3DTEXF_NONE]);
2776 checkGLcall("glTexParameteri");
2778 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2779 src = backup ? backup : Src->glDescription.textureName;
2780 } else {
2781 glReadBuffer(GL_FRONT);
2782 checkGLcall("glReadBuffer(GL_FRONT)");
2784 glGenTextures(1, &src);
2785 checkGLcall("glGenTextures(1, &src)");
2786 glBindTexture(GL_TEXTURE_2D, src);
2787 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2789 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2790 * out for power of 2 sizes
2792 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2793 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2794 checkGLcall("glTexImage2D");
2795 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2796 0, 0 /* read offsets */,
2797 0, 0,
2798 fbwidth,
2799 fbheight);
2801 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2802 checkGLcall("glTexParameteri");
2803 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2804 checkGLcall("glTexParameteri");
2806 glReadBuffer(GL_BACK);
2807 checkGLcall("glReadBuffer(GL_BACK)");
2809 if(texture_target != GL_TEXTURE_2D) {
2810 glDisable(texture_target);
2811 glEnable(GL_TEXTURE_2D);
2812 texture_target = GL_TEXTURE_2D;
2815 checkGLcall("glEnd and previous");
2817 left = (float) srect->x1 / (float) Src->pow2Width;
2818 right = (float) srect->x2 / (float) Src->pow2Width;
2820 if(upsidedown) {
2821 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2822 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2823 } else {
2824 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2825 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2828 /* draw the source texture stretched and upside down. The correct surface is bound already */
2829 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
2830 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
2832 glDrawBuffer(drawBuffer);
2833 glReadBuffer(drawBuffer);
2835 glBegin(GL_QUADS);
2836 /* bottom left */
2837 glTexCoord2f(left, bottom);
2838 glVertex2i(0, fbheight);
2840 /* top left */
2841 glTexCoord2f(left, top);
2842 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2844 /* top right */
2845 glTexCoord2f(right, top);
2846 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2848 /* bottom right */
2849 glTexCoord2f(right, bottom);
2850 glVertex2i(drect->x2 - drect->x1, fbheight);
2851 glEnd();
2852 checkGLcall("glEnd and previous");
2854 if(texture_target != This->glDescription.target) {
2855 glDisable(texture_target);
2856 glEnable(This->glDescription.target);
2857 texture_target = This->glDescription.target;
2860 /* Now read the stretched and upside down image into the destination texture */
2861 glBindTexture(texture_target, This->glDescription.textureName);
2862 checkGLcall("glBindTexture");
2863 glCopyTexSubImage2D(texture_target,
2865 drect->x1, drect->y1, /* xoffset, yoffset */
2866 0, 0, /* We blitted the image to the origin */
2867 drect->x2 - drect->x1, drect->y2 - drect->y1);
2868 checkGLcall("glCopyTexSubImage2D");
2870 if(drawBuffer == GL_BACK) {
2871 /* Write the back buffer backup back */
2872 if(backup) {
2873 if(texture_target != GL_TEXTURE_2D) {
2874 glDisable(texture_target);
2875 glEnable(GL_TEXTURE_2D);
2876 texture_target = GL_TEXTURE_2D;
2878 glBindTexture(GL_TEXTURE_2D, backup);
2879 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
2880 } else {
2881 if(texture_target != Src->glDescription.target) {
2882 glDisable(texture_target);
2883 glEnable(Src->glDescription.target);
2884 texture_target = Src->glDescription.target;
2886 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
2887 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2890 glBegin(GL_QUADS);
2891 /* top left */
2892 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2893 glVertex2i(0, 0);
2895 /* bottom left */
2896 glTexCoord2f(0.0, 0.0);
2897 glVertex2i(0, fbheight);
2899 /* bottom right */
2900 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2901 glVertex2i(fbwidth, Src->currentDesc.Height);
2903 /* top right */
2904 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2905 glVertex2i(fbwidth, 0);
2906 glEnd();
2907 } else {
2908 /* Restore the old draw buffer */
2909 glDrawBuffer(GL_BACK);
2911 glDisable(texture_target);
2912 checkGLcall("glDisable(texture_target)");
2914 /* Cleanup */
2915 if(src != Src->glDescription.textureName && src != backup) {
2916 glDeleteTextures(1, &src);
2917 checkGLcall("glDeleteTextures(1, &src)");
2919 if(backup) {
2920 glDeleteTextures(1, &backup);
2921 checkGLcall("glDeleteTextures(1, &backup)");
2924 LEAVE_GL();
2927 /* Not called from the VTable */
2928 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2929 WINED3DRECT rect;
2930 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2931 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2932 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2934 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2936 /* Get the swapchain. One of the surfaces has to be a primary surface */
2937 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2938 WARN("Destination is in sysmem, rejecting gl blt\n");
2939 return WINED3DERR_INVALIDCALL;
2941 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2942 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2943 if(Src) {
2944 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2945 WARN("Src is in sysmem, rejecting gl blt\n");
2946 return WINED3DERR_INVALIDCALL;
2948 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2949 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2952 /* Early sort out of cases where no render target is used */
2953 if(!dstSwapchain && !srcSwapchain &&
2954 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2955 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2956 return WINED3DERR_INVALIDCALL;
2959 /* No destination color keying supported */
2960 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2961 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2962 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2963 return WINED3DERR_INVALIDCALL;
2966 if (DestRect) {
2967 rect.x1 = DestRect->left;
2968 rect.y1 = DestRect->top;
2969 rect.x2 = DestRect->right;
2970 rect.y2 = DestRect->bottom;
2971 } else {
2972 rect.x1 = 0;
2973 rect.y1 = 0;
2974 rect.x2 = This->currentDesc.Width;
2975 rect.y2 = This->currentDesc.Height;
2978 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2979 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2980 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2981 /* Half-life does a Blt from the back buffer to the front buffer,
2982 * Full surface size, no flags... Use present instead
2984 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2987 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2988 while(1)
2990 RECT mySrcRect;
2991 TRACE("Looking if a Present can be done...\n");
2992 /* Source Rectangle must be full surface */
2993 if( SrcRect ) {
2994 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2995 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2996 TRACE("No, Source rectangle doesn't match\n");
2997 break;
3000 mySrcRect.left = 0;
3001 mySrcRect.top = 0;
3002 mySrcRect.right = Src->currentDesc.Width;
3003 mySrcRect.bottom = Src->currentDesc.Height;
3005 /* No stretching may occur */
3006 if(mySrcRect.right != rect.x2 - rect.x1 ||
3007 mySrcRect.bottom != rect.y2 - rect.y1) {
3008 TRACE("No, stretching is done\n");
3009 break;
3012 /* Destination must be full surface or match the clipping rectangle */
3013 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3015 RECT cliprect;
3016 POINT pos[2];
3017 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3018 pos[0].x = rect.x1;
3019 pos[0].y = rect.y1;
3020 pos[1].x = rect.x2;
3021 pos[1].y = rect.y2;
3022 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3023 pos, 2);
3025 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3026 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3028 TRACE("No, dest rectangle doesn't match(clipper)\n");
3029 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3030 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3031 break;
3034 else
3036 if(rect.x1 != 0 || rect.y1 != 0 ||
3037 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3038 TRACE("No, dest rectangle doesn't match(surface size)\n");
3039 break;
3043 TRACE("Yes\n");
3045 /* These flags are unimportant for the flag check, remove them */
3046 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3047 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3049 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3050 * take very long, while a flip is fast.
3051 * This applies to Half-Life, which does such Blts every time it finished
3052 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3053 * menu. This is also used by all apps when they do windowed rendering
3055 * The problem is that flipping is not really the same as copying. After a
3056 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3057 * untouched. Therefore it's necessary to override the swap effect
3058 * and to set it back after the flip.
3060 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3061 * testcases.
3064 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3065 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3067 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3068 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3070 dstSwapchain->presentParms.SwapEffect = orig_swap;
3072 return WINED3D_OK;
3074 break;
3077 TRACE("Unsupported blit between buffers on the same swapchain\n");
3078 return WINED3DERR_INVALIDCALL;
3079 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3080 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3081 return WINED3DERR_INVALIDCALL;
3082 } else if(dstSwapchain && srcSwapchain) {
3083 FIXME("Implement hardware blit between two different swapchains\n");
3084 return WINED3DERR_INVALIDCALL;
3085 } else if(dstSwapchain) {
3086 if(SrcSurface == myDevice->render_targets[0]) {
3087 TRACE("Blit from active render target to a swapchain\n");
3088 /* Handled with regular texture -> swapchain blit */
3090 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3091 FIXME("Implement blit from a swapchain to the active render target\n");
3092 return WINED3DERR_INVALIDCALL;
3095 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3096 /* Blit from render target to texture */
3097 WINED3DRECT srect;
3098 BOOL upsideDown, stretchx;
3100 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3101 TRACE("Color keying not supported by frame buffer to texture blit\n");
3102 return WINED3DERR_INVALIDCALL;
3103 /* Destination color key is checked above */
3106 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3107 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3109 if(SrcRect) {
3110 if(SrcRect->top < SrcRect->bottom) {
3111 srect.y1 = SrcRect->top;
3112 srect.y2 = SrcRect->bottom;
3113 upsideDown = FALSE;
3114 } else {
3115 srect.y1 = SrcRect->bottom;
3116 srect.y2 = SrcRect->top;
3117 upsideDown = TRUE;
3119 srect.x1 = SrcRect->left;
3120 srect.x2 = SrcRect->right;
3121 } else {
3122 srect.x1 = 0;
3123 srect.y1 = 0;
3124 srect.x2 = Src->currentDesc.Width;
3125 srect.y2 = Src->currentDesc.Height;
3126 upsideDown = FALSE;
3128 if(rect.x1 > rect.x2) {
3129 UINT tmp = rect.x2;
3130 rect.x2 = rect.x1;
3131 rect.x1 = tmp;
3132 upsideDown = !upsideDown;
3134 if(!srcSwapchain) {
3135 TRACE("Reading from an offscreen target\n");
3136 upsideDown = !upsideDown;
3139 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3140 stretchx = TRUE;
3141 } else {
3142 stretchx = FALSE;
3145 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3146 * flip the image nor scale it.
3148 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3149 * -> If the app wants a image width an unscaled width, copy it line per line
3150 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3151 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3152 * back buffer. This is slower than reading line per line, thus not used for flipping
3153 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3154 * pixel by pixel
3156 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3157 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3158 * backends.
3160 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3161 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3162 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3163 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3164 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3165 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3166 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3167 } else {
3168 TRACE("Using hardware stretching to flip / stretch the texture\n");
3169 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3172 if(!(This->Flags & SFLAG_DONOTFREE)) {
3173 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3174 This->resource.allocatedMemory = NULL;
3175 This->resource.heapMemory = NULL;
3176 } else {
3177 This->Flags &= ~SFLAG_INSYSMEM;
3179 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3180 * path is never entered
3182 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3184 return WINED3D_OK;
3185 } else if(Src) {
3186 /* Blit from offscreen surface to render target */
3187 float glTexCoord[4];
3188 DWORD oldCKeyFlags = Src->CKeyFlags;
3189 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
3190 RECT SourceRectangle;
3191 BOOL paletteOverride = FALSE;
3193 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3195 if(SrcRect) {
3196 SourceRectangle.left = SrcRect->left;
3197 SourceRectangle.right = SrcRect->right;
3198 SourceRectangle.top = SrcRect->top;
3199 SourceRectangle.bottom = SrcRect->bottom;
3200 } else {
3201 SourceRectangle.left = 0;
3202 SourceRectangle.right = Src->currentDesc.Width;
3203 SourceRectangle.top = 0;
3204 SourceRectangle.bottom = Src->currentDesc.Height;
3206 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3207 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3208 TRACE("Using stretch_rect_fbo\n");
3209 /* The source is always a texture, but never the currently active render target, and the texture
3210 * contents are never upside down
3212 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3213 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3214 return WINED3D_OK;
3217 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3218 /* Fall back to software */
3219 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3220 SourceRectangle.left, SourceRectangle.top,
3221 SourceRectangle.right, SourceRectangle.bottom);
3222 return WINED3DERR_INVALIDCALL;
3225 /* Color keying: Check if we have to do a color keyed blt,
3226 * and if not check if a color key is activated.
3228 * Just modify the color keying parameters in the surface and restore them afterwards
3229 * The surface keeps track of the color key last used to load the opengl surface.
3230 * PreLoad will catch the change to the flags and color key and reload if necessary.
3232 if(Flags & WINEDDBLT_KEYSRC) {
3233 /* Use color key from surface */
3234 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3235 /* Use color key from DDBltFx */
3236 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3237 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3238 } else {
3239 /* Do not use color key */
3240 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3243 /* When blitting from an offscreen surface to a rendertarget, the source
3244 * surface is not required to have a palette. Our rendering / conversion
3245 * code further down the road retrieves the palette from the surface, so
3246 * it must have a palette set. */
3247 if((Src->resource.format == WINED3DFMT_P8) && (Src->palette == NULL)) {
3248 paletteOverride = TRUE;
3249 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3250 Src->palette = This->palette;
3253 /* Now load the surface */
3254 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3257 /* Activate the destination context, set it up for blitting */
3258 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3259 ENTER_GL();
3261 glEnable(Src->glDescription.target);
3262 checkGLcall("glEnable(Src->glDescription.target)");
3264 if(!dstSwapchain) {
3265 TRACE("Drawing to offscreen buffer\n");
3266 glDrawBuffer(myDevice->offscreenBuffer);
3267 checkGLcall("glDrawBuffer");
3268 } else {
3269 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3270 TRACE("Drawing to %#x buffer\n", buffer);
3271 glDrawBuffer(buffer);
3272 checkGLcall("glDrawBuffer");
3275 /* Bind the texture */
3276 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3277 checkGLcall("glBindTexture");
3279 /* Filtering for StretchRect */
3280 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3281 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3282 checkGLcall("glTexParameteri");
3283 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3284 minMipLookup[Filter][WINED3DTEXF_NONE]);
3285 checkGLcall("glTexParameteri");
3286 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3287 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3288 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3289 checkGLcall("glTexEnvi");
3291 /* This is for color keying */
3292 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3293 glEnable(GL_ALPHA_TEST);
3294 checkGLcall("glEnable GL_ALPHA_TEST");
3296 /* When the primary render target uses P8, the alpha component contains the palette index.
3297 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3298 * should be masked away have alpha set to 0. */
3299 if(primary_render_target_is_p8(myDevice))
3300 glAlphaFunc(GL_NOTEQUAL, (float)This->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3301 else
3302 glAlphaFunc(GL_NOTEQUAL, 0.0);
3303 checkGLcall("glAlphaFunc\n");
3304 } else {
3305 glDisable(GL_ALPHA_TEST);
3306 checkGLcall("glDisable GL_ALPHA_TEST");
3309 /* Draw a textured quad
3311 glBegin(GL_QUADS);
3313 glColor3d(1.0f, 1.0f, 1.0f);
3314 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3315 glVertex3f(rect.x1,
3316 rect.y1,
3317 0.0);
3319 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3320 glVertex3f(rect.x1, rect.y2, 0.0);
3322 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3323 glVertex3f(rect.x2,
3324 rect.y2,
3325 0.0);
3327 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3328 glVertex3f(rect.x2,
3329 rect.y1,
3330 0.0);
3331 glEnd();
3332 checkGLcall("glEnd");
3334 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3335 glDisable(GL_ALPHA_TEST);
3336 checkGLcall("glDisable(GL_ALPHA_TEST)");
3339 /* Flush in case the drawable is used by multiple GL contexts */
3340 if(dstSwapchain && (dstSwapchain->num_contexts >= 2))
3341 glFlush();
3343 glBindTexture(Src->glDescription.target, 0);
3344 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3345 /* Leave the opengl state valid for blitting */
3346 glDisable(Src->glDescription.target);
3347 checkGLcall("glDisable(Src->glDescription.target)");
3349 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3350 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3352 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3353 glDrawBuffer(GL_BACK);
3354 checkGLcall("glDrawBuffer");
3356 /* Restore the color key parameters */
3357 Src->CKeyFlags = oldCKeyFlags;
3358 This->SrcBltCKey = oldBltCKey;
3360 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3361 if(paletteOverride)
3362 Src->palette = NULL;
3364 LEAVE_GL();
3366 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3367 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3368 * is outdated now
3370 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3371 /* TODO: This should be moved to ModifyLocation() */
3372 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3373 This->Flags |= SFLAG_INTEXTURE;
3376 return WINED3D_OK;
3377 } else {
3378 /* Source-Less Blit to render target */
3379 if (Flags & WINEDDBLT_COLORFILL) {
3380 /* This is easy to handle for the D3D Device... */
3381 DWORD color;
3383 TRACE("Colorfill\n");
3385 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3386 must be true if we are here */
3387 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3388 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3389 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3390 TRACE("Surface is higher back buffer, falling back to software\n");
3391 return WINED3DERR_INVALIDCALL;
3394 /* The color as given in the Blt function is in the format of the frame-buffer...
3395 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3397 if (This->resource.format == WINED3DFMT_P8) {
3398 DWORD alpha;
3400 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3401 else alpha = 0xFF000000;
3403 if (This->palette) {
3404 color = (alpha |
3405 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3406 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3407 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3408 } else {
3409 color = alpha;
3412 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3413 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3414 color = 0xFFFFFFFF;
3415 } else {
3416 color = ((0xFF000000) |
3417 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3418 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3419 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3422 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3423 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3424 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3426 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3427 color = DDBltFx->u5.dwFillColor;
3429 else {
3430 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3431 return WINED3DERR_INVALIDCALL;
3434 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3435 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3436 1, /* Number of rectangles */
3437 &rect, WINED3DCLEAR_TARGET, color,
3438 0.0 /* Z */,
3439 0 /* Stencil */);
3440 return WINED3D_OK;
3444 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3445 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3446 return WINED3DERR_INVALIDCALL;
3449 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3451 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3452 float depth;
3454 if (Flags & WINEDDBLT_DEPTHFILL) {
3455 switch(This->resource.format) {
3456 case WINED3DFMT_D16:
3457 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3458 break;
3459 case WINED3DFMT_D15S1:
3460 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3461 break;
3462 case WINED3DFMT_D24S8:
3463 case WINED3DFMT_D24X8:
3464 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3465 break;
3466 case WINED3DFMT_D32:
3467 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3468 break;
3469 default:
3470 depth = 0.0;
3471 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3474 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3475 DestRect == NULL ? 0 : 1,
3476 (WINED3DRECT *) DestRect,
3477 WINED3DCLEAR_ZBUFFER,
3478 0x00000000,
3479 depth,
3480 0x00000000);
3483 FIXME("(%p): Unsupp depthstencil blit\n", This);
3484 return WINED3DERR_INVALIDCALL;
3487 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3488 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3489 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3490 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3491 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3492 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3494 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3496 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3497 return WINEDDERR_SURFACEBUSY;
3500 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3501 * except depth blits, which seem to work
3503 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3504 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3505 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3506 return WINED3DERR_INVALIDCALL;
3507 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3508 TRACE("Z Blit override handled the blit\n");
3509 return WINED3D_OK;
3513 /* Special cases for RenderTargets */
3514 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3515 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3516 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3519 /* For the rest call the X11 surface implementation.
3520 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3521 * other Blts are rather rare
3523 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3526 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3527 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3528 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3529 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3530 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3532 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3534 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3535 return WINEDDERR_SURFACEBUSY;
3538 if(myDevice->inScene &&
3539 (iface == myDevice->stencilBufferTarget ||
3540 (Source && Source == myDevice->stencilBufferTarget))) {
3541 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3542 return WINED3DERR_INVALIDCALL;
3545 /* Special cases for RenderTargets */
3546 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3547 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3549 RECT SrcRect, DstRect;
3550 DWORD Flags=0;
3552 if(rsrc) {
3553 SrcRect.left = rsrc->left;
3554 SrcRect.top= rsrc->top;
3555 SrcRect.bottom = rsrc->bottom;
3556 SrcRect.right = rsrc->right;
3557 } else {
3558 SrcRect.left = 0;
3559 SrcRect.top = 0;
3560 SrcRect.right = srcImpl->currentDesc.Width;
3561 SrcRect.bottom = srcImpl->currentDesc.Height;
3564 DstRect.left = dstx;
3565 DstRect.top=dsty;
3566 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3567 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3569 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3570 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3571 Flags |= WINEDDBLT_KEYSRC;
3572 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3573 Flags |= WINEDDBLT_KEYDEST;
3574 if(trans & WINEDDBLTFAST_WAIT)
3575 Flags |= WINEDDBLT_WAIT;
3576 if(trans & WINEDDBLTFAST_DONOTWAIT)
3577 Flags |= WINEDDBLT_DONOTWAIT;
3579 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3583 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3586 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3587 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3588 RGBQUAD col[256];
3589 IWineD3DPaletteImpl *pal = This->palette;
3590 unsigned int n;
3591 TRACE("(%p)\n", This);
3593 if (!pal) return WINED3D_OK;
3595 if(This->resource.format == WINED3DFMT_P8 ||
3596 This->resource.format == WINED3DFMT_A8P8)
3598 if(!(This->Flags & SFLAG_INSYSMEM)) {
3599 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3600 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3602 TRACE("Dirtifying surface\n");
3603 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3606 if(This->Flags & SFLAG_DIBSECTION) {
3607 TRACE("(%p): Updating the hdc's palette\n", This);
3608 for (n=0; n<256; n++) {
3609 col[n].rgbRed = pal->palents[n].peRed;
3610 col[n].rgbGreen = pal->palents[n].peGreen;
3611 col[n].rgbBlue = pal->palents[n].peBlue;
3612 col[n].rgbReserved = 0;
3614 SetDIBColorTable(This->hDC, 0, 256, col);
3617 /* Propagate the changes to the drawable when we have a palette.
3618 * TODO: in case of hardware p8 palettes we should only upload the palette. */
3619 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3620 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3622 return WINED3D_OK;
3625 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3626 /** Check against the maximum texture sizes supported by the video card **/
3627 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3628 unsigned int pow2Width, pow2Height;
3629 const GlPixelFormatDesc *glDesc;
3631 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3632 /* Setup some glformat defaults */
3633 This->glDescription.glFormat = glDesc->glFormat;
3634 This->glDescription.glFormatInternal = glDesc->glInternal;
3635 This->glDescription.glType = glDesc->glType;
3637 This->glDescription.textureName = 0;
3638 This->glDescription.target = GL_TEXTURE_2D;
3640 /* Non-power2 support */
3641 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3642 pow2Width = This->currentDesc.Width;
3643 pow2Height = This->currentDesc.Height;
3644 } else {
3645 /* Find the nearest pow2 match */
3646 pow2Width = pow2Height = 1;
3647 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3648 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3650 This->pow2Width = pow2Width;
3651 This->pow2Height = pow2Height;
3653 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3654 WINED3DFORMAT Format = This->resource.format;
3655 /** TODO: add support for non power two compressed textures **/
3656 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3657 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3658 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3659 This, This->currentDesc.Width, This->currentDesc.Height);
3660 return WINED3DERR_NOTAVAILABLE;
3664 if(pow2Width != This->currentDesc.Width ||
3665 pow2Height != This->currentDesc.Height) {
3666 This->Flags |= SFLAG_NONPOW2;
3669 TRACE("%p\n", This);
3670 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3671 /* one of three options
3672 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)
3673 2: Set the texture to the maximum size (bad idea)
3674 3: WARN and return WINED3DERR_NOTAVAILABLE;
3675 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.
3677 WARN("(%p) Creating an oversized surface\n", This);
3678 This->Flags |= SFLAG_OVERSIZE;
3680 /* This will be initialized on the first blt */
3681 This->glRect.left = 0;
3682 This->glRect.top = 0;
3683 This->glRect.right = 0;
3684 This->glRect.bottom = 0;
3685 } else {
3686 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3687 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3688 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3689 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3691 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE) &&
3692 !((This->resource.format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE) && (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3694 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3695 This->pow2Width = This->currentDesc.Width;
3696 This->pow2Height = This->currentDesc.Height;
3697 This->Flags &= ~SFLAG_NONPOW2;
3700 /* No oversize, gl rect is the full texture size */
3701 This->Flags &= ~SFLAG_OVERSIZE;
3702 This->glRect.left = 0;
3703 This->glRect.top = 0;
3704 This->glRect.right = This->pow2Width;
3705 This->glRect.bottom = This->pow2Height;
3708 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3709 switch(wined3d_settings.offscreen_rendering_mode) {
3710 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3711 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3712 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3716 This->Flags |= SFLAG_INSYSMEM;
3718 return WINED3D_OK;
3721 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3722 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3723 IWineD3DBaseTexture *texture;
3725 TRACE("(%p)->(%s, %s)\n", iface,
3726 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3727 persistent ? "TRUE" : "FALSE");
3729 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3730 IWineD3DSwapChain *swapchain = NULL;
3732 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3733 TRACE("Surface %p is an onscreen surface\n", iface);
3735 IWineD3DSwapChain_Release(swapchain);
3736 } else {
3737 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3738 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3742 if(persistent) {
3743 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
3744 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3745 TRACE("Passing to container\n");
3746 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3747 IWineD3DBaseTexture_Release(texture);
3750 This->Flags &= ~SFLAG_LOCATIONS;
3751 This->Flags |= flag;
3752 } else {
3753 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
3754 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3755 TRACE("Passing to container\n");
3756 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3757 IWineD3DBaseTexture_Release(texture);
3760 This->Flags &= ~flag;
3764 struct coords {
3765 GLfloat x, y, z;
3768 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
3769 struct coords coords[4];
3770 RECT rect;
3771 IWineD3DSwapChain *swapchain = NULL;
3772 IWineD3DBaseTexture *texture = NULL;
3773 HRESULT hr;
3774 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3776 if(rect_in) {
3777 rect = *rect_in;
3778 } else {
3779 rect.left = 0;
3780 rect.top = 0;
3781 rect.right = This->currentDesc.Width;
3782 rect.bottom = This->currentDesc.Height;
3785 ActivateContext(device, device->render_targets[0], CTXUSAGE_BLIT);
3786 ENTER_GL();
3788 if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
3789 glEnable(GL_TEXTURE_RECTANGLE_ARB);
3790 checkGLcall("glEnable(GL_TEXTURE_RECTANGLE_ARB)");
3791 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName);
3792 checkGLcall("GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName)");
3793 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3794 checkGLcall("glTexParameteri");
3795 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3796 checkGLcall("glTexParameteri");
3798 coords[0].x = rect.left;
3799 coords[0].z = 0;
3801 coords[1].x = rect.left;
3802 coords[1].z = 0;
3804 coords[2].x = rect.right;
3805 coords[2].z = 0;
3807 coords[3].x = rect.right;
3808 coords[3].z = 0;
3810 coords[0].y = rect.top;
3811 coords[1].y = rect.bottom;
3812 coords[2].y = rect.bottom;
3813 coords[3].y = rect.top;
3814 } else if(This->glDescription.target == GL_TEXTURE_2D) {
3815 glEnable(GL_TEXTURE_2D);
3816 checkGLcall("glEnable(GL_TEXTURE_2D)");
3817 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
3818 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
3819 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3820 checkGLcall("glTexParameteri");
3821 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3822 checkGLcall("glTexParameteri");
3824 coords[0].x = (float)rect.left / This->pow2Width;
3825 coords[0].z = 0;
3827 coords[1].x = (float)rect.left / This->pow2Width;
3828 coords[1].z = 0;
3830 coords[2].x = (float)rect.right / This->pow2Width;
3831 coords[2].z = 0;
3833 coords[3].x = (float)rect.right / This->pow2Width;
3834 coords[3].z = 0;
3836 coords[0].y = (float)rect.top / This->pow2Height;
3837 coords[1].y = (float)rect.bottom / This->pow2Height;
3838 coords[2].y = (float)rect.bottom / This->pow2Height;
3839 coords[3].y = (float)rect.top / This->pow2Height;
3840 } else {
3841 /* Must be a cube map */
3842 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
3843 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
3844 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
3845 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
3846 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3847 checkGLcall("glTexParameteri");
3848 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3849 checkGLcall("glTexParameteri");
3851 switch(This->glDescription.target) {
3852 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3853 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
3854 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3855 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3856 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
3857 break;
3859 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3860 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3861 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
3862 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
3863 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3864 break;
3866 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3867 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
3868 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3869 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3870 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
3871 break;
3873 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3874 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3875 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3876 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3877 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3878 break;
3880 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3881 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3882 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3883 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
3884 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
3885 break;
3887 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
3888 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
3889 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
3890 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3891 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3892 break;
3894 default:
3895 ERR("Unexpected texture target\n");
3896 LEAVE_GL();
3897 return;
3901 glBegin(GL_QUADS);
3902 glTexCoord3fv(&coords[0].x);
3903 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
3905 glTexCoord3fv(&coords[1].x);
3906 glVertex2i(rect.left, device->render_offscreen ? rect.top : rect.bottom);
3908 glTexCoord3fv(&coords[2].x);
3909 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
3911 glTexCoord3fv(&coords[3].x);
3912 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
3913 glEnd();
3914 checkGLcall("glEnd");
3916 if(This->glDescription.target != GL_TEXTURE_2D) {
3917 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3918 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3919 } else {
3920 glDisable(GL_TEXTURE_2D);
3921 checkGLcall("glDisable(GL_TEXTURE_2D)");
3924 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain);
3925 if(hr == WINED3D_OK && swapchain) {
3926 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
3927 if(((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
3928 glFlush();
3930 IWineD3DSwapChain_Release(swapchain);
3931 } else {
3932 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
3933 * reset properly next draw
3935 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture);
3936 if(hr == WINED3D_OK && texture) {
3937 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
3938 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
3939 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
3940 IWineD3DBaseTexture_Release(texture);
3943 LEAVE_GL();
3946 /*****************************************************************************
3947 * IWineD3DSurface::LoadLocation
3949 * Copies the current surface data from wherever it is to the requested
3950 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
3951 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
3952 * multiple locations, the gl texture is preferred over the drawable, which is
3953 * preferred over system memory. The PBO counts as system memory. If rect is
3954 * not NULL, only the specified rectangle is copied (only supported for
3955 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
3956 * location is marked up to date after the copy.
3958 * Parameters:
3959 * flag: Surface location flag to be updated
3960 * rect: rectangle to be copied
3962 * Returns:
3963 * WINED3D_OK on success
3964 * WINED3DERR_DEVICELOST on an internal error
3966 *****************************************************************************/
3967 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
3968 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3969 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3970 IWineD3DSwapChain *swapchain = NULL;
3971 GLenum format, internal, type;
3972 CONVERT_TYPES convert;
3973 int bpp;
3974 int width, pitch, outpitch;
3975 BYTE *mem;
3977 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3978 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3979 TRACE("Surface %p is an onscreen surface\n", iface);
3981 IWineD3DSwapChain_Release(swapchain);
3982 } else {
3983 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
3984 * Prefer SFLAG_INTEXTURE. */
3985 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
3989 TRACE("(%p)->(%s, %p)\n", iface,
3990 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3991 rect);
3992 if(rect) {
3993 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
3996 if(This->Flags & flag) {
3997 TRACE("Location already up to date\n");
3998 return WINED3D_OK;
4001 if(!(This->Flags & SFLAG_LOCATIONS)) {
4002 ERR("Surface does not have any up to date location\n");
4003 This->Flags |= SFLAG_LOST;
4004 return WINED3DERR_DEVICELOST;
4007 if(flag == SFLAG_INSYSMEM) {
4008 surface_prepare_system_memory(This);
4010 /* Download the surface to system memory */
4011 if(This->Flags & SFLAG_INTEXTURE) {
4012 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4013 surface_bind_and_dirtify(This);
4015 surface_download_data(This);
4016 } else {
4017 read_from_framebuffer(This, rect,
4018 This->resource.allocatedMemory,
4019 IWineD3DSurface_GetPitch(iface));
4021 } else if(flag == SFLAG_INDRAWABLE) {
4022 if(This->Flags & SFLAG_INTEXTURE) {
4023 surface_blt_to_drawable(This, rect);
4024 } else {
4025 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4027 /* The width is in 'length' not in bytes */
4028 width = This->currentDesc.Width;
4029 pitch = IWineD3DSurface_GetPitch(iface);
4031 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4032 int height = This->currentDesc.Height;
4034 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4035 outpitch = width * bpp;
4036 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4038 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4039 if(!mem) {
4040 ERR("Out of memory %d, %d!\n", outpitch, height);
4041 return WINED3DERR_OUTOFVIDEOMEMORY;
4043 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4045 This->Flags |= SFLAG_CONVERTED;
4046 } else {
4047 This->Flags &= ~SFLAG_CONVERTED;
4048 mem = This->resource.allocatedMemory;
4051 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4053 /* Don't delete PBO memory */
4054 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4055 HeapFree(GetProcessHeap(), 0, mem);
4057 } else /* if(flag == SFLAG_INTEXTURE) */ {
4058 if (This->Flags & SFLAG_INDRAWABLE) {
4059 read_from_framebuffer_texture(This);
4060 } else { /* Upload from system memory */
4061 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4063 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4064 surface_bind_and_dirtify(This);
4065 ENTER_GL();
4067 /* The only place where LoadTexture() might get called when isInDraw=1
4068 * is ActivateContext where lastActiveRenderTarget is preloaded.
4070 if(iface == device->lastActiveRenderTarget && device->isInDraw)
4071 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
4073 /* Otherwise: System memory copy must be most up to date */
4075 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4076 This->Flags |= SFLAG_GLCKEY;
4077 This->glCKey = This->SrcBltCKey;
4079 else This->Flags &= ~SFLAG_GLCKEY;
4081 /* The width is in 'length' not in bytes */
4082 width = This->currentDesc.Width;
4083 pitch = IWineD3DSurface_GetPitch(iface);
4085 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4086 int height = This->currentDesc.Height;
4088 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4089 outpitch = width * bpp;
4090 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4092 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4093 if(!mem) {
4094 ERR("Out of memory %d, %d!\n", outpitch, height);
4095 return WINED3DERR_OUTOFVIDEOMEMORY;
4097 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4099 This->Flags |= SFLAG_CONVERTED;
4100 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
4101 d3dfmt_p8_upload_palette(iface, convert);
4102 This->Flags &= ~SFLAG_CONVERTED;
4103 mem = This->resource.allocatedMemory;
4104 } else {
4105 This->Flags &= ~SFLAG_CONVERTED;
4106 mem = This->resource.allocatedMemory;
4109 /* Make sure the correct pitch is used */
4110 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4112 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4113 TRACE("non power of two support\n");
4114 if(!(This->Flags & SFLAG_ALLOCATED)) {
4115 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4117 if (mem || (This->Flags & SFLAG_PBO)) {
4118 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4120 } else {
4121 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4122 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4124 if(!(This->Flags & SFLAG_ALLOCATED)) {
4125 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4127 if (mem || (This->Flags & SFLAG_PBO)) {
4128 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4132 /* Restore the default pitch */
4133 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4134 LEAVE_GL();
4136 /* Don't delete PBO memory */
4137 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4138 HeapFree(GetProcessHeap(), 0, mem);
4142 if(rect == NULL) {
4143 This->Flags |= flag;
4146 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
4147 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4148 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4149 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4152 return WINED3D_OK;
4155 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
4156 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4157 IWineD3DSwapChain *swapchain = NULL;
4159 /* Update the drawable size method */
4160 if(container) {
4161 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4163 if(swapchain) {
4164 This->get_drawable_size = get_drawable_size_swapchain;
4165 IWineD3DSwapChain_Release(swapchain);
4166 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4167 switch(wined3d_settings.offscreen_rendering_mode) {
4168 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4169 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4170 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4174 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4177 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4178 return SURFACE_OPENGL;
4181 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4183 /* IUnknown */
4184 IWineD3DBaseSurfaceImpl_QueryInterface,
4185 IWineD3DBaseSurfaceImpl_AddRef,
4186 IWineD3DSurfaceImpl_Release,
4187 /* IWineD3DResource */
4188 IWineD3DBaseSurfaceImpl_GetParent,
4189 IWineD3DBaseSurfaceImpl_GetDevice,
4190 IWineD3DBaseSurfaceImpl_SetPrivateData,
4191 IWineD3DBaseSurfaceImpl_GetPrivateData,
4192 IWineD3DBaseSurfaceImpl_FreePrivateData,
4193 IWineD3DBaseSurfaceImpl_SetPriority,
4194 IWineD3DBaseSurfaceImpl_GetPriority,
4195 IWineD3DSurfaceImpl_PreLoad,
4196 IWineD3DSurfaceImpl_UnLoad,
4197 IWineD3DBaseSurfaceImpl_GetType,
4198 /* IWineD3DSurface */
4199 IWineD3DBaseSurfaceImpl_GetContainer,
4200 IWineD3DBaseSurfaceImpl_GetDesc,
4201 IWineD3DSurfaceImpl_LockRect,
4202 IWineD3DSurfaceImpl_UnlockRect,
4203 IWineD3DSurfaceImpl_GetDC,
4204 IWineD3DSurfaceImpl_ReleaseDC,
4205 IWineD3DSurfaceImpl_Flip,
4206 IWineD3DSurfaceImpl_Blt,
4207 IWineD3DBaseSurfaceImpl_GetBltStatus,
4208 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4209 IWineD3DBaseSurfaceImpl_IsLost,
4210 IWineD3DBaseSurfaceImpl_Restore,
4211 IWineD3DSurfaceImpl_BltFast,
4212 IWineD3DBaseSurfaceImpl_GetPalette,
4213 IWineD3DBaseSurfaceImpl_SetPalette,
4214 IWineD3DSurfaceImpl_RealizePalette,
4215 IWineD3DBaseSurfaceImpl_SetColorKey,
4216 IWineD3DBaseSurfaceImpl_GetPitch,
4217 IWineD3DSurfaceImpl_SetMem,
4218 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4219 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4220 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4221 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4222 IWineD3DBaseSurfaceImpl_SetClipper,
4223 IWineD3DBaseSurfaceImpl_GetClipper,
4224 /* Internal use: */
4225 IWineD3DSurfaceImpl_AddDirtyRect,
4226 IWineD3DSurfaceImpl_LoadTexture,
4227 IWineD3DSurfaceImpl_BindTexture,
4228 IWineD3DSurfaceImpl_SaveSnapshot,
4229 IWineD3DSurfaceImpl_SetContainer,
4230 IWineD3DSurfaceImpl_SetGlTextureDesc,
4231 IWineD3DSurfaceImpl_GetGlDesc,
4232 IWineD3DSurfaceImpl_GetData,
4233 IWineD3DSurfaceImpl_SetFormat,
4234 IWineD3DSurfaceImpl_PrivateSetup,
4235 IWineD3DSurfaceImpl_ModifyLocation,
4236 IWineD3DSurfaceImpl_LoadLocation,
4237 IWineD3DSurfaceImpl_GetImplType