push 65f191514f6da34cdfbbc73b8f5d66c351839600
[wine/hacks.git] / dlls / wined3d / surface.c
blobd65105dfb699b5dc59ef8acc66094c3bffe7d2c2
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 surface_force_reload(IWineD3DSurface *iface)
39 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
41 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
44 void surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
46 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
47 GLuint *name;
48 DWORD flag;
50 if(srgb)
52 name = &This->glDescription.srgbTextureName;
53 flag = SFLAG_INSRGBTEX;
55 else
57 name = &This->glDescription.textureName;
58 flag = SFLAG_INTEXTURE;
61 TRACE("(%p) : setting texture name %u\n", This, new_name);
63 if (!*name && new_name)
65 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
66 * surface has no texture name yet. See if we can get rid of this. */
67 if (This->Flags & flag)
68 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
69 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
72 *name = new_name;
73 surface_force_reload(iface);
76 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
78 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
80 TRACE("(%p) : setting target %#x\n", This, target);
82 if (This->glDescription.target != target)
84 if (target == GL_TEXTURE_RECTANGLE_ARB)
86 This->Flags &= ~SFLAG_NORMCOORD;
88 else if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB)
90 This->Flags |= SFLAG_NORMCOORD;
93 This->glDescription.target = target;
94 surface_force_reload(iface);
97 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
98 int active_sampler;
100 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
101 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
102 * gl states. The current texture unit should always be a valid one.
104 * To be more specific, this is tricky because we can implicitly be called
105 * from sampler() in state.c. This means we can't touch anything other than
106 * whatever happens to be the currently active texture, or we would risk
107 * marking already applied sampler states dirty again.
109 * TODO: Track the current active texture per GL context instead of using glGet
111 GLint active_texture;
112 ENTER_GL();
113 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
114 LEAVE_GL();
115 active_sampler = This->resource.wineD3DDevice->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
117 if (active_sampler != -1) {
118 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_sampler));
120 IWineD3DSurface_BindTexture((IWineD3DSurface *)This, srgb);
123 /* This function checks if the primary render target uses the 8bit paletted format. */
124 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
126 if (device->render_targets && device->render_targets[0]) {
127 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
128 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
129 && (render_target->resource.format_desc->format == WINED3DFMT_P8))
130 return TRUE;
132 return FALSE;
135 /* This call just downloads data, the caller is responsible for activating the
136 * right context and binding the correct texture. */
137 static void surface_download_data(IWineD3DSurfaceImpl *This) {
138 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
140 /* Only support read back of converted P8 surfaces */
141 if (This->Flags & SFLAG_CONVERTED && format_desc->format != WINED3DFMT_P8)
143 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
144 return;
147 ENTER_GL();
149 if (format_desc->format == WINED3DFMT_DXT1 || format_desc->format == WINED3DFMT_DXT2
150 || format_desc->format == WINED3DFMT_DXT3 || format_desc->format == WINED3DFMT_DXT4
151 || format_desc->format == WINED3DFMT_DXT5 || format_desc->format == WINED3DFMT_ATI2N)
153 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
154 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
155 } else {
156 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n",
157 This, This->glDescription.level, format_desc->glFormat, format_desc->glType,
158 This->resource.allocatedMemory);
160 if(This->Flags & SFLAG_PBO) {
161 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
162 checkGLcall("glBindBufferARB");
163 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
164 checkGLcall("glGetCompressedTexImageARB()");
165 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
166 checkGLcall("glBindBufferARB");
167 } else {
168 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
169 checkGLcall("glGetCompressedTexImageARB()");
172 LEAVE_GL();
173 } else {
174 void *mem;
175 GLenum format = format_desc->glFormat;
176 GLenum type = format_desc->glType;
177 int src_pitch = 0;
178 int dst_pitch = 0;
180 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
181 if (format_desc->format == WINED3DFMT_P8 && primary_render_target_is_p8(This->resource.wineD3DDevice))
183 format = GL_ALPHA;
184 type = GL_UNSIGNED_BYTE;
187 if (This->Flags & SFLAG_NONPOW2) {
188 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
189 src_pitch = format_desc->byte_count * This->pow2Width;
190 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
191 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
192 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
193 } else {
194 mem = This->resource.allocatedMemory;
197 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
198 format, type, mem);
200 if(This->Flags & SFLAG_PBO) {
201 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
202 checkGLcall("glBindBufferARB");
204 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
205 type, NULL);
206 checkGLcall("glGetTexImage()");
208 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
209 checkGLcall("glBindBufferARB");
210 } else {
211 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
212 type, mem);
213 checkGLcall("glGetTexImage()");
215 LEAVE_GL();
217 if (This->Flags & SFLAG_NONPOW2) {
218 const BYTE *src_data;
219 BYTE *dst_data;
220 UINT y;
222 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
223 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
224 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
226 * We're doing this...
228 * instead of boxing the texture :
229 * |<-texture width ->| -->pow2width| /\
230 * |111111111111111111| | |
231 * |222 Texture 222222| boxed empty | texture height
232 * |3333 Data 33333333| | |
233 * |444444444444444444| | \/
234 * ----------------------------------- |
235 * | boxed empty | boxed empty | pow2height
236 * | | | \/
237 * -----------------------------------
240 * we're repacking the data to the expected texture width
242 * |<-texture width ->| -->pow2width| /\
243 * |111111111111111111222222222222222| |
244 * |222333333333333333333444444444444| texture height
245 * |444444 | |
246 * | | \/
247 * | | |
248 * | empty | pow2height
249 * | | \/
250 * -----------------------------------
252 * == is the same as
254 * |<-texture width ->| /\
255 * |111111111111111111|
256 * |222222222222222222|texture height
257 * |333333333333333333|
258 * |444444444444444444| \/
259 * --------------------
261 * this also means that any references to allocatedMemory should work with the data as if were a
262 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
264 * internally the texture is still stored in a boxed format so any references to textureName will
265 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
267 * Performance should not be an issue, because applications normally do not lock the surfaces when
268 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
269 * and doesn't have to be re-read.
271 src_data = mem;
272 dst_data = This->resource.allocatedMemory;
273 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
274 for (y = 1 ; y < This->currentDesc.Height; y++) {
275 /* skip the first row */
276 src_data += src_pitch;
277 dst_data += dst_pitch;
278 memcpy(dst_data, src_data, dst_pitch);
281 HeapFree(GetProcessHeap(), 0, mem);
285 /* Surface has now been downloaded */
286 This->Flags |= SFLAG_INSYSMEM;
289 /* This call just uploads data, the caller is responsible for activating the
290 * right context and binding the correct texture. */
291 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
292 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
294 if (format_desc->heightscale != 1.0 && format_desc->heightscale != 0.0) height *= format_desc->heightscale;
296 if (format_desc->format == WINED3DFMT_DXT1 || format_desc->format == WINED3DFMT_DXT2
297 || format_desc->format == WINED3DFMT_DXT3 || format_desc->format == WINED3DFMT_DXT4
298 || format_desc->format == WINED3DFMT_DXT5 || format_desc->format == WINED3DFMT_ATI2N)
300 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
301 FIXME("Using DXT1/3/5 without advertized support\n");
302 } else {
303 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
304 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
305 * function uses glCompressedTexImage2D instead of the SubImage call
307 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
308 ENTER_GL();
310 if(This->Flags & SFLAG_PBO) {
311 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
312 checkGLcall("glBindBufferARB");
313 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
315 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
316 width, height, 0 /* border */, This->resource.size, NULL));
317 checkGLcall("glCompressedTexSubImage2D");
319 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
320 checkGLcall("glBindBufferARB");
321 } else {
322 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
323 width, height, 0 /* border */, This->resource.size, data));
324 checkGLcall("glCompressedTexSubImage2D");
326 LEAVE_GL();
328 } else {
329 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
330 ENTER_GL();
332 if(This->Flags & SFLAG_PBO) {
333 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
334 checkGLcall("glBindBufferARB");
335 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
337 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
338 checkGLcall("glTexSubImage2D");
340 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
341 checkGLcall("glBindBufferARB");
343 else {
344 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
345 checkGLcall("glTexSubImage2D");
348 LEAVE_GL();
352 /* This call just allocates the texture, the caller is responsible for
353 * activating the right context and binding the correct texture. */
354 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
355 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
356 BOOL enable_client_storage = FALSE;
357 const BYTE *mem = NULL;
359 if (format_desc->heightscale != 1.0 && format_desc->heightscale != 0.0) height *= format_desc->heightscale;
361 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",
362 This, This->glDescription.target, This->glDescription.level, debug_d3dformat(format_desc->format),
363 internal, width, height, format, type);
365 if (format_desc->format == WINED3DFMT_DXT1 || format_desc->format == WINED3DFMT_DXT2
366 || format_desc->format == WINED3DFMT_DXT3 || format_desc->format == WINED3DFMT_DXT4
367 || format_desc->format == WINED3DFMT_DXT5 || format_desc->format == WINED3DFMT_ATI2N)
369 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
370 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
372 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
373 * once, unfortunately
375 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
376 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
377 This->Flags |= SFLAG_CLIENT;
378 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
379 ENTER_GL();
380 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
381 width, height, 0 /* border */, This->resource.size, mem));
382 LEAVE_GL();
385 return;
388 ENTER_GL();
390 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
391 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
392 /* In some cases we want to disable client storage.
393 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
394 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
395 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
396 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
397 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
399 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
400 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
401 This->Flags &= ~SFLAG_CLIENT;
402 enable_client_storage = TRUE;
403 } else {
404 This->Flags |= SFLAG_CLIENT;
406 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
407 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
409 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
412 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
413 checkGLcall("glTexImage2D");
415 if(enable_client_storage) {
416 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
417 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
419 LEAVE_GL();
422 /* In D3D the depth stencil dimensions have to be greater than or equal to the
423 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
424 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
425 /* GL locking is done by the caller */
426 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
427 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
428 renderbuffer_entry_t *entry;
429 GLuint renderbuffer = 0;
430 unsigned int src_width, src_height;
432 src_width = This->pow2Width;
433 src_height = This->pow2Height;
435 /* A depth stencil smaller than the render target is not valid */
436 if (width > src_width || height > src_height) return;
438 /* Remove any renderbuffer set if the sizes match */
439 if (width == src_width && height == src_height) {
440 This->current_renderbuffer = NULL;
441 return;
444 /* Look if we've already got a renderbuffer of the correct dimensions */
445 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
446 if (entry->width == width && entry->height == height) {
447 renderbuffer = entry->id;
448 This->current_renderbuffer = entry;
449 break;
453 if (!renderbuffer) {
454 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
455 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
456 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
457 This->resource.format_desc->glInternal, width, height));
459 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
460 entry->width = width;
461 entry->height = height;
462 entry->id = renderbuffer;
463 list_add_head(&This->renderbuffers, &entry->entry);
465 This->current_renderbuffer = entry;
468 checkGLcall("set_compatible_renderbuffer");
471 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
472 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
473 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
475 TRACE("(%p) : swapchain %p\n", This, swapchain);
477 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
478 TRACE("Returning GL_BACK\n");
479 return GL_BACK;
480 } else if (swapchain_impl->frontBuffer == iface) {
481 TRACE("Returning GL_FRONT\n");
482 return GL_FRONT;
485 FIXME("Higher back buffer, returning GL_BACK\n");
486 return GL_BACK;
489 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
490 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
492 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
493 IWineD3DBaseTexture *baseTexture = NULL;
495 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
496 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
498 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
499 if (dirty_rect)
501 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
502 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
503 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
504 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
506 else
508 This->dirtyRect.left = 0;
509 This->dirtyRect.top = 0;
510 This->dirtyRect.right = This->currentDesc.Width;
511 This->dirtyRect.bottom = This->currentDesc.Height;
514 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
515 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
517 /* if the container is a basetexture then mark it dirty. */
518 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
520 TRACE("Passing to container\n");
521 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
522 IWineD3DBaseTexture_Release(baseTexture);
526 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
528 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
529 ULONG ref = InterlockedDecrement(&This->resource.ref);
530 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
531 if (ref == 0) {
532 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
533 renderbuffer_entry_t *entry, *entry2;
534 TRACE("(%p) : cleaning up\n", This);
536 /* Need a context to destroy the texture. Use the currently active render target, but only if
537 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
538 * When destroying the primary rt, Uninit3D will activate a context before doing anything
540 if(device->render_targets && device->render_targets[0]) {
541 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
544 ENTER_GL();
545 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
546 TRACE("Deleting texture %d\n", This->glDescription.textureName);
547 glDeleteTextures(1, &This->glDescription.textureName);
550 if(This->Flags & SFLAG_PBO) {
551 /* Delete the PBO */
552 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
555 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
556 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
557 HeapFree(GetProcessHeap(), 0, entry);
559 LEAVE_GL();
561 if(This->Flags & SFLAG_DIBSECTION) {
562 /* Release the DC */
563 SelectObject(This->hDC, This->dib.holdbitmap);
564 DeleteDC(This->hDC);
565 /* Release the DIB section */
566 DeleteObject(This->dib.DIBsection);
567 This->dib.bitmap_data = NULL;
568 This->resource.allocatedMemory = NULL;
570 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
572 HeapFree(GetProcessHeap(), 0, This->palette9);
574 resource_cleanup((IWineD3DResource *)iface);
576 if(This->overlay_dest) {
577 list_remove(&This->overlay_entry);
580 TRACE("(%p) Released\n", This);
581 HeapFree(GetProcessHeap(), 0, This);
584 return ref;
587 /* ****************************************************
588 IWineD3DSurface IWineD3DResource parts follow
589 **************************************************** */
591 void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
593 /* TODO: check for locks */
594 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
595 IWineD3DBaseTexture *baseTexture = NULL;
596 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
598 TRACE("(%p)Checking to see if the container is a base texture\n", This);
599 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
600 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
601 TRACE("Passing to container\n");
602 tex_impl->baseTexture.internal_preload(baseTexture, SRGB_RGB);
603 IWineD3DBaseTexture_Release(baseTexture);
604 } else {
605 TRACE("(%p) : About to load surface\n", This);
607 if(!device->isInDraw) {
608 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
611 if (This->resource.format_desc->format == WINED3DFMT_P8
612 || This->resource.format_desc->format == WINED3DFMT_A8P8)
614 if(palette9_changed(This)) {
615 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
616 /* TODO: This is not necessarily needed with hw palettized texture support */
617 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
618 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
619 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
623 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
625 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
626 /* Tell opengl to try and keep this texture in video ram (well mostly) */
627 GLclampf tmp;
628 tmp = 0.9f;
629 ENTER_GL();
630 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
631 LEAVE_GL();
634 return;
637 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
638 surface_internal_preload(iface, SRGB_ANY);
641 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
642 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
643 This->resource.allocatedMemory =
644 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
646 ENTER_GL();
647 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
648 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
649 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
650 checkGLcall("glGetBufferSubData");
651 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
652 checkGLcall("glDeleteBuffers");
653 LEAVE_GL();
655 This->pbo = 0;
656 This->Flags &= ~SFLAG_PBO;
659 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
660 IWineD3DBaseTexture *texture = NULL;
661 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
662 renderbuffer_entry_t *entry, *entry2;
663 TRACE("(%p)\n", iface);
665 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
666 /* Default pool resources are supposed to be destroyed before Reset is called.
667 * Implicit resources stay however. So this means we have an implicit render target
668 * or depth stencil. The content may be destroyed, but we still have to tear down
669 * opengl resources, so we cannot leave early.
671 * Put the most up to date surface location into the drawable. D3D-wise this content
672 * is undefined, so it would be nowhere, but that would make the location management
673 * more complicated. The drawable is a sane location, because if we mark sysmem or
674 * texture up to date, drawPrim will copy the uninitialized texture or sysmem to the
675 * uninitialized drawable. That's pointless and we'd have to allocate the texture /
676 * sysmem copy here.
678 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
679 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
680 } else {
681 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
683 } else {
684 /* Load the surface into system memory */
685 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
686 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
688 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
689 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
691 /* Destroy PBOs, but load them into real sysmem before */
692 if(This->Flags & SFLAG_PBO) {
693 surface_remove_pbo(This);
696 /* Destroy fbo render buffers. This is needed for implicit render targets, for
697 * all application-created targets the application has to release the surface
698 * before calling _Reset
700 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
701 ENTER_GL();
702 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
703 LEAVE_GL();
704 list_remove(&entry->entry);
705 HeapFree(GetProcessHeap(), 0, entry);
707 list_init(&This->renderbuffers);
708 This->current_renderbuffer = NULL;
710 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
711 * destroy it
713 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
714 if(!texture) {
715 ENTER_GL();
716 glDeleteTextures(1, &This->glDescription.textureName);
717 This->glDescription.textureName = 0;
718 LEAVE_GL();
719 } else {
720 IWineD3DBaseTexture_Release(texture);
722 return;
725 /* ******************************************************
726 IWineD3DSurface IWineD3DSurface parts follow
727 ****************************************************** */
729 static void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription)
731 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
732 TRACE("(%p) : returning %p\n", This, &This->glDescription);
733 *glDescription = &This->glDescription;
736 /* Read the framebuffer back into the surface */
737 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
738 IWineD3DSwapChainImpl *swapchain;
739 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
740 BYTE *mem;
741 GLint fmt;
742 GLint type;
743 BYTE *row, *top, *bottom;
744 int i;
745 BOOL bpp;
746 RECT local_rect;
747 BOOL srcIsUpsideDown;
748 GLint rowLen = 0;
749 GLint skipPix = 0;
750 GLint skipRow = 0;
752 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
753 static BOOL warned = FALSE;
754 if(!warned) {
755 ERR("The application tries to lock the render target, but render target locking is disabled\n");
756 warned = TRUE;
758 return;
761 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
762 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
763 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
764 * context->last_was_blit set on the unlock.
766 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
767 ENTER_GL();
769 /* Select the correct read buffer, and give some debug output.
770 * There is no need to keep track of the current read buffer or reset it, every part of the code
771 * that reads sets the read buffer as desired.
773 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
775 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
776 TRACE("Locking %#x buffer\n", buffer);
777 glReadBuffer(buffer);
778 checkGLcall("glReadBuffer");
780 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
781 srcIsUpsideDown = FALSE;
782 } else {
783 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
784 * Read from the back buffer
786 TRACE("Locking offscreen render target\n");
787 glReadBuffer(myDevice->offscreenBuffer);
788 srcIsUpsideDown = TRUE;
791 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
792 if(!rect) {
793 local_rect.left = 0;
794 local_rect.top = 0;
795 local_rect.right = This->currentDesc.Width;
796 local_rect.bottom = This->currentDesc.Height;
797 } else {
798 local_rect = *rect;
800 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
802 switch(This->resource.format_desc->format)
804 case WINED3DFMT_P8:
806 if(primary_render_target_is_p8(myDevice)) {
807 /* In case of P8 render targets the index is stored in the alpha component */
808 fmt = GL_ALPHA;
809 type = GL_UNSIGNED_BYTE;
810 mem = dest;
811 bpp = This->resource.format_desc->byte_count;
812 } else {
813 /* GL can't return palettized data, so read ARGB pixels into a
814 * separate block of memory and convert them into palettized format
815 * in software. Slow, but if the app means to use palettized render
816 * targets and locks it...
818 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
819 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
820 * for the color channels when palettizing the colors.
822 fmt = GL_RGB;
823 type = GL_UNSIGNED_BYTE;
824 pitch *= 3;
825 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
826 if(!mem) {
827 ERR("Out of memory\n");
828 LEAVE_GL();
829 return;
831 bpp = This->resource.format_desc->byte_count * 3;
834 break;
836 default:
837 mem = dest;
838 fmt = This->resource.format_desc->glFormat;
839 type = This->resource.format_desc->glType;
840 bpp = This->resource.format_desc->byte_count;
843 if(This->Flags & SFLAG_PBO) {
844 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
845 checkGLcall("glBindBufferARB");
846 if(mem != NULL) {
847 ERR("mem not null for pbo -- unexpected\n");
848 mem = NULL;
852 /* Save old pixel store pack state */
853 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
854 checkGLcall("glIntegerv");
855 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
856 checkGLcall("glIntegerv");
857 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
858 checkGLcall("glIntegerv");
860 /* Setup pixel store pack state -- to glReadPixels into the correct place */
861 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
862 checkGLcall("glPixelStorei");
863 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
864 checkGLcall("glPixelStorei");
865 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
866 checkGLcall("glPixelStorei");
868 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
869 local_rect.right - local_rect.left,
870 local_rect.bottom - local_rect.top,
871 fmt, type, mem);
872 checkGLcall("glReadPixels");
874 /* Reset previous pixel store pack state */
875 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
876 checkGLcall("glPixelStorei");
877 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
878 checkGLcall("glPixelStorei");
879 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
880 checkGLcall("glPixelStorei");
882 if(This->Flags & SFLAG_PBO) {
883 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
884 checkGLcall("glBindBufferARB");
886 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
887 * to get a pointer to it and perform the flipping in software. This is a lot
888 * faster than calling glReadPixels for each line. In case we want more speed
889 * we should rerender it flipped in a FBO and read the data back from the FBO. */
890 if(!srcIsUpsideDown) {
891 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
892 checkGLcall("glBindBufferARB");
894 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
895 checkGLcall("glMapBufferARB");
899 /* TODO: Merge this with the palettization loop below for P8 targets */
900 if(!srcIsUpsideDown) {
901 UINT len, off;
902 /* glReadPixels returns the image upside down, and there is no way to prevent this.
903 Flip the lines in software */
904 len = (local_rect.right - local_rect.left) * bpp;
905 off = local_rect.left * bpp;
907 row = HeapAlloc(GetProcessHeap(), 0, len);
908 if(!row) {
909 ERR("Out of memory\n");
910 if (This->resource.format_desc->format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
911 LEAVE_GL();
912 return;
915 top = mem + pitch * local_rect.top;
916 bottom = mem + pitch * (local_rect.bottom - 1);
917 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
918 memcpy(row, top + off, len);
919 memcpy(top + off, bottom + off, len);
920 memcpy(bottom + off, row, len);
921 top += pitch;
922 bottom -= pitch;
924 HeapFree(GetProcessHeap(), 0, row);
926 /* Unmap the temp PBO buffer */
927 if(This->Flags & SFLAG_PBO) {
928 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
929 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
933 LEAVE_GL();
935 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
936 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
937 * the same color but we have no choice.
938 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
940 if ((This->resource.format_desc->format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice))
942 const PALETTEENTRY *pal = NULL;
943 DWORD width = pitch / 3;
944 int x, y, c;
946 if(This->palette) {
947 pal = This->palette->palents;
948 } else {
949 ERR("Palette is missing, cannot perform inverse palette lookup\n");
950 HeapFree(GetProcessHeap(), 0, mem);
951 return ;
954 for(y = local_rect.top; y < local_rect.bottom; y++) {
955 for(x = local_rect.left; x < local_rect.right; x++) {
956 /* start lines pixels */
957 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
958 const BYTE *green = blue + 1;
959 const BYTE *red = green + 1;
961 for(c = 0; c < 256; c++) {
962 if(*red == pal[c].peRed &&
963 *green == pal[c].peGreen &&
964 *blue == pal[c].peBlue)
966 *((BYTE *) dest + y * width + x) = c;
967 break;
972 HeapFree(GetProcessHeap(), 0, mem);
976 /* Read the framebuffer contents into a texture */
977 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
979 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
980 IWineD3DSwapChainImpl *swapchain;
981 int bpp;
982 GLenum format, internal, type;
983 CONVERT_TYPES convert;
984 GLint prevRead;
985 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
987 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
989 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
990 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
991 * states in the stateblock, and no driver was found yet that had bugs in that regard.
993 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
994 surface_bind_and_dirtify(This, srgb);
996 ENTER_GL();
997 glGetIntegerv(GL_READ_BUFFER, &prevRead);
998 LEAVE_GL();
1000 /* Select the correct read buffer, and give some debug output.
1001 * There is no need to keep track of the current read buffer or reset it, every part of the code
1002 * that reads sets the read buffer as desired.
1004 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
1006 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1007 TRACE("Locking %#x buffer\n", buffer);
1009 ENTER_GL();
1010 glReadBuffer(buffer);
1011 checkGLcall("glReadBuffer");
1012 LEAVE_GL();
1014 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1015 } else {
1016 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1017 * Read from the back buffer
1019 TRACE("Locking offscreen render target\n");
1020 ENTER_GL();
1021 glReadBuffer(device->offscreenBuffer);
1022 checkGLcall("glReadBuffer");
1023 LEAVE_GL();
1026 if(!(This->Flags & alloc_flag)) {
1027 surface_allocate_surface(This, internal, This->pow2Width,
1028 This->pow2Height, format, type);
1029 This->Flags |= alloc_flag;
1032 ENTER_GL();
1033 /* If !SrcIsUpsideDown we should flip the surface.
1034 * This can be done using glCopyTexSubImage2D but this
1035 * is VERY slow, so don't do that. We should prevent
1036 * this code from getting called in such cases or perhaps
1037 * we can use FBOs */
1039 glCopyTexSubImage2D(This->glDescription.target,
1040 This->glDescription.level,
1041 0, 0, 0, 0,
1042 This->currentDesc.Width,
1043 This->currentDesc.Height);
1044 checkGLcall("glCopyTexSubImage2D");
1046 glReadBuffer(prevRead);
1047 checkGLcall("glReadBuffer");
1049 LEAVE_GL();
1050 TRACE("Updated target %d\n", This->glDescription.target);
1053 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
1054 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1055 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1056 * changed
1058 if(!(This->Flags & SFLAG_DYNLOCK)) {
1059 This->lockCount++;
1060 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1061 if(This->lockCount > MAXLOCKCOUNT) {
1062 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1063 This->Flags |= SFLAG_DYNLOCK;
1067 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1068 * Also don't create a PBO for systemmem surfaces.
1070 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
1071 GLenum error;
1072 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1074 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1075 ENTER_GL();
1077 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1078 error = glGetError();
1079 if(This->pbo == 0 || error != GL_NO_ERROR) {
1080 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1083 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1085 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1086 checkGLcall("glBindBufferARB");
1088 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1089 checkGLcall("glBufferDataARB");
1091 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1092 checkGLcall("glBindBufferARB");
1094 /* We don't need the system memory anymore and we can't even use it for PBOs */
1095 if(!(This->Flags & SFLAG_CLIENT)) {
1096 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1097 This->resource.heapMemory = NULL;
1099 This->resource.allocatedMemory = NULL;
1100 This->Flags |= SFLAG_PBO;
1101 LEAVE_GL();
1102 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
1103 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1104 * or a pbo to map
1106 if(!This->resource.heapMemory) {
1107 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1109 This->resource.allocatedMemory =
1110 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1111 if(This->Flags & SFLAG_INSYSMEM) {
1112 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1117 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1118 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1119 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1121 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1123 /* This is also done in the base class, but we have to verify this before loading any data from
1124 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1125 * may interfere, and all other bad things may happen
1127 if (This->Flags & SFLAG_LOCKED) {
1128 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1129 return WINED3DERR_INVALIDCALL;
1131 This->Flags |= SFLAG_LOCKED;
1133 if (!(This->Flags & SFLAG_LOCKABLE))
1135 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1138 if (Flags & WINED3DLOCK_DISCARD) {
1139 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1140 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1141 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1142 This->Flags |= SFLAG_INSYSMEM;
1143 goto lock_end;
1146 if (This->Flags & SFLAG_INSYSMEM) {
1147 TRACE("Local copy is up to date, not downloading data\n");
1148 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1149 goto lock_end;
1152 /* Now download the surface content from opengl
1153 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1154 * Offscreen targets which are not active at the moment or are higher targets(FBOs) can be locked with the texture path
1156 if ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])
1158 const RECT *pass_rect = pRect;
1160 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1161 * because most caller functions do not need that. So do that here
1163 if(pRect &&
1164 pRect->top == 0 &&
1165 pRect->left == 0 &&
1166 pRect->right == This->currentDesc.Width &&
1167 pRect->bottom == This->currentDesc.Height) {
1168 pass_rect = NULL;
1171 switch(wined3d_settings.rendertargetlock_mode) {
1172 case RTL_TEXDRAW:
1173 case RTL_TEXTEX:
1174 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1175 #if 0
1176 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1177 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1178 * This may be faster on some cards
1180 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1181 #endif
1182 /* drop through */
1184 case RTL_AUTO:
1185 case RTL_READDRAW:
1186 case RTL_READTEX:
1187 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1188 break;
1190 case RTL_DISABLE:
1191 break;
1193 } else if(iface == myDevice->stencilBufferTarget) {
1194 /** the depth stencil in openGL has a format of GL_FLOAT
1195 * which should be good for WINED3DFMT_D16_LOCKABLE
1196 * and WINED3DFMT_D16
1197 * it is unclear what format the stencil buffer is in except.
1198 * 'Each index is converted to fixed point...
1199 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1200 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1201 * glReadPixels(This->lockedRect.left,
1202 * This->lockedRect.bottom - j - 1,
1203 * This->lockedRect.right - This->lockedRect.left,
1204 * 1,
1205 * GL_DEPTH_COMPONENT,
1206 * type,
1207 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1209 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1210 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1211 * none of that is the case the problem is not in this function :-)
1212 ********************************************/
1213 FIXME("Depth stencil locking not supported yet\n");
1214 } else {
1215 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1216 TRACE("locking an ordinary surface\n");
1217 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1220 lock_end:
1221 if(This->Flags & SFLAG_PBO) {
1222 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1223 ENTER_GL();
1224 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1225 checkGLcall("glBindBufferARB");
1227 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1228 if(This->resource.allocatedMemory) {
1229 ERR("The surface already has PBO memory allocated!\n");
1232 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1233 checkGLcall("glMapBufferARB");
1235 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1236 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1237 checkGLcall("glBindBufferARB");
1239 LEAVE_GL();
1242 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1243 /* Don't dirtify */
1244 } else {
1245 IWineD3DBaseTexture *pBaseTexture;
1247 * Dirtify on lock
1248 * as seen in msdn docs
1250 surface_add_dirty_rect(iface, pRect);
1252 /** Dirtify Container if needed */
1253 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1254 TRACE("Making container dirty\n");
1255 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1256 IWineD3DBaseTexture_Release(pBaseTexture);
1257 } else {
1258 TRACE("Surface is standalone, no need to dirty the container\n");
1262 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1265 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1266 GLint prev_store;
1267 GLint prev_rasterpos[4];
1268 GLint skipBytes = 0;
1269 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1270 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1271 IWineD3DSwapChainImpl *swapchain;
1273 /* Activate the correct context for the render target */
1274 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1275 ENTER_GL();
1277 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
1278 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1279 TRACE("Unlocking %#x buffer\n", buffer);
1280 glDrawBuffer(buffer);
1281 checkGLcall("glDrawBuffer");
1283 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1284 } else {
1285 /* Primary offscreen render target */
1286 TRACE("Offscreen render target\n");
1287 glDrawBuffer(myDevice->offscreenBuffer);
1288 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1291 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1292 checkGLcall("glIntegerv");
1293 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1294 checkGLcall("glIntegerv");
1295 glPixelZoom(1.0, -1.0);
1296 checkGLcall("glPixelZoom");
1298 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1299 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1300 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1302 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1303 checkGLcall("glRasterPos2f");
1305 /* Some drivers(radeon dri, others?) don't like exceptions during
1306 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1307 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1308 * catch to put the dib section in InSync mode, which leads to a crash
1309 * and a blocked x server on my radeon card.
1311 * The following lines read the dib section so it is put in InSync mode
1312 * before glDrawPixels is called and the crash is prevented. There won't
1313 * be any interfering gdi accesses, because UnlockRect is called from
1314 * ReleaseDC, and the app won't use the dc any more afterwards.
1316 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1317 volatile BYTE read;
1318 read = This->resource.allocatedMemory[0];
1321 if(This->Flags & SFLAG_PBO) {
1322 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1323 checkGLcall("glBindBufferARB");
1326 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1327 if(This->Flags & SFLAG_LOCKED) {
1328 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1329 (This->lockedRect.bottom - This->lockedRect.top)-1,
1330 fmt, type,
1331 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1332 checkGLcall("glDrawPixels");
1333 } else {
1334 glDrawPixels(This->currentDesc.Width,
1335 This->currentDesc.Height,
1336 fmt, type, mem);
1337 checkGLcall("glDrawPixels");
1340 if(This->Flags & SFLAG_PBO) {
1341 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1342 checkGLcall("glBindBufferARB");
1345 glPixelZoom(1.0,1.0);
1346 checkGLcall("glPixelZoom");
1348 glRasterPos3iv(&prev_rasterpos[0]);
1349 checkGLcall("glRasterPos3iv");
1351 /* Reset to previous pack row length */
1352 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1353 checkGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1355 if(!swapchain) {
1356 glDrawBuffer(myDevice->offscreenBuffer);
1357 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1358 } else if(swapchain->backBuffer) {
1359 glDrawBuffer(GL_BACK);
1360 checkGLcall("glDrawBuffer(GL_BACK)");
1361 } else {
1362 glDrawBuffer(GL_FRONT);
1363 checkGLcall("glDrawBuffer(GL_FRONT)");
1365 LEAVE_GL();
1367 return;
1370 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1371 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1372 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1373 BOOL fullsurface;
1375 if (!(This->Flags & SFLAG_LOCKED)) {
1376 WARN("trying to Unlock an unlocked surf@%p\n", This);
1377 return WINEDDERR_NOTLOCKED;
1380 if (This->Flags & SFLAG_PBO) {
1381 TRACE("Freeing PBO memory\n");
1382 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1383 ENTER_GL();
1384 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1385 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1386 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1387 checkGLcall("glUnmapBufferARB");
1388 LEAVE_GL();
1389 This->resource.allocatedMemory = NULL;
1392 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1394 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1395 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1396 goto unlock_end;
1399 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1401 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1402 static BOOL warned = FALSE;
1403 if(!warned) {
1404 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1405 warned = TRUE;
1407 goto unlock_end;
1410 if(This->dirtyRect.left == 0 &&
1411 This->dirtyRect.top == 0 &&
1412 This->dirtyRect.right == This->currentDesc.Width &&
1413 This->dirtyRect.bottom == This->currentDesc.Height) {
1414 fullsurface = TRUE;
1415 } else {
1416 /* TODO: Proper partial rectangle tracking */
1417 fullsurface = FALSE;
1418 This->Flags |= SFLAG_INSYSMEM;
1421 switch(wined3d_settings.rendertargetlock_mode) {
1422 case RTL_READTEX:
1423 case RTL_TEXTEX:
1424 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1425 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1426 /* drop through */
1428 case RTL_AUTO:
1429 case RTL_READDRAW:
1430 case RTL_TEXDRAW:
1431 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1432 break;
1435 if(!fullsurface) {
1436 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1437 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1438 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1439 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1440 * not fully up to date because only a subrectangle was read in LockRect.
1442 This->Flags &= ~SFLAG_INSYSMEM;
1443 This->Flags |= SFLAG_INDRAWABLE;
1446 This->dirtyRect.left = This->currentDesc.Width;
1447 This->dirtyRect.top = This->currentDesc.Height;
1448 This->dirtyRect.right = 0;
1449 This->dirtyRect.bottom = 0;
1450 } else if(iface == myDevice->stencilBufferTarget) {
1451 FIXME("Depth Stencil buffer locking is not implemented\n");
1452 } else {
1453 /* The rest should be a normal texture */
1454 IWineD3DBaseTextureImpl *impl;
1455 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1456 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1457 * states need resetting
1459 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1460 if(impl->baseTexture.bindCount) {
1461 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1463 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1467 unlock_end:
1468 This->Flags &= ~SFLAG_LOCKED;
1469 memset(&This->lockedRect, 0, sizeof(RECT));
1471 /* Overlays have to be redrawn manually after changes with the GL implementation */
1472 if(This->overlay_dest) {
1473 IWineD3DSurface_DrawOverlay(iface);
1475 return WINED3D_OK;
1478 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
1480 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1481 WINED3DLOCKED_RECT lock;
1482 HRESULT hr;
1483 RGBQUAD col[256];
1485 TRACE("(%p)->(%p)\n",This,pHDC);
1487 if(This->Flags & SFLAG_USERPTR) {
1488 ERR("Not supported on surfaces with an application-provided surfaces\n");
1489 return WINEDDERR_NODC;
1492 /* Give more detailed info for ddraw */
1493 if (This->Flags & SFLAG_DCINUSE)
1494 return WINEDDERR_DCALREADYCREATED;
1496 /* Can't GetDC if the surface is locked */
1497 if (This->Flags & SFLAG_LOCKED)
1498 return WINED3DERR_INVALIDCALL;
1500 /* According to Direct3D9 docs, only these formats are supported */
1501 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1502 if (This->resource.format_desc->format != WINED3DFMT_R5G6B5
1503 && This->resource.format_desc->format != WINED3DFMT_X1R5G5B5
1504 && This->resource.format_desc->format != WINED3DFMT_R8G8B8
1505 && This->resource.format_desc->format != WINED3DFMT_X8R8G8B8)
1506 return WINED3DERR_INVALIDCALL;
1509 memset(&lock, 0, sizeof(lock)); /* To be sure */
1511 /* Create a DIB section if there isn't a hdc yet */
1512 if(!This->hDC) {
1513 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1514 if(This->Flags & SFLAG_CLIENT) {
1515 surface_internal_preload(iface, SRGB_RGB);
1518 /* Use the dib section from now on if we are not using a PBO */
1519 if(!(This->Flags & SFLAG_PBO))
1520 This->resource.allocatedMemory = This->dib.bitmap_data;
1523 /* Lock the surface */
1524 hr = IWineD3DSurface_LockRect(iface,
1525 &lock,
1526 NULL,
1529 if(This->Flags & SFLAG_PBO) {
1530 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1531 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1534 if(FAILED(hr)) {
1535 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1536 /* keep the dib section */
1537 return hr;
1540 if (This->resource.format_desc->format == WINED3DFMT_P8
1541 || This->resource.format_desc->format == WINED3DFMT_A8P8)
1543 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1544 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1545 unsigned int n;
1546 const PALETTEENTRY *pal = NULL;
1548 if(This->palette) {
1549 pal = This->palette->palents;
1550 } else {
1551 IWineD3DSurfaceImpl *dds_primary;
1552 IWineD3DSwapChainImpl *swapchain;
1553 swapchain = (IWineD3DSwapChainImpl *)This->resource.wineD3DDevice->swapchains[0];
1554 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1555 if (dds_primary && dds_primary->palette)
1556 pal = dds_primary->palette->palents;
1559 if (pal) {
1560 for (n=0; n<256; n++) {
1561 col[n].rgbRed = pal[n].peRed;
1562 col[n].rgbGreen = pal[n].peGreen;
1563 col[n].rgbBlue = pal[n].peBlue;
1564 col[n].rgbReserved = 0;
1566 SetDIBColorTable(This->hDC, 0, 256, col);
1570 *pHDC = This->hDC;
1571 TRACE("returning %p\n",*pHDC);
1572 This->Flags |= SFLAG_DCINUSE;
1574 return WINED3D_OK;
1577 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
1579 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1581 TRACE("(%p)->(%p)\n",This,hDC);
1583 if (!(This->Flags & SFLAG_DCINUSE))
1584 return WINEDDERR_NODC;
1586 if (This->hDC !=hDC) {
1587 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1588 return WINEDDERR_NODC;
1591 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1592 /* Copy the contents of the DIB over to the PBO */
1593 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1596 /* we locked first, so unlock now */
1597 IWineD3DSurface_UnlockRect(iface);
1599 This->Flags &= ~SFLAG_DCINUSE;
1601 return WINED3D_OK;
1604 /* ******************************************************
1605 IWineD3DSurface Internal (No mapping to directx api) parts follow
1606 ****************************************************** */
1608 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) {
1609 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1610 const struct GlPixelFormatDesc *glDesc = This->resource.format_desc;
1611 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1613 /* Default values: From the surface */
1614 *format = glDesc->glFormat;
1615 *type = glDesc->glType;
1616 *convert = NO_CONVERSION;
1617 *target_bpp = glDesc->byte_count;
1619 if(srgb_mode) {
1620 *internal = glDesc->glGammaInternal;
1622 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
1623 && !(This->Flags & SFLAG_SWAPCHAIN))
1625 *internal = glDesc->rtInternal;
1626 } else {
1627 *internal = glDesc->glInternal;
1630 /* Ok, now look if we have to do any conversion */
1631 switch(This->resource.format_desc->format)
1633 case WINED3DFMT_P8:
1634 /* ****************
1635 Paletted Texture
1636 **************** */
1638 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1639 * of the two is available make sure texturing is requested as neither of the two works in
1640 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1641 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1642 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1643 * conflicts with this.
1645 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) ||
1646 (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) &&
1647 device->render_targets &&
1648 This == (IWineD3DSurfaceImpl*)device->render_targets[0])) ||
1649 colorkey_active || !use_texturing ) {
1650 *format = GL_RGBA;
1651 *internal = GL_RGBA;
1652 *type = GL_UNSIGNED_BYTE;
1653 *target_bpp = 4;
1654 if(colorkey_active) {
1655 *convert = CONVERT_PALETTED_CK;
1656 } else {
1657 *convert = CONVERT_PALETTED;
1660 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1661 *format = GL_ALPHA;
1662 *internal = GL_RGBA;
1663 *type = GL_UNSIGNED_BYTE;
1664 *target_bpp = 1;
1667 break;
1669 case WINED3DFMT_R3G3B2:
1670 /* **********************
1671 GL_UNSIGNED_BYTE_3_3_2
1672 ********************** */
1673 if (colorkey_active) {
1674 /* This texture format will never be used.. So do not care about color keying
1675 up until the point in time it will be needed :-) */
1676 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1678 break;
1680 case WINED3DFMT_R5G6B5:
1681 if (colorkey_active) {
1682 *convert = CONVERT_CK_565;
1683 *format = GL_RGBA;
1684 *internal = GL_RGBA;
1685 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1687 break;
1689 case WINED3DFMT_X1R5G5B5:
1690 if (colorkey_active) {
1691 *convert = CONVERT_CK_5551;
1692 *format = GL_BGRA;
1693 *internal = GL_RGBA;
1694 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1696 break;
1698 case WINED3DFMT_R8G8B8:
1699 if (colorkey_active) {
1700 *convert = CONVERT_CK_RGB24;
1701 *format = GL_RGBA;
1702 *internal = GL_RGBA;
1703 *type = GL_UNSIGNED_INT_8_8_8_8;
1704 *target_bpp = 4;
1706 break;
1708 case WINED3DFMT_X8R8G8B8:
1709 if (colorkey_active) {
1710 *convert = CONVERT_RGB32_888;
1711 *format = GL_RGBA;
1712 *internal = GL_RGBA;
1713 *type = GL_UNSIGNED_INT_8_8_8_8;
1715 break;
1717 case WINED3DFMT_R8G8_SNORM:
1718 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1719 *convert = CONVERT_V8U8;
1720 *format = GL_BGR;
1721 *internal = GL_RGB8;
1722 *type = GL_UNSIGNED_BYTE;
1723 *target_bpp = 3;
1724 break;
1726 case WINED3DFMT_L6V5U5:
1727 *convert = CONVERT_L6V5U5;
1728 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1729 *target_bpp = 3;
1730 /* Use format and types from table */
1731 } else {
1732 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1733 *target_bpp = 2;
1734 *format = GL_RGB;
1735 *internal = GL_RGB5;
1736 *type = GL_UNSIGNED_SHORT_5_6_5;
1738 break;
1740 case WINED3DFMT_X8L8V8U8:
1741 *convert = CONVERT_X8L8V8U8;
1742 *target_bpp = 4;
1743 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1744 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1745 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1746 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1747 * the needed type and format parameter, so the internal format contains a
1748 * 4th component, which is returned as alpha
1750 } else {
1751 *format = GL_BGRA;
1752 *internal = GL_RGB8;
1753 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1755 break;
1757 case WINED3DFMT_R8G8B8A8_SNORM:
1758 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1759 *convert = CONVERT_Q8W8V8U8;
1760 *format = GL_BGRA;
1761 *internal = GL_RGBA8;
1762 *type = GL_UNSIGNED_BYTE;
1763 *target_bpp = 4;
1764 break;
1766 case WINED3DFMT_R16G16_SNORM:
1767 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1768 *convert = CONVERT_V16U16;
1769 *format = GL_BGR;
1770 *internal = GL_RGB16_EXT;
1771 *type = GL_UNSIGNED_SHORT;
1772 *target_bpp = 6;
1773 break;
1775 case WINED3DFMT_A4L4:
1776 /* A4L4 exists as an internal gl format, but for some reason there is not
1777 * format+type combination to load it. Thus convert it to A8L8, then load it
1778 * with A4L4 internal, but A8L8 format+type
1780 *convert = CONVERT_A4L4;
1781 *format = GL_LUMINANCE_ALPHA;
1782 *internal = GL_LUMINANCE4_ALPHA4;
1783 *type = GL_UNSIGNED_BYTE;
1784 *target_bpp = 2;
1785 break;
1787 case WINED3DFMT_R16G16_UNORM:
1788 *convert = CONVERT_G16R16;
1789 *format = GL_RGB;
1790 *internal = GL_RGB16_EXT;
1791 *type = GL_UNSIGNED_SHORT;
1792 *target_bpp = 6;
1793 break;
1795 case WINED3DFMT_R16G16_FLOAT:
1796 *convert = CONVERT_R16G16F;
1797 *format = GL_RGB;
1798 *internal = GL_RGB16F_ARB;
1799 *type = GL_HALF_FLOAT_ARB;
1800 *target_bpp = 6;
1801 break;
1803 case WINED3DFMT_R32G32_FLOAT:
1804 *convert = CONVERT_R32G32F;
1805 *format = GL_RGB;
1806 *internal = GL_RGB32F_ARB;
1807 *type = GL_FLOAT;
1808 *target_bpp = 12;
1809 break;
1811 default:
1812 break;
1815 return WINED3D_OK;
1818 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey)
1820 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1821 IWineD3DPaletteImpl *pal = This->palette;
1822 BOOL index_in_alpha = FALSE;
1823 unsigned int i;
1825 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1826 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1827 * is slow. Further RGB->P8 conversion is not possible because palettes can have
1828 * duplicate entries. Store the color key in the unused alpha component to speed the
1829 * download up and to make conversion unneeded. */
1830 index_in_alpha = primary_render_target_is_p8(device);
1832 if (!pal)
1834 UINT dxVersion = ((IWineD3DImpl *)device->wineD3D)->dxVersion;
1836 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
1837 if (dxVersion <= 7)
1839 ERR("This code should never get entered for DirectDraw!, expect problems\n");
1840 if (index_in_alpha)
1842 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
1843 * there's no palette at this time. */
1844 for (i = 0; i < 256; i++) table[i][3] = i;
1847 else
1849 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
1850 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
1851 * capability flag is present (wine does advertise this capability) */
1852 for (i = 0; i < 256; ++i)
1854 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1855 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1856 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1857 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
1861 else
1863 TRACE("Using surface palette %p\n", pal);
1864 /* Get the surface's palette */
1865 for (i = 0; i < 256; ++i)
1867 table[i][0] = pal->palents[i].peRed;
1868 table[i][1] = pal->palents[i].peGreen;
1869 table[i][2] = pal->palents[i].peBlue;
1871 /* When index_in_alpha is set the palette index is stored in the
1872 * alpha component. In case of a readback we can then read
1873 * GL_ALPHA. Color keying is handled in BltOverride using a
1874 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
1875 * color key itself is passed to glAlphaFunc in other cases the
1876 * alpha component of pixels that should be masked away is set to 0. */
1877 if (index_in_alpha)
1879 table[i][3] = i;
1881 else if (colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue)
1882 && (i <= This->SrcBltCKey.dwColorSpaceHighValue))
1884 table[i][3] = 0x00;
1886 else if(pal->Flags & WINEDDPCAPS_ALPHA)
1888 table[i][3] = pal->palents[i].peFlags;
1890 else
1892 table[i][3] = 0xFF;
1898 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
1899 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
1901 const BYTE *source;
1902 BYTE *dest;
1903 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1905 switch (convert) {
1906 case NO_CONVERSION:
1908 memcpy(dst, src, pitch * height);
1909 break;
1911 case CONVERT_PALETTED:
1912 case CONVERT_PALETTED_CK:
1914 IWineD3DPaletteImpl* pal = This->palette;
1915 BYTE table[256][4];
1916 unsigned int x, y;
1918 if( pal == NULL) {
1919 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1922 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1924 for (y = 0; y < height; y++)
1926 source = src + pitch * y;
1927 dest = dst + outpitch * y;
1928 /* This is an 1 bpp format, using the width here is fine */
1929 for (x = 0; x < width; x++) {
1930 BYTE color = *source++;
1931 *dest++ = table[color][0];
1932 *dest++ = table[color][1];
1933 *dest++ = table[color][2];
1934 *dest++ = table[color][3];
1938 break;
1940 case CONVERT_CK_565:
1942 /* Converting the 565 format in 5551 packed to emulate color-keying.
1944 Note : in all these conversion, it would be best to average the averaging
1945 pixels to get the color of the pixel that will be color-keyed to
1946 prevent 'color bleeding'. This will be done later on if ever it is
1947 too visible.
1949 Note2: Nvidia documents say that their driver does not support alpha + color keying
1950 on the same surface and disables color keying in such a case
1952 unsigned int x, y;
1953 const WORD *Source;
1954 WORD *Dest;
1956 TRACE("Color keyed 565\n");
1958 for (y = 0; y < height; y++) {
1959 Source = (const WORD *)(src + y * pitch);
1960 Dest = (WORD *) (dst + y * outpitch);
1961 for (x = 0; x < width; x++ ) {
1962 WORD color = *Source++;
1963 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1964 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1965 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1966 *Dest |= 0x0001;
1968 Dest++;
1972 break;
1974 case CONVERT_CK_5551:
1976 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1977 unsigned int x, y;
1978 const WORD *Source;
1979 WORD *Dest;
1980 TRACE("Color keyed 5551\n");
1981 for (y = 0; y < height; y++) {
1982 Source = (const WORD *)(src + y * pitch);
1983 Dest = (WORD *) (dst + y * outpitch);
1984 for (x = 0; x < width; x++ ) {
1985 WORD color = *Source++;
1986 *Dest = color;
1987 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1988 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1989 *Dest |= (1 << 15);
1991 else {
1992 *Dest &= ~(1 << 15);
1994 Dest++;
1998 break;
2000 case CONVERT_CK_RGB24:
2002 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
2003 unsigned int x, y;
2004 for (y = 0; y < height; y++)
2006 source = src + pitch * y;
2007 dest = dst + outpitch * y;
2008 for (x = 0; x < width; x++) {
2009 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
2010 DWORD dstcolor = color << 8;
2011 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2012 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2013 dstcolor |= 0xff;
2015 *(DWORD*)dest = dstcolor;
2016 source += 3;
2017 dest += 4;
2021 break;
2023 case CONVERT_RGB32_888:
2025 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
2026 unsigned int x, y;
2027 for (y = 0; y < height; y++)
2029 source = src + pitch * y;
2030 dest = dst + outpitch * y;
2031 for (x = 0; x < width; x++) {
2032 DWORD color = 0xffffff & *(const DWORD*)source;
2033 DWORD dstcolor = color << 8;
2034 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2035 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2036 dstcolor |= 0xff;
2038 *(DWORD*)dest = dstcolor;
2039 source += 4;
2040 dest += 4;
2044 break;
2046 case CONVERT_V8U8:
2048 unsigned int x, y;
2049 const short *Source;
2050 unsigned char *Dest;
2051 for(y = 0; y < height; y++) {
2052 Source = (const short *)(src + y * pitch);
2053 Dest = dst + y * outpitch;
2054 for (x = 0; x < width; x++ ) {
2055 long color = (*Source++);
2056 /* B */ Dest[0] = 0xff;
2057 /* G */ Dest[1] = (color >> 8) + 128; /* V */
2058 /* R */ Dest[2] = (color) + 128; /* U */
2059 Dest += 3;
2062 break;
2065 case CONVERT_V16U16:
2067 unsigned int x, y;
2068 const DWORD *Source;
2069 unsigned short *Dest;
2070 for(y = 0; y < height; y++) {
2071 Source = (const DWORD *)(src + y * pitch);
2072 Dest = (unsigned short *) (dst + y * outpitch);
2073 for (x = 0; x < width; x++ ) {
2074 DWORD color = (*Source++);
2075 /* B */ Dest[0] = 0xffff;
2076 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
2077 /* R */ Dest[2] = (color ) + 32768; /* U */
2078 Dest += 3;
2081 break;
2084 case CONVERT_Q8W8V8U8:
2086 unsigned int x, y;
2087 const DWORD *Source;
2088 unsigned char *Dest;
2089 for(y = 0; y < height; y++) {
2090 Source = (const DWORD *)(src + y * pitch);
2091 Dest = dst + y * outpitch;
2092 for (x = 0; x < width; x++ ) {
2093 long color = (*Source++);
2094 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
2095 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2096 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2097 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
2098 Dest += 4;
2101 break;
2104 case CONVERT_L6V5U5:
2106 unsigned int x, y;
2107 const WORD *Source;
2108 unsigned char *Dest;
2110 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2111 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
2112 * fixed function and shaders without further conversion once the surface is
2113 * loaded
2115 for(y = 0; y < height; y++) {
2116 Source = (const WORD *)(src + y * pitch);
2117 Dest = dst + y * outpitch;
2118 for (x = 0; x < width; x++ ) {
2119 short color = (*Source++);
2120 unsigned char l = ((color >> 10) & 0xfc);
2121 char v = ((color >> 5) & 0x3e);
2122 char u = ((color ) & 0x1f);
2124 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
2125 * and doubles the positive range. Thus shift left only once, gl does the 2nd
2126 * shift. GL reads a signed value and converts it into an unsigned value.
2128 /* M */ Dest[2] = l << 1;
2130 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
2131 * from 5 bit values to 8 bit values.
2133 /* V */ Dest[1] = v << 3;
2134 /* U */ Dest[0] = u << 3;
2135 Dest += 3;
2138 } else {
2139 for(y = 0; y < height; y++) {
2140 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
2141 Source = (const WORD *)(src + y * pitch);
2142 for (x = 0; x < width; x++ ) {
2143 short color = (*Source++);
2144 unsigned char l = ((color >> 10) & 0xfc);
2145 short v = ((color >> 5) & 0x3e);
2146 short u = ((color ) & 0x1f);
2147 short v_conv = v + 16;
2148 short u_conv = u + 16;
2150 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
2151 Dest_s += 1;
2155 break;
2158 case CONVERT_X8L8V8U8:
2160 unsigned int x, y;
2161 const DWORD *Source;
2162 unsigned char *Dest;
2164 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2165 /* This implementation works with the fixed function pipeline and shaders
2166 * without further modification after converting the surface.
2168 for(y = 0; y < height; y++) {
2169 Source = (const DWORD *)(src + y * pitch);
2170 Dest = dst + y * outpitch;
2171 for (x = 0; x < width; x++ ) {
2172 long color = (*Source++);
2173 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2174 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2175 /* U */ Dest[0] = (color & 0xff); /* U */
2176 /* I */ Dest[3] = 255; /* X */
2177 Dest += 4;
2180 } else {
2181 /* Doesn't work correctly with the fixed function pipeline, but can work in
2182 * shaders if the shader is adjusted. (There's no use for this format in gl's
2183 * standard fixed function pipeline anyway).
2185 for(y = 0; y < height; y++) {
2186 Source = (const DWORD *)(src + y * pitch);
2187 Dest = dst + y * outpitch;
2188 for (x = 0; x < width; x++ ) {
2189 long color = (*Source++);
2190 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2191 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2192 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2193 Dest += 4;
2197 break;
2200 case CONVERT_A4L4:
2202 unsigned int x, y;
2203 const unsigned char *Source;
2204 unsigned char *Dest;
2205 for(y = 0; y < height; y++) {
2206 Source = src + y * pitch;
2207 Dest = dst + y * outpitch;
2208 for (x = 0; x < width; x++ ) {
2209 unsigned char color = (*Source++);
2210 /* A */ Dest[1] = (color & 0xf0) << 0;
2211 /* L */ Dest[0] = (color & 0x0f) << 4;
2212 Dest += 2;
2215 break;
2218 case CONVERT_G16R16:
2219 case CONVERT_R16G16F:
2221 unsigned int x, y;
2222 const WORD *Source;
2223 WORD *Dest;
2225 for(y = 0; y < height; y++) {
2226 Source = (const WORD *)(src + y * pitch);
2227 Dest = (WORD *) (dst + y * outpitch);
2228 for (x = 0; x < width; x++ ) {
2229 WORD green = (*Source++);
2230 WORD red = (*Source++);
2231 Dest[0] = green;
2232 Dest[1] = red;
2233 /* Strictly speaking not correct for R16G16F, but it doesn't matter because the
2234 * shader overwrites it anyway
2236 Dest[2] = 0xffff;
2237 Dest += 3;
2240 break;
2243 case CONVERT_R32G32F:
2245 unsigned int x, y;
2246 const float *Source;
2247 float *Dest;
2248 for(y = 0; y < height; y++) {
2249 Source = (const float *)(src + y * pitch);
2250 Dest = (float *) (dst + y * outpitch);
2251 for (x = 0; x < width; x++ ) {
2252 float green = (*Source++);
2253 float red = (*Source++);
2254 Dest[0] = green;
2255 Dest[1] = red;
2256 Dest[2] = 1.0;
2257 Dest += 3;
2260 break;
2263 default:
2264 ERR("Unsupported conversation type %d\n", convert);
2266 return WINED3D_OK;
2269 /* This function is used in case of 8bit paletted textures to upload the palette.
2270 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2271 extensions like ATI_fragment_shaders is possible.
2273 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2274 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2275 BYTE table[256][4];
2276 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2278 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2280 /* Try to use the paletted texture extension */
2281 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2283 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2284 ENTER_GL();
2285 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2286 LEAVE_GL();
2288 else
2290 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2291 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2292 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2294 ENTER_GL();
2296 /* Create the fragment program if we don't have it */
2297 if(!device->paletteConversionShader)
2299 const char *fragment_palette_conversion =
2300 "!!ARBfp1.0\n"
2301 "TEMP index;\n"
2302 /* { 255/256, 0.5/255*255/256, 0, 0 } */
2303 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n"
2304 /* The alpha-component contains the palette index */
2305 "TEX index, fragment.texcoord[0], texture[0], 2D;\n"
2306 /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2307 "MAD index.a, index.a, constants.x, constants.y;\n"
2308 /* Use the alpha-component as an index in the palette to get the final color */
2309 "TEX result.color, index.a, texture[1], 1D;\n"
2310 "END";
2312 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2313 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2314 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2315 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), fragment_palette_conversion));
2316 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2319 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2320 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2322 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2323 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2325 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2326 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2327 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2328 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2330 /* Switch back to unit 0 in which the 2D texture will be stored. */
2331 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2333 /* Rebind the texture because it isn't bound anymore */
2334 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2336 LEAVE_GL();
2340 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2341 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2343 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8
2344 && This->resource.format_desc->format != WINED3DFMT_A8P8))
2346 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2347 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2349 return FALSE;
2352 if(This->palette9) {
2353 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2354 return FALSE;
2356 } else {
2357 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2359 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2360 return TRUE;
2363 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2364 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2365 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2367 if (!(This->Flags & flag)) {
2368 TRACE("Reloading because surface is dirty\n");
2369 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2370 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2371 /* Reload: vice versa OR */
2372 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2373 /* Also reload: Color key is active AND the color key has changed */
2374 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2375 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2376 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2377 TRACE("Reloading because of color keying\n");
2378 /* To perform the color key conversion we need a sysmem copy of
2379 * the surface. Make sure we have it
2382 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2383 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2384 /* TODO: This is not necessarily needed with hw palettized texture support */
2385 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2386 } else {
2387 TRACE("surface is already in texture\n");
2388 return WINED3D_OK;
2391 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2392 * These resources are not bound by device size or format restrictions. Because of this,
2393 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2394 * However, these resources can always be created, locked, and copied.
2396 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2398 FIXME("(%p) Operation not supported for scratch textures\n",This);
2399 return WINED3DERR_INVALIDCALL;
2402 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2404 #if 0
2406 static unsigned int gen = 0;
2407 char buffer[4096];
2408 ++gen;
2409 if ((gen % 10) == 0) {
2410 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2411 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2414 * debugging crash code
2415 if (gen == 250) {
2416 void** test = NULL;
2417 *test = 0;
2421 #endif
2423 if (!(This->Flags & SFLAG_DONOTFREE)) {
2424 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2425 This->resource.allocatedMemory = NULL;
2426 This->resource.heapMemory = NULL;
2427 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2430 return WINED3D_OK;
2433 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2434 /* TODO: check for locks */
2435 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2436 IWineD3DBaseTexture *baseTexture = NULL;
2437 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2439 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2440 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2441 TRACE("Passing to container\n");
2442 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2443 IWineD3DBaseTexture_Release(baseTexture);
2444 } else {
2445 GLuint *name;
2446 TRACE("(%p) : Binding surface\n", This);
2448 name = srgb ? &This->glDescription.srgbTextureName : &This->glDescription.textureName;
2449 if(!device->isInDraw) {
2450 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2453 ENTER_GL();
2455 if (!This->glDescription.level) {
2456 if (!*name) {
2457 glGenTextures(1, name);
2458 checkGLcall("glGenTextures");
2459 TRACE("Surface %p given name %d\n", This, *name);
2461 glBindTexture(This->glDescription.target, *name);
2462 checkGLcall("glBindTexture");
2463 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2464 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2465 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2466 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2467 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2468 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2469 glTexParameteri(This->glDescription.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2470 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2471 glTexParameteri(This->glDescription.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2472 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2474 /* This is where we should be reducing the amount of GLMemoryUsed */
2475 } else if (*name) {
2476 /* Mipmap surfaces should have a base texture container */
2477 ERR("Mipmap surface has a glTexture bound to it!\n");
2480 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2481 checkGLcall("glBindTexture");
2483 LEAVE_GL();
2485 return;
2488 #include <errno.h>
2489 #include <stdio.h>
2490 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2492 FILE* f = NULL;
2493 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2494 char *allocatedMemory;
2495 const char *textureRow;
2496 IWineD3DSwapChain *swapChain = NULL;
2497 int width, height, i, y;
2498 GLuint tmpTexture = 0;
2499 DWORD color;
2500 /*FIXME:
2501 Textures may not be stored in ->allocatedgMemory and a GlTexture
2502 so we should lock the surface before saving a snapshot, or at least check that
2504 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2505 by calling GetTexImage and in compressed form by calling
2506 GetCompressedTexImageARB. Queried compressed images can be saved and
2507 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2508 texture images do not need to be processed by the GL and should
2509 significantly improve texture loading performance relative to uncompressed
2510 images. */
2512 /* Setup the width and height to be the internal texture width and height. */
2513 width = This->pow2Width;
2514 height = This->pow2Height;
2515 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2516 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2518 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2519 /* if were not a real texture then read the back buffer into a real texture */
2520 /* we don't want to interfere with the back buffer so read the data into a temporary
2521 * texture and then save the data out of the temporary texture
2523 GLint prevRead;
2524 ENTER_GL();
2525 TRACE("(%p) Reading render target into texture\n", This);
2527 glGenTextures(1, &tmpTexture);
2528 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2530 glTexImage2D(GL_TEXTURE_2D,
2532 GL_RGBA,
2533 width,
2534 height,
2535 0/*border*/,
2536 GL_RGBA,
2537 GL_UNSIGNED_INT_8_8_8_8_REV,
2538 NULL);
2540 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2541 checkGLcall("glGetIntegerv");
2542 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2543 checkGLcall("glReadBuffer");
2544 glCopyTexImage2D(GL_TEXTURE_2D,
2546 GL_RGBA,
2549 width,
2550 height,
2553 checkGLcall("glCopyTexImage2D");
2554 glReadBuffer(prevRead);
2555 LEAVE_GL();
2557 } else { /* bind the real texture, and make sure it up to date */
2558 surface_internal_preload(iface, SRGB_RGB);
2559 surface_bind_and_dirtify(This, FALSE);
2561 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2562 ENTER_GL();
2563 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2564 glGetTexImage(GL_TEXTURE_2D,
2565 This->glDescription.level,
2566 GL_RGBA,
2567 GL_UNSIGNED_INT_8_8_8_8_REV,
2568 allocatedMemory);
2569 checkGLcall("glTexImage2D");
2570 if (tmpTexture) {
2571 glBindTexture(GL_TEXTURE_2D, 0);
2572 glDeleteTextures(1, &tmpTexture);
2574 LEAVE_GL();
2576 f = fopen(filename, "w+");
2577 if (NULL == f) {
2578 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2579 return WINED3DERR_INVALIDCALL;
2581 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2582 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2583 /* TGA header */
2584 fputc(0,f);
2585 fputc(0,f);
2586 fputc(2,f);
2587 fputc(0,f);
2588 fputc(0,f);
2589 fputc(0,f);
2590 fputc(0,f);
2591 fputc(0,f);
2592 fputc(0,f);
2593 fputc(0,f);
2594 fputc(0,f);
2595 fputc(0,f);
2596 /* short width*/
2597 fwrite(&width,2,1,f);
2598 /* short height */
2599 fwrite(&height,2,1,f);
2600 /* format rgba */
2601 fputc(0x20,f);
2602 fputc(0x28,f);
2603 /* raw data */
2604 /* 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 */
2605 if(swapChain)
2606 textureRow = allocatedMemory + (width * (height - 1) *4);
2607 else
2608 textureRow = allocatedMemory;
2609 for (y = 0 ; y < height; y++) {
2610 for (i = 0; i < width; i++) {
2611 color = *((const DWORD*)textureRow);
2612 fputc((color >> 16) & 0xFF, f); /* B */
2613 fputc((color >> 8) & 0xFF, f); /* G */
2614 fputc((color >> 0) & 0xFF, f); /* R */
2615 fputc((color >> 24) & 0xFF, f); /* A */
2616 textureRow += 4;
2618 /* take two rows of the pointer to the texture memory */
2619 if(swapChain)
2620 (textureRow-= width << 3);
2623 TRACE("Closing file\n");
2624 fclose(f);
2626 if(swapChain) {
2627 IWineD3DSwapChain_Release(swapChain);
2629 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2630 return WINED3D_OK;
2633 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2634 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2635 HRESULT hr;
2637 TRACE("(%p) : Calling base function first\n", This);
2638 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2639 if(SUCCEEDED(hr)) {
2640 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2641 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2642 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2644 return hr;
2647 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2648 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2650 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2651 WARN("Surface is locked or the HDC is in use\n");
2652 return WINED3DERR_INVALIDCALL;
2655 if(Mem && Mem != This->resource.allocatedMemory) {
2656 void *release = NULL;
2658 /* Do I have to copy the old surface content? */
2659 if(This->Flags & SFLAG_DIBSECTION) {
2660 /* Release the DC. No need to hold the critical section for the update
2661 * Thread because this thread runs only on front buffers, but this method
2662 * fails for render targets in the check above.
2664 SelectObject(This->hDC, This->dib.holdbitmap);
2665 DeleteDC(This->hDC);
2666 /* Release the DIB section */
2667 DeleteObject(This->dib.DIBsection);
2668 This->dib.bitmap_data = NULL;
2669 This->resource.allocatedMemory = NULL;
2670 This->hDC = NULL;
2671 This->Flags &= ~SFLAG_DIBSECTION;
2672 } else if(!(This->Flags & SFLAG_USERPTR)) {
2673 release = This->resource.heapMemory;
2674 This->resource.heapMemory = NULL;
2676 This->resource.allocatedMemory = Mem;
2677 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2679 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2680 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2682 /* For client textures opengl has to be notified */
2683 if(This->Flags & SFLAG_CLIENT) {
2684 DWORD oldFlags = This->Flags;
2685 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2686 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2687 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2688 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2691 /* Now free the old memory if any */
2692 HeapFree(GetProcessHeap(), 0, release);
2693 } else if(This->Flags & SFLAG_USERPTR) {
2694 /* LockRect and GetDC will re-create the dib section and allocated memory */
2695 This->resource.allocatedMemory = NULL;
2696 /* HeapMemory should be NULL already */
2697 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2698 This->Flags &= ~SFLAG_USERPTR;
2700 if(This->Flags & SFLAG_CLIENT) {
2701 DWORD oldFlags = This->Flags;
2702 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2703 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2704 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2705 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2708 return WINED3D_OK;
2711 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2713 /* Flip the surface contents */
2714 /* Flip the DC */
2716 HDC tmp;
2717 tmp = front->hDC;
2718 front->hDC = back->hDC;
2719 back->hDC = tmp;
2722 /* Flip the DIBsection */
2724 HBITMAP tmp;
2725 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2726 tmp = front->dib.DIBsection;
2727 front->dib.DIBsection = back->dib.DIBsection;
2728 back->dib.DIBsection = tmp;
2730 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2731 else front->Flags &= ~SFLAG_DIBSECTION;
2732 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2733 else back->Flags &= ~SFLAG_DIBSECTION;
2736 /* Flip the surface data */
2738 void* tmp;
2740 tmp = front->dib.bitmap_data;
2741 front->dib.bitmap_data = back->dib.bitmap_data;
2742 back->dib.bitmap_data = tmp;
2744 tmp = front->resource.allocatedMemory;
2745 front->resource.allocatedMemory = back->resource.allocatedMemory;
2746 back->resource.allocatedMemory = tmp;
2748 tmp = front->resource.heapMemory;
2749 front->resource.heapMemory = back->resource.heapMemory;
2750 back->resource.heapMemory = tmp;
2753 /* Flip the PBO */
2755 GLuint tmp_pbo = front->pbo;
2756 front->pbo = back->pbo;
2757 back->pbo = tmp_pbo;
2760 /* client_memory should not be different, but just in case */
2762 BOOL tmp;
2763 tmp = front->dib.client_memory;
2764 front->dib.client_memory = back->dib.client_memory;
2765 back->dib.client_memory = tmp;
2768 /* Flip the opengl texture */
2770 glDescriptor tmp_desc = back->glDescription;
2771 back->glDescription = front->glDescription;
2772 front->glDescription = tmp_desc;
2776 DWORD tmp_flags = back->Flags;
2777 back->Flags = front->Flags;
2778 front->Flags = tmp_flags;
2782 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2783 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2784 IWineD3DSwapChainImpl *swapchain = NULL;
2785 HRESULT hr;
2786 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2788 /* Flipping is only supported on RenderTargets and overlays*/
2789 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2790 WARN("Tried to flip a non-render target, non-overlay surface\n");
2791 return WINEDDERR_NOTFLIPPABLE;
2794 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2795 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2797 /* Update the overlay if it is visible */
2798 if(This->overlay_dest) {
2799 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
2800 } else {
2801 return WINED3D_OK;
2805 if(override) {
2806 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2807 * FIXME("(%p) Target override is not supported by now\n", This);
2808 * Additionally, it isn't really possible to support triple-buffering
2809 * properly on opengl at all
2813 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2814 if(!swapchain) {
2815 ERR("Flipped surface is not on a swapchain\n");
2816 return WINEDDERR_NOTFLIPPABLE;
2819 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2820 * and only d3d8 and d3d9 apps specify the presentation interval
2822 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2823 /* Most common case first to avoid wasting time on all the other cases */
2824 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2825 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2826 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2827 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2828 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2829 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2830 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2831 } else {
2832 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2835 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2836 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2837 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2838 return hr;
2841 /* Does a direct frame buffer -> texture copy. Stretching is done
2842 * with single pixel copy calls
2844 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
2845 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
2846 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
2848 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2849 float xrel, yrel;
2850 UINT row;
2851 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2854 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2855 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
2856 ENTER_GL();
2858 /* Bind the target texture */
2859 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2860 checkGLcall("glBindTexture");
2861 if(!swapchain) {
2862 TRACE("Reading from an offscreen target\n");
2863 upsidedown = !upsidedown;
2864 glReadBuffer(myDevice->offscreenBuffer);
2865 } else {
2866 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2867 glReadBuffer(buffer);
2869 checkGLcall("glReadBuffer");
2871 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2872 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2874 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2875 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2877 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2878 ERR("Texture filtering not supported in direct blit\n");
2880 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2881 ERR("Texture filtering not supported in direct blit\n");
2884 if(upsidedown &&
2885 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2886 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2887 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2889 glCopyTexSubImage2D(This->glDescription.target,
2890 This->glDescription.level,
2891 drect->x1, drect->y1, /* xoffset, yoffset */
2892 srect->x1, Src->currentDesc.Height - srect->y2,
2893 drect->x2 - drect->x1, drect->y2 - drect->y1);
2894 } else {
2895 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2896 /* I have to process this row by row to swap the image,
2897 * otherwise it would be upside down, so stretching in y direction
2898 * doesn't cost extra time
2900 * However, stretching in x direction can be avoided if not necessary
2902 for(row = drect->y1; row < drect->y2; row++) {
2903 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2904 /* Well, that stuff works, but it's very slow.
2905 * find a better way instead
2907 UINT col;
2909 for(col = drect->x1; col < drect->x2; col++) {
2910 glCopyTexSubImage2D(This->glDescription.target,
2911 This->glDescription.level,
2912 drect->x1 + col, row, /* xoffset, yoffset */
2913 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2914 1, 1);
2916 } else {
2917 glCopyTexSubImage2D(This->glDescription.target,
2918 This->glDescription.level,
2919 drect->x1, row, /* xoffset, yoffset */
2920 srect->x1, yoffset - (int) (row * yrel),
2921 drect->x2-drect->x1, 1);
2925 checkGLcall("glCopyTexSubImage2D");
2927 LEAVE_GL();
2930 /* Uses the hardware to stretch and flip the image */
2931 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
2932 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
2933 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
2935 GLuint src, backup = 0;
2936 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2937 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2938 float left, right, top, bottom; /* Texture coordinates */
2939 UINT fbwidth = Src->currentDesc.Width;
2940 UINT fbheight = Src->currentDesc.Height;
2941 GLenum drawBuffer = GL_BACK;
2942 GLenum texture_target;
2943 BOOL noBackBufferBackup;
2945 TRACE("Using hwstretch blit\n");
2946 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2947 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2948 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
2950 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2951 if(!noBackBufferBackup && Src->glDescription.textureName == 0) {
2952 /* Get it a description */
2953 surface_internal_preload(SrcSurface, SRGB_RGB);
2955 ENTER_GL();
2957 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2958 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2960 if(myDevice->activeContext->aux_buffers >= 2) {
2961 /* Got more than one aux buffer? Use the 2nd aux buffer */
2962 drawBuffer = GL_AUX1;
2963 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && myDevice->activeContext->aux_buffers >= 1) {
2964 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2965 drawBuffer = GL_AUX0;
2968 if(noBackBufferBackup) {
2969 glGenTextures(1, &backup);
2970 checkGLcall("glGenTextures\n");
2971 glBindTexture(GL_TEXTURE_2D, backup);
2972 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2973 texture_target = GL_TEXTURE_2D;
2974 } else {
2975 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2976 * we are reading from the back buffer, the backup can be used as source texture
2978 texture_target = Src->glDescription.target;
2979 glBindTexture(texture_target, Src->glDescription.textureName);
2980 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2981 glEnable(texture_target);
2982 checkGLcall("glEnable(texture_target)");
2984 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2985 Src->Flags &= ~SFLAG_INTEXTURE;
2988 if(swapchain) {
2989 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
2990 } else {
2991 TRACE("Reading from an offscreen target\n");
2992 upsidedown = !upsidedown;
2993 glReadBuffer(myDevice->offscreenBuffer);
2996 /* TODO: Only back up the part that will be overwritten */
2997 glCopyTexSubImage2D(texture_target, 0,
2998 0, 0 /* read offsets */,
2999 0, 0,
3000 fbwidth,
3001 fbheight);
3003 checkGLcall("glCopyTexSubImage2D");
3005 /* No issue with overriding these - the sampler is dirty due to blit usage */
3006 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3007 magLookup[Filter - WINED3DTEXF_NONE]);
3008 checkGLcall("glTexParameteri");
3009 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3010 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
3011 checkGLcall("glTexParameteri");
3013 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
3014 src = backup ? backup : Src->glDescription.textureName;
3015 } else {
3016 glReadBuffer(GL_FRONT);
3017 checkGLcall("glReadBuffer(GL_FRONT)");
3019 glGenTextures(1, &src);
3020 checkGLcall("glGenTextures(1, &src)");
3021 glBindTexture(GL_TEXTURE_2D, src);
3022 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3024 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
3025 * out for power of 2 sizes
3027 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3028 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3029 checkGLcall("glTexImage2D");
3030 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3031 0, 0 /* read offsets */,
3032 0, 0,
3033 fbwidth,
3034 fbheight);
3036 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3037 checkGLcall("glTexParameteri");
3038 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3039 checkGLcall("glTexParameteri");
3041 glReadBuffer(GL_BACK);
3042 checkGLcall("glReadBuffer(GL_BACK)");
3044 if(texture_target != GL_TEXTURE_2D) {
3045 glDisable(texture_target);
3046 glEnable(GL_TEXTURE_2D);
3047 texture_target = GL_TEXTURE_2D;
3050 checkGLcall("glEnd and previous");
3052 left = srect->x1;
3053 right = srect->x2;
3055 if(upsidedown) {
3056 top = Src->currentDesc.Height - srect->y1;
3057 bottom = Src->currentDesc.Height - srect->y2;
3058 } else {
3059 top = Src->currentDesc.Height - srect->y2;
3060 bottom = Src->currentDesc.Height - srect->y1;
3063 if(Src->Flags & SFLAG_NORMCOORD) {
3064 left /= Src->pow2Width;
3065 right /= Src->pow2Width;
3066 top /= Src->pow2Height;
3067 bottom /= Src->pow2Height;
3070 /* draw the source texture stretched and upside down. The correct surface is bound already */
3071 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3072 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3074 glDrawBuffer(drawBuffer);
3075 glReadBuffer(drawBuffer);
3077 glBegin(GL_QUADS);
3078 /* bottom left */
3079 glTexCoord2f(left, bottom);
3080 glVertex2i(0, fbheight);
3082 /* top left */
3083 glTexCoord2f(left, top);
3084 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3086 /* top right */
3087 glTexCoord2f(right, top);
3088 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3090 /* bottom right */
3091 glTexCoord2f(right, bottom);
3092 glVertex2i(drect->x2 - drect->x1, fbheight);
3093 glEnd();
3094 checkGLcall("glEnd and previous");
3096 if(texture_target != This->glDescription.target) {
3097 glDisable(texture_target);
3098 glEnable(This->glDescription.target);
3099 texture_target = This->glDescription.target;
3102 /* Now read the stretched and upside down image into the destination texture */
3103 glBindTexture(texture_target, This->glDescription.textureName);
3104 checkGLcall("glBindTexture");
3105 glCopyTexSubImage2D(texture_target,
3107 drect->x1, drect->y1, /* xoffset, yoffset */
3108 0, 0, /* We blitted the image to the origin */
3109 drect->x2 - drect->x1, drect->y2 - drect->y1);
3110 checkGLcall("glCopyTexSubImage2D");
3112 if(drawBuffer == GL_BACK) {
3113 /* Write the back buffer backup back */
3114 if(backup) {
3115 if(texture_target != GL_TEXTURE_2D) {
3116 glDisable(texture_target);
3117 glEnable(GL_TEXTURE_2D);
3118 texture_target = GL_TEXTURE_2D;
3120 glBindTexture(GL_TEXTURE_2D, backup);
3121 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3122 } else {
3123 if(texture_target != Src->glDescription.target) {
3124 glDisable(texture_target);
3125 glEnable(Src->glDescription.target);
3126 texture_target = Src->glDescription.target;
3128 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3129 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
3132 glBegin(GL_QUADS);
3133 /* top left */
3134 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
3135 glVertex2i(0, 0);
3137 /* bottom left */
3138 glTexCoord2f(0.0, 0.0);
3139 glVertex2i(0, fbheight);
3141 /* bottom right */
3142 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
3143 glVertex2i(fbwidth, Src->currentDesc.Height);
3145 /* top right */
3146 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3147 glVertex2i(fbwidth, 0);
3148 glEnd();
3149 } else {
3150 /* Restore the old draw buffer */
3151 glDrawBuffer(GL_BACK);
3153 glDisable(texture_target);
3154 checkGLcall("glDisable(texture_target)");
3156 /* Cleanup */
3157 if(src != Src->glDescription.textureName && src != backup) {
3158 glDeleteTextures(1, &src);
3159 checkGLcall("glDeleteTextures(1, &src)");
3161 if(backup) {
3162 glDeleteTextures(1, &backup);
3163 checkGLcall("glDeleteTextures(1, &backup)");
3166 LEAVE_GL();
3169 /* Not called from the VTable */
3170 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3171 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3172 WINED3DTEXTUREFILTERTYPE Filter)
3174 WINED3DRECT rect;
3175 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3176 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3177 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3179 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3181 /* Get the swapchain. One of the surfaces has to be a primary surface */
3182 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3183 WARN("Destination is in sysmem, rejecting gl blt\n");
3184 return WINED3DERR_INVALIDCALL;
3186 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3187 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3188 if(Src) {
3189 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3190 WARN("Src is in sysmem, rejecting gl blt\n");
3191 return WINED3DERR_INVALIDCALL;
3193 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3194 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3197 /* Early sort out of cases where no render target is used */
3198 if(!dstSwapchain && !srcSwapchain &&
3199 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3200 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3201 return WINED3DERR_INVALIDCALL;
3204 /* No destination color keying supported */
3205 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3206 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3207 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3208 return WINED3DERR_INVALIDCALL;
3211 if (DestRect) {
3212 rect.x1 = DestRect->left;
3213 rect.y1 = DestRect->top;
3214 rect.x2 = DestRect->right;
3215 rect.y2 = DestRect->bottom;
3216 } else {
3217 rect.x1 = 0;
3218 rect.y1 = 0;
3219 rect.x2 = This->currentDesc.Width;
3220 rect.y2 = This->currentDesc.Height;
3223 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3224 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3225 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3226 /* Half-life does a Blt from the back buffer to the front buffer,
3227 * Full surface size, no flags... Use present instead
3229 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3232 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3233 while(1)
3235 RECT mySrcRect;
3236 TRACE("Looking if a Present can be done...\n");
3237 /* Source Rectangle must be full surface */
3238 if( SrcRect ) {
3239 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3240 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3241 TRACE("No, Source rectangle doesn't match\n");
3242 break;
3245 mySrcRect.left = 0;
3246 mySrcRect.top = 0;
3247 mySrcRect.right = Src->currentDesc.Width;
3248 mySrcRect.bottom = Src->currentDesc.Height;
3250 /* No stretching may occur */
3251 if(mySrcRect.right != rect.x2 - rect.x1 ||
3252 mySrcRect.bottom != rect.y2 - rect.y1) {
3253 TRACE("No, stretching is done\n");
3254 break;
3257 /* Destination must be full surface or match the clipping rectangle */
3258 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3260 RECT cliprect;
3261 POINT pos[2];
3262 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3263 pos[0].x = rect.x1;
3264 pos[0].y = rect.y1;
3265 pos[1].x = rect.x2;
3266 pos[1].y = rect.y2;
3267 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3268 pos, 2);
3270 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3271 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3273 TRACE("No, dest rectangle doesn't match(clipper)\n");
3274 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3275 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3276 break;
3279 else
3281 if(rect.x1 != 0 || rect.y1 != 0 ||
3282 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3283 TRACE("No, dest rectangle doesn't match(surface size)\n");
3284 break;
3288 TRACE("Yes\n");
3290 /* These flags are unimportant for the flag check, remove them */
3291 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3292 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3294 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3295 * take very long, while a flip is fast.
3296 * This applies to Half-Life, which does such Blts every time it finished
3297 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3298 * menu. This is also used by all apps when they do windowed rendering
3300 * The problem is that flipping is not really the same as copying. After a
3301 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3302 * untouched. Therefore it's necessary to override the swap effect
3303 * and to set it back after the flip.
3305 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3306 * testcases.
3309 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3310 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3312 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3313 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3315 dstSwapchain->presentParms.SwapEffect = orig_swap;
3317 return WINED3D_OK;
3319 break;
3322 TRACE("Unsupported blit between buffers on the same swapchain\n");
3323 return WINED3DERR_INVALIDCALL;
3324 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3325 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3326 return WINED3DERR_INVALIDCALL;
3327 } else if(dstSwapchain && srcSwapchain) {
3328 FIXME("Implement hardware blit between two different swapchains\n");
3329 return WINED3DERR_INVALIDCALL;
3330 } else if(dstSwapchain) {
3331 if(SrcSurface == myDevice->render_targets[0]) {
3332 TRACE("Blit from active render target to a swapchain\n");
3333 /* Handled with regular texture -> swapchain blit */
3335 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3336 FIXME("Implement blit from a swapchain to the active render target\n");
3337 return WINED3DERR_INVALIDCALL;
3340 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3341 /* Blit from render target to texture */
3342 WINED3DRECT srect;
3343 BOOL upsideDown, stretchx;
3344 BOOL paletteOverride = FALSE;
3346 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3347 TRACE("Color keying not supported by frame buffer to texture blit\n");
3348 return WINED3DERR_INVALIDCALL;
3349 /* Destination color key is checked above */
3352 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3353 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3355 if(SrcRect) {
3356 if(SrcRect->top < SrcRect->bottom) {
3357 srect.y1 = SrcRect->top;
3358 srect.y2 = SrcRect->bottom;
3359 upsideDown = FALSE;
3360 } else {
3361 srect.y1 = SrcRect->bottom;
3362 srect.y2 = SrcRect->top;
3363 upsideDown = TRUE;
3365 srect.x1 = SrcRect->left;
3366 srect.x2 = SrcRect->right;
3367 } else {
3368 srect.x1 = 0;
3369 srect.y1 = 0;
3370 srect.x2 = Src->currentDesc.Width;
3371 srect.y2 = Src->currentDesc.Height;
3372 upsideDown = FALSE;
3374 if(rect.x1 > rect.x2) {
3375 UINT tmp = rect.x2;
3376 rect.x2 = rect.x1;
3377 rect.x1 = tmp;
3378 upsideDown = !upsideDown;
3381 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3382 stretchx = TRUE;
3383 } else {
3384 stretchx = FALSE;
3387 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3388 * In this case grab the palette from the render target. */
3389 if ((This->resource.format_desc->format == WINED3DFMT_P8) && (This->palette == NULL))
3391 paletteOverride = TRUE;
3392 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3393 This->palette = Src->palette;
3396 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3397 * flip the image nor scale it.
3399 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3400 * -> If the app wants a image width an unscaled width, copy it line per line
3401 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3402 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3403 * back buffer. This is slower than reading line per line, thus not used for flipping
3404 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3405 * pixel by pixel
3407 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3408 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3409 * backends.
3411 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3412 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3413 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3414 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3415 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3416 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3417 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3418 } else {
3419 TRACE("Using hardware stretching to flip / stretch the texture\n");
3420 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3423 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3424 if(paletteOverride)
3425 This->palette = NULL;
3427 if(!(This->Flags & SFLAG_DONOTFREE)) {
3428 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3429 This->resource.allocatedMemory = NULL;
3430 This->resource.heapMemory = NULL;
3431 } else {
3432 This->Flags &= ~SFLAG_INSYSMEM;
3434 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3435 * path is never entered
3437 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3439 return WINED3D_OK;
3440 } else if(Src) {
3441 /* Blit from offscreen surface to render target */
3442 float glTexCoord[4];
3443 DWORD oldCKeyFlags = Src->CKeyFlags;
3444 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3445 RECT SourceRectangle;
3446 BOOL paletteOverride = FALSE;
3448 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3450 if(SrcRect) {
3451 SourceRectangle.left = SrcRect->left;
3452 SourceRectangle.right = SrcRect->right;
3453 SourceRectangle.top = SrcRect->top;
3454 SourceRectangle.bottom = SrcRect->bottom;
3455 } else {
3456 SourceRectangle.left = 0;
3457 SourceRectangle.right = Src->currentDesc.Width;
3458 SourceRectangle.top = 0;
3459 SourceRectangle.bottom = Src->currentDesc.Height;
3462 /* When blitting from an offscreen surface to a rendertarget, the source
3463 * surface is not required to have a palette. Our rendering / conversion
3464 * code further down the road retrieves the palette from the surface, so
3465 * it must have a palette set. */
3466 if ((Src->resource.format_desc->format == WINED3DFMT_P8) && (Src->palette == NULL))
3468 paletteOverride = TRUE;
3469 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3470 Src->palette = This->palette;
3473 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3474 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3475 TRACE("Using stretch_rect_fbo\n");
3476 /* The source is always a texture, but never the currently active render target, and the texture
3477 * contents are never upside down
3479 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3480 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3482 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3483 if(paletteOverride)
3484 Src->palette = NULL;
3485 return WINED3D_OK;
3488 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3489 /* Fall back to software */
3490 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3491 SourceRectangle.left, SourceRectangle.top,
3492 SourceRectangle.right, SourceRectangle.bottom);
3493 return WINED3DERR_INVALIDCALL;
3496 /* Color keying: Check if we have to do a color keyed blt,
3497 * and if not check if a color key is activated.
3499 * Just modify the color keying parameters in the surface and restore them afterwards
3500 * The surface keeps track of the color key last used to load the opengl surface.
3501 * PreLoad will catch the change to the flags and color key and reload if necessary.
3503 if(Flags & WINEDDBLT_KEYSRC) {
3504 /* Use color key from surface */
3505 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3506 /* Use color key from DDBltFx */
3507 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3508 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3509 } else {
3510 /* Do not use color key */
3511 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3514 /* Now load the surface */
3515 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3517 /* Activate the destination context, set it up for blitting */
3518 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3520 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3521 * while OpenGL coordinates are window relative.
3522 * Also beware of the origin difference(top left vs bottom left).
3523 * Also beware that the front buffer's surface size is screen width x screen height,
3524 * whereas the real gl drawable size is the size of the window.
3526 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3527 RECT windowsize;
3528 POINT offset = {0,0};
3529 UINT h;
3530 ClientToScreen(dstSwapchain->win_handle, &offset);
3531 GetClientRect(dstSwapchain->win_handle, &windowsize);
3532 h = windowsize.bottom - windowsize.top;
3533 rect.x1 -= offset.x; rect.x2 -=offset.x;
3534 rect.y1 -= offset.y; rect.y2 -=offset.y;
3535 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3538 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format_desc,
3539 Src->glDescription.target, Src->pow2Width, Src->pow2Height);
3541 ENTER_GL();
3543 /* Bind the texture */
3544 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3545 checkGLcall("glBindTexture");
3547 /* Filtering for StretchRect */
3548 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3549 magLookup[Filter - WINED3DTEXF_NONE]);
3550 checkGLcall("glTexParameteri");
3551 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3552 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
3553 checkGLcall("glTexParameteri");
3554 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3555 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3556 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3557 checkGLcall("glTexEnvi");
3559 /* This is for color keying */
3560 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3561 glEnable(GL_ALPHA_TEST);
3562 checkGLcall("glEnable GL_ALPHA_TEST");
3564 /* When the primary render target uses P8, the alpha component contains the palette index.
3565 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3566 * should be masked away have alpha set to 0. */
3567 if(primary_render_target_is_p8(myDevice))
3568 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3569 else
3570 glAlphaFunc(GL_NOTEQUAL, 0.0);
3571 checkGLcall("glAlphaFunc\n");
3572 } else {
3573 glDisable(GL_ALPHA_TEST);
3574 checkGLcall("glDisable GL_ALPHA_TEST");
3577 /* Draw a textured quad
3579 glBegin(GL_QUADS);
3581 glColor3d(1.0f, 1.0f, 1.0f);
3582 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3583 glVertex3f(rect.x1,
3584 rect.y1,
3585 0.0);
3587 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3588 glVertex3f(rect.x1, rect.y2, 0.0);
3590 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3591 glVertex3f(rect.x2,
3592 rect.y2,
3593 0.0);
3595 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3596 glVertex3f(rect.x2,
3597 rect.y1,
3598 0.0);
3599 glEnd();
3600 checkGLcall("glEnd");
3602 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3603 glDisable(GL_ALPHA_TEST);
3604 checkGLcall("glDisable(GL_ALPHA_TEST)");
3607 glBindTexture(Src->glDescription.target, 0);
3608 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3610 /* Restore the color key parameters */
3611 Src->CKeyFlags = oldCKeyFlags;
3612 Src->SrcBltCKey = oldBltCKey;
3614 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3615 if(paletteOverride)
3616 Src->palette = NULL;
3618 LEAVE_GL();
3620 /* Leave the opengl state valid for blitting */
3621 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3623 /* Flush in case the drawable is used by multiple GL contexts */
3624 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3625 glFlush();
3627 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3628 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3629 * is outdated now
3631 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3633 return WINED3D_OK;
3634 } else {
3635 /* Source-Less Blit to render target */
3636 if (Flags & WINEDDBLT_COLORFILL) {
3637 /* This is easy to handle for the D3D Device... */
3638 DWORD color;
3640 TRACE("Colorfill\n");
3642 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3643 must be true if we are here */
3644 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3645 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3646 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3647 TRACE("Surface is higher back buffer, falling back to software\n");
3648 return WINED3DERR_INVALIDCALL;
3651 /* The color as given in the Blt function is in the format of the frame-buffer...
3652 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3654 if (This->resource.format_desc->format == WINED3DFMT_P8)
3656 DWORD alpha;
3658 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3659 else alpha = 0xFF000000;
3661 if (This->palette) {
3662 color = (alpha |
3663 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3664 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3665 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3666 } else {
3667 color = alpha;
3670 else if (This->resource.format_desc->format == WINED3DFMT_R5G6B5)
3672 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3673 color = 0xFFFFFFFF;
3674 } else {
3675 color = ((0xFF000000) |
3676 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3677 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3678 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3681 else if ((This->resource.format_desc->format == WINED3DFMT_R8G8B8)
3682 || (This->resource.format_desc->format == WINED3DFMT_X8R8G8B8))
3684 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3686 else if (This->resource.format_desc->format == WINED3DFMT_A8R8G8B8)
3688 color = DDBltFx->u5.dwFillColor;
3690 else {
3691 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3692 return WINED3DERR_INVALIDCALL;
3695 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3696 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3697 1, /* Number of rectangles */
3698 &rect, WINED3DCLEAR_TARGET, color,
3699 0.0 /* Z */,
3700 0 /* Stencil */);
3701 return WINED3D_OK;
3705 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3706 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3707 return WINED3DERR_INVALIDCALL;
3710 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3711 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3713 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3714 float depth;
3716 if (Flags & WINEDDBLT_DEPTHFILL) {
3717 switch(This->resource.format_desc->format)
3719 case WINED3DFMT_D16_UNORM:
3720 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3721 break;
3722 case WINED3DFMT_D15S1:
3723 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3724 break;
3725 case WINED3DFMT_D24S8:
3726 case WINED3DFMT_D24X8:
3727 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3728 break;
3729 case WINED3DFMT_D32:
3730 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3731 break;
3732 default:
3733 depth = 0.0;
3734 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
3737 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3738 DestRect == NULL ? 0 : 1,
3739 (const WINED3DRECT *)DestRect,
3740 WINED3DCLEAR_ZBUFFER,
3741 0x00000000,
3742 depth,
3743 0x00000000);
3746 FIXME("(%p): Unsupp depthstencil blit\n", This);
3747 return WINED3DERR_INVALIDCALL;
3750 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3751 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3752 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3753 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3754 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3755 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3756 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3758 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3760 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3761 return WINEDDERR_SURFACEBUSY;
3764 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3765 * except depth blits, which seem to work
3767 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3768 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3769 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3770 return WINED3DERR_INVALIDCALL;
3771 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3772 TRACE("Z Blit override handled the blit\n");
3773 return WINED3D_OK;
3777 /* Special cases for RenderTargets */
3778 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3779 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3780 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3783 /* For the rest call the X11 surface implementation.
3784 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3785 * other Blts are rather rare
3787 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3790 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
3791 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
3793 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3794 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3795 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3796 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3798 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3800 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3801 return WINEDDERR_SURFACEBUSY;
3804 if(myDevice->inScene &&
3805 (iface == myDevice->stencilBufferTarget ||
3806 (Source && Source == myDevice->stencilBufferTarget))) {
3807 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3808 return WINED3DERR_INVALIDCALL;
3811 /* Special cases for RenderTargets */
3812 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3813 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3815 RECT SrcRect, DstRect;
3816 DWORD Flags=0;
3818 if(rsrc) {
3819 SrcRect.left = rsrc->left;
3820 SrcRect.top= rsrc->top;
3821 SrcRect.bottom = rsrc->bottom;
3822 SrcRect.right = rsrc->right;
3823 } else {
3824 SrcRect.left = 0;
3825 SrcRect.top = 0;
3826 SrcRect.right = srcImpl->currentDesc.Width;
3827 SrcRect.bottom = srcImpl->currentDesc.Height;
3830 DstRect.left = dstx;
3831 DstRect.top=dsty;
3832 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3833 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3835 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3836 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3837 Flags |= WINEDDBLT_KEYSRC;
3838 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3839 Flags |= WINEDDBLT_KEYDEST;
3840 if(trans & WINEDDBLTFAST_WAIT)
3841 Flags |= WINEDDBLT_WAIT;
3842 if(trans & WINEDDBLTFAST_DONOTWAIT)
3843 Flags |= WINEDDBLT_DONOTWAIT;
3845 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3849 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3852 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
3854 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3855 RGBQUAD col[256];
3856 IWineD3DPaletteImpl *pal = This->palette;
3857 unsigned int n;
3858 TRACE("(%p)\n", This);
3860 if (!pal) return WINED3D_OK;
3862 if (This->resource.format_desc->format == WINED3DFMT_P8
3863 || This->resource.format_desc->format == WINED3DFMT_A8P8)
3865 int bpp;
3866 GLenum format, internal, type;
3867 CONVERT_TYPES convert;
3869 /* Check if we are using a RTL mode which uses texturing for uploads */
3870 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX);
3872 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
3873 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, FALSE);
3875 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
3877 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
3878 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
3880 /* We want to force a palette refresh, so mark the drawable as not being up to date */
3881 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
3883 /* Re-upload the palette */
3884 d3dfmt_p8_upload_palette(iface, convert);
3885 } else {
3886 if(!(This->Flags & SFLAG_INSYSMEM)) {
3887 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3888 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3890 TRACE("Dirtifying surface\n");
3891 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3895 if(This->Flags & SFLAG_DIBSECTION) {
3896 TRACE("(%p): Updating the hdc's palette\n", This);
3897 for (n=0; n<256; n++) {
3898 col[n].rgbRed = pal->palents[n].peRed;
3899 col[n].rgbGreen = pal->palents[n].peGreen;
3900 col[n].rgbBlue = pal->palents[n].peBlue;
3901 col[n].rgbReserved = 0;
3903 SetDIBColorTable(This->hDC, 0, 256, col);
3906 /* Propagate the changes to the drawable when we have a palette. */
3907 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3908 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3910 return WINED3D_OK;
3913 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3914 /** Check against the maximum texture sizes supported by the video card **/
3915 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3916 unsigned int pow2Width, pow2Height;
3918 This->glDescription.textureName = 0;
3919 This->glDescription.target = GL_TEXTURE_2D;
3921 /* Non-power2 support */
3922 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO) || GL_SUPPORT(WINE_NORMALIZED_TEXRECT)) {
3923 pow2Width = This->currentDesc.Width;
3924 pow2Height = This->currentDesc.Height;
3925 } else {
3926 /* Find the nearest pow2 match */
3927 pow2Width = pow2Height = 1;
3928 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3929 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3931 This->pow2Width = pow2Width;
3932 This->pow2Height = pow2Height;
3934 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3935 WINED3DFORMAT Format = This->resource.format_desc->format;
3936 /** TODO: add support for non power two compressed textures **/
3937 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3938 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5
3939 || Format == WINED3DFMT_ATI2N)
3941 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3942 This, This->currentDesc.Width, This->currentDesc.Height);
3943 return WINED3DERR_NOTAVAILABLE;
3947 if(pow2Width != This->currentDesc.Width ||
3948 pow2Height != This->currentDesc.Height) {
3949 This->Flags |= SFLAG_NONPOW2;
3952 TRACE("%p\n", This);
3953 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3954 /* one of three options
3955 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)
3956 2: Set the texture to the maximum size (bad idea)
3957 3: WARN and return WINED3DERR_NOTAVAILABLE;
3958 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.
3960 WARN("(%p) Creating an oversized surface: %ux%u (texture is %ux%u)\n",
3961 This, This->pow2Width, This->pow2Height, This->currentDesc.Width, This->currentDesc.Height);
3962 This->Flags |= SFLAG_OVERSIZE;
3964 /* This will be initialized on the first blt */
3965 This->glRect.left = 0;
3966 This->glRect.top = 0;
3967 This->glRect.right = 0;
3968 This->glRect.bottom = 0;
3969 } else {
3970 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3971 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3972 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3973 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3975 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE)
3976 && !((This->resource.format_desc->format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE)
3977 && (wined3d_settings.rendertargetlock_mode == RTL_READTEX
3978 || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3980 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3981 This->pow2Width = This->currentDesc.Width;
3982 This->pow2Height = This->currentDesc.Height;
3983 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
3986 /* No oversize, gl rect is the full texture size */
3987 This->Flags &= ~SFLAG_OVERSIZE;
3988 This->glRect.left = 0;
3989 This->glRect.top = 0;
3990 This->glRect.right = This->pow2Width;
3991 This->glRect.bottom = This->pow2Height;
3994 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3995 switch(wined3d_settings.offscreen_rendering_mode) {
3996 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3997 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3998 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4002 This->Flags |= SFLAG_INSYSMEM;
4004 return WINED3D_OK;
4007 struct depth_blt_info
4009 GLenum binding;
4010 GLenum bind_target;
4011 enum tex_types tex_type;
4012 GLfloat coords[4][3];
4015 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
4017 GLfloat (*coords)[3] = info->coords;
4019 switch (target)
4021 default:
4022 FIXME("Unsupported texture target %#x\n", target);
4023 /* Fall back to GL_TEXTURE_2D */
4024 case GL_TEXTURE_2D:
4025 info->binding = GL_TEXTURE_BINDING_2D;
4026 info->bind_target = GL_TEXTURE_2D;
4027 info->tex_type = tex_2d;
4028 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
4029 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
4030 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4031 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4032 break;
4034 case GL_TEXTURE_RECTANGLE_ARB:
4035 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
4036 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
4037 info->tex_type = tex_rect;
4038 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
4039 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
4040 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4041 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4042 break;
4044 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4045 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4046 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4047 info->tex_type = tex_cube;
4048 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4049 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4050 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4051 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4053 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4054 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4055 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4056 info->tex_type = tex_cube;
4057 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4058 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4059 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4060 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4062 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4063 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4064 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4065 info->tex_type = tex_cube;
4066 coords[0][0] = -1.0f; coords[0][1] = 1.0f; coords[0][2] = 1.0f;
4067 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 1.0f;
4068 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4069 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4071 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4072 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4073 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4074 info->tex_type = tex_cube;
4075 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4076 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4077 coords[2][0] = -1.0f; coords[2][1] = -1.0f; coords[2][2] = 1.0f;
4078 coords[3][0] = 1.0f; coords[3][1] = -1.0f; coords[3][2] = 1.0f;
4080 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4081 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4082 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4083 info->tex_type = tex_cube;
4084 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4085 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4086 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4087 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4089 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4090 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4091 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4092 info->tex_type = tex_cube;
4093 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4094 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4095 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4096 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4100 /* GL locking is done by the caller */
4101 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4103 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4104 struct depth_blt_info info;
4105 GLint old_binding = 0;
4107 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4109 glDisable(GL_CULL_FACE);
4110 glEnable(GL_BLEND);
4111 glDisable(GL_ALPHA_TEST);
4112 glDisable(GL_SCISSOR_TEST);
4113 glDisable(GL_STENCIL_TEST);
4114 glEnable(GL_DEPTH_TEST);
4115 glDepthFunc(GL_ALWAYS);
4116 glDepthMask(GL_TRUE);
4117 glBlendFunc(GL_ZERO, GL_ONE);
4118 glViewport(0, 0, w, h);
4120 surface_get_depth_blt_info(target, w, h, &info);
4121 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4122 glGetIntegerv(info.binding, &old_binding);
4123 glBindTexture(info.bind_target, texture);
4125 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4127 glBegin(GL_TRIANGLE_STRIP);
4128 glTexCoord3fv(info.coords[0]);
4129 glVertex2f(-1.0f, -1.0f);
4130 glTexCoord3fv(info.coords[1]);
4131 glVertex2f(1.0f, -1.0f);
4132 glTexCoord3fv(info.coords[2]);
4133 glVertex2f(-1.0f, 1.0f);
4134 glTexCoord3fv(info.coords[3]);
4135 glVertex2f(1.0f, 1.0f);
4136 glEnd();
4138 glBindTexture(info.bind_target, old_binding);
4140 glPopAttrib();
4142 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4145 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4146 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4148 TRACE("(%p) New location %#x\n", This, location);
4150 if (location & ~SFLAG_DS_LOCATIONS) {
4151 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4154 This->Flags &= ~SFLAG_DS_LOCATIONS;
4155 This->Flags |= location;
4158 void surface_load_ds_location(IWineD3DSurface *iface, DWORD location) {
4159 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4160 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4162 TRACE("(%p) New location %#x\n", This, location);
4164 /* TODO: Make this work for modes other than FBO */
4165 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4167 if (This->Flags & location) {
4168 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4169 return;
4172 if (This->current_renderbuffer) {
4173 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4174 return;
4177 if (location == SFLAG_DS_OFFSCREEN) {
4178 if (This->Flags & SFLAG_DS_ONSCREEN) {
4179 GLint old_binding = 0;
4180 GLenum bind_target;
4182 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4184 ENTER_GL();
4186 if (!device->depth_blt_texture) {
4187 glGenTextures(1, &device->depth_blt_texture);
4190 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4191 * directly on the FBO texture. That's because we need to flip. */
4192 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4193 if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
4194 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4195 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4196 } else {
4197 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4198 bind_target = GL_TEXTURE_2D;
4200 glBindTexture(bind_target, device->depth_blt_texture);
4201 glCopyTexImage2D(bind_target,
4202 This->glDescription.level,
4203 This->resource.format_desc->glInternal,
4206 This->currentDesc.Width,
4207 This->currentDesc.Height,
4209 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4210 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4211 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4212 glBindTexture(bind_target, old_binding);
4214 /* Setup the destination */
4215 if (!device->depth_blt_rb) {
4216 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
4217 checkGLcall("glGenRenderbuffersEXT");
4219 if (device->depth_blt_rb_w != This->currentDesc.Width
4220 || device->depth_blt_rb_h != This->currentDesc.Height) {
4221 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4222 checkGLcall("glBindRenderbufferEXT");
4223 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
4224 checkGLcall("glRenderbufferStorageEXT");
4225 device->depth_blt_rb_w = This->currentDesc.Width;
4226 device->depth_blt_rb_h = This->currentDesc.Height;
4229 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->dst_fbo);
4230 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4231 checkGLcall("glFramebufferRenderbufferEXT");
4232 context_attach_depth_stencil_fbo(device, GL_FRAMEBUFFER_EXT, iface, FALSE);
4234 /* Do the actual blit */
4235 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4236 checkGLcall("depth_blt");
4238 if (device->activeContext->current_fbo) {
4239 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->current_fbo->id);
4240 } else {
4241 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4242 checkGLcall("glBindFramebuffer()");
4245 LEAVE_GL();
4246 } else {
4247 FIXME("No up to date depth stencil location\n");
4249 } else if (location == SFLAG_DS_ONSCREEN) {
4250 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4251 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4253 ENTER_GL();
4255 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4256 checkGLcall("glBindFramebuffer()");
4257 surface_depth_blt(This, This->glDescription.textureName, This->currentDesc.Width, This->currentDesc.Height, This->glDescription.target);
4258 checkGLcall("depth_blt");
4260 if (device->activeContext->current_fbo) {
4261 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, device->activeContext->current_fbo->id));
4262 checkGLcall("glBindFramebuffer()");
4265 LEAVE_GL();
4266 } else {
4267 FIXME("No up to date depth stencil location\n");
4269 } else {
4270 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4273 This->Flags |= location;
4276 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4277 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4278 IWineD3DBaseTexture *texture;
4279 IWineD3DSurfaceImpl *overlay;
4281 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4282 persistent ? "TRUE" : "FALSE");
4284 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4285 if (This->Flags & SFLAG_SWAPCHAIN)
4287 TRACE("Surface %p is an onscreen surface\n", iface);
4288 } else {
4289 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4290 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4294 if(persistent) {
4295 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4296 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & 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 &= ~SFLAG_LOCATIONS;
4304 This->Flags |= flag;
4306 /* Redraw emulated overlays, if any */
4307 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4308 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4309 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4312 } else {
4313 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4314 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4315 TRACE("Passing to container\n");
4316 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4317 IWineD3DBaseTexture_Release(texture);
4320 This->Flags &= ~flag;
4323 if(!(This->Flags & SFLAG_LOCATIONS)) {
4324 ERR("%p: Surface does not have any up to date location\n", This);
4328 struct coords {
4329 GLfloat x, y, z;
4332 struct float_rect
4334 float l;
4335 float t;
4336 float r;
4337 float b;
4340 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4342 f->l = ((r->left * 2.0f) / w) - 1.0f;
4343 f->t = ((r->top * 2.0f) / h) - 1.0f;
4344 f->r = ((r->right * 2.0f) / w) - 1.0f;
4345 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4348 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
4349 struct coords coords[4];
4350 RECT rect;
4351 IWineD3DSwapChain *swapchain;
4352 IWineD3DBaseTexture *texture;
4353 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4354 GLenum bind_target;
4355 struct float_rect f;
4357 if(rect_in) {
4358 rect = *rect_in;
4359 } else {
4360 rect.left = 0;
4361 rect.top = 0;
4362 rect.right = This->currentDesc.Width;
4363 rect.bottom = This->currentDesc.Height;
4366 switch(This->glDescription.target)
4368 case GL_TEXTURE_2D:
4369 bind_target = GL_TEXTURE_2D;
4371 coords[0].x = (float)rect.left / This->pow2Width;
4372 coords[0].y = (float)rect.top / This->pow2Height;
4373 coords[0].z = 0;
4375 coords[1].x = (float)rect.left / This->pow2Width;
4376 coords[1].y = (float)rect.bottom / This->pow2Height;
4377 coords[1].z = 0;
4379 coords[2].x = (float)rect.right / This->pow2Width;
4380 coords[2].y = (float)rect.bottom / This->pow2Height;
4381 coords[2].z = 0;
4383 coords[3].x = (float)rect.right / This->pow2Width;
4384 coords[3].y = (float)rect.top / This->pow2Height;
4385 coords[3].z = 0;
4386 break;
4388 case GL_TEXTURE_RECTANGLE_ARB:
4389 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4390 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4391 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4392 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4393 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4394 break;
4396 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4397 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4398 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4399 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4400 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4401 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4402 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4403 break;
4405 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4406 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4407 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4408 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4409 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4410 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4411 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4412 break;
4414 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4415 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4416 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4417 coords[0].x = f.l; coords[0].y = 1; coords[0].z = f.t;
4418 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4419 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4420 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4421 break;
4423 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4424 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4425 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4426 coords[0].x = f.l; coords[0].y = -1; coords[0].z = -f.t;
4427 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4428 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4429 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4430 break;
4432 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4433 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4434 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4435 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4436 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4437 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4438 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4439 break;
4441 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4442 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4443 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4444 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4445 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4446 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4447 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4448 break;
4450 default:
4451 ERR("Unexpected texture target %#x\n", This->glDescription.target);
4452 return;
4455 ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4456 ENTER_GL();
4458 glEnable(bind_target);
4459 checkGLcall("glEnable(bind_target)");
4460 glBindTexture(bind_target, This->glDescription.textureName);
4461 checkGLcall("bind_target, This->glDescription.textureName)");
4462 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4463 checkGLcall("glTexParameteri");
4464 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4465 checkGLcall("glTexParameteri");
4467 if (device->render_offscreen)
4469 LONG tmp = rect.top;
4470 rect.top = rect.bottom;
4471 rect.bottom = tmp;
4474 glBegin(GL_QUADS);
4475 glTexCoord3fv(&coords[0].x);
4476 glVertex2i(rect.left, rect.top);
4478 glTexCoord3fv(&coords[1].x);
4479 glVertex2i(rect.left, rect.bottom);
4481 glTexCoord3fv(&coords[2].x);
4482 glVertex2i(rect.right, rect.bottom);
4484 glTexCoord3fv(&coords[3].x);
4485 glVertex2i(rect.right, rect.top);
4486 glEnd();
4487 checkGLcall("glEnd");
4489 glDisable(bind_target);
4490 checkGLcall("glDisable(bind_target)");
4492 LEAVE_GL();
4494 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4496 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4497 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4498 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4499 glFlush();
4501 IWineD3DSwapChain_Release(swapchain);
4502 } else {
4503 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4504 * reset properly next draw
4506 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4508 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4509 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4510 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4511 IWineD3DBaseTexture_Release(texture);
4516 /*****************************************************************************
4517 * IWineD3DSurface::LoadLocation
4519 * Copies the current surface data from wherever it is to the requested
4520 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4521 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4522 * multiple locations, the gl texture is preferred over the drawable, which is
4523 * preferred over system memory. The PBO counts as system memory. If rect is
4524 * not NULL, only the specified rectangle is copied (only supported for
4525 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4526 * location is marked up to date after the copy.
4528 * Parameters:
4529 * flag: Surface location flag to be updated
4530 * rect: rectangle to be copied
4532 * Returns:
4533 * WINED3D_OK on success
4534 * WINED3DERR_DEVICELOST on an internal error
4536 *****************************************************************************/
4537 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4538 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4539 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4540 GLenum format, internal, type;
4541 CONVERT_TYPES convert;
4542 int bpp;
4543 int width, pitch, outpitch;
4544 BYTE *mem;
4545 BOOL drawable_read_ok = TRUE;
4547 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4548 if (This->Flags & SFLAG_SWAPCHAIN)
4550 TRACE("Surface %p is an onscreen surface\n", iface);
4551 } else {
4552 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4553 * Prefer SFLAG_INTEXTURE. */
4554 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4555 drawable_read_ok = FALSE;
4559 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4560 if(rect) {
4561 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4564 if(This->Flags & flag) {
4565 TRACE("Location already up to date\n");
4566 return WINED3D_OK;
4569 if(!(This->Flags & SFLAG_LOCATIONS)) {
4570 ERR("%p: Surface does not have any up to date location\n", This);
4571 This->Flags |= SFLAG_LOST;
4572 return WINED3DERR_DEVICELOST;
4575 if(flag == SFLAG_INSYSMEM) {
4576 surface_prepare_system_memory(This);
4578 /* Download the surface to system memory */
4579 if(This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) {
4580 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4581 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4583 surface_download_data(This);
4584 } else {
4585 read_from_framebuffer(This, rect,
4586 This->resource.allocatedMemory,
4587 IWineD3DSurface_GetPitch(iface));
4589 } else if(flag == SFLAG_INDRAWABLE) {
4590 if(This->Flags & SFLAG_INTEXTURE) {
4591 surface_blt_to_drawable(This, rect);
4592 } else {
4593 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4594 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4595 * values, otherwise we get incorrect values in the target. For now go the slow way
4596 * via a system memory copy
4598 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4601 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, FALSE);
4603 /* The width is in 'length' not in bytes */
4604 width = This->currentDesc.Width;
4605 pitch = IWineD3DSurface_GetPitch(iface);
4607 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4608 * but it isn't set (yet) in all cases it is getting called. */
4609 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4610 TRACE("Removing the pbo attached to surface %p\n", This);
4611 surface_remove_pbo(This);
4614 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4615 int height = This->currentDesc.Height;
4617 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4618 outpitch = width * bpp;
4619 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4621 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4622 if(!mem) {
4623 ERR("Out of memory %d, %d!\n", outpitch, height);
4624 return WINED3DERR_OUTOFVIDEOMEMORY;
4626 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4628 This->Flags |= SFLAG_CONVERTED;
4629 } else {
4630 This->Flags &= ~SFLAG_CONVERTED;
4631 mem = This->resource.allocatedMemory;
4634 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4636 /* Don't delete PBO memory */
4637 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4638 HeapFree(GetProcessHeap(), 0, mem);
4640 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4641 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4642 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4643 } else { /* Upload from system memory */
4644 BOOL srgb = flag == SFLAG_INSRGBTEX;
4645 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4646 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
4648 if(srgb) {
4649 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4650 /* Performance warning ... */
4651 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4652 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4654 } else {
4655 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4656 /* Performance warning ... */
4657 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4658 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4662 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4663 surface_bind_and_dirtify(This, srgb);
4665 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4666 This->Flags |= SFLAG_GLCKEY;
4667 This->glCKey = This->SrcBltCKey;
4669 else This->Flags &= ~SFLAG_GLCKEY;
4671 /* The width is in 'length' not in bytes */
4672 width = This->currentDesc.Width;
4673 pitch = IWineD3DSurface_GetPitch(iface);
4675 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4676 * but it isn't set (yet) in all cases it is getting called. */
4677 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4678 TRACE("Removing the pbo attached to surface %p\n", This);
4679 surface_remove_pbo(This);
4682 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4683 int height = This->currentDesc.Height;
4685 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4686 outpitch = width * bpp;
4687 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4689 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4690 if(!mem) {
4691 ERR("Out of memory %d, %d!\n", outpitch, height);
4692 return WINED3DERR_OUTOFVIDEOMEMORY;
4694 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4696 This->Flags |= SFLAG_CONVERTED;
4698 else if ((This->resource.format_desc->format == WINED3DFMT_P8)
4699 && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)))
4701 d3dfmt_p8_upload_palette(iface, convert);
4702 This->Flags &= ~SFLAG_CONVERTED;
4703 mem = This->resource.allocatedMemory;
4704 } else {
4705 This->Flags &= ~SFLAG_CONVERTED;
4706 mem = This->resource.allocatedMemory;
4709 /* Make sure the correct pitch is used */
4710 ENTER_GL();
4711 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4712 LEAVE_GL();
4714 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4715 TRACE("non power of two support\n");
4716 if(!(This->Flags & alloc_flag)) {
4717 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4718 This->Flags |= alloc_flag;
4720 if (mem || (This->Flags & SFLAG_PBO)) {
4721 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4723 } else {
4724 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4725 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4727 if(!(This->Flags & alloc_flag)) {
4728 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4729 This->Flags |= alloc_flag;
4731 if (mem || (This->Flags & SFLAG_PBO)) {
4732 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4736 /* Restore the default pitch */
4737 ENTER_GL();
4738 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4739 LEAVE_GL();
4741 /* Don't delete PBO memory */
4742 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4743 HeapFree(GetProcessHeap(), 0, mem);
4747 if(rect == NULL) {
4748 This->Flags |= flag;
4751 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !(This->Flags & SFLAG_SWAPCHAIN)
4752 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4753 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4754 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4757 return WINED3D_OK;
4760 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4762 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4763 IWineD3DSwapChain *swapchain = NULL;
4765 /* Update the drawable size method */
4766 if(container) {
4767 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4769 if(swapchain) {
4770 This->get_drawable_size = get_drawable_size_swapchain;
4771 IWineD3DSwapChain_Release(swapchain);
4772 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4773 switch(wined3d_settings.offscreen_rendering_mode) {
4774 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4775 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4776 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4780 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4783 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4784 return SURFACE_OPENGL;
4787 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4788 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4789 HRESULT hr;
4791 /* If there's no destination surface there is nothing to do */
4792 if(!This->overlay_dest) return WINED3D_OK;
4794 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4795 * update the overlay. Prevent an endless recursion
4797 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
4798 return WINED3D_OK;
4800 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
4801 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4802 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4803 NULL, WINED3DTEXF_LINEAR);
4804 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
4806 return hr;
4809 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4811 /* IUnknown */
4812 IWineD3DBaseSurfaceImpl_QueryInterface,
4813 IWineD3DBaseSurfaceImpl_AddRef,
4814 IWineD3DSurfaceImpl_Release,
4815 /* IWineD3DResource */
4816 IWineD3DBaseSurfaceImpl_GetParent,
4817 IWineD3DBaseSurfaceImpl_GetDevice,
4818 IWineD3DBaseSurfaceImpl_SetPrivateData,
4819 IWineD3DBaseSurfaceImpl_GetPrivateData,
4820 IWineD3DBaseSurfaceImpl_FreePrivateData,
4821 IWineD3DBaseSurfaceImpl_SetPriority,
4822 IWineD3DBaseSurfaceImpl_GetPriority,
4823 IWineD3DSurfaceImpl_PreLoad,
4824 IWineD3DSurfaceImpl_UnLoad,
4825 IWineD3DBaseSurfaceImpl_GetType,
4826 /* IWineD3DSurface */
4827 IWineD3DBaseSurfaceImpl_GetContainer,
4828 IWineD3DBaseSurfaceImpl_GetDesc,
4829 IWineD3DSurfaceImpl_LockRect,
4830 IWineD3DSurfaceImpl_UnlockRect,
4831 IWineD3DSurfaceImpl_GetDC,
4832 IWineD3DSurfaceImpl_ReleaseDC,
4833 IWineD3DSurfaceImpl_Flip,
4834 IWineD3DSurfaceImpl_Blt,
4835 IWineD3DBaseSurfaceImpl_GetBltStatus,
4836 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4837 IWineD3DBaseSurfaceImpl_IsLost,
4838 IWineD3DBaseSurfaceImpl_Restore,
4839 IWineD3DSurfaceImpl_BltFast,
4840 IWineD3DBaseSurfaceImpl_GetPalette,
4841 IWineD3DBaseSurfaceImpl_SetPalette,
4842 IWineD3DSurfaceImpl_RealizePalette,
4843 IWineD3DBaseSurfaceImpl_SetColorKey,
4844 IWineD3DBaseSurfaceImpl_GetPitch,
4845 IWineD3DSurfaceImpl_SetMem,
4846 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4847 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4848 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4849 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4850 IWineD3DBaseSurfaceImpl_SetClipper,
4851 IWineD3DBaseSurfaceImpl_GetClipper,
4852 /* Internal use: */
4853 IWineD3DSurfaceImpl_LoadTexture,
4854 IWineD3DSurfaceImpl_BindTexture,
4855 IWineD3DSurfaceImpl_SaveSnapshot,
4856 IWineD3DSurfaceImpl_SetContainer,
4857 IWineD3DSurfaceImpl_GetGlDesc,
4858 IWineD3DBaseSurfaceImpl_GetData,
4859 IWineD3DSurfaceImpl_SetFormat,
4860 IWineD3DSurfaceImpl_PrivateSetup,
4861 IWineD3DSurfaceImpl_ModifyLocation,
4862 IWineD3DSurfaceImpl_LoadLocation,
4863 IWineD3DSurfaceImpl_GetImplType,
4864 IWineD3DSurfaceImpl_DrawOverlay
4866 #undef GLINFO_LOCATION
4868 #define GLINFO_LOCATION device->adapter->gl_info
4869 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
4870 static void ffp_blit_free(IWineD3DDevice *iface) { }
4872 static HRESULT ffp_blit_set(IWineD3DDevice *iface, const struct GlPixelFormatDesc *format_desc,
4873 GLenum textype, UINT width, UINT height)
4875 ENTER_GL();
4876 glEnable(textype);
4877 checkGLcall("glEnable(textype)");
4878 LEAVE_GL();
4879 return WINED3D_OK;
4882 static void ffp_blit_unset(IWineD3DDevice *iface) {
4883 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4884 ENTER_GL();
4885 glDisable(GL_TEXTURE_2D);
4886 checkGLcall("glDisable(GL_TEXTURE_2D)");
4887 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP)) {
4888 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4889 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4891 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
4892 glDisable(GL_TEXTURE_RECTANGLE_ARB);
4893 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4895 LEAVE_GL();
4898 static BOOL ffp_blit_color_fixup_supported(struct color_fixup_desc fixup)
4900 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4902 TRACE("Checking support for fixup:\n");
4903 dump_color_fixup_desc(fixup);
4906 /* We only support identity conversions. */
4907 if (is_identity_fixup(fixup))
4909 TRACE("[OK]\n");
4910 return TRUE;
4913 TRACE("[FAILED]\n");
4914 return FALSE;
4917 const struct blit_shader ffp_blit = {
4918 ffp_blit_alloc,
4919 ffp_blit_free,
4920 ffp_blit_set,
4921 ffp_blit_unset,
4922 ffp_blit_color_fixup_supported