wined3d: Implement CONVERT_CK_RGB24 in d3dfmt_convert_surface.
[wine/hacks.git] / dlls / wined3d / surface.c
blob77f32ef47495754362aa91f5e80c51786484a50b
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
11 * Copyright 2007-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
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
36 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
37 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey);
38 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert);
39 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This);
40 static void surface_remove_pbo(IWineD3DSurfaceImpl *This);
42 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) {
90 int active_sampler;
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;
105 ENTER_GL();
106 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
107 LEAVE_GL();
108 active_sampler = This->resource.wineD3DDevice->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
109 } else {
110 active_sampler = 0;
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))
125 return TRUE;
127 return FALSE;
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");
135 return;
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));
141 return;
144 ENTER_GL();
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);
152 } else {
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");
163 } else {
164 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
165 checkGLcall("glGetCompressedTexImageARB()");
168 LEAVE_GL();
169 } else {
170 void *mem;
171 GLenum format = This->glDescription.glFormat;
172 GLenum type = This->glDescription.glType;
173 int src_pitch = 0;
174 int dst_pitch = 0;
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)) {
178 format = GL_ALPHA;
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);
188 } else {
189 mem = This->resource.allocatedMemory;
192 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
193 format, type, mem);
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,
200 type, NULL);
201 checkGLcall("glGetTexImage()");
203 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
204 checkGLcall("glBindBufferARB");
205 } else {
206 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
207 type, mem);
208 checkGLcall("glGetTexImage()");
210 LEAVE_GL();
212 if (This->Flags & SFLAG_NONPOW2) {
213 LPBYTE src_data, dst_data;
214 int y;
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
230 * | | | \/
231 * -----------------------------------
234 * we're repacking the data to the expected texture width
236 * |<-texture width ->| -->pow2width| /\
237 * |111111111111111111222222222222222| |
238 * |222333333333333333333444444444444| texture height
239 * |444444 | |
240 * | | \/
241 * | | |
242 * | empty | pow2height
243 * | | \/
244 * -----------------------------------
246 * == is the same as
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.
265 src_data = mem;
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");
295 } else {
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);
301 ENTER_GL();
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");
314 } else {
315 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
316 width, height, 0 /* border */, This->resource.size, data));
317 checkGLcall("glCompressedTexSubImage2D");
319 LEAVE_GL();
321 } else {
322 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
323 ENTER_GL();
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");
336 else {
337 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
338 checkGLcall("glTexSubImage2D");
341 LEAVE_GL();
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;
349 BYTE *mem = NULL;
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));
370 ENTER_GL();
371 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
372 width, height, 0 /* border */, This->resource.size, mem));
373 LEAVE_GL();
376 return;
379 ENTER_GL();
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;
394 } else {
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)");
410 LEAVE_GL();
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;
433 return;
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;
441 break;
445 if (!renderbuffer) {
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");
473 return GL_BACK;
474 } else if (swapchain_impl->frontBuffer == iface) {
475 TRACE("Returning GL_FRONT\n");
476 return GL_FRONT;
479 FIXME("Higher back buffer, returning GL_BACK\n");
480 return GL_BACK;
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);
487 if (ref == 0) {
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);
500 ENTER_GL();
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) {
507 /* Delete the 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);
515 LEAVE_GL();
517 if(This->Flags & SFLAG_DIBSECTION) {
518 /* Release the DC */
519 SelectObject(This->hDC, This->dib.holdbitmap);
520 DeleteDC(This->hDC);
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);
540 return ref;
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);
558 } else {
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) */
579 GLclampf tmp;
580 tmp = 0.9f;
581 ENTER_GL();
582 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
583 LEAVE_GL();
586 return;
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));
594 ENTER_GL();
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");
601 LEAVE_GL();
603 This->pbo = 0;
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 /
624 * sysmem copy here.
626 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
627 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
628 } else {
629 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
631 } else {
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) {
649 ENTER_GL();
650 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
651 LEAVE_GL();
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,
659 * destroy it
661 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
662 if(!texture) {
663 ENTER_GL();
664 glDeleteTextures(1, &This->glDescription.textureName);
665 This->glDescription.textureName = 0;
666 LEAVE_GL();
667 } else {
668 IWineD3DBaseTexture_Release(texture);
670 return;
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 /* TODO: think about moving this down to resource? */
684 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
685 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
686 /* This should only be called for sysmem textures, it may be a good idea to extend this to all pools at some point in the future */
687 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
688 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
690 return (CONST void*)(This->resource.allocatedMemory);
693 /* Read the framebuffer back into the surface */
694 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
695 IWineD3DSwapChainImpl *swapchain;
696 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
697 BYTE *mem;
698 GLint fmt;
699 GLint type;
700 BYTE *row, *top, *bottom;
701 int i;
702 BOOL bpp;
703 RECT local_rect;
704 BOOL srcIsUpsideDown;
706 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
707 static BOOL warned = FALSE;
708 if(!warned) {
709 ERR("The application tries to lock the render target, but render target locking is disabled\n");
710 warned = TRUE;
712 return;
715 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
716 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
717 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
718 * context->last_was_blit set on the unlock.
720 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
721 ENTER_GL();
723 /* Select the correct read buffer, and give some debug output.
724 * There is no need to keep track of the current read buffer or reset it, every part of the code
725 * that reads sets the read buffer as desired.
727 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
729 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
730 TRACE("Locking %#x buffer\n", buffer);
731 glReadBuffer(buffer);
732 checkGLcall("glReadBuffer");
734 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
735 srcIsUpsideDown = FALSE;
736 } else {
737 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
738 * Read from the back buffer
740 TRACE("Locking offscreen render target\n");
741 glReadBuffer(myDevice->offscreenBuffer);
742 srcIsUpsideDown = TRUE;
745 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
746 if(!rect) {
747 local_rect.left = 0;
748 local_rect.top = 0;
749 local_rect.right = This->currentDesc.Width;
750 local_rect.bottom = This->currentDesc.Height;
751 } else {
752 local_rect = *rect;
754 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
756 switch(This->resource.format)
758 case WINED3DFMT_P8:
760 if(primary_render_target_is_p8(myDevice)) {
761 /* In case of P8 render targets the index is stored in the alpha component */
762 fmt = GL_ALPHA;
763 type = GL_UNSIGNED_BYTE;
764 mem = dest;
765 bpp = This->bytesPerPixel;
766 } else {
767 /* GL can't return palettized data, so read ARGB pixels into a
768 * separate block of memory and convert them into palettized format
769 * in software. Slow, but if the app means to use palettized render
770 * targets and locks it...
772 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
773 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
774 * for the color channels when palettizing the colors.
776 fmt = GL_RGB;
777 type = GL_UNSIGNED_BYTE;
778 pitch *= 3;
779 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
780 if(!mem) {
781 ERR("Out of memory\n");
782 LEAVE_GL();
783 return;
785 bpp = This->bytesPerPixel * 3;
788 break;
790 default:
791 mem = dest;
792 fmt = This->glDescription.glFormat;
793 type = This->glDescription.glType;
794 bpp = This->bytesPerPixel;
797 if(This->Flags & SFLAG_PBO) {
798 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
799 checkGLcall("glBindBufferARB");
802 glReadPixels(local_rect.left, local_rect.top,
803 local_rect.right - local_rect.left,
804 local_rect.bottom - local_rect.top,
805 fmt, type, mem);
806 vcheckGLcall("glReadPixels");
808 if(This->Flags & SFLAG_PBO) {
809 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
810 checkGLcall("glBindBufferARB");
812 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
813 * to get a pointer to it and perform the flipping in software. This is a lot
814 * faster than calling glReadPixels for each line. In case we want more speed
815 * we should rerender it flipped in a FBO and read the data back from the FBO. */
816 if(!srcIsUpsideDown) {
817 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
818 checkGLcall("glBindBufferARB");
820 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
821 checkGLcall("glMapBufferARB");
825 /* TODO: Merge this with the palettization loop below for P8 targets */
826 if(!srcIsUpsideDown) {
827 UINT len, off;
828 /* glReadPixels returns the image upside down, and there is no way to prevent this.
829 Flip the lines in software */
830 len = (local_rect.right - local_rect.left) * bpp;
831 off = local_rect.left * bpp;
833 row = HeapAlloc(GetProcessHeap(), 0, len);
834 if(!row) {
835 ERR("Out of memory\n");
836 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
837 LEAVE_GL();
838 return;
841 top = mem + pitch * local_rect.top;
842 bottom = mem + pitch * ( local_rect.bottom - local_rect.top - 1);
843 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
844 memcpy(row, top + off, len);
845 memcpy(top + off, bottom + off, len);
846 memcpy(bottom + off, row, len);
847 top += pitch;
848 bottom -= pitch;
850 HeapFree(GetProcessHeap(), 0, row);
852 /* Unmap the temp PBO buffer */
853 if(This->Flags & SFLAG_PBO) {
854 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
855 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
859 LEAVE_GL();
861 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
862 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
863 * the same color but we have no choice.
864 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
866 if((This->resource.format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice)) {
867 PALETTEENTRY *pal = NULL;
868 DWORD width = pitch / 3;
869 int x, y, c;
871 if(This->palette) {
872 pal = This->palette->palents;
873 } else {
874 ERR("Palette is missing, cannot perform inverse palette lookup\n");
875 HeapFree(GetProcessHeap(), 0, mem);
876 return ;
879 for(y = local_rect.top; y < local_rect.bottom; y++) {
880 for(x = local_rect.left; x < local_rect.right; x++) {
881 /* start lines pixels */
882 BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
883 BYTE *green = blue + 1;
884 BYTE *red = green + 1;
886 for(c = 0; c < 256; c++) {
887 if(*red == pal[c].peRed &&
888 *green == pal[c].peGreen &&
889 *blue == pal[c].peBlue)
891 *((BYTE *) dest + y * width + x) = c;
892 break;
897 HeapFree(GetProcessHeap(), 0, mem);
901 /* Read the framebuffer contents into a texture */
902 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This)
904 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
905 IWineD3DSwapChainImpl *swapchain;
906 int bpp;
907 GLenum format, internal, type;
908 CONVERT_TYPES convert;
909 GLint prevRead;
911 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
913 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
914 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
915 * states in the stateblock, and no driver was found yet that had bugs in that regard.
917 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
918 surface_bind_and_dirtify(This);
920 ENTER_GL();
921 glGetIntegerv(GL_READ_BUFFER, &prevRead);
922 LEAVE_GL();
924 /* Select the correct read buffer, and give some debug output.
925 * There is no need to keep track of the current read buffer or reset it, every part of the code
926 * that reads sets the read buffer as desired.
928 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
930 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
931 TRACE("Locking %#x buffer\n", buffer);
933 ENTER_GL();
934 glReadBuffer(buffer);
935 checkGLcall("glReadBuffer");
936 LEAVE_GL();
938 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
939 } else {
940 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
941 * Read from the back buffer
943 TRACE("Locking offscreen render target\n");
944 ENTER_GL();
945 glReadBuffer(device->offscreenBuffer);
946 LEAVE_GL();
949 if(!(This->Flags & SFLAG_ALLOCATED)) {
950 surface_allocate_surface(This, internal, This->pow2Width,
951 This->pow2Height, format, type);
954 clear_unused_channels(This);
956 ENTER_GL();
957 /* If !SrcIsUpsideDown we should flip the surface.
958 * This can be done using glCopyTexSubImage2D but this
959 * is VERY slow, so don't do that. We should prevent
960 * this code from getting called in such cases or perhaps
961 * we can use FBOs */
963 glCopyTexSubImage2D(This->glDescription.target,
964 This->glDescription.level,
965 0, 0, 0, 0,
966 This->currentDesc.Width,
967 This->currentDesc.Height);
968 checkGLcall("glCopyTexSubImage2D");
970 glReadBuffer(prevRead);
971 vcheckGLcall("glReadBuffer");
973 LEAVE_GL();
974 TRACE("Updated target %d\n", This->glDescription.target);
977 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
978 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
979 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
980 * changed
982 if(!(This->Flags & SFLAG_DYNLOCK)) {
983 This->lockCount++;
984 /* MAXLOCKCOUNT is defined in wined3d_private.h */
985 if(This->lockCount > MAXLOCKCOUNT) {
986 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
987 This->Flags |= SFLAG_DYNLOCK;
991 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
992 * Also don't create a PBO for systemmem surfaces.
994 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
995 GLenum error;
996 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
998 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
999 ENTER_GL();
1001 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1002 error = glGetError();
1003 if(This->pbo == 0 || error != GL_NO_ERROR) {
1004 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1007 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1009 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1010 checkGLcall("glBindBufferARB");
1012 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1013 checkGLcall("glBufferDataARB");
1015 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1016 checkGLcall("glBindBufferARB");
1018 /* We don't need the system memory anymore and we can't even use it for PBOs */
1019 if(!(This->Flags & SFLAG_CLIENT)) {
1020 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1021 This->resource.heapMemory = NULL;
1023 This->resource.allocatedMemory = NULL;
1024 This->Flags |= SFLAG_PBO;
1025 LEAVE_GL();
1026 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
1027 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1028 * or a pbo to map
1030 if(!This->resource.heapMemory) {
1031 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1033 This->resource.allocatedMemory =
1034 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1035 if(This->Flags & SFLAG_INSYSMEM) {
1036 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1041 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1042 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1043 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1044 IWineD3DSwapChain *swapchain = NULL;
1046 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1048 /* This is also done in the base class, but we have to verify this before loading any data from
1049 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1050 * may interfere, and all other bad things may happen
1052 if (This->Flags & SFLAG_LOCKED) {
1053 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1054 return WINED3DERR_INVALIDCALL;
1056 This->Flags |= SFLAG_LOCKED;
1058 if (!(This->Flags & SFLAG_LOCKABLE))
1060 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1063 if (Flags & WINED3DLOCK_DISCARD) {
1064 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1065 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1066 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1067 This->Flags |= SFLAG_INSYSMEM;
1068 goto lock_end;
1071 if (This->Flags & SFLAG_INSYSMEM) {
1072 TRACE("Local copy is up to date, not downloading data\n");
1073 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1074 goto lock_end;
1077 /* Now download the surface content from opengl
1078 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1079 * Offscreen targets which are not active at the moment or are higher targets(FBOs) can be locked with the texture path
1081 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1082 if(swapchain || iface == myDevice->render_targets[0]) {
1083 const RECT *pass_rect = pRect;
1085 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1086 * because most caller functions do not need that. So do that here
1088 if(pRect &&
1089 pRect->top == 0 &&
1090 pRect->left == 0 &&
1091 pRect->right == This->currentDesc.Width &&
1092 pRect->bottom == This->currentDesc.Height) {
1093 pass_rect = NULL;
1096 switch(wined3d_settings.rendertargetlock_mode) {
1097 case RTL_TEXDRAW:
1098 case RTL_TEXTEX:
1099 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1100 #if 0
1101 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1102 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1103 * This may be faster on some cards
1105 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1106 #endif
1107 /* drop through */
1109 case RTL_AUTO:
1110 case RTL_READDRAW:
1111 case RTL_READTEX:
1112 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1113 break;
1115 case RTL_DISABLE:
1116 break;
1118 if(swapchain) IWineD3DSwapChain_Release(swapchain);
1120 } else if(iface == myDevice->stencilBufferTarget) {
1121 /** the depth stencil in openGL has a format of GL_FLOAT
1122 * which should be good for WINED3DFMT_D16_LOCKABLE
1123 * and WINED3DFMT_D16
1124 * it is unclear what format the stencil buffer is in except.
1125 * 'Each index is converted to fixed point...
1126 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1127 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1128 * glReadPixels(This->lockedRect.left,
1129 * This->lockedRect.bottom - j - 1,
1130 * This->lockedRect.right - This->lockedRect.left,
1131 * 1,
1132 * GL_DEPTH_COMPONENT,
1133 * type,
1134 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1136 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1137 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1138 * none of that is the case the problem is not in this function :-)
1139 ********************************************/
1140 FIXME("Depth stencil locking not supported yet\n");
1141 } else {
1142 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1143 TRACE("locking an ordinary surface\n");
1144 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1147 lock_end:
1148 if(This->Flags & SFLAG_PBO) {
1149 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1150 ENTER_GL();
1151 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1152 checkGLcall("glBindBufferARB");
1154 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1155 if(This->resource.allocatedMemory) {
1156 ERR("The surface already has PBO memory allocated!\n");
1159 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1160 checkGLcall("glMapBufferARB");
1162 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1163 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1164 checkGLcall("glBindBufferARB");
1166 LEAVE_GL();
1169 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1170 /* Don't dirtify */
1171 } else {
1172 IWineD3DBaseTexture *pBaseTexture;
1174 * Dirtify on lock
1175 * as seen in msdn docs
1177 IWineD3DSurface_AddDirtyRect(iface, pRect);
1179 /** Dirtify Container if needed */
1180 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1181 TRACE("Making container dirty\n");
1182 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1183 IWineD3DBaseTexture_Release(pBaseTexture);
1184 } else {
1185 TRACE("Surface is standalone, no need to dirty the container\n");
1189 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1192 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1193 GLint prev_store;
1194 GLint prev_rasterpos[4];
1195 GLint skipBytes = 0;
1196 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1197 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1198 IWineD3DSwapChainImpl *swapchain;
1200 /* Activate the correct context for the render target */
1201 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1202 ENTER_GL();
1204 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
1205 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1206 TRACE("Unlocking %#x buffer\n", buffer);
1207 glDrawBuffer(buffer);
1208 checkGLcall("glDrawBuffer");
1210 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1211 } else {
1212 /* Primary offscreen render target */
1213 TRACE("Offscreen render target\n");
1214 glDrawBuffer(myDevice->offscreenBuffer);
1215 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1218 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1219 vcheckGLcall("glIntegerv");
1220 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1221 vcheckGLcall("glIntegerv");
1222 glPixelZoom(1.0, -1.0);
1223 vcheckGLcall("glPixelZoom");
1225 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1226 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1227 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1229 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1230 vcheckGLcall("glRasterPos2f");
1232 /* Some drivers(radeon dri, others?) don't like exceptions during
1233 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1234 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1235 * catch to put the dib section in InSync mode, which leads to a crash
1236 * and a blocked x server on my radeon card.
1238 * The following lines read the dib section so it is put in InSync mode
1239 * before glDrawPixels is called and the crash is prevented. There won't
1240 * be any interfering gdi accesses, because UnlockRect is called from
1241 * ReleaseDC, and the app won't use the dc any more afterwards.
1243 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1244 volatile BYTE read;
1245 read = This->resource.allocatedMemory[0];
1248 if(This->Flags & SFLAG_PBO) {
1249 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1250 checkGLcall("glBindBufferARB");
1253 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1254 if(This->Flags & SFLAG_LOCKED) {
1255 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1256 (This->lockedRect.bottom - This->lockedRect.top)-1,
1257 fmt, type,
1258 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1259 checkGLcall("glDrawPixels");
1260 } else {
1261 glDrawPixels(This->currentDesc.Width,
1262 This->currentDesc.Height,
1263 fmt, type, mem);
1264 checkGLcall("glDrawPixels");
1267 if(This->Flags & SFLAG_PBO) {
1268 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1269 checkGLcall("glBindBufferARB");
1272 glPixelZoom(1.0,1.0);
1273 vcheckGLcall("glPixelZoom");
1275 glRasterPos3iv(&prev_rasterpos[0]);
1276 vcheckGLcall("glRasterPos3iv");
1278 /* Reset to previous pack row length */
1279 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1280 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1282 if(!swapchain) {
1283 glDrawBuffer(myDevice->offscreenBuffer);
1284 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1285 } else if(swapchain->backBuffer) {
1286 glDrawBuffer(GL_BACK);
1287 checkGLcall("glDrawBuffer(GL_BACK)");
1288 } else {
1289 glDrawBuffer(GL_FRONT);
1290 checkGLcall("glDrawBuffer(GL_FRONT)");
1292 LEAVE_GL();
1294 return;
1297 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1298 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1299 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1300 IWineD3DSwapChainImpl *swapchain = NULL;
1301 BOOL fullsurface;
1303 if (!(This->Flags & SFLAG_LOCKED)) {
1304 WARN("trying to Unlock an unlocked surf@%p\n", This);
1305 return WINED3DERR_INVALIDCALL;
1308 if (This->Flags & SFLAG_PBO) {
1309 TRACE("Freeing PBO memory\n");
1310 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1311 ENTER_GL();
1312 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1313 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1314 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1315 checkGLcall("glUnmapBufferARB");
1316 LEAVE_GL();
1317 This->resource.allocatedMemory = NULL;
1320 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1322 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1323 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1324 goto unlock_end;
1327 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1328 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1329 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1331 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1332 static BOOL warned = FALSE;
1333 if(!warned) {
1334 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1335 warned = TRUE;
1337 goto unlock_end;
1340 if(This->dirtyRect.left == 0 &&
1341 This->dirtyRect.top == 0 &&
1342 This->dirtyRect.right == This->currentDesc.Width &&
1343 This->dirtyRect.bottom == This->currentDesc.Height) {
1344 fullsurface = TRUE;
1345 } else {
1346 /* TODO: Proper partial rectangle tracking */
1347 fullsurface = FALSE;
1348 This->Flags |= SFLAG_INSYSMEM;
1351 switch(wined3d_settings.rendertargetlock_mode) {
1352 case RTL_READTEX:
1353 case RTL_TEXTEX:
1354 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1355 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1356 /* drop through */
1358 case RTL_AUTO:
1359 case RTL_READDRAW:
1360 case RTL_TEXDRAW:
1361 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1362 break;
1365 if(!fullsurface) {
1366 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1367 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1368 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1369 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1370 * not fully up to date because only a subrectangle was read in LockRect.
1372 This->Flags &= ~SFLAG_INSYSMEM;
1373 This->Flags |= SFLAG_INDRAWABLE;
1376 This->dirtyRect.left = This->currentDesc.Width;
1377 This->dirtyRect.top = This->currentDesc.Height;
1378 This->dirtyRect.right = 0;
1379 This->dirtyRect.bottom = 0;
1380 } else if(iface == myDevice->stencilBufferTarget) {
1381 FIXME("Depth Stencil buffer locking is not implemented\n");
1382 } else {
1383 /* The rest should be a normal texture */
1384 IWineD3DBaseTextureImpl *impl;
1385 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1386 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1387 * states need resetting
1389 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1390 if(impl->baseTexture.bindCount) {
1391 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1393 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1397 unlock_end:
1398 This->Flags &= ~SFLAG_LOCKED;
1399 memset(&This->lockedRect, 0, sizeof(RECT));
1401 /* Overlays have to be redrawn manually after changes with the GL implementation */
1402 if(This->overlay_dest) {
1403 IWineD3DSurface_DrawOverlay(iface);
1405 return WINED3D_OK;
1408 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1409 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1410 WINED3DLOCKED_RECT lock;
1411 HRESULT hr;
1412 RGBQUAD col[256];
1414 TRACE("(%p)->(%p)\n",This,pHDC);
1416 if(This->Flags & SFLAG_USERPTR) {
1417 ERR("Not supported on surfaces with an application-provided surfaces\n");
1418 return WINEDDERR_NODC;
1421 /* Give more detailed info for ddraw */
1422 if (This->Flags & SFLAG_DCINUSE)
1423 return WINEDDERR_DCALREADYCREATED;
1425 /* Can't GetDC if the surface is locked */
1426 if (This->Flags & SFLAG_LOCKED)
1427 return WINED3DERR_INVALIDCALL;
1429 /* According to Direct3D9 docs, only these formats are supported */
1430 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1431 if (This->resource.format != WINED3DFMT_R5G6B5 &&
1432 This->resource.format != WINED3DFMT_X1R5G5B5 &&
1433 This->resource.format != WINED3DFMT_R8G8B8 &&
1434 This->resource.format != WINED3DFMT_X8R8G8B8) return WINED3DERR_INVALIDCALL;
1437 memset(&lock, 0, sizeof(lock)); /* To be sure */
1439 /* Create a DIB section if there isn't a hdc yet */
1440 if(!This->hDC) {
1441 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1442 if(This->Flags & SFLAG_CLIENT) {
1443 IWineD3DSurface_PreLoad(iface);
1446 /* Use the dib section from now on if we are not using a PBO */
1447 if(!(This->Flags & SFLAG_PBO))
1448 This->resource.allocatedMemory = This->dib.bitmap_data;
1451 /* Lock the surface */
1452 hr = IWineD3DSurface_LockRect(iface,
1453 &lock,
1454 NULL,
1457 if(This->Flags & SFLAG_PBO) {
1458 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1459 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1462 if(FAILED(hr)) {
1463 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1464 /* keep the dib section */
1465 return hr;
1468 if(This->resource.format == WINED3DFMT_P8 ||
1469 This->resource.format == WINED3DFMT_A8P8) {
1470 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1471 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1472 unsigned int n;
1473 PALETTEENTRY *pal = NULL;
1475 if(This->palette) {
1476 pal = This->palette->palents;
1477 } else {
1478 IWineD3DSurfaceImpl *dds_primary;
1479 IWineD3DSwapChainImpl *swapchain;
1480 swapchain = (IWineD3DSwapChainImpl *)This->resource.wineD3DDevice->swapchains[0];
1481 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1482 if (dds_primary && dds_primary->palette)
1483 pal = dds_primary->palette->palents;
1486 if (pal) {
1487 for (n=0; n<256; n++) {
1488 col[n].rgbRed = pal[n].peRed;
1489 col[n].rgbGreen = pal[n].peGreen;
1490 col[n].rgbBlue = pal[n].peBlue;
1491 col[n].rgbReserved = 0;
1493 SetDIBColorTable(This->hDC, 0, 256, col);
1497 *pHDC = This->hDC;
1498 TRACE("returning %p\n",*pHDC);
1499 This->Flags |= SFLAG_DCINUSE;
1501 return WINED3D_OK;
1504 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1505 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1507 TRACE("(%p)->(%p)\n",This,hDC);
1509 if (!(This->Flags & SFLAG_DCINUSE))
1510 return WINED3DERR_INVALIDCALL;
1512 if (This->hDC !=hDC) {
1513 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1514 return WINED3DERR_INVALIDCALL;
1517 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1518 /* Copy the contents of the DIB over to the PBO */
1519 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1522 /* we locked first, so unlock now */
1523 IWineD3DSurface_UnlockRect(iface);
1525 This->Flags &= ~SFLAG_DCINUSE;
1527 return WINED3D_OK;
1530 /* ******************************************************
1531 IWineD3DSurface Internal (No mapping to directx api) parts follow
1532 ****************************************************** */
1534 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) {
1535 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1536 const GlPixelFormatDesc *glDesc;
1537 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1538 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1540 /* Default values: From the surface */
1541 *format = glDesc->glFormat;
1542 *type = glDesc->glType;
1543 *convert = NO_CONVERSION;
1544 *target_bpp = This->bytesPerPixel;
1546 if(srgb_mode) {
1547 *internal = glDesc->glGammaInternal;
1548 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1549 *internal = glDesc->rtInternal;
1550 } else {
1551 *internal = glDesc->glInternal;
1554 /* Ok, now look if we have to do any conversion */
1555 switch(This->resource.format) {
1556 case WINED3DFMT_P8:
1557 /* ****************
1558 Paletted Texture
1559 **************** */
1561 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1562 * of the two is available make sure texturing is requested as neither of the two works in
1563 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1564 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1565 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1566 * conflicts with this.
1568 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) ||
1569 (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) &&
1570 device->render_targets &&
1571 This == (IWineD3DSurfaceImpl*)device->render_targets[0])) ||
1572 colorkey_active || !use_texturing ) {
1573 *format = GL_RGBA;
1574 *internal = GL_RGBA;
1575 *type = GL_UNSIGNED_BYTE;
1576 *target_bpp = 4;
1577 if(colorkey_active) {
1578 *convert = CONVERT_PALETTED_CK;
1579 } else {
1580 *convert = CONVERT_PALETTED;
1583 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1584 *format = GL_ALPHA;
1585 *internal = GL_RGBA;
1586 *type = GL_UNSIGNED_BYTE;
1587 *target_bpp = 1;
1590 break;
1592 case WINED3DFMT_R3G3B2:
1593 /* **********************
1594 GL_UNSIGNED_BYTE_3_3_2
1595 ********************** */
1596 if (colorkey_active) {
1597 /* This texture format will never be used.. So do not care about color keying
1598 up until the point in time it will be needed :-) */
1599 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1601 break;
1603 case WINED3DFMT_R5G6B5:
1604 if (colorkey_active) {
1605 *convert = CONVERT_CK_565;
1606 *format = GL_RGBA;
1607 *internal = GL_RGBA;
1608 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1610 break;
1612 case WINED3DFMT_X1R5G5B5:
1613 if (colorkey_active) {
1614 *convert = CONVERT_CK_5551;
1615 *format = GL_BGRA;
1616 *internal = GL_RGBA;
1617 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1619 break;
1621 case WINED3DFMT_R8G8B8:
1622 if (colorkey_active) {
1623 *convert = CONVERT_CK_RGB24;
1624 *format = GL_RGBA;
1625 *internal = GL_RGBA;
1626 *type = GL_UNSIGNED_INT_8_8_8_8;
1627 *target_bpp = 4;
1629 break;
1631 case WINED3DFMT_X8R8G8B8:
1632 if (colorkey_active) {
1633 *convert = CONVERT_RGB32_888;
1634 *format = GL_RGBA;
1635 *internal = GL_RGBA;
1636 *type = GL_UNSIGNED_INT_8_8_8_8;
1638 break;
1640 case WINED3DFMT_V8U8:
1641 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1642 *convert = CONVERT_V8U8;
1643 *format = GL_BGR;
1644 *internal = GL_RGB8;
1645 *type = GL_UNSIGNED_BYTE;
1646 *target_bpp = 3;
1647 break;
1649 case WINED3DFMT_L6V5U5:
1650 *convert = CONVERT_L6V5U5;
1651 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1652 *target_bpp = 3;
1653 /* Use format and types from table */
1654 } else {
1655 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1656 *target_bpp = 2;
1657 *format = GL_RGB;
1658 *internal = GL_RGB5;
1659 *type = GL_UNSIGNED_SHORT_5_6_5;
1661 break;
1663 case WINED3DFMT_X8L8V8U8:
1664 *convert = CONVERT_X8L8V8U8;
1665 *target_bpp = 4;
1666 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1667 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1668 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1669 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1670 * the needed type and format parameter, so the internal format contains a
1671 * 4th component, which is returned as alpha
1673 } else {
1674 *format = GL_BGRA;
1675 *internal = GL_RGB8;
1676 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1678 break;
1680 case WINED3DFMT_Q8W8V8U8:
1681 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1682 *convert = CONVERT_Q8W8V8U8;
1683 *format = GL_BGRA;
1684 *internal = GL_RGBA8;
1685 *type = GL_UNSIGNED_BYTE;
1686 *target_bpp = 4;
1687 break;
1689 case WINED3DFMT_V16U16:
1690 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1691 *convert = CONVERT_V16U16;
1692 *format = GL_BGR;
1693 *internal = GL_RGB16_EXT;
1694 *type = GL_UNSIGNED_SHORT;
1695 *target_bpp = 6;
1696 break;
1698 case WINED3DFMT_A4L4:
1699 /* A4L4 exists as an internal gl format, but for some reason there is not
1700 * format+type combination to load it. Thus convert it to A8L8, then load it
1701 * with A4L4 internal, but A8L8 format+type
1703 *convert = CONVERT_A4L4;
1704 *format = GL_LUMINANCE_ALPHA;
1705 *internal = GL_LUMINANCE4_ALPHA4;
1706 *type = GL_UNSIGNED_BYTE;
1707 *target_bpp = 2;
1708 break;
1710 case WINED3DFMT_R32F:
1711 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1712 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1713 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1714 * 1.0 instead.
1716 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1718 *convert = CONVERT_R32F;
1719 *format = GL_RGB;
1720 *internal = GL_RGB32F_ARB;
1721 *type = GL_FLOAT;
1722 *target_bpp = 12;
1723 break;
1725 case WINED3DFMT_R16F:
1726 /* Similar to R32F */
1727 *convert = CONVERT_R16F;
1728 *format = GL_RGB;
1729 *internal = GL_RGB16F_ARB;
1730 *type = GL_HALF_FLOAT_ARB;
1731 *target_bpp = 6;
1732 break;
1734 case WINED3DFMT_G16R16:
1735 *convert = CONVERT_G16R16;
1736 *format = GL_RGB;
1737 *internal = GL_RGB16_EXT;
1738 *type = GL_UNSIGNED_SHORT;
1739 *target_bpp = 6;
1740 break;
1742 default:
1743 break;
1746 return WINED3D_OK;
1749 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1750 BYTE *source, *dest;
1751 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1753 switch (convert) {
1754 case NO_CONVERSION:
1756 memcpy(dst, src, pitch * height);
1757 break;
1759 case CONVERT_PALETTED:
1760 case CONVERT_PALETTED_CK:
1762 IWineD3DPaletteImpl* pal = This->palette;
1763 BYTE table[256][4];
1764 unsigned int x, y;
1766 if( pal == NULL) {
1767 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1770 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1772 for (y = 0; y < height; y++)
1774 source = src + pitch * y;
1775 dest = dst + outpitch * y;
1776 /* This is an 1 bpp format, using the width here is fine */
1777 for (x = 0; x < width; x++) {
1778 BYTE color = *source++;
1779 *dest++ = table[color][0];
1780 *dest++ = table[color][1];
1781 *dest++ = table[color][2];
1782 *dest++ = table[color][3];
1786 break;
1788 case CONVERT_CK_565:
1790 /* Converting the 565 format in 5551 packed to emulate color-keying.
1792 Note : in all these conversion, it would be best to average the averaging
1793 pixels to get the color of the pixel that will be color-keyed to
1794 prevent 'color bleeding'. This will be done later on if ever it is
1795 too visible.
1797 Note2: Nvidia documents say that their driver does not support alpha + color keying
1798 on the same surface and disables color keying in such a case
1800 unsigned int x, y;
1801 WORD *Source;
1802 WORD *Dest;
1804 TRACE("Color keyed 565\n");
1806 for (y = 0; y < height; y++) {
1807 Source = (WORD *) (src + y * pitch);
1808 Dest = (WORD *) (dst + y * outpitch);
1809 for (x = 0; x < width; x++ ) {
1810 WORD color = *Source++;
1811 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1812 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1813 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1814 *Dest |= 0x0001;
1816 Dest++;
1820 break;
1822 case CONVERT_CK_5551:
1824 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1825 unsigned int x, y;
1826 WORD *Source;
1827 WORD *Dest;
1828 TRACE("Color keyed 5551\n");
1829 for (y = 0; y < height; y++) {
1830 Source = (WORD *) (src + y * pitch);
1831 Dest = (WORD *) (dst + y * outpitch);
1832 for (x = 0; x < width; x++ ) {
1833 WORD color = *Source++;
1834 *Dest = color;
1835 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1836 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1837 *Dest |= (1 << 15);
1839 else {
1840 *Dest &= ~(1 << 15);
1842 Dest++;
1846 break;
1848 case CONVERT_CK_RGB24:
1850 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
1851 unsigned int x, y;
1852 for (y = 0; y < height; y++)
1854 source = src + pitch * y;
1855 dest = dst + outpitch * y;
1856 for (x = 0; x < width; x++) {
1857 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
1858 DWORD dstcolor = color << 8;
1859 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1860 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1861 dstcolor |= 0xff;
1863 *(DWORD*)dest = dstcolor;
1864 source += 3;
1865 dest += 4;
1869 break;
1871 case CONVERT_RGB32_888:
1873 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
1874 unsigned int x, y;
1875 for (y = 0; y < height; y++)
1877 source = src + pitch * y;
1878 dest = dst + outpitch * y;
1879 for (x = 0; x < width; x++) {
1880 DWORD color = 0xffffff & *(DWORD*)source;
1881 DWORD dstcolor = color << 8;
1882 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1883 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1884 dstcolor |= 0xff;
1886 *(DWORD*)dest = dstcolor;
1887 source += 4;
1888 dest += 4;
1892 break;
1894 case CONVERT_V8U8:
1896 unsigned int x, y;
1897 short *Source;
1898 unsigned char *Dest;
1899 for(y = 0; y < height; y++) {
1900 Source = (short *) (src + y * pitch);
1901 Dest = dst + y * outpitch;
1902 for (x = 0; x < width; x++ ) {
1903 long color = (*Source++);
1904 /* B */ Dest[0] = 0xff;
1905 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1906 /* R */ Dest[2] = (color) + 128; /* U */
1907 Dest += 3;
1910 break;
1913 case CONVERT_V16U16:
1915 unsigned int x, y;
1916 DWORD *Source;
1917 unsigned short *Dest;
1918 for(y = 0; y < height; y++) {
1919 Source = (DWORD *) (src + y * pitch);
1920 Dest = (unsigned short *) (dst + y * outpitch);
1921 for (x = 0; x < width; x++ ) {
1922 DWORD color = (*Source++);
1923 /* B */ Dest[0] = 0xffff;
1924 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1925 /* R */ Dest[2] = (color ) + 32768; /* U */
1926 Dest += 3;
1929 break;
1932 case CONVERT_Q8W8V8U8:
1934 unsigned int x, y;
1935 DWORD *Source;
1936 unsigned char *Dest;
1937 for(y = 0; y < height; y++) {
1938 Source = (DWORD *) (src + y * pitch);
1939 Dest = dst + y * outpitch;
1940 for (x = 0; x < width; x++ ) {
1941 long color = (*Source++);
1942 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1943 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1944 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1945 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1946 Dest += 4;
1949 break;
1952 case CONVERT_L6V5U5:
1954 unsigned int x, y;
1955 WORD *Source;
1956 unsigned char *Dest;
1958 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1959 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1960 * fixed function and shaders without further conversion once the surface is
1961 * loaded
1963 for(y = 0; y < height; y++) {
1964 Source = (WORD *) (src + y * pitch);
1965 Dest = dst + y * outpitch;
1966 for (x = 0; x < width; x++ ) {
1967 short color = (*Source++);
1968 unsigned char l = ((color >> 10) & 0xfc);
1969 char v = ((color >> 5) & 0x3e);
1970 char u = ((color ) & 0x1f);
1972 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1973 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1974 * shift. GL reads a signed value and converts it into an unsigned value.
1976 /* M */ Dest[2] = l << 1;
1978 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1979 * from 5 bit values to 8 bit values.
1981 /* V */ Dest[1] = v << 3;
1982 /* U */ Dest[0] = u << 3;
1983 Dest += 3;
1986 } else {
1987 for(y = 0; y < height; y++) {
1988 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1989 Source = (WORD *) (src + y * pitch);
1990 for (x = 0; x < width; x++ ) {
1991 short color = (*Source++);
1992 unsigned char l = ((color >> 10) & 0xfc);
1993 short v = ((color >> 5) & 0x3e);
1994 short u = ((color ) & 0x1f);
1995 short v_conv = v + 16;
1996 short u_conv = u + 16;
1998 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1999 Dest_s += 1;
2003 break;
2006 case CONVERT_X8L8V8U8:
2008 unsigned int x, y;
2009 DWORD *Source;
2010 unsigned char *Dest;
2012 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2013 /* This implementation works with the fixed function pipeline and shaders
2014 * without further modification after converting the surface.
2016 for(y = 0; y < height; y++) {
2017 Source = (DWORD *) (src + y * pitch);
2018 Dest = dst + y * outpitch;
2019 for (x = 0; x < width; x++ ) {
2020 long color = (*Source++);
2021 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2022 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2023 /* U */ Dest[0] = (color & 0xff); /* U */
2024 /* I */ Dest[3] = 255; /* X */
2025 Dest += 4;
2028 } else {
2029 /* Doesn't work correctly with the fixed function pipeline, but can work in
2030 * shaders if the shader is adjusted. (There's no use for this format in gl's
2031 * standard fixed function pipeline anyway).
2033 for(y = 0; y < height; y++) {
2034 Source = (DWORD *) (src + y * pitch);
2035 Dest = dst + y * outpitch;
2036 for (x = 0; x < width; x++ ) {
2037 long color = (*Source++);
2038 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2039 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2040 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2041 Dest += 4;
2045 break;
2048 case CONVERT_A4L4:
2050 unsigned int x, y;
2051 unsigned char *Source;
2052 unsigned char *Dest;
2053 for(y = 0; y < height; y++) {
2054 Source = src + y * pitch;
2055 Dest = dst + y * outpitch;
2056 for (x = 0; x < width; x++ ) {
2057 unsigned char color = (*Source++);
2058 /* A */ Dest[1] = (color & 0xf0) << 0;
2059 /* L */ Dest[0] = (color & 0x0f) << 4;
2060 Dest += 2;
2063 break;
2066 case CONVERT_R32F:
2068 unsigned int x, y;
2069 float *Source;
2070 float *Dest;
2071 for(y = 0; y < height; y++) {
2072 Source = (float *) (src + y * pitch);
2073 Dest = (float *) (dst + y * outpitch);
2074 for (x = 0; x < width; x++ ) {
2075 float color = (*Source++);
2076 Dest[0] = color;
2077 Dest[1] = 1.0;
2078 Dest[2] = 1.0;
2079 Dest += 3;
2082 break;
2085 case CONVERT_R16F:
2087 unsigned int x, y;
2088 WORD *Source;
2089 WORD *Dest;
2090 WORD one = 0x3c00;
2091 for(y = 0; y < height; y++) {
2092 Source = (WORD *) (src + y * pitch);
2093 Dest = (WORD *) (dst + y * outpitch);
2094 for (x = 0; x < width; x++ ) {
2095 WORD color = (*Source++);
2096 Dest[0] = color;
2097 Dest[1] = one;
2098 Dest[2] = one;
2099 Dest += 3;
2102 break;
2105 case CONVERT_G16R16:
2107 unsigned int x, y;
2108 WORD *Source;
2109 WORD *Dest;
2111 for(y = 0; y < height; y++) {
2112 Source = (WORD *) (src + y * pitch);
2113 Dest = (WORD *) (dst + y * outpitch);
2114 for (x = 0; x < width; x++ ) {
2115 WORD green = (*Source++);
2116 WORD red = (*Source++);
2117 Dest[0] = green;
2118 Dest[1] = red;
2119 Dest[2] = 0xffff;
2120 Dest += 3;
2123 break;
2126 default:
2127 ERR("Unsupported conversation type %d\n", convert);
2129 return WINED3D_OK;
2132 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2133 IWineD3DPaletteImpl* pal = This->palette;
2134 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2135 BOOL index_in_alpha = FALSE;
2136 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2137 int i;
2139 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2140 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2141 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2142 * duplicate entries. Store the color key in the unused alpha component to speed the
2143 * download up and to make conversion unneeded. */
2144 index_in_alpha = primary_render_target_is_p8(device);
2146 if (pal == NULL) {
2147 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2148 if(dxVersion <= 7) {
2149 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2150 if(index_in_alpha) {
2151 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2152 there's no palette at this time. */
2153 for (i = 0; i < 256; i++) table[i][3] = i;
2155 } else {
2156 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2157 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2158 capability flag is present (wine does advertise this capability) */
2159 for (i = 0; i < 256; i++) {
2160 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2161 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2162 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2163 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2166 } else {
2167 TRACE("Using surface palette %p\n", pal);
2168 /* Get the surface's palette */
2169 for (i = 0; i < 256; i++) {
2170 table[i][0] = pal->palents[i].peRed;
2171 table[i][1] = pal->palents[i].peGreen;
2172 table[i][2] = pal->palents[i].peBlue;
2174 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2175 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2176 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2177 of pixels that should be masked away is set to 0. */
2178 if(index_in_alpha) {
2179 table[i][3] = i;
2180 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2181 table[i][3] = 0x00;
2182 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2183 table[i][3] = pal->palents[i].peFlags;
2184 } else {
2185 table[i][3] = 0xFF;
2191 const char *fragment_palette_conversion =
2192 "!!ARBfp1.0\n"
2193 "TEMP index;\n"
2194 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
2195 "TEX index, fragment.texcoord[0], texture[0], 2D;\n" /* The alpha-component contains the palette index */
2196 "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 */
2197 "TEX result.color, index.a, texture[1], 1D;\n" /* use the alpha-component as a index in the palette to get the final color */
2198 "END";
2200 /* This function is used in case of 8bit paletted textures to upload the palette.
2201 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2202 extensions like ATI_fragment_shaders is possible.
2204 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2205 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2206 BYTE table[256][4];
2207 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2209 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2211 /* Try to use the paletted texture extension */
2212 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2214 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2215 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2217 else
2219 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2220 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2221 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2223 /* Create the fragment program if we don't have it */
2224 if(!device->paletteConversionShader)
2226 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2227 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2228 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2229 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2230 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2233 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2234 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2236 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2237 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2239 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2240 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2241 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2242 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2244 /* Switch back to unit 0 in which the 2D texture will be stored. */
2245 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2247 /* Rebind the texture because it isn't bound anymore */
2248 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2252 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2253 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2255 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2256 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2257 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2259 return FALSE;
2262 if(This->palette9) {
2263 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2264 return FALSE;
2266 } else {
2267 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2269 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2270 return TRUE;
2273 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2274 GLboolean oldwrite[4];
2276 /* Some formats have only some color channels, and the others are 1.0.
2277 * since our rendering renders to all channels, and those pixel formats
2278 * are emulated by using a full texture with the other channels set to 1.0
2279 * manually, clear the unused channels.
2281 * This could be done with hacking colorwriteenable to mask the colors,
2282 * but before drawing the buffer would have to be cleared too, so there's
2283 * no gain in that
2285 switch(This->resource.format) {
2286 case WINED3DFMT_R16F:
2287 case WINED3DFMT_R32F:
2288 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2289 /* Do not activate a context, the correct drawable is active already
2290 * though just the read buffer is set, make sure to have the correct draw
2291 * buffer too
2293 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2294 glDisable(GL_SCISSOR_TEST);
2295 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2296 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2297 glClearColor(0.0, 1.0, 1.0, 1.0);
2298 glClear(GL_COLOR_BUFFER_BIT);
2299 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2300 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2301 checkGLcall("Unused channel clear\n");
2302 break;
2304 default: break;
2308 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2309 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2311 if (!(This->Flags & SFLAG_INTEXTURE)) {
2312 TRACE("Reloading because surface is dirty\n");
2313 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2314 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2315 /* Reload: vice versa OR */
2316 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2317 /* Also reload: Color key is active AND the color key has changed */
2318 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2319 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2320 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2321 TRACE("Reloading because of color keying\n");
2322 /* To perform the color key conversion we need a sysmem copy of
2323 * the surface. Make sure we have it
2326 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2327 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2328 /* TODO: This is not necessarily needed with hw palettized texture support */
2329 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2330 } else {
2331 TRACE("surface is already in texture\n");
2332 return WINED3D_OK;
2335 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2336 * These resources are not bound by device size or format restrictions. Because of this,
2337 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2338 * However, these resources can always be created, locked, and copied.
2340 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2342 FIXME("(%p) Operation not supported for scratch textures\n",This);
2343 return WINED3DERR_INVALIDCALL;
2346 This->srgb = srgb_mode;
2347 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2349 #if 0
2351 static unsigned int gen = 0;
2352 char buffer[4096];
2353 ++gen;
2354 if ((gen % 10) == 0) {
2355 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2356 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2359 * debugging crash code
2360 if (gen == 250) {
2361 void** test = NULL;
2362 *test = 0;
2366 #endif
2368 if (!(This->Flags & SFLAG_DONOTFREE)) {
2369 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2370 This->resource.allocatedMemory = NULL;
2371 This->resource.heapMemory = NULL;
2372 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2375 return WINED3D_OK;
2378 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2379 /* TODO: check for locks */
2380 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2381 IWineD3DBaseTexture *baseTexture = NULL;
2382 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2384 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2385 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2386 TRACE("Passing to container\n");
2387 IWineD3DBaseTexture_BindTexture(baseTexture);
2388 IWineD3DBaseTexture_Release(baseTexture);
2389 } else {
2390 TRACE("(%p) : Binding surface\n", This);
2392 if(!device->isInDraw) {
2393 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2396 ENTER_GL();
2398 if (!This->glDescription.level) {
2399 if (!This->glDescription.textureName) {
2400 glGenTextures(1, &This->glDescription.textureName);
2401 checkGLcall("glGenTextures");
2402 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
2404 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2405 checkGLcall("glBindTexture");
2406 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2407 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2408 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2409 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2410 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2411 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2412 glTexParameteri(This->glDescription.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2413 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2414 glTexParameteri(This->glDescription.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2415 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2417 /* This is where we should be reducing the amount of GLMemoryUsed */
2418 } else if (This->glDescription.textureName) {
2419 /* Mipmap surfaces should have a base texture container */
2420 ERR("Mipmap surface has a glTexture bound to it!\n");
2423 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2424 checkGLcall("glBindTexture");
2426 LEAVE_GL();
2428 return;
2431 #include <errno.h>
2432 #include <stdio.h>
2433 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2434 FILE* f = NULL;
2435 UINT i, y;
2436 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2437 char *allocatedMemory;
2438 char *textureRow;
2439 IWineD3DSwapChain *swapChain = NULL;
2440 int width, height;
2441 GLuint tmpTexture = 0;
2442 DWORD color;
2443 /*FIXME:
2444 Textures may not be stored in ->allocatedgMemory and a GlTexture
2445 so we should lock the surface before saving a snapshot, or at least check that
2447 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2448 by calling GetTexImage and in compressed form by calling
2449 GetCompressedTexImageARB. Queried compressed images can be saved and
2450 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2451 texture images do not need to be processed by the GL and should
2452 significantly improve texture loading performance relative to uncompressed
2453 images. */
2455 /* Setup the width and height to be the internal texture width and height. */
2456 width = This->pow2Width;
2457 height = This->pow2Height;
2458 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2459 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2461 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2462 /* if were not a real texture then read the back buffer into a real texture */
2463 /* we don't want to interfere with the back buffer so read the data into a temporary
2464 * texture and then save the data out of the temporary texture
2466 GLint prevRead;
2467 ENTER_GL();
2468 TRACE("(%p) Reading render target into texture\n", This);
2470 glGenTextures(1, &tmpTexture);
2471 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2473 glTexImage2D(GL_TEXTURE_2D,
2475 GL_RGBA,
2476 width,
2477 height,
2478 0/*border*/,
2479 GL_RGBA,
2480 GL_UNSIGNED_INT_8_8_8_8_REV,
2481 NULL);
2483 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2484 vcheckGLcall("glGetIntegerv");
2485 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2486 vcheckGLcall("glReadBuffer");
2487 glCopyTexImage2D(GL_TEXTURE_2D,
2489 GL_RGBA,
2492 width,
2493 height,
2496 checkGLcall("glCopyTexImage2D");
2497 glReadBuffer(prevRead);
2498 LEAVE_GL();
2500 } else { /* bind the real texture, and make sure it up to date */
2501 IWineD3DSurface_PreLoad(iface);
2502 surface_bind_and_dirtify(This);
2504 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2505 ENTER_GL();
2506 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2507 glGetTexImage(GL_TEXTURE_2D,
2508 This->glDescription.level,
2509 GL_RGBA,
2510 GL_UNSIGNED_INT_8_8_8_8_REV,
2511 allocatedMemory);
2512 checkGLcall("glTexImage2D");
2513 if (tmpTexture) {
2514 glBindTexture(GL_TEXTURE_2D, 0);
2515 glDeleteTextures(1, &tmpTexture);
2517 LEAVE_GL();
2519 f = fopen(filename, "w+");
2520 if (NULL == f) {
2521 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2522 return WINED3DERR_INVALIDCALL;
2524 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2525 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2526 /* TGA header */
2527 fputc(0,f);
2528 fputc(0,f);
2529 fputc(2,f);
2530 fputc(0,f);
2531 fputc(0,f);
2532 fputc(0,f);
2533 fputc(0,f);
2534 fputc(0,f);
2535 fputc(0,f);
2536 fputc(0,f);
2537 fputc(0,f);
2538 fputc(0,f);
2539 /* short width*/
2540 fwrite(&width,2,1,f);
2541 /* short height */
2542 fwrite(&height,2,1,f);
2543 /* format rgba */
2544 fputc(0x20,f);
2545 fputc(0x28,f);
2546 /* raw data */
2547 /* 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 */
2548 if(swapChain)
2549 textureRow = allocatedMemory + (width * (height - 1) *4);
2550 else
2551 textureRow = allocatedMemory;
2552 for (y = 0 ; y < height; y++) {
2553 for (i = 0; i < width; i++) {
2554 color = *((DWORD*)textureRow);
2555 fputc((color >> 16) & 0xFF, f); /* B */
2556 fputc((color >> 8) & 0xFF, f); /* G */
2557 fputc((color >> 0) & 0xFF, f); /* R */
2558 fputc((color >> 24) & 0xFF, f); /* A */
2559 textureRow += 4;
2561 /* take two rows of the pointer to the texture memory */
2562 if(swapChain)
2563 (textureRow-= width << 3);
2566 TRACE("Closing file\n");
2567 fclose(f);
2569 if(swapChain) {
2570 IWineD3DSwapChain_Release(swapChain);
2572 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2573 return WINED3D_OK;
2577 * Slightly inefficient way to handle multiple dirty rects but it works :)
2579 HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2580 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2581 IWineD3DBaseTexture *baseTexture = NULL;
2583 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2584 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2586 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2587 if (NULL != pDirtyRect) {
2588 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2589 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2590 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2591 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2592 } else {
2593 This->dirtyRect.left = 0;
2594 This->dirtyRect.top = 0;
2595 This->dirtyRect.right = This->currentDesc.Width;
2596 This->dirtyRect.bottom = This->currentDesc.Height;
2598 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2599 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2600 /* if the container is a basetexture then mark it dirty. */
2601 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2602 TRACE("Passing to container\n");
2603 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2604 IWineD3DBaseTexture_Release(baseTexture);
2606 return WINED3D_OK;
2609 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2610 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2611 HRESULT hr;
2612 const GlPixelFormatDesc *glDesc;
2613 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2615 TRACE("(%p) : Calling base function first\n", This);
2616 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2617 if(SUCCEEDED(hr)) {
2618 /* Setup some glformat defaults */
2619 This->glDescription.glFormat = glDesc->glFormat;
2620 This->glDescription.glFormatInternal = glDesc->glInternal;
2621 This->glDescription.glType = glDesc->glType;
2623 This->Flags &= ~SFLAG_ALLOCATED;
2624 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2625 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2627 return hr;
2630 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2631 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2633 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2634 WARN("Surface is locked or the HDC is in use\n");
2635 return WINED3DERR_INVALIDCALL;
2638 if(Mem && Mem != This->resource.allocatedMemory) {
2639 void *release = NULL;
2641 /* Do I have to copy the old surface content? */
2642 if(This->Flags & SFLAG_DIBSECTION) {
2643 /* Release the DC. No need to hold the critical section for the update
2644 * Thread because this thread runs only on front buffers, but this method
2645 * fails for render targets in the check above.
2647 SelectObject(This->hDC, This->dib.holdbitmap);
2648 DeleteDC(This->hDC);
2649 /* Release the DIB section */
2650 DeleteObject(This->dib.DIBsection);
2651 This->dib.bitmap_data = NULL;
2652 This->resource.allocatedMemory = NULL;
2653 This->hDC = NULL;
2654 This->Flags &= ~SFLAG_DIBSECTION;
2655 } else if(!(This->Flags & SFLAG_USERPTR)) {
2656 release = This->resource.heapMemory;
2657 This->resource.heapMemory = NULL;
2659 This->resource.allocatedMemory = Mem;
2660 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2662 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2663 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2665 /* For client textures opengl has to be notified */
2666 if(This->Flags & SFLAG_CLIENT) {
2667 This->Flags &= ~SFLAG_ALLOCATED;
2668 IWineD3DSurface_PreLoad(iface);
2669 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2672 /* Now free the old memory if any */
2673 HeapFree(GetProcessHeap(), 0, release);
2674 } else if(This->Flags & SFLAG_USERPTR) {
2675 /* LockRect and GetDC will re-create the dib section and allocated memory */
2676 This->resource.allocatedMemory = NULL;
2677 /* HeapMemory should be NULL already */
2678 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2679 This->Flags &= ~SFLAG_USERPTR;
2681 if(This->Flags & SFLAG_CLIENT) {
2682 This->Flags &= ~SFLAG_ALLOCATED;
2683 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2684 IWineD3DSurface_PreLoad(iface);
2687 return WINED3D_OK;
2690 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2692 /* Flip the surface contents */
2693 /* Flip the DC */
2695 HDC tmp;
2696 tmp = front->hDC;
2697 front->hDC = back->hDC;
2698 back->hDC = tmp;
2701 /* Flip the DIBsection */
2703 HBITMAP tmp;
2704 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2705 tmp = front->dib.DIBsection;
2706 front->dib.DIBsection = back->dib.DIBsection;
2707 back->dib.DIBsection = tmp;
2709 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2710 else front->Flags &= ~SFLAG_DIBSECTION;
2711 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2712 else back->Flags &= ~SFLAG_DIBSECTION;
2715 /* Flip the surface data */
2717 void* tmp;
2719 tmp = front->dib.bitmap_data;
2720 front->dib.bitmap_data = back->dib.bitmap_data;
2721 back->dib.bitmap_data = tmp;
2723 tmp = front->resource.allocatedMemory;
2724 front->resource.allocatedMemory = back->resource.allocatedMemory;
2725 back->resource.allocatedMemory = tmp;
2727 tmp = front->resource.heapMemory;
2728 front->resource.heapMemory = back->resource.heapMemory;
2729 back->resource.heapMemory = tmp;
2732 /* Flip the PBO */
2734 GLuint tmp_pbo = front->pbo;
2735 front->pbo = back->pbo;
2736 back->pbo = tmp_pbo;
2739 /* client_memory should not be different, but just in case */
2741 BOOL tmp;
2742 tmp = front->dib.client_memory;
2743 front->dib.client_memory = back->dib.client_memory;
2744 back->dib.client_memory = tmp;
2747 /* Flip the opengl texture */
2749 glDescriptor tmp_desc = back->glDescription;
2750 back->glDescription = front->glDescription;
2751 front->glDescription = tmp_desc;
2755 DWORD tmp_flags = back->Flags;
2756 back->Flags = front->Flags;
2757 front->Flags = tmp_flags;
2761 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2762 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2763 IWineD3DSwapChainImpl *swapchain = NULL;
2764 HRESULT hr;
2765 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2767 /* Flipping is only supported on RenderTargets and overlays*/
2768 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2769 WARN("Tried to flip a non-render target, non-overlay surface\n");
2770 return WINEDDERR_NOTFLIPPABLE;
2773 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2774 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2776 /* Update the overlay if it is visible */
2777 if(This->overlay_dest) {
2778 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
2779 } else {
2780 return WINED3D_OK;
2784 if(override) {
2785 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2786 * FIXME("(%p) Target override is not supported by now\n", This);
2787 * Additionally, it isn't really possible to support triple-buffering
2788 * properly on opengl at all
2792 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2793 if(!swapchain) {
2794 ERR("Flipped surface is not on a swapchain\n");
2795 return WINEDDERR_NOTFLIPPABLE;
2798 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2799 * and only d3d8 and d3d9 apps specify the presentation interval
2801 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2802 /* Most common case first to avoid wasting time on all the other cases */
2803 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2804 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2805 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2806 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2807 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2808 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2809 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2810 } else {
2811 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2814 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2815 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2816 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2817 return hr;
2820 /* Does a direct frame buffer -> texture copy. Stretching is done
2821 * with single pixel copy calls
2823 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2824 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2825 float xrel, yrel;
2826 UINT row;
2827 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2830 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2831 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2832 ENTER_GL();
2834 /* Bind the target texture */
2835 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2836 checkGLcall("glBindTexture");
2837 if(!swapchain) {
2838 TRACE("Reading from an offscreen target\n");
2839 upsidedown = !upsidedown;
2840 glReadBuffer(myDevice->offscreenBuffer);
2841 } else {
2842 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2843 glReadBuffer(buffer);
2845 checkGLcall("glReadBuffer");
2847 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2848 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2850 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2851 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2853 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2854 ERR("Texture filtering not supported in direct blit\n");
2856 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2857 ERR("Texture filtering not supported in direct blit\n");
2860 if(upsidedown &&
2861 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2862 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2863 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2865 glCopyTexSubImage2D(This->glDescription.target,
2866 This->glDescription.level,
2867 drect->x1, drect->y1, /* xoffset, yoffset */
2868 srect->x1, Src->currentDesc.Height - srect->y2,
2869 drect->x2 - drect->x1, drect->y2 - drect->y1);
2870 } else {
2871 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2872 /* I have to process this row by row to swap the image,
2873 * otherwise it would be upside down, so stretching in y direction
2874 * doesn't cost extra time
2876 * However, stretching in x direction can be avoided if not necessary
2878 for(row = drect->y1; row < drect->y2; row++) {
2879 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2880 /* Well, that stuff works, but it's very slow.
2881 * find a better way instead
2883 UINT col;
2885 for(col = drect->x1; col < drect->x2; col++) {
2886 glCopyTexSubImage2D(This->glDescription.target,
2887 This->glDescription.level,
2888 drect->x1 + col, row, /* xoffset, yoffset */
2889 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2890 1, 1);
2892 } else {
2893 glCopyTexSubImage2D(This->glDescription.target,
2894 This->glDescription.level,
2895 drect->x1, row, /* xoffset, yoffset */
2896 srect->x1, yoffset - (int) (row * yrel),
2897 drect->x2-drect->x1, 1);
2901 vcheckGLcall("glCopyTexSubImage2D");
2903 LEAVE_GL();
2906 /* Uses the hardware to stretch and flip the image */
2907 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2908 GLuint src, backup = 0;
2909 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2910 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2911 float left, right, top, bottom; /* Texture coordinates */
2912 UINT fbwidth = Src->currentDesc.Width;
2913 UINT fbheight = Src->currentDesc.Height;
2914 GLenum drawBuffer = GL_BACK;
2915 GLenum texture_target;
2916 BOOL noBackBufferBackup;
2918 TRACE("Using hwstretch blit\n");
2919 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2920 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2921 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2923 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2924 if(!noBackBufferBackup && Src->glDescription.textureName == 0) {
2925 /* Get it a description */
2926 IWineD3DSurface_PreLoad(SrcSurface);
2928 ENTER_GL();
2930 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2931 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2933 if(myDevice->activeContext->aux_buffers >= 2) {
2934 /* Got more than one aux buffer? Use the 2nd aux buffer */
2935 drawBuffer = GL_AUX1;
2936 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && myDevice->activeContext->aux_buffers >= 1) {
2937 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2938 drawBuffer = GL_AUX0;
2941 if(noBackBufferBackup) {
2942 glGenTextures(1, &backup);
2943 checkGLcall("glGenTextures\n");
2944 glBindTexture(GL_TEXTURE_2D, backup);
2945 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2946 texture_target = GL_TEXTURE_2D;
2947 } else {
2948 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2949 * we are reading from the back buffer, the backup can be used as source texture
2951 texture_target = Src->glDescription.target;
2952 glBindTexture(texture_target, Src->glDescription.textureName);
2953 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2954 glEnable(texture_target);
2955 checkGLcall("glEnable(texture_target)");
2957 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2958 Src->Flags &= ~SFLAG_INTEXTURE;
2961 if(swapchain) {
2962 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
2963 } else {
2964 TRACE("Reading from an offscreen target\n");
2965 upsidedown = !upsidedown;
2966 glReadBuffer(myDevice->offscreenBuffer);
2969 /* TODO: Only back up the part that will be overwritten */
2970 glCopyTexSubImage2D(texture_target, 0,
2971 0, 0 /* read offsets */,
2972 0, 0,
2973 fbwidth,
2974 fbheight);
2976 checkGLcall("glCopyTexSubImage2D");
2978 /* No issue with overriding these - the sampler is dirty due to blit usage */
2979 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2980 magLookup[Filter - WINED3DTEXF_NONE]);
2981 checkGLcall("glTexParameteri");
2982 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2983 minMipLookup[Filter][WINED3DTEXF_NONE]);
2984 checkGLcall("glTexParameteri");
2986 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2987 src = backup ? backup : Src->glDescription.textureName;
2988 } else {
2989 glReadBuffer(GL_FRONT);
2990 checkGLcall("glReadBuffer(GL_FRONT)");
2992 glGenTextures(1, &src);
2993 checkGLcall("glGenTextures(1, &src)");
2994 glBindTexture(GL_TEXTURE_2D, src);
2995 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2997 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2998 * out for power of 2 sizes
3000 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3001 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3002 checkGLcall("glTexImage2D");
3003 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3004 0, 0 /* read offsets */,
3005 0, 0,
3006 fbwidth,
3007 fbheight);
3009 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3010 checkGLcall("glTexParameteri");
3011 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3012 checkGLcall("glTexParameteri");
3014 glReadBuffer(GL_BACK);
3015 checkGLcall("glReadBuffer(GL_BACK)");
3017 if(texture_target != GL_TEXTURE_2D) {
3018 glDisable(texture_target);
3019 glEnable(GL_TEXTURE_2D);
3020 texture_target = GL_TEXTURE_2D;
3023 checkGLcall("glEnd and previous");
3025 left = srect->x1;
3026 right = srect->x2;
3028 if(upsidedown) {
3029 top = Src->currentDesc.Height - srect->y1;
3030 bottom = Src->currentDesc.Height - srect->y2;
3031 } else {
3032 top = Src->currentDesc.Height - srect->y2;
3033 bottom = Src->currentDesc.Height - srect->y1;
3036 if(Src->Flags & SFLAG_NORMCOORD) {
3037 left /= Src->pow2Width;
3038 right /= Src->pow2Width;
3039 top /= Src->pow2Height;
3040 bottom /= Src->pow2Height;
3043 /* draw the source texture stretched and upside down. The correct surface is bound already */
3044 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3045 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3047 glDrawBuffer(drawBuffer);
3048 glReadBuffer(drawBuffer);
3050 glBegin(GL_QUADS);
3051 /* bottom left */
3052 glTexCoord2f(left, bottom);
3053 glVertex2i(0, fbheight);
3055 /* top left */
3056 glTexCoord2f(left, top);
3057 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3059 /* top right */
3060 glTexCoord2f(right, top);
3061 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3063 /* bottom right */
3064 glTexCoord2f(right, bottom);
3065 glVertex2i(drect->x2 - drect->x1, fbheight);
3066 glEnd();
3067 checkGLcall("glEnd and previous");
3069 if(texture_target != This->glDescription.target) {
3070 glDisable(texture_target);
3071 glEnable(This->glDescription.target);
3072 texture_target = This->glDescription.target;
3075 /* Now read the stretched and upside down image into the destination texture */
3076 glBindTexture(texture_target, This->glDescription.textureName);
3077 checkGLcall("glBindTexture");
3078 glCopyTexSubImage2D(texture_target,
3080 drect->x1, drect->y1, /* xoffset, yoffset */
3081 0, 0, /* We blitted the image to the origin */
3082 drect->x2 - drect->x1, drect->y2 - drect->y1);
3083 checkGLcall("glCopyTexSubImage2D");
3085 if(drawBuffer == GL_BACK) {
3086 /* Write the back buffer backup back */
3087 if(backup) {
3088 if(texture_target != GL_TEXTURE_2D) {
3089 glDisable(texture_target);
3090 glEnable(GL_TEXTURE_2D);
3091 texture_target = GL_TEXTURE_2D;
3093 glBindTexture(GL_TEXTURE_2D, backup);
3094 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3095 } else {
3096 if(texture_target != Src->glDescription.target) {
3097 glDisable(texture_target);
3098 glEnable(Src->glDescription.target);
3099 texture_target = Src->glDescription.target;
3101 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3102 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
3105 glBegin(GL_QUADS);
3106 /* top left */
3107 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
3108 glVertex2i(0, 0);
3110 /* bottom left */
3111 glTexCoord2f(0.0, 0.0);
3112 glVertex2i(0, fbheight);
3114 /* bottom right */
3115 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
3116 glVertex2i(fbwidth, Src->currentDesc.Height);
3118 /* top right */
3119 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3120 glVertex2i(fbwidth, 0);
3121 glEnd();
3122 } else {
3123 /* Restore the old draw buffer */
3124 glDrawBuffer(GL_BACK);
3126 glDisable(texture_target);
3127 checkGLcall("glDisable(texture_target)");
3129 /* Cleanup */
3130 if(src != Src->glDescription.textureName && src != backup) {
3131 glDeleteTextures(1, &src);
3132 checkGLcall("glDeleteTextures(1, &src)");
3134 if(backup) {
3135 glDeleteTextures(1, &backup);
3136 checkGLcall("glDeleteTextures(1, &backup)");
3139 LEAVE_GL();
3142 /* Not called from the VTable */
3143 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3144 WINED3DRECT rect;
3145 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3146 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3147 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3149 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3151 /* Get the swapchain. One of the surfaces has to be a primary surface */
3152 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3153 WARN("Destination is in sysmem, rejecting gl blt\n");
3154 return WINED3DERR_INVALIDCALL;
3156 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3157 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3158 if(Src) {
3159 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3160 WARN("Src is in sysmem, rejecting gl blt\n");
3161 return WINED3DERR_INVALIDCALL;
3163 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3164 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3167 /* Early sort out of cases where no render target is used */
3168 if(!dstSwapchain && !srcSwapchain &&
3169 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3170 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3171 return WINED3DERR_INVALIDCALL;
3174 /* No destination color keying supported */
3175 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3176 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3177 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3178 return WINED3DERR_INVALIDCALL;
3181 if (DestRect) {
3182 rect.x1 = DestRect->left;
3183 rect.y1 = DestRect->top;
3184 rect.x2 = DestRect->right;
3185 rect.y2 = DestRect->bottom;
3186 } else {
3187 rect.x1 = 0;
3188 rect.y1 = 0;
3189 rect.x2 = This->currentDesc.Width;
3190 rect.y2 = This->currentDesc.Height;
3193 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3194 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3195 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3196 /* Half-life does a Blt from the back buffer to the front buffer,
3197 * Full surface size, no flags... Use present instead
3199 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3202 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3203 while(1)
3205 RECT mySrcRect;
3206 TRACE("Looking if a Present can be done...\n");
3207 /* Source Rectangle must be full surface */
3208 if( SrcRect ) {
3209 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3210 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3211 TRACE("No, Source rectangle doesn't match\n");
3212 break;
3215 mySrcRect.left = 0;
3216 mySrcRect.top = 0;
3217 mySrcRect.right = Src->currentDesc.Width;
3218 mySrcRect.bottom = Src->currentDesc.Height;
3220 /* No stretching may occur */
3221 if(mySrcRect.right != rect.x2 - rect.x1 ||
3222 mySrcRect.bottom != rect.y2 - rect.y1) {
3223 TRACE("No, stretching is done\n");
3224 break;
3227 /* Destination must be full surface or match the clipping rectangle */
3228 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3230 RECT cliprect;
3231 POINT pos[2];
3232 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3233 pos[0].x = rect.x1;
3234 pos[0].y = rect.y1;
3235 pos[1].x = rect.x2;
3236 pos[1].y = rect.y2;
3237 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3238 pos, 2);
3240 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3241 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3243 TRACE("No, dest rectangle doesn't match(clipper)\n");
3244 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3245 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3246 break;
3249 else
3251 if(rect.x1 != 0 || rect.y1 != 0 ||
3252 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3253 TRACE("No, dest rectangle doesn't match(surface size)\n");
3254 break;
3258 TRACE("Yes\n");
3260 /* These flags are unimportant for the flag check, remove them */
3261 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3262 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3264 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3265 * take very long, while a flip is fast.
3266 * This applies to Half-Life, which does such Blts every time it finished
3267 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3268 * menu. This is also used by all apps when they do windowed rendering
3270 * The problem is that flipping is not really the same as copying. After a
3271 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3272 * untouched. Therefore it's necessary to override the swap effect
3273 * and to set it back after the flip.
3275 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3276 * testcases.
3279 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3280 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3282 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3283 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3285 dstSwapchain->presentParms.SwapEffect = orig_swap;
3287 return WINED3D_OK;
3289 break;
3292 TRACE("Unsupported blit between buffers on the same swapchain\n");
3293 return WINED3DERR_INVALIDCALL;
3294 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3295 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3296 return WINED3DERR_INVALIDCALL;
3297 } else if(dstSwapchain && srcSwapchain) {
3298 FIXME("Implement hardware blit between two different swapchains\n");
3299 return WINED3DERR_INVALIDCALL;
3300 } else if(dstSwapchain) {
3301 if(SrcSurface == myDevice->render_targets[0]) {
3302 TRACE("Blit from active render target to a swapchain\n");
3303 /* Handled with regular texture -> swapchain blit */
3305 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3306 FIXME("Implement blit from a swapchain to the active render target\n");
3307 return WINED3DERR_INVALIDCALL;
3310 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3311 /* Blit from render target to texture */
3312 WINED3DRECT srect;
3313 BOOL upsideDown, stretchx;
3314 BOOL paletteOverride = FALSE;
3316 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3317 TRACE("Color keying not supported by frame buffer to texture blit\n");
3318 return WINED3DERR_INVALIDCALL;
3319 /* Destination color key is checked above */
3322 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3323 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3325 if(SrcRect) {
3326 if(SrcRect->top < SrcRect->bottom) {
3327 srect.y1 = SrcRect->top;
3328 srect.y2 = SrcRect->bottom;
3329 upsideDown = FALSE;
3330 } else {
3331 srect.y1 = SrcRect->bottom;
3332 srect.y2 = SrcRect->top;
3333 upsideDown = TRUE;
3335 srect.x1 = SrcRect->left;
3336 srect.x2 = SrcRect->right;
3337 } else {
3338 srect.x1 = 0;
3339 srect.y1 = 0;
3340 srect.x2 = Src->currentDesc.Width;
3341 srect.y2 = Src->currentDesc.Height;
3342 upsideDown = FALSE;
3344 if(rect.x1 > rect.x2) {
3345 UINT tmp = rect.x2;
3346 rect.x2 = rect.x1;
3347 rect.x1 = tmp;
3348 upsideDown = !upsideDown;
3351 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3352 stretchx = TRUE;
3353 } else {
3354 stretchx = FALSE;
3357 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3358 * In this case grab the palette from the render target. */
3359 if((This->resource.format == WINED3DFMT_P8) && (This->palette == NULL)) {
3360 paletteOverride = TRUE;
3361 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3362 This->palette = Src->palette;
3365 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3366 * flip the image nor scale it.
3368 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3369 * -> If the app wants a image width an unscaled width, copy it line per line
3370 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3371 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3372 * back buffer. This is slower than reading line per line, thus not used for flipping
3373 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3374 * pixel by pixel
3376 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3377 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3378 * backends.
3380 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3381 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3382 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3383 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3384 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3385 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3386 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3387 } else {
3388 TRACE("Using hardware stretching to flip / stretch the texture\n");
3389 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3392 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3393 if(paletteOverride)
3394 This->palette = NULL;
3396 if(!(This->Flags & SFLAG_DONOTFREE)) {
3397 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3398 This->resource.allocatedMemory = NULL;
3399 This->resource.heapMemory = NULL;
3400 } else {
3401 This->Flags &= ~SFLAG_INSYSMEM;
3403 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3404 * path is never entered
3406 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3408 return WINED3D_OK;
3409 } else if(Src) {
3410 /* Blit from offscreen surface to render target */
3411 float glTexCoord[4];
3412 DWORD oldCKeyFlags = Src->CKeyFlags;
3413 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3414 RECT SourceRectangle;
3415 BOOL paletteOverride = FALSE;
3417 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3419 if(SrcRect) {
3420 SourceRectangle.left = SrcRect->left;
3421 SourceRectangle.right = SrcRect->right;
3422 SourceRectangle.top = SrcRect->top;
3423 SourceRectangle.bottom = SrcRect->bottom;
3424 } else {
3425 SourceRectangle.left = 0;
3426 SourceRectangle.right = Src->currentDesc.Width;
3427 SourceRectangle.top = 0;
3428 SourceRectangle.bottom = Src->currentDesc.Height;
3431 /* When blitting from an offscreen surface to a rendertarget, the source
3432 * surface is not required to have a palette. Our rendering / conversion
3433 * code further down the road retrieves the palette from the surface, so
3434 * it must have a palette set. */
3435 if((Src->resource.format == WINED3DFMT_P8) && (Src->palette == NULL)) {
3436 paletteOverride = TRUE;
3437 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3438 Src->palette = This->palette;
3441 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3442 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3443 TRACE("Using stretch_rect_fbo\n");
3444 /* The source is always a texture, but never the currently active render target, and the texture
3445 * contents are never upside down
3447 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3448 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3450 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3451 if(paletteOverride)
3452 Src->palette = NULL;
3453 return WINED3D_OK;
3456 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3457 /* Fall back to software */
3458 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3459 SourceRectangle.left, SourceRectangle.top,
3460 SourceRectangle.right, SourceRectangle.bottom);
3461 return WINED3DERR_INVALIDCALL;
3464 /* Color keying: Check if we have to do a color keyed blt,
3465 * and if not check if a color key is activated.
3467 * Just modify the color keying parameters in the surface and restore them afterwards
3468 * The surface keeps track of the color key last used to load the opengl surface.
3469 * PreLoad will catch the change to the flags and color key and reload if necessary.
3471 if(Flags & WINEDDBLT_KEYSRC) {
3472 /* Use color key from surface */
3473 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3474 /* Use color key from DDBltFx */
3475 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3476 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3477 } else {
3478 /* Do not use color key */
3479 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3482 /* Now load the surface */
3483 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3485 /* Activate the destination context, set it up for blitting */
3486 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3488 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3489 * while OpenGL coordinates are window relative.
3490 * Also beware of the origin difference(top left vs bottom left).
3491 * Also beware that the front buffer's surface size is screen width x screen height,
3492 * whereas the real gl drawable size is the size of the window.
3494 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3495 RECT windowsize;
3496 POINT offset = {0,0};
3497 UINT h;
3498 ClientToScreen(dstSwapchain->win_handle, &offset);
3499 GetClientRect(dstSwapchain->win_handle, &windowsize);
3500 h = windowsize.bottom - windowsize.top;
3501 rect.x1 -= offset.x; rect.x2 -=offset.x;
3502 rect.y1 -= offset.y; rect.y2 -=offset.y;
3503 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3506 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format,
3507 Src->glDescription.target, Src->pow2Width, Src->pow2Height);
3509 ENTER_GL();
3511 /* Bind the texture */
3512 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3513 checkGLcall("glBindTexture");
3515 /* Filtering for StretchRect */
3516 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3517 magLookup[Filter - WINED3DTEXF_NONE]);
3518 checkGLcall("glTexParameteri");
3519 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3520 minMipLookup[Filter][WINED3DTEXF_NONE]);
3521 checkGLcall("glTexParameteri");
3522 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3523 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3524 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3525 checkGLcall("glTexEnvi");
3527 /* This is for color keying */
3528 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3529 glEnable(GL_ALPHA_TEST);
3530 checkGLcall("glEnable GL_ALPHA_TEST");
3532 /* When the primary render target uses P8, the alpha component contains the palette index.
3533 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3534 * should be masked away have alpha set to 0. */
3535 if(primary_render_target_is_p8(myDevice))
3536 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3537 else
3538 glAlphaFunc(GL_NOTEQUAL, 0.0);
3539 checkGLcall("glAlphaFunc\n");
3540 } else {
3541 glDisable(GL_ALPHA_TEST);
3542 checkGLcall("glDisable GL_ALPHA_TEST");
3545 /* Draw a textured quad
3547 glBegin(GL_QUADS);
3549 glColor3d(1.0f, 1.0f, 1.0f);
3550 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3551 glVertex3f(rect.x1,
3552 rect.y1,
3553 0.0);
3555 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3556 glVertex3f(rect.x1, rect.y2, 0.0);
3558 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3559 glVertex3f(rect.x2,
3560 rect.y2,
3561 0.0);
3563 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3564 glVertex3f(rect.x2,
3565 rect.y1,
3566 0.0);
3567 glEnd();
3568 checkGLcall("glEnd");
3570 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3571 glDisable(GL_ALPHA_TEST);
3572 checkGLcall("glDisable(GL_ALPHA_TEST)");
3575 glBindTexture(Src->glDescription.target, 0);
3576 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3578 /* Restore the color key parameters */
3579 Src->CKeyFlags = oldCKeyFlags;
3580 Src->SrcBltCKey = oldBltCKey;
3582 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3583 if(paletteOverride)
3584 Src->palette = NULL;
3586 LEAVE_GL();
3588 /* Leave the opengl state valid for blitting */
3589 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3591 /* Flush in case the drawable is used by multiple GL contexts */
3592 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3593 glFlush();
3595 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3596 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3597 * is outdated now
3599 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3601 return WINED3D_OK;
3602 } else {
3603 /* Source-Less Blit to render target */
3604 if (Flags & WINEDDBLT_COLORFILL) {
3605 /* This is easy to handle for the D3D Device... */
3606 DWORD color;
3608 TRACE("Colorfill\n");
3610 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3611 must be true if we are here */
3612 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3613 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3614 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3615 TRACE("Surface is higher back buffer, falling back to software\n");
3616 return WINED3DERR_INVALIDCALL;
3619 /* The color as given in the Blt function is in the format of the frame-buffer...
3620 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3622 if (This->resource.format == WINED3DFMT_P8) {
3623 DWORD alpha;
3625 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3626 else alpha = 0xFF000000;
3628 if (This->palette) {
3629 color = (alpha |
3630 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3631 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3632 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3633 } else {
3634 color = alpha;
3637 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3638 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3639 color = 0xFFFFFFFF;
3640 } else {
3641 color = ((0xFF000000) |
3642 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3643 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3644 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3647 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3648 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3649 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3651 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3652 color = DDBltFx->u5.dwFillColor;
3654 else {
3655 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3656 return WINED3DERR_INVALIDCALL;
3659 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3660 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3661 1, /* Number of rectangles */
3662 &rect, WINED3DCLEAR_TARGET, color,
3663 0.0 /* Z */,
3664 0 /* Stencil */);
3665 return WINED3D_OK;
3669 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3670 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3671 return WINED3DERR_INVALIDCALL;
3674 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3676 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3677 float depth;
3679 if (Flags & WINEDDBLT_DEPTHFILL) {
3680 switch(This->resource.format) {
3681 case WINED3DFMT_D16:
3682 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3683 break;
3684 case WINED3DFMT_D15S1:
3685 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3686 break;
3687 case WINED3DFMT_D24S8:
3688 case WINED3DFMT_D24X8:
3689 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3690 break;
3691 case WINED3DFMT_D32:
3692 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3693 break;
3694 default:
3695 depth = 0.0;
3696 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3699 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3700 DestRect == NULL ? 0 : 1,
3701 (WINED3DRECT *) DestRect,
3702 WINED3DCLEAR_ZBUFFER,
3703 0x00000000,
3704 depth,
3705 0x00000000);
3708 FIXME("(%p): Unsupp depthstencil blit\n", This);
3709 return WINED3DERR_INVALIDCALL;
3712 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3713 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3714 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3715 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3716 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3717 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3719 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3721 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3722 return WINEDDERR_SURFACEBUSY;
3725 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3726 * except depth blits, which seem to work
3728 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3729 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3730 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3731 return WINED3DERR_INVALIDCALL;
3732 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3733 TRACE("Z Blit override handled the blit\n");
3734 return WINED3D_OK;
3738 /* Special cases for RenderTargets */
3739 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3740 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3741 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3744 /* For the rest call the X11 surface implementation.
3745 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3746 * other Blts are rather rare
3748 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3751 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3752 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3753 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3754 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3755 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3757 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3759 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3760 return WINEDDERR_SURFACEBUSY;
3763 if(myDevice->inScene &&
3764 (iface == myDevice->stencilBufferTarget ||
3765 (Source && Source == myDevice->stencilBufferTarget))) {
3766 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3767 return WINED3DERR_INVALIDCALL;
3770 /* Special cases for RenderTargets */
3771 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3772 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3774 RECT SrcRect, DstRect;
3775 DWORD Flags=0;
3777 if(rsrc) {
3778 SrcRect.left = rsrc->left;
3779 SrcRect.top= rsrc->top;
3780 SrcRect.bottom = rsrc->bottom;
3781 SrcRect.right = rsrc->right;
3782 } else {
3783 SrcRect.left = 0;
3784 SrcRect.top = 0;
3785 SrcRect.right = srcImpl->currentDesc.Width;
3786 SrcRect.bottom = srcImpl->currentDesc.Height;
3789 DstRect.left = dstx;
3790 DstRect.top=dsty;
3791 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3792 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3794 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3795 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3796 Flags |= WINEDDBLT_KEYSRC;
3797 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3798 Flags |= WINEDDBLT_KEYDEST;
3799 if(trans & WINEDDBLTFAST_WAIT)
3800 Flags |= WINEDDBLT_WAIT;
3801 if(trans & WINEDDBLTFAST_DONOTWAIT)
3802 Flags |= WINEDDBLT_DONOTWAIT;
3804 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3808 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3811 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3812 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3813 RGBQUAD col[256];
3814 IWineD3DPaletteImpl *pal = This->palette;
3815 unsigned int n;
3816 TRACE("(%p)\n", This);
3818 if (!pal) return WINED3D_OK;
3820 if(This->resource.format == WINED3DFMT_P8 ||
3821 This->resource.format == WINED3DFMT_A8P8)
3823 int bpp;
3824 GLenum format, internal, type;
3825 CONVERT_TYPES convert;
3827 /* Check if we are using a RTL mode which uses texturing for uploads */
3828 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX);
3830 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
3831 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, This->srgb);
3833 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
3835 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
3836 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
3838 /* We want to force a palette refresh, so mark the drawable as not being up to date */
3839 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
3841 /* Re-upload the palette */
3842 d3dfmt_p8_upload_palette(iface, convert);
3843 } else {
3844 if(!(This->Flags & SFLAG_INSYSMEM)) {
3845 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3846 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3848 TRACE("Dirtifying surface\n");
3849 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3853 if(This->Flags & SFLAG_DIBSECTION) {
3854 TRACE("(%p): Updating the hdc's palette\n", This);
3855 for (n=0; n<256; n++) {
3856 col[n].rgbRed = pal->palents[n].peRed;
3857 col[n].rgbGreen = pal->palents[n].peGreen;
3858 col[n].rgbBlue = pal->palents[n].peBlue;
3859 col[n].rgbReserved = 0;
3861 SetDIBColorTable(This->hDC, 0, 256, col);
3864 /* Propagate the changes to the drawable when we have a palette. */
3865 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3866 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3868 return WINED3D_OK;
3871 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3872 /** Check against the maximum texture sizes supported by the video card **/
3873 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3874 unsigned int pow2Width, pow2Height;
3875 const GlPixelFormatDesc *glDesc;
3877 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3878 /* Setup some glformat defaults */
3879 This->glDescription.glFormat = glDesc->glFormat;
3880 This->glDescription.glFormatInternal = glDesc->glInternal;
3881 This->glDescription.glType = glDesc->glType;
3883 This->glDescription.textureName = 0;
3884 This->glDescription.target = GL_TEXTURE_2D;
3886 /* Non-power2 support */
3887 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO) || GL_SUPPORT(WINE_NORMALIZED_TEXRECT)) {
3888 pow2Width = This->currentDesc.Width;
3889 pow2Height = This->currentDesc.Height;
3890 } else {
3891 /* Find the nearest pow2 match */
3892 pow2Width = pow2Height = 1;
3893 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3894 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3896 This->pow2Width = pow2Width;
3897 This->pow2Height = pow2Height;
3899 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3900 WINED3DFORMAT Format = This->resource.format;
3901 /** TODO: add support for non power two compressed textures **/
3902 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3903 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5
3904 || This->resource.format == WINED3DFMT_ATI2N) {
3905 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3906 This, This->currentDesc.Width, This->currentDesc.Height);
3907 return WINED3DERR_NOTAVAILABLE;
3911 if(pow2Width != This->currentDesc.Width ||
3912 pow2Height != This->currentDesc.Height) {
3913 This->Flags |= SFLAG_NONPOW2;
3916 TRACE("%p\n", This);
3917 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3918 /* one of three options
3919 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)
3920 2: Set the texture to the maximum size (bad idea)
3921 3: WARN and return WINED3DERR_NOTAVAILABLE;
3922 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.
3924 WARN("(%p) Creating an oversized surface\n", This);
3925 This->Flags |= SFLAG_OVERSIZE;
3927 /* This will be initialized on the first blt */
3928 This->glRect.left = 0;
3929 This->glRect.top = 0;
3930 This->glRect.right = 0;
3931 This->glRect.bottom = 0;
3932 } else {
3933 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3934 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3935 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3936 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3938 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE) &&
3939 !((This->resource.format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE) && (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3941 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3942 This->pow2Width = This->currentDesc.Width;
3943 This->pow2Height = This->currentDesc.Height;
3944 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
3947 /* No oversize, gl rect is the full texture size */
3948 This->Flags &= ~SFLAG_OVERSIZE;
3949 This->glRect.left = 0;
3950 This->glRect.top = 0;
3951 This->glRect.right = This->pow2Width;
3952 This->glRect.bottom = This->pow2Height;
3955 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3956 switch(wined3d_settings.offscreen_rendering_mode) {
3957 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3958 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3959 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3963 This->Flags |= SFLAG_INSYSMEM;
3965 return WINED3D_OK;
3968 struct depth_blt_info
3970 GLenum binding;
3971 GLenum bind_target;
3972 enum tex_types tex_type;
3973 GLfloat coords[4][3];
3976 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
3978 GLfloat (*coords)[3] = info->coords;
3980 switch (target)
3982 default:
3983 FIXME("Unsupported texture target %#x\n", target);
3984 /* Fall back to GL_TEXTURE_2D */
3985 case GL_TEXTURE_2D:
3986 info->binding = GL_TEXTURE_BINDING_2D;
3987 info->bind_target = GL_TEXTURE_2D;
3988 info->tex_type = tex_2d;
3989 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
3990 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
3991 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
3992 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
3993 break;
3995 case GL_TEXTURE_RECTANGLE_ARB:
3996 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
3997 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
3998 info->tex_type = tex_rect;
3999 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
4000 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
4001 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4002 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4003 break;
4005 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4006 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4007 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4008 info->tex_type = tex_cube;
4009 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4010 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4011 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4012 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4014 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4015 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4016 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4017 info->tex_type = tex_cube;
4018 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4019 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4020 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4021 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4023 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4024 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4025 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4026 info->tex_type = tex_cube;
4027 coords[0][0] = -1.0f; coords[0][1] = 1.0f; coords[0][2] = 1.0f;
4028 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 1.0f;
4029 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4030 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4032 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4033 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4034 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4035 info->tex_type = tex_cube;
4036 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4037 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4038 coords[2][0] = -1.0f; coords[2][1] = -1.0f; coords[2][2] = 1.0f;
4039 coords[3][0] = 1.0f; coords[3][1] = -1.0f; coords[3][2] = 1.0f;
4041 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4042 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4043 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4044 info->tex_type = tex_cube;
4045 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4046 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4047 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4048 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4050 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4051 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4052 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4053 info->tex_type = tex_cube;
4054 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4055 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4056 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4057 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4061 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4063 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4064 struct depth_blt_info info;
4065 GLint old_binding = 0;
4067 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4069 glDisable(GL_CULL_FACE);
4070 glEnable(GL_BLEND);
4071 glDisable(GL_ALPHA_TEST);
4072 glDisable(GL_SCISSOR_TEST);
4073 glDisable(GL_STENCIL_TEST);
4074 glEnable(GL_DEPTH_TEST);
4075 glDepthFunc(GL_ALWAYS);
4076 glDepthMask(GL_TRUE);
4077 glBlendFunc(GL_ZERO, GL_ONE);
4078 glViewport(0, 0, w, h);
4080 surface_get_depth_blt_info(target, w, h, &info);
4081 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4082 glGetIntegerv(info.binding, &old_binding);
4083 glBindTexture(info.bind_target, texture);
4085 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4087 glBegin(GL_TRIANGLE_STRIP);
4088 glTexCoord3fv(info.coords[0]);
4089 glVertex2f(-1.0f, -1.0f);
4090 glTexCoord3fv(info.coords[1]);
4091 glVertex2f(1.0f, -1.0f);
4092 glTexCoord3fv(info.coords[2]);
4093 glVertex2f(-1.0f, 1.0f);
4094 glTexCoord3fv(info.coords[3]);
4095 glVertex2f(1.0f, 1.0f);
4096 glEnd();
4098 glBindTexture(info.bind_target, old_binding);
4100 glPopAttrib();
4102 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4105 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4106 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4108 TRACE("(%p) New location %#x\n", This, location);
4110 if (location & ~SFLAG_DS_LOCATIONS) {
4111 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4114 This->Flags &= ~SFLAG_DS_LOCATIONS;
4115 This->Flags |= location;
4118 void surface_load_ds_location(IWineD3DSurface *iface, DWORD location) {
4119 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4120 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4122 TRACE("(%p) New location %#x\n", This, location);
4124 /* TODO: Make this work for modes other than FBO */
4125 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4127 if (This->Flags & location) {
4128 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4129 return;
4132 if (This->current_renderbuffer) {
4133 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4134 return;
4137 if (location == SFLAG_DS_OFFSCREEN) {
4138 if (This->Flags & SFLAG_DS_ONSCREEN) {
4139 GLint old_binding = 0;
4140 GLenum bind_target;
4142 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4144 ENTER_GL();
4146 if (!device->depth_blt_texture) {
4147 glGenTextures(1, &device->depth_blt_texture);
4150 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4151 * directly on the FBO texture. That's because we need to flip. */
4152 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4153 if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
4154 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4155 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4156 } else {
4157 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4158 bind_target = GL_TEXTURE_2D;
4160 glBindTexture(bind_target, device->depth_blt_texture);
4161 glCopyTexImage2D(bind_target,
4162 This->glDescription.level,
4163 This->glDescription.glFormatInternal,
4166 This->currentDesc.Width,
4167 This->currentDesc.Height,
4169 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4170 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4171 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4172 glBindTexture(bind_target, old_binding);
4174 /* Setup the destination */
4175 if (!device->depth_blt_rb) {
4176 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
4177 checkGLcall("glGenRenderbuffersEXT");
4179 if (device->depth_blt_rb_w != This->currentDesc.Width
4180 || device->depth_blt_rb_h != This->currentDesc.Height) {
4181 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4182 checkGLcall("glBindRenderbufferEXT");
4183 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
4184 checkGLcall("glRenderbufferStorageEXT");
4185 device->depth_blt_rb_w = This->currentDesc.Width;
4186 device->depth_blt_rb_h = This->currentDesc.Height;
4189 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->dst_fbo);
4190 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4191 checkGLcall("glFramebufferRenderbufferEXT");
4192 context_attach_depth_stencil_fbo(device, GL_FRAMEBUFFER_EXT, iface, FALSE);
4194 /* Do the actual blit */
4195 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4196 checkGLcall("depth_blt");
4198 if (device->activeContext->current_fbo) {
4199 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->current_fbo->id);
4200 } else {
4201 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4202 checkGLcall("glBindFramebuffer()");
4205 LEAVE_GL();
4206 } else {
4207 FIXME("No up to date depth stencil location\n");
4209 } else if (location == SFLAG_DS_ONSCREEN) {
4210 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4211 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4213 ENTER_GL();
4215 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4216 checkGLcall("glBindFramebuffer()");
4217 surface_depth_blt(This, This->glDescription.textureName, This->currentDesc.Width, This->currentDesc.Height, This->glDescription.target);
4218 checkGLcall("depth_blt");
4220 if (device->activeContext->current_fbo) {
4221 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, device->activeContext->current_fbo->id));
4222 checkGLcall("glBindFramebuffer()");
4225 LEAVE_GL();
4226 } else {
4227 FIXME("No up to date depth stencil location\n");
4229 } else {
4230 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4233 This->Flags |= location;
4236 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4237 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4238 IWineD3DBaseTexture *texture;
4239 IWineD3DSurfaceImpl *overlay;
4241 TRACE("(%p)->(%s, %s)\n", iface,
4242 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
4243 persistent ? "TRUE" : "FALSE");
4245 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4246 IWineD3DSwapChain *swapchain = NULL;
4248 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
4249 TRACE("Surface %p is an onscreen surface\n", iface);
4251 IWineD3DSwapChain_Release(swapchain);
4252 } else {
4253 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4254 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4258 if(persistent) {
4259 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
4260 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4261 TRACE("Passing to container\n");
4262 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4263 IWineD3DBaseTexture_Release(texture);
4266 This->Flags &= ~SFLAG_LOCATIONS;
4267 This->Flags |= flag;
4269 /* Redraw emulated overlays, if any */
4270 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4271 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4272 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4275 } else {
4276 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
4277 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4278 TRACE("Passing to container\n");
4279 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4280 IWineD3DBaseTexture_Release(texture);
4283 This->Flags &= ~flag;
4287 struct coords {
4288 GLfloat x, y, z;
4291 struct float_rect
4293 float l;
4294 float t;
4295 float r;
4296 float b;
4299 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4301 f->l = ((r->left * 2.0f) / w) - 1.0f;
4302 f->t = ((r->top * 2.0f) / h) - 1.0f;
4303 f->r = ((r->right * 2.0f) / w) - 1.0f;
4304 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4307 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
4308 struct coords coords[4];
4309 RECT rect;
4310 IWineD3DSwapChain *swapchain;
4311 IWineD3DBaseTexture *texture;
4312 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4313 GLenum bind_target;
4314 struct float_rect f;
4316 if(rect_in) {
4317 rect = *rect_in;
4318 } else {
4319 rect.left = 0;
4320 rect.top = 0;
4321 rect.right = This->currentDesc.Width;
4322 rect.bottom = This->currentDesc.Height;
4325 switch(This->glDescription.target)
4327 case GL_TEXTURE_2D:
4328 bind_target = GL_TEXTURE_2D;
4330 coords[0].x = (float)rect.left / This->pow2Width;
4331 coords[0].y = (float)rect.top / This->pow2Height;
4332 coords[0].z = 0;
4334 coords[1].x = (float)rect.left / This->pow2Width;
4335 coords[1].y = (float)rect.bottom / This->pow2Height;
4336 coords[1].z = 0;
4338 coords[2].x = (float)rect.right / This->pow2Width;
4339 coords[2].y = (float)rect.bottom / This->pow2Height;
4340 coords[2].z = 0;
4342 coords[3].x = (float)rect.right / This->pow2Width;
4343 coords[3].y = (float)rect.top / This->pow2Height;
4344 coords[3].z = 0;
4345 break;
4347 case GL_TEXTURE_RECTANGLE_ARB:
4348 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4349 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4350 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4351 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4352 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4353 break;
4355 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4356 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4357 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4358 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4359 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4360 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4361 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4362 break;
4364 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4365 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4366 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4367 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4368 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4369 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4370 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4371 break;
4373 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4374 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4375 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4376 coords[0].x = f.l; coords[0].y = 1; coords[0].z = f.t;
4377 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4378 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4379 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4380 break;
4382 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4383 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4384 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4385 coords[0].x = f.l; coords[0].y = -1; coords[0].z = -f.t;
4386 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4387 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4388 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4389 break;
4391 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4392 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4393 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4394 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4395 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4396 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4397 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4398 break;
4400 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4401 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4402 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4403 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4404 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4405 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4406 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4407 break;
4409 default:
4410 ERR("Unexpected texture target %#x\n", This->glDescription.target);
4411 return;
4414 ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4415 ENTER_GL();
4417 glEnable(bind_target);
4418 checkGLcall("glEnable(bind_target)");
4419 glBindTexture(bind_target, This->glDescription.textureName);
4420 checkGLcall("bind_target, This->glDescription.textureName)");
4421 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4422 checkGLcall("glTexParameteri");
4423 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4424 checkGLcall("glTexParameteri");
4426 if (device->render_offscreen)
4428 LONG tmp = rect.top;
4429 rect.top = rect.bottom;
4430 rect.bottom = tmp;
4433 glBegin(GL_QUADS);
4434 glTexCoord3fv(&coords[0].x);
4435 glVertex2i(rect.left, rect.top);
4437 glTexCoord3fv(&coords[1].x);
4438 glVertex2i(rect.left, rect.bottom);
4440 glTexCoord3fv(&coords[2].x);
4441 glVertex2i(rect.right, rect.bottom);
4443 glTexCoord3fv(&coords[3].x);
4444 glVertex2i(rect.right, rect.top);
4445 glEnd();
4446 checkGLcall("glEnd");
4448 glDisable(bind_target);
4449 checkGLcall("glDisable(bind_target)");
4451 LEAVE_GL();
4453 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4455 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4456 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4457 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4458 glFlush();
4460 IWineD3DSwapChain_Release(swapchain);
4461 } else {
4462 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4463 * reset properly next draw
4465 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4467 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4468 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4469 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4470 IWineD3DBaseTexture_Release(texture);
4475 /*****************************************************************************
4476 * IWineD3DSurface::LoadLocation
4478 * Copies the current surface data from wherever it is to the requested
4479 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4480 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4481 * multiple locations, the gl texture is preferred over the drawable, which is
4482 * preferred over system memory. The PBO counts as system memory. If rect is
4483 * not NULL, only the specified rectangle is copied (only supported for
4484 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4485 * location is marked up to date after the copy.
4487 * Parameters:
4488 * flag: Surface location flag to be updated
4489 * rect: rectangle to be copied
4491 * Returns:
4492 * WINED3D_OK on success
4493 * WINED3DERR_DEVICELOST on an internal error
4495 *****************************************************************************/
4496 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4497 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4498 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4499 IWineD3DSwapChain *swapchain = NULL;
4500 GLenum format, internal, type;
4501 CONVERT_TYPES convert;
4502 int bpp;
4503 int width, pitch, outpitch;
4504 BYTE *mem;
4506 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4507 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
4508 TRACE("Surface %p is an onscreen surface\n", iface);
4510 IWineD3DSwapChain_Release(swapchain);
4511 } else {
4512 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4513 * Prefer SFLAG_INTEXTURE. */
4514 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4518 TRACE("(%p)->(%s, %p)\n", iface,
4519 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
4520 rect);
4521 if(rect) {
4522 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4525 if(This->Flags & flag) {
4526 TRACE("Location already up to date\n");
4527 return WINED3D_OK;
4530 if(!(This->Flags & SFLAG_LOCATIONS)) {
4531 ERR("Surface does not have any up to date location\n");
4532 This->Flags |= SFLAG_LOST;
4533 return WINED3DERR_DEVICELOST;
4536 if(flag == SFLAG_INSYSMEM) {
4537 surface_prepare_system_memory(This);
4539 /* Download the surface to system memory */
4540 if(This->Flags & SFLAG_INTEXTURE) {
4541 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4542 surface_bind_and_dirtify(This);
4544 surface_download_data(This);
4545 } else {
4546 read_from_framebuffer(This, rect,
4547 This->resource.allocatedMemory,
4548 IWineD3DSurface_GetPitch(iface));
4550 } else if(flag == SFLAG_INDRAWABLE) {
4551 if(This->Flags & SFLAG_INTEXTURE) {
4552 surface_blt_to_drawable(This, rect);
4553 } else {
4554 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4556 /* The width is in 'length' not in bytes */
4557 width = This->currentDesc.Width;
4558 pitch = IWineD3DSurface_GetPitch(iface);
4560 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4561 * but it isn't set (yet) in all cases it is getting called. */
4562 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4563 TRACE("Removing the pbo attached to surface %p\n", This);
4564 surface_remove_pbo(This);
4567 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4568 int height = This->currentDesc.Height;
4570 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4571 outpitch = width * bpp;
4572 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4574 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4575 if(!mem) {
4576 ERR("Out of memory %d, %d!\n", outpitch, height);
4577 return WINED3DERR_OUTOFVIDEOMEMORY;
4579 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4581 This->Flags |= SFLAG_CONVERTED;
4582 } else {
4583 This->Flags &= ~SFLAG_CONVERTED;
4584 mem = This->resource.allocatedMemory;
4587 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4589 /* Don't delete PBO memory */
4590 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4591 HeapFree(GetProcessHeap(), 0, mem);
4593 } else /* if(flag == SFLAG_INTEXTURE) */ {
4594 if (This->Flags & SFLAG_INDRAWABLE) {
4595 read_from_framebuffer_texture(This);
4596 } else { /* Upload from system memory */
4597 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4599 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4600 surface_bind_and_dirtify(This);
4602 /* The only place where LoadTexture() might get called when isInDraw=1
4603 * is ActivateContext where lastActiveRenderTarget is preloaded.
4605 if(iface == device->lastActiveRenderTarget && device->isInDraw)
4606 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
4608 /* Otherwise: System memory copy must be most up to date */
4610 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4611 This->Flags |= SFLAG_GLCKEY;
4612 This->glCKey = This->SrcBltCKey;
4614 else This->Flags &= ~SFLAG_GLCKEY;
4616 /* The width is in 'length' not in bytes */
4617 width = This->currentDesc.Width;
4618 pitch = IWineD3DSurface_GetPitch(iface);
4620 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4621 * but it isn't set (yet) in all cases it is getting called. */
4622 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4623 TRACE("Removing the pbo attached to surface %p\n", This);
4624 surface_remove_pbo(This);
4627 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4628 int height = This->currentDesc.Height;
4630 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4631 outpitch = width * bpp;
4632 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4634 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4635 if(!mem) {
4636 ERR("Out of memory %d, %d!\n", outpitch, height);
4637 return WINED3DERR_OUTOFVIDEOMEMORY;
4639 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4641 This->Flags |= SFLAG_CONVERTED;
4642 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
4643 d3dfmt_p8_upload_palette(iface, convert);
4644 This->Flags &= ~SFLAG_CONVERTED;
4645 mem = This->resource.allocatedMemory;
4646 } else {
4647 This->Flags &= ~SFLAG_CONVERTED;
4648 mem = This->resource.allocatedMemory;
4651 /* Make sure the correct pitch is used */
4652 ENTER_GL();
4653 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4654 LEAVE_GL();
4656 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4657 TRACE("non power of two support\n");
4658 if(!(This->Flags & SFLAG_ALLOCATED)) {
4659 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4661 if (mem || (This->Flags & SFLAG_PBO)) {
4662 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4664 } else {
4665 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4666 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4668 if(!(This->Flags & SFLAG_ALLOCATED)) {
4669 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4671 if (mem || (This->Flags & SFLAG_PBO)) {
4672 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4676 /* Restore the default pitch */
4677 ENTER_GL();
4678 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4679 LEAVE_GL();
4681 /* Don't delete PBO memory */
4682 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4683 HeapFree(GetProcessHeap(), 0, mem);
4687 if(rect == NULL) {
4688 This->Flags |= flag;
4691 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
4692 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4693 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4694 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4697 return WINED3D_OK;
4700 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
4701 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4702 IWineD3DSwapChain *swapchain = NULL;
4704 /* Update the drawable size method */
4705 if(container) {
4706 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4708 if(swapchain) {
4709 This->get_drawable_size = get_drawable_size_swapchain;
4710 IWineD3DSwapChain_Release(swapchain);
4711 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4712 switch(wined3d_settings.offscreen_rendering_mode) {
4713 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4714 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4715 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4719 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4722 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4723 return SURFACE_OPENGL;
4726 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4727 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4728 HRESULT hr;
4730 /* If there's no destination surface there is nothing to do */
4731 if(!This->overlay_dest) return WINED3D_OK;
4733 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4734 * update the overlay. Prevent an endless recursion
4736 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
4737 return WINED3D_OK;
4739 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
4740 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4741 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4742 NULL, WINED3DTEXF_LINEAR);
4743 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
4745 return hr;
4748 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4750 /* IUnknown */
4751 IWineD3DBaseSurfaceImpl_QueryInterface,
4752 IWineD3DBaseSurfaceImpl_AddRef,
4753 IWineD3DSurfaceImpl_Release,
4754 /* IWineD3DResource */
4755 IWineD3DBaseSurfaceImpl_GetParent,
4756 IWineD3DBaseSurfaceImpl_GetDevice,
4757 IWineD3DBaseSurfaceImpl_SetPrivateData,
4758 IWineD3DBaseSurfaceImpl_GetPrivateData,
4759 IWineD3DBaseSurfaceImpl_FreePrivateData,
4760 IWineD3DBaseSurfaceImpl_SetPriority,
4761 IWineD3DBaseSurfaceImpl_GetPriority,
4762 IWineD3DSurfaceImpl_PreLoad,
4763 IWineD3DSurfaceImpl_UnLoad,
4764 IWineD3DBaseSurfaceImpl_GetType,
4765 /* IWineD3DSurface */
4766 IWineD3DBaseSurfaceImpl_GetContainer,
4767 IWineD3DBaseSurfaceImpl_GetDesc,
4768 IWineD3DSurfaceImpl_LockRect,
4769 IWineD3DSurfaceImpl_UnlockRect,
4770 IWineD3DSurfaceImpl_GetDC,
4771 IWineD3DSurfaceImpl_ReleaseDC,
4772 IWineD3DSurfaceImpl_Flip,
4773 IWineD3DSurfaceImpl_Blt,
4774 IWineD3DBaseSurfaceImpl_GetBltStatus,
4775 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4776 IWineD3DBaseSurfaceImpl_IsLost,
4777 IWineD3DBaseSurfaceImpl_Restore,
4778 IWineD3DSurfaceImpl_BltFast,
4779 IWineD3DBaseSurfaceImpl_GetPalette,
4780 IWineD3DBaseSurfaceImpl_SetPalette,
4781 IWineD3DSurfaceImpl_RealizePalette,
4782 IWineD3DBaseSurfaceImpl_SetColorKey,
4783 IWineD3DBaseSurfaceImpl_GetPitch,
4784 IWineD3DSurfaceImpl_SetMem,
4785 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4786 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4787 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4788 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4789 IWineD3DBaseSurfaceImpl_SetClipper,
4790 IWineD3DBaseSurfaceImpl_GetClipper,
4791 /* Internal use: */
4792 IWineD3DSurfaceImpl_AddDirtyRect,
4793 IWineD3DSurfaceImpl_LoadTexture,
4794 IWineD3DSurfaceImpl_BindTexture,
4795 IWineD3DSurfaceImpl_SaveSnapshot,
4796 IWineD3DSurfaceImpl_SetContainer,
4797 IWineD3DSurfaceImpl_GetGlDesc,
4798 IWineD3DSurfaceImpl_GetData,
4799 IWineD3DSurfaceImpl_SetFormat,
4800 IWineD3DSurfaceImpl_PrivateSetup,
4801 IWineD3DSurfaceImpl_ModifyLocation,
4802 IWineD3DSurfaceImpl_LoadLocation,
4803 IWineD3DSurfaceImpl_GetImplType,
4804 IWineD3DSurfaceImpl_DrawOverlay
4806 #undef GLINFO_LOCATION
4808 #define GLINFO_LOCATION device->adapter->gl_info
4809 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
4810 static void ffp_blit_free(IWineD3DDevice *iface) { }
4812 static HRESULT ffp_blit_set(IWineD3DDevice *iface, WINED3DFORMAT fmt, GLenum textype, UINT width, UINT height) {
4813 glEnable(textype);
4814 checkGLcall("glEnable(textype)");
4815 return WINED3D_OK;
4818 static void ffp_blit_unset(IWineD3DDevice *iface) {
4819 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4820 glDisable(GL_TEXTURE_2D);
4821 checkGLcall("glDisable(GL_TEXTURE_2D)");
4822 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP)) {
4823 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4824 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4826 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
4827 glDisable(GL_TEXTURE_RECTANGLE_ARB);
4828 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4832 static BOOL ffp_blit_conv_supported(WINED3DFORMAT fmt) {
4833 TRACE("Checking blit format support for format %s: [FAILED]\n", debug_d3dformat(fmt));
4834 return FALSE;
4837 const struct blit_shader ffp_blit = {
4838 ffp_blit_alloc,
4839 ffp_blit_free,
4840 ffp_blit_set,
4841 ffp_blit_unset,
4842 ffp_blit_conv_supported