push 7bb4e6a724086e39bb3a77eb03fa4dcdb495b071
[wine/hacks.git] / dlls / wined3d / surface.c
blob8fc0d28553345696ec25bbf035fce0a7453ba2b3
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 void surface_remove_pbo(IWineD3DSurfaceImpl *This);
41 static void surface_force_reload(IWineD3DSurface *iface)
43 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
45 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
48 void surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
50 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
51 GLuint *name;
52 DWORD flag;
54 if(srgb)
56 name = &This->glDescription.srgbTextureName;
57 flag = SFLAG_INSRGBTEX;
59 else
61 name = &This->glDescription.textureName;
62 flag = SFLAG_INTEXTURE;
65 TRACE("(%p) : setting texture name %u\n", This, new_name);
67 if (!*name && new_name)
69 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
70 * surface has no texture name yet. See if we can get rid of this. */
71 if (This->Flags & flag)
72 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
73 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
76 *name = new_name;
77 surface_force_reload(iface);
80 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
82 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
84 TRACE("(%p) : setting target %#x\n", This, target);
86 if (This->glDescription.target != target)
88 if (target == GL_TEXTURE_RECTANGLE_ARB)
90 This->Flags &= ~SFLAG_NORMCOORD;
92 else if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB)
94 This->Flags |= SFLAG_NORMCOORD;
97 This->glDescription.target = target;
98 surface_force_reload(iface);
101 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
102 int active_sampler;
104 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
105 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
106 * gl states. The current texture unit should always be a valid one.
108 * To be more specific, this is tricky because we can implicitly be called
109 * from sampler() in state.c. This means we can't touch anything other than
110 * whatever happens to be the currently active texture, or we would risk
111 * marking already applied sampler states dirty again.
113 * TODO: Track the current active texture per GL context instead of using glGet
115 GLint active_texture;
116 ENTER_GL();
117 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
118 LEAVE_GL();
119 active_sampler = This->resource.wineD3DDevice->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
121 if (active_sampler != -1) {
122 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_sampler));
124 IWineD3DSurface_BindTexture((IWineD3DSurface *)This, srgb);
127 /* This function checks if the primary render target uses the 8bit paletted format. */
128 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
130 if (device->render_targets && device->render_targets[0]) {
131 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
132 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
133 && (render_target->resource.format_desc->format == WINED3DFMT_P8))
134 return TRUE;
136 return FALSE;
139 /* This call just downloads data, the caller is responsible for activating the
140 * right context and binding the correct texture. */
141 static void surface_download_data(IWineD3DSurfaceImpl *This) {
142 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
144 /* Only support read back of converted P8 surfaces */
145 if (This->Flags & SFLAG_CONVERTED && format_desc->format != WINED3DFMT_P8)
147 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
148 return;
151 ENTER_GL();
153 if (format_desc->format == WINED3DFMT_DXT1 || format_desc->format == WINED3DFMT_DXT2
154 || format_desc->format == WINED3DFMT_DXT3 || format_desc->format == WINED3DFMT_DXT4
155 || format_desc->format == WINED3DFMT_DXT5 || format_desc->format == WINED3DFMT_ATI2N)
157 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
158 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
159 } else {
160 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n",
161 This, This->glDescription.level, format_desc->glFormat, format_desc->glType,
162 This->resource.allocatedMemory);
164 if(This->Flags & SFLAG_PBO) {
165 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
166 checkGLcall("glBindBufferARB");
167 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
168 checkGLcall("glGetCompressedTexImageARB()");
169 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
170 checkGLcall("glBindBufferARB");
171 } else {
172 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
173 checkGLcall("glGetCompressedTexImageARB()");
176 LEAVE_GL();
177 } else {
178 void *mem;
179 GLenum format = format_desc->glFormat;
180 GLenum type = format_desc->glType;
181 int src_pitch = 0;
182 int dst_pitch = 0;
184 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
185 if (format_desc->format == WINED3DFMT_P8 && primary_render_target_is_p8(This->resource.wineD3DDevice))
187 format = GL_ALPHA;
188 type = GL_UNSIGNED_BYTE;
191 if (This->Flags & SFLAG_NONPOW2) {
192 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
193 src_pitch = format_desc->byte_count * This->pow2Width;
194 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
195 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
196 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
197 } else {
198 mem = This->resource.allocatedMemory;
201 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
202 format, type, mem);
204 if(This->Flags & SFLAG_PBO) {
205 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
206 checkGLcall("glBindBufferARB");
208 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
209 type, NULL);
210 checkGLcall("glGetTexImage()");
212 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
213 checkGLcall("glBindBufferARB");
214 } else {
215 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
216 type, mem);
217 checkGLcall("glGetTexImage()");
219 LEAVE_GL();
221 if (This->Flags & SFLAG_NONPOW2) {
222 const BYTE *src_data;
223 BYTE *dst_data;
224 UINT y;
226 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
227 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
228 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
230 * We're doing this...
232 * instead of boxing the texture :
233 * |<-texture width ->| -->pow2width| /\
234 * |111111111111111111| | |
235 * |222 Texture 222222| boxed empty | texture height
236 * |3333 Data 33333333| | |
237 * |444444444444444444| | \/
238 * ----------------------------------- |
239 * | boxed empty | boxed empty | pow2height
240 * | | | \/
241 * -----------------------------------
244 * we're repacking the data to the expected texture width
246 * |<-texture width ->| -->pow2width| /\
247 * |111111111111111111222222222222222| |
248 * |222333333333333333333444444444444| texture height
249 * |444444 | |
250 * | | \/
251 * | | |
252 * | empty | pow2height
253 * | | \/
254 * -----------------------------------
256 * == is the same as
258 * |<-texture width ->| /\
259 * |111111111111111111|
260 * |222222222222222222|texture height
261 * |333333333333333333|
262 * |444444444444444444| \/
263 * --------------------
265 * this also means that any references to allocatedMemory should work with the data as if were a
266 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
268 * internally the texture is still stored in a boxed format so any references to textureName will
269 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
271 * Performance should not be an issue, because applications normally do not lock the surfaces when
272 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
273 * and doesn't have to be re-read.
275 src_data = mem;
276 dst_data = This->resource.allocatedMemory;
277 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
278 for (y = 1 ; y < This->currentDesc.Height; y++) {
279 /* skip the first row */
280 src_data += src_pitch;
281 dst_data += dst_pitch;
282 memcpy(dst_data, src_data, dst_pitch);
285 HeapFree(GetProcessHeap(), 0, mem);
289 /* Surface has now been downloaded */
290 This->Flags |= SFLAG_INSYSMEM;
293 /* This call just uploads data, the caller is responsible for activating the
294 * right context and binding the correct texture. */
295 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
296 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
298 if (format_desc->heightscale != 1.0 && format_desc->heightscale != 0.0) height *= format_desc->heightscale;
300 if (format_desc->format == WINED3DFMT_DXT1 || format_desc->format == WINED3DFMT_DXT2
301 || format_desc->format == WINED3DFMT_DXT3 || format_desc->format == WINED3DFMT_DXT4
302 || format_desc->format == WINED3DFMT_DXT5 || format_desc->format == WINED3DFMT_ATI2N)
304 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
305 FIXME("Using DXT1/3/5 without advertized support\n");
306 } else {
307 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
308 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
309 * function uses glCompressedTexImage2D instead of the SubImage call
311 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
312 ENTER_GL();
314 if(This->Flags & SFLAG_PBO) {
315 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
316 checkGLcall("glBindBufferARB");
317 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
319 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
320 width, height, 0 /* border */, This->resource.size, NULL));
321 checkGLcall("glCompressedTexSubImage2D");
323 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
324 checkGLcall("glBindBufferARB");
325 } else {
326 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
327 width, height, 0 /* border */, This->resource.size, data));
328 checkGLcall("glCompressedTexSubImage2D");
330 LEAVE_GL();
332 } else {
333 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
334 ENTER_GL();
336 if(This->Flags & SFLAG_PBO) {
337 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
338 checkGLcall("glBindBufferARB");
339 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
341 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
342 checkGLcall("glTexSubImage2D");
344 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
345 checkGLcall("glBindBufferARB");
347 else {
348 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
349 checkGLcall("glTexSubImage2D");
352 LEAVE_GL();
356 /* This call just allocates the texture, the caller is responsible for
357 * activating the right context and binding the correct texture. */
358 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
359 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
360 BOOL enable_client_storage = FALSE;
361 const BYTE *mem = NULL;
363 if (format_desc->heightscale != 1.0 && format_desc->heightscale != 0.0) height *= format_desc->heightscale;
365 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",
366 This, This->glDescription.target, This->glDescription.level, debug_d3dformat(format_desc->format),
367 internal, width, height, format, type);
369 if (format_desc->format == WINED3DFMT_DXT1 || format_desc->format == WINED3DFMT_DXT2
370 || format_desc->format == WINED3DFMT_DXT3 || format_desc->format == WINED3DFMT_DXT4
371 || format_desc->format == WINED3DFMT_DXT5 || format_desc->format == WINED3DFMT_ATI2N)
373 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
374 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
376 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
377 * once, unfortunately
379 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
380 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
381 This->Flags |= SFLAG_CLIENT;
382 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
383 ENTER_GL();
384 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
385 width, height, 0 /* border */, This->resource.size, mem));
386 LEAVE_GL();
389 return;
392 ENTER_GL();
394 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
395 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
396 /* In some cases we want to disable client storage.
397 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
398 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
399 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
400 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
401 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
403 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
404 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
405 This->Flags &= ~SFLAG_CLIENT;
406 enable_client_storage = TRUE;
407 } else {
408 This->Flags |= SFLAG_CLIENT;
410 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
411 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
413 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
416 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
417 checkGLcall("glTexImage2D");
419 if(enable_client_storage) {
420 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
421 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
423 LEAVE_GL();
426 /* In D3D the depth stencil dimensions have to be greater than or equal to the
427 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
428 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
429 /* GL locking is done by the caller */
430 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
431 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
432 renderbuffer_entry_t *entry;
433 GLuint renderbuffer = 0;
434 unsigned int src_width, src_height;
436 src_width = This->pow2Width;
437 src_height = This->pow2Height;
439 /* A depth stencil smaller than the render target is not valid */
440 if (width > src_width || height > src_height) return;
442 /* Remove any renderbuffer set if the sizes match */
443 if (width == src_width && height == src_height) {
444 This->current_renderbuffer = NULL;
445 return;
448 /* Look if we've already got a renderbuffer of the correct dimensions */
449 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
450 if (entry->width == width && entry->height == height) {
451 renderbuffer = entry->id;
452 This->current_renderbuffer = entry;
453 break;
457 if (!renderbuffer) {
458 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
459 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
460 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
461 This->resource.format_desc->glInternal, width, height));
463 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
464 entry->width = width;
465 entry->height = height;
466 entry->id = renderbuffer;
467 list_add_head(&This->renderbuffers, &entry->entry);
469 This->current_renderbuffer = entry;
472 checkGLcall("set_compatible_renderbuffer");
475 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
476 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
477 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
479 TRACE("(%p) : swapchain %p\n", This, swapchain);
481 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
482 TRACE("Returning GL_BACK\n");
483 return GL_BACK;
484 } else if (swapchain_impl->frontBuffer == iface) {
485 TRACE("Returning GL_FRONT\n");
486 return GL_FRONT;
489 FIXME("Higher back buffer, returning GL_BACK\n");
490 return GL_BACK;
493 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
494 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
496 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
497 IWineD3DBaseTexture *baseTexture = NULL;
499 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
500 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
502 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
503 if (dirty_rect)
505 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
506 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
507 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
508 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
510 else
512 This->dirtyRect.left = 0;
513 This->dirtyRect.top = 0;
514 This->dirtyRect.right = This->currentDesc.Width;
515 This->dirtyRect.bottom = This->currentDesc.Height;
518 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
519 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
521 /* if the container is a basetexture then mark it dirty. */
522 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
524 TRACE("Passing to container\n");
525 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
526 IWineD3DBaseTexture_Release(baseTexture);
530 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
532 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
533 ULONG ref = InterlockedDecrement(&This->resource.ref);
534 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
535 if (ref == 0) {
536 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
537 renderbuffer_entry_t *entry, *entry2;
538 TRACE("(%p) : cleaning up\n", This);
540 /* Need a context to destroy the texture. Use the currently active render target, but only if
541 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
542 * When destroying the primary rt, Uninit3D will activate a context before doing anything
544 if(device->render_targets && device->render_targets[0]) {
545 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
548 ENTER_GL();
549 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
550 TRACE("Deleting texture %d\n", This->glDescription.textureName);
551 glDeleteTextures(1, &This->glDescription.textureName);
554 if(This->Flags & SFLAG_PBO) {
555 /* Delete the PBO */
556 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
559 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
560 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
561 HeapFree(GetProcessHeap(), 0, entry);
563 LEAVE_GL();
565 if(This->Flags & SFLAG_DIBSECTION) {
566 /* Release the DC */
567 SelectObject(This->hDC, This->dib.holdbitmap);
568 DeleteDC(This->hDC);
569 /* Release the DIB section */
570 DeleteObject(This->dib.DIBsection);
571 This->dib.bitmap_data = NULL;
572 This->resource.allocatedMemory = NULL;
574 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
576 HeapFree(GetProcessHeap(), 0, This->palette9);
578 resource_cleanup((IWineD3DResource *)iface);
580 if(This->overlay_dest) {
581 list_remove(&This->overlay_entry);
584 TRACE("(%p) Released\n", This);
585 HeapFree(GetProcessHeap(), 0, This);
588 return ref;
591 /* ****************************************************
592 IWineD3DSurface IWineD3DResource parts follow
593 **************************************************** */
595 void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
597 /* TODO: check for locks */
598 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
599 IWineD3DBaseTexture *baseTexture = NULL;
600 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
602 TRACE("(%p)Checking to see if the container is a base texture\n", This);
603 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
604 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
605 TRACE("Passing to container\n");
606 tex_impl->baseTexture.internal_preload(baseTexture, SRGB_RGB);
607 IWineD3DBaseTexture_Release(baseTexture);
608 } else {
609 TRACE("(%p) : About to load surface\n", This);
611 if(!device->isInDraw) {
612 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
615 if (This->resource.format_desc->format == WINED3DFMT_P8
616 || This->resource.format_desc->format == WINED3DFMT_A8P8)
618 if(palette9_changed(This)) {
619 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
620 /* TODO: This is not necessarily needed with hw palettized texture support */
621 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
622 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
623 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
627 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
629 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
630 /* Tell opengl to try and keep this texture in video ram (well mostly) */
631 GLclampf tmp;
632 tmp = 0.9f;
633 ENTER_GL();
634 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
635 LEAVE_GL();
638 return;
641 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
642 surface_internal_preload(iface, SRGB_ANY);
645 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
646 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
647 This->resource.allocatedMemory =
648 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
650 ENTER_GL();
651 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
652 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
653 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
654 checkGLcall("glGetBufferSubData");
655 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
656 checkGLcall("glDeleteBuffers");
657 LEAVE_GL();
659 This->pbo = 0;
660 This->Flags &= ~SFLAG_PBO;
663 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
664 IWineD3DBaseTexture *texture = NULL;
665 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
666 renderbuffer_entry_t *entry, *entry2;
667 TRACE("(%p)\n", iface);
669 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
670 /* Default pool resources are supposed to be destroyed before Reset is called.
671 * Implicit resources stay however. So this means we have an implicit render target
672 * or depth stencil. The content may be destroyed, but we still have to tear down
673 * opengl resources, so we cannot leave early.
675 * Put the most up to date surface location into the drawable. D3D-wise this content
676 * is undefined, so it would be nowhere, but that would make the location management
677 * more complicated. The drawable is a sane location, because if we mark sysmem or
678 * texture up to date, drawPrim will copy the uninitialized texture or sysmem to the
679 * uninitialized drawable. That's pointless and we'd have to allocate the texture /
680 * sysmem copy here.
682 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
683 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
684 } else {
685 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
687 } else {
688 /* Load the surface into system memory */
689 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
690 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
692 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
693 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
695 /* Destroy PBOs, but load them into real sysmem before */
696 if(This->Flags & SFLAG_PBO) {
697 surface_remove_pbo(This);
700 /* Destroy fbo render buffers. This is needed for implicit render targets, for
701 * all application-created targets the application has to release the surface
702 * before calling _Reset
704 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
705 ENTER_GL();
706 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
707 LEAVE_GL();
708 list_remove(&entry->entry);
709 HeapFree(GetProcessHeap(), 0, entry);
711 list_init(&This->renderbuffers);
712 This->current_renderbuffer = NULL;
714 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
715 * destroy it
717 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
718 if(!texture) {
719 ENTER_GL();
720 glDeleteTextures(1, &This->glDescription.textureName);
721 This->glDescription.textureName = 0;
722 LEAVE_GL();
723 } else {
724 IWineD3DBaseTexture_Release(texture);
726 return;
729 /* ******************************************************
730 IWineD3DSurface IWineD3DSurface parts follow
731 ****************************************************** */
733 static void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription)
735 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
736 TRACE("(%p) : returning %p\n", This, &This->glDescription);
737 *glDescription = &This->glDescription;
740 /* Read the framebuffer back into the surface */
741 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
742 IWineD3DSwapChainImpl *swapchain;
743 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
744 BYTE *mem;
745 GLint fmt;
746 GLint type;
747 BYTE *row, *top, *bottom;
748 int i;
749 BOOL bpp;
750 RECT local_rect;
751 BOOL srcIsUpsideDown;
752 GLint rowLen = 0;
753 GLint skipPix = 0;
754 GLint skipRow = 0;
756 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
757 static BOOL warned = FALSE;
758 if(!warned) {
759 ERR("The application tries to lock the render target, but render target locking is disabled\n");
760 warned = TRUE;
762 return;
765 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
766 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
767 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
768 * context->last_was_blit set on the unlock.
770 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
771 ENTER_GL();
773 /* Select the correct read buffer, and give some debug output.
774 * There is no need to keep track of the current read buffer or reset it, every part of the code
775 * that reads sets the read buffer as desired.
777 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
779 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
780 TRACE("Locking %#x buffer\n", buffer);
781 glReadBuffer(buffer);
782 checkGLcall("glReadBuffer");
784 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
785 srcIsUpsideDown = FALSE;
786 } else {
787 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
788 * Read from the back buffer
790 TRACE("Locking offscreen render target\n");
791 glReadBuffer(myDevice->offscreenBuffer);
792 srcIsUpsideDown = TRUE;
795 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
796 if(!rect) {
797 local_rect.left = 0;
798 local_rect.top = 0;
799 local_rect.right = This->currentDesc.Width;
800 local_rect.bottom = This->currentDesc.Height;
801 } else {
802 local_rect = *rect;
804 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
806 switch(This->resource.format_desc->format)
808 case WINED3DFMT_P8:
810 if(primary_render_target_is_p8(myDevice)) {
811 /* In case of P8 render targets the index is stored in the alpha component */
812 fmt = GL_ALPHA;
813 type = GL_UNSIGNED_BYTE;
814 mem = dest;
815 bpp = This->resource.format_desc->byte_count;
816 } else {
817 /* GL can't return palettized data, so read ARGB pixels into a
818 * separate block of memory and convert them into palettized format
819 * in software. Slow, but if the app means to use palettized render
820 * targets and locks it...
822 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
823 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
824 * for the color channels when palettizing the colors.
826 fmt = GL_RGB;
827 type = GL_UNSIGNED_BYTE;
828 pitch *= 3;
829 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
830 if(!mem) {
831 ERR("Out of memory\n");
832 LEAVE_GL();
833 return;
835 bpp = This->resource.format_desc->byte_count * 3;
838 break;
840 default:
841 mem = dest;
842 fmt = This->resource.format_desc->glFormat;
843 type = This->resource.format_desc->glType;
844 bpp = This->resource.format_desc->byte_count;
847 if(This->Flags & SFLAG_PBO) {
848 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
849 checkGLcall("glBindBufferARB");
850 if(mem != NULL) {
851 ERR("mem not null for pbo -- unexpected\n");
852 mem = NULL;
856 /* Save old pixel store pack state */
857 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
858 checkGLcall("glIntegerv");
859 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
860 checkGLcall("glIntegerv");
861 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
862 checkGLcall("glIntegerv");
864 /* Setup pixel store pack state -- to glReadPixels into the correct place */
865 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
866 checkGLcall("glPixelStorei");
867 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
868 checkGLcall("glPixelStorei");
869 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
870 checkGLcall("glPixelStorei");
872 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
873 local_rect.right - local_rect.left,
874 local_rect.bottom - local_rect.top,
875 fmt, type, mem);
876 checkGLcall("glReadPixels");
878 /* Reset previous pixel store pack state */
879 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
880 checkGLcall("glPixelStorei");
881 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
882 checkGLcall("glPixelStorei");
883 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
884 checkGLcall("glPixelStorei");
886 if(This->Flags & SFLAG_PBO) {
887 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
888 checkGLcall("glBindBufferARB");
890 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
891 * to get a pointer to it and perform the flipping in software. This is a lot
892 * faster than calling glReadPixels for each line. In case we want more speed
893 * we should rerender it flipped in a FBO and read the data back from the FBO. */
894 if(!srcIsUpsideDown) {
895 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
896 checkGLcall("glBindBufferARB");
898 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
899 checkGLcall("glMapBufferARB");
903 /* TODO: Merge this with the palettization loop below for P8 targets */
904 if(!srcIsUpsideDown) {
905 UINT len, off;
906 /* glReadPixels returns the image upside down, and there is no way to prevent this.
907 Flip the lines in software */
908 len = (local_rect.right - local_rect.left) * bpp;
909 off = local_rect.left * bpp;
911 row = HeapAlloc(GetProcessHeap(), 0, len);
912 if(!row) {
913 ERR("Out of memory\n");
914 if (This->resource.format_desc->format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
915 LEAVE_GL();
916 return;
919 top = mem + pitch * local_rect.top;
920 bottom = mem + pitch * (local_rect.bottom - 1);
921 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
922 memcpy(row, top + off, len);
923 memcpy(top + off, bottom + off, len);
924 memcpy(bottom + off, row, len);
925 top += pitch;
926 bottom -= pitch;
928 HeapFree(GetProcessHeap(), 0, row);
930 /* Unmap the temp PBO buffer */
931 if(This->Flags & SFLAG_PBO) {
932 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
933 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
937 LEAVE_GL();
939 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
940 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
941 * the same color but we have no choice.
942 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
944 if ((This->resource.format_desc->format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice))
946 const PALETTEENTRY *pal = NULL;
947 DWORD width = pitch / 3;
948 int x, y, c;
950 if(This->palette) {
951 pal = This->palette->palents;
952 } else {
953 ERR("Palette is missing, cannot perform inverse palette lookup\n");
954 HeapFree(GetProcessHeap(), 0, mem);
955 return ;
958 for(y = local_rect.top; y < local_rect.bottom; y++) {
959 for(x = local_rect.left; x < local_rect.right; x++) {
960 /* start lines pixels */
961 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
962 const BYTE *green = blue + 1;
963 const BYTE *red = green + 1;
965 for(c = 0; c < 256; c++) {
966 if(*red == pal[c].peRed &&
967 *green == pal[c].peGreen &&
968 *blue == pal[c].peBlue)
970 *((BYTE *) dest + y * width + x) = c;
971 break;
976 HeapFree(GetProcessHeap(), 0, mem);
980 /* Read the framebuffer contents into a texture */
981 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
983 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
984 IWineD3DSwapChainImpl *swapchain;
985 int bpp;
986 GLenum format, internal, type;
987 CONVERT_TYPES convert;
988 GLint prevRead;
989 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
991 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
993 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
994 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
995 * states in the stateblock, and no driver was found yet that had bugs in that regard.
997 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
998 surface_bind_and_dirtify(This, srgb);
1000 ENTER_GL();
1001 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1002 LEAVE_GL();
1004 /* Select the correct read buffer, and give some debug output.
1005 * There is no need to keep track of the current read buffer or reset it, every part of the code
1006 * that reads sets the read buffer as desired.
1008 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
1010 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1011 TRACE("Locking %#x buffer\n", buffer);
1013 ENTER_GL();
1014 glReadBuffer(buffer);
1015 checkGLcall("glReadBuffer");
1016 LEAVE_GL();
1018 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1019 } else {
1020 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1021 * Read from the back buffer
1023 TRACE("Locking offscreen render target\n");
1024 ENTER_GL();
1025 glReadBuffer(device->offscreenBuffer);
1026 checkGLcall("glReadBuffer");
1027 LEAVE_GL();
1030 if(!(This->Flags & alloc_flag)) {
1031 surface_allocate_surface(This, internal, This->pow2Width,
1032 This->pow2Height, format, type);
1033 This->Flags |= alloc_flag;
1036 ENTER_GL();
1037 /* If !SrcIsUpsideDown we should flip the surface.
1038 * This can be done using glCopyTexSubImage2D but this
1039 * is VERY slow, so don't do that. We should prevent
1040 * this code from getting called in such cases or perhaps
1041 * we can use FBOs */
1043 glCopyTexSubImage2D(This->glDescription.target,
1044 This->glDescription.level,
1045 0, 0, 0, 0,
1046 This->currentDesc.Width,
1047 This->currentDesc.Height);
1048 checkGLcall("glCopyTexSubImage2D");
1050 glReadBuffer(prevRead);
1051 checkGLcall("glReadBuffer");
1053 LEAVE_GL();
1054 TRACE("Updated target %d\n", This->glDescription.target);
1057 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
1058 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1059 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1060 * changed
1062 if(!(This->Flags & SFLAG_DYNLOCK)) {
1063 This->lockCount++;
1064 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1065 if(This->lockCount > MAXLOCKCOUNT) {
1066 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1067 This->Flags |= SFLAG_DYNLOCK;
1071 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1072 * Also don't create a PBO for systemmem surfaces.
1074 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
1075 GLenum error;
1076 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1078 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1079 ENTER_GL();
1081 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1082 error = glGetError();
1083 if(This->pbo == 0 || error != GL_NO_ERROR) {
1084 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1087 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1089 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1090 checkGLcall("glBindBufferARB");
1092 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1093 checkGLcall("glBufferDataARB");
1095 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1096 checkGLcall("glBindBufferARB");
1098 /* We don't need the system memory anymore and we can't even use it for PBOs */
1099 if(!(This->Flags & SFLAG_CLIENT)) {
1100 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1101 This->resource.heapMemory = NULL;
1103 This->resource.allocatedMemory = NULL;
1104 This->Flags |= SFLAG_PBO;
1105 LEAVE_GL();
1106 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
1107 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1108 * or a pbo to map
1110 if(!This->resource.heapMemory) {
1111 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1113 This->resource.allocatedMemory =
1114 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1115 if(This->Flags & SFLAG_INSYSMEM) {
1116 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1121 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1122 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1123 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1125 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1127 /* This is also done in the base class, but we have to verify this before loading any data from
1128 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1129 * may interfere, and all other bad things may happen
1131 if (This->Flags & SFLAG_LOCKED) {
1132 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1133 return WINED3DERR_INVALIDCALL;
1135 This->Flags |= SFLAG_LOCKED;
1137 if (!(This->Flags & SFLAG_LOCKABLE))
1139 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1142 if (Flags & WINED3DLOCK_DISCARD) {
1143 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1144 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1145 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1146 This->Flags |= SFLAG_INSYSMEM;
1147 goto lock_end;
1150 if (This->Flags & SFLAG_INSYSMEM) {
1151 TRACE("Local copy is up to date, not downloading data\n");
1152 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1153 goto lock_end;
1156 /* Now download the surface content from opengl
1157 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1158 * Offscreen targets which are not active at the moment or are higher targets(FBOs) can be locked with the texture path
1160 if ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])
1162 const RECT *pass_rect = pRect;
1164 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1165 * because most caller functions do not need that. So do that here
1167 if(pRect &&
1168 pRect->top == 0 &&
1169 pRect->left == 0 &&
1170 pRect->right == This->currentDesc.Width &&
1171 pRect->bottom == This->currentDesc.Height) {
1172 pass_rect = NULL;
1175 switch(wined3d_settings.rendertargetlock_mode) {
1176 case RTL_TEXDRAW:
1177 case RTL_TEXTEX:
1178 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1179 #if 0
1180 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1181 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1182 * This may be faster on some cards
1184 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1185 #endif
1186 /* drop through */
1188 case RTL_AUTO:
1189 case RTL_READDRAW:
1190 case RTL_READTEX:
1191 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1192 break;
1194 case RTL_DISABLE:
1195 break;
1197 } else if(iface == myDevice->stencilBufferTarget) {
1198 /** the depth stencil in openGL has a format of GL_FLOAT
1199 * which should be good for WINED3DFMT_D16_LOCKABLE
1200 * and WINED3DFMT_D16
1201 * it is unclear what format the stencil buffer is in except.
1202 * 'Each index is converted to fixed point...
1203 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1204 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1205 * glReadPixels(This->lockedRect.left,
1206 * This->lockedRect.bottom - j - 1,
1207 * This->lockedRect.right - This->lockedRect.left,
1208 * 1,
1209 * GL_DEPTH_COMPONENT,
1210 * type,
1211 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1213 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1214 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1215 * none of that is the case the problem is not in this function :-)
1216 ********************************************/
1217 FIXME("Depth stencil locking not supported yet\n");
1218 } else {
1219 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1220 TRACE("locking an ordinary surface\n");
1221 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1224 lock_end:
1225 if(This->Flags & SFLAG_PBO) {
1226 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1227 ENTER_GL();
1228 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1229 checkGLcall("glBindBufferARB");
1231 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1232 if(This->resource.allocatedMemory) {
1233 ERR("The surface already has PBO memory allocated!\n");
1236 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1237 checkGLcall("glMapBufferARB");
1239 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1240 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1241 checkGLcall("glBindBufferARB");
1243 LEAVE_GL();
1246 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1247 /* Don't dirtify */
1248 } else {
1249 IWineD3DBaseTexture *pBaseTexture;
1251 * Dirtify on lock
1252 * as seen in msdn docs
1254 surface_add_dirty_rect(iface, pRect);
1256 /** Dirtify Container if needed */
1257 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1258 TRACE("Making container dirty\n");
1259 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1260 IWineD3DBaseTexture_Release(pBaseTexture);
1261 } else {
1262 TRACE("Surface is standalone, no need to dirty the container\n");
1266 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1269 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1270 GLint prev_store;
1271 GLint prev_rasterpos[4];
1272 GLint skipBytes = 0;
1273 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1274 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1275 IWineD3DSwapChainImpl *swapchain;
1277 /* Activate the correct context for the render target */
1278 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1279 ENTER_GL();
1281 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
1282 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1283 TRACE("Unlocking %#x buffer\n", buffer);
1284 glDrawBuffer(buffer);
1285 checkGLcall("glDrawBuffer");
1287 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1288 } else {
1289 /* Primary offscreen render target */
1290 TRACE("Offscreen render target\n");
1291 glDrawBuffer(myDevice->offscreenBuffer);
1292 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1295 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1296 checkGLcall("glIntegerv");
1297 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1298 checkGLcall("glIntegerv");
1299 glPixelZoom(1.0, -1.0);
1300 checkGLcall("glPixelZoom");
1302 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1303 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1304 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1306 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1307 checkGLcall("glRasterPos2f");
1309 /* Some drivers(radeon dri, others?) don't like exceptions during
1310 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1311 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1312 * catch to put the dib section in InSync mode, which leads to a crash
1313 * and a blocked x server on my radeon card.
1315 * The following lines read the dib section so it is put in InSync mode
1316 * before glDrawPixels is called and the crash is prevented. There won't
1317 * be any interfering gdi accesses, because UnlockRect is called from
1318 * ReleaseDC, and the app won't use the dc any more afterwards.
1320 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1321 volatile BYTE read;
1322 read = This->resource.allocatedMemory[0];
1325 if(This->Flags & SFLAG_PBO) {
1326 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1327 checkGLcall("glBindBufferARB");
1330 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1331 if(This->Flags & SFLAG_LOCKED) {
1332 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1333 (This->lockedRect.bottom - This->lockedRect.top)-1,
1334 fmt, type,
1335 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1336 checkGLcall("glDrawPixels");
1337 } else {
1338 glDrawPixels(This->currentDesc.Width,
1339 This->currentDesc.Height,
1340 fmt, type, mem);
1341 checkGLcall("glDrawPixels");
1344 if(This->Flags & SFLAG_PBO) {
1345 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1346 checkGLcall("glBindBufferARB");
1349 glPixelZoom(1.0,1.0);
1350 checkGLcall("glPixelZoom");
1352 glRasterPos3iv(&prev_rasterpos[0]);
1353 checkGLcall("glRasterPos3iv");
1355 /* Reset to previous pack row length */
1356 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1357 checkGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1359 if(!swapchain) {
1360 glDrawBuffer(myDevice->offscreenBuffer);
1361 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1362 } else if(swapchain->backBuffer) {
1363 glDrawBuffer(GL_BACK);
1364 checkGLcall("glDrawBuffer(GL_BACK)");
1365 } else {
1366 glDrawBuffer(GL_FRONT);
1367 checkGLcall("glDrawBuffer(GL_FRONT)");
1369 LEAVE_GL();
1371 return;
1374 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1375 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1376 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1377 BOOL fullsurface;
1379 if (!(This->Flags & SFLAG_LOCKED)) {
1380 WARN("trying to Unlock an unlocked surf@%p\n", This);
1381 return WINEDDERR_NOTLOCKED;
1384 if (This->Flags & SFLAG_PBO) {
1385 TRACE("Freeing PBO memory\n");
1386 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1387 ENTER_GL();
1388 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1389 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1390 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1391 checkGLcall("glUnmapBufferARB");
1392 LEAVE_GL();
1393 This->resource.allocatedMemory = NULL;
1396 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1398 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1399 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1400 goto unlock_end;
1403 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1405 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1406 static BOOL warned = FALSE;
1407 if(!warned) {
1408 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1409 warned = TRUE;
1411 goto unlock_end;
1414 if(This->dirtyRect.left == 0 &&
1415 This->dirtyRect.top == 0 &&
1416 This->dirtyRect.right == This->currentDesc.Width &&
1417 This->dirtyRect.bottom == This->currentDesc.Height) {
1418 fullsurface = TRUE;
1419 } else {
1420 /* TODO: Proper partial rectangle tracking */
1421 fullsurface = FALSE;
1422 This->Flags |= SFLAG_INSYSMEM;
1425 switch(wined3d_settings.rendertargetlock_mode) {
1426 case RTL_READTEX:
1427 case RTL_TEXTEX:
1428 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1429 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1430 /* drop through */
1432 case RTL_AUTO:
1433 case RTL_READDRAW:
1434 case RTL_TEXDRAW:
1435 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1436 break;
1439 if(!fullsurface) {
1440 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1441 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1442 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1443 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1444 * not fully up to date because only a subrectangle was read in LockRect.
1446 This->Flags &= ~SFLAG_INSYSMEM;
1447 This->Flags |= SFLAG_INDRAWABLE;
1450 This->dirtyRect.left = This->currentDesc.Width;
1451 This->dirtyRect.top = This->currentDesc.Height;
1452 This->dirtyRect.right = 0;
1453 This->dirtyRect.bottom = 0;
1454 } else if(iface == myDevice->stencilBufferTarget) {
1455 FIXME("Depth Stencil buffer locking is not implemented\n");
1456 } else {
1457 /* The rest should be a normal texture */
1458 IWineD3DBaseTextureImpl *impl;
1459 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1460 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1461 * states need resetting
1463 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1464 if(impl->baseTexture.bindCount) {
1465 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1467 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1471 unlock_end:
1472 This->Flags &= ~SFLAG_LOCKED;
1473 memset(&This->lockedRect, 0, sizeof(RECT));
1475 /* Overlays have to be redrawn manually after changes with the GL implementation */
1476 if(This->overlay_dest) {
1477 IWineD3DSurface_DrawOverlay(iface);
1479 return WINED3D_OK;
1482 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
1484 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1485 WINED3DLOCKED_RECT lock;
1486 HRESULT hr;
1487 RGBQUAD col[256];
1489 TRACE("(%p)->(%p)\n",This,pHDC);
1491 if(This->Flags & SFLAG_USERPTR) {
1492 ERR("Not supported on surfaces with an application-provided surfaces\n");
1493 return WINEDDERR_NODC;
1496 /* Give more detailed info for ddraw */
1497 if (This->Flags & SFLAG_DCINUSE)
1498 return WINEDDERR_DCALREADYCREATED;
1500 /* Can't GetDC if the surface is locked */
1501 if (This->Flags & SFLAG_LOCKED)
1502 return WINED3DERR_INVALIDCALL;
1504 /* According to Direct3D9 docs, only these formats are supported */
1505 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1506 if (This->resource.format_desc->format != WINED3DFMT_R5G6B5
1507 && This->resource.format_desc->format != WINED3DFMT_X1R5G5B5
1508 && This->resource.format_desc->format != WINED3DFMT_R8G8B8
1509 && This->resource.format_desc->format != WINED3DFMT_X8R8G8B8)
1510 return WINED3DERR_INVALIDCALL;
1513 memset(&lock, 0, sizeof(lock)); /* To be sure */
1515 /* Create a DIB section if there isn't a hdc yet */
1516 if(!This->hDC) {
1517 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1518 if(This->Flags & SFLAG_CLIENT) {
1519 surface_internal_preload(iface, SRGB_RGB);
1522 /* Use the dib section from now on if we are not using a PBO */
1523 if(!(This->Flags & SFLAG_PBO))
1524 This->resource.allocatedMemory = This->dib.bitmap_data;
1527 /* Lock the surface */
1528 hr = IWineD3DSurface_LockRect(iface,
1529 &lock,
1530 NULL,
1533 if(This->Flags & SFLAG_PBO) {
1534 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1535 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1538 if(FAILED(hr)) {
1539 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1540 /* keep the dib section */
1541 return hr;
1544 if (This->resource.format_desc->format == WINED3DFMT_P8
1545 || This->resource.format_desc->format == WINED3DFMT_A8P8)
1547 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1548 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1549 unsigned int n;
1550 const PALETTEENTRY *pal = NULL;
1552 if(This->palette) {
1553 pal = This->palette->palents;
1554 } else {
1555 IWineD3DSurfaceImpl *dds_primary;
1556 IWineD3DSwapChainImpl *swapchain;
1557 swapchain = (IWineD3DSwapChainImpl *)This->resource.wineD3DDevice->swapchains[0];
1558 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1559 if (dds_primary && dds_primary->palette)
1560 pal = dds_primary->palette->palents;
1563 if (pal) {
1564 for (n=0; n<256; n++) {
1565 col[n].rgbRed = pal[n].peRed;
1566 col[n].rgbGreen = pal[n].peGreen;
1567 col[n].rgbBlue = pal[n].peBlue;
1568 col[n].rgbReserved = 0;
1570 SetDIBColorTable(This->hDC, 0, 256, col);
1574 *pHDC = This->hDC;
1575 TRACE("returning %p\n",*pHDC);
1576 This->Flags |= SFLAG_DCINUSE;
1578 return WINED3D_OK;
1581 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
1583 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1585 TRACE("(%p)->(%p)\n",This,hDC);
1587 if (!(This->Flags & SFLAG_DCINUSE))
1588 return WINEDDERR_NODC;
1590 if (This->hDC !=hDC) {
1591 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1592 return WINEDDERR_NODC;
1595 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1596 /* Copy the contents of the DIB over to the PBO */
1597 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1600 /* we locked first, so unlock now */
1601 IWineD3DSurface_UnlockRect(iface);
1603 This->Flags &= ~SFLAG_DCINUSE;
1605 return WINED3D_OK;
1608 /* ******************************************************
1609 IWineD3DSurface Internal (No mapping to directx api) parts follow
1610 ****************************************************** */
1612 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) {
1613 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1614 const struct GlPixelFormatDesc *glDesc = This->resource.format_desc;
1615 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1617 /* Default values: From the surface */
1618 *format = glDesc->glFormat;
1619 *type = glDesc->glType;
1620 *convert = NO_CONVERSION;
1621 *target_bpp = glDesc->byte_count;
1623 if(srgb_mode) {
1624 *internal = glDesc->glGammaInternal;
1626 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
1627 && !(This->Flags & SFLAG_SWAPCHAIN))
1629 *internal = glDesc->rtInternal;
1630 } else {
1631 *internal = glDesc->glInternal;
1634 /* Ok, now look if we have to do any conversion */
1635 switch(This->resource.format_desc->format)
1637 case WINED3DFMT_P8:
1638 /* ****************
1639 Paletted Texture
1640 **************** */
1642 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1643 * of the two is available make sure texturing is requested as neither of the two works in
1644 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1645 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1646 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1647 * conflicts with this.
1649 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) ||
1650 (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) &&
1651 device->render_targets &&
1652 This == (IWineD3DSurfaceImpl*)device->render_targets[0])) ||
1653 colorkey_active || !use_texturing ) {
1654 *format = GL_RGBA;
1655 *internal = GL_RGBA;
1656 *type = GL_UNSIGNED_BYTE;
1657 *target_bpp = 4;
1658 if(colorkey_active) {
1659 *convert = CONVERT_PALETTED_CK;
1660 } else {
1661 *convert = CONVERT_PALETTED;
1664 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1665 *format = GL_ALPHA;
1666 *internal = GL_RGBA;
1667 *type = GL_UNSIGNED_BYTE;
1668 *target_bpp = 1;
1671 break;
1673 case WINED3DFMT_R3G3B2:
1674 /* **********************
1675 GL_UNSIGNED_BYTE_3_3_2
1676 ********************** */
1677 if (colorkey_active) {
1678 /* This texture format will never be used.. So do not care about color keying
1679 up until the point in time it will be needed :-) */
1680 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1682 break;
1684 case WINED3DFMT_R5G6B5:
1685 if (colorkey_active) {
1686 *convert = CONVERT_CK_565;
1687 *format = GL_RGBA;
1688 *internal = GL_RGBA;
1689 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1691 break;
1693 case WINED3DFMT_X1R5G5B5:
1694 if (colorkey_active) {
1695 *convert = CONVERT_CK_5551;
1696 *format = GL_BGRA;
1697 *internal = GL_RGBA;
1698 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1700 break;
1702 case WINED3DFMT_R8G8B8:
1703 if (colorkey_active) {
1704 *convert = CONVERT_CK_RGB24;
1705 *format = GL_RGBA;
1706 *internal = GL_RGBA;
1707 *type = GL_UNSIGNED_INT_8_8_8_8;
1708 *target_bpp = 4;
1710 break;
1712 case WINED3DFMT_X8R8G8B8:
1713 if (colorkey_active) {
1714 *convert = CONVERT_RGB32_888;
1715 *format = GL_RGBA;
1716 *internal = GL_RGBA;
1717 *type = GL_UNSIGNED_INT_8_8_8_8;
1719 break;
1721 case WINED3DFMT_R8G8_SNORM:
1722 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1723 *convert = CONVERT_V8U8;
1724 *format = GL_BGR;
1725 *internal = GL_RGB8;
1726 *type = GL_UNSIGNED_BYTE;
1727 *target_bpp = 3;
1728 break;
1730 case WINED3DFMT_L6V5U5:
1731 *convert = CONVERT_L6V5U5;
1732 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1733 *target_bpp = 3;
1734 /* Use format and types from table */
1735 } else {
1736 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1737 *target_bpp = 2;
1738 *format = GL_RGB;
1739 *internal = GL_RGB5;
1740 *type = GL_UNSIGNED_SHORT_5_6_5;
1742 break;
1744 case WINED3DFMT_X8L8V8U8:
1745 *convert = CONVERT_X8L8V8U8;
1746 *target_bpp = 4;
1747 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1748 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1749 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1750 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1751 * the needed type and format parameter, so the internal format contains a
1752 * 4th component, which is returned as alpha
1754 } else {
1755 *format = GL_BGRA;
1756 *internal = GL_RGB8;
1757 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1759 break;
1761 case WINED3DFMT_R8G8B8A8_SNORM:
1762 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1763 *convert = CONVERT_Q8W8V8U8;
1764 *format = GL_BGRA;
1765 *internal = GL_RGBA8;
1766 *type = GL_UNSIGNED_BYTE;
1767 *target_bpp = 4;
1768 break;
1770 case WINED3DFMT_R16G16_SNORM:
1771 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1772 *convert = CONVERT_V16U16;
1773 *format = GL_BGR;
1774 *internal = GL_RGB16_EXT;
1775 *type = GL_UNSIGNED_SHORT;
1776 *target_bpp = 6;
1777 break;
1779 case WINED3DFMT_A4L4:
1780 /* A4L4 exists as an internal gl format, but for some reason there is not
1781 * format+type combination to load it. Thus convert it to A8L8, then load it
1782 * with A4L4 internal, but A8L8 format+type
1784 *convert = CONVERT_A4L4;
1785 *format = GL_LUMINANCE_ALPHA;
1786 *internal = GL_LUMINANCE4_ALPHA4;
1787 *type = GL_UNSIGNED_BYTE;
1788 *target_bpp = 2;
1789 break;
1791 case WINED3DFMT_R16G16_UNORM:
1792 *convert = CONVERT_G16R16;
1793 *format = GL_RGB;
1794 *internal = GL_RGB16_EXT;
1795 *type = GL_UNSIGNED_SHORT;
1796 *target_bpp = 6;
1797 break;
1799 case WINED3DFMT_R16G16_FLOAT:
1800 *convert = CONVERT_R16G16F;
1801 *format = GL_RGB;
1802 *internal = GL_RGB16F_ARB;
1803 *type = GL_HALF_FLOAT_ARB;
1804 *target_bpp = 6;
1805 break;
1807 case WINED3DFMT_R32G32_FLOAT:
1808 *convert = CONVERT_R32G32F;
1809 *format = GL_RGB;
1810 *internal = GL_RGB32F_ARB;
1811 *type = GL_FLOAT;
1812 *target_bpp = 12;
1813 break;
1815 default:
1816 break;
1819 return WINED3D_OK;
1822 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
1823 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
1825 const BYTE *source;
1826 BYTE *dest;
1827 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1829 switch (convert) {
1830 case NO_CONVERSION:
1832 memcpy(dst, src, pitch * height);
1833 break;
1835 case CONVERT_PALETTED:
1836 case CONVERT_PALETTED_CK:
1838 IWineD3DPaletteImpl* pal = This->palette;
1839 BYTE table[256][4];
1840 unsigned int x, y;
1842 if( pal == NULL) {
1843 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1846 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1848 for (y = 0; y < height; y++)
1850 source = src + pitch * y;
1851 dest = dst + outpitch * y;
1852 /* This is an 1 bpp format, using the width here is fine */
1853 for (x = 0; x < width; x++) {
1854 BYTE color = *source++;
1855 *dest++ = table[color][0];
1856 *dest++ = table[color][1];
1857 *dest++ = table[color][2];
1858 *dest++ = table[color][3];
1862 break;
1864 case CONVERT_CK_565:
1866 /* Converting the 565 format in 5551 packed to emulate color-keying.
1868 Note : in all these conversion, it would be best to average the averaging
1869 pixels to get the color of the pixel that will be color-keyed to
1870 prevent 'color bleeding'. This will be done later on if ever it is
1871 too visible.
1873 Note2: Nvidia documents say that their driver does not support alpha + color keying
1874 on the same surface and disables color keying in such a case
1876 unsigned int x, y;
1877 const WORD *Source;
1878 WORD *Dest;
1880 TRACE("Color keyed 565\n");
1882 for (y = 0; y < height; y++) {
1883 Source = (const WORD *)(src + y * pitch);
1884 Dest = (WORD *) (dst + y * outpitch);
1885 for (x = 0; x < width; x++ ) {
1886 WORD color = *Source++;
1887 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1888 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1889 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1890 *Dest |= 0x0001;
1892 Dest++;
1896 break;
1898 case CONVERT_CK_5551:
1900 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1901 unsigned int x, y;
1902 const WORD *Source;
1903 WORD *Dest;
1904 TRACE("Color keyed 5551\n");
1905 for (y = 0; y < height; y++) {
1906 Source = (const WORD *)(src + y * pitch);
1907 Dest = (WORD *) (dst + y * outpitch);
1908 for (x = 0; x < width; x++ ) {
1909 WORD color = *Source++;
1910 *Dest = color;
1911 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1912 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1913 *Dest |= (1 << 15);
1915 else {
1916 *Dest &= ~(1 << 15);
1918 Dest++;
1922 break;
1924 case CONVERT_CK_RGB24:
1926 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
1927 unsigned int x, y;
1928 for (y = 0; y < height; y++)
1930 source = src + pitch * y;
1931 dest = dst + outpitch * y;
1932 for (x = 0; x < width; x++) {
1933 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
1934 DWORD dstcolor = color << 8;
1935 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1936 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1937 dstcolor |= 0xff;
1939 *(DWORD*)dest = dstcolor;
1940 source += 3;
1941 dest += 4;
1945 break;
1947 case CONVERT_RGB32_888:
1949 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
1950 unsigned int x, y;
1951 for (y = 0; y < height; y++)
1953 source = src + pitch * y;
1954 dest = dst + outpitch * y;
1955 for (x = 0; x < width; x++) {
1956 DWORD color = 0xffffff & *(const DWORD*)source;
1957 DWORD dstcolor = color << 8;
1958 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1959 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1960 dstcolor |= 0xff;
1962 *(DWORD*)dest = dstcolor;
1963 source += 4;
1964 dest += 4;
1968 break;
1970 case CONVERT_V8U8:
1972 unsigned int x, y;
1973 const short *Source;
1974 unsigned char *Dest;
1975 for(y = 0; y < height; y++) {
1976 Source = (const short *)(src + y * pitch);
1977 Dest = dst + y * outpitch;
1978 for (x = 0; x < width; x++ ) {
1979 long color = (*Source++);
1980 /* B */ Dest[0] = 0xff;
1981 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1982 /* R */ Dest[2] = (color) + 128; /* U */
1983 Dest += 3;
1986 break;
1989 case CONVERT_V16U16:
1991 unsigned int x, y;
1992 const DWORD *Source;
1993 unsigned short *Dest;
1994 for(y = 0; y < height; y++) {
1995 Source = (const DWORD *)(src + y * pitch);
1996 Dest = (unsigned short *) (dst + y * outpitch);
1997 for (x = 0; x < width; x++ ) {
1998 DWORD color = (*Source++);
1999 /* B */ Dest[0] = 0xffff;
2000 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
2001 /* R */ Dest[2] = (color ) + 32768; /* U */
2002 Dest += 3;
2005 break;
2008 case CONVERT_Q8W8V8U8:
2010 unsigned int x, y;
2011 const DWORD *Source;
2012 unsigned char *Dest;
2013 for(y = 0; y < height; y++) {
2014 Source = (const DWORD *)(src + y * pitch);
2015 Dest = dst + y * outpitch;
2016 for (x = 0; x < width; x++ ) {
2017 long color = (*Source++);
2018 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
2019 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2020 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2021 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
2022 Dest += 4;
2025 break;
2028 case CONVERT_L6V5U5:
2030 unsigned int x, y;
2031 const WORD *Source;
2032 unsigned char *Dest;
2034 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2035 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
2036 * fixed function and shaders without further conversion once the surface is
2037 * loaded
2039 for(y = 0; y < height; y++) {
2040 Source = (const WORD *)(src + y * pitch);
2041 Dest = dst + y * outpitch;
2042 for (x = 0; x < width; x++ ) {
2043 short color = (*Source++);
2044 unsigned char l = ((color >> 10) & 0xfc);
2045 char v = ((color >> 5) & 0x3e);
2046 char u = ((color ) & 0x1f);
2048 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
2049 * and doubles the positive range. Thus shift left only once, gl does the 2nd
2050 * shift. GL reads a signed value and converts it into an unsigned value.
2052 /* M */ Dest[2] = l << 1;
2054 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
2055 * from 5 bit values to 8 bit values.
2057 /* V */ Dest[1] = v << 3;
2058 /* U */ Dest[0] = u << 3;
2059 Dest += 3;
2062 } else {
2063 for(y = 0; y < height; y++) {
2064 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
2065 Source = (const WORD *)(src + y * pitch);
2066 for (x = 0; x < width; x++ ) {
2067 short color = (*Source++);
2068 unsigned char l = ((color >> 10) & 0xfc);
2069 short v = ((color >> 5) & 0x3e);
2070 short u = ((color ) & 0x1f);
2071 short v_conv = v + 16;
2072 short u_conv = u + 16;
2074 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
2075 Dest_s += 1;
2079 break;
2082 case CONVERT_X8L8V8U8:
2084 unsigned int x, y;
2085 const DWORD *Source;
2086 unsigned char *Dest;
2088 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2089 /* This implementation works with the fixed function pipeline and shaders
2090 * without further modification after converting the surface.
2092 for(y = 0; y < height; y++) {
2093 Source = (const DWORD *)(src + y * pitch);
2094 Dest = dst + y * outpitch;
2095 for (x = 0; x < width; x++ ) {
2096 long color = (*Source++);
2097 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2098 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2099 /* U */ Dest[0] = (color & 0xff); /* U */
2100 /* I */ Dest[3] = 255; /* X */
2101 Dest += 4;
2104 } else {
2105 /* Doesn't work correctly with the fixed function pipeline, but can work in
2106 * shaders if the shader is adjusted. (There's no use for this format in gl's
2107 * standard fixed function pipeline anyway).
2109 for(y = 0; y < height; y++) {
2110 Source = (const DWORD *)(src + y * pitch);
2111 Dest = dst + y * outpitch;
2112 for (x = 0; x < width; x++ ) {
2113 long color = (*Source++);
2114 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2115 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2116 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2117 Dest += 4;
2121 break;
2124 case CONVERT_A4L4:
2126 unsigned int x, y;
2127 const unsigned char *Source;
2128 unsigned char *Dest;
2129 for(y = 0; y < height; y++) {
2130 Source = src + y * pitch;
2131 Dest = dst + y * outpitch;
2132 for (x = 0; x < width; x++ ) {
2133 unsigned char color = (*Source++);
2134 /* A */ Dest[1] = (color & 0xf0) << 0;
2135 /* L */ Dest[0] = (color & 0x0f) << 4;
2136 Dest += 2;
2139 break;
2142 case CONVERT_G16R16:
2143 case CONVERT_R16G16F:
2145 unsigned int x, y;
2146 const WORD *Source;
2147 WORD *Dest;
2149 for(y = 0; y < height; y++) {
2150 Source = (const WORD *)(src + y * pitch);
2151 Dest = (WORD *) (dst + y * outpitch);
2152 for (x = 0; x < width; x++ ) {
2153 WORD green = (*Source++);
2154 WORD red = (*Source++);
2155 Dest[0] = green;
2156 Dest[1] = red;
2157 /* Strictly speaking not correct for R16G16F, but it doesn't matter because the
2158 * shader overwrites it anyway
2160 Dest[2] = 0xffff;
2161 Dest += 3;
2164 break;
2167 case CONVERT_R32G32F:
2169 unsigned int x, y;
2170 const float *Source;
2171 float *Dest;
2172 for(y = 0; y < height; y++) {
2173 Source = (const float *)(src + y * pitch);
2174 Dest = (float *) (dst + y * outpitch);
2175 for (x = 0; x < width; x++ ) {
2176 float green = (*Source++);
2177 float red = (*Source++);
2178 Dest[0] = green;
2179 Dest[1] = red;
2180 Dest[2] = 1.0;
2181 Dest += 3;
2184 break;
2187 default:
2188 ERR("Unsupported conversation type %d\n", convert);
2190 return WINED3D_OK;
2193 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2194 IWineD3DPaletteImpl* pal = This->palette;
2195 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2196 BOOL index_in_alpha = FALSE;
2197 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2198 unsigned int i;
2200 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2201 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2202 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2203 * duplicate entries. Store the color key in the unused alpha component to speed the
2204 * download up and to make conversion unneeded. */
2205 index_in_alpha = primary_render_target_is_p8(device);
2207 if (pal == NULL) {
2208 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2209 if(dxVersion <= 7) {
2210 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2211 if(index_in_alpha) {
2212 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2213 there's no palette at this time. */
2214 for (i = 0; i < 256; i++) table[i][3] = i;
2216 } else {
2217 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2218 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2219 capability flag is present (wine does advertise this capability) */
2220 for (i = 0; i < 256; i++) {
2221 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2222 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2223 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2224 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2227 } else {
2228 TRACE("Using surface palette %p\n", pal);
2229 /* Get the surface's palette */
2230 for (i = 0; i < 256; i++) {
2231 table[i][0] = pal->palents[i].peRed;
2232 table[i][1] = pal->palents[i].peGreen;
2233 table[i][2] = pal->palents[i].peBlue;
2235 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2236 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2237 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2238 of pixels that should be masked away is set to 0. */
2239 if(index_in_alpha) {
2240 table[i][3] = i;
2241 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2242 table[i][3] = 0x00;
2243 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2244 table[i][3] = pal->palents[i].peFlags;
2245 } else {
2246 table[i][3] = 0xFF;
2252 /* This function is used in case of 8bit paletted textures to upload the palette.
2253 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2254 extensions like ATI_fragment_shaders is possible.
2256 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2257 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2258 BYTE table[256][4];
2259 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2261 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2263 /* Try to use the paletted texture extension */
2264 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2266 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2267 ENTER_GL();
2268 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2269 LEAVE_GL();
2271 else
2273 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2274 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2275 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2277 ENTER_GL();
2279 /* Create the fragment program if we don't have it */
2280 if(!device->paletteConversionShader)
2282 const char *fragment_palette_conversion =
2283 "!!ARBfp1.0\n"
2284 "TEMP index;\n"
2285 /* { 255/256, 0.5/255*255/256, 0, 0 } */
2286 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n"
2287 /* The alpha-component contains the palette index */
2288 "TEX index, fragment.texcoord[0], texture[0], 2D;\n"
2289 /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2290 "MAD index.a, index.a, constants.x, constants.y;\n"
2291 /* Use the alpha-component as an index in the palette to get the final color */
2292 "TEX result.color, index.a, texture[1], 1D;\n"
2293 "END";
2295 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2296 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2297 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2298 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), fragment_palette_conversion));
2299 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2302 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2303 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2305 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2306 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2308 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2309 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2310 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2311 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2313 /* Switch back to unit 0 in which the 2D texture will be stored. */
2314 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2316 /* Rebind the texture because it isn't bound anymore */
2317 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2319 LEAVE_GL();
2323 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2324 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2326 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8
2327 && This->resource.format_desc->format != WINED3DFMT_A8P8))
2329 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2330 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2332 return FALSE;
2335 if(This->palette9) {
2336 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2337 return FALSE;
2339 } else {
2340 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2342 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2343 return TRUE;
2346 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2347 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2348 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2350 if (!(This->Flags & flag)) {
2351 TRACE("Reloading because surface is dirty\n");
2352 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2353 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2354 /* Reload: vice versa OR */
2355 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2356 /* Also reload: Color key is active AND the color key has changed */
2357 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2358 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2359 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2360 TRACE("Reloading because of color keying\n");
2361 /* To perform the color key conversion we need a sysmem copy of
2362 * the surface. Make sure we have it
2365 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2366 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2367 /* TODO: This is not necessarily needed with hw palettized texture support */
2368 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2369 } else {
2370 TRACE("surface is already in texture\n");
2371 return WINED3D_OK;
2374 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2375 * These resources are not bound by device size or format restrictions. Because of this,
2376 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2377 * However, these resources can always be created, locked, and copied.
2379 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2381 FIXME("(%p) Operation not supported for scratch textures\n",This);
2382 return WINED3DERR_INVALIDCALL;
2385 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2387 #if 0
2389 static unsigned int gen = 0;
2390 char buffer[4096];
2391 ++gen;
2392 if ((gen % 10) == 0) {
2393 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2394 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2397 * debugging crash code
2398 if (gen == 250) {
2399 void** test = NULL;
2400 *test = 0;
2404 #endif
2406 if (!(This->Flags & SFLAG_DONOTFREE)) {
2407 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2408 This->resource.allocatedMemory = NULL;
2409 This->resource.heapMemory = NULL;
2410 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2413 return WINED3D_OK;
2416 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2417 /* TODO: check for locks */
2418 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2419 IWineD3DBaseTexture *baseTexture = NULL;
2420 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2422 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2423 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2424 TRACE("Passing to container\n");
2425 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2426 IWineD3DBaseTexture_Release(baseTexture);
2427 } else {
2428 GLuint *name;
2429 TRACE("(%p) : Binding surface\n", This);
2431 name = srgb ? &This->glDescription.srgbTextureName : &This->glDescription.textureName;
2432 if(!device->isInDraw) {
2433 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2436 ENTER_GL();
2438 if (!This->glDescription.level) {
2439 if (!*name) {
2440 glGenTextures(1, name);
2441 checkGLcall("glGenTextures");
2442 TRACE("Surface %p given name %d\n", This, *name);
2444 glBindTexture(This->glDescription.target, *name);
2445 checkGLcall("glBindTexture");
2446 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2447 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2448 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2449 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2450 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2451 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2452 glTexParameteri(This->glDescription.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2453 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2454 glTexParameteri(This->glDescription.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2455 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2457 /* This is where we should be reducing the amount of GLMemoryUsed */
2458 } else if (*name) {
2459 /* Mipmap surfaces should have a base texture container */
2460 ERR("Mipmap surface has a glTexture bound to it!\n");
2463 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2464 checkGLcall("glBindTexture");
2466 LEAVE_GL();
2468 return;
2471 #include <errno.h>
2472 #include <stdio.h>
2473 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2475 FILE* f = NULL;
2476 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2477 char *allocatedMemory;
2478 const char *textureRow;
2479 IWineD3DSwapChain *swapChain = NULL;
2480 int width, height, i, y;
2481 GLuint tmpTexture = 0;
2482 DWORD color;
2483 /*FIXME:
2484 Textures may not be stored in ->allocatedgMemory and a GlTexture
2485 so we should lock the surface before saving a snapshot, or at least check that
2487 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2488 by calling GetTexImage and in compressed form by calling
2489 GetCompressedTexImageARB. Queried compressed images can be saved and
2490 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2491 texture images do not need to be processed by the GL and should
2492 significantly improve texture loading performance relative to uncompressed
2493 images. */
2495 /* Setup the width and height to be the internal texture width and height. */
2496 width = This->pow2Width;
2497 height = This->pow2Height;
2498 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2499 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2501 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2502 /* if were not a real texture then read the back buffer into a real texture */
2503 /* we don't want to interfere with the back buffer so read the data into a temporary
2504 * texture and then save the data out of the temporary texture
2506 GLint prevRead;
2507 ENTER_GL();
2508 TRACE("(%p) Reading render target into texture\n", This);
2510 glGenTextures(1, &tmpTexture);
2511 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2513 glTexImage2D(GL_TEXTURE_2D,
2515 GL_RGBA,
2516 width,
2517 height,
2518 0/*border*/,
2519 GL_RGBA,
2520 GL_UNSIGNED_INT_8_8_8_8_REV,
2521 NULL);
2523 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2524 checkGLcall("glGetIntegerv");
2525 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2526 checkGLcall("glReadBuffer");
2527 glCopyTexImage2D(GL_TEXTURE_2D,
2529 GL_RGBA,
2532 width,
2533 height,
2536 checkGLcall("glCopyTexImage2D");
2537 glReadBuffer(prevRead);
2538 LEAVE_GL();
2540 } else { /* bind the real texture, and make sure it up to date */
2541 surface_internal_preload(iface, SRGB_RGB);
2542 surface_bind_and_dirtify(This, FALSE);
2544 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2545 ENTER_GL();
2546 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2547 glGetTexImage(GL_TEXTURE_2D,
2548 This->glDescription.level,
2549 GL_RGBA,
2550 GL_UNSIGNED_INT_8_8_8_8_REV,
2551 allocatedMemory);
2552 checkGLcall("glTexImage2D");
2553 if (tmpTexture) {
2554 glBindTexture(GL_TEXTURE_2D, 0);
2555 glDeleteTextures(1, &tmpTexture);
2557 LEAVE_GL();
2559 f = fopen(filename, "w+");
2560 if (NULL == f) {
2561 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2562 return WINED3DERR_INVALIDCALL;
2564 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2565 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2566 /* TGA header */
2567 fputc(0,f);
2568 fputc(0,f);
2569 fputc(2,f);
2570 fputc(0,f);
2571 fputc(0,f);
2572 fputc(0,f);
2573 fputc(0,f);
2574 fputc(0,f);
2575 fputc(0,f);
2576 fputc(0,f);
2577 fputc(0,f);
2578 fputc(0,f);
2579 /* short width*/
2580 fwrite(&width,2,1,f);
2581 /* short height */
2582 fwrite(&height,2,1,f);
2583 /* format rgba */
2584 fputc(0x20,f);
2585 fputc(0x28,f);
2586 /* raw data */
2587 /* 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 */
2588 if(swapChain)
2589 textureRow = allocatedMemory + (width * (height - 1) *4);
2590 else
2591 textureRow = allocatedMemory;
2592 for (y = 0 ; y < height; y++) {
2593 for (i = 0; i < width; i++) {
2594 color = *((const DWORD*)textureRow);
2595 fputc((color >> 16) & 0xFF, f); /* B */
2596 fputc((color >> 8) & 0xFF, f); /* G */
2597 fputc((color >> 0) & 0xFF, f); /* R */
2598 fputc((color >> 24) & 0xFF, f); /* A */
2599 textureRow += 4;
2601 /* take two rows of the pointer to the texture memory */
2602 if(swapChain)
2603 (textureRow-= width << 3);
2606 TRACE("Closing file\n");
2607 fclose(f);
2609 if(swapChain) {
2610 IWineD3DSwapChain_Release(swapChain);
2612 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2613 return WINED3D_OK;
2616 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2617 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2618 HRESULT hr;
2620 TRACE("(%p) : Calling base function first\n", This);
2621 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2622 if(SUCCEEDED(hr)) {
2623 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2624 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2625 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2627 return hr;
2630 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2631 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2633 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2634 WARN("Surface is locked or the HDC is in use\n");
2635 return WINED3DERR_INVALIDCALL;
2638 if(Mem && Mem != This->resource.allocatedMemory) {
2639 void *release = NULL;
2641 /* Do I have to copy the old surface content? */
2642 if(This->Flags & SFLAG_DIBSECTION) {
2643 /* Release the DC. No need to hold the critical section for the update
2644 * Thread because this thread runs only on front buffers, but this method
2645 * fails for render targets in the check above.
2647 SelectObject(This->hDC, This->dib.holdbitmap);
2648 DeleteDC(This->hDC);
2649 /* Release the DIB section */
2650 DeleteObject(This->dib.DIBsection);
2651 This->dib.bitmap_data = NULL;
2652 This->resource.allocatedMemory = NULL;
2653 This->hDC = NULL;
2654 This->Flags &= ~SFLAG_DIBSECTION;
2655 } else if(!(This->Flags & SFLAG_USERPTR)) {
2656 release = This->resource.heapMemory;
2657 This->resource.heapMemory = NULL;
2659 This->resource.allocatedMemory = Mem;
2660 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2662 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2663 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2665 /* For client textures opengl has to be notified */
2666 if(This->Flags & SFLAG_CLIENT) {
2667 DWORD oldFlags = This->Flags;
2668 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2669 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2670 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2671 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2674 /* Now free the old memory if any */
2675 HeapFree(GetProcessHeap(), 0, release);
2676 } else if(This->Flags & SFLAG_USERPTR) {
2677 /* LockRect and GetDC will re-create the dib section and allocated memory */
2678 This->resource.allocatedMemory = NULL;
2679 /* HeapMemory should be NULL already */
2680 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2681 This->Flags &= ~SFLAG_USERPTR;
2683 if(This->Flags & SFLAG_CLIENT) {
2684 DWORD oldFlags = This->Flags;
2685 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2686 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2687 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2688 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
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 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
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 checkGLcall("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 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
2933 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2934 if(!noBackBufferBackup && Src->glDescription.textureName == 0) {
2935 /* Get it a description */
2936 surface_internal_preload(SrcSurface, SRGB_RGB);
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_desc->format == WINED3DFMT_P8) && (This->palette == NULL))
3374 paletteOverride = TRUE;
3375 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3376 This->palette = Src->palette;
3379 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3380 * flip the image nor scale it.
3382 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3383 * -> If the app wants a image width an unscaled width, copy it line per line
3384 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3385 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3386 * back buffer. This is slower than reading line per line, thus not used for flipping
3387 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3388 * pixel by pixel
3390 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3391 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3392 * backends.
3394 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3395 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3396 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3397 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3398 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3399 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3400 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3401 } else {
3402 TRACE("Using hardware stretching to flip / stretch the texture\n");
3403 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3406 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3407 if(paletteOverride)
3408 This->palette = NULL;
3410 if(!(This->Flags & SFLAG_DONOTFREE)) {
3411 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3412 This->resource.allocatedMemory = NULL;
3413 This->resource.heapMemory = NULL;
3414 } else {
3415 This->Flags &= ~SFLAG_INSYSMEM;
3417 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3418 * path is never entered
3420 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3422 return WINED3D_OK;
3423 } else if(Src) {
3424 /* Blit from offscreen surface to render target */
3425 float glTexCoord[4];
3426 DWORD oldCKeyFlags = Src->CKeyFlags;
3427 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3428 RECT SourceRectangle;
3429 BOOL paletteOverride = FALSE;
3431 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3433 if(SrcRect) {
3434 SourceRectangle.left = SrcRect->left;
3435 SourceRectangle.right = SrcRect->right;
3436 SourceRectangle.top = SrcRect->top;
3437 SourceRectangle.bottom = SrcRect->bottom;
3438 } else {
3439 SourceRectangle.left = 0;
3440 SourceRectangle.right = Src->currentDesc.Width;
3441 SourceRectangle.top = 0;
3442 SourceRectangle.bottom = Src->currentDesc.Height;
3445 /* When blitting from an offscreen surface to a rendertarget, the source
3446 * surface is not required to have a palette. Our rendering / conversion
3447 * code further down the road retrieves the palette from the surface, so
3448 * it must have a palette set. */
3449 if ((Src->resource.format_desc->format == WINED3DFMT_P8) && (Src->palette == NULL))
3451 paletteOverride = TRUE;
3452 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3453 Src->palette = This->palette;
3456 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3457 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3458 TRACE("Using stretch_rect_fbo\n");
3459 /* The source is always a texture, but never the currently active render target, and the texture
3460 * contents are never upside down
3462 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3463 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3465 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3466 if(paletteOverride)
3467 Src->palette = NULL;
3468 return WINED3D_OK;
3471 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3472 /* Fall back to software */
3473 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3474 SourceRectangle.left, SourceRectangle.top,
3475 SourceRectangle.right, SourceRectangle.bottom);
3476 return WINED3DERR_INVALIDCALL;
3479 /* Color keying: Check if we have to do a color keyed blt,
3480 * and if not check if a color key is activated.
3482 * Just modify the color keying parameters in the surface and restore them afterwards
3483 * The surface keeps track of the color key last used to load the opengl surface.
3484 * PreLoad will catch the change to the flags and color key and reload if necessary.
3486 if(Flags & WINEDDBLT_KEYSRC) {
3487 /* Use color key from surface */
3488 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3489 /* Use color key from DDBltFx */
3490 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3491 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3492 } else {
3493 /* Do not use color key */
3494 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3497 /* Now load the surface */
3498 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3500 /* Activate the destination context, set it up for blitting */
3501 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3503 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3504 * while OpenGL coordinates are window relative.
3505 * Also beware of the origin difference(top left vs bottom left).
3506 * Also beware that the front buffer's surface size is screen width x screen height,
3507 * whereas the real gl drawable size is the size of the window.
3509 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3510 RECT windowsize;
3511 POINT offset = {0,0};
3512 UINT h;
3513 ClientToScreen(dstSwapchain->win_handle, &offset);
3514 GetClientRect(dstSwapchain->win_handle, &windowsize);
3515 h = windowsize.bottom - windowsize.top;
3516 rect.x1 -= offset.x; rect.x2 -=offset.x;
3517 rect.y1 -= offset.y; rect.y2 -=offset.y;
3518 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3521 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format_desc,
3522 Src->glDescription.target, Src->pow2Width, Src->pow2Height);
3524 ENTER_GL();
3526 /* Bind the texture */
3527 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3528 checkGLcall("glBindTexture");
3530 /* Filtering for StretchRect */
3531 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3532 magLookup[Filter - WINED3DTEXF_NONE]);
3533 checkGLcall("glTexParameteri");
3534 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3535 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
3536 checkGLcall("glTexParameteri");
3537 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3538 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3539 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3540 checkGLcall("glTexEnvi");
3542 /* This is for color keying */
3543 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3544 glEnable(GL_ALPHA_TEST);
3545 checkGLcall("glEnable GL_ALPHA_TEST");
3547 /* When the primary render target uses P8, the alpha component contains the palette index.
3548 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3549 * should be masked away have alpha set to 0. */
3550 if(primary_render_target_is_p8(myDevice))
3551 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3552 else
3553 glAlphaFunc(GL_NOTEQUAL, 0.0);
3554 checkGLcall("glAlphaFunc\n");
3555 } else {
3556 glDisable(GL_ALPHA_TEST);
3557 checkGLcall("glDisable GL_ALPHA_TEST");
3560 /* Draw a textured quad
3562 glBegin(GL_QUADS);
3564 glColor3d(1.0f, 1.0f, 1.0f);
3565 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3566 glVertex3f(rect.x1,
3567 rect.y1,
3568 0.0);
3570 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3571 glVertex3f(rect.x1, rect.y2, 0.0);
3573 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3574 glVertex3f(rect.x2,
3575 rect.y2,
3576 0.0);
3578 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3579 glVertex3f(rect.x2,
3580 rect.y1,
3581 0.0);
3582 glEnd();
3583 checkGLcall("glEnd");
3585 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3586 glDisable(GL_ALPHA_TEST);
3587 checkGLcall("glDisable(GL_ALPHA_TEST)");
3590 glBindTexture(Src->glDescription.target, 0);
3591 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3593 /* Restore the color key parameters */
3594 Src->CKeyFlags = oldCKeyFlags;
3595 Src->SrcBltCKey = oldBltCKey;
3597 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3598 if(paletteOverride)
3599 Src->palette = NULL;
3601 LEAVE_GL();
3603 /* Leave the opengl state valid for blitting */
3604 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3606 /* Flush in case the drawable is used by multiple GL contexts */
3607 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3608 glFlush();
3610 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3611 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3612 * is outdated now
3614 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3616 return WINED3D_OK;
3617 } else {
3618 /* Source-Less Blit to render target */
3619 if (Flags & WINEDDBLT_COLORFILL) {
3620 /* This is easy to handle for the D3D Device... */
3621 DWORD color;
3623 TRACE("Colorfill\n");
3625 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3626 must be true if we are here */
3627 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3628 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3629 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3630 TRACE("Surface is higher back buffer, falling back to software\n");
3631 return WINED3DERR_INVALIDCALL;
3634 /* The color as given in the Blt function is in the format of the frame-buffer...
3635 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3637 if (This->resource.format_desc->format == WINED3DFMT_P8)
3639 DWORD alpha;
3641 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3642 else alpha = 0xFF000000;
3644 if (This->palette) {
3645 color = (alpha |
3646 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3647 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3648 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3649 } else {
3650 color = alpha;
3653 else if (This->resource.format_desc->format == WINED3DFMT_R5G6B5)
3655 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3656 color = 0xFFFFFFFF;
3657 } else {
3658 color = ((0xFF000000) |
3659 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3660 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3661 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3664 else if ((This->resource.format_desc->format == WINED3DFMT_R8G8B8)
3665 || (This->resource.format_desc->format == WINED3DFMT_X8R8G8B8))
3667 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3669 else if (This->resource.format_desc->format == WINED3DFMT_A8R8G8B8)
3671 color = DDBltFx->u5.dwFillColor;
3673 else {
3674 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3675 return WINED3DERR_INVALIDCALL;
3678 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3679 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3680 1, /* Number of rectangles */
3681 &rect, WINED3DCLEAR_TARGET, color,
3682 0.0 /* Z */,
3683 0 /* Stencil */);
3684 return WINED3D_OK;
3688 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3689 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3690 return WINED3DERR_INVALIDCALL;
3693 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3694 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3696 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3697 float depth;
3699 if (Flags & WINEDDBLT_DEPTHFILL) {
3700 switch(This->resource.format_desc->format)
3702 case WINED3DFMT_D16_UNORM:
3703 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3704 break;
3705 case WINED3DFMT_D15S1:
3706 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3707 break;
3708 case WINED3DFMT_D24S8:
3709 case WINED3DFMT_D24X8:
3710 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3711 break;
3712 case WINED3DFMT_D32:
3713 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3714 break;
3715 default:
3716 depth = 0.0;
3717 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
3720 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3721 DestRect == NULL ? 0 : 1,
3722 (const WINED3DRECT *)DestRect,
3723 WINED3DCLEAR_ZBUFFER,
3724 0x00000000,
3725 depth,
3726 0x00000000);
3729 FIXME("(%p): Unsupp depthstencil blit\n", This);
3730 return WINED3DERR_INVALIDCALL;
3733 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3734 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3735 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3736 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3737 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3738 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3739 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3741 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3743 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3744 return WINEDDERR_SURFACEBUSY;
3747 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3748 * except depth blits, which seem to work
3750 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3751 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3752 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3753 return WINED3DERR_INVALIDCALL;
3754 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3755 TRACE("Z Blit override handled the blit\n");
3756 return WINED3D_OK;
3760 /* Special cases for RenderTargets */
3761 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3762 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3763 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3766 /* For the rest call the X11 surface implementation.
3767 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3768 * other Blts are rather rare
3770 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3773 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
3774 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
3776 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3777 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3778 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3779 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3781 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3783 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3784 return WINEDDERR_SURFACEBUSY;
3787 if(myDevice->inScene &&
3788 (iface == myDevice->stencilBufferTarget ||
3789 (Source && Source == myDevice->stencilBufferTarget))) {
3790 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3791 return WINED3DERR_INVALIDCALL;
3794 /* Special cases for RenderTargets */
3795 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3796 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3798 RECT SrcRect, DstRect;
3799 DWORD Flags=0;
3801 if(rsrc) {
3802 SrcRect.left = rsrc->left;
3803 SrcRect.top= rsrc->top;
3804 SrcRect.bottom = rsrc->bottom;
3805 SrcRect.right = rsrc->right;
3806 } else {
3807 SrcRect.left = 0;
3808 SrcRect.top = 0;
3809 SrcRect.right = srcImpl->currentDesc.Width;
3810 SrcRect.bottom = srcImpl->currentDesc.Height;
3813 DstRect.left = dstx;
3814 DstRect.top=dsty;
3815 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3816 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3818 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3819 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3820 Flags |= WINEDDBLT_KEYSRC;
3821 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3822 Flags |= WINEDDBLT_KEYDEST;
3823 if(trans & WINEDDBLTFAST_WAIT)
3824 Flags |= WINEDDBLT_WAIT;
3825 if(trans & WINEDDBLTFAST_DONOTWAIT)
3826 Flags |= WINEDDBLT_DONOTWAIT;
3828 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3832 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3835 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
3837 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3838 RGBQUAD col[256];
3839 IWineD3DPaletteImpl *pal = This->palette;
3840 unsigned int n;
3841 TRACE("(%p)\n", This);
3843 if (!pal) return WINED3D_OK;
3845 if (This->resource.format_desc->format == WINED3DFMT_P8
3846 || This->resource.format_desc->format == WINED3DFMT_A8P8)
3848 int bpp;
3849 GLenum format, internal, type;
3850 CONVERT_TYPES convert;
3852 /* Check if we are using a RTL mode which uses texturing for uploads */
3853 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX);
3855 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
3856 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, FALSE);
3858 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
3860 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
3861 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
3863 /* We want to force a palette refresh, so mark the drawable as not being up to date */
3864 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
3866 /* Re-upload the palette */
3867 d3dfmt_p8_upload_palette(iface, convert);
3868 } else {
3869 if(!(This->Flags & SFLAG_INSYSMEM)) {
3870 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3871 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3873 TRACE("Dirtifying surface\n");
3874 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3878 if(This->Flags & SFLAG_DIBSECTION) {
3879 TRACE("(%p): Updating the hdc's palette\n", This);
3880 for (n=0; n<256; n++) {
3881 col[n].rgbRed = pal->palents[n].peRed;
3882 col[n].rgbGreen = pal->palents[n].peGreen;
3883 col[n].rgbBlue = pal->palents[n].peBlue;
3884 col[n].rgbReserved = 0;
3886 SetDIBColorTable(This->hDC, 0, 256, col);
3889 /* Propagate the changes to the drawable when we have a palette. */
3890 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3891 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3893 return WINED3D_OK;
3896 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3897 /** Check against the maximum texture sizes supported by the video card **/
3898 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3899 unsigned int pow2Width, pow2Height;
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_desc->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 || Format == WINED3DFMT_ATI2N)
3924 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3925 This, This->currentDesc.Width, This->currentDesc.Height);
3926 return WINED3DERR_NOTAVAILABLE;
3930 if(pow2Width != This->currentDesc.Width ||
3931 pow2Height != This->currentDesc.Height) {
3932 This->Flags |= SFLAG_NONPOW2;
3935 TRACE("%p\n", This);
3936 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3937 /* one of three options
3938 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)
3939 2: Set the texture to the maximum size (bad idea)
3940 3: WARN and return WINED3DERR_NOTAVAILABLE;
3941 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.
3943 WARN("(%p) Creating an oversized surface: %ux%u (texture is %ux%u)\n",
3944 This, This->pow2Width, This->pow2Height, This->currentDesc.Width, This->currentDesc.Height);
3945 This->Flags |= SFLAG_OVERSIZE;
3947 /* This will be initialized on the first blt */
3948 This->glRect.left = 0;
3949 This->glRect.top = 0;
3950 This->glRect.right = 0;
3951 This->glRect.bottom = 0;
3952 } else {
3953 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3954 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3955 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3956 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3958 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE)
3959 && !((This->resource.format_desc->format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE)
3960 && (wined3d_settings.rendertargetlock_mode == RTL_READTEX
3961 || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3963 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3964 This->pow2Width = This->currentDesc.Width;
3965 This->pow2Height = This->currentDesc.Height;
3966 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
3969 /* No oversize, gl rect is the full texture size */
3970 This->Flags &= ~SFLAG_OVERSIZE;
3971 This->glRect.left = 0;
3972 This->glRect.top = 0;
3973 This->glRect.right = This->pow2Width;
3974 This->glRect.bottom = This->pow2Height;
3977 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3978 switch(wined3d_settings.offscreen_rendering_mode) {
3979 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3980 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3981 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3985 This->Flags |= SFLAG_INSYSMEM;
3987 return WINED3D_OK;
3990 struct depth_blt_info
3992 GLenum binding;
3993 GLenum bind_target;
3994 enum tex_types tex_type;
3995 GLfloat coords[4][3];
3998 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
4000 GLfloat (*coords)[3] = info->coords;
4002 switch (target)
4004 default:
4005 FIXME("Unsupported texture target %#x\n", target);
4006 /* Fall back to GL_TEXTURE_2D */
4007 case GL_TEXTURE_2D:
4008 info->binding = GL_TEXTURE_BINDING_2D;
4009 info->bind_target = GL_TEXTURE_2D;
4010 info->tex_type = tex_2d;
4011 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
4012 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
4013 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4014 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4015 break;
4017 case GL_TEXTURE_RECTANGLE_ARB:
4018 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
4019 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
4020 info->tex_type = tex_rect;
4021 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
4022 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
4023 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4024 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4025 break;
4027 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4028 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4029 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4030 info->tex_type = tex_cube;
4031 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4032 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4033 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4034 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4036 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4037 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4038 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4039 info->tex_type = tex_cube;
4040 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4041 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4042 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4043 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4045 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4046 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4047 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4048 info->tex_type = tex_cube;
4049 coords[0][0] = -1.0f; coords[0][1] = 1.0f; coords[0][2] = 1.0f;
4050 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 1.0f;
4051 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4052 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4054 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4055 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4056 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4057 info->tex_type = tex_cube;
4058 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4059 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4060 coords[2][0] = -1.0f; coords[2][1] = -1.0f; coords[2][2] = 1.0f;
4061 coords[3][0] = 1.0f; coords[3][1] = -1.0f; coords[3][2] = 1.0f;
4063 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4064 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4065 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4066 info->tex_type = tex_cube;
4067 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4068 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4069 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4070 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4072 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4073 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4074 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4075 info->tex_type = tex_cube;
4076 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4077 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4078 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4079 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4083 /* GL locking is done by the caller */
4084 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4086 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4087 struct depth_blt_info info;
4088 GLint old_binding = 0;
4090 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4092 glDisable(GL_CULL_FACE);
4093 glEnable(GL_BLEND);
4094 glDisable(GL_ALPHA_TEST);
4095 glDisable(GL_SCISSOR_TEST);
4096 glDisable(GL_STENCIL_TEST);
4097 glEnable(GL_DEPTH_TEST);
4098 glDepthFunc(GL_ALWAYS);
4099 glDepthMask(GL_TRUE);
4100 glBlendFunc(GL_ZERO, GL_ONE);
4101 glViewport(0, 0, w, h);
4103 surface_get_depth_blt_info(target, w, h, &info);
4104 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4105 glGetIntegerv(info.binding, &old_binding);
4106 glBindTexture(info.bind_target, texture);
4108 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4110 glBegin(GL_TRIANGLE_STRIP);
4111 glTexCoord3fv(info.coords[0]);
4112 glVertex2f(-1.0f, -1.0f);
4113 glTexCoord3fv(info.coords[1]);
4114 glVertex2f(1.0f, -1.0f);
4115 glTexCoord3fv(info.coords[2]);
4116 glVertex2f(-1.0f, 1.0f);
4117 glTexCoord3fv(info.coords[3]);
4118 glVertex2f(1.0f, 1.0f);
4119 glEnd();
4121 glBindTexture(info.bind_target, old_binding);
4123 glPopAttrib();
4125 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4128 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4129 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4131 TRACE("(%p) New location %#x\n", This, location);
4133 if (location & ~SFLAG_DS_LOCATIONS) {
4134 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4137 This->Flags &= ~SFLAG_DS_LOCATIONS;
4138 This->Flags |= location;
4141 void surface_load_ds_location(IWineD3DSurface *iface, DWORD location) {
4142 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4143 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4145 TRACE("(%p) New location %#x\n", This, location);
4147 /* TODO: Make this work for modes other than FBO */
4148 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4150 if (This->Flags & location) {
4151 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4152 return;
4155 if (This->current_renderbuffer) {
4156 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4157 return;
4160 if (location == SFLAG_DS_OFFSCREEN) {
4161 if (This->Flags & SFLAG_DS_ONSCREEN) {
4162 GLint old_binding = 0;
4163 GLenum bind_target;
4165 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4167 ENTER_GL();
4169 if (!device->depth_blt_texture) {
4170 glGenTextures(1, &device->depth_blt_texture);
4173 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4174 * directly on the FBO texture. That's because we need to flip. */
4175 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4176 if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
4177 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4178 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4179 } else {
4180 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4181 bind_target = GL_TEXTURE_2D;
4183 glBindTexture(bind_target, device->depth_blt_texture);
4184 glCopyTexImage2D(bind_target,
4185 This->glDescription.level,
4186 This->resource.format_desc->glInternal,
4189 This->currentDesc.Width,
4190 This->currentDesc.Height,
4192 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4193 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4194 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4195 glBindTexture(bind_target, old_binding);
4197 /* Setup the destination */
4198 if (!device->depth_blt_rb) {
4199 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
4200 checkGLcall("glGenRenderbuffersEXT");
4202 if (device->depth_blt_rb_w != This->currentDesc.Width
4203 || device->depth_blt_rb_h != This->currentDesc.Height) {
4204 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4205 checkGLcall("glBindRenderbufferEXT");
4206 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
4207 checkGLcall("glRenderbufferStorageEXT");
4208 device->depth_blt_rb_w = This->currentDesc.Width;
4209 device->depth_blt_rb_h = This->currentDesc.Height;
4212 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->dst_fbo);
4213 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4214 checkGLcall("glFramebufferRenderbufferEXT");
4215 context_attach_depth_stencil_fbo(device, GL_FRAMEBUFFER_EXT, iface, FALSE);
4217 /* Do the actual blit */
4218 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4219 checkGLcall("depth_blt");
4221 if (device->activeContext->current_fbo) {
4222 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->current_fbo->id);
4223 } else {
4224 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4225 checkGLcall("glBindFramebuffer()");
4228 LEAVE_GL();
4229 } else {
4230 FIXME("No up to date depth stencil location\n");
4232 } else if (location == SFLAG_DS_ONSCREEN) {
4233 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4234 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4236 ENTER_GL();
4238 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4239 checkGLcall("glBindFramebuffer()");
4240 surface_depth_blt(This, This->glDescription.textureName, This->currentDesc.Width, This->currentDesc.Height, This->glDescription.target);
4241 checkGLcall("depth_blt");
4243 if (device->activeContext->current_fbo) {
4244 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, device->activeContext->current_fbo->id));
4245 checkGLcall("glBindFramebuffer()");
4248 LEAVE_GL();
4249 } else {
4250 FIXME("No up to date depth stencil location\n");
4252 } else {
4253 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4256 This->Flags |= location;
4259 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4260 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4261 IWineD3DBaseTexture *texture;
4262 IWineD3DSurfaceImpl *overlay;
4264 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4265 persistent ? "TRUE" : "FALSE");
4267 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4268 if (This->Flags & SFLAG_SWAPCHAIN)
4270 TRACE("Surface %p is an onscreen surface\n", iface);
4271 } else {
4272 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4273 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4277 if(persistent) {
4278 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4279 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX))) {
4280 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4281 TRACE("Passing to container\n");
4282 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4283 IWineD3DBaseTexture_Release(texture);
4286 This->Flags &= ~SFLAG_LOCATIONS;
4287 This->Flags |= flag;
4289 /* Redraw emulated overlays, if any */
4290 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4291 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4292 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4295 } else {
4296 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4297 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4298 TRACE("Passing to container\n");
4299 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4300 IWineD3DBaseTexture_Release(texture);
4303 This->Flags &= ~flag;
4306 if(!(This->Flags & SFLAG_LOCATIONS)) {
4307 ERR("%p: Surface does not have any up to date location\n", This);
4311 struct coords {
4312 GLfloat x, y, z;
4315 struct float_rect
4317 float l;
4318 float t;
4319 float r;
4320 float b;
4323 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4325 f->l = ((r->left * 2.0f) / w) - 1.0f;
4326 f->t = ((r->top * 2.0f) / h) - 1.0f;
4327 f->r = ((r->right * 2.0f) / w) - 1.0f;
4328 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4331 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
4332 struct coords coords[4];
4333 RECT rect;
4334 IWineD3DSwapChain *swapchain;
4335 IWineD3DBaseTexture *texture;
4336 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4337 GLenum bind_target;
4338 struct float_rect f;
4340 if(rect_in) {
4341 rect = *rect_in;
4342 } else {
4343 rect.left = 0;
4344 rect.top = 0;
4345 rect.right = This->currentDesc.Width;
4346 rect.bottom = This->currentDesc.Height;
4349 switch(This->glDescription.target)
4351 case GL_TEXTURE_2D:
4352 bind_target = GL_TEXTURE_2D;
4354 coords[0].x = (float)rect.left / This->pow2Width;
4355 coords[0].y = (float)rect.top / This->pow2Height;
4356 coords[0].z = 0;
4358 coords[1].x = (float)rect.left / This->pow2Width;
4359 coords[1].y = (float)rect.bottom / This->pow2Height;
4360 coords[1].z = 0;
4362 coords[2].x = (float)rect.right / This->pow2Width;
4363 coords[2].y = (float)rect.bottom / This->pow2Height;
4364 coords[2].z = 0;
4366 coords[3].x = (float)rect.right / This->pow2Width;
4367 coords[3].y = (float)rect.top / This->pow2Height;
4368 coords[3].z = 0;
4369 break;
4371 case GL_TEXTURE_RECTANGLE_ARB:
4372 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4373 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4374 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4375 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4376 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4377 break;
4379 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4380 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4381 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4382 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4383 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4384 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4385 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4386 break;
4388 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4389 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4390 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4391 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4392 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4393 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4394 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4395 break;
4397 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4398 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4399 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4400 coords[0].x = f.l; coords[0].y = 1; coords[0].z = f.t;
4401 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4402 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4403 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4404 break;
4406 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4407 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4408 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4409 coords[0].x = f.l; coords[0].y = -1; coords[0].z = -f.t;
4410 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4411 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4412 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4413 break;
4415 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4416 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4417 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4418 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4419 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4420 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4421 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4422 break;
4424 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4425 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4426 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4427 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4428 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4429 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4430 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4431 break;
4433 default:
4434 ERR("Unexpected texture target %#x\n", This->glDescription.target);
4435 return;
4438 ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4439 ENTER_GL();
4441 glEnable(bind_target);
4442 checkGLcall("glEnable(bind_target)");
4443 glBindTexture(bind_target, This->glDescription.textureName);
4444 checkGLcall("bind_target, This->glDescription.textureName)");
4445 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4446 checkGLcall("glTexParameteri");
4447 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4448 checkGLcall("glTexParameteri");
4450 if (device->render_offscreen)
4452 LONG tmp = rect.top;
4453 rect.top = rect.bottom;
4454 rect.bottom = tmp;
4457 glBegin(GL_QUADS);
4458 glTexCoord3fv(&coords[0].x);
4459 glVertex2i(rect.left, rect.top);
4461 glTexCoord3fv(&coords[1].x);
4462 glVertex2i(rect.left, rect.bottom);
4464 glTexCoord3fv(&coords[2].x);
4465 glVertex2i(rect.right, rect.bottom);
4467 glTexCoord3fv(&coords[3].x);
4468 glVertex2i(rect.right, rect.top);
4469 glEnd();
4470 checkGLcall("glEnd");
4472 glDisable(bind_target);
4473 checkGLcall("glDisable(bind_target)");
4475 LEAVE_GL();
4477 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4479 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4480 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4481 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4482 glFlush();
4484 IWineD3DSwapChain_Release(swapchain);
4485 } else {
4486 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4487 * reset properly next draw
4489 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4491 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4492 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4493 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4494 IWineD3DBaseTexture_Release(texture);
4499 /*****************************************************************************
4500 * IWineD3DSurface::LoadLocation
4502 * Copies the current surface data from wherever it is to the requested
4503 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4504 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4505 * multiple locations, the gl texture is preferred over the drawable, which is
4506 * preferred over system memory. The PBO counts as system memory. If rect is
4507 * not NULL, only the specified rectangle is copied (only supported for
4508 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4509 * location is marked up to date after the copy.
4511 * Parameters:
4512 * flag: Surface location flag to be updated
4513 * rect: rectangle to be copied
4515 * Returns:
4516 * WINED3D_OK on success
4517 * WINED3DERR_DEVICELOST on an internal error
4519 *****************************************************************************/
4520 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4521 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4522 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4523 GLenum format, internal, type;
4524 CONVERT_TYPES convert;
4525 int bpp;
4526 int width, pitch, outpitch;
4527 BYTE *mem;
4528 BOOL drawable_read_ok = TRUE;
4530 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4531 if (This->Flags & SFLAG_SWAPCHAIN)
4533 TRACE("Surface %p is an onscreen surface\n", iface);
4534 } else {
4535 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4536 * Prefer SFLAG_INTEXTURE. */
4537 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4538 drawable_read_ok = FALSE;
4542 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4543 if(rect) {
4544 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4547 if(This->Flags & flag) {
4548 TRACE("Location already up to date\n");
4549 return WINED3D_OK;
4552 if(!(This->Flags & SFLAG_LOCATIONS)) {
4553 ERR("%p: Surface does not have any up to date location\n", This);
4554 This->Flags |= SFLAG_LOST;
4555 return WINED3DERR_DEVICELOST;
4558 if(flag == SFLAG_INSYSMEM) {
4559 surface_prepare_system_memory(This);
4561 /* Download the surface to system memory */
4562 if(This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) {
4563 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4564 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4566 surface_download_data(This);
4567 } else {
4568 read_from_framebuffer(This, rect,
4569 This->resource.allocatedMemory,
4570 IWineD3DSurface_GetPitch(iface));
4572 } else if(flag == SFLAG_INDRAWABLE) {
4573 if(This->Flags & SFLAG_INTEXTURE) {
4574 surface_blt_to_drawable(This, rect);
4575 } else {
4576 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4577 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4578 * values, otherwise we get incorrect values in the target. For now go the slow way
4579 * via a system memory copy
4581 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4584 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, FALSE);
4586 /* The width is in 'length' not in bytes */
4587 width = This->currentDesc.Width;
4588 pitch = IWineD3DSurface_GetPitch(iface);
4590 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4591 * but it isn't set (yet) in all cases it is getting called. */
4592 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4593 TRACE("Removing the pbo attached to surface %p\n", This);
4594 surface_remove_pbo(This);
4597 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4598 int height = This->currentDesc.Height;
4600 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4601 outpitch = width * bpp;
4602 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4604 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4605 if(!mem) {
4606 ERR("Out of memory %d, %d!\n", outpitch, height);
4607 return WINED3DERR_OUTOFVIDEOMEMORY;
4609 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4611 This->Flags |= SFLAG_CONVERTED;
4612 } else {
4613 This->Flags &= ~SFLAG_CONVERTED;
4614 mem = This->resource.allocatedMemory;
4617 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4619 /* Don't delete PBO memory */
4620 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4621 HeapFree(GetProcessHeap(), 0, mem);
4623 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4624 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4625 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4626 } else { /* Upload from system memory */
4627 BOOL srgb = flag == SFLAG_INSRGBTEX;
4628 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4629 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
4631 if(srgb) {
4632 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4633 /* Performance warning ... */
4634 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4635 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4637 } else {
4638 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4639 /* Performance warning ... */
4640 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4641 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4645 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4646 surface_bind_and_dirtify(This, srgb);
4648 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4649 This->Flags |= SFLAG_GLCKEY;
4650 This->glCKey = This->SrcBltCKey;
4652 else This->Flags &= ~SFLAG_GLCKEY;
4654 /* The width is in 'length' not in bytes */
4655 width = This->currentDesc.Width;
4656 pitch = IWineD3DSurface_GetPitch(iface);
4658 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4659 * but it isn't set (yet) in all cases it is getting called. */
4660 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4661 TRACE("Removing the pbo attached to surface %p\n", This);
4662 surface_remove_pbo(This);
4665 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4666 int height = This->currentDesc.Height;
4668 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4669 outpitch = width * bpp;
4670 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4672 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4673 if(!mem) {
4674 ERR("Out of memory %d, %d!\n", outpitch, height);
4675 return WINED3DERR_OUTOFVIDEOMEMORY;
4677 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4679 This->Flags |= SFLAG_CONVERTED;
4681 else if ((This->resource.format_desc->format == WINED3DFMT_P8)
4682 && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)))
4684 d3dfmt_p8_upload_palette(iface, convert);
4685 This->Flags &= ~SFLAG_CONVERTED;
4686 mem = This->resource.allocatedMemory;
4687 } else {
4688 This->Flags &= ~SFLAG_CONVERTED;
4689 mem = This->resource.allocatedMemory;
4692 /* Make sure the correct pitch is used */
4693 ENTER_GL();
4694 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4695 LEAVE_GL();
4697 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4698 TRACE("non power of two support\n");
4699 if(!(This->Flags & alloc_flag)) {
4700 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4701 This->Flags |= alloc_flag;
4703 if (mem || (This->Flags & SFLAG_PBO)) {
4704 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4706 } else {
4707 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4708 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4710 if(!(This->Flags & alloc_flag)) {
4711 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4712 This->Flags |= alloc_flag;
4714 if (mem || (This->Flags & SFLAG_PBO)) {
4715 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4719 /* Restore the default pitch */
4720 ENTER_GL();
4721 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4722 LEAVE_GL();
4724 /* Don't delete PBO memory */
4725 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4726 HeapFree(GetProcessHeap(), 0, mem);
4730 if(rect == NULL) {
4731 This->Flags |= flag;
4734 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !(This->Flags & SFLAG_SWAPCHAIN)
4735 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4736 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4737 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4740 return WINED3D_OK;
4743 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4745 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4746 IWineD3DSwapChain *swapchain = NULL;
4748 /* Update the drawable size method */
4749 if(container) {
4750 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4752 if(swapchain) {
4753 This->get_drawable_size = get_drawable_size_swapchain;
4754 IWineD3DSwapChain_Release(swapchain);
4755 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4756 switch(wined3d_settings.offscreen_rendering_mode) {
4757 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4758 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4759 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4763 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4766 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4767 return SURFACE_OPENGL;
4770 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4771 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4772 HRESULT hr;
4774 /* If there's no destination surface there is nothing to do */
4775 if(!This->overlay_dest) return WINED3D_OK;
4777 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4778 * update the overlay. Prevent an endless recursion
4780 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
4781 return WINED3D_OK;
4783 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
4784 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4785 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4786 NULL, WINED3DTEXF_LINEAR);
4787 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
4789 return hr;
4792 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4794 /* IUnknown */
4795 IWineD3DBaseSurfaceImpl_QueryInterface,
4796 IWineD3DBaseSurfaceImpl_AddRef,
4797 IWineD3DSurfaceImpl_Release,
4798 /* IWineD3DResource */
4799 IWineD3DBaseSurfaceImpl_GetParent,
4800 IWineD3DBaseSurfaceImpl_GetDevice,
4801 IWineD3DBaseSurfaceImpl_SetPrivateData,
4802 IWineD3DBaseSurfaceImpl_GetPrivateData,
4803 IWineD3DBaseSurfaceImpl_FreePrivateData,
4804 IWineD3DBaseSurfaceImpl_SetPriority,
4805 IWineD3DBaseSurfaceImpl_GetPriority,
4806 IWineD3DSurfaceImpl_PreLoad,
4807 IWineD3DSurfaceImpl_UnLoad,
4808 IWineD3DBaseSurfaceImpl_GetType,
4809 /* IWineD3DSurface */
4810 IWineD3DBaseSurfaceImpl_GetContainer,
4811 IWineD3DBaseSurfaceImpl_GetDesc,
4812 IWineD3DSurfaceImpl_LockRect,
4813 IWineD3DSurfaceImpl_UnlockRect,
4814 IWineD3DSurfaceImpl_GetDC,
4815 IWineD3DSurfaceImpl_ReleaseDC,
4816 IWineD3DSurfaceImpl_Flip,
4817 IWineD3DSurfaceImpl_Blt,
4818 IWineD3DBaseSurfaceImpl_GetBltStatus,
4819 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4820 IWineD3DBaseSurfaceImpl_IsLost,
4821 IWineD3DBaseSurfaceImpl_Restore,
4822 IWineD3DSurfaceImpl_BltFast,
4823 IWineD3DBaseSurfaceImpl_GetPalette,
4824 IWineD3DBaseSurfaceImpl_SetPalette,
4825 IWineD3DSurfaceImpl_RealizePalette,
4826 IWineD3DBaseSurfaceImpl_SetColorKey,
4827 IWineD3DBaseSurfaceImpl_GetPitch,
4828 IWineD3DSurfaceImpl_SetMem,
4829 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4830 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4831 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4832 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4833 IWineD3DBaseSurfaceImpl_SetClipper,
4834 IWineD3DBaseSurfaceImpl_GetClipper,
4835 /* Internal use: */
4836 IWineD3DSurfaceImpl_LoadTexture,
4837 IWineD3DSurfaceImpl_BindTexture,
4838 IWineD3DSurfaceImpl_SaveSnapshot,
4839 IWineD3DSurfaceImpl_SetContainer,
4840 IWineD3DSurfaceImpl_GetGlDesc,
4841 IWineD3DBaseSurfaceImpl_GetData,
4842 IWineD3DSurfaceImpl_SetFormat,
4843 IWineD3DSurfaceImpl_PrivateSetup,
4844 IWineD3DSurfaceImpl_ModifyLocation,
4845 IWineD3DSurfaceImpl_LoadLocation,
4846 IWineD3DSurfaceImpl_GetImplType,
4847 IWineD3DSurfaceImpl_DrawOverlay
4849 #undef GLINFO_LOCATION
4851 #define GLINFO_LOCATION device->adapter->gl_info
4852 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
4853 static void ffp_blit_free(IWineD3DDevice *iface) { }
4855 static HRESULT ffp_blit_set(IWineD3DDevice *iface, const struct GlPixelFormatDesc *format_desc,
4856 GLenum textype, UINT width, UINT height)
4858 ENTER_GL();
4859 glEnable(textype);
4860 checkGLcall("glEnable(textype)");
4861 LEAVE_GL();
4862 return WINED3D_OK;
4865 static void ffp_blit_unset(IWineD3DDevice *iface) {
4866 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4867 ENTER_GL();
4868 glDisable(GL_TEXTURE_2D);
4869 checkGLcall("glDisable(GL_TEXTURE_2D)");
4870 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP)) {
4871 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4872 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4874 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
4875 glDisable(GL_TEXTURE_RECTANGLE_ARB);
4876 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4878 LEAVE_GL();
4881 static BOOL ffp_blit_color_fixup_supported(struct color_fixup_desc fixup)
4883 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4885 TRACE("Checking support for fixup:\n");
4886 dump_color_fixup_desc(fixup);
4889 /* We only support identity conversions. */
4890 if (is_identity_fixup(fixup))
4892 TRACE("[OK]\n");
4893 return TRUE;
4896 TRACE("[FAILED]\n");
4897 return FALSE;
4900 const struct blit_shader ffp_blit = {
4901 ffp_blit_alloc,
4902 ffp_blit_free,
4903 ffp_blit_set,
4904 ffp_blit_unset,
4905 ffp_blit_color_fixup_supported