push 63c1876572cbb61a874995ad42ef27c14590d232
[wine/hacks.git] / dlls / wined3d / surface.c
blob07c7076bd7fc2d18ff2bdfb3c007d4c193a81c03
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 void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert);
39 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This);
40 static void surface_remove_pbo(IWineD3DSurfaceImpl *This);
42 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This) {
43 int active_sampler;
45 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
46 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
47 * gl states. The current texture unit should always be a valid one.
49 * TODO: Track the current active texture per GL context instead of using glGet
51 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
52 GLint active_texture;
53 ENTER_GL();
54 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
55 LEAVE_GL();
56 active_sampler = This->resource.wineD3DDevice->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
57 } else {
58 active_sampler = 0;
61 if (active_sampler != -1) {
62 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_sampler));
64 IWineD3DSurface_BindTexture((IWineD3DSurface *)This);
67 /* This function checks if the primary render target uses the 8bit paletted format. */
68 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
70 if (device->render_targets && device->render_targets[0]) {
71 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
72 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
73 return TRUE;
75 return FALSE;
78 /* This call just downloads data, the caller is responsible for activating the
79 * right context and binding the correct texture. */
80 static void surface_download_data(IWineD3DSurfaceImpl *This) {
81 if (0 == This->glDescription.textureName) {
82 ERR("Surface does not have a texture, but SFLAG_INTEXTURE is set\n");
83 return;
86 /* Only support read back of converted P8 surfaces */
87 if(This->Flags & SFLAG_CONVERTED && (This->resource.format != WINED3DFMT_P8)) {
88 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(This->resource.format));
89 return;
92 ENTER_GL();
94 if (This->resource.format == WINED3DFMT_DXT1 ||
95 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
96 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5 ||
97 This->resource.format == WINED3DFMT_ATI2N) {
98 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
99 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
100 } else {
101 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
102 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
104 if(This->Flags & SFLAG_PBO) {
105 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
106 checkGLcall("glBindBufferARB");
107 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
108 checkGLcall("glGetCompressedTexImageARB()");
109 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
110 checkGLcall("glBindBufferARB");
111 } else {
112 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
113 checkGLcall("glGetCompressedTexImageARB()");
116 LEAVE_GL();
117 } else {
118 void *mem;
119 GLenum format = This->glDescription.glFormat;
120 GLenum type = This->glDescription.glType;
121 int src_pitch = 0;
122 int dst_pitch = 0;
124 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
125 if(This->resource.format == WINED3DFMT_P8 && primary_render_target_is_p8(This->resource.wineD3DDevice)) {
126 format = GL_ALPHA;
127 type = GL_UNSIGNED_BYTE;
130 if (This->Flags & SFLAG_NONPOW2) {
131 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
132 src_pitch = This->bytesPerPixel * This->pow2Width;
133 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
134 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
135 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
136 } else {
137 mem = This->resource.allocatedMemory;
140 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
141 format, type, mem);
143 if(This->Flags & SFLAG_PBO) {
144 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
145 checkGLcall("glBindBufferARB");
147 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
148 type, NULL);
149 checkGLcall("glGetTexImage()");
151 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
152 checkGLcall("glBindBufferARB");
153 } else {
154 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
155 type, mem);
156 checkGLcall("glGetTexImage()");
158 LEAVE_GL();
160 if (This->Flags & SFLAG_NONPOW2) {
161 LPBYTE src_data, dst_data;
162 int y;
164 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
165 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
166 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
168 * We're doing this...
170 * instead of boxing the texture :
171 * |<-texture width ->| -->pow2width| /\
172 * |111111111111111111| | |
173 * |222 Texture 222222| boxed empty | texture height
174 * |3333 Data 33333333| | |
175 * |444444444444444444| | \/
176 * ----------------------------------- |
177 * | boxed empty | boxed empty | pow2height
178 * | | | \/
179 * -----------------------------------
182 * we're repacking the data to the expected texture width
184 * |<-texture width ->| -->pow2width| /\
185 * |111111111111111111222222222222222| |
186 * |222333333333333333333444444444444| texture height
187 * |444444 | |
188 * | | \/
189 * | | |
190 * | empty | pow2height
191 * | | \/
192 * -----------------------------------
194 * == is the same as
196 * |<-texture width ->| /\
197 * |111111111111111111|
198 * |222222222222222222|texture height
199 * |333333333333333333|
200 * |444444444444444444| \/
201 * --------------------
203 * this also means that any references to allocatedMemory should work with the data as if were a
204 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
206 * internally the texture is still stored in a boxed format so any references to textureName will
207 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
209 * Performance should not be an issue, because applications normally do not lock the surfaces when
210 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
211 * and doesn't have to be re-read.
213 src_data = mem;
214 dst_data = This->resource.allocatedMemory;
215 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
216 for (y = 1 ; y < This->currentDesc.Height; y++) {
217 /* skip the first row */
218 src_data += src_pitch;
219 dst_data += dst_pitch;
220 memcpy(dst_data, src_data, dst_pitch);
223 HeapFree(GetProcessHeap(), 0, mem);
227 /* Surface has now been downloaded */
228 This->Flags |= SFLAG_INSYSMEM;
231 /* This call just uploads data, the caller is responsible for activating the
232 * right context and binding the correct texture. */
233 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
234 if (This->resource.format == WINED3DFMT_DXT1 ||
235 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
236 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5 ||
237 This->resource.format == WINED3DFMT_ATI2N) {
238 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
239 FIXME("Using DXT1/3/5 without advertized support\n");
240 } else {
241 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
242 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
243 * function uses glCompressedTexImage2D instead of the SubImage call
245 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
246 ENTER_GL();
248 if(This->Flags & SFLAG_PBO) {
249 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
250 checkGLcall("glBindBufferARB");
251 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
253 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
254 width, height, 0 /* border */, This->resource.size, NULL));
255 checkGLcall("glCompressedTexSubImage2D");
257 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
258 checkGLcall("glBindBufferARB");
259 } else {
260 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
261 width, height, 0 /* border */, This->resource.size, data));
262 checkGLcall("glCompressedTexSubImage2D");
264 LEAVE_GL();
266 } else {
267 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
268 ENTER_GL();
270 if(This->Flags & SFLAG_PBO) {
271 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
272 checkGLcall("glBindBufferARB");
273 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
275 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
276 checkGLcall("glTexSubImage2D");
278 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
279 checkGLcall("glBindBufferARB");
281 else {
282 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
283 checkGLcall("glTexSubImage2D");
286 LEAVE_GL();
290 /* This call just allocates the texture, the caller is responsible for
291 * activating the right context and binding the correct texture. */
292 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
293 BOOL enable_client_storage = FALSE;
294 BYTE *mem = NULL;
296 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,
297 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
299 if (This->resource.format == WINED3DFMT_DXT1 ||
300 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
301 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5 ||
302 This->resource.format == WINED3DFMT_ATI2N) {
303 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
304 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
306 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
307 * once, unfortunately
309 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
310 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
311 This->Flags |= SFLAG_CLIENT;
312 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
313 ENTER_GL();
314 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
315 width, height, 0 /* border */, This->resource.size, mem));
316 LEAVE_GL();
319 return;
322 ENTER_GL();
324 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
325 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
326 /* In some cases we want to disable client storage.
327 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
328 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
329 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
330 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
331 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
333 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
334 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
335 This->Flags &= ~SFLAG_CLIENT;
336 enable_client_storage = TRUE;
337 } else {
338 This->Flags |= SFLAG_CLIENT;
340 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
341 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
343 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
346 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
347 checkGLcall("glTexImage2D");
349 if(enable_client_storage) {
350 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
351 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
353 LEAVE_GL();
355 This->Flags |= SFLAG_ALLOCATED;
358 /* In D3D the depth stencil dimensions have to be greater than or equal to the
359 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
360 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
361 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
362 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
363 renderbuffer_entry_t *entry;
364 GLuint renderbuffer = 0;
365 unsigned int src_width, src_height;
367 src_width = This->pow2Width;
368 src_height = This->pow2Height;
370 /* A depth stencil smaller than the render target is not valid */
371 if (width > src_width || height > src_height) return;
373 /* Remove any renderbuffer set if the sizes match */
374 if (width == src_width && height == src_height) {
375 This->current_renderbuffer = NULL;
376 return;
379 /* Look if we've already got a renderbuffer of the correct dimensions */
380 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
381 if (entry->width == width && entry->height == height) {
382 renderbuffer = entry->id;
383 This->current_renderbuffer = entry;
384 break;
388 if (!renderbuffer) {
389 const GlPixelFormatDesc *glDesc;
390 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
392 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
393 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
394 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
396 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
397 entry->width = width;
398 entry->height = height;
399 entry->id = renderbuffer;
400 list_add_head(&This->renderbuffers, &entry->entry);
402 This->current_renderbuffer = entry;
405 checkGLcall("set_compatible_renderbuffer");
408 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
409 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
410 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
412 TRACE("(%p) : swapchain %p\n", This, swapchain);
414 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
415 TRACE("Returning GL_BACK\n");
416 return GL_BACK;
417 } else if (swapchain_impl->frontBuffer == iface) {
418 TRACE("Returning GL_FRONT\n");
419 return GL_FRONT;
422 FIXME("Higher back buffer, returning GL_BACK\n");
423 return GL_BACK;
426 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
427 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
428 ULONG ref = InterlockedDecrement(&This->resource.ref);
429 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
430 if (ref == 0) {
431 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
432 renderbuffer_entry_t *entry, *entry2;
433 TRACE("(%p) : cleaning up\n", This);
435 /* Need a context to destroy the texture. Use the currently active render target, but only if
436 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
437 * When destroying the primary rt, Uninit3D will activate a context before doing anything
439 if(device->render_targets && device->render_targets[0]) {
440 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
443 ENTER_GL();
444 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
445 TRACE("Deleting texture %d\n", This->glDescription.textureName);
446 glDeleteTextures(1, &This->glDescription.textureName);
449 if(This->Flags & SFLAG_PBO) {
450 /* Delete the PBO */
451 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
454 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
455 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
456 HeapFree(GetProcessHeap(), 0, entry);
458 LEAVE_GL();
460 if(This->Flags & SFLAG_DIBSECTION) {
461 /* Release the DC */
462 SelectObject(This->hDC, This->dib.holdbitmap);
463 DeleteDC(This->hDC);
464 /* Release the DIB section */
465 DeleteObject(This->dib.DIBsection);
466 This->dib.bitmap_data = NULL;
467 This->resource.allocatedMemory = NULL;
469 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
471 HeapFree(GetProcessHeap(), 0, This->palette9);
473 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
474 if(iface == device->ddraw_primary)
475 device->ddraw_primary = NULL;
477 if(This->overlay_dest) {
478 list_remove(&This->overlay_entry);
481 TRACE("(%p) Released\n", This);
482 HeapFree(GetProcessHeap(), 0, This);
485 return ref;
488 /* ****************************************************
489 IWineD3DSurface IWineD3DResource parts follow
490 **************************************************** */
492 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
493 /* TODO: check for locks */
494 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
495 IWineD3DBaseTexture *baseTexture = NULL;
496 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
498 TRACE("(%p)Checking to see if the container is a base texture\n", This);
499 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
500 TRACE("Passing to container\n");
501 IWineD3DBaseTexture_PreLoad(baseTexture);
502 IWineD3DBaseTexture_Release(baseTexture);
503 } else {
504 TRACE("(%p) : About to load surface\n", This);
506 if(!device->isInDraw) {
507 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
510 if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8) {
511 if(palette9_changed(This)) {
512 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
513 /* TODO: This is not necessarily needed with hw palettized texture support */
514 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
515 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
516 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
519 ENTER_GL();
520 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
521 if (!This->glDescription.level) {
522 if (!This->glDescription.textureName) {
523 glGenTextures(1, &This->glDescription.textureName);
524 checkGLcall("glGenTextures");
525 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
527 glBindTexture(This->glDescription.target, This->glDescription.textureName);
528 checkGLcall("glBindTexture");
529 LEAVE_GL();
530 IWineD3DSurface_LoadTexture(iface, FALSE);
531 /* This is where we should be reducing the amount of GLMemoryUsed */
532 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
533 /* assume this is a coding error not a real error for now */
534 FIXME("Mipmap surface has a glTexture bound to it!\n");
535 LEAVE_GL();
537 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
538 /* Tell opengl to try and keep this texture in video ram (well mostly) */
539 GLclampf tmp;
540 tmp = 0.9f;
541 ENTER_GL();
542 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
543 LEAVE_GL();
546 return;
549 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
550 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
551 This->resource.allocatedMemory =
552 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
554 ENTER_GL();
555 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
556 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
557 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
558 checkGLcall("glGetBufferSubData");
559 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
560 checkGLcall("glDeleteBuffers");
561 LEAVE_GL();
563 This->pbo = 0;
564 This->Flags &= ~SFLAG_PBO;
567 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
568 IWineD3DBaseTexture *texture = NULL;
569 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
570 renderbuffer_entry_t *entry, *entry2;
571 TRACE("(%p)\n", iface);
573 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
574 /* Default pool resources are supposed to be destroyed before Reset is called.
575 * Implicit resources stay however. So this means we have an implicit render target
576 * or depth stencil. The content may be destroyed, but we still have to tear down
577 * opengl resources, so we cannot leave early.
579 * Put the most up to date surface location into the drawable. D3D-wise this content
580 * is undefined, so it would be nowhere, but that would make the location management
581 * more complicated. The drawable is a sane location, because if we mark sysmem or
582 * texture up to date, drawPrim will copy the uninitialized texture or sysmem to the
583 * uninitialized drawable. That's pointless and we'd have to allocate the texture /
584 * sysmem copy here.
586 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
587 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
588 } else {
589 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
591 } else {
592 /* Load the surface into system memory */
593 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
594 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
596 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
597 This->Flags &= ~SFLAG_ALLOCATED;
599 /* Destroy PBOs, but load them into real sysmem before */
600 if(This->Flags & SFLAG_PBO) {
601 surface_remove_pbo(This);
604 /* Destroy fbo render buffers. This is needed for implicit render targets, for
605 * all application-created targets the application has to release the surface
606 * before calling _Reset
608 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
609 ENTER_GL();
610 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
611 LEAVE_GL();
612 list_remove(&entry->entry);
613 HeapFree(GetProcessHeap(), 0, entry);
615 list_init(&This->renderbuffers);
616 This->current_renderbuffer = NULL;
618 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
619 * destroy it
621 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
622 if(!texture) {
623 ENTER_GL();
624 glDeleteTextures(1, &This->glDescription.textureName);
625 This->glDescription.textureName = 0;
626 LEAVE_GL();
627 } else {
628 IWineD3DBaseTexture_Release(texture);
630 return;
633 /* ******************************************************
634 IWineD3DSurface IWineD3DSurface parts follow
635 ****************************************************** */
637 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
638 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
639 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
640 if (This->glDescription.textureName == 0 && textureName != 0) {
641 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
642 if((This->Flags & SFLAG_LOCATIONS) == 0) {
643 /* In 1.0-rc4 and earlier, AddDirtyRect was called in the place of this if condition.
644 * This had the problem that a correctly set INDRAWABLE flag was removed if the PreLoad
645 * during the offscreen rendering readback triggered the creation of the GL texture.
646 * The change intended to keep the INDRAWABLE intact. To prevent unintended side effects
647 * before release, set the INSYSMEM flag like the old AddDirtyRect did.
649 WARN("Wine 1.0 safety path hit\n");
650 This->Flags |= SFLAG_INSYSMEM;
653 if(target == GL_TEXTURE_RECTANGLE_ARB && This->glDescription.target != target) {
654 This->Flags &= ~SFLAG_NORMCOORD;
655 } else if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB && target != GL_TEXTURE_RECTANGLE_ARB) {
656 This->Flags |= SFLAG_NORMCOORD;
658 This->glDescription.textureName = textureName;
659 This->glDescription.target = target;
660 This->Flags &= ~SFLAG_ALLOCATED;
663 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
664 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
665 TRACE("(%p) : returning %p\n", This, &This->glDescription);
666 *glDescription = &This->glDescription;
669 /* TODO: think about moving this down to resource? */
670 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
671 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
672 /* 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 */
673 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
674 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
676 return (CONST void*)(This->resource.allocatedMemory);
679 /* Read the framebuffer back into the surface */
680 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
681 IWineD3DSwapChainImpl *swapchain;
682 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
683 BYTE *mem;
684 GLint fmt;
685 GLint type;
686 BYTE *row, *top, *bottom;
687 int i;
688 BOOL bpp;
689 RECT local_rect;
690 BOOL srcIsUpsideDown;
692 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
693 static BOOL warned = FALSE;
694 if(!warned) {
695 ERR("The application tries to lock the render target, but render target locking is disabled\n");
696 warned = TRUE;
698 return;
701 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
702 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
703 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
704 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
705 * context->last_was_blit set on the unlock.
707 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
708 ENTER_GL();
710 /* Select the correct read buffer, and give some debug output.
711 * There is no need to keep track of the current read buffer or reset it, every part of the code
712 * that reads sets the read buffer as desired.
714 if(!swapchain) {
715 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
716 * Read from the back buffer
718 TRACE("Locking offscreen render target\n");
719 glReadBuffer(myDevice->offscreenBuffer);
720 srcIsUpsideDown = TRUE;
721 } else {
722 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
723 TRACE("Locking %#x buffer\n", buffer);
724 glReadBuffer(buffer);
725 checkGLcall("glReadBuffer");
727 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
728 srcIsUpsideDown = FALSE;
731 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
732 if(!rect) {
733 local_rect.left = 0;
734 local_rect.top = 0;
735 local_rect.right = This->currentDesc.Width;
736 local_rect.bottom = This->currentDesc.Height;
737 } else {
738 local_rect = *rect;
740 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
742 switch(This->resource.format)
744 case WINED3DFMT_P8:
746 if(primary_render_target_is_p8(myDevice)) {
747 /* In case of P8 render targets the index is stored in the alpha component */
748 fmt = GL_ALPHA;
749 type = GL_UNSIGNED_BYTE;
750 mem = dest;
751 bpp = This->bytesPerPixel;
752 } else {
753 /* GL can't return palettized data, so read ARGB pixels into a
754 * separate block of memory and convert them into palettized format
755 * in software. Slow, but if the app means to use palettized render
756 * targets and locks it...
758 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
759 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
760 * for the color channels when palettizing the colors.
762 fmt = GL_RGB;
763 type = GL_UNSIGNED_BYTE;
764 pitch *= 3;
765 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
766 if(!mem) {
767 ERR("Out of memory\n");
768 LEAVE_GL();
769 return;
771 bpp = This->bytesPerPixel * 3;
774 break;
776 default:
777 mem = dest;
778 fmt = This->glDescription.glFormat;
779 type = This->glDescription.glType;
780 bpp = This->bytesPerPixel;
783 if(This->Flags & SFLAG_PBO) {
784 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
785 checkGLcall("glBindBufferARB");
788 glReadPixels(local_rect.left, local_rect.top,
789 local_rect.right - local_rect.left,
790 local_rect.bottom - local_rect.top,
791 fmt, type, mem);
792 vcheckGLcall("glReadPixels");
794 if(This->Flags & SFLAG_PBO) {
795 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
796 checkGLcall("glBindBufferARB");
798 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
799 * to get a pointer to it and perform the flipping in software. This is a lot
800 * faster than calling glReadPixels for each line. In case we want more speed
801 * we should rerender it flipped in a FBO and read the data back from the FBO. */
802 if(!srcIsUpsideDown) {
803 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
804 checkGLcall("glBindBufferARB");
806 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
807 checkGLcall("glMapBufferARB");
811 /* TODO: Merge this with the palettization loop below for P8 targets */
812 if(!srcIsUpsideDown) {
813 UINT len, off;
814 /* glReadPixels returns the image upside down, and there is no way to prevent this.
815 Flip the lines in software */
816 len = (local_rect.right - local_rect.left) * bpp;
817 off = local_rect.left * bpp;
819 row = HeapAlloc(GetProcessHeap(), 0, len);
820 if(!row) {
821 ERR("Out of memory\n");
822 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
823 LEAVE_GL();
824 return;
827 top = mem + pitch * local_rect.top;
828 bottom = mem + pitch * ( local_rect.bottom - local_rect.top - 1);
829 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
830 memcpy(row, top + off, len);
831 memcpy(top + off, bottom + off, len);
832 memcpy(bottom + off, row, len);
833 top += pitch;
834 bottom -= pitch;
836 HeapFree(GetProcessHeap(), 0, row);
838 /* Unmap the temp PBO buffer */
839 if(This->Flags & SFLAG_PBO) {
840 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
841 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
845 LEAVE_GL();
847 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
848 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
849 * the same color but we have no choice.
850 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
852 if((This->resource.format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice)) {
853 PALETTEENTRY *pal = NULL;
854 DWORD width = pitch / 3;
855 int x, y, c;
857 if(This->palette) {
858 pal = This->palette->palents;
859 } else {
860 ERR("Palette is missing, cannot perform inverse palette lookup\n");
861 HeapFree(GetProcessHeap(), 0, mem);
862 return ;
865 for(y = local_rect.top; y < local_rect.bottom; y++) {
866 for(x = local_rect.left; x < local_rect.right; x++) {
867 /* start lines pixels */
868 BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
869 BYTE *green = blue + 1;
870 BYTE *red = green + 1;
872 for(c = 0; c < 256; c++) {
873 if(*red == pal[c].peRed &&
874 *green == pal[c].peGreen &&
875 *blue == pal[c].peBlue)
877 *((BYTE *) dest + y * width + x) = c;
878 break;
883 HeapFree(GetProcessHeap(), 0, mem);
887 /* Read the framebuffer contents into a texture */
888 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This)
890 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
891 IWineD3DSwapChainImpl *swapchain;
892 int bpp;
893 GLenum format, internal, type;
894 CONVERT_TYPES convert;
895 GLint prevRead;
897 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
899 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
900 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
901 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
902 * states in the stateblock, and no driver was found yet that had bugs in that regard.
904 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
905 surface_bind_and_dirtify(This);
906 ENTER_GL();
908 glGetIntegerv(GL_READ_BUFFER, &prevRead);
910 /* Select the correct read buffer, and give some debug output.
911 * There is no need to keep track of the current read buffer or reset it, every part of the code
912 * that reads sets the read buffer as desired.
914 if(!swapchain) {
915 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
916 * Read from the back buffer
918 TRACE("Locking offscreen render target\n");
919 glReadBuffer(device->offscreenBuffer);
920 } else {
921 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
922 TRACE("Locking %#x buffer\n", buffer);
923 glReadBuffer(buffer);
924 checkGLcall("glReadBuffer");
926 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
929 if(!(This->Flags & SFLAG_ALLOCATED)) {
930 surface_allocate_surface(This, internal, This->pow2Width,
931 This->pow2Height, format, type);
934 clear_unused_channels(This);
936 /* If !SrcIsUpsideDown we should flip the surface.
937 * This can be done using glCopyTexSubImage2D but this
938 * is VERY slow, so don't do that. We should prevent
939 * this code from getting called in such cases or perhaps
940 * we can use FBOs */
942 glCopyTexSubImage2D(This->glDescription.target,
943 This->glDescription.level,
944 0, 0, 0, 0,
945 This->currentDesc.Width,
946 This->currentDesc.Height);
947 checkGLcall("glCopyTexSubImage2D");
949 glReadBuffer(prevRead);
950 vcheckGLcall("glReadBuffer");
952 LEAVE_GL();
953 TRACE("Updated target %d\n", This->glDescription.target);
956 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
957 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
958 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
959 * changed
961 if(!(This->Flags & SFLAG_DYNLOCK)) {
962 This->lockCount++;
963 /* MAXLOCKCOUNT is defined in wined3d_private.h */
964 if(This->lockCount > MAXLOCKCOUNT) {
965 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
966 This->Flags |= SFLAG_DYNLOCK;
970 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
971 * Also don't create a PBO for systemmem surfaces.
973 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
974 GLenum error;
975 ENTER_GL();
977 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
978 error = glGetError();
979 if(This->pbo == 0 || error != GL_NO_ERROR) {
980 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
983 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
985 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
986 checkGLcall("glBindBufferARB");
988 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
989 checkGLcall("glBufferDataARB");
991 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
992 checkGLcall("glBindBufferARB");
994 /* We don't need the system memory anymore and we can't even use it for PBOs */
995 if(!(This->Flags & SFLAG_CLIENT)) {
996 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
997 This->resource.heapMemory = NULL;
999 This->resource.allocatedMemory = NULL;
1000 This->Flags |= SFLAG_PBO;
1001 LEAVE_GL();
1002 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
1003 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1004 * or a pbo to map
1006 if(!This->resource.heapMemory) {
1007 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1009 This->resource.allocatedMemory =
1010 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1011 if(This->Flags & SFLAG_INSYSMEM) {
1012 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1017 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1018 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1019 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1020 IWineD3DSwapChain *swapchain = NULL;
1022 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1024 /* This is also done in the base class, but we have to verify this before loading any data from
1025 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1026 * may interfere, and all other bad things may happen
1028 if (This->Flags & SFLAG_LOCKED) {
1029 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1030 return WINED3DERR_INVALIDCALL;
1032 This->Flags |= SFLAG_LOCKED;
1034 if (!(This->Flags & SFLAG_LOCKABLE))
1036 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1039 if (Flags & WINED3DLOCK_DISCARD) {
1040 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1041 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1042 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1043 This->Flags |= SFLAG_INSYSMEM;
1044 goto lock_end;
1047 if (This->Flags & SFLAG_INSYSMEM) {
1048 TRACE("Local copy is up to date, not downloading data\n");
1049 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1050 goto lock_end;
1053 /* Now download the surface content from opengl
1054 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1055 * Offscreen targets which are not active at the moment or are higher targets(FBOs) can be locked with the texture path
1057 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1058 if(swapchain || iface == myDevice->render_targets[0]) {
1059 const RECT *pass_rect = pRect;
1061 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1062 * because most caller functions do not need that. So do that here
1064 if(pRect &&
1065 pRect->top == 0 &&
1066 pRect->left == 0 &&
1067 pRect->right == This->currentDesc.Width &&
1068 pRect->bottom == This->currentDesc.Height) {
1069 pass_rect = NULL;
1072 switch(wined3d_settings.rendertargetlock_mode) {
1073 case RTL_TEXDRAW:
1074 case RTL_TEXTEX:
1075 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1076 #if 0
1077 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1078 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1079 * This may be faster on some cards
1081 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1082 #endif
1083 /* drop through */
1085 case RTL_AUTO:
1086 case RTL_READDRAW:
1087 case RTL_READTEX:
1088 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
1089 break;
1091 case RTL_DISABLE:
1092 break;
1094 if(swapchain) IWineD3DSwapChain_Release(swapchain);
1096 } else if(iface == myDevice->stencilBufferTarget) {
1097 /** the depth stencil in openGL has a format of GL_FLOAT
1098 * which should be good for WINED3DFMT_D16_LOCKABLE
1099 * and WINED3DFMT_D16
1100 * it is unclear what format the stencil buffer is in except.
1101 * 'Each index is converted to fixed point...
1102 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1103 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1104 * glReadPixels(This->lockedRect.left,
1105 * This->lockedRect.bottom - j - 1,
1106 * This->lockedRect.right - This->lockedRect.left,
1107 * 1,
1108 * GL_DEPTH_COMPONENT,
1109 * type,
1110 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1112 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1113 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1114 * none of that is the case the problem is not in this function :-)
1115 ********************************************/
1116 FIXME("Depth stencil locking not supported yet\n");
1117 } else {
1118 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1119 TRACE("locking an ordinary surface\n");
1120 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1123 lock_end:
1124 if(This->Flags & SFLAG_PBO) {
1125 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1126 ENTER_GL();
1127 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1128 checkGLcall("glBindBufferARB");
1130 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1131 if(This->resource.allocatedMemory) {
1132 ERR("The surface already has PBO memory allocated!\n");
1135 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1136 checkGLcall("glMapBufferARB");
1138 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1139 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1140 checkGLcall("glBindBufferARB");
1142 LEAVE_GL();
1145 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1146 /* Don't dirtify */
1147 } else {
1148 IWineD3DBaseTexture *pBaseTexture;
1150 * Dirtify on lock
1151 * as seen in msdn docs
1153 IWineD3DSurface_AddDirtyRect(iface, pRect);
1155 /** Dirtify Container if needed */
1156 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
1157 TRACE("Making container dirty\n");
1158 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1159 IWineD3DBaseTexture_Release(pBaseTexture);
1160 } else {
1161 TRACE("Surface is standalone, no need to dirty the container\n");
1165 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1168 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1169 GLint prev_store;
1170 GLint prev_rasterpos[4];
1171 GLint skipBytes = 0;
1172 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1173 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1174 IWineD3DSwapChainImpl *swapchain;
1176 /* Activate the correct context for the render target */
1177 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1178 ENTER_GL();
1180 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
1181 if(!swapchain) {
1182 /* Primary offscreen render target */
1183 TRACE("Offscreen render target\n");
1184 glDrawBuffer(myDevice->offscreenBuffer);
1185 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1186 } else {
1187 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1188 TRACE("Unlocking %#x buffer\n", buffer);
1189 glDrawBuffer(buffer);
1190 checkGLcall("glDrawBuffer");
1192 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1195 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1196 vcheckGLcall("glIntegerv");
1197 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1198 vcheckGLcall("glIntegerv");
1199 glPixelZoom(1.0, -1.0);
1200 vcheckGLcall("glPixelZoom");
1202 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1203 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1204 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1206 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1207 vcheckGLcall("glRasterPos2f");
1209 /* Some drivers(radeon dri, others?) don't like exceptions during
1210 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1211 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1212 * catch to put the dib section in InSync mode, which leads to a crash
1213 * and a blocked x server on my radeon card.
1215 * The following lines read the dib section so it is put in InSync mode
1216 * before glDrawPixels is called and the crash is prevented. There won't
1217 * be any interfering gdi accesses, because UnlockRect is called from
1218 * ReleaseDC, and the app won't use the dc any more afterwards.
1220 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1221 volatile BYTE read;
1222 read = This->resource.allocatedMemory[0];
1225 if(This->Flags & SFLAG_PBO) {
1226 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1227 checkGLcall("glBindBufferARB");
1230 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1231 if(This->Flags & SFLAG_LOCKED) {
1232 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1233 (This->lockedRect.bottom - This->lockedRect.top)-1,
1234 fmt, type,
1235 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1236 checkGLcall("glDrawPixels");
1237 } else {
1238 glDrawPixels(This->currentDesc.Width,
1239 This->currentDesc.Height,
1240 fmt, type, mem);
1241 checkGLcall("glDrawPixels");
1244 if(This->Flags & SFLAG_PBO) {
1245 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1246 checkGLcall("glBindBufferARB");
1249 glPixelZoom(1.0,1.0);
1250 vcheckGLcall("glPixelZoom");
1252 glRasterPos3iv(&prev_rasterpos[0]);
1253 vcheckGLcall("glRasterPos3iv");
1255 /* Reset to previous pack row length */
1256 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1257 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1259 if(!swapchain) {
1260 glDrawBuffer(myDevice->offscreenBuffer);
1261 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1262 } else if(swapchain->backBuffer) {
1263 glDrawBuffer(GL_BACK);
1264 checkGLcall("glDrawBuffer(GL_BACK)");
1265 } else {
1266 glDrawBuffer(GL_FRONT);
1267 checkGLcall("glDrawBuffer(GL_FRONT)");
1269 LEAVE_GL();
1271 return;
1274 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1275 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1276 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1277 IWineD3DSwapChainImpl *swapchain = NULL;
1278 BOOL fullsurface;
1280 if (!(This->Flags & SFLAG_LOCKED)) {
1281 WARN("trying to Unlock an unlocked surf@%p\n", This);
1282 return WINED3DERR_INVALIDCALL;
1285 if (This->Flags & SFLAG_PBO) {
1286 TRACE("Freeing PBO memory\n");
1287 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1288 ENTER_GL();
1289 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1290 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1291 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1292 checkGLcall("glUnmapBufferARB");
1293 LEAVE_GL();
1294 This->resource.allocatedMemory = NULL;
1297 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1299 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1300 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1301 goto unlock_end;
1304 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1305 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1306 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1308 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1309 static BOOL warned = FALSE;
1310 if(!warned) {
1311 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1312 warned = TRUE;
1314 goto unlock_end;
1317 if(This->dirtyRect.left == 0 &&
1318 This->dirtyRect.top == 0 &&
1319 This->dirtyRect.right == This->currentDesc.Width &&
1320 This->dirtyRect.bottom == This->currentDesc.Height) {
1321 fullsurface = TRUE;
1322 } else {
1323 /* TODO: Proper partial rectangle tracking */
1324 fullsurface = FALSE;
1325 This->Flags |= SFLAG_INSYSMEM;
1328 switch(wined3d_settings.rendertargetlock_mode) {
1329 case RTL_READTEX:
1330 case RTL_TEXTEX:
1331 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1332 ENTER_GL();
1333 if (This->glDescription.textureName == 0) {
1334 glGenTextures(1, &This->glDescription.textureName);
1335 checkGLcall("glGenTextures");
1337 glBindTexture(This->glDescription.target, This->glDescription.textureName);
1338 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
1339 LEAVE_GL();
1340 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1341 /* drop through */
1343 case RTL_AUTO:
1344 case RTL_READDRAW:
1345 case RTL_TEXDRAW:
1346 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1347 break;
1350 if(!fullsurface) {
1351 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1352 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1353 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1354 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1355 * not fully up to date because only a subrectangle was read in LockRect.
1357 This->Flags &= ~SFLAG_INSYSMEM;
1358 This->Flags |= SFLAG_INDRAWABLE;
1361 This->dirtyRect.left = This->currentDesc.Width;
1362 This->dirtyRect.top = This->currentDesc.Height;
1363 This->dirtyRect.right = 0;
1364 This->dirtyRect.bottom = 0;
1365 } else if(iface == myDevice->stencilBufferTarget) {
1366 FIXME("Depth Stencil buffer locking is not implemented\n");
1367 } else {
1368 /* The rest should be a normal texture */
1369 IWineD3DBaseTextureImpl *impl;
1370 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1371 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1372 * states need resetting
1374 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1375 if(impl->baseTexture.bindCount) {
1376 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1378 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1382 unlock_end:
1383 This->Flags &= ~SFLAG_LOCKED;
1384 memset(&This->lockedRect, 0, sizeof(RECT));
1386 /* Overlays have to be redrawn manually after changes with the GL implementation */
1387 if(This->overlay_dest) {
1388 IWineD3DSurface_DrawOverlay(iface);
1390 return WINED3D_OK;
1393 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1394 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1395 WINED3DLOCKED_RECT lock;
1396 HRESULT hr;
1397 RGBQUAD col[256];
1399 TRACE("(%p)->(%p)\n",This,pHDC);
1401 if(This->Flags & SFLAG_USERPTR) {
1402 ERR("Not supported on surfaces with an application-provided surfaces\n");
1403 return WINEDDERR_NODC;
1406 /* Give more detailed info for ddraw */
1407 if (This->Flags & SFLAG_DCINUSE)
1408 return WINEDDERR_DCALREADYCREATED;
1410 /* Can't GetDC if the surface is locked */
1411 if (This->Flags & SFLAG_LOCKED)
1412 return WINED3DERR_INVALIDCALL;
1414 /* According to Direct3D9 docs, only these formats are supported */
1415 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1416 if (This->resource.format != WINED3DFMT_R5G6B5 &&
1417 This->resource.format != WINED3DFMT_X1R5G5B5 &&
1418 This->resource.format != WINED3DFMT_R8G8B8 &&
1419 This->resource.format != WINED3DFMT_X8R8G8B8) return WINED3DERR_INVALIDCALL;
1422 memset(&lock, 0, sizeof(lock)); /* To be sure */
1424 /* Create a DIB section if there isn't a hdc yet */
1425 if(!This->hDC) {
1426 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1427 if(This->Flags & SFLAG_CLIENT) {
1428 IWineD3DSurface_PreLoad(iface);
1431 /* Use the dib section from now on if we are not using a PBO */
1432 if(!(This->Flags & SFLAG_PBO))
1433 This->resource.allocatedMemory = This->dib.bitmap_data;
1436 /* Lock the surface */
1437 hr = IWineD3DSurface_LockRect(iface,
1438 &lock,
1439 NULL,
1442 if(This->Flags & SFLAG_PBO) {
1443 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1444 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1447 if(FAILED(hr)) {
1448 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1449 /* keep the dib section */
1450 return hr;
1453 if(This->resource.format == WINED3DFMT_P8 ||
1454 This->resource.format == WINED3DFMT_A8P8) {
1455 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1456 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1457 unsigned int n;
1458 PALETTEENTRY *pal = NULL;
1460 if(This->palette) {
1461 pal = This->palette->palents;
1462 } else {
1463 IWineD3DSurfaceImpl *dds_primary = (IWineD3DSurfaceImpl *)This->resource.wineD3DDevice->ddraw_primary;
1464 if (dds_primary && dds_primary->palette)
1465 pal = dds_primary->palette->palents;
1468 if (pal) {
1469 for (n=0; n<256; n++) {
1470 col[n].rgbRed = pal[n].peRed;
1471 col[n].rgbGreen = pal[n].peGreen;
1472 col[n].rgbBlue = pal[n].peBlue;
1473 col[n].rgbReserved = 0;
1475 SetDIBColorTable(This->hDC, 0, 256, col);
1479 *pHDC = This->hDC;
1480 TRACE("returning %p\n",*pHDC);
1481 This->Flags |= SFLAG_DCINUSE;
1483 return WINED3D_OK;
1486 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1487 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1489 TRACE("(%p)->(%p)\n",This,hDC);
1491 if (!(This->Flags & SFLAG_DCINUSE))
1492 return WINED3DERR_INVALIDCALL;
1494 if (This->hDC !=hDC) {
1495 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1496 return WINED3DERR_INVALIDCALL;
1499 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1500 /* Copy the contents of the DIB over to the PBO */
1501 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1504 /* we locked first, so unlock now */
1505 IWineD3DSurface_UnlockRect(iface);
1507 This->Flags &= ~SFLAG_DCINUSE;
1509 return WINED3D_OK;
1512 /* ******************************************************
1513 IWineD3DSurface Internal (No mapping to directx api) parts follow
1514 ****************************************************** */
1516 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) {
1517 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1518 const GlPixelFormatDesc *glDesc;
1519 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1520 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1522 /* Default values: From the surface */
1523 *format = glDesc->glFormat;
1524 *type = glDesc->glType;
1525 *convert = NO_CONVERSION;
1526 *target_bpp = This->bytesPerPixel;
1528 if(srgb_mode) {
1529 *internal = glDesc->glGammaInternal;
1530 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1531 *internal = glDesc->rtInternal;
1532 } else {
1533 *internal = glDesc->glInternal;
1536 /* Ok, now look if we have to do any conversion */
1537 switch(This->resource.format) {
1538 case WINED3DFMT_P8:
1539 /* ****************
1540 Paletted Texture
1541 **************** */
1543 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1544 * of the two is available make sure texturing is requested as neither of the two works in
1545 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1546 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1547 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1548 * conflicts with this.
1550 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) && primary_render_target_is_p8(device))) || colorkey_active || !use_texturing ) {
1551 *format = GL_RGBA;
1552 *internal = GL_RGBA;
1553 *type = GL_UNSIGNED_BYTE;
1554 *target_bpp = 4;
1555 if(colorkey_active) {
1556 *convert = CONVERT_PALETTED_CK;
1557 } else {
1558 *convert = CONVERT_PALETTED;
1561 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1562 *format = GL_ALPHA;
1563 *internal = GL_RGBA;
1564 *type = GL_UNSIGNED_BYTE;
1565 *target_bpp = 1;
1568 break;
1570 case WINED3DFMT_R3G3B2:
1571 /* **********************
1572 GL_UNSIGNED_BYTE_3_3_2
1573 ********************** */
1574 if (colorkey_active) {
1575 /* This texture format will never be used.. So do not care about color keying
1576 up until the point in time it will be needed :-) */
1577 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1579 break;
1581 case WINED3DFMT_R5G6B5:
1582 if (colorkey_active) {
1583 *convert = CONVERT_CK_565;
1584 *format = GL_RGBA;
1585 *internal = GL_RGBA;
1586 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1588 break;
1590 case WINED3DFMT_X1R5G5B5:
1591 if (colorkey_active) {
1592 *convert = CONVERT_CK_5551;
1593 *format = GL_BGRA;
1594 *internal = GL_RGBA;
1595 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1597 break;
1599 case WINED3DFMT_R8G8B8:
1600 if (colorkey_active) {
1601 *convert = CONVERT_CK_RGB24;
1602 *format = GL_RGBA;
1603 *internal = GL_RGBA;
1604 *type = GL_UNSIGNED_INT_8_8_8_8;
1605 *target_bpp = 4;
1607 break;
1609 case WINED3DFMT_X8R8G8B8:
1610 if (colorkey_active) {
1611 *convert = CONVERT_RGB32_888;
1612 *format = GL_RGBA;
1613 *internal = GL_RGBA;
1614 *type = GL_UNSIGNED_INT_8_8_8_8;
1616 break;
1618 case WINED3DFMT_V8U8:
1619 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1620 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1621 *format = GL_DUDV_ATI;
1622 *internal = GL_DU8DV8_ATI;
1623 *type = GL_BYTE;
1624 /* No conversion - Just change the gl type */
1625 break;
1627 *convert = CONVERT_V8U8;
1628 *format = GL_BGR;
1629 *internal = GL_RGB8;
1630 *type = GL_UNSIGNED_BYTE;
1631 *target_bpp = 3;
1632 break;
1634 case WINED3DFMT_L6V5U5:
1635 *convert = CONVERT_L6V5U5;
1636 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1637 *target_bpp = 3;
1638 /* Use format and types from table */
1639 } else {
1640 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1641 *target_bpp = 2;
1642 *format = GL_RGB;
1643 *internal = GL_RGB5;
1644 *type = GL_UNSIGNED_SHORT_5_6_5;
1646 break;
1648 case WINED3DFMT_X8L8V8U8:
1649 *convert = CONVERT_X8L8V8U8;
1650 *target_bpp = 4;
1651 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1652 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1653 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1654 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1655 * the needed type and format parameter, so the internal format contains a
1656 * 4th component, which is returned as alpha
1658 } else {
1659 /* Not supported by GL_ATI_envmap_bumpmap */
1660 *format = GL_BGRA;
1661 *internal = GL_RGB8;
1662 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1664 break;
1666 case WINED3DFMT_Q8W8V8U8:
1667 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1668 *convert = CONVERT_Q8W8V8U8;
1669 *format = GL_BGRA;
1670 *internal = GL_RGBA8;
1671 *type = GL_UNSIGNED_BYTE;
1672 *target_bpp = 4;
1673 /* Not supported by GL_ATI_envmap_bumpmap */
1674 break;
1676 case WINED3DFMT_V16U16:
1677 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1678 *convert = CONVERT_V16U16;
1679 *format = GL_BGR;
1680 *internal = GL_RGB16_EXT;
1681 *type = GL_UNSIGNED_SHORT;
1682 *target_bpp = 6;
1683 /* What should I do here about GL_ATI_envmap_bumpmap?
1684 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1686 break;
1688 case WINED3DFMT_A4L4:
1689 /* A4L4 exists as an internal gl format, but for some reason there is not
1690 * format+type combination to load it. Thus convert it to A8L8, then load it
1691 * with A4L4 internal, but A8L8 format+type
1693 *convert = CONVERT_A4L4;
1694 *format = GL_LUMINANCE_ALPHA;
1695 *internal = GL_LUMINANCE4_ALPHA4;
1696 *type = GL_UNSIGNED_BYTE;
1697 *target_bpp = 2;
1698 break;
1700 case WINED3DFMT_R32F:
1701 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1702 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1703 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1704 * 1.0 instead.
1706 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1708 *convert = CONVERT_R32F;
1709 *format = GL_RGB;
1710 *internal = GL_RGB32F_ARB;
1711 *type = GL_FLOAT;
1712 *target_bpp = 12;
1713 break;
1715 case WINED3DFMT_R16F:
1716 /* Similar to R32F */
1717 *convert = CONVERT_R16F;
1718 *format = GL_RGB;
1719 *internal = GL_RGB16F_ARB;
1720 *type = GL_HALF_FLOAT_ARB;
1721 *target_bpp = 6;
1722 break;
1724 case WINED3DFMT_G16R16:
1725 *convert = CONVERT_G16R16;
1726 *format = GL_RGB;
1727 *internal = GL_RGB16_EXT;
1728 *type = GL_UNSIGNED_SHORT;
1729 *target_bpp = 6;
1730 break;
1732 default:
1733 break;
1736 return WINED3D_OK;
1739 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1740 BYTE *source, *dest;
1741 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1743 switch (convert) {
1744 case NO_CONVERSION:
1746 memcpy(dst, src, pitch * height);
1747 break;
1749 case CONVERT_PALETTED:
1750 case CONVERT_PALETTED_CK:
1752 IWineD3DPaletteImpl* pal = This->palette;
1753 BYTE table[256][4];
1754 unsigned int x, y;
1756 if( pal == NULL) {
1757 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1760 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1762 for (y = 0; y < height; y++)
1764 source = src + pitch * y;
1765 dest = dst + outpitch * y;
1766 /* This is an 1 bpp format, using the width here is fine */
1767 for (x = 0; x < width; x++) {
1768 BYTE color = *source++;
1769 *dest++ = table[color][0];
1770 *dest++ = table[color][1];
1771 *dest++ = table[color][2];
1772 *dest++ = table[color][3];
1776 break;
1778 case CONVERT_CK_565:
1780 /* Converting the 565 format in 5551 packed to emulate color-keying.
1782 Note : in all these conversion, it would be best to average the averaging
1783 pixels to get the color of the pixel that will be color-keyed to
1784 prevent 'color bleeding'. This will be done later on if ever it is
1785 too visible.
1787 Note2: Nvidia documents say that their driver does not support alpha + color keying
1788 on the same surface and disables color keying in such a case
1790 unsigned int x, y;
1791 WORD *Source;
1792 WORD *Dest;
1794 TRACE("Color keyed 565\n");
1796 for (y = 0; y < height; y++) {
1797 Source = (WORD *) (src + y * pitch);
1798 Dest = (WORD *) (dst + y * outpitch);
1799 for (x = 0; x < width; x++ ) {
1800 WORD color = *Source++;
1801 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1802 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1803 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1804 *Dest |= 0x0001;
1806 Dest++;
1810 break;
1812 case CONVERT_CK_5551:
1814 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1815 unsigned int x, y;
1816 WORD *Source;
1817 WORD *Dest;
1818 TRACE("Color keyed 5551\n");
1819 for (y = 0; y < height; y++) {
1820 Source = (WORD *) (src + y * pitch);
1821 Dest = (WORD *) (dst + y * outpitch);
1822 for (x = 0; x < width; x++ ) {
1823 WORD color = *Source++;
1824 *Dest = color;
1825 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1826 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1827 *Dest |= (1 << 15);
1829 else {
1830 *Dest &= ~(1 << 15);
1832 Dest++;
1836 break;
1838 case CONVERT_RGB32_888:
1840 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
1841 unsigned int x, y;
1842 for (y = 0; y < height; y++)
1844 source = src + pitch * y;
1845 dest = dst + outpitch * y;
1846 for (x = 0; x < width; x++) {
1847 DWORD color = 0xffffff & *(DWORD*)source;
1848 DWORD dstcolor = color << 8;
1849 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1850 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1851 dstcolor |= 0xff;
1853 *(DWORD*)dest = dstcolor;
1854 source += 4;
1855 dest += 4;
1859 break;
1861 case CONVERT_V8U8:
1863 unsigned int x, y;
1864 short *Source;
1865 unsigned char *Dest;
1866 for(y = 0; y < height; y++) {
1867 Source = (short *) (src + y * pitch);
1868 Dest = dst + y * outpitch;
1869 for (x = 0; x < width; x++ ) {
1870 long color = (*Source++);
1871 /* B */ Dest[0] = 0xff;
1872 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1873 /* R */ Dest[2] = (color) + 128; /* U */
1874 Dest += 3;
1877 break;
1880 case CONVERT_V16U16:
1882 unsigned int x, y;
1883 DWORD *Source;
1884 unsigned short *Dest;
1885 for(y = 0; y < height; y++) {
1886 Source = (DWORD *) (src + y * pitch);
1887 Dest = (unsigned short *) (dst + y * outpitch);
1888 for (x = 0; x < width; x++ ) {
1889 DWORD color = (*Source++);
1890 /* B */ Dest[0] = 0xffff;
1891 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1892 /* R */ Dest[2] = (color ) + 32768; /* U */
1893 Dest += 3;
1896 break;
1899 case CONVERT_Q8W8V8U8:
1901 unsigned int x, y;
1902 DWORD *Source;
1903 unsigned char *Dest;
1904 for(y = 0; y < height; y++) {
1905 Source = (DWORD *) (src + y * pitch);
1906 Dest = dst + y * outpitch;
1907 for (x = 0; x < width; x++ ) {
1908 long color = (*Source++);
1909 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1910 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1911 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1912 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1913 Dest += 4;
1916 break;
1919 case CONVERT_L6V5U5:
1921 unsigned int x, y;
1922 WORD *Source;
1923 unsigned char *Dest;
1925 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1926 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1927 * fixed function and shaders without further conversion once the surface is
1928 * loaded
1930 for(y = 0; y < height; y++) {
1931 Source = (WORD *) (src + y * pitch);
1932 Dest = dst + y * outpitch;
1933 for (x = 0; x < width; x++ ) {
1934 short color = (*Source++);
1935 unsigned char l = ((color >> 10) & 0xfc);
1936 char v = ((color >> 5) & 0x3e);
1937 char u = ((color ) & 0x1f);
1939 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1940 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1941 * shift. GL reads a signed value and converts it into an unsigned value.
1943 /* M */ Dest[2] = l << 1;
1945 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1946 * from 5 bit values to 8 bit values.
1948 /* V */ Dest[1] = v << 3;
1949 /* U */ Dest[0] = u << 3;
1950 Dest += 3;
1953 } else {
1954 for(y = 0; y < height; y++) {
1955 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1956 Source = (WORD *) (src + y * pitch);
1957 for (x = 0; x < width; x++ ) {
1958 short color = (*Source++);
1959 unsigned char l = ((color >> 10) & 0xfc);
1960 short v = ((color >> 5) & 0x3e);
1961 short u = ((color ) & 0x1f);
1962 short v_conv = v + 16;
1963 short u_conv = u + 16;
1965 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1966 Dest_s += 1;
1970 break;
1973 case CONVERT_X8L8V8U8:
1975 unsigned int x, y;
1976 DWORD *Source;
1977 unsigned char *Dest;
1979 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1980 /* This implementation works with the fixed function pipeline and shaders
1981 * without further modification after converting the surface.
1983 for(y = 0; y < height; y++) {
1984 Source = (DWORD *) (src + y * pitch);
1985 Dest = dst + y * outpitch;
1986 for (x = 0; x < width; x++ ) {
1987 long color = (*Source++);
1988 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1989 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1990 /* U */ Dest[0] = (color & 0xff); /* U */
1991 /* I */ Dest[3] = 255; /* X */
1992 Dest += 4;
1995 } else {
1996 /* Doesn't work correctly with the fixed function pipeline, but can work in
1997 * shaders if the shader is adjusted. (There's no use for this format in gl's
1998 * standard fixed function pipeline anyway).
2000 for(y = 0; y < height; y++) {
2001 Source = (DWORD *) (src + y * pitch);
2002 Dest = dst + y * outpitch;
2003 for (x = 0; x < width; x++ ) {
2004 long color = (*Source++);
2005 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2006 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2007 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2008 Dest += 4;
2012 break;
2015 case CONVERT_A4L4:
2017 unsigned int x, y;
2018 unsigned char *Source;
2019 unsigned char *Dest;
2020 for(y = 0; y < height; y++) {
2021 Source = src + y * pitch;
2022 Dest = dst + y * outpitch;
2023 for (x = 0; x < width; x++ ) {
2024 unsigned char color = (*Source++);
2025 /* A */ Dest[1] = (color & 0xf0) << 0;
2026 /* L */ Dest[0] = (color & 0x0f) << 4;
2027 Dest += 2;
2030 break;
2033 case CONVERT_R32F:
2035 unsigned int x, y;
2036 float *Source;
2037 float *Dest;
2038 for(y = 0; y < height; y++) {
2039 Source = (float *) (src + y * pitch);
2040 Dest = (float *) (dst + y * outpitch);
2041 for (x = 0; x < width; x++ ) {
2042 float color = (*Source++);
2043 Dest[0] = color;
2044 Dest[1] = 1.0;
2045 Dest[2] = 1.0;
2046 Dest += 3;
2049 break;
2052 case CONVERT_R16F:
2054 unsigned int x, y;
2055 WORD *Source;
2056 WORD *Dest;
2057 WORD one = 0x3c00;
2058 for(y = 0; y < height; y++) {
2059 Source = (WORD *) (src + y * pitch);
2060 Dest = (WORD *) (dst + y * outpitch);
2061 for (x = 0; x < width; x++ ) {
2062 WORD color = (*Source++);
2063 Dest[0] = color;
2064 Dest[1] = one;
2065 Dest[2] = one;
2066 Dest += 3;
2069 break;
2072 case CONVERT_G16R16:
2074 unsigned int x, y;
2075 WORD *Source;
2076 WORD *Dest;
2078 for(y = 0; y < height; y++) {
2079 Source = (WORD *) (src + y * pitch);
2080 Dest = (WORD *) (dst + y * outpitch);
2081 for (x = 0; x < width; x++ ) {
2082 WORD green = (*Source++);
2083 WORD red = (*Source++);
2084 Dest[0] = green;
2085 Dest[1] = red;
2086 Dest[2] = 0xffff;
2087 Dest += 3;
2090 break;
2093 default:
2094 ERR("Unsupported conversation type %d\n", convert);
2096 return WINED3D_OK;
2099 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2100 IWineD3DPaletteImpl* pal = This->palette;
2101 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2102 BOOL index_in_alpha = FALSE;
2103 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2104 int i;
2106 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2107 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2108 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2109 * duplicate entries. Store the color key in the unused alpha component to speed the
2110 * download up and to make conversion unneeded. */
2111 index_in_alpha = primary_render_target_is_p8(device);
2113 if (pal == NULL) {
2114 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2115 if(dxVersion <= 7) {
2116 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2117 if(index_in_alpha) {
2118 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2119 there's no palette at this time. */
2120 for (i = 0; i < 256; i++) table[i][3] = i;
2122 } else {
2123 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2124 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2125 capability flag is present (wine does advertise this capability) */
2126 for (i = 0; i < 256; i++) {
2127 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2128 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2129 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2130 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2133 } else {
2134 TRACE("Using surface palette %p\n", pal);
2135 /* Get the surface's palette */
2136 for (i = 0; i < 256; i++) {
2137 table[i][0] = pal->palents[i].peRed;
2138 table[i][1] = pal->palents[i].peGreen;
2139 table[i][2] = pal->palents[i].peBlue;
2141 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2142 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2143 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2144 of pixels that should be masked away is set to 0. */
2145 if(index_in_alpha) {
2146 table[i][3] = i;
2147 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2148 table[i][3] = 0x00;
2149 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2150 table[i][3] = pal->palents[i].peFlags;
2151 } else {
2152 table[i][3] = 0xFF;
2158 const char *fragment_palette_conversion =
2159 "!!ARBfp1.0\n"
2160 "TEMP index;\n"
2161 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
2162 "TEX index, fragment.texcoord[0], texture[0], 2D;\n" /* The alpha-component contains the palette index */
2163 "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 */
2164 "TEX result.color, index.a, texture[1], 1D;\n" /* use the alpha-component as a index in the palette to get the final color */
2165 "END";
2167 /* This function is used in case of 8bit paletted textures to upload the palette.
2168 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2169 extensions like ATI_fragment_shaders is possible.
2171 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2172 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2173 BYTE table[256][4];
2174 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2176 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2178 /* Try to use the paletted texture extension */
2179 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2181 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2182 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2184 else
2186 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2187 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2188 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2190 /* Create the fragment program if we don't have it */
2191 if(!device->paletteConversionShader)
2193 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2194 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2195 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2196 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2197 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2200 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2201 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2203 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2204 glEnable(GL_TEXTURE_1D);
2205 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2207 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2208 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2209 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2210 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2212 /* Switch back to unit 0 in which the 2D texture will be stored. */
2213 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2215 /* Rebind the texture because it isn't bound anymore */
2216 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2220 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2221 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2223 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2224 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2225 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2227 return FALSE;
2230 if(This->palette9) {
2231 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2232 return FALSE;
2234 } else {
2235 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2237 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2238 return TRUE;
2241 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2242 GLboolean oldwrite[4];
2244 /* Some formats have only some color channels, and the others are 1.0.
2245 * since our rendering renders to all channels, and those pixel formats
2246 * are emulated by using a full texture with the other channels set to 1.0
2247 * manually, clear the unused channels.
2249 * This could be done with hacking colorwriteenable to mask the colors,
2250 * but before drawing the buffer would have to be cleared too, so there's
2251 * no gain in that
2253 switch(This->resource.format) {
2254 case WINED3DFMT_R16F:
2255 case WINED3DFMT_R32F:
2256 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2257 /* Do not activate a context, the correct drawable is active already
2258 * though just the read buffer is set, make sure to have the correct draw
2259 * buffer too
2261 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2262 glDisable(GL_SCISSOR_TEST);
2263 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2264 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2265 glClearColor(0.0, 1.0, 1.0, 1.0);
2266 glClear(GL_COLOR_BUFFER_BIT);
2267 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2268 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2269 checkGLcall("Unused channel clear\n");
2270 break;
2272 default: break;
2276 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2277 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2279 if (!(This->Flags & SFLAG_INTEXTURE)) {
2280 TRACE("Reloading because surface is dirty\n");
2281 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2282 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2283 /* Reload: vice versa OR */
2284 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2285 /* Also reload: Color key is active AND the color key has changed */
2286 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2287 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2288 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2289 TRACE("Reloading because of color keying\n");
2290 /* To perform the color key conversion we need a sysmem copy of
2291 * the surface. Make sure we have it
2294 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2295 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2296 /* TODO: This is not necessarily needed with hw palettized texture support */
2297 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2298 } else {
2299 TRACE("surface is already in texture\n");
2300 return WINED3D_OK;
2303 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2304 * These resources are not bound by device size or format restrictions. Because of this,
2305 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2306 * However, these resources can always be created, locked, and copied.
2308 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2310 FIXME("(%p) Operation not supported for scratch textures\n",This);
2311 return WINED3DERR_INVALIDCALL;
2314 This->srgb = srgb_mode;
2315 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2317 #if 0
2319 static unsigned int gen = 0;
2320 char buffer[4096];
2321 ++gen;
2322 if ((gen % 10) == 0) {
2323 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2324 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2327 * debugging crash code
2328 if (gen == 250) {
2329 void** test = NULL;
2330 *test = 0;
2334 #endif
2336 if (!(This->Flags & SFLAG_DONOTFREE)) {
2337 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2338 This->resource.allocatedMemory = NULL;
2339 This->resource.heapMemory = NULL;
2340 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2343 return WINED3D_OK;
2346 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2347 /* TODO: check for locks */
2348 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2349 IWineD3DBaseTexture *baseTexture = NULL;
2350 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2352 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2353 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2354 TRACE("Passing to container\n");
2355 IWineD3DBaseTexture_BindTexture(baseTexture);
2356 IWineD3DBaseTexture_Release(baseTexture);
2357 } else {
2358 TRACE("(%p) : Binding surface\n", This);
2360 if(!device->isInDraw) {
2361 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2363 ENTER_GL();
2364 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2365 LEAVE_GL();
2367 return;
2370 #include <errno.h>
2371 #include <stdio.h>
2372 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2373 FILE* f = NULL;
2374 UINT i, y;
2375 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2376 char *allocatedMemory;
2377 char *textureRow;
2378 IWineD3DSwapChain *swapChain = NULL;
2379 int width, height;
2380 GLuint tmpTexture = 0;
2381 DWORD color;
2382 /*FIXME:
2383 Textures may not be stored in ->allocatedgMemory and a GlTexture
2384 so we should lock the surface before saving a snapshot, or at least check that
2386 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2387 by calling GetTexImage and in compressed form by calling
2388 GetCompressedTexImageARB. Queried compressed images can be saved and
2389 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2390 texture images do not need to be processed by the GL and should
2391 significantly improve texture loading performance relative to uncompressed
2392 images. */
2394 /* Setup the width and height to be the internal texture width and height. */
2395 width = This->pow2Width;
2396 height = This->pow2Height;
2397 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2398 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2400 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2401 /* if were not a real texture then read the back buffer into a real texture */
2402 /* we don't want to interfere with the back buffer so read the data into a temporary
2403 * texture and then save the data out of the temporary texture
2405 GLint prevRead;
2406 ENTER_GL();
2407 TRACE("(%p) Reading render target into texture\n", This);
2408 glEnable(GL_TEXTURE_2D);
2410 glGenTextures(1, &tmpTexture);
2411 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2413 glTexImage2D(GL_TEXTURE_2D,
2415 GL_RGBA,
2416 width,
2417 height,
2418 0/*border*/,
2419 GL_RGBA,
2420 GL_UNSIGNED_INT_8_8_8_8_REV,
2421 NULL);
2423 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2424 vcheckGLcall("glGetIntegerv");
2425 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2426 vcheckGLcall("glReadBuffer");
2427 glCopyTexImage2D(GL_TEXTURE_2D,
2429 GL_RGBA,
2432 width,
2433 height,
2436 checkGLcall("glCopyTexImage2D");
2437 glReadBuffer(prevRead);
2438 LEAVE_GL();
2440 } else { /* bind the real texture, and make sure it up to date */
2441 IWineD3DSurface_PreLoad(iface);
2443 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2444 ENTER_GL();
2445 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2446 glGetTexImage(GL_TEXTURE_2D,
2447 This->glDescription.level,
2448 GL_RGBA,
2449 GL_UNSIGNED_INT_8_8_8_8_REV,
2450 allocatedMemory);
2451 checkGLcall("glTexImage2D");
2452 if (tmpTexture) {
2453 glBindTexture(GL_TEXTURE_2D, 0);
2454 glDeleteTextures(1, &tmpTexture);
2456 LEAVE_GL();
2458 f = fopen(filename, "w+");
2459 if (NULL == f) {
2460 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2461 return WINED3DERR_INVALIDCALL;
2463 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2464 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2465 /* TGA header */
2466 fputc(0,f);
2467 fputc(0,f);
2468 fputc(2,f);
2469 fputc(0,f);
2470 fputc(0,f);
2471 fputc(0,f);
2472 fputc(0,f);
2473 fputc(0,f);
2474 fputc(0,f);
2475 fputc(0,f);
2476 fputc(0,f);
2477 fputc(0,f);
2478 /* short width*/
2479 fwrite(&width,2,1,f);
2480 /* short height */
2481 fwrite(&height,2,1,f);
2482 /* format rgba */
2483 fputc(0x20,f);
2484 fputc(0x28,f);
2485 /* raw data */
2486 /* 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 */
2487 if(swapChain)
2488 textureRow = allocatedMemory + (width * (height - 1) *4);
2489 else
2490 textureRow = allocatedMemory;
2491 for (y = 0 ; y < height; y++) {
2492 for (i = 0; i < width; i++) {
2493 color = *((DWORD*)textureRow);
2494 fputc((color >> 16) & 0xFF, f); /* B */
2495 fputc((color >> 8) & 0xFF, f); /* G */
2496 fputc((color >> 0) & 0xFF, f); /* R */
2497 fputc((color >> 24) & 0xFF, f); /* A */
2498 textureRow += 4;
2500 /* take two rows of the pointer to the texture memory */
2501 if(swapChain)
2502 (textureRow-= width << 3);
2505 TRACE("Closing file\n");
2506 fclose(f);
2508 if(swapChain) {
2509 IWineD3DSwapChain_Release(swapChain);
2511 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2512 return WINED3D_OK;
2516 * Slightly inefficient way to handle multiple dirty rects but it works :)
2518 HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2519 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2520 IWineD3DBaseTexture *baseTexture = NULL;
2522 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2523 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2525 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2526 if (NULL != pDirtyRect) {
2527 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2528 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2529 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2530 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2531 } else {
2532 This->dirtyRect.left = 0;
2533 This->dirtyRect.top = 0;
2534 This->dirtyRect.right = This->currentDesc.Width;
2535 This->dirtyRect.bottom = This->currentDesc.Height;
2537 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2538 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2539 /* if the container is a basetexture then mark it dirty. */
2540 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2541 TRACE("Passing to container\n");
2542 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2543 IWineD3DBaseTexture_Release(baseTexture);
2545 return WINED3D_OK;
2548 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2549 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2550 HRESULT hr;
2551 const GlPixelFormatDesc *glDesc;
2552 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2554 TRACE("(%p) : Calling base function first\n", This);
2555 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2556 if(SUCCEEDED(hr)) {
2557 /* Setup some glformat defaults */
2558 This->glDescription.glFormat = glDesc->glFormat;
2559 This->glDescription.glFormatInternal = glDesc->glInternal;
2560 This->glDescription.glType = glDesc->glType;
2562 This->Flags &= ~SFLAG_ALLOCATED;
2563 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2564 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2566 return hr;
2569 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2570 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2572 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2573 WARN("Surface is locked or the HDC is in use\n");
2574 return WINED3DERR_INVALIDCALL;
2577 if(Mem && Mem != This->resource.allocatedMemory) {
2578 void *release = NULL;
2580 /* Do I have to copy the old surface content? */
2581 if(This->Flags & SFLAG_DIBSECTION) {
2582 /* Release the DC. No need to hold the critical section for the update
2583 * Thread because this thread runs only on front buffers, but this method
2584 * fails for render targets in the check above.
2586 SelectObject(This->hDC, This->dib.holdbitmap);
2587 DeleteDC(This->hDC);
2588 /* Release the DIB section */
2589 DeleteObject(This->dib.DIBsection);
2590 This->dib.bitmap_data = NULL;
2591 This->resource.allocatedMemory = NULL;
2592 This->hDC = NULL;
2593 This->Flags &= ~SFLAG_DIBSECTION;
2594 } else if(!(This->Flags & SFLAG_USERPTR)) {
2595 release = This->resource.heapMemory;
2596 This->resource.heapMemory = NULL;
2598 This->resource.allocatedMemory = Mem;
2599 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2601 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2602 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2604 /* For client textures opengl has to be notified */
2605 if(This->Flags & SFLAG_CLIENT) {
2606 This->Flags &= ~SFLAG_ALLOCATED;
2607 IWineD3DSurface_PreLoad(iface);
2608 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2611 /* Now free the old memory if any */
2612 HeapFree(GetProcessHeap(), 0, release);
2613 } else if(This->Flags & SFLAG_USERPTR) {
2614 /* LockRect and GetDC will re-create the dib section and allocated memory */
2615 This->resource.allocatedMemory = NULL;
2616 /* HeapMemory should be NULL already */
2617 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2618 This->Flags &= ~SFLAG_USERPTR;
2620 if(This->Flags & SFLAG_CLIENT) {
2621 This->Flags &= ~SFLAG_ALLOCATED;
2622 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2623 IWineD3DSurface_PreLoad(iface);
2626 return WINED3D_OK;
2629 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2631 /* Flip the surface contents */
2632 /* Flip the DC */
2634 HDC tmp;
2635 tmp = front->hDC;
2636 front->hDC = back->hDC;
2637 back->hDC = tmp;
2640 /* Flip the DIBsection */
2642 HBITMAP tmp;
2643 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2644 tmp = front->dib.DIBsection;
2645 front->dib.DIBsection = back->dib.DIBsection;
2646 back->dib.DIBsection = tmp;
2648 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2649 else front->Flags &= ~SFLAG_DIBSECTION;
2650 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2651 else back->Flags &= ~SFLAG_DIBSECTION;
2654 /* Flip the surface data */
2656 void* tmp;
2658 tmp = front->dib.bitmap_data;
2659 front->dib.bitmap_data = back->dib.bitmap_data;
2660 back->dib.bitmap_data = tmp;
2662 tmp = front->resource.allocatedMemory;
2663 front->resource.allocatedMemory = back->resource.allocatedMemory;
2664 back->resource.allocatedMemory = tmp;
2666 tmp = front->resource.heapMemory;
2667 front->resource.heapMemory = back->resource.heapMemory;
2668 back->resource.heapMemory = tmp;
2671 /* Flip the PBO */
2673 GLuint tmp_pbo = front->pbo;
2674 front->pbo = back->pbo;
2675 back->pbo = tmp_pbo;
2678 /* client_memory should not be different, but just in case */
2680 BOOL tmp;
2681 tmp = front->dib.client_memory;
2682 front->dib.client_memory = back->dib.client_memory;
2683 back->dib.client_memory = tmp;
2686 /* Flip the opengl texture */
2688 glDescriptor tmp_desc = back->glDescription;
2689 back->glDescription = front->glDescription;
2690 front->glDescription = tmp_desc;
2694 DWORD tmp_flags = back->Flags;
2695 back->Flags = front->Flags;
2696 front->Flags = tmp_flags;
2700 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2701 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2702 IWineD3DSwapChainImpl *swapchain = NULL;
2703 HRESULT hr;
2704 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2706 /* Flipping is only supported on RenderTargets and overlays*/
2707 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2708 WARN("Tried to flip a non-render target, non-overlay surface\n");
2709 return WINEDDERR_NOTFLIPPABLE;
2712 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2713 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2715 /* Update the overlay if it is visible */
2716 if(This->overlay_dest) {
2717 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
2718 } else {
2719 return WINED3D_OK;
2723 if(override) {
2724 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2725 * FIXME("(%p) Target override is not supported by now\n", This);
2726 * Additionally, it isn't really possible to support triple-buffering
2727 * properly on opengl at all
2731 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2732 if(!swapchain) {
2733 ERR("Flipped surface is not on a swapchain\n");
2734 return WINEDDERR_NOTFLIPPABLE;
2737 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2738 * and only d3d8 and d3d9 apps specify the presentation interval
2740 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2741 /* Most common case first to avoid wasting time on all the other cases */
2742 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2743 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2744 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2745 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2746 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2747 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2748 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2749 } else {
2750 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2753 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2754 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2755 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2756 return hr;
2759 /* Does a direct frame buffer -> texture copy. Stretching is done
2760 * with single pixel copy calls
2762 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2763 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2764 float xrel, yrel;
2765 UINT row;
2766 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2769 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2770 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2771 ENTER_GL();
2773 /* TODO: Do we need GL_TEXTURE_2D enabled fpr copyteximage? */
2774 glEnable(This->glDescription.target);
2775 checkGLcall("glEnable(This->glDescription.target)");
2777 /* Bind the target texture */
2778 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2779 checkGLcall("glBindTexture");
2780 if(!swapchain) {
2781 TRACE("Reading from an offscreen target\n");
2782 upsidedown = !upsidedown;
2783 glReadBuffer(myDevice->offscreenBuffer);
2784 } else {
2785 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2786 glReadBuffer(buffer);
2788 checkGLcall("glReadBuffer");
2790 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2791 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2793 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2794 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2796 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2797 ERR("Texture filtering not supported in direct blit\n");
2799 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2800 ERR("Texture filtering not supported in direct blit\n");
2803 if(upsidedown &&
2804 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2805 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2806 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2808 glCopyTexSubImage2D(This->glDescription.target,
2809 This->glDescription.level,
2810 drect->x1, drect->y1, /* xoffset, yoffset */
2811 srect->x1, Src->currentDesc.Height - srect->y2,
2812 drect->x2 - drect->x1, drect->y2 - drect->y1);
2813 } else {
2814 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2815 /* I have to process this row by row to swap the image,
2816 * otherwise it would be upside down, so stretching in y direction
2817 * doesn't cost extra time
2819 * However, stretching in x direction can be avoided if not necessary
2821 for(row = drect->y1; row < drect->y2; row++) {
2822 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2823 /* Well, that stuff works, but it's very slow.
2824 * find a better way instead
2826 UINT col;
2828 for(col = drect->x1; col < drect->x2; col++) {
2829 glCopyTexSubImage2D(This->glDescription.target,
2830 This->glDescription.level,
2831 drect->x1 + col, row, /* xoffset, yoffset */
2832 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2833 1, 1);
2835 } else {
2836 glCopyTexSubImage2D(This->glDescription.target,
2837 This->glDescription.level,
2838 drect->x1, row, /* xoffset, yoffset */
2839 srect->x1, yoffset - (int) (row * yrel),
2840 drect->x2-drect->x1, 1);
2844 vcheckGLcall("glCopyTexSubImage2D");
2846 /* Leave the opengl state valid for blitting */
2847 glDisable(This->glDescription.target);
2848 checkGLcall("glDisable(This->glDescription.target)");
2850 LEAVE_GL();
2853 /* Uses the hardware to stretch and flip the image */
2854 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2855 GLuint src, backup = 0;
2856 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2857 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2858 float left, right, top, bottom; /* Texture coordinates */
2859 UINT fbwidth = Src->currentDesc.Width;
2860 UINT fbheight = Src->currentDesc.Height;
2861 GLenum drawBuffer = GL_BACK;
2862 GLenum texture_target;
2863 BOOL noBackBufferBackup;
2865 TRACE("Using hwstretch blit\n");
2866 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2867 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2868 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2870 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2871 if(!noBackBufferBackup && Src->glDescription.textureName == 0) {
2872 /* Get it a description */
2873 IWineD3DSurface_PreLoad(SrcSurface);
2875 ENTER_GL();
2877 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2878 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2880 if(myDevice->activeContext->aux_buffers >= 2) {
2881 /* Got more than one aux buffer? Use the 2nd aux buffer */
2882 drawBuffer = GL_AUX1;
2883 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && myDevice->activeContext->aux_buffers >= 1) {
2884 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2885 drawBuffer = GL_AUX0;
2888 if(noBackBufferBackup) {
2889 glGenTextures(1, &backup);
2890 checkGLcall("glGenTextures\n");
2891 glBindTexture(GL_TEXTURE_2D, backup);
2892 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2893 texture_target = GL_TEXTURE_2D;
2894 } else {
2895 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2896 * we are reading from the back buffer, the backup can be used as source texture
2898 texture_target = Src->glDescription.target;
2899 glBindTexture(texture_target, Src->glDescription.textureName);
2900 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2901 glEnable(texture_target);
2902 checkGLcall("glEnable(texture_target)");
2904 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2905 Src->Flags &= ~SFLAG_INTEXTURE;
2908 if(swapchain) {
2909 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
2910 } else {
2911 TRACE("Reading from an offscreen target\n");
2912 upsidedown = !upsidedown;
2913 glReadBuffer(myDevice->offscreenBuffer);
2916 /* TODO: Only back up the part that will be overwritten */
2917 glCopyTexSubImage2D(texture_target, 0,
2918 0, 0 /* read offsets */,
2919 0, 0,
2920 fbwidth,
2921 fbheight);
2923 checkGLcall("glCopyTexSubImage2D");
2925 /* No issue with overriding these - the sampler is dirty due to blit usage */
2926 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2927 magLookup[Filter - WINED3DTEXF_NONE]);
2928 checkGLcall("glTexParameteri");
2929 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2930 minMipLookup[Filter][WINED3DTEXF_NONE]);
2931 checkGLcall("glTexParameteri");
2933 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2934 src = backup ? backup : Src->glDescription.textureName;
2935 } else {
2936 glReadBuffer(GL_FRONT);
2937 checkGLcall("glReadBuffer(GL_FRONT)");
2939 glGenTextures(1, &src);
2940 checkGLcall("glGenTextures(1, &src)");
2941 glBindTexture(GL_TEXTURE_2D, src);
2942 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2944 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2945 * out for power of 2 sizes
2947 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2948 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2949 checkGLcall("glTexImage2D");
2950 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2951 0, 0 /* read offsets */,
2952 0, 0,
2953 fbwidth,
2954 fbheight);
2956 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2957 checkGLcall("glTexParameteri");
2958 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2959 checkGLcall("glTexParameteri");
2961 glReadBuffer(GL_BACK);
2962 checkGLcall("glReadBuffer(GL_BACK)");
2964 if(texture_target != GL_TEXTURE_2D) {
2965 glDisable(texture_target);
2966 glEnable(GL_TEXTURE_2D);
2967 texture_target = GL_TEXTURE_2D;
2970 checkGLcall("glEnd and previous");
2972 left = srect->x1;
2973 right = srect->x2;
2975 if(upsidedown) {
2976 top = Src->currentDesc.Height - srect->y1;
2977 bottom = Src->currentDesc.Height - srect->y2;
2978 } else {
2979 top = Src->currentDesc.Height - srect->y2;
2980 bottom = Src->currentDesc.Height - srect->y1;
2983 if(Src->Flags & SFLAG_NORMCOORD) {
2984 left /= Src->pow2Width;
2985 right /= Src->pow2Width;
2986 top /= Src->pow2Height;
2987 bottom /= Src->pow2Height;
2990 /* draw the source texture stretched and upside down. The correct surface is bound already */
2991 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
2992 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
2994 glDrawBuffer(drawBuffer);
2995 glReadBuffer(drawBuffer);
2997 glBegin(GL_QUADS);
2998 /* bottom left */
2999 glTexCoord2f(left, bottom);
3000 glVertex2i(0, fbheight);
3002 /* top left */
3003 glTexCoord2f(left, top);
3004 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3006 /* top right */
3007 glTexCoord2f(right, top);
3008 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3010 /* bottom right */
3011 glTexCoord2f(right, bottom);
3012 glVertex2i(drect->x2 - drect->x1, fbheight);
3013 glEnd();
3014 checkGLcall("glEnd and previous");
3016 if(texture_target != This->glDescription.target) {
3017 glDisable(texture_target);
3018 glEnable(This->glDescription.target);
3019 texture_target = This->glDescription.target;
3022 /* Now read the stretched and upside down image into the destination texture */
3023 glBindTexture(texture_target, This->glDescription.textureName);
3024 checkGLcall("glBindTexture");
3025 glCopyTexSubImage2D(texture_target,
3027 drect->x1, drect->y1, /* xoffset, yoffset */
3028 0, 0, /* We blitted the image to the origin */
3029 drect->x2 - drect->x1, drect->y2 - drect->y1);
3030 checkGLcall("glCopyTexSubImage2D");
3032 if(drawBuffer == GL_BACK) {
3033 /* Write the back buffer backup back */
3034 if(backup) {
3035 if(texture_target != GL_TEXTURE_2D) {
3036 glDisable(texture_target);
3037 glEnable(GL_TEXTURE_2D);
3038 texture_target = GL_TEXTURE_2D;
3040 glBindTexture(GL_TEXTURE_2D, backup);
3041 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3042 } else {
3043 if(texture_target != Src->glDescription.target) {
3044 glDisable(texture_target);
3045 glEnable(Src->glDescription.target);
3046 texture_target = Src->glDescription.target;
3048 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3049 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
3052 glBegin(GL_QUADS);
3053 /* top left */
3054 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
3055 glVertex2i(0, 0);
3057 /* bottom left */
3058 glTexCoord2f(0.0, 0.0);
3059 glVertex2i(0, fbheight);
3061 /* bottom right */
3062 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
3063 glVertex2i(fbwidth, Src->currentDesc.Height);
3065 /* top right */
3066 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3067 glVertex2i(fbwidth, 0);
3068 glEnd();
3069 } else {
3070 /* Restore the old draw buffer */
3071 glDrawBuffer(GL_BACK);
3073 glDisable(texture_target);
3074 checkGLcall("glDisable(texture_target)");
3076 /* Cleanup */
3077 if(src != Src->glDescription.textureName && src != backup) {
3078 glDeleteTextures(1, &src);
3079 checkGLcall("glDeleteTextures(1, &src)");
3081 if(backup) {
3082 glDeleteTextures(1, &backup);
3083 checkGLcall("glDeleteTextures(1, &backup)");
3086 LEAVE_GL();
3089 /* Not called from the VTable */
3090 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3091 WINED3DRECT rect;
3092 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3093 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3094 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3096 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3098 /* Get the swapchain. One of the surfaces has to be a primary surface */
3099 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3100 WARN("Destination is in sysmem, rejecting gl blt\n");
3101 return WINED3DERR_INVALIDCALL;
3103 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3104 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3105 if(Src) {
3106 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3107 WARN("Src is in sysmem, rejecting gl blt\n");
3108 return WINED3DERR_INVALIDCALL;
3110 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3111 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3114 /* Early sort out of cases where no render target is used */
3115 if(!dstSwapchain && !srcSwapchain &&
3116 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3117 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3118 return WINED3DERR_INVALIDCALL;
3121 /* No destination color keying supported */
3122 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3123 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3124 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3125 return WINED3DERR_INVALIDCALL;
3128 if (DestRect) {
3129 rect.x1 = DestRect->left;
3130 rect.y1 = DestRect->top;
3131 rect.x2 = DestRect->right;
3132 rect.y2 = DestRect->bottom;
3133 } else {
3134 rect.x1 = 0;
3135 rect.y1 = 0;
3136 rect.x2 = This->currentDesc.Width;
3137 rect.y2 = This->currentDesc.Height;
3140 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3141 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3142 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3143 /* Half-life does a Blt from the back buffer to the front buffer,
3144 * Full surface size, no flags... Use present instead
3146 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3149 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3150 while(1)
3152 RECT mySrcRect;
3153 TRACE("Looking if a Present can be done...\n");
3154 /* Source Rectangle must be full surface */
3155 if( SrcRect ) {
3156 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3157 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3158 TRACE("No, Source rectangle doesn't match\n");
3159 break;
3162 mySrcRect.left = 0;
3163 mySrcRect.top = 0;
3164 mySrcRect.right = Src->currentDesc.Width;
3165 mySrcRect.bottom = Src->currentDesc.Height;
3167 /* No stretching may occur */
3168 if(mySrcRect.right != rect.x2 - rect.x1 ||
3169 mySrcRect.bottom != rect.y2 - rect.y1) {
3170 TRACE("No, stretching is done\n");
3171 break;
3174 /* Destination must be full surface or match the clipping rectangle */
3175 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3177 RECT cliprect;
3178 POINT pos[2];
3179 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3180 pos[0].x = rect.x1;
3181 pos[0].y = rect.y1;
3182 pos[1].x = rect.x2;
3183 pos[1].y = rect.y2;
3184 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3185 pos, 2);
3187 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3188 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3190 TRACE("No, dest rectangle doesn't match(clipper)\n");
3191 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3192 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3193 break;
3196 else
3198 if(rect.x1 != 0 || rect.y1 != 0 ||
3199 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3200 TRACE("No, dest rectangle doesn't match(surface size)\n");
3201 break;
3205 TRACE("Yes\n");
3207 /* These flags are unimportant for the flag check, remove them */
3208 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3209 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3211 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3212 * take very long, while a flip is fast.
3213 * This applies to Half-Life, which does such Blts every time it finished
3214 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3215 * menu. This is also used by all apps when they do windowed rendering
3217 * The problem is that flipping is not really the same as copying. After a
3218 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3219 * untouched. Therefore it's necessary to override the swap effect
3220 * and to set it back after the flip.
3222 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3223 * testcases.
3226 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3227 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3229 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3230 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3232 dstSwapchain->presentParms.SwapEffect = orig_swap;
3234 return WINED3D_OK;
3236 break;
3239 TRACE("Unsupported blit between buffers on the same swapchain\n");
3240 return WINED3DERR_INVALIDCALL;
3241 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3242 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3243 return WINED3DERR_INVALIDCALL;
3244 } else if(dstSwapchain && srcSwapchain) {
3245 FIXME("Implement hardware blit between two different swapchains\n");
3246 return WINED3DERR_INVALIDCALL;
3247 } else if(dstSwapchain) {
3248 if(SrcSurface == myDevice->render_targets[0]) {
3249 TRACE("Blit from active render target to a swapchain\n");
3250 /* Handled with regular texture -> swapchain blit */
3252 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3253 FIXME("Implement blit from a swapchain to the active render target\n");
3254 return WINED3DERR_INVALIDCALL;
3257 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3258 /* Blit from render target to texture */
3259 WINED3DRECT srect;
3260 BOOL upsideDown, stretchx;
3261 BOOL paletteOverride = FALSE;
3263 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3264 TRACE("Color keying not supported by frame buffer to texture blit\n");
3265 return WINED3DERR_INVALIDCALL;
3266 /* Destination color key is checked above */
3269 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3270 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3272 if(SrcRect) {
3273 if(SrcRect->top < SrcRect->bottom) {
3274 srect.y1 = SrcRect->top;
3275 srect.y2 = SrcRect->bottom;
3276 upsideDown = FALSE;
3277 } else {
3278 srect.y1 = SrcRect->bottom;
3279 srect.y2 = SrcRect->top;
3280 upsideDown = TRUE;
3282 srect.x1 = SrcRect->left;
3283 srect.x2 = SrcRect->right;
3284 } else {
3285 srect.x1 = 0;
3286 srect.y1 = 0;
3287 srect.x2 = Src->currentDesc.Width;
3288 srect.y2 = Src->currentDesc.Height;
3289 upsideDown = FALSE;
3291 if(rect.x1 > rect.x2) {
3292 UINT tmp = rect.x2;
3293 rect.x2 = rect.x1;
3294 rect.x1 = tmp;
3295 upsideDown = !upsideDown;
3298 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3299 stretchx = TRUE;
3300 } else {
3301 stretchx = FALSE;
3304 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3305 * In this case grab the palette from the render target. */
3306 if((This->resource.format == WINED3DFMT_P8) && (This->palette == NULL)) {
3307 paletteOverride = TRUE;
3308 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3309 This->palette = Src->palette;
3312 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3313 * flip the image nor scale it.
3315 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3316 * -> If the app wants a image width an unscaled width, copy it line per line
3317 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3318 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3319 * back buffer. This is slower than reading line per line, thus not used for flipping
3320 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3321 * pixel by pixel
3323 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3324 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3325 * backends.
3327 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3328 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3329 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3330 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3331 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3332 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3333 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3334 } else {
3335 TRACE("Using hardware stretching to flip / stretch the texture\n");
3336 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3339 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3340 if(paletteOverride)
3341 This->palette = NULL;
3343 if(!(This->Flags & SFLAG_DONOTFREE)) {
3344 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3345 This->resource.allocatedMemory = NULL;
3346 This->resource.heapMemory = NULL;
3347 } else {
3348 This->Flags &= ~SFLAG_INSYSMEM;
3350 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3351 * path is never entered
3353 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3355 return WINED3D_OK;
3356 } else if(Src) {
3357 /* Blit from offscreen surface to render target */
3358 float glTexCoord[4];
3359 DWORD oldCKeyFlags = Src->CKeyFlags;
3360 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3361 RECT SourceRectangle;
3362 BOOL paletteOverride = FALSE;
3363 GLenum buffer;
3365 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3367 if(SrcRect) {
3368 SourceRectangle.left = SrcRect->left;
3369 SourceRectangle.right = SrcRect->right;
3370 SourceRectangle.top = SrcRect->top;
3371 SourceRectangle.bottom = SrcRect->bottom;
3372 } else {
3373 SourceRectangle.left = 0;
3374 SourceRectangle.right = Src->currentDesc.Width;
3375 SourceRectangle.top = 0;
3376 SourceRectangle.bottom = Src->currentDesc.Height;
3379 /* When blitting from an offscreen surface to a rendertarget, the source
3380 * surface is not required to have a palette. Our rendering / conversion
3381 * code further down the road retrieves the palette from the surface, so
3382 * it must have a palette set. */
3383 if((Src->resource.format == WINED3DFMT_P8) && (Src->palette == NULL)) {
3384 paletteOverride = TRUE;
3385 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3386 Src->palette = This->palette;
3389 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3390 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3391 TRACE("Using stretch_rect_fbo\n");
3392 /* The source is always a texture, but never the currently active render target, and the texture
3393 * contents are never upside down
3395 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3396 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3398 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3399 if(paletteOverride)
3400 Src->palette = NULL;
3401 return WINED3D_OK;
3404 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3405 /* Fall back to software */
3406 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3407 SourceRectangle.left, SourceRectangle.top,
3408 SourceRectangle.right, SourceRectangle.bottom);
3409 return WINED3DERR_INVALIDCALL;
3412 /* Color keying: Check if we have to do a color keyed blt,
3413 * and if not check if a color key is activated.
3415 * Just modify the color keying parameters in the surface and restore them afterwards
3416 * The surface keeps track of the color key last used to load the opengl surface.
3417 * PreLoad will catch the change to the flags and color key and reload if necessary.
3419 if(Flags & WINEDDBLT_KEYSRC) {
3420 /* Use color key from surface */
3421 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3422 /* Use color key from DDBltFx */
3423 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3424 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3425 } else {
3426 /* Do not use color key */
3427 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3430 /* Now load the surface */
3431 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3434 /* Activate the destination context, set it up for blitting */
3435 myDevice->activeContext->last_was_blit = FALSE;
3436 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3438 if(!dstSwapchain) {
3439 TRACE("Drawing to offscreen buffer\n");
3440 buffer = myDevice->offscreenBuffer;
3441 } else {
3442 buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3444 /* Front buffer coordinates are screen coordinates, while OpenGL coordinates are
3445 * window relative. Also beware of the origin difference(top left vs bottom left).
3446 * Also beware that the front buffer's surface size is screen width x screen height,
3447 * whereas the real gl drawable size is the size of the window.
3449 if(buffer == GL_FRONT) {
3450 RECT windowsize;
3451 POINT offset = {0,0};
3452 UINT h;
3453 ClientToScreen(myDevice->ddraw_window, &offset);
3454 GetClientRect(myDevice->ddraw_window, &windowsize);
3455 h = windowsize.bottom - windowsize.top;
3456 rect.x1 -= offset.x; rect.x2 -=offset.x;
3457 rect.y1 -= offset.y; rect.y2 -=offset.y;
3458 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3460 TRACE("Drawing to %#x buffer\n", buffer);
3463 ENTER_GL();
3464 glDrawBuffer(buffer);
3465 checkGLcall("glDrawBuffer");
3467 glEnable(Src->glDescription.target);
3468 checkGLcall("glEnable(Src->glDescription.target)");
3470 /* Bind the texture */
3471 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3472 checkGLcall("glBindTexture");
3474 /* Filtering for StretchRect */
3475 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3476 magLookup[Filter - WINED3DTEXF_NONE]);
3477 checkGLcall("glTexParameteri");
3478 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3479 minMipLookup[Filter][WINED3DTEXF_NONE]);
3480 checkGLcall("glTexParameteri");
3481 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3482 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3483 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3484 checkGLcall("glTexEnvi");
3486 /* This is for color keying */
3487 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3488 glEnable(GL_ALPHA_TEST);
3489 checkGLcall("glEnable GL_ALPHA_TEST");
3491 /* When the primary render target uses P8, the alpha component contains the palette index.
3492 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3493 * should be masked away have alpha set to 0. */
3494 if(primary_render_target_is_p8(myDevice))
3495 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3496 else
3497 glAlphaFunc(GL_NOTEQUAL, 0.0);
3498 checkGLcall("glAlphaFunc\n");
3499 } else {
3500 glDisable(GL_ALPHA_TEST);
3501 checkGLcall("glDisable GL_ALPHA_TEST");
3504 /* Draw a textured quad
3506 glBegin(GL_QUADS);
3508 glColor3d(1.0f, 1.0f, 1.0f);
3509 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3510 glVertex3f(rect.x1,
3511 rect.y1,
3512 0.0);
3514 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3515 glVertex3f(rect.x1, rect.y2, 0.0);
3517 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3518 glVertex3f(rect.x2,
3519 rect.y2,
3520 0.0);
3522 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3523 glVertex3f(rect.x2,
3524 rect.y1,
3525 0.0);
3526 glEnd();
3527 checkGLcall("glEnd");
3529 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3530 glDisable(GL_ALPHA_TEST);
3531 checkGLcall("glDisable(GL_ALPHA_TEST)");
3534 glBindTexture(Src->glDescription.target, 0);
3535 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3536 /* Leave the opengl state valid for blitting */
3537 glDisable(Src->glDescription.target);
3538 checkGLcall("glDisable(Src->glDescription.target)");
3540 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3541 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3543 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3544 glDrawBuffer(GL_BACK);
3545 checkGLcall("glDrawBuffer");
3547 /* Restore the color key parameters */
3548 Src->CKeyFlags = oldCKeyFlags;
3549 Src->SrcBltCKey = oldBltCKey;
3551 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3552 if(paletteOverride)
3553 Src->palette = NULL;
3555 LEAVE_GL();
3557 /* Flush in case the drawable is used by multiple GL contexts */
3558 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3559 glFlush();
3561 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3562 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3563 * is outdated now
3565 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3566 /* TODO: This should be moved to ModifyLocation() */
3567 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3568 This->Flags |= SFLAG_INTEXTURE;
3571 return WINED3D_OK;
3572 } else {
3573 /* Source-Less Blit to render target */
3574 if (Flags & WINEDDBLT_COLORFILL) {
3575 /* This is easy to handle for the D3D Device... */
3576 DWORD color;
3578 TRACE("Colorfill\n");
3580 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3581 must be true if we are here */
3582 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3583 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3584 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3585 TRACE("Surface is higher back buffer, falling back to software\n");
3586 return WINED3DERR_INVALIDCALL;
3589 /* The color as given in the Blt function is in the format of the frame-buffer...
3590 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3592 if (This->resource.format == WINED3DFMT_P8) {
3593 DWORD alpha;
3595 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3596 else alpha = 0xFF000000;
3598 if (This->palette) {
3599 color = (alpha |
3600 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3601 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3602 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3603 } else {
3604 color = alpha;
3607 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3608 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3609 color = 0xFFFFFFFF;
3610 } else {
3611 color = ((0xFF000000) |
3612 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3613 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3614 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3617 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3618 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3619 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3621 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3622 color = DDBltFx->u5.dwFillColor;
3624 else {
3625 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3626 return WINED3DERR_INVALIDCALL;
3629 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3630 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3631 1, /* Number of rectangles */
3632 &rect, WINED3DCLEAR_TARGET, color,
3633 0.0 /* Z */,
3634 0 /* Stencil */);
3635 return WINED3D_OK;
3639 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3640 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3641 return WINED3DERR_INVALIDCALL;
3644 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3646 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3647 float depth;
3649 if (Flags & WINEDDBLT_DEPTHFILL) {
3650 switch(This->resource.format) {
3651 case WINED3DFMT_D16:
3652 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3653 break;
3654 case WINED3DFMT_D15S1:
3655 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3656 break;
3657 case WINED3DFMT_D24S8:
3658 case WINED3DFMT_D24X8:
3659 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3660 break;
3661 case WINED3DFMT_D32:
3662 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3663 break;
3664 default:
3665 depth = 0.0;
3666 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3669 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3670 DestRect == NULL ? 0 : 1,
3671 (WINED3DRECT *) DestRect,
3672 WINED3DCLEAR_ZBUFFER,
3673 0x00000000,
3674 depth,
3675 0x00000000);
3678 FIXME("(%p): Unsupp depthstencil blit\n", This);
3679 return WINED3DERR_INVALIDCALL;
3682 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3683 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3684 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3685 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3686 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3687 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3689 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3691 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3692 return WINEDDERR_SURFACEBUSY;
3695 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3696 * except depth blits, which seem to work
3698 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3699 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3700 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3701 return WINED3DERR_INVALIDCALL;
3702 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3703 TRACE("Z Blit override handled the blit\n");
3704 return WINED3D_OK;
3708 /* Special cases for RenderTargets */
3709 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3710 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3711 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3714 /* For the rest call the X11 surface implementation.
3715 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3716 * other Blts are rather rare
3718 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3721 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3722 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3723 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3724 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3725 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3727 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3729 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3730 return WINEDDERR_SURFACEBUSY;
3733 if(myDevice->inScene &&
3734 (iface == myDevice->stencilBufferTarget ||
3735 (Source && Source == myDevice->stencilBufferTarget))) {
3736 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3737 return WINED3DERR_INVALIDCALL;
3740 /* Special cases for RenderTargets */
3741 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3742 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3744 RECT SrcRect, DstRect;
3745 DWORD Flags=0;
3747 if(rsrc) {
3748 SrcRect.left = rsrc->left;
3749 SrcRect.top= rsrc->top;
3750 SrcRect.bottom = rsrc->bottom;
3751 SrcRect.right = rsrc->right;
3752 } else {
3753 SrcRect.left = 0;
3754 SrcRect.top = 0;
3755 SrcRect.right = srcImpl->currentDesc.Width;
3756 SrcRect.bottom = srcImpl->currentDesc.Height;
3759 DstRect.left = dstx;
3760 DstRect.top=dsty;
3761 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3762 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3764 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3765 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3766 Flags |= WINEDDBLT_KEYSRC;
3767 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3768 Flags |= WINEDDBLT_KEYDEST;
3769 if(trans & WINEDDBLTFAST_WAIT)
3770 Flags |= WINEDDBLT_WAIT;
3771 if(trans & WINEDDBLTFAST_DONOTWAIT)
3772 Flags |= WINEDDBLT_DONOTWAIT;
3774 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3778 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3781 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3782 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3783 RGBQUAD col[256];
3784 IWineD3DPaletteImpl *pal = This->palette;
3785 unsigned int n;
3786 TRACE("(%p)\n", This);
3788 if (!pal) return WINED3D_OK;
3790 if(This->resource.format == WINED3DFMT_P8 ||
3791 This->resource.format == WINED3DFMT_A8P8)
3793 int bpp;
3794 GLenum format, internal, type;
3795 CONVERT_TYPES convert;
3797 /* Check if we are using a RTL mode which uses texturing for uploads */
3798 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX);
3800 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
3801 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, This->srgb);
3803 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
3805 ENTER_GL();
3806 if (This->glDescription.textureName == 0) {
3807 glGenTextures(1, &This->glDescription.textureName);
3808 checkGLcall("glGenTextures");
3810 glBindTexture(This->glDescription.target, This->glDescription.textureName);
3811 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
3812 LEAVE_GL();
3814 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
3815 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
3817 /* We want to force a palette refresh, so mark the drawable as not being up to date */
3818 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
3820 /* Re-upload the palette */
3821 d3dfmt_p8_upload_palette(iface, convert);
3823 /* Without this some palette updates are missed. This at least happens on Nvidia drivers but
3824 * it works fine using Mesa. */
3825 glFlush();
3826 } else {
3827 if(!(This->Flags & SFLAG_INSYSMEM)) {
3828 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3829 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3831 TRACE("Dirtifying surface\n");
3832 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3836 if(This->Flags & SFLAG_DIBSECTION) {
3837 TRACE("(%p): Updating the hdc's palette\n", This);
3838 for (n=0; n<256; n++) {
3839 col[n].rgbRed = pal->palents[n].peRed;
3840 col[n].rgbGreen = pal->palents[n].peGreen;
3841 col[n].rgbBlue = pal->palents[n].peBlue;
3842 col[n].rgbReserved = 0;
3844 SetDIBColorTable(This->hDC, 0, 256, col);
3847 /* Propagate the changes to the drawable when we have a palette. */
3848 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3849 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3851 return WINED3D_OK;
3854 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3855 /** Check against the maximum texture sizes supported by the video card **/
3856 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3857 unsigned int pow2Width, pow2Height;
3858 const GlPixelFormatDesc *glDesc;
3860 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3861 /* Setup some glformat defaults */
3862 This->glDescription.glFormat = glDesc->glFormat;
3863 This->glDescription.glFormatInternal = glDesc->glInternal;
3864 This->glDescription.glType = glDesc->glType;
3866 This->glDescription.textureName = 0;
3867 This->glDescription.target = GL_TEXTURE_2D;
3869 /* Non-power2 support */
3870 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO) || GL_SUPPORT(WINE_NORMALIZED_TEXRECT)) {
3871 pow2Width = This->currentDesc.Width;
3872 pow2Height = This->currentDesc.Height;
3873 } else {
3874 /* Find the nearest pow2 match */
3875 pow2Width = pow2Height = 1;
3876 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3877 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3879 This->pow2Width = pow2Width;
3880 This->pow2Height = pow2Height;
3882 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3883 WINED3DFORMAT Format = This->resource.format;
3884 /** TODO: add support for non power two compressed textures **/
3885 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3886 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5
3887 || This->resource.format == WINED3DFMT_ATI2N) {
3888 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3889 This, This->currentDesc.Width, This->currentDesc.Height);
3890 return WINED3DERR_NOTAVAILABLE;
3894 if(pow2Width != This->currentDesc.Width ||
3895 pow2Height != This->currentDesc.Height) {
3896 This->Flags |= SFLAG_NONPOW2;
3899 TRACE("%p\n", This);
3900 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3901 /* one of three options
3902 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)
3903 2: Set the texture to the maximum size (bad idea)
3904 3: WARN and return WINED3DERR_NOTAVAILABLE;
3905 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.
3907 WARN("(%p) Creating an oversized surface\n", This);
3908 This->Flags |= SFLAG_OVERSIZE;
3910 /* This will be initialized on the first blt */
3911 This->glRect.left = 0;
3912 This->glRect.top = 0;
3913 This->glRect.right = 0;
3914 This->glRect.bottom = 0;
3915 } else {
3916 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3917 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3918 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3919 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3921 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE) &&
3922 !((This->resource.format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE) && (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3924 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3925 This->pow2Width = This->currentDesc.Width;
3926 This->pow2Height = This->currentDesc.Height;
3927 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
3930 /* No oversize, gl rect is the full texture size */
3931 This->Flags &= ~SFLAG_OVERSIZE;
3932 This->glRect.left = 0;
3933 This->glRect.top = 0;
3934 This->glRect.right = This->pow2Width;
3935 This->glRect.bottom = This->pow2Height;
3938 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3939 switch(wined3d_settings.offscreen_rendering_mode) {
3940 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3941 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3942 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3946 This->Flags |= SFLAG_INSYSMEM;
3948 return WINED3D_OK;
3951 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
3952 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3954 TRACE("(%p) New location %#x\n", This, location);
3956 if (location & ~SFLAG_DS_LOCATIONS) {
3957 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
3960 This->Flags &= ~SFLAG_DS_LOCATIONS;
3961 This->Flags |= location;
3964 void surface_load_ds_location(IWineD3DSurface *iface, DWORD location) {
3965 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3966 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3968 TRACE("(%p) New location %#x\n", This, location);
3970 /* TODO: Make this work for modes other than FBO */
3971 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3973 if (This->Flags & location) {
3974 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
3975 return;
3978 if (This->current_renderbuffer) {
3979 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
3980 return;
3983 if (location == SFLAG_DS_OFFSCREEN) {
3984 if (This->Flags & SFLAG_DS_ONSCREEN) {
3985 GLint old_binding = 0;
3987 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
3989 ENTER_GL();
3991 if (!device->depth_blt_texture) {
3992 glGenTextures(1, &device->depth_blt_texture);
3995 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3996 * directly on the FBO texture. That's because we need to flip. */
3997 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
3998 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3999 glBindTexture(GL_TEXTURE_2D, device->depth_blt_texture);
4000 glCopyTexImage2D(This->glDescription.target,
4001 This->glDescription.level,
4002 This->glDescription.glFormatInternal,
4005 This->currentDesc.Width,
4006 This->currentDesc.Height,
4008 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4009 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4010 glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4011 glBindTexture(GL_TEXTURE_2D, old_binding);
4013 /* Setup the destination */
4014 if (!device->depth_blt_rb) {
4015 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
4016 checkGLcall("glGenRenderbuffersEXT");
4018 if (device->depth_blt_rb_w != This->currentDesc.Width
4019 || device->depth_blt_rb_h != This->currentDesc.Height) {
4020 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4021 checkGLcall("glBindRenderbufferEXT");
4022 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
4023 checkGLcall("glRenderbufferStorageEXT");
4024 device->depth_blt_rb_w = This->currentDesc.Width;
4025 device->depth_blt_rb_h = This->currentDesc.Height;
4028 bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->dst_fbo);
4029 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4030 checkGLcall("glFramebufferRenderbufferEXT");
4031 attach_depth_stencil_fbo(device, GL_FRAMEBUFFER_EXT, iface, FALSE);
4033 /* Do the actual blit */
4034 depth_blt((IWineD3DDevice *)device, device->depth_blt_texture);
4035 checkGLcall("depth_blt");
4037 if (device->render_offscreen) {
4038 bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->fbo);
4039 } else {
4040 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4041 checkGLcall("glBindFramebuffer()");
4044 LEAVE_GL();
4045 } else {
4046 FIXME("No up to date depth stencil location\n");
4048 } else if (location == SFLAG_DS_ONSCREEN) {
4049 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4050 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4052 ENTER_GL();
4054 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4055 checkGLcall("glBindFramebuffer()");
4056 depth_blt((IWineD3DDevice *)device, This->glDescription.textureName);
4057 checkGLcall("depth_blt");
4059 if (device->render_offscreen) {
4060 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, device->fbo));
4061 checkGLcall("glBindFramebuffer()");
4064 LEAVE_GL();
4065 } else {
4066 FIXME("No up to date depth stencil location\n");
4068 } else {
4069 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4072 This->Flags |= location;
4075 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4076 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4077 IWineD3DBaseTexture *texture;
4079 TRACE("(%p)->(%s, %s)\n", iface,
4080 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
4081 persistent ? "TRUE" : "FALSE");
4083 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4084 IWineD3DSwapChain *swapchain = NULL;
4086 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
4087 TRACE("Surface %p is an onscreen surface\n", iface);
4089 IWineD3DSwapChain_Release(swapchain);
4090 } else {
4091 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4092 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4096 if(persistent) {
4097 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
4098 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4099 TRACE("Passing to container\n");
4100 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4101 IWineD3DBaseTexture_Release(texture);
4104 This->Flags &= ~SFLAG_LOCATIONS;
4105 This->Flags |= flag;
4106 } else {
4107 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
4108 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4109 TRACE("Passing to container\n");
4110 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4111 IWineD3DBaseTexture_Release(texture);
4114 This->Flags &= ~flag;
4118 struct coords {
4119 GLfloat x, y, z;
4122 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
4123 struct coords coords[4];
4124 RECT rect;
4125 IWineD3DSwapChain *swapchain = NULL;
4126 IWineD3DBaseTexture *texture = NULL;
4127 HRESULT hr;
4128 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4130 if(rect_in) {
4131 rect = *rect_in;
4132 } else {
4133 rect.left = 0;
4134 rect.top = 0;
4135 rect.right = This->currentDesc.Width;
4136 rect.bottom = This->currentDesc.Height;
4139 ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4140 ENTER_GL();
4142 if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
4143 glEnable(GL_TEXTURE_RECTANGLE_ARB);
4144 checkGLcall("glEnable(GL_TEXTURE_RECTANGLE_ARB)");
4145 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName);
4146 checkGLcall("GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName)");
4147 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4148 checkGLcall("glTexParameteri");
4149 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4150 checkGLcall("glTexParameteri");
4152 coords[0].x = rect.left;
4153 coords[0].z = 0;
4155 coords[1].x = rect.left;
4156 coords[1].z = 0;
4158 coords[2].x = rect.right;
4159 coords[2].z = 0;
4161 coords[3].x = rect.right;
4162 coords[3].z = 0;
4164 coords[0].y = rect.top;
4165 coords[1].y = rect.bottom;
4166 coords[2].y = rect.bottom;
4167 coords[3].y = rect.top;
4168 } else if(This->glDescription.target == GL_TEXTURE_2D) {
4169 glEnable(GL_TEXTURE_2D);
4170 checkGLcall("glEnable(GL_TEXTURE_2D)");
4171 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
4172 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
4173 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4174 checkGLcall("glTexParameteri");
4175 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4176 checkGLcall("glTexParameteri");
4178 coords[0].x = (float)rect.left / This->pow2Width;
4179 coords[0].z = 0;
4181 coords[1].x = (float)rect.left / This->pow2Width;
4182 coords[1].z = 0;
4184 coords[2].x = (float)rect.right / This->pow2Width;
4185 coords[2].z = 0;
4187 coords[3].x = (float)rect.right / This->pow2Width;
4188 coords[3].z = 0;
4190 coords[0].y = (float)rect.top / This->pow2Height;
4191 coords[1].y = (float)rect.bottom / This->pow2Height;
4192 coords[2].y = (float)rect.bottom / This->pow2Height;
4193 coords[3].y = (float)rect.top / This->pow2Height;
4194 } else {
4195 /* Must be a cube map */
4196 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
4197 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
4198 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
4199 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
4200 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4201 checkGLcall("glTexParameteri");
4202 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4203 checkGLcall("glTexParameteri");
4205 switch(This->glDescription.target) {
4206 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4207 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
4208 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
4209 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
4210 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
4211 break;
4213 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4214 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
4215 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
4216 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
4217 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
4218 break;
4220 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4221 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
4222 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
4223 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
4224 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
4225 break;
4227 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4228 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
4229 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
4230 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
4231 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
4232 break;
4234 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4235 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
4236 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
4237 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
4238 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
4239 break;
4241 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4242 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
4243 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
4244 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
4245 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
4246 break;
4248 default:
4249 ERR("Unexpected texture target\n");
4250 LEAVE_GL();
4251 return;
4255 glBegin(GL_QUADS);
4256 glTexCoord3fv(&coords[0].x);
4257 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
4259 glTexCoord3fv(&coords[1].x);
4260 glVertex2i(rect.left, device->render_offscreen ? rect.top : rect.bottom);
4262 glTexCoord3fv(&coords[2].x);
4263 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
4265 glTexCoord3fv(&coords[3].x);
4266 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
4267 glEnd();
4268 checkGLcall("glEnd");
4270 if(This->glDescription.target != GL_TEXTURE_2D) {
4271 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4272 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4273 } else {
4274 glDisable(GL_TEXTURE_2D);
4275 checkGLcall("glDisable(GL_TEXTURE_2D)");
4277 LEAVE_GL();
4279 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain);
4280 if(hr == WINED3D_OK && swapchain) {
4281 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4282 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4283 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4284 glFlush();
4286 IWineD3DSwapChain_Release(swapchain);
4287 } else {
4288 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4289 * reset properly next draw
4291 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture);
4292 if(hr == WINED3D_OK && texture) {
4293 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4294 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4295 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4296 IWineD3DBaseTexture_Release(texture);
4301 /*****************************************************************************
4302 * IWineD3DSurface::LoadLocation
4304 * Copies the current surface data from wherever it is to the requested
4305 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4306 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4307 * multiple locations, the gl texture is preferred over the drawable, which is
4308 * preferred over system memory. The PBO counts as system memory. If rect is
4309 * not NULL, only the specified rectangle is copied (only supported for
4310 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4311 * location is marked up to date after the copy.
4313 * Parameters:
4314 * flag: Surface location flag to be updated
4315 * rect: rectangle to be copied
4317 * Returns:
4318 * WINED3D_OK on success
4319 * WINED3DERR_DEVICELOST on an internal error
4321 *****************************************************************************/
4322 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4323 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4324 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4325 IWineD3DSwapChain *swapchain = NULL;
4326 GLenum format, internal, type;
4327 CONVERT_TYPES convert;
4328 int bpp;
4329 int width, pitch, outpitch;
4330 BYTE *mem;
4332 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4333 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
4334 TRACE("Surface %p is an onscreen surface\n", iface);
4336 IWineD3DSwapChain_Release(swapchain);
4337 } else {
4338 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4339 * Prefer SFLAG_INTEXTURE. */
4340 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4344 TRACE("(%p)->(%s, %p)\n", iface,
4345 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
4346 rect);
4347 if(rect) {
4348 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4351 if(This->Flags & flag) {
4352 TRACE("Location already up to date\n");
4353 return WINED3D_OK;
4356 if(!(This->Flags & SFLAG_LOCATIONS)) {
4357 ERR("Surface does not have any up to date location\n");
4358 This->Flags |= SFLAG_LOST;
4359 return WINED3DERR_DEVICELOST;
4362 if(flag == SFLAG_INSYSMEM) {
4363 surface_prepare_system_memory(This);
4365 /* Download the surface to system memory */
4366 if(This->Flags & SFLAG_INTEXTURE) {
4367 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4368 surface_bind_and_dirtify(This);
4370 surface_download_data(This);
4371 } else {
4372 read_from_framebuffer(This, rect,
4373 This->resource.allocatedMemory,
4374 IWineD3DSurface_GetPitch(iface));
4376 } else if(flag == SFLAG_INDRAWABLE) {
4377 if(This->Flags & SFLAG_INTEXTURE) {
4378 surface_blt_to_drawable(This, rect);
4379 } else {
4380 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4382 /* The width is in 'length' not in bytes */
4383 width = This->currentDesc.Width;
4384 pitch = IWineD3DSurface_GetPitch(iface);
4386 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4387 * but it isn't set (yet) in all cases it is getting called. */
4388 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4389 TRACE("Removing the pbo attached to surface %p\n", This);
4390 surface_remove_pbo(This);
4393 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4394 int height = This->currentDesc.Height;
4396 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4397 outpitch = width * bpp;
4398 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4400 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4401 if(!mem) {
4402 ERR("Out of memory %d, %d!\n", outpitch, height);
4403 return WINED3DERR_OUTOFVIDEOMEMORY;
4405 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4407 This->Flags |= SFLAG_CONVERTED;
4408 } else {
4409 This->Flags &= ~SFLAG_CONVERTED;
4410 mem = This->resource.allocatedMemory;
4413 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4415 /* Don't delete PBO memory */
4416 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4417 HeapFree(GetProcessHeap(), 0, mem);
4419 } else /* if(flag == SFLAG_INTEXTURE) */ {
4420 if (This->Flags & SFLAG_INDRAWABLE) {
4421 read_from_framebuffer_texture(This);
4422 } else { /* Upload from system memory */
4423 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4425 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4426 surface_bind_and_dirtify(This);
4427 ENTER_GL();
4429 /* The only place where LoadTexture() might get called when isInDraw=1
4430 * is ActivateContext where lastActiveRenderTarget is preloaded.
4432 if(iface == device->lastActiveRenderTarget && device->isInDraw)
4433 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
4435 /* Otherwise: System memory copy must be most up to date */
4437 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4438 This->Flags |= SFLAG_GLCKEY;
4439 This->glCKey = This->SrcBltCKey;
4441 else This->Flags &= ~SFLAG_GLCKEY;
4443 /* The width is in 'length' not in bytes */
4444 width = This->currentDesc.Width;
4445 pitch = IWineD3DSurface_GetPitch(iface);
4447 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4448 * but it isn't set (yet) in all cases it is getting called. */
4449 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4450 TRACE("Removing the pbo attached to surface %p\n", This);
4451 surface_remove_pbo(This);
4454 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4455 int height = This->currentDesc.Height;
4457 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4458 outpitch = width * bpp;
4459 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4461 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4462 if(!mem) {
4463 ERR("Out of memory %d, %d!\n", outpitch, height);
4464 return WINED3DERR_OUTOFVIDEOMEMORY;
4466 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4468 This->Flags |= SFLAG_CONVERTED;
4469 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
4470 d3dfmt_p8_upload_palette(iface, convert);
4471 This->Flags &= ~SFLAG_CONVERTED;
4472 mem = This->resource.allocatedMemory;
4473 } else {
4474 This->Flags &= ~SFLAG_CONVERTED;
4475 mem = This->resource.allocatedMemory;
4478 /* Make sure the correct pitch is used */
4479 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4481 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4482 TRACE("non power of two support\n");
4483 if(!(This->Flags & SFLAG_ALLOCATED)) {
4484 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4486 if (mem || (This->Flags & SFLAG_PBO)) {
4487 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4489 } else {
4490 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4491 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4493 if(!(This->Flags & SFLAG_ALLOCATED)) {
4494 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4496 if (mem || (This->Flags & SFLAG_PBO)) {
4497 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4501 /* Restore the default pitch */
4502 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4503 LEAVE_GL();
4505 /* Don't delete PBO memory */
4506 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4507 HeapFree(GetProcessHeap(), 0, mem);
4511 if(rect == NULL) {
4512 This->Flags |= flag;
4515 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
4516 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4517 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4518 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4521 return WINED3D_OK;
4524 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
4525 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4526 IWineD3DSwapChain *swapchain = NULL;
4528 /* Update the drawable size method */
4529 if(container) {
4530 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4532 if(swapchain) {
4533 This->get_drawable_size = get_drawable_size_swapchain;
4534 IWineD3DSwapChain_Release(swapchain);
4535 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4536 switch(wined3d_settings.offscreen_rendering_mode) {
4537 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4538 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4539 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4543 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4546 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4547 return SURFACE_OPENGL;
4550 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4551 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4552 HRESULT hr;
4554 /* If there's no destination surface there is nothing to do */
4555 if(!This->overlay_dest) return WINED3D_OK;
4557 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4558 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4559 NULL, WINED3DTEXF_LINEAR);
4561 return hr;
4564 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4566 /* IUnknown */
4567 IWineD3DBaseSurfaceImpl_QueryInterface,
4568 IWineD3DBaseSurfaceImpl_AddRef,
4569 IWineD3DSurfaceImpl_Release,
4570 /* IWineD3DResource */
4571 IWineD3DBaseSurfaceImpl_GetParent,
4572 IWineD3DBaseSurfaceImpl_GetDevice,
4573 IWineD3DBaseSurfaceImpl_SetPrivateData,
4574 IWineD3DBaseSurfaceImpl_GetPrivateData,
4575 IWineD3DBaseSurfaceImpl_FreePrivateData,
4576 IWineD3DBaseSurfaceImpl_SetPriority,
4577 IWineD3DBaseSurfaceImpl_GetPriority,
4578 IWineD3DSurfaceImpl_PreLoad,
4579 IWineD3DSurfaceImpl_UnLoad,
4580 IWineD3DBaseSurfaceImpl_GetType,
4581 /* IWineD3DSurface */
4582 IWineD3DBaseSurfaceImpl_GetContainer,
4583 IWineD3DBaseSurfaceImpl_GetDesc,
4584 IWineD3DSurfaceImpl_LockRect,
4585 IWineD3DSurfaceImpl_UnlockRect,
4586 IWineD3DSurfaceImpl_GetDC,
4587 IWineD3DSurfaceImpl_ReleaseDC,
4588 IWineD3DSurfaceImpl_Flip,
4589 IWineD3DSurfaceImpl_Blt,
4590 IWineD3DBaseSurfaceImpl_GetBltStatus,
4591 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4592 IWineD3DBaseSurfaceImpl_IsLost,
4593 IWineD3DBaseSurfaceImpl_Restore,
4594 IWineD3DSurfaceImpl_BltFast,
4595 IWineD3DBaseSurfaceImpl_GetPalette,
4596 IWineD3DBaseSurfaceImpl_SetPalette,
4597 IWineD3DSurfaceImpl_RealizePalette,
4598 IWineD3DBaseSurfaceImpl_SetColorKey,
4599 IWineD3DBaseSurfaceImpl_GetPitch,
4600 IWineD3DSurfaceImpl_SetMem,
4601 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4602 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4603 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4604 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4605 IWineD3DBaseSurfaceImpl_SetClipper,
4606 IWineD3DBaseSurfaceImpl_GetClipper,
4607 /* Internal use: */
4608 IWineD3DSurfaceImpl_AddDirtyRect,
4609 IWineD3DSurfaceImpl_LoadTexture,
4610 IWineD3DSurfaceImpl_BindTexture,
4611 IWineD3DSurfaceImpl_SaveSnapshot,
4612 IWineD3DSurfaceImpl_SetContainer,
4613 IWineD3DSurfaceImpl_SetGlTextureDesc,
4614 IWineD3DSurfaceImpl_GetGlDesc,
4615 IWineD3DSurfaceImpl_GetData,
4616 IWineD3DSurfaceImpl_SetFormat,
4617 IWineD3DSurfaceImpl_PrivateSetup,
4618 IWineD3DSurfaceImpl_ModifyLocation,
4619 IWineD3DSurfaceImpl_LoadLocation,
4620 IWineD3DSurfaceImpl_GetImplType,
4621 IWineD3DSurfaceImpl_DrawOverlay