wined3d: Reload the palette on a color key change. This fixes the ddex4/ddex5 samples...
[wine.git] / dlls / wined3d / surface.c
blob6cc3b5a6a82bef0b0c3fefba7a0942fd9519251e
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2007 Stefan Dösinger for CodeWeavers
11 * Copyright 2007 Henri Verbeet
12 * Copyright 2006-2007 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);
39 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This) {
40 /* Make sure that a proper texture unit is selected, bind the texture
41 * and dirtify the sampler to restore the texture on the next draw. */
42 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
43 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
44 checkGLcall("glActiveTextureARB");
46 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
47 IWineD3DSurface_BindTexture((IWineD3DSurface *)This);
50 /* This call just downloads data, the caller is responsible for activating the
51 * right context and binding the correct texture. */
52 static void surface_download_data(IWineD3DSurfaceImpl *This) {
53 if (0 == This->glDescription.textureName) {
54 ERR("Surface does not have a texture, but SFLAG_INTEXTURE is set\n");
55 return;
58 if(This->Flags & SFLAG_CONVERTED) {
59 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(This->resource.format));
60 return;
63 ENTER_GL();
65 if (This->resource.format == WINED3DFMT_DXT1 ||
66 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
67 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
68 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
69 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
70 } else {
71 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
72 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
74 if(This->Flags & SFLAG_PBO) {
75 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
76 checkGLcall("glBindBufferARB");
77 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
78 checkGLcall("glGetCompressedTexImageARB()");
79 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
80 checkGLcall("glBindBufferARB");
81 } else {
82 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
83 checkGLcall("glGetCompressedTexImageARB()");
86 LEAVE_GL();
87 } else {
88 void *mem;
89 int src_pitch = 0;
90 int dst_pitch = 0;
92 if (This->Flags & SFLAG_NONPOW2) {
93 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
94 src_pitch = This->bytesPerPixel * This->pow2Width;
95 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
96 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
97 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
98 } else {
99 mem = This->resource.allocatedMemory;
102 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
103 This->glDescription.glFormat, This->glDescription.glType, mem);
105 if(This->Flags & SFLAG_PBO) {
106 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
107 checkGLcall("glBindBufferARB");
109 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
110 This->glDescription.glType, NULL);
111 checkGLcall("glGetTexImage()");
113 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
114 checkGLcall("glBindBufferARB");
115 } else {
116 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
117 This->glDescription.glType, mem);
118 checkGLcall("glGetTexImage()");
120 LEAVE_GL();
122 if (This->Flags & SFLAG_NONPOW2) {
123 LPBYTE src_data, dst_data;
124 int y;
126 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
127 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
128 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
130 * We're doing this...
132 * instead of boxing the texture :
133 * |<-texture width ->| -->pow2width| /\
134 * |111111111111111111| | |
135 * |222 Texture 222222| boxed empty | texture height
136 * |3333 Data 33333333| | |
137 * |444444444444444444| | \/
138 * ----------------------------------- |
139 * | boxed empty | boxed empty | pow2height
140 * | | | \/
141 * -----------------------------------
144 * we're repacking the data to the expected texture width
146 * |<-texture width ->| -->pow2width| /\
147 * |111111111111111111222222222222222| |
148 * |222333333333333333333444444444444| texture height
149 * |444444 | |
150 * | | \/
151 * | | |
152 * | empty | pow2height
153 * | | \/
154 * -----------------------------------
156 * == is the same as
158 * |<-texture width ->| /\
159 * |111111111111111111|
160 * |222222222222222222|texture height
161 * |333333333333333333|
162 * |444444444444444444| \/
163 * --------------------
165 * this also means that any references to allocatedMemory should work with the data as if were a
166 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
168 * internally the texture is still stored in a boxed format so any references to textureName will
169 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
171 * Performance should not be an issue, because applications normally do not lock the surfaces when
172 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
173 * and doesn't have to be re-read.
175 src_data = mem;
176 dst_data = This->resource.allocatedMemory;
177 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
178 for (y = 1 ; y < This->currentDesc.Height; y++) {
179 /* skip the first row */
180 src_data += src_pitch;
181 dst_data += dst_pitch;
182 memcpy(dst_data, src_data, dst_pitch);
185 HeapFree(GetProcessHeap(), 0, mem);
189 /* Surface has now been downloaded */
190 This->Flags |= SFLAG_INSYSMEM;
193 /* This call just uploads data, the caller is responsible for activating the
194 * right context and binding the correct texture. */
195 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
196 if (This->resource.format == WINED3DFMT_DXT1 ||
197 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
198 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
199 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
200 FIXME("Using DXT1/3/5 without advertized support\n");
201 } else {
202 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
203 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
204 * function uses glCompressedTexImage2D instead of the SubImage call
206 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
207 ENTER_GL();
209 if(This->Flags & SFLAG_PBO) {
210 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
211 checkGLcall("glBindBufferARB");
212 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
214 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
215 width, height, 0 /* border */, This->resource.size, NULL));
216 checkGLcall("glCompressedTexSubImage2D");
218 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
219 checkGLcall("glBindBufferARB");
220 } else {
221 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
222 width, height, 0 /* border */, This->resource.size, data));
223 checkGLcall("glCompressedTexSubImage2D");
225 LEAVE_GL();
227 } else {
228 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
229 ENTER_GL();
231 if(This->Flags & SFLAG_PBO) {
232 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
233 checkGLcall("glBindBufferARB");
234 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
236 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
237 checkGLcall("glTexSubImage2D");
239 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
240 checkGLcall("glBindBufferARB");
242 else {
243 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
244 checkGLcall("glTexSubImage2D");
247 LEAVE_GL();
251 /* This call just allocates the texture, the caller is responsible for
252 * activating the right context and binding the correct texture. */
253 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
254 BOOL enable_client_storage = FALSE;
255 BYTE *mem = NULL;
257 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,
258 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
260 if (This->resource.format == WINED3DFMT_DXT1 ||
261 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
262 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
263 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
264 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
266 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
267 * once, unfortunately
269 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
270 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
271 This->Flags |= SFLAG_CLIENT;
272 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
273 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
274 width, height, 0 /* border */, This->resource.size, mem));
277 return;
280 ENTER_GL();
282 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
283 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
284 /* In some cases we want to disable client storage.
285 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
286 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
287 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
288 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
289 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
291 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
292 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
293 This->Flags &= ~SFLAG_CLIENT;
294 enable_client_storage = TRUE;
295 } else {
296 This->Flags |= SFLAG_CLIENT;
298 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
299 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
301 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
304 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
305 checkGLcall("glTexImage2D");
307 if(enable_client_storage) {
308 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
309 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
311 LEAVE_GL();
313 This->Flags |= SFLAG_ALLOCATED;
316 /* In D3D the depth stencil dimensions have to be greater than or equal to the
317 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
318 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
319 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
320 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
321 renderbuffer_entry_t *entry;
322 GLuint renderbuffer = 0;
323 unsigned int src_width, src_height;
325 src_width = This->pow2Width;
326 src_height = This->pow2Height;
328 /* A depth stencil smaller than the render target is not valid */
329 if (width > src_width || height > src_height) return;
331 /* Remove any renderbuffer set if the sizes match */
332 if (width == src_width && height == src_height) {
333 This->current_renderbuffer = NULL;
334 return;
337 /* Look if we've already got a renderbuffer of the correct dimensions */
338 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
339 if (entry->width == width && entry->height == height) {
340 renderbuffer = entry->id;
341 This->current_renderbuffer = entry;
342 break;
346 if (!renderbuffer) {
347 const GlPixelFormatDesc *glDesc;
348 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
350 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
351 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
352 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
354 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
355 entry->width = width;
356 entry->height = height;
357 entry->id = renderbuffer;
358 list_add_head(&This->renderbuffers, &entry->entry);
360 This->current_renderbuffer = entry;
363 checkGLcall("set_compatible_renderbuffer");
366 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
367 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
368 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
370 TRACE("(%p) : swapchain %p\n", This, swapchain);
372 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
373 TRACE("Returning GL_BACK\n");
374 return GL_BACK;
375 } else if (swapchain_impl->frontBuffer == iface) {
376 TRACE("Returning GL_FRONT\n");
377 return GL_FRONT;
380 FIXME("Higher back buffer, returning GL_BACK\n");
381 return GL_BACK;
384 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
385 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
386 ULONG ref = InterlockedDecrement(&This->resource.ref);
387 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
388 if (ref == 0) {
389 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
390 renderbuffer_entry_t *entry, *entry2;
391 TRACE("(%p) : cleaning up\n", This);
393 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
395 /* Need a context to destroy the texture. Use the currently active render target, but only if
396 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
397 * When destroying the primary rt, Uninit3D will activate a context before doing anything
399 if(device->render_targets && device->render_targets[0]) {
400 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
403 TRACE("Deleting texture %d\n", This->glDescription.textureName);
404 ENTER_GL();
405 glDeleteTextures(1, &This->glDescription.textureName);
406 LEAVE_GL();
409 if(This->Flags & SFLAG_PBO) {
410 /* Delete the PBO */
411 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
414 if(This->Flags & SFLAG_DIBSECTION) {
415 /* Release the DC */
416 SelectObject(This->hDC, This->dib.holdbitmap);
417 DeleteDC(This->hDC);
418 /* Release the DIB section */
419 DeleteObject(This->dib.DIBsection);
420 This->dib.bitmap_data = NULL;
421 This->resource.allocatedMemory = NULL;
423 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
425 HeapFree(GetProcessHeap(), 0, This->palette9);
427 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
428 if(iface == device->ddraw_primary)
429 device->ddraw_primary = NULL;
431 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
432 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
433 HeapFree(GetProcessHeap(), 0, entry);
436 TRACE("(%p) Released\n", This);
437 HeapFree(GetProcessHeap(), 0, This);
440 return ref;
443 /* ****************************************************
444 IWineD3DSurface IWineD3DResource parts follow
445 **************************************************** */
447 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
448 /* TODO: check for locks */
449 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
450 IWineD3DBaseTexture *baseTexture = NULL;
451 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
453 TRACE("(%p)Checking to see if the container is a base texture\n", This);
454 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
455 TRACE("Passing to container\n");
456 IWineD3DBaseTexture_PreLoad(baseTexture);
457 IWineD3DBaseTexture_Release(baseTexture);
458 } else {
459 TRACE("(%p) : About to load surface\n", This);
461 if(!device->isInDraw) {
462 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
465 ENTER_GL();
466 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
467 if (!This->glDescription.level) {
468 if (!This->glDescription.textureName) {
469 glGenTextures(1, &This->glDescription.textureName);
470 checkGLcall("glGenTextures");
471 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
473 glBindTexture(This->glDescription.target, This->glDescription.textureName);
474 checkGLcall("glBindTexture");
475 IWineD3DSurface_LoadTexture(iface, FALSE);
476 /* This is where we should be reducing the amount of GLMemoryUsed */
477 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
478 /* assume this is a coding error not a real error for now */
479 FIXME("Mipmap surface has a glTexture bound to it!\n");
481 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
482 /* Tell opengl to try and keep this texture in video ram (well mostly) */
483 GLclampf tmp;
484 tmp = 0.9f;
485 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
487 LEAVE_GL();
489 return;
492 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
493 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
494 This->resource.allocatedMemory =
495 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
497 ENTER_GL();
498 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
499 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
500 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
501 checkGLcall("glGetBufferSubData");
502 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
503 checkGLcall("glDeleteBuffers");
504 LEAVE_GL();
506 This->pbo = 0;
507 This->Flags &= ~SFLAG_PBO;
510 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
511 IWineD3DBaseTexture *texture = NULL;
512 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
513 renderbuffer_entry_t *entry, *entry2;
514 TRACE("(%p)\n", iface);
516 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
517 /* Default pool resources are supposed to be destroyed before Reset is called.
518 * Implicit resources stay however. So this means we have an implicit render target
519 * or depth stencil. The content may be destroyed, but we still have to tear down
520 * opengl resources, so we cannot leave early.
522 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
523 } else {
524 /* Load the surface into system memory */
525 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
527 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
528 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
529 This->Flags &= ~SFLAG_ALLOCATED;
531 /* Destroy PBOs, but load them into real sysmem before */
532 if(This->Flags & SFLAG_PBO) {
533 surface_remove_pbo(This);
536 /* Destroy fbo render buffers. This is needed for implicit render targets, for
537 * all application-created targets the application has to release the surface
538 * before calling _Reset
540 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
541 ENTER_GL();
542 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
543 LEAVE_GL();
544 list_remove(&entry->entry);
545 HeapFree(GetProcessHeap(), 0, entry);
547 list_init(&This->renderbuffers);
548 This->current_renderbuffer = NULL;
550 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
551 * destroy it
553 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
554 if(!texture) {
555 ENTER_GL();
556 glDeleteTextures(1, &This->glDescription.textureName);
557 This->glDescription.textureName = 0;
558 LEAVE_GL();
559 } else {
560 IWineD3DBaseTexture_Release(texture);
562 return;
565 /* ******************************************************
566 IWineD3DSurface IWineD3DSurface parts follow
567 ****************************************************** */
569 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
570 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
571 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
572 if (This->glDescription.textureName == 0 && textureName != 0) {
573 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
574 IWineD3DSurface_AddDirtyRect(iface, NULL);
576 This->glDescription.textureName = textureName;
577 This->glDescription.target = target;
578 This->Flags &= ~SFLAG_ALLOCATED;
581 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
582 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
583 TRACE("(%p) : returning %p\n", This, &This->glDescription);
584 *glDescription = &This->glDescription;
587 /* TODO: think about moving this down to resource? */
588 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
589 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
590 /* 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 */
591 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
592 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
594 return (CONST void*)(This->resource.allocatedMemory);
597 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
598 IWineD3DSwapChainImpl *swapchain;
599 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
600 BYTE *mem;
601 GLint fmt;
602 GLint type;
603 BYTE *row, *top, *bottom;
604 int i;
605 BOOL bpp;
606 RECT local_rect;
607 BOOL srcIsUpsideDown;
609 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
610 static BOOL warned = FALSE;
611 if(!warned) {
612 ERR("The application tries to lock the render target, but render target locking is disabled\n");
613 warned = TRUE;
615 return;
618 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
619 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
620 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
621 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
622 * context->last_was_blit set on the unlock.
624 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
625 ENTER_GL();
627 /* Select the correct read buffer, and give some debug output.
628 * There is no need to keep track of the current read buffer or reset it, every part of the code
629 * that reads sets the read buffer as desired.
631 if(!swapchain) {
632 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
633 * Read from the back buffer
635 TRACE("Locking offscreen render target\n");
636 glReadBuffer(myDevice->offscreenBuffer);
637 srcIsUpsideDown = TRUE;
638 } else {
639 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
640 TRACE("Locking %#x buffer\n", buffer);
641 glReadBuffer(buffer);
642 checkGLcall("glReadBuffer");
644 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
645 srcIsUpsideDown = FALSE;
648 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
649 if(!rect) {
650 local_rect.left = 0;
651 local_rect.top = 0;
652 local_rect.right = This->currentDesc.Width;
653 local_rect.bottom = This->currentDesc.Height;
654 } else {
655 local_rect = *rect;
657 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
659 switch(This->resource.format)
661 case WINED3DFMT_P8:
663 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
664 /* In case of P8 render targets the index is stored in the alpha component */
665 fmt = GL_ALPHA;
666 type = GL_UNSIGNED_BYTE;
667 mem = dest;
668 bpp = This->bytesPerPixel;
669 } else {
670 /* GL can't return palettized data, so read ARGB pixels into a
671 * separate block of memory and convert them into palettized format
672 * in software. Slow, but if the app means to use palettized render
673 * targets and locks it...
675 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
676 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
677 * for the color channels when palettizing the colors.
679 fmt = GL_RGB;
680 type = GL_UNSIGNED_BYTE;
681 pitch *= 3;
682 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
683 if(!mem) {
684 ERR("Out of memory\n");
685 LEAVE_GL();
686 return;
688 bpp = This->bytesPerPixel * 3;
691 break;
693 default:
694 mem = dest;
695 fmt = This->glDescription.glFormat;
696 type = This->glDescription.glType;
697 bpp = This->bytesPerPixel;
700 if(This->Flags & SFLAG_PBO) {
701 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
702 checkGLcall("glBindBufferARB");
705 glReadPixels(local_rect.left, local_rect.top,
706 local_rect.right - local_rect.left,
707 local_rect.bottom - local_rect.top,
708 fmt, type, mem);
709 vcheckGLcall("glReadPixels");
711 if(This->Flags & SFLAG_PBO) {
712 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
713 checkGLcall("glBindBufferARB");
715 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
716 * to get a pointer to it and perform the flipping in software. This is a lot
717 * faster than calling glReadPixels for each line. In case we want more speed
718 * we should rerender it flipped in a FBO and read the data back from the FBO. */
719 if(!srcIsUpsideDown) {
720 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
721 checkGLcall("glBindBufferARB");
723 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
724 checkGLcall("glMapBufferARB");
728 /* TODO: Merge this with the palettization loop below for P8 targets */
729 if(!srcIsUpsideDown) {
730 UINT len, off;
731 /* glReadPixels returns the image upside down, and there is no way to prevent this.
732 Flip the lines in software */
733 len = (local_rect.right - local_rect.left) * bpp;
734 off = local_rect.left * bpp;
736 row = HeapAlloc(GetProcessHeap(), 0, len);
737 if(!row) {
738 ERR("Out of memory\n");
739 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
740 LEAVE_GL();
741 return;
744 top = mem + pitch * local_rect.top;
745 bottom = mem + pitch * ( local_rect.bottom - local_rect.top - 1);
746 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
747 memcpy(row, top + off, len);
748 memcpy(top + off, bottom + off, len);
749 memcpy(bottom + off, row, len);
750 top += pitch;
751 bottom -= pitch;
753 HeapFree(GetProcessHeap(), 0, row);
755 /* Unmap the temp PBO buffer */
756 if(This->Flags & SFLAG_PBO) {
757 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
758 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
762 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
763 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
764 * the same color but we have no choice.
765 * In case of render targets, the index is stored in the alpha component so no conversion is needed.
767 if((This->resource.format == WINED3DFMT_P8) && !(This->resource.usage & WINED3DUSAGE_RENDERTARGET)) {
768 PALETTEENTRY *pal;
769 DWORD width = pitch / 3;
770 int x, y, c;
771 if(This->palette) {
772 pal = This->palette->palents;
773 } else {
774 pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
777 for(y = local_rect.top; y < local_rect.bottom; y++) {
778 for(x = local_rect.left; x < local_rect.right; x++) {
779 /* start lines pixels */
780 BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
781 BYTE *green = blue + 1;
782 BYTE *red = green + 1;
784 for(c = 0; c < 256; c++) {
785 if(*red == pal[c].peRed &&
786 *green == pal[c].peGreen &&
787 *blue == pal[c].peBlue)
789 *((BYTE *) dest + y * width + x) = c;
790 break;
795 HeapFree(GetProcessHeap(), 0, mem);
797 LEAVE_GL();
800 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
801 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
802 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
803 * changed
805 if(!(This->Flags & SFLAG_DYNLOCK)) {
806 This->lockCount++;
807 /* MAXLOCKCOUNT is defined in wined3d_private.h */
808 if(This->lockCount > MAXLOCKCOUNT) {
809 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
810 This->Flags |= SFLAG_DYNLOCK;
814 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
815 * Also don't create a PBO for systemmem surfaces.
817 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
818 GLenum error;
819 ENTER_GL();
821 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
822 error = glGetError();
823 if(This->pbo == 0 || error != GL_NO_ERROR) {
824 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
827 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
829 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
830 checkGLcall("glBindBufferARB");
832 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
833 checkGLcall("glBufferDataARB");
835 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
836 checkGLcall("glBindBufferARB");
838 /* We don't need the system memory anymore and we can't even use it for PBOs */
839 if(!(This->Flags & SFLAG_CLIENT)) {
840 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
841 This->resource.heapMemory = NULL;
843 This->resource.allocatedMemory = NULL;
844 This->Flags |= SFLAG_PBO;
845 LEAVE_GL();
846 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
847 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
848 * or a pbo to map
850 if(!This->resource.heapMemory) {
851 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
853 This->resource.allocatedMemory =
854 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
855 if(This->Flags & SFLAG_INSYSMEM) {
856 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
861 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
862 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
863 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
864 IWineD3DSwapChain *swapchain = NULL;
866 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
868 /* This is also done in the base class, but we have to verify this before loading any data from
869 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
870 * may interfere, and all other bad things may happen
872 if (This->Flags & SFLAG_LOCKED) {
873 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
874 return WINED3DERR_INVALIDCALL;
876 This->Flags |= SFLAG_LOCKED;
878 if (!(This->Flags & SFLAG_LOCKABLE))
880 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
883 if (Flags & WINED3DLOCK_DISCARD) {
884 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
885 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
886 This->Flags |= SFLAG_INSYSMEM;
889 if (This->Flags & SFLAG_INSYSMEM) {
890 TRACE("Local copy is up to date, not downloading data\n");
891 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
892 goto lock_end;
895 /* Now download the surface content from opengl
896 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
897 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
899 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
900 if(swapchain || iface == myDevice->render_targets[0]) {
901 const RECT *pass_rect = pRect;
903 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
904 * because most caller functions do not need that. So do that here
906 if(pRect &&
907 pRect->top == 0 &&
908 pRect->left == 0 &&
909 pRect->right == This->currentDesc.Width &&
910 pRect->bottom == This->currentDesc.Height) {
911 pass_rect = NULL;
914 switch(wined3d_settings.rendertargetlock_mode) {
915 case RTL_TEXDRAW:
916 case RTL_TEXTEX:
917 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
918 #if 0
919 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
920 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
921 * This may be faster on some cards
923 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
924 #endif
925 /* drop through */
927 case RTL_AUTO:
928 case RTL_READDRAW:
929 case RTL_READTEX:
930 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
931 break;
933 case RTL_DISABLE:
934 break;
936 if(swapchain) IWineD3DSwapChain_Release(swapchain);
938 } else if(iface == myDevice->stencilBufferTarget) {
939 /** the depth stencil in openGL has a format of GL_FLOAT
940 * which should be good for WINED3DFMT_D16_LOCKABLE
941 * and WINED3DFMT_D16
942 * it is unclear what format the stencil buffer is in except.
943 * 'Each index is converted to fixed point...
944 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
945 * mappings in the table GL_PIXEL_MAP_S_TO_S.
946 * glReadPixels(This->lockedRect.left,
947 * This->lockedRect.bottom - j - 1,
948 * This->lockedRect.right - This->lockedRect.left,
949 * 1,
950 * GL_DEPTH_COMPONENT,
951 * type,
952 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
954 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
955 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
956 * none of that is the case the problem is not in this function :-)
957 ********************************************/
958 FIXME("Depth stencil locking not supported yet\n");
959 } else {
960 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
961 TRACE("locking an ordinary surface\n");
962 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
965 lock_end:
966 if(This->Flags & SFLAG_PBO) {
967 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
968 ENTER_GL();
969 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
970 checkGLcall("glBindBufferARB");
972 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
973 if(This->resource.allocatedMemory) {
974 ERR("The surface already has PBO memory allocated!\n");
977 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
978 checkGLcall("glMapBufferARB");
980 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
981 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
982 checkGLcall("glBindBufferARB");
984 LEAVE_GL();
987 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
988 /* Don't dirtify */
989 } else {
990 IWineD3DBaseTexture *pBaseTexture;
992 * Dirtify on lock
993 * as seen in msdn docs
995 IWineD3DSurface_AddDirtyRect(iface, pRect);
997 /** Dirtify Container if needed */
998 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
999 TRACE("Making container dirty\n");
1000 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1001 IWineD3DBaseTexture_Release(pBaseTexture);
1002 } else {
1003 TRACE("Surface is standalone, no need to dirty the container\n");
1007 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1010 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1011 GLint prev_store;
1012 GLint prev_rasterpos[4];
1013 GLint skipBytes = 0;
1014 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1015 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1016 IWineD3DSwapChainImpl *swapchain;
1018 /* Activate the correct context for the render target */
1019 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1020 ENTER_GL();
1022 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
1023 if(!swapchain) {
1024 /* Primary offscreen render target */
1025 TRACE("Offscreen render target\n");
1026 glDrawBuffer(myDevice->offscreenBuffer);
1027 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1028 } else {
1029 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1030 TRACE("Unlocking %#x buffer\n", buffer);
1031 glDrawBuffer(buffer);
1032 checkGLcall("glDrawBuffer");
1034 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1037 glFlush();
1038 vcheckGLcall("glFlush");
1039 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1040 vcheckGLcall("glIntegerv");
1041 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1042 vcheckGLcall("glIntegerv");
1043 glPixelZoom(1.0, -1.0);
1044 vcheckGLcall("glPixelZoom");
1046 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1047 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1048 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1050 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1051 vcheckGLcall("glRasterPos2f");
1053 /* Some drivers(radeon dri, others?) don't like exceptions during
1054 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1055 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1056 * catch to put the dib section in InSync mode, which leads to a crash
1057 * and a blocked x server on my radeon card.
1059 * The following lines read the dib section so it is put in inSync mode
1060 * before glDrawPixels is called and the crash is prevented. There won't
1061 * be any interfering gdi accesses, because UnlockRect is called from
1062 * ReleaseDC, and the app won't use the dc any more afterwards.
1064 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1065 volatile BYTE read;
1066 read = This->resource.allocatedMemory[0];
1069 if(This->Flags & SFLAG_PBO) {
1070 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1071 checkGLcall("glBindBufferARB");
1074 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1075 (This->lockedRect.bottom - This->lockedRect.top)-1,
1076 fmt, type,
1077 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1078 checkGLcall("glDrawPixels");
1080 if(This->Flags & SFLAG_PBO) {
1081 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1082 checkGLcall("glBindBufferARB");
1085 glPixelZoom(1.0,1.0);
1086 vcheckGLcall("glPixelZoom");
1088 glRasterPos3iv(&prev_rasterpos[0]);
1089 vcheckGLcall("glRasterPos3iv");
1091 /* Reset to previous pack row length */
1092 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1093 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1095 if(!swapchain) {
1096 glDrawBuffer(myDevice->offscreenBuffer);
1097 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1098 } else if(swapchain->backBuffer) {
1099 glDrawBuffer(GL_BACK);
1100 checkGLcall("glDrawBuffer(GL_BACK)");
1101 } else {
1102 glDrawBuffer(GL_FRONT);
1103 checkGLcall("glDrawBuffer(GL_FRONT)");
1105 LEAVE_GL();
1107 return;
1110 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1111 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1112 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1113 IWineD3DSwapChainImpl *swapchain = NULL;
1114 BOOL fullsurface;
1116 if (!(This->Flags & SFLAG_LOCKED)) {
1117 WARN("trying to Unlock an unlocked surf@%p\n", This);
1118 return WINED3DERR_INVALIDCALL;
1121 if (This->Flags & SFLAG_PBO) {
1122 TRACE("Freeing PBO memory\n");
1123 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1124 ENTER_GL();
1125 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1126 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1127 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1128 checkGLcall("glUnmapBufferARB");
1129 LEAVE_GL();
1130 This->resource.allocatedMemory = NULL;
1133 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1135 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1136 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1137 goto unlock_end;
1140 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1141 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1142 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1144 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1145 static BOOL warned = FALSE;
1146 if(!warned) {
1147 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1148 warned = TRUE;
1150 goto unlock_end;
1153 if(This->dirtyRect.left == 0 &&
1154 This->dirtyRect.top == 0 &&
1155 This->dirtyRect.right == This->currentDesc.Width &&
1156 This->dirtyRect.bottom == This->currentDesc.Height) {
1157 fullsurface = TRUE;
1158 } else {
1159 /* TODO: Proper partial rectangle tracking */
1160 fullsurface = FALSE;
1161 This->Flags |= SFLAG_INSYSMEM;
1164 switch(wined3d_settings.rendertargetlock_mode) {
1165 case RTL_READTEX:
1166 case RTL_TEXTEX:
1167 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1168 ENTER_GL();
1169 if (This->glDescription.textureName == 0) {
1170 glGenTextures(1, &This->glDescription.textureName);
1171 checkGLcall("glGenTextures");
1173 glBindTexture(This->glDescription.target, This->glDescription.textureName);
1174 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
1175 LEAVE_GL();
1176 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1177 /* drop through */
1179 case RTL_AUTO:
1180 case RTL_READDRAW:
1181 case RTL_TEXDRAW:
1182 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1183 break;
1186 if(!fullsurface) {
1187 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1188 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1189 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1190 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1191 * not fully up to date because only a subrectangle was read in LockRect.
1193 This->Flags &= ~SFLAG_INSYSMEM;
1194 This->Flags |= SFLAG_INDRAWABLE;
1197 This->dirtyRect.left = This->currentDesc.Width;
1198 This->dirtyRect.top = This->currentDesc.Height;
1199 This->dirtyRect.right = 0;
1200 This->dirtyRect.bottom = 0;
1201 } else if(iface == myDevice->stencilBufferTarget) {
1202 FIXME("Depth Stencil buffer locking is not implemented\n");
1203 } else {
1204 /* The rest should be a normal texture */
1205 IWineD3DBaseTextureImpl *impl;
1206 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1207 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1208 * states need resetting
1210 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1211 if(impl->baseTexture.bindCount) {
1212 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1214 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1218 unlock_end:
1219 This->Flags &= ~SFLAG_LOCKED;
1220 memset(&This->lockedRect, 0, sizeof(RECT));
1221 return WINED3D_OK;
1224 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1225 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1226 WINED3DLOCKED_RECT lock;
1227 HRESULT hr;
1228 RGBQUAD col[256];
1230 TRACE("(%p)->(%p)\n",This,pHDC);
1232 if(This->Flags & SFLAG_USERPTR) {
1233 ERR("Not supported on surfaces with an application-provided surfaces\n");
1234 return WINEDDERR_NODC;
1237 /* Give more detailed info for ddraw */
1238 if (This->Flags & SFLAG_DCINUSE)
1239 return WINEDDERR_DCALREADYCREATED;
1241 /* Can't GetDC if the surface is locked */
1242 if (This->Flags & SFLAG_LOCKED)
1243 return WINED3DERR_INVALIDCALL;
1245 memset(&lock, 0, sizeof(lock)); /* To be sure */
1247 /* Create a DIB section if there isn't a hdc yet */
1248 if(!This->hDC) {
1249 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1250 if(This->Flags & SFLAG_CLIENT) {
1251 IWineD3DSurface_PreLoad(iface);
1254 /* Use the dib section from now on if we are not using a PBO */
1255 if(!(This->Flags & SFLAG_PBO))
1256 This->resource.allocatedMemory = This->dib.bitmap_data;
1259 /* Lock the surface */
1260 hr = IWineD3DSurface_LockRect(iface,
1261 &lock,
1262 NULL,
1265 if(This->Flags & SFLAG_PBO) {
1266 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1267 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1270 if(FAILED(hr)) {
1271 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1272 /* keep the dib section */
1273 return hr;
1276 if(This->resource.format == WINED3DFMT_P8 ||
1277 This->resource.format == WINED3DFMT_A8P8) {
1278 unsigned int n;
1279 if(This->palette) {
1280 PALETTEENTRY ent[256];
1282 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1283 for (n=0; n<256; n++) {
1284 col[n].rgbRed = ent[n].peRed;
1285 col[n].rgbGreen = ent[n].peGreen;
1286 col[n].rgbBlue = ent[n].peBlue;
1287 col[n].rgbReserved = 0;
1289 } else {
1290 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1292 for (n=0; n<256; n++) {
1293 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1294 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1295 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1296 col[n].rgbReserved = 0;
1300 SetDIBColorTable(This->hDC, 0, 256, col);
1303 *pHDC = This->hDC;
1304 TRACE("returning %p\n",*pHDC);
1305 This->Flags |= SFLAG_DCINUSE;
1307 return WINED3D_OK;
1310 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1311 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1313 TRACE("(%p)->(%p)\n",This,hDC);
1315 if (!(This->Flags & SFLAG_DCINUSE))
1316 return WINED3DERR_INVALIDCALL;
1318 if (This->hDC !=hDC) {
1319 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1320 return WINED3DERR_INVALIDCALL;
1323 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1324 /* Copy the contents of the DIB over to the PBO */
1325 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1328 /* we locked first, so unlock now */
1329 IWineD3DSurface_UnlockRect(iface);
1331 This->Flags &= ~SFLAG_DCINUSE;
1333 return WINED3D_OK;
1336 /* ******************************************************
1337 IWineD3DSurface Internal (No mapping to directx api) parts follow
1338 ****************************************************** */
1340 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) {
1341 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1342 const GlPixelFormatDesc *glDesc;
1343 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1344 BOOL p8_render_target = FALSE;
1345 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1347 /* Default values: From the surface */
1348 *format = glDesc->glFormat;
1349 *type = glDesc->glType;
1350 *convert = NO_CONVERSION;
1351 *target_bpp = This->bytesPerPixel;
1353 if(srgb_mode) {
1354 *internal = glDesc->glGammaInternal;
1355 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1356 *internal = glDesc->rtInternal;
1357 } else {
1358 *internal = glDesc->glInternal;
1361 /* Ok, now look if we have to do any conversion */
1362 switch(This->resource.format) {
1363 case WINED3DFMT_P8:
1364 /* ****************
1365 Paletted Texture
1366 **************** */
1368 if (device->render_targets && device->render_targets[0]) {
1369 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
1370 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
1371 p8_render_target = TRUE;
1374 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1375 * of the two is available make sure texturing is requested as neither of the two works in
1376 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1377 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1378 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1379 * conflicts with this.
1381 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) && p8_render_target)) || colorkey_active || !use_texturing ) {
1382 *format = GL_RGBA;
1383 *internal = GL_RGBA;
1384 *type = GL_UNSIGNED_BYTE;
1385 *target_bpp = 4;
1386 if(colorkey_active) {
1387 *convert = CONVERT_PALETTED_CK;
1388 } else {
1389 *convert = CONVERT_PALETTED;
1392 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1393 *format = GL_RED;
1394 *internal = GL_RGBA;
1395 *type = GL_UNSIGNED_BYTE;
1396 *target_bpp = 1;
1399 break;
1401 case WINED3DFMT_R3G3B2:
1402 /* **********************
1403 GL_UNSIGNED_BYTE_3_3_2
1404 ********************** */
1405 if (colorkey_active) {
1406 /* This texture format will never be used.. So do not care about color keying
1407 up until the point in time it will be needed :-) */
1408 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1410 break;
1412 case WINED3DFMT_R5G6B5:
1413 if (colorkey_active) {
1414 *convert = CONVERT_CK_565;
1415 *format = GL_RGBA;
1416 *internal = GL_RGBA;
1417 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1419 break;
1421 case WINED3DFMT_X1R5G5B5:
1422 if (colorkey_active) {
1423 *convert = CONVERT_CK_5551;
1424 *format = GL_BGRA;
1425 *internal = GL_RGBA;
1426 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1428 break;
1430 case WINED3DFMT_R8G8B8:
1431 if (colorkey_active) {
1432 *convert = CONVERT_CK_RGB24;
1433 *format = GL_RGBA;
1434 *internal = GL_RGBA;
1435 *type = GL_UNSIGNED_INT_8_8_8_8;
1436 *target_bpp = 4;
1438 break;
1440 case WINED3DFMT_X8R8G8B8:
1441 if (colorkey_active) {
1442 *convert = CONVERT_RGB32_888;
1443 *format = GL_RGBA;
1444 *internal = GL_RGBA;
1445 *type = GL_UNSIGNED_INT_8_8_8_8;
1447 break;
1449 case WINED3DFMT_V8U8:
1450 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1451 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1452 *format = GL_DUDV_ATI;
1453 *internal = GL_DU8DV8_ATI;
1454 *type = GL_BYTE;
1455 /* No conversion - Just change the gl type */
1456 break;
1458 *convert = CONVERT_V8U8;
1459 *format = GL_BGR;
1460 *internal = GL_RGB8;
1461 *type = GL_UNSIGNED_BYTE;
1462 *target_bpp = 3;
1463 break;
1465 case WINED3DFMT_L6V5U5:
1466 *convert = CONVERT_L6V5U5;
1467 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1468 *target_bpp = 3;
1469 /* Use format and types from table */
1470 } else {
1471 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1472 *target_bpp = 2;
1473 *format = GL_RGB;
1474 *internal = GL_RGB5;
1475 *type = GL_UNSIGNED_SHORT_5_6_5;
1477 break;
1479 case WINED3DFMT_X8L8V8U8:
1480 *convert = CONVERT_X8L8V8U8;
1481 *target_bpp = 4;
1482 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1483 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1484 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1485 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1486 * the needed type and format parameter, so the internal format contains a
1487 * 4th component, which is returned as alpha
1489 } else {
1490 /* Not supported by GL_ATI_envmap_bumpmap */
1491 *format = GL_BGRA;
1492 *internal = GL_RGB8;
1493 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1495 break;
1497 case WINED3DFMT_Q8W8V8U8:
1498 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1499 *convert = CONVERT_Q8W8V8U8;
1500 *format = GL_BGRA;
1501 *internal = GL_RGBA8;
1502 *type = GL_UNSIGNED_BYTE;
1503 *target_bpp = 4;
1504 /* Not supported by GL_ATI_envmap_bumpmap */
1505 break;
1507 case WINED3DFMT_V16U16:
1508 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1509 *convert = CONVERT_V16U16;
1510 *format = GL_BGR;
1511 *internal = GL_RGB16_EXT;
1512 *type = GL_UNSIGNED_SHORT;
1513 *target_bpp = 6;
1514 /* What should I do here about GL_ATI_envmap_bumpmap?
1515 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1517 break;
1519 case WINED3DFMT_A4L4:
1520 /* A4L4 exists as an internal gl format, but for some reason there is not
1521 * format+type combination to load it. Thus convert it to A8L8, then load it
1522 * with A4L4 internal, but A8L8 format+type
1524 *convert = CONVERT_A4L4;
1525 *format = GL_LUMINANCE_ALPHA;
1526 *internal = GL_LUMINANCE4_ALPHA4;
1527 *type = GL_UNSIGNED_BYTE;
1528 *target_bpp = 2;
1529 break;
1531 case WINED3DFMT_R32F:
1532 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1533 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1534 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1535 * 1.0 instead.
1537 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1539 *convert = CONVERT_R32F;
1540 *format = GL_RGB;
1541 *internal = GL_RGB32F_ARB;
1542 *type = GL_FLOAT;
1543 *target_bpp = 12;
1544 break;
1546 case WINED3DFMT_R16F:
1547 /* Similar to R32F */
1548 *convert = CONVERT_R16F;
1549 *format = GL_RGB;
1550 *internal = GL_RGB16F_ARB;
1551 *type = GL_HALF_FLOAT_ARB;
1552 *target_bpp = 6;
1553 break;
1555 case WINED3DFMT_G16R16:
1556 *convert = CONVERT_G16R16;
1557 *format = GL_RGB;
1558 *internal = GL_RGB16_EXT;
1559 *type = GL_UNSIGNED_SHORT;
1560 *target_bpp = 6;
1561 break;
1563 default:
1564 break;
1567 return WINED3D_OK;
1570 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1571 BYTE *source, *dest;
1572 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1574 switch (convert) {
1575 case NO_CONVERSION:
1577 memcpy(dst, src, pitch * height);
1578 break;
1580 case CONVERT_PALETTED:
1581 case CONVERT_PALETTED_CK:
1583 IWineD3DPaletteImpl* pal = This->palette;
1584 BYTE table[256][4];
1585 unsigned int x, y;
1587 if( pal == NULL) {
1588 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1591 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1593 for (y = 0; y < height; y++)
1595 source = src + pitch * y;
1596 dest = dst + outpitch * y;
1597 /* This is an 1 bpp format, using the width here is fine */
1598 for (x = 0; x < width; x++) {
1599 BYTE color = *source++;
1600 *dest++ = table[color][0];
1601 *dest++ = table[color][1];
1602 *dest++ = table[color][2];
1603 *dest++ = table[color][3];
1607 break;
1609 case CONVERT_CK_565:
1611 /* Converting the 565 format in 5551 packed to emulate color-keying.
1613 Note : in all these conversion, it would be best to average the averaging
1614 pixels to get the color of the pixel that will be color-keyed to
1615 prevent 'color bleeding'. This will be done later on if ever it is
1616 too visible.
1618 Note2: Nvidia documents say that their driver does not support alpha + color keying
1619 on the same surface and disables color keying in such a case
1621 unsigned int x, y;
1622 WORD *Source;
1623 WORD *Dest;
1625 TRACE("Color keyed 565\n");
1627 for (y = 0; y < height; y++) {
1628 Source = (WORD *) (src + y * pitch);
1629 Dest = (WORD *) (dst + y * outpitch);
1630 for (x = 0; x < width; x++ ) {
1631 WORD color = *Source++;
1632 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1633 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1634 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1635 *Dest |= 0x0001;
1637 Dest++;
1641 break;
1643 case CONVERT_CK_5551:
1645 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1646 unsigned int x, y;
1647 WORD *Source;
1648 WORD *Dest;
1649 TRACE("Color keyed 5551\n");
1650 for (y = 0; y < height; y++) {
1651 Source = (WORD *) (src + y * pitch);
1652 Dest = (WORD *) (dst + y * outpitch);
1653 for (x = 0; x < width; x++ ) {
1654 WORD color = *Source++;
1655 *Dest = color;
1656 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1657 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1658 *Dest |= (1 << 15);
1660 else {
1661 *Dest &= ~(1 << 15);
1663 Dest++;
1667 break;
1669 case CONVERT_V8U8:
1671 unsigned int x, y;
1672 short *Source;
1673 unsigned char *Dest;
1674 for(y = 0; y < height; y++) {
1675 Source = (short *) (src + y * pitch);
1676 Dest = dst + y * outpitch;
1677 for (x = 0; x < width; x++ ) {
1678 long color = (*Source++);
1679 /* B */ Dest[0] = 0xff;
1680 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1681 /* R */ Dest[2] = (color) + 128; /* U */
1682 Dest += 3;
1685 break;
1688 case CONVERT_V16U16:
1690 unsigned int x, y;
1691 DWORD *Source;
1692 unsigned short *Dest;
1693 for(y = 0; y < height; y++) {
1694 Source = (DWORD *) (src + y * pitch);
1695 Dest = (unsigned short *) (dst + y * outpitch);
1696 for (x = 0; x < width; x++ ) {
1697 DWORD color = (*Source++);
1698 /* B */ Dest[0] = 0xffff;
1699 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1700 /* R */ Dest[2] = (color ) + 32768; /* U */
1701 Dest += 3;
1704 break;
1707 case CONVERT_Q8W8V8U8:
1709 unsigned int x, y;
1710 DWORD *Source;
1711 unsigned char *Dest;
1712 for(y = 0; y < height; y++) {
1713 Source = (DWORD *) (src + y * pitch);
1714 Dest = dst + y * outpitch;
1715 for (x = 0; x < width; x++ ) {
1716 long color = (*Source++);
1717 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1718 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1719 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1720 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1721 Dest += 4;
1724 break;
1727 case CONVERT_L6V5U5:
1729 unsigned int x, y;
1730 WORD *Source;
1731 unsigned char *Dest;
1733 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1734 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1735 * fixed function and shaders without further conversion once the surface is
1736 * loaded
1738 for(y = 0; y < height; y++) {
1739 Source = (WORD *) (src + y * pitch);
1740 Dest = dst + y * outpitch;
1741 for (x = 0; x < width; x++ ) {
1742 short color = (*Source++);
1743 unsigned char l = ((color >> 10) & 0xfc);
1744 char v = ((color >> 5) & 0x3e);
1745 char u = ((color ) & 0x1f);
1747 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1748 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1749 * shift. GL reads a signed value and converts it into an unsigned value.
1751 /* M */ Dest[2] = l << 1;
1753 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1754 * from 5 bit values to 8 bit values.
1756 /* V */ Dest[1] = v << 3;
1757 /* U */ Dest[0] = u << 3;
1758 Dest += 3;
1761 } else {
1762 for(y = 0; y < height; y++) {
1763 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1764 Source = (WORD *) (src + y * pitch);
1765 for (x = 0; x < width; x++ ) {
1766 short color = (*Source++);
1767 unsigned char l = ((color >> 10) & 0xfc);
1768 short v = ((color >> 5) & 0x3e);
1769 short u = ((color ) & 0x1f);
1770 short v_conv = v + 16;
1771 short u_conv = u + 16;
1773 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1774 Dest_s += 1;
1778 break;
1781 case CONVERT_X8L8V8U8:
1783 unsigned int x, y;
1784 DWORD *Source;
1785 unsigned char *Dest;
1787 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1788 /* This implementation works with the fixed function pipeline and shaders
1789 * without further modification after converting the surface.
1791 for(y = 0; y < height; y++) {
1792 Source = (DWORD *) (src + y * pitch);
1793 Dest = dst + y * outpitch;
1794 for (x = 0; x < width; x++ ) {
1795 long color = (*Source++);
1796 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1797 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1798 /* U */ Dest[0] = (color & 0xff); /* U */
1799 /* I */ Dest[3] = 255; /* X */
1800 Dest += 4;
1803 } else {
1804 /* Doesn't work correctly with the fixed function pipeline, but can work in
1805 * shaders if the shader is adjusted. (There's no use for this format in gl's
1806 * standard fixed function pipeline anyway).
1808 for(y = 0; y < height; y++) {
1809 Source = (DWORD *) (src + y * pitch);
1810 Dest = dst + y * outpitch;
1811 for (x = 0; x < width; x++ ) {
1812 long color = (*Source++);
1813 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1814 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1815 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1816 Dest += 4;
1820 break;
1823 case CONVERT_A4L4:
1825 unsigned int x, y;
1826 unsigned char *Source;
1827 unsigned char *Dest;
1828 for(y = 0; y < height; y++) {
1829 Source = src + y * pitch;
1830 Dest = dst + y * outpitch;
1831 for (x = 0; x < width; x++ ) {
1832 unsigned char color = (*Source++);
1833 /* A */ Dest[1] = (color & 0xf0) << 0;
1834 /* L */ Dest[0] = (color & 0x0f) << 4;
1835 Dest += 2;
1838 break;
1841 case CONVERT_R32F:
1843 unsigned int x, y;
1844 float *Source;
1845 float *Dest;
1846 for(y = 0; y < height; y++) {
1847 Source = (float *) (src + y * pitch);
1848 Dest = (float *) (dst + y * outpitch);
1849 for (x = 0; x < width; x++ ) {
1850 float color = (*Source++);
1851 Dest[0] = color;
1852 Dest[1] = 1.0;
1853 Dest[2] = 1.0;
1854 Dest += 3;
1857 break;
1860 case CONVERT_R16F:
1862 unsigned int x, y;
1863 WORD *Source;
1864 WORD *Dest;
1865 WORD one = 0x3c00;
1866 for(y = 0; y < height; y++) {
1867 Source = (WORD *) (src + y * pitch);
1868 Dest = (WORD *) (dst + y * outpitch);
1869 for (x = 0; x < width; x++ ) {
1870 WORD color = (*Source++);
1871 Dest[0] = color;
1872 Dest[1] = one;
1873 Dest[2] = one;
1874 Dest += 3;
1877 break;
1880 case CONVERT_G16R16:
1882 unsigned int x, y;
1883 WORD *Source;
1884 WORD *Dest;
1886 for(y = 0; y < height; y++) {
1887 Source = (WORD *) (src + y * pitch);
1888 Dest = (WORD *) (dst + y * outpitch);
1889 for (x = 0; x < width; x++ ) {
1890 WORD green = (*Source++);
1891 WORD red = (*Source++);
1892 Dest[0] = green;
1893 Dest[1] = red;
1894 Dest[2] = 0xffff;
1895 Dest += 3;
1898 break;
1901 default:
1902 ERR("Unsupported conversation type %d\n", convert);
1904 return WINED3D_OK;
1907 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
1908 IWineD3DPaletteImpl* pal = This->palette;
1909 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1910 BOOL index_in_alpha = FALSE;
1911 int i;
1913 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1914 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1915 * is slow. Further RGB->P8 conversion is not possible because palettes can have
1916 * duplicate entries. Store the color key in the unused alpha component to speed the
1917 * download up and to make conversion unneeded. */
1918 if (device->render_targets && device->render_targets[0]) {
1919 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
1921 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
1922 index_in_alpha = TRUE;
1925 if (pal == NULL) {
1926 /* Still no palette? Use the device's palette */
1927 /* Get the surface's palette */
1928 for (i = 0; i < 256; i++) {
1929 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1930 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1931 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1933 /* BltOverride uses a GL_ALPHA_TEST based on GL_NOT_EQUAL 0, so the alpha component
1934 of pixels that should be masked away should be 0. When inde_in_alpha is set,
1935 we will store the palette index (the glReadPixels code reads GL_ALPHA back)
1936 or else we store 0xff. */
1937 if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1938 table[i][3] = 0;
1939 } else if(index_in_alpha) {
1940 table[i][3] = i;
1941 } else {
1942 table[i][3] = 0xFF;
1945 } else {
1946 TRACE("Using surface palette %p\n", pal);
1947 /* Get the surface's palette */
1948 for (i = 0; i < 256; i++) {
1949 table[i][0] = pal->palents[i].peRed;
1950 table[i][1] = pal->palents[i].peGreen;
1951 table[i][2] = pal->palents[i].peBlue;
1953 /* BltOverride uses a GL_ALPHA_TEST based on GL_NOT_EQUAL 0, so the alpha component
1954 of pixels that should be masked away should be 0. When inde_in_alpha is set,
1955 we will store the palette index (the glReadPixels code reads GL_ALPHA back)
1956 or else we store 0xff. */
1957 if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1958 table[i][3] = 0x00;
1959 } else if(index_in_alpha) {
1960 table[i][3] = i;
1961 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
1962 table[i][3] = pal->palents[i].peFlags;
1963 } else {
1964 table[i][3] = 0xFF;
1970 const char *fragment_palette_conversion =
1971 "!!ARBfp1.0\n"
1972 "TEMP index;\n"
1973 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
1974 "TEX index.x, fragment.texcoord[0], texture[0], 2D;\n" /* store the red-component of the current pixel */
1975 "MAD index.x, index.x, 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 */
1976 "TEX result.color, index, texture[1], 1D;\n" /* use the red-component as a index in the palette to get the final color */
1977 "END";
1979 /* This function is used in case of 8bit paletted textures to upload the palette.
1980 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
1981 extensions like ATI_fragment_shaders is possible.
1983 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
1984 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1985 BYTE table[256][4];
1986 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1988 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1990 /* Try to use the paletted texture extension */
1991 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
1993 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
1994 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
1996 else
1998 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
1999 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2000 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2002 /* Create the fragment program if we don't have it */
2003 if(!device->paletteConversionShader)
2005 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2006 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2007 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2008 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2009 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2012 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2013 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2015 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2016 glEnable(GL_TEXTURE_1D);
2017 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2019 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2020 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2021 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2022 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2024 /* Switch back to unit 0 in which the 2D texture will be stored. */
2025 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2027 /* Rebind the texture because it isn't bound anymore */
2028 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2032 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2033 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2035 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2036 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2037 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2039 return FALSE;
2042 if(This->palette9) {
2043 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2044 return FALSE;
2046 } else {
2047 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2049 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2050 return TRUE;
2053 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2054 GLboolean oldwrite[4];
2056 /* Some formats have only some color channels, and the others are 1.0.
2057 * since our rendering renders to all channels, and those pixel formats
2058 * are emulated by using a full texture with the other channels set to 1.0
2059 * manually, clear the unused channels.
2061 * This could be done with hacking colorwriteenable to mask the colors,
2062 * but before drawing the buffer would have to be cleared too, so there's
2063 * no gain in that
2065 switch(This->resource.format) {
2066 case WINED3DFMT_R16F:
2067 case WINED3DFMT_R32F:
2068 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2069 /* Do not activate a context, the correct drawable is active already
2070 * though just the read buffer is set, make sure to have the correct draw
2071 * buffer too
2073 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2074 glDisable(GL_SCISSOR_TEST);
2075 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2076 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2077 glClearColor(0.0, 1.0, 1.0, 1.0);
2078 glClear(GL_COLOR_BUFFER_BIT);
2079 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2080 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2081 checkGLcall("Unused channel clear\n");
2082 break;
2084 default: break;
2088 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2089 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2091 if (!(This->Flags & SFLAG_INTEXTURE)) {
2092 TRACE("Reloading because surface is dirty\n");
2093 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2094 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2095 /* Reload: vice versa OR */
2096 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2097 /* Also reload: Color key is active AND the color key has changed */
2098 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2099 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2100 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2101 TRACE("Reloading because of color keying\n");
2102 /* To perform the color key conversion we need a sysmem copy of
2103 * the surface. Make sure we have it
2106 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2107 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2108 /* TODO: This is not necessarily needed with hw palettized texture support */
2109 This->Flags &= ~SFLAG_INTEXTURE;
2110 } else if(palette9_changed(This)) {
2111 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2112 /* TODO: This is not necessarily needed with hw palettized texture support */
2113 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2115 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2116 This->Flags &= ~SFLAG_INTEXTURE;
2117 } else {
2118 TRACE("surface is already in texture\n");
2119 return WINED3D_OK;
2122 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2123 * These resources are not bound by device size or format restrictions. Because of this,
2124 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2125 * However, these resources can always be created, locked, and copied.
2127 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2129 FIXME("(%p) Operation not supported for scratch textures\n",This);
2130 return WINED3DERR_INVALIDCALL;
2133 This->srgb = srgb_mode;
2134 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2136 #if 0
2138 static unsigned int gen = 0;
2139 char buffer[4096];
2140 ++gen;
2141 if ((gen % 10) == 0) {
2142 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2143 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2146 * debugging crash code
2147 if (gen == 250) {
2148 void** test = NULL;
2149 *test = 0;
2153 #endif
2155 if (!(This->Flags & SFLAG_DONOTFREE)) {
2156 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2157 This->resource.allocatedMemory = NULL;
2158 This->resource.heapMemory = NULL;
2159 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2162 return WINED3D_OK;
2165 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2166 /* TODO: check for locks */
2167 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2168 IWineD3DBaseTexture *baseTexture = NULL;
2169 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2171 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2172 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2173 TRACE("Passing to container\n");
2174 IWineD3DBaseTexture_BindTexture(baseTexture);
2175 IWineD3DBaseTexture_Release(baseTexture);
2176 } else {
2177 TRACE("(%p) : Binding surface\n", This);
2179 if(!device->isInDraw) {
2180 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2182 ENTER_GL();
2183 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2184 LEAVE_GL();
2186 return;
2189 #include <errno.h>
2190 #include <stdio.h>
2191 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2192 FILE* f = NULL;
2193 UINT i, y;
2194 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2195 char *allocatedMemory;
2196 char *textureRow;
2197 IWineD3DSwapChain *swapChain = NULL;
2198 int width, height;
2199 GLuint tmpTexture = 0;
2200 DWORD color;
2201 /*FIXME:
2202 Textures may not be stored in ->allocatedgMemory and a GlTexture
2203 so we should lock the surface before saving a snapshot, or at least check that
2205 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2206 by calling GetTexImage and in compressed form by calling
2207 GetCompressedTexImageARB. Queried compressed images can be saved and
2208 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2209 texture images do not need to be processed by the GL and should
2210 significantly improve texture loading performance relative to uncompressed
2211 images. */
2213 /* Setup the width and height to be the internal texture width and height. */
2214 width = This->pow2Width;
2215 height = This->pow2Height;
2216 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2217 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2219 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2220 /* if were not a real texture then read the back buffer into a real texture */
2221 /* we don't want to interfere with the back buffer so read the data into a temporary
2222 * texture and then save the data out of the temporary texture
2224 GLint prevRead;
2225 ENTER_GL();
2226 TRACE("(%p) Reading render target into texture\n", This);
2227 glEnable(GL_TEXTURE_2D);
2229 glGenTextures(1, &tmpTexture);
2230 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2232 glTexImage2D(GL_TEXTURE_2D,
2234 GL_RGBA,
2235 width,
2236 height,
2237 0/*border*/,
2238 GL_RGBA,
2239 GL_UNSIGNED_INT_8_8_8_8_REV,
2240 NULL);
2242 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2243 vcheckGLcall("glGetIntegerv");
2244 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2245 vcheckGLcall("glReadBuffer");
2246 glCopyTexImage2D(GL_TEXTURE_2D,
2248 GL_RGBA,
2251 width,
2252 height,
2255 checkGLcall("glCopyTexImage2D");
2256 glReadBuffer(prevRead);
2257 LEAVE_GL();
2259 } else { /* bind the real texture, and make sure it up to date */
2260 IWineD3DSurface_PreLoad(iface);
2262 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2263 ENTER_GL();
2264 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2265 glGetTexImage(GL_TEXTURE_2D,
2266 This->glDescription.level,
2267 GL_RGBA,
2268 GL_UNSIGNED_INT_8_8_8_8_REV,
2269 allocatedMemory);
2270 checkGLcall("glTexImage2D");
2271 if (tmpTexture) {
2272 glBindTexture(GL_TEXTURE_2D, 0);
2273 glDeleteTextures(1, &tmpTexture);
2275 LEAVE_GL();
2277 f = fopen(filename, "w+");
2278 if (NULL == f) {
2279 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2280 return WINED3DERR_INVALIDCALL;
2282 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2283 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2284 /* TGA header */
2285 fputc(0,f);
2286 fputc(0,f);
2287 fputc(2,f);
2288 fputc(0,f);
2289 fputc(0,f);
2290 fputc(0,f);
2291 fputc(0,f);
2292 fputc(0,f);
2293 fputc(0,f);
2294 fputc(0,f);
2295 fputc(0,f);
2296 fputc(0,f);
2297 /* short width*/
2298 fwrite(&width,2,1,f);
2299 /* short height */
2300 fwrite(&height,2,1,f);
2301 /* format rgba */
2302 fputc(0x20,f);
2303 fputc(0x28,f);
2304 /* raw data */
2305 /* 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 */
2306 if(swapChain)
2307 textureRow = allocatedMemory + (width * (height - 1) *4);
2308 else
2309 textureRow = allocatedMemory;
2310 for (y = 0 ; y < height; y++) {
2311 for (i = 0; i < width; i++) {
2312 color = *((DWORD*)textureRow);
2313 fputc((color >> 16) & 0xFF, f); /* B */
2314 fputc((color >> 8) & 0xFF, f); /* G */
2315 fputc((color >> 0) & 0xFF, f); /* R */
2316 fputc((color >> 24) & 0xFF, f); /* A */
2317 textureRow += 4;
2319 /* take two rows of the pointer to the texture memory */
2320 if(swapChain)
2321 (textureRow-= width << 3);
2324 TRACE("Closing file\n");
2325 fclose(f);
2327 if(swapChain) {
2328 IWineD3DSwapChain_Release(swapChain);
2330 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2331 return WINED3D_OK;
2335 * Slightly inefficient way to handle multiple dirty rects but it works :)
2337 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2338 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2339 IWineD3DBaseTexture *baseTexture = NULL;
2341 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2342 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2344 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2345 if (NULL != pDirtyRect) {
2346 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2347 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2348 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2349 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2350 } else {
2351 This->dirtyRect.left = 0;
2352 This->dirtyRect.top = 0;
2353 This->dirtyRect.right = This->currentDesc.Width;
2354 This->dirtyRect.bottom = This->currentDesc.Height;
2356 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2357 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2358 /* if the container is a basetexture then mark it dirty. */
2359 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2360 TRACE("Passing to container\n");
2361 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2362 IWineD3DBaseTexture_Release(baseTexture);
2364 return WINED3D_OK;
2367 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2368 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2369 HRESULT hr;
2370 const GlPixelFormatDesc *glDesc;
2371 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2373 TRACE("(%p) : Calling base function first\n", This);
2374 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2375 if(SUCCEEDED(hr)) {
2376 /* Setup some glformat defaults */
2377 This->glDescription.glFormat = glDesc->glFormat;
2378 This->glDescription.glFormatInternal = glDesc->glInternal;
2379 This->glDescription.glType = glDesc->glType;
2381 This->Flags &= ~SFLAG_ALLOCATED;
2382 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2383 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2385 return hr;
2388 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2389 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2391 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2392 WARN("Surface is locked or the HDC is in use\n");
2393 return WINED3DERR_INVALIDCALL;
2396 if(Mem && Mem != This->resource.allocatedMemory) {
2397 void *release = NULL;
2399 /* Do I have to copy the old surface content? */
2400 if(This->Flags & SFLAG_DIBSECTION) {
2401 /* Release the DC. No need to hold the critical section for the update
2402 * Thread because this thread runs only on front buffers, but this method
2403 * fails for render targets in the check above.
2405 SelectObject(This->hDC, This->dib.holdbitmap);
2406 DeleteDC(This->hDC);
2407 /* Release the DIB section */
2408 DeleteObject(This->dib.DIBsection);
2409 This->dib.bitmap_data = NULL;
2410 This->resource.allocatedMemory = NULL;
2411 This->hDC = NULL;
2412 This->Flags &= ~SFLAG_DIBSECTION;
2413 } else if(!(This->Flags & SFLAG_USERPTR)) {
2414 release = This->resource.heapMemory;
2415 This->resource.heapMemory = NULL;
2417 This->resource.allocatedMemory = Mem;
2418 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2420 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2421 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2423 /* For client textures opengl has to be notified */
2424 if(This->Flags & SFLAG_CLIENT) {
2425 This->Flags &= ~SFLAG_ALLOCATED;
2426 IWineD3DSurface_PreLoad(iface);
2427 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2430 /* Now free the old memory if any */
2431 HeapFree(GetProcessHeap(), 0, release);
2432 } else if(This->Flags & SFLAG_USERPTR) {
2433 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2434 This->resource.allocatedMemory = NULL;
2435 /* HeapMemory should be NULL already */
2436 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2437 This->Flags &= ~SFLAG_USERPTR;
2439 if(This->Flags & SFLAG_CLIENT) {
2440 This->Flags &= ~SFLAG_ALLOCATED;
2441 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2442 IWineD3DSurface_PreLoad(iface);
2445 return WINED3D_OK;
2448 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2449 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2450 IWineD3DSwapChainImpl *swapchain = NULL;
2451 HRESULT hr;
2452 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2454 /* Flipping is only supported on RenderTargets */
2455 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2457 if(override) {
2458 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2459 * FIXME("(%p) Target override is not supported by now\n", This);
2460 * Additionally, it isn't really possible to support triple-buffering
2461 * properly on opengl at all
2465 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2466 if(!swapchain) {
2467 ERR("Flipped surface is not on a swapchain\n");
2468 return WINEDDERR_NOTFLIPPABLE;
2471 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2472 * and only d3d8 and d3d9 apps specify the presentation interval
2474 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2475 /* Most common case first to avoid wasting time on all the other cases */
2476 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2477 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2478 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2479 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2480 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2481 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2482 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2483 } else {
2484 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2487 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2488 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2489 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2490 return hr;
2493 /* Does a direct frame buffer -> texture copy. Stretching is done
2494 * with single pixel copy calls
2496 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2497 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2498 float xrel, yrel;
2499 UINT row;
2500 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2503 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2504 ENTER_GL();
2505 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2507 /* TODO: Do we need GL_TEXTURE_2D enabled fpr copyteximage? */
2508 glEnable(This->glDescription.target);
2509 checkGLcall("glEnable(This->glDescription.target)");
2511 /* Bind the target texture */
2512 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2513 checkGLcall("glBindTexture");
2514 if(!swapchain) {
2515 glReadBuffer(myDevice->offscreenBuffer);
2516 } else {
2517 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2518 glReadBuffer(buffer);
2520 checkGLcall("glReadBuffer");
2522 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2523 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2525 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2526 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2528 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2529 ERR("Texture filtering not supported in direct blit\n");
2531 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2532 ERR("Texture filtering not supported in direct blit\n");
2535 if(upsidedown &&
2536 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2537 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2538 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2540 glCopyTexSubImage2D(This->glDescription.target,
2541 This->glDescription.level,
2542 drect->x1, drect->y1, /* xoffset, yoffset */
2543 srect->x1, Src->currentDesc.Height - srect->y2,
2544 drect->x2 - drect->x1, drect->y2 - drect->y1);
2545 } else {
2546 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2547 /* I have to process this row by row to swap the image,
2548 * otherwise it would be upside down, so stretching in y direction
2549 * doesn't cost extra time
2551 * However, stretching in x direction can be avoided if not necessary
2553 for(row = drect->y1; row < drect->y2; row++) {
2554 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2555 /* Well, that stuff works, but it's very slow.
2556 * find a better way instead
2558 UINT col;
2560 for(col = drect->x1; col < drect->x2; col++) {
2561 glCopyTexSubImage2D(This->glDescription.target,
2562 This->glDescription.level,
2563 drect->x1 + col, row, /* xoffset, yoffset */
2564 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2565 1, 1);
2567 } else {
2568 glCopyTexSubImage2D(This->glDescription.target,
2569 This->glDescription.level,
2570 drect->x1, row, /* xoffset, yoffset */
2571 srect->x1, yoffset - (int) (row * yrel),
2572 drect->x2-drect->x1, 1);
2576 vcheckGLcall("glCopyTexSubImage2D");
2578 /* Leave the opengl state valid for blitting */
2579 glDisable(This->glDescription.target);
2580 checkGLcall("glDisable(This->glDescription.target)");
2582 LEAVE_GL();
2585 /* Uses the hardware to stretch and flip the image */
2586 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2587 GLuint src, backup = 0;
2588 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2589 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2590 float left, right, top, bottom; /* Texture coordinates */
2591 UINT fbwidth = Src->currentDesc.Width;
2592 UINT fbheight = Src->currentDesc.Height;
2593 GLenum drawBuffer = GL_BACK;
2594 GLenum texture_target;
2596 TRACE("Using hwstretch blit\n");
2597 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2598 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2599 ENTER_GL();
2601 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2603 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2604 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2606 if(GL_LIMITS(aux_buffers) >= 2) {
2607 /* Got more than one aux buffer? Use the 2nd aux buffer */
2608 drawBuffer = GL_AUX1;
2609 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2610 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2611 drawBuffer = GL_AUX0;
2614 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2615 glGenTextures(1, &backup);
2616 checkGLcall("glGenTextures\n");
2617 glBindTexture(GL_TEXTURE_2D, backup);
2618 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2619 texture_target = GL_TEXTURE_2D;
2620 } else {
2621 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2622 * we are reading from the back buffer, the backup can be used as source texture
2624 if(Src->glDescription.textureName == 0) {
2625 /* Get it a description */
2626 IWineD3DSurface_PreLoad(SrcSurface);
2628 texture_target = Src->glDescription.target;
2629 glBindTexture(texture_target, Src->glDescription.textureName);
2630 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2631 glEnable(texture_target);
2632 checkGLcall("glEnable(texture_target)");
2634 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2635 Src->Flags &= ~SFLAG_INTEXTURE;
2638 glReadBuffer(GL_BACK);
2639 checkGLcall("glReadBuffer(GL_BACK)");
2641 /* TODO: Only back up the part that will be overwritten */
2642 glCopyTexSubImage2D(texture_target, 0,
2643 0, 0 /* read offsets */,
2644 0, 0,
2645 fbwidth,
2646 fbheight);
2648 checkGLcall("glCopyTexSubImage2D");
2650 /* No issue with overriding these - the sampler is dirty due to blit usage */
2651 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2652 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2653 checkGLcall("glTexParameteri");
2654 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2655 minMipLookup[Filter][WINED3DTEXF_NONE]);
2656 checkGLcall("glTexParameteri");
2658 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2659 src = backup ? backup : Src->glDescription.textureName;
2660 } else {
2661 glReadBuffer(GL_FRONT);
2662 checkGLcall("glReadBuffer(GL_FRONT)");
2664 glGenTextures(1, &src);
2665 checkGLcall("glGenTextures(1, &src)");
2666 glBindTexture(GL_TEXTURE_2D, src);
2667 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2669 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2670 * out for power of 2 sizes
2672 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2673 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2674 checkGLcall("glTexImage2D");
2675 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2676 0, 0 /* read offsets */,
2677 0, 0,
2678 fbwidth,
2679 fbheight);
2681 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2682 checkGLcall("glTexParameteri");
2683 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2684 checkGLcall("glTexParameteri");
2686 glReadBuffer(GL_BACK);
2687 checkGLcall("glReadBuffer(GL_BACK)");
2689 if(texture_target != GL_TEXTURE_2D) {
2690 glDisable(texture_target);
2691 glEnable(GL_TEXTURE_2D);
2692 texture_target = GL_TEXTURE_2D;
2695 checkGLcall("glEnd and previous");
2697 left = (float) srect->x1 / (float) Src->pow2Width;
2698 right = (float) srect->x2 / (float) Src->pow2Width;
2700 if(upsidedown) {
2701 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2702 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2703 } else {
2704 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2705 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2708 /* draw the source texture stretched and upside down. The correct surface is bound already */
2709 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
2710 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
2712 glDrawBuffer(drawBuffer);
2713 glReadBuffer(drawBuffer);
2715 glBegin(GL_QUADS);
2716 /* bottom left */
2717 glTexCoord2f(left, bottom);
2718 glVertex2i(0, fbheight);
2720 /* top left */
2721 glTexCoord2f(left, top);
2722 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2724 /* top right */
2725 glTexCoord2f(right, top);
2726 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2728 /* bottom right */
2729 glTexCoord2f(right, bottom);
2730 glVertex2i(drect->x2 - drect->x1, fbheight);
2731 glEnd();
2732 checkGLcall("glEnd and previous");
2734 if(texture_target != This->glDescription.target) {
2735 glDisable(texture_target);
2736 glEnable(This->glDescription.target);
2737 texture_target = This->glDescription.target;
2740 /* Now read the stretched and upside down image into the destination texture */
2741 glBindTexture(texture_target, This->glDescription.textureName);
2742 checkGLcall("glBindTexture");
2743 glCopyTexSubImage2D(texture_target,
2745 drect->x1, drect->y1, /* xoffset, yoffset */
2746 0, 0, /* We blitted the image to the origin */
2747 drect->x2 - drect->x1, drect->y2 - drect->y1);
2748 checkGLcall("glCopyTexSubImage2D");
2750 if(drawBuffer == GL_BACK) {
2751 /* Write the back buffer backup back */
2752 if(backup) {
2753 if(texture_target != GL_TEXTURE_2D) {
2754 glDisable(texture_target);
2755 glEnable(GL_TEXTURE_2D);
2756 texture_target = GL_TEXTURE_2D;
2758 glBindTexture(GL_TEXTURE_2D, backup);
2759 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
2760 } else {
2761 if(texture_target != Src->glDescription.target) {
2762 glDisable(texture_target);
2763 glEnable(Src->glDescription.target);
2764 texture_target = Src->glDescription.target;
2766 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
2767 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2770 glBegin(GL_QUADS);
2771 /* top left */
2772 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2773 glVertex2i(0, 0);
2775 /* bottom left */
2776 glTexCoord2f(0.0, 0.0);
2777 glVertex2i(0, fbheight);
2779 /* bottom right */
2780 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2781 glVertex2i(fbwidth, Src->currentDesc.Height);
2783 /* top right */
2784 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2785 glVertex2i(fbwidth, 0);
2786 glEnd();
2787 } else {
2788 /* Restore the old draw buffer */
2789 glDrawBuffer(GL_BACK);
2791 glDisable(texture_target);
2792 checkGLcall("glDisable(texture_target)");
2794 /* Cleanup */
2795 if(src != Src->glDescription.textureName && src != backup) {
2796 glDeleteTextures(1, &src);
2797 checkGLcall("glDeleteTextures(1, &src)");
2799 if(backup) {
2800 glDeleteTextures(1, &backup);
2801 checkGLcall("glDeleteTextures(1, &backup)");
2804 LEAVE_GL();
2807 /* Not called from the VTable */
2808 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2809 WINED3DRECT rect;
2810 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2811 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2812 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2814 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2816 /* Get the swapchain. One of the surfaces has to be a primary surface */
2817 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2818 WARN("Destination is in sysmem, rejecting gl blt\n");
2819 return WINED3DERR_INVALIDCALL;
2821 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2822 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2823 if(Src) {
2824 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2825 WARN("Src is in sysmem, rejecting gl blt\n");
2826 return WINED3DERR_INVALIDCALL;
2828 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2829 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2832 /* Early sort out of cases where no render target is used */
2833 if(!dstSwapchain && !srcSwapchain &&
2834 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2835 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2836 return WINED3DERR_INVALIDCALL;
2839 /* No destination color keying supported */
2840 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2841 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2842 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2843 return WINED3DERR_INVALIDCALL;
2846 if (DestRect) {
2847 rect.x1 = DestRect->left;
2848 rect.y1 = DestRect->top;
2849 rect.x2 = DestRect->right;
2850 rect.y2 = DestRect->bottom;
2851 } else {
2852 rect.x1 = 0;
2853 rect.y1 = 0;
2854 rect.x2 = This->currentDesc.Width;
2855 rect.y2 = This->currentDesc.Height;
2858 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2859 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2860 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2861 /* Half-life does a Blt from the back buffer to the front buffer,
2862 * Full surface size, no flags... Use present instead
2864 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2867 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2868 while(1)
2870 RECT mySrcRect;
2871 TRACE("Looking if a Present can be done...\n");
2872 /* Source Rectangle must be full surface */
2873 if( SrcRect ) {
2874 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2875 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2876 TRACE("No, Source rectangle doesn't match\n");
2877 break;
2880 mySrcRect.left = 0;
2881 mySrcRect.top = 0;
2882 mySrcRect.right = Src->currentDesc.Width;
2883 mySrcRect.bottom = Src->currentDesc.Height;
2885 /* No stretching may occur */
2886 if(mySrcRect.right != rect.x2 - rect.x1 ||
2887 mySrcRect.bottom != rect.y2 - rect.y1) {
2888 TRACE("No, stretching is done\n");
2889 break;
2892 /* Destination must be full surface or match the clipping rectangle */
2893 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
2895 RECT cliprect;
2896 POINT pos[2];
2897 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
2898 pos[0].x = rect.x1;
2899 pos[0].y = rect.y1;
2900 pos[1].x = rect.x2;
2901 pos[1].y = rect.y2;
2902 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
2903 pos, 2);
2905 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
2906 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
2908 TRACE("No, dest rectangle doesn't match(clipper)\n");
2909 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
2910 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
2911 break;
2914 else
2916 if(rect.x1 != 0 || rect.y1 != 0 ||
2917 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
2918 TRACE("No, dest rectangle doesn't match(surface size)\n");
2919 break;
2923 TRACE("Yes\n");
2925 /* These flags are unimportant for the flag check, remove them */
2926 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
2927 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2929 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2930 * take very long, while a flip is fast.
2931 * This applies to Half-Life, which does such Blts every time it finished
2932 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2933 * menu. This is also used by all apps when they do windowed rendering
2935 * The problem is that flipping is not really the same as copying. After a
2936 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2937 * untouched. Therefore it's necessary to override the swap effect
2938 * and to set it back after the flip.
2940 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
2941 * testcases.
2944 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2945 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2947 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2948 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
2950 dstSwapchain->presentParms.SwapEffect = orig_swap;
2952 return WINED3D_OK;
2954 break;
2957 TRACE("Unsupported blit between buffers on the same swapchain\n");
2958 return WINED3DERR_INVALIDCALL;
2959 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
2960 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
2961 return WINED3DERR_INVALIDCALL;
2962 } else if(dstSwapchain && srcSwapchain) {
2963 FIXME("Implement hardware blit between two different swapchains\n");
2964 return WINED3DERR_INVALIDCALL;
2965 } else if(dstSwapchain) {
2966 if(SrcSurface == myDevice->render_targets[0]) {
2967 TRACE("Blit from active render target to a swapchain\n");
2968 /* Handled with regular texture -> swapchain blit */
2970 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2971 FIXME("Implement blit from a swapchain to the active render target\n");
2972 return WINED3DERR_INVALIDCALL;
2975 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
2976 /* Blit from render target to texture */
2977 WINED3DRECT srect;
2978 BOOL upsideDown, stretchx;
2980 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2981 TRACE("Color keying not supported by frame buffer to texture blit\n");
2982 return WINED3DERR_INVALIDCALL;
2983 /* Destination color key is checked above */
2986 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2987 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2989 if(SrcRect) {
2990 if(SrcRect->top < SrcRect->bottom) {
2991 srect.y1 = SrcRect->top;
2992 srect.y2 = SrcRect->bottom;
2993 upsideDown = FALSE;
2994 } else {
2995 srect.y1 = SrcRect->bottom;
2996 srect.y2 = SrcRect->top;
2997 upsideDown = TRUE;
2999 srect.x1 = SrcRect->left;
3000 srect.x2 = SrcRect->right;
3001 } else {
3002 srect.x1 = 0;
3003 srect.y1 = 0;
3004 srect.x2 = Src->currentDesc.Width;
3005 srect.y2 = Src->currentDesc.Height;
3006 upsideDown = FALSE;
3008 if(rect.x1 > rect.x2) {
3009 UINT tmp = rect.x2;
3010 rect.x2 = rect.x1;
3011 rect.x1 = tmp;
3012 upsideDown = !upsideDown;
3014 if(!srcSwapchain) {
3015 TRACE("Reading from an offscreen target\n");
3016 upsideDown = !upsideDown;
3019 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3020 stretchx = TRUE;
3021 } else {
3022 stretchx = FALSE;
3025 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3026 * flip the image nor scale it.
3028 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3029 * -> If the app wants a image width an unscaled width, copy it line per line
3030 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3031 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3032 * back buffer. This is slower than reading line per line, thus not used for flipping
3033 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3034 * pixel by pixel
3036 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3037 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3038 * backends.
3040 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3041 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3042 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3043 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3044 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3045 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3046 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3047 } else {
3048 TRACE("Using hardware stretching to flip / stretch the texture\n");
3049 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3052 if(!(This->Flags & SFLAG_DONOTFREE)) {
3053 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3054 This->resource.allocatedMemory = NULL;
3055 This->resource.heapMemory = NULL;
3056 } else {
3057 This->Flags &= ~SFLAG_INSYSMEM;
3059 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3060 * path is never entered
3062 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3064 return WINED3D_OK;
3065 } else if(Src) {
3066 /* Blit from offscreen surface to render target */
3067 float glTexCoord[4];
3068 DWORD oldCKeyFlags = Src->CKeyFlags;
3069 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
3070 RECT SourceRectangle;
3072 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3074 if(SrcRect) {
3075 SourceRectangle.left = SrcRect->left;
3076 SourceRectangle.right = SrcRect->right;
3077 SourceRectangle.top = SrcRect->top;
3078 SourceRectangle.bottom = SrcRect->bottom;
3079 } else {
3080 SourceRectangle.left = 0;
3081 SourceRectangle.right = Src->currentDesc.Width;
3082 SourceRectangle.top = 0;
3083 SourceRectangle.bottom = Src->currentDesc.Height;
3085 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3086 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3087 TRACE("Using stretch_rect_fbo\n");
3088 /* The source is always a texture, but never the currently active render target, and the texture
3089 * contents are never upside down
3091 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3092 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3093 return WINED3D_OK;
3096 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3097 /* Fall back to software */
3098 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3099 SourceRectangle.left, SourceRectangle.top,
3100 SourceRectangle.right, SourceRectangle.bottom);
3101 return WINED3DERR_INVALIDCALL;
3104 /* Color keying: Check if we have to do a color keyed blt,
3105 * and if not check if a color key is activated.
3107 * Just modify the color keying parameters in the surface and restore them afterwards
3108 * The surface keeps track of the color key last used to load the opengl surface.
3109 * PreLoad will catch the change to the flags and color key and reload if necessary.
3111 if(Flags & WINEDDBLT_KEYSRC) {
3112 /* Use color key from surface */
3113 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3114 /* Use color key from DDBltFx */
3115 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3116 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3117 } else {
3118 /* Do not use color key */
3119 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3122 /* Now load the surface */
3123 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3126 /* Activate the destination context, set it up for blitting */
3127 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3128 ENTER_GL();
3130 glEnable(Src->glDescription.target);
3131 checkGLcall("glEnable(Src->glDescription.target)");
3133 if(!dstSwapchain) {
3134 TRACE("Drawing to offscreen buffer\n");
3135 glDrawBuffer(myDevice->offscreenBuffer);
3136 checkGLcall("glDrawBuffer");
3137 } else {
3138 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3139 TRACE("Drawing to %#x buffer\n", buffer);
3140 glDrawBuffer(buffer);
3141 checkGLcall("glDrawBuffer");
3144 /* Bind the texture */
3145 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3146 checkGLcall("glBindTexture");
3148 /* Filtering for StretchRect */
3149 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3150 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3151 checkGLcall("glTexParameteri");
3152 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3153 minMipLookup[Filter][WINED3DTEXF_NONE]);
3154 checkGLcall("glTexParameteri");
3155 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3156 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3157 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3158 checkGLcall("glTexEnvi");
3160 /* This is for color keying */
3161 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3162 glEnable(GL_ALPHA_TEST);
3163 checkGLcall("glEnable GL_ALPHA_TEST");
3164 glAlphaFunc(GL_NOTEQUAL, 0.0);
3165 checkGLcall("glAlphaFunc\n");
3166 } else {
3167 glDisable(GL_ALPHA_TEST);
3168 checkGLcall("glDisable GL_ALPHA_TEST");
3171 /* Draw a textured quad
3173 glBegin(GL_QUADS);
3175 glColor3d(1.0f, 1.0f, 1.0f);
3176 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3177 glVertex3f(rect.x1,
3178 rect.y1,
3179 0.0);
3181 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3182 glVertex3f(rect.x1, rect.y2, 0.0);
3184 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3185 glVertex3f(rect.x2,
3186 rect.y2,
3187 0.0);
3189 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3190 glVertex3f(rect.x2,
3191 rect.y1,
3192 0.0);
3193 glEnd();
3194 checkGLcall("glEnd");
3196 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3197 glDisable(GL_ALPHA_TEST);
3198 checkGLcall("glDisable(GL_ALPHA_TEST)");
3201 /* Flush in case the drawable is used by multiple GL contexts */
3202 if(dstSwapchain && (dstSwapchain->num_contexts >= 2))
3203 glFlush();
3205 glBindTexture(Src->glDescription.target, 0);
3206 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3207 /* Leave the opengl state valid for blitting */
3208 glDisable(Src->glDescription.target);
3209 checkGLcall("glDisable(Src->glDescription.target)");
3211 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3212 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3214 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3215 glDrawBuffer(GL_BACK);
3216 checkGLcall("glDrawBuffer");
3218 /* Restore the color key parameters */
3219 Src->CKeyFlags = oldCKeyFlags;
3220 This->SrcBltCKey = oldBltCKey;
3222 LEAVE_GL();
3224 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3225 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3226 * is outdated now
3228 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3229 /* TODO: This should be moved to ModifyLocation() */
3230 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3231 This->Flags |= SFLAG_INTEXTURE;
3234 return WINED3D_OK;
3235 } else {
3236 /* Source-Less Blit to render target */
3237 if (Flags & WINEDDBLT_COLORFILL) {
3238 /* This is easy to handle for the D3D Device... */
3239 DWORD color;
3241 TRACE("Colorfill\n");
3243 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3244 must be true if we are here */
3245 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3246 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3247 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3248 TRACE("Surface is higher back buffer, falling back to software\n");
3249 return WINED3DERR_INVALIDCALL;
3252 /* The color as given in the Blt function is in the format of the frame-buffer...
3253 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3255 if (This->resource.format == WINED3DFMT_P8) {
3256 if (This->palette) {
3257 color = ((0xFF000000) |
3258 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3259 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3260 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3261 } else {
3262 color = 0xFF000000;
3265 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3266 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3267 color = 0xFFFFFFFF;
3268 } else {
3269 color = ((0xFF000000) |
3270 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3271 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3272 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3275 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3276 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3277 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3279 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3280 color = DDBltFx->u5.dwFillColor;
3282 else {
3283 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3284 return WINED3DERR_INVALIDCALL;
3287 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3288 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3289 1, /* Number of rectangles */
3290 &rect, WINED3DCLEAR_TARGET, color,
3291 0.0 /* Z */,
3292 0 /* Stencil */);
3293 return WINED3D_OK;
3297 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3298 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3299 return WINED3DERR_INVALIDCALL;
3302 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3304 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3305 float depth;
3307 if (Flags & WINEDDBLT_DEPTHFILL) {
3308 switch(This->resource.format) {
3309 case WINED3DFMT_D16:
3310 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3311 break;
3312 case WINED3DFMT_D15S1:
3313 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3314 break;
3315 case WINED3DFMT_D24S8:
3316 case WINED3DFMT_D24X8:
3317 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3318 break;
3319 case WINED3DFMT_D32:
3320 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3321 break;
3322 default:
3323 depth = 0.0;
3324 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3327 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3328 DestRect == NULL ? 0 : 1,
3329 (WINED3DRECT *) DestRect,
3330 WINED3DCLEAR_ZBUFFER,
3331 0x00000000,
3332 depth,
3333 0x00000000);
3336 FIXME("(%p): Unsupp depthstencil blit\n", This);
3337 return WINED3DERR_INVALIDCALL;
3340 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3341 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3342 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3343 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3344 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3345 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3347 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3348 * except depth blits, which seem to work
3350 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3351 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3352 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3353 return WINED3DERR_INVALIDCALL;
3354 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3355 TRACE("Z Blit override handled the blit\n");
3356 return WINED3D_OK;
3360 /* Special cases for RenderTargets */
3361 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3362 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3363 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3366 /* For the rest call the X11 surface implementation.
3367 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3368 * other Blts are rather rare
3370 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3373 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3374 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3375 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3376 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3377 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3379 if(myDevice->inScene &&
3380 (iface == myDevice->stencilBufferTarget ||
3381 (Source && Source == myDevice->stencilBufferTarget))) {
3382 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3383 return WINED3DERR_INVALIDCALL;
3386 /* Special cases for RenderTargets */
3387 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3388 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3390 RECT SrcRect, DstRect;
3391 DWORD Flags=0;
3393 if(rsrc) {
3394 SrcRect.left = rsrc->left;
3395 SrcRect.top= rsrc->top;
3396 SrcRect.bottom = rsrc->bottom;
3397 SrcRect.right = rsrc->right;
3398 } else {
3399 SrcRect.left = 0;
3400 SrcRect.top = 0;
3401 SrcRect.right = srcImpl->currentDesc.Width;
3402 SrcRect.bottom = srcImpl->currentDesc.Height;
3405 DstRect.left = dstx;
3406 DstRect.top=dsty;
3407 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3408 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3410 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3411 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3412 Flags |= WINEDDBLT_KEYSRC;
3413 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3414 Flags |= WINEDDBLT_KEYDEST;
3415 if(trans & WINEDDBLTFAST_WAIT)
3416 Flags |= WINEDDBLT_WAIT;
3417 if(trans & WINEDDBLTFAST_DONOTWAIT)
3418 Flags |= WINEDDBLT_DONOTWAIT;
3420 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3424 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3427 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3428 /** Check against the maximum texture sizes supported by the video card **/
3429 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3430 unsigned int pow2Width, pow2Height;
3431 const GlPixelFormatDesc *glDesc;
3433 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3434 /* Setup some glformat defaults */
3435 This->glDescription.glFormat = glDesc->glFormat;
3436 This->glDescription.glFormatInternal = glDesc->glInternal;
3437 This->glDescription.glType = glDesc->glType;
3439 This->glDescription.textureName = 0;
3440 This->glDescription.target = GL_TEXTURE_2D;
3442 /* Non-power2 support */
3443 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3444 pow2Width = This->currentDesc.Width;
3445 pow2Height = This->currentDesc.Height;
3446 } else {
3447 /* Find the nearest pow2 match */
3448 pow2Width = pow2Height = 1;
3449 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3450 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3452 This->pow2Width = pow2Width;
3453 This->pow2Height = pow2Height;
3455 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3456 WINED3DFORMAT Format = This->resource.format;
3457 /** TODO: add support for non power two compressed textures **/
3458 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3459 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3460 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3461 This, This->currentDesc.Width, This->currentDesc.Height);
3462 return WINED3DERR_NOTAVAILABLE;
3466 if(pow2Width != This->currentDesc.Width ||
3467 pow2Height != This->currentDesc.Height) {
3468 This->Flags |= SFLAG_NONPOW2;
3471 TRACE("%p\n", This);
3472 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3473 /* one of three options
3474 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)
3475 2: Set the texture to the maximum size (bad idea)
3476 3: WARN and return WINED3DERR_NOTAVAILABLE;
3477 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.
3479 WARN("(%p) Creating an oversized surface\n", This);
3480 This->Flags |= SFLAG_OVERSIZE;
3482 /* This will be initialized on the first blt */
3483 This->glRect.left = 0;
3484 This->glRect.top = 0;
3485 This->glRect.right = 0;
3486 This->glRect.bottom = 0;
3487 } else {
3488 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3489 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3490 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3491 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3493 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE) &&
3494 !((This->resource.format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE) && (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3496 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3497 This->pow2Width = This->currentDesc.Width;
3498 This->pow2Height = This->currentDesc.Height;
3499 This->Flags &= ~SFLAG_NONPOW2;
3502 /* No oversize, gl rect is the full texture size */
3503 This->Flags &= ~SFLAG_OVERSIZE;
3504 This->glRect.left = 0;
3505 This->glRect.top = 0;
3506 This->glRect.right = This->pow2Width;
3507 This->glRect.bottom = This->pow2Height;
3510 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3511 switch(wined3d_settings.offscreen_rendering_mode) {
3512 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3513 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3514 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3518 This->Flags |= SFLAG_INSYSMEM;
3520 return WINED3D_OK;
3523 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3524 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3525 IWineD3DBaseTexture *texture;
3527 TRACE("(%p)->(%s, %s)\n", iface,
3528 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3529 persistent ? "TRUE" : "FALSE");
3531 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3532 IWineD3DSwapChain *swapchain = NULL;
3534 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3535 TRACE("Surface %p is an onscreen surface\n", iface);
3537 IWineD3DSwapChain_Release(swapchain);
3538 } else {
3539 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3540 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3544 if(persistent) {
3545 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
3546 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3547 TRACE("Passing to container\n");
3548 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3549 IWineD3DBaseTexture_Release(texture);
3552 This->Flags &= ~SFLAG_LOCATIONS;
3553 This->Flags |= flag;
3554 } else {
3555 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
3556 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3557 TRACE("Passing to container\n");
3558 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3559 IWineD3DBaseTexture_Release(texture);
3562 This->Flags &= ~flag;
3566 struct coords {
3567 GLfloat x, y, z;
3570 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
3571 struct coords coords[4];
3572 RECT rect;
3573 IWineD3DSwapChain *swapchain = NULL;
3574 IWineD3DBaseTexture *texture = NULL;
3575 HRESULT hr;
3576 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3578 if(rect_in) {
3579 rect = *rect_in;
3580 } else {
3581 rect.left = 0;
3582 rect.top = 0;
3583 rect.right = This->currentDesc.Width;
3584 rect.bottom = This->currentDesc.Height;
3587 ActivateContext(device, device->render_targets[0], CTXUSAGE_BLIT);
3588 ENTER_GL();
3590 if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
3591 glEnable(GL_TEXTURE_RECTANGLE_ARB);
3592 checkGLcall("glEnable(GL_TEXTURE_RECTANGLE_ARB)");
3593 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName);
3594 checkGLcall("GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName)");
3595 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3596 checkGLcall("glTexParameteri");
3597 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3598 checkGLcall("glTexParameteri");
3600 coords[0].x = rect.left;
3601 coords[0].z = 0;
3603 coords[1].x = rect.left;
3604 coords[1].z = 0;
3606 coords[2].x = rect.right;
3607 coords[2].z = 0;
3609 coords[3].x = rect.right;
3610 coords[3].z = 0;
3612 coords[0].y = rect.top;
3613 coords[1].y = rect.bottom;
3614 coords[2].y = rect.bottom;
3615 coords[3].y = rect.top;
3616 } else if(This->glDescription.target == GL_TEXTURE_2D) {
3617 glEnable(GL_TEXTURE_2D);
3618 checkGLcall("glEnable(GL_TEXTURE_2D)");
3619 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
3620 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
3621 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3622 checkGLcall("glTexParameteri");
3623 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3624 checkGLcall("glTexParameteri");
3626 coords[0].x = (float)rect.left / This->pow2Width;
3627 coords[0].z = 0;
3629 coords[1].x = (float)rect.left / This->pow2Width;
3630 coords[1].z = 0;
3632 coords[2].x = (float)rect.right / This->pow2Width;
3633 coords[2].z = 0;
3635 coords[3].x = (float)rect.right / This->pow2Width;
3636 coords[3].z = 0;
3638 coords[0].y = (float)rect.top / This->pow2Height;
3639 coords[1].y = (float)rect.bottom / This->pow2Height;
3640 coords[2].y = (float)rect.bottom / This->pow2Height;
3641 coords[3].y = (float)rect.top / This->pow2Height;
3642 } else {
3643 /* Must be a cube map */
3644 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
3645 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
3646 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
3647 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
3648 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3649 checkGLcall("glTexParameteri");
3650 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3651 checkGLcall("glTexParameteri");
3653 switch(This->glDescription.target) {
3654 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3655 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
3656 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3657 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3658 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
3659 break;
3661 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3662 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3663 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
3664 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
3665 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3666 break;
3668 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3669 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
3670 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3671 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3672 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
3673 break;
3675 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3676 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3677 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3678 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3679 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3680 break;
3682 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3683 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3684 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3685 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
3686 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
3687 break;
3689 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
3690 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
3691 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
3692 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3693 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3695 default:
3696 ERR("Unexpected texture target\n");
3697 LEAVE_GL();
3698 return;
3702 glBegin(GL_QUADS);
3703 glTexCoord3fv(&coords[0].x);
3704 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
3706 glTexCoord3fv(&coords[1].x);
3707 glVertex2i(rect.left, device->render_offscreen ? rect.top : rect.bottom);
3709 glTexCoord3fv(&coords[2].x);
3710 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
3712 glTexCoord3fv(&coords[3].x);
3713 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
3714 glEnd();
3715 checkGLcall("glEnd");
3717 if(This->glDescription.target != GL_TEXTURE_2D) {
3718 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3719 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3720 } else {
3721 glDisable(GL_TEXTURE_2D);
3722 checkGLcall("glDisable(GL_TEXTURE_2D)");
3725 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain);
3726 if(hr == WINED3D_OK && swapchain) {
3727 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
3728 if(((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
3729 glFlush();
3731 IWineD3DSwapChain_Release(swapchain);
3732 } else {
3733 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
3734 * reset properly next draw
3736 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture);
3737 if(hr == WINED3D_OK && texture) {
3738 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
3739 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
3740 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
3741 IWineD3DBaseTexture_Release(texture);
3744 LEAVE_GL();
3747 /*****************************************************************************
3748 * IWineD3DSurface::LoadLocation
3750 * Copies the current surface data from wherever it is to the requested
3751 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
3752 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
3753 * multiple locations, the gl texture is preferred over the drawable, which is
3754 * preferred over system memory. The PBO counts as system memory. If rect is
3755 * not NULL, only the specified rectangle is copied (only supported for
3756 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
3757 * location is marked up to date after the copy.
3759 * Parameters:
3760 * flag: Surface location flag to be updated
3761 * rect: rectangle to be copied
3763 * Returns:
3764 * WINED3D_OK on success
3765 * WINED3DERR_DEVICELOST on an internal error
3767 *****************************************************************************/
3768 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
3769 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3770 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3771 IWineD3DSwapChain *swapchain = NULL;
3772 GLenum format, internal, type;
3773 CONVERT_TYPES convert;
3774 int bpp;
3775 int width, pitch, outpitch;
3776 BYTE *mem;
3778 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3779 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3780 TRACE("Surface %p is an onscreen surface\n", iface);
3782 IWineD3DSwapChain_Release(swapchain);
3783 } else {
3784 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
3785 * Prefer SFLAG_INTEXTURE. */
3786 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
3790 TRACE("(%p)->(%s, %p)\n", iface,
3791 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3792 rect);
3793 if(rect) {
3794 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
3797 if(This->Flags & flag) {
3798 TRACE("Location already up to date\n");
3799 return WINED3D_OK;
3802 if(!(This->Flags & SFLAG_LOCATIONS)) {
3803 ERR("Surface does not have any up to date location\n");
3804 This->Flags |= SFLAG_LOST;
3805 return WINED3DERR_DEVICELOST;
3808 if(flag == SFLAG_INSYSMEM) {
3809 surface_prepare_system_memory(This);
3811 /* Download the surface to system memory */
3812 if(This->Flags & SFLAG_INTEXTURE) {
3813 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
3814 surface_bind_and_dirtify(This);
3816 surface_download_data(This);
3817 } else {
3818 read_from_framebuffer(This, rect,
3819 This->resource.allocatedMemory,
3820 IWineD3DSurface_GetPitch(iface));
3822 } else if(flag == SFLAG_INDRAWABLE) {
3823 if(This->Flags & SFLAG_INTEXTURE) {
3824 surface_blt_to_drawable(This, rect);
3825 } else {
3826 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
3828 /* The width is in 'length' not in bytes */
3829 width = This->currentDesc.Width;
3830 pitch = IWineD3DSurface_GetPitch(iface);
3832 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
3833 int height = This->currentDesc.Height;
3835 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
3836 outpitch = width * bpp;
3837 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
3839 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
3840 if(!mem) {
3841 ERR("Out of memory %d, %d!\n", outpitch, height);
3842 return WINED3DERR_OUTOFVIDEOMEMORY;
3844 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
3846 This->Flags |= SFLAG_CONVERTED;
3847 } else {
3848 This->Flags &= ~SFLAG_CONVERTED;
3849 mem = This->resource.allocatedMemory;
3852 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
3854 /* Don't delete PBO memory */
3855 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
3856 HeapFree(GetProcessHeap(), 0, mem);
3858 } else /* if(flag == SFLAG_INTEXTURE) */ {
3859 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
3861 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
3862 surface_bind_and_dirtify(This);
3864 if (This->Flags & SFLAG_INDRAWABLE) {
3865 GLint prevRead;
3867 ENTER_GL();
3868 glGetIntegerv(GL_READ_BUFFER, &prevRead);
3869 vcheckGLcall("glGetIntegerv");
3870 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
3871 vcheckGLcall("glReadBuffer");
3873 if(!(This->Flags & SFLAG_ALLOCATED)) {
3874 surface_allocate_surface(This, internal, This->pow2Width,
3875 This->pow2Height, format, type);
3878 clear_unused_channels(This);
3880 glCopyTexSubImage2D(This->glDescription.target,
3881 This->glDescription.level,
3882 0, 0, 0, 0,
3883 This->currentDesc.Width,
3884 This->currentDesc.Height);
3885 checkGLcall("glCopyTexSubImage2D");
3887 glReadBuffer(prevRead);
3888 vcheckGLcall("glReadBuffer");
3890 LEAVE_GL();
3892 TRACE("Updated target %d\n", This->glDescription.target);
3893 } else {
3894 /* The only place where LoadTexture() might get called when isInDraw=1
3895 * is ActivateContext where lastActiveRenderTarget is preloaded.
3897 if(iface == device->lastActiveRenderTarget && device->isInDraw)
3898 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
3900 /* Otherwise: System memory copy must be most up to date */
3902 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
3903 This->Flags |= SFLAG_GLCKEY;
3904 This->glCKey = This->SrcBltCKey;
3906 else This->Flags &= ~SFLAG_GLCKEY;
3908 /* The width is in 'length' not in bytes */
3909 width = This->currentDesc.Width;
3910 pitch = IWineD3DSurface_GetPitch(iface);
3912 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
3913 int height = This->currentDesc.Height;
3915 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
3916 outpitch = width * bpp;
3917 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
3919 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
3920 if(!mem) {
3921 ERR("Out of memory %d, %d!\n", outpitch, height);
3922 return WINED3DERR_OUTOFVIDEOMEMORY;
3924 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
3926 This->Flags |= SFLAG_CONVERTED;
3927 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
3928 d3dfmt_p8_upload_palette(iface, convert);
3929 This->Flags &= ~SFLAG_CONVERTED;
3930 mem = This->resource.allocatedMemory;
3931 } else {
3932 This->Flags &= ~SFLAG_CONVERTED;
3933 mem = This->resource.allocatedMemory;
3936 /* Make sure the correct pitch is used */
3937 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
3939 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
3940 TRACE("non power of two support\n");
3941 if(!(This->Flags & SFLAG_ALLOCATED)) {
3942 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
3944 if (mem || (This->Flags & SFLAG_PBO)) {
3945 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
3947 } else {
3948 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
3949 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
3951 if(!(This->Flags & SFLAG_ALLOCATED)) {
3952 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
3954 if (mem || (This->Flags & SFLAG_PBO)) {
3955 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
3959 /* Restore the default pitch */
3960 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
3962 /* Don't delete PBO memory */
3963 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
3964 HeapFree(GetProcessHeap(), 0, mem);
3968 if(rect == NULL) {
3969 This->Flags |= flag;
3972 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
3973 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
3974 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3975 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3978 return WINED3D_OK;
3981 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
3982 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3983 IWineD3DSwapChain *swapchain = NULL;
3985 /* Update the drawable size method */
3986 if(container) {
3987 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
3989 if(swapchain) {
3990 This->get_drawable_size = get_drawable_size_swapchain;
3991 IWineD3DSwapChain_Release(swapchain);
3992 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3993 switch(wined3d_settings.offscreen_rendering_mode) {
3994 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3995 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3996 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4000 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4003 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4005 /* IUnknown */
4006 IWineD3DBaseSurfaceImpl_QueryInterface,
4007 IWineD3DBaseSurfaceImpl_AddRef,
4008 IWineD3DSurfaceImpl_Release,
4009 /* IWineD3DResource */
4010 IWineD3DBaseSurfaceImpl_GetParent,
4011 IWineD3DBaseSurfaceImpl_GetDevice,
4012 IWineD3DBaseSurfaceImpl_SetPrivateData,
4013 IWineD3DBaseSurfaceImpl_GetPrivateData,
4014 IWineD3DBaseSurfaceImpl_FreePrivateData,
4015 IWineD3DBaseSurfaceImpl_SetPriority,
4016 IWineD3DBaseSurfaceImpl_GetPriority,
4017 IWineD3DSurfaceImpl_PreLoad,
4018 IWineD3DSurfaceImpl_UnLoad,
4019 IWineD3DBaseSurfaceImpl_GetType,
4020 /* IWineD3DSurface */
4021 IWineD3DBaseSurfaceImpl_GetContainer,
4022 IWineD3DBaseSurfaceImpl_GetDesc,
4023 IWineD3DSurfaceImpl_LockRect,
4024 IWineD3DSurfaceImpl_UnlockRect,
4025 IWineD3DSurfaceImpl_GetDC,
4026 IWineD3DSurfaceImpl_ReleaseDC,
4027 IWineD3DSurfaceImpl_Flip,
4028 IWineD3DSurfaceImpl_Blt,
4029 IWineD3DBaseSurfaceImpl_GetBltStatus,
4030 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4031 IWineD3DBaseSurfaceImpl_IsLost,
4032 IWineD3DBaseSurfaceImpl_Restore,
4033 IWineD3DSurfaceImpl_BltFast,
4034 IWineD3DBaseSurfaceImpl_GetPalette,
4035 IWineD3DBaseSurfaceImpl_SetPalette,
4036 IWineD3DBaseSurfaceImpl_RealizePalette,
4037 IWineD3DBaseSurfaceImpl_SetColorKey,
4038 IWineD3DBaseSurfaceImpl_GetPitch,
4039 IWineD3DSurfaceImpl_SetMem,
4040 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4041 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4042 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4043 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4044 IWineD3DBaseSurfaceImpl_SetClipper,
4045 IWineD3DBaseSurfaceImpl_GetClipper,
4046 /* Internal use: */
4047 IWineD3DSurfaceImpl_AddDirtyRect,
4048 IWineD3DSurfaceImpl_LoadTexture,
4049 IWineD3DSurfaceImpl_BindTexture,
4050 IWineD3DSurfaceImpl_SaveSnapshot,
4051 IWineD3DSurfaceImpl_SetContainer,
4052 IWineD3DSurfaceImpl_SetGlTextureDesc,
4053 IWineD3DSurfaceImpl_GetGlDesc,
4054 IWineD3DSurfaceImpl_GetData,
4055 IWineD3DSurfaceImpl_SetFormat,
4056 IWineD3DSurfaceImpl_PrivateSetup,
4057 IWineD3DSurfaceImpl_ModifyLocation,
4058 IWineD3DSurfaceImpl_LoadLocation