2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
11 * Copyright 2007-2008 Henri Verbeet
12 * Copyright 2006-2008 Roderick Colenbrander
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
36 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
37 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey);
38 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert);
39 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This);
40 static void surface_remove_pbo(IWineD3DSurfaceImpl *This);
42 void surface_force_reload(IWineD3DSurface *iface)
44 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
46 This->Flags &= ~SFLAG_ALLOCATED;
49 void surface_set_texture_name(IWineD3DSurface *iface, GLuint name)
51 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
53 TRACE("(%p) : setting texture name %u\n", This, name);
55 if (!This->glDescription.textureName && name)
57 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
58 * surface has no texture name yet. See if we can get rid of this. */
59 if (This->Flags & SFLAG_INTEXTURE)
60 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
61 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
64 This->glDescription.textureName = name;
65 surface_force_reload(iface);
68 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
70 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
72 TRACE("(%p) : setting target %#x\n", This, target);
74 if (This->glDescription.target != target)
76 if (target == GL_TEXTURE_RECTANGLE_ARB)
78 This->Flags &= ~SFLAG_NORMCOORD;
80 else if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB)
82 This->Flags |= SFLAG_NORMCOORD;
85 This->glDescription.target = target;
86 surface_force_reload(iface);
89 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This) {
92 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
93 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
94 * gl states. The current texture unit should always be a valid one.
96 * To be more specific, this is tricky because we can implicitly be called
97 * from sampler() in state.c. This means we can't touch anything other than
98 * whatever happens to be the currently active texture, or we would risk
99 * marking already applied sampler states dirty again.
101 * TODO: Track the current active texture per GL context instead of using glGet
103 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
104 GLint active_texture;
106 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
108 active_sampler = This->resource.wineD3DDevice->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
113 if (active_sampler != -1) {
114 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_sampler));
116 IWineD3DSurface_BindTexture((IWineD3DSurface *)This);
119 /* This function checks if the primary render target uses the 8bit paletted format. */
120 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
122 if (device->render_targets && device->render_targets[0]) {
123 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
124 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
130 /* This call just downloads data, the caller is responsible for activating the
131 * right context and binding the correct texture. */
132 static void surface_download_data(IWineD3DSurfaceImpl *This) {
133 if (0 == This->glDescription.textureName) {
134 ERR("Surface does not have a texture, but SFLAG_INTEXTURE is set\n");
138 /* Only support read back of converted P8 surfaces */
139 if(This->Flags & SFLAG_CONVERTED && (This->resource.format != WINED3DFMT_P8)) {
140 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(This->resource.format));
146 if (This->resource.format == WINED3DFMT_DXT1 ||
147 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
148 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5 ||
149 This->resource.format == WINED3DFMT_ATI2N) {
150 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
151 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
153 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
154 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
156 if(This->Flags & SFLAG_PBO) {
157 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
158 checkGLcall("glBindBufferARB");
159 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
160 checkGLcall("glGetCompressedTexImageARB()");
161 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
162 checkGLcall("glBindBufferARB");
164 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
165 checkGLcall("glGetCompressedTexImageARB()");
171 GLenum format = This->glDescription.glFormat;
172 GLenum type = This->glDescription.glType;
176 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
177 if(This->resource.format == WINED3DFMT_P8 && primary_render_target_is_p8(This->resource.wineD3DDevice)) {
179 type = GL_UNSIGNED_BYTE;
182 if (This->Flags & SFLAG_NONPOW2) {
183 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
184 src_pitch = This->bytesPerPixel * This->pow2Width;
185 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
186 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
187 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
189 mem = This->resource.allocatedMemory;
192 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
195 if(This->Flags & SFLAG_PBO) {
196 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
197 checkGLcall("glBindBufferARB");
199 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
201 checkGLcall("glGetTexImage()");
203 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
204 checkGLcall("glBindBufferARB");
206 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
208 checkGLcall("glGetTexImage()");
212 if (This->Flags & SFLAG_NONPOW2) {
213 LPBYTE src_data, dst_data;
216 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
217 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
218 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
220 * We're doing this...
222 * instead of boxing the texture :
223 * |<-texture width ->| -->pow2width| /\
224 * |111111111111111111| | |
225 * |222 Texture 222222| boxed empty | texture height
226 * |3333 Data 33333333| | |
227 * |444444444444444444| | \/
228 * ----------------------------------- |
229 * | boxed empty | boxed empty | pow2height
231 * -----------------------------------
234 * we're repacking the data to the expected texture width
236 * |<-texture width ->| -->pow2width| /\
237 * |111111111111111111222222222222222| |
238 * |222333333333333333333444444444444| texture height
242 * | empty | pow2height
244 * -----------------------------------
248 * |<-texture width ->| /\
249 * |111111111111111111|
250 * |222222222222222222|texture height
251 * |333333333333333333|
252 * |444444444444444444| \/
253 * --------------------
255 * this also means that any references to allocatedMemory should work with the data as if were a
256 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
258 * internally the texture is still stored in a boxed format so any references to textureName will
259 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
261 * Performance should not be an issue, because applications normally do not lock the surfaces when
262 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
263 * and doesn't have to be re-read.
266 dst_data = This->resource.allocatedMemory;
267 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
268 for (y = 1 ; y < This->currentDesc.Height; y++) {
269 /* skip the first row */
270 src_data += src_pitch;
271 dst_data += dst_pitch;
272 memcpy(dst_data, src_data, dst_pitch);
275 HeapFree(GetProcessHeap(), 0, mem);
279 /* Surface has now been downloaded */
280 This->Flags |= SFLAG_INSYSMEM;
283 /* This call just uploads data, the caller is responsible for activating the
284 * right context and binding the correct texture. */
285 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
287 if(This->heightscale != 1.0 && This->heightscale != 0.0) height *= This->heightscale;
289 if (This->resource.format == WINED3DFMT_DXT1 ||
290 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
291 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5 ||
292 This->resource.format == WINED3DFMT_ATI2N) {
293 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
294 FIXME("Using DXT1/3/5 without advertized support\n");
296 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
297 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
298 * function uses glCompressedTexImage2D instead of the SubImage call
300 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
303 if(This->Flags & SFLAG_PBO) {
304 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
305 checkGLcall("glBindBufferARB");
306 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
308 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
309 width, height, 0 /* border */, This->resource.size, NULL));
310 checkGLcall("glCompressedTexSubImage2D");
312 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
313 checkGLcall("glBindBufferARB");
315 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
316 width, height, 0 /* border */, This->resource.size, data));
317 checkGLcall("glCompressedTexSubImage2D");
322 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
325 if(This->Flags & SFLAG_PBO) {
326 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
327 checkGLcall("glBindBufferARB");
328 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
330 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
331 checkGLcall("glTexSubImage2D");
333 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
334 checkGLcall("glBindBufferARB");
337 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
338 checkGLcall("glTexSubImage2D");
345 /* This call just allocates the texture, the caller is responsible for
346 * activating the right context and binding the correct texture. */
347 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
348 BOOL enable_client_storage = FALSE;
351 if(This->heightscale != 1.0 && This->heightscale != 0.0) height *= This->heightscale;
353 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,
354 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
356 if (This->resource.format == WINED3DFMT_DXT1 ||
357 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
358 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5 ||
359 This->resource.format == WINED3DFMT_ATI2N) {
360 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
361 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
363 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
364 * once, unfortunately
366 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
367 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
368 This->Flags |= SFLAG_CLIENT;
369 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
371 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
372 width, height, 0 /* border */, This->resource.size, mem));
381 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
382 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
383 /* In some cases we want to disable client storage.
384 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
385 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
386 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
387 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
388 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
390 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
391 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
392 This->Flags &= ~SFLAG_CLIENT;
393 enable_client_storage = TRUE;
395 This->Flags |= SFLAG_CLIENT;
397 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
398 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
400 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
403 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
404 checkGLcall("glTexImage2D");
406 if(enable_client_storage) {
407 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
408 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
412 This->Flags |= SFLAG_ALLOCATED;
415 /* In D3D the depth stencil dimensions have to be greater than or equal to the
416 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
417 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
418 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
419 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
420 renderbuffer_entry_t *entry;
421 GLuint renderbuffer = 0;
422 unsigned int src_width, src_height;
424 src_width = This->pow2Width;
425 src_height = This->pow2Height;
427 /* A depth stencil smaller than the render target is not valid */
428 if (width > src_width || height > src_height) return;
430 /* Remove any renderbuffer set if the sizes match */
431 if (width == src_width && height == src_height) {
432 This->current_renderbuffer = NULL;
436 /* Look if we've already got a renderbuffer of the correct dimensions */
437 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
438 if (entry->width == width && entry->height == height) {
439 renderbuffer = entry->id;
440 This->current_renderbuffer = entry;
446 const GlPixelFormatDesc *glDesc;
447 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
449 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
450 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
451 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glInternal, width, height));
453 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
454 entry->width = width;
455 entry->height = height;
456 entry->id = renderbuffer;
457 list_add_head(&This->renderbuffers, &entry->entry);
459 This->current_renderbuffer = entry;
462 checkGLcall("set_compatible_renderbuffer");
465 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
466 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
467 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
469 TRACE("(%p) : swapchain %p\n", This, swapchain);
471 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
472 TRACE("Returning GL_BACK\n");
474 } else if (swapchain_impl->frontBuffer == iface) {
475 TRACE("Returning GL_FRONT\n");
479 FIXME("Higher back buffer, returning GL_BACK\n");
483 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
484 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
485 ULONG ref = InterlockedDecrement(&This->resource.ref);
486 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
488 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
489 renderbuffer_entry_t *entry, *entry2;
490 TRACE("(%p) : cleaning up\n", This);
492 /* Need a context to destroy the texture. Use the currently active render target, but only if
493 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
494 * When destroying the primary rt, Uninit3D will activate a context before doing anything
496 if(device->render_targets && device->render_targets[0]) {
497 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
501 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
502 TRACE("Deleting texture %d\n", This->glDescription.textureName);
503 glDeleteTextures(1, &This->glDescription.textureName);
506 if(This->Flags & SFLAG_PBO) {
508 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
511 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
512 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
513 HeapFree(GetProcessHeap(), 0, entry);
517 if(This->Flags & SFLAG_DIBSECTION) {
519 SelectObject(This->hDC, This->dib.holdbitmap);
521 /* Release the DIB section */
522 DeleteObject(This->dib.DIBsection);
523 This->dib.bitmap_data = NULL;
524 This->resource.allocatedMemory = NULL;
526 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
528 HeapFree(GetProcessHeap(), 0, This->palette9);
530 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
532 if(This->overlay_dest) {
533 list_remove(&This->overlay_entry);
536 TRACE("(%p) Released\n", This);
537 HeapFree(GetProcessHeap(), 0, This);
543 /* ****************************************************
544 IWineD3DSurface IWineD3DResource parts follow
545 **************************************************** */
547 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
548 /* TODO: check for locks */
549 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
550 IWineD3DBaseTexture *baseTexture = NULL;
551 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
553 TRACE("(%p)Checking to see if the container is a base texture\n", This);
554 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
555 TRACE("Passing to container\n");
556 IWineD3DBaseTexture_PreLoad(baseTexture);
557 IWineD3DBaseTexture_Release(baseTexture);
559 TRACE("(%p) : About to load surface\n", This);
561 if(!device->isInDraw) {
562 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
565 if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8) {
566 if(palette9_changed(This)) {
567 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
568 /* TODO: This is not necessarily needed with hw palettized texture support */
569 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
570 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
571 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
575 IWineD3DSurface_LoadTexture(iface, FALSE);
577 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
578 /* Tell opengl to try and keep this texture in video ram (well mostly) */
582 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
589 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
590 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
591 This->resource.allocatedMemory =
592 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
595 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
596 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
597 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
598 checkGLcall("glGetBufferSubData");
599 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
600 checkGLcall("glDeleteBuffers");
604 This->Flags &= ~SFLAG_PBO;
607 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
608 IWineD3DBaseTexture *texture = NULL;
609 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
610 renderbuffer_entry_t *entry, *entry2;
611 TRACE("(%p)\n", iface);
613 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
614 /* Default pool resources are supposed to be destroyed before Reset is called.
615 * Implicit resources stay however. So this means we have an implicit render target
616 * or depth stencil. The content may be destroyed, but we still have to tear down
617 * opengl resources, so we cannot leave early.
619 * Put the most up to date surface location into the drawable. D3D-wise this content
620 * is undefined, so it would be nowhere, but that would make the location management
621 * more complicated. The drawable is a sane location, because if we mark sysmem or
622 * texture up to date, drawPrim will copy the uninitialized texture or sysmem to the
623 * uninitialized drawable. That's pointless and we'd have to allocate the texture /
626 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
627 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
629 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
632 /* Load the surface into system memory */
633 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
634 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
636 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
637 This->Flags &= ~SFLAG_ALLOCATED;
639 /* Destroy PBOs, but load them into real sysmem before */
640 if(This->Flags & SFLAG_PBO) {
641 surface_remove_pbo(This);
644 /* Destroy fbo render buffers. This is needed for implicit render targets, for
645 * all application-created targets the application has to release the surface
646 * before calling _Reset
648 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
650 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
652 list_remove(&entry->entry);
653 HeapFree(GetProcessHeap(), 0, entry);
655 list_init(&This->renderbuffers);
656 This->current_renderbuffer = NULL;
658 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
661 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
664 glDeleteTextures(1, &This->glDescription.textureName);
665 This->glDescription.textureName = 0;
668 IWineD3DBaseTexture_Release(texture);
673 /* ******************************************************
674 IWineD3DSurface IWineD3DSurface parts follow
675 ****************************************************** */
677 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
678 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
679 TRACE("(%p) : returning %p\n", This, &This->glDescription);
680 *glDescription = &This->glDescription;
683 /* Read the framebuffer back into the surface */
684 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
685 IWineD3DSwapChainImpl *swapchain;
686 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
690 BYTE *row, *top, *bottom;
694 BOOL srcIsUpsideDown;
696 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
697 static BOOL warned = FALSE;
699 ERR("The application tries to lock the render target, but render target locking is disabled\n");
705 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
706 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
707 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
708 * context->last_was_blit set on the unlock.
710 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
713 /* Select the correct read buffer, and give some debug output.
714 * There is no need to keep track of the current read buffer or reset it, every part of the code
715 * that reads sets the read buffer as desired.
717 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
719 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
720 TRACE("Locking %#x buffer\n", buffer);
721 glReadBuffer(buffer);
722 checkGLcall("glReadBuffer");
724 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
725 srcIsUpsideDown = FALSE;
727 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
728 * Read from the back buffer
730 TRACE("Locking offscreen render target\n");
731 glReadBuffer(myDevice->offscreenBuffer);
732 srcIsUpsideDown = TRUE;
735 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
739 local_rect.right = This->currentDesc.Width;
740 local_rect.bottom = This->currentDesc.Height;
744 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
746 switch(This->resource.format)
750 if(primary_render_target_is_p8(myDevice)) {
751 /* In case of P8 render targets the index is stored in the alpha component */
753 type = GL_UNSIGNED_BYTE;
755 bpp = This->bytesPerPixel;
757 /* GL can't return palettized data, so read ARGB pixels into a
758 * separate block of memory and convert them into palettized format
759 * in software. Slow, but if the app means to use palettized render
760 * targets and locks it...
762 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
763 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
764 * for the color channels when palettizing the colors.
767 type = GL_UNSIGNED_BYTE;
769 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
771 ERR("Out of memory\n");
775 bpp = This->bytesPerPixel * 3;
782 fmt = This->glDescription.glFormat;
783 type = This->glDescription.glType;
784 bpp = This->bytesPerPixel;
787 if(This->Flags & SFLAG_PBO) {
788 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
789 checkGLcall("glBindBufferARB");
792 glReadPixels(local_rect.left, local_rect.top,
793 local_rect.right - local_rect.left,
794 local_rect.bottom - local_rect.top,
796 vcheckGLcall("glReadPixels");
798 if(This->Flags & SFLAG_PBO) {
799 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
800 checkGLcall("glBindBufferARB");
802 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
803 * to get a pointer to it and perform the flipping in software. This is a lot
804 * faster than calling glReadPixels for each line. In case we want more speed
805 * we should rerender it flipped in a FBO and read the data back from the FBO. */
806 if(!srcIsUpsideDown) {
807 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
808 checkGLcall("glBindBufferARB");
810 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
811 checkGLcall("glMapBufferARB");
815 /* TODO: Merge this with the palettization loop below for P8 targets */
816 if(!srcIsUpsideDown) {
818 /* glReadPixels returns the image upside down, and there is no way to prevent this.
819 Flip the lines in software */
820 len = (local_rect.right - local_rect.left) * bpp;
821 off = local_rect.left * bpp;
823 row = HeapAlloc(GetProcessHeap(), 0, len);
825 ERR("Out of memory\n");
826 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
831 top = mem + pitch * local_rect.top;
832 bottom = mem + pitch * ( local_rect.bottom - local_rect.top - 1);
833 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
834 memcpy(row, top + off, len);
835 memcpy(top + off, bottom + off, len);
836 memcpy(bottom + off, row, len);
840 HeapFree(GetProcessHeap(), 0, row);
842 /* Unmap the temp PBO buffer */
843 if(This->Flags & SFLAG_PBO) {
844 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
845 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
851 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
852 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
853 * the same color but we have no choice.
854 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
856 if((This->resource.format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice)) {
857 PALETTEENTRY *pal = NULL;
858 DWORD width = pitch / 3;
862 pal = This->palette->palents;
864 ERR("Palette is missing, cannot perform inverse palette lookup\n");
865 HeapFree(GetProcessHeap(), 0, mem);
869 for(y = local_rect.top; y < local_rect.bottom; y++) {
870 for(x = local_rect.left; x < local_rect.right; x++) {
871 /* start lines pixels */
872 BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
873 BYTE *green = blue + 1;
874 BYTE *red = green + 1;
876 for(c = 0; c < 256; c++) {
877 if(*red == pal[c].peRed &&
878 *green == pal[c].peGreen &&
879 *blue == pal[c].peBlue)
881 *((BYTE *) dest + y * width + x) = c;
887 HeapFree(GetProcessHeap(), 0, mem);
891 /* Read the framebuffer contents into a texture */
892 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This)
894 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
895 IWineD3DSwapChainImpl *swapchain;
897 GLenum format, internal, type;
898 CONVERT_TYPES convert;
901 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
903 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
904 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
905 * states in the stateblock, and no driver was found yet that had bugs in that regard.
907 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
908 surface_bind_and_dirtify(This);
911 glGetIntegerv(GL_READ_BUFFER, &prevRead);
914 /* Select the correct read buffer, and give some debug output.
915 * There is no need to keep track of the current read buffer or reset it, every part of the code
916 * that reads sets the read buffer as desired.
918 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
920 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
921 TRACE("Locking %#x buffer\n", buffer);
924 glReadBuffer(buffer);
925 checkGLcall("glReadBuffer");
928 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
930 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
931 * Read from the back buffer
933 TRACE("Locking offscreen render target\n");
935 glReadBuffer(device->offscreenBuffer);
939 if(!(This->Flags & SFLAG_ALLOCATED)) {
940 surface_allocate_surface(This, internal, This->pow2Width,
941 This->pow2Height, format, type);
944 clear_unused_channels(This);
947 /* If !SrcIsUpsideDown we should flip the surface.
948 * This can be done using glCopyTexSubImage2D but this
949 * is VERY slow, so don't do that. We should prevent
950 * this code from getting called in such cases or perhaps
953 glCopyTexSubImage2D(This->glDescription.target,
954 This->glDescription.level,
956 This->currentDesc.Width,
957 This->currentDesc.Height);
958 checkGLcall("glCopyTexSubImage2D");
960 glReadBuffer(prevRead);
961 vcheckGLcall("glReadBuffer");
964 TRACE("Updated target %d\n", This->glDescription.target);
967 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
968 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
969 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
972 if(!(This->Flags & SFLAG_DYNLOCK)) {
974 /* MAXLOCKCOUNT is defined in wined3d_private.h */
975 if(This->lockCount > MAXLOCKCOUNT) {
976 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
977 This->Flags |= SFLAG_DYNLOCK;
981 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
982 * Also don't create a PBO for systemmem surfaces.
984 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
986 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
988 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
991 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
992 error = glGetError();
993 if(This->pbo == 0 || error != GL_NO_ERROR) {
994 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
997 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
999 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1000 checkGLcall("glBindBufferARB");
1002 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1003 checkGLcall("glBufferDataARB");
1005 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1006 checkGLcall("glBindBufferARB");
1008 /* We don't need the system memory anymore and we can't even use it for PBOs */
1009 if(!(This->Flags & SFLAG_CLIENT)) {
1010 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1011 This->resource.heapMemory = NULL;
1013 This->resource.allocatedMemory = NULL;
1014 This->Flags |= SFLAG_PBO;
1016 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
1017 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1020 if(!This->resource.heapMemory) {
1021 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1023 This->resource.allocatedMemory =
1024 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1025 if(This->Flags & SFLAG_INSYSMEM) {
1026 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1031 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1032 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1033 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1034 IWineD3DSwapChain *swapchain = NULL;
1036 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1038 /* This is also done in the base class, but we have to verify this before loading any data from
1039 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1040 * may interfere, and all other bad things may happen
1042 if (This->Flags & SFLAG_LOCKED) {
1043 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1044 return WINED3DERR_INVALIDCALL;
1046 This->Flags |= SFLAG_LOCKED;
1048 if (!(This->Flags & SFLAG_LOCKABLE))
1050 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1053 if (Flags & WINED3DLOCK_DISCARD) {
1054 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1055 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1056 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1057 This->Flags |= SFLAG_INSYSMEM;
1061 if (This->Flags & SFLAG_INSYSMEM) {
1062 TRACE("Local copy is up to date, not downloading data\n");
1063 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1067 /* Now download the surface content from opengl
1068 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1069 * Offscreen targets which are not active at the moment or are higher targets(FBOs) can be locked with the texture path
1071 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1072 if(swapchain || iface == myDevice->render_targets[0]) {
1073 const RECT *pass_rect = pRect;
1075 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1076 * because most caller functions do not need that. So do that here
1081 pRect->right == This->currentDesc.Width &&
1082 pRect->bottom == This->currentDesc.Height) {
1086 switch(wined3d_settings.rendertargetlock_mode) {
1089 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1091 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1092 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1093 * This may be faster on some cards
1095 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1102 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1108 if(swapchain) IWineD3DSwapChain_Release(swapchain);
1110 } else if(iface == myDevice->stencilBufferTarget) {
1111 /** the depth stencil in openGL has a format of GL_FLOAT
1112 * which should be good for WINED3DFMT_D16_LOCKABLE
1113 * and WINED3DFMT_D16
1114 * it is unclear what format the stencil buffer is in except.
1115 * 'Each index is converted to fixed point...
1116 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1117 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1118 * glReadPixels(This->lockedRect.left,
1119 * This->lockedRect.bottom - j - 1,
1120 * This->lockedRect.right - This->lockedRect.left,
1122 * GL_DEPTH_COMPONENT,
1124 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1126 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1127 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1128 * none of that is the case the problem is not in this function :-)
1129 ********************************************/
1130 FIXME("Depth stencil locking not supported yet\n");
1132 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1133 TRACE("locking an ordinary surface\n");
1134 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1138 if(This->Flags & SFLAG_PBO) {
1139 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1141 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1142 checkGLcall("glBindBufferARB");
1144 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1145 if(This->resource.allocatedMemory) {
1146 ERR("The surface already has PBO memory allocated!\n");
1149 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1150 checkGLcall("glMapBufferARB");
1152 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1153 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1154 checkGLcall("glBindBufferARB");
1159 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1162 IWineD3DBaseTexture *pBaseTexture;
1165 * as seen in msdn docs
1167 IWineD3DSurface_AddDirtyRect(iface, pRect);
1169 /** Dirtify Container if needed */
1170 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1171 TRACE("Making container dirty\n");
1172 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1173 IWineD3DBaseTexture_Release(pBaseTexture);
1175 TRACE("Surface is standalone, no need to dirty the container\n");
1179 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1182 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1184 GLint prev_rasterpos[4];
1185 GLint skipBytes = 0;
1186 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1187 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1188 IWineD3DSwapChainImpl *swapchain;
1190 /* Activate the correct context for the render target */
1191 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1194 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
1195 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1196 TRACE("Unlocking %#x buffer\n", buffer);
1197 glDrawBuffer(buffer);
1198 checkGLcall("glDrawBuffer");
1200 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1202 /* Primary offscreen render target */
1203 TRACE("Offscreen render target\n");
1204 glDrawBuffer(myDevice->offscreenBuffer);
1205 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1208 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1209 vcheckGLcall("glIntegerv");
1210 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1211 vcheckGLcall("glIntegerv");
1212 glPixelZoom(1.0, -1.0);
1213 vcheckGLcall("glPixelZoom");
1215 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1216 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1217 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1219 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1220 vcheckGLcall("glRasterPos2f");
1222 /* Some drivers(radeon dri, others?) don't like exceptions during
1223 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1224 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1225 * catch to put the dib section in InSync mode, which leads to a crash
1226 * and a blocked x server on my radeon card.
1228 * The following lines read the dib section so it is put in InSync mode
1229 * before glDrawPixels is called and the crash is prevented. There won't
1230 * be any interfering gdi accesses, because UnlockRect is called from
1231 * ReleaseDC, and the app won't use the dc any more afterwards.
1233 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1235 read = This->resource.allocatedMemory[0];
1238 if(This->Flags & SFLAG_PBO) {
1239 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1240 checkGLcall("glBindBufferARB");
1243 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1244 if(This->Flags & SFLAG_LOCKED) {
1245 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1246 (This->lockedRect.bottom - This->lockedRect.top)-1,
1248 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1249 checkGLcall("glDrawPixels");
1251 glDrawPixels(This->currentDesc.Width,
1252 This->currentDesc.Height,
1254 checkGLcall("glDrawPixels");
1257 if(This->Flags & SFLAG_PBO) {
1258 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1259 checkGLcall("glBindBufferARB");
1262 glPixelZoom(1.0,1.0);
1263 vcheckGLcall("glPixelZoom");
1265 glRasterPos3iv(&prev_rasterpos[0]);
1266 vcheckGLcall("glRasterPos3iv");
1268 /* Reset to previous pack row length */
1269 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1270 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1273 glDrawBuffer(myDevice->offscreenBuffer);
1274 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1275 } else if(swapchain->backBuffer) {
1276 glDrawBuffer(GL_BACK);
1277 checkGLcall("glDrawBuffer(GL_BACK)");
1279 glDrawBuffer(GL_FRONT);
1280 checkGLcall("glDrawBuffer(GL_FRONT)");
1287 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1288 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1289 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1290 IWineD3DSwapChainImpl *swapchain = NULL;
1293 if (!(This->Flags & SFLAG_LOCKED)) {
1294 WARN("trying to Unlock an unlocked surf@%p\n", This);
1295 return WINED3DERR_INVALIDCALL;
1298 if (This->Flags & SFLAG_PBO) {
1299 TRACE("Freeing PBO memory\n");
1300 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1302 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1303 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1304 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1305 checkGLcall("glUnmapBufferARB");
1307 This->resource.allocatedMemory = NULL;
1310 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1312 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1313 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1317 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1318 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1319 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1321 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1322 static BOOL warned = FALSE;
1324 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1330 if(This->dirtyRect.left == 0 &&
1331 This->dirtyRect.top == 0 &&
1332 This->dirtyRect.right == This->currentDesc.Width &&
1333 This->dirtyRect.bottom == This->currentDesc.Height) {
1336 /* TODO: Proper partial rectangle tracking */
1337 fullsurface = FALSE;
1338 This->Flags |= SFLAG_INSYSMEM;
1341 switch(wined3d_settings.rendertargetlock_mode) {
1344 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1345 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1351 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1356 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1357 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1358 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1359 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1360 * not fully up to date because only a subrectangle was read in LockRect.
1362 This->Flags &= ~SFLAG_INSYSMEM;
1363 This->Flags |= SFLAG_INDRAWABLE;
1366 This->dirtyRect.left = This->currentDesc.Width;
1367 This->dirtyRect.top = This->currentDesc.Height;
1368 This->dirtyRect.right = 0;
1369 This->dirtyRect.bottom = 0;
1370 } else if(iface == myDevice->stencilBufferTarget) {
1371 FIXME("Depth Stencil buffer locking is not implemented\n");
1373 /* The rest should be a normal texture */
1374 IWineD3DBaseTextureImpl *impl;
1375 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1376 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1377 * states need resetting
1379 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1380 if(impl->baseTexture.bindCount) {
1381 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1383 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1388 This->Flags &= ~SFLAG_LOCKED;
1389 memset(&This->lockedRect, 0, sizeof(RECT));
1391 /* Overlays have to be redrawn manually after changes with the GL implementation */
1392 if(This->overlay_dest) {
1393 IWineD3DSurface_DrawOverlay(iface);
1398 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1399 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1400 WINED3DLOCKED_RECT lock;
1404 TRACE("(%p)->(%p)\n",This,pHDC);
1406 if(This->Flags & SFLAG_USERPTR) {
1407 ERR("Not supported on surfaces with an application-provided surfaces\n");
1408 return WINEDDERR_NODC;
1411 /* Give more detailed info for ddraw */
1412 if (This->Flags & SFLAG_DCINUSE)
1413 return WINEDDERR_DCALREADYCREATED;
1415 /* Can't GetDC if the surface is locked */
1416 if (This->Flags & SFLAG_LOCKED)
1417 return WINED3DERR_INVALIDCALL;
1419 /* According to Direct3D9 docs, only these formats are supported */
1420 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1421 if (This->resource.format != WINED3DFMT_R5G6B5 &&
1422 This->resource.format != WINED3DFMT_X1R5G5B5 &&
1423 This->resource.format != WINED3DFMT_R8G8B8 &&
1424 This->resource.format != WINED3DFMT_X8R8G8B8) return WINED3DERR_INVALIDCALL;
1427 memset(&lock, 0, sizeof(lock)); /* To be sure */
1429 /* Create a DIB section if there isn't a hdc yet */
1431 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1432 if(This->Flags & SFLAG_CLIENT) {
1433 IWineD3DSurface_PreLoad(iface);
1436 /* Use the dib section from now on if we are not using a PBO */
1437 if(!(This->Flags & SFLAG_PBO))
1438 This->resource.allocatedMemory = This->dib.bitmap_data;
1441 /* Lock the surface */
1442 hr = IWineD3DSurface_LockRect(iface,
1447 if(This->Flags & SFLAG_PBO) {
1448 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1449 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1453 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1454 /* keep the dib section */
1458 if(This->resource.format == WINED3DFMT_P8 ||
1459 This->resource.format == WINED3DFMT_A8P8) {
1460 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1461 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1463 PALETTEENTRY *pal = NULL;
1466 pal = This->palette->palents;
1468 IWineD3DSurfaceImpl *dds_primary;
1469 IWineD3DSwapChainImpl *swapchain;
1470 swapchain = (IWineD3DSwapChainImpl *)This->resource.wineD3DDevice->swapchains[0];
1471 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1472 if (dds_primary && dds_primary->palette)
1473 pal = dds_primary->palette->palents;
1477 for (n=0; n<256; n++) {
1478 col[n].rgbRed = pal[n].peRed;
1479 col[n].rgbGreen = pal[n].peGreen;
1480 col[n].rgbBlue = pal[n].peBlue;
1481 col[n].rgbReserved = 0;
1483 SetDIBColorTable(This->hDC, 0, 256, col);
1488 TRACE("returning %p\n",*pHDC);
1489 This->Flags |= SFLAG_DCINUSE;
1494 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1495 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1497 TRACE("(%p)->(%p)\n",This,hDC);
1499 if (!(This->Flags & SFLAG_DCINUSE))
1500 return WINED3DERR_INVALIDCALL;
1502 if (This->hDC !=hDC) {
1503 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1504 return WINED3DERR_INVALIDCALL;
1507 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1508 /* Copy the contents of the DIB over to the PBO */
1509 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1512 /* we locked first, so unlock now */
1513 IWineD3DSurface_UnlockRect(iface);
1515 This->Flags &= ~SFLAG_DCINUSE;
1520 /* ******************************************************
1521 IWineD3DSurface Internal (No mapping to directx api) parts follow
1522 ****************************************************** */
1524 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) {
1525 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1526 const GlPixelFormatDesc *glDesc;
1527 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1528 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1530 /* Default values: From the surface */
1531 *format = glDesc->glFormat;
1532 *type = glDesc->glType;
1533 *convert = NO_CONVERSION;
1534 *target_bpp = This->bytesPerPixel;
1537 *internal = glDesc->glGammaInternal;
1538 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1539 *internal = glDesc->rtInternal;
1541 *internal = glDesc->glInternal;
1544 /* Ok, now look if we have to do any conversion */
1545 switch(This->resource.format) {
1551 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1552 * of the two is available make sure texturing is requested as neither of the two works in
1553 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1554 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1555 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1556 * conflicts with this.
1558 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) ||
1559 (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) &&
1560 device->render_targets &&
1561 This == (IWineD3DSurfaceImpl*)device->render_targets[0])) ||
1562 colorkey_active || !use_texturing ) {
1564 *internal = GL_RGBA;
1565 *type = GL_UNSIGNED_BYTE;
1567 if(colorkey_active) {
1568 *convert = CONVERT_PALETTED_CK;
1570 *convert = CONVERT_PALETTED;
1573 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1575 *internal = GL_RGBA;
1576 *type = GL_UNSIGNED_BYTE;
1582 case WINED3DFMT_R3G3B2:
1583 /* **********************
1584 GL_UNSIGNED_BYTE_3_3_2
1585 ********************** */
1586 if (colorkey_active) {
1587 /* This texture format will never be used.. So do not care about color keying
1588 up until the point in time it will be needed :-) */
1589 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1593 case WINED3DFMT_R5G6B5:
1594 if (colorkey_active) {
1595 *convert = CONVERT_CK_565;
1597 *internal = GL_RGBA;
1598 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1602 case WINED3DFMT_X1R5G5B5:
1603 if (colorkey_active) {
1604 *convert = CONVERT_CK_5551;
1606 *internal = GL_RGBA;
1607 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1611 case WINED3DFMT_R8G8B8:
1612 if (colorkey_active) {
1613 *convert = CONVERT_CK_RGB24;
1615 *internal = GL_RGBA;
1616 *type = GL_UNSIGNED_INT_8_8_8_8;
1621 case WINED3DFMT_X8R8G8B8:
1622 if (colorkey_active) {
1623 *convert = CONVERT_RGB32_888;
1625 *internal = GL_RGBA;
1626 *type = GL_UNSIGNED_INT_8_8_8_8;
1630 case WINED3DFMT_V8U8:
1631 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1632 *convert = CONVERT_V8U8;
1634 *internal = GL_RGB8;
1635 *type = GL_UNSIGNED_BYTE;
1639 case WINED3DFMT_L6V5U5:
1640 *convert = CONVERT_L6V5U5;
1641 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1643 /* Use format and types from table */
1645 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1648 *internal = GL_RGB5;
1649 *type = GL_UNSIGNED_SHORT_5_6_5;
1653 case WINED3DFMT_X8L8V8U8:
1654 *convert = CONVERT_X8L8V8U8;
1656 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1657 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1658 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1659 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1660 * the needed type and format parameter, so the internal format contains a
1661 * 4th component, which is returned as alpha
1665 *internal = GL_RGB8;
1666 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1670 case WINED3DFMT_Q8W8V8U8:
1671 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1672 *convert = CONVERT_Q8W8V8U8;
1674 *internal = GL_RGBA8;
1675 *type = GL_UNSIGNED_BYTE;
1679 case WINED3DFMT_V16U16:
1680 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1681 *convert = CONVERT_V16U16;
1683 *internal = GL_RGB16_EXT;
1684 *type = GL_UNSIGNED_SHORT;
1688 case WINED3DFMT_A4L4:
1689 /* A4L4 exists as an internal gl format, but for some reason there is not
1690 * format+type combination to load it. Thus convert it to A8L8, then load it
1691 * with A4L4 internal, but A8L8 format+type
1693 *convert = CONVERT_A4L4;
1694 *format = GL_LUMINANCE_ALPHA;
1695 *internal = GL_LUMINANCE4_ALPHA4;
1696 *type = GL_UNSIGNED_BYTE;
1700 case WINED3DFMT_R32F:
1701 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1702 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1703 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1706 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1708 *convert = CONVERT_R32F;
1710 *internal = GL_RGB32F_ARB;
1715 case WINED3DFMT_R16F:
1716 /* Similar to R32F */
1717 *convert = CONVERT_R16F;
1719 *internal = GL_RGB16F_ARB;
1720 *type = GL_HALF_FLOAT_ARB;
1724 case WINED3DFMT_G16R16:
1725 *convert = CONVERT_G16R16;
1727 *internal = GL_RGB16_EXT;
1728 *type = GL_UNSIGNED_SHORT;
1739 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1740 BYTE *source, *dest;
1741 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1746 memcpy(dst, src, pitch * height);
1749 case CONVERT_PALETTED:
1750 case CONVERT_PALETTED_CK:
1752 IWineD3DPaletteImpl* pal = This->palette;
1757 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1760 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1762 for (y = 0; y < height; y++)
1764 source = src + pitch * y;
1765 dest = dst + outpitch * y;
1766 /* This is an 1 bpp format, using the width here is fine */
1767 for (x = 0; x < width; x++) {
1768 BYTE color = *source++;
1769 *dest++ = table[color][0];
1770 *dest++ = table[color][1];
1771 *dest++ = table[color][2];
1772 *dest++ = table[color][3];
1778 case CONVERT_CK_565:
1780 /* Converting the 565 format in 5551 packed to emulate color-keying.
1782 Note : in all these conversion, it would be best to average the averaging
1783 pixels to get the color of the pixel that will be color-keyed to
1784 prevent 'color bleeding'. This will be done later on if ever it is
1787 Note2: Nvidia documents say that their driver does not support alpha + color keying
1788 on the same surface and disables color keying in such a case
1794 TRACE("Color keyed 565\n");
1796 for (y = 0; y < height; y++) {
1797 Source = (WORD *) (src + y * pitch);
1798 Dest = (WORD *) (dst + y * outpitch);
1799 for (x = 0; x < width; x++ ) {
1800 WORD color = *Source++;
1801 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1802 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1803 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1812 case CONVERT_CK_5551:
1814 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1818 TRACE("Color keyed 5551\n");
1819 for (y = 0; y < height; y++) {
1820 Source = (WORD *) (src + y * pitch);
1821 Dest = (WORD *) (dst + y * outpitch);
1822 for (x = 0; x < width; x++ ) {
1823 WORD color = *Source++;
1825 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1826 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1830 *Dest &= ~(1 << 15);
1838 case CONVERT_CK_RGB24:
1840 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
1842 for (y = 0; y < height; y++)
1844 source = src + pitch * y;
1845 dest = dst + outpitch * y;
1846 for (x = 0; x < width; x++) {
1847 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
1848 DWORD dstcolor = color << 8;
1849 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1850 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1853 *(DWORD*)dest = dstcolor;
1861 case CONVERT_RGB32_888:
1863 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
1865 for (y = 0; y < height; y++)
1867 source = src + pitch * y;
1868 dest = dst + outpitch * y;
1869 for (x = 0; x < width; x++) {
1870 DWORD color = 0xffffff & *(DWORD*)source;
1871 DWORD dstcolor = color << 8;
1872 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1873 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1876 *(DWORD*)dest = dstcolor;
1888 unsigned char *Dest;
1889 for(y = 0; y < height; y++) {
1890 Source = (short *) (src + y * pitch);
1891 Dest = dst + y * outpitch;
1892 for (x = 0; x < width; x++ ) {
1893 long color = (*Source++);
1894 /* B */ Dest[0] = 0xff;
1895 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1896 /* R */ Dest[2] = (color) + 128; /* U */
1903 case CONVERT_V16U16:
1907 unsigned short *Dest;
1908 for(y = 0; y < height; y++) {
1909 Source = (DWORD *) (src + y * pitch);
1910 Dest = (unsigned short *) (dst + y * outpitch);
1911 for (x = 0; x < width; x++ ) {
1912 DWORD color = (*Source++);
1913 /* B */ Dest[0] = 0xffff;
1914 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1915 /* R */ Dest[2] = (color ) + 32768; /* U */
1922 case CONVERT_Q8W8V8U8:
1926 unsigned char *Dest;
1927 for(y = 0; y < height; y++) {
1928 Source = (DWORD *) (src + y * pitch);
1929 Dest = dst + y * outpitch;
1930 for (x = 0; x < width; x++ ) {
1931 long color = (*Source++);
1932 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1933 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1934 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1935 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1942 case CONVERT_L6V5U5:
1946 unsigned char *Dest;
1948 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1949 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1950 * fixed function and shaders without further conversion once the surface is
1953 for(y = 0; y < height; y++) {
1954 Source = (WORD *) (src + y * pitch);
1955 Dest = dst + y * outpitch;
1956 for (x = 0; x < width; x++ ) {
1957 short color = (*Source++);
1958 unsigned char l = ((color >> 10) & 0xfc);
1959 char v = ((color >> 5) & 0x3e);
1960 char u = ((color ) & 0x1f);
1962 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1963 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1964 * shift. GL reads a signed value and converts it into an unsigned value.
1966 /* M */ Dest[2] = l << 1;
1968 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1969 * from 5 bit values to 8 bit values.
1971 /* V */ Dest[1] = v << 3;
1972 /* U */ Dest[0] = u << 3;
1977 for(y = 0; y < height; y++) {
1978 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1979 Source = (WORD *) (src + y * pitch);
1980 for (x = 0; x < width; x++ ) {
1981 short color = (*Source++);
1982 unsigned char l = ((color >> 10) & 0xfc);
1983 short v = ((color >> 5) & 0x3e);
1984 short u = ((color ) & 0x1f);
1985 short v_conv = v + 16;
1986 short u_conv = u + 16;
1988 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1996 case CONVERT_X8L8V8U8:
2000 unsigned char *Dest;
2002 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2003 /* This implementation works with the fixed function pipeline and shaders
2004 * without further modification after converting the surface.
2006 for(y = 0; y < height; y++) {
2007 Source = (DWORD *) (src + y * pitch);
2008 Dest = dst + y * outpitch;
2009 for (x = 0; x < width; x++ ) {
2010 long color = (*Source++);
2011 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2012 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2013 /* U */ Dest[0] = (color & 0xff); /* U */
2014 /* I */ Dest[3] = 255; /* X */
2019 /* Doesn't work correctly with the fixed function pipeline, but can work in
2020 * shaders if the shader is adjusted. (There's no use for this format in gl's
2021 * standard fixed function pipeline anyway).
2023 for(y = 0; y < height; y++) {
2024 Source = (DWORD *) (src + y * pitch);
2025 Dest = dst + y * outpitch;
2026 for (x = 0; x < width; x++ ) {
2027 long color = (*Source++);
2028 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2029 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2030 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2041 unsigned char *Source;
2042 unsigned char *Dest;
2043 for(y = 0; y < height; y++) {
2044 Source = src + y * pitch;
2045 Dest = dst + y * outpitch;
2046 for (x = 0; x < width; x++ ) {
2047 unsigned char color = (*Source++);
2048 /* A */ Dest[1] = (color & 0xf0) << 0;
2049 /* L */ Dest[0] = (color & 0x0f) << 4;
2061 for(y = 0; y < height; y++) {
2062 Source = (float *) (src + y * pitch);
2063 Dest = (float *) (dst + y * outpitch);
2064 for (x = 0; x < width; x++ ) {
2065 float color = (*Source++);
2081 for(y = 0; y < height; y++) {
2082 Source = (WORD *) (src + y * pitch);
2083 Dest = (WORD *) (dst + y * outpitch);
2084 for (x = 0; x < width; x++ ) {
2085 WORD color = (*Source++);
2095 case CONVERT_G16R16:
2101 for(y = 0; y < height; y++) {
2102 Source = (WORD *) (src + y * pitch);
2103 Dest = (WORD *) (dst + y * outpitch);
2104 for (x = 0; x < width; x++ ) {
2105 WORD green = (*Source++);
2106 WORD red = (*Source++);
2117 ERR("Unsupported conversation type %d\n", convert);
2122 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2123 IWineD3DPaletteImpl* pal = This->palette;
2124 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2125 BOOL index_in_alpha = FALSE;
2126 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2129 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2130 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2131 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2132 * duplicate entries. Store the color key in the unused alpha component to speed the
2133 * download up and to make conversion unneeded. */
2134 index_in_alpha = primary_render_target_is_p8(device);
2137 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2138 if(dxVersion <= 7) {
2139 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2140 if(index_in_alpha) {
2141 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2142 there's no palette at this time. */
2143 for (i = 0; i < 256; i++) table[i][3] = i;
2146 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2147 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2148 capability flag is present (wine does advertise this capability) */
2149 for (i = 0; i < 256; i++) {
2150 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2151 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2152 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2153 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2157 TRACE("Using surface palette %p\n", pal);
2158 /* Get the surface's palette */
2159 for (i = 0; i < 256; i++) {
2160 table[i][0] = pal->palents[i].peRed;
2161 table[i][1] = pal->palents[i].peGreen;
2162 table[i][2] = pal->palents[i].peBlue;
2164 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2165 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2166 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2167 of pixels that should be masked away is set to 0. */
2168 if(index_in_alpha) {
2170 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2172 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2173 table[i][3] = pal->palents[i].peFlags;
2181 const char *fragment_palette_conversion =
2184 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
2185 "TEX index, fragment.texcoord[0], texture[0], 2D;\n" /* The alpha-component contains the palette index */
2186 "MAD index.a, index.a, constants.x, constants.y;\n" /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2187 "TEX result.color, index.a, texture[1], 1D;\n" /* use the alpha-component as a index in the palette to get the final color */
2190 /* This function is used in case of 8bit paletted textures to upload the palette.
2191 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2192 extensions like ATI_fragment_shaders is possible.
2194 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2195 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2197 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2199 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2201 /* Try to use the paletted texture extension */
2202 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2204 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2205 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2209 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2210 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2211 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2213 /* Create the fragment program if we don't have it */
2214 if(!device->paletteConversionShader)
2216 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2217 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2218 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2219 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2220 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2223 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2224 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2226 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2227 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2229 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2230 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2231 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2232 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2234 /* Switch back to unit 0 in which the 2D texture will be stored. */
2235 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2237 /* Rebind the texture because it isn't bound anymore */
2238 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2242 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2243 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2245 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2246 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2247 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2252 if(This->palette9) {
2253 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2257 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2259 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2263 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2264 GLboolean oldwrite[4];
2266 /* Some formats have only some color channels, and the others are 1.0.
2267 * since our rendering renders to all channels, and those pixel formats
2268 * are emulated by using a full texture with the other channels set to 1.0
2269 * manually, clear the unused channels.
2271 * This could be done with hacking colorwriteenable to mask the colors,
2272 * but before drawing the buffer would have to be cleared too, so there's
2275 switch(This->resource.format) {
2276 case WINED3DFMT_R16F:
2277 case WINED3DFMT_R32F:
2278 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2279 /* Do not activate a context, the correct drawable is active already
2280 * though just the read buffer is set, make sure to have the correct draw
2283 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2284 glDisable(GL_SCISSOR_TEST);
2285 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2286 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2287 glClearColor(0.0, 1.0, 1.0, 1.0);
2288 glClear(GL_COLOR_BUFFER_BIT);
2289 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2290 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2291 checkGLcall("Unused channel clear\n");
2298 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2299 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2301 if (!(This->Flags & SFLAG_INTEXTURE)) {
2302 TRACE("Reloading because surface is dirty\n");
2303 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2304 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2305 /* Reload: vice versa OR */
2306 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2307 /* Also reload: Color key is active AND the color key has changed */
2308 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2309 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2310 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2311 TRACE("Reloading because of color keying\n");
2312 /* To perform the color key conversion we need a sysmem copy of
2313 * the surface. Make sure we have it
2316 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2317 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2318 /* TODO: This is not necessarily needed with hw palettized texture support */
2319 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2321 TRACE("surface is already in texture\n");
2325 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2326 * These resources are not bound by device size or format restrictions. Because of this,
2327 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2328 * However, these resources can always be created, locked, and copied.
2330 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2332 FIXME("(%p) Operation not supported for scratch textures\n",This);
2333 return WINED3DERR_INVALIDCALL;
2336 This->srgb = srgb_mode;
2337 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2341 static unsigned int gen = 0;
2344 if ((gen % 10) == 0) {
2345 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2346 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2349 * debugging crash code
2358 if (!(This->Flags & SFLAG_DONOTFREE)) {
2359 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2360 This->resource.allocatedMemory = NULL;
2361 This->resource.heapMemory = NULL;
2362 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2368 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2369 /* TODO: check for locks */
2370 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2371 IWineD3DBaseTexture *baseTexture = NULL;
2372 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2374 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2375 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2376 TRACE("Passing to container\n");
2377 IWineD3DBaseTexture_BindTexture(baseTexture);
2378 IWineD3DBaseTexture_Release(baseTexture);
2380 TRACE("(%p) : Binding surface\n", This);
2382 if(!device->isInDraw) {
2383 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2388 if (!This->glDescription.level) {
2389 if (!This->glDescription.textureName) {
2390 glGenTextures(1, &This->glDescription.textureName);
2391 checkGLcall("glGenTextures");
2392 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
2394 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2395 checkGLcall("glBindTexture");
2396 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2397 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2398 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2399 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2400 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2401 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2402 glTexParameteri(This->glDescription.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2403 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2404 glTexParameteri(This->glDescription.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2405 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2407 /* This is where we should be reducing the amount of GLMemoryUsed */
2408 } else if (This->glDescription.textureName) {
2409 /* Mipmap surfaces should have a base texture container */
2410 ERR("Mipmap surface has a glTexture bound to it!\n");
2413 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2414 checkGLcall("glBindTexture");
2423 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2426 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2427 char *allocatedMemory;
2429 IWineD3DSwapChain *swapChain = NULL;
2431 GLuint tmpTexture = 0;
2434 Textures may not be stored in ->allocatedgMemory and a GlTexture
2435 so we should lock the surface before saving a snapshot, or at least check that
2437 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2438 by calling GetTexImage and in compressed form by calling
2439 GetCompressedTexImageARB. Queried compressed images can be saved and
2440 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2441 texture images do not need to be processed by the GL and should
2442 significantly improve texture loading performance relative to uncompressed
2445 /* Setup the width and height to be the internal texture width and height. */
2446 width = This->pow2Width;
2447 height = This->pow2Height;
2448 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2449 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2451 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2452 /* if were not a real texture then read the back buffer into a real texture */
2453 /* we don't want to interfere with the back buffer so read the data into a temporary
2454 * texture and then save the data out of the temporary texture
2458 TRACE("(%p) Reading render target into texture\n", This);
2460 glGenTextures(1, &tmpTexture);
2461 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2463 glTexImage2D(GL_TEXTURE_2D,
2470 GL_UNSIGNED_INT_8_8_8_8_REV,
2473 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2474 vcheckGLcall("glGetIntegerv");
2475 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2476 vcheckGLcall("glReadBuffer");
2477 glCopyTexImage2D(GL_TEXTURE_2D,
2486 checkGLcall("glCopyTexImage2D");
2487 glReadBuffer(prevRead);
2490 } else { /* bind the real texture, and make sure it up to date */
2491 IWineD3DSurface_PreLoad(iface);
2492 surface_bind_and_dirtify(This);
2494 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2496 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2497 glGetTexImage(GL_TEXTURE_2D,
2498 This->glDescription.level,
2500 GL_UNSIGNED_INT_8_8_8_8_REV,
2502 checkGLcall("glTexImage2D");
2504 glBindTexture(GL_TEXTURE_2D, 0);
2505 glDeleteTextures(1, &tmpTexture);
2509 f = fopen(filename, "w+");
2511 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2512 return WINED3DERR_INVALIDCALL;
2514 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2515 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2530 fwrite(&width,2,1,f);
2532 fwrite(&height,2,1,f);
2537 /* 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 */
2539 textureRow = allocatedMemory + (width * (height - 1) *4);
2541 textureRow = allocatedMemory;
2542 for (y = 0 ; y < height; y++) {
2543 for (i = 0; i < width; i++) {
2544 color = *((DWORD*)textureRow);
2545 fputc((color >> 16) & 0xFF, f); /* B */
2546 fputc((color >> 8) & 0xFF, f); /* G */
2547 fputc((color >> 0) & 0xFF, f); /* R */
2548 fputc((color >> 24) & 0xFF, f); /* A */
2551 /* take two rows of the pointer to the texture memory */
2553 (textureRow-= width << 3);
2556 TRACE("Closing file\n");
2560 IWineD3DSwapChain_Release(swapChain);
2562 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2567 * Slightly inefficient way to handle multiple dirty rects but it works :)
2569 HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2570 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2571 IWineD3DBaseTexture *baseTexture = NULL;
2573 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2574 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2576 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2577 if (NULL != pDirtyRect) {
2578 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2579 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2580 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2581 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2583 This->dirtyRect.left = 0;
2584 This->dirtyRect.top = 0;
2585 This->dirtyRect.right = This->currentDesc.Width;
2586 This->dirtyRect.bottom = This->currentDesc.Height;
2588 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2589 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2590 /* if the container is a basetexture then mark it dirty. */
2591 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2592 TRACE("Passing to container\n");
2593 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2594 IWineD3DBaseTexture_Release(baseTexture);
2599 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2600 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2602 const GlPixelFormatDesc *glDesc;
2603 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2605 TRACE("(%p) : Calling base function first\n", This);
2606 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2608 /* Setup some glformat defaults */
2609 This->glDescription.glFormat = glDesc->glFormat;
2610 This->glDescription.glFormatInternal = glDesc->glInternal;
2611 This->glDescription.glType = glDesc->glType;
2613 This->Flags &= ~SFLAG_ALLOCATED;
2614 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2615 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2620 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2621 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2623 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2624 WARN("Surface is locked or the HDC is in use\n");
2625 return WINED3DERR_INVALIDCALL;
2628 if(Mem && Mem != This->resource.allocatedMemory) {
2629 void *release = NULL;
2631 /* Do I have to copy the old surface content? */
2632 if(This->Flags & SFLAG_DIBSECTION) {
2633 /* Release the DC. No need to hold the critical section for the update
2634 * Thread because this thread runs only on front buffers, but this method
2635 * fails for render targets in the check above.
2637 SelectObject(This->hDC, This->dib.holdbitmap);
2638 DeleteDC(This->hDC);
2639 /* Release the DIB section */
2640 DeleteObject(This->dib.DIBsection);
2641 This->dib.bitmap_data = NULL;
2642 This->resource.allocatedMemory = NULL;
2644 This->Flags &= ~SFLAG_DIBSECTION;
2645 } else if(!(This->Flags & SFLAG_USERPTR)) {
2646 release = This->resource.heapMemory;
2647 This->resource.heapMemory = NULL;
2649 This->resource.allocatedMemory = Mem;
2650 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2652 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2653 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2655 /* For client textures opengl has to be notified */
2656 if(This->Flags & SFLAG_CLIENT) {
2657 This->Flags &= ~SFLAG_ALLOCATED;
2658 IWineD3DSurface_PreLoad(iface);
2659 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2662 /* Now free the old memory if any */
2663 HeapFree(GetProcessHeap(), 0, release);
2664 } else if(This->Flags & SFLAG_USERPTR) {
2665 /* LockRect and GetDC will re-create the dib section and allocated memory */
2666 This->resource.allocatedMemory = NULL;
2667 /* HeapMemory should be NULL already */
2668 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2669 This->Flags &= ~SFLAG_USERPTR;
2671 if(This->Flags & SFLAG_CLIENT) {
2672 This->Flags &= ~SFLAG_ALLOCATED;
2673 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2674 IWineD3DSurface_PreLoad(iface);
2680 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2682 /* Flip the surface contents */
2687 front->hDC = back->hDC;
2691 /* Flip the DIBsection */
2694 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2695 tmp = front->dib.DIBsection;
2696 front->dib.DIBsection = back->dib.DIBsection;
2697 back->dib.DIBsection = tmp;
2699 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2700 else front->Flags &= ~SFLAG_DIBSECTION;
2701 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2702 else back->Flags &= ~SFLAG_DIBSECTION;
2705 /* Flip the surface data */
2709 tmp = front->dib.bitmap_data;
2710 front->dib.bitmap_data = back->dib.bitmap_data;
2711 back->dib.bitmap_data = tmp;
2713 tmp = front->resource.allocatedMemory;
2714 front->resource.allocatedMemory = back->resource.allocatedMemory;
2715 back->resource.allocatedMemory = tmp;
2717 tmp = front->resource.heapMemory;
2718 front->resource.heapMemory = back->resource.heapMemory;
2719 back->resource.heapMemory = tmp;
2724 GLuint tmp_pbo = front->pbo;
2725 front->pbo = back->pbo;
2726 back->pbo = tmp_pbo;
2729 /* client_memory should not be different, but just in case */
2732 tmp = front->dib.client_memory;
2733 front->dib.client_memory = back->dib.client_memory;
2734 back->dib.client_memory = tmp;
2737 /* Flip the opengl texture */
2739 glDescriptor tmp_desc = back->glDescription;
2740 back->glDescription = front->glDescription;
2741 front->glDescription = tmp_desc;
2745 DWORD tmp_flags = back->Flags;
2746 back->Flags = front->Flags;
2747 front->Flags = tmp_flags;
2751 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2752 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2753 IWineD3DSwapChainImpl *swapchain = NULL;
2755 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2757 /* Flipping is only supported on RenderTargets and overlays*/
2758 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2759 WARN("Tried to flip a non-render target, non-overlay surface\n");
2760 return WINEDDERR_NOTFLIPPABLE;
2763 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2764 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2766 /* Update the overlay if it is visible */
2767 if(This->overlay_dest) {
2768 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
2775 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2776 * FIXME("(%p) Target override is not supported by now\n", This);
2777 * Additionally, it isn't really possible to support triple-buffering
2778 * properly on opengl at all
2782 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2784 ERR("Flipped surface is not on a swapchain\n");
2785 return WINEDDERR_NOTFLIPPABLE;
2788 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2789 * and only d3d8 and d3d9 apps specify the presentation interval
2791 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2792 /* Most common case first to avoid wasting time on all the other cases */
2793 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2794 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2795 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2796 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2797 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2798 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2799 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2801 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2804 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2805 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2806 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2810 /* Does a direct frame buffer -> texture copy. Stretching is done
2811 * with single pixel copy calls
2813 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2814 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2817 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2820 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2821 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2824 /* Bind the target texture */
2825 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2826 checkGLcall("glBindTexture");
2828 TRACE("Reading from an offscreen target\n");
2829 upsidedown = !upsidedown;
2830 glReadBuffer(myDevice->offscreenBuffer);
2832 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2833 glReadBuffer(buffer);
2835 checkGLcall("glReadBuffer");
2837 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2838 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2840 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2841 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2843 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2844 ERR("Texture filtering not supported in direct blit\n");
2846 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2847 ERR("Texture filtering not supported in direct blit\n");
2851 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2852 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2853 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2855 glCopyTexSubImage2D(This->glDescription.target,
2856 This->glDescription.level,
2857 drect->x1, drect->y1, /* xoffset, yoffset */
2858 srect->x1, Src->currentDesc.Height - srect->y2,
2859 drect->x2 - drect->x1, drect->y2 - drect->y1);
2861 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2862 /* I have to process this row by row to swap the image,
2863 * otherwise it would be upside down, so stretching in y direction
2864 * doesn't cost extra time
2866 * However, stretching in x direction can be avoided if not necessary
2868 for(row = drect->y1; row < drect->y2; row++) {
2869 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2870 /* Well, that stuff works, but it's very slow.
2871 * find a better way instead
2875 for(col = drect->x1; col < drect->x2; col++) {
2876 glCopyTexSubImage2D(This->glDescription.target,
2877 This->glDescription.level,
2878 drect->x1 + col, row, /* xoffset, yoffset */
2879 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2883 glCopyTexSubImage2D(This->glDescription.target,
2884 This->glDescription.level,
2885 drect->x1, row, /* xoffset, yoffset */
2886 srect->x1, yoffset - (int) (row * yrel),
2887 drect->x2-drect->x1, 1);
2891 vcheckGLcall("glCopyTexSubImage2D");
2896 /* Uses the hardware to stretch and flip the image */
2897 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2898 GLuint src, backup = 0;
2899 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2900 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2901 float left, right, top, bottom; /* Texture coordinates */
2902 UINT fbwidth = Src->currentDesc.Width;
2903 UINT fbheight = Src->currentDesc.Height;
2904 GLenum drawBuffer = GL_BACK;
2905 GLenum texture_target;
2906 BOOL noBackBufferBackup;
2908 TRACE("Using hwstretch blit\n");
2909 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2910 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2911 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2913 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2914 if(!noBackBufferBackup && Src->glDescription.textureName == 0) {
2915 /* Get it a description */
2916 IWineD3DSurface_PreLoad(SrcSurface);
2920 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2921 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2923 if(myDevice->activeContext->aux_buffers >= 2) {
2924 /* Got more than one aux buffer? Use the 2nd aux buffer */
2925 drawBuffer = GL_AUX1;
2926 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && myDevice->activeContext->aux_buffers >= 1) {
2927 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2928 drawBuffer = GL_AUX0;
2931 if(noBackBufferBackup) {
2932 glGenTextures(1, &backup);
2933 checkGLcall("glGenTextures\n");
2934 glBindTexture(GL_TEXTURE_2D, backup);
2935 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2936 texture_target = GL_TEXTURE_2D;
2938 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2939 * we are reading from the back buffer, the backup can be used as source texture
2941 texture_target = Src->glDescription.target;
2942 glBindTexture(texture_target, Src->glDescription.textureName);
2943 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2944 glEnable(texture_target);
2945 checkGLcall("glEnable(texture_target)");
2947 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2948 Src->Flags &= ~SFLAG_INTEXTURE;
2952 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
2954 TRACE("Reading from an offscreen target\n");
2955 upsidedown = !upsidedown;
2956 glReadBuffer(myDevice->offscreenBuffer);
2959 /* TODO: Only back up the part that will be overwritten */
2960 glCopyTexSubImage2D(texture_target, 0,
2961 0, 0 /* read offsets */,
2966 checkGLcall("glCopyTexSubImage2D");
2968 /* No issue with overriding these - the sampler is dirty due to blit usage */
2969 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2970 magLookup[Filter - WINED3DTEXF_NONE]);
2971 checkGLcall("glTexParameteri");
2972 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2973 minMipLookup[Filter][WINED3DTEXF_NONE]);
2974 checkGLcall("glTexParameteri");
2976 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2977 src = backup ? backup : Src->glDescription.textureName;
2979 glReadBuffer(GL_FRONT);
2980 checkGLcall("glReadBuffer(GL_FRONT)");
2982 glGenTextures(1, &src);
2983 checkGLcall("glGenTextures(1, &src)");
2984 glBindTexture(GL_TEXTURE_2D, src);
2985 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2987 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2988 * out for power of 2 sizes
2990 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2991 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2992 checkGLcall("glTexImage2D");
2993 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2994 0, 0 /* read offsets */,
2999 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3000 checkGLcall("glTexParameteri");
3001 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3002 checkGLcall("glTexParameteri");
3004 glReadBuffer(GL_BACK);
3005 checkGLcall("glReadBuffer(GL_BACK)");
3007 if(texture_target != GL_TEXTURE_2D) {
3008 glDisable(texture_target);
3009 glEnable(GL_TEXTURE_2D);
3010 texture_target = GL_TEXTURE_2D;
3013 checkGLcall("glEnd and previous");
3019 top = Src->currentDesc.Height - srect->y1;
3020 bottom = Src->currentDesc.Height - srect->y2;
3022 top = Src->currentDesc.Height - srect->y2;
3023 bottom = Src->currentDesc.Height - srect->y1;
3026 if(Src->Flags & SFLAG_NORMCOORD) {
3027 left /= Src->pow2Width;
3028 right /= Src->pow2Width;
3029 top /= Src->pow2Height;
3030 bottom /= Src->pow2Height;
3033 /* draw the source texture stretched and upside down. The correct surface is bound already */
3034 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3035 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3037 glDrawBuffer(drawBuffer);
3038 glReadBuffer(drawBuffer);
3042 glTexCoord2f(left, bottom);
3043 glVertex2i(0, fbheight);
3046 glTexCoord2f(left, top);
3047 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3050 glTexCoord2f(right, top);
3051 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3054 glTexCoord2f(right, bottom);
3055 glVertex2i(drect->x2 - drect->x1, fbheight);
3057 checkGLcall("glEnd and previous");
3059 if(texture_target != This->glDescription.target) {
3060 glDisable(texture_target);
3061 glEnable(This->glDescription.target);
3062 texture_target = This->glDescription.target;
3065 /* Now read the stretched and upside down image into the destination texture */
3066 glBindTexture(texture_target, This->glDescription.textureName);
3067 checkGLcall("glBindTexture");
3068 glCopyTexSubImage2D(texture_target,
3070 drect->x1, drect->y1, /* xoffset, yoffset */
3071 0, 0, /* We blitted the image to the origin */
3072 drect->x2 - drect->x1, drect->y2 - drect->y1);
3073 checkGLcall("glCopyTexSubImage2D");
3075 if(drawBuffer == GL_BACK) {
3076 /* Write the back buffer backup back */
3078 if(texture_target != GL_TEXTURE_2D) {
3079 glDisable(texture_target);
3080 glEnable(GL_TEXTURE_2D);
3081 texture_target = GL_TEXTURE_2D;
3083 glBindTexture(GL_TEXTURE_2D, backup);
3084 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3086 if(texture_target != Src->glDescription.target) {
3087 glDisable(texture_target);
3088 glEnable(Src->glDescription.target);
3089 texture_target = Src->glDescription.target;
3091 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3092 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
3097 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
3101 glTexCoord2f(0.0, 0.0);
3102 glVertex2i(0, fbheight);
3105 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
3106 glVertex2i(fbwidth, Src->currentDesc.Height);
3109 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3110 glVertex2i(fbwidth, 0);
3113 /* Restore the old draw buffer */
3114 glDrawBuffer(GL_BACK);
3116 glDisable(texture_target);
3117 checkGLcall("glDisable(texture_target)");
3120 if(src != Src->glDescription.textureName && src != backup) {
3121 glDeleteTextures(1, &src);
3122 checkGLcall("glDeleteTextures(1, &src)");
3125 glDeleteTextures(1, &backup);
3126 checkGLcall("glDeleteTextures(1, &backup)");
3132 /* Not called from the VTable */
3133 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3134 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3135 WINED3DTEXTUREFILTERTYPE Filter)
3138 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3139 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3140 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3142 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3144 /* Get the swapchain. One of the surfaces has to be a primary surface */
3145 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3146 WARN("Destination is in sysmem, rejecting gl blt\n");
3147 return WINED3DERR_INVALIDCALL;
3149 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3150 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3152 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3153 WARN("Src is in sysmem, rejecting gl blt\n");
3154 return WINED3DERR_INVALIDCALL;
3156 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3157 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3160 /* Early sort out of cases where no render target is used */
3161 if(!dstSwapchain && !srcSwapchain &&
3162 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3163 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3164 return WINED3DERR_INVALIDCALL;
3167 /* No destination color keying supported */
3168 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3169 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3170 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3171 return WINED3DERR_INVALIDCALL;
3175 rect.x1 = DestRect->left;
3176 rect.y1 = DestRect->top;
3177 rect.x2 = DestRect->right;
3178 rect.y2 = DestRect->bottom;
3182 rect.x2 = This->currentDesc.Width;
3183 rect.y2 = This->currentDesc.Height;
3186 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3187 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3188 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3189 /* Half-life does a Blt from the back buffer to the front buffer,
3190 * Full surface size, no flags... Use present instead
3192 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3195 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3199 TRACE("Looking if a Present can be done...\n");
3200 /* Source Rectangle must be full surface */
3202 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3203 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3204 TRACE("No, Source rectangle doesn't match\n");
3210 mySrcRect.right = Src->currentDesc.Width;
3211 mySrcRect.bottom = Src->currentDesc.Height;
3213 /* No stretching may occur */
3214 if(mySrcRect.right != rect.x2 - rect.x1 ||
3215 mySrcRect.bottom != rect.y2 - rect.y1) {
3216 TRACE("No, stretching is done\n");
3220 /* Destination must be full surface or match the clipping rectangle */
3221 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3225 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3230 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3233 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3234 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3236 TRACE("No, dest rectangle doesn't match(clipper)\n");
3237 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3238 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3244 if(rect.x1 != 0 || rect.y1 != 0 ||
3245 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3246 TRACE("No, dest rectangle doesn't match(surface size)\n");
3253 /* These flags are unimportant for the flag check, remove them */
3254 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3255 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3257 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3258 * take very long, while a flip is fast.
3259 * This applies to Half-Life, which does such Blts every time it finished
3260 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3261 * menu. This is also used by all apps when they do windowed rendering
3263 * The problem is that flipping is not really the same as copying. After a
3264 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3265 * untouched. Therefore it's necessary to override the swap effect
3266 * and to set it back after the flip.
3268 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3272 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3273 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3275 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3276 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3278 dstSwapchain->presentParms.SwapEffect = orig_swap;
3285 TRACE("Unsupported blit between buffers on the same swapchain\n");
3286 return WINED3DERR_INVALIDCALL;
3287 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3288 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3289 return WINED3DERR_INVALIDCALL;
3290 } else if(dstSwapchain && srcSwapchain) {
3291 FIXME("Implement hardware blit between two different swapchains\n");
3292 return WINED3DERR_INVALIDCALL;
3293 } else if(dstSwapchain) {
3294 if(SrcSurface == myDevice->render_targets[0]) {
3295 TRACE("Blit from active render target to a swapchain\n");
3296 /* Handled with regular texture -> swapchain blit */
3298 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3299 FIXME("Implement blit from a swapchain to the active render target\n");
3300 return WINED3DERR_INVALIDCALL;
3303 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3304 /* Blit from render target to texture */
3306 BOOL upsideDown, stretchx;
3307 BOOL paletteOverride = FALSE;
3309 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3310 TRACE("Color keying not supported by frame buffer to texture blit\n");
3311 return WINED3DERR_INVALIDCALL;
3312 /* Destination color key is checked above */
3315 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3316 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3319 if(SrcRect->top < SrcRect->bottom) {
3320 srect.y1 = SrcRect->top;
3321 srect.y2 = SrcRect->bottom;
3324 srect.y1 = SrcRect->bottom;
3325 srect.y2 = SrcRect->top;
3328 srect.x1 = SrcRect->left;
3329 srect.x2 = SrcRect->right;
3333 srect.x2 = Src->currentDesc.Width;
3334 srect.y2 = Src->currentDesc.Height;
3337 if(rect.x1 > rect.x2) {
3341 upsideDown = !upsideDown;
3344 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3350 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3351 * In this case grab the palette from the render target. */
3352 if((This->resource.format == WINED3DFMT_P8) && (This->palette == NULL)) {
3353 paletteOverride = TRUE;
3354 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3355 This->palette = Src->palette;
3358 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3359 * flip the image nor scale it.
3361 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3362 * -> If the app wants a image width an unscaled width, copy it line per line
3363 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3364 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3365 * back buffer. This is slower than reading line per line, thus not used for flipping
3366 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3369 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3370 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3373 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3374 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3375 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3376 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3377 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3378 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3379 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3381 TRACE("Using hardware stretching to flip / stretch the texture\n");
3382 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3385 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3387 This->palette = NULL;
3389 if(!(This->Flags & SFLAG_DONOTFREE)) {
3390 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3391 This->resource.allocatedMemory = NULL;
3392 This->resource.heapMemory = NULL;
3394 This->Flags &= ~SFLAG_INSYSMEM;
3396 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3397 * path is never entered
3399 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3403 /* Blit from offscreen surface to render target */
3404 float glTexCoord[4];
3405 DWORD oldCKeyFlags = Src->CKeyFlags;
3406 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3407 RECT SourceRectangle;
3408 BOOL paletteOverride = FALSE;
3410 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3413 SourceRectangle.left = SrcRect->left;
3414 SourceRectangle.right = SrcRect->right;
3415 SourceRectangle.top = SrcRect->top;
3416 SourceRectangle.bottom = SrcRect->bottom;
3418 SourceRectangle.left = 0;
3419 SourceRectangle.right = Src->currentDesc.Width;
3420 SourceRectangle.top = 0;
3421 SourceRectangle.bottom = Src->currentDesc.Height;
3424 /* When blitting from an offscreen surface to a rendertarget, the source
3425 * surface is not required to have a palette. Our rendering / conversion
3426 * code further down the road retrieves the palette from the surface, so
3427 * it must have a palette set. */
3428 if((Src->resource.format == WINED3DFMT_P8) && (Src->palette == NULL)) {
3429 paletteOverride = TRUE;
3430 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3431 Src->palette = This->palette;
3434 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3435 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3436 TRACE("Using stretch_rect_fbo\n");
3437 /* The source is always a texture, but never the currently active render target, and the texture
3438 * contents are never upside down
3440 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3441 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3443 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3445 Src->palette = NULL;
3449 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3450 /* Fall back to software */
3451 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3452 SourceRectangle.left, SourceRectangle.top,
3453 SourceRectangle.right, SourceRectangle.bottom);
3454 return WINED3DERR_INVALIDCALL;
3457 /* Color keying: Check if we have to do a color keyed blt,
3458 * and if not check if a color key is activated.
3460 * Just modify the color keying parameters in the surface and restore them afterwards
3461 * The surface keeps track of the color key last used to load the opengl surface.
3462 * PreLoad will catch the change to the flags and color key and reload if necessary.
3464 if(Flags & WINEDDBLT_KEYSRC) {
3465 /* Use color key from surface */
3466 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3467 /* Use color key from DDBltFx */
3468 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3469 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3471 /* Do not use color key */
3472 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3475 /* Now load the surface */
3476 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3478 /* Activate the destination context, set it up for blitting */
3479 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3481 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3482 * while OpenGL coordinates are window relative.
3483 * Also beware of the origin difference(top left vs bottom left).
3484 * Also beware that the front buffer's surface size is screen width x screen height,
3485 * whereas the real gl drawable size is the size of the window.
3487 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3489 POINT offset = {0,0};
3491 ClientToScreen(dstSwapchain->win_handle, &offset);
3492 GetClientRect(dstSwapchain->win_handle, &windowsize);
3493 h = windowsize.bottom - windowsize.top;
3494 rect.x1 -= offset.x; rect.x2 -=offset.x;
3495 rect.y1 -= offset.y; rect.y2 -=offset.y;
3496 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3499 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format,
3500 Src->glDescription.target, Src->pow2Width, Src->pow2Height);
3504 /* Bind the texture */
3505 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3506 checkGLcall("glBindTexture");
3508 /* Filtering for StretchRect */
3509 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3510 magLookup[Filter - WINED3DTEXF_NONE]);
3511 checkGLcall("glTexParameteri");
3512 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3513 minMipLookup[Filter][WINED3DTEXF_NONE]);
3514 checkGLcall("glTexParameteri");
3515 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3516 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3517 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3518 checkGLcall("glTexEnvi");
3520 /* This is for color keying */
3521 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3522 glEnable(GL_ALPHA_TEST);
3523 checkGLcall("glEnable GL_ALPHA_TEST");
3525 /* When the primary render target uses P8, the alpha component contains the palette index.
3526 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3527 * should be masked away have alpha set to 0. */
3528 if(primary_render_target_is_p8(myDevice))
3529 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3531 glAlphaFunc(GL_NOTEQUAL, 0.0);
3532 checkGLcall("glAlphaFunc\n");
3534 glDisable(GL_ALPHA_TEST);
3535 checkGLcall("glDisable GL_ALPHA_TEST");
3538 /* Draw a textured quad
3542 glColor3d(1.0f, 1.0f, 1.0f);
3543 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3548 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3549 glVertex3f(rect.x1, rect.y2, 0.0);
3551 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3556 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3561 checkGLcall("glEnd");
3563 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3564 glDisable(GL_ALPHA_TEST);
3565 checkGLcall("glDisable(GL_ALPHA_TEST)");
3568 glBindTexture(Src->glDescription.target, 0);
3569 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3571 /* Restore the color key parameters */
3572 Src->CKeyFlags = oldCKeyFlags;
3573 Src->SrcBltCKey = oldBltCKey;
3575 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3577 Src->palette = NULL;
3581 /* Leave the opengl state valid for blitting */
3582 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3584 /* Flush in case the drawable is used by multiple GL contexts */
3585 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3588 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3589 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3592 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3596 /* Source-Less Blit to render target */
3597 if (Flags & WINEDDBLT_COLORFILL) {
3598 /* This is easy to handle for the D3D Device... */
3601 TRACE("Colorfill\n");
3603 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3604 must be true if we are here */
3605 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3606 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3607 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3608 TRACE("Surface is higher back buffer, falling back to software\n");
3609 return WINED3DERR_INVALIDCALL;
3612 /* The color as given in the Blt function is in the format of the frame-buffer...
3613 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3615 if (This->resource.format == WINED3DFMT_P8) {
3618 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3619 else alpha = 0xFF000000;
3621 if (This->palette) {
3623 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3624 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3625 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3630 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3631 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3634 color = ((0xFF000000) |
3635 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3636 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3637 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3640 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3641 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3642 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3644 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3645 color = DDBltFx->u5.dwFillColor;
3648 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3649 return WINED3DERR_INVALIDCALL;
3652 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3653 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3654 1, /* Number of rectangles */
3655 &rect, WINED3DCLEAR_TARGET, color,
3662 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3663 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3664 return WINED3DERR_INVALIDCALL;
3667 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3668 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3670 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3673 if (Flags & WINEDDBLT_DEPTHFILL) {
3674 switch(This->resource.format) {
3675 case WINED3DFMT_D16:
3676 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3678 case WINED3DFMT_D15S1:
3679 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3681 case WINED3DFMT_D24S8:
3682 case WINED3DFMT_D24X8:
3683 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3685 case WINED3DFMT_D32:
3686 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3690 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3693 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3694 DestRect == NULL ? 0 : 1,
3695 (const WINED3DRECT *)DestRect,
3696 WINED3DCLEAR_ZBUFFER,
3702 FIXME("(%p): Unsupp depthstencil blit\n", This);
3703 return WINED3DERR_INVALIDCALL;
3706 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3707 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3708 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3709 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3710 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3711 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3712 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3714 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3716 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3717 return WINEDDERR_SURFACEBUSY;
3720 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3721 * except depth blits, which seem to work
3723 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3724 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3725 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3726 return WINED3DERR_INVALIDCALL;
3727 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3728 TRACE("Z Blit override handled the blit\n");
3733 /* Special cases for RenderTargets */
3734 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3735 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3736 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3739 /* For the rest call the X11 surface implementation.
3740 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3741 * other Blts are rather rare
3743 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3746 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
3747 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans) {
3748 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3749 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3750 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3751 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3753 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3755 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3756 return WINEDDERR_SURFACEBUSY;
3759 if(myDevice->inScene &&
3760 (iface == myDevice->stencilBufferTarget ||
3761 (Source && Source == myDevice->stencilBufferTarget))) {
3762 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3763 return WINED3DERR_INVALIDCALL;
3766 /* Special cases for RenderTargets */
3767 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3768 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3770 RECT SrcRect, DstRect;
3774 SrcRect.left = rsrc->left;
3775 SrcRect.top= rsrc->top;
3776 SrcRect.bottom = rsrc->bottom;
3777 SrcRect.right = rsrc->right;
3781 SrcRect.right = srcImpl->currentDesc.Width;
3782 SrcRect.bottom = srcImpl->currentDesc.Height;
3785 DstRect.left = dstx;
3787 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3788 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3790 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3791 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3792 Flags |= WINEDDBLT_KEYSRC;
3793 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3794 Flags |= WINEDDBLT_KEYDEST;
3795 if(trans & WINEDDBLTFAST_WAIT)
3796 Flags |= WINEDDBLT_WAIT;
3797 if(trans & WINEDDBLTFAST_DONOTWAIT)
3798 Flags |= WINEDDBLT_DONOTWAIT;
3800 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3804 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3807 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3808 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3810 IWineD3DPaletteImpl *pal = This->palette;
3812 TRACE("(%p)\n", This);
3814 if (!pal) return WINED3D_OK;
3816 if(This->resource.format == WINED3DFMT_P8 ||
3817 This->resource.format == WINED3DFMT_A8P8)
3820 GLenum format, internal, type;
3821 CONVERT_TYPES convert;
3823 /* Check if we are using a RTL mode which uses texturing for uploads */
3824 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX);
3826 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
3827 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, This->srgb);
3829 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
3831 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
3832 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
3834 /* We want to force a palette refresh, so mark the drawable as not being up to date */
3835 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
3837 /* Re-upload the palette */
3838 d3dfmt_p8_upload_palette(iface, convert);
3840 if(!(This->Flags & SFLAG_INSYSMEM)) {
3841 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3842 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3844 TRACE("Dirtifying surface\n");
3845 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3849 if(This->Flags & SFLAG_DIBSECTION) {
3850 TRACE("(%p): Updating the hdc's palette\n", This);
3851 for (n=0; n<256; n++) {
3852 col[n].rgbRed = pal->palents[n].peRed;
3853 col[n].rgbGreen = pal->palents[n].peGreen;
3854 col[n].rgbBlue = pal->palents[n].peBlue;
3855 col[n].rgbReserved = 0;
3857 SetDIBColorTable(This->hDC, 0, 256, col);
3860 /* Propagate the changes to the drawable when we have a palette. */
3861 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3862 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3867 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3868 /** Check against the maximum texture sizes supported by the video card **/
3869 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3870 unsigned int pow2Width, pow2Height;
3871 const GlPixelFormatDesc *glDesc;
3873 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3874 /* Setup some glformat defaults */
3875 This->glDescription.glFormat = glDesc->glFormat;
3876 This->glDescription.glFormatInternal = glDesc->glInternal;
3877 This->glDescription.glType = glDesc->glType;
3879 This->glDescription.textureName = 0;
3880 This->glDescription.target = GL_TEXTURE_2D;
3882 /* Non-power2 support */
3883 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO) || GL_SUPPORT(WINE_NORMALIZED_TEXRECT)) {
3884 pow2Width = This->currentDesc.Width;
3885 pow2Height = This->currentDesc.Height;
3887 /* Find the nearest pow2 match */
3888 pow2Width = pow2Height = 1;
3889 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3890 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3892 This->pow2Width = pow2Width;
3893 This->pow2Height = pow2Height;
3895 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3896 WINED3DFORMAT Format = This->resource.format;
3897 /** TODO: add support for non power two compressed textures **/
3898 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3899 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5
3900 || This->resource.format == WINED3DFMT_ATI2N) {
3901 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3902 This, This->currentDesc.Width, This->currentDesc.Height);
3903 return WINED3DERR_NOTAVAILABLE;
3907 if(pow2Width != This->currentDesc.Width ||
3908 pow2Height != This->currentDesc.Height) {
3909 This->Flags |= SFLAG_NONPOW2;
3912 TRACE("%p\n", This);
3913 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3914 /* one of three options
3915 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)
3916 2: Set the texture to the maximum size (bad idea)
3917 3: WARN and return WINED3DERR_NOTAVAILABLE;
3918 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.
3920 WARN("(%p) Creating an oversized surface\n", This);
3921 This->Flags |= SFLAG_OVERSIZE;
3923 /* This will be initialized on the first blt */
3924 This->glRect.left = 0;
3925 This->glRect.top = 0;
3926 This->glRect.right = 0;
3927 This->glRect.bottom = 0;
3929 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3930 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3931 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3932 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3934 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE) &&
3935 !((This->resource.format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE) && (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3937 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3938 This->pow2Width = This->currentDesc.Width;
3939 This->pow2Height = This->currentDesc.Height;
3940 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
3943 /* No oversize, gl rect is the full texture size */
3944 This->Flags &= ~SFLAG_OVERSIZE;
3945 This->glRect.left = 0;
3946 This->glRect.top = 0;
3947 This->glRect.right = This->pow2Width;
3948 This->glRect.bottom = This->pow2Height;
3951 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3952 switch(wined3d_settings.offscreen_rendering_mode) {
3953 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3954 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3955 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3959 This->Flags |= SFLAG_INSYSMEM;
3964 struct depth_blt_info
3968 enum tex_types tex_type;
3969 GLfloat coords[4][3];
3972 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
3974 GLfloat (*coords)[3] = info->coords;
3979 FIXME("Unsupported texture target %#x\n", target);
3980 /* Fall back to GL_TEXTURE_2D */
3982 info->binding = GL_TEXTURE_BINDING_2D;
3983 info->bind_target = GL_TEXTURE_2D;
3984 info->tex_type = tex_2d;
3985 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
3986 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
3987 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
3988 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
3991 case GL_TEXTURE_RECTANGLE_ARB:
3992 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
3993 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
3994 info->tex_type = tex_rect;
3995 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
3996 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
3997 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
3998 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4001 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4002 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4003 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4004 info->tex_type = tex_cube;
4005 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4006 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4007 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4008 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4010 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4011 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4012 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4013 info->tex_type = tex_cube;
4014 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4015 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4016 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4017 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4019 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4020 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4021 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4022 info->tex_type = tex_cube;
4023 coords[0][0] = -1.0f; coords[0][1] = 1.0f; coords[0][2] = 1.0f;
4024 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 1.0f;
4025 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4026 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4028 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4029 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4030 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4031 info->tex_type = tex_cube;
4032 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4033 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4034 coords[2][0] = -1.0f; coords[2][1] = -1.0f; coords[2][2] = 1.0f;
4035 coords[3][0] = 1.0f; coords[3][1] = -1.0f; coords[3][2] = 1.0f;
4037 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4038 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4039 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4040 info->tex_type = tex_cube;
4041 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4042 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4043 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4044 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4046 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4047 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4048 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4049 info->tex_type = tex_cube;
4050 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4051 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4052 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4053 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4057 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4059 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4060 struct depth_blt_info info;
4061 GLint old_binding = 0;
4063 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4065 glDisable(GL_CULL_FACE);
4067 glDisable(GL_ALPHA_TEST);
4068 glDisable(GL_SCISSOR_TEST);
4069 glDisable(GL_STENCIL_TEST);
4070 glEnable(GL_DEPTH_TEST);
4071 glDepthFunc(GL_ALWAYS);
4072 glDepthMask(GL_TRUE);
4073 glBlendFunc(GL_ZERO, GL_ONE);
4074 glViewport(0, 0, w, h);
4076 surface_get_depth_blt_info(target, w, h, &info);
4077 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4078 glGetIntegerv(info.binding, &old_binding);
4079 glBindTexture(info.bind_target, texture);
4081 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4083 glBegin(GL_TRIANGLE_STRIP);
4084 glTexCoord3fv(info.coords[0]);
4085 glVertex2f(-1.0f, -1.0f);
4086 glTexCoord3fv(info.coords[1]);
4087 glVertex2f(1.0f, -1.0f);
4088 glTexCoord3fv(info.coords[2]);
4089 glVertex2f(-1.0f, 1.0f);
4090 glTexCoord3fv(info.coords[3]);
4091 glVertex2f(1.0f, 1.0f);
4094 glBindTexture(info.bind_target, old_binding);
4098 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4101 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4102 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4104 TRACE("(%p) New location %#x\n", This, location);
4106 if (location & ~SFLAG_DS_LOCATIONS) {
4107 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4110 This->Flags &= ~SFLAG_DS_LOCATIONS;
4111 This->Flags |= location;
4114 void surface_load_ds_location(IWineD3DSurface *iface, DWORD location) {
4115 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4116 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4118 TRACE("(%p) New location %#x\n", This, location);
4120 /* TODO: Make this work for modes other than FBO */
4121 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4123 if (This->Flags & location) {
4124 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4128 if (This->current_renderbuffer) {
4129 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4133 if (location == SFLAG_DS_OFFSCREEN) {
4134 if (This->Flags & SFLAG_DS_ONSCREEN) {
4135 GLint old_binding = 0;
4138 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4142 if (!device->depth_blt_texture) {
4143 glGenTextures(1, &device->depth_blt_texture);
4146 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4147 * directly on the FBO texture. That's because we need to flip. */
4148 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4149 if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
4150 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4151 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4153 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4154 bind_target = GL_TEXTURE_2D;
4156 glBindTexture(bind_target, device->depth_blt_texture);
4157 glCopyTexImage2D(bind_target,
4158 This->glDescription.level,
4159 This->glDescription.glFormatInternal,
4162 This->currentDesc.Width,
4163 This->currentDesc.Height,
4165 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4166 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4167 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4168 glBindTexture(bind_target, old_binding);
4170 /* Setup the destination */
4171 if (!device->depth_blt_rb) {
4172 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
4173 checkGLcall("glGenRenderbuffersEXT");
4175 if (device->depth_blt_rb_w != This->currentDesc.Width
4176 || device->depth_blt_rb_h != This->currentDesc.Height) {
4177 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4178 checkGLcall("glBindRenderbufferEXT");
4179 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
4180 checkGLcall("glRenderbufferStorageEXT");
4181 device->depth_blt_rb_w = This->currentDesc.Width;
4182 device->depth_blt_rb_h = This->currentDesc.Height;
4185 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->dst_fbo);
4186 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4187 checkGLcall("glFramebufferRenderbufferEXT");
4188 context_attach_depth_stencil_fbo(device, GL_FRAMEBUFFER_EXT, iface, FALSE);
4190 /* Do the actual blit */
4191 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4192 checkGLcall("depth_blt");
4194 if (device->activeContext->current_fbo) {
4195 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->current_fbo->id);
4197 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4198 checkGLcall("glBindFramebuffer()");
4203 FIXME("No up to date depth stencil location\n");
4205 } else if (location == SFLAG_DS_ONSCREEN) {
4206 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4207 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4211 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4212 checkGLcall("glBindFramebuffer()");
4213 surface_depth_blt(This, This->glDescription.textureName, This->currentDesc.Width, This->currentDesc.Height, This->glDescription.target);
4214 checkGLcall("depth_blt");
4216 if (device->activeContext->current_fbo) {
4217 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, device->activeContext->current_fbo->id));
4218 checkGLcall("glBindFramebuffer()");
4223 FIXME("No up to date depth stencil location\n");
4226 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4229 This->Flags |= location;
4232 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4233 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4234 IWineD3DBaseTexture *texture;
4235 IWineD3DSurfaceImpl *overlay;
4237 TRACE("(%p)->(%s, %s)\n", iface,
4238 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
4239 persistent ? "TRUE" : "FALSE");
4241 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4242 IWineD3DSwapChain *swapchain = NULL;
4244 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
4245 TRACE("Surface %p is an onscreen surface\n", iface);
4247 IWineD3DSwapChain_Release(swapchain);
4249 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4250 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4255 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
4256 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4257 TRACE("Passing to container\n");
4258 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4259 IWineD3DBaseTexture_Release(texture);
4262 This->Flags &= ~SFLAG_LOCATIONS;
4263 This->Flags |= flag;
4265 /* Redraw emulated overlays, if any */
4266 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4267 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4268 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4272 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
4273 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4274 TRACE("Passing to container\n");
4275 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4276 IWineD3DBaseTexture_Release(texture);
4279 This->Flags &= ~flag;
4295 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4297 f->l = ((r->left * 2.0f) / w) - 1.0f;
4298 f->t = ((r->top * 2.0f) / h) - 1.0f;
4299 f->r = ((r->right * 2.0f) / w) - 1.0f;
4300 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4303 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
4304 struct coords coords[4];
4306 IWineD3DSwapChain *swapchain;
4307 IWineD3DBaseTexture *texture;
4308 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4310 struct float_rect f;
4317 rect.right = This->currentDesc.Width;
4318 rect.bottom = This->currentDesc.Height;
4321 switch(This->glDescription.target)
4324 bind_target = GL_TEXTURE_2D;
4326 coords[0].x = (float)rect.left / This->pow2Width;
4327 coords[0].y = (float)rect.top / This->pow2Height;
4330 coords[1].x = (float)rect.left / This->pow2Width;
4331 coords[1].y = (float)rect.bottom / This->pow2Height;
4334 coords[2].x = (float)rect.right / This->pow2Width;
4335 coords[2].y = (float)rect.bottom / This->pow2Height;
4338 coords[3].x = (float)rect.right / This->pow2Width;
4339 coords[3].y = (float)rect.top / This->pow2Height;
4343 case GL_TEXTURE_RECTANGLE_ARB:
4344 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4345 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4346 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4347 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4348 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4351 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4352 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4353 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4354 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4355 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4356 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4357 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4360 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4361 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4362 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4363 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4364 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4365 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4366 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4369 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4370 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4371 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4372 coords[0].x = f.l; coords[0].y = 1; coords[0].z = f.t;
4373 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4374 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4375 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4378 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4379 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4380 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4381 coords[0].x = f.l; coords[0].y = -1; coords[0].z = -f.t;
4382 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4383 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4384 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4387 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4388 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4389 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4390 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4391 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4392 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4393 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4396 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4397 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4398 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4399 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4400 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4401 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4402 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4406 ERR("Unexpected texture target %#x\n", This->glDescription.target);
4410 ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4413 glEnable(bind_target);
4414 checkGLcall("glEnable(bind_target)");
4415 glBindTexture(bind_target, This->glDescription.textureName);
4416 checkGLcall("bind_target, This->glDescription.textureName)");
4417 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4418 checkGLcall("glTexParameteri");
4419 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4420 checkGLcall("glTexParameteri");
4422 if (device->render_offscreen)
4424 LONG tmp = rect.top;
4425 rect.top = rect.bottom;
4430 glTexCoord3fv(&coords[0].x);
4431 glVertex2i(rect.left, rect.top);
4433 glTexCoord3fv(&coords[1].x);
4434 glVertex2i(rect.left, rect.bottom);
4436 glTexCoord3fv(&coords[2].x);
4437 glVertex2i(rect.right, rect.bottom);
4439 glTexCoord3fv(&coords[3].x);
4440 glVertex2i(rect.right, rect.top);
4442 checkGLcall("glEnd");
4444 glDisable(bind_target);
4445 checkGLcall("glDisable(bind_target)");
4449 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4451 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4452 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4453 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4456 IWineD3DSwapChain_Release(swapchain);
4458 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4459 * reset properly next draw
4461 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4463 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4464 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4465 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4466 IWineD3DBaseTexture_Release(texture);
4471 /*****************************************************************************
4472 * IWineD3DSurface::LoadLocation
4474 * Copies the current surface data from wherever it is to the requested
4475 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4476 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4477 * multiple locations, the gl texture is preferred over the drawable, which is
4478 * preferred over system memory. The PBO counts as system memory. If rect is
4479 * not NULL, only the specified rectangle is copied (only supported for
4480 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4481 * location is marked up to date after the copy.
4484 * flag: Surface location flag to be updated
4485 * rect: rectangle to be copied
4488 * WINED3D_OK on success
4489 * WINED3DERR_DEVICELOST on an internal error
4491 *****************************************************************************/
4492 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4493 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4494 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4495 IWineD3DSwapChain *swapchain = NULL;
4496 GLenum format, internal, type;
4497 CONVERT_TYPES convert;
4499 int width, pitch, outpitch;
4502 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4503 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
4504 TRACE("Surface %p is an onscreen surface\n", iface);
4506 IWineD3DSwapChain_Release(swapchain);
4508 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4509 * Prefer SFLAG_INTEXTURE. */
4510 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4514 TRACE("(%p)->(%s, %p)\n", iface,
4515 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
4518 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4521 if(This->Flags & flag) {
4522 TRACE("Location already up to date\n");
4526 if(!(This->Flags & SFLAG_LOCATIONS)) {
4527 ERR("Surface does not have any up to date location\n");
4528 This->Flags |= SFLAG_LOST;
4529 return WINED3DERR_DEVICELOST;
4532 if(flag == SFLAG_INSYSMEM) {
4533 surface_prepare_system_memory(This);
4535 /* Download the surface to system memory */
4536 if(This->Flags & SFLAG_INTEXTURE) {
4537 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4538 surface_bind_and_dirtify(This);
4540 surface_download_data(This);
4542 read_from_framebuffer(This, rect,
4543 This->resource.allocatedMemory,
4544 IWineD3DSurface_GetPitch(iface));
4546 } else if(flag == SFLAG_INDRAWABLE) {
4547 if(This->Flags & SFLAG_INTEXTURE) {
4548 surface_blt_to_drawable(This, rect);
4550 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4552 /* The width is in 'length' not in bytes */
4553 width = This->currentDesc.Width;
4554 pitch = IWineD3DSurface_GetPitch(iface);
4556 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4557 * but it isn't set (yet) in all cases it is getting called. */
4558 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4559 TRACE("Removing the pbo attached to surface %p\n", This);
4560 surface_remove_pbo(This);
4563 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4564 int height = This->currentDesc.Height;
4566 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4567 outpitch = width * bpp;
4568 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4570 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4572 ERR("Out of memory %d, %d!\n", outpitch, height);
4573 return WINED3DERR_OUTOFVIDEOMEMORY;
4575 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4577 This->Flags |= SFLAG_CONVERTED;
4579 This->Flags &= ~SFLAG_CONVERTED;
4580 mem = This->resource.allocatedMemory;
4583 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4585 /* Don't delete PBO memory */
4586 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4587 HeapFree(GetProcessHeap(), 0, mem);
4589 } else /* if(flag == SFLAG_INTEXTURE) */ {
4590 if (This->Flags & SFLAG_INDRAWABLE) {
4591 read_from_framebuffer_texture(This);
4592 } else { /* Upload from system memory */
4593 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4595 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4596 surface_bind_and_dirtify(This);
4598 /* The only place where LoadTexture() might get called when isInDraw=1
4599 * is ActivateContext where lastActiveRenderTarget is preloaded.
4601 if(iface == device->lastActiveRenderTarget && device->isInDraw)
4602 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
4604 /* Otherwise: System memory copy must be most up to date */
4606 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4607 This->Flags |= SFLAG_GLCKEY;
4608 This->glCKey = This->SrcBltCKey;
4610 else This->Flags &= ~SFLAG_GLCKEY;
4612 /* The width is in 'length' not in bytes */
4613 width = This->currentDesc.Width;
4614 pitch = IWineD3DSurface_GetPitch(iface);
4616 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4617 * but it isn't set (yet) in all cases it is getting called. */
4618 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4619 TRACE("Removing the pbo attached to surface %p\n", This);
4620 surface_remove_pbo(This);
4623 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4624 int height = This->currentDesc.Height;
4626 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4627 outpitch = width * bpp;
4628 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4630 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4632 ERR("Out of memory %d, %d!\n", outpitch, height);
4633 return WINED3DERR_OUTOFVIDEOMEMORY;
4635 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4637 This->Flags |= SFLAG_CONVERTED;
4638 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
4639 d3dfmt_p8_upload_palette(iface, convert);
4640 This->Flags &= ~SFLAG_CONVERTED;
4641 mem = This->resource.allocatedMemory;
4643 This->Flags &= ~SFLAG_CONVERTED;
4644 mem = This->resource.allocatedMemory;
4647 /* Make sure the correct pitch is used */
4649 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4652 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4653 TRACE("non power of two support\n");
4654 if(!(This->Flags & SFLAG_ALLOCATED)) {
4655 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4657 if (mem || (This->Flags & SFLAG_PBO)) {
4658 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4661 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4662 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4664 if(!(This->Flags & SFLAG_ALLOCATED)) {
4665 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4667 if (mem || (This->Flags & SFLAG_PBO)) {
4668 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4672 /* Restore the default pitch */
4674 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4677 /* Don't delete PBO memory */
4678 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4679 HeapFree(GetProcessHeap(), 0, mem);
4684 This->Flags |= flag;
4687 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
4688 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4689 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4690 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4696 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
4697 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4698 IWineD3DSwapChain *swapchain = NULL;
4700 /* Update the drawable size method */
4702 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4705 This->get_drawable_size = get_drawable_size_swapchain;
4706 IWineD3DSwapChain_Release(swapchain);
4707 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4708 switch(wined3d_settings.offscreen_rendering_mode) {
4709 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4710 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4711 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4715 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4718 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4719 return SURFACE_OPENGL;
4722 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4723 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4726 /* If there's no destination surface there is nothing to do */
4727 if(!This->overlay_dest) return WINED3D_OK;
4729 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4730 * update the overlay. Prevent an endless recursion
4732 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
4735 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
4736 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4737 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4738 NULL, WINED3DTEXF_LINEAR);
4739 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
4744 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4747 IWineD3DBaseSurfaceImpl_QueryInterface,
4748 IWineD3DBaseSurfaceImpl_AddRef,
4749 IWineD3DSurfaceImpl_Release,
4750 /* IWineD3DResource */
4751 IWineD3DBaseSurfaceImpl_GetParent,
4752 IWineD3DBaseSurfaceImpl_GetDevice,
4753 IWineD3DBaseSurfaceImpl_SetPrivateData,
4754 IWineD3DBaseSurfaceImpl_GetPrivateData,
4755 IWineD3DBaseSurfaceImpl_FreePrivateData,
4756 IWineD3DBaseSurfaceImpl_SetPriority,
4757 IWineD3DBaseSurfaceImpl_GetPriority,
4758 IWineD3DSurfaceImpl_PreLoad,
4759 IWineD3DSurfaceImpl_UnLoad,
4760 IWineD3DBaseSurfaceImpl_GetType,
4761 /* IWineD3DSurface */
4762 IWineD3DBaseSurfaceImpl_GetContainer,
4763 IWineD3DBaseSurfaceImpl_GetDesc,
4764 IWineD3DSurfaceImpl_LockRect,
4765 IWineD3DSurfaceImpl_UnlockRect,
4766 IWineD3DSurfaceImpl_GetDC,
4767 IWineD3DSurfaceImpl_ReleaseDC,
4768 IWineD3DSurfaceImpl_Flip,
4769 IWineD3DSurfaceImpl_Blt,
4770 IWineD3DBaseSurfaceImpl_GetBltStatus,
4771 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4772 IWineD3DBaseSurfaceImpl_IsLost,
4773 IWineD3DBaseSurfaceImpl_Restore,
4774 IWineD3DSurfaceImpl_BltFast,
4775 IWineD3DBaseSurfaceImpl_GetPalette,
4776 IWineD3DBaseSurfaceImpl_SetPalette,
4777 IWineD3DSurfaceImpl_RealizePalette,
4778 IWineD3DBaseSurfaceImpl_SetColorKey,
4779 IWineD3DBaseSurfaceImpl_GetPitch,
4780 IWineD3DSurfaceImpl_SetMem,
4781 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4782 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4783 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4784 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4785 IWineD3DBaseSurfaceImpl_SetClipper,
4786 IWineD3DBaseSurfaceImpl_GetClipper,
4788 IWineD3DSurfaceImpl_AddDirtyRect,
4789 IWineD3DSurfaceImpl_LoadTexture,
4790 IWineD3DSurfaceImpl_BindTexture,
4791 IWineD3DSurfaceImpl_SaveSnapshot,
4792 IWineD3DSurfaceImpl_SetContainer,
4793 IWineD3DSurfaceImpl_GetGlDesc,
4794 IWineD3DBaseSurfaceImpl_GetData,
4795 IWineD3DSurfaceImpl_SetFormat,
4796 IWineD3DSurfaceImpl_PrivateSetup,
4797 IWineD3DSurfaceImpl_ModifyLocation,
4798 IWineD3DSurfaceImpl_LoadLocation,
4799 IWineD3DSurfaceImpl_GetImplType,
4800 IWineD3DSurfaceImpl_DrawOverlay
4802 #undef GLINFO_LOCATION
4804 #define GLINFO_LOCATION device->adapter->gl_info
4805 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
4806 static void ffp_blit_free(IWineD3DDevice *iface) { }
4808 static HRESULT ffp_blit_set(IWineD3DDevice *iface, WINED3DFORMAT fmt, GLenum textype, UINT width, UINT height) {
4810 checkGLcall("glEnable(textype)");
4814 static void ffp_blit_unset(IWineD3DDevice *iface) {
4815 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4816 glDisable(GL_TEXTURE_2D);
4817 checkGLcall("glDisable(GL_TEXTURE_2D)");
4818 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP)) {
4819 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4820 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4822 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
4823 glDisable(GL_TEXTURE_RECTANGLE_ARB);
4824 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4828 static BOOL ffp_blit_conv_supported(WINED3DFORMAT fmt) {
4829 TRACE("Checking blit format support for format %s: [FAILED]\n", debug_d3dformat(fmt));
4833 const struct blit_shader ffp_blit = {
4838 ffp_blit_conv_supported