wined3d: Make shader texture format fixups more generic.
[wine/testsucceed.git] / dlls / wined3d / surface.c
blobb34ab49bd1aaa925f85e0f8b718e4f716f3d7af3
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 WINE_DECLARE_DEBUG_CHANNEL(d3d);
35 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
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 const BYTE *src_data;
214 BYTE *dst_data;
215 UINT y;
217 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
218 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
219 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
221 * We're doing this...
223 * instead of boxing the texture :
224 * |<-texture width ->| -->pow2width| /\
225 * |111111111111111111| | |
226 * |222 Texture 222222| boxed empty | texture height
227 * |3333 Data 33333333| | |
228 * |444444444444444444| | \/
229 * ----------------------------------- |
230 * | boxed empty | boxed empty | pow2height
231 * | | | \/
232 * -----------------------------------
235 * we're repacking the data to the expected texture width
237 * |<-texture width ->| -->pow2width| /\
238 * |111111111111111111222222222222222| |
239 * |222333333333333333333444444444444| texture height
240 * |444444 | |
241 * | | \/
242 * | | |
243 * | empty | pow2height
244 * | | \/
245 * -----------------------------------
247 * == is the same as
249 * |<-texture width ->| /\
250 * |111111111111111111|
251 * |222222222222222222|texture height
252 * |333333333333333333|
253 * |444444444444444444| \/
254 * --------------------
256 * this also means that any references to allocatedMemory should work with the data as if were a
257 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
259 * internally the texture is still stored in a boxed format so any references to textureName will
260 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
262 * Performance should not be an issue, because applications normally do not lock the surfaces when
263 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
264 * and doesn't have to be re-read.
266 src_data = mem;
267 dst_data = This->resource.allocatedMemory;
268 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
269 for (y = 1 ; y < This->currentDesc.Height; y++) {
270 /* skip the first row */
271 src_data += src_pitch;
272 dst_data += dst_pitch;
273 memcpy(dst_data, src_data, dst_pitch);
276 HeapFree(GetProcessHeap(), 0, mem);
280 /* Surface has now been downloaded */
281 This->Flags |= SFLAG_INSYSMEM;
284 /* This call just uploads data, the caller is responsible for activating the
285 * right context and binding the correct texture. */
286 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
288 if(This->heightscale != 1.0 && This->heightscale != 0.0) height *= This->heightscale;
290 if (This->resource.format == WINED3DFMT_DXT1 ||
291 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
292 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5 ||
293 This->resource.format == WINED3DFMT_ATI2N) {
294 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
295 FIXME("Using DXT1/3/5 without advertized support\n");
296 } else {
297 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
298 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
299 * function uses glCompressedTexImage2D instead of the SubImage call
301 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
302 ENTER_GL();
304 if(This->Flags & SFLAG_PBO) {
305 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
306 checkGLcall("glBindBufferARB");
307 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
309 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
310 width, height, 0 /* border */, This->resource.size, NULL));
311 checkGLcall("glCompressedTexSubImage2D");
313 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
314 checkGLcall("glBindBufferARB");
315 } else {
316 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
317 width, height, 0 /* border */, This->resource.size, data));
318 checkGLcall("glCompressedTexSubImage2D");
320 LEAVE_GL();
322 } else {
323 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
324 ENTER_GL();
326 if(This->Flags & SFLAG_PBO) {
327 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
328 checkGLcall("glBindBufferARB");
329 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
331 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
332 checkGLcall("glTexSubImage2D");
334 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
335 checkGLcall("glBindBufferARB");
337 else {
338 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
339 checkGLcall("glTexSubImage2D");
342 LEAVE_GL();
346 /* This call just allocates the texture, the caller is responsible for
347 * activating the right context and binding the correct texture. */
348 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
349 BOOL enable_client_storage = FALSE;
350 const BYTE *mem = NULL;
352 if(This->heightscale != 1.0 && This->heightscale != 0.0) height *= This->heightscale;
354 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,
355 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
357 if (This->resource.format == WINED3DFMT_DXT1 ||
358 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
359 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5 ||
360 This->resource.format == WINED3DFMT_ATI2N) {
361 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
362 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
364 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
365 * once, unfortunately
367 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
368 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
369 This->Flags |= SFLAG_CLIENT;
370 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
371 ENTER_GL();
372 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
373 width, height, 0 /* border */, This->resource.size, mem));
374 LEAVE_GL();
377 return;
380 ENTER_GL();
382 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
383 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
384 /* In some cases we want to disable client storage.
385 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
386 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
387 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
388 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
389 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
391 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
392 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
393 This->Flags &= ~SFLAG_CLIENT;
394 enable_client_storage = TRUE;
395 } else {
396 This->Flags |= SFLAG_CLIENT;
398 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
399 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
401 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
404 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
405 checkGLcall("glTexImage2D");
407 if(enable_client_storage) {
408 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
409 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
411 LEAVE_GL();
413 This->Flags |= SFLAG_ALLOCATED;
416 /* In D3D the depth stencil dimensions have to be greater than or equal to the
417 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
418 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
419 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
420 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
421 renderbuffer_entry_t *entry;
422 GLuint renderbuffer = 0;
423 unsigned int src_width, src_height;
425 src_width = This->pow2Width;
426 src_height = This->pow2Height;
428 /* A depth stencil smaller than the render target is not valid */
429 if (width > src_width || height > src_height) return;
431 /* Remove any renderbuffer set if the sizes match */
432 if (width == src_width && height == src_height) {
433 This->current_renderbuffer = NULL;
434 return;
437 /* Look if we've already got a renderbuffer of the correct dimensions */
438 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
439 if (entry->width == width && entry->height == height) {
440 renderbuffer = entry->id;
441 This->current_renderbuffer = entry;
442 break;
446 if (!renderbuffer) {
447 const struct GlPixelFormatDesc *glDesc;
448 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
450 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
451 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
452 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glInternal, width, height));
454 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
455 entry->width = width;
456 entry->height = height;
457 entry->id = renderbuffer;
458 list_add_head(&This->renderbuffers, &entry->entry);
460 This->current_renderbuffer = entry;
463 checkGLcall("set_compatible_renderbuffer");
466 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
467 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
468 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
470 TRACE("(%p) : swapchain %p\n", This, swapchain);
472 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
473 TRACE("Returning GL_BACK\n");
474 return GL_BACK;
475 } else if (swapchain_impl->frontBuffer == iface) {
476 TRACE("Returning GL_FRONT\n");
477 return GL_FRONT;
480 FIXME("Higher back buffer, returning GL_BACK\n");
481 return GL_BACK;
484 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
486 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
487 ULONG ref = InterlockedDecrement(&This->resource.ref);
488 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
489 if (ref == 0) {
490 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
491 renderbuffer_entry_t *entry, *entry2;
492 TRACE("(%p) : cleaning up\n", This);
494 /* Need a context to destroy the texture. Use the currently active render target, but only if
495 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
496 * When destroying the primary rt, Uninit3D will activate a context before doing anything
498 if(device->render_targets && device->render_targets[0]) {
499 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
502 ENTER_GL();
503 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
504 TRACE("Deleting texture %d\n", This->glDescription.textureName);
505 glDeleteTextures(1, &This->glDescription.textureName);
508 if(This->Flags & SFLAG_PBO) {
509 /* Delete the PBO */
510 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
513 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
514 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
515 HeapFree(GetProcessHeap(), 0, entry);
517 LEAVE_GL();
519 if(This->Flags & SFLAG_DIBSECTION) {
520 /* Release the DC */
521 SelectObject(This->hDC, This->dib.holdbitmap);
522 DeleteDC(This->hDC);
523 /* Release the DIB section */
524 DeleteObject(This->dib.DIBsection);
525 This->dib.bitmap_data = NULL;
526 This->resource.allocatedMemory = NULL;
528 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
530 HeapFree(GetProcessHeap(), 0, This->palette9);
532 resource_cleanup((IWineD3DResource *)iface);
534 if(This->overlay_dest) {
535 list_remove(&This->overlay_entry);
538 TRACE("(%p) Released\n", This);
539 HeapFree(GetProcessHeap(), 0, This);
542 return ref;
545 /* ****************************************************
546 IWineD3DSurface IWineD3DResource parts follow
547 **************************************************** */
549 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface)
551 /* TODO: check for locks */
552 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
553 IWineD3DBaseTexture *baseTexture = NULL;
554 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
556 TRACE("(%p)Checking to see if the container is a base texture\n", This);
557 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
558 TRACE("Passing to container\n");
559 IWineD3DBaseTexture_PreLoad(baseTexture);
560 IWineD3DBaseTexture_Release(baseTexture);
561 } else {
562 TRACE("(%p) : About to load surface\n", This);
564 if(!device->isInDraw) {
565 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
568 if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8) {
569 if(palette9_changed(This)) {
570 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
571 /* TODO: This is not necessarily needed with hw palettized texture support */
572 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
573 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
574 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
578 IWineD3DSurface_LoadTexture(iface, FALSE);
580 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
581 /* Tell opengl to try and keep this texture in video ram (well mostly) */
582 GLclampf tmp;
583 tmp = 0.9f;
584 ENTER_GL();
585 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
586 LEAVE_GL();
589 return;
592 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
593 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
594 This->resource.allocatedMemory =
595 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
597 ENTER_GL();
598 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
599 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
600 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
601 checkGLcall("glGetBufferSubData");
602 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
603 checkGLcall("glDeleteBuffers");
604 LEAVE_GL();
606 This->pbo = 0;
607 This->Flags &= ~SFLAG_PBO;
610 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
611 IWineD3DBaseTexture *texture = NULL;
612 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
613 renderbuffer_entry_t *entry, *entry2;
614 TRACE("(%p)\n", iface);
616 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
617 /* Default pool resources are supposed to be destroyed before Reset is called.
618 * Implicit resources stay however. So this means we have an implicit render target
619 * or depth stencil. The content may be destroyed, but we still have to tear down
620 * opengl resources, so we cannot leave early.
622 * Put the most up to date surface location into the drawable. D3D-wise this content
623 * is undefined, so it would be nowhere, but that would make the location management
624 * more complicated. The drawable is a sane location, because if we mark sysmem or
625 * texture up to date, drawPrim will copy the uninitialized texture or sysmem to the
626 * uninitialized drawable. That's pointless and we'd have to allocate the texture /
627 * sysmem copy here.
629 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
630 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
631 } else {
632 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
634 } else {
635 /* Load the surface into system memory */
636 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
637 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
639 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
640 This->Flags &= ~SFLAG_ALLOCATED;
642 /* Destroy PBOs, but load them into real sysmem before */
643 if(This->Flags & SFLAG_PBO) {
644 surface_remove_pbo(This);
647 /* Destroy fbo render buffers. This is needed for implicit render targets, for
648 * all application-created targets the application has to release the surface
649 * before calling _Reset
651 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
652 ENTER_GL();
653 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
654 LEAVE_GL();
655 list_remove(&entry->entry);
656 HeapFree(GetProcessHeap(), 0, entry);
658 list_init(&This->renderbuffers);
659 This->current_renderbuffer = NULL;
661 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
662 * destroy it
664 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
665 if(!texture) {
666 ENTER_GL();
667 glDeleteTextures(1, &This->glDescription.textureName);
668 This->glDescription.textureName = 0;
669 LEAVE_GL();
670 } else {
671 IWineD3DBaseTexture_Release(texture);
673 return;
676 /* ******************************************************
677 IWineD3DSurface IWineD3DSurface parts follow
678 ****************************************************** */
680 static void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription)
682 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
683 TRACE("(%p) : returning %p\n", This, &This->glDescription);
684 *glDescription = &This->glDescription;
687 /* Read the framebuffer back into the surface */
688 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
689 IWineD3DSwapChainImpl *swapchain;
690 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
691 BYTE *mem;
692 GLint fmt;
693 GLint type;
694 BYTE *row, *top, *bottom;
695 int i;
696 BOOL bpp;
697 RECT local_rect;
698 BOOL srcIsUpsideDown;
700 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
701 static BOOL warned = FALSE;
702 if(!warned) {
703 ERR("The application tries to lock the render target, but render target locking is disabled\n");
704 warned = TRUE;
706 return;
709 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
710 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
711 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
712 * context->last_was_blit set on the unlock.
714 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
715 ENTER_GL();
717 /* Select the correct read buffer, and give some debug output.
718 * There is no need to keep track of the current read buffer or reset it, every part of the code
719 * that reads sets the read buffer as desired.
721 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
723 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
724 TRACE("Locking %#x buffer\n", buffer);
725 glReadBuffer(buffer);
726 checkGLcall("glReadBuffer");
728 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
729 srcIsUpsideDown = FALSE;
730 } else {
731 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
732 * Read from the back buffer
734 TRACE("Locking offscreen render target\n");
735 glReadBuffer(myDevice->offscreenBuffer);
736 srcIsUpsideDown = TRUE;
739 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
740 if(!rect) {
741 local_rect.left = 0;
742 local_rect.top = 0;
743 local_rect.right = This->currentDesc.Width;
744 local_rect.bottom = This->currentDesc.Height;
745 } else {
746 local_rect = *rect;
748 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
750 switch(This->resource.format)
752 case WINED3DFMT_P8:
754 if(primary_render_target_is_p8(myDevice)) {
755 /* In case of P8 render targets the index is stored in the alpha component */
756 fmt = GL_ALPHA;
757 type = GL_UNSIGNED_BYTE;
758 mem = dest;
759 bpp = This->bytesPerPixel;
760 } else {
761 /* GL can't return palettized data, so read ARGB pixels into a
762 * separate block of memory and convert them into palettized format
763 * in software. Slow, but if the app means to use palettized render
764 * targets and locks it...
766 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
767 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
768 * for the color channels when palettizing the colors.
770 fmt = GL_RGB;
771 type = GL_UNSIGNED_BYTE;
772 pitch *= 3;
773 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
774 if(!mem) {
775 ERR("Out of memory\n");
776 LEAVE_GL();
777 return;
779 bpp = This->bytesPerPixel * 3;
782 break;
784 default:
785 mem = dest;
786 fmt = This->glDescription.glFormat;
787 type = This->glDescription.glType;
788 bpp = This->bytesPerPixel;
791 if(This->Flags & SFLAG_PBO) {
792 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
793 checkGLcall("glBindBufferARB");
796 glReadPixels(local_rect.left, local_rect.top,
797 local_rect.right - local_rect.left,
798 local_rect.bottom - local_rect.top,
799 fmt, type, mem);
800 vcheckGLcall("glReadPixels");
802 if(This->Flags & SFLAG_PBO) {
803 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
804 checkGLcall("glBindBufferARB");
806 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
807 * to get a pointer to it and perform the flipping in software. This is a lot
808 * faster than calling glReadPixels for each line. In case we want more speed
809 * we should rerender it flipped in a FBO and read the data back from the FBO. */
810 if(!srcIsUpsideDown) {
811 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
812 checkGLcall("glBindBufferARB");
814 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
815 checkGLcall("glMapBufferARB");
819 /* TODO: Merge this with the palettization loop below for P8 targets */
820 if(!srcIsUpsideDown) {
821 UINT len, off;
822 /* glReadPixels returns the image upside down, and there is no way to prevent this.
823 Flip the lines in software */
824 len = (local_rect.right - local_rect.left) * bpp;
825 off = local_rect.left * bpp;
827 row = HeapAlloc(GetProcessHeap(), 0, len);
828 if(!row) {
829 ERR("Out of memory\n");
830 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
831 LEAVE_GL();
832 return;
835 top = mem + pitch * local_rect.top;
836 bottom = mem + pitch * ( local_rect.bottom - local_rect.top - 1);
837 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
838 memcpy(row, top + off, len);
839 memcpy(top + off, bottom + off, len);
840 memcpy(bottom + off, row, len);
841 top += pitch;
842 bottom -= pitch;
844 HeapFree(GetProcessHeap(), 0, row);
846 /* Unmap the temp PBO buffer */
847 if(This->Flags & SFLAG_PBO) {
848 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
849 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
853 LEAVE_GL();
855 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
856 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
857 * the same color but we have no choice.
858 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
860 if((This->resource.format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice)) {
861 const PALETTEENTRY *pal = NULL;
862 DWORD width = pitch / 3;
863 int x, y, c;
865 if(This->palette) {
866 pal = This->palette->palents;
867 } else {
868 ERR("Palette is missing, cannot perform inverse palette lookup\n");
869 HeapFree(GetProcessHeap(), 0, mem);
870 return ;
873 for(y = local_rect.top; y < local_rect.bottom; y++) {
874 for(x = local_rect.left; x < local_rect.right; x++) {
875 /* start lines pixels */
876 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
877 const BYTE *green = blue + 1;
878 const BYTE *red = green + 1;
880 for(c = 0; c < 256; c++) {
881 if(*red == pal[c].peRed &&
882 *green == pal[c].peGreen &&
883 *blue == pal[c].peBlue)
885 *((BYTE *) dest + y * width + x) = c;
886 break;
891 HeapFree(GetProcessHeap(), 0, mem);
895 /* Read the framebuffer contents into a texture */
896 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This)
898 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
899 IWineD3DSwapChainImpl *swapchain;
900 int bpp;
901 GLenum format, internal, type;
902 CONVERT_TYPES convert;
903 GLint prevRead;
905 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
907 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
908 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
909 * states in the stateblock, and no driver was found yet that had bugs in that regard.
911 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
912 surface_bind_and_dirtify(This);
914 ENTER_GL();
915 glGetIntegerv(GL_READ_BUFFER, &prevRead);
916 LEAVE_GL();
918 /* Select the correct read buffer, and give some debug output.
919 * There is no need to keep track of the current read buffer or reset it, every part of the code
920 * that reads sets the read buffer as desired.
922 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
924 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
925 TRACE("Locking %#x buffer\n", buffer);
927 ENTER_GL();
928 glReadBuffer(buffer);
929 checkGLcall("glReadBuffer");
930 LEAVE_GL();
932 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
933 } else {
934 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
935 * Read from the back buffer
937 TRACE("Locking offscreen render target\n");
938 ENTER_GL();
939 glReadBuffer(device->offscreenBuffer);
940 LEAVE_GL();
943 if(!(This->Flags & SFLAG_ALLOCATED)) {
944 surface_allocate_surface(This, internal, This->pow2Width,
945 This->pow2Height, format, type);
948 clear_unused_channels(This);
950 ENTER_GL();
951 /* If !SrcIsUpsideDown we should flip the surface.
952 * This can be done using glCopyTexSubImage2D but this
953 * is VERY slow, so don't do that. We should prevent
954 * this code from getting called in such cases or perhaps
955 * we can use FBOs */
957 glCopyTexSubImage2D(This->glDescription.target,
958 This->glDescription.level,
959 0, 0, 0, 0,
960 This->currentDesc.Width,
961 This->currentDesc.Height);
962 checkGLcall("glCopyTexSubImage2D");
964 glReadBuffer(prevRead);
965 vcheckGLcall("glReadBuffer");
967 LEAVE_GL();
968 TRACE("Updated target %d\n", This->glDescription.target);
971 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
972 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
973 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
974 * changed
976 if(!(This->Flags & SFLAG_DYNLOCK)) {
977 This->lockCount++;
978 /* MAXLOCKCOUNT is defined in wined3d_private.h */
979 if(This->lockCount > MAXLOCKCOUNT) {
980 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
981 This->Flags |= SFLAG_DYNLOCK;
985 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
986 * Also don't create a PBO for systemmem surfaces.
988 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
989 GLenum error;
990 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
992 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
993 ENTER_GL();
995 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
996 error = glGetError();
997 if(This->pbo == 0 || error != GL_NO_ERROR) {
998 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1001 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1003 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1004 checkGLcall("glBindBufferARB");
1006 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1007 checkGLcall("glBufferDataARB");
1009 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1010 checkGLcall("glBindBufferARB");
1012 /* We don't need the system memory anymore and we can't even use it for PBOs */
1013 if(!(This->Flags & SFLAG_CLIENT)) {
1014 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1015 This->resource.heapMemory = NULL;
1017 This->resource.allocatedMemory = NULL;
1018 This->Flags |= SFLAG_PBO;
1019 LEAVE_GL();
1020 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
1021 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1022 * or a pbo to map
1024 if(!This->resource.heapMemory) {
1025 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1027 This->resource.allocatedMemory =
1028 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1029 if(This->Flags & SFLAG_INSYSMEM) {
1030 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1035 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1036 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1037 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1038 IWineD3DSwapChain *swapchain = NULL;
1040 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1042 /* This is also done in the base class, but we have to verify this before loading any data from
1043 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1044 * may interfere, and all other bad things may happen
1046 if (This->Flags & SFLAG_LOCKED) {
1047 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1048 return WINED3DERR_INVALIDCALL;
1050 This->Flags |= SFLAG_LOCKED;
1052 if (!(This->Flags & SFLAG_LOCKABLE))
1054 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1057 if (Flags & WINED3DLOCK_DISCARD) {
1058 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1059 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1060 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1061 This->Flags |= SFLAG_INSYSMEM;
1062 goto lock_end;
1065 if (This->Flags & SFLAG_INSYSMEM) {
1066 TRACE("Local copy is up to date, not downloading data\n");
1067 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1068 goto lock_end;
1071 /* Now download the surface content from opengl
1072 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1073 * Offscreen targets which are not active at the moment or are higher targets(FBOs) can be locked with the texture path
1075 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1076 if(swapchain || iface == myDevice->render_targets[0]) {
1077 const RECT *pass_rect = pRect;
1079 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1080 * because most caller functions do not need that. So do that here
1082 if(pRect &&
1083 pRect->top == 0 &&
1084 pRect->left == 0 &&
1085 pRect->right == This->currentDesc.Width &&
1086 pRect->bottom == This->currentDesc.Height) {
1087 pass_rect = NULL;
1090 switch(wined3d_settings.rendertargetlock_mode) {
1091 case RTL_TEXDRAW:
1092 case RTL_TEXTEX:
1093 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1094 #if 0
1095 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1096 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1097 * This may be faster on some cards
1099 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1100 #endif
1101 /* drop through */
1103 case RTL_AUTO:
1104 case RTL_READDRAW:
1105 case RTL_READTEX:
1106 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1107 break;
1109 case RTL_DISABLE:
1110 break;
1112 if(swapchain) IWineD3DSwapChain_Release(swapchain);
1114 } else if(iface == myDevice->stencilBufferTarget) {
1115 /** the depth stencil in openGL has a format of GL_FLOAT
1116 * which should be good for WINED3DFMT_D16_LOCKABLE
1117 * and WINED3DFMT_D16
1118 * it is unclear what format the stencil buffer is in except.
1119 * 'Each index is converted to fixed point...
1120 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1121 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1122 * glReadPixels(This->lockedRect.left,
1123 * This->lockedRect.bottom - j - 1,
1124 * This->lockedRect.right - This->lockedRect.left,
1125 * 1,
1126 * GL_DEPTH_COMPONENT,
1127 * type,
1128 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1130 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1131 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1132 * none of that is the case the problem is not in this function :-)
1133 ********************************************/
1134 FIXME("Depth stencil locking not supported yet\n");
1135 } else {
1136 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1137 TRACE("locking an ordinary surface\n");
1138 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1141 lock_end:
1142 if(This->Flags & SFLAG_PBO) {
1143 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1144 ENTER_GL();
1145 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1146 checkGLcall("glBindBufferARB");
1148 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1149 if(This->resource.allocatedMemory) {
1150 ERR("The surface already has PBO memory allocated!\n");
1153 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1154 checkGLcall("glMapBufferARB");
1156 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1157 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1158 checkGLcall("glBindBufferARB");
1160 LEAVE_GL();
1163 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1164 /* Don't dirtify */
1165 } else {
1166 IWineD3DBaseTexture *pBaseTexture;
1168 * Dirtify on lock
1169 * as seen in msdn docs
1171 IWineD3DSurface_AddDirtyRect(iface, pRect);
1173 /** Dirtify Container if needed */
1174 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1175 TRACE("Making container dirty\n");
1176 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1177 IWineD3DBaseTexture_Release(pBaseTexture);
1178 } else {
1179 TRACE("Surface is standalone, no need to dirty the container\n");
1183 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1186 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1187 GLint prev_store;
1188 GLint prev_rasterpos[4];
1189 GLint skipBytes = 0;
1190 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1191 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1192 IWineD3DSwapChainImpl *swapchain;
1194 /* Activate the correct context for the render target */
1195 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1196 ENTER_GL();
1198 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
1199 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1200 TRACE("Unlocking %#x buffer\n", buffer);
1201 glDrawBuffer(buffer);
1202 checkGLcall("glDrawBuffer");
1204 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1205 } else {
1206 /* Primary offscreen render target */
1207 TRACE("Offscreen render target\n");
1208 glDrawBuffer(myDevice->offscreenBuffer);
1209 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1212 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1213 vcheckGLcall("glIntegerv");
1214 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1215 vcheckGLcall("glIntegerv");
1216 glPixelZoom(1.0, -1.0);
1217 vcheckGLcall("glPixelZoom");
1219 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1220 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1221 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1223 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1224 vcheckGLcall("glRasterPos2f");
1226 /* Some drivers(radeon dri, others?) don't like exceptions during
1227 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1228 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1229 * catch to put the dib section in InSync mode, which leads to a crash
1230 * and a blocked x server on my radeon card.
1232 * The following lines read the dib section so it is put in InSync mode
1233 * before glDrawPixels is called and the crash is prevented. There won't
1234 * be any interfering gdi accesses, because UnlockRect is called from
1235 * ReleaseDC, and the app won't use the dc any more afterwards.
1237 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1238 volatile BYTE read;
1239 read = This->resource.allocatedMemory[0];
1242 if(This->Flags & SFLAG_PBO) {
1243 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1244 checkGLcall("glBindBufferARB");
1247 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1248 if(This->Flags & SFLAG_LOCKED) {
1249 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1250 (This->lockedRect.bottom - This->lockedRect.top)-1,
1251 fmt, type,
1252 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1253 checkGLcall("glDrawPixels");
1254 } else {
1255 glDrawPixels(This->currentDesc.Width,
1256 This->currentDesc.Height,
1257 fmt, type, mem);
1258 checkGLcall("glDrawPixels");
1261 if(This->Flags & SFLAG_PBO) {
1262 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1263 checkGLcall("glBindBufferARB");
1266 glPixelZoom(1.0,1.0);
1267 vcheckGLcall("glPixelZoom");
1269 glRasterPos3iv(&prev_rasterpos[0]);
1270 vcheckGLcall("glRasterPos3iv");
1272 /* Reset to previous pack row length */
1273 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1274 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1276 if(!swapchain) {
1277 glDrawBuffer(myDevice->offscreenBuffer);
1278 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1279 } else if(swapchain->backBuffer) {
1280 glDrawBuffer(GL_BACK);
1281 checkGLcall("glDrawBuffer(GL_BACK)");
1282 } else {
1283 glDrawBuffer(GL_FRONT);
1284 checkGLcall("glDrawBuffer(GL_FRONT)");
1286 LEAVE_GL();
1288 return;
1291 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1292 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1293 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1294 IWineD3DSwapChainImpl *swapchain = NULL;
1295 BOOL fullsurface;
1297 if (!(This->Flags & SFLAG_LOCKED)) {
1298 WARN("trying to Unlock an unlocked surf@%p\n", This);
1299 return WINED3DERR_INVALIDCALL;
1302 if (This->Flags & SFLAG_PBO) {
1303 TRACE("Freeing PBO memory\n");
1304 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1305 ENTER_GL();
1306 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1307 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1308 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1309 checkGLcall("glUnmapBufferARB");
1310 LEAVE_GL();
1311 This->resource.allocatedMemory = NULL;
1314 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1316 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1317 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1318 goto unlock_end;
1321 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1322 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1323 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1325 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1326 static BOOL warned = FALSE;
1327 if(!warned) {
1328 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1329 warned = TRUE;
1331 goto unlock_end;
1334 if(This->dirtyRect.left == 0 &&
1335 This->dirtyRect.top == 0 &&
1336 This->dirtyRect.right == This->currentDesc.Width &&
1337 This->dirtyRect.bottom == This->currentDesc.Height) {
1338 fullsurface = TRUE;
1339 } else {
1340 /* TODO: Proper partial rectangle tracking */
1341 fullsurface = FALSE;
1342 This->Flags |= SFLAG_INSYSMEM;
1345 switch(wined3d_settings.rendertargetlock_mode) {
1346 case RTL_READTEX:
1347 case RTL_TEXTEX:
1348 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1349 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1350 /* drop through */
1352 case RTL_AUTO:
1353 case RTL_READDRAW:
1354 case RTL_TEXDRAW:
1355 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1356 break;
1359 if(!fullsurface) {
1360 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1361 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1362 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1363 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1364 * not fully up to date because only a subrectangle was read in LockRect.
1366 This->Flags &= ~SFLAG_INSYSMEM;
1367 This->Flags |= SFLAG_INDRAWABLE;
1370 This->dirtyRect.left = This->currentDesc.Width;
1371 This->dirtyRect.top = This->currentDesc.Height;
1372 This->dirtyRect.right = 0;
1373 This->dirtyRect.bottom = 0;
1374 } else if(iface == myDevice->stencilBufferTarget) {
1375 FIXME("Depth Stencil buffer locking is not implemented\n");
1376 } else {
1377 /* The rest should be a normal texture */
1378 IWineD3DBaseTextureImpl *impl;
1379 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1380 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1381 * states need resetting
1383 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1384 if(impl->baseTexture.bindCount) {
1385 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1387 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1391 unlock_end:
1392 This->Flags &= ~SFLAG_LOCKED;
1393 memset(&This->lockedRect, 0, sizeof(RECT));
1395 /* Overlays have to be redrawn manually after changes with the GL implementation */
1396 if(This->overlay_dest) {
1397 IWineD3DSurface_DrawOverlay(iface);
1399 return WINED3D_OK;
1402 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
1404 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1405 WINED3DLOCKED_RECT lock;
1406 HRESULT hr;
1407 RGBQUAD col[256];
1409 TRACE("(%p)->(%p)\n",This,pHDC);
1411 if(This->Flags & SFLAG_USERPTR) {
1412 ERR("Not supported on surfaces with an application-provided surfaces\n");
1413 return WINEDDERR_NODC;
1416 /* Give more detailed info for ddraw */
1417 if (This->Flags & SFLAG_DCINUSE)
1418 return WINEDDERR_DCALREADYCREATED;
1420 /* Can't GetDC if the surface is locked */
1421 if (This->Flags & SFLAG_LOCKED)
1422 return WINED3DERR_INVALIDCALL;
1424 /* According to Direct3D9 docs, only these formats are supported */
1425 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1426 if (This->resource.format != WINED3DFMT_R5G6B5 &&
1427 This->resource.format != WINED3DFMT_X1R5G5B5 &&
1428 This->resource.format != WINED3DFMT_R8G8B8 &&
1429 This->resource.format != WINED3DFMT_X8R8G8B8) return WINED3DERR_INVALIDCALL;
1432 memset(&lock, 0, sizeof(lock)); /* To be sure */
1434 /* Create a DIB section if there isn't a hdc yet */
1435 if(!This->hDC) {
1436 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1437 if(This->Flags & SFLAG_CLIENT) {
1438 IWineD3DSurface_PreLoad(iface);
1441 /* Use the dib section from now on if we are not using a PBO */
1442 if(!(This->Flags & SFLAG_PBO))
1443 This->resource.allocatedMemory = This->dib.bitmap_data;
1446 /* Lock the surface */
1447 hr = IWineD3DSurface_LockRect(iface,
1448 &lock,
1449 NULL,
1452 if(This->Flags & SFLAG_PBO) {
1453 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1454 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1457 if(FAILED(hr)) {
1458 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1459 /* keep the dib section */
1460 return hr;
1463 if(This->resource.format == WINED3DFMT_P8 ||
1464 This->resource.format == WINED3DFMT_A8P8) {
1465 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1466 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1467 unsigned int n;
1468 const PALETTEENTRY *pal = NULL;
1470 if(This->palette) {
1471 pal = This->palette->palents;
1472 } else {
1473 IWineD3DSurfaceImpl *dds_primary;
1474 IWineD3DSwapChainImpl *swapchain;
1475 swapchain = (IWineD3DSwapChainImpl *)This->resource.wineD3DDevice->swapchains[0];
1476 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1477 if (dds_primary && dds_primary->palette)
1478 pal = dds_primary->palette->palents;
1481 if (pal) {
1482 for (n=0; n<256; n++) {
1483 col[n].rgbRed = pal[n].peRed;
1484 col[n].rgbGreen = pal[n].peGreen;
1485 col[n].rgbBlue = pal[n].peBlue;
1486 col[n].rgbReserved = 0;
1488 SetDIBColorTable(This->hDC, 0, 256, col);
1492 *pHDC = This->hDC;
1493 TRACE("returning %p\n",*pHDC);
1494 This->Flags |= SFLAG_DCINUSE;
1496 return WINED3D_OK;
1499 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
1501 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1503 TRACE("(%p)->(%p)\n",This,hDC);
1505 if (!(This->Flags & SFLAG_DCINUSE))
1506 return WINED3DERR_INVALIDCALL;
1508 if (This->hDC !=hDC) {
1509 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1510 return WINED3DERR_INVALIDCALL;
1513 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1514 /* Copy the contents of the DIB over to the PBO */
1515 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1518 /* we locked first, so unlock now */
1519 IWineD3DSurface_UnlockRect(iface);
1521 This->Flags &= ~SFLAG_DCINUSE;
1523 return WINED3D_OK;
1526 /* ******************************************************
1527 IWineD3DSurface Internal (No mapping to directx api) parts follow
1528 ****************************************************** */
1530 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) {
1531 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1532 const struct GlPixelFormatDesc *glDesc;
1533 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1534 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1536 /* Default values: From the surface */
1537 *format = glDesc->glFormat;
1538 *type = glDesc->glType;
1539 *convert = NO_CONVERSION;
1540 *target_bpp = This->bytesPerPixel;
1542 if(srgb_mode) {
1543 *internal = glDesc->glGammaInternal;
1544 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1545 *internal = glDesc->rtInternal;
1546 } else {
1547 *internal = glDesc->glInternal;
1550 /* Ok, now look if we have to do any conversion */
1551 switch(This->resource.format) {
1552 case WINED3DFMT_P8:
1553 /* ****************
1554 Paletted Texture
1555 **************** */
1557 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1558 * of the two is available make sure texturing is requested as neither of the two works in
1559 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1560 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1561 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1562 * conflicts with this.
1564 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) ||
1565 (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) &&
1566 device->render_targets &&
1567 This == (IWineD3DSurfaceImpl*)device->render_targets[0])) ||
1568 colorkey_active || !use_texturing ) {
1569 *format = GL_RGBA;
1570 *internal = GL_RGBA;
1571 *type = GL_UNSIGNED_BYTE;
1572 *target_bpp = 4;
1573 if(colorkey_active) {
1574 *convert = CONVERT_PALETTED_CK;
1575 } else {
1576 *convert = CONVERT_PALETTED;
1579 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1580 *format = GL_ALPHA;
1581 *internal = GL_RGBA;
1582 *type = GL_UNSIGNED_BYTE;
1583 *target_bpp = 1;
1586 break;
1588 case WINED3DFMT_R3G3B2:
1589 /* **********************
1590 GL_UNSIGNED_BYTE_3_3_2
1591 ********************** */
1592 if (colorkey_active) {
1593 /* This texture format will never be used.. So do not care about color keying
1594 up until the point in time it will be needed :-) */
1595 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1597 break;
1599 case WINED3DFMT_R5G6B5:
1600 if (colorkey_active) {
1601 *convert = CONVERT_CK_565;
1602 *format = GL_RGBA;
1603 *internal = GL_RGBA;
1604 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1606 break;
1608 case WINED3DFMT_X1R5G5B5:
1609 if (colorkey_active) {
1610 *convert = CONVERT_CK_5551;
1611 *format = GL_BGRA;
1612 *internal = GL_RGBA;
1613 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1615 break;
1617 case WINED3DFMT_R8G8B8:
1618 if (colorkey_active) {
1619 *convert = CONVERT_CK_RGB24;
1620 *format = GL_RGBA;
1621 *internal = GL_RGBA;
1622 *type = GL_UNSIGNED_INT_8_8_8_8;
1623 *target_bpp = 4;
1625 break;
1627 case WINED3DFMT_X8R8G8B8:
1628 if (colorkey_active) {
1629 *convert = CONVERT_RGB32_888;
1630 *format = GL_RGBA;
1631 *internal = GL_RGBA;
1632 *type = GL_UNSIGNED_INT_8_8_8_8;
1634 break;
1636 case WINED3DFMT_V8U8:
1637 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1638 *convert = CONVERT_V8U8;
1639 *format = GL_BGR;
1640 *internal = GL_RGB8;
1641 *type = GL_UNSIGNED_BYTE;
1642 *target_bpp = 3;
1643 break;
1645 case WINED3DFMT_L6V5U5:
1646 *convert = CONVERT_L6V5U5;
1647 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1648 *target_bpp = 3;
1649 /* Use format and types from table */
1650 } else {
1651 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1652 *target_bpp = 2;
1653 *format = GL_RGB;
1654 *internal = GL_RGB5;
1655 *type = GL_UNSIGNED_SHORT_5_6_5;
1657 break;
1659 case WINED3DFMT_X8L8V8U8:
1660 *convert = CONVERT_X8L8V8U8;
1661 *target_bpp = 4;
1662 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1663 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1664 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1665 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1666 * the needed type and format parameter, so the internal format contains a
1667 * 4th component, which is returned as alpha
1669 } else {
1670 *format = GL_BGRA;
1671 *internal = GL_RGB8;
1672 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1674 break;
1676 case WINED3DFMT_Q8W8V8U8:
1677 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1678 *convert = CONVERT_Q8W8V8U8;
1679 *format = GL_BGRA;
1680 *internal = GL_RGBA8;
1681 *type = GL_UNSIGNED_BYTE;
1682 *target_bpp = 4;
1683 break;
1685 case WINED3DFMT_V16U16:
1686 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1687 *convert = CONVERT_V16U16;
1688 *format = GL_BGR;
1689 *internal = GL_RGB16_EXT;
1690 *type = GL_UNSIGNED_SHORT;
1691 *target_bpp = 6;
1692 break;
1694 case WINED3DFMT_A4L4:
1695 /* A4L4 exists as an internal gl format, but for some reason there is not
1696 * format+type combination to load it. Thus convert it to A8L8, then load it
1697 * with A4L4 internal, but A8L8 format+type
1699 *convert = CONVERT_A4L4;
1700 *format = GL_LUMINANCE_ALPHA;
1701 *internal = GL_LUMINANCE4_ALPHA4;
1702 *type = GL_UNSIGNED_BYTE;
1703 *target_bpp = 2;
1704 break;
1706 case WINED3DFMT_R32F:
1707 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1708 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1709 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1710 * 1.0 instead.
1712 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1714 *convert = CONVERT_R32F;
1715 *format = GL_RGB;
1716 *internal = GL_RGB32F_ARB;
1717 *type = GL_FLOAT;
1718 *target_bpp = 12;
1719 break;
1721 case WINED3DFMT_R16F:
1722 /* Similar to R32F */
1723 *convert = CONVERT_R16F;
1724 *format = GL_RGB;
1725 *internal = GL_RGB16F_ARB;
1726 *type = GL_HALF_FLOAT_ARB;
1727 *target_bpp = 6;
1728 break;
1730 case WINED3DFMT_G16R16:
1731 *convert = CONVERT_G16R16;
1732 *format = GL_RGB;
1733 *internal = GL_RGB16_EXT;
1734 *type = GL_UNSIGNED_SHORT;
1735 *target_bpp = 6;
1736 break;
1738 default:
1739 break;
1742 return WINED3D_OK;
1745 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
1746 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
1748 const BYTE *source;
1749 BYTE *dest;
1750 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1752 switch (convert) {
1753 case NO_CONVERSION:
1755 memcpy(dst, src, pitch * height);
1756 break;
1758 case CONVERT_PALETTED:
1759 case CONVERT_PALETTED_CK:
1761 IWineD3DPaletteImpl* pal = This->palette;
1762 BYTE table[256][4];
1763 unsigned int x, y;
1765 if( pal == NULL) {
1766 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1769 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1771 for (y = 0; y < height; y++)
1773 source = src + pitch * y;
1774 dest = dst + outpitch * y;
1775 /* This is an 1 bpp format, using the width here is fine */
1776 for (x = 0; x < width; x++) {
1777 BYTE color = *source++;
1778 *dest++ = table[color][0];
1779 *dest++ = table[color][1];
1780 *dest++ = table[color][2];
1781 *dest++ = table[color][3];
1785 break;
1787 case CONVERT_CK_565:
1789 /* Converting the 565 format in 5551 packed to emulate color-keying.
1791 Note : in all these conversion, it would be best to average the averaging
1792 pixels to get the color of the pixel that will be color-keyed to
1793 prevent 'color bleeding'. This will be done later on if ever it is
1794 too visible.
1796 Note2: Nvidia documents say that their driver does not support alpha + color keying
1797 on the same surface and disables color keying in such a case
1799 unsigned int x, y;
1800 const WORD *Source;
1801 WORD *Dest;
1803 TRACE("Color keyed 565\n");
1805 for (y = 0; y < height; y++) {
1806 Source = (const WORD *)(src + y * pitch);
1807 Dest = (WORD *) (dst + y * outpitch);
1808 for (x = 0; x < width; x++ ) {
1809 WORD color = *Source++;
1810 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1811 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1812 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1813 *Dest |= 0x0001;
1815 Dest++;
1819 break;
1821 case CONVERT_CK_5551:
1823 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1824 unsigned int x, y;
1825 const WORD *Source;
1826 WORD *Dest;
1827 TRACE("Color keyed 5551\n");
1828 for (y = 0; y < height; y++) {
1829 Source = (const WORD *)(src + y * pitch);
1830 Dest = (WORD *) (dst + y * outpitch);
1831 for (x = 0; x < width; x++ ) {
1832 WORD color = *Source++;
1833 *Dest = color;
1834 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1835 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1836 *Dest |= (1 << 15);
1838 else {
1839 *Dest &= ~(1 << 15);
1841 Dest++;
1845 break;
1847 case CONVERT_CK_RGB24:
1849 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
1850 unsigned int x, y;
1851 for (y = 0; y < height; y++)
1853 source = src + pitch * y;
1854 dest = dst + outpitch * y;
1855 for (x = 0; x < width; x++) {
1856 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
1857 DWORD dstcolor = color << 8;
1858 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1859 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1860 dstcolor |= 0xff;
1862 *(DWORD*)dest = dstcolor;
1863 source += 3;
1864 dest += 4;
1868 break;
1870 case CONVERT_RGB32_888:
1872 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
1873 unsigned int x, y;
1874 for (y = 0; y < height; y++)
1876 source = src + pitch * y;
1877 dest = dst + outpitch * y;
1878 for (x = 0; x < width; x++) {
1879 DWORD color = 0xffffff & *(const DWORD*)source;
1880 DWORD dstcolor = color << 8;
1881 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1882 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1883 dstcolor |= 0xff;
1885 *(DWORD*)dest = dstcolor;
1886 source += 4;
1887 dest += 4;
1891 break;
1893 case CONVERT_V8U8:
1895 unsigned int x, y;
1896 const short *Source;
1897 unsigned char *Dest;
1898 for(y = 0; y < height; y++) {
1899 Source = (const short *)(src + y * pitch);
1900 Dest = dst + y * outpitch;
1901 for (x = 0; x < width; x++ ) {
1902 long color = (*Source++);
1903 /* B */ Dest[0] = 0xff;
1904 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1905 /* R */ Dest[2] = (color) + 128; /* U */
1906 Dest += 3;
1909 break;
1912 case CONVERT_V16U16:
1914 unsigned int x, y;
1915 const DWORD *Source;
1916 unsigned short *Dest;
1917 for(y = 0; y < height; y++) {
1918 Source = (const DWORD *)(src + y * pitch);
1919 Dest = (unsigned short *) (dst + y * outpitch);
1920 for (x = 0; x < width; x++ ) {
1921 DWORD color = (*Source++);
1922 /* B */ Dest[0] = 0xffff;
1923 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1924 /* R */ Dest[2] = (color ) + 32768; /* U */
1925 Dest += 3;
1928 break;
1931 case CONVERT_Q8W8V8U8:
1933 unsigned int x, y;
1934 const DWORD *Source;
1935 unsigned char *Dest;
1936 for(y = 0; y < height; y++) {
1937 Source = (const DWORD *)(src + y * pitch);
1938 Dest = dst + y * outpitch;
1939 for (x = 0; x < width; x++ ) {
1940 long color = (*Source++);
1941 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1942 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1943 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1944 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1945 Dest += 4;
1948 break;
1951 case CONVERT_L6V5U5:
1953 unsigned int x, y;
1954 const WORD *Source;
1955 unsigned char *Dest;
1957 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1958 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1959 * fixed function and shaders without further conversion once the surface is
1960 * loaded
1962 for(y = 0; y < height; y++) {
1963 Source = (const WORD *)(src + y * pitch);
1964 Dest = dst + y * outpitch;
1965 for (x = 0; x < width; x++ ) {
1966 short color = (*Source++);
1967 unsigned char l = ((color >> 10) & 0xfc);
1968 char v = ((color >> 5) & 0x3e);
1969 char u = ((color ) & 0x1f);
1971 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1972 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1973 * shift. GL reads a signed value and converts it into an unsigned value.
1975 /* M */ Dest[2] = l << 1;
1977 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1978 * from 5 bit values to 8 bit values.
1980 /* V */ Dest[1] = v << 3;
1981 /* U */ Dest[0] = u << 3;
1982 Dest += 3;
1985 } else {
1986 for(y = 0; y < height; y++) {
1987 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1988 Source = (const WORD *)(src + y * pitch);
1989 for (x = 0; x < width; x++ ) {
1990 short color = (*Source++);
1991 unsigned char l = ((color >> 10) & 0xfc);
1992 short v = ((color >> 5) & 0x3e);
1993 short u = ((color ) & 0x1f);
1994 short v_conv = v + 16;
1995 short u_conv = u + 16;
1997 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1998 Dest_s += 1;
2002 break;
2005 case CONVERT_X8L8V8U8:
2007 unsigned int x, y;
2008 const DWORD *Source;
2009 unsigned char *Dest;
2011 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2012 /* This implementation works with the fixed function pipeline and shaders
2013 * without further modification after converting the surface.
2015 for(y = 0; y < height; y++) {
2016 Source = (const DWORD *)(src + y * pitch);
2017 Dest = dst + y * outpitch;
2018 for (x = 0; x < width; x++ ) {
2019 long color = (*Source++);
2020 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2021 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2022 /* U */ Dest[0] = (color & 0xff); /* U */
2023 /* I */ Dest[3] = 255; /* X */
2024 Dest += 4;
2027 } else {
2028 /* Doesn't work correctly with the fixed function pipeline, but can work in
2029 * shaders if the shader is adjusted. (There's no use for this format in gl's
2030 * standard fixed function pipeline anyway).
2032 for(y = 0; y < height; y++) {
2033 Source = (const DWORD *)(src + y * pitch);
2034 Dest = dst + y * outpitch;
2035 for (x = 0; x < width; x++ ) {
2036 long color = (*Source++);
2037 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2038 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2039 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2040 Dest += 4;
2044 break;
2047 case CONVERT_A4L4:
2049 unsigned int x, y;
2050 const unsigned char *Source;
2051 unsigned char *Dest;
2052 for(y = 0; y < height; y++) {
2053 Source = src + y * pitch;
2054 Dest = dst + y * outpitch;
2055 for (x = 0; x < width; x++ ) {
2056 unsigned char color = (*Source++);
2057 /* A */ Dest[1] = (color & 0xf0) << 0;
2058 /* L */ Dest[0] = (color & 0x0f) << 4;
2059 Dest += 2;
2062 break;
2065 case CONVERT_R32F:
2067 unsigned int x, y;
2068 const float *Source;
2069 float *Dest;
2070 for(y = 0; y < height; y++) {
2071 Source = (const float *)(src + y * pitch);
2072 Dest = (float *) (dst + y * outpitch);
2073 for (x = 0; x < width; x++ ) {
2074 float color = (*Source++);
2075 Dest[0] = color;
2076 Dest[1] = 1.0;
2077 Dest[2] = 1.0;
2078 Dest += 3;
2081 break;
2084 case CONVERT_R16F:
2086 unsigned int x, y;
2087 const WORD *Source;
2088 WORD *Dest;
2089 const WORD one = 0x3c00;
2090 for(y = 0; y < height; y++) {
2091 Source = (const WORD *)(src + y * pitch);
2092 Dest = (WORD *) (dst + y * outpitch);
2093 for (x = 0; x < width; x++ ) {
2094 WORD color = (*Source++);
2095 Dest[0] = color;
2096 Dest[1] = one;
2097 Dest[2] = one;
2098 Dest += 3;
2101 break;
2104 case CONVERT_G16R16:
2106 unsigned int x, y;
2107 const WORD *Source;
2108 WORD *Dest;
2110 for(y = 0; y < height; y++) {
2111 Source = (const WORD *)(src + y * pitch);
2112 Dest = (WORD *) (dst + y * outpitch);
2113 for (x = 0; x < width; x++ ) {
2114 WORD green = (*Source++);
2115 WORD red = (*Source++);
2116 Dest[0] = green;
2117 Dest[1] = red;
2118 Dest[2] = 0xffff;
2119 Dest += 3;
2122 break;
2125 default:
2126 ERR("Unsupported conversation type %d\n", convert);
2128 return WINED3D_OK;
2131 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2132 IWineD3DPaletteImpl* pal = This->palette;
2133 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2134 BOOL index_in_alpha = FALSE;
2135 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2136 unsigned int i;
2138 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2139 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2140 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2141 * duplicate entries. Store the color key in the unused alpha component to speed the
2142 * download up and to make conversion unneeded. */
2143 index_in_alpha = primary_render_target_is_p8(device);
2145 if (pal == NULL) {
2146 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2147 if(dxVersion <= 7) {
2148 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2149 if(index_in_alpha) {
2150 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2151 there's no palette at this time. */
2152 for (i = 0; i < 256; i++) table[i][3] = i;
2154 } else {
2155 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2156 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2157 capability flag is present (wine does advertise this capability) */
2158 for (i = 0; i < 256; i++) {
2159 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2160 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2161 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2162 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2165 } else {
2166 TRACE("Using surface palette %p\n", pal);
2167 /* Get the surface's palette */
2168 for (i = 0; i < 256; i++) {
2169 table[i][0] = pal->palents[i].peRed;
2170 table[i][1] = pal->palents[i].peGreen;
2171 table[i][2] = pal->palents[i].peBlue;
2173 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2174 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2175 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2176 of pixels that should be masked away is set to 0. */
2177 if(index_in_alpha) {
2178 table[i][3] = i;
2179 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2180 table[i][3] = 0x00;
2181 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2182 table[i][3] = pal->palents[i].peFlags;
2183 } else {
2184 table[i][3] = 0xFF;
2190 /* This function is used in case of 8bit paletted textures to upload the palette.
2191 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2192 extensions like ATI_fragment_shaders is possible.
2194 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2195 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2196 BYTE table[256][4];
2197 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2199 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2201 /* Try to use the paletted texture extension */
2202 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2204 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2205 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2207 else
2209 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2210 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2211 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2213 /* Create the fragment program if we don't have it */
2214 if(!device->paletteConversionShader)
2216 const char *fragment_palette_conversion =
2217 "!!ARBfp1.0\n"
2218 "TEMP index;\n"
2219 /* { 255/256, 0.5/255*255/256, 0, 0 } */
2220 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n"
2221 /* The alpha-component contains the palette index */
2222 "TEX index, fragment.texcoord[0], texture[0], 2D;\n"
2223 /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2224 "MAD index.a, index.a, constants.x, constants.y;\n"
2225 /* Use the alpha-component as an index in the palette to get the final color */
2226 "TEX result.color, index.a, texture[1], 1D;\n"
2227 "END";
2229 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2230 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2231 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2232 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2233 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2236 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2237 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2239 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2240 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2242 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2243 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2244 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2245 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2247 /* Switch back to unit 0 in which the 2D texture will be stored. */
2248 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2250 /* Rebind the texture because it isn't bound anymore */
2251 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2255 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2256 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2258 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2259 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2260 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2262 return FALSE;
2265 if(This->palette9) {
2266 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2267 return FALSE;
2269 } else {
2270 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2272 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2273 return TRUE;
2276 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2277 GLboolean oldwrite[4];
2279 /* Some formats have only some color channels, and the others are 1.0.
2280 * since our rendering renders to all channels, and those pixel formats
2281 * are emulated by using a full texture with the other channels set to 1.0
2282 * manually, clear the unused channels.
2284 * This could be done with hacking colorwriteenable to mask the colors,
2285 * but before drawing the buffer would have to be cleared too, so there's
2286 * no gain in that
2288 switch(This->resource.format) {
2289 case WINED3DFMT_R16F:
2290 case WINED3DFMT_R32F:
2291 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2292 /* Do not activate a context, the correct drawable is active already
2293 * though just the read buffer is set, make sure to have the correct draw
2294 * buffer too
2296 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2297 glDisable(GL_SCISSOR_TEST);
2298 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2299 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2300 glClearColor(0.0, 1.0, 1.0, 1.0);
2301 glClear(GL_COLOR_BUFFER_BIT);
2302 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2303 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2304 checkGLcall("Unused channel clear\n");
2305 break;
2307 default: break;
2311 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2312 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2314 if (!(This->Flags & SFLAG_INTEXTURE)) {
2315 TRACE("Reloading because surface is dirty\n");
2316 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2317 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2318 /* Reload: vice versa OR */
2319 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2320 /* Also reload: Color key is active AND the color key has changed */
2321 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2322 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2323 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2324 TRACE("Reloading because of color keying\n");
2325 /* To perform the color key conversion we need a sysmem copy of
2326 * the surface. Make sure we have it
2329 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2330 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2331 /* TODO: This is not necessarily needed with hw palettized texture support */
2332 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2333 } else {
2334 TRACE("surface is already in texture\n");
2335 return WINED3D_OK;
2338 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2339 * These resources are not bound by device size or format restrictions. Because of this,
2340 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2341 * However, these resources can always be created, locked, and copied.
2343 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2345 FIXME("(%p) Operation not supported for scratch textures\n",This);
2346 return WINED3DERR_INVALIDCALL;
2349 This->srgb = srgb_mode;
2350 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2352 #if 0
2354 static unsigned int gen = 0;
2355 char buffer[4096];
2356 ++gen;
2357 if ((gen % 10) == 0) {
2358 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2359 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2362 * debugging crash code
2363 if (gen == 250) {
2364 void** test = NULL;
2365 *test = 0;
2369 #endif
2371 if (!(This->Flags & SFLAG_DONOTFREE)) {
2372 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2373 This->resource.allocatedMemory = NULL;
2374 This->resource.heapMemory = NULL;
2375 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2378 return WINED3D_OK;
2381 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2382 /* TODO: check for locks */
2383 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2384 IWineD3DBaseTexture *baseTexture = NULL;
2385 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2387 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2388 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2389 TRACE("Passing to container\n");
2390 IWineD3DBaseTexture_BindTexture(baseTexture);
2391 IWineD3DBaseTexture_Release(baseTexture);
2392 } else {
2393 TRACE("(%p) : Binding surface\n", This);
2395 if(!device->isInDraw) {
2396 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2399 ENTER_GL();
2401 if (!This->glDescription.level) {
2402 if (!This->glDescription.textureName) {
2403 glGenTextures(1, &This->glDescription.textureName);
2404 checkGLcall("glGenTextures");
2405 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
2407 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2408 checkGLcall("glBindTexture");
2409 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2410 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2411 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2412 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2413 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2414 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2415 glTexParameteri(This->glDescription.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2416 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2417 glTexParameteri(This->glDescription.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2418 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2420 /* This is where we should be reducing the amount of GLMemoryUsed */
2421 } else if (This->glDescription.textureName) {
2422 /* Mipmap surfaces should have a base texture container */
2423 ERR("Mipmap surface has a glTexture bound to it!\n");
2426 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2427 checkGLcall("glBindTexture");
2429 LEAVE_GL();
2431 return;
2434 #include <errno.h>
2435 #include <stdio.h>
2436 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2438 FILE* f = NULL;
2439 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2440 char *allocatedMemory;
2441 const char *textureRow;
2442 IWineD3DSwapChain *swapChain = NULL;
2443 int width, height, i, y;
2444 GLuint tmpTexture = 0;
2445 DWORD color;
2446 /*FIXME:
2447 Textures may not be stored in ->allocatedgMemory and a GlTexture
2448 so we should lock the surface before saving a snapshot, or at least check that
2450 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2451 by calling GetTexImage and in compressed form by calling
2452 GetCompressedTexImageARB. Queried compressed images can be saved and
2453 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2454 texture images do not need to be processed by the GL and should
2455 significantly improve texture loading performance relative to uncompressed
2456 images. */
2458 /* Setup the width and height to be the internal texture width and height. */
2459 width = This->pow2Width;
2460 height = This->pow2Height;
2461 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2462 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2464 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2465 /* if were not a real texture then read the back buffer into a real texture */
2466 /* we don't want to interfere with the back buffer so read the data into a temporary
2467 * texture and then save the data out of the temporary texture
2469 GLint prevRead;
2470 ENTER_GL();
2471 TRACE("(%p) Reading render target into texture\n", This);
2473 glGenTextures(1, &tmpTexture);
2474 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2476 glTexImage2D(GL_TEXTURE_2D,
2478 GL_RGBA,
2479 width,
2480 height,
2481 0/*border*/,
2482 GL_RGBA,
2483 GL_UNSIGNED_INT_8_8_8_8_REV,
2484 NULL);
2486 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2487 vcheckGLcall("glGetIntegerv");
2488 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2489 vcheckGLcall("glReadBuffer");
2490 glCopyTexImage2D(GL_TEXTURE_2D,
2492 GL_RGBA,
2495 width,
2496 height,
2499 checkGLcall("glCopyTexImage2D");
2500 glReadBuffer(prevRead);
2501 LEAVE_GL();
2503 } else { /* bind the real texture, and make sure it up to date */
2504 IWineD3DSurface_PreLoad(iface);
2505 surface_bind_and_dirtify(This);
2507 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2508 ENTER_GL();
2509 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2510 glGetTexImage(GL_TEXTURE_2D,
2511 This->glDescription.level,
2512 GL_RGBA,
2513 GL_UNSIGNED_INT_8_8_8_8_REV,
2514 allocatedMemory);
2515 checkGLcall("glTexImage2D");
2516 if (tmpTexture) {
2517 glBindTexture(GL_TEXTURE_2D, 0);
2518 glDeleteTextures(1, &tmpTexture);
2520 LEAVE_GL();
2522 f = fopen(filename, "w+");
2523 if (NULL == f) {
2524 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2525 return WINED3DERR_INVALIDCALL;
2527 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2528 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2529 /* TGA header */
2530 fputc(0,f);
2531 fputc(0,f);
2532 fputc(2,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 fputc(0,f);
2540 fputc(0,f);
2541 fputc(0,f);
2542 /* short width*/
2543 fwrite(&width,2,1,f);
2544 /* short height */
2545 fwrite(&height,2,1,f);
2546 /* format rgba */
2547 fputc(0x20,f);
2548 fputc(0x28,f);
2549 /* raw data */
2550 /* 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 */
2551 if(swapChain)
2552 textureRow = allocatedMemory + (width * (height - 1) *4);
2553 else
2554 textureRow = allocatedMemory;
2555 for (y = 0 ; y < height; y++) {
2556 for (i = 0; i < width; i++) {
2557 color = *((const DWORD*)textureRow);
2558 fputc((color >> 16) & 0xFF, f); /* B */
2559 fputc((color >> 8) & 0xFF, f); /* G */
2560 fputc((color >> 0) & 0xFF, f); /* R */
2561 fputc((color >> 24) & 0xFF, f); /* A */
2562 textureRow += 4;
2564 /* take two rows of the pointer to the texture memory */
2565 if(swapChain)
2566 (textureRow-= width << 3);
2569 TRACE("Closing file\n");
2570 fclose(f);
2572 if(swapChain) {
2573 IWineD3DSwapChain_Release(swapChain);
2575 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2576 return WINED3D_OK;
2580 * Slightly inefficient way to handle multiple dirty rects but it works :)
2582 static HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect)
2584 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2585 IWineD3DBaseTexture *baseTexture = NULL;
2587 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2588 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2590 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2591 if (NULL != pDirtyRect) {
2592 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2593 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2594 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2595 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2596 } else {
2597 This->dirtyRect.left = 0;
2598 This->dirtyRect.top = 0;
2599 This->dirtyRect.right = This->currentDesc.Width;
2600 This->dirtyRect.bottom = This->currentDesc.Height;
2602 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2603 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2604 /* if the container is a basetexture then mark it dirty. */
2605 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2606 TRACE("Passing to container\n");
2607 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2608 IWineD3DBaseTexture_Release(baseTexture);
2610 return WINED3D_OK;
2613 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2614 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2615 HRESULT hr;
2616 const struct GlPixelFormatDesc *glDesc;
2617 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2619 TRACE("(%p) : Calling base function first\n", This);
2620 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2621 if(SUCCEEDED(hr)) {
2622 /* Setup some glformat defaults */
2623 This->glDescription.glFormat = glDesc->glFormat;
2624 This->glDescription.glFormatInternal = glDesc->glInternal;
2625 This->glDescription.glType = glDesc->glType;
2627 This->Flags &= ~SFLAG_ALLOCATED;
2628 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2629 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2631 return hr;
2634 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2635 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2637 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2638 WARN("Surface is locked or the HDC is in use\n");
2639 return WINED3DERR_INVALIDCALL;
2642 if(Mem && Mem != This->resource.allocatedMemory) {
2643 void *release = NULL;
2645 /* Do I have to copy the old surface content? */
2646 if(This->Flags & SFLAG_DIBSECTION) {
2647 /* Release the DC. No need to hold the critical section for the update
2648 * Thread because this thread runs only on front buffers, but this method
2649 * fails for render targets in the check above.
2651 SelectObject(This->hDC, This->dib.holdbitmap);
2652 DeleteDC(This->hDC);
2653 /* Release the DIB section */
2654 DeleteObject(This->dib.DIBsection);
2655 This->dib.bitmap_data = NULL;
2656 This->resource.allocatedMemory = NULL;
2657 This->hDC = NULL;
2658 This->Flags &= ~SFLAG_DIBSECTION;
2659 } else if(!(This->Flags & SFLAG_USERPTR)) {
2660 release = This->resource.heapMemory;
2661 This->resource.heapMemory = NULL;
2663 This->resource.allocatedMemory = Mem;
2664 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2666 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2667 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2669 /* For client textures opengl has to be notified */
2670 if(This->Flags & SFLAG_CLIENT) {
2671 This->Flags &= ~SFLAG_ALLOCATED;
2672 IWineD3DSurface_PreLoad(iface);
2673 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2676 /* Now free the old memory if any */
2677 HeapFree(GetProcessHeap(), 0, release);
2678 } else if(This->Flags & SFLAG_USERPTR) {
2679 /* LockRect and GetDC will re-create the dib section and allocated memory */
2680 This->resource.allocatedMemory = NULL;
2681 /* HeapMemory should be NULL already */
2682 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2683 This->Flags &= ~SFLAG_USERPTR;
2685 if(This->Flags & SFLAG_CLIENT) {
2686 This->Flags &= ~SFLAG_ALLOCATED;
2687 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2688 IWineD3DSurface_PreLoad(iface);
2691 return WINED3D_OK;
2694 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2696 /* Flip the surface contents */
2697 /* Flip the DC */
2699 HDC tmp;
2700 tmp = front->hDC;
2701 front->hDC = back->hDC;
2702 back->hDC = tmp;
2705 /* Flip the DIBsection */
2707 HBITMAP tmp;
2708 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2709 tmp = front->dib.DIBsection;
2710 front->dib.DIBsection = back->dib.DIBsection;
2711 back->dib.DIBsection = tmp;
2713 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2714 else front->Flags &= ~SFLAG_DIBSECTION;
2715 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2716 else back->Flags &= ~SFLAG_DIBSECTION;
2719 /* Flip the surface data */
2721 void* tmp;
2723 tmp = front->dib.bitmap_data;
2724 front->dib.bitmap_data = back->dib.bitmap_data;
2725 back->dib.bitmap_data = tmp;
2727 tmp = front->resource.allocatedMemory;
2728 front->resource.allocatedMemory = back->resource.allocatedMemory;
2729 back->resource.allocatedMemory = tmp;
2731 tmp = front->resource.heapMemory;
2732 front->resource.heapMemory = back->resource.heapMemory;
2733 back->resource.heapMemory = tmp;
2736 /* Flip the PBO */
2738 GLuint tmp_pbo = front->pbo;
2739 front->pbo = back->pbo;
2740 back->pbo = tmp_pbo;
2743 /* client_memory should not be different, but just in case */
2745 BOOL tmp;
2746 tmp = front->dib.client_memory;
2747 front->dib.client_memory = back->dib.client_memory;
2748 back->dib.client_memory = tmp;
2751 /* Flip the opengl texture */
2753 glDescriptor tmp_desc = back->glDescription;
2754 back->glDescription = front->glDescription;
2755 front->glDescription = tmp_desc;
2759 DWORD tmp_flags = back->Flags;
2760 back->Flags = front->Flags;
2761 front->Flags = tmp_flags;
2765 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2766 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2767 IWineD3DSwapChainImpl *swapchain = NULL;
2768 HRESULT hr;
2769 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2771 /* Flipping is only supported on RenderTargets and overlays*/
2772 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2773 WARN("Tried to flip a non-render target, non-overlay surface\n");
2774 return WINEDDERR_NOTFLIPPABLE;
2777 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2778 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2780 /* Update the overlay if it is visible */
2781 if(This->overlay_dest) {
2782 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
2783 } else {
2784 return WINED3D_OK;
2788 if(override) {
2789 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2790 * FIXME("(%p) Target override is not supported by now\n", This);
2791 * Additionally, it isn't really possible to support triple-buffering
2792 * properly on opengl at all
2796 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2797 if(!swapchain) {
2798 ERR("Flipped surface is not on a swapchain\n");
2799 return WINEDDERR_NOTFLIPPABLE;
2802 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2803 * and only d3d8 and d3d9 apps specify the presentation interval
2805 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2806 /* Most common case first to avoid wasting time on all the other cases */
2807 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2808 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2809 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2810 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2811 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2812 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2813 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2814 } else {
2815 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2818 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2819 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2820 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2821 return hr;
2824 /* Does a direct frame buffer -> texture copy. Stretching is done
2825 * with single pixel copy calls
2827 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
2828 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
2829 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
2831 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2832 float xrel, yrel;
2833 UINT row;
2834 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2837 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2838 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2839 ENTER_GL();
2841 /* Bind the target texture */
2842 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2843 checkGLcall("glBindTexture");
2844 if(!swapchain) {
2845 TRACE("Reading from an offscreen target\n");
2846 upsidedown = !upsidedown;
2847 glReadBuffer(myDevice->offscreenBuffer);
2848 } else {
2849 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2850 glReadBuffer(buffer);
2852 checkGLcall("glReadBuffer");
2854 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2855 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2857 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2858 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2860 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2861 ERR("Texture filtering not supported in direct blit\n");
2863 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2864 ERR("Texture filtering not supported in direct blit\n");
2867 if(upsidedown &&
2868 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2869 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2870 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2872 glCopyTexSubImage2D(This->glDescription.target,
2873 This->glDescription.level,
2874 drect->x1, drect->y1, /* xoffset, yoffset */
2875 srect->x1, Src->currentDesc.Height - srect->y2,
2876 drect->x2 - drect->x1, drect->y2 - drect->y1);
2877 } else {
2878 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2879 /* I have to process this row by row to swap the image,
2880 * otherwise it would be upside down, so stretching in y direction
2881 * doesn't cost extra time
2883 * However, stretching in x direction can be avoided if not necessary
2885 for(row = drect->y1; row < drect->y2; row++) {
2886 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2887 /* Well, that stuff works, but it's very slow.
2888 * find a better way instead
2890 UINT col;
2892 for(col = drect->x1; col < drect->x2; col++) {
2893 glCopyTexSubImage2D(This->glDescription.target,
2894 This->glDescription.level,
2895 drect->x1 + col, row, /* xoffset, yoffset */
2896 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2897 1, 1);
2899 } else {
2900 glCopyTexSubImage2D(This->glDescription.target,
2901 This->glDescription.level,
2902 drect->x1, row, /* xoffset, yoffset */
2903 srect->x1, yoffset - (int) (row * yrel),
2904 drect->x2-drect->x1, 1);
2908 vcheckGLcall("glCopyTexSubImage2D");
2910 LEAVE_GL();
2913 /* Uses the hardware to stretch and flip the image */
2914 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
2915 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
2916 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
2918 GLuint src, backup = 0;
2919 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2920 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2921 float left, right, top, bottom; /* Texture coordinates */
2922 UINT fbwidth = Src->currentDesc.Width;
2923 UINT fbheight = Src->currentDesc.Height;
2924 GLenum drawBuffer = GL_BACK;
2925 GLenum texture_target;
2926 BOOL noBackBufferBackup;
2928 TRACE("Using hwstretch blit\n");
2929 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2930 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2931 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2933 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2934 if(!noBackBufferBackup && Src->glDescription.textureName == 0) {
2935 /* Get it a description */
2936 IWineD3DSurface_PreLoad(SrcSurface);
2938 ENTER_GL();
2940 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2941 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2943 if(myDevice->activeContext->aux_buffers >= 2) {
2944 /* Got more than one aux buffer? Use the 2nd aux buffer */
2945 drawBuffer = GL_AUX1;
2946 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && myDevice->activeContext->aux_buffers >= 1) {
2947 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2948 drawBuffer = GL_AUX0;
2951 if(noBackBufferBackup) {
2952 glGenTextures(1, &backup);
2953 checkGLcall("glGenTextures\n");
2954 glBindTexture(GL_TEXTURE_2D, backup);
2955 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2956 texture_target = GL_TEXTURE_2D;
2957 } else {
2958 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2959 * we are reading from the back buffer, the backup can be used as source texture
2961 texture_target = Src->glDescription.target;
2962 glBindTexture(texture_target, Src->glDescription.textureName);
2963 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2964 glEnable(texture_target);
2965 checkGLcall("glEnable(texture_target)");
2967 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2968 Src->Flags &= ~SFLAG_INTEXTURE;
2971 if(swapchain) {
2972 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
2973 } else {
2974 TRACE("Reading from an offscreen target\n");
2975 upsidedown = !upsidedown;
2976 glReadBuffer(myDevice->offscreenBuffer);
2979 /* TODO: Only back up the part that will be overwritten */
2980 glCopyTexSubImage2D(texture_target, 0,
2981 0, 0 /* read offsets */,
2982 0, 0,
2983 fbwidth,
2984 fbheight);
2986 checkGLcall("glCopyTexSubImage2D");
2988 /* No issue with overriding these - the sampler is dirty due to blit usage */
2989 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2990 magLookup[Filter - WINED3DTEXF_NONE]);
2991 checkGLcall("glTexParameteri");
2992 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2993 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
2994 checkGLcall("glTexParameteri");
2996 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2997 src = backup ? backup : Src->glDescription.textureName;
2998 } else {
2999 glReadBuffer(GL_FRONT);
3000 checkGLcall("glReadBuffer(GL_FRONT)");
3002 glGenTextures(1, &src);
3003 checkGLcall("glGenTextures(1, &src)");
3004 glBindTexture(GL_TEXTURE_2D, src);
3005 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3007 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
3008 * out for power of 2 sizes
3010 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3011 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3012 checkGLcall("glTexImage2D");
3013 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3014 0, 0 /* read offsets */,
3015 0, 0,
3016 fbwidth,
3017 fbheight);
3019 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3020 checkGLcall("glTexParameteri");
3021 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3022 checkGLcall("glTexParameteri");
3024 glReadBuffer(GL_BACK);
3025 checkGLcall("glReadBuffer(GL_BACK)");
3027 if(texture_target != GL_TEXTURE_2D) {
3028 glDisable(texture_target);
3029 glEnable(GL_TEXTURE_2D);
3030 texture_target = GL_TEXTURE_2D;
3033 checkGLcall("glEnd and previous");
3035 left = srect->x1;
3036 right = srect->x2;
3038 if(upsidedown) {
3039 top = Src->currentDesc.Height - srect->y1;
3040 bottom = Src->currentDesc.Height - srect->y2;
3041 } else {
3042 top = Src->currentDesc.Height - srect->y2;
3043 bottom = Src->currentDesc.Height - srect->y1;
3046 if(Src->Flags & SFLAG_NORMCOORD) {
3047 left /= Src->pow2Width;
3048 right /= Src->pow2Width;
3049 top /= Src->pow2Height;
3050 bottom /= Src->pow2Height;
3053 /* draw the source texture stretched and upside down. The correct surface is bound already */
3054 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3055 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3057 glDrawBuffer(drawBuffer);
3058 glReadBuffer(drawBuffer);
3060 glBegin(GL_QUADS);
3061 /* bottom left */
3062 glTexCoord2f(left, bottom);
3063 glVertex2i(0, fbheight);
3065 /* top left */
3066 glTexCoord2f(left, top);
3067 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3069 /* top right */
3070 glTexCoord2f(right, top);
3071 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3073 /* bottom right */
3074 glTexCoord2f(right, bottom);
3075 glVertex2i(drect->x2 - drect->x1, fbheight);
3076 glEnd();
3077 checkGLcall("glEnd and previous");
3079 if(texture_target != This->glDescription.target) {
3080 glDisable(texture_target);
3081 glEnable(This->glDescription.target);
3082 texture_target = This->glDescription.target;
3085 /* Now read the stretched and upside down image into the destination texture */
3086 glBindTexture(texture_target, This->glDescription.textureName);
3087 checkGLcall("glBindTexture");
3088 glCopyTexSubImage2D(texture_target,
3090 drect->x1, drect->y1, /* xoffset, yoffset */
3091 0, 0, /* We blitted the image to the origin */
3092 drect->x2 - drect->x1, drect->y2 - drect->y1);
3093 checkGLcall("glCopyTexSubImage2D");
3095 if(drawBuffer == GL_BACK) {
3096 /* Write the back buffer backup back */
3097 if(backup) {
3098 if(texture_target != GL_TEXTURE_2D) {
3099 glDisable(texture_target);
3100 glEnable(GL_TEXTURE_2D);
3101 texture_target = GL_TEXTURE_2D;
3103 glBindTexture(GL_TEXTURE_2D, backup);
3104 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3105 } else {
3106 if(texture_target != Src->glDescription.target) {
3107 glDisable(texture_target);
3108 glEnable(Src->glDescription.target);
3109 texture_target = Src->glDescription.target;
3111 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3112 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
3115 glBegin(GL_QUADS);
3116 /* top left */
3117 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
3118 glVertex2i(0, 0);
3120 /* bottom left */
3121 glTexCoord2f(0.0, 0.0);
3122 glVertex2i(0, fbheight);
3124 /* bottom right */
3125 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
3126 glVertex2i(fbwidth, Src->currentDesc.Height);
3128 /* top right */
3129 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3130 glVertex2i(fbwidth, 0);
3131 glEnd();
3132 } else {
3133 /* Restore the old draw buffer */
3134 glDrawBuffer(GL_BACK);
3136 glDisable(texture_target);
3137 checkGLcall("glDisable(texture_target)");
3139 /* Cleanup */
3140 if(src != Src->glDescription.textureName && src != backup) {
3141 glDeleteTextures(1, &src);
3142 checkGLcall("glDeleteTextures(1, &src)");
3144 if(backup) {
3145 glDeleteTextures(1, &backup);
3146 checkGLcall("glDeleteTextures(1, &backup)");
3149 LEAVE_GL();
3152 /* Not called from the VTable */
3153 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3154 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3155 WINED3DTEXTUREFILTERTYPE Filter)
3157 WINED3DRECT rect;
3158 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3159 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3160 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3162 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3164 /* Get the swapchain. One of the surfaces has to be a primary surface */
3165 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3166 WARN("Destination is in sysmem, rejecting gl blt\n");
3167 return WINED3DERR_INVALIDCALL;
3169 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3170 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3171 if(Src) {
3172 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3173 WARN("Src is in sysmem, rejecting gl blt\n");
3174 return WINED3DERR_INVALIDCALL;
3176 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3177 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3180 /* Early sort out of cases where no render target is used */
3181 if(!dstSwapchain && !srcSwapchain &&
3182 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3183 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3184 return WINED3DERR_INVALIDCALL;
3187 /* No destination color keying supported */
3188 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3189 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3190 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3191 return WINED3DERR_INVALIDCALL;
3194 if (DestRect) {
3195 rect.x1 = DestRect->left;
3196 rect.y1 = DestRect->top;
3197 rect.x2 = DestRect->right;
3198 rect.y2 = DestRect->bottom;
3199 } else {
3200 rect.x1 = 0;
3201 rect.y1 = 0;
3202 rect.x2 = This->currentDesc.Width;
3203 rect.y2 = This->currentDesc.Height;
3206 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3207 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3208 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3209 /* Half-life does a Blt from the back buffer to the front buffer,
3210 * Full surface size, no flags... Use present instead
3212 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3215 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3216 while(1)
3218 RECT mySrcRect;
3219 TRACE("Looking if a Present can be done...\n");
3220 /* Source Rectangle must be full surface */
3221 if( SrcRect ) {
3222 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3223 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3224 TRACE("No, Source rectangle doesn't match\n");
3225 break;
3228 mySrcRect.left = 0;
3229 mySrcRect.top = 0;
3230 mySrcRect.right = Src->currentDesc.Width;
3231 mySrcRect.bottom = Src->currentDesc.Height;
3233 /* No stretching may occur */
3234 if(mySrcRect.right != rect.x2 - rect.x1 ||
3235 mySrcRect.bottom != rect.y2 - rect.y1) {
3236 TRACE("No, stretching is done\n");
3237 break;
3240 /* Destination must be full surface or match the clipping rectangle */
3241 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3243 RECT cliprect;
3244 POINT pos[2];
3245 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3246 pos[0].x = rect.x1;
3247 pos[0].y = rect.y1;
3248 pos[1].x = rect.x2;
3249 pos[1].y = rect.y2;
3250 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3251 pos, 2);
3253 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3254 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3256 TRACE("No, dest rectangle doesn't match(clipper)\n");
3257 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3258 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3259 break;
3262 else
3264 if(rect.x1 != 0 || rect.y1 != 0 ||
3265 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3266 TRACE("No, dest rectangle doesn't match(surface size)\n");
3267 break;
3271 TRACE("Yes\n");
3273 /* These flags are unimportant for the flag check, remove them */
3274 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3275 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3277 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3278 * take very long, while a flip is fast.
3279 * This applies to Half-Life, which does such Blts every time it finished
3280 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3281 * menu. This is also used by all apps when they do windowed rendering
3283 * The problem is that flipping is not really the same as copying. After a
3284 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3285 * untouched. Therefore it's necessary to override the swap effect
3286 * and to set it back after the flip.
3288 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3289 * testcases.
3292 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3293 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3295 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3296 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3298 dstSwapchain->presentParms.SwapEffect = orig_swap;
3300 return WINED3D_OK;
3302 break;
3305 TRACE("Unsupported blit between buffers on the same swapchain\n");
3306 return WINED3DERR_INVALIDCALL;
3307 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3308 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3309 return WINED3DERR_INVALIDCALL;
3310 } else if(dstSwapchain && srcSwapchain) {
3311 FIXME("Implement hardware blit between two different swapchains\n");
3312 return WINED3DERR_INVALIDCALL;
3313 } else if(dstSwapchain) {
3314 if(SrcSurface == myDevice->render_targets[0]) {
3315 TRACE("Blit from active render target to a swapchain\n");
3316 /* Handled with regular texture -> swapchain blit */
3318 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3319 FIXME("Implement blit from a swapchain to the active render target\n");
3320 return WINED3DERR_INVALIDCALL;
3323 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3324 /* Blit from render target to texture */
3325 WINED3DRECT srect;
3326 BOOL upsideDown, stretchx;
3327 BOOL paletteOverride = FALSE;
3329 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3330 TRACE("Color keying not supported by frame buffer to texture blit\n");
3331 return WINED3DERR_INVALIDCALL;
3332 /* Destination color key is checked above */
3335 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3336 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3338 if(SrcRect) {
3339 if(SrcRect->top < SrcRect->bottom) {
3340 srect.y1 = SrcRect->top;
3341 srect.y2 = SrcRect->bottom;
3342 upsideDown = FALSE;
3343 } else {
3344 srect.y1 = SrcRect->bottom;
3345 srect.y2 = SrcRect->top;
3346 upsideDown = TRUE;
3348 srect.x1 = SrcRect->left;
3349 srect.x2 = SrcRect->right;
3350 } else {
3351 srect.x1 = 0;
3352 srect.y1 = 0;
3353 srect.x2 = Src->currentDesc.Width;
3354 srect.y2 = Src->currentDesc.Height;
3355 upsideDown = FALSE;
3357 if(rect.x1 > rect.x2) {
3358 UINT tmp = rect.x2;
3359 rect.x2 = rect.x1;
3360 rect.x1 = tmp;
3361 upsideDown = !upsideDown;
3364 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3365 stretchx = TRUE;
3366 } else {
3367 stretchx = FALSE;
3370 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3371 * In this case grab the palette from the render target. */
3372 if((This->resource.format == WINED3DFMT_P8) && (This->palette == NULL)) {
3373 paletteOverride = TRUE;
3374 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3375 This->palette = Src->palette;
3378 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3379 * flip the image nor scale it.
3381 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3382 * -> If the app wants a image width an unscaled width, copy it line per line
3383 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3384 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3385 * back buffer. This is slower than reading line per line, thus not used for flipping
3386 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3387 * pixel by pixel
3389 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3390 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3391 * backends.
3393 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3394 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3395 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3396 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3397 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3398 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3399 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3400 } else {
3401 TRACE("Using hardware stretching to flip / stretch the texture\n");
3402 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3405 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3406 if(paletteOverride)
3407 This->palette = NULL;
3409 if(!(This->Flags & SFLAG_DONOTFREE)) {
3410 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3411 This->resource.allocatedMemory = NULL;
3412 This->resource.heapMemory = NULL;
3413 } else {
3414 This->Flags &= ~SFLAG_INSYSMEM;
3416 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3417 * path is never entered
3419 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3421 return WINED3D_OK;
3422 } else if(Src) {
3423 /* Blit from offscreen surface to render target */
3424 float glTexCoord[4];
3425 DWORD oldCKeyFlags = Src->CKeyFlags;
3426 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3427 RECT SourceRectangle;
3428 BOOL paletteOverride = FALSE;
3430 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3432 if(SrcRect) {
3433 SourceRectangle.left = SrcRect->left;
3434 SourceRectangle.right = SrcRect->right;
3435 SourceRectangle.top = SrcRect->top;
3436 SourceRectangle.bottom = SrcRect->bottom;
3437 } else {
3438 SourceRectangle.left = 0;
3439 SourceRectangle.right = Src->currentDesc.Width;
3440 SourceRectangle.top = 0;
3441 SourceRectangle.bottom = Src->currentDesc.Height;
3444 /* When blitting from an offscreen surface to a rendertarget, the source
3445 * surface is not required to have a palette. Our rendering / conversion
3446 * code further down the road retrieves the palette from the surface, so
3447 * it must have a palette set. */
3448 if((Src->resource.format == WINED3DFMT_P8) && (Src->palette == NULL)) {
3449 paletteOverride = TRUE;
3450 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3451 Src->palette = This->palette;
3454 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3455 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3456 TRACE("Using stretch_rect_fbo\n");
3457 /* The source is always a texture, but never the currently active render target, and the texture
3458 * contents are never upside down
3460 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3461 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3463 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3464 if(paletteOverride)
3465 Src->palette = NULL;
3466 return WINED3D_OK;
3469 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3470 /* Fall back to software */
3471 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3472 SourceRectangle.left, SourceRectangle.top,
3473 SourceRectangle.right, SourceRectangle.bottom);
3474 return WINED3DERR_INVALIDCALL;
3477 /* Color keying: Check if we have to do a color keyed blt,
3478 * and if not check if a color key is activated.
3480 * Just modify the color keying parameters in the surface and restore them afterwards
3481 * The surface keeps track of the color key last used to load the opengl surface.
3482 * PreLoad will catch the change to the flags and color key and reload if necessary.
3484 if(Flags & WINEDDBLT_KEYSRC) {
3485 /* Use color key from surface */
3486 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3487 /* Use color key from DDBltFx */
3488 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3489 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3490 } else {
3491 /* Do not use color key */
3492 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3495 /* Now load the surface */
3496 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3498 /* Activate the destination context, set it up for blitting */
3499 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3501 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3502 * while OpenGL coordinates are window relative.
3503 * Also beware of the origin difference(top left vs bottom left).
3504 * Also beware that the front buffer's surface size is screen width x screen height,
3505 * whereas the real gl drawable size is the size of the window.
3507 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3508 RECT windowsize;
3509 POINT offset = {0,0};
3510 UINT h;
3511 ClientToScreen(dstSwapchain->win_handle, &offset);
3512 GetClientRect(dstSwapchain->win_handle, &windowsize);
3513 h = windowsize.bottom - windowsize.top;
3514 rect.x1 -= offset.x; rect.x2 -=offset.x;
3515 rect.y1 -= offset.y; rect.y2 -=offset.y;
3516 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3519 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format,
3520 Src->glDescription.target, Src->pow2Width, Src->pow2Height);
3522 ENTER_GL();
3524 /* Bind the texture */
3525 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3526 checkGLcall("glBindTexture");
3528 /* Filtering for StretchRect */
3529 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3530 magLookup[Filter - WINED3DTEXF_NONE]);
3531 checkGLcall("glTexParameteri");
3532 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3533 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
3534 checkGLcall("glTexParameteri");
3535 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3536 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3537 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3538 checkGLcall("glTexEnvi");
3540 /* This is for color keying */
3541 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3542 glEnable(GL_ALPHA_TEST);
3543 checkGLcall("glEnable GL_ALPHA_TEST");
3545 /* When the primary render target uses P8, the alpha component contains the palette index.
3546 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3547 * should be masked away have alpha set to 0. */
3548 if(primary_render_target_is_p8(myDevice))
3549 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3550 else
3551 glAlphaFunc(GL_NOTEQUAL, 0.0);
3552 checkGLcall("glAlphaFunc\n");
3553 } else {
3554 glDisable(GL_ALPHA_TEST);
3555 checkGLcall("glDisable GL_ALPHA_TEST");
3558 /* Draw a textured quad
3560 glBegin(GL_QUADS);
3562 glColor3d(1.0f, 1.0f, 1.0f);
3563 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3564 glVertex3f(rect.x1,
3565 rect.y1,
3566 0.0);
3568 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3569 glVertex3f(rect.x1, rect.y2, 0.0);
3571 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3572 glVertex3f(rect.x2,
3573 rect.y2,
3574 0.0);
3576 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3577 glVertex3f(rect.x2,
3578 rect.y1,
3579 0.0);
3580 glEnd();
3581 checkGLcall("glEnd");
3583 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3584 glDisable(GL_ALPHA_TEST);
3585 checkGLcall("glDisable(GL_ALPHA_TEST)");
3588 glBindTexture(Src->glDescription.target, 0);
3589 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3591 /* Restore the color key parameters */
3592 Src->CKeyFlags = oldCKeyFlags;
3593 Src->SrcBltCKey = oldBltCKey;
3595 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3596 if(paletteOverride)
3597 Src->palette = NULL;
3599 LEAVE_GL();
3601 /* Leave the opengl state valid for blitting */
3602 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3604 /* Flush in case the drawable is used by multiple GL contexts */
3605 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3606 glFlush();
3608 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3609 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3610 * is outdated now
3612 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3614 return WINED3D_OK;
3615 } else {
3616 /* Source-Less Blit to render target */
3617 if (Flags & WINEDDBLT_COLORFILL) {
3618 /* This is easy to handle for the D3D Device... */
3619 DWORD color;
3621 TRACE("Colorfill\n");
3623 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3624 must be true if we are here */
3625 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3626 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3627 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3628 TRACE("Surface is higher back buffer, falling back to software\n");
3629 return WINED3DERR_INVALIDCALL;
3632 /* The color as given in the Blt function is in the format of the frame-buffer...
3633 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3635 if (This->resource.format == WINED3DFMT_P8) {
3636 DWORD alpha;
3638 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3639 else alpha = 0xFF000000;
3641 if (This->palette) {
3642 color = (alpha |
3643 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3644 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3645 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3646 } else {
3647 color = alpha;
3650 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3651 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3652 color = 0xFFFFFFFF;
3653 } else {
3654 color = ((0xFF000000) |
3655 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3656 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3657 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3660 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3661 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3662 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3664 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3665 color = DDBltFx->u5.dwFillColor;
3667 else {
3668 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3669 return WINED3DERR_INVALIDCALL;
3672 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3673 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3674 1, /* Number of rectangles */
3675 &rect, WINED3DCLEAR_TARGET, color,
3676 0.0 /* Z */,
3677 0 /* Stencil */);
3678 return WINED3D_OK;
3682 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3683 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3684 return WINED3DERR_INVALIDCALL;
3687 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3688 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3690 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3691 float depth;
3693 if (Flags & WINEDDBLT_DEPTHFILL) {
3694 switch(This->resource.format) {
3695 case WINED3DFMT_D16:
3696 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3697 break;
3698 case WINED3DFMT_D15S1:
3699 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3700 break;
3701 case WINED3DFMT_D24S8:
3702 case WINED3DFMT_D24X8:
3703 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3704 break;
3705 case WINED3DFMT_D32:
3706 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3707 break;
3708 default:
3709 depth = 0.0;
3710 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3713 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3714 DestRect == NULL ? 0 : 1,
3715 (const WINED3DRECT *)DestRect,
3716 WINED3DCLEAR_ZBUFFER,
3717 0x00000000,
3718 depth,
3719 0x00000000);
3722 FIXME("(%p): Unsupp depthstencil blit\n", This);
3723 return WINED3DERR_INVALIDCALL;
3726 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3727 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3728 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3729 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3730 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3731 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3732 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3734 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3736 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3737 return WINEDDERR_SURFACEBUSY;
3740 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3741 * except depth blits, which seem to work
3743 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3744 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3745 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3746 return WINED3DERR_INVALIDCALL;
3747 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3748 TRACE("Z Blit override handled the blit\n");
3749 return WINED3D_OK;
3753 /* Special cases for RenderTargets */
3754 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3755 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3756 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3759 /* For the rest call the X11 surface implementation.
3760 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3761 * other Blts are rather rare
3763 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3766 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
3767 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
3769 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3770 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3771 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3772 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3774 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3776 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3777 return WINEDDERR_SURFACEBUSY;
3780 if(myDevice->inScene &&
3781 (iface == myDevice->stencilBufferTarget ||
3782 (Source && Source == myDevice->stencilBufferTarget))) {
3783 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3784 return WINED3DERR_INVALIDCALL;
3787 /* Special cases for RenderTargets */
3788 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3789 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3791 RECT SrcRect, DstRect;
3792 DWORD Flags=0;
3794 if(rsrc) {
3795 SrcRect.left = rsrc->left;
3796 SrcRect.top= rsrc->top;
3797 SrcRect.bottom = rsrc->bottom;
3798 SrcRect.right = rsrc->right;
3799 } else {
3800 SrcRect.left = 0;
3801 SrcRect.top = 0;
3802 SrcRect.right = srcImpl->currentDesc.Width;
3803 SrcRect.bottom = srcImpl->currentDesc.Height;
3806 DstRect.left = dstx;
3807 DstRect.top=dsty;
3808 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3809 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3811 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3812 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3813 Flags |= WINEDDBLT_KEYSRC;
3814 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3815 Flags |= WINEDDBLT_KEYDEST;
3816 if(trans & WINEDDBLTFAST_WAIT)
3817 Flags |= WINEDDBLT_WAIT;
3818 if(trans & WINEDDBLTFAST_DONOTWAIT)
3819 Flags |= WINEDDBLT_DONOTWAIT;
3821 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3825 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3828 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
3830 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3831 RGBQUAD col[256];
3832 IWineD3DPaletteImpl *pal = This->palette;
3833 unsigned int n;
3834 TRACE("(%p)\n", This);
3836 if (!pal) return WINED3D_OK;
3838 if(This->resource.format == WINED3DFMT_P8 ||
3839 This->resource.format == WINED3DFMT_A8P8)
3841 int bpp;
3842 GLenum format, internal, type;
3843 CONVERT_TYPES convert;
3845 /* Check if we are using a RTL mode which uses texturing for uploads */
3846 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX);
3848 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
3849 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, This->srgb);
3851 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
3853 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
3854 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
3856 /* We want to force a palette refresh, so mark the drawable as not being up to date */
3857 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
3859 /* Re-upload the palette */
3860 d3dfmt_p8_upload_palette(iface, convert);
3861 } else {
3862 if(!(This->Flags & SFLAG_INSYSMEM)) {
3863 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3864 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3866 TRACE("Dirtifying surface\n");
3867 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3871 if(This->Flags & SFLAG_DIBSECTION) {
3872 TRACE("(%p): Updating the hdc's palette\n", This);
3873 for (n=0; n<256; n++) {
3874 col[n].rgbRed = pal->palents[n].peRed;
3875 col[n].rgbGreen = pal->palents[n].peGreen;
3876 col[n].rgbBlue = pal->palents[n].peBlue;
3877 col[n].rgbReserved = 0;
3879 SetDIBColorTable(This->hDC, 0, 256, col);
3882 /* Propagate the changes to the drawable when we have a palette. */
3883 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3884 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3886 return WINED3D_OK;
3889 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3890 /** Check against the maximum texture sizes supported by the video card **/
3891 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3892 unsigned int pow2Width, pow2Height;
3893 const struct GlPixelFormatDesc *glDesc;
3895 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3896 /* Setup some glformat defaults */
3897 This->glDescription.glFormat = glDesc->glFormat;
3898 This->glDescription.glFormatInternal = glDesc->glInternal;
3899 This->glDescription.glType = glDesc->glType;
3901 This->glDescription.textureName = 0;
3902 This->glDescription.target = GL_TEXTURE_2D;
3904 /* Non-power2 support */
3905 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO) || GL_SUPPORT(WINE_NORMALIZED_TEXRECT)) {
3906 pow2Width = This->currentDesc.Width;
3907 pow2Height = This->currentDesc.Height;
3908 } else {
3909 /* Find the nearest pow2 match */
3910 pow2Width = pow2Height = 1;
3911 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3912 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3914 This->pow2Width = pow2Width;
3915 This->pow2Height = pow2Height;
3917 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3918 WINED3DFORMAT Format = This->resource.format;
3919 /** TODO: add support for non power two compressed textures **/
3920 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3921 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5
3922 || This->resource.format == WINED3DFMT_ATI2N) {
3923 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3924 This, This->currentDesc.Width, This->currentDesc.Height);
3925 return WINED3DERR_NOTAVAILABLE;
3929 if(pow2Width != This->currentDesc.Width ||
3930 pow2Height != This->currentDesc.Height) {
3931 This->Flags |= SFLAG_NONPOW2;
3934 TRACE("%p\n", This);
3935 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3936 /* one of three options
3937 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)
3938 2: Set the texture to the maximum size (bad idea)
3939 3: WARN and return WINED3DERR_NOTAVAILABLE;
3940 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.
3942 WARN("(%p) Creating an oversized surface\n", This);
3943 This->Flags |= SFLAG_OVERSIZE;
3945 /* This will be initialized on the first blt */
3946 This->glRect.left = 0;
3947 This->glRect.top = 0;
3948 This->glRect.right = 0;
3949 This->glRect.bottom = 0;
3950 } else {
3951 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3952 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3953 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3954 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3956 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE) &&
3957 !((This->resource.format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE) && (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3959 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3960 This->pow2Width = This->currentDesc.Width;
3961 This->pow2Height = This->currentDesc.Height;
3962 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
3965 /* No oversize, gl rect is the full texture size */
3966 This->Flags &= ~SFLAG_OVERSIZE;
3967 This->glRect.left = 0;
3968 This->glRect.top = 0;
3969 This->glRect.right = This->pow2Width;
3970 This->glRect.bottom = This->pow2Height;
3973 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3974 switch(wined3d_settings.offscreen_rendering_mode) {
3975 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3976 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3977 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3981 This->Flags |= SFLAG_INSYSMEM;
3983 return WINED3D_OK;
3986 struct depth_blt_info
3988 GLenum binding;
3989 GLenum bind_target;
3990 enum tex_types tex_type;
3991 GLfloat coords[4][3];
3994 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
3996 GLfloat (*coords)[3] = info->coords;
3998 switch (target)
4000 default:
4001 FIXME("Unsupported texture target %#x\n", target);
4002 /* Fall back to GL_TEXTURE_2D */
4003 case GL_TEXTURE_2D:
4004 info->binding = GL_TEXTURE_BINDING_2D;
4005 info->bind_target = GL_TEXTURE_2D;
4006 info->tex_type = tex_2d;
4007 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
4008 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
4009 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4010 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4011 break;
4013 case GL_TEXTURE_RECTANGLE_ARB:
4014 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
4015 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
4016 info->tex_type = tex_rect;
4017 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
4018 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
4019 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4020 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4021 break;
4023 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
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_X:
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_Y:
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_Y:
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;
4059 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4060 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4061 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4062 info->tex_type = tex_cube;
4063 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4064 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4065 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4066 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4068 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4069 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4070 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4071 info->tex_type = tex_cube;
4072 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4073 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4074 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4075 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4079 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4081 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4082 struct depth_blt_info info;
4083 GLint old_binding = 0;
4085 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4087 glDisable(GL_CULL_FACE);
4088 glEnable(GL_BLEND);
4089 glDisable(GL_ALPHA_TEST);
4090 glDisable(GL_SCISSOR_TEST);
4091 glDisable(GL_STENCIL_TEST);
4092 glEnable(GL_DEPTH_TEST);
4093 glDepthFunc(GL_ALWAYS);
4094 glDepthMask(GL_TRUE);
4095 glBlendFunc(GL_ZERO, GL_ONE);
4096 glViewport(0, 0, w, h);
4098 surface_get_depth_blt_info(target, w, h, &info);
4099 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4100 glGetIntegerv(info.binding, &old_binding);
4101 glBindTexture(info.bind_target, texture);
4103 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4105 glBegin(GL_TRIANGLE_STRIP);
4106 glTexCoord3fv(info.coords[0]);
4107 glVertex2f(-1.0f, -1.0f);
4108 glTexCoord3fv(info.coords[1]);
4109 glVertex2f(1.0f, -1.0f);
4110 glTexCoord3fv(info.coords[2]);
4111 glVertex2f(-1.0f, 1.0f);
4112 glTexCoord3fv(info.coords[3]);
4113 glVertex2f(1.0f, 1.0f);
4114 glEnd();
4116 glBindTexture(info.bind_target, old_binding);
4118 glPopAttrib();
4120 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4123 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4124 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4126 TRACE("(%p) New location %#x\n", This, location);
4128 if (location & ~SFLAG_DS_LOCATIONS) {
4129 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4132 This->Flags &= ~SFLAG_DS_LOCATIONS;
4133 This->Flags |= location;
4136 void surface_load_ds_location(IWineD3DSurface *iface, DWORD location) {
4137 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4138 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4140 TRACE("(%p) New location %#x\n", This, location);
4142 /* TODO: Make this work for modes other than FBO */
4143 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4145 if (This->Flags & location) {
4146 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4147 return;
4150 if (This->current_renderbuffer) {
4151 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4152 return;
4155 if (location == SFLAG_DS_OFFSCREEN) {
4156 if (This->Flags & SFLAG_DS_ONSCREEN) {
4157 GLint old_binding = 0;
4158 GLenum bind_target;
4160 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4162 ENTER_GL();
4164 if (!device->depth_blt_texture) {
4165 glGenTextures(1, &device->depth_blt_texture);
4168 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4169 * directly on the FBO texture. That's because we need to flip. */
4170 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4171 if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
4172 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4173 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4174 } else {
4175 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4176 bind_target = GL_TEXTURE_2D;
4178 glBindTexture(bind_target, device->depth_blt_texture);
4179 glCopyTexImage2D(bind_target,
4180 This->glDescription.level,
4181 This->glDescription.glFormatInternal,
4184 This->currentDesc.Width,
4185 This->currentDesc.Height,
4187 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4188 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4189 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4190 glBindTexture(bind_target, old_binding);
4192 /* Setup the destination */
4193 if (!device->depth_blt_rb) {
4194 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
4195 checkGLcall("glGenRenderbuffersEXT");
4197 if (device->depth_blt_rb_w != This->currentDesc.Width
4198 || device->depth_blt_rb_h != This->currentDesc.Height) {
4199 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4200 checkGLcall("glBindRenderbufferEXT");
4201 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
4202 checkGLcall("glRenderbufferStorageEXT");
4203 device->depth_blt_rb_w = This->currentDesc.Width;
4204 device->depth_blt_rb_h = This->currentDesc.Height;
4207 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->dst_fbo);
4208 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4209 checkGLcall("glFramebufferRenderbufferEXT");
4210 context_attach_depth_stencil_fbo(device, GL_FRAMEBUFFER_EXT, iface, FALSE);
4212 /* Do the actual blit */
4213 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4214 checkGLcall("depth_blt");
4216 if (device->activeContext->current_fbo) {
4217 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->current_fbo->id);
4218 } else {
4219 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4220 checkGLcall("glBindFramebuffer()");
4223 LEAVE_GL();
4224 } else {
4225 FIXME("No up to date depth stencil location\n");
4227 } else if (location == SFLAG_DS_ONSCREEN) {
4228 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4229 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4231 ENTER_GL();
4233 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4234 checkGLcall("glBindFramebuffer()");
4235 surface_depth_blt(This, This->glDescription.textureName, This->currentDesc.Width, This->currentDesc.Height, This->glDescription.target);
4236 checkGLcall("depth_blt");
4238 if (device->activeContext->current_fbo) {
4239 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, device->activeContext->current_fbo->id));
4240 checkGLcall("glBindFramebuffer()");
4243 LEAVE_GL();
4244 } else {
4245 FIXME("No up to date depth stencil location\n");
4247 } else {
4248 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4251 This->Flags |= location;
4254 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4255 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4256 IWineD3DBaseTexture *texture;
4257 IWineD3DSurfaceImpl *overlay;
4259 TRACE("(%p)->(%s, %s)\n", iface,
4260 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
4261 persistent ? "TRUE" : "FALSE");
4263 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4264 IWineD3DSwapChain *swapchain = NULL;
4266 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
4267 TRACE("Surface %p is an onscreen surface\n", iface);
4269 IWineD3DSwapChain_Release(swapchain);
4270 } else {
4271 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4272 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4276 if(persistent) {
4277 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
4278 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4279 TRACE("Passing to container\n");
4280 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4281 IWineD3DBaseTexture_Release(texture);
4284 This->Flags &= ~SFLAG_LOCATIONS;
4285 This->Flags |= flag;
4287 /* Redraw emulated overlays, if any */
4288 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4289 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4290 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4293 } else {
4294 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
4295 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4296 TRACE("Passing to container\n");
4297 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4298 IWineD3DBaseTexture_Release(texture);
4301 This->Flags &= ~flag;
4305 struct coords {
4306 GLfloat x, y, z;
4309 struct float_rect
4311 float l;
4312 float t;
4313 float r;
4314 float b;
4317 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4319 f->l = ((r->left * 2.0f) / w) - 1.0f;
4320 f->t = ((r->top * 2.0f) / h) - 1.0f;
4321 f->r = ((r->right * 2.0f) / w) - 1.0f;
4322 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4325 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
4326 struct coords coords[4];
4327 RECT rect;
4328 IWineD3DSwapChain *swapchain;
4329 IWineD3DBaseTexture *texture;
4330 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4331 GLenum bind_target;
4332 struct float_rect f;
4334 if(rect_in) {
4335 rect = *rect_in;
4336 } else {
4337 rect.left = 0;
4338 rect.top = 0;
4339 rect.right = This->currentDesc.Width;
4340 rect.bottom = This->currentDesc.Height;
4343 switch(This->glDescription.target)
4345 case GL_TEXTURE_2D:
4346 bind_target = GL_TEXTURE_2D;
4348 coords[0].x = (float)rect.left / This->pow2Width;
4349 coords[0].y = (float)rect.top / This->pow2Height;
4350 coords[0].z = 0;
4352 coords[1].x = (float)rect.left / This->pow2Width;
4353 coords[1].y = (float)rect.bottom / This->pow2Height;
4354 coords[1].z = 0;
4356 coords[2].x = (float)rect.right / This->pow2Width;
4357 coords[2].y = (float)rect.bottom / This->pow2Height;
4358 coords[2].z = 0;
4360 coords[3].x = (float)rect.right / This->pow2Width;
4361 coords[3].y = (float)rect.top / This->pow2Height;
4362 coords[3].z = 0;
4363 break;
4365 case GL_TEXTURE_RECTANGLE_ARB:
4366 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4367 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4368 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4369 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4370 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4371 break;
4373 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4374 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4375 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4376 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4377 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4378 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4379 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4380 break;
4382 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4383 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4384 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4385 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4386 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4387 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4388 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4389 break;
4391 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
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 = 1; coords[0].z = f.t;
4395 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4396 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4397 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4398 break;
4400 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
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 = -1; coords[0].z = -f.t;
4404 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4405 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4406 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4407 break;
4409 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4410 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4411 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4412 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4413 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4414 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4415 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4416 break;
4418 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4419 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4420 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4421 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4422 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4423 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4424 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4425 break;
4427 default:
4428 ERR("Unexpected texture target %#x\n", This->glDescription.target);
4429 return;
4432 ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4433 ENTER_GL();
4435 glEnable(bind_target);
4436 checkGLcall("glEnable(bind_target)");
4437 glBindTexture(bind_target, This->glDescription.textureName);
4438 checkGLcall("bind_target, This->glDescription.textureName)");
4439 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4440 checkGLcall("glTexParameteri");
4441 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4442 checkGLcall("glTexParameteri");
4444 if (device->render_offscreen)
4446 LONG tmp = rect.top;
4447 rect.top = rect.bottom;
4448 rect.bottom = tmp;
4451 glBegin(GL_QUADS);
4452 glTexCoord3fv(&coords[0].x);
4453 glVertex2i(rect.left, rect.top);
4455 glTexCoord3fv(&coords[1].x);
4456 glVertex2i(rect.left, rect.bottom);
4458 glTexCoord3fv(&coords[2].x);
4459 glVertex2i(rect.right, rect.bottom);
4461 glTexCoord3fv(&coords[3].x);
4462 glVertex2i(rect.right, rect.top);
4463 glEnd();
4464 checkGLcall("glEnd");
4466 glDisable(bind_target);
4467 checkGLcall("glDisable(bind_target)");
4469 LEAVE_GL();
4471 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4473 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4474 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4475 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4476 glFlush();
4478 IWineD3DSwapChain_Release(swapchain);
4479 } else {
4480 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4481 * reset properly next draw
4483 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4485 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4486 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4487 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4488 IWineD3DBaseTexture_Release(texture);
4493 /*****************************************************************************
4494 * IWineD3DSurface::LoadLocation
4496 * Copies the current surface data from wherever it is to the requested
4497 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4498 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4499 * multiple locations, the gl texture is preferred over the drawable, which is
4500 * preferred over system memory. The PBO counts as system memory. If rect is
4501 * not NULL, only the specified rectangle is copied (only supported for
4502 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4503 * location is marked up to date after the copy.
4505 * Parameters:
4506 * flag: Surface location flag to be updated
4507 * rect: rectangle to be copied
4509 * Returns:
4510 * WINED3D_OK on success
4511 * WINED3DERR_DEVICELOST on an internal error
4513 *****************************************************************************/
4514 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4515 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4516 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4517 IWineD3DSwapChain *swapchain = NULL;
4518 GLenum format, internal, type;
4519 CONVERT_TYPES convert;
4520 int bpp;
4521 int width, pitch, outpitch;
4522 BYTE *mem;
4524 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4525 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
4526 TRACE("Surface %p is an onscreen surface\n", iface);
4528 IWineD3DSwapChain_Release(swapchain);
4529 } else {
4530 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4531 * Prefer SFLAG_INTEXTURE. */
4532 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4536 TRACE("(%p)->(%s, %p)\n", iface,
4537 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
4538 rect);
4539 if(rect) {
4540 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4543 if(This->Flags & flag) {
4544 TRACE("Location already up to date\n");
4545 return WINED3D_OK;
4548 if(!(This->Flags & SFLAG_LOCATIONS)) {
4549 ERR("Surface does not have any up to date location\n");
4550 This->Flags |= SFLAG_LOST;
4551 return WINED3DERR_DEVICELOST;
4554 if(flag == SFLAG_INSYSMEM) {
4555 surface_prepare_system_memory(This);
4557 /* Download the surface to system memory */
4558 if(This->Flags & SFLAG_INTEXTURE) {
4559 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4560 surface_bind_and_dirtify(This);
4562 surface_download_data(This);
4563 } else {
4564 read_from_framebuffer(This, rect,
4565 This->resource.allocatedMemory,
4566 IWineD3DSurface_GetPitch(iface));
4568 } else if(flag == SFLAG_INDRAWABLE) {
4569 if(This->Flags & SFLAG_INTEXTURE) {
4570 surface_blt_to_drawable(This, rect);
4571 } else {
4572 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4574 /* The width is in 'length' not in bytes */
4575 width = This->currentDesc.Width;
4576 pitch = IWineD3DSurface_GetPitch(iface);
4578 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4579 * but it isn't set (yet) in all cases it is getting called. */
4580 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4581 TRACE("Removing the pbo attached to surface %p\n", This);
4582 surface_remove_pbo(This);
4585 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4586 int height = This->currentDesc.Height;
4588 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4589 outpitch = width * bpp;
4590 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4592 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4593 if(!mem) {
4594 ERR("Out of memory %d, %d!\n", outpitch, height);
4595 return WINED3DERR_OUTOFVIDEOMEMORY;
4597 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4599 This->Flags |= SFLAG_CONVERTED;
4600 } else {
4601 This->Flags &= ~SFLAG_CONVERTED;
4602 mem = This->resource.allocatedMemory;
4605 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4607 /* Don't delete PBO memory */
4608 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4609 HeapFree(GetProcessHeap(), 0, mem);
4611 } else /* if(flag == SFLAG_INTEXTURE) */ {
4612 if (This->Flags & SFLAG_INDRAWABLE) {
4613 read_from_framebuffer_texture(This);
4614 } else { /* Upload from system memory */
4615 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
4617 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4618 surface_bind_and_dirtify(This);
4620 /* The only place where LoadTexture() might get called when isInDraw=1
4621 * is ActivateContext where lastActiveRenderTarget is preloaded.
4623 if(iface == device->lastActiveRenderTarget && device->isInDraw)
4624 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
4626 /* Otherwise: System memory copy must be most up to date */
4628 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4629 This->Flags |= SFLAG_GLCKEY;
4630 This->glCKey = This->SrcBltCKey;
4632 else This->Flags &= ~SFLAG_GLCKEY;
4634 /* The width is in 'length' not in bytes */
4635 width = This->currentDesc.Width;
4636 pitch = IWineD3DSurface_GetPitch(iface);
4638 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4639 * but it isn't set (yet) in all cases it is getting called. */
4640 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4641 TRACE("Removing the pbo attached to surface %p\n", This);
4642 surface_remove_pbo(This);
4645 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4646 int height = This->currentDesc.Height;
4648 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4649 outpitch = width * bpp;
4650 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4652 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4653 if(!mem) {
4654 ERR("Out of memory %d, %d!\n", outpitch, height);
4655 return WINED3DERR_OUTOFVIDEOMEMORY;
4657 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4659 This->Flags |= SFLAG_CONVERTED;
4660 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
4661 d3dfmt_p8_upload_palette(iface, convert);
4662 This->Flags &= ~SFLAG_CONVERTED;
4663 mem = This->resource.allocatedMemory;
4664 } else {
4665 This->Flags &= ~SFLAG_CONVERTED;
4666 mem = This->resource.allocatedMemory;
4669 /* Make sure the correct pitch is used */
4670 ENTER_GL();
4671 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4672 LEAVE_GL();
4674 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4675 TRACE("non power of two support\n");
4676 if(!(This->Flags & SFLAG_ALLOCATED)) {
4677 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4679 if (mem || (This->Flags & SFLAG_PBO)) {
4680 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4682 } else {
4683 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4684 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4686 if(!(This->Flags & SFLAG_ALLOCATED)) {
4687 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4689 if (mem || (This->Flags & SFLAG_PBO)) {
4690 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4694 /* Restore the default pitch */
4695 ENTER_GL();
4696 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4697 LEAVE_GL();
4699 /* Don't delete PBO memory */
4700 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4701 HeapFree(GetProcessHeap(), 0, mem);
4705 if(rect == NULL) {
4706 This->Flags |= flag;
4709 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
4710 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4711 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4712 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4715 return WINED3D_OK;
4718 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4720 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4721 IWineD3DSwapChain *swapchain = NULL;
4723 /* Update the drawable size method */
4724 if(container) {
4725 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4727 if(swapchain) {
4728 This->get_drawable_size = get_drawable_size_swapchain;
4729 IWineD3DSwapChain_Release(swapchain);
4730 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4731 switch(wined3d_settings.offscreen_rendering_mode) {
4732 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4733 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4734 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4738 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4741 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4742 return SURFACE_OPENGL;
4745 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4746 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4747 HRESULT hr;
4749 /* If there's no destination surface there is nothing to do */
4750 if(!This->overlay_dest) return WINED3D_OK;
4752 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4753 * update the overlay. Prevent an endless recursion
4755 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
4756 return WINED3D_OK;
4758 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
4759 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4760 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4761 NULL, WINED3DTEXF_LINEAR);
4762 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
4764 return hr;
4767 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4769 /* IUnknown */
4770 IWineD3DBaseSurfaceImpl_QueryInterface,
4771 IWineD3DBaseSurfaceImpl_AddRef,
4772 IWineD3DSurfaceImpl_Release,
4773 /* IWineD3DResource */
4774 IWineD3DBaseSurfaceImpl_GetParent,
4775 IWineD3DBaseSurfaceImpl_GetDevice,
4776 IWineD3DBaseSurfaceImpl_SetPrivateData,
4777 IWineD3DBaseSurfaceImpl_GetPrivateData,
4778 IWineD3DBaseSurfaceImpl_FreePrivateData,
4779 IWineD3DBaseSurfaceImpl_SetPriority,
4780 IWineD3DBaseSurfaceImpl_GetPriority,
4781 IWineD3DSurfaceImpl_PreLoad,
4782 IWineD3DSurfaceImpl_UnLoad,
4783 IWineD3DBaseSurfaceImpl_GetType,
4784 /* IWineD3DSurface */
4785 IWineD3DBaseSurfaceImpl_GetContainer,
4786 IWineD3DBaseSurfaceImpl_GetDesc,
4787 IWineD3DSurfaceImpl_LockRect,
4788 IWineD3DSurfaceImpl_UnlockRect,
4789 IWineD3DSurfaceImpl_GetDC,
4790 IWineD3DSurfaceImpl_ReleaseDC,
4791 IWineD3DSurfaceImpl_Flip,
4792 IWineD3DSurfaceImpl_Blt,
4793 IWineD3DBaseSurfaceImpl_GetBltStatus,
4794 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4795 IWineD3DBaseSurfaceImpl_IsLost,
4796 IWineD3DBaseSurfaceImpl_Restore,
4797 IWineD3DSurfaceImpl_BltFast,
4798 IWineD3DBaseSurfaceImpl_GetPalette,
4799 IWineD3DBaseSurfaceImpl_SetPalette,
4800 IWineD3DSurfaceImpl_RealizePalette,
4801 IWineD3DBaseSurfaceImpl_SetColorKey,
4802 IWineD3DBaseSurfaceImpl_GetPitch,
4803 IWineD3DSurfaceImpl_SetMem,
4804 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4805 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4806 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4807 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4808 IWineD3DBaseSurfaceImpl_SetClipper,
4809 IWineD3DBaseSurfaceImpl_GetClipper,
4810 /* Internal use: */
4811 IWineD3DSurfaceImpl_AddDirtyRect,
4812 IWineD3DSurfaceImpl_LoadTexture,
4813 IWineD3DSurfaceImpl_BindTexture,
4814 IWineD3DSurfaceImpl_SaveSnapshot,
4815 IWineD3DSurfaceImpl_SetContainer,
4816 IWineD3DSurfaceImpl_GetGlDesc,
4817 IWineD3DBaseSurfaceImpl_GetData,
4818 IWineD3DSurfaceImpl_SetFormat,
4819 IWineD3DSurfaceImpl_PrivateSetup,
4820 IWineD3DSurfaceImpl_ModifyLocation,
4821 IWineD3DSurfaceImpl_LoadLocation,
4822 IWineD3DSurfaceImpl_GetImplType,
4823 IWineD3DSurfaceImpl_DrawOverlay
4825 #undef GLINFO_LOCATION
4827 #define GLINFO_LOCATION device->adapter->gl_info
4828 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
4829 static void ffp_blit_free(IWineD3DDevice *iface) { }
4831 static HRESULT ffp_blit_set(IWineD3DDevice *iface, WINED3DFORMAT fmt, GLenum textype, UINT width, UINT height) {
4832 glEnable(textype);
4833 checkGLcall("glEnable(textype)");
4834 return WINED3D_OK;
4837 static void ffp_blit_unset(IWineD3DDevice *iface) {
4838 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4839 glDisable(GL_TEXTURE_2D);
4840 checkGLcall("glDisable(GL_TEXTURE_2D)");
4841 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP)) {
4842 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4843 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4845 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
4846 glDisable(GL_TEXTURE_RECTANGLE_ARB);
4847 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4851 static BOOL ffp_blit_color_fixup_supported(struct color_fixup_desc fixup)
4853 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4855 TRACE("Checking support for fixup:\n");
4856 dump_color_fixup_desc(fixup);
4859 /* We only support identity conversions. */
4860 if (is_identity_fixup(fixup))
4862 TRACE("[OK]\n");
4863 return TRUE;
4866 TRACE("[FAILED]\n");
4867 return FALSE;
4870 const struct blit_shader ffp_blit = {
4871 ffp_blit_alloc,
4872 ffp_blit_free,
4873 ffp_blit_set,
4874 ffp_blit_unset,
4875 ffp_blit_color_fixup_supported